日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

剥开比原看代码(十七):比原是如何显示交易的详细信息的?

發布時間:2025/3/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 剥开比原看代码(十七):比原是如何显示交易的详细信息的? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:freewind

比原項目倉庫:

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockchain/bytom

在上上篇文章里,我們還剩下一個小問題沒有解決,即前端是如何顯示一個交易的詳細信息的。

先看對應的圖片:

這個圖片由于太長,分成了兩個,實際上可以看作一個。

那么這個頁面是怎么來的呢?這是在前面以列表的方式顯示交易摘要信息后,可以點擊摘要信息右上角的“查看詳情”鏈接打開。

那我們在本文看一下,比原是如何顯示這個交易的詳細信息的。

由于它分成了前后兩端,那么我們跟以前一樣,把它再分成兩個小問題:

  • 前端是怎么向后臺發送請求,并顯示數據的
  • 后端是如何拿到相應的數據發送給前臺的
  • 需要說明的是,這個表格中包含了很多信息,但是我們在本文并不打算去解釋。因為能看懂的一看就能明白,看不懂的就需要準確的了解了比原的核心之后才能解釋清楚,而這一塊等到我們晚點再專門研究。

    前端是怎么向后臺發送請求,并顯示數據的

    首先我們看一下顯示交易詳細信息頁面的路由path是多少。當我們把鼠標放在交易摘要頁面右上角的“查看詳情”時,會發現url類似于:

    http://localhost:9888/dashboard/transactions/2d94709749dc59f69cad4d6aea666586d9f7e86b96c9ee81d06f66d4afb5d6dd 復制代碼

    其中http://localhost:9888/dashboard/可以看作是這個應用的根路徑,那么路由path應該就是/transactions/2d94709749dc59f69cad4d6aea666586d9f7e86b96c9ee81d06f66d4afb5d6dd,后面那么長的顯然是一個id,所以我們應該到代碼中尋找類似于/transactions/:id這樣的字符串,哦,遺憾的是沒有找到。。。

    那只能從頭開始了,先找到前端路由的定義:

    src/routes.js#L15-L35

    // ... import { routes as transactions } from 'features/transactions'// ...const makeRoutes = (store) => ({path: '/',component: Container,childRoutes: [// ...transactions(store),// ...] }) 復制代碼

    其中的transactions就是我們需要的,而它對應了features/transactions/routes.js:

    src/features/transactions/routes.js#L1-L21

    import { List, New, AssetShow, AssetUpdate } from './components' import { makeRoutes } from 'features/shared'export default (store) => {return makeRoutes(store,'transaction',List,New,Show,// ...) } 復制代碼

    這個函數將會為transactions生成很多相關的路由路徑。當我們把一些組件,比如列表顯示List,新建New,顯示詳情Show等等傳進去之后,makeRoutes就會按照預先定義好的路徑規則去添加相關的path。我們看一下makeRoutes:

    src/features/shared/routes.js#L1-L44

    import { RoutingContainer } from 'features/shared/components' import { humanize } from 'utility/string' import actions from 'actions'const makeRoutes = (store, type, List, New, Show, options = {}) => {const loadPage = () => {store.dispatch(actions[type].fetchAll())}const childRoutes = []if (New) {childRoutes.push({path: 'create',component: New})}if (options.childRoutes) {childRoutes.push(...options.childRoutes)}// 1. if (Show) {childRoutes.push({path: ':id',component: Show})}return {// 2. path: options.path || type + 's',component: RoutingContainer,name: options.name || humanize(type + 's'),name_zh: options.name_zh,indexRoute: {component: List,onEnter: (nextState, replace) => {loadPage(nextState, replace)},onChange: (_, nextState, replace) => { loadPage(nextState, replace) }},childRoutes: childRoutes} } 復制代碼

    這段代碼看起來眼熟,因為我們在之前研究余額和交易的列表顯示的時候,都見過它。而我們今天關注的是Show,即標記為第1處的代碼。

    可以看到,當傳進來了Show組件時,就需要為其生成相關的路由path。具體是在childRouters中添加一個path為:id,而它本身的路由path是在第2處定義的,默認為type + 's',而對于本例來說,type的值就是transaction,所以Show所對應的完整path就是/transactions/:id,正是我們所需要的。

    再回到第1處代碼,可以看到Show組件是從外部傳進來的,從前面的函數可以看到它對應的是src/features/transactions/components/Show.jsx。

    我們進去看一下這個Show.jsx,首先是定義html組件的函數render:

    src/features/transactions/components/Show.jsx#L16-L96

    class Show extends BaseShow {render() {// 1.const item = this.props.itemconst lang = this.props.langconst btmAmountUnit = this.props.btmAmountUnitlet viewif (item) {// ..view = <div><PageTitle title={title} /><PageContent>// ...<KeyValueTabletitle={lang === 'zh' ? '詳情' : 'Details'}items={[// ...]}/>{item.inputs.map((input, index) =><KeyValueTable// .../>)}{item.outputs.map((output, index) =><KeyValueTable// .../>)}</PageContent></div>}return this.renderIfFound(view)} } 復制代碼

    代碼被我進行了大量的簡化,主要是省略了很多數據的計算和一些顯示組件的參數。我把代碼分成了2部分:

  • 第1處需要注意的是類似于const item = this.props.item這樣的代碼,這里的item就是我們要展示的數據,對應本文就是一個transaction對象,它是從this.props中拿到的,所以我們可以推斷在這個文件(或者引用的某個文件)中,會有一個connect方法,把store里的數據塞過來。一會兒我們去看看。后面兩行類似就不說了。
  • 第2處代碼主要就是頁面view的定義了,可以看到里面主要是用到了另一個自定義組件KeyValueTable。代碼我們就不跟過去了,參照前面的頁面效果我們可以想像出來它就是以表格的形式把一些key-value數據顯示出來。
  • 那我們繼續去尋找connect,很快就在同一個頁面的后面,找到了如下的定義:

    src/features/transactions/components/Show.jsx#L100-L117

    import { actions } from 'features/transactions' import { connect } from 'react-redux'const mapStateToProps = (state, ownProps) => ({item: state.transaction.items[ownProps.params.id],lang: state.core.lang,btmAmountUnit: state.core.btmAmountUnit,highestBlock: state.core.coreData && state.core.coreData.highestBlock })// ...export default connect(mapStateToProps,// ... )(Show) 復制代碼

    我只留下了需要關注的mapStateToProps。可以看到,我們在前面第1處中看到的幾個變量的賦值,在這里都有定義,其中最重要的item,是從store的當前狀態state中的transaction中的items中取出來的。

    那么state.transaction是什么呢?我開始以為它是我們從后臺取回來的一些數據,使用transaction這個名字放到了store里,結果怎么都搜不到,最后終于發現原來不是的。

    實際情況是,在我們定義reducer的地方,有一個makeRootReducer:

    src/reducers.js#L1-L62

    // ... import { reducers as transaction } from 'features/transactions' // ...const makeRootReducer = () => (state, action) => {// ...return combineReducers({// ...transaction,// ...})(state, action) } 復制代碼

    原來它是在這里構建出來的。首先{ transaction }這種ES6的語法,換成平常的寫法,就是:

    {transaction: transaction } 復制代碼

    另外,combineReducers這個方法,是用來把多個reducer合并起來(可能是因為store太大,所以把它拆分成多個reducer管理,每個reducer只需要處理自己感興趣的部分),并且合并以后,這個store就會變成大概這樣:

    {"transaction": { ... },// ... } 復制代碼

    所以前面的state.transaction就是指的這里的{ ... }。

    那么繼續,在前面的代碼中,可以從state.transaction.items[ownProps.params.id]看到,state.transaction還有一個items的屬性,它持有的是向后臺/list-transactions取回的一個transaction數組,它又是什么時候加上去的呢?

    這個問題難倒了我,我花了幾個小時搜遍了比原的前后端倉庫,都沒找到,最后只好使出了Chrome的Redux DevTools大法,發現在一開始的時候,items就存在了:

    在圖上有兩個紅框,左邊的表示我現在選擇的是初始狀態,右邊顯示最開始transaction就已經有了items,于是恍然大悟,這不跟前面是一樣的道理嘛!于是很快找到了定義:

    src/features/transactions/reducers.js#L7-L16

    export default combineReducers({items: reducers.itemsReducer(type),queries: reducers.queriesReducer(type),generated: (state = [], action) => {if (action.type == 'GENERATED_TX_HEX') {return [action.generated, ...state].slice(0, maxGeneratedHistory)}return state}, }) 復制代碼

    果然,這里也是用combineReducers把幾個reducer組合在了一起,所以store里就會有這里的幾個key,包括items,以及我們不關心的queries和generated。

    花了一下午,終于把這塊弄清楚了。看來對于分析動態語言,一定要腦洞大開,不能預設原因,另外要利用各種調試工具,從不同的角度去查看數據。要不是Redux的Chrome插件,我不知道還要卡多久。

    我個人更喜歡靜態類型的語言,對于JavaScript這種,除非萬不得以,能躲就躲,主要原因就是代碼中互相引用的線索太少了,很多時候必須看文檔、代碼甚至去猜,無法利用編輯器提供的跳轉功能。

    知道了state.transaction.items的來歷以后,后面的事情就好說了。我們是從state.transaction.items[ownProps.params.id]拿到了當前需要的transaction,那么state.transaction.items里又是什么時候放進去數據的呢?

    讓我們再回到前面的makeRoutes:

    src/features/shared/routes.js#L1-L44

    // ... import actions from 'actions'const makeRoutes = (store, type, List, New, Show, options = {}) => {// 2.const loadPage = () => {store.dispatch(actions[type].fetchAll())}// ...return {path: options.path || type + 's',component: RoutingContainer,name: options.name || humanize(type + 's'),name_zh: options.name_zh,indexRoute: {component: List,onEnter: (nextState, replace) => {loadPage(nextState, replace)},// 1. onChange: (_, nextState, replace) => { loadPage(nextState, replace) }},childRoutes: childRoutes} } 復制代碼

    在上面的第1處,對于indexRoute,有一個onChange的觸發器。它的意思是,當路由的path改變了,并且新的path屬于當前的這個index路由的path(或者子path),后面的函數將會觸發。而后面函數中的loadPage的定義在第2處代碼,它又會將actions[type].fetchAll()生成的action進行dispatch。由于type在本文中是transaction,通過一步步追蹤(這里稍有點麻煩,不過我們在之前的文章中已經走過),我們發現actions[type].fetchAll對應了src/features/shared/actions/list.js:

    src/features/shared/actions/list.js#L4-L147

    export default function(type, options = {}) {const listPath = options.listPath || `/${type}s`const clientApi = () => options.clientApi ? options.clientApi() : chainClient()[`${type}s`]// ...const fetchAll = () => {// ...}// ...return {// ...fetchAll,// ...} } 復制代碼

    如果我們還對這一段代碼有印象的話,就會知道它最后將會去訪問后臺的/list-transactions,并在拿到數據后調用dispatch("RECEIVED_TRANSACTION_ITEMS"),而它將會被下面的這個reducer處理:

    src/features/shared/reducers.js#L6-L28

    export const itemsReducer = (type, idFunc = defaultIdFunc) => (state = {}, action) => {if (action.type == `RECEIVED_${type.toUpperCase()}_ITEMS`) {// 1.const newObjects = {}// 2.const data = type.toUpperCase() !== 'TRANSACTION' ? action.param.data : action.param.data.map(data => ({...data,id: data.txId,timestamp: data.blockTime,blockId: data.blockHash,position: data.blockIndex}));// 3. (data || []).forEach(item => {if (!item.id) { item.id = idFunc(item) }newObjects[idFunc(item)] = item})return newObjects}// ...return state } 復制代碼

    依次講解這個函數中的三處代碼:

  • 第1處是創建了一個新的空對象newObjects,它將在最后替代state.transaction.items,后面會向它里面賦值
  • 第2處是對傳進來的數據進行一些處理,如果type是transaction的話,會把數組中每個元素中的某些屬性提升到根下,方便使用
  • 第3處就是把各個元素放到newObjects中,id為key,對象本身為value
  • 經過這些處理以后,我們才能使用state.transaction.items[ownProps.params.id]拿到合適的transaction對象,并且由Show.jsx顯示。

    前端這塊基本上弄清楚了。我們繼續看后端

    后端是如何拿到相應的數據發送給前臺的

    前面我們說過,根據以往的經驗,我們可以推導出前端會訪問后端的/list-transactions這個接口。我們欣喜的發現,這個接口我們正好在前一篇文章中研究過,這里就可以完全跳過了。

    到今天為止,我們終于把“比原是如何創建一個交易的”這件事的基本流程弄清楚了。雖然還有很多細節,以及觸及到核心的知道都被忽略了,但是感覺自己對于比原內部的運作似乎又多了一些。

    也許現在積累的知識差不多了,該向比原的核心進發了。在下一篇,我將會嘗試理解和分析比原的核心,在學習的過程中,可能會采用跟目前探索流程分解問題不同的方式。另外,可能前期會花不少時間,所以下一篇出來得會晚一些。當然,如果失敗了,說明我目前積累的知識還是不夠,我還需要再回到當前的做法,想辦法再從不同的地方多剝一些比原的外殼,然后再嘗試。

    總結

    以上是生活随笔為你收集整理的剥开比原看代码(十七):比原是如何显示交易的详细信息的?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产亚洲第一页 | 国产精品午夜无码专区 | aaa在线视频 | 日本成人在线看 | 日韩欧美亚洲国产精品字幕久久久 | 91视频专区| 污片网站在线观看 | 日本不卡网 | 国产videos| 国产又黄又猛的视频 | 日本在线不卡一区 | 伊是香蕉大人久久 | 欧美精品自拍视频 | 欧美成人黄色片 | 四虎精品永久在线 | jjzz在线| 香蕉视频911 | 在线播放精品视频 | 人人射人人爽 | 超碰人人在线观看 | 日韩熟妇一区二区三区 | 黑人干亚洲 | 亚洲激情自拍 | 天天操天天舔 | 四虎成人精品永久免费av | 91福利在线免费观看 | 99久久99久久精品国产片果冰 | 午夜久久网 | 中文天堂在线播放 | 国产精品福利片 | 啪啪无遮挡 | 深夜久久久 | 欧美激情综合五月色丁香 | 91网站永久免费看nba视频 | 丰满女人又爽又紧又丰满 | 日本在线加勒比 | 福利在线小视频 | www.成人在线| 欧美黄网站在线观看 | 夜夜骚av一区二区三区 | 天天弄天天干 | 操模特| 日本国产在线播放 | 色婷婷网| 中文在线日韩 | 91福利在线视频 | 久久久久久高清 | 国产福利视频网站 | 4438x亚洲最大 | 蜜臀久久99精品久久久无需会员 | 免费国产精品视频 | 又黄又爽又色的视频 | 国产综合久久久久久鬼色 | 成人免费看片载 | 污黄视频在线观看 | 日韩久久久久久久久 | 色人阁在线视频 | 5a毛片| 新婚若妻侵犯中文字幕 | 亚洲片在线观看 | 四季av一区二区三区免费观看 | 国产精品色在线网站 | 蜜臀av性久久久久av蜜臀妖精 | 蜜桃免费在线视频 | 亚洲熟女综合色一区二区三区 | 欧美黄色片免费看 | 三级毛毛片 | www.国产| 日本在线视频www色 国产在线视频网址 | 成 年 人 黄 色 大 片大 全 | 红猫大本营在线观看的 | 青草成人免费视频 | 五月婷婷七月丁香 | 中文字幕一区二区精品 | 女同中文字幕 | 亚洲一区中文 | 精品国产一区二区三区久久久久久 | 亚洲欧美日韩精品色xxx | 免费国产91 | 一本一道久久综合狠狠老精东影业 | 少妇喷水在线观看 | 天堂av观看 | 日韩成人福利 | 国产视频手机在线 | 就去吻综合 | 亚洲国产视频一区二区 | www.香蕉视频在线观看 | 日韩欧美国产一区二区在线观看 | 在线观看免费av网址 | 亚洲精品综合在线观看 | 成人免费xxxxx在线观看 | 成人性生交大片免费看中文 | 黄色一级播放 | 黄黄视频在线观看 | 麻豆视频在线观看 | 国产情侣一区二区三区 | 黄色三极片 | 91传媒网站 | 久久精品aⅴ无码中文字字幕重口 |