Dynamic Import

webpack - split code and dynamic import

如果你这么做了,你的加载速度将提高到无法想象,因为我们把代码“分尸”了…

背景:

而且由于公司项目问题放在首要,因此放下学习,重点来解决下项目问题。
首先,要搞清楚问题:
输入URL后,浏览器加载网站太久了,用户体验非常差。

其次,搞清楚原因:
由于项目使用的是NG1,由于没有使用懒加载路由,导致应用一加载就加载了所有的路由,而且使用webpack打包出来只有一个bundel.js。浏览器每次加载,就相当于加载了几M的代码包。很费时间与资源。
要知道,加载时间也是影响用户体验的很大因素。

最后,根据问题与原因,找到对策来解决问题。

因此,降低加载网站的用时,就变成了我们优化的目标了。


需要做的事:

由于使用的前端构建工具是webpack,因此我们从构建工具开始入手。

从上面的拖慢加载速度的原因里,我们可以归类出下面几个优化方案:

优化1. 使用懒加载路由,代替原本的静态路由。
优化2. 使用代码分割,代替原本臃肿的一个bundel.JS。
优化3. 使用动态加载JS,在需要用到该功能时,才去加载对应的JS。
优化4. 使用以业务为分类点,来代替原本以功能为分类点的文件分类。
优化5. 使用CDN来存放静态资源,比如图片,字体包等。


着手开始干:

这里由于主要是针对 优化2和3来做主要介绍,其他就一笔带过了:
针对-优化1-:
懒加载路由:
基于NG1,所以我们使用$ocLazyLoad来懒加载路由。

针对-优化2和3-:
代码分割:
分割可以使用CommonJSAMD,或者更标准一点的说法,用ES6的模块化思想,来对代码根据功能/业务来分割成多个模块,每个模块就是一个JS文件。而这里使用的是ES6的import关键字,来实现动态加载。

但由于大多数浏览器还是不能很好支持ES6的语法,因此我们需要例如babel的编译工具,来把ES6专为浏览器支持的ES5的语法。

由于是单页面,有入口和出口,因此先贴上webpack的配置代码。
为了方便阅读,所以该配置 (已阉割) 为了能够实现代码分割就够了:

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
const webpack = require('webpack')

module.exports = {
entry: {
app: './src/app.js'
},
// 出口:
output: {
path: __dirname + '/dist/', // 出口文件的目录
filename: '[name].bundel.js', // 出口文件名
publicPath: __dirname + '/dist/', // 用来配合import或require.ensure的分割出来的文件目录
chunkFilename: '[name].bundel.js' // 就是分割出来的文件名
},
resolve: {
extensions: [' ', '.js']
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader", // babel加载器将ES6转为ES5
options: {
presets: ['env', 'es2017'], // 由于可能会有用到更新的语法,所以用上最新的
plugins: ['syntax-dynamic-import', 'transform-runtime'] // 这里的插件第一个是配合Import使用的,第二个有待查验
}
}]
}]
}
}

由于webpack配置文件里,有用到babel的加载器和插件,因此献上.babelrc文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"presets": [
[
"env",
"es2017"
]
],
"plugins": [
"syntax-dynamic-import","transform-runtime"
],
"ignore": [
"node_modules"
]
}

所以,你只要保证了webpack打包的项目能够正常跑起来(不使用分割代码)的前提下,你就可以开始你的分割之旅了:
比如,我想在加载后进行某个交互后再去加载对应的js逻辑代码,于是就有了a.js和b.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 首先,先码出在交互后,才,去加载的文件:
// b.js
let a = '我是交互后才被加载的代码a...'
// ...
exports {a}

// 接下来就是首屏要用到的a.js了:
// 假设使用jq:
$('button').on('click',() => {
// 如果到这里,说明我已经监听到用户的点击事件了,开始我要做的事了:加载b.js:
import(
/* webpackChunkName: "my-chunk-name" */ // 这里就是配合publicPath+chunkFilename的配置了,相当于这里加载到的文件就是 my-chunk-name.bundel.js
// 当然还有其他的选项,这些你要自己去试了...
"./b.js" // 此选项的后缀要加,这是你此时想导入的文件
)
// 由于ES6的import返回的是一个Promise,因此可以使用then来接收结果
.then((res)=> {},(err)=>{})
})

到这里,相当于,我们已经完成了一个简单的代码分割后,然后动态加载的例子了。

针对-优化4-:以业务分割文件夹,代替以功能为基础的分割文件夹。

针对-优化5-:使用CDN,代替原来的存放在目录内的静态资源,比如图片,字体包等。


总结:

经过本次改造webpack,从demo上来看,是已经做到代码分割,模块化开发,动态加载的功能了。可以说提升了性能至少50%,至少在首屏加载速度问题上得到大大的提高。

所以,办法总比问题多,所以这次得到的经验就是,遇到问题,不要急躁,也不要和同事急,争取多讨论多交流多分享多点思想碰撞,问题就没那么难解决了。


觉得我说的不对的欢迎扫描二维码来告诉我,十分感谢!!!

个人能力有限,有错误的麻烦微信或搜索:”强仔说前端”联系我(回复公众号我就能收到),指点指点我,共同进步。

个人公众号二维码

Author: JawQ_
Link: http://wujiaqiang.com/2018/02/13/dynamicImport/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.
支付宝-打赏
微信-打赏