React学习笔记之基础用法

最近在学习React,并使用React做了一个cnode(欢迎大家给我star、issue,一起学习讨论进步),现就记录一下自己的React学习笔记。

学习资料

React中文文档

环境配置

React的环境配置很麻烦,刚上手时可以使用React脚手架来进行学习。推荐Create React App,或者可以使用我自己写的一个脚手架。

Jsx语法

简单来说,就是可以把html和js混在一起写,即html代码里面可以有js代码,js代码里面可以有html。需要注意的是,在html中遇到js代码,需要加花括号,而在js代码中遇到html代码,需要加圆括号。

创建组件

创建组件有两种方式:

  • 函数创建
function Hello() {
  return 

Hello World

; }
  • ES6的class
class Hello extends React.Component {
  render() {
    return 

Hello World

; } }

不同之处:函数式组件没有state,也不能使用生命周期函数。

注意:组件名称必须以大写字母开头。组件的返回值只能有一个根元素。

props与state
  • props
    props是只读的,组件绝对不能修改自己的props
  • state
    状态与属性十分相似,但是状态是私有的,完全受控于当前组件。
    更新状态只有一个办法:那就是调用this.setState()
    该方法有两种调用方式
    // 接受一个对象为参数
    this.setState({
      loading: false
    })
    // 接受一个函数为参数,函数的第一个参数为先前的状态,第二个参数为props
    this.setState((prevState, props) => ({
     loading: !prevState.loading
    }))
    // 除此之外,this.setState()还接受一个可选的回调函数作为第二个参数
    
条件加载

React的条件加载和JavaScript中的条件判断一样,我们可以使用条件运算符(?:),与运算符(&&)等来进行条件加载。

循环加载

我们一般按如下的方式进行循环加载

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  
  • {number}
  • ) //一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串。 //通常,我们使用来自数据的id作为元素的key。当元素没有确定的id时,你可以使用他的序列号索引index作为key
    操作表单
    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: ''};
        this.handleChange = this.handleChange.bind(this);
      }
      handleChange(event) {
        this.setState({value: event.target.value});
      }
      render() {
        return (
            
    ); } }

    一个简单操作表单元素的例子就是这样。但是我们会发现这样操作表单有一些小问题。假如该表单有很多表单元素,那么我们需要为每一个表单元素注册一个change事件的处理函数,这会让组件显得很臃肿。不过不用担心,遇到这种情况我们依然有解决办法。那就是使用ref。

    关于Ref

    我们可以给DOM元素,类组件添加ref属性,不能给函数式组件添加ref属性。
    ref 属性接受一个回调函数,它在组件被加载或卸载时会立即执行。
    ref属性也接受一个字符串,(不过未来可能会废弃,推荐使用回调)
    ref 在加载时回调接收了底层的DOM元素或已经加载的 React 实例作为参数,在卸载时则会传入 null。

    class NameForm extends React.Component {
      constructor(props) {
        super(props);
      }
    
      render() {
        return (
            
    ); } } // 此时this.input指向inputDOM元素,我们可以使用this.input.value得到用户的输入。

    除可以操作DOM之外,ref还用于触发强制动画,处理焦点、文本选择或媒体控制等。
    不过我们应该尽量避免使用ref。

    事件处理
    • React事件绑定属性的命名采用驼峰式写法。
    • 如果采用 JSX 的语法需要传入一个函数作为事件处理函数,而不是一个字符串。
    • 不能使用返回 false 的方式阻止默认行为。
    • 事件对象是一个合成对象,所以不需要担心跨浏览器的兼容性问题。
    • 事件处理程序会成为类的一个方法(类的方法默认是不会绑定this 的),所以我们需要手动绑定this。
      手动绑定this有两种方法,一是在类的构造函数中使用bind绑定,二是在DOM中使用bind绑定。
      如果你的事件处理程序是箭头函数,则不需要手动绑定this。
    • 向事件处理程序传递参数有两种方法,一是使用箭头函数,二是使用bind方法。
    
    
    
    • 事件处理程序的最后一个参数为事件对象(该事件对象是SyntheticEvent的实例)。
      如果由于某些原因,你得使用一些底层的浏览器事件,只需用nativeEvent的属性就能得到原生的事件对象。
    DOM属性

    React实现了一套与浏览器无关的DOM系统,兼顾了性能和跨浏览器的兼容性。
    在React中,所有的DOM特性和属性都是小驼峰命名法命名。(aria-和data-属性除外)
    一些不同之处:

    • 使用className属性指定一个CSS类。
    • style属性接受一个键为小驼峰命名法命名的javascript对象作为值。(注意:样式属性不会自动补齐前缀的,浏览器前缀除了ms以外,都应该以大写字母开头)
    • onChange函数,无论form表单何时发生变化,这个事件都会被触发。
    • dangerouslySetInnerHTML函数是替换浏览器DOM中的innerHTML接口的一个函数。
    生命周期

    组件的生命周期大致分为三个阶段

    1. 装配阶段(这些方法会在组件实例被创建和插入DOM中时被调用)

      • constructor()
      • componentWillMount()
      • render()
      • componentDidMount()
    2. 更新阶段(属性或状态的改变会触发一次更新。当一个组件在被重渲时,这些方法将会被调用)

      • componentWillReceiveProps(nextProps)
      • shouldComponentUpdate(nextProps, nextState)
      • componentWillUpdate(nextProps, nextState)
      • render()
      • componentDidUpdate(prevProps, prevState)
    3. 卸载阶段(当一个组件被从DOM中移除时,该方法被调用)

      • componentWillUnmount()

    除此之外,组件还有一个方法forceUpdate(),调用forceUpdate()将会导致组件的 render()方法被调用,并忽略shouldComponentUpdate()

    注意点:

    • shouldComponentUpdate()返回false,render()函数将不会被调用。
    • 当为一个React.Component子类定义构造函数时,你应该在任何其他的表达式之前调用super(props)。否则,this.props在构造函数中将是未定义,并可能引发异常。
    • defaultProps可以被定义为组件类的一个属性,用以为类设置默认的属性。
    常见问题
    1. 使用React开发必须使用JSX语法吗?
      答:JSX并不是必须的。每一个JSX元素都只是 React.createElement(component, props, ...children)的语法糖。
      因此,任何时候你用JSX语法写的代码也可以用普通的 JavaScript 语法写出来。
    2. 使用React开发必须使用ES6+语法吗?
      答:ES6+并不是必须的。

    虚拟DOM

    • 虚拟DOM具有batching(批处理)和高效的Diff算法。
    • batching把所有的DOM操作搜集起来,一次性提交给真实的DOM。diff算法时间复杂度也从标准的的Diff算法的O(n^3)降到了O(n)。
    • render执行的结果得到的并不是真正的DOM节点,结果仅仅是轻量级的JavaScript对象,我们称之为virtual DOM。
    • 我们利用虚拟DOM树去构造真实DOM树,然后插入到文档中,当数据变化时,生成一个新的虚拟DOM树,比较新的虚拟DOM树与旧的虚拟DOM树,得到差异,将差异应用到真实DOM中。
    • Virtual DOM并没有完全实现DOM,Virtual DOM最主要的还是保留了Element之间的层次关系和一些基本属性。

    React diff算法

    • 树对比
      React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会对同一层次的节点进行比较。
    • 组件对比
      • 如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
      • 如果不是,直接删除旧组件,然后在该位置创建新组件。
      • 对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff。
    • 元素对比
      允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,虽然只是小小的改动,性能上却发生了翻天覆地的变化!
    • 总结
      • React 通过分层求异的策略,对 tree diff 进行算法优化;
      • React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化;
      • React 通过设置唯一 key的策略,对 element diff 进行算法优化;
    性能优化
    • 一般做法
      1. 使用工具来分析性能瓶颈。
      2. 尝试使用优化技巧解决这些问题。
      3. 使用工具测试性能是否确实有提升。
    • 具体实施
      很大程度上,React的性能优化就是干掉无谓的渲染。
      • 我们可以重写 shouldComponentUpdate()方法,对this.props与nextProps进行比较,对this.state与nextState进行比较,如果有改变,则返回true,否则返回false。
      • 组件继承React.PureComponent。(自动帮我们进行浅比较)
      • 使用pure-render-decorator装饰器。(自动帮我们进行浅比较)
      • 使用react-addons-shallow-compare。(自动帮我们进行浅比较)
      • 使用immutable.js。
      • 使用seamless-immutable。
    • 性能分析工具
      • React.addons.Perf
      • react-perf-tool
    参考

    使用immutable优化React
    React性能优化总结
    React 源码剖析系列 - 不可思议的 react diff

    未完待续....

    你可能感兴趣的:(React学习笔记之基础用法)