Vue.js 前端开发 快速入门与专业应用
上QQ阅读APP看书,第一时间看更新

2.2 数据绑定

Vue.js作为数据驱动视图的框架,我们首先要知道的就是如何将数据在视图中展示出来,传统的Web项目中,这一过程往往是通过后端模板引擎来进行数据和视图的渲染,例如PHP的smarty, Java的velocity和freemarker。但这样导致的情况是前后端语法会交杂在一起,前端HTML文件中需要包含后端模板引擎的语法,而且渲染完成后如果需要再次修改视图,就只能通过获取DOM的方法进行修改,并手动维持数据和视图的一致。而Vue.js的核心是一个响应式的数据绑定系统,建立绑定后,DOM将和数据保持同步,这样就无需手动维护DOM,使代码能够更加简洁易懂、提升效率。

2.2.1 数据绑定语法

本小节主要介绍Vue.js的数据绑定语法,出现的例子会基于以下js代码:

        var vm = new Vue({
          el : '#app',
          data: {
              id : 1,
              index : 0,
              name : 'Vue',
              avatar : 'http://……'
              count : [1, 2, 3, 4, 5]
              names : ['Vue1.0', 'Vue2.0'],
              items : [
                { name : 'Vue1.0', version : '1.0' },
                { name : 'Vue1.1', version : '1.0' }
              ]
          }
        });

1.文本插值

数据绑定最基础的形式就是文本插值,使用的是双大括号标签{{}},为“Mustache”语法(源自前端模板引擎Mustache.js),示例如下:

        <span>Hello {{ name }}</span> // -> Hello Vue

Vue.js实例vm中name属性的值将会替换Mustache标签中的name,并且修改数据对象中的name属性,DOM也会随之更新。在浏览器console中运行vm.name = 'Vue 1.0',输出结果为Hello Vue 1.0。

模板语法同时也支持单次插值,即首次赋值后再更改vm实例属性值不会引起DOM变化,例如以下模板在运行vm.name = 'Vue 1.0’后,依旧会输出Hello Vue:

        <span>Hello {{* name }} </span> // -> Hello Vue

Vue.js 2.0去除了{{*}}这种写法,采用v-once代替。以上模板需要改写为<span v-once=”name”>{{name}}</span>。

2.HTML属性

Mustache标签也同样适用于HTML属性中,例如:

        <div id="id-{{id}}"></div>  // <div id="id-1"></div>

Vue.js 2.0中废弃了这种写法,用v-bind指令代替,<div v-bind:id="'id-' + id"/></div> 代替,或简写为<div :id="'id-' + id"></div>

3.绑定表达式

放在Mustache标签内的文本内容称为绑定表达式。除了直接输出属性值之外,一段绑定表达式可以由一个简单的JavaScript表达式和可选的一个或多个过滤器构成。例如:

        {{ index + 1 }}  // 1
        {{ index == 0 ? 'a' : 'b'}} // a
        {{ name.split('').join('|') }}  // V|u|e

每个绑定中只能包含单个表达式,并不支持JavaScript语句,否则Vue.js就会抛出warning异常。并且绑定表达式里不支持正则表达式,如果需要进行复杂的转换,可以使用过滤器或者计算属性来进行处理,以下的例子即为无效的表达式:

        {{ var a = 1 }}    // 无效
        {{ if (ok) { return name } }} // 无效,但可以写成ok ? name : '' 或者ok && name这
    样的写法

Vue.js绑定表达式warning:

4.过滤器

Vue.js允许在表达式后添加可选的过滤器,以管道符“|”指示。示例:

        {{ name | uppercase }} // VUE

Vue.js将name的值传入给uppercase这个内置的过滤器中(本质是一个函数),返回字符串的大写值。同时也允许多个过滤器链式使用,例如:

        {{ name | filterA | filterB }}

也允许传入多个参数,例如:

        {{ name | filterA arg1 arg2}}

此时,filterA将name的值做为第一个参数,arg1, arg2做为第二、第三个参数传入过滤器函数中。最终函数的返回值即为输出结果。arg1, arg2可以使用表达式,也可以加上单引号,直接传入字符串。例如:

        {{ name.split('') | limitBy 3 1 }} // ->u, e

过滤器limitBy可以接受两个参数,第一个参数是设置显示个数,第二个参数为可选,指从开始元素的数组下标。

Vue.js内置了10个过滤器,下面简单介绍它们的功能和用法。

① capitalize:字符串首字符转化成大写

② uppercase:字符串转化成大写

③ lowercase:字符串转化成小写

④ currency参数为{String}[货币符号] , {Number} [小数位],将数字转化成货币符号,并且会自动添加数字分节号。例如:

        {{ amount | currency '¥' 2 }} // -> 若amount值为10000,则输出¥10,000.00

⑤ pluralize参数为{String} single, [double, triple],字符串复数化。如果接收的是一个参数,那复数形式就是在字符串末尾直接加一个“s”。如果接收多个参数,则会被当成数组处理,字符串会添加对应数组下标的值。如果字符串的个数多于参数个数,多出部分会都添加最后一个参数的值。例如:

        <p v-for="c in count">{{ c | pluralize 'item' }}   {{ c | pluralize 'st'
    'nd' 'rd' 'th' }}</p>

输出结果:

⑥ json参数为{Number}[indent]空格缩进数,与JSON.stringify()作用相同,将json对象数据输出成符合json格式的字符串。

⑦ debounce传入值必须是函数,参数可选,为{Number}[wait],即延时时长。作用是当调用函数n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。例如:

        <input v-on:keyup ="onKeyup | debounce 500"> // input元素上监听了keyup事件,
    并且延迟500ms触发

⑧ limitBy传入值必须是数组,参数为{Number}limit, {Number}[offset], limit为显示个数,offset为开始显示数组下标。例如:

        <div v-for="item in items | limitBy 10"></div>  // items为数组,且只显示数
    组中的前十个元素

⑨ filterBy传入值必须是数组,参数为{String | Function} targetStringOrFunction,即需要匹配的字符串或函数(通过函数返回值为true或false来判断匹配结果); “in”(可选分隔符); {String}[…searchKeys],为检索的属性区域。示例:

        <p v-for="name in names | filterBy '1.0'">{{name}}</p>  // 检索items数组中
    值包含1.0的元素
        <p v-for="item in items | filterBy '1.0' in 'name'">{{ item | json}}</p>
    // 检索items数组中元素属性name值为1.0的元素输出。检索区域也可以为数组,即in [name,
    version],在多个属性中进行检索

上述两个例子的输出结果为:

        <p v-for="item in items | filterBy customFilter">{{ item | json}}</p> //
    使用自定义的过滤函数,函数可以在选项methods中定义
        methods : {
          customFilter : function(item) {
            if(item.name) return true  // 检索所有元素中包含name属性的元素
          }
        }

⑩ orderBy传入值必须是数组,参数为{String|Array|Function}sortKeys,即指定排序策略。这里可以使用单个键名,也可以传入包含多个排序键名的数组。也可以像Array.Sort()那样传入自己的排序策略函数。第二个参数为可选参数{String}[order],即选择升序或降序,order>=0为升序,order<0为降序。下面以三种不同的参数例子来说明具体的用法:

        单个键名:<p v-for="item in items | orderBy 'name' -1">{{item.name}}</p>
    // items数组中以键名name进行降序排列
        多个键名:<p v-for="item in items | orderBy [name, version] ">{{item.name}}</p> //
    使用items里的两个键名进行排序
        自定义排序函数: <p v-for="item in items | orderBy customOrder">{{item.name}}
    </p>
        methods: {
          customOrder: function (a, b) {
            return parseFloat(a.version) > parseFloat(b.version)  //  对比item中
    version的值的大小进行排序
          }
        }

需要注意的是,Vue.js 2.0中已经去除了内置的过滤器,但也不用担心,我们会在第4章中详细说明过滤器的用法,以及如何声明自定义过滤器。而且Vue.js的社区中本身就有优秀的开源过滤器,比如处理时间的moment.js,和货币格式化处理的account.js,我们会在第8章中说明如何使用Vue.js的插件。

5.指令

Vue.js也提供指令(Directives)这一概念,可以理解为当表达式的值发生改变时,会有些特殊行为作用到绑定的DOM上。指令通常会直接书写在模板的HTML元素中,而为了有别于普通的属性,Vue.js指令是带有前缀的v-的属性。写法上来说,指令的值限定为绑定表达式,所以上述提到的JavaScript表达式及过滤器规则在这里也适用。本书会在第3章中详细讲述指令及自定义指令的作用。本节先简单介绍指令绑定数据和事件的语法。

① 参数

        <img v-bind:src="avatar" />

指令v-bind可以在后面带一个参数,用冒号(:)隔开,src即为参数。此时img标签中的src会与vm实例中的avatar绑定,等同于:

        <img src="{{avatar}}" />

② 修饰符

修饰符(Modifiers)是以半角句号.开始的特殊后缀,用于表示指令应该以特殊方式绑定。

        <button v-on:click.stop="doClick"></button>

v-on的作用是在对应的DOM元素上绑定事件监听器,doClick为函数名,而stop即为修饰符,作用是停止冒泡,相当于调用了e. stopPropagation()。

2.2.2 计算属性

在项目开发中,我们展示的数据往往需要经过一些处理。除了在模板中绑定表达式或者利用过滤器外,Vue.js还提供了计算属性这种方法,避免在模板中加入过重的业务逻辑,保证模板的结构清晰和可维护性。

1.基础例子

        var vm = new Vue({
          el : '#app,
          data: {
              firstName : 'Gavin',
              lastName: 'CLY'
          }
          computed : {
              fullName : function() {
                // this指向vm实例
                return this.firstName + ' ' + this.lastName
              }
          }
        });
        <p>{{ firstName }}</p> // Gavin
        <p>{{ lastName }}</p> // CLY
        <p>{{ fullName }}</p> // Gavin CLY

此时,你对vm.firstName和vm.lastName进行修改,始终会影响vm.fullName。

2.Setter

如果说上面那个例子并没有体现出来计算属性的优势的话,那计算属性的Setter方法,则在更新属性时给我们带来了便利。示例:

        var vm = new Vue({
          el : '#el',
          data: {
              cents : 100,
          }
          computed : {
              price : {
                set : function(newValue) {
                  this.cents = newValue * 100;
                },
                get : function() {
                  return (this.cents / 100).toFixed(2);
                }
              }
          }
        });

在处理商品价格的时候,后端往往会把价钱定义成以分为单位的整型,避免在处理浮点类型数据时产生的问题。而前端则需要把价钱再转成元进行展示,而且如果需要对价钱进行修改的话,则又要把输入的价格再恢复到分传给后端,很是繁琐。

而在使用Vue.js的计算属性后,我们可以将vm.cents设置为后端所存的数据,计算属性price为前端展示和更新的数据。

        <p>&yen; {{price}}</p>  // ¥1.00

此时更改vm.price = 2, vm.cents会被更新为200,在传递给后端时无需再手动转化一遍数据。

2.2.3 表单控件

Vue.js中提供v-model的指令对表单元素进行双向数据绑定,在修改表单元素值的同时,实例vm中对应的属性值也同时更新,反之亦然。本小节会介绍主要input元素绑定v-model后的具体用法和处理方式,示例所依赖的js代码如下:

        var vm = new Vue({
          el : '#app',
          data: {
            message : '',
            gender : '',
            checked : '',
            multiChecked : [],
            selected : '',
            multiSelected : []
          }
        });

1.Text

输入框示例,用户输入的内容和vm.message直接绑定:

        <input type="text" v-model="message" />
        <span>Your input is : {{ message }}</span>

2.Radio

单选框示例:

        <label><input type="radio" value="male" v-model="gender ">男</lable>
        <label><input type="radio" value="female" v-model="gender ">女</lable>
        <p>{{ gender }}</p>

gender值即为选中的radio元素的value值。

3.Checkbox

Checkbox分两种情况:单个勾选框和多个勾选框。

单个勾选框,v-model即为布尔值,此时input的value并不影响v-model的值。

        <input type="checkbox" v-model="checked" />
        <span>checked : {{ checked }}</span>

多个勾选框,v-model使用相同的属性名称,且属性为数组。

        <label><input type="checkbox" value="1" v-model=" multiChecked">1</lable>
        <label><input type="checkbox" value="2" v-model=" multiChecked">2</lable>
        <label><input type="checkbox" value="3" v-model=" multiChecked">3</lable>
        <p>MultiChecked: {{ multiChecked.join('|') }}</p>

4.Select

同Checkbox元素一样,Select也分单选和多选两种,多选的时候也需要绑定到一个数组。

单选:

        <select v-model="selected">
          <option selected>A</option>
          <option>B</option>
          <option>C</option>
        </select>
        <span>Selected: {{ selected }}</span>

多选:

        <select v-model="multiSelected" multiple>
          <option selected>A</option>
          <option>B</option>
          <option>C</option>
        </select>
        <br>
        <span>MultiSelected: {{ multiSelected.join('|') }}</span>

5.绑定value

表单控件的值同样可以绑定在Vue实例的动态属性上,用v-bind实现。示例:

1.Checkbox

<input type="checkbox" v-model="checked" v-bind:true-value="a" v-bind:false-value="b">

选中:vm.checked == vm.a // -> true

未选中:vm.checked == vm.b // -> true

2.Radio

<input type="radio" v-model="checked", v-bind:value="a">

选中: vm.checked == vm.a // -> true

3.Select Options

<select v-model="selected">

<! -- 对象字面量 -->

<option v-bind:value="{ number: 123 }">123</option>

</select>

选中:

typeof vm.selected // -> 'object'

vm.selected.number // -> 123

6.参数特性

Vue.js为表单控件提供了一些参数,方便处理某些常规操作。

① lazy

默认情况下,v-model在input事件中同步输入框值与数据,加lazy属性后从会改到在change事件中同步。

        <input v-model="query" lazy />

② number

会自动将用户输入转为Number类型,如果原值转换结果为NaN则返回原值。

        <input v-model="age" number/>

③ debounce

debounce为设置的最小延时,单位为ms,即为单位时间内仅执行一次数据更新。该参数往往应用在高耗操作上,例如在更新时发出ajax请求返回提示信息。

        <input v-model="query" debounce="500" />

不过Vue.js 2.0中取消了lazy和number作为参数,用修饰符(modifier)来代替:

        <input v-model.lazy="query" />  <input v-model.numer="age" />

新增了trim修饰符,去掉输入值首尾空格:

        <input v-model.trim="name" />

去除了debounce这个参数,原因是无法监测到输入新数据,但尚未同步到vm实例属性时这个状态。如果仍有需要,官方提供了手动实现的例子https://jsbin.com/zefawu/3/edit?html, output。

2.2.4 Class与Style绑定

在开发过程中,我们经常会遇到动态添加类名或直接修改内联样式(例如tab切换)。class和style都是DOM元素的attribute,我们当然可以直接使用v-bind对这两个属性进行数据绑定,例如<p v-bind:style='style'><p>,然后通过修改vm.style的值对元素样式进行修改。但这样未免过于繁琐而且容易出错,所以Vue.js为这两个属性单独做了增强处理,表达式的结果类型除了字符串之外,还可以是对象和数组。本小节就会对这两个属性具体的用法进行说明。

1.Class绑定

首先说明的是class属性,我们绑定的数据可以是对象和数组,具体的语法如下:

① 对象语法:v-bind:class接受参数是一个对象,而且可以与普通的class属性共存。

        <div class="tab" v-bind:calss="{'active' : active , 'unactive' : ! active}">
    </div>
        vm实例中需要包含
          data : {
            active : true
          }

渲染结果为: <div class="tab active"></div>

② 数组语法:v-bind:class也接受数组作为参数。

        <div v-bind:class="[classA, classB]"></div>
        vm实例中需要包含
          data : {
            classA : 'class-a',
            classB : 'class-b'
          }

渲染结果为:<div class="class-a class-b"></div>。

也可以使用三元表达式切换数组中的class, <div v-bind:class="[classA, isB ? classB :'']"></div>。如果vm.isB = false,则渲染结果为<div v-bind:class="class-a"></div>。

2.内联样式绑定

style属性绑定的数据即为内联样式,同样具有对象和数组两种形式:

① 对象语法:直接绑定符合样式格式的对象。

        <div v-bind:style="alertStyle"></div>
        data : {
          alertStyle : {
              color : 'red',
              fontSize : '20px'
          }
        }

除了直接绑定对象外,也可以绑定单个属性或直接使用字符串。

        <div v-bind:style="{ fontSize : alertStyle.fontSize, color : 'red'}"></div>

② 数组语法:v-bind:style允许将多个样式对象绑定到统一元素上。

        <div v-bind:style="[ styleObjectA, styleObjectB]" .></div>

3.自动添加前缀

在使用transform这类属性时,v-bind:style会根据需要自动添加厂商前缀。:style在运行时进行前缀探测,如果浏览器版本本身就支持不加前缀的css属性,那就不会添加。