前端知识--React

官方文档

1、什么是React

React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。主要用于构建UI,本质是前端组件化框架,不是一个完整的MVC框架,很多人认为Reatc是MVC中的View(视图)(React是一个js库,不是一个框架。跟Vue有区别

我们认为,React 是用 JavaScript 构建快速响应的大型 Web 应用程序的首选方式。它在 Facebook 和 Instagram 上表现优秀。

从年会看声明式编程

2、React优缺点

(1)优点:
1)声明式设计
2)高效:通过对DOM的模拟,最大限度的减少与DOM的交互。
3)灵活:可以与已知的框架或库很好的配合。
4)JSX:是js语法的扩展,不一定使用,但建议用。
5)组件:构建组件,使代码更容易得到复用,能够很好地应用在大项目的开发中。
6)单向响应的数据流:React实现了单向响应的数据流,从而减少了重复代码,这也是解释了它为什么比传统数据绑定更简单。

3、React的应用

(1)React的生命周期

(2)React官方文档FAQ
1) React不是必须使用JSX
每个 JSX 元素只是调用 React.createElement(component, props, …children) 的语法糖。

//用 JSX 编写的代码
class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}

ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

//不使用 JSX 的代码
class Hello extends React.Component {
  render() {
    return React.createElement('div', null, `Hello ${this.props.toWhat}`);
  }
}

ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

2)React不是必须使用ES6

//es6
class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

//es5  使用 create-react-class 模块
var createReactClass = require('create-react-class');
var Greeting = createReactClass({
  render: function() {
    return <h1>Hello, {this.props.name}</h1>;
  }
});

3)传递函数给组件
注意:在 render 方法中使用箭头函数也会在每次组件渲染时创建一个新的函数,这会破坏基于恒等比较的性能优化。

//方法一:
class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
  }
}

//方法二:
class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={() => this.handleClick()}>Click Me</button>;
  }
}

//可以改用这种方式
class Foo extends Component {
  // Note: this syntax is experimental and not standardized yet.
  handleClick = () => {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

4)组件状态

  1. setState 实际做了什么?state更新后对组件重新渲染

  2. 调用 setState 其实是异步的 —— 不要指望在调用 setState 之后,this.state 会立即映射为新的值,可以尝试传递一个函数而非对象

    //错误
    incrementCount() {
      // 注意:这样 *不会* 像预期的那样工作。
      this.setState({count: this.state.count + 1});
    }
    
    //正确
    incrementCount() {
      this.setState((state) => {
        // 重要:在更新的时候读取 `state`,而不是 `this.state`。
        return {count: state.count + 1}
      });
    }
    
    //在state更新完后,执行其他操作
    incrementCount() {
      this.setState({
      	aa:'1233'
      },() => {
         //xxxx
      });
    }
    
  3. 如果 Parent 和 Child 在同一个 click 事件中都调用了 setState ,这样就可以确保 Child 不会被重新渲染两次。
    取而代之的是,React 会将该 state “冲洗” 到浏览器事件结束的时候,再统一地进行更新

  4. setState队列:为了合并setState,我们需要一个队列来保存每次setState的数据,然后在一段时间后执行合并操作和更新state,并清空这个队列,然后渲染组件。

5)样式与 CSS
可以在 React 中实现动画效果:参见 React Transition Group、React Motion 以及 React Spring 等示例

6)Virtual DOM 及内核

  1. Shadow DOM 和 Virtual DOM 是一回事吗?
    Shadow DOM 是一种浏览器技术,主要用于在 web 组件中封装变量和 CSS。Virtual DOM 则是一种由 Javascript 类库基于浏览器 API 实现的概念

  2. Virtual DOM:DOM是js的api,虚拟dom是js对象
    DOM:DOM是浏览器中的概念,用js对象表示页面上的元素,并提供操作DOM对象的API,每次DOM操作会引起重绘或者回流

    虚拟DOM:一个JS对象(数据+JXS模板),用一个js对象来描述真实的DOM

(3)diff算法指的就是两个虚拟DOM作比对;(注意不是真实DOM和虚拟DOM作对比)
虚拟DOM和react中的diff算法总结
1)传统diff算法:
使用传统的diff算法进行节点的循环遍历,复杂度是 O(n^3)。

就是逐层比较的算法,一旦发现节点消失,就删除,一旦发现新节点,就创建,发现相同的,就保留。

缺点:
(1)两棵树变化非常陡峭,是低效的。
(2)兄弟节点排序和插入新节点是低效的。

2)深入理解React虚拟DOM

  1. React 通过制定大胆的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题;

  2. React 通过分层求异的策略,对 tree diff 进行算法优化,新旧两颗DOM树,同层对比,当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。

  3. React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化。如果是同一类型的组件,按照原策略继续比较 Virtual DOM 树即可,不同则替换整个组件下的所有子节点。

    对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff。

  4. React 通过设置唯一 key的策略,对 element diff 进行算法优化,在进行组件对比的时候,key值存在则只进行移动操作,不存在则进行添加、删除
    注意:v-for数组时不建议key为index,因为当数组内容更新时,key对应的index就发生改变,以至于对应的数据跟原来的不一样

(4)MVVM 的变化检查是数据层面的,而 React 的检查是 DOM 结构层面的
网上都说操作真实 DOM 慢,但测试结果却比 React 更快,为什么

(5)React 单向数据流(也叫单向绑定)
React 中的数据流是单向的,并顺着组件层级从上往下传递
React 哲学

(6)react也有响应式原理
如何理解react响应式
用户使用setState等方法去更新状态,react会通过虚拟dom去重新渲染页面

(7)高阶函数:传入函数传出函数;高阶组件:传入组件传出组件

1)高阶组件是参数为组件,返回值为新组件的函数。HOC是纯函数,没有副作用。HOC在React的第三方库中很常见,例如Redux的connect组件。

高阶组件的作用:

  1. 代码复用,逻辑抽象,抽离底层准备(bootstrap)代码
  2. 渲染劫持
  3. State 抽象和更改
  4. Props 更改

(8)创建组件

1)函数式定义无状态组件

  1. 组件不会被实例化,整体渲染性能得到提升
  2. 组件不能访问this对象
  3. 组件无法访问生命周期的方法
  4. 无状态组件只能访问输入的props,同样的props会得到同样的渲染结果,不会有副作用
  5. 函数组件看似只是一个返回值是DOM结构的函数,其实它的背后是无状态组件的思想。
//hooks也是使用函数式
function HelloComponent(props){
	return <div>hello {props.name}</div>
}

ReactDom.render(<HelloComponent name='haha'/>, mountNode)

2)es5原生方式React.createClass定义的组件

var InputControlES5 = React.createClass({
    propTypes: {//定义传入props中的属性各种类型
        initialValue: React.PropTypes.string
    },
    defaultProps: { //组件默认的props对象
        initialValue: ''
    },
    // 设置 initial state,ES6可以在constructor里设置state
    getInitialState: function() {//组件相关的状态对象
        return {
            text: this.props.initialValue || 'placeholder'
        };
    },
    handleChange: function(event) {
        this.setState({ //this represents react component instance
            text: event.target.value
        });
    },
    render: function() {
        return (
            <div>
                Type something:
                <input onChange={this.handleChange} value={this.state.text} />
            </div>
        );
    }
});
InputControlES6.propTypes = {
    initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
    initialValue: ''
};

3) es6形式的extends React.Component定义的组件

class HelloMessage extends React.Component {
  constructor(props) {
    super(props);
  }
  state = {
    val: "111",
  };

  render() {
    return <div>{this.props.name || ''}</div>;
  }
}

ReactDOM.render(<HelloMessage name="Runoob"/>, mountNode);

4) 在constructor调用 super(props) 的目的是什么
constructor( )——构造方法
super( ) ——继承

  1. super作为函数调用时,代表调用父类的构造函数。es6要求子类的构造函数必须执行一次super函数,这是必须的,否则js引擎会报错。

  2. 在super()调用之前,子类是不能使用this的。在es5中,子类必须在constructor中调用super(),传递props给super()。传递props给super()的原因则是便于在子类中能在constructor访问this.props

    如果你用到了constructor就必须写super(),是用来初始化this的,可以绑定事件到this上;
    如果你在constructor中要使用this.props,就必须给super加参数:super(props);
    如何理解react中的super(),super(props)

  3. 如果子类没有定义constructor方法,这个方法会被默认添加
    Class 的继承

    class ColorPoint extends Point {
    }
    
    // 等同于
    class ColorPoint extends Point {
      constructor(...args) {
        super(...args);
      }
    }
    

(9)什么是JSX

  1. JSX 是JavaScript XML 的简写
  2. 是 React 使用的一种文件,它利用 JavaScript 的表现力和类似 HTML 的模板语法
  3. 文件能使应用非常可靠,并能够提高其性能
  4. 为什么浏览器无法读取JSX?
    1. 浏览器只能处理 JavaScript 对象,而不能读取常规 JavaScript 对象中的 JSX
    2. 所以为了使浏览器能够读取 JSX,首先,需要用像 Babel 这样的 JSX 转换器将 JSX 文件转换为 JavaScript 对象,然后再将其传给浏览器。

(10)解释 React 中 render() 的目的

  1. 每一个react组件强制要求必须有一个render()
  2. 此函数必须保持纯净,即必须每次调用都返回相同结果
  3. 它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内,例如 form、group、div 等

(11)纯组件

  1. React提供了一种自带props和state浅比较模式来确定是否应该重新渲染组件的类React.PureComponent,通常只需要继承React.PureComponent就可以定义一个纯组件

  2. 如果定义了 shouldComponentUpdate(),无论组件是否是 PureComponent,它都会执行shouldComponentUpdate结果来判断是否 update。如果组件未实现 shouldComponentUpdate() ,则会判断该组件是否是 PureComponent,如果是的话,会对新旧 props、state 进行 shallowEqual 比较,一旦新旧不一致,会触发 update。

  3. 纯组件是可以编写的最简单、最快的组件。它们可以替换任何只有 render() 的组件。这些组件增强了代码的简单性和应用的性能

  4. 优点:纯组件是通过控制shouldComponentUpdate生命周期函数,减少render调用次数来减少性能损耗的

  5. 缺点:可能会因深层的数据不一致而产生错误的否定判断,从而shouldComponentUpdate结果返回false,界面得不到更新。
    因为深浅拷贝的问题,在进行浅比较时可能会对比失败以为数据没有修改
    Component和PureComponent的区别
    React 的性能优化(一)当 PureComponent 遇上 ImmutableJS

    解决方法:

    1. 解构操作符
     add = () => {
        const  { data } = this.state;
        this.setState({
          data: [...data, dataGenerate()]
        })
      }
    
    1. mmutable

(12) React组件间传值的方法有哪些?

主要传值方法有:props、context、发布/订阅、redux。

React中组件之间的传值方法有很多,按照不同的组件间关系可以把组件传值的方法分为父子组件传值,跨级组件传值和非嵌套关系组件传值。

1)父子组件传值

  1. props
    父组件通过props将属性和方法传递给子组件。而子组件向父组件传值需要通过回调函数触发

2)跨级组件传值

  1. props:写法很复杂,耦合程度也很高。需要一层层往下传递

  2. context对象:Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

    尤其解决需要深嵌套、传递的组件
    Context
    React context基本用法

    1. React context的局限性

      1. 在组件树中,如果中间某一个组件 ShouldComponentUpdate returning false 了,会阻碍 context 的正常传值,导致子组件无法获取更新。
      2. 组件本身 extends React.PureComponent 也会阻碍 context 的更新。
    2. 注意点:

      1. Context 应该是唯一不可变的
      2. 组件只在初始化的时候去获取 Context
  3. 发布/订阅( call/emit )

    //A.js
    componentDidMount() {
        this.eventEmitter = emitter.addListener('call',(msg) => {
            this.setState({
                msg
            })
        })
    }
    componentWillUnmount() {
        emitter.removeListener(this.eventEmitter)
    }
    
    //B.js
    call = () => {
        emitter.emit('call','It's me!)
    }
    
  4. redux:state、action、reduce

(13)高阶组件:参数/返回值都是组件

1)高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

2)HOC 在 React 的第三方库中很常见,例如 Redux 的 connect 和 Relay 的 createFragmentContainer。

3)请注意,HOC 不会修改传入的组件,也不会使用继承来复制其行为。相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用

4)注意事项

  1. 不要在 render 方法中使用 HOC
  2. Refs 不会被传递
    虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。

(14)性能优化

(15)Context:Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

尤其解决需要深嵌套、传递的组件 。Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。
React context基本用法

  1. React context的局限性

    1. 在组件树中,如果中间某一个组件 ShouldComponentUpdate returning false 了,会阻碍 context 的正常传值,导致子组件无法获取更新。
    2. 组件本身 extends React.PureComponent 也会阻碍 context 的更新。
  2. 注意点:

    1. Context 应该是唯一不可变的
    2. 组件只在初始化的时候去获取 Context

(16)合成事件
SyntheticEvent(合成事件) 实例将被传递给你的事件处理函数,它是浏览器的原生事件的跨浏览器包装器。除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation() 和 preventDefault()。

个人理解为,将浏览器有的监听事件如:click、keydown等进行一个兼容不同浏览器以及性能优化的处理,在react里,开发可以直接使用onClick这样的方式进行相同的操作

Hook

1)hooks解决了什么:

  1. 组件之间复用状态逻辑
  2. 相比之前一个生命周期可以处理多个不想关的业务逻辑,hook更希望用useEffect处理相关事件

2)hooks获取models层State、Action
使用useSelector useDispatch 替代connect

//获取state数据
import React from 'react'
import { useSelector } from 'react-redux'

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter)
  return <div>{counter}</div>
}

//获取action
const dispatch = useDispatch();
dispatch({
 type: 'name',
 payload: {
   query:xx
 },
})

Redux

Dva

官方文档
一图胜千言, 何况是四图? 图解DVA

面试题

(1)生命周期
(2)组件之间的通信
(3)性能优化
(4)高阶组件:是什么、有在项目中使用过吗
参数/返回值都是组件,不可修改参数组件的原型

(5)hook:什么时候出的,有什么特点
函数式创建组件,优化在组件之间复用状态逻辑很难的问题,使用use+State/Effect的形式,使得函数式里的数据也可响应式

其他知识

(1)在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流
什么是重排重绘,如何减少重拍重绘
浏览器的关键渲染路径
重排/回流:dom结构改变,重新渲染;此时在关键渲染路径中的 Layout 阶段

重绘:样式改变,重新渲染;此时在关键渲染路径中的 Paint 阶段

避免过多重拍重绘的方法:

  1. CSS 样式尽量批量修改

  2. 避免使用 table 布局

  3. 为元素提前设置好高宽,不因多次渲染改变位置

  4. 尽可能只修改BFC的元素,减少对外界的影响

  5. 动画开始GPU加速,translate使用3D变化
    2021年我的前端面试准备

    transform 不重绘,不回流是因为transform属于合成属性,对合成属性进行transition/animate动画时,将会创建一个合成层。这使得动画元素在一个独立的层中进行渲染。当元素的内容没有发生改变,就没有必要进行重绘。浏览器会通过重新复合来创建动画帧

(2)你对受控组件和非受控组件了解多少?

  1. 受控组件:一般是表单组件
    受控组件是必须要有value的,value用来传入一个参数,结合onchang来控制这个参数输出
    注意:value和onchange两者在受控组件中缺一不可,一旦缺少其中一个就会报错。
    在这里插入图片描述

(3)怎么理解react一切皆组件的说法

  1. React采用组件化的思想,最小的组件单位就是原生HTML元素,采用JSX的语法声明组件的调用
  2. React的虚拟DOM,就是一个大的组件树,从父组件层到子组件,在render函数中层层堆叠
  3. 从react-router v4开始,路由本身也是组件
  4. 各个库提供的hoc返回的也是组件,如withRouter、connect
  5. React中的基础数据state props的传递也是以组件为基础
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值