聲明:可以這么玩腐宋,不代表應(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)此去~~