幾天不寫簡文,手有點生了,今天繼續。在這幾天里,我讀了一下weex官方文檔,以便更好的閱讀閱讀源碼。另外呢,溫習了一下ES6和ES5. 話說ES6如果大家不熟悉的話,強烈建議了解一下,在這里我向大家推薦一下阮老師的《ECMAScript 6入門》。同時建議大家了解下webpack的使用,因為weex H5的渲染框架都是利用webpack開發的。
說了這么多先上一張圖,這張圖片是我在weex剛開源h5時做的,跟現在的是不一樣的。引用方式上的主要不同是把jsfw.js和h5render.js打包進了weex.js。
言歸正傳,我們進行今天的代碼閱讀。stream.js在瀏覽器中調試的地址是:
在下圖中,我們可以看到,stream提供了兩個方法,一個是sendHttp,另一個是fetch。
1. sendHttp方法
圖中._meta={} 是weex用來聲明api的一部分,主要是聲明我們具體業務中調用的方法。我們也可以通過這種方式建造我們的自己的api
/**
* sendHttp
* @deprecated
* Note: This API is deprecated. Please use stream.fetch instead.
* send a http request through XHR.
* @param {obj} params
* - method: 'GET' | 'POST',
* - url: url requested
* @param {string} callbackId
*/
sendHttp: function (param, callbackId) {
if (typeof param === 'string') {
try {
param = JSON.parse(param)
}
catch (e) {
return
}
}
if (typeof param !== 'object' || !param.url) {
return logger.error(
'invalid config or invalid config.url for sendHttp API')
}
const sender = this.sender
const method = param.method || 'GET'
const xhr = new XMLHttpRequest()
xhr.open(method, param.url, true)
xhr.onload = function () {
sender.performCallback(callbackId, this.responseText)
}
xhr.onerror = function (error) {
return logger.error('unexpected error in sendHttp API', error)
// sender.performCallback(
// callbackId,
// new Error('unexpected error in sendHttp API')
// )
}
xhr.send()
}
我們可以看一下sendHttp方法,上面的備注說明此方法不贊成使用了,請使用stream.fetch替代。作為學習者,我們應該看一下他源碼的寫作方式。
- <code>if (typeof param === 'string') {}</code>這個代碼是為了防止程序員在使用這個函數時,傳進字符串。通過JSON.parse()函數把字符串轉換為json對象。
- <code>const sender = this.sender</code>這句中的this值得思考一下。其實這里面的this,與我們在index.html中
陰影部分是同一個對象。這是在注冊api時傳進來的,實現原理我們將講api注冊源碼時分析。js中可以通過call,apply,bind函數可以調整this的指代,具體方法請自行百度 - 下面就是實例化xmlHttpRequest對象和初始化請求方法,這里面的sender.performCallback()方法是用來調用我們寫的方法的。這一個sender類我們將在下一節分析。
xhr.onload = function () {
sender.performCallback(callbackId, this.responseText)
}
xhr.onerror = function (error) {
return logger.error('unexpected error in sendHttp API', error)
}
- 在使用sendHttp方法時,需要先引入接口!!!可以像下面的的代碼一樣:
require("@weex-module/stream").sendHttp({url:"http://www.baidu.com",method:"POST"},function(data){
console.log(data);
});
2.fetch方法
- fetch方法共包含三部分:
- 常用變量聲明部分
- 請求參數設置部分
- 發送請求部分
- 常用變量聲明部分,這一部分配置的參數可以類比ajax的請求參數
const DEFAULT_METHOD = 'GET'//設置http請求方法,默認為get
const DEFAULT_MODE = 'cors'//設置跨域請求方式,默認使用跨域. 與modeOptions值對應.
const DEFAULT_TYPE = 'text'//設置返回數據的格式,默認使用文本方式.
const methodOptions = ['GET', 'POST']
//cors:支持跨域,no-cors:不支持跨域,same-origin:同源策略,適合微信的方案中
//navigate:這個可能用與頁面跳轉,我目前沒有發現適合在哪用
const modeOptions = ['cors', 'no-cors', 'same-origin', 'navigate']
//text:返回值是字符串,
//json:返回值是json文件或者json對象,如果服務器返回的是json字符串,則自動轉換為json對象。
//jsonp:是用于利用jsonp跨域解決方案中的返回值類型,不建議使用.
//arraybuffer:用于傳遞文件流的類型,可以用來傳輸多媒體文件. 可以使用Blob和FileReader進行讀取
//weex提供Blob和FileReader的是由js原生實現。
const typeOptions = ['text', 'json', 'jsonp', 'arraybuffer']
const sender = this.sender
const config=utils.extend({}, options)
utils.extend()方法讓config繼承了options。
- 請求參數設置部分
if (typeof config.method === 'undefined') {
config.method = DEFAULT_METHOD
logger.warn('options.method for \'fetch\' API has been set to '
+ 'default value \'' + config.method + '\'')
}
else if (methodOptions.indexOf((config.method + '')
.toUpperCase()) === -1) {
return logger.error('options.method \''
+ config.method
+ '\' for \'fetch\' API should be one of '
+ methodOptions + '.')
}
// validate options.url
if (!config.url) {
return logger.error('options.url should be set for \'fetch\' API.')
}
// validate options.mode
if (typeof config.mode === 'undefined') {
config.mode = DEFAULT_MODE
}
else if (modeOptions.indexOf((config.mode + '').toLowerCase()) === -1) {
return logger.error('options.mode \''
+ config.mode
+ '\' for \'fetch\' API should be one of '
+ modeOptions + '.')
}
// validate options.type
if (typeof config.type === 'undefined') {
config.type = DEFAULT_TYPE
logger.warn('options.type for \'fetch\' API has been set to '
+ 'default value \'' + config.type + '\'.')
}
else if (typeOptions.indexOf((config.type + '').toLowerCase()) === -1) {
return logger.error('options.type \''
+ config.type
+ '\' for \'fetch\' API should be one of '
+ typeOptions + '.')
}
// validate options.headers
config.headers = config.headers || {}
if (!utils.isPlainObject(config.headers)) {
return logger.error('options.headers should be a plain object')
}
// validate options.body
const body = config.body
if (!config.headers['Content-Type'] && body) {
if (utils.isPlainObject(body)) {
// is a json data
try {
config.body = JSON.stringify(body)
config.headers['Content-Type'] = TYPE_JSON
}
catch (e) {}
}
else if (utils.getType(body) === 'string' && body.match(REG_FORM)) {
// is form-data
config.body = encodeURI(body)
config.headers['Content-Type'] = TYPE_FORM
}
}
我們可以看到分別對method,url,mode,headers進行了設置。下面僅以設置method進行代碼分析。
if (typeof config.method === 'undefined') {
config.method = DEFAULT_METHOD
logger.warn('options.method for \'fetch\' API has been set to '
+ 'default value \'' + config.method + '\'')
}
else if (methodOptions.indexOf((config.method + '')
.toUpperCase()) === -1) {
return logger.error('options.method \''
+ config.method
+ '\' for \'f)
}
<code>if (typeof config.method === 'undefined') {}</code>用來判斷method是否有輸入,如果沒有輸入,即method為undefined,那么將method設置為默認的方法。
<code>else if (methodOptions.indexOf((config.method + '') .toUpperCase()) === -1) {}</code>是用來判斷輸入的mehod方法是否合法,如果不合法則拋出異常。
- 發送請求部分
發送請求部分中首先判斷是否存在過程處理函數函數,接下來再判斷發送請求使用的方法
const _callArgs = [config, function (res) {
sender.performCallback(callbackId, res)
}]
if (progressCallbackId) {
_callArgs.push(function (res) {
// Set 'keepAlive' to true for sending continuous callbacks
sender.performCallback(progressCallbackId, res, true)
})
}
if (config.type === 'jsonp') {
_jsonp.apply(this, _callArgs)
}
_callArgs對象是用來組裝請求體和返回處理方法的。<code>if(progressCallbackId) {}</code>是用來判斷過程處理函數存在不存在,如果存在則加進_callArgs對象。
關于過程處理函數,請參考官方文檔:
http://alibaba.github.io/weex/doc/modules/stream.html。
非常感謝大家的閱讀,如果喜歡的話,請點擊喜歡并且加關注啊,我近期將會一直更新這個文檔
stream類并沒有閱讀完,我將在下節繼續講解。