Vue2 第二天學習

龍恩0707發表於2017-05-02
個人小總結:1年多沒有寫部落格,感覺很多知識點生疏了,雖然工作上能解決問題,但是當別人問到某個知識點的時候,還是迷迷糊糊的,所以堅持寫部落格是硬道理的,因為大腦不可能把所有的知識點記住,有可能某一天忘了,但是我們工作上還是會使用,只是理論忘了,所以寫部落格的好處是可以把之前的東西重新看一遍後會在大腦裡面重新浮現起來,特別在面試的時候,別人問你的知識點的時候答不上來那種尷尬,但是平時經常使用到,只是說不出所以來的,因此寫部落格是最好的思路。

閱讀目錄

1.vue屬性和方法

每個Vue例項都會代理其 data物件裡所有的屬性。
如下程式碼:

var data = {
  a: 1
};
var vm = new Vue({
 data: data
});
console.log(vm);
console.log(vm.a === data.a); // true

// 設定屬性也會影響到原始資料
vm.a = 2;
console.log(data.a); // 2
// 反之
data.a = 3;
console.log(vm.a); // 3
//除了data屬性,Vue例項還暴露了一些有用的例項屬性與方法。這些屬性與方法都有字首$, 以便與代理的data屬性區分。
var data = { a: 1 }
var vm = new Vue({
  el: '#container1',
  data: data
})
console.log(vm.$data === data) // true
console.log(vm.$el === document.getElementById('container1')) // true
data.a = 5;
// $watch 是一個例項方法
vm.$watch('a', function (newVal, oldVal) {
  // 這個回撥將在 `vm.a`  改變後呼叫
  console.log(newVal);
  console.log(oldVal);
})

1-1. data 必須是函式
通過Vue構造器傳入的各種選項大多數都可以在元件裡用。 data 是一個例外,它必須是函式。 如下程式碼Vue 會停止,並在控制檯會報錯。

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      
      <component1></component1>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    var data = { counter: 0 };
    // 全域性註冊
    Vue.component('component1', {
      template: '<span>{{ message }}</span>',
      data: {
        message: 'hello'
      }
    });
    new Vue({
      el: '#container1'
    })
  </script>
</html>

data是函式解決該方案
程式碼如下:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <component1></component1>
      <component1></component1>
      <component1></component1>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    var data = { counter: 0 };
    // 全域性註冊
    Vue.component('component1', {
      template: '<button v-on:click="counter += 1">{{ counter }}</button>',
      // data是一個函式,vue不會報錯,但是我們返回給每個元件的實列引用了同一個data物件
      data: function() {
        return data
      }
    });
    new Vue({
      el: '#container1'
    })
  </script>
</html>

檢視效果

由於這三個元件共享了同一個 data , 因此增加一個 counter 會影響所有元件!這不對。我們可以通過為每個元件返回全新的 data 物件來解決這個問題:
程式碼如下:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <component1></component1>
      <component1></component1>
      <component1></component1>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    // 全域性註冊
    Vue.component('component1', {
      template: '<button v-on:click="counter += 1">{{ counter }}</button>',
      // data是一個函式,vue不會報錯,但是我們返回給每個元件的實列引用了同一個data物件
      data: function() {
        return {
          counter: 0
        }
      }
    });
    new Vue({
      el: '#container1'
    })
  </script>
</html>

檢視效果

現在每個 counter 都有它自己內部的狀態了.

2.理解元件的通訊。

一般情況父子元件是這樣的關係,元件A在它的模板中使用了元件B,他們之間必然需要相互通訊,父元件要給子元件傳遞資料,子元件需要將它內部發生的事情告知父元件,為了保證父子元件的解耦,可維護性及可重用性。在vue.js中,父元件通過props向下傳遞資料給子元件,子元件通過events給父元件傳送訊息。

2-1 使用props傳遞資料
不能在子元件的模板內直接引用父元件的資料,要讓子元件使用父元件的資料,我們需要通過子元件的props選項。
如下程式碼:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <child message="hello!"></child>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    // 全域性註冊
    Vue.component('child', {
      // 宣告props 
      props: ['message'],
      template: '<span>{{ message }}</span>'
    });
    new Vue({
      el: '#container1'
    })
  </script>
</html>

結果在頁面上會列印 hello。

檢視效果

注意: HTML特性是不區分大小寫的,所以當使用的不是字串模板,camelCased(駝峰式) 命名的prop需要轉換為相對應的 kebab-case(短橫線隔開式)命名:
如下程式碼:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <!-- kebab-case in HTML-->
      <child my-message="hello!"></child>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    // 全域性註冊
    Vue.component('child', {
      // 宣告props 
      props: ['myMessage'],
      template: '<span>{{ myMessage }}</span>'
    });
    new Vue({
      el: '#container1'
    })
  </script>
</html>

2-2 理解動態prop
在模板中,要動態地繫結父元件的資料到子模板的props,使用v-bind,每當父元件的資料變化時,該變化會傳遞給子元件。

<div id="container1">
  <input v-model='parentMsg' />
  <br />
  <!-- kebab-case in HTML-->
  <child v-bind:my-message="parentMsg"></child>
</div>

使用 v-bind 的縮寫語法通常更簡單:

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

程式碼如下:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <input v-model='parentMsg' />
      <br />
      <!-- kebab-case in HTML-->
      <child v-bind:my-message="parentMsg"></child>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    new Vue({
      el: '#container1',
      data: {
        parentMsg: 'Message'
      },
      components: {
        child: {
          props: ['myMessage'],
          template: '<span>{{myMessage}}</span>'
        }
      }
    })
  </script>
</html>

檢視效果

3.理解自定義事件

父元件使用props傳遞資料給子元件,但是如果子元件需要把資料傳回去的話,就需要自定義事件了;
3-1 使用v-on繫結自定義事件
每個vue例項都實現了事件介面,即:
1. 使用 $on(eventName) 監聽事件
2. 使用 $emit(eventName) 觸發事件
注意: $on 和 $emit 不是 addEventListener 和 dispatchEvent的別名。且 父元件可以在使用元件的地方直接用 v-on 來監聽子元件觸發的事件。
不能用$on偵聽子元件丟擲的事件,而必須在模板裡直接用v-on繫結,就像以下的例子:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <p> {{ total }} </p>
      <button-counter v-on:increment="incrementTotal"></button-counter>
      <button-counter v-on:increment="incrementTotal"></button-counter>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    Vue.component('button-counter', {
      template: '<button v-on:click="increment">{{ counter }}</button>',
      data: function() {
        return {
          counter: 0
        }
      },
      methods: {
        increment: function() {
          this.counter += 1;
          this.$emit('increment');
        }
      },
    })
    new Vue({
      el: '#container1',
      data: {
        total: 0
      },
      methods: {
        incrementTotal: function() {
          this.total += 1;
        }
      }
    })
  </script>
</html>

上面程式碼: 初始化時候 例項化設定 data: {total: 0}, 設定total為0, 子元件button-counter 預設為0, 當點選子元件的時候呼叫 increment方法,當前的counter自增1, 然後在子元件觸發 $emit('increment')事件,當使用 v-on:increment 會監聽到事件後,會呼叫父元件的incrementTotal方法,因此父元件也自增1.

檢視效果

上面程式碼中 子元件已經和它外部完全解耦了。它所做的只是報告自己的內部事件,至於父元件是否關心則與它無關。

4.理解使用自定義事件的表單輸入元件

自定義事件可以用來建立自定義的表單輸入元件,使用v-modal來進行資料雙向繫結。比如如下程式碼:

<input v-modal="something" />

上面的程式碼是下面的語法糖;如下程式碼:

<input v-bind:value="something" v-on:input="something=$event.target.value" />

因此在建立元件中時,相當於下面的簡寫;如下程式碼:

<custom-input v-bind:value="something" v-on:input="something=arguments[0]"></custom-input>

所以要讓元件的v-model 生效,必須滿足下面的條件:
1. 接受一個value屬性。
2. 在有新的value時觸發input事件。

如下測試程式碼:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <currency-input v-model="price"></currency-input>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script>
   
  </script>
  <script type="text/javascript">
    Vue.component('currency-input', {
      template: '\
        <span>\
          $\
          <input\
            ref="input"\
            v-bind:value="value"\
            v-on:input="updateValue($event.target.value)"\
          >\
        </span>\
      ',
      props: ['value'],
      methods: {
        // 不是直接更新值,而是使用此方法來對輸入值進行格式化和位數限制
        updateValue: function (value) {
          var formattedValue = value
            // 刪除兩側的空格符
            .trim()
            // 保留 2 小數位
            .slice(0, value.indexOf('.') + 3)
          // 如果值不統一,手動覆蓋以保持一致
          if (formattedValue !== value) {
            this.$refs.input.value = formattedValue
          }
          // 通過 input 事件發出數值
          this.$emit('input', Number(formattedValue))
        }
      }
    });
    new Vue({
      el: '#container1',
      data: {
        price: 0
      }
    })
  </script>
</html>

檢視效果

5.單個slot 

<slot>標籤中的任何內容都被視為 備用內容。備用內容在子元件的作用域內編譯,並且只有在宿主元素為空,且沒有插入的內容時才顯示備用內容。
如果<slot>標籤中有內容的話,就顯示該內容。
比如 my-component 元件有如下程式碼:

<div class="content">
  <h2>this is a component</h2>
  <slot>如果沒有分發內容,則顯示slot中的內容</slot>
  <p>asdsadsdad</p>
</div>

父元件有如下程式碼:

<div id="container1">
  <my-component>
    <h1>Hello Vue.js</h1>
  </my-component>
  <my-component></my-component>
</div>

渲染後的結果為:

<div id="container1">
  <div class="content">
    <h2>this is a component</h2> 
    <h1>Hello Vue.js</h1> 
    <p>asdsadsdad</p>
  </div> 
  <div class="content">
    <h2>this is a component</h2> 
    如果沒有分發內容,則顯示slot中的內容 
    <p>asdsadsdad</p>
  </div>
</div>

所有測試例項程式碼如下:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>

    <div id="container1">
      <my-component>
        <h1>Hello Vue.js</h1>
      </my-component>
      <my-component></my-component>
    </div>

    <template id="myComponent">
      <div class="content">
        <h2>this is a component</h2>
        <slot>如果沒有分發內容,則顯示slot中的內容</slot>
        <p>asdsadsdad</p>
      </div>
    </template>
    
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    Vue.component('my-component', {
      template: '#myComponent'
    })
    new Vue({
      el: '#container1'
    })
  </script>
</html>

檢視效果

6.具名slot 

<slot> 元素可以用一個特殊的屬性 name 來配置如何分發內容。多個 slot 可以有不同的名字。具名 slot 將匹配內容片段中有對應 slot 特性的元素。
如果沒有預設的 slot ,這些找不到匹配的內容片段將被拋棄。
比如:假如有一個 my-component 元件,它的模板為:

<template id="myComponent">
  <div class='content'>
    <header>
      <slot name='header'></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name='footer'></slot>
    </footer>
  </div>
</template>

父元件的模板如下:

<div id="container1">
  <h1 slot="header">這裡可能是一個頁面標題</h1>
  <p>主要內容的一個段落</p>
  <p>另一個主要段落</p>
  <p slot='footer'>這裡是底部資訊</p>
</div>

頁面渲染的結果如下:

<div id="container1">
  <h1>這裡可能是一個頁面標題</h1> 
  <p>主要內容的一個段落</p> 
  <p>另一個主要段落</p> 
  <p>這裡是底部資訊</p>
</div>

所有的程式碼如下:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>

    <div id="container1">
      <h1 slot="header">這裡可能是一個頁面標題</h1>
      <p>主要內容的一個段落</p>
      <p>另一個主要段落</p>
      <p slot='footer'>這裡是底部資訊</p>
    </div>

    <template id="myComponent">
      <div class='content'>
        <header>
          <slot name='header'></slot>
        </header>
        <main>
          <slot></slot>
        </main>
        <footer>
          <slot name='footer'></slot>
        </footer>
      </div>
    </template>

  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    Vue.component('my-component', {
      template: '#myComponent'
    })
    new Vue({
      el: '#container1'
    })
  </script>
</html>

檢視效果

7.理解作用域插槽(2.1.0新增的) 

   在slot分發中,無論是單分發還是具名分發,都是父元件替換子元件的資料,或者沒有替換,用子元件預設的資料。 但是通過設定作用域槽,就可以改變這種狀況,讓子元件可以在父元件進行分發時獲取自己的資料,至於是什麼資料,由子元件決定,這樣就能解耦了。
作用域槽通過slot的一個自定義的屬性,官方給出的DEMO是text,但也可以是其他,值為暴露的資料。 這個自定義屬性已經存放在子元件的prop物件裡了。等待著被父元件獲取。
怎麼獲取呢? 在父元件的模板裡,使用一個Vue自帶的特殊元件<template> ,並在該元件上使用scope屬性,值是一個臨時的變數,存著的是由子元件傳過來的prop物件,獲得由子傳過來的prop物件。這時候,父元件就可以訪問子元件在自定義屬性上暴露的資料了。
如下程式碼:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>
    <div id="container1">
      <parent-component></parent-component>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
    // 子元件
    Vue.component('child-component', {
      template: '<ul><slot name="child-ul" v-for="item in rets" v-bind:text="item.name"></slot></ul>',
      data: function() {
        return {
          rets: [
            {name: '我是蘋果'},
            {name: '我是香蕉'},
            {name: '我是橘子'}
          ]
        }
      }
    });
    // 父元件
    Vue.component('parent-component', {
      template: '<child-component><template scope="props" slot="child-ul"><li>{{props.text}}</li></template></child-component>'
    })
    new Vue({
      el: '#container1'
    })
  </script>
</html>

頁面渲染後的程式碼如下:

<div id="container1">
  <ul>
    <li>我是蘋果</li>
    <li>我是香蕉</li>
    <li>我是橘子</li>
  </ul>
</div>

檢視效果

8.理解動態元件

   通過使用保留的<component>元素,動態地繫結到它的 is 特性,我們可以讓多個元件使用同一個掛載點,並動態的切換。
keep-alive: 如果把切換出去的元件留在記憶體中,可以保留它的狀態或避免重新渲染,為此我們可以新增一個 keep-alive指令引數。
如下實現的tab切換程式碼:

<!DOCTYPE html>
<html>
  <body>
    <head>
      <title>演示Vue</title>
    </head>

    <h3>動態元件</h3>
    <template id="tab-01">
      <div>this is tab01</div>
    </template>
    <template id='tab-02'>
      <div>this is tab02</div>
    </template>
    <template id="tab-03">
      <div>this is tab03</div>
    </template>

    <div id="container1">
      <!-- 導航欄 -->
      <ul>
        <li>
          <a href="javascript:void(0)" @click="toggleTabs(tab01Text);">{{ tab01Text }}</a>
        </li>
        <li>
          <a href="javascript:void(0)" @click="toggleTabs(tab02Text);">{{ tab02Text }}</a>
        </li>
        <li>
          <a href="javascript:void(0)" @click="toggleTabs(tab03Text);">{{ tab03Text }}</a>
        </li>
      </ul>
      <!-- 點選導航後要切換的內容 -->
      <div class='content' style='height: 200px'>
        <!-- 如果把切換出去的元件保留在記憶體中,可以保留它的狀態或避免重新渲染。為此可以新增一個 keep-alive 指令引數 -->
        <keep-alive>
           <component :is="currentView"></component>
        </keep-alive>
      </div>
    </div>
  </body>
  <script src="./vue.js"></script>
  <script type="text/javascript">
     var tab01 = Vue.extend({
       template: '#tab-01'
     });
     var tab02 = Vue.extend({
       template: '#tab-02'
     });
     var tab03 = Vue.extend({
        template: '#tab-03'
     });
     // 新建vue例項
     var newVue = new Vue({
       el: '#container1',
       data: {
         tab01Text: "tab01",  // 選單一
         tab02Text: "tab02",  // 選單二
         tab03Text: "tab03",  // 選單三
         currentView: "tab01" // 預設選中的導航欄
       },
       // 區域性註冊元件
       components: {
         tab01: tab01,
         tab02: tab02,
         tab03: tab03,
       },
       methods: {
         // 繫結tab的切換事件
         toggleTabs: function(tabText) {
           this.currentView = tabText;
         }
       }
     })
  </script>
</html>

檢視效果

相關文章