Vue源码解读(一):准备工作

前言

Vue3 出来也有好一整子了,但 Vue2 的源码原理学习,不论在升职加薪还是在另谋高就的路上,一直是一个必要的环节,正应了“面试造火箭,上班拧螺丝”这句话。尽管之前对 Vue2 的源码也有学习过,但是一直没有进行一个系统的总结,说白了就是懒。最近在掘金上看到 李永宁 大佬的 《Vue 源码解读》 系列文章后,又开始蠢蠢欲动了。这次主要是对核心实现的一个梳理,细节方面不会太过介绍。

源码地址

本次学习的 Vue源码2.6.14 版本,git命令下载:

git clone https://github.com/vuejs/vue.git

这是我标记好注释的源码:

git clone https://github.com/zhangquanming/vue.git

Flow

Flow 是 facebook 出品的 JavaScript 静态类型检查工具。Vue 2.x 的源码就是利用了 Flow 做静态类型检查。类似 Flow 的工具还有如 TypeScript。大家对TS应该比较了解,对 Flow 感兴趣可以去 官方文档 自行了解。

调试环境

源码下载后,进入根目录,安装依赖

npm i

修改dev脚本,添加 sourcemap

//  package.json
{
"scripts": {
    "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
   // ...
  }
}

启动项目:

npm run dev

运行成功,可以在 dist 目录下找到打包出来的vue.jsvue.js.map 文件。接下来我们在 /examples 目录下新建文件 test.html

 <!-- /examples/test.html -->
 <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vue</title>
</head>
<body>
  <div id="app">
    {{ msg }}
  </div>
  <script src="../dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        msg: 'hello vue'
      }
    })
  </script>
</body>
</html>

在浏览器打开 test.html 文件,使用控制台,就可以进行断点调试,一步步看其运行过程了。

目录结构

|-- benchmarks               # 性能、基准测试
|-- dist                    # 发布目录
|-- examples                 # 范例
|-- flow                    # flow 类型检查
|-- packages                 # 核心代码之外的独立库
|-- scripts                  # 构建配置、脚本
|-- src                     # 源码目录
|-- test                     # 测试目录
|-- types                    # TS 类型声明

Vue 的源码都在 src 下,下面我们重点看下 src 目录:

src
|-- compiler                    # 编译相关,模板解析成 ast 语法树,ast 语法树优化,代码生成等功能。
|-- core                        # 核心代码
|   |-- components               # 通用组件,如 keep-alive
|   |-- config.js                # 一些默认配置项
|   |-- global-api               # 全局API封装
|   |-- index.js
|   |-- instance                 # 构造函数等
|   |-- observer                 # 响应式相关
|   |-- util                     # 工具函数
|   `-- vdom                     # 虚拟DOM相关
|-- platforms                    # 不同平台的支持
|   |-- web                      # web端
|   `-- weex                     # native 客户端
|-- server                       # 服务端渲染,这部分代码是跑在服务端的 Node.js。
|-- sfc                          # .vue 文件解析,将 .vue 文件内容解析成一个 JS 对象。
`-- shared                       # 共享代码,定义了一些工具方法,被浏览器端的 Vue.js 和服务端的 Vue.js 所共享的。

查找入口

通过之前运行构建命令 npm run dev ,找到构建脚本我就能发现:

//  package.json
{
"scripts": {
    "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
   // ...
  }
}
  • dev 脚本中-c scripts/config.js 是配置文件所在。
  • 参数 TARGET:web-full-dev 是输出文件配置项。
// scripts/config.js
const builds = {
   // ...
  // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),   // 构建入口
    dest: resolve('dist/vue.js'),       // 目标文件
    format: 'umd',      // 输出规范
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  }
  // ...

format 表示构建出来的文件规范:

  • cjs 表示 Common.js 规,用于 webpack1。
  • es 表示 ES Module 规范,ES模块,用于 webpack2+。
  • umd 表示 UMD 规范,兼容 cjs 和 amd ,用于浏览器。

我们在使用 vue-cli 创建项目的时候,会让我们选择使用 Runtime Only 版本的还是 Runtime + Compiler 版本。从单个配置也标注了提示,构建出来的文件是属于什么样的版本,我们来看看这两个版本的区别:

  • Runtime Only (仅运行时)——通常需要借助如 webpack 的 vue-loader 工具把 .vue 文件编译成 JS 渲染函数,因为构建后已经编译完成,所以使用时只需运行时的包即可,包的体积更小。
  • Runtime + Compiler (运行时+编译器)——如果没有进行预编译,或者使用了动态编译模板,如 Vue 的 template 属性并传入一个字符串, 需要在客户端进行编译,则需要含有编译器的全量包。

通过上面的单个配置,我们就能找到入口和出口,入口为 resolve('web/entry-runtime-with-compiler.js') ,先看一下 resolve 函数:

// scripts/config.js
const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}
// scripts/alias.js
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  sfc: resolve('src/sfc')
}

最终我们得出入口文件为: src/platforms/web/entry-runtime-with-compiler.js , 接下来我们就从这个入口文件出发,看一看整个 vue 的实现。

相关链接

Vue源码解读(预):手写一个简易版Vue

Vue源码解读(一):准备工作

Vue源码解读(二):初始化和挂载

Vue源码解读(三):响应式原理

Vue源码解读(四):更新策略

Vue源码解读(五):render和VNode

Vue源码解读(六):update和patch

Vue源码解读(七):模板编译(待续)

如果觉得还凑合的话,给个赞吧!!!也可以来我的个人博客逛逛 https://www.mingme.net/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349