Vue也已經(jīng)升級(jí)到2.0版本了,到現(xiàn)在為止(2016/11/19)比較流行的MVVM框架有AngularJS(也有人認(rèn)為其為MVC)、ReactJS和VueJS,這三個(gè)框架中,以我現(xiàn)在的情況來(lái)說(shuō)(AngularJS2還沒(méi)有去接觸),ReactJS和VueJS的對(duì)比應(yīng)該是比較適合的,感覺(jué)這哥倆就是好基友,不管是單向數(shù)據(jù)流、組件化思想、還是構(gòu)建大型應(yīng)用的路由和狀態(tài)管理都有些許相似之處。而AngularJS與Jquery對(duì)比我個(gè)人覺(jué)著比較合適。
為什么現(xiàn)在MVVM框架這么火呢?JQuery挺好用的呀,為什么要去代替它?...
可能會(huì)產(chǎn)生這樣的疑問(wèn),在我看來(lái),MVVM框架的流行是因?yàn)殡S著前端技術(shù)的發(fā)展對(duì)于要求越來(lái)越高和前端面對(duì)的場(chǎng)景越來(lái)越復(fù)雜導(dǎo)致了現(xiàn)實(shí)對(duì)于前端性能的要求也越來(lái)越高,這樣像JQuery那樣頻繁的操作DOM節(jié)點(diǎn)的方式顯然就不太合適了。所以MVVM開(kāi)始逐漸流行開(kāi)來(lái),另外我認(rèn)為JQuery目前來(lái)看還是不會(huì)被代替的,因?yàn)閷?duì)于一些對(duì)性能要求不是很高的前端項(xiàng)目,是用JQuery來(lái)開(kāi)發(fā)還是非常爽的。
廢話有點(diǎn)多了,進(jìn)入正題。接下來(lái)從數(shù)據(jù)雙向綁定、組件及數(shù)據(jù)流、路由、狀態(tài)管理等方面來(lái)分別對(duì)比一下怎樣去使用Vue和React。
數(shù)據(jù)雙向綁定
我理解的數(shù)據(jù)雙向綁定是,MVVM框架中的View層和Model層的數(shù)據(jù)相互影響。那么,那些行為會(huì)引起數(shù)據(jù)變動(dòng)呢?
首先,View層(即頁(yè)面上)的表單操作、觸發(fā)事件可能會(huì)引起數(shù)據(jù)變動(dòng);ajax請(qǐng)求也可能會(huì)引起數(shù)據(jù)變動(dòng),這個(gè)變動(dòng)我認(rèn)為更多在Model層;還有一種情況就是,某一數(shù)據(jù)變動(dòng)引起另外關(guān)聯(lián)數(shù)據(jù)的改變。
不管是哪一種數(shù)據(jù)改變,都會(huì)導(dǎo)致View層和Model層的變化,View層變動(dòng)引起頁(yè)面變化,Model層變動(dòng)保存數(shù)據(jù)。
Vue的數(shù)據(jù)雙向綁定
在Vue中,View層中與數(shù)據(jù)綁定有關(guān)的有插值表達(dá)式、指令系統(tǒng)、Class和Style、事件處理器和表單控件,ajax請(qǐng)求和計(jì)算屬性*也和數(shù)據(jù)變化有關(guān),下面我們分別說(shuō)一下這些知識(shí)點(diǎn)設(shè)計(jì)的一些數(shù)據(jù)綁定問(wèn)題。
插值表達(dá)式
在Vue中,插值表達(dá)式和指令對(duì)于數(shù)據(jù)的操作又稱(chēng)為模板語(yǔ)法。
Vue.js 使用了基于 HTML 的模版語(yǔ)法,允許開(kāi)發(fā)者聲明式地將 DOM 綁定至底層 Vue 實(shí)例的數(shù)據(jù)。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循規(guī)范的瀏覽器和 HTML 解析器解析。
在底層的實(shí)現(xiàn)上, Vue 將模板編譯成虛擬 DOM 渲染函數(shù)。結(jié)合響應(yīng)系統(tǒng),在應(yīng)用狀態(tài)改變時(shí), Vue 能夠智能地計(jì)算出重新渲染組件的最小代價(jià)并應(yīng)用到 DOM 操作上。
關(guān)于插值表達(dá)式的使用,在Vue官網(wǎng)模板語(yǔ)法的插值部分有詳細(xì)的使用說(shuō)明,不在贅述,需要注意的是,過(guò)濾器在插值中的使用有時(shí)可以起到意想不到的效果。
指令
Vue重的指令估計(jì)是從Angular那里學(xué)來(lái)的,有很多相似的地方,但是也不完全相同。
Vue中的指令我覺(jué)著非常的簡(jiǎn)單,并且就12個(gè),很容易記憶:
- v-bind:動(dòng)態(tài)地綁定一個(gè)或多個(gè) html屬性,或一個(gè)組件 prop 到表達(dá)式(組件單項(xiàng)數(shù)據(jù)流使用)。
- v-once:綁定只渲染元素和組件 一次 的特性或prop表達(dá)式。
- v-model:在表單控件或者組件上創(chuàng)建雙向綁定,代替value值。
- v-text:更新元素的 textContent。
- v-html:更新元素的 innerHTML。注意:內(nèi)容按普通 HTML 插入 - 不會(huì)作為 Vue 模板進(jìn)行編譯 。
- v-on:綁定事件監(jiān)聽(tīng)器。
- v-if / v-else / v-show:條件渲染。
- v-for:列表渲染。
-
v-pre:跳過(guò)這個(gè)元素和它的子元素的編譯過(guò)程。
10 v-cloak:這個(gè)指令保持在元素上直到關(guān)聯(lián)實(shí)例結(jié)束編譯。和 CSS 規(guī)則如[v-cloak] { display: none }
一起用時(shí),這個(gè)指令可以隱藏未編譯的 Mustache 標(biāo)簽直到實(shí)例準(zhǔn)備完畢。
大概列舉一下,詳細(xì)使用請(qǐng)參考Vue API 指令和Vue 指南的Class與Style綁定、條件渲染、列表渲染、事件處理器、表單控件綁定部分內(nèi)容。
Class與Style綁定
Vue為了方便操作控制元素的樣式,專(zhuān)門(mén)增強(qiáng)了v-bind:class和v-bind:style,通過(guò)增強(qiáng)的這兩個(gè)指令可以實(shí)用對(duì)象語(yǔ)法或者數(shù)組語(yǔ)法對(duì)元素的樣式進(jìn)行變動(dòng),這也不是本文重點(diǎn),Vue官方Class與Style綁定已經(jīng)說(shuō)得很詳細(xì)了。
條件渲染和列表渲染
條件渲染和列表渲染在Vue模板中動(dòng)態(tài)創(chuàng)建模板很不錯(cuò),讓我里面想到了JSP中的EL表達(dá)式和Struts中的JSTL(后端模板語(yǔ)言中基本都有),這就可以方便的根據(jù)后端傳過(guò)來(lái)的數(shù)據(jù)進(jìn)行模板創(chuàng)建了。你懂得,詳細(xì)語(yǔ)法和使用還是參考Vue文檔列表渲染和條件渲染,本篇主題是對(duì)比,并不是講解基礎(chǔ)語(yǔ)法,Vue的官方文檔我覺(jué)著非常給力,簡(jiǎn)單明了。
事件處理器
在Vue中我們可以通過(guò)v-on來(lái)給元素注冊(cè)事件,完成事件注冊(cè),Vue中的事件處理和平時(shí)使用的事件處理不同之處就是,既可以綁定數(shù)據(jù)處理函數(shù),也可以使用內(nèi)聯(lián)處理器。并且,Vue還講常用的事件方法,如preventDefault()等通過(guò)修飾符的方式來(lái)方便使用。
表單控件
你可以用v-model指令在表單控件元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會(huì)根據(jù)控件類(lèi)型自動(dòng)選取正確的方法來(lái)更新元素。
Vue中對(duì)于表單控件提供的v-model*指令非常的使用,可以方便的返回或者設(shè)置表單控件的信息。
計(jì)算屬性
在Vue中引入了計(jì)算屬性來(lái)處理模板中放入太多的邏輯會(huì)讓模板過(guò)重且難以維護(hù)的問(wèn)題,這樣不但解決了上面的問(wèn)題,而且也同時(shí)讓模板和業(yè)務(wù)邏輯更好的分離。
Vue計(jì)算屬性
ajax數(shù)據(jù)請(qǐng)求
在Vue1.x的版本中,官方推薦的ajax數(shù)據(jù)請(qǐng)求庫(kù)是vue-resource,但是在Vue2.0的版本中,不再推薦使用,該推薦使用axios。
其實(shí)這些ajax數(shù)據(jù)請(qǐng)求的使用都大差不差,隨你選擇,并且vue-resource還是繼續(xù)支持使用的,在Vue2.0中。
以上八個(gè)方面,我個(gè)人認(rèn)為都是和數(shù)據(jù)綁定相關(guān)的一些Vue基本項(xiàng),只是簡(jiǎn)單列舉,具體內(nèi)容請(qǐng)查看Vue文檔或者API。
為什么這么多呢?因?yàn)閂ue中有個(gè)模板的概念,所以,數(shù)據(jù)和模板進(jìn)行數(shù)據(jù)綁定需要分別來(lái)做,而在接下來(lái)的React中,你會(huì)發(fā)現(xiàn),React的數(shù)據(jù)綁定雖然也是這八項(xiàng),但是,不會(huì)展開(kāi)八項(xiàng)來(lái)說(shuō)明。
React中的數(shù)據(jù)雙向綁定
在上面的Vue中,我們已經(jīng)提及了有八個(gè)方面可以影響到數(shù)據(jù)的改變,但是React中就沒(méi)有必要展開(kāi)了說(shuō)明了,因?yàn)樵赗eact中沒(méi)有Vue中模板的概念,因?yàn)槿思矣幸粋€(gè)JSX語(yǔ)法呀,可以將HTML和JS還有CSS混合寫(xiě)在一起呀,,這樣寫(xiě)成的組件感覺(jué)還不錯(cuò),組件化也是ReactJS的重要特點(diǎn)之一。
React中通過(guò)將state(Model層) 與View層數(shù)據(jù)進(jìn)行雙向綁定達(dá)到數(shù)據(jù)的實(shí)時(shí)更新變化,具體來(lái)說(shuō)就是在View層直接寫(xiě)JS代碼將Model層中的數(shù)據(jù)拿過(guò)來(lái)渲染,一旦像表單操作、觸發(fā)事件、ajax請(qǐng)求等觸發(fā)數(shù)據(jù)變化,則進(jìn)行雙向同步。
所以說(shuō)React的特點(diǎn)是組件化,也就是說(shuō),接下來(lái)的小節(jié)才是React的重點(diǎn)部分。
組件及數(shù)據(jù)流
前端發(fā)展到現(xiàn)在,為了提高開(kāi)發(fā)效率,組件化已經(jīng)成為一個(gè)必然的趨勢(shì)。而MVVM框架中,如果沒(méi)有組件化的思想,它都不敢說(shuō)拿出來(lái)宣傳(純屬個(gè)人意淫,哈哈)。下面我們?cè)俜謩e簡(jiǎn)單介紹一下VueJS和ReactJS中組件思想和組件之間的數(shù)據(jù)流。
React中的組件和數(shù)據(jù)流
上一節(jié)中提到過(guò),React中的組件化是其重要的特點(diǎn)之一,因?yàn)樵贏ngular1.x的出現(xiàn),并沒(méi)有明確提出組件的思想,只是有一個(gè)類(lèi)似的指令思想來(lái)實(shí)現(xiàn)組件化的方式。所以,當(dāng)React中明確提出組件思想后,前端好像重生了(吹的有點(diǎn)大了)。
創(chuàng)建組件
React中實(shí)現(xiàn)組件有兩種方式,一種是createClass方法,另一種是通過(guò)ES2015的思想類(lèi)繼承React.Component來(lái)實(shí)現(xiàn)。
createClass方式實(shí)現(xiàn)
import React from 'react';
export default React.createClass({
render() {
return (
<div>hello5</div>
)
}
})
這樣,一個(gè)組件就創(chuàng)建完成,并且通過(guò)ES2015的模塊化思想將其暴露出去了,其他組件就可以引入并使用了。如下:
import React from 'react';
import ReactDom from 'react-dom';
import Hello from './hello.jsx';
ReactDom.render(
<Hello/>,
document.getElementById('app');
);
OK,這樣就使用簡(jiǎn)單使用了一個(gè)組件。
類(lèi)繼承React.Component來(lái)實(shí)現(xiàn)
import React from 'react';
class CommentItem extends React.Component {
render() {
return (
<div className="comment">
<span>{ this.props.author }</span>
<span>{ this.props.date }</span>
<div>{ this.props.children }</div>
</div>
)
}
}
export { CommentItem as default };
需要注意的是,這樣創(chuàng)建組件的時(shí)候,組件名稱(chēng)首字母必須大寫(xiě)(如:CommentItem)。同樣,我們使用一下這個(gè)組件。
import React from 'react';
import CommentItem from './comment-item';
class CommentList extends React.Component {
render() {
let CommentNodes = this.props.data.map((comment, index) => {
return (
<CommentItem key={index} author={comment.author} date={comment.date}>
{comment.content}
</CommentItem>
)
});
return (
<div>
{ CommentNodes }
</div>
)
}
}
export { CommentList as default };
這樣我們又創(chuàng)建了一個(gè)組件,并且在這個(gè)組件中我們使用了上面創(chuàng)建的那個(gè)組件。
組件之間的數(shù)據(jù)流
在上面類(lèi)繼承React.Component來(lái)實(shí)現(xiàn)一節(jié)中,我們可以看出例子中出現(xiàn)了組件嵌套的情況,仔細(xì)想想,組件之間傳遞信息肯定是必然的。那么React是怎樣進(jìn)行組件之間的數(shù)據(jù)通信的呢?
回答這個(gè)問(wèn)題之前,我們需要考慮一下,組件之間有幾種數(shù)據(jù)通信。首先,第一種比較容易想到,那就是父子組件之間的數(shù)據(jù)通信。第二種也就自然而然的出來(lái)了----非父子組件之間的數(shù)據(jù)通信。
父子組件數(shù)據(jù)通信
父子組件之間的數(shù)據(jù)通信細(xì)分其實(shí)還有兩種:父與子之間和子與父之間。
在React中,父與子之間的數(shù)據(jù)通信是通過(guò)props屬性就行傳遞的;
而子與父之間的數(shù)據(jù)通信可以通過(guò)父組件定義事件,子組件觸發(fā)父組件中的事件時(shí),通過(guò)實(shí)參的形式來(lái)改變父組件中的數(shù)據(jù)來(lái)通信;
下面我們來(lái)分別通過(guò)例子來(lái)再現(xiàn)一下這種場(chǎng)景:
父組件:
import React from 'react';
import CommentItem from './comment-item';
class CommentList extends React.Component {
render() {
let CommentNodes = this.props.data.map((comment, index) => {
return (
<CommentItem key={index} author={comment.author} date={comment.date}>
{comment.content}
</CommentItem>
)
});
return (
<div>
{ CommentNodes }
</div>
)
}
}
export { CommentList as default };
子組件:
import React from 'react';
class CommentItem extends React.Component {
render() {
return (
<div className="comment">
<span>{ this.props.author }</span>
<span>{ this.props.date }</span>
<div>{ this.props.children }</div>
</div>
)
}
}
export { CommentItem as default };
通過(guò)上面我們可以看出,子組件CommentItem需要父組件傳過(guò)來(lái)的值進(jìn)行展示,而父組件是這樣的:
<CommentItem key={index} author={comment.author} date={comment.date}> {comment.content} </CommentItem>
在父組件中添加了key
、author
、date
屬性來(lái)向子組件傳值。想對(duì)象的,子組件通過(guò)props對(duì)象來(lái)獲取父組件傳過(guò)來(lái)的值,如下:
<span>{ this.props.author }</span>
<span>{ this.props.date }</span>
<div>{ this.props.children }</div>
好的,我們?cè)賮?lái)看一下另一種子與父之間的通信。
父組件:
import React from 'react';
import CommentList from './comment-list';
import CommentForm from './comment-form';
class CommentBox extends React.Component {
constructor(props) {
super(props);
this.state = {data: []};
}
handleCommentSubmit(comment) {
let comments = this.state.data;
comments.push(comment);
this.setState({data: comments});
}
render() {
return (
<div className="m-index">
<div>
<h1>評(píng)論</h1>
</div>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)} />
</div>
)
}
}
export { CommentBox as default };
子組件:
import React from 'react';
class CommentForm extends React.Component {
handleClick(){
let author = this.refs.author.value,
content = this.refs.content.value;
this.props.onCommentSubmit({author, content, date:new Date().getTime()});
this.refs.author.value = "";
this.refs.content.value = "";
}
render() {
return (
<div className="yo-list yo-list-a">
<label className="item item-input">
<input type="text" className="yo-input flex" ref="author" placeholder="發(fā)布人" />
</label>
<label className="item item-input">
<textarea className="yo-input flex" ref="content" placeholder="留言?xún)?nèi)容"></textarea>
</label>
<label>
<button onClick={this.handleClick.bind(this)} className="yo-btn yo-btn-l">發(fā)表評(píng)論</button>
</label>
</div>
)
}
}
export { CommentForm as default };
簡(jiǎn)單解釋一下,子組件是一個(gè)表單組件,父組件中引用了該表單子組件,然后子組件中點(diǎn)擊button按鈕,觸發(fā)子組件中的處理函數(shù),處理函數(shù)通過(guò)refs獲取到表單輸入值然后調(diào)用父組件中傳過(guò)來(lái)的函數(shù),從而觸發(fā)父組件中的函數(shù)執(zhí)行改變data數(shù)據(jù),data數(shù)據(jù)變動(dòng)直接影響的是另一個(gè)組件CommentList的變化。
需要注意的是,在獲取表單控件內(nèi)的數(shù)據(jù)時(shí),我們利用了一個(gè)refs對(duì)象,該對(duì)象用于獲取真實(shí)DOM結(jié)構(gòu)。具體來(lái)說(shuō)就是,在React中組件并不是真實(shí)的 DOM 節(jié)點(diǎn),而是存在于內(nèi)存之中的一種數(shù)據(jù)結(jié)構(gòu),叫做虛擬 DOM (virtual DOM,這是React探索性的創(chuàng)新)。只有當(dāng)它插入文檔以后,才會(huì)變成真實(shí)的 DOM 。根據(jù) React 的設(shè)計(jì),所有的 DOM 變動(dòng),都先在虛擬 DOM 上發(fā)生,然后再將實(shí)際發(fā)生變動(dòng)的部分,反映在真實(shí) DOM上,這種算法叫做 DOM diff (詳細(xì)了解diff 算法),它可以極大提高網(wǎng)頁(yè)的性能表現(xiàn)。
在這里點(diǎn)擊button時(shí),input和textarea元素還是虛擬DOM,所以違法獲取到輸入的值,需要通過(guò)refs對(duì)象獲取一下。
非父子組件之間的通信
React中在處理非父子組件之間的通信時(shí),簡(jiǎn)單的,嵌套不深的非父子組件(如:兄弟組件)可以仍然使用上一節(jié)非父子組件之間通信中的事件函數(shù),傳形參的方式來(lái)實(shí)現(xiàn)。如子組件CommentList 和子組件CommentFrom之間的通信就是這樣實(shí)現(xiàn)的。
如果,需要通信的兩個(gè)非父子組件之間嵌套比較深,可以使用Flux和Redux來(lái)實(shí)現(xiàn)狀態(tài)管理,這里不做詳細(xì)闡述,下面會(huì)詳細(xì)對(duì)比vue的狀態(tài)管理進(jìn)行說(shuō)明。想先了解的可以看一下阮一峰老師的blog:
Flux 架構(gòu)入門(mén)教程
Redux 入門(mén)教程(一):基本用法
Redux 入門(mén)教程(二):中間件與異步操作
Redux 入門(mén)教程(三):React-Redux 的用法
組件的聲明周期
上面這張圖已經(jīng)很清楚的展示了react組件的聲明周期了,就不過(guò)多介紹了。這張圖摘自React組件生命周期小結(jié),對(duì)于理解React組件的聲明周期鉤子函數(shù)很有幫助。
Vue中的組件和數(shù)據(jù)流
Vue比React出來(lái)的要晚一些,自然順應(yīng)了前端組件化的大潮,并且個(gè)人覺(jué)得借鑒了部分React的思想來(lái)實(shí)現(xiàn)其組件化思想。
Vue默認(rèn)的是單向數(shù)據(jù)流,這是Vue直接提出來(lái)說(shuō)明的,父組件默認(rèn)可以向子組件傳遞數(shù)據(jù),但是子組件向父組件傳遞數(shù)據(jù)就需要額外設(shè)置了。
父子組件之間的數(shù)據(jù)通信是通過(guò)Prop和自定義事件實(shí)現(xiàn)的,而非父子組件可以使用訂閱/發(fā)布模式實(shí)現(xiàn)(類(lèi)似于Angualr中的非父子指令之間的通信),再?gòu)?fù)雜一點(diǎn)也是建議使用狀態(tài)管理(vuex)。
我一直覺(jué)得Vue的官方文檔是我看過(guò)最直接、易懂的技術(shù)文檔,所以就直接給大家貼一個(gè)中文鏈接,自己去跟隨尤雨溪學(xué)習(xí)吧。
狀態(tài)管理
上面對(duì)比React和Vue的組件及數(shù)據(jù)流的時(shí)候,都提到了當(dāng)非父子組件之間嵌套過(guò)深的時(shí)候都建議使用狀態(tài)管理來(lái)維護(hù)數(shù)據(jù)的變化,那么到底它們之間的狀態(tài)管理有什么區(qū)別呢?
Vue中的狀態(tài)管理--vuex
先放個(gè)官方中文鏈接,還是建議直接看官方文檔。然后在放一下小例子去體會(huì)一下。
先簡(jiǎn)單說(shuō)明一下,vuex狀態(tài)管理的幾個(gè)核心概念:
- State: Vuex 使用 單一狀態(tài)樹(shù) —— 是的,用一個(gè)對(duì)象就包含了全部的應(yīng)用層級(jí)狀態(tài)。至此它便作為一個(gè)『唯一數(shù)據(jù)源(SSOT)』而存在。這也意味著,每個(gè)應(yīng)用將僅僅包含一個(gè) store 實(shí)例。
- Getters: 從state中獲取狀態(tài)值
- Mutation: 更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutations 非常類(lèi)似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類(lèi)型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方,并且它會(huì)接受 state 作為第一個(gè)參數(shù)
- Action: 類(lèi)似于 mutation,不同在于:Action 提交的是 mutation,而不是直接變更狀態(tài);Action 可以包含任意異步操作。
例子來(lái)了:
store.js
import Vue from '../libs/vue.js';
import Vuex from '../libs/vuex.min.js';
Vue.use(Vuex);
const state = {
loginPrePath:['/']
};
const mutations ={
LOGINPREPATH(state,path){
state.loginPrePath.unshift(path);
},
LOGINPREPATHSHIFT(state){
state.loginPrePath.shift();
}
};
export default new Vuex.Store({
state,
mutations
});
actions.js:
export default {
loginPrePath: ({dispatch,state},path)=>{
console.log('actions loginPrePath:' +path);
dispatch('LOGINPREPATH',path);
},
loginPrePathShift: ({dispatch,state})=>{
console.log('delete....');
dispatch('LOGINPREPATHSHIFT');
}
}
getter.js:
export default {
loginPrePath: state => state.loginPrePath
};
login.vue:
<template>
...
</template>
<script>
import Vue from '../libs/vue.js';
import VueResource from '../libs/vue-resource.js';
import Vuex from '../vuex/actions.js';
import VuexGet from '../vuex/getters.js';
Vue.use(VueResource);
export default {
data(){
return {
username: '',
password: '',
loginBtn: 0
}
},
vuex: {
actions: {
setLoginPrePath: Vuex.loginPrePath
},
getters:{
getLoginPrePath: VuexGet.loginPrePath
}
},
methods: {
forget(){
//使用vuex,修改狀態(tài)值
this.setLoginPrePath({path:this.$route.path,title:'忘記密碼'});
this.$router.go({path:'/index2/forget.json'});
},
submit(){
if(this.loginBtn === 3){
if(this.checked){
this.$http.post('/zhixiao/password.json',{password:this.password}).then(
(res)=>{
if(res.ok){
console.log("注冊(cè)成功,正在跳轉(zhuǎn)登錄頁(yè)面");
setTimeout(()=>{
//獲取狀態(tài)值,通過(guò)getter
var path = this.getLoginPrePath[0].path;
this.loginPrePathShift();
this.$router.go(path);
},1500);
}
},(res)=>{
console.log('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)稍后重試');
}
)
}else{
console.log('請(qǐng)選擇同意用戶(hù)協(xié)議');
}
}else{
console.log('請(qǐng)?zhí)顚?xiě)驗(yàn)證碼');
}
}
}
}
</script>
上面的例子并無(wú)實(shí)際效果,是我從以前項(xiàng)目中拼出來(lái)的(vuex1.0),只是為了說(shuō)明loginPrePath這個(gè)狀態(tài)值在vuex中的使用方式。詳細(xì)請(qǐng)看Vue官方文檔。
React中的狀態(tài)管理--Flux
React中官方提供的狀態(tài)管理是Flux,但是貌似現(xiàn)在第三方開(kāi)發(fā)的Redux更強(qiáng)大,但是相比較使用的難度和學(xué)習(xí)曲線就有點(diǎn)陡峭了。
個(gè)人感覺(jué)Flux和Vue中的vuex思想基本相同,因?yàn)閂uex就是借鑒的Flux。
所以說(shuō),現(xiàn)在再來(lái)說(shuō)Flux就簡(jiǎn)單了。
回想一下,在vuex中如果我們想修改一個(gè)狀態(tài)值,我們應(yīng)該怎么辦呢?
在組件中配置vuex對(duì)象屬性里面的actions和getters屬性數(shù)組,然后配置的實(shí)際上是調(diào)用了Actions中的方法,Actions作用是將修改操作派生給store中的mutations,mutations真正處理業(yè)務(wù)邏輯并且修改狀態(tài)值。
其實(shí)Flux也是如此,只不過(guò)在vuex中的Actions層執(zhí)行了一個(gè)dispatcher方法將狀態(tài)操作轉(zhuǎn)發(fā)給了mutations,在Flux中直接需要顯示的配置一層Dispatcher層進(jìn)行轉(zhuǎn)發(fā)。并且實(shí)現(xiàn)方式有所不同,vuex中mutations中隱藏了一些事件觸發(fā)的操作,而Flux中直接通過(guò)我們自己編寫(xiě)代碼實(shí)現(xiàn),畢竟Flux是年紀(jì)大了,不如小弟vuex年輕呀。
例子:
components.js
import React from 'react';
import MyButton from './MyButton.jsx';
import ButtonActions from '../actions/ButtonActions.js';
import ListStore from '../stores/ListStore.js';
export default React.createClass({
getInitialState() {
return {
items: ListStore.getAll()
}
},
createNewItem() {
ButtonActions.addNewItem('New Item');
},
componentDidMount() {
ListStore.addChangeListener(this._onChange);
},
componentWillUnmount() {
ListStore.removeChangeListener(this._onChange);
},
_onChange() {
this.setState({
items: ListStore.getAll()
})
},
render() {
return (
<MyButton onClick={this.createNewItem} items={this.state.items} />
)
}
});
ButtonActions.js:
import AppDispatcher from '../dispatchers/AppDispatcher.js';
export default {
addNewItem(text) {
AppDispatcher.dispatch({
actionType: 'ADD_NEW_ITEM',
text: text
})
}
}
AppDispatcher.js':
import { Dispatcher } from 'flux';
import ListStore from '../stores/ListStore.js';
let AppDispatcher = new Dispatcher();
AppDispatcher.register(action => {
switch( action.actionType ) {
case 'ADD_NEW_ITEM':
ListStore.addNewItemHandle(action.text);
ListStore.emitChange();
break;
}
});
export default AppDispatcher;
**ListStore.js: **
import { EventEmitter } from 'events';
export default Object.assign({}, EventEmitter.prototype, {
items: [],
getAll(){
return this.items;
},
addNewItemHandler(text) {
this.items.push(text);
},
emitChange() {
this.emit('change');
},
addChangeListener(callback) {
this.on('change', callback);
},
removeChangeListener(callback) {
this.removeListener('change', callback);
}
});
仔細(xì)按照例子走一遍工作流程,相信你就理解Flux實(shí)現(xiàn)狀態(tài)管理的思想了。
路由
要想實(shí)現(xiàn)SPA,路由是個(gè)不可避免的話題,作為主流的MVVM框架,怎么可能沒(méi)有官方路由呢,兩者的路由也很相似,都是利用各自的組件實(shí)現(xiàn)思想來(lái)實(shí)現(xiàn)的。
Vue中的路由
還是先貼官方鏈接,簡(jiǎn)單易懂。
再給個(gè)例子(vue-router1.0),仔細(xì)看一下:
app.js
//引用component
import index from './components/index.vue';
import main from './components/main.vue';
import my from './components/my.vue';
//APP route
import Vue from './libs/vue.js';
import VueRouter from './libs/vue-router.js';
Vue.use(VueRouter);
router.map({
'/':{
component: index,
subRoutes:{
'/':{
component: main
},
'/my':{
name:'my',
component: my
},
'/results/:key':{
name:'results',
component:results
}
}
}
});
//啟動(dòng)router
router.start(App,'body');
index.vue
<template>
<div class="box">
<div class="index-container">
<router-view>
</router-view>
</div>
<footer id="footer">
<ul>
<li v-bind:class="getIndex == $index ? 'active' : ''"
v-for="data in navList"
v-on:click="changePage($index)"
v-link="{path:data.path,exact: true}">
<i class="iconfont">{{{data.icon}}}</i>
<p>{{{data.name}}}</p>
</li>
</ul>
</footer>
</div>
</template>
<script>
var Vue = require('../libs/vue.js');
var VueResource = require('../libs/vue-resource.js');
import {getIndex} from '../vuex/getters.js';
import {changeIndexPage} from '../vuex/actions.js';
Vue.use(VueResource);
export default {
vuex: {
actions:{
changeIndexPage
},
getters:{
getIndex
}
},
data(){
return {
cur: 0,
navList:[
{path:'/',icon:'',name:'主頁(yè)'},
{path:'/lee',icon:'',name:'排行榜'},
{path:'/search',icon:'',name:'發(fā)現(xiàn)'},
{path:'/my',icon:'',name:'我的'}
]
}
},
methods:{
changePage:function(i){
this.changeIndexPage(i);
this.cur = this.getIndex;
}
}
}
</script>
大概就這樣,感覺(jué)像是配置的感覺(jué),其實(shí)這就是利用的vue中組件思想來(lái)實(shí)現(xiàn)的,詳細(xì)看官方文檔。
React中的路由
React中的路由只需要安裝插件react-router即可。
再來(lái)看例子:
app.jsx
'use strict';
import '../styles/usage/page/app.scss';
import React from 'react';
import ReactDOM from 'react-dom';
// import router
import { Router, Route, hashHistory, IndexRoute, Redirect } from 'react-router';
// router components
import App from './components/router/router-app.jsx';
import TV from './components/router/router-tv.jsx';
import Shows from './components/router/router-show.jsx';
import ShowIndex from './components/router/router-show-index.jsx';
let app = document.getElementById('app');
let handleEnter = () => {
console.log('entered');
}
let handleLeave = () => {
console.log('leaved');
}
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="tv" component={TV}>
<IndexRoute component={ShowIndex}></IndexRoute>
<Route path="/shows/:id" onEnter={handleEnter} onLeave={handleLeave} component={Shows} />
<Redirect from="shows/:id" to="/shows/:id" />
</Route>
</Route>
</Router>
), app);
router-app.jsx:
'ure strict';
import React from 'react';
// import router
import { Link } from 'react-router';
export default React.createClass({
render() {
return (
<div>
<div>
<Link to="/">首頁(yè)</Link> |
<Link to="/tv">電視劇</Link>
</div>
{this.props.children}
</div>
)
}
});
router-tv.jsx
'use strict';
import React from 'react';
export default React.createClass({
render() {
return (
<div>
{this.props.children}
</div>
)
}
});
router-show.jsx
'use strict';
import React from 'react';
export default React.createClass({
render() {
return (
<h1>
節(jié)目?jī)?nèi)容 {this.props.params.id}
</h1>
)
}
});
router-show-index.jsx
'use strict';
import React from 'react';
import { Link } from 'react-router';
export default React.createClass({
render() {
return (
<div>
<Link to="tv/shows/2">電視節(jié)目列表</Link>
</div>
)
}
});
例子很簡(jiǎn)單,但是將常用的路由操作基本都涵蓋了。
總結(jié)
大概通過(guò)自己的理解,對(duì)比了一下Vue和React的一些主要概念和實(shí)現(xiàn)方式,主要是為了加深自己理解,有些東西自己水平有限,不好表述,大概就是堆砌一些知識(shí)點(diǎn)而已。