【前端】JS模块化解析-ESModule_es module
目录
- 基本介绍
- 基本使用
- 导入导出方式3种
- 结合使用
- default用法
- import函数
- ES Module的解析流程
-
- 导入的变量无法,导入方无法修改
- 导出的变量,只能由导出方进行修改
- 如果确实想在导入这边对导出的值进行修改,可以自定义set回调函数
- CommonJS和ESModule相互引用
-
- 实验一下
基本介绍
- JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:
CommonJS
、AMD
、CMD
等
所以在ES推出自己的模块化系统时,大家也是兴奋异常。 - ES Module和CommonJS的模块化有一些不同之处
- 一方面它使用了import和export关键字:
- 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式
- ES Module模块采用export和import关键字来实现模块化
- export负责将模块内的内容导出,
- import负责从其他模块导入内容;
- 了解:采用ES Module将自动采用严格模式:use strict
基本使用
// foo.jsexport const name = \"why\"export const age = \"18\"
// main.jsimport {name,age} from \"./foo.js\"console.log(name)console.log(age)
<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title></title></head><body> <script src=\"main.js\" type=\"module\"></script></body></html>
让模块化生效的两个前提条件
- script标签要加上 type=“module”
- index.html的访问必须是以服务器的方式访问。也就是访问协议必须是
http
或者https
直接本地文件打开file://
这个协议是不支持的
导入导出方式3种
// foo.js// 导出方式// 1. 第一种方式: export 声明语句export const name = \"why\"export const age = \"18\"export function foo(){console.log(\"foo function\")}export class Person {}
// 2. 第二种: export 导出 和 声明分开const name = \"why\"const age = \"18\"function foo(){console.log(\"foo function\")}export {name,age,foo}
// 3. 第三种方式:第二种导出时起别名const name = \"why\"const age = \"18\"function foo(){console.log(\"foo function\")}export { name as fName, age as fAge, foo as fFoo}
导入方式
// 导入方式// 1.导入方式1:普通导入import {name,age,foo,Person} from \"./foo.js\"// 2.导入方式二:起别名import {name as fName,age as fAge,foo as fFoo} from \"./foo.js\"// 3.导入方式三:将所有哦的内容放到一个标识符中import * as foo from \"./foo.js\"console.log(foo.name)console.log(foo.age)console.log(name)console.log(age)
结合使用
当我们某个目录下有很多js文件,导出了很多工具方法如下
// utils/math.jsfunction add(a,b){return a+b;}function sub(a,b){return a-b}export {add,sub}
// utils/format.jsfunction timeFormat(){return \"2222-12-12\"}function priceFormat(){return \"222.22\"}export {timeFormat,priceFormat}
然后在main.js中引入这些模块的函数,代码会变的很麻烦如下
// main.jsimport {add,sub} from \"./utils/math.js\"import {timeFormat,priceFormat} from \'./format.js\'
因此一般在utils
目录下会统一定义一个index.js
用来将该目录下的所有需要导出的变量进行一个统一导出。如下
这样mian.js
就可以只导入utils/index.js
即可
// utils/index.js// 导出方式一import {add,sub} from \'./math.js\'import {timeFormat,priceFormat} from \'./format.js\'export {add,sub,timeFormat,priceFormat}
导出方式一还是显得太复杂,可以结合使用
// utils/index.js// 导出方式二:export {add,sub} from \'./math.js\'export {timeFormat,priceFormat} from \'./format.js\'// 如果要全部导出可如下写法// 导出方式三:export * from \'./math.js\'export * from \'./format.js\'
default用法
当写的某一个变量,是非常常用的时候,可以使用默认导出。默认导出的方式如下
// foo.jsconst name = \"zxh\"const age = 18 const foo = \'foo value\'// 1.默认导出的方式一:export {name,age,foo as default}// 2.默认导出的方式二:常见export default foo// 注意默认导出只能有一个
main.js
在导入默认导出的变量时 如下使用
// 不用进行结构,可自定义接收的变量名 实际上是将 foo.js中的 foo赋值到aaa上import aaa from \'./foo.js\'console.log(aaa)
import函数
如下代码,在执行的时候是同步执行的,
首先浏览会先将要导入的文件下载下来,进行解析,然后才会执行后续的代码。
import {name,age,foo} from \'./foo.js\'console.log(\"导入的代码没有解析完之前,后续的代码是不会运行的\")
如果希望异步执行:比如页面初始化的时候并不需要用到导出的函数,只是在特定时候才需要使用。那么就可以使用import函数来进行异步执行
import(\"./foo.js\").then(res=>{console.log(\"res:\",res.name)})console.log(\"main.js 执行成功\")
// 查看控制台输出,可以看出是异步执行main.js 执行成功res:zxh
ES Module的解析流程
具体流程省略
导入的变量无法,导入方无法修改
需要重点提醒一下:导出的变量,是不允许在导入端被修改的
如下
// foo.jsexport let name = \"zifeiyu\"
import {name} from \"./foo.js\"// 这里修改是会报错的name = 333
// 控制台报错Uncaught TypeError: Assignment to constant variable. at main.js:3:6
导出的变量,只能由导出方进行修改
// foo.jsexport let name = \"zifeiyu\"setTimeout(() => {name = \"Kobe\"},100)
// main.jsimport {name} from \"./foo.js\"console.log(name)setTimeout(()=>{console.log(name)},2000)
可以看到控制台输出,说明导出方可以修改这个值
zifeiyuKobe
如果确实想在导入这边对导出的值进行修改,可以自定义set回调函数
// foo.jsexport let name = \"zxh\"export function setName(a){name = a}
import {name,setName} from \"./foo.js\"console.log(name)setName(3333333)console.log(name)
CommonJS和ESModule相互引用
解释一下:
- 模块A通过commonJS进行导出,然后模块B通过ESModule的方式进行导入。
- 模块C通过ESModule的方式进行导出,然后模块B通过commonJS的方式进行导入。
能否实现呢?
得分情况
- 浏览器:不能实现,因为默认不支持
CommonJS
- node环境下:分情况
- 之前的node版本,只支持
CommonJS
- 现在node版本逐渐在转用
ESModule
- 之前的node版本,只支持
- 正常开发环境
webpack
: 可以使用
实验一下
# 在一个干净的目录下# 1.通过npm帮我们初始化一下项目,也就是生成 package.jsonnpm init# 2.要使用webpack 需要安装 webpack 和 webpack-clinpm install webpack webpack-cli -D# 3.新建目录src,因为webpack默认的打包入库是 src/index.js
编写几个js进行互相引用
这边案例是打算
- foo.js 通过commonjs导出
- bar.js 通过ESmodule导出
- index.js
- 通过ESmodule 导入
foo.js
- 通过commonjs导入
bar.js
- 通过ESmodule 导入
代码如下
// src/foo.jsconst fooName = \"foo\"const fooAge = 18// commonjs导出module.exports = {fooName,fooAge}
// src/bar.jsconst barName = \"foo\"const barAge = 18// esmodule导出export{barName,barAge}
// src/index.jses moduleimport { fooName, fooAge } from \"./foo\"console.log(fooName, fooAge)// commonjs 导入const {barName,barAge} = require(\'./bar\')console.log(barName, barAge)
# 打包 会在当前命令所在目录下 创建一个dist文件夹 dist文件夹下会有一个main.jsnpx webpack
在当前目录下创建index.js
对打包后的js进行引用即可
<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title></title></head><body><script src=\"dist/main.js\"></script></body></html>
浏览器访问可以看到正常输出,说明互相引用是可以的。
具体原理还得看main.js
webpack打包出来的文件