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