Add support to computed and store for immutable structures

Adds optional performance support for apps using an immutable data structure such as redux. Adds the `immutable` boolean option for compile and an `immutable` option to store as well. When these options are used, computed will not recompute if the object has not changed. If your data structure is not immutable you should not use this as svelte cannot know if a mutation was made on objects.

This PR also adds support for Dates and NaN values so computed properties will not recompute if a date has not changed or a value did not change from NaN.

This closes out these issues:
* https://github.com/sveltejs/svelte/issues/1146
* https://github.com/sveltejs/svelte/issues/1161

This is my first PR for Svelte. Any feedback would be appreciated!
pull/1164/head
Jacob Wright 7 years ago
parent 0ef8229077
commit d2f8e472a5

@ -375,6 +375,7 @@ export default function dom(
if (sigil === '@') {
if (name in shared) {
if (options.dev && `${name}Dev` in shared) name = `${name}Dev`;
else if (options.immutable && `${name}Immutable` in shared) name = `${name}Immutable`;
usedHelpers.add(name);
}

@ -52,6 +52,7 @@ export interface CompileOptions {
cssOutputFilename?: string;
dev?: boolean;
immutable?: boolean;
shared?: boolean | string;
cascade?: boolean;
hydratable?: boolean;

@ -26,7 +26,25 @@ export function destroyDev(detach) {
}
export function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
export function differsImmutable(a, b) {
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b;
}
export function dispatchObservers(component, group, changed, newState, oldState) {
@ -165,6 +183,27 @@ export function _set(newState) {
}
}
export function _setImmutable(newState) {
var oldState = this._state,
changed = {},
dirty = false;
for (var key in newState) {
if (differsImmutable(newState[key], oldState[key])) changed[key] = dirty = true;
}
if (!dirty) return;
this._state = assign({}, oldState, newState);
this._recompute(changed, this._state);
if (this._bind) this._bind(changed, this._state);
if (this._fragment) {
dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
this._fragment.p(changed, this._state);
dispatchObservers(this, this._observers.post, changed, this._state, oldState);
}
}
export function setDev(newState) {
if (typeof newState !== 'object') {
throw new Error(

@ -2,12 +2,13 @@ import {
assign,
blankObject,
differs,
differsImmutable,
dispatchObservers,
get,
observe
} from './shared.js';
function Store(state) {
function Store(state, options) {
this._observers = { pre: blankObject(), post: blankObject() };
this._changeHandlers = [];
this._dependents = [];
@ -16,6 +17,7 @@ function Store(state) {
this._sortedComputedProperties = [];
this._state = assign({}, state);
this._differs = options && options.immutable ? differsImmutable : differs;
}
assign(Store.prototype, {
@ -88,7 +90,7 @@ assign(Store.prototype, {
if (dirty) {
var newValue = fn.apply(null, values);
if (differs(newValue, value)) {
if (store._differs(newValue, value)) {
value = newValue;
changed[key] = true;
state[key] = value;
@ -124,7 +126,7 @@ assign(Store.prototype, {
for (var key in newState) {
if (this._computed[key]) throw new Error("'" + key + "' is a read-only property");
if (differs(newState[key], oldState[key])) changed[key] = dirty = true;
if (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;
}
if (!dirty) return;

@ -52,7 +52,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -28,7 +28,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -28,7 +28,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -48,7 +48,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -40,7 +40,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -28,7 +28,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -48,7 +48,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -48,7 +48,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -60,7 +60,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -40,7 +40,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -40,7 +40,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -44,7 +44,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -48,7 +48,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -46,7 +46,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -63,7 +63,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -56,7 +56,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -42,7 +42,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -28,7 +28,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -28,7 +28,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -48,7 +48,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -28,7 +28,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -52,7 +52,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

@ -48,7 +48,14 @@ function destroy(detach) {
}
function differs(a, b) {
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
if (a == null || b == null) return a !== b;
if (a.constructor !== b.constructor) return true;
if (a.valueOf && b.valueOf) {
a = a.valueOf();
b = b.valueOf();
}
if (typeof a === 'number' && isNaN(a) && isNaN(b)) return false;
return a !== b || typeof a === 'object' || typeof a === 'function';
}
function dispatchObservers(component, group, changed, newState, oldState) {

Loading…
Cancel
Save