[WIP] Server Components#776
Conversation
# Conflicts: # packages/jaspr/lib/src/browser/browser_binding.dart # packages/jaspr/lib/src/browser/clients.dart # packages/jaspr/lib/src/browser/js_data.dart # packages/jaspr/lib/src/foundation/sync.dart # packages/jaspr/lib/src/framework/render_element.dart # packages/jaspr/lib/src/server/document/document.dart # packages/jaspr/lib/src/server/document/document_base.dart # packages/jaspr/lib/src/server/document/document_binding.dart # packages/jaspr/lib/src/server/document/document_file.dart # packages/jaspr/lib/src/server/markup_renderer.dart # packages/jaspr_builder/lib/src/client/client_part_builder.dart
# Conflicts: # packages/jaspr/lib/src/browser/clients.dart # packages/jaspr_builder/lib/src/client/client_module_builder.dart
# Conflicts: # packages/jaspr/lib/browser.dart # packages/jaspr/lib/src/browser/clients.dart # packages/jaspr/lib/src/components/raw_text/raw_text_web.dart # packages/jaspr/lib/src/foundation/options.dart # packages/jaspr/lib/src/server/adapters/client_component_adapter.dart # packages/jaspr/lib/src/server/adapters/client_script_adapter.dart # packages/jaspr/lib/src/server/adapters/sync_script_adapter.dart # packages/jaspr/lib/src/server/server_binding.dart # packages/jaspr_builder/lib/src/client/client_module_builder.dart # packages/jaspr_builder/lib/src/client/client_registry_builder.dart
# Conflicts: # packages/jaspr/lib/src/browser/clients.dart # packages/jaspr/lib/src/components/raw_text/raw_text_web.dart # packages/jaspr/lib/src/foundation/marker_utils.dart # packages/jaspr/lib/src/foundation/options.dart # packages/jaspr/lib/src/server/adapters/client_component_adapter.dart # packages/jaspr/lib/src/server/adapters/client_script_adapter.dart # packages/jaspr/lib/src/server/server_binding.dart # packages/jaspr/test/browser/hydration/hydration_browser_test.dart # packages/jaspr_builder/lib/src/client/client_module_builder.dart # packages/jaspr_builder/lib/src/codec/codecs.dart
# Conflicts: # packages/jaspr/lib/src/browser/clients.dart # packages/jaspr/lib/src/client/custom_node_component.dart # packages/jaspr/lib/src/dom/raw_text/raw_text_web.dart # packages/jaspr/lib/src/foundation/options.dart # packages/jaspr/lib/src/server/adapters/client_component_adapter.dart # packages/jaspr/lib/src/server/adapters/client_script_adapter.dart # packages/jaspr/lib/src/server/server_binding.dart # packages/jaspr/test/client/hydration/hydration_browser_test.dart # packages/jaspr_builder/lib/src/client/client_module_builder.dart # packages/jaspr_builder/lib/src/codec/codec_module_builder.dart # packages/jaspr_builder/test/client/sources/client_basic.dart # packages/jaspr_builder/test/client/sources/client_model_class.dart # packages/jaspr_builder/test/client/sources/client_model_extension.dart # packages/jaspr_builder/test/sync/sources/sync_model_class.dart # packages/jaspr_builder/test/sync/sources/sync_model_extension.dart
# Conflicts: # packages/jaspr/lib/src/client/component_anchors.dart # packages/jaspr/lib/src/server/adapters/client_component_adapter.dart # packages/jaspr/lib/src/server/server_binding.dart # packages/jaspr_builder/lib/src/client/client_module_builder.dart # packages/jaspr_builder/lib/src/codec/codecs.dart # packages/jaspr_builder/lib/src/sync/sync_mixins_builder.dart # packages/jaspr_builder/test/client/sources/bundle.dart # packages/jaspr_builder/test/client/sources/client_basic.dart # packages/jaspr_builder/test/client/sources/client_model_class.dart # packages/jaspr_builder/test/client/sources/client_model_extension.dart # packages/jaspr_builder/test/codec/sources/bundle.dart # packages/jaspr_builder/test/codec/sources/model_class.dart # packages/jaspr_builder/test/codec/sources/model_extension.dart # packages/jaspr_builder/test/sync/sources/sync_basic.dart # packages/jaspr_builder/test/sync/sources/sync_multi.dart
|
To view this pull requests documentation preview, visit the following URL: Documentation is deployed and generated using docs.page. |
Package Version ReportThe following packages have been updated: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## next #776 +/- ##
==========================================
+ Coverage 68.56% 69.51% +0.94%
==========================================
Files 212 215 +3
Lines 11867 12311 +444
==========================================
+ Hits 8137 8558 +421
- Misses 3730 3753 +23 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
parlough
left a comment
There was a problem hiding this comment.
Exciting!! I haven't tried it out yet, but some small questions and a few minor suggestions to consider:
38c987f to
e46ec38
Compare
parlough
left a comment
There was a problem hiding this comment.
Still love it. Some small fixes, a few suggestions, and a question but otherwise I think it's in a great place for developers to start trying it out.
| /// | ||
| const factory Component.wrapElement({ | ||
| Key? key, | ||
| const factory Component.apply({ |
There was a problem hiding this comment.
Should the ComponentFactoryMigration be updated to use this new factory instead?
| import 'components/expandable_card.dart'; | ||
|
|
||
| void main() { | ||
| Jaspr.initializeAll(options: defaultServerOptions); |
There was a problem hiding this comment.
Should this be initializeApp?
|
|
||
| You can use `@client` components normally as any other component. | ||
|
|
||
| To have separate interactive parts of your site, simply annotate multiple components with `@client`. Only the outermost `@client` component of a nested sub-tree will be hydrated on the client, while nested client components will be safely ignored. |
There was a problem hiding this comment.
This prose needs a slight update to account for server components in the sub tree.
|
|
||
| When building modern web applications, we want both rich, fluid interactivity and fast initial page loads. | ||
|
|
||
| Typically, once you make a parent component interactive by decorating it with `@client`, Jaspr must compile it and **all of its nested children** to JavaScript so they can run in the browser. Without Server Components, the moment a layout or parent container needs some client-side interactivity, every single child in its subtree gets pulled into the client-side JavaScript bundle—even if those children are completely static. |
There was a problem hiding this comment.
Consider adjusting this to account for possibility of compiling to Wasm.
| Typically, once you make a parent component interactive by decorating it with `@client`, Jaspr must compile it and **all of its nested children** to JavaScript so they can run in the browser. Without Server Components, the moment a layout or parent container needs some client-side interactivity, every single child in its subtree gets pulled into the client-side JavaScript bundle—even if those children are completely static. | |
| Typically, once you make a parent component interactive by decorating it with `@client`, Jaspr must compile it and **all of its nested children** so they can run in the browser. Without Server Components, the moment a layout or parent container needs some client-side interactivity, every single child in its subtree gets pulled into the client-side bundle—even if those children are completely static. |
| **Server Components** solve this by enabling **surgical hydration**. They allow you to render server-side-only components inside parts of an interactive, hydrated client-side application. With this pattern, you can build interactive client-side wrappers (like expandable cards, navigation shells, sidebars, or tabs) while completely skipping compiling or shipping the static or heavy content inside them. | ||
|
|
||
| As a result: | ||
| - **Reduced Bundle Size:** The Dart code for server-only components is never compiled to JavaScript or shipped to the browser, significantly reducing the application's JavaScript footprint. |
There was a problem hiding this comment.
| - **Reduced Bundle Size:** The Dart code for server-only components is never compiled to JavaScript or shipped to the browser, significantly reducing the application's JavaScript footprint. | |
| - **Reduced Bundle Size:** The Dart code for server-only components is never compiled to JavaScript/Wasm or shipped to the browser, significantly reducing the application's client-side footprint. |
|
|
||
| Instead, Server Components are a design paradigm enabled by Jaspr's compilation and hydration model. You achieve this simply by **passing components as parameters** (such as a `child` component, or lists/maps of components) to a component annotated with [`@client`](/api/utils/at_client). | ||
|
|
||
| ## A Simple Example: Expandable Card |
There was a problem hiding this comment.
I like the idea of building something to show the feature. It works well here and is the easiest way to build an understanding of it.
| Here is the interactive card component: | ||
|
|
||
| ```dart title="lib/components/expandable_card.dart" | ||
| import 'package:jaspr/jaspr.dart'; |
There was a problem hiding this comment.
| import 'package:jaspr/jaspr.dart'; | |
| import 'package:jaspr/dom.dart'; | |
| import 'package:jaspr/jaspr.dart'; |
| // WARNING: If showContent is false during SSR, 'widget.content' is not built on the server. | ||
| // When showContent becomes true on the client, it will render as EMPTY because | ||
| // the client does not have the Dart code to construct the server component. | ||
| if (showContent) widget.content, |
There was a problem hiding this comment.
| // WARNING: If showContent is false during SSR, 'widget.content' is not built on the server. | |
| // When showContent becomes true on the client, it will render as EMPTY because | |
| // the client does not have the Dart code to construct the server component. | |
| if (showContent) widget.content, | |
| // WARNING: If showContent is false during SSR, 'component.content' is not built on the server. | |
| // When showContent becomes true on the client, it will render as EMPTY because | |
| // the client does not have the Dart code to construct the server component. | |
| if (showContent) component.content, |
| @override | ||
| Component build(BuildContext context) { | ||
| return div([ | ||
| button(onClick: () => setState(() => showContent = true), [Text('Reveal')]), |
There was a problem hiding this comment.
| button(onClick: () => setState(() => showContent = true), [Text('Reveal')]), | |
| button(onClick: () => setState(() => showContent = true), [.text('Reveal')]), |
| @override | ||
| void willRebuildElement(Element element) {} | ||
| String getDataForServerComponent(Component component, Element parent) { | ||
| if (registry.serverComponents[component] case final s?) { |
There was a problem hiding this comment.
Is this safe? Say if two different elements on the page are from the same component but with different data? Do they need their own marker?
Description
Add support for Server Components to Jaspr.
Type of Change