动力节点老杜Vue笔记第三章 Vue组件化

3 Vue组件化

3.1 什么是组件

  1. 传统方式开发的应用

一个网页通常包括三部分:结构(HTML)、样式(CSS)、交互(JavaScript)

传统应用存在的问题:

  1. 关系纵横交织,复杂,牵一发动全身,不利于维护。
  2. 代码虽然复用,但复用率不高。
  3. 组件化方式开发的应用


使用组件化方式开发解决了以上的两个问题:

  1. 每一个组件都有独立的js,独立的css,这些独立的js和css只供当前组件使用,不存在纵横交错。更加便于维护。

  2. 代码复用性增强。组件不仅让js css复用了,HTML代码片段也复用了(因为要使用组件直接引入组件即可)。

  3. 什么是组件?
    组件:实现应用中局部功能的代码和资源的集合。凡是采用组件方式开发的应用都可以称为组件化应用。

  4. 模块:一个大的js文件按照模块化拆分规则进行拆分,生成多个js文件,每一个js文件叫做模块。凡是采用模块方式开发的应用都可以称为模块化应用。

  5. 任何一个组件中都可以包含这些资源:HTML CSS JS 图片 声音 视频等。从这个角度也可以说明组件是可以包括模块的。

  6. 组件的划分粒度很重要,粒度太粗会影响复用性。为了让复用性更强,Vue的组件也支持父子组件嵌套使用。


子组件由父组件来管理,父组件由父组件的父组件管理。在Vue中根组件就是vm。因此每一个组件也是一个Vue实例。

3.2 组件的创建、注册和使用

 <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**组件的创建注册和使用**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7. **</head>**  
8. **<body>**  
9.     **<div** id="app"**>**  
10.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
11.         <!-- 3.使用组件 -->  
12.         **<userlist></userlist>**  
13.         **<userlist></userlist>**  
14.         **<userlogin></userlogin>**  
15.         **<userlogin></userlogin>**  
16.     **</div>**  
17.     **<script>**  
18.         // 1.创建组件  
19.         const userListComponent = Vue.extend({
    
      
20.             template : `  
21.                 **<ul>**  
22.                     **<li** v-for="(user,index) of users" :key="user.id"**>**  
23.                         {
    
    {
    
    index}},{
    
    {
    
    user.name}}  
24.                     **</li>**  
25.                 **</ul>**      
26.             `,  
27.             data(){
    
      
28.                 return {
    
      
29.                     users : [  
30.                         {
    
    id:'001', name:'jack'},  
31.                         {
    
    id:'002', name:'lucy'},  
32.                         {
    
    id:'003', name:'james'}  
33.                     ]  
34.                 }  
35.             }  
36.         })  
37.   
38.         // 1.创建组件  
39.         const userLoginComponent = Vue.extend({
    
      
40.             template : `  
41.             **<div>**  
42.             **<h3>**用户登录**</h3>**  
43.             **<form** @submit.prevent="login"**>**  
44.             账号:**<input** type="text" v-model="username"**><br><br>**  
45.             密码:**<input** type="password" v-model="password"**><br><br>**  
46.             **<button>**登录**</button>**  
47.             **</form>**  
48.             **</div>**  
49.             `,  
50.             data(){
    
      
51.                 return {
    
      
52.                     username : 'admin',  
53.                     password : '123'  
54.                 }  
55.             },  
56.             methods : {
    
      
57.                 login(){
    
      
58.                     alert(this.username + "," + this.password)  
59.                 }  
60.             }  
61.         })  
62.   
63.         const vm = new Vue({
    
      
64.             el : '#app',  
65.             data : {
    
      
66.                 msg : '组件的创建注册和使用'  
67.             },  
68.             // 2.注册组件(局部注册)  
69.             components : {
    
      
70.                 // userlist 就是组件的名字  
71.                 userlist : userListComponent,  
72.                 userlogin : userLoginComponent  
73.             }  
74.         })  
75.     **</script>**  
76. **</body>**  
77. **</html>**

1. 创建组件
   1. const userComponent = Vue.extend({这个配置项和创建Vue实例的配置项几乎是一样的,只是略有差异})
   2. 需要注意的是:
      1. el不能用。组件具有通用性,不特定为某个容器服务,它为所有容器服务。
      2. data必须使用函数形式:return {}
      3. 使用template配置项配置页面结构:HTML。
2. 注册组件
   1. 局部注册
      1. 使用components配置项:components : {user : userComponent},user就是组件名。
   2. 全局注册
      1. Vue.component(‘user’, userComponent)
3. 使用组件
   1. 直接在页面需要使用组件的位置:<user></user>
   2. 也可以这样使用:<user/> (不在脚手架环境中使用这种方式会出现后续元素不渲染的问题。)
4. 创建组件对象也有简写形式:Vue.extend() 可以省略。直接写:{}
5. 组件的命名细节:
   1. 全部小写
   2. 首字母大写,后面全部小写
   3. kebab-case串式命名法
   4. CamelCase驼峰式命名法(这种方式需要在脚手架环境中使用)
   5. 不要使用HTML内置的标签作为组件名称。
   6. 可以使用name配置项来指定Vue开发者工具中显示的组件名。
## 3.3 组件嵌套
```java
<!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**组件嵌套**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7. **</head>**  
8. **<body>**  
9.     **<div** id="root"**></div>**  
10.     **<script>**  
11.         // 创建Y1组件  
12.         const y1 = {  
13.             template : `  
14.                 **<div>**  
15.                     **<h3>**Y1组件**</h3>**  
16.                 **</div>**  
17.             `  
18.         }  
19.         // 创建X1组件  
20.         const x1 = {  
21.             template : `  
22.                 **<div>**  
23.                     **<h3>**X1组件**</h3>**  
24.                 **</div>**  
25.             `  
26.         }  
27.         // 创建Y组件  
28.         const y = {  
29.             template : `  
30.                 **<div>**  
31.                     **<h2>**Y组件**</h2>**  
32.                     **<y1></y1>**  
33.                 **</div>**  
34.             `,  
35.             components : {y1}  
36.         }  
37.         // 创建X组件  
38.         const x = {  
39.             template : `  
40.                 **<div>**  
41.                     **<h2>**X组件**</h2>**  
42.                     **<x1></x1>**  
43.                 **</div>**  
44.             `,  
45.             components : {x1}  
46.         }  
47.         // 创建app组件  
48.         const app = {  
49.             template : `  
50.                 **<div>**  
51.                     **<h1>**App组件**</h1>**  
52.                     **<x></x>**  
53.                     **<y></y>**  
54.                 **</div>**  
55.             `,  
56.             // 注册X组件  
57.             components : {x,y}  
58.         }  
59.         // vm  
60.         const vm = new Vue({  
61.             el : '#root',  
62.             template : `  
63.                 **<app></app>**  
64.             `,  
65.             // 注册app组件  
66.             components : {app}  
67.         })  
68.     **</script>**  
69. **</body>**  
70. **</html>**

嵌套结构:这种结构更加贴切实际项目的开发

3.4 VueComponent & Vue

3.4.1 this

new Vue({})配置项中的this和Vue.extend({})配置项中的this他们分别是谁?

<!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**vm与vc**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7. **</head>**  
8. **<body>**  
9.     **<div** id="app"**>**  
10.         **<mc></mc>**  
11.     **</div>**  
12.     **<script>**  
13.         const myComponent = Vue.extend({
    
      
14.             template : `**<h1></h1>**`,  
15.             mounted(){
    
      
16.                 console.log('vc', this)  
17.             }  
18.         })  
19.   
20.         const vm = new Vue({
    
      
21.             el : '#app',  
22.             components : {
    
      
23.                 mc : myComponent  
24.             },  
25.             mounted() {
    
      
26.                 console.log('vm', this)  
27.             },  
28.         })  
29.     **</script>**  
30. **</body>**  
31. **</html>** 

测试结果:

new Vue({})配置项中的this就是:Vue实例(vm)。
Vue.extend({})配置项中的this就是:VueComponent实例(vc)。
打开vm和vc你会发现,它们拥有大量相同的属性。例如:生命周期钩子、methods、watch等。

3.4.2 vm === vc ???

只能说差不多一样,不是完全相等。
例如:
vm上有el,vc上没有。
另外data也是不一样的。vc的data必须是一个函数。
只能这么说:vm上有的vc上不一定有,vc上有的vm上一定有。

3.4.3 Vue.extend()方法做了什么?

每一次的extend调用返回的都是一个全新的VueComponent函数。
以下是Vue.extend()的源码:


注意:是每一次都会返回一个全新的VueComponent构造函数。是全新的!!!
构造函数有了,什么时候会调用构造函数来实例化VueComponent对象呢?

Vue在解析****时会创建一个VueComponent实例,也就是:new VueComponent()

3.4.4 通过vc可以访问Vue原型对象上的属性

通过vc可以访问Vue原型对象上的属性:

  1. Vue.prototype.counter = 100
  2. console.log(vc.counter) // 100

为什么要这么设计?代码复用。Vue原型对象上有很多方法,例如: m o u n t ( ) ,对于组件 V u e C o m p o n e n t 来说就不需要再额外提供了,直接使用 v c 调用 mount(),对于组件VueComponent来说就不需要再额外提供了,直接使用vc调用 mount(),对于组件VueComponent来说就不需要再额外提供了,直接使用vc调用mount(),代码得到了复用。
Vue框架是如何实现以上机制的呢?

  1. VueComponent.prototype.proto = Vue.prototype

测试:

 <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**测试**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7. **</head>**  
8. **<body>**  
9.     **<div** id="app"**></div>**  
10.     **<script>**  
11.         const userlist = Vue.extend({
    
      
12.             template : `**<div><h1>**用户列表**</h1></div>**`,  
13.         })  
14.         const vm = new Vue({
    
      
15.             el : '#app',  
16.             template : `**<userlist></userlist>**`,  
17.             components : {
    
    userlist}  
18.         })  
19.         console.log(userlist.prototype.__proto__ === Vue.prototype) // true  
20.     **</script>**  
21. **</body>**  
22. **</html>**  

3.4.4.1 回顾原型对象

prototype称为:显示的原型属性,用法:函数.prototype,例如:Vue.prototype
proto__称为:隐式的原型属性,用户:实例.proto,例如:vm.proto
无论是通过prototype还是__proto
,获取的对象都是同一个,它是一个共享的对象,称为:XX的原型对象。
如果通过Vue.prototype获取的对象就叫做:Vue的原型对象。
如果通过User.prototype获取的对象就叫做:User的原型对象。
请看下图:

3.4.4.2 原理剖析

VueComponent.prototype.proto = Vue.prototype

这样做的话,最终的结果就是:Vue、vm、VueComponent、vc都共享了Vue的原型对象(并且这个Vue的原型对象只有一个)。

3.5 单文件组件

  1. 什么是单文件组件?
    1. 一个文件对应一个组件(之前我们所学的是非单文件组件,一个html文件中定义了多个组件)
    2. 单文件组件的名字通常是:x.vue,这是Vue框架规定的,只有Vue框架能够认识,浏览器无法直接打开运行。需要Vue框架进行编译,将x.vue最终编译为浏览器能识别的html js css。
    3. 单文件组件的文件名命名规范和组件名的命名规范相同:
      1. 全部小写:userlist
      2. 首字母大写,后面全部小写:Userlist
      3. kebab-case命名法:user-list
      4. CamelCase命名法:UserList(我们使用这种方式,和Vue开发者工具呼应。)
  2. x.vue文件的内容包括三块:
    1. 结构:HTML代码
    2. 交互:
    3. 样式:
  3. export和import,ES6的模块化语法。
    1. 使用export导出(暴露)组件,在需要使用组件的x.vue文件中使用import导入组件
      1. 默认导入和导出
        1. export default {}
        2. import 任意名称 from ‘模块标识符’
      2. 按需导入和导出
        1. export {a, b}
        2. import {a, b} from ‘模块标识符’
      3. 分别导出

export var name = ‘zhangsan’
export function sayHi(){}

  1. VSCode工具可以安装一些插件,这样在编写x.vue的时候有提示。例如:vetur插件
    1. 使用该插件之后,有高亮显示,并且也可以通过输入 <v 生成代码。
  2. 把之前“组件嵌套”的例子修改为单文件组件



auto rename tag插件



记住一个要领:不管是单文件组件还是非单文件组件,永远都包括三步:创建组件、注册组件、使用组件。
创建vm的代码就不是一个组件了,这个js代码写到一个js文件中即可,一般这个起名:main.js。寓意:入口

还剩最后的一点HTML代码,一般这个文件叫做index.html,代码如下:

如上图,注意引入顺序。
代码执行原理:

  1. 第一步:浏览器打开index.html页面,加载容器
  2. 第二步:加载vue.js文件,有了Vue
  3. 第三步:加载main.js
    1. import App from ‘./App.vue’
    2. import X from ‘./X.vue’
    3. import X1 from ‘./X1.vue’
    4. import Y from ‘./Y.vue’
    5. import Y1 from ‘./Y1.vue’

这样就完成了所有组件以及子组件的创建和注册。

  1. 第四步:创建Vue实例vm,编译模板语句,渲染。

写完之后不能直接运行,浏览器不认识.vue文件,不认识ES6的模块化语法。需要安装Vue脚手架。

3.6 Vue脚手架

3.6.1 确保npm能用(安装Node.js)

Node.js的下载地址:
https://nodejs.org/zh-cn/download/


安装步骤如下:









打开dos命令窗口,输入npm命令。

3.6.2 Vue CLI(脚手架安装)

  1. Vue的脚手架(Vue CLI: Command Line Interface)是Vue官方提供的标准化开发平台。它可以将我们.vue的代码进行编译生成html css js代码,并且可以将这些代码自动发布到它自带的服务器上,为我们Vue的开发提供了一条龙服务。脚手架官网地址:https://cli.vuejs.org/zh

注意:Vue CLI 4.x需要Node.js v8.9及以上版本,推荐v10以上。

  1. 脚手架安装步骤:
  2. 建议先配置一下npm镜像:
    1. npm config set registry https://registry.npm.taobao.org
    2. npm config get registry 返回成功,表示设置成功
  3. 第一步:安装脚手架(全局方式:表示只需要做一次即可)
    1. npm install -g @vue/cli
    2. 安装完成后,重新打开DOS命令窗口,输入vue命令可用表示成功了
  4. 第二步:创建项目(项目中自带脚手架环境,自带一个HelloWorld案例)
    1. 切换到要创建项目的目录,然后使用 vue create vue_pro


这里选择Vue2,
babel:负责ES6语法转换成ES5。
eslint:负责语法检查的。
回车之后,就开始创建项目,创建脚手架环境(内置了webpack loader),自动生成HelloWorld案例。

  1. 第三步:编译Vue程序,自动将生成html css js放入内置服务器,自动启动服务。
    1. dos命令窗口中切换到项目根:cd vue_pro
    2. 执行:npm run serve,这一步会编译HelloWorld案例


ctrl + c停止服务。

  1. 打开浏览器,访问:http://localhost:8080


3.6.3 认识脚手架结构

使用VSCode将vue_pro项目打开:

package.json:包的说明书(包的名字,包的版本,依赖哪些库)。该文件里有webpack的短命令:
serve(启动内置服务器)
build命令是最后一次的编译,生成html css js,给后端人员
lint做语法检查的。

3.6.4 分析HelloWorld程序


可以看到在index.html中只有一个容器。没有引入vue.js,也没有引入main.js
Vue脚手架可以自动找到main.js文件。(所以main.js文件名不要修改,位置也不要随便移动)

接下来就是将之前写的程序拷贝到脚手架中,进行测试。
需要拷贝过来的是:App.vue、X.vue、Y.vue、X1.vue、Y1.vue。
main.js和index.html都不需要了,因为脚手架中有。

只需要将App.vue中的路径修改一下即可:

打开VSCode终端:ctrl + `
在终端中执行:npm run serve
报错了:

导致这个错误的原因是:组件的名字应该由多单词组成。这是eslint进行的es语法检测。
解决这个问题有两种方案:
第一种:把所有组件的名字修改一下。
第二种:在vue.config.js文件中进行脚手架的默认配置。配置如下:

在终端中ctrl + c 两次,终止之前的服务,再次运行命令:npm run serve

3.6.5 脚手架默认配置

脚手架默认配置在vue.config.js文件中进行。
main.js、index.html等都是可以配置的。
配置项可以参考Vue CLI官网手册,如下:

例如配置这两项:
第一个:保存时不检查语法 lintOnSave : false
第二个:配置入口

3.6.6 解释main.js中的render函数

将render函数更换为:template配置项,你会发现它是报错的。说明引入的Vue无法进行模板编译。
原因:Vue脚手架默认引入的是精简版的Vue,这个精简版的Vue缺失模板编译器。

实际引入的vue.js文件是:dist/vue.runtime.esm.js(esm版本是ES6模块化版本)
为什么缺失模板编译器?
Vue包含两部分:一部分是Vue的核心,一部分是模板编译器(模板编译器可能占整个vue.js文件的一大部分体积)。程序员最终使用webpack进行打包的时候,显然Vue中的模板编译器就没有存在的必要了。为了缩小体积,所以在Vue脚手架中直接引入的就是一个缺失模板编译器的vue.js。
这样就会导致template无法编译(注意:标签可以正常编译[package.json文件中进行了配置],说的是template配置项无法编译),解决这个问题包括两种方式:
第一种方式:引入一个完整的vue.js
第二种方式:使用render函数
关于render函数,完整写法:


这个函数被vue自动调用,并且传递过来一个参数createElement。
简写形式可以使用箭头函数:

3.7 props配置

使用props配置可以接收其他组件传过来的数据,让组件的数据变为动态数据,三种接收方式:

  1. 简单接收

props : [‘name’,’age’,’sex’]

  1. 接收时添加类型限制

props : {
name : String
age : Number
sex : String
}

  1. 接收时添加类型限制,必要性限制,默认值

props : {
name : {
type : Number,
required : true
},
age : {
type : Number,
default : 10
},
sex : {
type : String,
default : ‘男’
}
}
其他组件怎么把数据传过来?

注意事项:

  1. 不要乱接收,接收的一定是其它组件提供的。
  2. props接收到的数据不能修改。(修改之后会报错,但页面会刷新。)可以找个中间变量来解决。

3.8 从父组件中获取子组件

在组件上使用ref属性进行标识:

在程序中使用 r e f s 来获取子组件: t h i s . refs来获取子组件: this. refs来获取子组件:this.refs.userJack
访问子组件的属性:
this. r e f s . u s e r J a c k . n a m e 访问子组件的子组件属性: t h i s . refs.userJack.name 访问子组件的子组件属性: this. refs.userJack.name访问子组件的子组件属性:this.refs.userJack. r e f s . n a m e r e f 也可以使用在普通的 H T M L 标签上,这样获取的就是这个 D O M 元素: < i n p u t t y p e = ” t e x t ” r e f = ” u s e r n a m e ” > t h i s . refs.name ref也可以使用在普通的HTML标签上,这样获取的就是这个DOM元素: <input type=”text” ref=”username”> this. refs.nameref也可以使用在普通的HTML标签上,这样获取的就是这个DOM元素:<inputtype=textref=username>this.refs.username

3.9 mixins配置(混入)




运行效果:

可以看到以上Vip.vue和User.vue代码中都有相同的methods,这个代码可以复用吗?可以使用mixins配置进行混入。实现步骤:
第一步:提取
单独定义一个mixin.js(一般和main.js在同级目录),代码如下:

第二步:引入并使用

以上演示的是方法methods的混入,实际上混入时没有限制,之前所学的配置项都可以混入。
混入时会产生冲突吗?已经有一个方法a了,如果再混入一个a方法会怎样?


通过测试,如果冲突了,会执行组件自身的,不会执行混入的。(这是原则:混入的意思就是不破坏)
但对于生命周期周期钩子函数来说,混入时,会采用叠加方式:


执行结果:

通过测试得知:对于生命周期钩子函数来说,都有的话,采用叠加,先执行混入的,再执行自己的。
以上的混入属于局部混入,只混入到指定的组件当中。
全局混入:

执行结果:

一共四个组件,所以输入四次:mixin mounted

3.10 plugins配置(插件)

给Vue做功能增强的。
怎么定义插件?以下是定义插件并暴露插件。插件是一个对象,对象中必须有install方法,这个方法会被自动调用。

插件一般都放到一个plugins.js文件中。
导入插件并使用插件:

插件对象的install方法有两个参数:
第一个参数:Vue构造函数
第二个参数:插件使用者传递的数据
先学会用插件,后面我们做项目的时候会使用很多插件。到时再体会插件存在的意义。

3.11 局部样式scoped

默认情况下,在vue组件中定义的样式最终会汇总到一块,如果样式名一致,会导致冲突,冲突发生后,以后来加载的组件样式为准。怎么解决这个问题?

另外vue组件的style样式支持多种样式语言,例如:css、less、sass等。如何选择使用呢?

使用less注意安装less-loader:npm i less-loader
App根组件中的样式style不建议添加scoped。

3.12 BugList案例

  1. 先使用静态组件的方式把页面效果实现出来。
    1. App.vue
    2. BugHeader.vue
    3. BugList.vue
    4. BugItem.vue
    5. BugFooter.vue
  2. 在BugList.vue中提供bugList数据,实现动态数据展示。
  3. 保存bug:
    1. 获取用户输入的信息采用双向数据绑定。
      1. 通过Date.now()获取时间戳的方式来搞定id。
    2. 将BugList.vue中的bugList数据提升到父组件App.vue中。
    3. 父组件向子组件传递,采用 :bugList=”bugList”,在子组件当中使用props接收。
    4. 子组件向父组件传递,父组件可以提前定义一个函数,将函数传递给子组件,在子组件中调用这个函数即可。
    5. 该功能的小问题:
      1. 保存完成后自动清空。
      2. 输入为空时不能保存(可以加trim去除空白),并且提示必须输入。
  4. 修改bug的状态
    1. 勾选和取消勾选,会触发click事件或者change事件。
    2. 事件发生后,获取bug的id,将id传递给App组件中的回调函数,遍历数组,拿到要修改的bug对象,更改bug对象的resolved属性值。
  5. 删除bug
    1. 删除时可以调用数组的filter方法进行过滤,将过滤之后的新数组赋值给this.bugList
  6. 统计bug
    1. 第一种:普通计数器统计。
    2. 第二种:数组的reduce方法完成条件统计。
  7. 全选和取消全选
    1. 全选复选框的状态维护:
      1. 已解决的数量 === 总数量 时,勾选。
      2. 全部删除后,应该取消勾选。
    2. 全部删除了可以将footer隐藏。v-show
    3. 全选和取消全选
  8. 清除已解决
    1. 调用数组的filter方法进行过滤,生成新数组,将其赋值给this.bugList
  9. 实现编辑功能
    1. 功能描述
      1. 鼠标移动到描述信息上之后,光标变成小手。
      2. 点击描述信息之后,将描述信息放入文本框。并且同时让文本框获得焦点。
      3. 用户开始修改描述信息(要注意避免将信息修改为空)
      4. 输入修改信息之后,文本框失去焦点,显示修改后的描述信息。
    2. 实现功能的核心技术:
      1. 给bug对象扩展一个具有响应式的editState属性,如果是true表示处于编辑状态,false表示处于未编辑状态:this.$set(bug, ‘editState’, true)
      2. 获得焦点的动作如何完成:
        1. 在文本框上添加ref=”inputDesc”,然后通过this.$refs.inputDesc获取到dom元素,调用focus()让其获取焦点。
        2. 以上操作需要在下一次渲染DOM完成后执行:nextTick
          1. this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …unction(){this.refs.inputDesc.focus()})

3.13 localStorage和sessionStorage

window.localStorage 浏览器关闭,数据还在。
getItem removeItem setItem clear
JSON.stringify
JSON.parse
存储大小5mb
Window.sessionStorage 浏览器关闭清空存储。
getItem的key不存在的话返回null。JSON.parse(null),结果还是null。
改造项目。用本地存储来改造。使用监视属性watch,并且要开启深度监视。

3.14 使用本地存储改造BugList案例

3.15 组件自定义事件

click、keydown、keyup,这些事件都是内置事件。
Vue也支持给组件添加自定义事件。
包括两种方式:
第一种方式:直接在组件标签上绑定事件
第二种方式:通过代码来给组件绑定事件

3.15.1 直接在组件标签上绑定事件


<Car @event1=”doSome”>
表示给Car这个组件vc实例绑定event1事件,当event1事件发生时,doSome方法执行。
事件绑定在谁的身上,谁就负责触发这个事件,怎么触发?在Car组件中定义methods:
methods : {
triggerEvent1(){
// 触发事件并且给事件传数据
this.$emit(‘event1’, this.name, this.age, this.gender)
}
}
然后,在Car的父组件中编写doSome方法:
methods : {
doSome(name, age, gender){}
// 或者可以这样
doSome(name, …parameters){} // …parameters表示采用一个数组接收参数
}
通过这种方式可以轻松完成子组件向父组件传递数据
<Car @event1.once=”doSome”> 表示只触发一次。
<Car @click.native=”doSome”> 使原生事件生效。

3.15.2 通过代码给组件绑定事件

在父组件当中:

mounted(){ // 表示挂载完毕后给组件绑定事件。
// 这种方式更加灵活。例如:希望AJAX请求响应回来数据之后再给组件绑定事件。
this. r e f s . c a r . refs.car. refs.car.on(‘event1’, this.doSome)
}
this. r e f s . c a r . refs.car. refs.car.once(‘event1’, this.doSome) 表示只触发一次。
绑定时要注意:
this. r e f s . c a r . refs.car. refs.car.on(‘event1’, function(){
//这里的this是子组件实例(Car组件实例)
})
this. r e f s . c a r . refs.car. refs.car.on(‘event1’, ()=>{
// 这里的this是父组件实例(App组件实例)
})
this.doSome这个回调函数写成普通函数时:函数体中this是子组件实例。(Car组件实例)
this.doSome这个回调函数写成箭头函数时:函数体中this是父组件实例。(App组件实例)

3.15.3 解绑事件

哪个组件绑定的就找哪个组件解绑:
methods : {
unbinding(){
this. o f f ( ‘ e v e n t 1 ’ ) / / 这种方式只能解绑一个事件。 t h i s . off(‘event1’) // 这种方式只能解绑一个事件。 this. off(event1’)//这种方式只能解绑一个事件。this.off([‘event1’, ‘event2’]) // 这种方式解绑多个事件。
this.$off() // 解绑所有事件。
}
}
注意:vm和vc销毁的时候,所有组件以及子组件当中的事件会全部解绑。

3.16 全局事件总线

原理:给项目中所有的组件找一个共享的vc对象。把这个共享的对象vc叫做全局事件总线。所有的事件都可以绑定到这个共享对象上。所有组件都通过这个全局事件总线对象来传递数据。这种方式可以完美的完成兄弟组件之间传递数据。这样的共享对象必须具备两个特征:

  1. 能够让所有的vc共享。
  2. 共享对象上有 o n 、 on、 onoff、$emit等方法。

第一种解决方案:
在main.js文件中:
// 获取VueComponent构造函数
const VueComponentConstructor = Vue.extend({})
// 创建vc
const vc = new VueComponentConstructor()
// 让所有的vc都能够使用这个vc
Vue.prototype.KaTeX parse error: Expected '}', got '#' at position 57: …e({ el : '#̲app', rend…bus = this**
** }**
})
永远需要记住的:A组件向B组件传数据,应该在B组件中绑定事件(接)。应该在A组件中触发事件(传)。

数据发送方:触发事件
methods : {
triggerEvent(){
this. b u s . bus. bus.emit(‘eventx’, 传数据)
}
}
数据接收发:绑定事件
mounted(){
this. b u s . bus. bus.on(‘eventx’, this.doSome)
}
养成好习惯:组件实例被销毁前,将绑定在KaTeX parse error: Expected '}', got 'EOF' at end of input: …estroy(){ this.bus.off(‘eventx’)
}

3.17 BugList案例改造

3.17.1 使用组件自定义事件改造BugList案例

所有从父向子传递函数的位置,都可以修改为自定义事件方式。
主要改造子向父传数据的功能。

3.17.2 使用全局事件总线改造BugList案例

主要改造爷孙之间数据的传递。
自定义事件在Vue开发者工具当中是可以在看到的。
组件销毁的时候,记得把全局事件总线对象上绑定的事件解绑。

3.18 消息订阅与发布


使用pubsub-js库完成消息订阅与发布。该库可以在任意前端框架中实现消息的订阅与发布。
安装pubsub-js:npm i pubsub-js
程序中引入pubsub:import pubsub from ‘pubsub-js’
引入了一个pubsub对象,通过调用该对象的subscribe进行消息订阅,调用publish进行消息发布。
订阅:subscribe
mounted(){
this.pubsubId = pubsub.subscribe(‘message’, (messageName, data) => {
// 两个参数:第一个是消息的名字。第二个参数是消息发布时传过来的数据。
// 要使用箭头函数。这样才能保证this的使用。
})
}
beforeDestroy(){
pubsub.unsubscribe(this.pubsubId )
}
发布:publish
pubsub.publish(‘message’, ‘zhangsan’, 20)

组件间的通信方式总结:

  1. props:可以完成父向子传数据
  2. 父向子传一个函数:可以完成子向父传数据
  3. 组件自定义事件:可以完成子向父传数据。
  4. 全局事件总线
  5. 消息订阅与发布

3.19 使用消息订阅与发布改造BugList案例

组件销毁时,记得取消订阅。

猜你喜欢

转载自blog.csdn.net/weixin_54585403/article/details/130217638
今日推荐