Vue源码分析

约2万字-Vue源码解读汇总篇(续更) 未完待续...Vue源码解读:06Vue3探索篇Vue源码解读:05生命周期篇Vue源码解读:04模板编译篇Vue源码解读:03虚拟Dom篇Vue源码解读:02变化侦测篇Vue源码解读:01核心思想篇Vue源码解读:前言篇从写下的代码,到用户见到界面,大致经历以下几个流程:模板字符串=》ast树=》render函数=》vnode=》用户见到的界面。将开发者写下的模板字符串,经过一些列处理,生成render函数的这一过程就是模板编译。简单地说,模板编译解析就是为了得到render函数。 阅读详情

Vue.js 源码学习笔记

最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些。那么,就让我来吧:)

程序结构梳理

Vue 程序结构

Vue.js 是一个非常典型的 MVVM 的程序结构,整个程序从最上层大概分为

  1. 全局设计:包括全局接口、默认选项等
  2. vm 实例设计:包括接口设计 (vm 原型)、实例初始化过程设计 (vm 构造函数)

这里面大部分内容可以直接跟 Vue.js 的官方 API 参考文档对应起来,但文档里面没有且值得一提的是构造函数的设计,下面是我摘出的构造函数最核心的工作内容。

Vue 实例初始化

整个实例初始化的过程中,重中之重就是把数据 (Model) 和视图 (View) 建立起关联关系。Vue.js 和诸多 MVVM 的思路是类似的,主要做了三件事:

  1. 通过 observer 对 data 进行了监听,并且提供订阅某个数据项的变化的能力
  2. 把 template 解析成一段 document fragment,然后解析其中的 directive,得到每一个 directive 所依赖的数据项及其更新方法。比如 v-text="message" 被解析之后 (这里仅作示意,实际程序逻辑会更严谨而复杂):
    1. 所依赖的数据项 this.$data.message,以及
    2. 相应的视图更新方法 node.textContent = this.$data.message
  3. 通过 watcher 把上述两部分结合起来,即把 directive 中的数据依赖订阅在对应数据的 observer 上,这样当数据变化的时候,就会触发 observer,进而触发相关依赖对应的视图更新方法,最后达到模板原本的关联效果。

所以整个 vm 的核心,就是如何实现 observer, directive (parser), watcher 这三样东西

文件结构梳理

Vue.js 源代码都存放在项目的 src 目录中,我们主要关注一下这个目录 (事实上 test/unit/specs 目录也值得一看,它是对应着每个源文件的测试用例)。

src 目录下有多个并列的文件夹,每个文件夹都是一部分独立而完整的程序设计。不过在我看来,这些目录之前也是有更立体的关系的:

Vue 文件结构

  • 首先是 api/* 目录,这几乎是最“上层”的接口封装,实际的实现都埋在了其它文件夹里
  • 然后是 instance/init.js,如果大家希望自顶向下了解所有 Vue.js 的工作原理的话,建议从这个文件开始看起
    • instance/scope.js:数据初始化,相关的子程序 (目录) 有 observer/*、watcher.js、batcher.js,而 observer/dep.js 又是数据观察和视图依赖相关联的关键
    • instance/compile.js:视图初始化,相关的子程序 (目录) 有 compiler/*、directive.js、parsers/*
  • 其它核心要素:directives/*、element-directives/*、filters/*、transition/*
  • 当然还有 util/* 目录,工具方法集合,其实还有一个类似的 cache.js
  • 最后是 config.js 默认配置项

篇幅有限,如果大家有意“通读” Vue.js 的话,个人建议顺着上面的整体介绍来阅读赏析。

接下来是一些自己觉得值得一提的代码细节

一些不容错过的代码/程序细节

this._eventsCount 是什么?

一开始看 instance/init.js 的时候,我立刻注意到一个细节,就是 this._eventsCount = {} 这句,后面还有注释

eventsCount1

for $broadcast optimization

非常好奇,然后带着疑问继续看了下去,直到看到 api/events.js 中 $broadcast 方法的实现,才知道这是为了避免不必要的深度遍历:在有广播事件到来时,如果当前 vm 的 _eventsCount 为 0,则不必向其子 vm 继续传播该事件。而且这个文件稍后也有 _eventsCount 计数的实现方式。

eventsCount2

eventsCount3

这是一种很巧妙同时也可以在很多地方运用的性能优化方法。

数据更新的 diff 机制

前阵子有很多关于视图更新效率的讨论,我猜主要是因为 virtual dom 这个概念的提出而导致的吧。这次我详细看了一下 Vue.js 的相关实现原理。

实际上,视图更新效率的焦点问题主要在于大列表的更新和深层数据更新这两方面,而被热烈讨论的主要是前者 (后者是因为需求小还是没争议我就不得而知了)。所以这里着重介绍一下 directives/repeat.js 里对于列表更新的相关代码。

diff1

首先 diff(data, oldVms) 这个函数的注释对整个比对更新机制做了个简要的阐述,大概意思是先比较新旧两个列表的 vm 的数据的状态,然后差量更新 DOM。

diff2

第一步:便利新列表里的每一项,如果该项的 vm 之前就存在,则打一个 _reused 的标 (这个字段我一开始看 init.js 的时候也是困惑的…… 看到这里才明白意思),如果不存在对应的 vm,则创建一个新的。

diff3

第二步:便利旧列表里的每一项,如果 _reused 的标没有被打上,则说明新列表里已经没有它了,就地销毁该 vm。

diff4

第三步:整理新的 vm 在视图里的顺序,同时还原之前打上的 _reused 标。就此列表更新完成。

顺带提一句 Vue.js 的元素过渡动画处理 (v-transition) 也设计得非常巧妙,感兴趣的自己看吧,就不展开介绍了

组件的 [keep-alive] 特性

keepAlive1

keepAlive2

Vue.js 为其组件设计了一个 [keep-alive] 的特性,如果这个特性存在,那么在组件被重复创建的时候,会通过缓存机制快速创建组件,以提升视图更新的性能。代码在 directives/component.js。

数据监听机制

如何监听某一个对象属性的变化呢?我们很容易想到 Object.defineProperty 这个 API,为此属性设计一个特殊的 getter/setter,然后在 setter 里触发一个函数,就可以达到监听的效果。

ob

不过数组可能会有点麻烦,Vue.js 采取的是对几乎每一个可能改变数据的方法进行 prototype 更改:

ob_array1

但这个策略主要面临两个问题:

  1. 无法监听数据的 length,导致 arr.length 这样的数据改变无法被监听
  2. 通过角标更改数据,即类似 arr[2] = 1 这样的赋值操作,也无法被监听

为此 Vue.js 在文档中明确提示不建议直接角标修改数据

ob_array2

同时 Vue.js 提供了两个额外的“糖方法” $set 和 $remove 来弥补这方面限制带来的不便。整体上看这是个取舍有度的设计。我个人之前在设计数据绑定库的时候也采取了类似的设计 (一个半途而废的内部项目就不具体献丑了),所以比较认同也有共鸣。

path 解析器的状态机设计

首先要说 parsers 文件夹里有各种“财宝”等着大家挖掘!认真看一看一定不会后悔的

parsers/path.js 主要的职责是可以把一个 JSON 数据里的某一个“路径”下的数据取出来,比如:

var path = 'a.b[1].v'
var obj = {
  a: {
    b: [
      {v: 1},
      {v: 2},
      {v: 3}
    ]
  }
}
parse(obj, path) // 2

所以对 path 字符串的解析成为了它的关键。Vue.js 是通过状态机管理来实现对路径的解析的:

state1

咋一看很头大,不过如果再稍微梳理一下:

state graph

也许看得更清楚一点了,当然也能发现其中有一点小问题,就是源代码中 inIdent 这个状态是具有二义性的,它对应到了图中的三个地方,即 in ident 和两个 in (quoted) ident。

实际上,我在看代码的过程中顺手提交了这个 bug,作者眼明手快,当天就进行了修复,现在最新的代码里已经不是这个样子了:

state2

而且状态机标识由字符串换成了数字常量,解析更准确的同时执行效率也会更高。

一点自己的思考

首先是视图的解析过程,Vue.js 的策略是把 element 或 template string 先统一转换成 document fragment,然后再分解和解析其中的子组件和 directives。我觉得这里有一定的性能优化空间,毕竟 DOM 操作相比之余纯 JavaScript 运算还是会慢一些。

然后是基于移动端的思考,Vue.js 虽确实已经非常非常小巧了 (min+gzip 之后约 22 kb),但它是否可以更小,继续抽象出常用的核心功能,同时更快速,也是个值得思考的问题。

第三我非常喜欢通过 Vue.js 进行模块化开发的模式,Vue 是否也可以借助类似 web components + virtual dom 的形态把这样的开发模式带到更多的领域,也是件很有意义的事情。

总结

Vue.js 里的代码细节还不仅于此,比如:

  • cache.js 里的缓存机制设计和场景运用 (如在 parsers/path.js 中)
  • parsers/template.js 里的 cloneNode 方法重写和对 HTML 自动补全机制的兼容
  • 在开发和生产环境分别用注释结点和不可见文本结点作为视图的“占位符”等等

自己也在阅读代码,了解 Vue.js 的同时学到了很多东西,同时我觉得代码实现只是 Vue.js 优秀的要素之一,整体的程序设计、API 设计、细节的取舍、项目的工程考量都非常棒!

总之,分享一些自己的收获和代码的细节,希望可以帮助大家开阔思路,提供灵感。

vue源码浅解析(一) vue源码解析 阅读详情

相关推荐

vue-router源码分析(上)

路由的概念相信大部分同学并不陌生,我们在用 开发过实际项目的时候都会用到 这个官方插件来帮我们解决路由的问题。它的作用就是根据不同的路径映射到不同的视图。本文不再讲述路由的基础使用和,不清楚的同学可以自行查阅官方文档vue-router3 对应 vue2 和 vue-router4 对应 vue3。今天我们从源码出发以源码为例,一起来分析下的具体实现。vue-router源码分析(上)vue-router源码分析(中)vue-router源码分析(下)既然我们在分析路由,我们首先来说说什么是路由,什么后

军火库 792

Vue源码解析(详细篇)

vue数据观测实现原理,模版编译过程,模版挂载过程分析

weixin_46996561的博客 8299

vue3源码分析 -- watch

1)watch函数实际执行的是doWatch方法,调度器scheduler在watch中很关键2)scheduler两者之间存在互相作用的关系,一旦effect触发了scheduler,那么会导致执行,job方法就被塞入微任务的队列中3)只要job()触发,那么就表示watch触发了一次。

triggerV的博客 1295

vue源码解析

vue源码解析

weixin_47964837的博客 6630

vue.$nextTick源码分析

最近有人问我一个问题: 这是一个点击触发的函数,start绑定的是class为info的dom的v-if,但是把这句话放到下面就获取不到dom了是为什么 其实之前也遇到过这个问题,不过没有深究,因为赶时间完成功能,不过现在闲下来了,正好有人问问题,我就回过头去 好好再研究一下这个问题。 以下源码分析都是基于 “vue”: “^2.6.10” 版本。 这是我debugger用的测试页面 就很简单...

boringsummers的博客 628

vue源码分析

1. 获取vue源码地址 项目地址:https://github.com/vuejs/vue 迁出项目: git clone https://github.com/vuejs/vue.git 当前版本号:2.6.10 2. 文件目录 3. 调试环境搭建 (1)、安装rollup: npm i -g rollup (2)、修改dev脚本,添加sourcemap,package.json...

空谷足音的博客 692

vue 源码解析

watch:watcher解析表达式,收集依赖,在依赖(表达式值)改变时触发回调(handler);调用所有watcher的update更新。数据双向绑定:用Object.defineProperty方法重写data中每个数据的get,set方法,在get中执行依赖收集(执行 Dep.depend 方法)和set中派发更新通知(执行 Dep.notify 方法)。computed。

wanglun_的博客 3793

如何使用ArcGIS Pro制作带基底三维地图

点击运行生成范围线,因为之前对高程进行了夸大,导致生成的范围线和地形边缘不重合,我们打开范围线的图层属性,选择高程选项卡,要素位于选择地面上,如下图所示。目前制作的三维地图下面是空的一片,现在添加一个基底,在工具箱内点击“3D Analyst工具\栅格\转换\栅格范围”,调用栅格范围工具,如下图所示。打开ArcGIS Pro,新建一个局部场景,在高程表面下,地面上点击右键,选择添加高程源图层,将需要制作三维地图的DEM数据加载进来,如下图所示。在要素图层菜单内,类型选择绝对高度,如下图所示。

3978

【Vuejs】1057- Vue.js 源码分析—— Slots 是如何实现的

今天主要分析 Vue.js 中常用的 Slots 功能是如何设计和实现的。本文将分为普通插槽、作用域插槽以及 Vue.js2.6.x 版本的v-slot 语法三部分进行讨论。本文属于进...

pingan8787 639

把STM32MP135当单片机玩:实战SD卡脱机运行LED程序(基于MP135DK开发板)

本文详细介绍了如何在STM32MP135开发板上实现SD卡脱机运行LED程序,通过裸机开发方式(Bare Metal)利用HAL库完成GPIO控制,并解析了SD卡启动流程。内容涵盖工程创建、代码编写、SD卡烧录及常见问题排查,为开发者提供了一种高性能单片机开发方案。

weixin_33734785的博客 242

OpenCompass实战:从零部署大模型评测平台,量化模型能力选型

在大模型技术选型与评估中,客观、可量化的评测体系是工程实践的关键。其核心原理在于通过标准化的数据集、统一的评测方法与自动化流程,将主观的模型体验转化为可比较的指标数据。这种评测框架的技术价值在于解决了模型能力评估的模糊性问题,为开发者提供了模型选型、效果对比与迭代优化的科学依据。典型的应用场景包括对比不同开源模型在代码生成、逻辑推理等特定任务上的性能,或是评估微调后模型在业务数据集上的提升效果。OpenCompass作为开源的大模型全能力评测平台,正是这一理念的工程化实现。它通过模块化的四层流水线架构和配置

weixin_28725037的博客 626

vue-router源码分析

vue-router 源码分析 前言:在vue的生态当中,vue-router必然是有一席之地的,我们有用到过其中各种好用的api,比如addRoute、.push、.replace、vm.$route、vm.routes等等,可以这么说,在单页面应用盛行的今天,我们如果使用vue来开发项目,一定离不开vue-router。而我们今天就来认真的分析一下vue-router的源码,了解一下vue-router的各种各样api的原理,以及他是怎么嵌入vue项目中运行的,好了废话不多说我们开始吧! 一、源码结

weixin_50786692的博客 3615

SVM-支持向量机学习(1):线性可分SVM的基本型

1. foreword先看的是李航老师的书第7章,比较传统按照以下三节展开: 线性可分SVM 线性SVM 非线性SVM SMO算法 李航老师讲东西向来不拖泥带水,但本节也几乎用了40页的篇幅,足见涉及内容之多。7月底硬着头皮啃了下来,边看边在笔记上摘抄标注,花了2天时间对SVM有了基本认识。一个月过去了,如今重新整理为博客,再看轻松许多,并不断对比西瓜书的内容,启望达到融会贯通。西瓜书此节明显节奏轻

鸟恋旧林的博客 8679

Vue.extend源码分析

Vue.options 指的是定义的一些全局组件, 指令这种, 比如 Vue.components(‘aaa’, {}), Vue.components(‘aaa’, {})就等同于 Vue.options.components.aaa = {}, 源码内部就是这样实现的, 所以 Vue 会将全局的一些东西全部放到 Vue.options 里面。(5). 最后再将Sub构造函数返回, 这个就是Vue.extend的基本原理, 大概也就三步, 1创建构造函数 2改变构造函数原型指向Vue 3合并全局属性。

慕思 1338

开源CMDB与资产管理平台对比报告

本文对比分析了9款主流开源CMDB平台,从集成能力、功能完整性和多语言支持三个维度进行评测。重点介绍了VeCMDB、蓝鲸CMDB、WeCMDB和OpsAny等国产平台的特色:VeCMDB以动态模型和自动发现见长;蓝鲸CMDB适合超大规模企业级应用;WeCMDB专注金融级高并发场景;OpsAny提供一体化运维解决方案。针对不同规模企业给出选型建议:中小团队推荐Snipe-IT等轻量工具;大型企业建议蓝鲸或WeCMDB;需国产化适配的可选VeCMDB或OpsAny。文章还特别指出iTop、GLPI等适合ITIL

huang0612123的博客 1080

Mcgs 屏幕Modbus RTU通讯调试

昆仑通态屏幕作为Modbus Master 与 Modbus Slave通过RS485接口实现通信的调试记录

madao1234的博客 5568

vue的源码分析

vue分析原理

qq_34507902的博客 689

基于BS21的星闪模块设计电路SLE/BLE

基于BS21的星闪模块设计电路SLE/BLE HI2821

2503_91889531的博客 2738

vue3源码分析 -- reactive

reactive函数实际执行了createReactiveObject方法,该方法主要创建了一个Proxy实例对象,给代理对象添加了getter和setter行为 getter和setter方法定义在mutableHandlers对象中,用于拦截对象属性的获取和设置操作

triggerV的博客 1352

Web端百度离线地图3.0实战:从零搭建到私有化部署

本文详细介绍了Web端百度离线地图3.0的完整实战流程,从核心SDK本地化、功能模块下载到地图瓦片部署。通过逐步修改代码与配置,实现在无外网环境下的私有化部署,解决了内网或网络不稳定场景下的地图应用需求,并提供了关键的避坑指南。

n7o8p的博客 714

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

vue3源码分析 -- ref

ref函数本质上做了三件事返回RefImpl的实例对数据处理,如果当前数据为基本类型则直接返回;如果是为复杂类型则调用reactive返回reactive数据RefImpl提供get value和set value方法,这就是为什么设置ref值时,需要带上.value

triggerV的博客 806

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

[Vue源码分析]Vue.extend与Vue.component的区别

版本:Vue.js v2.6.11 Vue.extend与Vue.component的区别,网上搜了很多资料说的都挺好,但是心里似乎总有那么点恍惚,于是找来Vue的源码,一探它底层的实现原理。 直入主题,Vue.extend代码如下 /** * Class inheritance */ Vue.extend = function (extendOptions) { extendOptions = extendOptions || {}; var S

小问's blog 650

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

[Vue源码分析]Vue.use实现原理

最近小组有个关于vue源码分析的分享会,提前准备一下… 前言: 插件通常会为 Vue 添加全局功能,这个官网文档有说过了,如果对插件不了解,建议先阅读一下官网文档:https://cn.vuejs.org/v2/guide/plugins.html#使用插件 使用插件 插件的使用很简单,使用Vue.use注册后即可全局使用,比如项目中需要用到vue-router这个插件,如图引用即可: 接下来分...

一位不知名小前端的一些笔记 7875

Vue源码分析(一)| vue的初始起点

一直想自己来过一遍源码,所以接下来有一个源码学习过程。刚打开vue源码很迷茫,不知道从哪里开始,这里先推荐一个chrome工具Octotree,可以直接在github上看代码结构,都不用下载源码。 直接点到src的core文件夹中,大概这是核心代码吧,我们就从这里开始。。。。。。 注意⚠️:我把自己的一些理解以注释的形式写在代码块里啦,有错误请指正! /src/core/index.js // v...

qq_35536883的博客 555

1. vue源码分析——准备工作

本次源码分析的vue版本为2.x版本 在分析vue源码之前 我们需要先准备一些必要的知识 这样看源码时才不会出现一些疑惑 下面我们进入正题 1. 认识flow 我们都知道js是动态类型语言 它的灵活性有目共睹 但这也意味着我们在写js代码的时候很容易写出一些非常隐蔽的隐患代码 为了解决这个问题 vue使用了flow类型检查工具 之所以选择flow 主要因为babel和eslint都有对应的插件以及...

dhhjd的博客 546

视频教程-经典Vue从入门到案例到源码分析教程(含资料)-Vue

经典Vue从入门到案例到源码分析教程(含资料) 张长志技术全才、擅长领域:区...

weixin_33843943的博客 1250
上一篇: H5开发:手机访问www如何自动跳转到m
下一篇: jquery源码分析
Generon
博客等级 码龄11年 135粉丝 135原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值