Vue

Vue.js 是一套构建用户界面的渐进式框架

  • Vue 的核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。

  • 当与单文件组件和 Vue 生态系统支持的库结合使用时,Vue 也完全能够为复杂的单页应用程序提供驱动。

对比

对比其他框架

Quick Start

Hello World

  • install

Install Vue first.

<div id="app">
\{\{ message \}\}
</div>

(vue 的语法和 jekyll 冲突。实际请删除其中的转义符号\,后面亦如是。)

<script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>

<script>
    new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue.js!'
        }
    })
</script>

效果如下:

helloVue.html

Another way

<div class="well" id="vue-demo-2">
  <span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
</div>


<script>
    new Vue({
      el: '#vue-demo-2',
      data: {
        message: '页面加载于 ' + new Date().toLocaleString()
      }
    })
</script>

其中 v-bind 属性被称为指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊属性。

鼠标悬停几秒钟查看此处动态绑定的提示信息!

Condition

<div class="well" id="vue-demo-3">
  <p v-if="seen">现在你看到我了</p>
</div>

<script>
    new Vue({
      el: '#vue-demo-3',
      data: {
        seen: true
      }
    })
</script>

现在你看到我了

Loop

<div class="well" id="vue-demo-4">
  <li v-for="todo in todos">
     \{\{ todo.text \}\}
  </li>
</div>

<script>
    new Vue({
      el: '#vue-demo-4',
      data: {
          todos: [
            { text: '学习 JavaScript' },
            { text: '学习 Vue' },
            { text: '整个牛项目' }
          ]
        }
    })
</script>

vueLoop.html

Handle User Input

为了让用户和你的应用进行互动,我们可以用 v-on 指令绑定一个事件监听器,通过它调用我们 Vue 实例中定义的方法:

<div id="vue-demo-5">
  <p></p>
  <button class="btn btn-info" v-on:click="reverseMessage">逆转消息</button>
</div>
new Vue({
  el: '#vue-demo-5',
  data: {
      message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

vueOn.html

Componentized application build

几乎任意类型的应用界面都可以抽象为一个组件树:

components

<div id="app-7">
    <ol>
        <!--
          现在我们为每个 todo-item 提供 todo 对象
          todo 对象是变量,即其内容可以是动态的。
          我们也需要为每个组件提供一个“key”,晚些时候我们会做个解释。
        -->
        <todo-item
                v-for="item in groceryList"
                v-bind:todo="item"
                v-bind:key="item.id">
        </todo-item>
    </ol>
</div>
//定义一个组件。且此组件可以接受一个属性
Vue.component('todo-item', {
    // todo-item 组件现在接受一个
    // "prop",类似于一个自定义属性
    // 这个属性名为 todo。
    props: ['todo'],
    template: '<li></li>'
})

new Vue({
    el: '#app-7',
    data: {
        groceryList: [
            { id: 0, text: '蔬菜' },
            { id: 1, text: '奶酪' },
            { id: 2, text: '随便其他什么人吃的东西' }
        ]
    }
})

vueComponent.html

Vue Instance

(这文档写的很符合我的审美,直接抄了。。。XD)

Create Vue

每个 Vue 应用都是从创建一个实例开始:

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

虽未完全遵守 MMVM 模型,但二者渊源颇深。

Data & Method

当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})
// 他们引用相同的对象!
vm.a === data.a // => true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2
// ... and vice-versa
data.a = 3
vm.a // => 3

值得注意的是只有当实例被创建时 data 中存在的属性是响应式的。也就是说如果你添加一个新的属性

vm.b = 'hi'

那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:

data: {
  newTodoText: '',
  visitCount: 0,
  hideCompletedTodos: false,
  todos: [],
  error: null
}

除了 data 属性, Vue 实例暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。例如:

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

Life Cycle

每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。

同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码。

(这个和 jQuery 插件预留的钩子就很类似,是一种很利于拓展的设计。)

created 钩子:

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

在实例生命周期的不同场景下调用,如 mountedupdateddestroyed。 钩子的 this 指向调用它的 Vue 实例。

不要选项属性或回调上使用箭头函数,比如

created: () => console.log(this.a)vm.$watch('a', newValue => this.myMethod())

因为箭头函数是和父级上下文绑定在一起的,this 不会是如你做预期的 Vue 实例,且 this.a 或 this.myMethod 也会是未定义的。

上图

lifecycle

Template Grammar

所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

插值

  • 文本

最常见的形式就是使用 Mustache 语法的文本插值

<span>Message: \{\{ msg \}\}</span>

无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。

通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。

<span v-once>这个将不会改变: \{\{ msg \}\}</span>
  • 原始 HTML

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML ,你需要使用 v-html 指令:

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

这个 div 的内容将会被替换成为属性值 rawHtml,直接作为 HTML(会忽略解析属性值中的数据绑定)。

Attention

你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。 请只对可信内容使用 HTML 插值,绝不要对用户提供的内容插值。

  • 特性

mustache 语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind 指令:

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

这同样适用于布尔类特性,如果求值结果是 false 的值,则该特性将会被删除:

<button v-bind:disabled="isButtonDisabled">Button</button>
  • 使用 JS 表达式

对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。

\{\{ number + 1 \}\}
\{\{ ok ? 'YES' : 'NO' \}\}
\{\{ message.split('').reverse().join('') \}\}
<div v-bind:id="'list-' + id"></div>

这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

<!-- 这是语句,不是表达式 -->
\{\{ var a = 1 \}\}
<!-- 流控制也不会生效,请使用三元表达式 -->
\{\{ if (ok) { return message } \}\}

指令

指令 (Directives) 是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。 指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。回顾我们在介绍中看到的例子:

<p v-if="seen">现在你看到我了</p>

这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素。

  • 参数

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML 属性:

<a v-bind:href="url"></a>

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

<a v-on:click="doSomething">

在这里参数是监听的事件名。我们也会更详细地讨论事件处理。

  • 修饰符

修饰符 (Modifiers) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。

例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

<form v-on:submit.prevent="onSubmit"></form>

之后当我们更深入地了解 v-on 与 v-model时,会看到更多修饰符的使用。

缩写

v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的特性。 当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v- 前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。 同时,在构建由 Vue.js 管理所有模板的 单页面应用程序 (SPA - single page application) 时,v- 前缀也变得没那么重要了。 因此,Vue.js 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:

  • v-bind
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
  • v-on
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

Calc-Attr & Watcher

Calc-Attr

  • Base

模板内的表达式是非常便利的,但是它们实际上只用于简单的运算。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="example">
  \{\{ message.split('').reverse().join('') \}\}
</div>

在这个地方,模板不再简单和清晰。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。

这就是对于任何复杂逻辑,你都应当使用计算属性的原因。

calcAttrBase.html

  • Cache VS Method()

计算属性只有在它的相关依赖发生改变时才会重新求值。

相比之下,每当触发重新渲染时,方法的调用方式将总是再次执行函数。

calcAttrCacheOrMethod.html

  • Calc-attr VS Observer-attr

然而,通常更好的想法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

calcAttrWatch.html

  • Calc-attr Setter

calcAttrSetter.html

Watcher

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的 watcher。

这是为什么 Vue 通过 watch 选项提供一个更通用的方法,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。

calcAttrWatcher.html

Class & Style

数据绑定一个常见需求是操作元素的 class 列表和它的内联样式。因为它们都是属性 ,我们可以用 v-bind 处理它们:只需要计算出表达式最终的字符串。 不过,字符串拼接麻烦又易错。因此,在 v-bind 用于 class 和 style 时,Vue.js 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

Class

一、对象语法

我们可以传给 v-bind:class 一个对象,以动态地切换 class:

<div v-bind:class="{ active: isActive }"></div>

上面的语法表示 class active 的更新将取决于数据属性 isActive 是否为真值。

你可以在对象中传入更多属性用来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class 属性共存。如下模板:

<div class="static"
     v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

bindClassObject.html

二、数组语法

bindClassArray.html

三、组件

Jump…

Style

一、对象语法

bindStyleObject.html

二、数组语法

bindStyleArray.html

三、自动添加前缀

Vendor_Prefix

四、多重值

2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:

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

这会渲染数组中最后一个被浏览器支持的值。在这个例子中,如果浏览器支持不带浏览器前缀的 flexbox,那么渲染结果会是 display: flex

Condition Render

v-if

  • Handlebars
<!-- Handlebars 模板 -->
{`{#if ok}`}
  <h1>Yes</h1>
{`{/if}`}
  • v-if 实现同样的效果
<h1 v-if="ok">Yes</h1>
  • v-else
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

还有 v-else/v-else-if 和正常的使用方式一致。此处不再赘述。

template

如果我们想渲染一组数据,应该怎么做?

我们可以把一个 <template> 元素当做包装元素,并在上面使用 v-if。最终的渲染结果不会包含 <template> 元素。

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

Key


上述,文本框输入内容。进行切换时,输入框未被重新渲染。

只需如下,添加唯一 key:


v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

<h1 v-show="ok">Hello!</h1>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 是简单地切换元素的 CSS 属性 display。

List Render

list

forList.html

object

forObject.html

order/filter

filterOrder.html

for in range

forInRange.html

to-list

forTodoList.html

Event Handle

Event Listen

eventListen.html

事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。

尽管我们可以在 methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了 事件修饰符。通过由点 . 表示的指令后缀来调用修饰符。

<!-- 阻止单击事件冒泡 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件侦听器时使用事件捕获模式 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当事件在该元素本身 (比如不是子元素) 触发时触发回调 -->
<div v-on:click.self="doThat">...</div>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止元素上的点击。

键值修饰符

在监听键盘事件时,我们经常需要监测常见的键值。Vue 允许为 v-on 在监听键盘事件时添加关键修饰符:

<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">

可以通过全局 config.keyCodes 对象自定义键值修饰符别名:

// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112

为什么在 HTML 中监听事件?

你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 传统理念。

不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:

  • 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。

  • 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。

  • 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。

表单输入绑定

基础用法

你可以用 v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。 尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件以更新数据,并特别处理一些极端的例子。

  • 初始值忽略

v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值。因为它会选择 Vue 实例数据来作为具体的值。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

  • IME 不监听

对于要求 IME (如中文、日语、韩语等) (IME 意为“输入法”)的语言,你会发现 v-model 不会在 ime 输入中得到更新。如果你也想实现更新,请使用 input 事件。

  • text-area

在文本区域插值 <textarea></textarea> 并不会生效,应用 v-model 来代替。

base.html

值绑定

对于单选按钮,勾选框及选择列表选项,v-model 绑定的 value 通常是静态字符串 (对于勾选框是逻辑值):

<!-- 当选中时,`picked` 为字符串 "a" -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` 为 true 或 false -->
<input type="checkbox" v-model="toggle">
<!-- 当选中时,`selected` 为字符串 "abc" -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>

但是有时我们想绑定 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

二、单选按钮

<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a
选择列表的选项

<select v-model="selected">
    <!-- 内联对象字面量 -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123

修饰符

  • .lazy

在默认情况下,v-model 在 input 事件中同步输入框的值与数据 (除了 上述 IME 部分),但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:

<!-- 在 "change" 而不是 "input" 事件中更新 -->
<input v-model.lazy="msg" >
  • .number

如果想自动将用户的输入值转为 Number 类型 (如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值:

<input v-model.number="age" type="number">

这通常很有用,因为在 type=”number” 时 HTML 中输入的值也总是会返回字符串类型。

  • .trim

如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:

<input v-model.trim="msg">