Babelを使ってみようとしたらハマった話とドラッグで要素を動かしてみたい話
はじめに
投稿が大変遅れましたが、この記事は JavaScript Advent Calendar 2019 の13日目の記事です。
要素をドラッグして動かしたい。
せっかくなので JavaScript で書いてみよう。
と思ったら Babel ハマったという話です。
Babel と Webpack を使う
TypeScript で書いていたときは、(Promise などを除いて) IE でも動くようトランスパイルしてくれていました。
じゃあ JavaScript では?というと、いくつか方法はあると思うのですが、今回は Babel を使ってみることにしました。
このときと同じく、 Webpack も使います。
インストール
インストール、セットアップは下記ドキュメント( Build system として Webpack を選択)に従えば OK でした。
Babel 、 Webpack に加え、ドラッグで要素を移動させるときに使用する RxJs も追加しておきます。
npm install --save webpack webpack-cli rxjs babel-loader @babel/core @babel/preset-env
設定ファイル
Babel ver.7 では .babelrc というファイルを設定ファイルとして使用するようです(ググってると babel.config.js とか出てきて混乱しました)。
.babelrc
{ "presets": ["@babel/preset-env"], }
サンプルコード(失敗)
MainPage.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello</title> </head> <body> <button onclick="Page.doSomething()">Click</button> </body> <script src="../js/main.bundle.js"></script> </html>
mainPage.js
import { AsyncSample } from "./asyncSample"; export async function doSomething() { const s = new AsyncSample(); await s.doSomethingAsync(); }
asyncSample.js
export class AsyncSample { async doSomethingAsync() { return new Promise((resolve, reject) => { console.log('Hello World!'); resolve(); }) .catch((reason) => reject(reason)); } }
webpack.config.js
var path = require('path'); module.exports = { mode: 'development', entry: { 'main': './src/js/mainPage.js', }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] }, resolve: { extensions: [ '.js' ] }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, './dist/js'), library: 'Page', libraryTarget: 'umd' } };
そしてエラー
npx webpack -w とやるとコンパイルは成功します。
で、ボタンを押すとエラー発生と。
ReferenceError: regeneratorRuntime is not defined
regeneratorRuntime ってなんぞ?というと、async/await を Babel でトランスパイルするときに使われるもののようです。
なんとかする
で、これが足りないのでどうするか。
ぐぐると @babel/polyfill の話( ver.7.4? で Deprecated )が大量に出てきてこれまた混乱するのですが、今は regenerator-runtime と core-js を使うようです。
npm install --save regenerator-runtime core-js
どう使うのか、というと、 async/await や Promise を使うところで import します。
mainPage.js
import { AsyncSample } from "./asyncSample"; import "regenerator-runtime/runtime"; export async function doSomething() { const s = new AsyncSample(); await s.doSomethingAsync(); }
asyncSample.js
import "core-js"; import "regenerator-runtime/runtime"; export class AsyncSample { async doSomethingAsync() { return new Promise((resolve, reject) => { console.log('Hello World!'); resolve(); }) .catch((reason) => reject(reason)); } }
直接使っているわけでないものをインポートするのはしっくりこない気はしますが、別途 Polyfill を入れなくても Promise などに対応してくれるのはありがたいですね。
※ここより先は後ほど追記します(..)_
Angular で PWA に挑戦したい話
はじめに
※2019/12/06 一旦書き終わったところだけ公開いたします。
残りは仕事が終わったあと追記予定です(..)_
この記事は PWA Advent Calendar 2019 の 6 日目の記事です。
JSConf China 2019 で聴いて以来気になってはいた PWA(Progressive Web Application)。
Advent Calender に勢いで参加してみたは良いものの、思った以上に手が回らなかったので今回はとりあえず作って動かしてみる、ということで。
せっかくなので SheetJS を使って Excel 読み込み→表示をしてデスクトップアプリ感?を楽しんでみたいと思います。
インストール
とりあえず雑にプロジェクトを作りまして。。。
ng new pwa-xlsx-sample
ng add で PWA 用のパッケージを追加してもらう、と。
ng add @angular/pwa --project pwa-xlsx-sample
更に SheetJS を追加しておきます。
npm install xlsx --save
で ng s とかすればよいのかしら?と思ったらそうではなく、ビルドしてサーバー上で動かす( ng s ではなく)必要があるようです。
npm install http-server --save ng build --prod
ようやく?準備ができたので、早速実行してみます。
npx http-server -p 8080 -c-1 dist/pwa-xlsx-sample
PC の Chrome などではどこからたどれば良いのかわからなかったのですが、 Android の Chrome からは Home に追加することも確認できました。
PWA 完全に理解した。
【TypeScript】type の使いどころが知りたい話
はじめに
※2020/04/12
お前の仕事はいつまで続くんだという話ですが、ようやく書き終わりました。。。
この記事は TypeScript Advent Calendar 2019 の 6 日目の記事です。
ふとコードを書いていて気になったのが、例えばサーバー側から受け取った値を JSON に変換したいとき、 class を使うべき? interface を使うべき? それとも type ?ということでした。
C# であれば class を選択することになります。
では TypeScript の場合は?
というか、 C# にも存在する class と interface はともかく、 type って何よ?どう使うの?となったので調べてみることにしました。
type について
まず type (正確には Type Aliases) とは何か、という話から。
alias (別名)の名前通り、型に対する別名をつけるためのものです。
使い方はこんな感じです。
type SampleType = string; let sample: SampleType = "hello"; type SampleType2 = { id: number, name: string, } let sample2: SampleType2 = { id: 0, name: "world", }
interface と type aliases
共通点
よく比較される interface と type aliases ですが、実際使い方によっては全く区別がつかない場合もあります。
interface SampleInterface { id: number; name: string; } let sample3: SampleInterface = { id: 1, name: "!!!", }
どちらも JavaScript への変換後は消えてしまうためあくまで TypeScript 内での振る舞いに留まる点や、同じプロパティを持っていればその interface / type aliases として扱われる点など。
よく言われる(?)のが、(type aliases は継承ができないため)継承が必要なら interface を使う、という内容。
ver.3.8.3 現在、Classが実装する(implements)場合はどちらも可能です。
interface ISample{ name: string; } type TSample = { message: string }; /** OK */ class IClassSample implements ISample { public name: string = 'Hello' } /** OK */ class TClassSample implements TSample { public message: string = 'World' }
interface / type aliases で違いはあるのか?その使い分けは?というのが気になったので調べてみることにしました。
(口調がいかがでしたかブログっぽいな)
違い
declaration merging
interface の大きな特徴(だと思う)は "declaration merging" です。
同じ namespace 、同じファイル、同じモジュール内に同名の interface がある場合、ひとまとまりの interface として扱われます。
interface ISample{ name: string; } interface ISample{ message: string; } function main() { // 1つ目、2つ目の ISample が統合される. const iSample: ISample = { name: 'Hello', message: 'World' }; }
同様のことは type alias で Cross 型を使って再現することはできますが、明示的に書く必要があります。
interface ISample{
name: string;
}
type TSample = {
message: string
};
type TCrossSample = ISample & TSample
function main() {
const tSample: TCrossSample = {
name: 'Hello',
message: 'World'
};
}
extends
これも interface のみの特徴ですが、 interface は interface や type aliases を拡張 (extends) できます。
interface ISample{
name: string;
}
type TSample = {
message: string
};
interface IExtendSample extends ISample, TSample {
id: number;
}
function main() {
const iSample: IExtendSample = {
id: 0,
name: 'Hello',
message: 'World'
};
}
これも同じく type alias で Cross 型を使って再現できます。
定義できる型
interface で定義できるのは下記のような形式のみです。
(propertyName はなくてもOK)
interface InterfaceName {
propertyName: Type
};
type aliases はあくまで型に別名をつけるだけ、ということもあってか、色々な種類の型を定義できます。
interface ISample{
name: string;
}
type TSample = {
message: string
};
/** Union type */
type TUnionSample = ISample|TSample;
/** Cross type */
type TCrossSample = ISample & TSample & { id: number; }
/** Tuple */
type TTupleSample = [string, number];
/** Function */
type TFunctionSample = (message: string) => void;
/** Object */
type TObjectSample = {
message: string
};
/** Mapped type */
type TBase = {
id: number,
name: string
};
type TMappedSample = { [P in keyof TBase]: string; };
/** Conditional type */
type TConditionalSample = T extends string? 'string': 'other';
function main() {
const crossSample: TCrossSample = {
id: 0,
name: 'Hello',
message: 'World'
};
const tupleSample: TTupleSample = ['hello', 0];
const mappedSample: TMappedSample = {
id: '0',
name: 'Hello'
};
const conditionalSample: TConditionalSample = 'string';
}
Union 型、 Tuple は type aliases のみで定義できるため、 interface は使えません。
いつ type aliases を使うべきか?
interface と type aliases の違いはわかった気がしますが、 ではいつ type aliases を使うか。
(どちらも使用可能な場合)
Effective TypeScript によると、プロジェクトのスタイルに合わせる・または "declaration merging" が必要かどうかで判断するべき、と。
個人的には C# に慣れていることもあり、振る舞い(メソッド)を定義して Class で実装する場合は interface 、Data Transfer Object の用に、データを格納するためのオブジェクトは type aliases を使いたい気がします。
参照
ASP.NET Core + Entity Framework Core のプロジェクトを 2.2 から 3.0 にアップグレードした話
はじめに
この記事は C# Advent Calendar 2019 の二日目の記事です。
冷静に考えるとこれを C# の話として書いていいのか?と今更ながら思ったりもするのですが、プロジェクトは C# で書いてるし、 C# に関連する話もないではないから許してください(..)_
環境
- .NET Core : ver.2.2.402 (from), ver.3.0.100 (to)
- Microsoft.EntityFrameworkCore : ver.2.2.6 (from), ver.3.0.0 (to)
- Npgsql.EntityFrameworkCore.PostgreSQL : ver.2.2.4 (from), ver.3.0.1 (to)
- Windows10 : ver.1903
- Rider : ver.2019.2.3
- PostgreSQL : ver.12.0
元のプロジェクト
まずはアップグレードする前のプロジェクトを用意します。
UpgradeSample.csproj
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> <RuntimeIdentifier>win-x86</RuntimeIdentifier> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.4" /> </ItemGroup> </Project>
- RuntimeIdentifier は Self-contained app (実行環境に .NET Core が入っていなくても動作するよう出力されたアプリケーション) のために追加しています。
- Program.cs も存在しますが、特にプロジェクト作成時から変更なく、 3.0 にアップグレード後も触らないため省略しています。
Startup.cs
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Serialization; using UpgradeSample.Models; using UpgradeSample.Products; namespace UpgradeSample { public class Startup { private IConfigurationRoot Configuration { get; } public Startup(IHostingEnvironment env) { // config ファイル読み込み. var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", false, true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", false, true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public void ConfigureServices(IServiceCollection services) { // DB Connect services.AddDbContext<UpgradeSampleContext>(options => options.UseNpgsql(Configuration["DbConnect"])); // 生成される JSON のプロパティ名を大文字始まりで出力する. services.AddMvc() .AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); }); // DI services.AddScoped<IProductService, ProductService>(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseMvc(); } } }
ApiController.cs
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using UpgradeSample.Models; using UpgradeSample.Products; namespace UpgradeSample.Controllers { public class ApiController: Controller { private readonly IProductService _productService; public ApiController(IProductService productService) { _productService = productService; } [Route("/")] [Route("/Home")] public string Index() { return "hello"; } [HttpGet] [Route("/products")] public async Task<List<Product>> GetProducts([FromQuery] string[] names) { return await _productService.GetProductsAsync(names); } } }
Product.cs
using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; namespace UpgradeSample.Models { [Table("Product")] public class Product { [Column("ProductId")] [JsonProperty("ProductId")] public int? ProductId { get; set; } [Column("ProductName")] [JsonProperty("ProductName")] public string ProductName { get; set; } } }
UpgradeSampleContext.cs
using Microsoft.EntityFrameworkCore; namespace UpgradeSample.Models { public class UpgradeSampleContext: DbContext { public UpgradeSampleContext(DbContextOptions<UpgradeSampleContext> options) :base(options) { } public DbSet<Product> Products { get; set; } } }
IProductService.cs
using System.Collections.Generic; using System.Threading.Tasks; using UpgradeSample.Models; namespace UpgradeSample.Products { public interface IProductService { Task<List<Product>> GetProductsAsync(string[] names); } }
ProductService.cs
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using UpgradeSample.Models; namespace UpgradeSample.Products { public class ProductService: IProductService { private readonly UpgradeSampleContext _context; public ProductService(UpgradeSampleContext context) { _context = context; } public async Task<List<Product>> GetProductsAsync(string[] names) { return await _context.Products .Where(p => names.Length <= 0 || names.Any(n => p.ProductName == n)) .ToListAsync(); } } }
いかん。。。元のコードを並べるだけで結構スペースを取ってしまった。。。
3.0 にアップグレードする
気を取り直して早速 3.0 にアップグレードしてみますよ。
プロジェクトのバージョンを更新するには、 1 つまたは 2 つのファイルを変更する必要があります。
global.json
{ "sdk": { "version": "3.0.100" } }
UpgradeSample.csproj
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> <RuntimeIdentifier>win-x86</RuntimeIdentifier> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" /> </ItemGroup> </Project>
- global.json は Rider でプロジェクトを作った場合は出力されていましたが、 Powershell で dotnet new empty を実行して作った場合はありませんでした。
- Rider ではもう一つ、 Run/Debug configuration のバージョンも上げる必要があるかもしれません。
- これらのファイルを変更後、自動で実行されない場合は dotnet restore を実行します。
エラーを修正する
メジャーバージョンアップということで、主に下記に関連してエラーや警告が出ました。
- Endpoint Routing への変更
- Newtonsoft.Json から System.Text.Json への変更
- Entity Framework Core で、 Linq のクエリをクライアントで実行しない
RuntimeIdentifier がロードできない
- What's new in ASP.NET Core 3.0 - Microsoft Docs
- Comparing Startup.cs between the ASP.NET Core 3.0 templates: Exploring ASP.NET Core 3.0 - Part 2 - Andrew Lock | .NET Escapades
- My First Look at ASP.NET Core 3.0 - Shawn Wildermuth
1. MVC から Endpoint Routing への変更
2.2 ではルーティングを使うのに AddMvc, UseMvc を使っていましたが、 3.0 からは AddControllers, UseRouting, UseEndpoints に変わります。
Startup.cs
using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Identity; using Models; using UpgradeSample.Models; using UpgradeSample.Products; using UpgradeSample.Users; namespace UpgradeSample { public class Startup { ... public void ConfigureServices(IServiceCollection services) { ... // for setting JSON parameter names pascal case. services.AddControllers() .AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null); // DI ... } public void Configure(IApplicationBuilder app, IHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { // [Routing("")] を使ってルーティングする場合 endpoints.MapControllers(); }); } } }
- ASP.NET Core 2.2 で追加された Endpoint Routing と Core MVC での互換性 - しばやん雑記
- Routing in ASP.NET Core - Microsoft Docs
- Understanding ASP.NET Core Endpoint Routing - aregcode
- Endpoint Routing in ASP.NET Core 3.0 - Shawn Wildermuth
また今回は使用していませんが、認証などを行う場合、 UseRouting の後、 UseEndpoints の前に UseAuthentication, と UseAuthorization を記載順に実行する必要があります(順序が違ったり UseAuthentication が実行されていないと実行時に例外が発生します)。
操作しようとした人が誰かを確認した後、その人がその操作を実行できるか?を確認する、ということで、考えれば順序がわかるのですが、実行時に例外が発生するという辺りはちょっとややこしいですね。
2. Newtonsoft.Json から System.Text.Json への変更
3.0 にアップグレードしたときにコンパイルエラーが発生したのは、この Json 関連だけでした。
今回のサンプルで対象となるのは 2 箇所です。
Startup.cs
using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Identity; using Models; using UpgradeSample.Models; using UpgradeSample.Products; using UpgradeSample.Users; namespace UpgradeSample { public class Startup { ... public void ConfigureServices(IServiceCollection services) { ... // for setting JSON parameter names pascal case. services.AddControllers() .AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null); // DI ... } ...
Product.cs
using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; namespace UpgradeSample.Models { [Table("Product")] public class Product { [Column("ProductId")] public int? ProductId { get; set; } [Column("ProductName")] [JsonPropertyName("ProductName")] public string ProductName { get; set; } } }
基本的な使い方は変わらず、書き方が微妙に違っている、という感じです。
3. Entity Framework Core で、 Linq のクエリをクライアントで実行しない
個人的には一番影響が大きいのでは?と思っているところです。
Entity Framework Core で、 Linq を使ったコード( Where )が SQL に変換できない場合、例外を発生するようになりました。
これによってパフォーマンスの悪化が防げる、というメリットと、 List を Linq を使って検索するつもりで雑にアクセスしていると実行時例外を発生させまくるというデメリットがあります。
ProductService.cs
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using UpgradeSample.Models; namespace UpgradeSample.Products { public class ProductService: IProductService { ... public async Task<List<Product>> GetProductsAsync(string[] names) { // 実行時に例外発生 return await _context.Products .Where(p => names.Length <= 0 || names.Any(n => p.ProductName == n)) .ToListAsync(); } } }
対策としては、 Any など SQL に変換できない条件で検索したい場合、先に AsEnumerable を実行する、というのが考えられます。
ProductService.cs
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using UpgradeSample.Models; namespace UpgradeSample.Products { public class ProductService: IProductService { ... public async Task<List<Product>> GetProductsAsync(string[] names) { return await Task.Run(() => { return _context.Products .AsEnumerable() .Where(p => names.Length <= 0 || names.Any(n => p.ProductName == n)) .ToList(); }); } } }
ややこしいのが、例えば Where の中身が例えば Where(p => p.ProductId == id) のような内容なら( id は引数となどで渡されているとして)わざわざ AsEnumerable を含む必要がない、というところ。
全部に AsEnumerable を含む、というのはおかしな気がするので、できるだけ Any などを使わず、 SQL に変換できるように書くのが良い。。。のかしら?
- ASP.NET Core / Entity Framework Core 3.0 の気になった機能と 2.2 からの移行 - しばやん雑記
- Breaking changes included in EF Core 3.0
BeginTransaction
今回使用していませんが、 C# 8 で using 句に await が使えるようになった影響で、トランザクション開始時に実行する _context.Database.BeginTransaction() が await できるようになりました。
await using (var transaction = _context.Database.BeginTransaction())
public async Task SetUserNameAsync(ApplicationUser user, string userName, CancellationToken cancellationToken) { await using var transaction = _context.Database.BeginTransaction(); try { user.UserName = userName; _context.Users.Update(user); _context.SaveChanges(); transaction.Commit(); } catch (Exception e) { transaction.Rollback(); } }
Rider 先生曰く await using(var transaction = _context.Database.BeginTransaction()){ } より上記のように {} を使わない書き方を推奨、ということで、なぜかしら?と思っていたのですが、どうも単にネストを浅くしようとしているだけのようです。
4. RuntimeIdentifier がロードできない
3.0 にアップグレード後、 win-x86 のパッケージ?がロードできない旨の警告の後、全 C# クラスがエラーになる問題が発生してビビったという話です。
あれこれ試してみた結果、リストア -> 再ビルド で解決しました。
Single-file executable
また RuntimeIdentifier に関連して? Single-file executable というものが追加されています。
これまで Self-contained app で出力すると大量にファイルが出力されていたのですが、この設定をしておくとファイル数がぐっと少なくなります。
UpgradeSample.csproj
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> <RuntimeIdentifier>win-x86</RuntimeIdentifier> <PublishSingleFile>true</PublishSingleFile> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" /> </ItemGroup> </Project>
これで通常通り dotnet publish -c Release -r win-x86 を実行すると下記ファイルが出力されます。
おわりに
まだおっかなびっくり試しているのと、慌ててやるのはダメゼッタイというところではありますが、壊滅的な状況になることもなく 3.0 に上げることができました。
サポート期間的なこともありますが、パフォーマンスなど多くの改善点が含まれているだけに、早めに更新していきたいところです。
C# Advent Calendar, 明日は @takayoshitanaka さんです。
よろしくお願いいたします(..)_
Angular(ver.8.2) のページを IE11 に対応させたい話
はじめに
この記事は Angular #2 Advent Calendar 2019 一日目の記事です。
以前からちょろちょろ触っていた Angular ですが、そろそろ試しに使ってみたい、ということで、実践を意識して(どうにも逃れられない) IE11 の対応に挑戦してみたいと思います。
Environment
IE11 でページを表示する
デフォルトでは真っ白のページしか表示されないので、まずは表示されるようにしてみます。
といってもやったことは下記に従っただけです。
一応変更した内容を挙げておきます。
- プロジェクトに tsconfig-es5.app.json というファイルを追加
- angular.json で 1.が読み込まれるよう変更
- ng serve --configuration es5 で実行( package.json の Scripts に追加しても良い)
2.について、サンプルを丸コピーしたらプロジェクトが見つからないとエラーになりました。
angular.json
"serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { ... }, "configurations": { "production": { ... }, "es5": { "browserTarget": "app:build:es5" ← ここでエラー。 app をプロジェクト名にする必要がある。 } } },
Grid layout を有効にする
Angular には 以前 PostCSS で利用していた Autoprefixer が組み込まれており、 Flexbox などは IE でもそのまま使用できます。
ただ、 Grid Layout は対応されていない(多分)ため、個別に対応が必要です。
PostCSS の場合は postcss.config.js に書いていたわけですが、 Angular の場合は CSS に下記を追加します。
/* autoprefixer grid: autoplace */ #grid-frame{ height: 30%; width: 90%; display: grid; grid-template-columns: 10% 10%; grid-template-rows: 15% 85%; } ...
これで IE11 でも Grid Layout が有効になります。
ただし、この状態だと grid-column と grid-row が聞かず、 要素を横に grid-template-columns の数だけ並べ、超過すると改行、という動作となります。
autoplace の部分を変更すれば良さげですが。。。
背景色に Alpha のある色を設定する
まぁ Angular 独自の話ではないのですが。。。
000000AA のように、 background-color で RGBA で色指定をしてしまうと、 IE では色が表示されません。
rgba(r, g, b, a) を使って指定する必要があります。
#frame-background{ background-color: rgba(17, 17, 17, 0.67); }
PostCSS を使っていたときは自動で変換してくれていたので、つい見逃していました orz
Pikaday を使う
Chrome や Firefox などのブラウザでは input type="date" とすれば Date Picker が使えるわけですが、 IE ではそうはいかない、ということで、以前使用した Pikaday を使えるようにしたいと思います。
本当は Angular 用に用意された Date Picker もあるようなので、そちらにした方が便利なのかもしれませんが、仕事でも既に Pikaday を使っているため、まずはそちらを試してみます。
Install
npm install --save moment pikaday @types/pikaday
正直なところ、基本的な使い方はほぼ一緒です。
CSS(pikaday.css) は各画面共通ということで styles.css でインポートすることにしました。
styles.css
@import '../node_modules/pikaday/css/pikaday.css';
あとは Pikaday に関連付けるだけです。
今回は top-page というコンポーネントで使うことにしました。
top-page.component.ts
import { Component, OnInit } from '@angular/core'; import * as Pikaday from 'pikaday'; ~省略~ export class TopPageComponent implements OnInit { ~省略~ ngOnInit() { let picker = new Pikaday({ field: document.getElementById('datepicker') }); } }
top-page.component.html
<input type="text" id="datepicker">
Data bindings
さて、せっかくの Angular なので、値の受け取りは Two-way data binding を使いたいところ。
が、何も考えずに [(ngModel)]="dateText" のように書いてしまうと、 Pikaday で値が入力された場合にイベントが発火せず、 dateText の中身は空のままです。
この原因は上記で設定されているイベントが (input) であるためで、これを (change) にしてやる必要があります。
top-page.component.html
<input type="text" id="datepicker" [value]="dateText" (change)="dateText=$event.target.value">
- top-page.component.ts に dateText: string を追加してください
- Template Syntax - Angular
おわりに
基本的には自動で良い感じにしてくれる、ということで大変ありがたいのですが、 Angular に任せておきさえすればすべてうまくいく、というわけでもない辺りがちょっと辛いですね。
(主に IE の特に CSS 周りの対応)
まぁあれこれ地雷を踏み抜きながら覚えていくしかないか。。。
個人的には JS Framework 無しから Angular に切り替えて、一番嬉しいのは HTML の部分を TypeScript(JavaScript) ではなく HTML として書ける、ということかなと思っています。
コードで生成しても良いのですが、規模が大きくなってくるとやっぱり分かりづらい。。。
React や Vue だとまた違っていると思うので、そちらも試してみたいなとは思っています。
さて Advent Calender ですが、明日は ringtail003 さんです。
よろしくお願いいたします(..)_
JSConf China 2019 in 上海参加記録( 2 日目)
- はじめに
- 会場
- Keynote. Learn as I write the Docs
- 2. Serverless is your BFF
- 3. Entropic package registry
- 4. Redux Based Modular Design
- 5. Diving into TypeScript design metadata with NestJS
- 6. Writing code to refactor code
- 7. JavaScript 的执行 & 性能
- 8. 去除迷雾, 漫谈 WebAssembly
- Panel discussion
はじめに
こちらも途中ですが、 JSConf China 2019 2日目の内容をメモっておきます。
※ 2019/11/09 会場でメモった内容をほぼそのまま書いています。
意味不明な部分や聞き間違い、解釈違いなど多数あると思います。
もうすぐ資料や動画が配信されるはずなので、その後更新する予定です。
会場
- 9:55開始
- 開場時点での参加者数は昨日より少ない。
- チケットのやりとりで Keynote に間に合わないのを恐れたため?(つまり自分と一緒)
- 開始時点でも昨日より少なかった(昨日飲みすぎ?)
- PC自体は共通のMacを使ってるっぽい
- 中国語セッションは 3/8
0. Opening
- 開発者同士の交流、ということを再度強調
Keynote. Learn as I write the Docs
資料
昨日のSessionについて
- 各Sessionの良いところをかんたんな概要に触れながら。
フロントエンド、React を学んできた4年間
- 新しい技術にふれるとき、たくさんのDocsを読むことになる
4 years as Front End
- 最初は何もわからなくてもとにかく楽しかった。
- 3年目、より多くを得るために、Conferenceに参加
- Redux
- React Redux
自分自身について。
- 十分なキャリアがあるか?
- 英語の解釈問題
- 書くことの難しさ
Richard Feynman
Names don't constitude knowlege
tool や library は開発者を助けてくれる
いつ、どこで学ぶか?
誰かに教えることは、学ぶための最も良い方法
- Docsを書くことはこのことに繋がる
- どう書けば相手(ユーザー)に伝わるか、を考えなければならないから
tackling the challenge
- 英語で書くこと。
- 細かい Typo やミスなど気にしない
- 完璧じゃなくても良い
- Tipsの共有による練習
- thesaurusのようなツールを使う
- proof reading
- 他の人に説明する
- 1-1 sharing
- 習慣にすること
- メンテナと直接やり取りできる素晴らしさ
- ライブラリのアーリーアダプターになる
- チームにおけるドメインエキスパートになること
Docs
2. Serverless is your BFF
start
- かんたんなアバターを作るサービス モバイル、PCそれぞれからBFFに接続-> 各Serviceに
Webの歴史
- 1.0はとてもシンプル
- 2.0はたくさんのサービス・機能が生まれた。
- Front end/backend に別れた
- Frontendはツールやフレームワークの登場によりかんたんに。
- Fullstack
Full stack?
- UI -> Product
- 仕様ぎめ、開発、Ops
- Node.jsによりFrontend、Backendで再利用できるコードも増えた
- ただしFrontend、Backendのやり取りは依然困難。
- Backend はとても複雑
- Scalable, Security, Debuggable...
- Prototype -> Production architectureに移るに当たり、気にするものがいっぺんに増える
- Autoscale
Cloud computing
すべて自前のマシンで用意するOn-premissから、FunctionsのみのFaaSへ。
- ビジネスにフォーカスする
Node.js
- Faas
- SCF
- Faas
tencent cloud でどのようにアップロードしているかデモ
- アップロードした写真に帽子をつける
- Ergonomic Upload code and go
Serverless is economic
- reduce team size
- 使っただけ払う
VMだと必要なときもそうでないときも同額かかる
Serverless is reliable
- No ops
summary
- Serverless の紹介。Webの歴史、良いところを取り上げ、Tencent cloudを使うデモも。
- Live coding, Live debugging
3. Entropic package registry
What is package manager?
- Registory <--> client
- Do install work?
- publish permission
What is package?
- Softwareをひとまとめにしたもの
- NPMなどであつかう
- Npm registry -> npm -> disk
- disk -> webpack -> bunndle ひとまとめに
Node.js
- Node.jsも一つの file manager
- disk -> Node.js -> commonjs
- 動的に package manager を動かす?
yarn
- cache -> yarn -> Pnt resolution
Plugin
- loader of Node.js
Tink
- Disk にキャッシュする
Recap
基本的に
- 全部 Package managerを使う。
Entropic
- Entropic は Package を一箇所にまとめるのではなく、個別に分かれている
- それらが連携しあって動く。
4. Redux Based Modular Design
Slide
Motivation
- build large app
- Modular -> 模块化
- サイズが大きくなってくると、
- Combine reducers,Middleware, connection
- 機能ごとにクラス分け
modularity
困難
- CoreのBuisiness logicのUIからの分離
- 状態管理
- カプセル化
Pros
- CDのしやすさ Divide, conquer
- テストの完全自動化
Motivation & Goal
Redux Cons
- Immutable data
- 実現が難しい?
- verbose update operations
- Boilerplate Code が増える
- Modularity
- Loose, difficult
- ロジックが分割しづらい(UIに入り込んでるとか)
Solution
usm-redux
- Reducer
- @state decorateを使って状態管理、
- @action で処理を定義。関数につく。
- 状態を変える
- Router 的な動きが実現できるので、switchなどを取り除ける
- 引数にstateを追加
Action type
dispatch
- イベント処理の分割
Module Dependency
- Module inject
- Customizable Iocのようなものをつかう
Immutable data update
immer
- 今の値をベースにImmutable stateを作る
benefit
5. Diving into TypeScript design metadata with NestJS
Slide
memo
- KoaもExpressやNest.JSと同じフレームワークらしい
Design-time metadata
- Reflection
Parameter type metadata
- extract type
- DI, Type -> Nest.js feature
DIの説明
- @Injectable() をつけることで、複数のDependencyを解決できる
Compiler metadata
decorate( [metadata('design:parametypes', [HttpService, Logger])], CatsService,
Reflection API
Reflect.getMetadata('design:parametypes',CatsService)
Pipes
- castなども使われる
PipeTransform{ transform(
) }
Reflection API challenges
interfaces
- 依存interfaceの場合、JSにしたときはObjectとなる
Potential solution
- abstruct solution
Generics
- Genericsの場合にどう解決する?
- JSにするとクラス情報はなくなってしまう
*
Circular dependency
- JSにするとクラス情報はなくなってしまう
*
- 依存クラスがJS変換時にundefinedに
- @Inject( =>
Type metadata
export class User{ email: string; name: string; } ↓ export class User{ @Property email: string; @Property name: string; }
複数プロパティがあるとき Reflect.getMetadata('design: type, new User(), 'email')だと正しくmetadata がundefined になってしまい、Typeがとれない
@Propertyを使う
What's the point
AST
TypeScript Plugins
Return type metadata
- OpenAPI gRPC, GraphQL mutations
戻り地がPromise< string>のとき、Reflect.getMetadata が Promiseになる。stringは?
Recap
design-time metada
TODO: news letter 登録
6. Writing code to refactor code
Slide
概要
- 今年のはじめに実施した大規模なリファクタリングの話
- AST(Abstruct Syntax Trees)
はじめに
- Zenhubでは CommonJS-> ES6に変更した。
- 1752files
- 5 years old
Large scale refactoring のながれ(失敗)
- Find and Replace
- どうにもならん\(^o^)/
ASTの紹介
codemod
find & replace inspect the code
- Prettier
- ESLint autofix
webpack, babel transpilationはcodemodではない
- Codemod in action
- Transforming requires to import
- CommonJSの var a = require("doge.js").wow => import { wow as amused } from "doge.js"
- iTerm2
- npx codemod としてるから、npm でインストールするツールか?
from が同じものがある場合、combine?
ASTはリファクタリングのときにも使える
- Codemodだけでなく、StaticTypesなどいろんなツールがある
7. JavaScript 的执行 & 性能
Slide
はじめに
- a.b = 123 と a[' b'].b のパフォーマンス差?
- esparse
- V8と? の比較
- d8でどう解釈されているか
- BinaryExpression
bytecode
- staGlobal
- jerry --show-opcodes add.js
- V8での解釈・実行の流れや発生したパフォーマンス問題など
WebAssembly
JavaScript の歴史
- Mocha、LiveScript,
- JavaApplet, ActionScript, SilverLight
V8
- V8によって、JavaScriptがWebフロントエンドでの立場を不動のものにした?
8. 去除迷雾, 漫谈 WebAssembly
- C/C++, Go などの複数のから WAコードを生成
- ブラウザ上では実行できない?
WAはJavaScriptにとって代わるか?
- 補うもの
- JSで直接WebAssemblyを書くことはできない
実行速度はあまり変わらない?
- V8の場合、JS -> AST -> ignition -> turofan -> local machine code
- WA(.wasm) -> Liftoff -> TurboFan -> local machine code
パフォーマンスは変わらない?
- WAはDownload, Decodeを同時にするのでパフォーマンスが上がる
- 最適化
WA moduleの構成
- function signature
- run time
おすすめの言語
- Rust
- WA を推し進めているMozillaが作っている言語
Demo
- 書くときは普通にRustをかく。実行時はバイナリコードに。
- バイナリコードに変換する?ツールあり。
Panel discussion
翻訳するときに、ネイティブのような表現ができない
問題のあるパッケージが混ざった場合にどう対応するか
- npmなどと同じ?
Nest.jsでInversifyJSを使わない理由?
- InversifyJS自体は使っていないから実際のところはわからないが、 使い方が違う?
- 少なくともInversifyJSなど他のものを使うことは進めない
ASTによるリファクタリングが向いてないとき?
CommonJS -> ES6には使えるが、JS -> TSではASTによるリファクタリングが有効か?
- おそらく
Jerry.js
WAのセキュリティ上の問題など?将来的な問題が発生する恐れはある?
- すでに対応はしている
ドキュメントが人に伝わらないとき?
- 言い方を変えてみる、細かく説明してみる
- 時間をあけてもう一度自分で読んでみる。
- 理解できる?
Middleware of NestJS?
- Expressのものと同じ?
- HTTPアプリケーションを作るときには依存する
Nest.js は GraphQL に対応している?
- Yes.
- GraphQL のデータの作り方は2種類あり、Code firstとData first?がある
WAはマルチスレッドなどの対応している?
- している?が使ったことはない
- 基本的にもとの言語に依存する
- ブラウザの機能を使う?
CherryScript Quick.js?
Entrophic Production ready?
- No
- いつ? -> まだわからなそう
JSConf China 2019 in 上海参加記録( 1 日目)
- はじめに
- 会場
- Opening & Opening Note
- Keynote. Continuous learning & leadership evolution
- 2. The beauty of TypeScript
- 3. ClojureScript - Winning through simplicity
- 4. Modern GraphQL workflow
- 5. Building (Progressive) Web Apps
- 6. 面向传统, Serverless 进化之路
- 7. Evolution of Quality, Support, Releases at NIKI
- 8. 响应式编程使你看得更远
- Panel Discussion
はじめに
間が空いてしまいましたが、 JSConf China 2019 1日目の内容をメモっておきます。
※ 2019/11/06 会場でメモった内容をほぼそのまま書いています。
意味不明な部分や聞き間違い、解釈違いなど多数あると思います。
もうすぐ資料や動画が配信されるはずなので、その後更新する予定です。
会場
まずは会場の様子から。
ステージ
- 中央にステージと大きなスクリーンがあり、左右に少し小さめのスクリーンがありました。
- 英語セッションのときは中央のスクリーンの両端に、中国語訳がつけられていました。
椅子など
- パイプ椅子が並んでおり、机は最前列のみ設置されていました。
(長時間座っているとお尻が痛い(´・ω・`)) - 端の方には休憩スペースがあり、休憩時間にはコーヒーを飲んだり転がったり。。
- 休憩時間中、席に PC を置いて行く方がちらほら。信頼のあらわれ?面倒だっただけ?
- みんな Mac を使っていたのでちょっと欲しくなるなど。
参加者について
- 男女比は 9:1 くらい。参加者はざっと 300 人ぐらい。
- 欧米系の方もちらほら見られた。スタッフをしてくれている方も。
各セッションの言語
- 8 割くらい英語で行われていた。国際的なカンファレンスだから?海外ゲストにもわかるように?
- ただし、観客の方は(自分の周囲だけかもだが)最後の方はしんどそうだった。わかる。わかるよ。
- パネルディスカッションのときは司会の方が通訳したりもしていたが、間に合わず端折るところも。ですよね~。
Opening & Opening Note
- 今年で 10 年目 & 7 回目(毎年開催しているわけではない)
- 10年前には React も GraphQL もなかった。
- 中国内の各地で開催しており、上海の他北京、深センなどでも。
- カンファレンスの理念は、開発者同士でコミュニケーションを取ること。
- Web をめぐる開発環境はどんどん進化して便利になっているが、コミュニケーションについては十分とは言えない
- Opening にて Wechat でグループを作り、皆で参加する。
- 人数は 120 人くらいに
- 質問、休憩時間などでの連絡、終了後の連絡などで使われている
Keynote. Continuous learning & leadership evolution
Video
※このカンファレンスのものではありませんが、同じタイトルで別のカンファレンスで話ししている動画があったため載せておきます。
Optimized for cost
- deliver technology
- stranger pattern
- deliver value team
- move up team
- 将来的に何を選ぶ
- 客やこれまでの経緯など、とりまく環境の共有
チームをどうまとめていくか
- チームごとに担う仕事はバラバラである。特に QA チームは完全に開発チームから離れている
これらをどうまとめていくか
product model
- A3 problem solving
- Speed to value
- DevOps
- 計測しないと解決できない
- focus just speed
- 新しい環境に移ったら、90 日で慣れる?
- 実際のグラフ、カンバン?の様子なども見せてくれていた。
- Roleも大切
- global technology
- 他のチームに入っててこの役割
- data with context drives clear
- sharing success
- 以前は失敗とはネガティブなものだった
- who made change
- 組織ごとに異なる
- それに合わせる
- leadership need evolve
2. The beauty of TypeScript
Slide
最も良い言語は何か?
- 状況による。例えば 128kb RAM の環境なら C が選ばれるだろう
- フロントエンドなら JavaScript/TypeScript
- 高开发なら Golang や Java (C# 、 Python の人気は今ひとつのよう)
どういう状況だと TypeScript が一番輝くか?
- 開発者が多い
- 開発する項目数が多い
- TypeScript ならどんな JavaScript も TypeScript として扱うことができる
TypeScript の使用状況
- 使ったことのある人は八割くらい。
- 殆どの業務で TypeScript を使う人は四割くらい
JavaScript の辛いところ
- Top 10 JavaScript errors from 1000+ projects(and how to avoid them) - Rolllbar
- Intellisense, Error checking, Quick Fixes, IntelliCode
- IntelliCode -> 書いたコードの内容から、次に書きそうな内容を導き出して入力予測として表示
JavaScript のプロジェクトを TypeScript に変換するときの便利ツール?
- コードに下記を追加すると VS Code で処理を実行してくれるらしい
@ts-ignore
- TypeScript に変換したときのコンパイルエラーを消してくれる
@ts-nocheck
- @ts-ignore の処理をまとめて実行してくれる
@ts-check
- JavaScript のコードに対して実行し、 TypeScript に変換したときにコンパイルエラーになるところを検出してくれる。
3. ClojureScript - Winning through simplicity
Problem of JavaScript
- increase complexy
- デバッグしにくい
- window Scope -> TypeScriptと同じような問題
Choose simplicity over complexity
Compile はBavel で慣れてるでしょ?
Simplest language?
- Lisp!: (function argument)
- プログラムしないのが最強
- s-Expression
- tree/graph
Lisp
- Lisp はシンプルだけでなく古い(歴史がある)
- John McCarthy
Paul Graham
Brendan Eich
- Creator of JS
Clojure
ClojureScript
- Clojure compiled to JS
- リリースはRichHickeyによりOctober 2007に
- Functional
- Recursion
- メジャーなエディターなら対応してる
- インデントが意味を持つ(Python的な)
- REPL: Command line
- EDN
Database
- Datomic
- DataScript
Datalog
summary
- ClojureScript の紹介。 Lisp -> Clojure -> ClojureScript の流れをJavaScriptとともに
4. Modern GraphQL workflow
Slide
これまで
- サーバーはリクエストに対して全データを返す
- 必要なのはそのうちの一部(ほかは無駄になってしまう)
GraphQL
- 必要な分だけ返す
リクエスト側にも見分けるための仕組みがある?
graphql
- graphql-compose
- merge-graphql-schemas
- graphql-middleware
- apollo-server
GraphQL Joker
- スキャフォールドのためのツール。同じようなものをコピペで作りまくらなきゃいけないものを自動化してくれる。
- JavaScript で、 TypeScript の type のようなファイルを作る
- GitHub - zhangkaiyulw/graphql-joker: GraphQL Joker is the ultimate GraphQL scaffolding tool.
summary
- GraphQL の紹介(REST -> GraphQL への流れから)
- GraphQL Joker の紹介
5. Building (Progressive) Web Apps
- MaaS
What the hack PWA
- Fast
- Integrated
- touch, GPS, etc.
- Reliable
- Engaging
Why PWAs?
- 使用時間のうち87%がNative Appsを使っている
Shun the non PWA
- ネイティブ的にテーマカラーに合わせるとか、そもそもJavaScriptがオフにされていたら無理
- Offline時にレスポンスが遅い
Dev tool
- Lighthouse
- PWA の条件を満たしているかを調べてくれる
- PWA の条件の参考: プログレッシブウェブアプリの紹介 - プログレッシブウェブアプリ | MDN
Being Reliable
Angularの場合
- ng add @angular/pwa
- React, Vueもかんたんに入れられる
Workbox
Being Faster
- JPEG !== JS??
- サイズの話だったっぽい。でかすぎるのは☓
- https://angular.io/guide/universal
- Puppeteer
Being Engaging & Interactive
- https://app-manifest.firebaseapp.com/
- https://ngpwa.com
- https://github.com/MichaelSolati
- https://medium.com/@MichaelSolati/pwas-with-angular-being-engaging-83465ffd0313
6. 面向传统, Serverless 进化之路
Slide
memo
- 阿里巴巴のサービスをServerlessにしたときの効果、苦労や手順など。
- BFF 70%
- Node.js
- マシンや人の負荷を下げたい
- すでに過去の遺産がたくさんある
- Serverless への移行は 2018/10 に準備を開始した
- 淘宝网,飞猪大BU に反映
- コストは 100% -> 70% -> 40%
- Ops のリソース
- Dev 側は開発完了後はリソース不要(本当に減らすとリスクが高いため減らすことはできない)
- Note.js のバージョン固定
- 研究開発のリソース
- 多能工化
- 開発者、部署が少なくてもできる
- サーバーへの負荷軽減
- 今までは Controller がひとまとめに受けてきたが、 Edge -> 業務ロジック -> データ と個別にアクセスできる
- 負担軽減へ
- 今までは Controller がひとまとめに受けてきたが、 Edge -> 業務ロジック -> データ と個別にアクセスできる
- クラウド環境での統合テスト
- アップグレードモデルの研究開発
- これまでの期間は一年、まだ完了はしていない
- FaaS + BaaS
- Egg / Mildloway Server
- Http Server
7. Evolution of Quality, Support, Releases at NIKI
How we've transformed in 5 years
- modernization - microservices / cloud
- continous delivery
- follow the sun / GEO and global
- cultural shift
Memo
- 綿密なテストを重ねて失敗を責める文化 -> 早く小さく失敗する文化へ
- DevOps
- Engineering Mindset
- SRE
- Fire Fighting
- Share Ownership
From -> To
- 長くテストをして長い時間をかけてリリースしてた -> CI/CD による早いリリース
- 手動でテスト -> 自動化、Global Execution?、On demand test environments, sv
8. 响应式编程使你看得更远
Slide
過去話
- RxJsは今までも毎回テーマに上がっていたらしい。どんな新しい知識が得られるか?といった紹介からスタート
Rxについて
Memo
- callback -> promise
- async/await
- Message Driven / Responsive / Resilient / Elastic
- Observables
- Observer
- MergeMap -> Stream に流れる○をマージする?
- throttleTime(25)
- debounce & throttle
- switchmap - 競合問題の解決?
- CombineLatest
- withLatestFrom
- TakeUntile
- Cold Observable
- Hot Observable
- Share with "Subject"
- rxjs-hooks
- redux-observable
- Cycle.js
- 人と境界
Panel Discussion
概要
- Wechat で参加者から集めた質問を、登壇者に投げかける
- 質問は英語だったり中国語だったり
- 司会の方が通訳をしていたが、端折ったり翻訳しないことも(まぁ大変すぎですよね)
TypeScript をどのように導入するか
- 一度に全部切り替えるのではなく、一部から期間を決めて導入する
- 阿里巴巴では大部分で TypeScript を使っている(ここ三年くらい)
- 導入のメリットを鑑みる
- 新人も助ける
ClojureScript が強いところ
- 特定の場面で、というわけではない
- 複雑なデータや仕組みをもつものに使うのが良い
GraphQL
- fluent
- filtering
- reasonable request
- DB にとっても良いところ?
How the computability of PWAs
GraphQL には swagger や OpenAPI のような標準がある?
- Browse Schema であり、 API ではないのでそのようなものはないのでは?