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

Jaspr Dart Web Framework 박제창 @Devfest 2024

Jaspr Dart Web Framework 박제창 @Devfest 2024

Jaspr Dart Web Framework @Devfest 2024
- https://festa.io/events/6324
- 2024-12-21
- Devfest 2024
- 박제창 @jaichangpark

JaiChangPark

December 20, 2024
Tweet

More Decks by JaiChangPark

Other Decks in Programming

Transcript

  1. 3

  2. 6 @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); html, body { width: 100%; height: 100%;

    margin: 0; padding: 0; font-family: 'Roboto', sans-serif; } #output { padding: 20px; text-align: left; }
  3. 10 import 'package:web/web.dart' as web; final element = web.document.querySelector('#output') as

    web.HTMLDivElement; for (final item in thingsTodo()) { element.appendChild(newLI(item)); }
  4. 11

  5. SEO (Search Engine Optimization) Renderer (CanvasKit & HTML) Refresh /

    Update Flutter Web 13 Text Input & Text Field
  6. 2024 Flutter Roadmap 14 Web platform We'll continue to focus

    on performance and quality, including investigating reducing the overall application size, better use of multi-threading, supporting platform views, improving app load times, making CanvasKit the default renderer, improving text input, and investigating options for supporting SEO for Flutter web. We expect to complete the effort to compile Dart to WasmGC, and with that support Wasm compilation of Flutter web apps. This also includes a new JS interop mechanism for Dart that supports both JS and Wasm compilation. We also plan to resume work to support hot reload on the web.
  7. Incheon / Songdo We'll continue to focus on performance and

    quality, including … investigating options for supporting SEO for Flutter web. 15
  8. 20

  9. 21

  10. 22

  11. 26

  12. 28

  13. 1.Open Source 2.Documentation 3.Playground 4.CLI Tool 5.Examples 6.Test Jaspr Jaspr

    is an modern web framework for building websites in Dart with support for both client-side and server-side rendering. 32
  14. 33

  15. 34

  16. 35 Directories • /apps: Production apps built with jaspr ◦

    /jaspr_pad: Online Editor and Playground inspired by DartPad, built with jaspr. • /docs: Documentation hosted at docs.page/schultek/jaspr • /examples: Well-maintained and documented examples • /packages: ◦ /jaspr: The core framework package. ◦ /jaspr_builder: Code-generation builders for jaspr. ◦ /jaspr_cli: The command line interface of jaspr. ◦ /jaspr_flutter_embed: Flutter element embedding bindings for jaspr. ◦ /jaspr_lints: A collection of lints and assists for jaspr projects. ◦ /jaspr_riverpod: Riverpod implementation for jaspr. ◦ /jaspr_router: A router implementation for jaspr. ◦ /jaspr_test: A testing package for jaspr. ◦ /jaspr_tailwind: A tailwind integration for jaspr.
  17. 36 Directories • /apps: Production apps built with jaspr ◦

    /jaspr_pad: Online Editor and Playground inspired by DartPad, built with jaspr. • /docs: Documentation hosted at docs.page/schultek/jaspr • /examples: Well-maintained and documented examples • /packages: ◦ /jaspr: The core framework package. ◦ /jaspr_builder: Code-generation builders for jaspr. ◦ /jaspr_cli: The command line interface of jaspr. ◦ /jaspr_flutter_embed: Flutter element embedding bindings for jaspr. ◦ /jaspr_lints: A collection of lints and assists for jaspr projects. ◦ /jaspr_riverpod: Riverpod implementation for jaspr. ◦ /jaspr_router: A router implementation for jaspr. ◦ /jaspr_test: A testing package for jaspr. ◦ /jaspr_tailwind: A tailwind integration for jaspr.
  18. 37

  19. 38

  20. 39 Differences to Flutter Web Flutter Web is for building

    Web-Apps, not Web-Sites. Jaspr is an alternative to Flutter Web, when you want to build any kind of website with Dart. This includes (but is not limited to): • Static Sites • Server-Rendered Sites • Single-Page Applications Jaspr works by giving you the familiar look and feel of the Flutter framework, while using native web technologies, like HTML, the DOM and CSS to enable you building all kinds of websites using Dart.
  21. 40 class Counter extends StatefulComponent { const Counter({super.key}); @override State<Counter>

    createState() => CounterState(); } class CounterState extends State<Counter> { int count = 0; @override Iterable<Component> build(BuildContext context) sync* { yield div(classes: 'counter', [ Jaspr
  22. 41 class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget

    build(BuildContext context) { return const MaterialApp( home: Scaffold( body: Center( child: Text('Hello World!'), ), ), ); } } class Header extends StatelessComponent { const Header({super.key}); @override Iterable<Component> build(BuildContext context) sync* { yield header([ nav([ for (var route in [ (label: 'Home', path: '/'), ]) ]), ]); } } Flutter Jaspr Differences to Flutter Web
  23. 44 void main() { Jaspr.initializeApp(options: defaultJasprOptions); runApp(Document( title: "Flutter Plugin

    Interop", head: [ link(rel: "stylesheet", href: "styles.css"), ], body: App(), )); } Jaspr
  24. 45 const factory Document({ String? title, String? lang, String? base,

    String? charset, String? viewport, Map<String, String>? meta, List<StyleRule>? styles, List<Component> head, required Component body, }) = BaseDocument; Jaspr
  25. 46 Jaspr <!DOCTYPE html> <html> <head> <base href="/"/> <meta charset="utf-8"/>

    <title>My Site</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="description" content="My site description"/> </head> <body> <div id="main"></div> </body> </html>
  26. Jaspr CLI 48 Project (Jaspr comes with a cli tool

    to create, serve and build your website.) • jaspr create: Create a new jaspr project. • jaspr serve: Serve the project and automatically refresh when you make changes. • jaspr build: Build the project according to the selected rendering mode. Tooling (Additionally, there are the following service commands available) • jaspr doctor: Show information about the environment and current project. • jaspr clean: Delete the build/ and .dart_tool/ directories. • jaspr update: Update the jaspr cli. • jaspr analyze
  27. Jaspr Create 50 1 Select a rendering mode:ely client-rendered site.

    ❯ ◉ static: Build a statically pre-rendered site. ◯ server: Build a server-rendered site. ◯ client: Build a purely client-rendered site. Select a rendering mode: 2 (Recommended) Enable automatic hydration on the client? (Y/n) 3 Setup routing for different pages of your site? (Y/n) 4 (Recommended) Use multi-page (server-side) routing? 5 Setup Flutter web embedding? (y/N) 6 Enable support for using Flutter web plugins in your project? (Y/n)
  28. Jaspr Create 51 1 Select a rendering mode:ely client-rendered site.

    ❯ ◉ static: Build a statically pre-rendered site. ◯ server: Build a server-rendered site. ◯ client: Build a purely client-rendered site. Select a rendering mode: static: Build a statically pre-rendered site. 2 (Recommended) Enable automatic hydration on the client? (Y/n) Yes 3 Setup routing for different pages of your site? (Y/n) Yes 4 (Recommended) Use multi-page (server-side) routing? Yes 5 Setup Flutter web embedding? (y/N) No 6 Enable support for using Flutter web plugins in your project? (Y/n) Yes
  29. Static Pre-rendering at build time Static Site No server Server

    Pre-rendering at request time Dynamic site Server Client No pre-rendering Single page site No server Jaspr Rendering Mode 52
  30. Jaspr Project Structure (Static) 53 📦lib ┣ 📂components ┃ ┣

    📜counter.dart ┃ ┣ 📜header.dart ┃ ┗ 📜sample.dart ┣ 📂constants ┃ ┗ 📜theme.dart ┣ 📂pages ┃ ┣ 📜about.dart ┃ ┗ 📜home.dart ┣ 📜app.dart ┣ 📜jaspr_options.dart ┗ 📜main.dart 📦web ┣ 📂images ┃ ┗ 📜logo.png ┗ 📜favicon.ico
  31. Jaspr Project Structure (Client) 54 📦lib ┣ 📂components ┃ ┣

    📜counter.dart ┃ ┗ 📜header.dart ┣ 📂pages ┃ ┣ 📜about.dart ┃ ┗ 📜home.dart ┗ 📜app.dart 📦web ┣ 📂images ┃ ┗ 📜logo.png ┣ 📜favicon.ico ┣ 📜index.html ┣ 📜main.dart ┗ 📜styles.css
  32. Jaspr Routing (jaspr_router) 55 import 'package:jaspr_router/jaspr_router.dart'; import 'pages/home.dart'; import 'pages/about.dart'

    ; class App extends StatelessComponent { @override Iterable<Component> build(BuildContext context) sync* { yield Router(routes: [ Route(path: '/', builder: (context, state) => Home()), Route(path: '/about', builder: (context, state) => About()), ]); } }
  33. Jaspr Routing (jaspr_router) 57 class App extends StatelessComponent { @override

    Iterable<Component> build(BuildContext context) sync* { yield Router(routes: [ Route(path: '/', builder: (context, state) => Home()), Route(path: '/about', builder: (context, state) => About()), Route(path: "/profile", title: "Profile", builder: (context,state){ return Profile(); }), Route(path: "/admin", title: "Admin", builder: (context,state){ return Admin(); }) ]); } }
  34. Jaspr Component 59 <h1>This is heading 1</h1> <ol> <li>Coffee</li> <li>Tea</li>

    <li>Milk</li> </ol> @override Iterable<Component> build(BuildContext context) sync* { yield h1([text("This is heading 1")]); yield ol([ li([text("Coffee")]), li([text("Tea")]), li([text("Milk")]), ]);
  35. Jaspr Component 60 <h1>This is heading 1</h1> <ol> <li>Coffee</li> <li>Tea</li>

    <li>Milk</li> </ol> @override Iterable<Component> build(BuildContext context) sync* { yield h1([text("This is heading 1")]); yield ol([ li([text("Coffee")]), li([text("Tea")]), li([text("Milk")]), ]);
  36. Jaspr Component 61 @override Iterable<Component> build(BuildContext context) sync* { yield

    h1([text("This is heading 1")]); yield ol([ li([text("Coffee")]), li([text("Tea")]), li([text("Milk")]), ]);
  37. Jaspr Component 62 Component h1(List<Component> children, {Key? key, String? id,

    String? classes, Styles? styles, Map<String, String>? attributes, Map<String, EventCallback>? events}) { return DomComponent( tag: 'h1', key: key, id: id, classes: classes, styles: styles, attributes: attributes, events: events, children: children, ); } Component ProxyComponent DomComponent
  38. 65

  39. 66 yield div( styles: const Styles.background(color: Colors.red), [text("Devfest 2024")]); yield

    div( styles: const Styles.raw({ 'background-color': 'red', }), [text("Devfest 2024")]); yield div( styles: const Styles.background(color: Colors.red) .text(fontSize: 24.px), [text("Devfest 2024")]); yield div( styles: Styles.combine([ Styles.background(color: Colors.red), Styles.text(color: Colors.blue, fontSize: 23.px), ]), [text("Devfest 2024")]); Jaspr Styling
  40. 67 const redText = Styles.text(color: Colors.red); //Reusing Styles const redBlueTest

    = Styles.combine([ redText, Styles.background(color: Colors.blue), ]); const redGreenTest = Styles.combine([ redText, Styles.background(color: Colors.green), ]); Jaspr Styling
  41. 68 <style> #content { width: 100%; height: 100%; } #content

    a { color: green; } .red-text { color: red; } </style> Jaspr Styling
  42. 69 Style(styles: [ css('#content', [ css('&').box(width: 100.percent, height: 100.percent), css('a').text(color:

    Colors.green), ]), css('.red-text').text(color: Colors.red), ]); Jaspr Styling
  43. 70 Style(styles: [ css('#content', [ css('&').box(width: 100.percent, height: 100.percent), css('a').text(color:

    Colors.green), ]), css('.red-text').text(color: Colors.red), ]); Jaspr Styling #content { width: 100%; height: 100%; } #content a { color: green; } .red-text { color: red; }
  44. 71 Jaspr Styling @override Iterable<Component> build(BuildContext context) sync* { yield

    div(classes: 'main', [ p([text('Hello World')]), ]); } @css static final styles = [ css('.main', [ css('&').box(width: 100.px), css('p').text(color: Colors.blue), ]), ];
  45. 72 Jaspr Styling @override Iterable<Component> build(BuildContext context) sync* { yield

    div(classes: 'main', [ p([text('Hello World')]), ]); } @css static final styles = [ css('.main', [ css('&').box(width: 100.px), css('p').text(color: Colors.blue), ]), ]; 컴포넌트에서 정의되고 @css를 사용하여 등록된 스타일은 해당 컴포넌트에 범위가 지정되지 않습니다. 다른 컴포넌트에 원치 않는 영향을 주거나 스타일 정의가 충돌하는 것을 방지하려면 ID 또는 고유한 클래스 이름을 선택자로 사용해야 합니다
  46. 75 @RhysSullivan Rapidly build modern websites without ever leaving your

    HTML. A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup. <img class="w-24 h-24 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto" src="/sarah-dayan.jpg" alt="" width="384" height="512"> <div class="pt-6 md:p-8 text-center md:text-left space-y-4"> <blockquote> <p class="text-lg font-medium"> “Tailwind CSS” </p> </blockquote> </div>
  47. 76 Jaspr tailwindcss > curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/downloa d/tailwindcss-macos-arm64 > chmod

    +x tailwindcss-macos-arm64 > mv tailwindcss-macos-arm64 /usr/local/bin/tailwindcss Tailwind CSS Standalone CLI
  48. 77 Jaspr tailwindcss > curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/downloa d/tailwindcss-macos-arm64 > chmod

    +x tailwindcss-macos-arm64 > mv tailwindcss-macos-arm64 /usr/local/bin/tailwindcss Tailwind CSS Standalone CLI
  49. 79 Jaspr tailwindcss styles.tw.css 📦web ┣ 📂images ┃ ┗ 📜logo.png

    ┣ 📜favicon.ico ┗ 📜styles.tw.css @tailwind base; @tailwind components; @tailwind utilities;
  50. 83 Jaspr State Management class FabButtonComponent extends StatefulComponent { State<FabButtonComponent>

    createState() => FabButtonComponentState(); } class FabButtonComponentState extends State<FabButtonComponent> { int count = 0; int count1 = 0; @override Iterable<Component> build(BuildContext context) sync* { // code } }
  51. 84 yield div( classes: "fixed bottom-4 right-4 flex flex-col items-center",

    [ span( classes: "mb-2 text-gray-700 text-sm", [text("count: ${count.toString()}"),],), span( classes: "mb-2 text-gray-700 text-sm", [text("count1: ${count1.toString()}"),],), button( classes: "bg-blue-500 hover:bg-blue-600 text-white rounded-full p-4 shadow-lg", [text("클릭 ${count}")], onClick: () { setState(() { count += 1; }); }, )], );
  52. 85 yield div( classes: "fixed bottom-4 right-4 flex flex-col items-center",

    [ span( classes: "mb-2 text-gray-700 text-sm", [text("count: ${count.toString()}"),],), span( classes: "mb-2 text-gray-700 text-sm", [text("count1: ${count1.toString()}"),],), button( classes: "bg-blue-500 hover:bg-blue-600 text-white rounded-full p-4 shadow-lg", [text("클릭 ${count}")], onClick: () { setState(() { count += 1; }); }, )], );
  53. 86

  54. 87 Jaspr Riverpod Jaspr comes with a Riverpod package that

    ports over flutter_riverpod to Jaspr. It is based on Riverpod 2 and supports all providers and modifiers. • No Consumer / ConsumerComponent • Just use context.read / context.watch • Additional SyncProvider to sync values between server and client.
  55. 92 class Button extends StatelessComponent { Button({required this.label, required this.onPressed});

    final String label; final void Function() onPressed; @override Iterable<Component> build(BuildContext context) sync* { yield DomComponent( tag: 'button', events: {'click': (e) => onPressed()}, child: Text(label), ); } }
  56. 94 final initialTokenProvider = SyncProvider<int>((ref) async { final res =

    await http.get( Uri.parse( "https://us.api.blizzard.com/data/wow/token/index?namespace=dynamic-us&lo cale=en_US", ), headers: {'Authorization': 'Bearer <Key>'}); final token = Token.fromJson(json.decode(res.body)); return token.price ?? 0; }, id: 'init_token');
  57. 96 jobs: deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }}

    runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages uses: actions/configure-pages@v5 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: # Upload entire repository path: '.' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 Github Page & Action
  58. 97 Github Page & Action jobs: # Single deploy job

    since we're just deploying deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages uses: actions/configure-pages@v5 - name: "Setup Dart" uses: dart-lang/[email protected] - name: "Setup Flutter" uses: subosito/flutter-action@v2 - name: "Build Jaspr" run: | dart pub global activate jaspr_cli jaspr build --verbose - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: # Upload entire repository path: 'build/jaspr' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4
  59. 98 Github Page & Action jobs: # Single deploy job

    since we're just deploying deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Pages uses: actions/configure-pages@v5 - name: "Setup Dart" uses: dart-lang/[email protected] - name: "Setup Flutter" uses: subosito/flutter-action@v2 - name: "Build Jaspr" run: | dart pub global activate jaspr_cli jaspr build --verbose - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: # Upload entire repository path: 'build/jaspr' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4
  60. 99

  61. Summary 100 • Flutter Web & Dart Web ◦ Renderer,

    WASM • Jaspr ◦ Open-Source ◦ Documents ◦ Differences to Flutter Web ◦ CLI ◦ Rendering Modes ◦ Project Structure ◦ Component & Styling (CSS) ◦ State Management ◦ Deploy