sony/flutter-embedded-linux を使ってみた Raspberry Pi 4 (arm64) 編
あらすじ
上記のリポジトリを Raspberry Pi 4 (aarch64) 向けにビルドして、DRMで実行します。 flutter-embedded-linux
は libflutter_engine.so
を使いますが、プラットフォーム毎にビルドしないといけません。ただし、いまのところ Raspberry Pi 4 本体ではビルドできなさそうだったので、linux-x64 をホストにしてクロスビルドします。
Raspberry pi (セットアップ~ engine バージョン取得)
libflutter_engine.so を作成するために一旦flutterをインストールして、その中の engine.version ファイルの中身を取得します。
セットアップ
SDカードにイメージを焼いておく。今回使用したイメージは
ubuntu-20.04.2-preinstalled-server-arm64+raspi.img
です。SDカードをRaspberry PI に挿して起動するとログインを求められます。
ubuntu
/ubuntu
でログイン可能です。パッケージをアップデートして、必要なパッケージをインストールします。
$ sudo apt update $ sudo apt -y upgrade $ sudo apt -y unzip $ sudo apt install -y clang cmake build-essential pkg-config libegl1-mesa-dev libxkbcommon-dev libgles2-mesa-dev
- flutter をセットアップして
engine.version
を表示します。
$ git clone https://github.com/flutter/flutter $ sudo mv flutter /opt/ $ export PATH=$PATH:/opt/flutter/bin $ flutter config --enable-linux-desktop $ flutter doctor $ cat /opt/flutter/bin/internal/engine.version
ここで取得できるバージョンを .gclient
で使用します。 2021年5月22日 17:00 現在、 3fa3eb3a60e0dd741da98fe117977e7e2059042f
でした。
ホスト(セットアップ~libflutter_engine.so作成)
今のところ、Raspberry PI では libflutter_engine.so を作成できない(やり方がわからない)ので、linux-x64ベースのホストPCで作成します。今回はバーチャルマシンにubuntu-20.04をインストールしてその中で作成します。今回使用した ubuntu のイメージは ubuntu-20.04.2.0-desktop-amd64.iso
です。
- パッケージをアップデートして、必要なパッケージをインストールします。
$ sudo apt update $ sudo apt -y upgrade $ sudo apt install -y git curl vim virtualenv python2 $ sudo apt install -y clang cmake build-essential pkg-config libegl1-mesa-dev libxkbcommon-dev libgles2-mesa-dev
- .glient を作成します。
engine.version
のバージョンを url に指定します。
solutions = [ { "managed": False, "name": "src/flutter", "url": "https://github.com/flutter/engine.git@3fa3eb3a60e0dd741da98fe117977e7e2059042f", "custom_deps": {}, "deps_file": "DEPS", "safesync_url": "", }, ]
- libflutter_engine.so を作成します。途中の
./flutter/tools/gn
でGOMA usage was specified but can't be found, falling back to local builds. Set the GOMA_DIR environment variable to fix GOMA.
とか出るけど、無視して進めます。
$ gclient sync $ cd src $ ./flutter/tools/gn --target-os linux --linux-cpu arm64 --runtime-mode release --embedder-for-target --disable-desktop-embeddings $ ninja -C out/linux_release_arm64
ssh の設定 と libflutter_engine.so の転送
Raspberry PI 側
ssh サーバーをインストールします。
$ sudo apt -y install ssh $ sudo systemctl start ssh $ ip addr
ホスト
libflutter_engine.so を転送します
$ scp ~/src/out/linux_release_arm64/libflutter_engine.so ubuntu@<raspberry_ipのIPアドレス>:/home/ubuntu/
flutter-embedded-linux をビルド (DRM編)
$ cd $ sudo apt install libdrm-dev libgbm-dev libinput-dev libudev-dev libsystemd-dev $ git clone https://github.com/sony/flutter-embedded-linux $ cd flutter-embedded-linux $ mkdir build $ cd build $ cmake -DUSER_PROJECT_PATH=examples/flutter-drm-gbm-backend .. $ cp ~/libflutter_engine.so . $ cmake --build .
flutter-drm-gbm-backend の実行
flutter サンプルアプリを作成
$ sudo apt -y install ninja-build libgtk-3-dev $ flutter create sample $ cd sample/ $ flutter build linux $ cd ..
ubuntu 20.04 server は DRM がデフォルトで機能していないようです。 /boot/firmware/usercfg.txt
に以下の行を追記します。
dtoverlay=vc4-fkms-v3d
そのあといったん再起動した後、以下のコマンドを実行します。
$ cd flutter-embedded-linux/build $ sudo LD_LIBRARY_PATH=. ./flutter-drm-gbm-backend --bundle=./sample/build/linux/arm64/release/bundle/
これで、画面上いっぱいに flutter アプリが表示されたはずです。
以上!
参考資料
RaspberryPi3B+にUbuntu 20.04 LTS (Server)の64bit版をインストール - Qiita
sony/flutter-embedded-linux を使ってみる
要約
sony/flutter-embedded-linux
を Debian 10 で使ってみた。
参考資料
- Debian / Ubuntu上のwaylandとwestonで最小限のGUIを構築する - Qiita
- flutter-embedded-linux/doc at master · sony/flutter-embedded-linux · GitHub
- Build error in flutter linux · Issue #59890 · flutter/flutter · GitHub
- Unclear Linux instructions in doctor · Issue #64230 · flutter/flutter · GitHub
環境
- Oracle VM VirtualBox: 6.1
- Linux ディストリビューション: Debian 10.9
- GNOME デスクトップ環境インストール済
パッケージインストール
いくつかいらないパッケージもあるかもだけど
$ sudo apt -y install libnss-resolve libnss-systemd dbus-user-session policykit-1 $ sudo apt -y install weston xwayland xfonts-base fonts-vlgothic fonts-ipafont poppler-data $ sudo apt -y install vim $ sudo apt -y install git curl virtualenv $ sudo apt -y install clang cmake build-essential pkg-config libegl1-mesa-dev libxkbcommon-dev libgles2-mesa-dev $ sudo apt -y install libwayland-dev wayland-protocols $ sudo apt -y install libgtk-3-dev ninja-build $ sudo apt -y install python2
weston.ini
今回はもしかしたら必要ないかも?
[core] xwayland=true require-input=false backend=drm-backend.so modules=systemd-notify.so use-pixman=true [keyboard] keymap_layout=jp [shell] #client=/usr/bin/weston-terminal locking=false animation=fade allow-zap=true
Flutter Engine embedder のビルド
ビルドツールのインストール
$ cd ~ $ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $ export PATH=$PATH:~/depot_tools $ virtualenv .env -p python2 $ source .env/bin/activate
.gclient ファイルの作成
最新バージョンを使用
solutions = [ { "managed": False, "name": "src/flutter", "url": "https://github.com/flutter/engine.git", "custom_deps": {}, "deps_file": "DEPS", "safesync_url": "", }, ]
ソースファイルを取得
$ cd ~ $ gclient sync $ cd src $ ./flutter/tools/gn --runtime-mode release --embedder-for-target $ ninja -C out/host_release $ cp ~/src/out/host_release/libflutter_engine.so /usr/lib
flutter-embedded-linux のビルド
$ cd ~ $ git clone https://github.com/sony/flutter-embedded-linux $ cd flutter-embedded-linux $ mkdir build $ cd build $ cp ~/src/out/host_release/libflutter_engine.so . $ cmake -DUSER_PROJECT_PATH=examples/flutter-wayland-client .. $ cmake --build .
Flutter アプリのビルド
Flutter のインストール
$ cd ~ $ git clone https://github.com/flutter/flutter $ sudo mv flutter /opt/ $ export PATH=$PATH:/opt/flutter/bin $ flutter config --enable-linux-desktop $ flutter doctor
Flutter アプリのビルド
$ cd ~/flutter-embedded-linux $ flutter create sample $ cd sample/ $ flutter build linux $ cd ..
Flutter アプリの実行
$ cd ~/flutter-embedded-linux/build $ ./flutter-client ./sample/build/linux/x64/release/bundle
起動画面
まとめ
今回は Debian 10 で sony/flutter-embedded-linux
を実行してみました。githubのREADMEに従ってやれば基本的には詰まることなく表示できました。
RSAのEncrypt/Decrypt で例外が出るときの対処
要約
RSACryptoServiceProvider
を使って Encrypt を呼び出した場合、文字列が長いと WindowsCryptographicException
が出る。(Decryptの場合は CryptographicException
) その場合は KeySize
と パディングから最大長を割り出してそのサイズ以下でブロック化し暗号化・復号化を行う必要がある。また、暗号化したブロックのサイズは復号化するときにわかるように情報を付与しておかないと復号化は難しそうだ。
基本的には非対称暗号で大きなデータを扱うのではなく、共通鍵暗号をデータの暗号化に使用し、共通鍵の暗号化にのみ非対称暗号を使用するのが良いようだ。
参考資料
RSACryptoServiceProvider.Encrypt メソッド (System.Security.Cryptography) | Microsoft Docs
【C#】文字列を指定した文字数で分割する拡張メソッド - コガネブログ
Convert.ToBase64String メソッド (System) | Microsoft Docs
PHPのmcrypt関数で使用する初期化ベクトル(IV)とは公開されて… - 人力検索はてな
コード(非対称暗号での暗号化)
MSTest を使用して暗号化・復号化のサンプルを作成した。 TestEncryptAndDecrypt3
が今回のキモの部分。今回はパディングを PKCS # 1 v 1.5
で行うため、rsa.KeySize
bit / 8 bit - 11 バイト = 117 バイトがブロック最大長となる。今回は暗号化したデータブロックを復号化サイドで判断できるようにするため、BASE64でエンコードし改行で区切ることでブロックを分割した。
using System; using System.Text; using System.Collections.Generic; using System.Security.Cryptography; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace EncryptTest { [TestClass] public class UnitTest1 { private string publicKey, privateKey; [TestInitialize] public void TestInit() { using (var rsa = new RSACryptoServiceProvider()) { publicKey = rsa.ToXmlString(false); privateKey = rsa.ToXmlString(true); } } [DataTestMethod] [DataRow("Hello World!!")] [DataRow("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] public void TestEncryptAndDecrypt1(string rawString) { var rawBytes = Encoding.ASCII.GetBytes(rawString); byte[] encryptedBytes; using (var rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(publicKey); encryptedBytes = rsa.Encrypt(rawBytes, false); } string decryptedString; using (var rsa = new RSACryptoServiceProvider()) { byte[] decryptedBytes; rsa.FromXmlString(privateKey); decryptedBytes = rsa.Decrypt(encryptedBytes, false); decryptedString = Encoding.ASCII.GetString(decryptedBytes); } Assert.AreEqual(rawString, decryptedString); } [TestMethod] public void TestEncryptAndDecrypt2() { // 128 - 11 バイト (117 バイト) を超えると rsa.Encrypt() で例外が発生する。 var rawString = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; var rawBytes = Encoding.ASCII.GetBytes(rawString); using (var rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(publicKey); try { rsa.Encrypt(rawBytes, false); } catch (Exception e) { Assert.AreEqual("WindowsCryptographicException", e.GetType().Name); return; } Assert.Fail("This Test should be thrown exception."); } } [DataTestMethod] [DataRow("Hello World!!")] [DataRow("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] [DataRow("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] public void TestEncryptAndDecrypt3(string rawString) { string encryptedBase64String; using (var rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(publicKey); var count = (rsa.KeySize / 8) - 11; var splitStrings = new List<string>(); var length = (int)Math.Ceiling((double)rawString.Length / count); for (int i = 0; i < length; i++) { int start = count * i; if (rawString.Length <= start) { break; } if (rawString.Length < start + count) { splitStrings.Add(rawString.Substring(start)); } else { splitStrings.Add(rawString.Substring(start, count)); } } var sb = new StringBuilder(); foreach (var line in splitStrings) { var tempBytes = Encoding.ASCII.GetBytes(line); var tempEncBytes = rsa.Encrypt(tempBytes, false); sb.AppendLine(Convert.ToBase64String(tempEncBytes, 0, tempEncBytes.Length)); } encryptedBase64String = sb.ToString(); } var decryptedString = ""; using (var rsa = new RSACryptoServiceProvider()) { byte[] decryptedBytes; rsa.FromXmlString(privateKey); foreach(var line in encryptedBase64String.Split('\n')) { if (string.IsNullOrEmpty(line)) continue; var base64Bytes = Convert.FromBase64String(line); decryptedBytes = rsa.Decrypt(base64Bytes, false); decryptedString += Encoding.ASCII.GetString(decryptedBytes); } } Assert.AreEqual(rawString, decryptedString); } } }
コード(共通鍵暗号での暗号化)
さっきの例ではデータの暗号化に非対称暗号(RSA)を用いた。ただ、データが大きくなるにつれパフォーマンスの問題が出てくるらしい、なので、データの暗号化は共通鍵暗号を用い、共通鍵の暗号に非対称暗号を用いることで安全性・パフォーマンスの両面からカバーする。という方法が今の主流らしい。簡単に手順を示すと以下のようになる。
共通鍵受け渡しフロー
暗号化データ受け渡しフロー
- (B)は共通鍵でデータを暗号化
- (B)は暗号化されたデータをもう一方に受け渡す
- (A)は共通鍵で暗号化されたデータを復号化する
上記では便宜上データの暗号化を(B)で行っているが、共通鍵の受け渡しが終われば、データの暗号化はどちらが行っても問題なく行える。
以下、上記を実装したコードサンプルとなる。コード中の共通鍵のIVを平文で受け渡ししているが、暗号化・復号化が一回限りでIVを受け渡してすぐに行われるのであれば平文でもさほど問題ないようだ。(PHPのmcrypt関数で使用する初期化ベクトル(IV)とは公開されて… - 人力検索はてな)
using System; using System.IO; using System.Text; using System.Collections.Generic; using System.Security.Cryptography; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AESTest { [TestClass] public class UnitTest1 { [DataTestMethod] [DataRow("Hello World!!")] [DataRow("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] [DataRow("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] public void TestEncryptAndDecrypt1(string rawString) { var decryptor = new Decryptor(); var encryptor = new Encryptor(); TransferCommonKey(encryptor, decryptor); byte[] iv, encryptedText; (iv, encryptedText) = encryptor.Encrypt(rawString); string decString; decString = decryptor.Decrypt(iv, encryptedText); Assert.AreEqual(rawString, decString); } private void TransferCommonKey(Encryptor encryptor, Decryptor decryptor) { string publicKey = decryptor.GetPublicKey(); byte[] encryptedCommonKey = encryptor.GenerateEncryptedCommonKey(publicKey); decryptor.TransferCommonKey(encryptedCommonKey); } } class Encryptor { byte[] commonKey; public byte[] GenerateEncryptedCommonKey(string publicKey) { var aes = Aes.Create(); commonKey = aes.Key; using var rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(publicKey); return rsa.Encrypt(aes.Key, false); } public (byte[], byte[]) Encrypt(string text) { byte[] encrypted; var aes = Aes.Create(); using (var ms = new MemoryStream()) using (var cryptStream = new CryptoStream(ms, aes.CreateEncryptor(commonKey, aes.IV), CryptoStreamMode.Write)) { using (var sWriter = new StreamWriter(cryptStream)) { sWriter.Write(text); } encrypted = ms.ToArray(); } return (aes.IV, encrypted); } } class Decryptor { string publicKey, privateKey; byte[] commonKey; public Decryptor() { using var rsa = new RSACryptoServiceProvider(); publicKey = rsa.ToXmlString(false); privateKey = rsa.ToXmlString(true); } public string GetPublicKey() { return publicKey; } public void TransferCommonKey(byte[] encryptedCommonKey) { using var rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(privateKey); commonKey = rsa.Decrypt(encryptedCommonKey, false); } public string Decrypt(byte[] iv, byte[] data) { var aes = Aes.Create(); using var ms = new MemoryStream(data); using var cryptStream = new CryptoStream(ms, aes.CreateDecryptor(commonKey, iv), CryptoStreamMode.Read); using var sReader = new StreamReader(cryptStream); return sReader.ReadLine(); } } }
プライベート認証局によるCA署名証明書の作成
参考資料
OpenSSL で認証局 (CA) を構築する手順 (Windows) - OpenSSL - Node.js 環境構築 - Node.js 入門
OpenSSL で構築した認証局 (CA) でサーバ証明書を発行する方法 - OpenSSL - Node.js 環境構築 - Node.js 入門
OpenSSL で構築した認証局 (CA) で発行したサーバ証明書を利用して HTTPS 通信する方法 - OpenSSL - Node.js 環境構築 - Node.js 入門
CAの証明書を作成
openssl の 設定ファイル
[ca] default_ca = ca_default [ca_default] dir = . certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts database = $dir/index.txt certificate = $dir/certs/cacert.crt serial = $dir/serial crlnumber = $dir/crlnumber crl = $dir/crl.pem private_key = $dir/private/caprivkey.key x509_extensions = usr_cert name_opt = ca_default cert_opt = ca_default default_days = 365 default_crl_days = 30 default_md = default preserve = no policy = policy_anything [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional domainComponent = optional [req] prompt = no distinguished_name = dn [dn] CN = Root CA O = Root CA OU = Root CA L = localization ST = state C = JP [usr_cert] basicConstraints = CA:true,pathlen:0 nsComment = "OpenSSL Generated Certificate" extendedKeyUsage = serverAuth,clientAuth subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer subjectAltName = @alt_names [alt_names] URI=http://ca.example.com DNS.1=example.com
CAのプライベートキー作成
openssl genrsa -out caprivkey.key 2048
CAの証明書署名要求作成
openssl req -new -config ca.cnf -out cacert.csr -key caprivkey.key
証明書作成の前準備
mkdir newcerts touch index.txt echo 00 > serial
CAの証明書を自己署名証明書を作成
openssl ca -config ca.cnf -batch -extensions usr_cert -out cacert.crt -in cacert.csr -selfsign -keyfile caprivkey.key
CA署名証明書の作成
openssl の設定ファイル
CAの時とほぼ同じだが、basicConstraints
が違う。CAではないので、falseにする。
[ca] default_ca = ca_default [ca_default] dir = . certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts database = $dir/index.txt certificate = $dir/certs/cacert.crt serial = $dir/serial crlnumber = $dir/crlnumber crl = $dir/crl.pem private_key = $dir/private/caprivkey.key x509_extensions = usr_cert name_opt = ca_default cert_opt = ca_default default_days = 365 default_crl_days = 30 default_md = default preserve = no policy = policy_anything [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional domainComponent = optional [req] prompt = no distinguished_name = dn [dn] CN = My Certificate ST = Japan C = JP [usr_cert] basicConstraints = CA:false nsComment = "OpenSSL Generated Certificate" extendedKeyUsage = serverAuth,clientAuth subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer subjectAltName = @alt_names [alt_names] URI=http://cert.example.com DNS.1=example.com
CA署名証明書を作成する前準備
mkdir private mv caprivkey.key private/caprivkey.key mkdir certs mv cacert.crt certs/cacert.crt
CA署名対象の秘密鍵の作成
openssl genrsa -out server.key 2048
証明書署名要求作成
openssl req -new -config cert.cnf -out server.csr -key server.key
CA署名証明書の作成
openssl ca -config cert.cnf -batch -extensions usr_cert -out server.crt -in server.csr
おまけ
証明書を PEM から DER に変換
openssl x509 -in server.crt -inform pem -out server.der -outform der
CRLを発行(空)
echo 00 > crlnumber openssl ca -gencrl -config ca.cnf -out crl.pem
CRLをPEMからDERに変換
openssl crl -in crl.pem -inform pem -out crl.crl -outform der
(C#) 並行処理キー入力まちプログラム
参考サイト
ポイント
CancellationTokenSource() でトークン作成して、トークンからキャンセル用トークンを生成して、それをすべての非同期タスクで共有して、キャンセルが起きた時の処理をタスクに書くってとこ。
今回は WaitKeyTask でしかキャンセルしてないけど、すべてのタスクで共有して、WaitKeyTaskともども Task.WaitAll() で待っておけば、どこからでもキャンセルできるようになると思う。
コード
using System; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; namespace ExampleOfWaitKey { class Program { static void Task1(CancellationToken token, string c, int duration) { while (true) { Console.Write(c); Task.Delay(duration).Wait(); if(token.IsCancellationRequested) { // WaitKeyTask() でキャンセルが発行されると // Task.Delay().Wait() 明けにこちらに落ちる // 終了に時間がかかるような処理の代わり var t = new Random().Next(1,10); Task.Delay(t * 100).Wait(); Console.WriteLine(); Console.WriteLine("{0}: was canceled", c); return; } } } static void WaitKeyTask(CancellationTokenSource tokenSource) { Console.ReadLine(); // キー入力まち tokenSource.Cancel(); // キャンセル通知 } static void Main(string[] args) { // キャンセル伝播用トークンの作成 var tokenSource = new CancellationTokenSource(); var cancelToken = tokenSource.Token; // キー入力待ち非同期タスクの実行 var waitKeyTask = Task.Run(() =>WaitKeyTask(tokenSource)); // 非同期処理タスクの実行 // var tasks = new List<Task>(); tasks.Add(Task.Run(() => Task1(cancelToken, "a", 100))); tasks.Add(Task.Run(() => Task1(cancelToken, "b", 200))); // キー入力待ち waitKeyTask.Wait(); Console.WriteLine(); Console.WriteLine("キーが押されました。"); Console.WriteLine("すべてのタスクが終了するのを待ちます..."); // 全タスクのキャンセル処理待ち Task.WaitAll(tasks.ToArray()); } } }
Vue.js 練習
これはなに?
私が Vue.js を練習した時の忘備録的記録です。詳しい内容が知りたいならオフィシャルのリファレンスを見たほうがよいです。日本語ですし。
経緯
最近 PHP でもやってみるかな、と思って始めたけど、テンプレートエンジンでいいのがなさそうだったので(というか、Smarty しかつかってないけど)、それなら Vue.js をフロントにしてみては? と思って使い始めてる。気に入らなかったらやめると思う。
ちなみにVue.js をPHPのフロントにする方法は以下のサイトに載ってます。
レガシー管理ページをちょっと動的にするためだけのVue.jsとPHP - Qiita
初めの一歩。Hello World
以下の html を サーバーに置くと Hello Vue!! と表示される。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> {{ message }} </div> <script type="text/javascript"> var v = new Vue({ el: "#app", data: { message: "Hello Vue!!" } }); </script> </body> </html>
<div id="app">
下の要素で {{ }}
でくくった部分を置き換える。というのが基本的な動作のようだ。
属性の動的生成
属性にデータを割り当てたいからと言って <a href="{{ link }}">test</a>
という書き方はできないらしい。その代わりに <a v-bind:href="link">test</a>
という書き方をする。 v-bind:
というプレフィックスを属性名につけることで値にデータ名が使えるようになる。下記の例では link
の部分が #app
に置き換わる。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <a v-bind:href="link">test</a> </div> <script type="text/javascript"> var v1 = new Vue({ el: "#app", data: { link: "#app", } }); </script> </body> </html>
属性に式を指定して動的生成
さっきの例では バインドしている値はデータ名のみだが、値は式でもよい。以下の例では href="/sub_dir/"
となる。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <a v-bind:href="link + '/'">test</a> </div> <script type="text/javascript"> var v1 = new Vue({ el: "#app", data: { link: "/sub_dir", } }); </script> </body> </html>
属性にメソッドを指定して動的生成
式だけではなくてメソッドも可能。オリジナルのメソッドだけではなく、組み込み関数も指定できる。例えば、<a v-bind:href="Math.abs(-100)">test</a>
とすると <a href="100">test</a>
が生成される。意味はない。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <a v-bind:href="goto('/')">test</a> </div> <script type="text/javascript"> var v1 = new Vue({ el: "#app", methods: { goto: function(path) { return "/sub_dir" + path; } } }); </script> </body> </html>
コンポーネント
でもやっぱり、属性に式とか関数とか書きたくない。一回だけならまだいいけど、何回も書くとなるともうちょっと違う感じで書きたい。
コンポーネントは独自のノードを作成するための手法。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <link_to uri="/"></link_to> </div> <script type="text/javascript"> Vue.component('link_to', { props: ['uri'], template: ` <a :href="uri">test</a> ` }) var v1 = new Vue({ el: "#app", }); </script> </body> </html>
こんな感じでかける。ただ、これだど直接書くのと変わらないのでコンポーネント側で関数や式を使って入力値(ここでは uri
) を修飾してあげるとよいかも。ただ、オリジナルのメソッドやデータをコンポーネント内で使いたい場合には以下の例のように this.$root.goto(uri)
のようにし、少し工夫する必要がある。何回も同じような記述をする場合はコンポーネントにまとめておくのがよさそうだ。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <link_to uri="/"></link_to> </div> <script type="text/javascript"> Vue.component('link_to', { props: ['uri'], template: ` <a :href="this.$root.goto(uri)">test</a> ` }) var v1 = new Vue({ el: "#app", methods: { goto: function(uri) { return "/sub_dir" + uri; } } }); </script> </body> </html>
コンポーネントのネスト
先の例では、<a>
要素のテキストが test
固定になってしまって不便。props に足してもいいけど、HTMLっぽく書きたい場合もある。そういう場合は <slot>
を使う。
<html> <head> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> </head> <body> <div id="app"> <link_to uri="/">test</link_to> </div> <script type="text/javascript"> Vue.component('link_to', { props: ['uri'], template: ` <a :href="this.$root.goto(uri)"><slot></slot></a> ` }) var v1 = new Vue({ el: "#app", methods: { goto: function(uri) { return "/sub_dir" + uri; } } }); </script> </body> </html>
おわり
おわり。
ディスクのパーティションを変更後、Linuxの起動が遅った問題
あらすじ
Virtual Box で初回 10GBで仮想ディスクを組んでしまい、途中で空き容量不足でLinuxが固まる問題が発生したので、仮想ディスク容量を拡張したが、パーティション設定が以前のままで、拡張部分が使えず結局空き容量不足という状態だった。KNOPPIXを入れて、gpartedにてパーティション設定を変更し、再度起動すると空き容量の問題は解決したが、起動時にジャーナルの再チェックが必ず入るようになり、1分30秒待たされることになった。
参考サイト
起動で1分半待たされる問題の解決法! | ウィンドウズにおさらばした文系中高年
原因
パーティション設定を変更したことで、スワップのUUIDが更新されてしまい、/etc/fstab のUUIDと違うことでLinuxが異常を検知してしまい、起動時にディスクチェックが走ってしまうのが問題のようです。
復旧方法
- blkid コマンドでUUIDをチェック
$ sudo blkid
- 表示されたUUIDを /etc/fstab に反映
$ sudo vim /etc/fstab
- 起動時に「gave up waiting for suspend/resume device」が出る場合は /etc/initramfs-tools/conf.d/resumeも更新する
$ sudo vim /etc/initramfs-tools/conf.d/resume $ sudo update-initramfs -u
以上!!