feat: support casting plain element to component via is="vue:xxx"
In Vue 3's custom elements interop, we no longer process `is` usage on known native elements as component casting. (ref: https://v3.vuejs.org/guide/migration/custom-elements-interop.html) This introduced the need for `v-is`. However, since it is a directive, its value is considered a JavaScript expression. This makes it awkward to use (e.g. `v-is="'foo'"`) when majority of casting is non-dynamic, and also hinders static analysis when casting to built-in Vue components, e.g. transition-group. This commit adds the ability to cast a native element to a Vue component by simply adding a `vue:` prefix: ```html <button is="vue:my-button"></button> <ul is="vue:transition-group" tag="ul"></ul> ```
This commit is contained in:
		
							parent
							
								
									422b13e798
								
							
						
					
					
						commit
						af9e6999e1
					
				@ -488,7 +488,12 @@ function parseTag(
 | 
				
			|||||||
  const options = context.options
 | 
					  const options = context.options
 | 
				
			||||||
  if (!context.inVPre && !options.isCustomElement(tag)) {
 | 
					  if (!context.inVPre && !options.isCustomElement(tag)) {
 | 
				
			||||||
    const hasVIs = props.some(
 | 
					    const hasVIs = props.some(
 | 
				
			||||||
      p => p.type === NodeTypes.DIRECTIVE && p.name === 'is'
 | 
					      p =>
 | 
				
			||||||
 | 
					        p.name === 'is' &&
 | 
				
			||||||
 | 
					        // v-is="xxx" (TODO: deprecate)
 | 
				
			||||||
 | 
					        (p.type === NodeTypes.DIRECTIVE ||
 | 
				
			||||||
 | 
					          // is="vue:xxx"
 | 
				
			||||||
 | 
					          (p.value && p.value.content.startsWith('vue:')))
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    if (options.isNativeTag && !hasVIs) {
 | 
					    if (options.isNativeTag && !hasVIs) {
 | 
				
			||||||
      if (!options.isNativeTag(tag)) tagType = ElementTypes.COMPONENT
 | 
					      if (!options.isNativeTag(tag)) tagType = ElementTypes.COMPONENT
 | 
				
			||||||
 | 
				
			|||||||
@ -230,13 +230,19 @@ export function resolveComponentType(
 | 
				
			|||||||
  context: TransformContext,
 | 
					  context: TransformContext,
 | 
				
			||||||
  ssr = false
 | 
					  ssr = false
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  const { tag } = node
 | 
					  let { tag } = node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 1. dynamic component
 | 
					  // 1. dynamic component
 | 
				
			||||||
  const isProp = isComponentTag(tag)
 | 
					  const isExplicitDynamic = isComponentTag(tag)
 | 
				
			||||||
    ? findProp(node, 'is')
 | 
					  const isProp =
 | 
				
			||||||
    : findDir(node, 'is')
 | 
					    findProp(node, 'is') || (!isExplicitDynamic && findDir(node, 'is'))
 | 
				
			||||||
  if (isProp) {
 | 
					  if (isProp) {
 | 
				
			||||||
 | 
					    if (!isExplicitDynamic && isProp.type === NodeTypes.ATTRIBUTE) {
 | 
				
			||||||
 | 
					      // <button is="vue:xxx">
 | 
				
			||||||
 | 
					      // if not <component>, only is value that starts with "vue:" will be
 | 
				
			||||||
 | 
					      // treated as component by the parse phase and reach here.
 | 
				
			||||||
 | 
					      tag = isProp.value!.content.slice(4)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
      const exp =
 | 
					      const exp =
 | 
				
			||||||
        isProp.type === NodeTypes.ATTRIBUTE
 | 
					        isProp.type === NodeTypes.ATTRIBUTE
 | 
				
			||||||
          ? isProp.value && createSimpleExpression(isProp.value.content, true)
 | 
					          ? isProp.value && createSimpleExpression(isProp.value.content, true)
 | 
				
			||||||
@ -247,6 +253,7 @@ export function resolveComponentType(
 | 
				
			|||||||
        ])
 | 
					        ])
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
 | 
					  // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
 | 
				
			||||||
  const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
 | 
					  const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
 | 
				
			||||||
@ -416,8 +423,11 @@ export function buildProps(
 | 
				
			|||||||
          isStatic = false
 | 
					          isStatic = false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // skip :is on <component>
 | 
					      // skip is on <component>, or is="vue:xxx"
 | 
				
			||||||
      if (name === 'is' && isComponentTag(tag)) {
 | 
					      if (
 | 
				
			||||||
 | 
					        name === 'is' &&
 | 
				
			||||||
 | 
					        (isComponentTag(tag) || (value && value.content.startsWith('vue:')))
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        continue
 | 
					        continue
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      properties.push(
 | 
					      properties.push(
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user