33 个版本
0.1.14 | 2024年8月3日 |
---|---|
0.1.12 | 2024年7月30日 |
0.0.18 | 2023年9月10日 |
0.0.17 | 2022年12月27日 |
0.0.14 | 2022年6月27日 |
#261 in 网页编程
每月680次下载
190KB
5K SLoC
Respo 在 Rust 中
Rust 的微型玩具虚拟 DOM 基础框架。
状态:实验中,没有 HMR (热代码替换) 就不开心。
Respo最初是为与持久数据一起工作的动态语言以及HMR(热代码替换)而设计的,这与Rust截然不同。因此,这更像是一个实验。
使用方法
以下是 DOM 语法的预览
Ok(
div()
.class(ui_global())
.style(respo_style().padding(12.0))
.children([
comp_counter(&states.pick("counter"), store.counted)?,
comp_panel(&states.pick("panel"))?,
comp_todolist(memo_caches, &states.pick("todolist"), &store.tasks)?,
]),
)
Rust 中的 CSS
static_styles!(
style_remove_button,
(
"&",
respo_style()
.width(16.px())
.height(16.px())
.margin(4.)
.cursor("pointer")
.margin4(0.0, 0.0, 0.0, 16.0)
.color(CssColor::Hsl(0, 90, 90)),
),
("&:hover", respo_style().color(CssColor::Hsl(0, 90, 80))),
);
内置样式,演示
函数 | 用法 |
---|---|
ui_global |
全局样式 |
ui_fullscreen |
全屏样式 |
ui_button |
按钮样式 |
ui_input |
输入样式 |
ui_textarea |
文本区域样式 |
ui_link |
链接样式 |
ui_flex |
flex:1 样式 |
ui_expand |
flex:1 样式,带滚动条 |
ui_center |
flexbox 居中样式 |
ui_row |
flexbox 行样式 |
ui_column |
flexbox 列样式 |
ui_row_center |
flexbox 行居中样式 |
ui_column_center |
flexbox 列居中样式 |
ui_row_around |
flexbox 行环绕样式 |
ui_column_around |
flexbox 列环绕样式 |
ui_row_evenly |
flexbox 行平均样式 |
ui_column_evenly |
flexbox 列平均样式 |
ui_row_parted |
flexbox 行分隔样式 |
ui_column_parted |
flexbox 列分隔样式 |
ui_row_middle |
flexbox 行分隔样式 |
ui_column_middle |
flexbox 列分隔样式 |
ui_font_code |
代码字体家族 |
ui_font_normal |
正常字体家族(Hind) |
ui_font_fancy |
花体字体家族(Josefin Sans) |
演示中包含几个对话框组件。语法还不够优雅,所以我不做宣传。但它们的工作效果相对不错。
更多组件,请阅读 src/app/
中的代码,它们只是像 RespoNode::Component(..)
这样的变体。将来可能会进行简化,目前尚未确定。
存储抽象
声明存储
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Store {
pub states: RespoStatesTree,
// TODO you app data
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ActionOp {
// TODO
StatesChange(RespoUpdateState),
}
impl RespoAction for ActionOp {
type Intent = (); // Intent is optional, it's for async actions.
fn states_action(a: RespoUpdateState) -> Self {
Self::StatesChange(a)
}
}
impl RespoStore for Store {
type Action = ActionOp;
fn update(&mut self, op: Self::Action) -> Result<(), String> {
match op {
// TODO
}
Ok(())
}
}
声明应用
struct App {
store: Rc<RefCell<Store>>,
mount_target: Node,
}
impl RespoApp for App {
type Model = Store;
fn get_store(&self) -> Rc<RefCell<Self::Model>> {
self.store.to_owned()
}
fn get_mount_target(&self) -> &web_sys::Node {
&self.mount_target
}
fn dispatch(store: &mut RefMut<Self::Model>, op: Self::Action) -> Result<(), String> {
store.update(op)
}
fn view(store: Ref<Self::Model>, memo_caches: MemoCache<RespoNode<Self::Action>>) -> Result<RespoNode<Self::Action>, String> {
let states = &store.states;
// util::log!("global store: {:?}", store);
Ok(
div()
.class(ui_global())
.style(respo_style().padding(12.0))
.children([
comp_counter(&states.pick("counter"), store.counted)?,
comp_panel(&states.pick("panel"))?,
comp_todolist(memo_caches, &states.pick("todolist"), &store.tasks)?,
]),
)
}
}
挂载应用
let app = App {
mount_target: query_select_node(".app").expect("mount target"),
store: Rc::new(RefCell::new(Store {
counted: 0,
states: RespoStatesTree::default(),
tasks: vec![],
})),
};
app.render_loop().expect("app render");
许可证
Apache 许可证 2.0。
依赖项
~12MB
~202K SLoC