浅学前端:Vue篇(一)

2023-11-12 17:37:17 浏览数 (1)

1. Vue 基础

1) 环境准备

1. 安装脚手架
代码语言:javascript复制
 npm install -g @vue/cli
  • -g 参数表示全局安装,这样在任意目录都可以使用 vue 脚本创建项目
2. 创建项目
代码语言:javascript复制
 vue ui

使用图形向导来创建 vue 项目,如下图:

  1. 输入项目名,包管理器选择npm

不想用git,可以取消勾选初始化git仓库,也可以创建完之后,删除.git文件夹

  1. 选择手动配置项目
  1. 添加 vue router 和 vuex

一个是实现组件之间的跳转,一个是实现组件之间数据的共享。

  1. 选择版本,创建项目

安装完毕后会跳转到一个页面:

3. 安装 devtools

官方浏览器插件,用于调试 Vue.js 应用。你可以检查组件、vuex store 和事件等。

  • devtools 插件网址:https://devtools.vuejs.org/guide/installation.html
4. 运行项目

进入项目目录,执行

代码语言:javascript复制
 npm run serve

启动成功:

5. 修改端口

前端服务器默认占用了 8080 端口,需要修改一下

  • 文档地址:DevServer | webpack
  • 打开 vue.config.js 添加
代码语言:javascript复制
 const { defineConfig } = require('@vue/cli-service')  module.exports = defineConfig({    // ...    devServer: {      port: 8082 // 设置前端服务器端口    }  })

6. 添加代理
  • 文档地址:DevServer | webpack 不要使用第一段,使用这个:

为了避免前后端服务器联调时, fetch、xhr 请求产生跨域问题,需要配置代理

  • 文档地址同上
  • 打开 vue.config.js 添加
代码语言:javascript复制
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
    // ...
    devServer: {
        // 端口
        port: 8082,
        // 代理,将来只要以/api打头的请求,都会走代理的逻辑
        proxy: {
            '/api': {
                target: 'http://localhost:8080', //设置成后端的服务器地址
                changeOrigin: true
            }
        }
    }
})

测试:

后端服务器在8080端口:

7. Vue 项目结构
代码语言:javascript复制
 PS D:codeVScodeProjectsfountendstudystudy03-vue2> tree src
 D:CODEVSCODEPROJECTSFOUNTENDSTUDYSTUDY03-VUE2SRC
 ├─assets
 ├─components
 ├─router
 ├─store
 └─views
  • assets - 静态资源
  • components - 可重用组件
  • router - 路由
  • store - 数据共享
  • views - 视图组件

以后还会添加

  • api - 跟后台交互,发送 fetch、xhr 请求,接收响应
  • plugins - 插件

2) Vue 组件

Vue 的组件文件以 .vue 结尾,每个组件由三部分组成

代码语言:javascript复制
 <template></template>
 ​
 <script></script>
 ​
 <style></style>
  • template 模板部分,由它生成 html 代码
  • script 代码部分,控制模板的数据来源和行为
  • style 样式部分,一般不咋关心

入口组件是 App.vue

先删除原有代码,来个 Hello, World 例子

代码语言:javascript复制
 <template>
   <!-- {{}} 文本插值 -->
   <h1>{{msg}}</h1>
 </template>
 ​
 <script>
   // vue组件的`<script>`中必须默认导出一个`options`对象
   const options  = {
     data: function(){
       // options的属性data,的函数返回值才是模板要使用的数据对象
       return {msg:"Hello World!"};
     }
   };
   export default options;
 ​
 </script>

注意: vue组件的<script>中必须默认导出一个options对象。

  • export default 导出组件对象,供 main.js 导入使用
  • 这个对象有一个 data 方法,返回一个对象,给 template 提供数据
  • {{}} 在 Vue 里称之为插值表达式,用来绑定 data 方法返回的对象属性,绑定的含义是数据发生变化时,页面显示会同步变化

那么是谁在使用App.vue这个组件?

main.js导入了App.vue:

代码语言:javascript复制
 import Vue from 'vue'
 // 1. 
 import App from './App.vue'
 import router from './router'
 import store from './store'
 ​
 Vue.config.productionTip = false
 ​
 new Vue({
     router,
     store,
     // 2. 
     render: h => h(App)  
     // 3. 
 }).$mount('#app')
  • import App from './App.vue':实这个导入我们可以简单理解为把App.vue的模板部分拿到了main.js,并对模板部分进一步解析(h => h(App)),最终将{{msg}}解析成hello world。
  • h => h(App):对模板进行解析,解析后生成一个虚拟节点(简单理解,他也是一种HTML元素,只不过还没有跟最终的页面结合到一起,还没有显示出来)
  • $mount('#app'):上面提到了虚拟节点未显示出来,那么哪一步将这个虚拟的节点显示到页面上呢? $mount('#app'):会把解析后的虚拟节点放到页面上展示出来。 放到哪里去了呢? #app 就是一个id选择器:

可以看到/public/index.html里有这个标签,所以解析后的虚拟节点放到这里。 我们打开F12,选中Hello world可以看到index.html里的<div id="app"></div>被替换成了解析后的虚拟节点:

1. 文本插值
代码语言:javascript复制
<template>
    <div>
        <h1>{{ name }}</h1>
        <h1>{{ age > 60 ? '老年' : '青年' }}</h1>
    </div>
</template>
<script>
const options = {
    data: function () {
        return { name: '张三', age: 70 };
    }
};
export default options;
</script>
  • {{}} 里只能绑定一个属性,绑定多个属性需要用多个 {{}} 分别绑定:
代码语言:javascript复制
<template>
    <h1>{{name age}}</h1>
</template>

// 会编译报错。
  • template 内只能有一个根元素:
代码语言:javascript复制
<template>
    <h1>{{name}}</h1>
    <h1>{{age}}</h1>
</template>
// 会报错:
// error  The template root requires exactly one element  vue/no-multiple-template-root
  • 插值内可以进行简单的表达式计算
2. 属性绑定

对于标签中的文本数据,可以使用文本插值{{}}进行绑定,但是对于标签里的属性来讲,他的语法就不一样了,这就用到了属性绑定:

代码语言:javascript复制
<template>
    <div>
        <div><input type="text" v-bind:value="name"></div>
        <div><input type="date" v-bind:value="birthday"></div>
        <div><input type="text" :value="age"></div>
    </div>
</template>

<script>
const options = {
    data: function () {
        return { name: '王五', birthday: '1995-05-01', age: 20 };
    }
};
export default options;
</script>
  • 简写方式:可以省略 v-bind 只保留冒号
3. 事件绑定
代码语言:javascript复制
<!-- 事件绑定 -->
<template>
    <div>
        <div><input type="button" value="点我执行m1" v-on:click="m1"></div>
        <div><input type="button" value="点我执行m2" @click="m2"></div>
        <div>{{count}}</div>
    </div>
</template>
<script>
const options = {
    data: function () {
        return { count: 0 };
    },
    methods: {
        m1() {
            this.count   ;
            console.log("m1")
        },
        m2() {
            this.count --;
            console.log("m2")
        }
    }
};
export default options;
</script>
  • optinos里data是给模板提供数据的,methods是给模板提供方法的。
  • 简写方式:可以把 v-on: 替换为 @
  • 在 methods 方法中的 this 代表的是 data 函数返回的数据对象
4. 双向绑定

先阅读下面的代码:

代码语言:javascript复制
<template>
    <div>
        <div>
            <label>请输入姓名:</label>
            <input type="text" v-bind:value="name">
        </div>
        <div>
            <label>请输入年龄:</label>
            <input type="text" v-bind:value="age">
        </div>
    </div>
</template>
<script>
const optinos = {
    data: function () {
        return { name: '李刚', age: null }
    },
    methods: {

    }
};
export default optinos;
</script>

这段代码就是将javaScript的数据与标签中的属性进行绑定,但是这种绑定是单向的,只能将javaScript中的数据传到文本框中,但是文本框中用户输入的数据无法同步到javaScript这边。

这里的双向绑定就是用户输入的数据也同步到javaScript这边:

代码语言:javascript复制
<template>
    <div>
        <div>
            <label for="">请输入姓名</label>
            <input type="text" v-model="name">
        </div>
        <div>
            <label for="">请输入年龄</label>
            <input type="text" v-model="age">
        </div>
        <div>
            <label for="">请选择性别</label>
            男 <input type="radio" value="男" v-model="sex">
            女 <input type="radio" value="女" v-model="sex">
        </div>
        <div>
            <label for="">请选择爱好</label>
            游泳 <input type="checkbox" value="游泳" v-model="fav">
            打球 <input type="checkbox" value="打球" v-model="fav">
            健身 <input type="checkbox" value="健身" v-model="fav">
        </div>
    </div>
</template>
<script>
const options = {
    data: function () {
        return { name: '', age: null, sex:'男' , fav:['打球']};
    },
    methods: {
    }
};
export default options;
</script>
  • 用 v-model 实现双向绑定,即
    • javascript 数据可以同步到表单标签
    • 反过来用户在表单标签输入的新值也会同步到 javascript 这边
  • 双向绑定只适用于表单这种带【输入】功能的标签,其它标签的数据绑定,单向就足够了
  • 复选框<checkbox>这种标签,双向绑定的 javascript 数据类型一般用数组

vue的调试工具: F12-->vue,就可以看到之前下载的devtools调试工具了:

可以在这里看到data返回的数据对象的变化:

5. 计算属性
代码语言:javascript复制
<!-- 计算属性 -->
<template>
    <div>
        <!-- 方法1:直接拼接-->
        <!-- <h2>{{lastName   firstName}}</h2>-->
        <!-- 方法2:使用方法-->
        <!-- <h2>{{fullName()}}</h2>-->
        
        <!-- 方法3:采用计算属性的写法(推荐)-->
        <h2>{{fullName}}</h2>
        <h2>{{fullName}}</h2>
        <h2>{{fullName}}</h2>
    </div>
</template>
<script>
const options = {
    data: function () {
        return { firstName: '三', lastName: '张' };
    },
    /* methods: {
        fullName() {
            console.log('进入了 fullName')
            return this.lastName   this.firstName;
        }
    },*/
    computed: {
        fullName() {
            console.log('进入了 fullName')
            return this.lastName   this.firstName;
        }
    }
};
export default options;
  • 为什么把他叫做计算属性呢? 因为计算属性使用时就把它当属性来用,无需加 (),
  • 计算属性和方法的区别: 可以发现两种方式非常接近,只不过调用时一个有()一个没有(),那么计算属性有什么有点呢? 普通方法没有缓存功能,计算属性有缓存功能: 一次fullName()发生计算后,会将结果缓存,下次再计算时,只要数据没有变化,不会重新计算,直接返回缓存结果。 使用methods:
代码语言:javascript复制
<template>
<div>
  <h1>{{fullName()}}</h1>
  <h1>{{fullName()}}</h1>
  <h1>{{fullName()}}</h1>
</div>
</template>

使用computed:

代码语言:javascript复制
<template>
<div>
  <h1>{{fullName}}</h1>
  <h1>{{fullName}}</h1>
  <h1>{{fullName}}</h1>
</div>
</template>
6. axios

axios 它的底层是用了 XMLHttpRequest(xhr)方式发送请求和接收响应,xhr 相对于之前讲过的 fetch api 来说,功能更强大,但由于是比较老的 api,不支持 Promise,axios 对 xhr 进行了封装,使之支持 Promise,并提供了对请求、响应的统一拦截功能(相当于后端的过滤器,拦截器)

axios就是 ajax的一种实现。因为axios的底层是XMLHttpRequest,所以会发生跨域,下面因为使用了代理,所以没有出现跨域的问题。

1. 安装
代码语言:javascript复制
 npm install axios -S
2. 导入
代码语言:javascript复制
 import axios from 'axios'
  • axios 默认导出一个对象,这里的 import 导入的就是它默认导出的对象,源码: export default axios;
3. 示例:
代码语言:javascript复制
 <template>
     <div>
         <input type="button" value="获取远程数据" v-on:click="sendRequest()">
     </div>
 </template>
 <script>
     // 导入axiso
     import axios from 'axios'
     const options  = {
         methods:{
             async sendRequest(){
                 const resp = await axios.get("/api/students");
                 console.log(resp)
             }
         }
     };
     export default options
 </script>

F12,查看控制台:

4. 方法

请求

备注

axios.get(url[, config])

⭐️

axios.delete(url[, config])

axios.head(url[, config])

axios.options(url[, config])

axios.post(url[, data[, config]])

⭐️

axios.put(url[, data[, config]])

axios.patch(url[, data[, config]])

  • config - 选项对象、例如查询参数、请求头...
  • data - 请求体数据、最常见的是 json 格式数据
  • get、head 请求无法携带请求体,这应当是浏览器的限制所致(xhr、fetch api 均有限制)
  • options、delete 请求可以通过 config 中的 data 携带请求体

例子

代码语言:javascript复制
<template>
    <div>
        <input type="button" value="获取远程数据" @click="sendReq()">
    </div>
</template>
<script>
import axios from 'axios'
const options = {
    methods: {
        async sendReq() {
            //因为我们配置了代理,以/api打头的请求都会代理到8080,所以axiso发起的请求不用写那么完整。
------------------------------------------------------------------------  
            // 1. 演示 get, post
            // const resp = await axios.get('/api/a1');
            // const resp = await axios.post('/api/a2');
------------------------------------------------------------------------
            // 2. 发送请求头
			// go使用r.Header.Get()接收
            // const resp = await axios.post('/api/a3',{},{
            //     headers:{
            //         Authorization:'abc'
            //     }
            // });
------------------------------------------------------------------------
            // 3. 发送请求时携带查询参数 ?name=xxx&age=xxx
			// go使用r.FormValue()接收
            // 方法1:拼字符串
            // const name = encodeURIComponent('&&&');
            // const age = 18;
            // const resp = await axios.post(`/api/a4?name=${name}&age=${age}`);

            // 方法2(推荐):
            // 不想自己拼串、处理特殊字符、就用下面的办法
            // const resp = await axios.post('/api/a4', {}, {
            //     params: {
            //         "name":'&&&&',
            //         "age": 20
            //     }
            // });
------------------------------------------------------------------------
            // 4. 用请求体发数据,格式为 urlencoded
			// go使用r.FormValue()接收
            // const params = new URLSearchParams();
            // params.append("name", "张三");
            // params.append("age", 24)

            // const resp = await axios.post('/api/a4', params);
------------------------------------------------------------------------
            // 5. 用请求体发数据,格式为 multipart,
			// go使用r.ParseMultipartForm()接收
            // const params = new FormData();
            // params.append("name", "李四");
            // params.append("age", 30);
            // const resp = await axios.post('/api/a5', params);
------------------------------------------------------------------------
            // 6. 用请求体发数据,格式为 json
			// go 使用 io.ReadAll(r.Body) json.Unmarshal() 接收
            const resp = await axios.post('/api/a5json', {
                name: '王五',
                age: 50
            });
------------------------------------------------------------------------
            console.log(resp);
        }
    }
};
export default options;
</script>
  • 拼字符的时候需要注意:有些特殊字符必须经过URL编码,否则一些特殊字符是无法正确发给服务器的,例如&&&: 后端接收到的值: name: age: 10 所以需要进行编码:
代码语言:javascript复制
const name = encodeURIComponent('&&&');
  • 使用请求体发数据(格式为 urlencoded)的时候,不可以直接传入一个普通对象,因为这里的普通对象默认是json格式:
代码语言:javascript复制
const resp = await axios.post('/api/a4', {
    name: "123",
    age: 20
});
console.log(resp)
  • 我们打开F12,查看这个请求的信息,可以看到我们这样写,对应的数据格式Content-Type: application/json。后端对于json类型,需要配合对象去接收:
代码语言:javascript复制
× //fmt.Println("name: " r.FormValue("name"), "age: " r.FormValue("age"))
---------------------------------------------------------------------
type student struct {
    Name string
    Age  int
}
stu := new(student)
body, _ := io.ReadAll(r.Body)
json.Unmarshal(body, stu)
fmt.Println(stu)
  • 而是要以上面的例子的格式发送。
5. 默认设置:

上面使用axios,是import之后直接使用它里面那些发送请求的方法,这样做是有一个问题的,这种情况下,我们发送每个请求的时候使用的都是默认设置,如果你发请求的时候不想用他的默认设置了,那每个请求方法里都需要跟上config参数,这样不够通用。

解决办法就是,自己创建axios对象,进行配置。

创建实例

代码语言:javascript复制
const _axios = axios.create(config);
  • axios 对象可以直接使用,但使用的是默认的设置
  • 用 axios.create 创建的对象,可以覆盖默认设置,config 见下面说明

常见的 config 项有

名称

含义

baseURL

将自动加在 url 前面

headers

请求头,类型为简单对象

params

跟在 URL 后的请求参数,类型为简单对象或 URLSearchParams

data

请求体,类型有简单对象、FormData、URLSearchParams、File 等

withCredentials

跨域时是否携带 Cookie 等凭证,默认为 false

responseType

响应类型,默认为 json

注意: 开发环境:开发环境是程序猿们专门用于开发的服务器,配置可以比较随意, 为了开发调试方便,一般打开全部错误报告。简单讲就是项目尚且处于编码阶段,一般这时候会把代码放在开发环境中,不会放在生产环境中。 生产环境:是指正式提供对外服务的,一般会关掉错误报告,打开错误日志。简单讲就是所谓的线上,就是正式给用户使用的环境。

例子:

代码语言:javascript复制
const _axios = axios.create({
    baseURL: 'http://localhost:8080',
    withCredentials: true
});
await _axios.post('/api/a6set')
await _axios.post('/api/a6get')
  • 生产环境希望 xhr 请求不走代理,可以用 baseURL 统一修改(前端不用代理时,后端记得使用Access-Control-Allow-Origin头。) 使用代理的方式主要是用在开发环境,中间经过代理,性能肯定会受到影响,真正生存环境中解决跨域问题是不用代理的。
  • 希望跨域请求携带 cookie,需要配置 withCredentials: true,服务器也要配置 allowCredentials = true,否则浏览器获取跨域返回的 cookie 时会报错 w.Header().Set("Access-Control-Allow-Credentials", "true")
6. 响应格式

名称

含义

data

响应体数据 ⭐️

status

状态码 ⭐️

headers

响应头

  • 200 表示响应成功
  • 400 请求数据不正确 age=abc
  • 401 身份验证没通过
  • 403 没有权限(这个是身份验证通过了,但是你要访问更高权限的资源时,会出现403)
  • 404 资源不存在
  • 405 不支持请求方式 post
  • 500 服务器内部错误

注意: 这个status响应状态码与后端经常返回的code不一样,后端返回的code可以根据项目来设置,比如用1001表示错误1,1002表示错误2... 后端经常返回的code时候应用程序的状态码 这个status则是整个响应的状态码,是HTTP协议固定好的。

例子:

响应状态码200以下都会正常进行,200以上会出现异常,不在往下执行。

代码语言:javascript复制
<template>
    <div>
        <input type="button" value="获取远程数据" v-on:click="sendRequest()">
    </div>
</template>
<script>
// 导入axiso
import axios from 'axios'
const options = {
    methods: {
        async sendRequest() {
            const newAxios = new axios.create({
                baseURL: "http://localhost:8080",
                withCredentials: true
            })
            // 访问一个不存在的资源--->404,
            const resp = await newAxios.get('/api/students2');
            console.log(resp)
        }
    }
};
export default options
</script>

可以看到,报错了,且没有继续往下执行:

可以使用try-catch捕获异常

代码语言:javascript复制
try {
    const resp = await newAxios.get('/api/students2');
    console.log(resp)
} catch (error) {
    console.log(error.response)
}
7. 拦截器

1. 请求拦截器

代码语言:javascript复制
_axios.interceptors.request.use(
  function(config) {
    // 比如在这里添加统一的 headers
    return config;
  },
  function(error) {
    return Promise.reject(error);
  }
);
代码语言:javascript复制
// 拦截器:
newAxios.interceptors.request.use(
    function (config) {
        // 比如在这个添加统一的headers
        config.headers = {
            Authorization: "aaa.bbb.ccc"
        }
        return config;
    },
    function (error) {
        return Promise.reject(error);
    }
)

2. 响应拦截器

  • 参数为两个函数,第一个函数时响应正常的情况下执行的拦截操作,第二个是响应出错的情况下执行的拦截操作。
  • 例子: 可以帮助我们统一处理异常,之前的时候,一旦响应出现异常,都需要自己加try-catch,而如果加了响应拦截器,所有异常都可以放在第二个函数里处理。

代码语言:javascript复制
_axios.interceptors.response.use(
  function(response) {
    // 状态码在 2xx 范围内走这里
    return response;
  },
  function(error) {
    // 状态码 超出 2xx, 比如 4xx, 5xx 走这里
    return Promise.reject(error);
  }
);

return Promise.reject(error);相当于抛出了异常

外层如果没有捕捉的话,还是会在控制台显示出错误的,如果想要达到类似于捉住异常的效果,应该这样写:

代码语言:javascript复制
// 响应拦截器:
newAxios.interceptors.response.use(
    function (response) {
        return response;
    },
    function (error) {
        switch (error.response.status) {
            case 400:
                console.log("请求参数不正确")
                return
            case 401:
                console.log("认证未通过,跳转至登录页面")
                return
            case 403:
                console.log("权限不够")
                return
            case 404:
                console.log("资源未找到")
                return
            case 500:
                console.log("服务器异常")
                return
        }
        // 
        return Promise.reject(error);
    }
)
newAxios.get("/api/jwt")

在本部分我们自己创建了axiso对象,并且配置了请求拦截器和响应拦截器,这些代码具有一定通用性,我们没有必要在每个vue组件里都写一遍,所以像这种具有通用性的代码,我们可以把他们单独抽到一个js文件里:

/src/util/myaxiso.js

代码语言:javascript复制
 // 导入axiso
import axios from 'axios'

const newAxios = new axios.create({
    baseURL: "http://localhost:8080",
    withCredentials: true
});

// 请求拦截器:
newAxios.interceptors.request.use(
    function (config) {
        // 比如在这个添加统一的headers
        config.headers = {
            Authorization: "aaa.bbb.ccc"
        }
        return config;
    },
    function (error) {
        return Promise.reject(error);
    }
);
// 响应拦截器:
newAxios.interceptors.response.use(
    function (response) {
        return response;
    },
    function (error) {
        switch (error.response.status) {
            case 400:
                console.log("请求参数不正确")
                return Promise.resolve(400)
            case 401:
                console.log("认证未通过,跳转至登录页面")
                return Promise.resolve(401)
            case 403:
                console.log("权限不够")
                return Promise.resolve(403)
            case 404:
                console.log("资源未找到")
                return Promise.resolve(404)
            case 500:
                console.log("服务器异常")
                return Promise.resolve(500)
        }
        return Promise.reject(error);
    }
);

// 将自己的axiso默认导出,让其他地方使用
export default newAxios;

之后我们就可以在vue组件里,使用这个js文件了:

代码语言:javascript复制
<template>
    <div>
    	<input type="button" value="获取远程数据" v-on:click="sendRequest()">
    </div>
</template>
<script>
// 导入自己的axiso
import axios from "../util/myaxiso";
const options = {
    methods: {
        async sendRequest() {
            const resp = await axios.get("/api/students")
            console.log(resp)
        }
    }
};
export default options
</script>
7. 条件渲染 列表渲染

上面讲述的axios知识主要是为了接下来的vue小案例,这个案例里就可以使用axios,获取服务端的一些真实数据了,通过这个案例可以学到vue里的条件渲染与列表渲染。

代码语言:javascript复制
 <template>
     <div>
         <input type="button" value="获取远程数据" v-on:click="sendReq()">
         <div class="title">学生列表</div>
         <div class="thead">
             <div class="row bold">
                 <div class="col">编号</div>
                 <div class="col">姓名</div>
                 <div class="col">性别</div>
                 <div class="col">年龄</div>
             </div>
         </div>
         <!-- 要求:如果没有学生数据,做出处理 -->
         <!-- vue里v打头的这个叫做指令 -->
         <div class="tbody">
             <div v-if="students.length>0">
                 <!-- v-for 循环遍历,in of 都可以 -->
                 <!-- 注意:使用v-for的时候,需要v-bind:key='可以唯一标识的字段' -->
                 <div class="row" v-for="stu of students" v-bind:key="stu.ID">
                     <div class="col">{{stu.ID}}</div>
                     <div class="col">{{stu.Name}}</div>
                     <div class="col">{{stu.Sex}}</div>
                     <div class="col">{{stu.Age}}</div>
                 </div>
             </div>
             <div v-else>
                 暂无学生数据
             </div>
         </div>
 ​
     </div>
 </template>
 ​
 <script>
 import axios from '../util/myaxiso'
 const options = {
     data: function () {
         return { students: [] };
     },
     methods: {
         async sendReq() {
             const resp = await axios.get("/api/students")
             // 要把服务端返回的data数据赋值给我们的data方法里的students
             // 因为我们页面上的这些模板需要进行数据绑定,或者数据需要进行条件判断,
             // 数据必须来自我们options的data数据对象,不可以直接来自response数据
             // console.log(resp.data.data)
             this.students = resp.data.data
         }
     },
 };
 export default options
 </script>
 ​
 <!-- scoped: 样式仅影响当前组件,不影响其他组件 -->
 <style scoped>
 div {
     font-family: 华文行楷;
     font-size: 20px;
 }
 ​
 .title {
     margin-bottom: 10px;
     font-size: 30px;
     color: #333;
     text-align: center;
 }
 ​
 .row {
     background-color: #fff;
     display: flex;
     justify-content: center;
 }
 ​
 .col {
     border: 1px solid #007acc;
     width: 15%;
     height: 35px;
     text-align: center;
     line-height: 35px;
 }
 ​
 .bold .col {
     background-color: #f1f1f1;
 }
 </style>

但是当前的页面实现是,我们必须点一下按钮,才去服务器获取学生数据,能不能加载页面的时候就获取呢?

只需要在options对象里再加入一个方法属性mounted

代码语言:javascript复制
<script>
import axios from '../util/myaxiso'
const options = {
    data: function () {
        return { students: [] };
    },
    methods: {
        async sendReq() {
            const resp = await axios.get("/api/students")
            this.students = resp.data.data
        }
    },
    mounted:function(){
        this.sendReq()
    }
};
export default options
</script>

这样就可以在加载页面的时候就获取数据了。

注意:

  • v-if 和 v-for 不能用于同一个标签
  • v-for 需要配合特殊的标签属性 key 一起使用,并且 key 属性要绑定到一个能起到唯一标识作用的数据上,本例绑定到了学生编号上
  • options 的 mounted 属性对应一个函数,此函数会在组件挂载后(准备就绪)被调用,可以在它内部发起请求,去获取学生数据
8. 重用组件

我们页面上肯定有很多HTML 或者JS 代码,具备一定的重用性,那么我们可不可以吧这些可重用的代码集中起来,形成一个可重用的组件呢? 可重用的组件一般放在/src/components里:

按钮组件

代码语言:javascript复制
 <template>
     <div class="button primary middle">
         a<slot></slot>b
     </div>
 </template>
 <script>
 const options = {};
 export default options;
 </script>
 <style scoped>
 ...
 </style>
  • 注意,省略了样式部分
  • <slot>:插槽,起到占位的作用,后面你在父组件里my-button标签里写的数据会被展示到页面,否则子组件是不会使用父组件里写在my-button里的数据的。

使用组件

  1. 导入子组件。
  2. 页面内导入子组件标签。
代码语言:javascript复制
 <template>
     <div>
         <h1>父组件</h1>
         <!--2. 页面内导入子组件标签-->
         <my-button>1</my-button>
         <my-button>2</my-button>
         <my-button>3</my-button>
     </div>
 </template>
 <script>
 // 导入子组件
 import MyButton from '../components/MyButton.vue'
 const options = {
     components: {
         // 简写形式:
         // "MyButton":MyButton
         MyButton
     }
 };
 export default options;
 </script>
  • components属性:表示当前父组件要使用哪些子组件。
  • vue里子组件名字为大驼峰对应的HTML标签就是my-button

但是上面那种子组件还不够通用,他的颜色和大小样式是写死的,那能不能让他采用什么样式都是由父组件传入的呢?

采用自定义属性

代码语言:javascript复制
 <template>
     <div>
         <h1>父组件</h1>
         <!--2. 页面内导入子组件标签,-->
         <my-button type="primary" size="small">1</my-button>
         <my-button type="danger" size="middle">2</my-button>
         <my-button type="success" size="large">3</my-button>
     </div>
 </template>
 <script>
 // 导入子组件
 import MyButton from '../components/MyButton.vue'
 const options = {
     components: {
         // 简写形式:
         // "MyButton":MyButton
         MyButton
     }
 };
 export default options;
 </script>
代码语言:javascript复制
 <template>
     <div class="button" v-bind:class="[type,size]">
         a<slot></slot>b
     </div>
 </template>
 <script>
 const options = {
     props: ["type", "size"]
 };
 export default options;
 </script>
 <style>
 ...
 </style>
  • props:自定义属性,父组件向子组件传值。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞