赞
踩
组件生命周期
ref与$refs
nextTick
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <me-component :t="title"></me-component> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); }, // 构建完成以后 created() { console.log('meComponent:created'); }, // 组件挂载之前 beforeMount() { console.log('meComponent:beforeMount'); }, // 组件挂载完成以后 mounted() { console.log('meComponent:mounted'); }, // 组件更新 beforeUpdate() { console.log('meComponent:beforeUpdate'); }, // 组件更新后 updated() { console.log('meComponent:updated'); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true }, components: { meComponent }, beforeCreate() { console.log('beforeCreate'); }, // 构建完成以后 created() { console.log('created'); }, // 组件挂载之前 beforeMount() { console.log('beforeMount'); }, // 组件挂载完成以后 mounted() { console.log('mounted'); }, // 组件更新 beforeUpdate() { console.log('beforeUpdate'); }, // 组件更新后 updated() { console.log('updated'); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { console.log('destroyed'); } }); </script> </body> </html>
过程有点像递归,即先进后出
。
父组件从创建阶段到挂载之前阶段的beforeMount()
,父组件开始挂载之后,开始解析子组件,解析过程中发现有子组件,再由子组件从创建阶段到挂载阶段完成,最后到父组件挂载阶段的mounted()
完成。
最先开始创建的是最外层的组件,但是最先创建完的是最内层的组件。
其实整个就是一个递归解析的过程。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.21
Branch: branch04commit description:a1.21(组件生命周期引例)
tag:a1.21
new Vue()
之后进行初始化事件和生命周期,初始化完成之后再调用beforeCreate()
,调用之后在这一步对数据进行依赖注入、劫持、监听等。
完成之后,再调用created()
。
首先判断是否有el
选项,如果有el
选项,再判断有没有template
选项。
如果有el
选项而没有template
选项,它会把el
选项里内容作为innerhtml
和outerhtml
的template
,同时需要手动去挂载,调用$mount(el)
。
如果有template
则解析模板,否则将outerhtml
作为模板解析。
把模板解析成虚拟dom
以后,这个时候就开始准备挂载了,准备挂载调用beforeMount()
,函数执行完成之后对模板进行渲染,然后把渲染后的结果(真实dom
)处理完成,最后给$el
设置好根组件(根节点)。
这个时候就可以调用组件的根节点$el
了。
整个执行完成后,就相当于dom结构树
已经渲染完成了,这个时候就执行mounted()
函数了。
进入mounted()
,这个时候组件已经在页面上了,并时刻监听组件相关的一些变化。
当数据(data
)发生改变的时候,会首先执行beforeUpdate()
,再对虚拟dom
进行补丁式修改(修复)/变更,即根据变化地数据重新组织页面或结构。
然后重新渲染(更新)完成以后,执行updated
函数后继续监听组件相关变化。
以后就反复在这里循环了,数据发生变化,走一圈。其实这里就是一个循环,只要数据变化,就会走这个循环。直到将其(组件)从页面(应用中)上移除,当组件移除(或手动调用$destroy
方法),就会触发beforeDestroy()
。
执行beforeDestroy()
函数,在这个函数中会对组件进行一些销毁动作,比如把其内部的事件、数据、子组件全部去掉,处理完后,就会触发destroyed()
。
执行destroyed()
函数后,代表全部销毁完成了。
这就是vue
组件从无到有的一个过程。
注意:不是所有的生命周期阶段都是可以随意可以使用的,不同的阶段,有不同的事情可以做,具体往下看。
组件生命周期指的是组件从创建到销毁的过程,在这个过程中的一些不同的阶段,vue
会调用指定的一些组件方法
基本生命周期函数有下面几个阶段:
每一个阶段都对应着 之前 和 之后 两个函数
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <h1>{{title}}</h1> <button @click="show=!show">隐藏</button> <hr> <template v-if="hasError"> <h4>有错误发生了</h4> </template> <template v-else> <me-component v-if="show" :t="title"></me-component> </template> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t.a.b}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, created() { console.log('meComponent:created'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, beforeMount() { console.log('meComponent:beforeMount'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, mounted() { console.log('meComponent:mounted'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, beforeUpdate() { console.log('meComponent:beforeUpdate'); console.log('props', this.$props); console.log('='.repeat(100)); }, updated() { console.log('meComponent:updated'); console.log('='.repeat(100)); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); console.log('this', this); console.log('='.repeat(100)); }, destroyed() { console.log('meComponent:destroyed'); console.log('this', this); console.log('='.repeat(100)); } } let app = new Vue({ el: '#app', data: { title: 'csdn', show: true, hasError: false }, components: { 'me-component': meComponent }, beforeCreate() { console.log('beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, created() { console.log('created'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, beforeMount() { console.log('beforeMount'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, mounted() { console.log('mounted'); console.log('data', this.$data); console.log('el', this.$el); console.log('='.repeat(100)); }, beforeUpdate() { console.log('beforeUpdate'); console.log('props', this.$props); console.log('='.repeat(100)); }, updated() { console.log('updated'); console.log('='.repeat(100)); }, beforeDestroy() { console.log('beforeDestroy'); console.log('this', this); console.log('='.repeat(100)); }, destroyed() { console.log('destroyed'); console.log('this', this); console.log('='.repeat(100)); }, errorCaptured(err, vm, info) { console.log('errorCaptured'); console.log(err, vm, info); console.log('='.repeat(100)); this.hasError = true; return false; } }); </script> </body> </html>
初始化阶段,应用不多,类似React
的constructor
。
我们看最开始的图例,
new Vue()
之后进行初始化事件和生命周期,初始化完成之后再调用beforeCreate()
,调用之后在这一步对数据进行依赖注入、劫持、监听等。完成之后,调用created()
在实例创建完成后被立即调用,该阶段完成了对 data
中的数据的 observer
(对数据的拦截),该阶段可以处理一些异步任务,如发Ajax
请求。不过有人也喜欢将异步请求放入beforeMount()
和mounted(
)里,其实大致上没多大区别。
看图例,首先判断是否有
el
选项,如果有el
选项,再判断有没有template
选项。如果有
el
选项而没有template
选项,它会把el
选项里内容作为innerhtml
和outerhtml
的template
,同时需要手动去挂载,调用$mount(el)
。如果有
template
则解析模板,否则将outerhtml
作为模板解析。把模板解析成虚拟
dom
以后,这个时候就开始准备挂载了,准备挂载调用beforeMount()
在挂载开始之前被调用,应用不多。
看图例,由上一步解析成虚拟dom后,调用
beforeMount()
,函数执行完成之后对模板进行渲染,然后把渲染后的结果(真实dom
)处理完成,最后给$el
设置好根组件(根节点)。这个时候就可以调用组件的
根节点$el
了。整个执行完成后,就相当于
dom结构树
已经渲染完成了,这个时候就执行mounted()
函数了。
该阶段执行完了模板解析,以及挂载。同时组件根组件元素被赋给了 $el
属性,该阶段可以通过 DOM 操作来对组件内部元素进行处理了。该阶段其实就可以获取 $el
属性值了,即该阶段可进行dom
操作了,因为还没解析出来dom
,因此在之前的阶段是不能进行dom
操作的。
看图例,进入
mounted()
,这个时候组件已经在页面上了,并时刻监听组件相关的一些变化。看是更新还是销毁等。
在根组件中打印$data
和$el
beforeCreate() {
console.log('beforeCreate');
console.log('data', this.$data);
console.log('el', this.$el);
},
都为空,beforeCreate
代表组件才开始做事,这个时候数据也没处理,渲染也没完成,因此必然是空的。
如果现在的动作与当前组件的相关数据没有关系的话,就可以在这个生命周期实现了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.22
Branch: branch04commit description:a1.22(example02-1——beforeCreate组件生命周期——打印data和el)
tag:a1.22
再看看下一阶段 => created
beforeCreate() {
console.log('beforeCreate');
console.log('data', this.$data);
console.log('el', this.$el);
},
created() {
console.log('created');
console.log('data', this.$data);
console.log('el', this.$el);
},
数据已经有了,被拦截了。
但是el
还没有,el
是在mounted
周期才有的。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.23
Branch: branch04commit description:a1.23(example02-2——Created的组件生命周期——打印data和el)
tag:a1.23
beforeMount() {
console.log('beforeMount');
console.log('el', this.$el);
}
这个时候已经可以拿到当前节点了,但是是否真的拿到了吗?
我们换到子组件,看一下。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.24
Branch: branch04commit description:a1.24(example02-3——beforeMount的跟组件生命周期——打印el)
tag:a1.24
子组件beforeMount
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <me-component :t="title"></me-component> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); }, // 构建完成以后 created() { console.log('meComponent:created'); }, // 组件挂载之前 beforeMount() { console.log('meComponent:beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('meComponent:mounted'); }, // 组件更新 beforeUpdate() { console.log('meComponent:beforeUpdate'); }, // 组件更新后 updated() { console.log('meComponent:updated'); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true }, components: { meComponent }, beforeCreate() { console.log('beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); }, // 构建完成以后 created() { console.log('created'); console.log('data', this.$data); console.log('el', this.$el); }, // 组件挂载之前 beforeMount() { console.log('beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('mounted'); }, // 组件更新 beforeUpdate() { console.log('beforeUpdate'); }, // 组件更新后 updated() { console.log('updated'); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { console.log('destroyed'); } }); </script> </body> </html>
我们看显示结果有点不一样了,子组件的el
是undefined
,根组件是有数据的。
因为子组件是写在一个模板中的,模板没解析完成,肯定获取不到。
为啥根组件可以,因为根组件直接取的是页面上已经存在的元素,即<div id="app">
。
如果根组件模板拿的并不是outerhtml
,而是template
的话,这样就el
就获取不到了。
所以实际上beforeMount
的生命周期实际上是拿不到组件的$el
,注意此种特殊情况。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.25
Branch: branch04commit description:a1.25(example02-4——beforeMount的子组件生命周期——打印el)
tag:a1.25
根组件 => mounted
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <me-component :t="title"></me-component> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); }, // 构建完成以后 created() { console.log('meComponent:created'); }, // 组件挂载之前 beforeMount() { console.log('meComponent:beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('meComponent:mounted'); }, // 组件更新 beforeUpdate() { console.log('meComponent:beforeUpdate'); }, // 组件更新后 updated() { console.log('meComponent:updated'); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true }, components: { meComponent }, beforeCreate() { console.log('beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); }, // 构建完成以后 created() { console.log('created'); console.log('data', this.$data); console.log('el', this.$el); }, // 组件挂载之前 beforeMount() { console.log('beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('mounted'); console.log('el', this.$el); }, // 组件更新 beforeUpdate() { console.log('beforeUpdate'); }, // 组件更新后 updated() { console.log('updated'); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { console.log('destroyed'); } }); </script> </body> </html>
根组件mounted()
生命周期才是实际能取到el
的生命周期,组件已经挂载完成了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.26
Branch: branch04commit description:a1.26(example02-5——Mounted的根组件生命周期——打印el)
tag:a1.26
以上其实就是组件从无到有的一个过程。
数据更新时调用,但是还没有对视图进行重新渲染,这个时候,可以获取视图更新之前的状态。
看图例,执行完毕
mounted()
=> 刻监听组件相关的一些变化当数据(
data
)发生改变的时候,会首先执行beforeUpdate()
,再对虚拟dom
进行补丁式修改(修复)/变更,即根据变化地数据重新组织页面或结构。然后重新渲染(更新)完成以后,执行
updated
函数。
由于数据的变更导致的视图重新渲染,可以通过 DOM 操作来获取视图的最新状态。
看图例,重新渲染(更新)完成以后,执行
updated
函数后继续监听组件相关变化。以后就反复在这里循环了,数据发生变化,走一圈。其实这里就是一个循环,只要数据变化,就会走这个循环。直到将其(组件)从页面(应用中)上移除,当组件移除(或手动调用
$destroy
方法),就会触发beforeDestroy()
。
隐藏子组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="show=!show">隐藏</button> <me-component :t="title" v-if="show"></me-component> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); }, // 构建完成以后 created() { console.log('meComponent:created'); }, // 组件挂载之前 beforeMount() { console.log('meComponent:beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('meComponent:mounted'); }, // 组件更新 beforeUpdate() { console.log('meComponent:beforeUpdate'); }, // 组件更新后 updated() { console.log('meComponent:updated'); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true }, components: { meComponent }, beforeCreate() { console.log('beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); }, // 构建完成以后 created() { console.log('created'); console.log('data', this.$data); console.log('el', this.$el); }, // 组件挂载之前 beforeMount() { console.log('beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('mounted'); console.log('el', this.$el); }, // 组件更新 beforeUpdate() { console.log('beforeUpdate'); }, // 组件更新后 updated() { console.log('updated'); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { console.log('destroyed'); } }); </script> </body> </html>
点击隐藏按钮 =>
这个时候会触发根组件beforeUpdate、updated
生命周期,
以及子组件的meComponent:beforeDestroy、meComponent:destroyed
生命周期。
因为用的v-if
,此时就会把子组件从页面中删除,因此触发了销毁。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.27
Branch: branch04commit description:a1.27(example03-1——v-if隐藏子组件)
tag:a1.27
点击按钮 => 更新数据 => title
<div id="app">
<button @click="show=!show">隐藏</button>
<button @click="title='me'">更新title</button>
<me-component :t="title"></me-component>
</div>
更新title
后,此时触发父组件的beforeUpdate
、子组件的meComponent:beforeUpdate、meComponent:updated
,及父组件的updated
。
触发父组件更新后 => 子组件也会更新。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.28
Branch: branch04commit description:a1.28(example03-2——点击按钮 => 更新数据 => title)
tag:a1.28
页面放两个需要可以更新的子组件。
<div id="app">
<button @click="show=!show">隐藏</button>
<button @click="title='me'">更新title</button>
<me-component :t="title"></me-component>
<me-component :t="title"></me-component>
</div>
点击更新title
=> 此时子组件的生命周期触发了两次,因为这两个子组件都需要更新。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.29
Branch: branch04commit description:a1.29(example03-3——点击按钮 => 更新数据(两个子组件) => title)
tag:a1.29
设置两个不同title
的子组件,点击按钮 => 只更新:t="title"
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="show=!show">隐藏</button> <button @click="title='me'">更新title</button> <me-component :t="title"></me-component> <me-component :t="subtitle"></me-component> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); }, // 构建完成以后 created() { console.log('meComponent:created'); }, // 组件挂载之前 beforeMount() { console.log('meComponent:beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('meComponent:mounted'); }, // 组件更新 beforeUpdate() { console.log('meComponent:beforeUpdate'); }, // 组件更新后 updated() { console.log('meComponent:updated'); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', subtitle: '前端开发', // 子标题 show: true }, components: { meComponent }, beforeCreate() { console.log('beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); }, // 构建完成以后 created() { console.log('created'); console.log('data', this.$data); console.log('el', this.$el); }, // 组件挂载之前 beforeMount() { console.log('beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('mounted'); console.log('el', this.$el); }, // 组件更新 beforeUpdate() { console.log('beforeUpdate'); }, // 组件更新后 updated() { console.log('updated'); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { console.log('destroyed'); } }); </script> </body> </html>
点击按钮 =>
这个时候,点击发现只有一个子组件更新了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.30
Branch: branch04commit description:a1.30(example03-4——设置两个不同
title
的子组件,点击按钮 => 只更新:t="title"
)tag:a1.30
当一个组件更新过程中,其内部数据更新的时候,这个组件就会重新渲染,但是重新渲染,并不代表整个内容全部销毁重来,而是只会改变得东西。不变的东西,是不会发生任何变化的。 => 组件复用
当一个组件props
发生改变的时候,就会触发该组件的重新渲染。
一个组件的data
和props
不论谁更新了,都会触发该组件的重新渲染。
但是如果某个组件(如本例 => <me-component :t="subtitle"></me-component>
)在渲染的过程中,它没有更新数据,这个组件就不会调用重新渲染的接口。
以上与React
似乎不太一样!
在React
中更新以后,其组件是都会更新的,即父组件数据更新,子组件都会触发对应的生命周期,在其生命周期中决定是否渲染,生命周期在React
当中是一定会触发的,但是会不会渲染重新构建这个结构是不一定的,它会有自己的diff
算法去比较。
而在Vue
当中是不一样的,前期只要发现没更新,连生命周期函数都不执行的!
因此Vue
和React
大体思想一样,只是有一些细微的差别。
React
=> 更新了都会去执行生命周期,但是需不需要渲染整个结构,看比较的结果。
Vue
=> 发现了数据不更新就压根不管,根本不会进入生命周期。
当捕获一个来自子孙组件的错误时被调用,此钩子会收到三个参数:
错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。
此钩子可以返回 false
以阻止该错误继续向上传播。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h1>{{title}}</h1> <hr> <template v-if="hasError"> <h4>有错误发生了</h4> </template> <template v-else> <me-component v-if="show" :t="title"></me-component> </template> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, beforeCreate() { console.log('meComponent:beforeCreate'); }, // 构建完成以后 created() { console.log('meComponent:created'); }, // 组件挂载之前 beforeMount() { console.log('meComponent:beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('meComponent:mounted'); }, // 组件更新 beforeUpdate() { console.log('meComponent:beforeUpdate'); }, // 组件更新后 updated() { console.log('meComponent:updated'); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true, hasError: false }, components: { meComponent }, beforeCreate() { console.log('beforeCreate'); console.log('data', this.$data); console.log('el', this.$el); }, // 构建完成以后 created() { console.log('created'); console.log('data', this.$data); console.log('el', this.$el); }, // 组件挂载之前 beforeMount() { console.log('beforeMount'); console.log('el', this.$el); }, // 组件挂载完成以后 mounted() { console.log('mounted'); console.log('el', this.$el); }, // 组件更新 beforeUpdate() { console.log('beforeUpdate'); }, // 组件更新后 updated() { console.log('updated'); }, beforeDestroy() { console.log('beforeDestroy'); }, destroyed() { console.log('destroyed'); }, //err:错误 vm:虚拟dom(组件实例) info:信息 errorCaptured(err, vm, info) { console.log('errorCaptured'); console.log(err, vm, info); this.hasError = true; return false; } }); </script> </body> </html>
此时正常显示,子组件没有错误。
发生错误: => 调用不存在的数据
const meComponent = {
props: ['t'],
template: `
<div>
<h1>meComponent - {{t.a.b}}</h1>
</div>
`,
错误被浏览器拦截捕获了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.31
Branch: branch04commit description:a1.31(example04-1——错误捕获使用)
tag:a1.31
不进行错误显示。
<me-component v-if="show" :t="title"></me-component>
<!-- <template v-if="hasError">-->
<!-- <h4>有错误发生了</h4>-->
<!-- </template>-->
<!-- <template v-else>-->
<!-- <me-component v-if="show" :t="title"></me-component>-->
<!-- </template>-->
错误被浏览器拦截捕获了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.32
Branch: branch04commit description:a1.32(example04-2——错误捕获使用—页面不做处理)
tag:a1.32
不做捕获处理。
//err:错误 vm:虚拟dom(组件实例) info:信息
// errorCaptured(err, vm, info) {
// console.log('errorCaptured');
// console.log(err, vm, info);
// this.hasError = true;
// return false;
// }
如果错误也不处理了,直接交给vue
。 => 浏览器直接抛出error
报错,这样显示给用户没有任何效果,这样显示不太友好,所以最好父级拦截错误,并根据拦截到的错误,给用户展示比较好的页面(如给用户展示 => 页面出现故障,请联系管理,附上联系方式),不要让用户感觉应用做的很差。
跟React
的错误生命周期的概念是一样的,用法、作用也是一样的。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.33
Branch: branch04commit description:a1.33(example04-3——错误捕获使用—页面不做处理、不做捕获处理)
tag:a1.33
实例销毁之前调用,移除一些不必要的冗余数据,比如定时器。
看图例,执行
beforeDestroy()
函数,在这个函数中会对组件进行一些销毁动作,比如把其内部的事件、数据、子组件全部去掉,处理完后,就会触发destroyed()
。
Vue 实例
销毁后调用
看图例,执行
destroyed()
函数,代表全部销毁完成了。
组件销毁的时候,会把组件的事件绑定等等也会处理掉,但是有些东西是处理不掉的。
即与它的数据没有绑定的东西,比如定时器。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="show=!show">隐藏</button> <me-component v-if="show" :t="title"></me-component> </div> <script src="./js/vue.js"></script> <script> const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, created() { setInterval(() => { console.log(1); }, 100); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true, }, components: { meComponent } }); </script> </body> </html>
虽然销毁了组件,会发现定时器还是存在的!
这是一个大坑,因为销毁组件的生命周期不会自动帮你调用clear
。
类似这种东西,得手动自行销毁。如在组件生命周期中发了一个请求,但是这个请求比较耗时,但是还没请求完毕,组件就销毁了。
但是这个请求也不会随着组件的销毁而取消,所以需要手动回收和处理请求。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.34
Branch: branch04commit description:a1.34(example05-1——卸载阶段-处理不了异步相关内容)
tag:a1.34
所以很多可以长时间存在,比较耗时的任务,又与组件本身没有太大的关系,这个时候得需要我们自己手动清理。
const meComponent = { props: ['t'], template: ` <div> <h1>meComponent - {{t}}</h1> </div> `, created() { this._timer = setInterval(() => { console.log(1); }, 100); }, beforeDestroy() { console.log('meComponent:beforeDestroy'); }, destroyed() { console.log('meComponent:destroyed'); clearInterval(this._timer); } }; let app = new Vue({ el: '#app', data: { title: 'Github', show: true, }, components: { meComponent } });
这个时候就会发现定时器也没了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.35
Branch: branch04commit description:a1.35(example05-2——卸载阶段-处理不了异步相关内容 => 自行销毁)
tag:a1.35
如果我们希望获取组件节点,进行 DOM 相关操作,可以通过 ref
和 $refs
来完成
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <h1>{{title}}</h1> <button @click="getBoxHeight">获取 box 的高度</button> <button @click="getmeComponent">获取自定义组件实例及内部方法</button> <hr> <div ref="box"> 这是内容<br>这是内容<br>这是内容<br>这是内容<br>这是内容<br> </div> <hr> <me-component ref="me" :t="title"></me-component> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const meComponent = { props: ['t'], data() { return { isShow: true } }, template: ` <div v-if="isShow"> <h1>meComponent - {{t}}</h1> </div> `, methods: { hide() { this.isShow = false; } } } let app = new Vue({ el: '#app', data: { title: 'csdn' }, components: { 'me-component': meComponent }, mounted() { console.log(this.$refs.me); }, methods: { getBoxHeight() { console.log( this.$refs.box.clientHeight ); }, getmeComponent() { this.$refs.me.hide(); } } }); </script> </body> </html>
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.36
Branch: branch04commit description:a1.36(ref 与 $refs)
tag:a1.36
给元素或组件添加 ref
属性,则该元素或组件实例对象将被添加到当前组件实例对象的 $refs
属性下面
该属性的是一个对象,存储了通过 ref
绑定的元素对象或者组件实例对象
当数据更新的时候,视图并不会立即渲染,这个时候我们期望获取到视图更新后的数据,可以通过 nextTick
来进行操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <h1>{{title}}</h1> <button @click="setBoxContent">设置新的内容</button> <hr> <div ref="box" style="background: red" v-html="content"></div> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> let app = new Vue({ el: '#app', data: { title: 'csdn', n: 1 }, computed: { content() { return new Array(this.n).fill(this.title).join('<br>'); } }, methods: { setBoxContent() { this.n++; this.$nextTick(_=>{ console.log( this.$refs.box.clientHeight ); }) } } }); </script> </body> </html>
nextTick
方法将在更新队列循环结束之后立即调用
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.37
Branch: branch04commit description:a1.37(nextTick使用)
tag:a1.37
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。