github vue源码分析
认识flow
flow类型检查
安装flow
sudo npm install -g flow-bin
初始化flow
flow init
运行flow命令
命令: flow
在js文件中需要有需要有一下标志
/*@flow*/
例子 void
/*@flow*/
class Bar {
x: string; // x 是字符串
y: string | number | void; // y 可以是字符串或者数字
z: boolean;
constructor(x: string, y: string | number | void) {
this.x = x
this.y = y
this.z = false
}
}
var bar: Bar = new Bar('hello')
var obj: { a: string, b: number, c: Array<string>, d: Bar } = {
a: 'hello',
b: 11,
c: ['hello', 'world'],
d: new Bar('hello', 3)
}
Vue.js 源码目录设计
src
├── compiler # 编译相关
├── core # 核心代码
├── platforms # 不同平台的支持
├── server # 服务端渲染
├── sfc # .vue 文件解析
├── shared # 共享代码
Vue.js 源码构建
Vue.js 源码是基于Rollup 构建的,它的相关配置都在scripts目录下
构建脚本
通常一个基于NPM 托管的项目都会有一个package.json 文件,它的实际内容是JSON对象
我们通常会配置scripts字段作为NPM 执行脚本 Vue.js 源码构建脚本如下
{
"script":{
"build":"node scripts/build.js",
"build:ssr":"npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex":" npm run build --weex"
}
}
这里共有3条命令,作为都是构建Vue.js,后面2 条是在第一条命令的基础上,添加环境参数。
当在运行 npm run build
的时候 ,实际上就会执行 node scripts/build.js
接下来看看 它实际是怎么构建的
构建过程
我们对于构建过程分析是基于源码的,先打开构建的入口JS文件,在scripts/build.js 中
let builds = require('./config').getAllBuilds()
if(process.argv[2]){
const filters = process.argv[2].split(',')
builds = builds.filter(b => {
return filters.some(f => b.oupput.file.indexOf(f)) > -f || b._name.indexOf(f) > -1
})
} else {
builds = builds.filter(b => {
return b.output.file.indexOf('weex') === -1
})
}
build(builds)
这端代码逻辑非常简单。先从配置文件读取信息,在通过命令行参数对构建配置过滤,这样就可以构建出不同用途的Vue.js了。配置文件 在 scripts/config.js
const builds = {
'web-runtime-cjs': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.js'),
format:'cjs',
banner
},
'web-full-cjs': {
entry: resolve('web/entry-runtime-with-compilter.js'),
dest: resolve('dist/vue.common.js'),
format: 'cjs',
banner
},
'web-runtime-esm': {
entry:resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format:'es',
banner
},
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compilter.js')
}
}
这里列举了一些Vue.js 构建的配置 还有一些服务器渲染webpack 插件以及weex就不列举了
对于单个配置 它是遵循Rollop 的构建规则,其中entry 属性表示构建的入口JS文件地址,dess属性表示构建后的JS文件地址,fromat 属性表示构建的规则,cjs表示构建构建出来的文档遵循 CommonJS规范,es 表示构建出来的文件遵循ES Module 规范umd表示构建出来的文件遵循UMD规范
以web-runtime-cjs 配置为例 它的entry
是 resolve('web/entry-runtime.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.splice(base.length + 1))
}else {
return path.resolve(__dirname, '..', p)
}
}
这里resolve 函数实现非常简单,它先把resolve 函数传入的参数p
通过/
做了分割数组,然后取数组的第一个元素设置base
。在这个例子中参数p
是web/entry-runtime.js
,那么base
则是web
,base
并不是实际路径,它的真实路径借助了别名的配置,我们来看一下配置别名的代码,在scripts/alias 中
const path = require('path')
module.exports = {
vue: path.resole(__dirname,'../src/platforms/web/entry-runtime-with-compiler'),
compiler: path.resolve(__dirname,'../src/compiler'),
core: path.resolve(__dirname,'../scr/core'),
shared: path.resolve(__dirname,'../src/shared'),
web: path.resolve(__dirname, '../src/platforms/web'),
weex: path.resolve(__dirname, '../src/platforms/weex'),
server: path.resolve(__dirname, '../src/server'),
entries: path.resolve(__dirname, '../src/entries'),
sfc: path.resolve(__dirname, '../src/sfc')
}
很显然,这里web 对应的真实路径是path.resolve(__dirname,'../src/platforms/web')
, 这个路径就找到了Vue.js源码的web目录,然后resolve
函数通过path.resolve(aliases[base], p.slice(base.length + 1))
找到最终路径,它即使Vue.js源码目录下面的entry-runtime.js 。因此 web-runtime-cjs 配置对应的文件入口就找到了
它经过Rollup 的构建打包后,最终会在dist 目录先生成 vue.runtime.common.js
Runtime Only VS Runtime+ Compiler
通常我们利用vue-cli 去初始化我们的Vue.js 项目的时候会询问我们用 Runtime Only 版本还是Runtime+Compiler 版本,下面我们来对比这两个版本
我们在使用Runtime Only 版本的Vue.js 的时候,通常需要借助webpack的vue-loader工具把.vue文件编译成 JavaScript , 因为在编译阶段做的,所以它只包运行时的Vue.js 代码, 因此代码体积更轻
// 需要编译器的模版
new Vue({
template:'{
{h1}}'
})
// 这种情况不需要
new Vue({
render (h) {
return h('div',this.hi)
}
})
因为在Vue.js 2.0 中最终渲染都是通过render 函数 ,如果写tempalte 属性, 则需要编译成render 函数,那么这个编译过程发生运行时,所以需要带有编译器的版本
很显然,这个编译过程对性能又一定损耗,所以通常我们更推荐 Runtime-Only 的Vue.js
zon