混入
1.什么是混入?做什么用的?怎么用?
混入(mixins)是一种分发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()
2.选项合并是什么意思?
混入对象选项对象需要和组件的选项对象混合到一起,当二者同名时,这些选项将以恰当的方式混合
比如:数据对象在内部会进行浅合并,在和组件的数据发生冲突时以组件数据优先
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')
},
aaa: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
aaa: function () {
console.log('from self')
}
}
})
vm.foo() //foo
vm.bar() //bar
vm.aaa() //from self
Vue.extend()也使用相同的策略进行合并
3.自定义选项合并策略如何实现?用在哪方面?
自定义选项将使用默认策略,即简单地覆盖已有值。如果想让自定义选项以自定义逻辑合并,可以向Vue.config.optionMergeStrategies添加一个函数:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {}
对于大多数对象选项,可以使用methods的合并策略
var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods
自定义指令
除了核心功能默认内置的指令(v-model和v-show),vue也允许注册自定义指令。有的情况下,仍然需要对普通dom元素进行底层操作,这个时候就用到自定义指令
Vue.directive('focus', {
//当被绑定的元素插入到dom中时
inserted: function (el) {
el.focus();
}
})
如果你想注册局部指令,组件中也接收一个directives的选项
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
然后你可以在模板中任何元素使用新的v-focus属性
2.钩子函数
一个指令定义对象可以提供如下几个钩子函数(可选):
...bind:只调用一次,指定第一次绑定到元素时调用,在这里可以进行一次性的初始化设置
。。inserted:被绑定元素插入父节点时调用
。。update:所在组件的vnode更新时调用,但是可能发生在其子vnode更新之前
。。componentUpdated:指令所在组件的vnode及其子vnode全部更新后调用
。。unbind:只调用一次,指令与元素解绑时调用
3.钩子函数参数
。。el:指令所绑定的元素,可以用来直接操作DOM
。。binding:一个对象
。。name:指令名,不包括v-前缀
。。value:指令的绑定值
。。oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用
。。expression
渲染函数 &jsx
vue推荐在绝大多数情况下使用template来创建你的html,然而在一些场景中,你真的需要javascript的完全编程能力,这就是render函数,他比template更接近编译器。
hello world
在html层,我们决定这样定义组件:
hello word
当我们开始写一个通过level prop动态生成的heading标签组件
Vue.component('anchored-heading',{
template:"#anchored-heading-template",
props: {
level: {
type:Number,
required: true
}
}
})
在这种情况下使用template并不是最好的选择,首先代码冗长,为了在不同级别的标题中插入锚点元素,我们需要重复的使用slot
虽然模板在大多数组件中都非常好用,但是在这里它就不是很简洁了,我们来尝试使用render函数重写上面的例子:
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, //tag name标签名称
this.$slot.default //子组件中的阵列
)
},
props: {
level: {
type: Number,
required: true
return createElement('h1',this.blogTitle)
1.节点 树 以及虚拟dom
当浏览器读到一些html代码时,它会建立一个dom节点树来保持追踪,如同 一个家谱
vue通过建立一个虚拟dom对真实dom发生的变化保持追踪
return createElement('h1', this.blogTitle)
createElement 到底会返回什么呢?其实不是一个实际的dom元素,它更准确的名字可能是createNodeDescription,因为它所包含的信息会告诉vue页面需要渲染什么样的节点,及其子节点。我们把这样的节点描述为虚拟节点Vnode,虚拟dom是我们vue组件树建立起来的整个vnode树的称呼
createElement参数
createElement(
//一个html标签字符串,组件选项对象,或者解析上述任何一种的async异步函数,必要参数
'div',
//一个包含模板相关属性的数据对象,这样,你可以在template中使用这些属性,可选参数
{},
//子节点,或者使用字符串生成的文本节点
[
'先写一些文字',
createElement(MyComponent, {
props: {
someProp: "foobar"
}
})
]
)
var getChildrenTextContent = function (children) {
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
:node.text
}).join('')
}
Vue.component('anchored-heading', {
render: function (createElement) {
var headingId = getChildrenTextContent(this.$slots.default)
.toLowerCase()
.replace(/\W+/g,'-')
.replace(/(^\-|\-$)/g, '')
return createElement(
'h' + this.level,
[
createElement('a', {
attrs: {
name: headingId,
href: '#' + headingId
}
},this.$slots.default)
]
)
},
props: {
level: {
type:Number,
required: true
}
}
})
3.约束
组件树中的所有vnodes必须是唯一的,这意味着,下面的render function是无效的
render: function (createElement) {
var myParagrapVNode = createElement('p','h1')
return createElement('div', [
myParagraphVNode,myParagraphVNode
])
}
如果你需要重复多次的元素/组件,你可以使用工厂函数来实现
render: function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p','hi')
})
)
}
4.使用javascript代替模板功能
html
- {{item.name}}
No items fount.
js
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.')
}
}
render函数中没有与v-model相应的api,你必须自己来实现相应的逻辑
prop: ['value'],
render: function (createElement) {
var self = this
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}
过滤器
vue允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和v-bind表达式。过滤器应该被添加在javascript表达式的尾部,由管道符合
//双花括号中
{{ message | capitalize}}
//在v-bind中
你可以在组件的选项中定义本地的过滤器
filter: {
capitalize: function (value) {
if(!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者创建vue实例之前全局定义过滤器:
Vue.filter('capitalize', function (value) {
if(!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
})
过滤器函数总接收表达式的值作为第一个参数,并且过滤器可以串联
{{ message | filterA | filterB }}
message作为参数传到filterA中,filterA处理后的数据作为参数传到filterB中
插件
开发插件
插件通常会为vue添加全局功能
1.添加全局方法或者属性
2.添加全局资源:如指令、过滤器、过渡
3.通过全局mixin方法添加一些组件选项
4.添加vue实例方法,通过把他们添加到vue.protetype上实现
5.一个库,提供自己的API,同时提供上面提到的一个或多个功能
vue.js插件应当有一个公开方法install,这个方法的第一个参数是vue构造器,第二个参数是一个可选的选项对象
MyPlugin.install = function (Vue, options) {
//添加全局方法或属性
Vue.myGolbalMethod = function () {
//逻辑
}
//添加全局资源
Vue.directive('my-directive' ,{
bind (el, binding ,vnode, oldVnode) {
//逻辑
}
})
//注入组件
Vue.mixin({
created: function () {}
})
}