前端概念题(常更常新版)

129 阅读17分钟

ts

是 JavaScript 的一个超集,为 JavaScript 添加了可选的静态类型检查,最终会被编译成纯 JavaScript,创造的价值大部分提现在开发阶段。

强大的编辑器智能提示\代码可读性增强\编译时类型检查

  • interface定义一个对象或者函数的结构,用于声明对象或者函数,或者通过implements去实现类,支持声明合并
  • type不支持声明合并
  • 泛型可以保证入参跟返回值是相同类型的,它是一种特殊的变量,只用于表示类型而不是值
  • 访问修饰符
    • public(从任何地方访问,默认的)
    • private(内部访问,子类不可)
    • protected(内部访问,子类可访问)
    • readonly(只读,可复用 public readonly 属性名)
  • 允许开发者为变量函数参数返回值明确定义类型,添加类型注解,这有助于捕获类型错误,提高代码质量,以及在编写代码时提供更好的智能感知和自动补全
  • 抽象类(Abstract Class)抽象方法(Abstract Method)是一种特殊的类和方法,用于定义一个通用的基类,该基类不能直接实例化,只能被其他类继承,抽象方法只能在抽象类中定义

es6

promise

promise: 用于处理异步操作的编程模式

  • 三种状态: pendingfulfilledrejected, 用于表示一个异步操作的最终结果
  • 常用方法包括:then()catch()finally()
  • 缺点:多个异步操作 需要依次执行时,陷入回调地狱,难以阅读难以维护
  • race(): 接受一组promise实例为参数,如Promise.race([p1,p2,p3])
    • 只要p1,p2,p3有一个实例率先改变状态,当前实例的状态就会随着改变
    • 如p1,p2,p3是同时创建的 Promise,如果其中有未自定义catch的报错,则会触发当前实例的catch方法,如果未定义catch的参数实例异步执行,如setTimout,则优先输出同步执行的实例返回值
    • 应用场景:设置请求超时
  • 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状态
  • 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已删除,避免了 useRequest TypeScript 重载,因为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: 允许记住复杂计算的结果,避免在每次渲染都重新计算(回调函数、依赖数组)

自定义的经历: 帮助我们提取组件之间的共同逻辑,提高代码的复用性和可维护性

  1. useFormatDateRange(根据传入的时间,统一设置禁用时间区间,和返回时间格式,时分秒)
  2. useBigSave(活动的分步保存,活动的模板分步复制,入参是url和step)
  3. 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如何优化代码性能?

  1. 代码拆分(Code Splitting) :通过将代码拆分成多个较小的独立块(chunk),可以实现按需加载和并行加载,从而提高应用程序的性能。可以使用import()语法或SplitChunksPlugin插件来实现代码拆分。
  2. 使用缓存:通过配置缓存,可以加快Webpack的构建速度。可以使用cache-loader或配置cache选项来实现缓存(因为Webpack可以重用之前构建的结果,而不是每次都从头开始构建)
  3. 压缩代码:在生产环境中,使用压缩插件(如TerserWebpackPlugin)可以有效地减小JavaScript文件的大小。
  4. 优化图片和字体资源:使用适当的加载器(如url-loaderfile-loaderimage-webpack-loader等)和插件(如MiniCssExtractPlugin)来压缩和优化图片、字体等资源文件的大小。
  5. 热模块替换(HMR) :热模块替换是一种实时更新修改过的模块而不刷新整个页面的技术。通过配置webpack-dev-server和HMR客户端,可以实现热模块替换,提高开发过程中的性能。
  6. 分析和优化依赖关系:定期检查项目的依赖关系,移除不必要的依赖,优化项目结构。可以使用工具(如webpack-bundle-analyzer)来分析打包文件的组成,找出性能瓶颈。
  7. 配置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 uriuri 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

  • 跨文档通信机制;允许来自不同源的窗口之间安全的通信