『Vue』基本语法

1. 第一个 Vue 程序


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<div id="app">
    {
    {message}}
    {
    {message}}
div>

body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
    var vm = new Vue({
      
        el: "#app",
        data: {
      
            message: "Hello, Vue!"
        }
    });
script>
html>

在这里插入图片描述

2. MVVM 模式

2.1 什么是 MVVM

MVVM(Model - View - ViewModel)是一种软件架构的设计模式,是一种简化用户界面的事件驱动编程方式。

MVVM 源自经典的 MVC(Model - View - Controller)模式。

MVVM 的核心是 ViewModel层,负责转换 Model 中的数据对象来让数据变得更容易管理和使用,其作用如下:

  • 向上与视图层进行双向数据绑定
  • 向下与 Model 层通过接口请求进行数据操作

在这里插入图片描述

2.2 为什么用 MVVM

MVVM 模式和 MVC 模式一样,主要目的是分离视图(View)和模型(Model)

  • 低耦合

    视图(View)可以独立于 Model 和变化和修改,一个 ViewModel 可以绑定到不同的 View 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 可以不变

  • 可复用

    可以把一些视图逻辑放在一个 ViewModel 里面,让很多的 View 重复这段视图逻辑

  • 独立开发

    开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计

  • 可测试

    界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写

3. Vue 基本语法

3.1 Vue 实例

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

var vm = new Vue({
     
  // 选项
})

虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

当创建一个 Vue 实例时,你可以传入一个选项对象

Vue实例 有 7 个常用选项对象(属性):

  • el

    挂载点,用来指示 Vue 编译器从什么地方开始解析 Vue 的语法,可以说是一个占位符。

    其实就是跟一个元素节点相关联,假如设置el: "#app",那么 vm.$el === document.querySelector("#app") 的结果为 true,绑定的节点及其子节点都可以使用绑定的 Vue 实例。

  • data

    用来组织从 view 中抽象出来的属性,可以说将视图的数据抽象出来存放在 data 中

    在视图中可以使用绑定的 Vue 实例中 data 选项对象定义的属性

  • methods

    放置页面中的业务逻辑,js方法一般都放置在 methods 中

    定义函数的地方,绑定事件时可以将在此定义的函数绑定到事件中

    不要使用箭头函数,如果函数使用箭头函数会导致this指向的不是vue实例$vm。因为箭头函数不绑定 this 关键字,箭头函数中的 this,指向的是函数定义位置的上下文 this

  • computed

    定义计算属性的方法,类似于 Python 中 class 里面用 @Property 装饰器修饰的函数

  • template

    用来设置模板,会替换页面元素,包括占位符

  • watch

    • watch: function(new,old){}
    • 监听 data 中数据的变化
    • 两个参数,一个返回新值,一个返回旧值
  • render

    创建真正的 Virtual Dom

【示例】

<div id="app">
    {
    {msg}}
      <div>这是模板的第一种使用方法1div>
div>
 
<template id="bot">这是模板的第三种使用方法,不常用3template>
<script>
<div id="bot">模板的第四种创建方法4</div>
script>
<script>
    var vm1 = new Vue({
      
        data: {
      
            msg: "data属性"
        },
        methods: {
      
            Personal: function () {
      
                console.log("methods方法存放方法")
            }
        },
        template: `
模板的第二种使用方法2
`
, //template:"#bot", render: (createElement) => { return createElement("h1",{ "class":"类名""style":"color: red"},"这一个render属性创建的虚拟dom") }, })
script>

methods和computed其中都可以写算法,有什么区别呢?


<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<script src="javascript/vue.min.js">script>
head> <body>
<div id="app">
    <p>{
    {message}}p> //直接在模板中绑定表达式
    <p>{
    {message.split('').reverse().join('')}}p> //运用计算属性
    <p>{
    {reverseMessage}}p> //运用methods方式
    <p>{
    {methodMessage()}}p>
div>
<script>
    var vm=new Vue({
      
         el:"#app",
         data:{
      
             message:"hello"
         },
        computed:{
      
            reverseMessage:function(){
      
                 return this.message.split('').reverse().join('');
            }
        },
         methods:{
      
             methodMessage:function () {
      
                 return this.message.split('').reverse().join('');
             }
         }
    })
script>
body>
html>

3.2 生命周期

一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成,所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外)。

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着不断学习和使用,它的参考价值会越来越高。

特别地,本人不打算做大前端,只是学习 Vue 的简单使用,可能细节不一定对,不过图示结果的确都是运行得到的,我看Segmentfault上有人说src引入和vue-cli的项目运行的不一样,有虚拟DOM的问题,专门学前端的别被我误导了。

『Vue』基本语法_第1张图片

可以看到在vue一整个的生命周期中会有很多钩子函数提供给我们在vue生命周期不同的时刻进行操作,那么先列出所有的钩子函数,然后我们再一一详解:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

先来一波代码,各位复制在浏览器中运行,打开 console 查看就行了:


<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>vue生命周期学习title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>
<body>
  <div id="app">
    <h1>{
    {message}}h1>
  div>
body>
<script>
  var vm = new Vue({
      
    el: '#app',
    data: {
      
      message: 'Vue的生命周期'
    },
    beforeCreate: function() {
      
      console.group('------beforeCreate创建前状态------');
      console.log("%c%s", "color:red", "el     : " + this.$el);     //undefined
      console.log("%c%s", "color:red", "data   : " + this.$data);   //undefined 
      console.log("%c%s", "color:red", "message: " + this.message)  //undefined 
    },
    created: function() {
      
      console.group('------created创建完毕状态------');
      console.log("%c%s", "color:red", "el     : " + this.$el);     //undefined
      console.log("%c%s", "color:red", "data   : " + this.$data);   //已被初始化 
      console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
    },
    beforeMount: function() {
      
      console.group('------beforeMount挂载前状态------');
      console.log("%c%s", "color:red", "el     : " + (this.$el));   //已被初始化
      console.log(this.$el);
      console.log("%c%s", "color:red", "data   : " + this.$data);   //已被初始化  
      console.log("%c%s", "color:red", "message: " + this.message); //已被初始化  
    },
    mounted: function() {
      
      console.group('------mounted 挂载结束状态------');
      console.log("%c%s", "color:red","el     : " + this.$el); //已被初始化
      console.log(this.$el);    
      console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
      console.log("%c%s", "color:red","message: " + this.message); //已被初始化 
    },
    beforeUpdate: function () {
      
      console.group('beforeUpdate 更新前状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);   
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    updated: function () {
      
      console.group('updated 更新完成状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el); 
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    beforeDestroy: function () {
      
      console.group('beforeDestroy 销毁前状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);    
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message); 
    },
    destroyed: function () {
      
      console.group('destroyed 销毁完成状态===============》');
      console.log("%c%s", "color:red","el     : " + this.$el);
      console.log(this.$el);  
      console.log("%c%s", "color:red","data   : " + this.$data); 
      console.log("%c%s", "color:red","message: " + this.message)
    }
  })
script>
html>

运行后打开console可以看到打印出来内容如下:

『Vue』基本语法_第2张图片

可以看到一个 Vue 实例在创建过程中调用的几个生命周期钩子。

1. 在 beforeCreate 和 created 钩子函数之间的生命周期

在这个生命周期之间,进行初始化事件,进行数据的观测,可以看到在created的时候数据已经和data属性进行绑定(放在data中的属性当值发生改变的同时,视图也会改变)。

注意看下:此时还是没有 el 选项

2. created 钩子函数和 beforeMount 间的生命周期

『Vue』基本语法_第3张图片

在这一阶段发生的事情还是比较多的。

首先会判断对象是否有 el 选项。如果有的话就继续向下编译,如果没有 el 选项,则停止编译,也就意味着停止了生命周期,直到在该 Vue 实例上调用vm.$mount(el)。此时注释掉代码中:

el: '#app',

然后运行可以看到 created 的时候就停止了:

『Vue』基本语法_第4张图片

如果我们在后面继续调用 vm.$mount(el),可以发现代码继续向下执行了

vm.$mount(el) //这个el参数就是挂在的dom接点

『Vue』基本语法_第5张图片

然后,我们往下看,template 参数选项的有无对生命周期的影响。

  • 如果 Vue 实例对象中有 template 参数选项,则将其作为模板编译成render函数。
  • 如果没有 template 选项,则将外部 HTML 作为模板编译。
  • 可以看到 template 中的模板优先级要高于 outer HTML 的优先级。

『Vue』基本语法_第6张图片

对如下代码,在HTML结构中增加了一串html,在 Vue 对象中增加了 template 选项:


<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue生命周期学习title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>
<body>
  <div id="app">
    
    <h1>{
    {message + '——这是在outer HTML中的'}}h1>
  div>
body>
<script>
  var vm = new Vue({
      
    el: '#app',
    template: "

{ {message +'——这是在template中的'}}

"
, //在vue配置项中修改的 data: { message: 'Vue的生命周期' }});
script> html>

执行后的结果可以看到在页面中显示的是:

在这里插入图片描述

那么将 Vue 对象中 template 的选项注释掉后打印如下信息:

在这里插入图片描述

这下就可以想想为什么 el 的判断要在 template 之前了,是因为 Vue 需要通过 el 找到对应的 outer template

在 Vue 对象中还有一个 render 函数,它是以 createElement 作为参数,然后做渲染操作


<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>vue生命周期学习title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
    <div id="app">
        
        <h1>{
    {message + '——这是在outer HTML中的'}}h1>
    div>
body>
<script>
    var vm = new Vue({
      
        el: '#app',
        template: "

{ {message +'——这是在template中的'}}

"
, //在vue配置项中修改的 data: { message: 'Vue的生命周期' }, render: function (createElement) { return createElement('h1', 'this is createElement') } });
script> html>

可以看到页面中渲染的是:

在这里插入图片描述

所以综合排名优先级:

render 函数选项 > template 选项 > outer HTML

3. beforeMount 和 mounted 钩子函数间的生命周期

『Vue』基本语法_第7张图片

可以看到此时是给 Vue 实例对象添加 $el 成员(是绿色的部分!beforeMount 钩子函数内是没有 $el),并且替换掉挂载的 DOM 元素。因为在之前 console 中打印的结果可以看到 beforeMount 之前 el 上还是undefined。

beforeMount 阶段及其之前应该是虚拟 DOM,到了挂载完成,也就是 mounted 阶段及其之后才有真实的 DOM

4. mounted

注意看下面截图:

『Vue』基本语法_第8张图片

mounted 之前 h1 中还是通过 { {message}} 进行占位的,因为此时还有挂在到页面上,还是 JavaScript 中的虚拟 DOM 形式存在的。在 mounted 之后可以看到 h1 中的内容发生了变化。

挂载之前 eldata 是不关联的,挂载到那个模板上,看优先级:render 函数选项 > template 选项 > outer HTML

5. beforeUpdate 钩子函数和 updated 钩子函数间的生命周期

『Vue』基本语法_第9张图片

当 Vue发现 data 中的数据发生了改变,会触发对应组件的重新渲染,先后调用 beforeUpdateupdated 钩子函数。我们在 console 中输入:

vm.message = '触发组件更新';

发现触发了组件的更新:

『Vue』基本语法_第10张图片

beforeUpdate 是检测到 data 变化但是 view 还没有重新渲染,并且可以修改 data 的最后时机,updated 是 view 重新渲染后触发的

beforeUpdate,可以监听到 data 的变化但是 view 层没有被重新渲染,view 层的数据没有变化。

等到 updated 的时候 view 层才被重新渲染,数据更新。

6.beforeDestroy 和 destroyed 钩子函数间的生命周期

『Vue』基本语法_第11张图片

beforeDestroy 钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed 钩子函数在 Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

3.3 模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。

3.3.1 插值

  • 文本

    数据绑定最常见的形式就是使用 “Mustache” 语法 (双大括号) 的文本插值:

    <span>Message: {
          { msg }}span>
    

    Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。

    通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。

    只渲染元素和组件一次,随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

    
    <span v-once>This will never change: {
          {msg}}span>
    
    <div v-once>
      <h1>commenth1>
      <p>{
          {msg}}p>
    div>
    
    <my-component v-once: comment="msg">my-component>
    
    <ul>
      <li v-for="i in list" v-once>{
          {i}}li>
    ul>
    
  • 原始 HTML

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

    <p>使用双花括号语法:{
          { rawHtml }}p>
    <p>使用 v-html 指令:<span v-html="rawHtml">span>p>
    

    结果显示为:

    使用双花括号语法:<span style="color: red">This should be red.span>
    使用 v-html 指令:This should be red.
    
  • Attribute

    Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:

    <div v-bind:id="dynamicId">div>
    

    对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:

    <button v-bind:disabled="isButtonDisabled">Buttonbutton>
    

    如果 isButtonDisabled 的值是 nullundefinedfalse,则 disabled attribute 甚至不会被包含在渲染出来的

  • JavaScript 表达式

    迄今为止,在我们的模板中,我们一直都只绑定简单的 property 键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。

    {
          { number + 1 }}
    
    {
          { ok ? 'YES' : 'NO' }}
    
    {
          { message.split('').reverse().join('') }}
    
    <div v-bind:id="'list-' + id">div>
    

    这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

    
    {
          { var a = 1 }}
    
    
    {
          { if (ok) { return message } }}
    

3.3.2 指令

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

指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

<p v-if="seen">现在你看到我了p>

这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除

元素。

  • 参数

    一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:

    <a v-bind:href="url">...a>
    

    在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。

    另一个例子是 v-on 指令,它用于监听 DOM 事件:

    <a v-on:click="doSomething">...a>
    

    在这里参数是监听的事件名。

  • 动态参数

    从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

    
    <a v-bind:[attributeName]="url"> ... a>
    

    这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data property attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href

    同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:

    <a v-on:[eventName]="doSomething"> ... a>
    

    在这个示例中,当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus

    动态参数预期会求出一个字符串,异常情况下值为 null。这个特殊的 null 值可以被显性地用于移除绑定。

  • 修饰符

    饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

    <form v-on:submit.prevent="onSubmit">...form>
    
  • 缩写

    v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v- 前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的[单页面应用程序 (SPA - single page application) 时,v- 前缀也变得没那么重要了。因此,Vue 为 v-bindv-on 这两个最常用的指令,提供了特定简写:

    • v-bind 缩写:

      
      <a v-bind:href="url">...a>
      
      
      <a :href="url">...a>
      
      
      <a :[key]="url"> ... a>
      
    • v-on 缩写:

      
      <a v-on:click="doSomething">...a>
      
      
      <a @click="doSomething">...a>
      
      
      <a @[event]="doSomething"> ... a>
      

    它们看起来可能与普通的 HTML 略有不同,但 :@ 对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们。

3.4 计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="example">
  {
    { message.split('').reverse().join('') }}
div>

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。

所以,对于任何复杂逻辑,你都应当使用计算属性

3.4.1 基础示例

<div id="example">
  <p>Original message: "{
    { message }}"p>
  <p>Computed reversed message: "{
    { reversedMessage }}"p>
div>
var vm = new Vue({
     
  el: '#example',
  data: {
     
    message: 'Hello'
  },
  computed: {
     
    // 计算属性的 getter
    reversedMessage: function () {
     
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
});

结果:

Original message: "Hello"
Computed reversed message: "olleH"

这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数:

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

你可以打开浏览器的控制台,自行修改例子中的 vm。vm.reversedMessage 的值始终取决于 vm.message 的值。

你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。

3.4.2 计算属性缓存 vs 方法

我们可以通过在表达式中调用方法来达到同样的效果:

<p>Reversed message: "{
    { reversedMessage() }}"p>
// 在组件中
methods: {
     
  reversedMessage: function () {
     
    return this.message.split('').reverse().join('')
  }
}

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

computed: {
     
  now: function () {
     
    return Date.now()
  }
}

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

3.4.5 计算属性 vs 侦听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

<div id="demo">{
    { fullName }}div>
var vm = new Vue({
     
  el: '#demo',
  data: {
     
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
     
    firstName: function (val) {
     
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
     
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
     
  el: '#demo',
  data: {
     
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
     
    fullName: function () {
     
      return this.firstName + ' ' + this.lastName
    }
  }
})

好得多了,不是吗?

3.4.6 计算属性的 setter

计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:

// ...
computed: {
     
  fullName: {
     
    // getter
    get: function () {
     
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
     
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName 也会相应地被更新。

3.5 Class 与 Style 绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 classstyle 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

3.5.1 绑定 HTML class

1. 对象语法

我们可以传给 v-bind:class 一个对象,以动态地切换 class:

<div v-bind:class="{ active: isActive }">div>

上面的语法表示 active 这个 class 存在与否将取决于数据 property isActive 的 truthiness。

你可以在对象中传入更多字段来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class attribute 共存。当有如下模板:

<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }">div>

和如下 data:

data: {
     
  isActive: true,
  hasError: false
}

结果渲染为:

<div class="static active">div>

isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 "static active text-danger"

绑定的数据对象不必内联定义在模板里:

<div v-bind:class="classObject">div>
data: {
     
  classObject: {
     
    active: true,
    'text-danger': false
  }
}

渲染的结果和上面一样。我们也可以在这里绑定一个返回对象的计算属性。这是一个常用且强大的模式:

<div v-bind:class="classObject">div>
data: {
     
  isActive: true,
  error: null
},
computed: {
     
  classObject: function () {
     
    return {
     
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}

2. 数组语法

我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:

<div v-bind:class="[activeClass, errorClass]">div>
data: {
     
  activeClass: 'active',
  errorClass: 'text-danger'
}

渲染为:

<div class="active text-danger">div>

如果你也想根据条件切换列表中的 class,可以用三元表达式:

<div v-bind:class="[isActive ? activeClass : '', errorClass]">div>

这样写将始终添加 errorClass,但是只有在 isActive 是 truthy时才添加 activeClass

不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]">div>

3. 用在组件上

这个章节假设你已经对 Vue 组件有一定的了解。当然你也可以先跳过这里,稍后再回过头来看。

当在一个自定义组件上使用 class property 时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。

例如,如果你声明了这个组件:

Vue.component('my-component', {
     
  template: '

Hi

'
})

然后在使用它的时候添加一些 class:

<my-component class="baz boo">my-component>

HTML 将被渲染为:

<p class="foo bar baz boo">Hip>

对于带数据绑定 class 也同样适用:

<my-component v-bind:class="{ active: isActive }">my-component>

isActive 为 truthy[1] 时,HTML 将被渲染成为:

<p class="foo bar active">Hip>

3.5.2 绑定内联样式

1. 对象语法

v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:

<div v-bind:style="{
         color: activeColor, fontSize: fontSize + 'px' }">div>
data: {
     
  activeColor: 'red',
  fontSize: 30
}

直接绑定到一个样式对象通常更好,这会让模板更清晰:

<div v-bind:style="styleObject">div>
data: {
     
  styleObject: {
     
    color: 'red',
    fontSize: '13px'
  }
}

同样的,对象语法常常结合返回对象的计算属性使用。

2. 数组语法

v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]">div>

3. 自动添加前缀

v-bind:style 使用需要添加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会自动侦测并添加相应的前缀。

3.6 条件渲染

3.6.1 v-if

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。

<h1 v-if="awesome">Vue is awesome!h1>

也可以用 v-else 添加一个“else 块”:

<h1 v-if="awesome">Vue is awesome!h1>
<h1 v-else>Oh no h1>

1. 在