vue项目使用webpack build后vue组件的样式丢失

小叶
  • 16
开发环境正常

配置如下
webpack.base.config

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin'); // vue-loader需要引入
const AddAssetsHtmlPlugin = require('add-asset-html-webpack-plugin'); // 把dll文件增加到html
const HappyPack = require('happypack');
const happyThreedPool = HappyPack.ThreadPool({ size: 5 }) // 同享进程池,size代表子程个数
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // webpack4使用MiniCssExtractPlugin代替ExtractTextPlugin, 分离css

console.log(MiniCssExtractPlugin.loader);

function pathResolve(param) {
  return path.resolve(__dirname, param)
}

// const extractCSS = new ExtractTextPlugin('assets/css/[name]_[md5:contenthash:hex:8].css');
// const extractSASS = new ExtractTextPlugin('assets/css/[name]-two.css');

module.exports = {

  // devtool: 'eval-source-map',
 
  //Resolve 配置Webpack如何寻找模块所对应的文件
  resolve: {
    // 别名
    //使用alias 将导入vue 的语句换成直接使用单独、完整的vue.min.js 文件,减少耗时的递归解析操作
    alias: {
      '@': pathResolve('../src'), // 使用绝对路径
      'vue': pathResolve('../node_modules/vue/dist/vue.min.js')
    },
    //默认 自动解析确定的扩展,能够使用户在引入模块时不带扩展
    extensions: [".js", ".json", ".vue"],
    // 第三方模块位置,使用绝对路径减少搜搜
    modules: [pathResolve('../node_modules')] //TODO在这里路径写错了导致找不到模块
  },
  externals: {
    // ElementUi: 'element-ui'
  },// 防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['happypack/loader?id=happyBabel'],
        exclude: pathResolve('../node_modules')
      },
      // {
      //   test: /\.scss$/,
      //   use: ExtractTextPlugin.extract({
      //     fallback: 'vue-style-loader',
      //     use: ['css-loader', 'sass-loader']
      //   })
      // },
      // {
      //   test: /\.css$/,
      //   use: ExtractTextPlugin.extract({
      //     fallback: 'vue-style-loader',
      //     use: ['css-loader']
      //   })
      // },
      {
        test: /\.vue$/,
        // loader: 'happypack/loader?id=happyBabel'
        loader: 'vue-loader'
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: process.env.NODE_ENV === 'development'
            }
          },
          'css-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(png|jpg|gif|svg)(\?.*)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: 'assets/img/[name].[hash:7].[ext]'
            }
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              // optipng.enabled: false will disable optipng
              optipng: {
                enabled: false,
              },
              pngquant: {
                quality: [0.65, 0.90],
                speed: 4
              },
              gifsicle: {
                interlaced: false,
              },
              // the webp option will enable WEBP
              webp: {
                quality: 75
              }
            }
          }
        ]
      },
      {
        // 对于图片资源,当文件体积小于10kb时,将其生成为base64,直接插入html中
        // 当大于10kb时,将图片名称进行按照命名规则进行更改
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: 'assets/img/[name].[hash:7].[ext]'
              // name: 'assets/img/[name].[hash:7].[ext]'
            }
          }
        ]
      }, {
        // 字体资源打包规则,与图片资源相同
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000, // 超过限制会使用file-loader或者fallback配置的loader
          name: 'assets/font/[name].[hash:7].[ext]'
        }
      }
    ],
    noParse: /jquery|lodash/ // 忽略对部分没采用模块化的文件的递归解析和处理
  },
  plugins: [
    new VueLoaderPlugin(),
    new CleanWebpackPlugin(),
    //告诉Webpack 使用了哪些动态链接库
    new webpack.DllReferencePlugin({
      // context: '../',
      manifest: require('../public/vendor/vue.manifest.json'),
    }),
    new webpack.DllReferencePlugin({
      // context: '../',
      manifest: require('../public/vendor/polyfill.manifest.json'),
    }),
    new HtmlWebpackPlugin({
      title: 'webpack vue',
      template: './public/index.html',
      filename: 'index.html',
      favicon: './public/favicon.ico'
    }),
    new AddAssetsHtmlPlugin({ filepath: pathResolve('../public/vendor/*.dll.js') }),
    new HappyPack({
      //用id来标识 happypack处理那里类文件
      id: 'happyBabel',
      //如何处理  用法和loader 的配置一样
      loaders: [{
        loader: 'babel-loader?cacheDirectory=true'
      }],
      // 共享进程池
      threadPool: happyThreedPool,
      // 允许happyPack 输出日志
      verbose: true
    }),
    // extractCSS,
    new MiniCssExtractPlugin({
      filename: 'assets/css/[name]_[contenthash:8].css',
      chunkFilename: 'assets/css/[name]_[chunkhash:8].css'
    })

  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30,
      cacheGroups: {
        // 单页提取公共js
        utils: {
          test: /[\\/]src[\\/]utils[\\/]/,
          chunks: 'all',
          //minChunks: 2, // 共享最少使用模块数量
          priority: 1,
          reuseExistingChunk: true, // 重用该chunk
          name: 'utils'
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
          priority: -10
        },
        // styles: {
        //   name: 'styles',
        //   test: /\.css$/,
        //   chunks: 'all',
        //   enforce: true
        // }
      }
    }
  }
};

webpack.dev.config.js

const baseConfig = require('./webpack.base.config');
const webpackMerge = require('webpack-merge');
const webpack = require('webpack');
const path = require('path');
function pathResolve(param) {
  return path.resolve(__dirname, param)
}
module.exports = webpackMerge(baseConfig, {
 entry: ['./src/main.js','webpack-hot-middleware/client' ], 
 output: {
  path: pathResolve('../dist'),
  filename: 'assets/js/[name].[hash:8].js',
  chunkFilename: 'assets/js/[name].[chunkhash:8].js',
  publicPath: '/'
},
 mode: 'development',
 devtool: 'cheap-eval-source-map',
 plugins: [
  new webpack.HotModuleReplacementPlugin()
 ],
 stats: {
  modules: false,
  children: false,
  chunks: false,
  chunkModules: false
 }
})

index.vue样式
`

<style lang="scss" scoped>
 .layout {
   width: 100%;
   height: 100%;
   .layout-main {
     background-color: #ff0000;
   }
   .layout-footer {
     height: 50px;
     background-color: #00ff00;
   }
 }
</style>~~~~

`
结果打包后正常输出文件如下:
image.png
main.css中有vue组件的样式
`

.layout[data-v-69de1e74] {
  width: 100%;
  height: 100%;
}
.layout .layout-main[data-v-69de1e74] {
    background-color: #ff0000;
}
.layout .layout-footer[data-v-69de1e74] {
    height: 50px;
    background-color: #00ff00;
}

h1[data-v-7ba5bd90] {
  color: "#ff0000";
}
.box[data-v-7ba5bd90] {
  width: 200px;
  height: 200px;
  background: url(/assets/img/echarts.47f4e1e.png) no-repeat center;
  background-size: contain;
}

* {
  margin: 0;
  padding: 0; }

html, body {
  width: 100%;
  height: 100%; }

`

生产环境

webpack.prod.js

const baseConfig = require('./webpack.base.config');
const webpackMerge = require('webpack-merge');
const ParalleUglifyPlugin = require('webpack-parallel-uglify-plugin'); //webpack4 使用terser代替uglify,为了支持ES6
const TerserPlugin = require('terser-webpack-plugin'); // 使用TerserPlugin覆盖默认压缩工具(minimizer-基于terser)
const OpitimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); // 压缩css
const webpack = require('webpack');
const path = require('path');
function pathResolve(param) {
  return path.resolve(__dirname, param)
}
module.exports = webpackMerge(baseConfig, {
  entry: './src/main.js',
  output: {
    path: pathResolve('../dist'),
    filename: 'assets/js/[name].[chunkhash:8].js',
    chunkFilename: 'assets/js/[name].[chunkhash:8].js',
    publicPath: '/'
  },
  mode: 'production',
  devtool: 'none',
  plugins: [
    // webpack内置了uglify.js压缩js,但是只能一个一个的压缩

   
    new webpack.HashedModuleIdsPlugin() // 保持moduleId不变,main中引入其他module,会导致moduele顺序变化,这样vender中所有moduled的iD会变化会导致chunkhash变化,从而导致缓存失效
   
  ],
  optimization: {
    minimizer: [
      new TerserPlugin({
        cache: true, // 启用缓存
        parallel: true // 并行压缩 默认为cpu-1
      }),
      new OpitimizeCssAssetsWebpackPlugin({})
    ]
  }
})

问题main.css中vue组件的样式不见了

`*{margin:0;padding:0}body,html{width:100%;height:100%}`

不怎么回事求大声解答

回复
阅读 1.7k
1 个回答
✓ 已被采纳

自己已经解决,treeshaking把.vue文件的css去掉了,在package.json加sideEffects加上vue,或者在bable-loader加上sideEffects:false

"sideEffects": [
    "*.css",
    "*.vue"
]
 {
    test: /\.js$/,
    loader: 'babel-loader',
    sideEffects: false
 }
你知道吗?

宣传栏