Slide 1

Slide 1 text

1 Vue3/Electronで自作したマーク ダウンエディタをVue3/Tauriに リプレイスした話 Vue Fes Japan 2023

Slide 2

Slide 2 text

自己紹介 ● 名前:0yu(おゆ) ● 所属:合同会社DMM.com動画配信開発部 23新卒 ● 出身:北海道 2 っっっz yud0uhu

Slide 3

Slide 3 text

3 作ったもの

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

サポートする機能 ● .txt .md形式での保存 ● リアルタイムプレビュー ● コードハイライト 5

Slide 6

Slide 6 text

6 作ったもの

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

追加で実装した機能 ● 絵文字、マークダウンサジェスト ● Mermaid.js記法の対応 8

Slide 9

Slide 9 text

概説 Electron 9

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

概説:Electron ● Webフロントエンドの技術でデスクトップアプリが作れるGUIフ レームワーク ● ChromiumとNode.jsがバイナリに組み込まれている ○ 単一のJavaScriptコードベースを維持しつつ、クロスプラッ トフォームアプリが開発可能 ● 開発元はGitHub社 ○ VSCodeやSlackはElectronで作られている 11

Slide 12

Slide 12 text

12 概説 Tauri

Slide 13

Slide 13 text

概説:Tauri 13

Slide 14

Slide 14 text

概説:Tauri ● RustとWebフロントエンドの技術でデスクトップアプリが作れる GUIフレームワーク ● 一言で言うと「小さいElectron」 ● Chromiumなどのブラウザを組み込んでおらず、OSのWebView機 能を呼び出すラッパー(WRY)を用いている ○ 軽量・高速に動いてくれることが特徴 ○ ブラウザの脆弱性に関する対策を行う必要がない 14

Slide 15

Slide 15 text

15 WRY is 何?

Slide 16

Slide 16 text

WRY(Webview Rendering librarY) ● 「OSに備わっているWebViewを使ってレンダリングする」機能 をラップしたクレート ○ Windows 👉 WebView2 ○ Linux 👉 WebKitGTK ○ macOS 👉 WKWebView 16

Slide 17

Slide 17 text

17 Core Ecosystem

Slide 18

Slide 18 text

Core Ecosystem 18

Slide 19

Slide 19 text

19 ● tauri-build 👉実行可能なインストーラとバイナリを生成する ● tauri-codegen 👉アイコンやシステムトレイなどのアセットを埋め込み、ハッ シュ化・圧縮する 👉コンパイル時にtauri.conf.jsonを解析し、Config構造体を生成 する Core Ecosystem(1/3)

Slide 20

Slide 20 text

20 ● tauri-macros 👉tauri-codegenクレートを用いて、コンテキスト、ハンド ラー、コマンドのマクロを作成する ● tauri-runtime 👉TauriとWebViewライブラリ(WRY,TAO)の連携を行う層 ● tauri-utils 👉tauri-codegenクレートを用いて、コンテキスト、ハンド ラー、コマンドのマクロを作成する Core Ecosystem(2/3)

Slide 21

Slide 21 text

21 TauriのWebView部分を支える外部モジュール ● WRY 👉WebViewを扱うためのクレート ● TAO 👉Windowを操作するためのクレート Core Ecosystem(3/3)

Slide 22

Slide 22 text

22 Tauriのプロセスモデル

Slide 23

Slide 23 text

23 Tauriのプロセスモデル

Slide 24

Slide 24 text

24 Tauriの プロセス間通信の仕組 み

Slide 25

Slide 25 text

25 プロセス間通信:具体例

Slide 26

Slide 26 text

プロセス間通信:具体例 26 // … #[tauri::command] fn generate_mermaid_img(code: String) -> String { let encoded_code = encode_to_base64(&code); let response = ApiResponse { img: format!("https://mermaid.ink/img/{}", encoded_code), }; format!("{}", response.img) }

Slide 27

Slide 27 text

プロセス間通信:具体例 27 // … fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![generate_mermaid_img]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }

Slide 28

Slide 28 text

28 プロセス間通信:具体例

Slide 29

Slide 29 text

プロセス間通信:具体例 29 import { invoke } from "@tauri-apps/api"; // … const generateMermaidImg = async (code: string) => { try { const response = await invoke("generate_mermaid_img", { code: code }); return response; } catch (error) { // …

Slide 30

Slide 30 text

30 Tauriの セキュリティ

Slide 31

Slide 31 text

31 Tauriのセキュリティ ● tauri/allowlist 👉アプリケーション内でのJSの実行・API呼び出しを制御するた めの機構 👉使用する機能を個別に有効化、無効化できる

Slide 32

Slide 32 text

tauri/allowlist:具体例 "tauri": { "allowlist": { "all": false, ... "dialog": { "open": true, "save": true }, "fs": { "all": true, "writeFile": true }, ... }, 32

Slide 33

Slide 33 text

tauri/allowlist:具体例 import Header from "./components/Header.vue"; import MarkdownEditor from "./components/MarkdownEditor.vue"; import { ref } from "vue"; import { open, save } from "@tauri-apps/api/dialog"; import { readTextFile, writeFile } from "@tauri-apps/api/fs"; import { appDir } from "@tauri-apps/api/path"; 33

Slide 34

Slide 34 text

tauri/allowlist:具体例 const openFile = async () => { try { const selected = await open({ directory: false, multiple: false, defaultPath: await appDir(), filters: [{ name: "Markdown and Text Files", extensions: ["md", "txt"] }], }); 34

Slide 35

Slide 35 text

tauri/allowlist:具体例 if (selected !== null) { if (!Array.isArray(selected)) { const filePath = selected; const contents = await readTextFile(filePath); onMarkdownUpdate(contents); } } } catch (error) { // … 35

Slide 36

Slide 36 text

tauri/allowlist:具体例 const saveFile = async () => { try { const result = await save({ defaultPath: "untitled.txt", filters: [{ name: "Markdown and Text Files", extensions: ["md", "txt"] }], }); if (result) { const fileContents = markdownText.value; await writeFile({ path: result, contents: fileContents ? fileContents : "", }); 36

Slide 37

Slide 37 text

37 パフォーマンスを計測 してみる

Slide 38

Slide 38 text

38 パフォーマンスを計測してみる 比較 Tauri Electron インストーラサイズ 3.1MB 52.1MB メモリ利用量 180MB 462MB 起動速度 0.39秒 0.8秒 レンダリング WRY Chromium

Slide 39

Slide 39 text

39 実際のアプリケーションサイズ・実行時間の比較 パフォーマンスを計測してみる 比較 Tauri Electron 起動速度 0.49秒 0.58秒 アプリケーションサ イズ 31.1MB 588MB

Slide 40

Slide 40 text

最終的な実装 40 https://github.com/yud0uhu/markdown-editor-tauri-web

Slide 41

Slide 41 text

41 ご静聴ありがとうござ いました !