Rumah >hujung hadapan web >tutorial js >Saya menulis pengikat modul. nota, dsb

Saya menulis pengikat modul. nota, dsb

王林
王林asal
2024-07-25 03:10:43442semak imbas

I wrote a module bundler. notes, etc

Saya membina pengikat JavaScript yang mudah dan ternyata lebih mudah daripada yang saya jangkakan. Saya akan berkongsi semua yang saya pelajari dalam siaran ini.

Apabila menulis aplikasi besar, adalah amalan yang baik untuk membahagikan kod sumber JavaScript kami ke dalam fail js yang berasingan, namun menambah fail ini pada dokumen html anda menggunakan berbilang tag skrip memperkenalkan masalah baharu seperti

  • pencemaran ruang nama global.

  • syarat perlumbaan.

Pengikat modul menggabungkan kod sumber kami daripada fail yang berbeza menjadi satu fail besar, membantu kami menikmati faedah pengabstrakan sambil mengelakkan kelemahan.

Pengikat modul biasanya melakukan ini dalam dua langkah.

  1. Mencari semua fail sumber JavaScript, bermula dari fail kemasukan. Ini dikenali sebagai peleraian pergantungan dan peta yang dijana dipanggil graf pergantungan.
  2. Menggunakan graf pergantungan untuk menjana himpunan: rentetan besar kod sumber JavaScript yang boleh dijalankan dalam penyemak imbas. Ini boleh ditulis pada fail dan ditambahkan pada dokumen html menggunakan teg skrip.

PENYELESAIAN KEBERTANGGUNGAN

Seperti yang dinyatakan sebelum ini, di sini kami

  • ambil fail kemasukan,
  • baca dan huraikan kandungannya,
  • Tambahkannya pada tatasusunan modul
  • cari semua kebergantungannya (fail lain yang diimport),
  • Baca dan huraikan kandungan kebergantungan
  • Tambah kebergantungan pada tatasusunan
  • Cari kebergantungan kebergantungan dan seterusnya dan seterusnya sehingga kita sampai ke modul terakhir

Begini cara kami melakukannya (kod JavaScript di hadapan)

Buat fail bundler.js dalam editor teks anda dan tambahkan kod berikut:

const bundler = (entry)=>{
          const graph = createDependencyGraph(entry)

          const bundle = createBundle(graph)
          return bundle
}

Fungsi bundler adalah entri utama bundler kami. Ia mengambil laluan ke fail (fail kemasukan) dan mengembalikan rentetan (himpunan). Di dalamnya, ia menjana graf pergantungan menggunakan fungsi createDependencyGraph.

const createDependencyGraph = (path)=>{
          const entryModule = createModule(path)

          /* other code */
}

Fungsi createDependencyGraph mengambil laluan ke fail masukan. Ia menggunakan fungsi createModule menjana perwakilan modul pada fail ini.

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
                       }
}

Fungsi createAsset membawa laluan ke fail dan membaca kandungannya ke dalam rentetan. Rentetan ini kemudiannya dihuraikan ke dalam pokok sintaks abstrak. Pokok sintaks abstrak ialah perwakilan pokok kandungan kod sumber. Ia boleh disamakan dengan pokok DOM dokumen html. Ini memudahkan untuk menjalankan beberapa fungsi pada kod seperti mencari melalui, dsb.
Kami mencipta ast daripada modul menggunakan penghurai babylon.

Seterusnya dengan bantuan transpiler teras babel kami menukarkan kandungan kod kepada sintaks pra-es2015 untuk keserasian silang penyemak imbas.
Selepas itu, ast dilalui menggunakan fungsi khas dari babel untuk mencari setiap pengisytiharan import fail sumber kami(bergantungan).

Kami kemudian menolak kebergantungan ini (yang merupakan teks rentetan laluan fail relatif) ke dalam tatasusunan kebergantungan.

Kami juga mencipta id untuk mengenal pasti modul ini dan
secara unik Akhirnya kami mengembalikan objek yang mewakili modul ini. Modul ini mengandungi id, kandungan fail kami dalam format rentetan, tatasusunan kebergantungan dan laluan fail mutlak.

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
}

Kembali dalam fungsi createDependencyGraph kami, kami kini boleh memulakan proses menjana graf kami. Graf kami ialah tatasusunan objek dengan setiap objek mewakili setiap fail sumber yang digunakan dalam aplikasi kami.
Kami memulakan graf kami dengan modul kemasukan dan kemudian menggelungkannya. Walaupun ia mengandungi hanya satu item, kami menambah item pada penghujung tatasusunan dengan mengakses tatasusunan dependensi modul kemasukan (dan modul lain yang akan kami tambah).

Tatasusunan kebergantungan mengandungi laluan fail relatif semua kebergantungan modul. Tatasusunan digelung dan untuk setiap laluan fail relatif, laluan mutlak diselesaikan terlebih dahulu dan digunakan untuk mencipta modul baharu. Modul kanak-kanak ini ditolak ke penghujung graf dan proses bermula sekali lagi sehingga semua kebergantungan telah ditukar kepada modul.
Setiap modul juga memberikan objek pemetaan yang hanya memetakan setiap laluan relatif pergantungan kepada id modul anak.
Semakan sama ada modul sudah wujud dilakukan pada setiap kebergantungan untuk mengelakkan pertindihan modul dan kebergantungan bulat tak terhingga.
Akhirnya kami mengembalikan graf kami yang kini mengandungi semua modul aplikasi kami.

PENGUMPULAN

Dengan graf pergantungan selesai, penjanaan satu berkas akan melibatkan dua langkah

  1. Wrapping each module in a function. This creates the idea of each module having its own scope
  2. Wrapping the module in a runtime.

Wrapping each module

We have to convert our module objects to strings so we can be able to write them into the bundle.js file. We do this by initializing moduleString as an empty string. Next we loop through our graph appending each module into the module string as key value pairs, with the id of a module being the key and an array containing two items: first, the module content wrapped in function (to give it scope as stated earlier) and second an object containing the mapping of its dependencies.

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

Also to note, the function wrapping each module takes a require, export and module objects as arguments. This is because these don’t exist in the browser but since they appear in our code we will create them and pass them into these modules.

Creating the runtime

This is code that will run immediately the bundle is loaded, it will provide our modules with the require, module and module.exports objects.

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;
}

We use an immediately invoked function expression that takes our module object as an argument. Inside it we define our require function that gets a module from our module object using its id.
It constructs a localRequire function specific to a particular module to map file path string to id. And a module object with an empty exports property
It runs our module code, passing the localrequire, module and exports object as arguments and then returns module.exports just like a node js module would.
Finally we call require on our entry module (index 0).

To test our bundler, in the working directory of our bundler.js file create an index.js file and two directories: a src and a public directory.

In the public directory create an index.html file, and add the following code in the body tag:

<!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

const name = “David”
export default name

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

import name from ‘./name.js’
const hello = document.getElementById(“root”)
hello.innerHTML = “hello” + name

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 = (output , input)=>{
let bundle = creatBundle(entry)
fs.writeFileSync(bundle, ‘utf-8’)
}
run(“./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.

Atas ialah kandungan terperinci Saya menulis pengikat modul. nota, dsb. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn