mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Update optimizations.md: (#1422)
* Update optimizations.md:
* Make some sentences more clear
* Add additional links and references ("further reading")
* Capitalise some proper nouns
* Add missing terms to the dictionary.
* Split out a paragraph.
* Fix spelling.
* Update links.
* Apply suggestions from code review
Co-authored-by: Simon <simon@siku2.io>
* Update docs/advanced-topics/optimizations.md
Co-authored-by: Simon <simon@siku2.io>
This commit is contained in:
parent
d2b7c80860
commit
f3d53a7710
@ -6,6 +6,7 @@ AppRoute
|
||||
Borrow
|
||||
BorrowMut
|
||||
ComponentLink
|
||||
composable
|
||||
Config
|
||||
ConsoleService
|
||||
DevTools
|
||||
@ -98,4 +99,6 @@ Vec
|
||||
VecDeque
|
||||
wasm
|
||||
WeeAlloc
|
||||
workspaces
|
||||
yewtil
|
||||
Yewtil
|
||||
|
||||
@ -8,14 +8,18 @@ title: Introduction
|
||||
**Yew** is a modern [Rust](https://www.rust-lang.org/) framework for creating multi-threaded front-end web apps with [WebAssembly](https://webassembly.org/).
|
||||
|
||||
* It features a **component-based** framework which makes it easy to create interactive UIs. Developers who have experience with frameworks like [React](https://reactjs.org/) and [Elm](https://elm-lang.org/) should feel quite at home when using Yew.
|
||||
* It has **great performance** by minimizing DOM API calls and by helping developers easily offload processing to background web workers.
|
||||
* It supports **JavaScript interoperability**, allowing developers to leverage NPM packages and integrate with existing JavaScript applications.
|
||||
* It achieves **great performance** by minimizing DOM API calls and by helping developers easily
|
||||
offload processing to the background using web workers.
|
||||
* It supports **JavaScript interoperability**, allowing developers to leverage NPM packages and
|
||||
integrate with existing JavaScript applications.
|
||||
|
||||
### Join Us 😊
|
||||
|
||||
* You can report bugs and discuss features on the [GitHub issues page](https://github.com/yewstack/yew/issues)
|
||||
* We love pull requests. Check out [good first issues](https://github.com/yewstack/yew/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) if you'd like to help out!
|
||||
* Our [Discord chat](https://discord.gg/VQck8X4) is very active and is a great place to ask questions
|
||||
* We love pull requests. Check out the [good first issues](https://github.com/yewstack/yew/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
|
||||
if you'd like to help out!
|
||||
* Our [Discord chat](https://discord.gg/VQck8X4) is very active and is a great place to ask
|
||||
questions
|
||||
|
||||
### Ready to dive in?
|
||||
|
||||
@ -29,10 +33,16 @@ This project is built on cutting edge technology and is great for developers who
|
||||
|
||||
#### Wait, why WebAssembly?
|
||||
|
||||
WebAssembly _\(Wasm\)_ is a portable low-level language that Rust can compile into. It runs at native speeds in the browser and is interoperable with JavaScript and supported in all major browsers. For ideas on how to get the most out of WebAssembly for your app, check out this list of [Use Cases](https://webassembly.org/docs/use-cases/).
|
||||
|
||||
It should be noted that using Wasm is not \(yet\) a silver bullet for improving the performance of a web app. As of right now, using DOM APIs from WebAssembly is still slower than calling them directly from JavaScript. This is a temporary issue which the [WebAssembly Interface Types](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md) proposal aims to resolve. If you would like to learn more, check out this [excellent article](https://hacks.mozilla.org/2019/08/webassembly-interface-types/) from Mozilla.
|
||||
WebAssembly _\(Wasm\)_ is a portable low-level language that Rust can compile into. It runs at
|
||||
native speeds in the browser and is interoperable with JavaScript and supported in all major
|
||||
browsers. For ideas on how to get the most out of WebAssembly for your app, check out this list of
|
||||
[use cases](https://webassembly.org/docs/use-cases/).
|
||||
|
||||
It should be noted that using Wasm is not \(yet\) a silver bullet for improving the performance of
|
||||
web apps. As of the present, using DOM APIs from WebAssembly is still slower than calling them
|
||||
directly from JavaScript. This is a temporary issue which the
|
||||
[WebAssembly Interface Types](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md) proposal aims to resolve. If you would like to learn more, check out this
|
||||
[excellent article](https://hacks.mozilla.org/2019/08/webassembly-interface-types/) describing the proposal from Mozilla.
|
||||
#### Ok, but why Rust?
|
||||
|
||||
Rust is blazing fast and reliable with its rich type system and ownership model. It has a tough learning curve but is well worth the effort. Rust has been voted the most loved programming language in Stack Overflow's Developer Survey five years in a row: [2016](https://insights.stackoverflow.com/survey/2016#technology-most-loved-dreaded-and-wanted), [2017](https://insights.stackoverflow.com/survey/2017#most-loved-dreaded-and-wanted), [2018](https://insights.stackoverflow.com/survey/2018#technology-_-most-loved-dreaded-and-wanted-languages), [2019](https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages) and [2020](https://insights.stackoverflow.com/survey/2020#most-loved-dreaded-and-wanted).
|
||||
@ -45,4 +55,3 @@ We love to share ideas with other projects and believe we can all help each othe
|
||||
|
||||
* [Percy](https://github.com/chinedufn/percy) - _"A modular toolkit for building isomorphic web apps with Rust + WebAssembly"_
|
||||
* [Seed](https://github.com/seed-rs/seed) - _"A Rust framework for creating web apps"_
|
||||
|
||||
|
||||
@ -34,44 +34,95 @@ impl Example {
|
||||
|
||||
But we can go further! This is six lines of boilerplate can be reduced down to one by using a trait and a blanket implementation for anything that implements `PartialEq`. Check out the `yewtil` crate's `NeqAssign` trait [here](https://docs.rs/yewtil/*/yewtil/trait.NeqAssign.html).
|
||||
|
||||
## RC
|
||||
## Using smart pointers effectively
|
||||
|
||||
In an effort to avoid cloning large chunks of data to create props when re-rendering, we can use smart pointers to only clone the pointer instead. If you use `Rc<_>`s in your props and child components instead of plain unboxed values, you can delay cloning until you need to modify the data in the child component, where you use `Rc::make_mut` to clone and get a mutable reference to the data you want to alter. By not cloning until mutation, child components can reject props identical to their state-owned props in `Component::change` for almost no performance cost, versus the case where the data itself needs to be copied into the props struct in the parent before it is compared and rejected in the child.
|
||||
**Note: if you're unsure about some of the terms used in this section, the Rust Book has a useful
|
||||
[chapter about smart pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html).**
|
||||
|
||||
This optimization is most useful for data types that aren't `Copy`. If you can copy your data easily, then it probably isn't worth putting it behind a smart pointer. For structures that can contain lots of data like `Vec`, `HashMap`, and `String`, this optimization should be worthwhile.
|
||||
In an effort to avoid cloning large amounts of data to create props when re-rendering, we can use
|
||||
smart pointers to only clone a reference to the data instead of the data itself. If you pass
|
||||
references to the relevant data in your props and child components instead of the actual data you
|
||||
can avoid cloning any data until you need to modify it in the child component, where you can
|
||||
use `Rc::make_mut` to clone and obtain a mutable reference to the data you want to alter.
|
||||
|
||||
This optimization works best if the values are never updated by the children, and even better, if they are rarely updated by parents. This makes `Rc<_>s` a good choice for wrapping property values in for pure components.
|
||||
This brings further benefits in `Component::change` when working out whether prop changes require
|
||||
the component to re-render. This is because instead of comparing the value of the data the
|
||||
underlying pointer addresses (i.e. the position in a machine's memory where the data is stored) can
|
||||
instead be compared; if two pointers point to the same data then the value of the data they point to
|
||||
must be the same. Note that the inverse might not be true! Even if two pointer addresses differ the
|
||||
underlying data might still be the same - in this case you should compare the underlying data.
|
||||
|
||||
## View Functions
|
||||
To do this comparison you'll need to use `Rc::ptr_eq` instead of just using `PartialEq` (which is
|
||||
automatically used when comparing data using the equality operator `==`). The Rust documentation
|
||||
has [more details about `Rc::ptr_eq`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq).
|
||||
|
||||
For code readability reasons, it often makes sense to migrate sections of `html!` to their own functions so you can avoid the rightward drift present in deeply nested HTML.
|
||||
This optimization is most useful for data types that don't implement `Copy`. If you can copy your
|
||||
data cheaply, then it isn't worth putting it behind a smart pointer. For structures that
|
||||
can be data-heavy like `Vec`s, `HashMap`s, and `String`s using smart pointers is likely to bring
|
||||
performance improvements.
|
||||
|
||||
## Pure Components/Function Components
|
||||
This optimization works best if the values are never updated by the children, and even better, if
|
||||
they are rarely updated by parents. This makes `Rc<_>s` a good choice for wrapping property values
|
||||
in for pure components.
|
||||
|
||||
Pure components are components that don't mutate their state, only displaying content and propagating messages up to normal, mutable components. They differ from view functions in that they can be used from within the `html!` macro using the component syntax \(`<SomePureComponent />`\) instead of expression syntax \(`{some_view_function()}`\), and that depending on their implementation, they can be memoized - preventing re-renders for identical props using the aforementioned `neq_assign` logic.
|
||||
## View functions
|
||||
|
||||
For code readability reasons, it often makes sense to migrate sections of `html!` to their own
|
||||
functions. Not only does this make your code more readable because it reduces the amount of
|
||||
indentation present, it also encourages good design patterns – particularly around building
|
||||
composable applications because these functions can be called in multiple places which reduces the
|
||||
amount of code that has to be written.
|
||||
|
||||
## Pure Components
|
||||
|
||||
Pure components are components that don't mutate their state, only displaying content and
|
||||
propagating messages up to normal, mutable components. They differ from view functions in that they
|
||||
can be used from within the `html!` macro using the component syntax \(`<SomePureComponent />`\)
|
||||
instead of expression syntax \(`{some_view_function()}`\), and that depending on their
|
||||
implementation, they can be memoized (this means that once a function is called its value is "saved"
|
||||
so that if it's called with the same arguments more than once it doesn't have to recompute its value
|
||||
and can just return the saved value from the first function call) - preventing re-renders for
|
||||
identical props using the aforementioned `neq_assign` logic.
|
||||
|
||||
Yew doesn't natively support pure or function components, but they are available via external crates.
|
||||
|
||||
Function components don't exist yet, but in theory, pure components could be generated by using proc macros and annotating functions.
|
||||
## Functional components (a.k.a hooks)
|
||||
|
||||
Functional components are still in development! There's a
|
||||
[project board](https://github.com/yewstack/yew/projects/3) detailing their status.
|
||||
|
||||
## Keyed DOM nodes when they arrive
|
||||
|
||||
## Compile speed optimizations using Cargo Workspaces
|
||||
## Reducing compile time using workspaces
|
||||
|
||||
Arguably, the largest drawback to using Yew is the long time it takes to compile. Compile time seems to correlate with the quantity of code found within `html!` macro blocks. This tends to not be a significant problem for smaller projects, but for web apps that span multiple pages, it makes sense to break apart your code across multiple crates to minimize the amount of work the compiler has to do for each change made.
|
||||
Arguably, the largest drawback to using Yew is the long time it takes to compile Yew apps. The time
|
||||
taken to compile a project seems to be related to the quantity of code passed to the `html!` macro.
|
||||
This tends to not be much of an issue for smaller projects, but for larger applications it makes
|
||||
sense to split code across multiple crates to minimize the amount of work the compiler has to do for
|
||||
each change made to the application.
|
||||
|
||||
You should try to make your main crate handle routing/page selection, move all commonly shared code to another crate, and then make a different crate for each page, where each page could be a different component, or just a big function that produces `Html`. In the best case scenario, you go from rebuilding all of your code on each compile to rebuilding only the main crate, and one of your page crates. In the worst case, where you edit something in the "common" crate, you will be right back to where you started: compiling all code that depends on that commonly shared crate, which is probably everything else.
|
||||
One possible approach is to make your main crate handle routing/page selection, and then make a
|
||||
different crate for each page, where each page could be a different component, or just a big
|
||||
function that produces `Html`. Code which is shared between the crates containing different parts of
|
||||
the application could be stored in a separate crate which is depended on throughout the project.
|
||||
In the best case scenario, you go from rebuilding all of your code on each compile to rebuilding
|
||||
only the main crate, and one of your page crates. In the worst case, where you edit something in the
|
||||
"common" crate, you will be right back to where you started: compiling all code that depends on that
|
||||
commonly shared crate, which is probably everything else.
|
||||
|
||||
If your main crate is too heavyweight, or you want to rapidly iterate on a deeply nested page \(eg. a page that renders on top of another page\), you can use an example crate to create a more simple implementation of the main page and render your work-in-progress component on top of that.
|
||||
If your main crate is too heavyweight, or you want to rapidly iterate on a deeply nested page \(eg.
|
||||
a page that renders on top of another page\), you can use an example crate to create a simplified
|
||||
implementation of the main page and render the component you are working on on top of that.
|
||||
|
||||
## Build size optimization
|
||||
## Reducing binary sizes
|
||||
|
||||
* optimize Rust code
|
||||
* `wee_alloc` \( using tiny allocator \)
|
||||
* `cargo.toml` \( defining release profile \)
|
||||
* optimize wasm code using `wasm-opt`
|
||||
|
||||
More information about code size profiling: [rustwasm book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
|
||||
**Note: more information about reducing binary sizes can be found in the
|
||||
[Rust Wasm Book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size).**
|
||||
|
||||
### wee\_alloc
|
||||
|
||||
@ -87,9 +138,9 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
It is possible to setup release build for smaller size using `[profile.release]` section in `Cargo.toml`
|
||||
It is possible to configure release builds to be smaller using the available settings in the
|
||||
`[profile.release]` section of your `Cargo.toml`.
|
||||
|
||||
[Rust profiles documentation](https://doc.rust-lang.org/cargo/reference/profiles.html)
|
||||
|
||||
```text
|
||||
[profile.release]
|
||||
@ -109,9 +160,8 @@ lto = true
|
||||
|
||||
Further more it is possible to optimize size of `wasm` code.
|
||||
|
||||
wasm-opt info: [binaryen project](https://github.com/WebAssembly/binaryen)
|
||||
|
||||
The Rust Wasm book features a section about reducing the size of WASM binaries: [Shrinking .wasm size](https://rustwasm.github.io/book/game-of-life/code-size.html)
|
||||
The Rust Wasm Book has a section about reducing the size of WASM binaries:
|
||||
[Shrinking .wasm size](https://rustwasm.github.io/book/game-of-life/code-size.html)
|
||||
|
||||
* using `wasm-pack` which by default optimizes `wasm` code in release builds
|
||||
* using `wasm-opt` directly on `wasm` files.
|
||||
@ -130,3 +180,8 @@ Note: `wasm-pack` combines optimization for Rust and Wasm code. `wasm-bindgen` i
|
||||
| wasm-bindgen + wasm-opt -Os | 116KB |
|
||||
| wasm-pack | 99 KB |
|
||||
|
||||
## Further reading:
|
||||
* [The Rust Book's chapter on smart pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
|
||||
* [Information from the Rust Wasm Book about reducing binary sizes](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
|
||||
* [Documentation about Rust profiles](https://doc.rust-lang.org/cargo/reference/profiles.html)
|
||||
* [binaryen project](https://github.com/WebAssembly/binaryen)
|
||||
|
||||
@ -6,7 +6,9 @@ description: The planned feature roadmap for the Yew framework
|
||||
|
||||
## Prioritization
|
||||
|
||||
The prioritization of upcoming features and focuses of the framework are determined by the community. In Spring 2020, a developer survey was sent out to collect feedback on the direction of the project. You can find the summary in the [Yew Wiki](https://github.com/yewstack/yew/wiki/Dev-Survey-%5BSpring-2020%5D).
|
||||
The prioritization of upcoming features and focuses of the framework is determined by the community.
|
||||
In Spring 2020, a developer survey was sent out to collect feedback on the direction of the project.
|
||||
You can find the summary in the [Yew Wiki](https://github.com/yewstack/yew/wiki/Dev-Survey-%5BSpring-2020%5D).
|
||||
|
||||
{% hint style="info" %}
|
||||
Status of all major initiatives can be tracked on the Yew Github [Project board](https://github.com/yewstack/yew/projects)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user