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})` : 'auto'}"> <img class="y-content__reward" :src="rewardImg" /> </div> </template> <script> data: () => ({ theme: 'blcak' }), computed: { contentBg() { try { // this.theme是文件夹,将不同的皮肤放到不同的文件夹,用同样的命名 return require(`@assets/img/${this.theme}/contentBg.png`) } catch (err) { return ''; } }, rewardImg() { try { return require(`@assets/img/${this.theme}/rewardImg.png`) } catch (err) { return ''; } } } </script>
以上程式碼基本上能解決大部分的換膚需求,但是對於圖片需要預先加載的項目,我們還需要區分不同主題的圖片便於優化提前加載,由於編譯後的圖片連結跟編譯前的連結不同,因此我們取得編譯後的圖片連結。一般的項目中例如使用官方鷹架vue-cli
建構的項目,所有的圖片都會被url-loader
處理後放到同一個資料夾image當中,這樣編譯前不同資料夾相同名字的圖片編譯後之後hash
是不同,因此我們是無法區分不同主題的圖片的。
於是,首先我們需要將不同的主題圖片放置到不同的資料夾當中,同樣適用用url-loader
// vue-cli配置 const imagesRule = config.module.rule('images'); imagesRule.uses.clear() //清除原本的images loader配置 imagesRule .test(/white/.+.(jpg|gif|png|svg)$/) .use('url-loader') .loader('url-loader') .options({ name:"img/white/[name].[hash:8].[ext]", limit: 8192 }) // 加多一个主题的配置 config.module .rule('image2') .test(/black/.+.(jpg|gif|png|svg)$/) .use('url-loader') .loader('url-loader') .options({name:"img/black/[name].[hash:8].[ext]", limit: 8192 }) // 自定义webpack配置 rules: [ { test: /white/.+.(png|svg|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192, name: 'img/white/[name].[hash:8].[ext]', } } ], }, { test: /black/.+.(png|svg|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192, name: 'img/black/[name].[hash:8].[ext]', } } ], }, ]
這樣子編譯後不同主題的圖片就會被放置到不同的資料夾當中,這樣就結束了嗎?還沒有,我們還需要獲取編譯後的圖片鏈接,才能在進入遊戲頁面的時候提前加載主題圖片,這裡我們可以寫一個webpack plugin來幫我們收集相應的圖片,生產各自主題的json文件。外掛程式碼如下:
// webpack版本为4.x class WebPackSouceManifest { // 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数 constructor(option = {}) { // 黑色主题的文件名 this.blackManifestName = option.blackManifestName || 'js/blackManifest.json' // 白色主题的文件名 this.whiteManifestName = option.whiteManifestName || 'js/whiteManifest.json' this.blackJsonData = { code: 0, data: [] } this.whiteJsonData = { code: 0, data: [] } this.addFileToSouceManifest.bind(this) } apply(compiler) { // 指定要附加到的事件钩子函数 compiler.hooks.emit.tapAsync( 'HashServiceWorkerStartPlugin', (compilation, callback) => { const allBuildFiles = Object.keys(compilation.assets) allBuildFiles.forEach((file) => { if (file.indexOf('white') !== -1) { this.addFileToSouceManifest('white', file) } else { this.addFileToSouceManifest('black', 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 = 'text' } else if (/.js.map$/.test(file)) { fileItem.type = 'json' } if (type === 'white') { 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})` : 'auto'}"> <img class="y-content__reward" :src="rewardImg" /> </div> </template> <script> data: () => ({ theme: 'middleEast' }), computed: { contentBg() { // skinImage是跟组件下的字段 return this.$root.skinImage['contentBg'] // contentBg为name }, rewardImg() { return this.$root.skinImage['rewardImg'] } } </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('../black', 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('../black', true, /.(png|jpg|gif)$/); const middleImageObj = {}; black.keys().forEach(path => { // 获取图片的key const key = path.slice(2, -4); blackImageObj[key] = rawSkin(path); });
這樣我們就得到了兩個主題的圖片對象,接下來只要將某一個物件賦值給根組件的skinImage,就大功告成了,是不是比起上面的要更簡單,更簡潔。
更多程式相關知識,請造訪:程式設計入門! !
以上是Vue中如何根據主題取得不同的資源切換圖片的詳細內容。更多資訊請關注PHP中文網其他相關文章!