vue
vue2基础语法
1.初识vue
引入vue
<!-- 引入vue -->
<script type="text/javascript" src="../js/vue.js">
</script>
1.先准备一个容器
容器的作用:1.为vue提供模板 2.告诉vue将解析好的成果往哪里放
容器中的{{}}括号里要写js表达式,并且可以读到vue实例中的所有属性
所谓js表达式,就是返回一个具体值的特殊的js语句
<div id="id1">
<h1>hello,{{name}}</h1>
</div>
<div id="id2">
<h1>hello,{{name}}</h1>
</div>
注:vue代码要写在
2.创建vue实例
vue实例必须要传入一些配置对象
容器和vue实例是一一对应的,一个vue实例只能对应接管一个容器
真实开发中只有一个vue实例,并且会配合着组件一起使用
一旦data中的数据发生修改,那么模块中所有用到该数据的地方都会自动修改
new Vue({
el:'#id1',
data:{
name:"world"
}
})
new Vue({
el:"#id2",
data:{
name:"南工"
}
})
注:
el指定当前vue实例为哪个容器服务,通常为css选择器字符串
data用于存储数据,数据供el所指定的容器去使用,值暂时先写成对象
2.vue语法
差值语法:用于解析标签体的内容
指令语法:用于解析标签(包括标签属性,标签体内容,绑定事件等等)
因为在vue中有好多种指令语法,这里只是使用v-bind来演示,用于解析标签属性
<div id="div1">
<h1>差值语法</h1>
<h3>hello,{{name}}</h1>
<hr>
<h1>指令语法</h1>
<a v-bind:href="person.url">点一下,{{person.name}}1</a>
<a :href="person.url">点一下,哈哈哈2</a>
</div>
注:对于指令语法种的v-bind,可以简写成:,英文的冒号
new Vue({
el:"#div1",
data:{
//data中设计多层级结构
name:"jack",
person:{
name:"来喽来喽",
url:"http://www.baidu.com"
}
}
})
3.数据绑定
数据绑定分为单向数据绑定和双向数据绑定
<div id="div1">
单向数据绑定:<input type="text" v-bind:value="name"><br>
双向数据绑定:<input type="text" v-model:value="name"><br>
</div>
单向数据绑定:data中数据的改变会引起模板中内容的变化,但是模板中内容的改变不会引起data中数据的改变
双向数据绑定:data中数据的改变会引起模板中内容的变化,模板中内容的改变也会引起data中数据的变化
v-model只能用在表单类或者输入类元素上
v-model:value可以简写成v-model,因为v-model默认的找的属性就是value
new Vue({
el:"#div1",
data:{
name:"我最棒"
}
})
4.el与data的两种写法
el的两种写法
第一种
new Vue({
el:"#div1",
data:{
name:"南京"
}
})
第二种——使用x承接vue,使用$mount进行挂载
var x= new Vue({
data:{
name:"南京"
}
})
console.log(x);
x.$mount('#div1');
data的两种写法
第一种——对象式
new Vue({
el:"#div1",
data:{
name:"南京"
}
})
第二种——函数式
new Vue({
el:"#div1",
data(){
console.log('@@@',this)//此处的this就是指vue实例
return{
name:"南京"
}
}
})
注:使用函数式时,必须要有返回值
5.MVVM模型
MVVM
M:model 模型,data中的数据
V:view 视图,模板代码
VM:viewmodel 视图模板,vue实例
通过观察发现:
1.data中所有的数据,到最后都出现在vue(vm)身上了
2.vue身上所有的属性,及vue原型上的所有属性,在vue模板中都可以直接使用
6.数据代理
(1)回顾object.defineProperty方法
var number=18
var person={
name:"张三",
sex:"男"
}
现在有需求,想在person中添加age属性,使age和number之间有一座桥梁可以沟通
就要通过defineProperty里的get和set函数
往一个对象上添加一个属性
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true,设置属性可以枚举,默认值为false
// writable:true,设置属性可以修改,默认值为false
// configurable:true 控制属性可以被删除
//当有人读取person中的age属性时,get函数(getter)就会被调用,返回值就是age属性的值
get(){
console.log('有人读取属性了')
return number
},
//当有人修改person中的age属性时,set函数(setter)就会被调用,且收到修改的值
set(value){
console.log('有人修改属性了')
number = value
}
})
注:get方法是在有人读取到对象中的属性时,get方法会被调用,返回值就是读取属性的值
set方法是在有人修改对象中的属性时,set方法会被调用,且收到修改的值
object.defineProperty方法详解
Object.defineProperty()方法是给对象添加属性用的
Object.defineProperty(object, propertyname, descriptor);
其中三个参数分别是:
object:对象
propertyname:属性名
descriptor:要添加的属性值
let person = {
name: '张三',
sex: '男'
}
Object.defineProperty(person, 'age', {
value: 18, //这个age不能被枚举(不能遍历)
})
console.log(person);
输出的结果如下:
这个age和name、sex的颜色是不一样的,它不能被枚举(不能遍历)。
Object.defineProperty()方法添加的属性默认是不能修改、删除、遍历,但可以通过修改一些默认值设置是否可修改、删除、遍历。
(2)何为数据代理
数据代理就是通过一个对象代理对另一个对象中属性的操作
通过object2代理object1,往object2身上添加object1身上的属性
<script>
var obj={x:100}
var obj2={y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x=value
}
})
</script>
(3)vue中的数据代理
vue中的数据代理,通过vm对象来代理data对象中的属性操作,包括读写
数据代理的好处:更加方便地操作data中的数据
基本原理:
通过object.defineProperty方法将data中的所有属性都添加到vm上
为每一个添加到vm上的属性都添加一个setter和getter
在setter和getter内部去操作data中的数据
vm操作data中的数据,将data中的数据全部封装到_data中,并且加上get和set方法,就是所谓的数据代理
<div id="div1">
<h1>hello,{{name}}</h1>
<h1>{{address}}</h1>
</div>
<script>
var vm =new Vue({
el:"#div1",
data:{
name:"南京",
address:"南京工业职业技术大学"
}
})
console.log(vm)
</script>
7.事件处理
1.事件的基本使用
绑定点击事件
<body>
<div id="div1">
<h1>欢迎来到{{name}}学习</h1>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<!-- v-on:click也有简写方式 -->
<button @click="showInfo1">点我提示信息1</button>
<button @click="showInfo2(66,$event)">点我提示信息2</button>
</div>
<script>
new Vue({
el:"#div1",
data:{
name:"南京"
},
methods:{
showInfo1(event){
alert('同学你好!')
},
showInfo2(number,event){
alert('同学你好!!')
console.log(number,event)
}
}
})
</script>
</body>
注:
如果想向函数里传参数,但是如果单纯的传参数,那么会把event搞丢,于是用个关键字占位
当使用vue写触发函数的时候,函数也要写在vue实例中,使用methods属性
不是所有的触发函数都需要给event占位
2.vue中的事件修饰符
1.prevent阻止默认事件(常用)
2.stop阻止事件冒泡
3.once事件只触发一次
4.capture使用事件的捕捉模式
5.self 只有当event.target是当前操作的元素才触发事件
6.passive 事件的默认行为立即执行,无需等待事件回调执行完毕
<div id="div1">
<h1>欢迎来到{{name}}</h1><br>
<a href="http://www.niit.edu.cn" @click.prevent.stop="showInfo">点我一下</a>
</div>
<script>
new Vue({
el:"#div1",
data:{
name:"南京"
},
methods:{
showInfo(){
alert('同学你好')
}
}
})
</script>
注:
修饰符是可以连着写的
@click.prevent.stop这样写就代表先阻止默认事件再停止冒泡
3.键盘事件
(1)vue中常用的按键别名:
1.回车——> enter
2.删除——> delete
3.退出——> esc
4.空格——> space
5.换行——> tab(特殊:必须配合keydown去使用,因为tab本身会失去焦点,使用keyup达不到目的)
6.上——> up
7.下——> down
8.左——> left
9.右——> right
(2)vue未提供别名的按键,可以使用按键原始的key值去绑定,但要注意转为kebab-case(短横线命名)
(3)系统修饰键(用法特殊)
ctrl alt shift meta
【1】配个keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才会被触发
【2】配合keydown使用:正常触发事件
【3】可以使用keycode去指定具体的按键(不推荐)
(4) Vue.config.keyCodes.自定义键名= 键码, 可以去定制按键别名
<div id="div1">
<input type="text" placeholder="按下回车提示输入" @keyup.huiche="showInfo">
<br>
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
<br>
<!-- 对于现在有需求,只有按下ctrl+y时才能提示 -->
<!-- 对于系统修饰符,也可以连着写,后面再带一个就表示组合使用时才有效 -->
<input type="text" placeholder="按下ctrl+y提示输入" @keyup.ctrl.y="showInfo">
</div>
<script>
//创建个别名
Vue.config.keyCodes.huiche=13,
new Vue({
el:"#div1",
data:{
name:"南京"
},
methods:{
showInfo(event){
console.log(event.target.value)
}
}
})
</script>
8.计算属性
1.使用差值语法实现姓名案例
<div id="div1">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{firstName}}-{{lastName}}</span>
</div>
<script>
new Vue({
el:"#div1",
data:{
firstName:"张",
lastName:"三"
}
})
</script>
注:
data域中的数据发生变化,vue一定会重新解析模板,并且这里用到的v-model双向绑定
2.使用methods实现姓名案例
<div id="div1">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName()}}</span>
</div>
<script>
new Vue({
el:"#div1",
data:{
firstName:"张",
lastName:"三"
},
methods:{
fullName(){
//因为这里的this代表的是vue实例,所以可以直接获取data域中的数据
return this.firstName+"-"+this.lastName
}
}
})
</script>
注:
对于函数使用差值语法调用,如果不加括号就是在页面上显示函数内容,加了括号显示函数返回值
3.使用计算属性实现姓名案例
什么叫属性,data域中定义的就叫做属性,例如:firstName就叫做属性,张就叫做属性值
计算属性的概念:利用现有属性,通过拼拼凑凑使之成为一个全新的属性就叫做计算属性
data域中的属性会出现在_data中,但是计算属性中的属性不会出现在下划线data中
<div id="div1">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
var vm= new Vue({
el:"#div1",
data:{
firstName:"张",
lastName:"三"
},
computed:{
fullName:{
get(){
return this.firstName+'-'+this.lastName
},
set(value){
//将value按照-来拆分成两个部分
var arr = value.split("-");
this.firstName=arr[0]
this.lastName=arr[1]
}
}
//计算属性也存在简写形式
//如果要是想使用计算属性的简写形式就必须确定不会用到set方法
// fullName(){
// return this.firstName+'-'+this.lastName
// }
}
})
</script>
注:
和defineProperty中的getter类似,只要有人读取了fullname,get就会被调用,返回值就作为fullname的值
get函数何时调:1.初次使用fullname时会调用get函数 2.当get函数中所依赖的属性发生改变时,会调用get函数(因为计算属性有缓存的存在,为了能拿到最新的数据,所以产生了第二种会调用get函数的情况)
计算属性最终会出现在vm身上,直接读取使用即可
有get肯定就会有set,set函数何时调用:当fullname被修改的时候会调用set
有种感觉计算属性不是真实存在的东西,它有所依赖,所以要想把计算属性给改掉,要从根本下手,也就是把计算属性依赖的属性给改掉
9.监视属性
1.天气案例
<body>
<div id="div1">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">点击切换</button>
</div>
<script>
new Vue({
el:"#div1",
data:{
isHot:true
},
//为了使差值语法中的内容不是很复杂,使用计算属性
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
}
})
</script>
</body>
2.天气案例-监视属性
监视属性:watch
对于监视属性同样可以监视计算属性,监视属性必须存在才能监视
watch:{
isHot:{
immediate:true,
//handler什么时候调用,当isHot发生改变时调用
handler(newValue,oldValue){
console.log('isHot已经发生改变',newValue,oldValue)
}
},
info:{
//handler什么时候调用,当info发生改变时调用
handler(newValue,oldValue){
console.log('info已经发生改变',newValue,oldValue)
}
}
}
注:
immediate属性,默认值是false,在初始化时调用一下handler
handler什么时候调用,当被监视的属性发生改变时调用handler
监视属性的语法:
要监视的属性名:{
immediate(可以设置也可以不设置)
handler(newValue,oldValue){
handler有两个参数,newValue和oldValue,表示改变后的记录和改变前的记录
}
}
另一种实现监视属性的方法
vm.$watch('isHot',{
immediate:true,
//handler什么时候调用,当isHot发生改变时调用
handler(newValue,oldValue){
console.log('isHot已经发生改变',newValue,oldValue)
}
})
注:
有两个注意点:
1.vm的这个api有两个参数要传
2.当你指定了要去监视谁时一定要用引号括起来
其他的写法跟内部的watch写法完全相同
那么这两种方式分别在什么时候用
当创建vue实例时就已经知道要监视的是谁了,就在内部用watch
如果创建实例的时候不清楚要监听谁,那就用vm的外部接口
监视属性实现天气案例完整代码
<body>
<div id="div1">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">点击切换</button>
</div>
<script>
var vm= new Vue({
el:"#div1",
data:{
isHot:true
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
immediate:true,
handler(newValue,oldValue){
console.log('isHot已经发生改变',newValue,oldValue)
}
},
info:{
handler(newValue,oldValue){
console.log('info已经发生改变',newValue,oldValue)
}
}
}
})
</script>
</body>
3.天气案例-深度监视
vue中的watch默认不监测对象内部值的改变(默认只看一层)
配置deep:true,可以监测对象内部值得改变(看多层)
watch:{
isHot:{
handler(newValue,oldValue){
console.log('isHot已经发生改变',newValue,oldValue)
}
},
//现在有需求是想监视a但是不想监视b
//这种写法是监视多级结构中某个属性的变化
'numbers.a':{
handler(){
console.log('a已经被改变了')
}
},
//如果想要监视numbers中所有属性的变化
//这种写法是监视多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log('numbers中的属性发生了变化')
}
}
}
注:
需要注意的是如果只想监视多级结构中的某个属性的变化,对于要监视的属性,一定要拿引号引起来,for example: ‘numbers.a’
完整代码
<div id="div1">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">点击切换</button>
<hr/>
<h3>a的值是{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a加1</button>
<h3>b的值是{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b加1</button>
</div>
<script>
var vm= new Vue({
el:"#div1",
data:{
isHot:true,
numbers:{
a:1,
b:2
}
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
handler(newValue,oldValue){
console.log('isHot已经发生改变',newValue,oldValue)
}
},
'numbers.a':{
handler(){
console.log('a已经被改变了')
}
},
numbers:{
deep:true,
handler(){
console.log('numbers中的属性发生了变化')
}
}
}
})
</script>
4.使用watch实现姓名案例
当监听属性和计算属性都能完成一个功能时,首先选择计算属性,简单
但是如果你想在实现功能的同时在实现异步任务,还是监听属性方便
例如:你想在修改姓名一秒钟之后再显示改完的名字,就只能用监听属性
watch:{
firstName(newValue){
//在使用监视属性实现功能时,用新的姓+原来的名进行拼接
setTimeout(() => {
this.fullName=newValue+'-'+this.lastName
}, 1000);
},
lastName(newValue){
this.fullName=this.firstName+'-'+newValue
}
}
注:在使用监视属性时可以内置箭头函数,但是使用计算属性时不可以
完整代码
<div id="div1">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName}}</span>
</div>
<script>
var vm= new Vue({
el:"#div1",
data:{
firstName:"张",
lastName:"三",
fullName:'张-三'
},
watch:{
firstName(newValue){
//在使用监视属性实现功能时,用新的姓+原来的名进行拼接
setTimeout(() => {
this.fullName=newValue+'-'+this.lastName
}, 1000);
},
lastName(newValue){
this.fullName=this.firstName+'-'+newValue
}
}
})
</script>
10.vue绑定样式
(1)
注:绑定class样式,适用于,样式的类名不确定,需要动态绑定
(2)
注:数组写法,适用于,需要绑定的样式个数不确定,名字也不确定
两种方法最大的区别就在于,绑定的样式的个数是不是确定的,如果需要绑定的样式个数写死的话那么用字符串方便,但是如果个数不确定的话,数组明显比字符串方便
(3)
注:对象写法,适用于:需要绑定的样式个数确定,名字也确定,但是需要动态决定用还是不用
(4)绑定style内联样式
注:将style样式写成一个对象,然后通过vue去动态调整
11.条件渲染
使用v-show做条件渲染,如果v-show后面的判别式成立的话就显示内容,不成立的话就不显示内容
<script>
new Vue({
el:"#div1",
data:{
name:'南京',
a:false,
n:0
},
})
</script>
<h2 v-show="true">欢迎来到{{name}}</h2>
<h2 v-show="1===3">欢迎来到{{name}}</h2>
使用v-if做条件渲染,用法和v-show相同,但是v-if有一些延申用法
<h2 v-if="1===3">欢迎来到{{name}}</h2>
<h2 v-if="true">欢迎来到{{name}}</h2>
既然有v-if,那么对应的肯定有v-else-if,这两者属于一组判断
,一般情况下使用else-if能提高效率,当然还有v-else
如果想满足需求,有三个h2标签,想让标签在n=1时全部显示出来,一种选择就是在每个h2上都加上判断,但是这样实现起来太麻烦
还有一种办法就是使用div将代码块包裹起来,但是这样会破坏原来的结构,即
<div v-show="n===1">
<h2>你好</h2>
<h2>南京</h2>
<h2>仙林大学城</h2>
</div>
最好的办法是使用template将代码块包裹起来,通过控制台可以看出并没有影响结构
但是需要注意的一点是template只能配合v-if去使用
<template v-if="n===1">
<h2>你好</h2>
<h2>南京</h2>
<h2>仙林大学城</h2>
</template>
12.列表渲染
(1)基本列表
<script>
new Vue({
el:"#div1",
data:{
persons:[
{id:"001",name:"张三",age:"18"},
{id:"002",name:"李四",age:"19"},
{id:"003",name:"王五",age:"20"}
],
cars:{
name:"奥迪A8",
price:"70w",
color:"黑色"
}
}
})
</script>
对于遍历使用的是v-for标签
使用v-for遍历数组
<h2>人员列表</h2>
<ul v-for="p in persons" :key="p.id">
<!-- 此处的in也可以换成of,跟js中的两个循环一致for in 和 for of -->
<li>{{p.name}}-{{p.age}}</li>
</ul>
使用v-for遍历对象
<h2>汽车信息</h2>
<ul v-for="(value,key) in cars" :key="key">
<!-- 此处的in也可以换成of,跟js中的两个循环一致for in 和 for of -->
<li>{{key}}-{{value}}</li>
</ul>
注:对于遍历对象时,是先拿到值再拿到键,注意一个顺序问题
还可以遍历字符串,但是用的不多
还可以遍历指定次数,但是用的更少
(2)key的原理
react,vue中的key有什么作用
【1】虚拟dom中key的作用,key是虚拟dom对象的标识,当状态中的数据发生改变时,vue会根据新数据生成新的虚拟dom,随后,vue进行 新虚拟dom与旧虚拟dom的差异比较,比较规则如下
【2】对比规则
旧虚拟dom中找到与新虚拟dom中相同的key
若虚拟dom中的内容不变,直接使用之前的真实dom
若虚拟dom中的内容变了,就生成新的真实dom,随后替换掉页面中原来的真实dom
旧虚拟dom中未找到与新虚拟dom相同的key
创建新的真实dom,随后渲染到页面
【3】使用index可能会引发的问题
1.若数据进行逆序添加,逆序删除等破坏顺序的操作,会产生没有必要的真实dom更新,界面效果没有问题,但是效率低
2.如果结构中还包含输入类的dom:会产生错误的dom更新,界面就有问题了
【4】开发中如何选择key
1.最好是使用每条数据的唯一标识作为key,比如id,身份证号,手机号,学号等唯一数据
2.如果不存在对数据的逆序操作,仅用渲染列表进行展示,使用index作为key是没有问题的
(3)列表过滤
列表过滤,其实就是一个模糊查询
<div id="div1">
<h2>人员列表</h2>
<input type="text" placeholder="请输入姓名" v-model="keyWord">
<ul v-for="p in filPersons" :key="p.id">
<li>{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
el:"#div1",
data:{
keyWord:"",
persons:[
{id:"001",name:"马冬梅",age:"19",sex:"女"},
{id:"002",name:"周冬雨",age:"20",sex:"女"},
{id:"003",name:"周杰伦",age:"21",sex:"男"},
{id:"004",name:"温兆伦",age:"22",sex:"男"},
],
filPersons:[]
},
用监视属性来实现
取到用户的输入内容之后,如果用户输入的内容发生改变了,也应该知道改变的是什么,变成了什么
watch:{
keyWord:{
immediate:true,
handler(val){
//接下来要做的就是对比,也就是过滤
//这里要注意过滤函数的一点就是,过滤函数会返回一个新的数组,要让数组persons承接一下新数组
//但是使用persons去承接新数组,逻辑上又有问题,进行搜索动作之后,除了被搜索的数据,其他数据都不见了
//所以,要使用一个新的空数组去承接,就是filePersons
//使用新的空数组去承接又有一个新问题,刚打开页面的时候,新数组还是空的,不会显示数据
//解决:因为indexOf方法里将空串也判断进包含,所以,不使用简写的方法,而是使用完整的方法实现数据监视属性
//然后,使用immediate属性,这样刚打开页面的时候就会调用一次监视器,此时,input输入框中是空值,就可以显示数据了
this.filPersons= this.persons.filter((p)=>{
return p.name.indexOf(val) !==-1
// 在一个字符串里判断是否包含所传的字符,有了就返回下标索引,没有就返回-1
})
}
}
}
用计算属性来实现
el:"#div1",
data:{
keyWord:"",
persons:[
{id:"001",name:"马冬梅",age:"19",sex:"女"},
{id:"002",name:"周冬雨",age:"20",sex:"女"},
{id:"003",name:"周杰伦",age:"21",sex:"男"},
{id:"004",name:"温兆伦",age:"22",sex:"男"},
]
},
computed:{
filPersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !==-1
})
}
}
注:
计算属性和监视属性其实逻辑实现上是相同的,都用到了数组过滤器,只是计算属性实现的要更简单一些
(4)列表排序
使用计算属性实现
<div id="div1">
<h2>人员列表</h2>
<!-- 对于人员的年龄进行排序 -->
<input type="text" placeholder="请输入姓名" v-model="keyWord">
<!-- 如果想知道用户点的到底是哪一个,那么对于排序的标识也及其重要 -->
<button @click="sortNum=2">年龄升序</button>
<button @click="sortNum=1">年龄降序</button>
<button @click="sortNum=0">原顺序</button>
<ul v-for="p in filPersons" :key="p.id">
<li>{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
el:"#div1",
data:{
keyWord:"",
sortNum:0,//当降序时是1,升序时是2
persons:[
{id:"001",name:"马冬梅",age:"31",sex:"女"},
{id:"002",name:"周冬雨",age:"20",sex:"女"},
{id:"003",name:"周杰伦",age:"32",sex:"男"},
{id:"004",name:"温兆伦",age:"22",sex:"男"},
]
},
computed:{
filPersons(){
var arr= this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !==-1
})
//判断是否需要进行排序
if(this.sortNum!==0){
arr.sort((p1,p2)=>{
return this.sortNum ===1 ? p2.age-p1.age : p1.age-p2.age
})
}
return arr
}
}
注:
这里要说明一下,js自带的sort排序函数,带有两个参数,例如
arr.sort(a,b),如果是a-b,那就是升序排序,如果是b-a,那就是降序排序
还有,计算属性是真的牛逼
(5)更新时的一个问题
现在有一个需求,有一个按钮,点击按钮之后,就会更新页面上马冬梅的信息
<div id="div1">
<h2>人员列表</h2>
<button @click="updateMei">点我更新马冬梅信息</button>
<ul v-for="p in persons" :key="p.id">
<li>{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</d
methods: {
updateMei(){
this.$set(this.persons,0,{id:"001",name:"马老师",age:"50",sex:"男"})
}
}
注:
这里存在一个数组更新监测的问题
this.persons[0].name=“马老师” //奏效
this.persons[0].age=50 //奏效
this.persons[0].sex=“男” //奏效
但是如果这样写,就会出问题
this.persons[0] = {id:“001”,name:“马老师”,age:“50”,sex:“男”}
vue就不承认信息更新了
对于这个vue不会承认更新的问题,可以有解决办法
1.见word文档,或者是vue官方文档中列表渲染关于数组更新监测
this.persons.splice(0,1,{id:“001”,name:“马老师”,age:“50”,sex:“男”})
2.是使用vue.set方法一样可以对于数组进行更新
传入三个参数,1.想要更新的数组,2.更新数组中的哪个对象,3.更新的对象信息
(6)vue_set的使用
这里又有新的需求,本来在定义的data的student里并没有sex这一属性,但是到后来发现必须要有这个属性了,于是通过vue.set的方法进行添加属性
<h2>学生信息</h2>
<button @click="updateSex">点我添加性别,默认值是男</button>
<h2>学生姓名:{{student.name}}</h2>
<h2 v-if="student.sex">学生性别:{{student.sex}}</h2>
<h2>学生年龄,真实年龄:{{student.age.relAge}},对外年龄:{{student.age.vAge}}</h2>
methods: {
updateSex(){
// vue的set方法,有两种写法
// Vue.set(this.student,'sex','男')
// 往对象身上添加一个属性,后面是属性名和属性值
this.$set(this.student,'sex','男')
//这里的this是vm实例
}
},
(7)大总结
vue 监视数据的原理:
- vue 会监视data 中所有层次的数据·
- 如何监测对象中的数据?
通过setter 实现监视, 且要在new Vue 时就传从入要监视的数据。
( 1 )对象中后追加的属性, vue 默认不做响应式处理
(2 )如需给后添加的属性做响应式, 请使用如下API :
Vue .set(target , propertyName/index , value) 或
vm. $set(target , propertyName/index, value) - 如伺监测数组中的数据?
通过包裹数组更新元素的方法实现, 本质就是做了两件事:
( 1 ) 调用原生对应的方法对数组进行更新。
( 2 ) 重新解析模板, 进而更新页面。 - 在Vue 改数组中的某个元素一 定要用如下方法:
1 .使用这些api: push() 、pop() 、shift() 、unshift() 、splice() 、sort () 、reverse()
2.vue.set() 或vm. s e t ( ) ∗ ∗ 特 别 注 意 : v u e . s e t ( ) 和 v m . set () **特别注意: vue.set() 和vm. set()∗∗特别注意:vue.set()和vm.set() 不能给vm或者vm的根数据对象添加属性! !**
将自己写的vm.data中的数据经由vue转变成vm._data的过程叫做数据劫持
13.收集表单数据
<div id="div1">
<!-- 可以在表单上设置方法阻止默认提交 -->
<form @submit.prevent="demo">
<!-- v-model修饰符.trim,去掉字符串前后的空格 -->
账号:<input type="text" v-model.trim="userInfo.account"> <br><br>
密码:<input type="password" v-model="userInfo.password"><br><br>
<!--如果用原生的number来控制,那么传到vm中的数值是字符串类型的,不是数值类型的 -->
<!-- v-model有修饰符.number会帮你把数据转成数值类型的,而且两个一起使用的话还有输入限制,只能输入数值类型数值 -->
年龄:<input type="number" v-model.number="userInfo.age"><br><br>
性别:男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"><br><br>
爱好:
抽烟<input type="checkbox" v-model="userInfo.hobby" value="smoke">
喝酒<input type="checkbox" v-model="userInfo.hobby" value="drink">
烫头<input type="checkbox" v-model="userInfo.hobby" value="perm"><br><br>
所在校区:
<select v-model="userInfo.area">
<option value="">请选择校区</option>
<option value="xianlin">仙林校区</option>
<option value="tiantang">天堂校区</option>
<option value="zhongshan">中山校区</option>
</select>
<br><br>
补充;
<!-- v-model的修饰符.lazy,不进行实时的数据捕获,只有当输入框失去焦点的一瞬间才会捕获数据 -->
<textarea v-model="userInfo.otherInfo"></textarea>
<br><br>
<input type="checkbox" v-model="userInfo.whether">阅读并接受<a href="http://www.niit.edu.cn">用户协议</a>
<br><br>
<!-- 当点击提交按钮时,表单就默认提交了 -->
<button>提交</button>
</form>
</div>
注:
v-model的修饰符:trim,lazy,number
new Vue({
el:"#div1",
data:{
userInfo:{
account:"",
password:"",
sex:"",
//除了上面的单选框性别要指定value以外,对于可能有多个选择的checkbox,要将v-model指定的对象变成数组的形式
//否则只会显示单选框是否勾选,不会显示value指定的值,还带着些其他的奇葩错误
hobby:[],
area:"",
otherInfo:"",
age:"",
whether:""
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
},
})
注:
这里要特别注意多选checkbox的使用,要将v-model指定的对象变成数组形式,要不然会出现各种各样的奇葩错误,而且得不到想要的value返回
14.过滤器
过滤器一般有两种语法,可以在差值语法里使用,一种是v-bind动态绑定里可以使用
下面这个小案例实现时间格式的转变
(1)使用计算属性实现
<h2>转变格式后的时间</h2>
<!-- 计算属性实现 -->
<h3>当前的时间是:{{formTime}}</h3>
new Vue({
// 现在这些过滤器只是写在一个vue实例中,是局部过滤器,在new vue实例之前就创建的过滤器是全局过滤器
el:"#div1",
data:{
time:1656302701355
},
computed:{
formTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
})
(2)使用过滤器实现
过滤器的写法: time | 过滤器名称
前面是你想传给过滤器的参数,加一个空格,管道符|,再加一个空格,过滤器名称
<h3>当前的时间是:{{time | formTimer}}</h3>
<!-- 过滤器的传参 -->
<!-- 'YYYY_MM_DD',传到过滤器里的形式 -->
<h3>当前的时间是:{{time | formTimer('YYYY_MM_DD')}}</h3>
注
过滤器的使用,多个过滤器可以串联,像一条链一样从头到尾往下走,往下筛选
filters:{
// 过滤器里会接受参数,所以
// 这里就会有一个问题,当过滤器传参指定时间形式时,传参的那个好用,不传参的那个又不好用了
// 这里可以用es6的默认参数来解决,当有参数传进来时使用传进来的参数,没有的话用默认设定好的
// str="YYYY-MM-DD HH:mm:ss"设置好的默认值
formTimer(value,str="YYYY-MM-DD HH:mm:ss"){
return dayjs(value).format(str);
}
}
当然也可以设置全局过滤器
Vue.filter('myslice',function(value){
return value.slice(0,4)
})
注
全局过滤器,前面的myslice是过滤器名称,后面的function内容是过滤器内容体
15.内置指令
v-text和v-html
<div id="div1">
<div>{{name}}</div>
<div v-text="str"></div>
<div v-html="str"></div>
</div>
<script>
new Vue({
el:"#div1",
data:{
name:"南工",
str:"<h2>南工额</h2>"
}
})
注
这里有特别需要注意的几点
1.v-html支持结构的解析,但是v-text不支持
2.v-html有很严重的安全问题,及其容易导致xss跨站脚本攻击
v-clock
v-clock指令没有值,本质上是一个特殊属性,当vue实例创建完毕并接管容器后,会删掉v-clock属性
使用css配合v-clock解决因为网速慢的时候页面出现{{xxx}}的问题
v-once
用途
1.v-once所在的节点在初次渲染后就视为静态内容了,不在发生变化
2.以后数据的改变不会引起v-once所在节点数据的更新,可以用于优化性能
用例
现在想要的需求是获得一个初始化的n,但是与vue实例有关联不是直接写死的数值
<h2 v-once>初始化的数值是{{n}}</h2>
<h2>当前的数字是{{n}}</h2>
<button @click="n++">点我n+1</button>
new Vue({
el:"#div1",
data:{
n:1
}
})
注
在这里,v-once修饰的节点虽然是从data中读取的数据,但是data中数据的改变就不会影响到这个节点的变化了
v-pre
用途
1.跳过其所在节点的编译过程
2.可利用它跳过没有使用指令语法没有使用差值语法的节点,可以提高效率,加快编译
<div id="div1">
<h2 v-pre>vue其实真够难的</h2>
<h2>当前的数值是{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
<script>
new Vue({
el:"#div1",
data:{
n:1
}
})
</script>
16.自定义指令
需求1:定义一个v-big指令,与v-text的功能相同,但是会把字体放大十倍(函数指令)
需求2:定义一个v-fbind指令,与v-bind指令功能类似,但是可以让其所绑定的元素自动获取焦点(对象指令)
注意:指令名的问题
如果指令中存在多个单词,那么每个单词之间使用-连接,在下面定义指令体的时候要写完整的形式,也就是用’'把指令包起来
<div id="div1">
<h2>当前的数值是<span v-text="n"></span></h2>
<h2>放大之后的数值是<span v-big-number="n"></span></h2>
<button @click="n++">点我n+1</button>
<hr>
<input type="text" v-fbind="n">
</div>
自定义指令往模块中渲染不是靠return,而是靠两个参数,element和binding
new Vue({
el:"#div1",
data:{
n:1
},
directives:{
'big-number'(element,binding){
// console.log(element,binding)
element.innerText=binding.value*10
console.log('big-number',this)//注意此处的this就不再是vm了,此处的this是window
},
fbind:{
// 指令与元素成功绑定时会有vue来帮你调用
bind(element,binding){
console.log(element,binding)
// 把n=1放入输入框
element.value=binding.value
},
// 指令所在元素被插入页面时会有vue来帮你调用
inserted(element,binding){
element.focus()
},
// 指令所在模板被重新解析时,会有vue来帮你调用
update(element,binding){
// 把n=1放入输入框
element.value=binding.value
}
}
}
})
注
big-number函数何时会被调用
1.指令与元素成功绑定时会调用
2.指令所在的模板被重新解析时,也就是id="div1"这个模板
对于两个参数,element代表的是span元素,binding中主要关注的就是value值,就是v-big-number中用到的n
vue给定了固定的函数指令三个 bind inserted update
至于为何需求二要使用对象指令来完成
因为,如果你想在元素上获取焦点,就只能在元素已经被放到页面后再去执行.focus()才会奏效
如果继续使用函数自定义指令的话,那么再怎么执行.fouces()也不能在元素放到页面后执行,只会在之前执行,那样就无法达到目的
在vue实例中的指令是局部指令,如果想变成全局指令就和过滤器一样
Vue.directive('fbind2',{
bind(element,binding){
element.value=binding.value
},
inserted(element,binding){
element.focus()
},
update(element,binding){
element.value=binding.value
}
})
Vue.directive('big2',function(element,binding){
element.innerText=binding.value*10
console.log('big-number',this)
})
17.生命周期
引出生命周期
使用一个案例引出生命周期,完成渐变效果
<div id="div1">
<h2 :style="{opacity}">欢迎学习vue</h2>
</div>
<script>
var vm= new Vue({
el:"#div1",
data:{
opacity:1
},
mounted() {
console.log('mounted')
setInterval(()=>{
this.opacity-=0.01
if(vm.opacity<=0) this.opacity=1
},16)
},
})
</script>
注
mount有挂载的意思,那么何为挂载vue将模板从虚拟dom变成真实dom,将真实dom放在页面上的过程叫做挂载
vue完成模板解析,并把初始的真实dom元素放在页面中(挂载完毕)调用mounted
需要特别注意的是,是初始的真实dom,也就是说mounted只会在开头被调用一次
总结:
生命周期
1.又名 生命周期钩子,生命周期回调函数,生命周期函数
2.是什么:vue在关键时刻帮我们的一些特殊名称的函数
3.生命周期函数的名字不可更改,但是函数的具体内容是程序员根据需求编写的
4.生命周期中的this指向的是vm或组件实例对象
分析生命周期
<div id="div1">
<h2>当前的n值为:{{n}}</h2>
<button @click="add">点我n+1</button>
<button @click="bye">点我销毁vm</button>
</div>
<script>
new Vue({
el:"#div1",
data:{
n:1
},
methods:{
add(){
this.n++
},
bye(){
this.$destroy()
}
},
// vue生命周期函数
// 创建之前和创建完毕一定不是vm创建之前和创建完毕
// 是数据监测和数据代理的创建之前和创建完毕
beforeCreate() {
console.log('beforeCreate')
console.log(this)
},
created() {
console.log('created')
console.log(this)
},
beforeMount() {
// 页面呈现的是未经vue编译的dom结构,此时对所有dom的操作最终都不奏效
console.log('beforeMount')
console.log(this)
},
mounted() {
// 页面呈现的是经vue编译的dom结构
console.log('mounted')
console.log(this)
},
beforeUpdate() {
// 此时数据是新的,但是页面是旧的,就意思数据和页面并没有保持同步
console.log('beforeUpdate',this.n)
},
updated() {
//此时页面是新的,数据也是新的
console.log('update',this.n)
},
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('Destroyed')
},
})
</script>
总结完善
常用的生命周期钩子:
1.mounted:发送ajax请求 ,启动定时器,绑定自定义事件,订阅消息等【初始化操作】
2.beforeDestory:清除定时器,解绑自定义事件,取消订阅消息等【收尾工作】
关于销毁vue实例
1.销毁后借助vue开发者工具看不到任何信息
2.销毁后自定义事件会失效,但是原生dom事件依然有效
3.一般不会在beforeDestoryc操作数据,因为即使操作数据,也不会触发更新流程了
<div id="div1">
<h2 :style="{opacity}">欢迎学习vue</h2>
<button @click="stop">点击停止变换</button>
</div>
<script>
var vm= new Vue({
el:"#div1",
data:{
opacity:1
},
methods: {
stop(){
this.$destroy()
}
},
mounted() {
console.log('mounted')
this.timer= setInterval(()=>{
this.opacity-=0.01
if(vm.opacity<=0) this.opacity=1
},16)
},
beforeDestroy() {
clearInterval(this.timer)
},
})
</script>
18.组件
对于组件的理解
1.理解:用来实现局部(特定)功能效果的代码集合
2.为什么要使用组件:一个界面的功能很复杂
3.作用:复用代码,简化项目编码,提高运行效率
什么叫模块化
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用。
非单文件组件
一个文件中包含有n个组件
单文件组件
一个文件中只包含有1个组件
非单文件组件
第一步:创建一个组件
const school= Vue.extend({
// 组件里应当包括完成局部内容交互中的所有内容,所以上面的div中的内容也应该放到组件里来
template:`<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>`,
//对于组件里的data只能写成函数式,而不能再写成对象式了,如果写成对象式会报错,提醒你写成函数式
data(){
// 在函数里返回一个对象
return{
schoolName:"南京工业职业技术大学",
address:"仙林大学城"
}
},
methods: {
showName(){
alert(this.schoolName)
}
},
})
const student = Vue.extend({
template:`<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>`,
data(){
return {
studentName:"张鑫",
age:"22"
}
}
})
第二步:注册组件(局部注册)
new Vue({
el:"#div1",
// 第二步:注册组件(局部注册)
components:{
school,
student
}
})
第三步:使用组件标签使用组件
<div id="div1">
<school></school>
<hr>
<student></student>
</div>
注
将上述两个内容创建成两个组件,一个school组件,一个student组件
需要特别注意的是,在组件里是没有资格写el的,el只有在创建vue实例对象时候才能写
因为最终所有的组件都是由一个vm进行统一管理,由vm来决定到底用在哪而不是由组件来决定到底用在哪
既然有局部组件那么一定还有全局组件
几个注意点
关于组件标签的写法
1.开始结束
如:
2.自闭和
如:
注意:在不使用脚手架时,使用会导致后续组件不能渲染
3.关于组件的创建方式
const school =Vue.extend(options)
简写方式:
const school = options
<div id="div1">
<my-school></my-school>
</div>
<script>
const s= Vue.extend({
// 可以使用name配置项规定组件在开发者工具中呈现的名字,不随注册组件中的名字改变而改变
name:'Njuit',
template:`<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>`,
data(){
return{
schoolName:"南京工业职业技术大学",
address:"仙林大学城"
}
},
methods: {
showName(){
alert(this.schoolName)
}
},
})
new Vue({
el:"#div1",
components:{
// 第一个注意点:当组件的名称是多个单词组成的,vue建议是单词中间用短横杠去表示
'my-school':s
}
})
</script>
组件的嵌套
在标准化开发中,通常有一个app组件,这个app组件由vm来直接领导,然后这个app组件来管理其他组件
对于嵌套的组件,要把哪个组件嵌套在哪里,就在父组件里进行组件的注册和组件的调用
<div id="div1">
<app></app>
</div>
<script>
const student= Vue.extend({
template:`<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>`,
data(){
return{
name:"张鑫",
age:22
}
}
})
const school= Vue.extend({
template:`<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
<button @click="showName">点我提示学校名</button>
</div>`,
data(){
return{
schoolName:"南京工业职业技术大学",
address:"仙林大学城"
}
},
methods: {
showName(){
alert(this.schoolName)
}
},
components:{
student
}
})
const app = {
template:`
<div>
<school></school>
</div>`,
components:{
school
}
}
new Vue({
el:"#div1",
components:{
app
}
})
</script>
VueComponent
<div id="div1">
<school></school>
</div>
<script>
const hello = Vue.extend({
template:`<span>欢迎来到南京</span>`
})
const school = Vue.extend({
template:`<div>
<h2>{{name}}</h2>
<h2>{{address}}</h2>
<hello></hello>
</div>`,
data() {
return {
name:"南京工业职业技术大学",
address:"仙林大学城"
}
},
components:{
hello
}
})
new Vue({
el:"#div1",
components:{
school
}
})
</script>
总结
关于 VueComponent :
1.schoo1组件本质是一个名为 Vuecomponent 的构造函数,且不是程序员定义的,是 Vue.extend 生成的。
2.我们只需要写<school/>或<school></ school >, Vue 解析时会帮我们创建 school 组件的实例对象,
即 Vue 帮我们执行的: new Vuecomponent ( options )。
3.特别注意:每次调用 Vue . extend ,返回的都是一个全新的 VueComponent !!!
4.关于 this 指向:
(1).组件配置中:
data 函数、 methods 中的函数、 watch 中的函数、 computed 中的函数它们的 this 均是【 Vuecomponent 实例对象】.
(2). new Vue ()配置中:
data 函数、 methods 中的函数、 watch 中的函数、 computed 中的函数它们的 this 均是【 Vue 实例对象】。
5.Vuecomponent的实例对象,以后简称 vc (也可称之为:组件实例对象)。
Vue 的实例对象,以后简称 Vm 。
关于vc和vm的区别,如果不那么认真的比较话,vc和vm是可以画等号的,vm身上有的东西vc身上也有,但是vm还是有一些vc身上没有的东西
例如:vm中可以使用el来断定vm为哪个容器服务,但是vc中不能写el,还有vm中的data可以使用对象的形式进行定义,但是vc中的data只能是函数的形式
一个重要的内置关系
一个重要的内置关系
VueComponent.prototype.杠杠proto杠杆 = Vue.prototype
为什么要有这个关系
想要组件的实例对象vc也能使用vue上面的属性和方法
<script>
// 重温一下原型
// 定义一个构造函数
function Demo(){
this.a=1
this.b=2
}
// 创建一个实例
const d= new Demo()
console.log(Demo.prototype) // 显示原型属性
console.log(d.__proto__) // 隐式原型属性
// 对于显示,隐式原型,显示原型对象只有函数才可以拥有,隐式原型对象是实例拥有
// 隐式原型对象总是指向他缔造者的原型对象
console.log(Demo.prototype===d.__proto__)
// 通过显示原型属性操作原型对象,往对象中添加一个新的属性 x=99
Demo.prototype.x=99
console.log('@',d.x)
</script>
19.浅学一下css3样式
2D转化
<style>
div{
margin: 150px;
width: 200px;
height: 100px;
background-color: yellow;
border: 1px solid black;
border: 1px solid black;
/*scale(2,3)转变宽度为原来的大小的2倍,和其原始大小3倍的高度。 */
/* transform: scale(2,3); */
/* translate值(50px,100px)是从左边元素移动50个像素,并从顶部移动100像素。
定义的是图像的左右和上下移动 */
/* transform: translate(150px,200px); */
/* rotate()方法,在一个给定度数顺时针旋转的元素。负值是允许的,这样是元素逆时针旋转。
rotate(180deg)延顺时针方向转180度 */
/* transform: rotate(180deg); */
/* 包含两个参数值,分别表示X轴和Y轴倾斜的角度,如果第二个参数为空,则默认为0,参数为负表示向相反方向倾斜。
skewX(<angle>);表示只在X轴(水平方向)倾斜。
skewY(<angle>);表示只在Y轴(垂直方向)倾斜。
skew(30deg,20deg) 元素在X轴和Y轴上倾斜20度30度。*/
/* transform: skew(30deg,20deg); */
/* matrix其实就是将所有的2d转换方法进行了一个大融合
matrix 方法有六个参数,包含旋转,缩放,移动(平移)和倾斜功能。 */
transform: matrix(0.866,0.5,-0.5,0.866,0,0);
}
</style>
</head>
<body>
<div>
<h3>直接难上加难</h3>
</div>
</body>
过渡
<style>
div {
width: 100px;
height: 100px;
background: red;
transition: width 2s, height 2s, transform 2s;
}
/* 注意: 如果未指定的期限,transition将没有任何效果,因为默认值是0。
指定的CSS属性的值更改时效果会发生变化。一个典型CSS属性的变化是用户鼠标放在一个元素上时:
*/
div:hover {
width: 200px;
height: 200px;
transform: rotate(180deg);
}
</style>
</head>
<body>
<div>
<h3>真是难上加难喽</h3>
</div>
</body>
动画
/*
当在 @keyframes 创建动画,把它绑定到一个选择器,否则动画不会有任何效果。
指定至少这两个CSS3的动画属性绑定向一个选择器:
规定动画的名称
规定动画的时长
动画是使元素从一种样式逐渐变化为另一种样式的效果。
您可以改变任意多的样式任意多的次数。
请用百分比来规定变化发生的时间,或用关键词 "from" 和 "to",等同于 0% 和 100%。
0% 是动画的开始,100% 是动画的完成。
为了得到最佳的浏览器支持,您应该始终定义 0% 和 100% 选择器。
*/
div{
width:100px;
height:100px;
background:red;
position: relative;
animation: myFirstAnimal;
/* 以下就是动画中的所有属性 */
animation-duration:5s;
animation-timing-function:linear;
animation-delay:2s;
animation-iteration-count:infinite;
animation-direction:alternate;
animation-play-state:running;
}
@keyframes myFirstAnimal
{
/* 0% {background-color: red;}
25% {background-color: purple;}
50% {background-color: black;}
75% {background-color: greenyellow;}
100% {background-color: pink;} */
0% {background:red; left:0px; top:0px;}
25% {background:yellow; left:200px; top:0px;}
50% {background:blue; left:200px; top:200px;}
75% {background:green; left:0px; top:200px;}
100% {background:red; left:0px; top:0px;}
}
</style>
</head>
<body>
<div>
</div>
</body>