クマのブログ

つまづいたところ、学びを書いていきます

ライフサイクルフックの違いによるレンダリングのタイミング~img src編②~

はじめに

前回~imgのsrc編①~の続き

今回はv-ifの値が子コンポーネントで定義したdataプロパティだった時はどうなるのか、といいう確認がしたい

いまだに画面描画時に処理をしたい時にbeforeCreate, created, beforeMount, mountedの内どれを使えばいいのかの判断も曖昧なので、そんな自分の一助になればと思った

加えてmethodsでsrcの値をreturnするパターンはどうなのか、ということも確認

話すこと

前回と同じで、以下コードを例にどのライフサイクルフックをつかったら、どんな挙動を示すのかを順にみていく

やりたいこと

コンポーネントで定義するimgタグのsrc属性の内容に応じて表示する画像を変更する

OKな時

f:id:kuma_kuma0121:20220223214525p:plain

NGな時

f:id:kuma_kuma0121:20220223214455p:plain

ソースコード

親コンポ―ネント

<template>
  <div>
    <ImageChildComponent
      :url="url"
      @replace="replaceUrl"
    ></ImageChildComponent>
  </div>
</template>

<script>
import ImageChildComponent from './ImageChildComponent.vue'

export default {
  components: {
    ImageChildComponent
  },
  data() {
    return {
      url: '',
    }
  },
  methods: {
    replaceUrl(newUrl) {
      console.log(newUrl);
      this.url = newUrl;
    },
  }
}
</script>

コンポーネントでやっていることは前回と変わらない

コンポーネント

<template>
  <div>
    <!-- OK時の画像 -->
    <div
      v-if="url"
    >
      <h2>OK!!!</h2>
      <img
        :src="childUrl"
        width=200
      >
    </div>
    <!-- NG時の画像 -->
    <div
      v-else
    >
      <h2>NG...</h2>
      <img
        src="../assets/no-image.png"
        width=200
      >
    </div>
  </div>
</template>

<script>
export default {
  props: {
    url: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      childUrl: '',
    }
  },
beforeCreate() {
    console.log('beforeCreate: ', this.childUrl);
    // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    // this.childUrl = newUrl;
  },
  created() {
    console.log('created: ', this.childUrl);
    // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    // this.childUrl = newUrl;
  },
  beforeMount() {
    console.log('beforeMount: ', this.childUrl);
    // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    // this.childUrl = newUrl;
    // console.log('beforeMount: ', this.childUrl);
  },
  mounted() {
    console.log('mounted: ', this.childUrl);
    // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    // this.childUrl = newUrl;
    // console.log('mounted: ', this.childUrl);
  },
  methods: {
    getUrl() {
      console.log('methods(getUrl): ', this.childUrl);
      // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
      // this.childUrl = newUrl;
      // return this.childUrl;
    }
  }
}
</script>

<template>でやっていることは基本的には一緒ですが、変化点は以下

  • 表示するimgのsrc属性の値が子コンポーネント特有のdataプロパティchildUrlになっている

<script>でやっていることも基本一緒ですが、変化点は以下

  • コンポーネント特有のプロパティchildUrlを定義
  • 各ライフサイクルフックでchildUrlnewUrlを代入
  • consoleで出力するのはchildUrl

検証方法

手順として前回同様、コメントアウトしてある箇所を随時コメントインして実行していく

デフォルト

ブラウザ

f:id:kuma_kuma0121:20220226220839p:plain

コンソール

f:id:kuma_kuma0121:20220226220956p:plain

前回同様、エラーが2つ。理由も同じなので割愛

beforeCreated

...
beforeCreate() {
  console.log('beforeCreate: ', this.url);
  const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
  this.childUrl = newUrl;
},
...

ブラウザ

デフォルトと同じなので割愛

コンソール

コチラもデフォルトと同じなので割愛

ここまでも前回同様

※beforeCreatedに console.log('beforeCreate: ', this.url); を書いていると常にエラーがでてくるので、これ以降はこの処理をコメントアウトする

created

created() {
    console.log('created: ', this.url);
  const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
  this.childUrl = newUrl;
  console.log('created: ', this.url);
},

ブラウザ

beforeCreateと同じ

コンソール

f:id:kuma_kuma0121:20220226221048p:plain

今回は何も表示されない

恐らくv-ifでurlがnullなので、v-else側に入っているからだと思う

そして、再インスタンス化(前回同様console.logをcreatedに追加)しても結果は変わらない

結果は割愛

created() {
    console.log('created: ', this.url);
    const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    this.$emit("replace", newUrl);
        // インスタンス化するために追加
    console.log('created: ', this.url);
},

ギモン

多分上記推測であってるはず

今回はv-elseに入って、前回はv-if側に入った点からみると

一度templateの描画、インスタンス化→urlの値が変化(前回)→再描画、インスタンス

という流れだろうか

ただこれで行くと、前回各ライフサイクルフックでthis.urlの内容が表示されなかった点はつじつまが合わない

beforeMount

beforeMount() {
    console.log('beforeMount: ', this.url);
    const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    this.childUrl = newUrl;
    console.log('beforeMount: ', this.url);
},

ブラウザ

createdと同じ

コンソール

createdと同じ

ギモン

createdと同じ

mounted

mounted() {
    console.log('mounted: ', this.url);
    const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
    this.$emit("replace", newUrl);
},

ブラウザ

created, beforeMouteと同じ

コンソール

created, beforeMouteと同じ

ギモン

created, beforeMouteと同じ

methods

ここで想定するのは以下のようなソースコード

<template>
        <div
      v-if="url"
    >
      <h2>OK!!!</h2>
      <img
        :src="getUrl()" // 変更
        width=200
      >
    </div>
        ...
</template>

<script>
export default {
    ...,
    methods: {
    getUrl() {
      console.log('methods(getUrl): ', this.url);
      const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
      this.childUrl = newUrl;
      return this.childUrl;
  }
    ...
}
</script>

ブラウザ

created, beforeMoute, mountedと同じ

コンソール

created, beforeMoute, mountedと同じ

getUrlをv-ifで呼び出した場合

     
        <div
      v-if="getUrl()"  <!-- 変更 -->
    >
      <h2>OK!!!</h2>
      <img
        :src="getUrl()"
        width=200
      >
    </div>

ブラウザ

f:id:kuma_kuma0121:20220226221223p:plain

コンソール

f:id:kuma_kuma0121:20220226221250p:plain

ここで初めてブラウザに表示

コンソールの流れからして、

  • beforeMountまではDOMは生成されないためgetUrlが呼ばれないためchildUrlはnull
  • beforeMount以降はDOMが生成され、getUrlが呼ばれる
    • console3行目:1回目のDOM生成時のv-ifでのgetUrl。初回のconsole.logなので、childUrlには何も入っていない
    • console4行目:1回目のDOM生成時のimg内のgetUrl。既にchildUrlが変更されているので、newUrlが出力
    • console5行目:1回目のDOM生成が終わり、dataが変更されているので、mountedが呼び出される。childUrlはgetUrlで設定したnewUrlが入っている
    • console6, 7行目:それぞれ2回目のv-if, img内のgertUrl呼び出し時のconsole.log。1回目のDOM生成時に設定したchildUrlがそのまま引き継がれている

参考記事

https://jp.vuejs.org/v2/guide/instance.html#ライフサイクルダイアグラム

所見

  • 今回はv-ifの条件がnullでスタートしたので、ほぼno-imageの表示であまり変化が負えなかった
  • とは言え、最後のgetUrの例はライフサイクルを知るのにかなりわかりやすかった
  • いくつかVueのライフサイクルとは想定外の挙動をしている、、、
  • 次はdataをv-ifにした上で、ライフサイクルを追ってみる