mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2024-11-10 09:39:14 +08:00
feat: vSlots
This commit is contained in:
parent
e8861330ad
commit
17a0e56342
@ -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(
|
||||
(children.length || slots) ? (
|
||||
isComponent && (children.length || slots)
|
||||
? t.objectExpression([
|
||||
!!children.length && t.objectProperty(
|
||||
t.identifier('default'),
|
||||
t.arrowFunctionExpression([], t.arrayExpression(children))
|
||||
)
|
||||
]) : 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({
|
||||
|
Loading…
Reference in New Issue
Block a user