Open4

ejs→vue移行チートシート

からくれ178からくれ178

各々前提バージョン

vue: 3.4以上
(余裕ができたらTypeScriptに書き換えます)

コンポーネントに引数を持たせたい場合

共通のボタンを作成する場合を考えてみる。

ejs

<%
/** 呼び出す時に必要になる引数
 * @param {string} href - linkのurl
 * @param {string} addClass - 追加するクラス名
 * @param {string} isReverse - 色を反転させるか否か(空or指定なしだと自動でbutton--mainColor)
 */
const addClassName = typeof addClass !== 'undefined' && addClass !== "" ? ` ${addClass}` : '';
const reverseColor = typeof isReverse !== 'undefined' && isReverse !== "" ? "button--reverseColor" : "button--mainColor";

/**
:example
< include("../../../../common/components/button/button.ejs",
    { href: "/", addClass: "works__button", isReverse: "false" }); 
>
*/
-%>

<a class="button <%- addClassName -%> <%- reverseColor -%>" href="<%- href %>">
  <span>MORE</span>
</a>

ejsだと型ガードがないため知らぬ間にundifinedが渡されている可能性がある。

また、引数の情報をコメント等で記載がない場合、どれが引数なのかぱっと見ただけではわからない事態になる。例えば以下のように書くと、addClass、isReverse、hrefはejs上で定義されていないため実質引数だが、includeで引数として渡し忘れるとコンパイルエラーが返ってくる。

<%
const addClassName = typeof addClass !== 'undefined' && addClass !== "" ? ` ${addClass}` : '';
const reverseColor = typeof isReverse !== 'undefined' && isReverse !== "" ? "button--reverseColor" : "button--mainColor";

/**
:example 本来はこのように呼び出すとエラーが吐かない
< include("../../../../common/components/button/button.ejs",
    { href: "/", addClass: "works__button", isReverse: "false" }); 
>

引数の情報がぱっと見だとわからないので以下のようにしがち(コンパイルエラー)
< include("../../../../common/components/button/button.ejs"); >
*/
-%>

<a class="button <%- addClassName -%> <%- reverseColor -%>" href="<%- href %>">
  <span class="button">MORE</span>
</a>

vue

<script setup>
const props = defineProps({
  href: {
    type: String,
    default: "",
  },
  isReverse: {
    type: Boolean,
    default: false,
  },
});

const buttonColor =
  props.isReverse === false ? "button--mainColor" : "button--reverseColor";
</script>

<template>
  <a
    class="button"
    :class="buttonColor"
    :href="props.href"
  >
    <span>MORE</span>
  </a>
</template>

vueだと引数として渡す際に型とともに定義できるので、違う型が引数として渡されていたり、引数が渡されていなかった場合ブラウザ上でwarnを吐いてくれる。
また、ejsと違って、defineProps関数を使って引数を明示して定義する必要があるのでぱっと見でコンポーネントにどのような引数を渡す必要があるかがコメントを書かずともわかりやすい。

からくれ178からくれ178

静的な情報を渡したい

ejs

静的データは以下を想定する。
data.ejs

<% 
articlesData = {
  commonUrl: "assets/image/common/icon/",
  link: [
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/12/30",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/12/04",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/11/19",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/08/13",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/07/22",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/07/04",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
  ],
};
%>

ejsファイルを利用して静的データをグローバル変数としている。

articles.ejs

<%
include('./data.ejs');
const items = articlesData.items;
const commonUrl = articlesData.commonUrl;
%>

<section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <% for(let i = 0; i<items.length; i++){ %>
        <li class="articles__item">
          <a href="<%= items[i].href %>" class="articlesItem__link">
            <img src="<%= items[i].src %>" alt="<%= items[i].alt %>" />
            <span class="articlesItem__group">
              <p class="articlesItem__title"><%= items[i].title %></p>
              <span class="articlesItem__date"><%= items[i].date %></span>
            </span>
          </a>
        </li>
        <% } %>
      </ul>
    </div>
</section>

グローバル変数としたdata.ejsのarticlesDataをオブジェクトとしてejsにincludeして利用している。

vue

静的データは以下を想定する。
data.json

{
  "commonUrl": "assets/image/common/icon/",
  "link": [
    {
      "href": "/",
      "title": "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "date": "2023/12/30",
      "src": "icon.svg",
      "alt": "ニュース記事アイコン"
    },
    {
      "href": "/",
      "title": "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "date": "2023/12/04",
      "src": "icon.svg",
      "alt": "ニュース記事アイコン"
    },
    {
      "href": "/",
      "title": "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "date": "2023/11/19",
      "src": "icon.svg",
      "alt": "ニュース記事アイコン"
    },
    {
      "href": "/",
      "title": "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "date": "2023/08/13",
      "src": "icon.svg",
      "alt": "ニュース記事アイコン"
    },
    {
      "href": "/",
      "title": "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "date": "2023/07/22",
      "src": "icon.svg",
      "alt": "ニュース記事アイコン"
    },
    {
      "href": "/",
      "title": "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "date": "2023/07/04",
      "src": "icon.svg",
      "alt": "ニュース記事アイコン"
    }
  ]
}
<script setup>
import articlesData from "./data.json";

const items = articlesData.link;
const commonUrl = articlesData.commonUrl;
</script>

<template>
  <section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <li v-for="item in items" :key="item.href" class="articles__item">
          <a :href="item.href" class="articlesItem__link">
            <img :src="item.src" :alt="item.alt" />
            <span class="articlesItem__group">
              <p class="articlesItem__title">{{ item.title }}</p>
              <span class="articlesItem__date">{{ item.date }}</span>
            </span>
          </a>
        </li>
      </ul>
    </div>
  </section>
</template>

<style lang="scss" scoped></style>

data.json等jsonに静的データを持たせて対応させる。
jsにimportするときはimport articlesData from "./data.json";articlesDataのように、任意の変数名を設定してimportする。

以下のようにdata.jsなどのjsでも静的データを持たせらせる。
data.js

export const articlesData = {
  commonUrl: "assets/image/common/icon/",
  link: [
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/12/30",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/12/04",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/11/19",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/08/13",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/07/22",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
    {
      href: "/",
      title: "[oooo]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      date: "2023/07/04",
      src: "icon.svg",
      alt: "ニュース記事アイコン",
    },
  ],
};

Articles.vue

<script setup>
import { articlesData } from "./data.js";

const items = articlesData.link;
const commonUrl = articlesData.commonUrl;
</script>

<template>
  <section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <li v-for="item in items" :key="item.href" class="articles__item">
          <a :href="item.href" class="articlesItem__link">
            <img :src="item.src" :alt="item.alt" />
            <span class="articlesItem__group">
              <p class="articlesItem__title">{{ item.title }}</p>
              <span class="articlesItem__date">{{ item.date }}</span>
            </span>
          </a>
        </li>
      </ul>
    </div>
  </section>
</template>

<style lang="scss" scoped></style>
からくれ178からくれ178

繰り返し処理をしたい

ejs

<%
include('./data.ejs');
const items = articlesData.items;
const commonUrl = articlesData.commonUrl;
%>

<section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <% for(let i = 0; i<items.length; i++){ %>
        <li class="articles__item">
          <a href="<%= items[i].href %>" class="articlesItem__link">
            <img src="<%= items[i].src %>" alt="<%= items[i].alt %>" />
            <span class="articlesItem__group">
              <p class="articlesItem__title"><%= items[i].title %></p>
              <span class="articlesItem__date"><%= items[i].date %></span>
            </span>
          </a>
        </li>
        <% } %>
      </ul>
    </div>
</section>

ejsのタグ<% %>を使用してjsと同じようにfor文で回す。回したいタグの箇所までejsのタグ <% } %>を使用する。

vue

<script setup>
import Article from "./Article.vue";
import { articlesData } from "./data.js";

const items = articlesData.link;
const commonUrl = articlesData.commonUrl;
</script>

<template>
  <section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <li v-for="item in items" :key="item.href" class="articles__item">
          <a :href="item.href" class="articlesItem__link">
            <img :src="item.src" :alt="item.alt" />
            <span class="articlesItem__group">
              <p class="articlesItem__title">{{ item.title }}</p>
              <span class="articlesItem__date">{{ item.date }}</span>
            </span>
          </a>
        </li>
      </ul>
    </div>
  </section>
</template>

繰り返し処理をしたいタグの中でv-forを使用する。ejsと違って:keyに固有名を渡さないとeslint上でlint errorを吐き続ける&vueでコンポーネントの状態管理をする際に使用するため渡した方が安全。

参考文献

https://ja.vuejs.org/guide/essentials/list
https://ja.vuejs.org/guide/essentials/list#maintaining-state-with-key

からくれ178からくれ178

DOMの条件分けをしたい場合

ejs

<%
include('./data.ejs');
const items = articlesData.items;
const commonUrl = articlesData.commonUrl;
%>

<section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <% for(let i = 0; i<items.length; i++){ %>
          <% if(i === 0) %>
            <li class="articles__item">
              <a href="<%= items[i].href %>" class="articlesItem__link">
                <img src="<%= items[i].src %>" alt="<%= items[i].alt %>" />
                <span class="articlesItem__group">
                  <p class="articlesItem__title"><%= items[i].title %></p>
                  <span class="articlesItem__date"><%= items[i].date %></span>
                </span>
              </a>
            </li>
          <% } %>
        <% } %>
      </ul>
    </div>
</section>

ejsタグを使用してjsと同じようにif文を使って対応可能。

vue

v-ifを使うことでだし分け可能だが、以下のようにfor文で回したいタグとif文で条件分けをしたいタグが重なっている場合、使えるのだがfor文とif文との間で優先順位が生まれてしまいうまく動かない場合がある。(vue公式的に非推奨な書き方)

<script setup>
import articlesData from "./data.json";

const items = articlesData.link;
const commonUrl = articlesData.commonUrl;
</script>

<template>
  <section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <!-- 以下のように同じタグの中でv-ifとv-forを同時に使うのは非推奨 -->
        <li
          v-for="(item, index) in items"
          v-if="index === 0"
          :key="item.href"
          class="articles__item"
        >
          <a :href="item.href" class="articlesItem__link">
            <img :src="item.src" :alt="item.alt" />
            <span class="articlesItem__group">
              <p class="articlesItem__title">{{ item.title }}</p>
              <span class="articlesItem__date">{{ item.date }}</span>
            </span>
          </a>
        </li>
      </ul>
    </div>
  </section>
</template>

<style lang="scss" scoped></style>

こういった場合はdiv等のDOMを追加で用意してv-ifとv-forの書く場所を分けるか、以下のようにしてtemplateタグを使用すると回避できる。

<script setup>
import articlesData from "./data.json";

const items = articlesData.link;
const commonUrl = articlesData.commonUrl;
</script>

<template>
  <section class="articles">
    <div class="articles__inner">
      <h2 class="articles__heading">Articles</h2>
      <ul class="articles__group">
        <template v-for="(item, index) in items" :key="item.href">
          <li v-if="index === 0" class="articles__item">
            <a :href="item.href" class="articlesItem__link">
              <img :src="item.src" :alt="item.alt" />
              <span class="articlesItem__group">
                <p class="articlesItem__title">{{ item.title }}</p>
                <span class="articlesItem__date">{{ item.date }}</span>
              </span>
            </a>
          </li>
        </template>
      </ul>
    </div>
  </section>
</template>

<style lang="scss" scoped></style>

参考文献

https://ja.vuejs.org/guide/essentials/conditional
https://ja.vuejs.org/api/built-in-directives#v-if