vuejs / vue-loader

📦 Webpack loader for Vue.js components

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

webpack 4 tree-shaking sideEffects: false removes styles for single-file vue components in production builds

sjones6 opened this issue · comments

Version

15.4.0

Reproduction link

https://github.com/WireWheel/style-tree-shaking-bug

Steps to reproduce

Pull down the repo.

  1. Install deps: npx lerna bootstrap --hoist
  2. Run npm run build

Take a look in dist/css/index.[hash].css and notice that the styles in packages/one/components/HelloWorld.vue are not in the extracted css file.

  1. Set sideEffects: true in packages/one/package.json
  2. Rebuild with npm run build

Styles are now included in the dist/css/index.[hash].css.

What is expected?

styles are not removed for components included in the bundle.

What is actually happening?

styles are omitted from the bundled css.


The solution here could be to say that you cannot use sideEffects: false as a valid setting for any package that includes single-file Vue components. If so, it'd might be sufficient to document that. It'd be great if tree shaking could still be used in this setup without losing the styles.

The key components here are:

  1. webpack 4 tree-shaking
  2. a mono-repo (or at least a single-file Vue component in a separate package) with sideEffects: false in the package.json

I'm opening this against vue-loader since it seems appropriate for the vue-ecosystem—but things at play are webpack, vue-loader, css-loader, etc.

Check webpack/webpack#6741
I think we could document this for library authors. Importing css is a side effect

@posva : Thanks for the reference.

For clarity, it would impact both library authors and monorepo projects. Updating the docs for vue-loader would probably be sufficient to close this out.

Yes, it doesn't just affect library authors. We have a monorepo for our app and "sideEffects": false was present in package.json.
When we compiled in Webpack with mode: 'production', CSS files @imported from Vue single-file components were dropped from the final CSS file output which caused page rendering issues. (But it compiled without errors or warnings!)

Removing "sideEffects": false solved the issue.

So this should definitely be documented somewhere e.g. here.

An important update. As of now, I need to use

  "sideEffects": [
    "*.css",
    "*.vue"
  ]

in package.json, in order to use Webpack tree shaking in Production mode. Just CSS is not enough.

You can try it by running Webpack in Development mode, with usedExports: true, then search for unused harmony in the output. It marks CSS files as unused exports, discarding them in Production.

So weirdly, this works. Essentially it's an instruction to the vue-loader to import and use the style from the same file (recursively). The resulting value of style is {}, but it tricks webpack into thinking the style in this file is not a side-effect.

-- file: Palette.vue ---

<script>
import style from './Palette.vue?vue&type=style&index=0&module=true&lang=stylus&'

export default {
   style,
   ...
}
</script>
<style scoped lang="stylus">
...
</style>

I'm NOT suggesting this as a solution, but would it be possible to append a similar import to the front of the script section automatically in the vue-loader, thereby making the style mandatory .

@gombosg 's solution is what made my day. This is not documented anywhere. Our build was not tree shaking until we added the *.vue element in the sideEffects array.

I have my own set of UI components and a file re-exporting those components, so I can spare a few keystrokes, i.e.

// ui-components/index.js
export { default as ComponentA } from './ComponentA.vue
export { default as ComponentB } from './ComponentB.vue
...
// my-view.js
import { ComponentA } from '../ui'
...

And I struggled to exclude unused Vue modules without excluding the styles of the used ones until I came across @thelonecabbage's answer (#1435 (comment)) and I'm sincerely thankful for giving me an idea of how the tree-shaking can finally be fixed in my project:

{
  module: { 
    rules: [{
      test: /\.vue$/,
      sideEffects: false
    }, {
      test: /\.vue$/,
      resourceQuery: /type=style/,
      sideEffects: true
    }]
  }
}