11、消息订阅与发布pubsub
11-1、消息订阅与发布
一种组件间通信的方式,适用于任意组件间通信。
11-2、使用方法
1、调用pubsub.js库命令,安装pubsub:npm i pubsub-js
2、在所用的地方引入库: import pubsub from 'pubsub-js'
- 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 }
- 提供数据:
pubsub.publish('xxx',数据)
- 最好在beforeDestroy钩子中,用
PubSub.unsubscribe(this.pid)
去取消订阅。
11-3、TodoList案例pubsub
12、$nextTick(视图渲染完,操作DOM)
12-1、nextTick使用
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
13、vue封装的过度和动画
13-1、Vue封装的过度与动画
- 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
- 图示:
网络异常,图片无法展示|
- 写法:
3-1、准备好样式:
- 元素进入的样式:
1. v-enter:进入的起点
2. v-enter-active:进入过程中
3. v-enter-to:进入的终点
- 元素离开的样式:
1. v-leave:离开的起点
2. v-leave-active:离开过程中
3. v-leave-to:离开的终点
3-2、使用<transition>
包裹要过度元素,并配置name属性:
3-3、备注:若有多个元素需要过度,则需要使用:<transition-group>
,且每个元素都要指定key
值。
3-4、可以使用第三方动画库来实现动画效果
13-2、animate.css(开箱即用的动画库)
图片:
//安装animate.css库 npm install animate.css //引入样式库 import 'animate.css' //存放库里面的样式属性(animate__animated animate__bounce), //然后放置属性(enter-active-class) //和效果动画(animate__swing) <transition-group appear name="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>
14、配置代理
14-1、用脚手架去解决Ajax跨域的问题
解压文件中的test_proxy_server.zip
在文件页面打开cmd 输入node server1,然后复制(http://localhost:5000/students)网址,打开浏览器,输入页面发现是Ajax发出的get请求,端口也显示出来了
在文件页面打开cmd 输入node server1,然后复制(http://localhost:5000/students)网址,打开浏览器,输入页面发现是Ajax发出的get请求,端口也显示出来了
14-2、常用的发送一个Ajax请求的方式有哪些:(4种)
- xhr(不常用) new XMLHttpRequest()
常用apixhr.open() 、 xhr.send()等 - jQuery(不常用,经常使用DOM操作)
常用api $get 、 $post - axios(promise风格)
- vue-resource(不常用,在vue1.0流行)
- fetch(promise风格,但会包装两次promise)
最致密的是他IE中的兼容性极差
14-3、axios的使用与问题解决
安装axios
//axios的安装 npm i axios
引入axios //引入axios import axios from 'axios.js' 设置一个按钮 <button @click="getStudents">获取学生信息</button> 在methods中写请求 methods: { getStudents(){ axios.get('http://localhost:5000/students').then( response => { console.log('请求成功了',response.data) }, error => { console.log('请求失败了',error.message) } ) }, }
发生跨域问题
解决跨域问题的方式:
1、cors
2、jsonp script src(只能解决get请求post无法解决)
3、使用代理服务器(类似于生活中的房屋中介)
开启代理服务器方式{ 1、nginx 2、vue-cli }
14-4、用脚手架的方式配置一个代理服务器
14-4A、方式一
14-4A-1、配置方法
①在vue的脚手架官网搜索devServer.proxy(英文意思为开发中如何配置代理),②在vue.config.js中输入(这里的5000是你获取axios服务器的端口),③把methods中的axios请求的地址改成8080端口的(因为数据要发送到8080端口实现跨域)
devServer: { proxy: 'http://localhost:5000' }
//修改端口号5000,改成8080端口 axios.get('http://localhost:8080/students').then( response => { console.log('请求成功了',response.data) }, error => { console.log('请求失败了',error.message) } ) },
14-4A-2、工作方式:
若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
当你请求的内容代理服务器中原本就有则不会发送给5000的目标服务器public中有students,(即实现不了下面图片黄色箭头)
14-4A-3、方式一配置代理缺点
这种情况下只能配置一个单独的代理,并且不能灵活的控制走不走代理服务器。
14-4B、方式二
14-4B-1、配置方法
下图两个颜色为配置的两套代理(分别为红色和绿色,且红色是精简版),并且这里'/api'的意思是当你发送请求到代理服务器时,它会判断代理是否是 /api 形式如果是走代理,否则不走。target 为请求跳转的目标地址,pathRewrite:{'^/api':''}这里是请求后发给目标地址是把/api 替换成空字符串
此时请求完还得改axios跳转代理服务器的路径为
//原本 axios.get('http://localhost:8080/students') //修改后添加了个/api axios.get('http://localhost:8080/api/students') /* changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080 changeOrigin默认值为true */
14-4B-2、配置优缺点:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
14-5、github案例
1、先在index.html中引入bootstrap样式
2、main.js代码
//引入Vue import Vue from 'vue' //引入App import App from './App.vue' //关闭Vue的生产提示 Vue.config.productionTip = false //创建vm new Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this }, })
3、App.vue
<template> <div class="container"> <Search/> <List/> </div> </template> <script> import Search from './components/Search' import List from './components/List' export default { name:'App', components:{Search,List} } </script>
4、Search.vue
<template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="enter the name you search" v-model="keyWord"/> <button @click="searchUsers">Search</button> </div> </section> </template> <script> import axios from 'axios' export default { name:'Search', data() { return { keyWord:'' } }, methods: { searchUsers(){ //请求前更新List的数据 this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false}) axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { console.log('请求成功了') //请求成功后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items}) }, error => { //请求后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}) } ) } }, } </script>
5、List.vue
<template> <div class="row"> <!-- 展示用户列表 --> <div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login"> <a :href="user.html_url" target="_blank"> <img :src="user.avatar_url" style='width: 100px'/> </a> <p class="card-text">{{user.login}}</p> </div> <!-- 展示欢迎词 --> <h1 v-show="info.isFirst">欢迎使用!</h1> <!-- 展示加载中 --> <h1 v-show="info.isLoading">加载中....</h1> <!-- 展示错误信息 --> <h1 v-show="info.errMsg">{{info.errMsg}}</h1> </div> </template> <script> export default { name:'List', data() { return { info:{ isFirst:true, isLoading:false, errMsg:'', users:[] } } }, mounted() { this.$bus.$on('updateListData',(dataObj)=>{ this.info = {...this.info,...dataObj} }) }, } </script> <style scoped> .album { min-height: 50rem; /* Can be removed; just added for demo purposes */ padding-top: 3rem; padding-bottom: 3rem; background-color: #f7f7f7; } .card { float: left; width: 33.333%; padding: .75rem; margin-bottom: 2rem; border: 1px solid #efefef; text-align: center; } .card > img { margin-bottom: .75rem; border-radius: 100px; } .card-text { font-size: 85%; } </style>
效果图
15、vue-resource(不常用)
15-1、使用方法
安装vue-resource npm i vue-resource 在main.js中引入vue-resource插件 import vueResource from 'vue-resource' 在main.js中使用插件 vue.use(vueResource) 在一个vue实例内使用this.$http代替axios // 在一个Vue实例内使用$http this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { console.log('请求成功了') //请求成功后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items}) }, error => { //请求失败后更新List的数据 this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}) } )
16、插槽
- 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
- 分类:默认插槽、具名插槽、作用域插槽
- 使用方式:
- 默认插槽:
父组件中:
<Category> <div>html结构1</div> </Category>
子组件中:
<template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template> a. 具名插槽:
父组件中:
<Category> <template slot="center"> <div>html结构1</div> </template> <template v-slot:footer> <div>html结构2</div> </template> </Category>
子组件中:
<template> <div> <!-- 定义插槽 --> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
- 作用域插槽:
- 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
- 具体编码:
父组件中:
<Category> <template scope="scopeData"> <!-- 生成的是ul列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category>
子组件中:
<template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], //数据在子组件自身 data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } }, } </script>
17、动态组件
17-1、动态组件的is
- 动态组件就是几个组件放在一个挂载点下,然后根据父组件的某个变量来决定显示哪个,或者都不显示。
- 在挂载点使用 component 标签,然后使用 is =“组件名”,它会自动去找匹配的组件名,如果有,则显示;
看例子:
<!-- html部分 --> <div id="app"> <component is="one"></component> </div> // js 部分 new Vue({ el: '#app', components: { one: {template: '<div>我是线路一</div>'}, two: {template: '<div>我是线路二</div>'}, thr: {template: '<div>我是线路三</div>'} } })
上面代码注册了三个组件,在 component 标签里有个属性 is,is=one,所以页面会渲染名字叫 one 的组件,显示结果如下:
我是线路一
如果给 is 属性绑定动态值,那么就可以实现组件的动态切换,例子如下:
<!-- html 部分 --> <div id="app"> <button v-for="item in tabs" @click="change = item.id"> {{ item.text }} </button> <component :is="change"></component> </div> // js 部分 new Vue({ el: '#app', data: { change: 'one' // 默认显示组件 one tabs: [ {id: 'one', text: '线路一'}, {id: 'two', text: '线路二'}, {id: 'thr', text: '线路三'} ] }, components: { one: {template: '<div>我是线路一</div>'}, two: {template: '<div>我是线路二</div>'}, thr: {template: '<div>我是线路三</div>'} } })
上面代码用 v-bind 给属性 is 动态传递了值,实现了组件的动态切换,效果如下:
17-2、动态组件的keep-alive
在面试的时候,很多面试官再问vue的时候可能就会提一嘴,你知道keep-alive有什么作用吗?
keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,那么就可以利用keep-alive来实现
我这里利用脚手架创建项目后会生成home和about两个组件,并且通过路由进行切换
home组件
<template> <div class="home"> <input type="text"> </div> </template> <script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script>
我在home组件中写了一个input输入框
about组件
<template> <div class="about"> <input type="text"> </div> </template> <script> export default { name:"about" } </script>
同样的about组件也放了一个输入框
当我们在home组件的输入框输入一些内容的时候,然后切换到about组件,在切换回home组件,我们会发现之前输入的内容被清空了,其实也容易理解,就是当切换到about组建的时候,home组件就被销毁了,输入框的值自然被清空了
当切换到about组件的时候home组件的destroyed就触发了,所以home组件被销毁了
那么此时我们就可以利用keep-alive组件进行包裹router-view组件,将不活动的组件缓存起来
App组件
<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> </div> <keep-alive> <router-view /> </keep-alive> </div> </template>
写完之后会发现当切换到about组件时,home组件中的destroyed并没有触发,并且home组件的值也保存了下来
但是这样也肯定不好,我们会发现,about组件的值也被缓存了,就是所有的路由组件都被缓存了,严重浪费性能,而且也不符合需求,我们现在只想缓存home组件
在keep-alive上有两个属性
字符串或正则表达式。只有匹配的组件会被缓存。
- include 值为字符串或者正则表达式匹配的组件name会被缓存。
- exclude 值为字符串或正则表达式匹配的组件name不会被缓存。
首先利用include实现,匹配到组件中定义的name,将进行缓存
<keep-alive include="home"> <router-view /> </keep-alive>
我们会发现home已经被缓存了,但是about没有被缓存
而exclude就是排除了,这个就不在演示了,很简单,除了这个我们还可以利用路由中的meta属性来控制
{ path: '/', name: 'home', meta:{ keepAlive:true }, component: Home }
将home的路由规则钟的meta添加keepAlive属性为true,也就是当前路由组件要进行缓存
keep-alive代码可以结合v-if进行包裹,如果meta中的keepAlive为true进行缓存,否侧不进行缓存,这样可以更灵活一些
<keep-alive> <router-view v-if="$route.meta.keepAlive" /> </keep-alive> <router-view v-if="!$route.meta.keepAlive" />
这样组件的缓存是实现了,但是还是会有一些问题,就是因为组件被缓存,并没有被销毁,所以组件在切换的时候也就不会被重新创建,自然也就不会调用created等生命周期函数,所以此时要使用activated与deactivated来获取当前组件是否处于活动状态
我在home组件里面写入了activated与deactivated生命周期函数
activated(){ console.log("哎呀看见我了") console.log("----------activated--------") }, deactivated(){ console.log("讨厌!!你又走了") console.log("----------deactivated--------") }
通过上面的gif图相信已经看得很清楚了,此时keep-Alive也就差不多了