一、Html
二、Css
三、Js
2、Es6相关
1、数组解构、...展开运算符的原理
let [a,b,c] = [1,2,3]
let z = [...arr]
调用数组的iterator迭代器遍历原数组,通过生成器for of生成新数组
对象没有iterator 生成器for of不能遍历的遍历对象 遍历对象用for in
2、Proxy
proxy Es6新增的类,用于创建一个代理对象,原对象的操作由代理对象完成
const obj = {
name:'cj',
age:18
}
const objProxy = new Proxy(obj,{})
参数1 需求代理的对象 参数2 包含回调函数的对象
const objProxy = new Proxy(obj,{
//get操作符
get:function(target,key){
console.log(`监听到访问${key}属性`,target)
return target[key] //返回访问属性的值
},
//set操作符
set:function(target,key,newValue){
console.log(`监听到给${key}属性设置值`,target)
tarset[key] = newValue //将属性最新值,赋值给代理对象属性
}
//has操作符
has:function(target,key){
console.log(`监听到使用in操作符${key}`,target)
return key in target
}
//delete操作符
deleteProperty:function(){
console.log(`监听到使用delete操作符${key}`,target)
delete target[key]
}
})
console.log(objProxy.name)
console.log(objProxy.age)
objProxy.name = 'wx'
objProxy.age = 20
//in操作符
console.log('name' in objProxy) //true
//delete操作符
delete objProxy.name
Object.defineProperty直接监听对象的属性,proxy是监听整个代理对象
四、Ts
1、Ts数据类型
1、基础类型
字符串类型
let a:string='string'
number类型
let num:number= 1
布尔类型
let boolean:boolean=true
空值类型
void 表示没有任何返回值的函数
void 也可以定义undefined和null
function voidFn (): void
{console.log('test void')}
let u: void = undefined
let n: void = null
null、undefined类型
let u: undefined = undefined
let n: null = null
void、null、undefined的区别 null、undefined 可以分配给其他类型 void不能
2、任意类型
any类型、unknown类型
1、any类型为没有指定类型、可以改变的类型,不需要检查类型
2、声明变量时没有指定类型默认为any类型
3、缺点any类型失去Ts类型检查作用
4、Ts3.0中引入unknown类型,可以分配给任意类型,比any类型更安全、更严格
any和unknown的区别 1、unknown类型声明的变量不能赋值给其他类型非any类型的变量
// 不可以
let names:unknown = '123'
let names2:string = names
// 可以
let bbb:unknown = '123'
let aaa:any= '456'
2、any类型定义的对象访问不存在的属性和方法时不会报错,unknown类型不能
// 不会报错
let obj:any = {b:1}
obj.a
// 会报错
let obj:unknown = {b:1,ccc:():number=>213}
obj.b
obj.ccc()
3、对象类型(接口 interface)
用interface关键字声明一个满足所有数据类型的集合来定义对象
interface Rule {
a?:number,
readonly b:string,
[propName: string]: any,
fun:()=>void
}
const example:Rule = {
a:123,
b:'123',
fun:()=>{
console.log('123')
}
}
1、相同名称的interface会被合并一个
2、example对象中的数据要与interface定义的类型、数量一致
(1)、可用?可选链操作符定义interface兼容
(2)、可以添加任意属性 [propName: string]: any
3、定义readonly只读属性 只能被读取 不能修改和重新赋值
4、可以定义函数
4、数组类型
1、类型[]
let arr:number[] = [1, 2, 3]; //数字类型的数组
let arr:string[] = ["1", "2"]; //字符串类型的数组
let arr:any[] = [1, "2", true]; //任意类型的数组
let arr:any[][] = [[1,2], ['3','4'],[true,false]]; //任意类型的数组
数组内不能存在非对应类型的数据
2、数组泛型 Aray[类型]
let arr:Array<number> = [1,2,3,4,5]
let arr:Array<string> = ['1','2','3','4','5']
业务接口数组对象数据常用写法
interface Item {
age: number,
name: string
}
const array: Array<Item> = [
{ age: 123, name: "1" },
{ age: 123, name: "2" },
{ age: 123, name: "3" }
]
5、元组(Tuple)
元组为数组的变种,数量相同、类型不同的元素的集合
let arr:readonly[number,srting,boolean,any]=[1,'2',true,3]
6、枚举
使用enum关键字定义枚举 1、数字枚举
enum colors{
Red=1
Green
Yellow
}
默认从0开始,依次递增,给默认值后续从默认值开始递增、
2、字符串枚举
enum colors{
Red='red'
Green='green'
Blue='blue'
}
colors[Red]
字符串枚举没有自增行为,可以很好的序列化
3、异构枚举
enum colors{
red=1
green='1'
}
异构枚举可以包含各种不同类型
7、never类型
never类型标识不应该存在的状态 常用于报错的处理
function err (message:string):never {
throw new Errow (message)
}
never void的区别 1、void类型是定义函数返回值为空 never定义的抛出错误没有返回值
2、联合类型中never会被移除
type A = string | void | never // 不会显示never
8、symbol类型
symbol类型是通过Symbol构造函数创建的唯一值,只支持string和number类型的传参
2、Ts在函数中的拓展
const fn = (name: string, age?:number=1): string => {
return name + age
}
fn('张三',18)
1、参数、默认值、函数的返回值要与限制类型的相同
2、可用?兼容可能不存在的参数
3、类型断言、联合类型、交叉类型
1、联合类型
// 可以
let a:string | number='1'
let a:string | number= 1
// 不可以
let a:string | number= true
2、交叉类型 多种类型的集合,联合对象具有所有联合类型的所有属性
interface People {
age: number,
height: number
}
interface Man{
sex: string
}
const xiaoman = (man: People & Man) => {
console.log(man.age)
console.log(man.height)
console.log(man.sex)
}
xiaoman({age: 18,height: 180,sex: 'male'});
3、类型断言
语法
interface A {
run: string
}
interface B {
build: string
}
const fn = (type: A | B): string => {
return (type as A).run
return <A>type.run
}
类型断言只能跳过Ts编译器,无法避免错误
4、class类的定义
class Person {
public name:string
private age:number
protected some:any = 0
static staus:any
constructor (name:string,age:number){
this.name = name
this.age = age
this.any=any
}
}
1、定义了变量但未使用会报错,声明的时候给默认值或赋值
2、类的修饰符
(1)public 定义的变量内外部都可以访问 默认public
(2)private 定义的变量为私有 只能在内部访问不能在外部访问
(3)protected 定义的变量为私有 只能在内部或在继承的子类中访问
(4)static 定义的变量和函数只能用类名调用,不能通过this调用
5、类型推论、类型别名
1、类型推论
变量声明后未定义类型 Ts会自动推测出一个类型,不能再赋值给其他类型
2、类型别名
type关键字 可以给一个类型定义一个名字 多用于复合类型
type value = boolean | 0 | '213'
let s:value = true
类型别名type,接口interface的区别
1、interface可以继承,重名可以自动合并,type不可以
2、type可以定义联合、交叉类型,interface不可以
6、泛型
1、函数泛型
function Add<T>(a: T, b: T): Array<T> {
return [a,b]
}
Add<number>(1,2)
Add<string>('1','2')
函数名后面加 <参数>
2、接口定义泛型
interface MyInter<T> {
(arg: T): T
}
function fn<T>(arg: T): T {
return arg
}
let result: MyInter<number> = fn
result(123)
声明接口的时候 在名字后面加一个<参数> 使用的时候传递类型
3、泛型约束
interface Len {
length:number
}
function getLegnth<T extends Len>(arg:T) {
return arg.length
}
getLegnth<string>('123')
约束其为具有length属性的类型,这里我们会用到interface
7、Tsconfig.js文件
通过tsc --init命令生成tsconfig.json文件
常用选择项
1.include 指定编译文件默认是编译当前目录下所有的ts文件
2.exclude 指定排除的文件
3.target 指定编译js 的版本例如es5 es6
4.allowJS 是否允许编译js文件
5.removeComments 是否在编译过程中删除文件中的注释
6.rootDir 编译文件的目录
7.outDir 输出的目录
8.sourceMap 代码源文件
9.strict 严格模式
10.module 默认common.js 可选es6模式 amd umd 等
8、命名空间(namespace)
- 内部模块,主要用于组织代码,避免命名冲突。
- 命名空间内的类默认私有
- 通过
export暴露 - 通过
namespace关键字定义
namespace a {
export const Time: number = 1000
export const fn = <T>(arg: T): T => {
return arg
}
fn(Time)
}
namespace b {
export const Time: number = 1000
export const fn = <T>(arg: T): T => {
return arg
}
fn(Time)
}
a.Time
b.Time
9、Ts内置工具
Partial、Required、Pick、Record、Readonly
- Partial 将所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
- Required 将所有属性变为必选
- Pick 从类型定义T的属性中,选取指定一组属性,返回一个新的类型定义
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
- Readonly 将所有属性变为只读属性
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
- Record 生产一个新的属性为K,类型为T的类型集合
type Record<K extends keyof any, T> = {
[P in K]: T;
};
五、Vue
1、Vue2和Vue3的区别
语法区别
- template编译模版中可以写多个节点
- setup语法糖模式 包含了vue2的beforecreate和created生命周期
- 组合式Api,数据逻辑写在一起,需要用到的import引入
- 数据双向绑定用ref和reactive代替原data定义
- 生命周期函数名称改变前面+on
- 新增了watchEffect高级监听器
源码区别
- 数据绑定用Es6的Proxy代理替换了Object.defineProperty监听数据改变
- diff算法优化了最长增加递增子序列算法
2、Vue3 ref和reactive的区别
- ref支持所有类型,reactive只支持引用类型 object、array
- ref取值、赋值要加.value reactive不需要
- reactive是Proxy代理的一个对象不能直接赋值,会破坏响应式
六、React
七、Webpack
1、为什么要用打包工具
- 开发中使用的框架、css预处理、高级语法浏览器无法识别,必须要经过编译才能识别
- 打包工具能压缩代码,兼容处理,提升性能
- webpack生产环境会压缩代码 开发环境不会
2、webpack五大模块
1、entry(入口)
从那个文件作为入口开始打包
entry: "./src/main.js", // 相对路径
2、output(输出)
打包完成的文件输出到哪里
output: {
path: path.resolve(__dirname, "dist"), // 输出文件路径
filename: "static/js/main.js", // 输出文件名
clean: true, // 打包前清空dist目录
}
3、loader(加载器)
借助loader解析其他非js、json格式文件
module: {
// loader的配置
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"], // 处理cssloader 从后往前解析
},
// webpack5 内置图片处理
{
test: /\{png|jpe?g|gif|webp}$/,
type: 'asset',
parser:{
dataUrlCondition:{
maxSize:10*1024 // 小于10kb的图片转base64 减少服务器请求 但体积会变大
},
generator: {
filename: "static/images/[hash:10][ext][query]",
} // 处理图片保存路径
}
}
],
},
常用loader
- css-loader less-loader sass-loader 处理css文件
- file-loader url-loader 处理图片资源 webpack5 已内置不用单独配置
- eslint-loader babel-loader 处理代码格式 es6高级语法兼容
4、plugins(插件)
扩展webpack其他功能
常用plugins
- HtmlWebpackPlugin 生成一个html文件 自动引入外部资源
- EslintWebpackPlugin 对代码进行格式校验
- IgnorePlugin 处理不需要引入的模块
- MiniCssExtractPlugin link引入css样式 阻止图片闪屏
- CssMinimizerWebpackPlugin css文件压缩插件
5、mode(模式)
- 开发模式 development
- 生产模式 production
3、devServer
webpack配置 devServer 启动代理服务 可配置proxy处理开发跨域问题
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口
open: "true",
// 配置代理 处理跨域问题
proxy: {
'/insurance': {
target: 'http://www.baidu.com',
changeOrigin: true,
pathRewrite: {
'aaa': ''
}
}
}
},
八、Vite
九、NodeJs
十、NestJs
十一、微前端
十二、性能优化、前端安全
十三、设计模式
1、发布订阅模式
定义三个角色:发布者 订阅者 调度者
发布者发生变化后通知调度者变化,订阅者通过调度者的变化随之改变
-
on订阅/监听
-
emit 发布/注册
-
once 只执行一次
-
off解除绑定
interface EventFace {
on: (name: string, callback: Function) => void,
emit: (name: string, ...args: Array<any>) => void,
off: (name: string, fn: Function) => void,
once: (name: string, fn: Function) => void
}
interface List {
[key: string]: Array<Function>,
}
class Dispatch implements EventFace {
list: List
constructor() {
this.list = {}
}
on(name: string, callback: Function) {
const callbackList: Array<Function> = this.list[name] || [];
callbackList.push(callback)
this.list[name] = callbackList
}
emit(name: string, ...args: Array<any>) {
let evnetName = this.list[name]
if (evnetName) {
evnetName.forEach(fn => {
fn.apply(this, args)
})
} else {
console.error('该事件未监听');
}
}
off(name: string, fn: Function) {
let evnetName = this.list[name]
if (evnetName && fn) {
let index = evnetName.findIndex(fns => fns === fn)
evnetName.splice(index, 1)
} else {
console.error('该事件未监听');
}
}
once(name: string, fn: Function) {
let decor = (...args: Array<any>) => {
fn.apply(this, args)
this.off(name, decor)
}
this.on(name, decor)
}
}
const o = new Dispatch()
o.on('abc', (...arg: Array<any>) => {
console.log(arg, 1);
})
o.once('abc', (...arg: Array<any>) => {
console.log(arg, 'once');
})
// let fn = (...arg: Array<any>) => {
// console.log(arg, 2);
// }
// o.on('abc', fn)
// o.on('ddd', (aaaa: string) => {
// console.log(aaaa);
// })
//o.off('abc', fn)
o.emit('abc', 1, true, '小满')
o.emit('abc', 2, true, '小满')
// o.emit('ddd', 'addddddddd')