Vue和React對(duì)比

Vue也已經(jīng)升級(jí)到2.0版本了,到現(xiàn)在為止(2016/11/19)比較流行的MVVM框架有AngularJS(也有人認(rèn)為其為MVC)、ReactJSVueJS,這三個(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è),很容易記憶:

  1. v-bind:動(dòng)態(tài)地綁定一個(gè)或多個(gè) html屬性,或一個(gè)組件 prop 到表達(dá)式(組件單項(xiàng)數(shù)據(jù)流使用)。
  2. v-once:綁定只渲染元素和組件 一次 的特性或prop表達(dá)式。
  3. v-model:在表單控件或者組件上創(chuàng)建雙向綁定,代替value值
  4. v-text:更新元素的 textContent
  5. v-html:更新元素的 innerHTML注意:內(nèi)容按普通 HTML 插入 - 不會(huì)作為 Vue 模板進(jìn)行編譯
  6. v-on:綁定事件監(jiān)聽(tīng)器。
  7. v-if / v-else / v-show:條件渲染。
  8. v-for:列表渲染。
  9. 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:classv-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>

在父組件中添加了keyauthordate屬性來(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 的用法

組件的聲明周期

react 組件的聲明周期

上面這張圖已經(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í)吧。

Vue 中的組件和數(shù)據(jù)流

狀態(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è)核心概念:

  1. State: Vuex 使用 單一狀態(tài)樹(shù) —— 是的,用一個(gè)對(duì)象就包含了全部的應(yīng)用層級(jí)狀態(tài)。至此它便作為一個(gè)『唯一數(shù)據(jù)源(SSOT)』而存在。這也意味著,每個(gè)應(yīng)用將僅僅包含一個(gè) store 實(shí)例。
  2. Getters: 從state中獲取狀態(tài)值
  3. 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ù)
  4. 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ì)象屬性里面的actionsgetters屬性數(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)而已。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,263評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,946評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,708評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,409評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,774評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,641評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,872評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,650評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容