Vue专题 05_详解vue生命周期的每个节点

2022-09-26 11:36:12 浏览数 (1)

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(){}:

代码语言: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,
      },
      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')

代码语言: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');
      },
      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操作数据,因为即便操作数据,也不会再触发更新流程了。

我会在这里分享更多有用的干货知识,点击下边的框框关注哦!

0 人点赞