这篇文章通过记录 Gaea 构建工具的开发,进一步帮助大家学习 Webpack,希望人人都可以做架构师,进一步来讲,也可以根据自己的需求,对自己的项目进行定制化设计,让开发充满乐趣!
一、Gaea介绍
Gaea 是一款 Vue 前端单页面应用构建工具。她基于 Node.js、Webpack 模版工程等的 Vue 技术栈的整套解决方案,包含了开发、调试、打包上线完整的工作流程。目的是为了大家在工作中减少等待,同时提供一个稳定的工作平台。
Gaea 目前已经累计在
20+
项目中使用。C端项目如「京东PLUS会员」、「手机充值-定期充」、「车管家-京保养」;员工端广泛使用的京东ME也有大量应用,如「京东ME-用户之声」、
「京东ME-一线支援」、
「京东ME-叫号机」、「京东ME-固资盘点」等。
二、如何使用
g2 init fast
? 项目名称 fast
? 项目版本号 1.0.0
? 项目简介 A project named fast
? 上传服务器地址 测试服务器host地址
? 作者 佚名
? 是否选择推荐配置?是
✔ 正在下载模版
✔ 创建成功:)
完成之后进入项目,下载好依赖就可以去写项目了,这里建议使用推荐配置。
我们在 Gaea init 过程中提供了快速配置这个选项,提供了最基础的开发环境。此外可以根据自己的项目添加定制。
项目结构
Webpack 的配置代码全部放在了
.bin
这个文件中,
build
文件主要是用来放置打包后的文件,
src
则是我们开发的文件存放目录。
我们并把 app.ts 作为了入口文件 。各种命令和功能介绍可以在 readme.md 中去查看。所以很利于我的开发。
三、工具选择
随着前端技术的更新,各种构建工具应用而生,这些都能解决前端代码的构建,但是如何从众多的构建工具中选择适合我们的的呢?
我们熟悉的构建工具有
Npm Script
、
Grunt
、
Gulp
、
Webpack
、
Rollup
它们都有各自的特点,那么我们 Vue 项目构建工具为什么选择
Webpack
?
-
单页应用的流行,网页的功能和实现代码变得复杂、庞大,Web开发向模块化改进。
-
-
四、优化思路
dev or dev:low 模式合理利用机器性能好钢用在刀刃上!
对于
dev
这个构建模式的开发,我们做了很多功课,尤其是大家的开发习惯上,据调查发现
99%的开发者都是在chrome浏览器环境中开发的,针对这个现象,我们要做的工作就是 功能分割:
由上图我们发现 构建工具在工作过程中 编译过程占用了很多的时间,而我们在开发过程中很多步骤是不需要的,例如:
-
-
-
而把这些工作放到 ie9+浏览器 或 者生产环境中去就可以了,在一般的工作中我们只需要快速的编译它们就可以了。
帮机器偷偷懒
对于我们整个项目,每次进行构建都要去分析所有的代码,而实际的我们往往只有很少的改动,所以针对这个现象我们决定帮机器偷偷懒:
需要注意的是这个loader要放到每一个loader的前面,需要注意的是
url-loader
这个配置不能使用,会报错。如果我们什么都不配置它默认的缓存路径在
node_modules/.cache/
文件中,假如在dev或者build过程中发现
这个方法有一定的风险,就是引用的资源一定要是经过编译的,尤其是
.vue
组件库的引用,如果没有进过编译,我们建议把它从
node_modules
中提取出来,放到
src
路径下面。
如果不想这样,也可以自己去
.bin下
修改配置文件对这样的配置进行一下修改
exclude: /node_modules/ // 去掉
让它在编译过程中同时去编译
node_modules
中的文件。
(3) 不在编译公共资源,改为了以cdn的方式引入。
我们默认引入的资源如下,如果你有特殊的需求可以在
dll
中在去配置,这里要注意下大小写,它关系到你在项目中引用是否会报错。
externals:{
vue:'Vue',
'vue-router':'VueRouter',
'axios':'axios',
'vuex':'Vuex'
}
性能挖掘机-更快更好
由于运行在 Node.js 之上的 Webpack 是单线程模型的,所以Webpack 是一按照流程一步步向下执行的,而目前我们的 CPU 大部分都极为的强大,我们希望可以同时去处理多个任务,于是增加了
happyPack
和
thread-loader
。
使用的时候注意不要用在有路径处理的loader里面 例如图片 svg 这类的以及
'style-loader'
,因为我试验过了会报错。
我们是用在了
css-loader,sass-loader,babel-loader
这3个loader中。当我们的项目变的越来越复杂时候,你会发现相对于没有配置这个工具前时间节约了3倍以上。
lint 代码质量检测和自动格式化
我们大部分项目都是多人开发,由于每个人的编码方式不一样,这就造成了一个项目千人前面,后面同事接手后在改造,一个好端端的项目就会像一个垃圾场,于是我们在 Gaea 中就增加了这个功能。
npm i eslint eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
"lint": "eslint --fix --ext .js,.vue src",
执行这个命令如果代码中存在问题,那么我们就可以看到提示,通过链接打开进行修改,同时也会整理我们的代码:
/Users/xxx/xxx/xxx/gaea4-test-fast/fast/src/app.vue
8:21 error 'Prop' is defined but never used no-unused-vars
✖ 1 problem (1 error, 0 warnings)
图片压缩
考虑到我们项目中大量的图片,如果每次都要去手动一张张的压缩是很蛋疼的一件事。
这里借助了
imagemin
一款小工具, 通过简单的配置就可以进行图片压缩工作了,下面是一个核心片段:
const imageminWebp = require('imagemin-webp');
const imageminJpegtran = require('imagemin-jpegtran');
const imageminPngquant = require('imagemin-pngquant');
...
await imagemin(['./src/asset/img/*.{jpg,png}','./src/asset/img/webp/*.webp'],{
destination: './build/images',
plugins: [
imageminJpegtran(),
imageminWebp(),
imageminPngquant({
quality: [0.6, 0.8]
})
]
})
有好的优化方案了,接下来就要把它们都一一实现出来了
配置文件设计思路
在设计配置文件过程中我们主要根据 pack.json 去管理文件的区分,这样的设计方便使用者自行的去更改,也使得代码结构更加清晰。
webpack 我们带你踩过的坑
只有把配置优化到极致,才能把性能发挥到极致,在这过程中我们也踩过不少坑。
(1) 如何调试
我们在开 Webpack 过程中肯定不能一遍遍的去全部执行代码然后看结果,这样不仅浪费时间,而且效果不明显。
这里我们是使用 VS code 的调试工具进行调试的,它的配置过程很简单,点击工具的配置按钮进入配置界面,我们可以看到一个对象文件,只要像下面这样配置好,就可以啦
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"program": "${workspaceFolder}/.bin/index.js"
}
]
}
有了这个配置,我们就可以在编辑器中打断点或者进行数据变化的监听了。
(2) loader配置中遇到的问题
在做这块配置过程中,我们最大的感觉就是 Loader 用起来简单,但是实际配置过程中总是遇到一些意外。总的来说进来保持格式上统一这样有利于维护。wepack 的 loader 写法大体上有两种,我们这里统一推荐用下面这一种
test: /\.css$/, use: ['css-loader' ]
test: /\.css$/,
use: [{
loader:'css-loader',
option:{...}} ]
拿我们 Gaea 来讲,它是 Vue 项目的构建工具 所以在 Loader 的选择上主要如下:
3. style-loader 解析文件中 style标签内的
4. postcss-loader css 加前缀
其中 1、2、3 不管是 dev 还是 build 模式中都要用到,第 4 个我们认为在 build 中用到就可以了。值得注意的是 css-loader 起初我认为 可以用 scss-loader 代替,结果我发现如果 CSS 文件中有 注释等就会编译报错。
另一个问题就是因为我们所有的配置文件都是在 .bin 这个文件中配置的,在 loader 使用过程中会出现路径问题,例如在使用
postcss-loader
的时候我们要如下面代码一样配置:
{
loader: 'postcss-loader',
options: {
config: {
path: path.resolve
(__dirname)
}
}
}
Webpack 选择了函数式编程的方式,所以 Loader 的顺序编程了从右往左,知道这个顺序很重要,不然总会遇到一些未知的错误。
还有它们不会跨文件类型去处理编译的文件,也就是我们在配置文件过程中要以文件类型为最初的纬度,例如:
在 js 转 es5 这个功能,让我们纠结了很久,我们每次构建完成之后都会发现有些 es6 的语法没有成功的转为低版本的,原因就是我们只在 js 文件类型加了
babel-loader
,忽略了 ts 文件也要加上。
我们知道一点增加
cache-loader
会提高编译速度,但是在
ts-loader
文件前面使用它会报错:找不到该文件。
原因就是我们在
ts-loader
的 options 中添加了
appendTsSuffixTo: [/\.vue$/]
。它的意思就是给 .vue 文件增加后缀 。但是这个.ts文件并不实际存在,所以在使用 cache-loader 时,会报找不到这个文件的错误。
解决方案就是把 ts 文件单独提出来,在通过标签的形式引入就好了 :
那么我们怎么给
ts
文件提速呢?这里给大家介绍一种配置方案:
{ loader: 'cache-loader' },
{
loader: 'thread-loader',
options: {
workers: require('os').cpus().length - 1,
},
},
{
loader: 'ts-loader',
options: {
happyPackMode: true ,
}
}
这个插件并没有表面看起来那么美好,虽然官网仅仅说了 style-loader 会报错,但是经过印证发现, vue 和 ts 同样对它的支持不是特别美好,仅仅 js可以用用。
它的代替方式我使用的是
cache-loader
加
thread-loader:
const cpus = require('os').cpus().length - 1;
{
loader: 'thread-loader',
options: {
workers: cpus,
},
}
(6) dll 配置过程详解
通常我们在打包的过程中,大家熟悉的诸如 Jquery、vue、vue-router、react、react-dom、react-router 等等众多第三方库都会被打包进 bundle 文件中。
这样就会带来一个问题,就是每次打包的时候无疑是一个巨大的性能消耗。那么 DllPlugin 就出现了,有了它可以大大提高我们工程的构建速度。那我们开始配置吧~~
首先在 Gaea 工程目录 .bin 下新建 webpack.dll.config.js 文件。然后在 scripts 中增加运行脚本:
"dll": "webpack --config .bin/webpack.dll.config.js"
module.exports = {
entry: {
vendor
},
output: {
path: path.join(__dirname,'./../static/'),
filename: '[name].dll.js',
library
},
plugins:[
new CleanWebpackPlugin(),
new Webpack.DllPlugin({
path: path.join(__dirname, './../static', '[name]-manifest.json'),
name: library,
context: __dirname
})
]
}
主要是在 plugins 配置项中初始化 DllPlugin 插件,然后配置相应的参数。
只配置DllPlugin当然是不行的。DllPlugin 只是将第三方库进行了抽离,但是怎么引用呢?还需要配合DllReferencePlugin使用。
继续上一步,将 DllReferencePlugin 配置在base.config文件中,因为我们在执行 dev 和 build 时都会用到。
new Webpack.DllReferencePlugin({
context:__dirname,
manifest: require('./../static/vendor-manifest.json')
})
等下,按理说配置完这两项之后,我们就可以使用 npm run dll 去生成 JSON 文件了。到了目前这一步,只是我们之前的做法,使用 Gaea 搭建项目时,都必须去主动执行一下 dll。
这次升级之后我们换了一种思路,dll 这个命令我们不是每次搭建的时候都必须执行的。要根据自己实际项目的需要去执行。
Gaea 本身是基于 Vue 技术栈的,所以我们事先已经将 vue、vue-router、vuex、axios这些开发中常用的第三方库以 CDN 的方式引入了页面。
dll 则作为辅助作用,如果有依赖除此之外的其他第三方库,以 Jquery 为例,在初始化完工程之后,首先 NPM 安装 Jquery,然后在 package.json 中添加:
执行命令npm run dll,可以看到生成了对应的static目录:
这样我们就把依赖的 Jquery 第三方库抽离了出去。此外希望在执行 dev 或者 build 时,能够将对应生成的 dll 文件引入到页面。
这里我们用到了 Webpack 的一个插件 htmlWebpackIncludeAssetsPlugin,因为这个插件必须在 HtmlWebpackPlugin 这个插件后面去执行,HtmlWebpackPlugin 的作用就是可以创建 HTML 入口文件。
比如我们的单页面可以生成一个 HTML 文件入口,如果 new 多个,就会生成多个文件入口。
htmlWebpackIncludeAssetsPlugin 的作用就是将配置的静态资源引入页面。所以两个之间是有一定的先后顺序。故需要在 dev 和 build 两个 config 文件中分别引入这个插件。
new htmlWebpackIncludeAssetsPlugin({
assets
:vendor,
publicPath: argv.local ? "" : config.publicPath + '/',
append:false
}),
new CopyWebpackPlugin([
{ from: path.join(__dirname, vendordev), to: path.join(__dirname, "./../build/" + vendor) },
])
这个插件的作用就是将生成的 static 中的 dll 文件拷贝到 build 对应的配置路径下面。
dev 成功执行了,并且在本地启动的页面中也引入了dll文件。
同样,build 也执行了成功。同时将dll文件拷贝到了 build 目录中。
这样就算是完了么?当然没有!!!前面提到了,dll这个命令只是作为辅助作用,意思就是我们在项目中有时根本用不到,如果我们用不到,那之前的有关dll的配置岂不是都不能用了么?
当然了,在这里我们是需要做一些兼容处理的。我在这里处理比较简单,是直接通过生成的static目录是否存在去判断的:
if(fs.existsSync(path.join(__dirname,vendordev))) {
baseConfig.plugins = [
...baseConfig.plugins,
new Webpack.DllReferencePlugin({
context:__dirname,
manifest: require('./../static/vendor-manifest.json')
})
];
}
同样,在 dev 和 build 配置中也需要做同样的兼容:
这样就做到了即使没有 static 同样也可以构建成功。当然,在 dll 配置文件中也做了同样的兼容处理:
const vendor = config.vendorDll;
if (!vendor.length) {
console.log('dll为空,请配置')
process.exit(0)
}
如果 vendor 为空,会给出相应的提示并且中断执行。这样,整个 dll 的配置就算是大功告成了。
五、总结
我们整篇文章介绍了 Gaea 的优化方案,和一些 webpack 的配置注意事项。
另外值得一提的是在这个新版本中,如果选择快速模式就可以看到这次最新的gaea的引擎啦,它同时完美的兼容了我们架构部门开发的 yg。
解决了mac电脑开发环境下
没办法开热点的弊病,手机可以随时扫码查看我们项目在手机端的样式。
希望
能够在工作帮助大家,如果你有更好的想法或者疑问也可以留言告诉我们。
- - - - - - - 阅读推荐 - - - - - - -