iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Modern Web

如何用TypeScript水30天鐵人賽系列 第 29

[Day29]:喜歡的都裝一起 - svg sprite

  • 分享至 

  • xImage
  •  

Day 29 Banner

喜歡的都裝在一起

我才不要別人喜歡的
我要自己喜歡的
───────────────── By Opshell


目標: vite-plugin-svg-icons

因為想要隨時可以增減icon,
然後挑自己喜歡的Icon來用,
所以原先在Vue_clil的時候有使用svg-sprite-loader
svg-sprite-loader是基於webpack打包的,
現在換到Vite我們需要換一套:


過程:

  • 安裝 & 設定

  • 1. 安裝

     yarn add vite-plugin-svg-icons -D
    

  • 2. 設定

    修改vite.comfig.ts

     // vite.comfig.ts
     import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
    
     export default () => {
       return {
          plugins: [
             createSvgIconsPlugin({
                iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], // 指定需要占存的Icon目錄
                symbolId: '[name]', // 指定symbolId格式 預設:'icon-[dir]-[name]
    
                inject: 'body-last', // | 'body-first' sprite插入位置
                customDomId: '__svg__icons__dom__', // 自訂 Dom ID
             }),
    
     //... 以下省略
    
    

  • 3. src/main.ts

    src/main.ts內引用:

     // src/main.ts
     import 'virtual:svg-icons-register';
    

  • 4. tsconfig.json

    用Typescript,還需要在tsconfig.json内新增:

     // tsconfig.json
     {
       "compilerOptions": {
          "types": ["vite-plugin-svg-icons/client"]
       }
     }
    

  • 基本使用

  • 1. 新增

    把要用的svg都丟到src/assets/icon

  • 2. 使用

    src/components裡新增 el-svgIcon.vue
    這個部分不太需要改什麼,使用方式沒啥區別,
    畢竟原理一樣

     <div v-if="href == ''" class="icon">
         <svg class="svg">
             <use :xlink:href="`#${name}`" />
         </svg>
     </div>
    

  • 番外

  • 1. 在src/views裡新增 IconList.vue

    做一個icon列表,圖像化的方式,
    之後後台使用更方便,原碼:

     // IconList.vue(Javascript)
     import { onMounted, ref } from "vue";
     import { useStore } from "vuex";
    
     import elSvgIcon from "../components/el-svgIcon.vue";
    
     export default {
         components: { elSvgIcon },
         setup() {
             const store = useStore();
             const states = useState(["userData"]);
             const iconList = ref([]);
    
             onMounted(() => {
                 iconList.value = [];
    
                 let spriteSvg = [...document.getElementById('__SVG_SPRITE_NODE__').children];
                 spriteSvg.forEach(svgDom => {
                     iconList.value.push(svgDom.id);
                 });
             });
    
             store.commit('endLoading');
    
             return {
                 ...states,
                 iconList
             };
         },
     };
    

    Script 標籤改成這樣<script setup lang="ts">
    並且修改下面這些部分:

     import { useStore } from '@/store';
     import { Ref } from '@vue/reactivity'; // Ref的型別
    
     const store = useStore();
     const iconList: Ref<string[]> = ref([]);
    
     onMounted(() => {
         const spriteSvg = document.getElementById('__svg__icons__dom__');
    
         if (spriteSvg != null) {
             let svgList = Array.from(spriteSvg.children); // 2488
    
             svgList.forEach((svgDom) => {
                 iconList.value.push(svgDom.id);
             });
         }
     });
    
     store.commit('route/endLoading');
    

    這次的改寫過程遇到了好多問題:

    1. Ref型別:

    這個還行,看官方文件就可以解決了。

    1. 型別檢測:

    用型別檢測做個導流。

    1. Array-like轉換:

    錯誤代碼(TS2488),
    這個實在很奇怪,理論上來說Array.from(spriteSvg.children)
    等價於[...spriteSvg.children]
    但是就是會報錯,打死解不開,求有解的大大解惑。

    day29-1

    1. iconList在Tamplate中型別錯誤

    這個錯誤也是很奇怪,各種寫法都一樣的結果,
    猜測是莫名地抓不到暴露的vue,但是該有的設定都跑了,
    也查不到相關的錯誤,雖然能成功執行就是了。

    day 29-1

    ※ 3和4的錯誤一直沒有其他想法,
    不過編譯的時候根本不會報錯,
    Ops甚至覺得根本是Error Len在亂叫,
    期待有大大解惑,或者哪天我懂了再回來改文章。


小結:

這邊遇到了好多神奇的問題,
雖然大部分都沒有解開,
只是找了個方法繞過去,
但過程中學到了很多東西,
也算是獲益良多。


上一篇
[Day28]:剩下的包了 - getData 改寫
下一篇
[Day30]:無限層選單&結語
系列文
如何用TypeScript水30天鐵人賽33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言