codepen 上的代碼請 fork 后再修改。
環境基礎
- Chrome、FireFox等主流瀏覽器陸續支持 ES6+ 語法。
- QQ、360、搜狗等瀏覽器已支持 ES6+ 語法。
- 微軟全面轉移到 Edge。
- 淘寶不再支持 IE 8 。
- 項目已使用 webpack、babel 等來提供轉義支持。
簡介
A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
一個用于搭建用戶界面的js庫
核心思路:假定按照人機交互活動的不同狀態來設計UI,根據狀態的遷移來自動渲染頁面。同時,react 通過組件化來分治狀態。
Hello World
// 引入 js 類庫
import React from 'react'
import ReactDOM from 'react-dom'
// 定義 HelloWorld 組件
class HelloWorld extends React.Component {
render() {
// return 中是 JSX 語法
return (
<div>
Hello, world!
</div>
);
}
}
// 找到 HTML 中的 id="root" 的標簽,將 Hello 作為子元素插入
ReactDOM.render(<HelloWorld/>, document.getElementById('root'));
ReactDOM.render()
在客戶端將 react 組件渲染成 HTML 的方法,在一般的 web app 中只在入口 js 文件中寫一處。
JSX
一種在 js 中書寫 HTML 的簡單方式,可以在其中通過 {}
來使用 js 表達式、變量、函數等。需要注意的是,由于 class 是 js 的關鍵字/保留字,所以 HTML 中的 class
需要寫成 className
。簡單語法如下。
const generateJSX = (arg) => {
return <h3>{arg}</h3>
};
class HelloWorld extends React.Component {
constructor(props) {
super(props)
this.state = {
val: {key: 'JSX中使用對象的值'},
ifelse: '與其寫那些匿名函數箭頭函數,不如提出來寫更好維護。'
};
}
render() {
return (
<div>
<h1>JSX</h1>
<h3>{this.state.val.key}</h3>
<h3>{true?'JSX中不能直接使用if-else,可以使用三元表達式':null}</h3>
{(function() {
if (true) {
return <h3>在JSX中使用立即執行的匿名函數來寫if-else</h3>
}
})()}
{true && <h3>單if的時候可以用表達式&&jsx的方式來寫</h3>}
{(() => {
if (true)
return <h3>在JSX中使用立即執行的箭頭函數來寫if-else</h3>
})()}
{generateJSX(this.state.ifelse)}
</div>
);
}
}
ReactDOM.render(<HelloWorld/>, document.getElementById('root'));
需要注意的是,頂層只能有一對標簽。
// bad
return
<div></div>
<div></div>
// good
return
<div>
<div></div>
</div>
為什么要使用 JSX
使用 JSX 時。
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>
不使用 JSX 時。
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
就一般的 web 開發來說,顯然第一種比較直觀;對于不一般的 web 開發者來說,也還是第一種比較直觀。
React 組件(Component)
React 支持自定義組件,而組件化,是工程化的基礎之一。React 支持自定義組件和傳參(props)。將自定義組件類比 HTML 標簽,參數就類似標簽屬性,能在 HTML 中怎么使用標簽,就能在 JSX 中怎么使用組件。
定義組件
一般由兩種方式:函數和 es 6 class 。
函數方式:
function genComA(props={}) {
return <div>hello</div>
}
const genComB = (props={}) => {
return <div>hello</div>
};
class:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
傳參
前面已經有過很多示例。
在 React 中,可以傳遞給組件的參數類型和 js 一致,包括 number、string、boolean、object、function 等。在組件中通過 this.props
獲取接收到的參數。
class HiName extends React.Component {
render() {
return <div>Hi, {this.props.name}</div>
}
}
class HelloWorld extends React.Component {
render() {
return (
<div>
<HiName name={'sweetie'}/>
</div>
);
}
}
// 輸出 hi, sweetie
以函數形式創建的組件,則沒有 this
,也就沒有 this.props
,是通過封裝成對象的形式傳遞,直接用 js 函數傳參的方式獲取即可。注:必須首字母大寫,否則失效。
function Bye(props) {
return <div>{props.name}, goodbye.</div>
}
class HelloWorld extends React.Component {
render() {
return (
<div>
<Bye name={'sweetie'}/>
</div>
);
}
}
this.props.children
React 中有個保留的參數,叫 this.props.children
,主要用于封裝和動態加載子組件。常見的用法有單頁面 web app 中 header 和 footer 固定,內部內容動態變化等。
class App extends Component {
render() {
return (
<div>
<Header />
<div>
{this.props.children}
</div>
<Footer />
</div>
)
}
}
參數校驗
React 支持參數校驗,包括 js 的數據類型、自定義類型、非空、枚舉等。
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: React.PropTypes.string
};
具體參考官網。
狀態(state)
React 組件基于自身的 state 變化來觸發 render ,并進行相應的計算,達到渲染的效果。所以 state 的使用,是 React 中最重要的部分。
- React 組件的生命周期節點為
mount -> (update) -> unmount
。 - 其中
mount
和unmount
在生命周期中只執行一次,update
執行 0 到多次。 -
mount
和update
都會觸發render
。 - 對于
mount
和update
都有will
和did
兩種處理函數,對于unmount
只有componentWillUnmount
。 - 提供
shouldComponentUpdate
來處理比較復雜的情況下組件 state 變化是否渲染,以提升性能。緊急逃生,慎用。 - 提供
componentWillReceiveProps
用于組件mount
之后接收參數再次更新 state 。
class PropsCount extends React.Component {
componentWillMount() {
console.log('PropsCount will mount')
}
componentDidMount() {
console.log('PropsCount did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('PropsCount will update')
}
componentDidUpdate(prevProps, provState) {
console.log('PropsCount did update')
}
render() {
console.log('PropsCount render')
return <div>count update by props: {this.props.count}</div>
}
}
class StateCount extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.count};
}
componentWillMount() {
console.log('StateCount will mount')
}
componentDidMount() {
console.log('StateCount did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('StateCount will update')
}
componentDidUpdate(prevProps, provState) {
console.log('StateCount did update')
}
componentWillReceiveProps(nextProps) {
this.setState({count: nextProps.count});
}
render() {
console.log('StateCount render')
return <div>count update by state: {this.state.count}</div>
}
}
class Init extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.count}
}
componentWillMount() {
console.log('Init will mount')
}
componentDidMount() {
console.log('Init did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('Init will update')
}
componentDidUpdate(prevProps, provState) {
console.log('Init did update')
}
render() {
console.log('Init render')
return <div>init: {this.state.count}</div>
}
}
class HelloWorld extends React.Component {
addOne = () => {
this.setState({count: this.state.count + 1});
};
constructor(props) {
super(props);
this.state = {count: 0};
}
componentWillMount() {
console.log('HelloWorld will mount')
}
componentDidMount() {
console.log('HelloWorld did mount')
}
componentWillUpdate(nextProps, nextState) {
console.log('HelloWorld will update')
}
componentDidUpdate(prevProps, provState) {
console.log('HelloWorld did update')
}
render() {
console.log('HelloWorld render')
return (
<div>
<Init count={this.state.count} />
<button value="addOne" onClick={this.addOne}>add</button>
<PropsCount count={this.state.count} />
<StateCount count={this.state.count} />
</div>
);
}
}
ReactDOM.render(<HelloWorld/>, document.getElementById('root'));
注意:在 componentWillMount componentWillUpdate
中不要使用 setState
。初始化的異步請求最好放在 componentDidMount
里,其他初始化的同步操作放在 constructor
里。
處理事件和獲取值
官網參考。
class A extends React.Component {
constructor (props) {
super(props)
this.state={name:null}
}
onChange = (e) => {
this.setState({name: e.target.value})
};
render () {
return <div>
<input type="text" onChange={this.onChange} />
<br />
{this.state.name && <label>hi, {this.state.name}</label>}
</div>;
}
}