vue總結系列--數據驅動和響應式

在公司裏幫項目組裏開發後臺系統的前端項目也有一段時間了。 vue這種數據驅動,組件化的框架和react很像,
從一開始的快速上手基本的開發,到後來開始自定義組件,對element UI的組件二次封裝以知足項目需求,期間也是踩了很多坑。
因爲未來很長一段時間可能不再會接觸前端了,趁着如今對vue還很熟練的時候,趕忙將這期間的收穫記錄下來。css

按照個人理解,vue的特色在於,高層次的抽象模型,組件化,數據驅動與響應式。
<!--more-->html

注意:本文中全部涉及到{ {的,實際上不包括中間的空格,由於hexo會無腦把這個語法當成hexo的一種特殊語法 前端

vue是什麼及特色

vue是什麼

簡單的來講,vue是一套用於前端開發的框架。
學習一門技術首先應該對該技術有着一個清晰正確的認識和思路,不然猶如浮沙之上建高樓。下面就來談談我對vue這個框架的理解。vue

vue它的驚豔之處在於,對上,它提供了一套很是優秀的與傳統前端大相徑庭的前端開發模型,對下,它屏蔽了傳統前端開發的各類醜陋的技術。
在實現複雜的頁面需求時,vue幫你從傳統前端的各類HACK般的奇淫技巧解脫出來,vue提供的抽象開發模型擁有強大的 表達能力,可以如絲順滑般的完成各類複雜的需求。java

總而言之,vue之於傳統前端,相似C語言之於彙編語言,就像C語言提供了一個強大的表達能力強的語言模型同樣,vue一樣提供了一個強悍的表達前端的框架模型,這是抽象的威力。
所以,學習vue的80%的重心,都在學習這個強悍的vue模型上。python

vue的特色

從我我的對vue的理解,它的幾個重要特色有:react

  1. 抽象的組件。這是vue框架模型中的一個基礎,vue的一切都是基於vue組件的。你在vue組件上可使用vue提供的各類特性,vue會將其轉換成瀏覽器可以識別的結果經過瀏覽器渲染出來。
  2. 數據驅動。這是vue的理念之一,數據纔是項目的核心,也是狀態的最終保存者。而視圖,只不過是一種可以由數據延遲計算出來的最終結果而已,它自己不存儲狀態。
  3. 響應式。既然渲染結果只是數據的衍生產物,那麼當數據改變時,渲染結果天然而然的也要改變。這種機制就是響應式。
  4. 組件化。vue自己提供的 抽象特性 。從我我的的理解來看,自定義組件在vue中的地位,相似於函數在C,java之類的語言之中的地位。

它還有幾個其它的的特色:webpack

  1. 易用和功能兼顧。在我傳統的思惟裏,易用的東西通常會犧牲一部分功能,而功能強大的東西會須要很大的成本學習,魚和熊掌不可兼得。
    然而vue用實際行動狠狠給了我一耳光,只要模型設計的正交完美,徹底可以兼顧易用性和功能性。
  2. 與傳統前端的良好兼容。
    我目前所作的項目中,因爲需求的複雜化項目須要從easy UI迭代到vue,本來我作好了大刀闊斧大規模重構的準備,不過,通過查閱文檔和幾個小demo實驗我發現,這玩意竟然可以和傳統前端並存!
    結果是很是使人開心的,我逐步的,慢慢的將項目從easy UI迭代到vue版本,一個頁面一個頁面的替換,對項目的影響下降到了最小。

組件定義

下面的例子定義了一個簡單的組件,x-my-component。
因爲是須要兼容easy UI的後臺系統,該項目並不能使用到webpack + babel + es6等技術,所以我在vue上的經驗大都是使用原生的js編寫的,git

<div id="x-my-component">
    <div>
      { { message }}
    </div>
</div>

<script>
Vue.component('x-my-component', {
  template: '#x-my-component',
  data: function () {
      return {
        message: 'Hello Vue!'
      };
  }
});
</script>
  1. 能夠看見一個組件分爲兩部分:視圖部分,和數據部分。
  2. 視圖部分使用html定義,可是vue會對一些特別的語法賦予vue本身的意義。如 { { message }} 表示將數據message綁定到視圖的這個地方來。
  3. 這裏是使用原生js編寫的,所以數據部分和視圖部分經過id選擇器關聯起來,定義了一個x-my-component全局組件。
  4. 從這裏就能夠初步感覺到數據驅動。組件的狀態都是存放的數據中的,而視圖只負責渲染,自己不保存狀態。
    當數據不變時,渲染結果必定不變;當數據改變時,渲染結果就要發生改變。

因爲vue是組件化的,組件能夠層層嵌套。不過,最終的根節點得是app節點,這樣才能渲染出結果。它的定義方式不太相同:es6

<div id="app">
    <x-my-component></x-my-component>
</div>
<script>
var app = new Vue({
  el: '#app',
});
</script>
  1. 從這裏能夠看出,自定義的全局組件在vue的其它組件中能夠直接使用,就像使用一個已經存在的html標籤同樣。

 數據綁定

從數據驅動的觀點,咱們很容易分析出數據流向:
數據源 --> vue對象 --> DOM

文本綁定,html綁定及xss攻擊

<div id="x-my-component">
  <span v-bind:title="message">
  { { message }}
  { { message.split('').reverse().join('') }}
  </span>
  <span :title="message"> </span>
  <div v-html="rawHtml"></div>
</div>
<script>
Vue.component('x-my-component', {
  template: '#x-my-component',
  data: function () {
      return {
        message: 'Hello Vue!',
        rawHtml: '<s>deleted!</s>'
      };
  }
});
</script>
  1. 將數據綁定到通常的內容上,用簡單的 { {}} 的語法就能夠了。
  2. 將數據綁定到屬性上,須要用特殊的 v-bind 語法。上面將 message 數據綁定到了title屬性上。
    因爲這個語法太經常使用了, v-bind:title 可直接縮寫爲 :title
  3. -v-html 語法,綁定原始的html數據。
  4. 能綁定的不只僅是屬性鍵值,還能夠是 單個表達式 。注意 只能 是單個表達式。
    這些表達式只能訪問全局變量的一個白名單,如Math和Date, 不能訪問用戶自定義的全局變量

在傳統的前端編程中,須要仔細預防的一點是xss攻擊。xss攻擊的原理是:

  1. 用戶輸入的數據直接被插入到了DOM中。
  2. 用戶輸入的數據中含有html或js代碼,那麼它會直接做爲DOM的一部分,被渲染或被執行出來。
  3. 利用這一點,就可以創造一個指向攻擊者頁面的一張圖片或者連接,從而盜取用戶的cookies或sid,或者誘導用戶點擊釣魚網址。

所以,爲預防xss,任何用戶給定的數據展示到頁面上都須要仔細的處理。可是若是使用vue,就沒必要太操心這個問題。
由於,vue提供了一個更高層次的前端模型,全部的業務代碼都須要經過vue處理。咱們只須要看vue對xss的處理狀況就能夠了。
從上面咱們能夠看到,數據到視圖渲染中必須使用vue的插值語法:

  1. 普通的文本插值。vue會過濾xss攻擊,所以能夠安全使用。
  2. 使用v-html語法將html插入渲染後的DOM中。這固然會形成xss攻擊,所以,使用v-html語法時要很是的當心。

條件與循環

<div id="x-my-component">
  <p v-if="seen">如今你看到我了</p>
  <ol>
    <li v-for="todo in todos">
      { { todo.text }}
    </li>
  </ol>
</div>
<script>
Vue.component('x-my-component', {
  template: '#x-my-component',
  data: function () {
      return {
        seen: true,
        todos: ["A", "B", "C"]
      };
  }
});
</script>
  • v-if 綁定的數據,控制此元素是否顯示。
  • v-for 綁定的特殊語法,用於控制循環, v-for 的元素會被重複。如上所示,todos是一個數組。

響應式

上面的例子中能夠數據綁定的語法,實際上,將數據綁定到視圖上的操做並很多見,這些操做,和後端技術經常使用的模樣引擎相似,如python的Jinja2, java的freemarker。
可是,vue的數據綁定的還有一大不一樣點點是 響應式
因爲視圖渲染是從數據計算出來的產物,相似一個傳入數據傳出渲染結果的純函數,所以,當數據改變時,渲染結果也會改變,所獲得的結果是頁面展現的界面也會發送改變。

數據綁定還不能徹底走通一個vue組件的數據流程,從上面的數據綁定咱們能夠發現一個事實:
數據由vue對象流向視圖進而渲染出來。

那麼,反過來呢?若是讓數據從頁面流向vue對象?這就是下面要說到的事件處理。

事件處理

事件處理的數據流向是這樣的:
用戶操做頁面產生數據 --> 頁面 --> vue對象

<div id="x-my-component">
  <p>{ { message }}</p>
  <button v-on:click="reverseMessage">逆轉消息</button>
  <button @click="reverseMessage">逆轉消息</button>
  <input v-model="message">
</div>
<script>
Vue.component('x-my-component', {
  template: '#x-my-component',
  data: function () {
      return {
        message: 'Hello Vue.js!'
      };
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('');
    }
  }
});
</script>
  1. 經過 v-on 語法,綁定事件。按鈕的數據傳入到vue對象中。
    這個語法太經常使用了,因此 v-on:click 可縮寫成 @click
  2. 經過 v-model 語法,將表單的數據傳入到vue對象中。v-model的數據流動是雙向的,至關於:value數據綁定和@input事件綁定的語法糖。

可以與用戶交互的組件都可以產生數據。當用戶操做組件時,組件的相應事件被觸發,進而執行綁定到事件的相應函數。
在函數內部你能夠作任何處理邏輯,好比說改變vue對象記錄的狀態,也就是數據。

那麼,這樣,一個完整的數據流程就跑通了:

  1. 用戶操做vue組件。
  2. 相應的事件被觸發,從而致使綁定事件的函數被觸發。
  3. 函數執行業務代碼,改變vue組件的狀態,也就是前面說到的數據綁定中的數據。
  4. 數據被改變,綁定了該數據的視圖也會發送改變,視圖被從新渲染。
  5. 頁面發生相應改變,用戶觀察到了本身操做的反饋結果。

再談響應式

virtual DOM

咱們知道,數據被綁定到視圖上,視圖自己不存儲狀態,當數據不變時,渲染結果不變;當數據改變時,視圖也須要從新渲染。

好了,如今頭腦裏可能會有一個簡單的思路了:

  1. 當數據改變時,觸發數據的setter,setter除了更改數據變更外,還須要執行數據更新邏輯。
  2. 從新執行渲染函數,重新的數據中計算出新的渲染結果。
  3. 將渲染結果插入瀏覽器DOM樹中。

可是,咱們仔細考察下第三步。瀏覽器的DOM樹改變後,瀏覽器纔會真正的從新渲染這個DOM,計算各類css,各類佈局,層層調用各類繪圖函數從新繪製GUI等等。。。
總而言之,這個步驟是很是很是耗時的。

那麼,一個優化的手段是,儘量讓真正的DOM改變的最少,這樣瀏覽器只須要渲染最少的結果就能達到效果,提高性能。
所以,vurtual DOM就出現了。以下:

  1. 當數據改變時,觸發數據的setter,setter除了更改數據變更外,還須要執行數據更新邏輯。
  2. 從新執行渲染函數,重新的數據中計算出新的渲染結果。
  3. 渲染結果被放在virtual DOM中,vue將新的DOM和就的DOM進行diff。
  4. 將diff後的差別結果更新到真正的瀏覽器DOM樹中。

能夠看到,整個流程中的一大重點是diff算法。關於DOM的diff算法,我沒有接觸過。。。。。。。
以前研究過文本diff算法是基於LCS算法的動態規劃優化,以後有興趣能夠研究下DOM的diff算法。

數據更新檢測的坑

從上面咱們知道,當直接設置一個vue屬性的值時,會觸發getter函數。
實際上,getter函數是js的一個機制。在修改數組或對象時,也有相似的機制保證vue能檢測到數據的更新以觸發渲染。

然而,有幾種狀況,因爲js的限制沒法檢測到,實際編程中要特別注意,不然就會感受碰到了一個玄學bug。。。

  1. 數組。

    1. 利用索引來直接設置數組元素。如 this.todos[index] = "B"。解決方案是:Vue.set(this.todos, index, "B"),經過vue的函數設置。
    2. 直接修改數組的長度。如 this.todos.length = 10。替代方案建議使用不可變數據結構。
  2. 對象。vue沒法檢測到對象屬性的添加或刪除。添加的話,可使用Vue.set,刪除的話,可使用不可變數據結構。

最後

此篇博文梳理了vue的基本特性,可是對於vue最重量級的功能----組件化,沒有涉及到。對於組件化的相關梳理留到下篇博文梳理。

注:該文於2018-04-09撰寫於個人github靜態頁博客,現同步到個人segmentfault來。

相關文章
相關標籤/搜索