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

01-准备工作 #1

Open
FightingHao opened this issue Aug 27, 2019 · 0 comments
Open

01-准备工作 #1

FightingHao opened this issue Aug 27, 2019 · 0 comments

Comments

@FightingHao
Copy link
Owner

FightingHao commented Aug 27, 2019

本周主要熟悉 Vue 的源码架构、flowrollup、构建指令、入口等前备知识。文末有作者阅读源码时遇到的一些问题,欢迎解答和补充!

源码架构

|-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

    1. 打包 js 文件的时候如果发现无用变量,会将其删掉。
    2. 可以将你的 js 中的代码,编译成你想要的格式 (commonJS、ES6、UMD)
    3. rollup 所有资源放同一个地方,一次性加载, 利用 tree-shake 特性来剔除未使用的代码,减少冗余
    4. 适用于类库
  • webpack

    1. 静态资源导入(如 js、css、图片、字体等)
    2. 拆分代码、按需加载
    3. 拥有如此强大的功能,所以 webpack 在进行资源打包的时候,就会产生很多冗余的代码。
    4. 适用于复杂应用

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。

问题汇总

  1. 如何理解「运行时」(Runtime only) ?

  2. Bench:ssr 的作用?

  3. 依赖部分没有 dependencies 只有 devDependencies

  4. commitizenscripts/verify-commit-msg.js 指令区别?

  5. 实时编译的性能瓶颈在哪?

  6. 为什么不使用 class Vue {} 的形式来实现?

问题解答

  1. Vue 有 Runtime + Compiler & Runtime Only 两种模式,分别对应打包后 dist 文件夹下的 vue.jsvue.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 版本。
  1. 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()
})
  1. 问题待解决

  2. commitizen 以便捷的命令行提示,相对新手比较友好,方便提交代码。scripts/verify-commit-msg.js 无提示,直接做提交检测,相对第一种提交方式更加快捷,但需要一定的开发经验来支撑。

  3. 实时编译最终需要走到 new Vue(),vue 的实例相对复杂,私有属性、原型链属性生态庞大,所以开销很大,也是性能的一大瓶颈。

  4. 原因如下

  • 历史原因,vue 诞生时 class 写法不太成熟
  • function 写法性能更好
  • function 更方便模块管理,所有拓展功能模块可在 prototype 上拓展

参考

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

No branches or pull requests

1 participant