Vue-loader 的巧妙玩法

栏目: JavaScript · 发布时间: 8年前

内容简介: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 模板编译的意思更为接近

Vue-loader 的巧妙玩法

二是搜索:在 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 的巧妙玩法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Convergence Culture

Convergence Culture

Henry Jenkins / NYU Press / 2006-08-01 / USD 30.00

"Convergence Culture" maps a new territory: where old and new media intersect, where grassroots and corporate media collide, where the power of the media producer, and the power of the consumer intera......一起来看看 《Convergence Culture》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码