发布于2021-03-07 22:27 阅读(1002) 评论(0) 点赞(21) 收藏(1)
早期的 JS 编码并没有模块化这个概念,写出来的代码就像“挂面”一样,有时候单个页面的 JS 代码就很长,维护起来十分麻烦,而且还存在“变量污染”、“命名冲突”和“缺少文件依赖”等问题。随着Node.js的出现和前后端分离开发模式的愈益流行,JS模块化技术也越来越成熟,其主要思想是利用“闭包”和“异步加载JS”来解决以上的问题。其实JS模块化的发展历程也是比较曲折的,这里给出一个其描述比较详细的链接:https://www.cnblogs.com/lvdabao/p/js-modules-develop.html。
JS模块化的方式大致分为这么几种:CommonJS,AMD,CMD,ESM,UMD。
CommonJS 是 Node.js 的模块化规范,在 Node.js 中每个 JS 文件就是一个模块。每个 JS 文件都有一个 module 对象,它又有一个 exprots 属性,顾名思义它就是JS文件默认导出的对象。
而且每个 JS 文件都有自己的私有作用域,其中的变量、常量、函数和类等对其他的 JS 文件隐藏,如果其他JS文件想要访问这个 JS 文件中的变量、常量、函数和类等,需要我们把变量、常量、函数和类等添加到 module.exports 对象上,然后通过 require 方法来引入该文件(实际上就是引入了该JS文件的 module.exports 对象)进行访问。
有一点需要注意的是:直接给 exports 赋值(exports = xxx)是起不到任何作用的,你可以通过 module.exports = xxx 来改变引用。
- // ./test.js
-
- // 这种方式起不到任何作用,它也不会被挂载到 global 上..
- exports = {a:1};
- console.log(module.exports) // {}
-
- exports.a = 2;
- console.log(module.exports) // { a: 2 }
-
- module.exports = {a:3};
- console.log(module.exports); // { a: 2 }
-
- b = '我是所有模块共享的变量'
- console.log(global.b) // 我是所有模块共享的变量
-
- exports = {a:1}; // 它不会被挂载到 global 上
- console.log(global.exports); // undefined
- // ./main.js
-
- var o = require('./test');
- console.log(o); // { a: 3 }
AMD (Asynchronous Module Definition),即异步模块定义,是客户端实现 JS 模块化的一种方式,由 Require.js 实现。
它借鉴了 CommonJS 的思想,使用了 CommonJS 里 require、module 和 exports 特性,其主要方法有 config、define 和 require 。
在使用 Require.js 的时候,需要在标签属性上添加 data-main 属性,当 Require.js 加载完后会去加载 data-main 里面指定的 JS。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <title>Require.js</title>
- </head>
- <body>
- <script data-main="main.js" src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"></script>
- </body>
- </html>
- // ./main.js
-
- /**
- * webpack 不支持 require.config,
- * 这部分代码编译后为 undefined
- */
-
- /**
- * 配置模块加载位置,
- * Require.js 根据路径去加载对应的 JS,
- * 一般是引入第三方 JS 时使用
- */
- require.config({
- paths: {
- test: './test'
- }
- });
-
- /**
- * 这种方式 webpack 可以识别,不过文件需存在
- *
- * 第1种方式:根据 JS 文件路径去加载JS,
- * 等 test 对应的 JS 加载并执行完后就执行 callback
- */
- require(['./test'], function(test) {
- console.log('test2', test);
- });
-
- /**
- * 如果不是以别名或 '/', './' 开头,
- * webpack 会使用 nodejs 的 require 方式
- * 从 node_modules 一层层的网上找,找不到
- * 则报错。所以 wepack 和 require.js 的
- * 处理方式不一致,webpack 会因为找不到对
- * 应的模块而报错。
- */
-
- /**
- * 第2种方式:根据 id 去加载JS,
- * 等 test 对应的 JS 加载并执行完后就执行 callback
- */
- require(['test'],function(test){
- console.log('test1', test);
- });
-
- /**
- * 等定义的 uitl1 模块执行完后,调用 callback
- */
- require(['util1'],function(util){
- console.log('util1', util);
- });
-
- /**
- * webpack 编译后的代码只能导出最后一个,
- * 它默认一个文件只能有一个 define ,即
- * 一个文件一个模块。
- */
-
- /**
- * 同一文件中定义一个 util1 模块
- */
- define('util1', function() {
- return {desc: 'util1'};
- });
-
- /**
- * 同一文件中定义一个 util2 模块
- */
- define('util2', function() {
- return {desc: 'util2'};
- });
- // ./test.js
-
- // 定义一个模块,实际上也可以依赖其他模块的,这里不做研究
- // define({ a: 1, b: 2, c: 3 });
-
- // 还可以这么写
- define(function(require, exports, module) {
- // 可以这么写
- // return {a: 1, b: 2, c: 3};
- // 也可以这么些
- // exports.a = 1;
- // exports.b = 2;
- // exports.c = 3;
- module.exports = {a: 1, b: 2, c: 3};
- });
CMD(Common Module Definition),即普通模块定义,它看上去像是 AMD 和 CommonJS 的结合体,由 Sea.js 实现。在 CMD 的规范里,一个文件表示一个模块。
它同样使用了 CommonJS 里 require、module 和 exports 特性,常用方法有 config、define 和 require 这三个,这些方法和 AMD 中对应的方法类似,这里就不再赘述了。
另外它还有一个 use 方法,这个是用于加载入口模块的,和 data-mian 的作用类似,用来执行入口 JS 文件。和 AMD 不同的是,CMD 支持使用 require.async(id) 实现懒加载。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <title>Sea.js</title>
- </head>
- <body>
- <script src="https://cdn.bootcdn.net/ajax/libs/seajs/3.0.3/sea.js"></script>
- <script>
- /**
- * 使用 data-main 只支持加载单模块入口,
- * 使用 use 方法支持加载多模块入口
- */
- seajs.use(['./main'], (a) => {
- console.log('exec entry js', JSON.stringify(a)); // exec entry js {"desc":"entry module","data":{"a":1,"b":2,"C":3}}
- });
- </script>
- </body>
- </html>
- // ./main.js
-
- // webpack 不会“干掉”这段代码
- // seajs 的全局配置
- seajs.config({
- // 配置加载 JS 的基础路径
- base: '.',
- // 别名
- alias: {
- 'test': 'test'
- },
- charset: 'utf-8',
- timeout: 20000,
- debug: false
- });
-
- // 定义个模块,模块标识由文件名决定
- define(function(require, exports, module) {
- /**
- * 引入 test 模块,如果使用 var a = require('test'),
- * sea.js 支持这种写法, 但是 webpack 不支持,它会因
- * 为找不到模块而报错,其原因和用 webpack 编译 AMD 模
- * 块化代码一样,需要换为路径才能解析成功。
- */
- var a = require('./test');
- // 也可以写成
- // exports.desc = 'entry module';
- // exports.data = a;
- // 也可以写成
- // module.exports = {desc: 'entry module', data: a};
- return {
- desc: 'entry module',
- data: a
- }
- });
- // ./test.js
-
- // 定义一个模块
- define((require, exports, module) => {
- // 也可以写成
- // exports.a = 1;
- // exports.b = 2;
- // exports.c = 3;
- // 也可以写成
- // return {a: 1, b: 2, c: 3}
- module.exports = {
- a:1,
- b:2,
- C:3
- }
- });
ESM(ECMA Script Modules),即 ES6 的模块化规范。
但是因为目前有些低版本的浏览器还是不支持 ESM ,所以一般需要将 ESM 转成其他的模块化方式,这个过程一般由 TSC 编译器或者 Webpack 等自动化构建工具去转换。
- // ./main.js
-
- // 星号别名导出,导出的是整个的模块( Module 的实例)
- import * as obj from './test.js';
- console.log(obj);
-
- // 解构导入,这里从 Module 实例中解构 a
- import {a, b} from './test.js'
- console.log(a); // {a: 1}
- console.log(b); // {b: 2}
-
- // 导入默认,xxx 不是关键字就行了
- // 前提是被导入模块必须有 default 默认导出
- import xxx from './test.js';
- console.log(xxx); // {desc: "我是默认导出"}
- // ./test1.js
-
- // 将 a 、b 和 default 变为 Module 实例的属性,并导出 Module 实例
- export var a = { a: 1 }
- export var b = { b: 2 };
- export default { desc: '我是默认导出' };
-
- // 以上导出相当于下面这一句, 不过这里 default 是关键字,会有错误,
- // 所以这里的花括号并不是对象!它只是一种表示形式,类似于解构的反向操作。
- // export { a, b , default }
UMD(Universal Module Definition),即通用模块定义,是 CommonJS 和 AMD 模块化标准的集合,它还兼容 “Root” 模式,支持从全局对象上获取模块。
它不能直接支持 ESM ,但是上面说过 ESM 会变为 UMD 支持的模块化中的一种,也算是间接地支持 ESM 了。
- // ./main.js
-
- // UMD
- (function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- console.log('main', 'AMD');
- // AMD,定义一个以文件名为模块名的模块,并导入 vue 对应的依赖
- define(['./test'], factory);
- } else if (typeof exports === 'object') {
- console.log('main', 'CommonJS');
- // CommonJS
- module.exports = factory(require('./test'));
- } else {
- console.log('main', 'Root');
- // 浏览器全局变量(root 即 window)
- root.yyy= factory(root.test);
- }
- }(this, function(a) {
- console.log(a);
- }));
- // ./test.js
-
- // UMD
- (function(root, factory) {
- if (typeof define === 'function' && define.amd) {
- console.log('test', 'AMD');
- // AMD,定义一个以文件名为模块名的模块,并导入 vue 对应的依赖
- define(factory);
- } else if (typeof exports === 'object') {
- console.log('test', 'CommonJS');
- // CommonJS
- module.exports = factory();
- } else {
- console.log('test', 'Root');
- // 浏览器全局变量(root 即 window)
- root.test= factory();
- }
- }(this, function() {
- console.log('test');
- return {
- desc: 'test'
- }
- }));
可以看出,UMD 没有自己的实现,而是 AMD 、CommonJS 和 Root 模式的集合。
https://github.com/zzp-dog/reception-learn/tree/master/webpack_review/module
1.为了寻找你,我搬进了鸟的眼睛,经常盯着路过的风,也忘了听猎人的枪声。
2.与海为邻,住在无尽蓝的隔壁,却无壁可隔,一无所有,却拥有一切。
3.只要想起一生中后悔的事,梅花便落满了南山。
4.每个人都属于有自己的一片森林,我们也许从来不曾去过,但是它一直在那里,总会在那里。迷失的人迷失了,相逢的人,会再相逢。
原文链接:https://blog.csdn.net/qq_36605165/article/details/114438620
作者:代码搬运工
链接:http://www.qianduanheidong.com/blog/article/33542/31c504d74af8e3849e70/
来源:前端黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 前端黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-3
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!