Skip to content

Commit 17a0e56

Browse files
committed
feat: vSlots
1 parent e886133 commit 17a0e56

File tree

6 files changed

+91
-16
lines changed

6 files changed

+91
-16
lines changed

README-zh_CN.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,17 @@ const App = {
156156
157157
### 插槽
158158
159-
目前功能没有想好怎么实现,欢迎在 issue 中讨论,可以先使用 `props` 来代替
159+
```jsx
160+
const App = {
161+
setup() {
162+
const slots = {
163+
a: () => <div>A</div>,
164+
b: () => <span>B</span>
165+
}
166+
return () => <A vSlots={slots} />
167+
}
168+
}
169+
```
160170
161171
## 谁在用
162172

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,17 @@ const App = {
157157
158158
### Slot
159159
160-
Why Not props ?
160+
```jsx
161+
const App = {
162+
setup() {
163+
const slots = {
164+
a: () => <div>A</div>,
165+
b: () => <span>B</span>
166+
}
167+
return () => <A vSlots={slots} />
168+
}
169+
}
170+
```
161171
162172
## Who is using
163173

packages/babel-plugin-jsx/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface Opts {
1313
compatibleProps?: boolean;
1414
}
1515

16-
export type ExcludesFalse = <T>(x: T | false) => x is T;
16+
export type ExcludesBoolean = <T>(x: T | false | true) => x is T;
1717

1818
export default () => ({
1919
name: 'babel-plugin-jsx',

packages/babel-plugin-jsx/src/transform-vue-jsx.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
isFragment,
1515
} from './utils';
1616
import { PatchFlags, PatchFlagNames } from './patchFlags';
17-
import { State, ExcludesFalse } from './';
17+
import { State, ExcludesBoolean } from './';
1818

1919
const xlinkRE = /^xlink([A-Z])/;
2020
const onRE = /^on[^a-z]/;
@@ -116,6 +116,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
116116
const directives: t.ArrayExpression[] = [];
117117
const dynamicPropNames = new Set();
118118

119+
let slots: t.Identifier | t.Expression | null = null;
119120
let patchFlag = 0;
120121

121122
if (isFragment(path.get('openingElement').get('name'))) {
@@ -126,6 +127,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
126127
return {
127128
tag,
128129
isComponent,
130+
slots,
129131
props: t.nullLiteral(),
130132
directives,
131133
patchFlag,
@@ -201,7 +203,10 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
201203
value: attributeValue,
202204
});
203205

204-
if (directive) {
206+
if (directiveName === 'slots') {
207+
slots = attributeValue;
208+
return;
209+
} else if (directive) {
205210
directives.push(t.arrayExpression(directive));
206211
} else {
207212
// must be v-model and is a component
@@ -302,7 +307,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
302307
[
303308
...exps,
304309
!!objectProperties.length && t.objectExpression(objectProperties),
305-
].filter(Boolean as any as ExcludesFalse),
310+
].filter(Boolean as any as ExcludesBoolean),
306311
);
307312
} else {
308313
// single no need for a mergeProps call
@@ -316,6 +321,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
316321
tag,
317322
props: propsExpression,
318323
isComponent,
324+
slots,
319325
directives,
320326
patchFlag,
321327
dynamicPropNames,
@@ -377,6 +383,7 @@ const transformJSXElement = (
377383
directives,
378384
patchFlag,
379385
dynamicPropNames,
386+
slots
380387
} = buildProps(path, state);
381388

382389
const { scope: { bindings } } = path;
@@ -403,18 +410,25 @@ const transformJSXElement = (
403410
tag,
404411
// @ts-ignore
405412
compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props,
406-
!!children.length ? (
407-
isComponent ? t.objectExpression([
408-
t.objectProperty(
409-
t.identifier('default'),
410-
t.arrowFunctionExpression([], t.arrayExpression(children))
411-
)
412-
]) : t.arrayExpression(children)
413+
(children.length || slots) ? (
414+
isComponent && (children.length || slots)
415+
? t.objectExpression([
416+
!!children.length && t.objectProperty(
417+
t.identifier('default'),
418+
t.arrowFunctionExpression([], t.arrayExpression(children))
419+
),
420+
...(slots ? (
421+
t.isObjectExpression(slots)
422+
? (slots as any as t.ObjectExpression).properties
423+
: [t.spreadElement(slots as any)]
424+
) : [])
425+
].filter(Boolean as any as ExcludesBoolean))
426+
: t.arrayExpression(children)
413427
) : t.nullLiteral(),
414428
!!patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false),
415429
!!dynamicPropNames.size
416430
&& t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string))),
417-
].filter(Boolean as any as ExcludesFalse));
431+
].filter(Boolean as any as ExcludesBoolean));
418432

419433
if (!directives.length) {
420434
return createVNode;

packages/babel-plugin-jsx/src/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as t from '@babel/types';
22
import htmlTags from 'html-tags';
33
import svgTags from 'svg-tags';
44
import { NodePath } from '@babel/traverse';
5-
import { State, ExcludesFalse } from './';
5+
import { State, ExcludesBoolean } from './';
66

77
/**
88
* create Identifier
@@ -270,7 +270,7 @@ const parseDirectives = (args: {
270270
),
271271
),
272272
),
273-
].filter(Boolean as any as ExcludesFalse) : undefined,
273+
].filter(Boolean as any as ExcludesBoolean) : undefined,
274274
};
275275
};
276276

packages/babel-plugin-jsx/test/index.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,47 @@ describe('Transform JSX', () => {
263263
});
264264
});
265265

266+
describe('slots', () => {
267+
test('with default', () => {
268+
const A = (_, { slots }) => (
269+
<>
270+
{slots.default()}
271+
{slots.foo('val')}
272+
</>
273+
);
274+
275+
const wrapper = mount({
276+
setup() {
277+
const slots = {
278+
foo: (val) => <div>{val}</div>,
279+
};
280+
return () => <A vSlots={slots}><span>default</span></A>;
281+
},
282+
});
283+
284+
expect(wrapper.html()).toBe('<span>default</span><div>val</div>');
285+
});
286+
287+
test('without default', () => {
288+
const A = (_, { slots }) => (
289+
<>
290+
{slots.foo('foo')}
291+
</>
292+
);
293+
294+
const wrapper = mount({
295+
setup() {
296+
const slots = {
297+
foo: (val) => <div>{val}</div>,
298+
};
299+
return () => <A vSlots={slots} />;
300+
},
301+
});
302+
303+
expect(wrapper.html()).toBe('<div>foo</div>');
304+
});
305+
});
306+
266307
describe('PatchFlags', () => {
267308
test('static', () => {
268309
const wrapper = shallowMount({

0 commit comments

Comments
 (0)