重庆商城网站建设公司,公司简介100字范文,嵌入式培训一般多少钱,企业网站建设制作公司在日益复杂和多元的 Web 业务背景下#xff0c;前端工程化经常会被提及。工程化的目的是高性能、稳定性、可用性、可维护性、高效协同#xff0c;只要是以这几个角度为目标所做的操作#xff0c;都可成为工程化的一部分。工程化是软件工程中的一种思想#xff0c;当下的工程…在日益复杂和多元的 Web 业务背景下前端工程化经常会被提及。工程化的目的是高性能、稳定性、可用性、可维护性、高效协同只要是以这几个角度为目标所做的操作都可成为工程化的一部分。工程化是软件工程中的一种思想当下的工程化可以分为四个方面模块化、组件化、规范化、自动化。接下来此文会浅析其中的一个部分——模块化。
一、什么是模块化
模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程每个模块完成特定的子功能所有的模块按某种方法组装起来成为一个整体从而完成整个系统所要求的的功能。
块的内部数据与实现是私有的只是向外部暴露了一些接口和方法与外部其他模块通信。
二、为什么需要模块化
在早期的网页中一些页面和样式都很简单有极少交互行为一个页面也不会依赖很多文件所以逻辑代码会很少。但随着 Web 技术的发展各种交互以及新技术使网页越来越丰富逐渐地我们前端工程师的任务也越来越重前端代码急速上涨、复杂度也在逐步升高越来越多的业务逻辑和交互都放在 Web 层实现代码一多各种命名冲突、代码冗余、文件间依赖变大等等一系列问题都会浮现而且也会导致后期难以维护。
在这些问题上有很多语言已经有了很多实践经验那就是模块化。因为小的、组织良好的代码远比庞大的代码更易理解和维护于是前端也开启了模块化历程。
三、JS 模块化规范
CommonJS规范
2009年美国程序员 Ryan Dahl 以 CommonJS 规范为基础创造了 node.js 项目将 JS 语言用于服务器端编程为前端奠基此后nodejs 就成为了 CommonJS 的代名词。
1. 概述
CommonJS 规范中规定每个文件就是一个独立的模块有自己的作用域模块的变量、函数、类都是私有的对其他文件不可见。在服务器端模块的加载是运行时同步加载的在浏览器端模块需要提前编译打包处理。
2. 特点
1所有代码都运行在模块作用域。不会污染全局作用域。
2模块可以多次加载但只会在第一次加载时运行一次然后运行结果就被缓存以后再加载就直接读取缓存结果要想让模块再次运行必须清除缓存。
3模块加载的顺序按照其在代码中出现的顺序。
3. 基本语法
外部想要调用必须使用 module.exports 主动暴露而在另一个文件中引用则需要使用 require()如下
// moduleA.js
var a 1;
var b 2;
var add function() {return a b
}module.exports.a a;
module.exports.b b;
module.exports.add add;// 引用
var moduleA require(./moduleA.js);console.log(moduleA.a) // 1
console.log(moduleA.b) // 2
console.log(moduleA.add(a,b)) // 3
4. 加载机制
CommonJS 模块的加载机制是输入的是被输出的值的拷贝。也就是说一旦输出一个值模块内部的变化就影响不到这个值了。
5. 总结
CommonJS 是模块化的社区标准而 Nodejs 就是 CommonJS 模块化规范的实现它对模块的加载时同步的也就是说只有引入的模块加载完成才会执行后面的操作在 Node 服务端应用当中模块一般存在本地加载较快同步问题不大在浏览器中就不太合适了如果一个很大的项目所有的模块都要同步加载那可想而知体验感是极差的所以还需要异步模块化方案。
AMD规范
AMD异步模块定义是专门为浏览器环境设计的是非同步加载模块允许指定回调函数。
1. 基本语法
define(id?: String, dependencies?: String[], factory: Function|Object)
id模块的名字字符串类型可选参数dependencies依赖的模块列表一个字符串数组可选参数。每个依赖的模块的输出将作为参数一次传入 factory 中如果没有指定 dependencies那它的默认值是 [require, exports, module]factory包裹了模块的具体实现可以为函数或对象如果是函数返回值就是模块的输出接口或者值
使用 define() 定义暴露模块引入模块需要使用 require()如下
// 定义没有依赖模块
define(function() {return 模块
})// 定义有依赖模块
define([module1, module2], function(m1, m2) {return 模块
})// 引入使用模块
require([module1, module2], function(m1, m2) {使用m1/m2
})2. RequireJS
RequireJS 是一个遵守 AMD 规范的工具库用于客户端的模块管理。它就是通过 define 方法将代码定义为模块通过 require 方法实现代码的模块加载使用时需要下载和导入也就是说在浏览器中想要使用 AMD 规范时先在页面中引入 require.js 就可以了。
3. 总结
AMD 模块定义的方法非常清晰不会污染全局环境能够清楚地显示依赖关系。AMD 模式可以用于浏览器环境并且允许非同步加载模块也可以根据需要动态加载模块RequireJS 就是 AMD 的标准化实现。
CMD规范
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出而 CMD 规范以及 SeaJS 在国内曾经十分被推崇原因不只是因为它足够简单方便更是因为 SeaJS 的作者是阿里的玉伯大佬所写。
1. 概述
CMD 规范专门用于浏览器端模块的加载是异步的模块使用时才会加载执行。CMD 规范整合了CommonJS 和 AMD 规范的特点。在Sea.js 中所有 JavaScript 模块都遵循 CMD 模块定义规范。
2. 基本语法
define(factory: Function|Object|String)
在 CMD 规范中一个模块就是一个文件define 是一个全局函数用来定义模块。define 接收 factory 参数它可以是一个函数也可以是一个对象或字符串。
1参数为函数时如下
// 定义没有依赖的模块
define(function(require, exports, module) {exports.xxx value;module.exports value;
})// 定义有依赖的模块
define(function(require, exports, module) {// 引入依赖模块同步var module1 require(./module1)// 引入依赖模块异步require.async(./module2, function(m2) {...})// 引入依赖模块条件if(status) {var m3 require(./module3)}// 暴露模块exports.xxx value
})// 引入模块
define(function(require) {var m1 require(./module1)var m2 require(./module2)m1.show()m2.show()
})
require 是一个方法接收模块标识作为唯一参数用来获取其他模块提供的接口exports是一个对象用来向外提供模块接口module是一个对象上面存储了与当前模块相关联的一些属性和方法
2参数为对象和字符串时表示模块的接口就是该对象和字符串如下
// factory为JSON数据对象
define({name: bao});// factory为字符串模板
define(my name is {{name}});
3. 核心实现
对于 CMD 规范下的 SeaJS同 AMD 规范下的 RequireJS 一样都是浏览器端模块加载器两者很相似但又有明显不同CMD 与 AMD 的区别是 AMD 推崇依赖前置而 CMD 推崇依赖就近。
CMD 推崇尽可能的懒加载也称为延迟加载即在需要的时候才加载。对于依赖模块AMD 是提前执行CMD 是延迟加载两者执行的方式不同AMD 执行过程中会将所有依赖前置执行也就是在自己的代码逻辑开始前全部执行而 CMD 如果 require 引入了整个逻辑并未使用这个依赖或未执行到逻辑使用它的地方前是不会执行的。
ES6模块化
2015年6月ESMAScript2015 也就是我们所说的 ES6 发布了JS 终于在语言标准的层面上实现了模块功能设计思想是尽量的静态化使得在编译时就能确定模块的依赖关系以及其输入和输出的变量不像 CommonJS、AMD 之类的需要在运行时才能确定成为浏览器和服务器通用的模块解决方案。
所以说在 ES6 之前 JS 是没有官方的模块机制的ES6 在语言标准的层面上实现了模块化功能而且实现的相当简单旨在成为浏览器和服务器通用的模块化解决方案。
1. 基本语法
export 命令用于规定模块的对外接口import 命令可以输入其他模块提供的功能。export 可以导出的是对象中包含多个属性、方法提供的 export default 命令只能导出一个可以不具名的函数。
1使用 export 导出import 导入如下
// 定义模块module1
var count 0;
var add function(a, b) {return a b;
}export {count, add};// 引用模块
import {count, add} from ./module1;function test() {return add(1, count);
}2使用 import 命令导入时需要知道所要加载的变量名或函数名否则无法加载。export default 命令可以为模块默认输出如下
// 导出module2
export default function() {console.log(this is a msg);
}// 引入
import msgFun from ./module2;msgFun(); // this is a msg
2. ES6 模块与 CommonJS 模块的差异
CommonJS 模块输出的是一个值的拷贝ES6 模块输出的是值的引用CommonJS 模块是运行时加载ES6 模块是编译时输出接口
第二个差异是因为 CommonJS 加载的是一个对象该对象只有在脚本运行完才会生成而 ES6 模块不是对象它的对外接口只是一种静态定义在代码静态解析阶段就会生成。
ES6 模块运行机制与 CommonJS 运行机制不一样。js 引擎对脚本静态分析的时候遇到模块加载指令后会生成一个只读引用等到脚本真正执行的时候才会通过引用模块中获取值在引用到执行的过程中模块中的值发生变化导入到这里也会跟着发生变化。ES6 模块是动态引入的并不会缓存值模块里总是绑定其所在模块。
四、总结
对于 JS 模块化上述方案都在解决几个同样的问题
全局变量的污染命名冲突繁琐的文件依赖
不同的模块化手段都在致力于解决这些问题。前两个问题其实很好解决使用闭包配合立即执行函数就可以实现难点在于文件依赖关系的梳理以及加载。CommonJS 在服务端使用 fs 模块同步读取文件而在浏览器中不管是使用 AMD 规范的 RequireJS 还是 CMD 规范的 SeaJS其实都是使用动态创建 script 标签方式加载在依赖加载完毕之后再执行。
ESM 作为语言标准层面的模块化方案不需要我们额外引入用于模块化的三方包完全可以取代 CommonJS 和 AMD 等规范成为浏览器和服务器通用的模块解决方案。抛开兼容问题绝对是最好的选择也是未来趋势这点在 Vite 上就足以证明。