Vue 2.0 出来也有一段时间了,作为一个有志向的全面发展好青年,在征服 Vue 1.x,React,React Native 后,为了之后能更快迁移公司的项目到 Vue 2.x,于是决定先看看 Vue 2.0。
鉴于部分读者可能不了解 Vue,先简单看看各种特性。
本文假设你有一定的 HTML 基础,并熟悉一种或以上编程语言(那就能看懂 JS 了)。
模板语法
Vue 提供了一堆数据绑定语法。
- {{ text }} 文本插值
- {{{ raw_html }}} HTML 输出
- v-bind HTML 属性插值。如
- JavaScript 表达式。直接在 mustache、属性插值里面使用各种表达式(加减乘除、三元运算、方法调用等)。
- 过滤器(有点类似 Shell 命令中的管道,可以定义过滤器来对原始值进行变化)。
- 指令。之前提到的 v-bind 也是一种指定,其他包括 v-on: 系列(dom 事件的监听)、v-for、v-model等。
Vue 实例
Vue 实例,实则也就是 ViewModel(数据 + 函数),都是通过构造函数 Vue 创建的:
var data = { a: 1 } var vm = new Vue({ el: "#example", data: data, created: function () { // `this` points to the vm instance console.log("a is: " + this.a) } }) vm.$data === data // -> true vm.$el === document.getElementById("example") // -> true // $watch is an instance method vm.$watch("a", function (newVal, oldVal) { // this callback will be called when `vm.a` changes })
Vue 实例都有自己的生命周期,比如 created, mounted, updated 以及 destroyed。所有方法被 called 的时候,this 都指向所在的 Vue 实例。
Lifecycle 图如下:
计算属性和监听器
计算属性
其实就是一个需要计算的 getter:
<div id="example"> <p>Original message: "{{ message }}"p> <p>Computed reversed message: "{{ reversedMessage }}"p> div>
var vm = new Vue({ el: "#example", data: { message: "Hello" }, computed: { // a computed getter reversedMessage: function () { // `this` points to the vm instance return this.message.split("").reverse().join("") } } }) </div>
和使用 method 的区别在于,计算属性根据它的依赖被缓存,即如果 message 没有被修改,下次 get 不会进行重复计算,而 method 则每次调用都会重新计算。这也意味着如 Date.now() 这样返回的计算属性会永远得不到更新。
Setter
默认情况下,计算属性只有一个 getter,我们也可以给它加上 setter:
computed: { fullName: { // getter get: function () { return this.firstName + " " + this.lastName }, // setter set: function (newValue) { var names = newValue.split(" ") this.firstName = names[0] this.lastName = names[names.length - 1] } } }
如此,当我们调用 vm.fullName = "MarkZhai" 的时候,firstName 和 lastName 都会被更新。
监听器
Vue 的 watch 也可以用来做类似的事:
<div id="demo">{{ fullName }}div>
var vm = new Vue({ el: "#demo", data: { firstName: "Foo", lastName: "Bar", fullName: "Foo Bar" }, watch: { firstName: function (val) { this.fullName = val + " " + this.lastName }, lastName: function (val) { this.fullName = this.firstName + " " + val } } })
对比一下计算属性版本:
var vm = new Vue({ el: "#demo", data: { firstName: "Foo", lastName: "Bar" }, computed: { fullName: function () { return this.firstName + " " + this.lastName } } })
看上去好像简单了很多,那还要 Watcher 干啥呢。。。主要应用场景是异步或耗时操作:
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js">script> <script src="https://unpkg.com/lodash@4.13.1/lodash.min.js">script> <script> var watchExampleVM = new Vue({ el: "#watch-example", data: { question: "", answer: "I cannot give you an answer until you ask a question!" }, watch: { // whenever question changes, this function will run question: function (newQuestion) { this.answer = "Waiting for you to stop typing..." this.getAnswer() } }, methods: { // _.debounce is a function provided by lodash to limit how // often a particularly expensive operation can be run. // In this case, we want to limit how often we access // yesno.wtf/api, waiting until the user has completely // finished typing before making the ajax request. To learn // more about the _.debounce function (and its cousin // _.throttle), visit: https://lodash.com/docs#debounce getAnswer: _.debounce( function () { var vm = this if (this.question.indexOf("?") === -1) { vm.answer = "Questions usually contain a question mark. ;-)" return } vm.answer = "Thinking..." axios.get("https://yesno.wtf/api") .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = "Error! Could not reach the API. " + error }) }, // This is the number of milliseconds we wait for the // user to stop typing. 500 ) } }) script>
如此,使用 watch 让我们可以进行异步操作(访问 API),限制操作间隔,并设置中间状态直到获得了真正的答案。
除了使用 watch option,也可以用 vm.$watch API。
Class 和 Style 绑定
除了数据绑定,常见的还有 style、class 的绑定(正如很久以前在 JQuery 中常用的)。
对象语法
我们可以传递一个对象给 v-bind:class 来动态切换 classes:
<div class="static" v-bind:class="{ active: isActive, "text-danger": hasError }"> div>
对应的 active 和 text-danger 则通过 data 传递过来。
我们也可直接通过 data 把 class 传递过来
<div v-bind:class="classObject">div>
data: { classObject: { active: true, "text-danger": false } }
当然我们也能使用上面提到的 computed 来进行对应属性,如 active 的计算。
数组语法
可以直接传递一个数组给 v-bind:class:
<div v-bind:class="[activeClass, errorClass]">
data: { activeClass: "active", errorClass: "text-danger" }
也可以写成
<div v-bind:class="[isActive ? activeClass : "", errorClass]"> <div v-bind:class="[{ active: isActive }, errorClass]">
绑定内联样式
跟 class 差不多:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + "px" }">div>
或者直接绑定到 style:
"styleObject">div> data: { styleObject: { color: "red", fontSize: "13px" } }
类似的,也有数组绑定。
条件绑定
v-if
其实就是个标签啦
<h1 v-if="ok">Yesh1> <h1 v-if="ok">Yesh1> <h1 v-else>Noh1>
因为 v-if 必须附加到一个单一 element 上,那如果我们想切换多个元素呢?可以使用 template 元素:
<template v-if="ok"> <h1>Titleh1> <p>Paragraph 1p> <p>Paragraph 2p> template>
v-show
也可以用 v-show 来做条件显示的逻辑,
<h1 v-show="ok">Hello!h1>