网站建设中主页源码,网线制作的心得体会,河南旅游网站建设,网站seo设置是什么简介#xff1a;JavaScript 是前端应用主要语言#xff0c;相较于其他平台编程语言#xff0c;JS资源多数情况下要通过网络进行加载#xff0c;那么代码的体积直接影响了页面加载执行时间。“无效的代码”的多寡直接影响到了我们的代码质量#xff0c;所以度量代码的执行覆…简介JavaScript 是前端应用主要语言相较于其他平台编程语言JS资源多数情况下要通过网络进行加载那么代码的体积直接影响了页面加载执行时间。“无效的代码”的多寡直接影响到了我们的代码质量所以度量代码的执行覆盖率是一项重要的优化前置工作。
You cant manage what you cant measure. 一件事如果你无法衡量它你就无法管理它。——管理大师 彼得·德鲁克
前言
JavaScript 是前端应用主要语言相较于其他平台编程语言JS资源多数情况下要通过网络进行加载那么代码的体积直接影响了页面加载执行时间。“无效的代码”的多寡直接影响到了我们的代码质量所以度量代码的执行覆盖率是一项重要的优化前置工作。
什么是代码覆盖率
1、Dead code
Dead code 也叫无用代码这个概念应是在编译时静态分析出的对执行无影响的代码举个例子
// a.js
const a 1;
const b 2; /* dead code */
export default a;
// index.js
import a from ./a.js;
export default function() {console.log(a);
}
通常我们用 Tree Shaking 在编译时移除这些 dead code以减小代码体积。
2、冗余代码
而代码覆盖率里所提到的冗余代码 和 Dead Code 又略有不同简单来说Dead code适用于编译时而 Code coverage 适用于运行时。 Dead code 是任何情况下都不会执行的代码所以可以再编译阶段将其剔除。 冗余代码 是某些特定的业务逻辑之下并不会执行到这些代码逻辑比如在首屏加载时某个前端组件完全不会加载那么对于“首屏”这个业务逻辑用例来讲该前端代码就是冗余的
3、代码覆盖率
代码覆盖率Code coverage是软件测试中的一种度量指标。即描述测试过程中运行时被执行的源代码占全部源代码的比例。
怎么度量代码覆盖率
1、Chrome 浏览器 Dev Tools
chrome 浏览器的 DevTools 给我们提供了度量页面代码JS、CSS覆盖率的工具 Coverage。
使用方式Dev tools —— More tools —— Coverage可度量代码类型JS CSS统计可视化形式
使用率是以byte字节来计算的当我们选择一段脚本资源即可在 Source 栏可以看到加载页面时当前资源 run过得代码蓝色和没有run过得代码红色
缺点显然目前大部分网页上的JS脚本基本都是经过混淆压缩打包过后的产物对于开发者而言这种覆盖率可读性及参考价值不大。
TIPS当然如果在拥有 source map 的情况下也是可以用浏览器查看源代码的覆盖率的
在 source tab 中找到当前页面的 js 资源文件当然已经被混淆的面目全非输入 sourcemap URL以 def 发布平台为例在构建结果中可找到在 webpack:// 目录下即可查看对应源码的大致覆盖率不过没有什么消费价值那么问题来了有没有一种方法可以令开发者了解 源代码 的代码覆盖率的值呢
2、IstanbulNYC
这个软件以土耳其最大城市伊斯坦布尔命名因为土耳其地毯世界闻名而地毯则是用来覆盖的。
Istanbul 或者 NYC(New York City基于 istanbul 实现) 是度量 JavaScript 程序的代码覆盖率工具目前绝大多数的node代码测试框架使用该工具来获得测试报告其有四个测量维度
line coverage行覆盖率-每一行是否都执行了 【一般我们关注这个信息】
function coverage函数覆盖率-每个函数是否都调用了
branch coverage分支覆盖率-是否每个 if 代码块都执行了
statement coverage语句覆盖率-是否每个语句都执行了
可以度量的代码类型JS TS统计可视化的形式
HTMLterminal
缺点目前使用 istanbul 度量网页前端JS代码覆盖率没有非侵入的方案采用的是在编译构建时修改构建结果的方式埋入统计代码再在运行时进行统计展示。
我们可以使用 babel-plugin-istanbul 插件在对源代码在 AST 级别进行包装重写这种编译方式也叫 代码插桩 / 插桩构建instrument
3、插桩构建
我们如果要度量这一段代码哪些代码执行了 哪些代码没有执行我们会怎么做呢
// add.js
function add(a, b) {return a b
}
module.exports { add }我们可以很容易的想到加一些“装饰性”的代码在我们的源码里面那么当代码一行一行的执行到某处时那么我们就在全局环境变量中记录一下
// 全局对象记录了 __coverage__ 记录了上面代码中的语句和函数的执行次数
const c (window.__coverage__ {// f 表示每一个 function 被执行的次数// 当前代码只有一个 function 因此f 数组只有一个 且记录值为 0f: [0],// s 表示每一个 statement 被执行的次数// 3 个 statement 全部都以 0 赋值s: [0, 0, 0],})// 函数定义是一个语句statement那么我们 1
c.s[0]function add(a, b) {// 如果 add 函数function被调用f 1且改调用语句 s 1c.f[0]c.s[1]return a b}
// add 被调出语句 s 1
c.s[2]
module.exports { add }
且 istabul 确实也是这么做的babel-plugin-istanbul 在构建过程中分析 AST 并将相应统计单元语句、函数、分支等做装饰代码的添加最终在代码运行之后输出一份 json 格式的数据
{/Users/bairuobing/test/istanbul.js:{path:/Users/bairuobing/test/istanbul.js,s:{1:1,2:0,3:1},b:{},f:{1:0},fnMap:{ // function 的开始结束位置信息1:{name:add,line:1,loc:{start:{line:1,column:0},end:{line:1,column:19}}}},statementMap:{ // statement 的开始结束位置信息1:{start:{line:1,column:0},end:{line:3,column:1}},2:{start:{line:2,column:4},end:{line:2,column:16}},3:{start:{line:4,column:0},end:{line:4,column:24}}},branchMap:{ // branch 的开始结束位置信息}}
}
当我们在运行代码过后得到了上面的 json 便可以消费它了。
# terminal 形式输出
nyc report --reportertext
# HTML 形式输出
nyc report --reporterlcov --exclude-after-remapfalseterminalHTML代码覆盖率在 iHome Rax开发套件 Tbox 中的应用
tipstbox 每平每屋 消费者端 本地开发套件
既然我们知道了源代码的代码覆盖率我们可以用它为性能优化做些什么贡献呢
当工程主 bundle 较大那么采用拆包较大的/无用的前端组件来瘦身首屏主 JS 包不失为一种可行的选择此时就可以根据代码覆盖率来决定优化哪些代码。
1、代码分割
React.lazy 已经为我们提供了一种不错的思路就是利用动态加载模块规范 import()webpack对import()解析为代码分割的能力来实现前端组件代码懒加载/动态加载。
以此为灵感那么为何不将某些组件通过动态引入的方式加载来以此换取首页 bundle 的瘦身呢
// 动态引入组件
// ThisIsBigMod
import { createElement, useState, useEffect } from rax;
export default (props) {const [AsyncMod, setAsyncMod] useState(null);useEffect(() {const load async () {const Module await import(./ThisIsBigMod); // 关键try {setAsyncMod(Module);} catch (e) {console.log(e);}};load();}, []);if (!AsyncMod || !AsyncMod.default) {return null;}return AsyncMod.default {...props} /;
};
2、下一步
我们能通过代码覆盖率统计出哪些组件的代码首屏使用率为0或者门槛值30%以下并在项目工程中自动生成一个持久化的文件配置(app.json中)之后依据配置将这些低使用率的组件代码在生产构建时将产物代码改写为动态引入。
于是有了以下方案 3、如何使用
该功能需要项目下安装以下 build 插件
ali/build-plugin-coverageali/build-plugin-async-components
tnpm install --save-dev ali/build-plugin-coverage ali/build-plugin-async-components
build.json
// build.json
plugins: [......ali/build-plugin-coverage,[ali/build-plugin-async-components,{active: true}]
]
运行 Tbox 插桩构建
依赖 ali/build-plugin-coverage通过插桩将源码中插入统计代码本地构建之后页面全局会注入__coverage__变量可在页面控制台输出该变量检查插桩是否成功分析自动化生成配置
等待完成首屏渲染或者完成自定义的一系列行为用例此刻插桩代码已经完成了代码使用率的统计打开 Tlog 小工具 点击代码优化-生成源代码优化配置此刻 Tbox 本地服务已经接收到了发来的__coverage__并完成后续的代码覆盖率分析通过分析使用率低于门槛值的组件文件将这些组件的项目相对路径写入 app.json 的 modsPath 字段下此刻 ali/build-plugin-async-components 会根据 modsPath 配置自动将组件构建为动态引入的方式如果您想通过自己的配置来完成组件异步化请直接手动修改 app.json 里的 modsPath 字段只需依赖 ali/build-plugin-async-components 插件再次构件即可此时我们条件加载被异步化的组件会发现BigMod 组件已经被动态的拆包引入了页面主 js 包也得到了瘦身搞定
写在最后
istanbul 在 node 环境下跑测试用例代码能度量覆盖率是由于其对运行时模块加载器的源代码拦截但是比较遗憾的是本文介绍的代码插桩分析覆盖率这会引入一些多余的桩代码或许采用 puppeteer 无头浏览器提供的Coverage api sourceMap 逆编译的思路来进行度量是一种更加完美的方式期待与诸君一起探索继续努力
原文链接
本文为阿里云原创内容未经允许不得转载。