該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。
在前面的章節,我們講了許多 vue3 directive 的使用方式還有實際應用,接下來我們要把我們前面所講的 Skeleton
還有我們的 directive
給結合在一起,做一個組合的實戰應用。
You should use Skeleton : https://ithelp.ithome.com.tw/articles/10260925
這就是我們要處理,透過 directive + skeleton 完成以下的實際案例。
首先我先把資料給拿到手,我透過 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
這個方法就大功告成了。
以下就是完整的程式碼的部分
// 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>
我們今天特別把之前所講過的東西給組合在一起 (Skeleton
跟 directive
),就是讓大家知道其實我們在實際的開發上面,技術處理都是複合式的應用,不會只使用單一的概念來進行開發,所以特別把這兩個組合應用分享給大家知道,也是希望大家在實際開發的時候可以更加的融會貫通,那今天就先到這邊告一個段落了,我們明天見囉!
Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。
我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng