0時に寝て6時に起きた。

vuejs のライフサイクル

先日 vuejs で 画面作り に挑戦して出来たと喜んでいたが、検索して一覧画面のデータを更新した際に、フォームも再レンダリングされないといけないところがそうなっておらず、データは置き換わっているが画面に表示される値は変わっていないという致命的なバグがあることに気付いた。普通に開発していたら気付きそうなものだが、ローカルの dev server で動かしているとコードを更新すると再レンダリングが実行されるので検索後に画面の一覧が更新されないということを見逃したんだと思う。Lifecycle Diagram もみながら適切なフックポイントの振る舞いを確認したりしていた。setup 後、初期化されてその後に mounted が動いて、その後パラメーターが更新されたときに watch して再更新をかけるといった次のコードでも意図した振る舞いになることは確認した。

  setup(props, context) {
    const data: { [key: string]: any } = {};
    return { _data: ref(data), loading: false };
  },
  mounted() {
    this._data = JSON.parse(this.item.data);
  },
  watch: {
    item(value: any) {
      this._data = JSON.parse(value.data);
    },
  },

レビューしてもらったら、それよりもパラメーターをリアクティブにした方がよいのではないかと教えてもらって次のようにした。本当は setter は不要なんだけど、なぜか初期化のタイミングで setter が呼ばれるので設けた。私の作ったコンポーネントの設計が悪いせいかもしれない。

  setup(props, context) {
    const _item = toRef(props, 'item');
    return { _item };
  },
  computed: {
   _data: {
     get(): { [key: string]: any } {
       return JSON.parse(this._item.data);
     },
     set(value: any) {
       this.$emit('update:_data', value);
     },
    },
  },

これは vue2 の Options API と呼ばれる記法で、vue3 だと Composition API を使って次のような書き方ができるというのも教えてもらった。getter だけなら Composition API でもよさそうだけど、setter もあるとこのコードはまったく簡潔じゃないなと思って Options API を使うことにした。

  setup(props, context) {
    const _item = toRef(props, 'item');
    const _data = computed({
      get: () => JSON.parse(_item.value.data),
      set: (value: any) => this.$emit('update:_data', value),
    });
    return { _item, _data };
  },