以前書いた記事にたくさんの反響を頂きましたので、調子に乗って今回はTauriV2のプラグインでおすすめのものを紹介していきたいと思います。
まず注意点
プラグイン処理は全部非同期
Tauriプラグインを介したTypescript/javascriptの処理は例外なく全て非同期処理となります。
こればっかりはrustで書かれた処理をts/jsで呼び出すという構成上仕方ないです。
例えば、「アプリ起動時にconfigファイルを参照して中の値を取り出す」という処理だけでも、実際に実装してみると以下のようなawait祭りになります。
import { readTextFile, writeTextFile, exists, mkdir } from "@tauri-apps/plugin-fs";
import { join, resourceDir } from "@tauri-apps/api/path";
import { ref, onMounted } from 'vue'
import {parse} from 'yaml'
const ymlconfig = ref([])
const checkConfig = async() => {
const configPath = await join(await resourceDir(), "config.yml")
if(await exists(configPath)){
ymlconfig = parse(await readTextFile(configPath))
}
}
onMounted(() => {
checkConfig()
});
「awaitを多用するな」とは非同期処理の実装でよく言われることですが、Tauriプラグインの処理は中身がrustというのもあって処理自体は爆速で終わるためそこまでパフォーマンス面への影響はないと思います
(ちゃんと測ったわけじゃないので違ってたらゴメンね!)
権限スコープをちゃんと付けること
ほぼ全てのプラグインにsrc-tauri\capabilities\default.json
でのスコープ設定が必要になります。
基本の<プラグイン名>:default
で動くものもありますが、動かないものは画面上でエラーを返してきます。
とりあえず迷ったらプラグインの処理に対して以下のようなスコープ設定をする癖を身に着けておくといいと思います。
"permissions": [
{
"identifier": "fs:allow-exists",
"allow": [
{
"path": "**" ~ここはアプリの用途に合わせて都度書き換えてください。~
}
]
}
]
以下、私が個人的に多用しているプラグイン
fs
Nodeのfsとほぼ同じ機能を提供してくれます。
引数で渡したパスの存在確認をするexists
、
ファイルの読み書きをするwriteTextFile
, readTextFile
, writeFile
, readFile
あたりは個人的に必須
import { exists } from "@tauri-apps/plugin-fs";
import { writeTextFile,readTextFile,writeFile,readFile } from "@tauri-apps/plugin-fs"
//事前に権限スコープの設定をしてないとtrueでもfalseでもなくエラーを返すので注意
if(await exists('path/to/folder')){
...
}
const path = "path/to/file.txt"
const imgPath = "path/to/image.png"
const file = await readTextFile(path) //テキストファイル内容のstringを返す
const img = await readFile(imgPath) // ファイルのバイナリ(int8Array)を返す
await writeTextFile(path, "text") //対象ファイルをテキストで上書きする
const otherImagePath = "bbb/image.png"
await writeFile(imgPath, await readFile(otherImagePath)) //対象ファイルをバイナリで上書きする
path
こちらもnodeのpathと大体同じですが、Tauriプラグイン固有のメソッドとしてアプリの実行パスを返してくれるresourceDir
メソッドがあり、これが物凄く便利、
パス同士を結合させるjoin
と合わせて個人的に使用頻度が高いです。
import { join, resourceDir } from "@tauri-apps/api/path"
const resourceDir = await resourceDir() //exeの実行パスを返す
const configDir = await join(resourceDir, "config.json") //<実行パス>/config.jsonが返ってくる
Dialog
window.confirmなどのダイアログと同じ機能を提供します。
import { message, confirm, open, save } from "@tauri-apps/plugin-dialog";
//OKでtrue、キャンセルでfalseが返ってくる
const confirm = await confirm(
'続行しますか?', //確認ウィンドウに表示sれるメッセージ
{ title: '確認', kind: 'warning' }//タイトルでウィンドウ名、kindでアイコン種類を指定
);
//メッセージを出すだけ、OKを押すと消える
await message('メッセージ', { title: 'メッセージ', kind: 'error' });
//ファイルを選択するとファイルパスが、キャンセルするとnullが返ってくる
const file = await open({
multiple: false,//複数ファイル選択
directory: false,//trueにするとフォルダ選択メニューになる
filters: [//省略可能、省略すると全ファイル選択可能になる
{
name: '画像',//フィルター名称
extensions: ['png', 'jpeg'],//設定した拡張子しか選択できなくなる
},
],
});
//メニューから保存をクリックすると、保存先フォルダーにファイルが置かれた過程でフルパスを返す
const path = await save({
filters: [
{
name: '画像',
extensions: ['png', 'jpeg'],
},
],
});
SQL
MYSQL、PostgreSQL、SQliteの3バージョン選択可能
selectでSELECTを、executeでDELETE、INSERT、UPDATE等を使用可能。
自分はSQliteのみ使っていますが、dbファイルが無い場合のファイル新規作成までちゃんとやってくれました。
rustでわざわざDB接続のコードを書かなくてもこれ一つで事足りるのは非常に便利。
import Database from '@tauri-apps/plugin-sql';
const path = "path/to/aaa.sqlite"
const db = await Database.load(`sqlite:${path}`);
await db.execute('INSERT INTO ...');//DELETE、INSERT、UPDATE等を実行
const result = await db.select("SELECT * FROM ...")//SELECTの戻り値を返す
Store(超おすすめ)
Electronでも人気が高かったElectron-storeのTauri版。
jsonファイル形式で設定値を格納できるKey-Valueストアを構築できます。
対象パスにjsonファイルが無くても自動で新規作成してくれる機能も完備。
これの便利さに気付くまでずっとfsのテキストファイル読み書きで設定ファイルの取り扱いをしていましたが、storeに切り替えてから処理の手間が一気に少なくなりました。
import { load } from "@tauri-apps/plugin-store"
import { join, resourceDir } from "@tauri-apps/api/path"
//resourceDirを使えば実行パスでストア作成も可能
const configDir = await join(await resourceDir(), "config.json"
//渡されたパスでストアの読み込み設定を確立
//(ファイルには直接アクセスしないため、config.jsonが無くてもエラーになりません。)
const store = await load(configDir, { autoSave: false });
//getでキー値を渡して値を取得する
//jsonが無いか、キーが存在しない場合はundefinedが返ってくる。
const data = await store.get<string>('data');
//ストアにキーと値を設定する時はsetを使う、この時点ではまだファイル保存はされない。
await store.set('data', "test")
//ストアとなるjsonファイルを保存する、この時点で対象パスにjsonがなければ新規作成する。
await store.save()