前端工程化详解 & 包管理工具
- 前端工程化
- 什么是前端工程化
- 前端工程化发展
- 脚手架能力
- 体验度量
- 规范流程
- 效能流程扭转
- 稳定性建设
- 针对整体稳定性建设
- 可监控:
- 前端监控系统
- 包管理工具
- npm包详解
- package.json
- name 模块名
- description 模块描述信息
- keywords:关键词
- homepage:模块首页
- repository:git仓库地址
- private 私有化
- version 包版本
- 依赖配置
- 目录 & 文件项目
- script 脚本命令
- 依赖版本管理
- 版本约束文件 package-lock.json
- npm install 原理
- pnpm
- 怎么做到节省空间,提高安装速度的?
- multirepo vs monorepo
- 工程化体系
- 脚手架能力
- 体验度量
- 规范流程
- npm package.json 解析
- 包管理工具 pnpm等
不要仅仅只是局限于开发一个需求,而是要站在宏观的角度上看整个技术体系,对技术领域有全方位的认识。
工程化体系的演进,以及演进过程中做的什么事情,现有链路是否在工作流中存在,如果没有的话,自己尝试将工程链路进行搭建。
前端工程化
什么是前端工程化
前端工程化 = 前端 + 软件工程
软件工程:系统和软件工程层面上的方法、规范,技术手段提升开发的效率,软件工程来自于实体行业类似建筑行业的方法论,将建筑行业的方法论运用到软件领域上,软件领域的相关方法论又延伸到前端上
前端工程化 = 将工程化的方法系统化应用到前端开发中
Clean Code 代码整洁指导
基于业务诉求 => 产出架构设计 又快又好又稳 <= 系统 演进 可量化的方法
前端工程化发展
- 前后端不分离,服务端渲染前端页面,jsp,php内嵌前端逻辑
- 前后端分离:B/S架构
- 模块化:AMD,CMD,CommonJS,esmodule格式化规范工程+vite 开发
模块化 vite
(1)规范化 AMD CMD
(2)组件化 => 组件库 elementUI,ant design - 自动化:解决之前人工重复干预的事情,自动化构建使得体积非常的小,可以管理,简化开发过程。 前端框架自动化,构建系统 应运而生
基于自动化做的最佳实践:
开箱即用的框架,vite create-vite,不需要进行额外的配置,就可以在当前的工程中进行开发,无需从零到一进行整个项目的搭建。但是不能只停留在使用层面,需要了解其中的原理,消化吸收变成自己的。
达到的目标:好 快 稳
vite在开发环境设计思路巧妙,与传统打包工具是不太一样的,有了vite后,基于程序构建,按需引入就是秒级的开发体验,无论当前工程多么庞大,都与当前是无关的,当前需要哪个文件,才会及时进行编译处理,不会走构建的过程,是no-bundle的思想,内部的很多设计思路都是比较巧妙的
比如,预构建,怎么去进行预构建,需要进行模块的转换,像怎么把commonJS模块转为esModule模块,
因为esModule模块才能被浏览器所引入,解析执行import的逻辑,相关模块进行预编译处理,用到esbuild,
esbuild使用 go 语言来处理,能够多线程的对当前的文件进行打包处理,
所以现在,不要仅仅局限于前端相关语言开发工程化,还要尝试使用不同语言,rest,go语言,使用这些语言开发的工具,都可以被前端所使用。
构建工具使用其他语言来处理了
=>JS:高级语言,要处理成机器所能识别的语言,需要各种各样的转化
高级语言使用babel-loader进行转化的化,需要经历 ast 的转换,经历词法分析,语法分析,代码转换为语法树,将语法树进行修改调整,修改调整转换成代码的过程,需要频繁的经历转义,是比较耗时的,性能比较差的
如果再加上其他语言进行并行处理的话,效率是比较高的,就像 swc-loader 平替 babel-loader
其他语言加上提高效率 swc-loader => babel-loader
前端市场逐渐趋于饱和,从热门到稳定的阶段
并没有大的变动
当下能做的,生成自己的开发工程的最佳实践
脚手架能力
场景:非常多的系统,集成到同一个portal当中
技术体系:微前端 => 进行系统架构升级
主子应用
可以主子应用各自的技术体系
=> 形成自己的脚手架
=> 1. 封装一个子应用模板,需要引入组件库,来保证视觉的风格统一;引入编码规范,保证代码风格统一;子应用中需要适配微前端体系
=> 2. 通过自己的脚手架拉取这样的模板,xxx create 拉取模板
业务项目生成周期的几个阶段:
-
准备阶段
技术选型
代码规范的制定
(1)分支管理规范 git workflow
(2)项目初始化规范
(3)lint规范
集成到脚手架当中做处理,无需去关注统一的,又比较繁琐的这样的内容 -
生态规范:UI库 物料规范等
-
三方规范
(1) npm 发包
(2)github,需要遵循 ci cd 等规范 -
开发阶段
(1)规范落地
(2)开发 打包流程
(3)本地 mock 服务
(4)代码质量管控
(5)单元测试,E2E测试
这些都是在开发阶段可以集成的点 -
发布流程
(1)git commitlint
(2)changlog 规范
(3)部署 验收
业务项目生成周期:
MRD(市场层面调研最终产生的结果)=> PRD(产品方案的评审) =>进入上述所说的阶段
体验度量
=> 简历中 最好体现性能优化策略
用户体验:定义指标,衡量系统好不好
定性:问卷
定量
=> 举例:度量 京东的首页 性能
- 关注指标:FCP(首屏加载时间),LCP(最大内容加载时间) ,TTFB(首字节时间)
TTFB:在ssr渲染时,客户端发起请求到服务端响应数据这样一个过程的时间,用来衡量网络消耗的
性能相关网站
LCP:对指标的衡量,性能优化的提升,怎么做才能有更好的体验
-
收集数据:performance api 上报阶段数据,不能以本机(手机,电脑)这种样本量比较少的去分析数据
从浏览器中输入url,到最终界面load,经历如下几个阶段:
DNS解析,TCP的建联,请求的时间,响应的时间,页面加载渲染的时间,最终load完成
比如,针对计算DNS时间,domainLoopupEnd - domainLoopupStart,在每个阶段打相关点,收集到对应的数据进行上报,记录当前用户这次访问所消耗的,这样的话,这些数据才是 可信赖 的。
策略优化,比如,针对webpack的优化措施,针对网络请求的优化措施,针对图片处理相关的优化措施等。
数据效果:
衡量指标 — 不能用平均数据来看
性能水位分为100位,看95%的那个人
P95 LCP 4.0s 提升到了 2.4s => 才有可信度
P99 -
体验度量设计
要解决的问题:无行为埋点,埋点数据的上报,针对度量数据完善数据指标,各个阶段进行上报,建立度量体系
规范流程
效能流程扭转
针对这样的一个平台,就能关注到团队规模层面下需求层面一个产品的健康度,团队成员代码健康度等。
稳定性建设
前端的几把斧子:性能,稳定性,研发效率,质量
每个阶段都不能忽视
关注的几个点:
- 可预防
- 可监控
- 可回滚
针对整体稳定性建设
发布前防控
- 基础建设
团队机制:编码规范 故障规范 日志规范,p0,p1,p2,p3
稳定性工具:CLI,Snippet,物料市场 - 研发态能力建设
迭代质量:依赖监测,测试用例,数据聚合
源码架构:模块化,容器化,状态管控
攻防演练:故障演练,压测演练,CR注入
全域容灾:容灾策略,容灾演练,容灾预警
发布后监控:
- 研发态能力建设
监控预警:脚本异常,接口异常,资源异常
监控大盘:数据大盘,监控排名,预警配置
行为监控:行为上报,sourcemap,行为可视化
线上工具:健康报告,定义指标看板,错误定位
可监控:
前端监控系统
要关注的几个点:
- 异常捕获 收集异常(脚本,资源,接口)相关内容,监听 onError,promise事件来捕获
- 数据上报,通过gif图像方式避免跨域的问题
- 针对数据采集,每个阶段需要打上标
- 数据清洗
- 数据持久化
- 数据可视化
包管理工具
npm包详解
package.json
{"name": "demo","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC","dependencies":{"antd":"ant-design/ant-design#4.0.0-alpha.8","axios": "^1.2.0","test-js":"file:../test", // npm link"test2-js":"http://cdn*sSom/test2-js.tar.gz","core-js":"^1.1.5"},"devDependencies": {"eslint": "^5.16.0","eslint-config-airbnb-base": "^13.2.0","eslint-plugin-import": "^2.17.3"},"peerDependencies": {"react":">=16.8.0"},"optionalDependencies":{},"bundledDependencies": ["package1","package2"]
}
name 模块名
name:模块名,需要遵循官方的建议和规范
成为整个模块的url,命令行参数和文件夹相关的名称
在空间下要发模块的话,需要使用@符,像 @abc/def 避免命名重复,类似,作用域命名@babel/cli
查看当前包名是否被占用
npm view xxx(包名)
输出 404 说明没有被占用
npm search xxx 查看包内容
通过正则的方式,返回最匹配的包,以及其他相似的包
npm search xxx --json
通过json的方式返回
description 模块描述信息
description:模块描述信息
keywords:关键词
有利于模块的检索
homepage:模块首页
repository:git仓库地址
private 私有化
"private": true
意味着当前包不能发到 npm 上,对应着version也没有效果
version 包版本
查看最新版本数据 – 用的较多
npm view xxx version
返回所有版本数据
npm view xxx versions
遵循 semver
规范
-
发布版本:
x.y.z
x— major 主版本,做了不兼容api修改
y — minor 次版本号,向下兼容,功能性新增
z — patch 修订号,向下兼容,问题修正 -
先行版本:
alpha 内部版
beta 内测版
rc 公测版
像 1.0.0-beta.0
升级修订号
npm version patch
升级次版本号
npm version minor
升级主版本号
npm version major
比较版本号大小
npm install semver
做版本校验的话,安装 semver 版本包
npm install semver
比较版本号大小,使用semver来比较
依赖配置
-
dependencies 项目运行时所依赖的模板
- react:^16.8.6
- react-dom:^16.8.6
-
devDependencies 只在开发环境使用
eslint jest,线上是不需要的 -
peerDependencies 基础库(很少用),类库,像 antd 限制 react 版本,用react-hook,需要使用16.8.0版本后的版本,那么就可以写成:
业务项目依赖 demo 安装react 16.8.0以后的版本"peerDependencies": {"react":">=16.8.0"},
-
optionalDependencies 可选择的依赖,可有可无的(很少用)
需要注意的是:optionalDependencies中的配置会覆盖 dependencies 中的配置
那么需要放在optionalDependencies的配置,就不用放在 dependencies 配置中了,只需要配置一个地方即可 -
bundledDependencies 数组,这些模块将在这个包发布的时候,会一起打包
"bundledDependencies": ["package1","package2"]
目录 & 文件项目
"main": "index.js",
- main:程序入口的主文件、
import {xxx} from ‘demo’ - bin 命令行工具入口
像 @babel/cli 中的bin目录
"bin": {"babel": "./bin/babel.js","babel-external-helpers": "./bin/babel-external-helpers.js"},
执行 babel 命令时候,就执行的是 bin 目录下的 babel.js 这个文件内容
- 发布配置
files数组中的内容,推送到服务器
"files":["/dist","/assets","/schema.json","Readme.md"],
npm publish
最终代码目录内容:
script 脚本命令
- test:测试脚本
- dev
- build
- publish: pnpm run dev && npm run build 命令组合
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","dev": "pnpm run dev","build": "pnpm run build:fast","publish": "pnpm run dev && npm run build"},
依赖版本管理
- “signale”: “1.4.0”: 固定版本号;
- “figlet”: “*”: 任意版本(>=0.0.0);
- “react”: “16.x”: 匹配主要版本(>=16.0.0 <17.0.0);
- “react”: “16.3.x”: 匹配主要版本和次要版本(>=16.3.0 <16.4.0);
-
~
~ x.y.z 安装到patch(z)的最新版本
比如,~16.3.4,安装了16.3.4,下次如果patch的最新是10的话,就安装到 16.3.10 版本,不能安装到 16.4.x 这个版本 -
^
^x.y.z:保证主版本不变的情况
下,保持次版本,修订版本
是最新
版本。
比如:"react-dom":"^16.8.6"
要求,小于17.0.0,大于等于16.8.6等等
大于等于x.y.z版本,小于最新版本
版本约束文件 package-lock.json
生成lock文件进行版本约束,锁定版本内容
不管什么时候去安装,只要不去进行人为进行修改,那么所依赖的版本号都是固定的
npm install 原理
早期npm版本(npm 2.x),是通过递归
的方式进行包的安装的
npm 3.x,将早期的嵌套结构改为扁平结构
扁平结构还可能碰到幽灵依赖的情况:
例如,显示引入了 buffer,但是 buffer 又依赖了下面两个模块:base64-js 、 ieee754。
扁平结构使得 base64-js 、 ieee754 这两个的目录结构变成这样:
这时候,文件中这样写:
import xx from ‘base64-js’;
但是没有显示的引入 base64-js依赖,但是可以去使用,这样的情况就叫做 幽灵依赖
这样是有问题的
以后 buffer 不依赖 base64-js这个了,那么my-app安装时候就没有base64-js了,那么就不能使用base64-js了
解决方案:使用 pnpm
pnpm
performance npm - 高性能的 npm
速度很快 节省磁盘空间的包管理工具
节省空间 提高安装速度
npm的问题:随着时间的堆积,整个工程的体积会非常非常大,因为内部会重复包的情况
像这样:
怎么做到节省空间,提高安装速度的?
resolved 处理了 1193 个包,
reused 复用了 1138个包,
downloaded 重新下载了 1个包,
added 0, done
这是由于:
pnpm不是安装到当前目录下的,是安装对应系统的全局目录下面,可以让包进行复用
查看全局目录下的内容
pnpm store path
全局环境 每个项目都是独立的
利用 软链接(符号链接)
硬链接
来处理
- 显示的依赖,安装到node_modules下面
- 间接依赖 安装到 .pnpm 目录下 => 解决幽灵依赖问题,无法跨越层级访问其他包的依赖,只能找当前层级下的内容 无法跨越层级访问其他包的依赖
- 符号链接(软链接) 相当于快捷方式,更快的访问路径
这里通过软链接
的方式,链接的是 .pnpm 目录下的这个包,以 eslint-config-prettier 为例:
而这里 链接的是 pnpm store下的对应文件,通过硬链接
的方式
整个过程:
这也就是pnpm为什么快,并且体积小的原因,也解决了多版本的问题
multirepo vs monorepo
multirepo 传统的方式,单独的仓库,彼此互不影响
monorepo 严格统一 多个项目放到一个仓库里面处理,适用于 底层类库,基础库,多个项目放到一个仓库里去管理,方便统一管理
像babel,vite,react,vue,都是通过monorepo,将相似的包都放在一个仓库当中,便于整体维护
每个包都是单独的npm模块,都可以单独发包
在package.json入口层面上,可以统一管理相关的打包,发布,部署