愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

Vue.js学习使用总结(一)基础篇

由于有一段时间没用Vue了,之前学习的时候Vue还是1.0版本,现在出了2.0版本,所以重新学习一遍Vue,并做个记录,让自己印象更深刻

一、Vue实例

1、构造器

每个Vue应用,都是通过构造函数Vue来创建的,如:

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

选项中,包含有:

  • 数据(data
  • 方法(methods
  • 模板(template
  • 挂载元素(el
  • 生命周期钩子

可以扩展vue的构造器,从而用预定义的选项创建可复用的组件构造器,如:

var MyCOM = Vue.extend({
    // 拓展选项
});
new MyCOM();

2、属性和方法

每个Vue的实例,都会自动将data对象里的属性挂载到Vue的实例上,如:

var data = {
    a: 1,
    b: 2
}
var vm = new Vue({
    data: data
});
// 那么,会有以下的情况
vm.a; // 1
vm.b; // 2

vm.a = 3;
data.a; // 3

data.b = 4;
vm.b; // 4

注意: 只有在创建时候传入的属性才是可响应的,如果在实例创建完毕后,再添加新的属性到实例上,那么对它的变更不会触发视图的更新

为了将实例自身的属性和方法与挂载的属性方法区分开来,Vue中采用$为前缀,来区分,如:

var data = { a: 1 }
var vm = new Vue({
    el: '#app',
    data: data
});

vm.$data === data; // true
vm.$el === document.getElementById('app'); // true

3、生命周期

每个Vue实例在创建完成之前,都需要经历一系列的初始化过程(配置数据观测、编译模板、挂载实例到DOM、数据变化时更新DOM),这个过程中,Vue提供了一系列的生命周期钩子,允许我们更灵活地在生命周期中执行自定义的逻辑。如created钩子,便是在实例被创建完成后调用:

var vm = new Vue({
    data: {
        a: 1
    },
    created: function() {
        console.log('created');
    }
});
// 运行后,控制台输出“created”

生命周期的完整过程,图示如:


二、模板语法

Vue使用基于HTML的模板语法,于是我们可以声明式地将DOM绑定至底层Vue实例的数据。底层实现中,Vue会将模板编译为Virtual DOM渲染函数,并自动计算最小重新渲染代价以应用。

1、插值

数据绑定常用的方式是Mustache语法,即双大括号包围的串。如:{{message}}Mustache标签会被自动替换为对应的vue实例上的属性值,实例中的属性值一旦发生变更,Mustache插值处的属性值也会随之更新。
如果我们希望属性值不随之更新,那么可以使用v-once指令:

<span v-once>{{message}}</span>

2、插入HTML

默认情况下,Mustache语法是插入纯文本的。如果希望插入HTML,那么需要使用v-html指令,v-html指令接受一个参数,表示要插入的html是由实例的哪个属性值指定的。如:

<div v-html="rawHTML"></div>

3、属性

对于HTML标签的属性,我们不能直接使用Mustache语法,应该使用v-bind指令,即以下做法是错误的:

<div id="{{idName}}"></div>

应该使用:

<div v-bind:id="idName"></div>

4、表达式

Mustachev-bind中,都可以使用合法的JavaScript表达式(注意是表达式,不是语句,如果是语句,则不会生效),如:

{{ number + 1 }}
{{ ok ? 'Yes' : 'No' }}
<div v-bind:id="'list_' + idName"></div>

像下面的这些,是语句,而不是表达式,所以不会生效:

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

此外,模板表达式都被放在沙盒中,因此它只能访问在白名单中的全局变量(如MathDate),而不能访问用户自定义的全局变量

5、指令

指令这一概念,在前文中已经出现多次。Vue中的指令,其职责是当指令中的表达式(应该为单一表达式)的值发生变化时,某些行为能够得到响应。如:

  • v-if="seen"(表示根据seen属性的值显示或者隐藏)
  • v-bind:href="url"(表示将标签中href属性的值,和实例中的url属性绑定)
  • v-on:click="handler"(表示绑定事件监听函数)

指令都是以v-开头的:
1)它可以接受参数(通过:,如v-on:click
2)指令还可以接受一些修饰符,用于表示一个指令的特殊绑定方式,如v-on:click.prevent,表示对于触发的事件,执行event.preventDefault()

6、过滤器

所谓的过滤器,就是对值进行处理的函数。过滤器可以运用在Mustache插值处v-bind中,使用|来指示。如:

<!-- Mustache -->
{{ message | ucfirst }}
<!-- 而在属性中可以这么使用 -->
<span v-bind:title="message | ucfirst"></span>

过滤器函数中,表达式总是作为函数的第一个参数的。如:

<div id="app">
    {{ message | ucfirst }}
</div>
<script>
var vm = new Vue({
    el: '#app',
    data: {
        message: 'hello, world!'
    },
    filters: {
        ucfirst: function(value) {
            return value[0].toUpperCase() + value.substring(1)
        }
    }
});
</script>

过滤器是可以串联的,如:{{ message | handlerA | handlerB }},此外,过滤器也可以接收参数(接收的参数作为过滤器的第2~n个参数),如{{ message | handler('string', param) }}

7、缩写

1)v-bind:xxx可以缩写为:xxx
2)v-on:xxx可以缩写为@xxx


三、计算属性与watch

1、计算属性

Vue提供了计算属性这一功能,它能够在其依赖关系上的值发生改变的时候,自动响应并更新。同时,依赖值的计算是带有计算缓存的,所以,计算属性具有更高的性能。

<div id="app">{{ reversedMessage }}</div>

<script>
var vm = new Vue({
    el: '#app',
    data: {
        message: 'Hello'
    },
    computed: {
        reversedMessage: function() {
            return this.message.split('').reverse().join('');
        }
    }
});
</script>

这种情况下,当message的值发生改变的时候,reversedMessage的值也会相应地发生改变。
那么,计算属性,和methods有什么区别,例如,我们同样可以使用以下的代码来达到同样的效果:

<div id="app">{{ reversedMessage }}</div>
<script>
var vm = new Vue({
    el: '#app',
    data: {
        message: 'Hello'
    },
    methods: {
        reversedMessage: function() {
            return this.message.split('').reverse().join('');
        }
    }
});
</script>

区别在于,计算属性只有在其依赖发生改变的时候,才会重新计算,而method则每次都会重新计算。所以,当我们希望计算值进行缓存的时候,可以使用计算属性,而若不希望缓存值,则method是一个更好的选择。

2、watch

watch是Vue提供的一种机制,它会观察和响应Vue实例上的数据变动。如:

var vm = new Vue({
    el: '#app',
    data: {
        firstName: '',
        lastName : '',
        fullName : ''
    },
    watch: {
        firstName: function() {
            this.fullName = this.firstName + ' ' + this.lastName;
        },
        lastName: function() {
            this.fullName = this.firstName + ' ' + this.lastName;
        }
    }
});

这种情况下,当firstNamelastName的值发生改变的时候,fullName的值也就会跟着改变。但是这种情况下,使用watch并不是最佳的选择,因为我们可以发现,firstNamelastName的watcher在做着类似的工作。而这种情况下,使用计算属性可能是更加的选择,如:

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

但是,也不是什么情况下,计算属性都是最佳的方案。比如我们希望通过一个异步任务来获得一个值,而获得值之前,显示一个中间值,这种情况下,计算属性是没有办法做到的,我们就只能使用watch属性,如:

var vm = new Vue({
    el: '#app',
    data: {
        question: '',
        answer: 'No answer'
    },
    watch: {
        question: function(newValue) {
            this.answer = 'Getting Answer...';
            setTimeout(() => {
                this.answer = 'You got a new Value:' + newValue;
            }, 1000);
        }
    }
})

3、setter

计算属性默认情况下,是个getter,但是我们也可以指定一个setter,示例如:

var vm = new Vue({
    el: '#app',
    data: {
        firstName: '',
        lastName : ''
    },
    computed: {
        fullName: {
            set: function(newValue) {
                var names = newValue.split(' ');
                this.firstName = names[0];
                this.lastName  = names[names.length - 1];
            },
            get: function() {
                return this.firstName + ' ' + this.lastName;
            }
        }
    }
});


四、Class与Style绑定

1、动态绑定一个class

<div v-bind:class="{active: isActive}"></div>
<!-- isActive为true时,编译为: -->
<div class="active"></div>

如此就会根据isActive的值是否为true显示或者隐藏active这个class,此外,还可以是以下的形式:

<div v-bind:class="{active: isActive, 'green': true"}></div>
<!-- isActive为true时,编译为: -->
<div class="active green"></div>

类自身的classv-bind:class是共存的,如:

<div class="demo" v-bind:class="{active: isActive, 'green': true"}></div>
<!-- isActive为true时,编译为: -->
<div class="demo active green"></div>

也可以将class绑定为data里的一个对象,如:

<div v-bind:class="classObject"></div>
data: {
    classObject: {
        active: true,
        'is-red': true
    }
}

2、数组语法

可以把一个数组传给v-bind:class来确定一个class列表,如:

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

3、内联样式

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

编译为:

<div style="font-size: 20px; color: #F00"></div>

当然,也可以像v-bind:class那样子,直接绑定到一个对象上,如v-bind:style="styleObject"
注意:当v-bind:style需要使用特定前缀的CSS属性时,Vue会自动检测并添加前缀。
内联样式中,还可以使用数组来绑定多个对象,如:

<div v-bind:style="[styleObject1, styleObject2]"></div>

在Vue2.3.0起,加入了多重值功能,如:

<div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

通常情况下,会渲染被浏览器支持的数组中的最后一个值


五、条件渲染

1、v-ifv-else-ifv-else

Vue中可以使用条件渲染,如:

<div v-if="show"></div>

或者

<div v-if="show"></div>
<div v-else></div>

或者

<div v-if="showA"></div>
<div v-else-if="showB"></div>
<div v-else></div>

当我们要对一组元素进行条件渲染的时候,可以使用<template></template>包围,如:

<template v-if="show">
    <div>A</div>
    <div>B</div>
</template>

注意: Vue会尽可能高效地进行渲染,会复用已有的元素而非从头开始渲染。所以,对于以下的例子,切换的时候将不会清除用户输入(因为复用了input组件)

<div v-if="loginType === 'username'">
    Username: <input type="text" placeholder="Username" />
</div>
<div v-else>
    Email: <input type="text" placeholder="Email" />
</div>

但是,当我们希望切换loginType的时候不复用组件的时候,怎么办?可以通过添加唯一值的key来代表两个元素是完全独立的,不可复用的:

<div v-if="loginType === 'username'">
    Username: <input type="text" placeholder="Username" key="username-input" />
</div>
<div v-else>
    Email: <input type="text" placeholder="Email" key="email-input" />
</div>

2、v-show

v-show也可以用来实现条件渲染,但是它不支持v-else,它仅仅是做display的切换。所以,v-if是真正的条件渲染,但是v-show不是。相比之下:
1)v-if有更高的切换开销,适合于不经常改变的条件(此外,v-if是惰性的,如果初始渲染条件为false,则什么也不做)
2)v-show有更高的初始渲染开销,但是切换开销较小,适合频繁地切换时使用


六、列表渲染

可以使用v-for进行列表渲染,如:

<div v-for="item in items"></div>

或者

<div v-for="(item, index) in items"></div>

或者,也支持遍历一个对象,如:

<div v-for="(value, key, index) in someObject">
    <!-- // ... -->
</div>

或者使用整数迭代:

<div v-for="n in 5">{{ n }}</div>
<!-- 编译为 -->
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>

其中,in也可以使用of替换。也可以像条件渲染那样子,使用<template>来包围多个元素。

v-forv-if同时使用

v-forv-if同时使用的时候,即:

<div v-for="item in items" v-if="item.show"></div>

这种情况下,v-for的优先级是大于v-if

使用key

使用key,可以给Vue一个提示,以便于Vue跟踪每个节点的身份,从而重用和重新排序现有元素。如:

<div v-for="item in items" :key="item.id"></div>

数组更新检测

1、变异方法
当使用数组的push()pop()unshift()shift()splice()sort()reverse()方法时,Vue因为做过了处理,所以会触发视图层的更新。因为这些数组方法是会对原数组产生影响的,所以称为变异方法
2、重塑方法
有些方法,如filter()concat()slice(),不会改变原数组,而是返回一个新的数组,所以当使用非变异方法时候,对于以下的操作:

some.items = some.items.filter(function(item) {
    return item.message.match(/Foo/);
});

这种情况下,用新的数组替换掉了旧的数组。但是Vue 不会丢弃现有DOM来重新渲染整个列表,Vue会尽最大可能地进行DOM元素复用
3、无法检测的情况
由于JavaScript的限制,Vue对以下行为是检测不到的:

  • 使用索引值直接设置一个项时,如:vm.items[indexOfItem] = new Value
    解决方法: 使用Vue.set(example.items, indexOfItem, newValue),或者example.items.splice(indexOfItem, 1, newValue)
  • 修改了数组的长度时,如:vm.items.length = newLength
    解决方法: 使用splice,如examples.items.splice(newLength)


七、事件处理器

1、监听事件

例如:<div v-on:click="params"></div>
params可以是:

  • 事件方法名称(定义在methods里的)
  • 合法的表达式,如count += 1
  • 内联JavaScript语句,如say('Hello')。注意,如果需要在内联语句中访问原生DOM事件,可以传入$event变量,如:
<button v-on:click="fn($event)">Submit</button>
methods: {
    fn: function(event) {
        // ...
    }
}

2、事件修饰符

为了让methods里的方法有纯粹的数据处理逻辑,可以使用事件修饰符。如:

  • .stop 阻止冒泡
  • .prevent 阻止默认事件
  • .captrue 使用捕获
  • .self 只在元素本身发生事件时才触发回调
  • .once 只触发一次事件
  • v-on:keyup.keyCode 当按下的键对应为keyCode对应的键时触发回调
  • v-on:keyup.key 直接提供按键别名(如keyup.enter,表示按enter键)

默认情况下,支持如下的按键别名:
.enter.tab.delete.esc.space.up.down.left.right.ctrl.alt.shift.meta
如果需要自定义按键别名,使用Vue.config.keyCodes定义,如:

Vue.config.keyCodes.f1 = 112;


八、表单控件绑定

v-model可以实现双向数据绑定,它会根据控件的类型,自动选取正确的方法来更新元素。v-model本质上是监听用户的输入、更新数据

1、文本

<input type="text" v-model="message">

2、多行文本

<textarea v-model="textContent"></textarea>
注意,对于多行文本而言,使用插值是无效的,只能使用v-model

3、复选框

<input type="checkbox" v-model="checked" />
如果是多个多选框的话,那么将绑定到一个数组中,如

<div id="app">
    <label><input type="checkbox" value="A" v-model="checkedArr"> A</label>
    <label><input type="checkbox" value="B" v-model="checkedArr"> B</label>
    <label><input type="checkbox" value="C" v-model="checkedArr"> C</label>

    {{ checkedArr }}
</div>

4、单选按钮

<input type="radio" value="Boy" v-model="gender" />

5、单选列表

<select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
</select>

6、多选列表(绑定到一个数组)

<select multiple v-model="selectedArr">
    <option>A</option>
    <option>B</option>
    <option>C</option>
</select>

注意,如果是动态选项的话,可以使用v-for渲染,如:

<select v-model="selected">
    <option v-for="opt in opts" v-bind:value="opt.value">{{ opt.text }}</option>
</select>

7、绑定value

通常情况下,v-model绑定的是静态字符串(对于勾选框,是逻辑值),我们也可以将value绑定到Vue实例的一个动态属性上,此时就可以使用v-bind实现,如:

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

此时,会有如下情况:

vm.toggle === vm.a // 选中时
vm.toggle === vm.b // 未选中时

对于v-bind:后接的属性值,不同的表单控件是不同的,如:

  • input的checkbox,使用true-valuefalse-value
  • input的radio,使用pick
  • select的情况下,使用selected
  • 其他的文本类,使用value

修饰符:
使用如:v-model.number="age"

  • .lazy 限定只在change事件中同步
  • .number 限制只能输入Number类型的数值(如果转化后为NaN,则返回原值)
  • .trim 去除首尾空格