头图

📰 前言

众所周知,在 vue3v-if 总是优先于 v-for 生效。

然而,在某些情况下,我们可能更希望 v-for 的优先级更高,
虽然 vue3 并没有提供直接修改指令优先级的方法,但是我们可以使用 AST(抽象语法树) 转换来实现这一点。

🌈 在线演示


📄 vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { transformForIf } from './transformForIf'

export default defineConfig({
  plugins: [
    vue({
      template: { compilerOptions: { nodeTransforms: [transformForIf] } }
    })
  ]
})

📄 transformForIf.ts

import { remove } from '@vue/shared'
import { NodeTransform, findDir, findProp } from '@vue/compiler-core'

const ELEMENT = 1

// 创建 template 节点
export const createTemplateNode = (props: BaseElementNode['props'], children: BaseElementNode['children'], loc: BaseElementNode['loc']): TemplateNode => ({
  type: ELEMENT,
  tagType: TEMPLATE,
  tag: 'template',
  props: props.filter(e => e),
  children,
  codegenNode: undefined,
  ns: 0,
  isSelfClosing: false,
  loc
})

export const transformForIf: NodeTransform = node => {
  if (node.type != ELEMENT) return

  node.children.forEach((child, i) => {
    if (child.type != ELEMENT) return

    const VFor = findDir(child, 'for')
    if (!VFor) return

    const VIf = findDir(child, 'if')
    if (!VIf) return

    const key = findProp(child, 'key')

    remove(child.props, VFor)
    remove(child.props, VIf)
    remove(child.props, key)

    const templateIf = createTemplateNode([VIf], [child], VIf.loc)
    const templateFor = createTemplateNode([VFor, key], [templateIf], VFor.loc)

    node.children[i] = templateFor
  })
}

🔺 以上就是完整代码了


🎃 让我们来试试效果

<template>
  <div>
    <span v-for="i in 10" v-if="i % 2">{{ i }}</span>
  </div>
</template>

🚀 运行成功 😆

🚀 在线 StackBlitz 编辑


👍 点个赞吧 ✨ 👈


灬都是个谜
38 声望4 粉丝