1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【Vue】Vue全家桶(三)Vue组件通信+Vue组件插槽+动画与过渡+使用vue-cli解决Ajax跨域问题

【Vue】Vue全家桶(三)Vue组件通信+Vue组件插槽+动画与过渡+使用vue-cli解决Ajax跨域问题

时间:2021-04-23 11:40:38

相关推荐

【Vue】Vue全家桶(三)Vue组件通信+Vue组件插槽+动画与过渡+使用vue-cli解决Ajax跨域问题

1 Vue组件通信

1.1 组件间通信基本原则

不要在子组件中直接修改父组件的状态数据数据在哪, 更新数据的行为(函数)就应该定义在哪

1.2 vue 组件间通信方式

propsvue 的自定义事件消息订阅与发布(如: pubsub 库)slotvuex

1.3 props

props:让组件接收外部传过来的数据,此方式用于父组件向子组件传递数据

props传递数据原则:单向数据流,只能父传子

注意:

如果需要向非子后代传递数据必须多层逐层传递兄弟组件间也不能直接props 通信, 必须借助父组件才可以所有标签属性都会成为组件对象的属性, 模板页面可以直接引用

1.父组件通过传统方式或v-bind动态绑定向子组件传送数据

<!-- App父组件 --><template><div><!-- 传送数据一定要写在父组件的子组件标签上(通过标签属性)--><!-- 1.传统方式传送数据 -->//<Student name='张三'/><!-- 2.动态绑定传送数据(不限于形式,可能是函数) Student.name会作为表达式自动执行 --><Student :name='Student.name'/></div></template><script>//引入子组件import Student from "./components/Student.vue";export default {name: "App",components: {Student },data() {return {Student:{name:'张三'}};},};</script>

2.子组件内部通过props接收父组件传递的数据

<!-- Student子组件 -->//第一种方式(只接收)最常用props:['name']//第二种方式(限制类型)props:{name:String}//第三种方式(限制类型、限制必要性、指定默认值)props:{name:{type:String, //类型required:true, //必要性default:'张三' //默认值}}

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

App.vue 父组件

<template><div><!-- 传递数据 --><!-- :是v-bind动态绑定 18会作为表达式自动执行 --><Student name='李四' sex='女' :age='18'></Student></div></template><script>//引入子组件import Student from "./components/Student.vue";export default {name: "App",components: {Student },};</script><style></style>

Student.vue 子组件

<template><div><h2>学生姓名:{{name }}</h2><h2>学生性别:{{sex }}</h2><h2>学生年龄:{{myAge + 1 }}</h2><button @click="updateAge">尝试修改收到的年龄</button></div></template><script>export default {name: "Student",data() {return {//若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据myAge: this.age,};},methods: {updateAge() {this.myAge++;},},//接收数据 简单声明接收props:['name','age','sex']};</script>

1.4 自定义事件

自定义事件:用于子组件向父组件传递数据

使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

隔代组件或兄弟组件间通信此种方式不合适

1.绑定自定义事件

方式一: v-on

<!-- App.vue父组件 -->// 在父组件中给子组件 xxx为自定义事件 getStudentName为回调函数(在父组件中)<Student @xxx="getStudentName" />...methods: {//回调函数getStudentName(name) {this.studentName = name;},},

方式二:ref

//通过ref给Student组件打标识<Student ref="student" />...methods: {//回调函数getStudentName(name) {this.studentName = name;},},mounted() {//通过$refs获取Student组件//在获取到的Student组件上绑定自定义事件xxx getStudentName为回调函数this.$refs.student.$on("xxx", this.getStudentName); //$on当...时},

2.触发自定义事件

方法:this.$emit(xxx, data)

<!-- Student.vue子组件 --><button @click="sendStudentName">把学生名给App</button>...methods: {sendStudentName() {//触发Student组件实例身上的xxx自定义事事件this.$emit("xxx", this.name);}},

注意

解绑自定义事件:this.$off(‘xxx’)当需要自定义事件只能触发一次:可以使用once修饰符,或$once方法。组件上也可以绑定原生DOM事件,需要使用native修饰符。

<Student @click.native=“show” />隔代组件或兄弟组件间通信不能使用自定义事件

案例

App父组件

<template><div class="app"><h1>{{msg }},学生姓名是:{{studentName }}</h1><!-- 第一种:使用@或v-on --><!-- <Student @atguigu="getStudentName" /> --><!-- 第二种:使用ref --><Student ref="student" @click.native="show" /></div></template><script>import Student from "./components/Student";export default {name: "App",components: {Student },data() {return {msg: "你好啊!",studentName: "",};},methods: {getStudentName(name) {this.studentName = name;},show() {alert(123);},},mounted() {this.$refs.student.$on("atguigu", this.getStudentName); //绑定自定义事件},};</script><style scoped>.app {background-color: gray;padding: 5px;}</style>

Student.vue子组件

<template><div class="student"><h2>学生姓名:{{name }}</h2><h2>当前求和为:{{number }}</h2><button @click="add">点我number++</button><button @click="sendStudentName">把学生名给App</button><button @click="unbind">解绑atguigu事件</button><button @click="death">销毁当前Student组件的实例(vc)</button></div></template><script>export default {name: "Student",data() {return {name: "张三",number: 0,};},methods: {add() {this.number++;},sendStudentName() {//触发Student组件实例身上的atguigu事件this.$emit("atguigu", this.name);},unbind() {this.$off("atguigu"); //解绑一个自定义事件// this.$off(['atguigu','demo']) //解绑多个自定义事件// this.$off() //解绑所有的自定义事件},death() {this.$destroy(); //销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效。},},};</script><style lang="less" scoped>.student {background-color: pink;padding: 5px;margin-top: 30px;}</style>

1.5 全局事件总线(GlobalEventBus)

全局事件总线:适用于任意组件间通信。

1.安装全局事件总线

//main.jsnew Vue({el: '#app',render: h => h(App),//安装全局事件总线 beforeCreate创建实例前beforeCreate() {//组件实例对象vc可以访问到Vue原型上的属性和方法,往Vue原型上添加$bus属性 $bus为傀儡//那么子组件可以使用$bus,而$bus值为vm(this) 因为vm可以调用$on $emit这些方法Vue.prototype.$bus = this;},});

2.使用事件总线

接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

methods(){demo(data){......}}......//mounted():初始化操作,绑定自定义事件//xxx为自定义事件 this.demo为回调函数mounted() {this.$bus.$on('xxx',this.demo)}//使用完之后 beforeDestroy解绑自定义事件beforeDestroy() {this.$bus.$off("xxx");},

发送数据

this.$bus.$emit('xxx',数据)

案例:兄弟组件传值(Student => School)

main.js

//引入Vueimport Vue from 'vue'//引入Appimport App from './App.vue'//关闭Vue的生产提示Vue.config.productionTip = false//创建vmnew Vue({el:'#app',render: h => h(App),beforeCreate() {Vue.prototype.$bus = this //安装全局事件总线},})

School.vue

<template><div class="school"><h2>学校名称:{{name }}</h2><h2>学校地址:{{address }}</h2></div></template><script>export default {name: "School",data() {return {name: "尚硅谷",address: "北京",};},methods: {demo(data) {console.log("我是School组件,收到了数据", data);},},mounted() {//在School组件给傀儡绑定自定义事件hello,借助傀儡身上的$on方法获取数据this.$bus.$on("hello", this.demo);},beforeDestroy() {//解绑当前组件用到的事件this.$bus.$off("hello");},};</script><style scoped>.school {background-color: skyblue;padding: 5px;}</style>

Student.vue

template><div class="student"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2><button @click="sendStudentName">把学生名给School组件</button></div></template><script>export default {name:'Student',data() {return {name:'张三',sex:'男',}},methods: {sendStudentName(){this.$bus.$emit('hello',this.name)}},}</script><style lang="less" scoped>.student{background-color: pink;padding: 5px;margin-top: 30px;}</style>

1.6 消息订阅与发布(pubsub-js 库)

消息订阅与发布:适用于任意组件间通信。

安装pubsub-js 库:

npm i pubsub-js

引入:

import pubsub from 'pubsub-js'

接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

1.接收数据

methods: {demo(msgName, data) {...},},mounted() {//订阅消息 每一次订阅都会产生一个id//msgName代表数据名xxx data代表接收的数据this.pubId = pubsub.subscribe("xxx", this.demo);},beforeDestroy() {//通过id取消订阅pubsub.unsubscribe(this.pubId);},

2. 发送数据

methods: {sendStudentName() {//发布消息pubsub.publish("hello", this.name);},},

案例:兄弟组件传值(Student => School)

School.Vue

<template><div class="school"><h2>学校名称:{{name }}</h2><h2>学校地址:{{address }}</h2></div></template><script>import pubsub from "pubsub-js";export default {name: "School",data() {return {name: "尚硅谷",address: "北京",};},methods: {demo(msgName, data) {console.log("我是School组件,收到了数据", msgName, data);},},mounted() {//订阅消息 每一次订阅都会产生一个id//msgName代表数据名hello data代表接收的数据this.pubId = pubsub.subscribe("hello", this.demo);},beforeDestroy() {//通过id取消订阅消息pubsub.unsubscribe(this.pubId);},};</script><style scoped>.school {background-color: skyblue;padding: 5px;}</style>

Student.Vue

<template><div class="student"><h2>学生姓名:{{name }}</h2><h2>学生性别:{{sex }}</h2><button @click="sendStudentName">把学生名给School组件</button></div></template><script>import pubsub from "pubsub-js";export default {name: "Student",data() {return {name: "张三",sex: "男",};},methods: {sendStudentName() {//发布消息pubsub.publish("hello", this.name);},},};</script><style lang="less" scoped>.student {background-color: pink;padding: 5px;margin-top: 30px;}</style>

2 组件插槽

组件插槽是Vue的内置组件,为了让我们封装的组件更加具有扩展性,让使用者可以决定组件内部的一些内容到底展示什么

作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

分类:默认插槽、具名插槽、作用域插槽

插槽的使用:子组件定义插槽的位置,父组件定义插槽的内容, 控制内容的显示与隐藏

如何封装:抽取共性,保留不同。最好的封装方式就是将共性抽取到组件中,将不同预留为插槽。

默认(匿名)插槽:唯一存在<slot></slot>

具名插槽:可存在多个<slot name="up"></slot>

作用域插槽:父组件对子组件的内容进行加工处理

2.1 默认插槽

父组件

<Category><div>html结构1</div></Category>

子组件

<template><div><!-- 定义插槽 --><slot>插槽默认内容...</slot></div></template>

案例

App.vue

<template><div class="container"><Category title="美食" ><img src="//01/16/srJlq0.jpg" alt=""></Category><Category title="游戏" ><ul><li v-for="(g,index) in games" :key="index">{{g}}</li></ul></Category><Category title="电影"><video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video></Category></div></template><script>import Category from './components/Category'export default {name:'App',components:{Category},data() {return {foods:['火锅','烧烤','小龙虾','牛排'],games:['红色警戒','穿越火线','劲舞团','超级玛丽'],films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']}},}</script><style scoped>.container{display: flex;justify-content: space-around;}</style>

Category.vue

<template><div class="category"><h3>{{title}}分类</h3><!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --><slot>我是一些默认值,当使用者没有传递具体结构时,我不会出现</slot></div></template><script>export default {name:'Category',props:['title']}</script><style scoped>.category{background-color: skyblue;width: 200px;height: 300px;}h3{text-align: center;background-color: orange;}video{width: 100%;}img{width: 100%;}</style>

2.2 具名插槽

父组件

<Category><!-- slot ="xxx" 向插槽名为xxx的插槽里面放内容--><template slot="center"><div>html结构1</div></template><template v-slot:footer><div>html结构2</div></template></Category>

子组件

<template><div><!-- 定义具名插槽 name="xxx" xxx为插槽名 --><slot name="center">插槽默认内容...</slot><slot name="footer">插槽默认内容...</slot></div></template>

案例

App.vue

<template><div class="container"><Category title="美食" ><img slot="center" src="//01/16/srJlq0.jpg" alt=""><a slot="footer" href="">更多美食</a></Category><Category title="游戏" ><ul slot="center"><li v-for="(g,index) in games" :key="index">{{g}}</li></ul><div class="foot" slot="footer"><a href="">单机游戏</a><a href="">网络游戏</a></div></Category><Category title="电影"><video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video><template v-slot:footer><div class="foot"><a href="">经典</a><a href="">热门</a><a href="">推荐</a></div><h4>欢迎前来观影</h4></template></Category></div></template><script>import Category from './components/Category'export default {name:'App',components:{Category},data() {return {foods:['火锅','烧烤','小龙虾','牛排'],games:['红色警戒','穿越火线','劲舞团','超级玛丽'],films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']}},}</script><style scoped>.container,.foot{display: flex;justify-content: space-around;}h4{text-align: center;}</style>

Category.vue

<template><div class="category"><h3>{{title}}分类</h3><!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --><slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot><slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot></div></template><script>export default {name:'Category',props:['title']}</script><style scoped>.category{background-color: skyblue;width: 200px;height: 300px;}h3{text-align: center;background-color: orange;}video{width: 100%;}img{width: 100%;}</style>

2.3 作用域插槽

理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

父组件

<Category><template scope="scopeData"><!-- 生成的是ul列表 --><ul><!-- scopeData.games就是子组件中的数据 --><li v-for="g in scopeData.games" :key="g">{{g}}</li></ul></template></Category>

子组件

<template><div><slot :games="games"></slot></div></template><script>export default {name:'Category',props:['title'],//数据在子组件自身data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽']}},}</script>

案例

App.vue

<template><div class="container"><Category title="游戏"><template scope="atguigu"><ul><li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li></ul></template></Category><Category title="游戏"><template scope="{games}"><ol><li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li></ol></template></Category><Category title="游戏"><template slot-scope="{games}"><h4 v-for="(g,index) in games" :key="index">{{g}}</h4></template></Category></div></template><script>import Category from './components/Category'export default {name:'App',components:{Category},}</script><style scoped>.container,.foot{display: flex;justify-content: space-around;}h4{text-align: center;}</style>

Category.vue

<template><div class="category"><h3>{{title}}分类</h3><slot :games="games" msg="hello">我是默认的一些内容</slot></div></template><script>export default {name:'Category',props:['title'],data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽'],}},}</script><style scoped>.category{background-color: skyblue;width: 200px;height: 300px;}h3{text-align: center;background-color: orange;}video{width: 100%;}img{width: 100%;}</style>

3 Vue封装的过渡与动画

1.过渡与动画作用

在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名

2.图示

3.写法

准备好样式:

v-enter(进入的起点) v-enter-active(进入的过程) v-enter-to(进入的终点)

v-enter(离开的起点) v-enter-active(离开的过程) v-enter-to(离开的终点)

使用<transtion>包裹要过渡的元素,并配置name属性:

<transition name="hello" ><h1 v-show="isShow">你好啊!</h1></transition>

备注:若有多个元素需要过渡,则需要使用,且每个元素都要指定key值

App.vue

<template><div><Test /><Test2 /><Test3 /></div></template><script>import Test from "./components/Test";import Test2 from "./components/Test2";import Test3 from "./components/Test3";export default {name: "App",components: {Test, Test2, Test3 },};</script><style></style>

使用动画代码实现

Test.vue

<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition name="hello" appear><h1 v-show="isShow">你好啊!</h1></transition></div></template><script>export default {name:'Test',data() {return {isShow:true}},}</script><style scoped>h1{background-color: orange;}.hello-enter-active{animation: atguigu 0.5s linear;}.hello-leave-active{animation: atguigu 0.5s linear reverse;}@keyframes atguigu {from{transform: translateX(-100%);}to{transform: translateX(0px);}}</style>

使用过渡代码实现

Test2.vue

<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition-group name="hello" appear><h1 v-show="isShow" key="1">你好啊!</h1><h1 v-show="isShow" key="2">尚硅谷!</h1></transition-group></div></template><script>export default {name: "Test",data() {return {isShow: true,};},};</script><style scoped>h1 {background-color: orange;}/* 进入的起点 离开的终点 */.hello-enter,.hello-leave-to {transform: translateX(-100%);}/* 进入的整个过程 离开的整个过程 */.hello-enter-active,.hello-leave-active {transition: 0.5s linear;}/* 进入的终点 离开的起点 */.hello-enter-to,.hello-leave {transform: translateX(0);}</style>

使用第三方动画库实现(https://animate.style/)

Test3.vue

<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition-groupappearname="animate__animated animate__bounce"enter-active-class="animate__swing"leave-active-class="animate__backOutUp"><h1 v-show="!isShow" key="1">你好啊!</h1><h1 v-show="isShow" key="2">尚硅谷!</h1></transition-group></div></template><script>import "animate.css";export default {name: "Test",data() {return {isShow: true,};},};</script><style scoped>h1 {background-color: orange;}</style>

4 使用vue-cli解决Ajax跨域问题

使用vue-cli开启代理服务器ajax跨域问题

方法一:

在vue.config.js中添加如下配置:

devServer:{proxy:"http://localhost:5000"}

优点:配置简单,请求资源时直接发给前端(8080)即可。

缺点:不能配置多个代理,不能灵活的控制请求是否走代理。

工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二:

​ 编写vue.config.js配置具体代理规则:

module.exports = {devServer: {proxy: {'/api1': {// 匹配所有以 '/api1'开头的请求路径target: 'http://localhost:5000', // 将请求代理到目标服务器上changeOrigin: true,//重写路径 将请求中的/api1 重写为为空字符串pathRewrite: {'^/api1': '' },ws: true, //用于支持websocketchangeOrigin: true, //用于控制请求头中的host值},'/api2': {// 匹配所有以 '/api2'开头的请求路径target: 'http://localhost:5001', // 代理目标的基础路径changeOrigin: true,pathRewrite: {'^/api2': '' },ws: true, //用于支持websocketchangeOrigin: true, //用于控制请求头中的host值},},},}/*changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080changeOrigin默认值为true*/

优点:可以配置多个代理,且可以灵活的控制请求是否走代理。

缺点:配置略微繁琐,请求资源时必须加前缀。

开启两个资源服务器

app.vue

<template><div id="app"><!-- 在8080端口上获取5000和5001端口上的数据 --><button @click="getStudents">获取学生信息</button><button @click="getCars">获取汽车信息</button></div></template><script>import axios from "axios";export default {name: "App",methods: {getStudents() {axios.get("http://localhost:8080/api1/students").then((response) => {console.log("请求成功了", response.data);},(error) => {console.log("请求失败了", error.message);});},getCars() {axios.get("http://localhost:8080/api2/cars").then((response) => {console.log("请求成功了", response.data);},(error) => {console.log("请求失败了", error.message);});},},};</script>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。