Problem Description:

After setting up the vUE project structure using vue-CLI, I used background-image: url(‘.. /.. /images/home_banner.jpeg’); Loading a background image, unexpectedly error:

This relative module was not found: * .. /.. /images/home_banner.jpegin. / node_modules / _css - [email protected] @ CSS - loader? {"sourceM
ap":false}! . / node_modules / _vue - [email protected] @ vue - loader/lib/style - the compiler? {"vue":true."id
":"data-v-5a90ec03"."scoped":false."hasInlineConfig":false}! . / node_modules / _sass - loader @ 7. 1.0 @ sass - loader/lib/loader. The js? {"sourceMap":false}! . / node_modules / _vue - [email protected] @ vue - lo ader/lib/selector. Js?type=styles&index=0! ./src/pages/Home.vueCopy the code

So why is that? Although this is a known problem of Sass-Loader, we still need to record the solution process to enrich ourselves and benefit future generations.


Version Description:

"sass-loader": "^ 7.1.0"."vue": "^ 2.5.2." "."webpack": "^ 3.6.0"Copy the code

Code structure:

Home.vue

<template> <div class="home"> <PageHeader></PageHeader> <div class="img-wrap"> <img src=".. /assets/images/home_banner.jpeg"> </div> </div> </template> <script> import PageHeader from '.. /components/PageHeader' export default { data () { return {} }, components: { 'PageHeader': PageHeader } } </script> <style lang="scss" scoped> @import ".. /assets/scss/pages/Home.scss" </style>Copy the code

Home.scss

div.img-wrap{
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: url('.. /.. /images/home_banner.jpeg');
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center 75%;
}
Copy the code

src/

. ├ ─ ─ App. Vue ├ ─ ─ assets │ ├ ─ ─ images │ │ ├ ─ ─ home_banner. Jpeg │ │ └ ─ ─ user_default_grey. PNG │ └ ─ ─ SCSS │ ├ ─ ─ the components │ │ └ ─ ─ PageHeader. SCSS │ ├ ─ ─ pages │ │ └ ─ ─ Home. The SCSS │ └ ─ ─ _variables. SCSS ├ ─ ─ components │ ├ ─ ─ PageFooter. Vue │ └ ─ ─ Pagepageheader. ├── ├─ page │ ├─ Admin. Vue │ ├── index.jsCopy the code


Thinking:

1. When you start in the wrong direction, you always feel that there is something wrong with your path. I’m not sure how WebPack handles static resources, see Ref. 1.

2. Since I refer to the image directly in the img SRC path in home.vue, I also have no problem; Therefore, I thought there should be something wrong with using URL in SCSS. Finally, I searched and found that this is a known problem. The URL problem of Sass-Loader is described in the official document, see Reference 2.


Solutions:

1. Avoidant:

Instead of using a separate SCSS file, the contents of SCSS are written in the style of home.vue, and validation is available;

<style lang="scss" scoped> div.img-wrap{ position: absolute; width: 100%; height: 100%; background-image: url('.. /assets/images/home_banner.jpeg'); background-repeat: no-repeat; background-size: cover; background-position: center 75%; } </style>Copy the code

2. Indirect:

Instead of setting background-image in SCSS file, set it in vue as follows:

<template> <div class="home"> <PageHeader></PageHeader> <div class="img-wrap" :style="{backgroundImage: 'url('+imageUrl+')'}"> </div> </div> </template> <script> import PageHeader from '.. /components/PageHeader' export default { data () { return { imageUrl: require('.. /assets/images/home_banner.jpeg') } }, components: { 'PageHeader': PageHeader } } </script>Copy the code

3. Solve the model

Resolves the sass-loader’s lack of URL substitution using the method recommended in the official documentation using resolve-url-loader.

Set as follows :(note that sourceMap needs to be set to true)

Resolve-url-loader (); // Set resolve-url-loader between csS-loader and sas-loadersource-maps
//source-maps required for loaders preceding resolve-url-loader (regardless of devtool).
rules: [
  {
    test: /\.scss$/,
    use: [
      ...
      {
        loader: 'css-loader', options: {... } }, { loader:'resolve-url-loader', options: {... } }, { loader:'sass-loader',
        options: {
          sourceMap: true.sourceMapContents: false}}]},... ]Copy the code

Referring to the above Settings, the modifications in VUE project are as follows:

/config/index.js dev cssSourceMap is set totrue

/build/utils.js
// generate loader string to be used with extract text plugin
  function generateLoaders (loader, loaderOptions) {
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    if (loader === 'sass'// add resolve-url-loader loaders.push({loader:'resolve-url-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader'})}else {
      return ['vue-style-loader'].concat(loaders)
    }
  }Copy the code

After that, we can happily use urls in SCSS files.


Reference:

1. Webpack handles static resources

2. The URL of sass-Loader is incorrect