Home  >  Article  >  Web Front-end  >  How to obtain different resources and switch pictures according to the theme in Vue

How to obtain different resources and switch pictures according to the theme in Vue

青灯夜游
青灯夜游forward
2021-12-02 19:12:012033browse

How to obtain different resources and switch images according to the theme in Vue? The following article will introduce to you Vue's elegant method of realizing multi-image skinning needs. I hope it will be helpful to you.

How to obtain different resources and switch pictures according to the theme in Vue

[Related recommendation: "vue.js Tutorial"]

Recently, a small game Y of the company needs to implement a localized theme , that is, different regions need to display different themes, and there are many pictures in the game. How to load the correct skin pictures elegantly and quickly becomes particularly important.

There are already many solutions in the css style switching community, you can refer to them by yourself. The article has already been explained in great detail, so I won’t go into details here. This article mainly talks about how to obtain different resources to switch images according to the theme.

Initial plan

Image reskinning, based on past experience, thanks to the dependency management of webpack, we usually use require to introduce images. For example, this way of writing (require(`@assets/img/${theme}/bg.png`)), webpack will add all the files in @assets/img into the bundle so that the corresponding image can be found during runtime. The specific implementation is as follows:

<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>

The above code can basically solve most of the skin changing needs, but for projects that require pre-loading of images, we also need to distinguish images of different themes to facilitate optimization and early loading. Since the compiled The image link is different from the link before compilation, so we get the compiled image link. In general projects, such as projects built using the official scaffolding vue-cli, all images will be url-loader processed and placed in the same folder image, so before compilationPictures with the same name in different foldersAfter compilation, the hash is different, so we cannot distinguish pictures of different themes.

So, first we need to place different theme pictures into different folders. The same applies to using 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;,
        }
      }
    ],
  },
]

In this way, after compilation, pictures of different themes will be placed in In different folders, is this the end? Not yet, we still need to obtain the compiled image link to load the theme image in advance when entering the game page. Here we can write a webpack plugin to help us collect the corresponding images and produce json files for each theme. The plug-in code is as follows:

// 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;

So we get the json image list of different themes. When entering the page, we get the list through ajax and then load the images in the other tables. Although the above approach can meet the needs, but Too complicated? Is there any easy way? Of course there is.

Optimization plan

After carefully analyzing the above code, what we finally want to get is the result of the compiled image, so if we can generate a picture object, we will The name of the image is used as the key, and the compiled result of the image is used as the value. Then the above code can be simplified as follows:

<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>

In this way, the code becomes concise and does not need to be explained throughout the article. require and try catch, next how do we implement this skinImage object?

The answer is to use webpack's require.context

Get a specific context by executing the require.context function, which is mainly used Implement automatic import of modules. In front-end projects, if you encounter the situation of importing many modules from a folder, you can use this API. It will traverse the specified files in the folder and then automatically import them, eliminating the need to explicitly import them each time. Call the import import module. The specific usage will not be described here.

Please check the official document requirecontext

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

So we write a function that automatically imports images and generates skinImage objects

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);
});

In this way we get the black theme image object, because the execution of require.context is In the compilation process rather than runtime, so all parameters can only be static. Here we also need to obtain the white theme pictures in advance, as follows

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);
});

In this way we get the picture objects of the two themes , then just assign a certain object to the skinImage of the root component, and you are done. Is it simpler and more concise than the above?

For more programming-related knowledge, please visit: Introduction to Programming! !

The above is the detailed content of How to obtain different resources and switch pictures according to the theme in Vue. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete