上次给大家介绍了由尤雨溪推出的Vue框架,今天给大家简要介绍我正在学习的React框架。不想说太多废话,也不想贴太多代码,下面列出的内容都是React框架的核心内容(学过react的可以复习一下,没学过的可以简单了解一下react的核心特性),为了方便理解React的特性,部分内容与Vue进行了对比。

如果你看不懂,我送你一句话(来自Vue官网):你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

1、React简介

React是由Facebook推出的前端开发框架,于2013年5月开源,目前React已经演化成一套成熟的Web应用解决方案,其衍生出的React Native用写Web App的方式去写Native App,实现同一组人只需要写一次UI, 在理论上就能同时运行在PC端的浏览器和手机端APP上。

React特点:

  • 声明式设计 − React采用声明范式,可以轻松描述应用。
  • 高效 − React通过对DOM的模拟,最大限度地减少与DOM的交互。
  • 灵活 − React可以与已知的库或框架很好地配合。
  • JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
  • 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
  • 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

2、React核心原理

React的核心原理主要是两点:虚拟DOM和Diff算法。当然Vue里面也有虚拟DOM和Diff算法这两个概念,在面试找工作的过程中这也是经常被问到的问题,后续有机会我会再深入了解一下。

虚拟DOM(Virtual DOM):传统的 web 应用,操作 DOM 一般是直接更新操作的,DOM 更新通常是比较昂贵的。而 React 为了尽可能减少对 DOM 的操作,提供了一种不同的而又强大的方式来更新 DOM,代替直接的 DOM 操作。就是实现了一个轻量级的虚拟DOM。虚拟DOM是React抽象出来的一个对象,描述 DOM 应该什么样子的,应该如何呈现。通过这个 Virtual DOM 去更新真实的 DOM,由这个 Virtual DOM来管理真实 DOM 的更新。

Diff算法: 更新Virtual DOM并不保证马上影响真实的 DOM,React 会等到事件循环结束,然后利用这个 diff 算法,通过当前新的 dom 表述与之前的作比较,计算出最小的步骤更新真实的 DOM。

注意:以上只是对React核心原理的简单介绍, 实际上这两者背后的原理有更进一步深入学习的必要, 这留给以后再看。

3、通过create-react-app工具创建React项目

在工程项目中,往往都是使用现成的脚手架去构建项目, create-react-app就是专门用来构建React项目的脚手架, 相当于Vue的vue-cli。

使用方法:

  • 安装Node环境
  • 全局安装create-react-app: npm install -g create-react-app
  • 创建react项目: create-react-app demo-app
  • 进入项目目录: cd demo-app
  • 安装相关包: npm install
  • 运行react项目: npm start

4、React JSX语法

在Vue中模板文件是类似HTML的文件, 其学起来难度比较低, 而React理解需要通过JSX来定义HTML, 然后使用render函数进行渲染(注意render里面的return中只能有一个元素存在)。

JSX是React用来描述DOM结构的一种语法,它非常类似于原始的html写法,但是又有一些自己的规则。TypeScript语言原生支持JSX,两者配合可以让TS覆盖到界面部分类型检查,开发体验非常棒,JSX让我们可以完全使用JS来开发界面部分(HTML)和逻辑部分(JS),可以充分利用JS的灵活性。

插值

插值简单理解就是将数据的值插入页面,在Vue中通过{{}}实现插值, 而在React的JSX语法中通过{}实现插值。示例如下:

render() {
  let name = 'React'
  return <div>Hello {name}</div>
}

当然也可以在{}中放一些表达式或者调用函数, 这里不做演示。

添加属性

添加属性与插值的写法类似,如:

render() {
  return (
    <div tabIndex="0">
      <img src={user.avatarUrl} />
    </div>
  )
}

注意:JSX语法上更接近JavaScript,并非和HTML完全对应,所以React DOM使用 camelCase(小写驼峰命名)来定义属性的名称,而不使用HTML属性名称的约定。比如最常见的,JSX里面的 class 变成了 className

render() {
  return <div className="message">Hello</div>
}

事件处理

React元素的事件处理和DOM元素很类似,但是语法上有一些不同:

  • React事件的命名采用小驼峰模式(camelCase),而不是纯小写,比如onClick、onChange等
  • 使用JSX语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

Vue中采用v-on监听DOM事件,其中v-on可以简写为@

传统HTML的事件处理

<button onclick="onclick()">Click</button>

React的事件处理

onBtnClick() {
  console.log('button clicked!')
}

render() {
  return <button onClick={this.onBtnClick.bind(this)}></button>
}

注意: 在React里面我们不需要使用addEventListener给元素添加事件监听

5、React组件化开发

在 react 中,一切皆组件,根据划分不同组件,可以使代码复用,减少代码编写。组件分可以分为Class组件和函数组件。

Class组件

Class组件又叫有状态组件,定义Class组件需要继承React.Component,实现render函数,返回一个元素。基本示例如下:

class ClassComponent extends React.Component{
  render(){
    return <div>这是一个Class组件</div>;
  }
}

函数组件

函数组件又叫无状态组件。基本示例如下:

const FunctionComponent = () => <div>是一个Function组件</div>;

Class组件和函数组件的区别

  • 实现原理不同: 函数组件是一个纯函数,它接收一个props对象返回一个react元素;而类组件需要去继承React.Component并且创建render函数返回react元素。
  • 生命周期和属性不同: 函数组件没有生命周期、state和ref,而类组件有。两者都有props。

但是React16.8提供的hooks特性, 提供了useState、useEffect、useRef等钩子方法, 让函数组件也可以实现生命周期、state和ref, 目前react hooks已经在开发中得到广泛的使用,以后再详细看一下。

6、React三大属性

props

类似vue中的props, 可通过父子组件进行数据传递和交互。具体而言, props可实现以下三种组件通信:

  • 父传子
  • 子传父
  • 兄弟组件通讯

porps可在类组件和函数组件中直接使用。

state

在react组件中可以通过state保存当前所操作数据的状态

为什么React需要state:因为React不同于Vue, Vue是数据双向绑定, 而React是数据的单向绑定, 所以需要通过setState方法对state进行更新, 以后有时间再深入了解。

state可在类组件中直接使用,在函数组件中要用useState实现。

ref

在React中可以通过ref获取React元素或DOM元素。在日常写React代码的时候,一般情况是用不到Ref这个东西,因为一般来说并不直接操作底层DOM元素,而是在render函数里去编写我们的页面结构,由React来组织DOM元素的更新。凡事总有例外,总会有一些很奇葩的时候需要直接去操作页面的真实DOM,这就要求React具有直接访问真实DOM的能力,而Ref就是提供了这样的能力。

React.createRef: React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点。

ref的使用方法如下:

class RefDemo extends React.Component{
  constructor() {
    super()
    this.myRef = React.createRef()
  }

  componentDidMount() {
    // 输出当前绑定的DOM元素
    console.log(this.myRef.current)
  }

  render() {
    return <div ref={this.myRef} />
  }
}

也可以通过绑定字符串(String类型的refs)来获取ref:

class StringRef extends React.Component {
  componentDidMount() {
    console.log(this.refs.container.innerHTML) //获取绑定的节点
  }
  
  render() {
    return (
      <div ref='container'>怎知向何处</div>
    )
  }
}

注意: 不能在函数组件上使用 ref 属性,因为他们没有实例。函数组件可以使用useRef(),它所返回的对象在组件的整个生命周期内不变。

7、React的生命周期

React中常用的生命周期:

  • componentDidMount:会在组件挂在后(插入DOM树中)立即调用。对应Vue的created和mounted。
  • componentDidUpdate: 会在更新后会被立即调用。首次渲染不会执行此方法。对应Vue的updated。
  • componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作。对应Vue的destroyed。

注意: 不能在函数组件使用上述生命周期, 在函数组件中可通过useEffect模拟上述生命周期。

Vue中常用的生命周期:

  • created:页面初始化,在模板渲染成html前调用
  • mounted: 挂载(DOM挂载到页面上),在模板渲染成html后调用,用来进行发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
  • updated:更新(DOM更新)
  • destroyed:销毁,一般在beforeDestroy中清除定时器、解绑自定义事件、取消订阅消息等(收尾工作)

这部分内容没有贴代码,后续有机会再详细介绍一下React的生命周期。