從零開始學習vue

重要說明:本文會在我有空閒時間時持續更新,至關因而將官網的示例給徹底呈現,是爲了幫助初學者,也是爲了鞏固我本身的技術,我決定將官網給過濾一道消化,敬請期待。javascript

一.介紹

vue是一種漸進式框架,被設計爲自底向上逐層應用。所謂漸進式框架,個人理解就是vue是按部就班的,一步一步的用。
舉個簡單的例子,就算咱們不會webpack,不會node,但也能很快的入門。更多詳情參閱漸進式css

二.起步

1.hello,world

在學習vue以前,須要有紮實的HTML,CSS,JavaScript基礎。任何一個入門語言都離不開hello,world!例子,咱們來寫這樣一個例子:
新建一個html文件,helloworld.html,以下:html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>hello,world</title>
  </head>
  <body>
    <div id="app">
      {{ message }}
    </div>
    <script>
    //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:前端

var app = new Vue({
  el:"#app",
  data:{
    message:"hello,world!"
  }
})

如今咱們已經成功建立了第一個vue應用,數據和DOM已經被關聯,全部的東西都是響應式的,咱們要如何肯定呢,打開瀏覽器控制檯,修改app.message的值。vue

在這其中data對象的寫法,咱們還能夠寫成函數形式,以下:java

var app = new Vue({
  el:"#app",
  //這裏是重點
  data(){
     return{
        message:"hello,world!"
     }
  }
})

2.文本插值

固然除了文本插值,咱們還能夠綁定元素屬性,以下:node

<!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <!-- 引入vue.js開發版本 -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <title>v-bind</title>
      </head>
      <body>
        <div id="app">
          <span v-bind:title="message">鼠標懸浮上去能夠看到</span>
        </div>
        <script>
        //這裏寫JavaScript代碼
        </script>
      </body>
    </html>

js代碼以下:jquery

var app = new Vue({
  el:"#app",
  data:{
    message:"頁面加載於:" + new Date().toLocaleString()
  }
})

查看效果,前往此處查看效果:
一樣的咱們也能夠修改message的值,這樣的話,鼠標懸浮上去,懸浮的內容就會改變了。在這個例子中v-bind(或者也能夠寫成':')其實就是一個指令,指令一般前綴都帶有v-,用於表示vue指定的特殊特性,在渲染DOM的時候,它會應用特殊的響應式行爲。這個指令所表達的意思就是:將這個title屬性的值與vue實例的message值保持一致。webpack

3.元素的顯隱

固然,咱們也能夠控制一個元素的顯隱,那也是很是的簡單,只須要使用v-show指令便可:git

<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <!-- 引入vue.js開發版本 -->
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <title>v-if</title>
          </head>
          <body>
            <div id="app">
              <span v-show="seen">默認你是看不到個人哦</span>
            </div>
            <script>
            //這裏寫JavaScript代碼
            </script>
          </body>
        </html>

js代碼以下:

var app = new Vue({
       el:"#app",
       data:{
          seen:false
       }
   })

嘗試在控制檯中修改seen的值,也就是app.seen = true,而後你就能夠看到頁面中的span元素了,具體示例

4.列表渲染

還有v-for指令,用於渲染一個列表,以下:

<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <!-- 引入vue.js開發版本 -->
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <title>v-for</title>
          </head>
          <body>
            <div id="app">
              <div v-for="(item,index) in list" :key="index">
                <span>{{ item.name }}</span>
                <p>{{ item.content }}</p>
              </div>
            </div>
            <script>
            //這裏寫JavaScript代碼
            </script>
          </body>
        </html>

js代碼以下:

var app = new Vue({
       el:"#app",
       data:{
          list:[
            { name:"項目一",content:"HTML項目"},
            { name:"項目二",content:"CSS項目"},
            { name:"項目三",content:"JavaScript項目"},
          ]
       }
   })

固然你也能夠本身在控制檯改變list的值,具體示例

5.事件

vue經過v-on + 事件屬性名(也能夠寫成'@' + 事件屬性名)指令添加事件,例如v-on:click@click以下一個示例:

<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <!-- 引入vue.js開發版本 -->
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <title>v-on</title>
          </head>
          <body>
            <div id="app">
              <span>{{ message }}</span>
              <button type="button" v-on:click="reverseMessage">反轉信息</button>
              <!--也能夠寫成-->
              <!--<button type="button" @click="reverseMessage">反轉信息</button>-->
            </div>
            <script>
            //這裏寫JavaScript代碼
            </script>
          </body>
        </html>

js代碼以下:

var app = new Vue({
       el:"#app",
       data:{
          message:"hello,vue.js!"
       },
       methods:{
         reverseMessage:function(){
             //在這裏this指向構造函數構造的vue實例
            this.message = this.message.split('').reverse().join('');
         }
       }
   })

反轉信息的思路就是使用split()方法將字符串轉成數組,,而後使用數組的reverse()方法將數組倒序,而後再使用join()方法將倒序後的數組轉成字符串。

你也能夠嘗試在這裏查看示例

6.組件

組件是vue中的一個核心功能,它是一個抽象的概念,它把全部應用抽象成一個組件樹,一個組件樹就是一個預約義的vue實例,在vue中使用Vue.component()註冊一個組件,它有兩個參數,第一個參數爲組件名(尤爲要注意組件名的命名),第二個參數爲組件屬性配置對象,如:

//定義一個簡單的組件
Vue.component('todo-item',{
   template:`<li>待辦事項一</li>`
})

如今咱們來看一個完整的例子:

<!DOCTYPE html>
        <html>
          <head>
            <meta charset="utf-8">
            <!-- 引入vue.js開發版本 -->
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <title>component</title>
          </head>
          <body>
            <div id="app">
              <ul>
                <todo-item v-for="(item,index) in todoList" v-bind:todo="item" v-bind:key="index"></todo-item>
              </ul>
            </div>
            <script>
            //這裏寫JavaScript代碼
            </script>
          </body>
        </html>

js代碼以下:

Vue.component('todo-item',{
    props:['todo'],
    template:`<li>{{ todo.number }}.{{ todo.text }}</li>`
 })
 var app = new Vue({
       el:"#app",
       data:{
          todoList:[
             { number:1,text:"html"},
             { number:2,text:"css"},
             { number:3,text:"javascript"}
          ]
       },
       methods:{
         
       }
   })

這樣,一個簡單的組件就完成了,在這裏,咱們知道了,父組件app能夠經過props屬性將數據傳遞給子組件todo-item,這是vue父子組件之間的一種通訊方式。
你能夠嘗試在此處具體示例

三.核心

1.vue實例

每一個vue應用都是經過Vue構造函數建立的一個新的實例開始的:

var vm = new Vue({
   //選項對象
})

在這其中vm(viewModel的簡稱)一般都表示vue實例的變量名。當建立一個vue實例,你均可以傳入一個選項對象做爲參數,完整的選項對象,你可能須要查看API文檔

一個vue應用應該由一個經過new Vue構造的根實例和許多可嵌套可複用的組件構成,這也就是說全部的組件都是vue實例。

2.數據與方法

當一個vue實例被建立完成以後,就會向它的vue響應系統中加入了data對象中能找到的全部屬性,當這些屬性的值發生改變以後,視圖就會發生響應,也就是更新相應的值。咱們來看一個例子:

//源數據對象
var obj = { name:"eveningwater" };
//構建實例
var vm = new Vue({
   data:obj
})

//這二者是等價的
vm.name === obj.name;
//這也就意味着
//修改data對象裏的屬性也會影響到源數據對象的屬性
vm.name = "waterXi";
obj.name;//"waterXi"
//一樣的,修改源數據對象的屬性也會影響到data對象裏的屬性
obj.name = 'stranger';
vm.name;//"stranger"

可能須要注意的就是,只有data對象中存在的屬性纔是響應式的,換句話說,你爲源數據對象添加一個屬性,根本不會影響到data對象。如:

obj.sex = "男";
vm.sex;//undefined
obj.sex;//'男'
obj.sex = "哈哈哈";
vm.sex;//undefined

這也就意味着你對sex的修改並不會讓視圖更新,如此一來,你可能須要在data對象中初始化一些值,以下:

data:{
   str:'',
   bool:false,
   arr:[],
   obj:{},
   err:null,
   num:0
}

查看此處具體示例

只是還有一個例外Object.freeze(),這個方法就至關於鎖定(凍結)一個對象,使得咱們沒法修改現有屬性的特性和值,而且也沒法添加新屬性。所以這會讓vue響應系統沒法追蹤變化:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>freeze</title>
  </head>
  <body>
    <div id="app">
      <span>{{ message }}</span>
      <button type="button" v-on:click="reverseMessage">反轉信息</button>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var obj = {
          message: "hello,vue.js!"
      }
      //阻止對象
      Object.freeze(obj);
      var app = new Vue({
        el: "#app",
        data:obj,
        methods: {
          reverseMessage: function() {
            this.message = this.message.split("").reverse().join("");
          }
        }
      });

如此一來,不管咱們怎麼點擊按鈕,都不會將信息反轉,甚至頁面還會報錯。
可前往此處具體示例自行查看效果。

固然除了數據屬性之外,vue還暴露了一些有用的實例屬性和方法,它們一般都帶有$前綴,這樣作的方式是以便與用戶區分開來。來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>property</title>
  </head>
  <body>
    <div id="app">
        
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
     name:'eveningwater'
  }
  var vm = new Vue({
    data:obj,
  });
  //這行代碼表示將vue實例掛載到id爲app的DOM根節點上,至關於在實例的選項對象中的el選項,即
  //el:'#app'
 vm.$mount(document.querySelector('#app')) 
 //數據是相等的
 vm.$data === obj;//true
 //掛載的根節點
 vm.$el === document.querySelector('#app');//true
 //以上兩個屬性都是實例上的屬性,接下來還有一個watch即監聽方法是實例上的方法
 vm.$watch('name',function(oldValue,newValue){
   //數據原來的值
   console.log(oldValue);
   //數據最新的值
    console.log(newValue);
 })

接下來,能夠嘗試在瀏覽器控制檯修改name的值,你就會發現watch()方法的做用了。
這個示例可前往具體示例

3.實例生命週期

每一個vue實例在被建立的時候都會經歷一些初始化的過程,這其中提供了一些生命週期鉤子函數,這些鉤子函數表明不一樣的生命週期階段,這些鉤子函數的this就表明調用它的那個實例。對於生命週期,有一張圖:

lifecycle.png

你不須要當即這張圖所表明的含義,咱們來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>vue life cycle</title>
  </head>
  <body>
    <div id="app">
        <span>vue生命週期</span>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
     name:'eveningwater'
  }
  var app = new Vue({
    data:obj,
    beforeCreate:function(){
        //此時this指向app這個vue實例,但並不能獲得data屬性,所以this.name的值是undefined
        console.log('實例被建立以前,此時並不能訪問實例內的任何屬性' + this.name)
    }
  });

關於生命週期的所有理解,咱們須要理解後續的組件知識,再來補充,此處跳過。這個示例可前往具體示例

4.模板語法

vue使用基於HTML的模板語法,在vue的底層是將綁定數據的模板渲染成虛擬DOM,並結合vue的響應式系統,從而減小操做DOM的次數,vue會計算出至少須要渲染多少個組件。

最簡單的模板語法莫過於插值了,vue使用的是Mustache語法(也就是雙大括號"{{}}")。這個只能對文本進行插值,也就是說不管是字符串仍是標籤都會被看成字符串渲染。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>Mustache</title>
  </head>
  <body>
    <div id="app">
        <span>{{ greeting }}World!</span>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = { greeting:"Hello,"};
 var vm = new Vue({
    data:obj
 });
 vm.$mount(document.getElementById('app'));

如此以來Mustache標籤就會被data對象上的數據greeting給替代,並且咱們不管怎麼修改greeting的值,視圖都會響應,具體示例

咱們還可使用v-once指令對文本進行一次性插值,換句話說,就是這個指令讓插值沒法被更新:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>Mustache</title>
  </head>
  <body>
    <div id="app">
        <span v-once>{{ greeting }}World!</span>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = { greeting:"Hello,"};
 var vm = new Vue({
    data:obj
 });
 vm.$mount(document.getElementById('app'));

在瀏覽器控制檯中咱們輸入vm.greeting="stranger!"能夠看到視圖並無被更新,這就是這個指令的做用,咱們須要注意這個指令對數據形成的影響。具體實例

既然雙大括號只能讓我插入文本,那要是咱們要插入HTML代碼,咱們應該怎麼辦呢?v-html這個指令就可讓咱們插入真正的HTML代碼。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-html</title>
  </head>
  <body>
    <div id="app">
        <p>{{ message }}</p>
        <p v-html="message"></p>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = { message:"<span style='color:#f00;'>hello,world!</span>"};
 var vm = new Vue({
    data:obj
 });
 vm.$mount(document.getElementById('app'));

頁面效果如圖所示;

圖片描述

可前往具體示例

關於HTML特性,也就是屬性,咱們須要用到v-bind指令,例如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-bind</title>
  </head>
  <body>
    <div id="app">
        <div v-bind:id="propId">使用v-bind指令給該元素添加id屬性</div>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = { propId:"myDiv"};
 var vm = new Vue({
    data:obj
 });
 vm.$mount(document.getElementById('app'));

打開瀏覽器控制檯,定位到該元素,咱們就能看到div元素的id屬性爲"myDiv",以下圖所示:

圖片描述

具體示例

在綁定與元素實際做用相關的屬性,好比disabled,這個指令就被暗示爲true,在默認值是false,null,undefined,''等轉換成false的數據類型時,這個指令甚至不會表現出來。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-bind</title>
  </head>
  <body>
    <div id="app">
        <button type="button" v-bind:disabled="isDisabled">禁用按鈕</button>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = { isDisabled:123};
 var vm = new Vue({
    data:obj
 });
 vm.$mount(document.getElementById('app'));

這樣一來,不管咱們怎麼點擊按鈕都沒用,由於123被轉換成了布爾值true,也就表示按鈕已經被禁用了,咱們能夠打開控制檯看到:

圖片描述

你能夠嘗試這個示例具體示例

在使用模板插值的時候,咱們可使用一些JavaScript表達式。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>expression</title>
  </head>
  <body>
    <div id="app">
      <p>{{ number + 1 }}</p>
      <p>{{ ok ? "確認" : "取消" }}</p>
      <p>{{message.split("").reverse().join("")}}</p>
      <div v-bind:id="'my' + elementId">
        元素的id爲<span :style="{ 'color':color }">myDiv</span>
      </div>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    number: 123,
    ok: true,
    message: "hello,vue.js!",
    elementId: "Div",
    color: "red"
  };
  var vm = new Vue({
    data: obj
  });
  vm.$mount(document.getElementById("app"));

這些JavaScript表達式都會被vue實例做爲JavaScript代碼解析,具體示例

值得注意的就是有個限制,只能綁定單個表達式,像語句是沒法生效的。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>sentence</title>
  </head>
  <body>
    <div id="app">
      <p>{{ var number = 1 }}</p>
      <p>{{ if(ok){ return '確認'} }}</p>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    number: 123,
    ok: true
  };
  var vm = new Vue({
    data: obj
  });
  vm.$mount(document.getElementById("app"));

像這樣直接使用語句是不行的,瀏覽器控制檯報錯,以下圖:

圖片描述

不信能夠本身試試具體示例

指令(Directives)是帶有v-前綴的特殊特性,一般指令的預期值就是單個JavaScript表達式(v-for除外),例如v-ifv-show指令,前者表示DOM節點的插入和刪除,後者則是元素的顯隱。因此,指令的職責就是根據表達式值的改變,響應式的做用於DOM。如今咱們來看兩個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
  </head>
  <body>
    <div id="app">
      <p v-if="value === 1">{{ value }}</p>
      <p v-else-if="value === 2">{{ value }}</p>
      <p v-else>{{ value }}</p>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    value: 1
  };
  var vm = new Vue({
    el: "#app",
    data() {
      return obj;
    }
  });

運行在瀏覽器效果如圖:

圖片描述

如今你能夠嘗試在瀏覽器控制檯更改vm.value = 2vm.value = 3咱們就能夠看到頁面的變化。你也能夠狠狠點擊此處具體示例查看和編輯。

咱們再看v-show的示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-show</title>
  </head>
  <body>
    <div id="app">
      <p v-show="value === 1">{{ value }}</p>
      <p v-show="value === 2">{{ value }}</p>
      <p v-show="value === 3">{{ value }}</p>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
   value:1
} 
var vm = new Vue({
    data:obj
});
vm.$mount(document.querySelector('#app'))

而後查看效果如圖:

圖片描述
嘗試在控制檯修改vm.value = 2vm.value = 3咱們就能夠看到頁面的變化。你也能夠狠狠點擊具體示例查看。

從上面兩個示例的對比,咱們就能夠看出來v-showv-if指令的區別了,從切換效果來看v-if顯然不如v-show,這說明v-if有很大的切換開銷,由於每一次切換都要不停的執行刪除和插入DOM元素操做,而從渲染效果來看v-if又比v-show要好,v-show只是單純的改變元素的display屬性,而若是咱們只想頁面存在一個元素之間的切換,那麼v-if就比v-show要好,這也說明v-show有很大的渲染開銷。

並且v-if還能夠結合v-else-ifv-else指令使用,而v-show不能,須要注意的就是v-else必須緊跟v-if或者v-else-if以後。當須要切換多個元素時,咱們還可使用template元素來包含,好比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>template</title>
  </head>
  <body>
    <div id="app">
        <template v-if="value > 1">
            <p>{{ value }}</p>
            <h1>{{ value }}</h1>
        </template>
        <template v-else>
            <span>{{ value }}</span>
            <h2>{{ value }}</h2>
        </template>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    value: 1
  };
  var vm = new Vue({
    el: "#app",
    data() {
      return obj;
    }
  });

此時template至關於一個不可見元素,以下圖所示:

圖片描述
嘗試在控制檯修改vm.value = 2就能夠看到效果了,你也能夠狠狠的點擊此處具體示例

對於可複用的元素,咱們還能夠添加一個key屬性,好比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>key</title>
  </head>
  <body>
    <div id="app">
      <template v-if="loginType === 'username'">
        <label>username:</label>
        <input type="text" key="username" placeholder="enter your username" />
      </template>
      <template v-else-if="loginType === 'email'">
        <label>email:</label>
        <input type="text" key="email" placeholder="enter your email" />
      </template>
      <template v-else>
        <label>mobile:</label>
        <input type="text" key="mobile" placeholder="enter your mobile" />
      </template>
      <button type="button" @click="changeType">
        toggle login type
      </button>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
        loginType: "username",
        count:1
      };
      var vm = new Vue({
        el: "#app",
        data() {
          return obj;
        },
        methods: {
          changeType() {
            this.count++;
            if (this.count % 3 === 0) {
              this.loginType = "username";
            } else if (this.count % 3 === 1) {
              this.loginType = "email";
            } else {
              this.loginType = "mobile";
            }
          }
        }
      });

效果如圖:

圖片描述
你能夠狠狠的點擊具體示例查看。

從這幾個示例咱們也能夠看出v-if就是惰性,只有當條件爲真時,v-if纔會開始渲染。值得注意的就是v-ifv-for不建議合在一塊兒使用。來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if與v-for</title>
  </head>
  <body>
    <div id="app">
        <ul>
            <li v-for="(item,index) in list" v-bind:key="index" v-if="item.active">
                <span>{{ item.value }}</span>
            </li>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    list:[
        {
            value:'html',
            active:false
        },
        {
            value:'css',
            active:false
        },
        {
            value:"javascript",
            active:true
        }
    ]
  };
  var vm = new Vue({
    el: "#app",
    data() {
      return obj;
    }
  });

雖然以上代碼不會報錯,但這會形成很大的渲染開銷,由於v-for優先級高於v-if,這就形成每次執行v-if指令時總要先執行v-for遍歷一遍數據。你能夠點擊此處具體示例查看。

遇到這種狀況,咱們可使用計算屬性。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if和v-for</title>
  </head>
  <body>
    <div id="app">
      <ul v-if="newList">
        <li v-for="(item,index) in newList" v-bind:key="index">
          <span>{{ item.value }}</span>
        </li>
      </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    list: [
      {
        value: "html",
        active: false
      },
      {
        value: "css",
        active: false
      },
      {
        value: "javascript",
        active: true
      }
    ]
  };
  var vm = new Vue({
    el: "#app",
    //先過濾一次數組
    computed: {
      newList: function() {
       return this.list.filter(function(item) {
          return item.active;
        });
      }
    },
    data() {
      return obj;
    }
  });

如此一來,就減小了渲染開銷,你能夠狠狠點擊這裏具體示例查看。

指令的用法還遠不止如此,一些指令是能夠帶參數的,好比v-bind:title,在這裏title其實就是被做爲參數。基本上HTML5屬性均可以被用做參數。好比圖片路徑的src屬性,再好比超連接的href屬性,甚至事件的添加也屬於參數,如v-on:click中的click其實就是參數。來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>param</title>
  </head>
  <body>
    <div id="app">
        <a v-bind:href="url">思否</a>
        <img :src="src" alt="美女" />
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
    url: "https://segmentfault.com/",
    src:"http://eveningwater.com/project/imggallary/img/15.jpg"
  };
  var vm = new Vue({
    el: "#app",
    data() {
      return obj;
    }
  });

效果如圖所示:

圖片描述
你能夠點擊此處具體示例查看。

v-on指令還能夠添加修飾符,v-bindv-on指令還能夠縮寫成:@。縮寫對於咱們在繁瑣的使用指令的項目當中是一個很不錯的幫助。

5.計算屬性

模板表達式提供給咱們處理簡單的邏輯,對於更復雜的邏輯,咱們應該使用計算屬性。來看兩個示例的對比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>mustache</title>
  </head>
  <body>
    <div id="app">
      <span>{{ message.split('').reverse().join('') }}</span>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
   message:"hello,vue.js!"
 }
 var vm = new Vue({
     data:obj
 })
 vm.$mount(document.querySelector('#app'))

第二個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>mustache</title>
  </head>
  <body>
    <div id="app">
      <span>{{ reverseMessage }}</span>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
   message:"hello,vue.js!"
 }
 var vm = new Vue({
     data:obj,
     computed:{
         reverseMessage:function(){
            return this.message.split('').reverse().join('');
         }
     }
 })
 vm.$mount(document.querySelector('#app'))

與第一個示例有所不一樣的就是在這個示例當中,咱們申明瞭一個計算屬性reverseMessage,而且提供了一個getter函數將這個計算屬性同數據屬性message綁定在一塊兒,也許有人會有疑問getter函數到底在哪裏呢?
若是咱們將以上示例修改一下:

var obj = {
   message:"hello,vue.js!"
 }
 var vm = new Vue({
     data:obj,
     computed:{
         reverseMessage:{
            get:function(){
               return this.message.split('').reverse().join('');
            }
         }
     }
 })
 vm.$mount(document.querySelector('#app'))

相信如此一來,就能明白了。你能夠狠狠點擊此處具體示例。你能夠經過控制檯修改message的值,只要message的值發生改變,那麼綁定的計算屬性就會發生改變。事實上,在使用reverseMessage綁定的時候,咱們還能夠寫成調用方法同樣的方式,如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>mustache</title>
  </head>
  <body>
    <div id="app">
      <span>{{ reverseMessage() }}</span>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var obj = {
   message:"hello,vue.js!"
 }
 var vm = new Vue({
     data:obj,
     computed:{
         reverseMessage:function(){
            return this.message.split('').reverse().join('');
         }
     }
 })
 vm.$mount(document.querySelector('#app'))

那麼這二者有何區別呢?雖然二者的結果都同樣,但計算屬性是根據依賴進行緩存的,只有相關依賴發生改變時它們纔會從新求值。好比這裏計算屬性綁定的依賴就是message屬性,一旦message屬性發生改變時,那麼計算屬性就會從新求值,若是沒有改變,那麼計算屬性將會緩存上一次的求值。這也意味着,若是計算屬性綁定的是方法,那麼計算屬性不是響應式的。以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>mustache</title>
  </head>
  <body>
    <div id="app">
      <span>{{ date }}</span>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
     data:obj,
     computed:{
         reverseMessage:function(){
            return Date.now();
         }
     }
 })
 vm.$mount(document.querySelector('#app'))

與調用方法相比,調用方法總會在頁面從新渲染以後再次調用方法。咱們爲何須要緩存,假設你要計算一個性能開銷比較大的數組,並且若是其它頁面也會依賴於這個計算屬性,若是沒有緩存,那麼不管是讀取仍是修改都會去屢次修改它的getter函數,這並非咱們想要的。

計算屬性默認只有getter函數,讓咱們來嘗試使用一下setter函數,以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>computed</title>
  </head>
  <body>
    <div id="app">
       <input type="text" v-model="name">
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
      first_name: "li",
      last_name: "qiang"
    },
    computed: {
      name: {
        get: function() {
          return this.first_name + ' ' + this.last_name;
        },
        set: function(newValue) {
          var names = newValue.split(' ');
          this.first_name = names[0];
          this.last_name = names[names.length - 1];
        }
      }
    }
  });

如今,咱們只須要修改vm.name的值就能夠看到first_namelast_name的值相應的也改變了。你能夠狠狠點擊此處具體示例

6.偵聽器

雖然計算屬性在大多數狀況下更合適,但有時候也可使用偵聽器。vue經過watch選項提供一個方法來響應數據的變化。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
    <title>watch</title>
    <style>
        img{
           width:200px;
           height:200px;
        }
    </style>
  </head>
  <body>
    <div id="app">
       <p>
          能夠給我提出一個問題,而後我來回答?
          <input type="text" v-model="question">
       </p>
       <p>{{ answer }}</p>
       <img :src="answerImg" alt="答案" v-if="answerImg"/>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
        el:"#app",
        data(){
            return{
                answer:"我不能回答你除非你提出一個問題!",
                question:"",
                answerImg:""
            }
        },
        created:function(){
           // `_.debounce` 是一個經過 Lodash 限制操做頻率的函數。
           // 在這個例子中,咱們但願限制訪問 yesno.wtf/api 的頻率
           // AJAX 請求直到用戶輸入完畢纔會發出。想要了解更多關於
           // `_.debounce` 函數 (及其近親 `_.throttle`) 的知識,
           // 請參考:https://lodash.com/docs#debounce
           this.debounceGetAnswer = _.debounce(this.getAnswer,500);
        },
        //若是question值發生改變
        watch:{
           question:function(oldValue,newValue){
              this.answer="正在等待你中止輸入!";
              this.debounceGetAnswer();
           }
        },
        methods:{
            getAnswer:function(){
               //若是問題沒有以問號結束,則返回
               if(this.question.indexOf('?') === -1){
                 this.answer = "提出的問題須要用問號結束!";
                 return;
               }
               this.answer = "請稍等";
               var self = this;
               fetch('https://yesno.wtf/api').then(function(response){
                   //fetch發送請求,json()就是返回數據
                   response.json().then(function(data) {
                      self.answer = _.capitalize(data.answer);
                      self.answerImg = _.capitalize(data.image);
                   });
               }).catch(function(error){
                  self.answer = "回答失敗,請從新提問!";
                  console.log(error);
               })
            }
        }
    })

如今我們來看一下效果:

圖片描述
你能夠狠狠點擊此處具體示例查看。

7.計算屬性vs偵聽器

當在頁面中有一些數據須要根據其它數據的變更而改變時,就很容易濫用偵聽器watch。這時候命令式的偵聽還不如計算屬性,請看:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>watch</title>
  </head>
  <body>
    <div id="app">
       <p>{{ fullName }}</p>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
      el:"#app",
      data:{
         firstName:"li",
         lastName:"qiang",
         fullName:"li qiang"
      },
      watch:{
         firstName:function(val){
            this.fullName = val + ' ' + this.lastName;
         },
         lastName:function(val){
            this.fullName = this.firstName + ' ' + val;
         }
      }
 })

再看經過計算屬性實現的:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>computed</title>
  </head>
  <body>
    <div id="app">
       <p>{{ fullName }}</p>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
      el:"#app",
      data:{
         firstName:"li",
         lastName:"qiang"
      },
      computed:{
         fullName:function(){
           return this.firstNmae + ' ' + this.lastName;
         }
      }
 })

經過計算屬性實現的功能看起來更好,不是嗎?你能夠自行嘗試具體示例(watch)具體示例(computed)進行對比。

8.class與style綁定

操做元素的classstyle是構建一個頁面所常見的需求,由於它們都是屬性,因此利用v-bind指令就能夠操做元素的classstyle樣式。如;

<!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">
     <!-- 引入vue.js開發版本 -->
     <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>class</title>
    <style>
        .font-red{
            color: #f00;
        }
    </style>
</head>
<body>
    <div id="app">
        <span v-bind:class="className">添加一個class類,改變字體顏色爲紅色。</span>
    </div>
    <script>
    //這裏寫JavaScript代碼
    </script>
</body>
</html>

js代碼以下;

var vm = new Vue({
    el:"#app",
    data:{
        className:"font-red"
    }
})

你能夠狠狠點擊此處具體示例 查看。

再來看一個簡單綁定style的示例。

<!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">
     <!-- 引入vue.js開發版本 -->
     <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>style</title>
</head>
<body>
    <div id="app">
        <span v-bind:style="styleProp">改變元素的字體顏色爲紅色。</span>
    </div>
    <script>
    //這裏寫javascript代碼
    </script>
</body>
</html>

js代碼:

var vm = new Vue({
    el:"#app",
    data:{
        styleProp:"color:#f00;"
    }
})

你能夠狠狠點擊此處具體示例查看。

這只是classstyle的簡單用法,vue.js專門在這方面作了加強,使得綁定classstyle 的值能夠是對象,也能夠是數組,如:

<!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">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>class</title>
    <style>
      .font-red{
          color: #f00;
      }
      .font-blue{
          color: #00f;
      }
    </style>
</head>
<body>
    <div id="app">
        <span v-bind:class="{ 'font-red':isRed,'font-blue':isBlue }">改變字體的顏色</span>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
</body>
</html>

js代碼:

var vm = new Vue({
      el:"#app",
      data:{
          isRed:true,
          isBlue:false
      }
  })

咱們能夠看到頁面效果如圖:

圖片描述

你還能夠經過控制檯修改vm.isBluevm.isRed的值,這充分說明這兩個值是響應式的。如:

圖片描述
你能夠狠狠的點擊此處具體示例查看。

一樣的,style同樣也可使用對象語法,如:

<!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" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>style</title>
  </head>
  <body>
    <div id="app">
      <span v-bind:style="{ color:fontColor,fontSize:font18,'font-weight':fontBold }"
        >字體大小爲18px,字體顏色爲紅色,而且加粗的字體。</span
      >
    </div>
    <script>
    //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
      fontColor: "#f00",
      font18: "18px",
      fontBold:"bold"
    }
  });

效果如圖:

圖片描述
咱們同樣能夠修改其中的值,這些值也是響應式的,好比修改vm.fontColor="#0f0"就表示將字體顏色改變爲藍色。從上例咱們也能夠看出,咱們可使用駝峯式 (camelCase) 短橫線分隔 (kebab-case,須要用單引號括起來)來定義css屬性名。
你能夠狠狠點擊此處具體示例查看。

固然在更多時候,咱們直接綁定一個對象更有利於讓模板變得清晰,也方便咱們理解。

<!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" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>style</title>
  </head>
  <body>
    <div id="app">
      <span v-bind:style="styleObject"
        >字體大小爲18px,字體顏色爲紅色,而且加粗的字體。</span
      >
    </div>
    <script>
    //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
        styleObject:{
           fontSize:"18px",
           color:"#f00",
           'font-weight':"bold"
        }
    }
  });

這也是同樣的效果,你能夠點擊此處具體示例查看。

除了對象語法,數組語法也一樣適用於classstyle,如:

<!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" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>class</title>
    <style>
      .font-red {
        color: #f00;
      }
      .font-18 {
        font-size: 18px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <span v-bind:class="[fontColor,fontSize]">顏色爲紅色大小爲18px的字體</span>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
      fontColor: "font-red",
      fontSize: "font-18"
    }
  });

運行效果如圖:
圖片描述
你一樣能夠修改class的名字,諸如vm.fontColor="font-blue",這樣頁面就會將font-red更改成font-blue,這畢竟是響應式的。你能夠狠狠點擊此處具體示例查看。

一樣的,style也能如此作,如:

<!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" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>style</title>
  </head>
  <body>
    <div id="app">
      <span v-bind:style="[colorF,sizeF]">顏色爲紅色大小爲18px的字體</span>
    </div>
    <script>
      //javascript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
        colorF: {
            color:"#f00"
        },
        sizeF: {
            fontSize:"18px"
        }
    }
  });

這裏尤爲注意以下的寫法是錯誤的,vue.js並不能渲染出樣式:

//這說明style綁定的數組項只能是一個對象,而不能是字符串
 var vm = new Vue({
    el: "#app",
    data: {
        colorF: "color:#f00;",
        sizeF: "font-size:18px;"
    }
  });

一樣,咱們注意修改值的時候也應該修改爲一個對象,如:

vm.sizeF = {
   'font-size':"20px"
}

這點是須要注意的,另外在遇到帶有前綴的css屬性,如transition時,咱們沒必要寫前綴,由於vue會自動幫咱們添加前綴。你能夠狠狠點擊此處具體示例查看。

style的用法還不止如此,咱們還能夠綁定多重值。以下:

<!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" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>style</title>
  </head>
  <body>
    <div id="app">
      <span v-bind:style="{ display:[webkitD,nomarD] }">顏色爲紅色大小爲18px的字體</span>
    </div>
    <script>
     //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
        webkitD:"-webkit-flex",
        nomarD:"flex"
    }
  });

這樣一來,瀏覽器會根據支持-webkit-flexflex而採用支持的寫法,這個是在vue2.3.0+版本中增長的功能。你能夠點擊此處具體示例查看。

9.條件渲染

v-if指令用於條件性的渲染一塊內容,這個指令只在它綁定的值爲truthy的時候纔會渲染內容。例如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
  </head>
  <body>
    <div id="app">
        <p v-if="true">正常顯示</p>
        <span v-if="false">不顯示</span>
        <div v-if="show">也是正常顯示</div>
        <a href="#" v-if="hidden">也是不顯示</a>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data:{
            show:true,
            hidden:false
        }
      });
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

v-if指令也能夠與v-else指令結合使用,注意v-else必須緊跟v-if或者v-else-if以後。好比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
  </head>
  <body>
    <div id="app">
        <p v-if="true">正常顯示</p>
        <span v-else="false">不顯示</span>
        <div v-if="show">也是正常顯示</div>
        <a href="#" v-else>也是不顯示</a>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data:{
            show:true
        }
      });
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

v-if也能夠直接在<template></template>標籤上使用,這種狀況下,咱們一般是爲了切換多個元素,由於v-if必須添加到一個元素上,並且會把template看成不可見元素來渲染,也就是說最終渲染不會包含template元素。好比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
  </head>
  <body>
    <div id="app">
        <template  v-if="show">
            <p>呵呵呵</p>
            <h1>哈哈哈</h1>
            <div>嘻嘻嘻</div>
            <span>嘿嘿嘿</span>
        </template>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data:{
            show:true
        }
      });
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

vue2.1.0新增了v-else-if,顧名思義,也就是緊跟v-if以後,v-else以前的指令,可使用多個v-else-if指令。好比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
  </head>
  <body>
    <div id="app">
        <p v-if="type===0">哈哈哈</p>
        <span v-else-if="type===1">嘿嘿嘿</span>
        <div v-else-if="type===2">嘻嘻嘻</div>
        <h2 v-else>呵呵呵</h2>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data:{
            type:0
        }
      });
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。在這些示例中,只要綁定的是在vue實例data選項中的數據,那麼值就是響應式的,咱們能夠直接在控制檯中修改,好比以上的vm.type = 1,咱們就能夠看到頁面的的元素以及內容被改變,並從新渲染。

因爲vue是簡單的複用元素,而不是從新渲染元素,所以,這會讓vue很是的高效,但這不可避免出現了一個問題,以下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
</head>

<body>
    <div id="app">
        <template v-if="type==='name'">
            <label>姓名:</label>
            <input type="text" placeholder="請輸入姓名">
        </template>
        <template v-else-if="type==='email'">
            <label>郵箱:</label>
            <input type="text" placeholder="請輸入郵箱">
        </template>
        <template v-else>
            <label>密碼:</label>
            <input type="password" placeholder="請輸入密碼">
        </template>
        <button type="button" @click="changeType">切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                type: "name",
                count:0
            },
            methods:{
                changeType:function(){
                    this.count++;
                    if(this.count % 3 === 0){
                        this.type = 'name';
                    }else if(this.count % 3 === 1){
                        this.type = 'email';
                    }else{
                        this.type="password"
                    }
                }
            }
        });
    </script>
</body>

</html>

你能夠狠狠點擊此處具體示例查看效果。在輸入框中輸入值,而後再點擊切換按鈕,你會發現input的內容並無被清空,這也說明vue並非從新渲染元素,而是高效的複用元素而已。再實際開發中,這樣確定是不符合需求的,那麼咱們應該如何解決這個問題呢?

還好,vue提供了一個key屬性,咱們只須要給每一個複用的元素綁定一個key屬性,用於區分它們是不一樣的元素。以下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-if</title>
</head>

<body>
    <div id="app">
        <template v-if="type==='name'">
            <label>姓名:</label>
            <input type="text" placeholder="請輸入姓名" key="name">
        </template>
        <template v-else-if="type==='email'">
            <label>郵箱:</label>
            <input type="text" placeholder="請輸入郵箱" key="email">
        </template>
        <template v-else>
            <label>密碼:</label>
            <input type="password" placeholder="請輸入密碼" key="password">
        </template>
        <button type="button" @click="changeType">切換</button>
    </div>
    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                type: "name",
                count:0
            },
            methods:{
                changeType:function(){
                    this.count++;
                    if(this.count % 3 === 0){
                        this.type = 'name';
                    }else if(this.count % 3 === 1){
                        this.type = 'email';
                    }else{
                        this.type="password"
                    }
                }
            }
        });
    </script>
</body>

</html>

如今你再嘗試在輸入框中輸入值,而後點擊切換按鈕,就會發現值會被清空了。請點擊具體示例查看效果。

須要注意的是label元素其實也是被複用了,由於它們沒有添加key屬性。

v-show的指令用法跟v-if差很少,惟一須要注意的區別就是v-show僅僅只是改變了元素的display屬性而已,其DOM元素仍然存在於文檔中,而且v-show以後沒有v-else-ifv-else指令。看一個簡單的示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-show</title>
  </head>
  <body>
    <div id="app">
        <p v-show="true">正常顯示</p>
        <span v-show="false">不顯示</span>
        <div v-show="show">也是正常顯示</div>
        <a href="#" v-show="hidden">也是不顯示</a>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data:{
            show:true,
            hidden:false
        }
      });
    </script>
  </body>
</html>

具體效果以下圖所示:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。

因此咱們也能夠看得出來v-ifv-show的區別:

clipboard.png

還要注意的一點就是不推薦v-ifv-for一塊兒使用,由於它們同時使用的時候,v-for的優先級高於v-if

10.列表渲染

vue.js使用v-for指令來渲染一個列表,形式相似item in itemsitem of items,在這之中,item是數組中每一項的迭代名稱,而items則是源數據,在渲染列表的時候,一般都要添加一個key屬性,用以給vue.js一個提示,以便vue.js更好的跟蹤每一個節點的變化。key屬性工做方式就像一個屬性,所以使用v-bind指令來綁定,而且key屬性也是惟一的。理想的key屬性就是每個數組項的惟一id。來看一個示例:

<!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">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-for</title>
</head>
<body>
    <div id="app">
        <ul v-for="item in items">
            <li>{{ item }}</li>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
</body>
</html>

js代碼:

var vm = new Vue({
      el:"#app",
      data:{
          items:['html','css','javascript']
      }
  })

數組中的數組項還能夠是對象,如:

<!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">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-for</title>
</head>
<body>
    <div id="app">
        <ul v-for="item in items">
            <li>{{ item.id }}.{{ item.value }}</li>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
</body>
</html>

js代碼:

var vm = new Vue({
      el:"#app",
      data:{
          items:[
              {
                  id:1,
                  value:"html"
              },
              {
                  id:2,
                  value:"css"
              },
              {
                  id:3,
                  value:"javascript"
              }
          ]
      }
  })

你能夠狠狠點擊具體示例一具體示例二查看。

這也就是說,v-for包含塊中的父做用域,咱們是有徹底訪問的權限的,並且v-for還提供第二個可選參數,表示對當前項的索引。如:

<!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">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-for</title>
</head>
<body>
    <div id="app">
        <ul v-for="(item,index) in items">
            <li>索引:{{ index }}-{{ item }}</li>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
</body>
</html>

js代碼以下:

var vm = new Vue({
      el:"#app",
      data:{
          items:['html','css','javascript']
      }
  })

你能夠點擊此處具體示例查看。

v-for一樣能夠渲染一個對象,可選有三個參數,第一個參數爲對象值,第二個參數爲對象屬性鍵名,第三個參數則是索引。以下:

<!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">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-for</title>
</head>
<body>
    <div id="app">
        <ul v-for="(value,key,index) in info">
            <li>索引:{{ index }} + 屬性名:{{ key }} + 屬性值:{{ value }}</li>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
</body>
</html>

js代碼:

var vm = new Vue({
      el:"#app",
      data:{
          info:{
              name:"李白",
              value:"李太白"
          }
      }
  })

你能夠狠狠點擊此處具體示例查看。

爲了vue.js 可以高效的更新虛擬DOM,咱們有必要給vuev-for提供一個key屬性,理想的key屬性就是每一項的id。這是一個屬性,因此使用v-bind來綁定,添加key屬性是方便vue跟蹤每一個節點,從而根據數據的變化來重排序節點,要理解key屬性的意義,你可能須要理解虛擬DOM的DIFF算法的知識。雖然vue.js默認就採用就地複用的策略,但這隻針對不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出,因此最好的方法就是添加key屬性。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-for</title>
  </head>
  <body>
    <div id="app">
        <div v-for="item in items" :key="item.id" :class="item.value">
            <p>{{ item.value }}</p>
        </div>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data() {
      return{
          items:[
              {
                  id:1,
                  value:"html"
              },
              {
                  id:2,
                  value:"css"
              },
              {
                  id:3,
                  value:"javascript"
              }
          ]
      }
    }
  });

爲了方便理解key屬性帶來的高效性,能夠嘗試在控制檯更改數據,好比增刪改查操做。你能夠點擊此處具體示例查看。

vue包含一組數組的變異方法,他們也可讓視圖隨着數據的改變而更新。方法以下:
push(),pop(),unshift(),shift(),splice(),reverse(),sort()。咱們能夠來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>mutation methods</title>
  </head>
  <body>
    <div id="app">
        <ul>
            <li v-for="(item,index) in items" :key="index">{{ item.name }}</li>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
        el:"#app",
        data:{
            items:[
                {
                    id:1,
                    name:"html"
                },
                {
                    id:2,
                    name:"css"
                },
                {
                    id:3,
                    name:"javascript"
                }
            ]
        }
  });
  //vm.items.push({ id:4,name:"java"})
  //vm.items.pop()
  //vm.items.shift()
  //vm.items.unshift({ id:4,name:"java"})
  //vm.items.splice(0,1,{ id:1,name:"java"})
  //vm.items.reverse()

嘗試在控制檯,使用這些變異方法修改數組items的值,咱們就能夠看到這些方法的做用了。你能夠狠狠點擊此處具體示例查看。

變異的方法,顧名思義,就是會改變原數組,既然有變異方法,那固然也有非變異的方法,好比:filter(),slice()concat(),雖然這些方法不會改變一個數組,但老是會返回要給新數組,咱們能夠用新數組替換原數組。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>mutation methods</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="(item,index) in items" :key="index">{{ item.name }}</li>
      </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
    el: "#app",
    data: {
      items: [
        {
          id: 1,
          name: "html"
        },
        {
          id: 2,
          name: "css"
        },
        {
          id: 3,
          name: "javascript"
        }
      ]
    }
  });
  //   vm.items = vm.items.filter(function(item) {
  //     return item.name.match(/javascript/);
  //   });
//   vm.items = vm.items.concat([{ id: 4, name: "java" }]);
// vm.items = vm.items.slice(1);

嘗試在控制檯將以上註釋的js代碼給測試一下,你能夠狠狠點擊此處具體示例嘗試一下。

經過以上示例,你可能會認爲vue拋棄了原數組,從新渲染了DOM,但實際上並非這樣,這也說明這樣替換數組是很是高效的一個操做。

雖然咱們可使用變異方法修改數組,從而達到視圖的更新,但咱們也要注意兩點,那就是如下兩點並不會觸發視圖的更新:

1.根據數組的索引來修改數組項的值。示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>limit</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="(item,index) in items" :key="index">{{ item.name }}</li>
      </ul>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
    el: "#app",
    data: {
      items: [
        {
          id: 1,
          name: "html"
        },
        {
          id: 2,
          name: "css"
        },
        {
          id: 3,
          name: "javascript"
        }
      ]
    }
  });
//vm.items[0].value = "java";並不會觸發視圖的更新。

你能夠點擊此處具體示例親自查看。

2.修改數組的長度。如如下示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>limit</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="(item,index) in items" :key="index">{{ item.name }}</li>
      </ul>
    </div>
    <script>
        //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
    el: "#app",
    data: {
      items: [
        {
          id: 1,
          name: "html"
        },
        {
          id: 2,
          name: "css"
        },
        {
          id: 3,
          name: "javascript"
        }
      ]
    }
  });
//vm.items.length= 2;並不會觸發視圖的更新。

你一樣能夠點擊此處具體示例進行查看。

針對第一個問題,咱們能夠經過Vue.set()方法和變異方法splice()方法來解決,以下:

//第一種方案
Vue.set(vm.items[0],'name','java')
//或this.$set(vm.items[0],'name','java');
//第二種方案
vm.items.splice(0,1,{ id:1,name:"java"});

你能夠點擊此處具體示例查看。

針對第二個問題,你可使用splice()方法來改變。以下:

vm.items.splice(2);

你能夠點擊此處具體示例進行查看。

從以上的示例,咱們能夠看出Vue.set()this.$set()[只是一個別名而已]的參數能夠傳3個,即要改變的數組項,數組項的索引或者屬性名,新數組項的屬性值。而splice()則能夠傳1到3個參數,即數組的起始項索引,刪除幾項,從第三個參數開始就是須要替換的項或者說是須要添加的項

對於對象,其實也仍然有限制,對象的新增和刪除,vue是沒法檢測的。來看以下例子:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>limit</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <li>{{ item.value }}</li>
        <li>{{ item.name }}</li>
      </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    el: "#app",
    data: {
      item: {
          value:"123"
      }
    }
  });
  //vm.item.name = "javascript";//無效
  //delete vm.item.value;//無效

你能夠狠狠點擊此處具體示例查看。

針對以上屬性的添加的問題,咱們仍是使用Vue.set()方法來解決。而屬性的刪除,咱們可使用Vue.delete()來解決,理論上在項目開發中應該不多用到這個方法。

//對象屬性的添加
Vue.set(vm.item,'name','javascript');//或this.$set(vm.item,'name','javascript'); 
//對象屬性的刪除
Vue.delete(vm.item,'value');

當咱們須要添加多個對象時,可使用Object.assign()或_.extend()[jquery方法],以下:

vm.item = Object.assign({},vm.item,{ age:26,skill:['html','css','javascript']})
    //或者vm.item = $.extend({},vm.item,{ age:26,skill:['html','css','javascript']})

你能夠狠狠點擊此處具體示例查看。

有時候,在某種狀況下,咱們會須要過濾或者排序一個數組,而且不能改變原數組,從而渲染出知足條件的數組項,這時候,咱們能夠經過計算屬性結合數組的迭代方法filter() 這個方法不會改變原數組的值。咱們來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>filter array</title>
  </head>
  <body>
    <div id="app">
        <ul>
            <li v-for="(item,index) in filterNumbers" :key="index">{{ item }}</li>
        </ul>
    </div>
    <script>
     //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
        el:"#app",
        data:{
            items:[1,2,3,4,5,6,7,8,9]
        },
        computed:{
            filterNumbers(){
                return this.items.filter((num) => {
                    return num < 5;
                })
            }
        }
  });

你能夠狠狠點擊此處具體示例查看效果。
固然,你也能夠在計算屬性不適用的狀況下,使用方法來替代。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>filter array</title>
  </head>
  <body>
    <div id="app">
        <ul>
            <li v-for="(item,index) in filterNumbers(items)" :key="index">{{ item }}</li>
        </ul>
    </div>
    <script>
     //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
        el:"#app",
        data:{
            items:[1,2,3,4,5,6,7,8,9]
        },
        methods:{
            filterNumbers(numbers){
                return numbers.filter((num) => {
                    return num < 5;
                })
            }
        }
  });

你能夠狠狠點擊此處具體示例查看效果。

也許有時候,咱們是不必渲染每個父元素的,只須要渲染子元素的,這時候咱們就可使用template元素來使用v-for指令渲染元素,渲染的數據也不必定是一個數組,也能夠是一個整數。須要注意的就是當添加key屬性時,須要將這個屬性添加到真實的DOM元素上,這也就是說template元素是不推薦使用key屬性的。以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>template </title>
  </head>
  <body>
    <div id="app">
        <ul>
            <template v-for="n in number" >
                <span :key="n">{{ n }}</span>
            </template>
        </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
        el:"#app",
        data:{
            number:100
        },  
  });

你能夠狠狠點擊此處具體示例查看效果。

v-ifv-for指令結合一塊兒使用時,因爲v-if指令的優先級要高於v-for,所以每次進判斷都要重複遍歷一道數組,不過這在只須要渲染某些項的時候會很是有用。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-for with v-if </title>
  </head>
  <body>
    <div id="app">
        <ul>
            <li v-for="(item,index) in items" :key="index" v-if="item.show">
                <span >{{ item.value }}</span>
            </li>
        </ul>
    </div>
    <script>
     //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
        el:"#app",
        data:{
            items:[
                {
                    show:true,
                    value:'html'
                },
                {
                    show:false,
                    value:"css"
                },
                {
                    show:false,
                    value:"javascript"
                }
            ]                
        },  
  });

你能夠狠狠點擊此處具體示例進行查看。

v-for還能夠用在組件當中,但在組件中使用v-for是必需要加上key屬性的。以下是一個簡易版的todolist組件示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>todolist demo</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <form @submit.prevent="addNewToDo">
          <span>add a need to do thing</span>
          <input type="text" v-model="newToDo" placeholder="eg:write the code"/>
          <button type="button" @click="addNewToDo">add</button>
        </form>
        <todo-item         
          v-for="(item,index) in items" :key="index"
          :item="item"
          :index="index"
          @on-remove="items.splice(index,1)"
        ></todo-item>
      </ul>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

Vue.component("todoItem", {
    template: `
        <div>
            <li v-if="item.show">
                <span>{{ item.value }}</span>
                <button type="button" @click="$emit('on-remove')">remove</button>
            </li>
        </div>
      `,
    props: ["item"]
  });
  var vm = new Vue({
    el: "#app",
    data: {
      newToDo: "",
      items: [
        {
          show: true,
          value: "html"
        },
        {
          show: true,
          value: "css"
        },
        {
          show: true,
          value: "javascript"
        }
      ]
    },
    methods: {
      addNewToDo() {
        if (this.newToDo) {
          this.items.push({
            value: this.newToDo,
            show: true
          });
        }else{
            alert('please input your need to do thing!')
        }
      }
    }
  });

你能夠狠狠點擊此處具體示例進行查看。

11.事件處理

Vue中使用v-on(簡寫@)指令來爲DOM添加點擊事件。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
        <button v-on:click="count += 1">click me!</button>
      <p>The button above has been clicked {{ count }} times!</p>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼:

var vm = new Vue({
    data:{
        count:0
    }
  });
  vm.$mount(document.getElementById("app"));

你能夠狠狠點擊此處具體示例查看效果。

不少時候,事件的複雜程度遠不止如此,所以,事件能夠用方法來表示。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
        <button v-on:click="clickMethod">click me!</button>
      <p>The button above has been clicked {{ count }} times!</p>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
    data:{
        count:0
    },
    methods:{
        clickMethod:function(event){
            //this指向vm這個vue實例
            this.count += 1;
            alert('你點擊了按鈕' + this.count + '次!');
            alert(event.target.tagName);
        }
    }
  });
  //還能夠直接調用方法
//   vm.clickMethod();
  vm.$mount(document.getElementById("app"));

你能夠狠狠點擊此處具體示例查看效果。

除了直接綁定方法,固然還能夠在內聯JavaScript語句中調用方法。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:click="clickMethod(msg1)">click me!</button>
      <button v-on:click="clickMethod(msg2)">click me!</button>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
    data: {
      msg1:'hello,',
      msg2:"world!"
    },
    methods: {
      clickMethod: function(message) {
        alert(message)
      }
    }
  });
  vm.$mount(document.getElementById("app"));

你能夠狠狠點擊此處具體示例查看效果。

固然有時候咱們須要訪問原生DOM事件,這時候可使用一個特殊的變量$event傳入方法。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:click="clickMethod(msg,$event)">click me!</button>
    </div>
    <script>
      //這裏寫JavaScript代碼
    </script>
  </body>
</html>

js代碼以下:

var vm = new Vue({
    data: {
      msg: "hello,world!"
    },
    methods: {
      clickMethod: function(message, event) {
        //如今你能夠訪問原生的事件對象
        if (event) event.preventDefault();
        alert(message);
      }
    }
  });
  vm.$mount(document.getElementById("app"));

你能夠狠狠點擊此處具體示例查看效果。

在事件中阻止事件的默認行爲或者阻止事件冒泡是很是常見的需求,一般阻止事件的默認行爲,均可以在事件方法內調用事件對象的preventDefault()方法。例如:

var myDiv = document.getElementById('myDiv');
   myDiv.onclick = function(event){
       //event即事件對象
       event.preventDefault();
   }

再好比阻止事件冒泡,即調用stopPropagation()。例如如下一個示例:

<!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>
      #parent,#child{
        margin: 0;padding: 0;
      }
      #parent{
        width: 200px;
        height: 200px;
        text-align: center;
        line-height: 200px;
        background-color: #ff0000;
      }
      #child{
        width: 100px;
        height: 100px;
        text-align: center;
        line-height: 100px;
        background-color: #00ff00;
      }
    </style>
  </head>
  <body>
    <div id="parent">
        父元素
        <div id="child">子元素</div>
    </div>
    <script>
      parent.onclick = function(){
         alert('點擊了父元素!')
      }
      child.onclick = function(e){
        // e.stopPropagation();
        alert('點擊了子元素!')
      }
    </script>
  </body>
</html>

若是不對子元素調用stopPropagation(),那麼父元素的事件就會傳播到子元素上,也就是說會彈出兩個彈出框,父元素的事件冒泡到了子元素上,而若是調用該方法,則代表父元素的事件被阻止冒泡了,結果點擊子元素也就只彈出一個彈出框提示"點擊了子元素",這是在原生當中的用法,而vue則提供了事件修飾符。
事件修飾符以點開頭加上指令後綴。包含.stop,.prevent,.capture,.self,.once,.passive等事件修飾符。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
    <style>
        #parent{
            width: 200px;
            height: 200px;
            background-color: #ff0000;
            text-align: center;
            line-height: 200px;
        }
        #child{
            width: 100px;
            height: 100px;
            background-color: #00f000;
            text-align: center;
            line-height: 100px;
            display: inline-block;
        }
    </style>
  </head>
  <body>
    <div id="app">
       <div id="parent" @click="clickParent">
           父元素
           <div id="child" @click="clickChild">子元素</div>
           <!--<div id="child" @click.stop="clickChild">子元素</div> -->
       </div>
       <p>{{ count }}</p>
    </div>
    <script>
      var vm = new Vue({
        data:{
            count:0
        },
        methods:{
            clickParent(){
                this.count++;
            },
            clickChild(){
                this.count = 4;
            }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

對比加了.stop和不加.stop的效果以下圖所示:

//加了

圖片描述

//不加

圖片描述

你能夠狠狠點擊此處具體示例查看效果。

.prevent事件修飾符其實也就是阻止事件的默認行爲,好比:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
     <form action="" method="post">
         <input type="text" placeholder="請輸入帳號">
         <input type="password" placeholder="請輸入密碼">
         <input type="submit" value="提交" @click.prevent="submit">
     </form>
    </div>
    <script>
      var vm = new Vue({
        data: {
         
        },
        methods: {
            submit(){
                alert('阻止了表單提交');
            }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

嘗試將.prevent事件修飾符去掉,你會發現當你點擊了提交按鈕提交的時候,頁面會重載。你能夠狠狠點擊此處具體示例進行查看。

.capture修飾符的做用就是讓元素最早執行事件,若是有多個元素擁有該事件修飾符,那麼則由外到內依次觸發該事件。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <div @click.capture="doWhat">
        點擊我
        <p @click.capture="doWhat">
          <span @click="doWhat">請點擊我</span>
          <br>
          點擊我啊
        </p>
      </div>
      <span>{{ count }}</span>
    </div>
    <script>
      var vm = new Vue({
        data: {
          count: 0
        },
        methods: {
          doWhat() {
            this.count++;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

當點擊最外層的div元素時,觸發最外層的事件,當點擊到p元素(不是span元素),則先執行div的事件,而後再執行p元素,所以count的值就會相加兩次。同理,點擊span元素,則相加三次。你能夠狠狠點擊此處具體示例查看效果。

.self事件修飾符在只有event.target的值爲自身元素的時候纔會觸發,相似.stop事件修飾符,該事件修飾符阻止了事件的冒泡,由於event.target的值始終只會有一個值。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <div @click.self="doWhat($event)">
        點擊我
        <p @click.self="doWhat($event)">
          <span @click="doWhat($event)">請點擊我</span>
          <br>
          點擊我啊
        </p>
      </div>
      <span>{{ count }}</span>
    </div>
    <script>
      var vm = new Vue({
        data: {
          count: 0
        },
        methods: {
          doWhat(e) {
              console.log(e.target)
            this.count++;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

若是點擊div元素,那麼event.target的值就是div元素,所以也就觸發該元素的事件,count的值就加1,同理,點擊pspan元素就是同樣的道理,你能夠狠狠點擊此處具體示例查看效果。

.once事件修飾符只容許事件執行一次。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:click.once="clickMethod">click me!</button>
      <p>{{ count }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          count:0
        },
        methods: {
          clickMethod: function() {
            this.count++;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

運行點擊按鈕,咱們會發現count的值只會加1,無論點擊按鈕多少次,都沒有用,這也就是說事件只會執行一次。你能夠狠狠的點擊此處具體示例查看效果。

.passive事件修飾符主要是爲了提高頁面滑動的性能,主要是對passive事件監聽器的一個封裝,要理解這個事件修飾符,咱們須要知道原生的Passive Event Listeners。這是chrome提出的一個新的瀏覽器特性:Web開發者經過一個新的屬性passive來告訴瀏覽器,當前頁面內註冊的事件監聽器內部是否會調用preventDefault函數來阻止事件的默認行爲,以便瀏覽器根據這個信息更好地作出決策來優化頁面性能。當屬性passive的值爲true的時候,表明該監聽器內部不會調用preventDefault函數來阻止默認滑動行爲,Chrome瀏覽器稱這類型的監聽器爲被動(passive)監聽器。目前Chrome主要利用該特性來優化頁面的滑動性能,因此Passive Event Listeners特性當前僅支持mousewheel/touch相關事件。更多詳情參閱。咱們來看一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      section {
        width: 100%;
        height: 500px;
      }
      #sec-1 {
        background-color: #ff0000;
      }
      #sec-2 {
        background-color: #00ff00;
      }
      #sec-3 {
        background-color: #0000ff;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <section
        v-for="(item,index) in items"
        :key="index"
        :id="'sec-' + (index + 1)"
        @mousewheel="onScroll"
      >
        {{ item.content }}
      </section>
    </div>
    <script>
      var vm = new Vue({
        data: {
          items: [
            { content: "頁面內容一" },
            { content: "頁面內容二" },
            { content: "頁面內容三" }
          ]
        },
        mounted() {},
        methods: {
          onScroll() {
            console.log("頁面滾輪滾動!");
            //若是去掉註釋,加了passive事件修飾符,頁面還能流暢的滑動,可是若是不加,就不會流暢的滑動了
            // for (let i = 0; i < 1; i--) {
            //   console.log(i);
            // }
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

如以上註釋所示,若是給section加了passive事件修飾符,而且將註釋去掉,咱們會發現頁面仍是能流暢的滑動的。你能夠狠狠點擊此處具體示例查看效果。

事件修飾符也能夠組合使用,但組合的順序很是重要,好比@click.self.prevent@click.prevent.self徹底是兩個不一樣的概念,前者會阻止event.target的值指向的那個元素的全部點擊效果,由於是先執行.prevent事件修飾符,即阻止了該元素的全部點擊事件。然後者則只是阻止了event.target的值指向的那個元素的點擊事件,而不會阻止默認事件的執行。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <div @click="clickDiv1">
        <a href="https://www.baidu.com/"  @click.prevnt.self="clickA">
          <div @click="clickDiv2">點擊我</div>
        </a>
        <!-- 這兩個事件修飾符互換順序是不一樣的效果的:<a href="https://www.baidu.com/"  @click.self.prevent="clickA">
          <div @click="clickDiv2">點擊我</div>
        </a> -->
      </div>
    </div>
    <script>
      var vm = new Vue({
        data: {
          
        },
        methods: {
          clickDiv1(){
            alert('點擊了div1!');
          },
          clickA(){
            alert('點擊了a標籤!')
          },
          clickDiv2(){
            alert('點擊了div2!')
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

此外,值得注意的就是,不能將.passive.prevent事件修飾符組合在一塊兒使用。不然瀏覽器會給出一個警告,以下圖所示:

圖片描述

在監聽鍵盤事件時,每每須要監聽鍵盤的鍵值,vue則提供了按鍵修飾符,容許爲鍵盤事件添加鍵值。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
        <button v-on:keyup.13="clickMethod($event)">鍵盤按鍵事件</button>
        <p>你按下的鍵值爲:{{ count }}</p>
    </div>
    <script>
      var vm = new Vue({
        data:{
            count:0
        },
        methods:{
            clickMethod(e){
                console.log(e.keyCode);
                this.count = e.keyCode;
            }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

13是鍵盤上enter鍵的鍵值,一般咱們不容易記住鍵盤的鍵值,這時候vue提供了別名。如以上的鍵盤事件能夠改寫爲:

v-on:keyup.enter //@keyup.enter

目前已有的所有按鍵別名有.enter,.tab,.delete,.esc,.space,.up,.down,.left,.right,固然咱們還能夠經過全局去自定義別名。
好比設置數字鍵0(字母鍵的正上方的數字鍵,鍵值爲48)的別名,寫法如:Vue.config.keyCodes.num0 = 48。如示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:keyup.num0="clickMethod($event)">鍵盤按鍵事件,鼠標定位到按鈕上</button>
      <p>你按下的鍵值爲:{{ count }}</p>
    </div>
    <script>
      Vue.config.keyCodes.num0 = 48;
      var vm = new Vue({
        data: {
          count: 0
        },
        methods: {
          clickMethod(e) {
            console.log(e.keyCode);
            this.count = e.keyCode;
          }
        }
      });

      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊具體示例查看效果。

也能夠經過直接將keyboardEvent.key暴露的任意有效鍵名轉換成kebab-case(短橫線命名)來做爲修飾符,好比鍵盤上的PageDown鍵能夠寫成page-down,PageUp能夠寫成page-up,End能夠寫成end等。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:keyup.end="clickMethod($event)">鍵盤按鍵事件,鼠標定位到按鈕上</button>
      <p>你按下的鍵值爲:{{ count }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          count: 0
        },
        methods: {
          clickMethod(e) {
            console.log(e.keyCode);
            this.count = e.keyCode;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

將鼠標定位到按鈕上,按住鍵盤上的end鍵,查看效果。你能夠狠狠點擊此處具體示例查看效果。

值得注意的就是,有一些按鍵(.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」),這四個鍵一般是和其餘常規按鍵一塊兒組合使用,也就是說只有當按下這四個系統修飾鍵,再按下相應的常規鍵,才能觸發鍵盤事件,並且這四個鍵單獨使用是沒有效果的。要想讓四個鍵單獨有效果,那麼你就只能使用keyCode
好比@keyup.ctrl你應該寫成@keyup.17這樣纔會單獨按下ctrl鍵,發生鍵盤事件,產生效果。因此,咱們只能像下面那樣使用:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:keyup.shift.num0="clickMethod($event)">鍵盤按鍵事件,鼠標定位到按鈕上</button>
      <p>你按下的鍵值爲:{{ count }}</p>
    </div>
    <script>
      Vue.config.keyCodes.num0 = 48;
      var vm = new Vue({
        data: {
          count: 0
        },
        methods: {
          clickMethod(e) {
            console.log(e.keyCode);
            this.count = e.keyCode;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

鼠標光標定位到按鈕上,按下shift鍵,再同時按下數字鍵0(左上字母鍵對應的數字鍵0),就能看到效果呢。你能夠狠狠點擊此處具體示例查看效果。

固然咱們也能夠將系統修飾符組合使用到非鍵盤事件,好比click事件中,以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button v-on:click.ctrl="clickMethod($event)">鍵盤按鍵事件,鼠標定位到按鈕上</button>
      <p>事件名爲:{{ eventname }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          eventname: 0
        },
        methods: {
          clickMethod(e) {
            this.eventname = e.type;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

鼠標定位到按鈕上,首先單獨點擊按鈕是沒有效果的,須要按住鍵盤上的ctrl鍵,而後再點擊按鈕,才能產生效果。你能夠狠狠點擊此處具體示例查看效果。

由此看來,單獨使用.exact修飾符,就表示只要按下了系統修飾鍵,就不會執行點擊事件。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button @click.exact="clickMethod($event)">鍵盤按鍵事件,鼠標定位到按鈕上</button>
      <p>你按下的鍵值爲:{{ count }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          count: 0
        },
        methods: {
          clickMethod(e) {
            this.count = e.type;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

當按住鍵盤上的ctrl,shift,meta,alt鍵,而後再點擊按鈕就不會有效果。你能夠狠狠點擊此處具體示例查看效果。

除了經常使用鍵盤修飾符以及系統修飾符以外,vue還提供了鼠標按鈕修飾符,用於指定鼠標哪一個按鍵按下才會執行事件。鼠標按鈕修飾符有.left,.middle,.right。以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-on</title>
  </head>
  <body>
    <div id="app">
      <button @click.middle="clickMethod($event)">按鈕</button>
      <p>鼠標按鈕按住中間的滾輪鍵觸發:{{ event }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          event:""
        },
        methods: {
          clickMethod(e) {
            this.event = e.type;
          }
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

用鼠標滾輪鍵點擊按鈕,就能夠查看效果,你能夠狠狠點擊此處具體示例查看效果。

12.表單輸入綁定

vue能夠經過v-model指令在<input>,<select>,<textarea>這三種表單元素上建立數據雙向綁定,這個指令會根據表單控件類型選擇正確的方法來更新元素。v-model只不過是一個語法糖,經過監聽用戶輸入事件從而達到更新數據的目的,這在一些前端極端場景中尤其重要。

在使用v-model指令時,須要注意它會忽略全部表單元素中的valued,selected,checked等特性的初始值,而將vue實例的data對象做爲數據初始值,因此在使用過程當中須要在data選項中聲明初始值。

一個最簡單的示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <input type="text" v-model="message" />
      <p>綁定的數據:{{ message }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          message: "hello,vue.js!"
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

當用戶在輸入框中輸入數據的時候,對應綁定在data選項中的數據就會發生改變。你能夠狠狠點擊此處具體示例查看效果。

還能夠綁定在textarea標籤上,如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <textarea type="text" v-model="message"></textarea>
      <p>綁定的數據:{{ message }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          message: "hello,vue.js!"
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

綁定到單個複選框示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <input type="checkbox" v-model="isChecked">
      <p>是否選中:{{ isChecked }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
            isChecked: false
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

除此以外,還能夠將多個複選框綁定到一個數組,以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <input type="checkbox" id="name" value="eveningwater" v-model="checkArr"/>
      <label for="name">eveningwater</label>
      <input type="checkbox" id="sex" value="male" v-model="checkArr"/>
      <label for="sex">male</label>
      <input type="checkbox" id="skill" value="write code" v-model="checkArr" />
      <label for="skill">write code</label>
      <p>選中的值:{{ checkArr }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
            checkArr: []
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

還能夠綁定單選按鈕,以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <input type="radio" id="first" value="first" v-model="checkedValue">
      <label for="first">first</label>
      <input type="radio" id="second" value="second" v-model="checkedValue" />
      <label for="second">second</label>
      <p>選中的值:{{ checkedValue }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
            checkedValue: []
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

綁定到select的示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <select v-model="message">
          <option disabled value="">請選擇值</option>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
      </select>
      <p>選中的值:{{ message }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          message: ""
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

在這裏須要注意的就是,若是v-model綁定的初始值未匹配任何選項,那麼select元素就會渲染爲'未選中狀態',在IOS中,這會致使用戶沒法選中第一項,由於並無觸發change事件,所以更推薦像以上那樣提供一個默認禁用的選項。

select多選時:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <select v-model="message" multiple>
          <option disabled value="">請選擇值</option>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
      </select>
      <p>選中的值:{{ message }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          message: []
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

還能夠用v-for指令渲染動態選項:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <select v-model="selected">
        <option v-for="(option,index) in selects" :value="option.value">{{
          option.text
        }}</option>
      </select>
      <p>選中的值:{{ selected }}</p>
    </div>
    <script>
      var vm = new Vue({
        data: {
          selected: "first",
          selects: [
            {
              value: "first",
              text: "第一項"
            },
            {
              value: "second",
              text: "第二項"
            },
            {
              value: "third",
              text: "第三項"
            }
          ]
        }
      });
      vm.$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

對於單選按鈕,複選框,選擇框來講,v-model指令一般綁定的是靜態字符串,對於複選框,還能夠是布爾值。它們的value值,咱們也是能夠綁定到vue實例的data選項上的,並且綁定的值不必定是靜態字符串。一個完整的示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .form-radio,
      .form-checkbox,
      .form-select {
        width: 600px;
        padding: 16px 8px;
        background-color: #e2e3e4;
        margin: 15px auto;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="form-radio">
        <input type="radio" id="male" v-model="sex" value="男" />
        <label for="male">男</label>
        <input type="radio" id="female" v-model="sex" value="女" />
        <label for="female">女</label>
        <p>你選擇的是:{{ sex }}</p>
        <input type="radio" id="male" v-model="bindSex" v-bind:value="bindMale" />
        <label for="male">{{ bindMale }}</label>
        <input type="radio" id="female" v-model="bindSex" v-bind:value="bindFemale" />
        <label for="female">{{ bindFemale}}</label>
        <p>將value值動態綁定到vue實例上:{{ bindSex }}</p>
      </div>
      <div class="form-checkbox">
        <input type="checkbox" id="checked" v-model="isChecked" />
        <label for="checked">綁定的是布爾值</label>
        <p>布爾值:{{ isChecked }}</p>
        <input
          type="checkbox"
          v-model="toggle"
          true-value="是"
          false-value="否"
        />
        <p>是否選中:{{ toggle }}</p>
      </div>
      <div class="form-select">
        <select v-model="selected">
          <option value="請選擇你喜歡作的事情" disabled
            >請選擇你喜歡作的事情</option
          >
          <option
            v-for="(option,index) in options"
            :key="index"
            :value="option.text"
            v-text="option.text"
          ></option>
        </select>
        <p>你喜歡:{{ selected }}</p>
      </div>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data: {
          bindSex: "",
          bindMale: "男",
          bindFemale: "女",
          isChecked: false,
          sex: "男",
          toggle: "是",
          selected: "",
          options: [
            {
              text: "讀書"
            },
            {
              text: "遊戲"
            },
            {
              text: "唱歌"
            }
          ]
        }
      });
      console.log(vm.toggle === "是");
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例進行查看。

在表單綁定的時候,vue也提供了3個修飾符.lazy,.number,.trim,.lazy的做用就是改變表單元素觸發的input事件,通常說來,v-model一般是在每次input事件(即用戶只要輸入內容時),將輸入框的值與綁定的數據進行同步更新,若是不想要與input事件同步,而是與change事件同步,那麼就可使用.lazy修飾符,.number修飾符的做用就是將輸入的值轉換成number類型,由於默認輸入的值時string(字符串)類型。而.trim修飾符的做用就是自動過濾掉首位的空白。示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>v-model</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .font-green {
        color: #00ff31;
      }
      .box {
        width: 400px;
        padding: 20px;
        line-height: 35px;
        margin: 20px auto;
        background-color: #e2e2e2;
      }
      .box input{
          width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="box">
        <h2>未使用<span class="font-green">.lazy</span>修飾符:</h2>
        <input type="text" v-model="notMsg" />
        <p>{{ notMsg }}</p>
      </div>
      <div class="box">
        <h2>使用<span class="font-green">.lazy</span>修飾符:</h2>
        <input type="text" v-model.lazy="msg" />
        <p>{{ msg }}</p>
      </div>
      <div class="box">
        <h2>未使用<span class="font-green">.number</span>修飾符:</h2>
        <input type="text" v-model="notCount" />
        <p>
          {{ typeof notCount === 'string' ? '字符串值:"'+notCount+'"' : notCount }}
        </p>
      </div>
      <div class="box">
        <h2>使用<span class="font-green">.number</span>修飾符:</h2>
        <input type="text" v-model.number="count" />
        <p>
          {{ typeof count === 'number' ? '數值:' + count : count }}
        </p>
      </div>
      <div class="box">
        <h2>未使用<span class="font-green">.trim</span>修飾符:</h2>
        <input type="text" v-model="notTrim" />
        <p>{{ notTrim }}</p>
      </div>
      <div class="box">
        <h2>使用<span class="font-green">.trim</span>修飾符:</h2>
        <input type="text" v-model.trim="trim" />
        <p>{{ trim }}</p>
      </div>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data: {
          notMsg: "未使用.lazy修飾符",
          msg: "使用.lazy修飾符",
          notCount: "1",
          count: 1,
          notTrim: "首尾若是有空白,值就會有空白 ",
          trim: "首尾若是有空白,值也不會有空白,由於.trim消除了空白"
        }
      });
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例進行查看。

13.組件基礎

組件是可複用的vue實例,除了el選項不一致之外,其他的data,prop,computed,methods,watch等選項都是同樣的,而且組件都須要一個名字。一個基本的示例以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <button-num></button-num>
    </div>
    <script>
      Vue.component("button-num", {
        template: `<button v-on:click="num++">You click me {{ num }} times!</button>`,
        data: function() {
          return {
            num: 0
          };
        }
      });
      var vm = new Vue().$mount(document.getElementById("app"));
    </script>
  </body>
</html>

以上定義了一個簡單的示例,經過這個示例咱們能夠知道組件是可複用的vue實例所以能夠接收與new Vue()同樣的選項參數,組件的名字就是button-num,經過Vue.component()來全局註冊一個組件,這個方法接受2個參數,第一個參數就是組件名,第二個參數則是組件配置對象,配置對象的選項與vue實例是一致的。
你能夠狠狠點擊此處具體示例進行查看。

組件還能夠複用,當基礎組件要使用的多的時候,可使用v-for來渲染。以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <button-num></button-num>
      <button-num></button-num>
      <button-num v-for="n in 5" :key="n"></button-num>
    </div>
    <script>
      Vue.component("button-num", {
        template: `<button v-on:click="num++">You click me {{ num }} times!</button>`,
        data: function() {
          return {
            num: 0
          };
        }
      });
      var vm = new Vue().$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠點擊這裏具體示例查看該示例。在這裏,咱們須要注意一點,組件中的data選項必須是一個函數,若是不是一個函數,控制檯則會彈出一個警告,以下圖所示:

圖片描述

一個大型的網站項目,好比一個博客,咱們能夠將博客分割成多個小組件,好比導航組件,好比內容組件等等,這由許多個小組件組織起來,就成了一個SPA單頁應用。以下圖所示:

爲了可以在模板中使用,須要先註冊這些組件以便vue根實例來識別。註冊組件有兩種方式:全局註冊局部註冊,在這裏,咱們都是經過Vue.component()來全局註冊的。
全局註冊組件以後,就表示你能夠在你被註冊以後任何vue根實例中使用,也包括全部子組件中,到目前爲止,咱們不須要考慮局部註冊。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
        <blog-component title="博客組件的標題"></blog-component>
    </div>
    <script>
      Vue.component("blog-component", {
        template: `<h3>{{title}}</h3>`,
        props:['title']
      });
      var vm = new Vue().$mount(document.getElementById("app"));
    </script>
  </body>
</html>

在上例中,咱們能夠像訪問data選項同樣訪問props傳遞的值,props能夠接收任意類型傳遞的值,你能夠狠狠點擊此處具體示例進行查看。

一個prop被註冊以後,咱們其實能夠像下面這樣把數據做爲一個自定義的特性傳遞:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
        <blog-component title="博客組件的標題1"></blog-component>
        <blog-component title="博客組件的標題2"></blog-component>
        <blog-component title="博客組件的標題3"></blog-component>
    </div>
    <script>
      Vue.component("blog-component", {
        template: `<h3>{{title}}</h3>`,
        props:['title']
      });
      var vm = new Vue().$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看該示例。

然而,在一個典型的應用當中,咱們須要傳遞的數據不多是如此簡單的一個字符串,有多是一個對象,甚至是一個數組。這樣,以上的示例,咱們須要如此修改:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <blog-component v-for="(blog,index) in blogData" :key="index" v-bind:title="blog.title"></blog-component>
    </div>
    <script>
      Vue.component("blog-component", {
        template: `<h3>{{title}}</h3>`,
        props: ["title"]
      });
      var vm = new Vue({
        data: {
          blogData: [
            { title: "博客組件的標題1", content: "博客組件的內容1" },
            { title: "博客組件的標題2", content: "博客組件的內容2" },
            { title:"博客組件的標題3",content:"博客組件的內容3"}
          ]
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

如上所示,你應該會發現咱們使用v-bind來動態傳遞prop,這在你不知道要渲染的具體內容,尤爲是當渲染一個API數據接口時是很是有用的。到目前爲止,prop須要瞭解的就只有這些呢。你能夠點擊此處具體示例查看該示例。

但在一篇博文當中,咱們不只僅是須要添加標題,最起碼咱們須要渲染博文的內容,所以你的模板內容就須要作修改:

template:`<h3>{{ title }}</h3><div v-html="content"></div>`;

然而若是這樣寫的話,vue會顯示一個錯誤,every component must have a single root element (每一個組件必須只有一個根元素),所以咱們須要用一個根元素來包裹,以下:

template:`<div class="blog-demo"><h3>{{ title }}</h3><div v-html="content"></div></div>`;

當組件變得愈來愈複雜時,若是單獨定義一個prop,這會看起來愈來愈複雜。一篇博文可能會包含發佈日期,評論等,咱們如此作法,代碼看起來也不夠簡潔:

<blog-component v-for="(blog,index) in blogData" :key="index" v-bind:title="blog.title" :content="content" :date="date" :recommen="commen"></blog-component>

重構一下這個組件,讓它接收一個單獨的prop,以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <blog-component v-for="(blog,index) in blogData" :key="index" :blog="blog"></blog-component>
    </div>
    <script>
      Vue.component("blog-component", {
        props: ["blog"],
        template: `
            <div class="blog-demo">
                <h3>{{blog.title}}</h3>
                <div v-html="blog.content"></div>
                <p>
                    <span class="date">{{ blog.date }}</span>
                    </span class="recommen">{{ blog.commen }}</span>
                </p>
            </div>
        `
      });
      var vm = new Vue({
        data: {
          blogData: [
            { title: "博客組件的標題1", content: "博客組件的內容1",date:"2019年2月20日",commen:"真棒!" },
            { title: "博客組件的標題2", content: "博客組件的內容2",date:"2019年2月20日",commen:"真的好棒!" },
            { title:"博客組件的標題3",content:"博客組件的內容3",date:"2019年2月20日",commen:"真的真的很是棒!"}
          ]
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看。在開發blog組件時候,有時候,咱們不只須要創建父組件向子組件的通訊,咱們也許還須要創建子組件向父組件傳遞數據之間的通訊,這時候,咱們能夠採用自定義事件來向父組件傳遞數據。例如,咱們想要添加一個按鈕,用於改變字體的顏色,咱們能夠自定義一個顏色框,而後讓用戶選擇字體顏色來改變字體的顏色。以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <blog-component v-for="(blog,index) in blogData" :key="index" :blog="blog" @change-color="setColor($event,index)"></blog-component>
    </div>
    <script>
      Vue.component("blog-component", {
        props: ["blog"],
        template: `
            <div class="blog-demo" :style="{color:blog.color}">
                <h3>{{blog.title}}</h3>
                <div v-html="blog.content"></div>
                <p>
                    <span class="date">{{ blog.date }}</span>
                    </span class="recommen">{{ blog.commen }}</span>
                </p>
                <div class="setFontColor">
                    <input type="color" v-model="colorValue" />
                    <button type="button" @click="$emit('change-color',colorValue)">改變字體顏色</button>
                </div>
            </div>
        `,
        data:function(){
            return{
                colorValue:""
            }
        }
      });
      var vm = new Vue({
        data: {
          blogData: [
            { title: "博客組件的標題1", content: "博客組件的內容1",date:"2019年2月20日",commen:"真棒!" },
            { title: "博客組件的標題2", content: "博客組件的內容2",date:"2019年2月20日",commen:"真的好棒!" },
            { title:"博客組件的標題3",content:"博客組件的內容3",date:"2019年2月20日",commen:"真的真的很是棒!"}
          ]
        },
        methods:{
            setColor:function(color,index){
                this.$set(this.blogData[index],'color',color);
            }
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

經過以上示例咱們能夠看出,父組件經過像處理native DOM事件同樣經過v-on指令監聽子組件的任意事件,而後子組件經過$emit()方法來自定義一個事件,這個方法接收兩個參數,第一個參數就是自定義的事件名(在這裏是change-color),第二個參數則是要傳遞的參數,在這裏,傳遞了顏色選擇器選擇的顏色值colorValue,而後父組件使用這個事件,有了這個自定義的v-on:change-color監聽器,父組件就能夠爲數組的每一項對象新增一個color屬性,經過將設置style,將color屬性綁定到style標籤內,而後父組件就能夠更新數據,並渲染結果了。你能夠狠狠點擊此處具體示例來查看。

經過自定義事件咱們還能夠實如今組件上使用v-model指令,以下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <ew-input v-model="msg"></ew-input>
      <p>{{ msg }}</p>
    </div>
    <script>
      Vue.component("ew-input", {
        props: ["value"],
        template: `
          <input type="text" :value="value"  v-on:input="$emit('input',$event.target.value)" />
        `
      });
      var vm = new Vue({
        data: {
          msg: "在組件上使用v-model指令"
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看。

和HTML元素同樣,一般咱們也須要向組件傳遞內容,這時候,咱們可使用插槽。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .ew-alert{
            position:absolute;
            left: 50%;
            top: 50%;
            min-width: 300px;
            min-height: 200px;
            background-color: #ffffff;
            box-shadow: 0 0 15px #939393;
            text-align: center;
            transform: translate(-50%,-50%);
            border-radius: 15px;
        }
        .ew-alert > .ew-alert-title{
            padding: 10px 0;
        }
        .ew-alert > .ew-alert-content{
            padding: 8px 9px;
        }
    </style>
  </head>
  <body>
    <div id="app">
      <ew-alert title="彈出框標題"><p>{{ msg }}</p></ew-alert>
    </div>
    <script>
      Vue.component("ew-alert", {
        props: ["title"],
        template: `
          <div class="ew-alert">
            <div class="ew-alert-title">{{ title }}</div>
            <div class="ew-alert-content"><slot></slot></div>
          </div>
        `
      });
      var vm = new Vue({
        data: {
          msg: "彈出框主題內容"
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看。

有時候,咱們須要動態切換組件,也就是在作一個選項卡組件的時候,咱們把每個導航對應一個導航組件,這時候就須要動態切換組件。若是要動態切換組件,咱們可使用一個特性is來實現。例如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .tab-component-demo{
          width: 900px;
          margin: 15px auto;
          padding: 15px 10px;
      }
      button {
        outline: none;
        border: 1px solid #ebebeb;
        background-color: transparent;
        display: inline-block;
        padding: 6px 7px;
        text-align: center;
        line-height: 1;
        transition: all 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
        cursor: pointer;
        font-size: 16px;
        border-radius: 4px;
      }
      button:hover,
      button:active {
        border-color: #58e9fc;
        background-color: #0abce9;
        color: #ffffff;
      }
      .ew-page{
          border: 1px solid #999;
          padding: 15px 8px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="tab-component-demo">
        <button
          v-for="(tab,index) in tabs"
          :key="index"
          @click="currentTab = tab"
        >
          {{ tab }}
        </button>
        <component :is="currentTabComponent"></component>
      </div>
    </div>
    <script>
      Vue.component("tab-home", {
        template: `
          <div class="ew-page">
           home page!
          </div>
        `
      });
      Vue.component("tab-list", {
        template: `
          <div class="ew-page">
           list page!
          </div>
        `
      });
      Vue.component("tab-contact", {
        template: `
          <div class="ew-page">
          contact page!
          </div>
        `
      });
      var vm = new Vue({
        data: {
          currentTab: "home",
          tabs: ["home", "list", "contact"]
        },
        computed: {
          currentTabComponent: function() {
            return "tab-" + this.currentTab.toLowerCase();
          }
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看。經過這個示例,咱們能夠知道is綁定的值currentTabComponent能夠是一個組件名,但事實上,咱們還能夠綁定一個組件的選項對象,以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .tab-component-demo {
        width: 900px;
        margin: 15px auto;
        padding: 15px 10px;
      }
      button {
        outline: none;
        border: 1px solid #ebebeb;
        background-color: transparent;
        display: inline-block;
        padding: 6px 7px;
        text-align: center;
        line-height: 1;
        transition: all 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
        cursor: pointer;
        font-size: 16px;
        border-radius: 4px;
      }
      button:hover,
      button:active {
        border-color: #58e9fc;
        background-color: #0abce9;
        color: #ffffff;
      }
      .ew-page {
        border: 1px solid #999;
        padding: 15px 8px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="tab-component-demo">
        <button
          v  v-for="(tab,index) in tabs"
          :key="index"
          @click="currentTab = tab"
        >
          {{ tab.name.slice(4) }}
        </button>
        <component :is="currentTab.component"></component>
      </div>
    </div>
    <script>
      var tabs = [
        {
          name: "tab-home",
          component: {
            template: <div class="ew-page">home page! </div>`
          }
        },
        {
          name: "tab-list",
          component:{
            template: `<div class="ew-page">list page!</div>`
          }
        },
        {
          name: "tab-contact",
          component:{
            template: `<div class="ew-page">contact page!</div>`
          }
        }
      ];
      var vm = new Vue({
        data: {
          currentTab: tabs[0],
          tabs: tabs
        }
      }).$mount(document.getElementById("app"));
    </script>
  </body>
</html>

你能夠狠狠點擊此處具體示例查看。

在解析DOM模板時,咱們還須要注意一點,那就是有些HTML元素,諸如<ul>,<ol>,<table>,<select>,對於哪些元素出如今其內部是有嚴格限制的,而有些元素,諸如<li>,<tr>,<option>,只能出如今特定的元素內部。這會致使咱們在使用這些有約束的元素時出現問題,如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
        <table>
            <ew-div></ew-div>
        </table>
    </div>
    <script>
      Vue.component("ew-div", {
        template: `
           <div>測試</div>
        `
      });
      var vm = new Vue().$mount(document.getElementById("app"));
    </script>
  </body>
</html>

咱們能夠打開瀏覽器控制檯,看到以上自定義的ew-div組件中的元素,被渲染到最外部去了,以下圖所示:

圖片描述
你能夠狠狠點擊此處具體示例查看這個問題。

幸運的是,你可使用is解決這個問題。如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
        <table>
            <tr is="ew-div"></tr>
        </table>
    </div>
    <script>
      Vue.component("ew-div", {
        template: `
           <div>測試</div>
        `
      });
      var vm = new Vue().$mount(document.getElementById("app"));
    </script>
  </body>
</html>

此時再看瀏覽器控制檯,你會發現自定義的組件已經被渲染在table內部,以下圖所示:

圖片描述

你能夠狠狠點擊此處具體示例查看。

固然,若是咱們從以下來源使用模板的話,這條限制是不存在的。

1.字符串(如:template:'')
2.單文件組件(.vue)
3.<script type="text/x-template"></script>

到目前爲止,須要瞭解的組件基礎知識大概就只有這些呢,接下來是深刻組件的瞭解。

四.組件深刻

1.組件註冊

在註冊一個組件的時候,咱們須要爲組件起一個名字,好比全局註冊的時候,咱們已經知道了

Vue.component('my-component-name',{});

也就是Vue.component()的第一個參數。在給組件起名的時候,若是不是字符串模板或者單文件組件,直接在DOM中使用組件,組件名是有規範的(字母全小寫,而且單詞之間用連字符鏈接)。你能夠查閱API風格指南

這也就是說定義組件名有兩種方式,即(kebab-case)短橫線分隔命名(PascalCase)首字母大寫命名。當使用短橫線命名時,咱們在使用組件的時候,也必須是採用短橫線命名,而使用首字母大寫命名則既能夠採用首字母大寫也能夠採用短橫線命名,固然在DOM模板而非字符串模板或者單文件組件中,也仍是隻能採用短橫線命名。

到目前爲止,咱們只是用Vue.component來全局註冊一個組件,如:

Vue.component('com-a',{})
Vue.component('com-b',{})
Vue.component('com-c',{})

而後,你就能夠在new Vue()根實例內的任何組件中使用這三個組件,好比:

//js
new Vue(el:"#app")
//html
<div id="app">
    <com-a>
      <com-b></com-b>
    </com-a>
    <com-b>
      <com-c></com-c>
    </com-b>
    <com-c>
      <com-a></com-a>
    </com-c>
</div>

這每每是不夠理想的,好比在一個經過webpack構建的系統應用中,全局註冊全部的組件,則意味着即使你再也不使用一個組件了,但組件仍然被包含在最終構建的結果中,這也就增長了一些無謂的JavaScript代碼。

不過,咱們能夠局部註冊組件。局部註冊也就是將一個組件看成普通的JavaScript對象來定義。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
        <component-a></component-a>
        <component-b></component-b>
    </div>
    <script>
      var componentA = {
          template:`<div>a</div>`
      },
      componentB = {
          template:`<div>b</div>`
      }
      var vm = new Vue(
          {
              el:"#app",
              components:{
                  'component-a':componentA,
                  'component-b':componentB
              }
          }
      )
    </script>
  </body>
</html>

經過JavaScript定義一個組件對象,而後在vue實例中添加components選項,components選項的屬性名就是組件名,屬性值就是這個組件的選項對象。你能夠狠狠點擊此處具體示例查看效果。

可是局部註冊組件是沒法在其子組件中使用另外一局部註冊的組件的,若是想要這樣的效果,那麼代碼須要寫成下面那樣:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>component</title>
  </head>
  <body>
    <div id="app">
      <component-a></component-a>
      <component-b></component-b>
    </div>
    <script>
      var componentA = {
          template: `<div>a</div>`
        },
        componentB = {
          template: `<div>b<component-a></component-a></div>`,
          components:{
              'component-a':componentA
          }
        };
      var vm = new Vue({
        el: "#app",
        components: {
          "component-a": componentA,
          "component-b": componentB
        }
      });
    </script>
  </body>
</html>

也就是往局部註冊組件對象中添加一個components選項,註冊須要添加的局部註冊的組件而已。你能夠狠狠點擊此處具體示例查看效果。

若是是使用es6與babel以及webpack,那麼代碼,則更像是以下:

import componentA from './componentA.vue'

export default{
  components:{ componentA }
}

在對象中放一個componentA其實也就是componentA:componentA的縮寫,也就是說這個變量名同時是:

用在模板中的自定義元素的名稱。
包含了這個組件的選項的變量名。

若是你還不熟悉模塊化構建,那最好仍是跳過這裏。

2.prop

因爲HTML特性是不區分大小寫的,也就是說瀏覽器會將全部的大寫字母轉換成小寫字母,這也就意味着當你使用camelCase(駝峯命名法)來爲prop定義的時候,你在子組件使用prop的時候須要使用等價的kebab-case(短橫線命名)來命名,好比:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop</title>
    <style>
        .ew-button {
            outline: none;
            border: 1px solid transparent;
            display: inline-block;
            border-radius: 4px;
            font-size: 14px;
            text-align: center;
            transition: color 0.2s linear, backgroud-color 0.1s linear,
                border-color 0.1s linear, box-shadow 0.2s linear;
            cursor: pointer;
            border-color: #f3f3f3;
            color: #a9adb1;
            background-color: #ffffff;
            padding: 5px 15px 6px;
        }

        .ew-button-success {
            background-color: #2196ff;
            border-color: #4ca4f1;
            color: #ffffff;
        }

        .ew-button-success:hover,
        .ew-button-success:active {
            box-shadow: 0 0 3px #943321;
            opacity: 0.8;
        }
    </style>
</head>

<body>
    <div id="app">
        <ew-button ew-type="success">{{ content }}</ew-button>
    </div>
    <script>
        var ewButton = {
            props: ["ewType"],
            template: `<button type="button" :class="'ew-button-'+ ewType" class="ew-button"><slot></slot></button>`
        };
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    content: "success"
                };
            },
            components: {
                ewButton: ewButton
            }
        });
    </script>
</body>

</html>

在這裏,咱們是使用的局部註冊一個組件,也就是用一個對象表示一個組件,而後在Vue實例中添加components選項來註冊這個組件,組件名雖然使用的camelCase(駝峯命名法),也就是ewButton,但咱們也看到了,在使用這個組件的時候,須要使用kebab-case(短橫線命名),即ew-button來表示,一樣的,定義的prop名也是如此,在子組件中定義成ewType,但在使用的時候則是ew-prop,固然在單文件組件以及字符串模板中是不存在這個限制的,這裏是使用DOM模板來表示的。

你能夠狠狠點擊此處具體示例查看效果。

到目前爲止,咱們只看到了prop的形式只是一個字符串數組:

props:['str','num','bool','arr','obj']

可是,一般咱們想要每一個prop都有指定的值和類型,這時候,咱們能夠以對象的形式來列出prop,這些屬性的名稱和值分別表明prop名類型。以下:

props:{
  str:String,
  num:Number,
  bool:Boolean,
  arr:Array,
  obj:Object
}

若是指定的prop類型不對,那麼瀏覽器就會在控制檯報一個invalid prop的錯誤,這是vue內部實現的。

在此以前,咱們能夠看到prop有兩種用法,第一種是靜態的prop,如上例的:

<ew-button ew-type="success">{{ content }}</ew-button>

這裏的ew-type就是一個靜態的prop,但實際上,咱們還能夠用v-bind來表示一個動態的prop,以上的靜態prop,咱們就能夠表示成以下:

<ew-button v-bind:ew-type="'success'">{{ content }}</ew-button>
//或者<ew-button :ew-type="'success'">{{ content }}</ew-button>

有了v-bind,咱們就能夠爲prop綁定任意類型的值。例如:

如下是傳遞一個數值:

//即使100是靜態的,咱們也須要使用v-bind來告訴vue,這是一個JavaScript表達式,而不是字符串
<ew-button v-bind:ew-type="100">{{ content }}</ew-button>
//綁定一個變量名
<ew-button v-bind:ew-type="number">{{ content }}</ew-button>

在js代碼中,咱們就能夠如此寫:

new Vue({
    ...
    data(){
      return{
          number:100
      }
    }
    ...
})

如下是傳遞一個布爾值:

//若是不傳入值,默認就是一個布爾值true
<ew-button ew-type>{{ content }}</ew-button>
//即使false是靜態的,咱們也須要使用v-bind來告訴vue,這是一個JavaScript表達式,而不是字符串
<ew-button :ew-type="false">{{ content }}</ew-button>
/綁定一個變量名
<ew-button :ew-type="bool">{{ content }}</ew-button>

在js代碼中,咱們就能夠如此寫:

new Vue({
    ...
    data(){
      return{
          bool:false
      }
    }
    ...
})

如下是傳遞一個數組:

//即使數組是靜態的,咱們也須要使用v-bind來告訴vue,這是一個JavaScript表達式,而不是字符串
<ew-button :ew-type="[1,2,3]">{{ content }}</ew-button>
/綁定一個變量名
<ew-button :ew-type="arr">{{ content }}</ew-button>

在js代碼中,咱們就能夠如此寫:

new Vue({
    ...
    data(){
      return{
          arr:[1,2,3]
      }
    }
    ...
})

如下是傳遞一個對象:

//即使對象是靜態的,咱們也須要使用v-bind來告訴vue,這是一個JavaScript表達式,而不是字符串
<ew-button :ew-type="{ name:'eveningwater',sex:'male'}">{{ content }}</ew-button>
/綁定一個變量名
<ew-button :ew-type="obj">{{ content }}</ew-button>

在js代碼中,咱們就能夠如此寫:

new Vue({
    ...
    data(){
      return{
          obj:{
            name:"eveningwater",
            sex:"male"
          }
      }
    }
    ...
})

若是你想將一個對象的全部屬性都做爲prop傳入,那麼你可使用不帶參數的v-bind來取代v-bind:propName。例如:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop</title>
</head>

<body>
    <div id="app">
        <ew-div v-bind="item"></ew-div>
        //等價於<ew-div v-bind:name="item.name" v-bind:sex="item.sex"></ew-div>
    </div>
    <script>
        Vue.component('ew-div',{
            props:['name','sex'],
            template:`<div class="myDiv"><h1>{{ name }}</h1><p v-text="sex"></p></div>`,

        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                   item:{
                       name:"eveningwater",
                       sex:"male"
                   }
                };
            }
        });
    </script>
</body>

</html>

以上定義的v-bind="item"實際上就等價於v-bind:name="item.name"v-bind:sex="item.sex",因此,咱們才能夠在子組件的props裏獲取到namesex,而且將props的值綁定到元素中去。你能夠狠狠點擊此處具體示例查看效果。

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

這也就是說一旦父組件更新了,那麼子組件也就會馬上刷新爲最新的值,這也意味着你不該該修改一個子組件的prop,若是你如此作了,瀏覽器會發出一個警告,這是vue內部實現的。來看一個示例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop</title>
</head>

<body>
    <div id="app">
        <ew-div :content="content"></ew-div>
    </div>
    <script>
        Vue.component('ew-div',{
            props:['content'],
            template:`<div class="myDiv"><p>{{ content }}</p><button type="button" @click="updateContent">改變content的值</button></div>`,
            methods:{
                updateContent(){
                    this.content = '修改了值,瀏覽器控制檯會給出一個警告!'
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                   content:"文檔的內容"
                };
            }
        });
    </script>
</body>

</html>

如上例,當咱們點擊了按鈕,去修改prop的值,儘管最終值發生了改變,可是瀏覽器控制檯仍是給出了一個警告信息,以下圖所示:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。實際上,這個警告信息也就是提示,不能直接在子組件中修改prop,可是咱們有兩種辦法解決這個問題。

1.咱們將父組件傳給子組件的prop做爲一個初始值,而後在子組件的data選項中定義一個新的數據,將prop的值用做其初始值,而後在綁定數據的時候就不用綁定prop,而是綁定定義在data選項中的值。以下所示:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop</title>
</head>
<body>
    <div id="app">
        <ew-div :content="content"></ew-div>
    </div>
    <script>
        Vue.component('ew-div',{
            props:['content'],
            template:`<div class="myDiv"><p>{{ curContent }}</p><button type="button" @click="updateContent">改變content的值</button></div>`,
            data(){
                return{
                    curContent:this.content
                }
            },
            methods:{
                updateContent(){
                    this.curContent = '修改了值,瀏覽器控制檯不會給出一個警告!'
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                   content:"文檔的內容"
                };
            }
        });
    </script>
</body>
</html>

如今,咱們再點擊按鈕,值發生了改變,並且瀏覽器也沒有給出警告。你能夠狠狠點擊此處具體示例查看效果。

2.若是是這個prop做爲初始值,而且要進行轉換,那麼,最好仍是使用一個計算屬性來表示。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop</title>
</head>
<body>
    <div id="app">
        <ew-div :content="content"></ew-div>
    </div>
    <script>
        Vue.component('ew-div',{
            props:['content'],
            template:`<div class="myDiv"><p>{{ curContent }}</p></div>`,
            computed:{
                curContent(){
                    return '計算屬性:' + this.content;
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                   content:"文檔的內容"
                };
            }
        });
    </script>
</body>
</html>

你能夠狠狠點擊此處具體示例查看效果。又或者,綜合以上兩個方法,修改以下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop</title>
</head>
<body>
    <div id="app">
        <ew-div :content="content"></ew-div>
    </div>
    <script>
        Vue.component('ew-div',{
            props:['content'],
            template:`<div class="myDiv"><p>{{ curContent }}</p><button type="button" @click="updateContent">改變content的值</button></div>`,
            data(){
                return{
                    newContent:""
                }
            },
            computed:{
                curContent:{
                    get(){
                        if(this.newContent){
                            return '計算屬性:' + this.newContent;
                        }else{
                            return '計算屬性:' + this.content;
                        }
                    },
                    set(newVal){
                        this.newContent = newVal;
                    }
                }           
            },
            methods:{
                updateContent(){
                    this.curContent = '修改了值,瀏覽器控制檯不會給出一個警告!'
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                   content:"文檔的內容"
                };
            }
        });
    </script>
</body>
</html>

你能夠狠狠點擊此處具體示例查看效果。

注意在JavaScript數組和對象中,因爲數組和對象是經過引用傳入的,所以修改子組件的數據可能會影響到父組件的狀態。

在使用prop的時候,咱們還能夠爲prop指定驗證要求,若是不知足驗證要求,則vue會在瀏覽器控制檯中警告你。這在對於開發一個被別人用到的組件時尤其重要。

爲了指定prop的驗證需求,你應該爲props的值指定一個帶有驗證需求的對象,而不是字符串數組。以下一個示例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>validate-prop</title>
    <style>
        .font-red {
            color: #ff1944;
        }
    </style>
</head>

<body>
    <div id="app">
        <ew-div :num-success="count1" :num-fail="count2" :str-success="str1" :str-fail="str2" :str-require="str3"
            :num-or-str-success="numorstr1" :num-or-str-fail="numorstr2" define-by-self="字符串"></ew-div>
    </div>
    <script>
        Vue.component('ew-div', {
            props: {
                numSuccess: Number,//驗證數值成功
                numFail: Number,//驗證數值失敗
                strSuccess: String,//驗證字符串成功
                strFail: String,//驗證字符串失敗
                strRequire: {
                    type: String,
                    required: true //驗證字符串必填
                },
                numOrStrSuccess: [Number, String],//驗證字符串或者數值成功
                numOrStrFail: [Number, String],//驗證字符串或者數值失敗
                defaultNum: {
                    type: Number,
                    default: 50
                },//帶有默認值的驗證
                defaultObj: {
                    type: Object,
                    default: function () {
                        return { name: "eveningwater", sex: "male" }
                    } //默認值爲對象,對象或數組的默認值必須經過一個函數返回
                },
                defineBySelf: {
                    validator: function (value) {
                        // 值必須是如下數組中的幾個
                        return [123, '字符串', [1, 2, 3], { name: "eveningwater" }].indexOf(value) > -1;
                    },
                    default: 123
                }
            },
            template: `
                <div class="myDiv">
                    <p>數值類型驗證成功:{{ numSuccess }}</p>
                    <p>須要注意的就是null和undefined會經過任何類型的驗證,除了設置required爲true。</p>
                    <p>
                        數值類型驗證失敗:{{ numFail }}
                        瀏覽器控制檯會報如此的警告:<span class="font-red">[Vue warn]: Invalid prop: type check failed for prop "numFail". 
                        Expected Number with value 123, got String with value "123".</span>。大體意思就是:
                        不可用的prop,prop'numFail'的類型驗證失敗,使用數值型的123替代字符串的"123"。
                    </p>
                    <p>字符串類型驗證成功:{{ strSuccess }}</p>
                    <p>
                        字符串類型驗證失敗:{{ strFail }}
                        瀏覽器控制檯會報如此的警告:<span class="font-red">Invalid prop: type check failed for prop "strFail". 
                            Expected String with value "124", got Number with value 124.</span>。大體意思就是:
                        不可用的prop,prop'strFail'的類型驗證失敗,使用字符串的"124"替代數值的124。
                    </p>
                    <p>
                        必填字符串(包含空字符串):{{ strRequire }}
                        若是不知足條件,好比是null,則同樣會拋出相似警告:
                        <span class="font-red">
                        [Vue warn]: Invalid prop: type check failed for prop "strRequire".
                         Expected String with value "null", got Null.     
                        </span>
                    </p>
                    <p>字符串或數值類型驗證成功:{{ numOrStrSuccess }}</p>
                    <p>
                        字符串或數值類型驗證失敗:{{ numOrStrFail }}
                        瀏覽器控制檯會報如此的警告:<span class="font-red">Invalid prop: type check failed for prop "numOrStrFail". 
                            Expected Number, String, got Object </span>。大體意思就是:
                        不可用的prop,prop'numOrStrFail'的類型驗證失敗,使用字符串,數值替代對象。
                    </p>
                    <p>帶有默認值的驗證:{{ defaultNum }}</p>
                    <p>
                        對象或數組的默認值必須以一個函數返回:
                        <span>{{ defaultObj.name }}</span><br>
                        <span>{{ defaultObj.sex }}</span>
                    </p>
                    <p>自定義驗證函數:{{ defineBySelf }}若是驗證失敗,則控制檯會提示:
                        <span class="font-red">[Vue warn]: Invalid prop: custom validator check failed for prop "defineBySelf".</span>
                    </p>
                </div>
            `
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    count1: 100,
                    count2: '123',
                    str1: "成功!",
                    str2: 124,
                    str3: '123',
                    numorstr1: '字符串',
                    numorstr2: {}
                };
            }
        });
    </script>
</body>

</html>

根據以上示例,咱們能夠得知一個prop能夠驗證js當中的基本數據類型,也能夠驗證對象或數組,能夠指定必要值以及默認值,還能夠自定義驗證函數。你能夠狠狠點擊此處具體示例查看效果。

這裏須要注意一點,就是prop會在一個組件實例建立以前進行驗證,所以實例中的data,computed等屬性在defaultvalidator函數中是不可用的。

前面提到prop能夠經過一個type屬性來進行驗證,在這裏,type的值能夠是以下幾個原生構造函數:

Number,String,Boolean,Object,Array,Function,Date,Symbol

額外的,type其實也能夠是一個自定義的構造函數,而且經過instanceof來檢查確認。好比如下一個示例:

<!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">
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>prop type</title>
</head>

<body>
    <div id="app">
        <name-component :name="fullName"></name-component>
    </div>
    <script>
        function FullName(firstName,lastName){
            this.firstName = firstName;
            this.lastName = lastName;
            this.fullName = firstName + ' ' + lastName;
        }
        var f = new FullName('夕','水');
        var nameComponent = {
            props:{
                name:{
                    type:FullName
                }
            },
            template:`
                <div class="name">
                    <p>
                        <span>姓:</span>
                        <span>{{ name.firstName }}</span>
                    </p>
                    <p>
                        <span>名:</span>
                        <span>{{ name.lastName }}</span>
                    </p>
                    <p>
                        <span>姓名:</span>
                        <span>{{ name.fullName }}</span>
                    </p>
                </div>
            `
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    fullName:f
                }
            },
            components:{
                nameComponent:nameComponent
            }
        })
    </script>
</body>

</html>

在這裏,只要name是自定義構造函數FullName的實例,那麼數據就驗證成功,若是不是,那麼vue就會在瀏覽器控制檯給出警告。你能夠狠狠點擊此處具體示例查看效果。

在前面,咱們都是顯式的定義prop,然而,當咱們不顯式的定義prop的時候,實際上,它也做爲了一個特性,傳給了組件,只不過它是一個非prop特性,由於該組件並無顯式的定義該prop特性,顯式的定義prop當然適用於傳向一個組件,可是組件庫的做者並不能老是預見到prop特性會應用於什麼樣的場景。因此若是不顯式的定義prop特性,那麼該特性就會被添加到組件的根元素上。以下例:

<!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">
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>not prop</title>
</head>

<body>
    <div id="app">
        <name-component :name="nameValue"></name-component>
    </div>
    <script>
        var nameComponent = {
            template:`
                <div class="name">
                   <p>組件並無顯式的定義prop,因此特性被添加到了根元素上。</p>
                </div>
            `
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    nameValue:"eveningwater"
                }
            },
            components:{
                nameComponent:nameComponent
            }
        })
    </script>
</body>

</html>

在瀏覽器控制檯,咱們能夠看到,name特性被添加到class爲name的根元素上,以下圖所示:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。

在這裏,對於絕大多數的特性來講,外部提供的特性都會替換組件內部的特性,例如想象一下,若是組件內部是這樣一個模板:

<input type="text" v-model="newValue" class="ew-input"/>

若是咱們在組件外部提供一個type="number"的特性,一旦替換掉組件內部的特性,那麼就改變了原有的DOM元素,這並非咱們所想要的。來看以下一個例子:

<!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">
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>replace prop</title>
</head>

<body>
    <div id="app">
        <ew-input :type="type"></ew-input>
    </div>
    <script>
        var ewInput = {
            template:`
                <input type="text" v-model="value" class="ew-input">
            `,
            data(){
                return{
                    value:"eveningwater"
                }
            }
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    type:"number"
                }
            },
            components:{
                ewInput:ewInput
            }
        })
    </script>
</body>

</html>

顯然,type="number"已經替換掉了原有的type="text"屬性,這並非咱們所想要的。
你能夠狠狠點擊此處具體示例查看效果。

慶幸的是,classstyle則會替換或合併原有的特性,只要原有特性與提供的特性不一樣,那麼就會合並原有的特性。以下例所示:

<!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">
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>replace prop</title>
</head>

<body>
    <div id="app">
        <ew-input :class="className" :style="stylePadding"></ew-input>
    </div>
    <script>
        var ewInput = {
            template:`
                <input type="text" v-model="value" class="ew-input" style="border:2px solid #2396fe;">
            `,
            data(){
                return{
                    value:"eveningwater"
                }
            }
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    className:"ew-success-input",
                    stylePadding:"padding:6px 7px;"
                }
            },
            components:{
                ewInput:ewInput
            }
        })
    </script>
</body>

</html>

如今,你在控制檯中查看該組件的根元素,你會發現classstyle都已經被合併了。你能夠狠狠點擊此處具體示例查看效果。

雖然是如此,但咱們有時候並不想要這樣的效果,也就是說咱們想要組件的根元素不能繼承特性,那麼咱們能夠在組件的選項中設置inheritAttrs:false。格式以下:

Vue.component('my-component',{
    inheritAttrs:false,
    ......
})

固然,這個屬性的設置並不會影響styleclass的綁定。咱們能夠將這個屬性與實例的$attrs屬性一塊兒結合使用,在構造基礎組件的時候很是經常使用,$attrs屬性也是一個對象,包含傳遞給一個組件的特性名和特性值。例如:

{
  placeholder:"請輸入你的用戶名",
  id:"#myInput"
}

這個模式更容許你像寫原始的HTML元素同樣寫一個組件,而不須要擔憂組件的根元素究竟是哪一個元素。以下例:

<!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">
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>inherit prop</title>
</head>

<body>
    <div id="app">
        <ew-input v-model="name" label="姓名:" placeholder="請輸入你的姓名" name="eveningwater"></ew-input>
    </div>
    <script>
        var ewInput = {
            inheritAttrs:false,
            props:['label','value'],
            template:`
                <label>
                    {{ label }}
                    <input type="text" v-bind="$attrs" :value="value" class="ew-input">
                </label>
            `
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                   name:""
                }
            },
            components:{
                ewInput:ewInput
            }
        })
    </script>
</body>

</html>

這樣,咱們就已經完成了一個基本的表單輸入框組件,再加點樣式擴展,咱們能夠完美的構造一個媲美經常使用的UI框架的表單輸入框組件。你能夠狠狠的點擊此處具體示例查看效果。

3.自定義事件

事件名不一樣於prop和組件名的註冊,是不存在大小寫的轉換的。也就是說事件名註冊的是什麼,使用的時候就應該是什麼。好比若是用camelCase來定義事件名的話,那麼用kebab-case的事件名是無效的。以下例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>emit</title>
  </head>
  <body>
    <div id="app">
        <p>{{ count }}</p>
        <emit-button @add-count="count+=1"></emit-button>
    </div>
    <script>
        Vue.component('emit-button',{
            template:`<button class="btn" @click="$emit('addCount')">click me</button>`
        })
      var vm = new Vue({
        el: "#app",
        data:{
            count:0
        }
      });
    </script>
  </body>
</html>

在這個示例中,不管咱們怎麼點擊按鈕,都不會觸發自定義事件,由於咱們事件定義的名稱是camelCase,而在使用的時候則是kebab-case,這也說明了事件名是不存在大小寫的轉換的。你能夠狠狠點擊此處具體示例查看效果。

因此,推薦用kebab-case來定義事件名。

v-model指令默認是利用一個props和一個名爲input的事件來完成的,但在像單選框,複選框,下拉框等類型的控件中可能會將value特性用於其它目的。因此vue提供了model選項來避免這樣的衝突,來看一個自定義v-model的示例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>自定義v-model</title>
</head>

<body>
    <div id="app">
        <div class="form">
            <base-check v-model="gameChecked">遊戲</base-check>
            <div class="form-item">
                <p>是否選中:<span>{{ gameChecked }}</span></p>
            </div>
        </div>
    </div>
    <script>
        const baseCheck = {
            model: {
                prop: 'checked',
                event: "change"
            },
            props: {
                checked: Boolean
            },
            template: `
                <div class="checkbox">
                    <input type="checkbox" :value="checked" @change="onChecked">
                    <slot></slot>
                </div>
            `,
            methods:{
                onChecked(e){
                    this.$emit('change',e.target.checked)
                }
            }
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    gameChecked:false
                }
            },
            components: {
                baseCheck: baseCheck
            }
        });
    </script>
</body>

</html>

這裏的gameChecked的值會被傳入名爲checkedprop,同時當base-checkbox被更新的時候,這個做爲屬性的值也會發生相應的改變。你能夠狠狠點擊此處具體示例查看效果。

在這裏你須要注意的就是,須要顯式的聲明checked這個prop

在某些時候,咱們是想要直接在組件上監聽一個元素的原生事件的,這個時候,咱們能夠直接使用.native事件修飾符。好比:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>native</title>
</head>

<body>
    <div id="app">
        <base-input @on-focus="count+=1"></base-input>
        <p>{{ count }}</p>
    </div>
    <script>
        const baseInput = {
            template: `
                <input type="text" @focus.natve="$emit('on-focus',$event.target.value)">
            `
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    count:0
                }
            },
            methods:{

            },
            components: {
                baseInput: baseInput
            }
        });
    </script>
</body>

</html>

你能夠點擊此處具體示例查看效果。

在有的時候,這樣確實不錯,但事實上,在有些狀況下,這樣作卻並非一個好主意。好比在編寫一個基本的base-input組件時,這個組件的根元素不必定是input元素,這個時候監聽input的元素的原生事件時,則會無效,儘管瀏覽器控制檯並不會報錯。如:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>base-input</title>
</head>

<body>
    <div id="app">
        <base-input @focus.native="msg='hello'" label="姓名"></base-input>
        <p>{{ msg }}</p>
    </div>
    <script>
        const baseInput = {
            props:['label','value'],
            template: `
               <div class="base-input-container">
                    <div class="label">{{ label }}</div>
                    <input type="text" v-bind="$attrs" :value="value" @input="$emit('on-input',$event.target.value)">
               </div>
            `
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg:"base input component"
                }
            },
            methods:{

            },
            components: {
                baseInput: baseInput
            }
        });
    </script>
</body>

</html>

顯然,這個事件的監聽並無產生效果,你能夠狠狠點擊此處具體示例查看效果。

爲了解決這個問題,vue提供了一個$listeners屬性,它是一個對象,裏面就包含了這個組件的全部監聽器。例如:

{
    focus:function(event){/*業務邏輯代碼*/},
    input:function(event){/*業務邏輯代碼*/}
    ...
}

有了這個屬性,就能夠配合v-on:$listeners將全部的事件監聽器指向組件特定的子元素。對於相似input的你但願它也能夠配合v-model工做的組件來講,使用計算屬性來建立這些監聽器是很是有用的。以下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>base-input</title>
</head>

<body>
    <div id="app">
        <base-input @focus="msg='hello,vue.js!'" label="姓名" v-model="msg"></base-input>
        <p>{{ msg }}</p>
    </div>
    <script>
        const baseInput = {
            inheritAttrs:false,
            props:['label','value'],
            computed:{
                input$Listeners:function(){
                    let vm = this;
                    return Object.assign({},this.$listeners,{
                        focus(e){
                            vm.$emit('focus',e.target.value)
                        },
                        input(e){
                            vm.$emit('input',e.target.value)
                        }
                    })
                }
            },
            template: `
               <div class="base-input-container">
                    <div class="label">{{ label }}</div>
                    <input type="text" v-bind="$attrs" :value="value" v-on="input$Listeners">
               </div>
            `
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg:"base input component"
                }
            },
            methods:{
                
            },
            components: {
                baseInput: baseInput
            }
        });
    </script>
</body>

</html>

如今,base-input已經徹底和一個普通的input元素一摸同樣了,你能夠狠狠點擊此處具體示例查看效果。

在前面,咱們知道props是單向數據流,只能從父組件中修改,而不能從子組件中修改,這會讓vue在瀏覽器中給出一個警告,但是在實際場景中,咱們不免不會遇到想讓props成爲雙向綁定的存在,這會比用vuex更爲方便一些。固然,很不幸的是,這種願望是不便實現的,由於雙向綁定會帶來維護上的問題。雖然子組件能夠修改父組件,但父組件與子組件之間並無明顯的改動來源。

固然,這也並非說咱們就不能修改子組件從而觸發父組件的改變,vue2.3.0推薦v-on:update:propName的模式來觸發事件並修改prop的值。以下一個示例:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>sync修飾符</title>
  </head>
  <body>
    <div id="app">
        <text-document :title="title" v-on:update:title="title = $event"></text-document>
    </div>
    <script>
        Vue.component('text-document',{
            props:['title'],
            template:`<div class="text">
                <p>{{ title }}</p>
                <button type="button" @click="$emit('update:title',$event.target.textContent)">change props</button>
            </div>`
        })
      var vm = new Vue({
        el: "#app",
        data:{
            title:'標題'
        }
      });
    </script>
  </body>
</html>

在這裏,定義了一個名爲titleprop,而後綁定父組件實例data選項中的title數據,傳入子組件,子組件定義了一個按鈕,經過點擊觸發update:title的事件,將按鈕的文本傳遞給父組件,而後父組件v-on:update:title監聽事件,並修改data選項中的值,這樣也算是真正的讓prop實現了雙向綁定吧。你能夠狠狠點擊此處具體示例查看該示例。

v-on:update:title這樣看着總有些麻煩,爲了方便起見,vue將這種模式縮寫爲v-bind:title.sync。須要注意的就是這種模式是不能和JavaScript表達式一塊兒使用的,例如:v-bind:title.sync=myTitle + '!'這種寫法就是無效的。也就是說,咱們只能爲其綁定一個屬性名,相似v-model指令的功能。

當咱們用一個對象設置多個prop的時候,咱們還能夠將這種模式簡寫爲:v-bind.sync=object,這樣會把object對象的每個屬性做爲一個獨立的prop傳進去,而後經過v-on添加各自的事件監聽器。例如:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>sync修飾符</title>
  </head>
  <body>
    <div id="app">
        <text-document  v-bind.sync="articleObj"></text-document>
    </div>
    <script>
        Vue.component('text-document',{
            props:['title','content'],
            template:`<div class="text">
                <p>{{ title }}</p>
                <article>{{ content }}</article>
                <button type="button" @click="$emit('update:title',$event.target.textContent)">出師表</button>
                <button type="button" @click="$emit('update:content',$event.target.textContent)">先帝創業未半而中道崩殂。。。</button>
            </div>`
        })
      var vm = new Vue({
        el: "#app",
        data:{
            articleObj:{
                title:"標題",
                content:"內容"
            }
        }
      });
    </script>
  </body>
</html>

在這裏,咱們綁定了一個articleObj對象,有文章標題title,文章內容content屬性,而後在組件,分別接收它們,經過增長update事件模式,咱們就能夠修改數據了。你能夠狠狠點擊此處具體示例查看效果。

不過咱們也要注意一點,就是v-bind.sync不能綁定一個字面量對象,例如v-bind.sync="{ title:"標題"}"是沒法正常工做的,這是vue基於不少邊緣狀況考慮而決定的。

4.插槽

vue自定義組件當中,若是嵌套了內容,是會被拋棄的。也就是說,組件標籤之間的內容是沒法被渲染的。可有時候,咱們又須要在組件當中渲染嵌套的內容,就像標籤裏嵌套標籤同樣。vue提供了<slot>元素,來做爲內容分發的一個API。在組件當中,<slot>元素更像是一個分發內容的出口。以下例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-nav v-bind="nav">
            navgation url
        </base-nav>
    </div>
    <script>
        Vue.component('base-nav', {
            props: ['url', 'target'],
            template: `
                <a :href="url" :target="target">
                    <slot></slot>
                </a>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {
                nav: {
                    url: "https://www.baidu.com/",
                    target: "_blank"
                }
            }
        });
    </script>
</body>

</html>

以上定義了一個base-nav組件,咱們能夠看到在組件標籤之間,咱們傳入了navgation url做爲分發的內容。不止是文本,咱們還能夠在裏面嵌套HTML標籤,甚至是另外一組件,這都是能夠的。你能夠狠狠點擊此處具體示例查看效果。

咱們來看嵌入標籤和組件的示例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-nav v-bind="nav">
            navgation url
        </base-nav>
        <base-nav v-bind="nav">
            <p>這是一個p標籤</p>
            簡單的文本內容
            <base-li></base-li>
        </base-nav>
    </div>
    <script>
        Vue.component('base-nav', {
            props: ['url', 'target'],
            template: `
                <a :href="url" :target="target">
                    <slot></slot>
                </a>
            `
        })
        const baseLi = {
            template:`<li>這是一個li標籤</li>`
        }
        var vm = new Vue({
            el: "#app",
            data: {
                nav: {
                    url: "https://www.baidu.com/",
                    target: "_blank"
                }
            },
            components:{
                baseLi:baseLi
            }
        });
    </script>
</body>

</html>

咱們能夠看到以下圖所示:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。

當咱們在插槽中使用數據時,咱們可使用相同做用域下的實例中的數據,但咱們不能使用不一樣做用域的數據。咱們只須要記住一條規則:父模板的全部內容都是在父級做用域中編譯的,子模版的全部內容都是在子做用域編譯的。來看一個示例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-nav v-bind="nav" title="導航">
            navgation url:{{ nav.url}}{{ title }}
        </base-nav>
    </div>
    <script>
        Vue.component('base-nav', {
            props: ['url', 'target','title'],
            template: `
                <a :href="url" :target="target">
                    <slot></slot>
                </a>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {
                nav: {
                    url: "https://www.baidu.com/",
                    target: "_blank"
                }
            }
        });
    </script>
</body>

</html>

如上例所示,咱們能夠看到在組件的插槽中,咱們也能夠訪問vm實例中的數據對象nav對象,但是不一樣做用域的title,也就是組件上的數據,是沒法被訪問到了,因此瀏覽器控制檯會報一個錯誤,以下圖所示:

clipboard.png

如今,你能夠狠狠點擊此處具體示例查看效果。

有時候,咱們須要爲插槽提供默認的內容,vue也叫作後備內容。好比在封裝一個提交按鈕組件時,咱們能夠提供submit或提交做爲後備內容。以下圖所示:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot default content</title>
    <style>
        .ew-btn{
            display: inline-block;
            transition: all .2s linear;
            outline: none;
            border: 1px solid transparent;
            text-align: center;
            padding: 6px 7px;
            line-height: 1;
            cursor: pointer;
            border-radius: 4px;
        }
        .ew-primary-btn{
            background-color: #2396ef;
            color: #ffffff;
        }
        .ew-primary-btn:hover,
        .ew-primary-btn:active{
            background-color: #23f8ff;
            color: #535353;
        }
    
    </style>
</head>

<body>
    <div id="app">
        <base-button type="primary"></base-button>
        <base-button type="primary">提交</base-button>
    </div>
    <script>
        Vue.component('base-button', {
            props:['type'],
            template: `
                <button type="button" :class="'ew-'+ type +'-btn'" class="ew-btn">
                    <slot>submit</slot>
                </button>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {
                
            }
        });
    </script>
</body>

</html>

如上例所示,當咱們在組件之間並無提供內容的時候,就會將submit做爲按鈕文本渲染出來,而當咱們提供了內容以後,就會渲染咱們所提供的內容。你能夠狠狠點擊此處具體示例查看效果。

有時候,咱們須要用到多個插槽,好比在一個頁面佈局當中,咱們可能會有頭部,內容,底部組件,網頁DOM結構大概以下所示:

<div class="page">
    <header class="base-header">
        <!-- 頭部 -->
    </header>
    <main class="base-main">
      <!-- 主體內容 -->
    </main>
    <footer class="base-footer">
      <!-- 底部 -->
    </footer>
</div>

這時候,頭部,主體和底部都會用到插槽,咱們能夠爲每一個插槽指定一個屬性:name。若是沒有顯示的指定這個屬性的值,那麼這個值會默認的指定爲default。而後,咱們在父組件中使用該組件而且提供插槽內容的時候,咱們能夠經過v-slot的參數形式結合template元素來指定添加到哪一個插槽中。好比:

定義的組件:

<div class="page">
    <header class="base-header">
        <!-- 頭部 -->
        <slot name="header"></slot>
    </header>
    <main class="base-main">
      <!-- 主體內容 -->
       <slot name="main"></slot>
    </main>
    <footer class="base-footer">
      <!-- 底部 -->
       <slot name="footer"></slot>
    </footer>
</div>

假定這個佈局組件的組件名爲base-layout,如今咱們在使用這個組件:

<base-layout>
  <template v-slot:header>
      <h1>這是頭部內容</h1>
  </template>
  <template v-slot:main>
    <p>這是主題內容</p>
  </template>
  <template v-slot:footer>
    <span>這是底部的內容</span>
  </template>
</base-layout>

固然主題內容,咱們也能夠沒必要指定v-slot,這樣就是默認的default。這樣咱們就完成了一個基本的佈局頁面:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-layout>
            <template v-slot:header>
                <h1>這是頭部內容</h1>
            </template>
            <template v-slot:main>
                <p>這是主題內容</p>
            </template>
            <template v-slot:footer>
                <span>這是底部的內容</span>
            </template>
        </base-layout>
    </div>
    <script>
        Vue.component('base-layout', {
            template: `
                <div class="page">
                    <header class="base-header">
                        <!-- 頭部 -->
                        <slot name="header"></slot>
                    </header>
                    <main class="base-main">
                      <!-- 主體內容 -->
                       <slot name="main"></slot>
                    </main>
                    <footer class="base-footer">
                      <!-- 底部 -->
                       <slot name="footer"></slot>
                    </footer>
                </div>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {

            }
        });
    </script>
</body>

</html>

你能夠狠狠點擊具體示例查看效果。注意這裏的v-slot只能添加到一個template元素上,只有一種例外,後續會贅述。

在有的時候,咱們須要將插槽內容訪問子組件的數據。好比有一個base-button組件以下:

<base-button>
  <span>{{ btn.text }}</span>
</base-button>

在子組件中,咱們可能會有以下結構:

<button type="button">
  <slot>{{ btn.text }}</slot>
</button>

子組件的數據應該以下:

data(){
  return{
    btn:{
      text:"submit"
    }
  }
}

咱們在子組件中是能夠訪問到btn.text數據的,可是在父組件中,咱們是訪問不到的,它們是不一樣的做用域,這時候咱們可使用v-bind特性將數據綁定到slot元素上,這種特性也被叫作插槽prop。以下:

<button type="button">
  <slot :btn="btn"></slot>
</button>

而後在父級組件中,咱們可使用v-slot帶一個值來定義插槽prop的名字,結構如:v-slot="slotProps"v-slot:default="slotProps"。以下:

<base-button v-slot="slotprops">
  <span>{{ btn.text }}</span>
</base-button>

固然咱們也能夠在父級組件中使用template元素,將v-slot綁定到template元素上,例如:

<base-button>
  <template v-slot="slotProps">
    <span>{{ btn.text }}</span>
  </template>
</base-button>

而後父級中綁定的數據,咱們應該修改爲slotProps.btn.text。這樣,咱們就完成了插槽之間的數據傳遞。完整代碼以下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-button v-slot="slotProps">
            <template >
                <span>{{ slotProps.text }}</span>
            </template>
        </base-button>
    </div>
    <script>
        Vue.component('base-button', {
            template: `
                <button type="button" class="btn">
                    <slot :text="text"></slot>
                </button>
            `,
            data(){
                return{
                    text:"submit"
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data: {

            }
        });
    </script>
</body>

</html>

你能夠狠狠點擊此處具體示例查看效果。

在這個例子中,咱們將包含全部插槽prop的對象命名爲slotProps,固然,也能夠換其它的命名,這取決於每一個人本身的想法。在這裏,咱們須要注意的就是,當被提供的插槽只有默認插槽的時候,組件的標籤才能夠被當作模板使用,咱們才能夠將v-slot綁定到組件標籤上,以下:

<base-button v-slot:default="slotProps">
    <span>{{ slotProps.text }}</span>
</base-button>

固然,這種寫法是還能夠更簡單的,vue把不帶參數的v-slot指定爲默認插槽。以下:

<base-button v-slot="slotProps">
    <span>{{ slotProps.text }}</span>
</base-button>

當有多個插槽,也就是有具名插槽的時候,以上的寫法是不被容許的,由於這會致使做用域不明確。如如下示例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-layout v-slot="footer">
            <template v-slot:header>
                <h1>這是頭部內容</h1>
            </template>
        </base-layout>
    </div>
    <script>
        Vue.component('base-layout', {
            template: `
                <div class="page">
                    <header class="base-header">
                        <!-- 頭部 -->
                        <slot name="header"></slot>
                    </header>
                    <main class="base-main">
                      <!-- 主體內容 -->
                       <slot name="main"></slot>
                    </main>
                    <footer class="base-footer">
                      <!-- 底部 -->
                       <slot name="footer"></slot>
                    </footer>
                </div>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {

            }
        });
    </script>
</body>

</html>

vue會在瀏覽器控制檯給出一個提示,以下圖:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。

只要出現多個插槽,就應該將v-slot綁定到<template>元素上。以下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-layout>
            <template v-slot:header>
                <h1>這是頭部內容</h1>
            </template>
            <template v-slot:footer>
                <h1>這是底部內容</h1>
            </template>
        </base-layout>
    </div>
    <script>
        Vue.component('base-layout', {
            template: `
                <div class="page">
                    <header class="base-header">
                        <!-- 頭部 -->
                        <slot name="header"></slot>
                    </header>
                    <main class="base-main">
                      <!-- 主體內容 -->
                       <slot name="main"></slot>
                    </main>
                    <footer class="base-footer">
                      <!-- 底部 -->
                       <slot name="footer"></slot>
                    </footer>
                </div>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {

            }
        });
    </script>
</body>

</html>

這樣;vue就不會在瀏覽器中給出警告了。你能夠狠狠點擊此處具體示例查看效果。

做用域插槽的工做原理其實就是將插槽內容包括在一個封裝的單個參數的函數裏面,就像以下所示:

function slot(slotProps){
    //插槽內容
}

這也就意味着v-slot的值實際上就是任何能夠用做函數參數的JavaScript表達式,所以,在瀏覽器支持或者單文件組件的狀況下,咱們也是可使用es2015的解構賦值來做爲傳入的插槽。例如:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-button>
            <template v-slot="{ user }">
                <span>{{ user.firstName }}</span>
            </template>
        </base-button>
    </div>
    <script>
        Vue.component('base-button', {
            template: `
                <button type="button">
                    <slot :user="user"></slot>
                </button>
            `,
            data(){
                return{
                    user:{
                        firstName:"evening",
                        lastName:"water"
                    }
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data: {
                
            }
        });
    </script>
</body>

</html>

你能夠狠狠點擊此處具體示例查看效果。

你甚至還能夠將插槽prop重命名,例如:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-button>
            <template v-slot="{ user:person }">
                <span>{{ person.firstName }}</span>
            </template>
        </base-button>
    </div>
    <script>
        Vue.component('base-button', {
            template: `
                <button type="button">
                    <slot :user="user"></slot>
                </button>
            `,
            data(){
                return{
                    user:{
                        firstName:"evening",
                        lastName:"water"
                    }
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data: {
                
            }
        });
    </script>
</body>

</html>

如上,咱們將user重命名爲person,你能夠狠狠點擊此處具體示例查看效果。

咱們甚至還能夠定義後備內容,用於插槽propundefined的狀況,好比:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-button>
            <template v-slot="{ user = { firstName:'evening'} }">
                <span>{{ user.firstName }}</span>
            </template>
        </base-button>
    </div>
    <script>
        Vue.component('base-button', {
            template: `
                <button type="button">
                    <slot>{{ user.lastName }}</slot>
                </button>
            `,
            data(){
                return{
                    user:{
                       lastName:"water"
                    }
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data: {
                
            }
        });
    </script>
</body>

</html>

你能夠狠狠點擊此處具體示例查看效果。

咱們也能夠將具名插槽進行縮寫,也就是把v-slot:縮寫爲#。例如,將v-slot:footer縮寫爲#footer,值得注意的就是這種寫法是不被容許的,#={user}。咱們必需要指定插槽名,哪怕是默認插槽也同樣。如#default={user}。以下例:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-layout>
            <template #default>
                <span>這是默認插槽的內容</span>
            </template>
            <template #header>
                <h1>這是頭部內容</h1>
            </template>
            <template #main>
                <p>這是主題內容</p>
            </template>
            <template #footer>
                <span>這是底部的內容</span>
            </template>
        </base-layout>
    </div>
    <script>
        Vue.component('base-layout', {
            template: `
                <div class="page">
                    <slot></slot>
                    <header class="base-header">
                        <!-- 頭部 -->
                        <slot name="header"></slot>
                    </header>
                    <main class="base-main">
                      <!-- 主體內容 -->
                       <slot name="main"></slot>
                    </main>
                    <footer class="base-footer">
                      <!-- 底部 -->
                       <slot name="footer"></slot>
                    </footer>
                </div>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {

            }
        });
    </script>
</body>

</html>

若是咱們將#default中的default去掉,那麼vue會在瀏覽器中給出一個警告,以下圖所示:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。

咱們也能夠綁定動態插槽,如:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>slot</title>
</head>

<body>
    <div id="app">
        <base-layout>
            <template #default>
                <span>這是默認插槽的內容</span>
            </template>
            <template v-slot:[slotname]>
                <h1>這是頭部內容</h1>
            </template>
            <template #main>
                <p>這是主題內容</p>
            </template>
            <template #footer>
                <span>這是底部的內容</span>
            </template>
        </base-layout>
    </div>
    <script>
        Vue.component('base-layout', {
            template: `
                <div class="page">
                    <slot></slot>
                    <header class="base-header">
                        <!-- 頭部 -->
                        <slot name="header"></slot>
                    </header>
                    <main class="base-main">
                      <!-- 主體內容 -->
                       <slot name="main"></slot>
                    </main>
                    <footer class="base-footer">
                      <!-- 底部 -->
                       <slot name="footer"></slot>
                    </footer>
                </div>
            `
        })
        var vm = new Vue({
            el: "#app",
            data: {
                slotname:'header'
            }
        });
    </script>
</body>

</html>

動態插槽就是以v-slot:[slotName]的形式,但在這裏須要注意一點,那就是動態插槽的插槽名不能包含大寫字母,不然,vue會在瀏覽器中給出一個警告,以下圖所示:

clipboard.png

你能夠狠狠點擊此處具體示例查看效果。

關於插槽的基礎就只有這些,但要深刻的瞭解能夠瀏覽這些諸如Vue Virtual Scroller,Vue Promised,Portal Vue 等庫。

5.動態組件與異步組件

在前面,咱們曾完成這樣利用is特性的組件切換示例,is綁定組件名is綁定組件選項對象。如今咱們來嘗試修改一下示例的代碼,以下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <!-- 引入vue.js開發版本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <title>keep-alive</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .tab-component-demo {
            width: 900px;
            margin: 15px auto;
            padding: 15px 10px;
        }

        button {
            outline: none;
            border: 1px solid #ebebeb;
            background-color: transparent;
            display: inline-block;
            padding: 6px 7px;
            text-align: center;
            line-height: 1;
            transition: all 0.3s cubic-bezier(0.075, 0.82, 0.165, 1);
            cursor: pointer;
            font-size: 16px;
            border-radius: 4px;
        }

        button:hover,
        button:active {
            border-color: #58e9fc;
            background-color: #0abce9;
            color: #ffffff;
        }

        .ew-page {
            border: 1px solid #999;
            padding: 15px 8px;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="tab-component-demo">
            <button v-for="(tab,index) in tabs" :key="index" @click="currentTab = tab">
                {{ tab }}
            </button>
            <component :is="currentTabComponent"></component>
        </div>
    </div>
    <script>
        const childComponents = [
            {
                name: "tab-header",
                component: {
                    template: `<div class='home-content'>the header of home page!</div>`
                }
            },
            {
                name: "tab-content",
                component: {
                    template: `<div class='home-content'>the content of home page!</div>`
                }
            },
            {
                name: "tab-footer",
                component: {
                    template: `<div class='home-content'>the footer of home page!</div>`
                }
            }
        ];
        Vue.component("tab-home", {
            template: `
                <div class="ew-page">
                  <button
                      v-for="(childTab,index) in childTabs"
                      :key="index"
                      @click="currentChildTab = childTab"
                      >
                      {{ childTab.name.slice(4) }}
                   </button>
                   <component :is="currentChildTab.component"></component>
                </div>
            `,
            data() {
                return {
                    childTabs: childComponents,
                    currentChildTab: childComponents[0]
                }
            }
        });
        Vue.component("tab-list", {
            template: `
                <div class="ew-page">
                 list page!
                </div>
            `
        });
        Vue.component("tab-contact", {
            template: `
                <div class="ew-page">
                contact page!
                </div>
            `
        });
        var vm = new Vue({
            data: {
                currentTab: "home",
                tabs: ["home", "list", "contact"]
            },
            computed: {
                currentTabComponent: function () {
                    return "tab-" + this.currentTab.toLowerCase();
                }
            }
        }).$mount(document.getElementById("app"));
    </script>
</body>

</html>

你會注意到,當咱們第一個導航(即home)所展現的頁面中又繼續包含3個子導航,咱們能夠再繼續切換。可是當咱們切換了子導航以後,再去切換父導航,最後切換回到父導航home,咱們會發現以前切換過的子導航會回到初始狀態(即header),這是由於每次切換,vue都新建立了一個組件實例。你能夠狠狠點擊此處具體實例查看效果。

儘管從新建立動態組件是很是有用的,但在這個例子中,咱們可能更但願組件在第一次被建立的時候就緩存下來。爲了解決這個問題,vue提供了<keep-alive>組件。咱們只須要用這個組件包裹動態組件便可,以下:

<keep-alive>
    <component :is="currentTabComponent"></component>
  </keep-alive>

如今咱們再來測試一下這個修改後的示例具體示例。咱們能夠看到,如今組件已經保持了它的狀態。

注意一點,<keep-alive>組件要求被切換的組件都有本身的名字,不管是經過組件的name選項仍是局部或者全局註冊。

你能夠在keep-alive API文檔中查看更多細節內容。

相關文章
相關標籤/搜索