赞
踩
提升⾸屏的加载速度,是前端性能优化中「最重要」的环节,
1.1. 原因
SPA 项⽬,⼀个路由对应⼀个⻚⾯,如果不做处理,项⽬打包后,会把所有⻚⾯打包成⼀个 ⽂件,「当⽤户打开⾸⻚时,会⼀次性加载所有的资源」,造成⾸⻚加载很慢,降低⽤户体验
1.2. 原理
E S 6 的动态地加载模块— — i m p o r t ( )
w e b p a ck C h u n k N a m e 作用是 w e b p a ck 在打包的时候,对异步引入的库代码(l o d a s h ) 进行代码分割时,设置代码块的名字。w e b p a ck 会将任何一个异步模块与相同的块名称组合到相同的异步块中
1.2.3 代码示例
// 通过webpackChunkName设置分割后代码块的名字 const Home = () => import(/* webpackChunkName: "home" */ "@/views/home/index.vue"); const MetricGroup = () => import(/* webpackChunkName: "metricGroup" */ "@/views/metricGroup/index.vue"); ………… const routes = [ { path: "/", name: "home", component: Home }, { path: "/metricGroup", name: "metricGroup", component: MetricGroup }, ………… ]
2.1. 使用场景
有时资源拆分的过细也不好,可能会造成浏览器 h t t p 请求的增多
<script>
const dialogInfo = () => import(/* webpackChunkName: "dialogInfo" */ '@/components/dialogInfo');
export default {
name: 'homeView',
components: {
dialogInfo
}
}
</script>
消除无用的 J S 代码,减少代码体积
3.1. 原理
依赖于 E S 6 的模块特性,E S 6 模块依赖关系是确定的,和运行时的状 态无关,可以进行可靠的静态分析,这就是 t r e e - s h a k i n g 的基础
静态分析就是不需要执行代码,就可以从字面量上对代码进行分析。E S 6 之前的模块化,比如 C o m m o n J S 是动态加载,只有执行后才知道引用的什么模块,就不能通过静态分析去做优化,正是基于这个基础上,才使得 t r e e - s h a k i n g 成为可能
3.2. Tree shaking 并不是万能的!!!
无法通过静态分析判断出一个对象的哪些变量未被使用,所以 t r e e - s h a k i n g 只对使用 e x p o r t 导出的变量生效
S PA 单⻚应用,无论 v u e 还是 r e a c t ,最初的 h t m l 都是空白的, 需要通过加载 J S 将内容挂载到根节点上,这套机制的副作用:会造成⻓时间的白屏
4.1. 骨架屏插件v u e - s k e l e t o n - w e b p a c k - p l u g i n
n p m i v u e - s k e l e t o n - w e b p a c k - p l u g i n
// 骨架屏 const SkeletonWebpackPlugin = require("vue-skeleton-webpack-plugin"); module.exports = { configureWebpack: { plugins: [ new SkeletonWebpackPlugin({ // 实例化插件对象 webpackConfig: { entry: { app: path.join(__dirname, './src/skeleton.js') // 引入骨架屏入口文件 } }, minimize: true, // SPA 下是否需要压缩注入 HTML 的 JS 代码 quiet: true, // 在服务端渲染时是否需要输出信息到控制台 router: { mode: 'hash', // 路由模式 routes: [ // 不同页面可以配置不同骨架屏 // 对应路径所需要的骨架屏组件id,id的定义在入口文件内 { path: /^\/home(?:\/)?/i, skeletonId: 'homeSkeleton' }, { path: /^\/detail(?:\/)?/i, skeletonId: 'detailSkeleton' } ] } }) ] } }
// skeleton.js import Vue from "vue"; // 引入对应的骨架屏页面 import homeSkeleton from "./views/homeSkeleton"; import detailSkeleton from "./views/detailSkeleton"; export default new Vue({ components: { homeSkeleton, detailSkeleton, }, template: ` <div> <homeSkeleton id="homeSkeleton" style="display:none;" /> <detailSkeleton id="detailSkeleton" style="display:none;" /> </div> `, });
首⻚中不乏有需要渲染⻓列表的场景,当渲染条数过多时,所需要的渲染时间会很⻓,滚动时还会造成⻚面卡顿,整体体验非常不好
5.1. 定义
「虚拟滚动— — 指的是只渲染可视区域的列表项,非可⻅区域的」不渲染,在滚动时动态更新可视区域,该方案在优化大量数据渲染时效果是很明显的
5.2. 虚拟滚动基本原理
计算出 t o t a l H e i g h t 列表总高度,并在触发时滚动事件时根据 s c r o l l To p 值不断更新s t a r t I n d e x 以及 e n d I n d e x ,以 此从列表数据 l i s t D a t a 中截取对应元素
5.3. 虚拟滚动插件
虚拟滚动的插件有很多,比如 v u e - v i r t u a l - s c r o l l e r 、v u e - v i r t u a l - s c r o l l - l i s t 、r e a c t - t i ny v i r t u a l - l i s t 、r e a c t - v i r t u a l i z e d 等
5.4. vue-virtual-scroller
// 安装插件 npm install vue-virtual-scroller // main.js import VueVirtualScroller from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' Vue.use(VueVirtualScroller) // 使用 <template> <RecycleScroller class="scroller" :items="list" :item-size="32" key-field="id" v-slot="{ item }"> <div class="user"> {{ item.name }} </div> </RecycleScroller> </template>
该插件主要有 RecycleScroller.vue、DynamicScroller.vue 这两个组件,其中 RecycleScroller 需要 item 的高度为静态的,也就是列表每个 item 的高度都是一致的,而 DynamicScroller 可以兼容 item 的高度为动态的情况
由于浏览器 G U I 渲染线程与 J S 引擎线程是互斥的关系,当⻚面中有很多⻓任务时,会造成⻚面 U I 阻塞,出现界面卡顿、掉帧等情况
6.1. 哪些是长任务?
打开控制台,选择 Pe r f o r m a n c e 工具,点击 S t a r t 按钮,展开 M a i n 选项,会发现有很多红色的三⻆,这些就属于⻓任务(⻓任务:执行时间超过 5 0 m s 的任务)
6.2. Web Worker 的通信时⻓
假如一个运算执行时⻓为 1 0 0 m s ,但是通信时⻓为 3 0 0 m s , 用了 We b Wo r k e r 可能会更慢
什么是通信时长?
比如新建一个 w e b wo r k e r, 浏 览器会加载对应的 wo r k e r. j s 资源,下图中的 Ti m e 是这个资源的通信时⻓(也叫加载时⻓)
6.3. 什么时候使用web worker?
当任务的运算时⻓ - 通信时⻓ > 5 0 m s ,推荐使用 We b Wo r k e r
6.4. 代码示例
let sum = 0;
for (let i = 0; i < 200000; i++) {
for (let i = 0; i < 10000; i++) {
sum += Math.random()
}
}
// worker.js
onmessage = function (e) {
// onmessage获取传入的初始值
let sum = e.data;
for (let i = 0; i < 200000; i++) {
for (let i = 0; i < 10000; i++) {
sum += Math.random()
}
}
// 将计算的结果传递出去
postMessage(sum);
}
r e q u e s t A n i m a t i o n F r a m e 是浏览器专⻔为动画提供的 A P I ,它的刷新频率与显示器的频率保持一致,使用该 a p i 可以解决用 s e t Ti m e o u t / s e t I n t e r va l 制作动画卡顿的情况
8.1. 正常模式
< s c r i p t s r c = " i n d e x . j s " > < / s c r i p t >
这种情况下 J S 会阻塞 d o m 渲染,浏览器必须等待 i n d e x . j s 加载和执行完成后才能去做其它事情
8.2. async 模式
< s c r i p t a s y n c s r c = " i n d e x . j s " > < / s c r i p t >
a s y n c 模式下,它的加载是异步 的,J S 不会阻塞 D O M 的渲染,a s y n c 加载是无顺序的, 当它加载结束,J S 会立即执行
使用场景:若该 J S 资源与 D O M 元素没有依赖关系,也不会产生其他资源所需要的数据时,可以使用 a s y n c 模式,比如埋点统计
8.3. defer 模式
< s c r i p t d e f e r s r c = " i n d e x . j s " > < / s c r i p t >
d e f e r 模式下,J S 的加载也是异步的,d e f e r 资源会在 D O M C o n t e n t L o a d e d 执行之前,并且 d e f e r 是有顺序的加载。如果有多个设置了 d e f e r 的 s c r i p t 标签存在,则会按照引入的前后顺序执行,即便是后面的 s c r i p t 资源先返回
使用场景:
一般情况下都可以使用 d e f e r ,特别是需要控制资源加载顺序时
8.3.1. defer和async的相同点和区别
8.3.1.1. Defer 和 async 的相同点
8.3.1.2. Defer 和 async 的区别
页面的生命周期
DOMContentLoaded —— 浏览器已完全加载 HTML,并构建了 DOM 树,但像<img>
和样式表之类的外部资源可能尚未加载完成。
—DOM 已经就绪,因此处理程序可以查找 DOM 节点,并初始化接口。
load —— 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等。
—DOM 已经就绪,因此处理程序可以查找 DOM 节点,并初始化接口。
beforeunload/unload —— 当用户正在离开页面时。
- beforeunload 事件 —— 用户正在离开:我们可以检查用户是否保存了更改,并询问他是否真的要离开。
- unload 事件 —— 用户几乎已经离开了,但是我们仍然可以启动一些操作,例如发送统计数据。
8.4. module 模式
< s c r i p t t y p e = " m o d u l e " > i m p o r t { a } f r o m ' . / a . j s ' < / s c r i p t >
t y p e = " m o d u l e "
在主流的现代浏览器中,s c r i p t 标签的属性可以加上 t y p e = " m o d u l e " ,浏览器会对其内部的 i m p o r t 引用发起 H T T P 请求,获取模块内容。这时 s c r i p t 的行为会像是 d e f e r 一样,在后台下载,并且等待 D O M 解析Vi t e 就是利用浏览器支持原生的 e s m o d u l e 模块,开发时跳过打包的过程,提升编译效率
8.5. preload
< l i n k r e l = " p r e l o a d " a s = " s c r i p t " h r e f = " i n d e x . j s " >
8.5.1. 定义
关键字 preload 作为元素 的属性 rel 的值,表示用户十分有可能需要在当前浏览中加载目标资源,所以浏览器必须预先获取和缓存对应资源。(mdn)
8.5.2. 特点
8.6. prefetch
< l i n k r e l = " p r e f e t c h " a s = " s c r i p t " h r e f = " i n d e x . j s " >
8.6.1. 定义
prefetch是利用浏览器的空闲时 间,加载⻚面将来可能用到的资源的一种机制;通常可以用于加载其他⻚面(非首⻚)所需要的资源,以便加快后续⻚面的打开速度
8.6.2. 特点
8.7. 加载方式总结
async、defer是script标签的专属属性,对于网⻚中的其他资源,可以通过 l i n k 的p r e l o a d 、p r e f e t ch 属性来预加 载如今现代框架已经将 p r e l o a d 、p r e f e t ch 添加到打包流程中了,通过灵活的配置,去使用这些预加载功能,同时我们也可以审时度势地向 s c r i p t 标签添加 a s y n c 、d e f e r 属性去处理资源,这样可以显著提升性能
9.1. 图片的动态裁剪
很多云服务,比如阿里云或七牛云,都提供了图片的动态裁剪功能,效果很棒,确实是钱没有白花只需在图片的 u r l 地址上动态添加参数,就可以得到你所需要的尺寸大小,比如: h t t p : / / 7 x k v 1 q . c o m 1 . z 0 . g l b . c l o u d d n . c o m / g r a p e . j p g ? i m a g e V i e w 2 / 1 / w / 2 0 0 / h / 2 0 0
9.2. 图片的懒加载
对于一些图片量比较大的首⻚,用户打开⻚面后,只需要呈现出在屏幕可视区域内的图片,当用户滑动⻚面时,再去加载出现在屏幕内的图片,以优化图片的加载效果
9.3. 实现原理
由于浏览器会自动对⻚面中的 i m g 标签的 s rc 属性发送请求并下载 图片,可以通过 h t m l 5 自定义属性 d a t a - x x x 先暂存 s rc 的值,然后在图片出现在屏幕可视区域的时候,再将d a t a - x x x 的值重新赋值到 i m g 的 s rc 属性即可
<img src="" alt="" data-src="./images/1.jpg">
<img src="" alt="" data-src="./images/2.jpg">
9.4. 插件示例vue-lazyload
// 安装 npm install vue-lazyload // main.js 注册 import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload) // 配置项 Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png', // 图片加载失败时的占位图 loading: 'dist/loading.gif', // 图片加载中时的占位图 attempt: 1 }) // 通过 v-lazy 指令使用 <ul> <li v-for="img in list"> <img v-lazy="img.src" :key="img.src" > </li> </ul>
9.5. 使用字体图标
字体图标是⻚面使用小图标的不二选择,最常用的就是 i c o n f o n t
将小图片转换为 b a s e 6 4 编码字符串,并写入 H T M L 或者 C S S 中 ,减少 h t t p 请求
9.6.1. 优缺点
9.6.2. 插件示例url-loader
// 安装 npm install url-loader --save-dev // 配置 module.exports = { module: { rules: [{ test: /.(png|jpg|gif)$/i, use: [{ loader: 'url-loader', options: { // 小于 10kb 的图片转化为 base64 limit: 1024 * 10 } }] }] } };
1.1. ID选择器
$(‘#app’)
1.2. 类选择器
$(‘.class’)
1.3. 元素选择器
$(‘h1’)
1.4. *
匹配所有元素
$(‘*’)
1.5. 并集选择器
2.1. 后代选择器
2.2. 子选择器
2.3. 同辈选择器
2.4. 同辈选择器
3.1. 基本过滤选择器
3.2. 内容过滤选择器
3.3. 可见性过滤选择器
3.4. 属性过滤选择器
3.5. 表单属性过滤选择器
object-fit CSS 属性指定可替换元素(例如:
<img>
或<video>
)的内容应该如何适应到其使用高度和宽度确定的框。
原图:
object-fit:fill
:被替换的内容正好填充元素的内容框。整个对象将完全填充此框。如果对象的宽高比与内容框不相匹配,那么该对象将被拉伸以适应内容框。
object-fit:contain
:被替换的内容将被缩放,以在填充元素的内容框时保持其宽高比。整个对象在填充盒子的同时保留其长宽比,因此如果宽高比与框的宽高比不匹配,该对象将被添加“黑边”。(长边全部显示)
object-fit:cover
:被替换的内容在保持其宽高比的同时填充元素的整个内容框。如果对象的宽高比与内容框不相匹配,该对象将被剪裁以适应内容框。(短边全部显示,短边会占满容器!)
object-fit:none
:被替换的内容将保持其原有的尺寸。(原本的分辨率显示)
object-fit:scale-down
:内容的尺寸与 none 或 contain 中的一个相同,取决于它们两个之间谁得到的对象尺寸会更小一些。
总结,fill拉伸填充,contain显示全整个图像,适应长边,cover适应短边可能会被裁剪,none显示原图像(像素比例不变,也不会被拉伸,但是可能会显示不全),scale-down取决于contain和none其中取得尺寸更小的那个属性(down!down!down!)
webpack 是一个用于现代 JavaScript 应用程序的「静态模块打包工具」。我们可以使用webpack管理模块。因为在webpack看来,项目中的所有资源皆为模块,通过分析模块间的依赖关系,在其内部构建出一个依赖图,最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件(图片、字体等),让我们的开发过程更加高效。
webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
在以上过程中,webpack会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用webpack提供的 API 改变webpack的运行结果。
简单说:
默认情况下,webpack只支持对js和json文件进行打包,但是像css、html、png等其他类型的文件,webpack则无能为力。因此,就需要配置相应的loader进行文件内容的解析转换。
常用的loader如下:
webpack中的plugin赋予其各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在webpack的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期。目的在于「解决 loader 无法实现的其他事」。
常用的plugin如下:
模块热替换(HMR - hot module replacement),又叫做热更新,在不需要刷新整个页面的同时更新模块,能够提升开发的效率和体验。热更新时只会局部刷新页面上发生了变化的模块,同时可以保留当前页面的状态,比如复选框的选中状态等。
热更新的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上webpack-dev-server与浏览器之间维护了一个websocket,当本地资源发生变化时,webpack-dev-server会向浏览器推送更新,并带上构建时的hash,让客户端与上一次资源进行对比。客户端对比出差异后会向webpack-dev-server发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向webpack-dev-server发起 jsonp 请求获取该chunk的增量更新。
后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader和vue-loader都是借助这些 API 实现热更新。
module.exports = {
plugin:[
new HtmlwebpackPlugin({
minify:{
minifyCSS: false, // 是否压缩css
collapseWhitespace: false, // 是否折叠空格
removeComments: true // 是否移除注释
}
})
]
}
图片压缩
配置image-webpack-loader
Tree Shaking
Tree Shaking是一个术语,在计算机中表示消除死代码,依赖于 ES Module 的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)。在webpack实现Tree shaking有两种方案:
usedExports:通过标记某些函数是否被使用,之后通过 Terser 来进行优化的
module.exports = {
...
optimization:{
usedExports
}
}
使用之后,没被用上的代码在webpack打包中会加入unused harmony export mul注释,用来告知Terser在优化时,可以删除掉这段代码。
sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用
sideEffects用于告知webpack compiler哪些模块时有副作用,配置方法是在package.json中设置sideEffects属性。如果sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports。如果有些文件需要保留,可以设置为数组的形式,如:
"sideEffecis":[
"./src/util/format.js",
"*.css" // 所有的css文件
]
>=
3.0.0interface User {
name: string
age: number
}
interface SetUser {
(name: string, age: number): void;
}
type User = {
name: string
age: number
};
type SetUser = (name: string, age: number)=> void;
interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 extends interface 。 虽然效果差不多,但是两者语法不同。
// interface extends interface interface Name { name: string; } interface User extends Name { age: number; } // type extends type type Name = { name: string; } type User = Name & { age: number }; // interface extends type type Name = { name: string; } interface User extends Name { age: number; } // type extends interface interface Name { name: string; } type User = Name & { age: number; }
// 基本类型别名 type Name = string // 联合类型 interface Dog { wong(); } interface Cat { miao(); } type Pet = Dog | Cat // 具体定义数组每个位置的类型 type PetList = [Dog, Pet]
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
interface User { name: string age: number } interface User { sex: string } /* User 接口为 { name: string age: number sex: string } */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。