手動(dòng)實(shí)現(xiàn)微前端框架的思路

此文是本人學(xué)習(xí)慕課網(wǎng)微前端課程《從零打造微前端框架:實(shí)戰(zhàn)“汽車資訊平臺(tái)”項(xiàng)目》的筆記,主要記錄了自研實(shí)現(xiàn)微前端框架的思路。

  • 一、構(gòu)師思維塑造
    • 1.1.軟件設(shè)計(jì)原則
    • 1.2.架構(gòu)種類
    • 1.3.微前端實(shí)現(xiàn)方式
    • 1.4.概念上架構(gòu)質(zhì)量的衡量
    • 1.5.日常開發(fā)過程中的架構(gòu)質(zhì)量
    • 1.6.架構(gòu)的前期準(zhǔn)備
    • 1.7.技術(shù)債務(wù)的產(chǎn)生
    • 1.8.技術(shù)填補(bǔ)
    • 1.9.預(yù)防系統(tǒng)崩潰
    • 1.10.重構(gòu)流程
  • 二、打造微前端框架
    • 2.1.微前端實(shí)現(xiàn)方式對(duì)比
    • 2.2.確定技術(shù)棧
    • 2.3.分析框架
    • 2.4.梳理好思路,繪制項(xiàng)目架構(gòu)圖
    • 2.5.自動(dòng)啟動(dòng)所有子應(yīng)用的腳本
    • 2.6.構(gòu)建服務(wù)
    • 2.7.子應(yīng)用接入微前端vue2
    • 2.8.主應(yīng)用開發(fā)
    • 2.9.css樣式隔離
    • 2.10.父子通信
    • 2.11.提高加載性能
  • 三、發(fā)布框架與自動(dòng)部署應(yīng)用
    • 3.1.通過npm發(fā)布框架
    • 3.2.應(yīng)用部署-創(chuàng)建自動(dòng)部署平臺(tái)

一、構(gòu)師思維塑造

1.1.軟件設(shè)計(jì)原則

1.單一職責(zé)原則:永遠(yuǎn)不應(yīng)該有多于一個(gè)原因來改變某個(gè)類
2.開放封閉原則:軟件實(shí)體擴(kuò)展應(yīng)該是開放的,但是對(duì)于修改應(yīng)該是封閉的
3.里氏替換原則:父類一定是能夠被子類替換
4.最少知識(shí)原則:只與你最直接的對(duì)象交流(高內(nèi)聚,低耦合,做系統(tǒng)設(shè)計(jì)時(shí),盡量減少依賴關(guān)系)
5.接口隔離原則:一個(gè)類與另一個(gè)類之間的依賴性,應(yīng)該依賴于盡可能小的接口(不要對(duì)外暴露沒有實(shí)際意義的接口,用戶不應(yīng)該依賴它不需要的接口)
6.依賴倒置原則:高層模塊不應(yīng)該依賴底層模塊,它們應(yīng)該依賴于抽象,抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象(應(yīng)該面向接口編程,不應(yīng)該面向?qū)崿F(xiàn)類編程)

滿足上邊六大原則 設(shè)計(jì)出穩(wěn)定的軟件架構(gòu)

1.2.架構(gòu)種類

1.系統(tǒng)級(jí)架構(gòu),微前端可以算是系統(tǒng)級(jí)架構(gòu),也可以算是應(yīng)用級(jí)架構(gòu)
2.應(yīng)用級(jí)架構(gòu):腳手架,模式庫(ui庫),設(shè)計(jì)系統(tǒng)
3.模塊級(jí)架構(gòu):迭代
4.代碼級(jí)架構(gòu):規(guī)范與原則

微前端是一個(gè)系統(tǒng)內(nèi)應(yīng)用間的架構(gòu)方案
在多個(gè)應(yīng)用之間,微前端則是一種系統(tǒng)間等架構(gòu)方案

1.3.微前端實(shí)現(xiàn)方式

單實(shí)例:同一時(shí)刻,只有一個(gè)子應(yīng)用被展示,子應(yīng)用具備一個(gè)完整的應(yīng)用生命周期
多實(shí)例:同一時(shí)刻,可以展示多個(gè)子應(yīng)用,通常基于url的變化來做子應(yīng)用的切換,通常使用web components方案來做子應(yīng)用封裝,子應(yīng)用更像是一個(gè)業(yè)務(wù)組件而不是應(yīng)用。

系統(tǒng)的穩(wěn)定性:當(dāng)一個(gè)實(shí)際的系統(tǒng)處于一個(gè)平衡的狀態(tài)時(shí),如果受到外來作用的影響時(shí),系統(tǒng)經(jīng)過一個(gè)過渡過程仍然能夠回到原來的平衡狀態(tài),我們稱這個(gè)系統(tǒng)就是穩(wěn)定,否則稱系統(tǒng)不穩(wěn)定
1.架構(gòu)設(shè)計(jì)的基石 2.可以更好的實(shí)現(xiàn)自我修復(fù)

系統(tǒng)的健壯性:計(jì)算機(jī)軟件在輸入錯(cuò)誤,磁盤故障,網(wǎng)絡(luò)過載或有意攻擊情況下,能否不死機(jī),不崩潰,就是該軟件健壯性的具體表現(xiàn)
健壯性也可以稱之為魯棒性
健壯性的度量標(biāo)準(zhǔn):
1.一個(gè)軟件可以從錯(cuò)誤的輸入推斷出正確合理的輸入
2.一個(gè)軟件可以正確的運(yùn)行在不同環(huán)境下
3.一個(gè)軟件能夠檢測(cè)自己內(nèi)部的設(shè)計(jì)或者編碼錯(cuò)誤,并得到正確的結(jié)果

1.4.概念上架構(gòu)質(zhì)量的衡量

1.拓展性
2.維護(hù)性
3.可管理
4.高可用(故障修復(fù),容災(zāi),降級(jí),熔斷)

1.5.日常開發(fā)過程中的架構(gòu)質(zhì)量

1.理解難度
2.接入依賴的成本
3.崩潰率和錯(cuò)誤率的指標(biāo)
4.開發(fā)效率
5.錯(cuò)誤上報(bào)和信息收集等功能

1.6.架構(gòu)的前期準(zhǔn)備

架構(gòu)師分類:
系統(tǒng)架構(gòu)師:從系統(tǒng)的維度,負(fù)責(zé)整體系統(tǒng)的架構(gòu)設(shè)計(jì),純技術(shù)架構(gòu)
應(yīng)用架構(gòu)師:偏向業(yè)務(wù)的系統(tǒng),關(guān)注理解業(yè)務(wù)
業(yè)務(wù)架構(gòu)師:從業(yè)務(wù)流程的維度,關(guān)注某一個(gè)行業(yè),所做的事情可能會(huì)脫離具體的代碼開發(fā),偏向于數(shù)據(jù)分析

1.7.技術(shù)債務(wù)的產(chǎn)生

1.開發(fā)過程中因?yàn)闀r(shí)間緊迫而導(dǎo)致的實(shí)現(xiàn)不合理
2.開發(fā)過程中暫時(shí)沒有想到更好的辦法而去妥協(xié)進(jìn)行的實(shí)現(xiàn)
3.架構(gòu)設(shè)計(jì)前期沒有考慮到的細(xì)節(jié)
4.不合理的交互設(shè)計(jì),導(dǎo)致技術(shù)實(shí)現(xiàn)復(fù)雜
5.一些舊的功能在做的時(shí)候并沒有詳細(xì)的文檔,并沒有預(yù)留拓展接口,導(dǎo)致拓展困難,上線后問題劇增

技術(shù)債務(wù)累積過多產(chǎn)生的可能后果:
1.修復(fù)變重構(gòu),嚴(yán)重影響產(chǎn)出,
2.對(duì)舊功能需要不斷進(jìn)行兼容處理,嚴(yán)重影響開發(fā)速度

1.8.技術(shù)填補(bǔ)

1.項(xiàng)目拆分,代碼解耦
2.必須有日志模塊,操作日志,錯(cuò)誤日志,業(yè)務(wù)日志等等
3.技術(shù)培訓(xùn)和傳幫帶能力
4.做好代碼規(guī)范,編寫好技術(shù)文檔
5.不同工程師相互code review
6.用于發(fā)現(xiàn)系統(tǒng)中的技術(shù)債務(wù)

等產(chǎn)品上線后,開發(fā)就沒有那么緊啦,這個(gè)時(shí)間大家可以找個(gè)時(shí)間處理技術(shù)債務(wù),一遍建立感情,一遍品味原來的代碼

1.9.預(yù)防系統(tǒng)崩潰

系統(tǒng)崩潰屬于嚴(yán)重的架構(gòu)設(shè)計(jì)事故
崩潰預(yù)防:
1.抓取用戶行為,獲取用戶操作鏈條
2.解決村存量的技術(shù)債務(wù)問題
3.減少新增問題
4.對(duì)臟數(shù)據(jù)進(jìn)行兜底和檢驗(yàn)
5.單元測(cè)試
6.崩潰預(yù)警
7.自動(dòng)化測(cè)試
8.更廣的灰度觸達(dá)
9.性能優(yōu)化體系

當(dāng)技術(shù)債務(wù)累積到一定的程度時(shí),小軟件可以考慮直接重構(gòu),快刀斬亂麻
而隨便軟件系統(tǒng)越來越復(fù)雜,微前端架構(gòu)則成為架構(gòu)優(yōu)化(微重構(gòu))的解決方案之一

1.10.重構(gòu)流程

1.確定問題點(diǎn),確定重構(gòu)功能和范圍
2.舊架構(gòu)設(shè)計(jì)和邏輯梳理
3.穩(wěn)定性保證
4.性能保證
5.需求過程中的沖突問題

二、初識(shí)微前端

2.1.微前端實(shí)現(xiàn)方式對(duì)比

1.iframe
優(yōu)勢(shì):技術(shù)成熟,支持頁面嵌入,天然支持運(yùn)行沙箱隔離,獨(dú)立運(yùn)行
劣勢(shì):頁面之間的域名可以是不同的;需要設(shè)計(jì)相應(yīng)的應(yīng)用通訊機(jī)制,如何監(jiān)聽傳參格式等內(nèi)容;應(yīng)用加載,渲染,緩存都是交給了瀏覽器處理,可控性低
2.web component
優(yōu)勢(shì):支持自定義元素;支持shadow dom,并可通過關(guān)聯(lián)進(jìn)行控制;支持模板template和插槽slot,引入自定義組件內(nèi)容
劣勢(shì):接入微前端需要重寫當(dāng)前項(xiàng)目;生態(tài)系統(tǒng)不完善,容易出兼容性問題;整體架構(gòu)設(shè)計(jì)復(fù)雜,組件之間通訊也控制繁瑣
3.自研框架
優(yōu)勢(shì):高度定制化,滿足所兼容的一切場(chǎng)景;獨(dú)立的通信機(jī)制和沙箱運(yùn)行環(huán)境,可解決應(yīng)用之間相互影響的問題;支持不同技術(shù)棧子應(yīng)用,可無縫實(shí)現(xiàn)頁面無刷新渲染
劣勢(shì):技術(shù)實(shí)現(xiàn)難度較高;需要設(shè)計(jì)一套定制的通信機(jī)制;首次加載會(huì)出現(xiàn)資源過大的情況

最終實(shí)現(xiàn)選擇-自研框架
1.路由分發(fā)式
2.主應(yīng)用控制路由匹配和子應(yīng)用加載,共享依賴加載
3.子應(yīng)用做功能,并接入主應(yīng)用實(shí)現(xiàn)主子控制和聯(lián)動(dòng)

2.2.確定技術(shù)棧

主應(yīng)用:選定vue3技術(shù)棧
子應(yīng)用:
vue2:實(shí)現(xiàn)新能源應(yīng)用
vue3:首頁,選車
react15:資訊,視頻,視頻詳情
react16:新車,排行,登錄
后端服務(wù)和發(fā)布應(yīng)用
服務(wù)端接口:koa實(shí)現(xiàn)
發(fā)布應(yīng)用:express
使用原生nodejs去手寫完成微前端框架

2.3.分析框架

主應(yīng)用:1.注冊(cè)子應(yīng)用 2.加載和渲染子應(yīng)用 3.路由匹配(activeWhen,rules 由框架判斷) 4.獲取數(shù)據(jù)(公共以來,通過數(shù)據(jù)做鑒權(quán)處理) 5.通信機(jī)制(父子通信,子父的通信)
子應(yīng)用:1.渲染自身功能 2.監(jiān)聽通信(主應(yīng)用傳遞過來的數(shù)據(jù),根據(jù)傳遞過來的數(shù)據(jù)做一些對(duì)應(yīng)的更新操作)
微前端框架:1.實(shí)現(xiàn)子應(yīng)用的注冊(cè) 2.開始內(nèi)容(應(yīng)用加載完成,開啟微前端框架) 3.匹配對(duì)應(yīng)的子應(yīng)用,加載對(duì)應(yīng)子應(yīng)用的內(nèi)容,完成所有依賴項(xiàng)的執(zhí)行 4.將子應(yīng)用渲染在固定的容器內(nèi) 5.公共的事件管理 6.異常的捕獲和報(bào)錯(cuò) 6.全局的狀態(tài)管理 7.沙箱隔離,保證子應(yīng)用運(yùn)行期間不被其他子應(yīng)用干擾 7.通信機(jī)制
服務(wù)端的功能: 1.提供數(shù)據(jù)服務(wù)
發(fā)布平臺(tái):1.主子應(yīng)用的打包和發(fā)布

2.4.梳理好思路,繪制項(xiàng)目架構(gòu)圖

工具推薦:processon


image.png

2.5.自動(dòng)啟動(dòng)所有子應(yīng)用的腳本

在根目錄的package.json配置start命令
4-7的內(nèi)容:在微前端的根目錄下配置一個(gè)腳本去同時(shí)運(yùn)行一個(gè)指令,同時(shí)打開所有的應(yīng)用

2.6.構(gòu)建服務(wù)

構(gòu)建一個(gè)后端服務(wù):
npm install koa-generator -g
koa -V
koa2 service

快速生成一個(gè)koa2項(xiàng)目
bin里的www是啟動(dòng)文件
public 靜態(tài)資源文件

安裝一個(gè)插件幫助修改node的時(shí)候自動(dòng)重啟node服務(wù)
npm install supervisor --save-dev
安裝后start啟動(dòng)服務(wù)的命令中用supervisor 代替node

koa解決跨域問題:
npm install koa2-cors --save-dev
app.js進(jìn)行引入并使用const cors = require('koa2-cors')
將服務(wù)設(shè)置為可跨域的狀態(tài)

get請(qǐng)求中ctx.request.query可以獲取到前端接口路徑攜帶的參數(shù)
post請(qǐng)求 ctx.reuqest.body

2.7.子應(yīng)用接入微前端vue2

首先需要做的:主應(yīng)用獲得子應(yīng)用的生命周期,結(jié)構(gòu),方法,文件等,這樣主才能控制子的渲染和加載

Vue2子應(yīng)用接入微前端
子應(yīng)用vue.config.js配置關(guān)鍵點(diǎn)devServer里的contentBase和headers允許跨域和output

Vue2子應(yīng)用本身創(chuàng)建實(shí)例new Vue(),在微前端框架中,這個(gè)實(shí)例的執(zhí)行與銷毀,都應(yīng)該交給主應(yīng)用去執(zhí)行

子應(yīng)用需要改造的文件 1.vue.config.js 2.main.js

react15接入微前端
修改webpack.config.js里的output和devServer
改造入口文件index.js

webpack正常打包會(huì)將所有代碼打包成一個(gè)立即執(zhí)行函數(shù)

(function(){
})()

配置了output的library之后就會(huì)打包成library的屬性值比如demo

const demo = (function(){})()

就可以通國window.demo拿到當(dāng)前自應(yīng)用的所有內(nèi)容

2.8.主應(yīng)用開發(fā)

中央控制器-主應(yīng)用 main項(xiàng)目,使用的vue3技術(shù)棧
主應(yīng)用在頁面設(shè)置一個(gè)子應(yīng)用的容器

在主應(yīng)用進(jìn)行子應(yīng)用注冊(cè)
設(shè)置一個(gè)navList記錄子應(yīng)用的注冊(cè)信息store文件里的leftNav.js
添加注冊(cè)子應(yīng)用的方法utils文件夾里的startMicroApp.js

路由攔截,監(jiān)聽路由切換,重寫路由切換事件,另外別忘了監(jiān)聽瀏覽器的前進(jìn)/后退按鈕window.onpopstate

微前端框架-獲取首個(gè)子應(yīng)用
進(jìn)入項(xiàng)目,首個(gè)顯示的子應(yīng)用,先驗(yàn)證當(dāng)前子應(yīng)用列表是否為空,不為空的時(shí)候根據(jù)route匹配查找出當(dāng)前的子應(yīng)用,如果子應(yīng)用存在,則跳轉(zhuǎn)到子應(yīng)用對(duì)應(yīng)的url

因?yàn)槁酚傻奶D(zhuǎn)又結(jié)合了手動(dòng)的路由跳轉(zhuǎn),為了避免多次觸發(fā)自定義的路由跳轉(zhuǎn)事件,給當(dāng)前的子應(yīng)用添加上標(biāo)記到window對(duì)象上

window.__CURRENT_SUB_APP__ = app.activeRule;

if(window.__CURRENT_SUB_APP__ === window.location.pathname){
  return
}

通過主應(yīng)用生命周期去對(duì)應(yīng)控制子應(yīng)用的內(nèi)容顯示
beforeLoad開始加載
mounted渲染完成
destoryed卸載完成
傳遞過去的主應(yīng)用的生命周期先在微前端里進(jìn)行保存,然后通過全局的loading來控制子應(yīng)用的內(nèi)容顯示/loading

微前端掛載到主應(yīng)用,執(zhí)行注冊(cè)函數(shù)時(shí),傳入的就是主應(yīng)用的生命周期,遍歷執(zhí)行這個(gè)主應(yīng)用的所有生命周期,微前端框架就可以生成注冊(cè)到主應(yīng)用
微前端的生命周期:如果路由變化時(shí),對(duì)子應(yīng)用進(jìn)行對(duì)應(yīng)的銷毀和加載操作

獲取需要展示的頁面-加載和解析html
在主應(yīng)用生命周期beforeLoad去獲取頁面內(nèi)容
子應(yīng)用顯示頁面本質(zhì)上是通過get請(qǐng)求(network中的doc)去獲取頁面信息,由此我們就可以在微前端框架中通過get請(qǐng)求去獲取到子應(yīng)用對(duì)應(yīng)的頁面,根據(jù)url通過fetch拿到res,然后res.text()拿到頁面內(nèi)容,然后將內(nèi)容賦值給對(duì)應(yīng)的容器就可以顯示子應(yīng)用對(duì)應(yīng)的頁面了,但是直接賦值給容器,容器是沒法解析html中的標(biāo)簽,link和script(src,js代碼)元素,需要專門提取出來這些元素進(jìn)行處理

微前端框架環(huán)境變量設(shè)置
通過sandbox設(shè)置環(huán)境變量,運(yùn)行js文件

隔離運(yùn)行環(huán)境-快照沙箱(記錄當(dāng)前執(zhí)行的子應(yīng)用的變量,當(dāng)子應(yīng)用切換的時(shí)候,將變量置為初始值)
不同子應(yīng)用之間的運(yùn)行環(huán)境應(yīng)該進(jìn)行隔離,防止全局變量的互相污染
for循環(huán)window,new map一個(gè)新對(duì)象作為快照對(duì)象,快照沙箱適合比較老的瀏覽器

更現(xiàn)代化的沙箱方法:

let a = {}
const proxy = new Proxy(a , {
  get(){},
  set(){}
})
console.log(proxy.a) //此處會(huì)觸發(fā)proxy的get
proxy.a=1 //此處會(huì)觸發(fā)proxy的set

利用proxy去代理window,監(jiān)聽window的改變
設(shè)置一個(gè)空對(duì)象defaultValue去儲(chǔ)存子應(yīng)用的變量,當(dāng)window改變時(shí),將改變值以key,value的形式存儲(chǔ)到defaultValue中。當(dāng)需要獲取window屬性值的時(shí)候,也在代理的get中去返回defaultValue對(duì)應(yīng)的值,沙箱銷毀的時(shí)候inactive也只需要將defaultValue置為{}

2.9.css樣式隔離

1.css modules 利用webpack配置打包機(jī)制進(jìn)行css模塊化處理
2.shadow dom 創(chuàng)建個(gè)新的元素進(jìn)行包裹,但語法較新,兼容性較差
3.minicss 一個(gè)webpack插件,將css單獨(dú)打包成文件,然后每個(gè)頁面通過link進(jìn)行引用

2.10.父子通信

比如說動(dòng)態(tài)控制主應(yīng)用的header和footer的顯示與隱藏
1.props
好萊塢原則-不用聯(lián)系我,當(dāng)我需要的時(shí)候會(huì)打電話給你
依賴注入-主應(yīng)用的顯示隱藏,注入到子應(yīng)用內(nèi)部,通過子應(yīng)用內(nèi)部的方法進(jìn)行調(diào)用
2.customEvent 通過new CustomEvent對(duì)象去觸發(fā)和監(jiān)聽事件

子應(yīng)用之間進(jìn)行通信
1.props 子應(yīng)用1跟父應(yīng)用交互,子應(yīng)用2再跟父應(yīng)用交互,拿到子應(yīng)用1傳遞出來的信息
2.customEvent

全局狀態(tài)管理-全局store
利用發(fā)布訂閱模式在微前端

export const creatStore = (initData) => (() => {
  // 利用閉包去保存?zhèn)鲄⒌某跏紨?shù)據(jù)
  let store = initData;
  // 管理所有的訂閱者,依賴
  let observers = [];
  // 獲取store
  const getStore = () => {
    return store;
  }
  // 更新store
  const updateStore = (newValue) => new Promise((res) => {
    if (newValue !== store) {
      // 執(zhí)行保存store的操作
      let oldValue = store;
      // 將store更新
      store = newValue;
      res(store);
      // 通知所有的訂閱者
      observers.forEach(fn => fn(newValue, oldValue));
    }
  })
  // 添加訂閱者
  const subscribeStore = (fn) => {
    observers.push(fn);
  }
  // 把方法return出去
  return { getStore, updateStore, subscribeStore }
})()
//整個(gè)store本質(zhì)是一個(gè)閉包函數(shù)

2.11.提高加載性能

提高加載性能-性能緩存
定義一個(gè)cache對(duì)象,根據(jù)子應(yīng)用的name來做緩存,如果當(dāng)前的html已經(jīng)解析并且加載過,就返回已經(jīng)加載過的內(nèi)容。如果沒有,則走正常加載和解析的流程

提高加載性能-預(yù)加載子應(yīng)用
1.獲取到所有子應(yīng)用列表,不包括當(dāng)前正在顯示的
2.預(yù)加載剩下的所有的子應(yīng)用

三、發(fā)布框架與自動(dòng)部署應(yīng)用

3.1.通過npm發(fā)布框架

1.npm init初始化要發(fā)布的包
2.前置條件:需要有一個(gè)自己的npm賬號(hào);在終端npm login登錄上自己的npm賬號(hào);使用npm whoami查看當(dāng)前登錄的賬號(hào)
3.npm publish 進(jìn)行發(fā)布 npm unpublish 取消發(fā)布
1.0.0
第一位:主版本號(hào) 第二位:次版本號(hào) 第三位:修訂號(hào)
npm version patch 使用命令行修改修訂號(hào)
npm version minor 次版本號(hào)
npm version major 主版本號(hào)

3.2.應(yīng)用部署-創(chuàng)建自動(dòng)部署平臺(tái)

創(chuàng)建一個(gè)自動(dòng)發(fā)布的express項(xiàng)目platform文件夾
新建一個(gè)index. html,點(diǎn)擊按鈕,遍歷所有要發(fā)布的應(yīng)用,然后分別觸發(fā)ajax
npm install express express-generator -g
全局安裝了上面的兩個(gè)應(yīng)用后,在命令行就可以使用express命令了
express -e 生成服務(wù)代碼
文件路徑和koa-generator生成的項(xiàng)目差不多
app.js中設(shè)置允許跨域

app.all('*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});

npm start啟動(dòng)項(xiàng)目,routes中創(chuàng)建一個(gè)/start路由,這是實(shí)現(xiàn)各個(gè)應(yīng)用自動(dòng)打包和發(fā)布的關(guān)鍵路由

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容