用一个例子理解 ES6 的 export/import 用法

使用了一段时间的 Vue.js 以后,其中有大量的 ES6 的 export/import 用法,如

import axios from 'axios';
import Home from '../views/Home.vue';
export default {
  data() { .... }

因为目前对 ES6 的 export/import 用法是一知半解的,所以基本上都是基于 vue-cli 创建的项目上依葫芦画瓢,更是不太理解 vue 的项目是如何由 main.js -> App.vue 把所有的 router, views, 和 components 串联起来的。为了进一步理清 Vue.js 项目的初步运行机制,现在开始着手试图通过一个例子来尽可能多的理解 ES6 的 export/import 用法。

我不是标题党,所以不可能命名如:理解 export/import 一篇就够了,全网最全诸如此类的。且本人博客主要是记录自己学习过程的,而非为了招人眼球。所以尽力吧,在一个代码例子中尽可能多的展示 export/import 的用法,为不增加代码的复杂性,有些技巧在代码后进行解释。

首选推荐阅读 阮一峰 的 ECMAScript 6 入门中的 Module 的语法Module 的加载实现这两章。ES6 官方的名称是 ES2015, 规范一直在演进,每年一个,也就是说现在都 ES2020,我还要研究  ES6(ES2015)。

Edition Official name Date published
ES11 ES2020 Summer 2020
ES10 ES2019 Summer 2019
ES9 ES2018 June 2018
ES8 ES2017 June 2017
ES7 ES2016 June 2016
ES6 ES2015 June 2015

在 ES6 之前,JavaScript 世界只能以曲线的方式来支持模块化,出现了 CommonJS 和 AMD(Asynchronously Module Definition), 和它们的统一品 UMD(Universal Module Definition),CommonJS 用于服务端或桌面的 JS 应用。AMD 用于 Web 项目,它的常用实现有 RequireJS 和国内的一个  Sea.js。然而 ES6 的模块化(export/import) 出现把 CommonJS 和 AMD,还是 UMD 都扫入了历史文件堆。

还是马上开始吧,先来一个模块定义 utils.js 文件,先只写上一行来验证 import

一句话,什么都可以导出, 变量,常量,函数和类定义,当然广义上讲一切都是变量,但字面常量有些特殊。

现在来看怎么使用它,依照先前 <script src="utils.js"></script> 的惯性思维,我们写一个 index.html 文件

用浏览器打开文件,比如 file:///Users/yanbin/test/index.html, 在浏览器的 Inspect 中会看到一个错误

utils.js:3 Uncaught SyntaxError: Unexpected token 'export'

不能这么用,那么试下 import, 把 index.html 的内容改为

刷新 file:///Users/yanbin/test/index.html,新的错误

index.html:9 Uncaught SyntaxError: Cannot use import statement outside a module

google 了一下,说是 <script> 的 type 默认为 text/javascript,type 必须为  module 才能用 import 新语法,再次变 index.html 的内容为

再新,新问题又来了

index.html:1 Access to script at 'file:///Users/yanbin/test/utils.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.

import 是被允许了,但不能像以往 <script src="utils.js"></script> 那样引入本地的 js 文件,而需要借由 http, https 等协议来引入 js 文件。还需要启动一个 web 服务来使用 import,我们这里选择 nodejs 的 http-server, 可以用以下方式之一来安装

$ npm install --global http-server
$ brew install http-server                 # 或

/Users/yanbin/test 目录下运行 http-server 后,现在可以通过 http://localhost:8080/index.html 来打开页面了, 继续最后的 index.html 内容,在浏览器的 Inspect 中看到

utils module imported

模块 export/import 工作正常。

再接再厉,来一个更完整的 utils.js 的 index.html 演示实例,内容分别为

utils.js index.html

 

刷新 http://localhost:8080/index.html 后,在浏览器的 Console 中显示

utils module imported
localhost
function foo
function fn2
/apis
https
3.14
circle area: 12.56

为方便对应,输出内容添加到了 index.html 中的 output: 上。

一些说明:

  1. 一个模块可以 export 多个变量(常量,函数,类),一个模块只一个 export default { ... } 会更清晰些
  2. import 语句是编译时静态引入,任何给它安插运行时条件都无效,也因此早于 import 语句的模块中的代码是可运行的
  3. import 一个模块(js 文件) 多次只会加载模块一次,与 Java 等的 import 类型,不同于 C++ 的 #include
  4. default 是一个特殊别名,  方便于 import axios from 'axios' 这种用法。使用中注意 default 的有些特殊性
  5. import 的模块必须为 ./, ..//, import 'utils.js 会出错 Uncaught TypeError: Failed to resolve module specifier "utils.js". Relative references must start with either "/", "./", or "../".
  6. import 的模块默认不能省略扩展名,import './utils.js'import ./utils 会提示说类似找不到 http://localhost:8080/utils, 除非该 js 的文件名就是 utils, 或者像 Webpack 介入编译让 import 更智能,例如 import './utils' 自查打 ./utils.js 文件或 ./utils/index.js 文件

模块中导入别的模块再导出

一个模块中导入别的模块,再导出,可以简化

别的应用别名(含 default 别名)和 * 发挥

实现模块继承

继承层次其实也是维护着不同的命名空间,就是当前类中找不到的顺着继承链接往父类去找。对于非面向对象的语言,如 C++ 要实现两个结构的继承也可以这么做,把"父" 结构放在"子"结构的前部,即让"子", "父"结构共同起始指针。ES6 的模块继承让导入的第三方模块的东西再次作为自己的导出

ES6 的模块系统与 Python 的模块使用比较相近:

  1. 一个 js 或 py 文件就是一个模块
  2. from .. import ..,JS 是 import .. from ..
  3. import 时可以 as 别名
  4. Python  __all__ 指定的内容是 from .. import * 的所有,JS 的 import * from .. 为所有 export 的内容
  5. Python 中是 _ 私有内容不能被引入,JS 中只有用 export 明确的内容才能被引用

链接:

  1. Modules and import in ES6 for Python developers
  2. 各大浏览器对 ES6 的支持可以查看kangax.github.io/compat-table/es6/

类别: Web/JS. 标签: . 阅读(100). 订阅评论. TrackBack.
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x