webpack搭建服務(wù)端項(xiàng)目

webpack搭建服務(wù)端項(xiàng)目

參考文章: https://juejin.im/post/5cb1aabdf265da037b6101d3

前言

最近有如下一些零零碎碎的小需求,總結(jié)起來(lái),都是頁(yè)面偏向于展示,與用戶交互較少跛锌。因此,選擇搭建了一個(gè)多頁(yè)面的服務(wù)端項(xiàng)目栓霜。

  1. 低版本app用戶展示的升級(jí)頁(yè)面淤年,
  2. 以及其他需要作為app中一些用來(lái)幫忙實(shí)現(xiàn)部分功能的中間頁(yè)面

整個(gè)項(xiàng)目采用了webpack4 + express + ejs實(shí)現(xiàn)。

代碼地址:https://github.com/chestnut647/serverSideRendering

webpack構(gòu)建流程

由于是第一次脫離框架腳手架直接寫(xiě)webpack配置葵擎,花費(fèi)了些時(shí)間谅阿。整個(gè)思路如下:

  1. 構(gòu)思好項(xiàng)目的結(jié)構(gòu)
  2. 先簡(jiǎn)單配置entry 和output配置
  3. 需要支持es6【模塊化】,配置babel-loader
  4. 需要輸出html文件并直接js的自動(dòng)引入酬滤,引入html-loader loader以及html-webpack-plugin插件
  5. 不支持ejs語(yǔ)法中include語(yǔ)法签餐,使用ejs-html-loader
  6. 項(xiàng)目沒(méi)有熱更新,引入webpack-dev-middleware和webpack-hot-middleware

開(kāi)始構(gòu)建

介紹構(gòu)建過(guò)程中的一些重點(diǎn)盯串,項(xiàng)目初始架構(gòu)如下所示:

image

build內(nèi)文件如下氯檐,config.json用于存放一些webpack使用的常量配置

image

配置js入口

js入口配置,在配置entry的時(shí)候体捏,由于項(xiàng)目后續(xù)可能還會(huì)增加新的需求冠摄,不能每增加一次需求頁(yè)面,就要在webpack中新增entry几缭,因此采用了glob.sync來(lái)讀取js文件夾下的所有js文件河泳。

同時(shí)在使用html-webpack-plugin時(shí)我們也是使用glob.sync來(lái)避免每次都需要新增的問(wèn)題。

    entry: (function(fileLists) {
        let entryObj = {};
        const basePath = resolve(__dirname, "../source/public/javascripts/main/");
        fileLists.map((filePath) => {
            const temp  = filePath.split(basePath),
                  filename = temp[temp.length - 1].split('/').join('_').slice(1).split('.')[0];
            entryObj[filename] = filePath;
        });
        return entryObj;
    })(glob.sync(resolve(__dirname, "../source/public/javascripts/main/_*/_.js"))),
    output: {
        path: resolve(__dirname, `../${CONFIG.DIRC.DIST}`),
        filename: `${CONFIG.DIRC.SCRIPT}/[name].bundle.js`
    }

配置熱更新

使用webpack-dev-middleware配合webpack-hot-middleware來(lái)實(shí)現(xiàn)奏司。

  1. webpack-dev-middleware 用以實(shí)現(xiàn)文件變動(dòng)自動(dòng)編譯
  2. webpack-hot-middleware實(shí)現(xiàn)模塊熱替換

webpack-dev-middleware

首先我們判斷當(dāng)前是否為開(kāi)發(fā)環(huán)境乔询,非開(kāi)發(fā)環(huán)境的時(shí)候直接使用express方式啟動(dòng)項(xiàng)目。

當(dāng)為開(kāi)發(fā)環(huán)境時(shí)韵洋,我們采用webpack-dev-middleware【參數(shù)webpack.config.js生成的compile】生成的eppress中間件竿刁,訪問(wèn)的頁(yè)面的時(shí)候就會(huì)經(jīng)過(guò)webpack-dev-middleware,根據(jù)webpack.config.js里的配置搪缨,當(dāng)js文件變動(dòng)的時(shí)候可以自動(dòng)編譯食拜。

if(isDev) {
    app.use(webpackDevMiddleware(compile, {
        publicPath: webpackDevConfig.output.publicPath
    }))

    app.use(webpackDevConfig.output.publicPath, express.static(path.join(__dirname, 'source')))
} else {
    app.set('view engine', 'ejs');
    app.set('views', path.join(__dirname, 'dist/views'));
    app.use(virtualDirctory, express.static(path.join(__dirname, 'dist')));
}

使用自動(dòng)編譯打包webpack-dev-middleware帶來(lái)了一個(gè)問(wèn)題:直接source目錄下的views文件夾來(lái)直接設(shè)置views是無(wú)法達(dá)到預(yù)期效果的。因?yàn)閟ource下的是沒(méi)有打包的ejs模板副编,并沒(méi)有注入項(xiàng)目中的js负甸、css。因此需要重寫(xiě)render方法

原來(lái)使用方式為:

router.get('/test1', function(req, res, next) {
    res.render('test1', {
        title: 'test1'
    });
})

重寫(xiě)一個(gè)render方法:

當(dāng)在開(kāi)發(fā)環(huán)境下痹届,直接通過(guò)axios去獲取打包出來(lái)的ejs文件內(nèi)容呻待,再通過(guò)ejs.render渲染出來(lái)。

function render(res, filename, data) {
    if(isDev) {
        const localPath = `http://localhost:${packageConfig.config.port}${CONFIG.PATH.PUBLIC_PATH}/${CONFIG.DIRC.VIEW}/${filename}.ejs`;
        axios.get(localPath)
        .then(fileRes => {
            const html = ejs.render(fileRes.data, data);
            res.send(html)
        })
        return;
    }
    res.render(filename, data);
}

webpack-hot-middleware

通過(guò)上述配置队腐,可以發(fā)現(xiàn)蚕捉,當(dāng)對(duì)入口文件中的js或者css進(jìn)行修改后,webpack就會(huì)進(jìn)行自動(dòng)編譯柴淘,但是想要獲取最新代碼迫淹,還是需要手動(dòng)刷新瀏覽器秘通,引入webpack-hot-middleware

app.use(webpackHotMiddleware(compile, {
    publicPath: webpackDevConfig.output.publicPath,
}))

同時(shí)修改webpack文件中entry入口配置

    entry: (function(fileLists) {
        let entryObj = {};
        const basePath = resolve(__dirname, "../source/public/javascripts/main/");
        fileLists.map((filePath) => {
            const temp  = filePath.split(basePath),
                  filename = temp[temp.length - 1].split('/').join('_').slice(1).split('.')[0];
            // entryObj[filename] = filePath; 在開(kāi)發(fā)環(huán)境需要將webpack-hot-middleware添入到入口文件里
            entryObj[filename] = isDev ? ['webpack-hot-middleware/client?noInfo=true&reload=true', filePath] :filePath;
        });
        return entryObj;
    })(glob.sync(resolve(__dirname, "../source/public/javascripts/main/_*/_.js")))

通過(guò)這兩步配置后,修改js文件以及css文件瀏覽器會(huì)自動(dòng)更新敛熬。但是修改ejs模板文件肺稀,瀏覽器還是不能夠自動(dòng)更新。

在ejs模版文件的入口应民,增加如下代碼话原。

  1. 在js中直接require模版文件,當(dāng)ejs修改之后瑞妇,webpack就會(huì)將其視為需要熱更新的一部分
  2. 進(jìn)行模塊熱替換稿静,重新獲取打包后文件的內(nèi)容,替換到當(dāng)前頁(yè)面innerHtml上辕狰。
if(process.env.NODE_ENV === 'development') {
    require('raw-loader!@views/test1.ejs')
}
if(module.hot) {
    module.hot.accept();
    module.hot.dispose(() => {
        const axios = require('axios');
        const href = window.location.href
        axios.get(href).then(res => {
            const template = res.data
            document.body.innerHTML = template
        }).catch(e => {
            console.error(e)
        })
    })
}

當(dāng)入口頁(yè)面非常多的時(shí)候改备,你需要每個(gè)都手動(dòng)添加上述代碼,過(guò)于復(fù)雜蔓倍,寫(xiě)一個(gè)簡(jiǎn)單的loader悬钳,用以給每個(gè)入口j文件增加一段上述函數(shù)。


/**
 * 給項(xiàng)目提供views的實(shí)時(shí)更新
 * 在js文件中增加   enable hot updates of view, [filename] 的注釋即可
 */
module.exports = function (resource) {
    const reg = /enable hot updates of view,[\s]*([^\s]+)/i;
    const matchRes = resource.match(reg);
    if(matchRes) {
        const filename = matchRes[1];
        console.log(`給文件${filename}.js添加ejs熱更新代碼`);
        return resource + `
        if(process.env.NODE_ENV === 'development') {
            require('raw-loader!@views/${filename}.ejs')
        }

        if(module.hot) {
            module.hot.accept();
            module.hot.dispose(() => {
                const axios = require('axios');
                const href = window.location.href
                axios.get(href).then(res => {
                    const template = res.data
                    document.body.innerHTML = template
                }).catch(e => {
                    console.error(e)
                })
            })
        }`
    }
    return resource;
}

同時(shí)修改webpack中的js文件的loader

{
    test: /.js$/,
    use: [
        'babel-loader',
        resolve(__dirname, 'auto-update-ejs-loader') // 添加增加熱更新文件的loader
    ],
    exclude: /node_modules/
}

結(jié)語(yǔ)

除去上述的功能偶翅,代碼的完整配置里在生產(chǎn)環(huán)境下也包含了提取css默勾,splitchunk等功能【鬯可以直接下載代碼運(yùn)行起來(lái)~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末母剥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子形导,更是在濱河造成了極大的恐慌环疼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朵耕,死亡現(xiàn)場(chǎng)離奇詭異炫隶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)阎曹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)伪阶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人处嫌,你說(shuō)我怎么就攤上這事栅贴。” “怎么了熏迹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵筹误,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我癣缅,道長(zhǎng)厨剪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任友存,我火速辦了婚禮祷膳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘屡立。我一直安慰自己直晨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布膨俐。 她就那樣靜靜地躺著勇皇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焚刺。 梳的紋絲不亂的頭發(fā)上敛摘,一...
    開(kāi)封第一講書(shū)人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音乳愉,去河邊找鬼兄淫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蔓姚,可吹牛的內(nèi)容都是我干的捕虽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼坡脐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼泄私!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起备闲,我...
    開(kāi)封第一講書(shū)人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晌端,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后浅役,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體斩松,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年觉既,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惧盹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞪讼,死狀恐怖钧椰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情符欠,我是刑警寧澤嫡霞,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站希柿,受9級(jí)特大地震影響诊沪,放射性物質(zhì)發(fā)生泄漏养筒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一端姚、第九天 我趴在偏房一處隱蔽的房頂上張望晕粪。 院中可真熱鬧,春花似錦渐裸、人聲如沸巫湘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尚氛。三九已至,卻和暖如春洞渤,著一層夾襖步出監(jiān)牢的瞬間阅嘶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工您宪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奈懒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓宪巨,卻偏偏與公主長(zhǎng)得像磷杏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捏卓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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

  • 在現(xiàn)在的前端開(kāi)發(fā)中极祸,前后端分離、模塊化開(kāi)發(fā)怠晴、版本控制遥金、文件合并與壓縮、mock數(shù)據(jù)等等一些原本后端的思想開(kāi)始...
    Charlot閱讀 5,440評(píng)論 1 32
  • 寫(xiě)在開(kāi)頭 先說(shuō)說(shuō)為什么要寫(xiě)這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,289評(píng)論 4 31
  • 構(gòu)建一個(gè)小項(xiàng)目——FlyBird蒜田,學(xué)習(xí)webpack和react稿械。(本文成文于2017/2/25) 從webpac...
    布蕾布蕾閱讀 16,821評(píng)論 31 98
  • 我不是傻 只是懶得去計(jì)較 因?yàn)橛?jì)較多了 快樂(lè)就少了 你的身邊有沒(méi)有人說(shuō)你傻美莫?什么事都為別人著想,受了傷梯捕、吃了虧還傻...
    清淺白芷閱讀 1,964評(píng)論 2 19
  • 很多人都想養(yǎng)成閱讀厢呵、健身等好習(xí)慣,也有很多人想戒掉抽煙傀顾、酗酒等壞習(xí)慣一樣襟铭,但我們經(jīng)常會(huì)半途而廢。為何好習(xí)慣如...
    婧姐2019閱讀 2,903評(píng)論 0 4