第3课:Vue3 基础语法

【腾讯云】语音识别准确率高,支持多语种,多场景,限时特惠,最低14.9元起

推广

【腾讯云】语音识别准确率高,支持多语种,多场景,限时特惠,最低14.9元起

Vue3 基础语法

模板语法

文本插值

<template>
  <div>
    <!-- 基本插值 -->
    <p>{{ message }}</p>
    
    <!-- 表达式 -->
    <p>{{ number + 1 }}</p>
    <p>{{ ok ? 'YES' : 'NO' }}</p>
    <p>{{ message.split('').reverse().join('') }}</p>
    
    <!-- 函数调用 -->
    <p>{{ formatMessage(message) }}</p>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const message = ref('Hello Vue3!')
    const number = ref(42)
    const ok = ref(true)
    
    const formatMessage = (msg) => {
      return msg.toUpperCase()
    }
    
    return {
      message,
      number,
      ok,
      formatMessage
    }
  }
}
</script>

原始HTML

<template>
  <div>
    <!-- 普通插值会转义HTML -->
    <p>{{ rawHtml }}</p>
    
    <!-- v-html指令输出原始HTML -->
    <p v-html="rawHtml"></p>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const rawHtml = ref('<span style="color: red">这是红色文本</span>')
    
    return {
      rawHtml
    }
  }
}
</script>

响应式基础

ref和reactive

<template>
  <div>
    <h2>ref示例</h2>
    <p>计数: {{ count }}</p>
    <button @click="increment">增加</button>
    
    <h2>reactive示例</h2>
    <p>姓名: {{ user.name }}</p>
    <p>年龄: {{ user.age }}</p>
    <button @click="updateUser">更新用户</button>
    
    <h2>数组示例</h2>
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    <button @click="addItem">添加项目</button>
  </div>
</template>

<script>
import { ref, reactive } from 'vue'

export default {
  setup() {
    // ref:用于基本类型
    const count = ref(0)
    
    // reactive:用于对象类型
    const user = reactive({
      name: '张三',
      age: 25
    })
    
    // reactive:用于数组
    const items = reactive([
      { id: 1, name: '项目1' },
      { id: 2, name: '项目2' }
    ])
    
    // 方法
    const increment = () => {
      count.value++
    }
    
    const updateUser = () => {
      user.name = '李四'
      user.age = 30
    }
    
    const addItem = () => {
      const newId = items.length + 1
      items.push({
        id: newId,
        name: `项目${newId}`
      })
    }
    
    return {
      count,
      user,
      items,
      increment,
      updateUser,
      addItem
    }
  }
}
</script>

computed计算属性

<template>
  <div>
    <h2>计算属性示例</h2>
    <p>原始消息: {{ message }}</p>
    <p>反转消息: {{ reversedMessage }}</p>
    
    <h2>购物车示例</h2>
    <div v-for="item in cart" :key="item.id">
      <span>{{ item.name }} - ¥{{ item.price }} x {{ item.quantity }}</span>
    </div>
    <p><strong>总价: ¥{{ totalPrice }}</strong></p>
    
    <button @click="addToCart">添加商品</button>
  </div>
</template>

<script>
import { ref, reactive, computed } from 'vue'

export default {
  setup() {
    const message = ref('Hello Vue3')
    
    // 计算属性
    const reversedMessage = computed(() => {
      return message.value.split('').reverse().join('')
    })
    
    // 购物车数据
    const cart = reactive([
      { id: 1, name: '苹果', price: 5, quantity: 2 },
      { id: 2, name: '香蕉', price: 3, quantity: 3 }
    ])
    
    // 计算总价
    const totalPrice = computed(() => {
      return cart.reduce((total, item) => {
        return total + item.price * item.quantity
      }, 0)
    })
    
    const addToCart = () => {
      cart.push({
        id: cart.length + 1,
        name: '橙子',
        price: 4,
        quantity: 1
      })
    }
    
    return {
      message,
      reversedMessage,
      cart,
      totalPrice,
      addToCart
    }
  }
}
</script>

指令

v-bind属性绑定

<template>
  <div>
    <!-- 基本属性绑定 -->
    <img v-bind:src="imageSrc" v-bind:alt="imageAlt">
    
    <!-- 简写语法 -->
    <img :src="imageSrc" :alt="imageAlt">
    
    <!-- 动态属性名 -->
    <button :[attributeName]="attributeValue">动态属性</button>
    
    <!-- 绑定对象 -->
    <div v-bind="objectOfAttrs">多个属性</div>
    
    <!-- 类绑定 -->
    <div :class="{ active: isActive, 'text-danger': hasError }">
      类绑定示例
    </div>
    
    <!-- 数组类绑定 -->
    <div :class="[activeClass, errorClass]">数组类绑定</div>
    
    <!-- 样式绑定 -->
    <div :style="{ color: textColor, fontSize: fontSize + 'px' }">
      样式绑定示例
    </div>
    
    <!-- 数组样式绑定 -->
    <div :style="[baseStyles, overridingStyles]">数组样式绑定</div>
  </div>
</template>

<script>
import { ref, reactive } from 'vue'

export default {
  setup() {
    const imageSrc = ref('https://via.placeholder.com/150')
    const imageAlt = ref('示例图片')
    const attributeName = ref('title')
    const attributeValue = ref('这是一个动态属性')
    
    const objectOfAttrs = reactive({
      id: 'dynamic-id',
      class: 'dynamic-class'
    })
    
    const isActive = ref(true)
    const hasError = ref(false)
    const activeClass = ref('active')
    const errorClass = ref('text-danger')
    
    const textColor = ref('red')
    const fontSize = ref(16)
    
    const baseStyles = reactive({
      color: 'blue',
      fontSize: '14px'
    })
    
    const overridingStyles = reactive({
      fontWeight: 'bold'
    })
    
    return {
      imageSrc,
      imageAlt,
      attributeName,
      attributeValue,
      objectOfAttrs,
      isActive,
      hasError,
      activeClass,
      errorClass,
      textColor,
      fontSize,
      baseStyles,
      overridingStyles
    }
  }
}
</script>

<style scoped>
.active {
  background-color: #42b983;
  color: white;
}

.text-danger {
  color: #dc3545;
}
</style>

v-on事件处理

<template>
  <div>
    <!-- 基本事件监听 -->
    <button v-on:click="handleClick">点击我</button>
    
    <!-- 简写语法 -->
    <button @click="handleClick">点击我简写</button>
    
    <!-- 内联处理器 -->
    <button @click="count++">计数: {{ count }}</button>
    
    <!-- 传递参数 -->
    <button @click="greet('Vue3')">问候</button>
    
    <!-- 访问事件对象 -->
    <button @click="handleEvent">事件对象</button>
    
    <!-- 传递参数和事件对象 -->
    <button @click="handleWithEvent('参数', $event)">参数和事件</button>
    
    <!-- 事件修饰符 -->
    <form @submit.prevent="onSubmit">
      <input type="text" v-model="inputValue">
      <button type="submit">提交</button>
    </form>
    
    <!-- 键盘事件 -->
    <input @keyup.enter="onEnter" @keyup.esc="onEsc" placeholder="按Enter或Esc">
    
    <!-- 鼠标事件修饰符 -->
    <button @click.left="onLeftClick" @click.right.prevent="onRightClick">
      鼠标事件
    </button>
    
    <!-- 多个事件处理器 -->
    <button @click="one($event), two($event)">多个处理器</button>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const inputValue = ref('')
    
    const handleClick = () => {
      alert('按钮被点击了!')
    }
    
    const greet = (name) => {
      alert(`Hello, ${name}!`)
    }
    
    const handleEvent = (event) => {
      console.log('事件对象:', event)
      console.log('目标元素:', event.target)
    }
    
    const handleWithEvent = (message, event) => {
      console.log('消息:', message)
      console.log('事件:', event)
    }
    
    const onSubmit = () => {
      console.log('表单提交:', inputValue.value)
    }
    
    const onEnter = () => {
      console.log('按下了Enter键')
    }
    
    const onEsc = () => {
      console.log('按下了Esc键')
    }
    
    const onLeftClick = () => {
      console.log('左键点击')
    }
    
    const onRightClick = () => {
      console.log('右键点击')
    }
    
    const one = (event) => {
      console.log('处理器1', event)
    }
    
    const two = (event) => {
      console.log('处理器2', event)
    }
    
    return {
      count,
      inputValue,
      handleClick,
      greet,
      handleEvent,
      handleWithEvent,
      onSubmit,
      onEnter,
      onEsc,
      onLeftClick,
      onRightClick,
      one,
      two
    }
  }
}
</script>

v-model双向绑定

<template>
  <div>
    <!-- 文本输入 -->
    <div>
      <label>文本输入:</label>
      <input v-model="text" placeholder="输入文本">
      <p>输入的内容: {{ text }}</p>
    </div>
    
    <!-- 多行文本 -->
    <div>
      <label>多行文本:</label>
      <textarea v-model="textarea" placeholder="输入多行文本"></textarea>
      <p>多行文本: {{ textarea }}</p>
    </div>
    
    <!-- 复选框 -->
    <div>
      <label>
        <input type="checkbox" v-model="checked">
        复选框: {{ checked }}
      </label>
    </div>
    
    <!-- 多个复选框 -->
    <div>
      <label>选择爱好:</label>
      <label><input type="checkbox" value="读书" v-model="hobbies"> 读书</label>
      <label><input type="checkbox" value="运动" v-model="hobbies"> 运动</label>
      <label><input type="checkbox" value="音乐" v-model="hobbies"> 音乐</label>
      <p>选择的爱好: {{ hobbies }}</p>
    </div>
    
    <!-- 单选按钮 -->
    <div>
      <label>选择性别:</label>
      <label><input type="radio" value="男" v-model="gender"> </label>
      <label><input type="radio" value="女" v-model="gender"> </label>
      <p>选择的性别: {{ gender }}</p>
    </div>
    
    <!-- 选择框 -->
    <div>
      <label>选择城市:</label>
      <select v-model="selectedCity">
        <option disabled value="">请选择</option>
        <option value="北京">北京</option>
        <option value="上海">上海</option>
        <option value="广州">广州</option>
        <option value="深圳">深圳</option>
      </select>
      <p>选择的城市: {{ selectedCity }}</p>
    </div>
    
    <!-- 多选选择框 -->
    <div>
      <label>选择多个城市:</label>
      <select v-model="selectedCities" multiple>
        <option value="北京">北京</option>
        <option value="上海">上海</option>
        <option value="广州">广州</option>
        <option value="深圳">深圳</option>
      </select>
      <p>选择的城市: {{ selectedCities }}</p>
    </div>
    
    <!-- 修饰符 -->
    <div>
      <label>懒更新 (.lazy):</label>
      <input v-model.lazy="lazyText" placeholder="失去焦点时更新">
      <p>{{ lazyText }}</p>
    </div>
    
    <div>
      <label>数字类型 (.number):</label>
      <input v-model.number="numberValue" type="number">
      <p>: {{ numberValue }}, 类型: {{ typeof numberValue }}</p>
    </div>
    
    <div>
      <label>去除空格 (.trim):</label>
      <input v-model.trim="trimText" placeholder="自动去除首尾空格">
      <p>"{{ trimText }}"</p>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const text = ref('')
    const textarea = ref('')
    const checked = ref(false)
    const hobbies = ref([])
    const gender = ref('')
    const selectedCity = ref('')
    const selectedCities = ref([])
    const lazyText = ref('')
    const numberValue = ref(0)
    const trimText = ref('')
    
    return {
      text,
      textarea,
      checked,
      hobbies,
      gender,
      selectedCity,
      selectedCities,
      lazyText,
      numberValue,
      trimText
    }
  }
}
</script>

<style scoped>
div {
  margin-bottom: 15px;
}

label {
  display: inline-block;
  margin-right: 10px;
}

input, textarea, select {
  margin: 5px;
  padding: 5px;
}

textarea {
  width: 200px;
  height: 60px;
}

select[multiple] {
  height: 80px;
}
</style>

条件渲染

v-if、v-else-if、v-else

<template>
  <div>
    <h2>条件渲染示例</h2>
    
    <!-- 基本条件渲染 -->
    <div>
      <button @click="toggleShow">切换显示</button>
      <p v-if="isShow">这段文字会显示/隐藏</p>
    </div>
    
    <!-- 多条件判断 -->
    <div>
      <label>选择类型:</label>
      <select v-model="type">
        <option value="A">类型A</option>
        <option value="B">类型B</option>
        <option value="C">类型C</option>
        <option value="">其他</option>
      </select>
      
      <div v-if="type === 'A'">
        <h3>类型A的内容</h3>
        <p>这是类型A的详细信息</p>
      </div>
      <div v-else-if="type === 'B'">
        <h3>类型B的内容</h3>
        <p>这是类型B的详细信息</p>
      </div>
      <div v-else-if="type === 'C'">
        <h3>类型C的内容</h3>
        <p>这是类型C的详细信息</p>
      </div>
      <div v-else>
        <h3>未知类型</h3>
        <p>请选择一个有效的类型</p>
      </div>
    </div>
    
    <!-- template条件渲染 -->
    <div>
      <button @click="toggleGroup">切换组显示</button>
      <template v-if="showGroup">
        <h3>这是一组元素</h3>
        <p>第一个段落</p>
        <p>第二个段落</p>
        <p>第三个段落</p>
      </template>
    </div>
    
    <!-- v-show vs v-if -->
    <div>
      <button @click="toggleVisibility">切换可见性</button>
      <p v-if="isVisible">v-if: {{ isVisible ? '显示' : '隐藏' }}</p>
      <p v-show="isVisible">v-show: {{ isVisible ? '显示' : '隐藏' }}</p>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const isShow = ref(true)
    const type = ref('A')
    const showGroup = ref(true)
    const isVisible = ref(true)
    
    const toggleShow = () => {
      isShow.value = !isShow.value
    }
    
    const toggleGroup = () => {
      showGroup.value = !showGroup.value
    }
    
    const toggleVisibility = () => {
      isVisible.value = !isVisible.value
    }
    
    return {
      isShow,
      type,
      showGroup,
      isVisible,
      toggleShow,
      toggleGroup,
      toggleVisibility
    }
  }
}
</script>

列表渲染

v-for指令

<template>
  <div>
    <h2>列表渲染示例</h2>
    
    <!-- 遍历数组 -->
    <div>
      <h3>水果列表</h3>
      <ul>
        <li v-for="(fruit, index) in fruits" :key="fruit.id">
          {{ index + 1 }}. {{ fruit.name }} - ¥{{ fruit.price }}
        </li>
      </ul>
    </div>
    
    <!-- 遍历对象 -->
    <div>
      <h3>用户信息</h3>
      <ul>
        <li v-for="(value, key) in user" :key="key">
          {{ key }}: {{ value }}
        </li>
      </ul>
    </div>
    
    <!-- 遍历对象包含索引 -->
    <div>
      <h3>用户信息带索引</h3>
      <ul>
        <li v-for="(value, key, index) in user" :key="key">
          {{ index + 1 }}. {{ key }}: {{ value }}
        </li>
      </ul>
    </div>
    
    <!-- 遍历数字 -->
    <div>
      <h3>数字序列</h3>
      <span v-for="n in 10" :key="n">{{ n }} </span>
    </div>
    
    <!-- 嵌套循环 -->
    <div>
      <h3>嵌套列表</h3>
      <div v-for="category in categories" :key="category.id">
        <h4>{{ category.name }}</h4>
        <ul>
          <li v-for="item in category.items" :key="item.id">
            {{ item.name }}
          </li>
        </ul>
      </div>
    </div>
    
    <!-- 动态列表操作 -->
    <div>
      <h3>动态列表</h3>
      <button @click="addItem">添加项目</button>
      <button @click="removeItem">删除项目</button>
      <button @click="shuffleItems">打乱顺序</button>
      
      <ul>
        <li v-for="item in dynamicItems" :key="item.id">
          {{ item.name }}
          <button @click="removeSpecificItem(item.id)">删除</button>
        </li>
      </ul>
    </div>
    
    <!-- 过滤和排序 -->
    <div>
      <h3>过滤和排序</h3>
      <input v-model="searchText" placeholder="搜索...">
      <button @click="toggleSort">切换排序</button>
      
      <ul>
        <li v-for="item in filteredAndSortedItems" :key="item.id">
          {{ item.name }} - {{ item.score }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import { ref, reactive, computed } from 'vue'

export default {
  setup() {
    // 基础数据
    const fruits = reactive([
      { id: 1, name: '苹果', price: 5 },
      { id: 2, name: '香蕉', price: 3 },
      { id: 3, name: '橙子', price: 4 }
    ])
    
    const user = reactive({
      name: '张三',
      age: 25,
      email: 'zhangsan@example.com',
      city: '北京'
    })
    
    const categories = reactive([
      {
        id: 1,
        name: '水果',
        items: [
          { id: 1, name: '苹果' },
          { id: 2, name: '香蕉' }
        ]
      },
      {
        id: 2,
        name: '蔬菜',
        items: [
          { id: 3, name: '白菜' },
          { id: 4, name: '萝卜' }
        ]
      }
    ])
    
    // 动态列表
    const dynamicItems = reactive([
      { id: 1, name: '项目1' },
      { id: 2, name: '项目2' },
      { id: 3, name: '项目3' }
    ])
    
    let nextId = 4
    
    const addItem = () => {
      dynamicItems.push({
        id: nextId++,
        name: `项目${nextId - 1}`
      })
    }
    
    const removeItem = () => {
      if (dynamicItems.length > 0) {
        dynamicItems.pop()
      }
    }
    
    const removeSpecificItem = (id) => {
      const index = dynamicItems.findIndex(item => item.id === id)
      if (index > -1) {
        dynamicItems.splice(index, 1)
      }
    }
    
    const shuffleItems = () => {
      for (let i = dynamicItems.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [dynamicItems[i], dynamicItems[j]] = [dynamicItems[j], dynamicItems[i]]
      }
    }
    
    // 过滤和排序
    const searchText = ref('')
    const sortAsc = ref(true)
    
    const itemsWithScore = reactive([
      { id: 1, name: 'Alice', score: 85 },
      { id: 2, name: 'Bob', score: 92 },
      { id: 3, name: 'Charlie', score: 78 },
      { id: 4, name: 'David', score: 95 }
    ])
    
    const filteredAndSortedItems = computed(() => {
      let filtered = itemsWithScore.filter(item =>
        item.name.toLowerCase().includes(searchText.value.toLowerCase())
      )
      
      return filtered.sort((a, b) => {
        if (sortAsc.value) {
          return a.score - b.score
        } else {
          return b.score - a.score
        }
      })
    })
    
    const toggleSort = () => {
      sortAsc.value = !sortAsc.value
    }
    
    return {
      fruits,
      user,
      categories,
      dynamicItems,
      addItem,
      removeItem,
      removeSpecificItem,
      shuffleItems,
      searchText,
      filteredAndSortedItems,
      toggleSort
    }
  }
}
</script>

<style scoped>
div {
  margin-bottom: 20px;
}

button {
  margin: 5px;
  padding: 5px 10px;
}

input {
  margin: 5px;
  padding: 5px;
}

ul {
  list-style-type: disc;
  margin-left: 20px;
}

li {
  margin: 5px 0;
}
</style>

总结

本课我们学习了Vue3的基础语法:

  1. 模板语法:文本插值、表达式、原始HTML
  2. 响应式基础:ref、reactive、computed
  3. 指令系统:v-bind、v-on、v-model
  4. 条件渲染:v-if、v-else-if、v-else、v-show
  5. 列表渲染:v-for遍历数组、对象、数字

下一课预告

在下一课中,我们将深入学习Vue3的Composition API,包括:

  • setup函数详解
  • 生命周期钩子
  • 响应式API进阶
  • 逻辑复用和组合

💡 小贴士:Vue3的响应式系统基于Proxy,比Vue2的Object.defineProperty更强大。理解ref和reactive的区别是掌握Vue3的关键。

Vue3 + TypeScript 企业级项目实战

课程推荐

Vue3 + TypeScript 企业级项目实战
Python 全栈开发工程师培训

热门课程

Python 全栈开发工程师培训

📚 文章对你有帮助?请关注我的公众号,万分感谢!

获取更多优质技术文章,第一时间掌握最新技术动态

关注公众号

关注公众号

第一时间获取最新技术文章

添加微信

添加微信

技术交流 · 问题答疑 · 学习指导

评论讨论

欢迎留下你的想法和建议