这块放到后面的开发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)
}
}
复制代码
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 中。
用来访问被插槽分发的内容。每一个具名插槽有其相应的 property (例如:v-slot:foo 中的内容将会在 vm.$slots.foo 中被找到)。default property 包括了全部没有被包含在具名插槽中的节点,或 v-slot:default 的内容。
用来访问做用域插槽。对于包括 默认 slot 在内的每个插槽,该对象都包含一个返回相应 VNode 的函数。
做用域插槽函数如今保证返回一个 VNode 数组,除非在返回值无效的状况下返回 undefined。
全部的 $slots
如今都会做为函数暴露在 $scopedSlots
中。若是你在使用渲染函数,不论当前插槽是否带有做用域,咱们都推荐始终经过 $scopedSlots
访问它们。这不只仅使得在将来添加做用域变得简单,也可让你最终轻松迁移到全部插槽都是函数的 Vue 3。
<div>
<h1>My title</h1>
Some text content
<!-- TODO: Add tagline -->
</div>
复制代码
对应的DOM节点树
高效地更新全部这些节点会是比较困难的,不过所幸你没必要手动完成这个工做。你只须要告诉 Vue 你但愿页面上的 HTML 是什么
这能够是在一个模板里
<h1>{{ blogTitle }}</h1>
或者一个渲染函数里
render: function (createElement) {
return createElement('h1', this.blogTitle)
}
复制代码
在这两种状况下,Vue 都会自动保持页面的更新,即使 blogTitle 发生了改变。
Vue 经过创建一个虚拟 DOM 来追踪本身要如何改变真实 DOM
上面的return到底会返回什么呢?
其实不是一个实际的 DOM 元素。它更准确的名字多是 createNodeDescription,由于它所包含的信息会告诉 Vue 页面上须要渲染什么样的节点,包括及其子节点的描述信息。咱们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为“VNode”。“虚拟 DOM”是咱们对由 Vue 组件树创建起来的整个 VNode 树的称呼。
// @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 中能够轻松完成的操做,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 的直接对应——你必须本身实现相应的逻辑。
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
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模板就足足够够了。