首页 > web前端 > js教程 > 正文

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

PHPz
发布: 2024-07-25 08:31:01
转载
349人浏览过

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

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

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

  • 全局命名空间的污染。

  • 比赛条件。

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

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

  1. 从入口文件开始,找到所有的javascript源文件。这称为依赖解析,生成的映射称为依赖图。
  2. 使用依赖图生成一个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 = 0
const 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。
对每个依赖项执行检查模块是否已存在,以防止模块重复和无限循环依赖。
最后我们返回我们的图表,它现在包含我们应用程序的所有模块。

捆绑

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

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

包装每个模块

我们必须将模块对象转换为字符串,以便我们能够将它们写入到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标签中添加以下代码:

<!doctype html>
<html>
    <head>
        <title>module bundler</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    </head>
    <body>
       <div id='root'></div>
       <script src= ‘./bundler.js> <script>
    </body>
</html

in 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.
登录后复制

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

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

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