kgm-webpack-builder
v1.0.2
Published
webpack构建通用配置包
Downloads
1
Readme
kgm-webpack-builder
介绍
webpack构建通用配置包
软件架构
软件架构说明
安装教程
- xxxx
- xxxx
- xxxx
使用说明
- xxxx
- xxxx
- xxxx
参与贡献
- Fork 本仓库
- 新建 Feat_xxx 分支
- 提交代码
- 新建 Pull Request
构建配置包说明
构建配置包设计
- 通过多个配置文件管理不同环境的webpack配置
- 基础配置: webpack.base.js
- 开发环境: webpack.dev.js
- 生产环境: webpack.prod.js
- SSR环境: webpack.ssr.js
- 抽离成一个npm包统一管理
- 规范:Git日志、readme、ESLink、Semer规范
- 质量:冒烟测试、单元测试、测试覆盖率和CI
- 通过webpack-merge组合配置
merge = require('webpack-merge) merge( {a: [1], b: 5, c: 20}, {a: [2], b: 3, d: 20}, ) => {a: [1, 2], b: 3, c: 20, d: 20} // 合并配置 module.exports = merge(baseConfig, devConfig)
功能模块设计
基础配置:webpack.base.js
- 资源解析
- 解析ES6
npm i @babel/core @babel/preset-env babel-loader -D
// 用来编译ES6npm i -D @babel/plugin-proposal-class-properties
// 是用来编译class类的
- 解析React
npm i -S react react-dom @babel/preset-react
// 用来编译react
- 解析CSS
npm i -D css-loader style-loader
// 解析css
- 解析Less
npm i -D less less-loader
// 解析less
- 解析图片
npm i -D file-loader
npm i -D url-loader
// 也可以处理字体的图片,可以设置较小资源自动base64
- 解析字体
- 解析ES6
- 样式增强
- css前缀补齐
npm i postcss-loader autoprefixer -D
- css px转rem
npm i px2rem-loader -D
npm i lib-flexible -S
- css前缀补齐
- 目录清理
npm i clean-webpack-plugin -D
- 多页面打包
npm i -D glob
- 命令行信息显示优化
npm i -D friendly-errors-webpack-plugin
- 错误捕获和处理
- css提取成一个单独的文件
开发环境:webpack.dev.js
- 代码热更新
- css热更新
- js热更新
- sourcemap
生产环境: webpack.prod.js
- 代码压缩
- 文件指纹
- Tree Shaking
- Scope Hositing
- 速度优化: 基础包CDN
npm i -D html-webpack-externals-plugin
- 体积优化: 代码分隔
npm install --save-dev @babel/plugin-syntax-dynamic-import
- 修改文件:
.babelrc
{ "plugins": [ "@babel/plugin-syntax-dynamic-import" ] }
SSR环境: webpack.ssr.js
- output 的 libraryTarget 设置
- css 解析 ignore
tools环境:webpack.tiils.js
- 用于针对性打包的配置
npm i terser-webpack-plugin -S
目录结构设计
- 结构如下:(test:放置测试代码, lib:放置源代码)
/test /lib - webpack.base.js - webpack.dev.js - webpack.prod.js - webpack.ssr.js README.md CHANGELOG.md .eslinrc.js package.json index.js
配置开始
完善文件内容
- 项目初始化:
npm init -y
- 创建文件夹/文件
- webpack.base.js
- webpack.dev.js
- webpack.prod.js
- webpack.ssr.js
- 安装插件:
npm i webpack-merge -D
使用ESLint规范构建脚本
- 使用 eslint-config-airbnb-base
npm i eslint babel-eslint eslint-config-airbnb-base -D
- eslint --fix 可以自动处理空格
- 创建文件: .eslintrc.js
module.exports = { "parser": "babel-eslint", "extends": "airbnb-base", "env": { "browser": true, "node": true }, "rules": { <!-- "global-require": 0 , "no-console":"off" "semi": 0, // 结尾不加分号 'generator-star-spacing': 'off', "no-tabs":"off", "indent": "off", // h忽略换行, tab时4个空格才对["error", 4] "linebreak-style": ["off", "windows"], // 回车和换行符问题 "no-console": "off", // 允许console.log "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], // js文件中可以写jsx语法 --> } }
- 修改文件:package.json
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "eslint": "eslint ./lib --fix" }
- 运行检查eslint:
npm run eslint
package.json文件
- 要注意依赖和非依赖的位置
```
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"eslint": "eslint ./lib --fix",
"dev": "webpack-dev-server --config ./lib/webpack.dev.js --open",
"build": "webpack --config ./lib/webpack.prod.js"
},
"devDependencies": {
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.18.3",
"babel-eslint": "^10.1.0"
},
"dependencies": {
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"webpack-merge": "^4.2.2",
"@babel/core": "^7.8.4",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-react": "^7.8.3",
"autoprefixer": "^9.7.4",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.4.2",
"cssnano": "^4.1.10",
"file-loader": "^5.1.0",
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-externals-plugin": "^3.8.0",
"html-webpack-plugin": "^3.2.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"px2rem-loader": "^0.1.9",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"style-loader": "^1.1.3",
"url-loader": "^3.0.0"
}
}
```
冒烟测试
冒烟测试执行
- 构建是否成功
- 每次构建完成的build目录是否由内容输出
- 是否有js、css等静态资源文件
- 是否有HTML文件
- 在示例项目里面运行构建,看看是否有报错
- 判断基本功能是否正常
- 编写 mocha 测试用例
- 是否有js、css等静态资源文件
- 是否有HTML文件
开始 mocha 冒烟测试
- 在测试文件夹操作
- 创建文件夹:./test/smoke
- 创建文件:./test/smoke/index.js
- 创建文件夹:./test/smoke/template
- 在这里面拷贝一个项目
- 编辑文件:./test/smoke/index.js
- 安装插件:
npm i -D rimraf
。 构建前用来删除dist目录
const path = require('path') const webpack = require('webpack') const rimraf = require('rimraf') // 删除文件的插件 const Mocha = require('mocha') // 编写测试用例的插件 const mocha = new Mocha({ timeout: '10000ms' }) // 当前进程的工作目录 process.chdir(path.join(__dirname, 'template')) // process.chdir() 方法变更 Node.js 进程的当前工作目录 // 删除dist目录 rimraf('./dist', () => { // 获取构建配置 const prodConfig = require('../../lib/webpack.prod.js') // 运行配置 webpack(prodConfig, (err, stats) => { // 运行失败 if(err) { console.err(err); process.exit(2) } // 运行成功 console.log(stats.toString({ colors: true, modules: false, children: false })); // 加入测试用例 console.log('webpack build success, begin run test'); mocha.addFile(path.join(__dirname, 'html-test.js')) mocha.addFile(path.join(__dirname, 'css-js-test.js')) mocha.run() }) })
- 安装插件:
- 运行当前编写的文件:
node ./test/smoke/index.js
编写测试用例
- 安装插件:
npm i mocha -D
- 新建文件:./smoke/html-test.js
- 用来检查是否存在html文件:
npm i -D glob-all
const glob = require('glob-all') // 检测是否存在html文件 describe('Checking generated html files', () => { it('should generate html files', (done) => { // 同步判断文件是否生成了出来 const files = glob.sync([ './dist/index.html', './dist/search.html', ]) if (files.length > 0) { done() } else { throw new Error('no html files generate!') } }) })
- 用来检查是否存在html文件:
- 新建文件:./smoke/css-js-test.js
- 用来检查是否存在css/js文件
const glob = require('glob-all') // 检测是否存在html文件 describe('Checking generated css js files', () => { it('should generate css js files', (done) => { // 同步判断文件是否生成了出来 const files = glob.sync([ './dist/js/index_*.html', './dist/js/search_*.html', './dist/css/index_*.css', './dist/css/search_*.css', ]) if (files.length > 0) { done() } else { throw new Error('no css js files generate!') } }) })
单元测试与测试覆盖率
单元测试描述
- 技术选型:mocha + chai
- 测试代码:descript, it, expect
- 测试命令:mocha add.test.js
const expect = require('chai').expect const add = require('../src/add') descripe('use expect:src/add.js', () => { it('add(1,2) === 3', () => { expect(add(1, 2).to.equal(3)) }) })
单元测试接入
- 安装 mocha + chai
npm i mocha chai -D
- 新建test目录,并增加xxx.test.js测试文件
- 修改package.json文件
"script": { "test": "./node_modules/.bin/_mocha" }
- 执行测试命令
npm run test
- 创建文件夹/文件
- ./test/unit/webpack-base-test.js
const assert = require('assert') describe('webpack.base.js test case', () => { const baseConfig = require('../../lib/webpack.base') it('entry', () => { assert.equal(baseConfig(true).entry.index.includes('/test/smoke/template/src/index/index.js'), true) assert.equal(baseConfig(true).entry.search.includes('/test/smoke/template/src/search/index.js'), true) }) })
- ./text.index
const path = require('path') // 进入模板目录 process.chdir(path.join(__dirname, 'smoke/template' )) describe('builder-webpack test case', () => { require('./unit/webpack-base-test') // ... require其他文件 })
- 运行测试
npm run test
测试覆盖率(下面的代码测试失败)
- 安装插件:
npm i -g istanbul
- 修改package.json文件
"script": { "test": "istanbul cover ./node_modules/.bin/_mocha" }
持续集成和Travis-CI
- 优点:快速发现错误,防止分支大幅偏离主干(每次提交前都会运行测试)
- 核心措施:代码集成到主干前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
- 接入Travis-CI
- https://travis-ci.org 使用GitHub账号
- 在https://travis-ci.org/account/repositories 为项目开启
- 项目根目录下新增:.travis.yml
发布到npm
- npm搜索是否存在此包
- 如果发布之前需要执行某件事情
- 修改package.json文件
"script": { "prepare": "webpack" }
- 发布:
npm publish
- 如果本机第一次发布包(非第一次可忽略)
- 在终端输入npm adduser,提示输入账号,密码和邮箱,然后将提示创建成功
- 非第一次发布包
- 在终端输入npm login,然后输入你创建的账号和密码,和邮箱,登陆,结果上
- 如何撤销发布的包
- 终端执行:
npm unpublish
- 删除某个版本:
npm unpublish [email protected]
- 删除整个npm市场的包:
npm unpublish z-tool --force
- 终端执行:
- 登录
npm login
- 注意
- 发包前必须取消
webpack.config.js
中的文件监听模式
- 发包前必须取消
- 更新package.json的小版本号:
npm version patch
webpack 分析
- 初级分析:使用webpack内置的stats(颗粒度比较粗)
"script": { "build:stats": "webpack --config ./lib/webpack.prod.js --json > stats.json" }
- 速度分析:使用
npm i -D speed-measure-webpack-plugin
- 分析整个打包总耗时
- 每个插件和loader的耗时情况
- 编辑:./lib.webpack.prod.js
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin') // 分析打包速度 const smp = new SpeedMeasureWebpackPlugin() module.exports = smp.wrap(merge(baseConfig(false), prodConfig)) // module.exports = smp.wrap(/*webpack配置对象*/)
- 体积分析
- 分析项目体积:使用
npm i -D webpack-bundle-analyzer
- 编辑:./lib.webpack.prod.js
const WebpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 分析打包体积 // 插件 plugins: [ new WebpackBundleAnalyzer(), // 分析打包体积 ],
- 会自动打开:http://127.0.0.1:8888/
- 分析项目体积:使用
优化webpack构建
多进程/多实例:使用three-loader解析资源(效果不大,最终未使用)
- 安装:
npm i -D thread-loader
- 修改:webpack.prod.js
module: { rules: [ { test: /\.js$/, use: [ // { loader: 'thread-loader', options: { workers: 3 } }, // 多进程打包 'babel-loader', // 'eslint-loader', // 添加eslint ], }, ], },
多进程/多实例“并行压缩 (效果不大,最终未使用)
- terser-webpack-plugin 开启parallel参数
- 修改:webpack.prod.js
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 多进程/多实例并行压缩 optimization: { minimizer: [ // new TerserWebpackPlugin({parallel: true}) ] },
进一步分包:预编译资源模块(给基础库打包, 可以替代之前的optimization->splitChunks)
- 思路:
- 将react,react-dom,redux,react-redux基础包和业务包打成一个文件
- 方法:
- 使用DLLPlugin进行分包,
- DllReferencePlugin对 manifest.json进行引用
- 创建文件:./lib/webpack.dll.js
- 修改文件:package.json
"scripts": { "dll": "webpack --config ./lib/webpack.dll.js", },
- 后面没做了,自己看资料
缓存,提升二次构建速度
- 缓存思路
- babel-loader 开启缓存
- 修改:webpack.base.js
module: { rules: [ { test: /\.js$/, use: ['babel-loader?cacheDirectory=true', // 'eslint-loader', // 添加eslint ], } ] }
- terser-webpack-plugin 开启缓存
- 修改:webpack.prod.js
const TerserPlugin = require('terser-webpack-plugin'); module: { optimization: { minimizer: [new TerserPlugin({ parallel: true, cache: true })], }, }
- 使用cache-loader 或者 hard-source-webpack-plugin
- 安装插件:
npm i hard-source-webpack-plugin -D
- 修改:webpack.prod.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); // 用于打包缓存 plugins: [ new HardSourceWebpackPlugin(), // 用于打包中的缓存 ],
- 安装插件:
缩小构建目标
- 目的:尽可能少构建模块,比如:babel-loader 不解析node_modules
- 减少文件搜索范围
- 优化 resolve.modules 配置(减少模块搜索层级)
- 优化 resolve.mainField 配置
- 优化 resolve.extensions 配置
- 合理使用 alias
图片压缩(测试失败)
- 要求:基于 Node 库的imagemin 或者 tinypng API
- 使用:配置 image-webpack-loader
npm install image-webpack-loader --save-dev