node版本8.x→16.x,前端维护火葬场,问题及解决方案总结

为了后续的工程开发,我需要升级我的node,在此之前我的node版本是8,这个版本太老了,从8升级到16的跨度太大,对于以前的许多项目,产生了非常多维护方面的问题,历时四天终于全部解决了,简单总结一下:

1、依赖之间的版本冲突问题

2、node16.x与node-sass的版本冲突问题,进而引发node-sass与sass-loader的冲突、sass-loader与webpack的冲突问题等

3、各插件的版本冲突问题,例如file-loader与webpack的版本冲突问题

4、部分插件不再被官方维护,例如extract-text-webpack-plugin、uglifyjs-webpack-plugin等,需要找寻相关的替代品,并根据官方文档来做配置文件相关调整

5、部分插件升级导致语法变动报错问题

问题总结起来比较清晰,但是实际维护需要大量的测试,尤其是不同版本的node对应着不同版本的开发依赖,而开发依赖可能会对应着大量插件上的冲突。网上的方案太过零散而且很多解决的不太规范,建议直接肝官方文档:
webpack官方文档

与其相关的插件也可以直接查阅文档。

目录

dependencies

一般来说,这里的依赖关系不必再维护,只要环境能兼容即可,例如:

javascript 复制代码
"dependencies": {
    "@babel/preset-es2015": "^7.0.0-beta.53",
    "axios": "^0.18.0",
    "echarts": "^4.2.0-rc.2",
    "element-ui": "^2.4.11",
    "global": "^4.4.0",
    "vis": "^4.21.0",
    "vue": "^2.5.2",
    "vue-cookies": "^1.5.12",
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1",
    "vuex-persistedstate": "^2.5.4"
  },

如果使用npm i或一个个npm i 模块名的方式导入,很可能出现以下错误:
npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree

无法解析依赖树,这是因为用户依赖的包版本与各个子项目依赖的包版本相互不兼容。

当把node升级到16.x以后,npm版本会升级为8.x,而在npm7以后install指令默认会以peerDependencies的方式去下载。

peerDependencies介绍(可略)

peerDependencies 在 package.json 中起到一个包含了项目里需要的所有的包或则用户正在下载的版本号相同的所有的包的角色。采用 peerDepenedency 来下载,就可以避免依赖库被重复下载的问题。例如:

软件包"A"依赖于软件包"B",但"B"不应该直接被安装在"A"中,而应该依赖于宿主应用程序或其他模块中已经安装的 "B" 版本。

也就是说,当依赖中相互有冲突的时候,就会报错,导入失败。

解决方案

直接暴力使用指令npm install --legacy-peer-deps,但是需要注意,这里其实按我理解来看只是省略了一些相互的冲突,而不是解决冲突,在之前的项目并没有因为这些冲突而导致运行错误的情况下,直接使用这种方式还是很便捷的。

devDependencies维护

开发依赖的下载,通常使用的指令是npm install -D,在这里不仅会出现依赖相互的冲突,如果使用--legacy-peer-deps也会出错,这是因为有一些与node版本紧密相连的依赖,直接暴力下载也会出错。

因此,建议直接修改package.json文件,将devDependencies中的node-sass、node-notifier、sass-loader、webpack等与node直接或间接相关的依赖先删除,执行npm install -D --legacy-peer-deps下载其他依赖。最后再逐步下载其他依赖。

问题

其中一个最棘手的开发依赖的维护,我的开发依赖如下:

javascript 复制代码
"devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.2.3",
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-loader": "^7.1.5",
    "babel-plugin-syntax-jsx": "^6.18.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",
    "chalk": "^2.0.1",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "^0.28.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.4",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "html-webpack-plugin": "^2.30.1",
    "node-notifier": "^5.1.2",
    "node-sass": "^4.14.1",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "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",
    "sass-loader": "^7.1.0",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^0.5.8",
    "vue-loader": "^13.3.0",
    "vue-style-loader": "^3.0.1",
    "vue-template-compiler": "^2.5.2",
    "webpack": "^3.12.0",
    "webpack-bundle-analyzer": "^2.9.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0"
  },

主要出现的问题可以归结为两类:

1、node-sass与node的版本冲突

2、node-sass版本升级后,造成的一系列版本冲突问题

解决方案

需要根据版本对照表来下载对应的node-sass以及sass-loader版本:

1、node与node-sass的版本对照表

2、node-sass与sass-loader的版本对照表

我首先是使用npm install -D node-sass --legacy-peer-deps直接下载,默认版本非常高,达到10.x,我担心这个版本会造成太多问题,因此降低到了6.x,使用指令npm install -D node-sass@6.x --legacy-peer-deps即可。如果在下载时报了warning:

不必理会,后续根据peerDependencies来做版本的测试与修改(提高/回退),对应的sass-loader建议对照表格来限制,不要太高版本了,使用npm install -D sass-loader@10.x --legacy-peer-deps即可。

对于任何一个依赖,我们可以查看它的peerDependencies。

例如对于sass-loader,我们可以使用Ctrl+Shift+R查找package-lock.json中的peerDependencies:

很显然,我们需要更高版本的webpack,需要手动修改,在这里我将webpack升级为4.36.0版本。

插件版本冲突

以webpack为轴,还会继而引发更多的问题,例如html-webpack-plugin插件的peerDependencies显示其需要的webpack版本不超过4.x,所以需要进而更新各种插件,建议直接使用"webpack":来做全局搜索,从而寻找插件的版本冲突。

如果遇到了类似下面的warning,这时候不能不理会:

这说明我们的node版本无法达到要求,需要手动的降低版本,这种没有相关的文档,自己凭感觉降低版本,然后查看peerDependencies来做调试即可。

插件过时更新

在这里我总共有两个插件失效了,一个是extract-text-webpack-plugin,另外一个是uglifyjs-webpack-plugin,官方不再维护这两个插件,这两个插件的最高版本无法适配当前的环境(其实其他插件也曾经报错过,但是我把它们的peerDependencies对应的插件降低到最低版本,这两个是实在无奈只能弃用了,当然了不建议这样做,该换插件就换)

extract-text-webpack-plugin换成mini-css-extract-plugin

前面sass-loader对应的webpack版本最低就是4.36.0,无论我是否将node-sass以及sass-loader降低到极限最低版本,webpack的版本都无法降低到4.2.x,也就是说,这个插件必须要更新了。直接搜官方文档就可以看到该插件已经被mini-css-extract-plugin替代了:

对所有用到extract-text-webpack-plugin的地方进行改造:

1、utils.js

javascript 复制代码
// let ExtractTextPlugin = require('extract-text-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-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 MiniCssExtractPlugin.extract({
      //   use: loaders,
      //   fallback: 'vue-style-loader'
      // })
      return [MiniCssExtractPlugin.loader].concat(loaders)
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }

2、webpack.prod.config.js

javascript 复制代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const TerserJsPlugin  = require('terser-webpack-plugin')

const webpackConfig = merge(baseWebpackConfig, {
  minimize: true,
  minimizer: [new TerserJsPlugin({}), new OptimizeCSSPlugin({})],
  runtimeChunk: {
    name: 'runtime'
  },
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new TerserJsPlugin({
      terserOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    // extract css into its own file
    new MiniCssExtractPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].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,
      ignoreOrder: 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 }
    }),
    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: ['.*']
      }
    ])
  ]
})

uglifyjs-webpack-plugin换成terser-webpack-plugin

同样查看官方文档,这里的切换很容易,只需要把const UglifyjsJsPlugin = require('terser-webpack-plugin')换成const TerserJsPlugin = require('terser-webpack-plugin')即可,要求不高。

项目启动报错

代码编译成功,但是报了10%的waring:

直接根据提示去网上找解决方案全是亲一色的让我们修改localhost,该方案无法解决问题,因此直接去前端查看控制台输出:

解决方法:

1、语法:

exports 导出的东西需要 require 引入,export 导出的东西需要 import 引入

2、在.babelrc中将transform-runtime注释:

相关推荐
~甲壳虫2 小时前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
Beamon__3 小时前
element-plus按需引入报错AutoImport is not a function
webpack·element-plus
CodeToGym3 小时前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化
~甲壳虫3 小时前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫3 小时前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
熊的猫3 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
前端青山13 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
GDAL15 小时前
npm入门教程1:npm简介
前端·npm·node.js
郑小憨1 天前
Node.js简介以及安装部署 (基础介绍 一)
java·javascript·node.js
lin-lins1 天前
模块化开发 & webpack
前端·webpack·node.js