Ivi Versions Save

Lighweight Embeddable Web UI Library

v3.0.2

11 months ago

v3.0.0

1 year ago
  • Server-Side Rendering and Client-Side Hydration
  • Reduced public API surface
  • New HTML-like template language

v2.0.0

1 year ago

v2.0.0-alpha.0

1 year ago
  • Removed all experimental features.
  • Significantly reduced API, so it can be easily stabilized.
  • New data structures and algorithms for UI values.

v1.0.1

4 years ago

Bug Fixes

  • Fixed getDOMNode() returning null value when first non-null node in a fragment or TrackByKey operation doesn't have any DOM nodes.
  • Fixed event dispatching algorithm visiting more nodes than it is necessary.

v1.0.0

4 years ago

Components

Added second prop to components.

const Button = statelessComponent<{ id: string }, Op>((props, children) => (
  button("button", props, children)
));

Button({ id: "button-id" },
  "Click Me",
);

Custom areEqual function

const View = statelessComponent<number, [number, number]>(
  (a, b) => (
    a + b[0] + b[1]
  ),
  undefined,
  shallowEqualArray,
);
View(1, [2, 3]);

Dirty Checking / Observables

Dirty checking API were redesigned to improve support for use cases with coarse-grained observable graphs and mutable data structures.

New API for dirty checking is composable and can be used in stateless components.

  • useSelect() hook were removed.
  • Added pull-based observables.
  • Context is reimplemented with observables and it is now way much cheaper to dirty check.

Examples

Computed Values (lazy evaluation)
const a = observable(1);
const b = observable(2);
const sum = computed(() => watch(a) + watch(b));
const A = statelessComponent(() => div(_, _, watch(sum)()));
Basic selectors with immutable state
const STATE = { value: 1 };
const A = component((c) => {
  const getValue = selector(() => STATE.value);
  return () => div(_, _, watch(getValue)());
});
Memoized selector with immutable state
const STATE = { a: 1, b: 2 };
const A = component((c) => {
  const getValue = selector((prev) => (
    prev !== void 0 && prev.a === STATE.a && prev.b === STATE.b ? prev :
      { a: STATE.a, b: STATE.b, result: STATE.a + STATE.b };
  ));
  return () => div(_, _, watch(getValue)());
});
Composition
const a = observable(1);
const A = component((c) => {
  const getValue = memo((i) => computed(() => watch(a) + i));
  return (i) => div(_, _, watch(getValue(i))());
});

Boolean DOM Attribute Values

Removed automagic conversion from boolean values to empty string. Correct attribute values should be specified explicitly.

textContent="" Optimization

This optimization has a quite noticeable impact in popular benchmarks. But in real applications, use cases that would benefit from this optimization will work significantly faster by wrapping lists into a transient DOM node.

Deep State Tracking

Deep state tracking optimization were removed. It is one of those optimizations that improve performance in benchmarks, but make it worse in real applications.

This optimization worked by updating node state flags during stack unwinding. It saved information about node subtree, so we could skip dirty checking and unmounting for subtrees that didn't have any stateful components. In applications decomposed into small components there will be many stateful components used as leaf nodes, so instead of optimizing, it will make dirty checking and reconciliation algorithms slightly slower. Also, this optimization were adding a lot of complexity to the reconciliation algorithm.

Simplified track by key algorithm

Instead of returning LIS indices, nodes that are part of LIS are now marked in the input array.

Events

Stop Propagation

Synthetic event handlers do not propagate events anymore. To propagate events, event handler should return DispatchEvent.Propagate value.

Move Events

Removed touch/mouse/pointer move events. Move event handlers usually attached when down event is triggered. To make sure that we don't lose any move events, we can't wait until next frame is rerendered, so move event handlers should be attached with native DOM api.

Server Side Rendering

Removed. Not interested in supporting this feature.

v0.27.1

5 years ago

Deep state flags propagation algorithm were redesigned to merge flags only when going through fragments and TrackByKey nodes. New algorithm also fixes some edge cases when deep state flags were kept assigned even when subtree no longer had this state.

v0.27.0

5 years ago

Optimizations

Reduced memory consumption by operation nodes. All properties are now inlined into operation nodes and there are three different operation node shapes. Almost all callsites should stay in monomorphic state, except for the one that accessing operation type in the mount and update functions, it will be in polymorphic state and it is ok as long as it doesn't transition into megamorphic state.

Bug Fixes

  • Fixed nextNode assignment in a dirty checking algorithm when it is going through DOM elements.

v0.26.0

5 years ago

Optimizations

Reduced memory consumption in component hooks, refs and portals.

API that is used for this optimizations is now public. This API is using several objects: TASK_TOKEN, SELECT_TOKEN and UNMOUNT_TOKEN to identify who is invoking a callback function. Callbacks registered with scheduleMicrotask() will receive TASK_TOKEN as a first argument, hooks added with useUnmount() will receive UNMOUNT_TOKEN. SELECT_TOKEN is used internally to optimize useSelect().

Immediately executed useEffect()

useEffect() hook is now immediately executed when it is invoked in the internal component "update" function. It makes it fully deterministic to prevent unexpected bugs when useEffect() is used to listen some observable value that can generate an event before microtask with an effect is executed. When it is immediately executed, it will guarantee that we don't miss any events generated by an observable value.