Commit ae5bc858 authored by liyan's avatar liyan
parents 8e477e08 71f8eeed
Pipeline #16131 canceled with stage
......@@ -3,8 +3,7 @@ title: Map和Object
date: 2022/4/26 10:00:00
tags:
- map
- object
- object
author: 张芳利
---
......@@ -14,6 +13,9 @@ author: 张芳利
## 描述
Map 对象存有键值对,其中的键可以是任何数据类型。<br />Map 对象记得键的原始插入顺序。<br />Map 对象具有表示映射大小的属性。
<a name="g2duN"></a>
<!-- more -->
## 创建
构造函数创建
```javascript
......
This diff is collapsed.
---
title: 性能优化---Preload&Prefetch
date: 2022/5/7 14:06:20
tags:
- 前端
- 盒子模型
- CSS
author: 刘磊
---
# 性能优化---Preload&Prefetch
在preload和prefetch之前,我们一般根据编码需求通过link或者script标签引入页面渲染和交互所依赖的js和css等;然后这些资源由浏览器决定优先级进行加载、解析、渲染等。
然而,有些情况我们需要某些依赖在浏览器进入渲染的主进程之前就希望被加载,但是实际资源加载的状况并不受我们控制,而是根据浏览器内部决定资源的优先级等状况进行加载的。即使我们将需要的资源按照希望的那样放在期望的位置,但是由于资源的加载和解析,尤其是js的加载、解析和编译是阻塞式的,因此往往还是达不到预期。或者,有些资源是我们后边需要的,我们希望在需要它的时候在进行解析和执行,并不想让它的加载影响其他资源的加载及首屏渲染时间。
preload和prefetch的出现为我们提供了可以更加细粒度地控制浏览器加载资源的方法。
```
<link ref='preload'>
<link ref='prefetch'>
```
#### preload特点
- preload加载的资源是在浏览器渲染机制之前进行处理的,并且不会阻塞onload事件;
- preload可以支持加载多种类型的资源,并且可以加载跨域资源;
- preload加载的js脚本其加载和执行的过程是分离的。即preload会预加载相应的脚本代码,待到需要时自行调用;
#### prefetch特点
- prefetch加载的资源可以获取非当前页面所需要的资源,并且将其放入缓存至少5分钟(无论资源是否可以缓存);并且,当页面跳转时,未完成的prefetch请求不会被中断;
#### preload和prefetch对比
- preload主要用于预加载当前页面需要的资源;而prefetch主要用于加载将来页面可能需要的资源;
- 不论资源是否可以缓存,prefecth会存储在net-stack cache中至少5分钟;
- preload需要使用as属性指定特定的资源类型以便浏览器为其分配一定的优先级,并能够正确加载资源;
## webpack中的perload和perfetch
问题:异步加载模块是Webpack官方所希望开发者实现的,而且异步加载也能提升首页加载速度,但是又会导致异步加载的那部分代码逻辑迟迟不能执行,可能导致用户的交互长时间没有响应。
所谓预取,就是提前获取将来可能会用到的资源。prefetch chunk会在父chunk加载完成之后进行加载。
那么该怎么做呢?我们要通过一种称作Magic Comment(魔法注释)的方式,让Webpack输出"resource hint(资源提示)",来告知浏览器。
```
document.getElementById('btn').onclick = function () {
import( /* webpackChunkName: 'base',webpackPrefetch:true*/ './base').then(({
mul
}) => {
console.log(mul(40, 3));
}).catch((err) => {
console.log(`base.js加载失败:${err}`);
});
}
```
---
title: TypeScript泛型
date: 2022/5/10 16:00:00
tags:
- 前端
- typescript
author: 杨靖
---
## 泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
我们可以用一个函数createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值:
```typescript
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
```
上例中,我们使用了之前提到过的数组泛型来定义返回值的类型。
这段代码编译不会报错,但是一个显而易见的缺陷是,它并没有准确的定义返回值的类型:
Array<any> 允许数组的每一项都为任意类型。但是我们预期的是,数组中每一项都应该是输入的 value 的类型。
这时候,泛型就派上用场了:
```typescript
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
```
上例中,我们在函数名后添加了 <T>,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array<T> 中即可使用了。
接着在调用的时候,可以指定它具体的类型为 string。当然,也可以不手动指定,而让类型推论自动推算出来:
```typescript
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
```
*定义泛型的时候,可以一次定义多个类型参数:*
```typescript
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
```
上例中,我们定义了一个 swap 函数,用来交换输入的元组。
**泛型约束**
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法:
```typescript
function loggingIdentity<T>(arg: T): T {
console.log(arg.length);
return arg;
}
// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.
```
上例中,因为泛型 T 不一定包含属性 length,所以编译的时候报错了,是一个错误的例子。
这时,我们可以对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。这就是泛型约束:
```typescript
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
```
上例中,我们使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。
此时如果调用 loggingIdentity 的时候,传入的 arg 不包含 length,那么在编译阶段就会报错了:
```typescript
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.
```
多个类型参数之间也可以互相约束:
```typescript
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = (<T>source)[id];
}
return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 });
```
上例中,我们使用了两个类型参数,其中要求 T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。
**泛型接口**
使用接口的方式来定义一个函数需要符合的形状:
```typescript
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
```
当然也可以使用含有泛型的接口来定义函数的形状:
```typescript
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
```
```typescript
interface CreateArrayFunc<T> {
(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
```
*使用泛型接口的时候,需要定义泛型的类型。*
**泛型类**
与泛型接口类似,泛型也可以用于类的类型定义中:
```typescript
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
```
**泛型参数的默认类型**
在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
```typescript
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
```
......@@ -4,7 +4,7 @@ date: 2022/4/22 17:30:00
tags:
- 前端
- typescript
author: 杨靖
author: 杨靖
---
## typescript的高级类型详解
......
## 路由懒加载
#### 什么是路由懒加载
路由懒加载也叫延迟加载,即在需要的时候进行加载,随用随载。在单页面应用中浏览器默认会将所有的资源全部下载下来,这可能会导致首屏加载的时间过长。路由懒加载可以做到点击对应的路由再加载对应的文件资源
#### 路由懒加载的三种写法 (推荐用法:es6的import)
1. component: resolve => require(['../pages/home], resolve)
1. component: import(/* webpackChunkName: 'ImportFuncDemo' */ '../pages/home')
1. component: resolve=>{require.ensure(,()=>{resolve(require('../pages/home'))})}
#### 路由懒加载使用中的疑惑
项目中已经使用了路由懒加载为什么首屏渲染仍然请求了所有路由的chunk;
解答:vue-cli webpack中默认配置了prefetch这个功能可以在浏览器闲置时加载网站未来可能会用到的资源;但
经过验证这个prefetch预请求阻塞了首屏的加载
#### 解决prefetch影响首页加载问题
- 在webpack配置中删除prefetch插件
```
configplugins.delete(prefetch)
```
- 在引用时配置
```
import(<!--webpackChunkName:'[...]',webpackPrefetch:false-->)
```
#### 思考:既然可以通过import异步加载路由组件,非路由组件是否可以通过异步加载的方式,从而优化首屏的加载速度?很多情况中复杂的弹窗等组件比页面组件的资源更大。
- 经过查阅文档发现VUE官方也提到了异步组件,并有相关的用法
- 通过异步引入的弹窗组件从主包分离出来,但是仍然在首页加载是就将弹窗组件加载出来。并未实现点击组件时
再去加载文件:
```
const wechatEnter = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import(
/*webpackChunkName:'wechatEnter',webpackPrefetch:true */ '@/views/index/components/wechatEnter.vue'
),
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000,
})
```
#### 利用webpack——SplitChunks优化
SplitChunks 插件来控制 Webpack 打包生成的 js 文件的内容的精髓在于:防止模块被重复打包,拆分过大的js
文件,合并零散的js文件;
##### 每个浏览器并发请求数量不同,分包需要考虑并发
由于并发数量的限制,对于splitchunks的分包并不是越多越好。需要具体情况具体分析,比如并发请求数量设置为4,目前有三个包一个包体积较大,那么这时候对大包拆成两个包加载速度肯定会有提升;如果本来有4个包,并发数量也是4,这时候拆个包出来如果包的体积也不大,第5个包请求的时间加上队列之间多出来的时间,综合下来谁快谁慢还不好说;最好的办法还是对静态资源做多域名请求;
#### 通过CDN引入第三方库,但是这个有风险,就是第三方库挂掉或者什么的,可以选择付费的cdn加速。或者下载到本地上传到自己的服务器,总共三套方案,可以同时使用,保证安全性。
#### 静态资源压缩,代码压缩,图片压缩;gzip压缩一定要开,并且服务端配合开一下,真的很重要。开启gzip后你会发现加载速度有质的提升,尤其是你的vendor.js和app.js体积过大的时候。
#### 代码层面的优化
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment