Vue 2 实例 API 与全局 API
大白话解释: Vue 2 的 API 就像"工具箱里的工具"。有些工具是全局的(Vue.component、Vue.directive),有些是实例上的(this.$refs、this.$emit、this.$nextTick)。
为什么要了解这些 API?
- 日常开发必备:
$refs获取 DOM、$emit触发事件、$nextTick等待 DOM 更新 - 排查问题:
$data查看数据、$watch监听变化、$set强制更新 - 高级用法:
Vue.extend动态创建组件(弹窗、通知)、Vue.observable创建响应式对象
常用实例 API:
$refs:获取 DOM 元素或子组件引用$emit:触发父组件监听的事件$nextTick:等待 DOM 更新后执行$set:强制设置响应式属性(Vue 2 新增属性时必须用)$delete:强制删除响应式属性
Vue 2 实例属性/方法和全局 API 的详细用法。
全局 API
Vue.extend —— 创建子类
⚠️ Vue 3 已移除。Vue 3 替代方案:
- 创建应用:
createApp(App).mount('#app')- 动态创建组件:
createVNode+render(来自@vue/runtime-dom)- 类型推导:
defineComponent()(仅用于类型辅助,不能new实例化)
基于一个组件选项对象,返回一个可复用的构造函数。
import Vue from 'vue'
// 创建构造器
const Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}}</p>',
data() {
return {
firstName: '张',
lastName: '三'
}
}
})
// 挂载到 DOM
new Profile().$mount('#app')
// 挂载到指定元素(返回组件实例)
const instance = new Profile().$mount()
document.getElementById('app').appendChild(instance.$el)动态创建组件实例(弹窗/通知)
import Vue from 'vue'
import ConfirmDialog from './ConfirmDialog.vue'
function showConfirm(message) {
const Confirm = Vue.extend(ConfirmDialog)
const instance = new Confirm({
propsData: { message } // 传入 props
}).$mount()
// 插入 DOM
document.body.appendChild(instance.$el)
// 监听事件
instance.$on('confirm', () => {
console.log('用户确认')
instance.$destroy()
instance.$el.remove()
})
instance.$on('cancel', () => {
instance.$destroy()
instance.$el.remove()
})
}
// 使用
showConfirm('确定要删除吗?')Vue.component —— 注册全局组件
⚠️ Vue 3 改为
app.component()。Vue 3 通过createApp()创建应用实例后,使用app.component('name', component)注册全局组件,不再挂载在Vue构造函数上。
// Vue 2 写法
import Vue from 'vue'
// 注册全局组件(在任何模板中都可使用)
Vue.component('my-button', {
template: '<button @click="count++">{{ count }}</button>',
data() {
return { count: 0 }
}
})
// 注册时使用 .vue 文件
Vue.component('MyButton', MyButton)💡 推荐用局部注册(components 选项),按需加载,减少打包体积。
Vue.directive —— 注册全局指令
⚠️ Vue 3 改为
app.directive()。注册方式从Vue.directive('name', options)变为app.directive('name', options)。
import Vue from 'vue'
// 注册全局自定义指令
Vue.directive('focus', {
// 元素插入 DOM 时自动聚焦
inserted(el) {
el.focus()
}
})
// 使用
// <input v-focus>完整钩子函数
⚠️ Vue 3 指令钩子已更名:
bind→beforeMount,inserted→mounted,update→updated,componentUpdated已移除,unbind→unmounted。详见 Vue 3 自定义指令文档。
// Vue 2 指令钩子(Vue 3 钩子名称见上方说明)
Vue.directive('my-directive', {
bind(el, binding, vnode) { // Vue 3: beforeMount
// 指令第一次绑定到元素时调用(只执行一次)
// el: 元素, binding: 指令信息, vnode: VNode
console.log(binding.value) // v-my-directive="value" 中的 value
console.log(binding.arg) // v-my-directive:arg 中的 arg
console.log(binding.modifiers) // v-my-directive.modifier 中的 modifiers
},
inserted(el, binding) { // Vue 3: mounted
// 被插入父节点时调用(保证父节点存在)
},
update(el, binding, vnode, oldVnode) { // Vue 3: updated
// 所在组件的 VNode 更新时调用
},
componentUpdated(el, binding, vnode, oldVnode) { // Vue 3 已移除
// 所在组件的 VNode 及其子 VNode 全部更新后调用
},
unbind(el, binding) { // Vue 3: unmounted
// 指令与元素解绑时调用(只执行一次)
}
})实用示例:v-permission 权限指令
Vue.directive('permission', {
inserted(el, binding) {
const requiredRoles = binding.value // ['admin', 'editor']
const userRole = store.state.user.role
if (!requiredRoles.includes(userRole)) {
el.parentNode && el.parentNode.removeChild(el)
}
}
})
// 使用
// <button v-permission="['admin']">删除</button>
// <button v-permission="['admin', 'editor']">编辑</button>实用示例:v-click-outside 点击外部
Vue.directive('click-outside', {
bind(el, binding) {
el._clickOutside = (e) => {
if (!el.contains(e.target)) {
binding.value(e) // 执行回调
}
}
document.addEventListener('click', el._clickOutside)
},
unbind(el) {
document.removeEventListener('click', el._clickOutside)
delete el._clickOutside
}
})
// 使用
// <div v-click-outside="closeDropdown">...</div>Vue.filter —— 注册全局过滤器
import Vue from 'vue'
// 全局过滤器
Vue.filter('dateFormat', (value, format = 'YYYY-MM-DD') => {
if (!value) return ''
return dayjs(value).format(format)
})
Vue.filter('currency', (value, symbol = '¥') => {
return `${symbol}${Number(value).toFixed(2)}`
})
// 使用(模板中)
// {{ date | dateFormat('YYYY/MM/DD') }}
// {{ price | currency }}
// {{ price | currency('$') }}// 局部过滤器
export default {
filters: {
uppercase(value) {
return value.toUpperCase()
}
}
}
// 使用:{{ text | uppercase }}⚠️ 过滤器是 Vue 2 独有,Vue 3 已移除。推荐用 computed 或 methods 替代。
Vue.mixin —— 全局混入
import Vue from 'vue'
// 全局混入:影响所有组件(慎用)
Vue.mixin({
created() {
const myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
// 所有组件都会执行这段 created 钩子⚠️ 全局混入会影响每个组件,推荐用局部混入(mixins 选项)替代。
局部混入
// mixins/logger.js
export const loggerMixin = {
created() {
console.log(`[${this.$options.name}] 已创建`)
},
methods: {
log(msg) {
console.log(`[${this.$options.name}]`, msg)
}
}
}
// 组件中使用
import { loggerMixin } from '@/mixins/logger'
export default {
name: 'UserList',
mixins: [loggerMixin],
created() {
this.log('初始化完成') // [UserList] 初始化完成
}
}Vue.use —— 安装插件
import Vue from 'vue'
import ElementUI from 'element-ui'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
// 安装插件(插件必须有 install 方法,或本身是函数)
Vue.use(ElementUI)
Vue.use(VueRouter)
Vue.use(Vuex)自定义插件
// plugins/my-plugin.js
const MyPlugin = {
install(Vue, options) {
// 1. 添加全局方法
Vue.myGlobalMethod = () => { console.log('全局方法') }
// 2. 添加全局指令
Vue.directive('my-directive', {
bind(el, binding) { /* ... */ }
})
// 3. 添加全局过滤器
Vue.filter('my-filter', (value) => value.toUpperCase())
// 4. 添加实例方法(通过原型)
Vue.prototype.$myMethod = (msg) => {
console.log(msg)
}
}
}
// 使用
Vue.use(MyPlugin, { /* 选项 */ })Vue.observable —— 创建响应式对象
⚠️ Vue 3 已移除。Vue 3 直接使用
reactive()或ref()(从vue导入)创建响应式对象:tsimport { reactive } from 'vue' const store = reactive({ count: 0, name: '张三' })
import Vue from 'vue'
// 创建跨组件共享的响应式对象(简易状态管理)
const store = Vue.observable({
count: 0,
name: '张三'
})
// 在组件中使用
export default {
computed: {
count: () => store.count,
name: () => store.name
},
methods: {
increment() {
store.count++ // 直接修改,视图自动更新
}
}
}💡 Vue 2.6+ 可用。适合小型项目替代 Vuex。
Vue.set / Vue.delete
import Vue from 'vue'
// Vue.set —— 新增响应式属性
const vm = new Vue({
data: { user: { name: '张三' } }
})
// ❌ 直接添加属性不是响应式的
vm.user.age = 25
// ✅ Vue.set 使新属性是响应式的
Vue.set(vm.user, 'age', 25)
// 或
this.$set(vm.user, 'age', 25)
// Vue.set —— 数组下标赋值
Vue.set(vm.items, 0, 'new')
// 或
this.$set(vm.items, 0, 'new')
// Vue.delete —— 删除响应式属性
Vue.delete(vm.user, 'age')
// 或
this.$delete(vm.user, 'age')Vue.nextTick
import Vue from 'vue'
// DOM 更新后执行回调
Vue.nextTick(() => {
// DOM 已更新
})
// 实例方法(更常用)
this.$nextTick(() => {
// DOM 已更新
})
// async/await 写法
await this.$nextTick()
// DOM 已更新实例属性
$data —— 响应式数据
this.$data // 返回 data 选项的对象引用
this.$data.count // 等同于 this.count$props —— 当前 Props
this.$props // 当前组件接收到的 props(只读)$el —— 根 DOM 元素
this.$el // 组件对应的 DOM 元素
this.$el.style.color = 'red'
// 在 created 中不可用(DOM 还未挂载)
// 在 mounted 中可用$refs —— DOM/组件引用
<template>
<input ref="input" />
<MyForm ref="form" />
</template>
<script>
export default {
mounted() {
// 访问 DOM 元素
this.$refs.input.focus()
// 访问子组件
this.$refs.form.validate()
}
}
</script>$parent / $children / $root
// $parent:访问父组件实例(尽量避免使用,会增加父子组件的耦合度)
this.$parent.someMethod()
this.$parent.someData
// $children:子组件实例数组(非响应式,顺序不保证,不推荐使用)
this.$children[0].someMethod()
// $root:根组件实例(整个应用最顶层的组件)
// 适合场景:小型项目中跨组件共享数据(替代 Vuex)
this.$root.someData$slots / $scopedSlots
// $slots:获取插槽内容(VNode 数组)
this.$slots // 所有插槽(包括默认插槽)
this.$slots.header // 获取 name="header" 的具名插槽内容
// $scopedSlots:获取作用域插槽(Vue 2.6+ 推荐用 v-slot 语法)
// 适合场景:封装组件时,需要在 JS 中操作插槽内容
this.$scopedSlots⚠️ Vue 3 已移除
$scopedSlots。Vue 3 统一了插槽 API,所有插槽(包括作用域插槽)都通过$slots访问,且以函数形式暴露(如$slots.default?.())。
$attrs / $listeners
// $attrs: 未被 props 声明的属性(class/style 除外)
// 用于跨组件透传
this.$attrs
// $listeners: 父组件绑定的事件监听器
// 用于跨组件透传
this.$listeners实例方法
$emit —— 触发事件
// 触发自定义事件
this.$emit('change', newValue)
this.$emit('update:value', newValue)
// 模板中监听
// <MyComp @change="handleChange" />$on / $once / $off —— 事件监听
// 监听自定义事件(配合 $emit 使用)
this.$on('event-name', (data) => {
console.log(data)
})
// 只监听一次
this.$once('event-name', (data) => {
console.log(data)
})
// 取消监听
this.$off('event-name', handler) // 取消指定事件的指定回调
this.$off('event-name') // 取消指定事件的所有回调
this.$off() // 取消所有事件的所有回调⚠️
$on/$off只能监听自己$emit触发的事件,不能跨组件通信。跨组件用 EventBus。
$watch —— 手动监听
// 监听数据变化
const unwatch = this.$watch('count', (newVal, oldVal) => {
console.log(`${oldVal} → ${newVal}`)
})
// 取消监听(调用返回的函数)
unwatch()
// 深度监听
this.$watch('user', (newVal) => {
console.log(newVal)
}, { deep: true })
// 立即执行一次
this.$watch('count', (newVal) => {
console.log(newVal)
}, { immediate: true })
// 监听表达式(函数形式)
this.$watch(
() => this.user.name,
(newVal) => { console.log(newVal) }
)$set / $delete —— 响应式操作
// $set —— 新增响应式属性
this.$set(this.user, 'age', 25)
this.$set(this.items, 0, 'new')
// $delete —— 删除响应式属性
this.$delete(this.user, 'age')$forceUpdate —— 强制更新
// 强制组件重新渲染(不常用,一般不需要)
this.$forceUpdate()
// 典型场景:修改了非响应式数据,需要手动触发更新
this.$forceUpdate()⚠️ 大多数情况下不需要。如果需要,说明数据不是响应式的,应从根源解决。
$nextTick —— DOM 更新后执行
// 修改数据后,DOM 不会立即更新
this.count = 1
console.log(this.$el.textContent) // 旧值
// $nextTick 在 DOM 更新后执行
this.count = 1
this.$nextTick(() => {
console.log(this.$el.textContent) // 新值
})
// async/await
this.count = 1
await this.$nextTick()
console.log(this.$el.textContent) // 新值$destroy —— 销毁实例
// 销毁组件实例
this.$destroy()
// 销毁后清理事件监听、子组件、watcher
// 不会移除 DOM,需要手动 this.$el.remove()⚠️ 一般不需要手动调用。v-if 切换或路由切换会自动销毁。