web前端框架vue基础四
学习大纲
一、组件通信
加入商品组件里面触发点击事件,需要更改头部组件里面的数据
思考:如何实现
回答:通过组件通信技术
语法
父组件->子组件:通过props接受父传递数据
步骤1:子通过props键来接受父传递过来的数据 props:['变量名1',...,'变量名n']
步骤2:父传递给子 父必须在调用子的组件上通过语法 v-bind:子props中的变量名="父data中的键"
子组件->父组件:通过$emit创建自定义事件发送数据
步骤1:子创建自定义事件 通过语法this.$emit(事件名称, 数据1, ..., 数据n)
步骤2:父调用事件获取数据 父必须在调用子的组件上触发事件 @自定义事件="函数名"
留心1:函数在父定义
留心2:底层会将自定义事件中的数据 传递给函数的形参
兄弟组件:通过EventBus(事件总线)
const eventBus = new Vue() // 相当于创建一个组件数据共享中心
// 发送数据: eventBus.$emit(自定义事件名称,数据1,....,数据n)
// 接受数据: eventBus.$on(自定义事件名称,处理函数)
组件通信(父-子)
- 需求
自定义组件fathertag输出:
<div><p>我是父组件,我儿子叫:小小明</p>【<p>我是子组件,我爸爸给我取名:小小明</p>】</div>
其中子组件sontag部分:【】
变量:sonName-小小明
为了更明显看出父子关系推荐:https://www.w3school.com.cn/tags/tag_fieldset.asp
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vue组建通信</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<fathertag></fathertag>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 自定义组件fathertag输出:
// <div><p>我是父组件,我儿子叫:小小明</p>【<p>我是子组件,我爸爸给我取名:小小明</p>】</div>
// 其中子组件sontag部分:【】
// 变量:sonName-小小明
// 声明fathertag组件
Vue.component("fathertag", {
// 声明模型数据
data() {
return {
// 调用的时候注意大小写
sonName: "小小明"
}
},
// 声明组件内容
template: `
<fieldset>
<legend>父组件</legend>
<div>
<p>我是父组件,我儿子叫:{{sonName}}</p>
<!-- 调用子组件 -->
<sontag v-bind:myName="sonName"></sontag>
</div>
</fieldset>
`,
// 声明子组件
components: {
// 键就是组件标识
sontag: {
// 通过props键来获取父传递的数据
props: ['myName'],
// 脚下留心:下props相当于下面data
// data() {
// return {
// // myName: 数据
// myName: 6666,
// }
// },
// 声明子组件内容
template: `
<fieldset>
<legend>子组件</legend>
<p>我是子组件,我爸爸给我取名:{{myName}}</p>
</fieldset>
`
}
}
})
let vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
组件通信(子-父)
- 需求
上述代码基础上,在父组件fn1方法中能够获取子的myName2数据
子创建getSonData事件
父通过showSonData方法获取
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<title>vue 组建通信</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<fathertag></fathertag>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 自定义组件fathertag输出:
// <div><p>我是父组件,我儿子叫:小小</p>【<p>我是子组件,我爸爸给我取名:小小</p>】</div>
// 其中子组件sontag部分:【】
// 变量:sonName-小小
// 声明fathertag组件
Vue.component("fathertag", {
// 声明模型数据
data() {
return {
// 调用的时候注意大小写
sonName: "小小",
mySonData: ""
}
},
// 声明组件内容
template: `
<fieldset>
<legend>父组件</legend>
<div>
<p>我是父组件,我儿子叫:{{sonName}}</p>
<!-- 调用子组件 -->
<sontag @getSonData="showSonDataFn" v-bind:myName="sonName"></sontag>
<p>
子的数据: {{ mySonData }}
</p>
</div>
</fieldset>
`,
// 声明父普通函数
methods: {
showSonDataFn(data) {
// console.log(data)
this.mySonData = data
}
},
// 声明子组件
components: {
// 键就是组件标识
sontag: {
// 简单理解:页面加载完毕 -> 组件渲染完毕会触发下面这个键
mounted() {
// 在这里面创建自定义事件
this.$emit("getSonData", this.myName2)
},
// 通过props键来获取父传递的数据
props: ['myName'],
// 脚下留心:下props相当于下面data
data() {
return {
// myName: 数据
// myName: 6666,
myName2: "小小"
}
},
// 声明子组件内容
template: `
<fieldset>
<legend>子组件</legend>
<p>我是子组件,我爸爸给我取名:{{myName}}</p>
</fieldset>
`
}
}
})
let vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
组件通信(兄弟)
- 需求
步骤1:分别定义mytag1和mytag2组件
步骤2:点击mytag1组件中的按钮,将mytag1组件中的msg变量传递给mytag2使用
- 代码
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vue组件通信</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<mytag1></mytag1>
<mytag2></mytag2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 创建一个组件数据共享中心
const eventBus = new Vue()
Vue.component("mytag1", {
// 声明组件模型数据
data() {
return {
msg: "webopenfather",
other: 66666
}
},
// 声明组件内容
template: `
<fieldset>
<legend>兄弟1</legend>
<button @click="sendDataFn">点击传递数据</button>
</fieldset>
`,
// 声明普通方法
methods:{
sendDataFn() {
// 发送数据
eventBus.$emit("mytag1MsgData", this.msg, this.other)
}
}
})
Vue.component("mytag2", {
// 声明组件内容
template: `
<fieldset>
<legend>兄弟2</legend>
<p>{{data}}</p>
<p>{{data2}}</p>
</fieldset>
`,
// 声明自己的模型数据
data() {
return {
data: "",
data2: ""
}
},
// 组件渲染完毕触发函数
mounted() {
eventBus.$on("mytag1MsgData", (data, data2, data3) => {// 推荐写箭头函数
console.log(data)
console.log(data2)
console.log(data3)
// 将数据保存到自己的模型里面 -> 展示
this.data = data
this.data2 = data2
})
}
})
let vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
小总结
父->子
步骤1:子接受数据 通过props:['变量名1',...,'变量名n']来接受数据
步骤2:父传递数据 必须在父组件调用子组件上通过 v-bind:props中的变量名="父data中的键"
子->父
步骤1:子创建自定义事件 this.$emit(自定义事件名称, 数据1,...,数据n)
步骤2:父调用自定义事件 父在调用子组件上通过 @自定义事件名称="函数名" 你在父定义函数接受数据即可
兄弟
步骤1:创建所有组件共享数据中心 const eventBus = new Vue()
步骤2:传递数据 eventBus.$emit(自定义事件名称, 数据1,...,数据n)
步骤3:接受数据 eventBus.$on(自定义事件名称,处理函数)
二、slot 插槽
- 思考:如果你封装一个提示组件,如何实现每次调用可以传递不同的参数?
- 回答:通过slot插槽
简介
顾名思义:定义组件时留个口子,调用时插入数据到口子/槽
语法
后备(默认)内容
步骤1:定义组件时通过<slot></slot>占位留一个口子
步骤2:调用组件时(双标签中的内容就是【自动传递到口子里面的数据】)
具名插槽:顾名思义占位的口子/槽有名字,调用的时候根据名字插入
步骤1:定义组件时通过<slot name="标识"></slot>占位留一个口子/槽
步骤2:调用时根据标识插入数据(如果调用的时候没有写标识这插入默认槽)
旧:<template slot:标识>代码</template>
新:<template v-slot:标识>代码</template>
作用域插槽:调用组件,有时候需要使用组件模型中的数据
步骤1:定义组件 通过<slot name="标识" v-bind:任意名称="data中键" ....></slot>来接受数据
步骤2:调用组件
旧:<template slot="标识" slot-scope="任意名称">代码</template>
新:<template v-slot:标识="任意名称">代码</template> 任意名称就是传递的数据数据 对象
代码分析(后备内容)
定义tips组件(样式:红色边框,红色文字)内容通过插槽占位
<!DOCTYPE html>
<html lang="en">
<head>
<title>vue插槽应用</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<tips>用户名或密码错误</tips>
<tips>验证码有误</tips>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 定义tips组件(样式:红色边框,红色文字)内容通过插槽占位
// Vue.component("tips", {
// template: `
// <div style="max-width:200px;text-align:center;border:solid 1px red; color:red; padding:5px 10px">
// 用户名或密码错误
// </div>
// `
// })
Vue.component("tips", {
template: `
<div style="max-width:200px;text-align:center;border:solid 1px red; color:red; padding:5px 10px">
<slot></slot>
</div>
`
})
let vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
代码分析(具名插槽)
定义app组件(根标签 > h1 + 头部插槽 + 主体插槽 + 底部插槽)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vue插槽</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<app>
<template v-slot:header>
<h3>this is header</h3>
</template>
<template v-slot:main>
<h3>this is main</h3>
</template>
<template v-slot:footer>
<h3>this is footer</h3>
</template>
<!--
没有名字进没有名字的插槽
-->
<template>
<h3>this is other</h3>
</template>
</app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 定义app组件(根标签 > h1 + 头部插槽 + 主体插槽 + 底部插槽)
Vue.component("app", {
template: `
<div>
<h1>进口商城</h1>
<slot name="header"></slot>
<slot name="main"></slot>
<slot name="footer"></slot>
<slot></slot>
</div>
`
})
let vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
代码分析(作用域插槽)
在练习“代码分析(具有名插槽)” 基础上模型定义uname&age数据,并传递给调用
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vue插槽</title>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<app>
<template v-slot:header="item">
<h3>this is header</h3>
{{item}}<br />
{{item.data1}}<br />
{{item.data2}}
</template>
<template v-slot:main>
<h3>this is main</h3>
</template>
<template v-slot:footer>
<h3>this is footer</h3>
</template>
<!--
没有名字进没有名字的插槽
-->
<template>
<h3>this is other</h3>
</template>
</app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 定义app组件(根标签 > h1 + 头部插槽 + 主体插槽 + 底部插槽)
Vue.component("app", {
// 声明组件数据
data() {
return {
uname: "小明",
age: 666
}
},
// 声明组件内容
template: `
<div>
<h1>进口商城</h1>
<slot name="header" v-bind:data1="uname" v-bind:data2="age"></slot>
<slot name="main"></slot>
<slot name="footer"></slot>
<slot></slot>
</div>
`
})
let vm = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
小总结
slot插槽:顾名思义定义组件的时候留一个口子/槽,调用组件时将数组传递进去
后备(默认)内容
步骤1:定义组件 通过<slot></slot>来接受数据
步骤2:调用组件 传递数据 (注:调用组件双标签里面的内容 就是传递数据)
具名插槽:调用组件时,可以传递多个数据 根据插槽名字传递
步骤1:定义组件 通过<slot name="标识"></slot>来接受数据
步骤2:调用组件传递数据
旧:<template slot="标识">代码</template>
新:<template v-slot:标识>代码</template> 留心:冒号标识不加引号
作用域插槽:调用时候会需要使用组件中的模型数据
步骤1:定义组件 通过<slot name="标识" v-bind:任意名称="data中键" ....></slot>来接受数据
步骤2:调用组件
旧:<template slot="标识" slot-scope="任意名称">代码</template>
新:<template v-slot:标识="任意名称">代码</template> 任意名称就是传递的数据数据 对象
三、过渡(transition) 明确别记能用就行
明确需求
- 发现:鼠标放到右侧导航条上,提示框有个过渡效果(不加也行,提高逼格)
- 思考:你如何实现
- 回答:自己(C3 过渡&动画等) 或 使用Vue过渡知识点
复习
transform元素2D&3D转换效果
2D之移动(translate)
2D之旋转(rotate)
2D之缩放(scale)
2D之缩放(scale)
2D之倾斜转换(skew)
3D之旋转(rotateX)
预览地址:http://www.w3school.com.cn/cssref/pr_transform.asp
transition 元素过渡效果
transition: 过渡的属性 过渡的时间 过渡曲线 效果延迟时间
举个栗子:https://www.runoob.com/cssref/css3-pr-transition.html
动画(animation)/动画规则(@keyframes)
animation:
动画规则
动画时间
动画曲线
动画延迟播放时间
动画播放次数
@keyframes 动画名称
{
from {属性n..}
to {属性n..}
}
举个栗子:http://www.w3school.com.cn/cssref/pr_animation.asp
语法
单元素/组件的过渡
步骤1:写HTML(待加效果的代码)
步骤2:控制元素隐藏显示(因为进入/离开产生过渡效果)
步骤3:根据手册规则写指定CSS(进入效果,离开效果) 【复制】
步骤4:使用transition标签将代码包裹起来
初始渲染的过渡:页面打开的时候产生效果(加appear属性即可)
多个元素过渡(设置key):根据判断显示对应数据,过渡切换
步骤1:在transition中增加判断 & 设置key
步骤2:更新判断的条件时候 -> 测试
多个组件过渡(不设置key):上一讲动态组件,使用过渡切换(仅为提高体验度)
列表过渡
步骤1:通过transition-group替换待加动画的父级标签
步骤2:在transition-group增加tag声明代替的哪个标签
步骤3:待加动画的标签设置唯一的标识 key=唯一标识
代码分析(单元素/组件的过渡)
CSS3动画库效果预览:http://www.dowebok.com/demo/2014/98/
CSS3动画库文件:https://cdn.jsdelivr.net/npm/animate.css@3.5.1 (直接引入即可)
<!DOCTYPE html>
<html lang="en">
<head>
<title>动画</title>
<meta charset="UTF-8">
</head>
<style>
/* CSS过渡 效果1 */
.first-enter-active {
transition: all .3s ease;
}
.first-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.first-enter, .first-leave-to {
transform: translateX(100px);
opacity: 0;
}
/* CSS过渡 效果2 */
.two-enter-active {
transition: all .3s ease;
}
.two-leave-active {
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.two-enter, .two-leave-to {
transform: translateY(100px);
opacity: 0;
}
/* CSS动画 */
.three-enter-active {
animation: threeAnimation .5s;
}
.three-leave-active {
/* animation: threeAnimation .5s reverse;
reverse 从100% ~ 0%
去掉reverse 从0% ~ 100%
*/
animation: threeAnimation .5s;
}
@keyframes threeAnimation {
0% {
transform: translateX(0px);
}
/* 50% {
transform: translateX(50px);
} */
100% {
transform: translateX(100px);
}
}
</style>
<!-- 以前自己写动画,现在用别人写的动画 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1">
<body>
<div id="app">
<button @click="isShow = !isShow">点击</button>
<transition name="first">
<div v-show="isShow" >【CSS过渡】神龙摆尾</div>
</transition>
<transition name="three">
<div v-show="isShow" >【CSS动画】神龙拳</div>
</transition>
<transition
enter-active-class="animated bounceInLeft"
leave-active-class="animated bounceOutRight"
>
<div v-show="isShow" >【CSS动画库】神龙腿</div>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: true
}
})
</script>
</body>
</html>
代码分析(初始渲染的过渡)
<!DOCTYPE html>
<html lang="en">
<head>
<title>动画效果</title>
<meta charset="UTF-8">
</head>
<!-- 以前自己写动画,现在用别人写的动画 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1">
<body>
<div id="app">
<button @click="isShow = !isShow">点击</button>
<!-- 注:这里加了appear 打开页面动画就会生效 -->
<transition
appear
enter-active-class="animated bounceInLeft"
leave-active-class="animated bounceOutRight"
>
<div v-show="isShow" >【CSS动画库】动画效果</div>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: true
}
})
</script>
</body>
</html>
##代码分析(多个元素过渡)
<!DOCTYPE html>
<html lang="en">
<head>
<title>过度效果</title>
<meta charset="UTF-8">
</head>
<!-- 以前自己写动画,现在用别人写的动画 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1">
<body>
<div id="app">
<button @click="isShow = !isShow">点击</button>
<!--
元素/节点/标签
注:这里加了appear 打开页面动画就会生效
多元素过渡:
多个标签,通过v-if/v-else控制显示谁
点击时 两个元素切换产生动画(注:默认没有)
同名标签需要设置一个唯一的标识 ,通过key属性
切换模式:mode属性
默认 else先进来 if再走 in-out
现在 if先走 else再进来 mode="out-in"
-->
<transition
appear
enter-active-class="animated bounceInLeft"
leave-active-class="animated bounceOutRight"
mode="out-in"
>
<div key="1" v-if="isShow">动画效果</div>
<div key="2" v-else>坑爹Q 小组真幸福</div>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: true
}
})
</script>
</body>
</html>
代码分析(多个组件过渡)
步骤1:通过ul>li*3 在通过固定定位显示底部菜单(首页、购物车、我的)
步骤2:定义(首页、购物车、我的)三者组件,通过(动态组件)显示
步骤3:点击导航,显示不同组件内容
步骤4:增加过渡效果
<!DOCTYPE html>
<html lang="en">
<head>
<title>动画效果</title>
<meta charset="UTF-8">
<style>
html,body {overflow: hidden;}
* {padding:0px;margin:0px;}
ul {width:100%;height:80px;list-style: none;position: fixed; bottom: 0}
ul li {float:left;width:33.33%;cursor: pointer; line-height: 80px; background: #ccc;text-align: center;}
ul li:hover {color:red;}
</style>
</head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1">
<body>
<div id="app">
<ul>
<li @click="chooseComponent = 'index'">首页</li>
<li @click="chooseComponent = 'cart'">购物车</li>
<li @click="chooseComponent = 'my'">我的</li>
</ul>
<!-- leave-active-class="animated shake" -->
<transition
appear
enter-active-class="animated bounceInRight"
mode="out-in"
>
<!-- 动态组件知识点 -->
<component :is="chooseComponent"></component>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
chooseComponent: 'index'
},
// 声明组件
components: {
// index
index: {
template: `<div>this is index</div>`
},
// cart
cart: {
template: `<div>this is cart</div>`
},
// my
my: {
template: `<div>this is my</div>`
}
}
})
</script>
</body>
</html>
代码分析(列表过渡)
<!DOCTYPE html>
<html lang="en">
<head>
<title>过渡动画</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1">
</head>
<body>
<div id="app">
<input type="text" v-model="keywords">
<button @click="add">添加</button>
<hr />
<!-- <ul> -->
<transition-group
tag="ul"
enter-active-class="animated bounceInRight"
leave-active-class="animated bounceOut"
>
<li v-for="(todo,index) in todos" v-bind:key="todo.title">
{{todo.title}}
<button @click="del(index)">删除</button>
</li>
</transition-group>
<!-- </ul> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
// 关键词
keywords: "",
// 列表数据
todos: [
{id:1, title:"吃饭"},
{id:2, title:"睡觉"},
{id:3, title:"挤痘痘"}
]
},
// 声明普通方法
methods: {
add() {
this.todos.push({
// id: this.todos[this.todos.length-1].id + 1,
id: this.todos.length,
title: this.keywords
})
},
del(index) {
this.todos.splice(index, 1)
}
}
})
</script>
</body>
</html>
代码分析(可复用的过渡)
<!DOCTYPE html>
<html lang="en">
<head>
<title>动画</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1">
</head>
<body>
<div id="app">
<tips v-show="isShowTips">用户名或密码错误</tips>
<p>
用户名
<input type="text">
</p>
<p>
密码
<input type="text">
</p>
<p>
<button @click="isShowTips = !isShowTips">登录</button>
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("tips", {
template: `
<transition
enter-active-class="animated bounceInRight"
leave-active-class="animated bounceOut"
>
<div style="width:200px; height: 30px; line-height: 30px; border:solid 2px red; padding: 10px; text-align:center;">
<slot></slot>
</div>
</transition>
`
})
let vm = new Vue({
el: '#app',
data: {
isShowTips: false
}
})
</script>
</body>
</html>
请先 后发表评论~