1.引出生命周期
假如现在要实现一个更改透明度的功能:(让这几个字周而复始的消失和出现)
GIF
实现代码:
方法一:普通方法
代码语言:javascript复制<body>
<div id="root">
<h1 :style="{opacity:opacity}">学编程的GISer</h1>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
opacity: 1,
},
});
setInterval(() => {
vm.opacity -= 0.01;
if (vm.opacity <= 0) {
vm.opacity = 1;
}
}, 16);
</script>
</body>
但是上述代码还不够完美,如果我们想在实例化的Vue对象中实现这个功能,要怎么做呢?看⬇
方法二:先尝试用methods实现(剧透:实现不了):
尝试用methods实现,但是出现bug
要使用方法必须要调用方法,而这个方法中没有return,正好在调用的时候返回的是undefined,在也页面上不会显示,但是发现这样行不通,因为方法里面会修改opacity的值,这样又会重新渲染模板,然后重新调用方法,再修改opacity,再重新渲染模板,调用方法,修改opacity…… 这样会导致方法调用的次数成指数型大爆炸式增长。
方法三:用生命周期函数
生命周期函数钩子:在某个时间点会自动执行的函数。
引入生命周期函数中的mounted(){}
:
<body>
<div id="root">
<h1 :style="{opacity:opacity}">学编程的GISer</h1>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
opacity: 1,
},
methods: {},
mounted() {
setInterval(() => {
this.opacity -= 0.01;
if (this.opacity <= 0) {
this.opacity = 1;
}
}, 16);
console.log(this);
},
});
</script>
</body>
实现该功能:
GIF
解释一下mounted(){}
:这个生命周期钩子是在Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)才调用的。
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子(Vue会在一个特殊的时刻把生命周期函数勾出来调用)。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。
2.挂载流程
生命周期的挂载、更新和销毁流程图:
下边会对流程图中的①—⑩进行逐一讲解:
(1)证明①:beforeCreate
此时还没有做数据代理、数据监测
代码语言:javascript复制<body>
<div id="root">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
beforeCreate() {
console.log('beforeCreate', this);
debugger; //断点,使用断点要打开Live Server之后打开控制台,再刷新页面(不开控制台断点卡不进去)
},
});
</script>
</body>
(2)证明②:created
此时已经完成数据代理及数据监测,但是页面上还没有显示出经过Vue编译后的DOM结构
代码语言:javascript复制<body>
<div id="root">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
beforeCreate() {
console.log('beforeCreate');
},
created() {
console.log('created', this);
debugger;
},
});
</script>
</body>
(3)证明③:beforeMounte
证明页面所呈现的是未经Vue编译的DOM结构:
证明此时对DOM操作时不奏效的:
代码语言:javascript复制<body>
<div id="root">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
beforeCreate() {
console.log('beforeCreat', this);
},
beforeMount() {
console.log('beforeMount', this);
document.querySelector('h1').innerHTML = '我更改了DOM';
},
});
</script>
</body>
点击刷新,h1的内容并没有被修改,证明操作DOM无效:
GIF
(4)证明④:mounted
证明此时页面中呈现的都是经过Vue编译的DOM:
代码语言:javascript复制<body>
<div id="root">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
mounted() {
console.log('mounted', this);
debugger;//断点
},
});
</script>
</body>
证明对DOM的操作有效:
代码语言:javascript复制<body>
<div id="root">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
mounted() {
console.log('mounted', this);
//操作DOM:
document.querySelector('h1').innerHTML = 'DAPAN'
debugger;
},
});
</script>
</body>
(5)证明⑤
如果没有el:'#root'
,也没有vm.$mount('#root')
:
<body>
<div id="root">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
// el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
beforeCreate() {
console.log('beforeCreate');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
},
mounted() {
console.log('mounted', this);
},
});
// vm.$mount('#root')
</script>
</body>
只有前两个打印了出来:
未加el或$mount
加上vm.$mount('#root')
之后:
GIF
(6)解释⑥
template:''
注意看图片中的文字哦!
注意:
(1)这里template属性里面套用的标签不能是template标签,也就是说不能把div标签用template替换掉。
(2)vm.$el里面的DOM结构是真实的DOM结构,可以用这个来证明:
代码语言:javascript复制mounted:{
console.log('mounted',this.$el.instanceof HTMLElement)//true
}
3.更新流程
(1)证明⑦:beforeUpdate
data中的数据更新时调用。此时数据是新的,但是页面还没来得及更新,即数据和页面尚没有同步
代码语言:javascript复制<body>
<div id="root" :x="n"></div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
template: `
<div>
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
`,
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
beforeUpdate() {
console.log('beforeUpdate', this.n);
debugger;
},
});
</script>
</body>
点击按钮,n加一后,此时打印的n值为2,但是页面上显示的n值仍为1:
GIF
此时数据和页面尚未同步
解释:页面初始化的时候并没有运行beforeUpdate,点击按钮,n加1的时候(data中有数据改变的时候)才调用了beforeUpdate;而且此时打印的n值为2,但是此时页面上显示的是1(证明数据和页面尚未同步)
(2)证明⑧:updated
代码语言:javascript复制<body>
<div id="root" :x="n"></div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
template: `
<div>
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
</div>
`,
data: {
n: 1,
},
methods: {
addN() {
this.n ;
},
},
updated() {
console.log('updated', this.n);
debugger;
},
});
</script>
</body>
数据和页面保持同步:
数据和页面已经同步
4.销毁流程
先来看一下vm.$destroy
的官网解释(建立在一个组件化编码的思维方式上):
官网对vm.$destroy的解释
vm被销毁之后并没有解绑原生事件,只会解绑自定义事件:
@click=''是原生事件,所以并未销毁
页面演示如下⬇
销毁vm之后,click事件依然可以工作
(1)解释⑨:beforeDestroy
代码语言:javascript复制<body>
<div id="root" :x="n">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
<button @click="bye">点我销毁vm</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
console.log('addN');
},
bye() {
vm.$destroy();
},
},
beforeCreate() {
console.log('beforeCreate', '无法通过vm访问data和methods');
},
created() {
console.log('created', '可以通过vm访问data和methods');
},
beforeMount() {
console.log('beforeMount', 'DOM未经Vue编译');
},
mounted() {
console.log('mounted', 'DOM经过Vue编译');
},
beforeUpdate() {
console.log('beforeUpdate', '页面更新之前');
},
updated() {
console.log('updated', '页面更新完毕');
},
beforeDestroy() {
console.log('beforeDestroy', '马上执行销毁vm的状态');
this.addN()//测试能否调用addN方法
console.log(this.n)//测试能否拿到data中的数据
},
});
</script>
</body>
此时可以调用原生的方法addN(),也可以拿到n的值,但是此时对n的操作不再在页面上反映出来:
根据图中的1,2,3,4依次看哦
解释:页面上展示不出更新之后的数据(vm虽然销毁了,可是他的工作成果还在,页面上还能显示之前经过Vue编译的DOM结构,但此时Vue已经不会在帮你管理这个DOM了)其实此时已经调用了addN()方法,但是由于你处在的时间点很尴尬,你是已经在它进入销毁流程的时候里的销毁之前去调用,你在这里对数据进行的所有操作都不会再更新了。事实是只要进入了beforeDestroy你仍可以访问到数据和方法,但是修改数据无法再更新了,更新数据唯独放在beforeDestroy和destroy时,不会再触发更新了,一般在此时做一下关闭定时器、取消订阅消息、解绑自定义事件等首尾工作。
(2)解释⑩:destroy
此时已经解绑了事件监听器
代码语言:javascript复制<body>
<div id="root" :x="n">
<h1>n值是:{{n}}</h1>
<button @click="addN">点我n加一</button>
<button @click="bye">点我销毁vm</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
n: 1,
},
methods: {
addN() {
this.n ;
console.log('addN');
},
bye() {
vm.$destroy();
},
},
watch:{
n(){
console.log('n变了');
}
}
});
</script>
</body>
销毁vm之后,监视属性watch不再工作:
只剩下了原生的事件可以工作,watch被销毁了
5.总结
一共有8个生命周期钩子(4对)
类比张三的一生:
优化 '更改透明度' 的案例:
实现点击按钮使得透明度不再变换的功能:
方法一:这里有一个小技巧,如下
全局变量的设置技巧
方法二:通过销毁vm实现
代码语言:javascript复制<body>
<div id="root">
<h1 :style="{opacity:opacity}">学编程的GISer</h1>
<button @click="stop">停止变换透明度</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
opacity: 1,
},
methods: {
stop() {
// 销毁vm:
this.$destroy();
},
},
mounted() {
this.timer = setInterval(() => {
this.opacity -= 0.01;
if (this.opacity <= 0) {
this.opacity = 1;
}
console.log('setInterval');
}, 16);
console.log(this);
},
beforeDestroy() {
// 清除定时器:
clearInterval(this.timer);
},
});
</script>
</body>
大潘的解析:如果只销毁vm,不关定时器的话,即使销毁了vm,定时器也一直在走,耗费内存,故应该把定时器关掉。但是关定时器又有两种方法,一种是在方法stop()
里面关掉,另外一种是在beforeDestroy里面关,我们更倾向于第二种方法,因为在实际复杂的开发中,不一定清楚是在哪个时刻的哪个操作销毁了vm,但是不管在哪销毁vm,都会调用beforeDestroy这个生命周期钩子,所以我们在beforeDestroy里面关掉定时器更保险。
总结:
(1)常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
(2)关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
我会在这里分享更多有用的干货知识,点击下边的框框关注哦!