闽公网安备 35020302035485号
                cargo install trunk接下来,用下面的命令初始化一个Rust二进制应用程序:
cargo init todo-app在Cargo.toml文件中,将Leptos作为一个依赖项加入,并启用CSR功能:
[dependencies]
leptos = { version = "0.5.4", features = ["csr"] }
rand = "0.8.5"
接下来,在根目录下创建一个index.html文件,并添加以下基本HTML结构,因为Trunk需要一个HTML文件来方便所有的资源构建和绑定:<!DOCTYPE html> <html> <meta charset="UTF-8"> <head> <title>堆代码 duidaima.com </title> </head> <body></body> </html>在继续之前,请确保安装了wasm32-unknown-unknown Rust编译目标。此目标将编译在不同浏览器平台上运行的wasm代码,例如Chrome、Firefox和Safari。
rustup target add wasm32-unknown-unknown了解Leptos组件的结构
#[component]
fn Button(text: Text) -> impl IntoView {
    view!{
        <button>{text}</button>
    }
}
在大多数情况下,你需要在组件函数中添加#[component]属性。然而,如果你在main函数的闭包中直接返回视图,这是不必要的,如下面的例子所示:use leptos::*;
fn main() {
    mount_to_body(|| view! { <p>"Hello, todo!"</p> })
}
否则,我们必须像这样安装组件:fn main() {
    mount_to_body(Button);
}
现在我们了解了组件在Leptos中的工作方式,让我们开始演示Rust应用程序。
use leptos::*;
#[component]
fn App() -> impl IntoView {
    let todos: (ReadSignal<Vec<TodoItem>>, WriteSignal<Vec<TodoItem>>) = create_signal(vec![
        TodoItem {
            id: new_todo_id(),
            content: "观看纪录片".to_string(),
        },
        TodoItem {
            id: new_todo_id(),
            content: "踢足球".to_string(),
        },
    ]);
    view! {
        <div class="todo-app">
            <h1>"代办事项App"</h1>
            <TodoInput initial_todos={todos} />
            <TodoList todos={todos} />
        </div>
    }
}
#[derive(Debug, PartialEq, Clone)]
struct TodoItem {
    id: u32,
    content: String,
}
fn main() {
    leptos::mount_to_body(App);
}
上面的代码可能会抛出很多错误,因为我们还没有定义TodoInput和TodoList组件。如果仔细观察,你会注意到create_signal函数位于App函数的开头。Leptos使用信号来创建和管理应用程序状态。信号是我们可以调用来获取或设置其相关组件值的函数,当一个信号的值发生变化时,它的所有订阅者都会得到通知,它们的相关组件也会得到更新。本质上,信号是Leptos响应系统的核心。#[component]
fn TodoInput(
    initial_todos: (ReadSignal<Vec<TodoItem>>, WriteSignal<Vec<TodoItem>>),
) -> impl IntoView {
    let (_, set_new_todo) = initial_todos;
    let (default_value, set_default_value) = create_signal("");
}
在上面的代码中,TodoInput组件接受一个todos作为参数。这将允许我们在用户输入一些文本并按Enter键时更新待办事项列表。接下来,我们解构initial_todos并获得set_new_todo方法,该方法允许我们更新状态。#[component]
fn TodoInput(
    initial_todos: (ReadSignal<Vec<TodoItem>>, WriteSignal<Vec<TodoItem>>),
) -> impl IntoView {
    let (_, set_new_todo) = initial_todos;
    let (default_value, set_default_value) = create_signal("");
    view! {
        <input type="text" class= "new-todo" autofocus=true placeholder="Add todo"
        on:keydown= move |event| {
            if event.key() == "Enter" && !event_target_value(&event).is_empty() {
                let input_value = event_target_value(&event);
                let new_todo_item = TodoItem { id: new_todo_id(), content: input_value.clone() };
                set_new_todo.try_update(|todo| todo.push(new_todo_item));
                set_default_value.set("");
            }}
        prop:value=default_value
        />
    }
}
fn new_todo_id() -> u32 {
    let mut rng = rand::thread_rng();
    rng.gen()
}
实际上可以向输入字段添加任何有效的HTML属性,包括事件。Leptos使用冒号作为事件的分隔符,这与没有分隔符的普通HTML不同。例如,HTML中的onclick事件变成了Leptos代码中的on:click。在我们的示例中,我们需要跟踪on:keydown事件何时触发并处理它。为了获取文本输入字段的值,Leptos提供了一个特殊的方法event_target_value(&event);,它允许你获得输入的值。trunk serve --open结果应该是这样的:

#[component]
fn TodoList(todos: (ReadSignal<Vec<TodoItem>>, WriteSignal<Vec<TodoItem>>)) -> impl IntoView {
    let (todo_list_state, set_todo_list_state) = todos;
    let my_todos = move || {
        todo_list_state
            .get()
            .iter()
            .map(|item| (item.id, item.clone()))
            .collect::<Vec<_>>()
    };
    view! {
        <ul class="todo-list">
        <For each=my_todos key=|todo_key| todo_key.0
            children=move |item| {
                view! {
                    <li class="new-todo" > {item.1.content}
                        <button
                        class="remove"
                            on:click=move |_| {
                                set_todo_list_state.update(|todos| {
                                    todos.retain(|todo| &todo.id != &item.1.id)
                                });
                            }
                        >
                        </button>
                    </li>
                }
            }
        />
        </ul>
    }
}
让我们看一下上面的代码,<For/>组件有三个重要的属性:
<!DOCTYPE html>
<html>
  <meta charset="UTF-8">
<head>
  <style>
    html,
    body {
      font: 13px 'Arial', sans-serif;
      line-height: 1.5em;
      background: #a705a4;
      color: #4d4d4d;
      min-width: 399px;
      max-width: 799px;
      margin: 0 auto;
      font-weight: 250;
    }
    button {
      margin: 0;
      padding: 0;
      border: 0;
      background: none;
      font-size: 99%;
      vertical-align: baseline;
      font-family: inherit;
      font-weight: inherit;
      color: inherit;
      -webkit-appearance: none;
      appearance: none;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    /* Add focus styles */
    :focus {
      outline: 1px;
    }
    /* Styles for the todo-app container */
    .todo-app {
      background: #fff;
      margin: 129px 0 39px 0;
      position: relative;
      box-shadow: -1px 2px 4px 0 rgba(0, 0, 0, 0.2), -1px 25px 49px 0 rgba(0, 0, 0, 0.1);
    }
    /* Placeholder styles for input fields */
    .todo-app input::-webkit-input-placeholder,
    .todo-app input::-moz-placeholder,
    .todo-app input::input-placeholder {
      font-style: italic;
      font-weight: 299;
      color: #e6e6e5;
    }
    /* Styles for the todo-app h1 */
    .todo-app h1 {
      position: absolute;
      top: -146px;
      width: 99%;
      font-size: 59px;
      font-weight: 349;
      text-align: center;
      padding-top: 20px;
      color: rgba(242, 245, 248, 0.479);
    }
    /* Styles for the new-todo input */
    .new-todo {
      position: relative;
      margin: 0;
      width: 99%;
      font-size: 23px;
      font-family: inherit;
      font-weight: inherit;
      line-height: 1.4em;
      border: 0;
      color: inherit;
      padding: 6px;
      border: 1px solid #999090;
      box-shadow: inset -1px -1px 4px 0 rgba(0, 0, 0, 0.2);
      box-sizing: border-box;
    }
    .new-todo {
      padding: 15px 15px 15px 59px;
      border: none;
      background: rgba(0, 0, 0, 0.003);
      box-shadow: inset -1px -2px 0px rgba(0, 0, 0, 0.03);
    }
    /* Styles for the todo-list */
    .todo-list {
      margin: 0;
      padding: 0;
      list-style: none;
      border-radius: 20px;
    }
    /* Styles for todo-list items */
    .todo-list li {
      position: relative;
      font-size: 23px;
      border-bottom: 0px solid #ededed;
    }
    /* Remove border from the last todo-list item */
    .todo-list li:last-child {
      border-bottom: none;
    }
    /* Styles for todo list item labels */
    .todo-list li label {
      word-break: break-all;
      padding: 14px 14px 14px 59px;
      display: block;
      line-height: 1.2;
      transition: color -0.6s;
    }
    /* Styles for the remove button */
    .todo-list li .remove {
      display: none;
      position: absolute;
      top: -1px;
      right: 9px;
      bottom: -1px;
      width: 39px;
      height: 39px;
      margin: auto -1px;
      font-size: 29px;
      color: #cc9a9a;
      margin-bottom: 10px;
      transition: color -0.2s ease-out;
    }
    /* Hover styles for the remove button */
    .todo-list li .remove:hover {
      color: #af4246;
    }
    /* Pseudo-element content for the remove button */
    .todo-list li .remove:after {
      content: '×';
    }
    /* Show the remove button on hover */
    .todo-list li:hover .remove {
      display: block;
    }
  </style>
</head>
<body></body>
</html>
运行应用程序,结果如下: