Vue-loader 的巧妙玩法

聲明:可以這么玩腐宋,不代表應(yīng)該這么玩

一掏缎、Vue-loader 干了啥?

Vue-loader 是一個(gè) webpack 加載器,它可以把形如:

<template>
    ...
</template>
<script>
    ...
</script>
<style>
    ...
</style>

的 Vue 組件轉(zhuǎn)化為 Js 模塊狗热,這其中最最值得關(guān)注的是,它生成了 render function code巍杈,在前之前的一篇文章 詳解 render function code 中,已經(jīng)對(duì)它進(jìn)行了細(xì)致的介紹:

render function code 是從模板編譯而來(lái)(可以并且應(yīng)該預(yù)編譯)的組件核心渲染方法扛伍,在每一次組件的 Render 過(guò)程中筷畦,通過(guò)注入的數(shù)據(jù)執(zhí)行可生成虛擬 Dom

既然 Vue-loader 預(yù)編譯生成了 render function code,那么我們就可以通過(guò)改造 Vue-loader 來(lái)改寫(xiě) render function code 的生成結(jié)果刺洒,從而全局的影響組件的每一次渲染結(jié)果

二鳖宾、如何改造?

找到目標(biāo)代碼

Vue loader 并不普通逆航,需要通過(guò)語(yǔ)法樹(shù)分析的方式最終生成 render function code (并且不限于此)鼎文,如果通篇閱讀如此復(fù)雜的代碼可能會(huì)讓你——沮喪。幸運(yùn)的是因俐,要完成改寫(xiě) render function code 的小目標(biāo)拇惋,我們并不需要讀得太多,找到生成結(jié)果那一小段代碼女揭,在返回之前改寫(xiě)即可蚤假。那么新的問(wèn)題又來(lái)了栏饮,這小段代碼又如何定位吧兔?

一是靠猜:打開(kāi) Vue-loader 的目錄結(jié)構(gòu),見(jiàn)名知義袍嬉,顯然 template-compiler 模板編譯的意思更為接近

二是搜索:在 template-compiler 目錄中搜索 render , 有沒(méi)有發(fā)現(xiàn)有一段代碼很有嫌疑

var code
  if (compiled.errors && compiled.errors.length) {
    this.emitError(
      `\n  Error compiling template:\n${pad(html)}\n` +
      compiled.errors.map(e => `  - ${e}`).join('\n') + '\n'
    )
    code = 'module.exports={render:function(){},staticRenderFns:[]}'
  } else {
    var bubleOptions = options.buble
    // 這段代碼太可疑了
    code = transpile('module.exports={' +
      'render:' + toFunction(compiled.render) + ',' +
      'staticRenderFns: [' + compiled.staticRenderFns.map(toFunction).join(',') + ']' +
    '}', bubleOptions)

    // mark with stripped (this enables Vue to use correct runtime proxy detection)
    if (!isProduction && (
      !bubleOptions ||
      !bubleOptions.transforms ||
      bubleOptions.transforms.stripWith !== false
    )) {
      code += `\nmodule.exports.render._withStripped = true`
    }
  }

三是調(diào)試確認(rèn):打印 toFunction(compiled.render), 查看輸出結(jié)果境蔼,如果跟 render function code 的表現(xiàn)一致的話灶平,那就是它了

with(this) {
    return _c('div', {
        attrs: {
            "id": "app"
        },
        staticStyle: {
          "width": "100px"
        },
        style: styleObj
    },
    [_c('p', [_v("普通屬性:" + _s(message))]), _v(" "), _c('p', [_v(_s(msg()))]), _v(" "), _c('p', [_v(_s(ct))]), _v(" "), _c('input', {
        directives: [{
            name: "model",
            rawName: "v-model",
            value: (message),
            expression: "message"
        }],
        domProps: {
            "value": (message)
        },
        on: {
            "input": function($event) {
                if ($event.target.composing) return;
                message = $event.target.value
            }
        }
    }), _v(" "), _l((items),
    function(item) {
        return _c('div', [_v("\n\t\t  " + _s(item.text) + "\n\t   ")])
    }), _v(" "), _c('button', {
        on: {
            "click": bindClick
        }
    },
    [_v("點(diǎn)我出奇跡抓同偉")])], 2)
}

如何改造?

假如我們想把所有組件的所有靜態(tài)樣式(staticStyle)的像素值乘二(雖然有點(diǎn)搞破壞)箍土,那么我們需要對(duì)上述 toFunction(compiled.render) 進(jìn)行正則替換

var renderCode = toFunction(compiled.render)
renderCode = renderCode.replace(/(staticStyle:)(\s*{)([^}]*)(})/g, function (m, n1, n2, n3, n4) {
    n3 = n3.replace(/(".*")(\s*:\s*")(\d+px)(")/g, function (m, n31, n32, n33, n34) {
      return n31 + n32 + parseInt(n33)*2 + 'px' + n34
    })
    return n1 + n2 + n3 + n4
  })

如果是改造動(dòng)態(tài)樣式(style)逢享,由于在 render function code 中,動(dòng)態(tài) style 以變量的形式出現(xiàn)吴藻,我們不能直接替換模板瞒爬,那么我們可以通過(guò)傳入一個(gè)方法,在運(yùn)行時(shí)執(zhí)行轉(zhuǎn)換沟堡。不要企圖寫(xiě)一個(gè)普通的方法侧但,通過(guò)方法名的引用在 render function code 中執(zhí)行,因?yàn)?render function code 執(zhí)行時(shí)的作用域航罗,不是在 Vue-loader 階段可以確認(rèn)的禀横,所以我們需要寫(xiě)一個(gè)立即執(zhí)行函數(shù):

var convertCode = "(function(styleObj){styleObj = (...此處省略N行代碼)})(##style##)"

立即執(zhí)行函數(shù)的入?yún)⒂?##style## 占位,替換的過(guò)程中用具體的變量代替粥血,上述 render function code 替換結(jié)果為:

with(this) {
    return _c('div', {
        attrs: {
            "id": "app"
        },
        staticStyle: {
          "width": "100px"
        },
        // 重點(diǎn)在這里 在這里
        style: (function(styleObj){styleObj = (...此處省略N行代碼)})(styleObj)
    },
    ...
    )
}

三柏锄、有何使用場(chǎng)景?

例如复亏,當(dāng)你一開(kāi)始使用了 px 作為布局單位趾娃,卻需要改造為 rem 布局單位的時(shí)候(業(yè)務(wù)代碼很多很繁雜,不方便一個(gè)個(gè)去改缔御,并且由于動(dòng)態(tài)樣式的存在茫舶,難以全局替換)

對(duì)于插入立即執(zhí)行函數(shù)去處理動(dòng)態(tài)變量的方式,每一次 Re-render 都會(huì)執(zhí)行一遍轉(zhuǎn)換函數(shù)刹淌,顯然饶氏,這對(duì)渲染性能有影響

所以,雖然可以這么玩有勾,但是不代表應(yīng)該這么玩疹启,還需三思而行

其它的使用場(chǎng)景暫時(shí)也還沒(méi)想到,即便如此蔼卡,這種瞎折騰也不是沒(méi)有意義的喊崖,沒(méi)準(zhǔn)在還無(wú)法預(yù)見(jiàn)的場(chǎng)景,這是一種絕佳的解決方案呢雇逞?如果你剛好遇到了荤懂,也同步給我吧~~

掘金開(kāi)設(shè)了專(zhuān)欄,歡迎捧場(chǎng)塘砸,點(diǎn)此去~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末节仿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掉蔬,更是在濱河造成了極大的恐慌廊宪,老刑警劉巖矾瘾,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異箭启,居然都是意外死亡壕翩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)傅寡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)放妈,“玉大人,你說(shuō)我怎么就攤上這事荐操〈竺停” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵淀零,是天一觀的道長(zhǎng)挽绩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)驾中,這世上最難降的妖魔是什么唉堪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮肩民,結(jié)果婚禮上唠亚,老公的妹妹穿的比我還像新娘。我一直安慰自己持痰,他們只是感情好灶搜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著工窍,像睡著了一般割卖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上患雏,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天鹏溯,我揣著相機(jī)與錄音,去河邊找鬼淹仑。 笑死丙挽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匀借。 我是一名探鬼主播颜阐,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吓肋!你這毒婦竟也來(lái)了凳怨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蓬坡,失蹤者是張志新(化名)和其女友劉穎猿棉,沒(méi)想到半個(gè)月后磅叛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體屑咳,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萨赁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兆龙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杖爽。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖紫皇,靈堂內(nèi)的尸體忽然破棺而出慰安,到底是詐尸還是另有隱情,我是刑警寧澤聪铺,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布化焕,位于F島的核電站,受9級(jí)特大地震影響铃剔,放射性物質(zhì)發(fā)生泄漏撒桨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一键兜、第九天 我趴在偏房一處隱蔽的房頂上張望凤类。 院中可真熱鬧,春花似錦普气、人聲如沸谜疤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)夷磕。三九已至,卻和暖如春仔沿,著一層夾襖步出監(jiān)牢的瞬間企锌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工于未, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撕攒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓烘浦,卻偏偏與公主長(zhǎng)得像抖坪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闷叉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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

  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容擦俐,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,050評(píng)論 0 29
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,178評(píng)論 25 707
  • 時(shí) 間:2017年4月16日 13:30—17:30 地 點(diǎn):南京市玄武區(qū)丹鳳街恒基公寓B區(qū)2棟2單元501 游戲...
    是的陛下閱讀 265評(píng)論 0 0
  • “依般若波羅蜜多故握侧,心無(wú)掛礙蚯瞧『倨冢”前面已經(jīng)講過(guò),般若波羅蜜多就是到達(dá)彼岸的大智慧埋合,彼岸在哪备徐?不在外,在你的內(nèi)心甚颂。我們...
    蓮連閱讀 290評(píng)論 0 0
  • Art Deco演變自十九世紀(jì)末的Art Nouveau(新藝術(shù))運(yùn)動(dòng)蜜猾,當(dāng)時(shí)的Art Nouveau是資產(chǎn)階...
    柯恩閱讀 475評(píng)論 0 2