1. ホーム
  2. vue

マウントされたフックでのエラー: "TypeError: TypeError: Cannot read property 'XXXXX' of undefined" マウントされたフックにエラーが発生しました。

2022-02-09 23:57:02

今日、iview を使ってページを描画する際、コントロールを追加すると、非常に単純だが長年のバグに出くわした。

<user-select
 style="width:200px"
 @on-change="handleSelect"
 ref="user"
 v-model="form.userCode">
</user-select>

次に、$refs を使用して、コンポーネントのプロパティを呼び出します。

this.$refs.user.initData();

それから魔法が起こる、それがどのようにあるべきかに関係なく、それはマウントされたフックのエラーを報告し続ける: "TypeError.TypeError.TypeError.TypeError: このメソッドは以前から使われていたもので、なぜここで動作しないかはいろいろと推測できます。

やっとすごいものを見つけました。コンポーネントの位置を変えたらうまくいったのに、戻したら動かなくなったので、"これはiviewのバグで、2つのコントロールを真横に置くことができないのか、と思ったのです。そして、「まさか、vueのエコシステム全体はとても成熟しているので、こんな低レベルのエラーはないはずだ!」と思い、振り返りました。

そしてふと、このコントロールは余計なv-if判定がある位置にあることに気づいたのですが、レンダリング順のせいでしょうか?

<Row :gutter="32" v-if="type=='2'">
 <Col span="12>
    <FormItem label="user-select" prop="userCode" >
        <user-select
          style="width:200px"
          @on-change="handleSelect"
          ref="user"
          v-model="form.userCode">
        </user-select>
    <FormItem>
 </Col>
<Row>

突然、新しい世界を発見したような気分で、v-if属性を削除しても問題なく動作します。

そこで思ったのが、htmlコードのレンダリングを優先するvueの読み込みとレンダリングの仕組みで、jsコードはまだ実行されていないので、ダイナミックプロパティはまだ値を持っておらず、v-ifでラップされているコードブロックはラップされておらず、この時点でjs初期化コードを実行すると、当然callrefコンポーネントは未定義になってしまうということでした。

思い切って考えてみた結果、本当にそうなら、v-ifとv-showの違いによれば、v-showはhtmlを読み込むときに、表示しないだけでフルコードを読み込むので、その場合、jsコードを実行するときにコンポーネントが存在するはずだと推測されたのです。そこで、v-ifをv-showに変更してみると、確かにコードは正常に実行されます。

v-ifとv-showの違いをおさらいしておきましょう。

  • の意味は、v-if は dom ノードの有無を制御して要素の表示を制御し、v-show は DOM 要素の表示スタイルを設定し、block は表示、none は非表示にすることです。
  • コンパイルプロセス:v-ifトグルは、トグル中に内部のイベントリスナーやサブコンポーネントを適切に破棄して再構築する、ローカルなコンパイル/アンコンパイルプロセスを持ちます。v-showは、単純なcssベースのトグルです。
  • コンパイル条件:v-ifは不活性で初期条件が偽の場合は何もしない、ローカルコンパイルは条件が最初に真になったときのみ開始する(コンパイルはキャッシュされ、切り替え時にローカルにアンロードされる)、v-showは任意の条件でコンパイルし(条件が最初に真であっても)キャッシュし、DOM要素は保持される。
  • パフォーマンス消費:v-ifはトグルの消費量が多く、v-showは初期レンダリングの消費量が多くなります。

これらの違いから、頻繁にトグルする必要がある場合はv-showを、実行時に条件がほとんど変化しない場合はv-ifを使用するのがよいでしょう。