首先我說說我爲何學React,React能夠說是三大框架中思想最爲先進的一款,在國內Vue雖然很火,可是Vue也開始引入了React的一些思想,好比虛擬DOM,接着Vue3.0還會引入JSX,React很是注重組件化和複用性,學習React能夠比之前更熟悉一些架構知識。
工做時間也有3個多月了,該學會寫東西總結了,最近利用工做時間學習React,今天又有空寫一下總結,怕過了一段時間忘記,這篇文章我是看了 React小書 總結下來的,若是有人跟我同樣是React入門的話,建議去React小書學起,從腳手架學起真的是快不少,咱們老師教咱們學東西首先要學會用,再學原理。
這篇文章不適合大神看,若是有大神想指導能夠在下面發佈評論。
React是個幫你構建頁面UI的庫,他至關於MVC裏面的View,React將咱們界面分紅各個獨立的小塊,每一個小塊都是一個組件,而這些組件能夠互相嵌套丶結合,而後就造成了頁面css
因爲React要配合其餘工具和庫輔助,例如編譯須要babel,組織代碼須要redux等第三方狀態管理工具,寫單頁面應用須要應用到React-router,這也至關於React的全家桶。html
npm install -g create-react-app
前端
create-react-app my-app
node
npm
切換成cnpm
就能夠解決了。
npm config set registry https://registry.npm.taobao.org
//將npm切換成cnpmresource
下面的app裏面的extensions
打開後看到有git文件夾,刪除,重啓。建立完成以後而後cd
到項目根目錄而後npm start
就能夠啓動項目了react
建立完成以後cd
到建立的目錄而後能夠看到腳手架幫咱們建立的結構git
node_modules | public | scripts | src |
---|---|---|---|
依賴文件 | 靜態庫 | 項目啓動配置 | 開發目錄 |
開發時候在src目錄上開發就好了,若是要查看項目配置,能夠輸入npm run eject
把config
目錄暴露出來,這個是默認隱藏的,你也能夠在node_modules
目錄上找到,這個目錄是項目的配置。若是運行npm run eject
報錯的話,把代碼儲存到本地就好了git add .
,而後git commit -m ''
,接着從新運行指令,這執行是不可逆的。npm
在public
有個index.html
這是整個項目的頁面,以後咱們要把寫好的東西渲染到裏面去。
src
能夠看到一個index.js
,因爲寫React幾乎都是組件,而index
通常用來渲染其餘組件redux
首先咱們把index.js
的代碼改爲這樣再看看效果數組
import React, { Component } from 'react' // 寫React必需要引入這兩樣東西
import ReactDOM from 'react-dom' // 組件不須要引入,他只有一個做用,就是渲染DOM
import './index.css' // 能夠直接引入css或者是sass,也能夠引入圖片
<!-- 使用組件繼承Component類 -->
class Header extends Component {
render () { // render方法把裏面東西jsx返回
return (
<!-- jsx -->
<div>
<h1>React大法好</h1>
</div>
)
}
}
<!-- 渲染出去 -->
ReactDOM.render(
<Header />,
document.getElementById('root')
)
複製代碼
JSX 是 JavaScript 語言的一種語法擴展,長得像 HTML,但並非 HTML。瀏覽器
平時咱們寫html
是這樣寫的
<div class='box' id='content'>
<div class='title'>Hello</div>
<button>Click</button>
</div>
複製代碼
若是咱們用JS對象
來表示的話
{
tag: 'div',
attrs: { className: 'box', id: 'content'},
children: [
{
tag: 'div',
arrts: { className: 'title' },
children: ['Hello']
},
{
tag: 'button',
attrs: null,
children: ['Click']
}
]
}
複製代碼
沒錯,其實JSX
也至關於JS對象
,React也會根據這樣編譯,轉化爲HTML結構以後就能夠拿去構建真正的DOM,這是最後那段渲染DOM所作的事情
ReactDOM.render(
<Header />,
document.getElementById('root')
)
複製代碼
這是整個過程流程圖,單獨把ReactDOM抽出來的緣由是由於咱們能夠渲染到Canvas或者轉化原生App(ReactNative)
React中一切皆爲組件,寫組件的時候通常都須要繼承React.js的Component
,這個方法必須返回一個JSX
元素,而返回並列JSX
元素是不合法的
<!-- bad -->
render() {
return {
<div>one</div>
<div>two</div>
}
}
複製代碼
必需要用一個外層元素包起來,不能有多個外層元素
<!-- good -->
render() {
return {
<div>
<div>one</div>
<div>two</div>
</div>
}
}
複製代碼
表達式能夠插入變量,還能夠計算,還能夠條件返回,而且能夠寫函數,render會把真實的內容返回,特別的靈活
render() {
const word = 'word'
const isGoodWord = true
return(
<div>
<h1>HELLO {word}</h1> // 變量
<h1>{1 + 2}</h1> // 計算
<h1>{(function(){ return 'React' })()}</h1> // 函數
// 條件返回
{
isGoodWord
? <span>好文章</span>
: <span>壞文章</span>
}
</div>
)
}
複製代碼
表達式不只能夠插入標籤內部,還能夠插入屬性
render() {
const className = 'header'
return(
<div className={ className }></div>
)
}
複製代碼
因爲class
是JS的關鍵字,React.js換成了className
,還有一個就是for
換成了htmlfor
自定義的組件都必需要用大寫字母開頭
class Title extends Component {
render () {
return (
<h1>標題標題標題</h1>
)
}
}
class Header extends Component {
render () {
return (
<div>
<!-- 渲染三次 -->
<Title />
<Title />
<Title />
</div>
)
}
}
複製代碼
能夠直接在Header
標籤裏面使用,React.js會在<Title />
,組件的render
方法表示的JSX
內容渲染出來,
它會顯示在相應的位置上
在須要監聽事件的元素加上屬性相似 onClick
onKeyDown
這樣的屬性便可,事件屬性必須使用駝峯命名法。
class Title extends Component {
handleOnClickTitle(hello) {
console.log(hello)
console.log(this)
}
render () {
return (
<!-- 綁定事件時須要綁定this,不綁定則拿不到 -->
<h1 onClick={this.handleOnClickTitle.bind(this, 'hello')}>標題標題標題</h1>
)
}
}
複製代碼
在 React.js 不須要手動調用瀏覽器原生的addEventListener
進行事件監聽。React.js 幫咱們封裝好了一系列的 on* 的屬性,當你須要爲某個元素監聽某個事件的時候,只須要簡單地給它加上 on* 就能夠了。並且你不須要考慮不一樣瀏覽器兼容性的問題,React.js 都幫咱們封裝好這些細節了。
React.js 將瀏覽器原生的event
對象封裝了一下,對外提供統一的 API 和屬性,這樣你就不用考慮不一樣瀏覽器的兼容性問題。
若是你不手動綁定this
在函數裏面打印的會是undefined
,這是由於 React.js 調用你所傳給它的方法的時候,並非經過對象方法的方式調用,而是直接經過函數調用,因此事件監聽函數內並不能經過this 獲取到實例若是你想在事件函數當中使用當前的實例,你須要手動地將實例方法 bind 到當前實例上再傳入給 React.js
state
是組件的狀態,咱們能夠用它來進行狀態切換,如顯示或隱藏,或者是改變className
等操做。
class Index extends Component {
constructor(props) {
super(props)
<!-- 狀態 -->
this.state = {
isShow: true
}
}
render () {
handleState() {
this.setState({
isShow: !this.state.isShow
})
}
return (
<div className='index'>
{
this.state.isShow === true
? <h1>顯示<h1/> : null
}
<button onClick={this.handleState.bind(this)}></button>
</div>
)
}
}
複製代碼
setState
方法由父類 Component 所提供。當咱們調用這個函數的時候,React.js 會更新組件的狀態 state ,而且從新調用 render 方法,而後再把 render 方法所渲染的最新的內容顯示到頁面上。
setState
能夠接受函數或者對象做爲參數,這裏要注意一個問題,當你調用setState
時React並不會立刻修改state,而是把這個對象放在一個更新隊列,稍後纔會從隊列當中把新的狀態提取出來合併到 state 當中,而後再觸發組件更新。
handleState() {
console.log(this.state.isShow) // true
this.setState({
isShow: !this.state.isShow
})
console.log(this.state.isShow) // true
}
複製代碼
若是要作到後續操做依賴前一個setState
的這種操做的話,咱們能夠把參數換成函數。
handleState() {
console.log(this.state.isShow) // true
this.setState((prevState) => {
return { isShow: !this.state.isShow }
})
console.log(this.state.isShow) // false
}
複製代碼
在一個函數裏面同步使用setState
它只會執行一個,React.js 內部會把js
事件循環中的消息隊列的同一個消息中的setState
都進行合併之後再從新渲染組件,若是你先調用一個setState
而後用定時器調用的話,就會觸發兩次
說到setState
還要說到一個React的受控組件,好比 input
、textarea
這種輸入框,若是你直接寫這種標籤時候React會報一個錯誤。
state
控制,只要相似於 <input />
、<textarea />
這樣的輸入控件被設置了 value 值,那麼它們的值永遠以被設置的值爲準。這時候只要給他綁定一個onChange
就好了。 class InputWithUserName extends Component {
constructor() {
super()
this.state = {
value: ''
}
}
changeValue(event) {
console.log(event.target.value)
this.setState({
value: event.target.value
})
}
render() {
return (
<div>
{this.props.content}
<input type="text" value={this.state.value} onChange={this.changeValue.bind(this)}/>
</div>
)
}
}
複製代碼
每一個組件均可以接受一個 props 參數,它是一個對象,包含了全部你對這個組件的配置。
在使用一個組件的時候,能夠把參數放在標籤的屬性當中,全部的屬性都會做爲 props 對象的鍵值
class Index extends Component {
render () {
return (
<div className='index'>
<!-- 這裏兩個花括號只是JSX裏面再嵌套一個對象而已 -->
<!-- 還能夠傳函數 -->
<Main options={{title: 'React', content: 'React大法好'}} onClick={()=> console.log('Click in Index')} />
</div>
)
}
}
class Main extends Component {
<!-- 若是沒有東西傳過來能夠在這裏設置默認值 -->
static defaultProps = {
options = {
title: '默認頭部',
content: '默認內容'
}
}
<!-- 這裏能夠省略,由於React會自動生成 -->
constructor(props) {
super(props)
}
handleIndexClick() {
if(this.props.onClick) {
<!-- 執行Index傳過來的函數 -->
this.props.onClick()
}
}
render() {
const options = this.props.options
<div className='main'>
<h1>{options.title}</h1>
<p>{options.content}</p>
<button onClick={this.handleIndexClick.bind(this)}></button
</div>
}
}
複製代碼
不要試圖去改變傳過來的props
,React.js 但願一個組件在輸入肯定的 props 的時候,可以輸出肯定的 UI 顯示形態。若是 props 渲染過程當中能夠被修改,那麼就會致使這個組件顯示形態和行爲變得不可預測,這樣會可能會給組件使用者帶來困惑。
handleIndexClick() {
this.props.options = null; // 報錯
if(this.props.onClick) {
<!-- 執行Index傳過來的函數 -->
this.props.onClick()
}
}
複製代碼
但這並不意味着由props
決定的顯示形態不能被修改。組件的使用者能夠主動地經過從新渲染的方式把新的props
傳入組件當中,這樣這個組件中由props
決定的顯示形態也會獲得相應的改變。
class Index extends Component {
constructor (props) {
super(props)
this.state = {
options: {
title: 'React',
content: 'React大法好'
},
}
}
<!-- 修改傳過去的值 -->
handleOptionsChange() {
this.setState({
options: {
title: 'Vue',
content: 'Vue大法好'
},
})
}
render () {
return (
<div className='index'>
<Main options={ this.state.options } onClick={()=> console.log('Click in Index')} />
<!-- 點擊修改 -->
<button onClick={this.handleOptionsChange.bind(this)}></button>
</div>
)
}
}
複製代碼
因爲setState
會致使Index
從新渲染,因此<Main />
也會接受到新的值而且從新渲染,這樣就能作到修改<Main />
的顯示效果
state
和 props
state
的主要做用是用於組件保存、控制、修改本身的可變狀態。state
在組件內部初始化,能夠被組件自身修改,而外部不能訪問也不能修改。你能夠認爲 state
是一個局部的、只能被組件自身控制的數據源。state
中狀態能夠經過 this.setState
方法進行更新,setState
會致使組件的從新渲染。
props
的主要做用是讓使用該組件的父組件能夠傳入參數來配置該組件。它是外部傳進來的配置參數,組件內部沒法控制也沒法修改。除非外部組件主動傳入新的 props
,不然組件的 props
永遠保持不變。
state
和 props
有着千絲萬縷的關係。它們均可以決定組件的行爲和顯示形態。一個組件的 state
中的數據能夠經過 props
傳給子組件,一個組件可使用外部傳入的 props
來初始化本身的 state
。可是它們的職責其實很是明晰分明:state
是讓組件控制本身的狀態,props
是讓外部對組件本身進行配置。
若是你以爲仍是搞不清 state
和 props
的使用場景,那麼請記住一個簡單的規則:儘可能少地用 state
,儘可能多地用 props
。沒有 state
的組件叫 無狀態組件(stateless component),設置了 state
的叫作 有狀態組件件(stateful component)。由於狀態會帶來管理的複雜性,咱們儘可能多地寫無狀態組件,儘可能少地寫有狀態的組件。這樣會下降代碼維護的難度,也會在必定程度上加強組件的可複用性。前端應用狀態管理是一個複雜的問題。
有狀態組件
class HelloWorld extends Component {
constructor() {
super()
this.state = {
isShow: true
}
}
sayHi () {
alert('Hello World')
}
render () {
return (
<div onClick={this.sayHi.bind(this)}>Hello World</div>
)
}
}
複製代碼
無狀態組件
const HelloWorld = (props) => {
const sayHi = (event) => alert('Hello World')
return (
<div onClick={sayHi}>Hello World</div>
)
}
複製代碼
函數式的組件編寫方式是一個函數就是一個組件,你能夠和之前同樣經過<HellWorld />
使用該組件。不一樣的是,函數式組件只能接受 props
而沒法像跟類組件同樣能夠在 constructor 裏面初始化 state
。你能夠理解函數式組件就是一種只能接受 props
和提供render
方法的類組件。
直接使用ES6的map遍歷代碼比較簡潔,也能夠手動寫循環構建列表的JSX,在React遍歷使用map是很是常見的。
const users = [
{ username: 'Jerry', age: 21, gender: 'male' },
{ username: 'Tomy', age: 22, gender: 'male' },
{ username: 'Lily', age: 19, gender: 'female' },
{ username: 'Lucy', age: 20, gender: 'female' }
]
class Index extends Component {
render () {
return (
<div>
{users.map((user, i) => <User user={user} key={i} />)}
</div>
)
}
}
class User extends Component {
render () {
const { user } = this.props
return (
<div>
<div>姓名:{user.username}</div>
<div>年齡:{user.age}</div>
<div>性別:{user.gender}</div>
<hr />
</div>
)
}
}
複製代碼
React.js 的是很是高效的,它高效依賴於所謂的 Virtual-DOM 策略。簡單來講,能複用的話 React.js 就會盡可能複用,沒有必要的話絕對不碰 DOM。對於列表元素來講也是這樣,可是處理列表元素的複用性會有一個問題:元素可能會在一個列表中改變位置。但其實 React.js 只須要交換一下 DOM 位置就好了,可是它並不知道其實咱們只是改變了元素的位置,因此它會從新渲染後面兩個元素(再執行 Virtual-DOM 策略),這樣會大大增長 DOM 操做。但若是給每一個元素加上惟一的標識,React.js 就能夠知道這兩個元素只是交換了位置
render () {
return (
<div>
{users.map((user, i) => <User user={user} key={i} />)}
</div>
)
}
複製代碼
這樣 React.js 就簡單的經過 key 來判斷出來,這兩個列表元素只是交換了位置,能夠儘可能複用元素內部的結構。
對於用表達式套數組羅列到頁面上的元素,都要爲每一個元素加上 key 屬性,這個 key 必須是每一個元素惟一的標識,通常後臺會返回。