如何理解组件化
组件化就是将我们的页面按照业务逻辑、功能划分成为不同的模块化组件。每个模块只完成自己特定的功能,从而使得整个项目中的组件高内聚低耦合。组件化的尝试早在jsp、asp时代就已经在使用,但是当时的组件化属于静态组件化,通过后端服务器将组件渲染生成静态DOM元素,而如今的react,vue所构建的是动态组件化。每个组件基于数据驱动视图,动态变化,使前端开发人员不再将关注重点放在DOM操作中,而只需要关心数据流转。
解释下MVVM
MVVM分为三个模块。
- M model层: 数据模型,用于定义数据和业务逻辑
- V view层: UI视图层-DOM元素
- VM viewModel层: 有两个主要功能。①将Model数据的更新到DOM元素,②处理DOM事件,并更新数据层数据。 这种模式基于数据驱动视图,前端开发人员只需注重对数据的维护,不需要对DOM进行操作,有利于构建复杂的项目。
Vue响应式原理
核心API:
- 2.x 使用Object.defineProperty
- 缺点:
- 深度监听,使用递归监听到底,一次性计算量大
- 无法监听新增属性、删除属性。(Vue.set,Vue.delete)
- 无法原生监听数组,需要重写数据原型实现。
- 缺点:
- 3.x 使用Proxy
- 优点:
- 深度监听不是一次性递归监听,而是触发get时才会添加监听
- 可以监听删除属性、新增属性
- 可以监听数组变化
- 缺点:
- 无法兼容所有浏览器,且无法polyfill
- 优点:
vue2.x中如何监测数组变化
vue将data中的数组的原型链进行了重写,指向了自己定义的数组原型方法。当数组调用api时,就可以实时监听到数据变化,通知依赖更新。如果数组中包含引用类型。会对数组中的引用类型继续进行递归遍历进行监控。
vue生命周期
| 生命周期 | 描述 |
|---|---|
| beforeCreate | 组件实例被创建之初,组件的属性生效之前 |
| created | 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用 |
| beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用 |
| mounted | el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子 |
| beforeUpdate | 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前 |
| update | 组件数据更新之后 |
| activited | keep-alive 专属,组件被激活时调用 |
| deactivated | keep-alive 专属,组件被销毁时调用 |
| beforeDestory | 组件销毁前调用 |
| destoryed | 组件销毁后调用 |
- 生命周期示意图
computed 和 watch 的区别
- computed:是计算属性,依赖其他属性的值。具有缓存,只有他依赖的值发生变化,下一次取当前属性时才会重新计算。这样避免了每次取值时都重新计算。内部不允许改变其他属性的值。且必须返回当前属性值。
- watch:
- 观察作用。用于监听某个值变化后的回调操作。而且watch内部可以允许异步操作。内部可以同时改变多个属性的值或者执行多个方法。
- watch默认对引用类型数据只做一层监听。可以通过以下方法实现深度监听。但是监听引用类型的数据。无法取到oldval。
watch:{
info: {
handler: function (oldVal, newVal) {
console.log(oldVal, newVal)
},
deep: true
}
}
vue中 父组件和子组件生命周期函数执行的顺序
按照不同的过程可以分为4类:
-
加载渲染的过程 父组件beforeCreate -> 父组件created -> 父组件beforeMounted -> 子组件beforeCreate -> 子组件created -> 子组件beforeMount -> 子组件 mounted -> 父组件 mounted
-
子组件更新 父组件 beforeUpdate -> 子组件 beforeUpdate -> 子组件 updated -> 父组件updated
-
父组件更新 父组件 beforeUpdate -> 父组件updated
-
销毁 父组件 beforeDestroy -> 子组件 beforeDestroy -> 子组件 destroyed -> 父组件 destroyed
v-model 原理
v-model指令实际上就是语法糖,在表单元素上创建双向数据绑定。根据不同的表单元素,绑定不同的属性值和事件实现双向数据绑定。
- input[type='text']、textarea 绑定属性value 和 input 事件
<input v-model="a">
<!-- 相当于 -->
<input :value="a" @input="a = $event.target.value">
- input[type='checkbox']、input[type='radio'] 绑定属性checked 和 change 事件
<input type="checkbox" v-model="a">
<!-- 相当于 -->
<input :checked="a" @change="a = $event.target.checked">
- select 绑定属性value 和 change 事件
<select v-model="a"></select>
<!-- 相当于 -->
<select :value="a" @change="a = $event.target.value"></select>
在自定义组件上使用v-model,相当于给自定义组件传入一个属性 value, 并且绑定一个input方法
// 父组件:
<Parent v-model="a"></Parent>
// 子组件:
<template>
<div>{{value}}</div>
</template>
<script>
export default {
props:{
value: String
},
methods: {
test(){
this.$emit('input', 'aaaaaa')
},
},
}
</script>
vue 组件间通信的方法
props / $emit()用于父子组件间的通信ref和$parent / $children用于父子组件间的通信
- ref 绑定在DOM元素上是返回的是DOM元素,绑定在Vue组件上时返回的是Vue组件的实例对象
- EventBus
$emit() / $on用于父子组件、隔代组件和兄弟组件间的通信
// bus.js
import Vue from 'vue'
export default new Vue()
// 在组件中created()方法中监听事件
import Bus from 'path/to/bus'
export default {
created(){
Bus.$on('clickme', target => {
console.log('clickme', target)
})
}
}
// 在其他组件中触发事件
import Bus from 'path/to/bus'
export default {
methods:{
test(){
Bus.$emit('clickme', '啦啦啦啦啦')
}
}
}
$attrs / $listeners用于隔代组件间通信
通过$attrs向子孙组件传递属性
通过$listeners子孙组件向爷爷组件$emit()通信
看到一篇文章讲解很详细
provide / inject用于隔代组件间通信
// 爷爷组件
<script>
export default {
provide:{
a:1
}
}
</script>
// 孙子组件
<template>
<div>{{ a }}</div>
</template>
<script>
export default {
inject:{
a:{
defalut: 0
}
}
}
</script>
- vuex 用于父子组件、隔代组件、兄弟组件间通信
Vue模版编译原理
- 首先构建AST树。使用正则进行模板解析。
- 优化AST树,深度遍历AST树,将不是响应式的节点进行标记。这样这些节点就会跳过他们的对比,优化对比性能
- 转换代码,将优化后的AST树转换为render函数,执行render函数返回vnode。
Vue.extend、Vue.component、Vue.mixin的区别
- Vue.extend:传入一个对象option,包含组件选项的对象。最后返回一个子组件构造器方法.
- Vue.component:用于注册全局组件,传入两个参数。id和构造器。也就是说第二个参数就是一个vue.extend({})。如果第二个参数传入的是一个组件选项的对象,则会自动调用vue.extend
- Vue.mixin: 全局混入。用于将一些公用的方法注册进每个Vue实例中。
- 当一个组件中即存在mixin,还存在extend时。其中的同名属性。最终使用mixin的值。
vue性能优化的方式
- 合理使用v-show和v-if
- 合理使用computed
- v-for是使用key,不和v-if同时使用
- 自定义事件、DOM事件、定时器及时销毁
- 合理使用异步组件
- 合理使用keep-alive
- data层级不要太深。在实现响应式是会一次性深度递归添加监听函数,深度太深影响性能。