01-performance项目
vue-cli 搭建项目
- 查看 vue-cli 版本:
vue -V
报错或者说不存在 vue 命令:安装 vue-cli:
npm install -g @vue/cli
- 创建项目:
vue create 01-performance
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No
? Pick the package manager to use when installing dependencies: NPM
- 运行项目:
cd 01-performance,npm run serve
安装 axios
npm i --save axios
报错:npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve
原因:npm 不同版本库之间命令不兼容
解决:npm i --save axios --legacy-peer-deps
数据懒加载(DataLazy.vue)
该功能一共被分为三个阶段:
不获取数据
用户将要看到(使用 IntersectionObserver,或 useIntersectionObserver 进行判断)
获取数据并渲染(在视图元素可见时,再获取数据)
IntersectionObserver
作用:监听某个视窗是否被用户看见
// IntersectionObserver为 JS 的原生api,可直接使用
const box3Target = ref(null)
onMounted(() => {
const intersectionObserver = new IntersectionObserver((entries) => {
if (entries[0].intersectionRatio <= 0) {
return console.log('当前试图不可见')
}
console.log('当前视图可见')
// TODO 获取数据
})
// box3Target.value:被监听的元素对象
intersectionObserver.observe(box3Target.value)
})
useIntersectionObserver
vue 的工具库 vueuse 提供的一个基于 IntersectionObserver 的封装方法,需安装npm i --save @vueuse/core
报错:npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve
原因:npm 不同版本库之间命令不兼容
解决:npm i --save @vueuse/core --legacy-peer-deps
import { useIntersectionObserver } from '@vueuse/core'
const box3Target = ref(null)
onMounted(() => {
const { stop } = useIntersectionObserver(
box3Target,
([{ isIntersecting }]) => {
if (isIntersecting) {
console.log('当前视图可见')
// TODO 获取数据
// 触发 stop,监听停止
stop()
} else {
console.log('当前试图不可见')
}
}
)
})
图片懒加载(ImgLazy.vue)
使用 <b>自定义指令</b> 实现图片懒加载
通过 vue3 的 app.directive 完成 <b>自定义指令</b>
步骤:
构建自定义指令
利用 IntersectionObserver,或 useIntersectionObserver 完成监听
// 01-performance\src\directive\index.js
import { useIntersectionObserver } from '@vueuse/core'
const imgLazy = {
// mounted: 在绑定元素的父组件及他自己的所有子节点都挂载完成后调用
// el: 调用该指令的元素
mounted(el) {
// 图片懒加载:一开始不加载,等到将要看到时再加载
// 1. 缓存当前的图片路径
const catchSrc = el.src
console.log(catchSrc)
// 2. 把 img.src 变为占位图
el.src = 'https://res.lgdsunday.club/img-load.png'
// 3. 使用 useIntersectionObserver 监听 el 将要被看到
const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
if (isIntersecting) {
// 4. 渲染图片
el.src = catchSrc
// 5. 停止监听
stop()
}
})
}
}
export default {
// app.use 的方式使用
install: (app) => {
app.directive('imglazy', imgLazy)
}
}
// 01-performance\src\main.js
import directive from './directive'
app.use(directive)
<!-- 01-performance\src\views\ImgLazy.vue -->
<!-- v-imglazy: 自定义指令imglazy -->
<img :src="item.path" alt="" class="item-img" v-imglazy />
解决打包体积过大
例子:安装并使用了 xlsx 和 echarts 后,打包结果如下:
warning
asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
js/chunk-vendors.bd8f41b3.js (1.55 MiB)
warning
entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 ct web performance.
Entrypoints:
app (1.56 MiB)
js/chunk-vendors.bd8f41b3.js
js/app.f9f8599c.js
File Size Gzipped
dist\js\chunk-vendors.bd8f41b3.js 1591.84 KiB 522.06 KiB
dist\js\67.24f04139.js 37.50 KiB 14.77 KiB
dist\js\app.f9f8599c.js 5.74 KiB 2.61 KiB
dist\js\546.4ca973ee.js 2.26 KiB 0.86 KiB
dist\js\816.5c4d75f2.js 1.02 KiB 0.59 KiB
dist\css\546.2e47b4de.css 0.76 KiB 0.30 KiB
dist\css\816.5617b692.css 0.75 KiB 0.32 KiB
步骤:
- 分析包体积过大的主要原因
1.1 命令 vue-cli-service build --report,打包的同时生成 dist\report.html 以帮助分析包内容
// 01-performance\package.json
{
"scripts": {
"report": "vue-cli-service build --report"
}
}
1.2 查看 dist\report.html 可看到最大的占比为 echarts 和 xslx,即包体积过大的主要原因
- 使用 webpack5 的
externals配置选项,排除体积过大的包
// 01-performance\vue.config.js
// @vue/cli-service 集成了webpack的配置
const { defineConfig } = require('@vue/cli-service')
let externals = {}
// 排除打包,只需要在 build 排除
const isProd = process.env.NODE_ENV === 'production'
if (isProd) {
externals = {
xlsx: 'xlsx',
echarts: 'echarts'
}
}
module.exports = defineConfig({
transpileDependencies: true,
// 调整webpack配置
// 对象:会被 webpack-merge 合并入最终的 webpack 配置
configureWebpack: {
externals: externals
},
})
重新打包后的结果如下:
WARNING Compiled with 1 warning 14:22:38
[eslint]
D:\Study\前端\05-性能优化\01-performance\src\views\DataLazy.vue
44:3 warning Unexpected console statement no-console
51:3 warning Unexpected console statement no-console
59:3 warning Unexpected console statement no-console
79:9 warning Unexpected console statement no-console
85:9 warning Unexpected console statement no-console
D:\Study\前端\05-性能优化\01-performance\src\views\HomeView.vue
14:3 warning Unexpected console statement no-console
15:3 warning Unexpected console statement no-console
D:\Study\前端\05-性能优化\01-performance\src\views\ImgLazy.vue
7:3 warning Unexpected console statement no-console
✖ 8 problems (0 errors, 8 warnings)
You may use special comments to disable some warnings.
Use // eslint-disable-next-line to ignore the next line.
Use /* eslint-disable */ to ignore all warnings in a file.
File Size Gzipped
dist\js\chunk-vendors.8c4522c4.js 146.95 KiB 51.96 KiB
dist\js\67.e0736f3f.js 37.71 KiB 14.86 KiB
dist\js\app.8c52ff36.js 5.74 KiB 2.61 KiB
dist\js\546.4ca973ee.js 2.26 KiB 0.86 KiB
dist\js\816.5c4d75f2.js 1.02 KiB 0.59 KiB
dist\css\546.2e47b4de.css 0.76 KiB 0.30 KiB
dist\css\816.5617b692.css 0.75 KiB 0.32 KiB
- 运行此时的打包文件,会得到错误:
Uncaught ReferenceError: XLSX is not defined
可以利用第三方
anywhere运行打包之后的项目
- 安装:
npm i -g anywhere- 在 dist 目录下,执行终端命令:
anywhere- 会自动启动服务
- 使用 CDN 引入被 webpack 排除的包,利用 vue-cli 默认配置的
HtmlWebpackPlugin携带属性的特性,添加 CDN
// 01-performance\vue.config.js
const { defineConfig } = require('@vue/cli-service')
let cdnJS = []
// 排除打包,只需要在 build 排除
const isProd = process.env.NODE_ENV === 'production'
if (isProd) {
cdnJS = [
'https://unpkg.com/echarts@5.5.1/dist/echarts.js',
'https://unpkg.com/xlsx@0.18.5/dist/xlsx.full.min.js'
]
}
module.exports = defineConfig({
transpileDependencies: true,
// 允许对内部的 webpack 配置进行更细粒度的修改
chainWebpack(config) {
// 修改插件选项
// config.plugin('html'):获取到HtmlWebpackPlugin
// tap():修改参数
config.plugin('html').tap((args) => {
// 携带指定的属性到 HtmlWebpackPlugin
args[0].cdnJS = cdnJS
return args
})
}
})
<!-- 01-performance\public\index.html -->
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<!-- 请求不添加请求来源,解决403错误 -->
<meta name="referrer" content="no-referrer" />
<title>
<!-- 从 htmlWebpackPlugin 配置项中获取 title 属性 --> <%=
htmlWebpackPlugin.options.title %>
</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="app"></div>
<!-- 从 htmlWebpackPlugin 配置项中获取 cdnJS 属性 -->
<% for(var js of htmlWebpackPlugin.options.cdnJS){ %>
<script src="<%=js%>"></script>
<% } %>
</body>
</html>
其它优化
externals + CDN 只能减小打包的体积,加快访问速度
其它的优化如:
gzip 压缩
http 缓存
service worker
gzip 压缩
通过 LZ77 算法与 Huffman 编码来压缩文件,<b>重复度越高</b> 的文件可压缩的空间越大,对JS、CSS、HTML等文本资源均有效
当 Nginx 返回 js 文件的时候,会判断是否开启 gzip,然后压缩后再返回给浏览器
该方法需要 Nginx 配置开启 Gzip 压缩,单纯前端通过 webpack 插件开启 Gzip 压缩是不能达到优化效果的
http 缓存
网页 304 状态码:所请求的资源<b>未修改</b>。服务器返回此状态码时,<b>不会</b>返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之前修改的资源
大多数情况下,304 一般在服务框架中进行了处理,前端一般不需要过于关注
service worker
是一个 JS api:https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API
旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的资源
还提供入口以推送通知和访问后台同步 API
