나는 모듈 번들러를 작성했습니다. 메모 등

王林
풀어 주다: 2024-07-25 03:10:43
원래의
424명이 탐색했습니다.

I wrote a module bundler. notes, etc

간단한 JavaScript 번들러를 만들어봤는데 생각보다 훨씬 쉬웠습니다. 이번 포스팅에서 제가 배운 모든 것을 공유하겠습니다.

대규모 애플리케이션을 작성할 때는 JavaScript 소스 코드를 별도의 js 파일로 나누는 것이 좋습니다. 그러나 여러 스크립트 태그를 사용하여 이러한 파일을 html 문서에 추가하면 다음과 같은 새로운 문제가 발생합니다.

  • 글로벌 네임스페이스 오염.

  • 경합 조건.

모듈 번들러는 다양한 파일의 소스 코드를 하나의 큰 파일로 결합하여 단점을 피하면서 추상화의 이점을 누릴 수 있도록 도와줍니다.

모듈 번들러는 일반적으로 이 작업을 두 단계로 수행합니다.

  1. 항목 파일부터 시작하여 모든 JavaScript 소스 파일을 찾습니다. 이를 종속성 해결이라고 하며 생성된 맵을 종속성 그래프라고 합니다.
  2. 종속성 그래프를 사용하여 브라우저에서 실행할 수 있는 대규모 JavaScript 소스 코드 문자열인 번들을 생성합니다. 이는 파일에 작성하고 스크립트 태그를 사용하여 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에 간단히 매핑하는 매핑 개체를 제공합니다.
모듈 중복 및 무한 순환 종속성을 방지하기 위해 각 종속성별로 모듈이 이미 존재하는지 확인합니다.
마지막으로 애플리케이션의 모든 모듈이 포함된 그래프를 반환합니다.

번들링

종속성 그래프가 완료되면 번들을 생성하는 데 두 단계가 필요합니다

  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.
로그인 후 복사

위 내용은 나는 모듈 번들러를 작성했습니다. 메모 등의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!