淺談MVC,MVP,MVVM漸進變化及React與Vue比較

前言

由於沒有明確的界定,這裏不討論正確與否,只表達我的對前端MV*架構模式理解見解,再比較React和Vue兩種框架不一樣.
寫完以後我知道這文章好水,特別是框架對比部分都是別人說爛的,而我也是打算把這做爲長期文章來寫,慢慢梳理深刻,每次有新的理解就更新文章,我挺期待以後到了超過字數限制不得不寫成系列文章的那一天.css

2018/04/28 新增聲明式渲染 && 生命週期對比 && 狀態(State) OR 屬性(Data) && Props組件通訊
2018/05/02 新增狀態管理
2018/06/07 新增mobx狀態管理
2018/11/06 補充更新機制和狀態管理對比前端

MVC

MVC全名是Model View Controller,把應用程序分紅三部分分別是:vue

  • Model(業務模型): 用於管理應用程序數據處理邏輯的部分,經過觀察者模式(Pub&Sub / Events)發送消息給View;
  • View(視圖界面): 用於處理數據顯示的部分,註冊並接收Model的數據更新視圖,一般視圖是依據模型數據建立的;
  • Controller(控制器): 用於鏈接模型和視圖控制應用程序的流程(事件綁定等),一般控制器負責響應View的事件(路由,鍵盤,鼠標等),調用Model的接口進行操做;

圖片描述
(這些簡單的東西我就懶得特地畫圖了,直接百度圖片找張清晰的拿來用的..)
(更多內容請自行查閱,本節到此爲止了.)react

流程

  1. 當用戶在視圖界面中發生交互事件,View捕獲到這個操做會把處理的權利交移給Controller;
  2. Controller會對來自View數據進行預處理並決定調用Model的相關暴露接口;
  3. Model執行相關的業務邏輯更改數據以後會通知有關的View從新渲染;
  4. View收到通知後從Model請求最新的數據,而後從新渲染相關視圖界面;

還有一種狀況: MVC容許在不改變視圖外觀的狀況下改變視圖對用戶輸入的響應方式,只要用不一樣種類的controller實例替換便可。例如改變URL觸發hashChange事件,用戶不通過View直接到達Controller最後再影響回View.vuex

優勢:

  • 耦合性低,MVC 分層有助於管理複雜的應用程序,同時也讓應用程序的測試更加容易;
  • 重用性高,多個視圖能共享一個模型,能夠作到多視圖同時更新;
  • 生命週期成本低,MVC使開發和維護用戶接口的技術含量下降;
  • 部署快,只須要部署對應部分代碼而不是完整項目;
  • 可維護性高,分離視圖層和業務邏輯層也使得應用更易於維護和修改;
  • 有利軟件工程化管理,可使用控制器來聯接不一樣的模型和視圖去完成用戶的需求;

缺點:

  • 沒有明確的定義,徹底理解MVC並非很容易,現存就有不少對MVC不一樣解讀實現的方式;
  • 不適合小型,中等規模的應用程序,花費大量時間將MVC應用到規模並非很大的應用程序一般會得不償失;
  • 增長系統結構和實現的複雜性,對於簡單的界面,會增長結構的複雜性,並可能產生過多的更新操做,下降運行效率;
  • 視圖與控制器間的過於緊密的鏈接,視圖沒有控制器的存在,其應用是頗有限的,反之亦然,致使測試困難(依據模型數據建立部分);
  • 視圖對模型數據的低效率訪問,依據模型操做接口的不一樣,視圖可能須要屢次調用才能得到足夠的顯示數據;
  • 觀察者模式因爲事件觸發的隱式行爲可能致使很難查找問題的來源並影響其解決;

MVP

MVP全名是Model-View-Presenter,從經典的模式MVC演變而來,分兩種狀況:編程

Passive View(被動視圖)

Presenter佔據絕對主導地位,掌控著Model和View,然後二者之間互不聯繫.redux

  • Model(業務模型): 用於管理應用程序數據處理邏輯的部分,經過觀察者模式(Pub&Sub / Events)發送消息給Presenter;
  • View(視圖界面): 用於處理數據顯示的部分,傳遞事件和提供相關接口給Presenter;
  • Presenter(派發器): 做爲中間層同步控制著Model數據修改和View視圖變化;

圖片描述
(這些簡單的東西我就懶得特地畫圖了,直接百度圖片找張清晰的拿來用的..)
(更多內容請自行查閱,本節到此爲止了.)segmentfault

流程:

  1. 當用戶在視圖界面中發生交互事件,View捕獲到這個操做會把處理的權利交移給Presenter進行處理;
  2. Presenter須要時候能夠獲取Model其中的數據,並對Model進行操做更新;
  3. Model數據變化以後會通知Presenter;
  4. Presenter收到通知後會執行View提供的相關接口從新渲染相關視圖界面;

MVC和MVP(Passive View)區別:

  • 後者View和Model徹底解耦,它們之間的通訊是經過Presenter (MVC中的Controller)來進行的,全部的交互都發生在Presenter內部;
  • 前者Controller只能經過Model間接觸發View自行更新視圖,後者View再也不負責更新視圖,而是提供接口給Presenter執行;

Supervising Controller(監督控制器)

Presenter依舊佔據主導地位,可是會把一部分簡單的視圖邏輯(如雙向綁定)交還給View和Model進行處理,自身負責其餘複雜的視圖邏輯.數組

  • Model(業務模型): 用於管理應用程序數據處理邏輯的部分,經過觀察者模式(Pub&Sub / Events)發送消息給Presenter或者View;
  • View(視圖界面): 用於處理數據顯示的部分和接管部分簡單的視圖邏輯,同步簡單的視圖和模型的狀態,傳遞事件和提供相關接口給Presenter;
  • Presenter(派發器): 做爲中間層同步控制著Model數據修改和View視圖變化;

MVC和MVP(Supervising Controller)區別:
1, 視圖支持Presenter和View兩種途徑更新;瀏覽器

優勢:

1, 模型與視圖高度分離,咱們能夠修改視圖而不影響模型;
2, 能夠更高效地使用模型,由於全部的交互都發生在一個地方——Presenter內部;
3, 能夠將一個Presenter用於多個視圖,而不須要改變Presenter的邏輯;
4, 若是把邏輯放在Presenter中,就能夠脫離用戶接口來測試這些邏輯(單元測試);

缺點:

1, 因爲對視圖的渲染放在了Presenter中,因此View和Presenter的交互會過於頻繁而且難以維護;

MVVM

MVVM全名是Model-View-ViewModel,本質上就是MVC的改進版,也能夠說是MVP的改良版,把應用程序分紅三部分分別是:

  • Model(業務模型): 用於管理應用程序數據;
  • View(視圖界面): 經過使用模板語法來聲明式的將數據渲染進DOM;
  • ViewModel(視圖模型): 包含了領域模型(Domain Model)和視圖的狀態(State),核心就是雙向綁定技術(Two-Way-Data-Binding),View和Model之間數據同步操做交由給內部的Binder/Data-binding engine處理;

MVP和MVVM區別: 它使用 數據綁定(Data Binding)依賴屬性(Dependency Property)命令(Command)路由事件(Routed Event) 來搞定與view層的交互, 當ViewModel對Model進行更新的時候,會經過數據綁定更新到View.
圖片描述
(這些簡單的東西我就懶得特地畫圖了,直接百度圖片找張清晰的拿來用的..)
(更多內容請自行查閱,本節到此爲止了.)

優勢:

  • 雙向綁定(data-binding):View的變更,自動反映在 ViewModel,反之亦然;
  • 解放MVP大量手動同步狀態的問題,提升了代碼的可維護性;
  • 簡化測試,Model正確就能保證View輸出;

缺點:

  • 大型項目的綁定數據較多會提升維護成本;
  • View裏的數據綁定沒法檢測斷點,只能從Model下手;

React VS Vue

兩個框架是如今最熱門的選擇之一,它們既相似又不一樣.

  • 使用 Virtual DOM
  • 提供了響應式 (Reactive) 和組件化 (Composable) 的視圖組件。
  • 將注意力集中保持在覈心庫,而將其餘功能如路由和全局狀態管理交給相關的庫。

React就是MVC裏的V,只專一視圖層,而Vue算是MVVM框架,雙向綁定是特點之一.

介紹

咱們先看看它們本身的官方介紹:

React

React is a JavaScript library for building user interfaces.

  • Declarative: React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. Declarative views make your code more predictable, simpler to understand, and easier to debug.
  • Component-Based: Build encapsulated components that manage their own state, then compose them to make complex UIs. Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.
  • Learn Once, Write Anywhere: We don't make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code. React can also render on the server using Node and power mobile apps using React Native.

翻譯:

React是一個用於構建用戶界面的Javascript庫.

  • 聲明式: React讓你無痛建立交互式UI界面,爲你的App應用程序裏的每一個狀態設計簡單的視圖,而且當你的數據改變以後會進行高效地更新和正確地渲染對應組件,聲明式視圖讓你的代碼更可預測、更易於理解和更容易調試.
  • 組件化: 構建封裝組件管理它們本身的內部狀態,而後組合它們去構建複雜UI界面.由於組件邏輯寫在Javascript而不是模板裏,你能輕鬆注入豐富的數據到你的App而且狀態脫離在Dom以外.
  • 只需學習一次,就能用到任何地方,咱們不對你的其他技術棧做出假設,因此你能在React裏開發新的特性而不須要重寫你的現有代碼.React也能使用Nodejs進行服務器渲染和使用React Native進行移動端的豐富開發.

Vue

Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.

翻譯:

Vue.js (讀音 /vjuː/,相似於 view) 是一套構建用戶界面的漸進式框架。與其餘重量級框架不一樣的是,Vue 採用自底向上增量開發的設計。Vue 的核心庫只關注視圖層,它不只易於上手,還便於與第三方庫或既有項目整合。另外一方面,當與單文件組件和 Vue 生態系統支持的庫結合使用時,Vue 也徹底可以爲複雜的單頁應用程序提供驅動。

聲明式渲染

React(官方寫法)

React 組件實現一個 render() 方法,它接收輸入數據並返回顯示的內容。此示例使用相似XML的語法,稱爲 JSX 。輸入數據能夠經過 this.props 傳入組件,被 render() 訪問。

class HelloMessage extends React.Component {
  render() {
    return (
      <div>
        Hello {this.props.name}
      </div>
    );
  }
}

ReactDOM.render(
  <HelloMessage name="Taylor" />,
  mountNode
);

Vue(官方寫法)

Vue.js 的核心是一個容許採用簡潔的模板語法來聲明式的將數據渲染進 DOM 的系統

<div id="app">
  {{ message }}
</div>
// --------省略--------
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

HTML

React JSX

他是 JavaScrip 的一種擴展語法。 React 官方推薦使用這種語法來描述 UI 信息。JSX 可能會讓你想起某種模板語言,可是它具備 JavaScrip 的所有能力,從本質上講,JSX 只是爲 React.createElement(component, props, ...children) 函數提供的語法糖。

  • JSX 執行更快,由於它在編譯爲 JavaScript 代碼後進行了優化。
  • 它是類型安全的,在編譯過程當中就能發現錯誤。
  • 使用 JSX 編寫模板更加簡單快速。

JSX 對使用React 不是必須的。

Vue Templates

Vue.js 使用了基於 HTML 的模板語法,容許開發者聲明式地將 DOM 綁定至底層 Vue 實例的數據。全部 Vue.js 的模板都是合法的 HTML ,因此能被遵循規範的瀏覽器和 HTML 解析器解析。
在底層的實現上,Vue 將模板編譯成虛擬 DOM 渲染函數。結合響應系統,在應用狀態改變時,Vue 可以智能地計算出從新渲染組件的最小代價並應用到 DOM 操做上。
事實上 Vue 也提供了渲染函數,甚至支持 JSX。然而,默認推薦的仍是模板。

  • 對於不少習慣了 HTML 的開發者來講,模板比起 JSX 讀寫起來更天然。這裏固然有主觀偏好的成分,但若是這種區別會致使開發效率的提高,那麼它就有客觀的價值存在。
  • 基於 HTML 的模板使得將已有的應用逐步遷移到 Vue 更爲容易。
  • 這也使得設計師和新人開發者更容易理解和參與到項目中。
  • 你甚至可使用其餘模板預處理器,好比 Pug 來書寫 Vue 的模板。

對比

我的感受二者其實上手速度都挺快,相比之下JSX除了修改部分屬性名字跟普通HTML變化不算大,Templates額外添加不少自定義功能幫助開發者作更多的事,框架痕跡也比較重.
咱們能夠把組件區分爲兩類:一類是偏視圖表現的 (presentational)推薦使用模板,一類則是偏邏輯的 (logical)推薦使用 JSX 或渲染函數。

//Jsx寫法
<div className="list">
    <ol>
        { todos.map(item =><li>{todo.text}</li>) }
    </ol>
</div>
//Templates寫法
<div class="list">
    <ol>
        <li v-for="todo in todos">
            {{ todo.text }}
        </li>
    </ol>
</div>

CSS

React

React 中推薦經過 CSS-in-JS 的方案實現的 (好比 styled-components、glamorous 和 emotion),雖然在構建時將 CSS 提取到一個單獨的樣式表是支持的,但 bundle 裏一般仍是須要一個運行時程序來讓這些樣式生效。當你可以利用 JavaScript 靈活處理樣式的同時,也須要權衡 bundle 的尺寸和運行時的開銷

var styleObj = { color:"blue", fontSize:40, fontWeight:"normal" };
--------省略--------
<h1 style={styleObj} className="alert-text">Hello</h1>

Vue

Vue 設置樣式的默認方法是單文件組件裏相似 style 的標籤。讓你能夠在同一個文件裏徹底控制 CSS,將其做爲組件代碼的一部分。

<style scoped>
  @media (min-width: 250px) {
    .list-container:hover {
      background: orange;
    }
  }
</style>

這個可選 scoped 屬性會自動添加一個惟一的屬性 (好比 data-v-21e5b78) 爲組件內 CSS 指定做用域,編譯的時候 .list-container:hover 會被編譯成相似 .list-container[data-v-21e5b78]:hover。
最後,Vue 的單文件組件裏的樣式設置是很是靈活的。經過 vue-loader,你可使用任意預處理器、後處理器,甚至深度集成 CSS Modules——所有都在 <style> 標籤內。

對比

各有好壞吧,React侵入式作法不太喜歡,Vue組件式作法倒也還行,我的而言更傾向獨立css樣式外部引入易於管理.

狀態(State) OR 屬性(Data)

React

State是私有的,而且由組件自己徹底控制。

不要直接修改 state(狀態),setState() 代替;

// 錯誤
this.state.comment = 'Hello';

// 正確
this.setState({comment: 'Hello'});

state(狀態)更新會被合併,React 爲了優化性能,有可能會將多個setState()調用合併爲一次更新;

componentDidMount() {
  fetchPosts().then(response => {
    this.setState({
      posts: response.posts
    });
  });

  fetchComments().then(response => {
    this.setState({
      comments: response.comments
    });
  });
}

state(狀態) 更新多是異步的,不能依賴他們的值計算下一個state(狀態);

// 錯誤
this.setState({
  counter: this.state.counter + this.props.increment,
});

// 正確
//另外一種 setState() 的形式,它接受一個函數而不是一個對象。這個函數將接收前一個狀態做爲第一個參數,應用更新時的 props 做爲第二個參數
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

Vue

當一個 Vue 實例被建立時,它向 Vue 的響應式系統中加入了其 data 對象中能找到的全部的屬性。當這些屬性的值發生改變時,視圖將會產生「響應」,即匹配更新爲新的值。

只有當實例被建立時 data 中存在的屬性是響應式的

// 有效
data: {
  visitCount1: 0
}
--------省略--------
// 觸發任何視圖的更新
vm.visitCount1 = 2
// 不會觸發任何視圖的更新
vm.visitCount2 = 2

對比

React是屬於單向控制,即只能是經過改變State從而改變視圖,咱們能夠利用JS方法像表單等場景模擬雙向綁定的效果,實際仍是由State去觸發視圖更新
Vue是屬於雙向綁定,原理是經過 Object.defineProperty 監聽劫持data對象的getter/setter屬性來實現的

Props組件通訊

一個組件能夠選擇將數據向下傳遞,做爲其子組件的 props(屬性).

React

父組件單向控制

//父組件傳遞數據
<Child num={this.state.num} />
//--------省略--------
//子組件讀取數據
<h2>It is {this.props.num}.</h2>

子組件自主控制

//父組件傳遞數據
<Child num={this.state.num} />
//--------省略--------
constructor(props) {
  super(props);
  this.state = {
      //初始化成state
      num: this.props.num,
  };
}

父子組件雙向控制

//傳遞修改函數
class Father extends Component {
  construtor(props) {
    super(props);
    this.state = {
      num: 1,
    };
  }
  onChangeState(val) {
    this.setState(val);
  }
  render() {
    <Child num={this.state.num} onClicked={this.onChangeState.bind(this)} />;
  }
}

//調用修改函數添加入參
class Child extends Component {
  render() {
    <button onClicked={() => this.props.onClicked({num: 2})}>
      It is {this.props.num}.
    </button>;
  }
}

Vue

父組件單向控制

//父組件傳遞數據
<child message="hello!"></child>
//--------省略--------
//子組件要顯式地用 props 選項聲明它預期的數據
Vue.component('child', {
  // 聲明 props
  props: ['message'],
  // 就像 data 同樣,prop 也能夠在模板中使用
  // 一樣也能夠在 vm 實例中經過 this.message 來使用
  template: '<span>{{ message }}</span>'
})

子組件自主控制

//父組件傳遞數據
<child message="hello!"></child>
//--------省略--------
//1, 定義一個局部變量,並用 prop 的值初始化它:
props: ['message'],
data: function () {
  return { msg: this.message }
}
template: '<span>{{ msg }}</span>'

//2, 定義一個計算屬性,處理 prop 的值並返回:
props: ['message'],
computed: {
  msg: function () {
    return this.message
  }
}
template: '<span>{{ msg }}</span>'

父子組件雙向控制

//父組件傳遞數據
<child v-bind:message="message" v-on:update:message="message = $event"></child>
//--------可用.sync 修飾符替代--------
//後面傳遞的message是變量,非字符串
//<child :message.sync="message"></child>
//--------省略--------
//子組件
props: ['message'],
data: function () {
  return { msg: this.message }
}
watch: {
    //監聽msg變化自動通訊父組件更改
    msg(val) {
        this.$emit('update:message', newMsg)
    },
},

生命週期對比

React

React生命週期 做用
getDefaultProps 做用於組件類,只調用一次,返回對象用於設置默認的props,對於引用值,會在實例中共享
getInitialState 做用於組件的實例,在實例建立時調用一次,用於初始化每一個實例的state,此時能夠訪問this.props。
componentWillMount 在完成首次渲染以前調用,此時仍能夠修改組件的state
render 必選的方法,建立虛擬DOM,該方法具備特殊的規則: 1) 只能經過this.props和this.state訪問數據; 2) 能夠返回null、false或任何React組件; 3) 只能出現一個頂級組件(不能返回數組); 4) 不能改變組件的狀態; 5) 不能修改DOM的輸出;
componentDidMount 真實的DOM被渲染出來後調用,在該方法中可經過this.getDOMNode()訪問到真實的DOM元素。此時已可使用其餘類庫來操做這個DOM。在服務端中,該方法不會被調用。
componentWillReceiveProps 組件接收到新的props時調用,並將其做爲參數nextProps使用,此時能夠更改組件props及state
shouldComponentUpdate 組件是否應當渲染新的props或state,返回false表示跳事後續的生命週期方法,一般不須要使用以免出現bug。在出現應用的瓶頸時,可經過該方法進行適當的優化。在首次渲染期間或者調用了forceUpdate方法後,該方法不會被調用
componentWillUpdate 接收到新的props或者state後,進行渲染以前調用,此時不容許更新props或state
componentDidUpdate 完成渲染新的props或者state後調用,此時能夠訪問到新的DOM元素。
componentWillUnmount 組件被移除以前被調用,能夠用於作一些清理工做,在componentDidMount方法中添加的全部任務都須要在該方法中撤銷,好比建立的定時器或添加的事件監聽器
componentDidCatch 16.x新增,捕獲全局異常來進行頁面的友好提示
Vue生命週期 做用
beforeCreate 在實例初始化以後,數據觀測 (data observer) 和 event/watcher 事件配置以前被調用
created 在實例建立完成後被當即調用。在這一步,實例已完成如下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前不可見
beforeMount 在掛載開始以前被調用:相關的 render 函數首次被調用。該鉤子在服務器端渲染期間不被調用
mounted el 被新建立的 vm.$el 替換,並掛載到實例上去以後調用該鉤子。若是 root 實例掛載了一個文檔內元素,當 mounted 被調用時 vm.$el 也在文檔內. 注意 mounted 不會承諾全部的子組件也都一塊兒被掛載。若是你但願等到整個視圖都渲染完畢,能夠用 vm.$nextTick 替換掉 mounted
beforeUpdate 數據更新時調用,發生在虛擬 DOM 從新渲染和打補丁以前。你能夠在這個鉤子中進一步地更改狀態,這不會觸發附加的重渲染過程。該鉤子在服務器端渲染期間不被調用
updated 因爲數據更改致使的虛擬 DOM 從新渲染和打補丁,在這以後會調用該鉤子。當這個鉤子被調用時,組件 DOM 已經更新,因此你如今能夠執行依賴於 DOM 的操做。然而在大多數狀況下,你應該避免在此期間更改狀態。若是要相應狀態改變,一般最好使用計算屬性或 watcher 取而代之,注意 updated 不會承諾全部的子組件也都一塊兒被重繪。若是你但願等到整個視圖都重繪完畢,能夠用 vm.$nextTick 替換掉 updated:該鉤子在服務器端渲染期間不被調用
activated keep-alive 組件激活時調用。該鉤子在服務器端渲染期間不被調用
deactivated keep-alive 組件停用時調用。該鉤子在服務器端渲染期間不被調用
beforeDestroy 實例銷燬以前調用。在這一步,實例仍然徹底可用。該鉤子在服務器端渲染期間不被調用。
destroyed Vue 實例銷燬後調用。調用後,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬
errorCaptured 當捕獲一個來自子孫組件的錯誤時被調用。此鉤子會收到三個參數:錯誤對象、發生錯誤的組件實例以及一個包含錯誤來源信息的字符串。此鉤子能夠返回 false 以阻止該錯誤繼續向上傳播

React示意圖

圖片描述

Vue示意圖

圖片描述

再渲染性能

React

在React裏渲染機制是以組件單位更新的,也就是說當數據發生改變,當前視圖包括其中的組件子組件和底下的子組件都會一塊兒更新,這種違反性能的機制確定是有問題的,因此React提供了生命週期shouldComponentUpdate方法讓你決定當前組件是否更新,還有一個PureComponent方法會自動檢測到state或者props發生變化時,纔會調用render方法.可是隻是淺比較,若是搭配ImmutableJs持久化數據聽說性能大大的提高.除此以外還能節省大量的手動比較的代碼和時間,

簡單描述過程

  1. 調用render函數利用JS生成虛擬Dom樹,直到數據state/props發生改變的時候,render函數會被再次調用渲染出另一棵虛擬Dom樹;
  2. 比較先後兩棵Dom樹同層級的節點區別,非同層級節點包括所屬子節點整個直接刪除從新建立;

    • 不一樣的節點類型,直接替換.
    • 相同節點類型當中的DOM節點,替換屬性.
    • 相同類型當中的組件節點,繼續遞歸比較所屬子節點.
    • DOM節點的遞歸children,繼續遞歸比較children.
    • 列表比較.賦予惟一的key做比較.
  3. 更新視圖中差別地方;

Vue

在 Vue 應用中,組件的依賴是在渲染過程當中自動追蹤的,因此係統能精確知曉哪一個組件確實須要被重渲染,由於Vue是使用 Object.defineProperty對綁定屬性進行數據劫持的,因此比起React組件式更新它可以精確接收到哪些組件纔是須要渲染的.

  1. Vue 將遍歷此data對象全部的屬性,並使用 Object.defineProperty 把這些屬性所有轉爲 getter/setter.
  2. 每一個組件實例都有相應的 watcher 實例對象,它會在組件渲染的過程當中把屬性記錄爲依賴.
  3. 在屬性被訪問和修改時通知對應的組件.
  4. 對應的組件再次調動渲染函數,生成虛擬Dom樹對比,實現更新.

路由

React-Router

這是React-Router3的模板寫法,實際到了React-Router4的API和思想都有些大的差別

import React from 'react'
import { render } from 'react-dom'

// 首先咱們須要導入一些組件...
import { Router, Route, Link } from 'react-router'

// 而後咱們從應用中刪除一堆代碼和
// 增長一些 <Link> 元素...
const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        {/* 把 <a> 變成 <Link> */}
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inbox">Inbox</Link></li>
        </ul>

        {/*
          接著用 `this.props.children` 替換 `<Child>`
          router 會幫咱們找到這個 children
        */}
        {this.props.children}
      </div>
    )
  }
})

// 最後,咱們用一些 <Route> 來渲染 <Router>。
// 這些就是路由提供的咱們想要的東西。
React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
    </Route>
  </Router>
), document.body)

Vue-Router

Vue-Router3的模板寫法

// 0. 若是使用模塊化機制編程,導入Vue和VueRouter,要調用 Vue.use(VueRouter)

// 1. 定義(路由)組件。
// 能夠從其餘文件 import 進來
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定義路由
// 每一個路由應該映射一個組件。 其中"component" 能夠是
// 經過 Vue.extend() 建立的組件構造器,
// 或者,只是一個組件配置對象。
// 咱們晚點再討論嵌套路由。
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

// 3. 建立 router 實例,而後傳 `routes` 配置
// 你還能夠傳別的配置參數, 不過先這麼簡單著吧。
const router = new VueRouter({
  routes // (縮寫)至關於 routes: routes
})

// 4. 建立和掛載根實例。
// 記得要經過 router 配置參數注入路由,
// 從而讓整個應用都有路由功能
const app = new Vue({
  router
}).$mount('#app')

// 如今,應用已經啓動了!

狀態管理

狀態管理庫有不少種,我只是舉出我用過的例子,並非必須的.下面只會展現最基本的代碼,想跑完整流程還得看文檔.

Redux

Redux 能夠用這三個基本原則來描述:

  • 單一數據源: 整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。
  • State 是隻讀的: 惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。
  • 使用純函數來執行修改: 爲了描述 action 如何改變 state tree ,你須要編寫 reducers。

Actions: 把數據從應用傳到store的有效載荷。它是store數據的惟一來源.

/*
 * action 類型
 */
export const ADD_TODO = 'ADD_TODO';

/*
 * action 建立函數
 */
export function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}

Reducers: 指定了應用狀態的變化如何響應actions併發送到store的,記住actions只是描述了有事情發生了這一事實,並無描述應用如何更新state。

import {
    combineReducers
} from 'redux'
import {
    ADD_TODO,
} from './actions'

function todos(state = [], action) {
    switch (action.type) {
        case ADD_TODO:
            return [
                ...state,
                {
                    text: action.text,
                    completed: false
                }
            ]
        default:
            return state
    }
}

const todoApp = combineReducers({
    todos
})

export default todoApp

Store: 就是把Actions和Reducers聯繫到一塊兒的對象.

  • 維持應用的 state;
  • 提供 getState() 方法獲取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 經過 subscribe(listener) 註冊監聽器;
  • 經過 subscribe(listener) 返回的函數註銷監聽器。
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

單向數據流
圖片描述
(這些簡單的東西我就懶得特地畫圖了,直接百度圖片找張清晰的拿來用的..)

  1. 定義Action描述對象;
  2. 經過dispatcher觸發Action對象;
  3. Reducer響應變化更新到Store狀態管理對象;
  4. 注入Store狀態的組件視圖更新;
  5. 界面交互觸發Action再次跑相應流程;

進階:

  1. Action利用中間件發起異步請求;
  2. Reducer邏輯拆分;
  3. 組件注入部分Store狀態;
    等等,Redux 中文文檔,(更多內容請自行查閱,本節到此爲止了.)

Mobx

另外一種實現方式,具體可看Mobx4.X狀態管理入門

Vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。

Mutation: 必須是同步函數,更改Vuex的store中的狀態的惟一方法是提交mutatio

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import {
    SOME_MUTATION
} from './mutation-types'

const store = new Vuex.Store({
    state: { ...
    },
    mutations: {
        // 咱們可使用 ES2015 風格的計算屬性命名功能來使用一個常量做爲函數名
        [SOME_MUTATION](state) {
            // mutate state
        }
    }
})

Action 相似於mutation,不一樣在於:

  • Action 提交的是 mutation,而不是直接變動狀態;
  • Action 能夠包含任意異步操做;
actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

Getter: 從store中的state中派生出一些狀態

getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

State: 包含了所有的應用層級狀態。至此它便做爲一個「惟一數據源 (SSOT)」而存在.

const app = new Vue({
  el: '#app',
  // 把 store 對象提供給 「store」 選項,這能夠把 store 的實例注入全部的子組件
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})

Module: Vuex 容許咱們將 store 分割成模塊(module)。每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態

單向數據流
圖片描述
(官網來的)
Vuex

對比

  • Redux的Action能夠是異步或者同步函數返回Javascript對象,Vuex分爲同步函數不限制格式的mutation和可包含異步函數的action
  • React直接dispatch action觸發狀態更新,Vuex是dispatch action提交mutation再commmit觸發狀態更新
  • Redux指定了應用狀態的變化如何響應actions併發送到store的reducer,且必須爲純函數.Vuex其實是使用mutation更新狀態

    • Redux: (dispatch)action -> (reducer)store
    • Vuex: (dispatch)action -> (commit)mutation -> (mutate)store
  • Vuex能夠用getter派生出一些狀態,就像計算函數會被緩存起來只有依賴變化以後纔會從新計算.

(更多內容請自行查閱,本節到此爲止了.)

官方腳手架

React 官方提供了 create-react-app,詬病的地方比較多.

  • 它不容許在項目生成時進行任何配置,而 Vue 支持 Yeoman-like 定製。
  • 它只提供一個構建單頁面應用的單一模板,而 Vue 提供了各類用途的模板。
  • 它不能用用戶自建的模板構建項目,而自建模板對企業環境下預先創建協議是特別有用的。

更多人選擇本身搭建或者使用民間打包庫.

Vue 提供了 Vue-cli 腳手架,能讓你很是容易地構建項目,包含了 Webpack,Browserify,甚至 no build system,可是有些設置例如Scss預處理器等自定義配置須要本身搞,總的來講至關實用.

入門難度

React正常來講須要搭配Jsx和Es6語法和構建環境;Vue能夠直接引入js庫就能開發,並且內置的功能屬性比React多得多

相關文章
相關標籤/搜索