典型 MVVM 前端框架 Vue

1、引入

   <script src="http://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
    <script src="https://unpkg.com/vue"></script>

2、簡單指令

一、起步文本插值javascript

 <div id="app">
      {{message}}
  </div>
  var app=new Vue({
     el: "#app",
     data: {
        message:"hello"
     }
  })

Vue.js 的核心是一個容許採用簡潔的模板語法來聲明式的將數據渲染進 DOM 的系統: 如今數據和 DOM 已經被綁定在一塊兒,全部的元素都是響應式的。css

二、聲明式渲染html

 <div id="app-2">
    <span v-bind:title="message">
      鼠標懸停幾秒鐘查看此處動態綁定的提示信息!
    </span>
  </div>
  
  var app2=new Vue({
    el:'#app-2',
    data:{
      message:'頁面加載於'+new Date().toLocaleString()
    }
  })

指令帶有前綴 v-,以表示它們是 Vue 提供的特殊屬性 它們會在渲染的 DOM 上應用特殊的響應式行爲。簡言之,這裏該指令的做用是:「將這個元素節點的 title 屬性和 Vue 實例message屬性保持一致」。前端

三、條件與循環vue

 <div id="app-3">
    <p v-if="seen">
      如今你看到我了
    </p>
  </div>
  var app3=new Vue({
    el:'#app-3',
    data:{
      seen:true
    }
  })

綁定 DOM 文本到數據,也能夠綁定 DOM 結構到數據java

 <div id="app-4">
    <ol>
      <li v-for="todo in todos">
        {{todo.text}}}
      </li>
    </ol>
  </div>
  var app4=new Vue({
    el:"#app-4",
    data:{
      todos:[
        {text:"學習javascript"},
        {text:"學習Vue"},
        {text:"整個牛項目"}
      ]
    }
  })

v-for 指令能夠綁定數組的數據來渲染一個項目列表node

四、處理用戶輸入webpack

 <div id="app-5">
    <p>{{message}}</p>
    <button v-on:click="reverseMessage">逆轉消息</button>
  </div>

綁定 DOM 文本到數據,也能夠綁定 DOM 結構到數據 沒有觸碰 DOM,DOM 操做都由 Vue 來處理,你編寫的代碼不須要關注底層邏輯。ios

 var app5=new Vue({
    el:"#app-5",
    data:{
      message:"Hello Vue.js"
    },
    methods:{
      reverseMessage:function(){
        this.message=this.message.split('').reverse().join('')
      }
    }
  })

v-model 指令,它能輕鬆實現表單輸入和應用狀態之間的雙向綁定web

<div id="app-6">
  <p>{{message}}</p>
  <input v-model="message">
</div>

3、組件化應用構建

1.組件系統:容許咱們使用小型、獨立和一般可複用的組件構建大型應用。(仔細想一想,幾乎任意類型的應用界面均可以抽象爲一個組件樹)

  • 在 Vue 裏,一個組件本質上是一個擁有預約義選項的一個 Vue 實例,在 Vue 中註冊組件很簡單:

    // 定義名爲 todo-item 的新組件
    Vue.component('todo-item', {
    template: '<li>這是個待辦項</li>'
    })
  • 如今你能夠用它構建另外一個組件模板:

    <ol>
    <!-- 建立一個 todo-item 組件的實例 -->
    <todo-item></todo-item>
    </ol>
  • 可是這樣會爲每一個待辦項渲染一樣的文本,這看起來並不炫酷,咱們應該能將數據從父做用域傳到子組件。讓咱們來修改一下組件的定義,使之可以接受一個屬性:

    Vue.component('todo-item', {
      // todo-item 組件如今接受一個
      // "prop",相似於一個自定義屬性
      // 這個屬性名爲 todo。
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    })
  • 如今,咱們可使用 v-bind 指令將 todo 傳到每個重複的組件中:

    <div id="app-7">
      <ol>
        <!--
          如今咱們爲每一個 todo-item 提供 todo 對象
          todo 對象是變量,即其內容能夠是動態的。
          咱們也須要爲每一個組件提供一個「key」,晚些時候咱們會作個解釋。
        -->
        <todo-item
          v-for="item in groceryList"
          v-bind:todo="item"
          v-bind:key="item.id">
        </todo-item>
      </ol>
    </div>
    Vue.component('todo-item', {
      props: ['todo'],
      template: '<li>{{ todo.text }}</li>'
    })
    var app7 = new Vue({
      el: '#app-7',
      data: {
        groceryList: [
          { id: 0, text: '蔬菜' },
          { id: 1, text: '奶酪' },
          { id: 2, text: '隨便其餘什麼人吃的東西' }
        ]
      }
    })
  • 這只是一個假設的例子,可是咱們已經設法將應用分割成了兩個更小的單元,子單元經過 props 接口實現了與父單元很好的解耦。咱們如今能夠進一步爲咱們的 todo-item 組件實現更復雜的模板和邏輯的改進,而不會影響到父單元。

    使用了組件的應用模板是什麼樣的例子:

     <div id="app">
        <app-nav></app-nav>
        <app-view>
          <app-sidebar></app-sidebar>
          <app-content></app-content>
        </app-view>
      </div>

4、模板語法:

Vue.js 使用了基於 HTML 的模板語法,容許開發者聲明式地將 DOM 綁定至底層 Vue 實例的數據。全部 Vue.js 的模板都是合法的 HTML ,因此能被遵循規範的瀏覽器和 HTML 解析器解析。在底層的實現上,Vue 將模板編譯成虛擬 DOM 渲染函數。結合響應系統,在應用狀態改變時,Vue 可以智能地計算出從新渲染組件的最小代價並應用到 DOM 操做上。若是你熟悉虛擬 DOM 而且偏心 JavaScript 的原始力量,你也能夠不用模板,直接寫渲染 (render) 函數,使用可選的 JSX 語法。

插值

(1) 文本

數據綁定最多見的形式就是使用「Mustache」語法 (雙大括號) 的文本插值:

<span>Message: {{ msg }}</span>

Mustache 標籤將會被替代爲對應數據對象上 msg 屬性的值。不管什麼時候,綁定的數據對象上 msg 屬性發生了改變,插值處的內容都會更新。

經過使用 v-once 指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。但請留心這會影響到該節點上全部的數據綁定

<span v-once>這個將不會改變: {{ msg }}</span>

(2) 原始 HTML

雙大括號會將數據解釋爲普通文本,而非 HTML 代碼。爲了輸出真正的 HTML,你須要使用 v-html 指令:

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

(3) 特性

Mustache 語法不能做用在 HTML 特性上,遇到這種狀況應該使用 v-bind 指令:

<div v-bind:id="dynamicId"></div>

(4) 使用 JavaScript 表達式

迄今爲止,在咱們的模板中,咱們一直都只綁定簡單的屬性鍵值。但實際上,對於全部的數據綁定,Vue.js 都提供了徹底的 JavaScript 表達式支持

{ number + 1 }}
    
{{ ok ? 'YES' : 'NO' }}
    
{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>

模板表達式都被放在沙盒中,只能訪問全局變量的一個白名單,如 Math  Date 。你不該該在模板表達式中試圖訪問用戶定義的全局變量。

(5) 指令 (Directives) 是帶有 v- 前綴的特殊屬性。當表達式的值改變時,將其產生的連帶影響,響應式地做用於 DOM。

<p v-if="seen">如今你看到我了</p>

這裏,v-if 指令將根據表達式 seen 的值的真假來插入/移除元素。

  • 參數 

    一些指令可以接收一個「參數」,在指令名稱以後以冒號表示。例如,v-bind 指令能夠用於響應式地更新 HTML 屬性:

    <a v-bind:href="url">...</a>

    在這裏 href 是參數,告知 v-bind 指令將該元素的 href 屬性與表達式 url 的值綁定。

    另外一個例子是 v-on 指令,它用於監聽 DOM 事件:

    <a v-on:click="doSomething">...</a>
  • 修飾符
    修飾符 (Modifiers) 是以半角句號 . 指明的特殊後綴,用於指出一個指令應該以特殊方式綁定。例如,.prevent 修飾符告訴 v-on 指令對於觸發的事件調用 event.preventDefault()

    <form v-on:submit.prevent="onSubmit">...</form>

    縮寫: 

    • v-bind 縮寫

      <!-- 完整語法 -->
      <a v-bind:href="url">...</a>
      
      <!-- 縮寫 -->
      <a :href="url">...</a>
    • v-on 縮寫

      <!-- 完整語法 -->
      <a v-on:click="doSomething">...</a>
      <!-- 縮寫 -->
      <a @click="doSomething">...</a>

      它們看起來可能與普通的 HTML 略有不一樣,但 : 與 @ 對於特性名來講都是合法字符,在全部支持 Vue.js 的瀏覽器都能被正確地解析。並且,它們不會出如今最終渲染的標記中。

5、計算屬性

模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。例如:

<div id="example">
    {{ message.split('').reverse().join('') }}
</div>

在這個地方,模板再也不是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裏是想要顯示變量 message 的翻轉字符串。當你想要在模板中屢次引用此處的翻轉字符串時,就會更加難以處理。因此,對於任何複雜邏輯,你都應當使用計算屬性。

基礎例子

<div id="example">
    <p>Original message: "{{ message }}"</p>
    <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

var vm = new Vue({
    el: '#example',
    data: {
      message: 'Hello'
    },
    computed: {
      // 計算屬性的 getter
      reversedMessage: function () {
        // `this` 指向 vm 實例
        return this.message.split('').reverse().join('')
      }
    }
})

結果:

Original message: 「Hello」
Computed reversed message: 「olleH」

這裏咱們聲明瞭一個計算屬性 reversedMessage。咱們提供的函數將用做屬性 vm.reversedMessage  getter 函數:

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

你能夠像綁定普通屬性同樣在模板中綁定計算屬性。Vue 知道 vm.reversedMessage 依賴於 vm.message,所以當 vm.message 發生改變時,全部依賴 vm.reversedMessage 的綁定也會更新。並且最妙的是咱們已經以聲明的方式建立了這種依賴關係:計算屬性的 getter 函數是沒有反作用 (side effect) 的,這使它更易於測試和理解。

計算屬性緩存 vs 方法

<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在組件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

咱們能夠將同一函數定義爲一個方法而不是一個計算屬性。兩種方式的最終結果確實是徹底相同的。然而,不一樣的是計算屬性是基於它們的依賴進行緩存的。 

計算屬性只有在它的相關依賴發生改變時纔會從新求值。 這就意味着只要 message 尚未發生改變,屢次訪問 reversedMessage 計算屬性會當即返回以前的計算結果,而沒必要再次執行函數。這也一樣意味着下面的計算屬性將再也不更新,由於 Date.now() 不是響應式依賴:

computed: {
    now: function () {
      return Date.now()
    }
}

相比之下,每當觸發從新渲染時,調用方法將總會再次執行函數。咱們爲何須要緩存?假設咱們有一個性能開銷比較大的的計算屬性 A,它須要遍歷一個巨大的數組並作大量的計算。而後咱們可能有其餘的計算屬性依賴於 A 。若是沒有緩存,咱們將不可避免的屢次執行 A 的 getter!若是你不但願有緩存,請用方法來替代。

計算屬性 vs 偵聽屬性

Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變更:偵聽屬性。 當你有一些數據須要隨着其它數據變更而變更時,你很容易濫用 watch——特別是若是你以前使用過 AngularJS。 然而,一般更好的作法是使用計算屬性而不是命令式的 watch 回調。細想一下這個例子:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代碼是命令式且重複的。將它與計算屬性的版本進行比較:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
..

如今再運行 vm.fullName = ‘John Doe’ 時,setter 會被調用,vm.firstName  vm.lastName 也會相應地被更新。

偵聽器

雖然計算屬性在大多數狀況下更合適,但有時也須要一個自定義的偵聽器。 這就是爲何 Vue 經過 watch 選項提供了一個更通用的方法,來響應數據的變化。

當須要在數據變化時執行異步或開銷較大的操做時,這個方式是最有用的。

<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
  <meta charset="UTF-8">
  <title>api</title>
  <link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
  <script src="http://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
  <script src="https://unpkg.com/vue"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
        
</head>
<body>
  <div id="watch-example">
    <p>
      Ask a yes/no question:
      <input v-model="question">
    </p>
    <p>{{ answer }}</p>
  </div>
</body>
<script>
  var watchExampleVM = new Vue({
    el: '#watch-example',
    data: {
      question: '',
      answer: 'I cannot give you an answer until you ask a question!'
    },
    watch: {
      // 若是 `question` 發生改變,這個函數就會運行
      question: function (newQuestion) {
        this.answer = 'Waiting for you to stop typing...'
        this.getAnswer()
      }
    },
    methods: {
      // `_.debounce` 是一個經過 Lodash 限制操做頻率的函數。
      // 在這個例子中,咱們但願限制訪問 yesno.wtf/api 的頻率
      // AJAX 請求直到用戶輸入完畢纔會發出。想要了解更多關於
      // `_.debounce` 函數 (及其近親 `_.throttle`) 的知識,
      // 請參考:https://lodash.com/docs#debounce
      getAnswer: _.debounce(
        function () {
          if (this.question.indexOf('?') === -1) {
              this.answer = 'Questions usually contain a question mark. ;-)'
              return
          }
          this.answer = 'Thinking...'
          var vm = this
          axios.get('https://yesno.wtf/api')
            .then(function (response) {
                vm.answer = _.capitalize(response.data.answer)
            })
            .catch(function (error) {
                vm.answer = 'Error! Could not reach the API. ' + error
            })
        },
        // 這是咱們爲斷定用戶中止輸入等待的毫秒數
        500
      )
    }
  })
</script>
</html>

在這個示例中,使用 watch 選項容許咱們執行異步操做 (訪問一個 API),限制咱們執行該操做的頻率,並在咱們獲得最終結果前,設置中間狀態。這些都是計算屬性沒法作到的。

5、Class 與 Style 綁定

綁定 HTML Class

對象語法

一、咱們能夠傳給 v-bind:class 一個對象,以動態地切換 class:

<div v-bind:class="{ active: isActive }"></div>

上面的語法表示 active 這個 class 存在與否將取決於數據屬性 isActive  truthiness

二、你能夠在對象中傳入更多屬性來動態切換多個 class。此外,v-bind:class指令也能夠與普通的 class 屬性共存。當有以下模板:

<div class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
和以下 data:
data: {
    isActive: true,
    hasError: false
}

結果渲染爲:

當 isActive 或者 hasError 變化時,class 列表將相應地更新。例如,若是 hasError 的值爲 true,class 列表將變爲 「static active text-danger」。

三、綁定的數據對象沒必要內聯定義在模板裏:

<div v-bind:class="classObject"></div>
data: {
    classObject: {
      active: true,
      'text-danger': false
    }
}

四、渲染的結果和上面同樣。咱們也能夠在這裏綁定一個返回對象的計算屬性。這是一個經常使用且強大的模式:

 <div v-bind:class="classObject"></div>
  data: {
    isActive: true,
    error: null
  },
  computed: {
    classObject: function () {
      return {
        active: this.isActive && !this.error,
        'text-danger': this.error && this.error.type === 'fatal'
      }
    }
  }

數組語法

一、咱們能夠把一個數組傳給 v-bind:class,以應用一個 class 列表:

 <div v-bind:class="[activeClass, errorClass]"></div>
  data: {
    activeClass: 'active',
    errorClass: 'text-danger'
  }

渲染爲:

<div class="active text-danger"></div>

二、若是你也想根據條件切換列表中的 class,能夠用三元表達式:

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
這樣寫將始終添加 errorClass,可是隻有在 isActive 是 truthy[1] 時才添加 activeClass。

三、不過,當有多個條件 class 時這樣寫有些繁瑣。因此在數組語法中也可使用對象語法:

<div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在組件上

當在一個自定義組件上使用 class 屬性時,這些類將被添加到該組件的根元素上面。這個元素上已經存在的類不會被覆蓋。

例如,若是你聲明瞭這個組件:

Vue.component('my-component', {
    template: '<p class="foo bar">Hi</p>'
})

而後在使用它的時候添加一些 class:

<my-component class="baz boo"></my-component>

HTML 將被渲染爲:

<p class="foo bar baz boo">Hi</p>

綁定內聯樣式

對象語法

v-bind:style 的對象語法十分直觀——看着很是像 CSS,但實際上是一個 JavaScript 對象。CSS 屬性名能夠用駝峯式 (camelCase) 或短橫線分隔 (kebab-case,記得用單引號括起來) 來命名:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

直接綁定到一個樣式對象一般更好,這會讓模板更清晰:

<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

一樣的,對象語法經常結合返回對象的計算屬性使用。

數組語法

v-bind:style 的數組語法能夠將多個樣式對象應用到同一個元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

自動添加前綴 

 v-bind:style 使用須要添加瀏覽器引擎前綴的 CSS 屬性時,如 transform,Vue.js 會自動偵測並添加相應的前綴。

多重值 

從 2.3.0 起你能夠爲 style 綁定中的屬性提供一個包含多個值的數組,經常使用於提供多個帶前綴的值,例如:

<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

這樣寫只會渲染數組中最後一個被瀏覽器支持的值。在本例中,若是瀏覽器支持不帶瀏覽器前綴的 flexbox,那麼就只會渲染 display: flex 

6、條件渲染

v-if

<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

在 元素上使用 v-if 條件渲染分組 由於 v-if 是一個指令,因此必須將它添加到一個元素上。可是若是想切換多個元素呢?此時能夠把一個 元素當作不可見的包裹元素,並在上面使用 v-if。最終的渲染結果將不包含<template>元素。

<template v-if="ok">
    <h1>Title</h1>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</template>

v-else

你可使用 v-else 指令來表示 v-if 的「else 塊」:

<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-else 元素必須緊跟在帶 v-if 或者 v-else-if 的元素的後面,不然它將不會被識別。

v-else-if

v-else-if,顧名思義,充當 v-if 的「else-if 塊」,能夠連續使用:相似於 v-else,v-else-if 也必須緊跟在帶 v-if 或者 v-else-if的元素以後。用 key 管理可複用的元素 ##### Vue 會盡量高效地渲染元素,一般會複用已有元素而不是從頭開始渲染。這麼作除了使 Vue 變得很是快以外,還有其它一些好處。例如,若是你容許用戶在不一樣的登陸方式之間切換:

<template v-if="loginType === 'username'">
    <label>Username</label>
    <input placeholder="Enter your username">
</template>
<template v-else>
    <label>Email</label>
    <input placeholder="Enter your email address">
</template>

那麼在上面的代碼中切換 loginType 將不會清除用戶已經輸入的內容。由於兩個模板使用了相同的元素, 不會被替換掉——僅僅是替換了它的 placeholder

這樣也不老是符合實際需求,因此 Vue 爲你提供了一種方式來表達「這兩個元素是徹底獨立的,不要複用它們」。只需添加一個具備惟一值的 key 屬性便可:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show

另外一個用於根據條件展現元素的選項是 v-show 指令。用法大體同樣:

<h1 v-show="ok">Hello!</h1>

不一樣的是帶有 v-show 的元素始終會被渲染並保留在 DOM 中。v-show 只是簡單地切換元素的 CSS 屬性 display。

v-if 是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。

v-if 也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。 相比之下,v-show 就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。 通常來講,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用 v-show 較好;若是在運行時條件不多改變,則使用 v-if 較好。

v-if 與 v-for 一塊兒使用

當 v-if 與 v-for 一塊兒使用時,v-for 具備比 v-if 更高的優先級。

7、列表渲染

 v-for 把一個數組對應爲一組元素:

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

 v-for 塊中,咱們擁有對父做用域屬性的徹底訪問權限。v-for 還支持一個可選的第二個參數爲當前項的索引。

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

你也能夠用 of 替代 in 做爲分隔符,由於它是最接近 JavaScript 迭代器的語法:

<div v-for="item of items"></div>

對象的 v-for

  • 你能夠用 v-for 經過一個對象的屬性來迭代。

    <ul id="v-for-object" class="demo">
        <li v-for="value in object">
          {{ value }}
        </li>
    </ul>
    new Vue({
    el: '#v-for-object',
    data: {
      object: {
        firstName: 'John',
        lastName: 'Doe',
        age: 30
      }
    }
    })
  • 你也能夠提供第二個的參數爲鍵名:

    <div v-for="(value, key) in object">
    {{ key }}: {{ value }}
    </div>
  • 第三個參數爲索引:

    <div v-for="(value, key, index) in object">
    {{ index }}. {{ key }}: {{ value }}
    </div>`

在遍歷對象時,是按 Object.keys() 的結果遍歷,可是不能保證它的結果在不一樣的 JavaScript 引擎下是一致的。

key

當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它默認用「就地複用」策略。若是數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單複用此處每一個元素,而且確保它在特定索引下顯示已被渲染過的每一個元素。

這個默認的模式是高效的,可是隻適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。

爲了給 Vue 一個提示,以便它能跟蹤每一個節點的身份,從而重用和從新排序現有元素,你須要爲每項提供一個惟一 key 屬性。理想的 key 值是每項都有的且惟一的 id,它的工做方式相似於一個屬性,因此你須要用 v-bind 來綁定動態值 (在這裏使用簡寫):

<div v-for="item in items" :key="item.id">
  <!-- 內容 -->
</div>

建議儘量在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內容很是簡單,或者是刻意依賴默認行爲以獲取性能上的提高。

數組更新檢測

(1) 變異方法

Vue 包含一組觀察數組的變異方法,因此它們也將會觸發視圖更新。這些方法以下:

push() 
pop() 
shift() 
unshift() 
splice() 
sort() 
reverse()

(2) 替換數組

變異方法 (mutation method),顧名思義,會改變被這些方法調用的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如:filter(), concat()  slice() 。這些不會改變原始數組,但老是返回一個新數組。當使用非變異方法時,能夠用新數組替換舊數組:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

(3) 注意事項

因爲 JavaScript 的限制,Vue 不能檢測如下變更的數組:

  • 當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue

  • 當你修改數組的長度時,例如:vm.items.length = newLength

爲了解決第一類問題,如下兩種方式均可以實現和 vm.items[indexOfItem] = newValue 相同的效果,同時也將觸發狀態更新:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
爲了解決第二類問題,你可使用 splice:
example1.items.splice(newLength)

對象更改檢測注意事項

(1) 仍是因爲 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 如今是響應式的

vm.b = 2
// `vm.b` 不是響應式的

對於已經建立的實例,Vue 不能動態添加根級別的響應式屬性。可是,可使用 Vue.set(object, key, value) 方法向嵌套對象添加響應式屬性。例如,對於:

var vm = new Vue({
    data: {
      userProfile: {
        name: 'Anika'
      }
    }
})

你能夠添加一個新的 age 屬性到嵌套的 userProfile 對象:

Vue.set(vm.userProfile, 'age', 27)

你還可使用 vm.$set 實例方法,它只是全局 Vue.set 的別名:

this.$set(this.userProfile, 'age', 27)

(2) 有時你可能須要爲已有對象賦予多個新屬性,好比使用 Object.assign() 或 _.extend()。在這種狀況下,你應該用兩個對象的屬性建立一個新的對象。因此,若是你想添加新的響應式屬性,不要像這樣:

Object.assign(this.userProfile, {
    age: 27,
    favoriteColor: 'Vue Green'
})

你應該這樣作:

this.userProfile = Object.assign({}, this.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

顯示過濾/排序結果

有時,咱們想要顯示一個數組的過濾或排序副本,而不實際改變或重置原始數據。在這種狀況下,能夠建立返回過濾或排序數組的計算屬性。

<li v-for="n in evenNumbers">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

在計算屬性不適用的狀況下 (例如,在嵌套 v-for 循環中) 你可使用一個 method方法:

<li v-for="n in even(numbers)">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

一段取值範圍的 v-for

v-for 也能夠取整數。在這種狀況下,它將重複屢次模板。

<div>
    <span v-for="n in 10">{{ n }} </span>
</div>
v-for on a

相似於 v-if,你也能夠利用帶有 v-for 的 渲染多個元素。好比:

<ul>
 <template v-for="item in items">
  <li>{{ item.msg }}</li>
  <li class="divider"></li>
 </template>
</ul>
v-for with v-if

當它們處於同一節點,v-for 的優先級比 v-if 更高,這意味着 v-if 將分別重複運行於每一個 v-for 循環中。當你想爲僅有的一些項渲染節點時,這種優先級的機制會十分有用,以下:

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

上面的代碼只傳遞了未 complete  todos。 而若是你的目的是有條件地跳過循環的執行,那麼能夠將 v-if 置於外層元素 (或 )上。如:

<ul v-if="todos.length">
    <li v-for="todo in todos">
      {{ todo }}
    </li>
</ul>
<p v-else>No todos left!</p>

一個組件的 v-for

在自定義組件裏,你能夠像任何普通元素同樣用 v-for 

<my-component v-for="item in items" :key="item.id"></my-component>
2.2.0+ 的版本里,當在組件中使用 v-for 時,key 如今是必須的。然而,任何數據都不會被自動傳遞到組件裏,由於組件有本身獨立的做用域。爲了把迭代數據傳遞到組件裏,咱們要用 props 
 <my-component
    v-for="(item, index) in items"
    v-bind:item="item"
    v-bind:index="index"
    v-bind:key="item.id"
  ></my-component>

不自動將 item 注入到組件裏的緣由是,這會使得組件與 v-for 的運做緊密耦合。明確組件數據的來源可以使組件在其餘場合重複使用。 下面是一個簡單的 todo list的完整例子:

<div id="todo-list-example">
    <input
      v-model="newTodoText"
      v-on:keyup.enter="addNewTodo"
      placeholder="Add a todo"
    >
<ul>
  <li
    is="todo-item"
    v-for="(todo, index) in todos"
    v-bind:key="todo.id"
    v-bind:title="todo.title"
    v-on:remove="todos.splice(index, 1)"
  ></li>
</ul>
</div>

注意這裏的 is=」todo-item」 屬性。這種作法在使用 DOM 模板時是十分必要的,由於在ul元素內只有li元素會被看做有效內容。這樣作實現的效果與 相同,可是能夠避開一些潛在的瀏覽器解析錯誤。查看 DOM 模板解析說明 來了解更多信息。

Vue.component('todo-item', {
template: '\
  <li>\
    {{ title }}\
    <button v-on:click="$emit(\'remove\')">X</button>\
  </li>\
',
props: ['title']
})

new Vue({
el: '#todo-list-example',
data: {
  newTodoText: '',
  todos: [
    {
      id: 1,
      title: 'Do the dishes',
    },
    {
      id: 2,
      title: 'Take out the trash',
    },
    {
      id: 3,
      title: 'Mow the lawn'
    }
  ],
  nextTodoId: 4
},
methods: {
  addNewTodo: function () {
    this.todos.push({
      id: this.nextTodoId++,
      title: this.newTodoText
    })
    this.newTodoText = ''
  }
}
})

8、監聽事件

能夠用 v-on 指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼。

<div id="example-1">
    <button v-on:click="counter += 1">Add 1</button>
    <p>The button above has been clicked {{ counter }} times.</p>
</div>

var example1 = new Vue({
el: '#example-1',
    data: {
      counter: 0
    }
})

事件處理方法

然而許多事件處理邏輯會更爲複雜,因此直接把 JavaScript 代碼寫在 v-on 指令中是不可行的。所以 v-on 還能夠接收一個須要調用的方法名稱。

<div id="example-2">
    <!-- `greet` 是在下面定義的方法名 -->
    <button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
  name: 'Vue.js'
},
// 在 `methods` 對象中定義方法
methods: {
  greet: function (event) {
    // `this` 在方法裏指向當前 Vue 實例
    alert('Hello ' + this.name + '!')
    // `event` 是原生 DOM 事件
    if (event) {
      alert(event.target.tagName)
    }
  }
}
})

  // 也能夠用 `JavaScript` 直接調用方法
  example2.greet() // => 'Hello Vue.js!'

內聯處理器中的方法

(1)除了直接綁定到一個方法,也能夠在內聯 JavaScript 語句中調用方法:

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})

(2)有時也須要在內聯語句處理器中訪問原始的 DOM 事件。能夠用特殊變量 $event 把它傳入方法:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
// ...
methods: {
  warn: function (message, event) {
    // 如今咱們能夠訪問原生事件對象
    if (event) event.preventDefault()
    alert(message)
  }
}

事件修飾符

在事件處理程序中調用 event.preventDefault()  event.stopPropagation() 是很是常見的需求。儘管咱們能夠在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。爲了解決這個問題,Vue.js  v-on 提供了事件修飾符。以前提過,修飾符是由點開頭的指令後綴來表示的。

.stop
.prevent
.capture
.self
.once
<!-- 阻止單擊事件繼續傳播 -->
<a v-on:click.stop="doThis"></a>
    
<!-- 提交事件再也不重載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>
    
<!-- 修飾符能夠串聯 -->
<a v-on:click.stop.prevent="doThat"></a>
    
<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>
    
<!-- 添加事件監聽器時使用事件捕獲模式 -->
<!-- 即內部元素觸發的事件先在此到處理,而後才交由內部元素自身進行處理 -->
<div v-on:click.capture="doThis">...</div>
    
<!-- 只當在 event.target 是當前元素自身時觸發處理函數 -->
<!-- 即事件不是從內部元素觸發的 -->
<div v-on:click.self="doThat">...</div>    

使用修飾符時,順序很重要;相應的代碼會以一樣的順序產生。所以,用 @click.prevent.self 會阻止全部的點擊,而 @click.self.prevent 只會阻止對元素自身的點擊。

2.1.4 新增
<!-- 點擊事件將只會觸發一次 -->
<a v-on:click.once="doThis"></a>

不像其它只能對原生的 DOM 事件起做用的修飾符,.once 修飾符還能被用到自定義的組件事件上。若是你尚未閱讀關於組件的文檔,如今大可沒必要擔憂。 按鍵修飾符 在監聽鍵盤事件時,咱們常常須要檢查常見的鍵值。Vue 容許爲 v-on 在監聽鍵盤事件時添加按鍵修飾符:

<!-- 只有在 `keyCode` 是 13 時調用 `vm.submit()` -->
<input v-on:keyup.13="submit">
記住全部的 keyCode 比較困難,因此 Vue 爲最經常使用的按鍵提供了別名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
    
<!-- 縮寫語法 -->
<input @keyup.enter="submit">
.enter
.tab
.delete (捕獲「刪除」和「退格」鍵)
.esc
.space
.up
.down
.left
.right

能夠經過全局 config.keyCodes 對象自定義按鍵修飾符別名:

// 可使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

自動匹配按鍵修飾符

  • 你也可直接將 KeyboardEvent.key 暴露的任意有效按鍵名轉換爲 kebab-case 來做爲修飾符

<input @keyup.page-down="onPageDown">

在上面的例子中,處理函數僅在 $event.key === ‘PageDown’ 時被調用。 有一些按鍵 (.esc 以及全部的方向鍵) 在 IE9 中有不一樣的 key 值, 若是你想支持 IE9,它們的內置別名應該是首選。

系統修飾鍵

能夠用以下修飾符來實現僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器。

.ctrl
.alt
.shift
.meta

注意:在 Mac 系統鍵盤上,meta 對應 command 鍵 (⌘)。在 Windows系統鍵盤 meta 對應 Windows 徽標鍵 (⊞)。在 Sun 操做系統鍵盤上,meta 對應實心寶石鍵 (◆)。在其餘特定鍵盤上,尤爲在MIT  Lisp機器的鍵盤、以及其後繼產品,好比 Knight 鍵盤、space-cadet 鍵盤,meta 被標記爲「META」。在 Symbolics 鍵盤上,meta被標記爲「META」或者「Meta」

<!-- Alt + C -->
<input @keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

請注意修飾鍵與常規按鍵不一樣,在和 keyup 事件一塊兒用時,事件觸發時修飾鍵必須處於按下狀態。換句話說,只有在按住 ctrl 的狀況下釋放其它按鍵,才能觸發keyup.ctrl。而單單釋放 ctrl 也不會觸發事件。

.exact 修飾符

.exact 修飾符容許你控制由精確的系統修飾符組合觸發的事件

<!-- 即便 Alt 或 Shift 被一同按下時也會觸發 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的時候才觸發 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 沒有任何系統修飾符被按下的時候才觸發 -->
<button @click.exact="onClick">A</button>

鼠標按鈕修飾符

.left
.right
.middle

這些修飾符會限制處理函數僅響應特定的鼠標按鈕。

9、組件

組件 (Component)  Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠表現爲用 is 特性進行了擴展的原生 HTML 元素。 全部的 Vue 組件同時也都是 Vue 的實例,因此可接受相同的選項對象 (除了一些根級特有的選項) 並提供相同的生命週期鉤子。

使用組件

(1)全局註冊

咱們已經知道,能夠經過如下方式建立一個 Vue 實例:

new Vue({
  el: '#some-element',
  // 選項
})

要註冊一個全局組件,可使用 Vue.component(tagName, options)。例如:

Vue.component('my-component', {
  // 選項
})

組件在註冊以後,即可以做爲自定義元素 <my-component></my-component> 在一個實例的模板中使用。注意確保在初始化根實例以前註冊組件: 實例:

<div id="example">
  <my-component></my-component>
</div>
// 註冊
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 建立根實例
new Vue({
  el: '#example'
})

渲染爲:

<div id="example">
  <div>A custom component!</div>
</div>

(2)局部註冊

你沒必要把每一個組件都註冊到全局。你能夠經過某個 Vue 實例/組件的實例選項 components 註冊僅在其做用域中可用的組件:

var Child = {
  template: '<div>A custom component!</div>'
}

new Vue({
  // ...
  components: {
    // <my-component> 將只在父組件模板中可用
    'my-component': Child
  }
})

這種封裝也適用於其它可註冊的 Vue 功能,好比指令。

(3)DOM 模板解析注意事項

當使用 DOM 做爲模板時 (例如,使用 el 選項來把 Vue 實例掛載到一個已有內容的元素上),你會受到 HTML 自己的一些限制,由於 Vue 只有在瀏覽器解析、規範化模板以後才能獲取其內容。尤爲要注意,像<ul>、<ol>、<table>  <select>這樣的元素裏容許包含的元素有限制,而另外一些像<li>、<tr>  <option>這樣的元素只能出如今某些特定元素的內部。

在自定義組件中使用這些受限制的元素時會致使一些問題,例如:

<table>
  <my-row>...</my-row>
</table>

自定義組件 會被看成無效的內容,所以會致使錯誤的渲染結果。變通的方案是使用特殊的 is 特性:

<table>
  <tr is="my-row"></tr>
</table>

在模塊系統中局部註冊

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
    
export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

Prop*

(1)使用 Prop 傳遞數據

組件實例的做用域是孤立的。這意味着不能 (也不該該) 在子組件的模板內直接引用父組件的數據。父組件的數據須要經過 prop 才能下發到子組件中。 子組件要顯式地用 props 選項聲明它預期的數據:

Vue.component('child', {
  // 聲明 props
  props: ['message'],
  // 就像 data 同樣,prop 也能夠在模板中使用
  // 一樣也能夠在 vm 實例中經過 this.message 來使用
  template: '<span>{{ message }}</span>'
})

而後咱們能夠這樣向它傳入一個普通字符串:

<child message="hello!"></child>

(2)camelCase vs. kebab-case

HTML 特性是不區分大小寫的。因此,當使用的不是字符串模板時,camelCase (駝峯式命名) 的 prop 須要轉換爲相對應的 kebab-case (短橫線分隔式命名):

Vue.component('child', {
  // 在 JavaScript 中使用 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})
<!-- 在 HTML 中使用 kebab-case -->
<child my-message="hello!"></child>

(3)動態 Prop

與綁定到任何普通的 HTML 特性相相似,咱們能夠用 v-bind 來動態地將 prop 綁定到父組件的數據。每當父組件的數據變化時,該變化也會傳導給子組件:

<div>
    <input v-model="parentMsg">
    <br>
    <child v-bind:my-message="parentMsg"></child>
</div>

你也可使用 v-bind 的縮寫語法:

<child :my-message="parentMsg"></child>

單向數據流:

全部的 prop 都使得其父子 prop 之間造成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,可是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而致使你的應用的數據流向難以理解

1. 這個 prop 用來傳遞一個初始值;這個子組件接下來但願將其做爲一個本地的 prop 數據來使用

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

2. 這個 prop 以一種原始的值傳入且須要進行轉換。在這種狀況下,最好使用這個 prop 的值來定義一個計算屬性:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

prop驗證:

Vue.component('my-component', {
  props: {
    // 基礎的類型檢查 (`null` 匹配任何類型)
    propA: Number,
    // 多個可能的類型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 帶有默認值的數字
    propD: {
      type: Number,
      default: 100
    },
    // 帶有默認值的對象
    propE: {
      type: Object,
      // 對象或數組且必定會從一個工廠函數返回默認值
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗證函數
    propF: {
      validator: function (value) {
        // 這個值必須匹配下列字符串中的一個
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

Prop類型檢查:String、Number、Boolean、Function、Object、Array、Symbol

額外的,type 還能夠是一個自定義的構造函數,而且經過 instanceof 來進行檢查確認。例如,給定下列現成的構造函數:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

你可使用:

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

來驗證 author prop 的值是不是經過 new Person 建立的。

生命週期函數:

 beforeCreate(建立前),

  created(建立後),

  beforeMount(載入前),

  mounted(載入後),

  beforeUpdate(更新前),

  updated(更新後),

  beforeDestroy(銷燬前),

  destroyed(銷燬後)

不得不提的一個官方腳手架:vue-cli(需瞭解node jswebpack npm等)

命令行:

1.npm install -g vue-cli

2.vue init webpack my-project

3.cd my project

4.npm install

5.npm run dev

若是報錯:Module build failed: Error: No parser and no file path given,couldn't infer a parser.

解決方法:

npm i prettier@~1.12.0

而後從新啓動:

npm run dev

僅幾行命令即快速構建了一個web單頁應用,涵蓋vue、vue-router、axios、webpack等.

官方技術文檔:

  • vue.js官方文檔及API

  • vue-cli

  • 路由管理器vue-router



(end)



----------------------------------------


轉載請註明出處 


長按二維碼或搜索 fewelife 關注咱們哦


本文分享自微信公衆號 - 雲前端(fewelife)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索