webpack之按需加载

990 阅读3分钟

简介

首页展示的时候只需要对应的js,并不需要全部的js模块,所以这里就可以用按需加载。

目标:配合es6的按需加载import()方法,可以做到减少首页包体积,加快首页的请求速度,只有当需要的时候才会加载对应js。

按需加载某种程度上是代码的打包分割,默认只输出一个chunk.js, 配置动态按需加载之后,会出现多个chunk.js

实践

借助es6的 import(*) 的语法

备注: 按需加载,我们需要明白触发时机,比如目前常见的两种形式:

  1. 事件触发(触发点击事件进而触发动态加载)
  2. 路由触发(地址栏变化触发动态路由懒加载)

demo1事件触发


// main.js
window.document.getElementById('btn').addEventListener('click', function () {
  // 当按钮被点击后才去加载 show.js 文件,文件加载成功后执行文件导出的函数
  import(/* webpackChunkName: "show" */ './show').then((show) => {
    show('Webpack');
  })
});


// show.js
module.exports = function (content) {
  window.alert('Hello ' + content);
};

解析:

import(/* webpackChunkName: "show" */ './show') Webpack 内置了对 import(*) 语句的支持。Webpack会以 ./show.js 为入口新生成一个 Chunk(就像main.js入口生成默认chunk一样),当click事件被点击,此时就会加载import中指定的show.js,加载完show.js之后,import()会返回一个promise,并且将show.js中的默认导出内容作为下一个then的参数。

为了正确的输出在/* webpackChunkName: "show" */中配置的 ChunkName,还需要配置下 Webpack,配置如下:

module.exports = {
  // JS 执行入口文件
  entry: {
    main: './main.js',
  },
  output: {
    // 为从 entry 中配置生成的 Chunk 配置输出文件的名称
    filename: '[name].js',
    // 为动态加载的 Chunk 配置输出文件的名称
    chunkFilename: '[name].js',
  }
};

demo2路由触发

// 示例:router/index.js 路由配置文件

import Foo form './component/Foo.vue'
import Bar form './component/Bar.vue'
import Baz form './component/Baz.vue'

// 将上面的3行import导入修改为下面的import()d动态导入
// Foo.vue 和Bar.vue会被打包到同一个chunk为`group-foo`中
// Baz.vue 被单独打包到`group-baz`这个chunk中

const Foo = () => import(/*webpackChunkName: "group-foo"*/ './component/Foo.vue')
const Bar = () => import(/*webpackChunkName: "group-foo"*/ './component/Bar.vue')
const Baz = () => import(/*webpackChunkName: "group-baz"*/ './component/Baz.vue')

Vue.use(VueRouter);

const routes = [
    { path: "/foo", component: Foo },
    { path: "/bar", component: Bar },
    { path: "/baz", component: Baz },
];

备注

问题1:需要babel转换代码时,Babel 报出错误说不认识 import(*) 语法?

导致上面问题的原因是 import(*) 语法还没有被加入到在 ES6语言中提到的 ECMAScript 标准中去, 为此我们需要安装一个 Babel 插件 @babel/plugin-syntax-dynamic-import,用来支持 import(*) ,并且需要在 .babelrc /babel.config.js中进行配置

// .babelrc
{
  "presets": [
    "env",
    "react"
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import"
  ]
}

问题2:浏览器不支持promise怎么办?

在使用 import() 分割代码后,你的浏览器并且要支持 Promise API 才能让代码正常运行, 因为 import() 返回一个 Promise,它依赖 Promise。对于不原生支持Promise的浏览器,你可以注入 Promise polyfill

问题2import(/* webpackChunkName: "show" */)中的/**/包含的是什么?

/* webpackChunkName: "show" */ 的含义是为动态生成的 Chunk 赋予一个名称,如果不指定动态生成的Chunk的名称,默认名称将会是 [id].js/* webpackChunkName: "show" */ 是在 Webpack3 中引入的新特性,在 Webpack3 之前是无法为动态生成的 Chunk 赋予名称的。

参考

zhuanlan.zhihu.com/p/159216534

juejin.cn/post/684490…

webpack.wuhaolin.cn/4%E4%BC%98%…