是什么
- 一个前端的框架,基于组件思想设计。利用属性和元素块进行动态绑定,实现热更新。
基础概念+demo
- 数据插入:
- 可以有mustache方法,直接在文本中插值;
<span v-once>This will never change: {{ msg }}</span>
- 也有v-bind方法,在html的属性中插值。
<div v-bind:id="dynamicId"></div>
- 总结:都是通过元素埋点,再关联起埋下的点和data中的数据实现插入数据,
- 使用js表达式:类似于数据插入方式,js表达式也可以直接在templete中直接插入使用:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
注意:模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
- 对于概念1的扩展:过滤器。在1中,我们知道了可以使用{{msg }}插值的方式插入数据。而对于插入的数据,我们可以绑定一个过滤器对可以插入的数据做一个限定。使用方式如下:
{{ message | capitalize }}
Vue 2.x 中,过滤器只能在 mustache 绑定中
配合使用:
new Vue({
// ...
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
上述函数创建了一个过滤函数,并且在vue中挂载,所有该 .vue文件下绑定了名字是capitalize的mustache数据都会被它过滤。且表达式的值message 作为过滤函数的第一个参数。过滤器可以串联(同时绑定多个):
{{ message | filterA | filterB }}
- 指令:指令(Directives)是带有 v- 前缀的特殊属性。指令属性的值预期是单一 JavaScript 表达式(除了 v-for,之后再讨论)。指令的职责就是当其表达式的值改变时相应地将某些行为应用到 DOM 上。例如:
<p v-if="seen">Now you see me</p>
这里, v-if 指令将根据表达式 seen 的值的真假来移除/插入
元素。这个功能是v-if已经写好的,浏览器解析v-if的时候,其实是把他用了一个js表达式来代替的(参考指针思想)。
- 参数:一些指令可以接受一个参数,在指令后用冒号写明就好。例如:
<a v-bind:href="url"></a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。其实这里也是,浏览器解析的时候,将v-bind:解析成了一个需要传参的js表达式.将冒号中的内容视为参数传入。而内部函数的具体功能就是一个===。将url的值赋给了href。
相似的例子还有:
<a v-on:click="doSomething">
这里,浏览器解析的时候是将v-on:click用函数代替了。这个函数的具体内容是:doSomething事件绑定在了这个dom中。一旦链接被点击,就会触发绑定的事件(函数)。
- 修饰符:修饰符(Modifiers)是以半角句号 . 指明的特殊后缀,用于指出一个指定应该以特殊方式绑定。他是概念4的扩展。在4中,我们知道了:指令的职责就是当其表达式的值改变时相应地将某些行为应用到 DOM 上。而修饰符是对于指令的限制,或者说具体化。例如:
<form v-on:submit.prevent="onSubmit"></form>
.prevent
修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault().即:在调用onsubmit的基本作用后,还要执行一个.prevent
。
新概念之进一步
- 计算属性:是vue可以挂载的一个属性,地位等同于el,data,method。产生之因为是:在模板(概念2中提到的mustache方式就叫做模板,一个{{}}就是一个模板)中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
在这么长的表达式中,你比较难确定你最后产生的message到底是什么。所以,你要想方法确定它。这时候可以采用计算属性的方法。例如:
<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('')
}
}
})
Original message: “Hello”
Computed reversed message: “olleH”
这里我们声明了一个计算属性 reversedMessage。我们提供的函数将用作属性 vm.reversedMessage 的 getter 。即:虽然代码形式上,这似乎是reversedMessage是一个方法,但其实他是一个属性,这个属性绑定了一个getter方法,方法的内容就是function内的。它相当于:
computed: {
reversedMessage: {
// getter
get: function () {
return this.message.split('').reverse().join('')
},
// setter
set: function (newValue) {
}
}
}
这样一来,你可以像绑定普通属性一样在模板中绑定计算属性,因为本质上,计算属性和data中的属性,没啥不一样的。 Vue 知道 vm.reversedMessage 依赖于 vm.message ,因此当 vm.message 发生改变时,依赖于 vm.reversedMessage 的绑定也会更新。而且最妙的是我们是声明式地创建这种依赖关系:计算属性的 getter 是干净无副作用的,因此也是易于测试和理解的。
- 注意:你可能已经注意到我们可以通过调用表达式中的method来达到同样的效果:
<p>Reversed message: "{{ reverseMessage() }}"</p>
// in component
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
但是首先可以看出,计算属性使用方式是直接用属性名{{reverseMessage}}即可,这种使用方式是概念一里面提及的数据数据插入;而这里是概念2的js表达式方式(使用模块,模块的定义上面已经提到了)。所以这两种方式带来了差异如下:
- 计算属性是基于它的依赖缓存。计算属性只有在它的相关依赖发生改变时才会重新取值。这就意味着只要 message 没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。而函数则是每次都要渲染执行,带来效率的负担。
- 但是,有时候是需要时刻更新的,比如说时间date。如果用computed来定义时间的话,时间会因为缓存中的时间不变而一直不更新,者很明显不是我们想要的。例如:
computed: {
now: function () {
return Date.now()
}
}
关于计算属性,还可以看出$watch
方法(它用于观察 Vue 实例上的数据变动)的区别,例如:
<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
}
}
})
两种方式,显然下一种简单一些。因为watch不能监视两个属性的变化,所以在这个场景下还是有所缺陷的。
- 但是,在一些需要复杂响应的情况下,也需要一个自定义的 watcher 。这是为什么 Vue 提供一个更通用的方法通过 watch 选项,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或昂贵操作时,这是很有用的。这是watch的产生之因。
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Since there is already a rich ecosystem of ajax libraries -->
<!-- and collections of general-purpose utility methods, Vue core -->
<!-- is able to remain small by not reinventing them. This also -->
<!-- gives you the freedom to just use what you're familiar with. -->
<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: {
// 如果 question 发生改变,这个函数就会运行
question: function (newQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问yesno.wtf/api的频率
// ajax请求直到用户输入完毕才会发出
// 学习更多关于 _.debounce function (and its cousin
// _.throttle), 参考: 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
})
},
// 这是我们为用户停止输入等待的毫秒数
500
)
}
})
</script>
在这个示例中,使用 watch 选项允许我们执行异步操作(访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这是计算属性无法做到的。
可以看出,上述的例子中体现了watch的应用场景:数据改变的时候即可调用函数,实现同步调用,时刻监听。
并且在watch种异步访问了函数getAnswer。同时,getAnswer中的定义又可以看出,函数的定义,它可以不用自己写,直接一个getAnswer:另一个函数名(已经写好的)。就可以实现函数声明。
.debounce函数是:延时,本来作用是控制访问频率,访问时间过长会让他先排着队,之后再执行。这个过程不返回结果,而是一直占用。函数进行到这里,此时是无法进行下一步的,直到排队完成,执行完毕(或者返回bug)才开始下一步。而这类的函数是js中定义好的,直接用就好了,相似的还有:.capitalize。他们相当于是java中的sysout之类的静态函数,不需要构造对象就可以通过名字直接调用。
- 概念四(指令)和概念一的第二种方法(用v-bind的方式进行绑定)的扩展:绑定数据在class和style上。
有两种方式,一种是绑定在数据上。例如:
/*一*/
//基础用法:和绑定数据一个样子,只是这里绑定的是一个对象(在vue中,{name:属性,name2:属性}这种形式的数据结构叫做对象。)。绑定的对象,当isActive为true的时候,class发生改变
<div v-bind:class="{ active: isActive }"></div>
/*二*/
//动态绑定多个class
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
//对应的data
data: {
isActive: true,
hasError: false
}
//最后效果是:<div class="static active"></div>
/*三*/
//直接绑定对象,不过是已经在data中声明的对象,而不是一个未命名的{}对象。
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
//最终的渲染结果和例子二一样。
总结:上述的绑定都是绑定一个对象。关键其实就是v-bind:class在解析的时候会去寻找对象中为true的属性,那个属性的名字就会被解析为一个class。
第二种是绑定在数组上:那个其实相当于数组中的每一个位置有一个class,直接解析成为一个字符串,就是该div(假设是绑定在了div上)的class了。个人不太能感觉到它的存在意义。大概就是在data中可以操作数组来动态改变class吧。不过,。。感觉和绑定对象的方式有些重复了。但是,个人对于第二种方式其实更加熟悉(毕竟是c语言过来的人)。例子:
<div v-bind:class="[activeClass, errorClass]">
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
//渲染为:<div class="active text-danger"></div>
也可以结合两者,数组中存放一个对象,而不是一个数据:
<div v-bind:class="[{ active: isActive }, errorClass]">
- 同理,style的绑定也和class一样:例子:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
- 概念四扩展:v-if的使用:v-if是一个是否编译的指令。如果v-if的结果是false,那么编译的时候,包含的部分就会被跳过。例子:
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>//可以配合v-else使用
//可以实现同时切换(之所以说是切换,是v-if在应用中多是用来显示多个块中的一个,所以是切换的作用)多个元素。放在《div》中也是一样的效果。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
注意:v-if后面也可以接的是一个函数,或者js表达式。
<div v-if="Math.random() > 0.5">
Sorry
</div>
<div v-else>
Not sorry
</div>
和v-if有相似功能的还有v-show(不过v-show是一个css属性来的,而不是跳过渲染,所以会带来内存上的损耗)。
- 概念四的扩展:v-for:循环。基本使用是对data(可以是父作用域中的属性)中定义的数组进行循环遍历。进阶用法是取值的时候除了value,还可以将index也取出。
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{message: 'foo' },
{message: 'Bar' }
]
}
})
同时,结合template还可以将去除的元素放在不同的html标签中。
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
在vue中,对象和数组总是具有可代替性的。所以,v-for在对象中也是可以使用的。例如:
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
new Vue({
el: '#repeat-object',
data: {
object: {
FirstName: 'John',
LastName: 'Doe',
Age: 30
}
}
})
结果会将所有的属性输出:
和数组中可以同时取得index+value类似,对于属性可以同时取得key+value。例如:
<div v-for="(value, key) in object">
{{ key }} : {{ value }}
</div>
- 进阶用法:组件和v-for。
v-for在组件中直接使用,数据是无法传到组件内部的。因为组件自己定义了一个作用域。所以要依靠数据绑定(第二种)的方式,将自己的数据绑定到组件上。例如:
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index">
</my-component>
有趣的demo:
<div id="todo-list-example">
<input
v-model="newTodoText"
v-on:keyup.enter="addNewTodo"
placeholder="Add a todo"
>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:title="todo"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
Vue.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: [
'Do the dishes',
'Take out the trash',
'Mow the lawn'
]
},
methods: {
addNewTodo: function () {
this.todos.push(this.newTodoText)
this.newTodoText = ''
}
}
})
- vue中对于数组提供了变异方法和非变异方法。二者区别是:是否会改变原数组的内容。
- 变异方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
- 非变异方法:这些方法不会改变原始数组,但总是返回一个新数组。
filter(), concat(), slice()
- 注意:vue对于两种情况是不会同步数据(热更新)的。
- 当你直接设置一个项的索引时,例如: vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如: vm.items.length = newLength
所以为了达到效果。请使用以下方式改变数组。
//修改节点内容。
Vue.set(example1.items, indexOfItem, newValue)
example1.items.splice(indexOfItem, 1, newValue)
//修改长度
example1.items.splice(newLength)
- 概念四的扩展:
v-on的具体作用:可以用 v-on 指令监听 DOM 事件来触发一些 JavaScript 代码。
例如:
<div id="example-1">
<button v-on:click="counter += 1">增加 1</button>
<p>这个按钮被点击了 {{ counter }} 次。</p>
</div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
这是一个click直接绑定了一个js表达式的请情况。还可以绑定一个方法:
<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 事件
alert(event.target.tagName)
}
}
})
注意,绑定方法的时候有两种方式,一个是不用参数的,那么事件本身会被当成参数传递;二是需要参数的,那么要自己传入,这叫内联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)
}
}
})
//二
<button v-on:click="warn('Form cannot be submitted yet.', $event)">Submit</button>
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) event.preventDefault()
alert(message)
}
}
除此之外,对于事件的监听还需要有事件修饰符来扩展它的功能,有四个修饰符:
.stop
.prevent
.capture
.self
<!-- 阻止单击事件冒泡 -->
<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>
还有按键修饰符,可以让监听不仅仅是监听dom元素,还可以监听按键情况:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 缩写语法 -->
<input @keyup.enter="submit">
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input v-on:keyup.13="submit">
常见的监听快捷别名有:(即:其实普通的监听方式用的是数字代表键位的,上述例子中的13就是enter的数字编码)
enter
tab
delete (捕获 “删除” 和 “退格” 键)
esc
space
up
down
left
right
- 概念四的扩展:v-model在表单上的使用。
作用:实现数据的双向绑定,为什么和表单控件联系在一起呢,因为表单控件通常是输入框,多选框之类的,会改变数据的dom元素,所以,v-model产生和他搭配再适合不过了。
v-model在解析的时候应该是被解读成了这么一个方法:获得dom元素中的返回值value作为参数(当然,可以在dom中手动设置value,这时候返回的会优先是手动设置的value),并且赋值给了绑定的参数(data属性)。
例如:
//没有value的
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
//绑定了value的
<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' }
]
}
})
//这时候选择one,返回值会是A。
上述的两个例子说明了动态绑定value的作用。可以让返回值不再那么显而易见。应用场景可以是:选择剑法,返回的是独孤九剑,而不是剑法两个字。这样可以在王爷文字游戏中有个很好的应用。
类似的例子还有:
<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b"
>
<input type="radio" v-model="pick" v-bind:value="a">
对于v-model指令,也有修饰符:
//在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步,即:输入完成后回车,才将值传到data中,改变绑定的data的值。
<input v-model.lazy="msg" >
//自动将用户的输入值转为 Number 类型
<input v-model.number="age" type="number">
//自动过滤用户输入的首尾空格
<input v-model.trim="msg">
- 组件:组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。
- 注册:可以通过两种方式定义一个组件。
- 一个是全局挂载的方式,注意,注册一定要在vue实例化前。
new Vue({
el: '#some-element',
// 选项
})
Vue.component('my-component', {
// 选项
})
- 二是局部挂载
var abcd={
templace:'<p>hello</p>',
}
var vm =new vue(){
data:{
},
method:{
}
component:abcd
}
- 实例:
<div id="example">
<my-component></my-component>
</div>
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
最后渲染为:
<div id="example">
<div>A custom component!</div>
</div>
- 在
-
,
- 这类限制了内部只能包含固定的dom元素的元素里要想使用组件,可以用is属性。例如:
-
,
<table>
<tr is="my-row"></tr>
</table>
<!--my-row是自己定义的一个组件名-->
- 组件中的数据data必须是一个函数,用return的方式返回自己的数据到自己的template中。
var data = { counter: 0 }
Vue.component('simple-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
// data 是一个函数,因此 Vue 不会警告,
// 但是我们为每一个组件返回了同一个对象引用
data: function () {
return data <!--由此可知,返回的数据可以是一个全局变量var。不过此时,所有的组件实例都会共用同一个数据data-->
}
})
new Vue({
el: '#example-2'
})
- 父组件和子组件的关系:父向子传递数据,子组件的事件被父亲监听。
- 父子组件的数据传输:通过props。
具体用法:子组件在声明的时候声明自己的属性(相当于对外暴露接口,一个形参)。在父组件中使用子组件的时候可以通过属性传递数据到子组件的内部。例子:
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 可以用在模板内
// 同样也可以在 vm 实例中像 “this.message” 这样使用
template: '<span>{{ message }}</span>'
})
//然后向它传入一个普通字符串:
<child message="hello!"></child>
属性prop可以动态绑定数据:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
同时,prop也可以限定传入的数据类型。此时prop不用数组了,而是用对象的形式:
Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
})
注意:
数据的传递是单向的,不应该在子组件内部修改传入的数据。
- 父组件监听子组件事件 : 可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。例如:
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
可以知道,父组件通过给子组件定义属性的形式,可以达到传入数据:<button-counter v-bind:内部prop中的数据的名字="外部数据"></button-counter>
和监听方法<button-counter v-on:子组件的方法的名字="外部vue中定义的方法"></button-counter>
两个作用,实现交互。
例如:
<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
template: '\
<span>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
>\
</span>\
',
props: ['value'],
methods: {
// Instead of updating the value directly, this
// method is used to format and place constraints
// on the input's value
updateValue: function (value) {
var formattedValue = value
// Remove whitespace on either side
.trim()
// Shorten to 2 decimal places
.slice(0, value.indexOf('.') + 3)
// If the value was not already normalized,
// manually override it to conform
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// Emit the number value through the input event
this.$emit('input', Number(formattedValue))
}
}
})
这是一个v-model的组件实现,其实也可以反映v-model代表的js表达式的内部实质。
- 使用slot插槽:定义的组件如果需要组合其他的dom元素,那么可以使用slot进行组合。例如:
<child-component>
{{ message }}
</child-component>
那么要将 {{ message }}放到的哪个位置呢?这时候可以靠slot来定位了。(如果没有slot,那么,message将会被忽略)。slot的具体用法如下:
//子组件的template定义:
<div>
<h2>I'm the child title</h2>
<slot>
如果没有分发内容则显示我。
</slot>
</div>
//父组件中的使用
<div>
<h1>I'm the parent title</h1>
<my-component>
<p>This is some original content</p>
<p>This is some more original content</p>
</my-component>
</div>
//最后渲染的结果
<div>
<h1>I'm the parent title</h1>
<div>
<h2>I'm the child title</h2>
<p>This is some original content</p>
<p>This is some more original content</p>
</div>
</div>
如果要插入的位置有很多个,那么要对每个slot命名。
//app-layout的tempate
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
//父组件中的使用
<app-layout>
<h1 slot="header">Here might be a page title</h1>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<p slot="footer">Here's some contact info</p>
</app-layout>
//最终结果
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
还有一种类似的用法:
<my-component inline-template>
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
</div>
</my-component>
加上了inline-template说明后,div块中的内容会被当成是子组件的模板。此时div的内容不会被子组件覆盖。而是直接加在了子组件后面。(注意,不是直接加inline-template就可以的)
- 应用:动态组件:
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
<component v-bind:is="currentView">
<!-- 组件在 vm.currentview 变化时改变! -->
</component>
这个可以用在(ajax)切换页面的情形中。
var Home = {
template: '<p>Welcome home!</p>'
}
var vm = new Vue({
el: '#example',
data: {
currentView: Home
}
})
//注意:不是直接放在了component属性中,而是放在了data中。。。。算是第二种局部注册的方式了。
那么,到此位置,vue的入门概念基本介绍完毕了。