Skip to content

Commit

Permalink
feat: remove freeze and add some test cases (#53)
Browse files Browse the repository at this point in the history
* feat: remove freeze and add some test cases

* fix: rename isObject > isPlainObject

* fix: export toRaw
  • Loading branch information
Rongyan Chen authored Apr 20, 2022
1 parent 34e7e57 commit a75b9a7
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 91 deletions.
8 changes: 5 additions & 3 deletions packages/pwc/src/elements/__tests__/HTMLElement.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function getSimpleCustomElement() {
[
{
name: 'onclick',
value: this.onClick.bind(this),
value: this.onClick,
}
],
this.text,
Expand All @@ -44,7 +44,7 @@ function getNestedCustomElement() {
[
{
name: 'onclick',
value: this.onClick.bind(this),
value: this.onClick,
}
],
this.text,
Expand Down Expand Up @@ -85,7 +85,7 @@ function getReactiveCustomElement() {
},
{
name: 'onclick',
value: this.onClick.bind(this),
value: this.onClick,
}
],
this.#text,
Expand Down Expand Up @@ -185,6 +185,8 @@ describe('Render nested components', () => {

items = [];

callback() {}

get template() {
return mockChildFn(this);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/pwc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import './elements';

export { nextTick } from './elements/sheduler';
export * from './decorators';
export { toRaw } from './utils/toRaw';
export { toRaw } from './utils';

export { compileTemplateInRuntime as html } from '@pwc/compiler/compileTemplateInRuntime';
104 changes: 51 additions & 53 deletions packages/pwc/src/reactivity/__tests__/reactive.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Reactive } from '../reactive';
import { toRaw } from '../../utils';

class MockReactiveElement {
#initialized = false;
Expand All @@ -9,6 +10,7 @@ class MockReactiveElement {
};
constructor(initialValue) {
this.reactive.initValue('#data', initialValue);
this.reactive.initValue('data', initialValue);
this.#initialized = true;
this.isUpdating = false;
}
Expand All @@ -18,26 +20,10 @@ class MockReactiveElement {
get data() {
return this.reactive.getValue('#data');
}
_requestUpdate() {
this.isUpdating = true;
}
}
class MockNotReactiveElement {
#initialized = false;
isUpdating = false;
reactive = new Reactive(this);
_getInitialState() {
return this.#initialized;
};
constructor(initialValue) {
this.reactive.initValue('data', initialValue);
this.#initialized = true;
this.isUpdating = false;
}
set data(val) {
set publicData(val) {
this.reactive.setValue('data', val);
}
get data() {
get publicData() {
return this.reactive.getValue('data');
}
_requestUpdate() {
Expand All @@ -49,16 +35,27 @@ describe('Create a reactive property', () => {
it('A primitive property should request a update', () => {
const element = new MockReactiveElement('Jack');
expect(element.isUpdating).toBe(false);

// private property
element.data = 'Tom';
expect(element.isUpdating).toBe(true);
expect(element.data).toBe('Tom');

element.isUpdating = false;
expect(element.isUpdating).toBe(false);

// public property
element.publicData = 'Tom';
expect(element.isUpdating).toBe(true);
expect(element.publicData).toBe('Tom');
});
it('A object property should request a update', () => {
const element = new MockReactiveElement({
name: 'Jack'
});
expect(element.isUpdating).toBe(false);

// 1. private property
// change value
element.data.name = 'Tom';
expect(element.isUpdating).toBe(true);
Expand All @@ -75,11 +72,30 @@ describe('Create a reactive property', () => {
delete element.data['number'];
expect(element.isUpdating).toBe(true);
expect(element.data).toEqual({ name: 'Tom' });

// 2. public property
// change value
element.publicData.name = 'Tom';
expect(element.isUpdating).toBe(true);
expect(element.publicData).toEqual({ name: 'Tom' });
element.isUpdating = false;

// add prop
element.publicData.number = '1';
expect(element.isUpdating).toBe(true);
expect(element.publicData).toEqual({ name: 'Tom', number: '1' });
element.isUpdating = false;

// delete prop
delete element.publicData['number'];
expect(element.isUpdating).toBe(true);
expect(element.publicData).toEqual({ name: 'Tom' });
});
it('A array property should request a update', () => {
const element = new MockReactiveElement(['Jack']);
expect(element.isUpdating).toBe(false);

// 1. private property
// push
element.data.push('Tom')
expect(element.isUpdating).toBe(true);
Expand All @@ -90,44 +106,26 @@ describe('Create a reactive property', () => {
element.data.splice(1, 1);
expect(element.isUpdating).toBe(true);
expect(element.data).toEqual(['Jack']);
});
});

describe('Create a normal property', () => {
function assertError(it: any, msg: string) {
expect(it).toThrow(TypeError);
expect(it).toThrow(msg);
}

it('It should request a update when the source changed', () => {
const element = new MockNotReactiveElement('Jack');
expect(element.isUpdating).toBe(false);
element.data = 'Tom';
// 2. public property
// push
element.publicData.push('Tom')
expect(element.isUpdating).toBe(true);
expect(element.data).toBe('Tom');
});

it('It should throw a error when a readonly object changed', () => {
const element = new MockNotReactiveElement({
name: 'Jack'
});
// change value
assertError(() => element.data.name = 'Tom', `Cannot assign to read only property 'name' of object '#<Object>'`);

// add prop
assertError(() => element.data.number = '1', 'Cannot add property number, object is not extensible');

// delete prop
assertError(() => delete element.data['name'], `Cannot delete property 'name' of #<Object>`);
});

it('It should not request a update when a item changed', () => {
const element = new MockNotReactiveElement(['Jack']);
expect(element.publicData).toEqual(['Jack', 'Tom']);
element.isUpdating = false;

// push
assertError(() => element.data.push('Tom'), 'Cannot add property 1, object is not extensible');

// splice
assertError(() => element.data.splice(1, 1), `Cannot assign to read only property 'length' of object '[object Array]'`);
element.publicData.splice(1, 1);
expect(element.isUpdating).toBe(true);
expect(element.publicData).toEqual(['Jack']);
});
it('Public Data Should be shallow cloned', () => {
const data = {
someObject: {}
};
const element = new MockReactiveElement(data);

expect(toRaw(element.data) === data).toBe(true);
expect(toRaw(element.publicData) === data).toBe(false);
})
});
8 changes: 4 additions & 4 deletions packages/pwc/src/reactivity/reactive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isArray, isObject, isPrivate, shallowCloneAndFreeze } from '../utils';
import { isArray, isPlainObject, isPrivate, shallowClone } from '../utils';
import { getProxyHandler } from './handler';

interface ReactiveType {
Expand Down Expand Up @@ -41,8 +41,8 @@ export class Reactive implements ReactiveType {
if (isPrivate(prop)) {
this.#setReactiveValue(prop, value);
} else {
// Clone and Freeze public props and it should not be reactive
this.#setNormalValue(prop, shallowCloneAndFreeze(value));
// It should shallow clone public props to prevent effects of passing by reference
this.#setReactiveValue(prop, shallowClone(value));
}

if (forceUpdate) {
Expand All @@ -51,7 +51,7 @@ export class Reactive implements ReactiveType {
}

#setReactiveValue(prop: string, value: unknown) {
if (isArray(value) || isObject(value)) {
if (isArray(value) || isPlainObject(value)) {
this.#createReactiveProperty(prop, value);
} else {
this.#setNormalValue(prop, value);
Expand Down
2 changes: 1 addition & 1 deletion packages/pwc/src/utils/checkTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function isFunction(value: unknown) {
return typeof value === 'function';
}

export function isObject(value: unknown) {
export function isPlainObject(value: unknown) {
return Object.prototype.toString.call(value) === '[object Object]';
}

Expand Down
2 changes: 1 addition & 1 deletion packages/pwc/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ export * from './isEventName';
export * from './shallowEqual';
export * from './generateUid';
export * from './checkTypes';
export * from './shallowCloneAndFreeze';
export * from './shallowClone';
export * from './toRaw';

23 changes: 23 additions & 0 deletions packages/pwc/src/utils/shallowClone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { isArray, isPlainObject, isMap, isSet } from './checkTypes';

// Shallow Clone the Property value
// Attention, it only clones Set\Map\Array\Plain Object
export function shallowClone(value: any) {
if (isSet(value)) {
return new Set(value);
}

if (isMap(value)) {
return new Map(value);
}

if (isArray(value)) {
return [...value];
}

if (isPlainObject(value)) {
return Object.assign({}, value);
}

return value;
}
28 changes: 0 additions & 28 deletions packages/pwc/src/utils/shallowCloneAndFreeze.ts

This file was deleted.

0 comments on commit a75b9a7

Please sign in to comment.