最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 一文彻底读懂Vue 2.x运行机制

    正文概述 掘金(吴空)   2020-12-04   505

     前言

    之前我写过一篇文章,简单介绍了Vue 2.x源码的工程化和用到的技术,没看过的,大家可以去看看《Vue源码工程化及构建流程》。

    Vue项目初始化

    我们通过vue-cli或者自己封装的脚手架初始化一个项目之后,通常在项目根目录下会有src、public等文件夹,src文件夹下有整个项目的入口文件main.ts,main.ts也是构建工具webpack的入口,在main.ts中会有Vue的初始化:

    new Vue({
        render: h => h(App)
    }).$mount('#app');
    

    所以,整个项目运行是通过new一个Vue的实例开始,初始化Vue实例的时候其实有几个核心功能实现。

    Vue源码的几个核心实现

    1. Vue选项的规范化
    2. Vue选项的合并
    3. Vue数据响应式
    4. Vue模版编译器
    5. 模版解析成AST
    6. AST生成Render函数
    7. 虚拟DOM patch

    Vue选项规范化

    一个单文件组件通常会有几个部分:

    • props
    • data
    • components
    • methods
    • computed
    • watch

    比如我们想接收父组件传值,就会在单文件组件对象上定义props属性,但是在写props的时候,你会发现有好几种写法

    第一种

    export default {
      props: ['name', 'age']
    }
    

    第二种

    export default {
      props: {
        name: String,
        age: Number
      }
    }
    

    第三种

    export default {
      props: {
         name: {
            type: String,
            default: ''
         }
      }
    }
    

    以上三种写法,我相信用过Vue的同学都很清楚。Vue内部怎样处理这些不同的写法呢?不可能有三种适配器,那么只能规范化,Vue在初始化的时候就会规范props为第三种写法,其他的属性比如computed、methods、watch怎么规范,大家可以去看源码。

    Vue选项的合并

    一个中大型项目,都会做组件抽象,项目中可能存在上千个.vue组件,而每个组件中都定义了自己的data、props、methods、inject......,多个文件的共同属性是怎么合并在一起的呢?我们这次只讲流程,不讲细节,大家可以下去了解。

    Vue数据响应式

    Vue如何做响应式的,相信大家并不陌生,不管工作中还是出去面试,经常会遇到这个问题,下面贴一张官方图片

    一文彻底读懂Vue 2.x运行机制

    关于依赖收集、数据劫持、派发更新的关系,其实远比图片要复杂,因为里面还要考虑很多细节,包括数组的处理,多个属性被重复监听等,不过最重要的一点,Vue在初始化的时候初始化了一个全局的Watcher对象

    new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
    

    Watcher对象在初始化的时候,会往构造函数传一个updateComponent方法,我们看看updateComponent方法的定义

    updateComponent = () => {  
        vm._update(vm._render(), hydrating)
    }
    

    之后数据有变化,都会通知watcher执行update方法,执行update的时候,就会取出初始化时候的updateComponent来执行,这个方法内部会执行render function和patch,这些后面会讲,记住这里是核心。

    Vue模版编译器

    单文件组件通常会包含三个部分,template、script、style,template里面会包含html,v-for,v-if,@等指令或符号,vue怎么解析他们?内部有个模版解析器parseHTML

    一文彻底读懂Vue 2.x运行机制

    parseHTML会解析出template里面所有标签的属性,指令和属性值,然后转换成AST,内部的核心实现大家可以去看代码。

    模版解析成AST

    模版编译器解析模版之后会生成AST(抽象语法树),大家可能对AST没什么概念,我们举个例子

    HTML片段

    <div class="tree" v-if="show">
      <span>{{item}}</span>
    </div>
    

    转换成的AST

    {
        "type": 1,
        "tag": "div",
        "attrsList": [],
        "attrsMap": {
            "class": "tree",
            "v-if": "show"
        },
        "rawAttrsMap": {},
        "children": [
            {
                "type": 1,
                "tag": "span",
                "attrsList": [],
                "attrsMap": {},
                "rawAttrsMap": {},
                "parent": "[循环引用]",
                "children": [
                    {
                        "type": 2,
                        "expression": "_s(item)",
                        "tokens": [
                            {
                                "@binding": "item"
                            }
                        ],
                        "text": "{{item}}",
                        "static": false
                    }
                ],
                "plain": true,
                "static": false,
                "staticRoot": false
            }
        ],
        "if": "show",
        "ifConditions": [
            {
                "exp": "show",
                "block": "[循环引用]"
            }
        ],
        "plain": false,
        "staticClass": "\"tree\"",
        "static": false,
        "staticRoot": false,
        "ifProcessed": true
    }
    

    看起来是不是很简单?AST就是个js对象,这个对象描述了当前HTML的层级关系,并且通过编译器解析出来的各个标签,标签的属性,Vue指令等。

    AST生成Render函数

    编译器解析模版之后会生成AST,后面就是把AST转换成Render函数的过程,我们看看核心代码

    一文彻底读懂Vue 2.x运行机制

    把ast传到generate方法就能返回Render函数

    const code = generate(ast, options)
    

    我们看上面的HTML片段生成AST,然后生成Render函数

    with(this){
        return (show)?
            _c('div',{staticClass:"tree"},[_c('span',[_v(_s(item))])])
            :_e()
    }
    

    这段代码通过with(this)包裹,这能想象到this指向的就是当前vue实例,而_c,_v,_s是定义在Vue原型链上的一系列方法

    一文彻底读懂Vue 2.x运行机制

    _c = createElement

    _s = toString

    _e = createEmptyElement

    通过定义我们可以看出_c是createElement方法,_s是toString方法,_c和_e返回都数据类型是vNode即虚拟DOM,对createElement不熟悉的同学可以看看Vue文档中渲染函数render部分,其中render入参就是createElement。

    虚拟DOM patch

    通过上面我们知道,数据发生变化时会通知watcher执行update,update执行的时候会执行updateComponent方法

    updateComponent = () => { 
        vm._update(vm._render(), hydrating)
    }
    

    updateComponent方法内部执行了_update方法,其中入参vm._render方法执行的就是render function,即with(this) { /***/ }代码片段,render function返回当前项目的虚拟DOM,我们看看_update内部实现

    一文彻底读懂Vue 2.x运行机制

    _update内部执行了patch方法,入参老虚拟DOM和当前虚拟DOM,patch方法内部执行虚拟DOM的diff算法,细节不谈论,我们只讲流程。

    总结

    以上七个实现,基本完成了vue的所有核心功能,简单来说,一个vue项目启动的时候会初始化Vue实例,后续过程是这样的

    规范 -> 合并 -> 初始化数据响应 -> 编译模版 -> 生成AST -> 生成render函数 -> 执行patch
    

    一文彻底读懂Vue 2.x运行机制

    但是里面的细节还很多,我只是说了整体的实现思路,最后说明一下,我在之前的文章中提过,vue打包后会生成两个版本:完整版和运行时版,其中完整版会把编译器代码打包进去,而运行时版本不带编译器,但是我们的项目引入的是运行时版本,那是怎么做模版解析的呢,这就是webpack loader的职责,大家可以看看vue-loader都做了什么。


    起源地下载网 » 一文彻底读懂Vue 2.x运行机制

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元