背景介绍
默认情况下子组件是无法访问父组件中的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: 它主要处理一些计算逻辑,且必须有返回值,且计算结果会被缓存,如果没有变更是不会重新计算的,主要被当做属性来使用;