记一次node+react项目发布过程(一)--webpack生产环境打包优化

先附上项目效果:

项目地址: http://47.105.144.204/index

github: github.com/dsying/reac…

未优化之前 webpack配置文件

const path = require('path')
const webpack = require('webpack')
const HTMLPlugin = require('html-webpack-plugin')

//判断是否是开发环境
const isDev = process.env.NODE_ENV === 'development'

const config = {
    //入口
    entry: {
        app: path.join(__dirname, '../client/app.js')
    },
    //出口
    output: {
        filename: '[name].[hash].js', // 文件更改后重新打包,hash值变化,从而刷新缓存
        path: path.join(__dirname, '../dist'),
        //很重要
        publicPath: '/public/',
    },
    //解析
    resolve: {
        extensions: ['.js', '.jsx'], // 自动解析确定的扩展
    },
    module: {
        rules: [
            {
                //前置(在执行编译之前去执行eslint-loader检查代码规范,有报错就不执行编译)
                enforce: 'pre',
                test: /.(js|jsx)$/,
                loader: 'eslint-loader',
                exclude: [
                    path.join(__dirname,'../node_modules')
                ]
            },
            {   //将jsx转换成 js
                test: /.jsx$/,
                loader: 'babel-loader'
            },
            {   //将ES6语法转成 低版本语法
                test: /.js$/,
                loader: 'babel-loader',
                exclude: [//排除node_modules 下的js
                    path.join(__dirname,'../node_modules')
                ]
            },
            {   // 解析 图片
              test: /\.(png|jpg|gif|svg)$/,
              loader: 'file-loader',
              options: {
                name: '[name].[ext]?[hash]'
              }
            }
        ]
    },
    plugins: [
        // 生成一个html页面,同时把所有 entry打包后的 output 文件全部注入到这个html页面
        new HTMLPlugin({
            template: path.join(__dirname, '../client/template.html')
        })
    ],
    //开发模式
    mode: 'development'
}

if(isDev){
    config.entry = [
        'react-hot-loader/patch', //设置这里
        path.join(__dirname, '../client/app.js')
    ]
    config.devtool = '#cheap-module-eval-source-map'
    config.devServer = {
        host: '0.0.0.0',
        port: '8887',
        contentBase: path.join(__dirname, '../dist'), //告诉服务器从哪个目录中提供内容
        hot: true,//启用 webpack 的模块热替换特性
        overlay: {//当出现编译器错误或警告时,就在网页上显示一层黑色的背景层和错误信息
            errors: true
        },
        publicPath: '/public/',//webpack-dev-server打包的内容是放在内存中的,这些打包后的资源对外的的根目录就是publicPath,换句话说,这里我们设置的是打包后资源存放的位置
        historyApiFallback: {
            index: '/public/index.html'
        },
        proxy: { // client端 port为 8887, server端接口为 3333, 所以我们这里要设置 proxy代理
          '/api' : 'http://localhost:3333'
        }
    }
    config.plugins = [...config.plugins, new webpack.HotModuleReplacementPlugin() ]
}
module.exports = config
复制代码

运行 npm run build(package.json 内配置)之后

压缩js文件

  // 第一种方式
  const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
  config.plugins.push(
    new UglifyJsPlugin(),
  )
  // 第二种方式
  config.optimization ={
    minimize: true
  }
复制代码

分包

将第三方库分离到单独vendor chunk中

  config.entry = {
    app: path.join(__dirname, '../client/app.js'),
    vendor: [
      'react',
      'react-dom',
      'react-router-dom',
      'mobx',
      'mobx-react',
      'axios',
      'query-string',
      'dateformat',
      'marked'
    ]
  }
复制代码

如何解决重复打包问题(app中不打包vendor中的内容)

  • splitChunks: webpack4 移除了 commonChunkPlugin, splitChunks用于替代
  • runtimeChunk: 将webpack生成的 runtime 作为独立 chunk ,runtime包含在模块交互时,模块所需的加载和解析逻辑(manifest)
  config.optimization = {
    runtimeChunk: {
      name: "manifest"
    },
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendor",
          chunks: "all"
        }
      }
    }
  }
复制代码

将第三方库(library)(例如 lodash 或 react)提取到单独的 vendor chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。因此通过实现以上步骤,利用客户端的长效缓存机制,可以通过命中缓存来消除请求,并减少向服务器获取资源,同时还能保证客户端代码和服务器端代码版本一致

使用chunkhash

之前的配置中output是这样配置的

output: {
    filename: '[name].[hash],js'
}
复制代码

打包后生成的所有文件的hash值都是一样的,当我们修改了业务代码,导致app.js被重新打包,则 app,vendor,mainfest 三个chunk的hash全部发生改变,此时浏览器访问页面时就不能使用 vendor和mainfest的缓存了(因为文件名发生了改变),而是得重新发送http请求资源。 通过 chunkhash可以使每个chunk拥有自己单独的 hash值

config.output.filename = '[name].[chunkhash].js'
复制代码

走到这步,突然发现我的配置中 mode没有改为 production, 这也是为什么 我的代码有点大。 重新打包后,多出了很多警告

这是因为限制了文件大小为250kb,如果超过就会提示 避免提示设置如下:

const config = {
    performance: {
        // false | "error" | "warning" // 不显示性能提示 | 以错误形式提示 | 以警告...
        hints: "warning",
        // 开发环境设置较大防止警告
        // 根据入口起点的最大体积,控制webpack何时生成性能提示,整数类型,以字节为单位
        maxEntrypointSize: 5000000, 
        // 最大单个资源体积,默认250000 (bytes)
        maxAssetSize: 3000000
    }
}
复制代码

npm start 之后

你可能感兴趣的:(记一次node+react项目发布过程(一)--webpack生产环境打包优化)