在上一篇 **[前端性能优化到底该怎么做(上)— 开门见山] **[2] 一文中介绍了和前端性能优化相关的一些前置知识,那么本篇就针对优化方案进行总结,核心的方向还是上篇文章中提到的内容:
保证资源更快的 加载速度
**:达到越快渲染越快,视图展现就越快保证视图更快的 渲染速度/交互速度
**:用户与页面交互,前提是页面要渲染出来,其次是页面需要尽早反馈,目的就是保证用户良好的体验性其实将上述两点再进行翻译,那么其实指的就是 网络层面的优化 和 浏览器层面的优化,这样看来其实前端性能优化方向还是很明确的,只不过明确的方向中还是会涉及不同方面的具体优化手段。
0B249F1E.jpg
还是不得不回顾 从输入 URL
到页面加载完成 的核心过程:
DNS
解析TCP
连接HTTP
请求HTTP
资源从上述的内容来看,不难发现每一步都是需要消耗一定的时间,那么优化的方向就可以围绕着这些内容来考虑。
长文预警❗️❗️❗️ 长文预警❗️❗️❗️ 长文预警❗️❗️❗️ 长文预警❗️❗️❗️
1.gif
下面内容主要针对 DNS
解析、**TCP
连接、HTTP
请求/响应** 等阶段来谈的优化,核心优化核心其实就是 网络层面。
dns-prefetch
减少 DNS 的查询时间dns-prefetch
能够 提前解析 后续可能会用到的 不同域的域名,使解析结果 缓存到系统缓存 中,缩短 DNS
解析时间以提高网站的访问速度。
比如在掘金中的体现如下:
【扩展】
DNS
解析的核心过程
当浏览器访问一个域名时需解析一次 DNS
,以获得对应域名的 IP
地址:
IP
地址,若存在返回,若不存在进入下一步/etc/hosts
文件中查询是否有对应域名的 IP
地址,若存在则返回,若不存在进入下一步IP
地址,若存在返回结果,没有则进行下一步IP
地址记录到缓存中,并返回给客户机(会缓存起来),客户机根据收到的 IP
地址访问该网站preconnect
提前建立连接preconnect
的作用是提前和第三方资源建立连接,设置了它浏览器就会做好早期的连接工作,但这个连接通常只会维持 10 s
。
比如在当前域请求一个资源前,可能会涉及 DNS
寻址、TLS
握手、TCP
握手、重定向等,这过程也会花费一定的时间。
比如在掘金中的体现如下:
preload / prefetch
预先加载资源preload
的作用是提前加载页面对应的 关键资源 加快页面的渲染,preload
的优先级顺序和 as
属性相关,**具体可见**[3]。
【注意】
as
属性一定要设置,除了上面提到的设置优先级外,还涉及到浏览识别的问题:如果没有设置as
属性,后续遇到该请求就会被作为一个XHR
请求,把意味着资源预加载的功能可能会失效,因为可能会每次都发起新的请求获取
比如在掘金中的体现如下:
比如在 vue-cli
的默认 webpack
配置,如下:
image.png
preload
是对资源的预加载,它虽提前加载但只在需要执行时执行,即这个资源一定是当前页面所需要的资源,如果是需要为下一个页面提前加载资源,那么应该使用 prefetch
,它会在 浏览器空闲时 下载资源。
比如在 vue-cli
的默认 webpack
配置,如下:
image.png
资源是需要通过 http
数据包的方式在网络中进行传输的,那么只要能减少传输数据包的体积,也是能够使得资源更快到达客户端,这也是压缩资源体积的核心目的。
HTTP 压缩中一个典型代表就是 gzip
,它是一种优秀的压缩算法,可对 http
请求中的一些文件资源进行压缩处理,一般来讲是要在服务端处理的,可通过在响应头中设置 Content-encoding: gzip
表示当前资源使用的压缩方式(如:gzip、deflate、br
等),便于客户端使用正确的方式解压。
【注意】
gzip
并不是万能的,它不能保证针对每个文件的压缩都能使其体积变小,关于Content-Encoding
的内容 **可点此查阅**[4],或者可参考 **content-encoding 除了gzip之外,你还知道哪些?**[5]
比如在京东中的体现:
image.png
比如在掘金中的体现:
image.png
有 HTTP
压缩 不就够了吗?为什么还需要 Webpack
压缩?
首先必须要明确的是压缩的过程本身就是会消耗时间的,如果所有资源都等到被访问的时候再由服务端进行压缩,在压缩完成之前客户端还是得处于等待状态,即仍 不能保证资源以最快的速度到达客户端。
那么优化方案就是将压缩资源的时间放到打包构建中,毕竟只有真正需要发布线上生产环境时才需要执行一系列的打包优化的操作,而这相比于 http
的 请求/响应 速度,稍微延长产物打包时间没有什么大问题。
下面会列举一些 Webpack 插件,但并不会去讲其中的具体用法,因为这些只是达到目的的不同方案而已,每个方案要是细讲都可以独占一篇文章,在这是没有必要的,具体用法可自行查阅。
使用
CompressionPlugin
压缩文件
image.png
**webpack
文档**[6] 提供插件合集中就包含了该插件,它的作用就是:**Prepare compressed versions of assets to serve them with Content-Encoding.
**
使用
HtmlWebpackPlugin
压缩HTML
文件
image.png
通常我们需要 **HtmlWebpackPlugin
**[7] 插件来生成对应 HTML
或 对已有的 HTML
模板自动注入 webpack bundles
资源,除此之外,它还可配置 minify
选项实现压缩模板的目的。
可以在 vue
项目下执行 vue inspect \--mode production > webpack.config.js
来查看脚手架的默认 webpack
配置内容,比如:
image.png
使用
SplitChunksPlugin
自定义分包策略
Webpack
默认会将尽可能多的模块代码打包在一起,这种默认规则的带来的优点和缺点都很明显:
HTTP
请求数**SplitChunksPlugin
**[8] 是 Webpack 4
之后内置实现的最新分包方案,与 Webpack 3
中的 CommonsChunkPlugin
相比,它能够基于一些更灵活、合理的启发式规则将 Module
编排进不同的 Chunk
,最终构建出 性能更佳、缓存更友好 的应用产物。
比如在 vue-cli
的默认 webpack
配置,如下:
image.png
使用
MiniCssExtractPlugin
抽离和压缩CSS
**MiniCssExtractPlugin
**[9] 会将 CSS
提取到单独的文件中,为每个包含 CSS
的 JS
文件创建一个 CSS
文件,并且支持 CSS
和 SourceMaps
的 按需加载 。
比如在 vue-cli
的默认 webpack
配置,如下:
image.png
使用
ImageMinimizerWebpackPlugin
压缩图片资源
图片仍是一个 Web
应用中的必不可少的资源,而图片资源的体积也是首屏页面加载的瓶颈之一,因此,压缩图片也是性能优化需要考虑的内容。
**ImageMinimizerWebpackPlugin
**[10] 可用于使用 优化/压缩 所有图像,它可以支持 无损(不损失质量)、有损(质量下降) 两种模式的压缩方式。
image.png
通过
Tree Shaking
移除无用代码
**Tree Shaking
**[11] 依赖于 ES6
模块语法的 **静态结构**[12] 特性(如: import
[13] 和 export
[14]),当 webpack
的模式 mode
为 "production"
时,就可以启用 **更多优化项**[15],包括 压缩代码 与 Tree Shaking。
但同时我们就必须保证:
ES6
模块语法,即 import
和 export
babel
)将对应的 ES6
模块语法转换为 CommonJS
的语法(如:@babel/preset-env
的默认行为)package.json
文件中添加 "sideEffects"
属性,标识当前内容是否存在副作用操作/*#__PURE__*/
注释,将函数调用标记为无副作用[16]不同协议 下 请求数量 仍然可能成为 请求/响应 慢的原因:
base64
图片、通过 symbol
引用 svg
等不必要的 cookie
来回传输会造成带宽浪费:
cookie
存储的内容CDN
托管(即非同域),不同域名默认不携带 cookie
CDN
加速的本质是缓存加速,将服务器上存储的静资源容缓存在 CDN
节点上,当后续访问这些静态内容时,无需访问服务器源站,选择就近访问 CDN
节点即可,从而达到加速的效果,同时减轻服务器源站的压力。
在掘金中体现如下:
image.png
http1.x
存在的问题:HTTP
的底层协议是TCP
,而TCP
是面向连接即需要 三次握手 才能建立连接,其中:
http1.0
中使用的是 短连接,即 一次请求/响应 结束后就会断开连接,这个过程比较耗时
http1.1
中使用的是 长连接,在 请求/响应头 中设置 Connection: keep-alive
即可开启,优点是 长连接 允许多个请求共用一个 TCP
连接,缺点是带来了 队头阻塞:
每个 TCP
连接中的多个请求,需要进行排队,只有队头的请求被响应,才能继续处理下一个请求
其中一个缓解方案就是如果当前 TCP
连接中发生 队头阻塞,那就将部分请求放到其他 TCP
连接中
浏览器一般会限制同一个域名建立 6-8
个 TCP
链接,这也就是为什么需要为应用划分子域名、静态资源托管 CDN
的原因之一
http1.x
中 header
部分的内容可能会很大,而且每一个请求可能都需要携带大量 重复header
的 文本内容,而这些也是导致 请求/响应 慢的原因之一
以上问题
http2.0
都能够解决:
针对 TCP 连接数 被限制的问题,http2.0
采用 多路复用 一个域名只对应一个 TCP
连接
针对 http 队头阻塞 问题,http2.0
中通过二进制分帧层为每个 请求/响应 添加 stream id
保证 请求/响应 一一对应,即不必等待前面的请求处理完成,并且还可以为每个请求添加 优先级
针对 header
数据大的问题,http2.0
中传输的 header
帧经过处理后会用 二进制 的方式表示,替换了原本的 文本格式,并使用 HPACK
算法进行压缩
接收/发送 两端会维护一个 索引表,通过下标来标识 header
,针对后续重复的 header
信息就可以用对应的索引来代替
针对传统的 请求 —> 响应 模式,http2.0
中提供了 服务端推送 的能力,让服务端能够主动向客户端推送关键资源,加快资源加载
比如在掘金中的体现:
保证资源快速到达客户端后,接下来就需要针对 浏览器的解析和渲染 进行优化,当然还包括后续的页面交互的优化,这其实就是 浏览器层面 的优化。
浏览器渲染 HTML
文件的核心过程:
HTML 解释器:将 HTML
文档经过词法分析输出 DOM Tree
CSS 解释器:解析 CSS
文档,生成样式规则 CSSOM
样式计算:将 DOM Tree
和 CSSOM
合并生成 Render Tree
布局计算:计算 Render Tree
节点在页面中的坐标位置,创建 Layout Tree
划分图层:页面中有很多复杂的效果,如一些复杂的 3D
变换、页面滚动,或使用 z-index
做 z
轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,生成对应的图层树 Layer Tree
图层绘制:
染引擎实现图层的绘制时,会把一个图层的绘制拆分成很多小的 绘制指令,并将这些指令按照顺序组成一个 待绘制列表
当图层的绘制列表准备好后,主线程 会把 待绘制列表 提交(commit
)给 合成线程
栅格化 raster:
由于视口有限,用户只能看到页面的很小一部分,没必要绘制出所有图层内容,因此 合成线程 会将 图层(layer) 划分为 图块(tile)
渲染进程 把 生成图块的指令 发送给 GPU
并执行生成图块的 位图
合成和显示:
一旦所有图块都被光栅化,合成线程 就会生成一个绘制图块的命令 —— DrawQuad
,然后将该命令提交给 浏览器进程
浏览器进程 里面有一个 viz 组件,会根据 DrawQuad
命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上
真正渲染视图之前,必然要生成 DOM Tree
和 **CSSOM
**,因此必须保证 HTML 解释器 和 CSS 解释器 都尽早处理完成,同时 JavaScript
的加载和执行可能会阻塞这个过程:
HTML
文档中首次渲染的节点数量要尽量少,避免深层次的嵌套结构,避免大量使用慢标签(如:iframe
)等CSS
资源放文档头部,降低 CSS
复杂度,比如 合理使用 CSS
选择器JavaScript
资源放文档底部,合理使用 defer、async
的加载方式懒加载主要是针对数量大、资源加载慢的情况,比如图片资源、大量列表数据展示等:
延后加载
**,或者说当移入的可视区时再加载分页加载、上拉加载
等方式分批次渲染白屏是由于 SPA
应用需要等待 JavaScript
加载并执行完成后才会生成具体的页面结构内容导致的,即初始化模板中没有任何有意义需要被渲染的 HTML
结构:
loading
**,可在模板中添加默认的 loading
效果,等到真正页面内容被渲染就可以替换 loading
内容现代框架默认是属于客户端应用框架,即组件的代码会在浏览器中运行,然后向页面输出 DOM 元素,也叫 客户端渲染(client-side rendering,CSR):
优点
**用户体验更好
**,基于 前端路由 的方式并不会真正进行 页面跳转,即不会使页面重新刷新、加载,带来更高的流畅度
占用服务端资源少
,CSR 渲染 是交由客户端进行处理,服务端不需要关心渲染计算的过程,减轻了服务端的压力
缺点
**"白屏" 时间较长
**,主要是因为 CSR 渲染需要 *.js
的支持,而 *.js
又必须保证 *.html
被接收和解析, *.html
又强依赖于当前的 网络环境,因此,在差网环境下回导致 白屏时间过长,特别是在移动网络环境下
**对 SEO 的支持不友好
**,因为 白屏时间较长 导致在一段时间内没有重要的内容能够交由 搜索引擎 进行分析、分类、打标签等,并且 搜索引擎 并不会等待页面渲染完成,因此对 SEO 优化并不友好
服务端渲染(server-side rendering,SSR) 可将相同组件在服务渲染成相应的 HTML
字符串,并发送给浏览器进行渲染,即客户端不需要等待所有的 JavaScript 都被下载并执行之后才显示,所以用户可以更快看到完整的渲染好的内容。
上述 服务端渲染(server-side rendering,SSR) 虽然能够解决一些客户端存在的问题,但它也带来了别的问题:
需要保证开发一致性
**,比如 服务端 和 客户端 能够执行的组件生命周期钩子不同,一些外部库在 服务端渲染 应用中可能需要经过特殊处理需要更多的构建设定和部署要求
**,一个完全静态的 SPA 可以部署在任意的静态文件服务器,但服务端渲染应用需要一个能够运行 Node.js 服务器的环境更多的服务端负载
**,在 Node.js 中渲染一个完整的应用,会比仅供应静态文件产生更密集的 CPU 运算,并且需要考虑访问流量过大的情况等因此,并不是所有应用都合适 服务端渲染,如果只是希望通过 SSR 来改善一些 推广页面 (如 /
、/about
、/contact
等) 的 SEO,那么应该优先考虑 预渲染 的方式:
routes
路由预先生成对应的页面内容webpack
**[17],就可通过 **prerender-spa-plugin
**[18] 来支持 预渲染重绘:页面中元素样式的改变并不影响它在文档流中的位置时(如:color、background-color、visibility
等),浏览器会将新样式赋予给元素并 重新绘制
回流:当 Render Tree
中部分或全部元素的 尺寸、结构、某些属性 发生改变时,浏览器 重新渲染 部分或全部文档
减少对 DOM
进行频繁操作
使经常变动的元素脱离文档流,如具有持续性的动画效果,会一直触发回流和重绘
避免访问或减少访问会导致浏览器 强制刷新队列 的属性,如:offsetTop、offsetLeft、offsetWidth
等
【扩展】浏览器的渲染队列机制会通过 队列 将会触发 回流或重绘 的操作进行存储,等到一定的时间或一定的数量时再执行这些操作
避免对 css
进行单个修改,如在 JavaScript
修改多个样式时,尽量使用 css
选择器实现样式的集中变更
使用 will-change
开启 GPU
加速,will-change
指定的属性使得浏览器可在元素属性真正发生变化之前提前做好对应的优化
预先设定图片尺寸,避免图片资源加载完成后引发回流
防抖:多次频繁触发执行操作,以 最后一次 为准,忽略中间过程
节流:在指定的时间间隔内,只允许 执行一次对应的操作
合理使用 防抖/节流
优化应用中的操作,比如 节流
可用于优化 滚动事件、模糊搜索等,**防抖
** 可用于优化一些按钮点击操作等。
JavaScript
是单线程的,如果存在需要大量计算的场景(如视频解码),UI
线程就会被阻塞,甚至浏览器直接卡死。
Web Worker
可以使脚本运行在新的线程中,它们独立于主线程,可以进行大量的计算活动,而不会影响主线程的 UI
渲染,但不能滥用 Web Worker
。
最常用的还是 分页加载 的方式:
table
表格的渲染,只会渲染固定数量的 DOM
上拉加载
列表的渲染,随着加载数据的增多,对应的 DOM
节点也会增多,达到某个限制页面一定会发生卡顿虚拟列表 核心就是固定渲染的 DOM
数,通过动态切换数据内容实现视图的更新,并保证文档中真实 DOM
的数量不随着数据量增大而增大(其实和 table
分页很像,但它支持滚动)。
想了解其核心实现的,可查看 **虚拟滚动是怎么做性能优化的\?**[19]
大部分的项目总少不了文件上传功能,但对大文件的上传还是有必要进行优化,所谓的 断点续传、秒传 都要基于 分片上传 这个核心功能。
想了解其核心实现的,可查看 **请问:怎么实现大文件快速上传?**[20]
针对 Excel 导入/导出 的功能相信很多人第一印象是后端的活,但大多数情况下,后端接口的处理速度会受各种影响,导致速度方面不是很理想,有时候也是需要前端来进行优化处理的,比如导入时前端不发送文件只发送解析后的 JSON
数据,导出时不需要单独发送额外接口,直接使用当前展示数据实现导出等。
想了解其核心实现的,可查看 **给我实现一个前端的 Excel 导入和导出功能**[21]
这部分内容相信大家都不陌生,下面就简单列举一些内容(包括但不限于):
template
模板中使用的数据,可使用 Object.freeze()
进行冻结,避免被转为 不必要的响应式数据Vue
组件初始化是比较损耗性能的,使用 函数式组件 减少组件初始化的过程,适用于实现没有业务逻辑只展示内容的简单组件v-show
和 v-if
、为 v-for
组件设定唯一 key
(非 index
)、v-for
和 v-if
不要一起使用等KeepAlive
复用组件,避免组件重复的创建、销毁带来的性能损耗() => import(xxx)
方式实现路由懒加载ESM
的方式封装自定义工具库等setTimeout、setInterval、addEventListener
等基于 vue.config.js
或 webpack
进行优化,具体可见 **如何优化你的 vue-cli 项目?**[22]
以上优化方案对应到 **上一篇**[23] 中提到的性能指标,如下:
Time to First Byte,TTFB
)First Paint,FP
)First Contentful Paint,FCP
)Largest Contentful Paint, LCP
)Cumulative Layout Shift, CLS
)First Input Delay, FID
)关键资源越早到达客户端,证明 TTFB
时间越短,而这也能间接的减少 FP
和 FCP
的时间;对资源进行了压缩处理意味着能够尽可能提升 LCP
的时间;减少了页面的 回流/重绘 就能使得 CLS
的数值越小,视图越趋于稳定;FID
是一个用于跟踪浏览器对用户输入做出反应之前的延迟时间的指标,包括点击和敲击,保证资源的快速加载和页面尽早渲染,其对应的数值就越小,视图响应就越快。
前端性能优化 的范围实在太大,以上列举的优化主要围绕着 资源加载、页面渲染/交互 两个大的方向,而具体的优化方案其实有很多(包括但不限于上述内容),很多内容随着关注的方向不同而不同。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/FbFtNwBKaU7o0TcPFZSpvA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。