You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
</code></pre><p><ahref="https://jsfiddle.net/gh/get/library/pure/vanjs-org/vanjs-org.github.io/tree/master/jsfiddle/advanced/readonly-prop">Try on jsfiddle</a></p><p><b>NOTE:</b> for <b>Mini-Van</b>, since <codeclass="symbol">0.4.0</code>, we consistently assign the <codeclass="symbol">props</code> values via <codeclass="symbol">setAttribute</code> for all property keys in tag functions. This is because for SSR (server-side rendering), which is <b>Mini-Van</b>'s primary use case, setting the properties of a DOM node won't be visible in the rendered HTML string unless the action of setting the property itself will also set the corresponding HTML attribute (e.g.: setting the <codeclass="symbol">id</code> property of a DOM node will also set the <codeclass="symbol">id</code> attribute). This is helpful as <codeclass="language-js">input({type: "text", value: "value"})</code> can be rendered as <codeclass="language-html"><input type="text" value="value"></code> in <b>Mini-Van</b> but would be rendered as <codeclass="language-html"><input type="text"></code> if we set the property value via DOM property.</p><h2class="w3-xxlarge w3-text-red" id="state-and-state-binding"><aclass="self-link" href="#state-and-state-binding">State and State Binding</a></h2><hrstyle="width:50px;border:5px solid red" class="w3-round"><h3class="w3-large w3-text-red" id="why-not-dom-valued-states"><aclass="self-link" href="#why-not-dom-valued-states">Why can't states have DOM node as values?</a></h3><p>We might be prompted to assign a DOM node to a <codeclass="symbol">State</code> object, especially when the <codeclass="symbol">State</code> object is used as a <codeclass="symbol">State</code>-typed child node. However, this is problematic when the state is bound in multiple places, like the example below:</p><pre><codeclass="language-js">const {b, button, span} = van.tags
70
-
71
-
const TurnBold = () => {
69
+
</code></pre><p><ahref="https://jsfiddle.net/gh/get/library/pure/vanjs-org/vanjs-org.github.io/tree/master/jsfiddle/advanced/readonly-prop">Try on jsfiddle</a></p><p><b>NOTE:</b> for <b>Mini-Van</b>, since <codeclass="symbol">0.4.0</code>, we consistently assign the <codeclass="symbol">props</code> values via <codeclass="symbol">setAttribute</code> for all property keys in tag functions. This is because for SSR (server-side rendering), which is <b>Mini-Van</b>'s primary use case, setting the properties of a DOM node won't be visible in the rendered HTML string unless the action of setting the property itself will also set the corresponding HTML attribute (e.g.: setting the <codeclass="symbol">id</code> property of a DOM node will also set the <codeclass="symbol">id</code> attribute). This is helpful as <codeclass="language-js">input({type: "text", value: "value"})</code> can be rendered as <codeclass="language-html"><input type="text" value="value"></code> in <b>Mini-Van</b> but would be rendered as <codeclass="language-html"><input type="text"></code> if we set the property value via DOM property.</p><h2class="w3-xxlarge w3-text-red" id="state-and-state-binding"><aclass="self-link" href="#state-and-state-binding">State and State Binding</a></h2><hrstyle="width:50px;border:5px solid red" class="w3-round"><h3class="w3-large w3-text-red" id="why-not-dom-valued-states"><aclass="self-link" href="#why-not-dom-valued-states">Why can't states have DOM node as values?</a></h3><p>We might be prompted to assign a DOM node to a <codeclass="symbol">State</code> object, especially when the <codeclass="symbol">State</code> object is used as a <codeclass="symbol">State</code>-typed child node. However, this is problematic when the state is bound in multiple places, like the example below:</p><pre><codeclass="language-js">const TurnBold = () => {
" Welcome to ", vanJS, ". ", vanJS, " is awesome!"
76
74
)
77
75
}
78
-
79
-
van.add(document.body, TurnBold())
80
76
</code></pre><p><b>Demo:</b><spanid="demo-dom-valued-state"></span></p><p><ahref="https://jsfiddle.net/gh/get/library/pure/vanjs-org/vanjs-org.github.io/tree/master/jsfiddle/advanced/dom-valued-state">Try on jsfiddle</a></p><p>In this example, if we click the "Turn Bold" button, the first "<b>VanJS</b>" text will disappear, which is unexpected. This is because the same DOM node <codeclass="language-js">b("VanJS")</code> is used twice in the DOM tree. For this reason, an error will be thrown in <codeclass="symbol">van-{version}.debug.js</code> whenever we assign a DOM node to a <codeclass="symbol">State</code> object.</p><h3class="w3-large w3-text-red" id="state-granularity"><aclass="self-link" href="#state-granularity">State granularity</a></h3><p>Whenever possible, it's encouraged to define states at a more granular level. That is, it's recommended to define states like this:</p><pre><codeclass="language-js">const appState = {
const text = van.derive(() => `${prefix.val} - ${suffix.val}`)
220
216
return (renderPre.val ? pre : span)(text)
221
217
})
222
-
</code></pre><p>whenever <codeclass="symbol">renderPre</code> is toggled, a new <codeclass="symbol">text</code> state will be created and subscribe to changes of the <codeclass="symbol">prefix</code> state. However, the derivation from <codeclass="symbol">prefix</code> to the previous <codeclass="symbol">text</code> state will be garbage collected as the derivation was created while executing a binding function whose result DOM node no longer connects to the document tree. This is the mechanism to avoid memory leaks caused by state derivations that hold onto memory indefinitely.</p><p><ahref="/code/gc-derive" class="w3-hover-opacity">Try out the example here</a> (You can use developer console to watch <codeclass="symbol">prefix</code>'s <codeclass="symbol">_listeners</code>).</p></div>
223
-
<asideid="toc"><ul><li><ahref="#dom-attributes-vs-properties" class="w3-hover-opacity">DOM Attributes vs. Properties</a></li><li><ahref="#state-and-state-binding" class="w3-hover-opacity">State and State Binding</a><ul><li><ahref="#why-not-dom-valued-states" class="w3-hover-opacity">Why can't states have DOM node as values?</a></li><li><ahref="#state-granularity" class="w3-hover-opacity">State granularity</a></li><li><ahref="#minimize-the-scope-of-dom-updates" class="w3-hover-opacity">Minimize the scope of DOM updates</a></li><li><ahref="#advanced-state-derivation" class="w3-hover-opacity">Advanced state derivation</a></li><li><ahref="#conditional-state-binding" class="w3-hover-opacity">Conditional state binding</a></li><li><ahref="#self-referencing-in-side-effects" class="w3-hover-opacity">Self-referencing in side effects</a></li></ul></li><li><ahref="#gc" class="w3-hover-opacity">Garbage Collection</a><ul><li><ahref="#avoid-your-bindings-to-be-gc-ed-unexpectedly" class="w3-hover-opacity">Avoid your bindings to be GC-ed unexpectedly</a></li><li><ahref="#derived-states-and-side-effects-registered-inside-a-binding-function" class="w3-hover-opacity">Derived states and side effects registered inside a binding function</a></li></ul></li></ul></aside>
218
+
</code></pre><p>whenever <codeclass="symbol">renderPre</code> is toggled, a new <codeclass="symbol">text</code> state will be created and subscribe to changes of the <codeclass="symbol">prefix</code> state. However, the derivation from <codeclass="symbol">prefix</code> to the previous <codeclass="symbol">text</code> state will be garbage collected as the derivation was created while executing a binding function whose result DOM node no longer connects to the document tree. This is the mechanism to avoid memory leaks caused by state derivations that hold onto memory indefinitely.</p><p><ahref="/code/gc-derive" class="w3-hover-opacity">Try out the example here</a> (You can use developer console to watch <codeclass="symbol">prefix</code>'s <codeclass="symbol">_listeners</code>).</p><h2class="w3-xxlarge w3-text-red" id="lifecycle-hooks"><aclass="self-link" href="#lifecycle-hooks">Lifecycle Hooks</a></h2><hrstyle="width:50px;border:5px solid red" class="w3-round"><p>To keep <b>VanJS</b>'s simplicity, there isn't a direct support of lifecycle hooks in <b>VanJS</b> API. That being said, there are multiple ways to inject custom code upon lifecycle events (mount/unmount) of DOM elements.</p><h3class="w3-large w3-text-red" id="using-settimeout"><aclass="self-link" href="#using-settimeout">Using <codeclass="symbol">setTimeout</code></a></h3><p>A quick and dirty way to inject custom code upon a DOM element is mounted is to use <codeclass="symbol">setTimeout</code> with a small <codeclass="symbol">delay</code>. Since the rendering cycle starts right after the current thread of execution (internally, the rendering cycle is rescheduled via <codeclass="symbol">setTimeout</code> with a <codeclass="symbol">0</code><codeclass="symbol">delay</code>), the custom code injected via <codeclass="symbol">setTimeout</code> with a small <codeclass="symbol">delay</code> is guaranteed to be executed right after the upcoming rendering cycle, which ensures its execution upon the DOM element being mounted. This simple technique is used in a few places of the official <b>VanJS</b> codebase (in the website and demos), e.g.: <ahref="https://github.com/vanjs-org/vanjs-org.github.io/blob/e149ba503bf2da80d3023bb314e7fab57edbfa17/code/code-browser/src/main.js#L39" class="w3-hover-opacity">1</a>, <ahref="https://github.com/vanjs-org/vanjs-org.github.io/blob/e149ba503bf2da80d3023bb314e7fab57edbfa17/converter-ui/convert.ts#L81" class="w3-hover-opacity">2</a>.</p><h3class="w3-large w3-text-red" id="registering-a-side-effect-via-van-derive"><aclass="self-link" href="#registering-a-side-effect-via-van-derive">Registering a side effect via <codeclass="symbol">van.derive</code></a></h3><p><i>Requires <b>VanJS</b> 1.5.0 or later.</i></p><p>If you want to get rid of <codeclass="symbol">setTimeout</code> (thus the small <codeclass="symbol">delay</code> introduced by it). You can leverage the technique of registering a side effect via <codeclass="symbol"><ahref="/tutorial#api-derive" class="w3-hover-opacity">van.derive</a></code>, as demonstrated in the code below:</p><pre><codeclass="language-js">const Label = ({text, onmount}) => {
</code></pre><p><b>Demo:</b></p><pid="demo-onmount"></p><p><ahref="https://jsfiddle.net/gh/get/library/pure/vanjs-org/vanjs-org.github.io/tree/master/jsfiddle/advanced/onmount">Try on jsfiddle</a></p><p>This technique works because in <b>VanJS</b> 1.5.0 or later, derived states and side effects caused by state changes are scheduled asynchronously right in the next rendering cycle. Thus the side effects caused by the state changes of the current rendering cycle are guaranteed to be executed right after the completion of the current rendering cycle.</p><h3class="w3-large w3-text-red" id="register-connectedcallback-and-disconnectedcallback-of-custom-elements"><aclass="self-link" href="#register-connectedcallback-and-disconnectedcallback-of-custom-elements">Register <codeclass="symbol">connectedCallback</code> and <codeclass="symbol">disconnectedCallback</code> of custom elements</a></h3><p>Another option is to leverage the <codeclass="symbol">connectedCallback</code> and <codeclass="symbol">disconnectedCallback</code> of custom elements in <ahref="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" class="w3-hover-opacity">Web Components</a>. This is the only option to reliably inject custom code upon the unmount events of DOM elements. Note that in <b>VanJS</b>'s add-on: <ahref="https://van-element.pages.dev/" class="w3-hover-opacity">van_element</a>, you can easily <ahref="https://van-element.pages.dev/learn/lifecycle.html" class="w3-hover-opacity">register mount/unmount handlers</a> with the help of the add-on.</p></div>
240
+
<asideid="toc"><ul><li><ahref="#dom-attributes-vs-properties" class="w3-hover-opacity">DOM Attributes vs. Properties</a></li><li><ahref="#state-and-state-binding" class="w3-hover-opacity">State and State Binding</a><ul><li><ahref="#why-not-dom-valued-states" class="w3-hover-opacity">Why can't states have DOM node as values?</a></li><li><ahref="#state-granularity" class="w3-hover-opacity">State granularity</a></li><li><ahref="#minimize-the-scope-of-dom-updates" class="w3-hover-opacity">Minimize the scope of DOM updates</a></li><li><ahref="#advanced-state-derivation" class="w3-hover-opacity">Advanced state derivation</a></li><li><ahref="#conditional-state-binding" class="w3-hover-opacity">Conditional state binding</a></li><li><ahref="#self-referencing-in-side-effects" class="w3-hover-opacity">Self-referencing in side effects</a></li></ul></li><li><ahref="#gc" class="w3-hover-opacity">Garbage Collection</a><ul><li><ahref="#avoid-your-bindings-to-be-gc-ed-unexpectedly" class="w3-hover-opacity">Avoid your bindings to be GC-ed unexpectedly</a></li><li><ahref="#derived-states-and-side-effects-registered-inside-a-binding-function" class="w3-hover-opacity">Derived states and side effects registered inside a binding function</a></li></ul></li><li><ahref="#lifecycle-hooks" class="w3-hover-opacity">Lifecycle Hooks</a><ul><li><ahref="#using-settimeout" class="w3-hover-opacity">Using setTimeout</a></li><li><ahref="#registering-a-side-effect-via-van-derive" class="w3-hover-opacity">Registering a side effect via van.derive</a></li><li><ahref="#register-connectedcallback-and-disconnectedcallback-of-custom-elements" class="w3-hover-opacity">Register connectedCallback and disconnectedCallback of custom elements</a></li></ul></li></ul></aside>
0 commit comments