ts
是 JavaScript 的一个超集,为 JavaScript 添加了可选的静态类型检查,最终会被编译成纯 JavaScript,创造的价值大部分提现在开发阶段。
强大的编辑器智能提示\代码可读性增强\编译时类型检查
interface定义一个对象或者函数的结构,用于声明对象或者函数,或者通过implements去实现类,支持声明合并type不支持声明合并泛型可以保证入参跟返回值是相同类型的,它是一种特殊的变量,只用于表示类型而不是值访问修饰符- public(从任何地方访问,默认的)
- private(内部访问,子类不可)
- protected(内部访问,子类可访问)
- readonly(只读,可复用 public readonly 属性名)
- 允许开发者为
变量、函数参数和返回值明确定义类型,添加类型注解,这有助于捕获类型错误,提高代码质量,以及在编写代码时提供更好的智能感知和自动补全 抽象类(Abstract Class)和抽象方法(Abstract Method)是一种特殊的类和方法,用于定义一个通用的基类,该基类不能直接实例化,只能被其他类继承,抽象方法只能在抽象类中定义
es6
promise
promise: 用于处理异步操作的编程模式
- 三种状态:
pending、fulfilled、rejected, 用于表示一个异步操作的最终结果 - 常用方法包括:
then()、catch()、finally() - 缺点:多个异步操作 需要依次执行时,陷入回调地狱,难以阅读难以维护
- race(): 接受一组promise实例为参数,如Promise.race([p1,p2,p3])
- 只要p1,p2,p3有一个实例
率先改变状态,当前实例的状态就会随着改变 - 如p1,p2,p3是同时创建的 Promise,如果其中有未自定义catch的报错,则会触发当前实例的catch方法,如果未定义catch的参数实例异步执行,如setTimout,则优先输出同步执行的实例返回值
- 应用场景:设置请求超时
- 只要p1,p2,p3有一个实例
- allSettled(): 接受一组promise实例为参数,当所有实例
均返回结果,包装实例才会结束- 当你需要处理多个异步操作,并且
关心每个操作的结果(无论成功还是失败)时,它可以提供很大的帮助
- 当你需要处理多个异步操作,并且
- all(): 接受一组promise实例为参数,如Promise.all([p1,p2,p3])
- 只有当p1,p2,p3的状态
都变成fulfilled时,当前实例的状态才会变成fulfilled - 当p1,p2,p3中
有一个实例被rejected,当前实例的状态就会变成rejected,此时第一个被reject的实例的返回值就会传递给当前实例 - 如果作为参数的实例定义了自己的catch方法,那么他被reject时,并不会触发当前实例的catch方法,若
没有自定义catch,则会触发all函数的catch方法 - 应用场景:多个接口统一loading状态
- 只有当p1,p2,p3的状态
- all 和 allSettled 方法的不同点:
all适用于当你需要所有 Promise 都成功时才继续执行的场景allSettled适用于当你需要知道所有 Promise 的最终状态(无论成功还是失败)时。它不会因为某个 Promise 失败而中断其他 Promise 的执行
async/await
- async/await: 基于promise的语法糖,简化异步操作的书写,让异步代码看起来像同步代码
- async: 用于声明一个异步函数
- await:等待一个promise对象的返回,只能在async内部使用
- 缺点:每个async函数都会创建一个新的Promise对象,可能会导致性能问题
- 两者如何取舍?
- 旧版本js环境:promise (nodejs在v6以下)
- 性能:频繁创建销毁 promise => 通常可以忽略不计,代码的可读性和可维护性更重要
- 与其他库的兼容性: promise更友好
- ES6的Promise与async/await有何区别?
- 都是用于处理异步操作的方法,但它们在使用方式和语法上有所不同
Generator函数
执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
应用场景:dva 使用了 redux-saga 库来处理 saga(副作用),而 redux-saga 是基于 generator 函数的。在 redux-saga 中,effect 是一个 generator 函数,它可以通过 yield 关键字来暂停和恢复执行。
effect: function* fetchUser(action) {
try {
const response = yield call(api.fetchUser, action.payload);
yield put({ type: 'saveUser', payload: response });
} catch (error) {
yield put({ type: 'saveError', payload: error });
}
}
react及相关生态
-
react的组件
- 基础组件
- ui组件
- 路由组件
- 状态管理组件
- 自定义组件
-
react16.8之后的新特性
- useState(初始值、并返回一个数组 [ 当前状态值、更新状态的函数 ])
- useEffect(副作用函数、依赖项数组)
- useContext(组件中访问上下文)
- hooks
- 高阶组件(HOC是一个函数,它接收一个组件作为参数,并返回一个新的组件,尽量保持功能单一)
react.memo: 组件渲染时,浅比较传入的props,如果没有更新,则阻止渲染视图,可以通过修改props来强制刷新组件- withRouter
-
hook的理解与应用
- 允许在不编写class的情况下使用state和其他React特性
-
react fiber的实现思路
- Fiber 是 React 16 中新的协调引擎。它的主要目的是使 Virtual DOM 可以进行增量式渲染
- 在渲染更新时,React会构建一个新的Fiber树(称为工作树),而不是直接修改DOM。一旦新的Fiber树构建完成并且通过了所有的生命周期方法,React会将其与当前的Fiber树(称为当前树)进行比较,然后一次性地将差异应用到DOM上。
- 减少对DOM的直接操作次数
- 增量渲染:允许React暂停、继续或中止渲染工作,更好地利用浏览器的空闲时间
-
diff的思路
- 是React用来比较两个Fiber树之间差异的算法。当组件状态发生变化时,React不会重新渲染整个组件树,而是使用Diff算法来确定哪些真实DOM节点需要被更新。
- fiber架构为diff提供了基础
- 核心特性:递归比较组件树,确定最小更新集合,只对实际需要更新的dom节点进行操作
- 优化方式:react.memo
-
函数组件 和 类组件
- 16.8以后 引入hooks 函数组件几乎可以替代类组件
- 简洁 无需this 无状态(可通过setstate设置局部状态)
-
Context API(类比 Vue的 provide 和 inject)
- 提供了一种在组件树中传递数据的方法,而无需通过中间组件显式地传递道具。
- 允许一个父组件将数据“提升”到它的所有子孙组件中,这些子孙组件可以通过订阅Context来接收数据
- 特别适用于跨多个组件层级的数据共享,如主题设置、用户认证状态或全局配置等
- 需要注意的事项:避免过度使用、函数式更新、性能考虑(react.memo)
- 函数式更新(Functional Update)是一种优化状态更新的方法,它可以避免因状态更新而引起的不必要的组件重新渲染
<FormDataContext.Provider value={{ formData, setFormData }}>
<FormComponent>
<ChildComponent1 />
<ChildComponent2 />
</FormComponent>
</FormDataContext.Provider>
const MyContext = React.createContext();
// 需要使用`Provider`组件来提供Context的值。
// `Provider`组件接受一个`value`属性,这个属性的值将被所有使用该Context的子组件共享
class MyApp extends React.Component {
state = {
sharedData: 'This is the shared data',
};
render() {
return (
<MyContext.Provider value={this.state.sharedData}>
<ComponentA />
<ComponentB />
</MyContext.Provider>
);
}
}
// 组件树中的任何位置,子组件都可以消费Context提供的值
// 有两种主要的方式来消费Context:使用`useContext` Hook和使用`Consumer`组件
import React, { useContext } from 'react';
function ComponentC() {
const sharedData = useContext(MyContext);
return <div>{sharedData}</div>;
}
<MyContext.Consumer>
{(sharedData) => <div>{sharedData}</div>}
</MyContext.Consumer>
- 受控组件和非受控组件,类组件和函数组件
- 受控与非受控:这是根据组件状态的管理方式来区分的。受控组件由外部状态控制,而非受控组件由组件自身管理状态。
- 类与函数:这是根据组件的定义方式来区分的。类组件使用ES6类语法,支持状态和生命周期方法;函数组件使用函数语法,不支持状态和生命周期方法,但可以通过Hooks来模拟这些功能。
react-dnd 16.0
- react-dnd是一个针对react组件的拖放库,先用dndprovider包裹整个组件树,并用backend属性来指定后端实现,pc的话 一般是html5backend,还有一种是touchbackend用于触摸屏
- 用usedrag和usedrop来处理拖和放
- usedrop:isover悬停 drop事件:item(拖动的元数据), monitor(拖动状态)
- react-sortable-hoc:
SortableContainer包裹整个列表,SortableElement用于标记可排序的列表项。当拖动列表项并放置到新位置时,onSortEnd回调函数会被触发,从而更新列表项的顺序。
redux
recoil
- 是一个基于react的状态管理工具,他的核心概念是
- atom:原子,基础单位
- selector:派生属性 入参为atom或者函数,如果只提供
get方法,则 selector 便是只读的,并且会返回一个RecoilValueReadOnly对象。如果还提供了一个set方法,它的返回值将变为一个可写的RecoilState对象
const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
const fontSizeLabel = useRecoilValue(fontSizeLabelState);
- Recoil支持
异步操作和并发控制
ahooks
- useRequest:用于处理 HTTP 请求,支持自动重试、分页、无限滚动等功能(v3已删除,避免了
useRequestTypeScript 重载,因为formatResult函数的设计不够明确,使得TypeScript无法准确地推断返回类型) - useThrottle:用来处理节流值的 Hook,处理高频触发的值(如滚动位置、窗口大小等)
- useThrottleEffect:为
useEffect增加节流的能力,执行副作用函数时应用节流逻辑 - useThrottleFn: 用来处理函数节流的 Hook,处理高频触发的事件(如滚动事件、鼠标移动事件等)
- useDebouncefn:防抖
防抖: 输入,结束后过一段时间才开始执行
节流: 滚动,异步点击事件,持续触发事件,每隔一段时间,事件处理函数只执行一次
const { run:触发执行, cancel:取消当前防抖, flush:立即调用当前防抖函数 } = useDebounceFn(
fn: (...args: any[]) => any,
options?: Options // wait maxWait leading trailing
);
- useUnmount:在组件卸载时,执行函数 类似于 useeffect中的cleanup函数
- useDeepCompareEffect:在处理复杂对象(如对象、数组等)作为依赖项时非常有用
- useSize:监听 DOM 节点尺寸变化的 Hook。
- useImperativeHandle:在父组件中调用子组件的imperative中暴露的方法
- useref:一个持久化可变的值且不引起页面重新渲染
- usememo: 允许记住复杂计算的结果,避免在每次渲染都重新计算(回调函数、依赖数组)
自定义的经历: 帮助我们提取组件之间的共同逻辑,提高代码的复用性和可维护性
- useFormatDateRange(根据传入的时间,统一设置禁用时间区间,和返回时间格式,时分秒)
- useBigSave(活动的分步保存,活动的模板分步复制,入参是url和step)
- useCompSave(不同组件的单独保存)
antd
微前端
- 将单体前端应用拆分为多个独立但协同工作的较小应用的架构方法。每个小应用(也称为微应用)都负责特定的业务功能,并且可以独立开发、部署和扩展
- qiankun
发布组件包
diff算法
- react:fiber树的比较,单链表的数据结构来存储
- vue2:数组结构,两端比对优化
- vue3:数组结构,两端比对优化,最长递增子序列
webpack
- 模块打包工具,用于将项目中的多个模块(如JavaScript、CSS、图片等)打包成一个或多个文件,以便在浏览器中运行
- 流程梳理
- 从配置文件和shell语句中合并参数,加载plugin,初始化compiler对象,执行compiler对象run方法开始编译,根据配置文件的entry找到入口文件,调用对应的loader对模块进行编译,再找到模块依赖的模块递归的编译,直到入口依赖的所有文件都得到了编译,将编译过后的module组合成chunk(代码块),再将chunk转化成文件,根据配置文件的output输出到文件系统中
webpack.config.js是Webpack的配置文件,它导出一个配置对象,包含:- 入口(entry):指定Webpack开始构建的起点模块,Webpack从这个模块开始,分析依赖关系并构建依赖树。
- 输出(output):指定Webpack打包后的文件输出路径和文件名,path、filename
- 加载器(loader):用于处理非JavaScript模块,将它们转换为Webpack可以处理的模块。
- 插件(plugin)用于执行Webpack流程中的广泛任务,如优化、压缩、重新定义环境变量等
webpack.config.js是Webpack的配置文件,它导出一个配置对象,包含Webpack的各种配置选项。主要配置选项包括entry(入口)、output(输出)、module(模块,包含加载器配置)和plugins(插件)
- 热模块替换(HMR)是一种实时更新修改过的模块而不刷新整个页面的技术。要在Webpack中配置HMR,需要安装
webpack-dev-server,并在webpack.config.js文件中配置devServer.hot选项为true
Webpack如何优化代码性能?
- 代码拆分(Code Splitting) :通过将代码拆分成多个较小的独立块(chunk),可以实现按需加载和并行加载,从而提高应用程序的性能。可以使用
import()语法或SplitChunksPlugin插件来实现代码拆分。 - 使用缓存:通过配置缓存,可以加快Webpack的构建速度。可以使用
cache-loader或配置cache选项来实现缓存(因为Webpack可以重用之前构建的结果,而不是每次都从头开始构建) - 压缩代码:在生产环境中,使用压缩插件(如TerserWebpackPlugin)可以有效地减小JavaScript文件的大小。
- 优化图片和字体资源:使用适当的加载器(如
url-loader、file-loader、image-webpack-loader等)和插件(如MiniCssExtractPlugin)来压缩和优化图片、字体等资源文件的大小。 - 热模块替换(HMR) :热模块替换是一种实时更新修改过的模块而不刷新整个页面的技术。通过配置
webpack-dev-server和HMR客户端,可以实现热模块替换,提高开发过程中的性能。 - 分析和优化依赖关系:定期检查项目的依赖关系,移除不必要的依赖,优化项目结构。可以使用工具(如
webpack-bundle-analyzer)来分析打包文件的组成,找出性能瓶颈。 - 配置Babel:合理配置Babel(如使用
@babel/preset-env预设)可以避免不必要的转换,减小打包文件的大小
Vue
v-if 和 v-for的优先级
v-for的优先级高于v-if,所以不要将v-for和v-if同时用在一个元素上,会带来性能上的浪费(每次渲染都会先循环再进行条件判断),可在v-for外层套template进行v-if判断;- 如果条件判断出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
vue3中已经优化了这个问题,即在vue3中v-if的优先级会高于v-for的优先级,但我们还是需要尽量避免这种写法,原因如下:- 可读性 和 可维护性
- 更好的错误处理
- 避免潜在的性能陷阱:
- 如v-if中的条件依赖v-for中循环的数据
- v-if的条件依赖于外部变量,本来v-for的内容可不需要重新加载,因为v-if的原因而需要额外且多余的消耗
- 需要频繁变化的循环项
spa首屏优化方案(资源加载优化 和 页面渲染优化)
- 减小入口文件体积(代码拆分、动态路由)
- 静态资源本地缓存(localstorage、后端资源返回:强缓存、协商缓存)
- ui框架按需加载
- 图片资源压缩
- 组件重复打包(webpack的config文件修改commonschunkplugin的配置 minChunk:3)
- 开启GZip压缩(compression-webpack-plugin)
- 使用SSR (服务端渲染,nuxt.js)
vue中的data定义函数而非对象
- vue会通过vue.extend()构建组件实例,用对象定义data会使不同的实例对象的数据互相污染
- 根对象data可以是对象也可是函数(根对象是单例,不会产生数据污染)
- 组件实例data必须是函数,initdata时会将其作为工厂函数返回全新data对象
vue2动态添加新的响应属性
Vue.set()、Object.assign()、$forceUpdated (不建议)
mixin局部混入和全局混入
- 局部: Vue.component('componentA',{ mixins: [myMixin] })
- 全局:Vue.mixin({ created: function(){ console.log("log") } });会影响到每一个组件实例,包括第三方组件
- 当组件存在与mixin对象重名的属性时,进行递归合并的时候,组件属性会覆盖mixin属性
- 当组件存在与mixin对象重复的生命周期钩子时,会合并成数组,先执行mixin钩子再执行组件钩子
vue的几种类型合并策略
- 替换型:props、methods、computed、inject
- 合并型:data
- 队列型:生命周期、watch
- 叠加型:component、filters、directives
keep-alive 缓存组件
- props:incluede exclude max
- 被缓存的组件会多出两个生命周期钩子:activated deactivated
指令
- 全局注册 Vue.directive('lazy',{bind:(el)=>{}})
- 局部注册 directive: { lazy : { bind : function(){} } }
- 钩子:bind、inserted、update、componenUpdated、unbind
- 应用场景:表单的防重复提交v-throttle、图片懒加载v-lazy、页面水印v-mask
项目部署history模式404
- 原因:单页面应用路由由前端js代码处理,不是通过服务器上的实际文件,所以直接访问某个路由或者刷新页面时,服务器查找不到则会报错
- 解决方案:配置将任意页面都重定向到index.html,将路由交由前端处理
- nginx:location / { try_files uri/ /index.html}
- apache: mod_rewrite模块
- express: connect-history-api-fallback中间件
- hash模式:虽然出现在路由上,但只有#符号之前的内容会被包含在请求中,所以单页面应用对服务端完全没有影响
eslint
- 在大型项目中,如何组织ESLint配置以提高代码质量?
- 使用
.eslintignore文件排除不需要检查的文件和目录。 - 将规则分解到多个
.eslintrc文件中,根据目录结构进行配置。 - 在
.eslintrc中配置prettier作为代码格式化工具,并禁用与prettier冲突的ESLint规则。
- 使用
git
- 开源的分布式版本控制系统和源代码管理(SCM)系统
git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决- git pull --rebase 变基
缓存
- localstorage: 长久存储、同一域名下数据共享
- sessionstrorage:仅在当前浏览器窗口或标签页的生命周期内存在。当窗口或标签页关闭时,数据会被自动清除,仅限于当前页面的会话,不同页面之间的数据不会共享
- indexDB:
浏览器的缓存策略
- 强缓存:http缓存字段(expires、cache-control、last-modified、etag),比较这些字段是否有效,如果失效则重新请求,否则直接使用缓存
- 协商缓存:强缓存失效过后,跟服务端比较last-modified或etag来判断资源是否发生变化,如果没发生变化则返回304,客户端使用缓存,否则返回新资源
js概念
闭包
- 当一个内部函数引用了外部函数的变量后,就形成了闭包,当外部函数执行完之后,这个变量会因为被 内部函数使用了,所以不会被变量回收,仍会保留在内存中,这种现象称为“闭包”
- 用途:模拟私有变量、实现函数柯里化(将一个多参数的函数转化成一系列使用单个参数函数)
原型链
- 对象继承,当你想访问一个对象的属性,js首先会查找对象有没有这个属性,如果没有,就会去查找这个对象的原型,即 proto,如果在这个原型上没有查找到,则查找这个原型的原型,以此类推,直到找到该属性或者达到原型链的顶端。通常是object.proptotype,如果仍没有则是undefined,说明该属性原型链上不存在
js文件引入位置的区别
- head:优点:会在页面内容之前加载完毕;缺点:会阻塞页面渲染,如果脚本过大或网络缓慢,会影响用户体验
- body:优点:不会阻塞页面渲染;缺点:如果脚本内容会涉及dom内容,会有可能出现dom未加载的情况
- async:异步加载JS,下载完成后立即执行,不保证执行顺序
- defer:异步加载JS,整个HTML文档解析完成后按照在HTML文档中的顺序执行
ES6
const的用法
- const实际上保证的并不是变量的值不可改动,而是变量指向的那个内存地址所保存的数据不得改动
- const a = {};
- a.value = "1"; 可以
- a = {}; 报错 "a" is read-only
- var\let\const涉及的概念股:变量提升、暂时性死区、块级作用域、重复声明
const的用法
html解析的流程
单点登录 和 单设备登录
postmessage
- 跨文档通信机制;允许来自不同源的窗口之间安全的通信