Vue.js 中文教程

1. 项目概述

Vue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。它的设计理念是通过简单的 API 实现响应式数据绑定和组件化开发,使得构建复杂的单页应用变得更加简单和可维护。

1.1 主要功能

  • 响应式数据绑定:自动追踪依赖并更新 DOM
  • 组件化开发:将 UI 拆分为独立、可复用的组件
  • 虚拟 DOM:通过虚拟 DOM 提高渲染性能
  • 指令系统:提供丰富的指令来操作 DOM
  • 过渡动画:内置的过渡效果系统
  • 计算属性:缓存计算结果,提高性能
  • 监听器:响应数据变化
  • 生命周期钩子:在组件不同阶段执行代码
  • 模板语法:简洁直观的模板语法
  • 插件系统:可扩展的插件机制

1.2 技术栈特点

  • 易于学习:API 设计简洁明了,学习曲线平缓
  • 灵活渐进:可以从简单的库开始,逐步扩展为完整的框架
  • 性能优异:响应式系统和虚拟 DOM 保证了高效的渲染
  • 文档完善:官方文档详尽,示例丰富
  • 生态系统丰富:拥有大量的第三方库和工具
  • TypeScript 支持:官方支持 TypeScript
  • 强大的社区支持:作为前端开发的主流框架之一,拥有庞大的社区和丰富的学习资源

1.3 GitHub Stars

200k+

2. 安装设置

2.1 使用 npm 或 yarn

# 使用 npm
npm install vue

# 使用 yarn
yarn add vue

2.2 使用 CDN

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

2.3 使用 Vue CLI

Vue CLI 是 Vue 官方推荐的项目脚手架工具,它会自动配置好开发环境,让你可以专注于编写代码。

# 安装 Vue CLI
npm install -g @vue/cli

# 创建新的 Vue 项目
vue create my-app

# 进入项目目录
cd my-app

# 启动开发服务器
npm run serve

2.4 使用 Vite

Vite 是一个现代化的前端构建工具,它提供了更快的开发体验。

# 使用 npm
npm create vite@latest my-app -- --template vue

# 使用 yarn
yarn create vite my-app --template vue

# 使用 pnpm
pnpm create vite my-app --template vue

# 进入项目目录
cd my-app

# 安装依赖
npm install

# 启动开发服务器
npm run dev

3. 基本使用

3.1 创建 Vue 实例

// 引入 Vue
import Vue from 'vue'

// 创建 Vue 实例
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
<!-- HTML 模板 -->
<div id="app">
  {{ message }}
</div>

3.2 模板语法

3.2.1 文本插值

<div>{{ message }}</div>

3.2.2 指令

<!-- v-bind 指令:绑定属性 -->
<div v-bind:class="className"></div>
<!-- 简写 -->
<div :class="className"></div>

<!-- v-on 指令:绑定事件 -->
<button v-on:click="handleClick">Click me</button>
<!-- 简写 -->
<button @click="handleClick">Click me</button>

<!-- v-if 指令:条件渲染 -->
<div v-if="isVisible">Visible</div>

<!-- v-for 指令:列表渲染 -->
<ul>
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</ul>

<!-- v-model 指令:双向数据绑定 -->
<input v-model="message" type="text">

3.3 计算属性和监听器

3.3.1 计算属性

new Vue({
  el: '#app',
  data: {
    firstName: 'John',
    lastName: 'Doe'
  },
  computed: {
    // 计算属性的 getter
    fullName: function () {
      // `this` 指向 vm 实例
      return this.firstName + ' ' + this.lastName
    }
  }
})

3.3.2 监听器

new Vue({
  el: '#app',
  data: {
    message: 'Hello',
    watchCount: 0
  },
  watch: {
    // 当 message 变化时执行
    message: function (newValue, oldValue) {
      this.watchCount++
      console.log('message changed from', oldValue, 'to', newValue)
    }
  }
})

3.4 方法

new Vue({
  el: '#app',
  data: {
    count: 0
  },
  methods: {
    increment: function () {
      this.count++
    }
  }
})

3.5 生命周期钩子

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  },
  beforeCreate: function () {
    console.log('beforeCreate')
  },
  created: function () {
    console.log('created')
  },
  beforeMount: function () {
    console.log('beforeMount')
  },
  mounted: function () {
    console.log('mounted')
  },
  beforeUpdate: function () {
    console.log('beforeUpdate')
  },
  updated: function () {
    console.log('updated')
  },
  beforeDestroy: function () {
    console.log('beforeDestroy')
  },
  destroyed: function () {
    console.log('destroyed')
  }
})

4. 组件化开发

4.1 注册组件

4.1.1 全局注册

// 注册全局组件
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

4.1.2 局部注册

// 定义组件
const MyComponent = {
  template: '<div>A custom component!</div>'
}

// 在 Vue 实例中注册局部组件
new Vue({
  el: '#app',
  components: {
    'my-component': MyComponent
  }
})

4.2 组件通信

4.2.1 Props 向下传递

// 父组件
const ParentComponent = {
  template: '<child-component :message="parentMessage"></child-component>',
  data() {
    return {
      parentMessage: 'Hello from parent'
    }
  }
}

// 子组件
const ChildComponent = {
  props: ['message'],
  template: '<div>{{ message }}</div>'
}

4.2.2 事件向上传递

// 子组件
const ChildComponent = {
  template: '<button @click="$emit(\'child-click\', \'Hello from child\')">Click me</button>'
}

// 父组件
const ParentComponent = {
  template: '<child-component @child-click="handleChildClick"></child-component>',
  methods: {
    handleChildClick: function (message) {
      console.log(message) // 输出: Hello from child
    }
  }
}

4.2.3 插槽

// 子组件
const ChildComponent = {
  template: '<div><slot></slot></div>'
}

// 父组件
const ParentComponent = {
  template: '<child-component>Content from parent</child-component>'
}

5. 高级功能

5.1 自定义指令

// 注册全局自定义指令
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时...
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

// 在模板中使用
<input v-focus>

5.2 过滤器

// 注册全局过滤器
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

// 在模板中使用
<div>{{ message | capitalize }}</div>

5.3 混入

// 定义混入对象
const myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 使用混入
new Vue({
  mixins: [myMixin]
})

5.4 插件

// 定义插件
const MyPlugin = {
  install(Vue, options) {
    // 添加全局方法或属性
    Vue.myGlobalMethod = function () {
      // 逻辑...
    }

    // 添加全局资源
    Vue.directive('my-directive', {
      bind(el, binding, vnode, oldVnode) {
        // 逻辑...
      }
    })

    // 注入组件选项
    Vue.mixin({
      created: function () {
        // 逻辑...
      }
    })

    // 添加实例方法
    Vue.prototype.$myMethod = function (methodOptions) {
      // 逻辑...
    }
  }
}

// 使用插件
Vue.use(MyPlugin)

5.5 过渡动画

<!-- 单个元素的过渡 -->
<transition name="fade">
  <div v-if="show">Hello</div>
</transition>

<!-- 列表的过渡 -->
<transition-group name="list" tag="ul">
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</transition-group>
/* 淡入淡出效果 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}

.fade-enter,
.fade-leave-to
/* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

/* 列表过渡效果 */
.list-enter-active,
.list-leave-active {
  transition: all 0.5s;
}

.list-enter,
.list-leave-to
/* .list-leave-active below version 2.1.8 */ {
  opacity: 0;
  transform: translateX(30px);
}

5.6 响应式原理

Vue 的响应式系统基于 Object.defineProperty() 方法,它会遍历 data 对象的所有属性,并使用 Object.defineProperty() 将这些属性转换为 getter/setter。当属性被访问或修改时,Vue 会追踪依赖并触发更新。

// 简化版的响应式原理
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      console.log('get value')
      return val
    },
    set: function reactiveSetter(newVal) {
      if (newVal === val) return
      console.log('set value:', newVal)
      val = newVal
    }
  })
}

const obj = {}
defineReactive(obj, 'name', 'Vue')

console.log(obj.name) // 输出: get value, Vue
obj.name = 'React'    // 输出: set value: React

6. Vue 3 新特性

6.1 Composition API

Composition API 是 Vue 3 中引入的新 API,它提供了一种新的组织组件逻辑的方式,使得代码更加可维护和可复用。

import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    // 响应式数据
    const count = ref(0)
    
    // 计算属性
    const doubleCount = computed(() => count.value * 2)
    
    // 方法
    const increment = () => {
      count.value++
    }
    
    // 生命周期钩子
    onMounted(() => {
      console.log('Component mounted')
    })
    
    // 返回暴露给模板的内容
    return {
      count,
      doubleCount,
      increment
    }
  }
}

6.2 Teleport

Teleport 允许你将组件的内容渲染到 DOM 中的任何位置,而不仅仅是组件的父级元素内。

import { ref } from 'vue'

export default {
  setup() {
    const showModal = ref(false)
    
    return {
      showModal
    }
  }
}
<template>
  <button @click="showModal = true">Show Modal</button>
  
  <teleport to="body">
    <div v-if="showModal" class="modal">
      <div class="modal-content">
        <h2>Modal</h2>
        <p>Modal content</p>
        <button @click="showModal = false">Close</button>
      </div>
    </div>
  </teleport>
</template>

6.3 Fragments

Vue 3 允许组件返回多个根节点,而不需要用一个额外的 div 包裹。

export default {
  template: `
    <div>First root</div>
    <div>Second root</div>
  `
}

6.4 其他新特性

  • 更好的 TypeScript 支持:Vue 3 是用 TypeScript 重写的,提供了更好的类型推断
  • Suspense:用于处理异步组件的加载状态
  • Vue Router 4:与 Vue 3 兼容的路由库
  • Pinia:Vue 3 的官方状态管理库,替代 Vuex
  • Vite:Vue 3 官方推荐的构建工具

7. 实际应用场景

7.1 表单处理

new Vue({
  el: '#app',
  data: {
    form: {
      username: '',
      password: ''
    }
  },
  methods: {
    submitForm: function () {
      console.log('Form submitted:', this.form)
      // 处理表单提交逻辑
    }
  }
})
<form @submit.prevent="submitForm">
  <div>
    <label>Username:</label>
    <input v-model="form.username" type="text">
  </div>
  <div>
    <label>Password:</label>
    <input v-model="form.password" type="password">
  </div>
  <button type="submit">Submit</button>
</form>

7.2 数据获取

new Vue({
  el: '#app',
  data: {
    users: [],
    loading: true,
    error: null
  },
  mounted: function () {
    this.fetchUsers()
  },
  methods: {
    fetchUsers: function () {
      fetch('https://jsonplaceholder.typicode.com/users')
        .then(response => response.json())
        .then(data => {
          this.users = data
          this.loading = false
        })
        .catch(error => {
          this.error = error.message
          this.loading = false
        })
    }
  }
})
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<ul v-else>
  <li v-for="user in users" :key="user.id">
    {{ user.name }} ({{ user.email }})
  </li>
</ul>

7.3 路由

使用 Vue Router 来处理应用的路由。

# 安装 Vue Router
npm install vue-router
// 引入 Vue 和 Vue Router
import Vue from 'vue'
import VueRouter from 'vue-router'

// 注册 Vue Router
Vue.use(VueRouter)

// 定义路由组件
const Home = {
  template: '<div>Home</div>'
}

const About = {
  template: '<div>About</div>'
}

// 创建路由实例
const router = new VueRouter({
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
})

// 创建 Vue 实例并挂载路由
new Vue({
  el: '#app',
  router
})
<div id="app">
  <router-link to="/">Home</router-link>
  <router-link to="/about">About</router-link>
  <router-view></router-view>
</div>

7.4 状态管理

使用 Vuex 来管理应用的状态。

# 安装 Vuex
npm install vuex
// 引入 Vue 和 Vuex
import Vue from 'vue'
import Vuex from 'vuex'

// 注册 Vuex
Vue.use(Vuex)

// 创建 store
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment')
      }, 1000)
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
})

// 创建 Vue 实例并挂载 store
new Vue({
  el: '#app',
  store,
  computed: {
    count() {
      return this.$store.state.count
    },
    doubleCount() {
      return this.$store.getters.doubleCount
    }
  },
  methods: {
    increment() {
      this.$store.commit('increment')
    },
    incrementAsync() {
      this.$store.dispatch('incrementAsync')
    }
  }
})
<div id="app">
  <p>Count: {{ count }}</p>
  <p>Double Count: {{ doubleCount }}</p>
  <button @click="increment">Increment</button>
  <button @click="incrementAsync">Increment Async</button>
</div>

8. 代码优化建议

8.1 使用计算属性而非方法

对于需要基于响应式数据计算的值,使用计算属性而不是方法,因为计算属性会缓存结果,只有当依赖变化时才会重新计算。

// 推荐
computed: {
  fullName() {
    return this.firstName + ' ' + this.lastName
  }
}

// 不推荐
methods: {
  getFullName() {
    return this.firstName + ' ' + this.lastName
  }
}

8.2 合理使用 v-if 和 v-show

对于频繁切换的元素,使用 v-show 而不是 v-if,因为 v-show 只是切换元素的 display 属性,而 v-if 会销毁和重建元素。

<!-- 频繁切换时使用 -->
<div v-show="isVisible">Visible</div>

<!-- 不频繁切换时使用 -->
<div v-if="isVisible">Visible</div>

8.3 使用 key 属性

在使用 v-for 渲染列表时,总是添加 key 属性,并且使用唯一且稳定的值作为 key,避免使用索引作为 key(除非列表是静态的)。

<!-- 推荐 -->
<li v-for="item in items" :key="item.id">{{ item.text }}</li>

<!-- 不推荐 -->
<li v-for="(item, index) in items" :key="index">{{ item.text }}</li>

8.4 避免在模板中使用复杂表达式

对于复杂的表达式,将其移到计算属性或方法中。

// 推荐
computed: {
  isEligible() {
    return this.age >= 18 && this.hasPermission
  }
}

// 在模板中使用
<div v-if="isEligible">Eligible</div>

// 不推荐
<div v-if="age >= 18 && hasPermission">Eligible</div>

8.5 使用防抖和节流

对于频繁触发的事件,如输入、滚动等,使用防抖或节流来优化性能。

// 防抖
methods: {
  search: _.debounce(function (query) {
    // 搜索逻辑
  }, 300)
}

// 节流
methods: {
  handleScroll: _.throttle(function () {
    // 滚动逻辑
  }, 100)
}

8.6 合理使用 watch

对于需要响应数据变化执行副作用的场景,使用 watch。但要注意,对于复杂的计算,优先使用计算属性。

watch: {
  // 监听单个属性
  message(newValue, oldValue) {
    // 执行副作用
  },
  
  // 监听对象的所有属性
  user: {
    handler(newValue, oldValue) {
      // 执行副作用
    },
    deep: true
  }
}

8.7 组件拆分

将大型组件拆分为更小、更专注的组件,提高代码的可维护性和可测试性。

8.8 使用 TypeScript

对于大型应用,使用 TypeScript 来提高代码的可维护性和类型安全性。

8.9 优化首屏加载速度

  • 代码分割:使用动态导入来分割代码
  • 懒加载:懒加载非关键资源
  • 缓存:合理使用缓存策略
  • 压缩:压缩 JavaScript、CSS 和 HTML 文件

8.10 合理使用 Vuex

对于复杂的状态管理,使用 Vuex。但对于简单的应用,使用组件的状态即可,避免过度使用 Vuex。

9. 参考资源

10. 总结

Vue.js 是一个功能强大、灵活且高效的前端框架,它通过响应式数据绑定、组件化开发、虚拟 DOM 等特性,使得构建复杂的单页应用变得更加简单和可维护。

通过本教程的学习,你应该已经掌握了 Vue.js 的核心概念和基本使用方法,包括响应式数据绑定、组件化开发、指令系统、计算属性、监听器、生命周期钩子等。同时,你也了解了 Vue.js 的一些高级特性,如自定义指令、过滤器、混入、插件、过渡动画等。

Vue.js 的生态系统非常丰富,有许多优秀的第三方库和工具,如 Vue Router、Vuex、Element UI、Ant Design Vue 等,它们可以帮助你更高效地开发 Vue.js 应用。

作为前端开发的主流框架之一,Vue.js 拥有庞大的社区和丰富的学习资源,如果你在学习或开发过程中遇到问题,可以通过官方文档、社区论坛、GitHub 等渠道寻求帮助。

Vue 3 的发布带来了许多新特性,如 Composition API、Teleport、Fragments 等,这些新特性进一步提升了 Vue.js 的开发体验和性能。

希望本教程对你有所帮助,祝你在 Vue.js 开发之路上取得成功!

« 上一篇 Vue.js 教程 - 渐进式 JavaScript 框架 下一篇 » HTMX 中文教程