四、Vue与AJAX
4.1 回顾发送AJAX异步请求的方式
发送AJAX异步请求的常见方式包括:
原生方式,使用浏览器内置的JS对象XMLHttpRequest
- const xhr = new XMLHttpRequest()
- xhr.onreadystatechange = function(){}
- xhr.open()
- xhr.send()
原生方式,使用浏览器内置的JS函数fetch
- fetch(‘url’, {method : ‘GET’}).then().then()
第三方库方式,JS库jQuery(对XMLHttpRequest进行的封装)
- $.get()
- $.post()
第三方库方式,基于Promise的HTTP库:axios (对XMLHttpRequest进行的封装)
- axios.get().then()
axios是Vue官方推荐使用的。
4.2 回顾AJAX跨域
什么是跨域访问?
在a页面中想获取b页面中的资源,如果a页面和b页面所处的协议、域名、端口不同(只要有一个不同),所进行的访问行动都是跨域的。
哪些跨域行为是允许的?
- 直接在浏览器地址栏上输入地址进行访问
- 超链接
- <img src=”其它网站的图片是允许的”>
- <link href=”其它网站的css文件是允许的”>
- <script src=”其它网站的js文件是允许的”>
- ......
哪些跨域行为是不允许的?
- AJAX请求是不允许的
- Cookie、localStorage、IndexedDB等存储性内容是不允许的
- DOM节点是不允许的
AJAX请求无法跨域访问的原因:同源策略
- 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
- AJAX请求不允许跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
解决AJAX跨域访问的方案包括哪些
① CORS方案(工作中常用的)
- 这种方案主要是后端的一种解决方案,被访问的资源设置响应头,告诉浏览器我这个资源是允许跨域访问的:response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
② jsonp方案(面试常问的)
- 采用的是<script src=””>不受同源策略的限制来实现的,但只能解决GET请求。
③ 代理服务器方案(工作中常用的)
- Nginx反向代理
- Node中间件代理
- vue-cli(Vue脚手架自带的8080服务器也可以作为代理服务器,需要通过配置vue.config.js来启用这个代理)
- postMesssage
- websocket
- window.name + iframe
- location.hash + iframe
- document.domain + iframe
- ......
代理服务器方案的实现原理
同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略的。
4.3 演示跨域问题
Vue脚手架内置服务器的地址:http://localhost:8080
可以额外再开启一个其它的服务器,这个服务器随意,例如:node server、Apache服务器、JBOSS服务器、WebLogic服务器、WebSphere服务器、jetty服务器、tomcat服务器......我这里选择的是基于Java语言的一个服务器Tomcat,这个web服务器开启了一个8000端口,提供了以下的一个服务,可以帮助我们获取到一个Bug列表:
http://localhost:8000/bugs/
打开BugList案例的代码,在mounted钩子函数中发送ajax请求,获取bug列表。
vue-cli安装axios库:npm i axios。使用时:import导入axios
以上的访问表示:在8080服务器中发送AJAX请求访问8000服务器,必然会出现AJAX跨域问题:
4.4 启用Vue脚手架内置服务器8080的代理功能
1.简单开启
vue.config.js文件中添加如下配置:
devServer: {
proxy: 'http://localhost:8000' // 含义:Vue脚手架内置的8080服务器负责代理访问8000服务器
}
发送AJAX请求时,地址需要修改为如下:
原理:访问地址是http://localhost:8080/bugs,会优先去8080服务器上找/bugs资源,如果没有找到才会走代理。
另外需要注意的是:这种简单配置不支持配置多个代理。
2.高级开启
支持配置多个代理。
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8000',
pathRewrite:{'^/api', ''},
ws: true, // 支持websocket
changeOrigin: true // true表示改变起源(让目标服务器不知道真正的起源)
},
'/abc': {
target: 'http://localhost:9000',
pathRewrite:{'^/abc', ''},
ws: true, // 默认值true
changeOrigin: true // 默认值true
}
}
}
4.5 使用AJAX改造BugList案例
mounted钩子中发送ajax请求即可。
4.6 Vue插件库vue-resource发送AJAX请求
- 安装:npm i vue-resource
- import vueResource from ‘vue-resource’
- 使用插件:Vue.use(vueResource)
- 使用该插件之后,项目中所有的vm和vc实例上都添加了:$http属性。
- 使用办法:
- this.$http.get(‘’).then() 用法和axios相同,只不过把axios替换成this.$http
4.7天气预报
1、实现效果
2、接口来自:Сurrent weather and forecast - OpenWeatherMap
3、开发者进行注册,获取api key
4、获取当前天气的接口
5、根据城市名字获取经度和纬度的接口
6、功能实现要点:
(1)首先实现静态组件
- App根组件下有两个子组件:Search、Weather
(2)根据城市名获取经度纬度的接口:
- `http://api.openweathermap.org/geo/1.0/direct?q=${this.cityName}&appid=${apiKey}`
- 以上红色字体采用了ES6的模板字符串。
- 经度:const lon = response.data[0].lon
- 纬度:const lat = response.data[0].lat
(3)获取天气信息的接口:
- `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}`
- 天气图标:response.data.weather[0].icon
- 温度:response.data.main.temp
- 湿度:response.data.main.humidity
- 风力:response.data.wind.speed
(4)以上免费接口已经在服务端解决了跨域的问题,可以直接用。
(5)兄弟组件之间通信采用全局事件总线。
(6)以上接口返回的图标的id,根据图标id动态拼接图片地址
(7)初次打开页面的时候,不应该显示天气的任何信息,Weather组件应该是隐藏的。
(8)查询天气过程中,显示“正在拉取天气信息,请稍后...”
(9)如果访问的城市不存在时,应该提示“对不起,请检查城市名”
(9)如果出现其它错误一律显示“网络错误,请稍后再试”
(11)在Search组件中触发事件的时候,将多个属性封装为对象的形式(语义化更明确)传给Weather组件。
- 在Weather组件中可以采用对象的形式一次性接收。
- 注意结构中的代码:<p class="wind">{{weather.windSpeed}} m/s</p>
(12)ES6语法,合并对象
- this.weather = {...this.weather, ...weather}