懒加载JS脚本的方式
- CommonJS:require.ensure;引入但需要手动执行相关js代码。
- ES6:动态import(目前还没有原生支持,需要babel转换)--推荐使用。引入并且自动执行相关js代码。
如何动态import?
// 安装babel插件
yarn add @babel/plugin-syntax-dynamic-import -D
1
2
2
{
"plugins": [
"@babel/plugin-syntax-dynamic-import"
]
}
1
2
3
4
5
2
3
4
5
如何动态引入?
通过jsonp请求从服务器获取对应的文件。动态的创建script
标签,将对应的脚本插入到页面中。
动态import
// 查看当前代码目录结构
tree -L 2 -I "node_modules"
1
2
2
代码目录解构如下:
├── dist
│ ├── index.html
│ ├── main.bundle.js
│ ├── pageA.chunk.js
│ ├── pageB.chunk.js
│ └── vendors~jquery.chunk.js
├── package.json
├── src
│ ├── index.js // 入口文件
│ ├── module.js
│ ├── pageA.js
│ └── pageB.js
├── webpack.config.js
└── yarn.lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
module.js:
export default 'module';
1
pageA.js:
import './module';
console.log('this is pageA');
export default 'pageA';
1
2
3
2
3
pageB.js:
import './module';
console.log('this is pageB');
export default 'pageB';
1
2
3
2
3
其中,pageA.js和pageB.js共同引用module.js。注意:pageA.js和pageB.js两个文件中都执行了console.log
语句。之后将会看到import()和require()
不同的表现形式:是否会自动执行js
的代码?
webpack配置如下:
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
chunkFilename: '[name].chunk.js'
}
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
index.js:
// import可以通过注释的方法来指定打包后的chunk的名称
import(/* webpackChunkName: 'pageA'*/'./pageA').then(pageA => {
console.log(pageA);
});
import(/* webpackChunkName: 'pageB'*/'./pageB').then(pageB => {
console.log(pageB);
});
document.querySelector('#btn').onclick = () => {
import(/* webpackChunkName: 'jquery'*/'jquery').then($ => {
$.default('body').css('background-color', 'red');
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
使用过vue-router的朋友应该知道,其路由懒加载的配置也是通过import()语法来书写的。
在命令行中运行yarn run build
打包,结果如下:
index.html主要内如如下:
<body>
<button id="btn">按需加载</button>
<script src="./main.bundle.js"></script>
</body>
1
2
3
4
2
3
4
打开浏览器控制台,刷新界面,结果如下图所示:
上图结果说明import()
会自动运行pageA.js和pageB.js
中的代码。
点击按需加载的按钮也会动态加载对应的chunk
:页面背景变成红色,说明动态加载成功。
demo
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import logo from './images/logo.png';
import './search.less';
class Search extends Component {
constructor() {
super();
this.state = {
Text: null
};
this.loadComponent = this.loadComponent.bind(this);
}
loadComponent() {
import('./Text.js').then(Text => {
this.setState({
Text: Text.default
});
});
}
render() {
const {Text} = this.state;
console.log(Text);
return <div className="search">
hello react666
<img
src={logo}
onClick={this.loadComponent}
/>
{
Text ? <Text /> : null
}
</div>;
}
}
ReactDOM.render(
<Search />,
document.querySelector('#root')
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
打包结果如下:1_b9fee4d1.js
即动态import的脚本。
Asset Size Chunks Chunk Names
1_b9fee4d1.js 647 bytes 1 [emitted] [immutable]
img/logo_bd62f047.png 8.54 KiB [emitted]
index.html 293 bytes [emitted]
index_af0e9452.js 3.99 KiB 2 [emitted] [immutable] index
search.html 1.44 KiB [emitted]
search_1d9de098.css 127 bytes 0 [emitted] [immutable] search
search_5f7d4376.js 13.2 KiB 0 [emitted] [immutable] search
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
通过上下两图对比,动态import的脚本是通过创建动态创建script
标签的形式引入的,即jsonp的方式引入。
require.ensure方式
require.ensure()是 webpack特有的,已经被import()取代。require.ensure()不会自动执行js代码,请注意注释。
require.ensure(
dependencies: String[],
callback: function(require),
errorCallback: function(error),
chunkName: String
)
1
2
3
4
5
6
2
3
4
5
6
require.include('./module.js'); // 将pageA和pageB共用的module.js打包在此page中
require.ensure(
['./pageA.js', './pageB.js'], // js文件或者模块名称
function() {
var pageA = require('./pageA'); // 引入后需要手动执行,控制台才会打印
var pageB = require('./pageB');
},
'subPage' // chunkName
);
require.ensure(
['jquery'],
function() {
var $ = require('jquery');
$('body').css('background-color', 'red');
},
'jquery'
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18