vue生命周期、异步跟新dom

作者: 小红依 | 来源:发表于2018-11-09 17:29 被阅读2次

最近工作中遇到个bug,半天找不到为什么,场景大概是这样的

<template>
    <div id="app">
        <p>{{data.user_info.nick}}</p>
    </div>
<template>
<script>
export default {
    name: 'app',
    data() {
        return {
            data:{},
        }
    },
    created() {
        this.init();
    },
    methods:{
        init() {
            ajax({
                url:'hhh',
                type:'get',
                success() {
                    this.data = res;
                }
            })
            
        }
    }
}
</script>

运行时报错

TypeError: Cannot read property ‘nick’ of undefined

[图片上传失败...(image-2d58c6-1541755736318)]

为了快速解决问题,我做了这样的修改

data() {
    return {
        data:{
            user_info:{}
        },
    }
},

对象里面有对象,那我就先定义成想要的结构

或者,下面这种解决方法也是可行的

<div id="app">
    <p>{{data.user_info.nick}}</p>
</div>

后来,在项目做完了以后,我尝试着找到了原因

需要关注两点,一是生命周期,二是vue的异步dom更新

下面,我们来回顾一下vue的生命周期


image

在我上面的代码中,是把init放到了created阶段去执行,我们注意created阶段的描述,组件实例创建完成,属性已绑定,但是dom还未生成,$el属性还不存在。所以如果执行下面代码,将不会报错

export default {
    name: 'app',
    data() {
        return {
            data:{},
        }
    },
    created() {
        this.init();
    },
    methods:{
        init() {
            this.data = {
                user_info:{
                    nick:'ggg'
                }
            }
            //ajax({
            //    url:'hhh',
            //    type:'get',
            //    success() {
            //        this.data = res;
            //    }
            //})
            
        }
    }
}

因为在init中的代码是同步执行的,所以在created阶段我们更改了data但是dom并没有加载,所以不会报错。

但是,因为我把data的改变放在了一个异步请求中,所以data没有立即被改变,在它得到数据的时候已经错过了上一班车了。(错过了上一班车的概念等会会解释);

然后我们在来看一下如果把init函数放在mounted中呢。

mounted阶段,模版编译挂载之后

这个时候dom已经生成,而生成的dom读取的是原始的data数据,所以不管是不是放到请求中去改变data都会报错

export default {
    name: 'app',
    data() {
        return {
            data:{},
        }
    },
    mounted() {
        this.init();
    },
    methods:{
        init() {
            this.data = {
                user_info:{
                    nick:'ggg'//TypeError: Cannot read property ‘nick’ of undefined
                }
            }
        }
    }
}
export default {
    name: 'app',
    data() {
        return {
            data:{},
        }
    },
    mounted() {
        this.init();
    },
    methods:{
        init() {
            ajax({
                url:'hhh',
                type:'get',
                success() {
                    this.data = res;//TypeError: Cannot read property ‘nick’ of undefined
                }
            })
            
        }
    }
}

所以其实其实我把init写在created中是不好的,因为vue的生命周期的执行和请求数据是两个同时在执行的线程,因为网速的快慢,对于不同用户,他们可能会在不同的生命周期得到数据。而在created阶段因为dom还未生成,所以就加大了不确定性。所以最好把请求数据写到mounted中。

然后在来说说刚才的错过班车的问题

这里我们要来看看vue的内部实现机制


image

在new Vue()之后,Vue()会调用_init函数(不是自己写的那个init函数)进行初始化,它会初始化生命周期(beforeCreated阶段)、事件、props、methods、data、computed与watch等。其中最重要的是通过Object.defineProperty设置setter和getter函数,用来监控data数据的改变,实现所谓的数据双向绑定。

编译

complie编译可以分成parse、optimize与generate三个阶段

parse

parse负责解析template模版中的指令、class、style等数据,形成AST(抽象语法树)

optimize

optimize的主要作用是标记static静态节点,这是vue在编译过程中的一处优化,后面update更新界面时,会有一个patch的过程,diff算法会直接跳过静态节点,从而减少了比较的过程,优化了patch的性能。

generate

generate是将AST转化成render function字符串的过程,得到结果是render的字符串以及staticRenderFns字符串。

在经历过 parse、optimize 与 generate 这三个阶段以后,组件中就会存在渲染 VNode 所需的 render function 了。

响应式

当render function被渲染的时候,因为会读取所需对象的值,所以会触发getter函数进行依赖收集,依赖收集的目的是将观察者watcher对象存放到当前闭包中的订阅者Dep的subs中。在修改对象的值的时候,会触发对应的 setter, setter 通知之前「依赖收集」得到的 Dep 中的每一个 Watcher,告诉它们自己的值改变了,需要重新渲染视图。

需要注意的是,vue并不会在以后对象的修改的时候就更新视图,它会将这些更新收集起来,一段时间去更新一次视图。

因为我们其实并不很清楚的知道会更新视图的具体时间,所以如果有些操作一定要在dom更新之后执行,可以使用$nextTick函数。

$nextTick

它接收一个回调函数,回调函数会在这次更新完后执行。

相关文章

  • vue生命周期、异步跟新dom

    最近工作中遇到个bug,半天找不到为什么,场景大概是这样的 运行时报错 [图片上传失败...(image-2d58...

  • Vue.js中的this.$nextTick()

    Vue中的nextTick涉及到Vue中DOM的异步更新 this.$nextTick()将回调延迟到下次 DOM...

  • vue使用this.$nextTick()函数

    Vue.nextTick Vue 中的 nextTick 涉及到 Vue 中 DOM 的异步更新 Vue 实现响应...

  • Vue中的nextTick方法

    简单的说就是数据更新后,DOM随着数据更新改变之后执行的回调函数代码实例: 可以看出为异步函数 在Vue生命周期的...

  • Vue的生命周期

    Vue的生命周期 Vue的生命周期就是Vue从创建到销毁的过程,从开始创建、初始化数据、编译模板、挂载DOM ->...

  • this.$nextTick()

    官方文档是这样解释的:Vue 在更新 DOM 时是异步执行的,为了在数据变化之后等待 Vue 完成更新 DOM,可...

  • vue原理与开发逻辑

    1、vue中的$nextTick()的用法和原理 vue的DOM更新是异步的,当数据更新了,再dom中渲染后,自动...

  • 0725-

    1.virtual DOM 和 Diff算法? 2.vue生命周期 3.vue-router原理 4.vue通信 ...

  • VUE

    vue生命周期 vue数据双向绑定 vue虚拟dom computed和watch运行机制1 computed和w...

  • 生命周期

    生命周期方法: beforeCreatecreatedbeforeMount//Vue完成模板解析并把真实的DOM...

网友评论

    本文标题:vue生命周期、异步跟新dom

    本文链接:https://www.haomeiwen.com/subject/ijioxqtx.html