vue3 字符串模板解析

1,820 阅读1分钟

vue3 compile模板字符串解析

最近有需要动态展示form表单的情况,为了偷懒用编辑器动态修改,但是储存的内容确是静态的字符串,比对了2的写法,发现vue3中解析略有不同,自己记录下

compile

compile 返回一个 compileToFunctions

compileToFunctions 只返回Javascript代码,经过编译返回的render函数如下

(function anonymous(Vue
  ) {
  const _Vue = Vue
  const {  } = _Vue
  
  const _hoisted_1 = ["onUpdate:modelValue"]
  
  return function render(_ctx, _cache) {
    with (_ctx) {
      const { vModelText: _vModelText, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
  
      return _withDirectives((_openBlock(), _createElementBlock("input", {
        "onUpdate:modelValue": $event => ((this.formData.a1) = $event)
      }, null, 8 /* PROPS */, _hoisted_1)), [
        [_vModelText, this.formData.a1]
      ])
    }
  }
  })

注意点:

  • 在 template SFC模式下 v-model:value="formData.xxx",在解析的函数体内,默认没有对应的对象 formData,会有大量警告

通过调用 rdx.call(this, this) ,字符模板加上this.formData.xxx

  • 字符模板动态修改在setup中固定,不要到render中修改,否则双向绑定的数据会不断触发render
<template>
    <tableInput ref="questionRef" v-model:value="dataInfo" :template="temple" v-if="temple"></tableInput>
</template>
<script>
import { toRefs, reactive } from 'vue'
import tableInput from './tableInput.jsx'
export default {
  name: 'Login',
  components: { tableInput },
  setup (props, context) {
    let _data = reactive({
      questionRef: null,
      dataInfo: {},
      temple: `<table border="0" width="100%" cellpadding="0" cellspacing="0"><tbody><tr><th>项目</th><th>分数</th><th>项目</th><th>分数</th></tr><tr><td>是否</td><td>a1</td><td>是否8</td><td>a8</td></tr><tr><td>是否2</td><td>a2</td><td>是否9</td><td>a9</td></tr><tr><td>是否3</td><td>a3</td><td>是否10</td><td>a10</td></tr><tr><td>是否4</td><td>a4</td><td>是否11</td><td>a11</td></tr><tr><td>是否5</td><td>a5</td><td>是否12</td><td>a12</td></tr><tr><td>是否6</td><td>a6</td><td></td><td></td></tr><tr><td>是否7</td><td>a7</td><td></td><td></td></tr></tbody></table>`
    })
    for (let i = 1; i <= 12; i++) {
      _data.dataInfo['a' + i] = ''
    }
    return {
      ...toRefs(_data),
    }
  },
}
</script>
  • tableInput.jsx
import { watch, reactive, defineComponent, ref, compile, toRefs, getCurrentInstance } from "vue";
const App = defineComponent({
  props: {
    value: {
      type: Object,
      default () {
        return {}
      }
    },
    template: {
      type: String
    }
  },
  setup (props,context) {
    let _data = reactive({
      formData: JSON.parse(JSON.stringify(props.value)),
      tempInfo: ''
    })
    let info = props.template
    for (const key in _data.formData) {
      info = info.replace(key, `<a-input v-model:value="this.formData.${key}" />`)
    }
    _data.tempInfo = info
    // info = `<div><input type="text"  v-model="formData.test"  />{{formData}}</div> `
    let _methods = {
      getInfo () {
        return _data.formData
      }
    }
    watch(()=>_data.formData,(value)=>{
      context.emit('update:value',_data.formData)
    },{deep:true})
    return {
      ...toRefs(_data),
      ..._methods
    }
  },
  render () {
    let rdx = compile(this.tempInfo)
    return rdx.call(this, this)
  }
});
export default App