JavaScript
1、数组和对象的操作方法
老是用,但是突然问。一下子有点懵...就是打你一个措手不及
数组
- 添加
- push:在数组末尾添加一个或多个元素
- unshift:在数组开始添加一个或多个元素
- 删除
- shift:移除数组的第一个元素
- pop:移除数组的最后一个元素
- splice:可以删除、插入或替换数组中的元素
- 数组遍历
- forEach
- for
- 数组转换
- map
- filter
- reduce
- findIndex
- find
- fill
- concat
- indexOf
- lastIndexOf
- at
- every
- some
- join
- reverse
slice 与 splice 的区别
- slice:提取数组的某些部分,不会改变原来的数组
slice()
slice(start)
slice(start, end)
// 定义一个数组
const fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
// 使用 slice() 方法
const slicedFruits1 = fruits.slice(1, 3); // ['banana', 'cherry']
const slicedFruits2 = fruits.slice(-2); // ['date','elderberry'] Interview 1
- splice:删除、插入,会改变原来的数组
splice(start, deleteCount, item1, item2, ...);
const fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
const removedFruits = fruits.splice(1, 2, 'grape', 'fig');
console.log('splice() 移除的元素:', removedFruits); // ['apple', 'banana']
console.log('splice() 操作后的原数组:', fruits); // [ 'apple', 'grape', 'fig', 'date', 'elderberry' ]
- 用 splice 写个 react 版本的表格删除
import React, { useState } from 'react';
const TableDeleteRow = () => {
const [rows, setRows] = useState([
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 25 },
{ id: 3, name: '王五', age: 30 }
]);
const handleDelete = (index) => {
// Interview 4
// 为啥需要拷贝?
// React 数据不可变原则,直接修改会导致React 无法检测数据的改变,因为引用并没有变 -> Interview 5
const newRows = [...rows];
newRows.splice(index, 1);
setRows(newRows);
};
return (
<div>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{rows.map((row, index) => (
<tr key={row.id}>
<td>{row.name}</td>
<td>{row.age}</td>
<td>
<button onClick={() => handleDelete(index)}>删除</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default TableDeleteRow;
对象
- 遍历属性
Object.keys()、Object.values()和Object.entries()- for...in
- 删除属性 :delete
- 创建: Object.create
2、引用类型与基本数据类型
基本类型
基本类型:null、undefined、number、boolean、string、symbol、bigint
存储方式:存在栈内存里面。每个变量都有自己独立的存储空间,存储的是实际的值
引用类型
Object、Array、Function、Date
类型:引用类型的值存在堆内存中,而变量栈内存里面存放的是指向堆内存中对象的引用地址。这意味着多个变量可以指向同一个对象
举例:
let m = { a: 10, b: 20 }; let n = m; n.a = 15;
复制的是引用地址,他们指向的是同一个对象,所以上面的例子当中 m.a = 15
类型判断
- 基本类型: 可以用 typeof(Null、Array、Date会返回 object)
- 引用类型: instanceof 可以判断(基本类型无效)引用类型
- Object.prototype.toString.call()
- Array.isArray 专门用来判断数组
3、深拷贝与浅拷贝
浅拷贝
拷贝对象,只会拷贝对象的引用,因此他们引用的是同一个对象
实现方法:
1 Object.assign()
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
2、扩展运算符...
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
深拷贝
1、 JSON.parse() 和 JSON.stringify()
这种方式不能拷贝 undefind、Symbol
2、手写一个简易版的deepClone 方法
function deepClone(obj) {
if (typeof obj !== "object" || obj == null) {
return obj;
}
let clone;
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
clone[i] = deepClone(obj[i]);
}
} else {
clone = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = obj[key];
}
}
}
return clone;
}
4、const、let、var 的区别
var
各种不好,变量提升,可重新赋值、重复声明,作用域是函数作用域
// var 的函数作用域
function testVar() {
if (true) {
var x = 10;
}
console.log(x); // 输出 10
}
testVar();
let
块级作用域,块级作用域由 {} 界定,var 的那些问题,let 通通没有
const
const 声明的常量,一旦赋值就不能改变,但是 const 如果声明的是对象或者数组, 内部的属性可以改变(这是因为 const 声明的是变量的引用不变,而不是变量的指向的值不变) InterView 5
- 怎么让 const 声明的对象或者数组也不可变呢
使用 Object.freeze(), 但也只是浅浅的冻结,如果是引用类型,也是可以更改
const obj = {
a: 1,
b: {
c: 2,
d: 3,
},
}
const objFreeze = Object.freeze(obj)
obj.a = 10;
obj.b.c = 20;
console.log('obj:', obj);
// 输出:obj: { a: 1, b: { c: 20, d: 3 } }
- 解决办法:可以递归冻结和使用三方库
5、防抖节流
- 防抖 是在延迟一段后时间执行,如果这段时间内重新触发事件,则重新计时,适用于输入框、窗口改变
function debounce(fn,delay){
let timer = null;
return function(...args){
if(timer) clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this,args);
},delay)
}
}
input.addEventListener('keyup',(e)=>debouce(search,500))
- 节流
是每隔一定的时间执行,无论触发多少次,目标函数只会在间隔内执行一次,适用于滚动监听
funtion throttle(fn,interval){ let lastTime = 0; return function(...args){ const now = Date.now(); if(now -lastTime >= interval){ fn.apply(this,args); lastTime = now; } } } window.addEventListener('scroll', throttle(logScroll,1000))
6、 0.1 + 0.2 != 0.3 的原理
浮点数两种格式: 单精度浮点数(32 位)和 双精度浮点数(64 位)
浮点数组成部分:
- 符号位:0 表示正数,1 表示负数
- 指数位:表示浮点数的指数部分(单精度指数位占 8 位,双精度占 11 位),
- 尾数位:用于表示浮点数的小数部分,在单精度占 23 位,双精度占 52 位
原理: 在计算机当中,用二进制来表示数字。0.1和 0.2 无法被精确表示,而是以无限循环的二进制小数形式存储。因为存储的位数有限,他们被近似存储
解决办法:
- 使用 toFixed
- 先扩大后缩小
- 使用 BigInt
- 使用第三方库(如 decimal.js,big.js)
6、事件循环
js 本身是单线程语言,在同一时间只能只能一个任务,其目的是为了防止多线程修改共享数据。但是在实际工作中,会出现一些耗时比较久的任务,如果这些都同步执行,那将会是灾难,所以 js 引入异步机制来解决这个问题
- 执行栈与任务队列
执行栈:也称为调用栈,是一种存储函数调用关系的数据结构。当函数被调用时,它会被压入执行栈的顶部,当函数执行完毕后,会从执行栈中弹出。JavaScript 引擎会按照执行栈中函数的顺序依次执行它们
任务队列:用于存储异步任务。当一个异步任务被触发(如定时器到期、网络请求完成),它不会立即执行,而是会被放入任务队列中等待执行。任务队列可以分为宏任务队列和微任务队列
-
宏任务和微任务
- 宏任务:也称为 Task,常见的宏任务有
setTimeout、setInterval、setImmediate(仅在 Node.js 环境中)、I/O操作、UI渲染等。
- 宏任务:也称为 Task,常见的宏任务有
-
微任务:也称为 Jobs,常见的微任务有
Promise的回调函数、MutationObserver、process.nextTick(仅在 Node.js 环境中)等 -
事件循环:js 在执行任务的时候,会优先执行同步任务,会推入调用栈并且理解执行,执行完毕,调用栈会被弹出。当碰到异步任务的时候,js 不会立即执行,而是将这些任务交给运行时的环境(如浏览器的 Web API 货 Node.js 的 Libuv)。任务完成后,回调函数放入任务队列,任务队列分为宏任务和微任务。当调用栈被清空的时候,会从任务队列去拿,优先执行微任务
7、 ### 浏览器的两种路由
哈希路由
通过浏览器的hashChange 检测 # 后面的变化,兼容性好,对 SEO 不怎么优化,服务器不需要做什么配置
历史路由
通过 History API 来实现的,后退与前进都是通过 pushState、popState、replaceState 来实现的,对 SEO 好,需要服务器配置
server {
listen 80;
server_name your_domain.com;
root /var/www/html; index index.html index.htm;
location / {
try_files $uri $uri/ /index.html;
}
}
7、手写 flattenArray
function flatteryArray(arr) {
return arr.reduce((acc, val) => {
if (Array.isArray(val)) {
acc.push(...flatteryArray(val));
} else {
acc.push(val);
}
return acc;
}, []);
}
8、普通函数与箭头函数的区别
- 箭头函数: this 指向在定义的时候就确定了,指向外层作用域的 this,全局模式下,this 指向不能被改变,严格模式 this 是 undefined。没有 arguments,但是有剩余参数
- 普通函数:this 指向能够被改变(通过 apply、bind、call),this 指向取决于调用方式,比如通过对象调用,this 指向该对象;通过构造函数,this 指向创建的对象实例等等
一个经典问题:普通函数 setTimeout
const outerObj = {
outerProp: 'I am outer property',
middleObj: {
innerObj: {
// 普通函数
printOuterProp: function() {
// 使用闭包来保存最外层的this
const self = this;
return function() {
// 通过闭包变量self来访问最外层对象的属性
console.log(self.outerObj.outerProp);
};
}()
}
}
};
// 调用最内层方法
outerObj.middleObj.innerObj.printOuterProp;
const outerObj2 = {
outerProp: 'I am another outer property',
middleObj: {
innerObj: {
// 箭头函数
printOuterProp: () => {
console.log(outerObj2.outerProp);
}
}
}
};
// 调用最内层方法
outerObj2.middleObj.innerObj.printOuterProp();
9、如何判断对象是不是空对象
谁会这么判断
const obj = {};
// 方法1:使用 Object.keys()
if (Object.keys(obj).length === 0) {
console.log("对象为空");
}
// 方法2:使用 JSON.stringify()
if (JSON.stringify(obj) === '{}') {
console.log("对象为空");
}
直接用 lodash 的 isEmpty 可以判断,因为它可以
- 空对象
{}→true - 空数组
[]→true - 空字符串
""→true null或undefined→trueMap或Set为空 →true- 类数组对象(如
arguments)为空 →true - 数字、布尔值、函数等非集合类型 →
true(因为它们没有可枚举的属性)
源码分析(简化版)
function isEmpty(value) {
// 处理 null 和 undefined
if (value == null) {
return true;
}
// 处理数组、字符串、类数组对象(如 arguments)
if (Array.isArray(value) || typeof value === 'string' || isArrayLike(value)) {
return value.length === 0;
}
// 处理 Map 和 Set
if (value instanceof Map || value instanceof Set) {
return value.size === 0;
}
// 处理普通对象(检查可枚举的自身属性)
if (typeof value === 'object') {
return Object.keys(value).length === 0;
}
// 其他情况(如数字、布尔值、函数等)
return true;
}
10、 浏览器页面渲染
11、如何避免js 阻塞渲染
- 按需加载js脚本(Code Splitting)
- 使用 defer 和 async 属性
- defer 属性在 HTML DOM 完成后再执行,适用于依赖 DOM 或有执行顺序有要求的脚本
- 添加 async 属性,脚本会在下载后立即执行,不会阻塞 HTML 解析,适用于独立脚本,如统计代码
- 将脚本放在 标签之前,这样可以让 DOM 先渲染
- 使用 requestIdleCallback 或者 requestAnimationFrame
- requestIdleCallback: 在浏览器空闲的时候执行低优先级任务
- requestAnimationFrame:在下一帧渲染前执行动画相关任务
12、最近使用过的大模型
- Claude Sonnet's 3.7
- DeepSeek
- OpenAI
13、如何保证ws在什么情况下不会断联,客户端如何保证高可用,如何保证消息不丢失
- 服务器或网络问题
- 客户端页面关闭、
- 握手失败
- 协议违规
- 连接超时
- 并发连接数的限制
14、ws支持那些类型的消息,大消息如何优化
支持的类型: 文本、二进制(音频、图片、视频等)
如何优化: 消息分帧、压缩、异步处理、缓存策略
15、 APP选型如何对 flutter、rn进行考量,各自能解决什么样的业务场景和技术场景
| 特性 | Flutter | Reactive Native |
|---|---|---|
| 开发语言 | Dart | JS/TS |
| 性能 | 接近原生(无 Brigde) | 可能有 Bridge 瓶颈 |
| UI 渲染 | 自带 Skia 引擎 | 依赖原生组件(通过Bridge 通信) |
| 特性 | 跨平台、声明式 widget 编程 | 熟悉 ts/js |
16、h5 离线包的实现机制,需要注意哪些问题
分模块打包
控制业务模块的大小,按需加载
差异化更新
精准控制离线的资源,对于变动频繁的单独管理
监控体系
降级策略
离线包加载失败,有没有完善的网络回退机制
测试覆盖
关注弱网环境
虚拟 DOM 的优势
- 跨平台
- 提高性能
CSS
1、flex:1 的含义
经典面试题了,代表着 flex: 1 1 0%, 是 flex-grow、flex-shrink、flex-basis 的 缩写。 埋个彩蛋: flex:2 代表啥意思
- flex-grow:定义了剩余空间中扩展的能力,为 1 表示均分空间。假设有个项目为 2 表示,其他项目为 1,表示前者的空间比其他的多一倍。 为 0 表示不扩展(默认值)。
<style>
.container {
display: flex;
border: 2px dashed crimson;
width: 300px;
}
.container .item {
border: 2px solid deepskyblue;
}
.container .grow {
flex-grow: 2;
width: 400px;
}
</style>
</head>
<body>
<div class="container">
<div class="item grow">南蓝</div>
<div class="item">南蓝</div>
<div class="item">南蓝</div>
<div class="item">南蓝</div>
<div class="item">南蓝</div>
</div>
</body>
- flex-shrink:定义了空间不足的时候如何收缩,默认值为 1 。为 1 表示等比例收缩,为 0 表示不收缩
<style>
.container {
width: 300px;
display: flex;
border: 2px dashed crimson;
}
.container .item {
flex-shrink: 1;
width: 100px;
border: 2px solid deepskyblue;
}
.container .shrink {
flex-shrink: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="item shrink">南蓝</div>
<div class="item">南蓝</div>
<div class="item">南蓝</div>
<div class="item">南蓝</div>
<div class="item">南蓝</div>
</div>
</body>
flex-basis: 0,定义了项目占据的主轴空间大小,默认值是 auto
flex-none
flex:0 0 auto
表示元素尺寸不会收缩也不会扩展,flex-basis: auto 表示固定尺寸由内容决定,元素的内容不会换行
适用场景: 不换行,小范围的内容上面 Interview 2
<style>
.container {
display: flex;
padding: 0.5rem;
border: 1px solid lightgray;
background-color: #fff;
}
img {
width: 3rem;
height: 3rem;
margin-right: 0.5rem;
}
button {
align-self: center;
padding: 5px;
margin-left: 0.5rem;
}
</style>
<title>Flex Auto Navbar</title>
</head>
<body>
<div class="container">
<img src="1.jpg" />
<p>南兰兰兰纳啦啦啦电饭锅黄金季节好过分的</p>
<button>按钮</button>
</div>
</body>
哎呀,妈呀,以前遇到这种问题,直接按钮写死宽度,但是如果文字一旦过多,比如说英文比中文多,那么按钮文字会换行,其实一般不能换。 设置 flex:none 就好了
<button class="flex-none">按钮</button>
flex-0
flex: 0 1 0%
flex-shrink 为 1,表示会收缩,但是不会扩展,元素内容会换行。
flex-initial
0 1 auto
flex-auto
flex: 1 1 auto
这个属性和 flex:1 非常相似,请看图片的区别
适用的场景:基于内容动态布局。这个时候用 flex:1 就很丑了 Interview 3
总结
| 简写 | 详细 | 适用场景 |
|---|---|---|
flex:none | flex: 0 0 auto; | 适用于不换行的内容固定或者较少的小控件元素上,如按钮 |
flex:0 | flex: 0 1 0%; | 很少用 |
flex:initial(默认,相当于 dispaly: flex) | flex: 0 1 auto; | 默认行为 |
flex:1 | flex: 1 1 0%; | 等分布局 |
flex:auto | flex: 1 1 auto; | 基于内容动态适配的布局 |
参考:
2、BFC
如何触发 BFC
1、 根元素:<html> 天然就是一个 BFC
2、 浮动元素: float 设置为left 或 right
3、 绝对定位: position 设置为absolute 或 fixed
4、 display 属性:
- display:flow-root
- display: inline-block、 table、 flex、 grid
5、 overflow 属性:
- overflow:hidden、scroll、 auto
BFC 特性
1、外边距折叠问题: 在同一个 BFC 中,上下相邻的块级元素外边距会折叠;不同的BFC 不会折叠
<div style="margin-bottom: 20px; background: lightgreen;">块1</div>
<div style="margin-top: 30px; background: lightblue;">块2</div>
他们属于同一个 BFC,所以内边距会折叠。解决方式,创建一个 BFC,还记得创建的条件吗 ,overflow: hidden、float、display: flow-root
2、内容元素垂直排列
3、BFC 容器会自动包含其内部浮动元素
4、BFC 内部的布局不会影响外部元素,外部的布局也不会干扰 BFC 内部
应用场景
性能优化
清除浮动、防止外边距折叠、布局隔离(防止浮动或者定位元素干扰其他)、多列布局
1、性能优化指标
FCP(First Contentful Paint): 首次内容绘制
- 定义: 首次看到内容的加载时间
- 优化手段:减少服务器资源加载时间、比如通过分包、压缩资源等方式
LCP(Largest Contentful Paint):最大内容绘制
- 定义: 看到最大文本或者图片的加载时间
- 优化手段:图片懒加载,使用 Webp 的图片加载方式等,加载时间<=2.5s
FID(First Input Delay): 首次输入延迟
- 定义: 用户首次与页面交互,浏览器响应的时间(比如点击、输入等)
- 优化手段: 减少 js 主线程的交互时间,避免长任务,加载时间<=100ms
累计布局偏移 (Cumulative Layout Shift, CLS)
- 定义: 页面加载过程中因为偏移导致视觉稳定的得分
- 优化手段: 为图片设定尺寸,避免动态插入,加载时间<=0.1
时间到交互 (Time to Interactive, TTI)
- 定义:页面加载到完全可以交互(主线程空闲且能响应用户输入的时间)
- 优化建议: 减少三方脚本、异步加载非关键资源
总阻塞时间 (Total Blocking Time, TBT)
- 定义:页面加载过程中,主线程被长任务阻塞的时间
- 优化手段:拆分长任务、使用 web worker 处理复杂计算
速度指数 (Speed Index)
- 定义:页面内容在可视区域内逐渐显示的平均时间
- 优化建议: 优先加载视口内资源、优化渲染路径
2、 React 性能优化
代码层面
- 组件拆分:把大型组件拆分成多个小的、可复用的组件。这样不但能提升代码的可读性与可维护性,还可以借助 React 的按需渲染特性来减少不必要的渲染。
- 使用 React.memo:对于纯函数组件,可使用
React.memo对其进行包裹,以此来阻止组件在 props 未发生变化时进行重新渲染。
说是这么说,但在实际工作中也不是每次都在纯函数组件中包裹了 React.memo
import React from 'react';
const ChildComponent = React.memo(({ text }) => {
console.log('ChildComponent rendered');
return <div>{text}</div>;
});
export default ChildComponent;
- 避免内联函数:在 JSX 里使用内联函数会在每次渲染时都创建新的函数实例,这可能会致使子组件不必要的重新渲染。可以把内联函数提取到组件外部或者使用
useCallback来对其进行优化。 - 使用 useCallback 和 useMemo:
useCallback用于缓存函数,而useMemo用于缓存计算结果。在需要传递函数给子组件或者进行复杂计算时,使用它们能够避免不必要的计算和渲染。
- useCallback
// ChildComponent
import React from 'react';
const ChildComponent = ({ onClick }) => {
console.log('ChildComponent rendered');
return <button onClick={onClick}>Increment</button>;
};
export default ChildComponent;
// AppWithUseCallback
import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';
const AppWithUseCallback = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<h1>With useCallback</h1>
<h2>Count: {count}</h2>
<ChildComponent onClick={handleClick} />
</div>
);
};
export default AppWithUseCallback;
// AppWithoutUseCallback
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
const AppWithoutUseCallback = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<h1>Without useCallback</h1>
<h2>Count: {count}</h2>
<ChildComponent onClick={handleClick} />
</div>
);
};
export default AppWithoutUseCallback;
没加 useCallback 之后,handleClick 会重新创建会被重新渲染,所以 ChildComponent 接收到变化,会重新渲染。加了 useCallback之后,缓存了 handleClick 函数,count 变化,handleClick 才会变化
- useMemo
import React, { useState, useMemo } from 'react';
const App = () => {
const [count, setCount] = useState(0);
// 使用 useMemo 缓存计算结果
const doubleCount = useMemo(() => {
console.log('Calculating double count...');
return count * 2;
}, [count]);
return (
<div>
<h1>Count: {count}</h1>
<h2>Double Count: {doubleCount}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default App;
在 React 19 中,你不可以使用 React.memo、 useCallback、useMemo 来做优化,因为 React compiler 已经帮你做了这些事情。具体可以参考 React Compiler
- 减少不必要的状态:仅在状态确实需要改变 UI 时才使用状态。对于一些不需要影响 UI 的数据,可以使用普通的变量来进行存储。
- 使用 React.lazy 和 Suspense:针对大型组件(echarts、富文本等)或者路由组件,可使用
React.lazy进行懒加载,并且使用Suspense来处理加载状态。这样能够提升应用的初始加载速度。
// (router 使用的是 V6, React 使用的 18)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { Suspense } from 'react';
// 懒加载组件
const Home = React.lazy(() => import('./Home'));
const About = React.lazy(() => import('./About'));
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
性能层面
- 优化图片资源:对图片进行压缩和优化,使用合适的图片格式(如 WebP),并且使用懒加载技术来减少初始加载时的资源消耗。
- 使用 CDN:对于第三方库和静态资源,可使用 CDN 来加速加载。
网络层面
- 减少 API 请求:合并多个 API 请求,避免不必要的重复请求。可以使用缓存策略来减少对相同数据的重复请求。
- 优化 API 响应:在服务器端对数据进行分页、排序和过滤,减少返回的数据量。
打包层面
- 代码分割:使用 Webpack 等打包工具进行代码分割,将应用拆分成多个小的 bundle,实现按需加载
Webpack使用代码分割主要是两个思路,一个 es 6 的 dynamic import 和使用 splitChunks
// dynamic import 的 webpack 配置
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
chunkFilename: '[name].[contenthash].chunk.js'
}
};
optimization.splitChunks.chunks: 'all' 表示对所有类型的块(同步和异步)进行代码分割,将公共模块提取到单独的文件中
// splitChunks
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
- 压缩代码:在打包时对代码进行压缩,去除不必要的空格和注释,减少文件大小。
样式层面
- 避免内联样式:内联样式会增加 DOM 的大小,并且不利于样式的复用和维护。尽量使用 CSS 类来定义样式。
- 使用 CSS 模块化:使用 CSS Modules 或者 styled-components 等技术来避免全局样式冲突。
其他层面
- 优化事件处理:避免在事件处理函数中进行复杂的计算或者阻塞操作,可以使用异步操作来处理耗时任务。
- 使用 React 上下文(Context) :当多个组件需要共享数据时,使用 React 上下文可以避免通过 props 层层传递数据。
框架
Vue 2.0 与 Vue 3.0 的区别对比
Vue 2 与 Vue 3 的区别
- Vue2:使用
Object.defineProperty()来实现响应式、选项式 API- 选项式 API: 以“组件实例”的概念为中心 (即上述例子中的
this),对于有面向对象语言背景的用户来说,这通常与基于类的心智模型更为一致
- 选项式 API: 以“组件实例”的概念为中心 (即上述例子中的
- Vue3: 采用
Proxy对象来实现响应式,解决了Object.defineProperty()的局限性、组合式 API- 组合式 API:函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题
- 使用
ref()和reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。 - 组合式 API 并不是函数式编程。组合式 API 是以 Vue 中数据可变的、细粒度的响应性系统为基础的,而函数式编程通常强调数据不可变
- 与 React hooks 相比,有相同的逻辑组织能力
- 使用
- 声明周期
- Vue 2:有
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed等生命周期钩子 - Vue 3:保留了部分生命周期钩子,但名称有所改变,如
beforeDestroy改为beforeUnmount,destroyed改为unmounted
- Vue 2:有
- 组合式 API:函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题
网络
1、 如何取消请求
1、比如用户用户突然切换页面, AbortController