之前Element框架项目具体的梳理了项目结构以及所使用的打包构建工具,可以知道还是采用webpack来打包,支持yarn和npm来管理依赖。
本文就介绍Vue的项目结构以及打包构建过程(一些相关的构建会与Element项目做些比较),Vue版本2.6.8。
|-- Vue
|-- .circleci // 持续集成相关配置目录
|-- benchmarks // 基准测试相关
|-- dist // build结果目录
|-- example // 示例,用于测试相关
|-- flow // 类型检测工具flow配置目录
|-- packages // 依赖项目
|-- scripts // 脚本以及配置文件目录
|-- src // 源码内容
|-- test // 单元测试
|-- types // TypeScript相关
|-- .babelrc.js // babel配置
|-- .editorconfig // 文本编码样式配置文件
|-- .eslintignore // eslint校验忽略文件
|-- .eslintrc.js // eslint配置文件
|-- .flowconfig // flow配置文件
|-- .gitignore // Git提交忽略文件配置
|-- BACKERS.md // 赞助者信息文件
|-- LICENSE // 项目开源协议
|-- package.json
|-- README.md // 说明文件
|-- yarn.lock // yarn版本锁定文件
Vue的项目结构中需要关注的点如下:
- circleci:持续集成工具
- flow + .flowconfig:JavaScript静态类型检测
- .editorconfig:统一编码格式的配置文件
- package.json + yarn.lock + scripts:核心,打包构建
本文着重于最后一点,其他点仅仅是补充性的描述说明。具体如下:
在Vue框架项目中存在一个.circleci目录,该目录下存在一个config.js文件,即circleci的配置文件。
circleci持续集成工具,支持Github和Bitbucket(Travis CI是相同的功能,但其支持Github)。
具体相关的你可以看circleci官网。
flow是JavaScript代码的静态类型检查器,通过静态类型注释检查代码是否存在错误。
Vue框架项目中大量使用了flow,全局相关的都定义在flow目录下,例如:
declare type WarningMessage = {
msg: string;
start?: number;
end?: number;
}
详细介绍使用你可以查看Flow官方文档。
EditorConfig的配置文件,该工具是用于解决多人团队开发一个项目时不同编辑器下的编码格式问题。
常搭配eslint工具使用,统一代码风格,支持.editconfig文件的编辑器,该文件的配置优先级高于用户自定义配置。
支持EditorConfig编辑器,常见的有:VScode、Sublime Text等。
Vue中该文件的配置如下:
# 表示文件位于最顶层
root = true
[*]
# 字符编码、缩进样式、缩进个数、换行符表示方式、文件以空白行结束、行首空白字符去除
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
详细介绍请前往EditorConfig官网。
这里是本文核心,从Vue框架的项目结构可知两点信息:
具体看下package.json中所列依赖,主要有:
- babel:babel转换系列
- acorn:轻量级javaScript解析器
- buble:与babel类似的编译器
- chalk:定制终端字符串样式
- chromedriver:Chrome驱动相关的包,实际上是test e2e测试时候使用的
- codecov:集成测试覆盖率工具(Element框架项目中是coveralls)
- commitizen:格式化commit message的工具,提供git cz命令
- conventional-changelog:生成Change log的工具包,即CHANGELOG.md文件
- cz-conventional-changelog:Angular规范的适配器(即规定type类型的)
- cross-spawn:跨平台spawn命令支持
- de-indent:从代码块中删除额外缩进的工具包
- escodegen:JavaScript代码生成器
- hash-sum:hash生成器
- he:HTML实体转码/解码器
- http-server:HTTP服务器
- lint-staged:文件格式过滤工具,常用于配合git commit检查
- nightwatch:自动化测试框架
- phantomjs-prebuilt:提供API控制Chrome浏览器,用于页面抓取、自动化测试等方面
- puppeteer:提供API控制Chrome浏览器
- resolve:实现node require.resolve,同步或异步支持文件读取
- rollup:JavaScript模块打包工具
- Shelljs:Unix Shell命令实现库
- terser:JS解释器和工具,支持ES6+
- yorkie:在husky基础上做了些更改,用于Git hooks
基于package.json中的相关依赖包,实际上关注以下几个点:
rollup
Vue框架本身项目编译打包工具,Vue作为类库并没有使用webpack作为编译打包。至于选择rollup的原因,就需要比较下rollup和webpack的适用范围了。
首先rollup是JavaScript模块打包器,最主要的优点是基于ES6模块的,并且支持tree-shaking特性即无用代码分析移除。具体的适用可以查看rollup中文文档。
平时工作项目都是webpack作为打包工具,相对于webapck来说,rollup的功能相对较弱,rollup只能打包css和js,无法打包图片、字体资源等。
Vue框架本身项目主要就是JS代码(不存在图片、css、字体资源等),没必要使用webpack重量级打包工具。
yorkie + lint-staged
lint流程规范化,在修改代码后对代码进行相关的校验工作,规范代码以及减少Bug的产生。
常使用的方式就是在commit提交时做检验或eslint fix处理,使用的构建方式有:
- husky + lint-staged
- yorkie + githooks + lint-staged
- eslint:这种方式就是普通的eslint检查
husky + lint-staged的方式主要是两种:
// 第一种方式类似配置如下
scripts: {
"precommit": "lint-staged"
},
"lint-staged": {
"*.{js,vue}": [
"eslint --fix",
"git add"
]
}
// 第二种方式类似配置如下
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue}": [
"eslint --fix",
"git add"
]
}
yorkie + githooks + lint-staged这种方式常用就一种,即类似如下的配置:
"gitHooks": {
"pre-commit": "lint-staged",
"commit-msg": "node scripts/verify-commit-msg.js"
},
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
}
commitizen + conventional-changelog + cn-conventional-changelog
规范化提交message并根据
下面是Vue中package.json中除了测试相关的命令。
"scripts": {
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
"dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev",
"dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm",
"dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer",
"dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:web-compiler ",
"dev:weex": "rollup -w -c scripts/config.js --environment TARGET:weex-framework",
"dev:weex:factory": "rollup -w -c scripts/config.js --environment TARGET:weex-factory",
"dev:weex:compiler": "rollup -w -c scripts/config.js --environment TARGET:weex-compiler ",
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex",
"lint": "eslint src scripts test",
"flow": "flow check",
"release": "bash scripts/release.sh",
"release:weex": "bash scripts/release-weex.sh",
"release:note": "node scripts/gen-release-note.js",
"commit": "git-cz"
}
从上面的命令主要分类如下:
从上面的命令中可以看出dev命令主要是不同环境的区分,实际上都是执行scripts/config.js配置文件。在具体看该文件的配置前,先了解下rollup命令以及参数相关等,具体如下:
- -c:—config的简写,即使用配置文件的作用
- -w:—watch的简写,即观察要打包文件的变动,并且在变动时重新打包
- –slient:不要打印警告
- –environment:传给配置文件的设置
// scripts/config.js
const builds = {
// Runtime only (CommonJS)
'web-runtime-cjs-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.dev.js'),
format: 'cjs',
env: 'development',
banner
},
'web-runtime-cjs-prod': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.prod.js'),
format: 'cjs',
env: 'production',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.dev.js'),
format: 'cjs',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
'web-full-cjs-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.prod.js'),
format: 'cjs',
env: 'production',
alias: { he: './entity-decoder' },
banner
},
// 其他环境的生成配置
}
function genConfig (name) {
const opts = builds[name]
const config = {
input: opts.entry,
external: opts.external,
plugins: [
flow(),
alias(Object.assign({}, aliases, opts.alias))
].concat(opts.plugins || []),
output: {
file: opts.dest,
format: opts.format,
banner: opts.banner,
name: opts.moduleName || 'Vue'
},
onwarn: (msg, warn) => {
if (!/Circular/.test(msg)) {
warn(msg)
}
}
}
// 其他处理
return config
}
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
该配置文件的主要逻辑还是很清晰的,根据传递给配置文件的–environment TARGET参数来区分环境调用genConfig生成对应的配置文件。
build相关
编译相关的命令,主要是执行node文件即scripts/build.js,即编译生成config.js中定义的所有环境的文件,输出到dist目录。
release相关
主要是执行shell脚本,即release.sh,主要逻辑如下:
- version版本确认,并确定是否要编译
- 是否跳过执行test相关命令
- npm run build
- vue-template-compiler + npm publish
- vue-server-renderer + npm publish
- 提交新版本tag并且执行publish发布
commit:执行git cz命令,即生成相关的改变点log
Git能够在特定的的重要动作时触发自定义脚本。钩子都被存储在Git目录下的hooks子目录中即.git/hooks,客户端钩子主要有:
其他钩子可查看GIT钩子。