前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue的内部运行机制

Vue的内部运行机制

作者头像
刘亦枫
发布2020-05-18 23:12:50
6170
发布2020-05-18 23:12:50
举报

Vue的内部运行机制

最近做了一个关于Vue的内部运行机制的分享会,记录一番笔记。

目录大纲

  • 概述
  • 初始化及挂载
  • 响应式的实现
  • 编译
  • Virtual DOM和数据更新时的patch()

概述

Vue.js的内部运行机制
Vue.js的内部运行机制
  • 初始化
    • 初始化生命周期、事件、render、state
    • Object.definePrototy() 响应式的定义
  • 挂载 (运行时 + 编译器)
    • template到render Function的编译
    • 注册watcher
    • 响应式的触发
  • patch的diff算法更新机制
  • nextTick的异步更新策略

初始化及挂载

初始化及挂载
初始化及挂载

Vue的实现首先是通过Vue类里面构造函数中所执行的init()_init的函数里面主要包括

  1. 主要包括初始化生命周期、初始化事件、初始化render,初始化state(包括props、data,method、computed、watchers)。
  2. 执行vm.$mount()挂载组件

初始化以及挂载包括了Vue实例的整个前半的生命周期,在这个过程中,Vue完成了模板到真实DOM的显示,以及data与View的响应式绑定监控。

响应式的实现

关于Object.definePrototy()
  • Object.definePrototy() :
    • 概念:直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
    • 参数objpropdescriptor
    • 类型:数据描述符、存取描述符
    • 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。
依赖注入和更新视图
依赖注入和更新视图
依赖注入和更新视图
  • 一个Vue类的构造函数中绑定data
  • 为data注册 Observer
    • 对data中的每个key值调用Object.definePrototype
    • get中执行dep.depend()进行依赖绑定
    • set中执行dep.notify()通知观察者进行视图更新
  • 在$mouted执行挂载(运行中+编译器版本)
    • 编译模板将data对象和vue语法声明的模板编译成浏览器可读的html
    • 注册一个观察者Watcher,并将它的getter绑定为vm._update(vm._render())
    • 在将Dep.target设置为自生观察者实例,执行getter操作,即为执行了vm._update()
    • vm._update()中如果需要渲染某个数据就会触发本身的getter,完成依赖收集
  • 当某个值被改变的时候 * 触发set中的dep.notify()通知观察者进行视图更新 * notify调用dep.subs中的每一个watcherrun()进而触发watchergetter() * 进而触发vm._update(vm._render())进行重新渲染VNode与patch * 在patch中将新老的VNode进行diff算法分析,找到最小结构,进而更新到真实的DOM上
简单代码示例
代码语言:javascript
复制
		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'
nextTick的异步更新策略

在响应式更新数据的过程中,如果一个数据的值在一段时间内频繁更新了很多次,会依次触发响应式setter->Dep->Watcher->update->patch,所以引入nextTick的异步更新策略,实现一个queue队列,会在下一个tick去执行一次上面的响应式更新操作,大大优化了性能。

编译

编译
编译
  • baseCompile
    • parse——分析
    • optimize——优化
    • generate——生成
  • createCompileToFunctionFn
    • 将编译结果放入缓存
    • 同时staticRenderFns以及render函数会被转换成Funtion对象

Virtual DOM和数据更新时的patch()

VNode
VNode
VNode
  • 产生原因,对于大应用来说直接操作DOM来修改视图是一个很大的花销。
  • Vue.js将DOM抽象成一个以JavaScript对象为节点的虚拟DOM树,对这棵抽象树进行节点的增删查改。
  • 优点
  1. 不需要操作真实的DOM,只操作JavaScript对象
  2. 修改以后经过diff算法得出需修改的最小单位,更新映射到真实的DOM上面去,提高性能。
  3. 真实DOM的一层抽象,而不依赖某个平台,可在weex、浏览器平台、甚至是node平台使用
patch
patch
数据更新时的patch()
  • 数据发生修改会触发vm._update(vm._render())
  • vm._render()返回最新的template的Vnode模板
  • vm._update()中获取到新旧VNode节点进行vm._patch__()函数对比
    • 对比过程主要包括对数同层叶子节点的比较
  • 进而对真实DOM进行一系列的增删查改
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/08/19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Vue的内部运行机制
      • 目录大纲
        • 概述
          • 初始化及挂载
            • 响应式的实现
              • 关于Object.definePrototy()
              • 依赖注入和更新视图
              • nextTick的异步更新策略
            • 编译
              • Virtual DOM和数据更新时的patch()
                • VNode
                • 数据更新时的patch()
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档