HappyPick
开启子线程打包,加快打包速度,使用id标识
rules: [ { test:/\.css/, use: 'Happypack/loader?id=css' }, { test: /\.js$/, exclude:/node_modules/, include:path.resolve(__dirname,'src'), use: 'Happypack/loader?id=js' } ] // plugins new HappyPack({ id:'css', use:['style-loader','css-loader'] }), new HappyPack({ id:'js', use: [{ loader: 'babel-loader', options: { presets: [ '@babel/preset-env', '@babel/preset-react' // react jsx ] } }] }),复制代码
libraryTarget 和 library
当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到它们
output.libraryTarget
配置以何种方式导出库output.library
配置导出库的名称 output.libraryTarget 是字符串的枚举类型,支持以下配置
var(默认)
编写的库将通过var被赋值给通过library指定名称的变量。
// bundle.js var calculator=(function (modules) {}({}) // index.html 复制代码
commonjs(exports)
exports["calculator"] = (function (modules) {}({}) require('npm-name')['calculator'].add(1,2);复制代码
commonjs2
module.exports = (function (modules) {}({}) require('npm-name').add();复制代码
this
this["calculator"]= (function (modules) {}({}) this.calculator.add();复制代码
window
window["calculator"]= (function (modules) {}({}) window.calculator.add();复制代码
global
global["calculator"]= (function (modules) {}({}) global.calculator.add();复制代码
DLLPlugin
把基础模块独立出来打包到单独的动态连接库里.
定义DLL(DLLPlugin,注意output.library和DLLPlugin的name需要保持一致)
const path=require('path'); const DllPlugin=require('webpack/lib/DllPlugin'); module.exports={ entry: { react:['react','react-dom'] },// 把 React 相关模块的放到一个单独的动态链接库 output: { path: path.resolve(__dirname,'dist'),// 输出的文件都放到 dist 目录下 filename: '[name].dll.js',//输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称 library: '_dll_[name]',//存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react }, plugins: [ new DllPlugin({ // 动态链接库的全局变量名称,需要和 output.library 中保持一致 // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值 // 例如 react.manifest.json 中就有 "name": "_dll_react" name: '_dll_[name]', // 描述动态链接库的 manifest.json 文件输出时的文件名称 path: path.join(__dirname, 'dist', '[name].manifest.json') }) ] }复制代码
使用动态链接库文
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin') plugins: [ new DllReferencePlugin({ manifest:require('./dist/react.manifest.json') }) ]复制代码
html中使用
需要将dll文件先提前引入
复制代码
CDN
-
HTML文件不缓存,放在自己的服务器上,关闭自己服务器的缓存,静态资源的URL变成指向CDN服务器的地址
-
静态的JavaScript、CSS、图片等文件开启CDN和缓存,并且文件名带上HASH值
- 带上 Hash 值的原因是文件名会随着文件内容而变化,只要文件发生变化其对应的 URL 就会变化,它就会被重新下载,无论缓存时间有多长
-
为了并行加载不阻塞,把不同的静态资源分配到不同的CDN服务器上
- 多个域名后会增加域名解析时间
- 可以通过在 HTML HEAD 标签中 加入去预解析域名,以降低域名解析带来的延迟
-
接入cdn(publicPath)
output: { path: path.resolve(__dirname, 'dist'), filename: '[name]_[hash:8].js', publicPath: 'http://img.zhufengpeixun.cn' },复制代码
Tree Shaking
Tree Shaking 可以用来剔除JavaScript中用不上的死代码。它依赖静态的ES6模块化语法,例如通过import和export导入导出。
不要编译ES6模块
- 要让 Tree Shaking 正常工作的前提是交给 Webpack 的 JavaScript 代码必须是采用 ES6 模块化语法的
- "modules": false 的含义是关闭 Babel 的模块转换功能,保留原本的 ES6 模块化语法。
use:[{ loader: 'babel-loader', options: { presets:[['@babel/preset-env',{modules: false }],'@babel/preset-react'] } }]复制代码
显示未使用的导出实例
npx webpack --display-used-exports复制代码
剔除用不上的代码(UglifyJS处理)
webpack --display-used-exports --optimize-minimize复制代码
启动压缩
optimization: { minimizer: [ new UglifyJsPlugin({ cache: true,//启动缓存 parallel: true,//启动并行压缩 //如果为true的话,可以获得sourcemap sourceMap: true // set to true if you want JS source maps }), //压缩css资源的 new OptimizeCSSAssetsPlugin({}) ]}复制代码
提取公共代码
大网站有多个页面,每个页面由于采用相同技术栈和样式代码,会包含很多公共代码,如果都包含进来会有问题
optimization: { splitChunks: { cacheGroups: { commons: { // 页面之间的公共代码 chunks: "initial", minChunks: 2,//最小重复的次数 minSize: 0//最小提取字节数 }, vendor: { // 第三方库 test: /node_modules/, chunks: "initial", // 先抽离公共的第三方库 name: "vendor", } } } }复制代码
Scope Hoisting
Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快, 它又译作 "作用域提升",是在 Webpack3 中新推出的功能。
// hello.js export default 'Hello'; // index.js import str from './hello.js'; console.log(str); // main.js var n = name = "Hello"; console.log(n)复制代码
动态导入和懒加载(import导入返回一个promise)
用户当前需要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载 在给单页应用做按需加载优化时,一般采用以下原则:
- 对网站功能进行划分,每一类一个chunk
- 对于首次打开页面需要的功能直接加载,尽快展示给用户
- 某些依赖大量代码的功能点可以按需加载
- 被分割出去的代码需要一个按需加载的时机
// handler.js module.exports=function () { alert('你点我啦!'); } // index.js document.querySelector('#clickBtn').addEventListener('mouseover',() => { import('./handler').then(clickMe => { window.clickMe=clickMe.default; }); }); // html弹框复制代码
react-router4 路由懒加载
// index.js import React from 'react'; import ReactDOM from 'react-dom'; import {HashRouter as Router,Route} from 'react-router-dom'; import Bundle from './Bundle'; let LazyAbout=(props) => (import('./About')}/>) let Home=() => HomeReactDOM.render(,document.getElementById('root')); // Bundle import React from 'react'; export default class Bundle extends React.Component{ state={Mod: null} componentWillMount() { this.props.load().then(mod=>this.setState({Mod: mod.default? mod.default:mod})); } render() { let Mod=this.state.Mod; return Mod&& ; } } // about import React from 'react'; export default props => About复制代码
热更新
配置hot
const webpack = require('webpack'); module.exports = { entry:{ main:'./src/index.js', }, plugins: [ // 该插件的作用就是实现模块热替换,实际上当启动时带上 `--hot` 参数,会注入该插件,生成 .hot-update.json 文件。 new webpack.NamedModulesPlugin(), // 用于启动 HMR 时可以显示模块的相对路径 new webpack.HotModuleReplacementPlugin(), // Hot Module Replacement 的插件 ], devServer:{ // 告诉 DevServer 要开启模块热替换模式 hot: true, } };复制代码
代码实现
import React from 'react'; import { render } from 'react-dom'; import App from './App'; import './index.css'; render(, document.getElementById('root')); // 只有当开启了模块热替换时 module.hot 才存在 if (module.hot) { // accept 函数的第一个参数指出当前文件接受哪些子模块的替换,这里表示只接受 ./AppComponent 这个子模块 // 第2个参数用于在新的子模块加载完毕后需要执行的逻辑 module.hot.accept(['./App'], () => { // 新的 AppComponent 加载成功后重新执行下组建渲染逻辑 let App=require('./App').default; render( , document.getElementById('root')); }); }复制代码