最近做了一个关于Vue的内部运行机制的分享会,记录一番笔记。
Vue的实现首先是通过Vue类里面构造函数中所执行的init()。 _init的函数里面主要包括:
props、data,method、computed、watchers
)。vm.$mount()
挂载组件初始化以及挂载包括了Vue实例的整个前半的生命周期,在这个过程中,Vue完成了模板到真实DOM的显示,以及data与View的响应式绑定监控。
obj
、prop
、descriptor
configureable
(数据描述符 & 存取描述符)
当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默 认为 false。emunerable
(数据描述符 & 存取描述符)
当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。writable
(数据描述符)
当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。value
(数据描述符)
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。set
(存取描述符)
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认undefined。get
(存取描述符)
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。默认undefined。Object.definePrototype
get
中执行dep.depend()
进行依赖绑定set
中执行dep.notify()
通知观察者进行视图更新Watcher
,并将它的getter绑定为vm._update(vm._render())
Dep.target
设置为自生观察者实例,执行getter操作,即为执行了vm._update()
vm._update()
中如果需要渲染某个数据就会触发本身的getter
,完成依赖收集set
中的dep.notify()
通知观察者进行视图更新
* notify
调用dep.subs
中的每一个watcher
的run()
进而触发watcher
的getter()
* 进而触发vm._update(vm._render())
进行重新渲染VNode与patch
* 在patch中将新老的VNode进行diff算法分析,找到最小结构,进而更新到真实的DOM上 let uid=0
class Dep {
constructor(){
this.id = uid++
this.subs=[]
}
addSubs(){
this.subs.push(Dep.target)
}
depend(){
if(Dep.target){
this.addSubs()
}
}
/*通知所有订阅者*/
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
/*
一个解析表达式,进行依赖收集的观察者,同时在表达式数据变更时触发回调函数。它被用于$watch api以及指令
*/
class Watcher{
constructor(expns){
Dep.target=this
this.getter = expns
this.value = this.get()
}
run () {
this.get()
}
update(){
this.run()
}
get(){
return this.getter.call(vm)
}
}
// 存取描述符(数据描述符)
function defineReactive(obj,key,val){
let dep = new Dep()
Object.defineProperty(obj,key,{
enumerable:true,//可以修改
configurable:true,//可以出现在对象枚举属性
get:()=>{
//依赖收集
if (Dep.target) {
/*进行依赖收集*/
dep.depend()
}
return val
},
set:newVal=>{
val=newVal
dep.notify()
}
})
}
function observe (value){
Object.keys(value).forEach(key => {
defineReactive(value,key,value[key])
});
}
/*代理*/
function _proxy (data) {
const that = this;
Object.keys(data).forEach(key => {
Object.defineProperty(that, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return that._data[key];
},
set: function proxySetter (val) {
that._data[key] = val;
}
})
});
}
let vm
class Vue{
constructor(options){
vm = this
//在源码中是通过代理的方式会将_data代理成vm.data
this._data=options.data
_proxy.call(this, options.data);/*构造函数中*/
observe(this._data)
// 挂载
this._mount()
}
_update(){
for (const key in this._data) {
if (this._data.hasOwnProperty(key)) {
console.log('_update渲染更新视图~',this._data[key]);
}
}
}
_mount(){
let updateComponent
updateComponent = () => {
vm._update()
}
//注册一个观察者
vm._Watcher=new Watcher(updateComponent)
}
}
let app = new Vue({
el:'#app',
data:{
text:'text1',
text1:'text2'
}
})
//修改
app.text1='0'
在响应式更新数据的过程中,如果一个数据的值在一段时间内频繁更新了很多次,会依次触发响应式setter->Dep->Watcher->update->patch,所以引入nextTick的异步更新策略,实现一个queue队列,会在下一个tick去执行一次上面的响应式更新操作,大大优化了性能。
parse
——分析optimize
——优化generate
——生成staticRenderFns
以及render
函数会被转换成Funtion对象