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 相关文章推荐
一个不错的应用,用于提交获取文章内容,不推荐用
Mar 03 Javascript
jsp js鼠标移动到指定区域显示选项卡离开时隐藏示例
Jun 14 Javascript
jquery如何实现在加载完iframe的内容后再进行操作
Sep 10 Javascript
JavaScript实现的多个图片广告交替显示效果代码
Sep 04 Javascript
JS实现仿Windows经典风格的选项卡Tab切换代码
Oct 20 Javascript
Javascript基础之数组的使用
May 13 Javascript
jQuery基础知识点总结(DOM操作)
Jun 01 Javascript
angularjs实现文字上下无缝滚动特效代码
Sep 04 Javascript
ES6的Fetch异步请求的实现方法
Dec 07 Javascript
vue设计一个倒计时秒杀的组件详解
Apr 06 Javascript
Vue的transition-group与Virtual Dom Diff算法的使用
Dec 09 Javascript
vue实现简单全选和反选功能
Sep 15 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
雄兵连:天使彦天使彦为爱折翼,彦和炙心同时念动的誓言!
2020/03/02 国漫
基于mysql的论坛(3)
2006/10/09 PHP
PHP批量生成缩略图的代码
2008/07/19 PHP
php结合mysql与mysqli扩展处理事务的方法
2016/06/29 PHP
PHP实现表单提交时去除斜杠的方法
2016/12/26 PHP
基于yaf框架和uploadify插件,做的一个导入excel文件,查看并保存数据的功能
2017/01/24 PHP
PHP中使用CURL发送get/post请求上传图片批处理功能
2018/10/15 PHP
学习YUI.Ext 第七天--关于View&amp;JSONView
2007/03/10 Javascript
filemanage功能中用到的common.js
2007/04/08 Javascript
jQuery 取值、赋值的基本方法整理
2014/03/31 Javascript
jQuery遍历DOM节点操作之filter()方法详解
2016/04/14 Javascript
JS闭包、作用域链、垃圾回收、内存泄露相关知识小结
2016/05/16 Javascript
分享一个插件实现水珠自动下落效果
2016/06/01 Javascript
jQuery给div,Span, a ,button, radio 赋值与取值
2016/06/24 Javascript
JS获取input file绝对路径的方法(推荐)
2016/08/02 Javascript
Bootstrap下拉菜单样式
2017/02/07 Javascript
angular仿支付宝密码框输入效果
2017/03/25 Javascript
从零开始学习Node.js系列教程一:http get和post用法分析
2017/04/13 Javascript
nodejs判断文件、文件夹是否存在及删除的方法
2017/11/10 NodeJs
详解vue2.0+axios+mock+axios-mock+adapter实现登陆
2018/07/19 Javascript
深入浅析ng-bootstrap 组件集中 tabset 组件的实现分析
2019/07/19 Javascript
详解JavaScript 浮点数运算的精度问题
2019/07/23 Javascript
基于Element的组件改造的树形选择器(树形下拉框)
2020/02/27 Javascript
微信小程序实现拨打电话功能的示例代码
2020/06/28 Javascript
对于Python编程中一些重用与缩减的建议
2015/04/14 Python
Python列出一个文件夹及其子目录的所有文件
2016/06/30 Python
使用Python编写Prometheus监控的方法
2018/10/15 Python
关于Python核心框架tornado的异步协程的2种方法详解
2019/08/28 Python
Python3 使用selenium插件爬取苏宁商家联系电话
2019/12/23 Python
Python os模块常用方法和属性总结
2020/02/20 Python
pyqt5 QlistView列表显示的实现示例
2020/03/24 Python
基于Python下载网络图片方法汇总代码实例
2020/06/24 Python
HTML5+CSS3实现无插件拖拽上传图片(支持预览与批量)
2017/01/05 HTML / CSS
速比涛英国官网:Speedo英国
2019/07/15 全球购物
《赵州桥》教学反思
2014/02/17 职场文书
机电系毕业生求职信
2014/07/11 职场文书