跳转到内容

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 dev

2.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 serve

2.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.md

3. 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 生命周期流程图

  1. 创建阶段

    • beforeCreate: 组件实例刚创建,数据观测和事件配置之前
    • created: 组件实例创建完成,数据观测和事件配置已完成
  2. 挂载阶段

    • beforeMount: 模板编译完成,DOM挂载之前
    • mounted: DOM挂载完成,可访问DOM元素
  3. 更新阶段

    • beforeUpdate: 数据更新,DOM更新之前
    • updated: DOM更新完成
  4. 销毁阶段

    • beforeUnmount: 组件销毁之前
    • unmounted: 组件销毁完成,清理资源

5. Vue Router

5.1 安装Vue Router

bash
# 安装Vue Router
npm install vue-router@4

5.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 router
javascript
// 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@4

6.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 部署到静态服务器

  1. 复制dist目录到服务器
bash
# 使用scp复制到服务器
scp -r dist/* user@server:/path/to/webroot
  1. 配置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

  1. 连接GitHub仓库
  2. 设置构建命令npm run build
  3. 设置发布目录dist
  4. 部署

8.4 部署到Vercel

  1. 连接GitHub仓库
  2. 设置构建命令npm run build
  3. 设置输出目录dist
  4. 部署

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.md

10.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. 练习与思考

  1. 实现一个完整的待办事项应用,包括添加、编辑、删除、过滤等功能
  2. 实现一个计数器应用,使用Vuex管理状态
  3. 实现一个用户管理系统,包括用户列表、详情、添加、编辑、删除功能
  4. 实现一个天气应用,调用第三方API获取天气数据
  5. 实现一个购物车应用,使用Vuex管理购物车状态
  6. 学习使用Vue 3的Composition API重构现有组件
  7. 学习使用TypeScript开发Vue应用
  8. 学习使用Vue Test Utils编写单元测试

通过这些练习,你将进一步巩固Vue.js的使用技能,为实际项目开发打下坚实的基础。

评论区

专业的Linux技术学习平台,从入门到精通的完整学习路径