使用了一段时间的 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
1 2 3 |
console.info('utils module imported') export abc = 123 |
一句话,什么都可以导出, 变量,常量,函数和类定义,当然广义上讲一切都是变量,但字面常量有些特殊。
现在来看怎么使用它,依照先前 <script src="utils.js"></script>
的惯性思维,我们写一个 index.html 文件
1 |
<script src="utils.js"></script> |
用浏览器打开文件,比如 file:///Users/yanbin/test/index.html, 在浏览器的 Inspect 中会看到一个错误
utils.js:3 Uncaught SyntaxError: Unexpected token 'export'
不能这么用,那么试下 import
, 把 index.html 的内容改为
1 2 3 |
<script> import utils from './utils.js' </script> |
刷新 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 的内容为
1 2 3 |
<script type="module"> import utils from './utils.js' </script> |
再来,新问题又来了
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:
上。
一些说明:
- 一个模块可以 export 多个变量(常量,函数,类),一个模块只一个 export default { ... } 会更清晰些
- import 语句是编译时静态引入,任何给它安插运行时条件都无效,也因此早于
import
语句的模块中的代码是可运行的 - import 一个模块(js 文件) 多次只会加载模块一次,与 Java 等的 import 类型,不同于 C++ 的 #include
default
是一个特殊别名, 方便于import axios from 'axios'
这种用法。使用中注意default
的有些特殊性- import 的模块必须为
./
,../
或/
,import 'utils.js
会出错 Uncaught TypeError: Failed to resolve module specifier "utils.js". Relative references must start with either "/", "./", or "../". - import 的模块默认不能省略扩展名,
import './utils.js'
,import ./utils
会提示说类似找不到http://localhost:8080/utils
, 除非该 js 的文件名就是utils
, 或者像 Webpack 介入编译让import
更智能,例如import './utils'
自动查找./utils.js
文件或./utils/index.js
文件
模块中导入别的模块再导出
一个模块中导入别的模块,再导出,可以简化
1 2 3 4 5 |
import { foo, bar } from 'third_party_module' export { foo, bar } // 上面两行简化为一个 export export ( foo, bar } from 'third_party_module' |
别的应用别名(含 default 别名)和 *
发挥
1 2 3 4 5 |
export { foo as myFoo } from 'my_module' export * from 'my_module' export { default } from 'foo' export { es6 as default } from './someModule' export * as ns from 'my_module' // ES2020 |
实现模块继承
继承层次其实也是维护着不同的命名空间,就是当前类中找不到的顺着继承链接往父类去找。对于非面向对象的语言,如 C++ 要实现两个结构的继承也可以这么做,把"父" 结构放在"子"结构的前部,即让"子", "父"结构共同起始指针。ES6 的模块继承让导入的第三方模块的东西再次作为自己的导出
1 2 3 4 5 6 |
export * from 'circle' export var e = 2.71828 export default function(x) { return Math.exp(x) } |
ES6 的模块系统与 Python 的模块使用比较相近:
- 一个 js 或 py 文件就是一个模块
- 有
from .. import ..
,JS 是import .. from ..
- import 时可以
as
别名 - Python
__all__
指定的内容是from .. import *
的所有,JS 的import * from ..
为所有 export 的内容 - Python 中是
_
私有内容不能被引入,JS 中只有用export
明确的内容才能被引用
链接:
- Modules and import in ES6 for Python developers
- 各大浏览器对 ES6 的支持可以查看kangax.github.io/compat-table/es6/
本文链接 https://yanbin.blog/example-understand-es6-export-import/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。