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

摘要:
背景:19年的新vue项目使用webpack3。随着项目的积累,组件的增加导致本地建设、在线打包和其他操作极其缓慢,这极大地影响了开发效率和部署效率。基于这个问题,这次对webpacks和相关插件进行了优化和升级。本博客分为两部分。第一部分将直接附加可执行代码,可直接粘贴和复制以供使用。第二部分还将描述相关的修改和遇到的陷阱。

背景

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倍
老vue项目webpack3升级到webpack5全过程记录(一)第1张

升级过程

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')
    })
  }
}

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

免责声明:文章转载自《老vue项目webpack3升级到webpack5全过程记录(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇iOS 测试 | iOS 自动化性能采集[小程序]小程序框架的简单页面布局下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

vue+webpack+npm搭建的纯前端项目

转载来源:https://www.cnblogs.com/shenyf/p/8341641.html 搭建node环境 下载 1.进入node.js官方网站下载页,点击下图中框出位置,进行下载即可,当前版本为8.9.4,下载网址为:https://nodejs.org/zh-cn/download/ 2.至于什么是LTS,请参考该知乎问题:https://...

Linux 配置gitee

安装好git后, 如何配置连接至gitee ?首先, 需要在官网注册一个gitee账号, 然后进行以下配置步骤: 1. 设置账号 $ git config --global user.name "your name" 2. 设置邮箱 $ git config --global user.email "your email" 3. 生成密钥 $ ssh-...

前端数据可视化插件:Highcharts、Echarts和D3(区别)

    前端数据可视化插件有很多,但我用过的只有Highcharts(https://www.hcharts.cn/)、Echarts(http://echarts.baidu.com/)和D3(https://d3js.org/), Web开发过程中选择使用哪一个插件是很重要的问题。就我个人而言,一般会从以下几点考虑。   1.兼容性 使用一个插件之前...

msys git 安装配置、git命令行使用

安装 1、安装msys git客户端程序 2、打开git bash,命令ssh-keygen –C “admin@test.cn “ –t rsa 3、复制C:Usersfelix.sshid_rsa.pub的内容到gitHublab里面的个人profile--ssh key中,title随便起 4、初始配置 git config...

NODEJS文件上传

基于NodeJs的文件上传 kamiSDY 0.7612018.04.02 22:38:00字数 785阅读 19,969 最近,自己玩nodeJs,用上了Express框架,小白。上传文件,查了写资料。发现了有req.files这个接口,然后试了下。 使用req.files Express默认的上传路径为/tmp。如果是windows系统,会有报错。...

git从远程仓库gitLab上拉取指定分支到本地仓库

例如:将gitLab 上的dev分支拉取到本地 1》与远程仓库建立连接:git remote add origin XXXXX.git 2》使用git branch 查看本地是否具有dev分支 3》如果没有git fetch origin dev 4》git checkout -b dev origin/dev在本地创建分支dev并切换到该分支 5》gi...