js模块化
后端的commonjs规范
一. nodejs中, 只能有一个入口文件, 即启动文件. 在开发时候, 需要尽可能细致的分多个文件, 就产生了模块化规范的要求.
二. 模块化有两个核心要素: 隐藏和暴露;
1. 有导入和导出的文件叫模块.
2. 模块内的所有代码都是隐藏的, 不会污染全局.
3. 导出: 将模块化的内容暴露的过程就是导出.
4. 导入: 获取导出内容的过程
三. commonjs的导入导出
1.导出:
文件内的代码相当于放到一个立即执行函数中
如:
//a.js模块
const c = 3; module.exports = { a: 1, b: 2, c: c }
模块中的代码, 放到上面的立即执行函数内, 导出的结果{a:1,b:2,c:3};
注意: 导出的最终结果是module.exports, 而不是exports.
因此, exprots改变指针指向, 并不会造成导出内容的变化.
2.require导入
导入的结果是导出的结果.
模块缓存: 第一次导入会加载文件, 然后执行文件, 将执行后的内容存到一块缓存区域里
下次导入文件前, 先检查模块缓存中是否存在,
如果存在, 会直接从缓存中取,
如果不存在, 才去加载文件执行文件获取导出结果.
commonjs的执行过程, 或者原理就是:
1.用nodejs读取文件,
2. 将文件作为立即执行函数的执行内容
3. 返回module.exports的值.
前端过渡阶段的amd和cmd
一: 为什么在浏览器不能用commonjs规范.
1.由于commonjs中require函数的执行是同步的;
2. 浏览器到服务器获取是通过网络请求, 与commonjs中用nodejs直接读取文件相比, 网络请求会慢的多. 失败风险也大的多. 会导致执行缓慢甚至堵塞.
二. 解决思路, 用异步回调代替commonjs的require
三. AMD规范:
define(["模块1","模块二"...], function(arg1, arg2){ //arg1和arg2分别对应模块1和模块2的返回值,
//等加载完所有模块, 该回调函数会执行
} )
四. cmd规范:
define(function(require, exports, module){
//用require导入,
})
可以看出, cmd为了方便开发人员, 将后端nodejs的api习惯带入了前端模块化.
es6模块化
commonjs的导入;
1.依赖延迟声明, 优点是某些条件下能够减少模块加载量,
比如: if(xx){require("./a.js")}else{require("./b")}这样就会少加载一个文件,而不是a b两个文件都加载
2.导入路径./或../开头 与直接写文件名含义不一样;
es6的导入导出特点:
1.依赖预声明:,优点是依赖关系明确, 上来就能看到引入的所有模块, java, c语言都用这种导入模式.
2.规范的路径表示方法: 必须以./或者 ../开头, 不能直接写文件名.
3.灵活多样导出方式和导入方式.
导入导出分基本导入导出和默认导入导出;
基本导入导出和默认导入导出二者可以同时存在
基本导入导出可以有多个, 默认导入导出只能有一个
基本导入导出因为有多个, 所以必须具名. 即后面必须用表达式, 默认导出只有一个, 所以不需要.
基本导出语法:
1.export 声明表达式:
export var a=1;
2. export {变量}
var a = 1; export {a}; //相当于 导出的名字是a, 值是1.
注意: export a是错误的.
基本导入语法:
1. 导入其中某个或某几个模块的值,
import {a [as xx]} from "a.js";
2.导入所有值.
import * as obj from "a.js"
注意. 导入的值禁止更改. 是一个常量值.
默认导出:
export default xx; //xx可以是变量名, 字符串, 对象等
默认导入
import a from "/a.js" 表示将a.js里的默认导出赋值给常量a
import * as data from "a.js"; //data里有{default: xx, 基本导出}
注意: 导出的数据是常量, 最好用const声明.
es6导出和commonjs导出的结果有什么不一样:es6的导出结果不能更改,而commonjs导出结果可以更改的
如果用es6导入后更改,会报错
1、CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
2、所谓值的拷贝,原始类型的值被缓存,不随模块内部的改变而改变。
3、ES6 模块是动态引用,不缓存值,模块内外是绑定的,而且是只读引用,不能修改值。ES6 的 js 引擎对脚本静态分析的时候,遇到加载命令模块 import ,就会生成一个只读引用,当真正用到模块里边的值的时候,就会去模块内部去取。
4、CommonJS 模块是运行时加载,ES6 模块是编译时加载输出接口。
5、运行时加载:CommonJS 模块就是对象;是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
6、编译时加载: ES6模块不是对象,而是通过 export 命令「显式指定输出的代码」。import 时采用静态命令的形式,即在import指定「加载某个输出值」,而「不是加载整个模块」,这种加载称为“编译时加载”。
7、import的接口是read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对commonJS对重新赋值(改变指针指向),但是对ES6 Module赋值会编译报错。