我应该升级我的依赖项吗?
通常赞同者的答案是"提高系统的安全性"、"不升级会一直落后",这些结论并不是很让人信服和满意。大部分依赖项,其升级所带来的漏洞修复通常非常少,大部分都是更多的新增特性。对于项目主管来说,升级依赖意味着花费更多的工时,可能还会带来更多的兼容性风险,并且在一定的时间周期内几乎不会产生任何回报。
所以通常做法不会对依赖项进行任何升级,并固定依赖项版本。在一定的时间周期内这种做法是有益的,但如果一个软件持续生命周期较长,那么它也会带来较多的风险。
技术债务
维基百科 技术负债 (英语:Technical debt),又译技术债 ,也称为设计负债 (design debt)、代码负债 (code debt),是程序设计及软件工程中的一个比喻。指开发人员为了加速软件开发,在应该采用最优方案时进行了妥协,改用了短期内能加速软件开发的方案,从而在未来给自己带来的额外开发负担。这种技术上的选择,就像一笔债务一样,虽然眼前看起来可以得到好处,但必须在未来偿还。软件工程师必须付出额外的时间和精力持续修复之前的妥协所造成的问题及副作用,或是进行重构,把架构改善为最优实现方式。
过于老旧的依赖也是技术债务的一种,安全负担、维护心智负担等也会一直伴随着整个研发过程。随着时间的推移,你的项目将慢慢变得难以维护、从而降低团队的生产力和满意度。试问,谁会想去维护一个五年前、十年前项目创建当时技术栈的项目呢?所以升级依赖项也是软件研发中重要的一环。
以下为主要原因:
- 🚀**新功能:**大多数依赖项都有清晰的新功能路线图。例如,在 React 16.0 中,他们添加了对Portals概念的本机支持。在以前的 React 版本中,这个概念仅由像React-portal这样的第三方库支持。
- 🏎️性能改进: 除了新功能之外,性能改进也频繁进行。
- 🐛 修复错误: 虽然在您依赖的库中发现错误是很正常的,但补丁版本有助于修复这些问题。
- 👮安全补丁: 安全补丁是让您的库保持最新状态的最重要原因之一。您熟悉Equifax 安全漏洞吗? Equifax 被黑客攻击的原因是该公司依赖的外部库存在严重的安全漏洞。外部库本身收到了修复问题的补丁,但 Equifax 从未更新其系统中使用的内部代码。尽管 lib团队 知道该漏洞甚至发布了安全更新,但还是错过了这一点。
- 🌟减少心智负担: 开发过程中无需考虑当前版本已知缺陷问题等,并能使用更新的API,更新的API通常意味着减少代码量等。
示例:vue3.4提供的一些减少代码量的语法糖
html
<!-- vue < 3.4 实现外部 modelValue 双向绑定 -->
<template>
<input v-model="value" />
</template>
<script lang="ts" setup>
import { computed } from 'vue'
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(v) {
emit('update:modelValue', v)
}
})
</script>
<!-- 利用 vue3.4 defineModel api 实现 -->
<template>
<input v-model="value"/>
</template>
<script lang="ts" setup>
const value = defineModel<string>('modelValue')
</script>
如何更新
项目的依赖项,成百上千。我们不可能同时关注到这么多依赖包版本变动。一个一个依赖项来查找,多累人啊。
我们可以借助于一些工具,如 npm-check
、npm-check-updates
等。
bash
npx npm-check -u 130 ↵
Packages: +340
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 340, reused 308, downloaded 32, added 340, done
? Choose which packages to update. (Press <space> to select, <a> to toggle all, <i> to invert selection)
Minor Update New backwards-compatible features.
❯◯ @element-plus/icons-vue missing 2.0.5 ❯ 2.3.1 https://element-plus.org/
◯ echarts missing 5.3.3 ❯ 5.5.0 https://echarts.apache.org
◯ echarts-wordcloud missing 2.0.0 ❯ 2.1.0 https://github.com/ecomfe/echarts-wordcloud#readme
◯ element-plus missing 2.2.36 ❯ 2.6.0 https://element-plus.org/
◯ pinia missing 2.0.36 ❯ 2.1.7 https://github.com/vuejs/pinia#readme
◯ vue missing 3.2.47 ❯ 3.4.21 https://github.com/vuejs/core/tree/main/packages/vue#readme
◯ vue-i18n missing 9.1.10 ❯ 9.10.1 https://github.com/intlify/vue-i18n-next/tree/master/packages/vue-i18n#readme
◯ vue-router missing 4.0.16 ❯ 4.3.0 https://github.com/vuejs/router#readme
◯ @iconify/json devDep missing 2.1.159 ❯ 2.2.188 https://iconify.design/icon-sets/
◯ @types/qrcode devDep missing 1.4.3 ❯ 1.5.5 https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/qrcode
◯ commitizen devDep missing 4.2.6 ❯ 4.3.0 https://github.com/commitizen/cz-cli
◯ eslint devDep missing 8.18.0 ❯ 8.57.0 https://eslint.org
◯ eslint-plugin-import devDep missing 2.26.0 ❯ 2.29.1 https://github.com/import-js/eslint-plugin-import
◯ eslint-plugin-vue devDep missing 9.1.1 ❯ 9.22.0 https://eslint.vuejs.org
◯ less devDep missing 4.1.3 ❯ 4.2.0 http://lesscss.org
◯ postcss-html devDep missing 1.4.1 ❯ 1.6.0 https://github.com/ota-meshi/postcss-html#readme
◯ stylelint-config-html devDep missing 1.0.0 ❯ 1.1.0 https://github.com/ota-meshi/stylelint-config-html#readme
◯ stylelint-config-recommended-vue devDep missing 1.4.0 ❯ 1.5.0 https://github.com/ota-meshi/stylelint-config-recommended-vue#readme
◯ vite-plugin-eslint devDep missing 1.6.1 ❯ 1.8.1 https://github.com/gxmari007/vite-plugin-eslint
◯ vite-plugin-windicss devDep missing 1.8.10 ❯ 1.9.3 https://github.com/antfu/vite-plugin-windicss
Major Update Potentially breaking API changes. Use caution.
◯ @iconify/iconify missing 2.2.1 ❯ 3.1.1 https://iconify.design/
◯ @vueuse/core missing 8.7.5 ❯ 10.9.0 https://github.com/vueuse/vueuse#readme
◯ @zxcvbn-ts/core missing 2.0.5 ❯ 3.0.4 https://github.com/zxcvbn-ts/zxcvbn
◯ axios missing 0.27.2 ❯ 1.6.7 https://axios-http.com
◯ intro.js missing 5.1.0 ❯ 7.2.0 https://introjs.com
◯ vue-types missing 4.1.1 ❯ 5.1.1 https://dwightjack.github.io/vue-types/
◯ @commitlint/cli devDep missing 17.0.3 ❯ 19.0.3 https://commitlint.js.org/
◯ @commitlint/config-conventional devDep missing 17.0.3 ❯ 19.0.3 https://commitlint.js.org/
◯ @intlify/vite-plugin-vue-i18n devDep missing 3.4.0 ❯ 7.0.0 https://github.com/intlify/bundle-tools/blob/main/packages/vite-plugin-vue-i18n/README.md
◯ @types/intro.js devDep missing 3.0.2 ❯ 5.1.5 https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/intro.js
◯ @types/node devDep missing 18.0.6 ❯ 20.11.24 https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node
◯ @typescript-eslint/eslint-plugin devDep missing 5.30.7 ❯ 7.1.0 https://github.com/typescript-eslint/typescript-eslint#readme
◯ @typescript-eslint/parser devDep missing 5.30.7 ❯ 7.1.0 https://github.com/typescript-eslint/typescript-eslint#readme
◯ @vitejs/plugin-vue devDep missing 2.3.4 ❯ 5.0.4 https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue#readme
◯ @vitejs/plugin-vue-jsx devDep missing 1.3.10 ❯ 3.1.0 https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx#readme
◯ conventional-changelog-cli devDep missing 2.2.2 ❯ 4.1.0 https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-cli#readme
◯ eslint-config-prettier devDep missing 8.5.0 ❯ 9.1.0 https://github.com/prettier/eslint-config-prettier#readme
◯ eslint-define-config devDep missing 1.5.1 ❯ 2.1.0 https://github.com/eslint-types/eslint-define-config#readme
◯ eslint-plugin-prettier devDep missing 4.1.0 ❯ 5.1.3 https://github.com/prettier/eslint-plugin-prettier#readme
◯ husky devDep missing 8.0.3 ❯ 9.0.11 https://github.com/typicode/husky#readme
◯ lint-staged devDep missing 13.0.4 ❯ 15.2.2 https://github.com/okonet/lint-staged#readme
◯ plop devDep 3.1.2 ❯ 4.0.1 https://plopjs.com
前端依赖包基本都遵循语义化版本,可以通过此类工具进行选择性更新。
从上面执行结果来看,几乎所有的依赖包都发生了大版本更新。而大版本更新一般存在不兼容的情况,每次升级一两个这种依赖包可能还好,但几乎全盘更新,也是非常艰巨的任务,所以我们得定期来进行更新。
renovate
依赖项升级总是痛苦的,但保持有一定的升级频率。能大量减少升级带来的痛苦。
renovate 是一个开源且目前比较主流的依赖管理工具,支持私有化部署。能自动进行周期性的依赖扫描,并自动发起合并请求。通过此类依赖管理工具,并结合自动化测试,实际上更新依赖的成本开销已经非常之小。
结语
想要维护好一个大型项目需要有各种严格的程序,使项目依赖项保持在较新的程度应该是首要任务之一。如果不定期进行升级,代码库将慢慢变得难以维护且不安全。通过自动化工具和自动化测试、CICD来进行依赖升级,是目前比较好的升级方案。