首頁  >  文章  >  web前端  >  Vue中如何根據主題取得不同的資源切換圖片

Vue中如何根據主題取得不同的資源切換圖片

青灯夜游
青灯夜游轉載
2021-12-02 19:12:012036瀏覽

Vue中如何根據主題取得不同的資源切換圖片?以下這篇文章就來跟大家介紹一下Vue優雅的實現多圖片換膚需求的方法,希望對大家有幫助。

Vue中如何根據主題取得不同的資源切換圖片

【相關推薦:《vue.js教學》】

最近公司的一個小遊戲Y需要實現本土化主題,即不同地區需要展示不同的主題,而且遊戲的圖片眾多,如何優雅快速得加載正確的皮膚圖片就變得尤為重要。

css樣式切換社群裡已經有很多方案,可以自行參考,該文章已經講得非常詳細,這裡不再贅述,本文主要講講如何根據主題獲取不同的資源切換圖片。

初始方案

圖片換膚,根據過去經驗,得益於webpack的依賴管理,我們一般是使用require引入圖片。例如這種寫法(require(`@assets/img/${theme}/bg.png`)),webpack會將@assets/img中的所有檔案都加入到bundle中,從而在運行的時候可以找到對應的圖片。具體實現如下:

<template>
  <!-- 这里需要判断图片是否存在,如果不存在需要指定为auto,不然会引起404,导致系统告警 -->
  <div class="y-content" :style="{backgroundImage: contentBg ? `url(${contentBg})` : &#39;auto&#39;}">
    <img class="y-content__reward" :src="rewardImg" />
  </div>
</template>

<script>
  data: () => ({
    theme: &#39;blcak&#39;
  }),
  computed: {
    contentBg() {
      try {
        // this.theme是文件夹,将不同的皮肤放到不同的文件夹,用同样的命名
        return require(`@assets/img/${this.theme}/contentBg.png`)
      } catch (err) {
        return &#39;&#39;;
      }
    },
    rewardImg() {
      try {
        return require(`@assets/img/${this.theme}/rewardImg.png`)
      } catch (err) {
        return &#39;&#39;;
      }
    }
  }
</script>

以上程式碼基本上能解決大部分的換膚需求,但是對於圖片需要預先加載的項目,我們還需要區分不同主題的圖片便於優化提前加載,由於編譯後的圖片連結跟編譯前的連結不同,因此我們取得編譯後的圖片連結。一般的項目中例如使用官方鷹架vue-cli建構的項目,所有的圖片都會被url-loader處理後放到同一個資料夾image當中,這樣編譯前不同資料夾相同名字的圖片編譯後之後hash是不同,因此我們是無法區分不同主題的圖片的。

於是,首先我們需要將不同的主題圖片放置到不同的資料夾當中,同樣適用用url-loader

// vue-cli配置
const imagesRule = config.module.rule(&#39;images&#39;);
imagesRule.uses.clear()        //清除原本的images loader配置
imagesRule
  .test(/white/.+.(jpg|gif|png|svg)$/)
  .use(&#39;url-loader&#39;)
    .loader(&#39;url-loader&#39;)
    .options({ name:"img/white/[name].[hash:8].[ext]", limit: 8192 })

// 加多一个主题的配置
config.module
  .rule(&#39;image2&#39;)
  .test(/black/.+.(jpg|gif|png|svg)$/)
  .use(&#39;url-loader&#39;)
    .loader(&#39;url-loader&#39;)
    .options({name:"img/black/[name].[hash:8].[ext]", limit: 8192 })
    
// 自定义webpack配置
rules: [
  {
    test: /white/.+.(png|svg|jpg|gif)$/,
    use: [
      {
      loader: &#39;url-loader&#39;,
        options: {
          limit: 8192,
          name: &#39;img/white/[name].[hash:8].[ext]&#39;,
        }
      }
    ],
  },
  {
    test: /black/.+.(png|svg|jpg|gif)$/,
    use: [
      {
      loader: &#39;url-loader&#39;,
        options: {
          limit: 8192,
          name: &#39;img/black/[name].[hash:8].[ext]&#39;,
        }
      }
    ],
  },
]

這樣子編譯後不同主題的圖片就會被放置到不同的資料夾當中,這樣就結束了嗎?還沒有,我們還需要獲取編譯後的圖片鏈接,才能在進入遊戲頁面的時候提前加載主題圖片,這裡我們可以寫一個webpack plugin來幫我們收集相應的圖片,生產各自主題的json文件。外掛程式碼如下:

// webpack版本为4.x
class WebPackSouceManifest {
  // 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数
  constructor(option = {}) {
    // 黑色主题的文件名
    this.blackManifestName = option.blackManifestName || &#39;js/blackManifest.json&#39; 
    // 白色主题的文件名
    this.whiteManifestName = option.whiteManifestName || &#39;js/whiteManifest.json&#39;
    this.blackJsonData = {
      code: 0,
      data: []
    }
    this.whiteJsonData = {
      code: 0,
      data: []
    }
    this.addFileToSouceManifest.bind(this)
  }

  apply(compiler) {
    // 指定要附加到的事件钩子函数
    compiler.hooks.emit.tapAsync(
      &#39;HashServiceWorkerStartPlugin&#39;,
      (compilation, callback) => {
        const allBuildFiles = Object.keys(compilation.assets)
        allBuildFiles.forEach((file) => {
          if (file.indexOf(&#39;white&#39;) !== -1) {
            this.addFileToSouceManifest(&#39;white&#39;, file)
          } else {
            this.addFileToSouceManifest(&#39;black&#39;, file)
          }
        })


        const sourceBlack = JSON.stringify(this.blackJsonData)
        const sourceWhite = JSON.stringify(this.whiteJsonData)
        compilation.assets[this.blackManifestName] = {
          source: () => {
            return sourceBlack;
          },
          size: () => {
            return sourceBlack.length;
          }
        }

        compilation.assets[this.whiteManifestName] = {
          source: () => {
            return sourceWhite;
          },
          size: () => {
            return sourceWhite.length;
          }
        }
        callback()
      }
    );
  }

  addFileToSouceManifest(type, file) {
    let fileItem = {
      src: file,
    }
    if (/.js$/.test(file)) {
      fileItem.type = &#39;text&#39;
    } else if (/.js.map$/.test(file)) {
      fileItem.type = &#39;json&#39;
    }
    if (type === &#39;white&#39;) {
      this.whiteJsonData.data.push(fileItem)
    } else {
      this.blackJsonData.data.push(fileItem)
    }
  }
}

module.exports = WebPackSouceManifest;

因此我們得到不同主題的圖片清單json,在進入頁面的時候透過ajax取得到清單之後對別表中的圖片進行加載,雖然以上的做法可以實現需求,但是實在太過複雜?那還有沒有輕鬆的方式呢?當然是有的。

優化方案

仔細分析上面的程式碼後她,我們最終要獲得的是圖片編譯後的結果,所以如果我們能夠產生一個圖片對象,將圖片的name當作key,圖片編譯後的結果當作value,那麼上面的程式碼將可以簡化為如下:

<template>
  <!-- 这里需要判断图片是否存在,如果不存在需要指定为auto,不然会引起404,导致系统告警 -->
  <div class="y-content" :style="{backgroundImage: contentBg ? `url(${contentBg})` : &#39;auto&#39;}">
    <img class="y-content__reward" :src="rewardImg" />
  </div>
</template>

<script>
  data: () => ({
    theme: &#39;middleEast&#39;
  }),
  computed: {
    contentBg() {
      // skinImage是跟组件下的字段
      return this.$root.skinImage[&#39;contentBg&#39;] // contentBg为name
    },
    rewardImg() {
      return this.$root.skinImage[&#39;rewardImg&#39;]
    }
  }
</script>

如此程式碼變得簡潔,不需要通篇的require跟try catch,接下來我們來如何實作這個skinImage物件呢

答案就是使用webpack的require.context

透過執行require.context函數取得一個特定的上下文,主要用來實現自動化導入模組,在前端工程中,如果遇到從一個資料夾引入很多模組的情況,可以使用這個api,它會遍歷資料夾中的指定檔案,然後自動導入,使得不需要每次顯式的呼叫import導入模組,具體用法這裡不再贅述,

詳細請看官方文件requirecontext

https://webpack.docschina.org/guides/dependency-management/# requirecontext

於是我們來寫一個自動導入圖片並產生skinImage對象的函數

const black = require.context(&#39;../black&#39;, true, /.(png|jpg|gif)$/);
const middleImageObj = {};
black.keys().forEach(path => {
  // 获取图片的key
  const key = path.slice(2, -4); 
  blackImageObj[key] = rawSkin(path);
});

這樣子我們就得到了黑色主題的圖片對象,由於require.context的執行是在編譯過程而不是運行時,所以所有的參數只能是靜態的,這裡我們還需要將白色主題的圖片也提前獲取,如下

const black = require.context(&#39;../black&#39;, true, /.(png|jpg|gif)$/);
const middleImageObj = {};
black.keys().forEach(path => {
  // 获取图片的key
  const key = path.slice(2, -4); 
  blackImageObj[key] = rawSkin(path);
});

這樣我們就得到了兩個主題的圖片對象,接下來只要將某一個物件賦值給根組件的skinImage,就大功告成了,是不是比起上面的要更簡單,更簡潔。

更多程式相關知識,請造訪:程式設計入門! !

以上是Vue中如何根據主題取得不同的資源切換圖片的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除