flex布局,怎么让文字竖起来?
- 设置
flex-direction: column
useContext和redux有什么区别?
- useContext 管理的状态更新,下面的子组件全部更新,即使子组件没有订阅它的状态;
- redux 也是管理全局状态,但是能够精准的让订阅的组件更新;
- useContext 会带来性能问题;
useRef.current能被useEffect监听到吗?
- 不会。
- useEffect 只会遍历那些已经注册的状态,或者是props的变更;
nodejs中的token怎么生成?
const jwt = require('jsonwebtoken');
const payload = { userId: 123, username: 'alice', jti: uuidv4()}; // 放用户信息
const secret = 'your_jwt_secret_key'; // 建议用环境变量管理
const options = {
expiresIn: '7d' // 例如 7 天过期
};
const token = jwt.sign(payload, secret, options);
后端使用nodejs,怎么主动销毁token?
- 比如当前用户重复登录,然后就可以将token放到redis的黑名单中,并且设置过期时间;
- 查找token可以根据 jti 去查找;
- 过期时间是根据剩余时间计算的;
- 或者用户点击“登出”,那么就在redis中销毁token;
GET请求中可以带body吗?
- 可以带,但是不能这么做;
- express中如果放了中间件强行解析,自己编写的逻辑也会把它忽略掉;
- GET是幂等性的。
- 幂等,就是依次执行不会引起其它的变化。这个只是查询数据;
- POST是非幂等性的。
- 新增数据会引起其它的变化。
判断300ms之内的点击是单击还是双击
import React, { useRef } from "react";
/**
* 1、区分一下单击和双击
* 2、300ms以内点击一次就是单击,300ms以内点击两次就是双击。
* 3、这两个状态互斥
*/
export default function index() {
const count = useRef(0);
const clock = useRef(null);
const handlerClickV3 = () => {
count.current = count.current + 1;
if (clock.current) {
clearTimeout(clock.current);
}
clock = setTimeout(() => {
// 用户点击一次
if (count.current === 1) {
console.log("单击");
} else if (count.current === 2) {
console.log("双击");
}
count.current = 0;
}, 300);
};
return (
<>
<div>
<button onClick={handlerClickV3}></button>
</div>
</>
);
}
箭头函数和普通函数
- 如果没有使用 严格模式
strict mode那么箭头函数的this指向undefined。如果是严格模式,那么箭头函数的this指向window或者服务器的nodejs - 普通函数的this指向Object。例如:
const obj = {
name: "张三",
doSometings: function(){}
}
- 这里的 doSomtings 的this指向obj
function name() {
'use strict';
function age() {
console.log(this); // undefined
}
age();
}
name();
- 这里的age函数已经是严格模式,那么this指向undefined。
- name函数指向windows或者nodejs环境。
箭头函数和普通函数的参数
- 箭头函数没有arguments,但是可以使用 ...rest 指代剩下的参数
- 普通函数有arguments
typescript 中的interface和type有什么区别?
- 能用interface的时候就用
- type主要是给类型另外起一个别名
- interface能够融合在一起
interface Person {
name: string
age: number
}
interface Person {
weight: number
}
- 如果代码块中定义了同名的interface,那么会自动把他们两个 结合起来
- type就没法结合合并类型
- 但是type可以做基本类型的联合类型
type unionTypeCustom = number | string
- type 能做到,但是 interface 做不到的
// 联合类型
type Status = 'idle' | 'loading' | 'success' | 'error';
// 元组
type Point = [number, number];
// 基于其他类型的别名
type ID = string | number;
// 使用泛型 + 条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
// 映射类型
type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };
- 在 继承 上的写法差异
// interface extends
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
// type 使用交叉类型
type Animal = { name: string };
type Dog = Animal & { breed: string };
- type 也可以约束变量的值
type Status = "1" | "2"
const status: Status = "3" // 这里会报错的
下面是AI给出的 映射类型 的案例:
这段代码:
type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };
是 TypeScript 中 映射类型(Mapped Types) 的一个经典例子。它的作用是:基于已有的类型 Person,创建一个新类型,其中所有属性都变成只读(readonly)。
🌟 核心概念拆解
{ readonly [K in keyof Person]: Person[K] }
keyof Person:获取Person所有属性名的联合类型(比如"name" | "age")。[K in keyof Person]:遍历这些属性名(类似 for-in 循环)。Person[K]:获取Person中属性K对应的类型(索引访问类型)。readonly:给每个新属性加上只读修饰符。- 整体:创建一个新对象类型,结构和
Person一样,但所有属性只读。
💡 这其实就是内置工具类型
Readonly<T>的实现原理!
✅ 具体代码示例
1. 定义原始类型
interface Person {
name: string;
age: number;
email: string;
}
2. 使用映射类型创建只读版本
type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };
等价于手动写:
type ReadonlyPerson = {
readonly name: string;
readonly age: number;
readonly email: string;
};
3. 使用效果
const person: ReadonlyPerson = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
// 尝试修改属性 → ❌ 编译错误!
person.age = 31; // Cannot assign to 'age' because it is a read-only property.
🔧 实用变体(扩展)
✅ 内置工具类型
TypeScript 已经内置了 Readonly<T>,所以你通常可以直接写:
type ReadonlyPerson = Readonly<Person>;
🛠 自定义映射类型:可选变必填、只读变可写等
// 所有属性变为可选
type OptionalPerson = { [K in keyof Person]?: Person[K] };
// 所有属性变为可写(移除 readonly)
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type MutablePerson = Mutable<Readonly<Person>>;
// 所有属性变为只读 + 可选
type ReadonlyPartial<T> = { readonly [K in keyof T]?: T[K] };
注意
-readonly是移除readonly修饰符的语法(类似?变-?移除可选)。K
📌 什么时候用映射类型?
- 需要基于现有类型“批量修改”属性(加
readonly、?、改变类型等)。 - 编写通用工具类型(如
Partial<T>,Pick<T, K>,Record<K, T>等底层都用映射类型实现)。 - 减少重复代码,提高类型安全性。
✅ 总结
你的例子:
type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };
→ 是 映射类型 的典型应用,用于将任意对象类型的所有属性变为只读,在状态管理(如 Redux/Vuex)或不可变数据场景中非常有用。
如果你正在用 Vue + TypeScript,这类工具类型在定义 store state(防意外修改)时尤其实用。
ts中的never
- 这是一个 类型, 表示不会存在的类型
- 被 never 修饰的变量,不能赋值给任何变量,只能赋值给同样用 never 修饰的变量
- 和void的比较:void表示这个函数 能够正常结束 并且不会返回值。但是never表示这个函数不会正常结束,没有返回值。
- 下面是naver在 穷尽检查 ,也就是 switch 中的应用:
type Shape = 'circle' | 'square' | 'triangle';
function getArea(shape: Shape) {
switch (shape) {
case 'circle':
return Math.PI;
case 'square':
return 1;
case 'triangle':
return 0.5;
default:
// 如果 Shape 新增了类型但没处理,这里会报错
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck; // ✅ 正常编译通过
}
}
函数参数的 any 和 unknown
function myFunc(param: unknown) {
(param as number[]).forEach((element) => {
element = element + 1;
});
}
function myFunc(param: unknown) {
(param as unknown[]).forEach((element) => {
element = (element as number) + 1;
});
}
- 基本上用于数组的复杂类型 断言
- 如果数组里面传递了各种类型的object,就使用这个 unknown
- 如果使用 any ,那么表示放弃了类型的检查
as 的使用
interface IUser {
name: string;
job?: IJob;
}
interface IJob {
title: string;
}
const user: IUser = {
name: 'foo',
job: {
title: 'bar',
},
};
const { name, job = {} } = user;
const { title } = job; // 类型“{}”上不存在属性“title”,这里会报错
- 要解决这个问题,应该使用 as
const { name, job = {} as IJob } = user;
const { title } = job;
- 由于我们在第一次解构赋值时,为 job 提供了一个空对象作为默认值,TypeScript 会认为此时 job 的类型就是一个空对象,所以我们在第二次解构赋值时,就无法从 job 上获得 title
联合类型 | 和 交叉类型 $
type statusOne = 'string' | 'number' // 联合类型,并且值的类型只能是这两个基础类型中的任何一个
type statusTwo = 'string' & 'number' // 这是一个 交叉类型 ,但是这个类型实际上是 never 。表示:不可能存在的值
interface Person {
name: string;
age: number;
}
interface User {
age: number;
}
type statusThree = Person & User // 这是一个 交叉类型, 这里的最终类型是 “name:string age: number”
- 交叉类型。表示同时拥有两个类型的所有属性
- 如果相同的属性的类型不同,那么就会返回never
interface Person {
name: string;
age: number;
}
interface User {
age: string;
}
type statusThree = Person & User // 这是 never
- 注意这里的 age 的类型是不同的。
vue2和vue3的响应机制有什么区别?
- vue2 是用的 Object.defineProperty 做的双向数据绑定。
- vue3 是用的 proxy,加上 reflect 元数据 、 track 记录订阅 和 trigger 触发订阅事件
function reactive(target) {
return new Proxy(target, {
get(obj, key, receiver) {
track(obj, key); // 依赖收集(Vue 的逻辑)当前的 effect函数,盯着 obj[key]
return Reflect.get(obj, key, receiver);
},
set(obj, key, value, receiver) {
const result = Reflect.set(obj, key, value, receiver);
// 通知 所有盯着 obj[key] 的effect函数运行,这个effect大概率是放在value中。
// 而且trigger会去Proxy中的元数据中找到 key 对应的value,也就是effect函数
trigger(obj, key);
return result; // 保持 set 行为
},
deleteProperty(obj, key) {
const hadKey = Object.hasOwn(obj, key);
const result = Reflect.deleteProperty(obj, key);
if (hadKey && result) {
trigger(obj, key); // 删除属性也应触发更新
}
return result;
}
});
}
- 像这个reactive,那么如果我这样做:const obj = reactive({name:12, info:{address:"南宁"}),如果我设置
obj.info.address = '柳州'就是触发了set对吧?那么这里的 Reflect.set 会不会设置info中的value呢?- 这里涉及到 懒响应 的机制
- 如果 获取了 info 中的数据,这时候 vue3 才会使用 reactive 对info实现代理
- 当重新设置 info.address 的值的时候,才会触发更新。
- 总结:
- 1、为什么要在obj的外面再加上一层 weakMap ?
- 2、因为,如果不加这一层,那么数据变化的时候谁来响应?我首先想到的就是在当前obj的后面加上一个响应函数。那么只要我修改了obj中的某个数据,那么我就要手动的去执行这个响应函数,这时候维护成本就抬高了,而且有可能出现同名函数。
- 3、现在由于js中没有 针对属性改变之后的响应函数,那么就只能在外面加上一层 weakMap。
- 4、明白1/2/3点之后,就可以明白 this 的指向问题了。由于不能在当前obj后面加上响应函数,this指向响应函数就没有意义。但是 我又想自动执行,跟当前的obj相关的响应函数,怎么办?
- 5、这时候就需要 Proxy 上场。因为Proxy给每一层的obj都添加到了 WeakMap, 尤其是 get返回的是Proxy的当前层obj的 WeakMap 值,那么就可以在 get/set 中,通过 track/trigger 这两个函数去找到 Proxy的当前层obj的 WeakMap 对应的响应函数。
- 6、还好我手动敲了 Nest.js 的代码 ,知道 元数据和元编程。虽然 vue3 中的Proxy没有使用元数据,但是道理是相通的,不然看vue3原理的时候真的不知道怎么着手。
针对react和vue的,永恒不变的讨论
- 1、看上一点的总结
- 2、react是使用 fiber 引擎,使用diff算法去操作虚拟DOM的,性能肯定是比不过vue3的Proxy了。vue3是用ES6原生的Proxy语法去决定哪个节点的虚拟DOM要更新。react是根据批处理去更新的,当然有人会说react的性能不好。
- 实际上react在处理复杂表单的时候性能比vue更好
- 3、但是从 平台适用性 来说,vue2/vue3 又比不过 react。vue3不能在IE11之前运行,因为IE11之前的浏览器不支持proxy语法。万幸的是IE系列浏览器写得足够烂,没人用。我们程序员不用针对这些垃圾浏览器做兼容,拯救了两斤头发!
- 4、react有fiber引擎各种平台无所谓,爱怎么跑怎么跑,就像JVM那样。虽然性能差了点,但是能跑啊!
- vue虽然也有UNIAPP,但是生态上比不过react。
useEffect 中异步请求数据,能不能写成
useEffect(async()=>{
await fetch(url);
},[])
- 不能。因为useEffect的第一个参数要求的都是同步函数
- 可以这样定义
useEffect(()=>{
const fetchData = async ()=>{
await fetch(url);
}
fetchData()
},[])
useState 在react18中和react18之后,对比react18之前,有什么变化
const [count, useCount] = useState(0);
useEffect(()=>{
useCount(prev=>prev+1)
useCount(prev=>prev+1)
},[])
setTimeout(()=>{
useCount(prev=>prev+1)
useCount(prev=>prev+1)
}, 500)
- react17
- useEffect中的都会合并到一个批处理中,只会触发一次重新渲染;
- 但是setTimeout这个不是react控制的,会触发两次;
- react18
- 所有的都会合并到一次批处理,并且只触发一次重新渲染;