v2.6
模板语法
可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<a v-bind:[attributeName]="url"> ... </a>
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
对动态参数的值的约束
动态参数预期会求出一个字符串,异常情况下值为 null。
这个特殊的 null 值可以被显性地用于移除绑定。
任何其它非字符串类型的值都将会触发一个警告。
计算属性缓存vs方法
计算属性基于它们的响应式依赖进行缓存,
这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
计算属性的setter
computed:{
get:function(){
return this.firstName+' '+this.lastName
},
set:function(newValue){
var names=newValue.split(' ')
this.firstName=names[0]
this.lastName=names[names.length-1]
}
}
侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。
这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
class与style绑定
绑定class
对象绑定
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
和如下 data:
data: {
isActive: true,
hasError: false
}
数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
在数组中使用对象
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
绑定内联样式
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
数组语法数组语法
v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
Vue.component('blog-post',{
props:['title'],
template:`<h3>{{title}}</h3>`
})
全局注册
Vue.component('my-component-name', {
// ... 选项 ...
})
局部注册
在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
然后在 components 选项中定义你想要使用的组件:
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
基础组件的自动化全局注册
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/cameCase'
const requireComponent=require.context(
//其组件目录的相对路径
'./components',
//是否查询其子目录
false,
//匹配基础组件的文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName=>{
//获取组件配置
const componentConfig=requireComponent(fileName)
//获取组件的PascalCase命名
const componentName=upperFirst(
cmaeCase(
fileName.split('/').pop().replace(/\.\w+$/,'')
)
)
//全局注册
Vue.component(componentName,
//如果这个组件选项是通过'export default'导出
//那么就会优先使用'.default'
//否则回退到使用模块的根
componentConfig.default||componnentConfig
)
})
传入一个对象的所有属性
如果你想要将一个对象的所有属性都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图改变一个 prop 的情形:
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
propF:{
validator:function(value){
return ['success','danger'].indexOf(value)!==-1
}
}
将原生事件绑定到组件
在有的时候这是很有用的,不过在你尝试监听一个类似 <input> 的非常特定的元素时,这并不是个好主意。
比如上述 <base-input> 组件可能做了如下重构,所以根元素实际上是一个 <label> 元素:
Vue.component('base-input',{
inheritAttrs:false,
props:['label','value'],
computed:{
inputListeners:function(){
var vm=this
return Object.assign({},
this.$listeners,
//然后我们添加自定义监听器
//或覆写一些监听器的行为
{
//这里确保组件配合'v-model'的工作
input:function(event){
vm.$emit('input',event.target.value)
}
}
)
}
},
template:`
<label>
{{label}}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
具名参数
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name 的 <slot> 出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
现在 <template> 元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。
过渡js钩子
<transition v-on:enter="enter"></transition>
状态过滤-把过渡放组件里
Vue.component('animated-integer',{
template:'<span>{{tweeningValue}}</span>',
props:{
value:{
}
},
data:function(){
},
watch:{
},
mouted:function(){
},
methods:{
}
})
<div id="example">
<animated-integer></animated-integer>
</div>
new Vue({
el:'#example',
data:{
firstnumber:20,
lastnumber:40
},
computed:{
result:function(){
}
}
})
混入
var myMixin={
created:function(){
}
}
var Component=Vue.extend({
mixins:[myMixin]
})
var component=new Component()
选项合并
1 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
2 同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用
全局混入
Vue.mixin({
created:function(){
var myOption=this.$options.myOption
if(myOption){
console.log(myOption)
}
}
})
new Vue({
myOption:'HELLO'
})
自定义选项合并策略
//函数
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.optionMergeStragies.vux=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.directive('focus',{
//当被绑定元素插入到dom时
inserted:function(el){
//聚焦元素
el.focus()
}
})
注册局部指令,组件中也接受一个directives的选项
directives:{
focus:{
inserted:function(el){
el.focus()
}
}
}
<input v-focus>
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。
但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
函数简写
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
渲染函数
createElement 参数
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
//{object} 一个与模板中属性对应的数据对象
{
},
//子级虚拟节点(Vnode),由createElement(),构建而成
//也可以使用字符串来生成‘文本虚拟节点’,可选
[
'some word',
createElment(MyComponent,{
props:{
someProp:'foobar'
}
})
]
)
示例
var getChildrenTextContext=function(children){
return children.map(function(node){
return node.children?getChildrenTextContext(node.children):
node.text
}).join('')
}
Vue.component('anchored-heading',{
render:function(createElement){
var headingId=getChildrenTextContext(this.$slots.default)
.toLowerCase()
.replace(/\W+/g,'-')
.replace(/(^-|-$)/,'')
return createElement(
'h'+this.level,
[
createElement('a',{
attrs:{
name:headingId,
href:'#'+headingId
}
},this.$slots.default)
]
)
},
props:{
level:{
type:Number,
required:true
}
}
})
//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)
}
}
})
}
函数式组件
之前创建的锚点标题组件是比较简单,没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。
在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
Vue.component('my-component',{
functional:true,
props:{
},
render:function(createElement,content){
}
})
插件
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
添加全局方法或者属性。如: vue-custom-element
添加全局资源:指令/过滤器/过渡等。如 vue-touch
通过全局混入来添加一些组件选项。如 vue-router
添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
MyPlugin.install=function(Vue,options){
//全局方法或属性
Vue.myGlobalMethod=function(){
}
//添加全局资源
Vue.directive('my-directive',{
bind(el,binding,vnode,oldVnode){
}
})
//注入组件选项
Vue.mixin({
created:function(){
}
})
//添加实例方法
Vue.prototype.$myMethod=function(methodOptions){
}
}