前言
- 如何理解 MVVM
- 如何实现 MVVM
- 是否解读过 vue 的源码(理解能力 学习能力 自学能力)
面试问到的题目
- 说一下使用jQuery和使用MVVM框架的区别
- 说一下对 MVVM 的理解
- vue 中如何实现响应式
- vue 中如何解析模板
- vue 的整个实现流程
实现一个todo-list
jQuery版
vue版
两者的区别:
- 数据和视图的分离
- 以数据驱动视图
说一下使用jQuery和使用MVVM框架的区别
- 数据和视图的分离,解耦(开放封闭原则)
- 以数据驱动视图,只关心数据变化,DOM 操作被封装
什么是MVVM
- MVC
- MVVM
- 关于 ViewModel
MVC
- M:Model 数据
- V:View 视图、界面
- C:Controller 控制器、逻辑处理
MVVM
- Model:模型、数据
- View:视图、模板(视图和模型是分离的)
- ViewModel:连接 Model 和 View
关于ViewModel
- MVVM 不算是一种创新
- 但其中的 ViewModel 确实一种创新
- 真正结合前端场景应用的创建
解答
- MVVM:Model View ViewModel
- 三者之间的联系、以及如何对应到各段代码
- ViewModel 的理解,联系 View 和 Model,数据驱动视图,视图修改驱动数据变化
vue的MVVM
- 响应式:vue 如何监听到 data 的每个属性变化
- 模板引擎:Vue 的模板如何被解析,指令如何处理
- 渲染:vue 的模板如何被渲染成html?以及渲染过程
vue 中如何实现响应式
什么是响应式
- 修改 data 属性之后,vue 立刻监听到
- data 属性被代理到 vm 中
- Object.defineProperty
- 模拟
// var obj = {
// name: 'zhangsan',
// age: 25
// }
// console.log(obj) // {name: 'zhangsan',age: 25}
// var obj = {}
// var _name = 'shangsan'
// Object.defineProperty(obj, 'name', {
// get: function () {
// console.log('get', _name) // 监听
// return _name
// },
// set: function (newVal) {
// console.log('set', newVal) // 监听
// _name = newVal
// }
// })
// var vm = new Vue({
// el: '#app',
// data: {
// name: 'zhangsan',
// age: 20
// }
// })
var vm = {}
var data = {
name: 'zhangsan',
age: 20
}
var key, value
for (key in data) {
(function (key) { // 保证 key 的独立作用域
Object.defineProperty(vm, key, {
get: function () {
console.log('get', data[key]) // 监听
return data[key]
},
set: function (newVal) {
console.log('set', newVal) // 监听
data[key] = newVal
}
})
})(key)
}
- 关键是理解 Object.defineProperty
- 将 data 的属性代理到 vm 上
vue 中如何解析模板
模板是什么
- 本质:字符串
- 有逻辑,如 v-if v-for 等
- 与 html 格式很想,但有很大区别
- 最终还要转换为 html 来显示
- 分割线
- 模板最终必须转换成 JS 代码,因为
- 有逻辑(v-if v-for),必须用 JS 才能实现
- 转换为 html 渲染页面,必须用 JS 才能实现
- 因此,模板最终要转换成一个 JS 函数(render 函数)
render 函数
- 模板中所有信息都包含在了 render 函数中
- this 即 vm
- price 即 this.price 即 vm.price,即 data 中的 price
- _c 即 this._c 即 vm._c
- 从哪里可以看到 render 函数?(源码查找code.render 打印出来)
- 复杂一点的例子,render 函数是什么样子的?(参考下面的 todo-list)
- vm._c 是什么?(创建元素)
render 函数与 vdom
- vm._c 其实就相当于 snabbdom 中的 h 函数
- render 函数执行之后,返回的是 vnode
- updateComponent 中实现了 vdom 的 patch
- 页面首次渲染执行 updateComponent
- data 中每次修改属性,执行 updateComponent
// jquery
// html
// <div>
// <input type="text" name="" id="txt-title">
// <button id="btn-submit">submit</button>
// </div>
// <div>
// <ul id="ul-list"></ul>
// </div>
var $txtTitle = $('#txt-title')
var $btnSubmit = $('#btn-submit')
var $ulList = $('#ul-list')
$btnSubmit.click(function () {
var title = $txtTitle.val()
if (!title) {
return
}
var $li = $('<li>' + title + '</li>')
$ulList.append($li)
$txtTitle.val('')
})
// vue
// <div id="app">
// <div>
// <input v-model="title">
// <button v-on:click="add">submit</button>
// </div>
// <div>
// <ul>
// <li v-for="item in list">{{item}}</li>
// </ul>
// </div>
// </div>
// data 独立
var data = {
title: '',
list: []
}
// 初始化 Vue 实例
var vm = new Vue({
el: '#app',
data: data,
methods: {
add: function () {
this.list.push(this.title)
this.title = ''
}
}
})
/*
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))])})
)
]
)
]
)
}
*/
vue 的整个实现流程
- 模板:字符串,有逻辑,嵌入 JS 变量……
- 模板必须转换为 JS 代码(有逻辑、渲染 html、JS 变量)
- render 函数是什么样子的
- render 函数执行是返回 vnode
- updateComponent
流程:
第一步:解析模板成 render 函数
- with 的用法
- 模板中的所有信息都被 render 函数包含
- 模板中用到的 data 中的属性,都变成了 JS 变量
- 模板中的 v-model v-for v-on 都变成了 JS 逻辑
- render 函数返回 vnode
第二步:响应式开始监听
- Object.defineProperty
- 将 data 的属性代理到 vm 上
第三步:首次渲染,显示页面,且绑定依赖
- 初次渲染,执行 updateComponent,执行 vm._render()
- 执行 render 函数,会访问到 vm.list vm.title
- 会被响应式的 get 方法监听到(后面详细讲)
- 执行 updateComponent ,会走到 vdom 的 patch 方法
- patch 将 vnode 渲染成 DOM ,初次渲染完成
第四步:data 属性变化,触发 rerender
- 修改属性,被响应式的 set 监听到
- set 中执行 updateComponent
- updateComponent 重新执行 vm._render()
- 生成的 vnode 和 prevVnode ,通过 patch 进行对比
- 渲染到 html 中
可以参考 深入了解Vue的双向数据绑定 + 深入了解virtual dom