vue.js
一、Vue.js简介
1.vue.js是什么
- 是一个构建用户界面的框架
- 是一个轻量级的MVVM(Model-View-ViewModel)框架,angular、react也是MVVM框架其实就是所谓的数据双向绑定
- 数据驱动+组件化的前端开发(核心思想)
- 通过简单的API实现响应式的数据绑定和组合的视图组件
参考:官网
2.vue的特点
- 简单、易学、更轻量
- 指令以v-xxx开头
- 风格:HTML代码+JSON数据,再创建一个vue实例
- 由个人维护:尤雨溪(华人)
二、起步
1.下载核心库vue.js
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
vue2.0与vue1.0,最大的变化就是引入了Viryual DOM,页面更新效率更高,速度更快
2.vue的实现
js:
new Vue({
el:'#app01',
data:{
msg:'hello world',
name:'Tom'
}
})
html:
<div id="app01">
{{name}},{{msg}}
</div>
3.配置vue-devtools
//配置是否允许vue-devtools检查代码,方便调试;开发环境设置为true,生产环境需设置为false
Vue.config.devtools = false;
//阻止vue启动时生成消息
Vue.config.productionTip = false;
三、常用指令
1.什么是指令
用来扩展html标签的功能
2.vue中常用的指令
- v-model
双向数据绑定,一般用于表单元素 - v-for
对数组或对象进行循环操作
注:注意:key的使用,可以提供效率 - v-on
用来绑定事件,用法: v-on:事件=’函数’(比如v-on:click=’show’) - v-show/v-if
用来显示或隐藏元素,v-show是通过display实现,v-if是每次删除后重新创建
四、事件
1.事件简写
v-on:click=”” 简写方式 @click=”“
2.事件对象$event
#原生js获取事件对象
<div id="app01">
<button onclick="show(event)">click me</button>
</div>
<script type="text/javascript">
function show(e){
console.log(e.target.innerText)
}
</script>
#vue.js获取事件对象
<div id="app01">
<!-- <button v-on:click="show(event)">click me</button> -->
<button v-on:click="show($event)">click me</button>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
methods:{
show(e){
console.log(e)
}
}
});
</script>
3.事件冒泡
阻止事件冒泡:
3.1. 原生js方法,依赖事件对象
<div onclick="func1(event)" style="height: 400px;width: 400px;background: blue">
<div onclick="func2(event)" style="height: 200px;width: 200px;background: yellow">
<button onclick="func3(event)">click</button>
</div>
</div>
<script type="text/javascript">
function func1(e){
console.log(111);
};
function func2(e){
console.log(222);
};
function func3(e){
console.log(333);
e.stopPropagation(); //通过事件对象阻止事件传播
}
</script>
3.2. vue.js方法,更简洁,不依赖事件对象
<div id="app01">
<div @click="write">
<p @click="read">
<!-- <button v-on:click="show($event)">click me</button> -->
<!-- 使用v-on:click.stop=""加后缀的方法-->
<button v-on:click.stop="show($event)">click me</button>
</p>
</div>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
methods:{
write(){
console.log(111);
},
read(){
console.log(222);
},
show(e){
console.log(333);
}
}
});
</script>
4.事件默认行为
阻止默认行为
4.1. 原生js方法
<div>
<a href="https://www.baidu.com" onclick="func4(event)">百度</a>
</div>
<script>
function func4(e){
console.log('func4');
e.preventDefault(); //阻止默认事件发生
}
</script>
4.2. vue.js方法
<div id="app01">
<!--使用.prevent阻止默认事件发生-->
<a href="https://www.baidu.com" @click.prevent="test">百度</a>
<a href="https://www.baidu.com" @click="test">百度</a>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
methods:{
test(){
console.log('prevent default')
}
}
});
</script>
5.键盘事件
回车:@keydown.13或者@keydown.enter
上:@keydown.38或者@keydown.up
<div id="app01">
<!-- 键盘事件:@keydown,@keypress,@keyup -->
测试keycode:<input type="text" @keydown="show($event)"><br>
按up触发事件,其余不触发<input type="text" @keydown.up="print"><br>
按up触发事件,其余不触发<input type="text" @keydown.38="print"><br>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
methods:{
show(e){
console.log(e.keyCode)
},
print(){
console.log('up')
}
}
});
</script>
自定义键盘事件(举例,json格式)
Vue.config.keyCodes={left:37,up:[37,38]}
6.事件修饰符
.stop
.prevent
.capture
.self
.once
<!-- 举例.once -->
<div id="app01">
<button @click="print">xx</button> <!-- 可以触发多次 -->
<button @click.once="print">xx</button> <!-- 只能触发一次 -->
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
methods:{
print(){
console.log('1111')
}
}
});
</script>
五、属性
1.属性的绑定和简写
v-bind用于属性的绑定,语法->v-bind:属性=”“,简写方式->:属性=”“
2.class和style属性
- class属性
<style type="text/css">
.aa{
color: red;
font-size: 20px;
}
.bb{
background-color: blue;
}
</style>
<div id="app01">
<!-- 变量形式 -->
<p :class="s1">hello,vue!</p>
<!-- 数组形式 -->
<p :class="[s1,s2]">hello,vue!</p>
<!-- json形式 -->
<p :class="{aa:true,bb:false}">hello,vue!</p>
<p :class="{aa:num>5,bb:false}">hello,vue!</p>
<p :class="s">hello,vue!</p>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
s1:'aa',
s2:'bb',
num:10,
s:{aa:true,bb:false}
}
});
</script>
- style属性
<div id="app01">
<!-- 变量形式 -->
<p :style="s1">hello,vue!</p>
<!-- 数组形式 -->
<p :style="[s1,s2]">hello,vue!</p>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
s1:{color:'red',fontSize:'20px'},
s2:{backgroundColor:'blue'}
}
});
</script>
六、模板
### 1.通过{{}}进行数据绑定
### 2.数据绑定方式
+ 双向绑定,使用v-mode
<div id="app01">
<input type="text" v-model="msg">
<p>{{msg}}</p>
<!-- v-once表示数据只绑定一次不会被修改 -->
<p v-once>{{msg}}</p>
<!-- v-pre表示不编译,直接显示结果还是{{msg}} -->
<p v-pre>{{msg}}</p>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
msg:'hello,vue',
}
});
</script>
- 单向绑定
- {{}},v-text,v-html;v-text与v-html区别在于v-text用于文本,v-html用于html标签
<div id="app01">
<p>{{msg}}</p>
<p v-text="msg"></p>
<p v-html="msg"></p>
<p v-text="message"></p>
<p v-html="message"></p>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
msg:'hello,vue',
message:'<span style="color:red;">hello,vue<span>'
}
});
</script>
七、过滤器
1.简介
用来过滤模型数据,在显示之前进行数据处理和筛选;
语法:{{ data | filter1(参数) | filter2(参数)}}
2.关于内置过滤器
- vue 1.0 中内置了很多过滤器
- vue 2.0 中已删除所有内置过滤器
解决办法:
a.使用第三方库,如loadash、date-fns日期格式化,accounting.js货币格式化等(引入相关的js)
b.使用自定义过滤器
3.自定义过滤器
分类:全局过滤器、局部过滤器
- 自定义全局过滤器
<div id="app01">
<!-- 定义全局过滤器 -->
<h3>{{month|addZero}}</h3> <!-- 不带参数过滤器 -->
<h3>{{pi|leftTwo(2)}}</h3> <!-- 带参数过滤器 -->
</div>
<script type="text/javascript">
Vue.filter('addZero',function(data){
return data<10?'0'+data:data
});
Vue.filter('leftTwo',function(data,n){
return data.toFixed(n);
});
var app01 = new Vue({
el:'#app01',
data:{
month:8,
pi:3.1415
}
});
</script>
- 局部过滤器
<div id="app01">
<h3>{{currenttime|date}}</h3>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
currenttime:Date.now()
},
//局部过滤器
filters:{
date:function(data){
var d = new Date(data);
return d.getFullYear()+'-'+d.getMonth()+'-'+d.getDate()+' '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds()
}
}
});
</script>
八、发送AJAX请求
1. 简介
vue本身不支持发送ajax请求,需要使用vue-resource(vue1.0)、axios(vue2.0官方推荐)等插件实现
2.使用axios发送AJAX请求
- 安装并引入axios
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
- 基本用法(github搜索axios查看api)
a. axios.get().then().catch()
// url自带参数
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// url和参数分开
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
b. axios.post().then().catch()
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
c. axios().then().catch()
//post
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
//get
axios('/user/12345');
九、Vue生命周期
vue从创建到销毁的过程,称为生命周期,公有8个阶段
<div id="app01">
<p>{{msg}}</p>
<button @click="update">更新组件</button>
<button @click="destroy">销毁组件</button>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
msg:'welcome to vue'
},
methods:{
update(){
this.msg="hello vue"
},
destroy(){
// this.$destroy();
app01.$destroy();
}
},
beforeCreate(){
alert('组件实例刚刚创建,还未进行数据观测和事件配置');
},
created(){
alert('实例已创建完成,并且已进行数据观测和事件配置');
},
beforeMount(){
alert('模板编译之前,还未挂载');
},
mounted(){
alert('模板编译之后,已挂载,此时才会渲染页面,才能看到页面上数据的展示');
},
beforeUpdate(){
alert('组件更新之前');
},
updated(){
alert('组件更新之后');
},
beforeDestroy(){
alert('组件销毁之前');
},
destroyed(){
alert('组件销毁之后');
}
});
</script>
十、计算属性(computed)
1. 基本用法
计算属性也是用来存储数据,但具有以下几个特点:
+ 数据可以进行逻辑梳理操作,对计算属性中的数据进行监视
<div id="app01">
<!-- 1.基本用法 -->
<p>{{msg}}</p>
<p>{{msg2}}</p>
<!-- 2.对数据处理再显示 -->
<p>{{msg.split('').reverse().join('')}}</p> <!-- 这样写也可以但是逻辑处理和页面展示混在一起不怎么好 -->
<p>{{reverseMsg}}</p>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{ //普通属性
msg:'welcome to vue!'
},
computed:{ //计算属性
msg2:function(){ //该函数必须有返回值,用来获取属性,称为get函数
return 'hell vue!'
},
reverseMsg(){
//可以包含逻辑处理,同时依赖msg
return this.msg.split('').reverse().join('');
}
}
});
</script>
2. computed和methods的区别
将计算属性的get函数定义成一个方法也能实现类似的功能
区别:
计算属性是基于它的相关依赖更新的,没有相关依赖也行,但是那样就没啥意义,没必要使用计算属性;计算属性是缓存的,只要相关依赖没有改变,多次访问计算属性得到的值时之前缓存的结果,不会多次执行
<div id="app01">
<p>comput:{{comput}}</p>
<p>method:{{method()}}</p>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{ //普通属性
msg:'welcome to vue!'
},
computed:{ //计算属性
comput(){
this.msg.split();
return new Date();
}
},
methods:{
method(){
this.msg.split();
return new Date();
}
}
});
</script>
//打开浏览器调试窗口,调用app01.comput日期值不改变,改变app01.msg值,再调用app01.comput发现日期改变;无论是否改变app01.msg值,调用app01.method(),日期值都改变
3. get和set
计算属性由get和set俩部分组成,分别用来获取和设置计算属性,默认只有get,如果需要set,要自己添加
<div id="app01">
<p>fullname:{{fullName}}</p>
<button @click="change">改名</button>
</div>
<script type="text/javascript">
var app01 = new Vue({
el:'#app01',
data:{
firstName:'xu',
lastName:'haixiang'
},
/*
computed:{ //默认只有set方法
fullName:function(){
return this.firstName+this.lastName
}
},
*/
computed:{
fullName:{
get:function(){
return this.firstName+this.lastName
},
set:function(newValue){
var names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
},
methods:{
change:function(){
this.fullName="zhang yanyan";
}
}
});
</script>
十一、vue实例的属性和方法
1.常用属性
vm.$el
vm.$data
vm.$options
vm.$refs
<div id="app01">
<p>{{msg}}</p>
<h3 ref="hello">hello</h3> <!-- 定义ref属性元素 -->
<h3 ref="world">vue</h3>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
msg:'hello vue!'
},
name:'vue', //自定义属性
show:function(){ //自定义属性
console.log('show');
}
});
/*
属性
*/
//vm.属性名(vm.msg)获取data中的属性
//vm.$el获取vue实例关联的元素
//console.log(vm.$el);//DOM对象
//vm.$el.style.color='red';
//vm.$data //获取数据对象
//console.log(vm.$data);
//console.log(vm.$data.msg);
//vm.$options //获取自定义属性
// console.log(vm.$options)
// console.log(vm.$options.name)
// console.log(vm.$options.show())
//vm.$refs //获取所有添加ref属性的元素
// console.log(vm.$refs); //DOM对象
// console.log(vm.$refs.hello);
</script>
2. 方法
vm.$mount()
vm.$destroy()
vm.$nextTick()
<div id="app01">
<p ref="title">hello:{{name}}</p>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
name:'Tom'
},
});
/*
方法
*/
//vm.$mount() //手动挂载vue实例
/*
vm.$mount('#app01');
new Vue({ //这样写也行
data:{
msg:'hello vue'
}
}).$mount('#app01');
*/
//vm.$destroy(); //销毁实例
//vm.$nextTick(callback) //传入回调函数,在DOM更新完成后再执行回调函数;一般在修改数据之后使用该方法,以便获取更新后的DOM
/*
vm.name='汤姆'; //修改数据
//DOM还没更新完,vue实现响应式并不是数据发生改变之后DOM立即变化,需要一定的策略进行DOM更新,需要时间
console.log(vm.$refs.title.textContent); //打印hello:Tom,原因代码顺序执行太快,DOM还没更新完
vm.$nextTick(function(){
//回调函数里,DOM更新完成,再执行此代码
console.log(vm.$refs.title.textContent);
});
*/
</script>
vm.$set()
vm.$delete()
<div id="app01">
<button @click="doAdd()">添加属性</button>
<button @click="doDelete()">删除属性</button>
<hr>
<p>{{user.name}}</p>
<p>{{user.age}}</p>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
user:{
name:'Tom',
}
},
methods:{
doAdd(){
/*
this.user.age=10; //通过普通方式为对象添加属性时vue无法实时监测到
console.log(this.user); //虽然vue无法检测到,但是查看data中的user已经设置了age
*/
vm.$set(this.user,'age',18); //实例方法
// Vue.set(this.user,'age',18); //全局方法
console.log(this.user);
},
doDelete(){
vm.$delete(this.user,'age'); //实例方法
// Vue.delete(this.user,'age'); //全局方法
console.log(this.user);
}
}
});
</script>
vm.$watch()
<div id="app01">
<input type="text" v-model="name">
<p>{{name}}</p>
<hr>
<input type="text" v-model="age">
<p>{{age}}</p>
<hr>
<input type="text" v-model="user.height">
<p>{{user.height}}</p>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
name:'Tom',
age:20,
user:{
sex:'male',
height:180
}
},
watch:{ //方式一:使用vue实例提供的watch选项(更常用)
age:function(newValue,oldValue){ //针对字符串
console.log('age被修改了,原值:'+oldValue+',新值:'+newValue);
},
user:{ //针对对象,需要配置深度修改,该情况下newValue、oldValue值一直,因为引用没变
handler:function(newValue,oldValue){
console.log('user被修改了,原值:'+oldValue+',新值:'+newValue);
},
deep:true
}
}
});
/*
//方式二:使用vue实例提供的$watch()方法
vm.$watch('name',function(newValue,oldValue){ //针对字符串
console.log('name被修改了,原值:'+oldValue+',新值:'+newValue);
});
vm.$watch('user',function(newValue,oldValue){ //针对对象,需要配置深度修改,该情况下newValue、oldValue值一直,因为引用没变
console.log('user.name被修改了,原值:'+oldValue.height+',新值:'+newValue.height);
},{deep:true});
*/
</script>
十二、自定义指令
分类:全局指令,局部指令
1. 自定义全局指令
- 语法:Vue.directive(指令id,指令的定义对象);对象中的钩子函数bind、inserted、update、componentUpdated、unbind
<div id="app01">
<h3 v-hello>{{msg}}</h3>
<button @click="change">更新数据</button>
</div>
<script type="text/javascript">
/*
自定义全局指令
注:使用指令时,必须在指令前加v-,比如v-hello
*/
Vue.directive('hello',{
bind(){ //常用
alert('指令第一次绑定到元素上调用,只调用一次,课执行初始化操作');
},
inserted(){
alert('被绑定元素插入到DOM时调用');
},
update(){ //常用
alert('被绑定元素所在模板更新时调用');
},
componentUpdated(){
alert('被绑定元素所在模板完成一次更新周期时调用');
},
unbind(){alert('指令与模板解绑时调用')}
});
var vm = new Vue({
el:'#app01',
data:{
msg:'hello vue',
},
methods:{
change(){
this.msg="welcome to vue";
}
}
});
</script>
- 钩子函数的参数
<div id="app01">
<h3 v-hello>{{msg}}</h3>
<!-- 自定义指令可以指定字符串形式的指令表达式、值、参数、修饰符,如下分别对应username、xhx、confirm、(start,end) -->
<h4 v-hello:confirm.start.end="username">{{msg}}</h4>
</div>
<script type="text/javascript">
/*
钩子函数的参数(所有钩子函数一样)
*/
Vue.directive('hello',{
bind(el,binding){
// console.log(el); //指令所绑定的元素的DOM对象
// el.style.color='red';
console.log(binding); //指令对象
console.log(binding.name); //指令名
console.log(binding.expression); //指令表达式
console.log(binding.value); //值
console.log(binding.arg); //参数
console.log(binding.modifiers); //修饰符 对象
}
});
var vm = new Vue({
el:'#app01',
data:{
msg:'hello vue',
username:'xhx'
},
methods:{
change(){
this.msg="welcome to vue";
}
}
});
</script>
- 指令定义简单写法
//传入一个简单函数,在bind以及update钩子函数中都调用
Vue.directive('hello',function(){})
2. 定义局部指令
new Vue({
el:'#app01',
directives:{
focus1:function(el){el.focus()}, //简单写法
focus2:{ //复杂写法
bind(){},
inserted(el){el.focus()},
update(){}
}
}
});
3.自定义指令练习
移动页面中的元素(onmousedown,onmousemove,onmouseup)
<style>
#app01 div{
height: 100px;
width:100px;
position:absolute;
}
#app01 .hello{
background-color: blue;
left: 0px;
top: 0px;
}
#app01 .vue{
background-color: red;
top: 0px;
right: 0px;
}
</style>
<div id="app01">
<div class="hello" v-drag>hello</div>
<div class="vue" v-drag>vue</div>
</div>
<script type="text/javascript">
Vue.directive('drag',function(el){
el.onmousedown=function(e){
//获取鼠标点击处分别于div左边和上边的距离:鼠标位置-div位置
var disX=e.clientX-el.offsetLeft;
var disY=e.clientY-el.offsetTop;
// console.log(disX,disY);
//包含在onmousedown里面,表示点击后才移动
/*
el.onmousemove=function(e){
//获取鼠标移动后div的位置(div左上角位置)
var l=e.clientX-disX;
var t=e.clientY-disY;
el.style.left=l+'px';
el.style.top=t+'px';
};
//停止移动
el.onmouseup=function(e){
el.onmousemove=null;
el.onmouseup=null;
}
*/
//未防止鼠标移出div,使用document.onmousemove
document.onmousemove=function(e){
//获取鼠标移动后div的位置(div左上角位置)
var l=e.clientX-disX;
var t=e.clientY-disY;
el.style.left=l+'px';
el.style.top=t+'px';
};
//停止移动
document.onmouseup=function(e){
document.onmousemove=null;
document.onmouseup=null;
}
}
});
var vm = new Vue({
el:'#app01',
});
</script>
十三、过渡(动画)
1. 简介
vue在插入、更新或者移除dom元素时,提供多种不同方式的应用过渡效果
本质上还是使用css3动画:transition,animation
2. 基本用法
使用transition组件,将要执行动画的元素包含在该组件内
<transition>运动的元素</transition>
过渡的css类名6个:
v-enter //进入的初始状态
v-enter-active //过渡的过程当中
v-enter-to //进入最终需要变成的状态
v-leavte //离开的初始状态
v-leave-active //过渡的过程当中
v-leave-to //离开最终需要变成的状态
<style>
p{
height: 200px;
width: 200px;
background-color: red;
line-height: 200px;
text-align: center;
}
.v-enter-active,.v-leave-active{
transition:all 2s ease;
}
.v-enter-active{
opacity: 1;
width: 200px;
height: 200px;
}
.v-leave-active{
opacity: 0;
width: 50px;
height: 50px;
}
.v-enter{ /*需要放在最下面*/
opacity: 0;
width: 0px;
height: 0px;
}
</style>
<div id="app01">
<button @click="flag=!flag">click</button>
<!-- 切换没有动画效果 -->
<!-- <p v-show="flag">hell vue</p> -->
<transition>
<!-- 当给transiton设置name属性时,上面的v-enter需要写成fade-enter、v-enter-active写成fade-enter-active等 -->
<!-- <transition name="fade"> -->
<p v-show="flag">hell vue</p>
</transition>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
flag:false,
}
});
</script>
3. 钩子函数
钩子函数都可以传入参数el(元素对象)
beforeEnter
enter
afterEnter
enterCancelled
beforeLeave
leave
afterLeave
leaveCancelled
<style>
p{
height: 200px;
width: 200px;
background-color: red;
line-height: 200px;
text-align: center;
}
.fade-enter-active,.fade-leave-active{
transition:all 2s ease;
}
.fade-enter-active{
opacity: 1;
width: 200px;
height: 200px;
}
.fade-leave-active{
opacity: 0;
width: 50px;
height: 50px;
}
.fade-enter{ /*需要放在最下面*/
opacity: 0;
width: 0px;
height: 0px;
}
</style>
<div id="app01">
<button @click="flag=!flag">click</button>
<transition name="fade"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<p v-show="flag">hell vue</p>
</transition>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
flag:false,
},
methods:{
beforeEnter(el){
// alert('动画进入之前');
},
enter(el){
// alert('动画进入');
},
afterEnter(el){
// alert('动画进入之后');
el.style.background='blue';
},
enterCancelled(el){
// alert('动画进入之后取消');
},
beforeLeave(el){
// alert('动画离开之前');
},
leave(el){
// alert('动画离开');
},
afterLeave(el){
// alert('动画离开之后');
el.style.background='red';
},
leaveCancelled(el){
// alert('动画离开之后取消');
}
}
});
</script>
4. 自定义过渡类名
enter-class
enter-active-class
enter-to-class
leave-class
leave-active-class
leave-to-class
它们优先级高于普通的类名,结合第三方css动画库,如Animated.css
Animated.css下载地址
<link rel="stylesheet" type="text/css" href="css/animate.css">
<style>
p{
height: 200px;
width: 200px;
background-color: red;
line-height: 200px;
text-align: center;
margin: 0 auto;
}
</style>
<div id="app01">
<button @click="flag=!flag">click</button>
<transition enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
<p v-show="flag">hell vue</p>
</transition>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
flag:false,
},
});
</script>
5. 多元素动画
transition-group子元素必须绑定key
<transition-group></transition-group>
<link rel="stylesheet" type="text/css" href="css/animate.css">
<style>
p{
height: 200px;
width: 200px;
background-color: red;
line-height: 200px;
text-align: center;
margin: 50px auto;
}
</style>
<div id="app01">
<button @click="flag=!flag">click</button>
<transition-group enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
<p v-show="flag" :key='1'>hello vue</p> //必须绑定key
<p v-show="flag" :key='2'>welcome to vue</p> //必须绑定key
</transition-group>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
flag:false,
},
});
</script>
示例
<link rel="stylesheet" type="text/css" href="css/animate.css">
<style>
p{
height: 100px;
width: 100px;
background-color: red;
line-height: 100px;
text-align: center;
margin: 10px auto;
}
</style>
<div id="app01">
<input type="text" v-model='name'>
<transition-group enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
<p v-for="(v,k) in arr2" :key="k">{{v}}</p>
</transition-group>
</div>
<script type="text/javascript">
var vm = new Vue({
el:'#app01',
data:{
flag:false,
arr1:['xhx','zyy','xzt','xyh','zaz'],
name:''
},
computed:{
arr2:function(){
var arr=[];
this.arr1.forEach(val => { //箭头函数不会改变this
if(val.includes(this.name)){
arr.push(val);
}
});
/*
this.arr1.forEach(function(val){
if(val.includes(this.name)){
arr.push(val);
}
});
*/
return arr;
}
}
});
</script>
十四、组件
1. 什么是组件
组件(component)是vue.js最强大的功能之一,组件可以扩展html元素,封装可重用的代码,组件是自定义元素(对象)
2. 组件的定义方式
- 方式一:先创建组件构造器,然后由组件构造器创建组件
<!-- 1.创建构造器(Vue.extend) 2.注册组件 3.vue实例 -->
<div id="app01">
<my-hello></my-hello>
</div>
<script type="text/javascript">
var MyHello = Vue.extend({
template:'<h3>hello</h3>'
});
Vue.component('my-hello',MyHello)
new Vue({
el:'#app01',
})
</script>
- 方式二:直接创建组件
<div id="app02">
<my-world></my-world>
</div>
<script type="text/javascript">
Vue.component('my-world',{
template:'<h4>world</h4>'
});
new Vue({
el:'#app02',
});
</script>
3. 组件的类型
- 全局组件
<div id="app03">
<my-component></my-component>
</div>
<script type="text/javascript">
Vue.component('my-component',{
template:'<h3>全局组件</h3>'
});
var app03 = new Vue({
el:'#app03'
});
</script>
- 局部组件
<div id="app04">
<my-component></my-component>
</div>
<script type="text/javascript">
var child = {template:'<h4>局部组件</h4>'};
var app02 = new Vue({
el:'#app04',
components:{
'my-component':child
}
});
</script>
4. 引用模板
将组件的内容放到模板中并引用,通常组件比较复杂时可以利用template标签;注意事项:template中有且只能有一个根元素
<div id="app05">
<my-test></my-test>
</div>
<template id="wbs">
<!--这样写不行,组件的模板必须只能包含一个根元素
<h3>{{msg}}</h3>
<ul>
<li v-for="val in arr">{{val}}</li>
</ul>
-->
<div>
<h3>{{msg}}</h3>
<ul>
<li v-for="val in arr">{{val}}</li>
</ul>
</div>
</template>
<script type="text/javascript">
var app05 = new Vue({
el:'#app05',
components:{
'my-test':{
// template:'<h3>{{msg}}</h3>',
template:'#wbs',
// data:{msg:'welcome to test!'} 组件中的data必须为function
data:function(){
return {
msg:'welcome to test!',
arr:['Tom','Jack','David']
}
}
// data(){
// return {msg:'welcome to test!'}
// } //这种写法也可以
}
}
})
</script>
5.动态组件
<component v-bind:is="">组件,多个组件使用同一个挂载点,然后动态的在他们之间切换
<div id="app06">
<button v-on:click="flag='my-hello'">显示hello组件</button>
<button @click="flag='my-world'">显示world组件</button>
<div>
<!-- <component v-bind:is="flag"></component> -->
<component :is="flag"></component>
<!-- 使用<keep-alive>组件缓存非活动组件,可以保留状态避免重新渲染,默认每次切换都会销毁非活动组件并重新创建-->
<keep-alive> //
<component :is="flag"></component>
</keep-alive>
</div>
</div>
<script type="text/javascript">
var app06 = new Vue({
el:'#app06',
data:{
flag:'my-hello'
},
components:{
'my-hello':{
template:'<h3>show hello:{{x}}</h3>',
data(){
return {
x:Math.random()
}
}
},
'my-world':{
template:'<h3>show world:{{y}}</h3>',
data:function(){
return {y:Math.random()}
}
}
}
})
</script>
6. 组件间数据传递
6.1父子组件
一个组件内部定义另一个组件;子组件只能在父组件里使用;默认情况下子组件无法访问父组件中的东西
<div id="app07">
<h3>app07</h3>
<my-hello></my-hello>
<!-- //子组件(局部组件)不能定义在根组件中 -->
<!-- <my-world></my-world> -->
</div>
<template id='hello-app07'>
<div>
<h3>我是父组件hello</h3>
<h4>访问自己的数据:{{msg}},{{name}},{{age}},{{user.id}}</h4>
<hr>
<!-- 子组件只能定义在父组件中 -->
<my-world></my-world>
</div>
</template>
<template id="world-app07">
<div>
<h5>我是子组件world</h5>
<hr>
<hr>
<!-- 报错,访问不了 -->
<!-- <h5>访问父组件的数据:{{msg}},{{name}},{{age}},{{user.id}}</h5> -->
</div>
</template>
<script type="text/javascript">
var app07 = new Vue({ //根组件
el:'#app07',
components:{
'my-hello':{ //父组件
template:'#hello-app07',
data(){
return {
msg:'hi,Tom',
name:'Tom',
age:30,
user:{id:101,username:'test'}
}
},
components:{
'my-world':{ //子组件
template:'#world-app07',
}
}
}
}
})
</script>
6.2 子组件访问父组件的数据
a.在调用子组件时,绑定想要获取的父组件的数据
b.在子组件内部,使用props选项获取数据,即接受来自父组件的数据
总结:父组件通过props向下传递数据给子组件
注:组件中的数据共有三种方式:data,props,computed
<div id="app08">
<h3>app08</h3>
<my-hello></my-hello>
</div>
<template id='hello-app08'>
<div>
<h3>我是父组件hello</h3>
<h4>访问自己的数据:{{msg}},{{name}},{{age}},{{user.id}}</h4>
<hr>
<!-- 子组件只能定义在父组件中 -->
<my-world :message="msg" :name="name" :age="age" :user="user"></my-world>
</div>
</template>
<template id="world-app08">
<div>
<h5>我是子组件world</h5>
<h5>访问父组件的数据:{{message}},{{name}},{{age}},{{user.id}}</h5>
<hr>
<hr>
</div>
</template>
<script type="text/javascript">
var app08 = new Vue({ //根组件
el:'#app08',
components:{
'my-hello':{ //父组件
template:'#hello-app08',
data(){
return {
msg:'hi,Tom',
name:'Tom',
age:30,
user:{id:101,username:'test'}
}
},
components:{
'my-world':{ //子组件
template:'#world-app08',
// props:['message','name','age','user'] //简单的字符串数组
props:{ //也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值
message:String,
name:{
type:String,
required:true
},
age:{
type:Number,
default:18,
validator:function(value){
return value>=0;
}
},
// user:Object
user:{
type:Object,
// default:{id:102,username:'lalala'} //报错,对象这么写有问题,对象或数组的默认值必须由函数返回
default:function(){
return {id:102,username:'lalala'};
};
}
}
}
}
}
}
})
</script>
6.3父组件访问子组件中的数据
a.在子组件中使用vm.$emit(事件名,数据)事件名自定义
b.父组件在使用子组件的地方监听子组件触发的事件,并在父组件中定义方法,用来获取数据
总结:子组件通过event给父组件发消息,实际上就是子组件吧自己的数据发送到父组件
<div id="app09">
<h3>app09</h3>
<my-hello></my-hello>
</div>
<template id='hello-app09'>
<div>
<h3>我是父组件hello</h3>
<h4>访问自己的数据:{{msg}},{{name}},{{age}},{{user.id}}</h4>
<h4>访问子组件中的数据:{{sex}},{{height}}</h4>
<hr>
<!-- 子组件只能定义在父组件中 -->
<my-world :message="msg" :name="name" :age="age" :user="user" @event="getData"></my-world>
</div>
</template>
<template id="world-app09">
<div>
<h5>我是子组件world</h5>
<h5>访问父组件的数据:{{message}},{{name}},{{age}},{{user.id}}</h5>
<h5>访问自己的数据:{{sex}},{{height}}</h5>
<button @click="send">将子组件的数据向上传递给父组件</button>
<hr>
<hr>
</div>
</template>
<script type="text/javascript">
var app09 = new Vue({ //根组件
el:'#app09',
components:{
'my-hello':{ //父组件
methods:{
getData(sex,height){
this.sex=sex; //需要先初始化
this.height=height; //需要先初始化
}
},
template:'#hello-app09',
data(){
return {
msg:'hi,Tom',
name:'Tom',
age:30,
user:{id:101,username:'test'},
sex:'', //初始化sex
height:'', //初始化height
}
},
components:{
'my-world':{ //子组件
methods:{
send(){
// console.log(this) //this代表的是当前这个子组件
this.$emit('event',this.sex,this.height); //使用$emit()触发一个事件,发送数据,事件名随便取,这里我们叫event
}
},
data(){
return {
sex:'male',
height:180
}
},
template:'#world-app09',
// props:['message','name','age','user'] //简单的字符串数组
props:{ //也可以是对象,允许配置高级设置,如类型判断,数据校验,设置默认值
message:String,
name:{
type:String,
required:true
},
age:{
type:Number,
default:18,
validator:function(value){
return value>=0;
}
},
// user:Object
user:{
type:Object,
// default:{id:102,username:'lalala'} //报错,对象这么写有问题,对象或数组的默认值必须由函数返回
default:function(){
return {id:102,username:'lalala'}
}
}
}
}
}
}
}
})
</script>
7. 单向数据流
props是单向绑定的,当父组件的属性发生变化时,将传导给子组件,但是不会反过来,而且不允许子组件直接修改父组件中的数据,会报错
解决方式:
方式一:如果子组件想把它作为局部数据来用,可以将数据存入一个变量中再操作,不影响父组件中的数据
方式二:如果子组件想修改数据并同步到父组件,俩个方法:
a.使用.sync(1.0版本支持,2.0版本中不支持,2.3版本又开始支持)
需要显式地触发一个更新事件
b.可以将父组件中的数据包装成对象,然后在子组件中修改对象的属性(因为对象是引用类型,指向同一个内存空间) 更常用,推荐
<div id="app10">
<h2>app10</h2>
<h2>父组件:{{name}}</h2>
<input v-model='name'> <!-- 可以任意修改数据并影响子组件 -->
<my-hello :name='name'></my-hello>
<hr>
<hr>
</div>
<template id="hello-app10">
<div>
<h3>子组件:{{name}}</h3>
<button @click='change'>修改数据</button> <!-- 修改数据会报错 -->
</div>
</template>
<script type="text/javascript">
var app10 = new Vue({
el:'#app10',
data:{
name:'xhx',
},
components:{
'my-hello':{
template:'#hello-app10',
props:['name'],
methods:{
change(){
this.name='zyy';
}
}
}
}
});
</script>
7.1 如果子组件想把它作为局部数据来用,可以将数据存入一个变量中再操作,不影响父组件中的数据
<div id="app11">
<h2>app11</h2>
<h2>父组件:{{name}}</h2>
<input v-model='name'> <!-- 可以任意修改数据并影响子组件 -->
<my-hello :name='name'></my-hello>
<hr>
<hr>
</div>
<template id="hello-app11">
<div>
<h3>子组件:{{username}}</h3>
<button @click='change'>修改数据</button>
</div>
</template>
<script type="text/javascript">
var app11 = new Vue({
el:'#app11',
data:{
name:'xhx',
},
components:{
'my-hello':{
template:'#hello-app11',
props:['name'],
data(){
return{
username:this.name //将数据存入一个新的变量中再操作
}
},
methods:{
change(){
this.username='zyy';
}
}
}
}
});
</script>
7.2使用.sync使得子组件可以修改父组件中的数据,需要2个步骤:a.绑定props时加上.sync,b.修改时使用$emit方法修改
<div id="app12">
<h2>app12</h2>
<h2>父组件:{{name}}</h2>
<input v-model='name'> <!-- 可以任意修改数据并影响子组件 -->
<!-- <my-hello :name='name'></my-hello> -->
<my-hello :name.sync='name'></my-hello> <!-- 加入.sync -->
<hr>
<hr>
</div>
<template id="hello-app12">
<div>
<h3>子组件:{{name}}</h3>
<button @click='change'>修改数据</button>
</div>
</template>
<script type="text/javascript">
var app12 = new Vue({
el:'#app12',
data:{
name:'xhx',
},
components:{
'my-hello':{
template:'#hello-app12',
props:['name'],
methods:{
change(){
// this.name='zyy';
this.$emit('update:name','zyy') //不能直接赋值了需要这样固定格式写法,第一个参数'update:修改的变量名',第二个参数变量新的值
}
}
}
}
});
</script>
7.3 通过将父组件中的数据包装成对象,把对象传给子组件,在子组件中修改对象的属性来实现在子组件中修改父组件的数据(更常用)
<div id="app13">
<h2>app13</h2>
<h3>父组件:{{name}}</h3>
<input v-model='name'>
<input v-model='user.sex'>
<h3>父组件:{{user.age}},{{user.sex}}</h3>
<my-hello :name='name' :user='user'></my-hello>
<hr>
<hr>
</div>
<template id="hello-app13">
<div>
<h3>子组件:{{name}}</h3>
<h3>子组件:{{user.age}},{{user.sex}}</h3>
<button @click='change'>修改数据</button>
</div>
</template>
<script type="text/javascript">
var app13 = new Vue({
el:'#app13',
data:{
name:'xhx',
user:{sex:'male',age:29}, //将父组件中的数据包装成对象
},
components:{
'my-hello':{
template:'#hello-app13',
props:['name','user'],
methods:{
change(){
this.user.age=18; //在子组件中修改对象的属性
this.user.sex='fmale';
}
}
}
}
});
</script>
8. 非父子组件间通信
可以通过一个空的vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件
<div id="app14">
<h2>app14</h2>
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
<hr>
<hr>
</div>
<template id="a">
<div>
<h3>A组件:{{name}}</h3>
<button @click='send'>发送数据给C</button> <!-- 触发事件 -->
</div>
</template>
<template id="b">
<div>
<h3>B组件:{{age}}</h3>
<button @click='send'>发送数据给C</button> <!-- 触发事件 -->
</div>
</template>
<template id="c">
<div>
<h3>C组件:{{name}},{{age}},{{sex}}</h3>
</div>
</template>
<script type="text/javascript">
//定义一个空的vue实例,触发以及侦听事件的时候都通过这个实例来实现
var Event = new Vue();
var A = {
template:'#a',
data(){
return {
name:'Tom'
}
},
methods:{
send(){
Event.$emit('data-a',this.name); //通过空vue实例Event,触发事件,data-a为事件名,随便定义
}
}
};
var B = {
template:'#b',
data(){
return {
age:18
}
},
methods:{
send(){
Event.$emit('data-b',this.age) //触发事件
}
}
};
var C = {
template:'#c',
data:function(){
return {
name:'',
age:'',
sex:'male'
}
},
mounted(){
Event.$on('data-a',name => { //通过空的vue实例Event监听事件,事件名为上面定义的触发的事件名
console.log(this); //this指向的是C
this.name = name;
});
// Event.$on('data-b',function(age){
// console.log(this); //this指向的是Event
// })
Event.$on('data-b',age => {
this.age = age;
});
}
};
var app14 = new Vue({
el:'#app14',
components:{
'my-a':A,
'my-b':B,
'my-c':C
}
});
</script>
9. slot内容分发
作用用来获取组件中原来的内容
+ 不带名字的slot
<div id="app15">
<h2>app15</h2>
<!-- <my-hi>原来内容</my-hi> -->
<my-hi></my-hi>
<hr>
<hr>
</div>
<template id="my-hi-app15">
<div>
<h3>welcome to app15!</h3>
<slot>如果没有原内容则显示该内容</slot>
</div>
</template>
<script type="text/javascript">
var app15 = new Vue({
el:'#app15',
components:{
'my-hi':{
template:'#my-hi-app15'
}
}
});
</script>
- 带有名字的slot
<div id="app16">
<h2>app16</h2>
<my-hi>
<ol slot='s1'>
<li>111</li>
<li>222</li>
<li>333</li>
</ol>
<ul slot='s2'>
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
</ul>
<ul type="circle">
<li>square</li>
<li>circle</li>
<li>disc</li>
</ul>
</my-hi>
<hr>
<hr>
</div>
<template id="my-hi-app16">
<div>
<slot name='s2'></slot>
<h3>welcome to app15!</h3>
<slot name='s1'></slot>
<hr>
<slot></slot>
</div>
</template>
<script type="text/javascript">
var app16 = new Vue({
el:'#app16',
components:{
'my-hi':{
template:'#my-hi-app16'
}
}
});
</script>