webpack自己整理

204 阅读8分钟

1.工程化的概念

就是通过各种工具和技术,提升前端的开发效率。包括开发,编译,部署等各个阶段。

2.模块化的思想,commonjs,esmodule

  • 早期的模块化,命名空间,自执行函数,

CommonJS 它是 Node.js 中所遵循的模块规范,

一个文件就代表了一个模块。每个文件都有单独的作用域,通过 module.exports 导出成员,通过 require 函数载入模块。是以同步的方式加载模块,所以运行效率较低

前端模块,浏览器环境的异步加载模块规范

  • AMD()规范,requirejs,依赖前置,所有的 require 都会提前执行
  • CMD 规范,seajs,就近依赖或者依赖后置,下载完之后,并不执行加载,回调函数中遇到 require 时才执行
  • esmodule,是 ECMAScript 2015(es6)中制定的标准,通过 export 导出,import 导入。

3.webpack 的原理和构建流程

webpack 是现代 js 应用程序的静态模块打包工具,他从入口文件出发,对模块进行编辑,根据依赖关系递归进行编译,构建出一个依赖树。生成一个或者多个文件 bundle.

解决了什么问题

  • 模块打包: 可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。保证项目结构的清晰和可读性。
  • 编译兼容: 可以对代码做 polyfill ,还可以编译转换 .less, .vue, .jsx 文件。具备代码编译功能,将我们开发阶段编写的代码转换为大多数浏览器环境能运行的代码
  • 能力拓展:通过 webpack 的 Plugin 机制,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。

构建流程(简述)

  • 初始化:读取和合并配置,加载 plugin,实例化 complier
  • 编译:从入口文件出发,针对每个模块,调用模块对应的 loader 进行编译,然后根据依赖关系递归编译模块所依赖的模块。
  • 输出:将编译后的模块组合成一个个的 chunk,将 chunk 转换成 bundle 文件。根据配置输出到文件列表中

稍微详细版本

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  • 初始化:读取和合并配置实例化 complier,加载 plugin 配置插件,执行对象的 run 方法开始执行编译
  • 编译:从入口文件出发,针对每个模块,调用模块对应的 loader 进行编译,然后根据依赖关系递归编译模块所依赖的模块。得到每个模块被翻译后的最终内容以及它们之间的依赖关系
  • 输出:根据入口和模块之间的依赖关系,将编译后的模块组合成一个个的 chunk,将 chunk 转换成 bundle 文件。根据配置确定输出的路径和文件名,输出到文件列表中

4.loader 和 plugin 的本质和区别

loader

  • loader(直译器):本质上是一个函数,在函数中对导入的资源进行处理,然后到处一个为函数的 javascript 模块
  • 为什么需要 loader,因为 webpack 把一切视为模块,而本身只能识别 js 和 json 文件。所以需要 loader 把其他类型的文件转换为 wepack 能识别解析的文件。

plugin

  • webpack 在运行的生命周期中会广播出许多事件,plugin 通过监听这些事件,就可以在特定的阶段执行自己的插件任务,从而实现自己想要的功能改变输出结果。

区别

  • loader 是解析文件,plugin 是拓展功能
  • loader 是在 module.rules 作为模块的解析规则,类型为数组。配置什么类型的文件需要什么样的 loader 在什么范围和不包括什么范围进行配置,plugin 在 plugins 里面单独配置,类型为数组,每一项是一个 Plugin 的实例,
// 获取webpack配置的options,写loader的固定套路第一步
cosnt { getOptions } = require('loader-utils')

module.exports = function(content, map, meta){
  const options = getOptions(this) || {}

  cosnt code = JSON.stringify(content);
  const esModule  = typeof options.esModule !== 'undefined' ? options.esModule : true
  // 返回内容
  return `${esModule ? 'export default' : 'module.exports ='} ${json};`;
}

5.webpack 优化

开发优化

  • 1.preload 插件去除 map 等不需要提前加载的 js 文件
  • 2.删除 prefetch,Prefetch 链接将会消耗带宽。
  • 3.因为开发环境的时候使用路由懒加载热更新变慢, 使用 babel-plugin-dynamic-import-node 使开发环境不需要懒加载

编译优化

  • 使用高版本的 Webpack 和 Node.js
  • 多进程/多实例构建:HappyPack(不维护了)、thread-loader
  • external,dll 对不常改变的模块使用 cdn,或者打包成 dll 包引入,减少编译的时间。
  • dllPlugin,DllReferencePlugin
  • 通过 include 和 exclude 来限制 loader 编译返回
  • 按需加载框架里面都有了
  • 充分利用缓存提升二次构建速度:babel-loader 开启缓存

体积优化

  • splitChunks 进行分包
  • 最小化入口 chunk
new CommonsChunkPlugin({
  name: 'manifest',
  minChunks: Infinity
});

6.babel 是什么

babel 就是一个 Javascript Transpiler。

用途

我们平时主要用 babel 做 3 件事情

  • 转译 exNext、typescript、flow 等到目标环境支持的 js
  • 一些特定用途的代码转换。比如自动埋点
  • 代码的静态分析

babel 是如何把 es6 转成 es5 的

babel 的转译过程分为三个阶段:parsing(解析)、transforming(转化)、generating(生成)

  • Parse: 通过 parser 把源码转成抽象语法树 (AST)

babel 将 es6+(指 es6 及以上版本)分为语法层和 api 层:语法层: let、const、class、箭头函数等,这些需要在构建时进行转译,是指在语法层面上的转译,(比如 class...将来会被转 译成 var function...) api 层:Promise、includes、map 等,这些是在全局或者 Object、Array 等的原型上新增的方法,它们可以由相应 es5 的方式重 新定义

  • Transfrom: 遍历 AST, 调用各种 transform 插件对 AST 进行增删改
  • generate: 把转换后的 AST 打印成目标代码,并生成 sourcemap

7.vite

是一个基于浏览器原生 ES 模块导入的开发服务器,在开发环境下,利用浏览器去解析 import,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随启随用。同时不仅对 Vue 文件提供了支持,还支持热更新,而且热更新的速度不会随着模块增多而变慢。在生产环境下使用 Rollup 打包

  • 1.Webpack、Rollup 这些构建工具在本地开发调试的时候,会提前把模块打包成浏览器可读取的 js bundle。
  • 2.如路由懒加载等优化手段,但懒加载并不代表懒构建,Webpack 还是需要把你的异步路由用到的模块提前构建好。
    1. Vite 利用了浏览器原生 ES Module 的支持,在本地启动一个服务器,当浏览器读取这个 html 文件的时候,会在执行到 import 的时候才向服务端发送请求模块的请求。
  • 4.vite 的服务端会通过内部的一些机制,将模块解析成浏览器可以执行的 js 文件返回到浏览器端。
  • 5.这就保证了只用真正使用这个模块的时候,浏览器才会请求且解析加载,最大程度的做到了按需加载
  • 6.依赖预编译,其实是 Vite 2.0 在为用户启动开发服务器之前,先用 esbuild 把检测到的依赖预先构建了一遍,全部打包成为一个传统的 js bundle
<div id="app"></div>
<script type="module">
  import { createApp } from 'vue';
  import Main from './Main.vue';
  createApp(Main).mount('#app');
</script>

8.HMR(Hot Module Replacement)热更新又称热替换

当你对代码进行修改并保存后,webpack 将对代码重新打包,并将新的模块发送到浏览器端,浏览器通过新的模块替换老的模块,这样在不刷新浏览器的前提下就能够对应用进行更新。

  • HMR 的核心就是客户端从服务端拉取更新后的文件。准确的说是 chunk diff(chunk 需要更新的部分),
  • 实际上 WDS(webpack-dev-server)与浏览器之间维护了一个 websocket 进行通信。
  • 当本地资源发生变化后, WDS 会向浏览器推送更新,并带上构建时的 hash,让浏览器与上一次的资源进行对比
  • 客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),也就是请求 update.json,
  • 这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该 chunk 的增量更新也就是请求 update.js。更新的 js 是存储在内存中的

其他

使用 webpack 开发时,你用过哪些可以提高效率的插件?

  • webpack-dashboard:可以更友好的展示相关打包信息。
  • webpack-merge:提取公共配置,减少重复配置代码