Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack小笔记 #25

Open
chenjigeng opened this issue Aug 10, 2021 · 0 comments
Open

Webpack小笔记 #25

chenjigeng opened this issue Aug 10, 2021 · 0 comments
Labels

Comments

@chenjigeng
Copy link
Owner

Webpack 本质上是一个模块打包器,可以根据依赖图来将我们的文件打包。

原理

Webpack 的生命周期借助于 tapable 。tapable 类似于发布订阅库,webpack 的 hooks 就是 webpack 的生命周期,里面都是 tapable 一个实例,webpack 在适当的时机会触发这些生命周期。plugins 本质上则是通过监听 hooks 来做一些自定义处理。

  1. 根据文件配置,找到入口文件。

  2. 编译入口文件,使用配置的 loader 来处理文件。

  3. 将 loader 处理完后的文件,再由 webpack 做处理,这一步主要两件事情

    1. 将 require 转换为 webpack 自定义的 webpack_require。webpack 在这里会做文件的缓存处。
    2. 根据 require 来搜集该文件的依赖项,将依赖项加入到处理的文件列表里,之后递归做处理。
  4. 文件处理完后,输出bundle文件(emit)。

Webpack 解决依赖?

源码

对于不同的js模块方式(AMD/commonjs/import),有不同的处理方式。

比如 commonjs

compilation.dependencyFactories.set(

    CommonJsRequireContextDependency,

    contextModuleFactory

);

contextModuleFactory 则是专门用来处理依赖的。

Tree Shaking

用途:可以将你引入,但是没有用到的模块在构建的时候清除掉。比如你依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

局限:必须使用 es module。

局限的原因:因为它依赖了ES6的模块特性。

ES6模块特性

ES6 模块是静态分析的,不允许动态加载模块,所以可以在编译的时候就知道了模块间的依赖关系。

  • 只能作为模块顶层的语句出现
  • import 的模块名只能是字符串常量
  • import binding 是 immutable的

去除无用代码的范围:

  • 模块的函数
  • 模块的变量

不能去除类。

模块

对于 webpack 来说,万物都是模块。由于 webpack 本身是用来加载 js 文件的,所以 webpack 是把所有的其他模块都当做 js 模块来处理的。因此,比如加载 css 文件的时候,会将 css 代码转为纯文本,并通过生成一段 js 代码来讲 css 纯文本转换为 style 标签并放到页面里。大概如下

const jsContent = `

const code = ${JSON.stringify(code)};

const styleEl = document.createElement("style");

const codeEl = document.createTextNode(code);

styleEl.type = 'text/css';

styleEl.appendChild(codeEl);

document.head.appendChild(styleEl);`;



fs.writeFileSync(filename, jsContent);

HMR

Hmr 是可以让文件修改可以不重新刷新页面,主要步骤如下

  1. 执行构建命令,并开启一个服务器
  2. 服务器和客户端使用 ws 进行通信。
  3. 当文件发生变化的时候,构建完成后,服务端通过 ws 给客户端发送新的模块的 id 和内容
  4. 客户端加载完新的模块后,调用旧模块的 accept 和 dispose(dispose 主要用来清除副作用)

拿 css 为例子,需要再接收到 HMR 后,在 dispose 的时候,移除之前挂载的 style 标签

import * as __SNOWPACK_HMR_API__ from '/${buildOptions.metaDir}/hmr.js';

import.meta.hot = __SNOWPACK_HMR_API__.createHotContext(import.meta.url);

import.meta.hot.accept();

import.meta.hot.dispose(() => {

    document.head.removeChild(styleEl);

});

Devtool

Devtool 可以影响生成的代码和生成的 source-map。默认是 eval,就是生成的代码用 eval 包起来,并且没有 source-map

Vite

原理

  • 预编译:在项目初始化的时候,通过扫描入口文件的所有 html ,找到所有引用的 node_modules 包,并将其预编译好放在 node_modules/.vite 目录下
  • 按需编译:对于其他的文件,则在访问具体的文件后,再编译并返回。而对于该文件的依赖项,则是按需编译+返回。
  • No-bundle: 使用原生 es module 的特点,不事先把所有数据给 bundle, 而是采取按需 bundle (依赖浏览器 es module 的能力),因此启动速度会快很多。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant