上一章咱们介绍了Webpack经常使用配置和大体优化思路,这一章节咱们来看一下具体怎么优化(未完待续,大佬们能够在评论中提点意见)css
git地址:github.com/jxs7754/dem…html
const SpeedMeasureWebpackPlugin = reqire('speed-measure-webpack-plugin');
const smp = new SeedMeasureWebpackPlugin();
const webpackCofig = smp.wrap({
plugins:[
// MyPlugin(),
]
})
复制代码
能够分析整个打包的总耗时,能够查看每一个loader和plugins的耗时状况;vue
{
module:{
rules: [
{
test: '/.js$/',
use: [
{
loader: 'thread-loader',
options:{
workers: 3,
}
},
'babel-loader'
]
}
]
}
}
复制代码
const HappyPack = require('happypack');
exports.module = {
rules: [
{
test: /.js$/,
// 1) replace your original list of loaders with "happypack/loader":
// loaders: [ 'babel-loader?presets[]=es2015' ],
use: 'happypack/loader',
include: [ /* ... */ ],
exclude: [ /* ... */ ]
}
]
};
exports.plugins = [
// 2) create the plugin:
new HappyPack({
// 3) re-add the loaders you replaced above in #1:
loaders: [ 'babel-loader?presets[]=es2015' ]
})
];
复制代码
// terser-webpack-plugin
module.exports = {
optimization: {
minimizer: {
new TerserPlugin({
parallel: 4,
})
}
}
}
// 下面这俩个插件能够配置多线程
// parallel-uglify-plugin
// uglifyjs-webpack-plugin
复制代码
设置Externals,使用 html-webpack-externals-plugin将基础包(vue vue-router)经过CDN,不打入包中。node
new HtmlWebpackExternalsPlugin({
externals: [
{
module: 'react',
entry: 'https://xxx/react.min.js',
global: 'React',
},
{
module: 'react-dom',
entry: 'https://xxx/react-dom.min.js',
global: 'ReactDOM',
},
],
}),
复制代码
没有CDN的状况 能够预编译 DllPlugin进行分包,DllReferencePlugin对manifest.json 引用react
// 分包
module.exports = {
mode: 'production',
entry: {
vue: ['vue/dist/vue.esm.js', 'vue-router', 'vuex'],
axios: ['axios', 'qs'],
// ui: ['element-ui'],
},
output: {
filename: '[name]_[chunkhash:8].dll.js',
path: path.join(__dirname, 'build'),
library: '[name]',
},
plugins: [
new CleanWebpackPlugin(),
new webpack.DllPlugin({
name: '[name]_[hash]',
path: path.join(__dirname, 'build/[name].json'),
}),
],
};
// 引用
module.exports = {
plugins: [
...['vue', 'axios'].map((item) => new webpack.DllReferencePlugin({
context: path.join(__dirname, './build'),
manifest: require(`./build/${item}.json`),
})),
]
}
复制代码
缓存是为了二次构建时候,加快构建webpack
{
loader: 'babel-loader',
options:{
cacheDirectory: true
}
}
复制代码
{
optimization: {
minimizer: {
new TerserPlugin({
// 多线程
parallel: 4,
// 缓存
cache: true,
})
}
}
}
复制代码
因为 Loader 对文件的转换操做很耗时,因此须要让尽量少的文件被 Loader 处理。能够经过 test/include/exclude 三个配置项来命中 Loader 要应用规则的文件。ios
在实战项目中常常会依赖一些庞大的第三方模块,以 Vue 库为例,发布出去的 Vue 库中包含多套代码, vue.runtime.esm.js 中只包含运行时的代码。若是不用template选项能够直接用这个减小打包体积。git
module.exports = {
resolve: {
alias: {
'vue$': 'vue/dist/vue.runtime.esm.js',
}
}
}
复制代码
resolve.modules 的默认值是['node_modules'],含义是先去当前目录的node_modules目录下去找咱们想找的模块,若是没找到就去上一级目录 ../node_modules 中找,再没有就去 ../../node_modules中找,以此类推。当安装的第三方模块都放在项目根目录的 node_modules 目录下时,就没有必要按照默认的方式去一层层地寻找,能够指明存放第三方模块的绝对路径,以减小寻找。github
module.exports = {
resolve: {
modules: [path.resolve( __dirname,'node modules')]
}
}
复制代码
在安装的第三方模块中都会有一个package.json文件,用于描述这个模块的属性,其中能够存在多个字段描述入口文件,缘由是某些模块能够同时用于多个环境中,针对不一样的运行环境须要使用不一样的代码。 segmentfault.com/a/119000001…web
在导入语句没带文件后缀时,Webpack会自动带上后缀去尝试询问文件是否存在。若是这个列表越长,或者正确的后缀越日后,就会形成尝试的次数越多,因此resolve.extensions的配置也会影响到构建的性能在配置resolve.extensions时须要遵照如下几点,以作到尽量地优化构建性能。
{
extensions: ['.js'],
},
复制代码
能够分析依赖的第三方模块的大小、业务里面组件的代码大小
const BoundAnalysisPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BoundAnalysisPlugin(),
]
}
复制代码
// 组件按需加载
import {Button} from 'element-ui';
// 模块按需加载
import {cloneDeep} from 'lodash-es';
// Vue 路由懒加载
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
复制代码
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000,
minRemainingSize: 0,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
复制代码
1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到 bundle 里面去,tree shaking 就是只把用到的方法打入 bundle ,没用到的方法会在 uglify 阶段被擦除掉。 注意事项:
使用 purgecss-webpack-plugin 配合 mini-css-extract-plugin 使用
const config = {
module:{
rules: [
{
test: '/.scss$/',
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
{
loader: 'postcss-loader',
options: {
plugins: () => [
// 自动扩展css
require('autoprefixer')(),
],
},
},
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: getAssetPath(
`css/[name]_[contenthash:8]'}.css`, ), }), new PurgecssPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), }), ] } 复制代码
方案 | 优势 | 缺点 |
---|---|---|
babel-polyfill | 大而全 | 体积太大 |
@babel/plugin-transform-runtime | 只polyfill用到的方法和类,体积较小 | 不能polyfill原型上的方法 |
polyfill-service | 只返回客户须要的polyfill | 国内奇葩浏览器 |
ModuleConcatenationPlugin 如今webpack4在mode 不等于none都支持
使用 image-webpack-loader进行图片压缩
使用 @vue/preload-webpack-plugin 实现代码预加载
const config = {
plugins: [
new PreloadPlugin({
rel: 'preload',
include: 'initial',
fileBlacklist: [/\.map$/, /hot-update\.js$/],
}),
new PreloadPlugin({
rel: 'prefetch',
include: 'asyncChunks',
}),
]
}
复制代码
// js
{
output: {
filename: '[name]_[chunkhash:8].js'
}
}
// css
// MiniCssExtractPlugin
{
plugins:[
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
})
]
}
// 图片
// file-loader 使用hash(这里的hash是根据内容生成的,默认是md5)
{
module:{
rules:[
{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: 'file-loader’, options: { name: 'img/[name][hash:8].[ext] ' } }] } ] } } 复制代码
。。。未完待续