1. 页面加载性能
a. 检查资源加载时间
插件:webpack-bundle-analyzer 它可以帮助你分析和可视化 Webpack 构建输出的包(bundle)。通过这个工具,你可以清楚地看到每个模块在最终打包文件中的大小、依赖关系以及它们是如何被打包在一起的。这对于优化打包体积、减少加载时间以及识别不必要的依赖。
找出占用内存较大的模块,有针对性地进行优化。
// webpack.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
// 其他 Webpack 配置...
plugins: [
new BundleAnalyzerPlugin({
// 可选配置项
analyzerMode: 'server', // 默认值,启动一个 HTTP 服务器来查看报告
// analyzerMode: 'static', // 生成静态 HTML 文件
// analyzerMode: 'disabled', // 禁用分析器
openAnalyzer: true, // 自动打开浏览器查看报告
generateStatsFile: false, // 是否生成 stats.json 文件
statsOptions: null, // 传递给 stats.toJson() 的选项
reportFilename: 'report.html', // 报告文件名
defaultSizes: 'parsed', // 显示解析后的大小 ('parsed') 或 gzip 压缩后的大小 ('gzip')
logLevel: 'info', // 日志级别
})
]
};
工具:你可以通过Network面板查看哪些资源加载较慢,并考虑优化这些资源。
实践:
图片优化:使用像ImageOptim、TinyPNG这样的工具压缩图片,或者在构建过程中集成类似imagemin-webpack-plugin的插件。
const ImageminWebpackPlugin = require('imagemin-webpack-plugin');
const imageminMozjpeg = require('imagemin-mozjpeg');
const imageminPngquant = require('imagemin-pngquant');
module.exports = {
// ... 其他配置项 ...
plugins: [
new ImageminWebpackPlugin({
test: /.(jpe?g|png|gif|svg)$/i, // 指定要处理的图片类型
plugins: [
imageminMozjpeg({
quality: 75, // 设置 JPEG 压缩质量
progressive: true,
}),
imageminPngquant({
quality: [0.6, 0.8], // 设置 PNG 压缩质量范围
}),
],
}),
],
};
减少HTTP请求:将多个CSS/JS文件合并为一个,减少请求数量。可以通过Webpack配置来实现这一点,这里需要注意的就是合并的文件大小不要过大,不利于浏览器缓存
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
// 将所有 node_modules 中的依赖打包到一个 vendor chunk 中
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
// 将应用自身的代码打包到一个 app chunk 中
app: {
name: 'app',
chunks: 'all',
priority: -10, // 确保这个规则不会优先于 vendor 规则
}
}
},
runtimeChunk: 'single' // 创建一个单独的 runtime 文件
}
}
};
Vue CLI 默认会将所有的 CSS 提取到一个或多个 .css 文件中。如果你希望确保所有的样式都被提取到一个单一的 CSS 文件中,可以使用 mini-css-extract-plugin
module.exports = {
css: {
extract: true, // 确保 CSS 被提取到单独的文件中
sourceMap: false, // 生产环境中关闭 source map
}
};
如果你想确保所有的 CSS 被合并到一个文件中,而不仅仅是从 JavaScript 中提取
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
chainWebpack: config => {
// 修改 CSS 相关的规则
config.module
.rule('css')
.use('extract-css')
.loader(MiniCssExtractPlugin.loader)
.end();
// 移除默认的 CSS 分割规则
config.optimization.splitChunks.cacheGroups.vendors.delete('styles');
}
};
b. 启用缓存
对于传统的Vue应用,可以使用service-worker来添加PWA支持,从而启用离线缓存。
请查看下方 资源管理 - Service Worker
HTTP缓存头:在服务器上设置适当的Cache-Control和Expires头部信息。如果你使用的是Node.js后端,可以通过Express中间件如express-static来设置这些头部。
实践:
版本控制:在打包时给静态资源加上哈希值(例如app.[contenthash].js),这样当文件内容发生变化时,浏览器会自动更新缓存。
2. 首屏渲染优化
a. 代码分割
动态导入:Vue支持ES6的动态导入语法,允许按需加载组件。例如:
const Component = () => import('./MyComponent.vue');
路由懒加载:对于单页应用(SPA),可以利用Vue Router的懒加载功能,只在用户导航到特定路由时加载对应的组件。
const router = new VueRouter({
routes: [
{ path: '/about', component: () => import('./views/About.vue') }
]
});
实践:
异步组件:使用<template>标签内的<component :is="..." />语法来动态加载组件,确保非关键路径上的组件不会影响首屏渲染。
b. 预加载关键资源
预加载指令:在index.html中添加标签,提前加载字体、样式表、脚本等重要资源。
<link rel="preload" href="/fonts/my-font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/app.css" as="style">
<link rel="preload" href="/js/app.js" as="script">
实践:
优先级加载:确保关键资源尽早加载,而非关键资源延迟加载。可以使用loading="lazy"属性对图片进行懒加载。
3. 交互性能
a. 录制性能数据
Performance API:在Vue组件生命周期钩子(如mounted)中使用performance.mark()和performance.measure()记录关键时刻的时间戳。
User Timing API:使用performance.getEntriesByName()获取自定义的性能指标。
实践:
监控关键操作:例如,在提交表单或点击按钮后开始计时,直到操作完成。
<script>
export default {
name: 'MyComponent',
mounted() {
// 记录挂载开始的时间戳
performance.mark('my-component-mounted-start');
// 模拟一些异步操作或复杂的初始化逻辑
this.$nextTick(() => {
// 确保 DOM 已经更新
performance.mark('my-component-mounted-end');
// 测量挂载过程所需的时间
performance.measure(
'my-component-mounted-duration', // 测量名称
'my-component-mounted-start', // 开始标记
'my-component-mounted-end' // 结束标记
);
// 打印测量结果到控制台
const entries = performance.getEntriesByName('my-component-mounted-duration');
if (entries.length > 0) {
console.log(`MyComponent mounted in ${entries[0].duration.toFixed(2)} ms`);
}
// 清除不再需要的标记和测量
performance.clearMarks('my-component-mounted-start');
performance.clearMarks('my-component-mounted-end');
performance.clearMeasures('my-component-mounted-duration');
});
}
};
</script>
b. 异步处理复杂逻辑
Web Workers:创建一个Worker文件(如worker.js), 将复杂的计算任务移到Worker中执行。
async/await:对于异步API调用,使用async/await简化代码结构,提高可读性和维护性。
实践:
后台任务:比如图像处理等耗时任务可以放在Worker中执行,以保持主线程的流畅。
// worker.js
// 监听来自主线程的消息
self.onmessage = function (event) {
const data = event.data;
if (data.action === 'complexLogic') {
const result = complexLogic(data.n);
// 将结果发送回主线程
self.postMessage({ action: 'complexLogic', result });
}
};
function complexLogic(params) {
...
return ...
}
在组件或其他 JavaScript 文件中实例化 Worker 并与它通信
<template>
<div>
<button @click="start"></button>
</div>
</template>
<script>
export default {
data() {
return {
params: [], // 要计算所需要的参数
result: null,
worker: null,
};
},
methods: {
start() {
// 如果 Worker 已经存在,则终止它并重新创建
if (this.worker) {
this.worker.terminate();
}
// 创建新的 Worker 实例
this.worker = new Worker(new URL('./worker.js', import.meta.url));
// 监听来自 Worker 的消息
this.worker.onmessage = (event) => {
if (event.data.action === 'complexLogic') {
this.result = event.data.result;
}
};
// 向 Worker 发送消息
this.worker.postMessage({ action: 'complexLogic', params: this.params });
}
},
beforeDestroy() {
// 组件销毁时终止 Worker
if (this.worker) {
this.worker.terminate();
}
}
};
</script>
c. 批量更新DOM
批量更新 DOM 会导致阻塞?
同步任务:JavaScript 是单线程的,所有任务都在同一个事件循环中执行。如果在一个事件循环中进行大量同步的 DOM 操作,会阻塞其他任务(如用户交互、网络请求等),导致页面卡顿。
布局和重绘:每次修改 DOM 元素的样式或结构时,浏览器都需要重新计算布局(确定元素的位置和尺寸)并重绘页面。频繁的布局和重绘会消耗大量资源,特别是当页面上有大量元素时。
强制同步布局:某些 DOM 查询(如 offsetWidth, getBoundingClientRect 等)会强制浏览器立即进行布局计算,这会进一步增加性能开销
实践:
使用 requestAnimationFrame
requestAnimationFrame 是一个专门用于优化动画和 DOM 更新的 API。它会在下一次浏览器重绘之前调用回调函数,确保你的代码与浏览器的刷新频率同步。通过将 DOM 更新放在 requestAnimationFrame 中,你可以避免不必要的布局和重绘
减少 DOM 查询和修改
// 不推荐:频繁查询 DOM
for (let i = 0; i < 1000; i++) {
const element = document.querySelector(`.item-${i}`);
element.style.color = 'red';
}
// 推荐:减少 DOM 查询
const elements = document.querySelectorAll('.item');
elements.forEach((element, index) => {
element.style.color = 'red';
});
使用虚拟化列表
对于长列表或大量数据,考虑使用虚拟化技术(如 vue-virtual-scroller)来优化渲染性能
DocumentFragment 或者 DOMParser
DocumentFragment 是一个轻量级的 DOM 片段,不会立即影响真实 DOM,下面例子所有子节点会一次性被添加到 ul 元素中,而不是逐个插入,从而减少了性能开销
// 创建一个 DocumentFragment 实例
const fragment = document.createDocumentFragment();
// 在 DocumentFragment 中创建多个 DOM 元素
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i + 1}`;
fragment.appendChild(li);
}
// 将 DocumentFragment 插入到真实的 DOM 中
const ul = document.querySelector('ul');
ul.appendChild(fragment);
DOMParser 是一个用于解析字符串并将其转换为 DOM 文档或片段的 API。它特别适用于从 HTML 或 XML 字符串中创建复杂的 DOM 结构,而不需要逐个创建和插入元素
<template>
<div>
<ul>
<li>1</li>
<li>2</li>
...
1000条
</ul>
<button @click="updateItems">update 1000 Items</button>
</div>
</template>
<script>
export default {
methods: {
addItems() {
const sourceUl = document.querySelector('ul')
// 使用 DOMParser 解析 HTML 字符串
const parser = new DOMParser();
const htmlString = sourceUl.outerHTML
const doc = parser.parseFromString(`${htmlString}`, 'text/html');
// 修改每个li
const ul = doc.querySelector('ul');
for (let i = 0; i < ul.children.length; i++) {
const li = ul.children[i];
li.textContent = `Item ${i + 1}`;
}
//更新真实Dom
sourceUl.innerHTML = ul.innerHTML
}
}
};
</script>
延迟非关键任务
对于非关键的任务(如日志记录、分析、次要 UI 更新等),可以使用 setTimeout 将它们推迟到下一个事件循环中执行,避免阻塞主线程
使用 Web Workers
异步处理复杂逻辑 已经提到了,某些任务非常耗时且不需要直接操作 DOM,可以将它们移到 Web Worker 中执行
d. 减少重排和重绘
避免频繁修改布局属性:尽量只改变不影响布局的CSS属性(如颜色、背景色)。对于动画效果,推荐使用transform和opacity,因为它们是由GPU加速的。
实践:
CSS类切换:通过添加/移除CSS类来控制样式变化,而不是直接修改元素的内联样式。
e. 避免输入阻塞
Debounce和Throttle:使用lodash库提供的_.debounce和_.throttle函数来限制输入事件的频率。例如,搜索框中场景。
import _ from 'lodash';
export default {
data() {
return {
searchQuery: ''
};
},
watch: {
searchQuery: _.debounce(function(newVal) {
// 执行搜索逻辑
}, 300)
}
};
f. 减少input事件的触发频率
事件修饰符:Vue提供了v-model.lazy修饰符,它会在change事件而不是input事件时更新数据绑定。这可以减少不必要的更新。
实践:
延迟更新:对于不需要即时反馈的场景,可以使用v-model.lazy 来减少更新频率和上步的防抖逻辑同一个思路
4. 资源管理
a. 图片和媒体资源
现代格式:使用WebP、AVIF等现代图像格式。可以通过picture标签指定不同格式的图片路径。
<picture>
<source srcset="/img/photo.avif" type="image/avif">
<source srcset="/img/photo.webp" type="image/webp">
<img src="/img/photo.jpg" alt="A beautiful photo">
</picture>
懒加载:使用loading="lazy"属性或Vue的v-lazy指令(需要安装相应的插件)来延迟加载不在视口内的图片。
b. 减少字体文件大小
字体子集化:只加载页面实际使用的字符集。可以使用Google Fonts提供的子集化选项,或者使用工具如Font Squirrel来生成自定义字体子集。
WOFF2格式:在@font-face规则中优先引用WOFF2格式的字体文件。
@font-face {
font-family: 'MyFont';
src: url('/fonts/my-font.woff2') format('woff2'),
url('/fonts/my-font.woff') format('woff');
font-weight: normal;
font-style: normal;
}
c. 使用异步加载
第三方脚本:为分析代码等第三方脚本添加async或defer属性,确保它们不会阻塞主文档流。
<script async src="https://.../script.js"></script>
<script defer src="https://.../script.js"></script>
实践:
条件加载:根据用户的地理位置、设备类型等因素决定是否加载某些资源。
d. Service Worker
PWA支持:使用Workbox插件快速为Vue项目添加PWA功能。该插件可以帮助你轻松注册Service Worker并配置缓存策略。
实践:
这里推荐使用 Workbox ,是 Google 开发的一个基于 Service Worker 的js库,提供了很多高效好用的工具和策略,其目的在于更加高效的实现应用缓存和离线优先应用(PWA),不再需要繁杂的编写SW相关的代码
主要特性:
- 缓存管理:提供了一套强大的 API 来管理和优化资源的缓存策略,包括设置缓存有效期、限制缓存大小等。
- 路由:允许你定义如何响应不同的网络请求,例如根据 URL 或请求类型来决定是使用缓存还是从网络获取资源。
- 预缓存:可以预先下载并缓存静态资源,确保应用在首次加载后能够快速响应,即使在网络不佳的情况下。
- 运行时缓存:动态缓存用户访问的资源,以改善后续访问的速度。
- 集成简单:提供了与 Webpack 等构建工具的良好集成,使得配置和部署 Service Worker 变得非常容易。
- 更新策略:支持多种缓存更新策略,如 Cache First, Network First, 和 Stale While Revalidate,可以根据需求选择最合适的策略,Cache First 策略首先尝试从缓存中查找资源;Network First 策略首先尝试从网络获取资源;Stale While Revalidate 策略结合了 Cache First 和 Network First 的特点。它首先从缓存中返回资源(即使缓存已经过期),同时发起一个网络请求以获取最新的资源,并在收到新资源后更新缓存。这样,用户可以立即看到缓存中的内容,而后台则在尽力获取最新版本
使用:
yarn add workbox-webpack-plugin --dev
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
module.exports = {
// ... 其他 Webpack 配置 ...
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
// 这些是默认值.
clientsClaim: true,
skipWaiting: true,
// 缓存静态资源
runtimeCaching: [{
urlPattern: /.(?:png|jpg|jpeg|svg|gif)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images',
expiration: {
maxEntries: 50,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
},
},
}],
}),
],
};
创建了一个新的 GenerateSW 插件实例,会自动生成一个 Service Worker 文件,并将其添加到你的构建输出中。clientsClaim 和 skipWaiting 选项确保新版本的 Service Worker 立即生效,而不需要等待旧版本的生命周期结束。runtimeCaching 选项则定义了运行时缓存的规则,这里我们为图片资源设置了 CacheFirst 策略,并配置了一些缓存选项
注册 Service Worker,通常是写在main.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(error => {
console.log('ServiceWorker registration failed: ', error);
});
});
}
最终的效果
5. 用户体验
a. 骨架屏
骨架屏组件:创建一个简单的骨架屏组件,模仿页面的基本结构。可以在Vue应用启动时显示这个组件,直到主要内容加载完毕后再隐藏它。
实践:
渐变动画:使用CSS渐变和动画来制作骨架屏效果,使其看起来像是正在加载的内容。
b. 渐进式加载
Intersection Observer API:使用此API监听元素是否进入视口,只有当元素可见时才加载其内容。Vue有现成的插件如vue-lazyload来简化这一过程。
实践:
分段加载:对于长列表或无限滚动场景,可以采用分段加载的方式,每次只加载一部分数据,提升用户体验。
c. 预加载关键资源
Link Preload:如前所述,在index.html中添加标签预加载重要的资源。
实践:
资源优先级:确保预加载的资源是用户首次访问时最需要的,避免浪费带宽。
d. 减少FCP和LCP时间
优化首屏内容:减少阻塞渲染的资源(如CSS、JS),优化服务器响应时间和启用缓存。
渐进式JPEG:对于大尺寸图片,使用渐进式JPEG格式,让用户在图片完全加载之前就能看到模糊的预览。
实践:
精简代码:去除未使用的CSS和JavaScript代码,减少文件大小。
e. 减少INP时间
轻量化交互逻辑:确保交互后的任务尽可能简单,避免长时间运行的JavaScript任务。
微任务队列:对于必须执行的复杂任务,可以将其推迟到微任务队列中,例如使用setTimeout(fn, 0)或Promise。
实践:
通过performance 记录操作过程,分析耗时的地方
降低内存
降低 Web 应用的内存占用是提升性能和用户体验的关键步骤之一,尤其是在移动设备或资源受限的环境中。高内存占用不仅会导致应用响应变慢,还可能引发浏览器崩溃或系统资源耗尽的问题
减少不必要的全局变量和闭包
用 const 和 let 替代 var:const 和 let 具有块级作用域,可以减少不必要的变量泄漏到全局作用域。
避免不必要的闭包:如果不需要访问外部函数的作用域,尽量避免创建闭包。
及时释放不再使用的变量:通过将不再需要的变量设置为 null 或 undefined,帮助垃圾回收器更快地回收内存
优化 DOM 操作
频繁的 DOM 操作会增加内存占用,尤其是当操作涉及大量元素时。每次修改 DOM 都会触发浏览器的布局和重绘,这些操作是相当耗时的。通过减少 DOM 操作的频率和次数,可以显著降低内存占用
减少事件监听器的数量
过多的事件监听器会占用大量的内存,尤其是在动态添加或移除监听器时。确保在不再需要时及时移除事件监听器,避免内存泄漏
使用事件委托:将事件监听器绑定到父元素上,而不是每个子元素,减少事件监听器的数量。
移除不再需要的监听器:在组件卸载或页面销毁时,调用 removeEventListener 移除不再需要的监听器。
使用 once 选项:对于只需要触发一次的事件,使用 once 选项,确保事件监听器在触发后自动移除
优化 JavaScript 代码
避免内存泄漏:确保在不再需要时及时释放对象引用,避免内存泄漏。常见的内存泄漏原因包括全局变量、闭包、事件监听器等。
使用弱引用:对于不需要强引用的对象,可以使用 WeakMap 或 WeakSet 来存储它们,允许垃圾回收器在必要时回收这些对象。
优化数据结构:选择合适的数据结构来存储和处理数据。例如,使用 Set 和 Map 代替数组和对象,可以在某些情况下提高性能并减少内存占用。
分批处理大数据:对于需要处理大量数据的任务,考虑分批处理,避免一次性加载所有数据到内存中。可以使用 Web Workers 在后台线程中处理数据,避免阻塞主线程
内存比对,排查内存问题
对于 Chrome 浏览器,每个 Tab 页能使用的内存大小是有限制的。限制大小根据 Chrome 版本,Chrome位数(32/64),操作系统版本,会有所不同。可以通过 window.performance.memory 查看内存限制信息,对于在现代操作系统运行的较新版本 chrome,单tab的内存限制在 1.8G左右
内存比对(Memory Comparison)是性能优化和调试过程中的一项重要技术,尤其是在 Web 应用中。通过对比不同时间点的内存快照,你可以发现内存泄漏、不必要的内存占用以及潜在的性能问题
实践:
可以Performance monitor 实时查看js内存变化,当发现一些内存一直没有被回收,就可能有内存问题了。
接下来在 Memory 面板选择 Heap snapshot 可以记录内存堆快照。内存堆快照中只包含可访问的对象,在开始记录内存堆快照前,Chrome 总是会进行一次 GC 操作。切到Comparison 视图,可以通过比较前后两次堆快照数据来分析内存泄漏问题。
参数介绍
Constructor:对象的构造函数名称。
Objects Count Delta:两次快照之间的对象数量变化。正值表示新增的对象,负值表示删除的对象。
Shallow Size Delta:两次快照之间的对象自身内存占用变化。
Retained Size Delta:两次快照之间的对象及其引用的所有对象的总内存占用变化。这个指标特别有用,因为它可以帮助你识别哪些对象在内存中持续增长,可能是内存泄漏的来源。
常见的内存泄漏原因:
(1) 全局变量
原因:全局变量会在整个应用程序生命周期中保持在内存中,除非显式地将其设置为 null 或 undefined。
解决方案:尽量避免使用全局变量,转而使用局部变量或模块化的代码结构。
(2) 闭包
原因:闭包会捕获外部作用域中的变量,如果这些变量包含大量数据,可能会导致内存泄漏。
解决方案:确保闭包只捕获必要的变量,避免无意中保留大量数据。
(3) 事件监听器
原因:未移除的事件监听器会阻止相关对象被垃圾回收,导致内存泄漏。
解决方案:在组件卸载或页面销毁时,调用 removeEventListener 移除不再需要的监听器。
(4)定时器和间隔
原因:未清除的 setTimeout 或 setInterval 会持续占用内存,直到手动清除。
解决方案:在不再需要时,及时清除定时器,使用 clearTimeout 或 clearInterval。
(5)DOM 元素
原因:不再需要的 DOM 元素如果没有从文档中移除,仍然会占用内存。
解决方案:在移除 DOM 元素时,确保同时移除相关的事件监听器和其他引用。
(6) 缓存
原因:不合理的缓存策略可能导致缓存数据不断增长,最终耗尽内存。
解决方案:使用 LRU(Least Recently Used)缓存策略,确保只保留最近使用的数据,及时清理过期数据
6. 工具使用
Chrome DevTools
Network面板
分析资源加载顺序和时间,启用Throttling模拟不同网络条件。
Performance面板
录制性能数据,分析火焰图,识别性能瓶颈。
Memory面板
检查内存泄漏,分析堆快照,跟踪对象分配。
Summary 视图按构造函数(Constructor)分类显示对象的数量和大小。它是最常用的视图,适合快速了解哪些类型的对象占用了最多的内存
Comparison 视图用于对比两个堆快照之间的差异。它可以显示哪些对象在两个快照之间新增、删除或变化,帮助你识别内存泄漏或不必要的内存增长
Containment 视图以树状结构显示对象的引用链,帮助你了解对象是如何被保留的。通过这个视图,你可以追踪到某个对象是如何被其他对象引用的,从而找到导致内存泄漏的原因
Statistics 视图提供了堆快照的统计信息,包括对象的分布情况、内存占用的趋势等。它可以帮助你从宏观上了解内存的使用情况
Coverage面板
检测未使用的CSS和JavaScript代码,帮助精简资源。
实践:
定期审查:定期使用这些工具审查你的应用,找出潜在的性能问题。
识别未使用的代码:找出那些从未执行过的代码行或文件,可能是因为这些代码是冗余的、不必要的,或者是测试用例没有覆盖到的地方。
优化资源加载:通过移除未使用的代码,可以减少页面的加载时间和内存占用,提升性能。
提高代码质量:确保所有重要的代码路径都经过了充分的测试,避免潜在的 bug。
精简依赖:如果某些第三方库或模块没有被使用,可以考虑移除它们,减少打包体积
Lighthouse
报告解读:Lighthouse报告不仅包含性能评分,还包括SEO、可访问性和最佳实践等方面的评估。
实践:
持续改进:根据Lighthouse报告中的建议不断优化你的应用,逐步提高各项评分。