간단한 JavaScript 번들러를 만들어봤는데 생각보다 훨씬 쉬웠습니다. 이번 포스팅에서 제가 배운 모든 것을 공유하겠습니다.
대규모 애플리케이션을 작성할 때는 JavaScript 소스 코드를 별도의 js 파일로 나누는 것이 좋습니다. 그러나 여러 스크립트 태그를 사용하여 이러한 파일을 html 문서에 추가하면 다음과 같은 새로운 문제가 발생합니다.
글로벌 네임스페이스 오염.
경합 조건.
모듈 번들러는 다양한 파일의 소스 코드를 하나의 큰 파일로 결합하여 단점을 피하면서 추상화의 이점을 누릴 수 있도록 도와줍니다.
모듈 번들러는 일반적으로 이 작업을 두 단계로 수행합니다.
앞서 언급했듯이
이를 수행하는 방법은 다음과 같습니다(JavaScript 코드 앞)
텍스트 편집기에서 Bundler.js 파일을 만들고 다음 코드를 추가하세요.
const bundler = (entry)=>{ const graph = createDependencyGraph(entry) const bundle = createBundle(graph) return bundle }
번들러 기능은 번들러의 주요 항목입니다. 파일(항목 파일)에 대한 경로를 취하고 문자열(번들)을 반환합니다. 그 안에서 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 트리에 비유될 수 있습니다. 이렇게 하면 검색 등의 일부 기능을 코드에서 더 쉽게 실행할 수 있습니다.
바빌론 파서를 사용하여 모듈에서 ast를 생성합니다.
다음으로 바벨 코어 트랜스파일러의 도움으로 크로스 브라우저 호환성을 위해 코드 내용을 es2015 이전 구문으로 변환합니다.
그런 다음 바벨의 특수 함수를 사용하여 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 함수로 돌아가서 이제 그래프 생성 프로세스를 시작할 수 있습니다. 그래프는 애플리케이션에 사용되는 각 소스 파일을 나타내는 각 개체가 포함된 개체 배열입니다.
입력 모듈을 사용하여 그래프를 초기화한 다음 반복합니다. 항목이 하나만 포함되어 있지만 항목 모듈(및 추가할 다른 모듈)의 종속성 배열에 액세스하여 배열 끝에 항목을 추가합니다.
종속성 배열에는 모듈의 모든 종속성의 상대 파일 경로가 포함됩니다. 배열은 반복되며 각 상대 파일 경로에 대해 절대 경로가 먼저 확인되어 새 모듈을 만드는 데 사용됩니다. 이 하위 모듈은 그래프 끝으로 푸시되고 모든 종속성이 모듈로 변환될 때까지 프로세스가 다시 시작됩니다.
또한 각 모듈은 각 종속성 상대 경로를 하위 모듈의 ID에 간단히 매핑하는 매핑 개체를 제공합니다.
모듈 중복 및 무한 순환 종속성을 방지하기 위해 각 종속성별로 모듈이 이미 존재하는지 확인합니다.
마지막으로 애플리케이션의 모든 모듈이 포함된 그래프를 반환합니다.
종속성 그래프가 완료되면 번들을 생성하는 데 두 단계가 필요합니다
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.
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.
위 내용은 나는 모듈 번들러를 작성했습니다. 메모 등의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!