我写了一个模块捆绑器注释等

我写了一个模块捆绑器注释等

我构建了一个简单的 javascript 捆绑器,结果比我预期的要容易得多。我将分享我在这篇文章中学到的所有知识。

编写大型应用程序时,最好将 javascript 源代码划分为单独的 js 文件,但是使用多个脚本标签将这些文件添加到 html 文档中会带来新问题,例如

全局命名空间的污染。

比赛条件。

模块捆绑器将不同文件中的源代码合并到一个大文件中,帮助我们享受抽象的好处,同时避免缺点。

模块捆绑器通常分两步完成。

从入口文件开始,找到所有的javascript源文件。这称为依赖解析,生成的映射称为依赖图。使用依赖图生成一个bundle:一大串可以在浏览器中运行的javascript源代码。这可以写入文件并使用脚本标签添加到 html 文档。

依赖解析

如前所述,我们在这里

获取入口文件,阅读并解析其内容,将其添加到模块数组中找到它的所有依赖项(它导入的其他文件),读取并解析依赖内容向数组添加依赖项查找依赖项的依赖项等等,直到我们到达最后一个模块

我们将这样做(前面是 javascript 代码)

在文本编辑器中创建一个bundler.js 文件并添加以下代码:

const bundler = (entry)=>{          const graph = createdependencygraph(entry)          const bundle = createbundle(graph)          return bundle}

登录后复制

bundler 函数是我们bundler 的主要入口。它获取文件(入口文件)的路径并返回一个字符串(捆绑包)。在其中,它使用 createdependencygraph 函数生成依赖图。

const createdependencygraph = (path)=>{          const entrymodule = createmodule(path)          /* other code */}

登录后复制

createdependencygraph 函数获取入口文件的路径。它使用 createmodule 函数生成此文件的模块表示。

let id = 0const createmodule = (filename)=>{          const content = fs.readfilesync(filename)          const ast = babylon.parse(content, {sourcetype: “module”})          const {code} = babel.transformfromast(ast, null, {              presets: ['env']            })           const dependencies = [ ]           const id = id++           traverse(ast, {                   importdeclaration: ({node})=>{                       dependencies.push(node.source.value)                   }            }            return {                           id,                           filename,                           code,                           dependencies                       }}

登录后复制

createasset 函数获取文件的路径并将其内容读取到字符串中。然后该字符串被解析为抽象语法树。抽象语法树是源代码内容的树表示。它可以比作 html 文档的 dom 树。这使得在代码上运行一些功能变得更容易,例如搜索等。
我们使用babylon解析器从模块创建一个ast。

接下来,在 babel 核心转译器的帮助下,我们将代码内容转换为 es2015 之前的语法,以实现跨浏览器兼容性。
然后使用 babel 中的特殊函数遍历 ast 来查找源文件的每个导入声明(依赖项)。

然后我们将这些依赖项(相对文件路径的字符串文本)推送到依赖项数组中。

我们还创建一个 id 来唯一标识该模块并且
最后我们返回一个代表该模块的对象。该模块包含一个 id、字符串格式的文件内容、依赖项数组和绝对文件路径。

const createdependencygraph = (path)=>{          const entrymodule = createmodule(path)          const graph = [ entrymodule ]          for ( const module of graph) {                  module.mapping = { }module.dependencies.foreach((dep)=>{         let absolutepath = path.join(dirname, dep);         let child = graph.find(mod=> mod.filename == dep)         if(!child){               child = createmodule(dep)               graph.push(child)         }         module.mapping[dep] = child.id})          }          return graph}

登录后复制

回到 createdependencygraph 函数,我们现在可以开始生成图的过程。我们的图表是一个对象数组,每个对象代表我们应用程序中使用的每个源文件。
我们使用入口模块初始化图表,然后循环它。尽管它只包含一项,但我们通过访问入口模块(以及我们将添加的其他模块)的依赖项数组来将项添加到数组的末尾。

dependency 数组包含模块所有依赖项的相对文件路径。该数组被循环,对于每个相对文件路径,首先解析绝对路径并用于创建新模块。该子模块被推到图的末尾,并且该过程重新开始,直到所有依赖项都已转换为模块。
此外,每个模块都给出一个映射对象,该对象简单地将每个依赖项相对路径映射到子模块的 id。
对每个依赖项执行检查模块是否已存在,以防止模块重复和无限循环依赖。
最后我们返回我们的图表,它现在包含我们应用程序的所有模块。

捆绑

依赖图完成后,生成包将涉及两个步骤

将每个模块包装在一个函数中。这就产生了每个模块都有自己的作用域的想法在运行时包装模块。

包装每个模块

我们必须将模块对象转换为字符串,以便我们能够将它们写入到bundle.js 文件中。我们通过将 modulestring 初始化为空字符串来实现此目的。接下来,我们循环遍历图表,将每个模块作为键​​值对附加到模块字符串中,模块的 id 为键,数组包含两项:首先,包装在函数中的模块内容(以赋予其范围,如前所述) )和第二个包含其依赖项映射的对象。

const wrapmodules = (graph)=>{         let modules = ‘’           graph.foreach(mod => {    modules += `${http://mod.id}: [      function (require, module, exports) {        ${mod.code}      },      ${json.stringify(mod.mapping)},    ],`;  });return modules}

登录后复制

还要注意,包装每个模块的函数将 require、export 和 module 对象作为参数。这是因为这些在浏览器中不存在,但由于它们出现在我们的代码中,我们将创建它们并将它们传递到这些模块中。

创建运行时

这是将在加载包后立即运行的代码,它将为我们的模块提供 require、module 和 module.exports 对象。

const bundle = (graph)=>{        let modules = wrapmodules(graph)        const result = `    (function(modules) {      function require(id) {        const [fn, mapping] = modules[id];        function localrequire(name) {          return require(mapping[name]);        }        const module = { exports : {} };        fn(localrequire, module, module.exports);        return module.exports;      }      require(0);    })({${modules}})`;  return result;}

登录后复制

我们使用立即调用的函数表达式,它将我们的模块对象作为参数。在其中我们定义了 require 函数,该函数使用模块对象的 id 从模块对象中获取模块。
它构造一个特定于特定模块的 localrequire 函数,以将文件路径字符串映射到 id。以及一个具有空导出属性的模块对象
它运行我们的模块代码,传递 localrequire、模块和导出对象作为参数,然后返回 module.exports,就像 node js 模块一样。
最后我们在入口模块(索引 0)上调用 require。

为了测试我们的捆绑器,在bundler.js文件的工作目录中创建一个index.js文件和两个目录:一个src和一个公共目录。

在public目录下创建一个index.html文件,并在body标签中添加以下代码:

            module bundler                       
</htmlin the src directory create a name.js file and add the following code

登录后复制

常量名称=“大卫”
导出默认名称

also create a hello.js file and add the following code

登录后复制

从‘./name.js’导入名称
const hello = document.getelementbyid(“root”)
hello.innerhtml = “你好” + 名字

lastly in the index.js file of the root directory import our bundler, bundle the files and write it to a bundle.js file in the public directory

登录后复制

const createbundle = require(“./bundler.js”)
const run = (输出, 输入)=>{
让bundle = creatbundle(entry)
fs.writefilesync(bundle, ‘utf-8’)
}
运行(“./public/bundle.js”,“./src/hello.js”)

Open our index.html file in the browser to see the magic.In this post we have illustrated how a simple module bundler works. This is a minimal bundler meant for understanding how these technologies work behind the hood.please like if you found this insightful and comment any questions you may have.

登录后复制

以上就是我写了一个模块捆绑器注释等的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2672649.html

(0)
上一篇 2025年3月7日 13:28:59
下一篇 2025年3月6日 12:18:50

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • JavaScript 基础知识 – 开始

    一些新开发人员甚至那些有经验的开发人员,可能会在没有扎实的基础知识的情况下面临问题或困难。 为了解决这个问题,在本系列中,我想回顾一下 javascript 基础知识,以保持新鲜感,并帮助任何来到这里的开发人员记住任何特定主题。 首先,我们…

    2025年3月7日
    200
  • JavaScript 初学者最佳实践

    javascript 初学者最佳实践 javascript 是一种通用且广泛使用的语言,对于 web 开发至关重要。无论您是编程新手还是从其他语言过渡,了解 javascript 的最佳实践对于编写干净、高效且可维护的代码都至关重要。本文涵…

    2025年3月7日
    200
  • 了解 JavaScript 中的闭包:综合指南

    javascript 是一种多功能且强大的语言,其最有趣的功能之一就是闭包的概念。闭包是理解 javascript 函数如何工作的基础,尤其是与作用域和变量访问相关的函数。在本教程中,我们将探讨什么是闭包、它们如何工作,并提供实际示例来帮助…

    2025年3月7日
    200
  • 揭秘 JavaScript:深入探讨提升、临时死区和可变状态

    下面我有两行甜蜜而简单的代码。但我可以向你保证,它要么会让你很困惑(因为你忽略了 js 的下划线原则),要么安慰你。 但是它已经加载了如下的知识概念 吊装暂时死区变量(未声明、未初始化、未定义)(奖励) 我的矛盾声明就像 var、const…

    2025年3月7日
    200
  • 我见过的关于 JS 中闭包的最简单的解释(来源:roadmapsh)

    想在这里分享我在网上看到的关于著名的闭包主题的最简单的解释。 (来源:roadmap.sh) 闭包是一个即使在外部函数返回后也可以访问其外部函数作用域的函数。这意味着即使函数完成后,闭包也可以记住并访问其外部函数的变量和参数。 functi…

    2025年3月7日
    200
  • 理解 JavaScript 中的作用域链

    在 javascript 中,作用域链是一种定义变量解析在嵌套函数中如何工作的机制。它决定了引用变量时查找变量的顺序。 作用域链的工作原理是首先在局部作用域中查找变量,然后向上移动到外部(父)作用域,最后在必要时查找全局作用域。这个过程一直…

    2025年3月7日
    200
  • Let、Const 和 Var 概述:主要差异解释

    曾经有一段时间,我使用并理解了 javascript 中 let、const 和 var 的实际用法,但用语言解释它是具有挑战性的。如果您发现自己处于类似的困境,那么需要关注的关键点是范围、提升、重新初始化和重新分配方面的差异。 范围: 如…

    2025年3月7日
    200
  • JavaScript 中 let、const、var 的区别?

    在 javascript 中,let、const 和 var 用来声明变量,但它们在三个方面有所不同: 1.范围2.重新分配3.吊装 1.范围: var 是一个函数作用域,意味着我们在函数内的任何位置访问 var 变量,如果我们尝试在函数外…

    2025年3月7日
    200
  • 谷歌浏览器javascript怎么开启 谷歌浏览器调试js代码步骤

    在谷歌浏览器中,调试 JavaScript 代码的步骤包括:打开开发人员工具(F12/Command+Option+I)。导航到“Sources”选项卡。找到脚本文件并设置断点。单步调试、检查变量和堆栈跟踪。暂停(Pause)或继续(Res…

    2025年3月7日
    200
  • 了解 JavaScript 中的闭包:初学者指南

    javascript 是一种强大的语言,具有许多独特的功能,其中之一就是闭包。对于许多初学者来说,闭包一开始似乎令人困惑,但它们是一个基本概念,对于深入理解 javascript 至关重要。本文将通过解释闭包是什么、它们如何工作以及它们为什…

    2025年3月7日
    200

发表回复

登录后才能评论