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模板就足足够够了。

相关文章
相关标签/搜索