摘要:此文已由作者張磊授權網易云社區發布。歡迎訪問網易云社區,了解更多網易技術產品運營經驗。前言在對蜂巢項目從遷移到的過程中,遇到的問題,以及在此過程中所使用的解決方案。三個頭是一致的。文章來源網易云社區
此文已由作者張磊授權網易云社區發布。
歡迎訪問網易云社區,了解更多網易技術產品運營經驗。
前言
在對蜂巢項目從 nej + regularjs 遷移到 vue 的過程中,遇到的問題,以及在此過程中所使用的解決方案。
遇到的問題
父子頁面通信
項目分為待重構的模塊和已重構的模塊,待重構的模塊是使用 nej 和 regular ,重構的模塊是 vue。頁面是通過 iframe 引用重構的模塊。
這里會涉及到幾個問題。iframe 和父窗口數據交換問題、模態框、以及路由同步的問題。 這三個問題的解決方案都是使用了一個通信機制。這個通信機制對數據進行了序列化(在 ie 下,不序列化會遇到暗坑),所以函數是無法進行傳遞的,只可以傳遞值,然后通過 JSON.stringify 序列化成字符串,傳遞過去后再反序列化成相應類型。采用這種形式,在項目后期,對代碼進行批量修改的時候查找也很方便,甚至可以將通信機制二次改造,而不需要改業務代碼。
問題需要特殊說明的有:
模態框
由于 iframe 并不是鋪滿整個頁面,在 iframe 內部實現模態框的時候,導致導航欄不會被覆蓋掉。于是就可以看到頁面的一部分變灰,但導航欄還是可以點擊的。同時模態框的居中是相對于 iframe 的,所以看起來也不是特別居中。暫時解決方案,是使用通信機制傳參調用父頁面的模態框的邏輯。2. 路由同步
nej 和 vue 都有一套路由方案,但是路由的格式是不一致的,同時模塊的命名方案也會不一致,再者 iframe 和 父頁面路由的變更都需要通知到對方。
接下來講通信機制如何解決問題的。
API
/**
* 發送信息 * @param {string} receiver 收件人 * @param {string} action 描述 * @param {*} [msg] 內容 * @param {function} [cb] 回執函數 * @param {boolean} [isTemp] 如果收件人不存在,通過設置這個參數來指定該消息是丟棄還是保存,當未來某時刻收件人存在的時候,會依次讀取保存的信息,一般不需要指定。 */
send(receiver, action, msg, cb, isTemp) {
},
// Bridge.send("parent", "urlchange", "/module/list");
// Bridge.send("parent", "error", "網絡出錯");
action 可以在父子頁面的 handles 對象里注冊。eg:
// 父頁面const handles = {
show() { toggle(true); }, hide() { toggle(false); }, hideModal: _u._$hideModal, alert(options) { _u._$alert(options); }, error(msg) { CloudUI.Toast.error(msg); }, confirm(options, cb) { _u._$confirm(Object.assign({ onok() { cb("sub", { msg: true, }); }, oncancel() { cb("sub", { msg: false, }); }, }, options)); },
}
// 子頁面handles.urlchange = function urlchange(path) {
router.replace(path);
};
再來一些復雜的例子
同步調用
子頁面向父頁面傳遞數據
Bridge.send("parent", "urlchange", "/module/list");// 這里注意一點,為了以后方便,在 vue 模塊內部使用的路由均是 vue 的,vue 路由向 nej 路由的轉換在 /src/html/module/vue/map.js 進行配置,配置信息如下:// {// "/m/module/": "/module/list",// }
父頁面向子頁面傳遞數據
Bridge.send("sub", "urlchange", "/module/list");// 而在 nej 模塊,寫路由就可以隨意點,可以直接寫 vue 的路由,也可以讓其進行轉換,nej 的模塊在后面均會丟棄,所以允許隨意一點
異步調用
// send(receiver, action, msg, cb, isTemp)Bridge.send("parent", "confirm", { content: "所選快照正在維護中,創建可能需要等待較長時間,建議稍后再試。", okButton: "繼續創建", cancelButton: "稍后再試", primaryButton: "cancelButton",
}, (err, status) => { if (status) { this.create();
} else { this.submitting = false; }
});// 其中第四個參數是回調函數 callback,模仿 nodejs 的實現,err 存在的時候就是失敗,第二個參數是調用返回的 message??梢詤⒁娚厦?handles.confirm。
路由
vue 以及 vue-router 支持的異步加載僅僅是組件級別的,而不是路由級別的,所以實現路由級別的異步加載就會繞一些。eg:
// 一般實現const Create = () => import("./create.vue");const List = () => import("./list.vue");
這種方案下,webpack 會對每一個路由進行打包,導致一個路由一個 chunk 的模式,前端加載負擔過大。實際上,我們需要的粒度可能沒有這么細,在這里使用一種 vue-router 官方的方案(滑到頁面底部)。
// 優化實現const Create = () => import(/ webpackChunkName: "a" / "./create.vue");const List = () => import(/ webpackChunkName: "a" / "./list.vue");
這里是使用注釋 / webpackChunkName: "a" / 來標明打入同一個 chunk a 中。唯一的坑點是使用注釋。當然還有一種方案進行處理。eg:
// index.jsexport { default as create } from "./create.vue";export { default as list } from "./list.vue";// route.jsconst Create = () => import("./index.js").then((modules) => modules.create);
路由寫在每個模塊的下面,只存在一個文件。
eg:
// 建議- modules
- moduleA - routes.js
不建議在子目錄放置路由,不清晰,完整的路由,可能需要打開多個文件,才能看到
// 不建議- modules
- moduleA - detail - routes.js - routes.js
建議方案會產生 routes.js 文件變的很龐大的問題,不方便查看??蓞⒖既缦聦懛?,可以緩解此問題:
const routesA = [];const routesB = [];export default [
...routesA, ...routesB,
]
路由的劃分問題
eg:
// 不建議const router = [
{ path: "/", component: () => import(/* webpackChunkName: "module" */ "./list.vue"), children: [ { path: "tab1", name: "module.list.tab1", // ... }, { path: "tab2", name: "module.list.tab2", // ... }, { path: "tab3", name: "module.list.tab3", // ... }, ], }, { path: "tab1/edit", name: "module.edit.tab1", // ... }
];
module 模塊頁,有三個 tab。三個 tab 頭是一致的。 list.vue 的代碼僅僅是實現了 3個 tab 一致的部分即頭部。觀察 tab1/edit 和 tab1,在邏輯層面上它們應該被放到一起,但是 path: "/" 所在的組件的 dom 中含有 3個 tab 的頭,導致沒有辦法寫在一起(寫在一起的話同時會繼承頭部),權限控制更顯麻煩。更好的做法是 path: "/" 這一層級不做任何 dom 相關的東西,寫到每個 tab 內部。
// 建議const tab1 = { path: "tab1", // 權限控制 tab1 的準入 children: [
{ path: "list", name: "module.tab1.list", // ... }, { path: "edit", name: "module.tab1.edit", // ... }, ],
};
const router = [
{ path: "/", // 權限控制 module 的準入 children: [ tab1, { path: "tab2", name: "module.tab1.list", // ... }, { path: "tab3", name: "module.tab3.list", // ... }, ], }
];
當然可以根據權限控制進行調整,寫法不是很固定。交互可能不太喜歡定義 path: "list", 但是第一種寫法,相當于污染了整個路由的頂層,那后面必須定義多個頂層進行覆蓋,由模塊單入口路由變成了模塊多入口路由。
openapi 和 webapi 數據轉換
舉例說明:在模塊從 webapi 遷移到 openapi 的時候使用了一種方案,在數據獲取層面對數據進行轉換。即:
// openapiconst result1 = { Id: 1, Name: 2,
};// webapiconst result2 = { id: 1, name: 2,
};// transform(result1) 后的數據結構包含 result2 中有用的數據結構// 這個 transform 函數會將 openapi 的數據轉換成 webapi 的,這樣只需要改數據結構,讓新老保持一致,再修改少量的業務邏輯即可完成接口遷移工作。
這個問題本身屬于后端接口變更,與框架遷移屬于并行任務,多帶帶拿來看并無關聯,問題放在一起的時候,就變得棘手了。
在對 win 模塊進行遷移的時候,在使用 vue 的時候希望接口方面使用 opeanpi 的數據,不進行數據轉換。但是在使用老模塊的時候,為了盡量少的動業務代碼,對 opeanpi 的數據進行了轉換,那就意味著兩者的數據的并不一致,在使用上面提到的通信機制(調用父頁面的模態框,需要傳遞數據)的時候,這就很致命了,意味著一方需要再做一次數據轉換。目前代碼是 vue 模塊手工硬編碼轉換的,后面可以把這部分放到 nej ,可以借用其已有的接口的數據轉換函數,對 vue 傳遞的數據進行二次轉換。
另外還有一個問題,如果模塊先進行框架遷移,后進行接口遷移,此時就面臨兩個方案。一個是使用 transform 函數對數據進行轉換,另一種,推到重寫。此時肯定更傾向于第一種方案,那么就需要對 transform 函數進行設計,讓其更方便使用。這里簡單設計了一種。
const source = { standard: { bandwidth: 1, ipChargeType: 2
}, instanceId: 6,
};
const rules = { standard: "NewStandard", instanceId: "InstanceId", "standard.bandwidth": ["InternetMaxBandwidth", "BizParam.InternetMaxBandwidth"], "standard.ipChargeType": "BizParam.NetworkChargeType",
};
const out = { NewStandard: { bandwidth: 1, ipChargeType: 2
}, InstanceId: 6, BizParam: { InternetMaxBandwidth: 1, NetworkChargeType: 2, }, InternetMaxBandwidth: 1,
};
it(transform(source, rules) is correct, () => { assert.deepEqual(transform(source, rules), Object.assign({}, source, out));
});
it(transform(source, rules, true) is correct, () => { assert.deepEqual(transform(out, rules, true), Object.assign({}, out, source));
});
靜態資源
這里主要是 js 文件內引用的靜態資源,該靜態資源的路徑需要用此語法進行設置
default: { logo: require(@/assets/images/logos/logo.png), // @是項目根路徑},
這樣這個靜態文件就可以享受到 webpack 的處理,算出正確的路徑,不然有可能出現顯示不出來的情況。 另外靜態資源不推薦寫相對路徑。eg: ../../../assets/images/logos/logo.png
接口
nej + regular 在代碼里寫了接口,在 vue 需要再次找到接口,重新寫一遍,基本不可復用。但如果之前放置接口的地方稍顯混亂,那么在找接口的時候,就需要一個個業務邏輯的看過去。試想可不可以
將接口按照模塊放置在一起,稱為 api 層,同時再劃分出來 service 層。api 層通過 json 來描述一個接口的方方面面, service 層是從 api 層生成出來的,外加上對接口進行二次處理和對多個接口拼接。
然后就很好的抽象出一個獨立的 api 層,和業務邏輯無關,僅和后端文檔輸出有關,同時 service 層又很好的保持一定的業務相關性。那么在換框架的時候,api 直接拿走即可,service 層僅需要稍許改動。另有文章介紹具體的實現。
這里可能會有接口數據緩存以及一定時間內只發一次請求的需求,那么想一下,需要在該層實現嗎?還是需要更高的一層對數據處理的抽象,而不受限于僅對接口數據?又或者對 service 層的定義進行擴充,包含對數據處理的抽象?
網易云計算基礎服務深度整合了 IaaS、PaaS 及容器技術,提供彈性計算、DevOps 工具鏈及微服務基礎設施等服務,幫助企業解決 IT、架構及運維等問題,使企業更聚焦于業務,是新一代的云計算平臺,點擊可免費試用。
文章來源: 網易云社區
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/25269.html
摘要:本文將介紹網易云基礎服務蜂巢實例遷移功能的實現,并探討如何高效完成實例遷移任務。網易云基礎服務蜂巢提供了負載監控閾值選項,在業務負載超過該閾值時,會暫停遷移操作,直到負載重新低于閾值。 歡迎訪問網易云社區,了解更多網易技術產品運營經驗。 我們把數據庫里部分或全部 Schema和數據遷移到另一個實例的行為稱為實例遷移,將導出數據的實例稱為源實例,導入數據的實例稱為目標實例。 根據遷移數...
摘要:最后,張曉龍透露未來網易云會在以下三個方面繼續深耕研發高性能容器,跟進開源社區最新版本并適配,加大參與社區力度并反饋社區。文章來源網易云社區 歡迎訪問網易云社區,了解更多網易技術產品運營經驗。 10 月 15 日,聚焦 Kubernetes 中國行業應用與技術落地的首屆中國 Kubernetes 用戶大會(KEUC)在杭州成功舉辦。本次大會吸引了來自全球各地的技術精英齊聚一堂,共同探...
摘要:作為為應用平臺即時通訊技術開發和部署提供便捷途徑的云平臺,網易云聚焦穩定性和易用性,通過強化私有云定制化的服務來滿足不同用戶的需求,而這正是網易云作為即時通訊領域的老兵的競爭優勢所在。9月22日,2017中國移動互聯網大會暨中國首席技術官大會在杭州舉行。眾多業界技術領袖參與了此次盛會,就互聯網時代的IT技術研發挑戰和應對策略進行了探討。網易技術委員會資深專家委員徐杭生在會議上發表演講,認為移...
摘要:在互聯網的第三個時代,也就是下一個互聯網十年里,云計算將成為這場大戰的制勝關鍵所在。就在前不久,亞馬遜旗下的云計算服務平臺宣布正式在中國商用。的殺入對于中國的云計算市場也是一大不可忽視的力量。 互聯網的第一個時代我們定義為PC互聯網,互聯網的第二個時代我們定義為移動互聯網,而互聯網的第三個時代我們則定義為萬物聯網。當前國內的互聯網正處于第二個時代向第三個時代過渡期,而云計算則是支撐起萬物聯...
摘要:網易云信即時通訊云服務的產品優勢網易云信涉足多元行業提升用戶體驗滿足內外協同今年月,網易云信正式宣布通信與視頻業務實現戰略升級?! №n寒導演的影片《乘風破浪》中,有這樣一個片段,在六一還堅持囤BB機有前途的時候,從事電腦編程和軟件開發的小馬則認為即時通訊才是未來的發展方向??吹竭@里,很多觀影者都不謀而合地笑了,小馬口中的即時通訊就是在1999年誕生的OICQ。影片中阿浪說的那句話沒錯,這個世...
閱讀 1778·2021-11-15 11:37
閱讀 3048·2021-11-04 16:05
閱讀 1918·2021-10-27 14:18
閱讀 2749·2021-08-12 13:30
閱讀 2494·2019-08-29 14:18
閱讀 2081·2019-08-29 13:07
閱讀 2018·2019-08-27 10:54
閱讀 2719·2019-08-26 12:15