工程化


 

  1. 初始化参数,webpack.config.js 的module.export ,结合默认参数,merge出最终的参数。
  2. 开始编译,通过初始化参数来实例化 Compiler对象,加载所有配置的插件,执行对象的run方法。
  3. 确认入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的Loader对模块进行加载,再找出该模块依赖的模块。通过递归这个过程直到所有入口文件都经过处理,得到一条依赖线。
  5. webpack.js 中核心的操作就是 require 了 node_modules/webpack-cli/bin/cli.js
    • cli.js
      • 01 当前文件一般有二个操作,处理参数,将参数交给不同的逻辑(分发业务)
      • 02 options (初始化参数)
      • 03 complier (实例化 Compiler对象)
      • 04 complier.run( 那 run 里面做了什么,后续再看 )
    • 实例化complier对象,complier会贯穿整个webpack工作流的过程。
      • complier继承Tabable,所以complier具有操作钩子的能力。例如监听、触发事件,而webpack是个事件流。
      • 实例化complier对象时,会把很多属性挂载上去。其中NodeEnvironmentPlugin让complier具备文件读写的能力。
      • plugins中的插件都挂载到了complier身上
      • 将内部默认的pluginscomplier建立联系,其中有个EntryOptionsPlugin处理了入口模块的id
      • webpack/lib/SingleEntryPlugin.js里,compiler监听了make钩子
        • singleEntryPlugin.js 模块的apply方法中有两个钩子监听
        • dep = SingleEntryPlugin.createDependency(entry,name)
        • 其中compilation钩子就是让compilation具备了利用normalModuleFactory工厂创建一个普通模块的能力。因为compilation就是利用自己创建出来的模块来加载需要被打包的模块。
        • 其中make钩子在Compiler.run 的时候会被调用,到这里就意味着某个模块执行打包之前的所有准备工作就做完了。
        • 然后Compilation调用addEntry就标志着make构建阶段开始了。
      • run方法的执行
        • 刚说了Compiler.run方法执行会调用make钩子,那run方法里就是有一堆钩子按着顺序触发,例如 beforeRunruncompile
        • compile 方法的执行
          • 先准备些参数,例如刚才提到的 normalModuleFactory,用于后续创建模块。
          • 触发 beforeCompile
          • 将准备参数传入一个方法(newCompilation),用于创建一个compilation。在 newCompilation 内部,先调用 createCompilation,然后触发this.compilation钩子和compilation 钩子的监听
        • 创建了compilation对象之后就触发了make钩子。当触发 make 钩子监听的时候,会将 compilation 对象传入。 compilation.addEntry 就意味着make构建阶段开始。
        • make 钩子被触发,接收到 compilation 对象,那么从 compilation 可以解构出三个值。entry:当前被打包模块的相对路径,namecontext:当前项目的跟路径
        • processDependencies 处理模块间的依赖关系。函数内部通过async.forEach来递归创建每个被加载进来的模块。
        • compilation 调用 addEntry 方法,内部调用_addModuleChain方法去处理依赖。
        • compilation 当中可以通过 normalModuleFactory 工厂来创建一个普通的模块对象。 webpack 内部默认开启来一个100并发量的打包操作. 源码里看到的是normalModuleFactory.create这样一个方法。
        • 然后在 beforeResolve 方法里会触发一个 factory 钩子监听。上述操作完成后,factory 获取到一个函数并对其进行调用。函数中又有一个resolver钩子被触发,resolver其实是处理loader当触发resolver钩子,就意味着所有的Loader处理完毕。
        • 接下里就会触发 afterResolve 这个钩子,调用 new NormalModule
        • 最后就是调用 buildModule 方法开始编译 -> 调用build -> 调用doBuild。bulid过程中会将js代码转化成ast语法树,如果当前js模块引用了其它模块,那就需要递归重复 bulid。当前所有入口模块都被存放在 compilation 对象的 entries 数组里。
        • 那我还需要对当前模块的ast语法树进行一些修改,再转化回js代码。例如将require转化成__webpack_require__
        • 最后compile方法最后调用compilation.seal方法去处理chunk。 生成代码内容,最终输出文件到指定打包路径下。

 https://mp.weixin.qq.com/s?__biz=Mzg3OTYwMjcxMA==&mid=2247483744&idx=1&sn=d7128a76eed20746cd8c5100f0899138&scene=21#wechat_redirect

babel 原理

  1. babel 主要是将 A 类型的文件转化为 B 类型
  2. webpack 只支持 JS 文件,所以需要将其他文件类型都转化为 JS 文件
  3. parse => 主要将代码转化为 AST
  4. traversal => 遍历 AST 进行修改
  5. generator => 将修改后的 AST 转化为 code

webpack 流程

webpack 常见 loader 和 plugin 有哪些?二者区别是什么?

loader

  1、file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件

  2、url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去

  3、source-map-loader:加载额外的 Source Map 文件,以方便断点调试

  4、image-loader:加载并且压缩图片文件

  5、babel-loader:把 ES6 转换成 ES5

  6、css-loader:加载 CSS,支持模块化、压缩、文件导入等特性

  7、style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS

  8、eslint-loader:通过 ESLint 检查 JavaScript 代码

  9、vue-loader:解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理

  10、svg-url-loader:是位置支持webpack打包时候可以引入svg文件

  11、postcss-loader:用于补充css样式各种浏览器内核前缀,太方便了,不用我们手动写啦

  12、ts-loader:用于配置项目typescript

plugin

  1. HtmlWebpackPlugin => 创建 HTML 文件并自动引入 JS 和 css
  2. CleanWebpackPlugin => 用于清理之前打包的残余文件
  3. SplitChunksPlugin => 用于代码分包
  4. DLLPlugin + DLLReferencePlugin => 用于避免大依赖被频繁重新打包,大幅降低打包时间
  5. EslintWebpackPlugin => 用于检查代码中的错误
  6. DefinePlugin => 用于在 webpack config 中添加全局变量
  7. CopyWebpackPlugin => 用于拷贝静态文件到 dist
  8. css-Modules=>实现css模块分离
  9. extract-text-webpack-plugin=>实现一个入口生成一个css文件
  10. mini-css-extract-plugin(4.0) =>实现了css按需加载
  11. commonsChunkPlugin => 提取公共文件

区别

  1. loader 是文件加载器,主要使用在 make 阶段,将 A 类型的文件转化为 B 类型。它能够对文件进行编译、优化、压缩等
  2. plugin 是 webpack 插件,plugin 可以挂载在整个 webpack 打包过程中的钩子中,可以实现更多功能,如定义全局变量、加速编译、Code Split

webpack 如何解决开发时的跨域问题

  1. 在配置中添加代理即可
    devService: {
       proxy: {
          '/api': {
             target: 'http://host', 
             changeOrigin: true
          }
       }
    }
    

如何实现 tree-shaking?

  1. tree-shaking 就是让没有用到的 JS 代码不打包,以减少包的体积
  2. 利用 tree-shaking 部分 JS 代码
    1. 使用 ES2015 模块语法,即 export 和 import。
    2. 不要使用 CommonJS,CommonJS 无法 tree-shaking => babel-loader 添加 modules: false 选项
    3. 引入的时候只引用需要的模块
      • import {cloneDeep} from 'lodash-es' => 仅仅打包 cloneDeep
      • import _ from 'lodash' => lodash 全部打包,无法 tree-shaking 没有用到的模块
  3. 不 tree-shaking JS 代码如何开启 tree-shaking => 在 webpack config 中将 mode 设置为 production => mode: production 给 webpack 加了非常多的优化
    1. 在项目的 package.json 中添加 "sideEffects" 属性,防止某些文件被 tree-shaking
      • case: import x.js x.js 添加了 window.x 属性,那么 x.js 就要放到 sideEffects 中
      • 所有被 import 的 CSS 都要放在 sideEffects 中

如何提高 webpack 构建速度

  1. 使用 DLLPlugin 将不常变化的代码提前打包并复用,如 Vue、React
  2. 使用 thread-loader 进行多线程打包
  3. 处于开发环境时,在 webpack config 中将 cache 设为 true
  4. 处于生产环境时,关闭不必要的环节,如 source map

Webpack 和 Vite 的区别

开发环境区别

  1. Vite 自己实现 server,不对代码打包,充分利用浏览器对 <script type=module> 的支持
    • 假设 main.js 引入了 vue
    • 该 server 会把 import {createApp} from 'vue' 改为 import {createApp} from '/node_modules/.vite/vue.js'
      这样浏览器就知道去哪里找 vue.js 了
  2. webpack-dev-server 常使用 babel-loader 基于内存打包,比 Vite 慢很多很多
    • 该 server 会把 vue.js 的代码(递归地)打包进 main.js

生产环境区别

  1. Vite 使用 rollup + esbuild 来打包 JS 代码
  2. Webpack 使用 babel 来打包 JS 代码,比 esbuild 慢很多很多

文件处理时机

  1. Vite 只会在你请求某个文件的时候处理该文件
  2. Webpack 会提前打包好 main.js,等你请求的时候直接输出打包好的 JS 给你

目前已知 Vite 缺点

  1. 热更新常常失败
  2. 有些功能 rollup 不支持,需要自己写 rollup 插件
  3. 不支持非现代浏览器

Webpack 怎么配置多页应用

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    app: './src/app.js',
    admin: './src/admin.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      chunks: ['app']
    }),
    new HtmlWebpackPlugin({
      filename: 'admin.html',
      chunks: ['admin']
    })
  ]
}
  1. 但是这样配置后会有一个重复打包的问题:假设 app.js 和 admin.js 都引入了 vue.js,那么 vue.js 的代码会打包进 app.js,也会打包进 admin.js
  2. 我们需要使用 optimization.splitChunks 将共同依赖单独打包成 common.js,HtmlWebpackPlugin 会自动引入 common.js

swc、esbuild 是什么?

swc

  • 实现语言 => Rust
  • 功能 => 编译 JS/TS,打包 JS/TS => TS: 类型擦除
  • 优势 => 比 babel 快很多很多(20倍以上)
  • 能否集成进 webpack => 可以
  • 缺点 => 1. 对 TS 代码进行类型检查(用 tsc 先检查再打包)+ 2. 无法打包 CSS/SVG 等非 JS 文件

esbuild

  • 实现语言 => GO
  • 功能 => 编译 JS/TS,打包 JS/TS => TS: 类型擦除
  • 优势 => 比 babel 快很多很多(10 - 100倍)
  • 能否集成进 webpack => 可以
  • 缺点 => 1. 对 TS 代码进行类型检查(用 tsc 先检查在打包)+ 2. 无法打包 CSS/SVG 等非 JS 文件

Docker

  1. Docker 是一种技术
  2. 在系统上安装 Docker 软件即可使用
  3. 核心概念
    1. 容器(Container) => 一台虚拟的计算机,拥有独立的网络、文件系统、进程。默认和宿主机不发生任何交互
    2. 镜像(Image) => 一个预先定义好的模板文件,Docker 引擎按照这个模板文件启动无数个一模一样,互不干扰的容器。默认是分层的 => 复用、节省空间
  4. 命令 =>
    1. docker run --name -v -p -e
    2. docker images
    3. docker ps
  5. 解决了什么问题 =>实现隔离技术 => namespace + Control Groups
    1. 保证开发、测试、交付、部署的环境完全一致
    2. 保证资源的隔离
    3. 启动临时的、用完即弃的环境,如测试
    4. 秒级的超大规模的部署和扩容
posted @ 2023-05-10 09:00  前端菜园子  阅读(36)  评论(0编辑  收藏  举报