前端面试题React18考点

51 篇文章 14 订阅
14 篇文章 0 订阅

合集:2023年最全前端面试题考点HTML5+CSS3+JS+Vue3+React18+八股文+手写+项目+笔试_参宿7的博客-CSDN博客

⭐表示手写 和 重要程度,*表示了解即可

框架为一二面,面试官尤其喜欢问为什么要用+怎么用

为了简洁,相关文章参考链接在标题里

目录

React18

组件属性

构建组件的方式⭐⭐

props

状态提升⭐⭐

state

refs

总结

生命周期

状态提升⭐⭐

复用组件:Hooks之前复用组件

Render Props模式

HOC高阶组件模式⭐⭐

纯函数

不更改在调用前的对象或变量(如果改变了,就叫副作用)

输入相同,则输出相同

未更改的组件来 跳过渲染,以提高性能

Hooks

Router

路由模式(同Vue)

基础路由搭建

重定向路由

自定义全局守卫

动态路由

Redux状态管理库⭐⭐

React18新增特性⭐⭐

React脚手架

MVC、MVP、MVVM模式⭐⭐⭐

MVC (Model View Controller)

MVP(Model View Presenter)

MVVM (Model View View Model)

vue : MVVM 框架,由 MVC 发展而来

React :前端组件化框架,不是一个完整的MVC框架,可以认为是MVC中的V(View)


React18

React是用来构造用户界面的JS库 

构建组件的方式⭐⭐

React.createClass()、ES6 class 、无状态函数

props

对外公开属性,只读

传递数据

<body>
    <div id = "div"></div>
</body>

<script type="text/babel">

// 函数组件
function Welcome(props){
    return (
        <div>hello world, {props.msg}</div>
    );  
}
let element = (
    <Welcome msg="hi react" />
);
 
// 类组件
class Person extends React.Component{
        render(){
            return (
                <ul>
                    <!--接受数据并显示-->
                    <li>{this.props.name}</li>
                    <li>{this.props.age}</li>
                    <li>{this.props.sex}</li>
                </ul>
            )
        }
}

const p = {name:"张三",age:"18",sex:"女"}
ReactDOM.render(<Person {...p}/>,document.getElementById("div"));
//上下等同
ReactDOM.render(<Person name="tom" age = "41" sex="男"/>,document.getElementById("div"));
</script>

传递函数

// 子组件
class Head extends React.Component {
    render(){
        this.props.getData('子组件的问候~~~')
        return (
            <div>Head Component</div>
        );
    }
}
// 父组件
class Welcome extends React.Component {
    getData = (data) => {
        console.log(data)
    }
    render(){
        return (
            <div>
                hello world, {this.props.msg}
                <br />
                <Head getData={this.getData} />
            </div>
        );
    }
}

构造函数获取props

class Foo {
    constructor(props){
        this.props = props;
    }
}
class Bar extends Foo {
    constructor(props){
        super(props);
        console.log(this.props);
    }
    render(){
        console.log(this.props);
        return '';
    }
}
let props = {
    msg: 'hello world'
};
let b = new Bar(props);
b.props = props;
b.render();

多属性传递props

class Welcome extends React.Component {
    render(){
        //解构
        let { msg, username, age } = this.props;
        console.log( isChecked );
        return (
            <div>hello world, {msg}, {username}, {age}</div>
        );
    }
}
let info = {
    msg: 'hi react',
    username: 'xiaoming',
    age: 20
};
let element = (
    <Welcome {...info} />
);

设置props初始值和类型

import PropTypes from 'prop-types'
class Welcome extends React.Component {
    static defaultProps = {
        age: 0
    }
    static propTypes = {
        age: PropTypes.number
    }
    ...
}

状态提升⭐⭐

共享状态提升到最近的共同父组件中去,在父组件上改变状态,然后通过props分发给子组件。

state

组件的私有属性,值是对象(可以包含多个key-value的组合)

通过state的变化设置响应式视图受控于当前组件

class Welcome extends React.Component {
    state = {
        msg: 'hello',
        count: 0
    }
    handleClick = () => {   
        this.setState({
            msg: 'hi'
        });
    }
    render(){
        console.log('render');
        return (
            <div>
                <button onClick={this.handleClick}>点击</button>
                {this.state.msg}, {this.state.count}
            </div>
        );
    }
}
let element = (
    <Welcome />
);

refs

React操作原生DOM跟Vue框架是类似的,都是通过ref属性来完成的

class Welcome extends React.Component {
    myRef = React.createRef()
    handleClick = () => {   
        //console.log(this.myRef.current);  // 原生DOM input
        this.myRef.current.focus();//获取焦点
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick}>点击</button>
                <input type="text" ref={this.myRef} />
            </div>
        );
    }
}

总结

props


公开,单向数据流值,父子组件间的唯一通信不可改

1.每个组件对象都会有props(properties的简写)属性

2.组件标签的所有属性都保存在props中

3.内部读取某个属性值:this.props.propertyName

这有助于维护单向数据流,通常用于呈现动态生成的数据

state


私有(通过Ajax获取回来的数据,一般都是私有数据),

React 把组件看成是一个状态机(State Machines),

只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。


refs


当需要获取某一个真实的DOM元素来交互,比如文本框的聚焦、触发强制动画等

当需要操作的元素和获取的元素是同一个时,无需ref鼠标事件 mouseenter与mouseover区别

mouseenter: 鼠标进入被绑定事件监听元素节点时触发一次,再次触发是鼠标移出被绑定元素,再次进入时。而当鼠标进入被绑定元素节点触发一次后没有移出,即使鼠标动了也不再触发

mouseover: 鼠标进入被绑定事件监听元素节点时触发一次,如果目标元素包含子元素,鼠标移出子元素到目标元素上也会触发。

mouseenter 不支持事件冒泡 mouseover 会冒泡

生命周期

生命周期钩子函数就是回调函数

挂载

  • constructor():在 React 组件挂载之前,会调用它的构造函数。(注:如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。)
  • render(): class 组件中唯一必须实现的方法。
  • componentDidMount():在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。

更新

  • render(): class 组件中唯一必须实现的方法。一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染
  • componentDidUpdate():在更新后会被立即调用。首次渲染不会执行此方法。

卸载

  • componentWillUnmount():在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
     

可以看到挂载时和更新时都有render这个方法。这就是为什么state改变的时候,会触发render重渲染操作

class Welcome extends React.Component {
    state = {
        msg: 'hello world'
    }
    constructor(props){
        super(props);
        console.log('constructor');
    }
    componentDidMount = () => {
        // react中发起ajax请求的初始操作,在这个钩子中完成
        console.log('componentDidMount');
    }
    componentDidUpdate = () => {
        // 等DOM更新后触发的钩子
        console.log('componentDidUpdate');
    }
    componentWillUnmount = () => {
        console.log('componentWillUnmount');
    }
    handleClick = () => {  
        /* this.setState({
          msg: 'hi react'
        }); */
        //this.forceUpdate();
        root.unmount();   // 触发卸载组件
    }
    render(){
        console.log('render');
        return (
            <div>
                <button onClick={this.handleClick}>点击</button>
                { this.state.msg }
            </div>
        );
    }
}

状态提升⭐⭐

状态是 React 组件的核心,是数据的来源,必须尽可能简单。

基本上状态是确定组件呈现和行为的对象。

与 Props 不同,它们是可变的,并创建动态和交互式组件。可以通过this.state() 访问它们。

多个组件需要共享状态提升到它们最近的父组件上,在父组件上改变这个状态然后通过props分发给子组件。对子组件操作,子组件不改变自己的状态。

复用组件:Hooks之前复用组件

React Router也用到了这种复用组件。但这两种模式,会增加代码的层级关系

Render Props模式

组件之间使用一个值为函数prop 共享代码的简单技术。

class MouseXY extends React.Component {
    state = {
        x: 0,
        y: 0
    }
    componentDidMount = () => {
        document.addEventListener('mousemove', this.move)
    }
    componentWillUnmount = () => {
        document.removeEventListener('mousemove', this.move)
    }
    move = (ev) => {
        this.setState({
            x: ev.pageX,
            y: ev.pageY
        });
    }
    render(){
        return (
            <React.Fragment>
                { this.props.render(this.state.x, this.state.y) }
            </React.Fragment>
        );
    }
}
class Welcome extends React.Component {
    render(){
        return (
            <MouseXY render={(x, y)=> 
               <div>
                  hello world, {x}, {y}
               </div>
            } />
        );
    }
}
let element = (
    <Welcome />
);

render属性后面的值是一个回调函数,通过这个函数的形参可以得到组件中的数据,从而实现功能的复用。

HOC高阶组件模式⭐⭐

参数组件返回值新组件函数

function withMouseXY(WithComponent){
    return class extends React.Component {
        state = {
            x: 0,
            y: 0
        }
        componentDidMount = () => {
            document.addEventListener('mousemove', this.move)
        }
        componentWillUnmount = () => {
            document.removeEventListener('mousemove', this.move)
        }
        move = (ev) => {
            this.setState({
                x: ev.pageX,
                y: ev.pageY 
            })
        }
        render(){
            return <WithComponent {...this.state} />
        }
    }
}
class Welcome extends React.Component {
    render(){
        return (
            <div>
                hello world, { this.props.x }, { this.props.y }
            </div>
        );
    }
}
const MouseWelcome = withMouseXY(Welcome)
let element = (
    <MouseWelcome />
);

纯函数

不更改在调用前的对象或变量(如果改变了,就叫副作用)
输入相同,则输出相同
未更改的组件来 跳过渲染,以提高性能

Hooks

自定义Hook

实现函数组件复用

let { useState, useEffect } = React
let useMouseXY = () => {
    const [x, setX] = useState(0)
    const [y, setY] = useState(0)
    useEffect(()=>{
        function move(ev){
            setX(ev.pageX)
            setY(ev.pageY)
        }
        document.addEventListener('mousemove', move)
        //如果返回回调函数:相当于componentWillUnmount,会在组件卸载的时候执行清除操作。
        return () => {
            document.removeEventListener('mousemove', move)
        }
    }, [])//如果传空数组:相当于componentDidMount
    return {
        x,
        y
    }
}
let Welcome = ()=>{
    const {x, y} = useMouseXY()
    return (
        <div>
            hello Welcome, {x}, {y}
        </div>
    )
}

Router

路由是根据不同的url地址展示不同的内容或页面,是SPA(单页应用)的路径管理器

用于开发单页 Web 应用

1.一个路由就是一个映射关系(key:value)

2.key为路径, value可能是function或component

Router 用于定义多个路由,当用户定义特定的 URL 时,如果此 URL 与 Router 内定义的任何 “路由” 的路径匹配,则用户将重定向到该特定路由。

路由模式(同Vue)

React中的路由模式跟Vue中一样,分为history和hash模式。默认是hash

Hash模式

hash——即地址栏URL中的#符号(此hash不是密码学里的散列运算)。比如在 http://localhost:8080/#/donate 中,hash的值就是#/donate,我们在浏览器中可以通过BOM中的window.location.hash来获取当前URL的hash值

注:BOM(Browser Object Model) 是指浏览器对象模型,BOM由多个对象组成,其中代表浏览器窗口的Window对象是BOM的顶层对象,其他对象都是该对象的子对象。

history模式

通过在host后,直接添加斜线路径请求后端的一种URL模式

图中pathname变量为/donate,而hash值为空。

原理

  • hash通过window.addEventListener监听浏览器的onhashchange()事件变化,查找对应的路由规则
  • HTML5history API监听URL变化,所以有浏览器兼容问题

区别

  • 是否向后端传参:

    在hash模式中,我们刷新一下上面的网页,可以看到请求的URL为http://localhost:8080/,没有带上#/donate,说明hash 虽然出现 URL 中,#后面的内容是不会包含在http请求中的,对后端完全没有影 响,因此改变 hash 不会重新加载页面。

在history模式中,刷新一下网页,明显可以看到请求url为完整的url,url完整地请求了后端:

前端的 URL 必须和实际向后端发起请求的 URL 一致,否则返回 404 错误

 

在React中

  • history模式:createBrowserRouter

IE9及以下不兼容,需要由web server支持,在web client这边window.location.pathnamereact router解析

  • hash模式:createHashRouter

不需要由web server支持,因为它的只有‘/’path需要由web server支持,而#/react/route URL不能被web server读取,在web client这边window,location.hashreact router解析

history的好处是可以进行修改历史记录,并且不会立刻像后端发起请求。不过如果对于项目没有硬性标准要求,可以直接使用hash模式开发。

基础路由搭建

import { createBrowserRouter, createHashRouter } from 'react-router-dom'
//路由表
export const routes = [];
//路由对象
const router = createBrowserRouter(routes);
export default router;

接下来让路由配置文件与React结合,需要在主入口index.js进行操作,如下:

import { RouterProvider } from 'react-router-dom'
import router from './router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router}></RouterProvider>
  </React.StrictMode>
);

路由表的配置字段如下:

  • path:指定路径

  • element:对应组件

  • children:嵌套路由

//路由表
export const routes = [
    {
        path: '/',
        element: <App />,
        children: [
            {
                path: '',
                element: <Home />
            },
            {
                path: 'about',
                element: <About />,
                children: [
                    {
                        path: 'foo',
                        element: <Foo />,
                    },
                    {
                        path: 'bar',
                        element: <Bar />,
                    }
                ]
            }
        ]
    }
];

接下来就是显示路由区域,利用<outlet>组件占位,表明子路由渲染的位置

import React from "react";
import { Outlet, Link } from 'react-router-dom'
function App() {
  return (
    <div className="App">
      <h2>hello react</h2>
      <Link to="/">首页</Link> | <Link to="/about">关于</Link>
      <Outlet />
    </div>
  );
}
export default App;

可以看到 <Link>组件用于声明式路由切换使用。同样<outlet>组件也可以给嵌套路由页面进行使用,从而完成二级路由的切换操作。

import React from 'react'
import './About.scss'
import { Outlet, Link } from 'react-router-dom'
export default function About() {
  return (
    <div>
      <h3>About</h3>
      <Link to="/about/foo">foo</Link> | <Link to="/about/bar">bar</Link>
      <Outlet />
    </div>
  )
}
​

重定向路由

访问的URL跳转到另一个URL上,从而实现重定向的需求。

<Navigate>组件就是实现重定向需求的组件。

import { createBrowserRouter, createHashRouter, Navigate } from 'react-router-dom'
children: [ 
    {
        index: true,// 默认路由
        element: <Navigate to="/about/foo/123" />,
    },
    {
        path: 'foo',
        element: <Foo />
    },
    {
        path: 'bar',
        element: <Bar />
    }
 ]

自定义全局守卫

全局守卫:包裹根组件,访问根组件下面的所有子组件都要先通过守卫进行操作

在React里面,它不像Vue一样,为我们提供和很多方便的功能,一些功能都需要自己去进行封装比如说路由拦截

在/src/components/BeforeEach.jsx下创建守卫的组件。

import React from 'react'
import { Navigate } from 'react-router-dom'
import { routes } from '../../router';
export default function BeforeEach(props) {
  if(true){
    return <Navigate to="/login" />
  }
  else{
    return (
      <div>{ props.children }</div>
    )
  }
}

根据判断的结果,是否进入到组件内,还是重定向到其他的组件内。

调用BeforeEach.jsx,index.js通过路由配置文件引入,如下:

export const routes = [
  {
    path: '/',
    element: <BeforeEach><App /></BeforeEach>//包裹根组件APP
  }
]

动态路由

根据不同的URL,可以访问同一个组件。在React路由中,通过path字段来指定动态路由的写法。

foo/xxx都能访问到Foo组件,本身就有实现了动态路由

import { Outlet, Link } from 'react-router-dom'
export default function About() {
	return (
        <div>
            <Link to="/about/foo/123">foo 123</Link> | <Link to="/about/foo/456">foo 456</Link>
        </div>
   	)
}
{
    path: 'foo/:id',
    element: <Foo />
}

id就是变量名,可以在组件中用useParams来获取到对应的值。

import { useParams } from 'react-router-dom'
export default function Foo() {
  const params = useParams()
  return (
    <div>Foo, { params.id }</div>
  )
}

Redux状态管理库​​​​​​​⭐⭐

React18新增特性⭐⭐

React脚手架

开始一个react项目,不用手动配置,直接开发

MVC、MVP、MVVM模式⭐⭐⭐

MVC (Model View Controller)

  • Model(模型):提供数据
  • View(视图):显示数据
  • Controller(控制器):用户交互

【优点】

耦合性低,方便维护,可以利于分工协作 重用性高

【缺点】

使得项目架构变得复杂,对开发人员要求高

  • key 对 DOM 操作的代价非常高
  • 程序运行缓慢且效率低下
  • 内存浪费严重
  • 由于循环依赖性,组件模型需要围绕 models 和 views 进行创建

MVP(Model View Presenter)

从MVC演变而来,它们的基本思想有相通的地方Controller/Presenter负责逻辑的处理,

MVVM (Model View View Model)

视图业务逻辑分开。

View视图层Model 数据模型ViewModel 是它们双向绑定的桥梁,自动同步更新

【优点】

相比mvp各层的耦合度更低,一个viewmodel层可以给多个view层共用(一对多),提高代码的可重用性

*耦合度:模块间依赖的程度。

【缺点】

因为使用了dataBinding,增加了大量的内存开销,增加了程序的编译时间,所以适合轻量级项目。

数据绑定使得 Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题

vue : MVVM 框架,由 MVC 发展而来

React :前端组件化框架,不是一个完整的MVC框架,可以认为是MVC中的V(View)

React单向数据流:只能由数据层的变化去影响视图层的变化

Vue双向数据绑定

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值