后端自動(dòng)構(gòu)建前端css和js

service.js

引子:

別的復(fù)雜前端開(kāi)發(fā)技術(shù)不會(huì)浆熔,用得多的還是手寫(xiě)代碼吴攒,手動(dòng)處理。

3年前手寫(xiě)合并壓縮js和css文件的asp腳本代碼目前還能正常運(yùn)行孤页,也就沒(méi)有多大使用別的技術(shù)的動(dòng)力。

直到近期被一個(gè)問(wèn)題糾結(jié)著涩馆,今天花了一天時(shí)間摸索完成了一個(gè)后端構(gòu)建功能行施。

目前接觸到的項(xiàng)目前端開(kāi)發(fā)模式基本上是一樣的

  1. 項(xiàng)目不會(huì)很大
  2. 通用css、js魂那、第三方庫(kù)會(huì)獨(dú)立成單個(gè)文件
  3. 頁(yè)面依賴(lài)的多個(gè)文件會(huì)合并成一個(gè)css或js文件蛾号,并進(jìn)行自定義語(yǔ)法編譯和壓縮混淆
  4. 多個(gè)頁(yè)面會(huì)用到的但不夠通用的會(huì)獨(dú)立成一個(gè)html文件,內(nèi)嵌style涯雅、script標(biāo)簽鲜结,服務(wù)器端后端運(yùn)行時(shí)包含進(jìn)來(lái)
  5. 頁(yè)面使用的js、css完全內(nèi)嵌style斩芭、script標(biāo)簽(無(wú)法抗拒的優(yōu)點(diǎn):打開(kāi)一個(gè)文件就能看到所有源代碼)
  6. 曉得這種模式很low轻腺,不過(guò)好用

了解到的前端構(gòu)建方式

針對(duì)2、3項(xiàng)划乖,跟目前看到的前端自動(dòng)化構(gòu)建工具贬养,比如:webpack、gulp琴庵,其實(shí)處理思想上還是比較吻合误算,壓縮混淆實(shí)現(xiàn)是調(diào)用的一個(gè)壓縮js和css網(wǎng)站的接口tool.css-js.com/compressor.html

針對(duì)4迷殿、5項(xiàng)這種內(nèi)嵌的style和script儿礼,以前是沒(méi)有任何處理,一個(gè)月前特意加上了壓縮混淆功能庆寺。實(shí)現(xiàn)方式是開(kāi)發(fā)階段完成相關(guān)文件的壓縮混淆:壓縮混淆是只要頁(yè)面一運(yùn)行蚊夫,相關(guān)前端代碼后端會(huì)自動(dòng)化處理;然后手動(dòng)上傳結(jié)果到服務(wù)器懦尝。如果源碼有改動(dòng)知纷,必須重新壓縮混淆和上傳文件(自己寫(xiě)的后臺(tái)工具是這樣的規(guī)則)。

最近就是被如果源碼有改動(dòng)陵霉,必須重新壓縮混淆和上傳文件這個(gè)問(wèn)題困擾著琅轧,一個(gè)是操作復(fù)雜,致命的是:如果漏上傳了混淆后的文件踊挠。乍桂。。

好,現(xiàn)在知道目前采用的壓縮混淆方式了:編寫(xiě)前端代碼睹酌,后端代碼自動(dòng)處理生成新的壓縮混淆后的代碼权谁。

前奏

既然后端已經(jīng)有壓縮混淆功能了,那么還要這么復(fù)雜的操作流程就多余了忍疾。為什么不扔到服務(wù)器上自動(dòng)處理闯传?是考慮到第三方網(wǎng)站接口的穩(wěn)定性,如果他們掛了卤妒,至少不影響線上功能(他們確實(shí)經(jīng)常掛)。

問(wèn)題出在第三方壓縮混淆接口上字币,那把功能本地化不就穩(wěn)定了则披,沒(méi)毛病是這個(gè)邏輯。

昨天想了一下洗出,如果要本地實(shí)現(xiàn)壓縮混淆css和js士复,涉及的代碼有點(diǎn)多,就放棄了翩活。

今天上午又想了一下阱洪,如果不提前搞好,光生成出來(lái)的文件就一大堆菠镇,而且本質(zhì)是次要的生成文件冗荸,提交svn的時(shí)候還要特殊對(duì)待,不提交又會(huì)導(dǎo)致新檢出項(xiàng)目可能丟失文件利耍,提交的話(huà)又算是垃圾文件蚌本。還要上傳,如果是差異更新就有的對(duì)比了隘梨。必須動(dòng)手了程癌。

進(jìn)行時(shí)

要壓縮css,有很多辦法:正則替換空白轴猎,不過(guò)復(fù)雜css格式去掉空白后語(yǔ)義是不是一致就難說(shuō)了嵌莉;用現(xiàn)成的工具,甚至可以帶來(lái)更好的開(kāi)發(fā)方式的less捻脖、sass锐峭,最終采用了less,看別人說(shuō)的sass比less更好郎仆,不過(guò)要額外學(xué)和安裝Ruby起點(diǎn)比less高了許多只祠;最終采用less方案。

壓縮js扰肌,uglifyjs不二選擇抛寝。

安裝node模塊

cnpm install -g ing...

目前最新版本less 3.0.1壓縮css遇到問(wèn)題:The compress option has been deprecated -x新版本選項(xiàng)不能用了,得知裝上less-plugin-clean-css--clean-css命令可以進(jìn)行壓縮。
好吧裝上less-plugin-clean-css 1.5.1盗舰,Unable to load plugin clean-css please make sure that it is installed識(shí)別不到么晶府,到less-plugin-clean-css看到一個(gè)issusehttps://github.com/less/less-plugin-clean-css/issues/24說(shuō)是要降級(jí)到2.x。
cnpm show less versions 查詢(xún)到less 2.x最高版本2.7.3钻趋,裝上壓縮成功川陆。

uglifyjs 3.3.16最新版,裝上就能壓縮混淆蛮位,研究了一下參數(shù)较沪,提供-m -c兩個(gè)參數(shù)就足夠了,-m混淆變量名失仁,-c壓縮優(yōu)化代碼尸曼。

后端代碼接入和運(yùn)行測(cè)試

后端接入方式和控制臺(tái)敲命令行是一樣的,通過(guò)cmd調(diào)用less 或 uglifyjs進(jìn)行處理文件代碼萄焦,接入進(jìn)來(lái)很快的控轿。

測(cè)試?yán)玻簑in7環(huán)境

測(cè)試打開(kāi)新頁(yè)面速度:
比以前慢5、6倍

測(cè)試壓縮一個(gè)23k js:
調(diào)用tool.css-js.com/compressor.html接口大概要270毫秒
后臺(tái)代碼壓縮需要580毫秒

測(cè)試后臺(tái)代碼執(zhí)行uglifyjs -V:需要350多毫秒
測(cè)試后臺(tái)代碼執(zhí)行echo 123:需要0毫秒

測(cè)試分析:
實(shí)際壓縮混淆速度580-350=230毫秒拂封,和第三方接口出入不大茬射。
cmd調(diào)用速度0毫秒也可以忽略。
那就是啟動(dòng)uglifyjs而外占用了350毫秒冒签,猜了一下在抛,每次啟動(dòng)壓縮混淆都會(huì)重新啟動(dòng)node,然后初始化uglifyjs镣衡,所以導(dǎo)致巨慢霜定,大文件還好,小文件就不得了廊鸥,調(diào)用第三方接口40毫秒搞定的時(shí)望浩,后臺(tái)代碼至少要400毫秒起步。從而導(dǎo)致了頁(yè)面打開(kāi)速度變慢很多倍惰说。
基本上頁(yè)面上的script和style都是小的片段磨德,這個(gè)速度不能接受。

優(yōu)化

雖然已經(jīng)能夠?qū)崿F(xiàn)壓縮混淆了吆视,但是壓縮必須經(jīng)過(guò)啟動(dòng)速度巨慢這個(gè)問(wèn)題不能接受典挑。

既然是node和模塊啟動(dòng)的問(wèn)題導(dǎo)致的,那么將所有的壓縮混淆共用一次啟動(dòng)過(guò)程不就解決了啦吧?是這樣的您觉。

怎么共用?用cmd肯定復(fù)雜了授滓,直接上node琳水,啟動(dòng)和初始化所有模塊后等著命令的輸入肆糕,然后進(jìn)行壓縮混淆,然后繼續(xù)接受命令在孝,往復(fù)循環(huán)诚啃。

如此方式用http server最合適不過(guò)了,啟動(dòng)和初始化所有模塊私沮,等待http請(qǐng)求始赎,一有請(qǐng)求就馬上處理并返回結(jié)果。

上code:

package.json里面的模塊依賴(lài):

{
  "name": "node_server",
  "version": "1.0.0",
  "main": "server.js",
  "dependencies": {
    "uglify-js": "3.3.16",
    "less": "2.7.3",
    "less-plugin-clean-css": "1.5.1"
  }
}

server.js node程序啟動(dòng)代碼仔燕,特意只監(jiān)聽(tīng)127.0.0.1省的配置防火墻

var http = require('http');
var querystring = require('querystring');

var UglifyJS = require("uglify-js");
var less = require('less');
var cleanCss = require("less-plugin-clean-css");

http.createServer(function (req, rep) {
    var ctx={c:0,m:"",v:""};
    
    var post = '';
    req.on('data', function(chunk){
        post += chunk;
    });
    req.on('end', function(){
        (async function(){
            try{
                var params = querystring.parse(post);
            
                var path0=(/^\/([^\/]+)/.exec(req.url)||[])[1]||"";
                if(path0=="test"){
                    ctx.v="test";
                }else if(path0=="buildcss"){
                    ctx.v=await BuildCss(params);
                }else if(path0=="buildjs"){
                    ctx.v=await BuildJs(params);
                };
            }catch(e){
                ctx.c=1;
                ctx.m="執(zhí)行出錯(cuò):"+e.stack;
            };
            try{
                var sendData=JSON.stringify(ctx);
            }catch(e){
                ctx={c:1,m:"返回?cái)?shù)據(jù)失斣於狻:"+e.stack};
                sendData=JSON.stringify(ctx);
            }
            
            rep.writeHead(200, {'Content-Type': 'text/json; charset=utf-8'});
            rep.end(sendData);
        })();
    });
}).listen(8004,"127.0.0.1");







function BuildCss(params){
    return new Promise(function (resolve, reject) {
        less.render(params.code||"", { plugins: [new cleanCss({advanced: true})] })
        .then(function(output) {
            resolve(output.css);
        },function(e) {
            reject(new Error(e.message));
        });
    });
};
function BuildJs(params){
    return new Promise(function (resolve, reject) {
        var res=UglifyJS.minify(params.code||"");
        if(res.error){
            reject(res.error);
        }else{
            resolve(res.code);
        };
    });
};

提供接口地址:
http://127.0.0.1:8004/test 程序運(yùn)行狀態(tài)測(cè)試
http://127.0.0.1:8004/buildcss post code=css代碼 編譯css
http://127.0.0.1:8004/buildjs post code=js代碼 編譯js

接口返回結(jié)果
{c:0,m"錯(cuò)誤消息",v:"沒(méi)有錯(cuò)誤時(shí)返回的結(jié)果"},c為狀態(tài)碼涨享,如果出錯(cuò)c!=0筋搏,并提供錯(cuò)誤消息,沒(méi)有錯(cuò)誤時(shí)v有值厕隧。

如何運(yùn)行

沒(méi)有怎么用node和npm,經(jīng)驗(yàn)不足俄周,不過(guò)還是能跑起來(lái):

  1. 安裝node
  2. 安裝cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org npm太慢了
  3. 文件夾內(nèi)新建package.json吁讨,把代碼copy進(jìn)去
  4. 文件夾內(nèi)新建server.js,把代碼copy進(jìn)去
  5. 文件夾這個(gè)目錄上下文內(nèi)執(zhí)行cmd命令cnpm install峦朗,自動(dòng)下載依賴(lài)模塊
  6. npm start 啟動(dòng)
  7. 瀏覽器內(nèi)輸入http://127.0.0.1:8004/test 測(cè)試運(yùn)行狀態(tài)
已經(jīng)install過(guò)的,啟動(dòng)正常
服務(wù)已經(jīng)在運(yùn)行

最終

再次測(cè)試壓縮一個(gè)23k js:
調(diào)用tool.css-js.com/compressor.html接口大概要270毫秒
后臺(tái)代碼壓縮需要140毫秒

比第三方接口還要快,估計(jì)是新版本的原因吧厅克,(不要看網(wǎng)速影響笆檀,最多影響10ms就不錯(cuò)了)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尺铣,隨后出現(xiàn)的幾起案子拴曲,更是在濱河造成了極大的恐慌,老刑警劉巖凛忿,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澈灼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡店溢,警方通過(guò)查閱死者的電腦和手機(jī)叁熔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)床牧,“玉大人荣回,你說(shuō)我怎么就攤上這事「昕龋” “怎么了心软?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵壕吹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我糯累,道長(zhǎng)算利,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任泳姐,我火速辦了婚禮效拭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胖秒。我一直安慰自己缎患,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布阎肝。 她就那樣靜靜地躺著挤渔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪风题。 梳的紋絲不亂的頭發(fā)上判导,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音沛硅,去河邊找鬼眼刃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛摇肌,可吹牛的內(nèi)容都是我干的擂红。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼围小,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼昵骤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肯适,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤变秦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后疹娶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伴栓,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年雨饺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钳垮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡额港,死狀恐怖饺窿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情移斩,我是刑警寧澤肚医,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布绢馍,位于F島的核電站,受9級(jí)特大地震影響肠套,放射性物質(zhì)發(fā)生泄漏舰涌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一你稚、第九天 我趴在偏房一處隱蔽的房頂上張望瓷耙。 院中可真熱鬧,春花似錦刁赖、人聲如沸搁痛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸡典。三九已至,卻和暖如春枪芒,著一層夾襖步出監(jiān)牢的瞬間彻况,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工舅踪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疗垛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓硫朦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親背镇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咬展,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容