一、模块化
本文的核心说的是模块化.
话说js语言最早使用的很少,页面逻辑一行js就能实现,就不存在模块化。这么少的代码还要什么模块
是吧
当页面越复杂,开发人员越多,写的js代码越多; 这让人
为了解决两个问题:
1、首先防止全局代码逻辑混乱,需要根据业务逻辑划分模块;
2、其次是不同开发人员负责不同的模块,防止命名冲突或者是自己的声明的变量方法被别人误改,需要根据人员划分模块;
模块化概念就出现了!
模块化就是对js代码的划分,可以把一个js文件相当于一个模块,文件之间相互引用,就是模块的相互引用。
当前我们所熟知的Es module规范,并不是从模块化概念兴起的时候就制定的,直至ES6(2015)才推出。
按照时间顺序,在此之前的规范有AMD、CMD、CommonJs等等。
在node的大环境下,主流使用还是commonjs和esmodule,amd、cmd仅仅了解即可。
再来说明一个概念,规范和实现是不一样的东西,从名字就可以看出来,就想ESMA script是js语言的规范,但怎么执行js语言,这就需要node或浏览器实现,因为他们都有实现js代码的工具,就是JavaScript 引擎。
所以AMD 的主流实现是 requireJs;
CMD的主流实现是 SeaJs;
CommonJs的主流实现是node;
再来是执行环境:
| 环境 | node | 浏览器 |
|---|---|---|
| AMD | 不可以 | 可以 |
| CMD | 不可以 | 可以 |
| commonjs | 可以 | 可以 |
| ES module | 可以(仅限打包后的代码) | 可以 |
AMD和CMD必须在浏览器中;
Commonjs和ES module既可以在node中也可以在浏览器中;
node环境下,Esmodule和commonjs规范下的代码不能互相混用,要想ES module在node环境下也能执行,需要wepack打包工具打包文件,将代码转换成node可执行的代码!
二、模块化之前
再来了解下在规范之前想使用模块化,是怎么解决的?
匿名立即执行函数
//声明
//声明一个区域,避免和其他人的代码冲突
const moduleA = (function(){
const bar = 'c'
function foo(a, b){
return a + b
}
return {
bar,
foo
}
})()
//使用
//使用属性
moduleA.bar
//使用方法
moduleA.foo()
二、AMd和requireJs(了解)
AMD ==== Asynchronous Module Definition(异步模块定义)
1、js代码入口
// demo/demo.html
<!-- 引入require.js文件和一个入口文件main.js。main.js中配置require.config()并规定项目中用到的基础模块。 -->
<script src="lib/require.js" data-main="js/main"></script>
data-main属性用来说明文件的入口,并且在requires.js加载完成后再执行代码。或者
<script src="lib/require.js"></script>
<script src="js/main.js" ></script>
2、模块注册require.config()
// js/main.js 主文件入口
require.config({
baseUrl: "js",
paths: {
"jquery": "jquery-3.6.0.min", //实际路径为js/jquery.min.js //第三方模块
"math": "math", //实际路径为js/math.js //定义模块
}
})
paths: {key: path},中定义了模块使用的名称、模块的路径
既可以注册第三方模块,也可注册自定义模块
3、模块定义define()
// js/math.js
// 自定义math.js模块
define(function () {
var basicNum = 0;
var add = function (x, y) {
return x + y;
};
return {
add: add,
basicNum :basicNum
};
});
//第三模块需要自己下载,如jquery的jquery.min.js
4、模块使用require()
require(["jquery","math"],function($, _){
// 在代码中用 $, _来使用
console.log("$", $, $('#a'),)
console.log("1+2=", _.add(1,2));
});
//使用单个模块,可以不用中括号
require("jquery",function($){...}
三、CMD和seaJs(了解)
CMD ==== Common Module Definition(通用模块定义)
cmd不需要注册,直接require()使用
1、 js代码入口
<script src="./lib/sea.js"></script>
<script>
seajs.use('./js/main.js')
</script>
2、注册并使用define((require, exports, module)=>{})
require,模块引入函数,require(path)
exports, 导出对象
module,导出对象
// 定义js/math.js模块, math.js模块中也可依赖其他模块,只需要require引入即可
define(function(require, exports, module) {
var basicNum = 0;
var add = function (x, y) {
return x + y;
};
//导出方式一:
module.exports= {
add,
basicNum
};
//导出方式二:
module.exports.add = add;
//导出方式三:
exports.add = add;
});
//js/main.js
define(function(require, exports, module) {
var _ = require('./math')
console.log("1+2=", _.add(1,2));
});
注意:不能通过exports = {},错误方式
四、CommonJs和Node(重头戏)
模块中的内容进行导出:exports和module.exports;
导入其他模块: require;require(自定义模块/系统模块/第三方库模块);
1、exports和module.exports
//导出变量、函数、类等
const bar = 1;
const foo = function(a, b){
return a + b
}
//方式一: 这里module.exports = {} 不是一个对象,{}只是一个导出规则,必须要这样写
module.exports = {
bar,
foo
}
//方式二:
module.exports.bar = bar
module.exports.foo = foo
//方式三:
exports.bar = bar
exports.foo = foo
exports和module.exports 的本质
源码上的实际上是导出一个module.exports的引用,所以能用方式一 module.exports = {} 和 方式二 module.exports.bar = bar 导出内容;
源码中还多做了一步就是,exports = module.exports,让exports指向module.exports,这样两者都是指向同一个引用,所以能用方式三;
导出的是module.exports的引用,所以当我们想通过exports = {}导出时, 我们已经包exports的引用改掉了,这个时候,不论如何操作exports,里面的内容都是导不出去的。
module.exports = {}
exports = module.exports
2、require
- require(模块名)
//直接找到内置模块, 这些内置模块是在安装node时,自动安装的,应该存放在安装目录里的一个node_modules文件中
const fs = require('fs')
const path = require('path')
- require(路径)
require('./')
require('./')
require('../')
按照一下规则依次查找,直至找到
1、把require里的path当成一个文件路径; 查找顺序:
指定后缀名的文件 --》 .js后缀的文件 --》 .json后缀的文件 --》 .node后缀的文件
2、把require里的path当成一个目录路径;
指定目录index.js文件 --》 指定目录index.json文件 --》 指定目录index.node文件
3、如果没有找到,那么报错:not found
- require(X) 未知的东西
require(X) 引入未知的东西,既不是内置模块,也不是自定义模块
在每一层的node_modules中依次查找,直至找到
查找当前目录下node_modules文件 --》 上一层目录下node_modules文件 --》 ...
五、ESmodule和Webpack
具体规则可查看 阮一峰写的es6,上面写的很详尽,我就不多此一举了
主要说下,esmodule规范下的代码怎样在node和浏览器环境中执行:
问题
node执行Js代码采取的模块化规范是commonjs,node内部默认是不把js文件当做ES module,用node执行采取ES module规范的代码时,会出现如下错误:
//demo/main.js
import { add } from './utils.js'
console.log(add(1,1))
//demo/utils.js
export const add = (a, b) =>{
return a + b
}
执行命令: node ./main.js
解决
要想解决这个问题就需要让node把每个js文件当做一个模块! 按照提示解决: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
- 解决1: 生成一个package.json文件,在文件中设置"type": "module" npm init 生成文件
- 解决2 在html中引用main.js文件
<script src="./main.js" type="module"></script>
注意:要通过开启一个本地服务在浏览器打开html!
通过script标签引用模块main.js,相当于发起一个请求,获取到模块里的内容,开启本地服务可以组织跨域。
通过本地服务127.0.0.1查看
通过文件路径查看
- 解决3
webpack打包
1、npm init 生成package.json文件
2、有全局webpack,在根目录下执行webpack命令;
3、没有全局webapck,可npm install webpack webpack-cli -g,下载后,在执行webapck; 或者下载局部webpack,npm install webpack webpack-cli -D,执行npx webpack;
node中执行: node .\dist\main.js 或者
<script src="./dist/main.js"></script>