後端開發者的Vue學習之路(三)

首發日期:2019-01-26vue


上節內容回顧

  • 數據綁定:v-model
  • 樣式綁定:v-bind:class,v-bind:style
  • 事件:v-on
  • Vue指令
  • 數組操做(知道哪些數組操做是能被vm層監聽到並能響應式更新到視圖上的)
  • Vue的元素複用問題(不使用key時會盡可能複用)

組件


【官方的話】組件系統是 Vue 的另外一個重要概念,由於它是一種抽象,容許咱們使用小型、獨立和一般可複用的組件構建大型應用。仔細想一想,幾乎任意類型的應用界面均可以抽象爲一個組件樹:webpack

小菜鳥的話:定義組件就好像定義了一堆「帶名字」的模板,好比說可能會有叫作「頂部菜單欄」的組件,咱們能夠屢次複用這個「頂部菜單欄」而省去了大量重複的代碼。web


什麼是組件

  • 在之前的多頁面開發的時候,咱們可能會常常須要一個「頂部菜單欄」,因而咱們在每一個html文件中都要加上關於「頂部菜單欄」的代碼。可能你會想這份代碼可以「複用」就行了。而組件能夠定義模板,從而達到複用代碼的做用。
  • 組件能夠大大提升咱們構建頁面的效率。
  • 你能夠將組件進行任意次數的複用
    下面用代碼來演示一下"複用效果":
<body>
      <div id="app">
        <my-template></my-template><!-- 利用組件名來使用定義的「模板」 -->
        <my-template></my-template>
      </div>
      
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        Vue.component('my-template',{ // 第一個參數是組件名,第二個參數是模板的內容
          template: '<div><span>個人模板</span></div>'  
        })
        var vm = new Vue({
          el: '#app'
        })
    </script>

代碼效果:
vuex


組件註冊

組件註冊就是「定義模板」,只有註冊了的組件,Vue纔可以瞭解怎麼渲染。npm


全局註冊組件

  • 全局註冊的組件能夠用在其被註冊以後的任何 (經過 new Vue) 新建立的 Vue 實例中,也包括其組件樹中的全部子組件的模板中。【一個Vue應用只有一個根實例,但還容許有其餘的實例。在 Vue 裏,一個組件本質上是一個擁有預約義選項的一個 Vue 實例。】
  • 全局註冊的行爲必須在根 Vue 實例 (經過 new Vue) 建立以前發生
  • 全局註冊的組件能夠在另外一個組件中使用。
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <div id="app">
            <ol>
              <!-- 使用組件 -->
              <todo-item></todo-item>
              <todo-item></todo-item>
              <todo-item></todo-item>
            </ol>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        //全局定義組件,第一個參數是組件名,template的值是組件的內容
        Vue.component('todo-item', {
          template: '<li>這是個待辦項</li>'
        })
        // 實例化是必須的,要把使用組件的區域交給vue管理
       var app = new Vue({
                  el: '#app',
                })
    </script>
</html>


局部註冊組件


全局註冊每每是不夠理想的。好比,若是你使用一個像 webpack 這樣的構建系統,全局註冊全部的組件意味着即使你已經再也不使用一個組件了,它仍然會被包含在你最終的構建結果中。這形成了用戶下載的 JavaScript 的無謂的增長。數組

在這些狀況下,你能夠經過一個普通的 JavaScript 對象來定義組件:瀏覽器

<body>
      <div id="app">
        <my-component></my-component><!-- 利用組件名來使用組件 -->
        <my-component></my-component>
      </div>
      
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        var ComponentA = { // 定義一個組件
          template: '<div><span>個人模板</span></div>'            
        }
        var vm = new Vue({
          el: '#app',
          components: {  // 而後在實例中聲明要使用這個組件,key是在這個實例中的組件名,value是組件
            'my-component': ComponentA
          }
        })
    </script>


上面的全局註冊說了容許在組件中使用其餘組件,但注意局部註冊的組件要聲明使用其餘組件纔可以嵌套其餘組件。例如,若是你但願 ComponentA 在 ComponentB 中可用,則你須要這樣寫:緩存

<body>
      <div id="app">
        <my-component></my-component>
        <my-component-b></my-component-b>
      </div>
      
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        var ComponentA = { // 1.定義一個組件
          template: '<div><span>個人模板</span></div>'            
        }
        var ComponentB = { // 2.定義一個組件
          components: { //3.聲明使用A組件
            'my-component': ComponentA
          },
          template: '<my-component></my-component>'    // 4.須要在組件的template中寫上另外一個組件        
        }
        var vm = new Vue({
          el: '#app',
          components: {  
            'my-component': ComponentA,
            'my-component-b': ComponentB
          }
        })
    </script>


使用細節


組件註冊的命名規範:

組件名可使用類my-component-name(kebab-case (短橫線分隔命名))或MyComponentName的格式(PascalCase 首字母大寫命名法),使用組件的時候能夠<my-component-name><MyComponentName>,但在有些時候首字母大寫命名法定義組件的是不行的,因此一般推薦使用<my-component-name>【當你使用首字母大寫命名法來定義組件的時候,不能直接在body中直接寫組件名,而要求寫在template中,以下例】。app


<body>
      <div id="app">
        <my-component></my-component>
        <my-component-demo></my-component-demo>
        
      </div>
      
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        Vue.component('my-component',{
          template: '<div><span>個人模板A</span></div>'        
        })
        Vue.component('my-component-demo',{ // 這個是用來測試第二種命名法定義組件的,首字母大寫時要寫在字符串模板中才能顯示(否則顯示不了)
          template: '<MyComponentB></MyComponentB>'        
        })
        Vue.component('MyComponentB',{
          template: '<div><span>個人模板B</span></div>'        
        })
        var vm = new Vue({
          el: '#app'
        })
    </script>


組件中只有一個根元素

每一個組件必須只有一個根元素!!
因此下面是不合法的:

若是你確實要有多個元素,那麼要有一個根元素包裹它們:


組件也是一個實例


組件也是一個實例,因此組件也能夠定義咱們以前在根實例中定義的內容:data,methods,created,components等等。
但一個組件的 data 選項必須是一個函數,所以每一個實例能夠維護一份被返回對象的獨立的拷貝


組件在某些元素中渲染出錯

在一些html元素中只容許某些元素的存在,例如tbody元素中要求有tr,而不能夠有其餘的元素(有的話會被提到外面)。下面是一個元素被提到外面的例子【而ul並無太嚴格,因此咱們在前面的todo-list的例子中可以演示成功】

下圖能夠看的出來div被提到table外面了:

這是爲何呢?目前來講,咱們在頁面中實際上是先通過html渲染再通過vue渲染的(後面項目話後是總體渲染成功再展現的),當html渲染時,它就發現了tr裏面有一個「非法元素」,因此它就把咱們自定義的組件提到了table外面。
解決方案:
使用tr元素,元素裏面有屬性is,is的值是咱們要使用的組件名

<body>
        <div id="app">
            <table>
                <tr is='mtr'></tr>
            </table>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('mtr', {
            template: `<tr><td>123</td></tr>` // 這個成功的檢測要使用檢測元素來看
        });
       var app = new Vue({
                    el: '#app'
                })
    </script>



但不會在一下狀況中出錯:

  1. 定義組件的時候,template中包含自定義的組件
  2. 單文件組件,也就是說引用vue文件來註冊一個組件的時候(這個東西會在後面講)。
  3. <script type="text/x-template">



組件間的數據傳遞


在上面定義的組件中使用的數據都是固定的數據,一般咱們都但願模板能根據咱們傳入的數據來顯示


父子組件傳遞數據

(子組件的意思是當前組件的直接子組件,在目前的單個html文件爲例時,你能夠僅認爲是當前頁面的非嵌套組件。後面講到多個組件的合做時因爲多個組件之間的嵌套,就造成了組件的父子、祖孫、兄弟關係
要給子組件傳遞數據主要有兩個步驟

  1. 定義組件時,使用props:['數據名'] (能夠有多個數據名) 來聲明來傳入的數據的名字
  2. 使用組件時,給組件中名爲"數據名"的屬性賦值


演示代碼以下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <div id="app">
            <ol>
              <!-- 2.給數據賦值 -->
              <todo-item todo="第一個值"></todo-item>
              <todo-item todo="第二個值"></todo-item>
              <todo-item :todo="msg"></todo-item> 
              <!-- 傳入的數據能夠是父組件實例中定義的數據。注意要用:來綁定,否則不會識別成實例數據,而是一個字符串 -->
            </ol>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('todo-item', {
          props: ['todo'], // 1.使用props來定義這個組件支持傳的參數
          template: '<li>{{ todo }}</li>' // 3.這裏演示一下在組件中使用這個傳入的參數
        })
       var app = new Vue({
                    el: '#app',
                    data: {
                      msg: 'hello world'
                    }
                })
    </script>
</html>

代碼效果:很明顯的,咱們的值成功傳給子組件了。


子組件向父組件傳輸數據

  • 咱們能夠在子組件中使用emit來觸發事件,而後在使用這個組件的時候綁定這個事件就能夠監聽到這個事件的發生(這時候調用的函數是父組件的處理函數),從而使得父組件接受到子組件傳遞的消息了。
    要給父組件傳遞數據主要有兩個步驟
  1. 在定義組件時,定義一個包含觸發事件的元素,這個事件觸發時將會調用emit來觸發事件【例如能夠在按鈕上定義一個onclick事件,這個onclick事件觸發時將會調用emit】
  2. $emit()能夠有多個參數,第一個參數是觸發的事件的名稱,後面的參數都是隨着這個事件向外拋出的參數。
  3. 使用組件時,對組件進行事件監聽,監聽的事件與組件內拋出的事件一致
  4. 定義處理目標事件的函數,函數的參數是隨事件向外拋出的多個參數。


演示代碼以下:

<body>
        <div id="app" >
            <my-component-c v-on:change-color="doSomething"></my-component-c>
            <!-- 2.使用組件的時候監聽一下事件,這裏將會調用父組件的函數來處理事件 -->
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('my-component-c', {
          template: `
          <button @click="$emit('change-color','hello')">改變顏色</button> 
          ` // 1.定義組件,在組件內定義一個能觸發click事件的按鈕,onclick事件發生時將會調用emit來向外拋出事件【這裏觸發的事件不能使用駝峯命名法。推薦使用短橫線命名法】
          // 【第一個參數是自定義事件名稱,第二個和後續的參數能夠用於向組件拋出值,這些值會做爲事件響應函數的參數】
        })
       var app = new Vue({
                    el: '#app',
                    data: {
                      msg: 'hello world'
                    },
                    methods: {
                    // 3.將會調用下面的函數來處理事件
                      doSomething: function(value){ // 這裏的value是從子組件中拋出的。
                        console.log(value) // 打印一下
                      }
                    }
                })
    </script>

【小tips:上面有多重字符串的使用,普通的雙引號和單引號已經不足以嵌套使用了,在外層可使用反引號` ` `【esc下面那個鍵】來包裹,它也能夠達到字符串包裹的效果,特別的是它支持多行字符串。】


非父子組件之間的傳值

祖孫組件傳數據、兄弟組件傳數據都屬於非父子組件之間的傳值。

  1. 【若是是祖孫組件傳數據,可使用父組件傳給子組件,子組件傳給孫組件。但這是一個費事的作法。】
  2. 通常都會使用vuex,vuex就像一個變量存儲器,咱們能夠把一些多個組件都須要用到數據存儲到vuex的store中。【這個因爲內容較重要,留到後面再講】
  3. 只有少許組件使用某個數據的時候也可使用bus模式,bus至關於給每個組件都加上「同一個」新的vue實例,因爲bus是實例之間共享的,當數據發生改變時,能夠利用這個vue實例來調用emit方法來拋出新值,而其餘組件監聽bus中的事件就能夠獲取到新的值,這樣就實現了非父子組件之間的傳值。


使用bus傳輸數據的步驟:

  1. 在Vue的原型上定義vue:Vue.prototype.bus = new Vue()
  2. 當數據發生變化時,調用emit:this.bus.$emit('change',當前組件的數據)
  3. 在組件上監聽bus的事件:this.bus.$on('change',一個用於賦值的函數)
  4. 在函數中獲取事件觸發帶過來的參數,賦給當前組件,從而實現兩邊數據同步。

下面的代碼是點擊某個組件發生數據變化時,另外一個組件的數據也發生變化:

<body>
        <div id="app">
              <child-a></child-a>
              <child-a></child-a>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 1. 給Vue的原型添加一個bus,bus是一個Vue實例
        Vue.prototype.bus = new Vue()
        Vue.component('child-a', {
            data: function() {
                return {
                    content: 0
                }
            },
            methods: {
                // 2.定義一個函數,可以在數據發生變化時調用emit(這裏定義一個點擊事件的響應函數)
                handleClick: function() { 
                    this.content+=1
                    this.bus.$emit('change',this.content)
                }
            },
            mounted: function(){
                var this_= this // 這裏要有一個這樣的賦值,下面不能直接使用this,由於在函數中this指向的已經不是當前對象了,而用_this保存的纔是當前對象
                // 3. 在組件中對bus中觸發的事件進行監聽。(當emit觸發事件時會調用)
                this.bus.$on('change',function(msg){ // 4.函數中的參數是emit觸發事件帶的參數。
                    this_.content = msg  // 5.修改當前組件中的數據
                })
            },
            template: `<div @click='handleClick'>{{ content }}</div>`
        });
       var app = new Vue({
                    el: '#app'
                })
    </script>


單向數據流

  • 單向數據流:props使得父組件的數據可以傳輸到子組件,並且傳輸的源數據發生改變時,子組件也會發生改變。但若是在子組件中更改prop,這是不行的,會報警告。
  • 每次父級組件發生更新時,子組件中全部的 prop 都將會刷新爲最新的值。若是容許你在子組件中修改父組件傳入的數據的話,使用了父組件的這個數據的全部子組件的數據都會被修改(這樣就下降了組件的複用效果,致使數據流行不肯定,難以肯定這個數據是在哪一個組件中修改的,並且是一個相對危險的行爲)
  • 你不該該在一個子組件內部改變 prop。若是你這樣作了,Vue 會在瀏覽器的控制檯中發出警告。【這就是單向數據流】
  • 若是你確實須要修改:
    • 那麼你應該建立一個子組件內部的數據,這個內部數據由傳入的prop的數據進行初始化。(也就是進行數據拷貝)
    • 又或者你可使用計算屬性。



Props屬性


命名規範:

大小寫問題

【有個建議,建議寫屬性名的時候都使用kebab-case (短橫線分隔命名) 命名,由於這個的兼容效果最好】
HTML 中的特性名是大小寫不敏感的,因此瀏覽器會把全部大寫字符解釋爲小寫字符。若是你在props中使用了駝峯命名法,那你在定義屬性的時候須要使用kebab-case (短橫線分隔命名) 命名才能正確傳輸數據【由於短橫線後面的字符能夠識別成大寫,從而可以匹配到】。

若是在屬性中也使用駝峯命名法命名屬性的時候會報這樣的錯:Prop "mycontent" is passed to component , but the declared prop name is "myContent". Note that HTML attributes are case-insensitive and camelCased props need to use their kebab-case equivalents when using in-DOM templates. You should probably use "my-content" instead of "myContent"

<body>
        <div id="app">
            <!--下面這裏會報錯 -->
            <child myContent='hello-1'></child>
            <!-- 下面會正常 -->
            <child my-content='hello-2'></child>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('child', {
            props:['myContent'],
            template: `<div>{{myContent}}</div>` 
        });
       var app = new Vue({
                    el: '#app'
                })
    </script>

一樣的,若是在組件的template屬性中使用駝峯命名法的屬性,那麼這個限制就不存在了。


參數校驗

限制props的類型

有時候須要使用第三方的組件的時候,因此會須要傳輸數據給這個組件來渲染。但如何限制傳入的數據的類型呢?

  • 可用於限制數據類型的類型:
    • String:字符串
    • Number:數字
    • Boolean:布爾值
    • Array:數組
    • Object:對象
    • Date:日期
    • Function
    • Symbol


格式:

props: {
// 數據名:數據類型
  title: String,
  likes: Number,
  ...
}


若是傳入的類型不對,那麼會報Invalid prop: type check failed for prop "xxx". Expected String with value "xxx", got Number with value xxx.的錯誤。


若是容許多種值,能夠定義一個數組:

props: {
  content: [String,Number]
}


設置默認值

咱們也能夠給props中的數據設置默認值,若是使用default設置值,那麼沒有傳某個數據時默認使用這個數據。

props: {
  content: {
  type:[String,Number],
  default:'個人默認值'
  }
}

若是使用default給數組或對象類型的數據賦默認值,那麼要定義成一個函數。


要求數據必傳

若是要求某個數據必須傳給子組件,那麼能夠爲它設置required。
格式:

props: {
    content: {
       type: String,
       required: true
   }
}

若是沒傳,會報Missing required prop: "xxx"的錯。


自定義驗證函數:

若是想要更精確的校驗,可使用validator,裏面是一個函數,函數的第一個參數就是傳入的值,當函數內返回true時,這個值纔會校驗經過。
如下的代碼是要求傳入的字符串長度大於6位的校驗:

Vue.component('child', {
          props: {
            content: {
                type: String,
                validator: function(value) {
                    return (value.length > 6)
                }
            }
          }

若是驗證不經過,會報Invalid prop: custom validator check failed for prop "xxx"的錯。



傳遞靜態或動態Prop

  • 傳遞靜態或動態的prop意思就是傳遞的是一個常量仍是變量。
  • 傳遞常量的時候:
    • 若是是字符串,能夠不使用v-bind。
    • 若是是數字,布爾值,數組,對象,那麼須要使用vue的v-bind來設置屬性,由於使用普通的屬性設置會被認爲是字符串,使用v-bind的時候會被認爲是js表達式(從而成功識別成正確的類型)。
  • 傳遞變量的時候都要使用v-bind,否則沒法識別成一個變量。


補充:



給組件綁定原生的事件


用下面的代碼來講一個問題:

<html lang="en">
<head>
    <meta charset="utf-8">
    <title>demo</title>
</head>
    <body>
      <div id="app">
        <child @click='myFunction'></child>
      </div>
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        Vue.component('child',{
            template:`
                <button>按鈕</button>
            `
        });
        var vm = new Vue({
          el: '#app',
          methods: {
            myFunction: function() {
                console.log('haha')
            }
          }
        })
    </script>
</html>


上面的代碼你會發現點擊了按鈕卻沒有調用函數。
而下面的按鈕按了會打出child。

<html lang="en">
<head>
    <meta charset="utf-8">
    <title>demo</title>
</head>
    <body>
      <div id="app">
        <child @click='myFunction'></child>
      </div>
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        Vue.component('child',{
            template:`
                <button @click='childFunction'>按鈕</button>
            `,
            methods: {
                childFunction: function() {
                    console.log('child')
                }
            }
        });
        var vm = new Vue({
          el: '#app',
          methods: {
            myFunction: function() {
                console.log('haha')
            }
          }
        })
    </script>
</html>


  • 在上面咱們提過了父子數據傳遞,咱們知道了父組件的數據須要props來傳遞給子組件,說明了父組件的數據是不跟子組件共享的。
  • 而事件也是這樣的,在咱們使用組件的時候,並不能直接監聽一些事件的發生。【例如上面的子組件傳數據給父組件也須要使用emit。】
  • 對於父組件來講,這個組件是子組件下的按鈕,因此直接點擊這個按鈕並不會觸發事件【或者說你能夠認爲點擊了事件了,可是沒有emit出來,因此父組件監聽不到】。
  • 而若是但願點擊按鈕的時候可以觸發出按鈕的原生事件(不把它看成子組件下的按鈕),那麼須要把它綁定成原生事件。咱們可使用.native來修飾事件來講明監聽的是一個原生事件。


下面的代碼是使用了emit來達到一樣效果的代碼:

<html lang="en">
<head>
    <meta charset="utf-8">
    <title>demo</title>
</head>
    <body>
      <div id="app">
        <child @click='myFunction'></child>
      </div>
    </body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        Vue.component('child',{
            template:`
                <button @click="$emit('click')">按鈕</button>
            `
        });
        var vm = new Vue({
          el: '#app',
          methods: {
            myFunction: function() {
                console.log('haha')
            }
          }
        })
    </script>
</html>



template

  • template是vue中的一個元素。它是一個不會渲染到頁面的元素,一般地它有以下幾個用處:
    • 因爲不會被渲染處理,能夠在template上使用使用v-if,把一個 <template> 元素當作不可見的包裹元素,讓成組的元素可以統一被渲染出來。
    • 也可使用v-for,也是把<template> 元素當作不可見的包裹元素
    • 其餘特殊的用途:例以下面要說插槽的slot-scope會用到。


在template上使用v-if

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


使用v-for

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



插槽

  • 咱們能夠用prop來傳遞數據給子組件,而插槽是用來分發內容的。
  • 咱們有時候但願在使用子組件的時候順便傳給它一些內容,那麼可使用插槽。
  • 【props適合於傳輸數據,而slot一般用來達到「插入內容」的效果;使用props的話須要子組件聲明使用,而slot相較隨意】


經過插槽分發內容



具名插槽

插槽也能夠分發多份數據。使用指定的插槽名就能夠獲取指定的插槽數據。


插槽的默認內容

若是沒有數據傳過來,那麼插槽能夠定義一個默認的數據用來顯示。


做用域插槽

  • 有時候咱們但願獲取組件中的slot的值來進行其餘操做。好比,可能須要根據一個值來顯示不一樣的內容。這時候咱們可使用slot-scope來獲取組件中傳過來的slot的值。
  • slot-scope的值是能夠隨意的,它表明做用於插槽的名字。
  • 這時候再也不是插入數據給插槽,而是從插槽中獲取數據了。
<body>
        <div id="app">
              <child-a>
                  <template slot-scope='props'> <!--2. slot-scope的屬性值是能夠隨意的,表明做用域插槽的名字 -->
                      <h3>{{props.index}}</h3> <!--3. props.xxx 是傳過來的值的名字,值須要綁定到slot中 -->
                  </template>
              </child-a>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.prototype.bus = new Vue()
        Vue.component('child-a', {
            data: function() {
                return {
                    list: [2,4,6,8,10]
                }
            },
            // 1. 綁定屬性,名爲index,那麼做用域插槽就能夠獲取名爲index的數據
            template: `<div>
                        <slot v-for='item of list' :index=item></slot>
                    </div>`
        });
       var app = new Vue({
                    el: '#app'
                })
    </script>



動態組件


is

  • 動態組件是一個有趣的東西,它至關於一張空白的組件,只要你告訴它它是什麼組件,它就變成了什麼組件。
  • 有時候咱們但願發生了xxx事件以後,頁面的某個組件變成另外一個組件。【你可能會產生以下圖的需求:點擊按鈕,切換登陸方式】

  • 咱們只須要給component元素設置上is屬性就能夠把component元素渲染成目標組件了。<component :is='組件名'></component>若是改變了is屬性的值,那麼渲染的組件就會發生變化。
  • is能夠是v-bind的,也能夠是非v-bind的,若是但願它是動態可變化的,那麼應該使用v-bind:is,這樣才能傳入一個變量,從而動態渲染組件。【非v-bind的時候傳入的值會被認爲一個字符串,而後直接把這個字符串認爲是組件名】


下面是上圖的演示代碼:

<body>
      <div id="app">
        <button @click='changeLogin'>切換登陸方式</button>
        <component :is='loginType'></component>
      </div>
</body>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
        Vue.component('userInfoLogin',{
            template:`
            <div>
                <div>用戶名登錄:<input type="text"></div>
                <div>請輸入密碼:<input type="password"></div>
            </div>
            `
        });
        Vue.component('phoneLogin',{
            template:`
            <div>
                <div>請輸入手機號:<input type="text"></div>
                <div>請輸入驗證碼:<input type="text"></div>
            </div>
            `
        })
        var vm = new Vue({
          el: '#app',
          data: {
            loginType:'userInfoLogin'
          },
          methods: {
            changeLogin: function() {
                this.loginType = this.loginType === 'userInfoLogin'?'phoneLogin':'userInfoLogin';
            }
          }
        })
    </script>


keep-alive

  • 被keep-alive元素包裹的組件會被緩存,緩存以後組件從新渲染時會直接從緩存中獲取,避免了每次組件都從新渲染。

下面以上面的動態組件切換爲例:
若是給上面的登陸組件都加上一個created用來顯示渲染次數的話,咱們就能夠看到是否是每次切換都會從新渲染。
若是加了keep-alive以後再也不重複輸出,那麼就說明組件被緩存了。

<body>
        <div id="app">
            <button @click='changeLogin'>切換登陸方式</button><!-- 2.屢次點擊切換,查看組件渲染狀況 -->
            <keep-alive>
                <component :is='loginType'></component> 
                <!-- 4.注意在keep-alive包裹以前和以後的控制檯輸出狀況 -->
            </keep-alive>
        </div>
  </body>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script>
          Vue.component('userInfoLogin',{
              template:`
              <div>
                  <div>用戶名登錄:<input type="text"></div>
                  <div>請輸入密碼:<input type="password"></div>
              </div>
              `,
              created: function(){
                console.log('userInfoLogin')// 1.用來講明渲染了userInfoLogin組件,屢次渲染則屢次輸出
              }
          });
          Vue.component('phoneLogin',{
              template:`
              <div>
                  <div>請輸入手機號:<input type="text"></div>
                  <div>請輸入驗證碼:<input type="text"></div>
              </div>
              `,
              created: function(){
                console.log('phoneLogin') // 2.用來講明渲染了phoneLogin組件,屢次渲染則屢次輸出
              }
          })
          var vm = new Vue({
            el: '#app',
            data: {
              loginType:'userInfoLogin'
            },
            methods: {
              changeLogin: function() {
                  this.loginType = this.loginType === 'userInfoLogin'?'phoneLogin':'userInfoLogin';
              }
            }
          })
      </script>


補充


$refs


  • 有時候你可能須要獲取某個元素來進行操做,在沒有學refs以前,你可能須要document.xxx來獲取某個元素。
  • 學了refs以後,你可使用refs來獲取元素


使用步驟:

1.在元素中使用ref屬性給元素起一個有標識意義的名字。
2.this.\(refs能夠獲取當前組件實例的全部使用了ref的元素,`this.\)refs.名字`表明指定的元素。
3.而後你能夠進行dom操做了。

<body>
        <div id="app">
            <div ref='demo'>1</div> <!-- 1. 設置ref -->
            <button @click='changeColor'>+1</button>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
       var app = new Vue({
                    el: '#app',
                    methods: {
                        changeColor:function() { 
                            console.log(this.$refs.demo.innerText) // 獲取ref對象,有了dom對象你就能夠進行dom操做了。
                            this.$refs.demo.style = 'color:red'
                            this.$refs.demo.innerText+=this.$refs.demo.innerText
                        }
                    }
                })
    </script>


獲取組件的引用

refs也能夠用在組件上,能夠獲取組件的數據(但這時候的ref獲取的不是一個dom對象,而是一個vue實例對象,因此不能獲取innerText這類dom屬性)。

<body>
        <div id="app">
            <child ref='demo'></child>
            <button @click='changeColor'>+1</button>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('child',{
            data: function(){
                return {

                msg:'hello world!'
                }
            },
            template:'<div>hello</div>'
        })
       var app = new Vue({
                    el: '#app',
                    methods: {
                        changeColor:function() { 
                            console.log(this.$refs.demo.msg) // 獲取組件實例的數據。
                        }
                    }
                })
    </script>



動畫效果

若是說前面講的都是基礎,必需要掌握的話,那麼動畫效果是錦上添花,無關緊要(對於我這些不追求動畫效果就不顯得重要了),因此這裏就不講了,這裏僅僅是爲了顯示vue能有實現動畫效果的功能。
有興趣的能夠看一下官網:
vue官網動畫效果直通車

相關文章
相關標籤/搜索