Vue2 是一套渐进式 JavaScript 前端框架,以简洁易用、灵活高效的特点深受开发者喜爱,本手册从入门到进阶,全面覆盖 Vue2 核心知识点与实战技巧,适合零基础入门及前端开发者巩固提升。
直接在 HTML 文件中引入 Vue2 CDN 链接,无需额外配置,开箱即用。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Vue2 快速入门</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.min.js"></script>
</head>
<body>
<div id="app">{{ message }}</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue2!'
}
})
</script>
</body>
</html>
Vue CLI 是 Vue 官方提供的脚手架工具,用于快速搭建标准化 Vue 项目,内置 webpack 配置、热更新、打包优化等功能。
npm install -g @vue/cli
vue --version
vue create my-vue2-project
cd my-vue2-project
npm run serve
my-vue2-project/
├── node_modules/ # 项目依赖包(npm install 生成)
├── public/ # 静态资源根目录(不会被 webpack 编译,直接复制到打包目录)
│ ├── favicon.ico # 网站图标
│ └── index.html # 项目入口 HTML(唯一的 HTML 文件,单页应用核心)
├── src/ # 项目源代码目录(核心开发目录)
│ ├── assets/ # 静态资源目录(会被 webpack 编译处理,如图片、样式文件)
│ ├── components/ # 全局公共组件目录(可复用组件,如 Button、Card 等)
│ ├── router/ # 路由配置目录(Vue Router 相关配置)
│ ├── store/ # 状态管理目录(Vuex 相关配置)
│ ├── views/ # 页面组件目录(对应路由页面,如 Home、About 等)
│ ├── App.vue # 根组件(所有页面/组件的父组件)
│ └── main.js # 项目入口 JS 文件(创建 Vue 实例,挂载根组件)
├── package.json # 项目配置文件(依赖、脚本命令等)
├── babel.config.js # Babel 配置文件(ES6+ 转 ES5 兼容低版本浏览器)
└── README.md # 项目说明文档
Vue2 应用的核心是 Vue 实例,所有功能都围绕实例展开。
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
})
new Vue({
el: '#app',
data: {},
methods: {},
computed: {},
watch: {}
})
Vue 实例从创建到销毁的全过程称为生命周期,提供了一系列钩子函数,允许我们在不同阶段执行自定义逻辑。
示例:
<template>
<div id="app">{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue2'
}
},
beforeCreate() {
console.log('beforeCreate:', this.message) // undefined(数据未初始化)
},
created() {
console.log('created:', this.message) // Hello Vue2(数据已初始化)
// 发起异步请求
// this.fetchData()
},
mounted() {
console.log('mounted:', document.getElementById('app').innerText) // Hello Vue2(DOM 已渲染)
},
beforeDestroy() {
console.log('beforeDestroy:实例即将销毁')
// 清除定时器
// clearInterval(this.timer)
}
}
</script>
Vue2 使用基于 HTML 的模板语法,将实例数据与 DOM 绑定,无需手动操作 DOM,核心分为插值表达式和指令两类。
用于将 Vue 实例的数据渲染到 DOM 中,支持简单表达式计算。
<template>
<div>
<!-- 渲染普通数据 -->
<p>{{ name }}</p>
<!-- 支持简单表达式(算术、三元运算等,不支持语句) -->
<p>{{ age + 1 }}</p>
<p>{{ isShow ? '显示' : '隐藏' }}</p>
<!-- 渲染对象属性 -->
<p>{{ user.username }}</p>
<!-- 不渲染 HTML 标签(会原样输出) -->
<p>{{ htmlStr }}</p>
</div>
</template>
<script>
export default {
data() {
return {
name: 'Vue2 学习手册',
age: 18,
isShow: true,
user: { username: 'zhangsan' },
htmlStr: '<h1>这是一段HTML</h1>'
}
}
}
</script>
指令是带有 v- 前缀的特殊 HTML 属性,用于实现数据绑定、DOM 操作、事件绑定等功能,Vue2 核心指令如下:
关键指令示例:
<template>
<div>
<!-- v-bind 简写 -->
<a :href="link">Vue 官网</a>
<!-- v-on 简写 & 传递参数 -->
<button @click="handleBtnClick('参数1')">点击按钮</button>
<!-- v-for 遍历数组 -->
<ul>
<li v-for="(item, index) in fruitList" :key="item.id">
{{ index + 1 }} - {{ item.name }}
</li>
</ul>
<!-- v-model 双向绑定 -->
<input v-model="inputValue" placeholder="请输入内容">
<p>输入的内容:{{ inputValue }}</p>
<!-- v-show 与 v-if 区别 -->
<div v-show="isShow" style="background: red;">v-show 切换</div>
<div v-if="isShow" style="background: blue;">v-if 切换</div>
</div>
</template>
<script>
export default {
data() {
return {
link: 'https://v2.vuejs.org/',
fruitList: [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
],
inputValue: '',
isShow: true
}
},
methods: {
handleBtnClick(params) {
console.log('按钮被点击', params)
this.isShow = !this.isShow // 切换显示状态
}
}
}
</script>
<style>
/* 解决 v-cloak 闪烁问题 */
[v-cloak] {
display: none;
}
</style>
基于现有数据派生新数据,具有缓存特性:只有依赖的数据源发生变化时,才会重新计算,否则直接返回缓存结果,适合简单派生逻辑。
<template>
<div>
<p>姓:<input v-model="firstName"></p>
<p>名:<input v-model="lastName"></p>
<p>全名:{{ fullName }}</p>
<p>全名(方法):{{ getFullName() }}</p>
</div>
</template>
<script>
export default {
data() {
return {
firstName: '张',
lastName: '三'
}
},
// 计算属性
computed: {
// 只读计算属性(常用)
fullName() {
// 依赖 firstName 和 lastName,只有两者变化时,才会重新计算
return this.firstName + this.lastName
},
// 可读写计算属性(极少使用)
// fullName: {
// get() {
// return this.firstName + this.lastName
// },
// set(value) {
// this.firstName = value[0]
// this.lastName = value.slice(1)
// }
// }
},
methods: {
// 方法:每次调用都会重新执行,无缓存
getFullName() {
return this.firstName + this.lastName
}
}
}
</script>
监听 data 或 computed 数据的变化,执行异步操作或复杂业务逻辑,无缓存,数据变化即触发。
<template>
<div>
<p>输入框:<input v-model="searchValue"></p>
<p>搜索结果:{{ searchResult }}</p>
</div>
</template>
<script>
export default {
data() {
return {
searchValue: '',
searchResult: ''
}
},
// 侦听器
watch: {
// 基础监听(监听简单数据类型)
searchValue(newVal, oldVal) {
console.log('新值:', newVal, '旧值:', oldVal)
// 模拟异步搜索(如接口请求)
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.searchResult = `模拟搜索结果:${newVal}`
}, 500) // 防抖:500ms 内输入不触发,避免频繁请求
},
// 深度监听(监听对象/数组内部属性变化)
// user: {
// deep: true, // 开启深度监听
// handler(newVal, oldVal) {
// console.log('用户信息变化:', newVal)
// }
// },
// 立即执行(组件创建时立即触发一次监听)
// searchValue: {
// handler(newVal) {
// this.searchResult = `模拟搜索结果:${newVal}`
// },
// immediate: true
// }
},
beforeDestroy() {
// 清除定时器
clearTimeout(this.timer)
}
}
</script>
组件是 Vue2 的核心特性之一,用于将页面拆分为可复用、独立的功能模块,提高开发效率和代码可维护性。
- 全局组件:在 Vue 实例全局注册,所有组件 / 实例中均可使用
- 局部组件:在当前组件内部注册,仅当前组件可使用(推荐,避免命名冲突)
import Vue from 'vue'
import App from './App.vue'
import MyButton from './components/MyButton.vue'
Vue.component('MyButton', MyButton)
new Vue({
render: h => h(App)
}).$mount('#app')
<!-- 父组件 App.vue -->
<template>
<div>
<!-- 使用局部组件 -->
<MyCard></MyCard>
</div>
</template>
<script>
// 导入局部组件
import MyCard from './components/MyCard.vue'
export default {
// 注册局部组件
components: {
MyCard // 等价于 MyCard: MyCard(ES6 简写)
}
}
</script>
Vue2 工程化项目中,组件以 .vue 后缀的单文件形式存在,包含 <template>(模板)、<script>(逻辑)、<style>(样式)三部分。
<!-- components/MyButton.vue -->
<template>
<!-- 组件模板必须有且只有一个根元素 -->
<button class="my-button" @click="handleClick">
{{ buttonText }}
</button>
</template>
<script>
export default {
// 组件名称(推荐 PascalCase 命名,使用时可 kebab-case)
name: 'MyButton',
// 组件数据:必须是函数(避免多实例共享数据)
data() {
return {
buttonText: '自定义按钮'
}
},
methods: {
handleClick() {
console.log('按钮被点击')
// 触发自定义事件
this.$emit('btn-click')
}
}
}
</script>
<style scoped>
/* scoped:样式仅作用于当前组件,避免样式污染 */
.my-button {
padding: 8px 16px;
background: #42b983;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
组件间通信是 Vue2 开发的核心难点,根据组件关系(父子、兄弟、跨级),对应不同的通信方案。
props 是子组件接收父组件数据的专用属性,支持类型校验、默认值等配置。
<!-- 父组件 Parent.vue -->
<template>
<div>
<!-- 传递静态数据 -->
<MyChild title="父组件传递的静态标题"></MyChild>
<!-- 传递动态数据(v-bind 简写 :) -->
<MyChild :msg="parentMsg" :user="parentUser"></MyChild>
</div>
</template>
<script>
import MyChild from './MyChild.vue'
export default {
components: { MyChild },
data() {
return {
parentMsg: '父组件的动态消息',
parentUser: { id: 1, name: 'zhangsan' }
}
}
}
</script>
<!-- 子组件 MyChild.vue -->
<template>
<div>
<p>{{ title }}</p>
<p>{{ msg }}</p>
<p>{{ user.name }}</p>
</div>
</template>
<script>
export default {
name: 'MyChild',
// 方式1:简单数组形式(不推荐,无校验)
// props: ['title', 'msg', 'user']
// 方式2:对象形式(推荐,支持类型校验、默认值、必传项)
props: {
title: {
type: String, // 数据类型
default: '默认标题' // 默认值
},
msg: {
type: String,
required: true // 必传项
},
user: {
type: Object,
default: () => ({}) // 对象/数组默认值必须是函数
}
}
}
</script>
子组件通过 this.$emit('自定义事件名', 传递参数) 触发事件,父组件通过 @自定义事件名 监听并接收参数。
<!-- 子组件 MyChild.vue -->
<template>
<button @click="handleSendData">向父组件传递数据</button>
</template>
<script>
export default {
data() {
return {
childData: '子组件的内部数据'
}
},
methods: {
handleSendData() {
// 触发自定义事件,并传递参数
this.$emit('child-send', this.childData, '额外参数')
}
}
}
</script>
<!-- 父组件 Parent.vue -->
<template>
<div>
<MyChild @child-send="handleChildSend"></MyChild>
<p>接收子组件数据:{{ childMsg }}</p>
</div>
</template>
<script>
import MyChild from './MyChild.vue'
export default {
components: { MyChild },
data() {
return {
childMsg: ''
}
},
methods: {
// 接收子组件传递的参数
handleChildSend(data, extraData) {
console.log('额外参数:', extraData)
this.childMsg = data
}
}
}
</script>
通过创建一个空的 Vue 实例作为事件总线,实现兄弟组件间的通信,适用于小型项目。
import Vue from 'vue'
export default new Vue()
<!-- 兄弟组件 A:发送数据 -->
<template>
<button @click="sendMsgToB">向兄弟组件 B 发消息</button>
</template>
<script>
import bus from '../utils/bus'
export default {
data() {
return {
brotherAMsg: '来自组件 A 的消息'
}
},
methods: {
sendMsgToB() {
// 通过事件总线触发自定义事件
bus.$emit('brother-msg', this.brotherAMsg)
}
}
}
</script>
<!-- 兄弟组件 B:接收数据 -->
<template>
<p>接收组件 A 的消息:{{ msgFromA }}</p>
</template>
<script>
import bus from '../utils/bus'
export default {
data() {
return {
msgFromA: ''
}
},
mounted() {
// 通过事件总线监听自定义事件
this.busListener = bus.$on('brother-msg', (data) => {
this.msgFromA = data
})
},
beforeDestroy() {
// 销毁事件监听,避免内存泄漏
bus.$off('brother-msg', this.busListener)
}
}
</script>
- 方案 1:
$attrs + $listeners:适用于父子组件的跨级传递(无需手动逐层传递 props 和事件)
- 方案 2:
provide + inject:适用于深层级组件通信(父组件提供数据,子孙组件注入使用,非响应式默认,需配合 Vue.observable 实现响应式)
- 方案 3:Vuex(推荐,适用于大型项目,统一管理全局状态)
插槽用于实现组件的内容分发,允许父组件向子组件插入自定义内容,提高组件的灵活性和复用性。
子组件预留默认插槽位置,父组件可直接在子组件标签内插入内容。
<!-- 子组件 MySlot.vue -->
<template>
<div class="slot-container">
<h3>子组件标题</h3>
<!-- 默认插槽:父组件插入的内容会显示在这里 -->
<slot></slot>
</div>
</template>
<!-- 父组件使用 -->
<template>
<MySlot>
<!-- 插入自定义内容 -->
<p>这是父组件插入到默认插槽的内容</p>
</MySlot>
</template>
子组件预留多个命名插槽,父组件通过 slot 属性(Vue2.6 前)或 v-slot 指令(Vue2.6+,推荐,简写 #)指定插入的插槽位置。
<!-- 子组件 MyNamedSlot.vue -->
<template>
<div class="named-slot-container">
<header>
<!-- 具名插槽:name 为 header -->
<slot name="header"></slot>
</header>
<main>
<!-- 默认插槽(name 为 default) -->
<slot></slot>
</main>
<footer>
<!-- 具名插槽:name 为 footer -->
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件使用 -->
<template>
<MyNamedSlot>
<!-- 具名插槽:v-slot:header 简写 #header -->
<template #header>
<h1>父组件插入的头部内容</h1>
</template>
<!-- 默认插槽:无需指定名称 -->
<p>父组件插入的主体内容</p>
<!-- 具名插槽:v-slot:footer 简写 #footer -->
<template #footer>
<p>父组件插入的底部内容</p>
</template>
</MyNamedSlot>
</template>
子组件向插槽传递数据,父组件在插入内容时可接收并使用这些数据(实现子组件数据向父组件插槽内容传递)。
<!-- 子组件 MyScopeSlot.vue -->
<template>
<div class="scope-slot-container">
<h3>作用域插槽</h3>
<!-- 子组件通过属性向插槽传递数据 -->
<slot :user="user" :list="fruitList"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { id: 1, name: 'zhangsan' },
fruitList: ['苹果', '香蕉', '橙子']
}
}
}
</script>
<!-- 父组件使用 -->
<template>
<MyScopeSlot>
<!-- 接收子组件插槽传递的数据:v-slot="slotProps" -->
<template #default="slotProps">
<p>用户名:{{ slotProps.user.name }}</p>
<p>水果列表:{{ slotProps.list.join(', ') }}</p>
</template>
<!-- 解构赋值简化写法(推荐) -->
<!-- <template #default="{ user, list }">
<p>用户名:{{ user.name }}</p>
<p>水果列表:{{ list.join(', ') }}</p>
</template> -->
</MyScopeSlot>
</template>
Vue Router 是 Vue2 官方路由管理器,用于实现单页应用(SPA)的页面跳转与路由管理。
npm install vue-router@3 --save
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
<!-- App.vue(根组件) -->
<template>
<div id="app">
<!-- 路由导航:<router-link> 替代 a 标签,无刷新跳转 -->
<div class="nav">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于我们</router-link>
</div>
<!-- 路由出口:匹配的路由组件会渲染在这里 -->
<router-view></router-view>
</div>
</template>
<style>
/* 路由激活状态样式(默认类名:router-link-active) */
.router-link-active {
color: #42b983;
text-decoration: none;
font-weight: bold;
}
</style>
用于匹配动态变化的路由路径,如详情页 /user/1、/user/2。
{
path: '/user/:id',
name: 'User',
component: () => import('../views/User.vue')
}
<!-- User.vue 接收动态参数 -->
<template>
<div>
<h3>用户详情页</h3>
<p>用户ID:{{ $route.params.id }}</p>
</div>
</template>
<script>
export default {
mounted() {
// 也可通过 this.$route.params 获取动态参数
console.log('用户ID:', this.$route.params.id)
}
}
</script>
- 方式 1:
params 参数(对应动态路由,路径中显示参数,刷新页面不丢失)
<!-- 声明式导航 -->
<router-link :to="{ name: 'User', params: { id: 1 } }">用户1</router-link>
<!-- 编程式导航 -->
this.$router.push({ name: 'User', params: { id: 1 } })
- 方式 2:
query 参数(路径中显示为?key=value,无需配置动态路由,刷新页面不丢失)
<!-- 声明式导航 -->
<router-link :to="{ path: '/user', query: { id: 1 } }">用户1</router-link>
<!-- 编程式导航 -->
this.$router.push({ path: '/user', query: { id: 1 } })
<!-- 接收参数:this.$route.query.id -->
用于实现页面内的层级路由,如首页下的子菜单页面。
{
path: '/home',
name: 'Home',
component: Home,
children: [
{
path: 'news',
name: 'HomeNews',
component: () => import('../views/HomeNews.vue')
},
{
path: 'product',
name: 'HomeProduct',
component: () => import('../views/HomeProduct.vue')
}
]
}
<!-- Home.vue 中添加子路由出口 -->
<template>
<div>
<h3>首页</h3>
<!-- 子路由导航 -->
<div class="sub-nav">
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/product">产品</router-link>
</div>
<!-- 子路由出口:子路由组件渲染在这里 -->
<router-view></router-view>
</div>
</template>
路由守卫用于控制路由的跳转权限(如登录验证、页面权限控制),分为全局守卫、路由独享守卫、组件内守卫。
router.beforeEach((to, from, next) => {
console.log('全局前置守卫', to, from)
const isLogin = localStorage.getItem('isLogin')
if (to.path === '/about' && !isLogin) {
next('/login')
} else {
next()
}
})
router.afterEach((to, from) => {
console.log('全局后置守卫', to, from)
document.title = to.name || 'Vue2 路由示例'
})
Vuex 是 Vue2 官方的全局状态管理库,用于统一管理组件共享的数据(如用户信息、全局配置),实现组件间高效通信。
Vuex 采用单一状态树(Single Source of Truth),核心包含 5 个部分:
State:存储全局共享状态(类似组件的 data)
Getter:基于 State 派生的计算属性(类似组件的 computed)
Mutation:修改 State 的唯一方式(同步操作,必须通过 commit 触发)
Action:处理异步操作(如接口请求),通过 dispatch 触发,内部可 commit Mutation 修改 State
Module:将 Vuex 状态拆分模块(适用于大型项目,避免单一状态树过于庞大)
npm install vuex@3 --save
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userInfo: null,
count: 0
},
getters: {
userName: state => state.userInfo?.name || '',
doubleCount: state => state.count * 2
},
mutations: {
SET_USER_INFO(state, payload) {
state.userInfo = payload
},
INCREMENT(state, num = 1) {
state.count += num
},
DECREMENT(state) {
state.count--
}
},
actions: {
async login({ commit }, userData) {
await new Promise(resolve => setTimeout(resolve, 1000))
commit('SET_USER_INFO', userData)
return true
},
async incrementAsync({ commit }, num) {
await new Promise(resolve => setTimeout(resolve, 1000))
commit('INCREMENT', num)
}
},
modules: {
}
})
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
<template>
<div>
<p>用户名称:{{ userName }}</p>
<p>计数器:{{ count }}</p>
<p>计数器翻倍:{{ doubleCount }}</p>
</div>
</template>
<script>
export default {
// 方式1:通过 this.$store 直接访问
mounted() {
console.log('用户信息:', this.$store.state.userInfo)
console.log('计数器翻倍:', this.$store.getters.doubleCount)
},
// 方式2:通过 mapState、mapGetters 辅助函数(推荐,简化代码)
computed: {
// 映射 State
...Vuex.mapState(['count', 'userInfo']),
// 映射 Getter
...Vuex.mapGetters(['userName', 'doubleCount'])
}
}
</script>
<template>
<div>
<button @click="handleIncrement">增加计数器</button>
<button @click="handleDecrement">减少计数器</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex' // 导入辅助函数
export default {
methods: {
// 方式1:通过 this.$store.commit 触发
handleIncrement() {
this.$store.commit('INCREMENT', 2) // 传递参数
},
handleDecrement() {
this.$store.commit('DECREMENT')
},
// 方式2:通过 mapMutations 辅助函数
...mapMutations(['INCREMENT', 'DECREMENT']),
// 调用:this.INCREMENT(2)、this.DECREMENT()
}
}
</script>
<template>
<div>
<button @click="handleLogin">模拟登录</button>
<button @click="handleIncrementAsync">异步增加计数器</button>
</div>
</template>
<script>
import { mapActions } from 'vuex' // 导入辅助函数
export default {
methods: {
// 方式1:通过 this.$store.dispatch 触发
async handleLogin() {
const res = await this.$store.dispatch('login', { name: 'zhangsan', age: 18 })
if (res) {
console.log('登录成功')
}
},
async handleIncrementAsync() {
await this.$store.dispatch('incrementAsync', 3)
},
// 方式2:通过 mapActions 辅助函数
...mapActions(['login', 'incrementAsync']),
// 调用:this.login(userData)、this.incrementAsync(num)
}
}
</script>
自定义指令用于封装 DOM 操作相关的逻辑,分为全局自定义指令和局部自定义指令。
Vue.directive('focus', {
inserted(el) {
el.focus()
},
update(el) {
el.focus()
}
})
<!-- 局部自定义指令 -->
<template>
<div>
<!-- 使用自定义指令 -->
<input v-focus type="text">
<input v-color="'red'" type="text">
</div>
</template>
<script>
export default {
// 局部自定义指令
directives: {
color: {
// el:指令绑定的 DOM 元素
// binding:指令相关信息(value:指令值)
inserted(el, binding) {
el.style.color = binding.value
}
}
}
}
</script>
混入用于提取多个组件的公共逻辑,实现代码复用,分为全局混入和局部混入。
export default {
data() {
return {
mixinMsg: '这是混入的公共数据'
}
},
methods: {
mixinMethod() {
console.log('这是混入的公共方法')
}
},
mounted() {
console.log('混入的 mounted 钩子')
}
}
<!-- 组件中使用局部混入 -->
<template>
<div>{{ mixinMsg }}</div>
</template>
<script>
import commonMixin from '../mixins/commonMixin'
export default {
// 局部混入(数组形式,可多个)
mixins: [commonMixin],
mounted() {
this.mixinMethod() // 调用混入的方法
console.log('组件自身的 mounted 钩子')
}
}
</script>
过滤器用于格式化数据(如日期格式化、字符串截取),分为全局过滤器和局部过滤器,可在插值表达式或 v-bind 中使用(通过 | 管道符调用)。
Vue.filter('formatDate', (value) => {
if (!value) return ''
const date = new Date(value)
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
})
<!-- 局部过滤器 -->
<template>
<div>
<!-- 使用全局过滤器 -->
<p>格式化日期:{{ time | formatDate }}</p>
<!-- 使用局部过滤器 -->
<p>字符串大写:{{ str | toUpper }}</p>
</div>
</template>
<script>
export default {
data() {
return {
time: new Date(),
str: 'vue2'
}
},
// 局部过滤器
filters: {
toUpper(value) {
if (!value) return ''
return value.toUpperCase()
}
}
}
</script>
- 路由懒加载:减少首屏加载时间,按需加载路由组件
{
path: '/about',
name: 'About',
component: () => import( '../views/About.vue')
}
- 组件懒加载:非首屏组件按需加载
- 图片优化:使用懒加载、压缩图片、使用 CDN 加载图片
- 打包优化:去除 console.log、拆分第三方依赖(webpack 配置)
- 缓存优化:利用 localStorage/sessionStorage 缓存常用数据,减少接口请求
- 打包项目:
- 部署方式:
- 静态服务器部署(如 Nginx、Apache):将 dist 目录下的文件上传到服务器即可
- 云平台部署(如 Netlify、Vercel、阿里云 OSS):直接上传 dist 目录或关联 GitHub 仓库自动部署
data 必须是函数:组件中 data 必须返回函数,避免多实例共享数据
v-for 必须加 key:使用唯一标识(如 id)作为 key,避免 DOM 复用导致的问题
- 避免直接修改
props:子组件不能直接修改父组件传递的 props,需通过 $emit 通知父组件修改
- 避免在模板中使用复杂表达式:复杂逻辑应抽离到
computed 或 methods 中
- 及时销毁定时器 / 事件监听:在
beforeDestroy 钩子中清除定时器、取消事件监听,避免内存泄漏
Vuex 状态修改:只能通过 Mutation 修改 State,避免直接修改导致状态不可追溯