mirror of
				https://github.com/vuejs/babel-plugin-jsx.git
				synced 2025-11-01 01:42:21 +08:00 
			
		
		
		
	feat: vSlots
This commit is contained in:
		| @@ -156,7 +156,17 @@ const App = { | ||||
|  | ||||
| ### 插槽  | ||||
|  | ||||
| 目前功能没有想好怎么实现,欢迎在 issue 中讨论,可以先使用 `props` 来代替 | ||||
| ```jsx | ||||
| const App = { | ||||
|   setup() { | ||||
|     const slots = { | ||||
|       a: () => <div>A</div>, | ||||
|       b: () => <span>B</span> | ||||
|     } | ||||
|     return () => <A vSlots={slots} /> | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## 谁在用 | ||||
|  | ||||
|   | ||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -157,7 +157,17 @@ const App = { | ||||
|  | ||||
| ### Slot  | ||||
|  | ||||
| Why Not props ? | ||||
| ```jsx | ||||
| const App = { | ||||
|   setup() { | ||||
|     const slots = { | ||||
|       a: () => <div>A</div>, | ||||
|       b: () => <span>B</span> | ||||
|     } | ||||
|     return () => <A vSlots={slots} /> | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Who is using | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ interface Opts { | ||||
|   compatibleProps?: boolean; | ||||
| } | ||||
|  | ||||
| export type ExcludesFalse = <T>(x: T | false) => x is T; | ||||
| export type ExcludesBoolean = <T>(x: T | false | true) => x is T; | ||||
|  | ||||
| export default () => ({ | ||||
|   name: 'babel-plugin-jsx', | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import { | ||||
|   isFragment, | ||||
| } from './utils'; | ||||
| import { PatchFlags, PatchFlagNames } from './patchFlags'; | ||||
| import { State, ExcludesFalse } from './'; | ||||
| import { State, ExcludesBoolean } from './'; | ||||
|  | ||||
| const xlinkRE = /^xlink([A-Z])/; | ||||
| const onRE = /^on[^a-z]/; | ||||
| @@ -116,6 +116,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | ||||
|   const directives: t.ArrayExpression[] = []; | ||||
|   const dynamicPropNames = new Set(); | ||||
|  | ||||
|   let slots: t.Identifier | t.Expression | null = null; | ||||
|   let patchFlag = 0; | ||||
|  | ||||
|   if (isFragment(path.get('openingElement').get('name'))) { | ||||
| @@ -126,6 +127,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | ||||
|     return { | ||||
|       tag, | ||||
|       isComponent, | ||||
|       slots, | ||||
|       props: t.nullLiteral(), | ||||
|       directives, | ||||
|       patchFlag, | ||||
| @@ -201,7 +203,10 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | ||||
|             value: attributeValue, | ||||
|           }); | ||||
|  | ||||
|           if (directive) { | ||||
|           if (directiveName === 'slots') { | ||||
|             slots = attributeValue; | ||||
|             return; | ||||
|           } else if (directive) { | ||||
|             directives.push(t.arrayExpression(directive)); | ||||
|           } else { | ||||
|             // must be v-model and is a component | ||||
| @@ -302,7 +307,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | ||||
|         [ | ||||
|           ...exps, | ||||
|           !!objectProperties.length && t.objectExpression(objectProperties), | ||||
|         ].filter(Boolean as any as ExcludesFalse), | ||||
|         ].filter(Boolean as any as ExcludesBoolean), | ||||
|       ); | ||||
|     } else { | ||||
|       // single no need for a mergeProps call | ||||
| @@ -316,6 +321,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | ||||
|     tag, | ||||
|     props: propsExpression, | ||||
|     isComponent, | ||||
|     slots, | ||||
|     directives, | ||||
|     patchFlag, | ||||
|     dynamicPropNames, | ||||
| @@ -377,6 +383,7 @@ const transformJSXElement = ( | ||||
|     directives, | ||||
|     patchFlag, | ||||
|     dynamicPropNames, | ||||
|     slots | ||||
|   } = buildProps(path, state); | ||||
|  | ||||
|   const { scope: { bindings } } = path; | ||||
| @@ -403,18 +410,25 @@ const transformJSXElement = ( | ||||
|     tag, | ||||
|     // @ts-ignore | ||||
|     compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props, | ||||
|     !!children.length ? ( | ||||
|       isComponent ? t.objectExpression([ | ||||
|         t.objectProperty( | ||||
|           t.identifier('default'), | ||||
|           t.arrowFunctionExpression([], t.arrayExpression(children)) | ||||
|         ) | ||||
|       ]) : t.arrayExpression(children) | ||||
|     (children.length || slots) ? ( | ||||
|       isComponent && (children.length || slots) | ||||
|         ? t.objectExpression([ | ||||
|             !!children.length && t.objectProperty( | ||||
|               t.identifier('default'), | ||||
|               t.arrowFunctionExpression([], t.arrayExpression(children)) | ||||
|             ), | ||||
|             ...(slots ? ( | ||||
|               t.isObjectExpression(slots) | ||||
|                 ? (slots as any as t.ObjectExpression).properties | ||||
|                 : [t.spreadElement(slots as any)] | ||||
|             ) : []) | ||||
|           ].filter(Boolean as any as ExcludesBoolean)) | ||||
|         : t.arrayExpression(children) | ||||
|     ) : t.nullLiteral(), | ||||
|     !!patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false), | ||||
|     !!dynamicPropNames.size | ||||
|     && t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string))), | ||||
|   ].filter(Boolean as any as ExcludesFalse)); | ||||
|   ].filter(Boolean as any as ExcludesBoolean)); | ||||
|  | ||||
|   if (!directives.length) { | ||||
|     return createVNode; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import * as t from '@babel/types'; | ||||
| import htmlTags from 'html-tags'; | ||||
| import svgTags from 'svg-tags'; | ||||
| import { NodePath } from '@babel/traverse'; | ||||
| import { State, ExcludesFalse } from './'; | ||||
| import { State, ExcludesBoolean } from './'; | ||||
|  | ||||
| /** | ||||
|  * create Identifier | ||||
| @@ -270,7 +270,7 @@ const parseDirectives = (args: { | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ].filter(Boolean as any as ExcludesFalse) : undefined, | ||||
|     ].filter(Boolean as any as ExcludesBoolean) : undefined, | ||||
|   }; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -263,6 +263,47 @@ describe('Transform JSX', () => { | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('slots', () => { | ||||
|   test('with default', () => { | ||||
|     const A = (_, { slots }) => ( | ||||
|       <> | ||||
|         {slots.default()} | ||||
|         {slots.foo('val')} | ||||
|       </> | ||||
|     ); | ||||
|  | ||||
|     const wrapper = mount({ | ||||
|       setup() { | ||||
|         const slots = { | ||||
|           foo: (val) => <div>{val}</div>, | ||||
|         }; | ||||
|         return () => <A vSlots={slots}><span>default</span></A>; | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     expect(wrapper.html()).toBe('<span>default</span><div>val</div>'); | ||||
|   }); | ||||
|  | ||||
|   test('without default', () => { | ||||
|     const A = (_, { slots }) => ( | ||||
|       <> | ||||
|         {slots.foo('foo')} | ||||
|       </> | ||||
|     ); | ||||
|  | ||||
|     const wrapper = mount({ | ||||
|       setup() { | ||||
|         const slots = { | ||||
|           foo: (val) => <div>{val}</div>, | ||||
|         }; | ||||
|         return () => <A vSlots={slots} />; | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     expect(wrapper.html()).toBe('<div>foo</div>'); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('PatchFlags', () => { | ||||
|   test('static', () => { | ||||
|     const wrapper = shallowMount({ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user