该篇为翻译文章:原文为:Intro to Vue.js: Rendering, Directives, and Events
该系列文章目录:
Vue.js #1-渲染、指令、事件 (当前)
Vue最让我喜欢的一点就是它汲取了许多其它框架的优点,比如:
- 视图层是由虚拟DOM生成的组件组成的。同时,它还拥有像React中的props以及Redux的store一样的东西。
- 有选择性的渲染机制及服务,这一点像Angular。
- 在简洁和性能方面,借鉴了Polymer的一些东西,Vue提供了相似的开发风格把HTML,CSS,JavaScript串起来。
除此之外,和其它框架相比,Vue还有一些让我喜欢的优点可说:例如,它提供了更具语义化的API。在性能上面比React还要好一点。不会像Polymer那样有很多没用的polyfills,在MVC方面,它不像Angular有很多固执的观点。
其实还有很多别的东西,你可以读一下Vue官方的这篇文章:「对比其他框架」
Hello World!
我们最好还是通过使用Vue来写一个Hello World!
来看一下它的基本用法吧:
<div id="app">
{{ text }} Nice to meet Vue.
</div>
new Vue({
el: '#app',
data: {
text: 'Hello World!'
}
});
结果:http://codepen.io/sdras/pen/b52b1252469a353830683aeaccbecd01
如果你熟悉React的话,那么上面的Demo会和它有一些相似之处。我们在HTML中使用「胡子模板」(即花括号{{ }}
)来嵌入一个JS的变量,但和React的JSX不同,我们是直接使用HTML来完成的。JSX固然很好用,但仍然有一些操作是比较麻烦的,比如你每要把class
写成className
等等。当然,最重要的一点是,你可以看到上面的Demo非常简洁,非常容易上手。
接下来让我们来用一下Vue当中非常好用的「循环和条件渲染」。
条件渲染
假如我们要渲染一个导航列表,如果使用传统的JS(这里我们使用Babel)来写的话是这样一个思路:创意一个数组,然后对应导航的每一项都要创建一个<li>
,然后再创建一个<ul>
把它们包起来放到DOM当中:
<div id="container"></div>
const items = [
'thingie',
'another thingie',
'lots of stuff',
'yadda yadda'
];
function listOfStuff() {
let full_list = '';
for (let i = 0; i < items.length; i++) {
full_list = full_list + `<li> ${items[i]} </ul></li>`
}
const contain = document.querySelector('#container');
contain.innerHTML = `<ul> ${full_list} </ul>`;
}
listOfStuff();
结果:http://codepen.io/sdras/pen/e699f60b79b90a35401cc2bcbc588159
Demo可以正常工作,但是看起来非常的乱。现在让我们换成Vue当中的v-for
指令来试一下:
<div id="app">
<ul>
<li v-for="item in items">
{{ item }}
</li>
</ul>
</div>
const app4 = new Vue({
el: '#app',
data: {
items: [
'thingie',
'another thingie',
'lots of stuff',
'yadda yadda'
]
}
});
结果:http://codepen.io/sdras/pen/af6307c633262350c9642f554ff64b55
非常的简洁易懂。如果你熟悉Angular的话,那么一定会觉得它们很像。
另一个很棒的功能是通过使用v-model
来进行动态绑定:
<div id="app">
<h3>Type here:</h3>
<textarea v-model="message" class="message" rows="5" maxlength="72"></textarea><br>
<p class="booktext">{{ message }} </p>
</div>
new Vue({
el: '#app',
data() {
return {
message: 'This is a good place to type things.'
}
}
});
v-model
示例:http://codepen.io/sdras/pen/fc5a128716814995b888d362a5e1b367
针对上面的Demo,你可能会注意到两点:
第一,我们并没有直接在书上面进行一些操作,而书上的文本就会动态的更新。Vue使用v-mode
很容易的在<textarea>
和<p>
中间完成了双向绑定。
第二,你可能注意到了,我们将数据放到了一个方法当中。就这个例子而言,即使你不这样做,它也可以正常工作。即也可以只放一个对象进去,就像前面的v-for
示例一样。但显然这不是我们想要的,因为以后数据很可能是动态的,而不是一个固定的对象。我们需要写的是一个组件,而不单单是这一个功能。写一个组件最是好把数据放到方法里面,这个方法在Vue每次实例化的时候都返回一个新的对象。
除了上面介绍的指令之外,Vue还有很多其它的指令,比如v-if
,v-show
等等(如果你用过Angular 1的话,将会对这些非常熟悉)。
下面列出了一些常用的:
指令 | 缩写 | 用途 | 示例 |
---|---|---|---|
v-if, v-else-if, v-else |
none | 条件渲染 | <g v-if="flourish === 'A'"></g> |
v-bind |
: | 绑定动态属性,或传递props | <div :style="{ background: color }"></div> |
v-on |
@ | 给元素绑定事件 | <button @click="fnName"></button> |
v-model |
none | 创建双向绑定 | <textarea rows="5" v-model="message" maxlength="72"></textarea> |
v-pre |
none | 将模板直接原样输出,不做解析,这样会提高性能 | <div v-pre>{{ raw content with no methods}}</div> |
v-once |
none | 不做渲染 | <div class=”v-once”>Keep me from rerendering</div> |
v-show |
none | 显示或隐藏组件DOM (v-if是直接删除或插入组件DOM) | <child v-show=”showComponent”></child> (toggles visibility when showComponent is true) |
也有一些非常好用的事件修饰符和API来提升开发效率,例如:
@mousemove.stop
相当于e.stopPropogation()
@mousemove.prevent
相当于e.preventDefault()
@submit.prevent
是当表单提交的时候,会阻止页面刷新@click.once
和v-once
没关系,它会让绑定的事件函数只触发一次v-model.lazy
将不会自动生成内容,它将会等事件发生才进行绑定
你甚至可以配制自己的keycodes。
这些我们都会在后面的例子当中一点一点的向大家进行展示!
事件
在Vue里,我们把方法都写到methods
里面,包括事件方法:
new Vue({
el: '#app',
data() {
return {
counter: 0
}
},
methods: {
increment() {
this.counter++;
}
}
});
<div id="app">
<p><button @click="increment">+</button> {{ counter }}</p>
</div>
我们创建一个方法叫做increment
,接下来我们就可以在这个Vue实例中通过this
来访问到这个方法。
将方法放到methods
里并不是唯一的方法。你还可以使用watch
。它们的主要区别就是:methods
适合用在一些计算量较小的、同步的计算,而watch
则更适合用在一些更复杂的或者一些异步操作上。我更倾向于在动画当中使用watch
。
现在让我们通过事件来对样式进行动态的修改,之前那张表格里列出了一些指令的缩写形式,我们可以使用:
来代替v-bind
,我们可以使用:style
来传入一些状态,或者使用:class
来切换class。
下面的例子当中,我们使用了hsl()
来改变背景颜色的色相(让色相按照色环的值进行变化):
new Vue({
el: '#app',
data() {
return {
counter: 0,
x: 0
}
},
methods: {
increment() {
this.counter++;
},
decrement() {
this.counter--;
},
xCoordinate(e) {
this.x = e.clientX;
}
}
});
<div id="app" :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }" @mousemove="xCoordinate">
<p><button @click="increment">+</button> {{ counter }} <button @click="decrement">-</button></p>
<p>Pixels across: {{ x }}</p>
</div>
结果:http://codepen.io/sdras/pen/75205908c2189487ca91f9b49c1c978a
你可以看到,我们并不需要给事件函数传参,而是通过在方法里面找到对应的属性即可。另外,当一个函数被当做事件函数进行调用时,那么它会自动传入一个事件对象e
。
有时,我们甚至都不需要创意方法就能完成一些操作,只需直接把语句写在行内即可:
<div id="app">
<div class="item">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/backpack.jpg" width="235" height="300"/>
<div class="quantity">
<button class="inc" @click="counter > 0 ? counter -= 1 : 0">-</button>
<span class="quant-text">Quantity: {{ counter }}</span>
<button class="inc" @click="counter += 1">+</button>
</div>
<button class="submit" @click="">Submit</button>
</div><!--item-->
</div>
new Vue({
el: '#app',
data() {
return {
counter: 0
}
}
});
结果:http://codepen.io/sdras/pen/f979956bee610da7563db67b1358619f
当然,虽然可以这样写,但是并不推荐!最好还是把逻辑写到方法里,这样方便维护。