Vue-cli升級webpack4以及各種loader升級配置

Visupervi Reborn發表於2019-02-26

專案是基於Vue-cli webpack構建的專案,所以使用的webpack是3版本,vue-loader是13版本,所以有些配置需要修改。

1.先把package.json裝過的依賴貼出來

 {
    "name": "app",
    "version": "1.0.0",
    "description": "A Vue.js project",
    "author": "Visupervi <1109358872@qq.com>",
    "private": true,
    "scripts": {
        "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
        "start": "npm run dev",
        "unit": "jest --config test/unit/jest.conf.js --coverage",
        "e2e": "node test/e2e/runner.js",
        "test": "npm run unit && npm run e2e",
        "build": "node build/build.js"
                },
    "dependencies": {
        "axios": "^0.18.0",
        "babel-polyfill": "^6.26.0",
        "babel-runtime": "^6.26.0",
        "cors": "^2.8.5",
        "element-ui": "^2.5.4",
        "extract-text-webpack-plugin": "^4.0.0-beta.0",
        "i": "^0.3.6",
        "iview": "^3.2.2",
        "less": "^3.9.0",
        "less-loader": "^4.1.0",
        "mini-css-extract-plugin": "^0.5.0",
        "npm": "^6.5.0",
        "qs": "^6.6.0",
        "vue": "^2.5.2",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
        },
  "devDependencies": {
        "acorn": "^6.1.0",
        "acorn-jsx": "^5.0.1",
        "autoprefixer": "^7.1.2",
        "babel-core": "^6.22.1",
        "babel-helper-vue-jsx-merge-props": "^2.0.3",
        "babel-jest": "^21.0.2",
        "babel-loader": "^7.1.1",
        "babel-plugin-dynamic-import-node": "^1.2.0",
        "babel-plugin-syntax-jsx": "^6.18.0",
        "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
        "babel-plugin-transform-runtime": "^6.22.0",
        "babel-plugin-transform-vue-jsx": "^3.5.0",
        "babel-preset-env": "^1.3.2",
        "babel-preset-stage-2": "^6.22.0",
        "babel-register": "^6.22.0",
        "chalk": "^2.0.1",
        "chromedriver": "^2.27.2",
        "copy-webpack-plugin": "^5.0.0",
        "cross-spawn": "^5.0.1",
        "css-loader": "^2.1.0",
        "eslint": "^3.17.0",
        "eslint-loader": "^2.1.2",
        "file-loader": "^3.0.1",
        "friendly-errors-webpack-plugin": "^1.7.0",
        "html-webpack-plugin": "^3.2.0",
        "jest": "^22.0.4",
        "jest-serializer-vue": "^0.3.0",
        "mini-css-extract-plugin": "^0.5.0",
        "nightwatch": "^0.9.12",
        "node-notifier": "^5.1.2",
        "optimize-css-assets-webpack-plugin": "^5.0.1",
        "ora": "^1.2.0",
        "portfinder": "^1.0.13",
        "postcss-import": "^11.0.0",
        "postcss-loader": "^2.0.8",
        "postcss-url": "^7.2.1",
        "rimraf": "^2.6.0",
        "selenium-server": "^3.0.1",
        "semver": "^5.3.0",
        "shelljs": "^0.7.6",
        "uglifyjs-webpack-plugin": "^2.1.1",
        "url-loader": "^1.1.2",
        "vue-jest": "^1.0.2",
        "vue-loader": "^15.6.4",
        "vue-style-loader": "^4.1.2",
        "vue-template-compiler": "^2.6.7",
        "webpack": "^4.29.5",
        "webpack-bundle-analyzer": "^3.0.4",
        "webpack-cli": "^3.2.3",
        "webpack-dev-server": "^3.2.0",
        "webpack-merge": "^4.2.1"
    },
"engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
},
"browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
]
}
複製程式碼

這次把webpack升級到4.29.5,然後vue-loader升級到15,其他一些也是按照npm上最新的version更新。

2. 修改build資料夾下面的配置檔案

<1> 首先修改 webpack.base.conf.js
    'use strict'
    const path = require('path')
    const utils = require('./utils')
    const config = require('../config')
    const vueLoaderConfig = require('./vue-loader.conf')
    
    function resolve (dir) {
      return path.join(__dirname, '..', dir)
    }
    
    const createLintingRule = () => ({
      test: /\.(js|vue)$/,
      loader: 'eslint-loader',
      enforce: 'pre',
      include: [resolve('src'), resolve('test')],
      options: {
        formatter: require('eslint-friendly-formatter'),
        emitWarning: !config.dev.showEslintErrorsInOverlay
      }
    })
    
    module.exports = {
      context: path.resolve(__dirname, '../'),
      entry: {
        app: './src/main.js'
      },
      output: {
        path: config.build.assetsRoot,
        filename: '[name].js',
        publicPath: process.env.NODE_ENV === 'production'
          ? config.build.assetsPublicPath
          : config.dev.assetsPublicPath
      },
      resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js',
          '@': resolve('src'),
        }
      },
      module: {
        rules: [
          ...(config.dev.useEslint ? [createLintingRule()] : []),
          {
            test: /\.vue$/,
            loader: 'vue-loader',
            options: vueLoaderConfig
          },
          {
            test: /\.js$/,
            loader: 'babel-loader',
            include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
          },
          {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: utils.assetsPath('img/[name].[hash:7].[ext]')
            }
          },
          {
            test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: utils.assetsPath('media/[name].[hash:7].[ext]')
            }
          },
          {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
            }
          }
        ]
      },
      node: {
        // prevent webpack from injecting useless setImmediate polyfill because Vue
        // source contains it (although only uses it if it's native).
        setImmediate: false,
        // prevent webpack from injecting mocks to Node native modules
        // that does not make sense for the client
        dgram: 'empty',
        fs: 'empty',
        net: 'empty',
        tls: 'empty',
        child_process: 'empty'
      }
    }
複製程式碼
<2> 然後修改 webpack.dev.conf.js
    'use strict'
    const utils = require('./utils')
    const webpack = require('webpack')
    const config = require('../config')
    const merge = require('webpack-merge')
    const path = require('path')
    const baseWebpackConfig = require('./webpack.base.conf')
    const CopyWebpackPlugin = require('copy-webpack-plugin')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
    const portfinder = require('portfinder')
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    const HOST = process.env.HOST
    const PORT = process.env.PORT && Number(process.env.PORT)
    
    const devWebpackConfig = merge(baseWebpackConfig, {
      module: {
        rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
      },
      // cheap-module-eval-source-map is faster for development
      devtool: config.dev.devtool,
      mode: 'development',
      // these devServer options should be customized in /config/index.js
      devServer: {
        clientLogLevel: 'warning',
        historyApiFallback: {
          rewrites: [
            { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
          ],
        },
        hot: true,
        contentBase: false, // since we use CopyWebpackPlugin.
        compress: true,
        host: HOST || config.dev.host,
        port: PORT || config.dev.port,
        open: config.dev.autoOpenBrowser,
        overlay: config.dev.errorOverlay
          ? { warnings: false, errors: true }
          : false,
        publicPath: config.dev.assetsPublicPath,
        proxy: config.dev.proxyTable,
        quiet: true, // necessary for FriendlyErrorsPlugin
        watchOptions: {
          poll: config.dev.poll,
        }
      },
      plugins: [
        new VueLoaderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        // https://github.com/ampedandwired/html-webpack-plugin
        new HtmlWebpackPlugin({
          filename: 'index.html',
          template: 'index.html',
          inject: true
        }),
        // copy custom static assets
        new CopyWebpackPlugin([
          {
            from: path.resolve(__dirname, '../static'),
            to: config.dev.assetsSubDirectory,
            ignore: ['.*']
          }
        ])
      ]
    })
    
    module.exports = new Promise((resolve, reject) => {
      portfinder.basePort = process.env.PORT || config.dev.port
      portfinder.getPort((err, port) => {
        if (err) {
          reject(err)
        } else {
          // publish the new Port, necessary for e2e tests
          process.env.PORT = port
          // add port to devServer config
          devWebpackConfig.devServer.port = port
    
          // Add FriendlyErrorsPlugin
          devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
            compilationSuccessInfo: {
              messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
            },
            onErrors: config.dev.notifyOnErrors
              ? utils.createNotifierCallback()
              : undefined
          }))
    
          resolve(devWebpackConfig)
        }
      })
    })


##### <3> 再修改 webpack.prod.conf.js
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const env = require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  mode: 'production',
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          chunks: 'initial',
          name: 'vendors',
        },
        'async-vendors': {
          test: /[\\/]node_modules[\\/]/,
          minChunks: 2,
          chunks: 'async',
          name: 'async-vendors'
        }
      }
    },
    runtimeChunk: { name: 'runtime' }
  },
  plugins: [
    new VueLoaderPlugin(),
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      allChunks: true,
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vendor modules does not change
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'vendor',
    //   minChunks (module) {
    //     // any required modules inside node_modules are extracted to vendor
    //     return (
    //       module.resource &&
    //       /\.js$/.test(module.resource) &&
    //       module.resource.indexOf(
    //         path.join(__dirname, '../node_modules')
    //       ) === 0
    //     )
    //   }
    // }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'manifest',
    //   minChunks: Infinity
    // }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    // new webpack.optimize.CommonsChunkPlugin({
    //   name: 'app',
    //   async: 'vendor-async',
    //   children: true,
    //   minChunks: 3
    // }),

    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig
複製程式碼

3.遇見的問題以及解決辦法

1.使用了mini-css-extract-plugin以後報錯

本來是想使用這個loader來分離css但是一陣操作猛如虎,發現不能執行。 網上大神們的程式碼

  // webpack.prod.conf.js
  
  // const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// ...
// extract css into its own file
// new ExtractTextPlugin({
// ...
// })
// 升級 webpack4, 由 ExtractTextPlugin 改用 MiniCssExtractPlugin
new MiniCssExtractPlugin({
  filename: utils.assetsPath('css/[name].[contenthash].css'),
  allChunks: true,
}),

//utils.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// ...
// extract css into its own file
// new ExtractTextPlugin({
// ...
// })
// 升級 webpack4, 由 ExtractTextPlugin 改用 MiniCssExtractPlugin
new MiniCssExtractPlugin({
  filename: utils.assetsPath('css/[name].[contenthash].css'),
  allChunks: true,
}),

// utils.js
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
    const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// ...

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

  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)
  // }
  // 升級 webpack4, 由 ExtractTextPlugin 改用 MiniCssExtractPlugin
  return [
    options.extract ? MiniCssExtractPlugin.loader : 'vue-style-loader',
  ].concat(loaders)
}
複製程式碼

原文:blog.csdn.net/a295184686/…

2.這樣配置肯定是不對的

我看了一下vue-loader的文件之後分別再開發 和生產環境的配置檔案上加上下面程式碼

        const VueLoaderPlugin = require('vue-loader/lib/plugin')
        plugins[
            new VueLoaderPlugin(),
        ]
複製程式碼

這樣滿心歡喜的npm run build或者npm run dev,發現這次找不到element-ui的css和iview的css檔案,我記得我的地址早就修改了,查一下什麼原因有說是加上css-loader就試了一下,執行發現找不到.vue檔案,到這裡我知道可能是MiniCssExtractPlugin配置寫的有問題,但是實在是不知道 配置如何寫,這個時候又發現一個不同的寫法,就是在base配置loader的時候配置它的loader

    rules: [
  {
    test: /\.css$/,
    use: [
      {
        loader: MiniCssExtractPlugin.loader,
        options: {
          // 這裡可以指定一個 publicPath
          // 預設使用 webpackOptions.output中的publicPath
          publicPath: '../'
        },
      },
      'css-loader',
    ],
複製程式碼

3.經過上面的配置,執行以後發現css檔案怎麼跑到命令視窗裡來了,真是日了狗了啊,我沒截圖,反正挺搞笑的。

這個問題百度都查不到啊,點一根菸繼續分析一下·······99%是出現在mini-css-extract-plugin,所以果斷把它幹掉。

查了一下extract-text-webpack-plugin哪一個版本支援webpack4,果然有beta版本支援,然後又該回到extract-text-webpack-plugin。 當然了,webpack4本身就有的一些配置你就不用再配置了,所以把webpack.prod.conf.js中

    webpack.optimize.ModuleConcatenationPlugin(),
    webpack.optimize.CommonsChunkPlugin,
    new webpack.optimize.CommonsChunkPlugin
複製程式碼

這些配置去掉就好了。

4.一執行還是不行

分別再生產環境和開發環境加上mode:'production'和mode:'development'
再在生產環境中新增
        optimization: {
splitChunks: {
  cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      chunks: 'initial',
      name: 'vendors',
    },
    'async-vendors': {
      test: /[\\/]node_modules[\\/]/,
      minChunks: 2,
      chunks: 'async',
      name: 'async-vendors'
    }
  }
},
runtimeChunk: { name: 'runtime' }
    }
複製程式碼

Vue-cli升級webpack4以及各種loader升級配置

如果大佬看到的時候麻煩給評論一下,mini-css-extract-plugin在utils.js中配置到底怎麼寫才能執行。謝謝,也可以去https://github.com/Visupervi/-SSM-去把程式碼拉取,裡面含有SSM全部框架

相關文章