mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Update docs
This commit is contained in:
parent
3959f5119e
commit
9b6be14441
14 changed files with 308 additions and 60 deletions
|
|
@ -204,22 +204,23 @@ The above code produces a graph like so:
|
|||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#fff"
|
||||
"tertiaryBorderColor": "#1B1B1F"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart LR
|
||||
subgraph root
|
||||
direction LR
|
||||
show
|
||||
|
||||
subgraph subroot[show subroot]
|
||||
subgraph subroot["show() subroot"]
|
||||
p1[prop binding]
|
||||
end
|
||||
end
|
||||
|
||||
visible --> show
|
||||
count --> p1
|
||||
show .-> subroot
|
||||
show -.- subroot
|
||||
```
|
||||
|
||||
This can be recreated without the `show()` control-flow function, with the
|
||||
|
|
|
|||
|
|
@ -15,11 +15,10 @@ worrying about manually updating UI instances.
|
|||
|
||||
Some of the main focuses behind Vide's design choices:
|
||||
|
||||
- Concise syntax to reduce verbosity as much as possible.
|
||||
- Concise syntax.
|
||||
- Being completely typecheckable.
|
||||
- Independence from instance lifetimes.
|
||||
- A powerful reactive system that can update specific properties as a result of
|
||||
state changes, updates are immediate with no diffing needed.
|
||||
- Real reactivity.
|
||||
|
||||
## Structure Of A Vide App
|
||||
|
||||
|
|
@ -33,8 +32,10 @@ called *components*.
|
|||
|
||||
```lua
|
||||
local function App()
|
||||
return create "ScreenGui" {
|
||||
create "TextLabel" { Text = "hi" }
|
||||
return {
|
||||
PlayerStats(),
|
||||
Inventory(),
|
||||
Settings()
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,30 @@ when the timer component is destroyed, whether that is from unmounting the app
|
|||
or if it is dynamically created by a control-flow function, which will be
|
||||
covered next.
|
||||
|
||||
On a related note: the reason why `mount()` is used to create your app, is so
|
||||
that any top-level components that need to be cleaned up, can be cleaned up
|
||||
when the app is later unmounted, since `mount()` runs in a reactive scope to
|
||||
track `cleanup()` calls. Vide's entire reactive system is independent from the
|
||||
life-time of instances; instances are just a side-effect of the reactive system.
|
||||
This is another reason why `mount()` is used at the top level of your app, so
|
||||
that any registered cleanups created by your app components can be ran when
|
||||
they are destroyed.
|
||||
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1F",
|
||||
"primaryTextColor": "#fff",
|
||||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#161618"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart
|
||||
|
||||
subgraph root
|
||||
direction LR
|
||||
cleanup([cleanup]) ~~~ count
|
||||
count --> bind[text binding]
|
||||
end
|
||||
```
|
||||
|
|
@ -63,6 +63,37 @@ local function JoinMenu()
|
|||
end
|
||||
```
|
||||
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1F",
|
||||
"primaryTextColor": "#fff",
|
||||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#1C1C1F"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart
|
||||
|
||||
subgraph root ["mount() scope"]
|
||||
direction LR
|
||||
joined --> show -.- subroot
|
||||
|
||||
subgraph subroot ["show() scope"]
|
||||
direction LR
|
||||
Button
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The dotted line indicates that the new reactive scope isn't actually connected
|
||||
to the `show` on the graph, it is only managed internally through code.
|
||||
|
||||
## switch()
|
||||
|
||||
Similar to `show()`, `switch()`, also condtionally displays one instance at a
|
||||
|
|
@ -113,6 +144,34 @@ switch(menu) {
|
|||
}
|
||||
```
|
||||
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1F",
|
||||
"primaryTextColor": "#fff",
|
||||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#1C1C1F"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart
|
||||
|
||||
subgraph root ["mount() scope"]
|
||||
direction LR
|
||||
joined --> show -.- subroot
|
||||
|
||||
subgraph subroot ["switch() scope"]
|
||||
direction LR
|
||||
Button
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## indexes()
|
||||
|
||||
Often, you will have a table of values that will be displayed in a similar
|
||||
|
|
@ -121,7 +180,7 @@ UI element, `indexes()` allows you to create an instance for each table index,
|
|||
to display the value at that index.
|
||||
|
||||
```lua
|
||||
local todoList = {
|
||||
local todoList = source {
|
||||
"finish the crash course",
|
||||
"star vide's GitHub"
|
||||
}
|
||||
|
|
@ -153,5 +212,38 @@ given source for that index is updated.
|
|||
|
||||
An element is only destroyed if the value of an index is set to `nil`.
|
||||
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1F",
|
||||
"primaryTextColor": "#fff",
|
||||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#1C1C1F"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart
|
||||
|
||||
subgraph root ["mount() scope"]
|
||||
direction LR
|
||||
todoList --> indexes -.- subroot1 & subroot2
|
||||
|
||||
subgraph subroot1 ["indexes() scope 1"]
|
||||
direction LR
|
||||
value1[todo] --> prop1["prop binding"]
|
||||
end
|
||||
|
||||
subgraph subroot2 ["indexes() scope 2"]
|
||||
direction LR
|
||||
value2[todo] --> prop2[prop binding]
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Together, these control flow functions cover the majority of cases where you
|
||||
need to dynamically create and destroy parts of your UI.
|
||||
|
|
@ -24,6 +24,9 @@ count(count() + 1) -- increment count by 1
|
|||
Sources can be *derived* by wrapping them in functions. A wrapped source
|
||||
effectively becomes a new source.
|
||||
|
||||
Derived sources should be pure functions. This is where the same output is
|
||||
always produced for the same input no matter how many times it is reran.
|
||||
|
||||
```lua
|
||||
local count = source(0)
|
||||
|
||||
|
|
@ -36,7 +39,8 @@ count(1)
|
|||
print(text()) -- "count: 1"
|
||||
```
|
||||
|
||||
Derived sources should be pure functions. This is where the same output is
|
||||
always produced for the same input. As well as making source updates more
|
||||
predictable, knowing that updates are pure allows Vide to use optimizations such
|
||||
as caching, to avoid updating derived sources if their inputs are the same.
|
||||
Sources on their own aren't very special, the above can be achieved with plain
|
||||
variables. The real use for sources become apparent when used in combination
|
||||
with Vide's *reactive scopes*. When a source is read from within a reactive
|
||||
scope, it can automatically rerun the scope that reads it when the source is
|
||||
updated in the future.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Effects
|
||||
|
||||
Effects are functions that are ran in response to source updates. They are
|
||||
called effects because they cause *side-effects* when reacting to source updates
|
||||
which are pure.
|
||||
called effects because they cause *side-effects* when reacting to source
|
||||
updates.
|
||||
|
||||
Effects are created using `effect()`.
|
||||
|
||||
|
|
@ -26,10 +26,10 @@ from inside a reactive scope will be tracked, so that if any of those sources
|
|||
update, the effect will be reran too.
|
||||
|
||||
The callback is first ran immediately inside the `effect()` call to initially
|
||||
figure out what sources are being used.
|
||||
track sources used.
|
||||
|
||||
Effects also work with derived sources, it doesn't matter how deeply nested a
|
||||
source is.
|
||||
Effects also work with derived sources, it doesn't matter how deeply nested
|
||||
inside a function a source is.
|
||||
|
||||
```lua
|
||||
local source = vide.source
|
||||
|
|
@ -50,15 +50,7 @@ count(2)
|
|||
-- "doubled count: 4" printed
|
||||
```
|
||||
|
||||
Derived sources should be a *pure computation*. A pure computation is one where
|
||||
the same input will always produce the same output.
|
||||
|
||||
All observable changes to the user are considered to be side-effects of pure
|
||||
computations.
|
||||
|
||||
Sources, derived sources, and effects form what is called a *reactive graph*.
|
||||
In the above example, the following graph is formed. Anywhere
|
||||
an update occures, everything further down the graph is updated.
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
|
|
@ -69,25 +61,12 @@ an update occures, everything further down the graph is updated.
|
|||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#161618"
|
||||
"tertiaryBorderColor": "#fff"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart LR
|
||||
count --> doubled --> effect
|
||||
|
||||
count --> effect
|
||||
|
||||
```
|
||||
|
||||
You should not update other sources using an effect. Improper usage can lead to
|
||||
a cyclic loop in the graph, causing an infinite loop when it tries to update.
|
||||
Sources should be derived instead.
|
||||
|
||||
## Root Reactive Scopes
|
||||
|
||||
Effects must be created within another reactive scope. This is so that the
|
||||
effect itself can be tracked and later freed when the parent reactive scope is
|
||||
destroyed, such as from unmounting an app. The example code above will not
|
||||
actually work unless it is ran inside a root reactive scope, such as one created
|
||||
by `vide.mount(function)`. This generally isn't a concern since you can assume
|
||||
that all your components will be created within a single `mount()` call, which
|
||||
happens only once at the top level, where you put together your UI and parent it
|
||||
to a ScreenGUI.
|
||||
|
|
|
|||
49
docs/tut/crash-course/6-root.md
Normal file
49
docs/tut/crash-course/6-root.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Root Reactive Scopes
|
||||
|
||||
Any reactive scopes created, such as one from `effect()`, must be done so within
|
||||
a "root" reactive scope. This is the main purpose of `mount()`, which you use
|
||||
once at the top level to create your app as shown in the first introduction.
|
||||
|
||||
This is so that when the app is unmounted, it can clean up any reactive scopes
|
||||
created within it, since reactive scopes track any reactive scopes created
|
||||
within them.
|
||||
|
||||
```lua
|
||||
local source = vide.source
|
||||
local effect = vide.effect
|
||||
|
||||
local function App()
|
||||
local count = source(0)
|
||||
|
||||
effect(function()
|
||||
print(count())
|
||||
end)
|
||||
end
|
||||
|
||||
vide.mount(App) -- works!
|
||||
|
||||
App() -- will error since effect() was not called within a reactive scope
|
||||
```
|
||||
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1F",
|
||||
"primaryTextColor": "#fff",
|
||||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#161618"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart
|
||||
|
||||
subgraph root
|
||||
direction LR
|
||||
count --> effect
|
||||
end
|
||||
```
|
||||
|
|
@ -30,6 +30,9 @@ end
|
|||
Above is an example of a counter component, that when clicked, will increment
|
||||
its internal count, and automatically update its text to reflect that count.
|
||||
|
||||
Making a property update based on a source is also called *property
|
||||
binding*.
|
||||
|
||||
Each instance of `Counter()` will maintain its own independent count, since the
|
||||
count source is created inside the scope of the component.
|
||||
|
||||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
Explicitly creating effects to update properties can become verbose when there
|
||||
are a lot of properties to update. Vide provides a way to *implicitly* create
|
||||
an effect to update properties on source update. This is also known as
|
||||
*property binding*, since changes to a source will automatically update the
|
||||
property.
|
||||
an effect to update properties on source update.
|
||||
|
||||
```lua
|
||||
local create = vide.create
|
||||
|
|
@ -40,7 +38,9 @@ that are updated.
|
|||
|
||||
## Children Binding
|
||||
|
||||
Children can also be set in a similar manner.
|
||||
Children can also be set in a similar manner. Sources bound to properties can
|
||||
return an instance or an array of instances. Vide will automatically unparent
|
||||
removed instances and parent new instances.
|
||||
|
||||
```lua
|
||||
local items = source {
|
||||
95
docs/tut/crash-course/9-derived-source.md
Normal file
95
docs/tut/crash-course/9-derived-source.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Derived Sources
|
||||
|
||||
We have seen the basic way to derive a source:
|
||||
|
||||
```lua
|
||||
local count = source(0)
|
||||
|
||||
local text = function()
|
||||
return "count: " .. tostring(count())
|
||||
end
|
||||
|
||||
print(text()) -- "count: 0"
|
||||
count(1)
|
||||
print(text()) -- "count: 1"
|
||||
```
|
||||
|
||||
However, in some cases where this source could be used by multiple effects at
|
||||
the same time, the function wrapping the source will needlessly rerun to convert
|
||||
the count into a string for each effect using it.
|
||||
|
||||
```lua
|
||||
local source = vide.source
|
||||
local effect = vide.effect
|
||||
|
||||
local count = source(0)
|
||||
|
||||
local text = function()
|
||||
print "ran"
|
||||
return "count: " .. tostring(count())
|
||||
end
|
||||
|
||||
effect(function()
|
||||
text() -- prints "ran"
|
||||
end)
|
||||
|
||||
effect(function()
|
||||
text() -- prints "ran" again
|
||||
end)
|
||||
```
|
||||
|
||||
To avoid this, you can use `derive()` to derive a new source instead. This will
|
||||
run a callback in a new reactive scope only when a dependent source has updated.
|
||||
Reading this derived source multiple times will just return a cached result from
|
||||
when it last updated.
|
||||
|
||||
```lua
|
||||
local source = vide.source
|
||||
local derive = vide.derive
|
||||
local effect = vide.effect
|
||||
|
||||
local count = source(0)
|
||||
|
||||
local text = derive(function()
|
||||
print "ran"
|
||||
return "count: " .. tostring(count())
|
||||
end)
|
||||
|
||||
effect(function()
|
||||
text() -- prints "ran"
|
||||
end)
|
||||
|
||||
effect(function()
|
||||
text() -- does not print
|
||||
end)
|
||||
```
|
||||
|
||||
Deriving a source in this manner is similar to creating an effect to update
|
||||
another source. You should never manually do this using an effect however,
|
||||
improper usage could accidently create infinite loops in the reactive graph.
|
||||
Always favour deriving when you need one source to update based on another.
|
||||
|
||||
`derive()` must also be used within a root reactive scope, just like `effect()`.
|
||||
|
||||
The reactive graph for the above example:
|
||||
|
||||
```mermaid
|
||||
%%{init: {
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1F",
|
||||
"primaryTextColor": "#fff",
|
||||
"primaryBorderColor": "#1B1B1F",
|
||||
"lineColor": "#79B8FF",
|
||||
"tertiaryColor": "#161618",
|
||||
"tertiaryBorderColor": "#161618"
|
||||
}
|
||||
}}%%
|
||||
|
||||
flowchart
|
||||
|
||||
subgraph root
|
||||
direction LR
|
||||
count --> text --> effect1 & effect2
|
||||
end
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue