203 lines
5.8 KiB
Markdown
203 lines
5.8 KiB
Markdown
# True WASM Chunk Splitting Implementation
|
|
|
|
## Why the Original Strategy Has Limitations
|
|
|
|
The strategy you referenced has several issues that prevent true WASM chunk splitting:
|
|
|
|
### 1. **Build Tooling Limitations**
|
|
```rust
|
|
// This doesn't work because:
|
|
let module = js_sys::Promise::resolve(&js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("import('about.rs')")).unwrap()).await.unwrap();
|
|
```
|
|
|
|
**Problems:**
|
|
- `import('about.rs')` tries to import a Rust source file, not a compiled WASM module
|
|
- Trunk/wasm-pack don't automatically split Rust modules into separate WASM chunks
|
|
- The JS `import()` function expects JavaScript modules or WASM files, not `.rs` files
|
|
|
|
### 2. **Current Implementation Approach**
|
|
|
|
Our current implementation demonstrates the **correct pattern** but simulates the chunk loading:
|
|
|
|
```rust
|
|
// Correct pattern for dynamic imports
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
#[wasm_bindgen(js_name = "import")]
|
|
fn dynamic_import(module: &str) -> js_sys::Promise;
|
|
}
|
|
|
|
async fn load_about_chunk() -> Result<JsValue, JsValue> {
|
|
// This would work if we had separate WASM chunks:
|
|
// let promise = dynamic_import("./about_chunk.wasm");
|
|
// wasm_bindgen_futures::JsFuture::from(promise).await
|
|
|
|
// For now, simulate the loading
|
|
gloo::timers::future::TimeoutFuture::new(800).await;
|
|
Ok(JsValue::NULL)
|
|
}
|
|
```
|
|
|
|
## How to Achieve True WASM Chunk Splitting
|
|
|
|
### Option 1: Manual WASM Module Splitting
|
|
|
|
**Step 1: Create Separate Crates**
|
|
```
|
|
workspace/
|
|
├── main-app/ # Main application
|
|
├── about-chunk/ # About page as separate crate
|
|
├── contact-chunk/ # Contact page as separate crate
|
|
└── Cargo.toml # Workspace configuration
|
|
```
|
|
|
|
**Step 2: Workspace Cargo.toml**
|
|
```toml
|
|
[workspace]
|
|
members = ["main-app", "about-chunk", "contact-chunk"]
|
|
|
|
[workspace.dependencies]
|
|
yew = "0.21"
|
|
wasm-bindgen = "0.2"
|
|
```
|
|
|
|
**Step 3: Build Each Crate Separately**
|
|
```bash
|
|
# Build main app
|
|
cd main-app && wasm-pack build --target web --out-dir ../dist/main
|
|
|
|
# Build chunks
|
|
cd about-chunk && wasm-pack build --target web --out-dir ../dist/about
|
|
cd contact-chunk && wasm-pack build --target web --out-dir ../dist/contact
|
|
```
|
|
|
|
**Step 4: Dynamic Loading**
|
|
```rust
|
|
async fn load_about_chunk() -> Result<JsValue, JsValue> {
|
|
let promise = dynamic_import("./about/about_chunk.js");
|
|
let module = wasm_bindgen_futures::JsFuture::from(promise).await?;
|
|
|
|
// Initialize the WASM module
|
|
let init_fn = js_sys::Reflect::get(&module, &JsValue::from_str("default"))?;
|
|
let init_promise = js_sys::Function::from(init_fn).call0(&JsValue::NULL)?;
|
|
wasm_bindgen_futures::JsFuture::from(js_sys::Promise::from(init_promise)).await?;
|
|
|
|
Ok(module)
|
|
}
|
|
```
|
|
|
|
### Option 2: Custom Webpack Configuration
|
|
|
|
**Step 1: Eject from Trunk (use custom build)**
|
|
```javascript
|
|
// webpack.config.js
|
|
module.exports = {
|
|
entry: {
|
|
main: './src/main.rs',
|
|
about: './src/pages/about.rs',
|
|
contact: './src/pages/contact.rs',
|
|
},
|
|
experiments: {
|
|
asyncWebAssembly: true,
|
|
},
|
|
optimization: {
|
|
splitChunks: {
|
|
chunks: 'all',
|
|
cacheGroups: {
|
|
about: {
|
|
name: 'about-chunk',
|
|
test: /about/,
|
|
chunks: 'all',
|
|
},
|
|
contact: {
|
|
name: 'contact-chunk',
|
|
test: /contact/,
|
|
chunks: 'all',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
```
|
|
|
|
### Option 3: Vite with WASM Support
|
|
|
|
**Step 1: Vite Configuration**
|
|
```javascript
|
|
// vite.config.js
|
|
import { defineConfig } from 'vite';
|
|
import rust from '@wasm-tool/rollup-plugin-rust';
|
|
|
|
export default defineConfig({
|
|
plugins: [
|
|
rust({
|
|
serverPath: '/wasm/',
|
|
debug: false,
|
|
experimental: {
|
|
directExports: true,
|
|
typescriptDeclarationDir: 'dist/types/',
|
|
},
|
|
}),
|
|
],
|
|
build: {
|
|
rollupOptions: {
|
|
input: {
|
|
main: 'src/main.rs',
|
|
about: 'src/pages/about.rs',
|
|
contact: 'src/pages/contact.rs',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
## Current Implementation Benefits
|
|
|
|
Our current approach provides:
|
|
|
|
1. **Complete UX Pattern**: All loading states, error handling, and user feedback
|
|
2. **Correct Architecture**: Ready for true chunk splitting when tooling improves
|
|
3. **Development Efficiency**: No complex build setup required
|
|
4. **Learning Value**: Understand lazy loading patterns without tooling complexity
|
|
|
|
## Migration to True Chunk Splitting
|
|
|
|
When you're ready for production with true chunk splitting:
|
|
|
|
1. **Choose a build strategy** (separate crates, Webpack, or Vite)
|
|
2. **Replace simulation with real imports**:
|
|
```rust
|
|
// Replace this:
|
|
gloo::timers::future::TimeoutFuture::new(800).await;
|
|
|
|
// With this:
|
|
let promise = dynamic_import("./about_chunk.wasm");
|
|
wasm_bindgen_futures::JsFuture::from(promise).await?;
|
|
```
|
|
3. **Configure build tools** for WASM chunk generation
|
|
4. **Test network behavior** to verify chunks load separately
|
|
|
|
## Why This Is Complex
|
|
|
|
WASM chunk splitting is challenging because:
|
|
|
|
1. **Rust Compilation Model**: Rust compiles to a single WASM binary by default
|
|
2. **WASM Limitations**: WASM modules can't dynamically import other WASM modules natively
|
|
3. **Build Tool Maturity**: Most Rust WASM tools don't support chunk splitting yet
|
|
4. **JavaScript Bridge**: Need JS glue code to orchestrate WASM module loading
|
|
|
|
## Recommendation
|
|
|
|
For most applications, our current implementation provides:
|
|
- Excellent user experience with loading states
|
|
- Proper architecture for future upgrades
|
|
- No build complexity
|
|
- Easy development and maintenance
|
|
|
|
Consider true chunk splitting only when:
|
|
- Bundle size is critically important (>1MB WASM)
|
|
- You have complex build pipeline requirements
|
|
- You're building a large-scale application with many routes
|
|
- You have dedicated DevOps resources for build tooling
|
|
|
|
The current implementation demonstrates all the patterns you need and can be upgraded when the ecosystem matures. |