vue部分高級特性

概述

文章將講述指令、混入、高階組件、函數式組件、@hook、異步組件等內容。若是文中有不當的地方歡迎指正哦!html

特性以及部分原理

自定義指令(directive)

除了核心功能默認內置的指令 (v-model 和 v-show),Vue 也容許註冊自定義指令。有時候咱們想對dom進行操做的時候,就可使用自定義指令,好比設置標題樣式而且讓標題一直固定在頁面上方,可使用全局註冊或者局部註冊。而後你能夠在模板中任何元素上使用新的 v-title property。vue

//全局註冊
<div id="app">
    <div v-title>hello world</div>
</div>
<script>
    Vue.directive('title', {
        inserted: function (el) {
            console.log(el)
            el.style.position = 'fixed' 
            el.style.top = '50px' 
            el.style.left = '48%' 
            el.style.color = '#409EFF' 
        }
    })

    new Vue({
        el: '#app',
        data: {
            message: 'hello!'
        }
    })
</script>
<style>
   #app{
       height: 1000px
   } 
</style>
//局部註冊
  new Vue({
    el: '#app',
    directives: {
        title: {
            inserted: function (el) {
                console.log(el)
                el.style.position = 'fixed'
                el.style.top = '50px'
                el.style.left = '48%'
                el.style.color = '#409EFF'
            }
        }
    }
  })
複製代碼

image.png

directive鉤子函數參數

指令鉤子函數會被傳入如下參數:node

  • el:指令所綁定的元素,能夠用來直接操做 DOM。
  • binding:一個對象,包含如下 property:
  1. name:指令名,不包括 v- 前綴。
  2. value:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值爲 2。
  3. oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。不管值是否改變均可用。
  4. expression:字符串形式的指令表達式。例如 v-my-directive="1 + 1" 中,表達式爲 "1 + 1"。
  5. arg:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數爲 "foo"。
  6. modifiers:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象爲 { foo: true, bar: true }。
  • vnode:Vue 編譯生成的虛擬節點。移步 VNode API 來了解更多詳情。
  • oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。

咱們打印下函數傳入的參數,其實簡單來講就是el就是綁定dom元素,binging指令:後所攜帶的具體內容,VNode就當還未生成的節點好了。webpack

<div v-title:arr="message">hello world</div>
Vue.directive('title', {
        inserted: function (el, binding, vnode) {
            console.log(el, binding, vnode)
            el.style.position = 'fixed' 
            el.style.top = '50px' 
            el.style.left = '48%' 
            el.style.color = '#409EFF' 
        }
    })
複製代碼

image.png

鉤子函數

一個指令定義對象能夠提供以下幾個鉤子函數 (均爲可選):es6

  • bind:只調用一次,指令第一次綁定到元素時調用。在這裏能夠進行一次性的初始化設置。
  • inserted:被綁定元素插入父節點時調用 (僅保證父節點存在,但不必定已被插入文檔中)。
  • update:所在組件的 VNode 更新時調用,可是可能發生在其子 VNode 更新以前。指令的值可能發生了改變,也可能沒有。可是你能夠經過比較更新先後的值來忽略沒必要要的模板更新 (詳細的鉤子函數參數見下)。
  • componentUpdated:指令所在組件的 VNode 及其子 VNode 所有更新後調用
  • unbind:只調用一次,指令與元素解綁時調用。

咱們能夠測試下鉤子函數的調用時機:web

<div id="app">
    <div id="txt" v-title:data="sum">value: {{sum}}</div>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            sum: 0
        },
        directives: {
            title: {
                bind: (el, bind) => { console.log(bind.value, 'a') },// 第一次綁定元素時調用
                inserted: (el, bind) => { console.log(bind.value, 'b') },// 當被綁定的元素插入到 DOM 中時……
                update: (el, bind) => { console.log(bind.value, 'c') },// 所在組件VNode發生更新時調用
                componentUpdated: (el, bind) => { console.log(bind.value, 'd') }, // 指令所在組件的 VNode 及其子 VNode 所有更新後調用
                unbind: (el, bind) => { console.log(bind.value, 'e') }    // 只調用一次,指令與元素解綁時調用
            }
        },
        mounted() {
            console.log(this.sum, '???')
            let timer = setInterval(() => {
                this.sum++
            }, 200)
            setTimeout(() => {
                clearInterval(timer)
            }, 3000)
        }
    })
</script>
複製代碼

image.png

指令大體原理

在頁面渲染的過程當中,分別有建立(create)、激活(avtivate)、更新(update)、移除(remove)、銷燬(destroy),在這些過程當中,框架在每一個時段都會調用相應的鉤子函數,這些hooks中一部分的函數就包含了咱們的指令。源碼部分我瞭解的很少,給你們推薦一篇vue指令原理相關博文www.cnblogs.com/gerry2019/p…vue-router

混入

官方是這樣定義的:混入 (mixin) 提供了一種很是靈活的方式,來分發 Vue 組件中的可複用功能。一個混入對象能夠包含任意組件選項。當組件使用混入對象時,全部混入對象的選項將被「混合」進入該組件自己的選項。其實就是vue實例的一個複用。實用場景:公共組件或者功能,例如獲取用戶白名單、菜單返回、公共基礎table。 值得注意的點:vue-cli

  1. 當組件和混入對象含有同名選項時,這些選項將以恰當的方式混合。好比,數據對象在內部會進行淺合併 (一層屬性深度),在和組件的數據發生衝突時以組件數據優先。
  2. 同名鉤子函數將混合爲一個數組,所以都將被調用。另外,混入對象的鉤子將在組件自身鉤子以前調用。
  3. 值爲對象的選項,例如 methods, components 和 directives,將被混合爲同一個對象。兩個對象鍵名衝突時,取組件對象的鍵值對。
var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})
複製代碼

高階組件

一個函數接受一個組件爲參數,返回一個包裝後的組件。其實在vue中,組件能夠當作一個函數,那從本質上來講,高階組件就是高階函數(JavaScript的函數其實都指向某個變量。既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就稱之爲高階函數)express

高階函數

舉例一個最簡單的高階函數計算次方api

function pow(x, y, f){
    return f(x, y);
  }
  pow(3, 3, Math.pow)
複製代碼

在es6中也有不少高階函數,如map、reduce、filter。

高階組件的例子

<div id="app">
    <hoc></hoc>
</div>
<script>
    const view = {
        template: `<span>
                    <span>test hoc ...</span>
                    </span>`,
        props: ["result", "loading"],
    };
    const test = (wrapped, txt = 'hello') => {
        return {
            render(h) {
                const args = {
                    props: {
                        result: this.result,
                        loading: this.loading,
                    },
                };
                const wrapper = h("div", [
                    h(wrapped, args),
                    'loading'
                ]);
                return wrapper
            }
        }
    }
    const hoc = test(view, 'hui')
    console.log(hoc);

    new Vue({
        el: '#app',
        components: {
            hoc
        },
        data: {
            sum: 0
        }
    })
</script>
複製代碼

image.png

值得注意的點

  1. 高階組件(HOC)應該是無反作用的純函數,且不該該修改原組件,就是組件是一個新的組件,不會對原組件作修改。
  2. 高階組件(HOC)不關心你傳遞的數據(props)是什麼,而且被包裝組件(WrappedComponent)不關心數據來源
  3. 高階組件(HOC)接收到的 props 應該透傳給被包裝組件(WrappedComponent)
  4. 在高階組件中渲染函數向子組件中傳遞做用域插槽時候要注意上下文

動態組件 異步組件 遞歸組件

動態組件

能夠在同組件之間進行動態切換, 動態切換能夠經過 Vue 的 元素加一個特殊的 is attribute 來實現:

<!-- 組件會在 `currentTabComponent` 改變時改變 -->
<!DOCTYPE html>
<html>
  <head>
    <title>Dynamic Components Example</title>
    <script src="https://unpkg.com/vue"></script>
    <style>
      .tab-button {
        padding: 6px 10px;
        border-top-left-radius: 3px;
        border-top-right-radius: 3px;
        border: 1px solid #ccc;
        cursor: pointer;
        background: #f0f0f0;
        margin-bottom: -1px;
        margin-right: -1px;
      }
      .tab-button:hover {
        background: #e0e0e0;
      }
      .tab-button.active {
        background: #e0e0e0;
      }
      .tab {
        border: 1px solid #ccc;
        padding: 10px;
      }
    </style>
  </head>
  <body>
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab }]"
        v-on:click="currentTab = tab"
      >
        {{ tab }}
      </button>

      <component v-bind:is="currentTabComponent" class="tab"></component>
    </div>

    <script>
      Vue.component("tab-home", {
        template: "<div>Home component</div>"
      });
      Vue.component("tab-posts", {
        template: "<div>Posts component</div>"
      });
      Vue.component("tab-archive", {
        template: "<div>Archive component</div>"
      });

      new Vue({
        el: "#dynamic-component-demo",
        data: {
          currentTab: "Home",
          tabs: ["Home", "Posts", "Archive"]
        },
        computed: {
          currentTabComponent: function() {
            return "tab-" + this.currentTab.toLowerCase();
          }
        }
      });
    </script>
  </body>
</html>
複製代碼

異步組件

在大型應用中,咱們可能須要將應用分割成小一些的代碼塊,而且只在須要的時候才從服務器加載一個模塊。爲了簡化,Vue 容許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件須要被渲染的時候纔會觸發該工廠函數,且會把結果緩存起來供將來重渲染。

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回調傳遞組件定義
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})
複製代碼

在vue-cli中在使用異步組件

const first =()=>import(/* webpackChunkName: "group-foo" */ "../components/first.vue");
複製代碼

vue中部分鉤子函數(@hook)

Vue 實例同時在其事件接口中提供了其它的方法。咱們能夠:

經過 $on(eventName, eventHandler) 偵聽一個事件

經過 $once(eventName, eventHandler) 一次性偵聽一個事件

經過 $off(eventName, eventHandler) 中止偵聽一個事件

你一般不會用到這些,可是當你須要在一個組件實例上手動偵聽事件時,它們是派得上用場的。它們也能夠用於代碼組織工具。例如,你可能常常看到這種集成一個第三方庫的模式。官網提供一個案例:在不使用beforeDestroy鉤子清picker

//案例一
mounted: function () {
  var picker = new Pikaday({
    field: this.$refs.input,
    format: 'YYYY-MM-DD'
  })

  this.$once('hook:beforeDestroy', function () {
    picker.destroy()
  })
}
//案例二
//在父組件在子組件渲染階段作一些操做
<child
  @hook:mounted="handle"
  @hook:beforeUpdated="xxx"
  @hook:updated="xxx"
/>
method () {
  handle() {
  // do something...
  }
},
複製代碼

在vue生命週期中週期都有對應的鉤子函數

插件

插件一般用來爲 Vue 添加全局功能。插件的功能範圍沒有嚴格的限制——通常有下面幾種:

  • 添加全局方法或者 property。如:vue-custom-element

  • 添加全局資源:指令/過濾器/過渡等。如 vue-touch

  • 經過全局混入來添加一些組件選項。如 vue-router

  • 添加 Vue 實例方法,經過把它們添加到 Vue.prototype 上實現。

  • 一個庫,提供本身的 API,同時提供上面提到的一個或多個功能。如 vue-router

自定義插件

MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {
    // 邏輯...
  }

  // 2. 添加全局資源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 邏輯...
    }
    ...
  })

  // 3. 注入組件選項
  Vue.mixin({
    created: function () {
      // 邏輯...
    }
    ...
  })

  // 4. 添加實例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 邏輯...
  }
}
複製代碼

後續

文章都在在官網文檔上的整理和概括,只是停留在表層,歡迎你們指正。定下 下一個目標:寫一個小型vue-router。

引用

vue官網 cn.vuejs.org/v2/api
探索Vue高階組件 www.jianshu.com/p/6b149189e…
Vue 進階必學之高階組件 HOC zhuanlan.zhihu.com/p/126552443
Vue指令實現原理 www.cnblogs.com/gerry2019/p…

相關文章
相關標籤/搜索