mnv*框架開發時代

原文出處:極限前端javascript

  當下前端開發框架設計顯然已經在mvvm方式上又發展了一步,virtual dom 提出不久,使用前端代碼來調用native的思路就開始被實踐。相信你們也知道是什麼東西。到了今天,咱們不得不認可,mnv* 框架開發時代已經到來。html

  mnv是什麼,具體能夠這麼理解,model-Native-View-*,然後面的則能夠認爲是 virtual dommvvm 中的 ViewModel,或者咱們也能夠本身使用controller來實現的調用方式。想一想這樣定義是很是合適的。相比以前的不一樣,就是用 nativeView代替了 htmlView。那麼咱們再看看下從dom apimnv*,咱們爲何會看到這樣的變化。前端

1、dom交互

  在此以前不得不提下以前的dom交互框架,就是直接選擇找到特定的dom進行操做,思路十分直接也很實用,經過dom交互框架,相比JavaScript原生API,咱們能夠比較高效的處理dom的改變和事件綁定了,這種高效的方式給咱們到來了效率上的提升,可是頁面複雜了就很差處理了。java

  隨着ajax技術的盛行,SPA應用開始被普遍運用。SPA的引入將整個應用的內容都在一個頁面中進行異步交互。這樣,原有的dom交互方式開發就顯得很差管理,例如某SPA頁面上交互和異步加載的內容不少,咱們的作法是每一次請求渲染後作事件綁定,異步請求後再作另外一部分事件綁定,後面以此類推。當全部異步頁面所有調用完成,頁面上的綁定將變得十分混亂,各類元素綁定,渲染後的視圖內容邏輯不清,又不得不聲明各類變量保存每次異步加載時返回的數據,由於頁面交互須要對這些數據作操做,最後寫完,項目代碼就成了一鍋粥。react

2、前端mvc

  爲了更方便的統一管理頁面上的事件、數據和視圖內容,就有了早期mvc的框架設計。mvc能夠認爲是一種設計模式,其基本思路是將dom交互的過程分爲調用事件、獲取數據、管理視圖。即將以前全部的事件、數據、視圖分別統一管理。用model來存放數據請求和數據操做,視圖來存放對視圖的操做和改變,controller用來管理各類事件綁定。ajax

  例如,SPA中的每一個異步頁面能夠當作是一個組件,以前的作法是每一個組件獨立完成本身的數據請求操做、渲染和數據綁定,可是組件多了,每一個組件本身去作就比較混亂,邏輯比較混亂。到了mvc裏面,全部的組件數據請求、渲染、數據綁定都到一個統一的model、view、controller註冊管理。後面的操做咱們就不在管你有多少個組件了,你要調用必需要經過統一的model、view、controller來調。通俗來講就像是組件交出了本身控制權到一個統一的地方註冊調用,這樣就方便了不少,相信你們都已經瞭解過,這裏就省篇幅不舉例了。算法

3、 前端mvp

  mvp能夠跟mvp對照起來看,並且咱們也不多專門去關注它。和mvc同樣,mvc的M就是 Model, V就是View,而P,則表明Presenter,它與Controller有點類似。不一樣的是,在mvc中V會直接展示M,而在mvp中V會把全部的任務都委託給P。V和P會互相持有reference,所以能夠互相調用。編程

例如咱們能夠在MVC代碼上作一點改變,寫成這樣:react-native

<div controller="Controller.vp" id="text">html</div>
var Controller = new Controller();
Controller['vp']= new VP({
    $el: $('text'),
    click: fn(e){
        console.log(self.$el.html());
    },
    mouseenter: function(e){
        console.debug(self.$el.html());
    },
    mouseleave: function(e){
        console.info(self.$el.html());
    }
});

  幾個好處,這樣將view和Controller的引用關聯了起來,而MVC通常是經過事件監聽或觀察者的異步方式來實現的,咱們能夠在任意地方定義註冊監聽事件都不會有問題,這樣監聽的事件和觸發這個事件的html元素脫離了引用,當應用複雜起來後要維護dom的交互邏輯就比較麻煩了。而mvp提供了一個簡單的引用,將元素對應的操做於對應的presenter關聯起來。咱們要查詢元素對應的controller時只要經過Controller.vp就能夠直接調用了,其實這個時候就和mvvm的定義方式有點相似了,不是嗎?設計模式

4、前端mvvm

  mvvm概念能夠認爲是一個自動化的presenter,也這個時候進一步弱化了C層,任何操做都經過viewModel來驅動。Controller最終在頁面上的行爲經過directives的形式體現,經過對directives的識別來註冊事件,這樣管理起來就更清晰了。看一個mvvm框架定義的例子。

<form action="" id="form">
    <label for="text" q-html="label"></label>
    <input type="text" q-value="value" q-model="value" q-mydo="number | getValue">
    <button q-on="click: submit"></button>
</form>
let viewModel = new VM({
    $el: '#form',
    data:{
        label: '用戶名',
        value: '輸入初始值',
        number: 0
    },
    method:{
        submit(){
            // doSubmit
        }
    },
    directive:{
        mydo(value){
            console.log(value);
        }
    },

    filter:{
        getValue(){
            reutrn value ++;
        }
    }
})

  和MVP的定義比較,有點相似,mvvm設計一個很大的好處是將mvc中controller中的controller註冊到相對應的元素中,讓咱們後期維護時很快定位,免去了查看controller中event列表的工做,並且初始化後自動作數據綁定,能將頁面中全部同類操做複用,大大節省了咱們本身寫代碼作綁定的時間。這段代碼中初始化時自動幫咱們就作了數值填充、數據雙向綁定、事件綁定的事情。那麼框架怎樣幫我作的呢。咱們來看下new VM作了哪些事情:這裏傳入了元素、數據、方法列表、自定義directive列表,首先程序找到這個元素,開始對這個元素的屬性節點進行遍歷,一旦遍歷到屬性名稱含有q-開頭的屬性是,認爲是mvvm框架自定義的屬性,而後會對屬性的指進行特殊處理;例如遍歷到q-html="label"時,將data中的label值賦給這個元素的innerHTML;若是遍歷到q-on="click: submit"時,將這這個元素上綁定click事件,事件回調函數爲submit;也能夠自定義q-mydo的指令,遍歷到該節點屬性是,調用directive中的mydo方法,輸入參數爲data中的getValue方法返回的值,getValue輸入參數爲number值,這裏的getValue被稱爲過濾器。

  這裏要知道的是q-開頭的屬性指令是框架約定的,不一樣的框架約定的不同,例如ng-v-ms-,這些你們也都見過或用過。這裏viewModel建立進行綁定的原理就這麼簡單,按照這個思路去擴充,就能夠本身寫一個mvvm框架。固然完整的框架涉及東西多的多,含有豐富的directive、filter、表達式、vm完善的api和甚至一些兼容性處理等。

  總結來講從mvc到mvp,而後到mvvm,前端設計模式仍然是向着易實現、易維護、易擴展的基本方向發展的。但目前前端各種框架也已經成熟並開始版本迭代。可是,這尚未結束,咱們依然沒有脫離dom編程的基本套路,一次次框架的改進只是提升了咱們的開發效率,可是dom元素的效率仍沒有獲得本質的提高。

5、前端virtual dom

  爲了改進dom交互的效率,或者說是儘可能減小dom交互的次數,virtual dom的概念當下十分盛行,目前圈內各類大小團隊紛紛投入項目使用。由於viewModel的改變最終仍是要實時操做dom來刷新view層,而dom對象的操做相對於JavaScript對象的操做仍然是要慢些。緣由很簡單,dom節點對象的內置屬性不少,就建立一個dom對象而言,dom的建立須要處理各類內置屬性的初始化,而若是使用JavaScript對象來描述就簡單了。

  使用virtual dom,頁面的渲染過程再也不是數據直接經過前端模板渲染到頁面,也不是初始化viewModel進行頁面模板填充和事件綁定,而是經過dom衍生描述語法(這爲何稱爲DOM衍生描述語法,一般咱們經過html來描述,可是目前一些框架是經過非標準的html的方式描述的,定義的一套迎合本身框架的方式,其實使用html也是能夠的)解析生成virtual dom,頁面交互變成了是修改virtual dom,而後將virtual dom的改變反映到htmlView層上。

  例如如下結構:

<ul id="ui-list">
  <li class="ui-list=item">1</li>
  <li class="ui-list-item">2</li>
  <li class="ui-list-item">3</li>
</ul>

  可使用以下javascript來表示

var element = {
  tagName: 'ul',
  props: {
    id: 'ui-list'
  },
  children: [
    {tagName: 'li', props: {class: 'ui-list-item'}, children: ["1"]},
    {tagName: 'li', props: {class: 'ui-list-item'}, children: ["2"]},
    {tagName: 'li', props: {class: 'ui-list-item'}, children: ["3"]},
  ]
}

  若是javascript對象children屬性第三個元素要被移除,同時,添加一個class爲ui-list-item2的li節點,則首先須要對javascript對象進行修改記錄全部的操做,最後將修改的vitual dom變化反映到頁面上:

<ul id="ui-list">
  <li class="ui-list=item">1</li>
  <li class="ui-list-item">2</li>
  <li class="ui-list-item2"></li>
</ul>

  這裏的javascript對象就至關於virtual dom,用戶的某個交互操做可能致使dom的多個地方,若是沒有vitual dom,那可能就要進行屢次dom操做,virtual dom則能夠將多個用戶交互操做反映在virtual dom上,最後作的virtual dom DIFF算法而後再dispatch到頁面view層上。相對於mvvm,在頁面初始化渲染階段,也避免了掃面節點,解析directives,要知道這些操做都是dom操做,使用virtual dom顯然能將頁面渲染速度提升很多。

6、前端 mnv*

  若是說vitual dom減小了dom的交互次數,那麼mnv*想要作的一件事情就是徹底拋棄使用dom,那樣就只能在view層作改進了,使用nativeView來代替目前html的view,而交互邏輯依然可使用viewModel、virtual Dom或者controller來實現,具體就看實現的方式了。

  要作到NativeView的操做,這裏與以前不一樣之處就是調用時經過衍生HTML語法經過解釋器執行nativeView的渲染,這是就須要在native和衍生HTML語法之間添加一層解釋器來解析現有的view描述語法了。好比咱們看一個渲染Native的例子:

// iOS

import React, {
  Component,
} from 'react';
import {
  TabBarIOS, 
  NavigatorIOS 
} from 'react-native';

class App extends Component {
  render() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item title="React Native" selected={true}>
          <NavigatorIOS initialRoute={{ title: 'React Native' }} />
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
}

  這裏和vitual dom框架相似的地方都是都使用衍生的html描述語法來表示view層,而不一樣的是mnv模式是調用的nativeView來實現的衍生html的view展現。其實這裏和上節中的實現惟一不一樣的地方是這裏的view是native view。固然這只是一種實現,目前mnv的實現方案已經不止一種了,有人已經實踐了經過mvvm的編程方式來將viewModel渲染轉化爲native view的方案。

7、總結

  總結下來,前端框架一次次進化,先從效率的方向上提高,而後再性能上完善,這裏只是想提出mnv*的一個概念來描述前端native開發的這個階段。目前mnv的開發模式開始進入視線,也在快速地造成和創建生態。但儘管如此,咱們若是須要選擇的技術棧方案,固然仍是以最適合咱們的做爲最高原則。切忌過分設計。

相關文章
相關標籤/搜索