【Vue】MVVM 模板解析 渲染

学习vue过程中的一些收获,以问题解答的形式总结:
问题一:使用jQuery和框架的区别
问题二:对MVVM的理解
问题三:如何实现MVVM
问题四:Vue中如何实现响应式
问题五:Vue中如何解析模板
问题六:Vue的整个实现流程

★使用jQuery和框架的区别
这个问题通过用jQuery和vue实现todolist demo来对比说明。
jQuery实现todolist :


    vue实现todolist:

    
    
    
    

    由以上demo的实现方式对比可以看出两者的区别:
    1.vue数据和视图分离,以数据驱动视图
    2.jQuery数据和视图并没有分离,数据没有驱动视图的修改
    解释说明:
    不难发现,在jQuery中,视图如下:
    【Vue】MVVM 模板解析 渲染_第1张图片
    数据如下:
    【Vue】MVVM 模板解析 渲染_第2张图片
    ul标签只是容器,并没有列表的视图,列表视图在数据部分,所以jQuery数据和视图没有分离。从数据层可以看出,添加列表修改视图的时候并不是通过数据驱动视图修改的,而是通过用最底层的API append直接操作视图实现的。
    而在vue中,数据部分如下:
    【Vue】MVVM 模板解析 渲染_第3张图片
    视图部分如下:
    【Vue】MVVM 模板解析 渲染_第4张图片
    可以看出vue的数据和视图是分开的,vue通过给list数组push元素实现页面的渲染,即数据驱动视图显示的。
    总结:
    1.vue数据和视图的分离,以数据驱动视图,只关心数据变化,DOM操作被封装。解耦,不会牵一发而动全身,符合开放封闭原则
    2.jQuery数据和视图没有分离,不是通过数据驱动视图的方式渲染的,而是直接进行DOM操作。
    ★对MVVM的理解
    说到MVVM,必须提到MVC
    【Vue】MVVM 模板解析 渲染_第5张图片

    • M-Model 数据
    • V-View 视图、界面
    • C-Controller 控制器、逻辑处理
      一种情况:
      【Vue】MVVM 模板解析 渲染_第6张图片
      举这种情况的例子:用户在界面View上点击按钮,触发按钮事件,Controller进行逻辑处理,修改Model数据,Model将修改的数据 同步到View层。
      还有另一种情况:
      【Vue】MVVM 模板解析 渲染_第7张图片
      这种是用户直接在Controller修改Model数据,Model将修改了的数据同步到View界面。
      有时候这两种情况不能严格区分,这就是MVC。
      回顾上面用Vue写的todolist的demo,下面来看MVVM都对应哪块代码:
      【Vue】MVVM 模板解析 渲染_第8张图片
      【Vue】MVVM 模板解析 渲染_第9张图片
      【Vue】MVVM 模板解析 渲染_第10张图片
      下面看看M V VM三者之间的关系:
      【Vue】MVVM 模板解析 渲染_第11张图片
      View通过事件绑定操作Model
      Model通过数据绑定操作View

    ViewModel是将后端的MVC应用到前端,真正结合前端场景应用的创建,是一种微创新。ViewModel是连接View和Model的桥。

    ★如何实现MVVM
    MVVM是Vue的框架,Vue是对MVVM框架的实现,所以说,MVVM的实现也就是Vue的实现。
    首先,我们得知道Vue(MVVM)的三要素:

    1. 响应式:Vue如何监听到data属性的变化?
    2. 模板引擎:Vue的模板如何被解析?指令如何处理?
    3. 渲染:Vue的模板如何被渲染成html?它的渲染过程是怎样的?以及data属性变化的时候如何重新渲染。

    掌握Vue的三要素及各要素之间的联系,即可实现MVVM结构,也就可以知道vue的整个实现流程。
    来个总分总结构分析一下:
    先说响应式,首先什么是响应式呢?
    vue中的响应式就是修改data属性后,vue立刻就能监听到,立刻进行页面渲染。
    在响应式这块需要知道:
    1.vue如何监听data属性的变化
    2.实现响应式的核心函数Object.defineProperty
    3.data属性为啥被代理到vm(new的vue实例)上。

    我们在获取和设置属性值的时候,直接就获取到了或者是设置成功了,那怎么样才能监听到呢?(只有监听到data属性变化了再渲染页面)这就需要Object.defineProperty函数了。
    下面我们模拟实现vue如何监听到data属性的变化,以及data属性代理到vm上。

    
    

    以上代码实现了:监听到了data属性的变化,将data属性代理到了vm上
    在模板解析这块,我们需要知道:
    在vue代码中,模板是:

    
    

    模板的本质其实是一串有逻辑(一些指令v-if,v-for)的字符串。与HTML结构很像,但有很大区别:vue模板是动态的,HTML模板是静态的。模板字符串最终必须转换成JS代码(render函数),原因有两点:a.模板中有逻辑,必须用JS来处理逻辑(JS是前端中唯一一个图灵完备的语言) b.要将模板渲染到页面上,必须用JS实现。

    下面,看一下模板转换成的render函数(学习render函数必须会with的用法):
    先看一个简单的例子:
    在这里插入图片描述
    模板对应解析出的render函数是:
    【Vue】MVVM 模板解析 渲染_第12张图片

    下面看一下todolist demo中模板对应生成的render函数:

    • {{item}}

    生成的render函数是:

    with(this){  // this 就是 vm
                return _c(
                    'div',
                    {
                        attrs:{"id":"app"}
                    },
                    [
                        _c(
                            'div',
                            [
                                _c(
                                    'input',
                                    {
                                        directives:[
                                            {
                                                name:"model",
                                                rawName:"v-model",
                                                value:(title),
                                                expression:"title"
                                            }
                                        ],
                                        domProps:{
                                            "value":(title)
                                        },
                                        on:{
                                            "input":function($event){
                                                if($event.target.composing)return;
                                                title=$event.target.value
                                            }
                                        }
                                    }
                                ),
                                _v(" "),
                                _c(
                                    'button',
                                    {
                                        on:{
                                            "click":add
                                        }
                                    },
                                    [_v("submit")]
                                )
                            ]
                        ),
                        _v(" "),
                        _c('div',
                            [
                                _c(
                                    'ul',
                                    _l((list),function(item){return _c('li',[_v(_s(item))])})
                                )
                            ]
                        )
                    ]
                )
            }
    
    • 模板中所有的信息都被包含在render函数中。
    • this即vm(因为render函数的with(this)指的是vm,所以要把data属性代理到vm上)
    • price即this.price,即this.price
    • _c即this._c,即vm._c
    • 模板中用到的data属性都变成了JS变量
    • 模板中的指令v-for v-model v-if都变成了JS逻辑
    • render函数返回的是vnode

    render函数中的vm._c其实就是createElement函数,vm._v其实就是createTextVNode函数,vm._s其实就是toString函数,在控制台输出其实都可以看到。

    大家肯定有疑问,在哪怎么样输出render函数:
    答:在vue源码中搜索code.render,找到var code=generate(ast,options);
    在它后面console.log(code.render);其输出的内容就是该模板对应的render函数。
    大家肯定又会问道:那怎么生成render函数的呢?
    答:从vue2.0开始支持预编译,编译打包后就将模板生成对应的render函数,已经工具化了。

    从render函数中可以看到v-model v-on v-for等一些指令是怎么实现的:
    get set就可以实现v-model,v-on通过在渲染button的时候给它绑定onclick事件add实现的,v-for是通过一个_l函数实现的,这个函数通过对数组list的遍历,生成li标签,将所有的li标签作为数组一块返回。

    上面解决了模板中逻辑指令的处理的问题,接下来看模块生成html的问题(渲染)!!!

    在模板渲染这块,首先看一个函数:

    vm._update(vnode)
        {
            const prevVnode = vm._vnode;
            vm._vnode = vnode;
            if (!prevVnode) {
                vm.$el = vm._patch_(vm.$el, vnode);
            } else {
                vm.$el = vm._patch_(prevVnode, vnode);
            }
        }
        function updateComponent() {
            vm._update(vm._render())
        }
    

    updateComponent()函数实现了vdom中的patch函数
    updateComponent()函数在修改data属性的时候调用,即响应式的核心函数Object.defineProperty那块监听data属性值变化的时候调用。若是页面首次渲染,则执行vm.patch(container, vnode),若是data属性值发生变化,需要重新渲染界面,则执行vm.patch(vnode, newVnode);(用到了vdom中patch函数的两种用法)。

    emmm 学完react会发现,vdom中的h函数,vue中的vm._c函数,还有react中的React.createElement函数是一样的

    最后一个问题,vue的整个实现流程其实是对以上内容的联系总结,这个问题在下一个博客中总结。

    你可能感兴趣的:(【Vue】MVVM 模板解析 渲染)