10天彻底搞定-webpack4.0-打包优化(20-27)

591 阅读3分钟

主要是打包优化方面

ignorePlugin

当不需要一个包的所有的功能的时候,可以忽略某些文件,手动引入对应的功能 安装

// 一个时间转换的插件,里面包含多种语言包的插件,导致插件很庞大
npm install moment
// index.js
import moment from 'moment'
moment.locale('zh-cn')

var r = moment().endOf('day').fromNow();
console.log(r)

可以查看打印效果,打印出来是中文 但尝试打包一下,整个index.js 500+kb,之前打包出来只有50+kb, 主要是因为把moment/locale下所有的文件大欧

但我想只引入中文的文件,能不能不引入包里面所有的文件

配置

// webpack.config.js
let webpack = require('webpack')
module.exports = {
    plugins: [
        new webpack.ignorePlugin(/\.\/locale/, /moment/)
    ]
}

// index.js
import moment from 'moment'
// 手动引入
import 'moment/locale/zh-cn'
moment.locale('zh-cn')

var r = moment().endOf('day').fromNow();
console.log(r)

dllPlugin

动态链接库,能把某些大的库单独打包,单独用script引入,这样这些大的少变动的库,都不用在打包或者编译时重新打包编译

如,引用react

npm install react react-dom
// index.js
import React from 'react' // 引入react
import { render } from 'react-dom'

render(<h1>react</h1>, window.root)

新增webpack.react.congfig.js, 单独打包react

webpack.DllPlugin生成一个映射文件,与webpack.congfig.js的DllReferencePlugin形成关连,这样index.js引入react时,就会先去找manifest.json,有则不会再去打包进去

// webpack.react.congfig.js
let path = require('path')
let webpack = require('webpack')
module.exports = {
    mode: 'development',
    entry: {
        react: ['react', 'react-dom'] // 要打包的第三方
    },
    output: {
        filename: '_dll_[name].js', // 打包出来的文件名
        path: path.resolve(__dirname, 'dist'),
        library: '_dll_[name]', // 会承接(function(modules) {return XXX}) 返回的值
        // libraryTarget: 'var' // umd var this commonjs
    },
    plugins: [
        // 生成一个映射文件,与webpack.congfig.js的DllReferencePlugin形成关连,这样index.js引入react时,就会先去找manifest.json,有则不会再去打包进去
        new webpack.DllPlugin({ // name == library
            name: '_dll_[name]',
            path: path.resolve(__dirname, 'dist', 'manifest.json')
        })
    ]   
}

配置好来后,执行

npx webpack --config webpack.react.config.js

打包后,会生成 _dll_react.js 以及manifest.json, 那怎么让主工程打包时,能用这个_dll_react.js,而不把react 打包到index.js 里面去呢 配置webpack.config.js 的 new webpack.DllReferencePlugin,与 webpack.DllPlugin形成关连

// webpack.config.js
module.exports = {
    mode: "production",
    // 多入口 单入口是字符串 ‘./src/index.js’
    entry: {
        index: './src/index.js', 
    },
    devServer: {
        open: true,
        contentBase: './dist' // 启动服务时,文件去dist取得
    },
    output: {
        filename: '[name].js', // 打包后的文件名
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        ...
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, 'dist', 'manifest.json')
        }),
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env',
                            '@babel/preset-react'
                        ]
                    }
                }
            }
        ]
    }
}

重新打包主工程,你会发现index.js 很小,并不会把react都打包进去

最后在index.html 手动引入 打包出来的_dll_react.js

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document给对方</title>
</head>
<body>
    <div id="root"></div>
    <script src="/_dll_react.js"></script>
</body>
</html>

跑一下主工程

happypack

可以实现多线程打包,是第三方的包

npm install happypack

把之前写在rules的use的配置放到Happypack的use 中

// webpack.config.js
const Happypack = require('happypack')
module.exports = {
    ...
    plugins: [
        // css
        new Happypack({
            id: 'css',
            use: ['style-loader', 'css-loader']
        })
        // js
        new Happypack({
            id: 'js',
            use:[
                {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env',
                            '@babel/preset-react'
                        ]
                    }
                }
            ]
        }),
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: 'Happypack/loader?id=js' // 把之前
            },
            {
                test: /\.css$/,
                use: 'Happypack/loader?id=css'
            }
        ]
    }
}

打包,你会看到有3个进程进行打包,但是分配进程也会消耗性能,所以建议大的工程才采用这个方式,不然打包不快反而慢

noParse

不解析某个库的依赖库

module.exports = {
    ...
    module: {
        noParse: /jquery/
    }
}

webpack 自带优化

1)import 在生产环境下 会自动的去掉没用的代码

别名tree-shaking

比如一个包export 出多个方法,但是实际项目里面只用到其中一个, 当打包模式是production, 它不会把没用的方法打包进去

2)如果用require,它不会去掉无用代码,而且会把导出的结果放到default 中去

let calc = require('./test')
console.log(calc.default.sum(1, 2))

3)scope hosting作用域提升

在webpack 中会自动省略一些可以简化的代码

抽离公共代码

多入口工程,抽离公共代码到一个模块 文件准备 新增common.js

function sum(a, b) {
    return a + b
}

export default sum

other.js/index.js 都引入该模块

import sum from './common'
console.log(sum(3, 5))

配置多入口打包,此时打包,会发现,公共的用的代码都被打包到各自的文件中,整个项目来看是冗余了

那能不能,把公共的代码抽离成单独的文件出来

配置

module.exports = {
    mode: "development",
    optimization: {
        splitChunks: { // 分割代码块
            cacheGroups: { // 缓存组
                common: {
                    chunks: 'initial', // 入口
                    minSize: 0, // 代码多大的时候就抽离
                    minChunks: 2, // 用过一次以上就抽离
                },
            }
        }
    },
}

打包完,则会发现多生成一个commonindexother.js, 里面有共用的sum 模块,

但,假如说,我会引用第三方的库,比如jquery,boostrap等,这些可以抽离成另一个库吗, 如果没有配置的话,默认也会打包到common~index~other.js

第三方库打包配置 other.js/index.js 都引入该模块

import sum from './common'
console.log(sum(3, 5))

import $ from 'jquery'
console.log($)
module.exports = {
    mode: "development",
    optimization: {
        splitChunks: { // 分割代码块
            cacheGroups: { // 缓存组
                common: {
                    chunks: 'initial', // 从入口开始
                    minSize: 0, // 代码多大的时候就抽离
                    minChunks: 2, // 用过一次以上就抽离
                },
                vendor: {
                    priority: 1, // 权重,先抽离第三方,然后在去抽离common 公共代码块,不然会被全部都放到common
                    test: /node_modules/,
                    chunks: 'initial', // 入口
                    minSize: 0, // 代码多大的时候就抽离
                    minChunks: 2, // 用过一次以上就抽离
                }
            }
        }
    },
}

懒加载

可以控制某个点加载资源,而不是一开始全部都加载 文件准备 新建source.js

export default 'ZDFJ'

index.js,点击后加载source.js

let button = document.createElement('button')

button.addEventListener('click', function() {
    // 草案中语法,通过jsonp实现动态加载文件
    import('./source.js').then(function(data){
        console.log(data)
    })
})

document.body.append(button)

抛弃服务,点击按钮,则会发现加载0.js,打包后也会生成这个文件

热更新

不刷新页面,更新代码

文件准备

import str from './source'

if(module.hot) {
    module.hot.accept('./source', ()=>{
        console.log('文件更新了')
    })
}

配置

module.exports = {
    ...
    devServer: {
        ...
        hot: true // 启动热更新
    },
    plugins: [
        ...
        new webpack.HotModuleReplacementPlugin(), // 热更新插件
        new webpack.NamedModulesPlugin(), // 打印热更新的路径
    ]
}

更改source的代码,你会看到页面没有刷新