原生html渲染v-html
有时数据中携带了Html标签,输出它们时,按文本解析,如:
new Vue({
data: {
title:'<h3>使用<code>v-html</code>输出原生的html</h3>',
}
}).$mount('#app-1')
<div id="app-1" >
<div>{{title}}</div>
</div>
使用原生指令v-html
解析数据中标签为原生html
<div id="app-1" >
<div v-html="title"></div>
</div>
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
如:
new Vue({
data: {
title:'<h3>使用<code>v-html</code>输出原生的html</h3>',
recieveMessage:'',
}
}).$mount('#app-1')
<div id="app-1" >
<div v-html="title"></div>
<input type="text" style="width:300px" v-model="recieveMessage"/>
<div>
<output v-html="recieveMessage"></output>
</div>
</div>
使用时要注意这种情况。插入<script>
几乎可以做任何事情,还有插入无数<img>
占用带宽。
条件渲染v-if、v-show
当v-if
条件为truthy时,动态插入内容。
比如在晚7点到早上7点我们插入以下内容:
new Vue({
computed: {
isNight() {
return new Date().getHours() < 7 || new Date().getHours() > 19
}
}
}).$mount('#app-2')
<div id="app-2">
<div v-if="isNight" style="font-size:30px;color:yellow;background: black;width:300px">
? it's night
</div>
</div>
也可以使用v-show="isNight"
显示和隐藏内容
<div id="app-2">
<div v-show="isNight" style="font-size:30px;color:yellow;background: black;width:300px">
? it's night
</div>
</div>
两者区别为:v-show
元素始终存在,通过display控制显示/隐藏。而v-if
的转换要经历组件的销毁和重建。使用v-show
初始会有开销,而v-if
初始条件若为falsy,那么什么也不会做,节省用户的 CPU 时间。但是如果v-if
频繁切换条件,那么开销又比v-show
大的多。
可以用v-else
和v-else-if
作分支,注意先后顺序,顺序错误则无效。
<div v-else style="font-size:30px;color:orange;background: lightblue;width:300px">
? it's day
</div>
<template>
上面都在外包元素<div>
上使用条件指令,如果不想使用可见元素包裹内容,可以使用<template>
标签
<template v-if="isNight">
<span>?</span>
<span>it's night</span>
</template>
类似其他语言{}
的作用
if(isNight){
//...
}
key
以上,若v-if
新插入部分与被移除部分有元素是相似的,那么为了效率这些元素会被复用,不会重新渲染。
new Vue({
el:'#app-3',
data: {
loginType: 'username'
},
methods: {
troggle(){
this.loginType = this.loginType==='username'?'email':'username'
}
}
})
<div id="app-3">
<dl>
<dt>key</dt>
<dd v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</dd>
<dd v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</dd>
<dd>
<button @click="troggle">switch</button>
</dd>
</dl>
</div>
input
和label
俩元素本身没有被替换,因此input
中输入的内容在切换时会保留,而input
的 placeholder 和label
的内容被替换。
如果使用key
特性对元素作唯一标识,那么该元素必备替换。
<div id="app-3">
<dl>
<dt>不使用key</dt>
<dd v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username">
</dd>
<dd v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email">
</dd>
<dd>
<button @click="troggle">switch</button>
</dd>
</dl>
</div>
设置key
后,切换内容时input
的值不在保留。key
主要在v-for
里使用,以区分列表项,还有动画部分会大量使用,因为元素复用导致淡入淡出动画失效。
列表渲染 v-for
列表使用细节比较杂碎,按官网教程一个个试下就行了。
一般情况下用法v-for="列表项 in|of 列表|整数"
如
<h3>以一个整数进行遍历</h3>
<span v-for='n in 10'> {{n}} </span>
<h3>票房</h3>
<ul>
<li v-for="bo of boxOffice">
{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
</li>
</ul>
new Vue({
el: '#app-4',
data: {
query: '',
boxOffice: [
{ name: 'Avatar', year: 2009, sells: '27.88' },
{ name: 'Frozen', year: 2013, sells: '12.765' },
{ name: 'Furious 7', year: 2015, sells: '15.15' },
{ name: 'Iron Man 3', year: 2013, sells: '12.154' },
{ name: 'Titanic', year: 1997, sells: '21.868' },
{ name: 'Spectre', year: 2015, sells: '8.722' },
{ name: 'Inception', year: 2010, sells: '8.255' },
{ name: 'Jurassic World', year: 2015, sells: '16.99' }
]
}
})
以上的boxOffice
可以是个数组,当然我们也可以遍历一个对象的值。如给电影 Avatar 加一个导演属性
{
name: 'Avatar',
year: 2009,
sells: '27.88',
director:{firstname:'yannie',lastname:'cheung',age:'17'}
},
在列表中嵌套一个列表显示导演信息
<li v-for="bo of boxOffice">
{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
<ul>
<li v-for="value of bo.director">{{value}}</li>
</ul>
</li>
在列表遍历中还可以引用列表索引,对象的遍历中引用键值与索引,索引从0开始。
列表: (列表项,索引) in 列表
对象: (值,键值,索引) in 对象
修改示例以显示这些内容
<ul>
<li v-for="(bo,index) of boxOffice">
{{index+1}}、{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
<ul>
<li v-for="(value,key,index) of bo.director">{{index+1}}、{{key}}:{{value}}</li>
</ul>
</li>
</ul>
在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。
v-for中的key
与v-if
类似的,当列表被更新时,这些已被渲染的列表元素不会重新渲染,只会改变其中的内容。我们修改以上示例,点击按钮列表会按票房重新排序,我们再为每一个li
加上动画。
data(){
//...
isSort:false
},
//......
//点击按钮,在原顺序与票房顺序切换
computed: {
//点击按钮,在原顺序与票房顺序切换
sortBySells(){
var newArr = this.boxOffice.concat()
if(this.isSort){
return newArr.sort((b1,b2) => b1.sells - b2.sells)
}else{
return this.boxOffice
}
},
//...
}
<h3>票房</h3>
<ul>
<!-- 测试基本功能 -->
<!-- <li v-for="(bo,index) of boxOffice"> -->
<!-- 测试特性key -->
<li v-for="(bo,index) in sortBySells" class="animate">
{{index+1}}、{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
<ul>
<li v-for="(value,key,index) of bo.director">{{index+1}}、{{key}}:{{value}}</li>
</ul>
</li>
</ul>
.animate {
display: block;
margin-bottom: 10px;
color: #fff;
background: #2e8b57;
border-radius: 3px;
-webkit-animation: animate 5s infinite;
animation: animate 5s infinite;
}
@keyframes animate {
0% {
width: 350px;
}
to {
width: 600px;
}
}
当列表重新排列时,动画没有中断重新开始,说明这些<li>
元素没有被重新渲染,它们被复用了。
现在为列表加上key
<li v-for="(bo,index) in sortBySells" :key="bo.sells" class="animate">
设置key
后,某些元素的动画中断并重新开始,说明元素的确被重新渲染了。我们设置报价为key
的值,如果设置index
为值,列表还是会被完全的复用。
对遍历列表、对象的处理
在以上key
的使用中已经看到我们对列表进行的排序是在计算属性中进行的,这种方法的形式一般为这样:v-for="项 in 计算属性"
虽然处理列表首选计算属性,但也可以使用方法,特别是在计算属性不适用的情况下。v-for="项 in 方法(参数)"
改写以上示例,使用方法对列表进行排序,只为演示,最优还是使用计算属性
methods: {
//点击按钮,在原顺序与票房顺序切换
sort(isSort) {
var newArr = this.boxOffice.concat()
return isSort ? newArr.sort((b1, b2) => b1.sells - b2.sells) : this.boxOffice
}
}
<li v-for="(bo,index) in sort(isSort)" :key="bo.sells" class="animate">
效果与使用计算属性一样。
变异方法和非变异方法:以上对列表使用了sort()
方法,这个方法会改变原始数组,所以我们用concat()
复制出一份,不改变原数据。这里sort()
为变异方法,concat()
为非变异方法。
filter()
是一个非变异方法,比如我们过滤出2010年前的票房大于10亿的电影
computed: {
//...
//2010之前票房大于10亿的电影,调用两个方法
filterBoxOffice() {
return this.highSells(this.beforeYear(this.boxOffice))
},
//...
},
methods: {
//...
beforeYear(list) {
return list.filter(bo => bo.year <= 2010)
},
highSells(list) {
return list.filter(bo => bo.sells <= 10.0)
},
}
<ul>
<li v-for="bo in beforeYear(highSells(boxOffice))">
<!-- <li v-for="bo in filterBoxOffice"> -->
{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
</li>
</ul>
如上,可以使用一个计算属性返回过滤结果,也可以直接连续调用两个方法。
又比如,我们根据文本输入来进行过滤。
<h3>过滤</h3>
<input type="text" v-model="query" />
<ul>
<li v-for="bo in queryBo">
{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
</li>
</ul>
由于在filter()
里不能使用this
引用 vue 实例,因此在外部先把它赋给变量 vm
queryBo() {
var vm = this
var noblank = this.boxOffice.filter(item => Number(vm.query) === item.year)
return this.query ? noblank : this.boxOffice
}
Vue.set
以下两种情形的数据变化,vue无法再视图中作出响应
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
如添加以下代码,点击按钮修改
modifyItem(){
this.boxOffice[0] = { name: 'The Last Jedi', year: 201
![clipboard.png](/img/bV8201)
7, sells: '13.32', director: { firstname: 'yannie', lastname: 'cheung', age: '17' } }
this.boxOffice.length = 5
}
<button @click="modifyItem" style="margin-left:20px">vue.set test</button>
没有任何响应,但事实上值已经更新,
不过设置某索引对应对象的属性,时会有响应的
modifyItem(){
//无响应
this.boxOffice[0] = { name: 'The Last Jedi', year: 2017, sells: '13.32', director: { firstname: 'yannie', lastname: 'cheung', age: '17' } }
this.boxOffice.length = 5
//有响应
this.boxOffice[1].name = 'somename'
}
索引值和长度的改变是因为其他改变(this.boxOffice[1].name = 'somename')触发了响应,而并非他们自身是响应式的。
为了解决这个问题提供了两种:使用变异方法splice()
和Vue.set
//设置
modifyItem(){
//无响应
// this.boxOffice[0] = { name: 'The Last Jedi', year: 2017, sells: '13.32', director: { firstname: 'yannie', lastname: 'cheung', age: '17' } }
// this.boxOffice.length = 5
//无响应的解决方法
// this.$set 等价于 Vue.set
Vue.set(this.boxOffice, 0, { name: 'The Last Jedi', year: 2017, sells: '13.32', director: { firstname: 'yannie', lastname: 'cheung', age: '17' }})
// this.boxOffice.splice(0, 1, { name: 'The Last Jedi', year: 2017, sells: '13.32', director: { firstname: 'yannie', lastname: 'cheung', age: '17' }})
//修改长度
this.boxOffice.splice(5)
//有响应
// this.boxOffice[1].name = 'somename'
}
此时,点击按钮设置新状态,立刻作出响应。
对于对象而言,属性的添加或删除无法响应,如
//添加对象属性
modifyObject(){
this.boxOffice[0].director.sex = 'male'
}
可以使用Vue.set
解决这个问题
或者将对象原先属性和新增属性合并成新对象再赋给该对象,这使我们可以添加多个属性,如:
//methods
//添加对象属性
modifyObject(){
// 无响应
// this.boxOffice[0].director.sex = 'male'
//解决方法
Vue.set(this.boxOffice[0].director, 'sex', 'male')
this.boxOffice[0].director = Object.assign({}, this.boxOffice[0].director,{
aliasname:'kanzaki urumi',
graduation: 'NUIST'
})
}
template
同 v-if,包裹多个元素,下节有使用到。
v-if、v-for优先级
v-for 的优先级比 v-if 高。就是说可以向下面这样,先展开所有列表项,之后 v-if 将作用于每个列表项。
为影片添加是否上映属性run
boxOffice: [
{ name: 'Avatar', year: 2009, sells: '27.88',run:true, director: { firstname: 'yannie', lastname: 'cheung', age: '17' } },
{ name: 'Frozen', year: 2013, sells: '12.765',run:false },
{ name: 'Furious 7', year: 2015, sells: '15.15',run:true },
{ name: 'Iron Man 3', year: 2013, sells: '12.154',run:false },
{ name: 'Titanic', year: 1997, sells: '21.868',run:false },
{ name: 'Spectre', year: 2015, sells: '8.722',run:true },
{ name: 'Inception', year: 2010, sells: '8.255',run:false },
{ name: 'Jurassic World', year: 2015, sells: '16.99',run:false }
]
<h3>v-for|if 的优先级</h3>
<ul>
<li v-for="bo in boxOffice" v-if="bo.run">
{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
</li>
</ul>
v>
如果想设置在某条件下循环是否执行,那么可以在外层加上 v-if
<h3>v-for|if 的优先级</h3>
<ul>
<template v-if="boxOffice.length">
<li v-for="bo in boxOffice" v-if="bo.run">
{{bo.name}} {{bo.year}} ({{bo.sells}}$ )
</li>
</template>
<li v-else>没有影片</li>
</ul>
组件上的v-for
和在原生元素上使用一样,只是必须使用 key
new Vue({
data: {
boxOffice: [
{ id:1,name: 'Avatar', year: 2009, sells: '27.88', run: true, director: { firstname: 'yannie', lastname: 'cheung', age: '17' } },
{ id:2,name: 'Frozen', year: 2013, sells: '12.765', run: false },
{ id:3,name: 'Furious 7', year: 2015, sells: '15.15', run: true },
{ id:4,name: 'Iron Man 3', year: 2013, sells: '12.154', run: false },
{ id:5,name: 'Titanic', year: 1997, sells: '21.868', run: false },
{ id:6,name: 'Spectre', year: 2015, sells: '8.722', run: true },
{ id:7,name: 'Inception', year: 2010, sells: '8.255', run: false },
{ id:8,name: 'Jurassic World', year: 2015, sells: '16.99', run: false }
]
},
components: {
'child': {
props:['bo'],
template: '<li>{{bo.id}}、{{bo.name}}</li>'
}
}
}).$mount('#app-5')
<div id="app-5">
<child v-for="bo in boxOffice" :bo="bo" :key="bo.id"></child>
</div>
更多内容参见组件与单文件组件部分
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。