Fis3構(gòu)建遷移Webpack之路

Webpack從2015年9月第一個版本橫空初始至今已逾2載泛豪。它的出現(xiàn)寄雀,顛覆了一大批主流構(gòu)建如Ant莱坎、Grunt和Gulp等等玫氢。騰訊NOW直播IVWEB團隊之前一直采用Fis構(gòu)建倔撞,本篇文章主要介紹從Fis遷移到webpack遇到的問題和背后的黑科技讲仰,內(nèi)容包括inline-resource、多頁面構(gòu)建痪蝇、資源壓縮鄙陡、文件hash、文件目錄規(guī)則等等躏啰。

為什么要遷移至webpack?

有兩個層面的原因:

  • 首先webpack的社區(qū)生態(tài)火爆趁矾,插件齊全并且維護更新的很頻繁,遇到了問題给僵,比較容易解決毫捣。
  • webpack里面有happypack多實例構(gòu)建方案、code spliting按需加載文件等方案, 可以有效的進行打包構(gòu)建持續(xù)優(yōu)化, 這些在Fis里面是缺少的帝际。

區(qū)分構(gòu)建的開發(fā)or生產(chǎn)環(huán)境蔓同?

  "scripts": {
    "dev": "cross-env NODE_ENV=dev nodemon --watch webpack.config.js --exec \"webpack-dev-server --config webpack.config.js --env development\" --progress --colors",
    "build": "webpack --config webpack.config.js --env production --progress --colors",
    ...
  },

通過在package.json中注入環(huán)境變量的方式,注入NODE_ENV=dev代表開發(fā)環(huán)境蹲诀,默認(rèn)為生產(chǎn)環(huán)境斑粱。這里使用cross-env的原因是:windows下 在package.json中直接使用 NODE_ENV=dev 不生效,需寫成 set NODE_ENV=dev脯爪,cross-env的寫法兼容各個操作系統(tǒng)则北。

資源內(nèi)聯(lián) (inline-resource)

inline-resource的好處是可以減少css,js等的請求數(shù)蹋宦,同時html加載的時候即可同時加載了這些內(nèi)聯(lián)的css、js等靜態(tài)資源咒锻,可以有效的減少白屏或者頁面閃動的問題冷冗。

這里的內(nèi)聯(lián)分為2種,一種是靜態(tài)的html片段,css,js等惑艇,這些資源一開始就存在項目的某個目錄下蒿辙;另一種是構(gòu)建過程中動態(tài)生成的css,js文件。

對于html的內(nèi)聯(lián)滨巴,寫法如下:

  ${require('raw-loader!../src/assets/inline/meta.html')}

對于js的內(nèi)聯(lián)思灌,需要增加babel-loader將ES6的語法進行轉(zhuǎn)換,避免瀏覽器直接解析導(dǎo)致報錯恭取。寫法如下:

<script>${require('raw-loader!babel-loader!../src/node_modules/@tencent/report-whitelist/lib/index.js')}</script>

說明:不能將html-loader和html-webpack-plugin同時使用泰偿,html-loader會導(dǎo)致默認(rèn)的ejs模板引擎語法解析實效,造成 ${} 和 <% = %>等語法不生效

上面講述了如何內(nèi)聯(lián)靜態(tài)的資源文件蜈垮,那么如何內(nèi)聯(lián)構(gòu)建過程中動態(tài)生成的資源文件呢耗跛?這里需要借助html-webpack-inline-source-plugin來增強html-webpack-plugin的功能。比如:將構(gòu)建過程中生成的css文件inline到html模板里面去攒发。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');

    new HtmlWebpackPlugin({
        inlineSource: isDev ? undefined : '\\.css$',
        template: __dirname + '/template/index.tmpl.html',
        filename: 'activity.html',
        inject: true,
    }),
    new HtmlWebpackInlineSourcePlugin(),
    ...

上面這段代碼调塌,html-webpack-plugin本身并不具備inlineSource的屬性。引入了html-webpack-inline-source-plugin之后惠猿,就可以通過inlineSource屬性來匹配哪些文件需要動態(tài)的內(nèi)聯(lián)進html模板文件中了羔砾。

多頁面構(gòu)建

多頁面構(gòu)建,或者稱為通配(wildcards)構(gòu)建偶妖。即需要構(gòu)建的頁面數(shù)量是不確定的姜凄,可能A業(yè)務(wù)有3張頁面,B業(yè)務(wù)有5張頁面趾访。因此态秧,我們不能把entry寫死了:

entry: {
  activity: './src/pages/activity/init.js',            // 深海尋寶活動首頁
  my-reward: './src/pages/my-reward/init.js',          // 我的獎勵
  exchange: './src/pages/exchange/init.js'             // 線下兌換獎品
},

為什么上面的寫法不可取呢?很明顯:上面的寫法把entry寫死了腹缩,這并不通用屿聋。后面如果產(chǎn)品需求發(fā)生改變,需要新增一張頁面藏鹊,就需要手動修改構(gòu)建腳本。我們需要的entry是:'./src/pages/**/init.js'转锈,它能夠像一些linux的命令盘寡,具備匹配某個規(guī)則的所有結(jié)果的能力。這里的思路是借助glob撮慨,達到動態(tài)entry的目的竿痰。

entry: glob.sync('./src/pages/**/init.js'),

在webpack構(gòu)建中脆粥,一個頁面需要一個與之對應(yīng)的HtmlWebpackPlugin實例,N個頁面需要N個與之對應(yīng)的HtmlWebpackPlugin影涉。此處需要動態(tài)的設(shè)置HtmlWebpackPlugin的實例個數(shù)变隔。

const newEntry = {};

Object.keys(config.entry).map((index) => {
    const entry = config.entry[index];
    const match = entry.match(/\/pages\/(.*)\/init.js/);
    const pageName = match && match[1];

    newEntry[pageName] = entry;

    config.plugins.push(
        new HtmlWebpackPlugin({
            inlineSource: isDev ? undefined: '\\.css$',
            template: __dirname + '/template/index.tmpl.html',
            filename: `${pageName}.html`,
            chunks: [pageName],
            inject: true
        })
    );
});
config.entry = newEntry;

html、css和js壓縮

對于html文件里面的內(nèi)容壓縮可以通過給html-webpack-plugin設(shè)置minify參數(shù)蟹倾,html-webpack-plugin的壓縮配置其實是直接集成了 html-minifier匣缘,因此minify的參數(shù)設(shè)置可以直接參考html-minifier的文檔。

config.plugins.push(
    new HtmlWebpackPlugin({
        inlineSource: isDev ? undefined: '\\.css$',
        template: __dirname + '/template/index.tmpl.html',
        filename: `${pageName}.html`,
        chunks: [pageName],
        inject: true,
        minify: {
            minifyJS: true,           // 僅壓縮內(nèi)聯(lián)在html里面的js
            minifyCSS: true,          // 僅壓縮內(nèi)聯(lián)在html里面的css
            html5: true,              // 以html5的文檔格式解析html的模板文件
            removeComments: false,    // 不刪除Html文件里面的注釋
            collapseWhitespace: true, // 刪除空格
            preserveLineBreaks: false // 刪除換行
        }
    })
);

設(shè)置了上面的minify參數(shù)后鲜棠,看到生成的html文件的內(nèi)容全部在1行上肌厨,需要注意的是:minifyJS和minifyCSS只會壓縮內(nèi)聯(lián)在這個html文件的css和js內(nèi)容,對于單獨的css文件和js文件并不會壓縮豁陆。 那么打包出來的css和js文件如何壓縮呢柑爸?

對于css文件壓縮,直接開啟css-loader的壓縮參數(shù)參數(shù)minimize為true即可:

{
    test: /\.scss$/,
    use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
            {
                loader: "css-loader",
                options: {               // 設(shè)置css-loader的minimize參數(shù)為true
                  minimize: true
                }
            },
            {
                loader: "sass-loader"
            }
        ]
    })
},

css-loader開啟壓縮可能會報錯 Module build failed: BrowserlistError: unkonwn version 61 and _chr盒音,解決辦法:

$ npm i caniuse-db —save    #更新caniuse-db到最新版本

對于js文件的壓縮表鳍,可以通過引入 webpack 內(nèi)置的 UglifyJsPlugin:

const webpack = require('webpack');
plugins: [
    ...
    new webpack.optimize.UglifyJsPlugin(),
    ...
],

文件Hash

每次功能發(fā)布上線,都需要重新構(gòu)建一次源代碼祥诽,生成一個新的文件版本列表进胯。此處文件Hash的方式就是一種版本管理的方式,發(fā)布時替換有變化的版本的文件原押,達到增量更新的效果胁镐。此處Hash策略是:根據(jù)文件內(nèi)容進行hash,取8位诸衔。

JS文件:

output: {
    filename: '[name]_[chunkhash:8].js',     // 進行js腳本hash
    path: path.resolve(__dirname, 'public/'),
    publicPath: isDev ? '/' : cdnUrl + '/',
},

Css文件:

plugins: [
    new CleanWebpackPlugin(['./public']),
    new ExtractTextPlugin('[name]_[contenthash:8].css'),  // css文件hash
    new webpack.optimize.UglifyJsPlugin(),
    ...
]

Img文件:

rules: [
    {
        test: /\.(png|svg|jpg|gif)$/,
        use: {
            loader: 'file-loader',
            options: {
                name: '[name]_[hash:8].[ext]',    // img文件hash
            }
        }
    },
    ...
]    

多終端適配

開發(fā)過程中盯漂,不同分辨率的瀏覽器適配是個讓前端開發(fā)者頭疼的問題。手淘的rem方案完美解決了這個問題笨农,它的核心思想是頁面加載時動態(tài)設(shè)置body的font-size值和rem和px轉(zhuǎn)換的單位就缆。

為了不改變編程習(xí)慣,開發(fā)過程中仍然使用px單位來作為基礎(chǔ)布局長度單位谒亦,以750px寬度的視覺稿作為基準(zhǔn)竭宰,設(shè)置rem和px的轉(zhuǎn)換單位為1rem=75px。那么px2rem如何集成進webpack中呢份招?首先增加css的解析px2rem-loader切揭,然后在html頭部引入lib-flexible文件。

{
    test: /\.scss$/,
    use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
            {
                loader: "css-loader"
            },
            {
                loader: "px2rem-loader", // 增加px2rem-loader锁摔,并且設(shè)置rem單位為75px
                options: {
                    remUnit: 75
                }
            },
            {
                loader: "sass-loader"
            }
        ]
    })
},

其它feature

  • 開發(fā)環(huán)境支持WDS: webpack3.x版本自帶webpack-dev-server廓旬,開發(fā)環(huán)境中開啟WDS。這樣依賴的文件發(fā)生變化后谐腰,會自動增量構(gòu)建并且刷新瀏覽器
  • 支持HMR: webpack.config.js文件內(nèi)容變化后孕豹,會觸發(fā)熱更新邏輯涩盾,此處通過nodemon來守護webpack的構(gòu)建進程,eg:
      "scripts": {
      "dev": "cross-env NODE_ENV=dev nodemon --watch webpack.config.js --exec \"webpack-dev-server --config webpack.config.js --env development\" --progress --colors"
      ...
    },
    

由于篇幅原因励背,關(guān)于webpack的打包優(yōu)化將會用另外一篇文章介紹春霍,敬請期待~

參考文檔

打個廣告

  • 騰訊NOW直播工程化解決方案feflow正式開源啦~ 集項目初始化、開發(fā)構(gòu)建叶眉、代碼規(guī)范址儒、代碼發(fā)布 于一身的工具。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末竟闪,一起剝皮案震驚了整個濱河市离福,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炼蛤,老刑警劉巖妖爷,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異理朋,居然都是意外死亡絮识,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門嗽上,熙熙樓的掌柜王于貴愁眉苦臉地迎上來次舌,“玉大人,你說我怎么就攤上這事兽愤”四睿” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵浅萧,是天一觀的道長逐沙。 經(jīng)常有香客問我,道長洼畅,這世上最難降的妖魔是什么吩案? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮帝簇,結(jié)果婚禮上徘郭,老公的妹妹穿的比我還像新娘。我一直安慰自己丧肴,他們只是感情好残揉,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闪湾,像睡著了一般冲甘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上途样,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天江醇,我揣著相機與錄音,去河邊找鬼何暇。 笑死陶夜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的裆站。 我是一名探鬼主播条辟,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宏胯!你這毒婦竟也來了羽嫡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤肩袍,失蹤者是張志新(化名)和其女友劉穎杭棵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氛赐,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡魂爪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了艰管。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滓侍。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牲芋,靈堂內(nèi)的尸體忽然破棺而出撩笆,到底是詐尸還是另有隱情,我是刑警寧澤缸浦,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布夕冲,位于F島的核電站,受9級特大地震影響餐济,放射性物質(zhì)發(fā)生泄漏耘擂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一絮姆、第九天 我趴在偏房一處隱蔽的房頂上張望醉冤。 院中可真熱鬧,春花似錦篙悯、人聲如沸蚁阳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽螺捐。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間定血,已是汗流浹背赔癌。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留澜沟,地道東北人灾票。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像茫虽,于是被迫代替她去往敵國和親刊苍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

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

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺濒析,特此分享以備自己日后查看正什,也希望更多的人看到...
    小小字符閱讀 8,164評論 7 35
  • GitChat技術(shù)雜談 前言 本文較長,為了節(jié)省你的閱讀時間号杏,在文前列寫作思路如下: 什么是 webpack婴氮,它要...
    蕭玄辭閱讀 12,691評論 7 110
  • 版權(quán)聲明:本文為博主原創(chuàng)文章绰上,未經(jīng)博主允許不得轉(zhuǎn)載旨怠。 webpack介紹和使用 一、webpack介紹 1蜈块、由來 ...
    it筱竹閱讀 11,118評論 0 21
  • 在現(xiàn)在的前端開發(fā)中鉴腻,前后端分離、模塊化開發(fā)百揭、版本控制爽哎、文件合并與壓縮、mock數(shù)據(jù)等等一些原本后端的思想開始...
    Charlot閱讀 5,439評論 1 32
  • 電話亭器一,是爺爺留下的课锌。 (一)安家 總聽爺爺說起,我們的老家在山西祈秕,逃荒的時候來的青島渺贤,這里魚蝦蟹多,只要不惜力请毛,...
    鄰家安心閱讀 511評論 2 5