一、计算属性 - computed
computed
(计算属性)在 Vue 2 和 Vue 3 中都能使用,主要用于处理依赖响应式数据的计算逻辑,它的值会根据依赖的变化自动更新,同时有缓存,比 methods
更高效。
1. 基本使用
示例:把 message
转换为大写
<div id="app">
<p>原始文本: {
{ message }}</p>
<p>大写文本: {
{ upperMessage }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'hello vue'
},
computed: {
upperMessage() {
return this.message.toUpperCase()
}
}
})
</script>
upperMessage
依赖 message
,当 message
变化时,它会自动更新。
computed
有缓存,只有依赖的数据变化时才会重新计算,比 methods
更高效。
2. 计算属性 vs 方法 (methods
)
<div id="app">
<p>{
{ upperMessage() }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'hello vue'
},
methods: {
upperMessage() {
return this.message.toUpperCase()
}
}
})
</script>
区别:
computed
有缓存,只在message
变化时重新计算。methods
每次调用都会重新执行,即使message
没变,仍然会运行函数。
✅ 结论:
- 用
computed
处理依赖数据的计算,它只会在需要时计算,性能更好。 - 用
methods
处理事件或不需要缓存的逻辑。
3. 计算属性的 getter 和 setter
computed
默认只有 getter
(读取),但也可以加 setter
(写入):
<div id="app">
<input v-model="fullName">
</div>
<script>
new Vue({
el: '#app',
data: {
fullName: '张 三'
},
computed: {
fullName: {
get() {
return this.firstName + ' ' + this.lastName
},
set(newValue) {
const names = newValue.split(' ')
this.lastName = names[0]
this.firstName = names[1] || ''
}
}
}
})
</script>
4. 依赖多个属性
computed
可以依赖多个 data
变量:
<div id="app">
<p>{
{ userInfo }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
firstName: '三',
lastName: '张',
age: '18'
},
computed: {
userInfo() {
return `${this.lastName} ${this.firstName} - ${this.age}岁`
}
}
})
</script>
任何 firstName
、lastName
、age
变化,userInfo
都会自动更新。
5. 计算属性在 Vue 3 中的写法
Vue 3 组合式 API 中,computed
需要 import
:
import { ref, computed } from 'vue'
export default {
setup() {
const message = ref('hello vue3')
const upperMessage = computed(() => message.value.toUpperCase())
return { message, upperMessage }
}
}
Vue 3 computed
需要 .value
访问 ref
的数据。
总结
✅ computed
计算属性的特点:
- 有缓存,只有依赖数据变化时才会重新计算,比
methods
更高效。 - 适用于依赖多个
data
属性的计算逻辑。 - 可以有
getter
和setter
,适用于双向绑定。 - 在 Vue 3 组合式 API 里,需要
computed(() => {})
使用。
🔹 适用场景:
- 格式化数据(大写、日期、货币格式化等)
- 合并多个
data
变量 - 计算总价、折扣、平均值等
🚀 如果数据要频繁计算,优先用 computed
,避免 methods
造成不必要的计算!
二、监听器 - watch
watch
主要用于监听数据的变化并执行特定逻辑,适用于数据变化后需要进行异步操作、复杂逻辑或性能优化的场景。
1. 基本用法
watch
用于监听 data
或 computed
里的数据变化,并在变化时执行函数。
<div id="app">
<p>姓名: {
{ name }}</p>
<input v-model="name">
</div>
<script>
new Vue({
el: '#app',
data: {
name: '张三'
},
watch: {
name(newVal, oldVal) {
console.log(`姓名从 ${oldVal} 变成了 ${newVal}`)
}
}
})
</script>
name
发生变化时,watch
里的函数会被调用。newVal
是新值,oldVal
是旧值。- 每次
input
修改name
,都会触发watch
。
2. 监听多个数据
可以监听多个数据:
watch: {
firstName(newVal) {
console.log(`firstName 变了: ${newVal}`)
},
lastName(newVal) {
console.log(`lastName 变了: ${newVal}`)
}
}
3. 监听对象或数组
(1)监听对象的某个属性
data() {
return {
user: { name: '张三', age: 25 }
}
},
watch: {
'user.age'(newVal, oldVal) {
console.log(`年龄从 ${oldVal} 变成了 ${newVal}`)
}
}
(2)深度监听 (deep: true
)
如果监听整个对象,需要deep: true
data() {
return {
user: { name: '张三', age: 25 }
}
},
watch: {
user: {
handler(newVal) {
console.log('user 变了:', newVal)
},
deep: true
}
}
任何 user
内部属性变化,watch
都会触发。
(3)监听数组
data() {
return {
items: [1, 2, 3]
}
},
watch: {
items: {
handler(newVal) {
console.log('数组变了:', newVal)
},
deep: true
}
}
修改 items
数组(如 push/pop/splice
),都会触发 watch
。
4. 监听 computed
计算属性
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
},
watch: {
fullName(newVal) {
console.log(`fullName 变了: ${newVal}`)
}
}
监听 computed
的值变化,适用于计算后的数据需要额外处理的情况
5. immediate
立即执行
默认情况下,watch
不会在组件创建时立即执行,但可以加 immediate: true
让它立即执行一次:
watch: {
name: {
handler(newVal) {
console.log(`初始化时就执行: ${newVal}`)
},
immediate: true
}
}
适用于组件初始化时就要执行的逻辑(如请求接口)。
6. 监听数据变化后执行异步操作
可以在 watch
里调用 API:
watch: {
searchQuery(newVal) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.fetchResults(newVal)
}, 500) // 防抖,500ms 后执行
}
}
适用于搜索输入时防止频繁请求 API。
7. Vue 3 watch
用法
在 Vue 3 组合式 API 中,使用 watch
需要 import
:
import { ref, watch } from 'vue'
export default {
setup() {
const name = ref('张三')
watch(name, (newVal, oldVal) => {
console.log(`姓名从 ${oldVal} 变成了 ${newVal}`)
})
return { name }
}
}
Vue 3 的 watch
监听 ref
数据时,需要 .value
访问数据。
总结
✅ watch
监听数据变化,并执行指定逻辑:
- 适用于异步操作、API 请求、防抖、复杂计算。
- 监听对象/数组时,需加
deep: true
。 - 监听
computed
计算属性,可处理衍生数据。 - 用
immediate: true
让watch
立即执行一次。 - Vue 3 需要
import { watch }
,且监听ref
需要.value
。
🚀 如果数据需要动态监听并执行操作,watch
是最佳选择!
三、过滤器 - filters
在数据被渲染之前,可以对其进行进一步处理,比如将字符截取或者将小写统一转换为大写等等,过滤器本身就是一个方法。过滤器可以定义全局或局部。
在 Vue 2 中,过滤器(filters
) 主要用于格式化文本,可以在 { { 插值表达式 }}
和 v-bind
绑定中使用。它本质上是一个函数,接收输入值,返回格式化后的值。
1. 全局 & 局部过滤器
Vue 2 提供全局过滤器和局部过滤器两种方式。
(1)全局过滤器
在 Vue.filter()
中定义,全局可用。
Vue.filter('uppercase', function(value) {
if (!value) return ''
return value.toString().toUpperCase()
})
然后在模板中使用:
<p>{
{ 'hello' | uppercase }}</p>
<!-- 输出: HELLO -->
(2)局部过滤器
在组件的 filters
选项里定义,只在当前组件可用:
<div id="app">
<!-- 输出: Hello world -->
<p>{
{ message | capitalize }}</p>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'hello world'
},
filters: {
capitalize(value) {
if (!value) return ''
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
2. 过滤器的多重使用
可以多个过滤器链式调用:
<p>{
{ message | capitalize | uppercase }}</p>
3. 过滤器用于 v-bind
在 v-bind
中使用时,需要用方法调用的形式:
<input :value="message | capitalize">
Vue 3 认为 filters
在模板中容易混乱,移除了 filters
,建议改用计算属性(computed
)或方法:
<div id="app">
<input :value="formattedMessage">
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'hello'
},
computed: {
formattedMessage() {
return this.message.charAt(0).toUpperCase() + this.message.slice(1)
}
}
})
4. 过滤器的应用场景
- 格式化文本(如大小写转换)
- 货币格式化
- 时间格式化
- 数字千分位分隔
四、混入 - Mixins
mixins
(混入)是 Vue 里的一种代码复用机制,可以在多个组件之间共享逻辑,避免代码重复,提高可维护性。
1. 基本使用
创建一个 mixins
,然后在组件中使用它。
// 定义一个 mixin
const myMixin = {
data() {
return {
message: 'Hello from mixin'
}
},
created() {
console.log('Mixin 的 created 钩子被调用')
},
methods: {
greet() {
console.log(this.message)
}
}
}
在组件中使用 mixins
new Vue({
el: '#app',
mixins: [myMixin], // 这里使用 mixin
created() {
console.log('组件的 created 钩子被调用')
}
})
组件会继承 mixins
里的 data
、methods
、生命周期钩子。
2. mixins
逻辑合并规则
Vue 组件和 mixins
可能有相同的 data
、methods
、生命周期钩子,Vue 按以下规则合并:
项 | 合并规则 |
data | 合并,但组件数据优先(mixin 里的数据不会覆盖组件) |
methods | 合并,但组件方法优先(mixin 方法被组件覆盖) |
生命周期钩子 | 都会执行,先 mixin ,后组件 |
3. data
合并
const myMixin = {
data() {
return {
message: 'Hello from mixin'
}
}
}
new Vue({
mixins: [myMixin],
data() {
return {
message: 'Hello from component'
}
},
created() {
console.log(this.message) // 输出:Hello from component
}
})
data
以组件的为准,不会被 mixins
覆盖。
4. methods
合并
const myMixin = {
methods: {
sayHello() {
console.log('Hello from mixin')
}
}
}
new Vue({
mixins: [myMixin],
methods: {
sayHello() {
console.log('Hello from component')
}
},
created() {
this.sayHello() // 输出:Hello from component
}
})
如果 methods
重名,组件的 methods
会覆盖 mixins
的方法。
5. 生命周期钩子合并
如果 mixins
和组件都有相同的生命周期钩子,它们都会执行,但 mixins
里的先执行。
const myMixin = {
created() {
console.log('Mixin created')
}
}
new Vue({
mixins: [myMixin],
created() {
console.log('Component created')
}
})
输出顺序:
Mixin created
Component created
6. 监听器 (watch
) 合并
mixins
和组件里的 watch
会合并执行:
const myMixin = {
watch: {
message(newVal) {
console.log('Mixin watch:', newVal)
}
}
}
new Vue({
mixins: [myMixin],
data() {
return { message: 'Hello' }
},
watch: {
message(newVal) {
console.log('Component watch:', newVal)
}
}
})
message
变化时,两个 watch
都会执行。
7. 作用场景
- 封装 API 请求(如
axios
请求) - 封装全局
methods
(如格式化时间、数据处理等) - 监听路由变化
- 公共的
watch
监听逻辑 - 封装生命周期钩子逻辑
封装 API 请求
const fetchDataMixin = {
data() {
return { dataList: [] }
},
methods: {
async fetchData(url) {
const res = await fetch(url)
this.dataList = await res.json()
}
}
}
在多个组件中复用
new Vue({
mixins: [fetchDataMixin],
created() {
this.fetchData('https://api.example.com/data')
}
})
8. Vue 3 替代方案
Vue 3 使用 Composition API
(setup()
+ composables
)来取代 mixins
:
import { ref, onMounted } from 'vue'
export function useFetchData(url) {
const dataList = ref([])
const fetchData = async () => {
const res = await fetch(url)
dataList.value = await res.json()
}
onMounted(fetchData)
return { dataList }
}
在组件中使用
import { useFetchData } from './useFetchData'
export default {
setup() {
const { dataList } = useFetchData('https://api.example.com/data')
return { dataList }
}
}
总结
✅ mixins
主要用于代码复用,避免多个组件重复写相同逻辑:
data
、methods
、watch
、生命周期钩子都可以混入methods
、data
遇到重名,组件优先生命周期钩子
遇到重名,mixins
先执行- 适用于封装 API 请求、监听逻辑、全局方法等
🚀 Vue 3 推荐使用 Composition API
代替 mixins
,提高可读性和灵活性!
五、插件 - Plugin
Vue 的插件 (Plugins
) 是一种全局增强 Vue 功能的方式,适用于全局方法、指令、组件、混入、状态管理等,例如 Vue Router
、Vuex
、Element UI
等都是 Vue 插件。
1. Vue 插件的基本结构
Vue 插件本质上是一个包含 install
方法的对象或函数:
const MyPlugin = {
install(Vue, options) {
// 1. 添加全局方法
Vue.prototype.$myMethod = function () {
console.log('Hello from MyPlugin')
}
// 2. 添加全局指令
Vue.directive('focus', {
inserted(el) {
el.focus()
}
})
// 3. 添加全局混入
Vue.mixin({
created() {
console.log('Mixin from MyPlugin')
}
})
// 4. 注册全局组件
Vue.component('MyComponent', {
template: '<div>我是插件注册的全局组件</div>'
})
}
}
export default MyPlugin
📌 插件可以:
- 扩展 Vue 实例(如
Vue.prototype.$xxx
) - 添加全局指令
- 注册全局组件
- 混入全局
mixin
- 提供全局配置
2. 在 Vue 2 中使用插件
在 Vue 组件或 main.js
中引入并安装插件:
import Vue from 'vue'
import MyPlugin from './MyPlugin.js'
Vue.use(MyPlugin) // 安装插件
new Vue({
el: '#app'
})
Vue.use(MyPlugin)
让所有 Vue 组件都能使用插件提供的功能。
3. Vue 2 插件的使用示例
(1)全局方法
Vue.prototype.$alertMessage = function (msg) {
alert(msg)
}
组件中使用
<template>
<button @click="$alertMessage('Hello from Plugin!')">点击我</button>
</template>
(2)全局指令
Vue.directive('focus', {
inserted(el) {
el.focus()
}
})
组件中使用
<template>
<input v-focus />
</template>
输入框会在页面加载时自动获得焦点。
(3)注册全局组件
Vue.component('MyButton', {
template: '<button>我是插件的按钮</button>'
})
组件中使用
<template>
<MyButton />
</template>
(4)全局混入 (mixin
)
Vue.mixin({
created() {
console.log('每个组件都会执行这个 mixin')
}
})
所有组件 created
时都会执行这个逻辑。
4. Vue 3 中的插件
Vue 3 的插件仍然需要 install
方法,但 Vue.prototype
替换为 app.config.globalProperties
。
(1)创建 Vue 3 插件
const MyPlugin = {
install(app, options) {
// 1. 添加全局方法
app.config.globalProperties.$sayHello = function () {
console.log('Hello from MyPlugin')
}
// 2. 添加全局指令
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// 3. 注册全局组件
app.component('MyComponent', {
template: '<div>我是 Vue 3 插件的组件</div>'
})
}
}
export default MyPlugin
(2)在 Vue 3 中使用插件
import { createApp } from 'vue'
import App from './App.vue'
import MyPlugin from './MyPlugin.js'
const app = createApp(App)
app.use(MyPlugin) // 安装插件
app.mount('#app')
5. 插件的应用场景
✅ 适用于全局功能扩展,如:
- 封装全局方法(如
axios
请求) - 封装全局指令(如
v-focus
自动聚焦) - 注册全局组件(如
MessageBox
组件) - 提供全局 mixin(如
beforeRouteEnter
逻辑) - 集成第三方库(如
Vue Router
、Vuex
、Element UI
)
6. 插件 vs 组件 vs mixins
对比项 | 插件 (Plugins ) |
组件 (Components ) |
混入 (Mixins ) |
---|---|---|---|
用途 | 扩展 Vue 全局功能 | 复用 UI 结构和逻辑 | 复用数据逻辑 |
注册方式 | Vue.use(MyPlugin) |
Vue.component('MyComp', Comp) |
mixins: [MyMixin] |
作用范围 | 影响所有组件 | 只在使用组件的地方生效 | 影响 mixins 使用的组件 |
适用场景 | 全局工具、API、指令 | 复用 UI 结构 | 复用方法、数据 |