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 <h1>Hello World</h1>;
}
  • ES6的class
class Hello extends React.Component {
  render() {
    return <h1>Hello World</h1>;
  }
}

不同之处:函数式组件没有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) =>
  <li key={number.toString()}>
    {number}
  </li>
)
//一个元素的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 (
        <div>
          <label>Name:
            <input type="text" value={this.state.value} onChange={this.handleChange} />
          </label>
        </div>
    );
  }
}

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

关于Ref

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

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

  render() {
    return (
        <div>
          <label>Name:
            <input type="text" ref={ input => this.input=input }/>
          </label>
        </div>
    );
  }
}
// 此时this.input指向inputDOM元素,我们可以使用this.input.value得到用户的输入。

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

事件处理
  • React事件绑定属性的命名采用驼峰式写法。
  • 如果采用 JSX 的语法需要传入一个函数作为事件处理函数,而不是一个字符串。
  • 不能使用返回 false 的方式阻止默认行为。
  • 事件对象是一个合成对象,所以不需要担心跨浏览器的兼容性问题。
  • 事件处理程序会成为类的一个方法(类的方法默认是不会绑定this 的),所以我们需要手动绑定this。
    手动绑定this有两种方法,一是在类的构造函数中使用bind绑定,二是在DOM中使用bind绑定。
    如果你的事件处理程序是箭头函数,则不需要手动绑定this。
  • 向事件处理程序传递参数有两种方法,一是使用箭头函数,二是使用bind方法。
<button onClick={(e) => this.confirm(id, e)}>确定</button>
<button onClick={this.confirm.bind(this, id)}>确定</button>
  • 事件处理程序的最后一个参数为事件对象(该事件对象是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

未完待续....

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,804评论 0 24
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,048评论 2 35
  • HTML模版 之后出现的React代码嵌套入模版中。 1. Hello world 这段代码将一个一级标题插入到指...
    ryanho84阅读 6,220评论 0 9
  • 本笔记基于React官方文档,当前React版本号为15.4.0。 1. 安装 1.1 尝试 开始之前可以先去co...
    Awey阅读 7,647评论 14 128
  • 芸芸众生, 在这偌大的世界里, 收获过幸福的欢笑, 享受过成功的喜悦, 品尝过失败的泪水。 每个人都有着自己的保护...
    高苏辛阅读 387评论 0 4