手挽手帶你學VUE:二檔 元件開發以及常用全域性api

蔣吉兆發表於2019-02-16

視訊教程

由於思否不支援視訊連結 視訊請移步 http://www.henrongyi.top

你能學到什麼

二檔視訊當然要比一檔視訊難一點,如果前面的內容還沒有消化完畢的話,還是建議大家繼續消化前面的內容,然後再看接下來的部分。這一部分是VUE的核心,講到元件化開發的基礎部分,多學,多練。

生命週期

Vue官網給出的生命週期圖

<img src=`https://cn.vuejs.org/images/l…` >
這張圖裡包含了基本的生命週期,實際上生命週期鉤子函式還有兩個不在圖內(activated,deactivated),這兩個生命週期函式這裡不給大家講,如果大家有興趣可以自行到VUE官網看文件,工作中用到他們的機會不多,這邊講也會擾亂大家的學習程式。這裡我們通過程式碼來講解Vue的生命週期

<!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>
        .active{
            color:red
        }
    </style>
</head>
<body>
    <div id="app">
        <button v-on:click="add">+</button>{{num}}<button @click="minus">-</button>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
               num:1
            },
            methods:{
                add(){
                    this.num++
                },
                minus(){
                     this.num--
                }
            },
            beforeCreate:function(){
                console.log(`頁面初始化之後立刻執行`);
            },
            created:function(){
                console.log(`頁面初始化完成之後created,這裡模板還沒有渲染呢!`);
            },
            beforeMount:function(){
                console.log(`元件掛載之前beforeMount`);
            },
            mounted:function(){
                console.log(`元件掛載之後mounted`);
            },
            beforeUpdate:function(){
                console.log(`元件更新前beforeUpdate`);
            },
            updated:function(){
                console.log(`元件更新後updated`);
            },
        //   activated:function(){
        //      console.log(`activated`);
        //    },
        //    deactivated:function(){
        //        console.log(`deactivated`);
        //    },    你只需要知道這兩個生命週期是在這個位置進行就好了,別的可以不要想太多。
            beforeDestroy:function(){
                console.log(`元件銷燬之前beforeDestroy`);
            },
            destroyed:function(){
                console.log(`元件銷燬後destroyed`)
            }
        })
    </script>
</body>
</html>

這些生命週期函式大體就演示到這裡,剩下的兩個沒能給大家展示的請大家牢記,我們開始學元件的時候,就可以體會到這兩個鉤子函式的用法了。

元件建立

Vue.component
註冊全域性元件
Vue.component(“元件名”,{ 元件的各種屬性(模板啊,data啊,方法啊,生命週期啊之類的。) }), (視訊裡有一句口誤,應該是小駝峰。手動捂臉)

這個全域性API看過以後,我們來實現一個元件的hello world

<!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>
</head>
<body>
    <div id="app">
       <shuai-qm></shuai-qm>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component("shuai-qm",{
            data(){
                return{
                    hello:`hello world`
                }
            },
            template:`<h1>{{hello}}</h1>`
        })
        
        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello Word`,
                isTrue:true,
            },
        })
    </script>
</body>
</html>

一個全域性元件的hello world就完成了,但是大家是不是覺得全域性元件非常不踏實?隨時可能一個變動摧毀我們的專案?
那沒關係,接下來是區域性組建的註冊方法。

Vue的components屬性

我們在註冊區域性組建的時候,需要把區域性組建掛載到Vue構造器的components內,注意:全域性api不帶s,而這個屬性帶s。不說廢話,我們先來構造一個。

<!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>
</head>
<body>
    <div id="app">
    {{message}}
       <shuai-qm></shuai-qm>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
              components:{
                   "shuaiQm":{
                       data(){
                           return{
                               hello:"hello world"
                           }
                       },
                       template:`<h1>{{hello}}</h1>`
                       }
                }
            
        })
    </script>
</body>
</html>

這樣我們就得到了一個可以hello world的區域性元件,但是,一堆組建都這個樣子羅列出來的話,會將我們的專案弄得體無完膚?
所以我們抽離一下程式碼。變成這樣

<!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>
</head>
<body>
    <div id="app">
    {{message}}
      <shuai-qm></shuai-qm>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var shuaiQm = {
            data(){
                return{
                    hello:"hello world"
                }
            },
            template:`<h1>{{hello}}</h1>`
            }


        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
            components:{
                   "shuaiQm":shuaiQm,
            }
        })
    </script>
</body>
</html>

是不是用vue-cli開發過的同學就比較眼熟了?那麼問題又來了,元件裡面的html全都是寫的字串,都這麼玩下去,要亂死啊,沒問題,我們繼續抽離。

這裡介紹的就是vue的template用法了,馬上又是一個cli黨眼熟的東西

<!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>
</head>

<template id="shuaiQm">
    <h1>{{hello}}</h1>
</template>

<!-- 上面是html模板的用法,下面是script模板的用法,使用<script>標籤時,type指定為text/x-template,意在告訴瀏覽器這不是一段js指令碼,瀏覽器在解析HTML文件時會忽略<script>標籤內定義的內容。-->

<!-- 推薦 -->
<script type="x-template" id="shuaiQm2">
    <h2 style="color:blue">{{hello}}</h2>
</script>

<body>
    <div id="app">
    {{message}}
       <shuai-qm></shuai-qm>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var shuaiQm = {
            data(){
                return{
                    hello:"hello world"
                }
            },
            template:"#shuaiQm2"
            }


        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
            components:{
                   "shuaiQm":shuaiQm,
            }
        })
    </script>
</body>
</html>

拆出來以後是不是莫名其妙引起了極度舒適?當然後面我們學到使用vue-cli的時候 我們還會用到新的書寫方法 .vue 檔案的方法,這裡就先不給大家多說了。

元件裡面套元件(父子元件)

可能由同學會問了,元件裡面還可以巢狀元件嗎?我可以很負責任地告訴你,完全沒問題!怎麼寫呢?你如果會在Vue例項的構造器裡引用元件,你就會在別的元件內部引用元件,他們其實是一個寫法。這邊我使用我們的第三種模板寫法來給大家書寫。

<!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>
</head>

<script type="x-template" id="father">
  <div>
    <h2 style="color:blue">{{hello}}</h2>
    <childer  />
  </div>
</script>

<script type="x-template" id="childer">
    <h2 style="color:blue">{{hello}}</h2>
</script>

<body>
    <div id="app">
    {{message}}
    <shuai-qm></shuai-qm>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var childer = {
            data(){
                return{
                    hello:"hello i`m dawangraoming"
                }
            },
            template:"#childer"
        }


        var father = {
            data(){
                return{
                    hello:"hello world"
                }
            },
            template:"#father",
            components:{
                "childer":childer
            }
            }




        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
            components:{
                   "shuaiQm":father,
            }
        })
    </script>
</body>
</html>

這裡大家也可以看到,我們在 app內部只引用了 shuaiQm這一個元件, shuaiQm又包含他的子元件 childer,因此父子都被渲染出來了。這就是父子元件的寫法。

插槽slot

這時候又有朋友要問了,如果我想在元件裡面繼續書寫html怎麼辦呢? slot插槽就是個很好的東西了,這裡我用程式碼給大家演示一下slot插槽的用法。

<!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>
</head>

<script type="x-template" id="shuaiQm2">
    <div style="color:blue">
        <h2>{{hello}}</h2>
        <solt name="solt1">我是插槽solt1</solt>
        <solt name="solt2">我是插槽solt2</solt>
        <!-- 如果插槽不使用的話,內部的預設文字就會展示出來 -->
    </div>
</script>

<body>
    <div id="app">
    {{message}}
       <shuai-qm>
           <span slot="solt1">hello world</span>
       </shuai-qm>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var shuaiQm = {
            data(){
                return{
                    hello:"hello world"
                }
            },
            template:"#shuaiQm2"
            }


        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
            components:{
                   "shuaiQm":shuaiQm,
            }
        })
    </script>
</body>
</html>

插槽只有這一個作用嗎?不,那你就太小看插槽了,接下來要介紹一下插槽的作用域插槽用法。

作用域插槽,聽不懂可跳過,後面還會詳細講解

使用時候子元件標籤Child中要有 template scope=”scopeName” 標籤,再通過scopeName.childProp就可以呼叫子元件模板中的childProp繫結的資料,所以作用域插槽是一種子傳父傳參的方式,解決了普通slot在parent中無法訪問child資料的去問題;
這麼說太複雜了,直接上個例子顯而易見。
如果這裡聽不懂可以暫時跳過,只需要會用slot插槽的基礎用法即可,在後面講Element專案的時候,我會結合例項給大家講解這個作用域插槽。

<!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>
</head>

<script type="x-template" id="shuaiQm2">
    <div style="color:blue">
        <h2>{{hello}}</h2>
        <slot name="item" v-for="item in items" :text="item.text" :myname="item.myname" >
            slot的預設內容
        </slot>
    </div>
</script>

<body>
    <div id="app">
    {{message}}
       <shuai-qm>
          <template slot="item" scope="props">
            <li>{{props.myname}}</li>
            <li>{{props.text}}</li>
          </template>
       </shuai-qm>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var shuaiQm = {
            data(){
                return{
                    hello:"hello world",
                    items:[{
                      text:"我在子元件內,通過插槽展示在父元件",
                      myname:"Qm",
                    },{
                      text:"我在子元件內,通過插槽展示在父元件",
                      myname:"奇淼",
                    }]
                }
            },
            template:"#shuaiQm2"
            }


        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
            components:{
                   "shuaiQm":shuaiQm,
            }
        })
    </script>
</body>
</html>

prop 傳遞引數給元件(父傳值給子)

講到這裡,已經到了VUE一個需要理解的地方了,父子傳值,我們先講解一下,如何將值傳遞給子元件,這個整體來說還是比較簡單。引用我們的元件的標籤上寫上屬性,並且把引數傳入,這樣我們在元件內部使用props就可以獲得傳過來的值了,我們還是以上面的程式碼為例。

<!DOCTYPE html>
<html lang="zh-cn">
<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>
</head>

<script type="x-template" id="father">
  <div>
    <h2 style="color:blue">{{hello}}</h2>
    {{apptoshuaiqm}}
    <childer :shuai-qmtochilder="shuaiQmGiveMe" />
  </div>
</script>

<script type="x-template" id="childer">
    <div>
        <h2 style="color:blue">{{hello}}</h2>
        {{shuaiQmtochilder}}
    </div>
</script>

<body>
    <div id="app">
    <shuai-qm apptoshuaiqm="我是app傳過來的值" ></shuai-qm>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var childer = {
          props:[`shuaiQmtochilder`],
            data(){
                return{
                    hello:"hello i`m dawangraoming",
                }
            },
            template:"#childer"
        }


        var father = {
            props:["apptoshuaiqm"],// 這裡大家一定要注意,請完全使用小寫傳參
            data(){
                return{
                    hello:"hello world",
                    shuaiQmGiveMe:"我是從shuaiQm傳過來的值"
                }
            },
            template:"#father",
            components:{
                "childer":childer
            }
            }




        var app = new Vue({
            el:"#app",
            data:{
                message:`Hello World`,
                isTrue:true,
            },
            components:{
                   "shuaiQm":father,
            }
        })
    </script>
</body>
</html>

這一段程式碼注意,再給html上面新增屬性的時候,我們是不可以直接新增駝峰命名的,因為html不會區分大小寫,所以我們建議屬性的命名方式是完全小寫或者橫線命名的方式。如果我們使用橫線命名來傳遞引數的話,在接收的時候,橫線後面的首字母大寫,變成小駝峰來接受,否則使用的時候它會被渲染成NaN,這是為什麼呢?別忘了我們一檔講過的,在插值表示式內,是支援簡單計算的,- 會被當作減號處理,這裡我會在視訊中給大家詳細講解。

子元件傳值給父元件

學到這裡,如果大家已經有些迷茫,現在請先停下,喘口氣,這裡難度已經慢慢加大。我也會放慢講解的速度。
如果我們想要獲取到子元件內部的值,該怎麼辦呢?有什麼辦法能夠讓我們回去到內部的值呢?在這裡,先給大家插播一個JS寫法,我覺得這有助於理解子傳父值。

function thief (gold) {
    console.log(gold)
}

function richMan (){
    var money = 1000086
    thief(money)
}

richMan()

我們想要在vue中做到子傳參給父,那我們的父元件就要像子元件伸出小偷之手。我在程式碼中為大家書寫一下

<!DOCTYPE html>
<html lang="zh-cn">
<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>
</head>

<script type="x-template" id="shuaiQm">
  <div>
   
  </div>
</script>

<body>
    <div id="app">
      {{qmGold}}
    <shuai-qm :father="thief"></shuai-qm>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        var shuaiQm = {
            props:["father"],
            data(){
                return{
                    money:"10000",
                }
            },
            template:"#shuaiQm",
            created() {
              this.father(this.money)
            },
            }



        var app = new Vue({
            el:"#app",
            data:{
              qmGold:0,
            },
            components:{
                   "shuaiQm":shuaiQm,
            },
            methods:{
              thief(gold){
                this.qmGold = gold
              }
            }
        })
    </script>
</body>
</html>

這樣 你理解子傳參給父了嗎?

其餘會用到的全域性API

Vue.directivet
Vue.directive 我們用來編寫全域性指令,它也有自己的生命週期

// 註冊
Vue.directive(`my-directive`, {
  bind: function () {},
  inserted: function () {},
  update: function () {},
  componentUpdated: function () {},
  unbind: function () {}
})

/*
bind:只呼叫一次,指令第一次繫結到元素時呼叫。在這裡可以進行一次性的初始化設定。

inserted:被繫結元素插入父節點時呼叫 (僅保證父節點存在,但不一定已被插入文件中)。

update:所在元件的 VNode 更新時呼叫,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。但是你可以通過比較更新前後的值來忽略不必要的模板更新 (詳細的鉤子函式引數見下)。

componentUpdated:指令所在元件的 VNode 及其子 VNode 全部更新後呼叫。

unbind:只呼叫一次,指令與元素解綁時呼叫。

接下來我們來看一下鉤子函式的引數 (即 el、binding、vnode 和 oldVnode)。

在這些鉤子函式內部,都可以接受三個引數,我們來看看文件中的寫法。
el:指令所繫結的元素,可以用來直接操作 DOM 。

binding:一個物件,包含以下屬性:
    name:指令名,不包括 v- 字首。
    value:指令的繫結值,例如:v-my-directive="1 + 1" 中,繫結值為 2。
    oldValue:指令繫結的前一個值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。
    expression:字串形式的指令表示式。例如 v-my-directive="1 + 1" 中,表示式為 "1 + 1"。
    arg:傳給指令的引數,可選。例如 v-my-directive:foo 中,引數為 "foo"。
    modifiers:一個包含修飾符的物件。例如:v-my-directive.foo.bar 中,修飾符物件為 { foo: true, bar: true }。
vnode:Vue 編譯生成的虛擬節點。
oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。

這裡我會在視訊中結合官方樣例講解
*/

上面我們羅列了這麼多它的特性,不過真正開發中,我們最常用的只有 bind 和 update 這兩個時期
我們可以簡寫為

    Vue.directive(`color`, function (el, binding) {
        el.style.backgroundColor = binding.value
    })

下面我們來舉個例子

<!DOCTYPE html>
<html lang="zh-cn">
<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>
</head>

<script type="x-template" id="shuaiQm">
  <div>
   
  </div>
</script>

<body>
    <div id="app">
      <div v-color="color">
          我來測試測試directive
      </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.directive("color",function(el,binding){
            el.style.color = binding.value
        })

        var app = new Vue({
            el:"#app",
            data:{
                color:"red"
            }
        })
    </script>
</body>
</html>

好了我們可以看到加上v-color的這個div內部的文字變紅了

Vue.set

Vue.set官網給出的用法是 Vue.set( target, key, value ) 向響應式物件中新增一個屬性,並確保這個新屬性同樣是響應式的,且觸發檢視更新。它必須用於向響應式物件上新增新屬性,因為 Vue 無法探測普通的新增屬性
這麼聽起來是有些籠統的,我給大家用程式碼展示一下它在我們日常開發中經常出現的場景。

<!DOCTYPE html>
<html lang="zh-cn">
<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>
</head>

<body>
    <div id="app">
      <ul>
        <li v-for="(item,ket) in list" :key="key">{{item.hello}}</li>
      </ul>
      <button @click="addList">+</button>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        var app = new Vue({
            el:"#app",
            data:{
                list:[{hello:"hello world"},{hello:"hello two"}]
            },
            methods:{
                addList(){
                   this.list[0] = {hello:"ttt"}
                    console.log(this.list)
                }
            }
        })
    </script>
</body>
</html>

在上述程式碼中,我們通過this.list[0]直接修改了陣列中的第0專案物件,那麼檢視是沒有更新的,但是資料確實變更了,這是為什麼呢?因為Vue是通過Object.defineProperty()來進行資料的監聽,它的機制導致了它無法直接檢測出陣列中這種情況的變化。這時候我們就需要使用Vue.set了

<!DOCTYPE html>
<html lang="zh-cn">
<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>
</head>

<body>
    <div id="app">
      <ul>
        <li v-for="(item,ket) in list" :key="key">{{item.hello}}</li>
      </ul>
      <button @click="addList">+</button>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        var app = new Vue({
            el:"#app",
            data:{
                list:[{hello:"hello world"},{hello:"hello two"}]
            },
            methods:{
                addList(){
                   this.list[0] = {hello:"ttt"}
                    console.log(this.list)
                }
            }
        })
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-cn">
<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>
</head>

<body>
    <div id="app">
      <ul>
        <li v-for="(item,ket) in list" :key="key">{{item.hello}}</li>
      </ul>
      <button @click="addList">+</button>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>

        var app = new Vue({
            el:"#app",
            data:{
                list:[{hello:"hello world"},{hello:"hello two"}]
            },
            methods:{
                addList(){
                //    this.list[0] = {hello:"ttt"}
                    Vue.set(this.list,0, {hello:"我強制改變了!"})
                    // this.$set(this.list,0,{hello:"我強制改變了!"})  在methods 中可以寫成 this.$set 
                    console.log(this.list)
                }
            }
        })
    </script>
</body>
</html>

看 是不是強制將它改變了呢? 有了Vue.set 資料就都不再得瑟了

相關文章