我们知道Vue的模板提供了一个简洁风格的DSL来描述响应式界面,而JSX却提供了更强的灵活性和抽象能力

    在项目开发中有的时候会遇到一种需求,我们需要在同一个单文件组件中同时使用 template 和 jsx的能力,Vue框架本身好像并不允许这么干

    通过简单的变通好像可以实现这个特性

    jsx.vue

    1. <script>
    2. export default {
    3. props: {
    4. jsx: {
    5. type: Function
    6. }
    7. },
    8. render (createElement, context) {
    9. return this.jsx(createElement, context)
    10. },
    11. name: 'jsx'
    12. }
    13. </script>

    test.vue

    1. <template>
    2. <div>
    3. <div>111</div>
    4. <jsx :jsx="renderFn"></jsx>
    5. <div>222</div>
    6. </div>
    7. </template>
    8. <script>
    9. import jsx from '../../components/utils/jsx'
    10. export default {
    11. name: 'test',
    12. components: {
    13. jsx
    14. },
    15. data () {
    16. return {
    17. count: 0
    18. }
    19. },
    20. methods: {
    21. onAdd () {
    22. this.count++
    23. },
    24. renderFn (h, context) {
    25. return <div>
    26. <div>
    27. <button onClick={this.onAdd}>add</button>
    28. </div>
    29. <div class="num">{this.count}</div>
    30. </div>
    31. }
    32. }
    33. }
    34. </script>
    35. <style scoped>
    36. .num{
    37. color: red;
    38. }
    39. </style>

    原理也很简单,通过创建一个子组件 jsx.vue ,子组件使用render渲染,而子组件的render函数实际上是通过调用父组件传入的一个渲染函数,从而实现了好像在父组件可以混合template和jsx的能力,不过很快会发现一个问题,父组件如果定义了 scoped样式 ,将无定义渲染函数内部的样式
    原因就是,虽然渲染函数是写在父组件的,但是vue却会认为这一部内容属于子组件(事实上好像也是如此)

    所以我不得不对子组件的实现进行了hack,最后修改的组件如下:

    1. <script>
    2. import { keys, find } from 'ramda'
    3. import { it } from 'partial-application.macro'
    4. export default {
    5. props: {
    6. jsx: {
    7. type: Function
    8. }
    9. },
    10. render (h, context) {
    11. let rt = this.jsx(h, context)
    12. this.$nextTick(() => {
    13. let ds = rt?.parent?.elm?.dataset
    14. let vid = ds |> keys |> find(it.indexOf('v-') == 0)
    15. if (!vid) {
    16. return
    17. }
    18. function r (n) {
    19. let chs = n.children
    20. if (!chs) {
    21. return
    22. }
    23. for (let c of chs) {
    24. let ds = c?.elm?.dataset
    25. if (ds) {
    26. ds[vid] = ''
    27. }
    28. r(c)
    29. }
    30. }
    31. r(rt)
    32. })
    33. return rt
    34. },
    35. name: 'jsx'
    36. }
    37. </script>