Vue學習筆記10-深刻了解混入、渲染函數節點樹相關

過渡與動畫

這塊放到後面的開發demo裏再寫吧,說不定可以結合數據可視化組件進行分析寫。html

混入

混入 (mixin) 提供了一種很是靈活的方式,來分發 Vue 組件中的可複用功能。一個混入對象能夠包含任意組件選項。當組件使用混入對象時,全部混入對象的選項將被「混合」進入該組件自己的選項。vue

// 定義一個混入對象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定義一個使用混入對象的組件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"
複製代碼

選項合併

當組件和混入對象含有同名選項時,這些選項將以恰當的方式進行「合併」。node

數據對象在內部會進行遞歸合併,並在發生衝突時以組件數據優先react

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" }
  }
})
複製代碼

同名鉤子函數將合併爲一個數組,所以都將被調用。另外,混入對象的鉤子將在組件自身鉤子以前調用vuex

var mixin = {
  created: function () {
    console.log('混入對象的鉤子被調用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('組件鉤子被調用')
  }
})

// => "混入對象的鉤子被調用"
// => "組件鉤子被調用"
複製代碼

值爲對象的選項,例如 methods、components 和 directives,將被合併爲同一個對象。兩個對象鍵名衝突時,取組件對象的鍵值對。編程

var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
複製代碼

反正衝突時候組件數據最大就對了。數組

Vue.extend() 也使用一樣的策略進行合併。bash

全局混入

混入也能夠進行全局註冊。使用時格外當心!一旦使用全局混入,它將影響每個以後建立的 Vue 實例。使用恰當時,這能夠用來爲自定義選項注入處理邏輯。app

// 爲自定義的選項 'myOption' 注入一個處理器。
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello!'
})
// => "hello!"
複製代碼

慎用!!!dom

自定義選項合併策略

自定義選項將使用默認策略,即簡單地覆蓋已有值。若是想讓自定義選項以自定義邏輯合併,能夠向 Vue.config.optionMergeStrategies 添加一個函數。(不過模塊化以後感受基本用不到了啦)

Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
  // 返回合併後的值
}
複製代碼

對於多數值爲對象的選項,可使用與 methods 相同的合併策略

var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods
複製代碼
一個更高級的例子
const merge = Vue.config.optionMergeStrategies.computed
Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) {
  if (!toVal) return fromVal
  if (!fromVal) return toVal
  return {
    getters: merge(toVal.getters, fromVal.getters),
    state: merge(toVal.state, fromVal.state),
    actions: merge(toVal.actions, fromVal.actions)
  }
}
複製代碼

渲染函數&JSX

Vue 推薦在絕大多數狀況下使用模板來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力。這時你能夠用渲染函數,它比模板更接近編譯器。

假設咱們要生成一些帶錨點的標題

<h1>
  <a name="hello-world" href="#hello-world">
    Hello world!
  </a>
</h1>
對於上面的 HTML,你決定這樣定義組件接口
<anchored-heading :level="1">Hello world!</anchored-heading>


<h1 v-if="level === 1">
    <slot></slot>
  </h1>

複製代碼

咱們來嘗試使用 render 函數替代一大串v-if與v-else-if

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 標籤名稱
      this.$slots.default // 子節點數組
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})
複製代碼

向組件中傳遞不帶 v-slot 指令的子節點時,好比 anchored-heading 中的 Hello world!,這些子節點被存儲在組件實例中的 $slots.default 中。

vm.$slots

用來訪問被插槽分發的內容。每一個具名插槽有其相應的 property (例如:v-slot:foo 中的內容將會在 vm.$slots.foo 中被找到)。default property 包括了全部沒有被包含在具名插槽中的節點,或 v-slot:default 的內容。

vm.$scopedSlots

用來訪問做用域插槽。對於包括 默認 slot 在內的每個插槽,該對象都包含一個返回相應 VNode 的函數。

做用域插槽函數如今保證返回一個 VNode 數組,除非在返回值無效的狀況下返回 undefined。

全部的 $slots 如今都會做爲函數暴露在 $scopedSlots 中。若是你在使用渲染函數,不論當前插槽是否帶有做用域,咱們都推薦始終經過 $scopedSlots 訪問它們。這不只僅使得在將來添加做用域變得簡單,也可讓你最終輕鬆遷移到全部插槽都是函數的 Vue 3。

節點、樹以及虛擬 DOM(超級重點哦!)

<div>
  <h1>My title</h1>
  Some text content
  <!-- TODO: Add tagline -->
</div>
複製代碼

對應的DOM節點樹

DOM節點樹
每一個元素都是一個節點。每段文字也是一個節點。甚至註釋也都是節點。一個節點就是頁面的一個部分。就像家譜樹同樣,每一個節點均可以有孩子節點 (也就是說每一個部分能夠包含其它的一些部分)。

高效地更新全部這些節點會是比較困難的,不過所幸你沒必要手動完成這個工做。你只須要告訴 Vue 你但願頁面上的 HTML 是什麼

這能夠是在一個模板裏
<h1>{{ blogTitle }}</h1>

或者一個渲染函數裏

render: function (createElement) {
  return createElement('h1', this.blogTitle)
}
複製代碼

在這兩種狀況下,Vue 都會自動保持頁面的更新,即使 blogTitle 發生了改變。

虛擬DOM

Vue 經過創建一個虛擬 DOM 來追蹤本身要如何改變真實 DOM

上面的return到底會返回什麼呢?

其實不是一個實際的 DOM 元素。它更準確的名字多是 createNodeDescription,由於它所包含的信息會告訴 Vue 頁面上須要渲染什麼樣的節點,包括及其子節點的描述信息。咱們把這樣的節點描述爲「虛擬節點 (virtual node)」,也常簡寫它爲「VNode」。「虛擬 DOM」是咱們對由 Vue 組件樹創建起來的整個 VNode 樹的稱呼。

createElement 參數

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一個 HTML 標籤名、組件選項對象,或者
  // resolve 了上述任何一種的一個 async 函數。必填項。
  'div',

  // {Object}
  // 一個與模板中 attribute 對應的數據對象。可選。
  {
    // (詳情見下一節)
  },

  // {String | Array}
  // 子級虛擬節點 (VNodes),由 `createElement()` 構建而成,
  // 也可使用字符串來生成「文本虛擬節點」。可選。
  [
    '先寫一些文字',
    createElement('h1', '一則頭條'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)
複製代碼

深刻數據對象

正如 v-bind:class 和 v-bind:style 在模板語法中會被特別對待同樣,它們在 VNode 數據對象中也有對應的頂層字段。

該對象也容許你綁定普通的 HTML attribute,也容許綁定如 innerHTML 這樣的 DOM property (這會覆蓋 v-html 指令)

這塊詳見官方文檔吧,我也不是很搞得懂,後面再作補充好了

約束

VNode 必須惟一,組件樹中的全部 VNode 必須是惟一的。

這意味着,下面的渲染函數是不合法的

render: function (createElement) {
  var myParagraphVNode = createElement('p', 'hi')
  return createElement('div', [
    // 錯誤 - 重複的 VNode
    myParagraphVNode, myParagraphVNode
  ])
}
複製代碼

若是你真的須要重複不少次的元素/組件,你可使用工廠函數來實現。

render: function (createElement) {
  return createElement('div',
    Array.apply(null, { length: 20 }).map(function () {
      return createElement('p', 'hi')
    })
  )
}
複製代碼

使用 JavaScript 代替模板功能

v-if和v-for

只要在原生的 JavaScript 中能夠輕鬆完成的操做,Vue 的渲染函數就不會提供專有的替代方法。好比,在模板中使用的 v-if 和 v-for

<ul v-if="items.length">
  <li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>

//這些均可以在渲染函數中用 JavaScript 的 if/else 和 map 來重寫
props: ['items'],
render: function (createElement) {
  if (this.items.length) {
    return createElement('ul', this.items.map(function (item) {
      return createElement('li', item.name)
    }))
  } else {
    return createElement('p', 'No items found.')
  }
}
複製代碼

v-model(打個重點標記)

渲染函數中沒有與 v-model 的直接對應——你必須本身實現相應的邏輯。

props: ['value'],
render: function (createElement) {
  var self = this
  return createElement('input', {
    domProps: {
      value: self.value
    },
    on: {
      input: function (event) {
        self.$emit('input', event.target.value)
      }
    }
  })
}
複製代碼

事件 & 按鍵修飾符

對於 .passive、.capture 和 .once 這些事件修飾符,Vue 提供了相應的前綴能夠用於 on

Vue前綴

on: {
  '!click': this.doThisInCapturingMode,
  '~keyup': this.doThisOnce,
  '~!mouseover': this.doThisOnceInCapturingMode
}
複製代碼

對於全部其它的修飾符,私有前綴都不是必須的,由於你能夠在事件處理函數中使用事件方法(有種源碼重現的味兒了)

處理函數等價操做

插槽

你能夠經過 this.$slots 訪問靜態插槽的內容,每一個插槽都是一個 VNode 數組

render: function (createElement) {
  // `<div><slot></slot></div>`
  return createElement('div', this.$slots.default)
}
複製代碼

也能夠經過 this.$scopedSlots 訪問做用域插槽,每一個做用域插槽都是一個返回若干 VNode 的函數

props: ['message'],
render: function (createElement) {
  // `<div><slot :text="message"></slot></div>`
  return createElement('div', [
    this.$scopedSlots.default({
      text: this.message
    })
  ])
}
複製代碼

若是要用渲染函數向子組件中傳遞做用域插槽,能夠利用 VNode 數據對象中的 scopedSlots 字段

render: function (createElement) {
  // `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
  return createElement('div', [
    createElement('child', {
      // 在數據對象中傳遞 `scopedSlots`
      // 格式爲 { name: props => VNode | Array<VNode> }
      scopedSlots: {
        default: function (props) {
          return createElement('span', props.text)//??須要demo進行驗證
        }
      }
    })
  ])
}
複製代碼

render這塊有點搞暈了,等衝一遍react再回來寫好了。通常來說用HTML模板就足足夠夠了。

相關文章
相關標籤/搜索