@zhouyy
2018-04-12T01:16:50.000000Z
字数 24598
阅读 609
vue
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
data 和 dom 建立了关联,通过更改 app.message可以驱动页面变化
method:通过html中 id 和{{message}} 与 el(元素)和data.message对应
<div id="app">{{ message }}</div>//jsvar app = new Vue({el: '#app',data: {message: 'Hello Vue!'}})
指令带有前缀 v-,以表示它们是 Vue 提供的特殊特性
method:在html需要显示处 如<p v-if="seen">是否要显示</p>,<ol><li v-for="todo in todos">{{todo.data}}
- v-if
<div id="app-3"><p v-if="seen">现在你看到我了</p></div>//jsvar app=new Vue({el:'#app-3',data:{seen=true;}})
v-for
<div id="app-4"><ol><li v-for="todo in todos">{{ todo.text }}</li></ol></div>//jsvar app4 = new Vue({el: '#app-4',data: {todos: [{ text: '学习 JavaScript' },{ text: '学习 Vue' },{ text: '整个牛项目' }]}})
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。

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-itemv-for="item in groceryList"v-bind:todo="item"v-bind:key="item.id"></todo-item></ol></div>//jsVue.component('todo-item', {props: ['todo'],template: '<li>{{ todo.text }}</li>'})var app-7=new new Vue({el:'#app-7'data:{groceryList: [{ id: 0, text: '蔬菜' },{ id: 1, text: '奶酪' },{ id: 2, text: '随便其它什么人吃的东西' }]}
所有的 Vue 组件都是 Vue 实例。
var data = { a: 1 }var vm = new Vue({el: '#example',data: data})vm.$data === data // => truevm.$el === document.getElementById('example') // => true// $watch 是一个实例方法vm.$watch('a', function (newValue, oldValue) {// 这个回调将在 `vm.a` 改变后调用})
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

<span>Message: {{ msg }}</span>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
{{ number + 1 }}{{ ok ? 'YES' : 'NO' }}{{ message.split('').reverse().join('') }}<div v-bind:id="'list-' + id"></div>
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式
指令 (Directives) 是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
<a v-bind:href="url">...</a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">...</a>
在这里参数是监听的事件名。我们也会更详细地讨论事件处理。
修饰符 (Modifiers) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
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>
<div id="example"><p>Original message: "{{ message }}"</p><p>Computed reversed message: "{{ reversedMessage }}"</p></div>//jsvar vm = new Vue({el: '#example',data: {message: 'Hello'},computed: {// 计算属性的 getterreversedMessage: function () {// `this` 指向 vm 实例return this.message.split('').reverse().join('')}}})
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作属性 vm.reversedMessage 的 getter 函数。相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。
<p>Reversed message: "{{ reversedMessage() }}"</p>// 在组件中methods: {reversedMessage: function () {return this.message.split('').reverse().join('')}}
两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
prefer计算属性
html:
<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}}})
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :
// ...computed: {fullName: {// getterget: function () {return this.firstName + ' ' + this.lastName},// setterset: function (newValue) {var names = newValue.split(' ')this.firstName = names[0]this.lastName = names[names.length - 1]}}}// ...
现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的.
在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
<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.getAnswer()}},methods: {// `_.debounce` 是一个通过 Lodash 限制操作频率的函数。// 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率// AJAX 请求直到用户输入完毕才会发出。想要了解更多关于// `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,// 请参考:https://lodash.com/docs#debouncegetAnswer: _.debounce(function () {if (this.question.indexOf('?') === -1) {this.answer = 'Questions usually contain a question mark. ;-)'return}this.answer = 'Thinking...'var vm = thisaxios.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})},// 这是我们为判定用户停止输入等待的毫秒数500)}})</script>
v-bind:class 一个对象,以动态地切换 class:method1:
<div class="static"v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
和如下 data:
data: {isActive: true,hasError: false}
结果渲染为:
<div class="static active"></div>
method2:
<div v-bind:class="classObject"></div>js:data: {classObject: {active: true,'text-danger': false}}
metond3 应用计算属性:
<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'}}}
method1:
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>data: {activeClass: 'active',errorClass: 'text-danger'}
渲染为:
<div class="active text-danger"></div>
method2:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
Vue.component('my-component', {template: '<p class="foo bar">Hi</p>'})
添加Class:
<my-component class="baz boo"></my-component>
HTML 将被渲染为:
<p class="foo bar baz boo">Hi</p>
对于带数据绑定 class 也同样适用:
<my-component v-bind:class="{ active: isActive }"></my-component>
<div v-bind:style="styleObject"></div>data: {styleObject: {color: 'red',fontSize: '13px'}
同样适用计算属性
<div v-bind:style="[baseStyles, overridingStyles]"></div>
<h1 v-if="ok">Yes</h1><h1 v-else>No</h1>
<template v-if="ok"><h1>Title</h1><p>Paragraph 1</p><p>Paragraph 2</p></template>
v-else
v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
v-else-if
v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用,类似于 v-else,v-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
用key管理可复用的元素
例如,如果你允许用户在不同的登录方式之间切换:
<template v-if="loginType === 'username'"><label>Username</label><input placeholder="Enter your username"></template><template v-else><label>Email</label><input placeholder="Enter your email address"></template>
那么在上面的代码中切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 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>
注意, 元素仍然会被高效地复用,因为它们没有添加 key 属性。
另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
注意,v-show 不支持 元素,也不支持 v-else。
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。
v-for 指令需要使用 item in items 形式的特殊语法,items 是源数据数组并且 item 是数组元素迭代的别名。
<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' }]}})
在 v-for 块中,我们拥有对父作用域属性的完全访问权限。v-for 还支持一个可选的第二个参数为当前项的索引。
<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' }]}})
<ul id="v-for-object" class="demo"><li v-for="value in object">{{ value }}</li></ul>new Vue({el: '#v-for-object',data: {object: {firstName: 'John',lastName: 'Doe',age: 30}}})
你也可以提供第二个的参数为键名:
<div v-for="(value, key) in object">{{ key }}: {{ value }}</div>
结果:
firstName: Joh
lastName: Doe
age: 30
第三个参数为索引:
<div v-for="(value, key, index) in object">{{ index }}. {{ key }}: {{ value }}</div>
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素.
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有的且唯一的 id。
<div v-for="item in items" :key="item.id"><!-- 内容 --></div>
建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
变异方法
替换数组
变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如:filter(), concat()和 slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {return item.message.match(/Foo/)})
Vue 不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
举个例子:
var vm = new Vue({data: {items: ['a', 'b', 'c']}})vm.items[1] = 'x' // 不是响应性的vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:
// Vue.setVue.set(vm.items, indexOfItem, newValue)// Array.prototype.splicevm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
Vue 不能检测对象属性的添加或删除:
var vm = new Vue({data: {a: 1}})// `vm.a` 现在是响应式的vm.b = 2// `vm.b` 不是响应式的
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({data: {userProfile: {name: 'Anika'}}})
你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27)//或vm.$set(vm.userProfile, 'age', 27)
有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign() 或 _.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,你应该这样做:
vm.userProfile = Object.assign({}, vm.userProfile, {age: 27,favoriteColor: 'Vue Green'})
有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。
method1:
<li v-for="n in evenNumbers">{{ n }}</li>data: {numbers: [ 1, 2, 3, 4, 5 ]},computed: {evenNumbers: function () {return this.numbers.filter(function (number) {return number % 2 === 0})}}
在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 你可以使用一个 method 方法:
<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 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">{{ todo }}</li>
上面的代码只传递了未完成的 todos。
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 )上。如:
<ul v-if="todos.length"><li v-for="todo in todos">{{ todo }}</li></ul><p v-else>No todos left!</p>
v-for2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props .不自动将 item 注入到组件里的原因是,这会使得组件与 v-for 的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。
下面是一个简单的 todo list 的完整例子:
<div id="todo-list-example"><inputv-model="newTodoText"v-on:keyup.enter="addNewTodo"placeholder="Add a todo"><ul><liis="todo-item"v-for="(todo, index) in todos"v-bind:key="todo.id"v-bind:title="todo.title"v-on:remove="todos.splice(index, 1)"//删除功能,第一个参数为第一项位置,第二个参数为要删除几个。array.splice(index,num)></li></ul></div>//jsVue.component('todo-item', {template: '\<li>\{{ title }}\<button v-on:click="$emit(\'remove\')">X</button>\</li>\',props: ['title']})new Vue({el: '#todo-list-example',data: {newTodoText: '',todos: [{id: 1,title: 'Do the dishes',},{id: 2,title: 'Take out the trash',},{id: 3,title: 'Mow the lawn'}],nextTodoId: 4},methods: {addNewTodo: function () {this.todos.push({id: this.nextTodoId++,title: this.newTodoText})this.newTodoText = ''}}})
许多事件处理逻辑会相对复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。
<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!'
在内联 JavaScript 语句中调用方法:
<div id="example-3"><button v-on:click="say('hi')">Say hi</button><button v-on:click="say('what')">Say what</button></div>//jsnew 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()//preventDefault() 方法阻止元素发生默认的行为alert(message)}}
在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop
.prevent
.capture
.self
.once
<!-- 阻止单击事件继续传播 --><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>
在监听键盘事件时,我们经常需要检查常见的键值。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符.
记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:
<!-- 同上 --><input v-on:keyup.enter="submit"><!-- 缩写语法 --><input @keyup.enter="submit">
全部的按键别名:
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
<!-- Alt + C --><input @keyup.alt.67="clear"><!-- Ctrl + Click --><div @click.ctrl="doSomething">Do something</div>
.exact 修饰符.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 --><button @click.ctrl="onClick">A</button><!-- 有且只有 Ctrl 被按下的时候才触发 --><button @click.ctrl.exact="onCtrlClick">A</button><!-- 没有任何系统修饰符被按下的时候才触发 --><button @click.exact="onClick">A</button>
.left
.right
.middle
你可以用 v-model 指令在表单 及 元素上创建双向数据绑定
单向绑定与双向绑定
1、单向绑定
单向数据绑定的实现思路:
① 所有数据只有一份
② 一旦数据变化,就去更新页面(只有data-->DOM,没有DOM-->data)
③ 若用户在页面上做了更新,就手动收集(双向绑定是自动收集),合并到原有的数据中。
2、双向绑定
数据的双向绑定是vue实现的一大功能。
使用v-model指令,实现视图和数据的双向绑定。
所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据。这是通过设置属性访问器实现的。
v-model主要用在表单的input输入框,完成视图和数据的双向绑定。
v-model只能用在、、这些表单元素上。
双向绑定的缺点:不知道data什么时候变了,也不知道是谁变了,变化后也不会通知,当然可以watch来监听data的变化,但这复杂,还不如单向绑定。
<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>
<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>
<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: ''}})
多选时 (绑定到一个数组): 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' }]}})
对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值)。
<!-- 当选中时,`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>
但是有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用 v-bind 实现,并且这个属性的值可以不是字符串。
<inputtype="checkbox"v-model="toggle"true-value="yes"false-value="no">// 当选中时vm.toggle === 'yes'// 当没有选中时vm.toggle === 'no'
这里的 true-value 和 false-value 特性并不会影响输入控件的 value 特性。
<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
<!-- 在“change”时而非“input”时更新 --><input v-model.lazy="msg" >
<input v-model.number="age" type="number">
这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。
<input v-model.trim="msg">
Vue.component(tagName, options)
Vue.component('my-component', {// 选项})
组件在注册之后,便可以作为自定义元素 <my-component></my-component> 在一个实例的模板中使用。注意确保在初始化根实例之前注册组件:
// 注册Vue.component('my-component', {template: '<div>A custom component!</div>'})// 创建根实例new Vue({el: '#example'})
var Child = {template: '<div>A custom component!</div>'}new Vue({// ...components: {// <my-component> 将只在父组件模板中可用'my-component': Child}})
在自定义组件中使用这些受限制的元素时会导致一些问题,例如:
<table><my-row>...</my-row></table>
自定义组件 会被当作无效的内容,因此会导致错误的渲染结果。变通的方案是使用特殊的 is 特性:
<table><tr is="my-row"></tr></table>
应当注意,如果使用来自以下来源之一的字符串模板,则没有这些限制
- <script type="text/x-template">
- JavaScript 内联模板字符串
- .vue 组件
data 必须是函数
构造 Vue 实例时传入的各种选项大多数都可以在组件里使用。只有一个例外:data 必须是函数.避免组件实例共享了同一个 data 对象
组件设计
组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。然而,通过一个良好定义的接口来尽可能将父子组件解耦也是很重要的。这保证了每个组件的代码可以在相对隔离的环境中书写和理解,从而提高了其可维护性和复用性。
在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。看看它们是怎么工作的。

bbbbbbbnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn