• 老vue项目webpack3升级到webpack5全过程记录(一)


    背景

    19年新建的vue项目,使用的是webpack3,随着项目的积累,组件的增多导致本地构建,线上打包等操作速度极慢,非常影响开发效率和部署效率,基于此问题,本次对webpack及相关插件进行了优化和升级。本博文分为2篇,第 1 篇 会直接附上可运行的代码(去除了一些业务代码配置),直接粘贴复制即可使用(注意是基于vue2.0项目原配置基础上的修改哦,在网上找了一堆都是升级过程的各种坑说明,不如直接粘贴复制运行来的爽丫~~~),第 2 篇也会对相关的修改和遇到的坑进行描述说明。

    升级效果

    1、本地构建
    第一次构建 5分钟 缩短至 1.5分钟左右, 构建时间提升近 4 倍
    更新构建 由 5-10s 缩短至 1-5s ,构建提升 2 倍
    2、线上打包
    执行npm run build 由10-15分钟 缩短至 3-5分钟,构建时间提升近4倍

    升级过程

    1. 更新webpack依赖
    yarn upgrade webpack@5.37.0
    yarn add webpack-dev-server webpack-cli -D
    

    更新其他相关的组件,这里不需要一个个找了,可以使用npm-check-updates一键升级所需的组件,不再赘述怎么用的了,百度一下,你就知道,实在不会就一个个install吧~

    2. package.json启动命令修改
      "scripts": {
        "dev": "npx webpack serve --config build/webpack.dev.conf.js --color --progress",
        "build": "node build/build.js",
        "dll": "npx webpack --config build/webpack.dll.conf.js",
        "start": "npm run dev"
      }
    
    3、webpack.base.conf.js配置修改
    'use strict'
    const path = require('path')
    const utils = require('./utils')
    const config = require('../config')
    const webpack = require('webpack')
    const vueLoaderConfig = require('./vue-loader.conf')
    const { VueLoaderPlugin } = require('vue-loader');
    
    function resolve (dir) {
        return path.join(__dirname, '..', dir)
    }
    
    module.exports = {
        context: path.resolve(__dirname, '../'),
        entry: {
            app: './src/main.ts'
        },
        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', '.ts'],
            alias: {
                vue$: 'vue/dist/vue.esm.js',
                '@': resolve('src'),
                pages: resolve('src/pages')
            }
        },
        module: {
            rules: [
                {
                    test: /.vue$/,
                    use: {
                        loader: 'vue-loader'
                    }
                },
                {
                    test: /.tsx?$/,
                    exclude: resolve('node_modules'),
                    use: [
                        {
                            loader: 'babel-loader'
                        },
                        {
                            loader: "ts-loader",
                            options: { appendTsxSuffixTo: [/.vue$/], transpileOnly: true }
                        }
                    ]
                },
                {
                    test: /.js$/,
                    use: {
                        loader: 'babel-loader'
                    },
                    exclude: resolve('node_modules'),
                    include: resolve('src')
                },
                {
                    test: /.(png|jpe?g|gif|svg)(?.*)?$/,
                    type: 'asset',
                    parser: {
                        dataUrlCondition: {
                            maxSize: 10 * 1024 // 10kb
                        }
                    },
                    generator: {
                        filename: utils.assetsPath('img/[name].[hash:7].[ext]')
                    }
                }
            ]
        },
        node: {
            global: false
        },
        plugins: [
            new VueLoaderPlugin(),
            new webpack.ProvidePlugin({
                jQuery: 'jquery',
                $: 'jquery'
            }),
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: path.resolve(__dirname, './vendors.manifest.json')
            })
        ]
    }
    
    4、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 HOST = process.env.HOST
    const PORT = process.env.PORT && Number(process.env.PORT)
    
    const devWebpackConfig = merge(baseWebpackConfig, {
      mode: 'development',
      module: {
        rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
      },
      devtool: config.dev.devtool,
      devServer: {
        clientLogLevel: 'warning',
        historyApiFallback: {
          rewrites: [
            { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
          ],
        },
        hot: true,
        contentBase: false, 
        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, 
        watchOptions: {
          poll: config.dev.poll,
        }
      },
      plugins: [
        new webpack.DefinePlugin({
          'process.env': require('../config/dev.env')
        }),
        new webpack.HotModuleReplacementPlugin(),
        new HtmlWebpackPlugin({
          filename: 'index.html',
          template: 'index.html',
          inject: 'body',
          scriptLoading: 'blocking',
          minify: {
            removeComments: true
          }
        }),
        new CopyWebpackPlugin({
          patterns: [
            {
              from: path.resolve(__dirname, '../static'),
              to: config.dev.assetsSubDirectory,
              globOptions: {
                dot: true,
                gitignore: true,
                ignore: ['.*'],
              }
            },
          ]
        })
      ]
    })
    
    module.exports = new Promise((resolve, reject) => {
      portfinder.basePort = process.env.PORT || config.dev.port
      portfinder.getPort((err, port) => {
        if (err) {
          reject(err)
        } else {
          process.env.PORT = port
          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)
        }
      })
    })
    
    
    5、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 OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
    const TerserWebpackPlugin = require('terser-webpack-plugin');
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    const env = require('../config/prod.env')
    
    const webpackConfig = merge(baseWebpackConfig, {
        mode: "production",
        module: {
            rules: utils.styleLoaders({
                sourceMap: false,
                extract: true,
                usePostCSS: true
            })
        },
        devtool: config.build.devtool,
        output: {
            path: config.build.assetsRoot,
            filename: utils.assetsPath('js/[name].[chunkhash].js'),
            chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
            clean: true
        },
        optimization: {
            minimize: true,
            minimizer: [
                new TerserWebpackPlugin(),
                new OptimizeCSSPlugin(),
            ],
            runtimeChunk: { name: 'runtime' },
            concatenateModules: true,
            splitChunks: {
                cacheGroups: {
                    vendor: {
                        test: /[\/]node_modules[\/]/,
                        name: 'vendor',
                        chunks: 'all',
                        priority: -10
                    },
                    'async-vendors': {
                        test: /[\/]node_modules[\/]/,
                        minChunks: 2,
                        chunks: 'async',
                        name: 'async-vendors'
                    }
                },
            },
            moduleIds: 'deterministic'
        },
        plugins: [
            new webpack.DefinePlugin({
                'process.env': env
            }),
            new MiniCssExtractPlugin({
                filename: utils.assetsPath('css/[name].[contenthash].css'),
                chunkFilename: utils.assetsPath('css/[name].[contenthash].css')
            }),
            new HtmlWebpackPlugin({
                filename: config.build.index,
                template: 'index.html',
                inject: true,
                scriptLoading: 'blocking',
                minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true
                },
                chunksSortMode: 'auto'
            }),
            new CopyWebpackPlugin({
                patterns: [
                    {
                        from: path.resolve(__dirname, '../static'),
                        to: config.build.assetsSubDirectory,
                        globOptions: {
                            dot: true,
                            gitignore: true,
                            ignore: ['.*'],
                        }
                    },
                ]
            })
        ]
    })
    
    if (config.build.productionGzip) {
        const CompressionWebpackPlugin = require('compression-webpack-plugin');
        webpackConfig.plugins.push(
            new CompressionWebpackPlugin({
                filename: '[path][base].gz[query]',
                algorithm: 'gzip',
                test: /.(js|css|json|txt|html|ico|svg)(?.*)?$/i,
                threshold: 10240,
                minRatio: 0.8,
                deleteOriginalAssets: true
            })
        )
    }
    
    if (config.build.bundleAnalyzerReport) {
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
        webpackConfig.plugins.push(new BundleAnalyzerPlugin())
    }
    
    module.exports = webpackConfig
    
    
    6、utils.js配置修改
    'use strict'
    const path = require('path')
    const config = require('../config')
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    const packageConfig = require('../package.json')
    
    exports.assetsPath = function (_path) {
      const assetsSubDirectory = process.env.NODE_ENV === 'production' ?  config.build.assetsSubDirectory : config.dev.assetsSubDirectory;
      return path.posix.join(assetsSubDirectory, _path)
    }
    
    exports.cssLoaders = function (options) {
      options = options || {}
      const cssLoader = {
        loader: 'css-loader',
        options: {
          sourceMap: options.sourceMap
        }
      }
    
      const postcssLoader = {
        loader: 'postcss-loader',
        options: {
          sourceMap: options.sourceMap
        }
      }
    
      // 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 [
            {
              loader: MiniCssExtractPlugin.loader,
            }
          ].concat(loaders)
        } else {
          return [
            {
              loader: 'style-loader'
            }
          ].concat(loaders)
        }
      }
    
      // https://vue-loader.vuejs.org/en/configurations/extract-css.html
      return {
        css: generateLoaders(),
        postcss: generateLoaders(),
        less: generateLoaders('less'),
        // sass: generateLoaders('sass', { indentedSyntax: true }),
        // scss: generateLoaders('sass'),
        // stylus: generateLoaders('stylus'),
        // styl: generateLoaders('stylus')
      }
    }
    
    // Generate loaders for standalone style files (outside of .vue)
    exports.styleLoaders = function (options) {
      const output = []
      const loaders = exports.cssLoaders(options)
    
      for (const extension in loaders) {
        const loader = loaders[extension];
    
        // console.log("css-loader", loader)
        output.push({
          test: new RegExp('\.' + extension + '$'),
          use: loader
        })
      }
    
      return output
    }
    
    exports.createNotifierCallback = () => {
      const notifier = require('node-notifier')
    
      return (severity, errors) => {
        if (severity !== 'error') return
    
        const error = errors[0]
        const filename = error.file && error.file.split('!').pop()
    
        notifier.notify({
          title: packageConfig.name,
          message: severity + ': ' + error.name,
          subtitle: filename || '',
          icon: path.join(__dirname, 'logo.png')
        })
      }
    }
    

    转载请说明来处 黑玛鱼的前端博客

  • 相关阅读:
    <<< Tomcat运行报错IOException while loading persisted sessions: java.io.EOFException
    <<< Tomcat 部署项目There are no resources that can be added or removed from the server
    <<< tomcat启动报错StandardServer.await: create[8005]
    【LeetCode】最接近的三数之和【排序,固定k1,二分寻找k2和k3】
    【LeetCode】三数之和【排序,固定一个数,然后双指针寻找另外两个数】
    【LeetCode】最长公共前缀【二分】
    【LeetCode】盛最多水的容器【双指针+贪心 寻找最大面积】
    【LeetCode】整数反转【不能借助辅助空间,需要处理溢出】
    【LeetCode】最长回文子串【动态规划或中心扩展】
    【LeetCode】寻找两个有序数组的中位数【性质分析+二分】
  • 原文地址:https://www.cnblogs.com/webhmy/p/14791194.html
Copyright © 2020-2023  润新知