DNS预解析
使用缓存
no-cache: 不使用强缓存(但仍会使用协商缓存)。
no-store: 不使用缓存(不使用强缓存也不使用协商缓存),每次都向服务器发送资源请求。
private: 只允许客户端使用缓存,不允许其他代理服务器进行缓存。
public: 客户端和代理服务器都可缓存。
s-maxage: 与max-age类似,区别是s-maxage是设定代理服务器的缓存时间。
一般来说,浏览器会将较大的资源缓存到disk cache,而较小的资源则被缓存到memory cache里。内存缓存与磁盘缓存相比,访问速度要更快一些!
使用 CDN(内容分发网络)
压缩响应
使用多个域名
避免图片src为空
Webkit 渲染引擎渲染流程:
1. 处理 HTML 并构建 DOM 树
2. 处理 CSS 构建 CSS 规则树(CSSOM)
3. DOM Tree 和 CSSOM Tree 合成一棵渲染树 Render Tree。
4. 根据渲染树来布局,计算每个节点的位置
5. 调用 GPU 绘制,合成图层,显示在屏幕上
避免css阻塞
降低css选择器的复杂度
浏览器读取选择器,遵循的原则是从选择器的右边到左边读取。
避免使用CSS 表达式
避免js阻塞
使用外链式的js和css
使用字体图标 iconfont 代替图片图标
首屏加载优化
减少重绘和回流
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// console.log('元素进入视口');
} else {
// console.log('元素离开视口');
}
});
});
// 添加监听,targetElement监听的目标元素
observer.observe(targetElement);
// 要取消观察的目标
IntersectionObserver.unobserve(targetElement);
include: path.resolve(__dirname, "./src"),
module: {
rules: [
{
test: /.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
}
},
{
loader: 'image-webpack-loader',
options: {
// 压缩 jpeg 的配置
mozjpeg: {
progressive: true,
quality: 65
},
// 使用 imagemin**-optipng 压缩 png,enable: false 为关闭
optipng: {
enabled: false,
},
// 使用 imagemin-pngquant 压缩 png
pngquant: {
quality: '65-90',
speed: 4
},
// 压缩 gif 的配置
gifsicle: {
interlaced: false,
},
// 开启 webp,会把 jpg 和 png 图片压缩为 webp 格式
webp: {
quality: 75
}
}
}
]
},
]
}
module.exports = {
...
optimization:{
splitChunks:{
chunks:"all"
}
}
}
配置如下:
Chunks,对同步代码还是异步代码进行处理
minSize: 拆分包的大小, 至少为minSize,如何包的大小不超过minSize,这个包不会拆分
maxSize: 将大于maxSize的包,拆分为不小于minSize的包
minChunks:被引入的次数,默认是1
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'happypack/loader',
options: { id: 'js' }
}
}
]
},
plugins: [
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
loaders: ['babel-loader']
})
]
};
- thread-loader
他们三个的应用场景都是缓存结果,当依赖值没有改变时避免不必要的计算或者渲染。
Web Worker 为 JavaScript 创造了多线程环境,允许 JS 主线程创建 Worker 子线程,将一些任务分配给后者运行。这样做就能充分发挥多核 CPU 主机的优势,让两个线程并行执行
1. 独立线程:每个 Web Worker 运行在自己的线程中,拥有独立的 JavaScript 执行环境,不会阻塞主线程。
2. 消息传递:主线程和 Web Worker 之间通过消息传递进行通信。主线程可以向 Web Worker 发送消息,Web Worker 可以向 主线程发送消息。这种通信是异步的,不会阻塞任何线程。
3. 无 DOM 访问:Web Workers 无法直接访问主线程中的 DOM 元素,因为它们在不同的上下文中运行。
使用场景:
在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅, Worker 线程会在计算任务完成时,通过特定的通信方式将结果返回给主线程。
注意: Worker 子线程是浏览器开的,完全受主线程控制。即 Web Worker 是浏览器提供的,JS 并不提供,JS 还是单线程的
// 在主线程和myworker内都是通过postMessage()方法发送数据,监听message事件接收数据
// 主线程内
var myWorker = new Worker('work.js');
myWorker.onmessage = function (event) { // 接收来自子线程内部的数据
console.log('Received message ' + event.data);
doSomething();
}
const dataToProcess = [1, 2, 3, 4, 5];
myWorker.postMessage(dataToProcess); // 向`myworker`内部发送数据
// myWorker文件内容,子线程
addEventListener('message', function (e) { // 接收来自主线程数据
const result = processData(e.data); // 调用方法,移交处理数据的逻辑
postMessage(result); // 将处理的好结果返回给主线程
}, false); // false指定事件句柄在冒泡阶段执行
function processData(data) {
// 处理数据的逻辑
return data.map(item => item * 2);
}
为什么选择微前端
为什么不用iframe
隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。
选择qiankun的理由
成熟稳定:qiankun 是由 Ant Group(蚂蚁金服)团队开发和维护的微前端框架,已经在蚂蚁金服内部和众多大型项目中广泛应用并经过验证。它经过了长期的发展和迭代,具有较高的稳定性和可靠性。
功能丰富:qiankun 提供了完整的微前端解决方案,包括应用的注册、加载、通信、生命周期管理等功能。它具有独立运行、集成部署、按需加载等特性,可以满足复杂的微前端架构需求。
灵活性:qiankun 支持多种前端框架(如 React、Vue、Angular)的应用集成,不限制开发团队使用的具体技术栈。它提供了统一的应用接入和管理方式,使得不同团队开发的应用可以无缝集成,实现共享组件和状态管理等。
性能优化:qiankun 在应用加载和通信方面进行了优化,采用了基于浏览器标准的沙箱隔离机制,避免了应用之间的冲突和影响。它支持应用的按需加载,减少了初始加载时的资源开销,提高了整体性能和用户体验。
社区支持和生态系统:qiankun 拥有活跃的社区和广泛的生态系统,有大量的文档、教程和示例可供参考。它也得到了开源社区的认可和贡献,可以获得及时的技术支持和问题解答。
qiankun的原理
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'vue app',
entry: 'http://localhost:27272',
contrainer: '#yourContainer',
activeRule: '/yourActiveRule'
}
])
// 通过 onGlobalStateChange 来通讯
import { initGlobalState } from 'qiankun';
const state = {
baiduinit: window,
abc: 456
}
// 初始化通信池
const actions = initGlobalState(state);
// 监听通讯池的变化
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
});
当我们在主应用中引入了多个子应用时,子应用的样式会互相影响,这是因为子应用的样式是全局的,而 qiankun 默认情况下并没有对子应用的样式进行隔离
import { start, loadMicroApp } from 'qiankun'
// 方法1:启动添加experimentalStyleIsolation
start({
sandbox: {
experimentalStyleIsolation: true,
}
})
// 方法2:手动添加
loadMicroApp('reactApp', {
sandbox: {
experimentalStyleIsolation: true,
}
})
2. 静态资源引用问题
静态资源再子应用中相对路径引用,主应用下可能无法读取到
3. 通信数据初始化覆盖问题
使用initGlobalState初始化定义一级属性,已经定义过的数据,注意不要书写为如下
// initGlobalState() 的写法会覆盖掉已经初始化定义的一级属性
const { setGlobalState } = initGlobalState()
const { setGlobalState } = initGlobalState({
demoData: '',
demo2Data: ''
})
window.$setGlobalState = setGlobalState
// 需要通信更新更新demoData处调用示例:
window.$setGlobalState({
demoData: 'this is demo'
})
问题:配置的子应用报跨域错误
在Ts中的常用类型细分两类:JS已有的类型和TS新增的类型
JS已有的类型:number、string、boolean、null、 undefinded、symbol,对象类型Object(包括数组、对象、函数等对象)
TS新增类型
函数类型参数和返回值
// 普通函数
function fn(num: number, name: string): string {
return name + num;
}
// 箭头函数
const fn = (num: number, name: string): string {
return name + num;
}
type addFn = (num1: number, num2: number) => number;
const add:addFn = (num1, num2) => {
return num1 + num2;
}
函数类型void和undefined
// void:没有返回值
const fn = (num1: number, num2: number):void => {
return;
}
// 返回的是undefined
const fn1 = (num1: number, num2: number):undefined => {
return undefined;
}
对象类型
```
let person: { name: string } = {
name: ‘zs’
}
// 使用type
type Myobj = {
name: string;
age: number;
sex?: string;
sayHi(name: string): void; // 普通函数
sayHello: (age: number) =>vold; // 箭头函数
}
let obj: Myobj = {
name: 'zs',
age: 18,
sayHi(name) {},
sayHello: (age) => {
// return;
}
}
```
对象类型 interface(接口) 与 type(别名) (主流推荐type)
interface只能为对象指定类型,而type可以为任意类型指定别名
interface Person {
name: string;
age: number;
sayHi():void;
}
type Person1 = {
name: string,
age: number,
sayHi: () => void,
}
// type联合类型
type NumberStr = number | string;
// type组合类型
type Person2 = Person1 & { sayHello: (str: string) => void; }
是一类明确的元素类型和个数的数组
let tupArr: [number, number] = [1,2]
let str = 'zs';
enum Sex {
Male = 'mael',
Female = 'female'
}
const changeSex = (dir: Sex) => {};
changeSex(Sex.Male)
// 假设一个页面中的a标签,应为用getElementById()方法获取能获取任何HTML标签,所以该方法返回类型时HTMLElement,但是这个返回值太宽泛,这是需要使用 as 来添加更加具体的类型,如HTMLAnchorElment
const link = doucment.getElementById('#js_link') as HTMLAnchorElement;
let p = { x: 1, y: 2};
function form(point: typeof p) {}
form(p)
function fn(value: Type): Type {return value;};
function fn2(value: T): T{return value;};
能访问参数中的某个定义类型的属性,必须有约束的类型参数
interface MyName {
name: string
}
function fn3(value: Type): Type {
return value.name;
}
fn3({ name: 'zs' });
fn3({name: 'ls', age: 18});
// 1. Partial 来构造一个类型,将所有属性设置为可选
type Props = {
id: string,
children: number[]
}
// 设置为可选
type Part = Partial;
let p1:Props = {id: '1'};
let p2.Part = {age: 18};
// 2. Readonly 将所有属性设置只读属性
type Props1 = {
id: string,
readonly children: number[]
}
let p3:Props1 = {id: '2', children: [1]};
p3.id = '4';
type ReadonlyProps = Readonly;
//全部为只读属性
let p4:ReadonlyProps = {id: '3', children: [2]};
p4.id = '5'; // 报错
// Prick 自定义选取类型数据,参数1选择谁的属性,参数2选择哪个属性
interface Props3 {
id: string,
title: string,
children: number[]
}
type PrickProps = Prick;
// 含有id 或者title
let p5:prickProps = {
id: '6'
}