web面试题记录之react

Posted jiaojsun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web面试题记录之react相关的知识,希望对你有一定的参考价值。

为什么使用hooks


  1. class 在组件之间复用状态逻辑很难,由高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”。| Hook 从组件中提取状态逻辑, 使得这些逻辑可以单独复用

  1. 【拆分】class 组件不好理解, 每个生命周期常常包含一些不相关的逻辑【例如,组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是,同一个 componentDidMount 中可能也包含很多其它的逻辑,如设置事件监听,】| Hook 将组件中相互关联的部分拆分成更小的函数

  1. class组件需要理解各种this的指向,class 不能很好的压缩、

  1. react hooks带来了什么便利


在没有 hooks 之前,我们使用函数定义的组件中,不能使用 React 的 state、各种生命周期钩子类组件的特性。在 React 16.8 之后,推出了新功能: Hooks,通过 hooks 我们可以再函数定义的组件中使用类组件的特性。

好处:

  • 跨组件复用: 其实 render props / HOC 也是为了复用,相比于它们,Hooks 作为官方的底层 API,最为轻量,而且改造成本小,不会影响原来的组件层次结构和传说中的嵌套地狱;

  • 相比而言,类组件的实现更为复杂

  • 不同的生命周期会使逻辑变得分散且混乱,不易维护和管理;

  • 时刻需要关注this的指向问题;

  • 代码复用代价高,高阶组件的使用经常会使整个组件树变得臃肿;

  • 状态与 UI 隔离: 正是由于 Hooks 的特性,状态逻辑会变成更小的粒度,并且极容易被抽象成一个自定义 Hooks,组件中的状态和 UI 变得更为清晰和隔离。

注意:

  • 避免在 循环/条件判断/嵌套函数 中调用 hooks,保证调用顺序的稳定;

  • 不能在useEffect中使用useState,React 会报错提示;

  • 类组件不会被替换或废弃,不需要强制改造类组件,两种方式能并存

Hooks的缺点


  1. 学习成本 2.初学者在useEffect里面会存在死循环的问题

3.自定义 hook 也是 hook,只能在函数组件的顶层使用,不能在 if 或 for 循环中使用;

diff算法:

目的就是对比两次渲染的结果,找到可复用的部分,剩下的进行删除和新增就可以了

虚拟DOM


虚拟DOM表示真实DOM的JS对象,如何做到快速找到两个虚拟DOM之间存在的差异,来可以最小化的更新视图,就需要vue的diff算法

干前端的都知道DOM操作是性能杀手,因为操作DOM会引起页面的回流或者重绘。那么为什么现在的框架都使用虚拟DOM呢?因为使用虚拟DOM可以提高代码的性能下限,并极大的优化大量操作DOM时产生的性能损耗【因为 VM 并不是真实的操作 DOM,通过 diff 算法可以避免一些不变要的 DOM 操作,从而提高了性能。

69. react的虚拟DOM是怎么实现的


React 是把真实的 DOM 树转换为 JS 对象树,也就是 Virtual DOM。每次数据更新后,重新计算 VM,并和上一次生成的 VM 树进行对比,对发生变化的部分进行批量更新。除了性能之外,VM 的实现最大的好处在于和其他平台的集成。

  1. React的diff算法:


diff 算法探讨的就是虚拟 DOM 树发生变化后,生成 DOM 树更新补丁的方式。它通过对比新旧两株虚拟 DOM 树的变更差异,将更新补丁作用于真实 DOM,以最小成本完成视图更新

具体的流程是这样的:

  • 真实 DOM 与虚拟 DOM 之间存在一个映射关系。这个映射关系依靠初始化时的 JSX 建立完成;

  • 当虚拟 DOM 发生变化后,就会根据差距计算生成 patch,这个 patch 是一个结构化的数据,内容包含了增加、更新、移除等;

  • 最后再根据 patch 去更新真实的 DOM,反馈到用户的界面上。

更新时机:更新发生在setState、Hooks 调用等操作以后。

遍历算法:深度优先遍历算法。因为广度优先遍历可能会导致组件的生命周期时序错乱,而深度优先遍历算法就可以解决这个问题。

优化策略:策略一:忽略DOM树跨层级操作场景,提升比对效率。策略二:如果组件的 class 一致,则默认为相似的树结构,否则默认为不同的树结构【React.memo 可以提高性能的原因。】策略三:同一层级的子节点,可以通过标记 key 的方式进行列表对比

React 16 引入Fiber 设计:使得整个更新过程可以随时暂停恢复


性能优化

  • useCallback + memo 避免组件不必要重的重复渲染

  • useMemo 避免组件在每次渲染时都进行高开销的计算

react数据通信

  1. 类组件和函数式组件的区别


  1. 语法上:函数组件是一个纯函数, 它接收一个props对象返回一个react元素

而类组件需要去继承React.Component并且创建render函数返回react元素

  1. 状态管理:因为函数组件是一个纯函数,你不能在组件中使用setState(),这也是为什么把函数组件称作为无状态组件。

  1. 生命周期钩子:你不能在函数组件中使用生命周期钩子,原因和不能使用state一样,所有的生命周期钩子都来自于继承的React.Component中。

  1. 高阶组件HOC


const EnhancedComponent = higherOrderComponent(WrappedComponent);

  • 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧

  • 高阶组件的参数为一个组件返回一个新的组件

  • 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件

  1. 受控组件


非受控组件,即组件的状态不受React控制的组件,例如下边这个

import React,  Component  from 'react';
import ReactDOM from 'react-dom';

class Demo1 extends Component 
    render() 
        return (
            <input />
        )
    


ReactDOM.render(<Demo1/>, document.getElementById('content'))

在这个最简单的输入框组件里,我们并没有干涉input中的value展示,即用户输入的内容都会展示在上面

受控组件就是组件的状态受React控制。上面提到过,我们把input的value属性和state结合在一起,再绑定onChange事件,实时更新value值就行了。

class Demo1 extends Component 
    constructor(props) 
        super(props);
        this.state = 
            value: props.value
        
    

    handleChange(e) 
        this.setState(
            value: e.target.value
        )
    

    render() 
        return (
            <input value=this.state.value onChange=e => this.handleChange(e)/>
        )
    
  1. JSX


JSX即javascript XML。一种在React组件内部构建标签的类XML语法。

class MyComponent extends React.Component 
  render() 
    let props = this.props;  
    return (
      <div className="my-component">
      <a href=props.url>props.name</a>
      </div>
    );
  

优点

  • 允许使用熟悉的语法来定义 html 元素树;

  • 提供更加语义化且移动的标签;

  • 程序结构更容易被直观化

  • 抽象了 React Element 的创建过程;

  • 可以随时掌控 HTML 标签以及生成这些标签的代码;

  • 是原生的 JavaScript。

  1. React是啥


React是一个简单的javascript UI库,用于构建高效、快速的用户界面

它是一个轻量级库,因此很受欢迎。它遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效。

它使用虚拟DOM来有效地操作DOM。

它遵循从高阶组件到低阶组件的单向数据流。

  1. 常见的hook


  • 状态钩子 (useState): 用于定义组件的 State,类似类定义中 this.state 的功能

  • 生命周期钩子 (useEffect): 类定义中有许多生命周期函数,而在 React Hooks 中也提供了一个相应的函数 (useEffect),这里可以看做componentDidMount、componentDidUpdate和componentWillUnmount的结合。

  • useContext: 获取 context 对象,不需要死板的一层一层的传递数据

  • useCallback: 缓存回调函数,避免传入的回调每次都是新的函数实例而导致依赖组件重新渲染,具有性能优化的效果;

  • useMemo: 用于缓存传入的 props,避免依赖的组件每次都重新渲染;

  • useRef: 获取组件的真实节点【常用一点】或者其他的值,比较灵活;[class里面如果把 refs 放到 React 组件中,那么我们获得的就是组件的实例]

  • useReducer 是useState的复杂版本,和redux类似


  1. react-router里的<Link>和<a>标签的区别


对比 标签, Link 避免了不必要的重新渲染。【Link 的 “跳转” 行为只会触发相匹配的对应的页面内容更新,而不会刷新整个页面】

而 a 标签就是普通的超链接了,用于从当前页面跳转到href指向的另一个页面(非锚点情况)。


  1. redux


核心描述

  • 单一数据源:整个应用的全局 state 被存储在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

  • State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事情的普通对象。

  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree,你需要编写纯的 reducers。

知识拓展

  • 什么时候应该使用 redux:

  • 在应用的大量地方,都存在大量的状态

  • 应用状态会随着时间的推移而频繁更新

  • 更新该状态的逻辑可能很复杂

  • 中型和大型代码量的应用,很多人协同开发

  • reducer 是一个函数,接收当前的 state 和一个 action 对象,必要时决定如何更新状态,并返回新状态。reducer 必须符合以下规则:

  • 仅使用 state 和 action 参数计算新的状态值

  • 禁止直接修改 state。必须通过复制现有的 state 并对复制的值进行更改的方式来做不可变更新

  • 禁止任何异步逻辑、依赖随机值或导致其他副作用代码

  • reducer 遵守上述规则的原因:

  • redux 的目标之一是使代码可预测。当函数的输出仅根据输入参数计算时,更容易理解该代码的工作原理并对其进行测试

  • 如果一个函数依赖于自身之外的变量,或者随机行为,你永远不知道运行它时会发生什么

  • 如果一个函数 mutate 了其他对象,比如它的参数,这可能会意外地改变应用程序的工作方式。这可能是错误的常见来源

  • 不可变更新(Immutability),不能在 Redux 中更改 state 的原因:

  • 会导致bug,例如 UI 未正确更新以显示最新值

  • 更难理解状态更新的原因和方式

  • 编写测试变的困难

  • 打破了正确使用“时间旅行调试”的能力

  • 违背了 Redux 的预期精神和使用模式


自定义hooks和普通函数的区别

  • 官方提供的Hooks只应该在React函数组件/自定义Hooks内调用,而不应该在普通函数调用

  • 自定义Hooks能够调用诸如useState、useRef等,普通函数则不能。由此可以通过内置的Hooks获得Fiber的访问方式,可以实现在组件级别存储数据的方案等。

  • 自定义Hooks需要以use开头,普通函数则没有这个限制。使用use开头并不是一个语法或者一个强制性的方案,更像是一个约定,就像是GET请求约定语义不携带Body一样,使用use开头的目的就是让React识别出来这是个Hooks,从而检查这些规则约束,通常也会使用ESlint配合eslint-plugin-react-hooks检查这些规则,从而提前避免错误的使用。

如果我们使用Hooks的话,实际上由于可以调用useState、useRef等Hooks,从而获取了对于这个Fiber的访问方法,那么也就相当于我们可以将状态或者说数据存放于当前节点当中,而不是类似于普通函数在全局中共享当然如果需要全局共享状态的话,状态管理方案是更好的选择,而不是全局变量。


  1. setState是同步的还是异步 未完

https://mp.weixin.qq.com/s/GQQlE5WD9xzExixZhjMKTw


先说结论,首先,同步和异步主要取决于它被调用的环境

  • 如果 setState 在 React 能够控制的范围被调用,它就是异步的。

比如合成事件处理函数, 生命周期函数, 此时会进行批量更新, 也就是将状态合并后再进行 DOM 更新。

  • 如果 setState 在原生 JavaScript 控制的范围被调用,它就是同步的。

比如原生事件处理函数中, 定时器回调函数中, Ajax 回调函数中, 此时 setState 被调用后会立即更新 DOM 。

为什么会这样呢?

其实,我们看到的所谓的 “异步”,是开启了 “批量更新” 模式的。

批量更新模式可以减少真实 DOM 渲染的次数,所以只要是 React 能够控制的范围,出于性能因素考虑,一定是批量更新模式。批量更新会先合并状态,再一次性做 DOM 更新。

react18之后。

setState都会表现为异步(即批处理)。


react的性能优化


8.4JSX本质是什么


JSX 是一个 JavaScript 的语法扩展,它看起来像是一种模板语言,但它具有 JavaScript 的全部功能。


React.JSX转换成真实DOM过程


JSX通过babel最终转化成React.createElement这种形式。返回虚拟DOM(vnode)

在转化过程中,babel在编译时会判断 JSX 中组件的首字母:

  • 当首字母为小写时,其被认定为原生 DOM 标签,createElement 的第一个变量被编译为字符串

  • 当首字母为大写时,其被认定为自定义组件,createElement 的第一个变量被编译为对象

虚拟DOM会通过ReactDOM.render进行渲染成真实DOM,使用方法如下:

ReactDOM.render(<App />,  document.getElementById("root"));

14.1关于react hooks


14.2函数组件的特点



14.4useEffect生命周期模拟

DIdMount || WillUnMount

DidUpdate


fetch...直接放在Fetch组件内部,而不是用useEffect包裹起来也是可以的

但是!

理论上可以,useeffect本身是一个副作用,直接在函数里面进行这样的一个副作用,问题是很大的。因为组件随时都有可能去更新,比如副组件的更新会导致子租件更新,或者内部一些状态的更新导致组件的变化,或者props的变化等等。每一次更新,这些东西(fetch里面)都是要再次执行的,这样肯定不行啊。

所以上面console.log打印的次数是7次,放在useEffect里面只会打印2次。

这个是为什么呢?

useEffect(()=>)

这样写执行两次:在组件每一次更新的时候都会去执行,第一次更新是初始的渲染(相当于didmount),第二次是setResult更新完了之后渲染(相当于didUpdate)

useEffect(()=>,[])

这样写只会执行一次,后面不会再执行(相当于didmount)


总结:

class 组件

Hooks 组件

constructor

useState

getDerivedStateFromProps

useEffect 手动对比 props, 配合 useState 里面 update 函数

shouldComponentUpdate

React.memo

render

函数本身

componentDidMount

useEffect 第二个参数为[]

componentDidUpdate

useEffect 配合useRef

componentWillUnmount

useEffect 里面返回的函数

componentDidCatch

getDerivedStateFromError


14.12使用规范


ps: 不能在一起可能被打断的程序后面,如上面所示


14 13 为何Hooks要依赖于调用顺序

如何不按照这样的规则可能发生一些错乱


react-router的理解


react-router可以实现无刷新的条件下:改变浏览器地址栏中的URL地址,切换显示不同的页面,

两种模式:

  • history 模式:允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录

这两种模式对应的组件为:BrowserRouter、HashRouter: 作为最顶层组件包裹其他组


react-router的使用


Route用于路径的匹配,然后进行组件的渲染,

  • path 属性:用于设置匹配到的路径

  • component 属性:设置匹配到路径后,渲染的组件

  • render 属性:设置匹配到路径后,渲染的内容

  • exact 属性:开启精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件

import  BrowserRouter as Router, Route  from "react-router-dom";

export default function App() 
  return (
    <Router>
      <main>
        <nav>
          <ul>
            <li>
              < a href="/">Home</ a>
            </li>
            <li>
              < a href="/about">About</ a>
            </li>
            <li>
              < a href="/contact">Contact</ a>
            </li>
          </ul>
        </nav>
        <Route path="/" render=() => <h1>Welcome!</h1> />
      </main>
    </Router>
  );

Link、NavLink:

路径的跳转是使用Link组件,最终会被渲染成a元素.其中属性to代替a标题的href属性,

NavLink是在Link基础之上增加了一些样式属性,例如组件被选中时,发生样式变化,则可以设置NavLink的一下属性:

  • activeStyle:活跃时(匹配时)的样式

  • activeClassName:活跃时添加的class

<NavLink to="/" exact activeStyle=color: "red">首页</NavLink>
<NavLink to="/about" activeStyle=color: "red">关于</NavLink>
<NavLink to="/profile" activeStyle=color: "red">我的</NavLink>

redirect

用于路由的重定向

switch

swich组件的作用适用于当匹配到第一个组件的时候,后面的组件就不应该继续匹配

<Switch>
  <Route exact path="/" component=Home />
  <Route path="/about" component=About />
  <Route path="/profile" component=Profile />
  <Route path="/:userid" component=User />
  <Route component=NoMatch />
</Switch>

如果不使用switch组件进行包裹,相同 path 的就会被匹配到,然后一起展示。

除了一些路由相关的组件之外,react-router还提供一些hooks,如下:

  • useHistory

  • useParams

  • useLocation


react-router的参数传递

这些路由传递参数主要分成了三种形式:

  • 动态路由的方式

  • search传递参数

  • to传入对象

动态路由

动态路由的概念指的是路由中的路径并不会固定

例如将path在Route匹配时写成/detail/:id,那么 /detail/abc、/detail/123都可以匹配到该Route

<NavLink to="/detail/abc123">详情</NavLink>

<Switch>
    ... 其他Route
    <Route path="/detail/:id" component=Detail/>
    <Route component=NoMatch />
</Switch>

获取参数方式如下:

console.log(props.match.params.xxx)

search传递参数

在跳转的路径中添加了一些query参数;

<NavLink to="/detail2?name=why&age=18">详情2</NavLink>

<Switch>
  <Route path="/detail2" component=Detail2/>
</Switch>

获取形式如下:

console.log(props.location.search)

to传入对象

传递方式如下:

<NavLink to=
    pathname: "/detail2", 
    query: name: "kobe", age: 30,
    state: height: 1.98, address: "洛杉矶",
    search: "?apikey=123"
  >
  详情2
</NavLink>

获取参数的形式如下

console.log(props.location)

react-router的原理

不会导致浏览器向服务器发送请求,就不会刷新页面:

原理【hash】:hash模式路由就是利用 hashchange 事件监听 URL 的变化,从而进行 DOM 操作来模拟页面跳转

原理【router】:通过props传进来的pathcontext传进来的pathname进行匹配,然后决定是否执行渲染组件


React 18的新特性

1.批处理: react18以前批处理只限于 React 原生事件内部的更新。React 18批处理支持处理的操作范围扩大了:Promise,setTimeout,native event handlers 等这些非 React 原生的事件内部的更新也会得到合并:

// Before: only React events were batched.

setTimeout(() => 
  setCount((c) => c + 1);

  setFlag((f) => !f);

  // React will render twice, once for each state update (no batching)
, 1000);

// After: updates inside of timeouts, promises,

// native event handlers or any other event are batched.

setTimeout(() => 
  setCount((c) => c + 1);

  setFlag((f) => !f);

  // React will only re-render once at the end (that's batching!)
, 1000);

2.Transitions

Transitions 是 React 中一个用于区分高优更新和非高优更新的新概念。

starTransition:用于标记非紧急的更新,用 starTransition 包裹起来就是告诉 React,这部分代码渲染的优先级不高,可以优先处理其它更重要的渲染。

import  startTransition  from "react";

// Urgent
setSliderValue(input);

// Mark any state updates inside as transitions
startTransition(() => 
  // Transition: Show the results, non-urgent
  setGraphValue(input);
);

useTimeout 实现

function useTimeout(callback, delay) 
  const memorizeCallback = useRef();

  useEffect(() => 
    memorizeCallback.current = callback;
  , [callback]);

  useEffect(() => 
    if (delay !== null) 
      const timer = setTimeout(() => 
        memorizeCallback.current();
      , delay);
      return () => 
        clearTimeout(timer);
      ;
    
  , [delay]);
;

如何使用

  // callback 回调函数, delay 延迟时间
  useTimeout(callback, delay);

react hooks中如何捕获异常

-在React项目中,因为事件处理程序总是需要写 try/catch,不胜其烦。

-虽然可以丢给window.onerror或者 window.addEventListener("error")去处理,但是对错误细节的捕获以及错误的补偿是极其不友好的。

-实现: 其基本原理就是利用 useMemo和之前封装的observerHandler

export function useCatch<T extends (...args: any[]) => any>(callback: T, deps: DependencyList, options: CatchOptions =DEFAULT_ERRPR_CATCH_OPTIONS): T     

    const opt =  useMemo( ()=> getOptions(options), [options]);
    
    const fn = useMemo((..._args: any[]) => 
        const proxy = observerHandler(callback, undefined, function (error: Error) 
            commonErrorHandler(error, opt)
        );
        return proxy;

    , [callback, deps, opt]) as T;

    return fn;

redux使用的原则

  • 单一数据源:整个应用的全局 state 被存储在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

  • State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事情的普通对象。

  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree,你需要编写纯的 reducers。


react的事件代理机制

web前端面试题记录

记录了2017年5月下旬刚毕业时面试的经典面试题

布局方面

1. 响应式布局,左侧栏目固定,右侧内容随着屏幕宽度变化而变化(高频)

  • flex布局
  • position布局
  • css3计算宽度
  • float布局

flex布局

 

// html
<div class="box">
    <div class="left"></div>
    <div class="right"></div>
</div>

// css

.box {
  display: flex;
}
.left {
  width: 200px;
}
.right {
  flex: 1;
}

 

右侧div设置flex:1  自动填充满容器。

position布局

//    html

<div class="box">
  <div class="left"></div>
</div>

// css

.box {
  padding-left: 200px;
  width: 100%;
  position: relative;  
}
.left {
  position: absolute;
  width: 200px;
  left: 0;
}

用pading将要显示的右侧内容挤到右边,常用在图文列表

css3计算宽度

// html

<div class="box">
    <div class="left"></div>
    <div class="right"></div>
</div>

// css

.left {
    float: left;
    width: 200px;
}
.right {
    float: left;
    width: calc(100% - 200px);
}

通过css3的calc函数可以计算宽度来定义宽度

float布局(面试官想要的答案)

 

// html

<div class="box">
    <div class="left"></div>
    <div class="right">
      <div class="inner"></div>
    </div>
</div>

// css

.left {
  float: left;
  width: 200px;
  margin-right: -200px;
}
.right {
  float: left;
  width: 100%;
}
.inner {
  margin-left: 200px;
}

根据float元素的margin特性布局,兼容性好。以上css都没有给出高度和颜色区分。

javascript方面

1. 闭包和作用域、this的理解

2. 原型链有关的问题

3. es6方面:let块级作用域、generator函数的应用

4. javascript中的setTimeout、promise异步的考查

5. jQuery中的设计模式

  • 原型模式  : 整个jQuery库的构造就是一个原型继承的模式。
  • 发布/订阅模式:事件监听模块为发布订阅模式
  • 代理模式:jQuery中内置proxy方法便是代理模式
  • 外观模式:post、get等方法是对ajax的包装
  • 等等

6. jsonp的实现原理

js算法技巧方面

1. a[n] 数组中取值是 [1, n-1] ,也就是必然有重复数字,在时间复杂度和空间复杂度最小的情况下找出一个重复数字

博主也不懂复杂度,用正则写了个, a[n].toString().match(/(\\d+).*?\\1/)[1]

2. 两个单向链表的交点

博主懵逼,不懂数据结构不知啥叫链表交点。后来查了下就是两个链表成Y状,相交后必定后面的数据一样。这就不难了。

3. 给定一个 ram函数,该函数有50%几率返回0 和 50%几率返回1,根据这个ram函数写一个ran函数,ran函数有25%几率返回0 1 2 3。

博主脑子转不快,很慢很慢才理清楚这个简单的题,很尴尬。

http方面

1.  在浏览器输入一个网址到页面呈现,计算机做了哪些事情。

在一家公司的CTO问的,尴尬了,之前博主故意百度看了一遍这个问题,结果也是忘得一干二净。

在前端层面上就是 发送请求资源 - 建立连接 -  数据传输 - 解析数据

有很多大神写了完整过程: http://blog.csdn.net/xingxingba123/article/details/52743335      http://www.cnblogs.com/webhb/p/5615063.html

2. put和post请求的区别

一般情况我们用post请求来插入一条数据,用put请求更新一条数据。插入与更新的区别。。。

3. cookie和localStorage、sessionStorage的区别

cookie存储量小,存储数据小,跟随着http请求传输。

 

几次面试的总结,希望尽快掌握,下一次面试表现好一些


以上是关于web面试题记录之react的主要内容,如果未能解决你的问题,请参考以下文章

web前端面试题记录

2021年最新Web前端面试题精选大全及答案

Web前端面试题布局之02

Web前端面试题框架之03

web前端面试题@十九(怎么判断用户是不是处于登陆状态?)

前端面试高频React题及详解,另附有React面试题集合