一、理解VUE原理
用于构建用户界面的渐进式框架。通过Vue指令,实现JS和HTML的分离,JS代码仅仅是通过Module去控制View,而不是定义View。
中文官网:cn.vuejs.org/
1、Vue.js 框架的优势:
提高代码复用率
降低模块之间耦合度
提高开发速度
提高代码质量
组件化(web component)开发
2、VUE安装:
1、安装node.js
nodejs.org/en/ git: git-scm.com/download/wi…
2、安装bower
npm install bower -g (用 bower -v 查看是否安装成功)
3、bower install vue<#版本> (用bower info vue 查看版本)
3、VDOM原理
1、解决的问题:
Virtual DOM 用来解决DOM与Layout绘画压力问题。每一次DOM改变,Layout都会repaint,如果操作频繁,会造成渲染压力。
vue=>JSEngine=>DOM=>Layout
2、择时渲染:
VDOM 会择时渲染,1、setTimeout计时,或者引擎觉得合理的时机。
3、Diff算法:
VDOM在引擎源码中,是一个JS对象,其实是树状结构的JSON。所有节点都挂载在根节点上。每次diff的时候,有key就按照key比较,key必须是唯一的,可以用id+data作为key。没有key,就按照type比较。type不同的节点,直接拿掉,记录下来,下次重画。子节点里,比较出新增的,删除的,把这些都记录成diff,下次用diff重画节点。
面试题1:
用JS代码操作DOM写一个响应刷新
function changeDirectly(){
var app = document.getElementById('app');
var html = '';
var htmlTmp = document.getElementById('tmp').innerHTML;
html = htmlTmp.replace(/%word%/g, 'abc');
console.log(html);
app.innerHTML = html;
}
addEventListener('click', changeDirectly);
复制代码
实质就是在DOM中直接查找指定的标签,然后全局替换。
4、响应式原理
官网文章说的很透彻了。
cn.vuejs.org/v2/guide/re…
这里总结一下我的理解
1、JavaScript ES5 中 有个一个方法,Object.defineProperty,可以把传入vue的JS对象中的所有property转为getter/setter。(VUE不支持IE8以下是因为其不支持ES5)
2、每个vue组件实例都对应一个watcher实例,watcher把组件渲染过程中接触过的JS数据的property记录为依赖,当依赖的setter触发时,watcher被通知,然后重新渲染关联组件。
3、涉及vue中data对象的property属性修改,如果要实现修改后property的响应,必须要从原型链的层级修改。具体方法,参考文章中详细介绍。
4、另外在异步更新DOM过程中,如果同一个事件循环过程中,同一个watcher被触发多次,那么只会被推入到队列中一次,在下一次tick中,只执行最后更新的那次,这种去重简化了不必要的DOM刷新。
5、每次tick,实际原理和setTimeOut类似,都会在下一轮刷新时执行。可以自定义调用Vue.nextTick(callback)执行下次刷新内容。
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
复制代码
二、从MV*模式理解响应式原理
1、MVC模式:
Module存放数据
View更新DOM
Controller调用Module给View渲染
M——>C——>V
2、MVP模式
Presenter: 更新Module,修改View
M<==>P<==>V
3、MVVM模式
MVVM模式是进阶的MVP模式,这个才是vue使用的,前两个是过度版本介绍。
ViewModel: 自动化调用的Presenter
Module 更新,View自动随着更新
Vue的MVVM实现:
当Vue实例创建后,形成双向绑定,关键技术点就是 DOM Listener, Data bindings
M<=>VM<=>V
View层,DOM Listener监听DOM的变化,
Module层,Data bindings 帮助更新View 和 DOM
这个过程就是VM
Data bindings 就是指 Module里面有get()/set(),实现对数据的修改,get/set调用watcher()刷新View,然后修改DOM。
三、VUE语法:指令、事件、过滤器、计算属性,侦听器
1、指令:
- v-if:条件指令
- v-else: 条件指令,和v-if配合使用
- v-show:渲染指令 显示或隐藏元素
- v-for:列表指令
- v-model:用于将控件与module双向绑定
- v-model.lazy 只有在回车onblur时改动
- v-model.number 只能输入数据
- v-model.trim 去除前后空格
- input:与module双向绑定
- v-bind:用来控制元素的属性值,简写方式 “:” (v-bind = 🙂
- v-bind:src=”https://juejin.cn/post/expression”
- v-bind:class=”expression”
- v-bind:style=”expression”
2、事件:
v-on:<事件名>
.stop
.prevent
.13 或者 .enter
.left 或者 .right 或者 .middle
.once
.ctrl 或者 .shift 或者 .alt
3、过滤器
{{message | filter}} 用于格式化输出
4、计算属性:
把函数当成属性
5、侦听器:
防止watch滥用,改用 computed
四、组件
利用组件实现代码可复用。
1、组件生命周期:
四个阶段:Create/Mount/Update/Destroy
每一个阶段都有前后两个状态:
beforeCreate/created (创建)
beforeMount/mounted (挂载DOM)
beforeUpdate/updated (更新)
beforeDestroy/destroyed (销毁)
2、组件通信
1、父子通信
父组件向子组件传递字符串或者传递变量:以子组件属性 props的方式,透传字符串
Vue.component('child', {
template: `
<div>
<p :id="p1">{{str}}</p>
</div>
`,
props: ['p1', 'str'],
methods: {
},
mounted: function(){
console.log(this.p1, this.str);
}
});
Vue.component('parent', {
template: `
<div>
<child :p1="prtP1" :str="prtStr"></child> // 传递变量
// <child :p1="11111111" :str="222222222"></child> // 传递字符串
</div>
`,
data: function(){
return {
prtP1: 'p456',
prtStr: 'abc'
}
}
});
复制代码
2、子组件向父组件传递数据
- 在父组件中建立事件响应函数
- 在父组件模版中,绑定事件,事件调用事件响应函数
- 在子组件中,触发事件
// 子组件
Vue.component('child', {
template: `
<div>
<input type="button" @click="sendData" value="点我发送信息">
</div>
`,
methods: {
sendData: function(){
this.$emit('myEvent', 'Hello World! I am Vue!');
}
}
});
// 父组件
Vue.component('parent',{
template: `
<div>
<child @myEvent="getData"></child>
<hr />
<h4>现在的msg:{{msg}}</h4>
</div>
`,
methods: {
getData: function(msg){
console.log('收到了来自子组件的信息:' + msg);
this.msg = msg;
}
},
data: function(){
return {
msg: ''
};
}
});
复制代码
3、父组件访问子组件的数据:this.$ref
// 子组件
Vue.component('child', {
template: `
<div></div>
`,
data: function(){
return {
childStr: 'I am your son!'
};
},
mounted: function(){
console.log('子组件的mount:' + this.$parent.parentStr);
}
});
// 父组件
Vue.component('parent', {
template: `
<div>
<child ref="mySon"></child>
<input type="button" value="获得子组件信息" @click="getData">
</div>
`,
data: function(){
return {
parentStr: 'I am your father!'
}
},
methods: {
getData: function(){
console.log('获得子组件信息:' + this.$refs.mySon.childStr);
}
}
});
复制代码
4、子组件访问父组件的数据: this.$parent
Vue.component('parent', {
template: `
<div>
<ul v-show="isSwitchOn">
<li v-for="item in array">{{item}}</li>
</ul>
<child ref="mySon" ></child>
</div>
`,
data: function(){
return {
isSwitchOn: true,
array: [5, 10, 25, 50]
};
}
});
// 子组件
Vue.component('child', {
template: `
<div>
<input type="button" value="显示/不显示" @click="showOrNot" />
<input type="button" value="添加" @click="addItem">
</div>
`,
methods: {
showOrNot: function(){
this.$parent.isSwitchOn = !this.$parent.isSwitchOn;
},
addItem: function(){
this.$parent.array.push(25);
}
}
});
复制代码
5、兄弟组件之间通信
- 需要通过公共的Vue实例对象
- 接收方绑定事件,发送方触发事件
// 1. 创建公共的Vue对象
var bridge = new Vue();
// 2. 创建两个子组件
Vue.component('child-1',{
template: `
<div>
<h4>msg: {{msg}}</h4>
<input type="button" value="1->2" @click="sendData" />
</div>
`,
data: function(){
return {
msg: ''
}
},
methods: {
sendData: function(){
bridge.$emit('OneToTwoEvent', 'Hello 2, I am 1');
}
},
mounted: function(){
var that = this;
bridge.$on('TwoToOneEvent', function(msg){
console.log('收到来自2的:' + msg);
that.msg = msg;
})
}
});
Vue.component('child-2',{
template: `
<div>
<h4>msg: {{msg}}</h4>
<input type="button" value="2->1" @click="sendData" />
</div>
`,
data: function(){
return {
msg: ''
}
},
methods: {
sendData: function(){
bridge.$emit('TwoToOneEvent', 'Hello 1, I am 2');
}
},
mounted: function(){
// 1. mounted被调用时,只是定义了回调函数,但是并没有运行
// 2. 当child-1来触发事件时,引擎来调用回到函数,此时初始化回调函数中的this指向
// 注意:此时this并不只想child-2对象,所以,this.msg找不到的
// 3. 用闭包。
var that = this;
bridge.$on('OneToTwoEvent', function(msg){
console.log('收到来自1的:' + msg);
that.msg = msg;
})
}
});
复制代码
五、路由
路由就是设定URL和返回页面或者视图的对应关系,每个URL对应一个页面或者视图。
六、vuex
类Flux的状态管理。解决的问题是:vue原始数据被多个实例间共享时,如果被修改,将导致多个实例发生改变,且无法追溯是谁修改的。这容易造成混乱和不安全。
- vuex可以简单为,将每次对vue原始数据的修改都作记录
- 进阶理解:引用Flux思想,将data包裹上state,形成一个Store。View需要修改,可以发送一个action给dispatcher,然后由dispatcher去通知Store,修改完,View再更新。
更深入的理解 juejin.cn/post/695247…