iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
Modern Web

[ 重構倒數30天,你的網站不Vue白不Vue ] 系列 第 19

[重構倒數第12天] - Vue3 directive 與 Skeleton 實戰組合應用

前言

該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。

在前面的章節,我們講了許多 vue3 directive 的使用方式還有實際應用,接下來我們要把我們前面所講的 Skeleton 還有我們的 directive 給結合在一起,做一個組合的實戰應用。

You should use Skeleton : https://ithelp.ithome.com.tw/articles/10260925

這就是我們要處理,透過 directive + skeleton 完成以下的實際案例。

mike vue

首先我先把資料給拿到手,我透過 axios來拿資料

<script>
import { ref, onMounted } from "vue";
import axios from "axios";
export default {
  setup() {
    const postCard = ref([]);
    const isLoad = ref(true);

    onMounted(() => {
      axios
        .get("https://60bd9841ace4d50017aab3ec.mockapi.io/api/post_card")
        .then((res) => {
          postCard.value = res.data;
          isLoad.value = false;
        });
    });

    return {
      postCard,
      isLoad,
    };
  },
};
</script>

這邊會看到我宣告了兩筆資料,postCard是我拿來存放 API 回來的資料,isLoad是讓我判斷 API 的資料回來沒有,接下來我們來看一下我們的 html 的部分

<template>
  <div class="card" v-for="card in postCard" :key="card.id">
    <header>
      <img v-src="card.avatar" class="avatar load" />
      <div>
        <h1 :class="{ load: isLoad }">{{ isLoad ? "" : card.name }}</h1>
        <p :class="{ load: isLoad }" v-timeformat="card.post_date"></p>
      </div>
    </header>
    <p :class="['content', { load: isLoad }]">
      {{ isLoad ? "" : card.content }}
    </p>
    <img class="post_photo load" v-src="card.photo" alt="" />
  </div>
</template>

首先你會看到圖片的地方我使用了 v-src的這個模板語法,這就是我們在上一個章節所提到的用 directive包起來的方法。

app.directive("src", (el, binding) => {
  if (binding.value) {
    const img = new Image();
    img.src = binding.value;
    img.onload = () => {
      el.src = binding.value;
    };
  }
});

然後所有跟文字有關的地方都加上了一個 load 的class,這個 load 的class 就是為了讓我們可以在資料回來以前,讓我們的文字區塊出現灰色色塊所準備的,然後再裡面的內文我用了 isLoad來判斷要不要讓文字顯示出來。

h1.load, p.load {
  display: block;
  width: 300px;
  height: 14px;
  background-color: #ededed;
  color: rgba(#fff, 0);
}

@keyframes loading {
  to {
    background-position-x: -20%;
  }
}

.load {
    background: linear-gradient(
        100deg,
        rgba(256, 256, 256, 0) 30%,
        rgba(256, 256, 256, 0.5) 50%,
        rgba(256, 256, 256, 0) 30%
    )
        #ededed;
    background-size: 200% 100%;
    background-position-x: 180%;
    animation: 2s loading ease-in-out infinite;
}

透過這樣的方式我就可以很簡單的處理圖片的 load以及 Skeleton所需要顯示的那些區塊

import dayjs from "dayjs";

// 其他省略...

app.directive("timeformat", {
  mounted(el, binding) {
    const time = dayjs(binding.value).format("YYYY年MM月DD日");
    el.innerText = time;
  },
});

最後時間的部分處理就用了 v-timeformat這個方法就大功告成了。

Vue mike

以下就是完整的程式碼的部分

// index.js

import { createApp } from "vue";
import App from "./App.vue";
import dayjs from "dayjs";

const app = createApp(App);

app.directive("src", (el, binding) => {
  if (binding.value) {
    const img = new Image();
    img.src = binding.value;
    img.onload = () => {
      el.src = binding.value;
    };
  }
});

app.directive("timeformat", {
  mounted(el, binding) {
    const time = dayjs(binding.value).format("YYYY年MM月DD日");
    el.innerText = time;
  },
});

app.mount("#app");

// App.vue

<script>
import { ref, onMounted } from "vue";
import axios from "axios";
export default {
  setup() {
    const postCard = ref([]);
    const isLoad = ref(true);

    onMounted(() => {
      axios
        .get("https://60bd9841ace4d50017aab3ec.mockapi.io/api/post_card")
        .then((res) => {
          postCard.value = res.data;
          isLoad.value = false;
        });
    });

    return {
      postCard,
      isLoad,
    };
  },
};
</script>

<template>
  <div class="card" v-for="card in postCard" :key="card.id">
    <header>
      <img v-src="card.avatar" class="avatar load" />
      <div>
        <h1 :class="{ load: isLoad }">{{ isLoad ? "" : card.name }}</h1>
        <p :class="{ load: isLoad }" v-timeformat="card.post_date"></p>
      </div>
    </header>
    <p :class="['content', { load: isLoad }]">
      {{ isLoad ? "" : card.content }}
    </p>
    <img class="post_photo load" v-src="card.photo" alt="" />
  </div>
</template>

<style lang="scss">
.card {
  width: 400px;
  height: auto;
  background-color: #fff;
  box-shadow: 0 0 5px rgba(#000, 0.3);
  margin: 0 auto 30px auto;
  header {
    display: flex;
    padding: 10px 10px 0px 10px;
    margin: 0 0 5px 0;
    color: #666;
    h1 {
      font-size: 15px;
    }
    p {
      font-size: 13px;
    }
  }
  p.content {
    font-size: 14px;
    padding: 0px 10px 10px 10px;
    color: #666;
    &.load {
      display: block;
      width: 200px;
      height: 14px;
      background-color: #ededed;
      color: rgba(#fff, 0);
      margin: 10px 10px 10px 10px;
    }
  }
}
img.avatar {
  width: 40px;
  height: 40px;
  border-radius: 50px;
  margin-right: 10px;
  background-color: #ededed;
}
img.post_photo {
  width: 100%;
  height: 300px;
  background-color: #ededed;
}
h1.load {
  display: block;
  width: 300px;
  height: 14px;
  background-color: #ededed;
  margin-bottom: 10px;
  color: rgba(#fff, 0);
}
p.load {
  display: block;
  width: 100px;
  height: 14px;
  background-color: #ededed;
  color: rgba(#fff, 0);
}
@keyframes loading {
  to {
    background-position-x: -20%;
  }
}

.load {
  background: linear-gradient(
      100deg,
      rgba(256, 256, 256, 0) 30%,
      rgba(256, 256, 256, 0.5) 50%,
      rgba(256, 256, 256, 0) 30%
    )
    #ededed;
  background-size: 200% 100%;
  background-position-x: 180%;
  animation: 2s loading ease-in-out infinite;
}
</style>

先告一個段落

我們今天特別把之前所講過的東西給組合在一起 (Skeletondirective),就是讓大家知道其實我們在實際的開發上面,技術處理都是複合式的應用,不會只使用單一的概念來進行開發,所以特別把這兩個組合應用分享給大家知道,也是希望大家在實際開發的時候可以更加的融會貫通,那今天就先到這邊告一個段落了,我們明天見囉!

QRcode

那如果對於Vue3不夠熟的話呢?

Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。

我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/packages/AYR5m7VR3

那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/packages/Q9R4OYoyD

訂閱Mike的頻道享受精彩的教學與分享

Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng


上一篇
[重構倒數第13天] - Vue3定義自己的模板語法
下一篇
[重構倒數第11天] - 如何在 Vue 中寫出高效能的網頁渲染方式 ?
系列文
[ 重構倒數30天,你的網站不Vue白不Vue ] 31

尚未有邦友留言

立即登入留言