postmessage 游戏窗口内无效_前端的微前端在交通项目内的应用实践
項(xiàng)目背景
業(yè)務(wù)的快速發(fā)展,越來越多的接入渠道(百度、快應(yīng)用等等),人員增加,開發(fā)成本與管理成本都上升,效率反而越來越低,團(tuán)隊(duì)的人員重復(fù)造輪子,毫無挑戰(zhàn),當(dāng)然市面上也有多端解決方案,但是不太適用目前的業(yè)務(wù),所以前端聚合成“微前端”的概念。
單體應(yīng)用與「微前端」架構(gòu)
在傳統(tǒng)的軟件開發(fā)當(dāng)中,大多數(shù)軟件都是單體式應(yīng)用架構(gòu)的。為了適應(yīng)我們這個(gè)時(shí)代的不確定性。快速試驗(yàn),快速失敗。更快地推出新產(chǎn)品和有效地改進(jìn)當(dāng)前產(chǎn)品,從而為客戶提供有意義的數(shù)字體驗(yàn)。
而單體應(yīng)用這種軟件架構(gòu)對(duì)于企業(yè)來說的致命缺點(diǎn)就是,對(duì)于市場(chǎng)的響應(yīng)速度變慢。由于依賴關(guān)系,其響應(yīng)周期往往會(huì)變得非常漫長(zhǎng)。此時(shí)前端工程化越來越復(fù)雜,每次的整站編譯耗時(shí)嚴(yán)重,根據(jù)用戶漏斗原則,有一部分無效的消耗。
「微前端」的好處
安全高效:服務(wù)分離,降低整體風(fēng)險(xiǎn)與測(cè)試等成本;
靈活擴(kuò)展:為每一個(gè)服務(wù)選擇最合適的技術(shù)和基礎(chǔ)架構(gòu);
獨(dú)立敏捷:每一個(gè)服務(wù)可以獨(dú)立開發(fā),測(cè)試和部署。
「微前端」的四種可選實(shí)踐方案
使用后端模板引擎插入 HTML (方案A)
方案會(huì)增加后端復(fù)雜度,并且又將前端的渲染控制權(quán)交回了后端服務(wù)器。作為一個(gè)前端開發(fā)人員,一般不會(huì)選擇該方案,但并不是完全沒用,需要看使用場(chǎng)景,如果只是純展示的,可以適當(dāng)考慮。
客戶端 JavaScript 異步加載 (方案B)
這種方式可以通過前端模塊化方式在開發(fā)整個(gè)「微前端」方案(AMD,CMD等),對(duì)開發(fā)效率有一定影響。
WebComponents 整合所有功能模塊 (方案C)
使用了較新的技術(shù)方案,在較老的瀏覽器上,可能兼容性欠缺。我們需要在整個(gè) Web 應(yīng)用程序上做出改變,把它們?nèi)哭D(zhuǎn)換成 Web Components。
使用 iframe 隔離運(yùn)行 (方案D)
優(yōu)點(diǎn)
最強(qiáng)大的是隔離了組件和應(yīng)用程序部分的運(yùn)行時(shí)環(huán)境,因?yàn)槊總€(gè)模塊都可以獨(dú)立開發(fā),并且可以與其他部分的技術(shù)無關(guān);
因?yàn)槊總€(gè)模塊都可以獨(dú)立開發(fā),我們可以使用我們最熟悉的框架開發(fā)「微前端」模塊;
可以各自使用完全不同的前端框架,可以在 React 中開發(fā)一部分,在 Vue 中開發(fā)一部分,然后使用原生 JavaScript 開發(fā)其他部分或任何其他技術(shù);
消息傳遞也就相當(dāng)強(qiáng)大。(Window.postMessageAPI)。
缺點(diǎn)
Bundle的大小非常明顯,因?yàn)閼?yīng)用程序是分開的,所以在構(gòu)建時(shí)也不能提取公共依賴關(guān)系;
考慮到瀏覽器性能問題,盡量避免iframe的多層調(diào)用;
處理移動(dòng)端中的iframe時(shí),將變得相當(dāng)痛苦。
「微前端」的技術(shù)選型
需要接入的「微前端」的應(yīng)用
「微前端」開發(fā)要求
準(zhǔn):快速找出當(dāng)前應(yīng)用的痛點(diǎn),給出「微前端」解決方案;
快:快速迭代開發(fā)上線;
穩(wěn):保證線上代碼兼容性以及穩(wěn)定運(yùn)行。
「微前端」的演變過程
前端單體應(yīng)用痛點(diǎn)
每個(gè)站點(diǎn)中,都有一個(gè)通用的模塊《常旅》;
這么多站點(diǎn)也不是一次性完成,每次需要開發(fā)新的站點(diǎn),都通過Copy的方式,將《常旅》復(fù)制一份;
當(dāng)站點(diǎn)積累到一定程度時(shí),突然《常旅》有個(gè)新的需求需要開發(fā);
此時(shí),我們的開發(fā)量就成倍增加(1 x N),同時(shí)測(cè)試的工作量也一樣。
單體應(yīng)用到的「微前端」進(jìn)化
「微前端」優(yōu)劣
前端單體應(yīng)用中的常旅模塊分離,增加通用的「微前端」服務(wù)模組;
常旅模塊獨(dú)立為一個(gè)單獨(dú)的「微前端」應(yīng)用,為每個(gè)站點(diǎn)提供”常旅模塊“服務(wù);
此時(shí),如果需要做「常旅功能迭代」,工作量永遠(yuǎn)只需要一份就可以;
我們需要保證「微前端」穩(wěn)定運(yùn)行,一旦奔潰,將影響所有引入的單體應(yīng)用。
「微前端」架構(gòu)方案
主框架:vue?+?vue-router?+?vuex
通訊方案:url入?yún)?+?postMessage?+?vue-unicom(內(nèi)部廣播機(jī)制)
滾屏:iscroll
布局方案:px2rem?(750)布局
承載方式:iframe?+?webview
靜態(tài)資源加載優(yōu)化:asset-cache-webpack-plugin(h5離線緩存)
宿主(iframe)優(yōu)化方案?「微前端」預(yù)加載
「微前端」數(shù)據(jù)通訊方案
「微前端」數(shù)據(jù)通訊方案
「微前端」部分?jǐn)?shù)據(jù)通訊代碼
// routerimport router from '../../router' // 獲得一個(gè)唯一的值import getSole from 'rimjs/sole'// 全局unicom事件觸發(fā)import { unicomEmit } from 'rimjs/vueUnicom' // 當(dāng)前環(huán)境的變量數(shù)據(jù),這里主要是設(shè)置身份import { env } from '../env'let win = window// 默認(rèn)為 iframe 需要兼容其他的,可以這里兼容function postMessage(data, source = window.parent) {source.postMessage(data, '*')}// 臨時(shí)存放 Promise 的resolve的對(duì)象let postMessageResolveFns = {} // 信息輸入數(shù)據(jù)let messageInData = null// 信息輸出數(shù)據(jù) 暫存let messageOutData = {}let query = router.queryif (query._in != null) {messageInData = {}}if (messageInData) {// 是否為類微信小程序內(nèi)(webview和小程序無法實(shí)時(shí)雙向通訊)// 傳入的參數(shù)需要一次性輸入try {Object.assign(messageInData, JSON.parse(query._in) || {})} catch (e) {}}// 用戶同步用戶身份信息的函數(shù)function setUser(inEnv) {if (!inEnv) {return}env.userId = inEnv.userId || ''}// 從宿主獲取用戶身份async function getUser() {let inEnv = await bridge.postMessage('env:get')setUser(inEnv)}// 接收消息// type:類型 fnKey:回調(diào)方法 insruct:指令 data:數(shù)據(jù)function onMessage({ type, insruct, data } = {}, source) {let backData = nullif (type == 'unicom') {
backData = data == null ? {} : data// 事件廣播, 通過這個(gè)方法,宿主環(huán)境可以向「微前端」端發(fā)送廣播消息unicomEmit(insruct, backData)return}if (type == 'system') {// 一些系統(tǒng)設(shè)置if (insruct == 'font') {// rem布局,根節(jié)點(diǎn) 字體大小// 「微前端」如果為預(yù)加載,無法正確計(jì)算出根節(jié)點(diǎn)字體大小,需要宿主通知document.documentElement.style.fontSize = datareturn}if (insruct == 'href') {setUser(data.env)// iframe 預(yù)加載,重新定位 當(dāng)前路由if (data.replace) {window.location.replace(window.location.pathname + '#' + data.href)} else {window.location.href = window.location.pathname + '#' + data.href}return}if (insruct == 're_init') {// 重新初始化頁面,一般在iframe中,返回宿主環(huán)境后使用window.location.replace(window.location.pathname + '#/init?re=1')return}return}if (type == 'back') {// 找到回調(diào)的Promiselet resolveFn = postMessageResolveFns[insruct]if (resolveFn) {// 運(yùn)行 resolveFnresolveFn(data)// console.log("data", data)delete postMessageResolveFns[insruct]}return}if (fnKey) {// 數(shù)據(jù)回調(diào)postMessage({ type: 'back', from: 'microservice', insruct: fnKey, data: backData }, source)}}// 注冊(cè)接收的消息// 如果需要兼容其他渠道,可以這里寫不同的window.addEventListener('message', function (ev) {let opt = ev.data || {}let { from } = optif (from != 'microservice') {// 非目標(biāo),舍棄return}onMessage(opt, ev.source)})// 對(duì)象定義export let bridge = {// postMessage 發(fā)送消息// 可以通過 Promise 獲取到宿主的回調(diào)函數(shù)的值// bridge.postMessage("xxx") 同 bridge.postMessage("unicom:xxx")// data 為發(fā)送的參數(shù)postMessage(opt, data) {if (typeof opt == 'string') {let x = opt.match(/^([^:]*):*(.*)$/)if (!x) {
x = [opt]}if (!x[2]) {// type 默認(rèn)為 unicom
x[2] = x[1]
x[1] = 'unicom'}
opt = {
data,type: x[1],insruct: x[2]}}return new Promise(function (resolve) {// 回調(diào)唯一的keylet fnKey = 'ms:' + getSole()
postMessageResolveFns[fnKey] = resolveif (messageInData) {// 此處無法直接使用 postMessagelet key = opt.type + ':' + opt.insruct// 寄存需要發(fā)送出去的數(shù)據(jù)
messageOutData[key] = data// console.log(messageOutData, key, args)setTimeout(function () {// 模擬接收到 back 事件onMessage({type: 'back',insruct: fnKey,data: messageInData[key] || null})}, 0)return}// 發(fā)送下次postMessage(Object.assign({ from: 'microservice', fnKey }, opt))})},// 結(jié)束「微前端」endBack(opt, data) {if (opt) {// 如果在「微前端」中有對(duì)身份信息做一些修改,需要同步到宿主this.postMessage(opt, data)}// 退出「微前端」界面// 不同載體,需要不同的方法// 比如 微信小程序,就需要 win.wx.miniProgram.navigateBack()if (messageInData) {// 此處微信小程序內(nèi)嵌webview處理// 微信返回win.wx.miniProgram.navigateBack()// 微信發(fā)送寄存的數(shù)據(jù)win.wx.miniProgram.postMessage({ data: messageOutData })return}// 歷史記錄后退window.history.back()}}// 初始化時(shí),嘗試獲取用戶身份信息getUser()
滾屏為什么要使用iscroll?
問題 iframe引入時(shí),body上的滾屏失效。
解決方案: 引入 iscrollbar5.2 實(shí)現(xiàn)自定義滾動(dòng)條。
.cp-layout {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 2;
overflow: hidden;
.iScrollVerticalScrollbar {
position: absolute;
z-index: 9999;
width: 3px;
bottom: 2px;
top: 2px;
right: 4px;
overflow: hidden;
pointer-events: none;
}
.iScrollIndicator {
box-sizing: border-box;
position: absolute;
border-radius: 3px;
width: 100%;
transition-duration: 0ms;
transform: translate(0px, 0px);
transition-timing-function: cubic-bezier(0.1, 0.57, 0.1, 1);
background-color: rgba(0, 0, 0, 0.2);
}
}
class="cp-layout">
通訊方案使用了postMessage為什么還要使用url入?yún)?#xff1f;
問題:小程序webview對(duì)postMessage受限。解決方案: 使用url傳參一次性傳入所有參數(shù)。完成后,通過postMessage將數(shù)據(jù)一次性全部發(fā)送到宿主。
布局中,為什么要使用px2rem?
問題:iframe引入時(shí),viewport 設(shè)置布局模式無效。解決方案: 引入 px2rem ,用rem來做整體布局。
「微前端」優(yōu)化
如何減少「微前端」加載白屏?xí)r間?
通過骨架屏,減少白屏?xí)r間;
通過引入?asset-cache-webpack-plugin?插件,為資源加載做離線緩存(減少或消除白屏?xí)r間);
iframe時(shí),使用單列模式開發(fā),避免同時(shí)初始化多個(gè)「微前端」,造成性能損耗;
iframe加載微服務(wù)時(shí),對(duì)「微前端」預(yù)加載。當(dāng)需要使用時(shí),可以快速展現(xiàn)。
引入?px2rem?和?asset-cache-webpack-plugin
vue.config.js
const AssetCachePlugin = require('asset-cache-webpack-plugin')module.exports = {devServer: {port: 9001},// 樣式配置css: {// css不單獨(dú)一個(gè)文件編譯extract: false,loaderOptions: {postcss: {plugins: [require('postcss-plugin-px2rem')({rootValue: 100, //換算基數(shù), 默認(rèn)100 ,這樣的話把根標(biāo)簽的字體規(guī)定為1rem為50px,這樣就可以從設(shè)計(jì)稿上量出多少個(gè)px直接在代碼中寫多上px了。exclude: /(node_module)/,mediaQuery: false, //(布爾值)允許在媒體查詢中轉(zhuǎn)換px。minPixelValue: 0 //設(shè)置要替換的最小像素值(3px會(huì)被轉(zhuǎn)rem)。默認(rèn) 0
}) ]
conf.plugin('asset-cache').use(AssetCachePlugin, [
總結(jié)與思考:「微前端」的優(yōu)缺點(diǎn)
優(yōu)點(diǎn) ?
敏捷性 - 獨(dú)立開發(fā)和更快的部署周期;
快捷測(cè)試 - 每一個(gè)小的變化不必再觸碰整個(gè)應(yīng)用程序的回歸測(cè)試;
有助于持續(xù)集成、持續(xù)部署以及持續(xù)交付;
維護(hù)簡(jiǎn)單,每個(gè)團(tuán)隊(duì)都熟悉所維護(hù)特定的區(qū)域。
缺點(diǎn) ?
復(fù)雜的集成,「微前端」需要面對(duì)的多種環(huán)境,需要做多種兼容性驗(yàn)證;
第三方模塊重疊,依賴冗余增加了管理的復(fù)雜性。在團(tuán)隊(duì)之間共享公共資源的機(jī)制;
避免影響最終用戶的體驗(yàn),「微前端」初始化可能會(huì)增加不必要的等待時(shí)間。
使用場(chǎng)景
在單體應(yīng)用中那些重復(fù)試用的模塊可以抽取為「微前端」;
這些模塊也較穩(wěn)定,代碼迭代相對(duì)較少;
在單體應(yīng)用中的那些主流程,不適用于「微前端」。
總結(jié)
以上是生活随笔為你收集整理的postmessage 游戏窗口内无效_前端的微前端在交通项目内的应用实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ 学习杂谈:sizeof和size
- 下一篇: 书写README的各种markdown语