Saya menulis pengikat modul. nota, dsb

王林
Lepaskan: 2024-07-25 03:10:43
asal
222 人浏览过

I wrote a module bundler. notes, etc

Saya membina pengikat JavaScript yang mudah dan ternyata lebih mudah daripada yang saya jangkakan. Saya akan kongsikan semua yang saya pelajari dalam post 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 ke dalam 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 resolusi pergantungan dan peta yang dihasilkan 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 ditambah pada dokumen html menggunakan tag skrip.

PENYELESAIAN KEBERTANGGUNGAN

Seperti yang dinyatakan sebelum ini, di sini kita

  • ambil fail penyertaan,
  • baca dan huraikan kandungannya,
  • Tambahkannya pada pelbagai 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
}
Salin selepas log masuk

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 */
}
Salin selepas log masuk

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
                       }
}
Salin selepas log masuk

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 (iaitu teks rentetan laluan fail relatif) ke dalam tatasusunan kebergantungan.

Kami juga mencipta id untuk mengenal pasti modul ini secara unik dan
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
}
Salin selepas log masuk

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 dependensi mengandungi laluan fail relatif bagi semua dependensi 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
}
Salin selepas log masuk

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;
}
Salin selepas log masuk

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:



    
        Module bundler
        
    
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!