vue.js

Vue 3.0 について Podcast 番組風に語る

はいどーも、えー、うーねこ @unecochan です。Podcast 要素は特にありません。

[和訳] 次期 Vue (v3.0) の計画 / Plans for the Next Iteration of Vue.js - Qiita

こちらの記事なんですが、翻訳したはいいけど肝心の中身について触れていなかったので、書きます。
前提として、私は Vue で好きにやっていいよって言われた場合は ↓ みたいな感じでやることが多いです。

nyan.png
(vue-cli の vue ui のスクリーンショットです)

趣味が出てるのは Pug, Element あたりで他は割と一般的なんじゃないでしょうか。TypeScript は新規プロジェクトなら、普通でしょ、うん。
はいそれでは記事のほう、ダラダラ見ていきましょう。

詳細 / The Details

高レベルAPIの変更 / High-Level API Changes

3.0 は、あらゆる transpilation や stage-x features を必要とせずに、ネイティブ ES2015 で快適に使用できる API を提供することを目的として、クラスベースのコンポーネントをネイティブにサポートする。

いきなり「これっすよ!!!」って感じですね。今まで babel をはじめ vue-loader, vue-class-component については散々 ハマって 慣れ親しんできたので、急に「もう要らないよ」って言われてもあんまり実感ないわけですが、ようやく過渡期が終わりそうな動きが見えてきました。コンパイルもランタイムも速くなるしサイズも小さくなる。sourcemap への依存も軽くなるためデバッグも速く・正確になるでしょう。最高に違いありません。

現在のほとんどのオプションは、クラスベースの API に妥当にマッピングされる。class fields や decorator などの stage-x features は、記述体験を向上させるためにオプションとして使用できるようになる。

stage-x features (つまり ここ に載ってるような機能のことですね) との関わりについて明確なポジションを示しているのは良いことです。個人・業務で vue-property-decorator を導入しているので、これらの機能が「記述体験を向上させる」という位置づけなのは腑に落ちました。生 HTML 使うか Pug 使うか、みたいな話にも近いでしょうね。

さらに、API は TypeScript 型推論を考慮して設計される。3.x コードベースそのものも TypeScript で記述されるので、TypeScript のサポートも向上する。(とはいえ、アプリケーションで TypeScript を使用することは完全に任意である)

TypeScript 型推論を考慮して設計されるという点、vue-class-component がネイティブ化するということはつまりそういうことなのであんまり驚きませんが、まさかコードベースそのものを TS で書き直してくるとは……。後に詳しく。

最も多くの変更を受ける部分は、render 関数の中で使われる Virtual DOM のフォーマットである。私たちは現在、主要なライブラリ作者からのフィードバックを集めており、変更内容が確固たるものになり次第詳細を共有するが、あなたが手書きの (JSX でない) render 関数に大きく依存していない限りは、そこそこ明快にアップグレードできるはずだと考えている。

ここで「手書きの (JSX でない) render 関数」とあるのは、Single File Component を使っている以上は全く問題ないということで良いと思います。手書きの render 関数使ってる人で、かつ JSX 使ってない人、という意味でしょうね。翻訳が悪い気がしてきた。(訳者です)

ソースコードアーキテクチャ / Source Code Architecture

よりクリーンでメンテナブルなアーキテクチャのために、私たちは 3.0 を最初から書き直している。特に、よりコントリビューションしやすくなることを心がけている。また複雑さのスコープを分割するため、いくつかの内部機能を個々のパッケージへと切り出している。たとえば、observer モジュールは独自のパブリック API とテストを持つ独自のパッケージになる。しかしこれらは、framework-level-API には影響しない。Vue を使用するために個々のパッケージをいちいちインポートする必要もなく、最終的な Vue のパッケージがそれらの内部パッケージを使って組み立てられるようになる。

妥当な流れですね。パッケージ名は @vue/observer みたいになるのかな。Firebase とかを見てると、ここで環境依存のバグを出さないようにするのは難しそうなイメージがあります。本体と個別パッケージのバージョンのズレで TS の型情報がコンフリクトしたりする。

コードベースも TypeScript で書かれている。これは新しいコードベースにコントリビューションするために TypeScript の熟練が必要になることを意味するが、型情報と IDE のサポートにより、新しいコントリビューターがより簡単に有意義なコントリビューションができるようになると確信している。

これは結構思い切ったと思うんですが、個人的には大歓迎です。VSCode のプラグインさえも TS で autocomplete ばりばりでさらさら〜っと書けてしまうような昨今、型情報があることのありがたみは半端ないです。ユニットテストも型情報があったほうが100倍書きやすいし、かつ今までより薄くできるのではないでしょうか。TS 全盛の時代が近づいていますね。

監視メカニズム / Observation Mechanism

3.0 には Proxy ベースの observer 実装が付属している。これは完全な言語カバレッジを持つリアクティブトラッキングを提供する。これにより、Object.defineProperty に基づいた Vue 2 の現在の実装における多くの制限が解消される:
- プロパティの追加 / 削除の検出
- Array のインデックスの変更 / .length の変化の検出
- Map, Set, WeakMap, WeakSet のサポート

最高ですね。prop を空文字列で初期化しないといけないとか、Array にまつわる observer の制約は、コード量の増大に加えて、本質的でないコードを記述しなければいけない点が (特に初学者にとって) 非常に厄介でした。それがすべて解決された上で、しかも速くなります……素晴らしい。Proxy が使えるブラウザ でしか動かないという制約はありますが、この文書でも散々触れられている IE11 を除いてほぼ動いてるので、大丈夫でしょう。銀行系とかじゃなければ。

新しい observer には以下の機能もある:

まだあんの!? 1項1項が重いのでバラします。

  • observables を作成するための公開 API。これにより、中小規模のシナリオに対応する、軽量でシンプルなクロスコンポーネント状態管理ソリューションが提供される。

これは tc39/proposal-observable 準拠になるのではないかと予測。RxJS の operator がそのまま使えると最高にハッピーです。型レベルでの互換性はちょっと難しそうですが……。

  • Lazy Observation がデフォルトになる。2.x ではあらゆるリアクティブなデータが、その大小に関係なく、起動時に observe されていた。データセットが巨大であれば、アプリの起動に顕著なオーバーヘッドを引き起こす可能性がある。3.x では、アプリケーションの最初に表示される部分をレンダリングするために使用されるデータだけが observe されるようになり、もちろん observation 自体もより高速になる。

意識しなくても、アップデートしただけでかなり動作が速くなる可能性がありますね。Proxy-based observer の Array 対応とのシナジーも大きく、API レスポンスの戻り値をそのまま突っ込んだりしたくなりますね。

  • より正確な変更通知。例: 2.x では、Vue.set を使って強制的に新しいプロパティを追加すると、そのオブジェクトに依存するあらゆるウォッチャが再評価される原因となる。3.x では、特定のプロパティに依存するウォッチャにのみ通知されるようになる。

今までは Vue.setthis.$set を使っているすべての箇所でウォッチャの再評価が行われていたという話。はやく気にしなくてもよくなりたいですね。

  • Immutable observables: ネストされたプロパティにおいても不変な値が作成可能になる。それは内部的に、一時的にアンロックされる場合を除いて変更できない。このメカニズムは、渡された props を凍結したり、Vuex ステートツリーを mutation の外から変更できないようにするために使える。

気持ち悪かった挙動の一つが解消されますね。 Immutable.js とかで頑張ってた方々には朗報ではないでしょうか。TS の層で readonly かけちゃえるのも嬉しい。

  • より良いデバッグ機能: 新しい renderTracked フックと renderTriggered フックを使用して、コンポーネントの再レンダリングがいつ、なぜ track / trigger されるかをより正確にトレースすることができるようになる:

細かいパフォーマンスを追っていくとだいたい再レンダリングで煙に巻かれて困っていたのが解決しますね。

その他のランタイムの改善 / Other Runtime Improvements

より小さい: 新しいコードベースは、基本的に tree-shaking と相性が良いように設計されている。組み込みコンポーネント (<transition>, <keep-alive>) やディレクティブランタイムヘルパ (v-model) のような機能はオンデマンドに tree-shaking 可能にインポートされるようになった。

一応説明しておくと、Tree shaking というのは ES の import 構文を使うことによって「どこからも読み込まれていないファイルを最終的なバンドルから除去する」というような機能です。これによって、パッケージを細かく分割しなくても最終的な JS のファイルサイズを減らすことができるようになります。最近は locale や SVG Icon 周りも JS にバンドルされるようになったので、これを意識しないとファイルサイズはどんどこ膨れ上がります。

新しいランタイムのサイズは基本的に <10kb gzipped である。 加えて、tree-shaking 可能であることにより、今後は使用しないユーザにペイロードのペナルティを課すことなく、より多くの組み込み機能を提供できる。

ちっちゃ。Vue は昔から軽量を標榜しているので、ここも正統進化といえますね。Sizes of JS frameworks, just minified + minified and gzipped, (React, Angular 2, Vue, Ember) 曰く Vue 2.4.2 は 20.9kB, React 16.2.0 は 31.8kB とのことですが、tree-shaking されていない数字なので一概に比べることはできません。

より速い: 事前のベンチマークでは、raw Virtual DOM のマウント & パッチ (私たちは最速の Virtual DOM 実装である Inferno から多くのトリックを学んだ), コンポーネントインスタンスの初期化とデータの observation を含み、全面的に最大で 100% のパフォーマンス向上が見られている。3.0は、あなたのアプリが起動したときに JavaScript で費やされた時間の半分を削減する。

Proxy-based observer に加えて Virtual DOM も高速化しているとのことで、しかも Inferno の元で修行してきましたという風。期待が持てます。

Fragment と Portal: そのサイズが縮小されたにもかかわらず、3.0には、Fragment(複数のルートノードを返すコンポーネント)と Portal(コンポーネント内ではなく DOM の別の部分にサブツリーをレンダリングする)の組み込みサポートが付属する。

ここへ来て新機能です! Fragment はリスト内コンポーネントがより自由に書けるようになりそうです。Portal はモーダルウィンドウとかで CSS でめっちゃ頑張ってたやつが DOM ヒエラルキー上でも自然に書けるというやつですね。どちらも今すぐほしい。

改善された slot のメカニズム: コンパイラによって生成されたすべての slot は、子コンポーネントの render 呼び出し中に関数として呼び出されるようになる。これにより、slot の依存関係が親ではなく子の依存関係になることが保証される。これは、次のことを意味する。1. slot の内容が変更された場合、子のみが再レンダリングされる。2. 親を再レンダリングしても、その slot の内容が変更されなければ、子は再レンダリングする必要はない。この変更により、コンポーネントツリーレベルでより正確な変更の検出が提供されるため、無駄な再レンダリングはさらに少なくなった!

自前で slot を書くことってあんまりないんですが、Element とかサード製の UI フレームワークはめっちゃ使ってるので高速化は嬉しい。

カスタムレンダラ API: カスタムレンダラを作成するためのファーストクラス API が利用可能になり、それらの改変のために Vue コードベースをフォークする必要はなくなった。これにより、Weex や NativeScript-Vue などの render-to-native プロジェクトがアップストリームの変更に対して最新の状態に保ちやすくなる。また、さまざまな目的のためにカスタムレンダラを作成することが簡単になる。

これは超絶グッドニュースですよ! 過去に Weex を真似て cocos2d-x 上で動く Vue 実装を企んだことがあったのですが、ドキュメントは無いしハードフォークしないといけないしで挫折した経験があります。Vue 版の vadimdemedes/ink みたいなのも出てくるんじゃないかな。

コンパイラの改良 / Compiler Improvements

新しい Virtual DOM 実装の改善により、より効果的なコンパイル時の最適化も実行できる。静的なツリーの巻き上げ (static tree hoisting), 静的な props の巻き上げ (static props hoisting), ランタイムで子の正規化をスキップするためのコンパイラヒント (compiler-hints for runtime to skip children normalization), VNode 生成に関するファストパス (VNode creation fast paths), etc...

何言ってるかさっぱりわからん……

テンプレートのコンパイルエラーで位置情報を提供するためにパーサを書き直す予定がある。これは、テンプレートのソースマップのサポートにもつながるはずである。新しいパーサは、eslint-plugin-vue や IDE 言語サービスなど、サードパーティツールとのインテグレーションの基礎となる。

これはわかりますね、めっちゃ最高のやつです。今までテンプレート内でエラー吐いてるとトレースがしんどすぎたので……

IE11 サポート / IE11 Support

がんばってください。「悲しきかな」みたいなこと書いてたのがウケました。

どのように到達するか / How Do We Get There

Vue 3.0 までの道のりを詳細に書いてくれていますが、簡単に言うと「まだ決まってないけど2019年中にはたぶん…」て感じですね。

3. 2.x2.x-next に互換機能を導入する / Introduce Compatible Features in 2.x & 2.x-next

2.x のことも忘れないよ! と言うよりも、私たちは 2.x を使用してユーザに新しい変更を徐々に習得させる予定だ。API の変更は opt-in アダプタを通じて 2.x に徐々に導入していくし、またユーザは 2.x-next で新しい Proxy ベースの observer を試すことができるようになる。

ここは注目です。徐々に 2.x でも使えることになる!特に Proxy ベースの observer については最初はかなり Buggy なはずなので、みんなでガンガン叩いてフィードバックしましょう。Fragment / Portal も待ち遠しいですね。

まとめ

Vue 3.0 めっちゃ楽しみですね! パフォーマンスの向上や新機能もさることながら、Web 技術の進化にぐいぐい乗っていく姿勢を強く感じました。
私は2系から入ったユーザなので1系から2系への移行を経験していないのですが、マイナーで互換性が崩れるたびにマイグレーションガイドが丁寧に書かれていますし、2.x で deprecation warning 出してマイグレーションガイドに使える的な記述もあったので、今のところあんまり不安はないです。

全然関係ないこと

Japan Digital Design 株式会社 という会社で週1日、Vue + TypeScript + Firebase で高速プロトタイピング的なお仕事してます。
Fintech メインだけどなんとなくエッジなこともできる会社で、会社にドローン転がってたりするし、Vue Fes Japan 2018 のシルバースポンサーもやってます。
いま求人めっちゃ頑張ってるので、もし興味あれば Twitter で DM でもいただけるとよしなにいたします。