过渡与动画
这块放到后面的开发demo里再写吧,说不定能够结合数据可视化组件进行分析写。
混入
混入 (mixin) 提供了一种非常灵活的方式,来分发 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!"
选项合并
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
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" }
}
})
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
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() 也使用同样的策略进行合并。
全局混入
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"
慎用!!!
自定义选项合并策略
自定义选项将使用默认策略,即简单地覆盖已有值。如果想让自定义选项以自定义逻辑合并,可以向 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节点树

高效地更新所有这些节点会是比较困难的,不过所幸你不必手动完成这个工作。你只需要告诉 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

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