文章目录
1.vue动画
1.1 transition
使用transition元素,把需要动画控制的元素包裹起来,并且给动画的时间点和时间段设置样式
<style>
/*
*v-enter 进入之前的时间点,元素的起始状态,此时尚未开始进入动画
*v-leave-to 动画离开之后的时间点,离开的终止状态,元素动画已结束
*/
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(80px);//初始位位置,设置位移量
}
/*
*v-enter-active 入场时间段
*v-leave-active 离场时间段
*/
.v-enter-active,
.v-leave-active {
transition: all 0.4s ease;
}
</style>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<transition>
<h3 v-if="flag">this is H3</h3>
</transition>
</div>
上面这种方式会将所有的transition包裹的元素添加同样的动画,怎么才能够实现不同的transition实现不同的效果呢,只需要改变transition的’v-'前缀就行,同时给transition添加一个name属性的值,这个name属性的值就对应v-前缀中的v,也就是说v是vue中默认的。比如下面所示:
<style>
/*
*v-enter 进入之前的时间点,元素的起始状态,此时尚未开始进入动画
*v-leave-to 动画离开之后的时间点,离开的终止状态,元素动画已结束
*/
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(80px);//初始位位置,设置位移量
}
/*
*v-enter-active 入场时间段
*v-leave-active 离场时间段
*/
.v-enter-active,
.v-leave-active {
transition: all 0.4s ease;
}
/*
*arvin-enter 进入之前的时间点,元素的起始状态,此时尚未开始进入动画
*arvin-leave-to 动画离开之后的时间点,离开的终止状态,元素动画已结束
*/
.arvin-enter,
.arvin-leave-to {
opacity: 0;
transform: translateX(80px);//初始位位置,设置位移量
}
/*
*arvin-enter-active 入场时间段
*arvin-leave-active 离场时间段
*/
.arvin-enter-active,
.arvin-leave-active {
transition: all 0.4s ease;
}
</style>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag">
<transition>
<h3 v-if="flag">this is H3</h3>
</transition>
<input type="button" value="toggle2" @click="flag2=!flag2">
<transition name="arvin">
<h3 v-if="flag2">this is 2222H3</h3>
</transition>
</div>
1.2 动画钩子函数-实现半场动画(入场动画)
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled">
</transition>
只需要写入场钩子函数即可:
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
<div class="ball" v-show="addBall"></div>
</transition>
<script>
var vm = new Vue({
el: '#app',
data: {
addBall: true,
flag: false,
flag2: false
},
methods: {
beforeEnter(el) { //动画入场之前设置元素未入场的起始样式
el.style.transform = "translate(0,0)";
},
enter(el,done) { // 动画开始之后的样式,可以设置完成动画之后的结束状态
el.offsetWidth;//这一句必须加否则无过渡效果,还可以写成el.offsetRight/el.offsetTop/el.offsetBottom (写一个就行)。
el.style.transform = "translate(150px,500px)";
el.style.transition = 'all 1s ease';//过渡时间设置为1秒
//如果想让动画执行完立即执行afterEnter需要执行done() done 其实就是afterEnter函数的引用
done();
},
afterEnter(el) {
this.flag = !this.flag;
}
}
});
</script>
注意:动画钩子函数的第一个参数el表示要执行动画的DOM元素,是原生的JS DOM 对象即可认为是el = document.getElementById(’’);
1.3 列表动画
在实现列表过渡的时候,如果需要过渡的元素通过v-for循环渲染出来的,不能使用transition包裹,需要使用transition-group,要为v-for循环创建的元素设置动画,必须为每一个元素设置 :key属性
<style>
li {
border: 1px dashed #999999;
margin: 6px;
line-height: 38px;
padding-left: 6px;
font-size: 16px;
}
li:hover {
background-color: lightblue;
transition: all 0.4s ease;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(80px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.6s ease;
}
.v-move {//.v-move 和 .v-leave-active 可以设置元素移动时的渐变效果,也就是删除时下面的元素移动的效果
transition: all 0.6s ease;
}
.v-leave-active {//只设置.v-move 是不行的,还要设置.v-leave-active 为相对定位
position:absolute
}
</style>
<div id="app">
<div>
<label>
Id:
<input type="text" name="" id="" v-model="id">
</label>
<label>
Name:
<input type="text" name="" id="" v-model="name">
</label>
<input type="button" value="添加" @click="add">
</div>
<ul>
<transition-group>
<li v-for="item in list" :key="item.id">
{{item.id}} - - - - - - {{item.name}}
</li>
</transition-group>
</ul>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
list: [{ id: 1, name: '比亚迪' }, { id: 2, name: '布加迪' }]
},
methods: {
add() {
this.list.push({ id: this.id, name: this.name });
}
}
});
</script>
2.Vue 创建组件的方式
2.1 Vue.extend
使用Vue.extend 创建Vue组件分为两步:
- 使用Vue.extend 创建一个全局的组件模板对象
- 使用Vue.component(‘组件的名称’,上一步创建的组件模板对象)// 此处组件名称采用驼峰命名
- 在页面中使用上一步组件名称小写且以短横线隔开
示例如下:
<div>
<my-Component></my-Component><!--第三步-->
</div>
<script>
//第一步
var comp = Vue.extend({
template:'<h1>这是使用Vue.extend创建的组件</h1>'//通过template属性,指定组件要展示的HTML结构
});
//第二步
Vue.component('myComponent',comp);
</script>
注意:template属性里面的模板内容只能有一个跟标签
2.2 Vue.component
Vue.component('myComponent2',{
template:'<h3>这是通过Vue.component 中直接添加一个对象的方式 创建的组件</h3>'
});
2.3 使用template标签的id
<div id = "app">
<my-cmp></my-cmp>
</div>
<template id='atmp'>
<div><!-- template 中只能有一个跟标签-->
<h4>
Hello H4 by template tag
</h4>
</div>
</template>
<scritpt>
Vue.component('myCmp',{
template: '#atmp';//在vue控制的app之外创建一个带有id的templaget标签,只需要将对应的id传入即可
})
</scritpt>
2.4 使用vue实例的components创建私有组件
在vue实例中添加components,
语法格式:
components:{
'组件名称':模板对象
}
示例如下:
var vm = new Vue({
el:'#app',
data: {},
methods:{},
directives:{},
components:{//定义实力内部私有组件
'login':{ //使用的时候只能在app中才能使用<login></login>
template:'<h3>这是私有的login组件</h3>'//使用template id的方式也可以的
}
}
});
3.组件中的data和methods
Vue.component('mycmp',{
template:'<h3>Hello h3 {{msg}}</h3>',//这里msg就是下面的内容
data: function(){
return {
msg:'这是组件中的data数据'
}
}
});
组件中的data是一个有返回值,且返回值是一个对象的方法;
将data定义成一个function是为了实现让每个组件实例只用自己的数据,而不会发生多个组件操作同一个数据的问题。
例如:
<body>
<div id="app">
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<template id="tmp">
<div>
<input type="button" value="+1" @click="increament">
<h3>{{count}}</h3>
</div>
</template>
<script>
//var dataObj = { count: 0 };//模拟data是一个对象而不是一个方法,这样当上面多个counter操作时,会相互影响
Vue.component('counter', {
template: '#tmp',
data: function () {
//return dataObj;//为了不受影响,将此行注释掉,换成下面的方式
return {count:0};//这样的话就不会相互影响了
},
methods: {
increament() {
this.count++;
}
}
})
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
</script>
</body>
4.组件之间的切换方式
4.1 v-if 和 v-else 实现两个组件之间的切换
通过使用v-if和v-else设置为同一个布尔变量来控制组件的显示就可以实现组件视觉上的切换
<div id="app">
<a href="" @click.prevent="flag=true">登录</a>
<a href="" @click.prevent="flag=false">注册</a>
<login v-if="flag"></login>
<register v-else="flat"></register>
</div>
<scritpt>
Vue.component('login',{
template:'<h3> 这是 login</h3>'
});
Vue.component('register',{
template:'<h4> 这是register </h4>'
});
</scritpt>
4.2 component 标签
Vue提供了component,来展示对应名称的组件。component是一个占位符,:is属性可以用来指定要展示的组件的名称。
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<component :is="comName"> </component>
</div>
<script>
Vue.component('login',{
template:'<h3>登录组件</h3>'
});
Vue.component('register',{
template:'<h3>注册组件</h3>'
});
var vm = new Vue({
el:'#app',
data:{
comName:'login';//comName是当前component中的 :is绑定的组件的名称
},
methods:{}
});
</script>
5.组件切换动画
只需要用transition标签包裹住component就可以了,如下
<style>
.v-enter,
.v-leave-to{
opacity:0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active{
transition:all 0.5s ease;
}
</style>
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<transition mode="out-in"><!-- mode是切换模式 out-in 是当前组件消失后下个组件再进入-->
<component :is="comName"> </component>
</transition>
</div>
<script>
Vue.component('login',{
template:'<h3>登录组件</h3>'
});
Vue.component('register',{
template:'<h3>注册组件</h3>'
});
var vm = new Vue({
el:'#app',
data:{
comName:'login';//comName是当前component中的 :is绑定的组件的名称
},
methods:{}
});
</script>
背景介绍
默认情况下子组件是无法访问父组件中的data和methods的:
<div id="app">
<comp></comp>
</div>
<script>
var vm = new Vue({
el:'#app',
data:{
msg:' This is father msg'
},
methods:{},
components:{
comp: {
template:'<h2>这是子组件--{{msg}}</h2>'//此处是无法访问到父组件中的data和methods的
}
}
});
</script>
1.父子组件之间传值
1.1父组件向子组件传值
在使用组件标签时,通过属性绑定的方式,将父组件的值传入到子组件
语法:
v-bind:自定义一个属性名称="传入的父组件中的变量名称"
但是该自定义的属性必须要在子组件中事先声明,需要在子组件中的props数组中定义
props:['自定义属性名称']
组件中的props数组数据全都是父组件传递给子组件的。props中的数据都是只读的,无法重新赋值。data中的数据是组件私有的可读可写的
实现方式:
<div id="app">
<comp v-bind:parentmsg="msg"></comp>
</div>
<script>
var vm = new Vue({
el:'#app',
data:{
msg:' This is father msg'
},
methods:{},
components:{
comp: {
data:{},//子组件中的data是自身私有的,不是父组件传入的。是可读可写的,比如子组件通过ajax请求的数据可以放在data上。
template:'<h2>这是子组件--{{msg}}</h2>',//此处是无法访问到父组件中的data和methods的
props:['parentmsg']//该数组中的数据,都是父组件传入的,并且都是只读的,父组件定义的属性名称,要在此声明,才能接收。
}
}
});
</script>
1.2.父组件向子组件传递方法使用事件绑定机制
语法:
组件标签中通过v-on:自定事件属性名称="父组件中的方法名称"
简写为@自定义属性名称="父组件中的方法名称"
子组件在对应事件触发入口通过
this.$emit('自定义属性名称')
实例:
<div id="app">
<comp @func="show"></comp>
</div>
<template id="temp">
<div>
<h1>这是子组件</h1>
<input type="button" value="调用父组件的方法" @click="mycompClick">
</div>
</template>
<script>
var vm = new Vue({
el:'#app',
data:{
msg:' This is father msg'
},
methods:{
show(){
}
},
components:{
comp: {
data:{},
template:'#temp',
methods:{
mycompClick(){
this.$emit('func');//这样就实现了对父组件中的方法的调用
//this.$emit('func',参数1,参数2)//实现调用父组件中的方法并传参,参数只要跟着方法名后面就行了,其实通过参数的这种方式也可以实现子组件向父组件传值。
}
}
}
}
});
</script>
1.3 子组件向父组件传值
上面已经说了,可以通过父组件向子组件传入一个有参数的方法的方式,可以实现父组件中接收该参数的该方式,来实现子组件向父组件传值。
2.ref快速获取元素
vue提供了ref属性可以帮助我们快速获取到元素,vue实例中有一个$refs 中就是元素
<div id="app">
<input type="button" value="获取元素内容" @click="getElement">
<h3 ref="myh3">This is H3 </h3>
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: '<h3>这是登录组件</h3>'
};
var vm = new Vue({
el:'#app',
data:{},
methods:{
getElement(){
console.log(this.$refs.myh3.innerText);
console.log(this.$refs.mylogin.innerText);
}
},
components:{
login
}
});
</script>
上面可以得到我们可以使用ref获取组件,因此可以通过$refs获取到组件,然后直接获取使用其数据和方法。
this.$refs.mylogin.msg;//直接调取其数据
this.$refs.mylogin.login();//直接调用其方法
3.路由
3.1 概念
后端路由
对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上的资源。
前端路由
对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的的内容,及#号后面的内容;所以,单页面程序中的页面跳转主要用hash实现。在单页面应用程序中,这种通过hash改变切换页面的方式,叫做前端路由。
3.2 vue-router安装的方式
3.2.1 直接下载/cdn - - 在网页里直接开发的方式
Unpkg.com提供了基于npm的cdn链接。https://unpkg.com/vue-router/dist/vue-router.js 该链接一致指向NPM发布的最新版本。你也可以像https://unpkg.com/vue-router@2.0.0/dist/vue-router.js这样指定版本号或者Tag。在Vue后面加载vue-router,它会自动安装的:
<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>
3.2.2 NPM - - webpack构建工具进行开发的方式
npm install vue-router
如果在一个模块化工程(比如说通过webpack构建的)中使用它,必须要通过Vue.use()明确的安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
如果使用全局的script标签,则无需如此(手动安装),即3.2.1的方式是不需要使用Vue.use(VueRouter)这种操作的。
3.3 vue-router 使用
3.3.1 router-view方式
安装vue-router之后便可使用,创建VueRouter实例对象,传入一个配置对象或者配置对象数组,配置对象即路由配置规则,每一个路由配置规则有两个属性:
- path : 表示监听的那个路由链接地址;
- component : **该属性的值只能放模板对象不能放组件名称. **表示 如果路由是前面匹配到的path,则展示component属性对应的组件。
vue实例通过router属性,建立与VueRouter实例对象的关系,router属性的租用是将路由规则对象VueRouter注册到vm实例上,用来监听URL地址的变化,然后展示对应的组件。页面中通过vue-router提供的元素router-view标签来进行展示,否则无效果,因为该标签是专门用来当做占位符的,路由规则匹配到的组件就会展示到这个router-view中去。所以我们可以认为router-view 是一个占位符。
实例代码如下:
<script src="libs/vue.js"></script>
<script src="libs/vue-router.js"></script>
<body>
<div id="app">
<a href="#/login">登录</a><!-- 因为vue中的路由是基于hash的所以必须要加#号 -->
<a href="#/register">注册</a><!-- 因为vue中的路由是基于hash的所以必须要加#号 -->
<router-view></router-view>
</div>
<script>
var login = {
template: '<h3>我是登录组件</h3>'
};
var register = {
template: '<h3>我是注册组件</h3>'
};
var routerObj = new VueRouter({//在创建路有对象的时候可以通过构造函数,传递一个配置对象
//route //这个配置对象中的route 表示路由配置规则
routes: [// 路由匹配规则,可以有多个,即每个按钮跳转的组件或者页面
{path:'/login',component:login},//每个路由规则都是一个对象,path属性表示监听的那个路由链接地址,component属性表示如果路由是前面匹配到的path,则展示component属性对应的组件。此处component 值只能写成login不能写成'login'
{path:'/register',component:register}
]//
});
var vm = new Vue({
el:'#app',
data:{},
methods:{},
router:routerObj//通过该属性与VueRouter对象建立关联关系
});
</script>
</body>
从上述实例中我们可以得出,其实路由的改变规则就是通过hash方式改变了地址栏(即通过修改URL地址,因为该URL地址已经注册到了VueRouter对象上了,路由规则对象会监听URL的改变进行对路由规则的匹配,一旦检测到对应的path与该URL匹配便展示该path对应的对象的component组件到reouter-view中去。)
3.3.2 router-link方式
和上面的区别在于不使用a标签,而换成router-link标签,router-link默认渲染成一个a标签的效果。
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
<script>
routes: [// 路由匹配规则,可以有多个,即每个按钮跳转的组件或者页面
{path:'/',redirect:'/login'},//这里的redirect重定向是前端的重定向,和后端的重定向是不同的,这里作用是让页面默认展示某个组件
{path:'/login',component:login},
{path:'/register',component:register}
]//
</script>
router-link 选中时默认样式为router-link-active 类样式,所以可以通过该样式来修改被选中时的样式。
还可以通过设置链接激活时使用的CSS类名。默认值可以通过路由的构造选项linkActiveClass来全局配置
var routerObject = new VueRouter({
routes: [],
linkActiveClass:'myactive'
});
另外可以用transition标签将router-view包裹起来实现router-view的切换效果动画。
3.4 路由传参
3.4.1 通过$route.query方式获取查询字符串中的参数
如果在路由中使用查询字符串给路由传递参数,则不需要修改路由规则的path属性
<router-link to="/login?id=1101&name=张三"></router-link>
<router-view></router-view>
<script>
var login = {
template: '<h3>我是登录组件---{{$route.query.id}}---{{$route.query.name}}</h3>'
};
</script>
3.4.2通过$route.params方式获取参数
该方式需要再路由设置的时候指定参数
<div id="app">
<router-link to="/login/1201/张三">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template:'<h3>登录----{{$route.params.id}}---{{$route.params.name}}</h3>'
data(){
return {
msg:'123'
}
},
created(){//组件的生命周期函数
console.log(this.$route.params.id);
}
}
var router = new VueRouter({
routes:[
{path:'/login/:id/:name',component: login}//需要通过斜杠和冒号来指定参数
]
});
</script>
3.5 路由嵌套
通过route的children属性配置子路由
比如说一个account下嵌套login 和register两个组件,实现方式如下:
<script>
var router = new VueRouter({
routes:[
{
path:'/account',
component:account,
children:[//子路由的path 前面不要带斜线
{path:'login',component:login},//注意不能加斜线
{path:'login',component:register}//注意不能加斜线
]
}
]
});
</script>
3.6 命名视图
给视图组件router-view进行命名,然后通过route的components属性来制定对应视图组件要展示的组件。具体实例如下:
<div id="app">
<router-view></router-view>
<router-view name="left"></router-view><!-- 命名该视图组件为left-->
<router-view name="main"></router-view>
</div>
<script>
var header = {
template:'我是头部'
};
var leftBox = {
template:'我是左侧栏'
};
var mainBox = {
template:'我是主面板'
};
var router = new VueRouter({
routes:[
{
path:'/',
components:{
'default': header,//默认展示组件header
'left': leftBox,//试图组件名称为left的展示leftBox 组件
'main': mainBox
}
}
]
});
</script>
4.组件的watch属性
4.1 watch 监听数据变化
可以通过watch属性监视 data 中指定数据的变化,触发该属性中对应的function处理函数,对应的该function函数可以传入两个参数[newValue, oldValue] 分别对应的是输入改变后和输入改变前的数据内容。
<div id="app">
<input type = "text" v-model="firstName">
<input type = "text" v-model="lastName">
<input type = "text" v-model="fullName">
</div>
<script>
var vm = new Vue({
el: 'app' ,
data: {
firstName: '' ,
lastName: '' ,
fullName: ''
},
methods: {},
watch: {
'firstName' : function(){//当firstName中的值发生改变就会触发该function function(newValue,oldValue)// 参数可选
this.fullName = this.firstName + ' ' + this.lastName;
},
'lastName' : function(newValue){
this.fullName = this.firstName+ ' ' + newValue;//newValue 和 this.lastName 值是一样的
}
}
});
</script>
另一种实现上述功能的方式-- keyup
通过@keyup添加键盘抬起时的事件处理逻辑,实现数据的动态监听与改变。
<div id="app">
<input type = "text" v-model="firstName" @keyup="updateFullName">
<input type = "text" v-model="lastName" @keyup="updateFullName">
<input type = "text" v-model="fullName">
</div>
<script>
var vm = new Vue({
el: 'app' ,
data: {
firstName: '' ,
lastName: '' ,
fullName: ''
},
methods: {
updateFullName() {
this.fullName = this.firstName + ' ' + this.lastName;
}
}
});
</script>
但是,当上述事件改成路由的监听时,@keyup则无能为力了,这是就凸显了watch属性的作用了——watch监听路由地址的改变
4.2 watch监听路由地址的改变
思路:只需要监听到 $route.path 的值的改变也就是路由地址的值。
var vm = new Vue({
el:'#app',
data: {},
methods: {},
router,
watch: {
'$route.path': function(newValue,oldValue) {
//此处可以处理路由地址变更的逻辑
}
}
});
5.组件的computed属性
在computed属性中,可以定义一些计算属性,计算属性的本质就是一个方法,且是有返回值的一个方法,在使用这些计算属性的时候把方法的名称直接当做属性来使用,并不会把计算属性当做方法调用。
比如我们将上述watch属性的监听方式替换成computed的方式:
<div id="app">
<input type = "text" v-model="firstName">
<input type = "text" v-model="lastName">
<input type = "text" v-model="fullName">
</div>
<script>
var vm = new Vue({
el: 'app' ,
data: {
firstName: '' ,
lastName: '' //,
//fullName: '' //此处的data中的fullName删掉
},
methods: {},
computed: {
'fullName': function(){//将fullName的声明放在此处
return this.firstName + ' ' + this.lastName;
}
}
});
</script>
6.methods、watch 和 computed属性的对比
-
methods: 该属性内是一些方法,主要处理一些业务逻辑操作;
-
**watch:**内容组成方式是一个键(需要观察的表达式),值(是对应的回调函数),主要用于监听指定数据的变化或者是虚拟的数据(比如路由),从而进行某些具体的业务逻辑操作,可以看成是computed和methods的结合体;
-
computed: 它主要处理一些计算逻辑,且必须有返回值,且计算结果会被缓存,如果没有变更是不会重新计算的,主要被当做属性来使用;