核心API
创建应用实例
javascriptCopyimport { createApp } from 'vue' import App from './App.vue' const app = createApp(App) app.mount('#app')
应用配置
javascriptCopy// 全局注册组件 app.component('my-component', { /* ... */ }) // 全局注册指令 app.directive('focus', { mounted(el) { el.focus() } }) // 全局注册插件 app.use(router) // 配置全局属性 app.config.globalProperties.$http = axios // 错误处理 app.config.errorHandler = (err) => { console.error(err) } // 性能追踪 app.config.performance = true
组合式API (Composition API)
setup函数
javascriptCopyimport { ref, onMounted } from 'vue' export default { setup() { const count = ref(0) function increment() { count.value++ } onMounted(() => { console.log('组件已挂载') }) return { count, increment } } }
响应式API
javascriptCopyimport { ref, reactive, computed, readonly, watchEffect, watch } from 'vue' // ref: 创建单值响应式引用 const count = ref(0) console.log(count.value) // 0 count.value++ // reactive: 创建响应式对象 const state = reactive({ count: 0, name: 'Vue' }) state.count++ // computed: 计算属性 const doubleCount = computed(() => count.value * 2) // readonly: 创建只读代理 const readonlyState = readonly(state) // watchEffect: 自动跟踪依赖并执行 watchEffect(() => { console.log(`Count is: ${count.value}`) }) // watch: 显式监听数据源变化 watch(count, (newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`) })
生命周期钩子
javascriptCopyimport { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured } from 'vue' export default { setup() { onBeforeMount(() => console.log('beforeMount')) onMounted(() => console.log('mounted')) onBeforeUpdate(() => console.log('beforeUpdate')) onUpdated(() => console.log('updated')) onBeforeUnmount(() => console.log('beforeUnmount')) onUnmounted(() => console.log('unmounted')) onActivated(() => console.log('activated')) onDeactivated(() => console.log('deactivated')) onErrorCaptured((err) => { console.error(err) return true }) } }
依赖注入
javascriptCopy// 在父组件中提供 import { provide, ref } from 'vue' export default { setup() { const theme = ref('dark') provide('theme', theme) } } // 在子组件中注入 import { inject } from 'vue' export default { setup() { const theme = inject('theme', 'light') // 第二个参数是默认值 return { theme } } }
组合式工具 (Composition Utilities)
toRef 和 toRefs
javascriptCopyimport { reactive, toRef, toRefs } from 'vue' const state = reactive({ foo: 1, bar: 2 }) // 创建一个ref,引用state.foo const fooRef = toRef(state, 'foo') // 将响应式对象转换为ref对象的集合 const { foo, bar } = toRefs(state)
isRef, isReactive, isReadonly, isProxy
javascriptCopyimport { ref, reactive, readonly, isRef, isReactive, isReadonly, isProxy } from 'vue' const refVal = ref(0) console.log(isRef(refVal)) // true const reactiveObj = reactive({}) console.log(isReactive(reactiveObj)) // true const readonlyObj = readonly({}) console.log(isReadonly(readonlyObj)) // true console.log(isProxy(reactiveObj)) // true (reactive对象是proxy) console.log(isProxy(readonlyObj)) // true (readonly对象也是proxy)
unref, toRaw, markRaw, shallowRef, triggerRef, customRef
javascriptCopyimport { ref, unref, reactive, toRaw, markRaw, shallowRef, triggerRef, customRef } from 'vue' // unref: 如果是ref则返回内部值,否则原样返回 const value = ref(0) console.log(unref(value)) // 0 // toRaw: 获取响应式对象的原始对象 const original = {} const reactive = reactive(original) console.log(toRaw(reactive) === original) // true // markRaw: 标记一个对象永远不会转为响应式 const obj = markRaw({}) const reactiveObj = reactive({ nested: obj }) console.log(isReactive(reactiveObj.nested)) // false // shallowRef: 创建一个浅层响应式ref const shallow = shallowRef({ count: 1 }) // 修改.value不会触发更新 shallow.value.count = 2 // 必须替换整个value才会触发 shallow.value = { count: 3 } // triggerRef: 手动触发shallowRef的更新 shallow.value.count = 4 triggerRef(shallow) // 手动触发更新 // customRef: 创建自定义ref function useDebouncedRef(value, delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) } const debouncedText = useDebouncedRef('hello')
shallowReactive, shallowReadonly
javascriptCopyimport { shallowReactive, shallowReadonly } from 'vue' // 只有根级属性是响应式的 const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) // 只有根级属性是只读的 const shallowState = shallowReadonly({ foo: 1, nested: { bar: 2 } })
effectScope
javascriptCopyimport { effectScope, ref, watch } from 'vue' // 创建一个作用域来捕获其中的响应式副作用 const scope = effectScope() scope.run(() => { const counter = ref(0) // 这个watch会被scope收集 watch(counter, (count) => { console.log(`Count: ${count}`) }) // 增加counter counter.value++ }) // 停止作用域内的所有副作用 scope.stop()
模板指令
v-bind
htmlCopy<!-- 绑定attribute --> <img v-bind:src="imageSrc"> <!-- 简写 --> <img :src="imageSrc"> <!-- 动态attribute名 --> <button :[buttonAttr]="value"></button> <!-- 绑定多个值 --> <div v-bind="{ id: 'container', class: 'wrapper' }"></div>
v-on
htmlCopy<!-- 方法处理器 --> <button v-on:click="doThis"></button> <!-- 简写 --> <button @click="doThis"></button> <!-- 动态事件 --> <button @[event]="doThis"></button> <!-- 内联语句 --> <button @click="count++"></button> <!-- 修饰符 --> <form @submit.prevent="onSubmit"></form> <!-- 键盘修饰符 --> <input @keyup.enter="onEnter">
v-model
htmlCopy<!-- 基本用法 --> <input v-model="message"> <!-- 双向绑定一个值 --> <input v-model.trim="message"> <!-- 修饰符 --> <input v-model.lazy="message"> <!-- 在change事件之后同步 --> <input v-model.number="age"> <!-- 自动转换为数字 --> <input v-model.trim="message"> <!-- 自动去除首尾空格 --> <!-- 在组件上使用 --> <custom-input v-model="searchText"></custom-input> <!-- 在组件上使用v-model (等效于下面) --> <custom-input :modelValue="searchText" @update:modelValue="newValue => searchText = newValue"> </custom-input>
v-if, v-else, v-else-if
htmlCopy<div v-if="type === 'A'">A</div> <div v-else-if="type === 'B'">B</div> <div v-else-if="type === 'C'">C</div> <div v-else>Not A/B/C</div>
v-show
html Copy <div v-show="isVisible">Hello</div>
v-for
htmlCopy<div v-for="item in items" :key="item.id"> {{ item.text }} </div> <div v-for="(item, index) in items" :key="item.id"> {{ index }} - {{ item.text }} </div> <div v-for="(value, key) in object" :key="key"> {{ key }}: {{ value }} </div> <div v-for="n in 10" :key="n">{{ n }}</div>
v-slot
htmlCopy<!-- 默认插槽 --> <base-layout> <template v-slot:default> <p>内容放这里</p> </template> </base-layout> <!-- 具名插槽 --> <base-layout> <template v-slot:header> <h1>标题</h1> </template> <template v-slot:default> <p>内容</p> </template> <template v-slot:footer> <p>页脚</p> </template> </base-layout> <!-- 插槽简写 (#) --> <base-layout> <template #header> <h1>标题</h1> </template> </base-layout> <!-- 作用域插槽 --> <todo-list> <template v-slot:item="{ item }"> <span>{{ item.text }}</span> </template> </todo-list>
v-pre, v-once, v-cloak, v-memo
htmlCopy<!-- v-pre: 跳过编译 --> <span v-pre>{{ 这不会被编译 }}</span> <!-- v-once: 只渲染一次 --> <span v-once>{{ msg }}</span> <!-- v-cloak: 隐藏未编译的模板 --> <div v-cloak>{{ msg }}</div> <!-- v-memo: 记忆化一块模板 --> <div v-for="item in list" :key="item.id" v-memo="[item.id === selected]"> <p>{{ item.name }}</p> </div>
v-text, v-html
htmlCopy<span v-text="msg"></span> <!-- 等同于 --> <span>{{ msg }}</span> <div v-html="rawHtml"></div>
使用 <script setup>
htmlCopy<script setup> import { ref, computed, onMounted } from 'vue' import MyComponent from './MyComponent.vue' // 这里声明的变量、函数都会自动暴露给模板 const count = ref(0) const double = computed(() => count.value * 2) function increment() { count.value++ } onMounted(() => { console.log('mounted!') }) // 导入的组件自动注册 </script> <template> <button @click="increment">Count: {{ count }}</button> <p>Double: {{ double }}</p> <MyComponent /> </template>
defineProps, defineEmits, defineExpose
htmlCopy<script setup> // 声明props const props = defineProps({ title: String, likes: { type: Number, default: 0 } }) // 使用类型声明 // const props = defineProps<{ // title: string // likes?: number // }>() // 声明事件 const emit = defineEmits(['update', 'delete']) // 使用类型声明 // const emit = defineEmits<{ // (e: 'update', id: number): void // (e: 'delete', id: number): void // }>() function submitForm() { emit('update', 1) } // 显式暴露组件内部属性给父组件 defineExpose({ submitForm }) </script>
内置组件
Teleport
htmlCopy<teleport to="body"> <div class="modal"> <!-- 模态框内容 --> </div> </teleport>
Suspense
htmlCopy<suspense> <!-- 异步组件 --> <template #default> <async-component></async-component> </template> <!-- 加载状态 --> <template #fallback> <div>Loading...</div> </template> </suspense>
Transition 和 TransitionGroup
htmlCopy<!-- 单个元素的过渡 --> <transition name="fade"> <div v-if="visible">Hello</div> </transition> <style> .fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; } </style> <!-- 列表过渡 --> <transition-group name="list" tag="ul"> <li v-for="item in items" :key="item.id"> {{ item.text }} </li> </transition-group>
KeepAlive
htmlCopy<keep-alive> <component :is="currentComponent"></component> </keep-alive> <!-- 包含/排除特定组件 --> <keep-alive :include="['a', 'b']" :exclude="['c']" :max="10"> <component :is="currentComponent"></component> </keep-alive>
高级 API
defineComponent
javascriptCopyimport { defineComponent, ref } from 'vue' export default defineComponent({ props: { title: String }, setup(props) { const count = ref(0) return { count } } })
defineAsyncComponent
javascriptCopyimport { defineAsyncComponent } from 'vue' // 简单用法 const AsyncComponent = defineAsyncComponent(() => import('./components/AsyncComponent.vue') ) // 带配置的高级用法 const AsyncComponentWithOptions = defineAsyncComponent({ loader: () => import('./components/AsyncComponent.vue'), loadingComponent: LoadingComponent, errorComponent: ErrorComponent, delay: 200, // 显示加载组件前的延迟 | 默认为 200 timeout: 3000, // 超时时间 | 默认为 Infinity suspensible: false, // 控制组件是否可挂起 | 默认为 true onError(error, retry, fail, attempts) { if (error.message.match(/fetch/) && attempts <= 3) { // 请求发生错误时重试,最多重试 3 次 retry() } else { // 注意,retry/fail 就像 promise 的 resolve/reject fail() } } })
defineCustomElement
javascriptCopyimport { defineCustomElement } from 'vue' const MyVueElement = defineCustomElement({ // 正常的Vue组件选项 props: {}, emits: {}, template: `<div>{{ message }}</div>`, // setup 或其他选项... // 只对自定义元素有效,不会影响Vue组件 styles: [`div { color: red; }`] }) // 注册自定义元素 customElements.define('my-vue-element', MyVueElement) // 然后在任何HTML中使用 // <my-vue-element></my-vue-element>
服务端渲染API
javascriptCopyimport { renderToString, renderToNodeStream } from 'vue/server-renderer' // 渲染为字符串 renderToString(app).then(html => { console.log(html) }) // 渲染为Node流 const stream = renderToNodeStream(app)
Vue Router 4
创建路由和基本用法
javascriptCopyimport { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: Home }, { path: '/about', component: About, // 路由元数据 meta: { requiresAuth: true } }, { path: '/user/:id', component: User, // 路由嵌套 children: [ { path: 'profile', component: UserProfile }, { path: 'posts', component: UserPosts } ] }, // 动态导入组件,实现懒加载 { path: '/admin', component: () => import('./views/Admin.vue') }, // 捕获所有路由 { path: '/:pathMatch(.*)*', component: NotFound } ] }) // 全局前置守卫 router.beforeEach((to, from) => { if (to.meta.requiresAuth && !isAuthenticated) { return { path: '/login', query: { redirect: to.fullPath } } } }) // 全局解析守卫 router.beforeResolve(async to => { if (to.meta.requiresFetch) { await fetchData() } }) // 全局后置钩子 router.afterEach((to, from) => { sendAnalytics(to.fullPath) })
组合式API中使用路由
javascriptCopyimport { useRouter, useRoute } from 'vue-router' export default { setup() { const router = useRouter() const route = useRoute() function navigateTo() { router.push('/about') } // 获取当前路由参数 console.log(route.params.id) return { navigateTo } } }
Pinia (Vue 3官方推荐的状态管理)
创建和使用Store
javascriptCopy// store/counter.js import { defineStore } from 'pinia' // 定义store (使用选项式API风格) export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, name: 'Eduardo' }), getters: { doubleCount: (state) => state.count * 2, // 使用this访问其他getter doubleCountPlusOne() { return this.doubleCount + 1 } }, actions: { increment() { this.count++ }, async fetchData() { const data = await api.get('url') this.count = data.count } } }) // 使用组合式API风格定义store export const useCounterStore = defineStore('counter', () => { const count = ref(0) const name = ref('Eduardo') const doubleCount = computed(() => count.value * 2) function increment() { count.value++ } return { count, name, doubleCount, increment } })
在组件中使用Store
javascriptCopyimport { useCounterStore } from '@/stores/counter' export default { setup() { const counterStore = useCounterStore() // 访问state console.log(counterStore.count) // 访问getter console.log(counterStore.doubleCount) // 调用action counterStore.increment() // 监听状态变化 watch( () => counterStore.count, (newCount) => { console.log(`Count is: ${newCount}`) } ) // 使用解构时保持响应式 const { count, name, doubleCount } = storeToRefs(counterStore) return { // 将整个store实例暴露给模板 counterStore, // 或者使用解构后的响应式属性 count, name, doubleCount } } }
组合式函数 (Composables)
创建可复用的组合式函数
javascriptCopy// src/composables/useCounter.js import { ref, computed } from 'vue' export function useCounter(initialValue = 0) { const count = ref(initialValue) function increment() { count.value++ } function decrement() { count.value-- } const doubled = computed(() => count.value * 2) return { count, increment, decrement, doubled } } // 使用fetch的组合式函数 export function useFetch(url) { const data = ref(null) const error = ref(null) const loading = ref(true) fetch(url) .then(res => res.json()) .then(json => { data.value = json loading.value = false }) .catch(err => { error.value = err loading.value = false }) return { data, error, loading } }
在组件中使用组合式函数
javascriptCopyimport { useCounter, useFetch } from '@/composables' export default { setup() { // 使用计数器组合式函数 const { count, increment, doubled } = useCounter(10) // 使用fetch组合式函数 const { data, error, loading } = useFetch('https://jsonplaceholder.typicode.com/todos/1') return { count, increment, doubled, data, error, loading } } }
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。