ES6新特性:使用export和import实现模块化详解


Posted in Javascript onJuly 31, 2017

在ES6前, 前端就使用RequireJS或者seaJS实现模块化, requireJS是基于AMD规范的模块化库,  而像seaJS是基于CMD规范的模块化库,  两者都是为了为了推广前端模块化的工具, 更多有关AMD和CMD的区别, 后面参考给了几个链接;

现在ES6自带了模块化, 也是JS第一次支持module, 在很久以后 ,我们可以直接作用import和export在浏览器中导入和导出各个模块了, 一个js文件代表一个js模块;

现代浏览器对模块(module)支持程度不同, 目前都是使用babelJS, 或者Traceur把ES6代码转化为兼容ES5版本的js代码;

ES6的模块化的基本规则或特点:

ES6的模块化的基本规则或特点, 欢迎补充:

1:每一个模块只加载一次, 每一个JS只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象;

2:每一个模块内声明的变量都是局部变量, 不会污染全局作用域;

3:模块内部的变量或者函数可以通过export导出;

4:一个模块可以导入别的模块

运行下面代码

//lib.js
//导出常量
export const sqrt = Math.sqrt;
//导出函数
export function square(x) {
  return x * x;
}
//导出函数
export function diag(x, y) {
  return sqrt(square(x) + square(y));
}

//main.js
import { square, diag } from './lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

ES6新特性:使用export和import实现模块化详解

下面列出几种import和export的基本语法:

第一种导出的方式:

在lib.js文件中, 使用 export{接口} 导出接口, 大括号中的接口名字为上面定义的变量, import和export是对应的;

运行下面代码

//lib.js 文件
let bar = "stringBar";
let foo = "stringFoo";
let fn0 = function() {
  console.log("fn0");
};
let fn1 = function() {
  console.log("fn1");
};
export{ bar , foo, fn0, fn1}

//main.js文件
import {bar,foo, fn0, fn1} from "./lib";
console.log(bar+"_"+foo);
fn0();
fn1();

ES6新特性:使用export和import实现模块化详解

第二种导出的方式:

在export接口的时候, 我们可以使用 XX as YY, 把导出的接口名字改了, 比如: closureFn as sayingFn, 把这些接口名字改成不看文档就知道干什么的:

运行下面代码

//lib.js文件
let fn0 = function() {
  console.log("fn0");
};
let obj0 = {}
export { fn0 as foo, obj0 as bar};

//main.js文件
import {foo, bar} from "./lib";
foo();
console.log(bar);

ES6新特性:使用export和import实现模块化详解

第三种导出的方式:

这种方式是直接在export的地方定义导出的函数,或者变量:

运行下面代码

//lib.js文件
export let foo = ()=> {console.log("fnFoo") ;return "foo"},bar = "stringBar";

//main.js文件
import {foo, bar} from "./lib";
console.log(foo());
console.log(bar);

ES6新特性:使用export和import实现模块化详解

第四种导出的方式:

这种导出的方式不需要知道变量的名字, 相当于是匿名的, 直接把开发的接口给export;

如果一个js模块文件就只有一个功能, 那么就可以使用export default导出;

运行下面代码

//lib.js
export default "string";

//main.js
import defaultString from "./lib";
console.log(defaultString);

ES6新特性:使用export和import实现模块化详解

第五种导出方式:

export也能默认导出函数, 在import的时候, 名字随便写, 因为每一个模块的默认接口就一个:

运行下面代码

//lib.js
let fn = () => "string";
export {fn as default};

//main.js
import defaultFn from "./lib";
console.log(defaultFn());

第六种导出方式:

使用通配符*  ,重新导出其他模块的接口 (其实就是转载文章, 然后不注明出处啦);

运行下面代码

//lib.js
export * from "./other";
//如果只想导出部分接口, 只要把接口名字列出来
//export {foo,fnFoo} from "./other";

//other.js
export let foo = "stringFoo", fnFoo = function() {console.log("fnFoo")};

//main.js
import {foo, fnFoo} from "./lib";
console.log(foo);
console.log(fnFoo());

ES6新特性:使用export和import实现模块化详解

其他:ES6的import和export提供相当多导入以及导出的语法;

在import的时候可以使用通配符*导入外部的模块:

运行下面代码

import * as obj from "./lib";
console.log(obj);

ES6导入的模块都是属于引用:

每一个导入的js模块都是活的, 每一次访问该模块的变量或者函数都是最新的, 这个是原生ES6模块 与AMD和CMD的区别之一,以下代码修改自http://exploringjs.com/es6/ch_modules.html#_imports-are-read-only-views-on-exports

运行下面代码

//lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}
export function setCounter(value) {
  counter = value;
}


//main.js
import { counter, incCounter ,setCounter} from './lib';

// The imported value `counter` is live
console.log(counter); // 3
incCounter();
console.log(counter); // 4
setCounter(0);
console.log(counter); // 0

在main.js中, counter一直指向lib.js中的局部变量counter, 按照JS的尿性, 像数字或者字符串类型或者布尔值的原始值要被复制, 而不是赋址;

循环依赖的问题:

NodeJS的循环依赖是这么处理的:打开;

循环依赖是JS模块化带来的问题, 在浏览器端, 使用RequireJS测试模块化, 比如有一个文件file0.js依赖于file1.js, 而file1.js又依赖于file0.js, 那么file0.js和file1.js到底谁先执行?

运行下面代码

//index.html
<!DOCTYPE html>
<html>
<head>
  <title></title>
  <meta charset="utf-8"/>
</head>
<body>

<script data-main="cyclic" src="//cdn.bootcss.com/require.js/2.2.0/require.min.js"></script>
<script>
//cyclic.js
require(["file0"], function(file0) {
  console.log(file0)
})

//file0.js
define(["file1"], function(file1) {
  console.log(file1)
  return {
    file0 : "file0"
  }
})

//file1.js
define(["file0"], function(file0) {
  console.log(file0);
  return {
    file1 : "file1"
  }
})
</script>
</body>
</html>

在控制台的依次输出为:

运行下面代码

undefined
Object { file1: "file1" }
Object { file0: "file0" }

在执行file1.js的时候file0.js还没执行完, 所以输出了undefined, 这种输出结果和NodeJS输出的情况是一样的;

然后我又使用了司徒大神的mass-framework框架试了一下, 司徒大神的框架直接提示我: "模块与之前的某些模块存在循环依赖", 这样还比较好点, requireJS对于循环依赖是直接执行循环依赖的模块, 会导致在开发的时候给自己挖坑....;

接下来我又在babel-node下进行测试:下面是几个测试,可以无视:

我使用ES6的模块试一试, 只要每一个模块被引用, 无论模块是否执行完毕, 该模块的export已经被导出了, 如果导出的是函数:

运行下面代码 

//cyclic.js
import fn0 from "./file0";
fn0();

//file0.js
import fn1 from "./file1";
fn1();
console.log("file0.js runs");
export default function() {console.log("file0 export runs")}

//file1.js
import fn0 from "./file0";
fn0();
console.log("file1.js runs");
export default function() {console.log("file1 export runs")}

ES6新特性:使用export和import实现模块化详解

如果导出的是字符串:

运行下面代码

//cyclic.js
import str from "./file0";
console.log(str);

//file0.js
import str1 from "./file1";
console.log(str1)
console.log("file0.js runs");
export default "str0";

//file1.js
import str0 from "./file0";
console.log(str0)
console.log("file1.js runs");
export default "str1";

ES6新特性:使用export和import实现模块化详解

如果导出的是对象:

那么第一行会先输出一个初始值{},在最后等待file0.js和file1.js执行完毕以后, 才输出file0.js导出的对象;

如果是数组:

那么第一行会输出一个被静态分析过的初始值undefined,在最后等待file0.js和file1.js执行完毕以后, 才输出file0.js导出的对象;

如果是布尔值:

那么第一行会输出一个被静态分析过的初始值undefined,在最后等待file0.js和file1.js执行完毕以后, 才输出file0.js导出的布尔值;

为什么会这样呢? 我好像在这边找到了答案:http://exploringjs.com/es6/ch_modules.html#_modules ,ES6的import和export被提前到js的最顶层, 在函数或者对象,或者基本值被导出去的时候提前被静态分析过,参考:http://www.ecma-international.org/ecma-262/6.0/#sec-parsemodule , http://www.ecma-international.org/ecma-262/6.0/#sec-toplevelmoduleevaluationjob

结论:用ES6的export导出数据接口的时候, 最好统一用函数, 避免在循环依赖的时候, 因为JS会把不同类型的对象静态解析成不同的初始值;

浏览器兼容:

  1. chrome浏览器目前不支持import,和export;
  2. 火狐的支持也有限, 比chrome好;
  3. 我都用babel;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery插件制作 手风琴Panel效果实现
Aug 17 Javascript
iframe子页面获取父页面元素的方法
Nov 05 Javascript
Javascript动态引用CSS文件的2种方法介绍
Jun 06 Javascript
javascript定义变量时加var与不加var的区别
Dec 22 Javascript
jQuery的几个我们必须了解的特点
May 03 Javascript
使用Node.js处理前端代码文件的编码问题
Feb 16 Javascript
JS原型链 详解及示例代码
Sep 06 Javascript
jQuery实现的瀑布流加载效果示例
Sep 13 Javascript
JS打开摄像头并截图上传示例
Feb 18 Javascript
jQuery+ThinkPHP+Ajax实现即时消息提醒功能实例代码
Mar 21 jQuery
微信小程序实现自定义picker选择器弹窗内容
May 26 Javascript
Vue使用Clipboard.JS在h5页面中复制内容实例详解
Sep 03 Javascript
node koa2实现上传图片并且同步上传到七牛云存储
Jul 31 #Javascript
Angular.js初始化之ng-app的自动绑定与手动绑定详解
Jul 31 #Javascript
详解React中的组件通信问题
Jul 31 #Javascript
Angular.js前台传list数组由后台spring MVC接收数组示例代码
Jul 31 #Javascript
Angular.js中数组操作的方法教程
Jul 31 #Javascript
BootStrap导航栏问题记录
Jul 31 #Javascript
Angular4 中内置指令的基本用法
Jul 31 #Javascript
You might like
PHP数组循环操作详细介绍 附实例代码
2013/02/03 PHP
百度工程师讲PHP函数的实现原理及性能分析(一)
2015/05/13 PHP
PHP利用APC模块实现大文件上传进度条的方法
2015/10/29 PHP
Zend Framework实现将session存储在memcache中的方法
2016/03/22 PHP
使用PHPExcel实现数据批量导出为excel表格的方法(必看)
2017/06/09 PHP
php实现的redis缓存类定义与使用方法示例
2017/08/09 PHP
浅谈laravel框架与thinkPHP框架的区别
2019/10/23 PHP
JavaScript 原型学习总结
2010/10/29 Javascript
javascript间隔刷新的简单实例
2013/11/14 Javascript
ie7+背景透明文字不透明超级简单的实现方法
2014/01/17 Javascript
一个网页标题title的闪动提示效果实现思路
2014/03/22 Javascript
javascript原始值和对象引用实例分析
2015/04/25 Javascript
使用console进行性能测试
2015/04/27 Javascript
jQuery模拟原生态App上拉刷新下拉加载更多页面及原理
2015/08/10 Javascript
javascript瀑布流布局实现方法详解
2016/02/17 Javascript
Javascript对象字面量的理解
2016/06/22 Javascript
JS获取字符串实际长度(包含汉字)的简单方法
2016/08/11 Javascript
浅谈vue2 单页面如何设置网页title
2017/11/08 Javascript
解决vue打包之后静态资源图片失效的问题
2018/02/21 Javascript
vue2.0 可折叠列表 v-for循环展示的实例
2018/09/07 Javascript
js字符串倒序的实例代码
2018/11/30 Javascript
vue 自定义右键样式的实例代码
2019/11/06 Javascript
小程序接口的promise化的实现方法
2019/12/11 Javascript
详解element-ui 表单校验 Rules 配置 常用黑科技
2020/07/11 Javascript
Python使用Socket(Https)Post登录百度的实现代码
2012/05/18 Python
Python利用pyHook实现监听用户鼠标与键盘事件
2014/08/21 Python
Python3.5.3下配置opencv3.2.0的操作方法
2018/04/02 Python
python学生管理系统的实现
2020/04/05 Python
全球酒店比价网:HotelsCombined
2017/06/20 全球购物
贝尔帐篷精品店:Bell Tent Boutique
2019/06/12 全球购物
材料员岗位职责
2014/03/13 职场文书
信息员培训方案
2014/06/12 职场文书
物理教育专业求职信
2014/06/25 职场文书
党员个人对照检查材料
2014/10/01 职场文书
二年级作文之动物作文
2019/11/13 职场文书
JavaScript 实现页面滚动动画
2021/04/24 Javascript