jeecgboot / JeecgBoot

🔥「企业级低代码平台」前后端分离架构SpringBoot 2.x/3.x,SpringCloud,Ant Design&Vue3,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领新的开发模式OnlineCoding->代码生成->手工MERGE,帮助Java项目解决70%重复工作,让开发更关注业务,既能快速提高效率,帮助公司节省成本,同时又不失灵活性。
http://www.jeecg.com
Apache License 2.0
39.87k stars 14.65k forks source link

建议官网展示的前端项目nginx部署的时候加上gzip压缩,加速第一次访问 #88

Closed ileadall42 closed 5 years ago

ileadall42 commented 5 years ago

nginx.conf 的 http 中加入 # gzip config片断

具体参数:

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    # gzip config
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 9;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";

server {
    listen 80;
    root /usr/share/nginx/html;

    location / {
        # 用于配合 browserHistory使用
        try_files $uri $uri/ /index.html;

        # 如果有资源,建议使用 https + http2,配合按需加载可以获得更好的体验
        # rewrite ^/(.*)$ https://preview.pro.ant.design/$1 permanent;

    }
    location /api {
        proxy_pass https://preview.pro.ant.design;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Host              $http_host;
        proxy_set_header   X-Real-IP         $remote_addr;
    }
}

参考:在部署的时候开启nginx的gzip自己调节参数,以及history的try_files解决方案和相关https安全部署方案。

zhangdaiscott commented 5 years ago

3q

wanrengang commented 5 years ago

如何实现按需加载对应的文件 目前是一次性全部加载

shuperry commented 5 years ago

@ileadall42 你好,我用的是 caddy,也加了 gzip 相关的配置,第一次访问速度还是在 5 到 6 秒才能打开页面,去琢磨了下代码里面,优化空间确实不大了,这个速度算正常吗,咨询下你那边是多长时间,还有别的优化建议吗

shuperry commented 4 years ago

办法是有,在 vue.config.js 里面加上压缩的代码,在 nginx.conf 里面加上 gzip,我自己弄了,需要参考代码不

jlsunl commented 4 years ago

办法是有,在 vue.config.js 里面加上压缩的代码,在 nginx.conf 里面加上 gzip,我自己弄了,需要参考代码不

配置发一下,参考一下吧

shuperry commented 4 years ago

@jlsunl 项目整体需要把 ant-design-vue 改成按需加载

nginx.conf 的 http 中加入以下片断

  gzip              on;
  gzip_disable      "MSIE [1-6]\.";
  gzip_static       on;
  gzip_proxied      any;
  gzip_min_length   1k;
  gzip_comp_level   5;
  gzip_buffers      4 16k;
  gzip_types        text/plain text/javascript text/css text/xml application/javascript application/x-javascript application/xml application/json application/xml+rss image/jpeg image/gif image/png;
  gzip_vary         off;
  gzip_http_version 1.0;

vue.config.js

const path = require('path')

const TerserWebpackPlugin = require('terser-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')

const keys = require('lodash.keys')
const os = require('os')
const webpack = require('webpack')

const createThemeColorReplacerPlugin = require('./plugins/theme.plugin')

function resolve(dir) {
  return path.join(__dirname, dir)
}

const envConfig = require('./src/config/env.config')

const isOnline = !['local'].includes(process.env.mode)
const isProduction = ['prod'].includes(process.env.mode)

const assetsCDN = {
  // webpack build externals
  externals: {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    axios: 'axios'
  },
  css: [],
  js: [
    '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
    '//cdn.jsdelivr.net/npm/vue-router@3.1.6/dist/vue-router.min.js',
    '//cdn.jsdelivr.net/npm/vuex@3.3.0/dist/vuex.min.js',
    '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js'
  ]
}

const productionGzipExtensions = ['js', 'css', 'json', 'txt', 'html', 'ico', 'svg']

// vue.config.js
const vueConfig = {
  configureWebpack: config => {
    // webpack plugins
    config.plugins.push(
      // Ignore all locale files of moment.js
      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
      createThemeColorReplacerPlugin()
    )

    if (isOnline) {
      config.plugins.push(
        // 开启压缩
        new CompressionWebpackPlugin({
          filename: '[path].gz[query]',
          algorithm: 'gzip',
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          threshold: 10240,
          minRatio: 0.8
        }),
        new TerserWebpackPlugin({
          sourceMap: false,
          parallel: false,
          terserOptions: {
            compress: {
              drop_debugger: isProduction,
              drop_console: isProduction,
              pure_funcs: [] // 移除console
            },
            beautify: false,
            comments: false,
            keep_classnames: false,
            output: {
              comments: false,
            }
          }
        })
      )

      config.performance = {
        hints: 'warning',
        // 入口起点的最大体积 整数类型(以字节为单位 500k).
        maxEntrypointSize: 500 * 1024,
        // 生成文件的最大体积 整数类型(以字节为单位 300k).
        maxAssetSize: 300 * 1024,
        // 只给出 js 文件的性能提示.
        assetFilter: function(assetFilename) {
          return assetFilename.endsWith('.js')
        }
      }
    }

    // if prod, add externals
    config.externals = isOnline ? assetsCDN.externals : {}
  },

  chainWebpack: (config) => {
    config.resolve.alias
      .set('@$', resolve('src'))

    const svgRule = config.module.rule('svg')
    svgRule.uses.clear()
    svgRule
      .oneOf('inline')
      .resourceQuery(/inline/)
      .use('vue-svg-icon-loader')
      .loader('vue-svg-icon-loader')
      .end()
      .end()
      .oneOf('external')
      .use('file-loader')
      .loader('file-loader')
      .options({
        name: 'assets/[name].[hash:8].[ext]'
      })

    config.plugin('define').tap(args => {
      keys(envConfig[process.env.mode]).forEach(key => {
        args[0]['process.env'][key] = JSON.stringify(envConfig[process.env.mode][key])
      })
      return args
    })

    if (isOnline) {
      // 删除预加载
      config.plugins.delete('preload')
      config.plugins.delete('prefetch')

      config.output.filename('[name].[hash].js').end()

      config.plugin('html').tap(args => {
        args[0].cdn = assetsCDN
        return args
      })
    }
  },

  css: {
    sourceMap: false,
    loaderOptions: {
      less: {
        lessOptions: {
          modifyVars: {
            // less vars,customize ant design theme

            // 'primary-color': '#F5222D',
            // 'link-color': '#F5222D',
            'border-radius-base': '2px'
          },
          // DO NOT REMOVE THIS LINE
          javascriptEnabled: true
        }
      }
    }
  },

  devServer: {
    // development server port 3000
    port: 3000,
    proxy: {
      '/api2': {
        target: envConfig[process.env.mode].BASE_API_2, // 请求本地需要后台项目
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '/api2': '' // 默认所有请求都加了 api 前缀, 需要去掉
        }
      },
      '/api': {
        target: envConfig[process.env.mode].BASE_API, // 请求本地需要后台项目
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '/api': '' // 默认所有请求都加了 api 前缀, 需要去掉
        }
      }
    }
  },
  parallel: os.cpus().length > 1,
  // 如果你不需要生产环境的 source map, 可以将其设置为 false 以加速生产环境构建.
  productionSourceMap: false,
  lintOnSave: false,
  // babel-loader no-ignore node_modules/*
  transpileDependencies: ['vue-plyr']
}

module.exports = vueConfig
zhangdaiscott commented 4 years ago

https://github.com/zhangdaiscott/jeecg-boot/issues/1343

jwcjf commented 4 years ago

@jlsunl 项目整体需要把 ant-design-vue 改成按需加载

nginx.conf 的 http 中加入以下片断

  gzip              on;
  gzip_disable      "MSIE [1-6]\.";
  gzip_static       on;
  gzip_proxied      any;
  gzip_min_length   1k;
  gzip_comp_level   5;
  gzip_buffers      4 16k;
  gzip_types        text/plain text/javascript text/css text/xml application/javascript application/x-javascript application/xml application/json application/xml+rss image/jpeg image/gif image/png;
  gzip_vary         off;
  gzip_http_version 1.0;

vue.config.js

const path = require('path')

const TerserWebpackPlugin = require('terser-webpack-plugin')
const CompressionWebpackPlugin = require('compression-webpack-plugin')

const keys = require('lodash.keys')
const os = require('os')
const webpack = require('webpack')

const createThemeColorReplacerPlugin = require('./plugins/theme.plugin')

function resolve(dir) {
  return path.join(__dirname, dir)
}

const envConfig = require('./src/config/env.config')

const isOnline = !['local'].includes(process.env.mode)
const isProduction = ['prod'].includes(process.env.mode)

const assetsCDN = {
  // webpack build externals
  externals: {
    vue: 'Vue',
    'vue-router': 'VueRouter',
    vuex: 'Vuex',
    axios: 'axios'
  },
  css: [],
  js: [
    '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
    '//cdn.jsdelivr.net/npm/vue-router@3.1.6/dist/vue-router.min.js',
    '//cdn.jsdelivr.net/npm/vuex@3.3.0/dist/vuex.min.js',
    '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js'
  ]
}

const productionGzipExtensions = ['js', 'css', 'json', 'txt', 'html', 'ico', 'svg']

// vue.config.js
const vueConfig = {
  configureWebpack: config => {
    // webpack plugins
    config.plugins.push(
      // Ignore all locale files of moment.js
      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
      createThemeColorReplacerPlugin()
    )

    if (isOnline) {
      config.plugins.push(
        // 开启压缩
        new CompressionWebpackPlugin({
          filename: '[path].gz[query]',
          algorithm: 'gzip',
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          threshold: 10240,
          minRatio: 0.8
        }),
        new TerserWebpackPlugin({
          sourceMap: false,
          parallel: false,
          terserOptions: {
            compress: {
              drop_debugger: isProduction,
              drop_console: isProduction,
              pure_funcs: [] // 移除console
            },
            beautify: false,
            comments: false,
            keep_classnames: false,
            output: {
              comments: false,
            }
          }
        })
      )

      config.performance = {
        hints: 'warning',
        // 入口起点的最大体积 整数类型(以字节为单位 500k).
        maxEntrypointSize: 500 * 1024,
        // 生成文件的最大体积 整数类型(以字节为单位 300k).
        maxAssetSize: 300 * 1024,
        // 只给出 js 文件的性能提示.
        assetFilter: function(assetFilename) {
          return assetFilename.endsWith('.js')
        }
      }
    }

    // if prod, add externals
    config.externals = isOnline ? assetsCDN.externals : {}
  },

  chainWebpack: (config) => {
    config.resolve.alias
      .set('@$', resolve('src'))

    const svgRule = config.module.rule('svg')
    svgRule.uses.clear()
    svgRule
      .oneOf('inline')
      .resourceQuery(/inline/)
      .use('vue-svg-icon-loader')
      .loader('vue-svg-icon-loader')
      .end()
      .end()
      .oneOf('external')
      .use('file-loader')
      .loader('file-loader')
      .options({
        name: 'assets/[name].[hash:8].[ext]'
      })

    config.plugin('define').tap(args => {
      keys(envConfig[process.env.mode]).forEach(key => {
        args[0]['process.env'][key] = JSON.stringify(envConfig[process.env.mode][key])
      })
      return args
    })

    if (isOnline) {
      // 删除预加载
      config.plugins.delete('preload')
      config.plugins.delete('prefetch')

      config.output.filename('[name].[hash].js').end()

      config.plugin('html').tap(args => {
        args[0].cdn = assetsCDN
        return args
      })
    }
  },

  css: {
    sourceMap: false,
    loaderOptions: {
      less: {
        lessOptions: {
          modifyVars: {
            // less vars,customize ant design theme

            // 'primary-color': '#F5222D',
            // 'link-color': '#F5222D',
            'border-radius-base': '2px'
          },
          // DO NOT REMOVE THIS LINE
          javascriptEnabled: true
        }
      }
    }
  },

  devServer: {
    // development server port 3000
    port: 3000,
    proxy: {
      '/api2': {
        target: envConfig[process.env.mode].BASE_API_2, // 请求本地需要后台项目
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '/api2': '' // 默认所有请求都加了 api 前缀, 需要去掉
        }
      },
      '/api': {
        target: envConfig[process.env.mode].BASE_API, // 请求本地需要后台项目
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          '/api': '' // 默认所有请求都加了 api 前缀, 需要去掉
        }
      }
    }
  },
  parallel: os.cpus().length > 1,
  // 如果你不需要生产环境的 source map, 可以将其设置为 false 以加速生产环境构建.
  productionSourceMap: false,
  lintOnSave: false,
  // babel-loader no-ignore node_modules/*
  transpileDependencies: ['vue-plyr']
}

module.exports = vueConfig

请问相关require文件能发一下吗谢谢

jlsunl commented 4 years ago

@shuperry 谢谢

wyyina commented 4 years ago

plugins/theme.plugin? 在哪里 谢谢

wyyina commented 4 years ago

大佬 env.config 也发下啊

shuperry commented 4 years ago

@wyyina plugins/theme.plugin.js

const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default

const getAntdSerials = (color) => {
  // 淡化(即less的tint)
  const lightens = new Array(9).fill().map((t, i) => {
    return ThemeColorReplacer.varyColor.lighten(color, i / 10)
  })
  const colorPalettes = generate(color)
  const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
  return lightens.concat(colorPalettes).concat(rgb)
}

const themePluginOption = {
  fileName: 'css/theme-colors-[contenthash:8].css',
  matchColors: getAntdSerials('#1890ff'), // 主色系列
  // 改变样式选择器,解决样式覆盖问题
  changeSelector (selector) {
    switch (selector) {
      case '.ant-calendar-today .ant-calendar-date':
        return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
      case '.ant-btn:focus,.ant-btn:hover':
        return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
      case '.ant-btn.active,.ant-btn:active':
        return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
      case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
      case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
        return ':not(.ant-steps-item-process)' + selector
      // fixed https://github.com/vueComponent/ant-design-vue-pro/issues/876
      case '.ant-steps-item-process .ant-steps-item-icon':
        return ':not(.ant-steps-item-custom)' + selector
      case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
      case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
        return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
      case '.ant-menu-horizontal > .ant-menu-item-selected > a':
      case '.ant-menu-horizontal>.ant-menu-item-selected>a':
        return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
      case '.ant-menu-horizontal > .ant-menu-item > a:hover':
      case '.ant-menu-horizontal>.ant-menu-item>a:hover':
        return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
      default :
        return selector
    }
  }
}

const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)

module.exports = createThemeColorReplacerPlugin

env.config.js

module.exports = {
  local: {
    BASE_API: 'xxx'
  },
  dev: {
    BASE_API: 'xxx'
  },
  sit: {
    BASE_API: 'xxx'
  },
  prod: {
    BASE_API: 'xxx',
  }
}

package.json -> scripts

"scripts": {
    "start": "cross-env mode=local vue-cli-service serve --open --mode local",
    "build:dev": "cross-env mode=dev vue-cli-service build",
    "build:sit": "cross-env mode=sit vue-cli-service build",
    "build:prod": "cross-env mode=prod vue-cli-service build"
  }
JaneYork commented 2 years ago

访问速度很慢 #943

我也开启了GZIP,原来6M,后来2M,但是首次加载30秒??? 还有其他方式吗

zhangdaiscott commented 2 years ago

第一次就是慢点,等vue3出来,会显著改善

CondiGao commented 1 year ago

vue3也没快多少

wuchangrong commented 1 year ago

vue3也没快多少

是的,没有感觉到vue3快过

zhangdaiscott commented 1 year ago

部署之后比vue2现住快很多,开发环境的确不如vue2

dmcxys commented 1 month ago

It's really too slow.too slow...