Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vue] 请描述下vue的生命周期是什么? #229

Open
undefinedYu opened this issue Jun 11, 2019 · 11 comments
Open

[vue] 请描述下vue的生命周期是什么? #229

undefinedYu opened this issue Jun 11, 2019 · 11 comments
Labels
vue vue

Comments

@undefinedYu
Copy link
Contributor

undefinedYu commented Jun 11, 2019

[vue] 请描述下vue的生命周期是什么?

@undefinedYu undefinedYu added the vue vue label Jun 11, 2019
@haizhilin2013 haizhilin2013 changed the title [vue] 请描述下vue的生命周期 [vue] 请描述下vue的生命周期是什么? Jun 18, 2019
@xjt31012
Copy link

生命周期主要是vue加载页面时的一个过程,分为“加载前,加载中,加载后,更新,摧毁”等等,用于我们根据不同的状态处理不同的业务,比如加载前的等待图片等等

@Zjingbo
Copy link

Zjingbo commented Jul 2, 2019

emmm....原理呢?

@MachineDream
Copy link

MachineDream commented Jul 9, 2019

生命周期就是vue从开始创建到销毁的过程,分为四大步(创建,挂载,更新,销毁),每一步又分为两小步,如beforeCreate,created。beforeCreate前,也就是new Vue的时候会初始化事件和生命周期;beforeCreate和created之间会挂载Data,绑定事件;接下来会根据el挂载页面元素,如果没有设置el则生命周期结束,直到手动挂载;el挂载结束后,根据templete/outerHTML(el)渲染页面;在beforeMount前虚拟DOM已经创建完成;之后在mounted前,将vm.$el替换掉页面元素el;mounted将虚拟dom挂载到真实页面(此时页面已经全部渲染完成);之后发生数据变化时触发beforeUpdate和updated进行一些操作;最后主动调用销毁函数或者组件自动销毁时beforeDestroy,手动撤销监听事件,计时器等;destroyed时仅存在Dom节点,其他所有东西已自动销毁。这就是我所理解的vue的一个完整的生命周期;

@Ricemonster
Copy link

emmm....原理呢?

zzzzz

@Ricemonster
Copy link

vue 的生命周期是: vue 实例从创建到销毁,也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程。

在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

转载自 https://www.kancloud.cn/hanxuming/vue-iq/733839

@waterkitten
Copy link

image

@CoderNanT
Copy link

简单来说,就是从创建到销毁的过程叫做生命周期

@crush2020
Copy link

就是一个vue实例从创建,挂载,更新,销毁的全部过程,称为生命周期。

@hyj443
Copy link

hyj443 commented Oct 21, 2021

vue的实例经历:初始化创建,挂载,更新,销毁这几个全过程,在这些过程中,我们在一些时间点让开发者可以做一些事。

下面将生命周期函数执行的时机在源码中分析一下

  function callHook(vm, hook) { // 当前vm实例 和 生命周期函数名称
    pushTarget(); 
    var handlers = vm.$options[hook]; // 获取钩子函数数组,注意 已经是数组了
    var info = hook + " hook";
    if (handlers) {
      handlers.forEach(handler => { // 遍历钩子数组,执行每一个钩子
        invokeWithErrorHandling(handler, vm, null, vm, info);
      })
    }
    //当前组件注册有hookEvent,调用emit派发'hook:' + hook这个事件
    if (vm._hasHookEvent) vm.$emit('hook:' + hook)
    popTarget();
  }

这是执行钩子函数的函数,获取到配置的各个钩子函数数组(可能不止一个),遍历执行每一个钩子

那callHook在什么时候调用呢?

我们挑几个看看,比如beforeCreate和created

function initMixin (Vue) { // initMixin函数执行,会给Vue的原型添加_init方法
    Vue.prototype._init = function (options) {
        var vm = this;
        vm._uid = uid$3++;
        vm._isVue = true; // _isVue是Vue实例的标识,区别于普通的对象,避免被观测 
        if (options && options._isComponent) { //_isComponent表明是内部子组件
        initInternalComponent(vm, options) // 主要为vm.$options添加一些属性
        } else { //当前Vue实例不是组件,调用mergeOptions方法,将vm的构造器的options(合并了父级构造器的options),和当前实例化时接收的options合并。传入的当前vm实例。
        vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options || {}, vm)
        }
        initProxy(vm) //指定渲染函数的执行上下文为vm的proxy代理,通过拦截函数提供友好的开发提示
        vm._self = vm; // vm._self指向当前vm实例自己
        initLifecycle(vm); // 初始化生命周期相关的属性
        initEvents(vm); // 初始化事件相关的属性
        initRender(vm); // 渲染的初始化
        callHook(vm, 'beforeCreate') // 调用beforeCreate的钩子函数
        initInjections(vm); // 在data/props前初始化inject
        initState(vm); // 初始化data、props、computed、watcher等等
        initProvide(vm); // 在data/props后初始化 provide
        callHook(vm, 'created');
        // 执行完所有初始化后,开始挂载实例到DOM上,实例化组件构造器时,如果传了el,调用$mount开启模版编译和挂载,如果没有传,则需要用户手动调用vm.$mount,否则不会进入下一生命周期流程
        if (vm.$options.el) vm.$mount(vm.$options.el);
    };
}

其中 initState 包括了:initProps、initMethods、initData、initComputed 以及 initWatch。所以当 beforeCreate 钩子被调用时,所有与 props、methods、data、computed 以及 watch 相关的内容都不能使用。

等这些都init完了,才执行created 生命周期钩子,所以在 created 钩子中,是能够使用以上内容的。但由于此时还没有任何挂载的操作,所以在 created 中是不能访问DOM的,即不能访问 $el。

再看看beforeMount 和 mounted

function mountComponent (vm, el, hydrating) { // vm是组件实例,el是DOM挂载点
    vm.$el = el 
    if (!vm.$options.render) { 
      vm.$options.render = createEmptyVNode
      if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) {
        warn('你在使用运行时版本的Vue,无法编译模板,请把模版预编译成渲染函数,或使用有编译器的版本', vm)
      } else {
        warn('挂载组件失败:模版和render函数未定义', vm)
      }
    }
    callHook(vm, 'beforeMount') // 挂载前调用beforeMount钩子
    const updateComponent = () => { 
      vm._update(vm._render(), hydrating)
    }
    new Watcher(vm, updateComponent, noop, { // 创建渲染函数的watcher,cb回调传noop
      before() {
        if (vm._isMounted && !vm._isDestroyed) callHook(vm, 'beforeUpdate')
      }
    }, true /*只有渲染函数的watcher才传真*/)
    hydrating = false
   
    if (vm.$vnode == null) { // 如果vm.$vnode为空,说明当前实例没有父级vnode,是根实例
      vm._isMounted = true // 添加_isMounted属性 值为true,标识根实例挂载完成
      callHook(vm, 'mounted') // 调用mounted钩子
    }
    return vm
  }

mountComponent函数是 $mount的核心,做的事是将组件实例挂载到DOM元素上,即把模板渲染到DOM节点中,并且以后当数据变化时,会重新渲染指定的DOM元素。

我们看到 beforeMount 在挂载前执行,然后就定义了updateComponent函数,它是要扔进去new Watcher的,即它是要被观测的函数,做的是渲染和挂载的操作。new Watcher会把它执行一下。

而且我们看到,如果当前实例是根实例,即不是组件的挂载,而是new Vue是根组件的挂载,才执行mounted钩子函数,那么我们写的组件的mounted在哪里执行呢?

其实,组件的 VNode patch 到 DOM 后,会执行 invokeInsertHook 函数,把 insertedVnodeQueue 里保存的钩子函数依次执行一遍:

function invokeInsertHook (vnode, queue, initial) {
    // delay insert hooks for component root nodes, invoke them after the
    // element is really inserted
    if (isTrue(initial) && isDef(vnode.parent)) {
    vnode.parent.data.pendingInsert = queue;
    } else {
    for (var i = 0; i < queue.length; ++i) {
        queue[i].data.hook.insert(queue[i]);
    }
    }
}

会把queue中的insert函数都执行

insert (vnode: MountedComponentVNode) {
    const { context, componentInstance } = vnode
    if (!componentInstance._isMounted) {
      componentInstance._isMounted = true
      callHook(componentInstance, 'mounted')
    }
    // ...
  },

可见,每个子组件都是在这个insert钩子函数中执行 mounted 钩子,而且 insertedVnodeQueue 的添加顺序是先子后父,所以对于同步渲染的子组件而言,mounted 钩子函数的执行顺序也是先子后父。

@yxllovewq
Copy link

生命周期:vue实例从创建、挂载、更新到销毁的过程。
生命周期钩子:在vue的生命周期执行过程中,每执行到规定位置,会调用不同的勾子。
vue实例化,初始化事件和生命周期函数->触发beforeCreated->观察和设置data属性、设置methods属性->触发created->vue实例化传入参数具有el属性,有template属性,将template属性编译为虚拟DOM,没有template,向外找template,找到编译为虚拟DOM。没有el属性,则生命周期停止,直到手动挂载el->触发beforeMount->替换vm.$el,将vm.$el替换掉真实DOM->触发mounted。
修改数据->触发beforeUpdate->数据更新到真实DOM->触发updated。
$vm.destroy()->未处理->触发beforeDestroy->停止观察数据,卸载有关内容->触发destroyed

@Cai-zhiji
Copy link

定义

Vue的生命周期是指Vue实例在创建、更新和销毁过程中所经历的一系列阶段和方法。通过这些生命周期钩子函数,开发者可以在不同的阶段执行相应的代码逻辑。

生命周期

创建阶段(Creation):

beforeCreate:在实例被创建之前调用,此时数据观测(data observer)和事件/钩子函数初始化尚未开始。
created:在实例被创建后调用,此时实例已完成数据观测、属性和方法的运算,但尚未挂载到DOM上。

挂载阶段(Mounting):

beforeMount:在实例挂载到DOM之前调用,此时模板已编译完成,但尚未进行渲染。
mounted:在实例挂载到DOM后调用,此时实例已经完成渲染,可以访问到挂载后的DOM元素。

更新阶段(Updating):

beforeUpdate:在数据更新之前调用,即响应式数据发生变化,但DOM尚未重新渲染。
updated:在数据更新之后调用,此时DOM已经重新渲染完成。

销毁阶段(Unmounting):

beforeUnmount(Vue 3中为beforeUnmount,Vue 2中为beforeDestroy):在实例销毁之前调用,此时实例仍然完全可用。
unmounted(Vue 3中为unmounted,Vue 2中为destroyed):在实例销毁之后调用,此时实例及其相关的DOM已经被完全清理。

辅助生命周期的钩子函数

errorCaptured:用于捕获和处理子孙组件抛出的错误。
renderTracked(Vue 3)/ renderTriggered(Vue 2):用于跟踪渲染过程中依赖的追踪和触发事件。

开发者可以通过在Vue组件中定义这些生命周期钩子函数来执行特定的操作,例如初始化数据、发送网络请求、订阅事件、操作DOM等。这样,可以更好地控制组件的行为,并在需要的时候执行相应的逻辑。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
vue vue
Projects
None yet
Development

No branches or pull requests