第 2 章 数据绑定和第一个 Vue 应用

学习任何一种框架,从一个 Hello World 应用开始是最快了解该框架特性的途径,我们先从一 段简单的 HTML 代码开始,感受 Vue.js 最核心的功能。

<!DOCTYPE html>
<html>
  <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">
          <input type="text" v-model="name" placeholder="请填写您的名字..." />
          <h1>您好,<span style="color:red">{{ name }}</span></h1>
      </div>
  </body>
  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              name:''
          }
      }); 
  </script>
</html>

这是一段简单到不能再简单的代码,但却展示出了 Vue.js 最核心的功能: 数据的双向绑定。 在输入框输入的内容会实时展示在页面的 h1 标签内,如图所示。


Vue 实例与数据绑定
  • 实例与数据

Vue.js 应用的创建很简单,通过构造函数 Vue 就可以创建一个 Vue 的根实例,并启动 Vue 应用:

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

变量 app 就代表了这个 Vue 实例。事实上,几乎所有的代码都是一个对象,写入 Vue 实例的选项内的。
首先,必不可少的一个选项就是 el。 el 用于指定一个页面中己存在的 DOM 元素来挂载 Vue 实例,它可以是 HTMLElement ,也可以是 css 选择器,比如:

  <body>
      <div id="app">
      </div>
  </body>
  <script>
      var vm = new Vue({
          el:document.getElementById('app') //或者是'#app'           
      });     
  </script>
  • 挂载成功后,我们可以通过 app.$el 来访问该元素。 Vue 提供了很多常用的实例属性与方法, 都以$开头,比如$el, 后续还会介绍更多有用的方法。
  • 回顾章节开始的 Hello World 代码,在 input 标签上,有一个 v-model 的指令,它的值对应于我们创建的 Vue 实例的 data 选项中的 name 字段, 这就是 Vue 的数据绑定。
  • 通过 Vue 实例的 data 选项,可以声明应用内需要双向绑定的数据。建议所有会用到的数据都预先在 data 内声明,这样不至于将数据散落在业务逻辑中,难以维护。 Vue 实例本身也代理了 data 对象里的所有属性,所以可以这样访问:
      var vm = new Vue({
          el:'#app',
          data:{
              a:2
          }
      }); 
      console.log(vm.a);
  • 除了显式地声明数据外,也可以指向一个己有的变量,并且它们之间默认建立了双向绑定, 当修改其中任意一个时,另一个也会一起变化:
  <script>
      var myData = {
          a: 1
      }
      var vm = new Vue({
          el: '#app',
          data: myData
      }); 
      console.log(vm.a);//1
      //修改属性,原数据也会随之修改
      vm.a = 2;
      console.log(myData.a);//2
      //反之,修改是原数据,Vue属性也会修改
      myData.a = 3;
      console.log(vm.a);//3
  </script>
  • 生命周期

每个 Vue 实例创建时,都会经历一系列的初始化过程,同时也会调用相应的生命周期钩子, 我们可以利用这些钩子,在合适的时机执行我们的业务逻辑。如果你使用过 jQuery, 一定知道它的 ready() 方法,比如以下示例:

      $(document).ready(function(){
          // DOM 加载完后,会执行这里面的代码
      });

Vue 的生命周期钩子与之类似,比较常用的有:

  • created: 实例创建完成后调用,此阶段完成了数据的观测等,但尚未挂载, $el 还不可用。
    需要初始化处理一些数据时会比较有用,后面章节将有介绍。
  • mounted: el 挂载到实例上后调用,一般我们的第一个业务逻辑会在这里开始。
  • beforeDestroy: 实例销毁之前调用。主要解绑一些使用 addEventListener 监听的事件等。

这些钩子与 el 和 data 类似,也是作为选项写入 Vue 实例内,并且钩子的 this 指向的是调用它的 Vue 实例:

      var vm = new Vue({
          el: '#app',
          data: {
              a : 666
          },
          created: function () {
              console.log(this.a);//2
          },
          mounted: function () {
              console.log(this.$el);//<div id="app"></div>
          }
      }); 
  • 插值与表达式

使用双大括号(Mustache 语法)“{{}}”是最基本的文本插值方法,它会自动将我们双向绑定的数据实时显示出来,例如:

  <body>
      <div id="app">
          {{ book }}
      </div>
  </body>
  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              book: '《Vue.js 实战》' 
          }
      }); 
  </script>

大括号里的内容会被替换为《Vue.js 实战》,通过任何方法修改数据 book,大括号的内容都会被实时替换,比如下面的这个示例,实时显示当前的时间,每秒更新:

  <body>
      <div id="app">
          {{ date }}
      </div>
  </body>
  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              date: new Date() 
          },
          mounted: function(){
              //声明一个变量指向 Vue 实例 this.作用域一致
              var _this = this;
              this.timer = setInterval(function(){
                  //修改数据date
                  _this.date = new Date();
              },1000);
          },
          beforeDestroy: function(){
              if(this.timer){
                  //在 Vue 实例销毁前,清除我们的定时器
                  clearInterval();
              }
          }
      }); 
  </script>

这里的{{ date } }输出的是浏览器默认的时间格式,如下所示,所以在这里需要注意时区。有多种方法可以对时间格式化,比如赋值前先使用自定义的函数处理。 Vue 的过滤器( filter )或计算属性 (computed )也可以实现,稍后会介绍到 。



如果有的时候就是想输出 HTML,而不是将数据解释后的纯文本,可以使用 v-html:

  <body>
      <div id="app">
          <span v-html="link"></span>
      </div>
  </body>
  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              link: '<a href="#">我是一个连接,可以点我哈</a>'
          }
      }); 
  </script>

link 的内容将会被渲染为一个具有点击功能的 a 标签,而不是纯文本。这里要注意,如果将用户产生的内容使用 v-html 输出后,有可能导致 xss 攻击,所以要在服务端对用户提交的内容进行处理, 一般可将尖括号“< >”转义。
如果想显示{{}}标签,而不进行替换, 使用 v-pre 即可跳过这个元素和它的子元素的编译过程, 例如:
<span v-pre>{{这里的内容是不会被编译的门}}</ span>
在{{}}中,除了简单的绑定属性值外,还可以使用 JavaScript 表达式进行简单的运算、 三元运算等,例如:

  <body>
      <div id="app">
          {{ number / 10 }}
          {{ isOK ? '确定' : '取消' }}
          {{ text.split(',').reverse().join(',') }}
      </div>
  </body>
  <script>
      var vm = new Vue({
          el: '#app',
          data: {
              number: 100,
              isOK: false,
              text: '123,456'
          }
      }); 
  </script>

显示结果依次为: 10、取消、 456,123。
Vue.js 只支持单个表达式,不支持语句和流控制。另外, 在表达式中,不能使用用户自定义的全局变量, 只能使用 Vue 白名单内的全局变量, 例如 Math 和 Date。 以下是一些无效的示例:

          <!--这是语旬,不是表达式--> 
          {{ var book = 'Vue.js 实战' }}
          <!--不能使用流控制,要使用三元运算-->
          {{ if(ok) return msg }}
  • 过滤器

Vue. 支持在{{}}插值的尾部添加一个管道符 “ ( | )” 对数据进行过滤,经常用于格式化文本,比如字母全部大写、货币千位使用逗号分隔等。过滤的规则是自定义的, 通过给 Vue 实例添加选项 filters 来设置, 例如在上一节中实时显示当前时间的示例,可以对时间进行格式化处理:

 <body>
     <div id="app">
         {{ date | formatDate }}
     </div>
 </body>
 <script>
    //在月份、日期、小时等小于10时前面补0
    var padDate = function(value){
      return value < 10 ? '0' + value : value;
    };
     var vm = new Vue({
         el: '#app',
         data: {
             date: new Date() 
         },
         filters: {
            //这里的value就是要过滤的数据
            formatDate: function(value){
              var date = new Date(value);
              var year = date.getFullYear();
              var month = padDate(date.getMonth() + 1);
              var day = padDate(date.getDate());
              var hours = padDate(date.getDate());
              var minutes = padDate(date.getHours());
              var seconds = padDate(date.getSeconds());
              //将整理好的数据返回出去
              return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
            }
         },
         mounted: function(){
             //声明一个变量指向 Vue 实例 this.作用域一致
             var _this = this;
             this.timer = setInterval(function(){
                 //修改数据date
                 _this.date = new Date();
             },1000);
         },
         beforeDestroy: function(){
             if(this.timer){
                 //在 Vue 实例销毁前,清除我们的定时器
                 clearInterval();
             }
         }
     }); 
 </script>

过滤器也可以串联,而且可以接收参数,例如:

        <!--串联-->
         {{ message | filterA |  filterB }}
        <!--接收参数-->
         {{ date | filterA('arg1','arg2') }}

这里的字符串arg1 和 arg2 将分别传给过滤器的第二个和第三个参数,因为第一个是数据本身。
过滤器应当用于处理简单的文本转换,如果要实现更为复杂的数据变换,应该使用计 算属性,下一章中会详细介绍它的用法。

指令与事件

指令(Directives)是 Vue.js 模板中最常用的一项功能,它带有前缀 v-, 在前文我们已经使用过不少指令了,比如 v-if、 v-html、 v-pre 等。指令的主要职责就是当其表达式的值改变时,相应地将某些行为应用到 DOM 上,以 v-if 例:

 <body>
     <div id="app">
        <p v-if="show">显示这段文本</p>
     </div>
 </body>
 <script>
    var vm = new Vue({
      el: '#app',
      data: {
          show: true
      }
    })
 </script>

当数据 show 的值为 true 时, p 元素会被插入,为 false 时则会被移除。数据驱动 DOM 是 Vue.js 的核心理念,所以不到万不得已时不要主动操作 DOM,你只需要维护好数据, DOM 的事 Vue 会帮你优雅的处理。
Vue.js 内置了很多指令,帮助我们快速完成常见的 DOM 操作,比如循环渲染、显示与隐藏等。 在后面章节中会详细地介绍这些内置指令,但在此之前,你需要先知道 v-bind 和 v-on。 v-bind 的基本用途是动态更新 HTML 元素上的属性,比如 id、 class 等,例如下面几个示例:

 <body>
     <div id="app">
        <a v-bind:href="url">链接</a>
        <img v-bind:src="imgUrl"></img>
     </div>
 </body>
 <script>
    var vm = new Vue({
      el: '#app',
      data: {
          url: 'https://www.baidu.com',
          imgUrl: 'img/1.jpg'
      }
    })
 </script>
  • 示例中的链接地址与图片的地址都与数据进行了绑定,当通过各种方式改变数据时,链接和图片都会自动更新。上述示例渲染后的结果为:
    <a href="https://www.baidu.com">链接</a>
    <img src="img/1.jpg"></img>
  • 以上是介绍 v-bind 最基本的用法,它在 Vue.js 组件中还有着重要的作用,将在后面章节中详细介绍。 另一个非常重要的指令就是 v-on,它用来绑定事件监听器,这样我们就可以做一些交互了, 先来看下面的示例:
 <body>
     <div id="app">
      <p v-if="show">这是一段文本</p>
      <button v-on:click="handleClose">点击隐藏</button>
     </div>
 </body>
 <script>
    var vm = new Vue({
      el: '#app',
      data: {
          show: true
      },
      methods: {
          handleClose: function(){
              this.show = false;
          }
      }
    })
 </script>

在 button 按钮上,使用 v-on:click 给该元素绑定了一个点击事件,在普通元素上, v-on 可以监听原生的 DOM 事件,除了 click 外,还有 dblclick、 keyup, mousemove 等。表达式可以是一个方法名,这些方法都写在 Vue 实例的 methods 属性内,并且是函数的形式,函数内的 this 指向的是当前 Vue 实例本身,因此可以直接使用 this.xxx 的形式来访问或修改数据,如示例中的 this.show = false;把数据 show 修改为了 false,所以点击按钮时,文本 p 元素就被移除了。 表达式除了方法名, 也可以直接是一个内联语句,上例也可以改写为:

 <body>
     <div id="app">
      <p v-if="show">这是一段文本</p>
      <button v-on:click="show = false">点击隐藏</button>
     </div>
 </body>
 <script>
    var vm = new Vue({
      el: '#app',
      data: {
          show: true
      }       
    })
 </script>

如果绑定的事件要处理复杂的业务逻辑,建议还是在 methods 里声明一个方法,这样可读性更强也好维护。
Vue.js 将 methods 里的方法也代理了,所以也可以像访问 Vue 数据那样来调用方法:

 <body>
     <div id="app">
      <p v-if="show">这是一段文本</p>
      <button v-on:click="handleClose">点击隐藏</button>
     </div>
 </body>
 <script>
    var vm = new Vue({
      el: '#app',
      data: {
          show: true
      },
      methods: {
          handleClose: function(){
              this.close();
          },
          close: function(){
              this.show = false;
          }
      }
    })
 </script>

在 handleClose 方法内,直接通过 this.close() 调用了 close() 函数。在上面示例中是多此一举的, 只是用于演示它的用法,在业务中会经常用到,例如以下几种用法都是正确的:

 <script>
    var vm = new Vue({
      el: '#app',
      data: {
          show: true
      },
      methods: {
          init: function(text){
              console.log(text);
          }       
      },
      mounted: function(){
          this.init('在初始化时调用');//在初始化时调用
      }
    });
    vm.init('通过外部调用');//在 Vue 实例外部调用
 </script>

更多关于 v-on 事件的用法将会在后面章节中详细介绍。

语法糖

语法糖是指在不影响功能的情况下, 添加某种方法实现同样的效果, 从而方便程序开发。
Vue.js 的 v-bind 和 v-on 指令都提供了语法糖, 也可以说是缩写, 比如 v-bind, 可以省略 v-bind, 直接写一个冒号 “:”

       <a v-bind:href="url">链接</a>
       <img v-bind:src="imgUrl"></img>
       <!--缩写为-->
       <a :href="url">链接</a>
       <img :src="imgUrl"></img>

v-on 可以直接用 “@” 来缩写:

      <button v-on:click="handleClose">点击隐藏</button>
      <!--缩写为-->
      <button @click="handleClose">点击隐藏</button>

使用语法糖可以简化代码的书写, 从下一章开始, 所有示例的 v-bind 和 v-on 指令将默认使用语法糖的写法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容