当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到它们
output.libraryTarget
配置以何种方式导出库output.library
配置导出库的名称 output.libraryTarget 是字符串的枚举类型,支持以下配置编写的库将通过var
被赋值给通过library
指定名称的变量。
module.exports = {
add(a,b) {
return a+b;
}
}
var calculator=(function (modules) {}({})
<script src="bundle.js"></script>
<script>
let ret = calculator.add(1,2);
console.log(ret);
</script>
编写的库将通过 CommonJS 规范导出。
exports["calculator"] = (function (modules) {}({})
require('npm-name')['calculator'].add(1,2);
npm-name是指模块发布到 Npm 代码仓库时的名称
编写的库将通过 CommonJS 规范导出。
module.exports = (function (modules) {}({})
require('npm-name').add();
在 output.libraryTarget 为 commonjs2 时,配置 output.library 将没有意义。
编写的库将通过 this 被赋值给通过 library 指定的名称,输出和使用的代码如下:
this["calculator"]= (function (modules) {}({})
this.calculator.add();
编写的库将通过 window 被赋值给通过 library 指定的名称,即把库挂载到 window 上,输出和使用的代码如下:
window["calculator"]= (function (modules) {}({})
window.calculator.add();
编写的库将通过 global 被赋值给通过 library 指定的名称,即把库挂载到 global 上,输出和使用的代码如下:
global["calculator"]= (function (modules) {}({})
global.calculator.add();
.dll
为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据
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')
})
]
}
webpack --config webpack.dll.config.js --mode development
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
plugins: [
new DllReferencePlugin({
manifest:require('./dist/react.manifest.json')
})
]
webpack --config webpack.config.js --mode development
<script src="react.dll.js">script>
<script src="bundle.js">script>
npm i happypack@next -D
const HappyPack = require('happypack');
rules: [
{
test: /\.js$/,
// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
use: ['happypack/loader?id=babel'],
exclude: path.resolve(__dirname, 'node_modules'),
},
{
test: /\.css$/,
// 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
use: ['happypack/loader?id=css']
}
],
plugins: [
new Happypack({
//ID是标识符的意思,ID用来代理当前的happypack是用来处理一类特定的文件的
id: 'js',
use: [{
loader: 'babel-loader',
//options=query都是向插件传递参数的
options: {
presets: [["@babel/preset-env", { modules: false }], "@babel/preset-react"],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
]
}
}]
}),
new Happypack({
//ID是标识符的意思,ID用来代理当前的happypack是用来处理一类特定的文件的
id: 'css',
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
threads: 4,//你要开启多少个子进程去处理这一类型的文件
verbose: true//是否要输出详细的日志 verbose
})
]
CDN 又叫内容分发网络,通过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。
去预解析域名,以降低域名解析带来的延迟要给网站接入 CDN,需要把网页的静态资源上传到 CDN 服务上去,在服务这些静态资源的时候需要通过 CDN 服务提供的 URL 地址去访问
output: {
path: path.resolve(__dirname, 'dist'),
+ filename: '[name]_[hash:8].js',
+ publicPath: 'http://img.qiniu.cn'
},
Tree Shaking
可以用来剔除JavaScript
中用不上的死代码。它依赖静态的ES6
模块化语法,例如通过import
和export
导入导出。
calc.js
export function add(a,b){
return a + b;
}
export function minus(a,b){
return a - b;
}
index.js
import {add} from './calc';
add(1,2)
calc.js
export function add(a,b){
return a + b;
}
由于只用到了 calc.js 中的 add,所以剩下的都被 Tree Shaking 当作死代码给剔除了
module.export
Webpack 无法分析出哪些代码可以剔除"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({})
]
}
import _ from 'lodash-es';
//加法
function isArray(value) {
return _.isArray(value);
}
//减法
function add(a, b) {
return a + b + _.isArray([]);
}
export {
isArray,
add
}
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default;
module.export = {
plugins: [
...,
new WebpackDeepScopeAnalysisPlugin(),
],
}
大网站有多个页面,每个页面由于采用相同技术栈和样式代码,会包含很多公共代码,如果都包含进来会有问题
pageA.js
import utility1 from './utility1';
import utility2 from './utility2';
import $ from 'jquery';
pageB.js
import utility2 from './utility1';
import utility3 from './utility2';
import $ from 'jquery';
pageC.js
import utility2 from './utility3';
import utility3 from './utility1';
import $ from 'jquery';
entry: {
pageA: './src/pageA',
pageB: './src/pageB',
pageC: './src/pageC'
},
output: {
path: path.resolve(__dirname,'dist'),
filename: '[name].js'
},
optimization: {
splitChunks: {
cacheGroups: {
// 不同页面之间的公用模块
commons: {
chunks: "initial",
minChunks: 2,//最小重复使用的次数
minSize: 0 //最小提取字节数
},
// 第三方模块
vendor: {
test: /node_modules/,
chunks: "initial",
name: "vendor",
}
}
}
}
plugins:[
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'pageA.html',
chunks: ['pageA'],
minify: {
removeAttributeQuotes: true
}
}),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'pageB.html',
chunks: ['pageB'],
minify: {
removeAttributeQuotes: true
}
}),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'pageC.html',
chunks: ['pageC'],
minify: {
removeAttributeQuotes: true
}
})
]
Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快, 它又译作 “作用域提升”,是在 Webpack3 中新推出的功能。
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
resolve: {
// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 开启 Scope Hoisting
new ModuleConcatenationPlugin(),
],
};
hello.js
export default 'Hello';
index.js
import str from './hello.js';
console.log(str);
输出的结果main.js
var n = name = "zfpx";
console.log(n)
函数由两个变成了一个,hello.js 中定义的内容被直接注入到了 main.js 中
用户当前需要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载 在给单页应用做按需加载优化时,一般采用以下原则:
module.exports=function () {
alert('你点我啦!');
}
document.querySelector('#clickBtn').addEventListener('mouseover',() => {
import('./handler').then(clickMe => {
window.clickMe=clickMe.default;
});
});
<div id="clickBtn" onclick="clickMe()">弹框div>
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) => (<Bundle {...props} load={()=>import('./About')}/>)
let Home=() => <div>Home</div>
ReactDOM.render(
<Router>
<div>
<Route path="/" component={Home} />
<Route path="/about" component={LazyAbout}/>
</div>
</Router>,document.getElementById('root'));
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&&<Mod {...this.props}/>;
}
}
import React from 'react';
export default props => <div>About</div>