Creé un paquete de JavaScript simple y resultó ser mucho más fácil de lo que esperaba. Compartiré todo lo que aprendí en esta publicación.
Al escribir aplicaciones grandes, es una buena práctica dividir nuestro código fuente JavaScript en archivos js separados; sin embargo, agregar estos archivos a su documento html usando múltiples etiquetas de secuencia de comandos introduce nuevos problemas como
contaminación del espacio de nombres global.
condiciones de carrera.
Los paquetes de módulos combinan nuestro código fuente de diferentes archivos en un archivo grande, lo que nos ayuda a disfrutar de los beneficios de las abstracciones y evitar las desventajas.
Los paquetes de módulos generalmente hacen esto en dos pasos.
Como se mencionó anteriormente, aquí
Así es como lo haríamos (código JavaScript a continuación)
Cree un archivo Bundler.js en su editor de texto y agregue el siguiente código:
const bundler = (entry)=>{ const graph = createDependencyGraph(entry) const bundle = createBundle(graph) return bundle }
La función de paquete es la entrada principal de nuestro paquete. Toma la ruta a un archivo (archivo de entrada) y devuelve una cadena (el paquete). Dentro de él, genera un gráfico de dependencia usando la función createDependencyGraph.
const createDependencyGraph = (path)=>{ const entryModule = createModule(path) /* other code */ }
La función createDependencyGraph toma la ruta al archivo de entrada. Utiliza la función createModule para generar una representación de módulo de este archivo.
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 } }
La función createAsset toma la ruta a un archivo y lee su contenido en una cadena. Luego, esta cadena se analiza en un árbol de sintaxis abstracta. Un árbol de sintaxis abstracta es una representación en árbol del contenido de un código fuente. Puede compararse con el árbol DOM de un documento html. Esto facilita la ejecución de algunas funciones en el código, como búsquedas, etc.
Creamos un ast desde el módulo usando el analizador babylon.
A continuación, con la ayuda del transpilador central de Babel, convertimos el contenido del código a una sintaxis anterior a es2015 para compatibilidad entre navegadores.
Luego, se recorre el ast utilizando una función especial de babel para encontrar cada declaración de importación de nuestro archivo fuente (dependencias).
Luego insertamos estas dependencias (que son cadenas de texto de rutas de archivos relativas) en una matriz de dependencias.
También creamos una identificación para identificar de forma única este módulo y
Finalmente devolvemos un objeto que representa este módulo. Este módulo contiene una identificación, el contenido de nuestro archivo en formato de cadena, una serie de dependencias y la ruta absoluta del archivo.
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 }
De vuelta en nuestra función createDependencyGraph, ahora podemos comenzar el proceso de generación de nuestro gráfico. Nuestro gráfico es una matriz de objetos y cada objeto representa cada archivo fuente utilizado en nuestra aplicación.
Inicializamos nuestro gráfico con el módulo de entrada y luego lo hacemos en bucle. Aunque contiene solo un elemento, agregamos elementos al final de la matriz accediendo a la matriz de dependencias del módulo de entrada (y otros módulos que agregaremos).
La matriz de dependencias contiene rutas de archivo relativas de todas las dependencias de un módulo. La matriz se recorre y, para cada ruta de archivo relativa, primero se resuelve la ruta absoluta y se utiliza para crear un nuevo módulo. Este módulo secundario se empuja hasta el final del gráfico y el proceso comienza de nuevo hasta que todas las dependencias se hayan convertido en módulos.
Además, cada módulo proporciona un objeto de mapeo que simplemente asigna cada ruta relativa de dependencia a la identificación del módulo secundario.
Se realiza una verificación de si ya existe un módulo en cada dependencia para evitar la duplicación de módulos y dependencias circulares infinitas.
Finalmente devolvemos nuestro gráfico que ahora contiene todos los módulos de nuestra aplicación.
Una vez realizado el gráfico de dependencia, generar un paquete implicará dos pasos
Tenemos que convertir los objetos de nuestro módulo en cadenas para poder escribirlos en el archivo bundle.js. Hacemos esto inicializando moduleString como una cadena vacía. A continuación, recorremos nuestro gráfico agregando cada módulo a la cadena del módulo como pares clave-valor, siendo la identificación de un módulo la clave y una matriz que contiene dos elementos: primero, el contenido del módulo envuelto en una función (para darle alcance como se indicó anteriormente). ) y en segundo lugar un objeto que contiene el mapeo de sus dependencias.
const wrapModules = (graph)=>{ let modules = ‘’ graph.forEach(mod => { modules = `${http://mod.id}: [ function (require, module, exports) { ${mod.code} }, ${JSON.stringify(mod.mapping)}, ],`; }); return modules }
También hay que tener en cuenta que la función que envuelve cada módulo toma objetos require, export y module como argumentos. Esto se debe a que no existen en el navegador, pero como aparecen en nuestro código, los crearemos y los pasaremos a estos módulos.
Este es el código que se ejecutará inmediatamente cuando se cargue el paquete y proporcionará a nuestros módulos los objetos require, module y 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; }
Usamos una expresión de función invocada inmediatamente que toma nuestro objeto de módulo como argumento. Dentro de él definimos nuestra función require que obtiene un módulo de nuestro objeto módulo usando su identificación.
Construye una función localRequire específica de un módulo en particular para asignar la cadena de ruta del archivo a la identificación. Y un objeto de módulo con una propiedad de exportación vacía
Ejecuta el código de nuestro módulo, pasa el requisito local, el módulo y el objeto de exportación como argumentos y luego devuelve module.exports tal como lo haría un módulo de nodo js.
Finalmente llamamos a require en nuestro módulo de entrada (índice 0).
Para probar nuestro paquete, en el directorio de trabajo de nuestro archivo Bundler.js crea un archivo index.js y dos directorios: un src y un directorio público.
En el directorio público, cree un archivo index.html y agregue el siguiente código en la etiqueta del cuerpo:
Module bundler
nombre constante = “David”
exportar nombre predeterminado
also create a hello.js file and add the following code
importar nombre desde './name.js'
const hola = document.getElementById(“raíz”)
hola.innerHTML = “hola” nombre
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”)
ejecución constante = (salida, entrada) =>{
let paquete = creatBundle(entrada)
fs.writeFileSync(paquete, 'utf-8')
}
ejecutar(“./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.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3