主题
Vue.js前端开发基础
1. Vue.js介绍
Vue.js是一个渐进式的JavaScript框架,用于构建用户界面。它被设计为可以自底向上逐层应用,从简单的表单处理到复杂的单页应用都能胜任。
1.1 Vue.js的特点
- 渐进式框架:可以根据需要逐步集成到项目中
- 响应式数据绑定:自动追踪依赖并更新DOM
- 组件化开发:将UI拆分为可复用的组件
- 虚拟DOM:提高渲染性能
- 指令系统:提供简洁的模板语法
- 过渡动画:内置的过渡效果系统
- 路由:官方路由库Vue Router
- 状态管理:官方状态管理库Vuex/Pinia
- 构建工具:Vue CLI和Vite
1.2 Vue.js与其他框架的比较
| 框架 | 特点 | 适用场景 |
|---|---|---|
| Vue.js | 渐进式、易用、灵活 | 从简单表单到复杂SPA |
| React | 组件化、生态丰富 | 大型应用、跨平台 |
| Angular | 全功能、TypeScript支持 | 企业级应用、大型团队 |
| Svelte | 编译时优化、代码量少 | 性能要求高的应用 |
| Preact | 轻量级、React兼容 | 性能要求高的小型应用 |
2. Vue.js环境搭建
2.1 安装Node.js
Vue.js需要Node.js 14.0或更高版本。首先确保你的系统已经安装了Node.js。
2.2 创建Vue项目
2.2.1 使用Vite
Vite是Vue官方推荐的构建工具,速度更快:
bash
# 安装create-vite@6.5.0
npm create vite@6.5.0 my-vue-app -- --template vue
# 进入项目目录
cd my-vue-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev2.2.2 使用Vue CLI
Vue CLI是传统的Vue项目脚手架:
bash
# 安装Vue CLI
npm install -g @vue/cli
# 创建项目
vue create my-vue-app
# 进入项目目录
cd my-vue-app
# 启动开发服务器
npm run serve2.3 项目结构
使用Vite创建的Vue项目结构:
my-vue-app/
├── node_modules/
├── public/
│ └── favicon.ico
├── src/
│ ├── assets/
│ │ └── logo.png
│ ├── components/
│ │ └── HelloWorld.vue
│ ├── App.vue
│ └── main.js
├── index.html
├── package.json
├── vite.config.js
└── README.md3. Vue.js基础语法
3.1 模板语法
3.1.1 插值表达式
vue
<template>
<div>
<!-- 文本插值 -->
<h1>{{ message }}</h1>
<!-- HTML插值 -->
<div v-html="rawHtml"></div>
<!-- 表达式 -->
<p>{{ number + 1 }}</p>
<p>{{ ok ? 'Yes' : 'No' }}</p>
<p>{{ message.split('').reverse().join('') }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!',
rawHtml: '<span style="color: red">This is red</span>',
number: 42,
ok: true
}
}
}
</script>3.1.2 指令
vue
<template>
<div>
<!-- v-bind:绑定属性 -->
<img v-bind:src="imageSrc" alt="Logo">
<img :src="imageSrc" alt="Logo"> <!-- 简写 -->
<!-- v-if:条件渲染 -->
<div v-if="isVisible">Visible</div>
<div v-else-if="isLoading">Loading...</div>
<div v-else>Hidden</div>
<!-- v-for:列表渲染 -->
<ul>
<li v-for="(item, index) in items" :key="index">
{{ index }}: {{ item.name }}
</li>
</ul>
<!-- v-on:事件监听 -->
<button v-on:click="handleClick">Click me</button>
<button @click="handleClick">Click me</button> <!-- 简写 -->
<!-- v-model:双向绑定 -->
<input v-model="message" placeholder="Enter message">
<!-- v-show:条件显示(始终渲染,通过CSS控制显示/隐藏) -->
<div v-show="isVisible">Show/Hide</div>
<!-- v-pre:跳过编译 -->
<div v-pre>{{ this will not be compiled }}</div>
<!-- v-once:只编译一次 -->
<div v-once>{{ message }}</div>
</div>
</template>
<script>
export default {
data() {
return {
imageSrc: '/logo.png',
isVisible: true,
isLoading: false,
items: [
{ name: 'Item 1' },
{ name: 'Item 2' },
{ name: 'Item 3' }
],
message: 'Hello'
}
},
methods: {
handleClick() {
alert('Button clicked!');
}
}
}
</script>3.2 计算属性和监听器
3.2.1 计算属性
vue
<template>
<div>
<input v-model="message" placeholder="Enter message">
<p>Original message: {{ message }}</p>
<p>Reversed message: {{ reversedMessage }}</p>
<p>Message length: {{ messageLength }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
},
computed: {
// 计算属性:依赖变化时自动重新计算
reversedMessage() {
return this.message.split('').reverse().join('');
},
messageLength() {
return this.message.length;
}
}
}
</script>3.2.2 监听器
vue
<template>
<div>
<input v-model="message" placeholder="Enter message">
<p>Message: {{ message }}</p>
<p>Last updated: {{ lastUpdated }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!',
lastUpdated: new Date().toLocaleString()
}
},
watch: {
// 监听器:监听数据变化
message(newValue, oldValue) {
console.log(`Message changed from ${oldValue} to ${newValue}`);
this.lastUpdated = new Date().toLocaleString();
}
}
}
</script>3.3 方法和事件处理
3.3.1 方法定义
vue
<template>
<div>
<button @click="greet">Greet</button>
<button @click="say('Hello')">Say Hello</button>
<button @click="say('Hi')">Say Hi</button>
</div>
</template>
<script>
export default {
data() {
return {
name: 'Vue'
}
},
methods: {
greet() {
alert(`Hello ${this.name}!`);
},
say(message) {
alert(`${message} ${this.name}!`);
}
}
}
</script>3.3.2 事件修饰符
vue
<template>
<div>
<!-- 阻止默认行为 -->
<a href="https://vuejs.org" @click.prevent="handleClick">Vue.js</a>
<!-- 阻止冒泡 -->
<div @click="handleDivClick">
<button @click.stop="handleButtonClick">Click me</button>
</div>
<!-- 按键修饰符 -->
<input @keyup.enter="handleEnter" placeholder="Press Enter">
<input @keyup.esc="handleEsc" placeholder="Press Esc">
<!-- 鼠标修饰符 -->
<button @click.left="handleLeftClick">Left Click</button>
<button @click.right="handleRightClick">Right Click</button>
<!-- 系统修饰符 -->
<input @keyup.ctrl.enter="handleCtrlEnter" placeholder="Press Ctrl+Enter">
</div>
</template>
<script>
export default {
methods: {
handleClick() {
alert('Link clicked');
},
handleDivClick() {
alert('Div clicked');
},
handleButtonClick() {
alert('Button clicked');
},
handleEnter() {
alert('Enter pressed');
},
handleEsc() {
alert('Esc pressed');
},
handleLeftClick() {
alert('Left click');
},
handleRightClick() {
alert('Right click');
},
handleCtrlEnter() {
alert('Ctrl+Enter pressed');
}
}
}
</script>3.4 表单处理
vue
<template>
<form @submit.prevent="handleSubmit">
<!-- 文本输入 -->
<div>
<label for="name">Name:</label>
<input type="text" id="name" v-model="form.name" required>
</div>
<!-- 密码输入 -->
<div>
<label for="password">Password:</label>
<input type="password" id="password" v-model="form.password" required>
</div>
<!-- 单选按钮 -->
<div>
<label>
<input type="radio" v-model="form.gender" value="male"> Male
</label>
<label>
<input type="radio" v-model="form.gender" value="female"> Female
</label>
</div>
<!-- 复选框 -->
<div>
<label>
<input type="checkbox" v-model="form.terms"> I agree to terms
</label>
</div>
<!-- 下拉选择 -->
<div>
<label for="role">Role:</label>
<select id="role" v-model="form.role">
<option value="admin">Admin</option>
<option value="user">User</option>
<option value="guest">Guest</option>
</select>
</div>
<!-- 提交按钮 -->
<button type="submit">Submit</button>
</form>
<div v-if="submitted">
<h3>Form submitted:</h3>
<pre>{{ form }}</pre>
</div>
</template>
<script>
export default {
data() {
return {
form: {
name: '',
password: '',
gender: 'male',
terms: false,
role: 'user'
},
submitted: false
}
},
methods: {
handleSubmit() {
if (this.form.terms) {
this.submitted = true;
console.log('Form submitted:', this.form);
} else {
alert('Please agree to terms');
}
}
}
}
</script>4. Vue组件
4.1 组件基础
4.1.1 创建组件
vue
<!-- HelloWorld.vue -->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button @click="greet">Greet</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: {
type: String,
default: 'Hello World'
}
},
methods: {
greet() {
alert('Hello from HelloWorld component!');
}
}
}
</script>
<style scoped>
.hello {
margin: 20px;
padding: 20px;
border: 1px solid #eee;
border-radius: 4px;
}
</style>4.1.2 使用组件
vue
<!-- App.vue -->
<template>
<div id="app">
<HelloWorld msg="Welcome to Your Vue.js App" />
<HelloWorld msg="Hello Vue Component" />
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>4.2 组件通信
4.2.1 父组件向子组件传递数据(Props)
vue
<!-- ParentComponent.vue -->
<template>
<div>
<h2>Parent Component</h2>
<input v-model="parentMessage" placeholder="Enter message">
<ChildComponent :message="parentMessage" :count="counter" />
<button @click="counter++">Increment Count</button>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data() {
return {
parentMessage: 'Hello from parent',
counter: 0
}
}
}
</script>vue
<!-- ChildComponent.vue -->
<template>
<div class="child">
<h3>Child Component</h3>
<p>Message from parent: {{ message }}</p>
<p>Count from parent: {{ count }}</p>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
}
}
</script>
<style scoped>
.child {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>4.2.2 子组件向父组件传递数据(Events)
vue
<!-- ParentComponent.vue -->
<template>
<div>
<h2>Parent Component</h2>
<p>Message from child: {{ childMessage }}</p>
<p>Counter: {{ counter }}</p>
<ChildComponent @send-message="handleMessage" @increment="counter++" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data() {
return {
childMessage: '',
counter: 0
}
},
methods: {
handleMessage(message) {
this.childMessage = message;
}
}
}
</script>vue
<!-- ChildComponent.vue -->
<template>
<div class="child">
<h3>Child Component</h3>
<input v-model="message" placeholder="Enter message">
<button @click="sendMessage">Send Message to Parent</button>
<button @click="$emit('increment')">Increment Parent Counter</button>
</div>
</template>
<script>
export default {
data() {
return {
message: ''
}
},
methods: {
sendMessage() {
this.$emit('send-message', this.message);
}
}
}
</script>
<style scoped>
.child {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>4.2.3 兄弟组件通信(Event Bus)
javascript
// eventBus.js
import Vue from 'vue'
export default new Vue()vue
<!-- ComponentA.vue -->
<template>
<div class="component-a">
<h3>Component A</h3>
<input v-model="message" placeholder="Enter message">
<button @click="sendMessage">Send Message to B</button>
</div>
</template>
<script>
import eventBus from '../eventBus'
export default {
data() {
return {
message: ''
}
},
methods: {
sendMessage() {
eventBus.$emit('message-from-a', this.message);
}
}
}
</script>
<style scoped>
.component-a {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>vue
<!-- ComponentB.vue -->
<template>
<div class="component-b">
<h3>Component B</h3>
<p>Message from A: {{ messageFromA }}</p>
</div>
</template>
<script>
import eventBus from '../eventBus'
export default {
data() {
return {
messageFromA: ''
}
},
mounted() {
// 监听事件
eventBus.$on('message-from-a', (message) => {
this.messageFromA = message;
});
},
beforeUnmount() {
// 移除事件监听器
eventBus.$off('message-from-a');
}
}
</script>
<style scoped>
.component-b {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>4.3 组件生命周期
4.3.1 生命周期钩子
vue
<template>
<div class="lifecycle">
<h3>Component Lifecycle</h3>
<p>Message: {{ message }}</p>
<button @click="updateMessage">Update Message</button>
<button @click="destroyComponent">Destroy Component</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
},
// 创建阶段
beforeCreate() {
console.log('beforeCreate: Component is about to be created');
},
created() {
console.log('created: Component has been created');
// 可以访问data和methods,但DOM尚未挂载
},
// 挂载阶段
beforeMount() {
console.log('beforeMount: Component is about to be mounted');
},
mounted() {
console.log('mounted: Component has been mounted');
// DOM已挂载,可以访问DOM元素
},
// 更新阶段
beforeUpdate() {
console.log('beforeUpdate: Component is about to be updated');
},
updated() {
console.log('updated: Component has been updated');
// DOM已更新
},
// 销毁阶段
beforeUnmount() {
console.log('beforeUnmount: Component is about to be unmounted');
},
unmounted() {
console.log('unmounted: Component has been unmounted');
// 组件已销毁,清理定时器、事件监听器等
},
methods: {
updateMessage() {
this.message = 'Updated message at ' + new Date().toLocaleTimeString();
},
destroyComponent() {
// 触发组件销毁
this.$emit('destroy');
}
}
}
</script>
<style scoped>
.lifecycle {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>4.3.2 生命周期流程图
创建阶段
- beforeCreate: 组件实例刚创建,数据观测和事件配置之前
- created: 组件实例创建完成,数据观测和事件配置已完成
挂载阶段
- beforeMount: 模板编译完成,DOM挂载之前
- mounted: DOM挂载完成,可访问DOM元素
更新阶段
- beforeUpdate: 数据更新,DOM更新之前
- updated: DOM更新完成
销毁阶段
- beforeUnmount: 组件销毁之前
- unmounted: 组件销毁完成,清理资源
5. Vue Router
5.1 安装Vue Router
bash
# 安装Vue Router
npm install vue-router@45.2 配置Vue Router
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import ContactView from '../views/ContactView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: AboutView
},
{
path: '/contact',
name: 'contact',
component: ContactView
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default routerjavascript
// 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')5.3 使用Vue Router
vue
<!-- App.vue -->
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link to="/contact">Contact</router-link>
</nav>
<router-view />
</div>
</template>
<style>
nav {
margin: 20px 0;
}
nav a {
margin-right: 20px;
text-decoration: none;
color: #42b983;
}
nav a.router-link-active {
font-weight: bold;
color: #35495e;
}
</style>vue
<!-- views/HomeView.vue -->
<template>
<div class="home">
<h1>Home Page</h1>
<p>Welcome to the Home Page!</p>
</div>
</template>
<script>
export default {
name: 'HomeView'
}
</script>vue
<!-- views/AboutView.vue -->
<template>
<div class="about">
<h1>About Page</h1>
<p>About Us</p>
</div>
</template>
<script>
export default {
name: 'AboutView'
}
</script>vue
<!-- views/ContactView.vue -->
<template>
<div class="contact">
<h1>Contact Page</h1>
<p>Contact Us</p>
</div>
</template>
<script>
export default {
name: 'ContactView'
}
</script>5.4 路由参数
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import UserView from '../views/UserView.vue'
const routes = [
// ... 其他路由
{
path: '/user/:id',
name: 'user',
component: UserView,
props: true
}
]
// ...vue
<!-- views/UserView.vue -->
<template>
<div class="user">
<h1>User Page</h1>
<p>User ID: {{ id }}</p>
<button @click="goBack">Go Back</button>
</div>
</template>
<script>
export default {
name: 'UserView',
props: ['id'],
methods: {
goBack() {
this.$router.back();
}
}
}
</script>vue
<!-- 在其他组件中导航到用户页面 -->
<template>
<div>
<router-link :to="{ name: 'user', params: { id: 1 } }">User 1</router-link>
<button @click="navigateToUser(2)">Go to User 2</button>
</div>
</template>
<script>
export default {
methods: {
navigateToUser(id) {
this.$router.push({
name: 'user',
params: { id }
});
}
}
}
</script>6. Vuex状态管理
6.1 安装Vuex
bash
# 安装Vuex
npm install vuex@46.2 配置Vuex
javascript
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0,
user: null,
products: [
{ id: 1, name: 'Product 1', price: 100 },
{ id: 2, name: 'Product 2', price: 200 },
{ id: 3, name: 'Product 3', price: 300 }
]
},
getters: {
// 计算属性
doubleCount: (state) => state.count * 2,
expensiveProducts: (state) => {
return state.products.filter(product => product.price > 150);
},
productById: (state) => (id) => {
return state.products.find(product => product.id === id);
}
},
mutations: {
// 同步操作,修改状态
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
setCount(state, payload) {
state.count = payload;
},
setUser(state, user) {
state.user = user;
}
},
actions: {
// 异步操作,提交mutation
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
},
login({ commit }, userData) {
// 模拟API请求
return new Promise((resolve) => {
setTimeout(() => {
const user = {
id: 1,
name: userData.username,
email: userData.email
};
commit('setUser', user);
resolve(user);
}, 1000);
});
}
},
modules: {
// 模块拆分
cart: {
namespaced: true,
state: {
items: []
},
mutations: {
addToCart(state, product) {
state.items.push(product);
},
removeFromCart(state, productId) {
state.items = state.items.filter(item => item.id !== productId);
}
},
actions: {
addProductToCart({ commit }, product) {
commit('addToCart', product);
}
}
}
}
})javascript
// 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')6.3 使用Vuex
vue
<template>
<div class="counter">
<h2>Vuex Counter</h2>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
<button @click="incrementAsync">Increment Async</button>
<button @click="setCount(10)">Set Count to 10</button>
</div>
<div class="user">
<h2>User</h2>
<p v-if="user">Welcome, {{ user.name }}!</p>
<p v-else>Please login</p>
<button v-if="!user" @click="login">Login</button>
</div>
<div class="products">
<h2>Products</h2>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.name }} - ${{ product.price }}
<button @click="addToCart(product)">Add to Cart</button>
</li>
</ul>
<h3>Expensive Products</h3>
<ul>
<li v-for="product in expensiveProducts" :key="product.id">
{{ product.name }} - ${{ product.price }}
</li>
</ul>
</div>
<div class="cart">
<h2>Cart ({{ cartItems.length }} items)</h2>
<ul>
<li v-for="item in cartItems" :key="item.id">
{{ item.name }} - ${{ item.price }}
<button @click="removeFromCart(item.id)">Remove</button>
</li>
</ul>
</div>
</template>
<script>
export default {
computed: {
// 从Vuex获取状态
count() {
return this.$store.state.count;
},
doubleCount() {
return this.$store.getters.doubleCount;
},
user() {
return this.$store.state.user;
},
products() {
return this.$store.state.products;
},
expensiveProducts() {
return this.$store.getters.expensiveProducts;
},
cartItems() {
return this.$store.state.cart.items;
}
},
methods: {
// 提交mutation
increment() {
this.$store.commit('increment');
},
decrement() {
this.$store.commit('decrement');
},
setCount(value) {
this.$store.commit('setCount', value);
},
// 分发action
incrementAsync() {
this.$store.dispatch('incrementAsync');
},
login() {
this.$store.dispatch('login', {
username: 'John Doe',
email: 'john@example.com'
}).then(user => {
console.log('Login successful:', user);
});
},
addToCart(product) {
this.$store.dispatch('cart/addProductToCart', product);
},
removeFromCart(productId) {
this.$store.commit('cart/removeFromCart', productId);
}
}
}
</script>
<style scoped>
.counter,
.user,
.products,
.cart {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
margin: 5px;
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 3px;
background: #f0f0f0;
cursor: pointer;
}
button:hover {
background: #e0e0e0;
}
</style>7. Vue 3新特性
7.1 Composition API
7.1.1 setup()函数
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script>
import { ref, computed, onMounted, onUnmounted } from 'vue'
export default {
name: 'CompositionApiExample',
props: {
title: {
type: String,
default: 'Composition API Example'
}
},
setup(props) {
// 响应式数据
const count = ref(0)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
// 生命周期钩子
onMounted(() => {
console.log('Component mounted')
})
onUnmounted(() => {
console.log('Component unmounted')
})
// 返回需要在模板中使用的内容
return {
count,
doubleCount,
increment,
decrement
}
}
}
</script>7.1.2 Script Setup语法糖
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
// 定义props
const props = defineProps({
title: {
type: String,
default: 'Script Setup Example'
}
})
// 响应式数据
const count = ref(0)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
// 生命周期钩子
onMounted(() => {
console.log('Component mounted')
})
onUnmounted(() => {
console.log('Component unmounted')
})
</script>7.2 Teleport
vue
<template>
<div class="modal-example">
<h2>Teleport Example</h2>
<button @click="showModal = true">Show Modal</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<div class="modal-content">
<h3>Modal Title</h3>
<p>This modal is teleported to the body element</p>
<button @click="showModal = false">Close Modal</button>
</div>
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showModal = ref(false)
</script>
<style scoped>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
max-width: 500px;
width: 100%;
}
</style>7.3 Suspense
vue
<template>
<div class="suspense-example">
<h2>Suspense Example</h2>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div class="loading">Loading...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
// 异步组件
const AsyncComponent = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
loadingComponent: { template: '<div class="loading">Loading component...</div>' },
delay: 200,
timeout: 3000
})
</script>
<style scoped>
.loading {
padding: 20px;
text-align: center;
color: #666;
}
</style>vue
<!-- AsyncComponent.vue -->
<template>
<div class="async-component">
<h3>Async Component</h3>
<p>{{ data }}</p>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const data = ref('')
// 模拟异步数据获取
onMounted(() => {
setTimeout(() => {
data.value = 'Async data loaded successfully!'
}, 2000)
})
</script>
<style scoped>
.async-component {
margin: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>8. Vue项目构建和部署
8.1 构建项目
bash
# 构建生产版本
npm run build
# 构建结果会生成在dist目录8.2 部署到静态服务器
- 复制dist目录到服务器
bash
# 使用scp复制到服务器
scp -r dist/* user@server:/path/to/webroot- 配置Nginx
nginx
server {
listen 80;
server_name example.com;
root /path/to/webroot;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
}8.3 部署到Netlify
- 连接GitHub仓库
- 设置构建命令:
npm run build - 设置发布目录:
dist - 部署
8.4 部署到Vercel
- 连接GitHub仓库
- 设置构建命令:
npm run build - 设置输出目录:
dist - 部署
9. 最佳实践
9.1 代码组织
- 使用组件化开发
- 合理使用Props和Events进行组件通信
- 使用Vuex管理全局状态
- 使用Vue Router进行路由管理
- 合理使用计算属性和监听器
- 使用Composition API组织复杂逻辑
9.2 性能优化
- 使用虚拟列表处理长列表
- 合理使用v-if和v-show
- 使用keep-alive缓存组件
- 优化组件渲染(避免不必要的重新渲染)
- 使用懒加载
- 优化CSS选择器
- 减少DOM操作
- 使用Web Workers处理复杂计算
9.3 安全性
- 防止XSS攻击(使用v-html时要注意)
- 防止CSRF攻击
- 安全处理用户输入
- 合理使用CSP(内容安全策略)
- 保护敏感数据
- 定期更新依赖
9.4 可维护性
- 使用ESLint和Prettier保持代码风格一致
- 编写清晰的注释
- 使用TypeScript增加类型安全性
- 编写单元测试和集成测试
- 文档化组件和API
- 遵循命名规范
9.5 开发工具
- IDE:VS Code + Vetur/Volar插件
- 构建工具:Vite
- 包管理器:npm/yarn/pnpm
- 版本控制:Git
- 代码质量:ESLint, Prettier
- 测试:Jest, Vue Test Utils
- 文档:VuePress
10. 实战项目:待办事项应用
10.1 项目结构
todo-app/
├── public/
├── src/
│ ├── assets/
│ ├── components/
│ │ ├── TodoHeader.vue
│ │ ├── TodoInput.vue
│ │ ├── TodoList.vue
│ │ ├── TodoItem.vue
│ │ └── TodoFooter.vue
│ ├── views/
│ │ └── HomeView.vue
│ ├── router/
│ │ └── index.js
│ ├── store/
│ │ └── index.js
│ ├── App.vue
│ └── main.js
├── index.html
├── package.json
├── vite.config.js
└── README.md10.2 核心功能
- 添加待办事项
- 标记待办事项为已完成/未完成
- 编辑待办事项
- 删除待办事项
- 过滤待办事项(全部/已完成/未完成)
- 清空已完成待办事项
- 待办事项数量统计
- 本地存储持久化
10.3 技术栈
- 前端框架:Vue 3 + Composition API
- 状态管理:Vuex 4
- 路由:Vue Router 4
- 构建工具:Vite
- 样式:CSS/SCSS
- 持久化:localStorage
11. 总结
Vue.js是一个渐进式、易用、灵活的JavaScript框架,它提供了响应式数据绑定、组件化开发、虚拟DOM等特性,非常适合构建现代前端应用。通过本文的学习,你已经掌握了:
- Vue.js的基本概念和特点
- Vue.js环境搭建和项目创建
- Vue.js模板语法和指令
- Vue组件的创建和通信
- Vue Router路由管理
- Vuex状态管理
- Vue 3新特性
- Vue项目构建和部署
- 最佳实践和实战项目
Vue.js的学习曲线相对平缓,易于上手,同时又足够强大,可以满足各种前端开发需求。希望本文对你的Vue.js学习之旅有所帮助!
12. 练习与思考
- 实现一个完整的待办事项应用,包括添加、编辑、删除、过滤等功能
- 实现一个计数器应用,使用Vuex管理状态
- 实现一个用户管理系统,包括用户列表、详情、添加、编辑、删除功能
- 实现一个天气应用,调用第三方API获取天气数据
- 实现一个购物车应用,使用Vuex管理购物车状态
- 学习使用Vue 3的Composition API重构现有组件
- 学习使用TypeScript开发Vue应用
- 学习使用Vue Test Utils编写单元测试
通过这些练习,你将进一步巩固Vue.js的使用技能,为实际项目开发打下坚实的基础。