前端模块化(CMJ与ESM)
1.为什么要模块化
- 把具有不同功能的板块封装为不同的模块,比如把涉及到网络请求的函数全都写在一个network.js文件里,把一些工具类的函数写在tool.js文件里,框架里的每一个组件都占据一个.vue文件,避免所有代码都写到一起,难以管理,同时还可以提高代码的复用率
- 通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数和类。
2.模块化规范
目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统
一、CommonJS
NodeJS是CommonJS规范的主要实践者,它有四个重要的环境变量为模块化的实现提供支持:module
、exports
、require
、global
。实际使用时,用**module.exports
定义当前模块对外输出的接口(不推荐直接用exports
),用require
加载模块**。
1.NodeJS采用了CommonJS规范实现了模块系统
2.CommonJS规范
CommonJS规范规定了如何定义一个模块, 如何暴露(导出)模块中的变量函数, 以及如何使用定义好的模块
在CommonJS规范中一个文件就是一个模块
在CommonJS规范中每个文件中的变量函数都是私有的,对其他文件不可见的
在CommonJS规范中每个文件中的变量函数必须通过exports暴露(导出)之后其它文件才可以使用
在CommonJS规范中想要使用其它文件暴露的变量函数必须通过require()导入模块才可以使用
1.Node模块中暴露数据的几种方法
在学习Node中学习过现在大概复习了解一下
分为
exports xxx =
和module.exports.xxx
module.exports
可以不通过对象的属性赋值,可以直接赋值
2.require的引入规则
- require导入模块时可以不添加导入模块的类型(模块的后缀名可以不写)
如果没有指定导入模块的类型, 那么会按照查找顺序依次查找.js .json .node文件
无论是三种类型中的哪一种, 导入之后都会转换成JS对象返回给我们
导入自定义模块时必须指定路径 - require除了可以导入”自定义模块(文件模块)“、还可以导入”系统模块(核心模块–nodejs自带的)”、“第三方模块(别人写的,可以直接使用)”
导入”自定义模块”模块时前面必须加上路径(就是前面的./之类的)
导入”系统模块”和”第三方模块”是不用添加路径 - 查找
如果是”系统模块”直接到环境变量配置的路径中查找
如果是”第三方模块”会按照module.paths数组中的路径依次查找
二、ES6Module(ESM)
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:
export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。在ES6之前,要使用一个模块,必须使用require函数将一个模块引入,但ES6并没有采用这种模块化方案,在ES6中使用import指令引入一个模块或模块中的部分接口,并没有将require写入标准,这也就是说require对于ES6代码而言,只是一个普通函数。
1
2
3
4
5
6
7
8
9
10
11/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);如上例所示,使用
import
命令的时候,用户需要知道所要加载的变量名或函数名。其实ES6还提供了****export default
命令,为模块指定默认输出,对应的import
语句不需要使用大括号****。这也更趋近于AMD的引用写法。1
2
3
4
5
6
7
8
9/** export default **/
//定义输出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
ele.textContent = math.add(99 + math.basicNum);ES6的模块不是对象,
import
命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
export default具体使用
其实export default和export的功能是一样的,但是一个文件里面只能有一个export default语句。
export default是把所有{}中所有的内容都赋值给default,然后当我们导入的时候也可以改变名称,但是其实是把所有的default导入了
使用方式
使用liveserver打开
首先在使用上,唯一的区别就是需要在script
标签上添加一个type="module"
的属性来表示这个文件是作为module
的方式来运行的。
1 | <script type="module"> |
然后在对应的module
文件中就是经常会在webpack
中用到的那样。
语法上并没有什么区别(本来webpack
也就是为了让你提前用上新的语法:) )
message.js
1 | export default 'hello world' |
这里说一下为什么不能用浏览器直接打开而是需要open livesever
为什么会报错?
不会引起跨域的引入外部文件的就link标签,img标签,script标签,除开这三个,只要不符合同源策略(协议,域名,端口)就会引发跨域问题
解决方法:
通过搭建本地一个服务器去进行资源的问题来解决跨域问题
例如: 1.node 打开 2.phpStydy 打开
(50条消息) 在html中使用import遇到的问题_麦兜:)的博客-CSDN博客_html import
关于一些导入的用法
原生的ESModules import导入,后面不能省略,后缀名、不能省略index.js,必须以“./“或者”/“开始,相对和绝对路径,也可以直接使用连接完成的url。
1 |
|
在浏览器上使用原生的ESM
通过 script[type=module]
,可直接在浏览器中使用原生 ESM
。这也使得前端不打包 (Bundless
) 成为可能。
1 | <script type="module"> |
ImportMap
但 Http Import
每次都需要输入完全的 URL,相对以前的裸导入 (bare import specifiers
),很不太方便,如下例:
1 | import lodash from "lodash"; |
在 ESM 中,可通过 importmap
使得裸导入可正常工作:
1 | <script type="importmap"> |
此时可与以前同样的方式进行模块导入
1 | import lodash from 'lodash' |
示例
1 | <script type="importmap"> |
分析import和require引入模块时不同用法
import作为es6引入模块的方法必须让引进时的script或者package.json加上 "type": "module"
这一属性,直接看代码
index.js
1 | import _ from 'lodash' |
package.json
1 | { |
package.json应该是默认Commonjs语法所以不需要引入即可使用require
1 | var _ = require('lodash'); |
如果在"type": "module"
下要使用require方法引入文件
在require前加上这两句话
这是由于从node.js 14版及以上版本中,require作为COMMONJS的一个命令已不再直接支持使用,所以我们需要导入createRequire命令才可以
1 | import { createRequire } from 'module'; |
探讨ES6的import export default 和CommonJS的require module.exports - 掘金 (juejin.cn)
j