Vue-cli: When using "less" in vue single component file and "px2rem-loader", it will report "undefined missing '}'" error

Created on 29 Jun 2018  ·  6Comments  ·  Source: vuejs/vue-cli

Version

3.0.0-rc.3

Reproduction link

https://github.com/smk17/px2rem-vue-demo

Steps to reproduce

在命令窗口执行以下命令:
Execute the following command in the command window:
~ shell
git clone https://github.com/smk17/px2rem-vue-demo.git
cd px2rem-vue-demo
yarn
yarn serve
~

What is expected?

正确运行并px已经转成rem
Runs correctly and px has changed to rem

What is actually happening?

实际报错:
Actual error:
~~~ shell
yarn run v1.3.2
$ vue-cli-service serve
INFO Starting development server...
94% after seal

ERROR Failed to compile with 1 errors 14:57:02

error in ./src/App.vue?vue&type=style&index=0&lang=less

Module build failed (from ./node_modules/px2rem-loader/index.js):
Error: undefined:28:3: missing '}'
at error (/Users/smk17/VsCode/hello-world/node_modules/css/lib/parse/index.js:62:15)
at declarations (/Users/smk17/VsCode/hello-world/node_modules/css/lib/parse/index.js:260:26)
at rule (/Users/smk17/VsCode/hello-world/node_modules/css/lib/parse/index.js:561:21)
at rules (/Users/smk17/VsCode/hello-world/node_modules/css/lib/parse/index.js:118:70)
at stylesheet (/Users/smk17/VsCode/hello-world/node_modules/css/lib/parse/index.js:81:21)
at Object.module.exports [as parse] (/Users/smk17/VsCode/hello-world/node_modules/css/lib/parse/index.js:565:20)
at Px2rem.generateRem (/Users/smk17/VsCode/hello-world/node_modules/px2rem/lib/px2rem.js:70:20)
at Object.module.exports (/Users/smk17/VsCode/hello-world/node_modules/px2rem-loader/lib/px2rem-loader.js:7:20)

@ ./node_modules/vue-style-loader??ref--10-oneOf-1-0!./node_modules/css-loader??ref--10-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/lib??ref--10-oneOf-1-2!./node_modules/less-loader/dist/cjs.js??ref--10-oneOf-1-3!./node_modules/px2rem-loader??ref--10-oneOf-1-4!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=style&index=0&lang=less 4:14-473 14:3-18:5 14:473-18:4 15:22-481
@ ./src/App.vue?vue&type=style&index=0&lang=less
@ ./src/App.vue
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://192.168.0.128:8081/sockjs-node (webpack)/hot/dev-server.js ./src/main.js

~~~


分析该错误的发生:

  1. 发生该错误的根本原因:在less中使用样式嵌套就会发生该错误,如果不使用就完全没问题
  2. 导致该错误出现的可能:css-loader和less-loader对vue单文件组件的样式处理顺序
  3. 目前的规避方法:把vue单文件组件的less样式放到单独的less文件,然后通过<style src="./App.less"></style>的方式引用即可。
    Analyze the occurrence of this error:
  4. The root cause of the error: This error occurs when you use style nesting in less. If you don't use it, you have no problem at all.
  5. Possible causes of this error: css-loader and less-loader style handling of vue single file components
  6. Current avoidance methods: Put the less style of the vue single file component into a separate less file, and then refer to it via <style src="./App.less"></style>
question

Most helpful comment

The way you are adding the loader, it's added as the first loader to run, but it doesn't understand less.

it has to run after less-loader has run, so it has to be added before it, or even better, before postcss-loader.

Output of vue-cli-service inspect before this change (shortened):

/* config.module.rule('less').oneOf('vue') */
{
  resourceQuery: /\?vue/,
  use: [
    /* config.module.rule('less').oneOf('vue').use('vue-style-loader') */
    {
      loader: 'vue-style-loader',
      options: {
        sourceMap: false,
        shadowMode: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('css-loader') */
    {
      loader: 'css-loader',
      options: {
        minimize: false,
        sourceMap: false,
        importLoaders: 3
      }
    },
    /* config.module.rule('less').oneOf('vue').use('postcss-loader') */
    {
      loader: 'postcss-loader',
      options: {
        sourceMap: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('less-loader') */
    {
      loader: 'less-loader',
      options: {
        sourceMap: false
      }
    },
     /* config.module.rule('less').oneOf('vue').use('px2rem-loader') */
    {
      loader: 'px2rem-loader',
      options: {
        remUnit: 75,
        remPrecision: 8
      }
    },
  ]
},

Solution

config.module
  .rule(loader)
  .oneOf('vue')
  .use('px2rem-loader')
  .loader('px2rem-loader')
  .before('postcss-loader') // this makes it work.
  .options({ remUnit: 75, remPrecision: 8 })
  .end()

Output of vue-cli-service inspect after this change (shortened):

/* config.module.rule('less').oneOf('vue') */
{
  resourceQuery: /\?vue/,
  use: [
    /* config.module.rule('less').oneOf('vue').use('vue-style-loader') */
    {
      loader: 'vue-style-loader',
      options: {
        sourceMap: false,
        shadowMode: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('css-loader') */
    {
      loader: 'css-loader',
      options: {
        minimize: false,
        sourceMap: false,
        importLoaders: 3
      }
    },
    /* config.module.rule('less').oneOf('vue').use('px2rem-loader') */
    {
      loader: 'px2rem-loader',
      options: {
        remUnit: 75,
        remPrecision: 8
      }
    },
    /* config.module.rule('less').oneOf('vue').use('postcss-loader') */
    {
      loader: 'postcss-loader',
      options: {
        sourceMap: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('less-loader') */
    {
      loader: 'less-loader',
      options: {
        sourceMap: false
      }
    }
  ]
},

All 6 comments

建议你使用 postcss-plugin-px2rem,在我这里良好运行 :)

@u3u 关键是在一些庞大的旧项目,并不是简单的替换就可以解决的,还是需要从根本入手解决

The way you are adding the loader, it's added as the first loader to run, but it doesn't understand less.

it has to run after less-loader has run, so it has to be added before it, or even better, before postcss-loader.

Output of vue-cli-service inspect before this change (shortened):

/* config.module.rule('less').oneOf('vue') */
{
  resourceQuery: /\?vue/,
  use: [
    /* config.module.rule('less').oneOf('vue').use('vue-style-loader') */
    {
      loader: 'vue-style-loader',
      options: {
        sourceMap: false,
        shadowMode: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('css-loader') */
    {
      loader: 'css-loader',
      options: {
        minimize: false,
        sourceMap: false,
        importLoaders: 3
      }
    },
    /* config.module.rule('less').oneOf('vue').use('postcss-loader') */
    {
      loader: 'postcss-loader',
      options: {
        sourceMap: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('less-loader') */
    {
      loader: 'less-loader',
      options: {
        sourceMap: false
      }
    },
     /* config.module.rule('less').oneOf('vue').use('px2rem-loader') */
    {
      loader: 'px2rem-loader',
      options: {
        remUnit: 75,
        remPrecision: 8
      }
    },
  ]
},

Solution

config.module
  .rule(loader)
  .oneOf('vue')
  .use('px2rem-loader')
  .loader('px2rem-loader')
  .before('postcss-loader') // this makes it work.
  .options({ remUnit: 75, remPrecision: 8 })
  .end()

Output of vue-cli-service inspect after this change (shortened):

/* config.module.rule('less').oneOf('vue') */
{
  resourceQuery: /\?vue/,
  use: [
    /* config.module.rule('less').oneOf('vue').use('vue-style-loader') */
    {
      loader: 'vue-style-loader',
      options: {
        sourceMap: false,
        shadowMode: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('css-loader') */
    {
      loader: 'css-loader',
      options: {
        minimize: false,
        sourceMap: false,
        importLoaders: 3
      }
    },
    /* config.module.rule('less').oneOf('vue').use('px2rem-loader') */
    {
      loader: 'px2rem-loader',
      options: {
        remUnit: 75,
        remPrecision: 8
      }
    },
    /* config.module.rule('less').oneOf('vue').use('postcss-loader') */
    {
      loader: 'postcss-loader',
      options: {
        sourceMap: false
      }
    },
    /* config.module.rule('less').oneOf('vue').use('less-loader') */
    {
      loader: 'less-loader',
      options: {
        sourceMap: false
      }
    }
  ]
},

@LinusBorg perfect

@LinusBorg It is very good, tks.

loader is not defined

Was this page helpful?
0 / 5 - 0 ratings

Related issues

miyamoto-san picture miyamoto-san  ·  3Comments

Gonzalo2683 picture Gonzalo2683  ·  3Comments

jgribonvald picture jgribonvald  ·  3Comments

JIANGYUJING1995 picture JIANGYUJING1995  ·  3Comments

brandon93s picture brandon93s  ·  3Comments