学习vue过程中的一些收获,以问题解答的形式总结:
问题一:使用jQuery和框架的区别
问题二:对MVVM的理解
问题三:如何实现MVVM
问题四:Vue中如何实现响应式
问题五:Vue中如何解析模板
问题六:Vue的整个实现流程
★使用jQuery和框架的区别
这个问题通过用jQuery和vue实现todolist demo来对比说明。
jQuery实现todolist :
vue实现todolist:
- {{item}}
由以上demo的实现方式对比可以看出两者的区别:
1.vue数据和视图分离,以数据驱动视图
2.jQuery数据和视图并没有分离,数据没有驱动视图的修改
解释说明:
不难发现,在jQuery中,视图如下:
数据如下:
ul标签只是容器,并没有列表的视图,列表视图在数据部分,所以jQuery数据和视图没有分离。从数据层可以看出,添加列表修改视图的时候并不是通过数据驱动视图修改的,而是通过用最底层的API append直接操作视图实现的。
而在vue中,数据部分如下:
视图部分如下:
可以看出vue的数据和视图是分开的,vue通过给list数组push元素实现页面的渲染,即数据驱动视图显示的。
总结:
1.vue数据和视图的分离,以数据驱动视图,只关心数据变化,DOM操作被封装。解耦,不会牵一发而动全身,符合开放封闭原则
2.jQuery数据和视图没有分离,不是通过数据驱动视图的方式渲染的,而是直接进行DOM操作。
★对MVVM的理解
说到MVVM,必须提到MVC
ViewModel是将后端的MVC应用到前端,真正结合前端场景应用的创建,是一种微创新。ViewModel是连接View和Model的桥。
★如何实现MVVM
MVVM是Vue的框架,Vue是对MVVM框架的实现,所以说,MVVM的实现也就是Vue的实现。
首先,我们得知道Vue(MVVM)的三要素:
掌握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代码中,模板是:
- {{item}}
模板的本质其实是一串有逻辑(一些指令v-if,v-for)的字符串。与HTML结构很像,但有很大区别:vue模板是动态的,HTML模板是静态的。模板字符串最终必须转换成JS代码(render函数),原因有两点:a.模板中有逻辑,必须用JS来处理逻辑(JS是前端中唯一一个图灵完备的语言) b.要将模板渲染到页面上,必须用JS实现。
下面,看一下模板转换成的render函数(学习render函数必须会with的用法):
先看一个简单的例子:
模板对应解析出的render函数是:
下面看一下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函数中的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的整个实现流程其实是对以上内容的联系总结,这个问题在下一个博客中总结。