摘要:我們將使用,這是一個(gè)現(xiàn)代,簡(jiǎn)單,漂亮的框架,在內(nèi)部使用并將響應(yīng)式編程概念應(yīng)用于前端編程。驅(qū)動(dòng)程序采用從我們的應(yīng)用程序發(fā)出數(shù)據(jù)的,它們返回另一個(gè)導(dǎo)致副作用的。我們將使用來(lái)呈現(xiàn)我們的應(yīng)用程序。僅采用長(zhǎng)度超過(guò)兩個(gè)字符的文本。
Rxjs 響應(yīng)式編程-第一章:響應(yīng)式
Rxjs 響應(yīng)式編程-第二章:序列的深入研究
Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序
Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整的Web應(yīng)用程序
Rxjs 響應(yīng)式編程-第五章 使用Schedulers管理時(shí)間
Rxjs 響應(yīng)式編程-第六章 使用Cycle.js的響應(yīng)式Web應(yīng)用程序
隨著單頁(yè)應(yīng)用程序的出現(xiàn),網(wǎng)站突然被期望做更多,甚至與“原生”應(yīng)用程序進(jìn)行競(jìng)爭(zhēng)。在嘗試更快地開(kāi)發(fā)Web應(yīng)用程序時(shí),開(kāi)發(fā)人員意識(shí)到特定領(lǐng)域是瓶頸,使Web應(yīng)用程序不像其本地應(yīng)用程序那樣快速和強(qiáng)大。
在Facebook React的帶領(lǐng)下,有幾個(gè)Web框架正在使用著新技術(shù),以便在保持代碼簡(jiǎn)單和聲明式的同時(shí)制作更快的Web應(yīng)用程序。
在本章中,我們將介紹一些開(kāi)發(fā)Web應(yīng)用程序的新技術(shù),例如Virtual DOM。 我們將使用Cycle.js,這是一個(gè)現(xiàn)代,簡(jiǎn)單,漂亮的框架,在內(nèi)部使用RxJS并將響應(yīng)式編程概念應(yīng)用于前端編程。
Cycle.jsCycle.js是RxJS之上的一個(gè)小框架,用于創(chuàng)建響應(yīng)式用戶界面。 它提供了現(xiàn)代框架(如React)中的功能,例如虛擬DOM和單向數(shù)據(jù)流。
Cycle.js以反應(yīng)方式設(shè)計(jì),Cycle.js中的所有構(gòu)建塊都是Observables,這給我們帶來(lái)了巨大的優(yōu)勢(shì)。 它比其他框架更容易掌握,因?yàn)槔斫夂陀洃浀母拍钜俚枚唷?例如,與狀態(tài)相關(guān)的所有操作都不在路徑中,封裝在稱(chēng)為驅(qū)動(dòng)程序的函數(shù)中,我們很少需要?jiǎng)?chuàng)建新的操作。
什么是虛擬DOM?安裝Cycle.js文檔對(duì)象模型(DOM)定義HTML文檔中元素的樹(shù)結(jié)構(gòu)。 每個(gè)HTML元素都是DOM中的一個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都可以使用節(jié)點(diǎn)上的方法進(jìn)行操作。
DOM最初是為了表示靜態(tài)文檔而創(chuàng)建的,而不是我們今天擁有的超級(jí)動(dòng)態(tài)網(wǎng)站。 因此,當(dāng)DOM樹(shù)中的元素經(jīng)常更新時(shí),它的設(shè)計(jì)并不具有良好的性能。 這就是為什么當(dāng)我們對(duì)DOM進(jìn)行更改時(shí)會(huì)出現(xiàn)性能損失。
虛擬DOM是用JavaScript的DOM的映射。 每次我們更改組件中的狀態(tài)時(shí),我們都會(huì)為組件重新計(jì)算一個(gè)新的虛擬DOM樹(shù),并將其與之前的樹(shù)進(jìn)行比較。 如果存在差異,我們只會(huì)渲染這些差異。 這種方法非???,因?yàn)楸容^JavaScript對(duì)象很快,我們只對(duì)“真正的”DOM進(jìn)行絕對(duì)必要的更改。
這種方法意味著我們可以編寫(xiě)代碼,就好像我們?yōu)槊總€(gè)更改生成了整個(gè)應(yīng)用程序UI。 我們不必跟蹤DOM中的狀態(tài)。 在幕后,Cycle.js將檢查每次更新是否有任何不同,并負(fù)責(zé)有效地渲染我們的應(yīng)用程序。
我們可以通過(guò)使用標(biāo)記將它包含在HTML頁(yè)面中來(lái)使用Cycle.js,但這不是使用它的最佳方式,因?yàn)镃ycle.js是以極其模塊化的方式設(shè)計(jì)的。 每個(gè)模塊都盡可能地自我依賴管理,并且包括幾個(gè)模塊。因?yàn)?b>可以輕松加載大量重復(fù)代碼,從而導(dǎo)致不必要的下載和更長(zhǎng)的啟動(dòng)時(shí)間。
相反,我們將使用Node Package Manager,npm和Browserify為我們的最終腳本生成代碼。 首先,我們將創(chuàng)建一個(gè)項(xiàng)目將存在的新文件夾,并安裝我們的項(xiàng)目依賴項(xiàng):
mkdir wikipedia-search && cd wikipedia-search npm install browserify npm install @cycle/core npm install @cycle/dom
第一個(gè)npm命令安裝Browserify,它允許我們?yōu)闉g覽器編寫(xiě)代碼,就像它是Node.js應(yīng)用程序一樣。 使用Browserify,我們可以使用Node.js的模塊加載器,它將明智地包含哪些依賴項(xiàng),使代碼下載盡可能小。 接下來(lái),我們安裝了cycle-core和cycle-dom,它們是Cycle.js的兩個(gè)基本模塊。
有了這個(gè),我們可以創(chuàng)建一個(gè)名為index.js的文件,我們將編輯我們的應(yīng)用程序,然后使用本地Browserify二進(jìn)制文件將其編譯成一個(gè)名為bundle.js的文件:
touch index.js `npm bin`/browserify index.js --outfile bundle.js
上面的命令將遍歷我們的依賴樹(shù)并創(chuàng)建一個(gè)bundle.js文件,其中包含運(yùn)行我們的應(yīng)用程序所需的所有內(nèi)容,包括我們?cè)诖a中需要的任何依賴項(xiàng)。 我們可以在index.html中直接包含bundle.js:
cycle/index.html
我們的項(xiàng)目:維基百科搜索Wikipedia search
在本節(jié)中,我們將構(gòu)建一個(gè)搜索Wikipedia作為用戶類(lèi)型的應(yīng)用程序。
RxJS已經(jīng)使得檢索和處理遠(yuǎn)程數(shù)據(jù)變得容易了,但是,正如第4章“構(gòu)建完整的Web應(yīng)用程序”中所看到的那樣,我們?nèi)匀恍枰^(guò)一些環(huán)節(jié)來(lái)使我們的DOM操作高效。
Cycle.js的目標(biāo)之一是完全消除代碼中的DOM操作。 讓我們從一些基本的腳手架開(kāi)始:
cycle/step1.js
var Cycle = require("@cycle/core"); ? var CycleDOM = require("@cycle/dom") var Rx = Cycle.Rx; ? function main(responses) { return { DOM: Rx.Observable.just(CycleDOM.h("span", "Hi there!")) }; } var drivers = { ? DOM: CycleDOM.makeDOMDriver("#container") }; ? Cycle.run(main, drivers);
這段代碼在屏幕上顯示文字hi!,但已經(jīng)有相當(dāng)多的事情發(fā)生了。 重要的部分是主要功能和驅(qū)動(dòng)對(duì)象。 我們來(lái)看看這些步驟:
我們需要Cycle Core和Cycle DOM驅(qū)動(dòng)程序。 我將在下一節(jié)中解釋Cycle.js驅(qū)動(dòng)程序的內(nèi)容。
主要功能始終是我們應(yīng)用程序的入口點(diǎn)。 它返回一組Observable,一個(gè)用于應(yīng)用程序中的每個(gè)驅(qū)動(dòng)程序。 到目前為止,我們只使用一個(gè)驅(qū)動(dòng)程序:DOM驅(qū)動(dòng)程序。
DOM驅(qū)動(dòng)程序的Observable發(fā)出一個(gè)虛擬樹(shù),我們使用Cycle DOM庫(kù)中的h方法創(chuàng)建。 在這種情況下,我們只創(chuàng)建一個(gè)帶有“Hi there!”文本的span元素。 DOM驅(qū)動(dòng)程序使用該虛擬樹(shù)并從中呈現(xiàn)頁(yè)面上的實(shí)際DOM。
我們創(chuàng)建一個(gè)DOM驅(qū)動(dòng)程序,它將根據(jù)main函數(shù)構(gòu)建DOM樹(shù)。 DOM樹(shù)將構(gòu)建在我們作為參數(shù)傳遞的元素或選擇器中。 在這里傳的是#container。
Cycle.run將main函數(shù)與drivers對(duì)象連接起來(lái),在兩者之間創(chuàng)建循環(huán)流。
Cycle.js驅(qū)動(dòng)程序Cycle.js驅(qū)動(dòng)程序是我們用來(lái)引起副作用的函數(shù)。在我們的程序中,我們應(yīng)該以任何方式修改狀態(tài)。驅(qū)動(dòng)程序采用從我們的應(yīng)用程序發(fā)出數(shù)據(jù)的Observable,它們返回另一個(gè)導(dǎo)致副作用的Observable。
我們不會(huì)經(jīng)常創(chuàng)建驅(qū)動(dòng)程序 - 只有當(dāng)我們需要副作用時(shí),例如修改DOM,從其他接口讀取和寫(xiě)入(例如,本地存儲(chǔ))或發(fā)出請(qǐng)求。 在大多數(shù)應(yīng)用程序中,我們只需要DOM驅(qū)動(dòng)程序(呈現(xiàn)網(wǎng)頁(yè))和HTTP驅(qū)動(dòng)程序(我們可以使用它來(lái)發(fā)出HTTP請(qǐng)求)。 在這個(gè)例子中,我們將使用另一個(gè)JSONP驅(qū)動(dòng)程序。
用戶界面我們需要頁(yè)面的實(shí)際內(nèi)容,而不僅僅是span。 讓我們創(chuàng)建一個(gè)函數(shù)來(lái)創(chuàng)建代表我們頁(yè)面的虛擬樹(shù):
cycle/index.js
function vtreeElements(results) { var h = CycleDOM.h; return h("div", [ h("h1", "Wikipedia Search "), h("input", {className: "search-field", attributes: {type: "text"}}), h("hr"), h("div", results.map(function(result) { return h("div", [ h("a", { href: WIKI_URL + result.title }, result.title) ]); })) ]); }
這個(gè)功能可能看起來(lái)有點(diǎn)奇怪,但不要驚慌。 它使用Virtual Hyperscript,一種用于創(chuàng)建虛擬DOM樹(shù)的特定于域的語(yǔ)言。 Virtual Hyperscript包含一個(gè)名為h的方法。 h以類(lèi)似于HTML的方式聲明節(jié)點(diǎn),但使用JavaScript語(yǔ)言。我們可以通過(guò)將額外的對(duì)象或數(shù)組作為參數(shù)傳遞給h來(lái)向元素添加屬性或?qū)⒆釉馗郊拥剿鼈?。生成的虛擬樹(shù)最終將呈現(xiàn)為真正的瀏覽器DOM。
vtreeElements獲取一組對(duì)象,結(jié)果,并返回一個(gè)虛擬樹(shù),代表我們應(yīng)用程序的簡(jiǎn)單UI。 它呈現(xiàn)一個(gè)輸入字段和一個(gè)由結(jié)果中的對(duì)象組成的鏈接列表,最終將包含Wikipedia的搜索結(jié)果。 我們將使用vtreeElements來(lái)呈現(xiàn)我們的應(yīng)用程序。
使用JSX我們可以使用JSX編寫(xiě)我們的UI,而不是使用h函數(shù),JSX是一種由Facebook發(fā)明的類(lèi)似XML的語(yǔ)法擴(kuò)展,它使得編寫(xiě)虛擬DOM結(jié)構(gòu)更容易,更易讀。 我們的vtreeElements函數(shù)看起來(lái)像這樣:
cycle/index.js
function vtreeElementsJSX(results) { results = results.map(function(result) { var link = WIKI_URL + result.title; return }); return; }Wikipedia Search
{results}
它看起來(lái)不是更好嗎?JSX看起來(lái)對(duì)開(kāi)發(fā)人員來(lái)說(shuō)比較熟悉,因?yàn)樗?lèi)似于HTML,但是我們可以將它與JavaScript代碼一起編寫(xiě),并且我們可以將其視為JavaScript類(lèi)型。 例如,注意我們?nèi)绾蔚Y(jié)果數(shù)組,我們直接返回一個(gè)
由于JSX是一種語(yǔ)法擴(kuò)展,我們需要一個(gè)編譯器將其轉(zhuǎn)換為最終的JavaScript代碼(它看起來(lái)非常像我們上一節(jié)中基于h的代碼)。 我們將使用Babel。 Babel是一個(gè)編譯器,它將現(xiàn)代JavaScript轉(zhuǎn)換為可在任何地方運(yùn)行的JavaScript。它還轉(zhuǎn)換了一些JavaScript擴(kuò)展,例如JSX,也就是之前的用例。
如果要使用JSX,則需要安裝Babel并在編譯項(xiàng)目時(shí)使用它。 幸運(yùn)的是,Babel有一個(gè)名為Babelify的Browserify適配器:
npm install babelify
在每個(gè)使用JSX的文件中,我們需要在文件頂部添加以下行:
/** @jsx hJSX */ var hJSX = CycleDOM.hJSX;
這告訴Babel使用Cycle.js的hJSX適配器來(lái)處理JSX,而不是使用默認(rèn)的React。
現(xiàn)在,當(dāng)我們想要編譯項(xiàng)目時(shí),我們可以使用以下命令:
browserify index.js -t babelify --outfile bundle.js從用戶那里獲取搜索關(guān)鍵詞
我們需要一個(gè)函數(shù)來(lái)返回一個(gè)Observable of URL,它使用用戶輸入的搜索詞來(lái)查詢Wikipedia的API:
cycle/index.js
var MAIN_URL = "https://en.wikipedia.org"; var WIKI_URL = MAIN_URL + "/wiki/"; var API_URL = MAIN_URL + "/w/api.php?" + "action=query&list=search&format=json&srsearch="; function searchRequest(responses) { return responses.DOM.select(".search-field").events("input") .debounce(300) .map(function(e) { return e.target.value }) .filter(function(value) { return value.length > 2 }) .map(function(search) { return API_URL + search }); }
首先,我們聲明一些我們的應(yīng)用程序?qū)⒂糜诓樵僕ikipedia的URL。 在函數(shù)searchRequest中,我們獲取包含應(yīng)用程序中所有驅(qū)動(dòng)程序的響應(yīng)對(duì)象,并在DOM驅(qū)動(dòng)程序中使用get方法。select(element).event(type)的行為與fromEvent類(lèi)似:它采用DOM元素的選擇器和要監(jiān)聽(tīng)的事件類(lèi)型,并返回發(fā)出事件的Observable。
這時(shí),代碼的其余部分看起來(lái)應(yīng)該非常熟悉,因?yàn)樗ㄟ^(guò)我們常用的運(yùn)算符轉(zhuǎn)換Observable值:
節(jié)流結(jié)果最多每300毫秒接收一個(gè)。
提取輸入框的值。
僅采用長(zhǎng)度超過(guò)兩個(gè)字符的文本。
將最終值附加到Wikipedia的API URL。
太棒了! 到目前為止,我們有生成UI的功能和從該UI檢索用戶輸入的功能。我們現(xiàn)在需要添加將從維基百科獲取信息的功能。
修改我們的主要功能你可能已經(jīng)在之前的代碼中注意到main函數(shù)接受了一個(gè)我們沒(méi)有使用的參數(shù),responses。這些是來(lái)自run函數(shù)中的responses。驅(qū)動(dòng)程序和main函數(shù)形成一個(gè)循環(huán)(因此框架的名稱(chēng)):main的輸出是驅(qū)動(dòng)程序的輸入,驅(qū)動(dòng)程序的輸出是main的輸入。請(qǐng)記住,輸入和輸出始終是Observables。
我們使用JSONP查詢Wikipedia,就像我們?cè)诘?章中所做的那樣。我們使用JSONP而不是HTTP來(lái)更容易在本地計(jì)算機(jī)上運(yùn)行此示例,因?yàn)槭褂肏TTP從不同的域檢索數(shù)據(jù)會(huì)導(dǎo)致某些瀏覽器因?yàn)榘踩蜃柚惯@些請(qǐng)求。 在幾乎任何其他情況下,尤其是在生產(chǎn)代碼中,使用HTTP來(lái)檢索遠(yuǎn)程數(shù)據(jù)。
無(wú)論如何,使用JSONP并不影響本章的要點(diǎn)。 Cycle有一個(gè)JSONP的實(shí)驗(yàn)?zāi)K,我們可以使用npm安裝它:
npm install @cycle/jsonp
然后我們?cè)谖覀兊膽?yīng)用中使用它,如下所示:
cycle/step2.js
var Cycle = require("@cycle/core"); var CycleDOM = require("@cycle/dom"); var CycleJSONP = require("@cycle/jsonp"); var Rx = Cycle.Rx; var h = CycleDOM.h; function searchRequest(responses) { return responses.DOM.select(".search-field").events("input") .debounce(300) .map(function(e) { return e.target.value }) .filter(function(value) { return value.length > 2 }) .map(function(search) { return API_URL + search }); } function vtreeElements(results) { return h("div", [ h("h1", "Wikipedia Search "), h("input", {className: "search-field", attributes: {type: "text"}}), h("hr"), h("div", results.map(function(result) { return h("div", [ h("a", { href: WIKI_URL + result.title }, result.title) ]); })) ]); } function main(responses) { return { DOM: Rx.Observable.just(CycleDOM.h("span", "Hey there!")), JSONP: searchRequest(responses) } } var drivers = { DOM: CycleDOM.makeDOMDriver("#container"), JSONP: CycleJSONP.makeJSONPDriver() }; Cycle.run(main, drivers);
我們希望將searchRequest的結(jié)果插入到JSONP方法中,這樣一旦用戶輸入搜索詞,我們就會(huì)用術(shù)語(yǔ)查詢Wikipedia。
為此,我們使用CycleJSONP.makeJSONPDriver創(chuàng)建一個(gè)新的JSONP,它將接收我們?cè)趍ain的返回對(duì)象中放置在屬性JSONP中的任何內(nèi)容。在這之后,當(dāng)我們?cè)谳斎肟蛑幸胨阉髟~時(shí),我們應(yīng)該已經(jīng)在查詢維基百科,但由于我們沒(méi)有將JSONP輸出連接到任何內(nèi)容,我們?cè)陧?yè)面上看不到任何更改。 讓我們改變一下:
cycle/step3.js
function main(responses) { var vtree$ = responses.JSONP .filter(function(res$) { return res$.request.indexOf(API_URL) === 0; }) .mergeAll() .pluck("query", "search") .startWith([]) .map(vtreeElements); return { DOM: vtree$, JSONP: searchRequest(responses) }; }
main通過(guò)其響應(yīng)參數(shù)接收所有驅(qū)動(dòng)程序的輸出。我們可以在respond.JSONP中獲取JSON調(diào)用的結(jié)果,這是我們應(yīng)用程序中所有JSONP響應(yīng)的Observable。完成后,我們可以轉(zhuǎn)換Observable以我們想要的形式獲取搜索結(jié)果:
esponses.JSONP會(huì)在應(yīng)用程序中發(fā)出所有JSONP響應(yīng)。 我們首先在其請(qǐng)求中過(guò)濾包含Wikipedia的API URL的內(nèi)容,以確保我們正在處理相關(guān)的響應(yīng)。
respond.JSONP是一個(gè)Observable of Observables。 對(duì)于每個(gè)響應(yīng),都有一個(gè)Observable。 在這一行中,我們將它們?nèi)空蛊剑虼宋覀儚默F(xiàn)在開(kāi)始處理響應(yīng),而不是它們的Observables。
響應(yīng)是JSON對(duì)象,我們感興趣的信息在query.search屬性中。 我們使用pluck運(yùn)算符來(lái)提取它。
我們不知道我們是否會(huì)有任何結(jié)果,所以至少我們確保我們有一個(gè)空數(shù)組。
最后,我們將vtreeElements函數(shù)應(yīng)用于維基百科的每個(gè)結(jié)果。 這將更新我們的UI。
注意變量名稱(chēng)末尾的$符號(hào)。 在本章中,我采用了Cycle.js代碼中使用的命名約定,它將$添加到變量名稱(chēng),表示它是一個(gè)Observable。 我發(fā)現(xiàn)它可以更容易理解基于Observable的代碼!
前面代碼中最重要的一點(diǎn)是,在最后一步中,我們似乎重新繪制了我們收到的每個(gè)結(jié)果的整個(gè)UI。 但這里是虛擬DOM閃耀的地方。 無(wú)論我們重新呈現(xiàn)頁(yè)面多少次,虛擬DOM將始終確保僅呈現(xiàn)差異,從而使其非常高效。 如果虛擬DOM沒(méi)有更改,則不會(huì)在頁(yè)面中呈現(xiàn)任何更改。
這樣我們就不必?fù)?dān)心添加或刪除元素了。 我們每次只渲染整個(gè)應(yīng)用程序,我們讓Virtual DOM找出實(shí)際更新的內(nèi)容。
Model-View-Intent我們用于構(gòu)建維基百科實(shí)時(shí)搜索的架構(gòu)方法不僅僅是另一個(gè)框架的編程UI方法。結(jié)構(gòu)化代碼背后有一個(gè)設(shè)計(jì)模式,就像我們做的那樣:Model-View-Intent(MVI)。
Model-View-Intent是一個(gè)由Cycle.js創(chuàng)建者AndréStaltz創(chuàng)建的術(shù)語(yǔ),用于受模型 - 視圖 - 控制器(MVC)架構(gòu)啟發(fā)的體系結(jié)構(gòu).在MVC中,我們將應(yīng)用程序的功能分為三個(gè)部分: 模型,視圖和控制器。 在MVI中,三個(gè)組件是模型,視圖和意圖。 MVI旨在適應(yīng)像手套一樣的Reactive編程模型。
MVI是被動(dòng)的,意味著每個(gè)組件都會(huì)觀察其依賴關(guān)系并對(duì)依賴項(xiàng)的更改做出反應(yīng)。 這與MVC不同,MVC中的組件知道其依賴項(xiàng)并直接修改它們。 組件(C)聲明哪些其他組件影響它,而不是明確更新(C)的其他組件。
MVI中的三個(gè)組件由Observables表示,每個(gè)組件的輸出是另一個(gè)組件的輸入。
該模型表示當(dāng)前的應(yīng)用程序狀態(tài)。 它從intent中獲取已處理的用戶輸入,并輸出有關(guān)視圖消耗的數(shù)據(jù)更改的事件。
視圖是我們模型的直觀表示。 它采用具有模型狀態(tài)的Observable,并輸出所有潛在的DOM事件和頁(yè)面的虛擬樹(shù)。
意圖是MVI中的新組件。意圖從用戶獲取輸入并將其轉(zhuǎn)換為我們模型中的操作。如果我們重新調(diào)整和重命名我們的代碼,我們可以在我們的應(yīng)用程序中使這三種組件更清晰:
cycle/index-mvi.js
function intent(JSONP) { return JSONP.filter(function(res$) { return res$.request.indexOf(API_URL) === 0; }) .concatAll() .pluck("query", "search"); } function model(actions) { return actions.startWith([]); } function view(state) { return state.map(function(linkArray) { return h("div", [ h("h1", "Wikipedia Search "), h("input", {className: "search-field", attributes: {type: "text"}}), h("hr"), h("div", linkArray.map(function(link) { return h("div", [ h("a", { href: WIKI_URL + link.title }, link.title) ]); })) ]); }); } function userIntent(DOM) { return DOM.select(".search-field") .events("input") .debounce(300) .map(function(e) { return e.target.value }) .filter(function(value) { return value.length > 2 }) .map(function(search) { return API_URL + search }); } function main(responses) { return { DOM: view(model(intent(responses.JSONP))), JSONP: userIntent(responses.DOM) }; } Cycle.run(main, { DOM: CycleDOM.makeDOMDriver("#container"), JSONP: CycleJSONP.makeJSONPDriver() });
通過(guò)將模型,視圖和意圖拆分為多帶帶的函數(shù),我們使代碼更加清晰。 (另一個(gè)意圖,userIntent,是JSONP驅(qū)動(dòng)程序的輸入。)大多數(shù)應(yīng)用程序邏輯在我們傳遞給main函數(shù)中的DOM驅(qū)動(dòng)程序的屬性中表示為這三個(gè)函數(shù)的組合:
function main(responses) { return { DOM: view(model(intent(responses.JSONP))), JSONP: userIntent(responses.DOM) }; }
它沒(méi)有那么多功能!
創(chuàng)建可重用的小部件隨著我們制作更復(fù)雜的應(yīng)用程序,我們希望重用一些UI組件。 我們的維基百科搜索應(yīng)用程序很小,但是它已經(jīng)有一些可以在其他應(yīng)用程序中重用的組件。 以搜索輸入框?yàn)槔?我們絕對(duì)可以將它變成自己的小部件。
目標(biāo)是將我們的小部件封裝在自己的組件中,以便我們將其用作任何其他DOM元素。 我們還應(yīng)該能夠使用我們想要的任何屬性來(lái)參數(shù)化組件。 然后我們將在我們的應(yīng)用程序中使用它,如下所示:
var wpSearchBox = searchBox({ props$: Rx.Observable.just({ apiUrl: API_URL }) });
我們將使用Cycle.js引入的概念構(gòu)建我們的小部件,它將一個(gè)Observable事件作為輸入,并輸出一個(gè)Observable,其結(jié)果是將這些輸入應(yīng)用于其內(nèi)部邏輯。
讓我們開(kāi)始構(gòu)建搜索框組件。 我們首先創(chuàng)建一個(gè)函數(shù),它接受一個(gè)響應(yīng)參數(shù),我們將從主應(yīng)用程序傳遞任何我們想要的屬性:
cycle/searchbox.js
var Cycle = require("@cycle/core"); var CycleDOM = require("@cycle/dom"); var Rx = Cycle.Rx; var h = CycleDOM.h; var a; function searchBox(responses) { var props$ = responses.props$; var apiUrl$ = props$.map(function (props) { return props["apiUrl"]; }).first(); }
searchBox接收的每個(gè)參數(shù)都是一個(gè)Observable。 在這種情況下,props $是一個(gè)Observable,它發(fā)出一個(gè)包含Wikipedia搜索框配置參數(shù)的JavaScript對(duì)象。
檢索屬性后,我們?yōu)榇翱谛〔考x虛擬樹(shù)。 在我們的例子中,它只是一個(gè)非常簡(jiǎn)單的輸入字段:
cycle/searchbox.js
var vtree$ = Rx.Observable.just( h("div", { className: "search-field" }, [ h("input", { type: "text" }) ]) );
我們希望所有東西都是一個(gè)Observable,所以我們將虛擬樹(shù)包裝在一個(gè)Observable中,它只返回一個(gè)Observable,它發(fā)出我們傳遞它的值。
現(xiàn)在,只要用戶在輸入字段中鍵入搜索詞,我們就需要搜索框來(lái)查詢Wikipedia API。 我們重用上一節(jié)函數(shù)userIntent中的代碼:
cycle/searchbox.js
var searchQuery$ = apiUrl$.flatMap(function (apiUrl) { return responses.DOM.select(".search-field").events("input") .debounce(300) .map(function (e) { return e.target.value; }) .filter(function (value) { return value.length > 3; }) .map(function (searchTerm) { return apiUrl + searchTerm; }); });
我們?nèi)匀恍枰獙earchQuery的輸出連接到JSON驅(qū)動(dòng)程序的輸入。 我們就像在正常的Cycle應(yīng)用程序中那樣做:
cycle/searchbox.js
return { DOMTree: vtree$, JSONPQuery: searchQuery$ };
最后,我們不應(yīng)該忘記導(dǎo)出搜索框小部件:
cycle/searchbox.js
module.exports = searchBox; // Export it as a module
現(xiàn)在我們已準(zhǔn)備好在您的應(yīng)用程序中使用搜索框小部件。 主要方法現(xiàn)在看起來(lái)像這樣:
cycle/index-mvi2.js
var h = CycleDOM.h; ? var SearchBox = require("./searchbox"); function main(responses) { ? var wpSearchBox = SearchBox({ DOM: responses.DOM, props$: Rx.Observable.just({ apiUrl: API_URL }) }); ? var searchDOM$ = wpSearchBox.DOMTree; var searchResults$ = responses.JSONP .filter(function(res$) { return res$.request.indexOf(API_URL) === 0; }) .concatAll() .pluck("query", "search") .startWith([]); return { ? JSONP: wpSearchBox.JSONPQuery, ? DOM: Rx.Observable.combineLatest( searchDOM$, searchResults$, function(tree, links) { return h("div", [ h("h1", "Wikipedia Search "), tree, h("hr"), h("div", links.map(function(link) { return h("div", [ h("a", { href: WIKI_URL + link.title }, link.title) ]); })) ]); }) }; } Cycle.run(main, { DOM: CycleDOM.makeDOMDriver("#container"), JSONP: CycleJSONP.makeJSONPDriver() });
現(xiàn)在我們將處理用戶輸入和呈現(xiàn)搜索框的責(zé)任委托給wpSearchBox小部件,我們可以在另一個(gè)需要查詢URL API的搜索框的應(yīng)用程序中輕松地重用該小部件。 這些是主要的變化:
導(dǎo)入我們剛剛創(chuàng)建的searchBox小部件。
創(chuàng)建一個(gè)SearchBox實(shí)例,傳遞DOM驅(qū)動(dòng)程序和我們想要搜索小部件的屬性。
我們的wpSearchBox最終將從其DOMTree Observable中發(fā)出項(xiàng)目。 我們?cè)谶@里分配它以便在我們渲染實(shí)際DOM時(shí)使用它們。
我們將Wikipedia查詢URL發(fā)送到JSONP驅(qū)動(dòng)程序,以便檢索其結(jié)果。 當(dāng)這些可用時(shí),它將在response.JSONP中發(fā)出它們,我們?cè)趕earchResults中對(duì)它進(jìn)行了優(yōu)化。
為了渲染最終的DOM樹(shù),我們使用combineLatest與searchDOM和searchResults。它們中的每一個(gè)都會(huì)導(dǎo)致布局發(fā)生變化,因此只要這兩個(gè)Observable中的一個(gè)發(fā)出一個(gè)項(xiàng)目,我們就會(huì)重新渲染DOM樹(shù)。
有了最終的代碼,我們可以看到Cycle.js的最大亮點(diǎn)。 框架中沒(méi)有不同的類(lèi),特殊類(lèi)型或“魔術(shù)”。 這是所有無(wú)副作用的函數(shù),它們接受Observable并輸出更多的Observable。 只有這樣,我們才有一個(gè)簡(jiǎn)潔的Web應(yīng)用程序框架,清晰,反應(yīng)靈敏,使用起來(lái)很有趣。 它不惜一切代價(jià)避免副作用,使我們的Web應(yīng)用程序更加健壯。
改進(jìn)的想法除了迫切需要更好的圖形設(shè)計(jì)外,我們的應(yīng)用程序可以使用一些功能,而不僅僅是快速重定向到維基百科的結(jié)果:
讓用戶為特定結(jié)果添加書(shū)簽。 您可以在列表中的每個(gè)結(jié)果旁邊添加一個(gè)小星星,這樣當(dāng)用戶點(diǎn)擊時(shí),它會(huì)將該結(jié)果保存為收藏夾。 你可以將星星變成自己的小部件。 如果您使用某些持久性API(反應(yīng)性?。?,例如本地存儲(chǔ)或IndexedDB,則需要額外的分?jǐn)?shù)。
如果用戶單擊鏈接,則在屏幕右側(cè)顯示結(jié)果的“預(yù)覽”,其中包含概要及其相關(guān)元信息。 如果用戶想要查看實(shí)際的Wikipedia結(jié)果,則可以在其中包含“閱讀更多”鏈接。 將其實(shí)現(xiàn)為小部件。
總結(jié)現(xiàn)在您知道如何開(kāi)發(fā)使用現(xiàn)代技術(shù)的Web應(yīng)用程序而不放棄響應(yīng)性理念。 本章提供了如何使用Observables和RxJS作為其他框架或應(yīng)用程序的內(nèi)部引擎的想法。 通過(guò)站在Observables的肩膀和活躍的生活方式,我們可以極大地簡(jiǎn)化Web應(yīng)用程序并將狀態(tài)降低到最小的表達(dá),使我們的Web應(yīng)用程序不那么脆弱和易于維護(hù)。
感謝您閱讀本書(shū)。 我希望它能幫助您重新思考開(kāi)發(fā)JavaScript應(yīng)用程序的方式,并挑戰(zhàn)一些有關(guān)編程的現(xiàn)有概念。 這是快速,強(qiáng)大和反應(yīng)性的軟件!
關(guān)注我的微信公眾號(hào),更多優(yōu)質(zhì)文章定時(shí)推送
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/108196.html
摘要:由于技術(shù)棧的學(xué)習(xí),筆者需要在原來(lái)函數(shù)式編程知識(shí)的基礎(chǔ)上,學(xué)習(xí)的使用。筆者在社區(qū)發(fā)現(xiàn)了一個(gè)非常高質(zhì)量的響應(yīng)式編程系列教程共篇,從基礎(chǔ)概念到實(shí)際應(yīng)用講解的非常詳細(xì),有大量直觀的大理石圖來(lái)輔助理解流的處理,對(duì)培養(yǎng)響應(yīng)式編程的思維方式有很大幫助。 showImg(https://segmentfault.com/img/bVus8n); [TOC] 一. 響應(yīng)式編程 響應(yīng)式編程,也稱(chēng)為流式編程...
摘要:響應(yīng)式編程第一章響應(yīng)式響應(yīng)式編程第二章序列的深入研究響應(yīng)式編程第三章構(gòu)建并發(fā)程序響應(yīng)式編程第四章構(gòu)建完整的應(yīng)用程序響應(yīng)式編程第五章使用管理時(shí)間響應(yīng)式編程第六章使用的響應(yīng)式應(yīng)用程序使用管理時(shí)間自從接觸,就開(kāi)始在我的項(xiàng)目中使用它。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完...
摘要:響應(yīng)式編程具有很強(qiáng)的表現(xiàn)力,舉個(gè)例子來(lái)說(shuō),限制鼠標(biāo)重復(fù)點(diǎn)擊的例子。在響應(yīng)式編程中,我把鼠標(biāo)點(diǎn)擊事件作為一個(gè)我們可以查詢和操作的持續(xù)的流事件。這在響應(yīng)式編程中尤其重要,因?yàn)槲覀冸S著時(shí)間變換會(huì)產(chǎn)生很多狀態(tài)片段。迭代器模式的另一主要部分來(lái)自模式。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-...
摘要:建立一個(gè)實(shí)時(shí)地震我們將為地震儀表板應(yīng)用程序構(gòu)建服務(wù)器和客戶端部件,實(shí)時(shí)記錄地震的位置并可視化顯示。添加地震列表新儀表板的第一個(gè)功能是顯示地震的實(shí)時(shí)列表,包括有關(guān)其位置,大小和日期的信息。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整的Web應(yīng)用程序Rxjs 響應(yīng)式編程-...
摘要:接下來(lái),我們將實(shí)現(xiàn)一個(gè)真實(shí)的應(yīng)用程序,顯示幾乎實(shí)時(shí)發(fā)生的地震。得到的由表示,其中包含和的合并元素。如果不同同時(shí)傳出元素,合并序列中這些元素的順序是隨機(jī)的。是操作序列的強(qiáng)大操作符。但是的方法仍在運(yùn)行,表明取消并不會(huì)取消關(guān)聯(lián)的。 Rxjs 響應(yīng)式編程-第一章:響應(yīng)式Rxjs 響應(yīng)式編程-第二章:序列的深入研究Rxjs 響應(yīng)式編程-第三章: 構(gòu)建并發(fā)程序Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整...
閱讀 3713·2021-10-09 09:58
閱讀 1272·2021-09-22 15:20
閱讀 2553·2019-08-30 15:54
閱讀 3568·2019-08-30 14:08
閱讀 953·2019-08-30 13:06
閱讀 1882·2019-08-26 12:16
閱讀 2745·2019-08-26 12:11
閱讀 2571·2019-08-26 10:38