最近在组内推广进行项目(Vue
+ Express
)历史逻辑下线专项,实践完成后产出了一份 SOP,特此记录
SOP(Standard Operating Procedure): 一种详细的、书面的指导方案,用于指导如何完成特定任务或活动。
删除无用路由与接口#
前置逻辑:
- 开启埋点 SDK PV 插件自动上报或 Vue 插件路由自动上报,注意项目初始访问是否正常上报
- 开启埋点 SDK 接口请求上报功能(在通用请求响应拦截器里设置)
无访问路由#
通过 Druid SQL
从埋点上报数据中查询近三个月页面访问记录,对比项目中路由配置产出未访问历史路由,过滤掉明确需要使用的页面并逐个在项目中搜索确认是否有项目内跳转情况,之后与产品及后端确认剩余页面后才可删除。
附未访问路由匹配产出逻辑:
const fs = require('fs')
const path = require('path')
const list = [] as string[]
// 项目已访问路由数据
const { data } = require('./data.json')
// 遍历目录生成路由列表(针对 nuxt 等约定式路由类项目)
const listFile = (dir: string) => {
const arr = fs.readdirSync(dir)
arr.forEach((item: string) => {
const fullpath = path.join(dir, item)
const stats = fs.statSync(fullpath)
if (stats.isDirectory()) {
listFile(fullpath)
} else {
list.push(fullpath)
}
})
return list
}
// 项目已访问过路由列表,移除固定路由前缀
const projectVisitedPathList = data.map(item => item.uri.slice('/project_prefix'.length))
// 项目全量路由列表
const projectPath = '/your-project-path/src/pages'
const projectPathList = listFile(projectPath)
const startLen = projectPath.length
const indexEndString = '/index.vue'
const allPathList = projectPathList.map(item => {
if (item.endsWith('.vue')) {
if (item.endsWith(indexEndString)) {
// 移除前缀目录与 /index.vue 后缀
return item.slice(startLen, -indexEndString.length)
}
// 移除前缀目录与 .vue 后缀
return item.slice(startLen, -4)
}
}).filter(item => !!item)
// 将全量目录路由列表与已访问路由列表进行过滤,产出未访问路由
const unVisitedList = allPathList.filter(item => !projectVisitedPathList.includes(item))
// 未访问路由补充固定域名前缀并写入新文件内
fs.writeFileSync('unVisitedList.json', JSON.stringify(unVisitedList.map(item => 'https://xxx.com/project_prefix' + item)))
附查询 SQL
:
SELECT
uri,
SUM("count") as "total"
FROM
xxx
WHERE
"__time" >= @start
and "__time" <= @end
and type = '4' // 4 表示 pv 类型
and appId = @appId
GROUP BY
uri
ORDER BY
"total" DESC
无访问接口#
同样通过 Druid SQL
查询近三个月前端接口访问记录,对比项目中接口路由配置产出未访问历史接口,并结合后端服务接口日志确认后删除,查询 SQL
基本同上。
删除无用 Store
#
结合路由删除结果在项目内搜索排查对应 Store
是否还在使用:state
、getters
、mutations
、actions
,均未使用时可删除。
删除无用线上配置#
结合路由与 Store
删除结果排查相关线上配置是否还在使用,未使用时可删除。
删除无使用模块及变量#
未使用文件 / 变量检测工具对比:
工具 | 原理 | 主要作用 | 备注 |
---|---|---|---|
webpack-deadcode-plugin | 使用 webpack 生成的 stats 文件,通过 webpack 编译源文件时,生成的包含有关于模块的统计数据的 JSON 文件 这些统计数据不仅可以帮助开发者来分析应用的依赖图表,还可以优化编译的速度 | 基于 webpack 构建简单分析,扫描结果包括未使用文件与未使用变量 | 结果较准确,覆盖面广,推荐使用 产出分析文件后作为参考,再手动确认删除(当前文件内有使用但导出未使用时会误报) |
useless-files-webpack-plugin | 同上 | 基于 webpack 构建简单分析,主要是文件是否未使用,可以配置自动删除 | 检测逻辑相似,功能不如前者 |
find-unused-exports | 基于 babel 和 postcss 查找项目中的无用模块 | 单纯 js 实现的基于文件依赖进行分析的包 | 与第一项可结合分析 |
ts-prune | TypeScript 服务提供了一个实用的 API:findAllReferences ts-morph 这个库封装了包括 findAllReferences 在内的一些底层 API,提供更加简洁易用的调用方式 ts-prune 就是基于 ts-morph 封装而成 | 可以分析文件中导出的变量是否有被使用 | 功能不如前者,测试后在全量导出、vue 文件引入等情况下干扰度大 不建议使用(后续代码编写也应尽量避免全量导出写法) |
- 删除无用页面后,使用 webpack-deadcode-plugin 工具检测扫描 + 手动确认后删除(注:新增一条
npm script
命令用于后续持续性分析优化) - 开启未使用变量检测提示,配合 VS Code 插件提示辅助删除
- 开启
eslint
规则检测js/ts/vue
文件,针对未使用变量提交时强报错提示
注:官方针对{ "rules": { "@typescript-eslint/no-unused-vars": [ "error", { "argsIgnorePattern": "^_" } ], } }
no-unused-vars
规则不支持自动修复,需要手动修改- 开启
tsc
编译配置校验未使用变量与未使用函数参数
{ "noUnusedLocals": true, "noUnusedParameters": true }
- 手动修改建议优先参考 VS Code 提示,注意以下情况:
- 未使用函数参数不在末尾时不能直接删除,使用 _ 前缀约定
- 存在扩展参数解构时,不能直接删除未使用变量导致副作用产生,需要针对特定行禁用语法(同 VS Code 提示)
- 调用函数返回值声明未使用时 VS Code 提示会删除整段声明 + 调用,这里只删除声明即可
- service 方法中参数未使用时如 controller 未传入可直接删除,否则使用 _ 前缀约定
- 开启
监控报警配置#
通用类错误使用埋点 SDK 错误插件已上报,上线前需要调整观察至一个稳定幅度,避免后续干扰
空路由 / 接口访问需要新增采集逻辑:
- 空路由:404 访问页面展示,上报错误埋点,同时可与产品沟通导流逻辑。
- 空接口:服务端新增
Express
Router 兜底匹配上报错误日志。- 上报逻辑
controller.on('mounted', () => { resolve(void 0) // 确保所有路由挂载完成后再处理 setImmediate(() => { app.all('/api/*', function (req: any, res: any) { routeLog.error('route_empty_error', { pathname: req.path, http_method: req.method, referer: req.headers.referer }) res.status(404).json() }) }) })
- 上报逻辑
报警配置
模块 | 功能 | 报警策略(结合项目实际情况) |
---|---|---|
Vue Error | Vue 全局错误捕获 | xx mins 突增 xxx |
ReferenceError | 引用错误 | xx mins 突增 xxx |
TypeError | 类型错误 | xx mins 突增 xxx |
空路由访问 | 访问空路由埋点上报 | xx mins 出现 xx 次 |
空接口访问 | 访问空接口日志采集上报 | xx mins 出现 xx 次 |
验证上线#
在预发验证相应监控报警配置无误后,先上线一台机器观察 3 - 5 天正常后完成上线。