• 前端面试-React专题


    一.React

    1. React 的主要特性是什么

    • 虚拟 DOM(VirtualDOM) 替代了真实 DOM(RealDOM)
    • 支持服务端渲染
    • 遵循单向数据流或数据绑定
    • 使用可复用 / 可组合的 UI 组件来进行视图开发

    2.React 的核心思想是

    封装组件,各个组件维护自己的状态和UI,当状态变更,自动重新渲染整个组件

    3. 什么是jsx

    (JavaScript XML)实际上,它只是为 React.createElement() 函数提供语法糖,为我们提供了在 JavaScript 中使用类 HTML 模板语法的能力。

    原理:jsx语法最终会被babel编译成为React.createElement()方法,createElement方法会生成虚拟的DOM节点对象,再由ReactDOM.render()函数生成真实DOM,插入到对应节点中去

    //jsx
    <div className="wang.haoyu">hellodiv>
    
    • 1
    • 2

    经过bale编译之后的:

    React.createElement("div", {
        className:'wang.haoyu'
    }, "hello");
    
    • 1
    • 2
    • 3

    render()生成真实Dom

    ReactDOM.render(<App />, document.getElementById('root'));
    
    • 1

    4. render()函数

    1. 一个虚拟节点就是一个对象:
      下面输出的对象就是createElement()方法的返回值
      在这里插入图片描述
    2. 转化为真实Dom:
      // 真正渲染方法
      function render(vDom, el) {
        const newDom = createDom(vDom);
        el.appendChild(newDom);
      }
      
      // 先不考虑自定义组件
      function createDom(vDom) {
        const { type, props } = vDom;
        let dom;
        // 文本节点
        if (type === REACT_TEXT) {
          dom = document.createTextNode(props.content);
        } else {
          dom = document.createElement(type);
        }
        // 更新属性
        if (props) {
          // 更新跟节点Dom属性
          ....
        }
        // 记录挂载节点
        vDom.__dom = dom;
        return dom;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25

    5. 函数组件和类组件的区别

    1. 语法上: 类组件需要去继承 React.Component需要更多的代码;
    2. 状态管理: 函数组件是一个纯函数,无状态组件;类组件可以直接使用setSate()
    3. 生命周期钩子: 函数组件中不能使用生命周期钩子,所有的生命周期钩子都来自于继承的 React.Component 中;
    4. 调用方式: 函数组件可以直接调用;如果是一个类组件,需要先用 new操作符将其实例化,然后调用 render 方法
    // 函数组件
    function SayHi() { 
        return <p>Hello, React</p> 
    } 
    // React 内部 
    const result = SayHi(props) // » 

    Hello, React

    // 类组件 class SayHi extends React.Component { render() { return <p>Hello, React</p> } } // React 内部 const instance = new SayHi(props) // » SayHi {} const result = instance.render()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6. HTML和React中事件处理的区别

    HTMLREACT
    写法全小写onclick小驼峰onClick
    阻止默认行为可以直接 return false必须用 event.preventDefault();
    方法调用οnclick=“activateLasers()”onClick={activateLasers}
    传参activateLasers(abc)onClick={(e)=>this.handleClick(e, ‘aa’)} / onClick={this.handleClick.bind(this, ‘aa’)}

    7. React 中的合成事件

    SyntheticEvent是基于浏览器本地事件的跨浏览器包装。它的 API 与浏览器的本地事件相同,包括 stopPropagation() preventDefault(),但事件在所有浏览器中的表现均一致

    8. 虚拟Dom?为什么使用?如何实现?

    虚拟dom: 数据发生更改时,会计算旧的 DOM 与新的 DOM 之间的差异,更新真正改变的那部分到真实的 DOM。

    作用: 虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。

    实现步骤:

    1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;
    2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;
    3. 把新旧Dom树之间差异,应用到真正的 DOM 树上,视图就更新了。
    
    • 1
    • 2
    • 3

    9. 在 constructor 中给 super 函数传递 props 的目的是什么?

    在constructor 方法中,您可以对组件进行初始化,为属性设置默认值,并绑定事件处理程序
    调用 super() 时传递 props 主要是为了拿到 this 的引用,在子类的构造器中访问 this.props

    class MyComponent extends React.Component {
      constructor(props) {
      	//1.
        super(props);
        console.log(this.props); // 打印 { name: 'John', age: 42 }
        
        // 2.但是 props 参数依然可以访问
        super();
        console.log(this.props); // 打印 undefined
        console.log(props); // 打印 { name: 'John', age: 42 }
      }
      //3.在 constructor 之外没有影响
      render() {
        console.log(this.props); // 打印 { name: 'John', age: 42 }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    10.state和props之间的区别

    • state是一种数据结构,用于组件挂载时所需数据的默认值。state是可变的,可以被修改,多数时候作为用户时间行为的结果。
    • props是组件的配置,由父组件传递给子组件,props是不可变的,不能被修改

    11. 什么是React生命周期

    在这里插入图片描述

    钩子函数触发时机作用
    constructor ()创建组件时1,初始化state; 2.为事件处理程序绑定this
    render ()每次组件渲染都会触发渲染UI( 注意不能调用setState() )
    componentDidMount ()组件挂载(完成DOM渲染)后1.发送网络请求; 2.DOM操作
    render()每次组件渲染都会触发渲染UI
    componetDidUpdate()组件更新(完成Dom渲染)后1.发送网络请求;2.Dom操作
    componentWillUnMount()组件卸载之前完成组件的卸载和数据的销毁

    其他钩子:
    shouldCompontentUpdate(nextProps, nextState) 是否要更新组件时触发的函数。
    父组件的重新渲染会导致组件重新渲染,可以通过此钩子函数限制子组件是否需要更新,return false可以阻止组件更新

    12. React中如何使用Hooks

    1. useState 状态管理
    2. useEffect 生命周期管理(函数组件Didmount、DidUpdate、WillUnmount时会触发;也可以监听某个state)
    3. useContext共享状态数据
    4. useMemo缓存值
    5. useRef 获取Dom 操作
    6. seCallback 缓存函数
    7. useReducer redux 相似
    8. useImperativeHandle 子组件暴露值/方法
    9. useLayoutEffect 完成副作用操作,会阻塞浏览器绘制
    // 1.状态钩子 useState
    const [data, setData] = useState('微信公众号: 前端自学社区')
    
    // 2.生命周期钩子 useEffect
    useEffect(() => {
    	//默认会执行  
        // 这块相当于 class 组件 生命周期的 compoentDidmount compoentDidUpdate
        console.log(`num: ${num}`)
        console.log(`count: ${count}`)
        // 组件在卸载时,将会执行 return 中内容
        return () => {
            // 相当于 class 组件生命周期的 componentWillUnMount 
            console.log('测试')
        }
    }, [num])
    
    // 3.传值钩子-数据共享 useContext
    // Context.js
    import React from 'react';
    export const MyContext = React.createContext();
    // index.js
    const Son = () => {
        const res = useContext(MyContext)
        return (<h1>{res.code}</h1>)
    }
    export default () => {
        return (
            <MyContext.Provider value={{code:200,title:'添加数据成功'}}>
               <Son/>
            </MyContext.Provider>
        )
    }
    
    // 4.useMemo
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    13. React的单向数据绑定

    React ,只支持把数据从 state 上传输到页面,但是无法实现数据自动从页面传输到 state 中。

    可以通过change事件修改target.value,实现双向绑定的方法:

    const [msg, setMsg] = useState('xxxxx')
    <input type="text" value={msg} onChange={(e) => {
          setMsg(e.target.value)
    }} />
    
    • 1
    • 2
    • 3
    • 4

    14.什么是flux

    flux最大的特点就是单向数据流

    1. 用户访问view
    2. view发出用户的action
    3. dispatcher收到action,更新store
    4. store更新后,触发change事件
    5. view收到change事件后,更新页面
    
    • 1
    • 2
    • 3
    • 4
    • 5

    15.React中如何进行服务器渲染

    服务端渲染, SSR (Server-side Rendering),顾名思义,就是在浏览器发起页面请求后由服务端完成页面的 HTML 结构拼接,返回给浏览器解析后能直接构建出有内容的页面。
    流程:
    1、服务器端使用 renderToString 直接渲染出包含页面信息的静态 html。
    2、客户端根据渲染出的静态 html 进行二次渲染,做一些绑定事件等操作。

    16.服务端渲染的利弊

    在这里插入图片描述

    17.如何进行React性能优化

    1. 重写 shouldComponentUpdate来避免不必要的 dom 操作。
    2. 使用 production 版本的 React.js。
    3. 使用 key 来帮助 React 识别列表中所有子组件的最小变化

    18.React解决了什么问题

    组件复用问题、性能问题、兼容性问题

    19.React的协议

    React遵循的协议是“BSD许可证 + 专利开源协议”,是否可以自由的使用React,取决于你的产品与FaceBook是否有竞争关系

    20.shouldComponentUpdate生命周期

    在比较新旧虚拟Dom时,如果我们不希望某个组件刷新,或者刷后和之前的一样,可以用这个函数直接告诉React,省去Diff操作,进一步提高效率 。

    22.怎么阻止组件的渲染

    在组件的render方法中返回null。(不会影响触发生命周期方法)

    二、react-router相关

    npm install react-router-dom 安装后,直接引入,并用路由组件包裹根组件

    // index.js
    import ReactDOM from 'react-dom/client'
    // 1. 从 react-router-dom 中引入 HashRouter
    import {HashRouter} from 'react-router-dom'
    import App from './App'
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(
      // 2. 包裹根组件,给整个应用开启路由功能,就能监听路径的改变,并且将相应的路径传递给子组件
      <HashRouter>
        <App />
      </HashRouter>
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.路由跳转

    //js传参
    this.props.history.push('/discover/playlist');
    this.props.match.params.xxx(此处为id)
    this.props.location.query
    
    //umi
    import { history } from 'umi';
    history.push('/' + e.key);
    
    //标签
    <Routes><Route path='/about' element={<About/>} /></Routes>
    <Link to='/about'>关于</Link>
    <NavLink to='/about' className='about-link'>关于</NavLink>
    <Navigate to='/home' /> //重定向
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.路由模式

    • history模式:
    • hash模式:

    PS:两种模式的区别

    • hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件。
    • history利用了 H5 中新增的 pushState()replaceState() 方法。
    • 除了外观上;hash模式#后面的变化并不会导致浏览器向服务器发出请求,不会刷新请求、重新加载页面; history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致
    • hash兼容性会比history 模式好,因为history 模式是依赖HTML5 Web History API,特定浏览器才会支持;
    • hash模式不利SEO,爬虫不会读取到#后的内容,因为会把 # 当作是锚点,这也导致会与网站里的锚点功能产生冲突
    • hash模式,页面刷新后会导致路由state参数丢失
      在这里插入图片描述

    3. Link、NavLink和Navigate

    4.router中的路由钩子函数

    useNavigate():用于在函数组件中进行手动跳转
    useParams():用于在函数组件中获取动态路由的参数
    useSearchParams():用于在函数组件中获取路径中的search(?号后面的)参数
    useLocation():用于在函数组件中获取 location。
    useRoutes():用于在函数组件中通过配置的方式使用路由 {useRoutes(routers)}

    5.路由懒加载

    路由懒加载不是 React Router 提供的功能,而是借助 React 提供的 React.lazy() 来实现。 React.lazy() 需要传入一个函数作为参数,在函数内部可以使用 import() 来异步加载文件。

    const About = React.lazy(() => import('./components/About'))
    
    function App() {
      return (
        <div>
          <div>
            <Link to='/home'>首页</Link>
            <Link to='/about?userId=123'>关于</Link>
          </div>
          <Routes>
            <Route path='/home' element={<Home />} />
            <Route path='/about' element={<About />} />
          </Routes>
        </div>
      )
    }
    
    export default App
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    React Router 基础、组件、钩子函数

    三.Redux相关

    1.使用

    访问:useSelector钩子函数
    调用:在Redux Toolkitreducers中写计算逻辑,在组件中用useDispatch()钩子函数调用

    // 访问
    const count = useSelector(state => state.counter.value)
    
    // 调用
    // 1.在Redux Toolkit中配置
    export const counterSlice = createSlice({
      name: 'counter',
      initialState: {value: 0},
      reducers: {
        increment: (state, action) => {
          state.value += 1
        },
      }
    })
    //2.全局注册 store
    import {configureStore} from '@reduxjs/toolkit'
    import counterReducer from '../features/counter/counterSlice'
    export default configureStore({
      reducer: {
        counter: counterReducer
      }
    })
    // 3.引入 调用dispatch方法,并传入一个action对象
    import {increment} from './counterSlice'
    onClick={
    	() => dispatch(increment({ name: '参数' }))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2.state管理

    • view:基于当前状态的视图声明性描述
    • state:驱动应用的真实数据源头
    • actions:根据用户输入在应用程序中发生的事件,并触发状态更新
    • reducer: reducer 是一个函数,可以将 reducer 视为一个事件监听器,它根据接收到的 action(事件)类型处理事件。

    3.单向数据流

    1. State 描述了应用程序在特定时间点的状况
    2. 基于 state 来渲染视图
    3. 当发生某些事情时(例如用户单击按钮),state 会根据发生的事情进行更新
    4. 基于新的 state 重新渲染视图

    Redux 中文官网

  • 相关阅读:
    Python如何调用ixchariot进行吞吐量测试
    推测肿瘤细胞拷贝数
    【头歌】计算机网络DHCP服务器配置第四关配置路由器子接口答案
    Linux操作系统的基础指令 III
    【C++】——动态内存管理
    数据传输如何做才安全:保障隐私的5大秘诀!
    Java lambda表达式对List的常见操作记录
    远程控制软件
    Visdrone转为YOLO格式
    Kubernetes 架构介绍
  • 原文地址:https://blog.csdn.net/qq_42866164/article/details/130890497