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