- Vue基础
- Vue实例
Vue 实例是 Vue.js 中最基本的构建块之一。每个 Vue 应用程序都是由一个 Vue 根实例创建的,该实例通过将选项对象传递给 Vue 构造函数而创建。
Vue 实例的属性和方法包括:- data:用于存储数据的对象,可以在模板中进行访问。
- methods:用于定义在 Vue 实例中使用的方法。
- computed:用于定义计算属性,其值会根据依赖项的变化而变化。
- watch:用于监听特定数据的变化,一旦变化就会触发相应的回调函数。
- directives:用于定义自定义指令。
- filters:用于定义过滤器,可以在模板中使用。
除了这些属性和方法之外,Vue 实例还有一些生命周期钩子
函数,用于在实例的生命周期内执行特定的操作,例如在实例创建时执行某些操作,或在实例销毁时执行清理操作。
- 模板语法
Vue.js 的模板语法是基于 HTML 的扩展,通过 Vue 的指令和插值语法,使得模板能够更加动态和交互。下面是一些常用的 Vue 模板语法示例:- 插值语法
插值语法使用双大括号 {{}} 来绑定数据,它会将 Vue 实例中的数据渲染到模板中。<div>{{ message }}</div>
- 指令
指令是一种特殊的属性,它们带有 v- 前缀,指示 Vue.js 在模板中添加特殊行为。例如,v-if 指令根据表达式的值来条件性地渲染元素。
<div v-if="showMessage">{{ message }}</div>
- 绑定属性
Vue.js 通过 v-bind 指令来绑定元素的属性,这使得我们可以使用表达式来动态绑定属性值。
<img v-bind:src="imageUrl">
- 循环
使用 v-for 指令可以循环渲染列表中的元素。
<ul> <li v-for="item in items">{{ item }}</li> </ul>
- 事件绑定
使用 v-on 指令可以绑定事件处理函数,当事件触发时会执行相应的函数。
<button v-on:click="handleClick">Click me</button>
以上是一些常用的 Vue 模板语法示例,它们可以通过 Vue.js 的数据响应系统来实现数据与模板的双向绑定
- 插值语法
1.3 计算属性
Vue.js 的计算属性是基于 Vue 实例的数据计算得出的属性,它们具有缓存特性,只有当它们的依赖项发生变化时才会重新计算。计算属性常见的使用场景是对数据进行复杂的计算,并将计算结果作为属性进行渲染。下面是一些常见的计算属性的使用示例:
1.3.1 简单计算属性
<template>
<div>
<p>原价: {{ price }}</p>
<p>折扣价: {{ discountedPrice }}</p>
</div>
</template>
<script>
export default {
data() {
return {
price: 100,
discount: 0.2
}
},
computed: {
discountedPrice() {
return this.price * (1 - this.discount)
}
}
}
</script>
1.3.2 计算属性监听其他属性
<template>
<div>
<input v-model="firstName">
<input v-model="lastName">
<p>{{ fullName }}</p>
</div>
</template>
<script>
export default {
data() {
return {
firstName: '',
lastName: ''
}
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
}
</script>
1.3.3 计算属性使用 get 和 set
<template>
<div>
<p>商品数量: {{ quantity }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
computed: {
quantity: {
get() {
return this.count
},
set(value) {
this.count = value
}
}
},
methods: {
increment() {
this.quantity += 1
},
decrement() {
this.quantity -= 1
}
}
}
</script>
1.4 监听器
Vue.js 的监听器是一种用来监听 Vue 实例上指定属性变化的方法,当指定属性发生变化时,Vue.js 会自动调用监听器中指定的回调函数。监听器常用于在数据变化时执行自定义的操作,比如发送请求、更新其他数据等。下面是一些常见的使用场景和示例:
1.4.1 监听简单数据变化
<template>
<div>
<input v-model="message">
<p>{{ reversedMessage }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, Vue!',
reversedMessage: ''
}
},
watch: {
message(newValue, oldValue) {
this.reversedMessage = newValue.split('').reverse().join('')
}
}
}
</script>
在以上示例中,监听器会监听 message 属性的变化,当 message 发生变化时,监听器中的回调函数会将 reversedMessage 属性更新为 message 的反转字符串。
1.4.2 监听对象属性变化
<template>
<div>
<p>用户名: {{ user.name }}</p>
<p>年龄: {{ user.age }}</p>
<button @click="incrementAge">加1岁</button>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: '张三',
age: 18
}
}
},
watch: {
'user.age'(newValue, oldValue) {
console.log(`年龄从 ${oldValue} 变为 ${newValue}`)
}
},
methods: {
incrementAge() {
this.user.age += 1
}
}
}
</script>
在以上示例中,监听器会监听 user 对象的 age 属性变化,当 age 属性发生变化时,监听器中的回调函数会打印出变化的信息。
1.4.3 深度监听数组和对象
<template>
<div>
<p v-for="item in items">{{ item.name }} - {{ item.price }}</p>
<button @click="addItem">添加商品</button>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ name: '商品A', price: 100 },
{ name: '商品B', price: 200 }
]
}
},
watch: {
items: {
handler(newValue, oldValue) {
console.log('items 数组发生变化')
},
deep: true
}
},
methods: {
addItem() {
this.items.push({ name: '新商品', price: 300 })
}
}
}
</script>
在以上示例中,监听器会深度监听 items 数组的变化,当数组中的对象属性发生变化时,监听器中的回调函数会打印出变化的信息。
1.5 组件
Vue.js 的组件是可复用的 Vue 实例,每个组件都可以拥有自己的数据、方法和生命周期钩子等。在 Vue.js 中,我们可以通过 Vue.component() 方法来定义一个全局组件,也可以通过组件选项来定义一个局部组件。下面是一些常见的组件定义示例:
1.5.1 全局组件
<template>
<div>
<hello-world></hello-world>
</div>
</template>
<script>
Vue.component('hello-world', {
template: '<p>Hello, World!</p>'
})
</script>
在以上示例中,我们通过 Vue.component() 方法定义了一个名为 hello-world 的全局组件,然后在模板中使用该组件。
1.5.2 局部组件
<template>
<div>
<custom-button></custom-button>
</div>
</template>
<script>
import CustomButton from './CustomButton.vue'
export default {
components: {
CustomButton
}
}
</script>
在以上示例中,我们通过组件选项 components 来定义了一个名为 CustomButton 的局部组件,并在模板中使用该组件。注意,这里的 CustomButton 组件是从一个单独的 .vue 文件中导入的。
1.5.3 动态组件
<template>
<div>
<component :is="currentComponent"></component>
<button @click="changeComponent">切换组件</button>
</div>
</template>
<script>
import FirstComponent from './FirstComponent.vue'
import SecondComponent from './SecondComponent.vue'
export default {
data() {
return {
currentComponent: 'FirstComponent'
}
},
methods: {
changeComponent() {
this.currentComponent = this.currentComponent === 'FirstComponent' ? 'SecondComponent' : 'FirstComponent'
}
},
components: {
FirstComponent,
SecondComponent
}
}
</script>
在以上示例中,我们通过 :is 属性来动态渲染组件,同时提供一个按钮来切换组件。注意,这里的 currentComponent 是一个 data 属性,我们可以在方法中改变该属性来动态渲染不同的组件。
1.6 生命周期
Vue.js 的生命周期可以分为八个阶段,分别是 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy 和 destroyed。在每个阶段,Vue.js 会自动调用相应的生命周期钩子函数,我们可以在这些钩子函数中执行一些初始化操作或清理操作。下面是一些常见的生命周期示例:
1.6.1 beforeCreate 和 created 钩子
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, World!'
}
},
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
}
}
</script>
1.6.2 beforeMount 和 mounted 钩子
<template>
<div>
<p ref="message">{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, World!'
}
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
console.log(this.$refs.message.innerText)
}
}
</script>
在以上示例中,我们定义了一个名为 message 的 data 属性,并在 beforeMount 和 mounted 钩子函数中分别打印了日志。beforeMount 钩子函数在模板编译之后,挂载之前被调用,mounted 钩子函数在实例挂载完成之后被调用,这两个钩子函数常用于操作 DOM 元素或执行一些异步操作。
1.6.3 beforeUpdate 和 updated 钩子
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">改变消息</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, World!'
}
},
methods: {
changeMessage() {
this.message = 'Hello, Vue!'
}
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
}
}
</script>
在以上示例中,我们定义了一个名为 message 的 data 属性,并在 beforeUpdate 和 updated 钩子函数中分别打印了日志。beforeUpdate 钩子函数在数据更新之前被调用,updated 钩子函数在数据更新之后被调用,这两个钩子函数常用于在数据更新之前或之后执行一些操作。
1.6.4 beforeDestroy 和 destroyed 钩子
<template>
<div>
<p>{{ message }}</p>
<button @click="destroy">销毁实例</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello, World!'
}
},
methods: {
destroy() {
this.$destroy()
}
},
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
}
}
</script>
1.7 自定义指令
在 Vue.js 中,我们可以通过自定义指令来扩展其功能,从而实现一些非常灵活的操作。下面是一个简单的自定义指令示例:
<template>
<div>
<p v-highlight="'yellow'">这是一段需要高亮的文字</p>
</div>
</template>
<script>
export default {
directives: {
highlight: {
bind(el, binding) {
el.style.backgroundColor = binding.value
}
}
}
}
</script>
在以上示例中,我们定义了一个自定义指令 highlight,并在模板中使用了它。指令的作用是将绑定的元素的背景颜色设置为指定的颜色。在 directives 属性中定义了 highlight 指令的实现,它有一个 bind 钩子函数,在指令绑定到元素上时被调用。在 bind 钩子函数中,我们通过 el 参数访问到绑定的元素,通过 binding 参数访问到指令的绑定值,然后将背景颜色设置为绑定值即可。
1.8 渲染函数
Vue 渲染函数是一种用 JavaScript 编写模板的方法,通过编写 JavaScript 代码来生成虚拟 DOM,然后将虚拟 DOM 渲染到页面上。它可以更加灵活地控制页面的渲染过程,适用于一些需要动态生成页面的场景。
下面是 Vue 渲染函数的常见用法示例代码:
Vue.component('my-component', {
render: function (createElement) {
return createElement(
'div',
{
attrs: {
id: 'my-component'
}
},
[
createElement('h1', 'Hello World!'),
createElement('p', 'This is my component.')
]
)
}
})
在上面的代码中,我们定义了一个名为 my-component 的组件,通过 render 方法返回一个虚拟 DOM,其中使用了 createElement 方法来创建元素节点。在 createElement 方法中,第一个参数为标签名或组件名,第二个参数为元素的属性对象,第三个参数为元素的子节点数组。
在实际开发中,我们可以通过编写 JavaScript 代码来动态生成虚拟 DOM,从而更加灵活地控制页面的渲染过程。
- 插件
Vue 插件是一种可以扩展 Vue 功能的方式,它可以为 Vue 添加全局的功能或者组件。通常情况下,Vue 插件会暴露一个 install 方法,该方法会在 Vue 实例化之前被调用,从而可以在 Vue 实例化之前对其进行一些初始化或者注册全局组件等操作。
下面是一个示例代码,演示了如何编写一个 Vue 插件:
// 定义插件
const myPlugin = {
install: function (Vue, options) {
// 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 全局方法的逻辑代码
}
// 添加全局组件
Vue.component('my-component', {
// 组件的选项
})
// 添加实例方法
Vue.prototype.$myMethod = function (options) {
// 实例方法的逻辑代码
}
}
}
// 使用插件
Vue.use(myPlugin)
在上面的代码中,我们定义了一个名为 myPlugin 的插件,该插件暴露了一个 install 方法。在 install 方法中,我们可以添加全局方法、属性、组件以及实例方法等。在使用插件时,我们可以通过 Vue.use 方法来安装插件,从而将插件添加到 Vue 中。
例如,在上面的代码中,我们添加了一个名为 myGlobalMethod 的全局方法,一个名为 my-component 的全局组件,以及一个名为 $myMethod 的实例方法。在使用插件后,我们就可以在任何 Vue 实例中使用这些方法或组件了。
需要注意的是,Vue 插件通常会提供一些选项参数,以便用户在安装插件时进行配置。在上面的代码中,我们将选项参数传递给了 install 方法的第二个参数 options 中。
- Vue组件
2.1 组件通信
Vue 组件通信是指在不同的 Vue 组件之间进行数据传递和交互的过程。Vue 提供了多种组件通信的方式,包括 props、事件、插槽等。下面是一些常见的 Vue 组件通信示例代码:
2.1.1 父子组件通信 - 使用 props
// 父组件
<template>
<div>
<child-component :message="parentMessage"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data () {
return {
parentMessage: 'Hello from parent'
}
}
}
</script>
// 子组件
<template>
<div>
{{ message }}
</div>
</template>
<script>
export default {
props: {
message: String
}
}
</script>
在上面的代码中,我们使用了 props 属性将父组件中的数据传递给了子组件。在父组件中,我们通过 :message 的方式将 parentMessage 数据传递给了子组件。在子组件中,我们使用 props 属性来声明 message 属性,从而接收父组件传递过来的数据。
2.1.2 子父组件通信 - 使用事件
// 父组件
<template>
<div>
<child-component @emit-message="handleMessage"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
methods: {
handleMessage (message) {
console.log(message)
}
}
}
</script>
// 子组件
<template>
<div>
<button @click="emitMessage">Click me</button>
</div>
</template>
<script>
export default {
methods: {
emitMessage () {
this.$emit('emit-message', 'Hello from child')
}
}
}
</script>
在上面的代码中,我们使用了 $emit 方法将子组件中的事件传递给了父组件。在子组件中,我们通过 emitMessage 方法触发了 emit-message 事件,并将数据 'Hello from child' 作为参数传递给了该事件。在父组件中,我们使用 @emit-message 的方式监听了该事件,并在 handleMessage 方法中处理了传递过来的数据。
2.1.3 兄弟组件通信 - 使用事件总线
// 创建事件总线
export const eventBus = new Vue()
// 组件A
<template>
<div>
<button @click="sendMessage">Send message</button>
</div>
</template>
<script>
import { eventBus } from './event-bus.js'
export default {
methods: {
sendMessage () {
eventBus.$emit('message', 'Hello from A')
}
}
}
</script>
// 组件B
<template>
<div>
{{ message }}
</div>
</template>
<script>
import { eventBus } from './event-bus.js'
export default {
data () {
return {
message: ''
}
},
created () {
eventBus.$on('message', message => {
this.message = message
})
}
}
</script>
在上面的代码中,我们创建了一个名为 eventBus 的事件总线,并将其导出。在组件A中,我们使用 $emit
方法触发了名为 message 的事件,并将数据 'Hello from A' 作为参数传递给了该事件。在组件B中,我们使用 $on 方法监听了名为 message 的事件,并在回调函数中处理了传递过来的数据。由于事件总线是全局的,因此可以在任何组件中使用该方式进行通信。
2.2 组件生命周期
Vue 组件生命周期是指 Vue 组件从创建到销毁的整个过程中,会经历一系列的生命周期钩子函数,这些钩子函数会在不同的阶段执行,用于完成不同的任务。下面是 Vue 组件生命周期的常见钩子函数以及使用示例代码:
2.2.1 beforeCreate
在组件实例被创建之初,beforeCreate 钩子函数会被调用,此时组件的数据和方法都还未初始化。
export default {
beforeCreate () {
console.log('beforeCreate')
}
}
2.2.2 created
在组件实例创建完成之后,created 钩子函数会被调用,此时组件的数据和方法已经初始化完成,但 DOM 元素还未生成。
export default {
created () {
console.log('created')
}
}
2.2.3 beforeMount
在组件挂载到 DOM 元素之前,beforeMount 钩子函数会被调用。
export default {
beforeMount () {
console.log('beforeMount')
}
}
2.2.4 mounted
在组件挂载到 DOM 元素之后,mounted 钩子函数会被调用,此时组件已经渲染完成。
export default {
mounted () {
console.log('mounted')
}
}
2.2.5 beforeUpdate
在组件更新之前,beforeUpdate 钩子函数会被调用。
export default {
beforeUpdate () {
console.log('beforeUpdate')
}
}
2.2.6 updated
在组件更新之后,updated 钩子函数会被调用。
export default {
updated () {
console.log('updated')
}
}
2.2.7 beforeDestroy
在组件销毁之前,beforeDestroy 钩子函数会被调用。
export default {
beforeDestroy () {
console.log('beforeDestroy')
}
}
2.2.8 destroyed
在组件销毁之后,destroyed 钩子函数会被调用。
export default {
destroyed () {
console.log('destroyed')
}
}
以上是 Vue 组件生命周期的常见钩子函数以及使用示例代码。在实际开发中,我们可以利用这些钩子函数来完成一些特定的任务,比如在 created 钩子函数中发起网络请求,或者在 mounted 钩子函数中初始化一些插件等。
2.3 父子组件通信
在 Vue 中,父子组件之间的通信是非常常见的需求。Vue 提供了多种方式来实现父子组件之间的通信,下面是其中的两种方式:
2.3.1 Props
通过 props 将父组件的数据传递给子组件,子组件通过 props 接收父组件传递过来的数据。这是 Vue 中最基本、最常用的一种方式。
在父组件中,我们可以通过 v-bind 或简写的 : 来传递数据:
<template>
<div>
<ChildComponent :message="message"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data () {
return {
message: 'Hello World'
}
}
}
</script>
在子组件中,我们可以通过 props 接收父组件传递过来的数据:
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: {
message: String
}
}
</script>
2.3.2 emit 触发一个自定义事件,并携带参数,父组件通过 v-on 或简写的 @ 监听这个自定义事件,并处理子组件传递过来的数据。
在子组件中,我们可以通过 $emit 触发一个自定义事件:
<template>
<button @click="handleClick">Click me</button>
</template>
<script>
export default {
methods: {
handleClick () {
this.$emit('custom-event', 'Hello World')
}
}
}
</script>
在父组件中,我们可以通过 v-on 或简写的 @ 监听这个自定义事件,并处理子组件传递过来的数据:
<template>
<div>
<ChildComponent @custom-event="handleCustomEvent"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
methods: {
handleCustomEvent (data) {
console.log(data) // 输出 "Hello World"
}
}
}
</script>
以上是 Vue 父子组件通信的两种方式以及使用示例代码。在实际开发中,我们可以根据具体的需求选择合适的方式来实现父子组件之间的通信。
2.4 非父子组件通信
在 Vue 中,非父子组件之间的通信是指两个没有直接关系的组件之间的通信,比如兄弟组件、跨级组件等。Vue 提供了多种方式来实现非父子组件之间的通信,下面是其中的两种方式:
2.4.1 Event Bus
Event Bus 是一种常见的非父子组件通信方式,它利用一个空的 Vue 实例作为中央事件总线(或者叫事件中心),用来传递事件和数据。
// EventBus.js
import Vue from 'vue'
export default new Vue()
// ComponentA.vue
import EventBus from './EventBus.js'
export default {
methods: {
handleClick () {
EventBus.$emit('custom-event', 'Hello World')
}
}
}
// ComponentB.vue
import EventBus from './EventBus.js'
export default {
mounted () {
EventBus.$on('custom-event', data => {
console.log(data) // 输出 "Hello World"
})
}
}
2.4.2 Vuex
Vuex 是 Vue 的状态管理库,它提供了一种集中式管理应用所有组件的状态的方式,也可以用来实现非父子组件之间的通信。
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
message: 'Hello World'
},
mutations: {
setMessage (state, message) {
state.message = message
}
}
})
// ComponentA.vue
export default {
methods: {
handleClick () {
this.$store.commit('setMessage', 'Hello World')
}
}
}
// ComponentB.vue
export default {
computed: {
message () {
return this.$store.state.message
}
}
}
以上是 Vue 非父子组件通信的两种方式以及使用示例代码。在实际开发中,我们可以根据具体的需求选择合适的方式来实现非父子组件之间的通信。
2.5 动态组件
Vue 动态组件是一种常见的组件复用方式,它可以根据组件的数据来动态地选择要渲染的组件。下面是 Vue 动态组件的常见用法示例代码:
<template>
<div>
<component :is="currentComponent"></component>
<button @click="toggleComponent">Toggle Component</button>
</div>
</template>
<script>
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'
export default {
components: {
ComponentA,
ComponentB
},
data () {
return {
currentComponent: 'ComponentA'
}
},
methods: {
toggleComponent () {
this.currentComponent = this.currentComponent === 'ComponentA' ? 'ComponentB' : 'ComponentA'
}
}
}
</script>
在上面的示例代码中,我们通过 component 标签来渲染动态组件,并通过 :is 属性指定要渲染的组件。在 data 中定义了一个 currentComponent 变量,初始值为 ComponentA,表示默认渲染 ComponentA 组件。在 toggleComponent 方法中,我们通过判断当前渲染的组件来动态地切换要渲染的组件。
需要注意的是,动态组件的名称必须是一个字符串或一个组件选项对象,不能是一个变量或表达式。如果需要渲染一个动态组件列表,可以使用 v-for 循环来渲染多个动态组件。
2.6 组件注册
在 Vue 中,组件可以通过 Vue.component 方法或者单文件组件来注册。下面是两种方式的示例代码:
2.6.1 Vue.component 方法注册组件
<template>
<div>
<my-component></my-component>
</div>
</template>
<script>
import MyComponent from './MyComponent.vue'
export default {
components: {
'my-component': MyComponent
}
}
</script>
在上面的示例代码中,我们通过 Vue.component 方法来注册一个名为 my-component 的组件,它的定义在 MyComponent.vue 文件中。在组件选项中,我们通过 components 属性将 my-component 组件注册到当前组件中。在模板中,我们可以像普通 HTML 标签一样使用 my-component 组件。
2.6.2 单文件组件注册组件
<template>
<div>
<my-component></my-component>
</div>
</template>
<script>
import MyComponent from './MyComponent.vue'
export default {
components: {
'my-component': MyComponent
}
}
</script>
<style>
/* 样式代码 */
</style>
在上面的示例代码中,我们通过单文件组件的方式来注册一个名为 my-component 的组件,它的定义在同一个文件中的 <script> 标签中。在组件选项中,我们同样通过 components 属性将 my-component 组件注册到当前组件中。在模板中和样式中,我们可以像普通 HTML 标签和 CSS 样式一样使用 my-component 组件。
需要注意的是,组件名必须是一个 kebab-case 形式的字符串,即使用短横线分隔单词。在模板中使用组件时,组件名也必须是 kebab-case 形式的字符串。
2.7 组件props
2.7.1 基本用法:在子组件中定义 props,然后在父组件中通过属性绑定传递数据。
// 子组件
Vue.component('child-component', {
props: ['message'],
template: '<div>{{ message }}</div>'
});
// 父组件
<template>
<div>
<child-component :message="'Hello World'"></child-component>
</div>
</template>
2.7.2 指定 props 的类型和默认值:
// 子组件
Vue.component('child-component', {
props: {
message: {
type: String,
required: true,
default: 'Hello World'
}
},
template: '<div>{{ message }}</div>'
});
// 父组件
<template>
<div>
<child-component :message="'Hello Vue'"></child-component>
</div>
</template>
2.7.3 使用 props 来触发事件:
// 子组件
Vue.component('child-component', {
props: ['message'],
template: '<button @click="$emit(\'custom-event\', message)">Click me!</button>'
});
// 父组件
<template>
<div>
<child-component :message="'Hello Vue'" @custom-event="handleCustomEvent"></child-component>
</div>
</template>
<script>
export default {
methods: {
handleCustomEvent(message) {
console.log(message);
}
}
}
</script>
- Vue路由
3.1 安装Vue Router
3.1.1 npm 安装npm install vue-router
3.1.2 安装完成后,在您的 Vue 应用程序中导入 Vue Router:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
3.1.3 创建一个路由实例并将其添加到 Vue 实例中:
const router = new VueRouter({
routes: [
// 定义路由
{ path: '/home', component: Home },
{ path: '/about', component: About },
{ path: '/contact', component: Contact }
]
})
new Vue({
router,
el: '#app',
render: h => h(App)
})
3.2 路由配置
Vue Router 是 Vue.js 官方的路由管理器。它允许您在 Vue 应用程序中定义路由,以便用户在浏览器中导航到不同的页面或视图。以下是 Vue 路由配置的常见用法:
3.2.1 基本用法:定义路由和组件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from './components/Home.vue'
import About from './components/About.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
})
new Vue({
router,
el: '#app'
})
在这个示例中,我们定义了两个路由,一个是根路径 / 对应 Home 组件,另一个是 /about 对应 About 组件。我们还创建了一个 router 实例,并将其传递给 Vue 实例。
3.2.2 带参数的路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import User from './components/User.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
new Vue({
router,
el: '#app'
})
在这个示例中,我们定义了一个 /user/:id 路由,其中 :id 是一个参数。当用户访问 /user/123 路径时,Vue Router 会将参数 123 传递给 User 组件。
3.2.3 嵌套路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import User from './components/User.vue'
import UserProfile from './components/UserProfile.vue'
import UserPosts from './components/UserPosts.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
children: [
{
path: '',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]
})
new Vue({
router,
el: '#app'
})
在这个示例中,我们定义了一个 /user/:id 路由,并将其嵌套在 User 组件中。在 User 组件中,我们定义了两个子路由:一个是空路径对应 UserProfile 组件,另一个是 /posts 对应 UserPosts 组件。
这些是 Vue 路由配置的一些常见用法。使用这些功能,您可以创建复杂的单页应用程序,并使用户能够轻松地导航到不同的页面或视图。
3.3 路由导航
Vue Router 提供了许多用于导航的功能,以下是一些常见的路由导航功能:
3.3.1 声明式导航
使用 <router-link> 组件可以在 Vue 应用程序中声明式地导航。例如:
<router-link to="/home">Home</router-link>
这会创建一个链接,当用户单击它时,它会导航到 /home 路径。
3.3.2 编程式导航
您可以使用 $router 对象进行编程式导航。例如:
// 在当前路由下导航到 /home
this.$router.push('/home')
// 在当前路由下导航到 /about,并保留浏览历史记录
this.$router.push({ path: '/about', preserveQueryParams: true })
// 在当前路由下导航到 /user/123
this.$router.push('/user/123')
// 在当前路由下导航到 /user/123,并保留查询参数
this.$router.push({ path: '/user/123', query: { foo: 'bar' } })
// 在当前路由下导航到一个命名路由
this.$router.push({ name: 'user', params: { id: 123 } })
3.3.3 动态路由
您可以在路由路径中使用参数来创建动态路由。例如:
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
在这个示例中,我们定义了一个 /user/:id 路由,其中 :id 是一个参数。当用户访问 /user/123 路径时,Vue Router 会将参数 123 传递给 User 组件。
3.3.4 命名路由
您可以为路由定义一个名称,并在导航时使用该名称。例如:
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, name: 'user' }
]
})
// 在代码中使用命名路由
this.$router.push({ name: 'user', params: { id: 123 } })
3.3.5 重定向和别名
您可以使用重定向和别名来更改路由的行为。例如:
const router = new VueRouter({
routes: [
{ path: '/home', component: Home },
{ path: '/about', component: About },
{ path: '*', redirect: '/home' },
{ path: '/contact', component: Contact, alias: '/contact-us' }
]
})
在这个示例中,我们定义了一个 * 路由,它会将所有未匹配的路径重定向到 /home 路径。我们还定义了一个 /contact 路由,并为它定义了一个别名 /contact-us。
这些是 Vue Router 中一些常见的路由导航功能。使用这些功能,您可以轻松地创建复杂的单页应用程序,并使用户能够轻松地导航到不同的页面或视图。
3.6 命名路由
Vue 命名路由的常见用法是在路由定义中为路由指定一个名称,然后在代码中使用该名称来导航到该路由。以下是一些常见的用法示例代码:
3.6.1 简单的命名路由
const router = new VueRouter({
routes: [
{
path: '/home',
component: Home,
name: 'home'
}
]
})
在这个示例中,我们为 /home 路由指定了一个名称 home。我们可以在代码中使用 $router.push 方法来导航到该路由:
this.$router.push({ name: 'home' })
3.6.2 带参数的命名路由
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
name: 'user'
}
]
})
在这个示例中,我们为 /user/:id 路由指定了一个名称 user。我们可以在代码中使用 $router.push 方法来导航到该路由,并传递参数:
this.$router.push({ name: 'user', params: { id: 123 } })
3.6.3 嵌套命名路由
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
children: [
{
path: 'profile',
component: Profile,
name: 'user-profile'
},
{
path: 'posts',
component: Posts,
name: 'user-posts'
}
]
}
]
})
在这个示例中,我们定义了一个 /user/:id 路由,它有两个子路由:/user/:id/profile 和 /user/:id/posts。我们为这两个子路由分别指定了名称 user-profile 和 user-posts。我们可以在代码中使用 $router.push 方法来导航到这些子路由:
this.$router.push({ name: 'user-profile', params: { id: 123 } })
this.$router.push({ name: 'user-posts', params: { id: 123 } })
这些示例演示了 Vue 命名路由的常见用法。使用命名路由可以使代码更具可读性和可维护性,并使导航更加灵活。
3.7 嵌套路由
Vue 嵌套路由是指在一个路由下定义多个子路由,这些子路由可以有自己的组件和路径,从而实现更加灵活的页面结构。以下是一些常见的 Vue 嵌套路由用法示例代码:
3.7.1 基本的嵌套路由
const router = new VueRouter({
routes: [
{
path: '/home',
component: Home,
children: [
{
path: 'news',
component: News
},
{
path: 'about',
component: About
}
]
}
]
})
在这个示例中,我们定义了一个 /home 路由,它有两个子路由:/home/news 和 /home/about。我们可以在 Home 组件中使用 <router-view> 标签来渲染这些子路由。
3.7.2 带参数的嵌套路由
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
children: [
{
path: 'profile',
component: Profile
},
{
path: 'posts',
component: Posts
}
]
}
]
})
在这个示例中,我们定义了一个 /user/:id 路由,它有两个子路由:/user/:id/profile 和 /user/:id/posts。这些子路由都可以访问 User 组件中的 id 参数。
3.7.3 命名视图的嵌套路由
const router = new VueRouter({
routes: [
{
path: '/home',
components: {
default: Home,
sidebar: Sidebar
},
children: [
{
path: 'news',
components: {
default: News,
sidebar: NewsSidebar
}
},
{
path: 'about',
components: {
default: About,
sidebar: AboutSidebar
}
}
]
}
]
})
在这个示例中,我们定义了一个 /home 路由,它有两个子路由:/home/news 和 /home/about。我们使用命名视图来为每个路由指定多个组件。例如,我们为 /home 路由指定了 Home 组件和 Sidebar 组件,为 /home/news 路由指定了 News 组件和 NewsSidebar 组件。
这些示例演示了 Vue 嵌套路由的常见用法。使用嵌套路由可以使页面结构更加灵活,同时也可以提高代码的可读性和可维护性。
3.7.4 命名视图的进一步说明
Vue 命名视图(Named Views)是一种高级的路由技术,它允许我们在一个路由中同时渲染多个视图。通常情况下,一个路由只会渲染一个视图,这个视图通常是一个单文件组件。但是,在某些场景下,我们可能需要在一个路由中渲染多个视图,例如在一个页面中同时显示主要内容和侧边栏。
使用命名视图可以让我们在一个路由中指定多个组件,并且让这些组件分别渲染到不同的视图中。例如,在以下代码中:
const router = new VueRouter({
routes: [
{
path: '/home',
components: {
default: Home,
sidebar: Sidebar
}
}
]
})
我们为 /home 路由指定了两个组件:Home 组件和 Sidebar 组件。其中,Home 组件会被渲染到默认的视图中,而 Sidebar 组件会被渲染到名为 sidebar 的命名视图中。在使用命名视图时,我们需要在模板中使用 <router-view> 标签来指定每个视图的位置,例如:
<template>
<div>
<router-view></router-view>
<router-view name="sidebar"></router-view>
</div>
</template>
在这个模板中,我们使用了两个 <router-view> 标签来分别指定默认视图和命名视图的位置。其中,命名视图需要使用 name 属性来指定视图的名称。
使用命名视图可以让我们更加灵活地组织页面结构,同时也可以提高代码的可读性和可维护性。但是,在使用命名视图时需要注意一些问题,例如在定义路由时需要使用 components 属性来指定多个组件,而不是使用 component 属性来指定单个组件。此外,在模板中也需要使用正确的命名视图名称来渲染对应的组件。
3.8 路由参数
在 Vue 中,我们可以使用路由参数来传递数据。路由参数是指在路由路径中包含的动态参数,例如 /user/:id 中的 :id 就是一个路由参数。我们可以使用 $route.params
来访问当前路由的参数。除了路由参数,我们还可以使用查询参数(Query Parameters)来传递数据,查询参数是指在 URL 中以 ? 开头的键值对,例如 /user?id=123 中的 id=123 就是一个查询参数。我们可以使用 $route.query 来访问当前路由的查询参数。
下面是几个常见的路由参数传递的用法:
3.8.1 动态路由
动态路由是指在路由路径中包含动态参数的路由,例如 /user/:id。我们可以使用动态路由来实现根据用户 ID 显示不同用户信息的功能。以下是一个动态路由的示例代码:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User
}
]
})
// User.vue
<template>
<div>
User ID: {{ $route.params.id }}
</div>
</template>
在这个示例中,我们定义了一个动态路由 /user/:id,并且在 User 组件中通过 $route.params.id 访问了当前路由的参数。
3.8.2 命名路由
命名路由是指给一个路由设置一个名称,以便在代码中进行跳转或传递参数。命名路由可以让我们在代码中使用路由名称来跳转路由,而不必担心路由路径的变化。以下是一个命名路由的示例代码:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
name: 'user',
component: User
}
]
})
// 跳转到命名路由
<router-link :to="{ name: 'user', params: { id: 123 }}">User</router-link>
// User.vue
<template>
<div>
User ID: {{ $route.params.id }}
</div>
</template>
在这个示例中,我们定义了一个命名路由 user,并且在代码中使用 <router-link> 标签来跳转到该路由。在 User 组件中,我们仍然可以通过 $route.params.id 访问当前路由的参数。
3.8.3 查询参数
查询参数是指在 URL 中以 ? 开头的键值对,例如 /user?id=123 中的 id=123 就是一个查询参数。我们可以使用查询参数来传递一些非必要的参数,例如分页信息、搜索关键字等。以下是一个查询参数的示例代码:
const router = new VueRouter({
routes: [
{
path: '/user',
component: User
}
]
})
// 跳转到带查询参数的路由
<router-link :to="{ path: '/user', query: { id: 123 }}">User</router-link>
// User.vue
<template>
<div>
User ID: {{ $route.query.id }}
</div>
</template>
在这个示例中,我们定义了一个路由 /user,并且在 <router-link> 标签中使用 query 属性来传递查询参数。在 User 组件中,我们使用 $route.query.id 访问当前路由的查询参数。
3.9 路由组件传参
在 Vue 中,我们可以使用路由组件传参来实现组件之间的数据传递。路由组件传参是指通过路由配置传递数据给组件的一种方式。以下是几个常见的路由组件传参的用法:
3.9.1 props
我们可以在路由配置中使用 props 属性来传递数据给组件。props 属性可以是一个对象或一个函数,用来将路由参数映射为组件的属性。以下是一个使用 props 属性传递数据的示例代码:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
props: true
}
]
})
// User.vue
<template>
<div>
User ID: {{ id }}
</div>
</template>
<script>
export default {
props: {
id: {
type: Number,
required: true
}
}
}
</script>
在这个示例中,我们在路由配置中设置了 props: true,表示将路由参数作为组件的属性传递。在 User 组件中,我们定义了一个 id 属性,并且在 props 中声明了该属性的类型和是否必须。在模板中,我们使用 {{ id }} 访问该属性。
3.9.2 字符串
除了使用 props 属性,我们还可以在路由路径中使用字符串传递数据。例如,我们可以定义一个带有参数的路由 /user/:id,并且在代码中通过 $route.params.id 访问该参数。以下是一个使用字符串传递数据的示例代码:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User
}
]
})
// User.vue
<template>
<div>
User ID: {{ $route.params.id }}
</div>
</template>
在这个示例中,我们定义了一个带有参数的路由 /user/:id,并且在 User 组件中通过 $route.params.id 访问该参数。
3.9.3 对象
除了使用 props 属性和字符串,我们还可以在路由配置中使用对象传递数据。对象传递数据可以用来传递一些非路由参数的数据,例如页面标题、面包屑导航等。以下是一个使用对象传递数据的示例代码:
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
meta: {
title: 'User Profile'
}
}
]
})
// User.vue
<script>
export default {
metaInfo() {
return {
title: this.$route.meta.title
}
}
}
</script>
在这个示例中,我们在路由配置中设置了一个 meta 对象,用来传递页面标题。在 User 组件中,我们使用 metaInfo 函数来动态设置页面标题,通过 $route.meta.title 访问该标题。
- Vue状态管理
4.1 安装Vuex
4.1.1 首先,使用npm安装Vuex。在终端中,进入Vue项目的根目录并运行以下命令:
npm install vuex --save
4.1.2 安装完成后,在Vue项目的main.js文件中导入Vuex并创建一个新的store实例。您可以按照以下示例代码进行操作:
import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
new Vue({
el: '#app',
store,
render: h => h(App)
})
在上面的示例代码中,我们创建了一个名为“store”的新Vuex实例,并在Vue实例中将其传递给“store”选项。在“store”对象中,我们定义了一个名为“count”的状态和一个名为“increment”的突变。状态是我们应用程序中需要跟踪的数据,而突变是用于更改状态的方法。
4.1.3 现在,您可以在Vue组件中使用Vuex。例如,在组件中获取状态并触发突变的示例代码如下:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
computed: {
count () {
return this.$store.state.count
}
},
methods: {
increment () {
this.$store.commit('increment')
}
}
}
</script>
在上面的示例代码中,我们使用计算属性从Vuex store中获取状态,并使用方法来触发突变。当用户单击“Increment”按钮时,将调用名为“increment”的突变,从而递增状态中的计数器。
4.2 state
在Vue中,state是用于存储应用程序状态的对象。在Vuex中,state是唯一的数据源,并且所有状态的更改都必须通过mutations来进行。以下是一个简单的示例代码,演示如何在Vue中使用state:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
}
})
在上面的示例代码中,我们定义了一个名为“count”的状态,并使用mutations来递增和递减这个状态。现在,我们可以在Vue组件中使用这个状态了:
// Counter.vue
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment', 'decrement'])
}
}
</script>
在上面的示例代码中,我们使用了Vuex提供的辅助函数mapState和mapMutations来将state和mutations映射到组件的计算属性和方法中。现在,当用户单击“Increment”或“Decrement”按钮时,将调用相应的mutations来更改状态。
希望这个示例能够帮助您理解Vue中的state对象。
4.3 getters
Vuex getters是用于从store中获取数据的函数,类似于store中的计算属性。它们可以接受state作为第一个参数,并且可以接受其他getter作为第二个参数。Getter函数的返回值会根据它的依赖被缓存起来,只有当依赖发生改变才会重新计算。
下面是一个示例代码,展示如何使用Vuex getters:
// 在store中定义getter
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: 'Buy groceries', done: false },
{ id: 2, text: 'Do laundry', done: true },
{ id: 3, text: 'Clean room', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
// 使用其他getter作为参数
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
})
// 在组件中使用getter
export default {
computed: {
// 获取doneTodosCount的值
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
}
在上面的示例中,我们定义了一个getter函数doneTodos,用于获取所有已完成的任务。我们还定义了另一个getter函数doneTodosCount,它依赖于doneTodos,用于获取已完成任务的数量。在组件中,我们可以通过this.$store.getters.doneTodosCount来获取已完成任务的数量。
4.4 mutations
Vuex mutations是用于修改store中状态的唯一途径。每个mutation都有一个字符串类型的事件类型(type)和一个回调函数(handler),回调函数接受state作为第一个参数,并且可以接受额外的载荷(payload)作为第二个参数。我们可以在回调函数中修改state的值。
下面是一个示例代码,展示如何使用Vuex mutations:
// 在store中定义mutation
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
// 增加count的值
increment (state) {
state.count++
},
// 减少count的值
decrement (state) {
state.count--
},
// 增加count的值,根据传入的payload
incrementBy (state, payload) {
state.count += payload.amount
}
}
})
// 在组件中使用mutation
export default {
methods: {
// 调用increment mutation
increment () {
this.$store.commit('increment')
},
// 调用decrement mutation
decrement () {
this.$store.commit('decrement')
},
// 调用incrementBy mutation,并传入payload
incrementBy (amount) {
this.$store.commit('incrementBy', { amount: amount })
}
}
}
在上面的示例中,我们定义了三个mutation函数,分别用于增加count的值、减少count的值以及根据传入的payload增加count的值。在组件中,我们可以通过this.$store.commit('mutationName', payload)来调用mutation函数。其中,mutationName是mutation的事件类型,payload是可选的载荷。通过这种方式,我们可以修改store中的状态。
4.6 actions
Vuex actions是用于处理异步操作的函数,它们可以包含任意异步操作、提交mutation等。每个action都有一个字符串类型的事件类型(type)和一个回调函数(handler),回调函数接受一个context对象作为第一个参数,context对象中包含了state、commit、dispatch等方法,以及额外的载荷(payload)作为第二个参数。我们可以在回调函数中执行异步操作,并通过commit提交mutation来修改state的值。
下面是一个示例代码,展示如何使用Vuex actions:
// 在store中定义action
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
// 增加count的值
increment (state) {
state.count++
}
},
actions: {
// 延迟一秒后增加count的值
incrementAsync (context) {
setTimeout(() => {
context.commit('increment')
}, 1000)
},
// 增加count的值,根据传入的payload
incrementByAsync (context, payload) {
setTimeout(() => {
context.commit('incrementBy', payload)
}, 1000)
}
}
})
// 在组件中使用action
export default {
methods: {
// 调用incrementAsync action
incrementAsync () {
this.$store.dispatch('incrementAsync')
},
// 调用incrementByAsync action,并传入payload
incrementByAsync (amount) {
this.$store.dispatch('incrementByAsync', { amount: amount })
}
}
}
在上面的示例中,我们定义了两个action函数,分别用于延迟一秒后增加count的值以及根据传入的payload增加count的值。在组件中,我们可以通过this.$store.dispatch('actionName', payload)来调用action函数。其中,actionName是action的事件类型,payload是可选的载荷。通过这种方式,我们可以处理异步操作,并通过commit提交mutation来修改store中的状态。
4.7 modules
Vuex modules是用于将store分割成多个模块的功能,每个模块都有自己的state、mutations、actions、getters等。这样可以使得store更加灵活、易于维护。每个模块都可以包含自己的子模块,形成一个树形结构。
下面是一个示例代码,展示如何使用Vuex modules:
// 在store中定义module
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
// 使用moduleA
a: moduleA,
// 使用moduleB
b: moduleB
}
})
// 在组件中使用module
export default {
computed: {
// 获取moduleA中的count值
countA () {
return this.$store.state.a.count
},
// 获取moduleB中的count值
countB () {
return this.$store.state.b.count
}
},
methods: {
// 调用moduleA中的increment action
incrementA () {
this.$store.dispatch('a/increment')
},
// 调用moduleB中的decrement action
decrementB () {
this.$store.dispatch('b/decrement')
}
}
}
在上面的示例中,我们定义了两个module,分别是moduleA和moduleB。在store中,我们使用modules选项来引入这两个module。在组件中,我们可以通过this.$store.state.moduleName.stateName
来访问module中的state值,通过this.$store.dispatch('moduleName/actionName')
来调用module中的action。其中,moduleName是module的名称,actionName是action的名称。通过这种方式,我们可以将store分割成多个模块,使得store更加灵活、易于维护。
- Vue进阶
5.1 插槽
Vue的插槽(slot)是一种组件间通信的方式,它允许父组件向子组件传递任意内容,包括HTML、组件、函数等。插槽可以在子组件中作为一个标记位置,父组件可以将任意内容插入到这个标记位置中,从而实现动态渲染的效果。
下面是一个示例代码,展示如何使用Vue的插槽:
<!-- 父组件 -->
<template>
<div>
<h1>我是父组件</h1>
<child-component>
<!-- 插入内容到默认插槽 -->
<p>这是一段默认插槽的内容</p>
</child-component>
<child-component>
<!-- 插入内容到具名插槽 -->
<template v-slot:header>
<h2>这是header插槽的内容</h2>
</template>
<template v-slot:footer>
<h2>这是footer插槽的内容</h2>
</template>
</child-component>
</div>
</template>
<!-- 子组件 -->
<template>
<div>
<h3>我是子组件</h3>
<!-- 渲染默认插槽 -->
<slot></slot>
<!-- 渲染具名插槽 -->
<slot name="header"></slot>
<slot name="footer"></slot>
</div>
</template>
在上面的示例中,我们定义了一个父组件和一个子组件。在父组件中,我们使用<child-component>来引入子组件,并在其中通过<slot>标签插入内容到默认插槽中。同时,我们使用<template>标签来定义具名插槽,并在其中插入内容。在子组件中,我们使用<slot>标签来渲染默认插槽,使用<slot name="slotName">来渲染具名插槽。其中,slotName是具名插槽的名称。通过这种方式,父组件可以向子组件传递任意内容,并动态渲染出需要的效果。
5.2 过渡和动画
Vue的过渡和动画是一种在组件显示和隐藏时添加动画效果的方式。Vue提供了<transition>和<transition-group>两个组件来实现过渡和动画效果。
<transition>组件可以为组件的显示和隐藏添加过渡效果。可以通过设置name属性来指定过渡效果的名称,然后在CSS中定义过渡效果的样式。<transition>组件还提供了几个钩子函数,可以在过渡的不同阶段执行一些操作。
下面是一个示例代码,展示如何使用<transition>组件实现过渡效果:
<template>
<div>
<button @click="show=!show">Toggle</button>
<transition name="fade">
<p v-if="show">这是一个有过渡效果的段落。</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
在上面的示例中,我们使用<transition>组件包裹了一个段落,并通过v-if指令来控制段落的显示和隐藏。在CSS中,我们定义了.fade-enter-active和.fade-leave-active类来设置过渡效果的样式,定义了.fade-enter和.fade-leave-to类来设置过渡前后的样式。当段落显示和隐藏时,Vue会自动添加相应的类名,从而实现过渡效果。
<transition-group>组件可以为多个组件的显示和隐藏添加过渡效果。与<transition>组件不同的是,<transition-group>组件需要使用v-for指令来渲染多个组件,并设置name属性来指定过渡效果的名称。<transition-group>组件还提供了几个钩子函数,可以在过渡的不同阶段执行一些操作。
下面是一个示例代码,展示如何使用<transition-group>组件实现动画效果:
<template>
<div>
<button @click="addItem">Add Item</button>
<button @click="removeItem">Remove Item</button>
<transition-group name="list" tag="ul">
<li v-for="(item, index) in items" :key="item.id">{{ item.text }}</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' }
]
}
},
methods: {
addItem() {
const id = this.items.length + 1
this.items.push({ id, text: `Item ${id}` })
},
removeItem() {
this.items.pop()
}
}
}
</script>
<style>
.list-enter-active, .list-leave-active {
transition: all .5s;
}
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
</style>
在上面的示例中,我们使用<transition-group>组件包裹了一个<ul>元素,并使用v-for指令渲染多个<li>元素。在CSS中,我们定义了.list-enter-active和.list-leave-active类来设置过渡效果的样式,定义了.list-enter和.list-leave-to类来设置过渡前后的样式。当添加或删除<li>元素时,Vue会自动添加相应的类名,从而实现动画效果。
5.3 自定义过滤器
Vue.js 允许我们自定义过滤器来处理文本格式化。 自定义过滤器是一种简单的方法来过滤文本格式,以便在应用程序中重复使用。下面是一个示例代码:
<template>
<div>
<p>{{ message | capitalize }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'hello world'
}
},
filters: {
capitalize(value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
}
</script>
在这个示例中,我们定义了一个 capitalize 过滤器,它将字符串的第一个字母大写,并返回新的字符串。在模板中,我们使用 message 数据属性,并将其传递给 capitalize 过滤器。最后,我们将过滤器返回的结果渲染到页面上。
您可以使用 Vue.filter() 全局方法来定义全局过滤器。例如:
Vue.filter('capitalize', function(value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
然后,在任何组件中都可以使用 capitalize 过滤器。
5.4 mixin
Vue.js 中的 mixin 是一种可重复使用的代码块,它可以在多个组件之间共享。Mixin 可以包含任何组件选项,例如 data、methods、computed 等。
下面是一个示例代码:
const myMixin = {
data() {
return {
message: 'Hello from mixin!'
}
},
methods: {
sayHello() {
console.log(this.message)
}
}
}
export default {
mixins: [myMixin],
data() {
return {
name: 'John'
}
},
created() {
this.sayHello()
}
}
在这个示例中,我们定义了一个名为 myMixin 的 mixin,它包含一个名为 message 的数据属性和一个名为 sayHello 的方法。然后,在我们的组件中,我们使用 mixins 选项将 myMixin 添加到组件中。组件还包含一个名为 name 的数据属性和一个在 created 生命周期钩子中调用的 sayHello 方法。由于我们使用了 mixin,因此组件也具有 message 数据属性和 sayHello 方法。
您可以使用多个 mixin,并且它们将按照定义的顺序进行合并。如果多个 mixin 包含同名选项,则这些选项将合并为一个数组,并且在组件中定义的选项将覆盖 mixin 中的选项。
请注意,mixin 不应该被滥用。如果您发现自己在多个组件中使用相同的代码,则可以考虑将其提取到 mixin 中。但是,如果您的 mixin 包含太多代码或太多选项,则可能会导致代码难以维护。
5.5 v-model原理
v-model 是 Vue.js 中一种常见的指令,它用于在表单元素和组件之间创建双向数据绑定。当表单元素的值发生变化时,组件中的数据也会随之更新;反之亦然。
v-model 的实现原理是通过绑定 value 属性和 input 事件来实现双向数据绑定。对于表单元素,v-model 绑定的是 value 属性和 input 事件;对于自定义组件,v-model 绑定的是 value 属性和名为 input 的自定义事件。这样,当表单元素或自定义组件的值发生变化时,就会触发相应的 input 事件,从而更新组件中绑定的数据。
下面是一个示例代码:
<template>
<div>
<input v-model="message">
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello world!'
}
}
}
</script>
在这个示例中,我们使用 v-model 指令将输入框和组件中的 message 数据属性进行双向绑定。当用户在输入框中输入文本时,message 数据属性也会相应地更新,并且在页面上显示出来。
需要注意的是,对于一些特殊的表单元素,例如 checkbox 和 radio,v-model 的实现方式略有不同。对于 checkbox,v-model 绑定的是 checked 属性和 change 事件;对于 radio,v-model 绑定的是 value 属性和 change 事件。
5.6 服务端渲染
Vue.js 服务端渲染(SSR)是一种将 Vue.js 应用程序渲染为 HTML 字符串的技术,该技术可以提高应用程序的首次加载速度和搜索引擎优化(SEO)。服务端渲染可以将 Vue.js 应用程序的初始渲染放在服务器端完成,然后将渲染后的 HTML 字符串发送给客户端,客户端在接收到 HTML 字符串后,再将其转换为交互式应用程序。
下面是一个示例代码:
// server.js
const express = require('express')
const Vue = require('vue')
const serverRenderer = require('vue-server-renderer').createRenderer()
const app = express()
app.get('*', (req, res) => {
const vm = new Vue({
data: {
message: 'Hello from server!'
},
template: `<div>{{ message }}</div>`
})
serverRenderer.renderToString(vm, (err, html) => {
if (err) {
res.status(500).send('Internal Server Error')
} else {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Vue SSR</title>
</head>
<body>
${html}
</body>
</html>
`)
}
})
})
app.listen(3000, () => {
console.log('Server started on port 3000')
})
这个示例中,我们使用 Express 框架创建了一个简单的服务器。在服务器的路由处理函数中,我们创建了一个新的 Vue 实例,并将其渲染为 HTML 字符串。我们使用 vue-server-renderer 模块中的 createRenderer() 方法创建了一个渲染器,并使用 renderToString() 方法将 Vue 实例渲染为 HTML 字符串。最后,我们将 HTML 字符串包装在一个完整的 HTML 页面中,并将其发送给客户端。
需要注意的是,服务端渲染需要考虑到一些特殊的问题,例如在服务器上无法使用浏览器 API、需要处理异步数据获取等等。因此,在实际应用中,需要根据具体情况进行相应的调整和优化。
5.7 单元测试
Vue.js 的单元测试是一种测试技术,用于测试 Vue.js 组件的行为和功能。单元测试可以帮助我们确保组件的正确性和稳定性,从而提高代码质量和可维护性。
Vue.js 的单元测试通常使用 Jest 或 Mocha 等测试框架,以及 Vue Test Utils 等测试工具。其中,Vue Test Utils 是 Vue.js 官方提供的测试工具,它提供了一系列 API,用于模拟组件的行为和状态,并对组件进行断言验证。
下面是一个示例代码:
<!-- Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
},
decrement() {
this.count--
}
}
}
</script>
// Counter.spec.js
import { shallowMount } from '@vue/test-utils'
import Counter from './Counter.vue'
describe('Counter.vue', () => {
it('renders count', () => {
const wrapper = shallowMount(Counter)
expect(wrapper.text()).toContain('Count: 0')
})
it('increments count when button is clicked', () => {
const wrapper = shallowMount(Counter)
const button = wrapper.find('button:first-of-type')
button.trigger('click')
expect(wrapper.text()).toContain('Count: 1')
})
it('decrements count when button is clicked', () => {
const wrapper = shallowMount(Counter)
const button = wrapper.find('button:last-of-type')
button.trigger('click')
expect(wrapper.text()).toContain('Count: -1')
})
})
在这个示例中,我们创建了一个简单的计数器组件 Counter,并编写了三个测试用例来测试组件的行为和功能。在每个测试用例中,我们使用 shallowMount 方法创建了一个组件实例,并对其进行断言验证。其中,第一个测试用例验证了组件是否正确渲染初始计数器的值;第二个和第三个测试用例分别验证了点击增加和减少按钮后,计数器的值是否正确更新。
需要注意的是,在编写单元测试时,应该尽量避免测试具体的实现细节,而是应该关注组件的行为和功能。这样可以确保测试用例的可维护性和可重用性。