Home  >  Article  >  Web Front-end  >  An in-depth analysis of the packaging process and principles of webpack

An in-depth analysis of the packaging process and principles of webpack

青灯夜游
青灯夜游forward
2022-08-09 17:11:163317browse

How does Webpack implement packaging? The following article will give you an in-depth understanding of Webpack packaging principles. I hope it will be helpful to you!

An in-depth analysis of the packaging process and principles of webpack

As a front-end "siege lion", Webpack is all too familiar. Webpack can do too many things. It can combine all resources (including JS, TS, JSX , images, fonts and CSS, etc.) are packaged and placed in dependencies, so that you can reference dependencies to use resources as needed. Webpack has done an excellent job of translating multiple file resources on the front end and analyzing complex module dependencies. We can also customize the loader and load our own resources freely. So how does Webpack implement packaging? Come today and let’s take a look.

If we want to know the principles of Webpack packaging, we need to know two knowledge points in advance

1. What is require?

When it comes to require, the first thing that comes to mind may be import. Import is a syntax standard of es6.

– require is a runtime call, so require can theoretically be used in any part of the code. Place;

– import is called during compilation, so it must be placed at the beginning of the file;

When we use Webpack to compile, we will use babel to translate import into require. In CommonJS, There is a global method require(), which is used to load modules. AMD and CMD also use the require method to reference.

For example:

var add = require('./a.js');
add(1,2)

In simple terms, require is actually a function, and the referenced ./a.js is just a parameter of the function.

2. What are exports?

Here we can think of exports as an object, MDN export You can see the specific usage.

After understanding require and exports, we can start packaging

Let’s first look at the code structure after our packaging. We can find that require and exports will appear after packaging.

Not all browsers can execute require exports. You must implement require and exports yourself to ensure the normal operation of the code. The packaged code is a self-executing function. The parameters have dependency information and the code of the file. The executed function body executes the code through eval.

An in-depth analysis of the packaging process and principles of webpack

The overall design diagram is as follows:

An in-depth analysis of the packaging process and principles of webpack

Step 1: Write our configuration file

The configuration file configures our packaged entry entry and packaged exit output to prepare for subsequent generated files.

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./dist"),//打包后输出的文件地址,需要绝对路径因此需要path
    filename:"main.js"
  },
  mode:"development"

Second step: module analysis

Overall idea: It can be summarized as using fs file reading The entry file obtains the path of the import-dependent file through AST. If the dependent file still has dependencies, it will be recursed until the dependency analysis is clear and maintained in a map.

Detailed dismantling: Some people may wonder why AST is used because AST is born with this function. Its ImportDeclaration can help us quickly filter out the import syntax. Of course, regular matching is also possible. After all, the file is just a string after being read. The file dependency path can be obtained by writing awesome regular expressions, but it is not elegant enough.

step1: Create new index.js, a.js, b.js dependencies as follows

index.js file

import { str } from "./a.js";
console.log(`${str} Webpack`)

a.js file

import { b} from "./b.js"
export const str = "hello"

b.js file

export const b="bbb"

step2: Write Webpack

Module analysis: Use AST's @babel/parser Convert the string read from the file into an AST tree, perform syntax analysis using @babel/traverse, and use ImportDeclaration to filter out the import to find file dependencies.

    const content = fs.readFileSync(entryFile, "utf-8");
    const ast = parser.parse(content, { sourceType: "module" });
  
    const dirname = path.dirname(entryFile);
    const dependents = {};
    traverse(ast, {
      ImportDeclaration({ node }) {
        // 过滤出import
        const newPathName = "./" + path.join(dirname, node.source.value);
        dependents[node.source.value] = newPathName;
      }
    })
    const { code } = transformFromAst(ast, null, {
      presets: ["@babel/preset-env"]
    })
    return {
      entryFile,
      dependents,
      code
    }

The results are as follows:

An in-depth analysis of the packaging process and principles of webpack

Use recursion or loop to import files one by one for dependency analysis. Note here that we use for loop to analyze all Dependencies, the reason why the loop can analyze all dependencies, please note that the length of modules changes. When there are dependencies.modules.push new dependencies, modules.length will change.

 for (let i = 0; i <p><strong><span style="font-size: 18px;">Step 3: Write the WebpackBootstrap function to generate the output file</span></strong></p><p><strong>编写</strong> <strong>WebpackBootstrap</strong> <strong>函数</strong>:这里我们需要做的首先是 WebpackBootstrap 函数,编译后我们源代码的 import 会被解析成 require 浏览器既然不认识 require ,那我们就先声明它,毕竟 require 就是一个方法,在编写函数的时候还需要注意的是作用域隔离,防止变量污染。我们代码中 exports 也需要我们声明一下,保证代码在执行的时候 exports 已经存在。</p><p><strong>生成输出文件</strong>:生成文件的地址我们在配置文件已经写好了,再用 fs.writeFileSync 写入到输出文件夹即可。</p><pre class="brush:php;toolbar:false">  file(code) {
    const filePath = path.join(this.output.path, this.output.filename)
    const newCode = JSON.stringify(code);
    // 生成bundle文件内容
    const bundle = `(function(modules){
      function require(module){
        function pathRequire(relativePath){
          return require(modules[module].dependents[relativePath])
        }
        const exports={};
        (function(require,exports,code){
          eval(code)
        })(pathRequire,exports,modules[module].code);
        return exports
      }
      require('${this.entry}')
    })(${newCode})`;
      //  WebpackBoostrap
    // 生成文件。放入dist 目录
    fs.writeFileSync(filePath,bundle,'utf-8')
  }

An in-depth analysis of the packaging process and principles of webpack

第四步:分析执行顺序

An in-depth analysis of the packaging process and principles of webpack

我们可以在浏览器的控制台运行一下打包后的结果,如果能正常应该会打印出 hello Webpack。

An in-depth analysis of the packaging process and principles of webpack

总结

通过以上的分析,我们应该对 Webpack 的大概流程有基本的了解,利用 AST 去解析代码只是本次演示的一种方式,不是 Webpack 的真实实现,Webpack 他自己有自己的 AST 解析方式,万变不离其宗都是拿到模块依赖,Webpack 生态是很完整,有兴趣的童鞋可以考虑以下三个问题:

  • 如果出现组件循环引用那又应该如何处理?
  • Webpack 是如何加载 loader 的?
  • 犹大大极力推荐的 vite 可以实现按需打包,大大提升开发时候打包速度,如果是 webapck 又是应该如何实现?

更多编程相关知识,请访问:编程视频!!

The above is the detailed content of An in-depth analysis of the packaging process and principles of webpack. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete