前端模块化
认识模块化
什么是前端模块化?
- 模块化的开发方式可以提高代码复用率,方便进行代码的管理。通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。
模块化演变过程
阶段一:文件划分
//model_1.js
//将每一个功能单独放置在一个js文件中,通过script引入使用
val msg1 = '阶段一:文件划分'
//弊端:全局污染
阶段二:命名空间
//model_2.js
//在第一阶段的基础上面,将每一个模块内部的内容统一放置到一个变量下面
var Model_2 = {
msg2:'阶段二:命名空间'
}
//弊端:仍然没有私有空间,依然可以被外部访问修改
阶段三:立即执行函数包裹
//model_3.js
//使用立即执行函数进行包裹,利用函数作用域,使其具有私有空间(私有成员)
(function(){
var msg3 = '阶段三:立即执行函数'//私有变量
function fn3 (){
console.log(msg3)
}
window.modele3 = {
fn3:fn3//挂载暴露函数
}
}())
上述方法外部调用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="前端模块化演变过程" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <script src="阶段一_文件划分.js"></script> <script src="阶段二_命名空间.js"></script> <script src="阶段三_立即执行函数包裹.js"></script> <script> console.log(msg1) console.log(Model_2.msg2) window.modele3.fn3() </script> <body> </body> </html>
上述方法弊端:维护困难
模块化规范探索
CommonJS规范
Node.js是commonJS规范的主要实践者
弊端:因为CommonJS是同步加载的,不适用于浏览器
AMD
异步模块定义规范
弊端:使用起来相对复杂
CMD
弊端:使用起来相对复杂,与AMD类似
模块化标准规范 ES-Modules
ES Modules 特性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="ES Models" content="width=device-width, initial-scale=1.0">
<title>ESMt特性</title>
</head>
<body>
<h2>ESMt特性</h2>
<ul>
<li>特性一:自动采用严格模式,忽略'use strict'</li>
<li>特性二:每个ESM模块都是单独的私有作用域</li>
<li>特性三:ESM是通过CORS去请求外部JS模块的</li>
<li>特性四:ESM的script标签会延迟执行脚本,页面渲染完成后再加载</li>
</ul>
<script type="module">
//原生html中通过给script添加 type="module" 指定其为ESM模块
</script>
</body>
</html>
导入导出
导出
//app.js
//--------------------------------------------1
export var msg1 = '分别导出变量'
export var fn1 = function () {
return '分别导出方法'
}
//--------------------------------------------2
var msg2 = '统一导出变量'
var fn2 = function () {
return '统一导出方法'
}
//--------------------------------------------3
var msg3 = '统一导出变量,并重命名'
var fn3 = function () {
return '统一导出变量,并使用关键字default指定其为默认导出'
}
//--------------------------------------------4
//统一在末尾导出
export {
msg2,//统一导出变量
fn2,//统一导出方法
msg3 as Tmsg3,//统一导出变量,并重命名 --使用as重命名
fn3 as default//使用关键字default指定其为默认导出(被指定默认导出的变量接收时必须重命名)
}
//--------------------------------------------5
//export default fn3//使用export default默认(统一)导出
导入
//index.js
//----------------------------------------------------1
//分别导入
import {
msg1,
fn1,
msg2,
fn2,
Tmsg3 as TTmsg3,//使用as重命名
default as Dfn3//默认导出,分别导入时必须重命名
} from "./app.js";
//----------------------------------------------------2
//统一(默认)导入
import app from "./app.js"
//----------------------------------------------------3
//使用*导出所有成员
import * from './app.js'
//----------------------------------------------------4
//使用*获取导出的全部成员,并重命名接收
import * as Mapp from './app.js'
//----------------------------------------------------5
//只执行模块,而不导入其内容的写法
import {} from "./app.js"//方法一:空{}
import "./app.js"//方法二:只使用import
//----------------------------------------------------6
//动态加载-异步加载
import('./app.js')
//其是Promise风格
import('./app.js').then(M=>{
console.log(M)//其返回模块
})
//----------------------------------------------------7
//混合导入:同时获取分别默认暴露和分别(具名)暴露
import app,{msg1,fn1} from './app.js'//,号前获取默认暴露(导出),,后获取分别暴露
注意
导入导出中的{}
var msg = 'msg-app-2'
//分别暴露(导出)中的{}并非对象,其内部变量也不是对象字面量简写,这只是导出特有的写法
export {
msg
}
//统一暴露 其后{}则是一个对象
export default {
msg
}
//导入时的{}也并非解构,只是导入的特定写法
import {msg} from "./app2.js"
只读性
//导出时实际是对内存的引用,而非对对象的拷贝
//另外,导入的内容是只读的,不可修改
import {msg} from "./app2.js"
导入的完整性
//导入完整性
//导入必须是完整路径,项目中可省略是因为在打包工具中进行了处理
import {msg1} from "./app"//完整性一:不能省略后缀
import {msg2} from "app.js"//完整性二:相对路径中./不可省略,如果省略则可能会被误认为在加载第三方模块
import {msg3} from "/a1"//完整性三:如果要加载a1文件夹下的index.js文件,index.js不可省略
导入后立马导出
//导入后立马导出
export {msg1,fn1} from './app.js' //这样在本文件中无法使用其成员
浏览器兼容
。。。。。。
Polyfill:一个Js兼容适应的小玩具(补丁),闻一闻
nodeJs中使用ES-Modules
node8.5过后可以在nodeJs中实验性使用ES-Modules