目标
- 模块化系统演进
- ES6模块化实现
模块化概述
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
为什么使用模块化?
技术的诞生是为了解决某个问题,模块化也是。在js模块化诞生之前,开发者面临很多问题:随着前端的发展,web技术日趋成熟,js功能越来越多,代码量也越来越大。之前一个项目通常各个页面公用一个js,但是js逐渐拆分,项目中引入的js越来越多:
模块化系统演进
上面的js引入造成了问题:
1)全局变量污染:各个文件的变量都是挂载到window对象上,污染全局变量。
2)变量重名:不同文件中的变量如果重名,后面的会覆盖前面的,造成程序运行错误。
3)文件依赖顺序:多个文件之间存在依赖关系,需要保证一定加载顺序问题严重。
4)在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪
后来出来了CommonJS AMD CMD UMD(建议了解)
https://www.jianshu.com/p/c33c659b2a1e
ES6模块化实现
1、ES6模块化概述
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
2、ES6模块化特点
1)静态加载模块,效率比CommonJS 模块的加载方式高
2)ES6 模块是编译时加载,使得静态分析成为可能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
3)不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点
4)将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。
5)不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。
6)支持严格模式
3、export和import命令
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能
在ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出)。
然后在需要引用上述变量、函数、对象的文件里用import的形式进行引入:
a-export--->
b-import <--
1)变量模块化
** 输出两种方式(自选)**
param01_export.js
export let userName="wangqj";
export const PI=3.1415926;
简写形式
let userName="wangqj";
const PI=3.1415926;
export {userName,PI};
** 输入(注意type="module"):**
param01-import.html
运行结果:
上面代码的js文件,保存了一些基本信息。ES6 将其视为一个模块,里面用export命令对外部输出了几个变量。
2)函数模块化
输出fun_export.js :
第一种写法:
//fun_export.js
export function cheng(x, y) {
return x * y;
}
export function jia(x, y) {
return x + y;
}
export function jian(x, y) {
return x - y;
}
第二种 简写形式:
function cheng(x, y) {
return x * y;
}
function jia(x, y) {
return x + y;
}
function jian(x, y) {
return x - y;
}
export {cheng,jia,jian};
第三种 export时也可以用as定义别名,重命名后,import时需要用新名引入:
function cheng(x, y) {
return x * y;
}
function jia(x, y) {
return x + y;
}
function jian(x, y) {
return x - y;
}
export {cheng,jia as add,jian};
输入fun_import.html:
3)对象模块化
输出:
export class person{
constructor(username,password) {
this.username=username;
this.password=password;
}
toString(){
return this.username+' '+this.password;
}
}
输入:
其他需要说明的地方:
1)import 时可以定义别名,用别名来引用,重命名后,v2可以用不同的名字输出两次。
import { lastName as surname } from './profile.js';
2)import命令具有提升效果,会提升到整个模块的头部,首先执行
foo();
import { foo } from ‘./my_module.js';
上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。
3)import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构
4)import语句是 Singleton 模式 无论代码import了几次,都只执行一次
4、模块的整体加载
除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。
整体加载.js
let username="wang.qj";
let password="123456";
function login(username,password){
return "登录成功";
}
export{username,password,login} ;
整体加载 .html
5、export default 命令(掌握)
使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
default01.js
const str = "hello es6";
export default str;//此处不用加花括号
default01.html
注意:
1)原本直接export str外部是无法识别的,加上default就可以了.但是一个文件内最多只能有一个export default。 其实此处相当于为str变量值起了一个系统默认的变量名default,自然default只能有一个值,所以一个文件内不能有多个export default。
本质上,default01.js文件的export default输出一个叫做default的变量,然后系统允许你为它取任意名字。所以可以为import的模块起任何变量名,且不需要用大括号
2)书写时注意:两侧都不需要{}了
再来两个例子:
export default function
default02.js
export default function (x,y){
return x+y;
}
default02.html
export default class##
default03.js
export default class person{
constructor(username,password) {
this.username=username;
this.password=password;
}
toString(){
return this.username+' '+this.password;
}
}
default03.html
再来一个
export default{
username:"wang.qj",
password:"123456",
login:(username,password)=>{
return "success"+username;
}
}