Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Think About Front-end Web Development with Rust

Yosuke Onoue
November 01, 2020

Think About Front-end Web Development with Rust

This slide is the presentation material for rustfest global 2020.
https://rustfest.global/session/16-think-about-front-end-web-development-with-rust/

Abstract:
With the recent advances in WebAssembly and front-end application frameworks implemented in Rust, such as Yew and Percy, we are ready to develop web applications with Rust alone. However, there are still many issues such as performance and accessibility when compared to traditional front-end Web development with JavaScript. In this talk, I will report on the current state of front-end web development with Rust, including WebAssembly, toolchains, libraries and frameworks, and discuss future directions for more realistic front-end web development.

Yosuke Onoue

November 01, 2020
Tweet

More Decks by Yosuke Onoue

Other Decks in Programming

Transcript

  1. w :PTVLF0OPVFʢඌ্༸հʣ w "TTPDJBUF1SPGFTTPS %FQBSUNFOUPG*OGPSNBUJPO4DJFODF  $PMMFHFPG)VNBOJUJFTBOE4DJFODFT  /JIPO6OJWFSTJUZ w

    3FTFBSDI*OUFSFTUT w *OGPSNBUJPO7JTVBMJ[BUJPO w .BUIFNBUJDBM0QUJNJ[BUJPO w %FDJTJPO4VQQPSU w -PWFT w 3VTU w 8FCUFDIOPMPHJFT "CPVU.F IUUQTUXJUUFSDPN@MJLS IUUQTWETMBCKQ IUUQTKVEHJUOFU
  2. 3FDFOU8FC%FWFMPQNFOU w $IBOHFTJOBSDIJUFDUVSFPGXFCBQQTBOEXFCTJUFT    w *ODSFBTJOHXFJHIUPGGSPOUFOEXFCEFWFMPQNFOU w #FUUFS69BOECFUUFSQFSGPSNBODF

    w $PNQMJDBUJPOPGEFWFMPQNFOUQSPDFTT w 5SBOTQJMJOH CVOEMJOH UFTUJOH BTTFUNBOBHFNFOU FUD %#4FSWFS 8FC"QQ4FSWFS 8FC#SPXTFS )5.-HFOFSBUJPOXJUIUFNQMBUFFOHJOFT 1)1 1ZUIPO 3VCZ FUD )5.- $44 +BWB4DSJQU $MBTTJDBM8FC"QQ
  3. 3FDFOU8FC%FWFMPQNFOU w $IBOHFTJOBSDIJUFDUVSFPGXFCBQQTBOEXFCTJUFT    w *ODSFBTJOHXFJHIUPGGSPOUFOEXFCEFWFMPQNFOU w #FUUFS69BOECFUUFSQFSGPSNBODF

    w $PNQMJDBUJPOPGEFWFMPQNFOUQSPDFTT w 5SBOTQJMJOH CVOEMJOH UFTUJOH BTTFUNBOBHFNFOU FUD %#4FSWFS 8FC"QQ4FSWFS 8FC#SPXTFS )5.-HFOFSBUJPOXJUIUFNQMBUFFOHJOFT 1)1 1ZUIPO 3VCZ FUD )5.- $44 +BWB4DSJQU $MBTTJDBM8FC"QQ "QQ4FSWFS 8FC4FSWFS %#4FSWFS 8FC#SPXTFS +40/ )5.- $44 +BWB4DSJQU 6*DPOTUSVDUJPO JO8FC#SPXTFS +BWB4DSJQU 4JOHMF1BHF"QQ 41"  3&45"1*
  4. 3FDFOU8FC%FWFMPQNFOU w $IBOHFTJOBSDIJUFDUVSFPGXFCBQQTBOEXFCTJUFT    w *ODSFBTJOHXFJHIUPGGSPOUFOEXFCEFWFMPQNFOU w #FUUFS69BOECFUUFSQFSGPSNBODF

    w $PNQMJDBUJPOPGEFWFMPQNFOUQSPDFTT w 5SBOTQJMJOH CVOEMJOH UFTUJOH BTTFUNBOBHFNFOU FUD %#4FSWFS 8FC"QQ4FSWFS 8FC#SPXTFS )5.-HFOFSBUJPOXJUIUFNQMBUFFOHJOFT 1)1 1ZUIPO 3VCZ FUD )5.- $44 +BWB4DSJQU $MBTTJDBM8FC"QQ "QQ4FSWFS 8FC4FSWFS %#4FSWFS 8FC#SPXTFS +40/ )5.- $44 +BWB4DSJQU 6*DPOTUSVDUJPO JO8FC#SPXTFS +BWB4DSJQU 4JOHMF1BHF"QQ 41"  3&45"1*
  5. 3VTUBOE'SPOUFOE8FC%FWFMPQNFOU w 8PSLJOHXJUI+BWB4DSJQUBOECSPXTFS"1*T w XBTNCJOEHFO KTTZTBOEXFCTZT IUUQTHJUIVCDPNSVTUXBTNXBTNCJOEHFO w 8SJUJOH+BWB4DSJQUQBDLBHFTXJUI3VTU w

    XBTNQBDL IUUQTHJUIVCDPNSVTUXBTNXBTNQBDL w 8SJUJOHFOUJSFGSPOUFOEXFCBQQTXJUI3VTU w 'SPOUFOEGSBNFXPSLTXSJUUFOJO3VTU
  6. 3VTUBOE'SPOUFOE8FC%FWFMPQNFOU w 8PSLJOHXJUI+BWB4DSJQUBOECSPXTFS"1*T w XBTNCJOEHFO KTTZTBOEXFCTZT IUUQTHJUIVCDPNSVTUXBTNXBTNCJOEHFO w 8SJUJOH+BWB4DSJQUQBDLBHFTXJUI3VTU w

    XBTNQBDL IUUQTHJUIVCDPNSVTUXBTNXBTNQBDL w 8SJUJOHFOUJSFGSPOUFOEXFCBQQTXJUI3VTU w 'SPOUFOEGSBNFXPSLTXSJUUFOJO3VTU
  7. w 7JSUVBM%0. w %FDMBSBUJWF6*DPOTUSVDUJPO w &⒏DJFOUEJ⒎BMHPSJUIN w 4UBUFNBOBHFNFOU w 5IF&MN"SDIJUFDUVSF

    IUUQTHVJEFFMNMBOHPSHBSDIJUFDUVSF w *OTQJSFECZUIFDPODFQUTPG GVODUJPOBMQSPHSBNNJOH 1PQVMBS'SPOUFOE'SBNFXPSL"SDIJUFDUVSF IUUQTRJJUBDPN"@LJSJTBLJJUFNTGBBDDED
  8. "WBJMBCMF'SPOUFOE'SBNFXPSLT w :FX w 1FSDZ w 4FFE w %PESJP w

    4BVSPO w %SBDP w 4NJUIZ w %PNJOBUPS w .PHXBJ w TRVBSL w NJLB Rust Web Framework Comparison https://github.com/flosse/rust-web-framework-comparison (JU)VC4UBST  :FX 1FSDZ 4FFE %PESJP TBVSPO %SBDP 4NJUIZ %PNJOBUPS NPHXBJ TRVBSL     
  9. "WBJMBCMF'SPOUFOE'SBNFXPSLT w :FX w 1FSDZ w 4FFE w %PESJP w

    4BVSPO w %SBDP w 4NJUIZ w %PNJOBUPS w .PHXBJ w TRVBSL w NJLB Rust Web Framework Comparison https://github.com/flosse/rust-web-framework-comparison (JU)VC4UBST  :FX 1FSDZ 4FFE %PESJP TBVSPO %SBDP 4NJUIZ %PNJOBUPS NPHXBJ TRVBSL     
  10. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 }
  11. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 }
  12. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 } 3FOEFSJOHDPNQPOFOUTUBUF
  13. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 }
  14. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 } %JTQBUDINFTTBHF
  15. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 } 4UBUFVQEBUF
  16. :FX&YBNQMF w 4JNQMFDPVOUFSFYBNQMF IUUQTHJUIVCDPNZFXTUBDLZFXUSFF NBTUFSFYBNQMFTDPVOUFS w 6*DPOTUSVDUJPO w WJFXGVODUJPOBOEIUNMNBDSP w

    $PNQPOFOUTUBUFNBOBHFNFOU w VQEBUFGVODUJPODIBOHFTFMGTUBUF w DIBOHFGVODUJPODIBOHFGSPNQBSFOU 17 impl Component for Model { 18 type Message = Msg; 19 type Properties = (); 20 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 21 Model { link, value: 0 } 22 } 23 fn update(&mut self, msg: Self::Message) -> ShouldRender { 24 match msg { 25 Msg::Increment => { 26 self.value += 1; 27 } 28 Msg::Decrement => { 29 self.value -= 1; 30 } 31 } 32 true 33 } 34 fn change(&mut self, _: Self::Properties) -> ShouldRender { 35 false 36 } 37 fn view(&self) -> Html { 38 html! { 39 <div> 40 <nav class="menu"> 41 <button onclick=self.link.callback(|_| Msg::Increment)> 42 { "Increment" } 43 </button> 44 <button onclick=self.link.callback(|_| Msg::Decrement)> 45 { "Decrement" } 46 </button> 47 </nav> 48 <p>{ self.value }</p> 49 </div> 50 } 51 } 52 } 3FSFOEFSJOH
  17. w %FMBZFESFTQPOTFUPVTFST w 'SBNFSBUFESPQ .BJO5ISFBEJO8FC#SPXTFST w 5BTLTPGUIFNBJOUISFBE w %0.PQFSBUJPOT )5.-MBZPVU

     FWFOUIBOEMJOH TDSJQUJOH  FUD 5BTL 'SBNF 5BTL 5BTL 5BTL 'SBNF 'SBNF 'SBNF 'SBNF 'SBNF ❌ ❌ .BJO5ISFBE 5BTL &WFOU )BOEMJOH 8BJUJOH5JNF &WFOU .BJO5ISFBE
  18. 0⒎UIFNBJOUISFBE w -PBECBMBODJOHIFBWZUBTLT VTJOH8FC8PSLFST w *ODSFBTJOHNBJOUISFBEJEMFUJNF w *NQSPWJOH69 w "OPUIFSQPTTJCMFTPMVUJPO

    8FC"TTFNCMZ5ISFBET 5BTL 'SBNF 'SBNF 'SBNF 'SBNF 'SBNF 'SBNF .BJO5ISFBE 5BTL 'SBNF 5BTL 5BTL 5BTL 'SBNF 'SBNF 'SBNF 'SBNF 'SBNF ❌ ❌ .BJO5ISFBE 5BTL 8PSLFS 5BTL 5BTL 5BTL 5BTL 5BTL 5BTL 5BTL
  19. "HFOUTJO:FX 27 impl Agent for Worker { 43 fn update(&mut

    self, msg: Self::Message) { 44 match msg { 45 Msg::Updating => { 46 info!("Tick..."); 47 } 48 } 49 } 50 51 fn handle_input(&mut self, msg: Self::Input, who: HandlerId) { 52 info!("Request: {:?}", msg); 53 match msg { 54 Request::GetDataFromServer => { 55 // TODO fetch actual data 56 self.link.respond(who, Response::DataFetched); 57 } 58 } 59 } 64 } 19 impl Component for Model { 23 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 24 let callback = link.callback(|_| Msg::DataReceived); 25 let worker = Worker::bridge(callback); 26 27 Model { link, worker } 28 } 29 30 fn update(&mut self, msg: Self::Message) -> ShouldRender { 31 match msg { 32 Msg::SendToWorker => { 33 self.worker.send(Request::GetDataFromServer); 34 } 35 Msg::DataReceived => { 36 info!("DataReceived"); 37 } 38 } 39 true 40 } 41 42 fn view(&self) -> Html { 43 html! { 44 <div> 45 <nav class="menu"> 46 <button onclick=self.link.callback(|_| Msg::SendToWorker)>{"Send"}</button> 47 </nav> 48 </div> 49 } 50 } 55 } w IUUQTZFXSTEPDTFODPODFQUTBHFOUT w $BODBMMXPSLFSTUISPVHI BCTUSBDUFEJOUFSGBDF w "DUPSNPEFM .BJO5ISFBE 8PSLFS
  20. "HFOUTJO:FX 27 impl Agent for Worker { 43 fn update(&mut

    self, msg: Self::Message) { 44 match msg { 45 Msg::Updating => { 46 info!("Tick..."); 47 } 48 } 49 } 50 51 fn handle_input(&mut self, msg: Self::Input, who: HandlerId) { 52 info!("Request: {:?}", msg); 53 match msg { 54 Request::GetDataFromServer => { 55 // TODO fetch actual data 56 self.link.respond(who, Response::DataFetched); 57 } 58 } 59 } 64 } 19 impl Component for Model { 23 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 24 let callback = link.callback(|_| Msg::DataReceived); 25 let worker = Worker::bridge(callback); 26 27 Model { link, worker } 28 } 29 30 fn update(&mut self, msg: Self::Message) -> ShouldRender { 31 match msg { 32 Msg::SendToWorker => { 33 self.worker.send(Request::GetDataFromServer); 34 } 35 Msg::DataReceived => { 36 info!("DataReceived"); 37 } 38 } 39 true 40 } 41 42 fn view(&self) -> Html { 43 html! { 44 <div> 45 <nav class="menu"> 46 <button onclick=self.link.callback(|_| Msg::SendToWorker)>{"Send"}</button> 47 </nav> 48 </div> 49 } 50 } 55 } w IUUQTZFXSTEPDTFODPODFQUTBHFOUT w $BODBMMXPSLFSTUISPVHI BCTUSBDUFEJOUFSGBDF w "DUPSNPEFM .BJO5ISFBE 8PSLFS
  21. "HFOUTJO:FX 27 impl Agent for Worker { 43 fn update(&mut

    self, msg: Self::Message) { 44 match msg { 45 Msg::Updating => { 46 info!("Tick..."); 47 } 48 } 49 } 50 51 fn handle_input(&mut self, msg: Self::Input, who: HandlerId) { 52 info!("Request: {:?}", msg); 53 match msg { 54 Request::GetDataFromServer => { 55 // TODO fetch actual data 56 self.link.respond(who, Response::DataFetched); 57 } 58 } 59 } 64 } 19 impl Component for Model { 23 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 24 let callback = link.callback(|_| Msg::DataReceived); 25 let worker = Worker::bridge(callback); 26 27 Model { link, worker } 28 } 29 30 fn update(&mut self, msg: Self::Message) -> ShouldRender { 31 match msg { 32 Msg::SendToWorker => { 33 self.worker.send(Request::GetDataFromServer); 34 } 35 Msg::DataReceived => { 36 info!("DataReceived"); 37 } 38 } 39 true 40 } 41 42 fn view(&self) -> Html { 43 html! { 44 <div> 45 <nav class="menu"> 46 <button onclick=self.link.callback(|_| Msg::SendToWorker)>{"Send"}</button> 47 </nav> 48 </div> 49 } 50 } 55 } w IUUQTZFXSTEPDTFODPODFQUTBHFOUT w $BODBMMXPSLFSTUISPVHI BCTUSBDUFEJOUFSGBDF w "DUPSNPEFM .BJO5ISFBE 8PSLFS
  22. "HFOUTJO:FX 27 impl Agent for Worker { 43 fn update(&mut

    self, msg: Self::Message) { 44 match msg { 45 Msg::Updating => { 46 info!("Tick..."); 47 } 48 } 49 } 50 51 fn handle_input(&mut self, msg: Self::Input, who: HandlerId) { 52 info!("Request: {:?}", msg); 53 match msg { 54 Request::GetDataFromServer => { 55 // TODO fetch actual data 56 self.link.respond(who, Response::DataFetched); 57 } 58 } 59 } 64 } 19 impl Component for Model { 23 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 24 let callback = link.callback(|_| Msg::DataReceived); 25 let worker = Worker::bridge(callback); 26 27 Model { link, worker } 28 } 29 30 fn update(&mut self, msg: Self::Message) -> ShouldRender { 31 match msg { 32 Msg::SendToWorker => { 33 self.worker.send(Request::GetDataFromServer); 34 } 35 Msg::DataReceived => { 36 info!("DataReceived"); 37 } 38 } 39 true 40 } 41 42 fn view(&self) -> Html { 43 html! { 44 <div> 45 <nav class="menu"> 46 <button onclick=self.link.callback(|_| Msg::SendToWorker)>{"Send"}</button> 47 </nav> 48 </div> 49 } 50 } 55 } w IUUQTZFXSTEPDTFODPODFQUTBHFOUT w $BODBMMXPSLFSTUISPVHI BCTUSBDUFEJOUFSGBDF w "DUPSNPEFM .BJO5ISFBE 8PSLFS
  23. "HFOUTJO:FX 27 impl Agent for Worker { 43 fn update(&mut

    self, msg: Self::Message) { 44 match msg { 45 Msg::Updating => { 46 info!("Tick..."); 47 } 48 } 49 } 50 51 fn handle_input(&mut self, msg: Self::Input, who: HandlerId) { 52 info!("Request: {:?}", msg); 53 match msg { 54 Request::GetDataFromServer => { 55 // TODO fetch actual data 56 self.link.respond(who, Response::DataFetched); 57 } 58 } 59 } 64 } 19 impl Component for Model { 23 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 24 let callback = link.callback(|_| Msg::DataReceived); 25 let worker = Worker::bridge(callback); 26 27 Model { link, worker } 28 } 29 30 fn update(&mut self, msg: Self::Message) -> ShouldRender { 31 match msg { 32 Msg::SendToWorker => { 33 self.worker.send(Request::GetDataFromServer); 34 } 35 Msg::DataReceived => { 36 info!("DataReceived"); 37 } 38 } 39 true 40 } 41 42 fn view(&self) -> Html { 43 html! { 44 <div> 45 <nav class="menu"> 46 <button onclick=self.link.callback(|_| Msg::SendToWorker)>{"Send"}</button> 47 </nav> 48 </div> 49 } 50 } 55 } w IUUQTZFXSTEPDTFODPODFQUTBHFOUT w $BODBMMXPSLFSTUISPVHI BCTUSBDUFEJOUFSGBDF w "DUPSNPEFM .BJO5ISFBE 8PSLFS
  24. .PEFSO$448PSLqPX w .PUJWBUJPO w "WPJEJOH$44USPVCMFT w "TTFUPQUJNJ[BUJPO w 4PMVUJPOT w

    $MBTTOBNJOHDPOWFOUJPOTMJLF#&. w $44.PEVMFT w $44JO+4MJLF4UZMFE$PNQPOFOUT
  25. 3VTU"QQSPBDIFT w $MBTTJEFOUJpFSHFOFSBUJPO w DTTNBDSPJO1FSDZ IUUQTHJUIVCDPNDIJOFEVGOQFSDZUSFF NBTUFSFYBNQMFTDTTJOSVTU w $44JO3VTU IUUQTHJUIVCDPNMVLJEPFTDPEFDTTJOSVTU

    w 5ZQFTBGF$44HFOFSBUJPO w 3644 IUUQTHJUIVCDPNTJLVSVTT 12 impl Component for App { 16 fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self { 17 let style = match Style::create("App", include_str!("app.scss")) { 18 Ok(style) => style, 19 Err(error) => { 20 panic!("An error occured while creating the style: {}", error); 21 } 22 }; 23 App { style } 24 } 34 fn view(&self) -> Html { 35 html! {<div class=self.style.clone()> 38 </div>} 39 } 40 } 17 assert_eq!( 18 render(Calc::bin_div(Calc::bin_div(Length::px(100), 2), 2)), 19 "calc((100px / 2) / 2)" 20 ); 21 assert_eq!( 22 render(Color::rgba(Calc::bin_sub(255, 5), 0, 153, 1)), 23 "rgb(calc(255 - 5),0,153,1)" 24 );
  26. 4FSWFS4JEF3FOEFSJOH 443 w .PUJWBUJPO w *NQSPWJOHJOJUJBMQBJOUJOHQFSGPSNBODF w 3FOEFSJOHDPOUFOUTXJUIPVU+BWB4DSJQU 1SPHSFTTJWF&OIBODFNFOU 41"

    41" 443 w4DSJQU-PBEJOH w4DSJQU&YFDVUJPO w3FOEFSJOHDPOUFOUT 'JSTU1BJOU 'JSTU1BJOU 3FBEZUP*OUFSBDUJWF 3FBEZUP*OUFSBDUJWF %JTQMBZ%FMBZ
  27. 4FSWFS4JEF3FOEFSJOHJO1FSDZ w IUUQTHJUIVCDPNDIJOFEVGOQFSDZ w 8FC"TTFNCMZCBTFEBQQGSBNFXPSLTVQQPSUTTFSWFSTJEFSFOEFSJOH w *TPNPSQIJDFYBNQMF IUUQTHJUIVCDPNDIJOFEVGOQFSDZUSFFNBTUFSFYBNQMFTJTPNPSQIJD w *TPNPSQIJDXPSLTUIFTBNFPOUIFTFSWFSBOEDMJFOU

    "QQ4FSWFS 8FC#SPXTFS )BOEMJOHVTFSJOUFSBDUJPOT 1FSDZ "DUJYXFC 3PDLFU FUD 3VTU (FOFSBUJOHJOJUJBMWJFXBOEJOJUJBMTUBUF 1FSDZ 8FC"TTFNCMZ IUUQTFYBNQMFDPNTPNFQBUI QBSBNWBMVF %0$5:1&IUNM IUNM IFBEIFBE CPEZ EJWJEFOUSZQPJOU JOJUJBMWJFX EJW TDSJQU JOJUJBM4UBUF\ JOJUJBMTUBUF ^ TDJSQU CPEZ IUNM
  28. +BNTUBDLJO3VTU 1SPCBCMZ3BNTUBDL w +BNTUBDL w +BWB4DSJQU "1*T .BSLVQ w #FUUFSMPBEJOHQFSGPSNBODF

    VTJOH$%/ w /FYUKT w 3FBDUCBTFEGSBNFXPSLTVQQPSUT443BOE44( w &BTJMZQVCMJTIJOHXFCBQQTPWFS$%/T w )JHIMFWFMGSBNFXPSLMJLF/FYUKTXJMMIFMQ UPEFWFMPQTDBMBCMFXFCBQQTXJUI3VTU .JDSPTFSWJDFT $%/ 8FC#SPXTFS )5.- $44 +BWB4DSJQU &⒏DJFOUMZTFSWJOHTUBUJDDPOUFOUTPWFS$%/ +BNTUBDL
  29. 8FC$PNQPOFOUT w IUUQTXXXXFCDPNQPOFOUTPSH w 8FCOBUJWFDPNQPOFOUTZTUFN w 4QFDJpDBUJPOT w $VTUPN&MFNFOUT 4IBEPX%0.

     &4.PEVMFT )5.-5FNQMBUF w *OIFSJUBODFPG+BWB4DSJQUDMBTTCZXBTNCJOEHFO JTVOEFSEJTDVTTJPO IUUQTHJUIVCDPNSVTUXBTNSGDTQVMM
  30. 8IZ8FC$PNQPOFOUTXJUI3VTU IUUQTNJDSPGSPOUFOETPSH w &NCFEEJOH6*QBSUTXSJUUFOJO3VTUJOUP XFCBQQT w .JDSP'SPOUFOET w 4QMJUUJOHFOMBSHFEXFCTZTUFNT w

    *OUFHSBUJPOPGTVCTZTUFNT JNQMFNFOUFEJOWBSJPVTGSBNFXPSLT 8SJUUFOCZ"OHVMBS 8SJUUFOCZ3FBDU 8SJUUFOCZ3VTU 8FC"TTFNCMZ FH