vue基础

时间:May 30, 2019 分类:

目录:

安装

vue在兼容性上不支持IE8以下的版本,因为使用了IE8以下无法模拟的ECMAScript5特性

介绍

是什么

vue是一套用于构建用户界面的渐进式JavaScript框架,与其他框架不同,vue被设计为可自底向上逐层应用

起步

引入vue

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

可以参考scrimba的教程

声明式渲染

html代码

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

js代码

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

数据和DOM建立了联系,如果在浏览器的JavaScript控制台修改app.message也会有相应的更新

也可以通过指令进行渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app-2">
      <span v-bind:title="message">
        鼠标悬停几秒钟查看此处动态绑定的提示信息!
      </span>
    </div>
</body>
<script>
    var app2 = new Vue({
      el: '#app-2',
      data: {
        message: '页面加载于 ' + new Date().toLocaleString()
      }
    })
</script>
</html>

v-表示vue提供的特性,v-bind是将元素节点的title和vue实例保持一致

条件与循环

html代码

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

js代码

var app3 = new Vue({
  el: '#app-3',
  data: {
    seen: true
  }
})

在判断的过渡过程中添加过渡效果

html代码

<div id="app-4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>

js代码

var app4 = new Vue({
  el: '#app-4',
  data: {
    todos: [
      { text: '学习 JavaScript' },
      { text: '学习 Vue' },
      { text: '整个牛项目' }
    ]
  }
})

处理用户请求

html代码

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">逆转消息</button>
</div>

js代码

var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

绑定表单输入和应用状态

html代码

<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

js代码

var app6 = new Vue({
  el: '#app-6',
  data: {
    message: 'Hello Vue!'
  }
})

组件化应用构建

任意应用界面都可以抽象为组件树

vue上可以注册组件

js代码

// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
  template: '<li>这是个待办项</li>'
})

构建组件模板

<ol>
  <!-- 创建一个 todo-item 组件的实例 -->
  <todo-item></todo-item>
</ol>

修改为一个能接收数据的

Vue.component('todo-item', {
  // todo-item 组件现在接受一个
  // "prop",类似于一个自定义特性。
  // 这个 prop 名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

将待办事件传到循环的每个组件中

<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', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

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

子单元通过prop接口与父进程进行解耦,进行数据交互

vue实例

创建实例

创建实例通过vue

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

数据与方法

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})

// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

这就是响应式,但是必须是在data中的属性才具备响应式

不过例外的是Object.freeze()会阻止修改现有属性

html代码

<div id="app">
  <p>{{ foo }}</p>
  <!-- 这里的 `foo` 不会更新! -->
  <button v-on:click="foo = 'baz'">Change it</button>
</div>

js代码

var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj
})

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` 改变后调用
})

实例生命周期

实例在创建的时候可以进行一系列的初始化,例如设置数据监听,编译模板,将实例挂载到DOM等,在这个过程中会运营一些生命周期的钩子函数,可以通过这些函数加入

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

在钩子函数中不要使用箭头函数

生命周期示意图

模板语法

模板提供了渲染DOM的,当然也可以使用render函数

插值

文本

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

如果使用了v-once,插值不会改变,但是也影响了节点上其他数据的绑定

<span v-once>这个将不会改变: {{ msg }}</span>

html

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

这里使用了v-html来复合模板,但是主要不要将用户上传的内容在html中渲染,否则可能会造成XSS攻击

特性

<button v-bind:disabled="isButtonDisabled">Button</button>

根据isButtonDisabled来进行渲染,例如null,undefined和false,disabled是不会被渲染出来的

使用JavaScript表达式

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

注意每个绑定都只能有一个表达式,所以以下都不会生效

<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}

<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

指令

指令用于表达式改变的时候产生连带影响

参数

指令接收参数,下边一个是特性与表达式绑定,一个是与DOM事件绑定

<a v-bind:href="url">...</a>
<a v-on:click="doSomething">...</a>

动态参数(2.6版本后开始支持)

用方括号括起来的JavaScript表达式作为一个指令参数

<a v-bind:[attributeName]="url"> ... </a>

方括号内不能有空格引号等

修饰符

修饰符用于指令进行特殊的绑定

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

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

缩写

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

计算属性和侦听器

计算属性

模板的初衷是用于简单计算

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

但是如果逻辑复杂就增加了逻辑维护的难度

基础

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

然后为这个绑定一个计算属性reversedMessage

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

之后可以调用vm.reversedMessage用作属性的getter函数

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

当vm.message发生改变的时候,依赖它的vm.reversedMessage也会发生改变

计算属性缓存vs方法

可以使用表达式的方式达到相同的效果

<p>Reversed message: "{{ reversedMessage() }}"</p>

但是相当于每次都执行这个函数,而计算属性只有在依赖发生改变的时候才会重新求值,平时获取到的就是缓存了

不过以下也就是缓存值了

computed: {
  now: function () {
    return Date.now()
  }
}

计算属性vs监听属性

<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
    }
  }
})

计算属性的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]
    }
  }
}
// ...

执行setter方法设置fullName的时候也会更新vm.firstName和vm.lastName

监听器

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>

监听器主要是为了在数据变化时或者执行异步开销较大的操作的时候,这个方式是最有用的

<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/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: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
    // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
    // 请参考:https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      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
        })
    }
  }
})
</script>

Class与Style绑定

class与style绑定,使用bind

绑定style

对象语法

v-bind:class可以传递一个对象,动态切换class

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

这样active的值就取决于isActive,对于多个属性也可以

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

渲染之后就是

<div class="static active"></div>

这里是对class整体而言的,如果hasError变为true,就是

<div class="static active text-danger"></div>

绑定的数据可以不在模板里

<div v-bind:class="classObject"></div>
...
data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

也可以绑定一个计算属性

<div v-bind:class="classObject"></div>
...
data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}

数组语法

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

渲染完就是

<div class="active text-danger"></div>

甚至可以混着用

<div v-bind:class="[{ active: isActive }, errorClass]"></div>

在组件上

绑定内联样式

对象语法

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
...
data: {
  activeColor: 'red',
  fontSize: 30
}

或者

<div v-bind:style="styleObject"></div>
...
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

数组语法

<div v-bind:style="[baseStyles, overridingStyles]"></div>

自动添加前缀

当使用v-bind:style使用需要添加浏览器引擎前缀的css属性,如transform,vue.js会自动添加

条件渲染

v-if

<h1 v-if="awesome">Vue is awesome!</h1>

v-else

<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-else-if

为2.1.0新增

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

使用key来进行判定

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

v-show

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

与v-if相似,不同的是v-show是将元素设置为display,但是元素还是在DOM中的

如果是频繁的修改,最好使用v-show

v-if和v-for

v-if和v-for最好不要同时使用,因为v-for有更高的优先级

列表渲染

用v-for把一个数组对应为一组元素

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
...
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

一个对象的v-for

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
...
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

也可以

<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>

使用键名

维护状态

当Vue.js用v-for正在更新已渲染过的元素列表时,它默认用“就地复用”策略。

如果数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似Vue 1.x的 track-by="$index" 。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时DOM状态 (例如:表单输入值) 的列表渲染输出。

为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一key属性:

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

数组更新检测

变异方法

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

你打开控制台,然后用前面例子的items数组调用变异方法:example1.items.push({ message: 'Baz' }) 。

事件处理

监听事件

v-on可以监听DOM事件,并触发一些JavaScript代码

<div id="example-1">
  <button v-on:click="counter += 1">Add 1</button>
  <p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
  el: '#example-1',
  data: {
    counter: 0
  }
})

事件处理方式

<div id="example-2">
  <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  // 在 `methods` 对象中定义方法
  methods: {
    greet: function (event) {
      // `this` 在方法里指向当前 Vue 实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM 事件
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
})

// 也可以用 JavaScript 直接调用方法
example2.greet() // => 'Hello Vue.js!'

内联处理器中的方法

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})

在内联语句中获取原生的DOM事件,可以将event传入

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
// ...
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) event.preventDefault()
    alert(message)
  }
}

替换数组

filter(),conncat()和slice()等方法不会替换原始的数组,但是会返回一个新的数组

而替换就是用新的数组替换旧的数组

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

由于JavaScript限制,vue不能检测到以下的变化

  1. 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

举个栗子

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

对于第一类问题需要使用

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

也可以使用vm.$set实例方法,该方法是全局方法Vue.set的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用splice:

vm.items.splice(newLength)

对象更改检测注意事项

由于JavaScript的限制,vue不能检测对象属性的添加和删除

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 现在是响应式的

vm.b = 2
// `vm.b` 不是响应式的

可以通过Vue.set(object, propertyName, value)方法向嵌套对象添加响应式属性

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})

可以添加一个新的age属性嵌套的userProfile对象

Vue.set(vm.userProfile, 'age', 27)

如果需要赋值多个可以使用

vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

显示过滤/排序结果

需要对一个数组进行过滤或者排序但是不实质或者重置原始数据


data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

计算属性不适用的情况可以

<li v-for="n in even(numbers)">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

一段取值范围的v-for

<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

v-for on a <template>

渲染多个元素

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

v-for with v-if

在同一节点,for的优先级更高,就是v-if被应用到每一个for循环

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>

如果想要v-if先生效就要分开写

<ul v-if="todos.length">
  <li v-for="todo in todos">
    {{ todo }}
  </li>
</ul>
<p v-else>No todos left!</p>

一个组件的 v-for

事件修饰符

使用click接后缀事件

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- 阻止单击事件继续传播 -->
<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>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

使用修饰符的时候可以使用多个,需要注意顺序问题

  • v-on:click.prevent.self会阻止所有的点击
  • v-on:click.self.prevent只会阻止对元素自身的点击

在2.1.4版本增加了

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

.once修饰符不仅可以用在DOM上,还可以用到组件事件上

在2.3.0版本增加了

Vue对应addEventListener中的passive选项提供了.passive修饰符

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

.passive修饰符可以提高应用端的性能

按键修饰符

用于监听键盘事件

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

可以使用KeyboardEvent.key暴露任意有效键名转换为kebab-case作为修饰符

<input v-on:keyup.page-down="onPageDown">

还有等等更多的,但是我觉得我用不上

表单输入绑定

基础用法

使用v-model指令在表单input,textarea和select元素上进行双向的绑定,根据控件类型选择正确的方法更新元素

v-model的本质为语法糖,负责监听用户的输入事件以更新元素,并对一些极端场景进行一些特殊的处理,会忽略所有的表单元素的value,checked和selected特性的初始值而是量vue作为数据的来源,使用data选项声明初始值

  • text和textarea元素使用value属性和input事件
  • checkbox和radio使用checked属性和change事件
  • select字段将value作为prop并将change作为事件

文本

<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

多行文本

<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

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

复选框

单个复选框

<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

多个复选框

<div id='example-3'>
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames">
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
  <label for="mike">Mike</label>
  <br>
  <span>Checked names: {{ checkedNames }}</span>
</div>
...
new Vue({
  el: '#example-3',
  data: {
    checkedNames: []
  }
})

单选按钮

<div id="example-4">
  <input type="radio" id="one" value="One" v-model="picked">
  <label for="one">One</label>
  <br>
  <input type="radio" id="two" value="Two" v-model="picked">
  <label for="two">Two</label>
  <br>
  <span>Picked: {{ picked }}</span>
</div>
...
new Vue({
  el: '#example-4',
  data: {
    picked: ''
  }
})

选项框

对于单选

<div id="example-5">
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>Selected: {{ selected }}</span>
</div>
...
new Vue({
  el: '...',
  data: {
    selected: ''
  }
})

对于多选需要添加到数组

<div id="example-6">
  <select v-model="selected" multiple style="width: 50px;">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <br>
  <span>Selected: {{ selected }}</span>
</div>
new Vue({
  el: '#example-6',
  data: {
    selected: []
  }
})

用v-for渲染动态选项

<select v-model="selected">
  <option v-for="option in options" v-bind:value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C' }
    ]
  }
})

值绑定

复选框

<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no"
>
// 当选中时
vm.toggle === 'yes'
// 当没有选中时
vm.toggle === 'no'

单选框

<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

默认情况下在input事件触发后触发数据进行同步,而.lazy修饰符则是在change事件后触发事件同步

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >

.number

可以对输入的数据类型进行转换

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

.trim

自动过滤首尾部空白字符

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

组件上也可以使用v-model

组件基础

基本示例

// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

组件是一个可以复用的vue实例,且有一个名字,就可以在vue的根实例

<div id="components-demo">
  <button-counter></button-counter>
</div>
...
new Vue({ el: '#components-demo' })

组件复用

组件可以使用多个

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

每个组件会维护独立的count,每有一个组件就会有一个实例被创建,所以data必须是一个函数

data: function () {
  return {
    count: 0
  }
}

这样才能使每个组件返回的是一个独立的对象

组件的组织

为了能在模板中使用,组件需要注册使vue识别,有两种注册类型

  • 全局注册
  • 局部注册

全局注册的组件可以被新创建的vue使用

prop向子组件传递数据

组件上可以定义prop,当数值传递给prop的时候,就变成了实例的一个属性

Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

一个组件可以绑定任意多的prop,任何值都能传递给prop

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

也可以使用数组

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})
...
<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>

单个根元素

当构建一个<blog-post>组件的时候,模板会不止包含一个标题,还应该有正文

<h3>{{ title }}</h3>
<div v-html="content"></div>

如果这样写vue会报错every component must have a single root element(每个组件必须只有一个根元素)

需要修改为

<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>

当组件越来越复杂,包含日期,评论等等,就需要单独构建一个<blog-post>

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>
...
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})

监听子组件事件

如果涉及到父组件和子组件进行沟通,例如引入一个可访问的放大博文字号

可以在父组件设置一个postFontSize的数据属性

new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* ... */],
    postFontSize: 1
  }
})
...
<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
    ></blog-post>
  </div>
</div>

在博文加按钮放大字体

Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button>
        Enlarge text
      </button>
      <div v-html="post.content"></div>
    </div>
  `
})

为按钮绑定方法

<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>

子组件通过$emit方法传入事件名称来触发事件

<button v-on:click="$emit('enlarge-text')">
  Enlarge text
</button>

在组件上使用v-model

也可以输入组件名称

<input v-model="searchText">

等价于

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

用在组件上等价于

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

需要做的是

  • value特性绑定的到value的prop上
  • input事件触发时将新的值通过自定义input抛出
Vue.component('custom-input', {
  props: ['value'],
  template: `
    <input
      v-bind:value="value"
      v-on:input="$emit('input', $event.target.value)"
    >
  `
})
...
<custom-input v-model="searchText"></custom-input>

通过插槽分发内容

略,使用的<slot>

<alert-box>
  Something bad happened.
</alert-box>
...
Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

动态组件

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

currentTabComponent可以包括

  • 一个已经注册的组件
  • 一个组件的选项对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
    .tab-button {
      padding: 6px 10px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      border: 1px solid #ccc;
      cursor: pointer;
      background: #f0f0f0;
      margin-bottom: -1px;
      margin-right: -1px;
    }
    .tab-button:hover {
      background: #e0e0e0;
    }
    .tab-button.active {
      background: #e0e0e0;
    }
    .tab {
      border: 1px solid #ccc;
      padding: 10px;
    }
    </style>
</head>
<body>
<div id="dynamic-component-demo" class="demo">
  <button
    v-for="tab in tabs"
    v-bind:key="tab"
    v-bind:class="['tab-button', { active: currentTab === tab }]"
    v-on:click="currentTab = tab"
  >{{ tab }}</button>

  <component
    v-bind:is="currentTabComponent"
    class="tab"
  ></component>
</div>
    <script src="https://unpkg.com/vue"></script>
    <script>
    Vue.component('tab-home', {
        template: '<div>Home component</div>'
    })
    Vue.component('tab-posts', {
        template: '<div>Posts component</div>'
    })
    Vue.component('tab-archive', {
        template: '<div>Archive component</div>'
    })

    new Vue({
      el: '#dynamic-component-demo',
      data: {
        currentTab: 'Home',
        tabs: ['Home', 'Posts', 'Archive']
      },
      computed: {
        currentTabComponent: function () {
          return 'tab-' + this.currentTab.toLowerCase()
        }
      }
    })
</script>
</body>
</html>

解析DOM模板时的注意事项

  • <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的
  • 而有些元素,诸如<li><tr><option>,只能出现在其它某些特定的元素内部

例如

<table>
  <blog-post-row></blog-post-row>
</table>

就不能正常被渲染

可以使用

<table>
  <tr is="blog-post-row"></tr>
</table>

不过在以下来源是不受限制的

  • 字符串 template: '...'
  • 单文件组件 .vue
  • <script type="text/x-template">