Skip to content

Commit 4de0669

Browse files
authored
Merge pull request #180 from airbnb/refactor-template
Refactored the data structure in template and `setData` JSON data.
2 parents 65a0c74 + b22a3aa commit 4de0669

File tree

31 files changed

+398
-232
lines changed

31 files changed

+398
-232
lines changed

packages/core/src/__tests__/helpers/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { ReactElement } from 'react';
22
import { TestingAdaptor, TestingAdaptorInstance } from './adaptor';
3-
import { ElementNode } from '../../reconciler/instance';
43

54
export class RenderResult {
65
public constructor(private adaptorInstance: TestingAdaptorInstance) {}
76

8-
public getContainer = (): ElementNode => this.adaptorInstance.data;
7+
public getContainer = () => this.adaptorInstance.data;
98

109
// ByText
1110

@@ -21,7 +20,8 @@ export class RenderResult {
2120
// TODO:
2221
};
2322

24-
public resolveUpdateCallback = (renderId?: string) => this.adaptorInstance.resolveUpdateCallback(renderId);
23+
public resolveUpdateCallback = (renderId?: string) =>
24+
this.adaptorInstance.resolveUpdateCallback(renderId);
2525

2626
public setManuallyResolvedUpdateCallback = (enabled: boolean) => {
2727
this.adaptorInstance.setManuallyResolvedUpdateCallback(enabled);

packages/core/src/__tests__/updates.test.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('updates', () => {
3535

3636
adaptor.run(<TestComp />);
3737
updater(3);
38-
expect(diff).toEqual({ 'c[0].c[0].text': '3' });
38+
expect(diff).toEqual({ 'meta.children[0].children[0].text': '3' });
3939
});
4040

4141
it('create or remove instance, re-render the whole sub-tree', () => {
@@ -55,13 +55,13 @@ describe('updates', () => {
5555
adaptor.run(<TestComp />);
5656
updater(true);
5757

58-
expect(Object.keys(diff)).toEqual(['c[0].c[1]']);
59-
expect(diff['c[0].c[1]'].c.length).toEqual(1);
58+
expect(Object.keys(diff)).toEqual(['meta.children[0].children[1]']);
59+
expect(diff['meta.children[0].children[1]'].children.length).toEqual(1);
6060

6161
updater(false);
6262

63-
expect(Object.keys(diff)).toEqual(['c[0].c[1]']);
64-
expect(diff['c[0].c[1]'].c.length).toEqual(0);
63+
expect(Object.keys(diff)).toEqual(['meta.children[0].children[1]']);
64+
expect(diff['meta.children[0].children[1]'].children.length).toEqual(0);
6565
});
6666

6767
it('only update events, should not trigger setData', () => {
@@ -102,6 +102,6 @@ describe('updates', () => {
102102

103103
// @ts-expect-error
104104
updater(false);
105-
expect(Object.keys(diff)).toEqual(['c[0].sid']);
105+
expect(Object.keys(diff)).toEqual(['meta.children[0].simplifiedId']);
106106
});
107107
});

packages/core/src/adaptor/__tests__/__snapshots__/wechat.test.tsx.snap

+14-12
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
exports[`WeChatAdaptor works with Page 1`] = `
44
Object {
5-
"c": Array [
6-
Object {
7-
"c": Array [],
8-
"id": 2,
9-
"props": Object {},
10-
"sid": 0,
11-
"type": "view",
12-
},
13-
],
14-
"id": 1,
15-
"props": Object {},
16-
"type": "GOJI_VIRTUAL_ROOT",
5+
"meta": Object {
6+
"children": Array [
7+
Object {
8+
"children": Array [],
9+
"gojiId": 2,
10+
"props": Object {},
11+
"simplifiedId": 0,
12+
"type": "view",
13+
},
14+
],
15+
"gojiId": 1,
16+
"props": Object {},
17+
"type": "GOJI_VIRTUAL_ROOT",
18+
},
1719
}
1820
`;

packages/core/src/constants.ts

+33
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,36 @@ export const SIMPLIFY_COMPONENTS: Array<SimplifyComponent> = [
1919
events: [],
2020
},
2121
];
22+
23+
/**
24+
* Get identifiers used by template. For production we use shorter identifiers for less bundle size.
25+
* This function is shared by both `@goji/core` and `@goji/webpack-plugin`.
26+
*
27+
* meta: for element data source, e.g. `<template data="{{ meta: meta }}" />`
28+
* props: for element props object, e.g. `<input value="{{meta.props.value}}" />`
29+
* type: for element type, e.g. `<input wx:if="{{meta.type === 'input'}}" />`
30+
* text: for text element content, e.g. `<text>{{meta.text}}</text>`
31+
* children: for element's children, e.g. `<block wx:for="{{meta.children}}" />`
32+
* gojiId: for element Goji id, which is the unique id for each element, e.g. <view data-goji-id="{{meta.gojiId}}">
33+
* simplifiedId: for element simplified id
34+
*/
35+
export const getTemplateIds = (nodeEnv = process.env.NODE_ENV) =>
36+
nodeEnv === 'production'
37+
? {
38+
meta: 'm',
39+
props: 'p',
40+
type: 't',
41+
text: 'x',
42+
children: 'c',
43+
gojiId: 'g',
44+
simplifiedId: 's',
45+
}
46+
: {
47+
meta: 'meta',
48+
props: 'props',
49+
type: 'type',
50+
text: 'text',
51+
children: 'children',
52+
gojiId: 'gojiId',
53+
simplifiedId: 'simplifiedId',
54+
};

packages/core/src/container.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { renderIntoContainer } from './render';
66
import { ElementInstance } from './reconciler/instance';
77
import { createEventProxy } from './components/eventProxy';
88
import { GojiProvider } from './components';
9-
import { GOJI_VIRTUAL_ROOT } from './constants';
9+
import { getTemplateIds, GOJI_VIRTUAL_ROOT } from './constants';
1010

1111
let gojiBlockingMode = false;
1212

@@ -59,7 +59,9 @@ export class Container {
5959
}
6060

6161
public requestUpdate() {
62-
const [data, diff] = this.virtualRootElement.pure('');
62+
const ids = getTemplateIds();
63+
const [elementNode, diff] = this.virtualRootElement.pure(ids.meta);
64+
const data = { [ids.meta]: elementNode };
6365
if (process.env.NODE_ENV !== 'production') {
6466
// eslint-disable-next-line global-require
6567
const { verifyDiff } = require('./utils/diff');

packages/core/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export {
3636
// eslint-disable-next-line camelcase
3737
SimplifyComponent as unstable_SimplifyComponent,
3838
GojiTarget,
39+
// eslint-disable-next-line camelcase
40+
getTemplateIds as unstable_getTemplateIds,
3941
} from './constants';
4042
export { gojiEvents } from './events';
4143
export { Partial } from './partial';

packages/core/src/lifecycles/universal/__tests__/hooks.test.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,13 @@ describe('universal lifecycles', () => {
239239
it('resolve: 0 -> 1 -> 2', () => {
240240
update();
241241
wrapper.resolveUpdateCallback('0');
242-
expect(mockSetData.mock.calls[0][0]).toEqual({ 'c[0].c[0].text': '1' });
242+
expect(mockSetData.mock.calls[0][0]).toEqual({ 'meta.children[0].children[0].text': '1' });
243243
update();
244244
update();
245245
wrapper.resolveUpdateCallback('2');
246246
wrapper.resolveUpdateCallback('1');
247247
expect(mockSetData).toBeCalledTimes(2);
248-
expect(mockSetData.mock.calls[1][0]).toEqual({ 'c[0].c[0].text': '3' });
248+
expect(mockSetData.mock.calls[1][0]).toEqual({ 'meta.children[0].children[0].text': '3' });
249249
});
250250

251251
it('resolve: 1 -> 0 -> 1', () => {
@@ -256,12 +256,12 @@ describe('universal lifecycles', () => {
256256
update();
257257
wrapper.resolveUpdateCallback('0');
258258
expect(mockSetData).toBeCalledTimes(1);
259-
expect(mockSetData.mock.calls[0][0]).toEqual({ 'c[0].c[0].text': '2' });
259+
expect(mockSetData.mock.calls[0][0]).toEqual({ 'meta.children[0].children[0].text': '2' });
260260

261261
update();
262262
wrapper.resolveUpdateCallback('1');
263263
expect(mockSetData).toBeCalledTimes(2);
264-
expect(mockSetData.mock.calls[1][0]).toEqual({ 'c[0].c[0].text': '3' });
264+
expect(mockSetData.mock.calls[1][0]).toEqual({ 'meta.children[0].children[0].text': '3' });
265265
});
266266

267267
it('resolve: 2 -> 0 -> 1 -> 1', () => {
@@ -276,12 +276,12 @@ describe('universal lifecycles', () => {
276276
wrapper.resolveUpdateCallback('0');
277277
wrapper.resolveUpdateCallback('1');
278278
expect(mockSetData).toBeCalledTimes(1);
279-
expect(mockSetData.mock.calls[0][0]).toEqual({ 'c[0].c[0].text': '3' });
279+
expect(mockSetData.mock.calls[0][0]).toEqual({ 'meta.children[0].children[0].text': '3' });
280280

281281
update();
282282
wrapper.resolveUpdateCallback('1');
283283
expect(mockSetData).toBeCalledTimes(2);
284-
expect(mockSetData.mock.calls[1][0]).toEqual({ 'c[0].c[0].text': '4' });
284+
expect(mockSetData.mock.calls[1][0]).toEqual({ 'meta.children[0].children[0].text': '4' });
285285
});
286286
});
287287
});

packages/core/src/portal/__tests__/index.test.tsx

+15-8
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ import { createPortal } from '..';
33
import { View } from '../..';
44
import { render } from '../../__tests__/helpers';
55
import { act } from '../../testUtils';
6+
import { ElementNodeDevelopment } from '../../reconciler/instance';
67

78
describe('createPortal', () => {
89
test('works', () => {
910
const Content = () => createPortal(<View>Content</View>);
1011
const App = () => (
11-
<View>
12-
<View>title</View>
13-
<Content />
14-
</View>
15-
);
12+
<View>
13+
<View>title</View>
14+
<Content />
15+
</View>
16+
);
1617
const wrapper = render(<App />);
17-
expect(wrapper.getContainer().c.length).toBe(2);
18+
expect((wrapper.getContainer() as { meta: ElementNodeDevelopment }).meta.children.length).toBe(
19+
2,
20+
);
1821
});
1922

2023
test('works in condition', () => {
@@ -31,13 +34,17 @@ describe('createPortal', () => {
3134
);
3235
};
3336
const wrapper = render(<App />);
34-
expect(wrapper.getContainer().c.length).toBe(1);
37+
expect((wrapper.getContainer() as { meta: ElementNodeDevelopment }).meta.children.length).toBe(
38+
1,
39+
);
3540

3641
const spy = jest.spyOn(global.console, 'error');
3742
act(() => {
3843
showPortal();
3944
});
40-
expect(wrapper.getContainer().c.length).toBe(2);
45+
expect((wrapper.getContainer() as { meta: ElementNodeDevelopment }).meta.children.length).toBe(
46+
2,
47+
);
4148
expect(spy).not.toHaveBeenCalled();
4249
spy.mockRestore();
4350
});

packages/core/src/reconciler/__tests__/instance.test.tsx

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState, createRef } from 'react';
2-
import { ElementInstance, ElementNode, TextNode } from '../instance';
2+
import { ElementInstance, ElementNodeDevelopment, TextNodeDevelopment } from '../instance';
33
import { Container } from '../../container';
44
import { TYPE_SUBTREE } from '../../constants';
55
import { View, gojiEvents } from '../..';
@@ -33,8 +33,8 @@ describe('ElementInstance', () => {
3333

3434
test('pure works', () => {
3535
const [pured] = instance.pure('');
36-
expect(pured.props.className).toBe('wrapper');
37-
expect(pured.props.onClick).toBeUndefined();
36+
expect((pured as ElementNodeDevelopment).props.className).toBe('wrapper');
37+
expect((pured as ElementNodeDevelopment).props.onClick).toBeUndefined();
3838
});
3939

4040
test('simplify works', () => {
@@ -47,7 +47,7 @@ describe('ElementInstance', () => {
4747
new Container(new TestingAdaptorInstance()),
4848
);
4949
const [pured] = simpleInstance.pure('');
50-
expect(pured.sid).not.toBeUndefined();
50+
expect((pured as ElementNodeDevelopment).simplifiedId).not.toBeUndefined();
5151
});
5252

5353
describe('getSubtreeId', () => {
@@ -221,9 +221,12 @@ describe('ElementInstance', () => {
221221
const { getContainer } = render(<App />);
222222
const getTextList = () => {
223223
// FIXME: will implement better debug API for RenderResult
224-
const viewNodes = (getContainer().c[0] as ElementNode).c;
225-
const textNodes = viewNodes.map(view => (view as ElementNode).c[0]);
226-
return textNodes.map(text => (text as TextNode).text);
224+
const viewNodes = (
225+
(getContainer() as { meta: ElementNodeDevelopment }).meta
226+
.children[0] as ElementNodeDevelopment
227+
).children;
228+
const textNodes = viewNodes.map(view => (view as ElementNodeDevelopment).children[0]);
229+
return textNodes.map(text => (text as TextNodeDevelopment).text);
227230
};
228231
expect(getTextList()).toEqual(['1', '2', '3']);
229232
act(() => {
@@ -250,9 +253,9 @@ describe('ElementInstance', () => {
250253
const { getContainer } = render(<App />);
251254
const getTextList = () => {
252255
// FIXME: will implement better debug API for RenderResult
253-
const viewNodes = (getContainer() as ElementNode).c;
254-
const textNodes = viewNodes.map(view => (view as ElementNode).c[0]);
255-
return textNodes.map(text => (text as TextNode).text);
256+
const viewNodes = (getContainer() as { meta: ElementNodeDevelopment }).meta.children;
257+
const textNodes = viewNodes.map(view => (view as ElementNodeDevelopment).children[0]);
258+
return textNodes.map(text => (text as TextNodeDevelopment).text);
256259
};
257260
expect(getTextList()).toEqual(['1', '2', '3']);
258261
// @ts-expect-error

0 commit comments

Comments
 (0)