原稿:语雀 · Vue 知识点 · 原目录:Vue › Vue 知识点
组件通信
通信方式10种
Vuex 和单纯的全局对象的区别
Vuex 和全局对象主要有两大区别:响应式 & 提交更改的方式 2. Vuex 的状态存储是响应式的:当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 4. 不能直接改变 store 中的状态:改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
Vuex 的 mutation 中为什么不能异步
Vuex 中所有的 state 更新的唯一途径都是 mutation,异步操作必须通过 Action 提交 mutation 实现。
- actions 只是一个架构性的概念,并不是必须的,说到底只是一个函数,在里面想干嘛都可以,只要最后触发 mutation 就行。异步竞态怎么处理那是用户自己的事情。
- vuex 真正限制的只有 mutation 必需同步,每个 mutation 执行完成后都会对应到一个新的状态变更,这样 devtools 就可以打个快照存下来,然后就可以实现 time-travel 了。如果 mutation 支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
生命周期
Vue 组件有哪些生命周期钩子?
参考链接:生命周期钩子图示 生命周期钩子 API
beforeCreate | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用 |
完成以下配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调 | |
created | 在实例创建完成后被立即调用。然而,挂载阶段还没开始,$el property 目前尚不可用 |
| |
beforeMount | 在 mountComponent 中,创建 watcher 之前被调用:此时 el 属性可访问,但执行的是挂载的目标 DOM |
实例完成挂载,el 被新创建的 vm.$el 替换 | |
mounted | 实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内 |
beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器 |
完成由于数据更改导致的虚拟 DOM 重新渲染和打补丁(patch) | |
updated | 这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之 |
beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用 |
销毁 watchers、子组件、事件监听器 | |
destroyed | 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁 |
<keep-alive> 有自己独立的钩子函数 activated 和 deactivated,见 Keep-alive 与路由切换
Vue 的父子组件生命周期钩子执行顺序
渲染过程:
父组件挂载完成一定是等子组件都挂载完成后,才算是父组件挂载完,所以父组件的mounted在子组件mouted之后
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子组件更新过程: 2. 影响到父组件:父beforeUpdate -> 子beforeUpdate->子updated -> 父updated 4. 不影响父组件:子beforeUpdate -> 子updated
父组件更新过程: 2. 影响到子组件:父beforeUpdate -> 子beforeUpdate->子updated -> 父updated 4. 不影响子组件:父beforeUpdate -> 父updated
销毁过程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
不管是哪种情况,都一定是父组件等待子组件完成后,才会执行自己对应完成的钩子
可以使用 console.log 查看钩子执行情况
小结:
|
钩子与重要节点
new Vue() -> initMixin()
-> initLifecycle(vm)
-> initEvents(vm)
-> initRender(vm)
-> callHook(vm,"beforeCreate");
-> initState(vm)
-> initData()/initMethods()/initComputed()/initWatch()
-> callHook(vm,"created");
-> vm.$mount() -> compileToFunctions()
-> mountComponent()
-> callHook(vm,"beforeMount");
-> new Watcher()
-> callHook(vm,"mounted");
- beforeCreate() 前:实例初始化,数据观测未开始、方法、计算属性、侦听回调都未初始化
- created() 前:数据观测完成、方法、计算属性、侦听回调初始化完成
- beforeMount() 前:完成模板编译,即将创建 watcher 开始渲染
- mounted() 前:完成渲染
属性和指令
v-show 对比 v-if
- v-if 会在切换过程中对条件块的事件监听器和子组件进行销毁和重建,如果初始条件是 false,则不会渲染该 DOM,直到条件第一次为 true 时才开始渲染模块(惰性)
- v-show 只是基于 css 进行切换,不管初始条件是什么,都会渲染。带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display
<h1 v-show="ok">Hello!</h1>
总结:v-if 切换的开销更大,而 v-show 初始化渲染开销更大,在需要频繁切换,或者切换的部分 DOM 很复杂时,使用 v-show 更合适。渲染后很少切换的则使用 v-if 更合适
computed 对比 watch
- computed 计算属性,是依赖其他属性的计算值,作为新的属性,有缓存,只有当依赖的值变化时才会更新,需显式返回
- watch 是在监听的属性发生变化时,在回调中执行一些逻辑,该属性本身存在于 data 或 prop 中
选择:
|
<div id="demo">{{ fullName }}</div>
// 计算属性版本
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
// 侦听属性版本
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
computed 对比 methods
- 计算属性是基于他们的响应式依赖进行缓存的,在依赖发生变化时,会响应式地计算求值
- methods 需要被调用,每次都会执行相应的方法
Vue 中 v-html 会导致什么问题
v-html 的作用:更新元素的 innerHTML,按普通 HTML 插入,而不会作为 Vue 模板进行编译。在网站上动态渲染任意 HTML,很容易导致 XSS 攻击。所以只能在可信内容上使用 v-html,且永远不能用于用户提交的内容上。
下例中,假设组件的 rawHtml prop 值为 <span style=“color:red”></span>,则使用 {{}} 会把该值解析为文本,但使用 v-html 则会编译这段 innerHTML