Vue3全家桶学习涵盖了Vue3的基础知识、组件化开发、响应式原理、Vue Router与Vuex的使用,以及与TypeScript的结合,帮助开发者全面掌握Vue3的开发技巧。文章详细介绍了从安装配置到实战演练的全过程,旨在帮助读者快速上手并构建实际项目。此外,还提供了部署上线和常见问题的解决方法,确保项目顺利运行。
Vue.js 是一个渐进式 JavaScript 框架,它让构建优秀的用户界面变得非常轻松。Vue3 是 Vue.js 的最新版本,它带来了许多新特性、优化及增强,包括更好的性能、TypeScript 支持、Composition API 等。Vue3 提供了一个灵活的架构,支持自定义渲染器,这使得 Vue 可以应用于广泛的环境和运行时环境,如服务端渲染、静态站点生成、Web Components 等。
安装 Node.js
首先,你需要确保已经安装了 Node.js。你可以从 Node.js 官方网站下载并安装最新版本的 Node.js。Node.js 的安装将自动安装 npm(Node Package Manager),这是 Node.js 的包管理器。
安装 Vue CLI
Vue CLI 是一个用于快速创建 Vue.js 项目的命令行工具。安装 Vue CLI 可以使用以下命令:
npm install -g @vue/cli
创建 Vue3 项目
使用 Vue CLI 创建一个新的 Vue3 项目:
vue create my-vue3-project
在创建过程中,选择 Vue3 版本,或者在默认配置中手动选择 Vue3。
项目初始化
创建项目后,可以使用以下命令进入项目目录并启动开发服务器:
cd my-vue3-project npm run serve
这将启动开发服务器,你可以在浏览器中打开 http://localhost:8080
查看项目。
一个典型的 Vue3 项目结构如下:
my-vue3-project/ ├── node_modules/ ├── public/ │ ├── index.html │ └── favicon.ico ├── src/ │ ├── assets/ │ ├── components/ │ ├── App.vue │ ├── main.js │ └── router/ │ └── index.js ├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── babel.config.js ├── package.json ├── README.md └── yarn.lock
index.html
和 favicon.ico
。<template> <div id="app"> <h1>Hello Vue3</h1> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
Vue3 使用组件构建用户界面。组件可以看作是可重用的自包含的 Vue 实例。每个组件都有自己的模板、样式和逻辑。
组件是通过 Vue 的 defineComponent
函数定义的:
import { defineComponent } from 'vue'; export default defineComponent({ name: 'HelloWorld', props: { msg: String }, template: ` <div class="hello"> <h1>{{ msg }}</h1> </div> `, });
在其他组件中使用上面定义的组件:
<template> <h1>{{ title }}</h1> <HelloWorld msg="Hello Vue3" /> </template> <script> import HelloWorld from './components/HelloWorld.vue'; export default { name: 'App', components: { HelloWorld }, data() { return { title: 'App Title' }; } } </script>
父组件可以通过 props
向子组件传递数据:
<!-- Parent.vue --> <template> <ChildComponent :message="parentMessage" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { parentMessage: 'Hello from parent' }; } } </script>
<!-- ChildComponent.vue --> <template> <div>{{ message }}</div> </template> <script> export default { name: 'ChildComponent', props: { message: String } } </script>
子组件可以通过 $emit
触发事件,向父组件传递数据。
<!-- ChildComponent.vue --> <template> <button @click="sendMessage">Send Message</button> </template> <script> export default { name: 'ChildComponent', methods: { sendMessage() { this.$emit('messageSent', 'Hello from child'); } } } </script>
<!-- Parent.vue --> <template> <ChildComponent @messageSent="handleMessage" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { name: 'Parent', components: { ChildComponent }, methods: { handleMessage(message) { console.log(message); } } } </script>
插槽用于向子组件传递内容。插槽可以分为默认插槽、具名插槽和作用域插槽。
<!-- Parent.vue --> <template> <ChildComponent> <h2>This is default slot content</h2> </ChildComponent> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent } } </script>
<!-- ChildComponent.vue --> <template> <div> <slot></slot> </div> </template>
<!-- Parent.vue --> <template> <ChildComponent> <template v-slot:header> <h1>This is header slot content</h1> </template> <template v-slot:footer> <p>This is footer slot content</p> </template> </ChildComponent> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent } } </script>
<!-- ChildComponent.vue --> <template> <div> <slot name="header"></slot> <slot name="footer"></slot> </div> </template>
作用域插槽允许子组件传递数据给父组件。
<!-- ChildComponent.vue --> <template> <slot v-bind:item="item"></slot> </template> <script> export default { name: 'ChildComponent', data() { return { item: { id: 1, name: 'John' } }; } } </script>
<!-- Parent.vue --> <template> <ChildComponent> <template v-slot:default="{ item }"> <p>ID: {{ item.id }}</p> <p>Name: {{ item.name }}</p> </template> </ChildComponent> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent } } </script>
Vue3 使用 Proxy 来实现响应式系统,这比 Vue2 中的 Object.defineProperty
更加强大。响应式数据绑定是通过 ref
和 reactive
实现的。
ref
ref
用于基本数据类型:
import { ref } from 'vue'; export default { setup() { const count = ref(0); return { count }; } }
在模板中使用:
<template> <div> <p>{{ count }}</p> <button @click="count++">Increment</button> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const count = ref(0); return { count }; } } </script>
reactive
reactive
用于复杂的数据对象:
import { reactive } from 'vue'; export default { setup() { const state = reactive({ count: 0, name: 'John' }); return { state }; } }
在模板中使用:
<template> <div> <p>{{ state.count }}</p> <p>{{ state.name }}</p> </div> </template> <script> import { reactive } from 'vue'; export default { setup() { const state = reactive({ count: 0, name: 'John' }); return { state }; } } </script>
computed
计算属性计算属性是基于组件内部的状态计算出的结果。
import { ref, computed } from 'vue'; export default { setup() { const a = ref(1); const b = ref(2); const sum = computed(() => a.value + b.value); return { a, b, sum }; } }
在模板中使用:
<template> <div> <p>{{ a }}</p> <p>{{ b }}</p> <p>{{ sum }}</p> </div> </template> <script> import { ref, computed } from 'vue'; export default { setup() { const a = ref(1); const b = ref(2); const sum = computed(() => a.value + b.value); return { a, b, sum }; } } </script>
watch
侦听器侦听器用于监听数据的变化,并在数据变化时执行回调。
import { ref, watch } from 'vue'; export default { setup() { const count = ref(0); watch(count, (newValue, oldValue) => { console.log(`count changed from ${oldValue} to ${newValue}`); }); return { count }; } }
计算属性和侦听器都可以用于数据处理,但它们的工作方式不同。
import { ref, computed, watch } from 'vue'; export default { setup() { const a = ref(1); const b = ref(2); const sum = computed(() => a.value + b.value); watch([a, b], (newValues, oldValues) => { console.log(`Values changed from ${JSON.stringify(oldValues)} to ${JSON.stringify(newValues)}`); }); return { a, b, sum }; } }
在模板中使用:
<template> <div> <p>{{ a }}</p> <p>{{ b }}</p> <p>{{ sum }}</p> </div> </template> <script> import { ref, computed, watch } from 'vue'; export default { setup() { const a = ref(1); const b = ref(2); const sum = computed(() => a.value + b.value); watch([a, b], (newValues, oldValues) => { console.log(`Values changed from ${JSON.stringify(oldValues)} to ${JSON.stringify(newValues)}`); }); return { a, b, sum }; } } </script>
import { ref } from 'vue'; export default { setup() { const state = ref({ a: 1, b: { text: 'hello' } }); return { state }; } }
import { reactive } from 'vue'; export default { setup() { const state = reactive({ a: 1, b: { text: 'hello' } }); return { state }; } }
Vue Router 是 Vue.js 官方的路由插件,用于管理前端路由。它支持路由参数、重定向、命名路由、嵌套路由等。
npm install vue-router
import { createRouter, createWebHistory } from 'vue-router'; const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ]; const router = createRouter({ history: createWebHistory(), routes }); export default router;
import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; createApp(App).use(router).mount('#app');
// router/index.js import { createRouter, createWebHistory } from 'vue-router'; import Home from '../views/Home.vue'; import About from '../views/About.vue'; const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ]; const router = createRouter({ history: createWebHistory(), routes }); export default router;
<!-- views/Home.vue --> <template> <div> <h1>Home Page</h1> </div> </template>
<!-- views/About.vue --> <template> <div> <h1>About Page</h1> </div> </template>
Vuex 是一个集中式的状态管理库,用于 Vue.js 应用。它可以帮助你管理应用中的状态,使状态管理更加一致且可预测。
npm install vuex
import { createStore } from 'vuex'; export default createStore({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { increment(context) { context.commit('increment'); } } });
import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; import store from './store'; createApp(App).use(router).use(store).mount('#app');
// store/index.js import { createStore } from 'vuex'; export default createStore({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { increment(context) { context.commit('increment'); } } });
my-vue3-project/ ├── node_modules/ ├── public/ │ ├── index.html │ └── favicon.ico ├── src/ │ ├── assets/ │ ├── components/ │ ├── App.vue │ ├── main.js │ └── router/ │ └── index.js │ └── store/ │ └── index.js ├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── babel.config.js ├── package.json ├── README.md └── yarn.lock
// router/index.js import { createRouter, createWebHistory } from 'vue-router'; import Home from '../views/Home.vue'; import About from '../views/About.vue'; const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ]; const router = createRouter({ history: createWebHistory(), routes }); export default router;
<!-- views/Home.vue --> <template> <div> <h1>Home Page</h1> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script> import { mapState, mapActions } from 'vuex'; export default { computed: { ...mapState(['count']) }, methods: { ...mapActions(['increment']) } } </script>
<!-- views/About.vue --> <template> <div> <h1>About Page</h1> </div> </template>
// store/index.js import { createStore } from 'vuex'; export default createStore({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { increment(context) { context.commit('increment'); } } });
TypeScript 是 JavaScript 的超集,提供了静态类型检查,使得代码更加健壮和易于维护。TypeScript 的主要特性包括类型注解、接口、泛型等。
npm install typescript
// TypeScript 基本类型 let a: number = 1; let b: string = 'hello'; let c: boolean = true; let d: null = null; let e: undefined = undefined; // 数组 let arr: number[] = [1, 2, 3]; let arr2: Array<number> = [1, 2, 3]; // 对象 let obj: { name: string, age: number } = { name: 'John', age: 30 }; // 接口 interface Person { name: string; age: number; } let person: Person = { name: 'John', age: 30 }; // 泛型 function identity<T>(arg: T): T { return arg; } let result = identity<string>('hello');
npm install -g @vue/cli-plugin-typescript
vue create my-vue3-project
选择 Vue3 项目模板并启用 TypeScript 插件。
<script setup lang="ts"> import { ref } from 'vue'; const count = ref(0); function increment() { count.value++; } </script> <template> <div> <p>{{ count }}</p> <button @click="increment">Increment</button> </div> </template> <style scoped> p { font-size: 20px; } </style>
<script setup lang="ts"> import { defineComponent, ref, defineProps } from 'vue'; interface Props { message: string; } const props = defineProps<Props>(); const count = ref(0); function increment() { count.value++; } </script> <template> <div> <p>{{ props.message }}</p> <p>{{ count }}</p> <button @click="increment">Increment</button> </div> </template>
<script setup lang="ts"> import { defineComponent, ref, defineEmits } from 'vue'; interface Props { message: string; } const props = defineProps<Props>(); const count = ref(0); const emit = defineEmits<{ (e: 'incremented', value: number): void; }>(); function increment() { count.value++; emit('incremented', count.value); } </script> <template> <div> <p>{{ props.message }}</p> <p>{{ count }}</p> <button @click="increment">Increment</button> </div> </template>
假设我们需要创建一个简单的待办事项(To-Do List)应用。该应用需要有以下功能:
to-do-list/ ├── node_modules/ ├── public/ │ ├── index.html │ └── favicon.ico ├── src/ │ ├── assets/ │ ├── components/ │ │ └── TodoItem.vue │ ├── App.vue │ ├── main.ts │ └── router/ │ └── index.ts ├── .browserslistrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── babel.config.js ├── package.json ├── README.md └── yarn.lock
<script setup lang="ts"> import { ref } from 'vue'; import TodoItem from './components/TodoItem.vue'; import { useRouter } from 'vue-router'; const router = useRouter(); const todoList = ref<string[]>([]); const newTodo = ref(''); function addTodo() { if (newTodo.value) { todoList.value.push(newTodo.value); newTodo.value = ''; } } function deleteTodo(index: number) { todoList.value.splice(index, 1); } </script> <template> <div id="app"> <h1>To-Do List</h1> <input v-model="newTodo" @keyup.enter="addTodo" placeholder="Type a new todo"> <button @click="addTodo">Add</button> <ul> <TodoItem v-for="(todo, index) in todoList" :key="index" :todo="todo" @delete="deleteTodo(index)" /> </ul> </div> </template> <style> #app { width: 50%; margin: 0 auto; } input { width: 50%; padding: 5px; } button { padding: 5px 10px; } ul { list-style-type: none; padding: 0; } li { margin: 5px 0; } </style>
<script setup lang="ts"> import { defineProps, defineEmits } from 'vue'; interface Props { todo: string; } const props = defineProps<Props>(); const emit = defineEmits<{ (e: 'delete', index: number): void; }>(); function deleteTodo(index: number) { emit('delete', index); } </script> <template> <li> {{ props.todo }} <button @click="deleteTodo">Delete</button> </li> </template>
构建项目
使用 Vue CLI 构建项目:
npm run build
这将在 dist
目录下生成静态文件。
部署到服务器
将生成的静态文件部署到服务器。常见的部署方式包括使用服务器托管、云服务托管等。
npm install
.env
文件和 package.json
中的配置正确。npm ci
package.json
中的 scripts
配置正确。v-if
或 v-show
代替频繁的 DOM 操作。vue-server-renderer
进行服务端渲染。通过以上步骤和示例代码,你可以从零开始搭建一个完整的 Vue3 项目,并将其部署到生产环境。希望这篇教程对你有所帮助!