【Vue】模板语法与指令

个人主页:Guiat
归属专栏:Vue

文章目录

  • [1. Vue 模板语法基础](#1. Vue 模板语法基础)
    • [1.1 文本插值](#1.1 文本插值)
    • [1.2 原始 HTML](#1.2 原始 HTML)
    • [1.3 属性绑定](#1.3 属性绑定)
  • [2. Vue 指令系统](#2. Vue 指令系统)
    • [2.1 条件渲染](#2.1 条件渲染)
    • [2.2 列表渲染](#2.2 列表渲染)
    • [2.3 事件处理](#2.3 事件处理)
    • [2.4 表单输入绑定](#2.4 表单输入绑定)
  • [3. 计算属性与侦听器](#3. 计算属性与侦听器)
    • [3.1 计算属性](#3.1 计算属性)
    • [3.2 侦听器](#3.2 侦听器)
  • [4. 类与样式绑定](#4. 类与样式绑定)
    • [4.1 绑定 HTML 类](#4.1 绑定 HTML 类)
    • [4.2 绑定内联样式](#4.2 绑定内联样式)
  • [5. 条件渲染进阶](#5. 条件渲染进阶)
    • [5.1 v-if 与模板](#5.1 v-if 与模板)
  • [6. 列表渲染进阶](#6. 列表渲染进阶)
    • [6.1 数组变更检测](#6.1 数组变更检测)
    • [6.2 过滤和排序](#6.2 过滤和排序)
  • [7. 事件处理进阶](#7. 事件处理进阶)
    • [7.1 事件修饰符详解](#7.1 事件修饰符详解)
    • [7.2 按键修饰符](#7.2 按键修饰符)

正文

1. Vue 模板语法基础

Vue 模板语法允许开发者以声明式的方式将数据渲染到 DOM。

1.1 文本插值

html 复制代码
<div id="app">
  <!-- 基本文本插值 -->
  <p>{{ message }}</p>
  
  <!-- 表达式支持 -->
  <p>{{ message.split('').reverse().join('') }}</p>
  
  <!-- 数学运算 -->
  <p>{{ count + 1 }}</p>
  
  <!-- 三元表达式 -->
  <p>{{ isActive ? '激活' : '未激活' }}</p>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: 'Hello Vue!',
        count: 10,
        isActive: true
      }
    }
  }).mount('#app')
</script>

1.2 原始 HTML

html 复制代码
<div id="app">
  <!-- 文本插值会将HTML转义 -->
  <p>{{ rawHtml }}</p>
  
  <!-- v-html 指令用于输出原始HTML -->
  <p v-html="rawHtml"></p>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        rawHtml: '<span style="color: red">这是红色文本</span>'
      }
    }
  }).mount('#app')
</script>

1.3 属性绑定

html 复制代码
<div id="app">
  <!-- 绑定HTML属性 -->
  <div v-bind:id="dynamicId">动态ID元素</div>
  
  <!-- 简写语法 -->
  <img :src="imageSrc" :alt="imageAlt">
  
  <!-- 布尔属性 -->
  <button :disabled="isButtonDisabled">按钮</button>
  
  <!-- 动态绑定多个属性 -->
  <div v-bind="objectOfAttrs">多属性绑定</div>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        dynamicId: 'my-element',
        imageSrc: 'https://example.com/image.jpg',
        imageAlt: '示例图片',
        isButtonDisabled: true,
        objectOfAttrs: {
          id: 'container',
          class: 'wrapper',
          style: 'color: blue'
        }
      }
    }
  }).mount('#app')
</script>

2. Vue 指令系统

2.1 条件渲染

html 复制代码
<div id="app">
  <!-- v-if 条件渲染 -->
  <h1 v-if="awesome">Vue 很棒!</h1>
  <h1 v-else>哦不 😢</h1>
  
  <!-- v-if/v-else-if/v-else 链 -->
  <div v-if="type === 'A'">A类型</div>
  <div v-else-if="type === 'B'">B类型</div>
  <div v-else-if="type === 'C'">C类型</div>
  <div v-else>未知类型</div>
  
  <!-- v-show 切换元素的显示状态 -->
  <h1 v-show="isVisible">使用v-show控制显示</h1>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        awesome: true,
        type: 'B',
        isVisible: true
      }
    }
  }).mount('#app')
</script>

2.2 列表渲染

html 复制代码
<div id="app">
  <!-- 基本列表渲染 -->
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
    </li>
  </ul>
  
  <!-- 带索引的列表渲染 -->
  <ul>
    <li v-for="(item, index) in items" :key="item.id">
      {{ index }} - {{ item.name }}
    </li>
  </ul>
  
  <!-- 对象属性遍历 -->
  <ul>
    <li v-for="(value, key, index) in userObject" :key="key">
      {{ index }}. {{ key }}: {{ value }}
    </li>
  </ul>
  
  <!-- 遍历数字范围 -->
  <span v-for="n in 10" :key="n">{{ n }} </span>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        items: [
          { id: 1, name: '苹果' },
          { id: 2, name: '香蕉' },
          { id: 3, name: '橙子' }
        ],
        userObject: {
          name: '张三',
          age: 30,
          city: '上海'
        }
      }
    }
  }).mount('#app')
</script>

2.3 事件处理

html 复制代码
<div id="app">
  <!-- 基本事件绑定 -->
  <button v-on:click="counter++">点击计数: {{ counter }}</button>
  
  <!-- 简写语法 -->
  <button @click="greet">问候</button>
  
  <!-- 内联处理器 -->
  <button @click="say('你好', $event)">说你好</button>
  
  <!-- 事件修饰符 -->
  <a @click.stop.prevent="handleLink">阻止默认行为并停止传播</a>
  
  <!-- 按键修饰符 -->
  <input @keyup.enter="submitForm">
  
  <!-- 系统修饰键 -->
  <div @click.ctrl="handleCtrlClick">Ctrl + 点击</div>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        counter: 0
      }
    },
    methods: {
      greet() {
        alert('你好,Vue!')
      },
      say(message, event) {
        alert(message)
        console.log(event)
      },
      handleLink() {
        console.log('链接被点击,但默认行为被阻止')
      },
      submitForm() {
        console.log('表单提交')
      },
      handleCtrlClick() {
        console.log('Ctrl + 点击被触发')
      }
    }
  }).mount('#app')
</script>

2.4 表单输入绑定

html 复制代码
<div id="app">
  <!-- 文本输入 -->
  <input v-model="message" placeholder="编辑我">
  <p>消息: {{ message }}</p>
  
  <!-- 多行文本 -->
  <textarea v-model="description" placeholder="多行输入"></textarea>
  <p>描述: {{ description }}</p>
  
  <!-- 复选框 -->
  <input type="checkbox" id="checkbox" v-model="checked">
  <label for="checkbox">{{ checked ? '已选中' : '未选中' }}</label>
  
  <!-- 多个复选框 -->
  <div>
    <input type="checkbox" id="apple" value="苹果" v-model="checkedFruits">
    <label for="apple">苹果</label>
    <input type="checkbox" id="banana" value="香蕉" v-model="checkedFruits">
    <label for="banana">香蕉</label>
    <input type="checkbox" id="orange" value="橙子" v-model="checkedFruits">
    <label for="orange">橙子</label>
  </div>
  <p>选中的水果: {{ checkedFruits }}</p>
  
  <!-- 单选按钮 -->
  <div>
    <input type="radio" id="one" value="一" v-model="picked">
    <label for="one">一</label>
    <input type="radio" id="two" value="二" v-model="picked">
    <label for="two">二</label>
  </div>
  <p>选中的值: {{ picked }}</p>
  
  <!-- 选择框 -->
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>北京</option>
    <option>上海</option>
    <option>广州</option>
  </select>
  <p>选中的城市: {{ selected }}</p>
  
  <!-- 修饰符 -->
  <input v-model.lazy="lazyMessage" placeholder="失焦时才更新">
  <p>Lazy消息: {{ lazyMessage }}</p>
  
  <input v-model.number="age" type="number" placeholder="自动转为数字">
  <p>年龄: {{ age }} (类型: {{ typeof age }})</p>
  
  <input v-model.trim="trimmedMessage" placeholder="自动去除首尾空格">
  <p>去除空格后: "{{ trimmedMessage }}"</p>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: '',
        description: '',
        checked: false,
        checkedFruits: [],
        picked: '',
        selected: '',
        lazyMessage: '',
        age: 0,
        trimmedMessage: ''
      }
    }
  }).mount('#app')
</script>

3. 计算属性与侦听器

3.1 计算属性

html 复制代码
<div id="app">
  <!-- 原始数据 -->
  <p>原始消息: "{{ message }}"</p>
  
  <!-- 计算属性 -->
  <p>反转消息: "{{ reversedMessage }}"</p>
  
  <!-- 方法调用 (每次重新渲染都会调用) -->
  <p>方法反转: "{{ reverseMessage() }}"</p>
  
  <!-- 带缓存的计算属性 -->
  <p>全名: {{ fullName }}</p>
  <button @click="firstName = '李'">修改姓</button>
  <button @click="lastName = '四'">修改名</button>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        message: 'Hello Vue!',
        firstName: '张',
        lastName: '三'
      }
    },
    computed: {
      // 计算属性会基于其依赖进行缓存
      reversedMessage() {
        console.log('计算属性被执行')
        return this.message.split('').reverse().join('')
      },
      // 带getter和setter的计算属性
      fullName: {
        get() {
          return this.firstName + this.lastName
        },
        set(newValue) {
          const names = newValue.split(' ')
          this.firstName = names[0]
          this.lastName = names[names.length - 1]
        }
      }
    },
    methods: {
      // 方法不会缓存
      reverseMessage() {
        console.log('方法被调用')
        return this.message.split('').reverse().join('')
      }
    }
  }).mount('#app')
</script>

3.2 侦听器

html 复制代码
<div id="app">
  <p>
    问一个问题: 
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
  
  <p>
    姓: <input v-model="firstName">
    名: <input v-model="lastName">
  </p>
  <p>全名: {{ fullName }}</p>
  
  <!-- 深度监听示例 -->
  <button @click="updateUserInfo">更新用户信息</button>
  <pre>{{ user }}</pre>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        question: '',
        answer: '请输入问题',
        firstName: '张',
        lastName: '三',
        fullName: '张三',
        user: {
          name: '李四',
          address: {
            city: '北京'
          }
        }
      }
    },
    watch: {
      // 简单侦听器
      question(newQuestion, oldQuestion) {
        if (newQuestion.includes('?')) {
          this.getAnswer()
        }
      },
      
      // 侦听多个属性变化
      firstName(newVal) {
        this.fullName = newVal + this.lastName
      },
      lastName(newVal) {
        this.fullName = this.firstName + newVal
      },
      
      // 深度侦听对象变化
      user: {
        handler(newVal) {
          console.log('用户信息变化了', newVal)
        },
        deep: true // 深度监听
      },
      
      // 立即执行的侦听器
      'user.name': {
        handler(newVal) {
          console.log('用户名变化了', newVal)
        },
        immediate: true // 立即执行一次
      }
    },
    methods: {
      getAnswer() {
        this.answer = '思考中...'
        setTimeout(() => {
          this.answer = '这是一个很好的问题!'
        }, 1000)
      },
      updateUserInfo() {
        this.user.address.city = '上海'
      }
    }
  }).mount('#app')
</script>

4. 类与样式绑定

4.1 绑定 HTML 类

html 复制代码
<div id="app">
  <!-- 对象语法 -->
  <div :class="{ active: isActive, 'text-danger': hasError }">
    对象语法绑定类
  </div>
  
  <!-- 数组语法 -->
  <div :class="[activeClass, errorClass]">
    数组语法绑定类
  </div>
  
  <!-- 数组中使用对象 -->
  <div :class="[isActive ? activeClass : '', { error: hasError }]">
    混合语法绑定类
  </div>
  
  <!-- 组件上使用 -->
  <my-component :class="{ active: isActive }"></my-component>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        isActive: true,
        hasError: false,
        activeClass: 'active',
        errorClass: 'text-danger'
      }
    },
    components: {
      'my-component': {
        template: '<p class="base-class">组件</p>'
        // 最终渲染: <p class="base-class active">组件</p>
      }
    }
  }).mount('#app')
</script>

4.2 绑定内联样式

html 复制代码
<div id="app">
  <!-- 对象语法 -->
  <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">
    对象语法绑定样式
  </div>
  
  <!-- 直接绑定对象 -->
  <div :style="styleObject">
    绑定样式对象
  </div>
  
  <!-- 数组语法 -->
  <div :style="[baseStyles, overridingStyles]">
    数组语法绑定多个样式对象
  </div>
  
  <!-- 自动添加前缀 -->
  <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">
    自动添加浏览器前缀
  </div>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        activeColor: 'red',
        fontSize: 18,
        styleObject: {
          color: 'blue',
          backgroundColor: '#f0f0f0',
          padding: '10px'
        },
        baseStyles: {
          fontWeight: 'bold',
          letterSpacing: '1px'
        },
        overridingStyles: {
          color: 'green',
          textDecoration: 'underline'
        }
      }
    }
  }).mount('#app')
</script>

5. 条件渲染进阶

5.1 v-if 与模板

html 复制代码
<div id="app">
  <!-- 使用template元素包裹多个元素 -->
  <template v-if="loginType === 'username'">
    <label>用户名</label>
    <input placeholder="输入用户名" key="username-input">
  </template>
  <template v-else>
    <label>邮箱</label>
    <input placeholder="输入邮箱" key="email-input">
  </template>
  
  <button @click="toggleLoginType">切换登录类型</button>
  
  <!-- v-if vs v-show -->
  <h1 v-if="showWithIf">v-if: 条件为真时才渲染</h1>
  <h1 v-show="showWithShow">v-show: 始终渲染但条件为假时隐藏</h1>
  
  <button @click="toggleShow">切换显示</button>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        loginType: 'username',
        showWithIf: true,
        showWithShow: true
      }
    },
    methods: {
      toggleLoginType() {
        this.loginType = this.loginType === 'username' ? 'email' : 'username'
      },
      toggleShow() {
        this.showWithIf = !this.showWithIf
        this.showWithShow = !this.showWithShow
      }
    }
  }).mount('#app')
</script>

6. 列表渲染进阶

6.1 数组变更检测

html 复制代码
<div id="app">
  <ul>
    <li v-for="(item, index) in items" :key="item.id">
      {{ item.name }} - {{ item.price }}元
      <button @click="removeItem(index)">删除</button>
    </li>
  </ul>
  
  <div>
    <input v-model="newItemName" placeholder="商品名称">
    <input v-model.number="newItemPrice" type="number" placeholder="价格">
    <button @click="addItem">添加商品</button>
  </div>
  
  <button @click="sortItems">按价格排序</button>
  <button @click="reverseItems">反转列表</button>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        items: [
          { id: 1, name: '苹果', price: 5 },
          { id: 2, name: '香蕉', price: 3 },
          { id: 3, name: '橙子', price: 6 }
        ],
        newItemName: '',
        newItemPrice: 0,
        nextId: 4
      }
    },
    methods: {
      addItem() {
        if (this.newItemName && this.newItemPrice > 0) {
          this.items.push({
            id: this.nextId++,
            name: this.newItemName,
            price: this.newItemPrice
          })
          this.newItemName = ''
          this.newItemPrice = 0
        }
      },
      removeItem(index) {
        this.items.splice(index, 1)
      },
      sortItems() {
        // 变更方法: 会触发视图更新
        this.items.sort((a, b) => a.price - b.price)
      },
      reverseItems() {
        // 变更方法: 会触发视图更新
        this.items.reverse()
      },
      updateItem(index) {
        // 非变更方法: 需要替换数组才能触发更新
        // this.items[index].price += 1 // 不会触发视图更新
        
        // 正确的方式:
        this.items[index] = { ...this.items[index], price: this.items[index].price + 1 }
        // 或者使用 Vue.set 或 this.$set (Vue 2)
      }
    }
  }).mount('#app')
</script>

6.2 过滤和排序

html 复制代码
<div id="app">
  <input v-model="searchQuery" placeholder="搜索...">
  
  <ul>
    <li v-for="item in filteredItems" :key="item.id">
      {{ item.name }} - {{ item.price }}元
    </li>
  </ul>
  
  <div>
    <button @click="sortOrder = 'asc'">价格升序</button>
    <button @click="sortOrder = 'desc'">价格降序</button>
    <button @click="sortOrder = 'none'">原始顺序</button>
  </div>
</div>

<script>
  const app = Vue.createApp({
    data() {
      return {
        items: [
          { id: 1, name: '苹果', price: 5 },
          { id: 2, name: '香蕉', price: 3 },
          { id: 3, name: '橙子', price: 6 },
          { id: 4, name: '葡萄', price: 8 }
        ],
        searchQuery: '',
        sortOrder: 'none'
      }
    },
    computed: {
      filteredItems() {
        // 先过滤
        let result = this.items
        if (this.searchQuery) {
          const query = this.searchQuery.toLowerCase()
          result = result.filter(item => 
            item.name.toLowerCase().includes(query)
          )
        }
        
        // 再排序
        if (this.sortOrder !== 'none') {
          result = [...result].sort((a, b) => {
            return this.sortOrder === 'asc' 
              ? a.price - b.price 
              : b.price - a.price
          })
        }
        
        return result
      }
    }
  }).mount('#app')
</script>

7. 事件处理进阶

7.1 事件修饰符详解

html 复制代码
<div id="app">
  <!-- 阻止单击事件继续传播 -->
  <a @click.stop="doThis">阻止冒泡</a>
  
  <!-- 提交事件不再重载页面 -->
  <form @submit.prevent="onSubmit">
    <input type="text">
    <button type="submit">提交</button>
  </form>
  
  <!-- 修饰符可以串联 -->
  <a @click.stop.prevent="doThat">阻止默认行为和冒泡</a>
  
  <!-- 只有修饰符 -->
  <form @submit.prevent>
    <input type="text">
    <button type="submit">阻止默认提交</button>
  </form>
  
  <!-- 添加事件监听器时使用事件捕获模式 -->
  <div @click.capture="doThis">
    捕获模式
    <button @click="childClick">子元素</button>
  </div>
  
  <!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
  <div @click.self="doThat">
    只有点击这里才触发
    <button>点击我不会触发父元素事件</button>
  </div>
  
  <!-- 点击事件将只会触发一次 -->
  <button @click.once="doOnce">只触发一次</button>
  
  <!-- 滚动事件的默认行为 (滚动) 将立即发生,不等待完成 -->
  <div @scroll.passive="onScroll">
    被动监听的滚动区域
  </div>
</div>

<script>
  const app = Vue.createApp({
    methods: {
      doThis() {
        console.log('doThis 被调用')
      },
      doThat() {
        console.log('doThat 被调用')
      },
      onSubmit() {
        console.log('表单提交')
      },
      childClick() {
        console.log('子元素被点击')
      },
      doOnce() {
        console.log('这个处理函数只会触发一次')
      },
      onScroll() {
        console.log('滚动中...')
      }
    }
  }).mount('#app')
</script>

7.2 按键修饰符

html 复制代码
<div id="app">
  <!-- 按下Enter键时调用 -->
  <input @keyup.enter="submit">
  
  <!-- 按下Tab键时调用 -->
  <input @keyup.tab="handleTab">
  
  <!-- 按下Delete键时调用 -->
  <input @keyup.delete="handleDelete">
  
  <!-- 按下Esc键时调用 -->
  <input @keyup.esc="handleEscape">
  
  <!-- 按下空格键时调用 -->
  <input @keyup.space="handleSpace">
  
  <!-- 按下上方向键时调用 -->
  <input @keyup.up="handleUp">
  
  <!-- 按下Page Down键时调用 -->
  <input @keyup.page-down="handlePageDown">
  
  <!-- 组合按键 -->
  <input @keyup.alt.enter="handleAltEnter">
  
  <!-- Ctrl + 点击 -->
  <div @click.ctrl="handleCtrlClick">Ctrl + Click</div>
  
  <!-- 精确的系统修饰符 -->
  <button @click.ctrl.exact="onCtrlClick">仅当按下Ctrl时</button>
  <button @click.exact="onClickWithoutModifiers">没有任何系统修饰符</button>
</div>

<script>
  const app = Vue.createApp({
    methods: {
      submit() {
        console.log('提交表单')
      },
      handleTab() {
        console.log('Tab键被按下')
      },
      handleDelete() {
        console.log('Delete键被按下')
      },
      handleEscape() {
        console.log('Esc键被按下')
      },
      handleSpace() {
        console.log('空格键被按下')
      },
      handleUp() {
        console.log('上方向键被按下')
      },
      handlePageDown() {
        console.log('Page Down键被按下')
      },
      handleAltEnter() {
        console.log('Alt + Enter被按下')
      },
      handleCtrlClick() {
        console.log('Ctrl + Click被触发')
      },
      onCtrlClick() {
        console.log('仅当按下Ctrl时触发')
      },
      onClickWithoutModifiers() {
        console.log('没有任何系统修饰符时触发')
      }
    }
  }).mount('#app')
</script>

结语

感谢您的阅读!期待您的一键三连!欢迎指正!

相关推荐
a东方青30 分钟前
vue3学习笔记之属性绑定
vue.js·笔记·学习
沉默是金~1 小时前
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
javascript·vue.js·elementui
上趣工作室1 小时前
vue3专题1------父组件中更改子组件的属性
前端·javascript·vue.js
只会安静敲代码的 小周4 小时前
【长按图片识别】uniapp vue开发时,点击图片识别—实现转发、收藏、识别图片二维码
前端·vue.js·uni-app
ᥬ 小月亮4 小时前
uniapp(Vue)开发微信小程序 之 保存图片到本地
vue.js·微信小程序·uni-app
shmily ....4 小时前
从零构建 Vue3 登录页:结合 Vant 组件与 Axios 实现完整登录功能
前端·javascript·vue.js
爱怪笑的小杰杰6 小时前
如何优雅地为 Axios 配置失败重试与最大尝试次数
前端·javascript·vue.js
实习生小黄6 小时前
vue3 开启本地调试
前端·vue.js
程序猿熊跃晖6 小时前
如何在Vue中实现取消聚焦el-select——从零到部署的完整指南
前端·javascript·vue.js