Vue還有這種騷操作?淺析幾個新手常常忽略的API

陳杰夫發表於2018-04-22

一:實現子元件與父元件雙向繫結的【sync】修飾符:

一般來說,我們實現父子元件值的傳遞通常使用的是【props】和自定義事件【$emit】。父元件通過【props】將值傳給子元件,子元件通過【$emit】將值傳給父元件,父元件通過【$on】事件獲取子元件傳過來的值,如果說想要實現子元件修改父元件傳過來的值,最容易的就是這種方法了:

//父元件向子元件傳值
<template>
    <div>
        <child-com :value="text"></child-com>
    </div>
</template>

<script>
    export default{
        data(){
            return{
                 text:"父元件的值",
             }
        }   
    }
</script>         複製程式碼

//子元件向父元件傳值
<template>
    <div @click="post"></div>
</template>

<script>
    export default{
        methods:{
            post(){
                this.$emit('getChildValue',"子元件的值")
            }
        }
    }
</script>複製程式碼

此時父元件可以通過【$on】獲取子元件的值:

<template>
    <div>
        <child-com  @getChildValue = "getValue"></child-com>
    </div>
</template>

<script>
    export default{
        methods:{
            getValue(child_value){
                this.text = child_value;
            }
        }
    }
</script>複製程式碼

這樣,就可以實現子元件修改父元件的值。

不過,這種方法有一個弊端——子元件修改父元件的值需要一個傳遞的過程,或者說,兩個值並不是同步的

熟悉Vue1.0的朋友應該知道一個叫【.sync】的修飾符,它可以實現父子元件的雙向繫結,不過在Vue2.0被移除了,直到2.3.0版本釋出後才重新迴歸,所以一些和我一樣從2.0開始使用Vue的朋友很有可能不清楚,事實上,【.sync】可以很輕鬆的實現子元件同步修改父元件的值:

//父元件
<template>
    <div>
        <child-com :value.sync="text" ></child-com>
    </div>
</template>
<script>
    export default{
        data(){
            return {
                text:"父元件的值",
            }
        },
    }
</script>

==============================================================================================================
//子元件修改父元件的值
<template>
    <div @click="post"></div>
</template>

<script>
    export default{
        methods:{
            post(){
                this.$emit('update:value',"子元件的值")
            }
        }
    }
</script>複製程式碼

(感謝@糖小面兒 ,@asuishi ,@李佳毅 指出文中this.$emit('update:data',"子元件的值")的錯誤。)

我們可以看到,對於子元件來說,僅僅是自定義事件名做了一點改變,但是就程式碼底層邏輯來說,子元件和父元件真正實現了同步的雙向繫結

當然,正如文件所說:

.sync修飾符很方便,但也會導致問題,因為破壞了單向資料流。由於子元件改變 prop 的程式碼和普通的狀態改動程式碼毫無區別,當光看子元件的程式碼時,你完全不知道它何時悄悄地改變了父元件的狀態。這在 debug 複雜結構的應用時會帶來很高的維護成本。

二:自定義指令:“directives”:

關於自定義指令文件其實介紹的比較詳細了,而且還舉了一個非常詳細的例子:自定義指令

自定義指令其實就是Vue為我們提供直接操作dom的一些列方法,雖然大部分開發時間都會面向資料,但說不準什麼時候確實需要操作dom本身。

就我而言,自定義指令最大的用處就是可以引用一些第三方的程式碼插入到Vue專案中,比如有一個操作dom的函式:

//當然,真實情況第三方的程式碼要複雜的多
function changeColor(dom){
    dom.style.backgroundColor = "red";
}複製程式碼

我們可以註冊一個全域性的指令來為需要執行changeColor方法的dom提供指令:

Vue.directives('color',{
    bind:function(el){
        changeColor(el)
    }
})複製程式碼

這樣,如果需要這個dom改變顏色的話,只需要這樣即可:

<div v-color>改變顏色</div>複製程式碼

當日常開發遇到跟dom有關的問題卻一籌莫展時,可以想想自定義指令是否有功能可以解決為題。

三: inheritAttrs和attrs:

前面我已經提到過了,父元件通過props可以向子元件傳值,但在日常的開發中,還有一種情況很常見,就是父元件給子元件傳值,這個值還要從子元件傳給它的子元件,所以,我們可能會看到這樣的程式碼:

//父元件
<div>
    <child :text="text"></child>
</div>

//子元件
<div>
    <my-child :text="text"></my-child>
</div>

//子元件的子元件
<div>
    <div>{{text}}</div>
</div>複製程式碼

這樣做是非常麻煩而且不易於維護的,通常情況下,我們可以使用vuex來解決。不過,不復雜的專案中如果僅僅為這一個問題就引入vuex實際上是沒必要的,Vue提供了【inheritAttrs】和【attrs】兩個功能來解決這樣的問題:

//父元件
<template>
   <div>
     <child :text="text"  :count="count"></child>
   </div>
</template>

<script>
    export default{
        data(){
            return {
                text:"父元件的值",
                count:123456,
            }
        }
    }
</script>複製程式碼

//子元件
<template>
    <div>{{text}}</div>
</template>
<script>
    export default{
        props:["text"]
    }
</script>複製程式碼

注意,這裡父元件的count屬性僅僅掛在子元件上,並沒有使用。此時我們開啟瀏覽器,可以看到子元件的dom上顯示的展示了count="123456"。

Vue還有這種騷操作?淺析幾個新手常常忽略的API

此時,我們可以通過設定inheritAttrs: false來取消這種預設行為:

data(){
  return{
    ......   
  }  
}

inheritAttrs: false,
mounted(){
  console.log(this.$attrs); //{count:123456}
}複製程式碼

這時再看dom上就沒有count屬性了。然後,我還列印了this.$attrs的值,值為一個包含著count鍵值對的Object。

也就是說,父元件沒有props的屬性值會被儲存在一個名為$attrs中供子元件使用,然而這並沒有解決開頭子元件的子元件獲取值的問題。別急,我們只需要在子元件上加個東西就可以了:

<template>
    <div class="child">
        <my-child v-bind="$attrs"></my-child>
    </div>
</template>複製程式碼

這樣,子元件的子元件也可以獲取這個值了。

四:混入——mixins:

其實這個功能有些類似於es6中的Object.assign()方法。根據一定的規則合併兩個配置,具體的混入策略可以看官方文件:mixins混入策略

混入最大的用處是把一些常用的data或者methods等抽出來,比如在我的專案中有許多個模態框,而關閉模態框的程式碼邏輯是一模一樣的,為此我沒有必要在多個元件中重複把關閉模態框的邏輯寫入methods中,只需要在外面定義一個mixins,在需要的元件中通過:mixins: [myMin]寫入即可。

var mixin = {
  methods: {
    close: function () {
      this.showModal = false; //關閉模態框
    },
  }
}

var vm = new Vue(
    mixins: [mixin],
    .......
})

複製程式碼

五:provide / inject:

首先感謝評論區大佬們的提醒,provide/inject方法要比inheritAttrs/attrs更適合用來做父元件給子元件或孫元件傳值,先發一個文件的連結:provide/inject

//父元件使用provide
<template>  <div class="parent">      <child-component></child-component>    </div>  </div></template>
<script>export default {  ......  provide: {    parent: "父元件的值"  },  components:{    child-component,  },  ......</script>

//此時可以在子元件通過這種方式獲取父元件中“parent”的值:
//子元件中
export default {  mounted(){      console.log(this.parent); //"父元件的值"  },  inject: ['parent'],}複製程式碼

這塊兒內容我也是剛看文件,感興趣的朋友可以仔細看看文件,自己敲一遍看看。


相關文章