Vue2.0原理篇

2023-03-04 12:52:19 浏览数 (2)

Vue2.0 原理篇

    • *本文仅对Vue原理作简要概述/总结,具体实现不做详解*
  • 创建 Vue实例的两种写法
    • el挂载容器
    • mount()挂载容器
    • 总结
  • data的2种写法
    • 对象式
    • 函数式
    • 总结
  • 模板语法
    • 插值语法
    • 指令语法
    • 总结
    • js表达式
    • js语句
  • Vue中的MVVM模型
    • 总结
  • Vue响应式数据原理
    • 总结
  • 计算属性computed
    • 什么是计算属性
    • 原理
  • 侦听属性watch
    • 什么是侦听器
    • 什么是深度侦听
  • computed与watch区别
  • 样式绑定
    • class样式
    • style内联样式
  • 条件渲染
  • Vue核心—Differ算法
  • Vue核心—虚拟DOM
  • Vue工作流程
  • 列表渲染/key的选择
    • index作key缺点
    • id作key优点
  • v-model注意事项
  • 过滤器
  • 自定义指令
  • VueComponent构造函数
  • Vue原型链
  • render函数
  • ref属性
      • 作用
      • 语法
      • 注意
  • props属性
      • 功能
  • 语法
      • 传递数据
      • 接收数据
      • 注意
      • 应用场景
  • mixin混入
      • 功能
      • 使用方式
      • 注意
  • 自定义事件
      • 绑定自定义事件
      • 触发自定义事件
      • 解绑自定义事件
      • 注意
      • 应用场景
  • 全局事件总线
      • 安装全局事件总线
      • 使用全局事件总线
      • 提供数据:
      • 注意
      • 应用场景
  • 消息订阅与发布
      • 使用步骤
      • 注意
      • 应用场景
  • vuex
      • 定义
      • 使用步骤
      • 应用场景

本文仅对Vue原理作简要概述/总结,具体实现不做详解

创建 Vue实例的两种写法

el挂载容器

代码语言:javascript复制
new Vue ({ 
	el: '#root' ===>> document.getElementById('root')
})

mount()挂载容器

代码语言:javascript复制
new Vue({...}).mount('#root')===>> document.getElementById('root')

总结

  1. 通过el 或 mount()来挂载容器,其底层都是通过document.getElementById(‘root’)来操作
  2. 挂载:即在组件创建完毕后,将DOM结构放入页面的操作

data的2种写法

对象式

代码语言:javascript复制
new Vue({
data:{...}
})

注意:对象式只能写在vm中(new Vue中)

函数式

代码语言:javascript复制
data:function(){}
可简写为:
data(){
	return {...}
}

注意:

  1. 组件中只能用函数式写法,且有return
  2. 因为在复用组件的时候,data对象只是被复用了“引用”,一 个组件中的data数据改变,所有组件中的data数据都会改变

总结

  1. 对象式只能写在new Vue中
  2. 组件中只能用函数式写法,且有return
  3. data不能使用箭头函数。因为箭头函数没有this,默认指向其父级函数指向的对象。
  4. 在这里普通函数的this指向 vm(Vue的实例) ,或 组件的实例对象

模板语法

模板语法分为2大类:插值语法、指令语法

插值语法

语法格式:{{ value }}

功能:用于解析标签体内的内容,即写在标签体

举例:

代码语言:javascript复制
<h1> {{value}}</h1>

指令语法

语法格式:v-xxx

功能:用于解析标签(可解析标签属性、标签体内容、事件绑定…),即写在头标签内

举例:

代码语言:javascript复制
<h1 v-xxx:key="value"></h1>

总结

  1. 都将其value视为JavaScript表达式解析
  2. 注意区分js表达式和js语句

js表达式

js表达式:即会计算并返回一个值的算数运算

举例:

  • a b
  • x === y ? ‘a’ : ‘b’

js语句

js语句:即控制代码走向的语句

举例:

  • if() { }条件语句
  • for() { }循环语句

Vue中的MVVM模型

MVVM是什么,很多文章有介绍,这里就不废话直接总结!

总结

代码语言:javascript复制
<div id="root"></div> ===>> view
<script>
	new Vue({ ===>> view model
		data(){} ==>> model
	})
</script>

  1. data中的数据变化都会被vm侦听到,并响应到root中
  2. root中的数据变化都会被vm侦听到,并响应到data中

Vue响应式数据原理

由于响应式数据涉及到:数据代理、数据劫持、Object.definepropetry()等操作,这里就不废话,直接总结!

总结

备注:

1). get()方法 ===> getter 2). set()方法 ===> setter 3). 知道这个东西就行不多解释,面试的时候用getter/setter会更专业

总结:

  1. Vue会为data中的每一个属性都添加一个get()和set()方法
  2. data中数据的变化,实际是调用了set()方法,修改数据
  3. 当数据变化会被VM侦听到,自动调用属性的get()方法获取最新的数据,实现响应式数据变化
  4. v-model的原理也是这样的

计算属性computed

什么是计算属性

  1. 计算属性就是computed的一个属性。
  2. 通过计算已有的属性,得到一个返回值。这个返回值就是计算属性的值。
  3. 已有的属性:vm中存在的属性,常为data中的属性

语法:

代码语言:javascript复制
computed:{
	计算属性名(){
		return {
			-- 计算操作 --
		}
	}
}

原理

备注:

1). get()方法 ===> getter 2). 知道这个东西就行不多解释,面试的时候用getter会更专业 计算属性原理与响应式数据原理相似

原理:

  1. 当计算属性被调用时,get()就会被调用
  2. get()拿到vm中的已有属性进行计算
  3. get()自动返回计算结果,作为计算属性的值

注意:computed不能进行异步操作!eg:计算属性里不能用定时器

侦听属性watch

什么是侦听器

  1. 监听一个数据,当该数据变化时,侦听器会拿到这个数据的新值与旧值,程序员可以对这两个值进行一些操作
  2. 即当数据变化时,就立即执行对应的函数,对数据进行操作。

语法:

代码语言:javascript复制
watch:{
	侦听的数据(参数1,参数2){ //参数1接收新值,参数2接收旧值
		-- 对数据进行操作 --
	}
}

什么是深度侦听

  1. Vue中的watch默认只能侦听data中第一层对象的变化
  2. 深度侦听可以侦听到data中多层结构中所有属性的变化
  3. 若data中数据又嵌套,则需开启深度侦听

computed与watch区别

  1. computed可以完成的功能,watch都可以完成
  2. watch能完成的功能,computed不一定能完成。eg:watch可以完成异步操作,computed不可以
  3. watch:只侦听单个数据,无返回值。即不需要return。直接在内部通过this操作data中的数据
  4. computed:侦听多个数据,返回计算结果。即需要return

样式绑定

class样式

语法: class="xxx" xxx可以是字符串、对象、数组

字符串:最常用的方式,直接写类名。最常用

对象、数组:可根据数据结构中,数组和对象的优势,按需使用(知道有这2种写法即可,不做详解)

注意:若类名以array或object类型存放在data中,class需用“v-bind”绑定,即:class="xxx"

style内联样式

语法:

  1. :style="{key:value}"其中value为动态值,key若为复合词,则用小驼峰
  2. :style="[key1,key2]"其中key为样式对象,很少使用数组形式

条件渲染

  1. v-show===>> 底层通过display:none/block来控制元素显示与隐藏,该元素仍存在与DOM结构中。当显示与隐藏频率高时使用性能最佳
  2. v-if===>> 直接删除/添加元素。删除后DOM结构中没有该元素。

Vue核心—Differ算法

Differ即different(不同的),即将两个数据进行对比,找出两个数据之间的不同。

Vue核心—虚拟DOM

虚拟DOM也称VDOM,V即virtual(虚拟的)的简写

  1. Vue会根据vm生成一个虚拟DOM(这个虚拟DOM不会被直接渲染到页面)
  2. Vue再将虚拟DOM,渲染到页面(el或mount挂载容器,即为该步操作) 详情见下一章节 Vue工作流程

Vue工作流程

备注:

  1. VDOM ===>> 虚拟DOM
  2. VNode ===>> 虚拟节点

Vue工作流程:

  1. 根据初始数据生成VDOM
  2. 将VDOM 转成 真实DOM

当数据更新

  1. 根据新的数据生成新的VDOM
  2. 通过key将新的VDOM与旧的VDOM对比(Differ算法)
  3. 若旧的Vnode与新的Vnode不一样,则用新的Vnode替换旧的Vnode,渲染到页面
  4. 若旧的Vnode与新的Vnode一样,则将旧的Vnode复用,不进行任何操作。

注意:key的选择键下一章节

列表渲染/key的选择

  1. 列表渲染v-for的使用就不做多概述了。
  2. key的作用:节点的唯一标识

index作key缺点

  1. 数据错乱

若打乱的原始数据的顺序,node的index会改变,会导致在Differ对比时,对比的不是同一Node。(因为Differ将相同index的Node进行对比,而Node的index已经改变。如原来的index=1,而现在index=2。所以对比的不是同一节点。)

  1. 效率低

由于前后对比的不是同一Node,则Node不能复用,所有的Vnode都需要转成 真实的 Node( 整棵真实DOM数都被替换 )

  1. DOM结构混乱

若DOM结构中还有输入类的元素,会产生错误的DOM更新 ==>> 界面显示的DOM结构错位( 输入的内容为真实内容,不会出现在Vnode中,Differ在对比时,只能对比标签,标签里没有内容,而标签都是一致的则将标签复用, 因而在Vnode顺序改变,但真实内容还是显示在原来的位置,导致页面显示错位 )

id作key优点

  1. 效率高,无数据错乱问题
  2. 不管怎么改变顺序,id值是唯一的,不会改变,真实DOM数中只有部分Node被重写 不写key:Vue默认将 index作为key

key的选择:可为id、手机号、学号、账号…( 大型项目会出现id穷尽的现象 )

v-model注意事项

注意事项

  • text类型表单,则v-model收集的是表单value的值,用户输入的就是value值
  • radio类型表单,则v-model收集的是表单value的值,要给表单配置不同的value值
  • checkbox类型表单: 1. 未配置input的value值,则v-model收集的是checked(勾选 or 未勾选,是布尔值) 2. 配置了input的value值 * v-model的初始值是非数组,则v-model收集的是checked(勾选 or 未勾选,是布尔值) * v-model的初始值是数组,则收集的就是value组成的数组

注意

**v-model的3个修饰符

  • lazy:失去焦点再收集数据
  • number:将输入的字符串转为数字
  • trim:过滤输入首位空格

过滤器

过滤器的本质就是一个函数

功能:将要显示的数据,进行一定的格式化后,再显示

注意:没有改变原数据,产生的是新数据

局部过滤器语法: 调用:

  • 插值语法调用<xx>{{被过滤的对象|过滤器}}</xx>
  • 属性语法调用<xx:属性="被过滤的对象|过滤器"}></xx> 属性语法很少用

Vue通过管道符"|",自动将被过滤的对象作为实参传入过滤器,不需要我们手动传参

声明:

代码语言:javascript复制
filters: {//与data同级
	过滤器() {//这里要用形参来接收
		return --过滤操作--
	}
	//可以写多个过滤器
}

全局过滤器语法:

代码语言:javascript复制
Vue.filter( ' 被过滤的对象 ',function () { 
	 return --过滤数据操作--
	}
})  //写在实例化Vue之前

原理:

  1. 在插值表达式中,将被格式化的对象,作为参数传给过滤器。
  2. Vue自动调用过滤器,解析完后,自动将插值表达式替换为,解析后的结果

注意:

  1. 多个过滤器使用 管道符 分割。{{ 被格式化的对象 | 过滤器1 | 过滤器2 | 过滤器3 }}
  2. 在调用过滤器时,可以传参,用第二个形参接收传入的参数第一个形参接收的是 管道符 前的对象,Vue通过管道符自动调用该参数,不需要手动传参

自定义指令

** 定义语法**

  • 局部指令:
代码语言:javascript复制
new vue({
	directives:{指令名:配置对象} 
})

代码语言:javascript复制
new vue({
	directives:{指令名:回调函数} 
})
  • 全局指令
代码语言:javascript复制
Vue.directive(指令名,配置对象)

代码语言:javascript复制
Vue.directive(指令名,回调函数)

** 注意**

  • 定义指令时,不加 v-。使用时要加v-
  • 指令名若为复合词,则使用“-”连接,不用使用小驼峰或大驼峰

VueComponent构造函数

作用

生成组件的实例化对象

注意

  • 我们创建的组件,本质上就是一个VueComponent构造函数
  • 这个构造函数不需要我们去定义,由Vue自动生成

Vue实例化流程

  1. new Vue 创建Vue的实例vm 若App.vue文件中有我们自定义的组件标签
  2. Vue自动调用Vue.extend生成VueComponent构造函数
  3. 自动new VueComponent(options)生成组件的实例化对象,即vc

Vue原型链

注意:本节需要对原型原型链有一定了解。否则你看不懂!!!

Vue原型链

  1. 组件的实例vc的__proto__,指向VueComponent的原型对象

2.VueComponent的__proto__,指向Vue的原型对象

  1. Vue原型对象的__proto__,指向顶级对象Object的原型对象

VueComponent.prototype.proto === Vue.prototype

render函数

代码语言:javascript复制
new Vue({
	...
	render:h=>h(App)
})

这个你很眼熟吧,让我看看他的前世今生

render函数完整写法

代码语言:javascript复制
new Vue({
	...
	render(createdElement){ // createdElement是一个函数,用于创建元素
		return createdElement(App) // 将App作为实参,调用createdElement函数,创建App对应的元素
	}
})

render函数简写:因为不需要this,因而可以简写为箭头函数

代码语言:javascript复制
new Vue({
	...
	render:h=>h(App) // h只是一个形参,用其他字母也可
})

ref属性

作用

  • ref属性被用来给元素或者组件注册引用信息
  • this.$refs.xxx ===>> document.getElnmentById(‘xxx’),二者功能一样,但Vue不建议直接操作DOM,ref相当于id的代替者

语法

  • ref绑定在HTML标签上,得到的是真实的DOM元素
代码语言:javascript复制
<h1 ref="xxx"></h1>  // 绑定HTML标签

  • ref绑定在组件标签上,得到的是组件实例对象vc
代码语言:javascript复制
<组件 ref="xxx"></组件>  // 绑定组件标签

注意

  • this.$refs得到的是相应的真实的DOM元素
  • this.$refs.xxx得到的是具体的模板内容

props属性

功能

  • 让组件接收外部传来的数据

语法

传递数据

代码语言:javascript复制
<组件 name="xxx"></组件>

接收数据

代码语言:javascript复制
props:{
	name:{
		type:String, // 指定数据类型
		required:true, // 是否必须属性
		default:*** // 指定默认值
	}
}

注意

  • props是只读的
  • 不能直接修改props中的数据
  • 若要修改,将props中的数据复制一份到data中,进行相应的操作
  • v-model的值不能是props传来的值,因为props是不可修改的
  • props传来的若为对象类型的值,可以修改对象中属性的值,但不推荐这样做

应用场景

  • 父组件===>>子组件 通信
  • 子组件===>>父组件 通信(父组件要先给子组件一个函数)

mixin混入

功能

  • 将可复用的js代码封装到一个文件夹中

使用方式

  1. 在src下创建mixin.js文件
  2. 定义混入代码,mixin是一个对象
代码语言:javascript复制
export const xxx={ // 三种导出方式,按需使用即可
	data({...}),
	methods:{...}
	... // 可使用组件中的所有配置属性
}

  1. 使用混入 a. 全局混入:Vue.mixin(xxx)//慎用 b. 局部混入:mixins:[‘xxx’]

注意

  • 若混入的数据,与组件中的语句冲突,则以组件中的数据为准
  • 钩子函数冲突,则全部使用

自定义事件

绑定自定义事件

代码语言:javascript复制
<组件 @自定义事件="回调函数" ref="xxx"></组件> //法一
代码语言:javascript复制
mounted(){ // 法二
	this.$refs.xxx.$on('自定义事件',回调)
}

触发自定义事件

代码语言:javascript复制
this.$emit('自定义事件',数据) // 法一

解绑自定义事件

代码语言:javascript复制
this.$off('自定义事件')

注意

  • 若想事件只触发一次,可使用once修饰符,或者$once()方法
  • 组件上也可以绑定原生DOM事件,但需要使用native修饰符
  • this.refs.xxx.on('自定义事件',回调)绑定自定义事件时,回调函数要么配置在methods中,要么用箭头函数直接定义,否则会出现this指向问题!

应用场景

  • 子组件===>>父组件 通信

全局事件总线

安装全局事件总线

代码语言:javascript复制
new Vue({
	...
	beforeCreate(){
		Vue.prototype.$bus=this // $bus就是当前应用的vm
	}
})

使用全局事件总线

  1. 接收数据:组件想接收数据,则在组件中给$bus绑定自定义事件,事件的回调留在组件中
代码语言:javascript复制
mounted(){
	this.$bus.$on('事件',回调)
}

提供数据:

  • this.bus.emit('事件',数据)
  • 将数据作为实参传递给回调函数
  1. 最好在beforeDestory钩子中,用$off解绑当前组件所使用的所有事件

注意

  • 回调函数可以写在methods中,直接写在mounted中记得用箭头函数
  • this.bus.on注册事件,在回调中通过形参拿到数据,对数据进行处理
  • this.bus.emit触发事件,将第二个参数作为实参(即数据)
  • 在new Vue()中创建全局事件总线。

bus可以自定义,建议使用

bus规范

应用场景

  • 任意组件之间通信

消息订阅与发布

  • 原理和全局事件总线一样,建议使用事件总线,毕竟Vue出品

使用步骤

1.安装pubsub: npm i pubsub-js 2. 引入pubsub:import pubsub from ‘pubsub-js’ 3. 接收数据:A组件想接收数据,则在A组件中订阅消息,回调留在A组件自身

代码语言:javascript复制
mounted(){
	this.xxx=pubsub.subscribe('事件',回调)
}

4.提供数据:pubsub.publish('事件',数据)

注意

  • 记得在beforeDestory钩子中用pubsub.unsubscribe(xxx)取消订阅 回调函数可在methods中,直接写记得用箭头函数 第一个形参为订阅的消息名,第二个形参才是数据。 第一个形参不需要使用,常用_下划线占位

应用场景

  • 任意组件间通信

vuex

定义

  • 专门在Vue中实现集中式状态(数据)管理的一个插件

使用步骤

  • 使用比较复杂,在这里就不做详解

应用场景

  • 多个组件之间状态(数据)共享
  • 多个组件依赖于同意状态
  • 不同组件的行为需要变更同一状态

0 人点赞