Vue-loader 的巧妙玩法
栏目: JavaScript · 发布时间: 7年前
内容简介:Vue-loader 的巧妙玩法
声明:可以这么玩,不代表应该这么玩
一、Vue-loader 干了啥?
Vue-loader 是一个 webpack 加载器,它可以把形如:
<template> ... </template> <script> ... </script> <style> ... </style>
的 Vue 组件转化为 Js 模块,这其中最最值得关注的是,它生成了 render function code
,在前之前的一篇文章 详解 render function code 中,已经对它进行了细致的介绍:
render function code
是从模板编译而来(可以并且应该预编译)的组件核心渲染方法,在每一次组件的 Render 过程中,通过注入的数据执行可生成虚拟 Dom
既然 Vue-loader 预编译生成了 render function code
,那么我们就可以通过改造 Vue-loader 来改写 render function code
的生成结果,从而全局的影响组件的每一次渲染结果
二、如何改造?
找到目标代码
Vue loader 并不普通,需要通过语法树分析的方式最终生成 render function code
(并且不限于此),如果通篇阅读如此复杂的代码可能会让你——沮丧。幸运的是,要完成改写 render function code
的小目标,我们并不需要读得太多,找到生成结果那一小段代码,在返回之前改写即可。那么新的问题又来了,这小段代码又如何定位?
一是靠猜:打开 Vue-loader 的目录结构,见名知义,显然 template-compiler
模板编译的意思更为接近
二是搜索:在 template-compiler
目录中搜索 render
, 有没有发现有一段代码很有嫌疑
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` } }
三是调试确认:打印 toFunction(compiled.render)
, 查看输出结果,如果跟 render function code
的表现一致的话,那就是它了
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("点我出奇迹抓同伟")])], 2) }
如何改造?
假如我们想把所有组件的所有静态样式(staticStyle)的像素值乘二(虽然有点搞破坏),那么我们需要对上述 toFunction(compiled.render)
进行正则替换
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 })
如果是改造动态样式(style),由于在 render function code
中,动态 style 以变量的形式出现,我们不能直接替换模板,那么我们可以通过传入一个方法,在运行时执行转换。不要企图写一个普通的方法,通过方法名的引用在 render function code
中执行,因为 render function code
执行时的作用域,不是在 Vue-loader
阶段可以确认的,所以我们需要写一个立即执行函数:
var convertCode = "(function(styleObj){styleObj = (...此处省略N行代码)})(##style##)"
立即执行函数的入参用 ##style##
占位,替换的过程中用具体的变量代替,上述 render function code
替换结果为:
with(this) { return _c('div', { attrs: { "id": "app" }, staticStyle: { "width": "100px" }, // 重点在这里 在这里 style: (function(styleObj){styleObj = (...此处省略N行代码)})(styleObj) }, ... ) }
三、有何使用场景?
例如,当你一开始使用了 px 作为布局单位,却需要改造为 rem 布局单位的时候(业务代码很多很繁杂,不方便一个个去改,并且由于动态样式的存在,难以全局替换)
对于插入立即执行函数去处理动态变量的方式,每一次 Re-render
都会执行一遍转换函数,显然,这对渲染性能有影响
所以,虽然可以这么玩,但是不代表应该这么玩,还需三思而行
其它的使用场景暂时也还没想到,即便如此,这种瞎折腾也不是没有意义的,没准在还无法预见的场景,这是一种绝佳的解决方案呢?如果你刚好遇到了,也同步给我吧~~
更多精彩,期待您的关注
以上所述就是小编给大家介绍的《Vue-loader 的巧妙玩法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- jsonp劫持入门玩法
- SkyWalking之高级玩法
- 【译】2019 JavaScript 新玩法
- PHP异步的的玩法
- Github 新玩法 -- Profile ReadMe
- Hexo 博客终极玩法:云端写作,自动部署
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从Python开始学编程
Vamei / 电子工业出版社 / 2016-11-24 / CNY 49.00
改编自Vamei博客的《Python快速教程》。本书以Python为样本,不仅介绍了编程的基本概念,还着重讲解编程语言的主流范式:面向过程、面向对象、面向函数。读者不仅可以轻松学会Python,以后再学习其他编程语言时也会更加容易。一起来看看 《从Python开始学编程》 这本书的介绍吧!