0

0

Js中的模块化是如何实现的

小云云

小云云

发布时间:2018-01-09 17:01:53

|

2507人浏览过

|

来源于php中文网

原创

由于 js 起初定位的原因(刚开始没想到会应用在过于复杂的场景),所以它本身并没有提供模块系统,随着应用的复杂化,模块化成为了一个必须解决的问题。本着菲麦深入原理的原则,很有必要来揭开模块化的面纱,本文主要介绍了详解js中的模块化是如何实现的,详细的介绍了模块化的运行,具有一定的参考价值,有兴趣的可以了解下,希望能帮助到大家。

一、模块化需要解决的问题

要对一个东西进行深入的剖析,有必要带着目的去看。模块化所要解决的问题可以用一句话概括

在没有全局污染的情况下,更好的组织项目代码

举一个简单的栗子,我们现在有如下的代码:

function doSomething () {
 const a = 10;
 const b = 11;
 const add = function (a + b) {
  return a + b
 }
 add (a + b)
}

在现实的应用场景中,doSomething 可能需要做很多很多的事情,add 函数可能也更为复杂,并且可以复用,那么我们希望可以将 add 函数独立到一个单独的文件中,于是:

// doSomething.js 文件
const add = require('add.js');
const a = 10;
const b = 11;
add(a+ b);
// add.js 文件
function add (a, b) {
 return a + b;
}
module.exports = add;

这样做的目的显而易见,更好的组织项目代码,注意到两个文件中的 require 和 module.exports,从现在的上帝视角来看,这出自 CommonJS 规范(后文会有一个章节来专门讲规范)中的关键字,分别代表导入和导出,抛开规范而言,这其实是我们模块化之路上需要解决的问题。另外,虽然 add 模块需要得到复用,但是我们并不希望在引入 add 的时候造成全局污染

二、引入的模块如何运行

在上述的例子中,我们已经将代码拆分到了两个模块文件当中,在不造成全局污染的情况下,如何实现 require,才能使得例子中的代码做到正常运行呢?

先不考虑模块文件代码的载入过程,假设 require 已经可以从模块文件中读取到代码字符串,那么 require 可以这样实现

function require (path) {
  // lode 方法读取 path 对应的文件模块的代码字符串
  // let code = load(path);
  // 不考虑 load 的过程,直接获得模块 add 代码字符串
  let code = 'function add(a, b) {return a+b}; module.exports = add';
  // 封装成闭包
  code = `(function(module) {$[code]})(context)`
  // 相当于 exports,用于导出对象
  let context = {};
  // 运行代码,使得结果影响到 context
  const run = new Function('context', code);
  run(context, code);
  //返回导出的结果
  return context.exports;
}

这有几个要点:

1) 为了不造成全局污染,需要将代码字符串封装成闭包的形式,并且导出关键字 module.exports ,module 是与外界联系的唯一载体,需要作为闭包匿名函数的入参,与引用方传入的上下文 context 进行关联

2) 使用 new Function 来执行代码字符串,估计大部分同学对 new Function 是不熟悉的,因为一般情况下定义一个函数无需如此,要知道,用 Function 类可以直接创建函数,语法如下:

var function_name = new function(arg1, arg2, ..., argN, function_body)

在上面的形式中,每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串。也就是说,可以使用它来执行字符串代码,类似于 eval,并且相比 eval, 还可以通过参数的形式传入字符串代码中的某些变量的值

3)如果曾经你有疑惑过为什么规范的导出关键字只有 exports 而我们实际使用过程中却要使用module.exports(写过 Node 代码的应该不会陌生),那在这段代码中就可以找到答案了,如果只用 exports 来接收 context,那么对 exports 的重新赋值对 context 不会有任何影响(参数的地址传递),不信将代码改成如下形式再跑一跑:

演示结果

三、代码载入方式

解决了代码的运行问题,还需要解决模块文件代码的载入问题,根据上述实例,我们的目标是将模块文件代码以字符串的形式载入

在 Node 容器,所有的模块文件都在本地,只需要从本地磁盘读取模块文件载入字符串代码,再走上述的流程就可以了。事实证明,Node 非内建、核心、c++ 模块的载入执行方式大体如此(虽然使用的不是 new Function,但也是一个类似的方法)

在 RN/Weex 容器,要载入一个远程 bundle.js,可以通过 Native 的能力请求一个远程的 js 文件,再读取成字符串代码载入即可(按照这个逻辑,Node 读取一个远程的 js 模块好像也无不可,虽然大多数情况下我们不需要这么做)

在浏览器环境,所有的 Js 模块都需要远程读取,尴尬的是,受限于浏览器提供的能力,并不能通过 ajax 以文件流的形式将远程的 js 文件直接读取为字符串代码。前提条件无法达成,上述运行策略便行不通,只能另辟蹊径

这就是为什么有了 CommonJs 规范了,为什么还会出现 AMD/CMD 规范的原因

那么浏览器上是怎么做的呢?在浏览器中通过 Js 控制动态的载入一个远程的 Js 模块文件,需要动态的插入一个

// 摘抄自 require.js 的一段代码
var node = config.xhtml ?
        document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
        document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);

要知道,设置了

// add.js 文件
define ('add',function () {
  function add (a, b) {
   return a + b;
  }
  return add;
})

而对于 define 的实现,最重要的就是将 callback 的执行结果注册到 context 的一个模块数组中:

  context.modules = {}
  function define(name, callback) {
    context.modules[name] = callback && callback()
  }

于是 require 就可以从 context.modules 中根据模块名载入模块了,是不是有了一种自己去写一个 “requirejs” 的冲动感

具体的 AMD 实现当然还会复杂很多,还需要控制模块载入时序、模块依赖等等,但是了解了这其中的灵魂,想必去精读 require.js 的源码也不是一件困难的事情

中国电视购物网电子商务系统
中国电视购物网电子商务系统

来自中国电视购物网的电子商务系统,系统是全站结构,带有完善的购物模块。完整的后台管理系统使您方便的实现后台管理。...

下载

四、Webpack 中的模块化

Webpack 也可以配置异步模块,当配置为异步模块的时候,在浏览器环境同样的是基于动态插入

嫑忘记,Webpack 除了有模块化的能力,还是一个在辅助完善开发工作流的工具,也就是说,Webpack 的模块化是在开发阶段的完成的,使用 Webpack 构筑的工作环境,在开发阶段虽然是独立的模块文件,但是在运行时,却是一个合并好的文件

所以 Webpack 是一种在非运行时的模块化方案(基于 CommonJs),只有在配置了异步模块的时候对异步模块的加载才是运行时的(基于 AMD)

五、模块化规范

通用的问题在解决的过程中总会形成规范,上文已经多次提到 CommonJs、AMD、CMD,有必要花点篇幅来讲一讲规范

Js 的模块化规范的萌发于将 Js 扩展到后端的想法,要使得 Js 具备类似于 Python、Ruby 和 Java 那样具备开发大型应用的基础能力,模块化规范是必不可少的。CommonJS 规范的提出,为Js 制定了一个美好愿景,希望 Js 能在任何地方运行,包括但不限于:

  • 服务器端 Js 应用

  • 命令行工具

  • 桌面应用

  • 混合应用

CommonJS 对模块的定义并不复杂,主要分为模块引用、模块定义和模块标识

  1. 模块引用:使用 require 方法来引入一个模块

  2. 模块定义:使用 exports 导出模块对象

  3. 模块标识:给 require 方法传入的参数,小驼峰命名的字符串、相对路径或者绝对路径

模块示意

CommonJs 规范在 Node 中大放异彩并且相互促进,但是在浏览器端,鉴于网络的原因,同步的方式加载模块显然不太实用,在经过一段争执之后,AMD 规范最终在前端场景中胜出(全称 Asynchronous Module Definition,即“异步模块定义”)

什么是 AMD,为什么需要 AMD ?在前述模块化实现的推演过程中,你应该能够找到答案

除此之外还有国内玉伯提出的 CMD 规范,AMD 和 CMD 的差异主要是,前者需要在定义之初声明所有的依赖,后者可以在任意时机动态引入模块。CMD 更接近于 CommonJS

两种规范都需要从远程网络中载入模块,不同之处在于,前者是预加载,后者是延迟加载

相关推荐:

javascript高级模块化require.js的具体使用方法实例分享

Laravel 的模块化开发框架 Notadd RC1

JavaScript使用高级模块化require.js的具体方法详解

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

778

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

686

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

769

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

740

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1445

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

571

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

581

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

752

2023.08.11

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

1

2026.01.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.2万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号