最新公告
  • 欢迎您光临码农资源网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!加入我们
  • 我写了一个模块捆绑器注释等

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

    我构建了一个简单的 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标签中添加以下代码:

    
        
            <title>module bundler</title><meta name="viewport" content="width=device-width, initial-scale=1"><div id="root"></div>
           <script src="%E2%80%98./bundler.js"> <script>
        
    in the src directory create a name.js file and add the following code
    </script>

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

    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.
    
    想要了解更多内容,请持续关注码农资源网,一起探索发现编程世界的无限可能!
    本站部分资源来源于网络,仅限用于学习和研究目的,请勿用于其他用途。
    如有侵权请发送邮件至1943759704@qq.com删除

    码农资源网 » 我写了一个模块捆绑器注释等
    • 7会员总数(位)
    • 25846资源总数(个)
    • 0本周发布(个)
    • 0 今日发布(个)
    • 294稳定运行(天)

    提供最优质的资源集合

    立即查看 了解详情