Lit is a great library but its js api is kinda messed up. I made a version of lit that is optimized to build things rapidly without ts.
- tag name is automatically set to the class name in kebab case
- global state with simple syntax and automatic subscription
- declare reactive properties and set defaults in the same place
- (no shadow dom by default (just add
static shadow = true
))
let's look at the syntax it gives us
import { VLitElement, html, init } from "./vlit.js"
// global state
const state = init({
s: 2,
})
class MyMain extends VLitElement {
r(state) {
return html`
<my-pair></my-pair>
<my-pair></my-pair>
<my-pair></my-pair>
`
}
}
MyMain.done
class MyPair extends VLitElement {
static props = () => ({ k: 0 })
r(state) {
return html`
<button @click=${e => this.k++}>prop ${this.k}</button>
<button @click=${e => state.s++}>state ${state.s}</button> <br />
`
}
}
MyPair.done
probable faqs:
-
why
r
and notrender
?render
method will return a call ofr
method but with thestate
argument which is used for global state. -
where are Property options?
they're still there instatic properties
but you can also write it withprops
. it defines a default reactive properties with{}
but if if you have options already inproperties
they won't be overwritten. -
what is the
state
argument? and whyr
?
the argument is a proxy which will give you whatever you would got from the real state (which can be used by importing. alsoinit
sets and returns to this state) but it will record whatever you used inrender
. here we usestate.s
from the argument so it knows that this component uses the states
. but if we'd just importstate
and use it then the result would be correct but changing the state wouldn't refresh the component. -
I want my shadow back
static shadow = true
-
why the
.done
tho?
this getter will define the dynamic properties fromprops
and define the tag. so you must.done
after defining a class.(also makes the class observe the old way manually defined state in
props
) (currently no way to pass other arguments tocustomElements.define()
)
VLitElement
is also exported as V
and html
is exported as v
if you're
a lazy typer or don't want a 4 letter prefix when using in template literals
the older version of vlit used manual subscription for state. if you need to
define render
rather than the wrapper r
, it's possible this way or get the
automatic subscribing state in render simply by this.state
or
const { state } = this
.
import { VLitElement, html, state } from "./vlit.js"
class MyPair extends VLitElement {
static props = ({
s, // observes global state
}) => ({
k: 0, // reactive property
})
render() {
return html`
<button @click=${e => this.k++}>prop ${this.k}</button>
<button @click=${e => state.s++}>state ${state.s}</button> <br />
`
}
}
MyPair.done
-
how does this
({ s }) => ({ k: 0 })
weird syntax work?
the one before arrow is argument deconstruction. a proxy is given as an argument and this way you are deconstructing the propertys
from it. if we'd write it with a traditional arguments that'd be like:proxy => { let s = proxy.s return { k: 0 } }
or we also could use
let { s } = proxy
. the point here is to signal that this element usess
from state. the most straightforward way would be an array of strings["s"]
but we can use shiny JavaScript features to make it a bit more appealing and easy to write and read. the return object's properties will be assigned to the element one-by-one so you don't have to write the properties in the constructor again.- should I deconstruct what properties of state the element observes?
no. if the parent element hass
then all its inherited classes will update whenstate.s
is set. but for the default properties yes you should. the best way is to make another function and use .
- should I deconstruct what properties of state the element observes?
I know it's a bit disgusting to see deconstructed properties from an argument that aren't gonna be used but I just didn't want to write observing state property names just in strings. Indeed it does look elegant to me - being before the default property values and it has no default cuz uses the global. also it's recommended to use first way with automatic subscription.