react学习笔记

JSX

是js的一种语法扩展,在React中用于描述UI。

  • 在JSX中使用{}嵌入任何js表达式
  • 多行JSX最好使用括号wrap
  • 编译后,JSX表达式成为常规的JavaScript对象。因此可以在if或者for循环中使用,或者赋值给其他的变量、从函数中返回等等。
  • 由于JSX比起HTML更接近JavaScript,所以React DOM使用驼峰命名法为属性命名而不是使用HTML属性名称。
    比如,class在JSX中变为className
  • JSX可以阻止注入攻击。
    在jSX中包含用户输入是安全的,因为默认情况下ReactDOM在渲染JSX之前会将JSX中嵌入的值进行转义,有助于防御XSS攻击。

  • Babel将JSX编译成对React.createElement()的调用,最终会生成一个描述元素属性的object。这个object就是React元素,React会读取这些object并最终构建为DOM。

渲染

与DOM元素不同,React元素就是纯Object,因此创建它的代价是很低的。
React DOM负责根据React元素来更新DOM。

ReactDOM.render(element,document.getElementById('root'))

组件

  • 最简单的定义组件的方法就是定义一个函数:

    • 接受props为参数
    • 返回一个JSX
  • 也可以使用class来定义一个组件

    • 继承React.Component
    • render()用于返回JSX
    • constructor()
      • 由于是继承了component基类,必须调用基类的constructor,并传入props:super(props)
      • 初始化state
  • 组件名称首字母大写
  • 组件必须只返回一个根元素,一般用一个div来对内部元素进行包裹
  • 函数式组件或者组件的render方法返回null,则此组件不会被渲染

props

父组件传递给子组件的数据。

  • 组件永远不能修改props,是只读的。

state

setState 方法由父类 Component 所提供。当我们调用这个函数的时候,React.js 会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上。

setState异步性

  • 当你调用 setState 的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。

    • 由于这种异步性,你不能依赖它们的值去计算下一个状态的state。
      如:this.setState({ count: this.state.count + 1})
  • 这里就要用上setState 的第二种使用方式,可以接受一个函数作为参数。React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:

this.setState((prevState) => {
      return { count: prevState.count + 1 } 
    }) 

自上而下

state只被组件自己拥有,要想传递给其他的组件(子组件),就要使用props向下传递。

一个组件不知道其他组件是有状态的还是无状态的。组件间只是通过props来传递数据。

state与props区别

  • props是父组件传递进来的数据,是只读的
  • state是组件自己维护的状态,是可变的

共同点:

  • 二者的变化都会触发组件的重新渲染

处理事件

与DOM中处理事件类似,但也有一些区别:

  • React事件名使用驼峰命名法,而不是DOM中的小写形式
  • 在JSX中为事件属性传入一个函数,而不是一个字符串。
<button onClick={activateLasers}>
  Activate Lasers
</button>
  • 不能通过返回false来取消默认行为,必须在事件处理函数中显式调用preventDefault。
  • 当使用class来定义组件的时候,一个常用的模式就是定义class的方法来处理事件:
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}
  1. 在元素中使用为click事件定义处理函数:onClick={this.handleClick},即class中的handleClick方法
  2. 在constructor的初始化工作中要注意为事件处理函数绑定this。在js中,class中的方法并不默认绑定this,因此要自己强制绑定,以保证在click发生时handleClick内this指向的是当前组件

也可以使用箭头函数来达到同样的目的:

class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.

  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

列表

  • 一般使用map()函数来遍历一个数组并生成对应的React元素:
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

可以使用{}包含一个元素列表:

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);

key属性

最好为每一个列表中的元素分配一个key属性。
key属性帮助React识别哪些项被修改了,被添加了或者被移除了。

  • 不要求key属性在全局唯一,只要在兄弟元素中唯一
  • key属性对于React来说是一个重要信息,但是不会传递给你的组件。如果在组件中你需要同一个值,需要自己使用另一个prop属性去传递。

虚拟DOM

在React中,组件render方法得到的结果并不是真正的DOM节点,而是纯粹的js对象,用于描述DOM节点。我们称之为virtual DOM

React之所以比直接操作DOM的JS库快,原因就是在渲染时,React 会把组件当前的虚拟DOM结构和前一次的虚拟DOM结构做比较,只有存在差异性,React才会把差异的内容同步到实体DOM上。

React速度快的原因,还有一个是它出色的Diff算法。标准的比较两棵树的Diff算法的时间复杂是 O(n3) 。而React基于非常符合实际场景的几个策略,就将Diff算法的时间复杂度降到了接近O(n)。这几个策略是:

  • DOM 节点跨层级的移动操作特别少,可以忽略不计,因此对两棵树的比较分层进行只会对同一层次的节点进行比较
    如果发现层内的一个节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。
    这里写图片描述
  • 如果两个组件或元素类型不同,那么他们就是完全不同的树,不需要再比较他们的子节点。
    比如, 如果有个 <Header><ExampleBlock> 替换掉了,React 会删除掉 Header再创建一个 ExampleBlock.
  • 对同一层级的节点,diff算法提供了插入、移动和删除三种节点操作。
    可以为组件或元素设置key属性,key用来标识这个组件或元素。一般多用于列表中。列表中key属性的存在,可以让React正确识别新增、修改和删除,从而做出最优的修改。

refs

ref属性用于获取组件的引用。
这里子组件必须是一个React component的实例或者一个DOM元素

ref的应用场景一般是想要直接调用一个组件实例的方法,而不是通过传递新的props来使组件重新渲染.
一个典型的例子就是使某个input元素获得焦点:

class AutoFocusTextInput extends React.Component {
  componentDidMount() {
    this.textInput.focus();
  }

   render () {
    return (
      <input ref={(input) => this.textInput= input} />
    )
  }
}

可以看到我们给 input 元素加了一个 ref 属性,这个属性值是一个函数。当 input 元素在页面上挂载完成以后,React.js 就会调用这个函数,并且把这个挂载以后的 DOM 节点传给这个函数。在函数中我们把这个 DOM 元素设置为组件实例的一个属性,这样以后我们就可以通过 this.input 获取到这个 DOM 元素。

不能在函数式组件中使用refs,因为它们没有实例
但是可以在函数式组件内部的DOM元素或者class组件上可以使用。

props.children

我们可以在组件标签中编写内嵌的结构,就像普通的html标签一样:

 <Card>
    <h2>React.js 小书</h2>
    <div>开源、免费、专业、简单</div>
    订阅:<input />
  </Card>

这样我们希望组件Card只是一个包含型的组件,其中的内容是随意的。

在Card组件中我们可以通过props.children获得嵌套在组件内部的JSX结构:

class Card extends Component {
  render () {
    return (
      <div className='card'>
        <div className='card-content'>
          {this.props.children}
        </div>
      </div>
    )
  }
}

dangerouslySetHTML

根据之前的知识,我们知道在 React.js 当中所有的表达式插入的内容都会被自动转义。但有时我们想不通过转义,直接将传入的内容当做html结构显示在页面中,这时就需要dangerouslySetHTML属性,让我们可以动态设置元素的innerHTML。

render () {
    return (
      <div
        className='editor-wrapper'
        dangerouslySetInnerHTML={{__html: this.state.content}} />
    )
  }

style

React.js 中的元素的 style 属性的用法和 DOM 里面的 style 不大一样。
在React中需要把CSS属性变为一个对象再传给元素:

<h1 style={{fontSize: '12px', color: 'red'}}>React.js 小书</h1>

style 接受一个对象,这个对象里面是这个元素的 CSS 属性键值对,原来 CSS 属性中带 - 的元素都必须要去掉 - 换成驼峰命名,如fontSize、textAlign。

我们可以利用这种方式使用props或者state动态设置元素的样式。

PropTypes

React.js 就提供了一种机制,让你可以给组件的props加上类型验证,这样就能防止传入错误的数据类型导致出错。

PropTypes是一个第三方库,需要单独安装和引入。

class Comment extends Component {
  static propTypes = {
    comment: PropTypes.object
  }

  render () {
    const { comment } = this.props
    return (
      <div className='comment'>
        <div className='comment-user'>
          <span>{comment.username} </span>:
        </div>
        <p>{comment.content}</p>
      </div>
    )
  }
}

并且给 Comment 组件类添加了类属性 propTypes,并规定了comment类型必须为object。此时再传入不是object的值会报错。

通过isRequired也可以要求某一个参数必须传入:

static propTypes = {
  comment: PropTypes.object.isRequired
}

PropTypes 提供了一系列的数据类型可以用来配置组件的参数:

PropTypes.array
PropTypes.bool
PropTypes.func
PropTypes.number
PropTypes.object
PropTypes.string
PropTypes.node
PropTypes.element

Element、Component与instance

  • Element:是一个纯粹的对象,用于描述一个DOM元素。
    JSX用于描述Element,而Babel会将其转化为调用React.createElement的方式,最终结果就是生成一个Element。

  • Component:是一个接受参数并返回Element的函数或者类。

  • instance:当你调用ReactDOM.render()把一个组件渲染到一个具体的DOM元素中时,返回的值即使一个实例

生命周期

这里写图片描述

初始化阶段

getDefaultProps与getInitialState

当我们拥有ES6运行环境时,可以通过class语法定义组件。
但是without ES6的时候,就需要通过createReactClass()函数来创建一个组件类。在这个函数中要使用getDefaultPropsgetInitialState 来定义props与state的初始值。

var Counter = React.createClass({
    getDefaultProps: function() {
        return {
            title: 'Basic counter!!!'
        }
    },
    getInitialState: function() {
        return {
            count: 0
        }
    },
    render:function(){
        ……
    },
    propTypes: {
        title: React.PropTypes.string
    }
});

挂载阶段

我们把 React.js 将组件渲染,并且构造 DOM 元素然后塞入页面的过程称为组件的挂载

componentWillMount与componentDidMount

在挂载前后我们可以有机会做一些处理:

-> constructor()
-> componentWillMount()
-> render()
// 构造 DOM 元素插入页面
-> componentDidMount()
  • React会在组件render之前调用componentWillMount,在这个阶段调用this.setState()方法将不会触发重复渲染。
  • React会在组件被渲染成DOM元素插入页面后调用componentDidMount,此时对应的DOM元素已经生成,这意味着这个方法是初始化其他需要访问DOM或操作数据的第三方库的最佳时机。

更新阶段

除了挂载阶段,还有一种“更新阶段”。说白了就是 state或者props更改导致 React.js 重新渲染组件并且把组件的变化应用到 DOM 元素上的过程,这是一个组件的变化过程。

当props更新时,这些生命周期函数会按以下顺序调用:
这里写图片描述

当state更新时,则会按以下顺序调用:

这里写图片描述

componentWillReceiveProps(nextProps)

组件从父组件接收到新的 props 之前调用。
在此方法内调用this.setState()将不会导致重复render。

shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate 允许我们手动地判断是否要进行组件更新,常作为优化React性能使用。
当shouldComponentUpdate返回false时,组件本次的render方法不会被触发。可以通过在这个方法中比较前后两次state或者props,根据实际业务场景决定是否需要触发render方法。

componentWillUpdate

组件重新渲染之前调用。
在这个函数内你不能调用setState改变组件状态。

componentDidUpdate

render()调用完毕,组件重新渲染完成,已经变更到真实的 DOM 以后调用。

卸载阶段

componentWillUnmount

同样在元素被从页面中删除的时候React也控制了这个组件的删除过程:

// 即将从页面中删除
-> componentWillUnmount()
// 从页面中删除

在从页面中删除前会调用componentWillUnmount方法。
可以在这个阶段进行一些清理工作,如计数器的清理等等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值