目录
Vue 渲染
插值表达式
数据绑定最常见的形式就是使用 {{值}}
(双大括号)的文本插值:
<body>
<div id="id">
{{a}}
{{ff()}}
{{up()}}
{{xx}}
{{zz}}
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
new Vue({
el:"#id",
data:{ //属性
a:'abc',
},
methods:{ //函数
ff(){return 2+2},
up(){return this.a.toUpperCase()}
},
computed:{ //计算属性
xx(){return 2+2},
zz(){return this.a.toUpperCase()}
}
})
</script>
说明:{{}}中通常是变量, 但也可以是表达式(比如 a+b) 有返回值的函数调用(函数必须加()) 计算属性不能加()
v-text和v-html指令
如果数据不是预先定义好,而是通过网络获取时,使用{{}}
方式在网速较慢时会出现问题。在数据未加载完成时,页面会显示出原始的 {{}}
,加载完毕后才显示正确数据,称为插值闪烁。此时,可以使用v-text代替插值表达式:
v-text指令
<div id="app">
<h1 v-text="msg"></h1>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
msg:"hello vue"
}
})
</script>
如果数据中包含有HTML标签,双大括号和 v-text指令
会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html指令:
v-html指令
<div id="app">
<h1>{{msg}}</h1>
<h1 v-text="msg"></h1>
<h1 v-html="msg"></h1>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
msg:"<span style='color:red'>hello vue</span>"
}
})
</script>
v-if和v-show指令
v-if指令: v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染
当和 v-if
一起使用时,v-for
的优先级比 v-if
更高。
v-else 不需要表达式
限制:前一兄弟元素必须有 v-if 或 v-else-if
用法: 为 v-if 或者 v-else-if 添加“else 块”.
v-else-if 2.1.0 新增
限制:前一兄弟元素必须有 v-if 或 v-else-if
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
<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>
v-show指令 另一个用于根据条件展示元素的指令是 v-show
指令。和v-if
用法大致一样,不过v-show
后不能跟v-else
:
<div id="app">
<h1 v-show="show">
<span style="color:green">show=true</span>
</h1>
<h1 v-show="!show">
<span style="color:red">show=false</span>
</h1>
<button onclick="handleClick()">点我</button>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
show:true
}
})
function handleClick(){
vm.show = !vm.show;
}
</script>
v-if 和 v-show 的区别:
<div id="app">
<h1 v-if="show">
<span style="color:green">v-if指令</span>
</h1>
<h1 v-show="show">
<span style="color:green">v-show指令</span>
</h1>
<button onclick="handleClick()">点我</button>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
show:true
}
})
function handleClick(){
vm.show = !vm.show;
}
</script>
v-if 和 v-show 指令展示效果相同,但是打开开发者工具(F12或者ctrl+shift+i)查看Element面板,会发现2者的区别
-
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的子组件适当地被销毁和重建。当初始条件为false时,其内部组件不会渲染。 -
v-show
就简单得多,它在切换过程中,只是简单地基于 CSS 进行切换。当初始条件为false时,其内部组件也会预先渲染。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
Vue v-for
列表渲染
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
<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' }
]
}
})
结果:
Foo
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' }
]
}
})
你也可以用 of
替代 in
作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
在v-for里使用对象 你也可以用 v-for
来遍历一个对象的 property。
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
你也可以提供第二个的参数为 property 名称 (也就是键名):
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
还可以用第三个参数作为索引:
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
v-for时的key属性
当我们需要更新 v-for 渲染过的元素列表时,比如向数组中添加一个新的元素。为了保证Vue正确并高效的渲染页面,官方建议我们为每项提供一个唯一 key
属性,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。key一般使用在遍历完后,又增、减数组的元素的时候更有意义。
<ul>
<li v-for="(item,index) in items" :key="index"></li>
</ul>
-
这里使用了属性赋值语法:
:key="index"
为每一个生成的元素设置一个唯一的key属性 -
这里绑定的key是数组的索引,应该是唯一的
Vue 事件绑定 v-on指令
在Vue中通过v-on
指令给页面元素绑定事件。
v-on:事件名="js代码片段或函数" //事件名=事件属性-on 比如:click=onclick-on
简单写法
@事件名="js代码片段或函数"
ES事件
标签的事件属性
所有标签都有的事件属性:
单击事件 onclick(重要)
双击事件 ondblclick
鼠标移入事件 onmouseover(重要)
鼠标移出事件 onmouseout(重要)
鼠标按下事件 onmousedown
鼠标弹起事件 onmouseup
鼠标移动事件 onmousemove
body标签
页面加载完毕事件 onload//通常在该事件中定义只需要执行1次的代码
form标签相关事件属性[重点]:
input标签的事件:
获取焦点事件 onfocus(重要)
失去焦点事件 onblur(重要)
值发生改变事件 onchange(重要)
form标签的事件:
表单提交事件 onsubmit(重要)
Vue 事件修饰符
.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 (子元素的事件冒泡来的不执行)-->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
注意: 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
不要把 .passive
和 .prevent
一起使用,因为 .prevent
将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive
会告诉浏览器你不想阻止事件的默认行为。
Vue 标签属性的赋值:v-bind指令
无法直接在标签的属性中使用插值表达式动态的设置标签的属性值,这时候就需要使用v-bind
指令。语法如下:
不过,v-bind
太麻烦,因此可以省略,直接写成:
,:属性名="属性值"
,即: <img :src="imgsrc" :width="abc" />
<div id="app">
<img v-bind:src="imgsrc" v-bind:width="abc" />
<img :src="imgsrc" :width="abc" />
</div>
<!--引入vue文件-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
const v=new Vue({
el:"#app",
data:{
imgsrc:"img/nane.gif",
abc:100
}
});
</script>
绑定class属性的对象语法
我们可以传给 `v-bind:class` 一个对象,以动态地切换 class:
<div id="app">
<div v-bind:class="{red_bg:showRed,green_bg:!showRed}">点击按钮改变背景样式</div>
<button @click="showRed=true">红色</button>
<button @click="showRed=false">绿色</button>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
showRed:true
}
})
</script>
Vue JSON对象拷贝
obj2 复制 obj1 的数据
obj2 = JSON.parse(JSON.stringify(obj1)) obj1是需要拷贝的对象
优点:obj1 与 obj2都是独立的对象,对obj2对象操作,对obj1没有影响
Vue 表单的双向数据绑定:v-model指令
之前我们演示的数据绑定都是单向绑定,数据影响了视图渲染,但是反过来就不行(视图的变化不会影响模型数据)。如下例所示:
<div id="app">
<input type="text" :value="name"> <br>
<button @click="setName">点我设置value</button>
<button @click="getName">点我获取value</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
const vm = new Vue({
el:"#app",
data:{
name:""
},
methods:{
setName(){
this.name="xiaohei";
},
getName(){
alert(this.name);
}
}
})
</script>
而对于表单中的控件而言,我们需要数据的绑定是双向的,即:模型数据的变化会影响视图,同时视图发生变化也会同步到模型数据。
接下来学习的v-model
是双向绑定,视图(View
)和模型(Model
)之间会互相影响。既然是双向绑定,一定是在视图中可以修改数据的组件,这样就限定了视图的元素类型。
目前v-model
的可使用元素有:
-
input
-
radio
-
checkbox
-
select
-
textarea
-
components(Vue中的自定义组件)
v-model本质上是一个语法糖。如下代码<input v-model="test">
本质上是<input :value="test" @input="test = $event.target.value">
,其中@input是对<input>输入事件的一个监听:value="test"是将监听事件中的数据放入到input,下面代码是v-model的一个简单的例子。在这边需要强调一点,v-model不仅可以给input赋值还可以获取input中的数据,而且数据的获取是实时的,因为语法糖中是用@input对输入框进行监听的。
基本上除了最后一项,其它都是表单的输入项。 首先我们先看一个简单的示例:
<div id="app">
<input v-model="test">
<input :value="test" @input="test= $event.target.value"> 语法糖
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
new Vue({
el: '#app',
data: {
test: '这是一个测试'
}
});
</script>
Vue 过滤器
过滤器的作用是对数据进行格式化,比如字母全部大写、日期格式化输出、货币前添加货币符号等场景。Vue.js支持在{{ }}插值的尾部添加一个管道符“(|)”对数据进行过滤。过滤的规则,通过给Vue实例添加选项filters来设置。
<div id="app">
<!-- 过滤器只有1个参数时,过滤器不需要写出(),默认传入|前的数据 -->
<h2>使用过滤器字母全大写:{{str | toUpperCase}}</h2>
<h2>使用过滤器日期格式化:{{date | dateFormat}}</h2>
<!-- 过滤器有多个参数时,第1个参数仍然是|前的数据,后续的参数需要显式给出 -->
<h2>货币格式化输出:{{money | moneyFormat("¥")}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
const vm = new Vue({
el:"#app",
data:{
str:"hello filter",
date:new Date(),
money:1000.0
},
filters:{
toUpperCase(value){
return value.toUpperCase();
},
dateFormat(value){
let year = value.getFullYear();
let month = value.getMonth();
let day = value.getDay();
let hours = value.getHours();
let seconds = value.getSeconds();
let minutes = value.getMinutes();
return `${year}-${month}-${day} ${hours}:${seconds}:${minutes}`;
},
moneyFormat(value,symbol){
return symbol+" "+value;
}
}
})
</script>
说明:还可以将过滤器定义为全局过滤器。
Vue.filter('过滤器名称', function (value[,param1,...] ) {
//逻辑代码
})
Vue 钩子函数
每个 Vue 实例在被创建时都要经过一系列的初始化过程。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这些函数在生命周期的不同阶段自动触发执行,这给了用户在不同阶段添加自己的代码的机会。
生命周期钩子 | 含义 |
---|---|
beforeCreate(vue对象创建前) | 组件实例刚被创建,组件属性计算之前,此时不能访问属性 |
created(创建后) | 组件实例创建完,属性可以访问,但是还不能通过 $el 访问DOM |
beforeMount(加载前) | 模板编译、挂载之前,可以通过 $el 访问渲染前的DOM |
mounted(载入后) | 模板编译、挂载之后,可以通过 $el 访问渲染前的DOM |
beforeUpdate(更新前) | 组件更新之前,可以获取组件更新前的DOM |
updated(更新后) | 组件更新之后,可以获取组件更新后的DOM |
beforeDestroy(销毁前) | 组件销毁前调用 |
destroyed(销毁后) | 组件销毁后调用 |
<div id="app">
<h1>{{msg}}</h1>
<button @click="changeMsg">点我修改msg</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
const vm = new Vue({
el:"#app",
data:{
msg:"hell vue"
},
methods:{
changeMsg(){
this.msg = "hello baizhi";
}
},
beforeCreate:function(){
// alert("beforeCreate...");
console.log("beforeCreate...");
console.log(this.$el);//undefined
console.log(this.msg);//undefined
},
created:function(){
// alert("created...");
console.log("created...");
console.log(this.$el);//undefined
console.log(this.msg);//hello vue
},
beforeMount:function(){
// alert("beforeMount...");
console.log("beforeMount...");
console.log(this.$el);//加载前的标签,就是原始代码,插值表达式、事件绑定都还没解析
console.log(this.msg);//hello vue
},
mounted:function(){
// alert("mounted...");
console.log("mounted...");
console.log(this.$el);//加载后的标签,插值表达式、事件绑定均已解析
console.log(this.msg);//hello vue
},
beforeUpdate:function(){
// alert("beforeUpdated...");
console.log("beforeUpdated...");
console.log(this.$el.innerHTML);//更新前的DOM
console.log(this.msg);//hello baizhi
},
updated:function(){
// alert("updated...");
console.log("updated...");
console.log(this.$el.innerHTML);//更新后的DOM
console.log(this.msg);//hello baizhi
},
beforeDestroy:function(){//在console中执行vm.$destroy()触发,开发时很少关注
// alert("destroyed...");
console.log("beforeDestroy...");
console.log(this.$el);
console.log(this.msg);
},
destroyed:function(){
// alert("destroyed...");
console.log("destroyed...");
console.log(this.$el);
console.log(this.msg);
}
})
</script>
说明:一般地,我们会在 created
钩子函数中,从服务端获取数据,并对数据进行初始化。
Vue 组件
全局注册
全局注册的组件,可以在不同的Vue实例中使用。语法如下:
<script>
Vue.component("组件名",{
template:"复用的html片段",
data:function(){
return {...}//return的对象,类似于创建Vue实例时的data
},
methods:{
//和定义Vue实例时一样,用于定义函数
}
})
</script>
局部注册
一旦全局注册,就意味着即便以后你不再使用这个组件,它依然会随着Vue的加载而加载。因此,对于一些并不频繁使用的组件,我们会采用局部注册。在Vue实例中添加选项 components
语法如下
<script>
const vm = new Vue({
el:"选择器",
data:{
//属性
},
components:{
//注册局部组件
"组件名":{
template:"复用的html片段",
data:function(){
return {...}//return的对象,类似于创建Vue实例时的data
},
methods:{
//和定义Vue实例时一样,用于定义函数
}
}
}
});
</script>
注册优化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<template id="temp">
<h1>dafjoiddf</h1>
</template>
<template id="temp2">
<h1>dafjoiddf</h1>
</template>
<div id="id">
<aaa></aaa>
<bbb></bbb>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script>
const temp2 = {
template: "#temp2",
}
new Vue({
el: "#id",
components: {
aaa: {
template: "#temp"
},
bbb: temp2,
}
})
</script>
</html>