Vue中的Ajax
4.1Vue中代理和ajax
原理
×
XHR newHttpRequest() xhr.open() xhr.send() win内置×
jQuery $get $set 80%都是封装DOM操作- axios promise风格
×
fetch win内置 promise风格(包两层,兼容性有问题)
npm i axios
getData() {
axios.get("http://localhost:5000/students").then(
(response) => {
console.log("请求成功", response.data);
},
(error) => {
console.log("请求失败", error.message);
}
);
},
解决跨域
- cors(服务器里面写了,携带特殊的响应头)
- jsonp 通过script标签的src属性在外部引用资源不受同源限制的策略(只能解决get请求)
- 代理服务器(vue-cli,ngnix)
devServer: {
proxy: "http://localhost:5000"
}
上述就是开启一个代理服务器:1、协议http 2、主机localhost 3、端口5000
问题:
- 本机服务器中不能和数据服务器同名的文件或者地址
- 不能开
多个代理
devServer: {
proxy: {
'/api':{
target: "http://localhost:5000",
pathRewrite: { '^/api': '' }//这里通过下面原理图解决
ws: true, // 用于websocket
changeOrigin: true // 代理服务器是否说谎,控制请求头中host值,true 那么数据服务器检测来源是5000,false,那么数据服务器检测来源是8080
}
}
}
原理
方法一
在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
方法二
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
说明:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
4.2Github搜索案例
asset里面的静态资源是要用ES6的import,会严格检验
解决方法
在public下面建立css,公共引用,用到哪个样式再去看有没有这个样式
<!-- 引入第三方样式 -->
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
List.vue 接受方
<script>
export default {
name: "List",
data() {
return {
info: {
isFirst: true,
isLoad: false,
errormsg: "",
users: [],
},
};
},
mounted() {
this.$bus.$on("UpdateList", (dataObj) => {
this.info = { ...this.info, ...dataObj };
});
},
};
</script>
search.vue 发送方
<script>
export default {
name: "Search",
data() {
return {
keyword: "",
};
},
methods: {
searchName() {
this.$bus.$emit("UpdateList", {
isFirst: false,
isLoad: true,
errormessage: "",
users: [],
});
axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
(response) => {
console.log("请求成功");
this.$bus.$emit("UpdateList", {
isLoad: true,
errormsg: "",
users: response.data.items,
});
},
(error) => {
this.$bus.$emit("UpdateList", {
isLoad: true,
errormsg: error.message,
users: [],
});
}
);
},
},
};
</script>
4.3vue-resource
npm i vue-resource
一个插件
import vueResource from 'vue-resource'
Vue.user(vueResource)
axios.get(xxx)
this.$http.get(xxx)
4.4solt插槽
默认插槽
App.vue
<template>
<div class="container">
<Category title="美食">
<img src="https://s3.ax1x.com/2021/01/16/srJ1q0.jpg" alt="" />
</Category>
<Category title="游戏">
<u1>
<li v-for="(g,index)in games" key="index">{{ g }}</li>
</u1>
</Category>
<Category title="电影">
<video
controls
src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
></video>
</Category>
</div>
</template>
category.vue
<slot>默认</slot>
这里在App里面解析完了才会传给category组件
具名插槽
<Category tit1e="电影">
<video slot="center"controls src="http://clips.vorwaerts-gmbh.de/big_buck"></video>
<template v-slot:footer>
<div class="foot">
<a href=:"http:/nw.atguigu.com">经典</a>
<a href=:"http:/Mww.atguigu.com">热门k/a>
<a href=:"http://wMw.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
<slot name="foot">默认</slot>
v-slot:name是vue2.6新出的,只能配合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:['lol','dota2','cos','2077']
}
},
}
</script>
VueX
5.1概念
**概念:**在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
5.2使用场景
**使用场景:**多个组件需要共享数据时
- 多个组件依赖同一状态
- 来自不同组件的行为需要变更同一状态
ps:全局事件总线实现数据共享
ps:vuex实现数据共享
5.3纯vue案例
非常简单略
5.4Vuex工作原理
原理图
- 这里的state就是存放数据的对象,像之前的toddolist都属于状态或者叫数据
- action和mutations都是对象,里面的值是函数,mutations函数有两个参数一个是state,value
- ajax需要用到Api
- 三个对象都要
store
管理
5.5搭建Vuex
Vue2对应的vuex是3
npm i vuex@3
-
创建文件:
src/store/index.js
//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //应用Vuex插件 Vue.use(Vuex) //准备actions对象——响应组件中用户的动作 const actions = { jia(contex,value){ context.commit('jia',value) } } //准备mutations对象——修改state中的数据 const mutations = { jia(state,value){ state.sum+=value } } //准备state对象——保存具体的数据 const state = { sum = 0; } //创建并暴露store export default new Vuex.Store({ actions, mutations, state })
-
在
main.js
中创建vm时传入store
配置项...... //引入store import store from './store' ...... //创建vm new Vue({ el:'#app', render: h => h(App), store })
-
初始化数据、配置
actions
、配置mutations
,操作文件store.js
-
组件中读取vuex中的数据:
$store.state.sum
-
组件中修改vuex中的数据:
$store.dispatch('action中的方法名',数据)
或$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
ps:必须在创建store之前use,es6会把import全部提前
5.6Vuex案例
略
5.7Vuex开发工具
和Vue开发工具同一个
5.8store里的getter
相当于配置了store的计算属性
const getters = {
bigSum(state) {
return state.sum * 10
}
}
{{$store.getters.bigSum}}
5.9mapState/getter
先引入 import {mapState} from ‘vuex’
在computed中使用
对象写法
computed:{
...mapState({he:'sum',jian:'jian'})
}
数组写法
computed:{
...mapState(['sum','jian'])
}
5.10mapActs/Mutations
对象写法
<button @click="jia(n)">每次加1</button>
...mapActions({ jia: "jia" }),
...mapMutations({ jia: "JIA" }),
这里默认生成的方法传入的默认值是$event,因此我们需要手动修改传入的参数
数组写法
<button @click="jia(n)">每次加1</button>
...mapActions(['jia']),
...mapMutations(['JIA']),
5.11多组件数据共享
computed:{
...mapState(['sum','jian'])
}
本质就是通过操作$store中的state从而引入state就可以
5.12Vuex模块化
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const countAbout = {
namespaced:true,
actions: {
jia(context, value) {
context.commit("JIA", value)
}
},
mutations: {
JIA(state, value) {
state.sum += value;
}
},
state: {
sum: 98
}
}
export default new Vuex.Store({
modules: {
countAbout
}
})
这里namespaced要true才会被认可s
...mapActions("coutAbout", { jia: "jia" }),
...mapActions("coutAbout", ["jia"]),
methods: {
add() {
const personobj = { id: nanoid(), name: this.name };
this.$store.commit("personAbout/ADD_PERSON", personobj);
this.name = "";
},
},
computed:{
firstP(){
this.$store.getter["personAbout/firstPersonName"];
}
}
这里就需要用路径来解决命名空间问题
Vue-router
6.1介绍和使用
概念
- 路由:一组key-value的对应关系
- 路由器:多个路由需要经过路由器管理
应用场景
实现SPA应用
-
安装vue-router,命令:
npm i vue-router@3
vue2是用的3,vue3用的4 -
应用插件:
Vue.use(VueRouter)
-
编写router配置项:
//引入VueRouter import VueRouter from 'vue-router' //引入Luyou 组件 import About from '../components/About' import Home from '../components/Home' //创建router实例对象,去管理一组一组的路由规则 const router = new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] }) //暴露router export default router
-
实现切换(active-class可配置高亮样式)
<router-link active-class="active" to="/about">About</router-link>
-
指定展示位置
<router-view></router-view>
几个注意点
- pages放路由组件
- 路由组件是频繁挂载和销毁的
- 每个组件都有$route,存放自己路由信息
- 只有一个$router
const router = new VueRouter({
routes:[
{
path:'/about',
component:About,
children:[
{
path:'news',
component:News
}
]
},
{
path:'/home',
component:Home
}
]
})
6.2路由传参
简单示例
-
传递参数
<router-link :to="·/home/message/detail?id=${msg.id}&title=${msg.title}·">跳转</router-link> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link>
-
接收参数:
$route.query.id $route.query.title
6.3路由命名
可以把使用路由的时候替换path
:to={name:‘about’}
6.4路由的params参数
直接示例
-
配置路由,声明接收params参数
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] }
-
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
6.5路由的props配置
作用:让路由组件更方便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
6.6router-link的replace
浏览器的历史记录(栈)push模式
replace模式就是破坏push模式
<router-link replace :to="·/home/message/detail?id=${msg.id}&title=${msg.title}·">跳转</router-link>
6.7编程式路由导航
作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
method:{
pushShow(m){
this.$router.push({
name: '详情',
path: '/home/message/detail'
query:{
id:m.id,
title:m.title
}
})
},
replaveShow(m){
this.$router.replace({
name: '详情',
query:{
id:m.id,
title:m.title
}
})
}
}
回退,前进
methods: {
back() {
this.$router.back();
},
forward() {
this.$router.forward();
},
},
6.8缓存路由组件
实际上我们切换组件的时候会销毁组件,缓存哪个路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive> <keep-alive :include=["News","Messages"]> <router-view></router-view> </keep-alive>
6.9新的生命周期钩子
场景:
- 缓存组件的里面使用了
定时器
,不触发销毁了 - 两个新的钩子是路由组件独有的
- 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
- 具体名字:
activated
路由组件被激活时触发。deactivated
路由组件失活时触发。
6.10路由守卫
- 都是在
src/router/index.js
里面进行配置 - 前置路由过滤之后有的标题要改
- 独享是每个route里面的配置项
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('name') === 'yovvis'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{ console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
-
独享守卫:
beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() } }
-
组件内守卫:
//进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) { }, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) { }
***ps:***组件内守卫的离开是要切换另一个组件的时候调用,而不是像全局路由守卫点击组件前后都调用
6.11路由器工作模式
mode:'history'
-
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
-
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
-
hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
-
history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。(
每次刷新都会找服务器要资源
)
简单创建一个本地服务器
UI组件库
-
PC端
-
pc端
:
```js
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
```
***ps:***组件内守卫的离开是要切换另一个组件的时候调用,而不是像全局路由守卫点击组件前后都调用
6.11路由器工作模式
mode:'history'
-
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
-
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
-
hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
-
history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。(
每次刷新都会找服务器要资源
)
简单创建一个本地服务器
UI组件库
-
PC端
-
pc端