该篇为翻译文章:原文为:Intro to Vue.js: Rendering, Directives, and Events

该系列文章目录:
Vue.js #1-渲染、指令、事件 (当前)

Vue.js #2-组件、属性、Slots(原文)

Vue.js #3-Vue-cli及生命周期(原文)

Vue.js #4-Vuex(原文)

Vue.js #5-动画(原文)

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>
<g v-else-if="flourish === 'B'"></g>
<g v-else></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.oncev-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

当然,虽然可以这样写,但是并不推荐!最好还是把逻辑写到方法里,这样方便维护。