FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
362 stars 39 forks source link

ES6之使用import()做动态引入 #220

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

项目中有用到vue-router和webpack实现异步加载组件,使用的就是import的函数语法import()语法 在学习webpack的代码拆分章节时,也有专门的一个章节用来讲这个,因此需要系统性学习一下import的函数语法import()

最简单的结构

var promise = import("module-name");

初识import()

标准版的import语法是static的,因此会在加载时就引入所有的代码。 但是很多情况下我们需要动态引入模块,也就是说满足特定条件才加载模块,我们可以使用动态引入去做这件事。

为什么要动态引入

一共有5个原因。

动态引入使用示例

在必要时使用动态引入。 静态引入用于加载初始化的依赖,可以从静态分析工具和tree shaking中做优化。

为了做到动态引入一个module,import需要被当做一个函数去调用。 import当函数调用时,返回结果是一个promise。

import('/modules/my-modules.js')
    .then((module)=>{
        // 用module做点什么
     })

还可以用await语法:

let module = await import('/modules/my-module.js');

如何在webpack中使用import()实现动态引入

// project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
|- /node_modules
// src/index.js
+ function getComponent() {
+   return import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
+     const element = document.createElement('div');
+
+     element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+
+     return element;
+
+   }).catch(error => 'An error occurred while loading the component');
  }
+ getComponent().then(component => {
+   document.body.appendChild(component);
+ })

或者

// src/index.js
+ async function getComponent() {
+   const element = document.createElement('div');
+   const { default: _ } = await import(/* webpackChunkName: "lodash" */ 'lodash');
+
+   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+
+   return element;
  }

  getComponent().then(component => {
    document.body.appendChild(component);
  });

打包后的结果为:体积很小的主程序;体积很大的vendor程序。主程序动态引入verdor。

...
                   Asset      Size          Chunks             Chunk Names
         index.bundle.js  7.88 KiB           index  [emitted]  index
vendors~lodash.bundle.js   547 KiB  vendors~lodash  [emitted]  vendors~lodash
Entrypoint index = index.bundle.js
...

vue-router中如何结合webpack如何动态引入

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

打包出来的结果为:

0.23f238869b2a7076bb5c.js
group-bar.34f238869b2a776bbd2.js
group-baz.43f23882b2a707623b2c.js
const chat = () => import(/* webpackChunkName: "chat" */ './chat.vue');

打包出来的结果为:chat.35f23886902a7076bb5c.js

当我们访问Foo 页面时,会只加载0.23f238869b2a7076bb5c.js。 此时访问Bar,动态引入group-bar.34f238869b2a776bbd2.js。 访问Baz,动态引入group-baz.43f23882b2a707623b2c.js。

资源加载过程为:

// srouces 访问Foo
0.23f238869b2a7076bb5c.js
// srouces 访问Foo后访问Bar
0.23f238869b2a7076bb5c.js
group-bar.34f238869b2a776bbd2.js
// srouces 访问Foo后访问Bar,再访问Baz
0.23f238869b2a7076bb5c.js
group-bar.34f238869b2a776bbd2.js
group-baz.43f23882b2a707623b2c.js

webpack是通过webpackJsonp实现动态引入的。

webpackJsonp动态引入本质上是JSONP,动态增删script,从script引入需要的文件。

参考资料: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import https://webpack.js.org/guides/code-splitting/

tybt commented 3 years ago

这只是异步加载 不是按需导入

FrankKai commented 3 years ago

这只是异步加载 不是按需导入

纠正过来了。按需加载应该是https://www.npmjs.com/package/babel-plugin-import 这样的