Vue2
对象类型:通过Object.defineProperty() 对属性的读取、修改进行拦截 ( get / set ) 数据劫持
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
Object.defineProperty( data , 'count' , {
get ( ) { },
set ( ) { }
})
存在问题:
新增属性、删除属性,界面不会更新 this.$set(this.person,'sex','女')
直接通过下边修改数组,界面不会自动更新 this.$set(this.person.hobby,0,'逛街')
Vue3
const person={
name:"zhangsan",
age:19
}
const p = new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`);
return Reflect.get(target,propName)
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新页面!`);
Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新页面!`);
return Reflect.defineProperty(target,propName)
}
})
通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的填写、属性的添加、属性的删除等 通过Reflect(反射):对源对象的属性进行操作
//基本类型的数据:响应式依然是靠 Object.defineProperty() 的 get 与 set 完成的
let name = ref('zhangsan')//字符串通过 ref 加工生成一个引用对象
//对象类型的数据:内部“求助”了Vue3.0 中的一个新函数———— reactive 函数
//ref
// let job = ref({
// type:'qianduan',
// salary:'30k'
// })// job.value —— 一个Proxy对象
//使用reactive定义一个对象类型的响应式数据
let job = reactive({
type:'qianduan',
salary:'30k'
})// job —— 一个Proxy对象
//数组类型
let Hobby = reactive(['a','b','c'])
function changeInfo(){
name.value = "lisi"
// ref
// job.value.type = "UIsheji"
// job.value.salary = "40k"
//reactive
job.type = "UIsheji"
job.salary = "40k"
Hobby[0] = 'd'
}
vue 中 template 到 render的过程
简单来说分为三步
- 通过parse 将template转成AST 抽象语法树
解析过程是利用正则表达式按顺序解析模板,当执行到开始标签、结束标签、文本的时候都会调用相对应的回调函数,最终生成AST树。AST元素节点有三种类型:type为1表示普通元素,2为表达式,3为纯文本
- 通过optimize对静态节点进行优化
optimize可以给AST树中的静态节点打上一个标记,标记之后的节点生成的DOM永远不会改变,后续的更新渲染也会跳过这些被标记的节点。
- 通过generate将AST树生成render函数代码
mixin
可以把多个组件共用的配置抽离出来,提取成一个混入对象 mixin的data、props、methods、watch等都只会将自己有的但是组件上没有的内容混合到组件上;而生命周期hooks则会全部都混合到组件上,不管组件上是否已经定义了生命周期钩子函数,并且混入的钩子函数先于组件的钩子函数调用。
响应式数据以及收集依赖和派发更新
初始化Vue的时候 initState 通过 defineReactive 将数据变成响应式对象(为数据属性新增getter 和setter)其中getter部分就是用来收集依赖 dep.depend();setter部分是用来派发更新 dep.notify(). 在收集依赖中还有一个小细节是,vue中在每次添加完新的订阅,会移除掉旧的订阅。