第2章 Vue.js基础语法

第3节:Vue实例与模板语法

Vue.js的核心是创建Vue实例,通过实例来管理数据和DOM。在本节中,我们将学习Vue实例的创建与配置,以及模板语法的使用。

2.3.1 Vue应用实例创建与配置

Vue 3中,我们使用createApp函数来创建Vue应用实例。一个Vue应用由一个通过createApp函数创建的根实例,以及可选的嵌套组件树组成。

选项式API创建Vue实例

// main.js
import { createApp } from 'vue'
import App from './App.vue'

// 创建Vue应用实例
const app = createApp({
  // 数据选项
  data() {
    return {
      message: 'Hello Vue!',
      count: 0
    }
  },
  // 方法选项
  methods: {
    reverseMessage() {
      this.message = this.message.split('').reverse().join('')
    },
    increment() {
      this.count++
    }
  },
  // 计算属性
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  // 生命周期钩子
  mounted() {
    console.log('Vue实例已挂载')
  }
})

// 挂载应用到DOM
app.mount('#app')

应用配置选项

Vue应用实例提供了一些配置选项,用于自定义应用的行为:

const app = createApp({
  /* 根组件选项 */
})

// 配置全局属性
app.config.globalProperties.$api = axios
app.config.globalProperties.$formatDate = formatDate

// 配置错误处理
app.config.errorHandler = (err, vm, info) => {
  console.error('Vue错误:', err)
  console.error('组件:', vm)
  console.error('错误信息:', info)
}

// 配置警告处理
app.config.warnHandler = (msg, vm, trace) => {
  console.warn('Vue警告:', msg)
  console.warn('组件:', vm)
  console.warn('调用栈:', trace)
}

// 挂载应用
app.mount('#app')

2.3.2 模板插值语法深度解析

Vue的模板语法允许我们将数据绑定到DOM中,主要有以下几种插值方式:

1. 文本插值:{{ }}

最基本的数据绑定形式是文本插值,使用双大括号:

<template>
  <h1>{{ message }}</h1>
  <p>{{ count }}</p>
  <p>{{ count + 1 }}</p>
  <p>{{ message.split('').reverse().join('') }}</p>
</template>

2. 原始HTML:v-html指令

如果需要输出原始HTML,可以使用v-html指令:

<template>
  <div v-html="rawHtml"></div>
</template>

<script>
export default {
  data() {
    return {
      rawHtml: '<h2 style="color: red;">这是红色的标题</h2>'
    }
  }
}
</script>

注意:使用v-html可能会导致XSS攻击,只在可信内容上使用,不要在用户提供的内容上使用。

3. JavaScript表达式使用

在插值中,我们可以使用JavaScript表达式:

<template>
  <!-- 算术运算 -->
  <p>{{ count + 1 }}</p>
  <p>{{ count * 2 }}</p>
  
  <!-- 字符串操作 -->
  <p>{{ message.toUpperCase() }}</p>
  <p>{{ message.split('').reverse().join('') }}</p>
  
  <!-- 三元表达式 -->
  <p>{{ count > 0 ? '正数' : '零' }}</p>
  
  <!-- 数组操作 -->
  <p>{{ [1, 2, 3].map(item => item * 2) }}</p>
  
  <!-- 对象操作 -->
  <p>{{ { name: 'Vue', version: 3 }.name }}</p>
</template>

注意:模板插值只能包含单个表达式,不能包含语句或流控制结构。

2.3.3 指令系统入门

指令是带有v-前缀的特殊属性,用于在表达式的值改变时,将某些行为应用到DOM上。

1. v-bind绑定属性

用于动态绑定HTML属性:

<template>
  <!-- 绑定元素属性 -->
  <img v-bind:src="imageUrl" alt="Vue logo">
  <a v-bind:href="linkUrl">Vue官网</a>
  
  <!-- 简写形式 -->
  <img :src="imageUrl" alt="Vue logo">
  <a :href="linkUrl">Vue官网</a>
  
  <!-- 绑定CSS类 -->
  <div :class="{ active: isActive, 'text-danger': hasError }">状态</div>
  <div :class="[activeClass, errorClass]">状态</div>
  
  <!-- 绑定内联样式 -->
  <div :style="{ color: textColor, fontSize: fontSize + 'px' }">样式绑定</div>
  <div :style="styleObject">样式对象绑定</div>
</template>

<script>
export default {
  data() {
    return {
      imageUrl: 'https://vuejs.org/images/logo.png',
      linkUrl: 'https://vuejs.org/',
      isActive: true,
      hasError: false,
      activeClass: 'active',
      errorClass: 'text-danger',
      textColor: 'red',
      fontSize: 18,
      styleObject: {
        color: 'blue',
        fontSize: '20px'
      }
    }
  }
}
</script>

2. v-on监听事件

用于监听DOM事件:

<template>
  <!-- 监听点击事件 -->
  <button v-on:click="increment">点击增加</button>
  
  <!-- 简写形式 -->
  <button @click="increment">点击增加</button>
  
  <!-- 传递参数 -->
  <button @click="add(5)">增加5</button>
  
  <!-- 访问事件对象 -->
  <button @click="handleClick">点击事件</button>
  
  <!-- 使用事件修饰符 -->
  <button @click.stop="handleClick">阻止冒泡</button>
  <form @submit.prevent="handleSubmit">提交表单</form>
  <a @click.prevent href="https://vuejs.org/">阻止默认跳转</a>
  
  <!-- 键修饰符 -->
  <input @keyup.enter="submitForm" placeholder="按回车提交">
  <input @keyup.esc="clearInput" placeholder="按ESC清空">
  
  <!-- 系统修饰符 -->
  <button @click.ctrl="ctrlClick">按住Ctrl点击</button>
  <button @click.alt="altClick">按住Alt点击</button>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    },
    add(amount) {
      this.count += amount
    },
    handleClick(event) {
      console.log('事件对象:', event)
      console.log('当前元素:', event.target)
    },
    handleSubmit() {
      console.log('表单提交')
    },
    submitForm() {
      console.log('表单提交')
    },
    clearInput() {
      console.log('清空输入')
    },
    ctrlClick() {
      console.log('Ctrl+点击')
    },
    altClick() {
      console.log('Alt+点击')
    }
  }
}
</script>

3. v-model双向绑定

用于在表单元素上创建双向数据绑定:

<template>
  <!-- 文本输入 -->
  <input v-model="message" type="text" placeholder="输入消息">
  <p>消息:{{ message }}</p>
  
  <!-- 多行文本 -->
  <textarea v-model="content" rows="4"></textarea>
  
  <!-- 复选框 -->
  <input v-model="checked" type="checkbox">
  <p>是否选中:{{ checked }}</p>
  
  <!-- 多个复选框绑定到数组 -->
  <input v-model="checkedFruits" type="checkbox" value="apple"> 苹果
  <input v-model="checkedFruits" type="checkbox" value="banana"> 香蕉
  <input v-model="checkedFruits" type="checkbox" value="orange"> 橙子
  <p>选中的水果:{{ checkedFruits }}</p>
  
  <!-- 单选按钮 -->
  <input v-model="picked" type="radio" value="A"> 选项A
  <input v-model="picked" type="radio" value="B"> 选项B
  <p>选中的选项:{{ picked }}</p>
  
  <!-- 下拉选择 -->
  <select v-model="selected">
    <option value="">请选择</option>
    <option value="vue">Vue.js</option>
    <option value="react">React</option>
    <option value="angular">Angular</option>
  </select>
  <p>选中的框架:{{ selected }}</p>
  
  <!-- 多选下拉 -->
  <select v-model="selectedFrameworks" multiple>
    <option value="vue">Vue.js</option>
    <option value="react">React</option>
    <option value="angular">Angular</option>
  </select>
  <p>选中的框架:{{ selectedFrameworks }}</p>
</template>

<script>
export default {
  data() {
    return {
      message: '',
      content: '',
      checked: false,
      checkedFruits: [],
      picked: '',
      selected: '',
      selectedFrameworks: []
    }
  }
}
</script>

2.3.4 计算属性与侦听器

计算属性

计算属性是基于它们的依赖进行缓存的,只有在依赖发生变化时才会重新计算。这使得计算属性比方法更高效,尤其是在依赖不变的情况下。

export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    // 计算属性的getter
    fullName() {
      return this.firstName + ' ' + this.lastName
    },
    // 可写的计算属性
    fullNameWithSetter: {
      get() {
        return this.firstName + ' ' + this.lastName
      },
      set(newValue) {
        const parts = newValue.split(' ')
        this.firstName = parts[0]
        this.lastName = parts[1] || ''
      }
    }
  },
  methods: {
    // 方法,每次调用都会重新计算
    getFullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}

计算属性 vs 方法

特性 计算属性 方法
缓存 有,依赖变化才重新计算 无,每次调用都重新计算
调用方式 直接作为属性使用:{{ fullName }} 作为方法调用:{{ getFullName() }}
适用场景 基于现有数据派生新数据 需要传入参数或执行复杂逻辑

侦听器

侦听器用于观察和响应数据的变化,可以执行异步操作或复杂逻辑。

export default {
  data() {
    return {
      question: '',
      answer: '请输入问题',
      user: {
        name: '',
        age: 0
      }
    }
  },
  watch: {
    // 基本侦听
    question(newQuestion, oldQuestion) {
      if (newQuestion.includes('?')) {
        this.getAnswer()
      }
    },
    // 深层侦听对象
    user: {
      handler(newUser, oldUser) {
        console.log('用户信息变化:', newUser)
      },
      deep: true, // 深层侦听
      immediate: true // 立即执行
    },
    // 侦听对象的单个属性
    'user.name'(newName, oldName) {
      console.log('用户名变化:', oldName, '->', newName)
    }
  },
  methods: {
    async getAnswer() {
      this.answer = '思考中...'
      try {
        // 模拟异步请求
        await new Promise(resolve => setTimeout(resolve, 1000))
        this.answer = '这是一个答案'
      } catch (error) {
        this.answer = '请求失败'
      }
    }
  }
}

本章小结

在本节中,我们学习了Vue实例与模板语法的核心内容:

  • Vue应用实例创建与配置
  • 模板插值语法深度解析
    • 文本插值:{{ }}
    • 原始HTML:v-html指令
    • JavaScript表达式使用
  • 指令系统入门
    • v-bind绑定属性
    • v-on监听事件
    • v-model双向绑定
  • 计算属性与侦听器
    • 计算属性的定义与使用
    • 计算属性vs方法
    • 侦听器的使用场景

掌握这些基础内容是学习Vue.js的关键,它们将帮助我们构建更复杂的Vue应用。

« 上一篇 第2节:创建第一个Vue项目 下一篇 » 第4节:条件与列表渲染