webpack、babel、vite笔记

webpack

webpack 是什么,解决了什么问题

zhuanlan.zhihu.com/p/44438844/

webpack 是一个现代 javascript 应用程序的静态模块打包器。 当 webpack 处理应用程序时,会递归后见一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle.

解决什么什么问题

  • 具备代码编译功能,将我们开发阶段编写的代码转换为大多数浏览器环境能运行的代码
  • webpack 通过 loader 和 plugin 的编译,提升了开发人员的开发效率

作用

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

核心概念

  • 入口(entry)
  • 输出 (output)
  • loader
  • 插件(plugin)
  • 模式(mode)
  • 浏览器兼容性()
  • 环境(environment)

= 在 webpack 里一个模块对应着一个文件,webpack 会从配置的 Entry 开始递归找出所有依赖的模块。

webpack 的工作原理,构建流程简介

  • 初始化:启动构建,读取和合并配置参数,
  • 编译:从入口文件出发,调用模块配置的 loader,对模板进行编译,然后再找到该模块依赖的模块进行编译,递归进行编译处理
  • 输出:根据入口和依赖之间的关系,将编译的模块组合成一个个的 Chunk,将 chunk 转换成文件根据配置输出到文件列表中。

webpack 的工作原理,构建流程(只需要看看)

初始化阶段:

  • 1.初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
  • 2.开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行 Compiler 对象的 run 方法开始执行编译;
  • 3.确定入口:根据配置中的 entry 找出所有的入口文件;

编译阶段:

  • 4.编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  • 5.完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;

生成阶段:

  • 6.输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
  • 7.输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

loader

原理

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。loader 本质上是导出为函数的 JavaScript 模块

loader 的分类

后置 post,普通 normal,行内 inline,前置 pre。

优先级

四种 loader 调用先后顺序为:pre > normal > inline > post 。

在相同种类 loader 的情况下,调用的优先级为,自下而上,自右向左。(pitch 情况下,则反过来)。

简单实现一个 loader

一个 loader 是一个导出为函数的 js 模块,这个函数有三个参数:content, map, meta

  • content: 表示源文件字符串或者 buffer
  • map: 表示 sourcemap 对象
  • meta: 表示元数据,辅助对象
// 获取webpack配置的options,写loader的固定套路第一步
cosnt { getOptions } = require('loader-utils')

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

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

复制代码

常用的 loader

  • less-loader :将 less 规格的内容转换为标准 css
  • css-loader :将 css 内容包裹为 JavaScript 模块
  • style-loader :将 JavaScript 模块的导出结果以 link 、style 标签等方式挂载到 html 中,让 css 代码能够正确运行在浏览器上

plugin

plugin 通常是在 webpack 在打包的某个时间节点做一些操作,Plugin便是负责功能扩展.

webpack 基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务,从而实现自己想要的功能。

实现一个 plugin

  • 一个 js 类
  • 原型上需要一个 apply 方法
  • 传给每个插件的 compiler 和 compilation 对象都是同一个引用,若在一个插件中修改了它们身上的属性,会影响后面的插件;
class MyPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('My Plugin', stats => {
      console.log('Bravo!');
    });
  }
}
module.exports = MyPlugin;
复制代码

babel

babel 就是一个 Javascript Transpiler。

用途

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

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

babel 的名字来自巴别塔的典故,是一个 js 转译器,用于 es next、typescript 等代码的转换,同时还暴露出了 api 让开发者可以进行特定用途的转换。除此以外,还可以做各种静态分析。

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

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>
复制代码

webpack 面试题

为什么开发时候的代码

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 是存储在内存中的

常见的 loader

raw-loader,file-loader,url-loader,babel-loader,less-loader,vue-loader, ts-loader

常见的 Plugin

  • DefinePlugin 定义环境变量 Webpack4 之后指定 mode 会自动配置
  • html-webpack-plugin
  • uglifyjs-webpack-plugin 压缩插件
  • clean-webpack-plugin 目录清理
  • ModuleConcatenationPlugin 开启 Scope
  • speed-measure-webpack-plugin 以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时)
  • webpack-bundle-analyzer 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)

loader 和 plugin 的区别

loader

module,chunk 和 bundle 的区别是什么?

我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle。

webpck 项目优化

查看打包后的页面

"preview": "node build/index.js --preview"
复制代码

运行时优化

  • 1.preload 插件去除 map 等不需要提前加载的 js 文件

  • 2.删除 prefetch,Prefetch 链接将会消耗带宽。

  • 3.因为开发环境的时候使用路由懒加载热更新变慢, 使用 babel-plugin-dynamic-import-node 使开发环境不需要懒加载

env: {
  development: {
    plugins: ['dynamic-import-node'];
  }
}
复制代码
    1. 增加 postcss.config.js 自动为 css 添加不同浏览器的标识前缀:
module.exports = {
  plugins: {
    autoprefixer: {}
  }
};
复制代码

打包时的优化

  • 1.升级 vue-cli 这一步不用配置已经提升了打包速度和包大小。

  • 2.splitChunks 对资源进行分块。element-ui,echarts 等

  • 3.分析一些大的文件再次进行分包

    1.1 大小 26.6 MB

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享