网站首页 > 技术文章 正文
前端模块化
省流:chatGPT总结
该文章主要讲述了前端模块化的发展历史和各个阶段的技术方案,包括无模块化(IIFE)、CommonJS、AMD、CMD、ESModule、UMD。
其中,无模块化时期的文件拆分是最基础的模块化,但也存在函数命名冲突的问题;
IIFE 是现代模块化的基石,利用函数的块级作用域进行隔离,可以控制作用域;
CommonJS 文件即模块,模块加载同步,适用于服务器端 node,浏览器端使用 webpack 或 browserfy。
最后,各种模块化技术方案都是为了更好地满足前端代码管理、组织、通信的需求,模块已经成为了代码管理/编译、业务分离的基本单元。
一、参考资料
- es-module-history
- 网页性能管理详解
- defer和async的区别
- 可能是最详细的UMD模块入门指南
- 在浏览器中使用 ECMAScript Modules
二、发展历史
- js的设计之初就是为了满足简单的页面设计+表单提交,并无模块化 or 命名空间的概念
- 而是实实在在的需求推进了所有技术的演进,模块化也是。
- 站在前端发展的上帝视角来看,随着前端的能力在纵深都得到增强之后,迫切的需要更好的代码管理、组织、通信的模式,各种模块化的技术方案开始出现。
- 现如今模块已经成为了代码管理/编译,业务分离的基本单元。
总的发展历史:
- 无模块化(IIFE) -> CommonJS -> AMD -> CMD -> ESModule、UMD
1.无模块化
需求:
- 开始需要在页面中加载不同的js:动画、组件、格式化
- 多种js分布在不同的文件中
- 不同的文件又被同一个模块中引用
文件拆分是最基础的模块化
Bash
<script src='jq.js'></script>
<script src='main.js'></script>
<script src='dep1.js'></script>
// ...
问题:这个时期函数命名可能会冲突,影响到其他人写的代码
引出的问题:
- script标签的参数 - async & defer 的区别?
总结: 主要是对标签下载和执行时机的控制
- 普通标签 - 遇到标签就去下载,下载完毕之后立刻去解析代码并执行,这个时候会阻塞GUI线程渲染
- defer - 遇到标签之后异步下载,下载完成之后等待其他标签解析完成之后开始执行(在主线程解析完成之后才执行,降低脚本的优先级,保持用户体验,使用相对较多)
- async - 遇到标签之后异步下载,下载完成之后立即执行并阻塞渲染,执行完成之后继续渲染(异步下载结束之后立即执行,不保证脚本执行顺序,一般用来给那些不需要任何依赖的脚本使用)
- 拓展
- ESM 默认是通过 defer 的方式加载的,所以是不需要在 script 标签上加 defer 属性的
横向拓展
- 兼容性如何? > IE9
- 引导内容
- 浏览器渲染原理
- 同步异步的原理(Promise,任务队列)
- 模块化加载原理
- 产生的问题
- 污染全局作用域 => 不利于大型项目的开发以及多人团队的共建
2.IIFE
IIFE主要是开始对作用域的把控 利用函数的块级作用域进行隔离 可以说IIFE是现代模块化的基石
Bash
(function($){
console.log($)
return {
data:[]
}
})(jQuery) //注入对象
3.Commonjs(cjs)
- 服务器端node,浏览器端webpack|browserfy
- 文件即模块
- 模块加载同步
- 服务器模块加载是运行时同步加载
- 浏览器模块加载是提前编译打包处理
- exports = module.exports
- 注意:不能直接给exports赋值,会导致与module断开引用
- 使用require进行引入
- 缓存
- cjs在引用文件的时候,会将文件执行一遍,然后将结果通过浅拷贝的方式写入全局缓存中
- 后续再次require同一个文件时,直接从缓存中读取,不会重新执行模块文件
// a.js
var name = 'morrain'
var age = 18
exports.name = name
exports.getAge = function(){
return age
}
// b.js
var a = require('a.js')
console.log(a.name) // 'morrain'
a.name = 'rename'
var b = require('a.js')
console.log(b.name) // 'rename'
- 模块输出的结果是值的拷贝,一但输出,模块内部变化后,无法影响之前的引用,而ESModule是引用拷贝
// a.js
var name = 'morrain'
var age = 18
exports.name = name
exports.age = age
exports.setAge = function(a){
age = a
}
// b.js
var a = require('a.js')
console.log(a.age) // 18
a.setAge(19)
console.log(a.age) // 18
- cjs在运行时加载,ESM是编译时加载
- 缺点:不支持异步
- cjs更偏向于服务端,因为服务端I/O能力强,所以CMJ是同步的方法
- ESM时机遇编译时的,所以支持异步能力
4.AMD
AMD(Asynchronous module definition)异步的模块定义 解决了Commonjs不支持异步的缺点,可以在浏览器端运行
经典代表:require.js
使用方法:
// define来定义模块
define(id, [depends], callback);
// require进行加载
require([module], callback);
示例:
//提前加载执行顺序
// RequireJS
define('a', function () {
console.log('a load')
return {
run: function () { console.log('a run') }
}
})
define('b', function () {
console.log('b load')
return {
run: function () { console.log('b run') }
}
})
require(['a', 'b'], function (a, b) {
console.log('main run') //
a.run()
b.run()
})
// a load
// b load
// main run
// a run
// b run
缺点:
- 在代码运行时,会先递归的找出所有的依赖,然后将依赖放到前面加载
- 如果依赖过多,项目可能会变慢,引入成本升高
引出的问题:
- 如果现在AMD中兼容CJS的代码怎么办?
define('amdModule', [], require => {
const dep1 = require('./dep1');
const dep2 = require('./dep2');
// 业务逻辑……
})
5.CMD
CMD(Common Module Definition-通用模块定义)推崇依赖后置,也就是按需执行 CMD解决了AMD依赖前置导致的引入成本过高的问题 整合了CJS和AMD的特点,浏览器端运行
经典代表:Sea.js
// 引入require
var fs = require('fs'); //同步
require.async('./module3', function (m3) {}) //异步
// sea.js,按需引入
define('a', function (require, exports, module) {
console.log('a load')
exports.run = function () { console.log('a run') }
})
define('b', function (require, exports, module) {
console.log('b load')
exports.run = function () { console.log('b run') }
})
define('main', function (require, exports, module) {
console.log('main run')
var a = require('a')
a.run()
var b = require('b')
b.run()
})
seajs.use('main')
// main run
// a load
// a run
// b load
// b run
缺点:
- 依赖打包,加载逻辑存在于每个模块中
- 扩大了模块体积,同时功能上依赖编译
6.UMD
UMD (Universal Module Definition)就是一种通用模块定义规范,让你的模块能在所有运行环境中使用,如CommonJS, AMD, CMD
(function(root, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
console.log('是commonjs模块规范,nodejs环境')
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
console.log('是AMD模块规范,如require.js')
define(factory)
} else if (typeof define === 'function' && define.cmd) {
console.log('是CMD模块规范,如sea.js')
define(function(require, exports, module) {
module.exports = factory()
})
} else {
console.log('没有模块环境,直接挂载在全局对象上')
root.umdModule = factory();
}
}(this, function() {
return {
name: '我是一个umd模块'
}
}))
7.ESM
ESModule是伴随着ES6推出的原生模块化解决方案 import输入、export输出
- 支持异步加载
- 编译时加载,支持静态分析
- 更好的支持chunk和tree shaking
- 支持动态导入(按需加载)import().then()
- 支持import.meta获取模块元数据
- 上一篇: 【JS基础】一文看懂前端模块化规范
- 下一篇: 详细讲解前端模块化开发与开发规范
猜你喜欢
- 2024-10-04 前端开发周报:JS 模块化和web图片优化
- 2024-10-04 前端面试:JavaScript 模块化开发怎么做?
- 2024-10-04 模块化和组件化区别 模块化 组件化 插件化
- 2024-10-04 前端系列——DOS常用命令以及npm的模块化管理
- 2024-10-04 这款模块化Mac Pro帅呆了:前端加入TouchBar
- 2024-10-04 VUE前端工程化、组件化、模块化有自己的理解和总结
- 2024-10-04 JS模块化 - 浅谈 CommonJS require 函数实现
- 2024-10-04 前端模块化(CommonJS) 前端模块化和组件化的理解
- 2024-10-04 好程序员web前端学习路线分享css模块化方案
- 2024-10-04 极简模块化前端UI框架 layui 前端模块化开发框架
你 发表评论:
欢迎- 591℃几个Oracle空值处理函数 oracle处理null值的函数
- 584℃Oracle分析函数之Lag和Lead()使用
- 571℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 569℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 565℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 557℃【数据统计分析】详解Oracle分组函数之CUBE
- 542℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 537℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端懒加载 (49)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)