JSX简介
JSX是一种JavaScript的语法扩展,可以在大括号内中使用任何JavaScript表达式。
推荐在JSX代码的外面扩上一个小括号,这样可以防止分号自动插入的bug。
可以使用引号来定义以字符串为值的属性,也可以使用大括号定义以JavaScript表达式为值的属性。
闭合标签需要在结尾处用
/>
标记。使用小驼峰命名来定义属性的名称。
更多ES6的语法介绍,请参考下面内容
const element = <h1>Hello, world</h1>;
<div id="root"></div>
const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root'));
ReactDOM.render()
方法。function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
组件名称必须以大写字母开头。
组件的返回值只能有一个根元素。
所有的React组件必须像纯函数那样使用它们的props。
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
React.Component
类,并且重写其中的render()
方法即可。props
接收外部状态,通过state
保存内部状态。this.setState()
,否则界面不会动态更新。setState()
调用合并成一个调用来提高性能,如果想获取上一次的state和props,需要传递一个函数给setState()
方法,调用方式如下:this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
preventDefault()
。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> ); } } ReactDOM.render( <Toggle />, document.getElementById('root') );
this.handleClick.bind
实际为Function.prototype.bind()
,它会创建一个新的方法,新方法内部会设置this
环境。this
的方式:handleClick
当作类的属性来初始化,而不是成员函数。class LoggingButton extends React.Component { // This syntax ensures `this` is bound within handleClick. // Warning: this is *experimental* syntax. handleClick = () => { console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}> Click me </button> ); } }
this
,箭头函数在解析阶段会绑定父作用域的this
。class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } render() { // This syntax ensures `this` is bound within handleClick return ( <button onClick={(e) => this.handleClick(e)}> Click me </button> ); } }
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
React.createElement(component, props, ...children)
方法提供的语法糖。...
作为展开符来传递整个属性对象;false
、true
、null
或 undefined
出现在输出中,必须先把它转换成字符串);/
开头的路径;绝对路径:以/
开头的路径。import { Redirect } from 'react-router' React.render(( <Router> <Route path="/" component={App}> <IndexRoute component={Dashboard} /> <Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Route path="/messages/:id" component={Message} /> {/* 跳转 /inbox/messages/:id 到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> </Route> </Route> </Router> ), document.body)
const routeConfig = [ { path: '/', component: App, indexRoute: { component: Dashboard }, childRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox, childRoutes: [ { path: '/messages/:id', component: Message }, { path: 'messages/:id', onEnter: function (nextState, replaceState) { replaceState(null, '/messages/' + nextState.params.id) } } ] } ] } ] React.render(<Router routes={routeConfig} />, document.body)
onEnter
和onLeave
两个hook,这些hook会在页面跳转确认时触发一次。onLeave
hook 会在所有将离开的路由中触发,从最下层的子路由开始直到最外层父路由结束。然后onEnter
hook会从最外层的父路由开始直到最下层子路由结束。:paramName
- 匹配一段位于 /、? 或 # 之后的 URL。 命中的部分将被作为一个参数;()
- 在它内部的内容被认为是可选的;*
- 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并创建一个 splat 参数。<Route path="/hello/:name"> // 匹配 /hello/michael 和 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello, /hello/michael 和 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
location
对象,然后router使用它匹配到路由,最后正确的渲染对应的组件。browserHistory
hashHistory
#
)部分去创建形如 example.com/#/some/path 的路由,不推荐在实际线上环境中使用。createMemoryHistory
const history = createMemoryHistory(location)
import React from 'react' import { render } from 'react-dom' import { browserHistory, Router, Route, IndexRoute } from 'react-router' import App from '../components/App' import Home from '../components/Home' import About from '../components/About' import Features from '../components/Features' render( <Router history={browserHistory}> <Route path='/' component={App}> <IndexRoute component={Home} /> <Route path='about' component={About} /> <Route path='features' component={Features} /> </Route> </Router>, document.getElementById('app') )
<Router> <Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>
<Link to="/">Home</Link>
,它会一直处于激活状态,因为所有的URL开头都是/
,如果需要在Home路由被渲染后才激活指向/的链接,请使用<IndexLink to="/">Home</IndexLink>
。getChildRoutes
,getIndexRoute
和 getComponents
这几个函数。它们都是异步执行,并且只有在需要时才被调用。 React Router 会逐渐的匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。const CourseRoute = { path: 'course/:courseId', getChildRoutes(location, callback) { require.ensure([], function (require) { callback(null, [ require('./routes/Announcements'), require('./routes/Assignments'), require('./routes/Grades'), ]) }) }, getIndexRoute(location, callback) { require.ensure([], function (require) { callback(null, require('./components/Index')) }) }, getComponents(location, callback) { require.ensure([], function (require) { callback(null, require('./components/Course')) }) } }
500
的响应30x
的响应<Router>
API 下一层使用:match
在渲染之前根据 location 匹配 routeRoutingContext
同步渲染 route 组件import { renderToString } from 'react-dom/server' import { match, RoutingContext } from 'react-router' import routes from './routes' serve((req, res) => { // 注意!这里的 req.url 应该是从初始请求中获得的 // 完整的 URL 路径,包括查询字符串。 match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { if (error) { res.send(500, error.message) } else if (redirectLocation) { res.redirect(302, redirectLocation.pathname + redirectLocation.search) } else if (renderProps) { res.send(200, renderToString(<RoutingContext {...renderProps} />)) } else { res.send(404, 'Not found') } }) })
<Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="invoices/:invoiceId" component={Invoice}/> <Route path="accounts/:accountId" component={Account}/> </Route>
App: componentDidMount
Home: componentDidMount
Invoice: N/A
Account: N/A
App: componentWillReceiveProps ⇒ componentDidUpdate
Home: componentWillUnmount
Invoice: componentDidMount
Account: N/A
App: componentWillReceiveProps ⇒ componentDidUpdate
Home: N/A
Invoice: componentWillReceiveProps ⇒ componentDidUpdate
Account: N/A
App: componentWillReceiveProps ⇒ componentDidUpdate
Home: N/A
Invoice: componentWillUnmount
Account: componentDidMount
HooksContext<Route component={App}> {/* ... 其它 route */} </Route> const App = React.createClass({ getInitialState() { return { showBackButton: false } }, componentWillReceiveProps(nextProps) { const routeChanged = nextProps.location !== this.props.location this.setState({ showBackButton: routeChanged }) } })