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>
举报
评论 0