//math.js
function square(x){
return x*x;
}
function cube(x){
return x*x*x;
}
function add(x,y){
return x+y;
}
function sub(x,y){
return x-y;
}
function mul(x,y){
return x*y;
}
function div(x,y){
return x/y;
}
export {
square,
cube,
add,
sub,
mul,
div
}
//index.js
import {cube} from './math.js';
var elm = document.createElement('code');
elm.textContent = '5*5*5='+cube(5);
document.body.appendChild(elm);
//calculate.js
import {add,sub,mul,div} from './math.js';
var results = [add(0,1),sub(3,1),mul(3,1),div(8,2)];
var fragment = document.createDocumentFragment();
results.forEach(res => {
var elm = document.createElement('code');
elm.textContent = res;
elm.appendChild(document.createElement('br'));
fragment.appendChild(elm);
});
document.body.appendChild(fragment);
//webpack.config.js
const path = require('path');
const HtmlWebapckPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
module.exports = {
mode:'development',
devtool:'cheap-source-map',
// mode:'production',
devServer:{
port:3000,
contentBase:path.join(__dirname,'dist')
},
entry:{
'calculate':'./src/calculate.js',
'index':"./src/index.js",
},
output:{
filename:"[name].bundle.js",
path:path.join(__dirname,"dist")
},
module:{
rules:[
{
test:/\.js$/,
include:/src/,
exclude:/node_modules/,
use:{
loader:'babel-loader',
options:{
// presets:['@babel/preset-react']
// presets:[
// [
// '@babel/preset-env',{
// modules:false
// }
// ]
// ]
}
}
},
{
test:/\.css$/,
include:/src/,
use:['style-loader','css-loader'],
}
]
},
plugins:[
new HtmlWebapckPlugin({
template:'./index.html'
}),
new CleanWebpackPlugin()
]
}
index.bundle.js
和calculate.bundle.js
中包含重复模块math.js
,所以这里有优化空间。
//webpack.config.js
entry:{
'calculate':{
import:'./src/calculate.js',
dependOn:'shared'
},
'index':{
import:'./src/index.js',
dependOn:'shared'
},
'shared':"./src/math.js"
},
output:{
filename:"[name].bundle.js",
path:path.join(__dirname,"dist")
}
entry:{
'calculate':'./src/calculate.js',
'index':"./src/index.js",
},
output:{
filename:"[name].bundle.js",
path:path.join(__dirname,"dist")
},
optimization:{
splitChunks:{
chunks:'all',
name:'math'
}
}
splitChunks.chunks
initial
、async
和all
,默认是async
。
initial
async
all
splitChunks.minSize
byte
,默认是10000
,意思是,分割前,该模块的代码体积大于10000 bytes
时,才会被分割出来。splitChunks.minChunks
1
,意思是,分割前,该模块至少被引用1
次,才会被分割出来。splitChunks.name
splitChunks.cacheGroups
splitChunks
中的任何配置项。test
、priority
和reuseExistingChunk
是cacheGroups
这一层独有的。
splitChunks.cacheGroups.{cacheGroup}.test
splitChunks.cacheGroups.{cacheGroup}.priority
defaultVendors
和default
,它俩的优先级都是负数,优先级都很低。splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk
实际开发中,安装在项目根目录的node_modules
下的vue、react等第三方模块,很有必要将它们分割出来并进行缓存。
optimization:{
splitChunks:{
minSize:100,
cacheGroups:{
vendor:{
priority:1,
name:"vendor",
test:/[\\/]node_modules[\\/]/,
// test:/[\\/]node_modules[\\/](react|react-dom)[\\/]/,
minChunks:1,
chunks:'initial'
},
default:{
name:"common",
minChunks:2,
chunks:'initial'
}
}
}
}
为确保babel
能正确解析import()
,安装插件babel-plugin-syntax-dynamic-import
先。
entry:{
'index':"./src/index.js"
},
output:{
filename:"[name].bundle.js",
path:path.join(__dirname,"dist")
},
//index.js
import('./math.js').then(math => {
const {cube} = math;
var elm = document.createElement('code');
elm.textContent = '5*5*5='+cube(5);
document.body.appendChild(elm);
})
import('./math.js')
,math.js
独立成chunk:src_math_js.bundle.js
。
可以看到,webpack
根据math.js
所在路径进行了默认命名。
import(/*webpackChunkName:"math"*/"./math.js")
+output.chunkFilename:"[name].bundle.js"
,将动态导入的math.js
重命名为math.bundle.js
。
//webpack.config.js
entry:{
'index':"./src/index.js"
},
output:{
filename:"[name].bundle.js",
path:path.join(__dirname,"dist"),
chunkFilename:"[name].bundle.js"
},
//index.js
function getComponent(){
return import(/*webpackChunkName:'math'*/'./math.js').then(math => {
const {cube} = math;
var elm = document.createElement('code');
elm.textContent = '5*5*5='+cube(5);
return elm;
})
}
getComponent().then(component => {
document.body.appendChild(component);
})
// async function getComponent(){
// const elm = document.createElement('code');
// const math = await import(/*webpackChunkName:"math"*/"./math.js")
// const {cube} = math;
// elm.textContent = '5*5*5='+cube(5);
// return elm;
// }
// getComponent().then(component => {
// document.body.appendChild(component);
// })
先来了解下。
rel='prefetch'
算是 未雨绸缪,它告诉浏览器说:“这个资源我以后可能会用到,你有空就先帮我拿一下,到时候我就可以直接用了”。
浏览器这方也给力,在当前页面加载完后,一空闲下来就会默默地根据href
到指定路径取资源并放入缓存。也就是说,prefetch
这个动作是在 onload
事件触发后 发生的。
回到/* webpackPrefetch:true*/
,它其实就是 往标签里插入了
看个例子来理解。
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import HomePage from "./components/homePage.js";
ReactDOM.render(<HomePage/>,document.querySelector('#root'));
//homePage.js
import React from 'react';
import Button from './button.js';
class HomePage extends React.Component{
constructor(props){
super(props);
this.state = {
isLogged:false,
welcome:null
}
this.handleLogin = this.handleLogin.bind(this);
}
handleLogin(){
const getWelcome = async () => {
const welcomeModule = await import(
/*webpackChunkName:"welcome"*/
/*webpackPrefetch:true */
'./welcome.js'
);
this.setState({
welcome:welcomeModule.default(),
isLogged:true
})
}
getWelcome();
// import(
// /*webpackChunkName:"welcome"*/
// /*webpackPrefetch:true */
// './welcome.js').then(welcomeModule => {
// this.setState({
// welcome:welcomeModule.default(),
// isLogged:true
// })
// })
}
render(){
const {isLogged,welcome} = this.state;
return isLogged ? welcome : <Button handleLogin={this.handleLogin}/>
}
}
export default HomePage;
//button.js
import React from 'react';
class Button extends React.Component{
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.props.handleLogin();
}
render(){
return <button onClick={this.handleClick}>Login</button>
}
}
export default Button;
//welcome.js
import React from "react";
function Welcome(){
return <p>Welcome!</p>
}
export default Welcome;
点击Login后,才需要Welcome组件
,所以在事件处理程序handleLogin
里才 import
了Welcome组件
,实现按需加载。
import()
里内联/*webpackChunkName:"welcome"*/
则是我们期望的 预拉取。
请注意,prefetch
只是下载了一个资源,并没有执行。
HomePage组件
装载完成后,浏览器才拉取了welcome.bundle.js
,但并没有执行该文件。
点击Login后,才执行了welcome.bundle.js
。
先来了解下。
rel='prefetch'
是将以后可能用来的资源预先拉取下来,以备不时之需。
rel='preload'
则稳扎稳打,它知道自己一定需要哪些资源,所以先把它们下载下来,但不会运行,只有用到这些资源的时候才会运行。
举个简单的例子。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
<link rel="preload" href="./style.css" as='style'>
<link rel="preload" href="./main.js" as="script" >
</head>
<body>
<div id="root">hello world</div>
</body>
</html>
瞧,style.css
和main.js
下载下来了,但style.css
里的样式没有应用上,main.js
里的js代码也没有执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
<link rel="preload" href="./style.css" as='style'>
<link rel="preload" href="./main.js" as="script" >
<link rel="stylesheet" href="./style.css" >
</head>
<body>
<div id="root">hello world</div>
<script src='./main.js'></script>
</body>
</html>
瞧,加了和
后,背景变蓝了,打印
have a nice day
了。
回到/* webpackPreload*/
,它其实就是 往标签里插入了
。
一个有问题的且没有成功的例子:刷新页面后,必须等5秒,这样this.welcome
才完成初始化,这时点击按钮才会显示Welcome!
。另外,这个例子感受不到webpackPreload
的效果。“囧囧”有神~~~~
//homePage.js
import React from 'react';
import Button from './button.js';
class HomePage extends React.Component{
constructor(props){
super(props);
this.state = {
isLogged:false,
welcome:null
}
this.welcome = null;
this.handleLogin = this.handleLogin.bind(this);
}
componentDidMount(){
const getWelcome = async () => {
const welcomeModule = await import(
/*webpackChunkName:"welcome"*/
/*webpackPreload:true */
'./welcome'
);
welcomeModule.default().then( component => {
this.welcome = component;
})
}
getWelcome();
}
handleLogin(){
this.setState({
welcome:this.welcome,
isLogged:true
})
}
render(){
const {isLogged,welcome} = this.state;
return isLogged ? welcome : <Button handleLogin={this.handleLogin}/>
}
}
export default HomePage;
//welcome.js
import React from "react";
async function Welcome(){
const timeout = ms => new Promise(resolve => setTimeout(resolve,ms));
await timeout(5000);
return <p>Welcome!</p>;
}
export default Welcome;
代码分离
链接类型:prefetch
Link prefetching FAQ
rel=‘preload’
preload有什么好
Webpack之prefetch和preload
prefetch和preload
Webpack优化之prefetch和preload