前端工程化之模块化

news2024/11/24 5:47:01

模块化的背景

  • 前端模块化是一种标准,不是实现
  • 理解模块化是理解前端工程化的前提
  • 前端模块化是前端项目规模化的必然结果

什么是前端模块化?

前端模块化就是将复杂程序根据规范拆分成若干模块,一个模块包括输入和输出。而且模块的内部实现是私有的,它通过对外暴露接口与其他模块通信,而不是直接调用。现在在HTML文件中可以引用的script包括脚本和模块,其中模块具有更高的开发效率(可读性强、复用性高),而脚本具有更高的页面性能,因为模块相对文件多,加载速度慢。需要注意的是模块在浏览器中运行会存在兼容性的问题,在script声明type="module"即可使用ES Module模块化规范。

模块化的进化过程

  1. 全局function模式,将不同的功能封装成不同的函数,缺陷是容易引发全局命名冲突。比如刚开始在一个js文件中中定义一个一个的函数作为模块,但是这些函数挂载windows上的,污染全局作用域,我在另一个js文件中定义同名的函数就会导致命名冲突,而且无法管理模块依赖关系,进而早起模块化完全依靠约定。
    function api(){
        return {
            data:{
                a:1,
                b:2
            }
        }
    }
  1. 全局namespace模式,约定每个模块暴露一个全局对象,所有的模块成员暴露在对象下,缺点是外部能够修改模块内部数据。将函数放在对象模块中挂载到window上,但是这样外部也能修改对象内部的属性。
var __Module={
    api(){
        return {
            data:{
                a:1,
                b:2
            }
        }
    }
}

那么我们可以通过函数作用域加闭包的特性来解决。

  1. IIFE模式,将模块成员放在函数私有作用域中,通过自执行函数创建闭包,对于暴露给外部的成员通过挂载到全局对象这种方式实现。这种方式实现了私有成员的概念,缺陷是无法解决模块间相互依赖问题。
(function(){
    var a =1;
    function getA(){
        return a;
    }
    function setA(a){
        window.__module.a = a;
    }
    window.__module = {
        a,
        getA,
        setA
    }
})();
  1. IIFE模式增强,支持传入自定义依赖,缺陷是多依赖传入时,代码阅读困难,无法支持大规模的模块化开发,无特定语法支持,代码简陋。
(function(global){
    var a =1;
    function getA(){
        return a;
    }
    function setA(a){
        window.__module.a = a;
    }
    global.__Module_API = {
        a,
        getA,
        setA
    }
})(window)

(function(global,moduleAPI){
    global.__Module = {
        setA:moduleApi.setA
    }
})(window,window.__Module_API)

以上就是早期没有工具和规范的情况下对模块化的落地方式。

通过约定的方式去做模块化,不同的开发者和项目会有不同的差异,我们就需要一个标准去规范模块化的实现方式。针对与模块加载的方式,以上方法都是通过script标签手动引入的方式,模块加载不受代码的控制,时间久了维护会非常麻烦。那么就需要一个模块化的标准和自动加载模块的基础库。

CommonJS规范

介绍

CommonJS规范是Node.js默认模块规范,他规定了每个文件就是一个模块,每个模块有自己的作用域,它的模块加载采用同步加载方式,加载模块的时候必须模块加载完成后再执行后续的代码。它通过require来加载模块,通过exports或module.exports输出模块。

特点

  • 所有代码都运行在模块作用域,不会污染全局作用域
  • 模块可以多次加载,第一次加载时会运行模块,模块输出结果会被缓存,再次加载时,会从缓存结果中直接读取模块输出结果
  • 模块加载的顺序是按照其在代码中出现的顺序
  • 模块输出的值是值得拷贝,类似IIFE方案中的内部变量

1.png

打包

CommonJS要想在浏览器中使用的话需要使用browserify来打包。

browserify打包原理主要是:通过自执行函数实现模块化,将每个模块编号,存入一个对象,每个模块标记依赖模块。它内部实现了require方法,核心是通过call方法调用模块,并传入require、module、exports方法,通过module存储模块信息,通过exports存储模块输出信息。

AMD规范

AMD(Asynchronous Module Definition)规范采用异步加载模块,允许指定回调函数。Node模块通常都位于本地,加载速度快,所以适用于同步加载,但是在浏览器运行环境中,用同步加载会阻塞浏览器渲染,所以在浏览器环境中,模块需要请求获取,适用于异步加载。因此就诞生了AMD规范,用于异步加载,其中require.js就是AMD的一个具体实现库。目前不管是CMD或是AMD用的都很少,在Node开发中通常用CommonJS规范,在浏览器中用ES Module规范。

引用了require.js之后,它会全局定义一个define函数和require函数,所有的模块要用define去定义。define有三个参数:第一个模块名,第二个是数组用于声明模块依赖项,第三个是一个函数,函数参数与第二个参数依赖项一一对应,每一个参数为依赖项导出的成员。函数的作用可以理解为当前模块提供私有的空间,如果要向外导出成员可以通过return来实现。

define('module1',['jquery','./module2'],function($,module2){
    return {

    }
});

而require函数则用来加载模块,当调用require函数的时候,其内部会自动创建script标签来发送脚本文件的请求,并执行相对应的模块代码。

require(['./module1'],function (module1){
    module1.start();
})

目前绝大部分第三方库都支持AMD规范,但是因为要使用频繁使用require、define导致AMD使用起来相对复杂。另外如果模块代码划分的很细致,那么在同一个页面中,JS文件的请求次数相对多,导致页面效率低下。

CMD规范

CMD规范整合了CommonJS和AMD的优点,通过异步加载模块。CMD专门用于浏览器端,其中淘宝的sea.js就是CMD规范的一个具体实现库。

//CMD规范,类似CommonJS规范
define(function(require,exports,module){
    //通过require引入依赖
    var $ = require('jquery')
    //通过exports 或者module.exports对外暴露成员
    module.exports = function(){

    }
})

ESModule规范

目前模块化标准规范已经非常成熟统一了,在Node.js中遵循CommonJS组织模块,在浏览器端使用ESModule规范。目前前端模块化基本统一为CommonJS + ESModule规范。

ESModule规范是目前应用最为广泛的模块化规范,它的设计理念是在编译时就确定模块依赖关系及输入输出。而CommonJS和AMD由于采用闭包的形式必须在运行时才能确定依赖和输入、输出。ESM通过import加载模块,通过export输出模块。

使用ESModule,通过给script标签添加type=module属性,就可以以ESModule的标准执行JS代码。

<script type="module"></script>

ESModule有如下几个特性:

  • ESM自动采用严格模式,忽略’use strict’
  • 每个ESM都是运行在单独的私有作用域中
  • 在ESM中外部的JS模块是通过CORS的方式请求的
  • ESM的script标签会延迟执行脚本,等同于defer属性

注意

在ESM中 export {}它只是导出成员的语法,不是导出字面量对象,import {} from 'xxx',也是语法,不是解构对象,如果想导出对象可以使用export default {},使用import xxx from 'xxx'获取对象。其次通过export {name}导出的name值是引用的不是拷贝,它的值会受到导出模块内部修改的影响,而且name只是可读不能修改。

导入模块的用法

  1. 引用路径必须是完整的文件名称不能省略.js扩展名,相对路径要是用./,可以使用绝对路径和url的方式。
import {name} from './module.js';
import {name} from '/xxx/module.js';
import {name} from 'http://localhost:3000/module.js';
  1. 加载模块,并不导出成员
import './xxx.js';
  1. 使用 * 的方式提取所有的模块,使用as存放在对象中
import * as mod from './xxx.js';
  1. 使用import函数,在运行阶段动态导入模块
import('./xxx.js').then((module)=>{
    console.log(module);
});
  1. 同时导出命名成员和默认成员
//a.js
...
export {name,age}
export default 'title'

//b.js
import {name,age,default as title} from './a.js';
//或者
import title,{name,age} from './a.js';

导出模块的用法

通过export将目录下散落的模块在index文件中导出,方便外部使用

export {name,age} from './module.js';

如果要使用commonjs规范的话就要将对应的js文件改为.cjs后缀名。

Node.js新版本支持

在Node.js新版本中,在package.json中添加type="module"字段就可以使用ESModule规范了。

CommonJS和ESModule规范对比

  • CommonJS模块输出的是值得拷贝,ES6模块输出的是值得引用
  • CommonJS模块是运行时加载,ES6模块是编译时输出接口
  • CommonJS是单个值导出,ES6 Module可以导出多个
  • CommonJS是同步加载,ES6 Module是支持异步加载,通过import()来实现
  • CommonJS的this是当前模块,ES6 Module的this是undefined
  • CommonJS和ES6 Module的语法不同
  • CommonJS是Node.js默认采用的模块化规范,Node14后默认支持ESM。ESM是浏览器默认采用的模块化规范。

模块打包工具

模块化解决了我们在复杂应用当中的代码组织问题,但是随着引入模块化,又产生了新的问题。例如以下问题:

  • ES Modules存在环境兼容问题
  • 通过模块化的方式划分的模块文件比较多,且网络请求频繁
  • 所有的前端资源都需要模块化,不仅仅是JS需要模块化,像html和css都需要模块化

针对第一个问题,我们需要一个工具将代码进行编译,在开发阶段将新特性的代码转换为兼容绝大多数环境的代码。

32.png

针对第二个问题,将模块化的文件打包到一块。

33.png

针对第三个问题,将其他资源通过代码的方式进行控制,统一去维护。

34.png

针对以上问题就需要模块打包工具来解决,例如webpack、parcel和rollup。

通过使用webpack就可以将零散的代码打包到js文件中,对于那些有环境兼容问题的代码就可以通过模块加载器loader去做兼容转换,他还具有代码拆分的能力,可以按我们的需要去打包,不用担心将所有的代码打包到一块,导致文件比较大的问题。我们可以将应用加载过程中初次运行所必须的模块打包到一起,其他的模块单独存放,等到应用运行过程中需要某个模块,再异步加载这个模块,从而实现增量加载或者渐进式加载。

对于资源文件webpack支持在JS中以模块化的方式去载入任意类型的资源文件。打包工具解决的是前端整体的模块化,并不单指JavaScript模块化。

看完觉得对您有所帮助别忘记关注呦

weixin.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/923313.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++学习之内存管理

目录 1.c/c内存分布 2.new与delete/malloc与free c内存管理方式&#xff1a; new/delete操作内置类型&#xff1a; new/delete操作自定义类型 operator new与operator delete函数 new和delete的实现原理 内置类型 自定义类型 malloc/free和new/delete的区别 1.c/c内存分…

Linux——初始

linux是一个操作系统&#xff0c;目前主流就是在服务器后端被选作服务器的操作系统来使用&#xff0c;所以我们没有直接接触到。 Linux的历史和概念 先是国家投钱给科研技术人员&#xff0c;科研技术人员将科研成果部分投入生活用品卖给老百姓&#xff0c;老百姓购买产品同时还…

Java课题笔记~ 综合案例

3.综合案例 3.1 功能介绍 以上是我们在综合案例要实现的功能。除了对数据的增删改查功能外&#xff0c;还有一些复杂的功能&#xff0c;如 批量删除、分页查询、条件查询 等功能 批量删除 功能&#xff1a;每条数据前都有复选框&#xff0c;当我选中多条数据并点击 批量删除 按…

YOLOv5算法改进(4)— 添加CA注意力机制

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。注意力机制是近年来深度学习领域内的研究热点&#xff0c;可以帮助模型更好地关注重要的特征&#xff0c;从而提高模型的性能。在许多视觉任务中&#xff0c;输入数据通常由多个通道组成&#xff0c;例如图像中的RGB通道或…

村口的人家排放污水,污水浸染了整个村子,怎么办

从前有一个很不错的村子里&#xff0c;村子里有很多户人家&#xff0c;随着生活水平越来越好&#xff0c;房子也修起来了&#xff0c;柏油马路也宽敞了&#xff0c;大家进出村子&#xff0c;都要走那条马路&#xff0c;要不就出不去。 目录 1. 修厕所 2. 村口的日家 3. 告诉…

商城的TPS与并发用户数是如何换算的?

商城的TPS与并发用户数的换算关系可以通过以下公式计算&#xff1a; TPS 并发用户数 / 平均事务响应时间 其中&#xff0c;平均事务响应时间是指系统处理一个事务所需的平均时间。 下面是商城性能测试的一些用例示例&#xff1a; 用户登录&#xff1a; 目标&#xff1a;测…

4.物联网LWIP之C/S编程,stm32作为服务器,stm32作为客户端,代码的优化

LWIP配置 服务器端实现 客户端实现 错误分析 一。LWIP配置&#xff08;FREERTOS配置&#xff0c;ETH配置&#xff0c;LWIP配置&#xff09; 1.FREERTOS配置 为什么要修改定时源为Tim1&#xff1f;不用systick&#xff1f; 原因&#xff1a;HAL库与FREERTOS都需要使用systi…

【Python原创毕设|课设】基于Python Flask的上海美食信息与可视化宣传网站项目-文末附下载方式以及往届优秀论文,原创项目其他均为抄袭

基于Python Flask的上海美食信息与可视化宣传网站&#xff08;获取方式访问文末官网&#xff09; 一、项目简介二、开发环境三、项目技术四、功能结构五、运行截图六、功能实现七、数据库设计八、源码获取 一、项目简介 随着大数据和人工智能技术的迅速发展&#xff0c;我们设…

【JavaEE进阶】MyBatis表查询

文章目录 一. 使用MyBatis完成数据库的操作1. MyBatis程序中sql语句的即时执行和预编译1.1 即时执行&#xff08;${}&#xff09;1.2 预编译&#xff08;#{}&#xff09;1.3 即时执行和预编译的优缺点 2. 单表的增删改等操作2.1 增加操作2.2 修改操作2.3 删除操作2.4 like(模糊…

星际争霸之小霸王之小蜜蜂(六)--让子弹飞

目录 前言 一、添加子弹设置 二、创建子弹 三、创建绘制和移动子弹函数 四、让子弹飞 五、效果 总结 前言 小蜜蜂的基本操作已经完成了&#xff0c;现在开始编写子弹的代码了。 一、添加子弹设置 在我的预想里&#xff0c;我们的小蜜蜂既然是一只猫&#xff0c;那么放出的子弹…

基于小波神经网络的短时交通流量预测Matlab代码

1案例背景 1.1小波理论 小波分析是针对傅里叶变换的不足发展而来的。傅里叶变换是信号处理领域中应用最广泛的一种分析手段,然而它有一个严重不足,就是变换时抛弃了时间信息,通过变换结果无法判断某个信号发生的时间,即傅里叶变换在时域中没有分辨能力。小波是长度有限、平均为…

分布式与微服务相关知识

分布式与微服务 1.zookeeper是什么2.zookeeper保证数据一致性3.zookeeper的快速领导者选举是怎么实现的4.CAP理论5.BASE理论6.分布式id生成方案&#xff08;1&#xff09;UUID&#xff08;2&#xff09;数据库自增序列&#xff08;3&#xff09;Leaf-segment&#xff08;4&…

Linux下的系统编程——vim/gcc编辑(二)

前言&#xff1a; 在Linux操作系统之中有很多使用的工具&#xff0c;我们可以用vim来进行程序的编写&#xff0c;然后用gcc来生成可执行文件&#xff0c;最终运行程序。下面就让我们一起了解一下vim和gcc吧 目录 一、vim编辑 1.vim的三种工作模式 2.基本操作之跳转字符 &a…

实现外网访问本地服务

最近开发需要其他项目组的人访问我本地服务测试,但又不在同一个地方,不能使用内网访问,所以需要外网访问本地服务功能. 条件: 1.需要一台具备公网IP的服务器 我用的服务器是windows,电脑也是Windows系统 2.下载frp 软件,只需要下载一份就可以了,分别放到服务器上和本地目录既…

2011-2021年全国各省绿色创新效率数据(原始数据+测算结果)

2011-2021年全国各省绿色创新效率数据&#xff08;原始数据测算结果) 2011-2021年全国各省绿色创新效率 1、时间&#xff1a;2011-2021年 2、范围&#xff1a;全国31省市 3、来源&#xff1a;各省年鉴、科技年鉴、环境年鉴 4、指标&#xff1a;地区、编号、年份、R&D人…

设计模式大白话——命令模式

命令模式 一、概述二、经典举例三、代码示例&#xff08;Go&#xff09;四、总结 一、概述 ​ 顾名思义&#xff0c;命令模式其实和现实生活中直接下命令的动作类似&#xff0c;怎么理解这个命令是理解命令模式的关键&#xff01;&#xff01;&#xff01;直接说结论是很不负责…

树形结构的快速生成

背景 相信大家都遇到过树形结构&#xff0c;像是文件列表、多级菜单、评论区的设计等等&#xff0c;我们都发现它有很多层级&#xff0c;第一级可以有多个&#xff0c;下边的每一个层级也可以有多个&#xff1b;有的可以设计成无限层级的&#xff0c;有的只能设计成两级。那么…

工程师使用IT服务台软件可以解决哪些问题?

现如今企业数字化建设已初具规模&#xff0c;业务系统基本已告一段落&#xff0c;而下一步关注的重点则从技术转向管理&#xff0c;如何能让这些系统更好运行起来&#xff0c;如何提高管理效率已是重中之重。在此向您推荐一款高效的IT服务管理工具——ServiceDesk Plus&#xf…

elementui的el-tabs标签页样式修改

一、官网样式&#xff1a; 二、修改样式 1.去掉下划线 效果&#xff1a; 代码: /* 去掉tabs标签栏下的下划线 */ ::v-deep .el-tabs__nav-wrap::after {position: static !important;/* background-color: #fff; */ } 2.改变下划线颜色 效果&#xff1a; 代码&#xff1a;…

使用VisualStudio制作上位机(三)

文章目录 使用VisualStudio制作上位机(三)第三部分:GUI内部函数设计使用VisualStudio制作上位机(三) Author:YAL 第三部分:GUI内部函数设计 这一部分,主要实现CAN设备的打开 将CAN厂家的二次开发文件添加到工程里调用相关函数打开或关闭CAN首先,添加“类文件”,类主…