Module 模块化
Module 模块化
1. 模块起步
1-1 模块化规范
AMD
—— 异步模块定义规范,最初由require.js
库实现,用于浏览器的模块系统CommonJS
—— 为Node.js
服务器创建的模块系统UMD
—— 通用模块定义规范,作为通用的模块系统CMD
—— 阿里SeaJS
,用于浏览器的模块系统ES6 Module
——JavaScript
模块浏览器和服务器通用的模块系统
规范 | 描述 |
---|---|
CommonJS
|
是服务器模块的规范,加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作 |
AMD
|
规范加载模块是异步的,并允许函数回调,不必等到所有模块都加载完成,后续操作可以正常执行,只要模块作为依赖时,就会加载并初始化 |
CMD
|
CMD 规范和 AMD 类似通用模块定义,模块作为依赖且被引用时才会初始化,否则只会加载
|
UMD
|
通用模块定义,UMD 是 AMD 和 CommonJS 的结合,实现跨平台的解决方案,先判断是否支持 Node.js 的模块(exports )是否存在,存在则使用 Node.js 模块模式。再判断是否支持 AMD (define 是否存在),存在则使用 AMD 方式加载模块
|
1-2 什么是模块?
ES Module
是 ES6
中提出的一个新的模块加载方式,完全可以取代 commonJs
和 AMD
两种模块加载方式,成为服务端和浏览器端模块加载的解决方案
ES6
模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量,所以在资源加载速度和静态分析方面效率更高
export
—— 从当前模块外部访问的变量和函数import
—— 允许从其他模块导入功能
通过使用
<script type="module">
特性告诉浏览器,脚本应该被当作模块module
来对待
<!-- 添加type="module"让浏览器以模块解析脚本 -->
<script type="module">// import 从其它模块引入功能import { module } from './module.js'console.log(module)
</script>
// module.js
let module = 'ES Modules'
// export 从当前模块导出变量与方法
export { module }
1-3 模块严格模式
在模块下始终在严格模式下运行,始终使用 use strict
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 全局
this
执行undefined
<script>// 不在模块下允许 this指向windowconsole.log(this); // window
</script>
<script type="module">console.log(this); // defined
</script>
1-4 模块作用域
每个模块都有自己的作用域,一个模块中的作用域变量和函数在其他脚本中是不可访问的
对于模块,我们使用导入/导出而不是依赖全局变量
<script src="./normal.js"></script><!-- 模块A -->
<script type="module">let module = 'module js'
</script><!-- 模块B -->
<script type="module">// 对于模块只能使用导入导出 不能依赖全局变量console.log(module)//Uncaught ReferenceError module is not defined
</script><script>// 不在模块下可访问外部组用于console.log(normal)console.log(module); //Uncaught ReferenceError module is not defined
</script>
// normal.js
let normal = 'normal js'
1-5 模块解析
模块代码仅在第一次导入时被解析,当导出的对象或者其它类型数据时被修改了,后面再导入时获取的是修改后的新值
import.meta
对象包含关于当前模块的 url
信息,在浏览器环境中,它包含当前脚本的 URL
,或者如果它是在 HTML
中的话,则包含当前页面的 URL
<script type="module">// import.meta获取当前脚本urlconsole.log(import.meta.url)// 模块代码仅在第一次导入时被解析// a模块导入module立即执行改变name属性 b模块引入module里的name是被更改的import {} from './module-a.js'import {} from './module-b.js'
</script>
// module.js
// 模块代码只会在第一次导入时解析
export let obj = {name: 'jsx'
}
// module-a.js
import { obj } from './module.js'
obj.name = 'ljj'
// module-b.js
import { obj } from './module.js'
console.log(obj.name)
1-6 模块延迟
模块脚本是延迟加载的,与 defer
特性一样
- 外部模块脚本
<script type="module" src="...">
不会阻塞HTML
的处理,它们会与其他资源并行加载 - 模块脚本会等到
HTML
文档完全准备就绪,然后才会运行 - 保持脚本的相对顺序:在文档中排在前面的脚本先执行
<!-- 普通脚本不会延时加载不会等待文档加载会阻塞文档加载 -->
<!-- 1. defer -->
<!-- 2. 脚本放置元素后 -->
<!-- defer async只能用于外部引入的脚本延时加载 -->
<script src="./normal.js"></script><!-- 模块脚本具有defer属性 延时加载不会阻塞文档加载 等文档加载完再加载 -->
<script type="module">let head = document.querySelector('h3')console.log(head)
</script><script type="module" src="./module.js"></script>
// module.js
// 延时加载
let head = document.querySelector('h3')
console.log(head)
// normal.js
let head = document.querySelector('h3')
console.log(head)
具有 type="module"
的内联脚本具有 async
属性,不会等待任何东西,文档加载预模块脚本谁先加载完谁就先执行
<!-- 模块内联脚本可以使用async 不会等待文档加载不会阻塞 -->
<script type="module" async>let head = document.querySelector('h3')console.log(head)
</script>
具有 type="module"
的外部脚本,具有相同 src
的外部脚本仅运行一次
从另一个源获取的外部脚本需要配置 CORS
跨域请求,远程服务器必须提供表示允许获取的 header
Access-Control-Allow-Origin
<!-- 相同的外部脚本引入只运行一次 -->
<script type="module" src="./module.js" async></script>
<script type="module" src="./module.js" async></script><!-- cdnjs.cloudflare.com 必须提供 Access-Control-Allow-Origin -->
<!-- 否则,脚本将无法执行 -->
<script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script>
1-7 模块路径与兼容
import
必须给出相对或绝对的URL
路径,没有任何路径的模块被称为裸模块。在import
中不允许这种模块- 通过
nomodule
特性设置不支持type="module"
模块的浏览器提示
<!-- 模块中import必须使用绝对或者相对路径 没有任何路径的模块无效 -->
<script type="module">// 相对引用必须以“/”、“/”或“./”开头// import { module } from 'module.js'import { module } from './module.js'
</script><!-- 兼容处理 nomodule属性 -->
<script type="module">alert("支持ES Module");
</script><script nomodule>alert("不支持ES Module")
</script>
2. 导入&导出
ES6
使用基于文件的模块,即一个文件一个模块
- 使用
export
将开发的接口导出 - 使用
import
导入模块接口 - 使用
*
可以导入全部模块接口 - 导出是以引用方式导出,无论是标量还是对象,即模块内部变量发生变化将影响已经导入的变量
表达式 | 说明 |
---|---|
export function show(){} | 导出函数 |
export const name=‘jsx’ | 导出变量 |
export class User{} | 导出类 |
export default show | 默认导出 |
const name = ‘jsx’ export {name} | 导出已经存在变量 |
export {name as hd_name} | 别名导出 |
import defaultVar from ‘hello.js’ | 导入默认导出 |
import {name,show} from ‘hello.j’ | 导入命名导出 |
Import {name as asName,show} from ‘hello.js’ | 别名导入 |
Import * as api from ‘hello.js’ | 导入全部接口 |
2-1 声明与导出
导出 export
和导入import
指令使用
在声明前导出 —— 通过在声明之前放置 export
来标记任意声明为导出,无论声明的是变量,函数还是类都可以
// 变量声明前导出
export let module = 'export'// 函数声明前导出
export function sayHi() {console.log('Hi')
}// 类声明前导出
export class User {constructor(name) {this.name = name}
}
导出与声明分开 —— 可以将 export
分开放置,通过变量,函数名,类名导出
// 声明与导出分开
let name = 'jsx'function sayHi() {console.log('Hi')
}class User {constructor(name) {this.name = name}
}export {name, sayHi, User}
2-2 具名导入
具名导入 —— 通过 import {...}
按名称导入
// 具名导出 通过import{} 按export导出名导出
import {name, sayHi, User} from './export.js'
console.log(name)
sayHi()
let user = new User('ljj')
console.log(user.name)
// export.js
let name = 'jsx'function sayHi() {console.log('Hi')
}class User {constructor(name) {this.name = name}
}export {name, sayHi, User}
import
静态导入时,模块路径必须是原始类型字符串,无法根据条件或者在运行时导入,模块默认是在顶层静态导入,为了方便模块编译打包
请注意在
{...}
中的import/export
语句无效
if (true) {// import静态导入无法条件判断导入import {name, sayHi, User} from './export.js'
}
2-3 全部导入
全部导入 —— 使用 import * as objName
将所有内容导入为一个对象
// 通过import*全部导入到一个对象内
import * as all from './export.js'
console.log(all.name)
all.sayHi()
let user = new all.User('ljj')
console.log(user.name)
按需导入 —— 现代的构建工具 webpack
会将模块打包到一起并对其进行优化,以加快加载速度并删除未使用的代码
优化器会检测到 import
导入的变量,并从打包好的代码中删除那些未被使用的函数,从而使构建更小
// 按需导入 通过import{} 按需求导入 没有引入的函数在打包时会删除减少打包体积
import {sayHi} from './export.js'
sayHi(); // Hi
// export.js
// sayHello 函数在打包优化时会删除,因为未被引用
function sayHi() {console.log('Hi')
}function sayHello() {console.log('Hello')
}export {sayHi, sayHello}
2-4 别名
导出别名 —— 模块可以通过 as
对导出给外部的功能起别名
// 别名导出 通过as重命名
function sayHi() {console.log('Hi')
}function sayHello() {console.log('Hello')
}export {sayHi as hi, sayHello as hello}
导入别名 —— 可以为导入的模块重新命名通过 as
,可以防止模块命名冲突与模名称简洁性
<script type="module">// 别名导出后 引入必须是导出的别名import { hi, hello } from './alias.js'hi()hello()
</script><script type="module">// import as 配置别名import { hi as sayHi, hello as sayHello } from './alias.js'sayHi()sayHello()
</script>
2-5 默认导出
模块分为两种:
- 包含库或函数包的模块
- 声明单个实体的模块
模块提供了一个特殊的默认导出 export default
语法,用于导出一个模块中只有单个实体的模块
- 将
export default
放在要导出的实体前 - 每个文件可能只有一个
export default
- 默认导出的模块,导入时不需要花括号
- 由于每个文件最多只能有一个默认的导出,因此导出的实体可以没有名称
命名的导出会强制我们使用正确的名称进行导入,对于默认的导出,我们总是在导入时选择名称,团队成员可能会使用不同的名称来导入相同的内容
命名的导出 | 默认的导出 |
---|---|
export class User {...}
|
export default class User {...}
|
import {User} from ...
|
import User from ...
|
// export default 一个模块只能有一个
export default function sayHello() {console.log('Hello')
}
// 默认导入模块不需要{}
import sayHello from './default.js'
sayHello()
// 默认导出可以没有名称
export default sayHello() {console.log('Hello')
}
2-6 混合导出
default
关键字被用于引用默认的导出,可以通过 as default
将函数与其定义分开导出(将函数默认导出)
// 通过default关键字将函数与定义分开导出
function sayHello () {console.log('Hello')
}export { sayHello as default }
import {default as sayHello} from './mixin.js'
sayHello()
在一个模块中同时有默认的导出和命名的导出时,使用 default as
别名为默认导出定义
// 当模块有具名与默认导出时
export default function () {console.log('Hi')
}export function sayBye() {console.log('bye')
}
// 模块导出默认与具名导出时
// default表示默认导出 将default别名配置
import {default as sayHi, sayBye} from './mixin.js'
sayHi()
sayBye()
将所有内容 *
作为一个对象导入,那么 default
属性是默认的导出
// *所有导入时 default属性为默认导入
import * as user from './mixin.js'
user.default();
console.log(user)
user.sayBye()
2-6 重新导出
export ... from ...
允许导入内容,并立即将其导出
当一个包含大量模块的文件夹需要导出一些功能到外部,每个模块都有一些功能,需要通过单个入口暴露包的功能(单个模块包含所有模块暴露的功能),需要将各个模块的功能在主文件中 import
导入再 export
导出给外部使用
- 重新导出时,默认导出需要单独处理,必须明确写出
export {default as User}
- 通过
export * from ...
重新导出只导出了命名的导出,忽略默认的导出 - 再通过
export {default as 别名} from ...
重新导出默认的导出
import {sayBye, friend} from './index.js'
friend()
sayBye()
// index.js
// 需要先导入各个模块功能 再导出
// import {getName, getGrilFriend, default as friend} from './get.js'
// export {getName, getGrilFriend, friend}// import {sayHello, sayHi, default as sayBye} from './say.js'
// export {sayHello, sayHi, sayBye}// 通过export from 导入重新导出
// export {getName, getGrilFriend, default as friend} from './get.js'
// export {sayHello, sayHi, default as sayBye} from './say.js'// 重新导入默认导出
// export * from ... 重新导出命名的导出
// export {default} 重新导出默认的导出
export * from './get.js'
export {default as friend} from './get.js'export * from './say.js'
export {default as sayBye} from './say.js'
// get.js
function getName() {console.log('jsx')
}function getGrilFriend() {console.log('ljj')
}function friend() {console.log('ddc zj')
}export {getName, getGrilFriend, friend as default}
// say.js
function sayHello() {console.log('Hello')
}function sayHi() {console.log('Hi')
}function sayBye() {console.log('bye')
}export {sayHello, sayHi, sayBye as default}
3. 动态导入
3-1 静态导入
通过 import ...
语句用于导入由另一个模块导出的绑定,该语句是静态导入
import
语句只能在声明了 type="module"
的 script
的标签中使用
- 静态导入模块路径必须是原始类型字符串
- 无法根据条件或者在运行时导入
import ... from getModuleName(); // error 路径必须是字符串
if(...) {import ...; // 不能条件判断执行
}
3-2 import动态导入
import(module)
表达式,它不需要依赖 type="module"
的 script
标签
该表达式加载模块并返回一个 Promise
,该 Promise
resolve
为一个包含其所有导出的模块对象。我们可以在代码中的任意位置调用这个表达式
静态与动态 import
优点:
- 按照一定的条件或者按需加载模块的时候,动态
import()
是非常有用的 - 静态型的
import
是初始化加载依赖项的最优选择,使用静态import
更容易从代码静态分析工具和tree shaking
中受益
// import() 动态导入返回Promise resolve是一个包含所有导出模块的对象
const promise = import('./get.js')
promise.then((reslove, reject) => {console.log(reslove)
})// 按需加载动态导入
if (true) {let result = import('./get.js').then(res => {res.getName()})
}async function importFunc() {let result = await import('./say.js')console.log(result)result.default()
}
<button onclick="importFunc()">按需加载</button>
3-3 Webpack编译模块
创建文件夹 bundle
生成配置 package.json
npm init -y
修改 package.json
配置文件,添加打包命令
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","dev": "webpack --mode development --watch"
}
安装 webpack
工具包
npm i webpack webpack-cli --save-dev
目录结构
bundle文件夹
--dist打包压缩文件
--node_modules依赖包
--index.html
--src文件夹
----index.js入口文件
----style.js功能模块
index.html
内容
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>webpack打包编译</title><script defer src="./dist/main.js"></script>
</head><body></body></html>
index.js
入口文件
import Style from './style'
Style()
style.js
功能模块
export default function () {document.body.style.backgroundColor = "#3496db"
}
执行打包
npm run dev
Module 模块化相关推荐
- 模块式开发 java_Java9系列第8篇-Module模块化编程
我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...
- 微服务 java9模块化_Java9系列第8篇-Module模块化编程
我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...
- java8模块化_Java9系列第8篇-Module模块化编程
我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...
- javascript 的module 模块化
The Module Pattern,模块模式,也译为模组模式,是一种通用的对代码进行模块化组织与定义的方式.这里所说的模块(Modules),是指实现某特定功能的一组方法和代码.许多现代语言都定义了 ...
- module (模块化)
模块化 没有模块化的问题 如果没有模块化的话直接引入两个js文件到主页面,呢么两个js文件中没有独立作用域的变量就会变为全局变量,变为全局变量时可能会出现变量的命名冲突的问题,这样的话可能会把变量的值 ...
- [享学Jackson] 十四、深入理解Jackson的Module模块化设计及原理分析
有些问题看起来重要,但实际上我们可以通过合理的设计或者将问题分解来规避.如果硬是要把时间花在解决问题本身,实际上不仅效率低下而且也是一种浪费. –> 返回专栏总目录 <– 代码下载地址:h ...
- 前端模块化详解(CommonJS、AMD、CMD、ES Module)
大家好,我是一碗周,一个不想被喝(内卷)的前端.如果写的文章有幸可以得到你的青睐,万分有幸~ 写在前面 随着前端项目的不断复杂,代码日益膨胀,项目的维护难度随之越来越大,此时模块化也就相继的出现了,本 ...
- 深入基础(一)模块化
简介 学习基础,JQuery 原生JS有一定基础,有自己一定技术认知(ps:原型链依然迷糊中.闭包6不起来!哎!) 当然最好有语言基础,C#,java,PHP等等.. 最初学习这个东西的原因很简单,在 ...
- 前端学习笔记(5)-ES6中promise、Generator、Module等使用场景
1. Promise 一种异步编程的解决方案,通过一个回调,避免更多的回调 异步编程允许我们在执行一个长时间任务时,程序不需要等待,而是继续执行之后的代码,直到这些任务完成之后再来通知你,通常是以回调 ...
最新文章
- 原理分析_变色近视眼镜原理分析
- 给recycleview添加尾部
- arcgis python工具-使用python制作ArcGIS插件(1)工具介绍
- 经典项目|手撸一个高质量RPC框架
- VGA光纤收发器特点及参数
- JdbcTemplate(操作数据库-查询返回对象、查询返回集合)
- 大数据基础学习三:Ubuntu下安装VMware Tools超详细步骤及需要注意的问题(以ubuntu-18.04.3、Mware Workstation 15.1.0 Pro为例)
- Java8————Optional
- 用存储过程实现的分页程序
- 深度学习5-模型的保存与加载
- Android Jetpack组件之App Startup,源码+原理+手写框架
- 2022年除了深度学习,人工智能算法有可能突破的10个方向
- matlab色散光纤,基于MATLAB的高速光通信色散补偿技术
- 6种摆脱百度竞价恶意点击的技巧
- 大多数Nobody游戏下载(带象棋残局攻略) 中文破解版
- endNote教程-5、6-高效阅读文献+高效文献调研
- 【回眸】关于网线水晶头里八根线的作用
- iOS13的暗黑模式
- js(jquery)鼠标移入移出事件时,出现闪烁、隐藏显示隐藏显示不停切换的情况
- (附源码)anjule客户信息管理系统 毕业设计 181936
热门文章
- ArcGIS Desktop、Arcengine、ArcObjects_SDK for NET.Framework安装教程
- 大数据时代的背景与变化
- Git 备份 Hexo 博客
- 一线互联网技术团队管理经验谈
- RxSwift 的简单使用
- linux多个文件删除重复行,shell中删除文件中重复行的方法
- Python中的os.chdir()函数:改变当前工作目录
- python 语音交互_Python调用WIN10语音交互+识别+控制+自定义对话
- 让HR眼前一亮:30个APP项目软件测试经验,点燃你的简历
- [Spring] [最佳实践] 如何对 @Async 标记的异步方法进行单元测试