Vue2 API 怎么学更高效?核心用法、实战场景全解析
想学透Vue2 API 却一头雾水?看着文档里一堆选项、方法、指令,不知道从哪入手?其实把核心API 按场景拆分,结合实际项目需求去理解,学起来就顺多了,这篇文章用问答形式,把Vue2 里最常用、最容易迷糊的API 知识点掰碎了讲,从基础选项到组件通信,从生命周期到插件生态,帮你搭起知识框架~
Vue2 核心选项式API 有哪些必掌握的?
Vue2 用「选项式API(Options API)」组织代码,把 data
、methods
、computed
这些「选项」整合到Vue 实例或组件中,这些基础选项是玩转Vue2 的地基,得先搞清楚每个选项的作用和配合逻辑。
data
:存储响应式数据的容器,组件里的data
必须是函数(避免组件复用导致数据互相影响),返回一个对象;根实例的data
可以是对象,比如做个计数器组件,data() { return { count: 0 } }
,页面上用{{ count }}
就能显示数据,点击按钮修改count
会触发界面更新。methods
:存放事件处理、业务逻辑等方法,要注意 this 指向——如果用箭头函数,this
会指向外层作用域(而非Vue 实例),methods
里的方法得用普通函数。methods: { increment() { this.count++ } }
,按钮绑定@click="increment"
就能触发计数。computed
:计算属性,依赖其他响应式数据且自带缓存,适合处理「依赖其他数据的复杂计算」或「数据格式化」场景,比如购物车页面,商品单价(price
)和数量(quantity
)存在data
里,用computed
写总价:totalPrice() { return this.price * this.quantity }
,只要price
和quantity
不变,totalPrice
就不会重复计算,性能比直接用methods
更高(methods
每次渲染都会执行)。watch
:数据观察者,专门监听数据变化后执行逻辑,适合做「异步操作」「复杂副作用(如弹窗、请求)」,比如用户输入搜索关键词(data
里的keyword
),每次变化都要发请求查数据,就可以用watch
:watch: { keyword(newVal) { this.fetchData(newVal) } }
,还能配置deep: true
(深度监听对象内部变化)和immediate: true
(页面加载时立即执行一次监听逻辑)。props
:负责父子组件通信,父组件给子组件传递数据,子组件用props: ['title']
声明接收,父组件用<Child title="我是标题" />
传递,注意props
是单向数据流,子组件不能直接修改props
的值,需通过$emit
通知父组件修改。components
:注册局部组件,比如在父组件里导入子组件后,通过components: { ChildComponent }
注册,页面上就能用<ChildComponent />
调用。
data
与 computed
、watch
怎么选?
很多刚学Vue2 的同学会迷糊:同样处理数据,data
、computed
、watch
有啥区别?何时用哪个?其实它们的定位完全不同,得结合场景判断。
data
是「数据容器」,存储页面渲染、逻辑处理所需的原始响应式数据(比如用户输入内容、列表数据、开关状态),它是“原材料”,其他选项基于它加工。computed
是「衍生数据工厂」,必须依赖其他响应式数据(如data
里的内容),且自带缓存,适合两种场景:- 复杂计算:如购物车总价(依赖商品数量和单价),用
computed
写一次,依赖不变就不重复计算; - 数据格式化:如把时间戳转成「年-月-日」,或过滤数组后返回结果。
但注意:computed
不能做异步操作,也不能直接修改其他数据(它是“计算”,不是“操作”)。
- 复杂计算:如购物车总价(依赖商品数量和单价),用
watch
是「数据变化的响应者」,适合做副作用操作(如数据变化后发请求、弹提示、操作DOM),比如用户修改个人信息(data
里的userInfo
),每次修改后调用接口保存,就可以用watch
监听userInfo
,变化时发请求。watch
支持异步操作(如等接口返回后再处理),还能通过deep: true
深度监听对象内部变化。
组件通信API 怎么灵活运用?
Vue2 里组件嵌套层级深了,通信就成难题,但掌握几种核心方式,再根据场景选择,就能轻松应对。
父子通信:props
+ $emit
最基础也最常用!父组件通过 props
给子组件传数据,子组件通过 $emit
给父组件传事件。
- 父传子:父组件写
<Child :msg="parentMsg" />
,子组件用props: ['msg']
声明,即可通过this.msg
拿到数据。 - 子传父:子组件触发事件
this.$emit('handleClick', 123)
,父组件写<Child @handleClick="parentMethod" />
,parentMethod
就能收到子组件传的123
。
场景:导航栏子组件,父组件传菜单数据(props
),子组件点击菜单项通知父组件跳转($emit
)。
父调子:$refs
父组件想主动调用子组件的方法或获取数据,用 $refs
,给子组件加 ref
属性:<Child ref="childRef" />
,然后父组件通过 this.$refs.childRef
拿到子组件实例,调用其方法(如 this.$refs.childRef.openDialog()
)。
场景:弹窗组件,父组件需要控制弹窗显示/隐藏,子组件暴露 open
、close
方法,父组件用 $refs
调用。
祖孙通信:provide
+ inject
若组件层级很深(如祖父→父→子→孙),用 props
逐层传递太繁琐,provide
+ inject
更高效,祖父组件通过 provide: { userInfo: this.userInfo }
提供数据,孙子组件通过 inject: ['userInfo']
直接获取祖父的 userInfo
。
注意:Vue2 中 provide/inject
默认非响应式(传值不是响应式,除非用函数返回对象),若需响应式,可结合 Vue.observable
或Vuex。
场景:全局主题配置、用户登录信息等跨多层级的共享数据。
全局通信:Vuex
多组件共享的复杂状态(如购物车数据、用户权限、全局loading),用 Vuex 更合适,核心API 包括:
state
:存储状态(如state.cartList
);getters
:派生状态(如getters.cartTotalPrice
,类似computed
);mutations
:同步修改state
的唯一方式(如mutations.ADD_TO_CART
);actions
:处理异步操作(如发请求),再提交mutation
(如actions.fetchCartList
);modules
:拆分模块(如用户模块、商品模块),避免状态臃肿。
场景:用户登录后,将用户信息存入Vuex 的 state
,所有组件均可访问;购物车添加商品时,通过 dispatch action
、commit mutation
更新 cartList
。
指令类API(如 v-bind
、v-model
)底层逻辑是啥?
Vue2 的指令分内置指令(如 v-bind
、v-model
、v-for
)和自定义指令,先理解内置指令的作用与底层,再扩展自定义指令,思路会更清晰。
内置指令核心逻辑
v-bind
(缩写 ):动态绑定属性,如<img :src="imgUrl" />
,将data
里的imgUrl
绑定到src
属性,底层是Vue 在编译阶段将指令转换为DOM 属性更新,当imgUrl
变化时,自动更新DOM 的src
。v-model
:语法糖,本质是v-bind:value + v-on:input
(针对输入框),如<input v-model="username" />
等价于<input :value="username" @input="username = $event.target.value" />
,对于复选框、单选框,v-model
绑定的是checked
属性和change
事件,底层会根据表单元素类型自动适配。v-for
:列表渲染,必须加key
(唯一标识),Vue 的diff 算法靠key
识别变化的元素,避免重复渲染,如<ul><li v-for="(item, index) in list" :key="item.id">{{ item.name }}</li></ul>
,key
用item.id
(唯一)比index
更可靠(列表增删时index
会变化,导致diff 出错)。v-if
vsv-show
:v-if
是“真正的销毁/创建DOM”,切换时触发生命周期;v-show
是“display: none/block
切换”,初始渲染时v-show
的DOM 已存在,频繁切换用v-show
(性能高),切换少用v-if
(节省渲染资源)。
自定义指令怎么玩?
若内置指令满足不了需求(如权限控制、自动聚焦、拖拽),可自定义指令,分全局和局部注册:
- 全局注册:
Vue.directive('focus', { inserted(el) { el.focus() } })
,页面用<input v-focus />
即可自动聚焦。 - 局部注册:组件内写
directives: { focus: { inserted(el) { ... } } }
。
自定义指令有钩子函数:bind
(指令绑定到元素时)、inserted
(元素插入DOM 时)、update
(组件更新时,不保证子组件更新完成)、componentUpdated
(组件和子组件均更新完成)、unbind
(指令解绑时)。
举个实用例子:做一个 v-permission
指令,根据用户权限控制元素显示,逻辑:bind
钩子中判断用户权限,无权限则移除元素:
Vue.directive('permission', { bind(el, binding) { const permission = binding.value; // 指令传的值,如v-permission="'admin'" const userRoles = store.state.user.roles; // 假设从Vuex取用户权限 if (!userRoles.includes(permission)) { el.parentNode && el.parentNode.removeChild(el); // 移除元素 } } });
页面用 <button v-permission="'admin'">删除</button>
,仅admin 权限用户能看到该按钮。
Vue2 生命周期钩子怎么结合业务场景用?
Vue2 的生命周期钩子,是组件从创建到销毁过程中触发的函数,掌握每个阶段的作用,才能在合适时机写代码。
生命周期阶段拆解
-
beforeCreate
:实例刚初始化,data
、methods
未挂载,无法访问数据和方法,基本用不上。 -
created
:实例创建完成,data
和methods
已初始化,可访问数据和方法,但DOM 未生成,适合发异步请求(如页面加载时获取列表数据),因为数据赋值后,后续DOM 渲染能直接使用。 -
beforeMount
:模板编译完成,即将挂载到DOM,仍无法访问真实DOM,用得少。 -
mounted
:DOM 已挂载完成,可访问真实DOM 元素,适合操作DOM(如初始化echarts 图表、绑定第三方库事件),例如做图表组件,mounted
里初始化echarts:mounted() { this.chart = echarts.init(this.$refs.chartDom); this.chart.setOption(this.chartOption); }
-
beforeUpdate
:数据变化后、DOM 更新前触发,可在此阻止不必要的更新(一般不用)。 -
updated
:DOM 更新完成后触发,适合数据变化后更新DOM 相关操作(如重新计算图表尺寸)。 -
beforeDestroy
:实例销毁前触发,此时实例仍可用,适合清除定时器、解绑事件、销毁第三方实例,防止内存泄漏,例如定时器场景:created() { this.timer = setInterval(() => { console.log('刷新数据'); }, 5000); }, beforeDestroy() { clearInterval(this.timer); }
-
destroyed
:实例销毁后触发,组件所有资源已解绑,用得少。
业务场景举例
- 实时刷新组件:
created
里启动定时器拉取数据,beforeDestroy
里清除定时器,避免组件销毁后定时器仍运行。 - 弹窗组件:
mounted
里给document 绑定点击事件(关闭弹窗逻辑),beforeDestroy
里解绑事件,防止影响其他弹窗。 - 懒加载图片组件:
mounted
里判断图片是否进入视口,进入后加载真实图片;beforeDestroy
里取消监听滚动事件。
自定义指令和过滤器还值得学吗?
Vue3 中过滤器被移除,自定义指令语法也有变化,但Vue2 项目仍大量使用这两个功能,学透它们,对维护老项目、理解Vue 设计思路很有帮助。
自定义指令:扩展DOM 能力
自定义指令能实现权限控制、自动聚焦、拖拽、水印等操作,例如做一个 v-drag
指令,让元素可拖拽:
Vue.directive('drag', { inserted(el) { el.style.position = 'absolute'; let startX, startY, startLeft, startTop; el.addEventListener('mousedown', (e) => { startX = e.pageX; startY = e.pageY; startLeft = el.offset
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。