webpack 打包编译优化之路
一、摘要
从最初的html css js 混合编程,到分离编程。再到用上各种框架 react vue angular , 伴随着框架和模块化的概念铺开,打包编译工具渐渐浮出水面。从2013年开始使用打包编译工具,其也经历了多个更新换代,从最初的grunt -> gulp -> webpack , 还有一些自成体系的,例如 fis , 还有一些比较小众的 rollup , browserify 等。我这里主要分享的是基于 typescript + scss 的webpack3打包编译。
二、webpack3配置
2.1 关键配置
entry
打包编译入口,可以单入口也可以多入口
entry : 'xxx/index.tsx', // 单入口
entry : ['xxx/index.tsx', 'yyy/index.tsx'], // 多入口
entry : { // 多入口, 并指定编译后的名字
a: 'xxx/index.tsx',
b: 'yyy/index.tsx',
}, // 多入口
output
path 指定输出路径
filename 输出的文件名字,可增加 hash 后缀
publicPath 指定了你在浏览器中用什么地址来引用你的静态文件,它会包括你的图片、脚本以及样式加载的地址,一般用于线上发布以及CDN部署的时候使用。
{
output: {
path: path.resolve(__dirname, 'build'),
filename: "[name].bundle.[hash:8].js",
publicPath: "http://localhost:3000/build/"
},
}
resolve
extensions 自动加上文件扩展名,在 import 时 可省略, 比如 import './a.tsx' 可简写成 import './a'
alias 设置别名,一些常用的库设置别名,可以有效减少import的路径。推荐一个 很好用的插件 alias-resolve-loader 可以把node_modules 依赖的alias 也一并替换。
resolve: {
alias: {
jquery: 'xxxx/xxxx/jqueryjs'
}
}
module
根据你项目使用的语言选择解析loader , 我这里有两种文件 tsx scss ,就分别指定了两种文件的解析loader
url-loader
超出 8192 大小的 img 用图片形式,其他采用 base64 码形式打包到js中调用
module: {
loaders:[
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192&publicPath=./dist/'},
]
}
plugins
webpack 最强大的就是plugins 生态了,基本你想要的功能都能有现成 plugin 插件直接使用。我们一般常见的插件有
- webpack.optimize.CommonsChunkPlugin 抽离不常改变的公共组件, 线上环境使用
- webpack.optimize.UglifyJsPlugin 丑化 js , 线上环境使用
- ExtractTextPlugin css js 拆分,webpack 默认是把css js 全部打包到一个文件,这样通常会面临加载一个庞大的bundle.js 文件,有这个插件就可以实现 css js 拆分,html 并行加载css js
- BundleAnalyzerPlugin 分析打包文件大小,可以进一步做文件抽离
devtool
可以把错误映射到真实代码中,方便排查错误,生产环境不加
2.2 真实案例
真实项目中,一般线下调试和线上是两个版本 webpack 配置
线下
线下就做最简单的tsx scss 编译,打包成一个大文件
const path = require('path'); // 导入路径包
const webpack = require('webpack');
let config = {
context: __dirname,
devtool: "source-map",
entry: {
assetManage: "./src/xxx/index.tsx",
},
output: {
path: path.resolve(__dirname, 'build'),
filename: "[name].bundle.js",
publicPath: "http://localhost:3000/build/"
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
},
// 使用loader模块
module: {
loaders: [
{test: /\.scss$/, loader: "style-loader!css-loader!sass-loader"},
{test: /\.tsx?$/, loader: ['react-hot-loader', "ts-loader"]}
]
},
plugins: [],
};
module.exports = config;
线上
线上需要生成文件大小,尽量减少页面加载时间
const path = require('path'); // 导入路径包
const webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CompressionPlugin = require("compression-webpack-plugin");
let config = {
context: __dirname,
entry: {
service: "./src/xxx/index.tsx",
vendor: ['react', 'react-dom'],
},
output: {
path: path.resolve(__dirname, 'build'),
filename: "[name].bundle.js",
publicPath: "http://localhost:3000/build/"
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
},
module: {
loaders: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
minimize: true
}
}, "sass-loader"]
})
},
{test: /\.tsx?$/, loader: ['react-hot-loader', "ts-loader"]}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin({
filename: 'css/[name].css',
allChunks: true
}),
new BundleAnalyzerPlugin()
],
};
module.exports = config;
三、优化手段
-
抽离公共组件 vendor , 拆分基础组件 与 业务逻辑代码
-
拆分css至单独文件并压缩
-
去除 devtool
-
增加 BundleAnalyzerPlugin 分析