zoukankan      html  css  js  c++  java
  • 详解Vue2.0生命周期

    网上已经有很多关于vue生命周期的文章,我的这篇文章的由来,其实是我对官网上描述的一句话的思考与理解:“el被新创建的vm.$el替换”,所以文章更多的内容可能是在对vue生命周期中“created -> beforeMount -> mounted” 过程的理解。

    beforeCreate --> created 之间

    在这个阶段初始化事件,进行数据观测。

    created

    vue实例创建完后被调用,此时已经完成了数据观测(data observer),属性和方法的运算,watch/event 事件回调的配置。

    可调用methods中的方法,访问和修改data中的数据,并触发响应式变化使DOM渲染更新,触发watch中相应的方法,computed相关属性进行重新计算。

    一般在created时,进行ajax请求初始化实例数据。

    此时,vm.$el不可见。

    created --> beforeMount 之间

     在这个过程中,

      a、首先判断实例中是否有el选项,有的话继续向下编译,没有的话会停止编译,直到vm.$mount(el)被调用时才继续(el是挂载的DOM节点);

      b、接下来判断实例中是否有template,有的话将其作为模板编译成rander函数;

      c、没有template的话就,就将 挂载DOM元素的html(即el所对应的DOM元素及其内部的元素)提取出来作为模板编译;

    *注:el所对应的DOM元素不能为body/html元素,因为在后面vue实例挂载时,el所对应的DOM元素及其内部的元素会被模板渲染出来的新的DOM所替换。

      d、如果实例对象中有rander函数,就直接通过它进行渲染操作。

    优先级:rander函数 > template > 外部html

    此时,rander函数已经准备好并首次被调用。

    在这个过程中,$el被初始化为实例中el选项所对应的DOM元素,所以在beforeMount时,用vm.$el获取到的是挂载DOM元素的html。

    beforeMount

     beforeMount被调用时,此时$el可见。

    beforeMount --> mounted 之间

    在这个过程中,el被新创建的vm.$el所替换,并完成实例的挂载。即实例中的el选项被模板渲染创建出来的DOM元素所替换,页面中的挂载点被渲染出的vue实例代码段所替换。

    Mounted

    此时实例已挂载到DOM上,可以通过DOM API获取实例中DOM节点。在控制台打印vm.$el,发现之前的挂载点及内容已被替换成新的DOM。

    下面通过栗子看一下这两个过程。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    </head>
    <body>
    <div id="app">
        <a id='ela' href="">{{message}}</a>
    </div>
    </body>
    <script>
        var app = new Vue({
            el: '#app',
            data: function () {
                return {
                    message: 'hello'
                };
            },
            template: '<p id="elp">{{message}}</p>',
            beforeMount: function () {
                console.group('beforeMount 挂载前状态===============》');
                let state = {
                    'el': this.$el,
                    'data': this.$data,
                    'message': this.message
                }
                let a = document.getElementById('ela');
                let p = document.getElementById('elp');
                console.log(this.$el);
                console.log(state);
                console.log(a);    // <a id='ela' href="">{{message}}</a>
                console.log(p);     // null
            },
            mounted: function () {
                console.group('mounted 挂载结束状态===============》');
                let state = {
                    'el': this.$el,
                    'data': this.$data,
                    'message': this.message
                }
                let a = document.getElementById('ela');
                let p = document.getElementById('elp');
                console.log(this.$el);
                console.log(state);
                console.log(a);    // null
                console.log(p);     // <p id="elp">father</p>
            }
    </script>
    </html>

    用template/rander选项可以在控制台清楚的看到:挂载完成后,el被新创建的vm.$el替换。

    挂载前是以初始的el和虚拟DOM的形式存在的,此时以template中的内容为模板,模板内容不可见,打印p标签为null;

    挂载后,模板渲染的新DOM替换原来的el,原el所对应的DOM不存在,打印a标签为null。

     beforeUpdate 和 updated

    当data对象中数据更新时,会触发 beforeUpdate钩子,此时view层还没有更新。 beforeUpdate有下面几个需要注意的地方:

      a、更新的数据必须在模板中直接或间接的使用,才会触发beforeUpdate;

      b、在挂载之前,data中数据更新不会触发 beforeUpdate!即在created、beforeMount时改变数据不会触发更新流程;

      c、如果在beforeUpdate中,再次修改了该数据的值,会再次触发beforeUpdate钩子,进行两次更新流程。

    updated时,view层已更新完毕。

    (在上面的代码中增加两个钩子)

    mounted: function () {
        this.message = 'first';
    //     this.show = false;        // 由于模板中没有用到show,所以show的改变不会触发beforeUpdate
    },
    beforeUpdate: function () {
        console.group('beforeUpdate 更新前状态===============》');
        let elp = document.getElementById('elp').innerHTML;
        console.log('message:' + this.message);
        console.log('DOM:' + elp);
    },
    updated: function () {
        console.group('updated 更新完成状态===============》');
        let elp = document.getElementById('elp').innerHTML;
        console.log('message:' + this.message);
        console.log('DOM:' + elp);
    }

    这里需要注意一点:view层我们需要通过innerHTML获取对应元素节点中的内容,而不能直接获取元素节点。直接获取元素节点,在控制台打印出来的view层中的数据都是更新之后的状态,不能打印出实时的正确的值,这应该和Chrome控制台的输出有关。

    针对第三条,我们看一下下面的代码演示:

    mounted: function () {
        this.message = 'first';
    },
    beforeUpdate: function () {
        console.group('beforeUpdate 更新前状态===============》');
        let elp = document.getElementById('elp').innerHTML;
        console.log('message:' + this.message);
        console.log('DOM:' + elp);
        this.message = 'second';    // 此时在beforeUpdate中再次修改了message的值
    },
    updated: function () {
        console.group('updated 更新完成状态===============》');
        let elp = document.getElementById('elp').innerHTML;
        console.log('message:' + this.message);
        console.log('DOM:' + elp);
    }

     

    这里我们可以清楚的看到进行了两次更新流程,但是对打印的结果有些疑问:第一次将message的值改为first,并且以first来渲染更新DOM,那么第一次调用updated时,message和DOM中的值都应该是first,而此时打印出来的时second。我理解的是,在第一次执行updated时,DOM就已经完成了第二次渲染更新,具体的过程还需要通过之后对源码的学习去理解。这里各位有不同的理解或者更详细的解释,可以在评论区留言,共同学习。

    在这里,我们可以在beforeUpdate中加定时器去修改message的值,就可以等待第一次数据改变,DOM更新渲染完成后,进行第二次数据改变。

    beforeUpdate: function () {
        console.group('beforeUpdate 更新前状态===============》');
        let elp = document.getElementById('elp').innerHTML;
        console.log('message:' + this.message);
        console.log('DOM:' + elp);
        var that = this;
        setTimeout(function(){
          that.message = 'second';
        });
    //    this.message = 'second';    // 此时在beforeUpdate中再次修改了message的值
    },

    这里可以清楚看到两次数据改变时,数据和view层的更新状态。

    beforeDestroy 和 destroyed

    beforeDestroy:实例在销毁之前调用,此时实例仍然可用。

    beforeDestroy -> destroyed: Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

    destroyed:vue实例销毁后调用。

    结尾:关于vue生命周期就总结完毕,有错误的地方烦请指出,会及时修改!

  • 相关阅读:
    14使用函数时要特别考虑的问题
    13子程序的参数
    12子程序的名字和长度
    (解决tomcat端口被占用的问题)create[8005]java.net.BindException: Address already in use: JVM_Bind
    纠错记录(Could not open the editor: Android XML Editor cannot process this input.)
    查看端口被占用的进程号然后结束进程(解决端口被进程占用的问题)
    使用java对执行命令行 或 执行bat文件
    ant_0105
    ant 具体命令行展示代码
    ant 关键字和关键语句
  • 原文地址:https://www.cnblogs.com/shiningly/p/9495035.html
Copyright © 2011-2022 走看看