七爪源码:如何执行依赖分析并推断修改代码的影响
执行依赖分析并计算修改代码对在线页面的影响。
今天我们将讨论如何进行依赖分析,并统计修改后的代码对在线页面的影响。
您可能有多人合作开发项目的经验。在这个过程中,难免会修改一些常用的代码。如果项目工作的多人之间信息不完全同步,就会出现协调不一致的问题,最终可能导致在线问题。
我将用一个特定的场景来说明这个问题。学生 A 正在开发一个需求。他发现,在这个需求中,有一个组件可以分离成一个公共组件,既可以自己使用,也可以提供给其他同学,提高开发效率。所以学生 A 封装了公共组件 C。
B同学在开发需求的时候,发现C组件可以在需求中复用,所以直接使用C组件。至此,A同学和B同学的需求已经分别上线。经过QA同学的测试,没有问题。突然有一天,A同学发现要调整组件,增加了一个参数。他负责的页面也进行了改编,自测没有问题,就交给QA测试A同学的页面,上线没有问题。刚上线,B同学的页面就出现了问题。原因是A同学修改的公共组件C没有和B同学同步,B同学的页面没有适配,导致上线问题。
我这里用一张图来说明这个问题:
至此,相信大家应该明白B同学页面出事的原因了,就是A同学修改了公开代码,而因为信息不同步,B同学不知道公开修改 组件,并没有进行适配,最终导致了上线事故。 那么如何使用技术手段来评估修改后的代码对页面的影响呢? 接下来,我将通过一个Demo来讲解。 现在有一个通用组件Common,分别被Page01和Page02引用。 代码如下:
// Common.js
export default function Common() {
return <div> Common Component </div>
}
// Page01.js
import Common from './Common';
export default function Page01() {
return <div>
<Common />
Page01
</div>
}
// Page02.js
import Common from './Common';
export default function Page02() {
return <div>
<Common />
Page02
</div>
}
通过阅读代码应该明白,如果修改了Common组件,会同时影响Page01和Page02页面。如何通过技术手段分析Common组件的影响?在解决这个问题之前,让我们学习如何分析组件的依赖关系。
如何分析依赖关系?
直观上,通过AST解析代码,找到模块的引用路径,然后在项目中分析路径关系,找到对应的文件,就可以找到import语句。接下来,递归查找所有模块依赖路径。如果我们从0开始开发这样的工具,要考虑的因素会很多,要做好也不是一件容易的事。所以,我们可以站在巨人的肩膀上,用 WebPack 做到这一点。
在 WebPack 的插件机制中,我们有很多编译好的生命周期可以使用。 ContextModuleFactory Hooks主要做模块解析的工作,其中afterResolve是模块解析的最后阶段,在这个阶段你可以得到一些依赖。核心代码如下:
module.exports = class DemoPlugin {
constructor(options) {
}
apply(compiler) {
compiler.hooks.normalModuleFactory.tap('DepAnalysisPlugin', (nmf) => {
nmf.hooks.afterResolve.tap('DepAnalysisPlugin', (result) => {
const resourceResolveData = result.createData.resourceResolveData || {};
const curFile = resourceResolveData.path;
const parentFile = resourceResolveData.context.issuer;
// ...
}
)
});
}
}
每次解析一个依赖,在afterResolve中,我们可以存储一次依赖,等文件中的所有依赖都解析完后,我们就可以把所有的依赖放到一个map上。 以之前的Demo为例,最终的地图是这样的:
{
path: 'src/App.js',
deps: [
{
path: 'src/components/Page01.js',
deps: [
{
path: 'src/components/Common.js',
deps: []
}
]
},
{
path: 'src/components/Page02.js',
deps: [
{
path: 'src/components/Common.js',
deps: []
}
]
}
]
}
至此,依赖关系就搞清楚了,接下来我们就需要分析哪些页面受到了Common组件的影响。
如何建立页面和文件之间的链接?
建立页面和文件的链接就是建立路由和文件的链接,所以我们只要找到路由和文件的对应关系就可以了。 但是在实际的业务开发中,大家的写法和路由方式千差万别。 如果从代码分析的角度来解决这个问题,可能并不容易解决。 有没有更合理的解决方案?
答案是肯定的。 根据我多年的经验,路由和文件的关系需要一个规范来约束,对于公司级的项目更准确,更适用。 这里我们临时制定一个配置文件,格式如下代码所示:
const path = require('path');
exports.default = [
{
page: 'Page01',
entry: path.join(__dirname, './src/components/Page01.js')
},
{
page: 'Page02',
entry: path.join(__dirname, './src/components/Page02.js')
}
]
至此,我们已经确定了页面与入口文件的对应关系。
如何确定哪些页面受到修改文件的影响?
前面我们已经拿到了整个依赖关系图,结合定义的页面配置,我们可以将页面和依赖关系结合起来。 具体的组合算法可以通过遍历依赖图来实现。 您可以通过 DFS(深度优先搜索)或 BFS(广度优先搜索)算法来实现它。
如果现在修改Common组件,那么我们可以通过最终分析结果是page 01和page 02得到所有的影响范围,然后把影响范围交给QA同学进行函数回归,就可以避免我提到的 一开始。 修改公共依赖,QA不清楚影响范围,导致漏测,进而导致线上事故。
结论
通过编写自定义的 WebPack 插件,我们可以在 normalModuleFactory 的 afterResolve 生命周期中建立组件依赖,然后根据路由配置规范推导出常用组件的受影响页面范围,最后交给 QA 完成全面的功能回归。 这可以在很大程度上避免由于 QA 不知道新功能的影响而错过测试的风险。
感谢您的阅读。
关注七爪网,获取更多APP/小程序/网站源码资源!