前端工程化之概念介绍

2022-08-25 14:59:57 浏览数 (1)

前言

大家好,我是柒八九。从今天起,我们又双叒叕yòu shuāng ruò zhuó, 开辟了一个新的领域:「前端工程化」。可能大家对这个词,比较陌生,但是如果把这个问题具象化,那就会感觉到倍感亲切。我简单罗列几个概念和技术

  • 脚手架
  • SouceMap
  • Babel/SWC
  • Gulp/Webpack/Rollup/Vite打包框架
  • DevServer - HMR/LiveReload
  • CI/CD
  • Three Shaking/DCE/Mangle
  • 等等

有的概念很熟悉,毕竟每天都用npm run dev/start启动了一个,然后利用某种打包工具对网站资源进行增量更新,并推送到浏览器端,实现HMR,省去了刷新全页来获取最新的资源信息。使开发变得更加「丝滑顺畅」

而刚才讲的只是「前端工程化」的冰山一角,在很多地方还有很多的细节点和设计模式需要去挖掘和探讨。既然,这是一个需要探索和研究的领域,并且和我们平时工作息息相关。那就有必要做一个专题来研究。

前端工程化

正如上面所逻辑的技术点和框架,都是一些「大而全」的技术,所以,我们有必要厘清一些比较基础且容易被忽略的基础概念。「万丈高楼平地起」也需要一个夯实的地基做支撑。

那我们就闲话少叙,开车走起。

文章主要点:

  1. 脚手架
  2. Source Map

1. 何为脚手架

脚手架一词最早来源于建筑工程领域。

❝脚手架作为一种创建项目「初始文件」的工具被广泛地应用于「新项目」或者「跌代初始阶段」

使用工具替代人工操作能够避免人为失误引起的低级错误,同时结合整体前端工程化方案,快速生成功能「模块配置」「自动安装依赖」等,降低了时间成本。

项目文件结构

简单概括脚手架的功能就是:「创建项目初始文件」

而项目初始化文件,一般都是有一些既定的格式或者模板。

代码语言:javascript复制
package.json             1) npm 项目文件 

package-lock.json        2) npm 依赖 lock 文件 

public/                  3) 预设的静态目录 

src/                     3) 源代码目录 

    main.ts                   3) 源代码中的初始入口文件 

    router.ts                 3) 源代码中的路由文件 

    store/                    3) 源代码中的数据流模块目录 

webpack/                  4) webpack 配置目录 

    common.config.js          4) webpack 通用配置文件 

    dev.config.js             4) webpack 开发环境配置文件 

    prod.config.js            4) webpack 生产环境配置文件 

.browserlistrc            5) 浏览器兼容描述 browserlist 配置文件 

babel.config.js           5) ES 转换工具 babel 配置文件 

tsconfig.json             5) TypeScript 配置文件 

postcss.config.js         5) CSS 后处理工具 postcss 配置文件 

.eslintrc                 6) 代码检查工具 eslint 配置文件 

jest.config.js            6) 单元测试工具 jest 配置文件 

.gitignore                7) Git 忽略配置文件 

README.md                 7) 默认文档文件

简单来解释各个文件的含义

文件/文件夹

文件含义

1) package.json

npm 依赖管理体系下的基础配置文件

2) 包管理器

使用 npm 或 Yarn,会在项目里添加上对应的 lock 文件,「确保在不同环境下部署项目时的依赖稳定性」

3) 确定项目技术栈

可以采用React/Vue来构建视图

4) 构建工具

构建工具的主流选择还是 webpack增加相关的 webpack 配置文件: 开发环境/生产环境

5) 构建流程

安装与配置各种 Loader 、插件和其他配置项

6) 选择和调试辅助工具

代码检查工具/单元测试工具

7) 收尾工作

编写说明文档 README.md不需要纳入版本管理的文件目录记入 .gitignore

整个流程可以简单归纳为

  1. 「选定方案」
  2. 「配置方案」细节
  3. 「配置完成」
  4. 根据定制方案「创建项目文件」
  5. 结束流程

常见脚手架工具

现在主流的前端脚手架工具有两类:

名称

模板框架

多选项生产

支持自定义模板

特点

Create-React-App

React

React 官方维护

Vue CLI

Vue

Vue官方维护

  • 「CRA」: Facebook 官方提供的 React 开发工具集,
    • 包含两个基础包
    • 自定义配置能力
    1. react-app-rewired:利用config-overrides.js 文件来对 webpack 配置进行扩展
    2. customize-cra:利用react-app-rewired的配置文件config-overrides.jswebpack配置进行修改
    3. create-react-app 用于选择脚手架「创建项目」
    4. react-scripts 则作为所创建项目中的「运行时依赖包」,提供了封装后的项目「启动、编译、测试」等基础工具
  • 「Vue CLI」: Vue CLI 由 Vue.js 官方维护,其定位是 Vue.js 快速开发的完整系统。完整的 Vue CLI 由三部分组成
    1. 作为全局命令的 @vue/cli
    2. 作为项目内集成工具的 @vue/cli-service
    3. 作为功能插件系统的 @vue/cli-plugin

当然,CRA/Vue CLI是现在比较流行的框架级别的脚手架,其实还有一些老牌通用的脚手架,只不过它们的应用市场很少了,但是有些场景也是有点用的。例如:Yeoman,由 Google I/O 在 2012 年首次发布,基于特定生成器(Generator)来创建项目基础代码的功能,有庞大的生成器仓库。

脚手架模板

在实际开发中,我们可以通过创建脚手架对应的模板对项目进行「定制化处理」

定制化模板可以「弥补」官方提供基础工具集不满足特定需求的场景。

定制化有如下优点(但有不仅限这些优点)

  • 为项目引入「新的」通用特性
  • 针对构建环节的 webpack 配置优化,来提升开发环境的效率和生产环境的性能等
  • 定制符合「团队内部规范」的代码检测规则配置
  • 定制单元测试等「辅助工具模块」的配置项
  • 定制符合团队内部规范的「目录结构与通用业务模块」
    • 业务组件库
    • 辅助工具类
    • 页面模板

我们简单的为CRA配置一个最简单的模板。

为 CRA 创建自定义模板

作为一个最简化的 CRA 模板,模板中包含如下必要文件

  • README.md:用于在 npm 仓库中显示的「模板说明」
  • package.json:用于描述模板本身的「元信息」, 例如名称、运行脚本、依赖包名和版本等
  • template.json:用于描述基于模板创建的项目中的 package.json 信息
  • template 目录:用于「复制」到创建后的项目中,gitignore 在复制后重命名为 .gitignore,「public/index.html和src/index 为运行 react-scripts 的必要文件」

对应的目录结构如下:

代码语言:javascript复制
cra-template-[template-name]/
  README.md (for npm)
  template.json
  package.json
  template/
    README.md (for projects created from this template)
    gitignore
    public/
      index.html
    src/
      index.js (or index.tsx)

在使用时,还是需要将模板通过 npm link 命令「映射」到全局依赖中或发布到 npm 仓库中。

然后执行创建项目的命令

代码语言:javascript复制
 npx create-react-app [app-name] --template [template-name]

❝脚手架的「功能和本质」:功能是「创建项目初始文件」,本质是「方案的封装」

2. Source Map

Source Map 简介

Source Map:「映射」转换后的代码与源代码之间的关系

一段转换后的代码,通过转换过程中生成的 Source Map 文件就可以「逆向解析」得到对应的源代码。

Source Map是一个 JSON 格式的文件,这个 JSON 里面记录的就是「转换后和转换前」代码之间的映射关系。

你可以认为:

「Souce Map 就是存储于JSON文件中的Map(哈希表)」

Source Map 的基本原理

在编译器(Babel/SWC)编译处理的过程中,在生成产物代码的同时,也生成产物代码中被转换的部分与源代码中相应部分的「映射关系表」

有了完整的映射表,就可以通过 Chrome 控制台中的"Enable Javascript source map"来实现调试时的显示与定位源代码功能。

「chrome-setting」

「perference-Enable Javascript source map」

Source map的格式

代码语言:javascript复制
{
    version : 3,
    file: "out.js",
    sourceRoot : "",
    sources: ["foo.js", "bar.js"],
    names: ["src", "maps", "are", "fun"],
    mappings: "AAgBC,SAAQ,CAAEA"
}

整个文件就是一个「JavaScript对象」,可以被解释器读取。

字段名

含义

version

Source Map的「版本」,目前为3最常用的方法是使用Google的Closure「编译器」

file

转换后的文件名

sourceRoot

「转换前」的文件所在的目录如果与转换前的文件在同一目录,该项为空

sources

记录的是转换前的源文件名称 「因为有可能出现多个文件打包转换为一个文件的情况,所以这里是一个数组」

names

源代码中使用的一些「成员名称」 压缩代码时会将「开发阶段」编写的有意义的「变量名」替换为一些「简短的字符」这个属性中记录的就是「原始的名称」

mappings

它是一个叫作 base64-VLQ 编码的字符串 里面记录的信息就是转换后代码中的「字符」与转换前代码中的字符之间的「映射关系」

❝这里额外对mappings字段做一个介绍 这是一个很长的字符串,它分成「三层」

  1. 第一层是「行对应」,以分号(;)表示,「每个分号对应转换后源码的一行」
  2. 第二层是「位置对应」,以逗号(,)表示,「每个逗号对应转换后源码的一个位置」
  3. 第三层是「位置转换」,以VLQ编码表示,「代表该位置对应的转换前的源码位置」

mappings:"AAAAA,BBBBB;CCCCC":转换后的源码分成「两行」,第一行有两个位置,第二行有一个位置。

❝一般我们会在「转换后的代码中」通过「添加一行注释」的方式来去「引入 Source Map 文件」

对于同一个源文件,根据不同的目标,可以生成不同效果的 Source Map。在开发环境和生产环境下,对于 source map 功能的期望也有所不同。

  • 在开发环境中,要求构建速度快/质量高/便于提升开发效率,而不关注生成文件的大小和访问方式
  • 在生产环境中,需要考虑是否需要提供线上Source Map/生成的文件大小/访问方式是否会对页面性能造成影响,最后才考虑质量和构建速度

由于,现在在打包领域,Webpack还是一个绕不过去的大山,所以,在了解到基础的知识点后,需要将知识配合实际项目进行分析和学习。

Source Map 处理插件

根据不同规则,实际上 Webpack 是从三种「插件」中选择其一作为 source map 的处理插件。

  1. EvalDevToolModulePlugin
    • 模块代码后添加 sourceURL=webpack:/// 模块引用路径
    • 不生成 source map 内容
    • 模块产物代码通过 eval() 封装
  2. EvalSourceMapDevToolPlugin
    • 生成 base64 格式的 source map 并「附加在模块代码之后」
    • source map 后添加 sourceURL=webpack:/// 模块引用路径
    • 「不单独生成文件」
    • 模块产物代码通过 eval() 封装
  3. SourceMapDevToolPlugin
    • 生成「单独的」 .map 文件
    • 模块产物代码不通过 eval 封装

❝插件中有eval字段的,模块产物是通过eval()封装的,只有SourceMapDevToolPlugin(AKA:SMDP)生成.map文件 ❞

Webpack 中的 Source Map 预设

Webpack 中,通过设置 devtool 来选择 Source Map 的预设类型。文档中共有 20 余种 Source Map 的预设。这些预设通常包含了 eval/cheap/module/inline/hidden/nosource/source-map等关键字的组合。

devtool 的值匹配「并非精确匹配」,某个关键字只要包含在赋值中即可获得匹配。 ❞

例如:

  • foo-eval-bar 等同于 eval;
  • wl-source-map 等同于 cheap-source-map

Webpack预设Source Map各个字段的含义 即(devtool:xxx

字段名

作用

false

不开启 Source Map 功能

eval

在编译器中使用 EvalDevToolModulePlugin 作为 Source Map 的处理插件

[xxx-...]source-map

根据 devtool 对应值中「是否有」 eval 关键字来决定使用 1. 有eval:使用EvalSourceMapDevToolPlugin作为处理插件2. 没有eval:使用SourceMapDevToolPlugin作为处理插件`

inline

作用是决定「单独生成」 source map 文件还是在「行内显示」

hidden

作用是判断是否添加 SourceMappingURL 的注释

module

作用是为加载器(Loaders)生成 source map

cheap

它决定插件 columns 参数的取值,作用是决定生成的 source map 中「是否包含列信息」在不包含列信息的情况下,调试时只能定位到指定代码所在的行而定位不到所在的列


不同预设的效果总结

分别从「质量」/「构建速度」/「包大小和生成方式」三个角度来分析

质量

生成的 source map 的质量分为 「5 个级别」,对应的「调试便捷性」依次降低。

级别

解释

5

「源码且包含列信息」

4)

「缺少列信息的源代码」

3)

「Loader 转换后的代码」

2)

「生成后的产物代码」

1)

「无法显示代码」

构建速度

「再次构建」速度都要显著快于初次构建速度 ❞

不同环境下关注的速度也不同

  • 在开发环境下
    • 一直开着 devServer,再次构建的速度对我们的效率影响远大于初次构建的速度
    • eval- 对应的 EvalSourceMapDevToolPlugin 整体要「快于」不带 eval- 的 SourceMapDevToolPlugin
    • 在质量最佳的配置下,eval-source-map 的再次构建速度要远快于其他几种
  • 在生产环境下
    • 通常不会开启再次构建,因此相比再次构建,「初次构建的速度更值得关注」

包大小和生成方式

「开发环境」下我们并不需要关注这些因素。

开发环境下 Source Map 推荐预设

通常来说,「开发环境」首选哪一种预设取决于 source map 对于我们的帮助程度。

针对不同场景,我们可以大致分为以下三种:

  • 通过 source map 来「快速定位」到源代码
    • 优先考虑使用 eval-cheap-module-source-map
    • 它的质量与初次/再次构建速度都属于次优级
    • 以牺牲定位到列的功能为代价换取更快的构建速度
  • 「质量」要求高:eval-source-map
  • 「速度」要求高: eval-cheap-source-map

利用EvalSourceMapDevToolPlugin对开发环境提速。

在开发阶段,调试的是开发的业务代码部分,而非依赖的第三方模块部分,所以在生成 source map 的时候如果可以「排除第三方模块」的部分,而只生成业务代码的 source map,无疑能进一步提升构建的速度。

代码语言:javascript复制
webpack.config.js 

  ... 

  //devtool: 'eval-source-map', 

  devtool: false, 

  plugins: [ 

    new webpack.EvalSourceMapDevToolPlugin({ 

      exclude: /node_modules/, 

      module: true, 

      columns: false 

    }) 

  ], 

  ...

devtool 设为 false,就是丢弃webpack或者CRA的默认配置,而是直接使用 EvalSourceMapDevToolPlugin,通过传入 module: truecolumn:false,达到和预设 eval-cheap-module-source-map 一样的质量。但是,我们传入 exclude 参数,「排除第三方依赖包的 source map 生成」。进一步提升了构建的速度。

捕捉Source Map文件& 反编译Source Map

❝在控制台的「网络面板」中通常看不到 source map 文件的请求,其原因是出于安全考虑 Chrome 「隐藏」了 source map 的请求 ❞

我们可以通过一些抓包工具进行文件的抓取

  • Chrome自带的 chrome://net-export/
  • whistle
  • WireShark

既然能通过抓包工具进行Source Map文件的捕获,那么在前面我们就说了,「Source Map:映射转换后的代码与源代码之间的关系」,那么我们反其道而行,我们是不是可以通过Source Map来反向推出源代码。然后,找到了源代码,我们是不是可以肆无忌惮的做一个合格的CV工程师。

这里推荐一个库shuji。可以通过x.map (x可以是js也可以是css),反编译出源代码。

0 人点赞