Vue3 常用代码指南手抄,超详细 cheatsheet
一、Vue3 基础
1.1 创建 Vue3 项目
- 使用 Vite 创建
npm create vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install
npm run dev
- 使用 Vue CLI 创建
npm install -g @vue/cli
vue create my-vue-app
1.2 项目结构
my-vue-app
├── node_modules
├── public
│ └── favicon.ico
├── src
│ ├── assets
│ ├── components
│ ├── views
│ ├── App.vue
│ ├── main.js
│ └── router
├── .gitignore
├── index.html
├── package.json
├── vite.config.js
└── README.md
1.3 main.js 配置
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'const app = createApp(App)app.use(router)
app.use(store)app.mount('#app')
二、组件
2.1 组件注册
- 全局注册
import { createApp } from 'vue'
import MyComponent from './components/MyComponent.vue'const app = createApp(App)
app.component('MyComponent', MyComponent)
app.mount('#app')
- 局部注册
<template><MyComponent />
</template><script>
import MyComponent from './components/MyComponent.vue'export default {components: {MyComponent}
}
</script>
2.2 组件通信
- 父传子: props
// 父组件
<template><ChildComponent :message="parentMessage" />
</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: {type: String,required: true}}
}
</script>
- 子传父: emit
// 子组件
<template><button @click="sendMessage">Send Message</button>
</template><script>
export default {emits: ['message'],methods: {sendMessage() {this.$emit('message', 'Hello from child')}}
}
</script>
// 父组件
<template><ChildComponent @message="handleMessage" />
</template><script>
import ChildComponent from './ChildComponent.vue'export default {components: { ChildComponent },methods: {handleMessage(message) {console.log(message)}}
}
</script>
- 兄弟组件通信: EventBus
// event-bus.js
import { EventEmitter } from 'vue'
export const EventBus = new EventEmitter()
// 组件 A
<template><button @click="sendMessage">Send Message</button>
</template><script>
import { EventBus } from './event-bus'export default {methods: {sendMessage() {EventBus.emit('message', 'Hello from component A')}}
}
</script>
// 组件 B
<template><div>{{ message }}</div>
</template><script>
import { onMounted, onUnmounted } from 'vue'
import { EventBus } from './event-bus'export default {data() {return {message: ''}},setup() {const handleMessage = (msg) => {message.value = msg}onMounted(() => {EventBus.on('message', handleMessage)})onUnmounted(() => {EventBus.off('message', handleMessage)})}
}
</script>
2.3 插槽
- 默认插槽
// 子组件
<template><slot></slot>
</template>
// 父组件
<ChildComponent><p>默认插槽内容</p>
</ChildComponent>
- 具名插槽
// 子组件
<template><slot name="header"></slot><slot></slot><slot name="footer"></slot>
</template>
// 父组件
<ChildComponent><template #header><h1>Header</h1></template><p>默认插槽内容</p><template #footer><p>Footer</p></template>
</ChildComponent>
- 作用域插槽
// 子组件
<template><slot :user="user"></slot>
</template><script>
export default {data() {return {user: { name: 'Alice', age: 25 }}}
}
</script>
// 父组件
<ChildComponent v-slot:default="slotProps"><p>{{ slotProps.user.name }}</p>
</ChildComponent>
三、Vue Router
3.1 基本配置
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'const routes = [{ path: '/', name: 'Home', component: Home },{ path: '/about', name: 'About', component: About }
]const router = createRouter({history: createWebHistory(),routes
})export default router
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'const app = createApp(App)
app.use(router)
app.mount('#app')
3.2 导航守卫
- 全局守卫
router.beforeEach((to, from, next) => {console.log('全局前置守卫')next()
})
- 路由独享守卫
const routes = [{path: '/',component: Home,beforeEnter: (to, from, next) => {console.log('路由独享守卫')next()}}
]
3.3 动态路由
- 定义动态路由
const routes = [{ path: '/user/:id', name: 'User', component: User },
]
- 获取路由参数
<template><div>User ID: {{ id }}</div>
</template><script>
import { useRoute } from 'vue-router'export default {setup() {const route = useRoute()return {id: route.params.id}}
}
</script>
- 路由跳转
<template><router-link to="/user/123">Go to User 123</router-link>
</template>
<script>
import { useRouter } from 'vue-router'export default {setup() {const router = useRouter()const goToUser = () => {router.push({ name: 'User', params: { id: 123 } })}return { goToUser }}
}
</script>
3.4 嵌套路由
const routes = [{path: '/dashboard',component: Dashboard,children: [{path: 'profile',component: Profile},{path: 'settings',component: Settings}]}
]
<!-- Dashboard.vue -->
<template><div><h1>Dashboard</h1><router-view></router-view></div>
</template>
3.5 路由懒加载
const Home = () => import('../views/Home.vue')
const routes = [{ path: '/', name: 'Home', component: Home },
]
四、Vuex
4.1 基本配置
// store/index.js
import { createStore } from 'vuex'export default createStore({state: {count: 0},mutations: {increment(state) {state.count++}},actions: {increment({ commit }) {commit('increment')}},getters: {doubleCount(state) {return state.count * 2}}
})
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'const app = createApp(App)
app.use(store)
app.mount('#app')
4.2 使用 Vuex
- 在组件中获取 state
<template><div>Count: {{ count }}</div>
</template><script>
import { mapState } from 'vuex'export default {computed: {...mapState(['count'])}
}
</script>
- 在组件中提交 mutation
<template><button @click="increment">Increment</button>
</template><script>
import { mapMutations } from 'vuex'export default {methods: {...mapMutations(['increment'])}
}
</script>
- 在组件中分发 action
<template><button @click="increment">Increment</button>
</template><script>
import { mapActions } from 'vuex'export default {methods: {...mapActions(['increment'])}
}
</script>
- 使用 getters
<template><div>Double Count: {{ doubleCount }}</div>
</template><script>
import { mapGetters } from 'vuex'export default {computed: {...mapGetters(['doubleCount'])}
}
</script>
4.3 模块化
// store/modules/user.js
export default {namespaced: true,state: {username: ''},mutations: {setUsername(state, username) {state.username = username}},actions: {setUsername({ commit }, username) {commit('setUsername', username)}},getters: {username: (state) => state.username}
}
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'export default createStore({modules: {user}
})
// 使用模块中的 state, mutation, action, getter
<template><div>Username: {{ username }}</div><button @click="setUsername('Alice')">Set Username</button>
</template><script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'export default {computed: {...mapState('user', ['username']),...mapGetters('user', ['username'])},methods: {...mapMutations('user', ['setUsername']),...mapActions('user', ['setUsername'])}
}
</script>
五、组合式 API
5.1 setup 函数
<template><div>{{ count }}</div><button @click="increment">Increment</button>
</template><script>
import { ref } from 'vue'export default {setup() {const count = ref(0)const increment = () => {count.value++}return {count,increment}}
}
</script>
5.2 响应式数据
- ref
const count = ref(0)
- reactive
const state = reactive({count: 0,user: {name: 'Alice'}
})
- computed
const doubleCount = computed(() => count.value * 2)
- watch
watch(count, (newVal, oldVal) => {console.log(`count changed from ${oldVal} to ${newVal}`)
})
5.3 生命周期钩子
- onMounted
onMounted(() => {console.log('mounted')
})
- onUnmounted
onUnmounted(() => {console.log('unmounted')
})
- 其他钩子: onBeforeMount, onBeforeUnmount, onActivated, onDeactivated, onErrorCaptured, onRenderTracked, onRenderTriggered
六、其他常用功能
6.1 指令
- v-bind
<img v-bind:src="imageSrc" />
- v-model
<input v-model="message" />
- v-if, v-else-if, v-else
<div v-if="isLoggedIn">Welcome!</div>
<div v-else>Please log in.</div>
- v-for
<ul><li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
-
v-on 修饰符
-
.stop
:阻止事件冒泡<button @click.stop="handleClick">Click</button>
-
.prevent
:阻止默认事件<form @submit.prevent="handleSubmit"></form>
-
.once
:事件只触发一次<button @click.once="handleClick">Click</button>
-
.self
:只有事件是从元素本身触发时才触发处理函数<div @click.self="handleClick"><button>Click</button> </div>
-
按键修饰符
<input @keyup.enter="submit" />
-
-
v-for 进阶用法
-
遍历对象
<ul><li v-for="(value, key, index) in object" :key="key">{{ index }} - {{ key }}: {{ value }}</li> </ul>
-
遍历数字范围
<div v-for="n in 10" :key="n">{{ n }}</div>
-
使用
v-for
和template
结合<ul><template v-for="item in items" :key="item.id"><li>{{ item.name }}</li><li class="divider" role="presentation"></li></template> </ul>
-
-
v-slot 插槽
-
具名插槽
<!-- 子组件 --> <slot name="header"></slot> <slot></slot> <slot name="footer"></slot>
<!-- 父组件 --> <ChildComponent><template #header><h1>Header</h1></template><p>默认插槽内容</p><template #footer><p>Footer</p></template> </ChildComponent>
-
作用域插槽
<!-- 子组件 --> <slot :user="user"></slot>
<!-- 父组件 --> <ChildComponent v-slot:default="slotProps"><p>{{ slotProps.user.name }}</p> </ChildComponent>
-
6.2 过渡与动画
-
单元素过渡
<transition name="fade"><div v-if="isVisible">Hello</div> </transition>
.fade-enter-active, .fade-leave-active {transition: opacity 0.5s; } .fade-enter-from, .fade-leave-to {opacity: 0; }
-
列表过渡
<transition-group name="list" tag="ul"><li v-for="item in items" :key="item.id">{{ item.name }}</li> </transition-group>
.list-enter-active, .list-leave-active {transition: all 0.5s; } .list-enter-from, .list-leave-to {opacity: 0;transform: translateY(30px); }
-
使用 JavaScript 钩子
<transition @before-enter="beforeEnter" @enter="enter" @leave="leave"><div v-if="isVisible">Hello</div> </transition>
export default {methods: {beforeEnter(el) {el.style.opacity = 0},enter(el, done) {Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300, complete: done })},leave(el, done) {Velocity(el, { opacity: 0, fontSize: '1em' }, { duration: 300, complete: done })}} }
6.3 混入(Mixin)
-
定义混入
// mixins/log.js export default {created() {console.log('Mixin created')},methods: {log(message) {console.log(message)}} }
-
使用混入
<script> import logMixin from './mixins/log'export default {mixins: [logMixin],created() {this.log('Hello from component')} } </script>
6.4 自定义指令
-
定义自定义指令
// directives/focus.js export default {mounted(el) {el.focus()} }
-
注册全局指令
import { createApp } from 'vue' import App from './App.vue' import focusDirective from './directives/focus'const app = createApp(App) app.directive('focus', focusDirective) app.mount('#app')
-
使用自定义指令
<input v-focus />
6.5 插件
-
创建插件
// plugins/logger.js export default {install(app, options) {console.log(options)app.config.globalProperties.$logger = (message) => {console.log(message)}} }
-
使用插件
// main.js import { createApp } from 'vue' import App from './App.vue' import loggerPlugin from './plugins/logger'const app = createApp(App) app.use(loggerPlugin, { someOption: 'someValue' }) app.mount('#app')
// 在组件中使用 export default {mounted() {this.$logger('Hello from plugin')} }
七、Vue3 新特性
7.1 Teleport
-
使用 Teleport 将组件内容渲染到指定位置
<!-- App.vue --> <div><h1>App</h1><Teleport to="body"><div id="modal"><p>Modal Content</p></div></Teleport> </div>
7.2 Suspense
-
使用 Suspense 处理异步组件
<template><Suspense><template #default><AsyncComponent /></template><template #fallback><div>Loading...</div></template></Suspense> </template>
-
异步组件示例
// components/AsyncComponent.vue <template><div>Async Component</div> </template><script> export default {async setup() {// 模拟异步操作await new Promise((resolve) => setTimeout(resolve, 2000))return {}} } </script>
<!-- 使用 Suspense 包裹异步组件 --> <template><Suspense><template #default><AsyncComponent /></template><template #fallback><div>Loading...</div></template></Suspense> </template>
-
错误处理
<template><Suspense><template #default><AsyncComponent /></template><template #fallback><div>Loading...</div></template><template #error><div>Error occurred!</div></template></Suspense> </template>
// components/AsyncComponent.vue <script> export default {async setup() {await new Promise((_, reject) => setTimeout(() => reject(new Error('Async Error')), 2000))} } </script>
7.3 Fragment
-
Vue3 支持组件返回多个根节点,无需再使用包裹元素
<template><header>Header</header><main>Main Content</main><footer>Footer</footer> </template>
7.4 组合式 API 进阶
-
响应式 API
-
reactive
:创建响应式对象import { reactive } from 'vue'const state = reactive({count: 0,user: {name: 'Alice'} })
-
ref
:创建响应式引用import { ref } from 'vue'const count = ref(0)
-
computed
:创建计算属性import { ref, computed } from 'vue'const count = ref(0) const doubleCount = computed(() => count.value * 2)
-
watch
:监听数据变化import { ref, watch } from 'vue'const count = ref(0) watch(count, (newVal, oldVal) => {console.log(`count changed from ${oldVal} to ${newVal}`) })
-
-
生命周期钩子
-
onMounted
:组件挂载完成后调用import { onMounted } from 'vue'onMounted(() => {console.log('mounted') })
-
onUnmounted
:组件卸载后调用import { onUnmounted } from 'vue'onUnmounted(() => {console.log('unmounted') })
-
其他钩子:
onBeforeMount
,onBeforeUnmount
,onActivated
,onDeactivated
,onErrorCaptured
,onRenderTracked
,onRenderTriggered
-
-
依赖注入
-
provide
和inject
// 父组件 import { provide, ref } from 'vue'const theme = ref('dark') provide('theme', theme)
// 子组件 import { inject, onMounted } from 'vue'const theme = inject('theme') onMounted(() => {console.log(`Theme is ${theme.value}`) })
-
-
自定义 Hooks
-
创建可复用的组合式函数
// hooks/useMousePosition.js import { ref, onMounted, onUnmounted } from 'vue'export function useMousePosition() {const x = ref(0)const y = ref(0)const updatePosition = (e) => {x.value = e.pageXy.value = e.pageY}onMounted(() => {window.addEventListener('mousemove', updatePosition)})onUnmounted(() => {window.removeEventListener('mousemove', updatePosition)})return { x, y } }
// 使用自定义 Hook import { useMousePosition } from '../hooks/useMousePosition'export default {setup() {const { x, y } = useMousePosition()return { x, y }} }
-
八、性能优化
8.1 组件懒加载
-
使用动态导入实现组件懒加载
const Home = () => import('./views/Home.vue') const routes = [{ path: '/', component: Home } ]
8.2 避免不必要的渲染
-
使用
v-once
渲染静态内容<div v-once>{{ message }}</div>
-
使用
shallowRef
和shallowReactive
避免深层响应式import { shallowRef } from 'vue'const data = shallowRef({ count: 0 })