All Projects → aclueless → spair

aclueless / spair

Licence: MPL-2.0 license
A small and fast frontend framework in Rust

Programming Languages

rust
11053 projects
shell
77523 projects

Projects that are alternatives of or similar to spair

The Elmish Book
A practical guide to building modern and reliable web applications in F# from first principles
Stars: ✭ 231 (+255.38%)
Mutual labels:  spa
react-redux-boilerplate
A React boilerplate based on Redux, React Router, styled components and Parcel
Stars: ✭ 62 (-4.62%)
Mutual labels:  spa
Blapy
jQuery plugin that helps you to create and manage ajax and single page web applications (SPA) with almost no javascript coding to do it
Stars: ✭ 30 (-53.85%)
Mutual labels:  spa
Blog Admin
blog-admin @react、@typescript、@apollographql
Stars: ✭ 239 (+267.69%)
Mutual labels:  spa
Vuetober
A webpack starting point for single page apps with October CMS and Vue
Stars: ✭ 251 (+286.15%)
Mutual labels:  spa
laravel-vue-spa-boilerplate
Laravel Vue Spa BoilerPlate
Stars: ✭ 36 (-44.62%)
Mutual labels:  spa
Auth0 Angular Samples
Auth0 Integration Samples for Angular 2+ Applications
Stars: ✭ 227 (+249.23%)
Mutual labels:  spa
dimigoin-front-v2
한국디지털미디어고등학교 인트라넷, 디미고인
Stars: ✭ 19 (-70.77%)
Mutual labels:  spa
Laravel Vue Spa
A Laravel-Vue SPA starter kit.
Stars: ✭ 2,889 (+4344.62%)
Mutual labels:  spa
dimigoin-front
Official Korea Digital Media Highschool Intranet Service, DIMIGOIN
Stars: ✭ 34 (-47.69%)
Mutual labels:  spa
Butterfly Server
The Everything is Real-Time C# Backend for Single Page Applications
Stars: ✭ 247 (+280%)
Mutual labels:  spa
Prism
Build frontend web apps with Ruby and WebAssembly
Stars: ✭ 251 (+286.15%)
Mutual labels:  spa
miso-tutorial-app
An example GHCJS + Miso single page application. 🍜
Stars: ✭ 45 (-30.77%)
Mutual labels:  spa
Productivity Frontend
Productivity Application - Kanban Style Productivity Management Application with Customizable Boards, Lists and Cards to Make You More Productive.
Stars: ✭ 234 (+260%)
Mutual labels:  spa
hacker-feud
💥 A single page web game made with Svelte.
Stars: ✭ 61 (-6.15%)
Mutual labels:  spa
Mmf Blog Vue2
mmf-blog vue2.0 (vue2, vue-router, vuex)
Stars: ✭ 232 (+256.92%)
Mutual labels:  spa
vue-auth-boilerplate
This is a simple Vue template/starter kit, scaffolded on vue-cli 3, with full Auth functions to Login & Register
Stars: ✭ 77 (+18.46%)
Mutual labels:  spa
elm-spa
Pure Elm library to easily build Single Page Applications
Stars: ✭ 37 (-43.08%)
Mutual labels:  spa
karax
Karax. Single page applications for Nim.
Stars: ✭ 932 (+1333.85%)
Mutual labels:  spa
project-acorn-ssr
A Vue.js SPA built around the WordPress REST API with Vuex, Vue Router, Axios and SSR.
Stars: ✭ 14 (-78.46%)
Mutual labels:  spa

Spair

Crates.io docs.rs Build

A small and fast frontend framework for Single Page Application in Rust.

This project is in its early stage, things are still missing.

Why not using Spair?

  • Routing is just at the minimum level. You have to implement it manually.
  • No support for SSR.
  • No event delegation (*)
  • No RSX (*).
    • Spair is verbose.
  • No community

(*) You can see these as PROS. If these features will ever get implemented, they will be put behind feature-flags.

Why using Spair?

  • Both small and fast.
  • Both vDOM-like and reactive-like, in the same framework.
    • Incremtental render (vDOM-like, but Spair doesn't re-create a new vDOM in every run.)
    • Queue render (reactive-like, but not using any kind of signals), just queue relevant pieces of code to render change on data change. (The current version of queue render may not very efficient because each fine-grained-render need to borrow the component state separately by it own.)
  • Component state is automatically available in every piece of the render code.
  • (Almost) no macro is required for constructing DOM.
    • But Spair is quite verbose because of this.
  • Routing (but just basic support).
  • svg
  • Missing things here and there...
    • Errr, this is definitely a why not, obviously. I just put this here to remind potential users not to surprise about missing things :D.
    • For example, Spair currently just implements a handful number of events.

Cargo features

You can enabled a feature in your Cargo.toml like this: spair = { version="x.y.z", features = ["feature-name"] }

feature-name desciption
keyed-list Support keyed-list for incremental mode
svg Support svg element
queue-render Support fined-grained render (*)

(*) Lists render by queue-render are always keyed.

Run examples

Prerequisites:

  • Rust with wasm32-unknown-unknown target.
  • Trunk

In an example folder:

trunk serve

or, if it's slow, use: (especialy examples/boids or examples/game_of_life)

trunk serve --release

Open your browser at http://localhost:8080

Documentation

Not yet. /examples/* is the best place to start now.

Sections below provide first looks into Spair.

Static-mode and update-mode

Spair works by iterating through every elements and attributes/properties in the current DOM, which is empty before the first render, creating new items or modifying existing items, it's the update-mode. But there are elements or attributes that will never change. You can tell Spair to just create them but ignore them when iterating over them later by turn on the static-mode.

items update-mode static-mode notes
attributes / properties default .static_attributes() call .static_attributes() after you are done with update-mode-attributes
elements default, .update_nodes() .static_nodes() only apply to elements, not apply to texts
texts .update_text(value) .static_text(value) not affected by mode introduced by .update_nodes() or .static_nodes()
  • .update_nodes() and .static_nodes() can be switched back and forth as many times as you want.
element
    // default to update-mode attributes
    .value(&some_value) // will be checked and updated if changed
    .class_if("class-name", bool_value)
    .static_attributes() // we are done with update-mode attributes!
    .class("class-name") // class="class-name" is added on creation, but ignored on subsequence renders
    // just add child-elements, default to update mode.
    .p(|p| {}) // create and update a <p>
    .update_text(value) // create and update a text
    .static_text(value) // a create-only text - not affected by update-mode (default).
    .static_nodes()
    .div(|d| {}) // a create-only <div> (because creating in static-mode)
    .update_text(value) // an updatable text - not affected by `.static_nodes()`
    .static_text(value) // a create-only text - because of `static_text`, not cause by `static_nodes`
  • Important note: when an element is creating in static mode, all its content will be ignored (not update) after the first render.
element
    .static_nodes() // Elements append after this will be in static-mode
    .p(|p| {
        // This closure only execute once on the creation of <p>.
        // In the future update, this closure will be IGNORED,
        // therefore, all child-nodes of <p> will NOT be updated despite
        // being created in update-mode.
        p.span(|s| {})
            .update_text(value); // NEW VALUE OF `value` WILL NEVER BE RENDERED.
    });

Example

Look in /examples for full examples

This is the render method of examples/counter:

impl spair::Component for State {
    type Routes = ();
    fn render(&self, element: spair::Element<Self>) {
        let comp = element.comp();
        element
            .static_nodes()
            .p(|p| {
                p.static_nodes()
                    .static_text("The initial value is ")
                    .static_text(self.value);
            })
            .rfn(|nodes| button("-", comp.handler(State::decrement), nodes))
            .update_text(self.value)
            .rfn(|nodes| button("+", comp.handler(State::increment), nodes));
    }
}

Access to the component state.

You can split your code into small functions. In those functions, you may want to access the state of your component:

fn some_render_fn(self, nodes: spair::Nodes<ComponentState>) {
    // type of `state` is `&ComponentState`
    let state = nodes.state();
    // render a value from the state
    nodes.update_text(state.value);
}

Reconciliation? - No, you must use .match_if()

Spair does not do reconciliation, users must do it by themselves. When an expected element is not found, Spair create it, but if Spair found an element at the expected index, Spair just assume it is the expected element. Therefore, when you want to render different elements base on a condition, you must tell Spair to do that via .match_if().

The following code is extracted from examples/fetch/src/lib.rs:

element
    .match_if(|mi| match self.branch.as_ref() {
        Some(branch) => spair::set_arm!(mi) // `spair::set_arm!()` uses a unique identifier internally to set `render_on_arm_index()`
            // Render the content of `Some(branch)`
            .rfn(|nodes| render_branch(branch, nodes))
            // some code removed
            .done(),
        None => spair::set_arm!(mi)
            // There is no value: `None`? Then just render a button
            .button(|b| {/* some code removed */})
            .done(),
    })

DON'T DO THIS, IT DOES NOT WORK

if some_condition {
    element.div(|d| {})
} else {
    element.p(|p| {})
}

Child components

Example: examples/components

Notes

Names conflict with Rust keywords

HTML's tags and attributes are implemented as methods in Spair. Names that are conflicted with Rust's keywords are implemented using raw identifers such as r#type, r#for...

Element and attribute/property with the same name.

There are elements named <span>, <label>... there are also attributes named span, label... and Spair implement all of them as methods. It's obviously not able to implement them on the same object. (Actually, Spair use traits for these, but conflicts are still there).

Therefore, to immediately add elements witch such names, call .update_nodes() or .static_nodes().

To set attributes/properties with such names, you have to call .attributes_only() or .static_attributes_only() first. After setting attributes/properties, you have to explicitly switch to nodes-mode using .update_nodes() or .static_nodes().

Attributes/properties with the same name.

Both <button> and <input> have an attribute named type. The methods are prefixed with their element name: button_type, input_type. This is also applied for all other conflicted attribute/property names.

Example:

element.span(); // => Error (an attribute or an element?)

element
    .update_nodes() // => Only have methods for elements, no methods for attributes
    .span(); // Element <span>  

element
    .attributes_only() // => Only have methods for attributes, no methods for elements 
    .span() // attribute
    .update_nodes()
    .span(); // Element <span>  

Common errors

Using Spair, you may encounter common mistakes listed in this section. They are really annoying. How these problems can be avoided?

static_attributes(), static_nodes()

If you set attributes or add nodes in static-mode it will never be updated. It is easy to misplace an update-mode item under static-mode. For example, you have an app and have already converted all things that are you considered static to static-mode. Now, after a while, You decide to add something that you want it to be updated on change. But you placed it under a branch of the DOM tree without noticing that the branch is under static-mode. Finally, you give the new version of the app a test, at first, you may scratch head and check back and forth many times because it is rendered, but its value never gets updated.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].