Vuejs基础使用
快速入门
引入vue
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
写代码
<div id="app">
{{ message }}
</div>
创建实例
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
生命钩子
样式绑定
class绑定
-
对象语法
根据条件判断是否添加该class
<div id="app" :class="{fontColor:isClass}">123</div>
.fontColor{ color: red; }
var vm = new Vue({ el: '#app', data: { isClass:true }, })
-
数组语法
传数组以应用一个 class 列表
<div id="app" :class="[fontClass,divClass]">123</div>
var vm = new Vue({ el: '#app', data: { fontClass:'baseFont', divClass:'baseDiv' }, })
.baseFont{ font-size: 40px; font-weight: 700; color: brown; } .baseDiv{ display: block; width: 200px; height: 200px; background-color: blueviolet; }
-
三元表达式
根据条件决定是否添加该class
<div id="app" :class="[isFont?fontClass:'',divClass]">123</div>
var vm = new Vue({ el: '#app', data: { isFont:false, fontClass:"baseFont", divClass:"baseDiv" }, })
也可以用对象语法写,会更清晰。此时baseFont不是变量,而是类名
<div id="app" :class="[{baseFont:isFont},divClass]">123</div>
style绑定
-
对象语法
与css相似
<div id="app" :style="{color:baseColor,fontSize:baseSize}">123</div>
var vm = new Vue({ el: '#app', data: { baseColor:'red', baseSize:'40px' }, })
你也可以在直接绑定到一个样式对象,会更清晰
<div id="app" :style="baseFont">123</div>
var vm = new Vue({ el: '#app', data: { baseFont:{ color:'red', fontSize:'40px' } }, })
-
数组语法
将多个样式对象应用到同一个元素上
<div id="app" :style="[baseFont,basediv]">123</div>
var vm = new Vue({ el: '#app', data: { baseFont:{ color:'red', fontSize:'40px' }, basediv:{ display:'block', width:'100px', height:'100px', backgroundColor:'green' } }, })
条件渲染
v-if
-
指令表达式返回 true时才会渲染内容
<div id="app"> <h1 v-if="cool">cool~~~</h1> </div>
data: { cool: true },
-
可以与
v-else
配合,如果不是true将返回v-else的内容<div id="app"> <h1 v-if="cool">cool~~~</h1> <h1 v-else>what?</h1> </div>
-
多个条件时可以用
v-else-if
<div id="app"> <h1 v-if="cool===1">cool~~~~</h1> <h1 v-else-if="cool===2">what?</h1> <h1 v-else="cool===3">I don't know </h1> </div>
data: { cool: 2 },
key
-
一般情况下,Vue会复用已有元素
<div id="app"> <template v-if="username"> Username:<input placeholder="Enter your username"> </template> <template v-else> Email:<input placeholder="Enter your email address"> </template> <br/><input type="button" @click="username=!username" value="change"> </div>
data: { username: true },
template
可用于包裹元素。在上面例子中,如果你已经输入了内容,由于Vue的复用,切换时内容将不会清空 -
key
管理可复用元素,将元素独立开。应用时只需在需要独立的元素上加上不同的key值Username:<input placeholder="Enter your username" key="1"> Email:<input placeholder="Enter your email address" key="2">
v-show
-
v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS propertydisplay
<h1 v-show="show">Hello!</h1>
-
不支持
<template>
列表渲染
v-for
-
一个数组对应一组元素
<ul id="list"> <li v-for="animal in animals"> {{animal.name}} </li> </ul>
var vm = new Vue({ el: '#list', data: { animals:[ {name:'狮子'}, {name:'黑豹'}, {name:'小猪'}, ] }, })
第二个参数:索引
<li v-for="(animal,i) in animals"> {{i}}-{{animal.name}} </li>
-
使用对象遍历属性值
<li v-for="info in lion"> {{info}} </li>
data: { lion:{ name:'king', age:3, character:'violent' } },
第二个参数:属性名称
<li v-for="(info,name) in lion"> {{name}}:{{info}} </li>
第三个参数:索引
<li v-for="(info,name,i) in lion"> {{i}}:{{name}}:{{info}} </li>
-
维护状态
key
用于跟踪每个节点的身份。<li v-for="animal in animals" :key="animal.id"> {{animal.name}} </li>
data: { animals:[ {name:'狮子',id:1}, {name:'黑豹',id:2}, {name:'小猪',id:3}, ] },
-
可以在
<template>
和组件上使用,在组件是必须加上key
数组更新
push(元素)
:从数组末尾添加元素pop()
:从数组中删除最后一个元素shift()
:从数组中删除第一个元素unshift(元素)
:从数组开头添加元素splice(开始位置,移除个数,要添加(替换)的元素)
:对数组进行删除替换添加元素的操作sort()
:排列reverse()
:颠倒数组元素的位置
-
filter()
:返回符合标准的元素,返回新数组。如:arr.filter(item=>item.length>6)
-
concat()
:合并数组,然后新数组。如:arr1.concat(arr2)
-
slice(开始索引,结束索引)
:抽取数组,返回新数组。如:arr.slice(2, 4)
显示过滤/排序后的结果
-
如果想要在不改变原数组的情况下显示过滤/排序后的结果,可以创建一个计算属性
<li v-for="n in evenNumbers">{{ n }}</li>
data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(number=>{ return number % 2 === 0 }) } }
-
如果是嵌套
v-for
,可以使用方法:<ul v-for="set in sets"> <li v-for="n in even(set)">{{ n }}</li> </ul>
data: { sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]] }, methods: { even: function (numbers) { return numbers.filter(number=>{ return number % 2 === 0 }) } }
事件处理
v-on
v-on
指令监听 DOM 事件,缩写@
-
简单事件直接写
<div id="app"> <button v-on:click="count += 1">Add 1</button> <p>按钮被点了 {{ count }} 下</p> </div>
var vm = new Vue({ el: '#app', data: { count: 0 } })
-
较复杂时在methods定义方法并监听
<div id="app"> <button @click="greet">Greet</button> </div>
var vm = new Vue({ el: '#app', data: { name: '小明' }, methods: { greet: function (event) { alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } })
-
内联js语句中调用
<div id="app"> <button @click="say('hi')">Say hi</button> <button @click="say('what')">Say what</button> </div>
new Vue({ el: '#app', methods: { say: function (message) { alert(message) } } })
如果需要访问原始的 DOM 事件。可以传入
$event
:<button @click="say('hi',$event)">Say hi</button>
事件修饰符
<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form @submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div @click.self="doThat">...</div>
<!-- 阻止对元素自身的点击 -->
<div @click.self.prevent="doThat">...</div>
按键修饰符
<input v-on:keyup.enter="submit">
<input v-on:keyup.page-down="onPageDown">
<!-- keyCode attribute -->
<input v-on:keyup.13="submit">
<!-- 自定义按键修饰符别名 -->
Vue.config.keyCodes.f1 = 112
系统修饰符
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
-
.exact
修饰符
修饰符允许你控制由精确的系统修饰符组合触发的事件<!-- 即使 Alt 或 Shift 被一同按下时也会触发 --> <button v-on:click.ctrl="onClick">A</button> <!-- 有且只有 Ctrl 被按下的时候才触发 --> <button v-on:click.ctrl.exact="onCtrlClick">A</button>
-
鼠标按钮修饰符
.left
、.right
、middle
表单输入绑定
v-model
可以用 v-model
指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
#1. 文本
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
#2. 多行文本
<textarea v-model="message"></textarea>
<p>{{ message }}</p>
#3. 复选框
<!-- 当选中时,checked=true -->
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<!-- true-value与false-value属性,如下选中时为yes -->
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no">
#4. 多个复选框,绑定到同一个数组
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<p>Checked names: {{ checkedNames }}</p>
#5. 单选按钮
<!-- 当选中第一个的时候,picked="one" -->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<p>Picked: {{ picked }}</p>
<!-- v-bind绑定,如下,选中时pick=a -->
<input type="radio" v-model="pick" v-bind:value="a">
#6. 选择框
<!-- 当选中A时,selected="A" -->
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>Selected: {{ selected }}</p>
#7. 选择框多选时绑定到一个数组----selected:[]
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>Selected: {{ selected }}</p>
#8. 选择框v-for动态渲染
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
#8. 选择框v-for动态渲染
data:{
selected:'A',
options:[
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
在文本区域插值不会生效
修饰符
-
.lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步。 lazy 修饰符可以转为在 change 事件_之后_进行同步:<!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg">
-
.number
自动将用户的输入值转为数值类型
<input v-model.number="age" type="number">
-
.trim
自动过滤用户输入的首尾空白字符
<input v-model.trim="msg">
组件
- 组件是可复用的 Vue 实例,且带有一个名字
- 每用一次组件,就会有一个它的新实例被创建
- data 必须是一个函数
data: function () { return { count: 0 } }
1. 注册组件
-
全局注册
可以用在任何新创建的 Vue 根实例的模板中。第一个参数是组件名
Vue.component('my-component-name', { // ... options ... })
-
局部注册
即在哪注册在哪用,这样就不会过多的占用内存,首先注册一个局部组件
var ComponentA={ props:['title'], template:`<h3>{{title}}</h3>` }
然后实例的
components
对象中使用,属性名就是组件名,属性值就是你想传入的组件new Vue({ el: '#app', components:{ 'blog-title':ComponentA } })
注意:推荐使用kebab-case(短横线分割命名)给组件命名,例如该例子中的
<blog-title>
,否则容易报错:
注意:每个组件必须只有一个根元素,即
template
里只能有一个根标签
2. Prop
#prop大小写
prop对大小写不敏感,所以可以这样使用:
Vue.component('blog', {
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
<blog post-title="hello!"></blog>
#prop类型
一般情况下是以字符串数组形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
也可以给每个prop指定值类型。属性名是prop名称,属性值是prop类型
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
//也可以是Data、Symbol
#传递prop
一个例子
Vue.component('blog-post', {
props: ['title'], //给组件定义一个属性
template: '<h3>{{ title }}</h3>'
})
<blog-post title="My journey with Vue"></blog-post> //传值
-
静态传递
# 传入字符串 <blog-post title="My journey with Vue"></blog-post> #有时候需要用v-bind告诉vue这是一个js表达式而不是字符串 # 传入布尔值, <blog-post :is-published="false"></blog-post> # 传入数组 <blog-post :comment-ids="[234, 266, 273]"></blog-post> # 传入对象 <blog-post :author="{name: 'Veronica',company: 'Veridian Dynamics'}"></blog-post> # 传入一个对象所有的属性
-
动态传递
new Vue({ el: '#blog', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
<blog-post v-for="post in posts" :key="post.id" :title="post.title" ></blog-post>
当需要传递的属性很多时,可以将这些属性成合成一个。在这个例子中,可以把所有
post
所有的属性如title、content写在一个post
中:
-
重构组件
原本子组件
Vue.component('blog-post', { props: ['title','content'], template: ` <div> <h3>{{ title }}</h3> <div v-html="content"></div> </div> ` })
现在子组件
Vue.component('blog-post', { props: ['post'], template: ` <div> <h3>{{ post.title }}</h3> <div v-html="post.content"></div> </div> ` })
-
创建实例
new Vue({ el: '#blog', data: { posts: [ { id: 1, title: 'My journey with Vue',content:'111' }, { id: 2, title: 'Blogging with Vue',content:'222' }, { id: 3, title: 'Why Vue is so fun',content:'333'} ] } })
-
在实例中使用组件
原本父级
<blog-post v-for="post in posts" :key="post.id" :title="post.title" :content="post.comtent"></blog-post>
现在父级
<blog-post v-for="post in posts" :key="post.id" :post="post"></blog-post>
#单项数据流
父级 prop 的更新会向下流动到子组件中,但是反过来则不行
#prop验证
可以为组件的 prop 指定验证要求。
- 当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
- prop 会在一个组件实例创建之前进行验证
Vue.component('my-component', {
props: {
//1. 基础的类型检查 (null和undefined会通过任何类型验证)
propA: Number,
//2. 多个可能的类型
propB: [String, Number],
//3. 必填的字符串
propC: {
type: String,
required: true
},
//4. 带有默认值的数字
propD: {
type: Number,
default: 100
},
//5.自定义验证函数。下面例子表示必须匹配下列字符串中的一个
propF: {
validator: function (value) {
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
3. 自定义事件
#事件名
推荐始终使用 kebab-case 的事件名
this.$emit('my-event')
<my-component @my-event="doSomething"></my-component>
#监听子组件事件
-
创建实例
new Vue({ el: '#demo', data: { posts: [/* ... */], postFontSize: 1 } })
-
用
$emit
给子组件添加事件Vue.component('blog-post', { props: ['post'], template: ` //... <button @click="$emit('enlarge-text')">Enlarge</button> //... ` })
-
v-on
监听子组件事件。当子组件触发时,开始响应<div id="demo"> <div :style="{ fontSize: postFontSize + 'em' }"> <blog-post ... @enlarge-text="postFontSize += 0.1" ></blog-post> </div> </div>
$emit
可以接收第二个参数
-
给子组件事件添加参数
<button v-on:click="$emit('enlarge-text', 0.1)">Enlarge</button>
-
父级通过
$event
获取整个参数值<blog-post ... @enlarge-text="postFontSize += $event" ></blog-post>
第二种写法:
<blog-post ... @enlarge-text="enlargeText" ></blog-post>
传过来的参数值会作为该方法的第一个参数
methods: { enlargeText: function (num) { this.postFontSize += num } }
#自定义事件中的v-model
-
普通情况下自定义事件
<input v-model="searchText">
等价于
<input :value="searchText" @input="searchText = $event.target.value">
-
在组件上自定义事件
<custom-input v-model="searchText"/>
等价于
<custom-input :value="searchText" @input="searchText = $event"/>
这时候需要更改子组件,才能让其正常运作。
Vue.component('custom-input', { props: ['value'], template: ` <input :value="value" @input="$emit('input', $event.target.value)" > ` })
解析:这时子组件的
input
触发时,会抛出$event.target.value
,父级会监听到并接收$event.target.value
。此时父级的$event
实际上就等于子组件抛出的$event.target.value
,变化后的searchText
的值会绑定在父级的value上,然后通过props
传回子组件,子组件接收到这个props值后就绑定在input
的value上,这时候就在页面呈现出效果了
#绑定原生事件
使用 v-on
的 .native
修饰符:
<base-input @focus.native="onFocus"></base-input>
注意:
.native
监听的是组件的根元素。如果想监听到子元素,需要用到$listeners
对象
4. 插槽
#一个例子
-
在子组件模板中加上一个插槽
<slot></slot>
Vue.component('alert-box', { template: ` <div> <strong>Error!</strong> <slot></slot> </div> ` })
-
在父级组件内添加内容后,内容就会自动放进插槽内
<alert-box>Something bad happened.</alert-box>
注意:如果子的
template
中没有包含一个slot
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃
#默认内容
默认内容会在没有提供内容的时候被渲染。只需要将内容放在slot
中:
//在一个子组件的template中
<button>
<slot>Submit</slot>
</button>
//1. 父级中没有添加任何内容
<submit-button></submit-button>
// 渲染后备内容:
<button>Submit</button>
//2. 父级中有内容
<submit-button>Save</submit-button>
// 后备内容会被取代:
<button>Save</button>
#具名插槽
当我们需要多个插槽时,具名插槽很有用:
-
组件模板中,给
slot
添加name
属性//假设这是base-layout组件的模板,需要分别给header、main、footer添加插槽: <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> //这个插槽没有名字,name的值默认为default </main> <footer> <slot name="footer"></slot> </footer> </div>
-
父级中,用带有
v-slot
的template
包裹内容,v-slot
指定插槽名称<base-layout> <template v-slot:header> <h1>I am a header</h1> </template> //任何没有按规定包裹的内容都会被视为默认插槽的内容 <p>A paragraph for the main content.</p> <p>And another one.</p> <template v-slot:footer> <p>I am a footer</p> </template> </base-layout>
v-slot
可以缩写:<template #header> <h1>I am a header</h1> </template>
#编译作用域
插槽的作用域与实例相同。即能访问到实例的数据,而不能访问组件的数据
//可以访问
<navigation-link url="/profile">{{ user.name }}</navigation-link>
//不可以访问
<navigation-link url="/profile">{{ url }}</navigation-link>
#插槽作用域
如果想在父级插槽内容中访问到子组件的数据
-
在子组件的
slot
中绑定数据Vue.component('user-info', { //user作为属性绑定数据。在这里user属性被称为插槽prop template: `<span><slot :user="userdata">{{userdata.current}}</slot></span>`, data:()=>({ userdata:{ before:'lihua', current:'xiaoming' } }), })
-
用
v-slot
来定义插槽 prop 的名字。名字也可以是其他<user-info v-slot:default="slotProps"> {{slotProps.user.before}} </user-info>
由于指定的插槽是default,所以以上也可以简写成:注意不要与具名插槽混淆
<user-info v-slot="slotProps"> {{slotProps.user.before}} </user-info>
自 2.6.0 起有所更新。已废弃的使用
slot-scope
。
# 动态插槽名
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
5. 动态组件
如果在一个多标签界面实现组件切换,可以通过 Vue 的 <component>
元素加一个特殊的 is
attribute 来实现::
<!-- 切换tab值 -->
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">{{tab}}</button>
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
Vue.component("com1", {
template: "<div>com1</div>"
});
Vue.component("com2", {
template: "<div>com2</div>"
});
Vue.component("com3", {
template: "<div>com3</div>"
});
new Vue({
el: '#app',
data:{
currentTab:"com1",
tabs:["com1", "com2", "com3"]
},
computed: {
//currentTabComponent可以是已注册组件的名字,或一个组件的选项对象
currentTabComponent: function () {
return this.currentTab
}
}
})
过渡和动画
过渡的类名
-
[name]-enter
:进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。 -
[name]-enter-active
:进入过渡生效时的状态。在元素被插入之前生效,在过渡/动画完成之后移除。用来定义进入过渡的过程时间,延迟和曲线函数。 -
[name]-enter-to
:进入过渡的结束状态。在元素被插入之后下一帧生效 ,在过渡/动画完成之后移除。 -
[name]-leave
:离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。 -
[name]-leave-active
:离开过渡生效时的状态在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 -
[name]-leave-to
:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 ,在过渡/动画完成之后移除。
单元素/组件的过渡
Vue 提供了
transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡:
- 条件渲染 (使用
v-if
) - 条件展示 (使用
v-show
) - 动态组件
- 组件根节点
css过渡例子
-
用
transition
包裹元素,并命名<button @click="show = !show">切换</button> <transition name="fade"> <p v-if="show">hello</p> </transition>
-
定义类
//进入过渡 .fade-enter-active { transition: all .3s ease; } //离开过渡 .fade-leave-active { transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0); } //开始和结束的状态 .fade-enter, .fade-leave-to { transform: translateX(10px); opacity: 0; }
css动画例子
-
用
transition
包裹元素,并命名<button @click="show = !show">切换</button> <transition name="bounce"> <p v-if="show">hello</p> </transition>
-
定义类
//进入动画 .bounce-enter-active { animation: bounce-in .5s; } //离开动画 .bounce-leave-active { animation: bounce-in .5s reverse; } //定义动画关键帧 @keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(1.5); } 100% { transform: scale(1); } }
多个元素/组件过渡
多个元素
<transition>
<table v-if="items.length > 0"></table>
<p v-else>Sorry, no items found.</p>
</transition>
当有相同标签名的元素切换时,需要通过 key
设置唯一的值来标记以让 Vue 区分它们
<transition>
<button v-if="isEditing" key="save">Save</button>
<button v-else key="edit">Edit</button>
</transition>
多个组件
不需要使用 key
。相反,需要使用动态组件
<input type="radio" value="com1" id="1" v-model="view">com1
<input type="radio" value="com2" id="2" v-model="view">com2
<transition name="fade" mode="out-in">
<component v-bind:is="view"></component>
</transition>
new Vue({
el: '#app',
data: {
view: 'com1'
},
components: {
'com1': {template: '<div>Component A</div>'},
'com2': {template: '<div>Component B</div>'}
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity .3s ease;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
过渡模式
in-out
:新元素先进行过渡,完成之后当前元素过渡离开out-in
:当前元素先进行过渡,完成之后新元素过渡进入
JavaScript 钩子
-
钩子介绍
<transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:after-enter="afterEnter" v-on:enter-cancelled="enterCancelled" v-on:before-leave="beforeLeave" v-on:leave="leave" v-on:after-leave="afterLeave" v-on:leave-cancelled="leaveCancelled" > <!-- ... --> </transition>
methods: { beforeEnter: function (el) {...}, enter: function (el, done) { //当与 CSS 结合使用时,回调函数 done 是可选的 // ... done() }, afterEnter: function (el) {...}, enterCancelled: function (el) {...}, // 离开时 beforeLeave: function (el) {...}, leave: function (el, done) {// 当与 CSS 结合使用时,回调函数 done 是可选的 // ... done() }, afterLeave: function (el) {...},// leaveCancelled 只用于 v-show 中 leaveCancelled: function (el) {...} }
-
使用Velocity.js的例子
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script> <div id="app"> <button @click="show = !show"> Toggle </button> <transition v-on:before-enter="beforeEnter" v-on:enter="enter" v-on:leave="leave" v-bind:css="false" > <p v-if="show"> Demo </p> </transition> </div>
new Vue({ el: '#pp data: { show: false }, methods: { beforeEnter: function (el) { el.style.opacity = 0 el.style.transformOrigin = 'left' }, enter: function (el, done) { Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 }) Velocity(el, { fontSize: '1em' }, { complete: done }) }, leave: function (el, done) { Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 }) Velocity(el, { rotateZ: '100deg' }, { loop: 2 }) Velocity(el, { rotateZ: '45deg', translateY: '30px', translateX: '30px', opacity: 0 }, { complete: done }) } } })
初始渲染的过渡
可以通过 appear
设置节点在初始渲染的过渡,只渲染一次
<transition
appear
appear-class="enter-class"
appear-active-class="enter-active-class"
appear-to-class="enter-to-class"
>
<!-- ... -->
</transition>
自定义 JavaScript 钩子
<transition
appear
v-on:before-appear="customBeforeAppearHook"
v-on:appear="customAppearHook"
v-on:after-appear="customAfterAppearHook"
v-on:appear-cancelled="customAppearCancelledHook"
>
<!-- ... -->
</transition>
列表过渡
同v-for渲染列表时,使用<transition-group>
组件。需要提供key
,此时过渡模式不可用。
不同于 transition
,它会以一个真实元素呈现:默认为一个 span
。你也可以通过 tag
attribute 更换为其他元素。
<div id="app">
<!--... -->
<transition-group name="list" tag="p">
<span v-for="item in items" :key="item">
{{ item }}
</span>
</transition-group>
</div>
其他
混入
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。当组件使用混入对象时,所有混入对象的选项将被混入进该组件本身的选项。
- 假设要混入一个方法
const userMixin={ methods:{ getInfo(){ //Fetch API 提供了一个获取资源的接口(包括跨域请求) return fetch(`/api/user/${userId}`) .then((res)=>res.json()) } } }
- 使用
import userMixin from './mixins/user' Vue.component('user-com',{ mixins:[userMixin],//混入 //... mounted(){ //这个时候已经可以使用混合进来的方法了 this.getInfo(this.userId) .then((res)=>{...}) } })
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”
- 数据对象同名,合并数据
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
})
//这时候的data:
//{ message: "goodbye", foo: "abc", bar: "def" }
- 钩子函数同名,合并方法
var mixin = {
created: function () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('组件钩子被调用')
}
})
//这时候调用时:
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。
** 全局混入**
Vue.mixin({...})
自定义指令
注册
- 全局注册
Vue.directive('focus', { inserted: function (el) { el.focus() } })
- 局部注册
directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } } }
使用:<input v-focus>
钩子函数
bind
:只调用一次,指令第一次绑定到元素时调用。inserted
:被绑定元素插入父节点时调用update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind
:只调用一次,指令与元素解绑时调用。
钩子函数参数
el
:指令所绑定的元素,可以用来直接操作 DOM。binding
:一个对象,包含以下 property:name
:指令名,不包括 v- 前缀。value
:指令的绑定值,例如:v-my="1 + 1" 中,绑定值为 2oldValue
:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用expression
:字符串形式的指令表达式。例如 v-my="1 + 1" 中,表达式为 "1 + 1"。arg
:传给指令的参数。例如 v-my:foo 中,参数为 "foo"modifiers
:一个包含修饰符的对象。例如:v-my.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
vnode
:Vue 编译生成的虚拟节点。oldVnode
:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
插件
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
- 添加全局方法或者 property。如:vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
//全局使用
Vue.use(MyPlugin)
//传入选项
Vue.use(MyPlugin, { someOption: true })
// 用 Browserify 或 webpack 提供的 CommonJS 模块环境时
var Vue = require('vue')
var VueRouter = require('vue-router')
// 不要忘了调用此方法
Vue.use(VueRouter)
过滤器
注册
- 全局注册
//将字母转为大写 Vue.filter('capitalize', function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) })
- 局部注册
//在组件选项中: filters: { capitalize: function (value) { //... } }
使用
过滤器可以用在两个地方:
- 双花括号插值
{{ message | capitalize }}
v-bind
表达式<div :id="rawId | formatId"></div>
链式调用
在下面的例子中,首先会调用round过滤器,它返回的结果会传递给formatCost过滤器进行处理:
{{ productOneCost | round |formatCost}}
参数
{{ message | filterA('arg1', arg2) }}
其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。
单文件组件
全局组件的缺点:
- 全局定义强制要求每个 component 中的命名不得重复
- 字符串模板缺乏语法高亮,看不清晰
- 不支持 CSS ,意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
- 没有构建步骤,限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug 和 Babel
文件扩展名为
.vue
的 单文件组件为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。以下是一个文件名为Hello.vue
的简单实例
<template>
<p>Hello!{{msg}}</p>
</template>
<script>
export default{
name:'test',
props:{
msg:{
type:String,
default:"test msg"
}
},
methods:{}
data () {
return {
name: 'lihua'
}
}
}
</script>
<style scoped>
p{color: antiquewhite;}
</style>
在其他地方中使用
<template>
<div id="app">
<test></test>
</div>
</template>
<script>
//1. 引入组件
import test form './components/test.vue'
export default{
name:'app',
//2. 注册组件
components:{
test
}
}
</script>
render函数
使用模板并不是唯一能让vue知道应该在页面显示什么内容的方式:还可以使用render函数。当你将一个函数传递给Vue实例的render属性时,该函数会传入一个createElement函数,可以使用它来指定需要在页面上显示的HTML。
new Vue({
el:'#app',
render(createELement){
return createELement('h1',"hello world!")
}
})
createElement
接收3个参数:标签名称、包含配置信息的数据对象(诸如HTML特性、属性、事件侦听器以及要绑定的class和style等)和一个子节点或是包含子节点的数组
标签名称
标签名称是最简单的,也是唯一一个必需的参数。它可以是一个字符串,或是一个返回字符串的函数。render函数中也可以访问this,所以可以将标签名称设置为data对象的某个属性、prop、计算属性或是任何类似的东西,例如下方代码,这是render函数优于template的一个很大的优势:
new Vue({
el:'#app',
render(createELement){
return createELement('this.tagName',"hello world!")
},
data:{
tagName:"h1"
}
})
数据对象
数据对象是设置一系列配置属性的地方,这些属性会影响生成的组件或元素。
例如,对于<custom-button type="submit" v-bind:text="buttonText">
。type是传递给组件的一个HTML属性,text是一个组件prop,与变量buttonText绑定。现在使用createElement来实现相同的示例:
new Vue({
el:'#app',
render(createELement){
return createELement('custom-button',{
attrs:{
type:"submit"
},
props:{
text:this.buttonText
}
})
},
})
下面的示例,它列出了所有的选项:
{
//html特性
attrs:{
type:"submit"
},
//传递给组件的prop
props:{
test:"单击我!"
},
//DOM属性,比如innerHtml
domProps:{
innerHtml:'...'
},
//事件监听器
on:{
click:this.handleClick
},
//当组件是某个组件的子组件时使用,等同于:slot="Slot"
slot:'Slot',
//用于某个循环产生的数组,等同于key="Key"
key:"Key",
//与ref="Ref"相同
ref="Ref",
class:['Aclass',{'Bclass':true}],
style:{backgroundColor:'red'}
}
子节点
用来设置元素的子节点的。它可以是一个数组也可以是一个字符串。如果你不需要设置数据对象,只是需要配置子节点,那么可以将子节点设置为第二个参数,而不是原来的第三个参数。
<div>
<button v-on:click="count++">单击增加计数</button>
<p>你已经单击了{{count}}次</p>
</div>
render(createELement){
return createELement('div',[
createELement('button',{
on:{
click:()=>this.count++
}
},
'单击增加计数'),
createELement('p','你已经单击了${{this.count}}次')
])
},
JSX
在babel-plugin-transform-vue-jsx
插件的帮助下,可以使用JSX来编写render函数。createElement
函数通常会有个较短的别名,h
。
安装了插件之后你可以写成:
render(){
return(
<div>
<button v-on:click="count++">单击增加计数</button>
<p>你已经单击了{{count}}次</p>
</div>
)
}
除了像在模板中那样,可以导入并使用组件——只要将组件的名称设置为标签的名称——还可以像React那样导入组件。如果导入的组件存储在首字母大写的变量中,则不需要在components对象中指定或是使用Vue.component()函数注册,就可以使用该组件。例如:
import MyComponent from './components/MyComponent.vue'
new Vue({
el:'#app',
render(){
return(
<MyComponent/>
)
}
})
JSX的展开操作符也同样得到支持。可以在属性对象上使用展开操作符,它会与其他已经设置的属性相合并,一起应用到元素上:
render(){
const props={
class:'world',
href:'...'
};
return(
<a {...props}>hello</a>
);
}