js代码分离
SPA单页面应用的最大一个问题就是首屏加载的非常慢,原因就是webpack打包后生成的是一个boundle.js,一个boundle.js的好处是减少了网络请求,但是规模较大的项目一个boundle.js文件甚至几十上百M,从而造成请求非常慢。
Code Splitting就主要解决这个问题。
Code Splitting是webpack打包时用到的==重要的优化特性之一==,此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
有三种常用的代码分离方法:
- 入口起点(entry points):使用
entry
配置手动地分离代码。 - 防止重复(prevent duplication):使用
SplitChunksPlugin
去重和分离 chunk。 - 动态导入(dynamic imports):通过模块的内联函数调用来分离代码。
单页面中第一种几乎不会用到,第二种也几乎不会用。
手动配置多入口
-
在webpack配置文件中配置多个入口
entry: { main: './src/main.js', other: './src/other.js' }, output: { // path.resolve() : 解析当前相对路径的绝对路径 // path: path.resolve('./dist/'), // path: path.resolve(__dirname, './dist/'), path: path.join(__dirname, '..', './dist/'), // filename: 'bundle.js', filename: '[name].bundle.js', publicPath: '/' },
-
在main.js和other.js中都引入同一个模块,并使用其功能
main.js
import $ from 'jquery' $(function() { $('<div></div>').html('main').appendTo('body') })
other.js
import $ from 'jquery' $(function() { $('<div></div>').html('other').appendTo('body') })
-
修改package.json的脚本,添加一个使用dev配置文件进行打包的脚本(目的是不压缩代码检查打包的bundle时更方便)
"scripts": { "build": "webpack --config ./build/webpack.prod.js", "dev-build": "webpack --config ./build/webpack.dev.js" }
-
运行
npm run dev-build
,进行打包 -
查看打包后的结果,发现other.bundle.js和main.bundle.js都同时打包了jQuery源文件
这种方法存在一些问题:
- 如果入口 chunks 之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。
- 这种方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码。
代码分割 抽取公共代码
Webpack v4以上使用的插件为SplitChunksPlugin
,以前使用的CommonsChunkPlugin
已经被移除了,最新版的webpack只需要在配置文件中的optimization
节点下添加一个splitChunks
属性即可进行相关配置
-
修改webpack配置文件
optimization: { splitChunks: { chunks: 'all' } },
-
运行
npm run dev-build
重新打包 -
查看
dist
目录main.boundle.js other.boundle.js cvendors~main~other.boundle.js
-
查看
vendors~main~other.bundle.js
,其实就是把都用到的jQuery打包到了一个单独的js中
动态导入 (懒加载)
webpack4默认是允许import语法动态导入的,但是需要babel的插件支持;
最新版babel的插件包为:@babel/plugin-syntax-dynamic-import
,以前老版本不是@babel
开头,已经无法使用,需注意;
动态导入最大的好处是实现了懒加载,用到哪个模块才会加载哪个模块,(比如点击时使用到了jq才会去加载),可以提高SPA应用程序的首屏加载速度,Vue、React、Angular框架的路由懒加载原理一样。
-
安装babel插件
npm install -D @babel/plugin-syntax-dynamic-import
-
修改.babelrc配置文件,添加
@babel/plugin-syntax-dynamic-import
插件{ "presets": ["@babel/env"], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-runtime", "@babel/plugin-syntax-dynamic-import" ] }
-
将jQuery模块进行动态导入
相当于promise是一个异步的操作,返回的是promised对象,import可以写在条件判断中function getComponent() { return import('jquery').then(({ default: $ }) => { return $('<div></div>').html('main') }) }
-
给某个按钮添加点击事件,点击后调用getComponent函数创建元素并添加到页面
window.onload = function () { document.getElementById('btn').onclick = function () { getComponent().then(item => { item.appendTo('body') }) } }
静态导入 vs 动态导入
- 静态导入: 写在顶级作用域的import export
- 动态导入: require 或者动态导入 (懒加载)
静态导入的问题:
在单页面应用中,大部分是模块化开发,当用户访问首页的时候,会加载全部的打包好的文件,使得首屏速度很慢,很影响性能;
从性能优化的角度考虑,上面的代码,如果在点击或其他操作时候才去加载jq的相关文件,而不是一开始就全部加载,性能会提高很多,首屏打开速度大大提升。
网友评论