一篇文章就能让小白对React得心应手?

三大主流框架之一的 React,由于其出色的性能,完善的周边设施风头一时无两。
在这里插入图片描述
疫情在家闲无事,不如学习一门成熟框架(react),下面就带大家一起掌握 React。

一、jsx语法

每一个前端MVVM主流框架一套自己的模板处理方法,react 必然有它独特的 jsx语法,在组件中插入html类似的语法,简化创建view的流程。
jsx 原理:React.createElement(tag , { attrs }, content)
jsx 是 JavaScript 的扩展语法 js + xml

1、原生元素
ReactDOM.render((
  <div>
    <h1>标题</h1>
  </div>
), document.getElementById('root'))

通过简单的语法页面就会被插入一个div、一个h1标签。原生的 html 元素可以被直接使用。这并不是 js 支持的语法,需要被转换之后才能运行。

2、自定义元素
import * as React from 'react'

class Page extends React.Component {
  render() {
    return (<div>
      home111 &copy; © \ua9
    </div>)
  }
}

ReactDOM.render((
  <div>
    <Page/>
  </div>
), document.getElementById('root'))

react 强大之处就在于可以组件的自定义,实现组件的复用。如果我们创建了一个组件,就可以通过jsx语法调用,这里定义了一个 Page组件,可以在jsx里面像调用 html 一样直接调用。

3、动态数据(差值表达式)
let name = 'hi'

ReactDOM.render((
  <div>
    {name}
  </div>
), document.getElementById('root'))

React 的插值表达式使用 { } 插入数据,{ } 中间的必须是js表达式,不能是语句。如果表达式的执行结果是一个数组,则会自动 join

4、注释

jsx 语法和 html 语法一样,也是可以插入注释,只不过写的时候有一些区别
子组件注释
在子组件中插入注释,需要使用{}包裹起来,在/ /之间插入注释文字

let name = 'hi'

ReactDOM.render((
  <div>
    {/* 注释 */}
    {name}
  </div>
), document.getElementById('root'))

属性注释
在标签中间,可以插入一个多行注释,如下所示

let name = 'hi'

ReactDOM.render((
  <div>
    {name}
    <img /* 
        多行注释
    */ src="1.jpg"/>
  </div>
), document.getElementById('root'))
5、属性 props

( 1 ) 可以向使用html的attr一样使用属性,就像下面img的src一样

let name = 'hi'

ReactDOM.render((
  <div>
    <img src="1.png"/>
  </div>
), document.getElementById('root'))

( 2 ) 如果需要传递动态属性,使用{ },多个属性,使用展开运算符

let props = {
    src: '1.png',
    alt: '1图片'
}
ReactDOM.render((
  <div>
    <img src={"1.png"}/>
    <img {...props}/>
  </div>
), document.getElementById('root'))

( 3 ) 两个转换 , class >> className , for >> htmlFor
class和for是javascript关键字,所以这里需要用转换之后名称

ReactDOM.render((
  <div className="tab">
    <label htmlFor="name">姓名:</label><input id="name"/>
  </div>
), document.getElementById('root'))

( 4 ) 布尔属性
如果一个属性的值是布尔值,当这个值是true的时候则可以省略=后面的值,只保留key。

ReactDOM.render((
  <div className="tab">
    <input type="text" required/>
    <input type="text" required={true}/>
  </div>
), document.getElementById('root'))

( 5 ) 原生元素的自定义属性
react对元素属性做了校验,如果在原生属性上使用此元素不支持的属性,则不能编译成功。必须使用data-前缀

ReactDOM.render((
  <div className="tab">
    <input type="text" data-init="22"/>
  </div>
), document.getElementById('root'))
6、插入html

如果动态的插入html元素,react出于安全性考虑会自动帮我们转义。所以一定要动态的插入元素的话,使用 dangerouslySetInnerHTML

ReactDOM.render((
  <div className="tab">
    <div dangerouslySetInnerHTML={{__html: '<span>test</span>'}}></div>
  </div>
), document.getElementById('root'))

二、React组件

1、无状态组件
function Button(props, context) {
    return (
        <button>
            <em>{props.text}</em>
            <span>{context.name}</span>
        </button>
    );
}

纯函数 , 不存在state,只接受props和state。纯函数有优点,优点就是易于测试,无副作用。

2、类组件
import * as React from 'react'

class Page extends React.Component {
  render() {
    return (<div>
      home
    </div>)
  }
}

react 基本组件,就是一个实现了 render 方法的 class

三、React数据流

1、state

state是组件的内部状态,需要在视图里面用到的状态,才需要放到state里面去,如下,我们在类上创建一个state属性,在视图里面通过使用this.state.name去引用

import * as React from 'react'

class Page extends React.Component {
  state = {
      name: '小明'
  }    

  render() {
    return (<div>
      {this}
    </div>)
  }
}

如何更新state呢,直接更改 state 其实也可以,不过这样子无法触发组件视图的更新机制。所以使用 setState() 。值得注意的是 setState 是异步的,原因是 react 内部需要对 setState 做优化,不是state 变了立刻去更新视图,而是拦截一部分state的改变,等到合适的时机再去更新视图。

import * as React from 'react'

class Page extends React.Component {
  state = {
      name: '小明'
  }    

  render() {
    setTimeout(() => this.setState({name: '小明儿子'})5000)
  
    return (<div>
      {this.state.name}
    </div>)
  }
}

真实开发中绝不要在 render函数里面去更改state,以上只是为了演示

2、props

props 是组件之间传递数据的最主要api, react推崇的是自顶向下的数据流向,也就是组件的数据要从父组件传给子组件。如果子组件需要向父组件传递数据,则需要使用回调函数的方式。

import * as React from 'react'
  // 父传子
class Child extends React.Component {
  render() {
    return (<div>
      {this.props.parentName}
    </div>)
  }
}

class Parent extends React.Component {
  state = {
      name: '小明'
  }    

  render() {
    setTimeout(() => this.setState({name: '小明儿子'})5000)
  
    return (<div>
      <Child parentName={this.state.name}/>
    </div>)
  }
}

代码中,Child组件显示了父组件的name。当父组件状态更新了,子组件同步更新。那如何在子组件中更改父组件状态呢?那就是回调函数。

import * as React from 'react'

class Child extends React.Component {
  update() {
      this.props.onChange('小明名字改了')
  }

  render() {
    return (<div>
      {this.props.parentName}
      <button onClick={this.update.bind(this)}>更新</button>
    </div>)
  }
}

class Parent extends React.Component {
  state = {
      name: '小明'
  }    

  changeName(name) {
      this.setState({
          name
      })
  }
  
  render() {
    setTimeout(() => this.setState({name: '小明儿子'})5000)
  
    return (<div>
      <Child onChange={this.changeName.bind(this)} parentName={this.state.name}/>
    </div>)
  }
}

注:props是不可更改的,这既不符合react单向数据流思想,也为维护带来灾难。

3、事件

react里面的用户事件都是合成事件,被React封装过。内部使用的还是事件的委托机制。
常用的事件有点击事件onClick,input的onChange事件等,官网都可以查到。
(1)合成事件的this指向问题
为传递到组件内部的只是一个函数,而脱离了当前对象的函数的this指向是不能指到当前组件的,需要显示指定
Ⅰ.通过bind

<button onClick={this.update.bind(this)}>更新</button>

Ⅱ.构造器内部指定

import * as React from 'react'

class Child extends React.Component {
  constructor(props) {
     super(props) 
     this.update = this.update.bind(this)
  }

  update() {
      this.props.onChange('小明名字改了')
  }

  render() {
    return (<div>
      {this.props.parentName}
      <button onClick={this.update}>更新</button>
    </div>)
  }
}

Ⅲ.箭头函数

import * as React from 'react'

class Child extends React.Component {
  update => e = {
      this.props.onChange('小明名字改了')
  }

  render() {
    return (<div>
      {this.props.parentName}
      <button onClick={this.update}>更新</button>
    </div>)
  }
}

Ⅳ.装饰器

import * as React from 'react'

class Child extends React.Component {
  constructor(props) {
     super(props) 
  }

  @autoBind
  update() {
      this.props.onChange('小明名字改了')
  }

  render() {
    return (<div>
      {this.props.parentName}
      <button onClick={this.update}>更新</button>
    </div>)
  }
}

装饰器是es7语法,如果需要使用需要安装对应的babel:present版本。而typescript则原生支持。

autoBind原理大概就是劫持get方法,get时改变this指向

(2)如何获得evnt原生事件

通过e.nativeEvent获取原生事件对象

import * as React from 'react'

class Child extends React.Component {
  constructor(props) {
     super(props) 
     this.update = this.update.bind(this)
  }

  update(e) {
      console.log(e.nativeEvent)
  }

  render() {
    return (<div>
      <button onClick={this.update}>更新</button>
    </div>)
  }
}

(3)解决冒泡和取消默认事件

 e.preventDefault( ) //取消默认行为
 e.stopPropagation( ) //取消冒泡

这个和浏览器原生事件处理方案是一致的。问题是我们只可以调合成事件的e的方法,不可以通过e.nativeEvent方法做这些操作,原因是上文讲过的委托。

四、ReactDom

1、ref

特殊的props,ref组件对象的引用,现在官方也不建议直接给ref赋值,需要通过函数来赋值。

ReactDOM.render((
  <div>
    <Calendar ref={ref => this.c = ref} any-ss="text"/>
  </div>
), document.getElementById('root'))
2、render

顶层api , 只有在根组件时候才需要使用。第一个参数是Component,第二个参数是dom节点

3、findDOMNode

通过传入component实例获取此component根dom节点,在这里可以去dom节点进行操作了,虽然极其不建议这么做,但是你确实可以做。

4、unmountComponentAtNode

卸载此组件,并销毁组件state和事件

接收组件的引用,也就是ref。仅仅是取消挂载,组件还在,如果需要彻底清除的话,需要手动删掉此dom。

五、表单

1、onchange配合value

与vue框架不同的是,react如果要实现表单元素变化,状态同步更新,必须要自己去监听表单事件。

import * as React from 'react'

class Child extends React.Component {
  state = {
      name: '小明'
  }

  constructor(props) {
     super(props) 
     this.update = this.update.bind(this)
  }

  update(e) {
      this.setState({
          name: e.target.value
      })
  }

  render() {
    return (<div>
      <input onChange={this.update} value={this.state.name}/>
    </div>)
  }
}
2、受控组件和非受控组件

受控组件和非受控组件这些都是指的表单组件,当一个表单的值是通过value改变的而不是通过defaultValue是受控组件,否则就是非受控组件
下面组件中的input就是受控组件

import * as React from 'react'

class Child extends React.Component {
  state = {
      name: '小明'
  }

  constructor(props) {
     super(props) 
     this.update = this.update.bind(this)
  }

  update(e) {
      this.setState({
          name: e.target.value
      })
  }

  render() {
    return (<div>
      <input onChange={this.update} value={this.state.name}/>
    </div>)
  }
}

下面组件中的input是非受控组件

import * as React from 'react'

class Child extends React.Component {
  state = {
      name: '小明'
  }

  constructor(props) {
     super(props) 
     this.update = this.update.bind(this)
  }

  update(e) {
      this.setState({
          name: e.target.value
      })
  }

  render() {
    return (<div>
      <input onChange={this.update} defaultValue={this.state.name}/>
    </div>)
  }
}

六、组件间通信

1、父子之间通讯

父子之间通讯又分为 父 >> 子子 >> 父

因为react单向数据流向的缘故,父 >> 子通信的话直接通过props。父组件数据变动,直接传递给子组件。

子 >> 父组件之间就要通过回调函数来通信了,父组件传递一个回调函数给子组件,子组件通过调用此函数的方式通知父组件通信。

2、跨级组件通信

react为了实现祖先组件和后辈组件之间的通信问题,引入了context Api。

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.context.color}}>
        {this.props.children}
      </button>
    );
  }
}

Button.contextTypes = {
  color: React.PropTypes.string
};

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  getChildContext() {
    return {color: "purple"};
  }

  render() {
    const children = this.props.messages.map((message) =>
      <Message text={message.text} />
    );
    return <div>{children}</div>;
  }
}

MessageList.childContextTypes = {
  color: React.PropTypes.string
};

MessageList 中的 color 会自动更新到儿孙组件里面去,实现跨级通信。如果需要反过来通信,则需要借助其他工具,比如事件系统(Pub/Sub)。

3、没有嵌套关系组件之间通信

组件之间通信最主流的两种方式脱胎于 观察者模式中介者模式这两种。
跨级之间通信现在最主流的方式就是观察者模式的实现Pub/Sub,react中的 redux 也是使用这种方式实现的。
redux 详细见:React-redux的原理解析及基本使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值