Closed18

Svelteに入門する

オクトオクト

Svelteとは

https://svelte.dev/

Webアプリケーションを高速にビルドするツールである。
ReactやVueといったJavaScriptのフレームワークと似ており、洗練されたインタラクティブなユーザーインターフェースを簡単に構築することを目的としている。
コードを実行時に解釈するのではなく、ビルド時にVanillaJavaScriptに変換することで、アプリケーションの高速化とパフォーマンスの向上を実現している。
参考:
https://www.twilio.com/blog/all-you-need-to-know-svelte-jp

オクトオクト

公式チュートリアルを試す

https://svelte.dev/tutorial/basics

Dynamic attributes

<script>
  let src = 'hoge.png';
  const name = 'A man';
</script>

<img {src} alt="{name} dances.">  // {src} => `src={src} `

{ }を使って、その中でJavaScriptを扱える。
HTML属性と変数名が一致する場合は{src}のようにショートハンドが使える。
便利すぎ。。。

Styling

Vue.jsなどのように<style>タグを使ってスタイリングをすることができる。

<p>This is a paragraph.</p>

<style>
  /* Write your CSS here */
  p {
	color: purple;
	font-family: 'Comic Sans MS', cursive;
	font-size: 2em;
  }
</style>

ここで重要なことは、この<p>要素に対して当てたスタイルのスコープはこのコンポーネント内である。
つまり、他の場所(スコープ外)に影響を与えない。

Nested components

App.svelte
<script>
  import Nested from './Nested.svelte';
</script>

<p>This is a paragraph.</p>
<Nested />  // This is another paragraph.

<style>
  p {
	color: purple;
	font-family: 'Comic Sans MS', cursive;
	font-size: 2em;
  }
</style>
Nested.svelte
<p>This is another paragraph.</p>

import <component name> from <path>で別のファイルにあるコンポーネントを持ってくる。
ここで気をつけたい点が、持ってきたコンポーネントにはimportしているファイルのスタイルは当たらないことである。
このサンプルでいえば、This is another paragraph.にはパープルのカラーなどが当たらない。

オクトオクト

HTML Tags

Svelteでは、通常文字列をプレーンなものとして認識するため、この文字列の中にHTMLタグを埋め込んでも認識してくれない。

<script>
  let string = `this string contains some <strong>HTML!!!</strong>`;
</script>

<p>{string}</p>
// => this string contains some <strong>HTML!!!</strong>

HTMLタグとして、認識させたいときは、{@html ~}を使用すればいける。

<script>
  let string = `this string contains some <strong>HTML!!!</strong>`;
</script>

<p>{@html string}</p>
// <strong>タグとして認識される。
オクトオクト

Assignments

Svelteは、イベントに対する応答のようなDOMを同期するための強力なリアクティブシステムである。
これはVueと似ている。

<script>
  let count = 0;

  function incrementCount() {
	// event handler code goes here
	count += 1;
  }
</script>

<button on:click={incrementCount}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

Declarations

Svelteは、上記のリアクティブと、リアクティブ宣言を使って変数同士を同期させることもできる。
$: doubled = count * 2;はJavaScriptのlabeled statementと同じように考える。
参照している値が変わるたびにコードを再実行している。

<script>
  let count = 0;
  $: doubled = count * 2;

  function handleClick() {
	count += 1;
  }
</script>

<p>{count} doubled is {doubled}</p>
// count: 2 doubled: 4
// count: 3 doubled: 6

これがどんな時に役立つのか?

  • 複数回参照する必要がある場合
  • 他のリアクティブ値に依存しているリアクティブ値を持っている場合

Statements

$:は、リアクティブ値を宣言するだけでなく、任意のコードもリアクティブにすることができる。

<script>
  let count = 0;

  function handleClick() {
	count += 1;
  }
  $: console.log('the count is ' + count);  // countが変更されるたびにコンソールに出力される。
</script>

<button on:click={handleClick}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

さらにブロック{ }を渡すことで、まとめて実行もできる。

<script>
  [...]  

  $: {
	console.log('the count is ' + count);
	console.log('I SAID THE COUNT IS ' + count);
  }
</script>
[...]

さらにif() { }ブロックも置くことができる。
$: if(count > 10) { ~~~ }
すごい。

オクトオクト

Updating arrays and objects

配列やオブジェクトを変更するメソッドはそれらの更新をトリガーしない。
下のサンプルで言うと、テンプレートのnumbersは初期値の1, 2, 3, 4のまま。

<script>
  let numbers = [1, 2, 3, 4];

  function addNumber() {
	numbers.push(numbers.length + 1);
	console.log(numbers);  // output: 1, 2, 3, 4, 5
  }
</script>

<p>{numbers}</p>

<button on:click={addNumber}>
  Add a number
</button>

これをできるようにするには以下のようにする。

// 方法1
numbers.push(numbers.length + 1);
numbers = numbers;

// 方法2
numbers = [...numbers, numbers.length + 1];

// 方法3
numbers[numbers.length] = numbers.length + 1;

ただし、間接的に参照するとうまく動かない。

const hoge = obj.hoge;
hoge.fizz = 'baz';

つまり、変更する変数は直接左辺にいないと動かない。

オクトオクト

Declaring props

あるコンポーネントから、子コンポーネントへデータを渡すためには、props(プロパティ)を宣言する必要がある。
そこで登場するのがexportというキーワードだ。

Child.svelte
<script>
  export let answer;  // propsを宣言
</script>

<p>The answer is {answer}</p>
Parent.svelte
<script>
  import Nested from './Nested.svelte';
</script>

<Nested answer={42}/>  // 子コンポーネントへデータを渡す

Default values

propsのデフォルト値を設定するには、通常の変数宣言と同じようにすれば良い。

Child.svelte
<script>
  export let answer = 'secret';  // propsのデフォルト値を設定
</script>

<p>The answer is {answer}</p>
Parent.svelte
<script>
  import Nested from './Nested.svelte';
</script>

<Nested />  // データを渡さない
// => The answer is secret

Spread props

オブジェクトの場合、{...variable}みたいな感じで、まとめて渡すことができる。
めっちゃ便利。

Child.svelte
<script>
  export let name;
  export let version;
  export let speed;
  export let website;
</script>

<p>
  The <code>{name}</code> package is {speed} fast.
  Download version {version} from <a href="https://www.npmjs.com/package/{name}">npm</a>
  and <a href={website}>learn more here</a>
</p>
Parent.svelte
<script>
  import Info from './Info.svelte';

  const pkg = {
	name: 'svelte',
	version: 3,
	speed: 'blazing',
	website: 'https://svelte.dev'
  };
</script>

<Info {...pkg} />
// 個別で指定するとこんなに面倒
// Info name={pkg.name} version={pkg.version} speed={pkg.speed} website={pkg.website}
オクトオクト

if blocks

Svelteでは、条件付けやループ処理も扱える。
条件付けの場合は、{#if ~~~} { /if }

<script>
  let user = { loggedIn: false };

  function toggle() {
	user.loggedIn = !user.loggedIn;
  }
</script>

{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{/if}

{#if !user.loggedIn}
  <button on:click={toggle}>
    Log in
  </button>
{/if}

elseブロックを使うとこうなる。
ifのには#をつけて、elseの前には:をつける。else if:をつける。
これは少しややこしい。

Start Continuation End
# : /
[...]
{#if user.loggedIn}
  <button on:click={toggle}>
    Log out
  </button>
{:else}
 <button on:click={toggle}>
    Log in
  </button>
{/if}
オクトオクト

Each blocks

繰り返し処理は、ifブロックと同じように組む。
#each vals as val, intのように第二引数に変数を置くと、indexを格納できる。

<script>
  let cats = [
	{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
	{ id: 'z_AbfPXTKms', name: 'Maru' },
	{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
  ];
</script>

<ul>
  {#each cats as cat}
    <li>
      <a target="_blank" rel="noreferrer" href="https://www.youtube.com/watch?v={cat.id}">		 
        {cat.name}
      </a>
    </li>
   {/each}
</ul>

オブジェクトのキーをそれぞれ指定して、繰り返すこともできる。
ここでいうと、#each cats as { id, name }

<ul>
  {#each cats as {id, name}}
    <li>
      <a target="_blank" rel="noreferrer" href="https://www.youtube.com/watch?v={id}">
        {name}
      </a>
    </li>
  {/each}
</ul>

eachブロックの値に変更を加えたとき、デフォルトでは子コンポーネントのデータブロックの最後の要素に対して新しく追加したり、その要素を削除したりして、変更された値を更新する。
それを防ぐためにはユニークキーをeachブロックに対して明示することで、どのDOM要素を変更するかSvelteに教えることができる。

Child.svelte
<script>
  const ages = {
      Bob: 10,
      Alice: 20,
      Mark: 30,
  }

  export let name;
  const age = ages[name];
</script>

<p>
  <span>{ name } is { age }</span>
</p>
Parent.svelte
<script>
  import Child from './Child.svelte';

  let persons = [
	{ id: 1, name: 'Bob' },
	{ id: 2, name: 'Alice' },
	{ id: 3, name: 'Mark' },
  ];

  function handleClick() {
	persons = persons.slice(1);
  }
</script>

<button on:click={handleClick}>
  Remove first thing
</button>

{#each persons as person (person.id) }
  <Child name={person.name}/>
{/each}

オブジェクトをそのまま渡すこともできるみたいだが、公式ではあまりお勧めされていない。

オクトオクト

DOM events

on:ディレクティブを使うことでイベントをリッスンできる。

<button on:click={handleClick}>Click!</button>

ワンラインで書くこともできる。クオートで括ったほうが、シンタックスハイライトで役に立つ。
他のフレームワークではパフォーマンスの面で避けた方が良いそうだが、Svelteの場合は例外らしい。
コンパイラーがよしなにやってくれるみたいだ。

<button on:click="{e => val = e.target}">Click!</button>
<p>{val}</p>

on:ディレクティブに対して修飾子をつけることができる。

<button on:click|once={handleClick}>
  Click me
</button>

その他にも、preventDefaultpassiveなどがあるみたいだ。
詳しい修飾子はここから確認できる。
この修飾子はチェーンすることができる。

<button on:click|once|capture={~~~}>
  Click me
</button>

event dispatcherを作成することで、コンポーネントはイベントを送ることができる。
その時に使うのがcreateEventDispatcherである。これはコンポーネントがインスタンス化された時に呼ばれている必要がある。だから、setTimeoutなどのコールバックの中では使えない。

Child.svelte
<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  function something() {
    dispatch('eventName', {
      text: 'hoge',
    }
  }
</script>
Parent.svelte
<script>
  import Child from './Child.svelte';

  function fuga(event) {
    console.log(event.detail.text)
  }
</script>

<Child on:eventName={fuga} />

子コンポーネントのdispatch()で付けたイベント名を親コンポーネントのon:ディレクティブの修飾子につけて、イベントを送っている。この修飾子を消すと、イベント自体は送られるが、反応はしない。

Event forwarding

DOMのイベントとは違って、コンポーネントのイベントはバブリングしない。
深くネストされたコンポーネントに対してイベントをリッスンさせたい場合は、中間のコンポーネントはそのイベントを転送しないといけない。

# イメージ
A component > intermediate component > nested component
// Aコンポーネントでnested componentのイベントを使いたい場合は、intermediateコンポーネントで転送しないといけない

それを解決する方法の一つとして、中間コンポーネントでもcreateEventDispatchを定義する。

function forward(event) {
  dispatch('eventName' , event.detail);
}
// <Component on:eventName={forward} />

ただ、これだとコードが冗長になる。
そこでSvelteでは、ショートハンドが用意されている。
シンプルにon:eventNameのみを記述する。なんて分かりやすい!

<script>
  import Component from './Component.svelte'
</script>

<Component on:eventName />

DOM event forwarding

DOMのイベントも送ることができる。
送る方法は上記の方法と同じ。これは子コンポーネントに対して送っている。

<button on:click>Click!!</button>
オクトオクト

Text inputs

Svelteでは、基本的なルールとして、データフローはトップダウンである。
つまり、親コンポーネントは子コンポーネントに対して、propsをセットしたり、HTML要素に属性をセットしたりできるが、その逆をすることはできない。

on:inputを定義して、val = event.target.valueで更新もできるが、冗長。
そこで使えるのがbind:ディレクティブである。

<script>
  let name = 'world';
</script>

// ここで双方向バイディングを実現している
<input bind:value={name}>

<h1>Hello {name}!</h1>

Numeric inputs

DOMの中では、すべての要素が文字列である。
input要素のtype="number"などは役に立たないのである。
しかし、bind:ディレクティブはそれを考慮して、扱うことができる。

Checkbox inputs

checkboxに対してもバインディングできる。
checked属性に対して、bind:ディレクティブをつけてあげるだけ。

じゃあ、複数管理したいときはどうするの?
そういう時は、bind:group={~~~}を使用する。

// const hoge = 1;
<input type="checkbox" bind:group={hoge} name="hoge" value="Bob and Alice" >

これを利用すると、繰り返し処理で簡単に記述できる。
eachで回す場合のbind:groupの値はデフォルトでチェックされるものになる。

<script>
  let defaultPlayer = 'kuwahara'
  let players = [
    'kuwahara',
    'sano',
    'maki',
  ];
</script>

{#each players as player}
  <label for="players">
    <input type="checkbox" bind:group={defaultPlayer} name="players" value={player}>
    {player}
  </label>
{/each}

Textarea inputs

Text inputsと同じように扱うことができる。
HTML属性と同じ変数名を当てる場合は、省略してbind:valueのみで動く。

<script>
  let value = 'Hello world';
</script>

<textarea name="text" bind:value></textarea>
<p>You write {value}</p>

select要素に対しても、使うことができる。
この場合、初期値は設定していない。Svelteではデフォルトで、リストの最初の要素を初期値にしている。

<select bind:value={selected} on:change="{( ) => {hogehoge}}">

複数選択できるようにするためには、select要素にmultipleをつけてあげる。

<select multiple bind:value={selected}>

contenteditable="true"の要素に対しては、textContentinnerHTMLをサポートしている。

Media elements

Dimentions

Component bindings

DOMのプロパティをバイディングできるようにComponentのプロパティもバイディングできる。
TODO:要復習

オクトオクト

onMount

すべてのコンポーネントはlifecycleを持っている。コンポーネントが作成された時に始まり、なくなった時に終了する。これは、ライフサイクルの中で、重要な瞬間に処理を実行したい場合に、実行できる機能があることを意味する。

そのうちの一つでよく使われるであろう機能がonMountである。これはコンポーネントがDOMに対して、レンダリングされた後に走る処理を提供してくれる。

ライフサイクル関数はコンポーネントが作成されている間に呼ばれるため、その関数はコンポーネントに紐づいていないといけない。(setTimeoutではない。)
onMountコールバックが関数を返す場合、コンポーネントが破棄されたときにその関数が呼ばれる。

それ以外は、他の時間で復習。
https://svelte.dev/tutorial/ondestroy

オクトオクト

Writable stores

すべてのアプリケーションの状態がアプリケーションのコンポーネント階層の中にあるわけではない。コンポーネント階層?は、コンポーネントの中のDOMツリーを指している?
異なるコンポーネントや通常のJavaScriptモジュールからアクセスする必要がある値を持っているときがある。
Svelteでは、storesを使って、アクセスできるようにする。storeは単なるオブジェクト。storesubscribeメソッドを持っていて、これは値が変更されるといつでも関係するものは知ることができる。

writableメソッドは、subscribeメソッドに加えてsetメソッドとupdateメソッドを持っている。
TODO:この違いは何?

// stores.js
import { writable } from 'svelte/store';

export const count = writable(0);

// increment.svelte
const increment = () => {
  count.update(n => n + 1);
}

// reset.svelte
const reset = () => {
  count.set(0);
}

上記のやり方だと、subscribeはするが、unsubscribeはしない。つまり、コンポーネントが作成されたり、破棄されたりを繰り返すと、メモリリークを引き起こしてしまう。
なので、unsubscribedを定義する必要がある。どうするかは、subscribeメソッドの返り値がunsubscribeメソッドであることを利用する。

const unsubscribed = count.subscribe(value => {
  countValue = value;
}

これだけだと、不十分でこのunsubscribedメソッドを呼び出す必要がある。

import { onDestroy } from 'svelte';

[...]

onDestroy(unsubscribed);

Auto-subscription

ただ、これを複数のストアに対して行なっていたら、冗長になってくる。
なので、Svelteでは、自動サブスクリプションが提供されている。どうするかというと、ストア変数の前に$をつけるだけで良いらしい。
さらに、テンプレート内だけでなくイベントハンドラやリアクティブ宣言などの<script>内でも使える。

<h1>The count is {$countValue}</h1>

Readable stores

すべてのストア変数が書き込みをしたいわけではない。つまり、読み取りしかしないストア変数もある。
そんな時は、readableメソッドが用意されている。readableメソッドの返り値は、end関数。
start関数はストアが通知を受け取った時に実行される。end関数はストアがunsubscribeされるときに実行される。

import { readable } from 'svelte/store'

// redable(<initial value>, <start function>)
export const time = readable(null, function() {
  hogehoge
  return function stop() {}
});

Derived stores

他のストア変数の値に基づいてストア変数を作成したいときもある。(派生するイメージ)
そんなときは、derivedメソッドを使う。

import { derived } from 'svelte/store';

export const val = derived(<initial value>, <start function>)

Custom stores

ドメイン固有のロジック?
多分、複数関数が散らばっていたものを一つまとめることができるっていうことか?

Child.svelte
<script>
  import { writable } from 'svelte/store';

  const createCount = () => {
    const { subscribe, set , update } = writable(0);

    return {
      subscribe,
      increment: () => update(n => n + 1),
      decrement: () => update(n => n + 1),
      reset: () => set(0),
    };
  }

  export const count = createCount();
</script>
Parent.svelte
<script>
  import { count } from './store.js'
</script>

<h1>The count is {$count}</h1>

// countは`$`をつけていない点に注意!!
<button on:click={count.increment}>Increment</button>
<button on:click={count.decrement}>Decrement</button>
<button on:click={count.reset}>Reset</button>

Store binding

writableなストアをバイディングすることもできる。
例えば、nameから派生したnicknameのストアがあったとする。
inputの値を変更すると、それに紐づくすべてのストア(ここではnickname)も変更される。

<input bind:value={$name}>
// name: 'Bob' - nickname: 'Bob'

直接ストア変数を割り当てることができる。

<button on:click="{() => $name += '!'}">  // name.set($name + '!')と同じ
  Add exclamation mark!
</button>
オクトオクト

Animation

/* TODO: learning later */

Actions

/* TODO: learning later */

オクトオクト

Advansed styling

The class directive(The style directive)

他のHTML属性と同じように、
JavaScriptを使ってクラス名を指定することができる。

<button
  class="{klass === 'hoge' ? 'fizz' : ''}"
  on:click="{() => klass = 'hoge'}"
>
  Click Me!
</button>

これをもっと簡単な方法で行える。
クラス属性に対してfizzクラスをくっつけて、値はBoolianで制御すれば良い。

<button
  class:fizz="{klass === 'hoge'}"
  on:click="{() => klass = 'hoge'}"
>
  Click Me!
</button>

クラス名とその変数名が同じであれば、値の部分を省略できる。

// Before
<button class:fizz={fizz}>
  Click Me!
</button>
// After
<button class:fizz>
  Click Me!
</button>
オクトオクト

Component composition

Slots

HTML要素が子要素を持てるように、コンポーネントも子要素を持つことができる。
slotを使うことで、親コンポーネントからコンテンツを子コンポーネントに渡して、指定の場所に入れることができる。

Parent.svelte
<script>
  import Hoge from './Hoge.svelte'
</script>

<Hoge>
  <div>
    <p>Slot is nice</p>
  </div>
</Hoge>
Child.svelte
<div class="child">
  <slot></slot>
</div>

Named slots

slotには名前をつけて、それぞれにコンテンツを渡せる。

Child.svelte
<div>
  <slot name="hoge">
    <p>Default display</p>
  </slot>
</div>
Parent.svelte
[...]
<Child>
  <p slot="hoge">FizzBuzz</p>
</Child>

Checking for slot content

slotが空の場合にレンダリングしたくなかったり、slotがある場合にクラスを当てたいなどのケースが出てくる。そういうときは、$$slots変数を使ってプロパティをチェックできる。
$$slotsはオブジェクトで、そのキーは親コンポーネントから渡されたスロットの名前である。

Slot props

子コンポーネントで持っているデータを親コンポーネントに返したいときは、let:ディレクティブを使う。
デフォルトのslotの場合は、コンポーネント名の要素に定義する。

Child.svelte
<script>
  let counter = 0;

  function increment() {
    counter += 1;
  }
</script>

<div on:click={increment}>
  <slot {counter}></slot>  // counter={counter}と同じ
</div>
Parent.svelte
<script>
  import Child from "./Child.svelte";
</script>

<Child let:counter={counter}>  // 子コンポーネントから親コンポーネントへデータを渡せるようにする。
  <div>  // ここからslotに移植されるコンテンツ
    {#if counter >= 5}  // 渡ってきたデータを使う
      <p>Counter is more than 5.</p>
    {:else}
      <p>Counter is less than 4.</p>
    {/if}
    <p>Current counter is {counter}</p>
  </div>
</Child>

名前付きスロットに対しても、let:ディレクティブを使える。
名前付きスロットの場合は、slot="..."の要素に定義する。

Child.svelte
[...]
<div on:click={increment}>
  <slot name="number" {counter}></slot>
</div>
Parent.svelte
[...]
<Child let:counter={counter}>
  <div slot="number">
    {#if counter >= 5}
      <p>Counter is more than 5.</p>
    {:else}
      <p>Counter is less than 4.</p>
    {/if}
    <p>Current counter is {counter}</p>
  </div>
</Child>
オクトオクト

Special elements

Svelteは、いろんな組み込み要素を提供している。

<svelte:self>

通常、モジュールは自分自身をインポートできないため、コンポーネントの中にコンポーネントを呼び出すことはできない。
<svelte:self>要素を使うと、コンポーネントの中に、そのコンポーネント自体を入れることができる。

<script>
  export let count = 0;
</script>

{#if count > 0}
  <p>counting down...{count}</p>
  <svelte:self count="count - 1" />
{:else}
  <p>lift-off!</p>
{/if}

<svelte:component>

コンポーネントを動的に変更・破棄して、再生成できる。
これを使うと、ifブロックの代わりに短く記述することができる。これはすごい。。。

Parent.svelte
<script>
  import Child from "./Child.svelte";
  import GrandChild from "./GrandChild.svelte";

  const family = [
    {
      type: "GrandChild",
      component: GrandChild
    },
    {
      type: "Child",
      component: Child
    }
  ];
  let selected = family[0];
</script>

<select bind:value={selected}>
  {#each family as f}
    <option value={f}>
      {f.type}
    </option>
  {/each}
</select>

// Before
{#if selected.type === 'Child'}
  <Child />
{:else}
  <GrandChild />
{/if}

// After
<svelte:component this={selected.component} />
Child.svelte
<p>Child Component</p>
GrandChild.svelte
<p>GrandChild Component</p>

<svelte:element>

<svelte:component>要素のHTML要素バージョン。
ただし、このスペシャルエレメントでは、bind:thisのみサポートされている。

<svelte:window>

windowオブジェクトに対して、イベントリスナーを追加できる。

<svelte:window on:keydown={handleKeydown} />

また、バインディングを当てることもできる。

<svelte:window bind:scrollY={y} />

似ているスペシャル要素として<svelte:body>要素がある。
windowオブジェクトに対して、イベントハンドラを追加できない時に、役立つ。

<svelte:body on:mouseenter={handleEvent} />

<svelte:head>

<svelte:head>要素は<head>タグの中に要素を挿入できる。
Railsでいうところの、content_for(:head)みたいなノリか。

<svelte:head>
  <link rel="stylesheet" href="./hoge.css">
</svelte:head>

<svelte:options>

<svelte:options>要素はコンポーネントに対して、コンパイラオプションを指定できる。
例えば、<svelte:options tag="my-custom-component"/>とすると、HTML内で<my-custom-component></my-custom-component>でコンポーネントを作成できる。

<svelte:fragment>

名前付きスロットの中にDOM要素のコンテナで包むことなく、コンテンツを置くことができる。
つまり、<svelte:fragment>の中に入れたコンテンツは、要素をつけることなくスロットに渡すことができる。
https://reffect.co.jp/svelte/svelte-tutorial

このスクラップは2022/11/16にクローズされました