MVVM模式
M:model 模型 包括数据和一些基本操作
V:view 视图,页面渲染结果
VM:view model 把视图与数据联系到一起 模型与视图的双向操作(无需开发人员干涉) 将DOM操作完全封装
数据与视图互相独立
只要Model发生改变,View上自然会表现出来
当用户修改了View,Model也会跟着改变 视图与数据模型的双向绑定
通过视图与模型的双向绑定,简化前端操作
vue中不涉及到视图与数据的绑定 例如jquery中视图与数据进行绑定
$('#app').click(function(){})
渐进式框架
可以选择性的使用该框架的一个或者一些组件,也不要求系统都使用该框架,提高开发效率
创建一个vue实例
var app = new Vue({
//el 绑定到哪个地方
el: '#app',
// 数据
data: {
message: 'test',
messages: 1
},
// 方法
methods: {
num() {
this.messages = this.messages - 0 + 1
},
}
})
Object.freeze() 阻止响应系统
Object.freeze()
,这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。
var obj = {
foo: 'bar'
}
Object.freeze(obj)
new Vue({
el: '#app',
data: obj
})
<div id="app">
<p>{{ foo }}</p>
<!-- 这里的 `foo` 不会更新! 因为响应被阻止了-->
<button v-on:click="foo = 'baz'">Change it</button>
</div>
v-on 事件处理
可以直接用 @
<div>
<button @click='num'>添加</button>
</div>
生命周期及钩子函数
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会,当实例处于不同的周期的时候,对应的函数会被触发。
比如 created
钩子可以用来在一个实例被创建之后执行代码
不能使用箭头函数来定义生命周期方法
不要在选项 property 或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误
看懂这个图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tGNEF2VQ-1601186966016)(file:///C:\Users\asus\AppData\Roaming\Tencent\Users\504708627\QQ\WinTemp\RichOle{5_}UXMZESZ~V[@WF2[TF4J.png)]
在mounted时将挂在的el中的{{}}里面的内容进行解析
let app = new Vue({
el: '#app',
data: {
masg: 'hgellovue',
id: 'redbg',
message: '',
isShow: true
},
beforeCreate: function() {
// vue实例创建出来 但是数据还没有绑定 事件也没有绑定
console.log('beforecreate')
console.log(this)
// undefined
console.log(this.masg)
console.log(this.event1)
},
created: function() {
// 数据已经绑定 方法也已经初始化完成
// 但是只是拿到了数据 并没有渲染到视图上
console.log('created')
console.log(this.masg)
console.log(this.event1)
},
beforeMount: function() {
// null 渲染之前 拿不到根据数据生成的dom对象
let classname = document.querySelector('.redbg');
console.log(classname)
},
mounted: function() {
//渲染之后 可以获得根据数据生成的数据对象
let classname = document.querySelector('.redbg');
console.log(classname)
},
beforeUpdate: function() {
console.log('这是修改之前的阶段')
},
updated: function() {
console.log('修改完成,现在的数据是' + this.message)
},
// 总共8个生命周期 剩下两个 destroy brforedestroy用不太到
methods: {
event1: function() {
console.log('事件1');
},
event2: function() {
alert('输入完成')
},
event3: function() {
this.isShow = !this.isShow
}
},
// 组件分全局注册与局部注册
// 全局注册是vue.component 必须在创建实例之前填写
// 局部注册只在当前实例中生效 在实例中添加components
components: {
'hello-vom': {
// data是要 return出来
data: function() {
return {
vom: 'hellovom'
}
},
template: `<div>这是一个局部注册的组件{{vom}}</div>`,
// destroy brforedestroy一般用于销毁组件
// 比如给组件一个v-if 当为false的时候就涉及到了组件的销毁 性能消耗大
// v-show是将数据进行隐藏 并不是销毁
destroyed: function() {
console.log('销毁已经完成')
},
}
}
})
指令
带有v-前缀的特殊属性
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
指令中的动态参数问题
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName
会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data
property attributeName
,其值为 "href"
,那么这个绑定将等价于 v-bind:href
。 就是相当于吧[]里的当作一个js表达式来进行解析
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
在这个示例中,当 eventName
的值为 "focus"
时,v-on:[eventName]
将等价于 v-on:focus
属实我觉得没啥用。
对动态参数的值的约束
动态参数预期会求出一个字符串,异常情况下值为 null
。这个特殊的 null
值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。
对动态参数表达式的约束
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。例如:
<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>
变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
插值表达式
{{表达式}}
说明:
- 该表达式支持JS语法,可以调用JS内置函数(必须有返回值)
- 表达式必须有返回的结果 例如 1+1
- 可以直接获取VUE实例中的数据或者函数
- 这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式
以下表达式可以生效:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
以下表达式不会生效:
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math
和 Date
。你不应该在模板表达式中试图访问用户定义的全局变量。
v-once
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:
<span v-once>这个将不会改变: {{ msg }}</span>
v-html
可以将数据解析成html代码 渲染成html
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html
指令:
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
v-text
将数据直接按照text格式来进行输出 直接输出
v-text与v-html区别
<body>
<div id="app">
<input type="text" v-model='num'>
<div v-text='text'></div>
<div v-html='text'></div>
</div>
<script>
var sm = new Vue({
el: '#app',
data: {
num: 1,
text: "<span>lalal</span>"
},
// 生命周期及钩子函数 以created为例 必会执行
// 这是es6简化写法 原本完整写法 created:function(){}
created() {
alert('vue实例被创建了')
}
})
</script>
</body>
输出结果: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ESGjx8Ro-1601186966021)(file:///C:\Users\asus\AppData\Roaming\Tencent\Users\504708627\QQ\WinTemp\RichOle\A@[77LFB72XCH]A4BJO4GKE.png)]
二者最开始是用于解决使用{{}}出现的插值闪烁的问题,不过目前使用{{}}也不太会出现插值闪烁问题
注意:此处为单向绑定,数据对象上的值改变,插值会发生变化;但是当插值发生变化并不会影响数据对象的值
v-pre
- 显示原始信息跳过编译过程
- 跳过这个元素和它的子元素的编译过程。
- **一些静态的内容不需要编译加这个指令可以加快渲染
<span v-pre>{{ this will not be compiled }}</span>
<!-- 显示的是{{ this will not be compiled }} -->
<span v-pre>{{msg}}</span>
<!-- 即使data里面定义了msg这里仍然是显示的{{msg}} -->
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue.js'
}
});
</script>
v-cloak
防止出现闪烁问题
<style type="text/css">
/*
1、通过属性选择器 选择到 带有属性 v-cloak的标签 让他隐藏
*/
[v-cloak]{
/* 元素隐藏 */
display: none;
}
</style>
<body>
<div id="app">
<!-- 2、 让带有插值 语法的 添加 v-cloak 属性
在 数据渲染完场之后,v-cloak 属性会被自动去除,
v-cloak一旦移除也就是没有这个属性了 属性选择器就选择不到该标签
也就是对应的标签会变为可见
-->
<div v-cloak >{{msg}}</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
// el 指定元素 id 是 app 的元素
el: '#app',
// data 里面存储的是数据
data: {
msg: 'Hello Vue'
}
});
</script>
</body>
</html>
v-model
可以实现双向绑定
限制在 <input>、<select>、<textarea>、components
使用
输入框中改变的时候会双向改变messages的内容
<input v-model='messages'>
绑定在不同的标签的时候,对应的值不同
多个checkbox对应一个model时,model的类型是数组
单个checkbox的值是boolean
radio对应的值是input的value
input和textarea对应的是字符串
seleck单选对应字符串 多选对应数组
修饰符
.lazy
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model
添加 number
修饰符
<input v-model.number="age" type="number">
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model
添加 trim
修饰符:
<input v-model.trim="msg">
v-bind
动态绑定属性 可以响应式更新
绑定属性 v-bind
指令可以用于响应式地更新 HTML attribute:
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">
<!-- 缩写 -->
<img :src="imageSrc">
注意:v-bind:class指令可以与普通的class特性共存
v-bind 中支持绑定一个对象
如果绑定的是一个对象 则 键为 对应的类名 值 为对应data中的数据
<ul class="box" v-bind:class="{textColor:isColor, textSize:isSize}">
<li>学习Vue</li>
<li>学习Node</li>
<li>学习React</li>
</ul>
其中 值一般是true false
v-bind支持绑定一个数组
数组中classA和 classB 对应为data中的数据
<ul class="box" :class="[classA, classB]">
<li>学习Vue</li>
<li>学习Node</li>
<li>学习React</li>
</ul>
<script>
var vm= new Vue({
el:'.box',
data:{
classA:‘textColor‘,
classB:‘textSize‘
}
})
</script>
<style>
.box{
border:1px dashed #f0f;
}
.textColor{
color:#f00;
background-color:#eef;
}
.textSize{
font-size:30px;
font-weight:bold;
}
</style>
最终类名绑定为textColor textSize
绑定对象和绑定数组 的区别
- 绑定对象的时候 对象的属性 即要渲染的类名 对象的属性值对应的是 data 中的数据
- 绑定数组的时候数组里面存的是data 中的数据
绑定style时的常用操作
用v-bind来绑定style属性的时候 一般使用绑定对象的方法
<div v-bind:style="styleObject">绑定样式对象</div>'
<!-- CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来) -->
<div v-bind:style="{ color: activeColor, fontSize: fontSize,background:'red' }">内联样式</div>
<!--组语法可以将多个样式对象应用到同一个元素 -->
<div v-bind:style="[styleObj1, styleObj2]"></div>
<script>
new Vue({
el: '#app',
data: {
styleObject: {
color: 'green',
fontSize: '30px',
background:'red'
},
activeColor: 'green',
fontSize: "30px"
},
styleObj1: {
color: 'red'
},
styleObj2: {
fontSize: '30px'
}
</script>
v-on
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ycgDUeGj-1601186966024)(file:///C:\Users\asus\AppData\Roaming\Tencent\Users\504708627\QQ\WinTemp\RichOle[AVTQ8W]{P%CT2QSALJ0[L8.png)]
v-on事件函数中传入参数
如果方法未传入参数 ,则默认只有参数event(事件对象)
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法:
<body>
<div id="app">
<div>{{num}}</div>
<div>
<!-- 如果事件直接绑定函数名称,那么默认会传递事件对象作为事件函数的第一个参数 -->
<button v-on:click='handle1'>点击1</button>
<!-- 2、如果事件绑定函数调用,那么事件对象必须作为最后一个参数显示传递,
并且事件对象的名称必须是$event
-->
<button v-on:click='handle2(123, 456, $event)'>点击2</button>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
num: 0
},
methods: {
handle1: function(event) {
console.log(event.target.innerHTML)
},
handle2: function(p, p1, event) {
console.log(p, p1)
console.log(event.target.innerHTML)
this.num++;
}
}
});
</script>
事件修饰符
- 在事件处理程序中调用
event.preventDefault()
或event.stopPropagation()
是非常常见的需求。 - Vue 不推荐我们操作DOM 为了解决这个问题,Vue.js 为
v-on
提供了事件修饰符 - 修饰符是由点开头的指令后缀来表示的
<!-- 阻止单击事件继续传播 阻止冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 点击事件将只会触发一次 -->
不像其它只能对原生的 DOM 事件起作用的修饰符,.once 修饰符还能被用到自定义的组件事件上。
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 可以提升移动端性能-->
<div v-on:scroll.passive="onScroll">...</div>
<!-- 修饰符可以串联 -->
<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>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
按键修饰符
- 在做项目中有时会用到键盘事件,在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为
v-on
在监听键盘事件时添加按键修饰符
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
<!-- -当点击enter 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
<!--当点击enter或者space时 时调用 `vm.alertMe()` -->
<input type="text" v-on:keyup.enter.space="alertMe" >
常用的按键修饰符
.enter => enter键
.tab => tab键
.delete (捕获“删除”和“退格”按键) => 删除键
.esc => 取消键
.space => 空格键
.up => 上
.down => 下
.left => 左
.right => 右
<script>
var vm = new Vue({
el:"#app",
methods: {
submit:function(){},
alertMe:function(){},
}
})
</script>
自定义按键修饰符别名
- 在Vue中可以通过
config.keyCodes
自定义按键修饰符别名 - 你还可以通过全局
config.keyCodes
对象自定义按键修饰符别名:
<div id="app">
预先定义了keycode 116(即F5)的别名为f5,因此在文字输入框中按下F5,会触发prompt方法
<input type="text" v-on:keydown.f5="prompt()">
</div>
<script>
Vue.config.keyCodes.f5 = 116;
let app = new Vue({
el: '#app',
methods: {
prompt: function() {
alert('我是 F5!');
}
}
});
</script>
系统修饰键
.ctrl
.alt
.shift
.meta
<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>
修饰符+keycode 就是二者一起按下才会触发
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。
.exact修饰符
.exact
修饰符允许你控制由精确的系统修饰符组合触发的事件。
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>
鼠标按钮修饰符
.left
.right
.middle
为何在html中绑定事件
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on
有几个好处:
- 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
- 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们
v-for
写在哪个标签中 循环输出哪个标签
v-for遍历数组
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
一般情况下,要遍历的数据可以通过钩子函数created发送异步请求获取数据】
<ul id="example-1">
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
在 v-for
块中,我们可以访问所有父作用域的 property。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' }
]
}
})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbTkkr5W-1601186966026)(file:///C:\Users\asus\AppData\Roaming\Tencent\Users\504708627\QQ\WinTemp\RichOle_LEV@V8@98TVIHF[_QPCE3U.png)]
也可以用 of
替代 in
作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
总结:第一个参数是数组元素 第二个是索引
v-for遍历对象
你也可以用 v-for
来遍历一个对象的 property。
你也可以提供第二个的参数为 property 名称 (也就是键名):
还可以用第三个参数作为索引:
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
在遍历对象时,会按 Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
维护状态(默认要使用:key)
当 Vue 正在更新使用 v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by="$index"
。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
attribute:
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>
不要使用对象或数组之类的非基本类型值作为 v-for
的 key
。请用字符串或数值类型的值。
添加key之后 在数据发生改变的时候可以按照期望的顺序重排数据,在遍历完数据又对集合增减的时候有意义
key 的作用
- key来给每个节点做一个唯一标识 复用时更加便利
- key的作用主要是为了高效的更新虚拟DOM
vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
key的简单解释
说到底,key的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。在渲染简单的无状态组件时,如果不添加key组件默认都是就地复用,不会删除添加节点,只是改变列表项中的文本值,要知道节点操作是十分耗费性能的。而添加了key之后,当对比内容不一致时,就会认为是两个节点,会先删除掉旧节点,然后添加新节点。
v-if 与v-for同时使用
- 不推荐同时使用
v-if
和v-for
- 当
v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。
这意味着 v-if
将分别重复运行于每个 v-for
循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
上面的代码将只渲染未完成的 todo。
而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if
置于外层元素 (或 `) 上。如:
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
显示过滤/排序后的结果
有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
<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
循环中) 你可以使用一个方法:
<ul v-for="set in sets">
<li v-for="n in even(set)">{{ n }}</li>
</ul>
data: {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
v-for使用值范围
v-for
也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
在template使用v-for
类似于 v-if
,你也可以利用带有 v-for
的 ` 来循环渲染一段包含多个元素的内容。比如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
在组件里使用
在自定义组件上,你可以像在任何普通元素上一样使用 v-for
。
<my-component v-for="item in items" :key="item.id"></my-component>
2.2.0+ 的版本里,当在组件上使用 v-for
时,key
现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop:
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
分支结构
v-if 使用场景
- 1- 多个元素 通过条件判断展示或者隐藏某个元素。或者多个元素
- 2- 进行两个视图之间的切换
<div id="app">
<!-- 判断是否加载,如果为真,就加载,否则不加载-->
<span v-if="flag">
如果flag为true则显示,false不显示!
</span>
</div>
<script>
var vm = new Vue({
el:"#app",
data:{
flag:true
}
})
</script>
----------------------------------------------------------
<div v-if="type === 'A'">
A
</div>
<!-- v-else-if紧跟在v-if或v-else-if之后 表示v-if条件不成立时执行-->
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<!-- v-else紧跟在v-if或v-else-if之后-->
<div v-else>
Not A/B/C
</div>
<script>
new Vue({
el: '#app',
data: {
type: 'C'
}
})
</script>
因为 v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 元素当做不可见的包裹元素,并在上面使用
v-if。最终的渲染结果将不包含
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-else
你可以使用 v-else
指令来表示 v-if
的“else 块”
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-else-if
顾名思义,充当 v-if
的“else-if 块”,可以连续使用:
<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
v-show='true’其实就是设置了display:none
v-show 和 v-if的区别
- v-show本质就是标签display设置为none,控制隐藏
- v-show只编译一次,后面其实就是控制css,而v-if不停的销毁和创建,故v-show性能更好一点。
- v-if是动态的向DOM树内添加或者删除DOM元素
- v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
v-if在条件不满足的时候直接不渲染并且原本渲染的从DOM中移除,v-show在不满足情况的时候只是进行隐藏而已
计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。当你想要在模板中多包含此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,你都应当使用计算属性。
- 模板中放入太多的逻辑会让模板过重且难以维护 使用计算属性可以让模板更加的简洁
- 计算属性是基于它们的响应式依赖进行缓存的
- computed比较适合对多个变量或者对象进行处理后返回一个结果值,也就是数多个变量中的某一个值发生了变化则我们监控的这个值也就会发生变化
- 计算属性要有一个返回值
- 最终就是经过计算 可以当作data中的属性来使用 使得页面简洁
<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: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
计算属性与方法的区别
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
总结:如果未发生响应式依赖,计算属性在第二次使用到的时候会直接 调用上一次得到的值,也就是说计算好的值会放在缓存里面,而方法却在每次调用的时候都再次执行函数
<div id="app">
<!--
当多次调用 reverseString 的时候
只要里面的 num 值不改变 他会把第一次计算的结果直接返回
直到data 中的num值改变 计算属性才会重新发生计算
-->
<div>{{reverseString}}</div>
<div>{{reverseString}}</div>
<!-- 调用methods中的方法的时候 他每次会重新调用 -->
<div>{{reverseMessage()}}</div>
<div>{{reverseMessage()}}</div>
</div>
<script type="text/javascript">
/*
计算属性与方法的区别:计算属性是基于依赖进行缓存的,而方法不缓存
*/
var vm = new Vue({
el: '#app',
data: {
msg: 'Nihao',
num: 100
},
methods: {
reverseMessage: function(){
console.log('methods')
return this.msg.split('').reverse().join('');
}
},
//computed 属性 定义 和 data 已经 methods 平级
computed: {
// reverseString 这个是我们自己定义的名字
reverseString: function(){
console.log('computed')
var total = 0;
// 当data 中的 num 的值改变的时候 reverseString 会自动发生计算
for(var i=0;i<=this.num;i++){
total += i;
}
// 这里一定要有return 否则 调用 reverseString 的 时候无法拿到结果
return total;
}
}
});
</script>
计算属性vs侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 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
}
}
})
显而易见 计算属性版本的代码更加简洁
计算属性的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 = 'John Doe'
时,setter 会被调用,vm.firstName
和 vm.lastName
也会相应地被更新。
侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
-
使用watch来响应数据的变化
-
一般用于异步或者开销较大的操作
-
watch 中的属性 一定是data 中 已经存在的数据
-
当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要设置deep属性为true对对象进行深度监听 开启深度监控来修改对象,在深度监控中回调函数为handler
// 深度监控写法 watch:{ person:{ deep :true, handler(obj){ console.log(obj.name) } } }
,,
例如:
<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>
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 = 'John Doe'
时,setter 会被调用,vm.firstName
和 vm.lastName
也会相应地被更新。
过滤器
- Vue.js允许自定义过滤器,可被用于一些常见的文本格式化。
- 过滤器可以用在两个地方:双花括号插值和v-bind表达式。
- 过滤器应该被添加在JavaScript表达式的尾部,由“管道”符号指示
- 支持级联操作 {{ message | filterA | filterB }}
- 过滤器不改变真正的
data
,而只是改变渲染的结果,并返回过滤后的版本 - 全局注册时是filter,没有s的。而局部过滤器是filters,是有s的
<div id="app">
<input type="text" v-model='msg'>
<!-- upper 被定义为接收单个参数的过滤器函数,表达式 msg 的值将作为参数传入到函数中 -->
<div>{{msg | upper}}</div>
<!--
支持级联操作
upper 被定义为接收单个参数的过滤器函数,表达式msg 的值将作为参数传入到函数中。
然后继续调用同样被定义为接收单个参数的过滤器 lower ,将upper 的结果传递到lower中
-->
<div>{{msg | upper | lower}}</div>
<div :abc='msg | upper'>测试数据</div>
</div>
<script type="text/javascript">
// lower 为全局过滤器
Vue.filter('lower', function(val) {
return val.charAt(0).toLowerCase() + val.slice(1);
});
var vm = new Vue({
el: '#app',
data: {
msg: ''
},
//filters 属性 定义 和 data 已经 methods 平级
// 定义filters 中的过滤器为局部过滤器
filters: {
// upper 自定义的过滤器名字
// upper 被定义为接收单个参数的过滤器函数,表达式 msg 的值将作为参数传入到函数中
upper: function(val) {
// 过滤器中一定要有返回值 这样外界使用过滤器的时候才能拿到结果
return val.charAt(0).toUpperCase() + val.slice(1);
}
}
});
</script>
过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数
过滤器中传递参数
<div id="box">
<!--
filterA 被定义为接收三个参数的过滤器函数。
其中 message 的值作为第一个参数,
普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。
-->
{{ message | filterA('arg1', 'arg2') }}
</div>
<script>
// 在过滤器中 第一个参数 对应的是 管道符前面的数据 n 此时对应 message
// 第2个参数 a 对应 实参 arg1 字符串
// 第3个参数 b 对应 实参 arg2 字符串
Vue.filter('filterA',function(n,a,b){
if(n<10){
return n+a;
}else{
return n+b;
}
});
new Vue({
el:"#box",
data:{
message: "哈哈哈"
}
})
</script>
组件
组件定义
组件的定义有一点要牢记,与vue实例不同,组件中data必须是一个函数,在函数中return一个对象,这样做的好处是 每个实例可以维护一份被返回对象的独立的拷贝,也就是说如果引入三个相同的组件,她们之间的数据是独立的。
//1.定义组件
let counter = {
template: '<button @click="nums++">你一共点击了{{nums}}次</button>',
data: function() {
return {
nums: 0
}
}
};
组件注册(全局 局部)
全局注册
- Vue.component(‘组件名称’, { }) 第1个参数是标签名称,第2个参数是一个选项对象
- 全局组件注册后,任何vue实例都可以用
- 组件注册要在创建根实例之前, 全局注册的组件可以用在其被注册之后的任何 (通过
new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
Vue.component('buttondel', {
template: '<button @click="nums--">你一共点击了{{nums}}次</button>',
data: function() {
return {
nums: 100
}
}
});
组件注意事项
- 组件参数的data值必须是函数同时这个函数要求返回一个对象
- 组件模板必须是单个根元素
- 组件模板的内容可以是模板字符串 反引号``
- 在html中及时你定义组件使用的是驼峰方式命名,你也只能通过-方式来引入组件
// 如果组件使用驼峰式命名 只有在template模板中可以使用驼峰式命名 在html中引入的时候只能转换为A-B的类型
Vue.component('buttonDel', {
template: '<button @click="nums--">你一共点击了{{nums}}次</button>',
data: function() {
return {
nums: 100
}
}
});
<!-- 有效引入 -->
<button-del></button-del>
<!-- 无效引入 -->
<buttonDel></buttonDel>
局部注册
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。 所以需要用到局部注册
只能在当前注册它的vue实例中使用
在该vue实例中添加components属性】
<div id="app">
<my-component></my-component>
</div>
<script>
// 定义组件的模板
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
//局部注册组件
components: {
// <my-component> 将只在父模板可用 一定要在实例上注册了才能在html文件中使用
'my-component': Child
}
})
</script>
局部注册注意事项
注意局部注册的组件在其子组件中*不可用*。例如,如果你希望 ComponentA
在 ComponentB
中可用,则你需要这样写:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
或者如果你通过 Babel 和 webpack 使用 ES2015 模块,那么代码看起来更像:
import ComponentA from './ComponentA.vue'
export default {
components: {
ComponentA
},
// ...
}
注意在 ES2015+ 中,在对象中放一个类似 ComponentA
的变量名其实是 ComponentA: ComponentA
的缩写,即这个变量名同时是:
- 用在模板中的自定义元素的名称
- 包含了这个组件选项的变量名
组件名大小写
定义组件名的方式有两种:
使用 kebab-case
Vue.component('my-component-name', { /* ... */ })
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 ``。
使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 和
都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
也就是说 如果采用驼峰命名法,在模板中两种写法都可以接收,但是在html的body中(也就是直接在DOM中)只能使用kebab-case这一模式来引入
在模块系统中的组件全局化注册
一般做项目时会使用模块系统
同时讲组件写在各自的vue文件中,统一放在components文件夹中
示例代码如下:
import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'
export default {
components: {
BaseButton,
BaseIcon,
BaseInput
}
}
也可以将一些组件在vue-router的index,js中进行全局化注册
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/login.vue'
import Home from '../components/home.vue'
import welcome from '../components/welcome.vue'
import Users from '../components/user/Users.vue'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login
},
{
path: '/home',
component: Home,
// 在Home下挂载一个子路由组件
redirect: '/welcome',
children: [{ path: '/welcome', component: welcome },
{ path: '/users', component: Users }
]
}
]
});
局部注册原理差不多 就是在某一个组件的vue文件中进行Import,然后进行局部注册
记住全局注册的行为必须在根 Vue 实例 (通过 new Vue
) 创建之前发生
组件之间传值
父组件向子组件传值(prop)
- 父组件发送的形式是以属性的形式绑定值到子组件身上。
- 然后子组件用属性props接收
- 在props中使用驼峰形式,DOM模板中需要使用短横线的形式,在字符串形式的模板中没有这个限制(详细解释见之后)
<div id="app">
<div>{{pmsg}}</div>
<!--1、menu-item 在 APP中嵌套着 故 menu-item 为 子组件 -->
<!-- 给子组件传入一个静态的值 -->
<menu-item title='来自父组件的值'></menu-item>
<!-- 2、 需要动态的数据的时候 需要属性绑定的形式设置 此时 ptitle 来自父组件data 中的数据 .
传的值可以是数字、对象、数组等等
-->
<menu-item :title='ptitle' content='hello'></menu-item>
</div>
<script type="text/javascript">
Vue.component('menu-item', {
// 3、 子组件用属性props接收父组件传递过来的数据
props: ['title', 'content'],
data: function() {
return {
msg: '子组件本身的数据'
}
},
template: '<div>{{msg + "----" + title + "-----" + content}}</div>'
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
ptitle: '动态绑定属性'
}
});
</script>
prop的大小写
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
重申一次,如果你使用字符串模板,那么这个限制就不存在了。
prop 类型
到这里,我们只看到了以字符串数组形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
但是,通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
这不仅为你的组件提供了文档,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户。你会在这个页面接下来的部分看到类型检查和其它 prop 验证。
prop验证
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
为了定制 prop 的验证方式,你可以为 props
中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
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 ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data
、computed
等) 在 default
或 validator
函数中是不可用的。
类型检查 (没太看懂)
type
可以是下列原生构造函数中的一个:
String
Number
Boolean
Array
Object
Date
Function
Symbol
额外的,type
还可以是一个自定义的构造函数,并且通过 instanceof
来进行检查确认。例如,给定下列现成的构造函数:
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
你可以使用:
Vue.component('blog-post', {
props: {
author: Person
}
})
来验证 author
prop 的值是否是通过 new Person
创建的。
prop传入值的类型
字符串
像这样,你已经知道了可以像这样给 prop 传入一个静态的值:
<blog-post title="My journey with Vue"></blog-post>
你也知道 prop 可以通过 v-bind
动态赋值,例如:
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
在上述两个示例中,我们传入的值都是字符串类型的,但实际上任何类型的值都可以传给一个 prop。
数字
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post>
注意:在传入的不是字符串的时候要用v-bind来动态赋值 因为数字布尔值等等都是一个js表达式
布尔值
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
<blog-post is-published></blog-post>
<!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:is-published="false"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:is-published="post.isPublished"></blog-post>
数组
<!-- 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
对象
<!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post
v-bind:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:author="post.author"></blog-post>
传入方法
可以通过prop把父元素的方法传入到子组件中,不过要注意的是如果在方法中包含this,this指向的仍然是父元素
传入一个对象的所有属性
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind:prop-name
)。例如,对于一个给定的对象 post
:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
prop是一个单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个 prop 的情形:
-
**这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。**在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
-
**这个 prop 以一种原始的值传入且需要进行转换。**在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态,因为改变的是源地址所存储的值
替换/合并已有的属性
想象一下 `` 的模板是这样的:
<input type="date" class="form-control">
为了给我们的日期选择器插件定制一个主题,我们可能需要像这样添加一个特别的类名:
<bootstrap-date-input
data-date-picker="activated"
class="date-picker-theme-dark"
></bootstrap-date-input>
在这种情况下,我们定义了两个不同的 class
的值:
form-control
,这是在组件的模板内设置好的date-picker-theme-dark
,这是从组件的父级传入的
对于绝大多数 attribute 来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入 type="text"
就会替换掉 type="date"
并把它破坏!庆幸的是,class
和 style
attribute 会稍微智能一些,即两边的值会被合并起来,从而得到最终的值:form-control date-picker-theme-dark
。
禁用属性继承
如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false
。例如:
Vue.component('my-component', {
inheritAttrs: false,
// ...
})
这尤其适合配合实例的 $attrs
property 使用,该 property 包含了传递给一个组件的 attribute 名和 attribute 值,例如:
{
required: true,
placeholder: 'Enter your username'
}
有了 inheritAttrs: false
和 $attrs
,你就可以手动决定这些 attribute 会被赋予哪个元素。在撰写基础组件的时候是常会用到的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
注意 inheritAttrs: false
选项不会影响 style
和 class
的绑定。
这个模式允许你在使用基础组件的时候更像是使用原始的 HTML 元素,而不会担心哪个元素是真正的根元素:
<base-input
v-model="username"
required
placeholder="Enter your username"
></base-input>
单个根元素
当构建一个 `` 组件时,你的模板最终会包含的东西远不止一个标题:
<h3>{{ title }}</h3>
最最起码,你会包含这篇博文的正文:
<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>
看起来当组件变得越来越复杂的时候,我们的博文不只需要标题和内容,还需要发布日期、评论等等。为每个相关的信息定义一个 prop 会变得很麻烦:
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"
v-bind:content="post.content"
v-bind:publishedAt="post.publishedAt"
v-bind:comments="post.comments"
></blog-post>
所以是时候重构一下这个 `` 组件了,让它变成接受一个单独的 post
prop:
<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>
`
})
上述的这个和一些接下来的示例使用了 JavaScript 的模板字符串来让多行的模板更易读。它们在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用折行转义字符取而代之。
现在,不论何时为 post
对象添加一个新的 property,它都会自动地在 `` 内可用。
也就是可以直接传入post这个对象或者数组,可以使代码更加简洁
子组件向父组件传值
- 子组件用
$emit()
触发事件 $emit()
第一个参数为 自定义的事件名称 第二个参数为需要传递的数据- 父组件用v-on 监听子组件的事件
<div id="app">
<div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
<!-- 2 父组件用v-on 监听子组件的事件
这里 enlarge-text 是从 $emit 中的第一个参数对应 handle 为对应的事件处理函数
-->
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
子组件向父组件传值-携带参数
*/
Vue.component('menu-item', {
props: ['parr'],
template: `
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
### 1、子组件用$emit()触发事件
### 第一个参数为 自定义的事件名称 第二个参数为需要传递的数据
<button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>
<button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小</button>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
pmsg: '父组件中内容',
parr: ['apple','orange','banana'],
fontSize: 10
},
methods: {
handle: function(val){
// 扩大字体大小
this.fontSize += val;
}
}
});
</script>
总结;1.在组件模板上使用$emit()触发事件,第一个参数为自定义的事件名称,第二个参数是需要传递的参数
2.在父组件上用v-on监听 @后加的是组件中的自定义事件名称
3.调用mehods的方法,其中传递的参数作为方法中的第一个形参
注意事项
不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。举个例子,如果触发一个 camelCase 名字的事件:
this.$emit('myEvent')
则监听这个名字的 kebab-case 版本是不会有任何效果的:
<!-- 没有效果 -->
<my-component v-on:my-event="doSomething"></my-component>
监听这个名字的驼峰版本是不会有任何效果的:
<!-- 没有效果 -->
<my-component v-on:myEvent="doSomething"></my-component>
正确监听:
<my-component v-on:myevent="doSomething"></my-component>
不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或 property 名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on
事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent
将会变成 v-on:myevent
——导致 myEvent
不可能被监听到。
因此,我们推荐你始终使用 kebab-case 的事件名
在组件上绑定原生事件 .native
你可能有很多次想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on
的 .native
修饰符:
<base-input v-on:focus.native="onFocus"></base-input>
在有的时候这是很有用的,不过在你尝试监听一个类似 的非常特定的元素时,这并不是个好主意。比如上述
组件可能做了如下重构,所以根元素实际上是一个 label
元素:
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
这时,父级的 .native
监听器将静默失败。它不会产生任何报错,但是 onFocus
处理函数不会如你预期地被调用。
为了解决这个问题,Vue 提供了一个 $listeners
property,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
有了这个 $listeners
property,你就可以配合 v-on="$listeners"
将所有的事件监听器指向这个组件的某个特定的子元素。对于类似 `` 的你希望它也可以配合 v-model
工作的组件来说,为这些监听器创建一个类似下述 inputListeners
的计算属性通常是非常有用的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
现在 组件是一个**完全透明的包裹器**了,也就是说它可以完全像一个普通的
元素一样使用了:所有跟它相同的 attribute 和监听器都可以工作,不必再使用 .native
监听器。
.sync修饰符
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。
这也是为什么我们推荐以 update:myPropName
的模式触发事件取而代之。举个例子,在一个包含 title
prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:
this.$emit('update:title', newTitle)
然后父组件可以监听那个事件并根据需要更新一个本地的数据 property。例如:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
为了方便起见,我们为这种模式提供一个缩写,即 .sync
修饰符:
<text-document v-bind:title.sync="doc.title"></text-document>
注意带有 .sync
修饰符的 v-bind
不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’”
是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似 v-model
。
当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync
修饰符和 v-bind
配合使用:
<text-document v-bind.sync="doc"></text-document>
这样会把 doc
对象中的每一个 property (如 title
) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on
监听器。
将 v-bind.sync
用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”
,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。
在组件上使用v-model
自定义事件也可以用于创建支持 v-model
的自定义输入组件。记住:
<input v-model="searchText">
等价于:
等价于两点 1.value值等于v-model的值 2. input改变的时候 改变v-model的值
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
当用在组件上时,v-model
则会这样:
<custom-input v-model="searchText"></custom-input>
等价于
子组件通过$emit('input', $event.target.value)传值 在这边通过接收来达成子组件向父组件你传值
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event" 这个$event就是传上来的$event.target.value
></custom-input>
为了让它正常工作,这个组件内的 `` 必须:
- 将其
value
attribute 绑定到一个名叫value
的 prop 上 - 在其
input
事件被触发时,将新的值通过自定义的input
事件抛出
写成代码之后是这样的:
// 1.绑定value值 2.更新v-model
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
现在 v-model
就应该可以在这个组件上完美地工作起来了:
<custom-input v-model="searchText"></custom-input>
个人实现代码
<body>
<div id="app">
<!-- 试一下在组件内使用v-model -->
<vue-model v-model='msg'></vue-model>
<div>{{msg}}</div>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
msg: '123'
},
components: {
'vue-model': {
template: `<input type="text" v-bind:value='value'
v-on:input='$emit("input",$event.target.value)'>`,
props: ['value'],
data: function() {
return {
val: 123
}
}
},
}
})
</script>
</body>
单选复选解决方案 (代码没成功)
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的事件,但是像单选框、复选框等类型的输入控件可能会将 value
attribute 用于不同的目的。model
选项可以用来避免这样的冲突:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
现在在这个组件上使用 v-model
的时候:
<base-checkbox v-model="lovingVue"></base-checkbox>
这里的 lovingVue
的值将会传入这个名为 checked
的 prop。同时当 `` 触发一个 change
事件并附带一个新的值的时候,这个 lovingVue
的 property 将会被更新。
注意你仍然需要在组件的 props
选项里声明 checked
这个 prop。
兄弟之间的传递(不太用)
- 兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据 提供事件中心 var hub = new Vue()
- 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
- 接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
- 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
<div id="app">
<div>父组件</div>
<div>
<button @click='handle'>销毁事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
兄弟组件之间数据传递
*/
//1、 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods: {
handle: function(){
//2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('tom-event', 1);
}
},
mounted: function() {
// 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '#app',
data: {
},
methods: {
handle: function(){
//4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
动态组件
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<!-- component 内置的用于加载组件的标签-->
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
currentTabComponent 组件名
使用标签可以让组件被创建后缓存,可以只请求一次而避免重新请求,如果不使用,在失活后会被删除需要重新创建,可以避免因为反复渲染而导致的性能问题
引用场景 :点击不同的按钮 为is传递不同的组件名 显示不同的组件
<div id="app">
<button @click='change'>切换不同组件</button>
<component :is='component'></component>
</div>
<script>
let componenta = {
template: `<h1> componenta</h1>`,
data: function() {
return {
a: 123
}
}
};
let componentb = {
template: `<h1> componentb</h1>`,
data: function() {
return {
a: 123
}
}
};
let app = new Vue({
el: '#app',
data: {
component: 'componenta'
},
methods: {
change() {
if (this.component == 'componenta') {
this.component = 'componentb'
} else {
this.component = 'componenta'
}
}
},
components: {
componenta,
componentb
}
})
</script>
异步组件
加载组件的时候,以异步的形式进行加载
实现方式:在导入的时候 使用()=>来动态导入,从而返回一个promise对象
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。例如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
如你所见,这个工厂函数会收到一个 resolve
回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason)
来表示加载失败。这里的 setTimeout
是为了演示用的,如何获取组件取决于你自己。一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
你也可以在工厂函数中返回一个 Promise
,所以把 webpack 2 和 ES2015 语法加在一起,我们可以这样使用动态导入:
Vue.component(
'async-webpack-example',
// 这个动态导入会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
当使用[局部注册]的时候,你也可以直接提供一个返回 Promise
的函数:
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
异步组件加载状态
这里的异步组件工厂函数也可以返回一个如下格式的对象:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
异步组件的用处
当组件比较多或者有的组件耗时比较长的时候 要使用异步组件 可以提高其他同步组件的加载速度,提高用户的使用体验
组件传值的一些技巧 root parent children
除了传统通过poro $emit进行传值之外 还有一些技巧
子元素可以通过调用$parent的方法修改父元素的值
在子元素中 this.$parent 指向的就是父元素的vue实例
methods: {
change: function(val) {
this.$parent.theprice = '';
this.$parent.theprice = val
}
}
与 prop相比 这是个技巧
$children
父组件通过$children可以找到子组件
例如图中所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tVfPHiC7-1601186966029)(C:%5CUsers%5Casus%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20200919183211199.png)]
可以知道这是个数组 可以通过索引找到对应的组件 从而修改
$root
直接找到处于第一层的vue实例
但是不建议这样操作 因为耦合过高
全局变量
通过特定写法来挂载
Vue.prototype.名字='';
为了表明全局变量一般在名字前面加$,一般用于挂载axios,一般只建议读取全局对象而不建议修改,因为其他引用该变量的地方不会动态响应
Vue.prototype.$http=axios
组件插槽(看一下黑马视频 现在还不太懂)
-
组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
-
Vue 自定义的
<slot>
元素 -
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即
v-slot
指令)。它取代了slot
和slot-scope
这两个目前已被废弃但未被移除且仍在文档中的 attribute。 -
插槽内可以包含任何模板代码,包括 HTML ,甚至其他的组件
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
-
如果 组件 的
template
中没有包含一个<slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。 -
传递数据的方案 但是传递的是结构
-
插槽一共就三大类
1.匿名插槽(也叫默认插槽): 没有命名,有且只有一个
2.具名插槽: 相对匿名插槽组件slot标签带name命名的
3.作用域插槽: 子组件内数据可以被父页面拿到(解决了数据只能从父页面传递给子组件)
匿名插槽
<div id="app">
<!-- 这里的所有组件标签中嵌套的内容会替换掉slot 如果不传值 则使用 slot 中的默认值 -->
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript">
/*
组件插槽:父组件向子组件传递内容
*/
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
# 当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”。
# 插槽内可以包含任何模板代码,包括 HTML
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
具名插槽
与普通插槽相比 就是要在template上使用v-slot给插槽起个名字,然后在组件内部的slot上使用不同的name属性进行接收
<div id="app">
<!-- 插槽的用法 -->
<!-- 匿名插槽 -->
<!-- 剧名插槽 -->
<slot-demo>
<template v-slot:juming>
<h3>演示剧名插槽用法2</h3>
</template>
<!-- v-slot可以缩写为# 但是这样的话如果是default不能省略-->
<template #juming2>
<h3>演示剧名插槽用法2</h3>
</template>
</slot-demo>
</div>
<script>
let app = new Vue({
el: '#app',
data: {},
methode: {
},
components: {
slotDemo: {
template: `<div>
<slot name="juming"></slot>
<slot name="juming2"></slot>
</div>`,
}
}
})
</script>
一个不带 name
的 <slot>
会带有隐含的名字“default”。
作用域插槽(目的 在插槽中可以访问到子组件的内容)
插槽中可以传递参数,使得在使用插槽的时候可以访问到子组件中才有的数据
父级模板中的所有内容都是在父级作用域中进行编译
子模版中的内容在子级作用域中进行编译
在下面例子中 User属于子组件的内容
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的 子组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我们可能想在插槽中显示如下:
<current-user>
{{ user.firstName }}
</current-user>
然而上述代码不会正常工作,因为只有 子组件可以访问到 user
而我们提供的内容是在父级渲染的。
为了让 user
在父级的插槽内容中可用,我们可以将 user
作为 slot
元素的一个 attribute 绑定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
绑定在 slot
元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps
,但你也可以使用任意你喜欢的名字。
作用域插槽步骤
总结作用域插槽的步骤:
1.在组件内部定义插槽prop,即添加一个属性,属性内容是你想让父组件使用的数据
2.在插槽中 在template中将所有插槽prop的对象进行一个命名,然后使用
3.在插槽中使用 v-slot进行数据绑定
我这里直接展示 v-slot的用法
首先在子组件中
Child.vue
<slot :user="user"></slot>
export default {
data(){
return {
user:{
name:'小王'
}
}
}
}
//父组件中的用法
Parent.vue
<child>
<template v-slot="slotProps">
<h3>{{slotProps.user.name}}</h3>
</template>
//你也可以使用解构的方式
<template v-slot="{ user }">
<h3>{{user.name}}</h3>
</template>
</child>
v-slot的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
<!-- 这样会触发一个警告 因为这样所写的话default不能省略 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
后备内容
即插槽默认值,在子组件的slot中进行定义,如果没有传入插槽内容 则使用这个默认值,如果传入则使用传入的值
<button type="submit">
<slot>Submit</slot>
</button>
进入离开列表过度
在进入/离开的过渡中,会有 6 个 class 切换。
-
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。 也就是进入时所要进行的过度操作/* 进来的时候的位置 消失后点击 从1500px出现 一直移动到最开始 如果想移动到特定的位置可以使用enter-to 不过怕最后还是会返回到起始点*/ .slide-enter { transform: translateX(1500px); opacity: .5; }
-
v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 也就是在进入这个阶段的状态 -
v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。 定义进入时走到哪 但是完成后会被移除走到500px处 然后返回原点 .slide-enter-to { transform: translateX(500px); }
-
v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。 参考v-enter 不过一般不设置 -
v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 参考v-enter-active -
v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。一般情况下与v-enter一起使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKnkKjYZ-1601186966031)(file:///C:\Users\asus\AppData\Roaming\Tencent\Users\504708627\QQ\WinTemp\RichOle\LM]`B%LO2ISP29VAS~GQM90.png)]
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter
会替换为 my-transition-enter
。
v-enter-active
和 v-leave-active
可以控制进入/离开过渡的不同的缓和曲线,在下面章节会有个示例说明。
css过度
eg:
<div id="example-1">
<button @click="show = !show">
Toggle render
</button>
<transition name="slide-fade">
<p v-if="show">hello</p>
</transition>
</div>
/* 可以设置不同的进入和离开动画 */
/* 设置持续时间和动画函数 */
.slide-fade-enter-active {
transition: all .3s ease;
}
.slide-fade-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to
/* .slide-fade-leave-active for below version 2.1.8 点击移动10px 再点击从10px处回来*/ {
transform: translateX(10px);
opacity: 0;
}
new Vue({
el: '#example-1',
data: {
show: true
}
})
原理:自动识别name 然后自动追加相对应的类名 比如进入时追加 name-enter-active name-enter
.slide-fade-enter, .slide-fade-leave-to一般一起使用
/* 进来的时候的位置 消失后点击 从1500px出现 一直移动到最开始 如果想移动到特定的位置可以使用enter-to 不过怕最后还是会返回到起始点*/
.slide-enter {
transform: translateX(1500px);
opacity: .5;
}
/* 移动的时候 移动到多远 点击button移动500px */
.slide-leave-to {
transform: translateX(500px);
opacity: .5;
}
自定义过度的类名
有的时候 我们希望可以调用别人已经写好的类来实现过度效果,这样就需要使用自定义的类名
我们可以通过以下 attribute 来自定义过渡类名:
enter-class
enter-active-class
enter-to-class
(2.1.8+)leave-class
leave-active-class
leave-to-class
(2.1.8+)
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css 结合使用十分有用。
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<div id="example-3">
<button @click="show = !show">
Toggle render
</button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
通过自定义属性的方式来添加类名
这样也就是 v-enter-active就相当于 animated tada这个类
与一些第三方库一起使用比较方便
css 动画
CSS 动画用法同 CSS 过渡,区别是在动画中 v-enter
类名在节点插入 DOM 后不会立即删除,而是在 animationend
事件触发时删除。
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
列表过度
比如有的时候我们需要使用v-for来渲染一个列表,给整个列表添加过渡效果
使用 transition-grou 其余用法与transition相同
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
同时使用过度与动画
Vue 为了知道过渡的完成,必须设置相应的事件监听器。它可以是 transitionend
或 animationend
,这取决于给元素应用的 CSS 规则。如果你使用其中任何一种,Vue 能自动识别类型并设置监听。
但是,在一些场景中,你需要给同一个元素同时设置两种过渡动效,比如 animation
很快的被触发并完成了,而 transition
效果还没结束。在这种情况中,你就需要使用 type
attribute 并设置 animation
或 transition
来明确声明你需要 Vue 监听的类型。
显性的过度持续时间
在很多情况下,Vue 可以自动得出过渡效果的完成时机。默认情况下,Vue 会等待其在过渡效果的根元素的第一个 transitionend
或 animationend
事件。然而也可以不这样设定——比如,我们可以拥有一个精心编排的一系列过渡效果,其中一些嵌套的内部元素相比于过渡效果的根元素有延迟的或更长的过渡效果。
在这种情况下你可以用 <transition>
组件上的 duration
prop 定制一个显性的过渡持续时间 (以毫秒计):
<transition :duration="1000">...</transition>
你也可以定制进入和移出的持续时间:
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
动态过度
在 Vue 中即使是过渡也是数据驱动的!动态过渡最基本的例子是通过 name
attribute 来绑定动态值。
<transition v-bind:name="transitionName">
<!-- ... -->
</transition>
当你想用 Vue 的过渡系统来定义的 CSS 过渡/动画在不同过渡间切换会非常有用。
渲染函数&&JSX
Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。也就是render
可以用render来代替template,二者必有其一
<script>
let app = new Vue({
el: '#app',
data: {
msg: 123
},
components: {
renderDemo: {
render: (h, params) => {
return h('div', {
style: {
width: '100px',
height: '100px',
background: '#ccc'
}
}, '地方')
}
},
}
})
</script>
}
/* 移动的时候 移动到多远 点击button移动500px */
.slide-leave-to {
transform: translateX(500px);
opacity: .5;
}
### 自定义过度的类名
有的时候 我们希望可以调用别人已经写好的类来实现过度效果,这样就需要使用自定义的类名
我们可以通过以下 attribute 来自定义过渡类名:
- `enter-class`
- `enter-active-class`
- `enter-to-class` (2.1.8+)
- `leave-class`
- `leave-active-class`
- `leave-to-class` (2.1.8+)
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 [Animate.css](https://daneden.github.io/animate.css/) 结合使用十分有用。
```html
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<div id="example-3">
<button @click="show = !show">
Toggle render
</button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">hello</p>
</transition>
</div>
通过自定义属性的方式来添加类名
这样也就是 v-enter-active就相当于 animated tada这个类
与一些第三方库一起使用比较方便
css 动画
CSS 动画用法同 CSS 过渡,区别是在动画中 v-enter
类名在节点插入 DOM 后不会立即删除,而是在 animationend
事件触发时删除。
.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
列表过度
比如有的时候我们需要使用v-for来渲染一个列表,给整个列表添加过渡效果
使用 transition-grou 其余用法与transition相同
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
同时使用过度与动画
Vue 为了知道过渡的完成,必须设置相应的事件监听器。它可以是 transitionend
或 animationend
,这取决于给元素应用的 CSS 规则。如果你使用其中任何一种,Vue 能自动识别类型并设置监听。
但是,在一些场景中,你需要给同一个元素同时设置两种过渡动效,比如 animation
很快的被触发并完成了,而 transition
效果还没结束。在这种情况中,你就需要使用 type
attribute 并设置 animation
或 transition
来明确声明你需要 Vue 监听的类型。
显性的过度持续时间
在很多情况下,Vue 可以自动得出过渡效果的完成时机。默认情况下,Vue 会等待其在过渡效果的根元素的第一个 transitionend
或 animationend
事件。然而也可以不这样设定——比如,我们可以拥有一个精心编排的一系列过渡效果,其中一些嵌套的内部元素相比于过渡效果的根元素有延迟的或更长的过渡效果。
在这种情况下你可以用 <transition>
组件上的 duration
prop 定制一个显性的过渡持续时间 (以毫秒计):
<transition :duration="1000">...</transition>
你也可以定制进入和移出的持续时间:
<transition :duration="{ enter: 500, leave: 800 }">...</transition>
动态过度
在 Vue 中即使是过渡也是数据驱动的!动态过渡最基本的例子是通过 name
attribute 来绑定动态值。
<transition v-bind:name="transitionName">
<!-- ... -->
</transition>
当你想用 Vue 的过渡系统来定义的 CSS 过渡/动画在不同过渡间切换会非常有用。
渲染函数&&JSX
Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。也就是render
可以用render来代替template,二者必有其一
<script>
let app = new Vue({
el: '#app',
data: {
msg: 123
},
components: {
renderDemo: {
render: (h, params) => {
return h('div', {
style: {
width: '100px',
height: '100px',
background: '#ccc'
}
}, '地方')
}
},
}
})
</script>