最近接手了一個React開源項目,上手前看了一些react的基礎教程就擼了,以前有着半年Angular轉2年Vue的前端經驗,可是仍是對React心生畏懼。javascript
項目作了大概一個周了,發現果真世界上代碼都是同樣的,React幾乎和Vue的用法同樣,惟一的學習成本在於語法(若是你能理解前端框架和組件化的概念)。html
可是三大框架裏面,惟一讓人以爲讚許的、適合上手的官方文檔,我我的認爲,我的!是Vue,因此打算採用Vue的文檔結構將Vue中的一些語法和功能對應的React的實現方式,向大家一一道來,帶大家再讀Vue文檔,順便學會React!前端
- 整篇文章結構採用Vue的文檔目錄,作了一些刪減,有的我還沒用到,有的可能對應的描述我思路不清晰就摘掉了
- 每一個部分通常會先把Vue文檔中的功能實現或者語法搬過來,而後手打對應的React中實現或者語法。
- 整篇文章一萬字出頭,建議仔細看每一個例子,你可能須要20分鐘??
開讀以前,心理默默大聲念出那句:vue
這一部分就不分別介紹兩個框架了,由於你看這篇文章表明着必定要熟悉Vue的基本語法和理念,不然建議 × 掉該文章。另外建議你不要是React的精通大神,由於接下來的內容如Vue的官方文檔同樣基礎,建議 × 掉。java
vue的起步固然是vue-cli建立一個項目,這個地方就不上vue-cli代碼了,react也是同樣的。react
首先你須要安裝一個create-react-app
,它就是你要用的「vue-cli」了:es6
npm install -g create-react-app
複製代碼
而後咱們用它構建一個react項目:vue-cli
create-react-app react-demo
複製代碼
執行命令過程不一樣於vue-cli會給你選擇一些配置項,creat react會一直加載到最後結束,而後你就能夠看到一個嶄新的react項目基礎結構: npm
Vue.js 的核心是一個容許採用簡潔的模板語法來聲明式地將數據渲染進 DOM 的系統,語法使用v-bind和雙大括號:小程序
<div id="app">
<span v-bind:title="message">
{{ name }}
</span>
</div>
複製代碼
var app = new Vue({
el: '#app',
data: {
name: 'Hello Vue!'
message: '頁面加載於 ' + new Date().toLocaleString()
}
})
複製代碼
vue的模板綁定語法就很少說了,react中採用了幾乎一樣的雙向綁定方式:
import React, { Component } from 'react'
class DemoComponent extends Component {
constructor() {
super()
// 將this.state看作你vue中的data
this.state = {
name: 'hello',
title: '頁面加載於 ' + new Date().toLocaleString()
}
}
render() {
return (
// 單大括號的雙向綁定語法
<span title={this.state.title}>{this.state.name}</span>
)
}
}
export default DemoComponent
複製代碼
效果圖就不上了,太雞肋,簡單加個tip:
- react的雙向綁定使用
單括號
- react的雙向綁定屬性也直接使用單括號,
沒有冒號沒有引號
- react的雙向綁定的變量要使用
this.state.變量名
,而不是直接this引用變量名
vue中條件判斷使用v-if和v-show,循環使用v-for
<div id="app">
<p v-if="seen">如今你看到我了</p>
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
複製代碼
var app = new Vue({
el: '#app',
data: {
seen: true,
todos: [
{ text: '學習 JavaScript' },
{ text: '學習 Vue' },
{ text: '整個牛項目' }
]
}
})
複製代碼
而在react中可能就會略顯麻煩:
import React, { Component } from 'react'
class DemoComponent extends Component {
constructor() {
super()
this.state = {
seen: true,
todos: [
{ text: '學習 JavaScript' },
{ text: '學習 Vue' },
{ text: '整個牛項目' }
]
}
}
render() {
return (
<div> /* 使用三元表達式 */ {this.state.seen ? <p>如今你看到我了</p> : null } /* 使用三元表達式的另外一種 */ <p>{this.state.seen ? '如今你看到我了' : null}</p> /* 使用map()進行dom遍歷 */ <ol> {this.state.todos.map((item) => { return ( <li>{item.text}</li> ) })} </ol> </div>
)
}
}
export default DemoComponent
複製代碼
- react中條件判斷基本使用js條件表達式的特性實現(求科普有沒有v-show的對應)
- react中循環通常使用
map
方法去遍歷return須要循環的dom結構- 固然這只是基本用法
vue用 v-on 指令添加一個事件監聽器,經過它調用在 Vue 實例中定義的方法: vue還提供了 v-model 指令,它能輕鬆實現表單輸入和應用狀態之間的雙向綁定:
<div id="app">
<input v-model="message">
<button v-on:click="reverseMessage">反轉消息</button>
</div>
複製代碼
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
複製代碼
而後咱們看一下相應的如何在react中綁定事件和表單元素:
import React, { Component } from 'react'
class DemoComponent extends Component {
constructor() {
super()
this.state = {
message: 'Hello Vue.js!'
}
}
reverseMessage(event) {
this.setState({
message: this.state.message.split('').reverse().join('')
})
}
render() {
return (
<div> /* 單大括號綁定 */ <input value={this.state.message} /> <button onClick={this.reverseMessage.bind(this)}>反轉消息</button> </div> ) } } export default DemoComponent 複製代碼
知識點來了:
- 由於react使用的render函數,react全部特性幾乎都是使用js的語法來實現,而不是指令型語法
- 像click等事件,還有標籤屬性如title,palceholder都是直接使用大括號
- react中雙向綁定的變量不會隨js對變量的一系列操做修改而響應,任何雙向綁定的變量要達到在頁面渲染更新,須要使用react的
this.setState({})
語法,key爲state中的變量名,value爲更新後的變量值(若是寫過原生小程序應該很容易理解)- 方法能夠直接在Class中定義,不一樣於vue的存在methods對象中,且通常在綁定方法時經過bind將this傳進去,不然你的方法裏
this.
將會報錯,經典this做用域問題,自行科普。
每一個 Vue 應用都是經過用 Vue 函數建立一個新的 Vue 實例開始的:
vue實例:
var vm = new Vue({
// 選項
})
複製代碼
react實例:
import React, { Component } from 'react'
class DemoComponent extends Component {
// 內容
render () {
}
}
export default DemoComponent
複製代碼
知識點:
- react的一個組件以一個從react中繼承Component的Class類爲實例
- 一個react組件必須經過
render ()
返回JSX
,且只能返回一個JSX
元素(2
個必要條件)- vue和react的共同點是最外層必須有一個
單一
的根元素
標籤
愈來愈簡單是否是,next=>
data函數
返回一個對象來保存當前vue實例中的變量,vue實例中的自寫方法都放置在methods
中state
中,方法能夠直接在class中定義,若是你對ES6的class足夠了解,你很容易就能夠理解我在說什麼。(以爲彆扭的能夠重看一遍ES6)render() {
// render()函數中直接定義變量,畢竟它是個函數,這是合理的
let tip = "Hello!"
return (
<div> {{tip}} </div>
)
}
複製代碼
由於render是一個函數,因此你能夠直接在return一些dom結構以前定義函數內的變量。 這恰好再次強調以前我說的
「 由於react是使用的render函數,因此react全部特性幾乎都是使用js的語法來實現」
換個表達方式,這樣我能夠少打點字:
生命週期鉤子 | vue | react |
---|---|---|
實例建立前 | beforeCreate | - - - |
實例建立後 | created | constructor() |
DOM掛載前 | beforeMount | componentWillMount |
DOM掛載後 | mounted | componentDidMount |
數據更新時 | beforeUpdate | componentWillUpdate |
數據更新後 | updated | componentDidUpdate |
keep-alive激活時 | activated | |
keep-alive 組件停用時 | deactivated | |
實例銷燬前 | beforeDestroy | componentWillUnmount |
實例銷燬後 | destroyed | componentWillUnmount |
子組件發生錯誤時 | errorCaptured |
vue數據綁定最多見的形式就是使用「Mustache」語法 (雙大括號) 的文本插值:
<span>Message: {{ msg }}</span>
複製代碼
react直接使用大括號,這個已經提過:
<span>Message: { this.state.msg }</span>
複製代碼
(另外一樣不知有沒有對應的v-once實現,歡迎評論里科普)
vue使用v-html直接輸出真正的 HTML,假設咱們有一個叫rawHtml的html字符串模板:
<p>
<span v-html="rawHtml"></span>
</p>
複製代碼
react就簡單了,由於它是render函數,因此能夠:
render () {
const rawHtml= `<span> is span </span>`
return (
<div> <p>{rawHtml}</p> </div>
)
}
複製代碼
vue使用v-bind:
做用在 HTML 特性上:
<button v-bind:disabled="isButtonDisabled">Button</button>
複製代碼
react直接使用{}
綁定:
<button disabled={this.state.isButtonDisabled}>Button</button>
複製代碼
vue官方示例的基本表達式使用方式有:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
複製代碼
使用簡單的運算符,三元表達式,鏈式調用均可以,而react的使用則徹底是你平常js的使用方式,這3種均可以. 另外提一點vue中能夠直接使用{{}}綁定某一個methods或者計算屬性中的某一個方法名,好比:
<div>{{changeName()}}</div>
複製代碼
react中也能夠直接寫成一個當即執行函數表達式返回:
<div>{(function () { return 'is div'})()}</div>
複製代碼
沒看到有關react的計算屬性用法,應該是react的getter不會使用緩存,割了
vue 經過 watch 選項提供了一個更通用的方法,來響應數據的變化,具體不叭叭了,由於:
react 的狀態都是手動 setstate 變化的,react 不監聽數據變化,
這不是我割的……
vue能夠傳給 v-bind:class 一個對象,以動態地切換 class:
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div>
複製代碼
active和 'text-danger' 這2個 class 存在與否將取決於數據屬性 isActive和hasError的判斷
vue還能夠把一個數組傳給 v-bind:class,以應用一個 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
複製代碼
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
複製代碼
而在react中,
不能使用class
,由於class是關鍵字,因此react使用className
代替,<div className={["activeClass",hasError? item.color :'' ].join('')} ></div>
複製代碼
<div className={`activeClass ${hasError?item.color:'' }`} ></div>
複製代碼
vue的v-bind:style 的對象語法十分直觀——看着很是像 CSS,但實際上是一個 JavaScript 對象。CSS 屬性名能夠用駝峯式 (camelCase) 或短橫線分隔 (kebab-case,記得用引號括起來) 來命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
複製代碼
data: {
activeColor: 'red',
fontSize: 30
}
複製代碼
固然你也能夠將全部樣式定義成一個對象變量,直接綁定該對象。
這個時候,看好知識點
,
{{}}
:
<div style={{display: this.state.isShow ? "block" : "none"}}></div>
複製代碼
多個樣式時,使用逗號 ,
分隔!
<div style={{display: this.state.isShow ? "block" : "none", color:"pink"}}></div>
複製代碼
建議你看到這,稍微停頓一下分辨區別。
vue就是v-if,v-else,v-show就很少說了,這個就比較簡單了:
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
複製代碼
react的條件判斷上面開始的時候介紹過了,一般使用三元表達式來判斷顯示,這裏補充一種將JSX 元素賦值給了不一樣變量,再使用三元表達式判斷的方式:
render () {
const display = true
const showDiv = <p>顯示</p>
const noneDiv = <p>隱藏</p>
return (
<div>{display ? showDiv : noneDiv }</div>
)
}
複製代碼
也沒有vue相似v-show這種比較節省dom切換性能消耗的語法,歡迎評論補充。
vue中咱們能夠用 v-for 指令基於一個數組來渲染一個列表。
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
複製代碼
至於具體裏面的那些爲何要加key啊,怎麼遍歷對象啊,還有怎麼v-for對數組的檢測問題就不深究了。
react中的循環在開始也提到過了,使用map()方法遍歷,不說了。
vue中表單元素的綁定都是使用v-model綁定,拿個input舉例:
<input v-model="message" placeholder="edit me">
複製代碼
react中以前也提了使用元素自己的屬性value來直接綁定
<input value={this.state.message} placeholder="edit me">
複製代碼
提過了,基本一個實例就是一個組件且vue中後面也出現了純函數組件,兩個框架組件概念都同樣。
vue和react的組件複用方式都是同樣的,先引用,而後使用組件名標籤。
vue中引用通常習慣使用-
間隔標籤,例如:
<div id="components-demo">
<button-counter></button-counter>
</div>
複製代碼
import DuttonCounter from './modules/DuttonCounter'
export default {
components: {
DuttonCounter
},
}
複製代碼
react中引用標籤直接使用export的class類名,且組件都必需要用大寫字母開頭
:
import DuttonCounter from './modules/DuttonCounter'
function App() {
return (
<div className="App"> <DuttonCounter /> </div>
);
}
複製代碼
來了啊,知識點,要考的!
父組件:
<super-man work="超人"></super-man>
複製代碼
子組件接收:
<div>{{work}}</div>
複製代碼
export default {
props: {
work: {
required: true,
type: String
}
},
}
複製代碼
而後,經過一個例子看一下react的props:
class Index extends Component {
render () {
return (
<div>
<SuperMan work='超人' />
</div>
)
}
}
複製代碼
這是組件傳值的方式,和vue大同小異,看一下接收組件傳值:
class SuperMan extends Component {
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
複製代碼
在react中,接收父組件的傳值一樣使用關鍵字props,與vue不一樣的區別在於:
- vue的全部傳值須要放置在子組件的props的對象中,才能夠被當前子組件使用,使用方式如同訪問data的變量,直接
this.變量
使用。 react是使用this.props.變量
這種方式就能夠直接使用全部父組件傳遞的變量。- react中向子組件傳遞對象參數,可使用
{{}}
雙括號直接傳遞對象
class Index extends Component {
render () {
return (
<div> <SuperMan work={{name: '超人', task: '拯救世界'}} /> </div> ) } } 複製代碼
3.甚至於傳遞函數:
class Index extends Component {
render () {
return (
<div> <SuperMan onClick={this.handleClick.bind(this)}/> </div> ) } } 複製代碼
而後,補充一些額外的知識點,
那麼react如何實現對props傳值的參數校驗呢: 首先假設父組件沒有傳值過來,咱們可能須要一個默認值,這時候你可能會常常在父組件傳值的時候使用三元表達式來判斷傳值,react只須要:
class SuperMan extends Component {
// 知識點
static defaultProps = {
work: '默認是超人'
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
複製代碼
react中定義defaultProps就對 props 中各個屬性的默認配置。這樣咱們就不須要判斷配置屬性是否傳進來了:若是沒有傳進來,能夠直接this.props使用 defaultProps 中的默認屬性。
其次,關於react中props的校驗,先聲明一句「react的類型檢查PropTypes自React v15.5起已棄用」,那我該怎麼繼續講下去?? 這個時候就須要單獨安裝prop-types,如今一般在react中咱們使用prop-types對組件的prop進行類型檢測。
npm install --save prop-types
複製代碼
使用方式也很簡單,有例子你就能理解:
// 知識點
import PropTypes from 'prop-types'
class SuperMan extends Component {
// 知識點
static propTypes = {
work: PropTypes.string
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
複製代碼
簡單明瞭,經過引入prop-types,而且添加一個類屬性 propTypes,在propTypes中爲你props傳入的變量指定數據類型,這樣就至關於vue的type:String
。
而後,咱們看一下required: true
在react中如何實現:
import PropTypes from 'prop-types'
class SuperMan extends Component {
static propTypes = {
work: PropTypes.string.isRequired // 知識點
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
複製代碼
仔細看和上面一個例子的區別在於,當咱們指定數據類型PropTypes.string
以後,同時後面經過 isRequired 關鍵字來強制組件某個參數必須傳入。
最後,如何react實現組件傳參校驗呢? 這個我還沒用到,可是呢專門爲大家查了一下,和我猜的差很少,直接使用函數:
import PropTypes from 'prop-types'
class SuperMan extends Component {
static propTypes = {
work: function(props,propName,componentName){
} // 知識點
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
複製代碼
使用自定義函數,默認傳參分別爲props,propName,componentName,而後校驗錯誤就return new Error('錯誤信息');
就能夠了。
再放上一些看到的其餘的例子,雖然我也還沒用到,可是是很好的小技巧:
//指定枚舉類型:你能夠把屬性限制在某些特定值以內
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 指定多個類型:你也能夠把屬性類型限制在某些指定的類型範圍內
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 指定某個類型的數組
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 指定類型爲對象,且對象屬性值是特定的類型
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
複製代碼
react和vue有着一樣的組件特徵——單向數據流。 在react中想要改變父組件中的值就要用方法去觸發父組件的方法,由父組件本身的方法操做屬於父組件的值。
那麼如何實現觸發父組件或者說父組件監聽子組件變化而變化,在vue中是經過$emit
方法來觸發父組件方法,例子不舉了,直接來看react如何實現:
// 父組件
class Index extends Component {
this.state = {type:'小超人'}
changeType(data) {
this.setState({
type: data //把父組件中type的替換爲子組件傳遞的值
})
}
render () {
return (
<div> <SuperMan work='超人' super={this.fn.changeType(this)}/> // 知識點 </div> ) } } 複製代碼
// 子組件
class SuperMan extends Component {
constructor () {
super()
this.state = {childText:'大超人'}
}
clickFun(text) {
this.props.super(text)//這個地方把值傳遞給了props的事件當中
}
render () {
return (
<div onClick={this.clickFun.bind(this, this.state.childText)}> {this.props.work} </div>
)
}
}
複製代碼
沒啥問題,看的懂ok,只是沒有使用$emit
,而是直接經過this.props
調用父組件的方法便可。
插槽部分簡單說下使用,首先咱們如何在vue中使用一個插槽:
// 父組件
<my-component>
<p>超人</p>
</my-component>
// 子組件
<div class="child-page">
<h1>無限地球危機</h1>
<slot></slot>
</div>
// 渲染結果
<div class="child-page">
<h1>無限地球危機</h1>
<p>超人</p>
</div>
複製代碼
插槽那一堆具體就不解釋了,咱們只看如何在react也實現像vue這種插槽方式:
// 父組件
ReactDOM.render(
<MyComponent> <p>超人</p> </MyComponent>,
document.getElementById('root')
)
class MyComponent extends Component {
render () {
return (
<div class="child-page"> <h1>無限地球危機</h1> {this.props.children} </div>
)
}
}
複製代碼
渲染結果和vue的渲染就是一致了,能夠看到react中將父組件中的JSX內容傳到子組件內部,經過 {this.props.children}
把JSX內容渲染到頁面指定結構上,仍是很好理解的。
截止到我寫完,已經斷斷續續一個周過去了,看了看右下角11386字數,項目上的代碼研究的也差很少,由於是開源項目因此大量都是高階組件,一些純函數組件組成,剛開始仍是很迷糊,可是如今已經差很少改掉一些功能了。
至此按照vue文檔中的結構(部分作了刪減),把對應的react的語法和功能作了一遍梳理,若是你能仔細把每一個例子對比看一遍,我以爲你寫不了react,也能夠看懂一個react項目了。
有補充的評論加個知識點,沒補充的捧個贊。
謝謝。