《Vue.js 實戰(zhàn)》基礎(chǔ)篇(上)

本章內(nèi)容:認識 Vue.js、數(shù)據(jù)綁定芥颈、計算屬性、內(nèi)置指令

一赚抡、初始 Vue.js

1.1 Vue.js 是什么

簡單小巧的核心爬坑,漸進式技術(shù)棧,足以應(yīng)付任何規(guī)模的應(yīng)用涂臣。
簡單小巧 指Vue.js 壓縮后大小僅 17KB
所謂漸進式(Progressive)就是你可以一步一步盾计、有階段性地來使用 Vue.js

Vue.js 提供了現(xiàn)代 Web 開發(fā)中常見的高級功能,比如:

  • 解耦視圖與數(shù)據(jù)
  • 可復(fù)用的組件
  • 前端路由
  • 狀態(tài)管理
  • 虛擬 DOM(Virtual DOM)
1.1.1 MVVM 模式

Vue.js使用 MVVM (Model-View-ViewModel)模式赁遗。MVVM 模式是由經(jīng)典的軟件架構(gòu)MVC 衍生而來的署辉。當 View(視圖層)變化時,會自動更新到 ViewModel(視圖模型)吼和,反之亦然

MVVM 關(guān)系

1.1.2涨薪、Vue.js 有什么不同

與傳統(tǒng)開發(fā)模式相比,Vue.js 通過 MVVM 的模式拆分為 視圖數(shù)據(jù) 兩部分炫乓,并將其分離刚夺,因此,你只需要關(guān)系數(shù)據(jù)即可末捣,DOM的 事情 Vue 會自動幫你搞定侠姑。

1.2、如何使用 Vue.js

1.2.1箩做、傳統(tǒng)的前端開發(fā)模式

前些年稱之為“萬金油” 的一套技術(shù)棧:

jQuery + RequireJS(SeaJS) + artTemplate(doT) + Gullp(Grunt)

這套技術(shù)棧 以 jQuery 為核心莽红,RequireJS 或 SeaJS 進行模塊開發(fā);加上輕量級的前端模板artTemplate 或 doT邦邦;最后使用自動構(gòu)建工具(如 Gulp)壓縮代碼

1.2.2安吁、Vue.js 的開發(fā)模式

Vue.js 是一個漸進式框架,根據(jù)項目需求的不同燃辖,你可以選擇從不同的維度來使用它鬼店。

  • 如果只是想體驗 Vue.js 或 開發(fā)簡單的 HTML5頁面 或 小應(yīng)用,可以直接通過 <script> 加載 CDN 文件黔龟。

  • 對于一些業(yè)務(wù)邏輯復(fù)雜妇智,對前端工程有要求的項目,可以使用 Vue 單文件的形式配合 webpack 使用氏身,必要時還會用到 vuex 狀態(tài)管理巍棱,vue-router 管理路由。

二蛋欣、數(shù)據(jù)綁定 和 第一個Vue應(yīng)用

學(xué)習任何一種框架航徙,都應(yīng)該從 寫 Hello World 應(yīng)用 開始

<body>
  
  <div id="app">

    <input type="text" v-model="user">

    <h1>Hello {{user}}</h1>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
  
      const app = new Vue({
        el: '#app',
        data: {
          user: ''
        }
      })
  </script>
</body>

在輸入框輸入的內(nèi)容會實時展示在 頁面的 h1 標簽內(nèi),雖然這是一段簡單的代碼陷虎,卻展示了 Vue.js 最核心的功能:數(shù)據(jù)的雙向綁定

2.1捉偏、Vue 實例 與 數(shù)據(jù)綁定

2.1.1倒得、實例 與 數(shù)據(jù)

Vue.js 應(yīng)用的創(chuàng)建很簡單,通過 構(gòu)造函數(shù) Vue 就可以 創(chuàng)建一個 Vue 實例夭禽,并啟動 Vue 應(yīng)用霞掺。

const app = new Vue({
  // options
})

app 就代表了這個 Vue 實例

實例選項

首先,必不可少的一個選項就是 el讹躯。el 用于指定一個頁面中已經(jīng)存在的 DOM
元素來掛載 Vue 實例菩彬,他可以是 HTMLElement,也可以是 CSS 選擇器潮梯。

const app1 = new Vue({
  el: document.getElementById('app')
})

const app2 = new Vue({
  el: '#app'
})

掛載成功后骗灶,我們可以通過 app.$el 來訪問該元素

data 選項,可以聲明應(yīng)用中需要雙向綁定的數(shù)據(jù)秉馏,建議所有會用到的數(shù)據(jù)都預(yù)先在 data 內(nèi)聲明耙旦,這樣不至于將數(shù)據(jù)散落在業(yè)務(wù)邏輯中,難以維護萝究。
Vue 實例本身也代理了 data 對象里的所有屬性免都,所以可以這樣訪問:

var app = new Vue({
  el: '#app',
  data: {
    a: 1
  }
})

console.log(app.a) // 1

處理顯式地聲明數(shù)據(jù)外,也可以指定一個已有的變量帆竹,并且他們之間默認建立了雙向數(shù)據(jù)綁定绕娘,當修改其中任意一個時,另一個也會一起改變栽连。

2.1.2险领、生命周期

每個 Vue 實例創(chuàng)建時,都會經(jīng)歷一系列的初始化過程秒紧,同時也會調(diào)用相應(yīng)的生命周期鉤子绢陌,我們可以利用這些鉤子,在合適的時機執(zhí)行我們的業(yè)務(wù)邏輯熔恢。
比較常用的有:

  • created:實例創(chuàng)建完成后調(diào)用脐湾,此階段完成了數(shù)據(jù)的觀測等,但尚未掛載绩聘,$el 還不可用沥割。需要初始化處理一些數(shù)據(jù)時比較有用
  • mounted:el 掛載到實例上后調(diào)用耗啦,一般我們的第一個業(yè)務(wù)邏輯在這里開始
  • beforeDestroy:實例銷毀之前調(diào)用凿菩。主要解綁一些使用 addEventListener 監(jiān)聽的事件等。
    詳細的聲明周期函數(shù)帜讲,會在后面章節(jié)講解
    這些鉤子 與 eldata 類似衅谷,也是作為選項寫入 Vue 實例內(nèi),并且 鉤子的 this 指向的是調(diào)用它的 Vue 實例:
var app = new Vue({
  el: '#app',
  data: {
    a: 2
  },
  created: function() {
    console.log(this.a) // 2
  },
  mouted: function() {
    console.log(this.$el) // <div id="app"></div>
  }
})
2.1.3似将、差值與表達式

使用 雙大括號(Mustache 語法)“{{}}” 是最基本的文本插值方法获黔,它會自動將我們雙向綁定的數(shù)據(jù)實時顯示出來蚀苛。

<body>
  
  <div id="app">
    <h1>{{ book }}</h1>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
  
      const app = new Vue({
        el: '#app',
        data: {
          book: '《云邊有個小賣部》'
        }
      })
  </script>
</body>

如果有時候想要輸出 HTML,而不是將數(shù)據(jù)解釋后的存文本玷氏,可以使用 v-html

<body>
  
  <div id="app">
    <span v-html="bookLink"></span>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
  
      const app = new Vue({
        el: '#app',
        data: {
          bookLink: '<a href="#">《云邊有個小賣部》</a>'
        }
      })
  </script>
</body>

這里要注意堵未,如果將用戶產(chǎn)生的內(nèi)容 使用 v-html 輸出后,有可能會導(dǎo)致 XSS 攻擊盏触,所以要在服務(wù)端對用戶提交的內(nèi)容進行處理渗蟹,一般可將 尖括號 "<>" 轉(zhuǎn)義


如果想顯示 {{}} 標簽而不進行替換,可以使用 v-pre赞辩,即可跳過這個元素和它的子元素的編譯過程雌芽。

<span v-pre> {{ 這里的內(nèi)容不會被編譯 }} <span>

{{}} 中,處理簡單的綁定屬性外辨嗽,還可以使用 JavaScript 表單是進行簡單的運算世落、三元運算等,例如:

<div id="app">
  {{num / 10}}
  {{ isOK ? '確定' : '取消' }}
  {{ text.split(',').reverse().join(',') }}
</div>
<script>
  var app = new Vue({
    el: '#app',
    data: {
      number: 100,
      isOK: false,
      text: '123,456'
    }
  })
</script>

Vue.js 只支持單個表達式糟需,不支持語句和流程控制屉佳。另外,在表達式中篮灼,不能使用用戶自定義的全局變量忘古,只能使用 Vue 白名單內(nèi)的全局變量,例如 Math 和 Date

2.1.4诅诱、過濾器

Vue.js 支持在 {{}} 插值的尾部添加一個 管道符 “ | ” 來對數(shù)據(jù)進行過濾髓堪,經(jīng)曾用于格式化文本,比如字母全部大寫娘荡,貨幣千位使用逗號分割等干旁。過濾的規(guī)則是自定義的,通過給 Vue 實例添加 filters 來實現(xiàn)
時間案例

<body>
  
  <div id="app">
    {{ date | formatDate}}
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
  
      const app = new Vue({
        el: '#app',
        data: {
          date: new Date()
        },
        mounted() { // 實例掛載后初始化義時器
          this.timer = setInterval(() => this.date = new Date(), 1000)
        },
        beforeDestroy () { // 實例銷毀時 情況定時器
          if (this.timer) clearInterval(this.timer)
        },
        filters: { // 定義過濾器
          formatDate(value) { // value 指需要進行過濾的數(shù)據(jù),  管道符前面的數(shù)據(jù)
            const date = new Date(value)
            const year = date.getFullYear()
            const month = (date.getMonth() + 1).toString().padStart(2, 0)
            const day = date.getDate().toString().padStart(2, 0)
            const hours = date.getDate().toString().padStart(2, 0)
            const min = date.getMinutes().toString().padStart(2, 0)
            const sec = date.getSeconds().toString().padStart(2, 0)

            return `${year}-${month}-${day} ${hours}:${min}:${sec}`
          },
        }
      })
  </script>
</body>

過濾器也可以串聯(lián)炮沐,而且可以接收參數(shù)

<!-- 串聯(lián) -->
{{ message | filterA | filterB }}

<!-- 接收參數(shù) -->
{{ message | filter('arg1', 'arg2') }}

這里的 arg1 和 arg2 將分別傳給過濾器的第二個 和 第三個參數(shù)争群,因為第一個是數(shù)據(jù)本身。

2.2大年、指令

指令(Directives)是 Vue.js 模板中最常用的 一項功能换薄,它帶有前綴的 v-,Vue,js內(nèi)置了很多指令翔试,后面會詳細講到轻要,在此之前,你需要先知道 v-bindv-on

v-bind 的基本用途是動態(tài)更新 HTML 元素上的屬性垦缅,比如 id冲泥、class 等
例如下面示例:

<body>
  
  <div id="app">
    <a v-bind:href="url">鏈接</a>
    <img v-bind:src="imgUrl" alt="">
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        url: 'https://www.baidu.com',
        imgUrl: 'https://wallhaven.cc/w/8396gk'
      }
    })
  </script>
</body>

v-on,用來綁定事件監(jiān)聽,這樣我們就可以實現(xiàn)一些交互

<body>
  
  <div id="app">
    <p v-if="isShow">The End of the F***ing World</p>
    <button v-on:click="clickHandle">toggle</button>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isShow: true
      },
      methods: {
        clickHandle() {
          this.isShow = !this.isShow
        }
      }
    })
  </script>
</body>

表達式可以是一個方法名凡恍,這些方法都寫在 Vue 實例的 methods 屬性內(nèi)志秃,并且是函數(shù)的形式,函數(shù)內(nèi)的this 指向的是當前 Vue 實例本身嚼酝,因此可以直接使用 this.xxx 的形式來訪問或修改數(shù)據(jù)

表達式除了方法名浮还,也可以直接是一個內(nèi)聯(lián)語句。

  <div id="app">
    <p v-if="isShow">The End of the F***ing World</p>
    <button v-on:click="this.isShow = !this.isShow">toggle</button>
  </div>

如果綁定的事件要處理復(fù)雜的業(yè)務(wù)邏輯闽巩,建議還是在 methods 里聲明一個方法碑定,這樣可讀性更強也好維護。

Vue.js 將 methods 里的方法也代理了又官,所以也可以像 訪問 Vue 數(shù)據(jù)那樣來調(diào)用方法延刘。在外部 也可以通過 “實例.方法名” 調(diào)用內(nèi)部的方法

2.3、語法糖

語法糖是指在不影響功能的情況下六敬,添加某種方法實現(xiàn)同樣的效果碘赖,從而方便程序開發(fā)。

Vue.js 的 v-bind 和 v-on 指令都提供了語法糖外构,

v-bind普泡,可以省略 v-bind,直接寫一個冒號 “:”

<a :href="url"> 連接 </a>

<img :src="imgUrl" />

v-on 可以直接用 “@” 來縮寫

<button @click="handleClick">toggle</button>

三、計算屬性

3.1、什么是計算屬性

在模板中雙向綁定一些數(shù)據(jù)或表達式蔗候,如果過長,或邏輯更為復(fù)雜時砰嘁,就會變得臃腫甚至難以閱讀和維護,比如:

 {{ text.split(',').reverse().join(',') }}

當遇到復(fù)雜的邏輯是應(yīng)該使用 計算屬性勘究。

<body>
  
  <div id="app">
    {{ reversedText}}
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        text: '123,456'
      },
      computed: {
        reversedText() {
          // 這里的this 指向的是當前啊的 vue 實例
          return this.text.split(',').reverse().join(',')
        }
      }
    })
  </script>
</body>

3.2矮湘、計算屬性用法

計算屬性只要其中任意數(shù)據(jù)變化,計算屬性就會重新執(zhí)行口糕,視圖也會更新缅阳。

<body>
  
  <div id="app">
    總價:{{ prices }}
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        package: [
          {
            name: '桔梗',
            price: 10,
            count: 2
          },
          {
            name: '荼蘼',
            price: 11,
            count: 10,
          },
          {
            name: '滿天星',
            price: 20,
            count: 15
          }
        ]
      },
      computed: {
        prices() {
          var prices = 0

          for (var i = 0, len = this.package.length; i < len; i++) {
            var item = this.package[i]
            prices += item.price * item.count
          }

          return prices
        }
      }
    })
  </script>
</body>

如上 當 package 中的商品有任何變化時,計算屬性 prices 就會自動更新

每一個計算屬性都包含一個 getter 和 一個 setter景描。在你需要時十办,也可以提供一個 setter 函數(shù),當手動修改計算屬性的值就像修改一個普通數(shù)據(jù)那樣時超棺,就會觸發(fā) setter 函數(shù)向族,執(zhí)行一些自定義操作。

<body>
  
  <div id="app">
    {{ fullName }}
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        firstName: 'Don',
        lastName: 'Shirley'
      },
      computed: {
        fullName: {
          // getter 用于讀取
          get() {
            return this.firstName + ' ' + this.lastName
          },
          // setter 寫入時觸發(fā)
          set(newVal) {
            var names = newVal.split(' ')
            this.firstName = names[0]
            this.lastName = name[names.length -1]
          }
        }
      }
    })
  </script>
</body>

絕大多數(shù)情況下说搅,我們只會用默認的 getter 方法來讀取一個計算屬性炸枣,在業(yè)務(wù)中很少用到 setter虏等,所以在聲明一個計算屬性時弄唧,可以直接使用 默認的寫法适肠,不必將 getter 和 setter 都聲明。

計算屬性還有兩個很實用的小技巧容易被忽略:

  • 一是計算屬性可以依賴其他計算屬性候引;
  • 而是計算屬性不僅可以依賴當前 Vue 實例的數(shù)據(jù)侯养,還可以依賴其他實例的數(shù)據(jù)。

3.3澄干、計算屬性緩存

調(diào)用 methods 里的方法也可以 與 計算屬性起到同樣的作用逛揩。
本小節(jié)的第一個例子,使用 methods改寫:

<body>
  
  <div id="app">
    <!-- 注意麸俘,這里的 reversedText 是方法辩稽,所以要帶 () -->
    {{ reversedText() }}
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        text: '123,456'
      },
      methods: {
        reversedText() {
          // 這里的this 指向的是當前啊的 vue 實例
          return this.text.split(',').reverse().join(',')
        }
      }
    })
  </script>
</body>

使用方法還可以接受參數(shù),使用起來更靈活从媚,那么為什么還需要計算屬性呢逞泄?原因就是 計算屬性是基于它的依賴緩存的。一個計算屬性所依賴的數(shù)據(jù)發(fā)生變化時拜效,它才會重新取值喷众。所以 text 只要不改變,計算屬性也就不更新紧憾。
使用 計算屬性 還是 methods 取決于你是否需要緩存到千,當遍歷大數(shù)組和做大量計算時,應(yīng)該使用計算屬性赴穗,除非你不希望得到緩存憔四。

四、v-bind 及 class 與 style 綁定

4.1般眉、了解 v-bind 指令

前面已經(jīng)介紹了 v-bind 指令的基本用法及語法糖加矛,它的主要用處是動態(tài)更新 HTML 元素上的屬性。在數(shù)據(jù)綁定中煤篙,最常見的兩個需求就是 元素的樣式名稱 class 和 內(nèi)聯(lián)樣式 style 的動態(tài)綁定

4.2斟览、綁定 class 的幾種方式

4.2.1、對象語法

給 v-bind:class 設(shè)置 一個對象辑奈,可以動態(tài)地切換 class
例如

<body>
  
  <div id="app">
    <div :class="{'active': isActive}"></div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isActive: false
      }
    })
  </script>
</body>

對象中也可以傳入多個屬性苛茂,來動態(tài)切換 class。:class 與 普通 class 共存

<body>
  
  <div id="app">
    <div :class="{'active': isActive, 'error': isError}"></div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isActive: false,
        isError: true
      }
    })
  </script>
</body>

:class 內(nèi)的表達式每項為真時鸠窗,對應(yīng)的類名就會加載妓羊。

當 :class 的表達式過長 或 邏輯復(fù)雜時,還可以綁定一個計算屬性稍计,這是一種很友好和常見的用法躁绸,一般當條件多于兩個時,都可以使用 data 或 computed。
例如使用 計算屬性:

<body>
  
  <div id="app">
    <div :class="classes"></div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isActive: true,
        isError: null
      },
      computed: {
        classes() {
          return {
            active: this.isActive && !this.isError,
            'text-fail': this.isError && this.error.type === 'fail'
          }
        }
      }
    })
  </script>
</body>

除了計算屬性净刮,你也可以直接綁定一個 Object 類型的數(shù)據(jù)剥哑,或者使用 雷士計算屬性的 methods

4.2.2、數(shù)組語法

當需要應(yīng)用多個 class 時淹父,可以使用數(shù)組語法株婴。

<body>
  
  <div id="app">
    <div :class="[activeCls, errorCls]"></div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        activeCls: 'active',
        errorCls: null
      }
    })
  </script>
</body>

也可以在數(shù)組中使用 三元表達式。

  <div :class="[activeCls ? 'active-success' : '', errorCls]"></div>

當 class 有多個條件是暑认,這樣寫比較煩瑣困介,可以在數(shù)組語法中使用對象語法:

<body>
  
  <div id="app">
      <div :class="[{'activeCls': isActive}, errorCls]"></div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isActive: true,
        errorCls: null
      }
    })
  </script>
</body>

與對象語法一樣,也可以使用 data蘸际、computed座哩、methods 三種方法。

4.2.3粮彤、在組件上使用

如果直接在 自定義組件上使用 class 或 :class八回,樣式規(guī)則會直接應(yīng)用到這個組件的根元素上,例如聲明一個簡單的組件:

<body>
  
  <div id="app">
      <my-component :class="{'active': isActive}">
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>

    Vue.component('my-component', {
      template: '<p class="article"> some text </p>'
    })
    const app = new Vue({
      el: '#app',
      data: {
        isActive: true,
        errorCls: null
      }
    })
  </script>
</body>

最終組件渲染的結(jié)果為:

<p class="article active"> some text </p>

4.3驾诈、綁定內(nèi)聯(lián)樣式

使用 v-bind:style(:style) 可以給元素綁定內(nèi)聯(lián)樣式缠诅,方法與 :class 類似,也有對象語法和數(shù)組語法乍迄,看起來很像直接在元素上寫 CSS:

<body>
  
  <div id="app">
      <div :style="{'color': color, 'fontSize': fontSize + 'px'}">The End of the F***ing World</div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        color: 'cyan',
        fontSize: 40
      }
    })
  </script>
</body>

大多數(shù)情況下管引,直接寫一長串的樣式不便于閱讀和維護,所以一般 寫在 data 或 computed 里闯两。

<body>
  
  <div id="app">
      <div :style="styles">The End of the F***ing World</div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        styles: {
          color: 'cyan',
          fontSize: '40px'
        }
      }
    })
  </script>
</body>

在實際業(yè)務(wù)中褥伴,:style 的數(shù)組語法并不常用,因為往往可以寫在一個對象里面漾狼;而較為常用的應(yīng)當時計算屬性重慢。
在使用 :style 時, Vue.js 會自動給特殊的 CSS 屬性 名稱添加前綴(weikit逊躁、ms...)似踱。

五、內(nèi)置指令

5.1稽煤、基本指令

5.1.1核芽、v-cloak

v-cloak 不需要表達式,它會在 Vue 實例結(jié)束編譯是從綁定的 HTML 元素上移除酵熙,經(jīng)常和 CSS 的 display: none; 配合使用轧简。

<head>
  <style>
    [v-cloak] {
      display: none;
    }
  </style>
</head>
<body>
  
  <div id="app">
      <span v-cloak>{{ message }}</span>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        message: 'some message'
      }
    })
  </script>
</body>

在一般情況下,v-cloak 是一個解決初始化慢導(dǎo)致頁面閃動的最佳實踐匾二,對于簡單的項目很實用哮独。

5.1.2拳芙、v-once

v-once 也是一個不需要表達式的指令,作用是定義它的元素或組件只渲染一次皮璧,包括元素或組件的所有子節(jié)點舟扎。首次渲染后,不再隨數(shù)據(jù)的變化重新渲染恶导,將被視為靜態(tài)內(nèi)容。

<body>
  
  <div id="app">
      <span v-once>{{ message }}</span>
      <div v-once>
        <span>
          {{ message }}
        </span>
      </div>


      <span>{{ message }}</span>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        message: 'some message'
      }
    })

    app.message = 'no message'
  </script>
</body>
渲染結(jié)果

5.2浸须、條件渲染指令

5.2.1惨寿、v-if 、v-else-if删窒、v-else

與 JavaScript 的條件語句 if裂垦、else、else if 類似肌索,Vue.js 的條件指令可以根據(jù)表達式的值在 DOM 中渲染或銷毀元素/組件蕉拢,例如:

<body>
  
  <div id="app">
     <p v-if="status === 1">當 status 為1時顯示 該行</p>
     <p v-else-if="status === 2">當 status 為2時顯示 該行</p>
     <p v-else>否則顯示該行</p>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        status: 3
      }
    })
  </script>
</body>

v-else-if 要緊跟 v-if, v-else 要跟緊 v-else-if 或 v-if诚亚。
表達式的值為真時晕换,當前元素/組件及所有子節(jié)點被渲染,為假時被移除站宗。

如果一次判斷多個元素闸准,可以在 Vue.js 內(nèi)置的 <template> 元素上使用條件指令,最終渲染的結(jié)果不會包含該元素梢灭。

<body>
  
  <div id="app">
     <template v-if="status === 1">
       <p>message</p>
       <p>message</p>
       <p>message</p>
     </template>
    
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        status: 1
      }
    })
  </script>
</body>

Vue 在渲染元素是夷家,出于效率考慮,會盡可能地復(fù)用已有的元素而非重新渲染敏释。
如下示例:

<body>
  
  <div id="app">
    <template v-if="type === 'name'">
      <label for="username">用戶名:</label> 
        <input type="text" id="username" placeholder="用戶名" /> 
    </template>
    <template v-else>
        <label for="email">郵箱:</label>  
          <input type="email" id="email" placeholder="郵箱" />
    </template>

    <button @click="handleToggle">切換輸入類型</button>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        type: 'name'
      },
      methods: {
        handleToggle() {
          this.type = this.type === 'name' ? 'email' : 'name'
        }
      }
    })
  </script>
</body>

如上代碼库快,輸入內(nèi)容后,點擊切換按鈕钥顽,DOM會發(fā)生改變义屏,但是之前在輸入框鍵入的內(nèi)容并沒有改變,只是替換了 placeholder 的內(nèi)容蜂大,說明 <input> 元素被復(fù)用了湿蛔。

切換前

切換后

如果你不希望這樣做,可以使用 Vue.js 提供的 key 屬性县爬,它可以讓你自己決定是否要復(fù)用元素阳啥,key 的值必須是唯一的,例如:

<!-- ...省略其他代碼 -->
<input type="text" id="username" placeholder="用戶名" key="username-input" /> 
<!-- ...省略其他代碼 -->
<input type="email" id="email" placeholder="郵箱" key="username-email"/>
<!-- ...省略其他代碼 -->

給兩個 <input> 元素都增加 key 后财喳,就不會復(fù)用了察迟,切換類型時鍵入的內(nèi)容也會被刪除斩狱,不過 <label> 元素仍然是被復(fù)用的,因為沒有添加 key 屬性

5.2.2扎瓶、v-show

v-show 的用法 和 v-if 基本一致所踊。只不過 v-show 指令是通過修改元素的 CSS屬性 display。當 v-show 表達式的值為 false 時概荷,元素會隱藏秕岛,查看 DOM 結(jié)構(gòu)會看到元素上加載了 內(nèi)聯(lián)樣式 display: none;

<body>
  
  <div id="app">
    <span v-show="status === 1"> 嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibil</span>
    <span v-show="status === 2"> xxx </span>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        status: 1
      }
    })
  </script>
</body>

渲染的結(jié)果為

<span> 嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibil</span>
<span style="display: none;"> xxx </span></div>

v-show 不能再 <template> 上使用

5.2.3、v-if 與 v-show 的選擇

v-if 和 v-show 具有類似的功能误证。

  • v-if 真正的條件渲染继薛。若表達式初始值為 false,則一開始元素/組件比不會渲染愈捅,只有當條件第一次變?yōu)?true 時才開始渲染
  • v-show 只是簡單的 CSS 屬性切換遏考,無論條件真與否,都會被編譯蓝谨。

相比之下灌具,v-if 更適合條件不經(jīng)常改變的常見,因為他切換開銷相對較大譬巫,而 v-show 適合頻繁切換條件咖楣。

5.3、列表渲染指令 v-for

5.3.1芦昔、基本用法

當需要將一個數(shù)組遍歷或枚舉一個對象顯示循環(huán)時截歉,就會用到列表渲染指令 v-for

<body>
  
  <div id="app">
    <ul>
      <li v-for="item in books"> {{ item.name }}</li>
    </ul>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        books: [
          {name: 'Vue.js 實戰(zhàn)'},
          {name: 'JavaScript 高級程序設(shè)計'},
          {name: 'jQuery 基礎(chǔ)入門'}
        ]
      }
    })
  </script>
</body>

列表渲染也支持用 of 來代替 in 作為分隔符,它更接近 JavaScript 迭代器的語法

v-for 的表達式 支持一個 可選參數(shù) 作為當前項的索引烟零。
例如:

    <ul>
      <li v-for="(item, index) in books"> {{ index }} ---- {{ item.name }}</li>
    </ul>

與 v-if 一樣瘪松,v-for 也可以使用 內(nèi)置標簽 <template> 上,將多個元素進行渲染:

<body>
  
  <div id="app">
    <template v-for="(item, index) in books">
        <p>
          <span>{{ item.name }}</span> --- 
          <span>{{ item.price }}</span> ---
          <span>{{ item.level }}</span>
        </p>
    </template>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        books: [
          {
            name: 'Vue.js 實戰(zhàn)',
            price: 50,
            level: '1'
          },
          {
            name: 'ES 6 標準入門',
            price: 70,
            leevl: '2'
          },
          {
            name: 'JavaScript 高級程序設(shè)計',
            price: 100,
            level: '3'
          }
        ]
      }
    })
  </script>
</body>

除了數(shù)組外锨阿,對象的屬性也是可以遍歷的宵睦。遍歷對象時,有兩個可選參數(shù)墅诡,分別是鍵名和索引:

<body>
  <div id="app">
    <p v-for="(value, key, index) in user">value: {{value}} ----- key: {{key}} ------ index: {{index}}</p> 
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        user: {
          name: '了凡',
          age: 1,
          male: true
        }
      }
    })
  </script>
</body>

v-for 還可以迭代整數(shù)

<span v-for="n in 10"> {{ n }}</span>
5.3.2壳嚎、數(shù)組更新

Vue 的核心是數(shù)據(jù)與視圖的雙向綁定,當我們修改數(shù)組時末早,Vue 會檢測到數(shù)據(jù)變化烟馅,所以用 v-for 渲染的視圖也會立即更新。Vue包含了 一組觀察數(shù)組變異的方法然磷,使用他們改變數(shù)組會觸發(fā)視圖更新:

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

使用以上方法會改變被這些方法調(diào)用的原數(shù)組郑趁。

有些方法不會改變原數(shù)組,例如:

  • filter()
  • concat()
  • slice()

它們返回的是一個新數(shù)組姿搜,在使用這些非變異方法時寡润,可以用新數(shù)組來替換原數(shù)組

<body>
  <div id="app">
    <template v-for="book in books">
      <p>
        <span>書名:{{ book.name }}</span>
        <span>作者:{{ book.author }}</span>
      </p>
    </template>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        books: [
          {
            name: '《Vue.js 實戰(zhàn)》',
            author: '梁灝'
          }, {
            name: 'Professional JavaScript for Web Developers',
            author: 'Nicholas C.Zakas'
          }
        ]
      }
    })

    app.books = app.books.filter(function(item) {
      return item.name.match(/JavaScript/) // 返回 書名中 含有 JavaScript 的書
    })
  </script>
</body>

Vue 在檢測到數(shù)組變化時捆憎,并不是直接重新渲染整個列表,而是最大化地復(fù)用 DOM 元素梭纹。替換的數(shù)組中躲惰,含有相同元素的項不會被重寫渲染,因此可以大膽地用新數(shù)組來替換舊數(shù)組变抽,不用擔心性能問題础拨。

需要注意的是,以下變動的數(shù)組中绍载,Vue 是不能檢測的诡宗,也不會觸發(fā)視圖更新

  • 通過 索引直接設(shè)置項,比如 app.books[3] = {...}
  • 修改數(shù)組長度逛钻,比如 app.books.length = 1僚焦。

解決第一個問題可以用兩種方法實現(xiàn)同樣的效果锰提,第一種是使用 Vue 內(nèi)置的 set方法曙痘。

Vue.set(app.books, 3, {
  name: '《CSS 揭秘》',
  author: 'Lea Verou'
})

如果是在 webpack 中使用 組件化的方式(后面會介紹到),默認是沒有Vue的立肘,這是可以使用$set边坤。

this.$set(app.books, 3, {
  name: '《CSS 揭秘》',
  author: 'Lea Verou'
})

這里的this 指向的就是當前組件實例,即 app谅年。在非 webpack 模式下也可以用 $set 方法茧痒,例如 app.$set(...)

另一種方法,使用 splice() 方法也可以達到同樣的效果:

app.books.aplice(3, 1, {
  name: '《CSS 揭秘》',
  author: 'Lea Verou'
})

第二個問題也可以直接使用 splice() 來解決

app.books.splice(1)
5.3.3融蹂、過濾與排序

但你不想改變原數(shù)組旺订,想通過一個數(shù)組的副本來做過濾或排序的顯示時,可以使用 計算屬性來返回過濾或排序后的數(shù)組超燃。

<body>
  <div id="app">
    <template v-for="book in filterBooks">
      <p>
        <span>書名:{{ book.name }}</span>
        <span>作者:{{ book.author }}</span>
      </p>
    </template>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        books: [
          {
            name: '《Vue.js 實戰(zhàn)》',
            author: '梁灝'
          }, {
            name: 'Professional JavaScript for Web Developers',
            author: 'Nicholas C.Zakas'
          }
        ]
      },
      computed: {
        filterBooks: function() {
          return this.books.filter(function(book) {
            return book.name.match(/JavaScript/) // 返回 書名中 含有 JavaScript 的書
          })
        }
      }
    })
  </script>
</body>

類似的 實現(xiàn)排序

  
  sortedBooks() {
    return this.books.sort(function(a, b) {
      return a.name.length - b.name.length
    })
  }
}

5.4区拳、方法和事件

5.4.1、基本用法

事件綁定的表達式 可以直接使用 JavaScript 語句意乓,也可以是一個在 Vue 實例中 methods 選項內(nèi)的函數(shù)名樱调。

調(diào)用的方法名后可以不跟括號“()”。此時届良,如果該方法有參數(shù)笆凌,默認會將原生對象事件 event 傳入,在大部分業(yè)務(wù)場景中士葫,如果方法不需要傳入?yún)?shù)乞而,為了簡便可以不寫括號

Vue提供一個特殊變量 $event,用于訪問原生 DOM 事件慢显。
如下示例:

<body>
  <div id="app">
    <a  @click="handleClick('禁止打開', $event)">百度</a>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      methods: {
        handleClick(msg, event) {
          console.log(msg)
          event.preventDefault() // 阻止跳轉(zhuǎn)
        }
      }
    })
  </script>
</body>
5.4.2晦闰、修飾符

在 @ 綁定的事件后面加 小圓點“.”放祟,后跟一個后綴來使用修飾符。
Vue 支持以下修飾符:

  • .stop —— 阻止事件冒泡
  • .prevent —— 阻止默認行為
  • .capture —— 使用事件捕獲模式
  • .self —— 只當在 event.target 是當前元素自身時觸發(fā)處理函數(shù)
  • .once —— 事件將只會觸發(fā)一次
  • .passive —— 不會調(diào)用 preventDefault()呻右,仍然調(diào)用了這個函數(shù)跪妥,客戶端將會忽略它并拋出一個控制臺警告。

在表單元素上 監(jiān)聽鍵盤事件時声滥,還可以使用按鍵修飾符眉撵。
比如按下具體某個鍵時才調(diào)用方法

<!-- 按下 keyCode 是 13 時 調(diào)用 vm.submit() -->
<input @keyup.13="submit" />

也可以自定義具體按鍵

Vue.config.keyCode.f1 = 112

這樣就可以通過 f1 來訪問 keyCode=112 的鍵了。

除了具體的某個 keyCode 外落塑,Vue 還提供了一些快捷鍵名稱纽疟。
以下是全部的別名

  • .enter
  • .tab
  • .delete
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .left
  • .right
    下列按鍵修飾也可以組合使用,或者和鼠標一起配合使用
  • .ctrl
  • .alt
  • .shift
  • .meta
<!-- shift + s -->
<input @keyup.shift.83="handleSave" />

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

5.5憾赁、 發(fā)開購物車

基本功能:商品數(shù)量增減污朽、移除操作、計算總價

效果圖

以下是 實現(xiàn)代碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    [v-cloak] {
      display: none;
    }
    table {
      border: 1px solid #e9e9e9;
      border-collapse: collapse;
      border-spacing: 0;
      empty-cells: show;
    }

    td, th {
      padding: 8px 16px;
      border: 1px solid #e9e9e9;
      text-align: left
    }

    th {
      background: #f7f7f7;
      color: #5c6b77;
      font-weight: 600;
      white-space: nowrap
    }
  </style>
</head>
<body>
  <div id="app" v-cloak>
    <template v-if="list.length">
      <table>
        <thead>
          <tr>
            <th></th>
            <th>商品名稱</th>
            <th>商品單價</th>
            <th>購買數(shù)量</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(item, index) in list">
            <td>{{ index + 1 }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.price }}</td>
            <td>
              <button @click="handleReduce(index)" :disbaled="item.count === 1">-</button> <!-- 減少按鈕 -->
              {{ item.count }}
              <button @click="handleAdd(index)">+</button> <!-- 增加按鈕 -->
            </td>
            <td>
              <button @click="handleRemove(index)">移除</button> <!-- 移除按鈕 -->
            </td>
          </tr>
        </tbody>
      </table>

      <div>總價:¥ {{ totalPrice }}</div>
    </template>
  </div>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        list: [ 
          {
            id: 1,
            name: 'iPhone 7',
            price: 6188,
            count: 1
          }, {
            id: 2,
            name: 'iPad Pro',
            price: 5888,
            count: 1
          }, {
            id: 3,
            name: 'MacBook Pro',
            price: 21488,
            count: 1
          }
        ]
      },
      methods: {
        handleReduce(index) { // 減少
          if (this.list[index].count === 1) return

          this.list[index].count--
        },
        handleAdd(index) { // 增加
          this.list[index].count++
        },
        handleRemove(index) { // 移除
          this.list.splice(index, 1)
        }
      },
      computed: {
        totalPrice() { // 計算總價
          var total = 0;
          for (var i = 0, len = this.list.length; i < len; i++) {
            var item = this.list[i]
            total += item.price * item.count
          }
          /*
          \B:匹配非單詞邊界
          (?=):正向零寬斷言
          \d{3}:匹配三個數(shù)字字符
          +:與前面的\d{3}結(jié)合表示匹配3的整數(shù)倍個數(shù)字字符
          $:字符串結(jié)尾
          所以合起來的意思就是:匹配單詞中的某個位置龙考,這個位置之后的字符全部為數(shù)字蟆肆,且出現(xiàn)次數(shù)是3的整數(shù)倍。
          */
          return total.toString().replace(/\B(?=(\d{3})+$)/g, ',') // 千元分隔符
        }
      }
    })
  </script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晦款,一起剝皮案震驚了整個濱河市炎功,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缓溅,老刑警劉巖蛇损,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坛怪,居然都是意外死亡淤齐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門袜匿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來更啄,“玉大人,你說我怎么就攤上這事沉帮⌒馑溃” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵穆壕,是天一觀的道長待牵。 經(jīng)常有香客問我,道長喇勋,這世上最難降的妖魔是什么缨该? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮川背,結(jié)果婚禮上贰拿,老公的妹妹穿的比我還像新娘蛤袒。我一直安慰自己,他們只是感情好膨更,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布妙真。 她就那樣靜靜地躺著,像睡著了一般荚守。 火紅的嫁衣襯著肌膚如雪珍德。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天矗漾,我揣著相機與錄音锈候,去河邊找鬼。 笑死敞贡,一個胖子當著我的面吹牛泵琳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播誊役,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼获列,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了势木?” 一聲冷哼從身側(cè)響起蛛倦,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤歌懒,失蹤者是張志新(化名)和其女友劉穎啦桌,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體及皂,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡甫男,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了验烧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片板驳。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碍拆,靈堂內(nèi)的尸體忽然破棺而出若治,到底是詐尸還是另有隱情,我是刑警寧澤感混,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布端幼,位于F島的核電站,受9級特大地震影響弧满,放射性物質(zhì)發(fā)生泄漏婆跑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一庭呜、第九天 我趴在偏房一處隱蔽的房頂上張望滑进。 院中可真熱鬧犀忱,春花似錦、人聲如沸扶关。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽节槐。三九已至鲫寄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疯淫,已是汗流浹背地来。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留熙掺,地道東北人未斑。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像币绩,于是被迫代替她去往敵國和親蜡秽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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

  • vue概述 在官方文檔中缆镣,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,210評論 0 25
  • 主要還是自己看的芽突,所有內(nèi)容來自官方文檔。 介紹 Vue.js 是什么 Vue (讀音 /vju?/董瞻,類似于 vie...
    Leonzai閱讀 3,348評論 0 25
  • 1. Vue 實例 1.1 創(chuàng)建一個Vue實例 一個 Vue 應(yīng)用由一個通過 new Vue 創(chuàng)建的根 Vue 實...
    王童孟閱讀 1,019評論 0 2
  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,207評論 0 6
  • 33寞蚌、JS中的本地存儲 把一些信息存儲在當前瀏覽器指定域下的某一個地方(存儲到物理硬盤中)1、不能跨瀏覽器傳輸:在...
    萌妹撒閱讀 2,080評論 0 2