持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
先说结论
开局一张表
| 标题 | Finish | DOMContentLoaded | Load |
|---|---|---|---|
| 优化前 | 1.66s | 720ms | 925ms |
| 优化后 | 795ms | 523ms | 544ms |
面向人群
- 没有实操过图片优化的前端同学
- 想体验一下云资源的同学
背景
nginx + docker + ghost 搭建个人博客,奈何选购的服务器水管小配置低,想尽一切办法,要提高博客的渲染效率,看到腾讯云上有很多免费的资源,决定试一试
准备
这里建议小伙伴们,最好所有的云资源都用同一家供应商了,例如我这里的所有云资源都是用的腾讯云的,这样会带来很多方便的操作,一般同一供应商的各种资源之间会有合作,会产出一些最佳实践和快捷操作,可以方便开发者快速接入
对于没有服务器使用经验的前端来说,可以使用
宝塔面板来完成服务器的操作
腾讯云上轻量服务器(Lighthouse)还是很便宜的,可以是新手优惠
CVM/Lighthouse、COS、CDN、域名、宝塔
分析
就以往的经验而言,优化页面渲染性能,主要从两个方面入手
- 网络
- 请求并发
- 网络延时
- 资源包
- 压缩
总而言之,想尽一切办法,加快网络响应
基于上面的经验,我们打开newwork看看目前网页的请求情况
不需要优化的点
- gzip压缩
可优化点
- 开启http2
- 开启cdn
- 图片压缩
- 域名分片,将资源放到不同的域名下
方向制定
处理图片
首先想到是通过cos存储图片,这样既可以实现域名分片,又可以降低服务器的压力,还方便后续接入cdn。可以通过Nginx转发接口的方式,将上传的请求转发到我们自己写的服务中
大概的流程:网页 -
上传图片-> Nginx -转发-> nest.js -上传-> COS
先本地验证一下可行性,这里利用whistlejs,把线上的接口代理到本地调试
通过查看浏览器,可以得到真实的上传请求是ghost/api/admin/images/upload,响应数据格式是{ images: [{url, ref}] }
配置whistle
搭建nest.js服务
最终发现请求成功走到了本地,且页面上显示了本地返回的图片。至此,我们可以分析出可行的方案:
- 图片上传cos,通过不同域名暴露给ghost使用 - 域名分片
- 接入cdn,降低网络延时,并开启webp压缩
- Nginx开启HTTP2协议
方案实施
- 利用
nest.js生成基础的后台项目。当然,你也可以搭建自己熟悉的后台服务。腾讯云COS使用
// app.controller.ts
import { Controller, Post, UseInterceptors, UploadedFile, UploadedFiles } from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
@Post() // 图片上传
@UseInterceptors(FilesInterceptor('file'))
async uploadImages(@UploadedFiles() file) {
const url = await this.appService.uploadAssets(file);
return {
images: [
{
"url": url,
"ref": null
}
]
}
}
// app.service.ts
@Injectable()
export class AppService {
async uploadAssets(files): Promise<any> {
console.log('资源', files);
if (!files.length) {
return null
}
const { key, buffer } = this.handleParams(files)
const res = await this.handleImage({ key, buffer })
return res;
}
handleParams(files) {
const file = files[0]
// @ts-ignore
const { originalname, buffer } = file
// 图片格式默认值
let suffix = 'jpg'
const names = originalname.split('.')
if (names.length) {
suffix = names[names.length - 1]
}
const time = new Date().getTime()
const fileName = `${time}.${suffix}`
// 以时间为目录
const catalog = moment(Date.now()).format('YYYY-MM')
// 拼装图片目录
const key = `ghost/${catalog}/${fileName}`
return { buffer, key }
}
handleImage({ buffer, key }) {
return new Promise((resolve, reject) => {
cos.putObject({
Bucket: 'xxx', /* 必须 */
Region: 'xxx', /* 必须 */
Key: key, /* 必须 */
StorageClass: 'STANDARD',
Body: buffer, // 上传文件对象
onProgress: function(progressData) {
console.log(JSON.stringify(progressData));
}
}, function(err, data) {
if (!err) {
const { Location } = data
// 将存储桶地址换成CDN地址
// CDN地址是下面第2步中`自定义加速域名`
const url = Location.replace("存储桶地址", "CDN地址")
resolve(url)
} else {
reject(err)
}
})
})
}
}
将nest.js部署到服务器上,并通过Nginx完成接口的转发
node服务的部署也很简单,不想折腾
ci/cd的话,直接把打包之后的文件拷贝到服务器中,在通过pm2 start main.js来启动
// Nginx配置
server {
listen 443 ssl;
...
location /ghost/api/admin/images/upload/ {
proxy_pass http://172.18.0.1:3000/;
}
location /ghost/api/admin/media/upload/ {
proxy_pass http://172.18.0.1:3000/video/;
}
location /ghost/api/admin/files/upload/ {
proxy_pass http://172.18.0.1:3000/files/;
}
...
}
这样我们就完成将ghost图片上传接口,转发到我们自己写的node服务上,并通过node将图片上传到COS的功能,接下来给COS开启CDN支持
- 给COS开启CDN
CDN每月都有免费额度(10GB左右),对于个人博客网站,完全够用
查看加速之后的图片链接,选择自定义加速域名
- CDN开启图片压缩 webp
Webp自适应,每月也有免费额度(10TB),对于个人博客网站,完全够用
在CDN控制台,点击域名管理->图片优化,勾选WebP自适应
- Nginx开启http2
server {
listen 443 ssl http2;
...
}
对比
图片(一张图)
未优化前
优化后
| 标题 | 原图大小 | 压缩后大小(webp) | 网络协议 | 请求时长 |
|---|---|---|---|---|
| 优化前 | 158KB | -- | h1 | 756ms |
| 优化后 | 158KB | 47KB | h2 | 161ms |
首屏渲染 (3张图+280个字)
优化前
优化后
| 标题 | Finish | DOMContentLoaded | Load |
|---|---|---|---|
| 优化前 | 1.66s | 720ms | 925ms |
| 优化后 | 795ms | 523ms | 544ms |
结论
经过一波处理之后,对网站的首屏渲染提升不大,主要是提升了图片渲染的效率。哈哈哈哈哈哈~
总结
总结一下图片所用到的优化点:
- 开启h2网络协议
- cos存储
- cdn加速
- webp压缩
除了以上的优化点,其实我们还可以:
- 图片懒加载
- 对不同的屏幕,展示不同尺寸的图片