深入vue2.0源码系列:生命周期的实现

前言

在Vue.js 2.0中,组件的生命周期函数分为8个阶段,分别是:

  1. beforeCreate:组件实例刚被创建,但是数据和事件监听还未被初始化。
  2. created:组件实例已经被创建,并且数据和事件监听已经初始化完成。
  3. beforeMount:组件将要被挂载到页面上,但是还没有开始渲染。
  4. mounted:组件已经被挂载到页面上,并且已经渲染完毕。
  5. beforeUpdate:组件将要被更新,但是还没有开始重新渲染。
  6. updated:组件已经被更新,并且重新渲染完毕。
  7. beforeDestroy:组件将要被销毁,但是还没有开始销毁。
  8. destroyed:组件已经被销毁。

实现过程

在Vue.js 2.0中,生命周期函数的实现主要是通过一个叫做“lifecycle.js”的模块来完成的。下面是该模块的完整代码,包括注释:


/* @flow */import config from '../config'
import { callHook, activateChildComponent } from '../instance/lifecycle'
import { createEmptyVNode } from '../vdom/vnode'
import { updateComponentListeners } from '../vdom/helpers/index'export function lifecycleMixin (Vue: Class<Component>) {Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = thisconst prevEl = vm.$elconst prevVnode = vm._vnodeconst prevActiveInstance = config.activeInstanceconfig.activeInstance = vmvm._vnode = vnode// Vue.js 的更新流程if (!prevVnode) {// 如果没有旧的 VNode,说明这是组件的首次渲染vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)} else {// 如果有旧的 VNode,说明这是组件的更新过程vm.$el = vm.__patch__(prevVnode, vnode)}config.activeInstance = prevActiveInstance// update __vue__ referenceif (prevEl) {prevEl.__vue__ = null}if (vm.$el) {vm.$el.__vue__ = vm// 如果组件已经被挂载,并且有子组件,则需要激活子组件的钩子函数if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {activateChildComponent(vm.$vnode, vm.$options._renderChildren, vm)}// 触发组件的 updated 钩子函数callHook(vm, 'updated')}}Vue.prototype.$forceUpdate = function () {const vm: Component = thisif (vm._watcher) {vm._watcher.update()}}Vue.prototype.$destroy = function () {const vm: Component = thisif (vm._isBeingDestroyed) {return}// 触发组件的 beforeDestroy 钩子函数callHook(vm, 'beforeDestroy')vm._isBeingDestroyed = true// 如果组件有父组件,则需要将其从父组件的 $children 中删除if (vm.$parent) {const index = vm.$parent.$children.indexOf(vm)if (index > -1) {vm.$parent.$children.splice(index, 1)}}// 销毁组件上的所有监听器vm._watcher && vm._watcher.teardown()let i = vm._watchers.lengthwhile (i--) {vm._watchers[i].teardown()}// 删除组件上所有的属性和方法if (vm._data.__ob__) {vm._data.__ob__.vmCount--}vm._isDestroyed = true// 触发组件的 destroyed 钩子函数callHook(vm, 'destroyed')Vue.prototype.$nextTick = function (fn: Function) {return nextTick(fn, this)}Vue.prototype._render = function (): VNode {const vm: Component = thisconst { render, _parentVnode } = vm.$optionsif (_parentVnode) {vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject}// 设置父节点,用于在子组件中使用 $parentvm.$vnode = _parentVnode// 渲染 VNodelet vnodetry {// 调用渲染函数,得到组件的 VNodevnode = render.call(vm._renderProxy, vm.$createElement)} catch (e) {handleError(e, vm, `render`)vnode = vm._vnode || createEmptyVNode()}// 如果返回的是一个数组,则只取第一个元素作为 VNodeif (Array.isArray(vnode) && vnode.length === 1) {vnode = vnode[0]}// 判断是否为合法的 VNode,如果不是,则创建一个空的 VNodeif (!(vnode instanceof VNode)) {vnode = createEmptyVNode()}// 设置组件的父子关系vnode.parent = _parentVnodereturn vnode}
}
// Vue 实例的生命周期钩子函数
export function initLifecycle (vm: Component) {const options = vm.$options// locate first non-abstract parentlet parent = options.parentif (parent && !options.abstract) {while (parent.$options.abstract && parent.$parent) {parent = parent.$parent}parent.$children.push(vm)}// 设置当前实例的 $parent、$root、$children 属性vm.$parent = parentvm.$root = parent ? parent.$root : vmvm.$children = []vm.$refs = {}vm._watcher = nullvm._inactive = nullvm._directInactive = falsevm._isMounted = falsevm._isDestroyed = falsevm._isBeingDestroyed = false
}export function lifecycleMixin (Vue: Class<Component>) {Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = this// 如果已经渲染过了,则会调用 beforeUpdate 钩子函数if (vm._isMounted) {callHook(vm, 'beforeUpdate')}// 保存旧的 VNodeconst prevEl = vm.$elconst prevVnode = vm._vnodeconst prevActiveInstance = activeInstanceactiveInstance = vmvm._vnode = vnode// 如果之前没有 VNode,则直接渲染if (!prevVnode) {vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)} else {// 如果有旧的 VNode,则对比新旧 VNode,然后更新vm.$el = vm.__patch__(prevVnode, vnode)}activeInstance = prevActiveInstance// 更新完毕之后,会调用 updated 钩子函数if (prevEl) {vm.$forceUpdate()callHook(vm, 'updated')} else if (vm.$vnode == null) {callHook(vm, 'mounted')vm._isMounted = true}return vm}Vue.prototype.$forceUpdate = function () {const vm: Component = thisif (vm._watcher) {vm._watcher.update()}}Vue.prototype.$destroy = function () {const vm: Component = this// 如果正在被销毁,则直接返回if (vm._isBeingDestroyed) {return}// 触发 beforeDestroy 钩子函数callHook(vm, 'beforeDestroy')// 标记当前实例正在被销毁vm._isBeingDestroyed = true// 从父组件的 $children 数组中移除当前实例const parent = vm.$parentif (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {remove(parent.$children, vm)}// 卸载所有的 watcherif (vm._watcher) {vm._watcher.teardown()}let i = vm._watchers.lengthwhile (i--) {vm._watchers[i].teardown()}// remove reference from data// remove reference from data ob// frozen object may not have observer.if (vm._data.__ob__) {vm._data.__ob__.vmCount--}// 使当前实例处于销毁状态vm._isDestroyed = true// 触发 destroyed 钩子函数callHook(vm, 'destroyed')// 释放所有占用的资源vm.$off()// 卸载所有子组件if (vm.$options._parentElm && vm.$options._refElm) {vm.$options._parentElm.removeChild(vm.$options._refElm)}// 解除父子关系vm.$parent = nullvm.$root = null// 移除所有监听器vm.$listeners = {}// 移除所有实例属性vm._watchers = []vm._data = {}vm._props = {}vm._methods = {}vm._computedWatchers = {}// 标记当前实例已被销毁vm._renderProxy = nullvm._staticTrees = null// 禁用当前实例vm._disable = true}
}// 触发钩子函数
export function callHook (vm: Component, hook: string) {const handlers = vm.$options[hook]if (handlers) {for (let i = 0, j = handlers.length; i < j; i++) {try {handlers[i].call(vm)} catch (e) {handleError(e, vm, `${hook} hook`)}}}if (vm._hasHookEvent) {vm.$emit('hook:' + hook)}
}

在上面的代码中,我们可以看到 initLifecycle 函数会设置当前实例的 $parent$root$children$refs 属性。它还会查找当前实例的第一个非抽象父组件,并将当前实例添加到父组件的 $children 数组中。lifecycleMixin 函数实现了 Vue 实例的更新、强制更新和销毁。它还定义了 callHook 函数来触发生命周期钩子函数。

总结

Vue.js 的生命周期钩子函数是在 Vue 实例的不同阶段执行的回调函数。通过使用这些钩子函数,我们可以在 Vue 实例的不同生命周期阶段执行自定义代码。

Vue 2.x 的生命周期钩子函数包括 beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed。这些钩子函数按照执行顺序分为三个阶段:创建阶段、挂载阶段和销毁阶段。

Vue 3.x 中的生命周期钩子函数与 Vue 2.x 中的类似,但是 beforeCreatecreated 钩子函数的实现有所不同。Vue 3.x 使用 setup 函数来代替 beforeCreatecreated 钩子函数。

在 Vue.js 源码中,生命周期钩子函数的实现是在 lifecycleMixin 函数中完成的。lifecycleMixin 函数实现了 Vue 实例的更新、强制更新和销毁,并定义了 callHook 函数来触发生命周期钩子函数。

通过深入理解 Vue.js 的生命周期钩子函数实现,我们可以更好地理解 Vue.js 的运行机制,并且可以在自己的 Vue.js 项目中更加灵活地使用生命周期钩子函数来实现自定义功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/125013.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 13.0 rom定制专栏系列解读

一.前言 在从事android系统rom定制化的这几年里&#xff0c;经历了坎坎坷坷&#xff0c;开发过好几种类型的产品&#xff0c;也随着google对android系统的更新加快&#xff0c;也需要跟随上时代的进步&#xff0c;所以需要把平时工作中遇到的问题总结出来&#xff0c;及时做好…

leetCode 136.只出现一次的数字 + 位运算

136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算…

保护生产中 Node.js 应用程序安全的 15 项最佳实践

在后端开发方面&#xff0c;Node.js 是开发人员最喜欢的技术之一。它的受欢迎程度不断上升&#xff0c;现已成为在线攻击的主要目标之一。这就是为什么保护 Node.js 免受漏洞和威胁至关重要。 在本指南中&#xff0c;您将看到为生产设计安全 Node.js 应用程序架构的 15 种最佳…

Three.js 开发引擎的特点

Three.js 是一个流行的开源 3D 游戏和图形引擎&#xff0c;用于在 Web 浏览器中创建高质量的三维图形和互动内容。以下是 Three.js 的主要特点和适用场合&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作…

如何处理不稳定的自动化测试?

abluecolor 在解决这个问题之前&#xff0c;请停止编写更多测试&#xff0c;因为这将花费你较高的测试维护成本。你需要尽快行动起来对不稳定的原因进行深入研究&#xff0c;找到不稳定的根因&#xff0c;并且尝试在流程、环境和代码方面做一些优化工作解决它。 MasterKindew…

数字频带传输——二进制数字调制及MATLAB仿真

文章目录 前言一、OOK1、表达式2、功率谱密度3、调制框图 二、2PSK1、表达式2、功率谱密度 三、2FSK1、表达式 四、MATLAB 仿真1、MATLAB 源码2、仿真及结果①、输入信号及频谱图②、2ASK 调制③、2PSK 调制④、2FSK 调制⑤、随机相位 2FSK 调制 五、资源自取 前言 数字频带信…

一天掌握python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium

大家好&#xff0c;我是python222小锋老师。前段时间卷了一套 Python3零基础7天入门实战 以及1小时掌握Python操作Mysql数据库之pymysql模块技术 近日锋哥又卷了一波课程&#xff0c;python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium&#xff0c;文字版视频版。1…

vscode C++项目相对路径的问题

如图所示的项目目录结构 如果要在main.cpp里用相对路径保存一个txt文件 std::ofstream file("./tree_model/my_file.txt");if (file.is_open()) {file << "This is a sample text.\n";file.close();std::cout << "File saved in the mode…

什么是 CNN? 卷积神经网络? 怎么用 CNN 进行分类?(2)

参考视频&#xff1a;https://www.youtube.com/watch?vE5Z7FQp7AQQ&listPLuhqtP7jdD8CD6rOWy20INGM44kULvrHu 视频4&#xff1a;CNN 中 stride 的概念 如上图&#xff0c;stride 其实就是 ”步伐“ 的概念。 默认情况下&#xff0c;滑动窗口一次移动一步。而当 stride …

【vue】vue前端、生产(线上)环境请求unicloud云服务空间axios报错

目录 原因总结&#xff1a;借助Nginx使得axios可跨域请求 原因 使用axios的时候&#xff0c;如果是开发环境下&#xff0c;WebStorm&#xff08;IDEA&#xff09;会自带跨域功能&#xff0c;说白了就是不用考虑跨域的事情了。但是在生产环境下&#xff0c;vue前端编译成静态文…

香港第一金:重磅事件数据利率决议之前 黄金作出调整修正

香港第一金基本面分析&#xff1a; 中国纸黄金交易通显示&#xff0c;全球最大黄金上市交易基金(ETF)截至10月31日持仓量为859.49吨&#xff0c;较上日减持2.31吨&#xff0c;本月止净减持14.15吨。 自10月7日哈马斯袭击以色列以来&#xff0c;贵金属市场成为避险资产需求上升…

C/C++中指针的加一能力

一.再日常使用C语言的时候&#xff0c;我们会用到数组&#xff0c;指针。这里就会牵扯到指针以及指针的解引用和加一能力的问题。 1.普通指针的加一能力 int a0;int *p&a;cout<<*(p1); 在指针p加一后&#xff0c;因为变量a是int型&#xff0c;所以在加一之后就到a变量…

开发语言工具编程系统化教程入门和初级专辑课程上线

开发语言工具编程系统化教程入门和初级专辑课程上线 学习编程捷径&#xff1a;&#xff08;不论是正在学习编程的大学生&#xff0c;还是IT人士或者是编程爱好者&#xff0c;在学习编程的过程中用正确的学习方法 可以达到事半功倍的效果。对于初学者&#xff0c;可以通过下面…

图神经网络论文笔记(一)——北邮:基于学习解纠缠因果子结构的图神经网络去偏

作者 &#xff1a;范少华 研究方向 &#xff1a;图神经网络 论文标题 &#xff1a;基于学习解纠缠因果子结构的图神经网络去偏 论文链接 &#xff1a;https://arxiv.org/pdf/2209.14107.pdf        https://doi.org/10.48550/arXiv.2209.14107 大多数图神经网络(GNNs)通…

VS+QT添入外部含QObject子类的文件,出现“无法解析的外部符号”的报错

背景&#xff1a;我在别处有downloading.h,downloading.cpp,downloading.ui 现在想添加到VS2019与Qt联合开发的项目中。 项目右键--->添加--->现有项 然后出现了错误&#xff0c;无法解析的外部符号。 原因是&#xff1a;这个downloading.h文件中的类是带有Q_OBJECT的…

【python与数据结构】(leetcode算法预备知识)

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ python与数据结构 Python 中常见的数据类型数据结构1.数组&#xff08;Array&#xff09;2.链表&#xff08;Linked List&#xff09;3.哈希表&#xff08;Hash Table&#xff09;4.队列&#xff08;Queue&#x…

cookie

什么是会话呢&#xff1f; 所谓的会话过程就是指从打开浏览器到关闭浏览器的过程。 Cookie和Session指的又是什么呢&#xff1f; 大家都知道&#xff0c;http是无状态的协议&#xff0c;客户每次读取web页面时&#xff0c;服务器都打开新的会话&#xff0c;而且…

jenkins如何安装?

docker pull jenkins/jenkins:lts-centos7-jdk8 2.docker-compose.yml version: 3 services:jenkins:image: jenkins/jenkins:lts-centos7-jdk8container_name: my-jenkinsports:- "8080:8080" # 映射 Jenkins Web 界面端口volumes:- jenkins_home:/var/jenkins_h…

Android NDK开发详解之Android.mk探秘

Android NDK开发详解之Android.mk探秘 概览基础知识变量和宏NDK 定义的 include 变量CLEAR_VARSBUILD_EXECUTABLEBUILD_SHARED_LIBRARYBUILD_STATIC_LIBRARYPREBUILT_SHARED_LIBRARYPREBUILT_STATIC_LIBRARY 目标信息变量TARGET_ARCHTARGET_PLATFORMTARGET_ABI 模块描述变量LOC…

Java架构师系统架构设计全流程

目录 1 导学2 软件架构概述2.1 软件架构设计与生命周期2.2 构件3 软件架构风格3.1 数据流风格3.2 虚拟机风格3.3 层次架构风格3.4 面向服务的架构风格4 软件架构复用4.1 特定领域软件架构4.2 基于架构的软件开发5 软件系统的质量属性6 软件架构评估6.1 质量属性6.2 三种常用的评…