Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic imports with webpack #2

Open
chenbin92 opened this issue Jul 19, 2017 · 1 comment
Open

Dynamic imports with webpack #2

chenbin92 opened this issue Jul 19, 2017 · 1 comment

Comments

@chenbin92
Copy link
Owner

chenbin92 commented Jul 19, 2017

问题

使用 webpack 构建, 动态去加载 css 时,为什么使用 import() 会报错,使用 System.import() 可以?

$ webpack => 2.6.1

dynamic-load-css-file
dynamic-load-css-file-error


原因

require.ensure()

在 webpack1.x 中提供了 require.ensure() 方法动态导入文件,其函数签名如下:

require.ensure(dependencies, callback, chunkName)

简单示例:

require.ensure([], function(require) {
  const foo = require("./module");
});

注:require.ensure() 出自 CommonJS 中有一个 Modules/Async/A 规范,该规范定义了 require.ensure 语法。webpack 基于此基础,在打包的时候根据 require.ensure() 进行代码切分,并异步加载切分后的代码。

System.import()

ES6 Loader 规范定义了 System.import 方法,用于在运行时动态加载 ES6 模块,Webpack 把 System.import 作为拆分点;所以也可以通过 System.import() 方法实现动态引入,与 require.ensure()
不同的是 System.import() 返回的是一个 Promise,如:

System.import("./module")
  .then(module => console.log(module))
  .catch(err => console.log(Chunk loading failed))

import()

而在 webpack v2 (v2.1.0-beta.28) 废弃了 System.import() 方法,在 webpack v3 会完全删除System.import() 方法, 进而支持 ECMAScript State 3 草案阶段的原生 import() 方法来替代 System.import() 方法。

import("./module").then(module => {
    return module.default;
  }).catch(err => {
    console.log("Chunk loading failed");
  });

import

ES6 模块与 CommonJS 模块的差异 ,自行理解:

  • CommonJS

    • CommonJS 和 AMD 模块是运行时加载
    • CommonJS 模块就是对象,输入时必须查找对象属性
    • CommonJS 模块输出的是值的缓存,不存在动态更新
  • ES6 Module

    • ES6 模块是编译时加载
    • ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入
    • ES6 export 语句输出的接口,与其对应的值是动态绑定关系

由于 ES6 中的模块是编译时加载,import 命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行。所以,下面的代码会报错:

// 报错
if (x === 2) {
  import MyModual from './myModual';
}

注:在 Webpack 2(v2.2.0-rc.5) 中增加对 ES6 模块的原生支持。这意味着 Webpack 现在可以识别 import 和 export,不需要先把它们转换成 CommonJS 模块的格式。

require()、import、import()

require() 是 CommonJS 的实现,用于运行时加载,推荐阅读 require() 源码解读

import 是 ECMAScript 6 Module 的语法,import 是静态执行,推荐阅读 import 命令

import() 函数是 ECMAScript Stage 3 草案阶段的语法;用于完成动态加载即运行时加载,可以用在任何地方。import()函数 返回的是一个 Promise。类似于 CommonJs 的 require() ,区别主要是前者是异步加载,后者是同步加载,推荐阅读 proposal-dynamic-import

import() 的适用场景:

  • 按需加载
  • 条件加载
  • 动态的模块路径

结论

webpack 的 Dynamic Imports 实现主要是利用 ECMAScript的 import() 动态加载特性,而 import() 目前只是一个草案,如果需要用此方法,需要引入对应的转换器,如 babel-plugin-syntax-dynamic-import

模块化的实现对比

CommonJS AMD CMD ES6 Module
解决的问题 模块化开发 模块化开发 模块化开发 模块化开发
适用环境 服务端 浏览器 浏览器 浏览器/服务端
加载方式 同步加载/异步加载 异步加载 异步加载 同步加载/异步加载
模块定义 module.exports define(id?,dependencies?,factory) define(function(require,exports,module){ }) export
模块加载 require()/require.ensure() require([module], callback) require(module) import / import()
执行时机 提前执行 延迟执行
依赖原则 依赖前置 依赖就近
规范化的实现 require.js SeaJS
webpack 的支持 require.ensure() require() import / import()

参考

推荐阅读

写在最后

感谢 @Sean Larkin (webpack 核心开发者之一) 的指点,一眼看出了错误所在 ,那种感觉就像...你懂得...;值得记录一下 😀 😀 😀 😀 😀 😀

dynamic-imports-with-webpack-Chat-record

@ghost
Copy link

ghost commented Mar 6, 2018

Thx~,a awesome technical post. But I have some puzzles about the details of the above post. Take, for example,the last table(about the comparing of multiple modules) seems to be incorrect in some cases. As for CommonJS, the support for webpack include require.ensure and require. However the post said that the support for webpack include require with AMD. This is the point where the difference arises. Look forward to your reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant