学习能力 理解能力 自学能力 是否有技术热情 工作效率(读源码)
收益,不同,总结,理解的地方
### 如何理解mvvm
### 如何实现mvvm
### 是否解读过vue的源码
## 题目
1. 使用jquery和使用框架的区别(总结出来)
2. 说一下对mvvm的理解
3. vue中如何实现响应式(原理)
4. vue如何解析模板
5. vue的整个实现流程(看博客,看文章,不一定非要看源码)
### 使用jquery和使用框架的区别(总结出来)
1. jQuery todo-list
```
$text = $("#text");
$ul = $("#ul");
$btnSubmit = $("#btnSubmit");
$btnSubmit.click(function(){
var title = $text.val();
var $li =$('<li>'+title+"</li>");
var $ul.append($li);
$txt.val();
})
```
append(操作dom)
2. vue todo-list
```
<input v-model="title" />
<ul>
<li v-for="item in list" >{{item}}</li>
</ul>
<div id ="app"></div>
定义容器
//初始化vue实例
var vm = new Vue({
el:"#app",
data:{title:'',list:[]},
method:{
add:function(){
this.list.push(this.title);
this.title = "";
}
}
})
```
3. jQuery和vue的区别
* **数据和视图的分离,解耦(开放封闭原则)--(对扩展开放,对修改封闭)**
jquery数据和视图的处理根本没有分离,
不分离的坏处:开放封闭原则问题
vue的是分离的,数据在data中
* **以数据驱动视图,只关心数据变化,DOM操作被封装**
jquery从最直接最底层的api进行操作dom,直接干预dom的修改
### 对mvvm的理解
1. **mvc**
view(视图,界面)
Model(模型,数据源)
Controller(控制器,控制view的变化和Model的变化)
一般:view发生变化到control到model,model再去改view
2. **mvvm**
m(模型,数据:data,js对象)
v(视图、模板:(视图和模型是分离的)view,最后对应的都是dom)
vm(连接view和model vm实例 viewModel:**view和model之间的桥**)
**view通过事件绑定操作model,model通过数据绑定操作view**
view和model彻底分开
3. 关于ViewModel
不算创新,算是mvc的一种微创新
4. mvc和mvvm的区别
Controller负责在Controller里面把Model的数据赋值给View,比如在controller中写document.getElementById("box").innerHTML = data\[”title”\],只是还没有刻意建一个Model类出来而已。
MVVM与MVC最大的区别就是:**它实现了View和Model的自动同步**,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是**改变属性后该属性对应View层显示会自动改变**。
### MVVM框架的三大要素(vue的三大要素)
vue固定的模板并不是html,因此需要模板的解析
最终生成的都是DOM元素
监听数据变化怎么更新dom元素的
1. **响应式**:vue如何监听到data的每个属性的变化?
2. **模板(解析)引擎**:vue的模板如何被解析,指令如何处理?
3. **渲染**:vue的模板如何被渲染成html?一级渲染过程
### vue中如何实现响应式
1. 什么是响应式?
2. **Object.defineProperty**(vue响应式的**核心函数**)
3. 模拟响应式
*****
1. 什么是响应式
* 修改data属性之后,**vue立刻监听到**(如何监听到)
* ***data属性被代理到vm***(this)上
~~~
修改data中数据
vm.name(this.name)可以直接修改(而不是vm.data.name this.data.name)
~~~
#### **Object.defineProperty(ES5 IE9+ 手机 兼容性没有问题)**
获取属性,设置属性,如何监听得到
~~~
var obj = {};
var name ="peipei";
Object.defineProperty(obj,"name",{
get:function(){
console.log('get',name);
return name;
},
set:function(newVal){
console.log('set',newVal);
name = newVal;
}
});
console.log(obj.name);
obj.name="list";//set操作
obj.name//get操作
可重写get和set(获取和设置都是函数),加上自己的逻辑
~~~
模拟vue实现监听
~~~
//vue实例
var vm \= new Vue({
el:'#app',
data:{
price:100,
name:'苹果'
}
});
~~~
**模拟 1.defineProperty怎么用 2,.data怎么代理到vm中**
~~~
var vm \= {};
var data \= {
price:100,
name:'苹果'
};
var key,value;
for(key in data){
//命中闭包,新建一个函数,保证key的独立的作用域
//自执行函数
(function(key){
Object,defineProperty(vm,key,{
get:function(){
console.log('get');
return data\[key\];
},
set:function(newVal){
console.log('set');
data\[key\] \= newVal;
}
})
})(key);
}
~~~
1.监听数据变化 **defineProperty**,将设置和获取写成一个函数,在函数中进行监听
2. data的属性怎么代理到vm中,还是**defineProperty**(通过defineProperty给vm定义属性)
### **问题总结**
响应式:
1. 关键是理解Object.defineProperty
2. 将data的属性代理到vm上
*****
### **vue中如何解析模板**
1. 模板是什么
~~~
div v-model v-on:click v-for v-if @click
~~~
* 本质:字符串(像html)
* 有逻辑 v-if v-for
* 与html格式很像,但有很大区别 (**html是静态的,vue中的模板时动态的**)
* 最终要转换为html来显示
* 模板**最终必须转换成js代码**
因为:1.**有逻辑**v-if v-for,**必须用js**(图灵完备)实现;2.**转换为html渲染页面**,必须用js实现(**js才能渲染页面**)
因此:**模板最终(必须)要转成一个js函数(render函数)(才能执行)**
2. render函数
* with的用法(自己不要用)
~~~
var obj = {
name:'zhang',
age:20,
getAddress:function(){
alert('bbb')
}
};
//使用with
function fun1(){
with(obj){
alert(name)
alert(age)
getAddress()
}
}
fun1();
~~~
通过width把对象包起来,可以直接读取属性(实际开发不好用)
vue使用width是为了使代码变的简洁
模板
~~~
<div id="app"><p>{{price}}</p></div>
~~~
生成的render函数
~~~
//this为vm实例 _c 为vm._c _v为vm._v price为this.price
//_c(创建元素)
function render(){
with(this){
return _c(
'div',
{
attrs:{'id':'app'}
},
//子元素
[
_c('p',[_v(_s(price))])
]
)
}
}
//翻译with
function render1(){
return vm. _c(
'div',
{
attrs:{'id':'app'}
},
//子元素
[
vm._c('p',[vm._v(vm._s(vm.price))])
]
)
}
~~~
vm._c:创建标签 createElement
vm._v:创建文本节点 createTextVNode
vm._s:toString(val)
总结:
* **模板中所有信息都包含在了render函数中**
* this即vm
* price即this.price即this.price即data中的price
* _c为this._c
### 其他问题
1. 从哪里可以看到render函数
**源码中搜索code.render**
~~~
var code = generate(ast, options);
alert(code.render)
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
~~~
vue2.0开始,支持**预编译**
**开发环境:写模板
编译打包:
生产环境下:js代码**
2. 复杂一点的例子,render函数是什么样子的
~~~
with(this){//vm
return \_c(//vm.\_c
'div',
{
attrs:{'id':'app'}
},
[
_c('div',
[_c(
'input',//双向数据绑定
{'directives':\[{//数据驱动view 赋值
name:'model',rawName:'v-model',value:(title),expression:'title'
}\],
'domProps':{
'value':(title),
},
'on':{
'input':function($event){
if($event.target.composing) return;
title = $event.target.title;//view修改data
}
}
}
),
_v(" "),
_c('button',
{
'on':{'click':add}//vm.add
},
[_v('submit')\]//文本节点
)
]
),
_v(" "),
_c('div',
[
_c('ul',//vm._l(函数):返回数组,数组中遍历list,对每个元素创建li标签,vm.list
_l((list),function(item){
return _c('li',[_v(_s(item))])
//vm._l 可查看当前方法的实现
}
~~~
理解设计理念,不用太深究
**双向数据绑定:即能get又能set(v-model)**
3. v-if v-for v-on 都是怎么处理的
v-model:**v-model是双向数据绑定,即能get又能set(v-model)**
v-on:click:绑定click事件,
v-for: 针对li对数据遍历,每个元素都封装成li标签,将li标签汇成数组,写入ul标签
v-if:判断
4. render函数与vdom(集成在vue,react)
**vdom**:**js模拟dom结构**
vdom核心api:
**h():通过标签,属性,子元素,创建vnode(虚拟节点)**
**patch():将虚拟节点(vnode)渲染到空的dom元素上;用新的vnode和旧的vnode进行对比,对比结果(diff地方,有差异的地方),重新渲染到dom节点上**
**patch(container,vnode)**将vnode渲染到空的容器上
**patch(vnode,newVnode)**:用新的和旧的进行对比,将不同的进行重新渲染
5. 模板生成html页面的问题,vm._c是什么,render函数返回了什么?
模板生成render函数之后,render函数和html有什么关系?
render函数,返回(return vm._c)
vm._c和h()函数基本一样,
vm._c其实相当于snabbdom中的h函数,**render函数执行之后,返回的是vnode**,
vm.render(): 返回vnode
vm._update(vm.render()):
**updateComponent中实现了vdom的patch**
**页面首次渲染执行updateComponent;**
**data中每次修改属性(响应式),执行updateComponent;**
可在defineProperty的set函数中执行updateComponent
~~~
vm._update(vnode){
const preVnode = vm._vnode;
vm._vnode = vnode;
if(!prevVnode){
//第一次,直接将vnode渲染到空的容器中
vm.$el = vm.__patch__(vm.$el,vnode);
}else{
// 以后通过patch将有差异的地方进行渲染
vm.$el = vm.__patch__(prevVnode,vnode);
}
}
function updateComponent(){
vm._update(vm._render())
}
~~~
*****
### 总结
1. **模板:**
* 本质是字符串,语法上有逻辑(v-for v-if),也可以嵌入js变量(name,age),像html但不是html
* 模板必须转为js代码(因为有逻辑,渲染html,js变量)(前端只有js才能处理逻辑,只有js才能渲染html)
2. **render函数**
* with语法
* vm._c h()函数
* 最终返回**vnode**
3. **updateComponent组件**
* **patch** 首次渲染和对比渲染
*****
### vue的整个实现流程
1. **第一步:解析模板成render函数**
开发环境一打包就是render函数,因此肯定是第一步
code.render 打印出
* width的应用
* 模板中的所有信息都被render函数包含
* 模板中用到的data中的属性,都变成了js变量
* 模板中v-model,v-for,v-on都变成了js逻辑
* render函数返回vnode节点
2. **第二步:响应式开始监听**
js执行的时候才会发生
* Object.defineproperty 监听(get set)
* 将data的属性代码到vm上(属性代理到this上,with(this)才能获取)
3. **第三步:首次渲染,显示页面,且绑定依赖**
* 初次渲染,执行updateComponent,执行vm.render()
* 执行render函数,会访问vm.list和vm.title
* 会被响应式的get方法监听到
**为什么监听get?直接监听到set中不行吗?**
因为:data中很多属性,有些被用到,有些不被用到;被用到的走get,不被用到的不会走get;**未走到get的属性,set的时候我们也无需关心**;**因此会避免不必要的重复渲染**;
* 执行updateComponent,会走到vdom的patch方法
* patch将vnode渲染成dom,初次渲染完成
4. **第四步:data属性变化,触发rerender**
data发生变化,命中响应式,set会被监听到(会首先判断有没有走get,监听到set才会走updateComponent)
* 修改属性,被响应式的set监听到
* set中执行updateComponent
* updateComponent重新执行vm.render()
* 生成vnode和prevVnode,通过patch进行对比
* 渲染到html中
*****
### 自己补充
1. Vue不能检测数组或对象变动问题
由于javascript的限制,vue不能检测一下变动的数组
原因:vue对于object类型的data,直接用Object.defineproperty定义getter和setter,在setter节点对提交是否的更新
**而对于Array,数组的每个元素没有getter和setter,所以不能简单的defineProperty,而只能监听一些操作如push,pop等****每个数组的 prototype 单独 hack push 这些操作**
* 利用索引设置一个项:vm.items[index] = newValue(不是响应性的)
* 修改数组的长度:vm.items.length = newLength(不是响应性的)
解决办法(可触发更新):
1) **Vue.set(vm.items,index,newValue)**
2) **vm.items.splice(index,1,newValue)**
也可使用vm.$set实例方法,是全局方法Vue.set的一个别名
vm.$set(vm.items,index,newValue)
第二类:**vm.items.splice(newLength)
**
由于javascript的限制,vue不能检测对象属性的添加或删除
~~~
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
~~~
对于已经创建的实例,**vue不能动弹添加跟级别的响应式属性**,但是可以使用Vue.set(object,key,value)方法,**向嵌套对象添加响应式属性**。
~~~
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
~~~
~~~
Vue.set(vm.userProfile, 'age', 27)
vm.$set(vm.userProfile, 'age', 27)
~~~
对已有对象赋予多个属性,使用Object.assign(),_.extend()
这种情况下,应该用两个对象的属性**创建一个新的对象**,不要这样
~~~
Object.assign(vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
~~~
应该:
~~~
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
~~~
还有第三种方法:深度拷贝
~~~
vm.userProfile = JSON.parse(JSON.stringify(vm.userProfile))
~~~
但深度拷贝会有坑:
1. 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
2. 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
3. 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4. 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5. JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
6. 如果对象中存在循环引用的情况也无法正确实现深拷贝;
广为流传的深拷贝方式有
1. JSON方式
2. jQuery的 extend方式
3. es6 的Object.assign方式
4. vue的 vue.util.extend 方式
5. 递归拷贝
### 浅拷贝
其实这段代码就是浅拷贝,有时候我们只是想备份数组,但是只是简单让它赋给一个变量,改变其中一个,另外一个就紧跟着改变,但很多时候这不是我们想要的
~~~
var obj = {
name:'wsscat',
age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}
~~~
### 深拷贝
*数组*
对于数组我们可以使用`slice()`和`concat()`方法来解决上面的问题
*对象*
对象我们可以定义一个新的对象并遍历新的属性上去实现深拷贝
当然我们可以封装好一个方法来处理对象的深拷贝,代码如下
~~~
var obj = {
name: 'wsscat',
age: 0
}
var deepCopy = function(source) {
var result = {};
for(var key in source) {
if(typeof source[key] === 'object') {
result[key] = deepCopy(source[key])
} else {
result[key] = source[key]
}
}
return result;
}
var obj3 = deepCopy(obj)
obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj3);//Object {name: "wsscat", age: 0}
~~~
- 空白目录
- 双樾
- JS基础知识
- JS-WEB-API
- 开发环境
- 运行环境
- ES6
- 原型
- 异步
- 虚拟dom
- mvvm
- 组件化和React
- hybrid
- 其他
- 补充
- 技巧
- 快乐动起来呀
- css
- 掘金小册子
- js基础知识
- ES6知识点
- JS异步
- JS进阶知识
- 思考题
- DevTools Tips
- 浏览器基础知识
- 浏览器缓存机制0
- 浏览器渲染原理
- 安全防范知识点0
- 从V8中看JS性能优化0
- 性能优化琐碎事
- Webpack性能优化0
- 实现小型打包工具0
- React和Vue
- Vue生命周期
- vue基础知识点
- Vue响应式
- vue高级
- React基础
- Vue.js技术解密
- 准备工作
- 数据驱动
- new Vue()
- vue实例挂载
- 组件化
- 深入响应式原理
- 编译
- 扩展
- Vue Router
- Vuex