前端模块化开发

article/2025/10/22 9:57:33

前端模块化开发

什么是模块化?
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、分解和更换的单元

编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并互相依赖的多个小模块,是将不同功能的函数封装起来,并提供使用接口,他们彼此之间互不影响,想要什么功能,就加载什么模块,可以实现按需加载。

简单来说,模块化就是把系统分成各个独立的部分,每个部分单独实现功能,将系统分割成多个可独立的功能部分。

重点:模块化的好处是什么?(为什么要用模块化)
当我们在一个html文件中引入了多个js文件,此时需要依赖多个模块,就意味着会发送多个请求,从而导致请求过多,同时我们不知道他们的具体依赖关系是什么,也就是说很容易因为不了解他们之间的依赖关系导致加载先后顺序出错。将不同的功能封装成不同的全局函数时会导致全局变量污染, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系,这也间接导致我们所写出的代码难以维护。

模块化作用
1.高内聚低耦合,有利于团队作战,当项目很复杂的时候,将项目划分为子模块分给不同的人开发,最后再组合在一起,这样可以降低模块与模块之间的依赖关系体现低耦合,模块又有特定功能体现高内聚。

2.可重用,方便维护,模块的特点就是有特定功能,当两个项目都需要某种功能的时候,我们定义一个特定的模块来实现该功能,这样只需要在两个项目中都引入这个模块就能够实现该功能,不需要书写重复性的代码;并且当需求变更该功能需要改变的时候,我们直接修改该模块,这样就能够修改所有项目的功能,维护起来很方便。

3、通过 exports 暴露接口。这意味着不需要命名空间了,更不需要全局变量。这是一种彻底的命名冲突解决方案。

4、通过 require 引入依赖。**这可以让依赖内置,开发者只需关心当前模块的依赖,其他事情 Sea.js(CMD)/ Require.js (AMD)都会自动处理好

commonjs依赖关系

CommonJS是node.js遵守的模块化规范, commonjs 规范应用于 nodejs 应用中,在 nodejs 应用中每个文件就是一个模块,拥有自己的作用域,文件中的变量、函数都是私有的,与其他文件相隔离。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。主要用于服务器端。

nodejs下载与安装

链接: ==nodejs下载与配置==
注意事项:
1.nodejs有兴趣的可以跟博客配置,仅使用全部下一步安装完并检查是否安装成功即可(建议下最新版)
2.在vscode上安装code runner扩展
3.做完上述内容就可以在vscode上直接运行js代码,使用方法为在js文件上右击选择run code,或者右上角的运行按钮,或者ctrl+alt+n

conmonjs

CommonJS规范规定:
每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
在模块中使用global 定义全局变量,不需要导出,在别的文件中可以访问到。
每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。
通过 require加载模块,读取并执行一个js文件,然后返回该模块的exports对象。
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。

用例子来了解一下commonjs依赖关系

//main1
console.log("main导入a模块");
const a = require('./a');
//-----------------------------
//main2
console.log("main输出a模块----");
console.log(a)
//a1
console.log('a模块--导入b之前')
const b = require('./b'); 
//----------------------------
//a2
console.log("a输出B模块----");
console.log(b)
console.log('a模块--导入b之后')
module.exports = '我是A模块'
//b1
console.log('b模块--导入a之前')
const a = require('./a'); 
//b2
//----------------------------
console.log("B输出a模块----");
console.log(a)
console.log('b模块--导入a之后')
module.exports = '我是B模块'

运行结果
在这里插入图片描述
模块a、b会被require分为上下两部分,在main.js中遇到require后,加载并执行a模块,a.js中遇到require,加载并执行b模块,b.js中又遇到require(‘./a’)发生了循环引用,因为模块a已经被加载过,直接从缓存中取,由于取出的a模块还没执行完,因此输出a是个空对象。b.js执行完后会返回到刚刚a.js的地方继续执行。三个文件的执行流程为:main1->a1->b1->b2->a2->main2。因此说CommonJS 模块在执行阶段分析模块依赖,采用深度优先遍历(depth-first traversal),执行顺序是父 -> 子 -> 父。由于存在一个缓存机制,所以可以解决循环引用的问题。

ESM

在 ES Module 中用 export 用来导出模块,import 用来导入模块。CommonJS 模块同步加载并执行模块文件,ESM 模块提前加载并执行模块文件,ESM模块在预处理阶段分析模块依赖,在执行阶段执行模块,两个阶段都采用深度优先遍历,执行顺序是子 -> 父。

CommonJS 输出的是一个值的拷贝;ES Modules 生成一个引用,等到真的需要用到时,再到模块里面去取值,模块里面的变量,绑定其所在的模块。以后需要用到这个模块的时候,就会到exports属性上面取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。

//count文件
let num = 1
function add(){return num++
}
module.exports = {num,add}
//ESM
let num = 1
function add(count){return count++
}
export {num,add}
const {num,add} = require('./count')
console.log(num)   //1
add()
console.log(num)   //1
//ESM
import {num,add} from './count'
console.log(num)  //1
add(num)
console.log(num)  //2

AMD规范(requirejs)

AMD规范,全称是Asynchronous Module Definition,即异步模块加载机制。完整描述了模块的定义,依赖关系,引用关系以及加载机制。

AMD的核心是预加载,先对依赖的全部文件进行加载,加载完了再进行处理。
适合在浏览器环境中异步加载模块。可以并行加载多个模块。 并可以按需加载。

遵循AMD规范的主要指requirejs,下面来进行requirejs的学习

requirejs下载与配置

官网链接:https://requirejs.org/docs/download.html
在这里插入图片描述
将文件另存为js文件即可

requirejs

定义暴露模块定义没有依赖的模块define(function(){return moudle//模块})定义有依赖的模块define(['require','dependency'], function(require, factory) {'use strict';});引用注入模块require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){代码,使用moudleA,B,C});

RequireJS主要解决了两个问题:

多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
js加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长

加载文件
之前的例子中加载模块都是本地js,但是大部分情况下网页需要加载的JS可能来自本地服务器、其他网站或CDN,这样就不能通过这种方式来加载了,我们以加载一个jquery库为例:

require.config({paths : {"jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"]   }
})
require(["jquery","js/a"],function($){$(function(){alert("load finished");  })
})

使用
然后再页面中使用下面的方式来使用requirejs:

<script data-main="js/main" src="js/require.js"></script>
src引入requirejs,date-main将main模块为主模块加载

解释一下,加载 requirejs 脚本的 script 标签加入了data-main属性,这个属性指定的 js 将在加载完 require.js 后处理,我们把require.config的配置加入到data-main后,就可以使每一个页面都使用这个配置,然后页面中就可以直接使用require来加载所有的短模块名

data-main还有一个重要的功能,当script标签指定data-main属性时,require会默认的将data-main指定的js为根路径,是什么意思呢?如上面的data-main="js/main"设定后,我们在使用require([‘jquery’])后(不配置jquery的paths),require会自动加载js/jquery.js这个文件,而不是jquery.js,相当于默认配置了:

require.config({baseUrl : "js"
})

引入第三方模块
通过require加载的模块一般都需要符合AMD规范即使用define来申明模块,但是部分时候需要加载非AMD规范的js,这时候就需要用到另一个功能:shim,shim解释起来也比较难理解,shim直接翻译为"垫",其实也是有这层意思的,目前我主要用在两个地方

非AMD模块输出,将非标准的AMD模块"垫"成可用的模块,例如:在老版本的jquery中,是没有继承AMD规范的,所以不能直接require[“jquery”],这时候就需要shim,比如我要是用underscore类库,但是他并没有实现AMD规范,插件形式的非AMD模块,我们经常会用到jquery插件,而且这些插件基本都不符合AMD规范,比如jquery.form插件,这时候就需要将form插件"垫"到jquery中:

require.config({shim: {"underscore" : {exports : "_";},"jquery.form" : {deps : ["jquery"]//为非模块化内容添加依赖项}}
})
require(["underscore"], function(_){_.each([1,2,3], alert);
})require.config(["jquery", "jquery.form"], function($){$(function(){$("#form").ajaxSubmit({...});})
})

CMD规范(seajs)

CMD规范:全称是Common Module Definition,即通用模块定义。按需加载。在 CMD 规范中,一个模块就是一个文件,使用define来进行模块,define是一个全局函数。
factory 可以是一个函数,也可以是一个对象或字符串。

factory 为对象、字符串时,表示模块的接口就是该对象、字符串。通过require载入它们时,就是返回对象或者字符串。

factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:require、exports 和 module。这三个参数可根据情况省略,甚至全部省略也可以。

define({'website':'oecom'});
define('这里是OECOM');
define(function(require,exports,module){})
require参数也是一个方法,接收的参数为模块标识,其实就是需要加载模块的相对路径,作用就是加载其他模块。
define(function(require,exports,module){var a = require('./a');a.out();//假设模块a有out方法。
})
直接使用require加载属于是同步加载,require提供了async方法来在模块内部进行也不加载模块,并在加载完成以后执行指定的回调函数。
define(function(require,exports,module){require.async('./a',function(a){a.doSomething()})require.async(['./c','./b'],function(c,b){c.doSomething()b.doSomething()})}
require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。exports是一个用来想外接提供模块接口的对象
define(function(require,exports){var name = 10;exports.name = name;exports.out = function(){console.log("输出内容")}})当然导出模块还可以直接使用return的方式
define(function(require){return{name:10,out:function(){console.log("输出内容")}}
})

AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。

异步加载:不阻碍后面代码加载执行

AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了哪些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

提升代码质量

参考博客:https://blog.csdn.net/z591102/article/details/107542071?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%A6%82%E4%BD%95%E8%AE%A9js%E4%BB%A3%E7%A0%81%E6%9B%B4%E5%8A%A0%E8%A7%84%E8%8C%83%E5%8C%96&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-107542071.142v47new_blog_pos_by_title,201v3control_1&spm=1018.2226.3001.4187

文件结构

创建项目结构
dist打包生成的文件
libs第三方文件引入(jquery,requirejs等)
src源代码所在的目录moudle1moudle2moudle3app.js应用主模块
index.html
package.json
{
name:"包名",
version:"版本号"
}

想了解的package.json的可以参考博客:https://blog.csdn.net/qq_34703156/article/details/121401990?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166253120616782428667436%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166253120616782428667436&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-121401990-null-null.142v47new_blog_pos_by_title,201v3control_1&utm_term=package.json&spm=1018.2226.3001.4187

webapp

web app是用HTML5编写的移动web运用,依赖于浏览器(微信浏览器、qq浏览器),可以不加修改,直接运行在Android、iOS、pc端。看起来像是一个app,其实就是给web站变异打包了个app壳,可以发布到各个平台上,比如:安卓、iOS、黑莓、win phone等等。

但是web app与原生app相比有几点不足

Web基于DOM,而DOM很慢。浏览器打开网页时,需要解析文档,在内存中生成DOM结构,如果遇到复杂的文档,这个过程是很慢的。可以想象一下,如果网页上有上万个、甚至几十万个形状(不管是图片或CSS),生成DOM需要多久?更不要提与其中某一个形状互动了。

DOM拖慢JavaScript。所有的DOM操作都是同步的,会堵塞浏览器。JavaScript 操作 DOM 时,必须等前一个操作结束,才能执行后一个操作。只要一个操作有卡顿,整个网页就会短暂失去响应。浏览器重绘网页的频率是60FPS(即16毫秒/帧),JavaScript 做不到在16毫秒内完成 DOM 操作,因此产生了跳帧。用户体验上的不流畅、不连贯就源于此。

虽然现在浏览器也支持多线程,比如JS解析在一个线程,渲染在一个线程,但一般是按照先解析再渲染再执行 JS 这样的顺序执行的。

在Android早期的版本中,webview的性能很差
webapp相关操作,可以参考这个博客:https://blog.csdn.net/qq_36232611/article/details/96032839?ops_request_misc=&request_id=&biz_id=102&utm_term=webapp&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-4-96032839.142v47new_blog_pos_by_title,201v3control_1&spm=1018.2226.3001.4187


http://chatgpt.dhexx.cn/article/0kQlKQRP.shtml

相关文章

模块化开发

模块化开发 1. 模块化开发最终的目的是将程序划分成 一个个小的结构 。 2. 这个结构中编写属于 自己的逻辑代码 &#xff0c;有 自己的作用域 &#xff0c;定义变量名词时不会影响到其他的结构。 3. 这个结构可以将自己希望暴露的 变量、函数、对象等导出 给其结构使用&#xf…

HttpClient CloseableHttpClient GetMethod PostMethod http

pom依赖 <!--HttpClient的依赖--><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency><!--CloseableHttpClient的依赖--><de…

JAVA小工具-05-HttpClient/PostMethod上传文件(解决中文文件名乱码问题)

言于头:本节讨论的是在项目中利用HttpClient/PostMethod相关api进行上传文件操作时&#xff0c;会出现上传中文文件名乱码问题。为解决这个问题&#xff0c;下面是总结的一个HTTP工具类以及测试用例。 public class HttpUtils {public static final String UTF_8 "UTF-8&…

解决PostMethod的中文乱码

解决HttpClient的PostMethod的中文乱码问题 问题场景&#xff1a; 解决代码&#xff1a; 请求时设定编码格式&#xff1a; post.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8"); 完整代码&#xff1a; /*** 封装请求参数&#xff0…

php 取整,PHP取整的方法有哪些

本篇文章主要给大家介绍PHP取整的四种方法。 PHP实现取整的问题&#xff0c;不仅在我们学习PHP过程中会遇到&#xff0c;在我们PHP面试过程中也是常见的考点之一。 下面我们结合简单的示例给大家总结介绍PHP取整的四种方法。 第一种方法&#xff1a;直接取整&#xff0c;舍弃小…

php 如何取整,解析php取整的几种方式

解析php取整的几种方式 floor 舍去法取整 语法格式:float floor ( float value )返回不大于value 的下一个整数&#xff0c;将value 的小数部分舍去取整。floor() 返回的类型仍然是float&#xff0c;因为float 值的范围通常比integer 要大。 echo floor(4.3); // 4 echo floo…

VUE 数据分页

只要涉及到数据查询&#xff0c;通常我们都会进行分页查询。 假设你的表中有上百万条记录&#xff0c;不分页的话&#xff0c;我们不可能一次性将所有数据全部都载入到前端吧&#xff0c;那前后端都早就崩溃了。 结合 Spring Spring 和 Vue 都提供了开箱即用的分页功能。 S…

前端Vue分页及后端PageHelper分页综合运用

分页显示数据对项目开发中尤为重要&#xff0c;同时能提升用户体验&#xff0c;下面的前端css、js是我引用这篇文章的《使用Vue开发一个分页插件》&#xff0c;我在这个的基础上结合了后端稍微完善了一下&#xff0c;修改了disable的样式&#xff0c;在里面加了pointer-events:…

antd design vue分页组件

我们在使用分页组件的时候可以有两种方法&#xff1a; 第一种是直接用表格()的自定义:pagination属性最方便&#xff1b;如下图所示&#xff1a; 第二种是分页组件 这里我总结的是第二种方法的使用&#xff0c;由于是 Ant Design Vue 的组件&#xff0c;所以必须安装Ant Desig…

Vue分页页码栏设计

Vue分页页码栏设计 效果展示HTML数据需要函数需要运用 效果展示 HTML <div class"page_bar no-select"><ul class"clearfix"><li class"iconfont":class"{vh : currentPage 1}"click"subCurrentPage">&…

超级详细:一个漂亮的Vue分页器组件的实现

整篇分两个部分&#xff1a; 思路部分&#xff1a;讲解怎么实现分页器组件【大把时间看-建议】 后面部分&#xff1a;按照步骤&#xff0c;直接引入组件【没有时间看-建议】 思路&#xff1a;基于连续页码进行判断 需要添加分页器的组件&#xff08;Search组件&#xff09;中…

vue实现分页vue分页查询怎么实现

效果图&#xff1a; 代码&#xff1a; 复制过去即可运行 <!DOCTYPE html> <html lang"en" xmlns:th"http://www.w3.org/1999/xhtml"> <head><meta charset"UTF-8"><title>Title</title><!-- <scrip…

vue分页单位设置为中文格式

根据我搭建前端项目时遇到的问题做一个记录&#xff0c;我下载了一个vue-element-admin前端项目demo&#xff0c;但是默认情况下此demo分页展示时为如下图所示&#xff1a; 遇到此种情况想要调整为中文显示时&#xff0c;如下中文显示案例&#xff1a; 此时需要修改demo项目中…

vue分页器的封装

1.需要注册为全局组件 //main.js // 封装分页器为全局组件 Vue.component(Pagination.name,Pagination); 2.父组件的使用分页器&#xff0c;需要传递相应的参数和自定义事件 <Pagination :pageNo"searchParam.pageNo" :pageSize"searchParam.pageSize"…

django与vue分页

后端django进行自定义分页 1.编写自定义配置文件 from rest_framework.pagination import LimitOffsetPaginationclass LimitPagination(LimitOffsetPagination):max_limit 2 # 最大limit限制&#xff0c;默认Nonedefault_limit 2 # 默认限制&#xff0c;和page_size作用…

Django+vue 分页展示

这里提供两种分页方法 一种是手写分页,不常用,但是明白一下分页的逻辑实现 第二种是用heyui提供的组件.很多功能都给封装好了,用起来也比较美观. 手写分页 后端接口 class GoodList(APIView):def get(self, request):# 当前页page int(request.GET.get(page, 1))# 一页有多…

Vue分页及页码跳转

效果如下&#xff1a; HTML&#xff1a; <ul class"page f16 tc mt30"> <li> <span v-if"page > 1"><b click"page--,pageClick()">上一页</b></span> <span v-if"page 1">上一页<…

VUE分页出现省略号

VUE分页出现省略号 废话不多说直接上代码 calcPageNum() {let pageTotal Math.ceil(this.total / this.limit); //获取最大页码数let cur this.currentPage;//获取当前页码数if (pageTotal < 7) {//判断什么时候正常显示return Math.ceil(this.total / this.limit);} els…

vue 分页表格数据导出

vue分页表格数据导出功能&#xff0c;超简单&#xff01;不用安装不用引入 写个方法即可实现 //导出exportExcel() {let url "/VueDemo/api/record/export.htm"; //这里写后台给的接口&#xff0c;注意反向代理也要写上&#xff01;VueDemo是我这边的反向代理url …

vue分页功能Bug

今天分页功能报错了&#xff0c;报错是这样报错的 看代码 <pagination v-show"adminTotal>0":total"adminTotal":page.sync"adminParams.pageNum":limit.sync"adminParams.pageSize"pagination"getAdminListPage" /&…