使用方法

向服务端获取数据的方法。web端使用XMLHTTPRequest对象,node端使用http模块。

get获取数据

根据用户的ID获取数据信息

  1. axios.get('/user?ID=12345')
  2. .then(function (response) {
  3. // handle success
  4. console.log(response);
  5. })
  6. .catch(function (error) {
  7. // handle error
  8. console.log(error);
  9. })
  10. .then(function () {
  11. // always executed
  12. });

也可以传递一个params对象进行请求

  1. axios.get('/user', {
  2. params: {
  3. ID: 12345
  4. }
  5. })
  6. .then(function (response) {
  7. console.log(response);
  8. })
  9. .catch(function (error) {
  10. console.log(error);
  11. })
  12. .then(function () {
  13. // always executed
  14. });

还可以使用async和await语法

  1. async function getUser() {
  2. try {
  3. const response = await axios.get('/user?ID=12345');
  4. console.log(response);
  5. } catch (error) {
  6. console.error(error);
  7. }
  8. }

post提交数据方式

直接提交数据对象作为参数

  1. axios.post('/user', {
  2. firstName: 'Fred',
  3. lastName: 'Flintstone'
  4. })
  5. .then(function (response) {
  6. console.log(response);
  7. })
  8. .catch(function (error) {
  9. console.log(error);
  10. });

创建axios类

根据以上的使用方法,简单实现get的请求方式。以get为实例分析axios原理。

一:axios包含get方法

重新创建一个新的axios类,Nxios。

  1. class Nxios{
  2. constructor(config){
  3. this.defaults = config;
  4. }
  5. // get方法包含url和configs
  6. get(url,config){
  7. config.url = url;
  8. return new Promise((resolve, reject) => {
  9. let xhr = new XMLHttpRequest();
  10. xhr.onreadystatechange = function () {
  11. if (xhr.readyState === 4 && xhr.status === 200) {
  12. resolve({
  13. statusCode: xhr.statusCode,
  14. msg: xhr.statusText,
  15. data: xhr.responseText
  16. })
  17. }
  18. };
  19. xhr.open(config.method, config.baseUrl + config.url, true);
  20. let headers = config.headers
  21. for (let k in headers) {
  22. xhr.setRequestHeader(k, headers[k])
  23. }
  24. xhr.send()
  25. })
  26. }
  27. }
  28. export default Nxios;

通过封装XMLHttpRequest对象请求过程,可以简单实现get的需求。get方法返回promise对象,所以get请求后可以使用then/catch进行数据处理。

通用配置config

使用深copy引用config对象

在进行this.defaults = config赋值时,需要对config对象进行深copy。因为对象的引用都是值的引用,如果一个对象发生修改,则相互引用的对象都发生改变。
创建utils工具库,实现deepCopy方法,也可以直接使用loadsh的cloneDeep方法。

  1. function deepCopy(obj) {
  2. let target = Array.isArray(obj) ? [] : {};
  3. for (let key in obj) {
  4. // 判断key属性值是obj自身属性,而不是原型链上的
  5. if (obj.hasOwnProperty(key)) {
  6. // 判断key对应的值必须存在并且是object对象时,进行迭代操作。
  7. if (obj[key] !== null && typeof obj[key] === 'object') {
  8. target[key] = deepCopy(obj[key])
  9. } else {
  10. //其他空,或者非object时,直接赋值
  11. target[key] = obj[key]
  12. }
  13. }
  14. }
  15. return target
  16. }
  17. export {
  18. deepCopy,
  19. };

然后对nxios对象进行修改

  1. class Nxios{
  2. constructor(config){
  3. this.defaults = deepClone(config);
  4. }
  5. get(url, configs){
  6. // ......
  7. }
  8. }

处理config不同属性

属性分为:全局属性/配置属性/请求属性
在设置axios的config属性时,headers属性是需要和默认的配置属性相加。其它属性是要替换覆盖。
使用单独的merge方法进行处理

  1. function merge(obj1, obj2) {
  2. let target = deepCopy(obj1)
  3. let source = deepCopy(obj2)
  4. Object.keys(source).reduce((prev, key) => {
  5. if(['url', 'method', 'baseUrl'].includes(key)) {
  6. prev[key] = source[key]
  7. }
  8. if(['headers'].includes(key)) {
  9. prev[key] = Object.assign(target[key], source[key]);
  10. }
  11. return prev
  12. }, target)
  13. return target;
  14. }
  15. export {merge}

拦截器interceptor

拦截器,对request方法和response做统一处理,创建interceptorManager类

  1. class interceptorManager{
  2. constructor(){
  3. this.handlers = []
  4. }
  5. use(handlerResolve, handlerReject){
  6. this.handlers.push({
  7. fulfilled: handlerResolve,
  8. rejected: handlerReject
  9. })
  10. }
  11. }
  12. export default interceptorManager;

在nxios中引入interceptorManager类,然后在添加interceptors和在get中处理

  1. class Nxios{
  2. constructor(config){
  3. // this.defaults = config;
  4. this.defaults = deepCopy(config);
  5. this.interceptors = {
  6. request: new interceptorManager,
  7. response: new interceptorManager
  8. }
  9. }
  10. get(url, config){
  11. let configs = merge(this.defaults, config);
  12. configs.url = url;
  13. // console.log(configs)
  14. let promise = Promise.resolve(configs);
  15. // 拦截器request,在promise.then之前,发起请求之前
  16. this.interceptors.request.handlers.forEach((handler)=>{
  17. promise = promise.then(
  18. handler.fulfilled,
  19. handler.rejected
  20. )
  21. })
  22. promise = promise.then(this.dispatch, undefined);
  23. // 拦截器response,在发起请求之后
  24. this.interceptors.response.handlers.forEach(function(handler){
  25. promise = promise.then(
  26. handler.fulfilled,
  27. handler.rejected
  28. )
  29. return promise
  30. })
  31. return promise
  32. }
  33. dispatch(configs){
  34. return new Promise((resolve, reject) => {
  35. let xhr = new XMLHttpRequest();
  36. xhr.onreadystatechange = function () {
  37. if (xhr.readyState === 4 && xhr.status === 200) {
  38. resolve({
  39. statusCode: xhr.statusCode,
  40. msg: xhr.statusText,
  41. data: xhr.responseText
  42. })
  43. }
  44. };
  45. xhr.open(configs.method, configs.baseUrl + configs.url, true);
  46. let headers = configs.headers
  47. // 设置了自定义header后,需要在后端进行header字段设置,可以只把需要添加的设置,也可以使用*表示所有。
  48. // ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , Request-Ajax, *');
  49. for (let k in headers) {
  50. xhr.setRequestHeader(k, headers[k])
  51. }
  52. xhr.send()
  53. })
  54. }
  55. }

适配器Adaptor

测试web

web端使用的ajax.js的XMLHTTPRequest对待,直接刷新index.html进行接口请求,可以通过network查看网络请求。
image.png

测试node

node环境下,使用的是http模块的接口,创建nodeHttp.js

  1. import http from 'http';
  2. export default function(configs) {
  3. return new Promise(function(resolve, reject) {
  4. const postData = ''
  5. const options = {
  6. hostname: 'localhost',
  7. port:4321,
  8. path: '/',
  9. method: 'GET',
  10. headers:{
  11. 'Content-Type': 'application/x-www-form-urlencoded',
  12. 'Content-Length': Buffer.byteLength(postData)
  13. }
  14. };
  15. const req = http.request(options, (res)=>{
  16. res.setEncoding('utf8');
  17. let chunks = [];
  18. let size = 0;
  19. res.on('data', chunk =>{
  20. chunks.push(chunk)
  21. size += chunk.length
  22. })
  23. res.on('end', () =>{
  24. resolve(chunks.join(''));
  25. })
  26. })
  27. req.on('error', (error) =>{
  28. console.error(`error: ${error.message}`);
  29. })
  30. req.write(postData)
  31. req.end()
  32. })
  33. }

注:node环境下测试,使用server/server_request.js,但是文件让node运行时支持ECMA语法,必须使用
node —experimental-specifier-resolution=node server_request.js
进行启动测试
使用server_request.js 进行测试

  1. import nxios from '../client/js/nxios/index.js'
  2. // 在node环境中使用ECMA语法,需要使用实验配置node --experimental-specifier-resolution=node server_request.js
  3. nxios.get('/')
  4. .then(response =>{
  5. console.log('object', response);
  6. })

执行成功,打印出hello world。

项目代码地址:https://github.com/shenshuai89/axioscopy