Vue.js 基本语法

本教程将详细介绍Vue.js的核心语法,包括模板语法指令系统计算属性侦听器等。每个概念都会通过实例代码进行演示。

1. 模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。

文本插值

使用双大括号语法进行文本插值:


<div id="app">
  <p>{{ message }}</p>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue!'
    }
  });
</script>
                                        

结果: Hello Vue!

原始HTML

使用 v-html 指令输出真正的 HTML:


<div id="app">
  <p>{{ rawHtml }}</p>
  <p v-html="rawHtml"></p>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      rawHtml: '<span style="color: red">这是红色的文字</span>'
    }
  });
</script>
                                        

结果:

<span style="color: red">这是红色的文字</span>

这是红色的文字

提示: 双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令。

2. 指令系统

指令是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式(除了 v-for)。

常用指令列表

  • v-bind 动态绑定属性
  • v-model 表单输入双向绑定
  • v-if / v-else-if / v-else 条件渲染
  • v-show 条件显示(基于CSS)
  • v-for 列表渲染
  • v-on 事件监听
  • v-html 输出原始HTML
  • v-text 更新元素的textContent

v-bind 指令示例

v-bind 用于动态绑定一个或多个特性,或一个组件 prop 到表达式。


<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">

<!-- 动态特性名 (2.6.0+) -->
<button v-bind:[key]="value"></button>

<!-- 缩写 -->
<img :src="imageSrc">

<!-- 动态特性名缩写 -->
<button :[key]="value"></button>

<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName">
                                    

<div id="app">
  <!-- 绑定href -->
  <a v-bind:href="url">链接</a>

  <!-- 绑定class -->
  <div v-bind:class="{ active: isActive }">动态类</div>

  <!-- 绑定style -->
  <div v-bind:style="{ color: textColor, fontSize: size + 'px' }">动态样式</div>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      url: 'https://vuejs.org',
      isActive: true,
      textColor: 'red',
      size: 20
    }
  });
</script>
                                    

<!-- 完整语法 -->
<a v-bind:href="url">链接</a>

<!-- 缩写 -->
<a :href="url">链接</a>

<!-- 动态参数缩写 (2.6.0+) -->
<a :[attributeName]="url">链接</a>
                                    
注意: v-bind 的缩写是 :。例如 v-bind:href 可以缩写为 :href

3. 计算属性

计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。

计算属性示例

new Vue({
  el: '#app',
  data: {
    firstName: '张',
    lastName: '三'
  },
  computed: {
    // 计算属性的 getter
    fullName: function() {
      return this.firstName + ' ' + this.lastName;
    },

    // 带setter的计算属性
    fullNameWithSetter: {
      get: function() {
        return this.firstName + ' ' + this.lastName;
      },
      set: function(newValue) {
        var names = newValue.split(' ');
        this.firstName = names[0];
        this.lastName = names[1] || '';
      }
    }
  }
});
                                        
计算属性 vs 方法
特性 计算属性 方法
缓存 有缓存 无缓存
调用方式 作为属性访问
{{ fullName }}
作为方法调用
{{ getFullName() }}
适用场景 复杂计算、性能敏感 事件处理、简单计算
选择建议: 当需要基于响应式数据进行复杂计算时,使用计算属性;当不需要缓存或需要传递参数时,使用方法。

计算属性完整示例

购物车总价计算示例
计算属性

<div id="cart-example">
  <h4>购物车</h4>
  <ul>
    <li v-for="item in cart" :key="item.id">
      {{ item.name }} - ¥{{ item.price }} × {{ item.quantity }}
    </li>
  </ul>
  <p>总价: ¥{{ totalPrice }}</p>
  <p>含税总价 (税率{{ taxRate * 100 }}%): ¥{{ totalPriceWithTax }}</p>
</div>

<script>
  new Vue({
    el: '#cart-example',
    data: {
      cart: [
        { id: 1, name: '商品A', price: 100, quantity: 2 },
        { id: 2, name: '商品B', price: 200, quantity: 1 },
        { id: 3, name: '商品C', price: 50, quantity: 5 }
      ],
      taxRate: 0.1
    },
    computed: {
      totalPrice: function() {
        // 计算购物车总价
        return this.cart.reduce((sum, item) => {
          return sum + (item.price * item.quantity);
        }, 0);
      },
      totalPriceWithTax: function() {
        // 基于其他计算属性进行计算
        return this.totalPrice * (1 + this.taxRate);
      }
    }
  });
</script>
                            

运行结果:

  • 商品A - ¥100 × 2
  • 商品B - ¥200 × 1
  • 商品C - ¥50 × 5

总价: ¥650

含税总价 (税率10%): ¥715

4. 侦听器

侦听器用于观察和响应 Vue 实例上的数据变动。当需要在数据变化时执行异步或开销较大的操作时,使用侦听器。

基本侦听器

new Vue({
  el: '#app',
  data: {
    question: '',
    answer: '请先输入问题!'
  },
  watch: {
    // 监听question的变化
    question: function(newQuestion, oldQuestion) {
      this.answer = '正在思考...';
      this.getAnswer();
    }
  },
  methods: {
    getAnswer: function() {
      // 模拟异步API调用
      setTimeout(() => {
        this.answer = '答案是:42';
      }, 1000);
    }
  }
});
                                        
深度侦听

new Vue({
  el: '#app',
  data: {
    user: {
      name: '张三',
      age: 25,
      profile: {
        email: 'zhangsan@example.com'
      }
    }
  },
  watch: {
    // 深度侦听对象内部值的变化
    user: {
      handler: function(newVal, oldVal) {
        console.log('user changed:', newVal);
      },
      deep: true, // 深度侦听
      immediate: true // 立即执行一次
    },

    // 侦听对象特定属性
    'user.name': function(newVal, oldVal) {
      console.log('name changed:', newVal);
    }
  }
});
                                        

计算属性 vs 侦听器

场景 使用计算属性 使用侦听器
数据变化时执行操作 推荐 适用于同步操作 推荐 适用于异步操作
需要返回一个值 必须 计算属性返回计算值 不适用 侦听器不返回值
开销大的操作 不推荐 计算属性会缓存结果 推荐 可以控制执行时机
响应多个数据变化 推荐 可以依赖多个数据 复杂 需要侦听多个数据

5. 条件渲染

Vue 提供了 v-if, v-else-if, v-elsev-show 指令用于条件渲染。

v-if 指令

<div id="app">
  <!-- 基本用法 -->
  <p v-if="show">显示内容</p>

  <!-- v-if / v-else -->
  <p v-if="score >= 60">及格</p>
  <p v-else>不及格</p>

  <!-- 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>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      show: true,
      score: 85,
      type: 'B'
    }
  });
</script>
                                        
v-show 指令

<div id="app">
  <!-- v-show 只是切换CSS的display属性 -->
  <p v-show="isVisible">显示内容</p>

  <!-- v-if 与 v-show 对比 -->
  <div v-if="count > 0">v-if: 条件为真</div>
  <div v-show="count > 0">v-show: 条件为真</div>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      isVisible: true,
      count: 5
    }
  });
</script>
                                        

v-if vs v-show

特性 v-if v-show
初始渲染开销 条件为假时,元素不会被渲染到DOM 元素始终会被渲染,只是通过CSS切换显示
切换开销 切换时元素会被销毁/重建,开销较高 切换时只是修改CSS属性,开销较低
适用场景 运行时条件很少改变,或需要避免初始渲染 需要频繁切换显示状态
与v-else配合 支持 不支持
注意: 使用 v-if 时,元素可能无法被获取到,因为条件为假时元素不会存在于DOM中。而 v-show 的元素始终存在于DOM中,只是通过CSS控制显示。

6. 列表渲染

使用 v-for 指令基于数组或对象渲染列表。

数组渲染

<div id="app">
  <!-- 基本用法 -->
  <ul>
    <li v-for="item in items">
      {{ item.name }}
    </li>
  </ul>

  <!-- 带索引 -->
  <ul>
    <li v-for="(item, index) in items">
      {{ index }}. {{ item.name }}
    </li>
  </ul>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      items: [
        { name: '苹果' },
        { name: '香蕉' },
        { name: '橙子' }
      ]
    }
  });
</script>
                                        
对象渲染

<div id="app">
  <!-- 遍历对象 -->
  <ul>
    <li v-for="value in user">
      {{ value }}
    </li>
  </ul>

  <!-- 带键名和索引 -->
  <ul>
    <li v-for="(value, key, index) in user">
      {{ index }}. {{ key }}: {{ value }}
    </li>
  </ul>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      user: {
        name: '张三',
        age: 25,
        city: '北京'
      }
    }
  });
</script>
                                        

维护状态与key属性

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的 key 特性:


<div id="app">
  <!-- 正确的做法:使用key -->
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
      <input type="text" placeholder="输入内容">
    </li>
  </ul>

  <!-- 错误的做法:不使用key -->
  <ul>
    <li v-for="item in items">
      {{ item.name }}
      <input type="text" placeholder="输入内容">
    </li>
  </ul>

  <button @click="addItem">添加新项</button>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      items: [
        { id: 1, name: '第一项' },
        { id: 2, name: '第二项' },
        { id: 3, name: '第三项' }
      ]
    },
    methods: {
      addItem: function() {
        this.items.unshift({
          id: Date.now(),
          name: '新项' + (this.items.length + 1)
        });
      }
    }
  });
</script>
                            

说明: 使用 :key 可以确保在列表重新排序时,Vue能够正确识别每个元素,避免不必要的DOM操作。

重要: 总是使用 :keyv-for 的元素提供唯一标识,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获得性能提升。

7. 事件处理

使用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

内联处理器

<div id="app">
  <!-- 直接执行代码 -->
  <button v-on:click="counter += 1">
    点击增加: {{ counter }}
  </button>

  <!-- 调用方法 -->
  <button v-on:click="greet">打招呼</button>

  <!-- 内联语句中调用方法 -->
  <button v-on:click="say('Hello')">说 Hello</button>

  <!-- 访问原始DOM事件 -->
  <button v-on:click="warn('表单不能提交', $event)">
    提交
  </button>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      counter: 0,
      name: 'Vue.js'
    },
    methods: {
      greet: function(event) {
        alert('Hello ' + this.name + '!');
        if (event) {
          alert(event.target.tagName);
        }
      },
      say: function(message) {
        alert(message);
      },
      warn: function(message, event) {
        if (event) {
          event.preventDefault();
        }
        alert(message);
      }
    }
  });
</script>
                                        
事件修饰符

<div id="app">
  <!-- 阻止默认行为 -->
  <a v-on:click.prevent="doSomething" href="#">链接</a>

  <!-- 停止事件冒泡 -->
  <div v-on:click.stop="doSomething">
    点击这里不会冒泡
  </div>

  <!-- 事件只会触发一次 -->
  <button v-on:click.once="doSomething">只触发一次</button>

  <!-- 按键修饰符 -->
  <input v-on:keyup.enter="submit" placeholder="按回车提交">

  <!-- 系统修饰键 -->
  <button v-on:click.ctrl="doSomething">Ctrl+点击</button>
</div>

<script>
  new Vue({
    el: '#app',
    methods: {
      doSomething: function() {
        console.log('事件处理');
      },
      submit: function() {
        console.log('表单提交');
      }
    }
  });
</script>
                                        
修饰符: Vue提供了事件修饰符,可以链式调用,如 @click.stop.prevent

事件修饰符列表

修饰符 作用 示例
.stop 阻止事件冒泡 @click.stop="handleClick"
.prevent 阻止默认行为 @submit.prevent="handleSubmit"
.capture 使用事件捕获模式 @click.capture="handleClick"
.self 只当事件是从元素本身触发时触发回调 @click.self="handleClick"
.once 事件只触发一次 @click.once="handleClick"
.passive 提升移动端滚动性能 @scroll.passive="handleScroll"

8. 表单输入绑定

使用 v-model 指令在表单输入元素上创建双向数据绑定。

v-model 在各种表单元素上的应用
双向绑定

<div id="app">
  <!-- 文本输入框 -->
  <input v-model="message" placeholder="输入内容">
  <p>输入的内容: {{ message }}</p>

  <!-- 多行文本 -->
  <textarea v-model="multilineText" placeholder="多行文本"></textarea>
  <p style="white-space: pre-line">{{ multilineText }}</p>

  <!-- 复选框 -->
  <div>
    <input type="checkbox" id="checkbox" v-model="checked">
    <label for="checkbox">{{ checked ? '已选中' : '未选中' }}</label>
  </div>

  <!-- 多个复选框 -->
  <div>
    <input type="checkbox" id="vue" value="Vue" v-model="checkedNames">
    <label for="vue">Vue</label>
    <input type="checkbox" id="react" value="React" v-model="checkedNames">
    <label for="react">React</label>
    <input type="checkbox" id="angular" value="Angular" v-model="checkedNames">
    <label for="angular">Angular</label>
    <p>选择的框架: {{ checkedNames }}</p>
  </div>

  <!-- 单选按钮 -->
  <div>
    <input type="radio" id="one" value="One" v-model="picked">
    <label for="one">One</label>
    <input type="radio" id="two" value="Two" v-model="picked">
    <label for="two">Two</label>
    <p>选择的值: {{ picked }}</p>
  </div>

  <!-- 选择框 -->
  <div>
    <select v-model="selected">
      <option disabled value="">请选择</option>
      <option value="A">选项A</option>
      <option value="B">选项B</option>
      <option value="C">选项C</option>
    </select>
    <p>选择的选项: {{ selected }}</p>
  </div>

  <!-- 多选选择框 -->
  <div>
    <select v-model="multiSelected" multiple style="width: 200px">
      <option value="A">选项A</option>
      <option value="B">选项B</option>
      <option value="C">选项C</option>
    </select>
    <p>选择的选项: {{ multiSelected }}</p>
  </div>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      message: '',
      multilineText: '',
      checked: false,
      checkedNames: [],
      picked: '',
      selected: '',
      multiSelected: []
    }
  });
</script>
                            

修饰符

v-model 有一些修饰符,可以改变默认行为:

.lazy 修饰符

change 事件之后进行同步,而不是在 input 事件之后:


<!-- 在"change"时而非"input"时更新 -->
<input v-model.lazy="msg">
                                        
.number 修饰符

将用户的输入值转为数值类型:


<!-- 自动将输入值转为数值 -->
<input v-model.number="age" type="number">
                                        
.trim 修饰符

自动过滤用户输入的首尾空白字符:


<!-- 自动过滤输入的首尾空白字符 -->
<input v-model.trim="msg">
                                        

总结

本教程详细介绍了Vue.js的基本语法,包括:

  • 模板语法:插值、指令等基本用法
  • 计算属性与侦听器:处理复杂逻辑和数据变化响应
  • 条件与列表渲染v-ifv-for等指令的使用
  • 事件处理v-on指令和事件修饰符
  • 表单输入绑定v-model双向数据绑定

这些是Vue.js的核心语法,掌握它们是使用Vue.js进行开发的基础。在实际开发中,这些语法会经常组合使用。