-
Notifications
You must be signed in to change notification settings - Fork 0
Description
本周主要熟悉
Vue
的源码架构、flow
、rollup
、构建指令、入口等前备知识。文末有作者阅读源码时遇到的一些问题,欢迎解答和补充!
源码架构
|-vue
|-.circleci // ci 相关配置
|-.github // github 提交相关脚本
|-benchmarks // 测试基准相关文件
|-dist // 打包目录
|-examples // 测试用例
|-flow // 全局 flow 相关声明
|-node_modules // packages 打包的 npm 包
|-scripts // 构建脚本
|-src
|-compiler // 编译相关
|-core // vue 核心
|-platforms // 平台相关
|-web // web
|-weex // weex
|-server // ssr 相关
|-sfc // .vue 文件解析
|-shared // 共用代码,比如 utils
|-test // 测试脚本,使用 ts 编写
|-types // 测试脚本的 ts 类型声明文件
flow
类似于 typescript
,提供了静态类型检测功能
const vm: Component = this
export function initMixin (Vue: Class<Component>) {}
// @flow
function square(n: number): number {
return n * n;
}
square('2'); // Error!
Vue 的 flow 相关类型声明配置都在根目录的 flow 文件夹下
rollup
简单的来说 rollup 是一个 JS 模块打包器,可以将小块代码编译成大块复杂的代码。现在已经有很多类库都在使用 rollup 进行打包了,比如:react, vue, preact, three.js, moment, d3 等。
rollup & webpack
-
rollup
- 打包 js 文件的时候如果发现无用变量,会将其删掉。
- 可以将你的 js 中的代码,编译成你想要的格式 (commonJS、ES6、UMD)
- rollup 所有资源放同一个地方,一次性加载, 利用 tree-shake 特性来剔除未使用的代码,减少冗余
- 适用于类库
-
webpack
- 静态资源导入(如 js、css、图片、字体等)
- 拆分代码、按需加载
- 拥有如此强大的功能,所以 webpack 在进行资源打包的时候,就会产生很多冗余的代码。
- 适用于复杂应用
Vue 的 rollup 相关构建配置都在 scripts 目录下,构建命令可以在 package.json
文件中找到
Runtime Only & Runtime + Compiler 两种模式
-
Runtime Only 版本通常需要借助 webpack 的 vue-loader 工具把 .vue 文件编译成浏览器可识别的 JS 文件,这个过程是在编译阶段做的,所以只需要用到运行时的 vue 代码,相对来说体积更加轻量
-
Runtime + Compiler 版本没有对 .vue 文件进行预编译的操作,所以额外的编译器做编译工作,所以对于性能上有所损耗
在实际开发中,更推荐使用 Runtime Only 版本
Tree-Shaking
直译为——树摇,可以理解为通过工具"摇"我们的 JS 文件,将其中用不到的代码"摇"掉。
// 模块1 module1.js
export function a() {}
export function b() {}
export function c() {}
// 模块2 module12.js
import { a } from 'module1' // 只引入 module1 模块中的 a 函数
a() // 执行 a 函数
// rollup 经过 Tree-Shaking 打包后结果
function a() {} // 只打包 a 函数,b、c 函数被 shaking 掉
a()
构建指令
- dev: Runtime + compiler development build (Browser)
- dev:cjs: Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
- dev:esm: Runtime only ES modules build (for bundlers)
- dev:test: 测试所有对外暴露的特性、全局API、错误格式、debug信息是否符合预期
- dev:ssr: 服务端渲染相关
- dev:compiler: Web compiler (CommonJS)
- dev:weex: Weex runtime framework (CommonJS).
- dev:weex:factory: Weex runtime factory.
- dev:weex:compiler: Weex compiler (CommonJS). Used by Weex's Webpack loader.
- build: no more bb
- build:ssr: web Runtime + Web server renderer (CommonJS).
- build:weex: weex 相关的构建都会执行
- sauce: 浏览器兼容测试
- bench:ssr:
- release: lint + test + build + npm publish + git push
- release:note: generate release notes
- commit: check Commit message
入口
入口在 src/platforms
下,分为 web 和 weex 两种平台。以 web 为例,可以看到之前说的 Runtime Only & Runtime + Compiler 两种模式的入口文件:
- entry-runtime-with-compiler.js
- entry-runtime.js
两个文件入口中都引入了 import Vue from './runtime/index'
Vue 的运行时部分,找到 ./runtime/index
这部分代码
import Vue from 'core/index' // vue 的核心
import config from 'core/config'
这部分代码引入了 vue 的核心
import Vue from './instance/index'
vue 核心引入了 instance 中的代码,揭晓了 Vue 的最终面目
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
function Vue (options) { // vue 的构造函数
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue) // 初始化模块
stateMixin(Vue) // 状态相关模块
eventsMixin(Vue) // 事件相关模块
lifecycleMixin(Vue) // 生命周期相关模块
renderMixin(Vue) // 页面渲染相关模块
export default Vue
结论:Vue 本质就是一个构造函数,所以我们一般会使用 new Vue()
的方式来创建 vue 的实例。除此之外,Vue 其它功能模块由对应的 Mixin 来完成,这些 Minxin 把 Vue 当参数传入,然后在 Vue 的 prototype 上拓展功能,所以我们创建的 vue 实例可以直接调用一些 API。
问题汇总
-
如何理解「运行时」(Runtime only) ?
-
Bench:ssr
的作用? -
依赖部分没有
dependencies
只有devDependencies
? -
commitizen
和scripts/verify-commit-msg.js
指令区别? -
实时编译的性能瓶颈在哪?
-
为什么不使用
class Vue {}
的形式来实现?
问题解答
- Vue 有
Runtime + Compiler
&Runtime Only
两种模式,分别对应打包后dist
文件夹下的vue.js
和vue.runtime.js
。
我们从src/platforms/web
入口文件可以看到两种模式对应的入口:
|-web
|-entry-runtime-with-compiler.js
|-entry-runtime.js
- entry-runtime-with-compiler.js 中额外引入了编译部分相关的代码,这部分代码最终会被
rollup
打包到dist
目录的vue.js
中,所以相对于vue.runtime.js
来说体积更大。 - Runtime Only 版本通常需要借助
vue-loader
工具把.vue
文件编译成浏览器可识别的JS
文件。例如将template
转化为对应的render
函数。这个过程是在离线编译阶段做好的,所以只需要用到运行时的vue
代码。 - Runtime + Compiler 版本没有对 .vue 文件进行预编译的操作,需要额外的编译器做编译工作。这个工作是在运行时动态做编译的,所以对于性能上有所损耗,更加推荐使用 runtime 版本。
bench:ssr
主要对应根目录下的benchmarks/ssr
文件夹,做一些 ssr 相关基准的测试工作,返回相应服务端模板字符串渲染的总耗时。
相关代码:
// renderToStream.js
stream.on('end', () => {
complete = self.performance.now() - s
console.log(`first chunk: ${first.toFixed(2)}ms`)
console.log(`complete: ${complete.toFixed(2)}ms`)
console.log()
})
// renderToString.js
renderToString(new Vue(gridComponent), (err, res) => {
if (err) throw err
// console.log(res)
console.log('Complete time: ' + (self.performance.now() - self.s).toFixed(2) + 'ms')
console.log()
})
-
问题待解决
-
commitizen
以便捷的命令行提示,相对新手比较友好,方便提交代码。scripts/verify-commit-msg.js
无提示,直接做提交检测,相对第一种提交方式更加快捷,但需要一定的开发经验来支撑。 -
实时编译最终需要走到
new Vue()
,vue 的实例相对复杂,私有属性、原型链属性生态庞大,所以开销很大,也是性能的一大瓶颈。 -
原因如下
- 历史原因,vue 诞生时 class 写法不太成熟
- function 写法性能更好
- function 更方便模块管理,所有拓展功能模块可在 prototype 上拓展