mirror of
https://github.com/vuejs/babel-plugin-jsx.git
synced 2024-11-13 02:59:13 +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
|
### 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
|
## Who is using
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ interface Opts {
|
|||||||
compatibleProps?: boolean;
|
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 () => ({
|
export default () => ({
|
||||||
name: 'babel-plugin-jsx',
|
name: 'babel-plugin-jsx',
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
isFragment,
|
isFragment,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { PatchFlags, PatchFlagNames } from './patchFlags';
|
import { PatchFlags, PatchFlagNames } from './patchFlags';
|
||||||
import { State, ExcludesFalse } from './';
|
import { State, ExcludesBoolean } from './';
|
||||||
|
|
||||||
const xlinkRE = /^xlink([A-Z])/;
|
const xlinkRE = /^xlink([A-Z])/;
|
||||||
const onRE = /^on[^a-z]/;
|
const onRE = /^on[^a-z]/;
|
||||||
@ -116,6 +116,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
|||||||
const directives: t.ArrayExpression[] = [];
|
const directives: t.ArrayExpression[] = [];
|
||||||
const dynamicPropNames = new Set();
|
const dynamicPropNames = new Set();
|
||||||
|
|
||||||
|
let slots: t.Identifier | t.Expression | null = null;
|
||||||
let patchFlag = 0;
|
let patchFlag = 0;
|
||||||
|
|
||||||
if (isFragment(path.get('openingElement').get('name'))) {
|
if (isFragment(path.get('openingElement').get('name'))) {
|
||||||
@ -126,6 +127,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
|||||||
return {
|
return {
|
||||||
tag,
|
tag,
|
||||||
isComponent,
|
isComponent,
|
||||||
|
slots,
|
||||||
props: t.nullLiteral(),
|
props: t.nullLiteral(),
|
||||||
directives,
|
directives,
|
||||||
patchFlag,
|
patchFlag,
|
||||||
@ -201,7 +203,10 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
|||||||
value: attributeValue,
|
value: attributeValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (directive) {
|
if (directiveName === 'slots') {
|
||||||
|
slots = attributeValue;
|
||||||
|
return;
|
||||||
|
} else if (directive) {
|
||||||
directives.push(t.arrayExpression(directive));
|
directives.push(t.arrayExpression(directive));
|
||||||
} else {
|
} else {
|
||||||
// must be v-model and is a component
|
// must be v-model and is a component
|
||||||
@ -302,7 +307,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
|||||||
[
|
[
|
||||||
...exps,
|
...exps,
|
||||||
!!objectProperties.length && t.objectExpression(objectProperties),
|
!!objectProperties.length && t.objectExpression(objectProperties),
|
||||||
].filter(Boolean as any as ExcludesFalse),
|
].filter(Boolean as any as ExcludesBoolean),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// single no need for a mergeProps call
|
// single no need for a mergeProps call
|
||||||
@ -316,6 +321,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => {
|
|||||||
tag,
|
tag,
|
||||||
props: propsExpression,
|
props: propsExpression,
|
||||||
isComponent,
|
isComponent,
|
||||||
|
slots,
|
||||||
directives,
|
directives,
|
||||||
patchFlag,
|
patchFlag,
|
||||||
dynamicPropNames,
|
dynamicPropNames,
|
||||||
@ -377,6 +383,7 @@ const transformJSXElement = (
|
|||||||
directives,
|
directives,
|
||||||
patchFlag,
|
patchFlag,
|
||||||
dynamicPropNames,
|
dynamicPropNames,
|
||||||
|
slots
|
||||||
} = buildProps(path, state);
|
} = buildProps(path, state);
|
||||||
|
|
||||||
const { scope: { bindings } } = path;
|
const { scope: { bindings } } = path;
|
||||||
@ -403,18 +410,25 @@ const transformJSXElement = (
|
|||||||
tag,
|
tag,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props,
|
compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props,
|
||||||
!!children.length ? (
|
(children.length || slots) ? (
|
||||||
isComponent ? t.objectExpression([
|
isComponent && (children.length || slots)
|
||||||
t.objectProperty(
|
? t.objectExpression([
|
||||||
|
!!children.length && t.objectProperty(
|
||||||
t.identifier('default'),
|
t.identifier('default'),
|
||||||
t.arrowFunctionExpression([], t.arrayExpression(children))
|
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(),
|
) : t.nullLiteral(),
|
||||||
!!patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false),
|
!!patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false),
|
||||||
!!dynamicPropNames.size
|
!!dynamicPropNames.size
|
||||||
&& t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string))),
|
&& 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) {
|
if (!directives.length) {
|
||||||
return createVNode;
|
return createVNode;
|
||||||
|
@ -2,7 +2,7 @@ import * as t from '@babel/types';
|
|||||||
import htmlTags from 'html-tags';
|
import htmlTags from 'html-tags';
|
||||||
import svgTags from 'svg-tags';
|
import svgTags from 'svg-tags';
|
||||||
import { NodePath } from '@babel/traverse';
|
import { NodePath } from '@babel/traverse';
|
||||||
import { State, ExcludesFalse } from './';
|
import { State, ExcludesBoolean } from './';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create Identifier
|
* 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', () => {
|
describe('PatchFlags', () => {
|
||||||
test('static', () => {
|
test('static', () => {
|
||||||
const wrapper = shallowMount({
|
const wrapper = shallowMount({
|
||||||
|
Loading…
Reference in New Issue
Block a user