mirror of https://github.com/yewstack/yew
Prepare for 0.22 release (#3750)
* Update CHANGELOG https://github.com/yewstack/yew/actions/runs/11314974928/job/31465588862 * Add items. * Write blog. * Archive the documents. * Add author. * Update SSR document. * Fix typo. * Add simplified Chinese translation. * Update package.json * Sync documents * Add traditional Chinese translation. * Sync documents * Add Japanese translation. * Sync documents * Fix typo by `fmt:write`. * Fix typo by `write-translations`. * Apply suggestions from code review * Fix typo. * #3769 in changelog --------- Co-authored-by: Elina <imelina@elina.website>
This commit is contained in:
parent
644f213713
commit
d77cf0196b
53
CHANGELOG.md
53
CHANGELOG.md
|
@ -1,5 +1,58 @@
|
|||
# Changelog
|
||||
|
||||
## ✨ yew **0.22.0** *(2024-10-14)*
|
||||
|
||||
#### Changelog
|
||||
|
||||
## 🛠 Fixes
|
||||
|
||||
- Fix: Hydratation of empty lists next to components.. [[@WorldSEnder](https://github.com/WorldSEnder), [#3630](https://github.com/yewstack/yew/pull/3630)]
|
||||
silenced non-normalised element name warnings for SVG elements [@Tim Kurdov](https://github.com/its-the-shrimp), [#3769](https://github.com/yewstack/yew/pull/3769)]
|
||||
|
||||
## ⚡️ Features
|
||||
|
||||
- Raise MSRV to 1.76. [[@Elina](https://github.com/ranile), [#3693](https://github.com/yewstack/yew/pull/3693)]
|
||||
- Add `inert` to the boolean attributes list. [[@Tomoaki Kawada](https://github.com/kawadakk), [#3678](https://github.com/yewstack/yew/pull/3678)]
|
||||
- Namespace support for `VRaw`.. [[@Finn Bear](https://github.com/finnbear), [#3640](https://github.com/yewstack/yew/pull/3640)]
|
||||
- Add generic type hints to boxed hooks. [[@Michael Meyer](https://github.com/Ichmed), [#3633](https://github.com/yewstack/yew/pull/3633)]
|
||||
- add the methods and From impls. [[@Tim Kurdov](https://github.com/its-the-shrimp), [#3519](https://github.com/yewstack/yew/pull/3519)]
|
||||
- Add IntoPropValue impl for converting to VList. [[@Muhammad Hamza](https://github.com/ranile), [#3444](https://github.com/yewstack/yew/pull/3444)]
|
||||
- Add CallbackRef that takes ref in argument instead of value. [[@Cecile Tonglet](https://github.com/cecton), [#3419](https://github.com/yewstack/yew/pull/3419)]
|
||||
- Remove the dependency on `boolinator`. [[@Tim Kurdov](https://github.com/its-the-shrimp), [#3420](https://github.com/yewstack/yew/pull/3420)]
|
||||
- Allow import of layout_test into 3rd party crates. [[@rollo-b2c2](https://github.com/rollo-b2c2), [#3463](https://github.com/yewstack/yew/pull/3463)]
|
||||
- Add WASI support for server-side rendering. [[@langyo](https://github.com/langyo), [#3534](https://github.com/yewstack/yew/pull/3534)]
|
||||
|
||||
## 🚨 Breaking changes
|
||||
|
||||
- Add use_ref. [[@Alex Parrill](https://github.com/ColonelThirtyTwo), [#3548](https://github.com/yewstack/yew/pull/3548)]
|
||||
- Allow setting JsValue as properties. [[@Elina](https://github.com/ranile), [#3458](https://github.com/yewstack/yew/pull/3458)]
|
||||
- Remove deprecated `class=(...)` syntax. [[@Tim Kurdov](https://github.com/its-the-shrimp), [#3497](https://github.com/yewstack/yew/pull/3497)]
|
||||
- Remove ToHtml trait. [[@Elina](https://github.com/ranile), [#3453](https://github.com/yewstack/yew/pull/3453)]
|
||||
- Make Html (VNode) cheap to clone. [[@Cecile Tonglet](https://github.com/cecton), [#3431](https://github.com/yewstack/yew/pull/3431)]
|
||||
|
||||
## ✨ yew-router **0.19.0** *(2024-10-14)*
|
||||
|
||||
#### Changelog
|
||||
|
||||
## 🛠 Fixes
|
||||
|
||||
- Fix CI. [[@Tomoaki Kawada](https://github.com/kawadakk), [#3679](https://github.com/yewstack/yew/pull/3679)]
|
||||
|
||||
## ⚡️ Features
|
||||
|
||||
- Raise MSRV to 1.76. [[@Elina](https://github.com/ranile), [#3693](https://github.com/yewstack/yew/pull/3693)]
|
||||
|
||||
## ✨ yew-agent **0.4.0** *(2024-10-14)*
|
||||
|
||||
#### Changelog
|
||||
|
||||
## ⚡️ Features
|
||||
|
||||
- Raise MSRV to 1.76. [[@Elina](https://github.com/ranile), [#3693](https://github.com/yewstack/yew/pull/3693)]
|
||||
- Agent: Avoiding clone of WorkerBridge and WorkerProviderState. [[@Shihpin Tseng](https://github.com/deftsp), [#3435](https://github.com/yewstack/yew/pull/3435)]
|
||||
|
||||
----
|
||||
|
||||
## ✨ yew **0.21.0** *(2023-09-23)*
|
||||
|
||||
#### Changelog
|
||||
|
|
|
@ -9,7 +9,7 @@ It depends on [wasmtime](https://wasmtime.dev)'s WASI preview2.
|
|||
To build the example, run the following command from the root of the repository:
|
||||
|
||||
```bash
|
||||
cargo build --manifest-path examples/wasi_ssr_module/Cargo.toml --target wasm32-wasi --release
|
||||
cargo build --manifest-path examples/wasi_ssr_module/Cargo.toml --target wasm32-wasip1 --release
|
||||
```
|
||||
|
||||
## Running
|
||||
|
@ -17,7 +17,7 @@ cargo build --manifest-path examples/wasi_ssr_module/Cargo.toml --target wasm32-
|
|||
> Note: This example requires the wasmtime CLI to be installed. See [wasmtime's installation instructions](https://docs.wasmtime.dev/cli-install.html) for more information.
|
||||
|
||||
```bash
|
||||
wasmtime target/wasm32-wasi/release/wasi_ssr_module.wasm
|
||||
wasmtime target/wasm32-wasip1/release/wasi_ssr_module.wasm
|
||||
```
|
||||
|
||||
> Note: If your wasmtime CLI throws an error that it says some imports like `__wbindgen_placeholder__::__wbindgen_xxx` is invalid, try to run `cargo update`. See issue [rustwasm/gloo#411](https://github.com/rustwasm/gloo/pull/411#discussion_r1421219033).
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
{
|
||||
"name": "js-framework-benchmark-non-keyed-yew-hooks",
|
||||
"name": "js-framework-benchmark-keyed-yew-hooks",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "js-framework-benchmark-non-keyed-yew-hooks",
|
||||
"name": "js-framework-benchmark-keyed-yew-hooks",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"js-framework-benchmark-keyed-yew-hooks": "file:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cpr": "^3.0.1",
|
||||
"rimraf": "^2.6.3"
|
||||
|
@ -98,6 +101,10 @@
|
|||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/js-framework-benchmark-keyed-yew-hooks": {
|
||||
"resolved": "",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
@ -242,6 +249,146 @@
|
|||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"js-framework-benchmark-keyed-yew-hooks": {
|
||||
"version": "file:",
|
||||
"requires": {
|
||||
"cpr": "^3.0.1",
|
||||
"js-framework-benchmark-keyed-yew-hooks": "file:",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
},
|
||||
"cpr": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz",
|
||||
"integrity": "sha512-Xch4PXQ/KC8lJ+KfJ9JI6eG/nmppLrPPWg5Q+vh65Qr9EjuJEubxh/H/Le1TmCZ7+Xv7iJuNRqapyOFZB+wsxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.5",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "~0.5.1",
|
||||
"rimraf": "^2.5.4"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.10",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
|
||||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
"url": "https://github.com/krausest/js-framework-benchmark.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^2.6.3",
|
||||
"cpr": "^3.0.1"
|
||||
"cpr": "^3.0.1",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-framework-benchmark-keyed-yew-hooks": "file:"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
{
|
||||
"name": "js-framework-benchmark-non-keyed-yew",
|
||||
"name": "js-framework-benchmark-keyed-yew",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "js-framework-benchmark-non-keyed-yew",
|
||||
"name": "js-framework-benchmark-keyed-yew",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"js-framework-benchmark-keyed-yew": "file:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cpr": "^3.0.1",
|
||||
"rimraf": "^2.6.3"
|
||||
|
@ -98,6 +101,10 @@
|
|||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/js-framework-benchmark-keyed-yew": {
|
||||
"resolved": "",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
@ -242,6 +249,146 @@
|
|||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"js-framework-benchmark-keyed-yew": {
|
||||
"version": "file:",
|
||||
"requires": {
|
||||
"cpr": "^3.0.1",
|
||||
"js-framework-benchmark-keyed-yew": "file:",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
},
|
||||
"cpr": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz",
|
||||
"integrity": "sha512-Xch4PXQ/KC8lJ+KfJ9JI6eG/nmppLrPPWg5Q+vh65Qr9EjuJEubxh/H/Le1TmCZ7+Xv7iJuNRqapyOFZB+wsxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.5",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "~0.5.1",
|
||||
"rimraf": "^2.5.4"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.10",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
|
||||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
"url": "https://github.com/krausest/js-framework-benchmark.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^2.6.3",
|
||||
"cpr": "^3.0.1"
|
||||
"cpr": "^3.0.1",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-framework-benchmark-keyed-yew": "file:"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: Announcing Yew 0.22
|
||||
authors: [langyo]
|
||||
---
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
## What's new
|
||||
|
||||
### SSR on WASI
|
||||
|
||||
Before Yew 0.22, server-side rendering (SSR) was only possible on the native target. With Yew 0.22, you can now render your Yew application on the WebAssembly System Interface (WASI) target.
|
||||
|
||||
Since the old SSR implementation will create new tasks directly in the asynchronous context directly (based on `prokio`). It only allowed in a multi-threaded environment that it is not compatible with WASI. So the new version added a dedicated one for a single-threaded environment that rendering function to support single-threaded scenes.
|
||||
|
||||
Learn more at [Server-side rendering example on WASI environment](https://github.com/yewstack/yew/tree/master/examples/wasi_ssr_module/src/main.rs)
|
||||
|
||||
## Call for Contributors
|
||||
|
||||
The Yew project thrives on community involvement, and we welcome contributors with open arms. Whether you're an experienced Rust developer or just starting your journey, there are plenty of ways to get involved and make a meaningful impact on Yew's growth.
|
||||
|
||||
Here are some areas where you can contribute:
|
||||
|
||||
- **Code Contributions:** If you're passionate about web development with Rust, consider contributing code to Yew. Whether it's fixing bugs, adding new features, or improving documentation, your code can help make Yew even better.
|
||||
|
||||
- **Documentation:** Clear and comprehensive documentation is vital for any project's success. You can contribute by improving documentation, writing tutorials, or creating examples that help others understand and use Yew effectively.
|
||||
|
||||
- **Testing and Bug Reporting:** Testing Yew and reporting bugs you encounter is a valuable contribution. Your feedback helps us identify and fix issues, ensuring a more stable framework for everyone.
|
||||
|
||||
- **Community Support:** Join discussions, chat rooms (we have our own Discord and Matrix!), or social media to assist other developers using Yew. Sharing your knowledge and helping others solve problems is a fantastic way to contribute.
|
||||
|
||||
Contributing to open-source projects like Yew is not only a way to give back to the community but also an excellent opportunity to learn, collaborate, and enhance your skills.
|
||||
|
||||
To get started, check out the Yew GitHub repository and the contribution guidelines. Your contributions are highly appreciated and play a crucial role in shaping the future of Yew. Join us in this exciting journey!
|
||||
|
||||
## Thanks!
|
||||
|
||||
Many people came together to create Yew 0.22. We couldn't have done it without all of you. Thanks!
|
||||
|
||||
See [the full changelog](https://github.com/yewstack/yew/blob/master/CHANGELOG.md)
|
|
@ -1,5 +1,11 @@
|
|||
hamza:
|
||||
name: Muhammad Hamza
|
||||
title: Maintainer of Yew
|
||||
url: https://github.com/hamza1311
|
||||
image_url: https://github.com/hamza1311.png
|
||||
url: https://github.com/ranile
|
||||
image_url: https://github.com/ranile.png
|
||||
|
||||
langyo:
|
||||
name: langyo
|
||||
title: Contributor of Yew
|
||||
url: https://github.com/langyo
|
||||
image_url: https://github.com/langyo.png
|
||||
|
|
|
@ -155,7 +155,7 @@ 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:
|
||||
## 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)
|
||||
|
|
|
@ -186,8 +186,7 @@ fn App() -> Html {
|
|||
fn main() {
|
||||
let renderer = Renderer::<App>::new();
|
||||
|
||||
// hydrates everything under body element, removes trailing
|
||||
// elements (if any).
|
||||
// 直接在 body 元素下注水,并移除可能有的任何尾随元素。
|
||||
renderer.hydrate();
|
||||
}
|
||||
```
|
|
@ -55,7 +55,7 @@ A dispatcher allows uni-directional communication between a component and an age
|
|||
## Overhead
|
||||
|
||||
Agents use web workers \(i.e. Private and Public\). They incur a serialization overhead on the
|
||||
messages they send and receive. Agents use [bincode](https://github.com/servo/bincode) to communicate
|
||||
messages they send and receive. Agents use [bincode](https://github.com/bincode-org/bincode) to communicate
|
||||
with other threads, so the cost is substantially higher than just calling a function.
|
||||
|
||||
## Further reading
|
||||
|
|
|
@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem'
|
|||
Yew does not natively provide a CSS-in-Rust solution but helps with styling by providing
|
||||
programmatic ways to interact with the HTML `class` attribute.
|
||||
|
||||
## Classes
|
||||
## `classes!` macro
|
||||
|
||||
The `classes!` macro and associated `Classes` struct simplify the use of HTML classes:
|
||||
|
||||
|
@ -87,7 +87,7 @@ We will expand upon this concept in [more CSS](../../more/css).
|
|||
|
||||
## Inline Styles
|
||||
|
||||
Currently Yew does not provide any special help with inline styles specified via the `styles` attribute,
|
||||
Currently Yew does not provide any special help with inline styles specified via the `style` attribute,
|
||||
but you can use it like any other HTML attribute:
|
||||
|
||||
```rust
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: 'HTML with html!'
|
||||
description: 'Its HTML but not quite!'
|
||||
description: 'It is HTML but not quite!'
|
||||
comment: 'Keep this file as short and simple as possible. Its purpose is to ease the reader into components in Yew instead of providing proper API docs'
|
||||
---
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@ use wasm_bindgen::prelude::*;
|
|||
// correctness of these annotations!
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
|
||||
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
||||
// `log(..)`
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
|
@ -98,12 +97,12 @@ These implementations allow you to call a method from `A` on an instance of `C`
|
|||
it was `&B` or `&A`.
|
||||
|
||||
It is important to note that every single type imported using `#[wasm-bindgen]` has the same root type,
|
||||
you can think of it as the `A` in the example above, this type is [`JsValue`](#jsvalue) which has
|
||||
you can think of it as the `A` in the example above, this type is [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html) which has
|
||||
its section below.
|
||||
|
||||
_[extends section in The `wasm-bindgen` Guide](https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_
|
||||
|
||||
### [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html) {#jsvalue}
|
||||
### [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)
|
||||
|
||||
This is a representation of an object owned by JavaScript, this is a root catch-all type for `wasm-bindgen`.
|
||||
Any type that comes from `wasm-bindgen` is a `JsValue` and this is because JavaScript does not have
|
||||
|
@ -117,7 +116,7 @@ being imported as to whether an exception (panic) will be raised if that value i
|
|||
|
||||
_[`JsValue` documentation](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)._
|
||||
|
||||
### [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html) {#JsCast}
|
||||
### [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)
|
||||
|
||||
Rust has a strong type system and JavaScript...doesn't 😞. For Rust to maintain these
|
||||
strong types but still be convenient, the WebAssembly group came up with a pretty neat trait `JsCast`.
|
||||
|
|
|
@ -69,7 +69,6 @@ fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
|
|||
|
||||
// The AsRef implementations allow you to treat the HtmlTextAreaElement
|
||||
// as an &EventTarget.
|
||||
|
||||
let event_target: &EventTarget = text_area.as_ref();
|
||||
|
||||
}
|
||||
|
|
|
@ -78,11 +78,6 @@ fn App() -> Html {
|
|||
```rust
|
||||
use yew::{function_component, html, Html};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld() -> Html {
|
||||
html! { "Hello world" }
|
||||
|
|
|
@ -107,7 +107,7 @@ pub enum Msg {
|
|||
### Using `JsCast`
|
||||
|
||||
The [`wasm-bindgen`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) crate has
|
||||
a useful trait; [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)
|
||||
a useful trait: [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html),
|
||||
which allows us to hop and skip our way to the type we want, as long as it implements `JsCast`. We can
|
||||
do this cautiously, which involves some runtime checks and failure types like `Option` and `Result`,
|
||||
or we can do it dangerously.
|
||||
|
|
|
@ -83,7 +83,7 @@ html! {
|
|||
|
||||
We have [Keyed list](https://github.com/yewstack/yew/tree/master/examples/keyed_list) example that lets you test the performance improvements, but here is a rough rundown:
|
||||
|
||||
1. Go to [Keyed list](https://github.com/yewstack/yew/tree/master/examples/keyed_list) hosted demo
|
||||
1. Go to [Keyed list hosted demo](https://examples.yew.rs/keyed_list)
|
||||
2. Add 500 elements.
|
||||
3. Disable keys.
|
||||
4. Reverse the list.
|
||||
|
|
|
@ -190,7 +190,7 @@ routes. Except you supply a `to` attribute instead of a `href`. An example usage
|
|||
Struct variants work as expected too:
|
||||
|
||||
```rust ,ignore
|
||||
<Link<Route> to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew v0.19 out now!" }</Link<Route>>
|
||||
<Link<Route> to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew!" }</Link<Route>>
|
||||
```
|
||||
|
||||
#### Navigator API
|
||||
|
|
|
@ -123,10 +123,6 @@
|
|||
"message": "サイドバーを隠す",
|
||||
"description": "The title attribute for collapse button of doc sidebar"
|
||||
},
|
||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": {
|
||||
"message": "Toggle the collapsible sidebar category '{label}'",
|
||||
"description": "The ARIA label to toggle the collapsible sidebar category"
|
||||
},
|
||||
"theme.docs.tagDocListPageTitle.nDocsTagged": {
|
||||
"message": "One doc tagged|{count} docs tagged",
|
||||
"description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
|
||||
|
@ -207,10 +203,6 @@
|
|||
"message": "light mode",
|
||||
"description": "The name for the light color mode"
|
||||
},
|
||||
"theme.docs.DocCard.categoryDescription": {
|
||||
"message": "{count} items",
|
||||
"description": "The default description for a category card in the generated index about how many items this category includes"
|
||||
},
|
||||
"theme.docs.versionBadge.label": {
|
||||
"message": "Version: {versionLabel}"
|
||||
},
|
||||
|
@ -230,9 +222,6 @@
|
|||
"message": "Toggle word wrap",
|
||||
"description": "The title attribute for toggle word wrapping button of code block lines"
|
||||
},
|
||||
"theme.SearchBar.noResultsText": {
|
||||
"message": "No results"
|
||||
},
|
||||
"theme.SearchBar.seeAll": {
|
||||
"message": "See all results"
|
||||
},
|
||||
|
@ -308,14 +297,6 @@
|
|||
"message": "Collapse sidebar category '{label}'",
|
||||
"description": "The ARIA label to collapse the sidebar category"
|
||||
},
|
||||
"theme.unlistedContent.title": {
|
||||
"message": "Unlisted page",
|
||||
"description": "The unlisted content banner title"
|
||||
},
|
||||
"theme.unlistedContent.message": {
|
||||
"message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.",
|
||||
"description": "The unlisted content banner message"
|
||||
},
|
||||
"theme.SearchPage.inputPlaceholder": {
|
||||
"message": "検索するキーワードを入力してください",
|
||||
"description": "The placeholder for search page input"
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
---
|
||||
title: '子コンポーネント'
|
||||
---
|
||||
|
||||
:::caution
|
||||
|
||||
`Children` をチェックおよび操作すると、アプリケーションで驚くべきかつ説明が難しい動作が発生することがよくあります。これにより、エッジケースが発生し、通常は予期しない結果が生じる可能性があります。`Children` を操作しようとする場合は、他の方法を検討する必要があります。
|
||||
|
||||
Yew は、子コンポーネントのプロパティの型として `Html` を使用することをサポートしています。`Children` または `ChildrenRenderer` が必要ない場合は、子コンポーネントとして `Html` を使用することをお勧めします。これは `Children` の欠点がなく、パフォーマンスのオーバーヘッドも低くなります。
|
||||
|
||||
:::
|
||||
|
||||
## 一般的な使用法
|
||||
|
||||
_ほとんどの場合、_ コンポーネントに子コンポーネントを持たせる場合、子コンポーネントの型を気にする必要はありません。この場合、以下の例で十分です。
|
||||
|
||||
````rust
|
||||
use yew::{html, Component, Context, Html, Properties};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ListProps {
|
||||
#[prop_or_default]
|
||||
pub children: Html,
|
||||
}
|
||||
|
||||
pub struct List;
|
||||
|
||||
impl Component for List {
|
||||
type Message = ();
|
||||
type Properties = ListProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="list">
|
||||
{ctx.props().children.clone()}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## 高度な使用法
|
||||
|
||||
### 型指定された子コンポーネント
|
||||
|
||||
特定のタイプのコンポーネントを子コンポーネントとして渡したい場合は、`yew::html::ChildrenWithProps<T>` を使用できます。
|
||||
|
||||
```rust
|
||||
use yew::{html, ChildrenWithProps, Component, Context, Html, Properties};
|
||||
|
||||
pub struct Item;
|
||||
|
||||
impl Component for Item {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "item" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ListProps {
|
||||
#[prop_or_default]
|
||||
pub children: ChildrenWithProps<Item>,
|
||||
}
|
||||
|
||||
pub struct List;
|
||||
|
||||
impl Component for List {
|
||||
type Message = ();
|
||||
type Properties = ListProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="list">
|
||||
{ for ctx.props().children.iter() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
## プロパティを持つネストされた子コンポーネント
|
||||
|
||||
コンポーネントがその子コンポーネントを型指定している場合、ネストされたコンポーネントのプロパティにアクセスして変更することができます。
|
||||
|
||||
```rust
|
||||
use std::rc::Rc;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ListItemProps {
|
||||
value: String,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn ListItem(props: &ListItemProps) -> Html {
|
||||
let ListItemProps { value } = props.clone();
|
||||
html! {
|
||||
<span>
|
||||
{value}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub children: ChildrenWithProps<ListItem>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn List(props: &Props) -> Html {
|
||||
let modified_children = props.children.iter().map(|mut item| {
|
||||
let mut props = Rc::make_mut(&mut item.props);
|
||||
props.value = format!("item-{}", props.value);
|
||||
item
|
||||
});
|
||||
html! { for modified_children }
|
||||
}
|
||||
|
||||
html! {
|
||||
<List>
|
||||
<ListItem value="a" />
|
||||
<ListItem value="b" />
|
||||
<ListItem value="c" />
|
||||
</List>
|
||||
};
|
||||
```
|
||||
|
||||
### 列挙型の子コンポーネント
|
||||
|
||||
もちろん、時には子コンポーネントをいくつかの異なるコンポーネントに制限する必要がある場合があります。そのような場合には、Yewについてさらに深く理解する必要があります。
|
||||
|
||||
ここでは、より良いエルゴノミクスを提供するために [`derive_more`](https://github.com/JelteF/derive_more) を使用しています。使用したくない場合は、各バリアントに対して手動で `From` を実装することができます。
|
||||
|
||||
```rust
|
||||
use yew::{
|
||||
html, html::ChildrenRenderer, virtual_dom::VChild, Component,
|
||||
Context, Html, Properties,
|
||||
};
|
||||
|
||||
pub struct Primary;
|
||||
|
||||
impl Component for Primary {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "Primary" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Secondary;
|
||||
|
||||
impl Component for Secondary {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "Secondary" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, derive_more::From, PartialEq)]
|
||||
pub enum Item {
|
||||
Primary(VChild<Primary>),
|
||||
Secondary(VChild<Secondary>),
|
||||
}
|
||||
|
||||
// 現在、`Into<Html>` を実装して、yew が `Item` をどのようにレンダリングするかを知ることができるようにします。
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<Html> for Item {
|
||||
fn into(self) -> Html {
|
||||
match self {
|
||||
Self::Primary(child) => child.into(),
|
||||
Self::Secondary(child) => child.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ListProps {
|
||||
#[prop_or_default]
|
||||
pub children: ChildrenRenderer<Item>,
|
||||
}
|
||||
|
||||
pub struct List;
|
||||
|
||||
impl Component for List {
|
||||
type Message = ();
|
||||
type Properties = ListProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="list">
|
||||
{ for ctx.props().children.iter() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### オプションの型の子コンポーネント
|
||||
|
||||
特定の型の単一のオプションの子コンポーネントを持つこともできます:
|
||||
|
||||
```rust
|
||||
use yew::{
|
||||
html, html_nested, virtual_dom::VChild, Component,
|
||||
Context, Html, Properties
|
||||
};
|
||||
|
||||
pub struct PageSideBar;
|
||||
|
||||
impl Component for PageSideBar {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "sidebar" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct PageProps {
|
||||
#[prop_or_default]
|
||||
pub sidebar: Option<VChild<PageSideBar>>,
|
||||
}
|
||||
|
||||
struct Page;
|
||||
|
||||
impl Component for Page {
|
||||
type Message = ();
|
||||
type Properties = PageProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="page">
|
||||
{ ctx.props().sidebar.clone().map(Html::from).unwrap_or_default() }
|
||||
// ... ページ内容
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ページコンポーネントはサイドバーを含むかどうかを選択できます:
|
||||
|
||||
pub fn render_page(with_sidebar: bool) -> Html {
|
||||
if with_sidebar {
|
||||
// サイドバーを含むページ
|
||||
html! {
|
||||
<Page sidebar={html_nested! {
|
||||
<PageSideBar />
|
||||
}} />
|
||||
}
|
||||
} else {
|
||||
// サイドバーを含まないページ
|
||||
html! {
|
||||
<Page />
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## さらに読む
|
||||
|
||||
- このパターンの実際の例については、yew-router のソースコードを参照してください。より高度な例については、yew リポジトリの[関連する例のリスト](https://github.com/yewstack/yew/tree/master/examples/nested_list)を参照してください。
|
|
@ -1,8 +1,39 @@
|
|||
---
|
||||
title: How it works
|
||||
description: Low level details about the framework
|
||||
title: '仕組み'
|
||||
description: 'フレームワークの低レベルの詳細について'
|
||||
---
|
||||
|
||||
# 低レベルなライブラリの中身
|
||||
# 基本ライブラリの内部詳細
|
||||
|
||||
コンポーネントのライフサイクルの状態機械、VDOM の異なるアルゴリズム
|
||||
## `html!` マクロの内部
|
||||
|
||||
`html!` マクロは、HTMLに似たカスタム構文で記述されたコードを有効なRustコードに変換します。このマクロを使用することはYewアプリケーションの開発に必須ではありませんが、推奨されています。このマクロが生成するコードはYewのパブリックライブラリAPIを使用しており、希望すれば直接使用することもできます。いくつかのメソッドは意図的に文書化されていないため、誤用を避けるために注意が必要です。`yew-macro`の各更新により、生成されるコードはより効率的になり、`html!`構文をほとんど(または全く)変更することなく破壊的な変更を処理できるようになります。
|
||||
|
||||
`html!` マクロを使用すると、宣言的なスタイルでコードを記述できるため、UIレイアウトコードはページのHTMLに非常に似たものになります。アプリケーションがよりインタラクティブになり、コードベースが大きくなるにつれて、この方法はますます有用になります。DOM 操作のすべてのコードを手動で記述するのに比べて、マクロがこれらすべてを処理してくれます。
|
||||
|
||||
`html!` マクロの使用は非常に魔法のように感じるかもしれませんが、隠すべきものは何もありません。その仕組みに興味がある場合は、プログラム内の `html!` マクロ呼び出しを展開してみてください。`cargo expand` という便利なコマンドがあり、Rustマクロの展開を確認できます。`cargo expand` はデフォルトで `cargo` に含まれていないため、まだインストールしていない場合は `cargo install cargo-expand` を使用してインストールする必要があります。[Rust-Analyzer](https://rust-analyzer.github.io/) も[IDEからマクロ出力を取得するメカニズム](https://rust-analyzer.github.io/manual.html#expand-macro-recursively)を提供しています。
|
||||
|
||||
`html!` マクロの出力は通常非常に簡潔です!これは特徴です:機械生成のコードは時々アプリケーション内の他のコードと衝突することがあります。問題を防ぐために、`proc_macro` は「衛生」ルールに従っています。いくつかの例を以下に示します:
|
||||
|
||||
1. Yewパッケージを正しく参照するために、マクロ生成コードでは `::yew::<module>` を使用し、直接 `yew::<module>` を使用しません。これは `::alloc::vec::Vec::new()` を呼び出すのと同じ理由です。
|
||||
2. トレイトメソッド名の衝突を避けるために、`<Type as Trait>` を使用して正しいトレイトメンバーを使用していることを確認します。
|
||||
|
||||
## 仮想 DOM とは?
|
||||
|
||||
DOM(「ドキュメントオブジェクトモデル」)は、ブラウザによって管理されるHTMLコンテンツの表現です。「仮想」 DOM は、単にメモリ内の DOM のコピーです。仮想 DOM を管理することで、メモリのオーバーヘッドが増加しますが、ブラウザAPIの使用を回避または遅延させることでバッチ処理と高速な読み取りを実現できます。
|
||||
|
||||
メモリ内に DOM のコピーを持つことは、宣言的UIを使用するライブラリの使用を促進するのに役立ちます。ユーザーイベントに基づいて DOM を変更するための特定のコードが必要な場合とは異なり、ライブラリは一般的な方法を使用して DOM の「差分」を行うことができます。Yewコンポーネントが更新され、そのレンダリング方法を変更したい場合、Yewライブラリは仮想 DOM の2番目のコピーを構築し、現在画面上に表示されている内容をミラーリングする仮想 DOM と直接比較します。両者の「差分」は増分更新に分解され、ブラウザAPIと共に適用されます。更新が適用されると、古い仮想 DOM のコピーは破棄され、新しいコピーが将来の差分チェックのために保存されます。
|
||||
|
||||
この「差分」アルゴリズムは、時間の経過とともに最適化され、複雑なアプリケーションのパフォーマンスを向上させることができます。YewアプリケーションはWebAssemblyを介して実行されるため、Yewは将来的により複雑なアルゴリズムを採用する上で競争力を持つと信じています。
|
||||
|
||||
Yewの仮想 DOM はブラウザの DOM と完全に一対一対応しているわけではありません。DOM 要素を整理するための「リスト」や「コンポーネント」も含まれています。リストは単に要素の順序付きリストである場合もありますが、より強力な場合もあります。各リスト要素に「キー」注釈を追加することで、アプリケーション開発者はリストが変更されたときに差分更新の計算に必要な作業量を最小限に抑えるための追加の最適化をYewに提供できます。同様に、コンポーネントは再レンダリングが必要かどうかを示すカスタムロジックを提供し、パフォーマンスを向上させるのに役立ちます。
|
||||
|
||||
## Yewスケジューラとコンポーネントスコープのイベントループ
|
||||
|
||||
_貢献ドキュメント - `yew::scheduler` と `yew::html::scope` の仕組みを詳しく説明_
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [Rustのマクロに関する詳細情報](https://doc.rust-lang.org/stable/book/ch19-06-macros.html)
|
||||
- [`cargo-expand` に関する詳細情報](https://github.com/dtolnay/cargo-expand)
|
||||
- [`yew::virtual_dom` のAPIドキュメント](https://docs.rs/yew/*/yew/virtual_dom/index.html)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
title: 'イミュータブルタイプ'
|
||||
description: 'Yew のイミュータブルデータ構造'
|
||||
---
|
||||
|
||||
## イミュータブルタイプとは?
|
||||
|
||||
これらのタイプは、インスタンス化はできるが値を変更することはできないタイプです。値を更新するには、新しい値をインスタンス化する必要があります。
|
||||
|
||||
## なぜイミュータブルタイプを使用するのですか?
|
||||
|
||||
React と同様に、プロパティは祖先から子孫に伝播されます。これは、各コンポーネントが更新されるたびにプロパティが存在する必要があることを意味します。したがって、プロパティは理想的には簡単にクローンできるべきです。これを実現するために、通常は `Rc` にラップします。
|
||||
|
||||
イミュータブルタイプは、コンポーネント間でプロパティの値を低コストでクローンできるため、プロパティの値を保持するのに最適です。
|
||||
|
||||
## さらに読む
|
||||
|
||||
- [イミュータブルの例](https://github.com/yewstack/yew/tree/master/examples/immutable)
|
||||
- [Crate `implicit-clone`](https://docs.rs/implicit-clone/)
|
|
@ -1,171 +1,112 @@
|
|||
---
|
||||
title: Optimizations
|
||||
description: Make your app faster
|
||||
title: '最適化とベストプラクティス'
|
||||
sidebar_label: Optimizations
|
||||
description: 'アプリケーションのパフォーマンスを最適化する'
|
||||
---
|
||||
|
||||
# 最適化とベストプラクティス
|
||||
## スマートポインタの使用
|
||||
|
||||
## neq_assign
|
||||
**注意:このセクションで使用されている用語に混乱がある場合は、Rustのマニュアルにある[スマートポインタに関する章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)が役立ちます。**
|
||||
|
||||
親コンポーネントから props を受け取った際、`change`メソッドが呼ばれます。
|
||||
これはコンポーネントの状態を更新することができるのに加え、コンポーネントが props が変わった際に再レンダリングするかどうかを決める
|
||||
`ShouldRender`という真偽値を返すことができます。
|
||||
再レンダリング時に大量のデータをクローンしてpropsを作成するのを避けるために、スマートポインタを使用してデータ自体ではなくデータへの参照のみをクローンすることができます。propsや子コンポーネントに関連データの参照を渡すことで、データを変更する必要がある子コンポーネントでデータをクローンするのを避けることができます。`Rc::make_mut`を使用してデータをクローンし、変更するための可変参照を取得できます。
|
||||
|
||||
再レンダリングはコストがかかるもので、もし避けられるのであれば避けるべきです。
|
||||
一般的なルールとして props が実際に変化した際にのみ再レンダリングすれば良いでしょう。
|
||||
以下のコードブロックはこのルールを表しており、props が前と変わったときに`true`を返します。
|
||||
これにより、`Component::changed`でのpropの変更がコンポーネントの再レンダリングを必要とするかどうかを判断する際にさらに利点があります。これは、データの値ではなくポインタのアドレス(つまり、データがマシンメモリに格納されている場所)を比較できるためです。2つのポインタが同じデータを指している場合、それらが指しているデータの値は同じでなければなりません。逆は必ずしも真ではないことに注意してください!2つのポインタアドレスが異なる場合でも、基になるデータは同じである可能性があります。この場合、基になるデータを比較する必要があります。
|
||||
|
||||
```rust
|
||||
use yew::ShouldRender;
|
||||
この比較を行うには、`PartialEq`(データを比較する際に自動的に使用される等価演算子`==`)ではなく、`Rc::ptr_eq`を使用する必要があります。Rustのドキュメントには、`Rc::ptr_eq`に関する[詳細](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq)があります。
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct ExampleProps;
|
||||
この最適化は、`Copy`を実装していないデータ型に最も有用です。データを安価にコピーできる場合、それをスマートポインタの後ろに置く必要はありません。`Vec`、`HashMap`、`String`などのデータ集約型の構造体に対して、スマートポインタを使用することでパフォーマンスの向上が見込まれます。
|
||||
|
||||
struct Example {
|
||||
props: ExampleProps,
|
||||
};
|
||||
この最適化は、子コンポーネントが値を更新しない場合に最も効果的であり、親コンポーネントがほとんど更新されない場合にさらに効果的です。これにより、`Rc<_>`は純粋なコンポーネントでpropsの値をラップするのに適した選択肢となります。
|
||||
|
||||
impl Example {
|
||||
fn change(&mut self, props: ExampleProps) -> ShouldRender {
|
||||
if self.props != props {
|
||||
self.props = props;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
ただし、子コンポーネントでデータを自分でクローンする必要がない限り、この最適化は無駄であり、不要な参照カウントのコストを追加するだけです。Yewのpropsはすでに参照カウントされており、内部でデータのクローンは行われません。
|
||||
|
||||
しかし我々は先に進んでいけます!
|
||||
この 6 行のボイラープレードは`PartialEq`を実装したものにトレイトとブランケットを用いることで 1 行のコードへと落とし込むことができます。
|
||||
[こちら](https://docs.rs/yewtil/*/yewtil/trait.NeqAssign.html)にて`yewtil`クレートの`NewAssign`トレイトを見てみてください。
|
||||
## レンダリング関数
|
||||
|
||||
## 効果的にスマートポインタを使う
|
||||
|
||||
**注意: このセクションで使われている用語がわからなければ Rust book は
|
||||
[スマートポインタについての章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
|
||||
があり、非常に有用です。**
|
||||
|
||||
再レンダリングの際に props を作るデータを大量にコピーしないために、スマートポインタを用いてデータ自体ではなくデータへの参照だけを
|
||||
コピーできます。
|
||||
props や子コンポーネントで関連するデータに実データではなく参照を渡すと、子コンポーネントでデータを変更する必要がなければ
|
||||
データのコピーを避けることができます。
|
||||
その際、`Rc::make_mut`によって変更したいデータの変更可能な参照を得ることができます。
|
||||
|
||||
これにより、props が変更されたときにコンポーネントが再レンダリングされるかどうかを決めるかで`Component::change`に更なる恩恵があります。
|
||||
なぜなら、データの値を比較する代わりに元々のポインタのアドレス (つまりデータが保管されている機械のメモリの場所) を比較できるためです。
|
||||
2 つのポインターが同じデータを指す場合、それらのデータの値は同じでなければならないのです。
|
||||
ただし、その逆は必ずしも成り立たないことに注意してください!
|
||||
もし 2 つのポインタが異なるのであれば、そのデータは同じである可能性があります。
|
||||
この場合はデータを比較するべきでしょう。
|
||||
|
||||
この比較においては`PartialEq`ではなく`Rc::ptr_eq`を使う必要があります。
|
||||
`PartialEq`は等価演算子`==`を使う際に自動的に使われます。
|
||||
Rust のドキュメントには[`Rc::ptr_eq`についてより詳しく書いてあります](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq)。
|
||||
|
||||
この最適化は`Copy`を実装していないデータの型に対して極めて有効なものです。
|
||||
もしデータを簡単に変更できるのであれば、スマートポインタに取り換える必要はありません。
|
||||
しかし`Vec`や`HashMap`、`String`などのような重たいデータの構造体に対してはスマートポインタを使うことで
|
||||
パフォーマンスを改善することができるでしょう。
|
||||
|
||||
この最適化は値がまだ一度も子によって更新されていない場合に極めて有効で、親からほとんど更新されない場合においてもかなり有効です。
|
||||
これにより、`Rc<_>s`が純粋なコンポーネントに対してプロパティの値をラップする良い一手となります。
|
||||
|
||||
## View 関数
|
||||
|
||||
コードの可読性の理由から`html!`の部分を関数へと移植するのは意味があります。
|
||||
これは、インデントを減らすのでコードを読みやすくするだけでなく、良いデザインパターンを産むことにも繋がるのです。
|
||||
これらの関数は複数箇所で呼ばれて書くべきコード量を減らせるため、分解可能なアプリケーションを作ることができるのです。
|
||||
コードの可読性のために、`html!`の一部の繰り返しコードを専用の分割関数に移行することは通常意味があります。これにより、コードが読みやすくなり、インデントが減り、良いデザインパターンを奨励します。特に、複数の場所で呼び出すことができるこれらの関数を使用して、コード量を減らすことができます。
|
||||
|
||||
## 純粋なコンポーネント
|
||||
|
||||
純粋なコンポーネントは状態を変化せず、ただ中身を表示してメッセージを普通の変更可能なコンポーネントへ渡すコンポーネントのことです。
|
||||
View 関数との違いとして、純粋なコンポーネントは式の構文\(`{some_view_function()}`\)ではなく
|
||||
コンポーネントの構文\(`<SomePureComponent />`\)を使うことで`html!`マクロの中で呼ばれる点、
|
||||
そして実装次第で記憶され (つまり、一度関数が呼ばれれば値は"保存"され、
|
||||
同じ引数でもう一度呼ばれても値を再計算する必要がなく最初に関数が呼ばれたときの保存された値を返すことができる)、
|
||||
先述の`neq_assign`ロジックを使う別々の props で再レンダリングを避けられる点があります。
|
||||
純粋なコンポーネントは、その状態を変更せず、コンテンツを表示し、メッセージを通常の可変コンポーネントに伝播するコンポーネントです。これらは、`html!`マクロ内でコンポーネント構文(`<SomePureComponent />`)を使用する点でビュー関数とは異なり、実装に応じてメモ化される可能性があります(これは、一度関数が呼び出されると、その値が「保存」されることを意味し、同じパラメータで複数回呼び出された場合、その値を再計算する必要がなく、最初の関数呼び出しから保存された値を返すだけです)。Yewは内部でpropsを比較するため、propsが変更された場合にのみUIを再レンダリングします。
|
||||
|
||||
Yew は純粋な関数やコンポーネントをサポートしていませんが、外部のクレートを用いることで実現できます。
|
||||
## ワークスペースを使用してコンパイル時間を短縮する
|
||||
|
||||
## 関数型コンポーネント (a.k.a フック)
|
||||
Yewの最大の欠点は、コンパイルにかかる時間が長いことです。プロジェクトのコンパイルにかかる時間は、`html!`マクロに渡されるコードの量に関連しているようです。小規模なプロジェクトでは問題にならないようですが、大規模なアプリケーションでは、コンパイラがアプリケーションのために行う作業量を最小限に抑えるためにコードを複数のクレートに分割することが理にかなっています。
|
||||
|
||||
関数型コンポーネントはまだ開発中です!
|
||||
開発状況については[プロジェクトボード](https://github.com/yewstack/yew/projects/3)に詳しく書いてあります。
|
||||
1つの方法として、メインクレートがルーティング/ページ選択を処理し、各ページごとに異なるクレートを作成することが考えられます。各ページは異なるコンポーネントまたは`Html`を生成する大きな関数である可能性があります。アプリケーションの異なる部分を含むクレート間で共有されるコードは、プロジェクトが依存する別のクレートに格納できます。理想的には、すべてのコードを再コンパイルするのではなく、メインクレートと1つのページクレートのみを再コンパイルすることになります。最悪の場合、「共通」クレートで何かを編集した場合、すべての依存コードを再コンパイルする必要があり、元の状態に戻ります。
|
||||
|
||||
## キー付き DOM ノード
|
||||
メインクレートが重すぎる場合や、深くネストされたページ(例:別のページ上にレンダリングされるページ)を迅速に反復したい場合は、メインページの簡略化された実装を作成し、作業中のコンポーネントを追加でレンダリングするためにサンプルクレートを使用できます。
|
||||
|
||||
## ワークスペースでコンパイル時間を減らす
|
||||
## バイナリサイズの縮小
|
||||
|
||||
間違いなく Yew を使う上での最大の欠点はコンパイルに時間がかかる点です。
|
||||
プロジェクトのコンパイルにかかる時間は`html!`マクロに渡されるコードの量に関係しています。
|
||||
これは小さなプロジェクトにはそこまで問題ないようですが、大きなアプリではコードを複数クレートに分割することでアプリに変更が加られた際に
|
||||
コンパイラの作業量を減らすのが有効です。
|
||||
- Rustコードの最適化
|
||||
- `cargo.toml`(リリースプロファイルの定義)
|
||||
- `wasm-opt` を使用してwasmコードを最適化
|
||||
|
||||
一つ可能なやり方として、ルーティングとページ洗濯を担当するメインのクレートを作り、それぞれのページに対して別のクレートを作ることです。
|
||||
そうして各ページは異なるコンポーネントか、`Html`を生成する大きな関数となります。
|
||||
アプリの異なる部分を含むクレート同士で共有されるコードはプロジェクト全体で依存する分離したクレートに保存されます。
|
||||
理想的には 1 回のコンパイルでコード全てを再ビルドせずメインのクレートかどれかのページのクレートを再ビルドするだけにすることです。
|
||||
最悪なのは、"共通"のクレートを編集して、はじめに戻ってくることです:
|
||||
共有のクレートに依存している全てのコード、恐らく全てのコードをコンパイルすることです。
|
||||
|
||||
もしメインのクレートが重たすぎる、もしくは深くネストしたページ (例えば別ページのトップでレンダリングされるページ)
|
||||
で速く繰り返したい場合、クレートの例を用いてメインページの実装をシンプルにしたりトップで動かしているコンポーネントをレンダリングできます。
|
||||
|
||||
## バイナリサイズを小さくする
|
||||
|
||||
- Rust のコードを最適化する
|
||||
- `cargo.toml` \( release profile を定義 \)
|
||||
- `wasm-opt`を用いて wasm のコードを最適化する
|
||||
|
||||
**注意: バイナリサイズを小さくするのについては[Rust Wasm Book](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)に詳しく書いてあります。**
|
||||
**注意:バイナリサイズの縮小に関する詳細は、[Rust Wasmマニュアル](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)を参照してください。**
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
`Cargo.toml`で`[profile.release]`のセクションに設定を書き込むことでリリースビルドを小さくすることが可能です。
|
||||
リリースビルドをより小さくするために、`Cargo.toml`の`[profile.release]`セクションで利用可能な設定を使用して構成できます。
|
||||
|
||||
```text
|
||||
```toml, title=Cargo.toml
|
||||
[profile.release]
|
||||
# バイナリに含むコードを少なくする
|
||||
# バイナリサイズを小さくする
|
||||
panic = 'abort'
|
||||
# コードベース全体での最適化 ( 良い最適化だがビルドが遅くなる)
|
||||
# コード全体を最適化する(最適化は良くなるが、ビルド速度は遅くなる)
|
||||
codegen-units = 1
|
||||
# サイズの最適化( よりアグレッシブに )
|
||||
# サイズを最適化する(より積極的なアプローチ)
|
||||
opt-level = 'z'
|
||||
# サイズの最適化
|
||||
# サイズを最適化する
|
||||
# opt-level = 's'
|
||||
# プログラム全体の分析によるリンク時最適化
|
||||
# プログラム全体の解析を使用してリンク時に最適化
|
||||
lto = true
|
||||
```
|
||||
|
||||
### 開発版 Cargo 設定
|
||||
|
||||
Rust と cargo の実験的な開発版機能から追加の利点を得ることもできます。`trunk` の開発版ツールチェーンを使用するには、`RUSTUP_TOOLCHAIN="nightly"` 環境変数を設定します。その後、`.cargo/config.toml` で不安定な rustc 機能を構成できます。不安定機能のドキュメント、特に[`build-std`]および[`build-std-features`]に関する部分を参照して、設定方法を確認してください。
|
||||
|
||||
```toml, title=".cargo/config.toml"
|
||||
[unstable]
|
||||
# rust-srcコンポーネントが必要です。`rustup +nightly component add rust-src`
|
||||
build-std = ["std", "panic_abort"]
|
||||
build-std-features = ["panic_immediate_abort"]
|
||||
```
|
||||
|
||||
[不安定な機能のリスト]: https://doc.rust-lang.org/cargo/reference/unstable.html
|
||||
[`build-std`]: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std
|
||||
[`build-std-features`]: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std-features
|
||||
|
||||
:::caution
|
||||
開発版のRustコンパイラには、[この例](https://github.com/yewstack/yew/issues/2696)のようなバグが含まれている可能性があるため、定期的に監視し調整する必要があります。これらの実験的なオプションを使用する際は注意が必要です。
|
||||
:::
|
||||
|
||||
### wasm-opt
|
||||
|
||||
更に`wasm`のコードのサイズを最適化することができます。
|
||||
さらに、`wasm` コードのサイズを最適化することができます。
|
||||
|
||||
The Rust Wasm Book には Wasm バイナリのサイズを小さくすることについてのセクションがあります:
|
||||
[Shrinking .wasm size](https://rustwasm.github.io/book/game-of-life/code-size.html)
|
||||
Rust Wasm マニュアルには、Wasm バイナリファイルのサイズを縮小する方法に関するセクションがあります:[.wasm サイズの縮小](https://rustwasm.github.io/book/game-of-life/code-size.html)
|
||||
|
||||
- `wasm-pack`でデフォルトの`wasm`のコードをリリースビルド時に最適化する
|
||||
- `wasm-opt`によって直接`wasm`ファイルを最適化する
|
||||
- `wasm-pack` を使用すると、デフォルトでリリースビルドの `wasm` コードが最適化されます
|
||||
- `wasm` ファイルに直接 `wasm-opt` を使用する
|
||||
|
||||
```text
|
||||
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
|
||||
```
|
||||
|
||||
#### yew/examples/にある例を小さなサイズでビルドする
|
||||
#### yew/examples/ の 'minimal' サンプルのビルドサイズ
|
||||
|
||||
注意: `wasm-pack`は Rust と Wasm のコードへの最適化を組み合わせます。`wasm-bindgen`はこの例では Rust のサイズ最適化を用いていません。
|
||||
注意:`wasm-pack` は Rust と Wasm コードの最適化を組み合わせています。この例では、`wasm-bindgen` は Rust のサイズ最適化を行っていません。
|
||||
|
||||
| 使用したツール | サイズ |
|
||||
| ツールチェーン | サイズ |
|
||||
| :-------------------------- | :----- |
|
||||
| wasm-bindgen | 158KB |
|
||||
| wasm-bindgen + wasm-opt -Os | 116KB |
|
||||
| wasm-pack | 99 KB |
|
||||
|
||||
## 参考文献:
|
||||
## さらに読む
|
||||
|
||||
- [The Rust Book のスマートポインタに関する章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
|
||||
- [the Rust Wasm Book でのバイナリサイズを小さくすることについて](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
|
||||
- [Rust profiles についてのドキュメント](https://doc.rust-lang.org/cargo/reference/profiles.html)
|
||||
- [Rust マニュアルのスマート ポインターに関する章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
|
||||
- [Rust Wasm マニュアルのコードサイズの縮小に関する章](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
|
||||
- [Rust プロファイルに関するドキュメント](https://doc.rust-lang.org/cargo/reference/profiles.html)
|
||||
- [binaryen プロジェクト](https://github.com/WebAssembly/binaryen)
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
title: 'ポータル (Portals)'
|
||||
description: 'コンテンツをDOMツリー外のノードにレンダリングする'
|
||||
---
|
||||
|
||||
## ポータルとは?
|
||||
|
||||
ポータル (Portal) は、子要素を親コンポーネントのDOM階層外のDOMノードにレンダリングする方法を提供します。`yew::create_portal(child, host)` は `Html` 値を返し、`child` を `host` 要素の子要素としてレンダリングしますが、親コンポーネントの階層下ではありません。
|
||||
|
||||
## 使用方法
|
||||
|
||||
ポータルの典型的な用途には、モーダルダイアログやホバーカード、さらに技術的な用途として、要素の [`shadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot) の内容を制御すること、スタイルシートを周囲のドキュメントの `<head>` に添付すること、`<svg>` の中央の `<defs>` 要素に参照される要素を収集することなどがあります。
|
||||
|
||||
`yew::create_portal` は低レベルの構成要素であることに注意してください。ライブラリはこれを使用してより高レベルのAPIを実装し、その後アプリケーションはこれらのAPIを使用できます。例えば、ここでは `children` を `yew` 以外の要素にレンダリングするシンプルなモーダルダイアログを示します。この要素は `id="modal_host"` で識別されます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ModalProps {
|
||||
#[prop_or_default]
|
||||
pub children: Html,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Modal(props: &ModalProps) -> Html {
|
||||
let modal_host = gloo::utils::document()
|
||||
.get_element_by_id("modal_host")
|
||||
.expect("Expected to find a #modal_host element");
|
||||
|
||||
create_portal(
|
||||
props.children.clone(),
|
||||
modal_host.into(),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## イベント処理
|
||||
|
||||
ポータル内部の要素で発生するイベントは、仮想DOMのバブリングに従います。つまり、ポータルが要素の子要素としてレンダリングされる場合、その要素上のイベントリスナーは、ポータル内部から発生するイベントをキャプチャします。たとえポータルが実際のDOM内の無関係な位置にその内容をレンダリングしていてもです。
|
||||
|
||||
これにより、開発者は使用しているコンポーネントがポータルを使用して実装されているかどうかを気にする必要がなくなります。いずれにせよ、その子要素上で発生するイベントはバブリングします。
|
||||
|
||||
既知の問題として、ポータルから **閉じた** シャドウルートへのイベントは2回分配されます。1回はシャドウルート内部の要素に対して、もう1回はホスト要素自体に対してです。**開いた** シャドウルートは正常に動作しますので、これが影響する場合は、いつでもバグレポートを提出してください。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [ポータルの例](https://github.com/yewstack/yew/tree/master/examples/portals)
|
|
@ -0,0 +1,184 @@
|
|||
---
|
||||
title: 'サーバーサイドレンダリング'
|
||||
description: 'Yewコンポーネントをサーバーサイドでレンダリングする。'
|
||||
---
|
||||
|
||||
# サーバーサイドレンダリング (Server-Side Rendering)
|
||||
|
||||
デフォルトでは、Yewコンポーネントはクライアントサイドでレンダリングされます。ユーザーがウェブサイトにアクセスすると、サーバーは実際のコンテンツを含まない骨組みのHTMLファイルとWebAssemblyパッケージをブラウザに送信します。すべてのコンテンツはクライアントサイドでWebAssemblyパッケージによってレンダリングされます。これをクライアントサイドレンダリングと呼びます。
|
||||
|
||||
この方法はほとんどのウェブサイトにとって有効ですが、いくつかの注意点があります:
|
||||
|
||||
1. ユーザーはWebAssemblyパッケージがダウンロードされ、初期レンダリングが完了するまで何も表示されません。これにより、ネットワークが遅い場合にユーザーエクスペリエンスが悪化する可能性があります。
|
||||
2. 一部の検索エンジンは動的にレンダリングされたウェブページのコンテンツをサポートしておらず、サポートしている検索エンジンでも通常は動的なウェブサイトのランキングが低くなります。
|
||||
|
||||
これらの問題を解決するために、ウェブサイトをサーバーサイドでレンダリングすることができます。
|
||||
|
||||
## 動作原理
|
||||
|
||||
Yewはページをサーバーサイドでレンダリングするための `ServerRenderer` を提供しています。
|
||||
|
||||
Yewコンポーネントをサーバーサイドでレンダリングするには、`ServerRenderer::<App>::new()` を使用してレンダラーを作成し、`renderer.render().await` を呼び出して `<App />` を `String` としてレンダリングします。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use yew::ServerRenderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! {<div>{"Hello, World!"}</div>}
|
||||
}
|
||||
|
||||
// この例が CI の WASM 環境で動作することを保証するために `flavor = "current_thread"` を使用しています。
|
||||
// マルチスレッドを使用したい場合は、デフォルトの `#[tokio::main]` マクロを使用できます。
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn no_main() {
|
||||
let renderer = ServerRenderer::<App>::new();
|
||||
|
||||
let rendered = renderer.render().await;
|
||||
|
||||
// プリント: <div>Hello, World!</div>
|
||||
println!("{}", rendered);
|
||||
}
|
||||
```
|
||||
|
||||
## コンポーネントのライフサイクル
|
||||
|
||||
クライアントサイドレンダリングとは異なり、サーバーサイドレンダリング時のコンポーネントのライフサイクルは異なります。
|
||||
|
||||
コンポーネントが最初に `Html` として正常にレンダリングされるまで、`use_effect`(および `use_effect_with`)以外のすべてのフックは正常に動作します。
|
||||
|
||||
:::caution ブラウザインターフェースは利用できません!
|
||||
|
||||
`web_sys` などのブラウザ関連のインターフェースは、サーバーサイドレンダリング時には利用できません。これらを使用しようとすると、アプリケーションがクラッシュします。このロジックは `use_effect` または `use_effect_with` に隔離する必要があります。これらはサーバーサイドレンダリング時には実行されないためです。
|
||||
|
||||
:::
|
||||
|
||||
:::danger 構造化コンポーネント
|
||||
|
||||
サーバーサイドレンダリング時に構造化コンポーネントを使用することは可能ですが、クライアントサイドの安全なロジック(関数コンポーネントの `use_effect` フックなど)とライフサイクルイベントの間には明確な境界がなく、ライフサイクルイベントの呼び出し順序もクライアントとは異なります。
|
||||
|
||||
さらに、構造化コンポーネントは、すべての子コンポーネントがレンダリングされ `destroy` メソッドが呼び出されるまでメッセージを受け取り続けます。開発者は、コンポーネントに渡される可能性のあるメッセージがブラウザインターフェースを呼び出すロジックにリンクされないようにする必要があります。
|
||||
|
||||
サーバーサイドレンダリングをサポートするアプリケーションを設計する際は、特別な理由がない限り、関数コンポーネントを使用することをお勧めします。
|
||||
|
||||
:::
|
||||
|
||||
## サーバーサイドレンダリング中のデータ取得
|
||||
|
||||
データ取得はサーバーサイドレンダリングとハイドレーション(hydration)中の難点の一つです。
|
||||
|
||||
従来の方法では、コンポーネントがレンダリングされるとすぐに利用可能になります(仮想DOMを出力してレンダリングします)。コンポーネントがデータを取得する必要がない場合、この方法は有効です。しかし、コンポーネントがレンダリング時にデータを取得しようとするとどうなるでしょうか?
|
||||
|
||||
以前は、Yewにはコンポーネントがまだデータを取得しているかどうかを検出するメカニズムがありませんでした。データ取得クライアントは、初期レンダリング中に何が要求されたかを検出し、要求が完了した後に再レンダリングをトリガーするソリューションを実装する責任がありました。サーバーはこのプロセスを繰り返し、応答を返す前にレンダリング中に追加の保留中の要求がないことを確認します。
|
||||
|
||||
これは、コンポーネントを繰り返しレンダリングするため、CPUリソースを浪費するだけでなく、データクライアントは、サーバー側で取得したデータをハイドレーション中に利用可能にする方法を提供する必要があり、初期レンダリングで返される仮想DOMがサーバーサイドレンダリングのDOMツリーと一致することを保証する必要があります。これは実現が難しい場合があります。
|
||||
|
||||
Yewは、`<Suspense />` を使用してこの問題を解決する異なるアプローチを採用しています。
|
||||
|
||||
`<Suspense />` は特別なコンポーネントで、クライアント側で使用する場合、コンポーネントがデータを取得(保留)している間にフォールバックUIを表示し、データ取得が完了した後に通常のUIに戻る方法を提供します。
|
||||
|
||||
アプリケーションがサーバーサイドレンダリングされると、Yewはコンポーネントが保留状態でなくなるまで待機し、それを文字列バッファにシリアル化します。
|
||||
|
||||
ハイドレーション中、`<Suspense />` コンポーネント内の要素は、すべての子コンポーネントが保留状態でなくなるまでハイドレーションされません。
|
||||
|
||||
この方法により、開発者はサーバーサイドレンダリングに対応したクライアント非依存のアプリケーションを簡単に構築し、データ取得を行うことができます。
|
||||
|
||||
## SSR ハイドレーション
|
||||
|
||||
## サーバーサイドレンダリングハイドレーション(SSR Hydration)
|
||||
|
||||
ハイドレーションは、Yewアプリケーションをサーバー側で生成されたHTMLファイルに接続するプロセスです。デフォルトでは、`ServerRender` はハイドレーション可能なHTML文字列を出力し、追加情報を含んでハイドレーションを容易にします。`Renderer::hydrate` メソッドを呼び出すと、Yewは最初からレンダリングするのではなく、アプリケーションが生成した仮想DOMとサーバーレンダラーが生成したHTML文字列を調整します。
|
||||
|
||||
:::caution
|
||||
|
||||
`ServerRenderer` が作成したHTMLマークアップを正常にハイドレーションするためには、クライアントはSSRに使用されたレイアウトと完全に一致する仮想DOMレイアウトを生成する必要があります。要素を含まないコンポーネントも含めてです。特定の実装でのみ使用されるコンポーネントがある場合は、`PhantomComponent` を使用して追加のコンポーネントの位置を埋めることを検討してください。
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
SSR出力(静的HTML)をブラウザが初期レンダリングした後、実際のDOMが期待されるDOMと一致する場合にのみ、ハイドレーションは成功します。HTMLが規格に準拠していない場合、ハイドレーションは失敗する可能性があります。ブラウザは不正なHTMLのDOM構造を変更する可能性があり、実際のDOMが期待されるDOMと異なることがあります。例えば、[`<tbody>` のない `<table>` がある場合、ブラウザはDOMに `<tbody>` を追加する可能性があります](https://github.com/yewstack/yew/issues/2684)。
|
||||
:::
|
||||
|
||||
## ハイドレーション中のコンポーネントライフサイクル
|
||||
|
||||
ハイドレーション中、コンポーネントは作成後に2回連続してレンダリングされます。すべてのエフェクトは2回目のレンダリングが完了した後に呼び出されます。コンポーネントのレンダリング関数に副作用がないことを確認することが重要です。状態を変更したり、追加のレンダリングをトリガーしたりしないようにしてください。現在、状態を変更したり追加のレンダリングをトリガーしたりするコンポーネントがある場合は、それらを `use_effect` フックに移動してください。
|
||||
|
||||
ハイドレーション中、構造化コンポーネントを使用してサーバーサイドレンダリングを行うことができます。ビュー関数はレンダリング関数の前に複数回呼び出されます。レンダリング関数が呼び出されるまで、DOMは未接続と見なされ、`rendered()` メソッドが呼び出される前にレンダリングノードにアクセスすることを防ぐ必要があります。
|
||||
|
||||
## 例
|
||||
|
||||
```rust ,ignore
|
||||
use yew::prelude::*;
|
||||
use yew::Renderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! {<div>{"Hello, World!"}</div>}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let renderer = Renderer::<App>::new();
|
||||
|
||||
// body 要素の下のすべてのコンテンツをハイドレーションし、末尾の要素を削除します(存在する場合)。
|
||||
renderer.hydrate();
|
||||
}
|
||||
```
|
||||
|
||||
例: [simple_ssr](https://github.com/yewstack/yew/tree/master/examples/simple_ssr)
|
||||
例: [ssr_router](https://github.com/yewstack/yew/tree/master/examples/ssr_router)
|
||||
|
||||
## シングルスレッドモード
|
||||
|
||||
Yewは `yew::LocalServerRenderer` を使用してシングルスレッドでのサーバーサイドレンダリングをサポートしています。このモードはWASIのようなシングルスレッド環境に適しています。
|
||||
|
||||
```rust
|
||||
// Rustc 1.78以降では、`wasm32-wasip1` または `wasm32-wasip2` ターゲットを使用してビルドします。
|
||||
// 古いバージョンのRustc(1.84以前)を使用している場合は、`wasm32-wasi` ターゲットを使用してビルドすることもできます。
|
||||
// 詳細については、https://blog.rust-lang.org/2024/04/09/updates-to-rusts-wasi-targets.html を参照してください。
|
||||
|
||||
use yew::prelude::*;
|
||||
use yew::LocalServerRenderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
use yew_router::prelude::*;
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{"Yew WASI SSR demo"}</h1>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn render() -> String {
|
||||
let renderer = LocalServerRenderer::<App>::new();
|
||||
let html_raw = renderer.render().await;
|
||||
|
||||
let mut body = String::new();
|
||||
body.push_str("<body>");
|
||||
body.push_str("<div id='app'>");
|
||||
body.push_str(&html_raw);
|
||||
body.push_str("</div>");
|
||||
body.push_str("</body>");
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
println!("{}", render().await);
|
||||
}
|
||||
```
|
||||
|
||||
例: [wasi_ssr_module](https://github.com/yewstack/yew/tree/master/examples/wasi_ssr_module)
|
||||
|
||||
:::note
|
||||
`wasm32-unknown-unknown` ターゲットを使用してSSRアプリケーションをビルドする場合、`not_browser_env` 機能フラグを使用して、Yew内部のブラウザ固有のAPIへのアクセスを無効にすることができます。これは、Cloudflare Workerのようなサーバーレスプラットフォームで非常に便利です。
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
サーバーサイドレンダリングは現在実験的な機能です。バグを見つけた場合は、[GitHubで報告してください](https://github.com/yewstack/yew/issues/new?assignees=&labels=bug&template=bug_report.md&title=)。
|
||||
|
||||
:::
|
|
@ -1,35 +1,85 @@
|
|||
---
|
||||
title: Callbacks
|
||||
description: ComponentLink and Callbacks
|
||||
title: 'コールバック関数 (Callbacks)'
|
||||
---
|
||||
|
||||
”リンク”コンポーネントはコンポーネントがコールバックを登録できて自身を更新することができるメカニズムです。
|
||||
## コールバック関数 (Callbacks)
|
||||
|
||||
## ComponentLink API
|
||||
コールバック関数は、Yew でサービス、エージェント、および親コンポーネントと通信するために使用されます。内部的には、それらの型は `Rc` に包まれた `Fn` に過ぎず、クローンを許可します。
|
||||
|
||||
### callback
|
||||
それらには `emit` 関数があり、その `<IN>` 型を引数として取り、それをターゲットが期待するメッセージに変換します。親コンポーネントのコールバック関数が子コンポーネントに props として提供される場合、子コンポーネントはその `update` ライフサイクルフックでコールバック関数の `emit` 関数を呼び出して、メッセージを親コンポーネントに送信できます。`html!` マクロで props として提供されるクロージャまたは関数は、自動的にコールバック関数に変換されます。
|
||||
|
||||
実行時にコンポーネントの更新メカニズムにメッセージを送信するコールバックを登録します。
|
||||
これは、渡されたクロージャから返されるメッセージで `send_self` を呼び出します。
|
||||
`Fn(IN) -> Vec<COMP::Message>`が渡され、`Callback<IN>`が返されます。
|
||||
シンプルなコールバック関数の使用例は次のようになります:
|
||||
|
||||
### send_message
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html};
|
||||
|
||||
現在のループが終了した直後にコンポーネントにメッセージを送信し、別の更新ループを開始します。
|
||||
enum Msg {
|
||||
Clicked,
|
||||
}
|
||||
|
||||
### send_message_batch
|
||||
struct Comp;
|
||||
|
||||
実行時に一度に多数のメッセージを一括して送信するコールバックを登録します。
|
||||
メッセージによってコンポーネントが再レンダリングされる場合、バッチ内のすべてのメッセージが処理された後、コンポーネントは再レンダリングされます。
|
||||
`Fn(IN) -> COMP::Message`が渡され、`Callback<IN>`が返されます。
|
||||
impl Component for Comp {
|
||||
|
||||
## コールバック
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
_\(This might need its own short page.\)_
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
コールバックは、Yew 内のサービス、エージェント、親コンポーネントとの通信に使われます。
|
||||
これらは単に `Fn` を `Rc` でラップしただけであり、クローンを作成できるようにするためのものです。
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
// highlight-next-line
|
||||
let onclick = ctx.link().callback(|_| Msg::Clicked);
|
||||
html! {
|
||||
// highlight-next-line
|
||||
<button {onclick}>{ "Click" }</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
これらの関数には `emit` 関数があり、`<IN>` 型を引数に取り、それをアドレスが欲しいメッセージに変換します。
|
||||
親からのコールバックが子コンポーネントに props で提供されている場合、子は `update` ライフサイクルフックで `emit` をコールバックに呼び出して親にメッセージを返すことができます。
|
||||
マクロ内で props として提供されたクロージャや関数は自動的にコールバックに変換されます。
|
||||
この関数を `callback` に渡す場合、常に1つの引数を持つ必要があります。例えば、`onclick` ハンドラは `MouseEvent` 型の引数を受け取る関数である必要があります。その後、ハンドラはコンポーネントにどのタイプのメッセージを送信するかを決定できます。このメッセージは無条件に次の更新サイクルにスケジュールされます。
|
||||
|
||||
更新を引き起こす必要がないコールバック関数が必要な場合は、`batch_callback` を使用してください。
|
||||
|
||||
```rust
|
||||
use yew::{events::KeyboardEvent, html, Component, Context, Html};
|
||||
|
||||
enum Msg {
|
||||
Submit,
|
||||
}
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
// highlight-start
|
||||
let onkeypress = ctx.link().batch_callback(|event: KeyboardEvent| {
|
||||
if event.key() == "Enter" {
|
||||
Some(Msg::Submit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<input type="text" {onkeypress} />
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 関連例
|
||||
|
||||
- [Counter](https://github.com/yewstack/yew/tree/master/examples/counter)
|
||||
- [Timer](https://github.com/yewstack/yew/tree/master/examples/timer)
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
title: '高階コンポーネント'
|
||||
---
|
||||
|
||||
いくつかの状況では、構造コンポーネントは特定の機能(例えば Suspense)を直接サポートしていないか、または特定の機能を使用するために大量のボイラープレートコードが必要です(例えば Context)。
|
||||
|
||||
このような場合、高階コンポーネントの関数コンポーネントを作成することをお勧めします。
|
||||
|
||||
## 高階コンポーネントの定義
|
||||
|
||||
高階コンポーネントは、新しい HTML を追加せず、他のコンポーネントをラップして追加機能を提供するコンポーネントです。
|
||||
|
||||
### 例
|
||||
|
||||
Context(コンテキスト)フックを使用し、それを構造コンポーネントに渡す例
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Theme {
|
||||
foreground: String,
|
||||
background: String,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn App() -> Html {
|
||||
let ctx = use_state(|| Theme {
|
||||
foreground: "#000000".to_owned(),
|
||||
background: "#eeeeee".to_owned(),
|
||||
});
|
||||
|
||||
html! {
|
||||
<ContextProvider<Theme> context={(*ctx).clone()}>
|
||||
<ThemedButtonHOC />
|
||||
</ContextProvider<Theme>>
|
||||
}
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
#[function_component]
|
||||
pub fn ThemedButtonHOC() -> Html {
|
||||
let theme = use_context::<Theme>().expect("no ctx found");
|
||||
|
||||
html! {<ThemedButtonStructComponent {theme} />}
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
struct ThemedButtonStructComponent;
|
||||
|
||||
impl Component for ThemedButtonStructComponent {
|
||||
type Message = ();
|
||||
type Properties = Props;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let theme = &ctx.props().theme;
|
||||
html! {
|
||||
<button style={format!(
|
||||
"background: {}; color: {};",
|
||||
theme.background,
|
||||
theme.foreground
|
||||
)}
|
||||
>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
title: '紹介'
|
||||
description: 'Yew のコンポーネント'
|
||||
---
|
||||
|
||||
## コンポーネントとは?
|
||||
|
||||
コンポーネントは Yew の構成要素です。内部状態を管理し、要素を DOM にレンダリングできます。`Component` トレイトを実装することでコンポーネントを作成します。
|
||||
|
||||
## コンポーネントマークアップの作成
|
||||
|
||||
Yew は仮想 DOM を使用して要素を DOM にレンダリングします。仮想 DOM ツリーは `html!` マクロを使用して構築できます。`html!` の構文は HTML に似ていますが、同じではありません。ルールもより厳格です。また、条件付きレンダリングやイテレータを使用したリストのレンダリングなどの強力な機能も提供します。
|
||||
|
||||
:::info
|
||||
[`html!` マクロ、その使用方法、および構文についてさらに詳しく知る](concepts/html/introduction.mdx)
|
||||
:::
|
||||
|
||||
## コンポーネントにデータを渡す
|
||||
|
||||
Yew コンポーネントは _props_ を使用して親コンポーネントと子コンポーネント間で通信します。親コンポーネントは任意のデータを props として子コンポーネントに渡すことができます。Props は HTML 属性に似ていますが、任意の Rust 型を props として渡すことができます。
|
||||
|
||||
:::info
|
||||
[props についてさらに詳しく知る](advanced-topics/struct-components/properties.mdx)
|
||||
:::
|
||||
|
||||
:::info
|
||||
親/子通信以外の通信には、[コンテキスト](../../concepts/contexts.mdx) を使用してください
|
||||
:::
|
|
@ -1,175 +1,248 @@
|
|||
---
|
||||
title: Introduction
|
||||
description: Components and their lifecycle hooks
|
||||
title: 'ライフサイクル'
|
||||
description: 'コンポーネントとそのライフサイクルフック'
|
||||
---
|
||||
|
||||
## コンポーネントとは?
|
||||
|
||||
コンポーネントは Yew を構成するブロックです。
|
||||
コンポーネントは状態を管理し、自身を DOM へレンダリングすることができます。
|
||||
コンポーネントはライフサイクルの機能がある`Component`トレイトを実装することによって作られます。
|
||||
`Component` トレイトには、実装する必要がある多くのメソッドがあります。Yew はコンポーネントのライフサイクルのさまざまな段階でこれらのメソッドを呼び出します。
|
||||
|
||||
## ライフサイクル
|
||||
|
||||
:::important contribute
|
||||
`Contribute to our docs:` [Add a diagram of the component lifecycle](https://github.com/yewstack/docs/issues/22)
|
||||
:::important ドキュメントの改善
|
||||
`ドキュメントに貢献する:` [カスタムライフサイクルを持つコンポーネントの例を追加](https://github.com/yewstack/yew/issues/1915)
|
||||
:::
|
||||
|
||||
## ライフサイクルのメソッド
|
||||
## ライフサイクルメソッド
|
||||
|
||||
### Create
|
||||
|
||||
コンポーネントが作られると、`ComponentLink`と同様に親コンポーネントからプロパティを受け取ります。
|
||||
プロパティはコンポーネントの状態を初期化するのに使われ、"link"はコールバックを登録したりコンポーネントにメッセージを送るのに使われます。
|
||||
|
||||
props と link をコンポーネント構造体に格納するのが一般的です。
|
||||
例えば:
|
||||
コンポーネントが作成されるとき、それは親コンポーネントからプロパティを受け取り、それらは `create` メソッドに渡される `Context<Self>` に保存されます。これらのプロパティはコンポーネントの状態を初期化するために使用でき、"link" はコールバックを登録したり、コンポーネントにメッセージを送信したりするために使用できます。
|
||||
|
||||
```rust
|
||||
pub struct MyComponent {
|
||||
props: Props,
|
||||
link: ComponentLink<Self>,
|
||||
}
|
||||
use yew::{Component, Context, html, Html, Properties};
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct Props;
|
||||
|
||||
pub struct MyComponent;
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = ();
|
||||
type Properties = Props;
|
||||
// ...
|
||||
|
||||
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
MyComponent { props, link }
|
||||
// highlight-start
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
MyComponent
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### View
|
||||
|
||||
コンポーネントは`view()`メソッドによってレイアウトを宣言します。
|
||||
Yew は`html!`マクロによって HTML と SVG ノード、リスナー、子コンポーネントを宣言できます。
|
||||
マクロは React の JSX のような動きをしますが、JavaScript の代わりに Rust の式を用います。
|
||||
|
||||
```rust
|
||||
impl Component for MyComponent {
|
||||
// ...
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let onclick = self.link.callback(|_| Msg::Click);
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<button {onclick}>{ self.props.button_text }</button>
|
||||
// 具体的な実装
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使い方については[`html!`ガイド](concepts/html/introduction.mdx)をご確認ください。
|
||||
### View
|
||||
|
||||
`view` メソッドは、コンポーネントがDOMにどのようにレンダリングされるべきかを記述することを可能にします。Rust関数を使用してHTMLに似たコードを書くことは非常に混乱する可能性があるため、Yewは`html!`マクロを提供しています。これにより、HTMLおよびSVGノードを宣言し(およびそれらに属性とイベントリスナーを追加し)、子コンポーネントを便利にレンダリングする方法が提供されます。このマクロは、ReactのJSXに似ています(プログラミング言語の違いを除いて)。一つの違いは、YewがSvelteのようなプロパティの簡略化された構文を提供している点です。ここでは、`{onclick}`とだけ書くことができ、`onclick={onclick}`と書く必要はありません。
|
||||
|
||||
```rust
|
||||
use yew::{Component, Context, html, Html, Properties};
|
||||
|
||||
enum Msg {
|
||||
Click,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
struct Props {
|
||||
button_text: String,
|
||||
}
|
||||
|
||||
struct MyComponent;
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = Msg;
|
||||
type Properties = Props;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let onclick = ctx.link().callback(|_| Msg::Click);
|
||||
html! {
|
||||
<button {onclick}>{ &ctx.props().button_text }</button>
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
使用方法の詳細については、[html! ガイド](concepts/html/introduction.mdx) を参照してください。
|
||||
|
||||
### Rendered
|
||||
|
||||
`rendered()`コンポーネントのライフサイクルのメソッドは`view()`が処理されたて Yew がコンポーネントをレンダリングした後、
|
||||
ブラウザがページを更新する前に呼ばれます。
|
||||
コンポーネントは、コンポーネントが要素をレンダリングした後にのみ実行できるアクションを実行するため、このメソッドを実装したい場合があります。
|
||||
コンポーネントが初めてレンダリングされたかどうかは `first_render` パラメータで確認できます。
|
||||
`rendered` コンポーネントライフサイクルメソッドは、`view` が呼び出され、Yew がその結果を DOM にレンダリングした後、ブラウザがページを更新する前に呼び出されます。このメソッドは、コンポーネントが要素をレンダリングした後にのみ完了できる操作を実行したい場合に非常に便利です。また、`first_render` という名前のパラメーターがあり、この関数が最初のレンダリング時に呼び出されたか、後続のレンダリング時に呼び出されたかを判断するために使用できます。
|
||||
|
||||
```rust
|
||||
use stdweb::web::html_element::InputElement;
|
||||
use stdweb::web::IHtmlElement;
|
||||
use yew::prelude::*;
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::{
|
||||
Component, Context, html, Html, NodeRef,
|
||||
};
|
||||
|
||||
pub struct MyComponent {
|
||||
node_ref: NodeRef,
|
||||
}
|
||||
|
||||
impl Component for MyComponent {
|
||||
// ...
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn view(&self) -> Html {
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
node_ref: NodeRef::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<input ref={self.node_ref.clone()} type="text" />
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, first_render: bool) {
|
||||
// highlight-start
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
|
||||
if first_render {
|
||||
if let Some(input) = self.node_ref.try_into::<InputElement>() {
|
||||
if let Some(input) = self.node_ref.cast::<HtmlInputElement>() {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
:::tip note
|
||||
ライフサイクルメソッドは実装の必要がなく、デフォルトでは何もしません。
|
||||
このライフサイクルメソッドは実装する必要はなく、デフォルトでは何も実行しません。
|
||||
:::
|
||||
|
||||
### Update
|
||||
|
||||
コンポーネントは動的で、非同期メッセージを受信するために登録することができます。
|
||||
ライフサイクルメソッド `update()` はメッセージごとに呼び出されます。
|
||||
これにより、コンポーネントはメッセージが何であったかに基づいて自身を更新し、自身を再レンダリングする必要があるかどうかを判断することができます。
|
||||
メッセージは、HTML 要素リスナーによってトリガーされたり、子コンポーネント、エージェント、サービス、または Futures によって送信されたりします。
|
||||
コンポーネントとの通信は主にメッセージを通じて行われ、これらのメッセージは `update` ライフサイクルメソッドによって処理されます。これにより、コンポーネントはメッセージに基づいて自身を更新し、再レンダリングが必要かどうかを判断できます。メッセージはイベントリスナー、子コンポーネント、エージェント、サービス、またはフューチャーによって送信されることがあります。
|
||||
|
||||
`update()`がどのようなのかについての例は以下の通りです:
|
||||
以下は `update` の実装例です:
|
||||
|
||||
```rust
|
||||
use yew::{Component, Context, html, Html};
|
||||
|
||||
// highlight-start
|
||||
pub enum Msg {
|
||||
SetInputEnabled(bool)
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = Msg;
|
||||
|
||||
// ...
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::SetInputEnabled(enabled) => {
|
||||
if self.input_enabled != enabled {
|
||||
self.input_enabled = enabled;
|
||||
true // Re-render
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
struct MyComponent {
|
||||
input_enabled: bool,
|
||||
}
|
||||
```
|
||||
|
||||
### Change
|
||||
|
||||
コンポーネントは親によって再レンダリングされることがあります。
|
||||
このような場合、新しいプロパティを受け取り、再レンダリングを選択する可能性があります。
|
||||
この設計では、プロパティを変更することで、親から子へのコンポーネントの通信が容易になります。
|
||||
|
||||
典型的な実装例は以下の通りです:
|
||||
|
||||
```rust
|
||||
impl Component for MyComponent {
|
||||
// ...
|
||||
// highlight-next-line
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||
if self.props != props {
|
||||
self.props = props;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
input_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::SetInputEnabled(enabled) => {
|
||||
if self.input_enabled != enabled {
|
||||
self.input_enabled = enabled;
|
||||
true // 再レンダリング
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
// 具体的な実装
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Changed
|
||||
|
||||
コンポーネントは親コンポーネントによって再レンダリングされることがあります。この場合、新しいプロパティを受け取り、再レンダリングが必要になることがあります。この設計により、プロパティの値を変更するだけで親子コンポーネント間の通信が促進されます。プロパティが変更されると、デフォルトの実装によりコンポーネントが再レンダリングされます。
|
||||
|
||||
### Destroy
|
||||
|
||||
コンポーネントが DOM からアンマウントされた後、Yew は `destroy()` ライフサイクルメソッドを呼び出し、必要なクリーンアップ操作をサポートします。
|
||||
このメソッドはオプションで、デフォルトでは何もしません。
|
||||
コンポーネントがDOMからアンマウントされると、Yewは`destroy`ライフサイクルメソッドを呼び出します。コンポーネントが破棄される前にクリーンアップ操作を実行する必要がある場合に便利です。このメソッドはオプションであり、デフォルトでは何も実行しません。
|
||||
|
||||
## Associated Types
|
||||
### 無限ループ
|
||||
|
||||
`Component`トレイトは 2 つの関連型があります: `Message`と`Properties`です。
|
||||
Yewのライフサイクルメソッドでは無限ループが発生する可能性がありますが、それは各レンダリング後に同じコンポーネントを更新し、その更新が再レンダリングを要求する場合にのみ発生します。
|
||||
|
||||
以下は簡単な例です:
|
||||
|
||||
```rust
|
||||
use yew::{Context, Component, Html};
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
// どのメッセージでも常に再レンダリングを要求します
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
// レンダリングする内容は重要ではありません
|
||||
Html::default()
|
||||
}
|
||||
|
||||
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
|
||||
// この新しいメッセージを使用してコンポーネントを更新するように要求します
|
||||
ctx.link().send_message(());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ここで何が起こっているのか見てみましょう:
|
||||
|
||||
1. `create` 関数を使用してコンポーネントを作成します。
|
||||
2. `view` メソッドを呼び出して、Yew がブラウザの DOM にレンダリングする内容を知ることができます。
|
||||
3. `rendered` メソッドを呼び出し、`Context` リンクを使用して更新メッセージをスケジュールします。
|
||||
4. Yew がレンダリングフェーズを完了します。
|
||||
5. Yew はスケジュールされたイベントをチェックし、更新メッセージキューが空でないことを確認してメッセージを処理します。
|
||||
6. `update` メソッドを呼び出し、変更が発生し、コンポーネントが再レンダリングする必要があることを示す `true` を返します。
|
||||
7. ステップ2に戻ります。
|
||||
|
||||
`rendered` メソッドで更新をスケジュールすることは依然として可能であり、これは通常便利ですが、その際にはこのループをどのように終了させるかを考慮してください。
|
||||
|
||||
## 関連タイプ
|
||||
|
||||
`Component` トレイトには、`Message` と `Properties` の2つの関連タイプがあります。
|
||||
|
||||
```rust ,ignore
|
||||
impl Component for MyComponent {
|
||||
type Message = Msg;
|
||||
type Properties = Props;
|
||||
|
@ -178,21 +251,23 @@ impl Component for MyComponent {
|
|||
}
|
||||
```
|
||||
|
||||
`Message`はコンポーネントによって処理され、何らかの副作用を引き起こすことができるさまざまなメッセージを表します。
|
||||
例えば、API リクエストをトリガーしたり、UI コンポーネントの外観を切り替えたりする `Click` メッセージがあります。
|
||||
コンポーネントのモジュールで `Msg` という名前の列挙型を作成し、それをコンポーネントのメッセージ型として使用するのが一般的です。
|
||||
"message"を"msg"と省略するのも一般的です。
|
||||
`Message` タイプは、イベントが発生した後にコンポーネントにメッセージを送信するために使用されます。例えば、ユーザーがボタンをクリックしたり、ページをスクロールしたりしたときに何かを実行したい場合があります。コンポーネントは通常、複数のイベントに応答する必要があるため、`Message` タイプは通常、処理するイベントごとにバリアントを持つ列挙型です。
|
||||
|
||||
コードベースを整理する際には、`Message` タイプの定義をコンポーネントを定義する同じモジュールに含めるのが賢明です。メッセージタイプの命名に一貫した命名規則を採用することが役立つ場合があります。一つのオプション(唯一のオプションではありませんが)は、タイプを `ComponentNameMsg` と命名することです。例えば、コンポーネントが `Homepage` と名付けられている場合、タイプを `HomepageMsg` と命名することができます。
|
||||
|
||||
```rust
|
||||
enum Msg {
|
||||
Click,
|
||||
FormInput(String)
|
||||
}
|
||||
```
|
||||
|
||||
`Properties`は、親からコンポーネントに渡される情報を表します。
|
||||
この型は Properties trait を実装していなければならず\(通常はこれを派生させることで\)、特定のプロパティが必須かオプションかを指定することができます。
|
||||
この型は、コンポーネントの作成・更新時に使用されます。
|
||||
コンポーネントのモジュール内に `Props` という構造体を作成し、それをコンポーネントの `Properties` 型として使用するのが一般的です。
|
||||
”Properties”を"props"に短縮するのが一般的です。
|
||||
Props は親コンポーネントから継承されるので、アプリケーションのルートコンポーネントは通常`()`型の`Properties`を持ちます。
|
||||
ルートコンポーネントのプロパティを指定したい場合は、`App::mount_with_props`メソッドを利用します。
|
||||
`Properties` は、親コンポーネントからコンポーネントに渡される情報を表します。この型は `Properties` トレイトを実装する必要があり(通常はそれを派生させる)、特定のプロパティが必須かオプションかを指定できます。コンポーネントの作成および更新時にこの型が使用されます。コンポーネントのモジュール内で `Props` という名前の構造体を作成し、それをコンポーネントの `Properties` 型として使用するのが一般的な方法です。通常、"properties" は "props" と略されます。プロパティは親コンポーネントから渡されるため、アプリケーションのルートコンポーネントは通常、`Properties` 型として `()` を持ちます。ルートコンポーネントにプロパティを指定する場合は、`App::mount_with_props` メソッドを使用します。
|
||||
|
||||
:::info
|
||||
[プロパティに関する詳細はこちら](./properties)
|
||||
:::
|
||||
|
||||
## ライフサイクルコンテキスト
|
||||
|
||||
すべてのコンポーネントライフサイクルメソッドは、コンテキストオブジェクトを受け取ります。このオブジェクトは、コンポーネントのスコープへの参照を提供し、コンポーネントにメッセージを送信したり、コンポーネントに渡されたプロパティを取得したりすることができます。
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
---
|
||||
title: Properties
|
||||
description: Parent to child communication
|
||||
title: 'プロパティ (Props)'
|
||||
description: '親子コンポーネント間の通信'
|
||||
---
|
||||
|
||||
プロパティは、子コンポーネントと親コンポーネントが互いに通信できるようにします。
|
||||
プロパティ (Properties) は、子コンポーネントと親コンポーネントの間で通信を可能にします。各コンポーネントには、親コンポーネントから渡される内容を記述するための関連プロパティ型があります。理論的には、これは `Properties` トレイトを実装した任意の型である可能性がありますが、実際には、各フィールドがプロパティを表す構造体であるべきです。
|
||||
|
||||
## マクロの継承
|
||||
## 派生マクロ
|
||||
|
||||
`Properties`を自分で実装しようとせず、代わりに`#[derive(Properties)]`を使ってください。
|
||||
`Properties` トレイトを自分で実装する必要はありません。`#[derive(Properties)]` を使用して実装を自動生成できます。`Properties` を派生する型は `PartialEq` も実装する必要があります。
|
||||
|
||||
:::note
|
||||
`Properties`を継承した型は`Clone`を実装していなければいけません。
|
||||
これは`#[derive(Properties, Clone)`か`Clone`を手で実装することで可能です。
|
||||
### フィールド属性
|
||||
|
||||
`Properties` を派生する際、デフォルトではすべてのフィールドが必須です。以下の属性を使用すると、他の値が設定されていない限り、プロパティに初期値を提供できます。
|
||||
|
||||
:::tip
|
||||
プロパティは Rustdoc によって生成されたドキュメントには表示されません。プロパティのドキュメント文字列には、そのプロパティがオプションであるかどうか、または特別なデフォルト値があるかどうかを記載する必要があります。
|
||||
:::
|
||||
|
||||
### 必要な属性
|
||||
#### `#[prop_or_default]`
|
||||
|
||||
デフォルトでは、`Properties` を導出する構造体内のフィールドは必須です。
|
||||
フィールドが欠落していて `html!` マクロでコンポーネントが作成された場合、コンパイラエラーが返されます。
|
||||
オプションのプロパティを持つフィールドについては、`#[prop_or_default]` 属性を使用して、prop が指定されていない場合はその型のデフォルト値を使用します。
|
||||
値を指定するには `#[prop_or(value)]` 属性を用います。
|
||||
ここで value はプロパティのデフォルト値、あるいは代わりに `#[prop_or_else(function)]` を使用して、`function` はデフォルト値を返します。
|
||||
例えば、ブール値のデフォルトを `true` とするには、属性 `#[prop_or(true)]` を使用します。オプションのプロパティでは、デフォルト値 `None` を持つ `Option` 列挙型を使うのが一般的です。
|
||||
フィールド型のデフォルト値を使用してプロパティ値を初期化します。これは `Default` トレイトを使用します。
|
||||
|
||||
### PartialEq
|
||||
#### `#[prop_or(value)]`
|
||||
|
||||
もし可能なら props で `PartialEq` を継承するのが良いかもしれません。
|
||||
`PartialEq`を使うことで、不必要な再レンダリングを避けることができます
|
||||
(これについては、**最適化とベストプラクティス**のセクションで説明しています)。
|
||||
`value` を使用してプロパティ値を初期化します。`value` はフィールド型を返す任意の式である可能性があります。例えば、ブールプロパティをデフォルトで `true` にするには、属性 `#[prop_or(true)]` を使用します。
|
||||
|
||||
## プロパティを使用する際のメモリと速度のオーバーヘッド
|
||||
#### `#[prop_or_else(function)]`
|
||||
|
||||
`Compoenent::view`ではコンポーネントの状態への参照を取り、それを使って `Html` を作成します。
|
||||
しかし、プロパティは自身の値です。
|
||||
つまり、それらを作成して子コンポーネントに渡すためには、`view` 関数で提供される参照を所有する必要があるのです。
|
||||
これは所有する値を取得するためにコンポーネントに渡される参照を暗黙のうちにクローンすることで行われます。
|
||||
`function` を呼び出してプロパティ値を初期化します。`function` は `FnMut() -> T` のシグネチャを持つ必要があります。ここで、`T` はフィールド型です。
|
||||
|
||||
これは、各コンポーネントが親から受け継いだ状態の独自のコピーを持っていることを意味し、コンポーネントを再レンダリングするときはいつでも、再レンダリングしたコンポーネントのすべての子コンポーネントの props がクローンされなければならないことを意味します。
|
||||
## `PartialEq`
|
||||
|
||||
このことの意味するところは、もしそうでなければ*大量の*データ \(10KB もあるような文字列\) を props として渡してしまうのであれば、子コンポーネントを親が呼び出す `Html` を返す関数にすることを考えた方がいいかもしれないということです。
|
||||
`Properties` は `PartialEq` を実装する必要があります。これにより、Yew はそれらを比較し、変更があった場合に `changed` メソッドを呼び出すことができます。
|
||||
|
||||
props を介して渡されたデータを変更する必要がない場合は、実際のデータそのものではなく、データへの参照カウントされたポインタのみが複製されるように `Rc` でラップすることができます。
|
||||
## Properties のパフォーマンスオーバーヘッド
|
||||
|
||||
内部プロパティは参照カウントされたポインタに基づいて格納されます。これにより、コンポーネントツリーに渡されるプロパティにはポインタのみが渡され、プロパティ全体をクローンすることによる高価なパフォーマンスオーバーヘッドを回避できます。
|
||||
|
||||
:::tip
|
||||
`AttrValue` を使用してください。これは、クローンが必要な String やその他の類似の型を使用せずに済むようにするために提供されているカスタムプロパティ値型です。
|
||||
:::
|
||||
|
||||
## 例
|
||||
|
||||
```rust
|
||||
use std::rc::Rc;
|
||||
use yew::Properties;
|
||||
/// virtual_dom から AttrValue をインポート
|
||||
use yew::virtual_dom::AttrValue;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum LinkColor {
|
||||
|
@ -57,28 +57,78 @@ pub enum LinkColor {
|
|||
Purple,
|
||||
}
|
||||
|
||||
impl Default for LinkColor {
|
||||
fn default() -> Self {
|
||||
// The link color will be blue unless otherwise specified.
|
||||
LinkColor::Blue
|
||||
}
|
||||
fn create_default_link_color() -> LinkColor {
|
||||
LinkColor::Blue
|
||||
}
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct LinkProps {
|
||||
/// The link must have a target.
|
||||
href: String,
|
||||
/// If the link text is huge, this will make copying the string much cheaper.
|
||||
/// This isn't usually recommended unless performance is known to be a problem.
|
||||
text: Rc<String>,
|
||||
/// Color of the link.
|
||||
#[prop_or_default]
|
||||
/// リンクにはターゲットが必要です
|
||||
href: AttrValue,
|
||||
/// また、String ではなく AttrValue を使用していることに注意してください
|
||||
text: AttrValue,
|
||||
/// リンクの色、デフォルトは `Blue`
|
||||
#[prop_or_else(create_default_link_color)]
|
||||
color: LinkColor,
|
||||
/// The view function will not specify a size if this is None.
|
||||
/// 値が None の場合、ビュー関数はサイズを指定しません
|
||||
#[prop_or_default]
|
||||
size: Option<u32>,
|
||||
/// When the view function doesn't specify active, it defaults to true.
|
||||
/// ビュー関数がアクティブを指定しない場合、デフォルトは true
|
||||
#[prop_or(true)]
|
||||
active: bool,
|
||||
}
|
||||
```
|
||||
|
||||
## Props マクロ
|
||||
|
||||
`yew::props!` マクロを使用すると、`html!` マクロと同じ方法でプロパティを構築できます。
|
||||
|
||||
このマクロは構造体の式と同じ構文を使用しますが、属性や基本式 (`Foo { ..base }`) を使用することはできません。型パスはプロパティ (`path::to::Props`) に直接指すことも、コンポーネントの関連プロパティ (`MyComp::Properties`) に指すこともできます。
|
||||
|
||||
```rust
|
||||
use yew::{props, Properties, virtual_dom::AttrValue};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum LinkColor {
|
||||
Blue,
|
||||
Red,
|
||||
Green,
|
||||
Black,
|
||||
Purple,
|
||||
}
|
||||
|
||||
fn create_default_link_color() -> LinkColor {
|
||||
LinkColor::Blue
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct LinkProps {
|
||||
/// リンクにはターゲットが必要です
|
||||
href: AttrValue,
|
||||
/// また、String ではなく AttrValue を使用していることに注意してください
|
||||
text: AttrValue,
|
||||
/// リンクの色、デフォルトは `Blue`
|
||||
#[prop_or_else(create_default_link_color)]
|
||||
color: LinkColor,
|
||||
/// 値が None の場合、ビュー関数はサイズを指定しません
|
||||
#[prop_or_default]
|
||||
size: Option<u32>,
|
||||
/// ビュー関数がアクティブを指定しない場合、デフォルトは true
|
||||
#[prop_or(true)]
|
||||
active: bool,
|
||||
}
|
||||
|
||||
impl LinkProps {
|
||||
/// この関数は href と text を String として受け取ります
|
||||
/// `AttrValue::from` を使用してそれらを `AttrValue` に変換できます
|
||||
pub fn new_link_with_size(href: String, text: String, size: u32) -> Self {
|
||||
// highlight-start
|
||||
props! {LinkProps {
|
||||
href: AttrValue::from(href),
|
||||
text: AttrValue::from(text),
|
||||
size,
|
||||
}}
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,24 +1,51 @@
|
|||
---
|
||||
title: Refs
|
||||
description: Out-of-band DOM access
|
||||
title: '参照 (Refs)'
|
||||
description: 'DOM への越境アクセスを実現する'
|
||||
---
|
||||
|
||||
`ref`は、任意の HTML 要素やコンポーネントの内部で、割り当てられている DOM`Element`を取得するために使用することができます。
|
||||
これは、`view` ライフサイクルメソッドの外で DOM に変更を加えるために使用できます。
|
||||
`ref` キーワードは、任意の HTML 要素やコンポーネントに使用して、その要素に付随する DOM `Element` を取得できます。これにより、`view` ライフサイクルメソッドの外で DOM を変更することができます。
|
||||
|
||||
これは、キャンバスの要素を取得したり、ページの異なるセクションにスクロールしたりするのに便利です。
|
||||
これは、canvas 要素を取得したり、ページの異なる部分にスクロールしたりするのに便利です。例えば、コンポーネントの `rendered` メソッドで `NodeRef` を使用すると、`view` からレンダリングされた後に canvas 要素に描画呼び出しを行うことができます。
|
||||
|
||||
構文は以下の通りです:
|
||||
構文は次のとおりです:
|
||||
|
||||
```rust
|
||||
// In create
|
||||
self.node_ref = NodeRef::default();
|
||||
use web_sys::Element;
|
||||
use yew::{html, Component, Context, Html, NodeRef};
|
||||
|
||||
// In view
|
||||
html! {
|
||||
<div ref={self.node_ref.clone()}></div>
|
||||
struct Comp {
|
||||
node_ref: NodeRef,
|
||||
}
|
||||
|
||||
// In update
|
||||
let has_attributes = self.node_ref.try_into::<Element>().has_attributes();
|
||||
impl Component for Comp {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
// highlight-next-line
|
||||
node_ref: NodeRef::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
// highlight-next-line
|
||||
<div ref={self.node_ref.clone()}></div>
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
// highlight-start
|
||||
let has_attributes = self.node_ref
|
||||
.cast::<Element>()
|
||||
.unwrap()
|
||||
.has_attributes();
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 関連例
|
||||
|
||||
- [ノード参照](https://github.com/yewstack/yew/tree/master/examples/node_refs)
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
title: 'スコープ'
|
||||
description: 'コンポーネントのスコープ'
|
||||
---
|
||||
|
||||
## コンポーネントの `Scope<_>` インターフェース
|
||||
|
||||
`Scope` は、メッセージを介してコールバックを作成し、自身を更新するメカニズムです。コンポーネントに渡されるコンテキストオブジェクトで `link()` を呼び出すことで、その参照を取得します。
|
||||
|
||||
### `send_message`
|
||||
|
||||
この関数は、コンポーネントにメッセージを送信できます。メッセージは `update` メソッドによって処理され、コンポーネントが再レンダリングするかどうかを決定します。
|
||||
|
||||
### `send_message_batch`
|
||||
|
||||
この関数は、コンポーネントに複数のメッセージを同時に送信できます。これは `send_message` に似ていますが、任意のメッセージが `update` メソッドで `true` を返す場合、バッチ内のすべてのメッセージの処理が完了した後にコンポーネントが再レンダリングされます。
|
||||
|
||||
指定された引数ベクターが空の場合、この関数は何も実行しません。
|
||||
|
||||
### `callback`
|
||||
|
||||
コールバックを作成し、実行時にコンポーネントにメッセージを送信します。内部的には、提供されたクロージャが返すメッセージを使用して `send_message` を呼び出します。
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html};
|
||||
|
||||
enum Msg {
|
||||
Text(String),
|
||||
}
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
// テキストを受け取り、それを `Msg::Text` メッセージバリアントとしてコンポーネントに送信するコールバックを作成します。
|
||||
// highlight-next-line
|
||||
let cb = ctx.link().callback(|text: String| Msg::Text(text));
|
||||
|
||||
// 上の行は冗長であり、より明確にするために次のように簡略化できます:
|
||||
// highlight-next-line
|
||||
let cb = ctx.link().callback(Msg::Text);
|
||||
|
||||
// `Msg::Text("Hello World!")` をコンポーネントに送信します。
|
||||
// highlight-next-line
|
||||
cb.emit("Hello World!".to_owned());
|
||||
|
||||
html! {
|
||||
// ここに HTML を配置
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `batch_callback`
|
||||
|
||||
バッチメッセージを送信するコールバックを作成します。このメソッドに渡されるクロージャはメッセージを返す必要はありません。代わりに、クロージャは `Vec<Msg>` または `Option<Msg>` を返すことができます。ここで、`Msg` はコンポーネントのメッセージタイプです。
|
||||
|
||||
`Vec<Msg>` はバッチメッセージとして扱われ、内部的に `send_message_batch` を使用します。
|
||||
|
||||
`Option<Msg>` は値が `Some` の場合に `send_message` を呼び出します。値が `None` の場合は何も実行しません。これは、更新が不要な場合に使用できます。
|
||||
|
||||
これは、これらの型に対してのみ実装された `SendAsMessage` トレイトを使用して実現されています。独自の型に対して `SendAsMessage` を実装することで、`batch_callback` でそれらを使用できるようになります。
|
|
@ -1,56 +1,56 @@
|
|||
---
|
||||
title: Agents
|
||||
description: Yew's Actor System
|
||||
title: 'エージェント (Agents)'
|
||||
description: 'Yew のエージェントシステム'
|
||||
---
|
||||
|
||||
エージェントは Angular の[サービス](https://angular.io/guide/architecture-services)に似ており\(ただし依存性インジェクションはありません\)、
|
||||
[アクターモデル](https://en.wikipedia.org/wiki/Actor_model)を提供します。
|
||||
エージェントはコンポーネント階層のどこに位置するかに関わらず、コンポーネント間でメッセージをルーティングしたり、共有状態を作成したり、UI をレンダリングするメインスレッドから計算量の多いタスクをオフロードするために使用することができます。
|
||||
また、Yew アプリケーションがタブをまたいで通信できるようにするためのエージェントのサポートも\(将来的には\)計画されています。
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
import ThemedImage from '@theme/ThemedImage'
|
||||
|
||||
エージェントが並行に動くように Yew は[web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)を使用しています。
|
||||
エージェント (Agents) は、タスクを Web Workers にオフロードする方法です。
|
||||
|
||||
エージェントが並行して動作できるようにするために、Yew は [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) を使用します。
|
||||
|
||||
## ライフサイクル
|
||||
|
||||

|
||||
<!--
|
||||
The diagram is produced with nomnoml (nomnoml.com),
|
||||
The code can be found in the <desc> tag of the svgs.
|
||||
-->
|
||||
|
||||
<ThemedImage
|
||||
alt="agent lifecycle diagram"
|
||||
sources={{
|
||||
light: useBaseUrl('/img/agent-lifecycle-light.svg'),
|
||||
dark: useBaseUrl('/img/agent-lifecycle-dark.svg'),
|
||||
}}
|
||||
/>
|
||||
|
||||
## エージェントの種類
|
||||
|
||||
### Reaches
|
||||
### 範囲
|
||||
|
||||
- Context - Context エージェントのインスタンスは、常に最大 1 つ存在します。
|
||||
Bridges は、UI スレッド上で既にスポーンされたエージェントをスポーンするか、接続します。
|
||||
これは、コンポーネントまたは他のエージェント間の状態を調整するために使用することができます。
|
||||
このエージェントに Bridges が接続されていない場合、このエージェントは消滅します。
|
||||
- 公開 - 任意の時点で、公開エージェントのインスタンスは最大で1つだけです。ブリッジはWeb Worker内でエージェントを生成するか、既に生成されたエージェントに接続します。ブリッジがこのエージェントに接続されていない場合、エージェントは消滅します。
|
||||
|
||||
- Job - 新しいブリッジごとに UI スレッド上で新しいエージェントをスポーンします。
|
||||
これは、ブラウザと通信する共有されているが独立した動作をコンポーネントの外に移動させるのに適しています。
|
||||
(TODO 確認) タスクが完了すると、エージェントは消えます。
|
||||
- 私有 - 新しいブリッジごとにWeb Worker内で新しいエージェントを生成します。これは、ブラウザと通信する共有だが独立した動作をコンポーネントから移動するのに適しています。接続されたブリッジが破棄されると、エージェントは消滅します。
|
||||
|
||||
- Public - Context と同じですが、独自の web worker で動作します。
|
||||
- グローバル \(WIP\)
|
||||
|
||||
- Private - Job と同じですが、独自の web worker で動作します。
|
||||
## エージェントとコンポーネント間の通信
|
||||
|
||||
- Global \(WIP\)
|
||||
### 通信ブリッジ (Bridges)
|
||||
|
||||
## エージェントとコンポーネントのやり取り
|
||||
通信ブリッジ(ブリッジ)は、コンポーネントとエージェント間の通信チャネルです。これにより、コンポーネントはエージェントにメッセージを送信し、エージェントからのメッセージを受信できます。
|
||||
|
||||
### Bridges
|
||||
`use_bridge` フックは、関数コンポーネント内でブリッジを作成する機能も提供します。
|
||||
|
||||
Bridge は、エージェントとコンポーネント間の双方向通信を可能にします。
|
||||
また、Bridge はエージェント同士の通信を可能にします。
|
||||
### ディスパッチャー (Dispatchers)
|
||||
|
||||
### Dispatchers
|
||||
|
||||
Dispatcher は、コンポーネントとエージェント間の一方向通信を可能にします。
|
||||
Dispatcher は、コンポーネントがエージェントにメッセージを送信することを可能にします。
|
||||
ディスパッチャー(ディスパッチャー)は、コンポーネントとエージェント間の一方向通信を可能にし、コンポーネントがこの方法でエージェントにメッセージを送信します。
|
||||
|
||||
## オーバーヘッド
|
||||
|
||||
独自の独立した web worker(プライベートとパブリック)にあるエージェントは、送受信するメッセージにシリアライズするオーバーヘッドが発生します。
|
||||
他のスレッドとの通信には[bincode](https://github.com/servo/bincode)を使用するので、関数を呼び出すよりもコストはかなり高くなります。
|
||||
計算コストがメッセージの受け渡しコストを上回る場合を除き、ロジックを UI スレッドエージェント\(Job または Context\)に格納する必要があります。
|
||||
エージェントはWeb Workers(つまり、私有および公開)を使用します。メッセージの送受信時にシリアル化オーバーヘッドが発生します。エージェントは [bincode](https://github.com/bincode-org/bincode) を使用して他のスレッドと通信するため、コストは関数を呼び出すだけの場合よりもはるかに高くなります。
|
||||
|
||||
## 参考資料
|
||||
## さらなる読み物
|
||||
|
||||
- [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib)の例でコンポーネントがどのようにエージェントと通信させているかがわかります。
|
||||
- [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib) の例は、コンポーネントがエージェントにメッセージを送信し、エージェントからのメッセージを受信する方法を示しています。
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
title: 'classes! マクロを使用して CSS クラスを処理する'
|
||||
description: '便利なマクロを使用して CSS クラスを処理する'
|
||||
comment: 'ファイルを短くシンプルに保つようにしてください。これは、読者が Yew のコンポーネントをより理解しやすくするためであり、正確な API ドキュメントを提供するためではありません。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
Yew はネイティブの CSS-in-Rust ソリューションを提供していませんが、HTML の `class` 属性とプログラム的に対話する方法を提供することでスタイルを支援します。
|
||||
|
||||
## `classes!` マクロ
|
||||
|
||||
`classes!` マクロと関連する `Classes` 構造体は、HTML クラスの使用を簡素化します:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Literal" label="Literal">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!("container")}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Multiple" label="Multiple">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!("class-1", "class-2")}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="String" label="String">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(String::from("class-1 class-2"))}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Optional" label="Optional">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(Some("class"))} />
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Vector" label="Vector">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(vec!["class-1", "class-2"])}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Slice" label="Slice">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(["class-1", "class-2"].as_ref())}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
詳細な CSS に関する内容は[こちらのドキュメント](../../more/css)をご覧ください。
|
||||
|
||||
## インラインスタイル
|
||||
|
||||
現在、Yew は `style` 属性を使用して指定されたインラインスタイルを処理するための特別な支援ツールを提供していませんが、他の HTML 属性と同様に処理することができます:
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div style="color: red;"></div>
|
||||
};
|
||||
```
|
||||
|
||||
詳細な CSS に関する内容は[こちらのドキュメント](../../more/css)をご覧ください。
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: 'html! マクロを使用してHTMLを処理する'
|
||||
description: 'これはHTMLですが、完全にそうではありません!'
|
||||
comment: 'ファイルを短く簡潔に保つようにしてください。これは、読者がYewのコンポーネントをより簡単に理解できるようにするためであり、正確なAPIドキュメントを提供するためではありません。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
`html!` マクロを使用して、HTML に似た式を記述できます。Yew はバックグラウンドでそれを DOM を表現する Rust コードに変換します。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let my_header: Html = html! {
|
||||
<img src="img_girl.jpg" alt="Girl in a jacket" width="500" height="600" />
|
||||
};
|
||||
```
|
||||
|
||||
フォーマットされた式と同様に、波括弧を使用して周囲のコンテキストの値を HTML に埋め込むことができます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let header_text = "Hello world".to_string();
|
||||
let header_html: Html = html! {
|
||||
<h1>{header_text}</h1>
|
||||
};
|
||||
|
||||
let count: usize = 5;
|
||||
let counter_html: Html = html! {
|
||||
<p>{"My age is: "}{count}</p>
|
||||
};
|
||||
|
||||
let combined_html: Html = html! {
|
||||
<div>{header_html}{counter_html}</div>
|
||||
};
|
||||
```
|
||||
|
||||
`html!` を使用する際の重要なルールの 1 つは、1 つのラッピングノードしか返せないということです。複数の要素のリストをレンダリングするために、`html!` は空のタグ(フラグメント)の使用を許可しています。空のタグは名前のないタグで、それ自体は HTML 要素を生成しません。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Invalid" label="Invalid">
|
||||
|
||||
```rust , compile_fail
|
||||
use yew::html;
|
||||
|
||||
// エラー:ルート HTML 要素は1つだけ許可されています
|
||||
html! {
|
||||
|
||||
<div></div>
|
||||
<p></p>
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Valid" label="Valid">
|
||||
|
||||
```rust
|
||||
use yew::html;
|
||||
|
||||
// 修正:HTML 空のタグを使用してラップする
|
||||
html! {
|
||||
<>
|
||||
<div></div>
|
||||
<p></p>
|
||||
</>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
詳細については、[HTML の詳細](concepts/html/introduction.mdx)を参照してください。
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: 'JavaScript と Rust'
|
||||
description: 'Rust で JavaScript を使用する'
|
||||
comment: 'ファイルを簡潔に保つようにしてください。これは、読者が Yew のコンポーネントをより理解しやすくするためのものであり、正確な API ドキュメントを提供するためのものではありません。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
> Yew は、再利用可能な UI 部分に必要なすべてのコンテンツを1か所に集める一方で、必要に応じて基盤技術へのアクセスも維持します。
|
||||
|
||||
今日現在、WebAssembly は DOM との相互作用を完全にはサポートしていません。これは、Yew でも時々 JavaScript の呼び出しに依存することを意味します。次に、関係するライブラリの概要を示します。
|
||||
|
||||
## wasm-bindgen
|
||||
|
||||
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) は、JavaScript と Rust 関数の間に呼び出しの橋を架けるライブラリとツールです。
|
||||
|
||||
彼らの[ドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/)と私たちの[クイックガイド](./wasm-bindgen.mdx)を強くお勧めします。
|
||||
|
||||
## web-sys
|
||||
|
||||
[`web-sys` crate](https://crates.io/crates/web-sys) は Web API のバインディングを提供し、Rust で処理され安全な方法で JavaScript コードを書くことを可能にします。
|
||||
|
||||
例:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="JS" label="JS">
|
||||
|
||||
```js
|
||||
let document = window.document
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="RS" label="RS">
|
||||
|
||||
```rust ,no_run
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
use web_sys::window;
|
||||
|
||||
let document = window()
|
||||
.expect_throw("window is undefined")
|
||||
.document()
|
||||
.expect_throw("document is undefined");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
繰り返しになりますが、彼らの[ドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/)と私たちの[クイックガイド](./web-sys.mdx)を強くお勧めします。
|
|
@ -0,0 +1,177 @@
|
|||
---
|
||||
title: 'wasm-bindgen'
|
||||
sidebar_label: wasm-bindgen
|
||||
---
|
||||
|
||||
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) は、JavaScript と Rust 関数の間に呼び出しブリッジを作成するためのライブラリおよびツールです。これは [Rust と WebAssembly ワーキンググループ](https://rustwasm.github.io/) によって Rust で構築されました。
|
||||
|
||||
Yew は `wasm-bindgen` を使用して、いくつかのクレートを介してブラウザと対話します:
|
||||
|
||||
- [`js-sys`](https://crates.io/crates/js-sys)
|
||||
- [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||||
- [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||||
- [`web-sys`](https://crates.io/crates/web-sys)
|
||||
|
||||
このセクションでは、これらのクレートをより抽象的なレベルから探求し、Yew での `wasm-bindgen` API の理解と使用を容易にします。`wasm-bindgen` および関連するクレートに関する詳細なガイドについては、[`wasm-bindgen` ガイド](https://rustwasm.github.io/docs/wasm-bindgen/) を参照してください。
|
||||
|
||||
上記のクレートのドキュメントについては、[`wasm-bindgen docs.rs`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) を参照してください。
|
||||
|
||||
:::tip
|
||||
`wasm-bindgen` doc.rs 検索を使用して、`wasm-bindgen` を使用してインポートされたブラウザ API および JavaScript タイプを見つけます。
|
||||
:::
|
||||
|
||||
## [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||||
|
||||
このクレートは、上記の他のクレートに多くの構成要素を提供します。このセクションでは、`wasm-bindgen` クレートの主要な領域の 2 つ、つまりマクロと、何度も目にするタイプ/トレイトのいくつかについてのみ説明します。
|
||||
|
||||
### `#[wasm_bindgen]` マクロ
|
||||
|
||||
`#[wasm_bindgen]` マクロは Rust と JavaScript の間のインターフェースを提供し、両者の間で変換を行うシステムを提供します。このマクロの使用はより高度であり、外部の JavaScript ライブラリを使用する場合を除いて使用しないでください。`js-sys` および `web-sys` クレートは、組み込みの JavaScript タイプおよびブラウザ API に対して `wasm-bindgen` 定義を提供します。
|
||||
|
||||
`#[wasm-bindgen]` マクロを使用して、特定のバージョンの [`console.log`](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) 関数をインポートする簡単な例を見てみましょう。
|
||||
|
||||
```rust ,no_run
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// まず、`web_sys` を使用せずに `console.log` を手動でバインドしてみましょう。
|
||||
// ここでは、手動で `#[wasm_bindgen]` アノテーションを書きます。プログラムの正確性はこれらのアノテーションの正確性に依存します!
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
// ここで `js_namespace` を使用して `console.log(..)` をバインドします。`log(..)` だけではありません。
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
|
||||
// `console.log` は多態的なので、複数のシグネチャを使用してバインドできます。
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_u32(a: u32);
|
||||
|
||||
// 複数の引数も可能です!
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_many(a: &str, b: &str);
|
||||
}
|
||||
|
||||
// インポートされた関数を使用します!
|
||||
log("Hello from Rust!");
|
||||
log_u32(42);
|
||||
log_many("Logging", "many values!");
|
||||
```
|
||||
|
||||
_この例は、[1.2 `wasm-bindgen` ガイドの console.log を使用する](https://rustwasm.github.io/docs/wasm-bindgen/examples/console-log.html) に基づいています。_
|
||||
|
||||
### 継承のシミュレーション
|
||||
|
||||
JavaScript クラス間の継承は、JavaScript 言語のコア機能であり、DOM(ドキュメントオブジェクトモデル)はそれを中心に設計されています。`wasm-bindgen` を使用して型をインポートする際にも、それらの継承関係を記述する属性を追加できます。
|
||||
|
||||
Rust では、この継承関係は [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) と [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) トレイトを使用して表現されます。ここで例を挙げると役立つかもしれません。例えば、`A`、`B`、`C` という 3 つの型があり、`C` が `B` を拡張し、`B` が `A` を拡張しているとします。
|
||||
|
||||
これらの型をインポートする際、`#[wasm-bindgen]` マクロは次のように `Deref` と `AsRef` トレイトを実装します:
|
||||
|
||||
- `C` は `B` に `Deref` できます
|
||||
- `B` は `A` に `Deref` できます
|
||||
- `C` は `B` に `AsRef` できます
|
||||
- `C` と `B` はどちらも `A` に `AsRef` できます
|
||||
|
||||
これらの実装により、`C` のインスタンスで `A` のメソッドを呼び出したり、`C` を `&B` または `&A` として使用したりできます。
|
||||
|
||||
注意すべき点は、`#[wasm-bindgen]` を使用してインポートされたすべての型には同じルート型があり、それを上記の例の `A` と見なすことができるということです。この型は [`JsValue`](#jsvalue) であり、以下にそのセクションがあります。
|
||||
|
||||
_[`wasm-bindgen` ガイドの extends セクション](https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_
|
||||
|
||||
### [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)
|
||||
|
||||
これは JavaScript が所有するオブジェクトの表現であり、`wasm-bindgen` のルートキャプチャ型です。`wasm-bindgen` からの任意の型は `JsValue` です。これは、JavaScript には強い型システムがないため、変数 `x` を受け取る任意の関数がその型を定義しないため、`x` は有効な JavaScript 値である可能性があるためです。したがって `JsValue` です。`JsValue` を受け取るインポート関数や型を使用している場合、技術的には任意のインポート値が有効です。
|
||||
|
||||
`JsValue` は関数で受け取ることができますが、その関数は特定の型のみを受け取る可能性があり、それがパニックを引き起こす可能性があります。したがって、元の `wasm-bindgen` API を使用する場合は、インポートされた JavaScript のドキュメントを確認して、その値が特定の型でない場合に例外(パニック)を引き起こすかどうかを確認してください。
|
||||
|
||||
_[`JsValue` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)_
|
||||
|
||||
### [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)
|
||||
|
||||
Rust には強い型システムがありますが、JavaScript にはありません😞。Rust がこれらの強い型を維持しながらも便利であるために、WebAssembly ワーキンググループは非常に巧妙な機能 `JsCast` を提案しました。これは、ある JavaScript "型" から別の "型" への変換を支援するものです。これは曖昧に聞こえますが、ある型が別の型であることがわかっている場合、`JsCast` の関数を使用してある型から別の型にジャンプできます。`web-sys`、`wasm_bindgen`、`js-sys` を使用する際にこの機能を理解しておくと便利です。これらのクレートから多くの型が `JsCast` を実装していることに気付くでしょう。
|
||||
|
||||
`JsCast` はチェック付きとチェックなしの変換メソッドを提供します。したがって、実行時にオブジェクトがどの型であるかわからない場合は、変換を試みることができ、失敗する可能性のある型として [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) や [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) を返します。
|
||||
|
||||
一般的な例は [`web-sys`](./web-sys.mdx) で、イベントのターゲットを取得しようとする場合です。ターゲット要素が何であるかを知っているかもしれませんが、[`web_sys::Event`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html) API は常に [`Option<web_sys::EventTarget>`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target) を返します。
|
||||
その要素型に変換する必要があり、そのメソッドを呼び出すことができます。
|
||||
|
||||
```rust
|
||||
// このトレイトを最初にインポートする必要があります
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement};
|
||||
|
||||
fn handle_event(event: Event) {
|
||||
let target: EventTarget = event
|
||||
.target()
|
||||
.expect("I'm sure this event has a target!");
|
||||
|
||||
// もしかしたらターゲットは選択要素かもしれませんか?
|
||||
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
|
||||
// 別のことをする
|
||||
return;
|
||||
}
|
||||
|
||||
// それが選択要素でないことが確実であれば、入力要素であることが確実です!
|
||||
let input_element: HtmlInputElement = target.unchecked_into();
|
||||
}
|
||||
```
|
||||
|
||||
[`dyn_ref`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_ref) メソッドはチェック付きの変換であり、`Option<&T>` を返します。これは、変換が失敗した場合に元の型を再度使用できることを意味し、`None` を返します。 [`dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) メソッドは `self` を消費し、Rust の `into` メソッドの規約に従い、`Result<T, Self>` 型を返します。変換が失敗した場合、元の `Self` 値は `Err` に返されます。再試行するか、元の型で他の操作を行うことができます。
|
||||
|
||||
_[`JsCast` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)._
|
||||
|
||||
### [`Closure`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)
|
||||
|
||||
`Closure` 型は、Rust のクロージャを JavaScript に渡す方法を提供します。健全性の理由から、JavaScript に渡されるクロージャは `'static` ライフタイムを持つ必要があります。
|
||||
|
||||
この型は「ハンドル」であり、破棄されると、それが参照する JS クロージャを無効にします。`Closure` が破棄された後、JS 内のクロージャの使用はすべて例外を引き起こします。
|
||||
|
||||
`Closure` は、[`&js_sys::Function`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Function.html) 型を受け取る `js-sys` または `web-sys` API を使用する際に一般的に使用されます。Yew で `Closure` を使用する例は、[Events](../html/events.mdx) ページの[Using `Closure` セクション](../html/events.mdx#using-closure-verbose) にあります。
|
||||
|
||||
_[`Closure` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)._
|
||||
|
||||
## [`js-sys`](https://crates.io/crates/js-sys)
|
||||
|
||||
`js-sys` クレートは、JavaScript の標準組み込みオブジェクトのバインディング/インポートを提供します。これには、それらのメソッドやプロパティが含まれます。
|
||||
|
||||
これは Web API を含みません。Web API は [`web-sys`](./web-sys.mdx) の役割です!
|
||||
|
||||
_[`js-sys` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/js_sys/index.html)._
|
||||
|
||||
## [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||||
|
||||
`wasm-bindgen-futures` クレートは、JavaScript の Promise 型を Rust の [`Future`](https://doc.rust-lang.org/stable/std/future/trait.Future.html) として扱うためのブリッジを提供し、Rust の Future を JavaScript の Promise に変換するユーティリティを含みます。Rust(wasm)で非同期または他のブロッキング作業を処理する際に役立ち、JavaScript のイベントや JavaScript I/O プリミティブと対話する能力を提供します。
|
||||
|
||||
現在、このクレートには3つの主要なインターフェースがあります:
|
||||
|
||||
1. [`JsFuture`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/struct.JsFuture.html) -
|
||||
[`Promise`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Promise.html) を使用して構築された型で、`Future<Output=Result<JsValue, JsValue>>` として使用できます。`Promise` が解決されると、この `Future` は `Ok` に解決され、`Promise` が拒否されると `Err` に解決され、それぞれ `Promise` の解決または拒否の値を含みます。
|
||||
|
||||
2. [`future_to_promise`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) -
|
||||
Rust の `Future<Output=Result<JsValue, JsValue>>` を JavaScript の `Promise` に変換します。Future の結果は、JavaScript 内の解決または拒否された `Promise` に変換されます。
|
||||
|
||||
3. [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) -
|
||||
現在のスレッドで `Future<Output = ()>` を生成します。これは、Rust 内で Future を実行する最良の方法であり、JavaScript に送信するのではなく、Rust 内で実行します。
|
||||
|
||||
_[`wasm-bindgen-futures` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/index.html)._
|
||||
|
||||
### [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)
|
||||
|
||||
`spawn_local` は、非同期 API を使用するライブラリを使用する際に、Yew で `wasm-bindgen-futures` クレートの最も一般的に使用される部分です。
|
||||
|
||||
```rust ,no_run
|
||||
use web_sys::console;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
async fn my_async_fn() -> String { String::from("Hello") }
|
||||
|
||||
spawn_local(async {
|
||||
let mut string = my_async_fn().await;
|
||||
string.push_str(", world!");
|
||||
// "Hello, world!" を出力します
|
||||
console::log_1(&string.into());
|
||||
});
|
||||
```
|
||||
|
||||
Yew はいくつかの API に futures のサポートを追加しており、特に `async` ブロックを受け入れる `callback_future` を作成できることが注目されます。これは内部的に `spawn_local` を使用しています。
|
||||
|
||||
_[`spawn_local` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)._
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
title: 'web-sys'
|
||||
description: 'web-sys クレートは Web API のバインディングを提供します。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
[`web-sys` クレート](https://crates.io/crates/web-sys) は Web API のバインディングを提供します。これはブラウザの WebIDL から生成されるため、名前が長くなったり、型が曖昧になったりすることがあります。
|
||||
|
||||
## `web-sys` の特性 (features)
|
||||
|
||||
`web-sys` クレートで全ての特性を有効にすると、Wasm アプリケーションに多くの冗長性が追加される可能性があります。この問題を解決するために、ほとんどの型は特性を有効にすることで制御され、アプリケーションに必要な型だけを含めることができます。Yew は `web-sys` のいくつかの特性を有効にし、その公開 API でいくつかの型を公開しています。通常、`web-sys` を依存関係として追加する必要があります。
|
||||
|
||||
## `web-sys` の継承
|
||||
|
||||
[継承のシミュレーション](./wasm-bindgen.mdx#simulating-inheritance)のセクションでは、Rust が通常 JavaScript の継承をシミュレートする方法を提供していることがわかります。これは `web-sys` で非常に重要です。ある型にどのようなメソッドがあるかを理解するためには、その継承を理解する必要があります。
|
||||
|
||||
このセクションでは、特定の要素を見て、Rust で [`Deref::deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html#tymethod.deref) を呼び出して、その値が [`JsValue`](./wasm-bindgen.mdx#jsvalue) になるまでの継承をリストします。
|
||||
|
||||
```rust
|
||||
use std::ops::Deref;
|
||||
use web_sys::{
|
||||
Element,
|
||||
EventTarget,
|
||||
HtmlElement,
|
||||
HtmlTextAreaElement,
|
||||
Node,
|
||||
};
|
||||
|
||||
fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
|
||||
// HtmlTextAreaElement は HTML の <textarea> です。
|
||||
let html_element: &HtmlElement = text_area.deref();
|
||||
|
||||
let element: &Element = html_element.deref();
|
||||
|
||||
let node: &Node = element.deref();
|
||||
|
||||
let event_target: &EventTarget = node.deref();
|
||||
|
||||
// 注意: ここで web-sys タイプから js-sys クレート内の組み込み JavaScript タイプに移行しました。
|
||||
let object: &js_sys::Object = event_target.deref();
|
||||
|
||||
// 注意: ここで js-sys タイプから wasm-bindgen クレートのルート JsValue に移行しました。
|
||||
let js_value: &wasm_bindgen::JsValue = object.deref();
|
||||
|
||||
// このように deref を使用することで、継承ツリーを手動でたどる必要があります。
|
||||
// しかし、HtmlTextAreaElement タイプで JsValue メソッドを呼び出すことができます。
|
||||
assert!(!text_area.is_string());
|
||||
|
||||
// この空の関数は、HtmlTextAreaElement を &EventTarget として渡すことができることを示すためのものです。
|
||||
fn this_function_only_takes_event_targets(targets: &EventTarget) {};
|
||||
|
||||
// コンパイラはここでタイプを一致させるために deref チェーンを下にたどります。
|
||||
this_function_only_takes_event_targets(&text_area);
|
||||
|
||||
// AsRef 実装により、HtmlTextAreaElement を &EventTarget として扱うことができます。
|
||||
let event_target: &EventTarget = text_area.as_ref();
|
||||
}
|
||||
```
|
||||
|
||||
[`wasm-bindgen` ガイドの `web-sys` 継承](https://rustwasm.github.io/wasm-bindgen/web-sys/inheritance.html)
|
||||
|
||||
## `NodeRef` の `Node`
|
||||
|
||||
Yew は [`NodeRef`](concepts/function-components/node-refs.mdx) を使用して、[`html!`](concepts/html/introduction.mdx) マクロによって作成された `Node` の参照を保持する方法を提供します。`NodeRef` の `Node` は [`web_sys::Node`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Node.html) を指します。`NodeRef::get` メソッドは `Option<Node>` 値を返しますが、Yew ではほとんどの場合、この値を特定の要素に変換して、その特定のメソッドを使用することを望みます。存在する場合、[`JsCast`](./wasm-bindgen.mdx#JsCast) を使用して `Node` 値を変換できますが、Yew はこの変換を実行するための `NodeRef::cast` メソッドを提供しているため、`JsCast` 特性のために `wasm-bindgen` 依存関係を含める必要はありません。
|
||||
|
||||
以下の2つのコードブロックは本質的に同じです。最初のものは `NodeRef::cast` を使用し、2 番目のものは `NodeRef::get` が返す `web_sys::Node` 上で [`JsCast::dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) を使用しています。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Using NodeRef::cast" label="Using NodeRef::cast">
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::NodeRef;
|
||||
|
||||
fn with_node_ref_cast(node_ref: NodeRef) {
|
||||
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
|
||||
// HtmlInputElement をここで処理します
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Using NodeRef::get" label="Using NodeRef::get">
|
||||
|
||||
```rust
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::NodeRef;
|
||||
|
||||
fn with_jscast(node_ref: NodeRef) {
|
||||
if let Some(input) = node_ref
|
||||
.get()
|
||||
.and_then(|node| node.dyn_into::<HtmlInputElement>().ok()) {
|
||||
// HtmlInputElement をここで処理します
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Rust にリファクタリングされた JavaScript の例
|
||||
|
||||
このセクションでは、Web API と対話する JavaScript コードの例を Rust の `web-sys` にリファクタリングする方法を示します。
|
||||
|
||||
### JavaScript の例
|
||||
|
||||
```js
|
||||
document.getElementById('mousemoveme').onmousemove = (e) => {
|
||||
// e はマウスイベントオブジェクトです
|
||||
var rect = e.target.getBoundingClientRect()
|
||||
var x = e.clientX - rect.left // 要素内の x 位置。
|
||||
var y = e.clientY - rect.top // 要素内の y 位置。
|
||||
console.log('Left? : ' + x + ' ; Top? : ' + y + '.')
|
||||
}
|
||||
```
|
||||
|
||||
### `web-sys` を使用して書き直した例
|
||||
|
||||
`web-sys` のみを使用して、上記の JavaScript の例は次のように実装できます:
|
||||
|
||||
```toml title=Cargo.toml
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
# 使用したいすべての web-sys 機能を有効にする必要があります!
|
||||
features = [
|
||||
"console",
|
||||
"Document",
|
||||
"HtmlElement",
|
||||
"MouseEvent",
|
||||
"DomRect",
|
||||
]
|
||||
```
|
||||
|
||||
```rust ,no_run
|
||||
use wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use web_sys::{console, Document, HtmlElement, MouseEvent};
|
||||
|
||||
let mousemove = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|e| {
|
||||
let rect = e
|
||||
.target()
|
||||
.expect("mouse event doesn't have a target")
|
||||
.dyn_into::<HtmlElement>()
|
||||
.expect("event target should be of type HtmlElement")
|
||||
.get_bounding_client_rect();
|
||||
let x = (e.client_x() as f64) - rect.left();
|
||||
let y = (e.client_y() as f64) - rect.top();
|
||||
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
||||
}));
|
||||
|
||||
Document::new()
|
||||
.expect("global document not set")
|
||||
.get_element_by_id("mousemoveme")
|
||||
.expect("element with id `mousemoveme` not present")
|
||||
.unchecked_into::<HtmlElement>()
|
||||
.set_onmousemove(mousemove.as_ref().dyn_ref());
|
||||
|
||||
// 現在、イベントが発生したときにクロージャがメモリに残るように、`mousemove` クロージャを保存する必要があります。
|
||||
```
|
||||
|
||||
このバージョンはより冗長ですが、その一部は失敗した型が私たちにいくつかの関数呼び出しに保持しなければならない不変条件を思い出させるためです。これらの不変条件が守られないと、Rust ではパニックが発生します。もう一つの冗長な部分は、特定のメソッドを呼び出すために異なる型を特定の型に変換するための `JsCast` の呼び出しです。
|
||||
|
||||
### Yew で書き直した例
|
||||
|
||||
Yew では、主に [`Callback`](concepts/function-components/callbacks.mdx) を作成して [`html!`](concepts/html/introduction.mdx) マクロで使用するため、例はこの方法を使用します。上記の方法を完全にコピーするのではなく、この方法を使用します:
|
||||
|
||||
```toml title=Cargo.toml
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
# `get_bounding_client_rect` メソッドを使用するには、`DomRect` 特性を有効にする必要があります。
|
||||
features = [
|
||||
"console",
|
||||
"HtmlElement",
|
||||
"MouseEvent",
|
||||
"DomRect",
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
```rust
|
||||
use web_sys::{console, HtmlElement, MouseEvent};
|
||||
use yew::{
|
||||
html,
|
||||
Callback, TargetCast,
|
||||
};
|
||||
|
||||
let onmousemove = Callback::from(|e: MouseEvent| {
|
||||
if let Some(target) = e.target_dyn_into::<HtmlElement>() {
|
||||
let rect = target.get_bounding_client_rect();
|
||||
let x = (e.client_x() as f64) - rect.left();
|
||||
let y = (e.client_y() as f64) - rect.top();
|
||||
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<div id="mousemoveme" {onmousemove}></div>
|
||||
};
|
||||
```
|
||||
|
||||
## 追加の依存ライブラリ
|
||||
|
||||
`web-sys` は Web API の生のバインディングであるため、Rust で使用する際にはいくつかの困難が伴います。これは、`web-sys` が Rust や強い型システムのために設計されていないためです。そこで、コミュニティのクレートが `web-sys` に対する抽象化を提供し、Rust の慣習により適した API を提供しています。
|
||||
|
||||
_[追加の依存ライブラリ一覧](/community/external-libs)_
|
|
@ -0,0 +1,188 @@
|
|||
---
|
||||
title: 'コンテキスト (Contexts)'
|
||||
sidebar_label: コンテキスト
|
||||
description: 'コンテキストを使用して深くネストされたデータを渡す'
|
||||
---
|
||||
|
||||
通常、データは props を介して親コンポーネントから子コンポーネントに渡されます。
|
||||
しかし、多くの中間コンポーネントを介してデータを渡す必要がある場合や、アプリケーション内の多くのコンポーネントが同じ情報を必要とする場合、props を介してデータを渡すことは冗長で煩わしいものになります。
|
||||
コンテキストはこの問題を解決し、親コンポーネントがデータをその下のツリー内の任意のコンポーネントに渡すことを可能にし、props を介してデータを渡す必要がなくなります。
|
||||
|
||||
## Props を使用する際の問題:"Prop Drilling"
|
||||
|
||||
[props](./function-components/properties.mdx) を介してデータを親コンポーネントから直接子コンポーネントに渡すことは良い方法です。
|
||||
しかし、深くネストされたコンポーネントツリーを介してデータを渡す必要がある場合や、複数のコンポーネントが同じデータを共有する必要がある場合、props を渡すことは煩雑になります。
|
||||
一般的なデータ共有の解決策は、データを共通の祖先に持ち上げ、子コンポーネントがそれを props として受け取るようにすることです。
|
||||
しかし、これにより props が複数のコンポーネントを介して渡される必要がある場合があります。
|
||||
この状況は "Prop Drilling" と呼ばれます。
|
||||
|
||||
以下の例を考えてみましょう。これは props を介してテーマを渡しています:
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html, Properties, function_component};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Theme {
|
||||
foreground: String,
|
||||
background: String,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct NavbarProps {
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Navbar(props: &NavbarProps) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<Title theme={props.theme.clone()}>
|
||||
{ "App title" }
|
||||
</Title>
|
||||
<NavButton theme={props.theme.clone()}>
|
||||
{ "Somewhere" }
|
||||
</NavButton>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct ThemeProps {
|
||||
theme: Theme,
|
||||
children: Html,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Title(_props: &ThemeProps) -> Html {
|
||||
html! {
|
||||
// impl
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn NavButton(_props: &ThemeProps) -> Html {
|
||||
html! {
|
||||
// impl
|
||||
}
|
||||
}
|
||||
|
||||
/// アプリのルート
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let theme = Theme {
|
||||
foreground: "yellow".to_owned(),
|
||||
background: "pink".to_owned(),
|
||||
};
|
||||
|
||||
html! {
|
||||
<Navbar {theme} />
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
私たちはテーマ設定を `Navbar` に渡して、それが `Title` と `NavButton` に到達するようにしています。
|
||||
もし `Title` と `NavButton` のようなテーマにアクセスする必要があるコンポーネントが、prop を介さずに直接テーマにアクセスできるとしたら、もっと良いでしょう。
|
||||
コンテキストはこの問題を解決し、親コンポーネントがデータ(この場合はテーマ)をその子コンポーネントに渡すことを可能にします。
|
||||
|
||||
## コンテキストの使用
|
||||
|
||||
### ステップ 1:コンテキストの提供
|
||||
|
||||
コンテキストを消費するには、コンテキストプロバイダーが必要です。`ContextProvider<T>` は、`T` がコンテキスト構造体として使用されるプロバイダーです。
|
||||
`T` は `Clone` と `PartialEq` を実装する必要があります。`ContextProvider` は、その子コンポーネントがコンテキストを持つコンポーネントです。
|
||||
コンテキストが変更されると、子コンポーネントは再レンダリングされます。データを渡すための構造体が定義されます。`ContextProvider` は次のように使用できます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
/// アプリのテーマ
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Theme {
|
||||
foreground: String,
|
||||
background: String,
|
||||
}
|
||||
|
||||
/// メインコンポーネント
|
||||
#[function_component]
|
||||
pub fn App() -> Html {
|
||||
let ctx = use_state(|| Theme {
|
||||
foreground: "#000000".to_owned(),
|
||||
background: "#eeeeee".to_owned(),
|
||||
});
|
||||
|
||||
html! {
|
||||
// `ctx` は `Rc<UseStateHandle<Theme>>` 型であり、`Theme` が必要です
|
||||
// したがって、デリファレンスします。
|
||||
<ContextProvider<Theme> context={(*ctx).clone()}>
|
||||
// ここにあるすべての子コンポーネントとその子コンポーネントは、このコンテキストにアクセスします。
|
||||
<Toolbar />
|
||||
</ContextProvider<Theme>>
|
||||
}
|
||||
}
|
||||
|
||||
/// ツールバー
|
||||
/// このコンポーネントはコンテキストにアクセスできます。
|
||||
#[function_component]
|
||||
pub fn Toolbar() -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<ThemedButton />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
/// `Toolbar` 内に配置されたボタン
|
||||
/// このコンポーネントは、コンポーネントツリー内の `ThemeContextProvider` の子コンポーネントであるため、
|
||||
/// コンテキストにアクセスできます。
|
||||
#[function_component]
|
||||
pub fn ThemedButton() -> Html {
|
||||
let theme = use_context::<Theme>().expect("no ctx found");
|
||||
|
||||
html! {
|
||||
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ステップ 2:コンテキストの使用
|
||||
|
||||
#### 関数コンポーネント
|
||||
|
||||
`use_context` フックは、関数コンポーネント内でコンテキストを使用するために使用されます。
|
||||
詳細については、[use_context ドキュメント](https://yew-rs-api.web.app/next/yew/functional/fn.use_context.html) を参照してください。
|
||||
|
||||
#### 構造体コンポーネント
|
||||
|
||||
構造体コンポーネント内でコンテキストを使用するには、2つの方法があります:
|
||||
|
||||
- [高階コンポーネント](../advanced-topics/struct-components/hoc.mdx):高階関数コンポーネントがコンテキストを使用し、必要なデータを構造体コンポーネントに渡します。
|
||||
- 構造体コンポーネント内で直接コンテキストを使用します。詳細については、[構造体コンポーネントのコンシューマーとしての例](https://github.com/yewstack/yew/tree/master/examples/contexts/src/struct_component_subscriber.rs) を参照してください。
|
||||
|
||||
## 使用シナリオ
|
||||
|
||||
通常、ツリーの異なる部分のリモートコンポーネントでデータを使用する必要がある場合、コンテキストが役立ちます。
|
||||
以下はいくつかの例です:
|
||||
|
||||
- **テーマ**:アプリケーションのトップにコンテキストを配置し、アプリケーションのテーマを保持し、視覚的な外観を調整するために使用できます(上記の例を参照)。
|
||||
- **現在のユーザーアカウント**:多くの場合、コンポーネントは現在ログインしているユーザーを知る必要があります。コンテキストを使用して、現在のユーザーオブジェクトをコンポーネントに提供できます。
|
||||
|
||||
### コンテキストを使用する前の考慮事項
|
||||
|
||||
コンテキストは非常に使いやすいですが、それが誤用/過度に使用される可能性もあります。
|
||||
複数のレベル深いコンポーネントに props を共有するためにコンテキストを使用できるからといって、必ずしもそうすべきではありません。
|
||||
|
||||
例えば、コンポーネントを抽出して、そのコンポーネントを別のコンポーネントの子コンポーネントとして渡すことができます。
|
||||
例えば、`Layout` コンポーネントが `articles` を prop として受け取り、それを `ArticleList` コンポーネントに渡す場合、
|
||||
`Layout` コンポーネントをリファクタリングして、子コンポーネントを props として受け取り、`<Layout> <ArticleList {articles} /> </Layout>` と表示するようにするべきです。
|
||||
|
||||
## 子コンポーネントのコンテキスト値を変更する
|
||||
|
||||
Rust の所有権ルールにより、コンテキストには子コンポーネントが呼び出せる `&mut self` メソッドを持つことができません。
|
||||
コンテキストの値を変更するには、リデューサーと組み合わせて使用する必要があります。これは、[`use_reducer`](https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html) フックを使用して行うことができます。
|
||||
|
||||
[コンテキストの例](https://github.com/yewstack/yew/tree/master/examples/contexts) は、可変コンテキストの使用を示しています。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [コンテキストの例](https://github.com/yewstack/yew/tree/master/examples/contexts)
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: 'コールバック (Callbacks)'
|
||||
---
|
||||
|
||||
コールバック関数は、コンポーネントツリー内で情報を上向きに伝達したり、イベント処理中に他のコンポーネント(例えばエージェントやDOM)と通信したりするために使用されます。内部的には、コールバック関数の型は単なる `Fn` であり、安価にクローンできるように `Rc` に包まれています。
|
||||
|
||||
コールバック関数を手動で呼び出したい場合は、`emit` 関数を使用できます。
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html, Callback};
|
||||
|
||||
let cb: Callback<String, String> = Callback::from(move |name: String| {
|
||||
format!("Bye {}", name)
|
||||
});
|
||||
|
||||
let result = cb.emit(String::from("Bob")); // コールバック関数を呼び出す
|
||||
// web_sys::console::log_1(&result.into()); // コメントを解除すると、「Bye Bob」 が出力されます
|
||||
```
|
||||
|
||||
## コールバック関数をプロパティとして渡す
|
||||
|
||||
yew で一般的なパターンは、コールバック関数を作成し、それをプロパティとして子コンポーネントに渡すことです。
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties, Callback};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub on_name_entry: Callback<String>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(props: &Props) -> Html {
|
||||
|
||||
props.on_name_entry.emit(String::from("Bob"));
|
||||
|
||||
html! { "Hello" }
|
||||
}
|
||||
|
||||
// 次にプロパティ (Props) を提供します
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let on_name_entry: Callback<String> = Callback::from(move |name: String| {
|
||||
let greeting = format!("Hey, {}!", name);
|
||||
// web_sys::console::log_1(&greeting.into()); // コメントを解除すると、ここにテキストが出力されます
|
||||
});
|
||||
|
||||
html! { <HelloWorld {on_name_entry} /> }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## DOM イベントとコールバック関数
|
||||
|
||||
コールバック関数は、DOM イベントに接続するためにも使用されます。
|
||||
|
||||
例えば、ここではユーザーがボタンをクリックしたときに呼び出されるコールバック関数を定義します:
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties, Callback};
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let onclick = Callback::from(move |_| {
|
||||
let greeting = String::from("Hi there");
|
||||
// web_sys::console::log_1(&greeting.into()); // コメントを解除すると、ここにテキストが出力されます
|
||||
});
|
||||
|
||||
html! {
|
||||
<button {onclick}>{ "Click" }</button>
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
title: '子要素 (Children)'
|
||||
---
|
||||
|
||||
`Children` は特別なプロパティタイプで、HTMLの子要素のようにネストされた `Html` を受け取ることができます。
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties};
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! {
|
||||
// highlight-start
|
||||
<HelloWorld>
|
||||
<span>{"Hey what is up ;)"}</span>
|
||||
<h1>{"THE SKY"}</h1>
|
||||
</HelloWorld>
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
// highlight-next-line
|
||||
pub children: Html, // `children` キーは重要です!
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(props: &Props) -> Html {
|
||||
html! {
|
||||
<div class="very-stylized-container">
|
||||
// highlight-next-line
|
||||
{ props.children.clone() } // この方法で子要素を転送できます
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: 'コンポーネント間の通信'
|
||||
---
|
||||
|
||||
## 親コンポーネントから子コンポーネントへのメッセージ送信
|
||||
|
||||
データを [props](./properties) として渡すと、再レンダリングが発生し、これが子コンポーネントにメッセージを渡す方法です。
|
||||
|
||||
## 子コンポーネントから親コンポーネントへのメッセージ送信
|
||||
|
||||
props を介してコールバックを渡し、子コンポーネントはイベントでそれを呼び出すことができます。[例](callbacks#passing-callbacks-as-props)
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: 'ジェネリックコンポーネント'
|
||||
description: '関数コンポーネントの #[function_component] 属性'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
`#[function_component]` 属性は、ジェネリックコンポーネントを作成するためのジェネリック関数にも適用されます。
|
||||
|
||||
```rust
|
||||
use std::fmt::Display;
|
||||
use yew::{function_component, html, Properties, Html};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
data: T,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
|
||||
where
|
||||
T: PartialEq + Clone + Into<Html>,
|
||||
{
|
||||
html! {
|
||||
<p>
|
||||
{ props.data.clone().into() }
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
// その後、このように使用できます
|
||||
html! {
|
||||
<MyGenericComponent<i32> data=123 />
|
||||
};
|
||||
|
||||
// または
|
||||
html! {
|
||||
<MyGenericComponent<String> data={"foo".to_string()} />
|
||||
};
|
||||
```
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
title: 'カスタムフック'
|
||||
---
|
||||
|
||||
## カスタムフックの定義
|
||||
|
||||
コンポーネントのステートフルなロジックは、カスタムフックを作成することで再利用可能な関数として抽出できます。
|
||||
|
||||
例えば、`window` オブジェクト上のイベントをリッスンするイベントリスナーを作成したいとします。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use gloo::events::EventListener;
|
||||
use gloo::utils::window;
|
||||
use std::mem::drop;
|
||||
|
||||
|
||||
#[function_component(ShowStorageChanged)]
|
||||
pub fn show_storage_changed() -> Html {
|
||||
let state_storage_changed = use_state(|| false);
|
||||
|
||||
{
|
||||
let state_storage_changed = state_storage_changed.clone();
|
||||
use_effect(|| {
|
||||
let listener = EventListener::new(&window(), "storage", move |_| state_storage_changed.set(true));
|
||||
|
||||
move || { drop(listener); }
|
||||
});
|
||||
}
|
||||
|
||||
html! { <div>{"Storage Event Fired: "}{*state_storage_changed}</div> }
|
||||
}
|
||||
```
|
||||
|
||||
このコードには問題があります。ロジックは他のコンポーネントで再利用できません。異なるイベントをリッスンする別のコンポーネントを作成する場合、コードをコピーするのではなく、ロジックをカスタムフックに移すことができます。
|
||||
|
||||
まず、`use_event` という新しい関数を作成します。`use_` プレフィックスは関数がフックであることを示します。この関数はイベントターゲット、イベントタイプ、およびコールバックを受け取ります。すべてのフックはその関数定義に `#[hook]` とマークする必要があります。
|
||||
|
||||
```rust
|
||||
use web_sys::{Event, EventTarget};
|
||||
use std::borrow::Cow;
|
||||
use gloo::events::EventListener;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[hook]
|
||||
pub fn use_event<E, F>(target: &EventTarget, event_type: E, callback: F)
|
||||
where
|
||||
E: Into<Cow<'static, str>>,
|
||||
F: Fn(&Event) + 'static,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
このシンプルなフックは、組み込みのフックを組み合わせることで作成できます。この例では、`use_effect_with` フックを使用します。これにより、フックのパラメータが変更されたときにイベントリスナーを再作成できます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use web_sys::{Event, EventTarget};
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use gloo::events::EventListener;
|
||||
|
||||
#[hook]
|
||||
pub fn use_event<E, F>(target: &EventTarget, event_type: E, callback: F)
|
||||
where
|
||||
E: Into<Cow<'static, str>>,
|
||||
F: Fn(Event) + 'static,
|
||||
{
|
||||
#[derive(PartialEq, Clone)]
|
||||
struct EventDependents {
|
||||
target: EventTarget,
|
||||
event_type: Cow<'static, str>,
|
||||
callback: Callback<Event>,
|
||||
}
|
||||
|
||||
let deps = EventDependents {
|
||||
target: target.clone(),
|
||||
event_type: event_type.into(),
|
||||
callback: Callback::from(callback),
|
||||
};
|
||||
|
||||
use_effect_with(
|
||||
deps,
|
||||
|deps| {
|
||||
let EventDependents {
|
||||
target,
|
||||
event_type,
|
||||
callback,
|
||||
} = deps.clone();
|
||||
|
||||
let listener = EventListener::new(&target, event_type, move |e| {
|
||||
callback.emit(e.clone());
|
||||
});
|
||||
|
||||
move || {
|
||||
drop(listener);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
この方法はほとんどすべてのケースで有効ですが、私たちがすでに使用しているような基本的なフックを作成するためには使用できません。
|
||||
|
||||
[docs.rs](https://docs.rs/yew) 上のドキュメントや `hooks` ディレクトリを参照して、事前定義されたフックの実装を確認してください。
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: 'Hooks'
|
||||
slug: /concepts/function-components/hooks
|
||||
---
|
||||
|
||||
## Hooks
|
||||
|
||||
Hooks は、状態を保存し、副作用を実行することができる関数の一種です。
|
||||
|
||||
Yew はいくつかの事前定義された hooks を提供しています。また、自分で hooks を作成することもできますし、多くの[コミュニティ製の hooks](/community/awesome#hooks) を見つけることもできます。
|
||||
|
||||
## Hooks のルール
|
||||
|
||||
1. 各 Hook 関数の名前は `use_` で始める必要があります
|
||||
2. Hooks は次の場所でのみ使用できます:
|
||||
- 関数/ Hook のトップレベル
|
||||
- 関数/ Hook 内のブロック、ただし分岐していない場合
|
||||
- 関数/ Hook 内トップレベルの `if` 式の条件
|
||||
- 関数/ Hook 内トップレベルの `match` 式のセレクター
|
||||
3. 各レンダリング時に、Hooks は同じ順序で呼び出される必要があります。[Suspense](../../suspense.mdx) を使用する場合のみ、早期リターンが許可されます
|
||||
|
||||
これらのルールは、コンパイル時または実行時のエラーによって強制されます。
|
||||
|
||||
### 事前定義された Hooks
|
||||
|
||||
Yew は次の事前定義された Hooks を提供しています:
|
||||
|
||||
- `use_state`
|
||||
- `use_state_eq`
|
||||
- `use_memo`
|
||||
- `use_callback`
|
||||
- `use_ref`
|
||||
- `use_mut_ref`
|
||||
- `use_node_ref`
|
||||
- `use_reducer`
|
||||
- `use_reducer_eq`
|
||||
- `use_effect`
|
||||
- `use_effect_with`
|
||||
- `use_context`
|
||||
- `use_force_update`
|
||||
|
||||
これらの hooks のドキュメントは [Yew API ドキュメント](https://yew-rs-api.web.app/next/yew/functional/)で見つけることができます。
|
||||
|
||||
### カスタム Hooks
|
||||
|
||||
場合によっては、独自の Hooks を定義して、コンポーネント内の状態を持つ可能性のあるロジックを再利用可能な関数にカプセル化することが望ましいことがあります。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- React ドキュメントには [React hooks](https://reactjs.org/docs/hooks-intro.html) に関するセクションがあります。
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
title: '関数コンポーネント'
|
||||
slug: /concepts/function-components
|
||||
---
|
||||
|
||||
以前のスローガンをもう一度見てみましょう:
|
||||
|
||||
> Yew の核心思想は、再利用可能な UI 部分に必要なすべての内容を 1 つの場所 - Rust ファイルに集中させることです。
|
||||
|
||||
この声明を完成させるために、アプリケーションのロジックとレンダリングの動作を定義する概念を導入します:"コンポーネント"。
|
||||
|
||||
## コンポーネントとは?
|
||||
|
||||
コンポーネントは Yew の構成要素です。
|
||||
|
||||
それらは次のことを行うべきです:
|
||||
|
||||
- [Props](./properties.mdx) の形式でパラメータを受け取る
|
||||
- 独自の状態を持つことができる
|
||||
- ユーザーに見える HTML フラグメント(DOM)を計算する
|
||||
|
||||
## Yew コンポーネントの 2 つのフレーバー
|
||||
|
||||
現在、関数コンポーネントについて読んでいます - これは Yew を使い始めるときや、シンプルなレンダリングロジックを書くときにコンポーネントを書くための推奨方法です。
|
||||
|
||||
もう一つの、より高度ですがアクセスしにくいコンポーネントの書き方があります - [構造コンポーネント](advanced-topics/struct-components/introduction.mdx)。それらは非常に詳細な制御を可能にしますが、ほとんどの場合、そこまで詳細な制御は必要ありません。
|
||||
|
||||
## 関数コンポーネントの作成
|
||||
|
||||
関数コンポーネントを作成するには、関数に `#[function_component]` 属性を追加します。慣例として、関数の名前は PascalCase を使用し、`html!` マクロ内の通常の html 要素と対比させます。
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html};
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld() -> Html {
|
||||
html! { "Hello world" }
|
||||
}
|
||||
|
||||
// そして他の場所で、`html!` 内でコンポーネントを使用できます
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! { <HelloWorld /> }
|
||||
}
|
||||
```
|
||||
|
||||
## コンポーネント内部で何が起こっているのか
|
||||
|
||||
レンダリング時に、Yew はこれらのコンポーネントの仮想ツリーを構築します。各(関数)コンポーネントの view 関数を呼び出して、DOM の仮想バージョン(VDOM)を計算します。ライブラリのユーザーとして、これを `Html` 型として扱います。上記の例では、次のようになります:
|
||||
|
||||
```xhtml
|
||||
<App>
|
||||
<HelloWorld>
|
||||
<p>"Hello world"</p>
|
||||
</HelloWord>
|
||||
</App>
|
||||
```
|
||||
|
||||
更新が必要な場合、Yew は再び view 関数を呼び出し、新しい仮想 DOM を以前のバージョンと調整し、新しい/変更された/必要な部分のみを実際の DOM に伝播します。これが **レンダリング** と呼ばれるものです。
|
||||
|
||||
:::note
|
||||
|
||||
実際には、`Html` は `VNode` の別名に過ぎません - 仮想ノードです。
|
||||
|
||||
:::
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
title: 'ノード参照'
|
||||
description: 'DOM 外部アクセス'
|
||||
---
|
||||
|
||||
`ref` 属性を使用して、`NodeRef` を HTML 要素にアタッチできます。コールバック内で、`ref` がアタッチされた DOM `Element` を取得できます。これを使用して、`view` ライフサイクルメソッドの外部で DOM を変更したり、`<input>` の値を取得したり、JavaScript API を介して直接 DOM と対話したりできます。
|
||||
|
||||
これは、canvas 要素を取得したり、ページの異なる部分にスクロールしたりするのに便利です。
|
||||
|
||||
:::caution
|
||||
Yew がレンダリングした DOM ツリーを手動で変更しないでください。確信が持てない場合は、`NodeRef` を読み取り専用アクセスとして扱ってください。
|
||||
:::
|
||||
|
||||
## さらに読む
|
||||
|
||||
- [use_node_ref フック](https://yew-rs-api.web.app/next/yew/functional/fn.use_node_ref.html)
|
||||
- [`node_refs` の例](https://github.com/yewstack/yew/tree/master/examples/node_refs)
|
|
@ -0,0 +1,339 @@
|
|||
---
|
||||
title: 'プロパティ (Properties)'
|
||||
description: '親子コンポーネントの通信'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
:::note
|
||||
|
||||
プロパティ (Properties) は通常 "Props" と略されます。
|
||||
|
||||
:::
|
||||
|
||||
プロパティ (Properties) はコンポーネントのパラメータであり、Yew はこれらのパラメータを監視できます。
|
||||
|
||||
コンポーネントのプロパティで型を使用する前に、その型は `Properties` トレイトを実装している必要があります。
|
||||
|
||||
## リアクティブ性
|
||||
|
||||
再レンダリング時に、Yew は仮想DOMを調整する際にプロパティが変更されたかどうかを確認し、ネストされたコンポーネントを再レンダリングする必要があるかどうかを判断します。これにより、Yew は非常にリアクティブなフレームワークと見なされます。親コンポーネントからの変更は常に下位に伝播し、ビューはプロパティ/状態からのデータと常に同期します。
|
||||
|
||||
:::tip
|
||||
|
||||
まだ [チュートリアル](../../tutorial) を完了していない場合は、このリアクティブ性を自分でテストしてみてください!
|
||||
|
||||
:::
|
||||
|
||||
## 派生マクロ
|
||||
|
||||
Yew は、構造体に `Properties` トレイトを簡単に実装できる派生マクロを提供します。
|
||||
|
||||
`Properties` を派生する型は、Yew がデータ比較を行えるように `PartialEq` も実装している必要があります。
|
||||
|
||||
```rust
|
||||
use yew::Properties;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub is_loading: bool,
|
||||
}
|
||||
```
|
||||
|
||||
## 関数コンポーネントでの使用
|
||||
|
||||
属性 `#[function_component]` は、関数の引数で Props を選択的に受け取ることを可能にします。それらを提供するには、`html!` マクロ内の属性を通じて割り当てることができます。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="with-props" label="With Props">
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub is_loading: bool,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(&Props { is_loading }: &Props) -> Html {
|
||||
html! { <>{"Am I loading? - "}{is_loading}</> }
|
||||
}
|
||||
|
||||
// そしてプロパティを提供します
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! { <HelloWorld is_loading=true /> }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="no-props" label="No Props">
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html};
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld() -> Html {
|
||||
html! { "Hello world" }
|
||||
}
|
||||
|
||||
// 提供するプロパティはありません
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! { <HelloWorld /> }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 派生マクロフィールド属性
|
||||
|
||||
`Properties` を派生する際、デフォルトではすべてのフィールドが必須です。
|
||||
|
||||
以下の属性を使用すると、親コンポーネントがそれらを設定しなかった場合にデフォルト値を提供することができます。
|
||||
|
||||
:::tip
|
||||
属性は Rustdoc によって生成されたドキュメントには表示されません。属性のドキュメント文字列には、その属性がオプションであるかどうか、および特定のデフォルト値があるかどうかを記載する必要があります。
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="prop_or_default" label="#[prop_or_default]">
|
||||
|
||||
`Default` トレイトを使用して、フィールド型のデフォルト値でプロパティ値を初期化します。
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
// highlight-start
|
||||
#[prop_or_default]
|
||||
// highlight-end
|
||||
pub is_loading: bool,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(&Props { is_loading }: &Props) -> Html {
|
||||
if is_loading {
|
||||
html! { "Loading" }
|
||||
} else {
|
||||
html! { "Hello world" }
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルト値を使用する
|
||||
#[function_component]
|
||||
fn Case1() -> Html {
|
||||
html! { <HelloWorld /> }
|
||||
}
|
||||
// またはデフォルト値を上書きしない
|
||||
#[function_component]
|
||||
fn Case2() -> Html {
|
||||
html! { <HelloWorld is_loading=true /> }
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="prop_or_value" label="#[prop_or(value)]">
|
||||
|
||||
`value` を使用してプロパティ値を初期化します。`value` はフィールド型を返す任意の式である可能性があります。
|
||||
|
||||
例えば、ブールプロパティをデフォルトで `true` にするには、属性 `#[prop_or(true)]` を使用します。プロパティが構築されるときに、式が評価され、明示的な値が与えられていない場合に適用されます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
#[prop_or_default]
|
||||
pub is_loading: bool,
|
||||
// highlight-start
|
||||
#[prop_or(AttrValue::Static("Bob"))]
|
||||
// highlight-end
|
||||
pub name: AttrValue,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
|
||||
if is_loading {
|
||||
html! { "Loading" }
|
||||
} else {
|
||||
html! { <>{"Hello "}{name} </>}
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルト値を使用する
|
||||
#[function_component]
|
||||
fn Case1() -> Html {
|
||||
html! { <Hello /> }
|
||||
}
|
||||
// またはデフォルト値を上書きしない
|
||||
#[function_component]
|
||||
fn Case2() -> Html {
|
||||
html! { <Hello name="Sam" /> }
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="prop_or_else_function" label="#[prop_or_else(function)]">
|
||||
|
||||
属性値を初期化するために `function` を呼び出します。`function` は `FnMut() -> T` シグネチャを持つ必要があり、ここで `T` はフィールドの型です。このプロパティに明示的な値が与えられていない場合、その関数が呼び出されます。
|
||||
この関数はプロパティが構築されるときに呼び出されます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
fn create_default_name() -> AttrValue {
|
||||
AttrValue::Static("Bob")
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
#[prop_or_default]
|
||||
pub is_loading: bool,
|
||||
// highlight-start
|
||||
#[prop_or_else(create_default_name)]
|
||||
// highlight-end
|
||||
pub name: AttrValue,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
|
||||
if is_loading {
|
||||
html! { "Loading" }
|
||||
} else {
|
||||
html! { <>{"Hello "}{name}</> }
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルト値を使用する
|
||||
#[function_component]
|
||||
fn Case1() -> Html {
|
||||
html! { <Hello /> }
|
||||
}
|
||||
// またはデフォルト値を上書きしない
|
||||
#[function_component]
|
||||
fn Case2() -> Html {
|
||||
html! { <Hello name="Sam" /> }
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Properties のパフォーマンスオーバーヘッド
|
||||
|
||||
内部プロパティは参照カウントされたスマートポインタとして渡されます。これにより、コンポーネントツリー内のプロパティに対して共有ポインタが1つだけ渡されるため、プロパティ全体をクローンする高コストを節約できます。
|
||||
|
||||
:::tip
|
||||
`AttrValue` はプロパティ値に使用するカスタムタイプであり、これにより String やその他のクローンコストが高いタイプとして定義する必要がなくなります。
|
||||
:::
|
||||
|
||||
## Props マクロ
|
||||
|
||||
`yew::props!` マクロを使用すると、`html!` マクロと同じ方法でプロパティを構築できます。
|
||||
|
||||
このマクロは構造体の式と同じ構文を使用しますが、プロパティや基本式 (`Foo { ..base }`) を使用することはできません。タイプパスはプロパティ (`path::to::Props`) に直接指すことも、コンポーネントの関連プロパティ (`MyComp::Properties`) に指すこともできます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
#[prop_or_default]
|
||||
pub is_loading: bool,
|
||||
#[prop_or(AttrValue::Static("Bob"))]
|
||||
pub name: AttrValue,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Hello(&Props { is_loading, ref name }: &Props) -> Html {
|
||||
if is_loading {
|
||||
html! { "Loading" }
|
||||
} else {
|
||||
html! { <>{"Hello "}{name}</> }
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
// highlight-start
|
||||
let pre_made_props = yew::props! {
|
||||
Props {} // 名前属性を指定する必要はありません
|
||||
};
|
||||
// highlight-end
|
||||
html! { <Hello ..pre_made_props /> }
|
||||
}
|
||||
```
|
||||
|
||||
## 自動生成プロパティ (yew-autoprops)
|
||||
|
||||
開発プロセスを簡素化するために、`#[autoprops]` マクロ(`yew-autoprops` パッケージから)を使用して `Properties` 構造体を自動生成することもできます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use yew_autoprops::autoprops;
|
||||
|
||||
// #[autoprops] マクロは #[function_component] の前に配置する必要があります。順序が重要です。
|
||||
#[autoprops]
|
||||
#[function_component]
|
||||
fn Greetings(
|
||||
#[prop_or_default]
|
||||
is_loading: bool,
|
||||
#[prop_or(AttrValue::Static("Hello"))]
|
||||
message: &AttrValue,
|
||||
#[prop_or(AttrValue::Static("World"))]
|
||||
name: &AttrValue,
|
||||
) -> Html {
|
||||
if is_loading {
|
||||
html! { "Loading" }
|
||||
} else {
|
||||
html! { <>{message}{" "}{name}</> }
|
||||
}
|
||||
}
|
||||
|
||||
// 構造体 "GreetingsProps" は自動的に生成されます。
|
||||
//
|
||||
// `is_loading` は値としてコンポーネントに渡され、`message` と `name` は定義に先行する `&` があるため参照として渡されます。
|
||||
```
|
||||
|
||||
## 評価順序
|
||||
|
||||
属性は指定された順序で評価されます。以下の例を参照してください:
|
||||
|
||||
```rust
|
||||
#[derive(yew::Properties, PartialEq)]
|
||||
struct Props { first: usize, second: usize, last: usize }
|
||||
|
||||
fn main() {
|
||||
let mut g = 1..=3;
|
||||
let props = yew::props!(Props { first: g.next().unwrap(), second: g.next().unwrap(), last: g.next().unwrap() });
|
||||
|
||||
assert_eq!(props.first, 1);
|
||||
assert_eq!(props.second, 2);
|
||||
assert_eq!(props.last, 3);
|
||||
}
|
||||
```
|
||||
|
||||
## アンチパターン
|
||||
|
||||
ほとんどのRust型はプロパティとして渡すことができますが、避けるべきアンチパターンがいくつかあります。これらには以下が含まれますが、これに限定されません:
|
||||
|
||||
1. `String` 型を `AttrValue` の代わりに使用する。 <br />
|
||||
**なぜ悪いのか?** `String` のクローンは高コストです。プロパティ値がフックやコールバックと一緒に使用される場合、通常クローンが必要です。`AttrValue` は参照カウントされた文字列 (`Rc<str>`) または `&'static str` であり、非常に安価にクローンできます。<br />
|
||||
**注意**:`AttrValue` は内部的には [implicit-clone](https://crates.io/crates/implicit-clone) からの `IString` です。詳細はそのパッケージを参照してください。
|
||||
2. 内部可変性を使用する。 <br />
|
||||
**なぜ悪いのか?** 内部可変性(例えば `RefCell`、`Mutex` など)は _通常_ 避けるべきです。これにより再レンダリングの問題が発生する可能性があり(Yewは状態が変更されたことを認識しません)、手動で再レンダリングを強制する必要があるかもしれません。すべてのものと同様に、適切な使用場所があります。慎重に使用してください。
|
||||
3. `Vec` 型を `IArray` の代わりに使用する。 <br />
|
||||
**なぜ悪いのか?** `Vec` も `String` と同様にクローンのコストが高いです。`IArray` は参照カウントされたスライス (`Rc<T>`) または `&'static [T]` であり、非常に安価にクローンできます。<br />
|
||||
**注意**:`IArray` は [implicit-clone](https://crates.io/crates/implicit-clone) からインポートできます。詳細はそのパッケージを参照してください。
|
||||
4. 新しい発見があるかもしれません。早く知っておきたかったエッジケースに遭遇しましたか?問題を作成するか、このドキュメントに修正のPRを提供してください。
|
||||
|
||||
## yew-autoprops
|
||||
|
||||
[yew-autoprops](https://crates.io/crates/yew-autoprops) は実験的なパッケージで、関数の引数に基づいて動的にProps構造体を作成することを可能にします。プロパティ構造体が再利用されない場合、これは有用かもしれません。
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: '純粋コンポーネント'
|
||||
---
|
||||
|
||||
すべての関数コンポーネントは、プロパティオブジェクトを受け取り、`Html` オブジェクトを返す[純粋](https://ja.wikipedia.org/wiki/%E7%B4%94%E9%96%A2%E6%95%B0)関数です。純粋関数とは、同じ入力に対して常に同じ出力を返す関数のことです。
|
||||
|
||||
この例は純粋コンポーネントです。与えられたプロパティ `is_loading` に対して、常に同じ `Html` を返し、副作用はありません。
|
||||
|
||||
```rust
|
||||
use yew::{Properties, function_component, Html, html};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub is_loading: bool,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(props: &Props) -> Html {
|
||||
if props.is_loading {
|
||||
html! { "Loading" }
|
||||
} else {
|
||||
html! { "Hello world" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
内部の純粋コンポーネントがフックや他のコンポーネントメカニズムを使用しない場合、それを `Html` を返す通常の関数として記述することができ、Yew がコンポーネントのライフサイクルに関連するオーバーヘッドを回避することができます。[式構文](concepts/html/literals-and-expressions.mdx#expressions) を使用して `html!` 内でそれらをレンダリングします。
|
||||
:::
|
||||
|
||||
## 非純粋コンポーネント
|
||||
|
||||
コンポーネントがグローバル変数を使用しない場合、それが「純粋」でない可能性があるかどうか疑問に思うかもしれません。なぜなら、それは毎回レンダリングされる固定関数として呼び出されるだけだからです。
|
||||
これが次のトピック - [フック](./hooks) の出番です。
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: '状態'
|
||||
---
|
||||
|
||||
## 状態を保存するための一般的なビュー
|
||||
|
||||
この表は、どの状態保存タイプがあなたのユースケースに最適かを決定するためのガイドとして役立ちます:
|
||||
|
||||
| フック | タイプ | いつレンダリングされるか | スコープ |
|
||||
| ------------------ | -------------------------- | ------------------------------------------ | ---------------------------- |
|
||||
| [use_state] | `T` | 値が設定されたとき | コンポーネントインスタンス内 |
|
||||
| [use_state_eq] | `T: PartialEq` | 異なる値が設定されたとき | コンポーネントインスタンス内 |
|
||||
| [use_reducer] | `T: Reducible` | リデューサーが呼び出されたとき | コンポーネントインスタンス内 |
|
||||
| [use_reducer_eq] | `T: Reducible + PartialEq` | リデューサーが呼び出され、結果が異なるとき | コンポーネントインスタンス内 |
|
||||
| [use_memo] | `Deps -> T` | 依存関係が変わったとき | コンポーネントインスタンス内 |
|
||||
| [use_callback] | `Deps -> Callback<E>` | 依存関係が変わったとき | コンポーネントインスタンス内 |
|
||||
| [use_mut_ref] | `T` | - | コンポーネントインスタンス内 |
|
||||
| グローバル静的定数 | `T` | - | グローバル、どこでも使用可能 |
|
||||
|
||||
[use_state]: https://yew-rs-api.web.app/next/yew/functional/fn.use_state.html
|
||||
[use_state_eq]: https://yew-rs-api.web.app/next/yew/functional/fn.use_state_eq.html
|
||||
[use_reducer]: https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html
|
||||
[use_reducer_eq]: https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer_eq.html
|
||||
[use_memo]: https://yew-rs-api.web.app/next/yew/functional/fn.use_memo.html
|
||||
[use_callback]: https://yew-rs-api.web.app/next/yew/functional/fn.use_callback.html
|
||||
[use_mut_ref]: https://yew-rs-api.web.app/next/yew/functional/fn.use_mut_ref.html
|
|
@ -0,0 +1,122 @@
|
|||
---
|
||||
title: 'クラス'
|
||||
description: 'クラスを処理するための便利なマクロ'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## クラス
|
||||
|
||||
`Classes` 構造体は、HTML クラスを処理するために使用できます。
|
||||
|
||||
文字列をコレクションにプッシュすると、`Classes` は各クラスが一つの要素を持つことを保証します。単一の文字列が複数のクラスを含む場合でも同様です。
|
||||
|
||||
`Classes` は、`Extend`(例:`classes1.extend(classes2)`)や `push()`(例:`classes1.push(classes2)`)を使用してマージすることもできます。`Into<Classes>` を実装している任意の型を既存の `Classes` にプッシュすることができます。
|
||||
|
||||
`classes!` は、単一の `Classes` を作成するための便利なマクロです。その入力はカンマで区切られた式のリストを受け入れます。唯一の要件は、各式が `Into<Classes>` を実装していることです。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Literal" label="Literal">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!("container")}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Multiple" label="Multiple">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!("class-1", "class-2")}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="String" label="String">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
let my_classes = String::from("class-1 class-2");
|
||||
|
||||
html! {
|
||||
<div class={classes!(my_classes)}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Optional" label="Optional">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(Some("class"))} />
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Vector" label="Vector">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(vec!["class-1", "class-2"])}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Array" label="Array">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(["class-1", "class-2"])}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## クラスを受け入れるコンポーネント
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
struct Props {
|
||||
#[prop_or_default]
|
||||
class: Classes,
|
||||
fill: bool,
|
||||
children: Html,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent(props: &Props) -> Html {
|
||||
let Props {
|
||||
class,
|
||||
fill,
|
||||
children,
|
||||
} = props;
|
||||
html! {
|
||||
<div
|
||||
class={classes!(
|
||||
"my-container-class",
|
||||
fill.then(|| Some("my-fill-class")),
|
||||
class.clone(),
|
||||
)}
|
||||
>
|
||||
{ children.clone() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
|
@ -1,108 +1,124 @@
|
|||
---
|
||||
title: Components
|
||||
description: Create complex layouts with component hierarchies
|
||||
title: 'コンポーネント'
|
||||
description: 'コンポーネント階層を使用して複雑なレイアウトを作成する'
|
||||
---
|
||||
|
||||
## 基本
|
||||
|
||||
`Component`を実装しているあらゆる型は`html!`マクロの中で使えます:
|
||||
コンポーネントは `html!` マクロで使用できます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
html! {
|
||||
{ "This component has no properties!" }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
struct Props {
|
||||
user_first_name: String,
|
||||
user_last_name: String,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn MyComponentWithProps(props: &Props) -> Html {
|
||||
let Props { user_first_name, user_last_name } = props;
|
||||
html! {
|
||||
<>{"user_first_name: "}{user_first_name}{" and user_last_name: "}{user_last_name}</>
|
||||
}
|
||||
}
|
||||
|
||||
let props = Props {
|
||||
user_first_name: "Bob".to_owned(),
|
||||
user_last_name: "Smith".to_owned(),
|
||||
};
|
||||
|
||||
html!{
|
||||
<>
|
||||
// No properties
|
||||
// プロパティなし
|
||||
<MyComponent />
|
||||
|
||||
// With Properties
|
||||
<MyComponent prop1="lorem" prop2="ipsum" />
|
||||
// プロパティを使用
|
||||
<MyComponentWithProps user_first_name="Sam" user_last_name="Idle" />
|
||||
|
||||
// With the whole set of props provided at once
|
||||
<MyComponent ..props />
|
||||
// すべてのプロパティを一度に提供
|
||||
<MyComponentWithProps ..props.clone() />
|
||||
|
||||
// With Properties from a variable and specific values overridden
|
||||
<MyComponent prop2="lorem" ..props />
|
||||
// 変数のプロパティを使用し、特定の値を上書き
|
||||
<MyComponentWithProps user_last_name="Elm" ..props />
|
||||
</>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## ネスト
|
||||
|
||||
`children`フィールドが`Properties`の中にある場合はコンポーネントは子に渡されます。
|
||||
コンポーネントの `Properties` に `children` フィールドがある場合、子コンポーネント/要素を受け入れることができます。
|
||||
|
||||
```rust title="parent.rs"
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
struct Props {
|
||||
id: String,
|
||||
children: Html,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Container(props: &Props) -> Html {
|
||||
html! {
|
||||
<div id={props.id.clone()}>
|
||||
{ props.children.clone() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
html! {
|
||||
<Container>
|
||||
<Container id="container">
|
||||
<h4>{ "Hi" }</h4>
|
||||
<div>{ "Hello" }</div>
|
||||
</Container>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```rust title="container.rs"
|
||||
pub struct Container(Props);
|
||||
`html!` マクロは、各プロパティを個別に指定するのではなく、基本式を `..props` 構文で渡すことを可能にします。これは Rust の[関数的更新構文](https://doc.rust-lang.org/stable/reference/expressions/struct-expr.html#functional-update-syntax)に似ています。
|
||||
この基本式は、個別のプロパティを渡した後に現れる必要があります。
|
||||
`children` フィールドを持つ基本 props 式を渡す場合、`html!` マクロ内で渡された子要素は、props 内に既に存在する子要素を上書きします。
|
||||
|
||||
#[derive(Properties, Clone)]
|
||||
pub struct Props {
|
||||
pub children: Children,
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
struct Props {
|
||||
id: String,
|
||||
children: Html,
|
||||
}
|
||||
|
||||
impl Component for Container {
|
||||
type Properties = Props;
|
||||
|
||||
// ...
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<div id="container">
|
||||
{ self.0.children.clone() }
|
||||
</div>
|
||||
}
|
||||
#[function_component]
|
||||
fn Container(props: &Props) -> Html {
|
||||
html! {
|
||||
<div id={props.id.clone()}>
|
||||
{ props.children.clone() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
`Properties`を継承した型は`Clone`を実装していなければいけません。
|
||||
これは`#[derive(Properties, Clone)]`を使うか手で`Clone`を実装すれば良いです。
|
||||
:::
|
||||
let props = yew::props!(Props {
|
||||
id: "container-2",
|
||||
children: Html::default(),
|
||||
});
|
||||
|
||||
## Props とネストした子コンポーネント
|
||||
|
||||
ネストしたコンポーネントのプロパティは格納しているコンポーネントの型が子である場合はアクセス可能、または変更可能です。
|
||||
以下の例では`List`コンポーネントは`ListItem`コンポーネントをラップできています。
|
||||
実際の使用においてこのパターンの例については`yew-router`のソースコードを確認してみてください。
|
||||
より進んだ例としては Yew のメインのリポジトリにある`nested-list`を確認してみてください。
|
||||
|
||||
```rust title="parent.rs"
|
||||
html! {
|
||||
<List>
|
||||
<ListItem value="a" />
|
||||
<ListItem value="b" />
|
||||
<ListItem value="c" />
|
||||
</List>
|
||||
}
|
||||
<Container ..props>
|
||||
// 子要素は props.children を上書きします
|
||||
<span>{ "I am a child, as you can see" }</span>
|
||||
</Container>
|
||||
};
|
||||
```
|
||||
|
||||
```rust title="list.rs"
|
||||
pub struct List(Props);
|
||||
## 参考例
|
||||
|
||||
#[derive(Properties, Clone)]
|
||||
pub struct Props {
|
||||
pub children: ChildrenWithProps<ListItem>,
|
||||
}
|
||||
|
||||
impl Component for List {
|
||||
type Properties = Props;
|
||||
|
||||
// ...
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html!{{
|
||||
for self.0.children.iter().map(|mut item| {
|
||||
item.props.value = format!("item-{}", item.props.value);
|
||||
item
|
||||
})
|
||||
}}
|
||||
}
|
||||
}
|
||||
```
|
||||
- [関数型 Todo MVC](https://github.com/yewstack/yew/tree/master/examples/function_todomvc)
|
||||
- [関数型ルーティング](https://github.com/yewstack/yew/tree/master/examples/function_router)
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: '条件レンダリング'
|
||||
description: 'HTMLで条件付きノードをレンダリングする!'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## If ブロック
|
||||
|
||||
条件付きでマークアップをレンダリングするには、それを `if` ブロックでラップします:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="if" label="if">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
if true {
|
||||
<p>{ "True case" }</p>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="if - else" label="if - else">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
let some_condition = true;
|
||||
|
||||
html! {
|
||||
if some_condition {
|
||||
<p>{ "True case" }</p>
|
||||
} else {
|
||||
<p>{ "False case" }</p>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="if let" label="if let">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
let some_text = Some("text");
|
||||
|
||||
html! {
|
||||
if let Some(text) = some_text {
|
||||
<p>{ text }</p>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="if let else" label="if let else">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
let some_text = Some("text");
|
||||
|
||||
html! {
|
||||
if let Some(text) = some_text {
|
||||
<p>{ text }</p>
|
||||
} else {
|
||||
<p>{ "False case" }</p>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
|
@ -1,373 +1,138 @@
|
|||
---
|
||||
title: Elements
|
||||
description: Both HTML and SVG elements are supported
|
||||
title: '要素'
|
||||
description: 'HTML および SVG 要素のサポート'
|
||||
---
|
||||
|
||||
## タグ構造
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
要素のタグは`<... />`のような自己完結タグか、開始タグに対応した終了タグを持っている必要があります。
|
||||
## DOM ノード
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Open - Close-->
|
||||
Yew で DOM ノードを手動で作成または管理する理由はたくさんあります。たとえば、管理されたコンポーネントと競合する可能性のある JS ライブラリとの統合などです。
|
||||
|
||||
`web-sys` を使用すると、DOM 要素を作成して `Node` に変換できます。次に、`VRef` を使用して `Html` 値として使用できます:
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div id="my_div"></div>
|
||||
use web_sys::{Element, Node};
|
||||
use yew::prelude::*;
|
||||
use gloo::utils::document;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
// メモ化された関数、一度だけ実行されます
|
||||
let node = use_memo(
|
||||
(),
|
||||
|_| {
|
||||
// ドキュメントから div 要素を作成
|
||||
let div: Element = document().create_element("div").unwrap();
|
||||
// コンテンツ、クラスなどを追加
|
||||
div.set_inner_html("Hello, World!");
|
||||
// Element を Node に変換
|
||||
let node: Node = div.into();
|
||||
// その Node を Html 値として返す
|
||||
Html::VRef(node)
|
||||
},
|
||||
);
|
||||
|
||||
// use_memo は Rc ポインタを返すので、参照解除とクローンが必要です
|
||||
(*node).clone()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<!--Invalid-->
|
||||
## 動的なタグ名
|
||||
|
||||
高階コンポーネントを構築する際、タグ名が静的ではない状況に遭遇することがあります。例えば、`Title` コンポーネントがあり、レベル属性に応じて `h1` から `h6` までの任意の内容をレンダリングする場合です。大きなマッチ式を使用する代わりに、Yew は `@{name}` を使用してタグ名を動的に設定することを許可します。ここで、`name` は文字列を返す任意の式です。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let level = 5;
|
||||
let text = "Hello World!".to_owned();
|
||||
|
||||
html! {
|
||||
<div id="my_div"> // <- MISSING CLOSE TAG
|
||||
}
|
||||
<@{format!("h{}", level)} class="title">{ text }</@>
|
||||
};
|
||||
```
|
||||
|
||||
<!--Self-closing-->
|
||||
## 論理値属性
|
||||
|
||||
いくつかのコンテンツ属性(例えば、checked、hidden、required)は論理値属性と呼ばれます。Yew では、論理値属性はブール値に設定する必要があります:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<input id="my_input" />
|
||||
}
|
||||
<div hidden=true>
|
||||
{ "This div is hidden." }
|
||||
</div>
|
||||
};
|
||||
```
|
||||
|
||||
<!--Invalid-->
|
||||
これは次の **HTML** と機能的に同等です:
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<input id="my_input"> // <- MISSING SELF-CLOSE
|
||||
}
|
||||
```html
|
||||
<div hidden>This div is hidden.</div>
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
:::note
|
||||
便利さのために、*普通は*終了タグを必要とする要素は自己完結タグとすることが**できます**。
|
||||
例えば`html! { <div class="placeholder" /> }`と書くのは有効です。
|
||||
:::
|
||||
|
||||
## 子
|
||||
|
||||
複雑にネストした HTML や SVG のレイアウトを書くのには以下のようにするのが楽です:
|
||||
\*\*
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--HTML-->
|
||||
論理値属性を false に設定することは、その属性を使用しないことと同等です。論理式の値を使用することもできます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let no = 1 + 1 != 2;
|
||||
|
||||
html! {
|
||||
<div hidden={no}>
|
||||
{ "This div is NOT hidden." }
|
||||
</div>
|
||||
};
|
||||
```
|
||||
|
||||
これは次の **HTML** と機能的に同等です:
|
||||
|
||||
```html
|
||||
<div>This div is NOT hidden.</div>
|
||||
```
|
||||
|
||||
## 文字列に似た属性
|
||||
|
||||
いくつかの論理値属性に加えて、多くの文字列に似た HTML 属性を扱うことがあります。Yew には、文字列に似た値をコンポーネントに渡すためのいくつかのオプションがあります。
|
||||
|
||||
```rust
|
||||
use yew::{html, virtual_dom::AttrValue};
|
||||
|
||||
let str_placeholder = "I'm a str!";
|
||||
let string_placeholder = String::from("I'm a String!");
|
||||
let attrvalue_placeholder = AttrValue::from("I'm an AttrValue!");
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<div data-key="abc"></div>
|
||||
<div class="parent">
|
||||
<span class="child" value="anything"></span>
|
||||
<label for="first-name">{ "First Name" }</label>
|
||||
<input type="text" id="first-name" value="placeholder" />
|
||||
<input type="checkbox" checked=true />
|
||||
<textarea value="write a story" />
|
||||
<select name="status">
|
||||
<option selected=true disabled=false value="">{ "Selected" }</option>
|
||||
<option selected=false disabled=true value="">{ "Unselected" }</option>
|
||||
</select>
|
||||
</div>
|
||||
<input placeholder={str_placeholder} />
|
||||
<input placeholder={string_placeholder} />
|
||||
<input placeholder={attrvalue_placeholder} />
|
||||
</div>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<!--SVG-->
|
||||
それらはすべて有効ですが、特にクローンを作成する必要がある場合や、別のコンポーネントに属性として渡す必要がある場合は、Yew のカスタム `AttrValue` を使用することをお勧めします。
|
||||
|
||||
## HTML 要素のオプション属性
|
||||
|
||||
ほとんどの HTML 属性はオプションの値(Some(x) または None)を使用できます。これにより、属性がオプションとしてマークされている場合にその属性を省略できます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let maybe_id = Some("foobar");
|
||||
|
||||
html! {
|
||||
<svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
|
||||
<path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g filter="url(#filter0_d)">
|
||||
<circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
|
||||
<circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
|
||||
</g>
|
||||
<circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
}
|
||||
<div id={maybe_id}></div>
|
||||
};
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
属性が `None` に設定されている場合、その属性は DOM に設定されません。
|
||||
|
||||
## クラス
|
||||
## 関連例
|
||||
|
||||
要素へのクラスを特定する便利なやり方はたくさんあります:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Literal-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div class="container"></div>
|
||||
}
|
||||
```
|
||||
|
||||
<!--Multiple-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div class="container center-align"></div>
|
||||
}
|
||||
```
|
||||
|
||||
<!--Interpolated-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div class={format!("{}-container", size)}></div>
|
||||
}
|
||||
```
|
||||
|
||||
<!--Expression-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div class={self.classes()}></div>
|
||||
}
|
||||
```
|
||||
|
||||
<!--Tuple-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div class={("class-1", "class-2")}></div>
|
||||
}
|
||||
```
|
||||
|
||||
<!--Vector-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div class={vec!["class-1", "class-2"]}></div>
|
||||
}
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
## リスナー
|
||||
|
||||
リスナー属性はクロージャのラッパーである`Callback`に渡される必要があります。
|
||||
コールバックをどのように作るかはアプリをリスナーイベントにどう反応させたいかによります。
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Component handler-->
|
||||
|
||||
```rust
|
||||
struct MyComponent {
|
||||
link: ComponentLink<Self>,
|
||||
}
|
||||
|
||||
enum Msg {
|
||||
Click,
|
||||
}
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
MyComponent { link }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::Click => {
|
||||
// Handle Click
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
// Create a callback from a component link to handle it in a component
|
||||
let click_callback = self.link.callback(|_: ClickEvent| Msg::Click);
|
||||
html! {
|
||||
<button onclick={click_callback}>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--Agent Handler-->
|
||||
|
||||
```rust
|
||||
struct MyComponent {
|
||||
worker: Dispatcher<MyWorker>,
|
||||
}
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||
MyComponent {
|
||||
worker: MyWorker::dispatcher()
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||
false
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
// Create a callback from a worker to handle it in another context
|
||||
let click_callback = self.worker.callback(|_: ClickEvent| WorkerMsg::Process);
|
||||
html! {
|
||||
<button onclick={click_callback}>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--Other Cases-->
|
||||
|
||||
```rust
|
||||
struct MyComponent;
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||
MyComponent
|
||||
}
|
||||
|
||||
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||
false
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
// Create an ephemeral callback
|
||||
let click_callback = Callback::from(|| {
|
||||
ConsoleService::log("clicked!");
|
||||
});
|
||||
|
||||
html! {
|
||||
<button onclick={click_callback}>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
## イベントの型
|
||||
|
||||
:::note
|
||||
以下のテーブルにある全てのイベントの型は`yew::events`で再エクスポートされています。
|
||||
All the event types mentioned in the following table are re-exported under `yew::events`. Using the types from
|
||||
`yew::events` makes it easier to ensure version compatibility than if you were to manually include `web-sys`
|
||||
or `stdweb` as dependencies in your crate because you won't end up using a version which conflicts with
|
||||
the version Yew specifies.
|
||||
:::
|
||||
|
||||
| イベント名 | `web_sys` イベント型 |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `onabort` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onauxclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onblur` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `oncancel` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncanplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncanplaythrough` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onchange` | [ChangeData](https://docs.rs/yew/latest/yew/events/enum.ChangeData.html) |
|
||||
| `onclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onclose` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncontextmenu` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `oncuechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ondblclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `ondrag` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragend` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragenter` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragexit` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragleave` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.htmk) |
|
||||
| `ondragover` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragstart` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondrop` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondurationchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onemptied` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onended` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onerror` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onfocus` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onformdata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oninput` | [InputData](https://docs.rs/yew/latest/yew/events/struct.InputData.html) |
|
||||
| `oninvalid` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onkeydown` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onkeypress` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onkeyup` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onload` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onloadeddata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onloadedmetadata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onloadstart` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
|
||||
| `onmousedown` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseenter` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseleave` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmousemove` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseout` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseover` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseup` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onpause` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onplaying` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onprogress` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
|
||||
| `onratechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onreset` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onresize` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onscroll` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onsecuritypolicyviolation` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onseeked` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onseeking` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onselect` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onslotchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onstalled` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onsubmit` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onsuspend` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ontimeupdate` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ontoggle` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onvolumechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onwaiting` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onwheel` | [WheelEvent](https://docs.rs/web-sys/latest/web_sys/struct.WheelEvent.html) |
|
||||
| `oncopy` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncut` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onpaste` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onanimationcancel` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `onanimationend` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `onanimationiteration` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `onanimationstart` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `ongotpointercapture` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onloadend` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
|
||||
| `onlostpointercapture` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointercancel` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerdown` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerenter` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerleave` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerlockchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onpointerlockerror` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onpointermove` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerout` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerover` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerup` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onselectionchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onselectstart` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onshow` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ontouchcancel` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontouchend` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontouchmove` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontouchstart` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontransitioncancel` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
| `ontransitionend` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
| `ontransitionrun` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
| `ontransitionstart` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
- [インライン HTML](https://github.com/yewstack/yew/tree/master/examples/inner_html)
|
||||
|
|
|
@ -0,0 +1,542 @@
|
|||
---
|
||||
title: 'イベント'
|
||||
---
|
||||
|
||||
## 紹介
|
||||
|
||||
Yew は [`web-sys`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/) クレートと統合されており、このクレートのイベントを使用します。以下の[表](#event-types)には、`html!` マクロで受け入れられるすべての `web-sys` イベントが一覧表示されています。
|
||||
|
||||
下記の表に記載されていないイベントについても、[`Callback`](../function-components/callbacks.mdx) を追加してリッスンすることができます。詳細は[手動イベントリスナー](#manual-event-listener)を参照してください。
|
||||
|
||||
## イベントタイプ
|
||||
|
||||
:::tip
|
||||
すべてのイベントタイプは `yew::events` に再エクスポートされています。
|
||||
`yew::events` のタイプを使用することで、`web-sys` を手動でクレートに依存関係として追加するよりも、バージョン互換性を確保しやすくなります。
|
||||
Yew が指定するバージョンと競合するバージョンを使用することがなくなります。
|
||||
:::
|
||||
|
||||
イベントリスナーの名前は、`html` マクロでイベント `Callback` を追加する際に期待される名前です:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<button onclick={Callback::from(|_| ())}>
|
||||
// ^^^^^^^ event listener name
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
};
|
||||
```
|
||||
|
||||
イベント名はリスナー名から "on" プレフィックスを削除したもので、したがって `onclick` イベントリスナーは `click` イベントをリッスンします。ページの最後にある[完全なイベントリスト](#available-events)とそのタイプを参照してください。
|
||||
|
||||
## イベントキャプチャ {#event-bubbling}
|
||||
|
||||
Yew がディスパッチするイベントは仮想 DOM 階層に従い、リスナーに向かってバブルアップします。現在、リスナーのバブルフェーズのみがサポートされています。仮想 DOM 階層は通常(ただし常にではありません)実際の DOM 階層と同じです。[ポータル](../../advanced-topics/portals.mdx)やその他の高度な技術を扱う際には、この違いが重要です。よく設計されたコンポーネントでは、直感的にイベントは子コンポーネントから親コンポーネントにバブルアップするはずです。これにより、`html!` で記述した階層がイベントハンドラによって観察される階層となります。
|
||||
|
||||
イベントのバブルアップを避けたい場合は、アプリケーションを起動する前に以下のコードを呼び出すことができます
|
||||
|
||||
```rust
|
||||
yew::set_event_bubbling(false);
|
||||
```
|
||||
|
||||
アプリケーションを起動する*前に*。これによりイベント処理が高速化されますが、期待されるイベントを受信しないために一部のコンポーネントが動作しなくなる可能性があります。慎重に使用してください!
|
||||
|
||||
## イベントデリゲート
|
||||
|
||||
驚くかもしれませんが、イベントリスナーはレンダリングされた要素に直接登録されるわけではありません。代わりに、イベントは Yew アプリケーションのサブツリーのルートノードから委譲されます。ただし、イベントはそのネイティブ形式で渡され、合成形式は作成されません。これにより、HTML リスナーで予期されるイベントと Yew で発生するイベントとの間に不一致が生じる可能性があります。
|
||||
|
||||
- [`Event::current_target`] はリスナーが追加された要素ではなく、Yew サブツリーのルートノードを指します。基になる `HtmlElement` にアクセスしたい場合は、[`NodeRef`](../function-components/node-refs.mdx) を使用してください。
|
||||
- [`Event::event_phase`] は常に [`Event::CAPTURING_PHASE`] です。内部的には、イベントはバブリングフェーズにあるかのように振る舞い、イベント伝播が再生され、イベントは[上位にバブルアップ](#event-bubbling)します。つまり、仮想 DOM 内の上位のイベントリスナーが下位のイベントリスナーの後にトリガーされます。現在、Yew はキャプチャリスナーをサポートしていません。
|
||||
|
||||
これも意味するところは、Yew によって登録されたイベントは通常、他のイベントリスナーよりも先にトリガーされるということです。
|
||||
|
||||
[`event::current_target`]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.current_target
|
||||
[`event::event_phase`]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.event_phase
|
||||
[`event::capturing_phase`]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#associatedconstant.CAPTURING_PHASE
|
||||
|
||||
## 型付きイベントターゲット
|
||||
|
||||
:::caution
|
||||
このセクションでは、**target ([`Event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target))** は常にイベントが発生した要素を指します。
|
||||
|
||||
これは**必ずしも** `Callback` が配置された要素を指すわけではありません。
|
||||
:::
|
||||
|
||||
イベント `Callback` の中で、イベントのターゲットを取得したい場合があります。例えば、`change` イベントは何かが変更されたことを通知するだけで、具体的な情報を提供しません。
|
||||
|
||||
Yew では、正しい型でターゲット要素を取得する方法がいくつかあり、ここで順を追って説明します。イベント上の [`web_sys::Event::target`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target) を呼び出すと、オプションの [`web_sys::EventTarget`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.EventTarget.html) 型が返されますが、入力要素の値を知りたい場合にはあまり役に立たないかもしれません。
|
||||
|
||||
以下のすべての方法で、同じ問題を解決します。これにより、方法の違いが明確になり、問題に対処することができます。
|
||||
|
||||
**問題:**
|
||||
|
||||
`<input>` 要素に `onchange` `Callback` があり、呼び出されるたびにコンポーネントに[更新](components#update) `Msg` を送信したいとします。
|
||||
|
||||
`Msg` 列挙型は次のようになります:
|
||||
|
||||
```rust
|
||||
pub enum Msg {
|
||||
InputValue(String),
|
||||
}
|
||||
```
|
||||
|
||||
### `JsCast` の使用
|
||||
|
||||
[`wasm-bindgen`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) クレートには便利なトレイトがあります:[`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)。これにより、型が `JsCast` を実装している限り、型間の直接キャストが可能になります。慎重にキャストすることもできますが、これはランタイムチェックと `Option` や `Result` のロジックを処理することを伴います。また、強制的にキャストすることもできます。
|
||||
|
||||
コードを見てみましょう:
|
||||
|
||||
```toml title="Cargo.toml"
|
||||
[dependencies]
|
||||
# JsCast を呼び出すために wasm-bindgen が必要です
|
||||
wasm-bindgen = "0.2"
|
||||
```
|
||||
|
||||
```rust
|
||||
//highlight-next-line
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{EventTarget, HtmlInputElement};
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
let input_value_handle = use_state(String::default);
|
||||
let input_value = (*input_value_handle).clone();
|
||||
|
||||
let on_cautious_change = {
|
||||
let input_value_handle = input_value_handle.clone();
|
||||
|
||||
Callback::from(move |e: Event| {
|
||||
// イベントが作成されたとき、ターゲットは未定義であり、ディスパッチされるときにのみターゲットが追加されます。
|
||||
let target: Option<EventTarget> = e.target();
|
||||
// イベントはバブルアップする可能性があるため、
|
||||
// このリスナーは HtmlInputElement 型ではない子要素のイベントをキャッチする可能性があります。
|
||||
//highlight-next-line
|
||||
let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
|
||||
|
||||
if let Some(input) = input {
|
||||
input_value_handle.set(input.value());
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let on_dangerous_change = Callback::from(move |e: Event| {
|
||||
let target: EventTarget = e
|
||||
.target()
|
||||
.expect("Event should have a target when dispatched");
|
||||
// target が HtmlInputElement であることを理解している必要があります。
|
||||
// そうでない場合、value を呼び出すと未定義の動作(UB)になります。
|
||||
// ここでは、これが入力要素であることを確信しているため、チェックせずに適切な型に変換できます。
|
||||
//highlight-next-line
|
||||
input_value_handle.set(target.unchecked_into::<HtmlInputElement>().value());
|
||||
});
|
||||
|
||||
html! {
|
||||
<>
|
||||
<label for="cautious-input">
|
||||
{ "My cautious input:" }
|
||||
<input onchange={on_cautious_change}
|
||||
id="cautious-input"
|
||||
type="text"
|
||||
value={input_value.clone()}
|
||||
/>
|
||||
</label>
|
||||
<label for="dangerous-input">
|
||||
{ "My dangerous input:" }
|
||||
<input onchange={on_dangerous_change}
|
||||
id="dangerous-input"
|
||||
type="text"
|
||||
value={input_value}
|
||||
/>
|
||||
</label>
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`JsCast` が提供するメソッドは [`dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) と [`unchecked_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.unchecked_into) です。これらのメソッドを使用すると、`EventTarget` から [`HtmlInputElement`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.HtmlInputElement.html) への変換が可能になります。`dyn_into` メソッドは慎重で、実行時に型が実際に `HtmlInputElement` であるかどうかをチェックし、そうでない場合は `Err(JsValue)` を返します。[`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html) は汎用型で、元のオブジェクトを返し、別の型への変換を再試行することができます。
|
||||
|
||||
ここで、危険なバージョンを使用するタイミングについて考えるかもしれません。上記のケースでは、子要素のない要素に `Callback` を設定しているため、ターゲットは同じ要素である必要があるため、安全です<sup>1</sup>。
|
||||
|
||||
_<sup>1</sup> JS の領域に関わる限り、安全です。_
|
||||
|
||||
### `TargetCast` の使用
|
||||
|
||||
**[JsCast の使用](#using-jscast) を先に読むことを強くお勧めします!**
|
||||
|
||||
:::note
|
||||
`TargetCast` は新しいユーザーが `JsCast` の動作を理解するために設計されていますが、範囲はイベントとそのターゲットに限定されています。
|
||||
|
||||
`TargetCast` または `JsCast` を選択するのは純粋に個人の好みの問題であり、実際には `TargetCast` の実装と `JsCast` の機能は非常に似ています。
|
||||
:::
|
||||
|
||||
`TargetCast` トレイトは `JsCast` の上に構築されており、イベントから型付きのイベントターゲットを取得するために特化されています。
|
||||
|
||||
`TargetCast` は Yew の一部であるため、依存関係を追加せずにイベント上でトレイトメソッドを使用できますが、その動作は `JsCast` と非常に似ています。
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
let input_value_handle = use_state(String::default);
|
||||
let input_value = (*input_value_handle).clone();
|
||||
|
||||
let on_cautious_change = {
|
||||
let input_value_handle = input_value_handle.clone();
|
||||
|
||||
Callback::from(move |e: Event| {
|
||||
let input = e.target_dyn_into::<HtmlInputElement>();
|
||||
|
||||
if let Some(input) = input {
|
||||
input_value_handle.set(input.value());
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let on_dangerous_change = Callback::from(move |e: Event| {
|
||||
// target が HtmlInputElement であることを理解している必要があります。
|
||||
// そうでない場合、value を呼び出すと未定義の動作(UB)になります。
|
||||
//highlight-next-line
|
||||
input_value_handle.set(e.target_unchecked_into::<HtmlInputElement>().value());
|
||||
});
|
||||
|
||||
html! {
|
||||
<>
|
||||
<label for="cautious-input">
|
||||
{ "My cautious input:" }
|
||||
<input onchange={on_cautious_change}
|
||||
id="cautious-input"
|
||||
type="text"
|
||||
value={input_value.clone()}
|
||||
/>
|
||||
</label>
|
||||
<label for="dangerous-input">
|
||||
{ "My dangerous input:" }
|
||||
<input onchange={on_dangerous_change}
|
||||
id="dangerous-input"
|
||||
type="text"
|
||||
value={input_value}
|
||||
/>
|
||||
</label>
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
もし `JsCast` についてすでに知っているか、このトレイトに精通している場合、`TargetCast::target_dyn_into` が `JsCast::dyn_into` に似ていることに気付くでしょうが、イベントのターゲットに特化しています。`TargetCast::target_unchecked_into` は `JsCast::unchecked_into` に似ているため、上記の `JsCast` に関するすべての警告が `TargetCast` にも適用されます。
|
||||
|
||||
### `NodeRef` の使用
|
||||
|
||||
[`NodeRef`](../function-components/node-refs.mdx) は、与えられたイベントを `Callback` に渡す代わりに使用できます。
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
//highlight-next-line
|
||||
let input_node_ref = use_node_ref();
|
||||
|
||||
let input_value_handle = use_state(String::default);
|
||||
let input_value = (*input_value_handle).clone();
|
||||
|
||||
let onchange = {
|
||||
let input_node_ref = input_node_ref.clone();
|
||||
|
||||
Callback::from(move |_| {
|
||||
//highlight-next-line
|
||||
let input = input_node_ref.cast::<HtmlInputElement>();
|
||||
|
||||
if let Some(input) = input {
|
||||
input_value_handle.set(input.value());
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
<label for="my-input">
|
||||
{ "My input:" }
|
||||
//highlight-next-line
|
||||
<input ref={input_node_ref}
|
||||
{onchange}
|
||||
id="my-input"
|
||||
type="text"
|
||||
value={input_value}
|
||||
/>
|
||||
</label>
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`NodeRef` を使用すると、イベントを無視して `NodeRef::cast` メソッドを使用して `Option<HtmlInputElement>` を取得できます。これはオプションであり、`NodeRef` を設定する前に `cast` を呼び出すか、型が一致しない場合に `None` を返します。
|
||||
|
||||
`NodeRef` を使用することで、常に `input_node_ref` にアクセスできるため、状態に `String` を送信する必要がないことがわかるかもしれません。したがって、次のようにすることができます:
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
let input_node_ref = use_node_ref();
|
||||
|
||||
//highlight-start
|
||||
let onchange = {
|
||||
let input_node_ref = input_node_ref.clone();
|
||||
|
||||
Callback::from(move |_| {
|
||||
if let Some(input) = input_node_ref.cast::<HtmlInputElement>() {
|
||||
let value = input.value();
|
||||
// value に対して何かを行う
|
||||
}
|
||||
})
|
||||
};
|
||||
//highlight-end
|
||||
|
||||
html! {
|
||||
<>
|
||||
<label for="my-input">
|
||||
{ "My input:" }
|
||||
<input ref={input_node_ref}
|
||||
{onchange}
|
||||
id="my-input"
|
||||
type="text"
|
||||
/>
|
||||
</label>
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
どの方法を選択するかは、コンポーネントと個人の好みによります。*推奨される*方法はありません。
|
||||
|
||||
## 手動イベントリスナー
|
||||
|
||||
Yew の `html` マクロがサポートしていないイベントをリッスンしたい場合があります。サポートされているイベントのリストは[こちら](#event-types)を参照してください。
|
||||
|
||||
手動で要素にイベントリスナーを追加するには、[`NodeRef`](../function-components/node-refs.mdx) を使用して、`use_effect_with` 内で [`web-sys`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/index.html) と [wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) API を使用してリスナーを追加します。
|
||||
|
||||
以下の例では、架空の `custard` イベントにリスナーを追加する方法を示します。Yew がサポートしていないすべてのイベントやカスタムイベントは、[`web_sys::Event`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html) として表現できます。カスタム/サポートされていないイベントの特定のメソッドやフィールドにアクセスする必要がある場合は、[`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html) のメソッドを使用して必要な型に変換できます。
|
||||
|
||||
### `Closure` を使用する(冗長バージョン)
|
||||
|
||||
直接 `web-sys` と `wasm-bindgen` のインターフェースを使用するのは少し面倒かもしれません……なので、心の準備をしてください([`gloo` のおかげで、より簡潔な方法があります](#using-gloo-concise))。
|
||||
|
||||
```rust
|
||||
use wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use web_sys::HtmlElement;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
let div_node_ref = use_node_ref();
|
||||
|
||||
use_effect_with(
|
||||
div_node_ref.clone(),
|
||||
{
|
||||
let div_node_ref = div_node_ref.clone();
|
||||
|
||||
move |_| {
|
||||
let mut custard_listener = None;
|
||||
|
||||
if let Some(element) = div_node_ref.cast::<HtmlElement>() {
|
||||
// 通常作成する Callback を作成
|
||||
let oncustard = Callback::from(move |_: Event| {
|
||||
// カスタードに対して何かを行う..
|
||||
});
|
||||
|
||||
// Box<dyn Fn> から Closure を作成 - これは 'static である必要があります
|
||||
let listener =
|
||||
Closure::<dyn Fn(Event)>::wrap(
|
||||
Box::new(move |e: Event| oncustard.emit(e))
|
||||
);
|
||||
|
||||
element
|
||||
.add_event_listener_with_callback(
|
||||
"custard",
|
||||
listener.as_ref().unchecked_ref()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
custard_listener = Some(listener);
|
||||
}
|
||||
|
||||
move || drop(custard_listener)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
html! {
|
||||
<div ref={div_node_ref} id="my-div"></div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Closure` の詳細については、[wasm-bindgen ガイド](https://rustwasm.github.io/wasm-bindgen/examples/closures.html) を参照してください。
|
||||
|
||||
### `gloo` を使用する(簡潔なバージョン)
|
||||
|
||||
より便利な方法は、`gloo`、具体的には [`gloo_events`](https://docs.rs/gloo-events/0.1.1/gloo_events/index.html) を使用することです。
|
||||
これは `web-sys`、`wasm-bindgen` の高レベル抽象実装です。
|
||||
|
||||
`gloo_events` は、イベントリスナーを作成および保存するために使用できる `EventListener` 型を提供します。
|
||||
|
||||
```toml title="Cargo.toml"
|
||||
[dependencies]
|
||||
gloo-events = "0.1"
|
||||
```
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlElement;
|
||||
use yew::prelude::*;
|
||||
|
||||
use gloo::events::EventListener;
|
||||
|
||||
#[function_component]
|
||||
fn MyComponent() -> Html {
|
||||
let div_node_ref = use_node_ref();
|
||||
|
||||
use_effect_with(
|
||||
div_node_ref.clone(),
|
||||
{
|
||||
let div_node_ref = div_node_ref.clone();
|
||||
|
||||
move |_| {
|
||||
let mut custard_listener = None;
|
||||
|
||||
if let Some(element) = div_node_ref.cast::<HtmlElement>() {
|
||||
// 通常作成する Callback を作成
|
||||
let oncustard = Callback::from(move |_: Event| {
|
||||
// カスタードに対して何かを行う..
|
||||
});
|
||||
|
||||
// Box<dyn Fn> から Closure を作成 - これは 'static である必要があります
|
||||
let listener = EventListener::new(
|
||||
&element,
|
||||
"custard",
|
||||
move |e| oncustard.emit(e.clone())
|
||||
);
|
||||
|
||||
custard_listener = Some(listener);
|
||||
}
|
||||
|
||||
move || drop(custard_listener)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
html! {
|
||||
<div ref={div_node_ref} id="my-div"></div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`EventListener` の詳細については、[gloo_events docs.rs](https://docs.rs/gloo-events/0.1.1/gloo_events/struct.EventListener.html) を参照してください。
|
||||
|
||||
## 利用可能なイベントの完全なリスト {#available-events}
|
||||
|
||||
| リスナー名 | `web_sys` イベントの種類 |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `onabort` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onauxclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onblur` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `oncancel` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncanplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncanplaythrough` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onclose` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncontextmenu` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `oncuechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ondblclick` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `ondrag` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragend` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragenter` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragexit` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragleave` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragover` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondragstart` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondrop` | [DragEvent](https://docs.rs/web-sys/latest/web_sys/struct.DragEvent.html) |
|
||||
| `ondurationchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onemptied` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onended` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onerror` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onfocus` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onfocusin` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onfocusout` | [FocusEvent](https://docs.rs/web-sys/latest/web_sys/struct.FocusEvent.html) |
|
||||
| `onformdata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oninput` | [InputEvent](https://docs.rs/web-sys/latest/web_sys/struct.InputEvent.html) |
|
||||
| `oninvalid` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onkeydown` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onkeypress` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onkeyup` | [KeyboardEvent](https://docs.rs/web-sys/latest/web_sys/struct.KeyboardEvent.html) |
|
||||
| `onload` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onloadeddata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onloadedmetadata` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onloadstart` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
|
||||
| `onmousedown` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseenter` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseleave` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmousemove` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseout` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseover` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onmouseup` | [MouseEvent](https://docs.rs/web-sys/latest/web_sys/struct.MouseEvent.html) |
|
||||
| `onpause` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onplay` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onplaying` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onprogress` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
|
||||
| `onratechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onreset` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onresize` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onscroll` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onsecuritypolicyviolation` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onseeked` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onseeking` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onselect` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onslotchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onstalled` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onsubmit` | [SubmitEvent](https://docs.rs/web-sys/latest/web_sys/struct.SubmitEvent.html) |
|
||||
| `onsuspend` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ontimeupdate` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ontoggle` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onvolumechange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onwaiting` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onwheel` | [WheelEvent](https://docs.rs/web-sys/latest/web_sys/struct.WheelEvent.html) |
|
||||
| `oncopy` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `oncut` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onpaste` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onanimationcancel` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `onanimationend` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `onanimationiteration` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `onanimationstart` | [AnimationEvent](https://docs.rs/web-sys/latest/web_sys/struct.AnimationEvent.html) |
|
||||
| `ongotpointercapture` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onloadend` | [ProgressEvent](https://docs.rs/web-sys/latest/web_sys/struct.ProgressEvent.html) |
|
||||
| `onlostpointercapture` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointercancel` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerdown` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerenter` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerleave` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerlockchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onpointerlockerror` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onpointermove` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerout` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerover` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onpointerup` | [PointerEvent](https://docs.rs/web-sys/latest/web_sys/struct.PointerEvent.html) |
|
||||
| `onselectionchange` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onselectstart` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `onshow` | [Event](https://docs.rs/web-sys/latest/web_sys/struct.Event.html) |
|
||||
| `ontouchcancel` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontouchend` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontouchmove` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontouchstart` | [TouchEvent](https://docs.rs/web-sys/latest/web_sys/struct.TouchEvent.html) |
|
||||
| `ontransitioncancel` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
| `ontransitionend` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
| `ontransitionrun` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
||||
| `ontransitionstart` | [TransitionEvent](https://docs.rs/web-sys/latest/web_sys/struct.TransitionEvent.html) |
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
title: '空のタグ (Fragments)'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
`html!` マクロは常にルートノードを必要とします。この制限を回避するために、「空のタグ」(または fragments)を使用できます。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Valid" label="Valid">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<>
|
||||
<div></div>
|
||||
<p></p>
|
||||
</>
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="Invalid" label="Invalid">
|
||||
|
||||
```rust, compile_fail
|
||||
use yew::prelude::*;
|
||||
|
||||
// エラー: ルート HTML 要素は1つだけ許可されます
|
||||
|
||||
html! {
|
||||
<div></div>
|
||||
<p></p>
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
|
@ -1,22 +1,208 @@
|
|||
---
|
||||
title: Introduction
|
||||
description: The procedural macro for generating HTML and SVG
|
||||
title: 'HTML'
|
||||
sidebar_label: Introduction
|
||||
description: 'HTML および SVG を生成するためのプロシージャルマクロ'
|
||||
slug: /concepts/html
|
||||
---
|
||||
|
||||
`html!`マクロによって HTML と SVG のコードを宣言的に書くことができます。
|
||||
JSX \(HTML のようなコードを JavaScript 内部に書くことができる JavaScript の拡張\) に似ています。
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
**重要な注意**
|
||||
`html!` マクロを使用すると、宣言的に HTML および SVG コードを記述できます。これは、JavaScript で HTML に似たコードを記述できる拡張機能である JSX に似ています。
|
||||
|
||||
1. `html!`マクロはルートの HTML ノードのみ受け付けます \([フラグメントかイテレータを使う](./lists.mdx)ことでやり取りできます\)
|
||||
2. 空の`html! {}`の呼び出しは可能ですが何もレンダリングしません
|
||||
3. リテラルはクオーテーションがつけられ、ブレースで囲う必要があります: `html! { "Hello, World" }`
|
||||
**重要な注意点**
|
||||
|
||||
1. `html! {}` マクロは 1 つのルート HTML ノードしか受け入れません(これを回避するには、[fragments](./fragments.mdx) または [iterators](./../html/lists.mdx) を使用できます)
|
||||
2. 空の `html! {}` 呼び出しは有効で、何もレンダリングしません
|
||||
3. リテラルは常に引用符で囲み、中括弧で囲む必要があります:`html! { <p>{ "Hello, World" }</p> }`
|
||||
4. `html!` マクロはすべてのタグ名を小文字に変換します。大文字の文字(特定の SVG 要素に必要な文字)を使用するには、[動的タグ名](concepts/html/elements.mdx#dynamic-tag-names) を使用してください:`html! { <@{"myTag"}></@> }`
|
||||
|
||||
:::note
|
||||
`html!`マクロはコンパイラのデフォルトの再帰の上限に簡単に達してしまいます。
|
||||
もしコンパイラエラーに遭遇した場合はその値を押し出すといいかもしれません。
|
||||
クレートのルート\(つまり、`lib.rs`か`main.rs`\)で`#![recursion_limit="1024"]`のような属性を使えば解決します。
|
||||
|
||||
詳しくは[公式ドキュメント](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute)と[Stack Overflow の質問](https://stackoverflow.com/questions/27454761/what-is-a-crate-attribute-and-where-do-i-add-it)を見てみてください。
|
||||
`html!` マクロはコンパイラのデフォルトの再帰制限に達する可能性があります。コンパイル エラーが発生した場合は、クレートのルートに `#![recursion_limit="1024"]` のような属性を追加して問題を解決してください。
|
||||
:::
|
||||
|
||||
## タグ (Tags) 構造
|
||||
|
||||
タグ (Tags) は HTML タグに基づいています。コンポーネント、要素、およびリストはすべてこのタグ構文に基づいています。
|
||||
|
||||
タグは自己閉鎖 `<... />` であるか、開始タグごとに対応する終了タグが必要です。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Open - Close" label="Open - Close" default>
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<div id="my_div"></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Invalid" label="Invalid">
|
||||
|
||||
```rust ,compile_fail
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<div id="my_div"> // <- 閉じタグがありません
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Self-closing" label="Self-closing">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<input id="my_input" />
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Invalid" label="Invalid">
|
||||
|
||||
```rust ,compile_fail
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<input id="my_input"> // <- 閉じタグがありません
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::tip
|
||||
便利のために、通常は閉じタグが必要な要素も**自己閉じ**が許可されています。例えば、`html! { <div class="placeholder" /> }` と記述することができます。
|
||||
:::
|
||||
|
||||
複雑なネストされた HTML および SVG レイアウトを作成するのは依然として簡単です:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="HTML" label="HTML">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<div data-key="abc"></div>
|
||||
<div class="parent">
|
||||
<span class="child" value="anything"></span>
|
||||
<label for="first-name">{ "First Name" }</label>
|
||||
<input type="text" id="first-name" value="placeholder" />
|
||||
<input type="checkbox" checked=true />
|
||||
<textarea value="write a story" />
|
||||
<select name="status">
|
||||
<option selected=true disabled=false value="">{ "Selected" }</option>
|
||||
<option selected=false disabled=true value="">{ "Unselected" }</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="SVG" label="SVG">
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
<svg width="149" height="147" viewBox="0 0 149 147" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M60.5776 13.8268L51.8673 42.6431L77.7475 37.331L60.5776 13.8268Z" fill="#DEB819"/>
|
||||
<path d="M108.361 94.9937L138.708 90.686L115.342 69.8642" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g filter="url(#filter0_d)">
|
||||
<circle cx="75.3326" cy="73.4918" r="55" fill="#FDD630"/>
|
||||
<circle cx="75.3326" cy="73.4918" r="52.5" stroke="black" stroke-width="5"/>
|
||||
</g>
|
||||
<circle cx="71" cy="99" r="5" fill="white" fill-opacity="0.75" stroke="black" stroke-width="3"/>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="16.3326" y="18.4918" width="118" height="118" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<@{"feGaussianBlur"} stdDeviation="2"/>
|
||||
<@{"feColorMatrix"} in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Lints
|
||||
|
||||
もし、Rust コンパイラの開発者バージョンを使用して Yew をコンパイルする場合、マクロは一般的な落とし穴について警告します。もちろん、安定版コンパイラを使用してリリースビルドを行う必要があるかもしれません(例えば、組織のポリシーでそうする必要がある場合など)。しかし、安定版ツールチェーンを使用している場合でも、`cargo +nightly check` を実行すると、HTML コードを改善する方法がいくつか示されるかもしれません。
|
||||
|
||||
現在、これらのリントは主にアクセシビリティに関連しています。リントに関するアイデアがあれば、[この問題](https://github.com/yewstack/yew/issues/1334)に自由にコメントしてください。
|
||||
|
||||
## 属性とプロパティの指定
|
||||
|
||||
属性は通常の HTML と同じ方法で要素に設定されます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let value = "something";
|
||||
html! { <div attribute={value} /> };
|
||||
```
|
||||
|
||||
属性は要素名の前に `~` を使用して指定されます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! { <my-element ~property="abc" /> };
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
値がリテラルの場合、値を囲む中括弧は省略できます。
|
||||
|
||||
:::
|
||||
|
||||
:::note リテラルとは
|
||||
|
||||
リテラルは、Rust のすべての有効な[リテラル式](https://doc.rust-lang.org/reference/expressions/literal-expr.html)です。注意してください、[負の数は**リテラルではありません**](https://users.rust-lang.org/t/why-are-negative-value-literals-expressions/43333)、したがって中括弧で囲む必要があります `{-6}`。
|
||||
|
||||
:::
|
||||
|
||||
:::note コンポーネント属性
|
||||
コンポーネント属性は Rust オブジェクトとして渡され、ここで説明されている要素のパラメータ (Attributes) / 属性 (Properties) とは異なります。
|
||||
[コンポーネント属性](../function-components/properties.mdx)で詳細を確認してください。
|
||||
:::
|
||||
|
||||
### 特殊属性
|
||||
|
||||
いくつかの特殊な属性があり、これらは直接 DOM に影響を与えるのではなく、Yew 仮想 DOM の指示として機能します。現在、2 つの特殊な属性があります:`ref` と `key`。
|
||||
|
||||
`ref` は、基礎となる DOM ノードに直接アクセスして操作することを可能にします。詳細については、[Refs](../function-components/node-refs.mdx)を参照してください。
|
||||
|
||||
一方、`key` は要素に一意の識別子を提供し、Yew が最適化のために使用できます。
|
||||
|
||||
:::info
|
||||
[詳細はこちら](./html/lists)
|
||||
:::
|
||||
|
||||
## 条件付きレンダリング
|
||||
|
||||
Rust の条件構造を使用して、条件付きでマークアップをレンダリングできます。現在、`if` と `if let` のみがサポートされています。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
html! {
|
||||
if true {
|
||||
<p>{ "True case" }</p>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
:::info
|
||||
条件付きレンダリングの詳細については、[条件付きレンダリング](./conditional-rendering.mdx)のセクションを参照してください。
|
||||
:::
|
||||
|
|
|
@ -1,60 +1,119 @@
|
|||
---
|
||||
title: Lists
|
||||
title: 'リスト'
|
||||
---
|
||||
|
||||
## フラグメント
|
||||
|
||||
`html!`マクロは常にルートノードが 1 つであることを要求します。
|
||||
この制限のために、空のタグを使って内容をラップすると良いでしょう。
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Valid-->
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<>
|
||||
<div></div>
|
||||
<p></p>
|
||||
</>
|
||||
}
|
||||
```
|
||||
|
||||
<!--Invalid-->
|
||||
|
||||
```rust
|
||||
/* error: only one root html element allowed */
|
||||
|
||||
html! {
|
||||
<div></div>
|
||||
<p></p>
|
||||
}
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## イテレータ
|
||||
|
||||
Yew はイテレータから HTML をビルドするのに 2 つの方法をサポートしています。
|
||||
Yew は、イテレータから HTML を構築するための 2 つの異なる構文をサポートしています。
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Syntax Type 1-->
|
||||
<Tabs>
|
||||
<TabItem value="Syntax type 1" label="Syntax type 1">
|
||||
|
||||
最初の方法は、イテレータの最終変換で `collect::<Html>()` を呼び出すことで、Yew が表示できるリストを返します。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let items = (1..=10).collect::<Vec<_>>();
|
||||
|
||||
html! {
|
||||
<ul class="item-list">
|
||||
{ self.props.items.iter().map(renderItem).collect::<Html>() }
|
||||
{ items.iter().collect::<Html>() }
|
||||
</ul>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<!--Syntax Type 2-->
|
||||
</TabItem>
|
||||
<TabItem value="Syntax type 2" label="Syntax type 2">
|
||||
|
||||
もう一つの方法は、`for` キーワードを使用することです。これはネイティブの Rust 構文ではなく、HTML マクロによってイテレータを表示するために必要なコードを出力します。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let items = (1..=10).collect::<Vec<_>>();
|
||||
|
||||
html! {
|
||||
<ul class="item-list">
|
||||
{ for self.props.items.iter().map(renderItem) }
|
||||
{ for items.iter() }
|
||||
</ul>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## キー付きリスト
|
||||
|
||||
キー付きリストは、すべての子要素にキーがある最適化されたリストです。
|
||||
`key` は Yew が提供する特別な属性で、HTML 要素やコンポーネントに一意の識別子を与え、Yew 内部での最適化に使用されます。
|
||||
|
||||
:::caution
|
||||
キーは各リスト内で一意である必要があり、HTML の `id` のようにグローバルに一意である必要はありません。リストの順序に依存してはいけません。
|
||||
:::
|
||||
|
||||
リストにキーを追加することを常にお勧めします。
|
||||
|
||||
一意の `String`、`str`、または整数を特別な `key` 属性に渡すことでキーを追加できます。
|
||||
|
||||
```rust , ignore
|
||||
use yew::prelude::*;
|
||||
|
||||
let names = vec!["Sam","Bob","Ray"]
|
||||
|
||||
html! {
|
||||
<div id="introductions">
|
||||
{
|
||||
names.into_iter().map(|name| {
|
||||
html!{<div key={name}>{ format!("Hello, I'am {}!",name) }</div>}
|
||||
}).collect::<Html>()
|
||||
}
|
||||
</div>
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### パフォーマンスの最適化
|
||||
|
||||
キー付きリストのパフォーマンス向上をテストするための[例](https://github.com/yewstack/yew/tree/master/examples/keyed_list)があります。以下は簡単なテスト手順です:
|
||||
|
||||
1. [オンラインデモ](https://examples.yew.rs/keyed_list)にアクセスします。
|
||||
2. 500個の要素を追加します。
|
||||
3. キーを無効にします。
|
||||
4. リストを反転します。
|
||||
5. "最後のレンダリングにかかった時間 Xms" を確認します(この記事の執筆時点では約60ms)。
|
||||
6. キーを有効にします。
|
||||
7. 再度リストを反転します。
|
||||
8. "最後のレンダリングにかかった時間 Xms" を確認します(この記事の執筆時点では約30ms)。
|
||||
|
||||
この記事の執筆時点では、500個のコンポーネントに対して速度が2倍に向上しました。
|
||||
|
||||
### 原理の説明
|
||||
|
||||
通常、リストを反復処理する際には、各リスト項目にキーを追加するだけで済みます。データの順序が変わる可能性があるためです。
|
||||
リストを再レンダリングする際に、キーは調整プロセスを高速化するために使用されます。
|
||||
|
||||
キーがない場合、例えば `["bob", "sam", "rob"]` を反復処理すると、最終的に以下のようなHTMLが生成されます:
|
||||
|
||||
```html
|
||||
<div id="bob">My name is Bob</div>
|
||||
<div id="sam">My name is Sam</div>
|
||||
<div id="rob">My name is rob</div>
|
||||
```
|
||||
|
||||
次のレンダリングでリストが `["bob", "rob"]` に変更された場合、Yew は id="rob" の要素を削除し、id="sam" を id="rob" に更新できます。
|
||||
|
||||
各要素にキーを追加すると、初期の HTML は変わりませんが、変更後のリスト `["bob", "rob"]` をレンダリングすると、Yew は2番目の HTML 要素のみを削除し、他の要素はそのまま残ります。キーを使用して要素を関連付けることができるためです。
|
||||
|
||||
コンポーネントから別のコンポーネントに切り替える際に、両方に最高レンダリング要素として div がある場合にバグ/"機能" に遭遇した場合。
|
||||
Yew はこれらの状況で最適化として既にレンダリングされた HTML div を再利用します。
|
||||
その div を再利用せずに再作成する必要がある場合は、異なるキーを追加することで再利用されなくなります。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [TodoMVC の例](https://github.com/yewstack/yew/tree/master/examples/todomvc)
|
||||
- [キー付きリストの例](https://github.com/yewstack/yew/tree/master/examples/keyed_list)
|
||||
- [ルーティングの例](https://github.com/yewstack/yew/tree/master/examples/router)
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
---
|
||||
title: Literals and Expressions
|
||||
title: 'リテラルと式'
|
||||
---
|
||||
|
||||
## リテラル
|
||||
|
||||
式が`Display`を実装した型を解決する場合、文字列に変換されて DOM に[Text](https://developer.mozilla.org/en-US/docs/Web/API/Text)ノードとして挿入されます。
|
||||
式が `Display` を実装する型に解決される場合、それらは文字列に変換され、[Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) ノードとしてDOMに挿入されます。
|
||||
|
||||
テキストは式として処理されるため、全ての表示される内容は`{}`ブロックによって囲まれる必要があります。
|
||||
これは Yew のアプリと通常の HTML の構文で最も異なる点です。
|
||||
:::note
|
||||
文字列リテラルは `Text` ノードを作成し、ブラウザはそれを文字列として扱います。そのため、式に `<script>` タグが含まれていても、式を `<script>` ブロックでラップしない限り、XSS などのセキュリティ問題に遭遇することはありません。
|
||||
:::
|
||||
|
||||
すべての表示テキストは式と見なされるため、`{}` ブロックで囲む必要があります。これは、Yew と通常の HTML 構文の最大の違いです。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let text = "lorem ipsum";
|
||||
html!{
|
||||
<>
|
||||
|
@ -17,14 +22,18 @@ html!{
|
|||
<div>{"dolor sit"}</div>
|
||||
<span>{42}</span>
|
||||
</>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 式
|
||||
|
||||
HTML に`{}`ブロックを使って式を挿入することができます。
|
||||
`{}` ブロックを使用して、HTML 内に式を挿入できます。それらが `Html` に解決される限り。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let show_link = true;
|
||||
|
||||
html! {
|
||||
<div>
|
||||
{
|
||||
|
@ -37,12 +46,14 @@ html! {
|
|||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
式を関数やクロージャに分離するのはコードの可読性の観点から有効なことがあります。
|
||||
通常、これらの式を関数やクロージャに抽出して、可読性を最適化することが意味があります:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let show_link = true;
|
||||
let maybe_display_link = move || -> Html {
|
||||
if show_link {
|
||||
|
@ -56,5 +67,5 @@ let maybe_display_link = move || -> Html {
|
|||
|
||||
html! {
|
||||
<div>{maybe_display_link()}</div>
|
||||
}
|
||||
};
|
||||
```
|
||||
|
|
|
@ -1,95 +1,461 @@
|
|||
---
|
||||
title: Router
|
||||
description: Yew's official router
|
||||
title: 'ルーター (Router)'
|
||||
description: 'Yew の公式ルーターライブラリ'
|
||||
---
|
||||
|
||||
[crates.io にあるルータ](https://crates.io/crates/yew-router)
|
||||
シングルページアプリケーション (SPA) のルーターは、URL に基づいて異なるページを表示する処理を行います。リンクをクリックしたときに異なるリモートリソースを要求するデフォルトの動作とは異なり、ルーターはアプリケーション内の有効なルートを指すようにローカルで URL を設定します。その後、ルーターはこの変更を検出し、レンダリングする内容を決定します。
|
||||
|
||||
シングルページアプリケーション\(SPA\)におけるルータは URL よってページを出し分けます。
|
||||
リンクがクリックされたときに異なるリソースを要求するというデフォルトの動作の代わりに、ルータはアプリケーション内の有効なルートを指すように URL をローカルに設定します。
|
||||
ルータはこの変更を検出してから、何をレンダリングするかを決定します。
|
||||
Yew は `yew-router` クレートでルーターサポートを提供します。使用を開始するには、依存関係を `Cargo.toml` ファイルに追加してください。
|
||||
|
||||
## コアとなる要素
|
||||
<!-- Reminder: fix this when we release a new version of yew -->
|
||||
|
||||
### `Route`
|
||||
```toml
|
||||
yew-router = { git = "https://github.com/yewstack/yew.git" }
|
||||
```
|
||||
|
||||
URL 内のドメインの後のすべてを表す文字列と、オプションで history API に保存されている状態を含みます。
|
||||
必要なツールはすべて `yew_router::prelude` モジュールで提供されています。
|
||||
|
||||
### `RouteService`
|
||||
## 使用方法
|
||||
|
||||
ブラウザとやりとりしてルーティングを決めます。
|
||||
まず、`Route` を定義する必要があります。
|
||||
|
||||
### `RouteAgent`
|
||||
|
||||
RouteService を所有し、ルートが変更された際の更新を調整するために使用します。
|
||||
|
||||
### `Switch`
|
||||
|
||||
`Switch`トレイトは`Route`をトレイトの実装する側の間で変換するために用いられます。
|
||||
|
||||
### `Router`
|
||||
|
||||
Router コンポーネントは RouteAgent とやり取りし、エージェントがどうスイッチするか Routes を自動的に解決します。
|
||||
これは、結果として得られるスイッチがどのように Html に変換されるかを指定できるようにするため、props を介して公開されます。
|
||||
|
||||
## ルータをどのように使うか
|
||||
|
||||
まず、アプリケーションのすべての状態を表す型を作成します。
|
||||
これは通常は列挙型ですが、構造体もサポートされており、`Switch` を実装した他のアイテムを内部に入れ子にすることができることに注意してください。
|
||||
|
||||
次に、`Switch`を型に継承させなければいけません。
|
||||
列挙型の場合は全ての variant は`#[to = "/some/route"]`とアノテーションされている必要があり、代わり構造体を用いている場合は構造体宣言が外部から見えるようにしてなければいけません。
|
||||
ルートは `Routable` を派生する `enum` で定義されます。この列挙型は `Clone + PartialEq` を実装する必要があります。
|
||||
|
||||
```rust
|
||||
#[derive(Switch)]
|
||||
enum AppRoute {
|
||||
#[to="/login"]
|
||||
Login,
|
||||
#[to="/register"]
|
||||
Register,
|
||||
#[to="/delete_account"]
|
||||
Delete,
|
||||
#[to="/posts/{id}"]
|
||||
ViewPost(i32),
|
||||
#[to="/posts/view"]
|
||||
ViewPosts,
|
||||
#[to="/"]
|
||||
Home
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
enum Route {
|
||||
#[at("/")]
|
||||
Home,
|
||||
#[at("/secure")]
|
||||
Secure,
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
```
|
||||
|
||||
`Route` と `<Switch />` コンポーネントはペアで使用され、後者はブラウザの現在の URL に一致するパスのバリアントを見つけ、それを `render` コールバックに渡します。その後、コールバックがレンダリングする内容を決定します。パスが一致しない場合、ルーターは `not_found` 属性を持つパスにナビゲートします。指定されたルートがない場合、何もレンダリングされず、一致するルートがないことを示すメッセージがコンソールに記録されます。
|
||||
|
||||
yew-router のほとんどのコンポーネント、特に `<Link />` と `<Switch />` は、ある Router コンポーネント(例: `<BrowserRouter />`)の(深い)子要素である必要があります。通常、アプリケーションには 1 つの Router しか必要なく、通常は最上位の `<App />` コンポーネントによって直ちにレンダリングされます。Router はコンテキストを登録し、これは Links と Switches の機能に必要です。以下に例を示します。
|
||||
|
||||
:::caution
|
||||
ブラウザ環境で `yew-router` を使用する場合、`<BrowserRouter />` を強く推奨します。他のルータータイプについては [API リファレンス](https://docs.rs/yew-router/) を参照してください。
|
||||
:::
|
||||
|
||||
```rust
|
||||
use yew_router::prelude::*;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
enum Route {
|
||||
#[at("/")]
|
||||
Home,
|
||||
#[at("/secure")]
|
||||
Secure,
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[function_component(Secure)]
|
||||
fn secure() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
|
||||
let onclick = Callback::from(move |_| navigator.push(&Route::Home));
|
||||
html! {
|
||||
<div>
|
||||
<h1>{ "Secure" }</h1>
|
||||
<button {onclick}>{ "Go Home" }</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn switch(routes: Route) -> Html {
|
||||
match routes {
|
||||
Route::Home => html! { <h1>{ "Home" }</h1> },
|
||||
Route::Secure => html! {
|
||||
<Secure />
|
||||
},
|
||||
Route::NotFound => html! { <h1>{ "404" }</h1> },
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(Main)]
|
||||
fn app() -> Html {
|
||||
html! {
|
||||
<BrowserRouter>
|
||||
<Switch<Route> render={switch} /> // <- must be child of <BrowserRouter>
|
||||
</BrowserRouter>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### パスセグメント
|
||||
|
||||
ルーターは、動的および名前付きワイルドカードセグメントを使用してルートから情報を抽出することもできます。次に、`<Switch />` 内で投稿の ID にアクセスし、それを適切なコンポーネントにプロパティとして渡すことができます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
enum Route {
|
||||
#[at("/")]
|
||||
Home,
|
||||
#[at("/post/:id")]
|
||||
Post { id: String },
|
||||
#[at("/*path")]
|
||||
Misc { path: String },
|
||||
}
|
||||
|
||||
fn switch(route: Route) -> Html {
|
||||
match route {
|
||||
Route::Home => html! { <h1>{ "Home" }</h1> },
|
||||
Route::Post { id } => html! {<p>{format!("You are looking at Post {}", id)}</p>},
|
||||
Route::Misc { path } => html! {<p>{format!("Matched some other path: {}", path)}</p>},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
`Post {id: String}` の代わりに通常の `Post` バリアントを使用することもできます。例えば、`Post` が別のルーターと一緒にレンダリングされる場合、そのフィールドは冗長になる可能性があります。詳細については、以下の[ネストされたルーター](#nested-router)セクションを参照してください。
|
||||
:::
|
||||
|
||||
フィールドは `Route` 列挙型の一部として `Clone + PartialEq` を実装する必要があることに注意してください。また、シリアル化と逆シリアル化のために `std::fmt::Display` と `std::str::FromStr` を実装する必要があります。整数、浮動小数点数、および文字列などのプリミティブ型はこれらの要件を既に満たしています。
|
||||
|
||||
パスの形式が一致しても、逆シリアル化が失敗した場合(`FromStr` に基づく)、ルーターはルートが一致しないと見なし、見つからないルートをレンダリングしようとします(または、見つからないルートが指定されていない場合は空白ページをレンダリングします)。
|
||||
|
||||
以下の例を参照してください:
|
||||
|
||||
```rust ,ignore
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
enum Route {
|
||||
#[at("/news/:id")]
|
||||
News { id: u8 },
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
// switch 関数は News と id をレンダリングします。ここでは省略されています。
|
||||
```
|
||||
|
||||
セグメントが 255 を超えると、`u8::from_str()` は失敗し、`ParseIntError` を返します。この場合、ルーターはルートが一致しないと見なします。
|
||||
|
||||

|
||||
|
||||
ルーティング構文やパラメータのバインディング方法の詳細については、[route-recognizer](https://docs.rs/route-recognizer/0.3.1/route_recognizer/#routing-params) を参照してください。
|
||||
|
||||
### 位置 (Location)
|
||||
|
||||
ルーターはコンテキストを介して一般的な `Location` 構造体を提供し、ルート情報にアクセスするために使用できます。これらはフックまたは `ctx.link()` 上の便利な関数を介して取得できます。
|
||||
|
||||
### ナビゲーション
|
||||
|
||||
`yew_router` はナビゲーションを処理するためのいくつかのツールを提供します。
|
||||
|
||||
#### リンク
|
||||
|
||||
`<Link />` は `<a>` 要素としてレンダリングされ、`onclick` イベントハンドラは [preventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) を呼び出し、ターゲットページを履歴にプッシュして必要なページをレンダリングします。これはシングルページアプリケーションに期待される動作です。通常のアンカー要素のデフォルトの `onclick` はページをリロードします。
|
||||
|
||||
`<Link />` コンポーネントはその子要素を `<a>` 要素に渡します。これはアプリ内ルーティングのための `<a/>` の代替として考えることができます。違いは、`href` の代わりに `to` 属性を提供する必要があることです。使用例は以下の通りです:
|
||||
|
||||
```rust ,ignore
|
||||
<Link<Route> to={Route::Home}>{ "click here to go home" }</Link<Route>>
|
||||
```
|
||||
|
||||
構造体変数も正常に動作します:
|
||||
|
||||
```rust ,ignore
|
||||
<Link<Route> to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew!" }</Link<Route>>
|
||||
```
|
||||
|
||||
#### ナビゲーションインターフェース
|
||||
|
||||
ナビゲーター API は、関数コンポーネントと構造体コンポーネントの両方で提供されます。これにより、コールバックがルートを変更できるようになります。どちらの場合でも、`Navigator` インスタンスを取得してルートを操作できます。
|
||||
|
||||
##### 関数コンポーネント
|
||||
|
||||
関数コンポーネントの場合、基礎となるナビゲータープロバイダーが変更されると、`use_navigator` フックはコンポーネントを再レンダリングします。
|
||||
以下は、クリック時に `Home` ルートにナビゲートするボタンを実装する例です。
|
||||
|
||||
```rust ,ignore
|
||||
#[function_component(MyComponent)]
|
||||
pub fn my_component() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
let onclick = Callback::from(move |_| navigator.push(&Route::Home));
|
||||
|
||||
html! {
|
||||
<>
|
||||
<button {onclick}>{"Click to go home"}</button>
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::caution
|
||||
`Switch`用の派生マクロによって生成された実装は、各 variant を最初から最後までの順にマッチさせようとするので、指定した`to`アノテーションのうち 2 つのルートにマッチする可能性がある場合は、最初のルートがマッチし、2 つ目のルートは試行されないことに注意してください。例えば、以下の`Switch`を定義した場合、マッチするルートは`AppRoute::Home`だけになります。
|
||||
ここでの例では `Callback::from` を使用しています。ターゲットルートがコンポーネントのルートと同じになる可能性がある場合、または安全のために、通常のコールバックを使用してください。例えば、各ページにロゴボタンがあり、そのボタンをクリックするとホームに戻るとします。ホームページでそのボタンを2回クリックすると、同じHomeルートがプッシュされ、`use_navigator` フックが再レンダリングをトリガーしないため、コードがクラッシュします。
|
||||
:::
|
||||
|
||||
```rust
|
||||
#[derive(Switch)]
|
||||
enum AppRoute {
|
||||
#[to="/"]
|
||||
Home,
|
||||
#[to="/login"]
|
||||
Login,
|
||||
#[to="/register"]
|
||||
Register,
|
||||
#[to="/delete_account"]
|
||||
Delete,
|
||||
#[to="/posts/{id}"]
|
||||
ViewPost(i32),
|
||||
#[to="/posts/view"]
|
||||
ViewPosts,
|
||||
現在の位置をスタックに新しい位置としてプッシュするのではなく置き換えたい場合は、`navigator.push()` の代わりに `navigator.replace()` を使用してください。
|
||||
|
||||
`navigator` はコールバックに移動する必要があるため、他のコールバックで再利用できないことに気付くかもしれません。幸いなことに、`navigator` は `Clone` を実装しているため、異なるルートに対して複数のボタンを設定する方法は次のとおりです:
|
||||
|
||||
```rust ,ignore
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[function_component(NavItems)]
|
||||
pub fn nav_items() -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
|
||||
let go_home_button = {
|
||||
let navigator = navigator.clone();
|
||||
let onclick = Callback::from(move |_| navigator.push(&Route::Home));
|
||||
html! {
|
||||
<button {onclick}>{"click to go home"}</button>
|
||||
}
|
||||
};
|
||||
|
||||
let go_to_first_post_button = {
|
||||
let navigator = navigator.clone();
|
||||
let onclick = Callback::from(move |_| navigator.push(&Route::Post { id: "first-post".to_string() }));
|
||||
html! {
|
||||
<button {onclick}>{"click to go the first post"}</button>
|
||||
}
|
||||
};
|
||||
|
||||
let go_to_secure_button = {
|
||||
let onclick = Callback::from(move |_| navigator.push(&Route::Secure));
|
||||
html! {
|
||||
<button {onclick}>{"click to go to secure"}</button>
|
||||
}
|
||||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
{go_home_button}
|
||||
{go_to_first_post_button}
|
||||
{go_to_secure_button}
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### 構造体コンポーネント
|
||||
|
||||
構造体コンポーネントの場合、`ctx.link().navigator()` API を使用して `Navigator` インスタンスを取得できます。残りの部分は関数コンポーネントの場合と同じです。以下は、単一のボタンをレンダリングするビュー関数の例です。
|
||||
|
||||
```rust ,ignore
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let navigator = ctx.link().navigator().unwrap();
|
||||
let onclick = Callback::from(move |_| navigator.push(&MainRoute::Home));
|
||||
html!{
|
||||
<button {onclick}>{"Go Home"}</button>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### リダイレクト
|
||||
|
||||
`yew-router` は prelude に `<Redirect />` コンポーネントも提供しています。これはナビゲーター API と同様の効果を実現するために使用できます。このコンポーネントは、ターゲットルートとして `to` 属性を受け取ります。`<Redirect/>` がレンダリングされると、ユーザーは指定されたルートにリダイレクトされます。以下はその例です:
|
||||
|
||||
```rust ,ignore
|
||||
#[function_component(SomePage)]
|
||||
fn some_page() -> Html {
|
||||
// `use_user` フックを使用してユーザーを取得
|
||||
let user = match use_user() {
|
||||
Some(user) => user,
|
||||
// ユーザーが `None` の場合、ログインページにリダイレクト
|
||||
None => return html! {
|
||||
<Redirect<Route> to={Route::Login}/>
|
||||
},
|
||||
};
|
||||
// ... 実際のページ内容
|
||||
}
|
||||
```
|
||||
|
||||
:::tip `Redirect` と `Navigator` の選択方法
|
||||
Navigator API はコールバック内でルートを操作する唯一の方法です。
|
||||
一方、`<Redirect />` はコンポーネント内の戻り値として使用できます。また、[ネストされたルーター](#nested-router)の switch 関数など、他の非コンポーネントコンテキストでも `<Redirect />` を使用することができます。
|
||||
:::
|
||||
|
||||
また、`#[to = ""]`アノテーションの中で`{}`のバリエーションを使ってセクションをキャプチャすることもできます。
|
||||
`{}`は、次の区切り文字\(コンテキストに応じて "/", "?", "&", "#" のいずれか\) までのテキストをキャプチャします。
|
||||
`{*}`は、次の文字が一致するまでテキストをキャプチャすることを意味します。
|
||||
`{<number>}`は、指定した数の区切り文字が見つかるまでテキストをキャプチャすることを意味します
|
||||
\(例: `{2}`は区切り文字が 2 つ見つかるまでキャプチャします\)。
|
||||
### 変更のリスニング
|
||||
|
||||
名前付きフィールドを持つ構造体や列挙型の場合は、キャプチャグループ内で以下のようにフィールドの名前を指定する必要があります。
|
||||
`{user_name}` または `{*:age}` のように、キャプチャグループ内でフィールドの名前を指定しなければなりません。
|
||||
#### 関数コンポーネント
|
||||
|
||||
Switch トレイトは文字列よりも構造化されたキャプチャグループで動作します。
|
||||
`Switch`を実装した任意の型を指定することができます。
|
||||
そのため、キャプチャグループが `usize` であることを指定することができ、URL のキャプチャ部分がそれに変換できない場合、variant はマッチしません。
|
||||
`use_location` と `use_route` フックを使用できます。提供された値が変更されると、コンポーネントが再レンダリングされます。
|
||||
|
||||
#### 構造体コンポーネント
|
||||
|
||||
ルートの変更に応答するために、`ctx.link()` の `add_location_listener()` メソッドにコールバッククロージャを渡すことができます。
|
||||
|
||||
:::note
|
||||
位置リスナーが削除されると、それは登録解除されます。ハンドルをコンポーネントの状態に保存することを確認してください。
|
||||
:::
|
||||
|
||||
```rust ,ignore
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
let listener = ctx.link()
|
||||
.add_location_listener(ctx.link().callback(
|
||||
// イベントを処理する
|
||||
))
|
||||
.unwrap();
|
||||
MyComponent {
|
||||
_listener: listener
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ctx.link().location()` と `ctx.link().route::<R>()` も、一度だけ位置とルートを取得するために使用できます。
|
||||
|
||||
### クエリパラメータ
|
||||
|
||||
#### ナビゲーション時にクエリパラメータを指定する
|
||||
|
||||
新しいルートにナビゲートする際にクエリパラメータを指定するには、`navigator.push_with_query` または `navigator.replace_with_query` 関数を使用します。これは `serde` を使用してパラメータを URL のクエリ文字列にシリアル化するため、`Serialize` を実装している任意の型を渡すことができます。最も簡単な形式は文字列ペアを含む `HashMap` です。
|
||||
|
||||
#### 現在のルートのクエリパラメータを取得する
|
||||
|
||||
クエリパラメータを取得するには、`location.query` を使用します。これは `serde` を使用して URL のクエリ文字列からパラメータを逆シリアル化します。
|
||||
|
||||
## ネストされたルーター
|
||||
|
||||
アプリケーションが大きくなると、ネストされたルーターが役立つ場合があります。次のルーター構造を考えてみましょう:
|
||||
|
||||
<!--
|
||||
The graph is produced with the following code, with graphviz.
|
||||
To reproduce. Save the code in a file, say `input.dot`,
|
||||
And run `$ dot -Tsvg input.dot -o nested-router.svg`
|
||||
|
||||
digraph {
|
||||
bgcolor=transparent
|
||||
node [shape=box style="filled, rounded" fillcolor=white]
|
||||
Home; News; Contact; "Not Found"; Profile; Friends; Theme; SettingsNotFound [label="Not Found"];
|
||||
|
||||
node [fillcolor=lightblue style="filled, rounded"]
|
||||
"Main Router"; "Settings Router";
|
||||
|
||||
"Main Router" -> {Home News Contact "Not Found" "Settings Router"} [arrowhead=none]
|
||||
"Settings Router" -> {SettingsNotFound Profile Friends Theme } [arrowhead=none]
|
||||
SettingsNotFound -> "Not Found" [constraint=false]
|
||||
}
|
||||
-->
|
||||
|
||||
<!--
|
||||
Also the dark-themed version:
|
||||
digraph {
|
||||
bgcolor=transparent
|
||||
node [shape=box style="filled, rounded" fillcolor=grey color=white fontcolor=white]
|
||||
Home; News; Contact; "Not Found"; Profile; Friends; Theme; SettingsNotFound [label="Not Found"];
|
||||
|
||||
node [fillcolor=lightblue style="filled, rounded" color=white fontcolor=black]
|
||||
"Main Router"; "Settings Router";
|
||||
|
||||
"Main Router" -> {Home News Contact "Not Found" "Settings Router"} [arrowhead=none color=white]
|
||||
"Settings Router" -> {SettingsNotFound Profile Friends Theme } [arrowhead=none color=white]
|
||||
SettingsNotFound -> "Not Found" [constraint=false color=white]
|
||||
}
|
||||
-->
|
||||
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
import ThemedImage from '@theme/ThemedImage'
|
||||
|
||||
<ThemedImage
|
||||
alt="nested router structure"
|
||||
sources={{
|
||||
light: useBaseUrl('/img/nested-router-light.svg'),
|
||||
dark: useBaseUrl('/img/nested-router-dark.svg'),
|
||||
}}
|
||||
/>
|
||||
|
||||
ネストされた `SettingsRouter` は、すべての `/settings` で始まる URL を処理します。また、一致しない URL をメインの `NotFound` ルートにリダイレクトします。したがって、`/settings/gibberish` は `/404` にリダイレクトされます。
|
||||
|
||||
:::caution
|
||||
|
||||
このインターフェースはまだ開発中であり、このように記述する方法は最終決定されていません。
|
||||
|
||||
:::
|
||||
|
||||
以下のコードで実装できます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
use gloo::utils::window;
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
enum MainRoute {
|
||||
#[at("/")]
|
||||
Home,
|
||||
#[at("/news")]
|
||||
News,
|
||||
#[at("/contact")]
|
||||
Contact,
|
||||
#[at("/settings")]
|
||||
SettingsRoot,
|
||||
#[at("/settings/*")]
|
||||
Settings,
|
||||
#[not_found]
|
||||
#[at("/404")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(Clone, Routable, PartialEq)]
|
||||
enum SettingsRoute {
|
||||
#[at("/settings")]
|
||||
Profile,
|
||||
#[at("/settings/friends")]
|
||||
Friends,
|
||||
#[at("/settings/theme")]
|
||||
Theme,
|
||||
#[not_found]
|
||||
#[at("/settings/404")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
fn switch_main(route: MainRoute) -> Html {
|
||||
match route {
|
||||
MainRoute::Home => html! {<h1>{"Home"}</h1>},
|
||||
MainRoute::News => html! {<h1>{"News"}</h1>},
|
||||
MainRoute::Contact => html! {<h1>{"Contact"}</h1>},
|
||||
MainRoute::SettingsRoot | MainRoute::Settings => html! { <Switch<SettingsRoute> render={switch_settings} /> },
|
||||
MainRoute::NotFound => html! {<h1>{"Not Found"}</h1>},
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_settings(route: SettingsRoute) -> Html {
|
||||
match route {
|
||||
SettingsRoute::Profile => html! {<h1>{"Profile"}</h1>},
|
||||
SettingsRoute::Friends => html! {<h1>{"Friends"}</h1>},
|
||||
SettingsRoute::Theme => html! {<h1>{"Theme"}</h1>},
|
||||
SettingsRoute::NotFound => html! {<Redirect<MainRoute> to={MainRoute::NotFound}/>}
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
pub fn app() -> Html {
|
||||
html! {
|
||||
<BrowserRouter>
|
||||
<Switch<MainRoute> render={switch_main} />
|
||||
</BrowserRouter>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ベースパス (Basename)
|
||||
|
||||
`yew-router` を使用してベースパス (Basename) を定義できます。
|
||||
ベースパスはすべてのルートの共通プレフィックスです。ナビゲーター API と `<Switch />` コンポーネントはどちらもベースパスの設定をサポートしています。プッシュされるすべてのルートにはベースパスのプレフィックスが追加され、すべてのスイッチはパスを `Routable` に解析する前にベースパスを削除します。
|
||||
|
||||
Router コンポーネントにベースパス属性が提供されていない場合、HTML ファイルの `<base />` 要素の href 属性を使用し、HTML ファイルに `<base />` 要素がない場合は `/` にフォールバックします。
|
||||
|
||||
## 関連例
|
||||
|
||||
- [ルーター](https://github.com/yewstack/yew/tree/master/examples/router)
|
||||
|
||||
## インターフェースリファレンス
|
||||
|
||||
- [yew-router](https://docs.rs/yew-router/)
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
title: 'サスペンス (Suspense)'
|
||||
description: 'データ取得のためのサスペンス'
|
||||
---
|
||||
|
||||
サスペンス (Suspense) は、タスクが完了するまでコンポーネントのレンダリングを一時停止し、その間にフォールバック(プレースホルダー)UI を表示する方法です。
|
||||
|
||||
これは、サーバーからデータを取得したり、プロキシがタスクを完了するのを待ったり、他のバックグラウンド非同期タスクを実行したりするために使用できます。
|
||||
|
||||
サスペンスが表示される前に、データ取得は通常、コンポーネントのレンダリング後(レンダリング時取得)またはレンダリング前(取得後レンダリング)に発生します。
|
||||
|
||||
### レンダリングしながらダウンロード
|
||||
|
||||
サスペンス (Suspense) は、新しい方法を提供し、コンポーネントがレンダリング中にデータリクエストを発行できるようにします。コンポーネントがデータリクエストを発行すると、レンダリングプロセスが一時停止され、リクエストが完了するまでフォールバック UI が表示されます。
|
||||
|
||||
サスペンスを使用するには、フック (Hook) を使用することをお勧めします。
|
||||
|
||||
```rust ,ignore
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component(Content)]
|
||||
fn content() -> HtmlResult {
|
||||
let user = use_user()?;
|
||||
|
||||
Ok(html! {<div>{"Hello, "}{&user.name}</div>})
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
let fallback = html! {<div>{"Loading..."}</div>};
|
||||
|
||||
html! {
|
||||
<Suspense {fallback}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上記の例では、`use_user` フックはユーザー情報の読み込み中にコンポーネントのレンダリングを一時停止し、`user` が読み込まれる前に `Loading...` プレースホルダーを表示します。
|
||||
|
||||
コンポーネントのレンダリングを一時停止するフックを定義するには、`SuspensionResult<T>` を返す必要があります。コンポーネントが一時停止する必要がある場合、フックは `Err(Suspension)` を返すべきであり、ユーザーはそれを `?` でアンパックする必要があります。これにより、それが `Html` に変換されます。
|
||||
|
||||
```rust ,ignore
|
||||
use yew::prelude::*;
|
||||
use yew::suspense::{Suspension, SuspensionResult};
|
||||
|
||||
struct User {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[hook]
|
||||
fn use_user() -> SuspensionResult<User> {
|
||||
match load_user() {
|
||||
// ユーザーが読み込まれたら、それを Ok(user) として返します。
|
||||
Some(user) => Ok(user),
|
||||
None => {
|
||||
// ユーザーがまだ読み込まれていない場合、`Suspension` を作成し、
|
||||
// データの読み込みが完了したときに `SuspensionHandle::resume` を呼び出します。
|
||||
// これにより、コンポーネントは自動的に再レンダリングされます。
|
||||
let (s, handle) = Suspension::new();
|
||||
on_load_user_complete(move || {handle.resume();});
|
||||
Err(s)
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### サスペンスフック (Hook) の実装に関する注意事項
|
||||
|
||||
[`Suspension::new`](https://docs.rs/yew/latest/yew/suspense/struct.Suspension.html#method.new) は 2 つの値を返します:サスペンスコンテキスト自体とサスペンスハンドル。後者はサスペンスされたコンポーネントを再レンダリングするタイミングを管理し、2 つの方法で操作できます:
|
||||
|
||||
1. その [`resume`](https://docs.rs/yew/latest/yew/suspense/struct.SuspensionHandle.html#method.resume) メソッドを呼び出す。
|
||||
2. ハンドルを破棄する。
|
||||
|
||||
:::danger
|
||||
|
||||
サスペンスハンドルは、新しいデータを受け取ってコンポーネントを更新するまで保存する必要があります。そうしないと、サスペンスされたコンポーネントが無限再レンダリングループに入り、パフォーマンスに影響を与えます。
|
||||
上記の例では、サスペンスハンドルはクロージャに移動し、`on_load_user_complete` に渡されることで保存されます。
|
||||
仮想ユーザーが読み込まれると、クロージャが呼び出され、`handle.resume()` が呼び出され、サスペンスコンテキストに関連するコンポーネントが再レンダリングされます。
|
||||
|
||||
:::
|
||||
|
||||
# 完全な例
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use yew::suspense::{Suspension, SuspensionResult};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct User {
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn load_user() -> Option<User> {
|
||||
todo!() // 省略
|
||||
}
|
||||
|
||||
fn on_load_user_complete<F: FnOnce()>(_fn: F) {
|
||||
todo!() // 省略
|
||||
}
|
||||
|
||||
#[hook]
|
||||
fn use_user() -> SuspensionResult<User> {
|
||||
match load_user() {
|
||||
// ユーザーが読み込まれたら、それを Ok(user) として返します。
|
||||
Some(user) => Ok(user),
|
||||
None => {
|
||||
// ユーザーがまだ読み込まれていない場合、`Suspension` を作成し、
|
||||
// データの読み込みが完了したときに `SuspensionHandle::resume` を呼び出します。
|
||||
// これにより、コンポーネントは自動的に再レンダリングされます。
|
||||
let (s, handle) = Suspension::new();
|
||||
on_load_user_complete(move || {handle.resume();});
|
||||
Err(s)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(Content)]
|
||||
fn content() -> HtmlResult {
|
||||
let user = use_user()?;
|
||||
|
||||
Ok(html! {<div>{"Hello, "}{&user.name}</div>})
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
let fallback = html! {<div>{"Loading..."}</div>};
|
||||
|
||||
html! {
|
||||
<Suspense {fallback}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 構造体コンポーネントでプレースホルダーを使用する
|
||||
|
||||
構造体コンポーネントを直接サスペンドすることはできません。しかし、関数コンポーネントを[高階コンポーネント](../advanced-topics/struct-components/hoc)として使用し、プレースホルダーに基づいたデータ取得を実現することができます。
|
||||
|
||||
Yew リポジトリの[プレースホルダーの例](https://github.com/yewstack/yew/tree/master/examples/suspense/src/struct_consumer.rs)は、このコンポーネントの使用方法を示しています。
|
||||
|
||||
## 関連例
|
||||
|
||||
- [プレースホルダー](https://github.com/yewstack/yew/tree/master/examples/suspense)
|
|
@ -1,126 +1,149 @@
|
|||
---
|
||||
title: Build a sample app
|
||||
title: 'サンプルアプリケーションの構築'
|
||||
---
|
||||
|
||||
はじめに、Rust の新規ライブラリを作りましょう(**重要:** `--lib`フラグを渡すことで*バイナリ*ではなく*ライブラリ*を作ってください)
|
||||
環境が整ったら、基本的な Yew アプリケーションに必要なテンプレートを使用するか、小さなプロジェクトを手動で設定することができます。
|
||||
|
||||
```bash
|
||||
cargo new --lib yew-app && cd yew-app
|
||||
## テンプレートを使用して迅速に開始
|
||||
|
||||
[`cargo-generate`](https://github.com/cargo-generate/cargo-generate) のインストール手順に従ってツールをインストールし、次のコマンドを実行します:
|
||||
|
||||
```shell
|
||||
cargo generate --git https://github.com/yewstack/yew-trunk-minimal-template
|
||||
```
|
||||
|
||||
依存ライブラリに`yew`と`wasm-bindgen`を追加してください \(最新バージョンについては[こちら](https://docs.rs/yew)を参照してください\)
|
||||
## 手動でアプリケーションを設定する
|
||||
|
||||
```toml title="Cargo.toml"
|
||||
### プロジェクトの作成
|
||||
|
||||
まず、新しい cargo プロジェクトを作成してください。
|
||||
|
||||
```bash
|
||||
cargo new yew-app
|
||||
```
|
||||
|
||||
新しく作成したディレクトリを開きます。
|
||||
|
||||
```bash
|
||||
cd yew-app
|
||||
```
|
||||
|
||||
### Hello World サンプルを実行する
|
||||
|
||||
Rust 環境が正しく設定されているかを確認するために、`cargo run` を使用して初期プロジェクトを実行します。"Hello World!" メッセージが表示されるはずです。
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
# output: Hello World!
|
||||
```
|
||||
|
||||
### プロジェクトを Yew Web アプリケーションに設定する
|
||||
|
||||
このシンプルなコマンドラインアプリケーションを基本的な Yew Web アプリケーションに変換するために、いくつかの変更が必要です。
|
||||
|
||||
#### Cargo.toml の更新
|
||||
|
||||
依存関係リストに `yew` を追加します。
|
||||
|
||||
```toml title=Cargo.toml
|
||||
[package]
|
||||
name = "yew-app"
|
||||
version = "0.1.0"
|
||||
authors = ["Yew App Developer <name@example.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
yew = "0.17"
|
||||
wasm-bindgen = "0.2"
|
||||
# 開発バージョンの Yew
|
||||
yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
|
||||
```
|
||||
|
||||
以下のテンプレートを `src/lib.rs`ファイルにコピーしてください:
|
||||
:::info
|
||||
|
||||
```rust title="src/lib.rs"
|
||||
use wasm_bindgen::prelude::*;
|
||||
アプリケーションを構築するだけの場合は、`csr` 特性のみが必要です。これにより、`Renderer` とクライアントサイドレンダリングに関連するすべてのコードが有効になります。
|
||||
|
||||
ライブラリを作成している場合は、この特性を有効にしないでください。クライアントサイドレンダリングロジックがサーバーサイドレンダリングパッケージに含まれることになります。
|
||||
|
||||
テストやサンプルのために Renderer が必要な場合は、`dev-dependencies` で有効にするべきです。
|
||||
|
||||
:::
|
||||
|
||||
#### main.rs の更新
|
||||
|
||||
テンプレートを生成し、クリック時に値を更新するボタンをレンダリングする `App` という名前のルートコンポーネントを設定する必要があります。以下のコードで `src/main.rs` の内容を置き換えます。
|
||||
|
||||
:::note
|
||||
`main` 関数内の `yew::Renderer::<App>::new().render()` 呼び出しは、アプリケーションを起動し、ページの `<body>` タグにマウントします。動的なプロパティを使用してアプリケーションを起動したい場合は、`yew::Renderer::<App>::with_props(..).render()` を使用できます。
|
||||
:::
|
||||
|
||||
```rust ,no_run, title=main.rs
|
||||
use yew::prelude::*;
|
||||
|
||||
struct Model {
|
||||
link: ComponentLink<Self>,
|
||||
value: i64,
|
||||
}
|
||||
|
||||
enum Msg {
|
||||
AddOne,
|
||||
}
|
||||
|
||||
impl Component for Model {
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
Self {
|
||||
link,
|
||||
value: 0,
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let counter = use_state(|| 0);
|
||||
let onclick = {
|
||||
let counter = counter.clone();
|
||||
move |_| {
|
||||
let value = *counter + 1;
|
||||
counter.set(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::AddOne => self.value += 1
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
||||
// Should only return "true" if new properties are different to
|
||||
// previously received properties.
|
||||
// This component has no properties so we will always return "false".
|
||||
false
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<button onclick={self.link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
|
||||
<p>{ self.value }</p>
|
||||
</div>
|
||||
}
|
||||
html! {
|
||||
<div>
|
||||
<button {onclick}>{ "+1" }</button>
|
||||
<p>{ *counter }</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run_app() {
|
||||
App::<Model>::new().mount_to_body();
|
||||
fn main() {
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
||||
```
|
||||
|
||||
このテンプレートはルートに`Component`をセットアップし、`Model`と呼ばれるクリックしたら更新するボタンを作ります。
|
||||
`main()`の中にある`App::<Model>::new().mount_to_body()`がアプリをスタートしてページの`<body>`タグをマウントすることに特に注意してください。
|
||||
動的なプロパティでアプリをスタートしたい場合は代わりに`App::<Model>::new().mount_to_body_with_props(..)`を使うことで実現できます。
|
||||
#### index.html の作成
|
||||
|
||||
最後に、アプリの中の`static`という名前のフォルダに`index.html`ファイルを追加してください。
|
||||
最後に、アプリケーションのルートディレクトリに `index.html` ファイルを追加します。
|
||||
|
||||
```bash
|
||||
mkdir static
|
||||
```
|
||||
|
||||
```markup title="index.html"
|
||||
```html , title=index.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Yew Sample App</title>
|
||||
<script type="module">
|
||||
import init from "./wasm.js"
|
||||
init()
|
||||
</script>
|
||||
<meta charset="utf-8" />
|
||||
<title>Yew App</title>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## アプリを動かす!
|
||||
## アプリケーションの表示
|
||||
|
||||
[`wasm-pack`](https://rustwasm.github.io/docs/wasm-pack/)を使うのがアプリを動かすのに推奨される方法です。
|
||||
まだ`wasm-pack`をインストールしていない場合、`cargo install wasm-pack`でインストールして開発サーバーを動かしてみましょう:
|
||||
以下のコマンドを実行して、ローカルでアプリケーションをビルドおよび提供します。
|
||||
|
||||
```bash
|
||||
wasm-pack build --target web --out-name wasm --out-dir ./static
|
||||
trunk serve
|
||||
```
|
||||
|
||||
`wasm-pack`はコンパイルされた WebAssembly と JavaScript ラッパーをまとめたものを`./static`ディレクトリに作り、
|
||||
アプリの WebAssembly バイナリを読み込んで動かします。
|
||||
:::info
|
||||
`--open` オプションを追加して、デフォルトのブラウザを開くことができます:`trunk serve --open`。
|
||||
:::
|
||||
|
||||
そして、`./static`以下で好きなサーバーをファイルをサーブしてみましょう。
|
||||
例えば:
|
||||
Trunk は、ソースコードファイルを変更するたびにアプリケーションをリアルタイムで再構築します。
|
||||
デフォルトでは、サーバーはアドレス '127.0.0.1' のポート '8080' でリッスンします => [http://localhost:8080](http://127.0.0.1:8080)。
|
||||
この設定を変更するには、次のファイルを作成して必要に応じて編集してください:
|
||||
|
||||
```bash
|
||||
cargo +nightly install miniserve
|
||||
miniserve ./static --index index.html
|
||||
```toml title="Trunk.toml"
|
||||
[serve]
|
||||
# ローカルネットワーク上のリッスンアドレス
|
||||
address = "127.0.0.1"
|
||||
# 広域ネットワーク上のリッスンアドレス
|
||||
# address = "0.0.0.0"
|
||||
# リッスンするポート
|
||||
port = 8000
|
||||
```
|
||||
|
||||
## おめでとうございます
|
||||
|
||||
これで、Yew 開発環境の設定が完了し、最初の Web アプリケーションを構築できました。
|
||||
|
||||
このアプリケーションを試してみて、さらに学習するために[サンプル](./examples.mdx)を参照してください。
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
title: 'エディタの設定'
|
||||
description: 'コードエディタの設定'
|
||||
---
|
||||
|
||||
:::important 改善ドキュメント
|
||||
異なるエディタを使用していますか?おすすめがあれば、選択したエディタの説明を自由に追加してください。
|
||||
:::
|
||||
|
||||
## コンポーネント作成のためのテンプレートを追加
|
||||
|
||||
### JetBrains IDEs
|
||||
|
||||
1. ナビゲーションバーから順に File | Settings | Editor | Live Templates をクリックします。
|
||||
2. Rust を選択し、+ アイコンをクリックして新しい Live Template を追加します。
|
||||
3. 必要に応じて名前と説明を入力します。
|
||||
4. 以下のコードスニペットをテンプレートテキスト部分に貼り付けます。
|
||||
5. 右下の適用範囲を変更し、Rust > Item > Module を選択します。
|
||||
|
||||
関数型コンポーネントの場合、以下のテンプレートを使用します。
|
||||
|
||||
- (オプション) 変数を編集し、`tag` に適切なデフォルト値(例:"div")を設定します。
|
||||
|
||||
```rust ,ignore
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct $Name$Props {
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn $Name$(props: &$Name$Props) -> Html {
|
||||
html! {
|
||||
<$tag$>$END$</$tag$>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
構造体コンポーネントの場合、以下のより複雑なテンプレートを使用できます。
|
||||
|
||||
```rust ,ignore
|
||||
struct $NAME$;
|
||||
|
||||
enum $NAME$Msg {
|
||||
}
|
||||
|
||||
impl Component for $NAME$ {
|
||||
type Message = $NAME$Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
$HTML$
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### VS Code
|
||||
|
||||
1. ナビゲーションバーから順に File > Preferences > User Snippets をクリックします。
|
||||
2. 設定言語として Rust を選択します。
|
||||
3. 以下の JSON ファイルにコードスニペットを追加します。
|
||||
|
||||
````json
|
||||
{
|
||||
"New Yew function component": {
|
||||
"prefix": "yewfc",
|
||||
"body": [
|
||||
"#[derive(PartialEq, Properties)]",
|
||||
"pub struct ${1:ComponentName}Props {}",
|
||||
"",
|
||||
"#[function_component]",
|
||||
"pub fn $1(props: &${1}Props) -> Html {",
|
||||
" let ${1}Props {} = props;",
|
||||
" html! {",
|
||||
" <${2:div}>$0</${2}>",
|
||||
" }",
|
||||
"}"
|
||||
],
|
||||
"description": "Create a minimal Yew function component"
|
||||
},
|
||||
"New Yew struct component": {
|
||||
"prefix": "yewsc",
|
||||
"body": [
|
||||
"pub struct ${1:ComponentName};",
|
||||
"",
|
||||
"pub enum ${1}Msg {",
|
||||
"}",
|
||||
"",
|
||||
"impl Component for ${1} {",
|
||||
" type Message = ${1}Msg;",
|
||||
" type Properties = ();",
|
||||
"",
|
||||
" fn create(ctx: &Context<Self>) -> Self {",
|
||||
" Self",
|
||||
" }",
|
||||
"",
|
||||
" fn view(&self, ctx: &Context<Self>) -> Html {",
|
||||
" html! {",
|
||||
" $0",
|
||||
" }",
|
||||
" }",
|
||||
"}"
|
||||
],
|
||||
"description": "Create a new Yew component with a message enum"
|
||||
}
|
||||
}
|
||||
## `html!` マクロのサポート
|
||||
|
||||
### JetBrains IDEs
|
||||
|
||||
JetBrains は 2021 年 4 月にプロシージャルマクロ拡張の実験的サポートを追加しました。
|
||||
この機能を有効にする必要があります。
|
||||
[詳細については、このブログを参照してください。](https://blog.jetbrains.com/rust/2021/04/08/intellij-rust-updates-for-2021-1/#proc-macros)
|
||||
|
||||
これにより、HTML の自動補完やフォーマット支援は有効になりませんが、マクロ内のコンポーネント名やプロパティの変数解析が有効になります。
|
||||
リネーム、宣言へのジャンプ、使用箇所の検索などのツールがマクロ内で動作します。
|
||||
|
||||
### VS Code
|
||||
|
||||
#### Rust-Yew 拡張機能
|
||||
|
||||
> これは**進行中の**、**コミュニティが維持している**プロジェクトです
|
||||
|
||||
Rust-Yew 拡張機能は [VSC Marketplace で見つけることができます](https://marketplace.visualstudio.com/items?itemName=TechTheAwesome.rust-yew)、シンタックスハイライト、リネーム、ホバーなどの機能を提供します。
|
||||
|
||||
Emmet サポートは直接使用できるはずですが、できない場合は `settings.json` ファイルを編集してください:
|
||||
|
||||
```json
|
||||
"emmet.includeLanguages": {
|
||||
"rust": "html",
|
||||
}
|
||||
````
|
||||
|
||||
### Neovim
|
||||
|
||||
#### Lazyvim
|
||||
|
||||
> 以下の設定は [LazyVim](https://www.lazyvim.org) 設定および lazy.vim プラグインに適用されます。`lua/plugins/nvim-lspconfig.lua` にファイルを作成するか、既存の `lspconfig` を更新してください:
|
||||
|
||||
```json
|
||||
return {
|
||||
{
|
||||
"neovim/nvim-lspconfig",
|
||||
init_options = {
|
||||
userLanguages = {
|
||||
eelixir = "html-eex",
|
||||
eruby = "erb",
|
||||
rust = "html",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
|
@ -1,14 +1,17 @@
|
|||
---
|
||||
title: Examples
|
||||
title: '例'
|
||||
---
|
||||
|
||||
Yew のリポジトリは[例](https://github.com/yewstack/yew/tree/v0.17/examples)がたくさんあります
|
||||
\(メンテナンス状況は様々\)。
|
||||
様々なフレームワークの機能の使い方を知るのにはそれらの例に取り組むのを勧めます。
|
||||
プルリクエストや Issue はウェルカムです。
|
||||
Yew リポジトリには多くの[例](メンテナンス状態はさまざま)があります。
|
||||
フレームワークのさまざまな機能を理解するために、それらを参照することをお勧めします。
|
||||
無視されがちで助けが必要な場合に備えて、プルリクエストや問題も歓迎します ♥️。
|
||||
|
||||
- [**Todo アプリ** ](https://github.com/yewstack/yew/tree/v0.17/examples/todomvc)
|
||||
- [**カスタムコンポーネント**](https://github.com/yewstack/yew/tree/v0.17/examples/custom_components)
|
||||
- [**マルチスレッド\(エージェント\)**](https://github.com/yewstack/yew/tree/v0.17/examples/multi_thread)
|
||||
- [**タイマーサービス**](https://github.com/yewstack/yew/tree/v0.17/examples/timer)
|
||||
- [**ネストしたコンポーネント**](https://github.com/yewstack/yew/tree/v0.16.0/examples/nested_list)
|
||||
詳細については、[README] を参照してください。
|
||||
|
||||
:::note
|
||||
ほとんどの例には、https://examples.yew.rs/< example_name > で見つけることができるオンラインデプロイがあります。
|
||||
それぞれのサブフォルダーの README ページでバッジをクリックして、オンラインデモに移動します。
|
||||
:::
|
||||
|
||||
[例のリスト]: https://github.com/yewstack/yew/tree/master/examples
|
||||
[例のドキュメント README]: https://github.com/yewstack/yew/tree/master/examples#yew-examples
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: '始めに'
|
||||
---
|
||||
|
||||
Yew アプリケーションをコンパイル、ビルド、パッケージ、およびデバッグするためにいくつかのツールが必要です。最初に、[Trunk](https://trunkrs.dev/) を使用することをお勧めします。Trunk は Rust 用の WASM Web アプリケーションパッケージツールです。
|
||||
|
||||
## Rust のインストール
|
||||
|
||||
Rust をインストールするには、[公式の手順](https://www.rust-lang.org/tools/install) に従ってください。
|
||||
|
||||
:::important
|
||||
Yew がサポートする最低 Rust バージョン(MSRV)は `1.76.0` です。古いバージョンではコンパイルできません。`rustup show`(「active toolchain」の下)または `rustc --version` を使用してツールチェーンのバージョンを確認できます。ツールチェーンを更新するには、`rustup update` を実行してください。
|
||||
:::
|
||||
|
||||
## WebAssembly ターゲットのインストール
|
||||
|
||||
Rust は異なる「ターゲット」(例えば異なるプロセッサ)に対してソースコードをコンパイルできます。ブラウザベースの WebAssembly 用のコンパイルターゲットは `wasm32-unknown-unknown` と呼ばれます。以下のコマンドは、開発環境に WebAssembly ターゲットを追加します。
|
||||
|
||||
```shell
|
||||
rustup target add wasm32-unknown-unknown
|
||||
```
|
||||
|
||||
## Trunk のインストール
|
||||
|
||||
Trunk は、デプロイとパッケージ管理に推奨されるツールであり、ドキュメントやサンプル全体で使用されています。
|
||||
|
||||
```shell
|
||||
# 注意:これはすべての内容をゼロからコンパイルするため、インストールに時間がかかる場合があります
|
||||
# Trunk は多くの主要なパッケージマネージャーに対して事前構築されたバイナリを提供しています
|
||||
# 詳細については、https://trunkrs.dev/#install を参照してください
|
||||
cargo install --locked trunk
|
||||
```
|
||||
|
||||
### 他のオプション
|
||||
|
||||
Trunk の他にも、Yew アプリケーションをパッケージ化するための他のオプションがあります。以下のオプションのいずれかを試してみることをお勧めします:
|
||||
|
||||
- [`wasm-pack`](https://rustwasm.github.io/wasm-pack/)
|
||||
- [`wasm-run`](https://github.com/IMI-eRnD-Be/wasm-run)
|
||||
- [`xtask-wasm`](https://github.com/rustminded/xtask-wasm/)(まだ初期開発段階です)
|
||||
|
||||
## 次のステップ
|
||||
|
||||
開発環境の設定が完了したら、ドキュメントの読み進めを続けることができます。実践を通じて学ぶのが好きな方は、[チュートリアル](../tutorial)をチェックすることをお勧めします。
|
|
@ -1,18 +1,27 @@
|
|||
---
|
||||
title: CSS
|
||||
title: 'CSS'
|
||||
---
|
||||
|
||||
# CSS
|
||||
Yew に CSS サポートを統合する最良の方法についての議論は、こちらで見つけることができます:[https://github.com/yewstack/yew/issues/533](https://github.com/yewstack/yew/issues/533)
|
||||
|
||||
<TODO>
|
||||
ここには、Yew に CSS サポートを統合する最良の方法についての多くの議論が含まれています。
|
||||
|
||||
統合的な CSS サポートについての提案はこちらにあります: [https://github.com/yewstack/yew/issues/533](https://github.com/yewstack/yew/issues/533)
|
||||
現在、私たちが採用している方法は、開発者が最も人気のあるシステムを採用する前に多くのシステムを構築することを奨励することです。
|
||||
|
||||
## スタイルフレームワーク:
|
||||
コミュニティは現在、プロジェクトにスタイルを追加するためのいくつかのプロジェクトを開発しています。以下はその一部です:
|
||||
|
||||
今のところ、コミュニティメンバーは以下のスタイルフレームワークを開発しています。
|
||||
#### コンポーネントライブラリ
|
||||
|
||||
- [yew_styles](https://github.com/spielrs/yew_styles) - JavaScript に依存しない Yew のスタイルフレームワーク
|
||||
- [yew-mdc](https://github.com/Follpvosten/yew-mdc) - マテリアルデザインのコンポーネント
|
||||
- [muicss-yew](https://github.com/AlephAlpha/muicss-yew) - MUI の CSS コンポーネント
|
||||
- [Yewtify](https://github.com/yewstack/yewtify) – Yew で Vuetify フレームワークで提供されている機能の実装
|
||||
- [yew_styles](https://github.com/spielrs/yew_styles) - JavaScript 依存なしの Yew スタイルフレームワーク。
|
||||
- [yew-mdc](https://github.com/Follpvosten/yew-mdc) - マテリアルデザインコンポーネント。
|
||||
- [muicss-yew](https://github.com/AlephAlpha/muicss-yew) - MUI CSS コンポーネント。
|
||||
- [Yewtify](https://github.com/yewstack/yewtify) – Yew で Vuetify フレームワークの機能を実現。
|
||||
|
||||
#### スタイルソリューション
|
||||
|
||||
- [stylist](https://github.com/futursolo/stylist-rs) - WebAssembly アプリケーション用の CSS-in-Rust スタイルソリューション。
|
||||
- [tailwind-css](https://github.com/thedodd/trunk/tree/master/examples/yew-tailwindcss) - Tailwind ユーティリティクラス。
|
||||
|
||||
:::important ドキュメントの改善
|
||||
Yew にスタイルを追加するプロジェクトを開発している場合は、このリストに自分を追加する PR を提出してください!
|
||||
:::
|
||||
|
|
|
@ -1,58 +1,98 @@
|
|||
---
|
||||
title: Debugging
|
||||
title: 'デバッグ'
|
||||
---
|
||||
|
||||
# デバッグ
|
||||
## パニック (Panics)
|
||||
|
||||
## パニック
|
||||
Yew はブラウザのコンソールにパニックログを自動的に出力します。
|
||||
|
||||
Rust シンボルで良いスタックトレースをするには
|
||||
[`console_error_panic`](https://github.com/rustwasm/console_error_panic_hook)クレートを使用してください。
|
||||
注意として、`cargo-web`でビルドされたものとは互換性がありません。
|
||||
## コンソールログ
|
||||
|
||||
## コンソールでのログ
|
||||
|
||||
一般的に、Wasm の Web アプリはブラウザの API と連携することができ、`console.log`の API も例外ではありません。
|
||||
いつくかの選択肢があります:
|
||||
JavaScript では、`console.log()` を使用してブラウザのコンソールに出力します。以下は Yew のいくつかのオプションです。
|
||||
|
||||
### [`wasm-logger`](https://crates.io/crates/wasm-logger)
|
||||
|
||||
このクレートは Rust の`log`クレートと親和性があります。
|
||||
`wasm-logger` クレートは [`log`](https://crates.io/crates/log) クレートと統合されており、ログレベル、ソース行、ファイル名をブラウザのコンソールに送信します。
|
||||
|
||||
```rust ,ignore
|
||||
use log::info;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
```rust
|
||||
// セットアップ
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::default());
|
||||
|
||||
let object = JsValue::from("world");
|
||||
info!("Hello {}", object.as_string().unwrap());
|
||||
}
|
||||
|
||||
// 使用方法
|
||||
log::info!("Update: {:?}", msg);
|
||||
```
|
||||
|
||||
### [`ConsoleService`](https://docs.rs/yew/latest/yew/services/console/struct.ConsoleService.html)
|
||||
### [`gloo-console`](https://crates.io/crates/gloo-console)
|
||||
|
||||
このサービスは Yew に含まれており、`"services"`の機能が有効化されている場合は利用可能です。
|
||||
このクレートは Gloo の一部で、ブラウザ API の Rust ラッパーを提供します。`log!` マクロは `JsValue` を直接受け入れることができ、`wasm_logger` よりも使いやすいです。
|
||||
|
||||
```rust
|
||||
// 使用方法
|
||||
ConsoleService::info(format!("Update: {:?}", msg).as_ref());
|
||||
```rust ,ignore
|
||||
use gloo_console::log;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
fn main() {
|
||||
let object = JsValue::from("world");
|
||||
log!("Hello", object)
|
||||
}
|
||||
```
|
||||
|
||||
## ソースマップ
|
||||
### [`tracing-web`](https://crates.io/crates/tracing-web)
|
||||
|
||||
今のところは Rust/Wasm の Web アプリにはソースマップへの第一級のサポートがありません。
|
||||
もちろん、これは変更される可能性があります。これが当てはまらない場合、または進捗が見られる場合は、変更を提案してください!
|
||||
`tracing-web` は [`tracing-subscriber`](https://crates.io/crates/tracing-subscriber) と一緒に使用でき、メッセージをブラウザのコンソールに出力します。
|
||||
|
||||
### 最新情報
|
||||
```rust ,ignore
|
||||
use tracing_subscriber::{
|
||||
fmt::{
|
||||
format::{FmtSpan, Pretty},
|
||||
time::UtcTime,
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
\[2019 年 12 月\] [Chrome DevTools update](https://developers.google.com/web/updates/2019/12/webassembly#the_future)
|
||||
fn main() {
|
||||
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||
.with_ansi(false)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.with_writer(tracing_web::MakeConsoleWriter)
|
||||
.with_span_events(FmtSpan::ACTIVE);
|
||||
let perf_layer = tracing_web::performance_layer().with_details_from_fields(Pretty::default());
|
||||
|
||||
> やらなければいけないことがまだたくさんあります。例えばツール側では Emscripten\(Binaryen\)と wasm-pack\(wasm-bindgen\)がそれらが実行する変換に関する DWARF 情報の更新をまだサポートしていません。
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt_layer)
|
||||
.with(perf_layer)
|
||||
.init();
|
||||
let object = JsValue::from("world");
|
||||
tracing::info!("Hello {}", object.as_string().unwrap());
|
||||
}
|
||||
```
|
||||
|
||||
\[2020\] [Rust Wasm デバッグガイド](https://rustwasm.github.io/book/reference/debugging.html#using-a-debugger)
|
||||
## コンポーネントライフサイクルのデバッグ
|
||||
|
||||
> 残念なことに、WebAssembly のデバッグの物語はまだ未成熟です。ほとんどの Unix のシステムでは[DWARF](http://dwarfstd.org/)は実行中のプログラムをソースレベルで検査するためにデバッガに必要な情報をエンコードするために使用されます。Windows には同様の情報をエンコードする代替形式があります。現在、WebAssembly に相当するものはありません。
|
||||
[`tracing`](https://crates.io/crates/tracing) は、コンポーネントのライフサイクルに関連するイベント情報を収集するために使用できます。`tracing` には `log` サポートの機能フラグもあり、`wasm-logger` とうまく統合できます。
|
||||
|
||||
\[2019\] [Rust Wasm ロードマップ](https://rustwasm.github.io/rfcs/007-2019-roadmap.html#debugging)
|
||||
[コンパイル時フィルタ](https://docs.rs/tracing/latest/tracing/level_filters/index.html#compile-time-filters) は、詳細度を調整したりログ記録を無効にしたりするために使用できます。これにより、より小さな Wasm ファイルが生成されるはずです。
|
||||
|
||||
> デバッグはトリッキーです。なぜなら、多くの話はこの活動チームの手の届かないところにあり、WebAssembly の標準化団体とブラウザ開発者ツールを実装している人たちの両方に依存しているからです。
|
||||
## ソースマップ (Source Maps)
|
||||
|
||||
[ソースマップ](https://developer.chrome.com/blog/wasm-debugging-2019/#enter-dwarf) をサポートするいくつかの方法がありますが、いくつかの設定が必要です。
|
||||
|
||||
## 過去の記事
|
||||
|
||||
以下は、Rust における WebAssembly デバッグの現状に関する過去の記事です。興味深い読み物かもしれません。
|
||||
|
||||
\[2019 年 12 月\] [Chrome DevTools 更新](https://developers.google.com/web/updates/2019/12/webassembly#the_future)
|
||||
|
||||
> これらの作業にはまだ多くのことが残されています。例えば、ツールの面では、Emscripten(Binaryen)と wasm-pack(wasm-bindgen)は、それらが実行する変換に対して DWARF 情報を更新することをまだサポートしていません。
|
||||
|
||||
\[2020 年\] [Rust Wasm デバッグガイド](https://rustwasm.github.io/book/reference/debugging.html#using-a-debugger)
|
||||
|
||||
> 残念ながら、WebAssembly のデバッグ機能はまだ未成熟です。ほとんどの Unix システムでは、[DWARF](http://dwarfstd.org/) が実行中のプログラムのソースレベルの検査に必要な情報をエンコードするために使用されますが、Windows では同様の情報をエンコードする代替フォーマットがあります。しかし、現在のところ、WebAssembly には対応するフォーマットがありません。
|
||||
|
||||
\[2019 年\] [Rust Wasm ロードマップ](https://rustwasm.github.io/rfcs/007-2019-roadmap.html#debugging)
|
||||
|
||||
> デバッグは難しいです。なぜなら、多くの状況がこの作業グループの管理下にないからです。これは、WebAssembly の標準化機関やブラウザ開発者ツールを実装する人々に依存しています。
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
title: 'デプロイ'
|
||||
description: 'Yew アプリケーションのデプロイ'
|
||||
---
|
||||
|
||||
Yew アプリケーションをサーバーにデプロイする準備ができたら、いくつかのデプロイオプションがあります。
|
||||
|
||||
`trunk build --release` は、リリースモードでアプリケーションをビルドします。HTTP サーバーを設定して、サイトにアクセスしたときに `index.html` を提供し、静的パス(例:`index_<hash>.js` および `index_bg_<hash>.wasm`)のリクエストに対して trunk が生成した dist ディレクトリから適切なコンテンツを提供する必要があります。
|
||||
|
||||
:::important `trunk serve --release` について
|
||||
`trunk serve --release` を使用してアプリケーションを提供しないでください。
|
||||
これは開発中にリリースビルドをテストするためだけに使用されるべきです。
|
||||
:::
|
||||
|
||||
## サーバー設定
|
||||
|
||||
### `index.html` をフォールバックとして提供する
|
||||
|
||||
アプリケーションが [Yew ルーター](concepts/router.mdx) を使用している場合、存在しないファイルへのリクエスト時にサーバーが `index.html` を返すように設定する必要があります。
|
||||
|
||||
Yew ルーターを使用するアプリケーションは [シングルページアプリケーション (SPA)](https://developer.mozilla.org/en-US/docs/Glossary/SPA) として構築されています。ユーザーが実行中のクライアントから URL にナビゲートすると、ルーターが URL を解釈してそのページにルーティングします。
|
||||
|
||||
しかし、ページをリフレッシュしたり、アドレスバーに URL を入力したりすると、これらの操作は実行中のアプリケーションではなく、ブラウザー自体によって処理されます。ブラウザーはその URL を直接サーバーにリクエストし、ルーターをバイパスします。誤って設定されたサーバーは 404 - 見つかりません 状態を返します。
|
||||
|
||||
`index.html` を返すことで、アプリケーションは通常通りにロードされ、ルーターがルート `/show/42` を認識して適切なコンテンツを表示するまで、リクエストが `/` であるかのように動作します。
|
||||
|
||||
### Web Assembly リソースに正しい MIME タイプを設定する
|
||||
|
||||
WASM ファイルは `application/wasm` MIME タイプで [Content-Type ヘッダー](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) を設定する必要があります。
|
||||
|
||||
ほとんどのサーバーとホスティングサービスはデフォルトでこれを行います。サーバーがこれを行わない場合は、そのドキュメントを参照してください。ほとんどの Web ブラウザーでは、誤った MIME タイプは次のようなエラーを引き起こします:
|
||||
|
||||
```ignore
|
||||
`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:
|
||||
TypeError: WebAssembly: Response has unsupported MIME type 'text/plain' expected 'application/wasm'
|
||||
```
|
||||
|
||||
## 相対パスのビルド
|
||||
|
||||
デフォルトでは、trunk はサイトが `/` で提供されると仮定し、それに応じてサイトをビルドします。この動作は、`index.html` ファイルに `<base data-trunk-public-url />` を追加することで上書きできます。Trunk はこのタグを書き換えて、`--public-url` に渡された値を含めます。Yew ルーターは `<base />` の存在を自動的に検出し、適切に処理します。
|
||||
|
||||
## 環境変数を使用して動作をカスタマイズする
|
||||
|
||||
通常、環境変数を使用してビルド環境をカスタマイズします。アプリケーションがブラウザで実行されるため、実行時に環境変数を読み取ることはできません。
|
||||
[`std::env!`](https://doc.rust-lang.org/std/macro.env.html) マクロは、コンパイル時に環境変数の値を取得できます。
|
|
@ -1,45 +1,44 @@
|
|||
---
|
||||
title: Roadmap
|
||||
description: The planned feature roadmap for the Yew framework
|
||||
title: 'ロードマップ'
|
||||
description: 'Yew フレームワークの計画機能ロードマップ'
|
||||
---
|
||||
|
||||
# ロードマップ
|
||||
|
||||
## 優先順位
|
||||
|
||||
フレームワークの今後の機能やフォーカスの優先順位は、コミュニティによって決定されます。2020 年の春には、プロジェクトの方向性についてのフィードバックを集めるために開発者アンケートが行われました。その概要は [Yew Wiki](https://github.com/yewstack/yew/wiki/Dev-Survey-%5BSpring-2020%5D) で見ることができます。
|
||||
フレームワークの今後の機能と重点の優先順位はコミュニティによって決定されます。
|
||||
2020 年春に、プロジェクトの方向性に関するフィードバックを収集するために開発者調査を実施しました。
|
||||
調査の概要は [Yew Wiki](https://github.com/yewstack/yew/wiki/Dev-Survey-%5BSpring-2020%5D) で確認できます。
|
||||
|
||||
:::note
|
||||
主要な取り組みの状況は、Yew の Github の[Project board](https://github.com/yewstack/yew/projects)で確認できます。
|
||||
すべての主要なイニシアチブのステータスは Yew Github の [プロジェクトボード](https://github.com/yewstack/yew/projects) で追跡できます。
|
||||
:::
|
||||
|
||||
## 焦点
|
||||
## 重点
|
||||
|
||||
1. Top Requested Features
|
||||
2. Production Readiness
|
||||
3. Documentation
|
||||
4. Pain Points
|
||||
1. 最も人気のある機能
|
||||
2. プロダクションレディ
|
||||
3. ドキュメント
|
||||
4. 痛点
|
||||
|
||||
### Top Requested Features
|
||||
### 最も人気のある機能
|
||||
|
||||
1. [関数型コンポーネント](https://github.com/yewstack/yew/projects/3)
|
||||
2. [Component ライブラリ](https://github.com/yewstack/yew/projects/4)
|
||||
1. [関数コンポーネント](https://github.com/yewstack/yew/projects/3)
|
||||
2. [コンポーネントライブラリ](https://github.com/yewstack/yew/projects/4)
|
||||
3. より良い状態管理
|
||||
4. [サーバーサイドでのレンダリング](https://github.com/yewstack/yew/projects/5)
|
||||
4. [サーバーサイドレンダリング](https://github.com/yewstack/yew/projects/5)
|
||||
|
||||
### Production Readiness
|
||||
### プロダクションレディに必要な問題
|
||||
|
||||
- テストカバレッジの向上
|
||||
- バイナリサイズ
|
||||
- [ベンチマークのパフォーマンス](https://github.com/yewstack/yew/issues/5)
|
||||
- Yew のテストカバレッジを向上させる
|
||||
- バイナリサイズを小さくする
|
||||
- [パフォーマンスベンチマーク](https://github.com/yewstack/yew/issues/5)
|
||||
|
||||
### Documentation
|
||||
### ドキュメント
|
||||
|
||||
- チュートリアルを作る
|
||||
- プロジェクトのセットアップをシンプルにする
|
||||
- チュートリアルを作成する
|
||||
- プロジェクト設定を簡素化する
|
||||
|
||||
### Pain Points
|
||||
### 痛点
|
||||
|
||||
- [Component のボイラープレート](https://github.com/yewstack/yew/issues/830)
|
||||
- Fetch API
|
||||
- [コンポーネントテンプレート](https://github.com/yewstack/yew/issues/830)
|
||||
- [エージェント](https://github.com/yewstack/yew/projects/6)
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
---
|
||||
title: Testing apps
|
||||
description: Testing your app
|
||||
title: 'アプリケーションのテスト'
|
||||
description: 'アプリケーションをテストする'
|
||||
---
|
||||
|
||||
# Testing
|
||||
:::info
|
||||
コンポーネントのテストをより簡単にするために努力していますが、現在進行中です。
|
||||
|
||||
<TODO>
|
||||
[浅いレンダリング](https://github.com/yewstack/yew/issues/1413) のサポートは GitHub リポジトリで見つけることができます。
|
||||
:::
|
||||
|
||||
## スナップショットテスト
|
||||
|
||||
Yew はコンポーネントのスナップショットテストを容易にするために `yew::tests::layout_tests` モジュールを提供しています。
|
||||
|
||||
:::important ドキュメントの改善
|
||||
スナップショットテストのドキュメントを改善するための助けが必要です。
|
||||
:::
|
||||
|
||||
## wasm_bindgen_test
|
||||
|
||||
Rust Wasm ワーキンググループは wasm_bindgen_test というフレームワークをメンテナンスしており、組み込みの #[test] プロシージャルマクロの動作と同様の方法でブラウザでテストを実行することができます。詳細は、[Rust Wasm 活動グループのドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html)に記載されています。
|
||||
Rust/WASM ワーキンググループは [`wasm_bindgen_test`](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html) というクレートを維持しています。
|
||||
これにより、組み込みの `#[test]` プロシージャマクロに似た方法でブラウザ内でテストを実行できます。
|
||||
このモジュールの詳細については、[Rust Wasm ワーキンググループのドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/wasm-bindgen-test/index.html) を参照してください。
|
||||
|
|
|
@ -0,0 +1,584 @@
|
|||
---
|
||||
title: 'チュートリアル'
|
||||
slug: /tutorial
|
||||
---
|
||||
|
||||
## 紹介
|
||||
|
||||
この実践チュートリアルでは、Yew を使用して Web アプリケーションを構築する方法を学びます。
|
||||
**Yew** は、[WebAssembly](https://webassembly.org/) を使用してフロントエンド Web アプリケーションを構築するためのモダンな [Rust](https://www.rust-lang.org/) フレームワークです。
|
||||
Yew は Rust の強力な型システムを活用し、再利用可能で保守しやすく、良好に構造化されたアーキテクチャを奨励します。
|
||||
Rust の [crates](https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html) と呼ばれるライブラリのエコシステムは、状態管理などの一般的なパターンのためのコンポーネントを提供します。
|
||||
Rust のパッケージマネージャー [Cargo](https://doc.rust-lang.org/cargo/) を使用すると、Yew などの多くの crate を [crates.io](https://crates.io) から利用できます。
|
||||
|
||||
### 構築する内容
|
||||
|
||||
Rustconf は、Rust コミュニティが毎年開催する星間集会です。
|
||||
Rustconf 2020 には多くの講演があり、大量の情報が提供されました。
|
||||
この実践チュートリアルでは、他の Rustaceans がこれらの講演を理解し、1つのページから視聴できるようにする Web アプリケーションを構築します。
|
||||
|
||||
## セットアップ
|
||||
|
||||
### 前提条件
|
||||
|
||||
このチュートリアルは、Rust に精通していることを前提としています。Rust の初心者である場合、無料の [Rust 本](https://doc.rust-lang.org/book/ch00-00-introduction.html) は初心者にとって素晴らしい出発点であり、経験豊富な Rust 開発者にとっても優れたリソースです。
|
||||
|
||||
最新バージョンの Rust がインストールされていることを確認するには、`rustup update` を実行するか、[Rust をインストール](https://www.rust-lang.org/tools/install) します。
|
||||
|
||||
Rust をインストールした後、Cargo を使用して以下のコマンドを実行し、`trunk` をインストールします:
|
||||
|
||||
```bash
|
||||
cargo install trunk
|
||||
```
|
||||
|
||||
WASM のビルドターゲットも追加する必要があります。次のコマンドを実行します:
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-unknown-unknown
|
||||
```
|
||||
|
||||
### プロジェクトの設定
|
||||
|
||||
まず、新しい cargo プロジェクトを作成します:
|
||||
|
||||
```bash
|
||||
cargo new yew-app
|
||||
cd yew-app
|
||||
```
|
||||
|
||||
Rust 環境が正しく設定されていることを確認するために、cargo ビルドツールを使用して初期プロジェクトを実行します。
|
||||
ビルドプロセスの出力に続いて、期待される "Hello, world!" メッセージが表示されるはずです。
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
## 最初の静的ページ
|
||||
|
||||
このシンプルなコマンドラインアプリケーションを基本的な Yew Web アプリケーションに変換するために、いくつかの変更が必要です。
|
||||
|
||||
```toml title="Cargo.toml" {7}
|
||||
[package]
|
||||
name = "yew-app"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
アプリケーションを構築するだけの場合は、`csr` 特性のみが必要です。これにより、`Renderer` とクライアントサイドレンダリングに関連するすべてのコードが有効になります。
|
||||
|
||||
ライブラリを作成している場合は、この特性を有効にしないでください。クライアントサイドレンダリングロジックがサーバーサイドレンダリングパッケージに含まれてしまいます。
|
||||
|
||||
テストやサンプルのために Renderer が必要な場合は、`dev-dependencies` で有効にするべきです。
|
||||
|
||||
:::
|
||||
|
||||
```rust ,no_run title="src/main.rs"
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
html! {
|
||||
<h1>{ "Hello World" }</h1>
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
||||
```
|
||||
|
||||
それでは、プロジェクトのルートディレクトリに `index.html` を作成しましょう。
|
||||
|
||||
```html title="index.html"
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head></head>
|
||||
<body></body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 開発サーバーの起動
|
||||
|
||||
以下のコマンドを実行して、アプリケーションをビルドし、ローカルで提供します。
|
||||
|
||||
```bash
|
||||
trunk serve --open
|
||||
```
|
||||
|
||||
:::info
|
||||
`--open` オプションを削除して、`trunk serve` を実行した後にデフォルトのブラウザを開かないようにします。
|
||||
:::
|
||||
|
||||
Trunk は、ソースコードファイルを変更するたびにアプリケーションをリアルタイムで再構築します。
|
||||
デフォルトでは、サーバーはアドレス '127.0.0.1' のポート '8080' でリッスンします => [http://localhost:8080](http://127.0.0.1:8080)。
|
||||
この設定を変更するには、次のファイルを作成して必要に応じて編集します:
|
||||
|
||||
```toml title="Trunk.toml"
|
||||
[serve]
|
||||
# ローカルネットワーク上のリッスンアドレス
|
||||
address = "127.0.0.1"
|
||||
# 広域ネットワーク上のリッスンアドレス
|
||||
# address = "0.0.0.0"
|
||||
# リッスンするポート
|
||||
port = 8000
|
||||
```
|
||||
|
||||
もし興味があれば、`trunk help` および `trunk help <subcommand>` を実行して、進行中のプロセスの詳細についてさらに学ぶことができます。
|
||||
|
||||
### おめでとうございます
|
||||
|
||||
これで、Yew 開発環境を正常にセットアップし、最初の Yew Web アプリケーションを構築しました。
|
||||
|
||||
## HTML の構築
|
||||
|
||||
Yew は Rust のプロシージャルマクロを利用しており、JSX(JavaScript の拡張で、JavaScript 内で HTML に似たコードを書くことができる)に似た構文を提供して、マークアップを作成します。
|
||||
|
||||
### クラシック HTML への変換
|
||||
|
||||
私たちのウェブサイトがどのように見えるかについての良いアイデアが既にあるので、単純にドラフトを `html!` と互換性のある表現に変換することができます。シンプルな HTML を書くことに慣れているなら、`html!` でマークアップを書くのに問題はないはずです。このマクロは HTML といくつかの違いがあることに注意してください:
|
||||
|
||||
1. 式は中括弧(`{ }`)で囲む必要があります。
|
||||
2. ルートノードは1つだけでなければなりません。コンテナにラップせずに複数の要素を持ちたい場合は、空のタグ/フラグメント(`<> ... </>`)を使用できます。
|
||||
3. 要素は正しく閉じる必要があります。
|
||||
|
||||
レイアウトを構築したいので、元の HTML は次のようになります:
|
||||
|
||||
```html
|
||||
<h1>RustConf Explorer</h1>
|
||||
<div>
|
||||
<h3>Videos to watch</h3>
|
||||
<p>John Doe: Building and breaking things</p>
|
||||
<p>Jane Smith: The development process</p>
|
||||
<p>Matt Miller: The Web 7.0</p>
|
||||
<p>Tom Jerry: Mouseless development</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>John Doe: Building and breaking things</h3>
|
||||
<img
|
||||
src="https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder"
|
||||
alt="video thumbnail"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
それでは、この HTML を `html!` に変換しましょう。次のコードスニペットを `app` 関数の本体に入力(またはコピー/ペースト)して、関数が `html!` の値を返すようにします。
|
||||
|
||||
```rust ,ignore
|
||||
html! {
|
||||
<>
|
||||
<h1>{ "RustConf Explorer" }</h1>
|
||||
<div>
|
||||
<h3>{"Videos to watch"}</h3>
|
||||
<p>{ "John Doe: Building and breaking things" }</p>
|
||||
<p>{ "Jane Smith: The development process" }</p>
|
||||
<p>{ "Matt Miller: The Web 7.0" }</p>
|
||||
<p>{ "Tom Jerry: Mouseless development" }</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>{ "John Doe: Building and breaking things" }</h3>
|
||||
<img src="https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder" alt="video thumbnail" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
```
|
||||
|
||||
ブラウザページをリフレッシュすると、次の出力が表示されるはずです:
|
||||
|
||||

|
||||
|
||||
### マークアップ内でRustの構造を使用する
|
||||
|
||||
Rustでマークアップを書く大きな利点の1つは、マークアップ内でRustのすべての利点を享受できることです。
|
||||
今では、HTML内にビデオリストをハードコーディングするのではなく、それらを `Vec` の `Video` 構造体として定義します。
|
||||
データを保持するために、`main.rs` または選択した任意のファイルにシンプルな `struct` を作成します。
|
||||
|
||||
```rust
|
||||
struct Video {
|
||||
id: usize,
|
||||
title: String,
|
||||
speaker: String,
|
||||
url: String,
|
||||
}
|
||||
```
|
||||
|
||||
次に、この構造体のインスタンスを `app` 関数内で作成し、ハードコーディングされたデータの代わりにそれらを使用します:
|
||||
|
||||
```rust
|
||||
use website_test::tutorial::Video; // 自分のパスに置き換えてください
|
||||
|
||||
let videos = vec![
|
||||
Video {
|
||||
id: 1,
|
||||
title: "Building and breaking things".to_string(),
|
||||
speaker: "John Doe".to_string(),
|
||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
||||
},
|
||||
Video {
|
||||
id: 2,
|
||||
title: "The development process".to_string(),
|
||||
speaker: "Jane Smith".to_string(),
|
||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
||||
},
|
||||
Video {
|
||||
id: 3,
|
||||
title: "The Web 7.0".to_string(),
|
||||
speaker: "Matt Miller".to_string(),
|
||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
||||
},
|
||||
Video {
|
||||
id: 4,
|
||||
title: "Mouseless development".to_string(),
|
||||
speaker: "Tom Jerry".to_string(),
|
||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
それらを表示するために、`Vec` を `Html` に変換する必要があります。これを実現するには、イテレータを作成し、それを `html!` にマッピングして `Html` として収集します:
|
||||
|
||||
```rust ,ignore
|
||||
let videos = videos.iter().map(|video| html! {
|
||||
<p key={video.id}>{format!("{}: {}", video.speaker, video.title)}</p>
|
||||
}).collect::<Html>();
|
||||
```
|
||||
|
||||
:::tip
|
||||
リスト項目にキーを使用することで、Yew はリスト内のどの項目が変更されたかを追跡し、より高速な再レンダリングを実現できます。[リストには常にキーを使用することをお勧めします](/concepts/html/lists.mdx#keyed-lists)。
|
||||
::
|
||||
|
||||
最後に、データから作成された `Html` を使用してハードコーディングされたビデオリストを置き換える必要があります:
|
||||
|
||||
```rust ,ignore {6-10}
|
||||
html! {
|
||||
<>
|
||||
<h1>{ "RustConf Explorer" }</h1>
|
||||
<div>
|
||||
<h3>{ "Videos to watch" }</h3>
|
||||
- <p>{ "John Doe: Building and breaking things" }</p>
|
||||
- <p>{ "Jane Smith: The development process" }</p>
|
||||
- <p>{ "Matt Miller: The Web 7.0" }</p>
|
||||
- <p>{ "Tom Jerry: Mouseless development" }</p>
|
||||
+ { videos }
|
||||
</div>
|
||||
// ...
|
||||
</>
|
||||
}
|
||||
```
|
||||
|
||||
## コンポーネント
|
||||
|
||||
コンポーネントは Yew アプリケーションの構成要素です。コンポーネントを組み合わせることで(他のコンポーネントで構成されることもあります)、アプリケーションを構築します。再利用可能性を考慮してコンポーネントを構築し、それらを汎用的に保つことで、コードやロジックを繰り返すことなく、アプリケーションの複数の部分でそれらを使用できるようになります。
|
||||
|
||||
これまで使用してきた `app` 関数は `App` と呼ばれるコンポーネントであり、「関数コンポーネント」と呼ばれます。
|
||||
|
||||
1. 構造体コンポーネント
|
||||
2. 関数コンポーネント
|
||||
|
||||
このチュートリアルでは、関数コンポーネントを使用します。
|
||||
|
||||
では、`App` コンポーネントをより小さなコンポーネントに分割しましょう。まず、ビデオリストを独自のコンポーネントに抽出します。
|
||||
|
||||
```rust ,compile_fail
|
||||
use yew::prelude::*;
|
||||
|
||||
struct Video {
|
||||
id: usize,
|
||||
title: String,
|
||||
speaker: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
struct VideosListProps {
|
||||
videos: Vec<Video>,
|
||||
}
|
||||
|
||||
#[function_component(VideosList)]
|
||||
fn videos_list(VideosListProps { videos }: &VideosListProps) -> Html {
|
||||
videos.iter().map(|video| html! {
|
||||
<p key={video.id}>{format!("{}: {}", video.speaker, video.title)}</p>
|
||||
}).collect()
|
||||
}
|
||||
```
|
||||
|
||||
`VideosList` 関数コンポーネントのパラメータに注意してください。関数コンポーネントは1つの引数しか受け取らず、その引数は "props"("properties" の略)を定義します。Props は親コンポーネントから子コンポーネントにデータを渡すために使用されます。この場合、`VideosListProps` は props を定義する構造体です。
|
||||
|
||||
:::important
|
||||
props に使用される構造体は `Properties` を派生実装する必要があります。
|
||||
:::
|
||||
|
||||
上記のコードをコンパイルするために、`Video` 構造体を次のように変更する必要があります:
|
||||
|
||||
```rust {1}
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct Video {
|
||||
id: usize,
|
||||
title: String,
|
||||
speaker: String,
|
||||
url: String,
|
||||
}
|
||||
```
|
||||
|
||||
次に、`VideosList` コンポーネントを使用するように `App` コンポーネントを更新できます。
|
||||
|
||||
```rust ,ignore {4-7,13-14}
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
// ...
|
||||
- let videos = videos.iter().map(|video| html! {
|
||||
- <p key={video.id}>{format!("{}: {}", video.speaker, video.title)}</p>
|
||||
- }).collect::<Html>();
|
||||
-
|
||||
html! {
|
||||
<>
|
||||
<h1>{ "RustConf Explorer" }</h1>
|
||||
<div>
|
||||
<h3>{"Videos to watch"}</h3>
|
||||
- { videos }
|
||||
+ <VideosList videos={videos} />
|
||||
</div>
|
||||
// ...
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ブラウザウィンドウを確認することで、リストが期待通りにレンダリングされているかどうかを検証できます。リストのレンダリングロジックをそのコンポーネントに移動しました。これにより、`App` コンポーネントのソースコードが短くなり、読みやすく理解しやすくなりました。
|
||||
|
||||
### アプリケーションをインタラクティブにする
|
||||
|
||||
ここでの最終目標は、選択したビデオを表示することです。そのためには、`VideosList` コンポーネントがビデオを選択したときに親コンポーネントに「通知」する必要があります。これは `Callback` を使用して行います。この概念は「ハンドラの伝播」と呼ばれます。props を変更して `on_click` コールバックを受け取るようにします:
|
||||
|
||||
```rust ,ignore {4}
|
||||
#[derive(Properties, PartialEq)]
|
||||
struct VideosListProps {
|
||||
videos: Vec<Video>,
|
||||
+ on_click: Callback<Video>
|
||||
}
|
||||
```
|
||||
|
||||
次に、選択したビデオをコールバックに渡すように `VideosList` コンポーネントを変更します。
|
||||
|
||||
```rust ,ignore {2-4,6-12,15-16}
|
||||
#[function_component(VideosList)]
|
||||
-fn videos_list(VideosListProps { videos }: &VideosListProps) -> Html {
|
||||
+fn videos_list(VideosListProps { videos, on_click }: &VideosListProps) -> Html {
|
||||
+ let on_click = on_click.clone();
|
||||
videos.iter().map(|video| {
|
||||
+ let on_video_select = {
|
||||
+ let on_click = on_click.clone();
|
||||
+ let video = video.clone();
|
||||
+ Callback::from(move |_| {
|
||||
+ on_click.emit(video.clone())
|
||||
+ })
|
||||
+ };
|
||||
|
||||
html! {
|
||||
- <p key={video.id}>{format!("{}: {}", video.speaker, video.title)}</p>
|
||||
+ <p key={video.id} onclick={on_video_select}>{format!("{}: {}", video.speaker, video.title)}</p>
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
```
|
||||
|
||||
次に、`VideosList` の使用を変更してそのコールバックを渡す必要があります。しかし、その前に、新しいコンポーネント `VideoDetails` を作成し、ビデオがクリックされたときに表示されるようにします。
|
||||
|
||||
```rust
|
||||
use website_test::tutorial::Video;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
struct VideosDetailsProps {
|
||||
video: Video,
|
||||
}
|
||||
|
||||
#[function_component(VideoDetails)]
|
||||
fn video_details(VideosDetailsProps { video }: &VideosDetailsProps) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<h3>{ video.title.clone() }</h3>
|
||||
<img src="https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder" alt="video thumbnail" />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
次に、`App` コンポーネントを変更して、ビデオが選択されたときに `VideoDetails` コンポーネントを表示するようにします。
|
||||
|
||||
```rust ,ignore {4,6-11,13-15,22-23,25-29}
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
// ...
|
||||
+ let selected_video = use_state(|| None);
|
||||
|
||||
+ let on_video_select = {
|
||||
+ let selected_video = selected_video.clone();
|
||||
+ Callback::from(move |video: Video| {
|
||||
+ selected_video.set(Some(video))
|
||||
+ })
|
||||
+ };
|
||||
|
||||
+ let details = selected_video.as_ref().map(|video| html! {
|
||||
+ <VideoDetails video={video.clone()} />
|
||||
+ });
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{ "RustConf Explorer" }</h1>
|
||||
<div>
|
||||
<h3>{"Videos to watch"}</h3>
|
||||
- <VideosList videos={videos} />
|
||||
+ <VideosList videos={videos} on_click={on_video_select.clone()} />
|
||||
</div>
|
||||
+ { for details }
|
||||
- <div>
|
||||
- <h3>{ "John Doe: Building and breaking things" }</h3>
|
||||
- <img src="https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder" alt="video thumbnail" />
|
||||
- </div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
今は `use_state` について心配する必要はありません。後でこの問題に戻ります。リストデータを `{ for details }` で抽出するテクニックに注目してください。
|
||||
`Option<_>` は `Iterator` を実装しているので、特殊な `{ for ... }` 構文を使用して `Iterator` が返す唯一の要素を順番に表示することができます。これは [`html!` マクロ](concepts/html/lists) によってサポートされています。
|
||||
|
||||
### 状態の処理
|
||||
|
||||
以前に使用した `use_state` を覚えていますか?それは "フック" と呼ばれる特殊な関数です。フックは関数コンポーネントのライフサイクルに "フック" して操作を実行するために使用されます。このフックや他のフックについては[こちら](concepts/function-components/hooks/introduction.mdx#pre-defined-hooks)で詳しく学ぶことができます。
|
||||
|
||||
:::note
|
||||
構造体コンポーネントは異なる動作をします。これらについては[ドキュメント](advanced-topics/struct-components/introduction.mdx)を参照してください。
|
||||
:::
|
||||
|
||||
## データの取得(外部 REST API の使用)
|
||||
|
||||
実際のアプリケーションでは、データは通常ハードコーディングされているのではなく、API から取得されます。外部ソースからビデオリストを取得してみましょう。そのためには、以下のクレートを追加する必要があります:
|
||||
|
||||
- [`gloo-net`](https://crates.io/crates/gloo-net)
|
||||
fetch 呼び出しを行うために使用します。
|
||||
- [`serde`](https://serde.rs) とその派生特性
|
||||
JSON 応答をデシリアライズするために使用します。
|
||||
- [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||||
Rust の Future を Promise として実行するために使用します。
|
||||
|
||||
`Cargo.toml` ファイルの依存関係を更新しましょう:
|
||||
|
||||
```toml title="Cargo.toml"
|
||||
[dependencies]
|
||||
gloo-net = "0.6"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
```
|
||||
|
||||
:::note
|
||||
依存関係を選択する際には、それらが `wasm32` と互換性があることを確認してください!そうでない場合、アプリケーションを実行することはできません。
|
||||
:::
|
||||
|
||||
`Deserialize` 特性を派生するように `Video` 構造体を更新します:
|
||||
|
||||
```rust ,ignore {1, 3-4}
|
||||
+ use serde::Deserialize;
|
||||
|
||||
- #[derive(Clone, PartialEq)]
|
||||
+ #[derive(Clone, PartialEq, Deserialize)]
|
||||
struct Video {
|
||||
id: usize,
|
||||
title: String,
|
||||
speaker: String,
|
||||
url: String,
|
||||
}
|
||||
```
|
||||
|
||||
最後のステップとして、ハードコーディングされたデータを使用するのではなく、fetch リクエストを行うように `App` コンポーネントを更新する必要があります。
|
||||
|
||||
```rust ,ignore {1,5-25,34-35}
|
||||
+ use gloo_net::http::Request;
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
- let videos = vec![
|
||||
- // ...
|
||||
- ]
|
||||
+ let videos = use_state(|| vec![]);
|
||||
+ {
|
||||
+ let videos = videos.clone();
|
||||
+ use_effect_with((), move |_| {
|
||||
+ let videos = videos.clone();
|
||||
+ wasm_bindgen_futures::spawn_local(async move {
|
||||
+ let fetched_videos: Vec<Video> = Request::get("https://yew.rs/tutorial/data.json")
|
||||
+ .send()
|
||||
+ .await
|
||||
+ .unwrap()
|
||||
+ .json()
|
||||
+ .await
|
||||
+ .unwrap();
|
||||
+ videos.set(fetched_videos);
|
||||
+ });
|
||||
+ || ()
|
||||
+ });
|
||||
+ }
|
||||
|
||||
// ...
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{ "RustConf Explorer" }</h1>
|
||||
<div>
|
||||
<h3>{"Videos to watch"}</h3>
|
||||
- <VideosList videos={videos} on_click={on_video_select.clone()} />
|
||||
+ <VideosList videos={(*videos).clone()} on_click={on_video_select.clone()} />
|
||||
</div>
|
||||
{ for details }
|
||||
</>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
ここでは `unwrap` を使用していますが、これはデモアプリケーションのためです。実際のアプリケーションでは、[適切なエラーハンドリング](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)を行うことをお勧めします。
|
||||
:::
|
||||
|
||||
さて、ブラウザを確認して、すべてが期待通りに動作しているかを確認しましょう……CORS の問題がなければ。これを解決するために、プロキシサーバーが必要です。幸いなことに、trunk はこの機能を提供しています。
|
||||
|
||||
これらの行を更新します:
|
||||
|
||||
```rust ,ignore {2-3}
|
||||
// ...
|
||||
- let fetched_videos: Vec<Video> = Request::get("https://yew.rs/tutorial/data.json")
|
||||
+ let fetched_videos: Vec<Video> = Request::get("/tutorial/data.json")
|
||||
// ...
|
||||
```
|
||||
|
||||
次に、以下のコマンドを使用してサーバーを再起動します:
|
||||
|
||||
```bash
|
||||
trunk serve --proxy-backend=https://yew.rs/tutorial
|
||||
```
|
||||
|
||||
ページをリフレッシュすると、すべてが期待通りに動作するはずです。
|
||||
|
||||
## まとめ
|
||||
|
||||
おめでとうございます!外部 API からデータを取得し、ビデオリストを表示する Web アプリケーションを作成しました。
|
||||
|
||||
## 次に
|
||||
|
||||
このアプリケーションは、完璧または有用になるまでにはまだ長い道のりがあります。このチュートリアルを完了した後、より高度なトピックを探求するための出発点として使用できます。
|
||||
|
||||
### スタイル
|
||||
|
||||
私たちのアプリケーションは非常に見栄えが悪いです。CSS やその他のスタイルがありません。残念ながら、Yew は組み込みのスタイルコンポーネントを提供していません。スタイルシートを追加する方法については、[Trunk のアセット](https://trunkrs.dev/assets/)を参照してください。
|
||||
|
||||
### さらなる依存ライブラリ
|
||||
|
||||
私たちのアプリケーションは、非常に少ない外部依存関係を使用しています。使用できる多くのクレートがあります。詳細については、[外部ライブラリ](/community/external-libs)を参照してください。
|
||||
|
||||
### Yew についてもっと知る
|
||||
|
||||
私たちの[公式ドキュメント](../getting-started/introduction.mdx)を読んでください。多くの概念についてより詳細に説明しています。Yew API についてもっと知りたい場合は、[API ドキュメント](https://docs.rs/yew)を参照してください。
|
|
@ -3,42 +3,6 @@
|
|||
"message": "0.18.0",
|
||||
"description": "The label for version 0.18.0"
|
||||
},
|
||||
"sidebar.sidebar.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Project Setup": {
|
||||
"message": "Project Setup",
|
||||
"description": "The label for category Project Setup in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Concepts": {
|
||||
"message": "Concepts",
|
||||
"description": "The label for category Concepts in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Components": {
|
||||
"message": "Components",
|
||||
"description": "The label for category Components in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.wasm-bindgen": {
|
||||
"message": "wasm-bindgen",
|
||||
"description": "The label for category wasm-bindgen in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.HTML": {
|
||||
"message": "HTML",
|
||||
"description": "The label for category HTML in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Services": {
|
||||
"message": "Services",
|
||||
"description": "The label for category Services in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Advanced topics": {
|
||||
"message": "Advanced topics",
|
||||
"description": "The label for category Advanced topics in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.More": {
|
||||
"message": "More",
|
||||
"description": "The label for category More in sidebar sidebar"
|
||||
},
|
||||
"sidebar.docs.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar docs"
|
||||
|
|
|
@ -3,58 +3,6 @@
|
|||
"message": "0.19.0",
|
||||
"description": "The label for version 0.19.0"
|
||||
},
|
||||
"sidebar.sidebar.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Project Setup": {
|
||||
"message": "Project Setup",
|
||||
"description": "The label for category Project Setup in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Concepts": {
|
||||
"message": "Concepts",
|
||||
"description": "The label for category Concepts in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.wasm-bindgen": {
|
||||
"message": "wasm-bindgen",
|
||||
"description": "The label for category wasm-bindgen in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Components": {
|
||||
"message": "Components",
|
||||
"description": "The label for category Components in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.HTML": {
|
||||
"message": "HTML",
|
||||
"description": "The label for category HTML in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Function Components": {
|
||||
"message": "Function Components",
|
||||
"description": "The label for category Function Components in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Advanced topics": {
|
||||
"message": "Advanced topics",
|
||||
"description": "The label for category Advanced topics in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.More": {
|
||||
"message": "More",
|
||||
"description": "The label for category More in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.Migration guides": {
|
||||
"message": "Migration guides",
|
||||
"description": "The label for category Migration guides in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.yew": {
|
||||
"message": "yew",
|
||||
"description": "The label for category yew in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.yew-agent": {
|
||||
"message": "yew-agent",
|
||||
"description": "The label for category yew-agent in sidebar sidebar"
|
||||
},
|
||||
"sidebar.sidebar.category.yew-router": {
|
||||
"message": "yew-router",
|
||||
"description": "The label for category yew-router in sidebar sidebar"
|
||||
},
|
||||
"sidebar.docs.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar docs"
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"version.label": {
|
||||
"message": "Next",
|
||||
"description": "The label for version current"
|
||||
},
|
||||
"sidebar.docs.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Concepts": {
|
||||
"message": "Concepts",
|
||||
"description": "The label for category Concepts in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Concepts.link.generated-index.title": {
|
||||
"message": "Yew concepts",
|
||||
"description": "The generated-index page title for category Concepts in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Concepts.link.generated-index.description": {
|
||||
"message": "Learn about the important Yew concepts!",
|
||||
"description": "The generated-index page description for category Concepts in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.HTML": {
|
||||
"message": "HTML",
|
||||
"description": "The label for category HTML in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Components": {
|
||||
"message": "Function Components",
|
||||
"description": "The label for category Components in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Advanced topics": {
|
||||
"message": "Advanced topics",
|
||||
"description": "The label for category Advanced topics in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Advanced topics.link.generated-index.title": {
|
||||
"message": "Advanced topics",
|
||||
"description": "The generated-index page title for category Advanced topics in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Advanced topics.link.generated-index.description": {
|
||||
"message": "Learn about the advanced topics and inner workings of Yew!",
|
||||
"description": "The generated-index page description for category Advanced topics in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.More": {
|
||||
"message": "More",
|
||||
"description": "The label for category More in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.More.link.generated-index.title": {
|
||||
"message": "Miscellaneous",
|
||||
"description": "The generated-index page title for category More in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Migration guides": {
|
||||
"message": "Migration guides",
|
||||
"description": "The label for category Migration guides in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.yew": {
|
||||
"message": "yew",
|
||||
"description": "The label for category yew in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.yew-agent": {
|
||||
"message": "yew-agent",
|
||||
"description": "The label for category yew-agent in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.yew-router": {
|
||||
"message": "yew-router",
|
||||
"description": "The label for category yew-router in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Using Basic Web Technologies In Yew": {
|
||||
"message": "Intro With Basic Web Technologies",
|
||||
"description": "The label for category Using Basic Web Technologies In Yew in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.title": {
|
||||
"message": "Yew Take on Basic Web Technologies",
|
||||
"description": "The generated-index page title for category Using Basic Web Technologies In Yew in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Using Basic Web Technologies In Yew.link.generated-index.description": {
|
||||
"message": "Yew mostly operates on the idea of keeping everything that a reusable piece of UI may need, in one place - rust files. But also seeks to stay close to the original look of the technology. Explore further to fully grasp what we mean by these statements:",
|
||||
"description": "The generated-index page description for category Using Basic Web Technologies In Yew in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Hooks": {
|
||||
"message": "Hooks",
|
||||
"description": "The label for category Hooks in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Struct Components": {
|
||||
"message": "Struct Components",
|
||||
"description": "The label for category Struct Components in sidebar docs"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
---
|
||||
title: '子コンポーネント'
|
||||
---
|
||||
|
||||
:::caution
|
||||
|
||||
`Children` をチェックおよび操作すると、アプリケーションで驚くべきかつ説明が難しい動作が発生することがよくあります。これにより、エッジケースが発生し、通常は予期しない結果が生じる可能性があります。`Children` を操作しようとする場合は、他の方法を検討する必要があります。
|
||||
|
||||
Yew は、子コンポーネントのプロパティの型として `Html` を使用することをサポートしています。`Children` または `ChildrenRenderer` が必要ない場合は、子コンポーネントとして `Html` を使用することをお勧めします。これは `Children` の欠点がなく、パフォーマンスのオーバーヘッドも低くなります。
|
||||
|
||||
:::
|
||||
|
||||
## 一般的な使用法
|
||||
|
||||
_ほとんどの場合、_ コンポーネントに子コンポーネントを持たせる場合、子コンポーネントの型を気にする必要はありません。この場合、以下の例で十分です。
|
||||
|
||||
````rust
|
||||
use yew::{html, Component, Context, Html, Properties};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ListProps {
|
||||
#[prop_or_default]
|
||||
pub children: Html,
|
||||
}
|
||||
|
||||
pub struct List;
|
||||
|
||||
impl Component for List {
|
||||
type Message = ();
|
||||
type Properties = ListProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="list">
|
||||
{ctx.props().children.clone()}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## 高度な使用法
|
||||
|
||||
### 型指定された子コンポーネント
|
||||
|
||||
特定のタイプのコンポーネントを子コンポーネントとして渡したい場合は、`yew::html::ChildrenWithProps<T>` を使用できます。
|
||||
|
||||
```rust
|
||||
use yew::{html, ChildrenWithProps, Component, Context, Html, Properties};
|
||||
|
||||
pub struct Item;
|
||||
|
||||
impl Component for Item {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "item" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ListProps {
|
||||
#[prop_or_default]
|
||||
pub children: ChildrenWithProps<Item>,
|
||||
}
|
||||
|
||||
pub struct List;
|
||||
|
||||
impl Component for List {
|
||||
type Message = ();
|
||||
type Properties = ListProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="list">
|
||||
{ for ctx.props().children.iter() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
## プロパティを持つネストされた子コンポーネント
|
||||
|
||||
コンポーネントがその子コンポーネントを型指定している場合、ネストされたコンポーネントのプロパティにアクセスして変更することができます。
|
||||
|
||||
```rust
|
||||
use std::rc::Rc;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ListItemProps {
|
||||
value: String,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn ListItem(props: &ListItemProps) -> Html {
|
||||
let ListItemProps { value } = props.clone();
|
||||
html! {
|
||||
<span>
|
||||
{value}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub children: ChildrenWithProps<ListItem>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn List(props: &Props) -> Html {
|
||||
let modified_children = props.children.iter().map(|mut item| {
|
||||
let mut props = Rc::make_mut(&mut item.props);
|
||||
props.value = format!("item-{}", props.value);
|
||||
item
|
||||
});
|
||||
html! { for modified_children }
|
||||
}
|
||||
|
||||
html! {
|
||||
<List>
|
||||
<ListItem value="a" />
|
||||
<ListItem value="b" />
|
||||
<ListItem value="c" />
|
||||
</List>
|
||||
};
|
||||
```
|
||||
|
||||
### 列挙型の子コンポーネント
|
||||
|
||||
もちろん、時には子コンポーネントをいくつかの異なるコンポーネントに制限する必要がある場合があります。そのような場合には、Yewについてさらに深く理解する必要があります。
|
||||
|
||||
ここでは、より良いエルゴノミクスを提供するために [`derive_more`](https://github.com/JelteF/derive_more) を使用しています。使用したくない場合は、各バリアントに対して手動で `From` を実装することができます。
|
||||
|
||||
```rust
|
||||
use yew::{
|
||||
html, html::ChildrenRenderer, virtual_dom::VChild, Component,
|
||||
Context, Html, Properties,
|
||||
};
|
||||
|
||||
pub struct Primary;
|
||||
|
||||
impl Component for Primary {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "Primary" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Secondary;
|
||||
|
||||
impl Component for Secondary {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "Secondary" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, derive_more::From, PartialEq)]
|
||||
pub enum Item {
|
||||
Primary(VChild<Primary>),
|
||||
Secondary(VChild<Secondary>),
|
||||
}
|
||||
|
||||
// 現在、`Into<Html>` を実装して、yew が `Item` をどのようにレンダリングするかを知ることができるようにします。
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<Html> for Item {
|
||||
fn into(self) -> Html {
|
||||
match self {
|
||||
Self::Primary(child) => child.into(),
|
||||
Self::Secondary(child) => child.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ListProps {
|
||||
#[prop_or_default]
|
||||
pub children: ChildrenRenderer<Item>,
|
||||
}
|
||||
|
||||
pub struct List;
|
||||
|
||||
impl Component for List {
|
||||
type Message = ();
|
||||
type Properties = ListProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="list">
|
||||
{ for ctx.props().children.iter() }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### オプションの型の子コンポーネント
|
||||
|
||||
特定の型の単一のオプションの子コンポーネントを持つこともできます:
|
||||
|
||||
```rust
|
||||
use yew::{
|
||||
html, html_nested, virtual_dom::VChild, Component,
|
||||
Context, Html, Properties
|
||||
};
|
||||
|
||||
pub struct PageSideBar;
|
||||
|
||||
impl Component for PageSideBar {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
{ "sidebar" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct PageProps {
|
||||
#[prop_or_default]
|
||||
pub sidebar: Option<VChild<PageSideBar>>,
|
||||
}
|
||||
|
||||
struct Page;
|
||||
|
||||
impl Component for Page {
|
||||
type Message = ();
|
||||
type Properties = PageProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="page">
|
||||
{ ctx.props().sidebar.clone().map(Html::from).unwrap_or_default() }
|
||||
// ... ページ内容
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ページコンポーネントはサイドバーを含むかどうかを選択できます:
|
||||
|
||||
pub fn render_page(with_sidebar: bool) -> Html {
|
||||
if with_sidebar {
|
||||
// サイドバーを含むページ
|
||||
html! {
|
||||
<Page sidebar={html_nested! {
|
||||
<PageSideBar />
|
||||
}} />
|
||||
}
|
||||
} else {
|
||||
// サイドバーを含まないページ
|
||||
html! {
|
||||
<Page />
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## さらに読む
|
||||
|
||||
- このパターンの実際の例については、yew-router のソースコードを参照してください。より高度な例については、yew リポジトリの[関連する例のリスト](https://github.com/yewstack/yew/tree/master/examples/nested_list)を参照してください。
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: '仕組み'
|
||||
description: 'フレームワークの低レベルの詳細について'
|
||||
---
|
||||
|
||||
# 基本ライブラリの内部詳細
|
||||
|
||||
## `html!` マクロの内部
|
||||
|
||||
`html!` マクロは、HTMLに似たカスタム構文で記述されたコードを有効なRustコードに変換します。このマクロを使用することはYewアプリケーションの開発に必須ではありませんが、推奨されています。このマクロが生成するコードはYewのパブリックライブラリAPIを使用しており、希望すれば直接使用することもできます。いくつかのメソッドは意図的に文書化されていないため、誤用を避けるために注意が必要です。`yew-macro`の各更新により、生成されるコードはより効率的になり、`html!`構文をほとんど(または全く)変更することなく破壊的な変更を処理できるようになります。
|
||||
|
||||
`html!` マクロを使用すると、宣言的なスタイルでコードを記述できるため、UIレイアウトコードはページのHTMLに非常に似たものになります。アプリケーションがよりインタラクティブになり、コードベースが大きくなるにつれて、この方法はますます有用になります。DOM 操作のすべてのコードを手動で記述するのに比べて、マクロがこれらすべてを処理してくれます。
|
||||
|
||||
`html!` マクロの使用は非常に魔法のように感じるかもしれませんが、隠すべきものは何もありません。その仕組みに興味がある場合は、プログラム内の `html!` マクロ呼び出しを展開してみてください。`cargo expand` という便利なコマンドがあり、Rustマクロの展開を確認できます。`cargo expand` はデフォルトで `cargo` に含まれていないため、まだインストールしていない場合は `cargo install cargo-expand` を使用してインストールする必要があります。[Rust-Analyzer](https://rust-analyzer.github.io/) も[IDEからマクロ出力を取得するメカニズム](https://rust-analyzer.github.io/manual.html#expand-macro-recursively)を提供しています。
|
||||
|
||||
`html!` マクロの出力は通常非常に簡潔です!これは特徴です:機械生成のコードは時々アプリケーション内の他のコードと衝突することがあります。問題を防ぐために、`proc_macro` は「衛生」ルールに従っています。いくつかの例を以下に示します:
|
||||
|
||||
1. Yewパッケージを正しく参照するために、マクロ生成コードでは `::yew::<module>` を使用し、直接 `yew::<module>` を使用しません。これは `::alloc::vec::Vec::new()` を呼び出すのと同じ理由です。
|
||||
2. トレイトメソッド名の衝突を避けるために、`<Type as Trait>` を使用して正しいトレイトメンバーを使用していることを確認します。
|
||||
|
||||
## 仮想 DOM とは?
|
||||
|
||||
DOM(「ドキュメントオブジェクトモデル」)は、ブラウザによって管理されるHTMLコンテンツの表現です。「仮想」 DOM は、単にメモリ内の DOM のコピーです。仮想 DOM を管理することで、メモリのオーバーヘッドが増加しますが、ブラウザAPIの使用を回避または遅延させることでバッチ処理と高速な読み取りを実現できます。
|
||||
|
||||
メモリ内に DOM のコピーを持つことは、宣言的UIを使用するライブラリの使用を促進するのに役立ちます。ユーザーイベントに基づいて DOM を変更するための特定のコードが必要な場合とは異なり、ライブラリは一般的な方法を使用して DOM の「差分」を行うことができます。Yewコンポーネントが更新され、そのレンダリング方法を変更したい場合、Yewライブラリは仮想 DOM の2番目のコピーを構築し、現在画面上に表示されている内容をミラーリングする仮想 DOM と直接比較します。両者の「差分」は増分更新に分解され、ブラウザAPIと共に適用されます。更新が適用されると、古い仮想 DOM のコピーは破棄され、新しいコピーが将来の差分チェックのために保存されます。
|
||||
|
||||
この「差分」アルゴリズムは、時間の経過とともに最適化され、複雑なアプリケーションのパフォーマンスを向上させることができます。YewアプリケーションはWebAssemblyを介して実行されるため、Yewは将来的により複雑なアルゴリズムを採用する上で競争力を持つと信じています。
|
||||
|
||||
Yewの仮想 DOM はブラウザの DOM と完全に一対一対応しているわけではありません。DOM 要素を整理するための「リスト」や「コンポーネント」も含まれています。リストは単に要素の順序付きリストである場合もありますが、より強力な場合もあります。各リスト要素に「キー」注釈を追加することで、アプリケーション開発者はリストが変更されたときに差分更新の計算に必要な作業量を最小限に抑えるための追加の最適化をYewに提供できます。同様に、コンポーネントは再レンダリングが必要かどうかを示すカスタムロジックを提供し、パフォーマンスを向上させるのに役立ちます。
|
||||
|
||||
## Yewスケジューラとコンポーネントスコープのイベントループ
|
||||
|
||||
_貢献ドキュメント - `yew::scheduler` と `yew::html::scope` の仕組みを詳しく説明_
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [Rustのマクロに関する詳細情報](https://doc.rust-lang.org/stable/book/ch19-06-macros.html)
|
||||
- [`cargo-expand` に関する詳細情報](https://github.com/dtolnay/cargo-expand)
|
||||
- [`yew::virtual_dom` のAPIドキュメント](https://docs.rs/yew/*/yew/virtual_dom/index.html)
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
title: 'イミュータブルタイプ'
|
||||
description: 'Yew のイミュータブルデータ構造'
|
||||
---
|
||||
|
||||
## イミュータブルタイプとは?
|
||||
|
||||
これらのタイプは、インスタンス化はできるが値を変更することはできないタイプです。値を更新するには、新しい値をインスタンス化する必要があります。
|
||||
|
||||
## なぜイミュータブルタイプを使用するのですか?
|
||||
|
||||
React と同様に、プロパティは祖先から子孫に伝播されます。これは、各コンポーネントが更新されるたびにプロパティが存在する必要があることを意味します。したがって、プロパティは理想的には簡単にクローンできるべきです。これを実現するために、通常は `Rc` にラップします。
|
||||
|
||||
イミュータブルタイプは、コンポーネント間でプロパティの値を低コストでクローンできるため、プロパティの値を保持するのに最適です。
|
||||
|
||||
## さらに読む
|
||||
|
||||
- [イミュータブルの例](https://github.com/yewstack/yew/tree/master/examples/immutable)
|
||||
- [Crate `implicit-clone`](https://docs.rs/implicit-clone/)
|
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
title: '最適化とベストプラクティス'
|
||||
sidebar_label: Optimizations
|
||||
description: 'アプリケーションのパフォーマンスを最適化する'
|
||||
---
|
||||
|
||||
## スマートポインタの使用
|
||||
|
||||
**注意:このセクションで使用されている用語に混乱がある場合は、Rustのマニュアルにある[スマートポインタに関する章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)が役立ちます。**
|
||||
|
||||
再レンダリング時に大量のデータをクローンしてpropsを作成するのを避けるために、スマートポインタを使用してデータ自体ではなくデータへの参照のみをクローンすることができます。propsや子コンポーネントに関連データの参照を渡すことで、データを変更する必要がある子コンポーネントでデータをクローンするのを避けることができます。`Rc::make_mut`を使用してデータをクローンし、変更するための可変参照を取得できます。
|
||||
|
||||
これにより、`Component::changed`でのpropの変更がコンポーネントの再レンダリングを必要とするかどうかを判断する際にさらに利点があります。これは、データの値ではなくポインタのアドレス(つまり、データがマシンメモリに格納されている場所)を比較できるためです。2つのポインタが同じデータを指している場合、それらが指しているデータの値は同じでなければなりません。逆は必ずしも真ではないことに注意してください!2つのポインタアドレスが異なる場合でも、基になるデータは同じである可能性があります。この場合、基になるデータを比較する必要があります。
|
||||
|
||||
この比較を行うには、`PartialEq`(データを比較する際に自動的に使用される等価演算子`==`)ではなく、`Rc::ptr_eq`を使用する必要があります。Rustのドキュメントには、`Rc::ptr_eq`に関する[詳細](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.ptr_eq)があります。
|
||||
|
||||
この最適化は、`Copy`を実装していないデータ型に最も有用です。データを安価にコピーできる場合、それをスマートポインタの後ろに置く必要はありません。`Vec`、`HashMap`、`String`などのデータ集約型の構造体に対して、スマートポインタを使用することでパフォーマンスの向上が見込まれます。
|
||||
|
||||
この最適化は、子コンポーネントが値を更新しない場合に最も効果的であり、親コンポーネントがほとんど更新されない場合にさらに効果的です。これにより、`Rc<_>`は純粋なコンポーネントでpropsの値をラップするのに適した選択肢となります。
|
||||
|
||||
ただし、子コンポーネントでデータを自分でクローンする必要がない限り、この最適化は無駄であり、不要な参照カウントのコストを追加するだけです。Yewのpropsはすでに参照カウントされており、内部でデータのクローンは行われません。
|
||||
|
||||
## レンダリング関数
|
||||
|
||||
コードの可読性のために、`html!`の一部の繰り返しコードを専用の分割関数に移行することは通常意味があります。これにより、コードが読みやすくなり、インデントが減り、良いデザインパターンを奨励します。特に、複数の場所で呼び出すことができるこれらの関数を使用して、コード量を減らすことができます。
|
||||
|
||||
## 純粋なコンポーネント
|
||||
|
||||
純粋なコンポーネントは、その状態を変更せず、コンテンツを表示し、メッセージを通常の可変コンポーネントに伝播するコンポーネントです。これらは、`html!`マクロ内でコンポーネント構文(`<SomePureComponent />`)を使用する点でビュー関数とは異なり、実装に応じてメモ化される可能性があります(これは、一度関数が呼び出されると、その値が「保存」されることを意味し、同じパラメータで複数回呼び出された場合、その値を再計算する必要がなく、最初の関数呼び出しから保存された値を返すだけです)。Yewは内部でpropsを比較するため、propsが変更された場合にのみUIを再レンダリングします。
|
||||
|
||||
## ワークスペースを使用してコンパイル時間を短縮する
|
||||
|
||||
Yewの最大の欠点は、コンパイルにかかる時間が長いことです。プロジェクトのコンパイルにかかる時間は、`html!`マクロに渡されるコードの量に関連しているようです。小規模なプロジェクトでは問題にならないようですが、大規模なアプリケーションでは、コンパイラがアプリケーションのために行う作業量を最小限に抑えるためにコードを複数のクレートに分割することが理にかなっています。
|
||||
|
||||
1つの方法として、メインクレートがルーティング/ページ選択を処理し、各ページごとに異なるクレートを作成することが考えられます。各ページは異なるコンポーネントまたは`Html`を生成する大きな関数である可能性があります。アプリケーションの異なる部分を含むクレート間で共有されるコードは、プロジェクトが依存する別のクレートに格納できます。理想的には、すべてのコードを再コンパイルするのではなく、メインクレートと1つのページクレートのみを再コンパイルすることになります。最悪の場合、「共通」クレートで何かを編集した場合、すべての依存コードを再コンパイルする必要があり、元の状態に戻ります。
|
||||
|
||||
メインクレートが重すぎる場合や、深くネストされたページ(例:別のページ上にレンダリングされるページ)を迅速に反復したい場合は、メインページの簡略化された実装を作成し、作業中のコンポーネントを追加でレンダリングするためにサンプルクレートを使用できます。
|
||||
|
||||
## バイナリサイズの縮小
|
||||
|
||||
- Rustコードの最適化
|
||||
- `cargo.toml`(リリースプロファイルの定義)
|
||||
- `wasm-opt` を使用してwasmコードを最適化
|
||||
|
||||
**注意:バイナリサイズの縮小に関する詳細は、[Rust Wasmマニュアル](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)を参照してください。**
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
リリースビルドをより小さくするために、`Cargo.toml`の`[profile.release]`セクションで利用可能な設定を使用して構成できます。
|
||||
|
||||
```toml, title=Cargo.toml
|
||||
[profile.release]
|
||||
# バイナリサイズを小さくする
|
||||
panic = 'abort'
|
||||
# コード全体を最適化する(最適化は良くなるが、ビルド速度は遅くなる)
|
||||
codegen-units = 1
|
||||
# サイズを最適化する(より積極的なアプローチ)
|
||||
opt-level = 'z'
|
||||
# サイズを最適化する
|
||||
# opt-level = 's'
|
||||
# プログラム全体の解析を使用してリンク時に最適化
|
||||
lto = true
|
||||
```
|
||||
|
||||
### 開発版 Cargo 設定
|
||||
|
||||
Rust と cargo の実験的な開発版機能から追加の利点を得ることもできます。`trunk` の開発版ツールチェーンを使用するには、`RUSTUP_TOOLCHAIN="nightly"` 環境変数を設定します。その後、`.cargo/config.toml` で不安定な rustc 機能を構成できます。不安定機能のドキュメント、特に[`build-std`]および[`build-std-features`]に関する部分を参照して、設定方法を確認してください。
|
||||
|
||||
```toml, title=".cargo/config.toml"
|
||||
[unstable]
|
||||
# rust-srcコンポーネントが必要です。`rustup +nightly component add rust-src`
|
||||
build-std = ["std", "panic_abort"]
|
||||
build-std-features = ["panic_immediate_abort"]
|
||||
```
|
||||
|
||||
[不安定な機能のリスト]: https://doc.rust-lang.org/cargo/reference/unstable.html
|
||||
[`build-std`]: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std
|
||||
[`build-std-features`]: https://doc.rust-lang.org/cargo/reference/unstable.html#build-std-features
|
||||
|
||||
:::caution
|
||||
開発版のRustコンパイラには、[この例](https://github.com/yewstack/yew/issues/2696)のようなバグが含まれている可能性があるため、定期的に監視し調整する必要があります。これらの実験的なオプションを使用する際は注意が必要です。
|
||||
:::
|
||||
|
||||
### wasm-opt
|
||||
|
||||
さらに、`wasm` コードのサイズを最適化することができます。
|
||||
|
||||
Rust Wasm マニュアルには、Wasm バイナリファイルのサイズを縮小する方法に関するセクションがあります:[.wasm サイズの縮小](https://rustwasm.github.io/book/game-of-life/code-size.html)
|
||||
|
||||
- `wasm-pack` を使用すると、デフォルトでリリースビルドの `wasm` コードが最適化されます
|
||||
- `wasm` ファイルに直接 `wasm-opt` を使用する
|
||||
|
||||
```text
|
||||
wasm-opt wasm_bg.wasm -Os -o wasm_bg_opt.wasm
|
||||
```
|
||||
|
||||
#### yew/examples/ の 'minimal' サンプルのビルドサイズ
|
||||
|
||||
注意:`wasm-pack` は Rust と Wasm コードの最適化を組み合わせています。この例では、`wasm-bindgen` は Rust のサイズ最適化を行っていません。
|
||||
|
||||
| ツールチェーン | サイズ |
|
||||
| :-------------------------- | :----- |
|
||||
| wasm-bindgen | 158KB |
|
||||
| wasm-bindgen + wasm-opt -Os | 116KB |
|
||||
| wasm-pack | 99 KB |
|
||||
|
||||
## さらに読む
|
||||
|
||||
- [Rust マニュアルのスマート ポインターに関する章](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)
|
||||
- [Rust Wasm マニュアルのコードサイズの縮小に関する章](https://rustwasm.github.io/book/reference/code-size.html#optimizing-builds-for-code-size)
|
||||
- [Rust プロファイルに関するドキュメント](https://doc.rust-lang.org/cargo/reference/profiles.html)
|
||||
- [binaryen プロジェクト](https://github.com/WebAssembly/binaryen)
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
title: 'ポータル (Portals)'
|
||||
description: 'コンテンツをDOMツリー外のノードにレンダリングする'
|
||||
---
|
||||
|
||||
## ポータルとは?
|
||||
|
||||
ポータル (Portal) は、子要素を親コンポーネントのDOM階層外のDOMノードにレンダリングする方法を提供します。`yew::create_portal(child, host)` は `Html` 値を返し、`child` を `host` 要素の子要素としてレンダリングしますが、親コンポーネントの階層下ではありません。
|
||||
|
||||
## 使用方法
|
||||
|
||||
ポータルの典型的な用途には、モーダルダイアログやホバーカード、さらに技術的な用途として、要素の [`shadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot) の内容を制御すること、スタイルシートを周囲のドキュメントの `<head>` に添付すること、`<svg>` の中央の `<defs>` 要素に参照される要素を収集することなどがあります。
|
||||
|
||||
`yew::create_portal` は低レベルの構成要素であることに注意してください。ライブラリはこれを使用してより高レベルのAPIを実装し、その後アプリケーションはこれらのAPIを使用できます。例えば、ここでは `children` を `yew` 以外の要素にレンダリングするシンプルなモーダルダイアログを示します。この要素は `id="modal_host"` で識別されます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ModalProps {
|
||||
#[prop_or_default]
|
||||
pub children: Html,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Modal(props: &ModalProps) -> Html {
|
||||
let modal_host = gloo::utils::document()
|
||||
.get_element_by_id("modal_host")
|
||||
.expect("Expected to find a #modal_host element");
|
||||
|
||||
create_portal(
|
||||
props.children.clone(),
|
||||
modal_host.into(),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## イベント処理
|
||||
|
||||
ポータル内部の要素で発生するイベントは、仮想DOMのバブリングに従います。つまり、ポータルが要素の子要素としてレンダリングされる場合、その要素上のイベントリスナーは、ポータル内部から発生するイベントをキャプチャします。たとえポータルが実際のDOM内の無関係な位置にその内容をレンダリングしていてもです。
|
||||
|
||||
これにより、開発者は使用しているコンポーネントがポータルを使用して実装されているかどうかを気にする必要がなくなります。いずれにせよ、その子要素上で発生するイベントはバブリングします。
|
||||
|
||||
既知の問題として、ポータルから **閉じた** シャドウルートへのイベントは2回分配されます。1回はシャドウルート内部の要素に対して、もう1回はホスト要素自体に対してです。**開いた** シャドウルートは正常に動作しますので、これが影響する場合は、いつでもバグレポートを提出してください。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [ポータルの例](https://github.com/yewstack/yew/tree/master/examples/portals)
|
|
@ -0,0 +1,184 @@
|
|||
---
|
||||
title: 'サーバーサイドレンダリング'
|
||||
description: 'Yewコンポーネントをサーバーサイドでレンダリングする。'
|
||||
---
|
||||
|
||||
# サーバーサイドレンダリング (Server-Side Rendering)
|
||||
|
||||
デフォルトでは、Yewコンポーネントはクライアントサイドでレンダリングされます。ユーザーがウェブサイトにアクセスすると、サーバーは実際のコンテンツを含まない骨組みのHTMLファイルとWebAssemblyパッケージをブラウザに送信します。すべてのコンテンツはクライアントサイドでWebAssemblyパッケージによってレンダリングされます。これをクライアントサイドレンダリングと呼びます。
|
||||
|
||||
この方法はほとんどのウェブサイトにとって有効ですが、いくつかの注意点があります:
|
||||
|
||||
1. ユーザーはWebAssemblyパッケージがダウンロードされ、初期レンダリングが完了するまで何も表示されません。これにより、ネットワークが遅い場合にユーザーエクスペリエンスが悪化する可能性があります。
|
||||
2. 一部の検索エンジンは動的にレンダリングされたウェブページのコンテンツをサポートしておらず、サポートしている検索エンジンでも通常は動的なウェブサイトのランキングが低くなります。
|
||||
|
||||
これらの問題を解決するために、ウェブサイトをサーバーサイドでレンダリングすることができます。
|
||||
|
||||
## 動作原理
|
||||
|
||||
Yewはページをサーバーサイドでレンダリングするための `ServerRenderer` を提供しています。
|
||||
|
||||
Yewコンポーネントをサーバーサイドでレンダリングするには、`ServerRenderer::<App>::new()` を使用してレンダラーを作成し、`renderer.render().await` を呼び出して `<App />` を `String` としてレンダリングします。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use yew::ServerRenderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! {<div>{"Hello, World!"}</div>}
|
||||
}
|
||||
|
||||
// この例が CI の WASM 環境で動作することを保証するために `flavor = "current_thread"` を使用しています。
|
||||
// マルチスレッドを使用したい場合は、デフォルトの `#[tokio::main]` マクロを使用できます。
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn no_main() {
|
||||
let renderer = ServerRenderer::<App>::new();
|
||||
|
||||
let rendered = renderer.render().await;
|
||||
|
||||
// プリント: <div>Hello, World!</div>
|
||||
println!("{}", rendered);
|
||||
}
|
||||
```
|
||||
|
||||
## コンポーネントのライフサイクル
|
||||
|
||||
クライアントサイドレンダリングとは異なり、サーバーサイドレンダリング時のコンポーネントのライフサイクルは異なります。
|
||||
|
||||
コンポーネントが最初に `Html` として正常にレンダリングされるまで、`use_effect`(および `use_effect_with`)以外のすべてのフックは正常に動作します。
|
||||
|
||||
:::caution ブラウザインターフェースは利用できません!
|
||||
|
||||
`web_sys` などのブラウザ関連のインターフェースは、サーバーサイドレンダリング時には利用できません。これらを使用しようとすると、アプリケーションがクラッシュします。このロジックは `use_effect` または `use_effect_with` に隔離する必要があります。これらはサーバーサイドレンダリング時には実行されないためです。
|
||||
|
||||
:::
|
||||
|
||||
:::danger 構造化コンポーネント
|
||||
|
||||
サーバーサイドレンダリング時に構造化コンポーネントを使用することは可能ですが、クライアントサイドの安全なロジック(関数コンポーネントの `use_effect` フックなど)とライフサイクルイベントの間には明確な境界がなく、ライフサイクルイベントの呼び出し順序もクライアントとは異なります。
|
||||
|
||||
さらに、構造化コンポーネントは、すべての子コンポーネントがレンダリングされ `destroy` メソッドが呼び出されるまでメッセージを受け取り続けます。開発者は、コンポーネントに渡される可能性のあるメッセージがブラウザインターフェースを呼び出すロジックにリンクされないようにする必要があります。
|
||||
|
||||
サーバーサイドレンダリングをサポートするアプリケーションを設計する際は、特別な理由がない限り、関数コンポーネントを使用することをお勧めします。
|
||||
|
||||
:::
|
||||
|
||||
## サーバーサイドレンダリング中のデータ取得
|
||||
|
||||
データ取得はサーバーサイドレンダリングとハイドレーション(hydration)中の難点の一つです。
|
||||
|
||||
従来の方法では、コンポーネントがレンダリングされるとすぐに利用可能になります(仮想DOMを出力してレンダリングします)。コンポーネントがデータを取得する必要がない場合、この方法は有効です。しかし、コンポーネントがレンダリング時にデータを取得しようとするとどうなるでしょうか?
|
||||
|
||||
以前は、Yewにはコンポーネントがまだデータを取得しているかどうかを検出するメカニズムがありませんでした。データ取得クライアントは、初期レンダリング中に何が要求されたかを検出し、要求が完了した後に再レンダリングをトリガーするソリューションを実装する責任がありました。サーバーはこのプロセスを繰り返し、応答を返す前にレンダリング中に追加の保留中の要求がないことを確認します。
|
||||
|
||||
これは、コンポーネントを繰り返しレンダリングするため、CPUリソースを浪費するだけでなく、データクライアントは、サーバー側で取得したデータをハイドレーション中に利用可能にする方法を提供する必要があり、初期レンダリングで返される仮想DOMがサーバーサイドレンダリングのDOMツリーと一致することを保証する必要があります。これは実現が難しい場合があります。
|
||||
|
||||
Yewは、`<Suspense />` を使用してこの問題を解決する異なるアプローチを採用しています。
|
||||
|
||||
`<Suspense />` は特別なコンポーネントで、クライアント側で使用する場合、コンポーネントがデータを取得(保留)している間にフォールバックUIを表示し、データ取得が完了した後に通常のUIに戻る方法を提供します。
|
||||
|
||||
アプリケーションがサーバーサイドレンダリングされると、Yewはコンポーネントが保留状態でなくなるまで待機し、それを文字列バッファにシリアル化します。
|
||||
|
||||
ハイドレーション中、`<Suspense />` コンポーネント内の要素は、すべての子コンポーネントが保留状態でなくなるまでハイドレーションされません。
|
||||
|
||||
この方法により、開発者はサーバーサイドレンダリングに対応したクライアント非依存のアプリケーションを簡単に構築し、データ取得を行うことができます。
|
||||
|
||||
## SSR ハイドレーション
|
||||
|
||||
## サーバーサイドレンダリングハイドレーション(SSR Hydration)
|
||||
|
||||
ハイドレーションは、Yewアプリケーションをサーバー側で生成されたHTMLファイルに接続するプロセスです。デフォルトでは、`ServerRender` はハイドレーション可能なHTML文字列を出力し、追加情報を含んでハイドレーションを容易にします。`Renderer::hydrate` メソッドを呼び出すと、Yewは最初からレンダリングするのではなく、アプリケーションが生成した仮想DOMとサーバーレンダラーが生成したHTML文字列を調整します。
|
||||
|
||||
:::caution
|
||||
|
||||
`ServerRenderer` が作成したHTMLマークアップを正常にハイドレーションするためには、クライアントはSSRに使用されたレイアウトと完全に一致する仮想DOMレイアウトを生成する必要があります。要素を含まないコンポーネントも含めてです。特定の実装でのみ使用されるコンポーネントがある場合は、`PhantomComponent` を使用して追加のコンポーネントの位置を埋めることを検討してください。
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
SSR出力(静的HTML)をブラウザが初期レンダリングした後、実際のDOMが期待されるDOMと一致する場合にのみ、ハイドレーションは成功します。HTMLが規格に準拠していない場合、ハイドレーションは失敗する可能性があります。ブラウザは不正なHTMLのDOM構造を変更する可能性があり、実際のDOMが期待されるDOMと異なることがあります。例えば、[`<tbody>` のない `<table>` がある場合、ブラウザはDOMに `<tbody>` を追加する可能性があります](https://github.com/yewstack/yew/issues/2684)。
|
||||
:::
|
||||
|
||||
## ハイドレーション中のコンポーネントライフサイクル
|
||||
|
||||
ハイドレーション中、コンポーネントは作成後に2回連続してレンダリングされます。すべてのエフェクトは2回目のレンダリングが完了した後に呼び出されます。コンポーネントのレンダリング関数に副作用がないことを確認することが重要です。状態を変更したり、追加のレンダリングをトリガーしたりしないようにしてください。現在、状態を変更したり追加のレンダリングをトリガーしたりするコンポーネントがある場合は、それらを `use_effect` フックに移動してください。
|
||||
|
||||
ハイドレーション中、構造化コンポーネントを使用してサーバーサイドレンダリングを行うことができます。ビュー関数はレンダリング関数の前に複数回呼び出されます。レンダリング関数が呼び出されるまで、DOMは未接続と見なされ、`rendered()` メソッドが呼び出される前にレンダリングノードにアクセスすることを防ぐ必要があります。
|
||||
|
||||
## 例
|
||||
|
||||
```rust ,ignore
|
||||
use yew::prelude::*;
|
||||
use yew::Renderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! {<div>{"Hello, World!"}</div>}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let renderer = Renderer::<App>::new();
|
||||
|
||||
// body 要素の下のすべてのコンテンツをハイドレーションし、末尾の要素を削除します(存在する場合)。
|
||||
renderer.hydrate();
|
||||
}
|
||||
```
|
||||
|
||||
例: [simple_ssr](https://github.com/yewstack/yew/tree/master/examples/simple_ssr)
|
||||
例: [ssr_router](https://github.com/yewstack/yew/tree/master/examples/ssr_router)
|
||||
|
||||
## シングルスレッドモード
|
||||
|
||||
Yewは `yew::LocalServerRenderer` を使用してシングルスレッドでのサーバーサイドレンダリングをサポートしています。このモードはWASIのようなシングルスレッド環境に適しています。
|
||||
|
||||
```rust
|
||||
// Rustc 1.78以降では、`wasm32-wasip1` または `wasm32-wasip2` ターゲットを使用してビルドします。
|
||||
// 古いバージョンのRustc(1.84以前)を使用している場合は、`wasm32-wasi` ターゲットを使用してビルドすることもできます。
|
||||
// 詳細については、https://blog.rust-lang.org/2024/04/09/updates-to-rusts-wasi-targets.html を参照してください。
|
||||
|
||||
use yew::prelude::*;
|
||||
use yew::LocalServerRenderer;
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
use yew_router::prelude::*;
|
||||
|
||||
html! {
|
||||
<>
|
||||
<h1>{"Yew WASI SSR demo"}</h1>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn render() -> String {
|
||||
let renderer = LocalServerRenderer::<App>::new();
|
||||
let html_raw = renderer.render().await;
|
||||
|
||||
let mut body = String::new();
|
||||
body.push_str("<body>");
|
||||
body.push_str("<div id='app'>");
|
||||
body.push_str(&html_raw);
|
||||
body.push_str("</div>");
|
||||
body.push_str("</body>");
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
println!("{}", render().await);
|
||||
}
|
||||
```
|
||||
|
||||
例: [wasi_ssr_module](https://github.com/yewstack/yew/tree/master/examples/wasi_ssr_module)
|
||||
|
||||
:::note
|
||||
`wasm32-unknown-unknown` ターゲットを使用してSSRアプリケーションをビルドする場合、`not_browser_env` 機能フラグを使用して、Yew内部のブラウザ固有のAPIへのアクセスを無効にすることができます。これは、Cloudflare Workerのようなサーバーレスプラットフォームで非常に便利です。
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
サーバーサイドレンダリングは現在実験的な機能です。バグを見つけた場合は、[GitHubで報告してください](https://github.com/yewstack/yew/issues/new?assignees=&labels=bug&template=bug_report.md&title=)。
|
||||
|
||||
:::
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
title: 'コールバック関数 (Callbacks)'
|
||||
---
|
||||
|
||||
## コールバック関数 (Callbacks)
|
||||
|
||||
コールバック関数は、Yew でサービス、エージェント、および親コンポーネントと通信するために使用されます。内部的には、それらの型は `Rc` に包まれた `Fn` に過ぎず、クローンを許可します。
|
||||
|
||||
それらには `emit` 関数があり、その `<IN>` 型を引数として取り、それをターゲットが期待するメッセージに変換します。親コンポーネントのコールバック関数が子コンポーネントに props として提供される場合、子コンポーネントはその `update` ライフサイクルフックでコールバック関数の `emit` 関数を呼び出して、メッセージを親コンポーネントに送信できます。`html!` マクロで props として提供されるクロージャまたは関数は、自動的にコールバック関数に変換されます。
|
||||
|
||||
シンプルなコールバック関数の使用例は次のようになります:
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html};
|
||||
|
||||
enum Msg {
|
||||
Clicked,
|
||||
}
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
// highlight-next-line
|
||||
let onclick = ctx.link().callback(|_| Msg::Clicked);
|
||||
html! {
|
||||
// highlight-next-line
|
||||
<button {onclick}>{ "Click" }</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
この関数を `callback` に渡す場合、常に1つの引数を持つ必要があります。例えば、`onclick` ハンドラは `MouseEvent` 型の引数を受け取る関数である必要があります。その後、ハンドラはコンポーネントにどのタイプのメッセージを送信するかを決定できます。このメッセージは無条件に次の更新サイクルにスケジュールされます。
|
||||
|
||||
更新を引き起こす必要がないコールバック関数が必要な場合は、`batch_callback` を使用してください。
|
||||
|
||||
```rust
|
||||
use yew::{events::KeyboardEvent, html, Component, Context, Html};
|
||||
|
||||
enum Msg {
|
||||
Submit,
|
||||
}
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
// highlight-start
|
||||
let onkeypress = ctx.link().batch_callback(|event: KeyboardEvent| {
|
||||
if event.key() == "Enter" {
|
||||
Some(Msg::Submit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<input type="text" {onkeypress} />
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 関連例
|
||||
|
||||
- [Counter](https://github.com/yewstack/yew/tree/master/examples/counter)
|
||||
- [Timer](https://github.com/yewstack/yew/tree/master/examples/timer)
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
title: '高階コンポーネント'
|
||||
---
|
||||
|
||||
いくつかの状況では、構造コンポーネントは特定の機能(例えば Suspense)を直接サポートしていないか、または特定の機能を使用するために大量のボイラープレートコードが必要です(例えば Context)。
|
||||
|
||||
このような場合、高階コンポーネントの関数コンポーネントを作成することをお勧めします。
|
||||
|
||||
## 高階コンポーネントの定義
|
||||
|
||||
高階コンポーネントは、新しい HTML を追加せず、他のコンポーネントをラップして追加機能を提供するコンポーネントです。
|
||||
|
||||
### 例
|
||||
|
||||
Context(コンテキスト)フックを使用し、それを構造コンポーネントに渡す例
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Theme {
|
||||
foreground: String,
|
||||
background: String,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn App() -> Html {
|
||||
let ctx = use_state(|| Theme {
|
||||
foreground: "#000000".to_owned(),
|
||||
background: "#eeeeee".to_owned(),
|
||||
});
|
||||
|
||||
html! {
|
||||
<ContextProvider<Theme> context={(*ctx).clone()}>
|
||||
<ThemedButtonHOC />
|
||||
</ContextProvider<Theme>>
|
||||
}
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
#[function_component]
|
||||
pub fn ThemedButtonHOC() -> Html {
|
||||
let theme = use_context::<Theme>().expect("no ctx found");
|
||||
|
||||
html! {<ThemedButtonStructComponent {theme} />}
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
struct ThemedButtonStructComponent;
|
||||
|
||||
impl Component for ThemedButtonStructComponent {
|
||||
type Message = ();
|
||||
type Properties = Props;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let theme = &ctx.props().theme;
|
||||
html! {
|
||||
<button style={format!(
|
||||
"background: {}; color: {};",
|
||||
theme.background,
|
||||
theme.foreground
|
||||
)}
|
||||
>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
title: '紹介'
|
||||
description: 'Yew のコンポーネント'
|
||||
---
|
||||
|
||||
## コンポーネントとは?
|
||||
|
||||
コンポーネントは Yew の構成要素です。内部状態を管理し、要素を DOM にレンダリングできます。`Component` トレイトを実装することでコンポーネントを作成します。
|
||||
|
||||
## コンポーネントマークアップの作成
|
||||
|
||||
Yew は仮想 DOM を使用して要素を DOM にレンダリングします。仮想 DOM ツリーは `html!` マクロを使用して構築できます。`html!` の構文は HTML に似ていますが、同じではありません。ルールもより厳格です。また、条件付きレンダリングやイテレータを使用したリストのレンダリングなどの強力な機能も提供します。
|
||||
|
||||
:::info
|
||||
[`html!` マクロ、その使用方法、および構文についてさらに詳しく知る](concepts/html/introduction.mdx)
|
||||
:::
|
||||
|
||||
## コンポーネントにデータを渡す
|
||||
|
||||
Yew コンポーネントは _props_ を使用して親コンポーネントと子コンポーネント間で通信します。親コンポーネントは任意のデータを props として子コンポーネントに渡すことができます。Props は HTML 属性に似ていますが、任意の Rust 型を props として渡すことができます。
|
||||
|
||||
:::info
|
||||
[props についてさらに詳しく知る](advanced-topics/struct-components/properties.mdx)
|
||||
:::
|
||||
|
||||
:::info
|
||||
親/子通信以外の通信には、[コンテキスト](../../concepts/contexts.mdx) を使用してください
|
||||
:::
|
|
@ -0,0 +1,273 @@
|
|||
---
|
||||
title: 'ライフサイクル'
|
||||
description: 'コンポーネントとそのライフサイクルフック'
|
||||
---
|
||||
|
||||
`Component` トレイトには、実装する必要がある多くのメソッドがあります。Yew はコンポーネントのライフサイクルのさまざまな段階でこれらのメソッドを呼び出します。
|
||||
|
||||
## ライフサイクル
|
||||
|
||||
:::important ドキュメントの改善
|
||||
`ドキュメントに貢献する:` [カスタムライフサイクルを持つコンポーネントの例を追加](https://github.com/yewstack/yew/issues/1915)
|
||||
:::
|
||||
|
||||
## ライフサイクルメソッド
|
||||
|
||||
### Create
|
||||
|
||||
コンポーネントが作成されるとき、それは親コンポーネントからプロパティを受け取り、それらは `create` メソッドに渡される `Context<Self>` に保存されます。これらのプロパティはコンポーネントの状態を初期化するために使用でき、"link" はコールバックを登録したり、コンポーネントにメッセージを送信したりするために使用できます。
|
||||
|
||||
```rust
|
||||
use yew::{Component, Context, html, Html, Properties};
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct Props;
|
||||
|
||||
pub struct MyComponent;
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = ();
|
||||
type Properties = Props;
|
||||
|
||||
// highlight-start
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
MyComponent
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
// 具体的な実装
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### View
|
||||
|
||||
`view` メソッドは、コンポーネントがDOMにどのようにレンダリングされるべきかを記述することを可能にします。Rust関数を使用してHTMLに似たコードを書くことは非常に混乱する可能性があるため、Yewは`html!`マクロを提供しています。これにより、HTMLおよびSVGノードを宣言し(およびそれらに属性とイベントリスナーを追加し)、子コンポーネントを便利にレンダリングする方法が提供されます。このマクロは、ReactのJSXに似ています(プログラミング言語の違いを除いて)。一つの違いは、YewがSvelteのようなプロパティの簡略化された構文を提供している点です。ここでは、`{onclick}`とだけ書くことができ、`onclick={onclick}`と書く必要はありません。
|
||||
|
||||
```rust
|
||||
use yew::{Component, Context, html, Html, Properties};
|
||||
|
||||
enum Msg {
|
||||
Click,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
struct Props {
|
||||
button_text: String,
|
||||
}
|
||||
|
||||
struct MyComponent;
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = Msg;
|
||||
type Properties = Props;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let onclick = ctx.link().callback(|_| Msg::Click);
|
||||
html! {
|
||||
<button {onclick}>{ &ctx.props().button_text }</button>
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
使用方法の詳細については、[html! ガイド](concepts/html/introduction.mdx) を参照してください。
|
||||
|
||||
### Rendered
|
||||
|
||||
`rendered` コンポーネントライフサイクルメソッドは、`view` が呼び出され、Yew がその結果を DOM にレンダリングした後、ブラウザがページを更新する前に呼び出されます。このメソッドは、コンポーネントが要素をレンダリングした後にのみ完了できる操作を実行したい場合に非常に便利です。また、`first_render` という名前のパラメーターがあり、この関数が最初のレンダリング時に呼び出されたか、後続のレンダリング時に呼び出されたかを判断するために使用できます。
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::{
|
||||
Component, Context, html, Html, NodeRef,
|
||||
};
|
||||
|
||||
pub struct MyComponent {
|
||||
node_ref: NodeRef,
|
||||
}
|
||||
|
||||
impl Component for MyComponent {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
node_ref: NodeRef::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<input ref={self.node_ref.clone()} type="text" />
|
||||
}
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
|
||||
if first_render {
|
||||
if let Some(input) = self.node_ref.cast::<HtmlInputElement>() {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
:::tip note
|
||||
このライフサイクルメソッドは実装する必要はなく、デフォルトでは何も実行しません。
|
||||
:::
|
||||
|
||||
### Update
|
||||
|
||||
コンポーネントとの通信は主にメッセージを通じて行われ、これらのメッセージは `update` ライフサイクルメソッドによって処理されます。これにより、コンポーネントはメッセージに基づいて自身を更新し、再レンダリングが必要かどうかを判断できます。メッセージはイベントリスナー、子コンポーネント、エージェント、サービス、またはフューチャーによって送信されることがあります。
|
||||
|
||||
以下は `update` の実装例です:
|
||||
|
||||
```rust
|
||||
use yew::{Component, Context, html, Html};
|
||||
|
||||
// highlight-start
|
||||
pub enum Msg {
|
||||
SetInputEnabled(bool)
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
struct MyComponent {
|
||||
input_enabled: bool,
|
||||
}
|
||||
|
||||
impl Component for MyComponent {
|
||||
// highlight-next-line
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
input_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
// highlight-start
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::SetInputEnabled(enabled) => {
|
||||
if self.input_enabled != enabled {
|
||||
self.input_enabled = enabled;
|
||||
true // 再レンダリング
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// highlight-end
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
// 具体的な実装
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Changed
|
||||
|
||||
コンポーネントは親コンポーネントによって再レンダリングされることがあります。この場合、新しいプロパティを受け取り、再レンダリングが必要になることがあります。この設計により、プロパティの値を変更するだけで親子コンポーネント間の通信が促進されます。プロパティが変更されると、デフォルトの実装によりコンポーネントが再レンダリングされます。
|
||||
|
||||
### Destroy
|
||||
|
||||
コンポーネントがDOMからアンマウントされると、Yewは`destroy`ライフサイクルメソッドを呼び出します。コンポーネントが破棄される前にクリーンアップ操作を実行する必要がある場合に便利です。このメソッドはオプションであり、デフォルトでは何も実行しません。
|
||||
|
||||
### 無限ループ
|
||||
|
||||
Yewのライフサイクルメソッドでは無限ループが発生する可能性がありますが、それは各レンダリング後に同じコンポーネントを更新し、その更新が再レンダリングを要求する場合にのみ発生します。
|
||||
|
||||
以下は簡単な例です:
|
||||
|
||||
```rust
|
||||
use yew::{Context, Component, Html};
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
||||
// どのメッセージでも常に再レンダリングを要求します
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
// レンダリングする内容は重要ではありません
|
||||
Html::default()
|
||||
}
|
||||
|
||||
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
|
||||
// この新しいメッセージを使用してコンポーネントを更新するように要求します
|
||||
ctx.link().send_message(());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ここで何が起こっているのか見てみましょう:
|
||||
|
||||
1. `create` 関数を使用してコンポーネントを作成します。
|
||||
2. `view` メソッドを呼び出して、Yew がブラウザの DOM にレンダリングする内容を知ることができます。
|
||||
3. `rendered` メソッドを呼び出し、`Context` リンクを使用して更新メッセージをスケジュールします。
|
||||
4. Yew がレンダリングフェーズを完了します。
|
||||
5. Yew はスケジュールされたイベントをチェックし、更新メッセージキューが空でないことを確認してメッセージを処理します。
|
||||
6. `update` メソッドを呼び出し、変更が発生し、コンポーネントが再レンダリングする必要があることを示す `true` を返します。
|
||||
7. ステップ2に戻ります。
|
||||
|
||||
`rendered` メソッドで更新をスケジュールすることは依然として可能であり、これは通常便利ですが、その際にはこのループをどのように終了させるかを考慮してください。
|
||||
|
||||
## 関連タイプ
|
||||
|
||||
`Component` トレイトには、`Message` と `Properties` の2つの関連タイプがあります。
|
||||
|
||||
```rust ,ignore
|
||||
impl Component for MyComponent {
|
||||
type Message = Msg;
|
||||
type Properties = Props;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
`Message` タイプは、イベントが発生した後にコンポーネントにメッセージを送信するために使用されます。例えば、ユーザーがボタンをクリックしたり、ページをスクロールしたりしたときに何かを実行したい場合があります。コンポーネントは通常、複数のイベントに応答する必要があるため、`Message` タイプは通常、処理するイベントごとにバリアントを持つ列挙型です。
|
||||
|
||||
コードベースを整理する際には、`Message` タイプの定義をコンポーネントを定義する同じモジュールに含めるのが賢明です。メッセージタイプの命名に一貫した命名規則を採用することが役立つ場合があります。一つのオプション(唯一のオプションではありませんが)は、タイプを `ComponentNameMsg` と命名することです。例えば、コンポーネントが `Homepage` と名付けられている場合、タイプを `HomepageMsg` と命名することができます。
|
||||
|
||||
```rust
|
||||
enum Msg {
|
||||
Click,
|
||||
FormInput(String)
|
||||
}
|
||||
```
|
||||
|
||||
`Properties` は、親コンポーネントからコンポーネントに渡される情報を表します。この型は `Properties` トレイトを実装する必要があり(通常はそれを派生させる)、特定のプロパティが必須かオプションかを指定できます。コンポーネントの作成および更新時にこの型が使用されます。コンポーネントのモジュール内で `Props` という名前の構造体を作成し、それをコンポーネントの `Properties` 型として使用するのが一般的な方法です。通常、"properties" は "props" と略されます。プロパティは親コンポーネントから渡されるため、アプリケーションのルートコンポーネントは通常、`Properties` 型として `()` を持ちます。ルートコンポーネントにプロパティを指定する場合は、`App::mount_with_props` メソッドを使用します。
|
||||
|
||||
:::info
|
||||
[プロパティに関する詳細はこちら](./properties)
|
||||
:::
|
||||
|
||||
## ライフサイクルコンテキスト
|
||||
|
||||
すべてのコンポーネントライフサイクルメソッドは、コンテキストオブジェクトを受け取ります。このオブジェクトは、コンポーネントのスコープへの参照を提供し、コンポーネントにメッセージを送信したり、コンポーネントに渡されたプロパティを取得したりすることができます。
|
|
@ -0,0 +1,134 @@
|
|||
---
|
||||
title: 'プロパティ (Props)'
|
||||
description: '親子コンポーネント間の通信'
|
||||
---
|
||||
|
||||
プロパティ (Properties) は、子コンポーネントと親コンポーネントの間で通信を可能にします。各コンポーネントには、親コンポーネントから渡される内容を記述するための関連プロパティ型があります。理論的には、これは `Properties` トレイトを実装した任意の型である可能性がありますが、実際には、各フィールドがプロパティを表す構造体であるべきです。
|
||||
|
||||
## 派生マクロ
|
||||
|
||||
`Properties` トレイトを自分で実装する必要はありません。`#[derive(Properties)]` を使用して実装を自動生成できます。`Properties` を派生する型は `PartialEq` も実装する必要があります。
|
||||
|
||||
### フィールド属性
|
||||
|
||||
`Properties` を派生する際、デフォルトではすべてのフィールドが必須です。以下の属性を使用すると、他の値が設定されていない限り、プロパティに初期値を提供できます。
|
||||
|
||||
:::tip
|
||||
プロパティは Rustdoc によって生成されたドキュメントには表示されません。プロパティのドキュメント文字列には、そのプロパティがオプションであるかどうか、または特別なデフォルト値があるかどうかを記載する必要があります。
|
||||
:::
|
||||
|
||||
#### `#[prop_or_default]`
|
||||
|
||||
フィールド型のデフォルト値を使用してプロパティ値を初期化します。これは `Default` トレイトを使用します。
|
||||
|
||||
#### `#[prop_or(value)]`
|
||||
|
||||
`value` を使用してプロパティ値を初期化します。`value` はフィールド型を返す任意の式である可能性があります。例えば、ブールプロパティをデフォルトで `true` にするには、属性 `#[prop_or(true)]` を使用します。
|
||||
|
||||
#### `#[prop_or_else(function)]`
|
||||
|
||||
`function` を呼び出してプロパティ値を初期化します。`function` は `FnMut() -> T` のシグネチャを持つ必要があります。ここで、`T` はフィールド型です。
|
||||
|
||||
## `PartialEq`
|
||||
|
||||
`Properties` は `PartialEq` を実装する必要があります。これにより、Yew はそれらを比較し、変更があった場合に `changed` メソッドを呼び出すことができます。
|
||||
|
||||
## Properties のパフォーマンスオーバーヘッド
|
||||
|
||||
内部プロパティは参照カウントされたポインタに基づいて格納されます。これにより、コンポーネントツリーに渡されるプロパティにはポインタのみが渡され、プロパティ全体をクローンすることによる高価なパフォーマンスオーバーヘッドを回避できます。
|
||||
|
||||
:::tip
|
||||
`AttrValue` を使用してください。これは、クローンが必要な String やその他の類似の型を使用せずに済むようにするために提供されているカスタムプロパティ値型です。
|
||||
:::
|
||||
|
||||
## 例
|
||||
|
||||
```rust
|
||||
use yew::Properties;
|
||||
/// virtual_dom から AttrValue をインポート
|
||||
use yew::virtual_dom::AttrValue;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum LinkColor {
|
||||
Blue,
|
||||
Red,
|
||||
Green,
|
||||
Black,
|
||||
Purple,
|
||||
}
|
||||
|
||||
fn create_default_link_color() -> LinkColor {
|
||||
LinkColor::Blue
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct LinkProps {
|
||||
/// リンクにはターゲットが必要です
|
||||
href: AttrValue,
|
||||
/// また、String ではなく AttrValue を使用していることに注意してください
|
||||
text: AttrValue,
|
||||
/// リンクの色、デフォルトは `Blue`
|
||||
#[prop_or_else(create_default_link_color)]
|
||||
color: LinkColor,
|
||||
/// 値が None の場合、ビュー関数はサイズを指定しません
|
||||
#[prop_or_default]
|
||||
size: Option<u32>,
|
||||
/// ビュー関数がアクティブを指定しない場合、デフォルトは true
|
||||
#[prop_or(true)]
|
||||
active: bool,
|
||||
}
|
||||
```
|
||||
|
||||
## Props マクロ
|
||||
|
||||
`yew::props!` マクロを使用すると、`html!` マクロと同じ方法でプロパティを構築できます。
|
||||
|
||||
このマクロは構造体の式と同じ構文を使用しますが、属性や基本式 (`Foo { ..base }`) を使用することはできません。型パスはプロパティ (`path::to::Props`) に直接指すことも、コンポーネントの関連プロパティ (`MyComp::Properties`) に指すこともできます。
|
||||
|
||||
```rust
|
||||
use yew::{props, Properties, virtual_dom::AttrValue};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum LinkColor {
|
||||
Blue,
|
||||
Red,
|
||||
Green,
|
||||
Black,
|
||||
Purple,
|
||||
}
|
||||
|
||||
fn create_default_link_color() -> LinkColor {
|
||||
LinkColor::Blue
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct LinkProps {
|
||||
/// リンクにはターゲットが必要です
|
||||
href: AttrValue,
|
||||
/// また、String ではなく AttrValue を使用していることに注意してください
|
||||
text: AttrValue,
|
||||
/// リンクの色、デフォルトは `Blue`
|
||||
#[prop_or_else(create_default_link_color)]
|
||||
color: LinkColor,
|
||||
/// 値が None の場合、ビュー関数はサイズを指定しません
|
||||
#[prop_or_default]
|
||||
size: Option<u32>,
|
||||
/// ビュー関数がアクティブを指定しない場合、デフォルトは true
|
||||
#[prop_or(true)]
|
||||
active: bool,
|
||||
}
|
||||
|
||||
impl LinkProps {
|
||||
/// この関数は href と text を String として受け取ります
|
||||
/// `AttrValue::from` を使用してそれらを `AttrValue` に変換できます
|
||||
pub fn new_link_with_size(href: String, text: String, size: u32) -> Self {
|
||||
// highlight-start
|
||||
props! {LinkProps {
|
||||
href: AttrValue::from(href),
|
||||
text: AttrValue::from(text),
|
||||
size,
|
||||
}}
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
title: '参照 (Refs)'
|
||||
description: 'DOM への越境アクセスを実現する'
|
||||
---
|
||||
|
||||
`ref` キーワードは、任意の HTML 要素やコンポーネントに使用して、その要素に付随する DOM `Element` を取得できます。これにより、`view` ライフサイクルメソッドの外で DOM を変更することができます。
|
||||
|
||||
これは、canvas 要素を取得したり、ページの異なる部分にスクロールしたりするのに便利です。例えば、コンポーネントの `rendered` メソッドで `NodeRef` を使用すると、`view` からレンダリングされた後に canvas 要素に描画呼び出しを行うことができます。
|
||||
|
||||
構文は次のとおりです:
|
||||
|
||||
```rust
|
||||
use web_sys::Element;
|
||||
use yew::{html, Component, Context, Html, NodeRef};
|
||||
|
||||
struct Comp {
|
||||
node_ref: NodeRef,
|
||||
}
|
||||
|
||||
impl Component for Comp {
|
||||
type Message = ();
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
// highlight-next-line
|
||||
node_ref: NodeRef::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
// highlight-next-line
|
||||
<div ref={self.node_ref.clone()}></div>
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||
// highlight-start
|
||||
let has_attributes = self.node_ref
|
||||
.cast::<Element>()
|
||||
.unwrap()
|
||||
.has_attributes();
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 関連例
|
||||
|
||||
- [ノード参照](https://github.com/yewstack/yew/tree/master/examples/node_refs)
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
title: 'スコープ'
|
||||
description: 'コンポーネントのスコープ'
|
||||
---
|
||||
|
||||
## コンポーネントの `Scope<_>` インターフェース
|
||||
|
||||
`Scope` は、メッセージを介してコールバックを作成し、自身を更新するメカニズムです。コンポーネントに渡されるコンテキストオブジェクトで `link()` を呼び出すことで、その参照を取得します。
|
||||
|
||||
### `send_message`
|
||||
|
||||
この関数は、コンポーネントにメッセージを送信できます。メッセージは `update` メソッドによって処理され、コンポーネントが再レンダリングするかどうかを決定します。
|
||||
|
||||
### `send_message_batch`
|
||||
|
||||
この関数は、コンポーネントに複数のメッセージを同時に送信できます。これは `send_message` に似ていますが、任意のメッセージが `update` メソッドで `true` を返す場合、バッチ内のすべてのメッセージの処理が完了した後にコンポーネントが再レンダリングされます。
|
||||
|
||||
指定された引数ベクターが空の場合、この関数は何も実行しません。
|
||||
|
||||
### `callback`
|
||||
|
||||
コールバックを作成し、実行時にコンポーネントにメッセージを送信します。内部的には、提供されたクロージャが返すメッセージを使用して `send_message` を呼び出します。
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html};
|
||||
|
||||
enum Msg {
|
||||
Text(String),
|
||||
}
|
||||
|
||||
struct Comp;
|
||||
|
||||
impl Component for Comp {
|
||||
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
// テキストを受け取り、それを `Msg::Text` メッセージバリアントとしてコンポーネントに送信するコールバックを作成します。
|
||||
// highlight-next-line
|
||||
let cb = ctx.link().callback(|text: String| Msg::Text(text));
|
||||
|
||||
// 上の行は冗長であり、より明確にするために次のように簡略化できます:
|
||||
// highlight-next-line
|
||||
let cb = ctx.link().callback(Msg::Text);
|
||||
|
||||
// `Msg::Text("Hello World!")` をコンポーネントに送信します。
|
||||
// highlight-next-line
|
||||
cb.emit("Hello World!".to_owned());
|
||||
|
||||
html! {
|
||||
// ここに HTML を配置
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `batch_callback`
|
||||
|
||||
バッチメッセージを送信するコールバックを作成します。このメソッドに渡されるクロージャはメッセージを返す必要はありません。代わりに、クロージャは `Vec<Msg>` または `Option<Msg>` を返すことができます。ここで、`Msg` はコンポーネントのメッセージタイプです。
|
||||
|
||||
`Vec<Msg>` はバッチメッセージとして扱われ、内部的に `send_message_batch` を使用します。
|
||||
|
||||
`Option<Msg>` は値が `Some` の場合に `send_message` を呼び出します。値が `None` の場合は何も実行しません。これは、更新が不要な場合に使用できます。
|
||||
|
||||
これは、これらの型に対してのみ実装された `SendAsMessage` トレイトを使用して実現されています。独自の型に対して `SendAsMessage` を実装することで、`batch_callback` でそれらを使用できるようになります。
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: 'エージェント (Agents)'
|
||||
description: 'Yew のエージェントシステム'
|
||||
---
|
||||
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
import ThemedImage from '@theme/ThemedImage'
|
||||
|
||||
エージェント (Agents) は、タスクを Web Workers にオフロードする方法です。
|
||||
|
||||
エージェントが並行して動作できるようにするために、Yew は [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) を使用します。
|
||||
|
||||
## ライフサイクル
|
||||
|
||||
<!--
|
||||
The diagram is produced with nomnoml (nomnoml.com),
|
||||
The code can be found in the <desc> tag of the svgs.
|
||||
-->
|
||||
|
||||
<ThemedImage
|
||||
alt="agent lifecycle diagram"
|
||||
sources={{
|
||||
light: useBaseUrl('/img/agent-lifecycle-light.svg'),
|
||||
dark: useBaseUrl('/img/agent-lifecycle-dark.svg'),
|
||||
}}
|
||||
/>
|
||||
|
||||
## エージェントの種類
|
||||
|
||||
### 範囲
|
||||
|
||||
- 公開 - 任意の時点で、公開エージェントのインスタンスは最大で1つだけです。ブリッジはWeb Worker内でエージェントを生成するか、既に生成されたエージェントに接続します。ブリッジがこのエージェントに接続されていない場合、エージェントは消滅します。
|
||||
|
||||
- 私有 - 新しいブリッジごとにWeb Worker内で新しいエージェントを生成します。これは、ブラウザと通信する共有だが独立した動作をコンポーネントから移動するのに適しています。接続されたブリッジが破棄されると、エージェントは消滅します。
|
||||
|
||||
- グローバル \(WIP\)
|
||||
|
||||
## エージェントとコンポーネント間の通信
|
||||
|
||||
### 通信ブリッジ (Bridges)
|
||||
|
||||
通信ブリッジ(ブリッジ)は、コンポーネントとエージェント間の通信チャネルです。これにより、コンポーネントはエージェントにメッセージを送信し、エージェントからのメッセージを受信できます。
|
||||
|
||||
`use_bridge` フックは、関数コンポーネント内でブリッジを作成する機能も提供します。
|
||||
|
||||
### ディスパッチャー (Dispatchers)
|
||||
|
||||
ディスパッチャー(ディスパッチャー)は、コンポーネントとエージェント間の一方向通信を可能にし、コンポーネントがこの方法でエージェントにメッセージを送信します。
|
||||
|
||||
## オーバーヘッド
|
||||
|
||||
エージェントはWeb Workers(つまり、私有および公開)を使用します。メッセージの送受信時にシリアル化オーバーヘッドが発生します。エージェントは [bincode](https://github.com/bincode-org/bincode) を使用して他のスレッドと通信するため、コストは関数を呼び出すだけの場合よりもはるかに高くなります。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [web_worker_fib](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib) の例は、コンポーネントがエージェントにメッセージを送信し、エージェントからのメッセージを受信する方法を示しています。
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
title: 'classes! マクロを使用して CSS クラスを処理する'
|
||||
description: '便利なマクロを使用して CSS クラスを処理する'
|
||||
comment: 'ファイルを短くシンプルに保つようにしてください。これは、読者が Yew のコンポーネントをより理解しやすくするためであり、正確な API ドキュメントを提供するためではありません。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
Yew はネイティブの CSS-in-Rust ソリューションを提供していませんが、HTML の `class` 属性とプログラム的に対話する方法を提供することでスタイルを支援します。
|
||||
|
||||
## `classes!` マクロ
|
||||
|
||||
`classes!` マクロと関連する `Classes` 構造体は、HTML クラスの使用を簡素化します:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Literal" label="Literal">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!("container")}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Multiple" label="Multiple">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!("class-1", "class-2")}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="String" label="String">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(String::from("class-1 class-2"))}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Optional" label="Optional">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(Some("class"))} />
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Vector" label="Vector">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(vec!["class-1", "class-2"])}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Slice" label="Slice">
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div class={classes!(["class-1", "class-2"].as_ref())}></div>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
詳細な CSS に関する内容は[こちらのドキュメント](../../more/css)をご覧ください。
|
||||
|
||||
## インラインスタイル
|
||||
|
||||
現在、Yew は `style` 属性を使用して指定されたインラインスタイルを処理するための特別な支援ツールを提供していませんが、他の HTML 属性と同様に処理することができます:
|
||||
|
||||
```rust
|
||||
use yew::{classes, html};
|
||||
|
||||
html! {
|
||||
<div style="color: red;"></div>
|
||||
};
|
||||
```
|
||||
|
||||
詳細な CSS に関する内容は[こちらのドキュメント](../../more/css)をご覧ください。
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: 'html! マクロを使用してHTMLを処理する'
|
||||
description: 'これはHTMLですが、完全にそうではありません!'
|
||||
comment: 'ファイルを短く簡潔に保つようにしてください。これは、読者がYewのコンポーネントをより簡単に理解できるようにするためであり、正確なAPIドキュメントを提供するためではありません。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
`html!` マクロを使用して、HTML に似た式を記述できます。Yew はバックグラウンドでそれを DOM を表現する Rust コードに変換します。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let my_header: Html = html! {
|
||||
<img src="img_girl.jpg" alt="Girl in a jacket" width="500" height="600" />
|
||||
};
|
||||
```
|
||||
|
||||
フォーマットされた式と同様に、波括弧を使用して周囲のコンテキストの値を HTML に埋め込むことができます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
|
||||
let header_text = "Hello world".to_string();
|
||||
let header_html: Html = html! {
|
||||
<h1>{header_text}</h1>
|
||||
};
|
||||
|
||||
let count: usize = 5;
|
||||
let counter_html: Html = html! {
|
||||
<p>{"My age is: "}{count}</p>
|
||||
};
|
||||
|
||||
let combined_html: Html = html! {
|
||||
<div>{header_html}{counter_html}</div>
|
||||
};
|
||||
```
|
||||
|
||||
`html!` を使用する際の重要なルールの 1 つは、1 つのラッピングノードしか返せないということです。複数の要素のリストをレンダリングするために、`html!` は空のタグ(フラグメント)の使用を許可しています。空のタグは名前のないタグで、それ自体は HTML 要素を生成しません。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Invalid" label="Invalid">
|
||||
|
||||
```rust , compile_fail
|
||||
use yew::html;
|
||||
|
||||
// エラー:ルート HTML 要素は1つだけ許可されています
|
||||
html! {
|
||||
|
||||
<div></div>
|
||||
<p></p>
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Valid" label="Valid">
|
||||
|
||||
```rust
|
||||
use yew::html;
|
||||
|
||||
// 修正:HTML 空のタグを使用してラップする
|
||||
html! {
|
||||
<>
|
||||
<div></div>
|
||||
<p></p>
|
||||
</>
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
詳細については、[HTML の詳細](concepts/html/introduction.mdx)を参照してください。
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: 'JavaScript と Rust'
|
||||
description: 'Rust で JavaScript を使用する'
|
||||
comment: 'ファイルを簡潔に保つようにしてください。これは、読者が Yew のコンポーネントをより理解しやすくするためのものであり、正確な API ドキュメントを提供するためのものではありません。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
> Yew は、再利用可能な UI 部分に必要なすべてのコンテンツを1か所に集める一方で、必要に応じて基盤技術へのアクセスも維持します。
|
||||
|
||||
今日現在、WebAssembly は DOM との相互作用を完全にはサポートしていません。これは、Yew でも時々 JavaScript の呼び出しに依存することを意味します。次に、関係するライブラリの概要を示します。
|
||||
|
||||
## wasm-bindgen
|
||||
|
||||
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) は、JavaScript と Rust 関数の間に呼び出しの橋を架けるライブラリとツールです。
|
||||
|
||||
彼らの[ドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/)と私たちの[クイックガイド](./wasm-bindgen.mdx)を強くお勧めします。
|
||||
|
||||
## web-sys
|
||||
|
||||
[`web-sys` crate](https://crates.io/crates/web-sys) は Web API のバインディングを提供し、Rust で処理され安全な方法で JavaScript コードを書くことを可能にします。
|
||||
|
||||
例:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="JS" label="JS">
|
||||
|
||||
```js
|
||||
let document = window.document
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="RS" label="RS">
|
||||
|
||||
```rust ,no_run
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
use web_sys::window;
|
||||
|
||||
let document = window()
|
||||
.expect_throw("window is undefined")
|
||||
.document()
|
||||
.expect_throw("document is undefined");
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
繰り返しになりますが、彼らの[ドキュメント](https://rustwasm.github.io/docs/wasm-bindgen/)と私たちの[クイックガイド](./web-sys.mdx)を強くお勧めします。
|
|
@ -0,0 +1,177 @@
|
|||
---
|
||||
title: 'wasm-bindgen'
|
||||
sidebar_label: wasm-bindgen
|
||||
---
|
||||
|
||||
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) は、JavaScript と Rust 関数の間に呼び出しブリッジを作成するためのライブラリおよびツールです。これは [Rust と WebAssembly ワーキンググループ](https://rustwasm.github.io/) によって Rust で構築されました。
|
||||
|
||||
Yew は `wasm-bindgen` を使用して、いくつかのクレートを介してブラウザと対話します:
|
||||
|
||||
- [`js-sys`](https://crates.io/crates/js-sys)
|
||||
- [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||||
- [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||||
- [`web-sys`](https://crates.io/crates/web-sys)
|
||||
|
||||
このセクションでは、これらのクレートをより抽象的なレベルから探求し、Yew での `wasm-bindgen` API の理解と使用を容易にします。`wasm-bindgen` および関連するクレートに関する詳細なガイドについては、[`wasm-bindgen` ガイド](https://rustwasm.github.io/docs/wasm-bindgen/) を参照してください。
|
||||
|
||||
上記のクレートのドキュメントについては、[`wasm-bindgen docs.rs`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/index.html) を参照してください。
|
||||
|
||||
:::tip
|
||||
`wasm-bindgen` doc.rs 検索を使用して、`wasm-bindgen` を使用してインポートされたブラウザ API および JavaScript タイプを見つけます。
|
||||
:::
|
||||
|
||||
## [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||||
|
||||
このクレートは、上記の他のクレートに多くの構成要素を提供します。このセクションでは、`wasm-bindgen` クレートの主要な領域の 2 つ、つまりマクロと、何度も目にするタイプ/トレイトのいくつかについてのみ説明します。
|
||||
|
||||
### `#[wasm_bindgen]` マクロ
|
||||
|
||||
`#[wasm_bindgen]` マクロは Rust と JavaScript の間のインターフェースを提供し、両者の間で変換を行うシステムを提供します。このマクロの使用はより高度であり、外部の JavaScript ライブラリを使用する場合を除いて使用しないでください。`js-sys` および `web-sys` クレートは、組み込みの JavaScript タイプおよびブラウザ API に対して `wasm-bindgen` 定義を提供します。
|
||||
|
||||
`#[wasm-bindgen]` マクロを使用して、特定のバージョンの [`console.log`](https://developer.mozilla.org/en-US/docs/Web/API/Console/log) 関数をインポートする簡単な例を見てみましょう。
|
||||
|
||||
```rust ,no_run
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// まず、`web_sys` を使用せずに `console.log` を手動でバインドしてみましょう。
|
||||
// ここでは、手動で `#[wasm_bindgen]` アノテーションを書きます。プログラムの正確性はこれらのアノテーションの正確性に依存します!
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
// ここで `js_namespace` を使用して `console.log(..)` をバインドします。`log(..)` だけではありません。
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
|
||||
// `console.log` は多態的なので、複数のシグネチャを使用してバインドできます。
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_u32(a: u32);
|
||||
|
||||
// 複数の引数も可能です!
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_many(a: &str, b: &str);
|
||||
}
|
||||
|
||||
// インポートされた関数を使用します!
|
||||
log("Hello from Rust!");
|
||||
log_u32(42);
|
||||
log_many("Logging", "many values!");
|
||||
```
|
||||
|
||||
_この例は、[1.2 `wasm-bindgen` ガイドの console.log を使用する](https://rustwasm.github.io/docs/wasm-bindgen/examples/console-log.html) に基づいています。_
|
||||
|
||||
### 継承のシミュレーション
|
||||
|
||||
JavaScript クラス間の継承は、JavaScript 言語のコア機能であり、DOM(ドキュメントオブジェクトモデル)はそれを中心に設計されています。`wasm-bindgen` を使用して型をインポートする際にも、それらの継承関係を記述する属性を追加できます。
|
||||
|
||||
Rust では、この継承関係は [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) と [`AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) トレイトを使用して表現されます。ここで例を挙げると役立つかもしれません。例えば、`A`、`B`、`C` という 3 つの型があり、`C` が `B` を拡張し、`B` が `A` を拡張しているとします。
|
||||
|
||||
これらの型をインポートする際、`#[wasm-bindgen]` マクロは次のように `Deref` と `AsRef` トレイトを実装します:
|
||||
|
||||
- `C` は `B` に `Deref` できます
|
||||
- `B` は `A` に `Deref` できます
|
||||
- `C` は `B` に `AsRef` できます
|
||||
- `C` と `B` はどちらも `A` に `AsRef` できます
|
||||
|
||||
これらの実装により、`C` のインスタンスで `A` のメソッドを呼び出したり、`C` を `&B` または `&A` として使用したりできます。
|
||||
|
||||
注意すべき点は、`#[wasm-bindgen]` を使用してインポートされたすべての型には同じルート型があり、それを上記の例の `A` と見なすことができるということです。この型は [`JsValue`](#jsvalue) であり、以下にそのセクションがあります。
|
||||
|
||||
_[`wasm-bindgen` ガイドの extends セクション](https://rustwasm.github.io/docs/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_
|
||||
|
||||
### [`JsValue`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)
|
||||
|
||||
これは JavaScript が所有するオブジェクトの表現であり、`wasm-bindgen` のルートキャプチャ型です。`wasm-bindgen` からの任意の型は `JsValue` です。これは、JavaScript には強い型システムがないため、変数 `x` を受け取る任意の関数がその型を定義しないため、`x` は有効な JavaScript 値である可能性があるためです。したがって `JsValue` です。`JsValue` を受け取るインポート関数や型を使用している場合、技術的には任意のインポート値が有効です。
|
||||
|
||||
`JsValue` は関数で受け取ることができますが、その関数は特定の型のみを受け取る可能性があり、それがパニックを引き起こす可能性があります。したがって、元の `wasm-bindgen` API を使用する場合は、インポートされた JavaScript のドキュメントを確認して、その値が特定の型でない場合に例外(パニック)を引き起こすかどうかを確認してください。
|
||||
|
||||
_[`JsValue` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)_
|
||||
|
||||
### [`JsCast`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)
|
||||
|
||||
Rust には強い型システムがありますが、JavaScript にはありません😞。Rust がこれらの強い型を維持しながらも便利であるために、WebAssembly ワーキンググループは非常に巧妙な機能 `JsCast` を提案しました。これは、ある JavaScript "型" から別の "型" への変換を支援するものです。これは曖昧に聞こえますが、ある型が別の型であることがわかっている場合、`JsCast` の関数を使用してある型から別の型にジャンプできます。`web-sys`、`wasm_bindgen`、`js-sys` を使用する際にこの機能を理解しておくと便利です。これらのクレートから多くの型が `JsCast` を実装していることに気付くでしょう。
|
||||
|
||||
`JsCast` はチェック付きとチェックなしの変換メソッドを提供します。したがって、実行時にオブジェクトがどの型であるかわからない場合は、変換を試みることができ、失敗する可能性のある型として [`Option`](https://doc.rust-lang.org/std/option/enum.Option.html) や [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) を返します。
|
||||
|
||||
一般的な例は [`web-sys`](./web-sys.mdx) で、イベントのターゲットを取得しようとする場合です。ターゲット要素が何であるかを知っているかもしれませんが、[`web_sys::Event`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html) API は常に [`Option<web_sys::EventTarget>`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target) を返します。
|
||||
その要素型に変換する必要があり、そのメソッドを呼び出すことができます。
|
||||
|
||||
```rust
|
||||
// このトレイトを最初にインポートする必要があります
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement};
|
||||
|
||||
fn handle_event(event: Event) {
|
||||
let target: EventTarget = event
|
||||
.target()
|
||||
.expect("I'm sure this event has a target!");
|
||||
|
||||
// もしかしたらターゲットは選択要素かもしれませんか?
|
||||
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
|
||||
// 別のことをする
|
||||
return;
|
||||
}
|
||||
|
||||
// それが選択要素でないことが確実であれば、入力要素であることが確実です!
|
||||
let input_element: HtmlInputElement = target.unchecked_into();
|
||||
}
|
||||
```
|
||||
|
||||
[`dyn_ref`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_ref) メソッドはチェック付きの変換であり、`Option<&T>` を返します。これは、変換が失敗した場合に元の型を再度使用できることを意味し、`None` を返します。 [`dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) メソッドは `self` を消費し、Rust の `into` メソッドの規約に従い、`Result<T, Self>` 型を返します。変換が失敗した場合、元の `Self` 値は `Err` に返されます。再試行するか、元の型で他の操作を行うことができます。
|
||||
|
||||
_[`JsCast` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)._
|
||||
|
||||
### [`Closure`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)
|
||||
|
||||
`Closure` 型は、Rust のクロージャを JavaScript に渡す方法を提供します。健全性の理由から、JavaScript に渡されるクロージャは `'static` ライフタイムを持つ必要があります。
|
||||
|
||||
この型は「ハンドル」であり、破棄されると、それが参照する JS クロージャを無効にします。`Closure` が破棄された後、JS 内のクロージャの使用はすべて例外を引き起こします。
|
||||
|
||||
`Closure` は、[`&js_sys::Function`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Function.html) 型を受け取る `js-sys` または `web-sys` API を使用する際に一般的に使用されます。Yew で `Closure` を使用する例は、[Events](../html/events.mdx) ページの[Using `Closure` セクション](../html/events.mdx#using-closure-verbose) にあります。
|
||||
|
||||
_[`Closure` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)._
|
||||
|
||||
## [`js-sys`](https://crates.io/crates/js-sys)
|
||||
|
||||
`js-sys` クレートは、JavaScript の標準組み込みオブジェクトのバインディング/インポートを提供します。これには、それらのメソッドやプロパティが含まれます。
|
||||
|
||||
これは Web API を含みません。Web API は [`web-sys`](./web-sys.mdx) の役割です!
|
||||
|
||||
_[`js-sys` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/js_sys/index.html)._
|
||||
|
||||
## [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||||
|
||||
`wasm-bindgen-futures` クレートは、JavaScript の Promise 型を Rust の [`Future`](https://doc.rust-lang.org/stable/std/future/trait.Future.html) として扱うためのブリッジを提供し、Rust の Future を JavaScript の Promise に変換するユーティリティを含みます。Rust(wasm)で非同期または他のブロッキング作業を処理する際に役立ち、JavaScript のイベントや JavaScript I/O プリミティブと対話する能力を提供します。
|
||||
|
||||
現在、このクレートには3つの主要なインターフェースがあります:
|
||||
|
||||
1. [`JsFuture`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/struct.JsFuture.html) -
|
||||
[`Promise`](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Promise.html) を使用して構築された型で、`Future<Output=Result<JsValue, JsValue>>` として使用できます。`Promise` が解決されると、この `Future` は `Ok` に解決され、`Promise` が拒否されると `Err` に解決され、それぞれ `Promise` の解決または拒否の値を含みます。
|
||||
|
||||
2. [`future_to_promise`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) -
|
||||
Rust の `Future<Output=Result<JsValue, JsValue>>` を JavaScript の `Promise` に変換します。Future の結果は、JavaScript 内の解決または拒否された `Promise` に変換されます。
|
||||
|
||||
3. [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) -
|
||||
現在のスレッドで `Future<Output = ()>` を生成します。これは、Rust 内で Future を実行する最良の方法であり、JavaScript に送信するのではなく、Rust 内で実行します。
|
||||
|
||||
_[`wasm-bindgen-futures` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/index.html)._
|
||||
|
||||
### [`spawn_local`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)
|
||||
|
||||
`spawn_local` は、非同期 API を使用するライブラリを使用する際に、Yew で `wasm-bindgen-futures` クレートの最も一般的に使用される部分です。
|
||||
|
||||
```rust ,no_run
|
||||
use web_sys::console;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
async fn my_async_fn() -> String { String::from("Hello") }
|
||||
|
||||
spawn_local(async {
|
||||
let mut string = my_async_fn().await;
|
||||
string.push_str(", world!");
|
||||
// "Hello, world!" を出力します
|
||||
console::log_1(&string.into());
|
||||
});
|
||||
```
|
||||
|
||||
Yew はいくつかの API に futures のサポートを追加しており、特に `async` ブロックを受け入れる `callback_future` を作成できることが注目されます。これは内部的に `spawn_local` を使用しています。
|
||||
|
||||
_[`spawn_local` ドキュメント](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)._
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
title: 'web-sys'
|
||||
description: 'web-sys クレートは Web API のバインディングを提供します。'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
[`web-sys` クレート](https://crates.io/crates/web-sys) は Web API のバインディングを提供します。これはブラウザの WebIDL から生成されるため、名前が長くなったり、型が曖昧になったりすることがあります。
|
||||
|
||||
## `web-sys` の特性 (features)
|
||||
|
||||
`web-sys` クレートで全ての特性を有効にすると、Wasm アプリケーションに多くの冗長性が追加される可能性があります。この問題を解決するために、ほとんどの型は特性を有効にすることで制御され、アプリケーションに必要な型だけを含めることができます。Yew は `web-sys` のいくつかの特性を有効にし、その公開 API でいくつかの型を公開しています。通常、`web-sys` を依存関係として追加する必要があります。
|
||||
|
||||
## `web-sys` の継承
|
||||
|
||||
[継承のシミュレーション](./wasm-bindgen.mdx#simulating-inheritance)のセクションでは、Rust が通常 JavaScript の継承をシミュレートする方法を提供していることがわかります。これは `web-sys` で非常に重要です。ある型にどのようなメソッドがあるかを理解するためには、その継承を理解する必要があります。
|
||||
|
||||
このセクションでは、特定の要素を見て、Rust で [`Deref::deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html#tymethod.deref) を呼び出して、その値が [`JsValue`](./wasm-bindgen.mdx#jsvalue) になるまでの継承をリストします。
|
||||
|
||||
```rust
|
||||
use std::ops::Deref;
|
||||
use web_sys::{
|
||||
Element,
|
||||
EventTarget,
|
||||
HtmlElement,
|
||||
HtmlTextAreaElement,
|
||||
Node,
|
||||
};
|
||||
|
||||
fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
|
||||
// HtmlTextAreaElement は HTML の <textarea> です。
|
||||
let html_element: &HtmlElement = text_area.deref();
|
||||
|
||||
let element: &Element = html_element.deref();
|
||||
|
||||
let node: &Node = element.deref();
|
||||
|
||||
let event_target: &EventTarget = node.deref();
|
||||
|
||||
// 注意: ここで web-sys タイプから js-sys クレート内の組み込み JavaScript タイプに移行しました。
|
||||
let object: &js_sys::Object = event_target.deref();
|
||||
|
||||
// 注意: ここで js-sys タイプから wasm-bindgen クレートのルート JsValue に移行しました。
|
||||
let js_value: &wasm_bindgen::JsValue = object.deref();
|
||||
|
||||
// このように deref を使用することで、継承ツリーを手動でたどる必要があります。
|
||||
// しかし、HtmlTextAreaElement タイプで JsValue メソッドを呼び出すことができます。
|
||||
assert!(!text_area.is_string());
|
||||
|
||||
// この空の関数は、HtmlTextAreaElement を &EventTarget として渡すことができることを示すためのものです。
|
||||
fn this_function_only_takes_event_targets(targets: &EventTarget) {};
|
||||
|
||||
// コンパイラはここでタイプを一致させるために deref チェーンを下にたどります。
|
||||
this_function_only_takes_event_targets(&text_area);
|
||||
|
||||
// AsRef 実装により、HtmlTextAreaElement を &EventTarget として扱うことができます。
|
||||
let event_target: &EventTarget = text_area.as_ref();
|
||||
}
|
||||
```
|
||||
|
||||
[`wasm-bindgen` ガイドの `web-sys` 継承](https://rustwasm.github.io/wasm-bindgen/web-sys/inheritance.html)
|
||||
|
||||
## `NodeRef` の `Node`
|
||||
|
||||
Yew は [`NodeRef`](concepts/function-components/node-refs.mdx) を使用して、[`html!`](concepts/html/introduction.mdx) マクロによって作成された `Node` の参照を保持する方法を提供します。`NodeRef` の `Node` は [`web_sys::Node`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Node.html) を指します。`NodeRef::get` メソッドは `Option<Node>` 値を返しますが、Yew ではほとんどの場合、この値を特定の要素に変換して、その特定のメソッドを使用することを望みます。存在する場合、[`JsCast`](./wasm-bindgen.mdx#JsCast) を使用して `Node` 値を変換できますが、Yew はこの変換を実行するための `NodeRef::cast` メソッドを提供しているため、`JsCast` 特性のために `wasm-bindgen` 依存関係を含める必要はありません。
|
||||
|
||||
以下の2つのコードブロックは本質的に同じです。最初のものは `NodeRef::cast` を使用し、2 番目のものは `NodeRef::get` が返す `web_sys::Node` 上で [`JsCast::dyn_into`](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) を使用しています。
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Using NodeRef::cast" label="Using NodeRef::cast">
|
||||
|
||||
```rust
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::NodeRef;
|
||||
|
||||
fn with_node_ref_cast(node_ref: NodeRef) {
|
||||
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
|
||||
// HtmlInputElement をここで処理します
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Using NodeRef::get" label="Using NodeRef::get">
|
||||
|
||||
```rust
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlInputElement;
|
||||
use yew::NodeRef;
|
||||
|
||||
fn with_jscast(node_ref: NodeRef) {
|
||||
if let Some(input) = node_ref
|
||||
.get()
|
||||
.and_then(|node| node.dyn_into::<HtmlInputElement>().ok()) {
|
||||
// HtmlInputElement をここで処理します
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Rust にリファクタリングされた JavaScript の例
|
||||
|
||||
このセクションでは、Web API と対話する JavaScript コードの例を Rust の `web-sys` にリファクタリングする方法を示します。
|
||||
|
||||
### JavaScript の例
|
||||
|
||||
```js
|
||||
document.getElementById('mousemoveme').onmousemove = (e) => {
|
||||
// e はマウスイベントオブジェクトです
|
||||
var rect = e.target.getBoundingClientRect()
|
||||
var x = e.clientX - rect.left // 要素内の x 位置。
|
||||
var y = e.clientY - rect.top // 要素内の y 位置。
|
||||
console.log('Left? : ' + x + ' ; Top? : ' + y + '.')
|
||||
}
|
||||
```
|
||||
|
||||
### `web-sys` を使用して書き直した例
|
||||
|
||||
`web-sys` のみを使用して、上記の JavaScript の例は次のように実装できます:
|
||||
|
||||
```toml title=Cargo.toml
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
# 使用したいすべての web-sys 機能を有効にする必要があります!
|
||||
features = [
|
||||
"console",
|
||||
"Document",
|
||||
"HtmlElement",
|
||||
"MouseEvent",
|
||||
"DomRect",
|
||||
]
|
||||
```
|
||||
|
||||
```rust ,no_run
|
||||
use wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use web_sys::{console, Document, HtmlElement, MouseEvent};
|
||||
|
||||
let mousemove = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|e| {
|
||||
let rect = e
|
||||
.target()
|
||||
.expect("mouse event doesn't have a target")
|
||||
.dyn_into::<HtmlElement>()
|
||||
.expect("event target should be of type HtmlElement")
|
||||
.get_bounding_client_rect();
|
||||
let x = (e.client_x() as f64) - rect.left();
|
||||
let y = (e.client_y() as f64) - rect.top();
|
||||
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
||||
}));
|
||||
|
||||
Document::new()
|
||||
.expect("global document not set")
|
||||
.get_element_by_id("mousemoveme")
|
||||
.expect("element with id `mousemoveme` not present")
|
||||
.unchecked_into::<HtmlElement>()
|
||||
.set_onmousemove(mousemove.as_ref().dyn_ref());
|
||||
|
||||
// 現在、イベントが発生したときにクロージャがメモリに残るように、`mousemove` クロージャを保存する必要があります。
|
||||
```
|
||||
|
||||
このバージョンはより冗長ですが、その一部は失敗した型が私たちにいくつかの関数呼び出しに保持しなければならない不変条件を思い出させるためです。これらの不変条件が守られないと、Rust ではパニックが発生します。もう一つの冗長な部分は、特定のメソッドを呼び出すために異なる型を特定の型に変換するための `JsCast` の呼び出しです。
|
||||
|
||||
### Yew で書き直した例
|
||||
|
||||
Yew では、主に [`Callback`](concepts/function-components/callbacks.mdx) を作成して [`html!`](concepts/html/introduction.mdx) マクロで使用するため、例はこの方法を使用します。上記の方法を完全にコピーするのではなく、この方法を使用します:
|
||||
|
||||
```toml title=Cargo.toml
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
# `get_bounding_client_rect` メソッドを使用するには、`DomRect` 特性を有効にする必要があります。
|
||||
features = [
|
||||
"console",
|
||||
"HtmlElement",
|
||||
"MouseEvent",
|
||||
"DomRect",
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
```rust
|
||||
use web_sys::{console, HtmlElement, MouseEvent};
|
||||
use yew::{
|
||||
html,
|
||||
Callback, TargetCast,
|
||||
};
|
||||
|
||||
let onmousemove = Callback::from(|e: MouseEvent| {
|
||||
if let Some(target) = e.target_dyn_into::<HtmlElement>() {
|
||||
let rect = target.get_bounding_client_rect();
|
||||
let x = (e.client_x() as f64) - rect.left();
|
||||
let y = (e.client_y() as f64) - rect.top();
|
||||
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
||||
}
|
||||
});
|
||||
|
||||
html! {
|
||||
<div id="mousemoveme" {onmousemove}></div>
|
||||
};
|
||||
```
|
||||
|
||||
## 追加の依存ライブラリ
|
||||
|
||||
`web-sys` は Web API の生のバインディングであるため、Rust で使用する際にはいくつかの困難が伴います。これは、`web-sys` が Rust や強い型システムのために設計されていないためです。そこで、コミュニティのクレートが `web-sys` に対する抽象化を提供し、Rust の慣習により適した API を提供しています。
|
||||
|
||||
_[追加の依存ライブラリ一覧](/community/external-libs)_
|
|
@ -0,0 +1,188 @@
|
|||
---
|
||||
title: 'コンテキスト (Contexts)'
|
||||
sidebar_label: コンテキスト
|
||||
description: 'コンテキストを使用して深くネストされたデータを渡す'
|
||||
---
|
||||
|
||||
通常、データは props を介して親コンポーネントから子コンポーネントに渡されます。
|
||||
しかし、多くの中間コンポーネントを介してデータを渡す必要がある場合や、アプリケーション内の多くのコンポーネントが同じ情報を必要とする場合、props を介してデータを渡すことは冗長で煩わしいものになります。
|
||||
コンテキストはこの問題を解決し、親コンポーネントがデータをその下のツリー内の任意のコンポーネントに渡すことを可能にし、props を介してデータを渡す必要がなくなります。
|
||||
|
||||
## Props を使用する際の問題:"Prop Drilling"
|
||||
|
||||
[props](./function-components/properties.mdx) を介してデータを親コンポーネントから直接子コンポーネントに渡すことは良い方法です。
|
||||
しかし、深くネストされたコンポーネントツリーを介してデータを渡す必要がある場合や、複数のコンポーネントが同じデータを共有する必要がある場合、props を渡すことは煩雑になります。
|
||||
一般的なデータ共有の解決策は、データを共通の祖先に持ち上げ、子コンポーネントがそれを props として受け取るようにすることです。
|
||||
しかし、これにより props が複数のコンポーネントを介して渡される必要がある場合があります。
|
||||
この状況は "Prop Drilling" と呼ばれます。
|
||||
|
||||
以下の例を考えてみましょう。これは props を介してテーマを渡しています:
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html, Properties, function_component};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Theme {
|
||||
foreground: String,
|
||||
background: String,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct NavbarProps {
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Navbar(props: &NavbarProps) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<Title theme={props.theme.clone()}>
|
||||
{ "App title" }
|
||||
</Title>
|
||||
<NavButton theme={props.theme.clone()}>
|
||||
{ "Somewhere" }
|
||||
</NavButton>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct ThemeProps {
|
||||
theme: Theme,
|
||||
children: Html,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn Title(_props: &ThemeProps) -> Html {
|
||||
html! {
|
||||
// impl
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn NavButton(_props: &ThemeProps) -> Html {
|
||||
html! {
|
||||
// impl
|
||||
}
|
||||
}
|
||||
|
||||
/// アプリのルート
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let theme = Theme {
|
||||
foreground: "yellow".to_owned(),
|
||||
background: "pink".to_owned(),
|
||||
};
|
||||
|
||||
html! {
|
||||
<Navbar {theme} />
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
私たちはテーマ設定を `Navbar` に渡して、それが `Title` と `NavButton` に到達するようにしています。
|
||||
もし `Title` と `NavButton` のようなテーマにアクセスする必要があるコンポーネントが、prop を介さずに直接テーマにアクセスできるとしたら、もっと良いでしょう。
|
||||
コンテキストはこの問題を解決し、親コンポーネントがデータ(この場合はテーマ)をその子コンポーネントに渡すことを可能にします。
|
||||
|
||||
## コンテキストの使用
|
||||
|
||||
### ステップ 1:コンテキストの提供
|
||||
|
||||
コンテキストを消費するには、コンテキストプロバイダーが必要です。`ContextProvider<T>` は、`T` がコンテキスト構造体として使用されるプロバイダーです。
|
||||
`T` は `Clone` と `PartialEq` を実装する必要があります。`ContextProvider` は、その子コンポーネントがコンテキストを持つコンポーネントです。
|
||||
コンテキストが変更されると、子コンポーネントは再レンダリングされます。データを渡すための構造体が定義されます。`ContextProvider` は次のように使用できます:
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
/// アプリのテーマ
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Theme {
|
||||
foreground: String,
|
||||
background: String,
|
||||
}
|
||||
|
||||
/// メインコンポーネント
|
||||
#[function_component]
|
||||
pub fn App() -> Html {
|
||||
let ctx = use_state(|| Theme {
|
||||
foreground: "#000000".to_owned(),
|
||||
background: "#eeeeee".to_owned(),
|
||||
});
|
||||
|
||||
html! {
|
||||
// `ctx` は `Rc<UseStateHandle<Theme>>` 型であり、`Theme` が必要です
|
||||
// したがって、デリファレンスします。
|
||||
<ContextProvider<Theme> context={(*ctx).clone()}>
|
||||
// ここにあるすべての子コンポーネントとその子コンポーネントは、このコンテキストにアクセスします。
|
||||
<Toolbar />
|
||||
</ContextProvider<Theme>>
|
||||
}
|
||||
}
|
||||
|
||||
/// ツールバー
|
||||
/// このコンポーネントはコンテキストにアクセスできます。
|
||||
#[function_component]
|
||||
pub fn Toolbar() -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<ThemedButton />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
/// `Toolbar` 内に配置されたボタン
|
||||
/// このコンポーネントは、コンポーネントツリー内の `ThemeContextProvider` の子コンポーネントであるため、
|
||||
/// コンテキストにアクセスできます。
|
||||
#[function_component]
|
||||
pub fn ThemedButton() -> Html {
|
||||
let theme = use_context::<Theme>().expect("no ctx found");
|
||||
|
||||
html! {
|
||||
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
|
||||
{ "Click me!" }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ステップ 2:コンテキストの使用
|
||||
|
||||
#### 関数コンポーネント
|
||||
|
||||
`use_context` フックは、関数コンポーネント内でコンテキストを使用するために使用されます。
|
||||
詳細については、[use_context ドキュメント](https://yew-rs-api.web.app/next/yew/functional/fn.use_context.html) を参照してください。
|
||||
|
||||
#### 構造体コンポーネント
|
||||
|
||||
構造体コンポーネント内でコンテキストを使用するには、2つの方法があります:
|
||||
|
||||
- [高階コンポーネント](../advanced-topics/struct-components/hoc.mdx):高階関数コンポーネントがコンテキストを使用し、必要なデータを構造体コンポーネントに渡します。
|
||||
- 構造体コンポーネント内で直接コンテキストを使用します。詳細については、[構造体コンポーネントのコンシューマーとしての例](https://github.com/yewstack/yew/tree/master/examples/contexts/src/struct_component_subscriber.rs) を参照してください。
|
||||
|
||||
## 使用シナリオ
|
||||
|
||||
通常、ツリーの異なる部分のリモートコンポーネントでデータを使用する必要がある場合、コンテキストが役立ちます。
|
||||
以下はいくつかの例です:
|
||||
|
||||
- **テーマ**:アプリケーションのトップにコンテキストを配置し、アプリケーションのテーマを保持し、視覚的な外観を調整するために使用できます(上記の例を参照)。
|
||||
- **現在のユーザーアカウント**:多くの場合、コンポーネントは現在ログインしているユーザーを知る必要があります。コンテキストを使用して、現在のユーザーオブジェクトをコンポーネントに提供できます。
|
||||
|
||||
### コンテキストを使用する前の考慮事項
|
||||
|
||||
コンテキストは非常に使いやすいですが、それが誤用/過度に使用される可能性もあります。
|
||||
複数のレベル深いコンポーネントに props を共有するためにコンテキストを使用できるからといって、必ずしもそうすべきではありません。
|
||||
|
||||
例えば、コンポーネントを抽出して、そのコンポーネントを別のコンポーネントの子コンポーネントとして渡すことができます。
|
||||
例えば、`Layout` コンポーネントが `articles` を prop として受け取り、それを `ArticleList` コンポーネントに渡す場合、
|
||||
`Layout` コンポーネントをリファクタリングして、子コンポーネントを props として受け取り、`<Layout> <ArticleList {articles} /> </Layout>` と表示するようにするべきです。
|
||||
|
||||
## 子コンポーネントのコンテキスト値を変更する
|
||||
|
||||
Rust の所有権ルールにより、コンテキストには子コンポーネントが呼び出せる `&mut self` メソッドを持つことができません。
|
||||
コンテキストの値を変更するには、リデューサーと組み合わせて使用する必要があります。これは、[`use_reducer`](https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html) フックを使用して行うことができます。
|
||||
|
||||
[コンテキストの例](https://github.com/yewstack/yew/tree/master/examples/contexts) は、可変コンテキストの使用を示しています。
|
||||
|
||||
## さらなる読み物
|
||||
|
||||
- [コンテキストの例](https://github.com/yewstack/yew/tree/master/examples/contexts)
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: 'コールバック (Callbacks)'
|
||||
---
|
||||
|
||||
コールバック関数は、コンポーネントツリー内で情報を上向きに伝達したり、イベント処理中に他のコンポーネント(例えばエージェントやDOM)と通信したりするために使用されます。内部的には、コールバック関数の型は単なる `Fn` であり、安価にクローンできるように `Rc` に包まれています。
|
||||
|
||||
コールバック関数を手動で呼び出したい場合は、`emit` 関数を使用できます。
|
||||
|
||||
```rust
|
||||
use yew::{html, Component, Context, Html, Callback};
|
||||
|
||||
let cb: Callback<String, String> = Callback::from(move |name: String| {
|
||||
format!("Bye {}", name)
|
||||
});
|
||||
|
||||
let result = cb.emit(String::from("Bob")); // コールバック関数を呼び出す
|
||||
// web_sys::console::log_1(&result.into()); // コメントを解除すると、「Bye Bob」 が出力されます
|
||||
```
|
||||
|
||||
## コールバック関数をプロパティとして渡す
|
||||
|
||||
yew で一般的なパターンは、コールバック関数を作成し、それをプロパティとして子コンポーネントに渡すことです。
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties, Callback};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
pub on_name_entry: Callback<String>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(props: &Props) -> Html {
|
||||
|
||||
props.on_name_entry.emit(String::from("Bob"));
|
||||
|
||||
html! { "Hello" }
|
||||
}
|
||||
|
||||
// 次にプロパティ (Props) を提供します
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let on_name_entry: Callback<String> = Callback::from(move |name: String| {
|
||||
let greeting = format!("Hey, {}!", name);
|
||||
// web_sys::console::log_1(&greeting.into()); // コメントを解除すると、ここにテキストが出力されます
|
||||
});
|
||||
|
||||
html! { <HelloWorld {on_name_entry} /> }
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## DOM イベントとコールバック関数
|
||||
|
||||
コールバック関数は、DOM イベントに接続するためにも使用されます。
|
||||
|
||||
例えば、ここではユーザーがボタンをクリックしたときに呼び出されるコールバック関数を定義します:
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties, Callback};
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let onclick = Callback::from(move |_| {
|
||||
let greeting = String::from("Hi there");
|
||||
// web_sys::console::log_1(&greeting.into()); // コメントを解除すると、ここにテキストが出力されます
|
||||
});
|
||||
|
||||
html! {
|
||||
<button {onclick}>{ "Click" }</button>
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
title: '子要素 (Children)'
|
||||
---
|
||||
|
||||
`Children` は特別なプロパティタイプで、HTMLの子要素のようにネストされた `Html` を受け取ることができます。
|
||||
|
||||
```rust
|
||||
use yew::{function_component, html, Html, Properties};
|
||||
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
html! {
|
||||
// highlight-start
|
||||
<HelloWorld>
|
||||
<span>{"Hey what is up ;)"}</span>
|
||||
<h1>{"THE SKY"}</h1>
|
||||
</HelloWorld>
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props {
|
||||
// highlight-next-line
|
||||
pub children: Html, // `children` キーは重要です!
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn HelloWorld(props: &Props) -> Html {
|
||||
html! {
|
||||
<div class="very-stylized-container">
|
||||
// highlight-next-line
|
||||
{ props.children.clone() } // この方法で子要素を転送できます
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: 'コンポーネント間の通信'
|
||||
---
|
||||
|
||||
## 親コンポーネントから子コンポーネントへのメッセージ送信
|
||||
|
||||
データを [props](./properties) として渡すと、再レンダリングが発生し、これが子コンポーネントにメッセージを渡す方法です。
|
||||
|
||||
## 子コンポーネントから親コンポーネントへのメッセージ送信
|
||||
|
||||
props を介してコールバックを渡し、子コンポーネントはイベントでそれを呼び出すことができます。[例](callbacks#passing-callbacks-as-props)
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: 'ジェネリックコンポーネント'
|
||||
description: '関数コンポーネントの #[function_component] 属性'
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
`#[function_component]` 属性は、ジェネリックコンポーネントを作成するためのジェネリック関数にも適用されます。
|
||||
|
||||
```rust
|
||||
use std::fmt::Display;
|
||||
use yew::{function_component, html, Properties, Html};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct Props<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
data: T,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
|
||||
where
|
||||
T: PartialEq + Clone + Into<Html>,
|
||||
{
|
||||
html! {
|
||||
<p>
|
||||
{ props.data.clone().into() }
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
// その後、このように使用できます
|
||||
html! {
|
||||
<MyGenericComponent<i32> data=123 />
|
||||
};
|
||||
|
||||
// または
|
||||
html! {
|
||||
<MyGenericComponent<String> data={"foo".to_string()} />
|
||||
};
|
||||
```
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
title: 'カスタムフック'
|
||||
---
|
||||
|
||||
## カスタムフックの定義
|
||||
|
||||
コンポーネントのステートフルなロジックは、カスタムフックを作成することで再利用可能な関数として抽出できます。
|
||||
|
||||
例えば、`window` オブジェクト上のイベントをリッスンするイベントリスナーを作成したいとします。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use gloo::events::EventListener;
|
||||
use gloo::utils::window;
|
||||
use std::mem::drop;
|
||||
|
||||
|
||||
#[function_component(ShowStorageChanged)]
|
||||
pub fn show_storage_changed() -> Html {
|
||||
let state_storage_changed = use_state(|| false);
|
||||
|
||||
{
|
||||
let state_storage_changed = state_storage_changed.clone();
|
||||
use_effect(|| {
|
||||
let listener = EventListener::new(&window(), "storage", move |_| state_storage_changed.set(true));
|
||||
|
||||
move || { drop(listener); }
|
||||
});
|
||||
}
|
||||
|
||||
html! { <div>{"Storage Event Fired: "}{*state_storage_changed}</div> }
|
||||
}
|
||||
```
|
||||
|
||||
このコードには問題があります。ロジックは他のコンポーネントで再利用できません。異なるイベントをリッスンする別のコンポーネントを作成する場合、コードをコピーするのではなく、ロジックをカスタムフックに移すことができます。
|
||||
|
||||
まず、`use_event` という新しい関数を作成します。`use_` プレフィックスは関数がフックであることを示します。この関数はイベントターゲット、イベントタイプ、およびコールバックを受け取ります。すべてのフックはその関数定義に `#[hook]` とマークする必要があります。
|
||||
|
||||
```rust
|
||||
use web_sys::{Event, EventTarget};
|
||||
use std::borrow::Cow;
|
||||
use gloo::events::EventListener;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[hook]
|
||||
pub fn use_event<E, F>(target: &EventTarget, event_type: E, callback: F)
|
||||
where
|
||||
E: Into<Cow<'static, str>>,
|
||||
F: Fn(&Event) + 'static,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
このシンプルなフックは、組み込みのフックを組み合わせることで作成できます。この例では、`use_effect_with` フックを使用します。これにより、フックのパラメータが変更されたときにイベントリスナーを再作成できます。
|
||||
|
||||
```rust
|
||||
use yew::prelude::*;
|
||||
use web_sys::{Event, EventTarget};
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use gloo::events::EventListener;
|
||||
|
||||
#[hook]
|
||||
pub fn use_event<E, F>(target: &EventTarget, event_type: E, callback: F)
|
||||
where
|
||||
E: Into<Cow<'static, str>>,
|
||||
F: Fn(Event) + 'static,
|
||||
{
|
||||
#[derive(PartialEq, Clone)]
|
||||
struct EventDependents {
|
||||
target: EventTarget,
|
||||
event_type: Cow<'static, str>,
|
||||
callback: Callback<Event>,
|
||||
}
|
||||
|
||||
let deps = EventDependents {
|
||||
target: target.clone(),
|
||||
event_type: event_type.into(),
|
||||
callback: Callback::from(callback),
|
||||
};
|
||||
|
||||
use_effect_with(
|
||||
deps,
|
||||
|deps| {
|
||||
let EventDependents {
|
||||
target,
|
||||
event_type,
|
||||
callback,
|
||||
} = deps.clone();
|
||||
|
||||
let listener = EventListener::new(&target, event_type, move |e| {
|
||||
callback.emit(e.clone());
|
||||
});
|
||||
|
||||
move || {
|
||||
drop(listener);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
この方法はほとんどすべてのケースで有効ですが、私たちがすでに使用しているような基本的なフックを作成するためには使用できません。
|
||||
|
||||
[docs.rs](https://docs.rs/yew) 上のドキュメントや `hooks` ディレクトリを参照して、事前定義されたフックの実装を確認してください。
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue