This is the 8th day of my participation in the August Text Challenge.More challenges in August
preface
Solid is an unpopular Web framework. Similar to Vue, React, and Svelte. It has the following characteristics:
- DSL using React Hooks
- Higher execution efficiency than other frameworks (Vue, React…) Because the native operation of pre-compiling code into the DOM skips the diff part of modern Web frameworks
- Supports Typescript, SSR, and a number of modern Web must-have features
introduce
The focus of this article is on the responsive principle of Solid.
At the heart of Solid responsiveness are createSignal and createEffect. We can create reactive variables using createSignal and create effects to create functions that listen for reactive variables.
Let’s take a look at some code for Solid responsiveness.
const [count, setCount] = createSignal(0)
createEffect(() = > {
console.log('latest count is ', count())
})
setInterval(() = > {
setCount(count() + 1)},1000)
Copy the code
First, we pass in a base data type 0 through createSignal and return an array with the first two entries to get count and update count, respectively.
Then, with createEffect, a listener is created that is re-executed when the value of count changes.
How does Solid automatically execute the listener passed in createEffect when count updates?
Analysis of the
Let’s take a look at what createEffect does.
First, we can observe that the array members returned by createSignal are function types.
CreateEffect will definitely execute the function passed in. In addition, it will also cache the function.
function createEffect(fn) {
const lastListener = Listener // Cache listener function
Listener = fn // Set the current listener function
fn() // Execute the listener function
Listener = lastListener // Restore the listener function
}
Copy the code
As you can see, this code does two main things:
-
Caches the last listening function
-
Execute the current listener function
Count (), console.log(‘latest count is ‘) will be executed during the listener execution.
So let’s see what does count() do?
Since count is the first item in the array returned by createSignal, we exclude extraneous code and focus on the first item in the array returned by createSignal.
function createSignal(init) {
const getter = () = > {
if (Listener) {
// Create an association between 'current variable' and 'Listener'
}
return node.value
}
// ...
return [getter, ]
}
Copy the code
As you can see, each responsive variable created by createSignal is checked for the presence of a Listener when the getter is fired, and if so, the responsive variable is associated with the Listener.
So you might be thinking, well, how does the init value that’s passed in, which is the basic data type 0, relate to the Listener?
Looking at the Solid source, we can see that Solid stores both the initial value and the Listener in the same object and maintains this object throughout.
function createSignal(init) {
+ const node = {
+ value: init,
+ Listeners: []
+}Const getter = () => {if (Listener) {// Creates an association between 'current variable' and 'Listener'+ node.Listeners.push(Listener)
}
return node.value
}
// ...
return [getter, ]
}
Copy the code
Note That node.listeners are an array type of Listeners created by creating a Node object, because multiple Listeners may listen for the same responsive variable.
So the next question is, what happens when you update a variable?
As you can probably guess, any Listeners that have been cached before should be notified of the changes, traversed and executed sequentially.
We continued to refine the createSignal code.
function createSignal(init) {
const node = {
value: init,
Listeners: []
}
const getter = () => {
if (Effect) {
node.Listeners.push(Listener)
}
return node.value
}
+ const setter = (newValue) => {
+ node.value = newValue
+ node.Listeners.forEach(fn => fn())
+}
return [getter, setter]
}
Copy the code
At this point, the reactive principle of Solid has been introduced.
Now, let’s put the above code together:
let Listener = null
function createSignal(init) {
const node = {
value: init,
Listeners: []}const getter = () = > {
if (Listener) {
node.Listeners.push(Listener)
}
return node.value
}
const setter = (newValue) = > {
node.value = newValue
node.Listeners.forEach(fn= > fn())
}
return [getter, setter]
}
function createEffect(fn) {
Listener = fn
fn()
Listener = null
}
// Test the code
const [count, setCount] = createSignal(0)
createEffect(() = > {
console.log('latest count is ', count())
})
setInterval(() = > {
setCount(count() + 1)},1000)
Copy the code
Paste the code above into the Chrome console and you can see the browser’s output:
Finally, let’s comb through the implementation process:
—- initially —->
-
Perform createEffect
-
Set Listener to the Listener function passed in for createEffect
-
To perform the count ()
-
Trigger the getter for count
-
Bind the count value and Listener
-
Reduction of the Listener
—- update count —->
-
Fire the setter for count
-
Execute the Listener of count
conclusion
In Solid, the reactive core is createSignal. It does this by creating a Node object, storing variable values, Listener functions, and then storing Listener functions in the getter of the variable and firing Listener functions in the setter.
We know that Vue also has reactive updates, but unlike Vue, the getter for createSignal is triggered by a function call, and we can bind listener functions inside the getter. Vue can directly trigger getters in a way like obj.name, so Vue uses Object.defineProperty or new Proxy to listen for getters. But other than that, their implementations are pretty much the same.