JSX简介

JSX是一种JavaScript的语法扩展,可以在大括号内中使用任何JavaScript表达式。
推荐在JSX代码的外面扩上一个小括号,这样可以防止分号自动插入的bug。
可以使用引号来定义以字符串为值的属性,也可以使用大括号定义以JavaScript表达式为值的属性。
闭合标签需要在结尾处用/>标记。
使用小驼峰命名来定义属性的名称。
更多ES6的语法介绍,请参考下面内容

元素渲染

元素是构成React应用的最小单位。
将页面渲染到DOM中:
  • 在一个HTML页面中添加一个 id="root" 的 <div>:
  • 将React元素渲染到根DOM结点中:
React元素都是immutable不可变的。一个元素就好像是动画里的一帧,它代表应用界面在某一时间点的样子。更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render() 方法。

组件&Props

组件从概念上看就像是函数,它可以接收任意的输入值(称之为"props"),并返回一个需要在页面上展示的React元素。
使用函数方式定义组件:
使用类方式定义组件:
组件名称必须以大写字母开头。
组件的返回值只能有一个根元素。
所有的React组件必须像纯函数那样使用它们的props。

State & 生命周期

 
notion image
创建React组件类,需要类继承自React.Component类,并且重写其中的render()方法即可。
类通过props接收外部状态,通过state保存内部状态。
只有构造函数中才可以直接修改this.state的值,其余地方想修改state,需要调用this.setState(),否则界面不会动态更新。
state和props的更新是异步的,React内部可能将多个setState()调用合并成一个调用来提高性能,如果想获取上一次的state和props,需要传递一个函数给setState()方法,调用方式如下:
React中的数据都是自顶向下的单向数据流,父组件或子组件都不知道某个组件是有状态的还是无状态的,组件可以将其状态作为属性传递给其子组件。任何状态始终由某些特定的组件所有,并且改状态的任何数据或UI只能影响树中下方的组件。

事件处理

React元素的事件处理和DOM元素的事件处理区别:
  • React事件绑定属性的命名采用驼峰式写法,而不是小写。
  • 如果采用JSX的语法你需要传入一个函数作为事件处理函数,而不是一个字符串。
  • React中不能使用返回false的方式阻止默认行为,而必须明确的调用preventDefault()
类的方法默认是不会绑定this的,对于回调函数的this问题,可以参考文章
this.handleClick.bind实际为Function.prototype.bind(),它会创建一个新的方法,新方法内部会设置this环境。
另外两种绑定this的方式:
  • 属性初始化器,即把回调函数handleClick当作类的属性来初始化,而不是成员函数。
  • 箭头函数,箭头函数没有自己的this,箭头函数在解析阶段会绑定父作用域的this
向事件处理程序传递参数:
通过箭头函数的方式,事件对象必须显示的进行传递,但是通过bind方式,事件对象会被隐式传递,并且是作为最后一个参数被传递。

列表&Keys

可以通过使用{}在JSX内构建一个元素集合。
Keys帮助React识别哪些元素发生了变化,从而进行动态更新,需要在其兄弟之间是唯一的,并且只有在其环绕数组的上下文中才有意义。Keys不会作为props的属性传递给子组件。

表单

在HTML中,像<input>, <textarea><select>这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。其值由React控制的输入表单元素称为“受控组件”。
当你有处理多个受控的input元素时,你可以通过给每个元素添加一个name属性,来让处理函数根据 event.target.name的值来选择做什么。
要编写一个非受控组件,而非为每个状态更新编写事件处理程序,你可以 使用 ref 从 DOM 获取表单值。

状态提升

在React中,状态分享是通过将state数据提升至离需要这些数据的组件最近的父组件来完成的。这就是所谓的状态提升
在React应用中,对应任何可变数据理应只有一个单一“数据源”。通常,状态都是首先添加在需要渲染数据的组件中。然后,如果另一个组件也需要这些数据,你可以将数据提升至离它们最近的共同祖先中。你应该依赖自上而下的数据流,而不是尝试在不同组件中同步状态。

组合vs继承

建议使用组合而不是继承来复用组件之间的代码。可以使用children属性将子元素直接传递到输出。

深入JSX

本质上来讲,JSX 只是为 React.createElement(component, props, ...children) 方法提供的语法糖。
用户定义组件必须首字母大写。
JSX的属性
  • 使用JavaScript表达式作为属性;
  • 字符串常量;
  • 属性默认为"True";
  • 可以使用...作为展开符来传递整个属性对象;
JSX中的子代
在既包含开始标签又包含结束标签的 JSX 表达式中,这两个标签之间的内容被传递为专门的属性:props.children
  • 字符串字面量作为子代;
  • JSX子代;
  • JavaScript表达式作为子代;
  • 函数作为子代;
  • 布尔值、Null 和 Undefined 被忽略(如果你想让类似 falsetruenull undefined 出现在输出中,必须先把它转换成字符串);

路由(0.13.x版本)

路由配置

相对路径:不以/开头的路径;绝对路径:以/开头的路径。
两种配置方式(5.0版本配置文件请参考文档):
Route可以定义onEnteronLeave两个hook,这些hook会在页面跳转确认时触发一次。
在路由跳转过程中,onLeave hook 会在所有将离开的路由中触发,从最下层的子路由开始直到最外层父路由结束。然后onEnter hook会从最外层的父路由开始直到最下层子路由结束。

路由匹配原理

路由拥有三个属性来决定是否匹配一个URL:
  • 嵌套关系
    • 当一个给定的URL被调用时,整个集合中命中部分都会被渲染。
  • 路径语法
    • :paramName - 匹配一段位于 /、? 或 # 之后的 URL。 命中的部分将被作为一个参数;
    • () - 在它内部的内容被认为是可选的;
    • * - 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并创建一个 splat 参数。
  • 优先级
    • 路由算法会根据定义的顺序自顶向下匹配路由。

Histories

React Router是建立在history之上的。history可以监听浏览器地址栏的变化,并解析这个URL转化为location对象,然后router使用它匹配到路由,最后正确的渲染对应的组件。
常见的history有三种,但是你也可以使用 React Router 实现自定义的 history。
  • browserHistory
    • 推荐的模式,它使用浏览器的History API用于处理URL,创建一个像example.com/some/path这样真实的 URL。对于不支持window.historyAPI的浏览器,任何调用调转的应用就会导致全页面刷新。
  • hashHistory
    • Hash history 使用 URL 中的 hash(#)部分去创建形如 example.com/#/some/path 的路由,不推荐在实际线上环境中使用。
  • createMemoryHistory
    • Memory history 不会在地址栏被操作或读取,必须显示的创建它。
使用实例:

IndexRoute与IndexLinks

如果在app中使用<Link to="/">Home</Link>,它会一直处于激活状态,因为所有的URL开头都是/,如果需要在Home路由被渲染后才激活指向/的链接,请使用<IndexLink to="/">Home</IndexLink>

动态路由

程序应当只加载当前渲染页所需的JavaScript,将代码分拆成多个小包,在用户浏览过程中按需加载。
React Router里的路径匹配以及组件加载都是异步完成的,不仅允许你延迟加载组件,而且还可以延迟加载路由配置。
Route 可以定义 getChildRoutesgetIndexRoute getComponents 这几个函数。它们都是异步执行,并且只有在需要时才被调用。 React Router 会逐渐的匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。

跳转前确认

React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件可以拦截正在发生的跳转,或在离开 route 前提示用户。routerWillLeave 返回值有以下两种:
  1. return false 取消此次跳转;
  1. return 返回提示信息,在离开 route 前提示用户进行确认。

服务端渲染

与客户端渲染区别:
  • 发生错误时发送一个 500 的响应
  • 需要重定向时发送一个 30x 的响应
  • 在渲染之前获得数据 (用 router 帮你完成这点)
为了迎合这一需求,你要在 <Router> API 下一层使用:
  • 使用 match 在渲染之前根据 location 匹配 route
  • 使用 RoutingContext 同步渲染 route 组件

组件生命周期

路由配置如下:
用户打开应用'/'页面
App: componentDidMount
Home: componentDidMount
Invoice: N/A
Account: N/A
 
用户从'/'跳转到'/invoice/123'
App: componentWillReceiveProps ⇒ componentDidUpdate
Home: componentWillUnmount
Invoice: componentDidMount
Account: N/A
 
用户从'/invoice/123'跳转到'/invoice/456'
App: componentWillReceiveProps ⇒ componentDidUpdate
Home: N/A
Invoice: componentWillReceiveProps ⇒ componentDidUpdate
Account: N/A
 
用户从 '/invoice/789' 跳转到 '/accounts/123'
App: componentWillReceiveProps ⇒ componentDidUpdate
Home: N/A
Invoice: componentWillUnmount
Account: componentDidMount

组件外部使用导航

使用history的API操作导航:
  • history.push(path, [state])
  • history.replace(path, [state])
  • history.go(n)
  • history.goBack()
  • history.goForward()
  • history.canGo(n) (only in createMemoryHistory)
badge