父组件向子组件传值
- 组件实例定义方式,注意:一定要使用
props属性来定义父组件传递过来的数据:
<script>
var vm=new Vue({
el:'#app',
data () {
return{
msg:'这是父组件中的消息'
}
},
methods:{
},
components:{
son:{
template:'<h1>这是子组件 --- {{finfo}}</h1>',
props:['finfo']
}
}
});
</script>
- 使用
v-bind或简化指令,将数据传递到子组件中:
<div id="app">
<son :finfo="msg"></son>
</div>
代码实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
</head>
<body>
<div id="app">
<!-- 父组件可以再引用子组件的时候,通过属性绑定(v-bind:)的形式 ,
把咱们需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用-->
<com1 v-bind:parentmsg="msg"></com1>
</div>
<script>
var vm = new Vue({
el: '#app',
data() {
//注意:子组件中的data数据,并不是通过父组件传递过来的,而是子组件自身私有的
//data上的数据,都是可读可写的
return {
msg: '123 啊-父组件中的数据'
}
},
methods: {
},
components: {
//结论:经过演示,发现子组件中,默认无法访问到父组件中的data上的数据和methods中的方法
com1: {
template: '<h1 @click="change">这是子组件 --- {{parentmsg}}</h1>',
//注意:组件中的所有props中的数据,都是通过父组件传递给子组件的
//props中的数据,都是只读的,无法重新赋值
props: ['parentmsg'], //把父组件传递过来的parentmsg属性,在props数组中定义一下,这样才能使用这个数据
methods:{
change(){
this.parentmsg = "被修改了"
}
}
}
},
});
</script>
</body>
</html>
父组件向子组件传递方法,并且把子组件中的值带回父组件
代码实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
</head>
<body>
<div id="app">
<!-- 父组件向子组件传递方法,使用的是事件绑定机制 v-on,当我们自定义了一个事件属性之后
那么子组件就能够通过某种方式,来调用传递进去的这个方法了 -->
<com2 @func="show"></com2>
</div>
<template id="tmp1">
<div>
<h1>这是子组件</h1>
<input type="button" value="这是子组件上的按钮,点击它,触发父组件传递过来的func方法" @click="myclick">
</div>
</template>
<script>
//定义了一个字面量类型组件模板类型
let com2 = {
template:'#tmp1',//通过指定一个Id,表示说要去加载这个指定id的template元素中的内容,当做组件的HTML结构
data(){
return {
sonmsg:{name:'小头儿子', age: 6}
}
},
methods:{
myclick(){
//当点击子组件的按钮的时候,如何拿到父组件传递过来的func方法,并调用呢?
//emit 英文原译:是触发,调用的意思
this.$emit('func', this.sonmsg)
}
}
}
var vm=new Vue({
el:'#app',
data () {
return{
datamsgFormSon:null
}
},
methods:{
show(data){
console.log("调用了父组件身上的show方法:---" + data)
this.datamsgFormSon = data
}
},
components:{
com2
}
});
</script>
</body>
</html>
组建案例-评论列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
<cmt-box @func="loadComments"></cmt-box>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item.id">
<span class="badge">评论人: {{item.user}}</span>
{{item.content}}
</li>
</ul>
</div>
<template id="tmp1">
<div>
<div class="form-group">
<label>评论人:</label>
<input type="text" class="form-control" v-model="user">
</div>
<div class="form-group">
<label>评论内容:</label>
<textarea class="form-control" v-model="content"></textarea>
</div>
<div class="form-group">
<input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
</div>
</div>
</template>
<script>
let commentBox = {
template: '#tmp1',
data(){
return {
user:'',
content:'',
}
},
methods:{
postComment(){//发表评论的方法
//分析:发表评论的业务逻辑
//1.评论数据存到哪里去??? 存到了localStorage中
//2.先组织出一个最新的评论数据对象
//3.想办法把第二步得到的评论对象,保存到localStorage中
// 3.1 localStorage只支持存放字符串数据,要优先调用JSON.stringify
// 3.2 在保存最新的评论 数据之前,要先从localStorage获取到之前的评论数据(string),
// 转换为一个数组对象,然后把最新的评论,push到这个数组
// 3.3 如果获取到的localStorage中的评论字符串为空不存在,则可以返回一个'[]' 让JSON.parse去转换
// 3.4 把最新的评论列表数组,再次调用JSON.stringify转换为数组字符串,然后调用localStorage.setItem()
// this.list.push()
let comment = {id:Date.now, user:this.user, content:this.content}
//从localStorage中获取所有评论
let list = JSON.parse(localStorage.getItem('cmts') || '[]')
list.unshift(comment)
//重新保存最新的评论数据
localStorage.setItem('cmts', JSON.stringify(list))
this.user = this.content = ''
this.$emit('func')
}
}
}
var vm = new Vue({
el: '#app',
data() {
return {
list: [
{ id: Date.now(), user: '李白', content: '天生我材必有用' },
{ id: Date.now(), user: '江小白', content: '劝君更尽一杯酒' },
{ id: Date.now(), user: '小马', content: '我姓马,风吹草低见牛羊的马' },
]
}
},
methods: {
loadComments(){//从本地 localStorage加载评论列表
let list = JSON.parse(localStorage.getItem('cmts') || '[]')
this.list = list
}
},
components: {
'cmt-box': commentBox
},
created() {
this.loadComments()
},
});
</script>
</body>
</html>
最终效果
使用 this.$refs 来获取元素和组件
可以直接使用this.$refs来调用子组件中的属性和方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
</head>
<body>
<div id="app">
<input type="button" value="获取元素" @click="getElement" ref="btn">
<h3 ref='myh3'>哈哈哈,今天天气太好了!!!</h3>
<hr>
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>',
data(){
return {
msg:'son msg'
}
},
methods:{
show(){
console.log('调用了子组件的方法')
}
}
}
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
getElement() {
// ref 是英文单词 [reference] 值类型和引用类型 referenceError
// console.log(this.$refs.myh3.innerText)
// console.log(this.$refs.mylogin.msg)
this.$refs.mylogin.show()
}
},
components: {
login
}
});
</script>
</body>
</html>
什么是路由
- 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
- 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;
- 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);
在Vue中使用vue-router
- 导入vue-router组件类库
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
- 使用router-link组件来导航
<!-- router-link 默认渲染为一个a标签,使用tag可以更改默认渲染标签 -->
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
- 使用router-view组件来显示匹配到的组件
<!-- 这是 vue-router提供的元素,专门用来当做占位符的,
将来路由规则匹配到的组件,就会展示到这个vue-router中去,所以我们可以把它认为是一个占位符-->
<router-view></router-view>
- 创建使用
VueRouter创建组件
//2. 创建一个路由对象,当导入vue-router包之后,在window全局对象中,就有了一个路由的构造函数,叫做VueRouter
// 在new路由对象的时候,可以为
const routerObj = new VueRouter({
//route这个配置对象中的route表示【路由匹配规则】的意思
routes: [//路由匹配规则
//每个路由都是一个对象,这个规则对象,身上有必须的属性
//属性1是path,表示监听哪个路由连接地址
//属性2是component,表示如果路由是前面匹配到的path,则展示component属性对应的那个组件
//注意:component的属性值必须是一个组件的模板对象,不能是属性的名称
{ path: "/login", component: login },
{ path: "/register", component: register },
]
})
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
},
router:routerObj//将路由规则对象,注册到我们的vm实例上,用来监听URL地址变化,然后展示对应的组件
});
实现路由默认高亮的代码实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
<!-- 1. 安装vue-router路由模块 -->
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<style>
.router-link-active, .myactive{
color: red;
font-weight: 800;
font-style: ittalic;
font-size: 80px;
text-decoration: underline;
background-color: green;
}
</style>
</head>
<body>
<div id="app">
<!-- <a href="#/login">登录</a>
<a href="#/register">注册</a> -->
<!-- router-link 默认渲染为一个a标签,使用tag可以更改默认渲染标签 -->
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
<!-- 这是 vue-router提供的元素,专门用来当做占位符的,
将来路由规则匹配到的组件,就会展示到这个vue-router中去,所以我们可以把它认为是一个占位符-->
<router-view></router-view>
</div>
<script>
let login = {
template: '<h1>登录组件</h1>'
}
let register = {
template: '<h1>注册组件</h1>'
}
//2. 创建一个路由对象,当导入vue-router包之后,在window全局对象中,就有了一个路由的构造函数,叫做VueRouter
// 在new路由对象的时候,可以为
const routerObj = new VueRouter({
//route这个配置对象中的route表示【路由匹配规则】的意思
routes: [//路由匹配规则
//每个路由都是一个对象,这个规则对象,身上有必须的属性
//属性1是path,表示监听哪个路由连接地址
//属性2是component,表示如果路由是前面匹配到的path,则展示component属性对应的那个组件
//注意:component的属性值必须是一个组件的模板对象,不能是属性的名称
{ path: "/", redirect:'/login' },//进行重定向,这里的redirect和Node中的redirect完全是两码事
{ path: "/login", component: login },
{ path: "/register", component: register },
],
linkActiveClass:'myactive'
})
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
},
router:routerObj//将路由规则对象,注册到我们的vm实例上,用来监听URL地址变化,然后展示对应的组件
});
</script>
</body>
</html>
为路由添加切换动画
- 使用
transition包裹router-view标签
<transition mode="out in">
<router-view></router-view>
</transition>
- 定义固定的动画样式
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(140px);
}
.v-enter-active,
.v-laeve-active {
transition: all 0.5s ease;
}
路由规则中实现参数的传递
方式一:使用query去获取参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>
<body>
<div id="app">
<!-- 如果在路由中,使用查询字符串,给路由传递参数,则不需要修改路由规则的path属性 -->
<router-link to="/login?=10&name=zs">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
let login = {
template: '<h1>登录 --- {{$route.query.id}} --- {{$route.query.name}}</h1>',
created() {//组件的生命周期钩子函数
console.log(this.$route.query.id)
},
data(){
return{
id : this.$route.query.id
}
}
}
let register = {
template: '<h1>注册</h1>'
}
let router = new VueRouter({
routes: [
{ path: '/login', component: login },
{ path: '/register', component: register }
]
})
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
},
router
});
</script>
</body>
</html>
方式二:使用params去获取数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>
<body>
<div id="app">
<!-- 如果在路由中,使用查询字符串,给路由传递参数,则不需要修改路由规则的path属性 -->
<router-link to="/login/10/lisi">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
let login = {
template: '<h1>登录 --- {{this.$route.params.id}} --- {{this.$route.params.name}}</h1>',
created() {//组件的生命周期钩子函数
console.log(this.$route)
},
data(){
return{
// id : this.$route
}
}
}
let register = {
template: '<h1>注册</h1>'
}
let router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: login },
{ path: '/register', component: register }
]
})
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
},
router
});
</script>
</body>
</html>
路由的嵌套
有可能我们在需求里面需要用到在一个路由里面嵌套其他的路由,这时候我们需要使用children去嵌套路由规则
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>
<body>
<div id="app">
<router-link to="/account">Account</router-link>
<router-view></router-view>
</div>
<template id="tmp1">
<div>
<h1>这是Account组件</h1>
<router-link to="/account/login">登录</router-link>
<router-link to="/account/register">注册</router-link>
<router-view></router-view>
</div>
</template>
<template id="tmp2">
<div>
<h3>这是登录组件</h3>
</div>
</template>
<template id="tmp3">
<div>
<h3>这是注册组件</h3>
</div>
</template>
<script>
//组件的模板对象
let account = {
template: "#tmp1"
}
let login = {
template: "#tmp2"
}
let register = {
template: "#tmp3"
}
const router = new VueRouter({
routes: [
{
path: '/account',
component: account,
//使用children实现子路由,同时子路由的path前面,不要带 / ,
//否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
children: [
{ path: 'login', component: login },
{ path: 'register', component: register },
]
},
// { path: '/account/login', component: login },
// { path: '/account/register', component: register },
]
})
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
},
router
});
</script>
</body>
</html>
命名视图实现经典布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src='https://cdn.bootcss.com/vue/2.6.10/vue.min.js'></script>
<script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
<style>
html, body{
margin: 0;
padding: 0;
}
.header{
background-color: orange;
height: 80px;
}
.left{
background-color: lightgreen;
flex:2
}
.main{
background-color: lightpink;
flex:8
}
h1{
margin: 0;
padding: 0;
font-size: 16px;
}
.container{
display: flex;
height: 600px;
}
</style>
</head>
<body>
<div id="app">
<router-view></router-view>
<div class = "container">
<router-view name = "left"></router-view>
<router-view name = "main"></router-view>
</div>
</div>
<script>
let header = {
template: '<h1 class = "header">Header头部区域</h1>'
}
let leftBox = {
template: '<h1 class = "left">Left头部区域</h1>'
}
let mainBox = {
template: '<h1 class = "main">Main头部区域</h1>'
}
//创建路由对象
const router = new VueRouter({
routes: [
// {path:'/', component:header},
// {path:'/left', component:leftBox},
// {path:'/main', component:mainBox},
{ path: '/', components: {
'default':header,
'left':leftBox,
'main':mainBox
}
},
]
})
var vm = new Vue({
el: '#app',
data() {
return {
}
},
methods: {
},
router
});
</script>
</body>
</html>