js模块化 Common.js / AMD / CMD / es6

JS模块化

一、理解:

1、什么是模块化?

(1)、一个js就是一个模块
模块化进化史
(1)、GO被污染,容易命名冲突
foo() {}
bar() {}

(2)、nameSpace 模式、减少GO上变量数目,虽然实现了命名冲突,但是一点也不安全,可以随意修改
var obj = {
foo: function() {},
bar: function() {}
}
obj.foo()

(3)、IIFE 现在模块实现的基石,可以引入依赖,函数是javaScript唯一的 Local Scope
(function(window, $){

}(window, jquery) )

2、为什么要模块化?

(1)、复杂性
(2)、提高解藕
(3)、部署(功能点明确)

3、模块化的好处?

(1)、避免命名冲突
(2)、更好的分离,按需加载
(3)、更高的复用性
(4)、可维护性

4、页面引入加载script

可能会同时引入10个 script标签

(1)、可能会为浏览器发4次请求,
依赖模糊,2依赖1,3依赖2,4依赖3,有一个报错都无法继续执行

二、规范

动态加载和静态加载指的是什么时候加载,动态加载是运行时加载(CommonJS、node),静态加载是编译(预编译)时加载(ES6)。当然es6也提供的动态加载,即import() 。

对于模块的依赖,何为动态?何为静态?

动态:是指对于模块的依赖关系建立在代码执行阶段;

静态:是指对于模块的依赖关系建立在代码编译阶段;

上文提到,CommonJS导入时,require 的路径参数是支持表达式的,例如

// A.js
let fileName = 'example.js'
const bModule = require('./' + fileName)

因为该路径在代码执行时是可以动态改变的,所以如果在代码编译阶段就建立各个模块的依赖关系,那么一定是不准确的,只有在代码运行了以后,才可以真正确认模块的依赖关系,因此说CommonJS是动态的。
那么现在你也应该也知道为什么 ES6 Module 是静态的了吧

举例: 在有热更新的项目中,同时引入module/module1 两个文件:

module.js
alert(‘我是import from我是静态的(编译时)加载’)
module1.js
export default function (){
alert(‘我是import函数,我是动态时加载的’)
}

import module from ‘./module’ // (静态的)页面保存module文件就会执行
const module1 = () => import(‘./module1’) // (动态的)运行时加载,页面添加一个延时器,import返回的是一个 promise

setTimeout(() => {
console.log('module1: ', module1().then(value => console.log(value.default(), ‘value’)));
}, 3000)

同步加载还是异步加载指的是加载的方式。 静态加载中都是同步加载的。动态加载中CommonJS(node)的require是同步的。而es6import()是异步的。

同步加载:CommonJS(node) (C、同、运、动、module.exports(导出内存)、require(缓存引入模块))

(commonJs、同步加载、运行时加载、动态的)

1.CommonJS,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。
  var math = require(‘math‘);
然后,就可以调用模块提供的方法:
  var math = require(‘math‘);
  math.add(2,3); // 5
第二行math.add(2, 3),在第一行require(‘math‘)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。
这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

异步加载:(A、异、预加载)

2:AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
  require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:
  require([‘math‘], function (math) {
    math.add(2, 3);
  });//这里是否意味着可以有多个require异步执行,谁先加载完成就先执行自身回调?
math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。

参考链接1:https://www.jianshu.com/p/0fef8b7ca222
参考链接2: https://blog.csdn.net/goutinga/article/details/123923505
参考链接3: https://www.jianshu.com/p/b82623c8c8a0

1、common

(1)、node-common
module1.js文件
module.exports = {
  msg:'module1',
  foo() {
    console.log(this.msg);
  }
}

app.js 文件
let module1 = require('./module1')
module1.foo()
(2)、 browserify ,可以实在在浏览器访问 common.js
module1.js文件
module.exports = {
 msg:'module1',
 foo() {
   console.log(this.msg);
 }
}

app.js 文件
let module1 = require('./module1')
module1.foo()

npm init
npm install browserify

package.json文件修改打包后的出口
“scripts”: {
“build”:“browserify js/src/app.js -o js/dist/bundle.js”
// -o 左边为大包入口文件, 右边为大包后的文件
},

<!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>Document</title>
  <script type="text/javascript" src="./js/dist/bundle.js"></script>
</head>
<body>
</body>
</html>

2、AMD / RequireJS 在推广过程中对模块定义的规范化产出。

记忆法:AMD requirejs 静编译,前(因为A在前)
定义模块:define([文件名称], dependencies(依赖), factory)
使用模块:require(dependencies(依赖), callback)

amd 类似 import 静态,编译时(编写代码时有错误就会抛出)

1. 定义模块:define(id?, dependencies?, factory)

依赖有三个默认的,即"require", “exports”, “module”。顺序个数均可视情况
如果忽略则factory默认此三个传入参数
id一般是不传的,默认是文件名

语法一:不使用 amd 写法
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>Document</title>
</head>
<body>
  <script src="./module1.js"></script>
  <script src="./module2.js"></script>
</body>
</html>

module1.js

// 定义一个没有依赖的模块
(function(window) {
  const msg = '我是module1模块,被module2引入了'
  function module1Fun () {
    console.log(msg);
  }
  window.module1 = { module1Fun }
}(window))

module2.js

// 定义一个有依赖的模块
(function(module1){
  const msg = '我是module2模块'
  console.log('我是module2模块,我来来执行module1模块', module1);
  module1.module1Fun()
}(module1))
语法二:使用 amd 写法
加载模块:require([module], factory)

文件层级
requireAMD
js
libs
require.js
modules
alerter.js
dataService.js
index.html
main.js

alert.js  文件
	//定义有依赖的模块
	define(['dataService'],function (dataService) {  
	  let msg = 'alerter.js'
	  function showMsg() {
	    console.log(msg,dataService.getName());
	  }
	  // 暴露模块
	  return {showMsg}
	})
	
dataService.js 文件
	// 定义没有依赖的模块
	// 使用AMD语法
	define(function () {
	  let name = 'dataService.js'
	  function getName() {
	    return name
	  }
	  // 暴露模块
	  return {getName}
	})

main.js 文件

(function () {  
  requirejs.config({
    baseUrl:'/js', // 基本的路径 出发点正在根目录下 不配置时从main.js出发去找
    paths:{
      dataService:'./modules/dataService', //不要加.js 默认会添加后缀
      alerter:'./modules/alerter'
    }
  })
  requirejs(['alerter'],function(alerter) {
    alerter.showMsg()
  })
})()

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>Document</title>
</head>
<body>
  <!-- 引入require.js并指定js主文件的入口 -->
  <script data-main="js/main.js" src="./js/libs/require.js"></script>
</body>
</html>
AMD 依赖前置,require中会执行 依赖中所有的文件,一期返回给 callback
function define(name, dependencies, factory) {
    debugger
    // console.log(101010, dependencies)
    if(!dependencies || !dependencies.length) {
        factories[name] = factory
    }else {
        // console.log('14--->', factory)
        // bind 与 call 语法一致,区别在于立即执行还是等待执行
        // fn.call(obj, 1, 2); // 改变fn中的this,并且把fn立即执行
        // fn.bind(obj, 1, 2); // 改变fn中的this,fn并不执行

        factories[name] = factory.bind(this, ...dependencies.map( moduleName => factories[moduleName]() ))
        // console.log(12, factories[name])
    }
}
function require(dependencies, callback) {
    // amd 和 cmd 最大区别 amd 依赖执行前置
    let dependencyResults = dependencies.map( moduleName => factories[moduleName]() )
    callback(...dependencyResults)
}

3、CMD/ SeaJS 在推广过程中对模块定义的规范化产出

记忆法:CMD SeaJS 异 ,近, 缓存(跟common)
定义模块:define(dependencies(依赖), factory(require, exports, module))
使用模块:require(dependencies(依赖), callback(require, exports, module))
定义模块:define(factory)

require, exports, module参数顺序不可乱
暴露api方法可以使用exports、module.exports、return
不同
CMD与AMD不同:与requirejs不同的是,若是未暴露,则返回{},requirejs返回undefined
// amd cmd 区别在哪: amd 依赖前置 cmd 依赖就近

加载模块:require

定义模块无需列依赖,它会调用factory的toString方法对其进行正则匹配以此分析依赖
预先下载,延迟执行

例子如下:

文件地址
libs
	sea.js
modules
	main
	module1
	module2
	modlue3
	module4
sea.js
index.html

libs
	1、可以使用sea.js文件,也可以使用
	zhangxinxu.com/sp/seajs/docs/zh-cn/module-definition.html#module-definition
	2、CDN:https://cdn.bootcdn.net/ajax/libs/seajs/3.0.3/sea-debug.js

mian.js
define(function (require, exports, module){
  const module3 = require('./module3')
  module3.foo3()
  //依赖就近,什么时候需要这个模块什么时候引入
  const module4 = require('./module4')
  console.log(module4.module4.foo4(), '4444')
})

module1.js
define(function(require, exports, module){
  const msg = 'module1--导出对象.module1'
  function foo () {
    console.log(msg)
  }
  exports.module1 = foo
})

module2.js
define(function(require, exports, module){
  const msg = 'module2-导出函数'
  function foo2 () {
    console.log(msg)
  }
  module.exports = foo2
})

// module使用return也可以导出
define(function(require, exports, module){
  const msg = 'module3--导出对象'
  function foo3 () {
    console.log(msg)
  }
  return { foo3 }
})

// module不导出,默认为空对象
define(function(require, exports, module){
  const msg = 'module4'
  // 同步执行
  const module1 = require('./module1')
  module1.module1()
  // 异步引入
  require.async('./module2', function(m2){
    console.log('m2', m2())
  })
  function foo4 () {
    console.log(msg)
  }
})

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>Document</title>
</head>
<body>
  <script src="./libs/sea.js"></script>
  <script>
    seajs.use('./modules/main.js')
  </script>
</body>
</html>
好处:1、是异步的 希望像node靠齐 他的源码 就类似于 commonjs webpack
2、模块缓存
cmd.js // cmd的核心是交换函数执行权
let fatories = {}
let modules = {}
function define(name, factory) {
    fatories[name] = factory
}

function require(name) {
    let script = document.createElement('script')
    script.src = name // './modules/moduleA.js'
    script.onload = function() {
		    if(modules[name]) {
		        return modules[name]
		    }
		    let exports = {}
		    // 递归作用 交换函数的执行权 cmd 依赖就近 关键点就在这
		    // require('moduleA')
		    fatories[name](require, exports)
		    modules[name] = exports
		    return exports
    }
   
}

4、es6/import

导出
export default (x, y) => x + y // 导出默认模块
export function moduleB() {} // 导出具名模块

导入
import moduleA from "./modulePath" // 默认模块
import { moduleB } from "./modulePath" // 具名模块
区别项es模块化commonJSAMDCMD
可用于服务端还是浏览器服务端和浏览器服务端 / 浏览器端(npm i browserify)浏览器浏览器
模块依赖关系何时确定(即:何时加载模块)编译时运行时编译时运行时
设计思想静态的静态的
模块导出export / export defaultreturn value(任意值)return value(任意值)exports / module.exports (return)
是否整体加载模块(即加载的所有方法)
是否是动态更新(即通过接口,可以取到模块内部实时的值)是。es module输出的是值的引用不是。commonJS模块输出的是值的拷贝,不存在动态更新
模块变量是否是只读的是。原因:ES6 输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。

差异

AMD 与 CMD:

AMD是 RequireJS 在推广过程中对模块定义的规范化产出。
AMD在浏览器端异步加载,AMD推崇依赖前置,加载完模块之后就会立即执行它。
1、模块的加载不影响后面语句的执行;
2、所有依赖于这些模块的语句都写在一个回调函数中;
3、而加载内部是同步的(加载完毕后,这个回调函数才运行)

CMD是 SeaJS 在推广过程中对模块定义的规范化产出。
CMD在浏览器端异步加载,CMD推崇依赖就近,加载完模块不会立即执行,只是加载,等到需要的时候才会执行。

ES Module与CommonJS:

CommonJS模块是对象,是运行时加载,运行时才把模块挂载在exports之上(加载整个模块的所有),加载模块其实就是查找对象属性。
ES Module不是对象,是使用export显示指定输出,再通过import输入。此法为编译时加载,编译时遇到import就会生成一个只读引用。等到运行时就会根据此引用去被加载的模块取值。所以不会加载模块所有方法,仅取所需。
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

CommonJS与AMD/CMD:

AMD/CMD是CommonJS在浏览器端的解决方案。
CommonJS是同步加载(代码在本地,加载时间基本等于硬盘读取时间)。
AMD/CMD是异步加载(浏览器必须这么做,代码在服务端)

UMD与AMD/CMD

UMD(Universal Module Definition)是AMD和CommonJS的糅合,跨平台的解决方案。
AMD模块以浏览器第一的原则发展,异步加载模块。
CommonJS模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。
UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式。再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。

参考链接:https://juejin.cn/post/6844903663404580878
参考链接2: https://juejin.cn/post/6896397110078504973

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要升级Node.js的版本,您可以使用以下三种方法之一: 方法一:使用n模块升级Node.js(推荐使用此方法): 1. 首先,使用命令`node -v`检查当前Node.js版本。 2. 接下来,清除npm缓存,使用命令`npm cache clean -f`。 3. 然后,全局安装n模块,使用命令`npm install -g n`。 4. 升级到最新稳定版,使用命令`n stable`。 5. 如果需要升级到最新版,使用命令`n latest`。 6. 如果需要升级到指定版本,使用命令`n v14.6.0`,将版本号替换为您想要安装的版本。 7. 如果需要切换使用不同的版本,使用命令`n 13.10.0`,将版本号替换为您想要切换的版本。 8. 如果需要删除指定版本,使用命令`n rm 13.10.0`,将版本号替换为您想要删除的版本。 9. 如果需要使用指定版本执行脚本,使用命令`n use 13.10.0 some.js`,将版本号替换为您想要使用的版本和脚本路径。 10. 最后,使用命令`node -v`再次查看Node.js版本确认升级完成。 方法二:使用NVM(Node Version Manager)来升级Node.js: 1. 首先,从GitHub下载并安装NVM。 2. 然后,配置NVM项目到环境变量中,使用命令`source ~/.bashrc`。 3. 使用命令`nvm --version`检查NVM是否安装成功。 4. 如果需要升级到指定版本,使用命令`nvm install 13.10.0`,将版本号替换为您想要安装的版本。 5. 如果需要升级到最新版,使用命令`nvm install latest`。 6. 如果需要升级到稳定版,使用命令`nvm install stable`。 方法三:通过官网下载安装最新的LTS版本Node.js: 1. 前往Node.js官网下载LTS版本的Node.js安装包。 2. 安装下载好的Node.js安装包。 以上就是升级Node.js版本的三种方法。希望对您有帮助!如需了解更多Node.js知识,您可以参考《Node.js教程》。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值