Axios使用及源碼解析

簡(jiǎn)介

axios 是一個(gè)用于瀏覽器和Node.js上的基于 Promise 的http網(wǎng)絡(luò)庫(kù)。

大綱

在這里插入圖片描述

使用方式
安裝:

npm install axios

使用:

//引入axios
const axios = require('axios');
import axios from 'axios';

axios的四種使用方式

1. axios(config)

直接將相關(guān)配置包括請(qǐng)求url作為參數(shù)傳入到axios方法中

axios({
    url: 'https://jsonplaceholder.typicode.com/todos/1',
    method: 'get'
}).then(response => {
    console.log(response.data)
}).catch(error => {
    console.log(error)
});
2. axios(url[, config])

還是使用axios方法,但是第一個(gè)參數(shù)傳入請(qǐng)求url,第二個(gè)參數(shù)傳入其他配置參數(shù)。

axios('https://jsonplaceholder.typicode.com/todos/1', {
    method: 'get'
}).then(response => {
    console.log(response.data)
}).catch(error => {
    console.log(error)
});
3. axios[method](url[, config])

使用axios暴露出來的get,post,delete,put等請(qǐng)求方法,參數(shù)設(shè)置同第2種 axios(url[, config])

axios.get('https://jsonplaceholder.typicode.com/todos/1', {
    timeout: 1000
}).then(response => {
    console.log(response.data)
}).catch(error => {
    console.log(error);
});
4. axios.request(config)

使用axios暴露出來的request方法,參數(shù)設(shè)置同第1種axios(config)

axios.request({
    url: 'https://jsonplaceholder.typicode.com/todos/1',
    timeout: 1000
}).then(response => {
    console.log(response.data)
}).catch(error => {
    console.log(error)
});

請(qǐng)求配置

在上一步的發(fā)起請(qǐng)求的方法中,我們都能看到config這個(gè)配置參數(shù),通過設(shè)置這個(gè)參數(shù)的值,可以達(dá)到配置請(qǐng)求的目的。在axios中,config是溝通調(diào)用方和網(wǎng)絡(luò)庫(kù)的橋梁,

常用的配置項(xiàng)如下所示:

{
  // `url` 是用于請(qǐng)求的服務(wù)器 URL,相對(duì)路徑/絕對(duì)路徑
 url: '/api/users',

 // `method` 是創(chuàng)建請(qǐng)求時(shí)使用的http方法,包括get, post, put, delete等
 method: 'get', // default

 // `baseURL` 將自動(dòng)加在 `url` 前面,除非 `url` 是一個(gè)絕對(duì) URL。
 // 它可以通過設(shè)置一個(gè) `baseURL` 便于為 axios 實(shí)例的方法傳遞相對(duì) URL
 baseURL: 'https://some-domain.com/api/',

 // `transformRequest` 允許在向服務(wù)器發(fā)送前,修改請(qǐng)求數(shù)據(jù)
 // 只能用在 'PUT', 'POST' 和 'PATCH' 這幾個(gè)請(qǐng)求方法
 // 后面數(shù)組中的函數(shù)必須返回一個(gè)字符串,或 ArrayBuffer,或 Stream
 transformRequest: [function (data, headers) {
   // 對(duì) data 進(jìn)行任意轉(zhuǎn)換處理
   return data;
 }],

 // `transformResponse` 在傳遞給 then/catch 前,允許修改響應(yīng)數(shù)據(jù)
 transformResponse: [function (data) {
   // 對(duì) data 進(jìn)行任意轉(zhuǎn)換處理
   return data;
 }],

 // `headers` 是即將被發(fā)送的自定義請(qǐng)求頭
 headers: {'X-Requested-With': 'XMLHttpRequest'},

 // `params` 是即將與請(qǐng)求一起發(fā)送的 URL 參數(shù)
 // 必須是一個(gè)無格式對(duì)象(plain object)或 URLSearchParams 對(duì)象
 params: {
   name: 'John'
 },

  // `paramsSerializer` 是一個(gè)負(fù)責(zé) `params` 序列化的函數(shù)
 // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
 paramsSerializer: function(params) {
   return Qs.stringify(params, {arrayFormat: 'brackets'})
 },

 // `data` 是作為請(qǐng)求主體被發(fā)送的數(shù)據(jù)
 // 只適用于這些請(qǐng)求方法 'PUT', 'POST', 和 'PATCH'
 // 在沒有設(shè)置 `transformRequest` 時(shí),必須是以下類型之一:
 // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
 // - 瀏覽器專屬:FormData, File, Blob
 // - Node 專屬: Stream
 data: {
   firstName: 'John'
 },

 // `timeout` 指定請(qǐng)求超時(shí)的毫秒數(shù)(0 表示無超時(shí)時(shí)間)
 // 如果請(qǐng)求花費(fèi)了超過 `timeout` 的時(shí)間,請(qǐng)求將被中斷
 timeout: 1000,

 // `adapter` 允許自定義處理請(qǐng)求,以使測(cè)試更輕松
 // 返回一個(gè) promise 并應(yīng)用一個(gè)有效的響應(yīng) (查閱 [response docs](#response-api)).
 adapter: function (config) {
   /* ... */
 },

// `auth` 表示應(yīng)該使用 HTTP 基礎(chǔ)驗(yàn)證,并提供憑據(jù)
 // 這將設(shè)置一個(gè) `Authorization` 頭,覆寫掉現(xiàn)有的任意使用 `headers` 設(shè)置的自定義 `Authorization`頭
 auth: {
   username: 'janedoe',
   password: 's00pers3cret'
 },

  // `responseType` 表示服務(wù)器響應(yīng)的數(shù)據(jù)類型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
 responseType: 'json', // default

 // `responseEncoding` 表示用于響應(yīng)數(shù)據(jù)的解碼方式 
 responseEncoding: 'utf8', // default

 // `validateStatus` 定義對(duì)于給定的HTTP 響應(yīng)狀態(tài)碼是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者設(shè)置為 `null` 或 `undefined`),promise 將被 resolve; 否則,promise 將被 rejecte
 validateStatus: function (status) {
   return status >= 200 && status < 300; // default
 },
 
 // `cancelToken` 指定用于取消請(qǐng)求的 cancel token
 cancelToken: new CancelToken(function (cancel) {
 }),
 ...
}

攔截器

攔截器,類似于中間件的概念,是axios的核心功能之一,主要是分為兩種:請(qǐng)求攔截器和響應(yīng)攔截器。有了攔截器,我們能在網(wǎng)絡(luò)請(qǐng)求之前,對(duì)網(wǎng)絡(luò)請(qǐng)求配置做處理。在返回?cái)?shù)據(jù)之前,對(duì)返回?cái)?shù)據(jù)做處理。

中間件,攔截器: 一般用于對(duì)一個(gè)目標(biāo)方法的前置或后置切片操作,可以將一些額外的臟邏輯寫到其他的文件中管理,提高目標(biāo)方法的簡(jiǎn)潔性。

使用方式:

//請(qǐng)求攔截器
const requestInterceptor = axios.default.interceptors.request.use((config) => {
   //在請(qǐng)求發(fā)送前,對(duì)請(qǐng)求配置(AxiosRequestConfig)做一些處理
   return config;
}, (error) => {
   return Promise.reject(error);
});

//移除之前添加的攔截器
axios.default.interceptors.request.eject(requestInterceptor);

//響應(yīng)攔截器
axios.default.interceptors.response.use((response) => {
   //對(duì)請(qǐng)求響應(yīng)數(shù)據(jù)做一些處理
   return response;
}, (error) => {
   return Promise.reject(error);
});

取消請(qǐng)求

支持取消請(qǐng)求也是axios的一個(gè)核心功能,在配置中實(shí)現(xiàn)一個(gè)cancelToken的參數(shù)就能取消。

//取消請(qǐng)求
const cancelToken = axios.CancelToken;
const source = cancelToken.source();

axios.get('https://jsonplaceholder.typicode.com/todos/1', {
   cancelToken: source.token
}).then(response => {
   console.log(response.data);
}).catch(error => {
   if(axios.isCancel(error)) {
       console.log(error.message);
   } else {
       console.log(error)
   }
});

source.cancel('canceled by user');

默認(rèn)配置

請(qǐng)求配置可以在每個(gè)請(qǐng)求中單獨(dú)設(shè)置,也可以在defaults中為全局設(shè)置。

//默認(rèn)baseUrl
axios.defaults.baseUrl = 'https://jsonplaceholder.typicode.com';
//默認(rèn)超時(shí)時(shí)間
axios.defaults.timeout = 3000;
//默認(rèn)Authorization頭
axios.defaults.headers.common['Authorization'] = 'AUTH_TOKEN';
數(shù)據(jù)轉(zhuǎn)換
請(qǐng)求數(shù)據(jù)轉(zhuǎn)換
axios.defaults.transformRequest.push((data, headers)=>{
    //處理請(qǐng)求的data
    return data;
});

返回?cái)?shù)據(jù)轉(zhuǎn)換

axios.defaults.transformResponse.push((data, headers)=>{
    //處理返回的data
    return data;
});

源碼解析

源碼分析基于0.19.2版本

首先看下源碼的目錄結(jié)構(gòu):

在這里插入圖片描述

在這里插入圖片描述

請(qǐng)求分析

首先從axios的幾種請(qǐng)求方式來入手,我們從axios庫(kù)中導(dǎo)入的axios對(duì)象。找到源碼axios.js類,可以看到創(chuàng)建的默認(rèn)axios對(duì)象。

//axios.js

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);//創(chuàng)建Axios實(shí)例

  //將context綁定到Axios的request方法上
  //也可以這樣實(shí)現(xiàn):var instance = Axios.prototype.request.bind(context);
  //instance指向了request方法,并且上下文是實(shí)例context
  //所以我們能直接以axios(url, {config})的方式來發(fā)送請(qǐng)求。本質(zhì)上還是調(diào)用的request方法
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance
  //把Axios.prototype上的方法拓展到instance上,同時(shí)上下文是context,也就是this指向context
  //所以我們能以axios.get/post的方式發(fā)送請(qǐng)求
  utils.extend(instance, Axios.prototype, context);

  //將context上的屬性和方法拓展到instance上
  //所以我們以axios.defaults,axios.interceptors能獲取到攔截器和默認(rèn)屬性
  // Copy context to instance
  utils.extend(instance, context);

  return instance;
}

// Create the default instance to be exported
var axios = createInstance(defaults);

module.exports = axios;

從以上源碼可以得知,axios中導(dǎo)出的axios對(duì)象是通過createInstance方法以及默認(rèn)配置defaults來創(chuàng)建的。
createInstance方法沒有僅僅創(chuàng)建Axios實(shí)例,還做了一系列綁定和拓展的操作,使得獲得的Axios實(shí)例支持axios(url,{config})和axios.get/post這種請(qǐng)求方式。

Axios類及request方法分析

從前面的分析可知,不管是創(chuàng)建的默認(rèn)實(shí)例還是用自定義配置創(chuàng)建的實(shí)例,以及axios請(qǐng)求的幾種寫法,都和Axios類以及request方法息息相關(guān)。

function Axios(instanceConfig) {
  this.defaults = instanceConfig; //默認(rèn)配置
  this.interceptors = { //攔截器
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

Axios.prototype.request = function request(config) {
    if (typeof config === 'string') {//為了支持axios(url, {config})這種寫法
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);//合并配置

  //設(shè)置請(qǐng)求方法
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  //通過以promise resolve, reject為一組值的鏈?zhǔn)綌?shù)組,來支持?jǐn)r截器中間件,并將配置參數(shù)傳給dispatchRequest方法
  // config配置--> 請(qǐng)求攔截器 --> dispatchRequest--> 響應(yīng)攔截器
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);//請(qǐng)求攔截器插入到數(shù)組前部
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected); //響應(yīng)攔截器插入到數(shù)組尾部
  });

  while (chain.length) {//遍歷生成最終的請(qǐng)求promise(包含配置信息,請(qǐng)求攔截器,dispatchRequest方法,響應(yīng)攔截器)
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};


//為了支持axios.get(url, config)這種寫法
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url
    }));
  };
});

//為了支持axios.post(url, data, config)這種寫法
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

通過以上源碼,Axios類有默認(rèn)配置和攔截器兩個(gè)屬性值,同時(shí)類上有Axios的核心方法request。

  • request方法只聲明了一個(gè)參數(shù)config,但是通過判斷config的類型是否是字符串,巧妙的支持了axios(url,config)這種寫法。
  • config = mergeConfig(this.defaults,config);合并了默認(rèn)配置和請(qǐng)求上設(shè)置的配置。結(jié)合axios類中的create工廠方法的代碼可以知道,配置信息的優(yōu)先級(jí)由高到低分別是請(qǐng)求方法上的>創(chuàng)建axios實(shí)例的> axios默認(rèn)的
  • axios支持promise是通過在request方法中按照Promise中的then方法中的參數(shù)結(jié)構(gòu),一個(gè)resolve和一個(gè)reject為一組將dispatchRequest,請(qǐng)求攔截器和響應(yīng)攔截器塞進(jìn)數(shù)組中的。
// axios內(nèi)部Promise的簡(jiǎn)要流程
 Promise.resolve(config).then(function requestInterceptorFulfill(config) {
    return config;
  }, function requestInterceptorReject(error) {
    return Promise.reject(error);
  }).then(function dispatchrequest(config) {
    return dispatchRequest(config);
  }, undefined).then(function responseInterceptorFulfill(response) {
    return response;
  }, function responseInterceptorReject(error) {
    return Promise.reject(error);
  });

dispatchRequest分析

通過上面的源碼,我們知道了axios是如何支持?jǐn)r截器的,以及config在內(nèi)部的流動(dòng)方向。其中,有個(gè)dispatchRequest方法,還沒有分析它做了什么。

從字面意思來看,dispatchRequest 就是發(fā)送請(qǐng)求的意思,查看源碼,可以發(fā)現(xiàn)這個(gè)方法主要做了這幾件事情:

1.支持取消請(qǐng)求
2.對(duì)請(qǐng)求數(shù)據(jù)做轉(zhuǎn)換
3.處理請(qǐng)求頭
4.使用網(wǎng)絡(luò)請(qǐng)求適配器adapter以及配置config發(fā)送請(qǐng)求
5.對(duì)返回?cái)?shù)據(jù)做轉(zhuǎn)換

module.exports = function dispatchRequest(config) {

 //如果設(shè)置了cancelToken則直接取消請(qǐng)求,后續(xù)會(huì)分析取消請(qǐng)求的相關(guān)源碼
  throwIfCancellationRequested(config);

  // 確保headers存在
  config.headers = config.headers || {};

  // 對(duì)請(qǐng)求的數(shù)據(jù)做轉(zhuǎn)換
    config.data = transformData(
    config.data,
    config.headers,
    config.transformRequest
  );

  // 合并headers
  config.headers = utils.merge(
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers
  );

  utils.forEach(
    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );

 // 獲取config上設(shè)置的網(wǎng)絡(luò)請(qǐng)求適配器(若沒有則使用默認(rèn)的)
 // axios中有兩個(gè)預(yù)定義的適配器:分別是nodejs中的http和瀏覽器中的XMLHttpRequest
 
  var adapter = config.adapter || defaults.adapter;

  //將配置config傳入adpater中,return這個(gè)promise
  return adapter(config).then(function onAdapterResolution(response) {
    //如果設(shè)置了cancelToken則直接取消請(qǐng)求
    throwIfCancellationRequested(config);

    // 對(duì)返回的數(shù)據(jù)做轉(zhuǎn)換
    response.data = transformData(
      response.data,
      response.headers,
      config.transformResponse
    );

    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);

      // 對(duì)返回的數(shù)據(jù)做轉(zhuǎn)換
      if (reason && reason.response) {
        reason.response.data = transformData(
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }

    return Promise.reject(reason);
  });
};

可以看到,就算是走到了dispatchRequest方法內(nèi)部,也不是真正發(fā)送請(qǐng)求的地方。源碼告訴我們,請(qǐng)求是從adapter內(nèi)部發(fā)送出去的。

adapter-xhr分析

在axios內(nèi)部,默認(rèn)定義了兩種請(qǐng)求適配器,分別是nodejs端的http和瀏覽器端的xhr。在這里主要分析xhr的源碼。

xhr:
即XMLHttpRequest,具體用法可以參考MDN文檔https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

//xhr的精簡(jiǎn)源碼,刪除了一些非重點(diǎn)代碼
module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
  
    //構(gòu)造一個(gè)XMLHttpRequest對(duì)象
    var request = new XMLHttpRequest();

    //構(gòu)造請(qǐng)求完整路徑(相對(duì)路徑->絕對(duì)路徑)
      var fullPath = buildFullPath(config.baseURL, config.url);
      
     //根據(jù)配置config中的數(shù)據(jù),初始化請(qǐng)求
     //open方法三個(gè)參數(shù)分別為:請(qǐng)求方法,url,是否異步
     //https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open  
    request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);

    //設(shè)置監(jiān)聽請(qǐng)求的onreadystatechange回調(diào)事件
    request.onreadystatechange = function handleLoad() {
     //響應(yīng)頭
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
      //響應(yīng)數(shù)據(jù)
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
    
     //構(gòu)造axios中的響應(yīng)對(duì)象
      var response = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config: config,
        request: request
      };
      //根據(jù)響應(yīng)的狀態(tài),返回promise的reslove或reject
      settle(resolve, reject, response);
      request = null;
    };

     //設(shè)置監(jiān)聽請(qǐng)求的onabort回調(diào)事件
     request.onabort = function handleAbort() {
          reject(createError('Request aborted', config, 'ECONNABORTED', request));

      request = null;
    };
        //設(shè)置監(jiān)聽請(qǐng)求的onerror回調(diào)事件
        request.onerror = function handleError() {
    
      reject(createError('Network Error', config, null, request));

      request = null;
    };

   //設(shè)置監(jiān)聽請(qǐng)求的ontimeout回調(diào)事件
    request.ontimeout = function handleTimeout() {
      var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';
      if (config.timeoutErrorMessage) {
        timeoutErrorMessage = config.timeoutErrorMessage;
      }
      reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',
        request));
      request = null;
    };
   
       //若設(shè)置了cancelToken,則取消請(qǐng)求
       if (config.cancelToken) {
          config.cancelToken.promise.then(function onCanceled(cancel) {
        request.abort();//中斷請(qǐng)求
        reject(cancel);//使用cancel信息返回promise的reject
        request = null;
      });
    }

    if (requestData === undefined) {
      requestData = null;
    }

    request.send(requestData);//使用請(qǐng)求數(shù)據(jù)requestData,最終發(fā)送請(qǐng)求
  });
};

可以看到,adapter中封裝了使用XMLHttpRequest的具體細(xì)節(jié),包括,創(chuàng)建XHR對(duì)象,初始化請(qǐng)求,構(gòu)造請(qǐng)求鏈接,設(shè)置請(qǐng)求參數(shù),構(gòu)造響應(yīng)對(duì)象等等

取消請(qǐng)求分析

在前面,我們講到了兩種取消請(qǐng)求的用法,現(xiàn)在就分析下取消請(qǐng)求相關(guān)部分的源碼。

//CancelToken.js
function CancelToken(executor) {

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {//已經(jīng)取消過了
      return;
    }

    //構(gòu)造Cancel類,用于標(biāo)志是否是取消請(qǐng)求,同時(shí)設(shè)置取消請(qǐng)求的信息
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });


    //xhr.js
    if (config.cancelToken) {
      // 處理取消請(qǐng)求的情況
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }

        request.abort();//中斷請(qǐng)求
        reject(cancel);
        request = null;
      });
    }

通過以上源碼,我們知道

1.CancelToken內(nèi)部聲明了promise成員變量this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; });。

2.構(gòu)造CancelToken對(duì)象的時(shí)候,傳入的executor方法在其中執(zhí)行,并傳入了一個(gè)cancel方法作為參數(shù),在這個(gè)cancel方法中,判斷了這個(gè)請(qǐng)求是否已經(jīng)取消過,構(gòu)造了Cancel類,用于存儲(chǔ)取消信息,然后將cancel對(duì)象通過保存的promise的reslove方法傳出去。

3.在xhr代碼中,第二步resolve的cancel對(duì)象,通過then方法繼續(xù)傳遞,并在其中中斷了請(qǐng)求,并通過xhr的promise的reject方法傳到外部。也就是我們使用axios請(qǐng)求的catch中得到的。

4.在使用CancelToken的時(shí)候,會(huì)把第2步中的cancel方法保存下來,當(dāng)需要取消請(qǐng)求的時(shí)候再像這樣調(diào)用。cancel('Cancel by user!')。方法參數(shù)就是Cancel對(duì)象中的message。

梳理一下:

//xhr中的promise
 new Promise((resolve, reject)=>{
        let request = {
            abort: ()=>{
                
            }
        };
        //CancelToken中的promise
        Promise.resolve(new Cancel('Cancel by User!')).then(cancel => {
            request.abort();//中斷請(qǐng)求
            reject(cancel);//將cancel對(duì)象reject出去
        });
    }).catch(error => {
        if(axios.isCancel(error)) { //捕獲在xhr中reject出來的cancel對(duì)象并打印message
            console.log(cancel.message);
        }
    });

參考:【
https://blog.csdn.net/qq_27053493/article/details/97462300
https://www.cnblogs.com/JohnTsai/p/axios.html
https://zhuanlan.zhihu.com/p/156862881
https://zhuanlan.zhihu.com/p/33918784

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,616評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,727評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,928評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,165評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評(píng)論 1 282
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,585評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,892評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 實(shí)在來不及自己寫了 把讀過的文章先轉(zhuǎn)過來 明天再進(jìn)行編輯 axios項(xiàng)目目錄結(jié)構(gòu) 注:因?yàn)槲覀冃枰吹拇a都是...
    vivianXIa閱讀 895評(píng)論 0 1
  • Axios是近幾年非常火的HTTP請(qǐng)求庫(kù),官網(wǎng)上介紹Axios 是一個(gè)基于 promise 的 HTTP 庫(kù),可以...
    milletmi閱讀 3,509評(píng)論 0 9
  • 基類 Axios 跟隨入口 index.js 進(jìn)入/lib/axios.js,第一個(gè)方法則是createInsta...
    丶梅邊閱讀 674評(píng)論 0 1
  • axios 是一個(gè)基于 Promise 的http請(qǐng)求庫(kù),可以用在瀏覽器和node.js中 備注: 每一小節(jié)都會(huì)從...
    Polaris_ecf9閱讀 660評(píng)論 0 1
  • 0、寫在前面 先掌握源碼結(jié)構(gòu)再到實(shí)際的運(yùn)行使用中去復(fù)盤源碼。就是 源碼—>使用—>源碼 的學(xué)習(xí)線路。思維導(dǎo)圖配合文...
    吃自己家大米閱讀 1,393評(píng)論 0 5