webpack优化

1.production 模式打包自带优化

  • tree shaking

    tree shaking是一个术语、通常用于打包时移除js中未引用的代码(dead-code),它依赖于ES6模块系统中的import 和 export 的静态结构特性

    开发时引入一个模块时,如果只引用其中一个功能,上线打包时只会把用到的功能打包进bundle中,其他没有用到的功能都不会打包进来,可以实现最简单的基本优化

    1. 创建一个 math.js, 抛出两个方法
    export const add = (a, b) => a + b
    export const minus = (a, b) => a- b
    1. 在 main.js 中使用
    // tree shaking 分析
    // 若是此时使用 require 引入,不管 math 中的方法是否使用,都会被打包
    const math = require('./utils/math')
    // 若是使用 import 引入, 只会打包使用了 math 的方法
    import { add } from './utils/math'
    console.log('index 页面',math.add(1,2));
    console.log('index 页面',add(1,2));
    1. 根据不同的引入方式进行打包,观察打包后的文件
  • scope hoisting

    Scope hositing 作用:是将模块之间的关系进行结果推测,可以让webpack文件打包出来的代码文件更小、运行的更快

    scope hositing实现原理:分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中,但是前提是不能造成代码冗余, 因此只有哪些被引用了一次的模块可能被合并

    由于scope hositing 需要分析出模块之间的依赖关系,因此源码必须使用ES6模块化语句,不然就不能生效,原因和 tree shaking一样

    1. 在 main.js 中定义几个变量并输出

      const a = 1
      const b = 2
      const c = 3
      // webpack 在这里会进行 预执行,将结果推断后打包放在这里
      console.log(a + b + c)
      console.log(a, b, c)
    2. 打包之后代码变成

      console.log(6),console.log(1,2,3)

      因为三个变量只是在这个地方定义并且使用,并没有在其他位置使用,webpack会直接以具体的数值进行打包,节省了三个变量的定义

  • 代码压缩

    所有代码使用UglifyJsPlugin进行压缩、混淆

2.CSS优化

2.1 将CSS提取到独立文件中

Mini-css-extract-plugin 是用于将 CSS 提取为独立的文件的插件,对每个包含css的js文件都会创建一个css文件,支持按需加载css和sourceMap

  • 只能用于webpack4中,优势

    • 异步加载
    • 不重复编译,性能更好
    • 更容易使用
    • 只针对css
  • 使用

    • 安装

      npm i -D mini-css-extract-plugin
    • 引用

      const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    • 创建插件对象,配置抽离的css文件名,支持placeholder语法

      new MiniCssExtractPlugin({
      filename:'[name].css' // [name] 就是 placeholder 语法
      })
    • 将原来配置的所有 style-loader 替换为 MiniCssExtractPlugin.loader

      {
      test:/\.css$/,
      use:[MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
      test:/\.less$/,
      use:[MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      },
      {
      test:/\.scss$/,
      use:[MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      },

2.2 自动添加CSS前缀

使用 postcss,需要使用 postcss-loader 和 autoprefixer

  1. 安装

    npm i -D postcss-loader autoprefixer
  2. 修改配置文件,将 postcss-loader 放置在 css-loader 右边

    {
    test:/\.css$/,
    use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader',]
    },
    {
    test:/\.less$/,
    use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader']
    },
    {
    test:/\.scss$/,
    use:[MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader']
    },
  3. 项目根目录下添加 postcss 的配置文件: postcss.config.js

    module.exports = {
    plugins: [
    require('autoprefixer')({
    browsers: [
    // 加这个后可以出现额外的兼容性前缀
    "> 0.01%"
    ]
    })
    ]
    }

2.3 开启CSS压缩

需要使用 optimize-css-assets-webpack-plugin 插件来完成css压缩

但是由于配置css压缩时会覆盖掉webpack默认的优化设置,导致JS代码无法压缩,所以还需要把JS代码压缩插件倒入进来 terser-webpack-plugin

  1. 安装

    npm i -D terser-webpack-plugin optimize-css-assets-webpack-plugin
  2. 引用

    const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    const TerserPlugin = require('terser-webpack-plugin');
  3. 配置

    optimization:{
    minimizer: [
    new TerserPlugin({}),
    new OptimizeCssAssetsPlugin({})
    ]
    }

webpack4默认采用的JS压缩插件是 uglifyjs-webpack-plugin,在 mini-css-extract-plugin上一个版本中还推荐使用该插件,但是新的版本却建议使用 terser-webpack-plugin

3.JS优化

code splitting 是webpack打包时用到的重要的优化特性之一、此特性能够把代码分离到不同的bundle中,然后可以按需加载或者并行加载这些文件,代码分离可以用于获取更小的bundle,以及控制资源加载优先级,如果能够合理的使用能够极大影响加载时间

  • 三种常见的代码分离方法

    • 入口起点:使用entry配置,手动的分离代码
    • 放置重复:使用 SplitChunksPlugin 去重和分离 chunk
    • 动态导入:通过模块的内联函数调用来分离代码

3.1手动配置多入口

  • 手动配置多入口会存在一些问题

    • 如果入口chunks之间包含重复的模块,哪些重复的模块都会被引入到各个打包后的js文件中
    • 方法不够灵活,并且不能将核心应用程序逻辑进行动态拆分代码
  1. 在webpack配置文件中配置多个入口

    entry:{
    main: './src/main.js',
    other: './src/other.js'
    },
    output:{
    path: path.join(__dirname, '..', './dist'),
    filename: '[name].js',
    publickPath: '/'
    }
  2. 在main.js 和 other.js 中都共同引入一个模块, 并使用其功能

    1. Main.js

      import $ from 'jquery'
      
      $(() => {
      $('<div></div>').html('main').appendTo('body')
      })
    2. other.js

      import $ from 'jquery'
      
      $(() => {
      $('<div></div>').html('other').appendTo('body')
      })
  3. 打包文件,可以看到 main 和 other 打包的文件中都加载的了 jquery

3.2抽取公共代码

webpack4 以上使用的插件为 SplitChunksPlugin,webpack4 之前的使用的 CommonChunkPlugin已经被移除,最新版本的webpack中只需要在配置文件中的optimization节点下添加一个splitChunks属性即可进行相关配置

  1. 修改配置文件

    optimization
    splitChunks:{
    chunks: "all"
    }
    }
  2. 打包查看文件

    打包之后会将各自的入口文件进行打包,额外会再生产一份js文件,此文件中就是各个chunk中所引用的公共部分

  • splitChunksPlugin 配置参数

    SplitChunksPlugin 的配置只需要在 optimization 节点下的 splitChunks 进行修改即可,如果没有任何修改,则会使用默认设置

    默认的 SplitChunksPlugin 配置适用于绝大多数用户

    • webpack 会基于如下默认原则自动分割代码

      • 公用代码块或者来自 node_modules 文件夹的组件模块
      • 打包的代码块大小超过30kb,最小化压缩之前的
      • 按需加载代码块时,同时发送的请求最大数量不应该超过5
      • 页面初始化时,同时发送的请求最大数量不应该超过3
    • SplitChunksPlugin 默认配置

    module.exports = {
    optimization: {
    splitChunks: {
    chunks: 'async', // 只对异步加载的模块进行拆分,import('jquery').then()就是典型的异步加载,可选项还有 all | initial
    minSize: 30000, // 模块最少大于 30kb 才会拆分
    maxSize: 0, // 为0时模块大小无上限,只要大于 30kb 都会拆分。若是非0,超过了maxSize的值,会进一步拆分
    minChunks: 1, // 模块最少引用一次才会拆分
    maxAsyncRequests: 5, // 异步加载时同时发送的请求数量最大不能超过5,超过5的部分不拆分
    maxInitialRequests: 3, // 页面初始化时,同时发送的请求数量最大不能超过3,超过3的不跟不拆分
    automaticNameDelimiter: '~', // 默认的连接符
    name: true, // 拆分的chunk名,设置为true表示根据模块名和CacheGroup的key来自动生成,使用上面的连接符连接
    cacheGroups: { // 缓存组配置,上面配置读取完成后进行拆分,如果需要把多个模块拆分到一个文件,就需要缓存,所以命名为缓存组
    vendors: { // 自定义缓存组名
    test: /[\\/]node_modules[\\/]/, // 检查 node_modules 目录,只要模块在该目录下就使用上面配置拆分到这个组
    priority: -10, // 权重为-10,决定了那个组优先匹配,假如node_modules下面有个模块要拆分,同时满足vendors和default组,此时就会分到 priority 值比较大的组,因为 -10 > -20 所以分到 vendors 组
    filename:'vendoes.js'
    },
    default: { // 默认缓存组名
    minChunks: 2, // 最少引用两次才会被拆分
    priority: -20, // 权重 -20
    reuseExistingChunk: true // 如果主入口中引入了两个模块,其中一个正好也引用了后一个,就会直接复用,无需引用两次
    }
    }
    }
    }
    };

3.3动态导入(懒加载)

webpack4默认是允许import语法动态导入的,但是需要babel的插件支持,最新版babel的插件包为:@babel/plugin-syntax-dynamic-import,需要注意动态导入最大的好处就是实现了懒加载,用到那个模块才会加载那个模块,可以提高SPA应用程序的首屏加载速度,三大框架的路由懒加载原理一样

  1. 安装

    npm i -D @babel/plugin-syntax-dynamic-import
  2. 修改 .babelrc ,添加 @babel/plugin-syntax-dynamic-import 插件

    {
    "presets": ["@babel/env"],
    "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-syntax-dynamic-import"
    ]
    }
  3. 将jq模块动态导入

    function getDivDom(){
    // import('jquery') 返回的是一个 promise,若是低版本需要注意
    return import('jquery').then(({default: $}) => {
    return $('<div></div>').html('动态导入')
    })
    }
  4. 给某个按钮添加点击事件,点击后调用getDivDom函数创建元素并添加到页面

    window.onload = () => {
    document.getElementById('btn').addEventListener('click',() => {
    getDivDom().then(item => {
    item.appendTo('body')
    })
    })
    }

4.noParse

在引入一些第三方模块时,如jq等,我们知道其内部肯定不会依赖其他模块,因为我们用到的只是一个单独的js或者css文件,所以此时如果webpack再去解析他们的内部依赖关系,其实是非常浪费时间的,就需要阻止webpack浪费精力去解析这些明知道没有依赖的库,可以在webpack的配置文件的module节点下加上noParse,并配置正则来确定不需要解析依赖关系的模块

module:{
noParse: /jquery|bootstrap/ // jquery|bootstrap 之间不能加空格变成 jquery | bootstrap, 会无效
}

5.IgnorePlugin

在引入一些第三方模块时,例如momentJS、dayJS,其内部会做i18n处理,所以会包含很多语言包,而语言包打包时会比较占用空间,如果项目只需要用到中文或者少数语言,可以忽略掉所有的语言包,然后按需引入语言包,从而使得构建效率更高,打包生成的文件更小

  • 以moment为例

    import moment from 'moment'
    moment.locale('zh-CN') // 设置为中文 console.log(moment().subtract(6, 'days').calendar())
  1. 首先要找到moment依赖的语言包时什么,通过查看moment的源码来分析

    function loadLocale(name) {
    var oldLocale = null;
    // TODO: Find a better way to register and load all the locales in Node
    if (!locales[name] && (typeof module !== 'undefined') &&
    module && module.exports) {
    try {
    oldLocale = globalLocale._abbr;
    var aliasedRequire = require;
    aliasedRequire('./locale/' + name);
    getSetGlobalLocale(oldLocale);
    } catch (e) {}
    }
    return locales[name];
    }

    通过 aliasedRequire('./locale/' + name) 可以知道momentJS的多语言目录是locale,所有的语言JS文件都在这个目录中

  2. 使用IgnorePlugin插件忽略其依赖

    将momentJS的多语言目录locale忽略

    new webpack.IgnorePlugin(/\.\/locale/, /moment/)
  3. 需要使用某些依赖时自行手动引入

    忽略其依赖之后,moment.locale('zh-CN')就会失效,因为其所依赖的语言包全都被忽略了,需要手动将其引入

    import moment from 'moment'
    import 'moment/locale/zh-cn' // 需要手动引入方可生效
    moment.locale('zh-CN') console.log(moment().subtract(6, 'days').calendar())

6.DLLPlugin

在引入一些第三方模块时,例如Vue、React等,这些框架的文件一般都是不会修改的,而每次打包都需要去解析他们,也会影响打包速度,就算是做了拆分,也只是提高了上线后的用户访问速度,并不会提高构建速度,所以如果需要提高构建速度,应该使用动态链接库的方式,类似windows的dll文件

借助DLLPlugin插件实现将这些框架作为一个个的动态链接库,只构建一次,以后的每次构建都只会生成自己的业务代码,可以很好的提高构建效率

猪哟思想在于,讲一些不做修改的依赖文件,提前打包,这样我们开发代码发布的时候就不需要再对这些代码进行打包,从而节省了打包时间,主要使用两个插件: DLLPlugin和DLLReferencePlugin

需要注意的是,若是使用的DLLPlugin,CleanWebpackPlugin插件会存在冲突,需要移除CleanWebpackPlugin插件

  • DLLPlugin

    使用一个单独webpack配置创建一个dll文件,并且它还创建一个manifest.json,DLLReferencePlugin使用该json文件来做映射依赖性,这个文件会告诉webpack哪些文件已经提取打包好了

    • 配置参数

      • context(可选):manifest文件中请求的上下文,默认为该webpack文件上下文
      • name:公开的dll函数的名称,和output.library保持一致即可
      • path:manifest.json 生成的文件夹及名称
  • DLLReferencePlugin

    该插件主要用于主webpack配置,它引用的dll需要预先构建的依赖该系

    • 配置参数

      • context: manifest文件中的请求上下文
      • manifest: DLLPlugin插件生成的manifest.json
      • content(可选): 请求的映射模块id(默认为manifest.content)
      • name(可选): dll暴露的名称
      • scope(可选): 前缀用于访问dll的文件
      • sourceType(可选): dll是如何暴露(libraryTarget)

将VUE项目中的库抽取成DLL

  1. 准备一份将VUE打包成DLL的webpack配置文件。
  • 在build目录下新建一个文件webpack.vue.js,专门用于打包vue的DLL的。
  • 配置入口:将多个要做成dll的库全放进来
  • 配置出口:一定要设置library属性,将打包好的结果暴露在全局
  • 配置plugin:设置打包后dll文件名和manifest文件所在地
// 此配置文件 是打包VUE全家桶的
const path = require('path')
const webpack = require('webpack') module.exports = {
mode: 'production',
entry:{
vue: [
'vue/dist/vue',
'vue-router'
]
},
output:{
path: path.resolve(__dirname, '../dist'),
filename: '[name]_dll.js',
library: '[name]_dll' // 最终会在全局暴露出一个[name]_dll的对象
},
plugins:[
new webpack.DllPlugin({
name: '[name]_dll',
path: path.resolve(__dirname, '../dist/manifest.json'),
})
]
}

webpack.vue.js 只是用来打包生成 [name]_dd.js 文件和 manifest.json文件的,是不需要参与到业务代码打包的,因为只会在每一次修改了需要生成dll文件的时间才会执行一次,否则不需要参与到打包

  1. webpack.base.js中进行插件的配置

使用DllReferencePlugin指定manifest文件的位置即可

new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dist/manifest.json'),
})
  1. 由于[name]_dll文件生成之后,并没有动态的引入进去,所以需要一个插件可以动态的将生成的dll文件引入

安装add-asset-html-webpack-plugin

npm i -D add-asset-html-webpack-plugin

配置插件自动添加script标签到HTML中,需要注意的是,必须在HtmlWebpackPlugin后面引入,因为HtmlWebpackPlugin是生产一个html文件,AddAssetHtmlWebpackPlugin是在已有的html中注入一个script,否则会被覆盖

new AddAssetHtmlWebpackPlugin({
filepath: path.resolve(__dirname, '../dist/vue_dll.js')
})

7.浏览器缓存

在做了众多代码分离的优化后,其目的是为了更好的利用浏览器缓存,达到提高访问速度的效果,所以构建项目时做代码分割是必须的,

例如将固定的第三方模板抽离,下次修改了业务代码,重新发布上线不重启服务器,用户再次访问服务器就不需要再次加载第三方模板了

但是此时会遇到一个问题,如果再次打包上线不重启服务器,客户端会把以前的业务代码和第三方模块同时缓存,再次访问时依旧会访问缓存中的业务代码,所以会导致业务代码也无法更新

需要在output节点的filename中使用placeholder语法,根据代码内容生产文件名的hash,之后每次打包业务代码时,如果有改变,会生成新的hash作为文件名,浏览器就不会使用缓存了,而第三方模块不会重新打包生成新的名字,则会继续使用缓存

output: {
path: path.join(__dirname, '..','./dist'),
filename:'[name].[contenthash:8].bundle.js',
publicPath: '/'
},

8.打包分析

项目构建完成后,需要通过一些工具对打包后的bundle进行分析,通过分析可以得到一些有用的信息

  1. 使用 --profile --josn 参数,以json格式来输出打包后的结果到某个指定的文件中
webpack --profile --json > stats.json
  1. 将stats.json文件放到工具中进行分析
  • 官方工具: analyse

  • webpack-chart:webpack stats 可交互饼图。

  • webpack-visualizer:可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。

  • webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为便捷的、交互式、可缩放的树状图形式。是一个插件,可以以插件安装到项目中

    • 安装
    npm i -D webpack-bundle-analyzer
    • 使用, 配置在一个单独的文件中 webpack.analyse.js (直接拷贝的web pack.prod.js,仅仅是多了此插件的使用)
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    
    module.exports = {
    plugins: [
    new BundleAnalyzerPlugin()
    ]
    }
  • webpack bundle optimize helper:此工具会分析你的 bundle,并为你提供可操作的改进措施建议,以减少 bundle 体积大小。

9.prefetching && preloading

在优化访问性能时,除了利用浏览器缓存之外,还需要涉及到一个性能指标: 覆盖率(coverage rate)

可以在chrome浏览器的控制台中按 ctrl + shift + p,查找 coverage,打开覆盖率面板,开始录制后刷新页面,即可看到每个js文件的覆盖率,以及总的覆盖率

想提高覆盖率,需要尽可能多的使用impor动态导入,也就是懒加载的功能,将一切能使用懒加载的地方都是用懒加载,这样可以大大的提高覆盖率

但是有时候使用懒加载会影响用户体验,所以可以在使用懒加载的时候使用魔法注释(Magic Comments): prefetching,是指在首页资源加载完毕后,空闲的时候,将动态导入的资源加载进来,这样既可以提高首屏加载速度,也可以解决懒加载可能会影响用户体验的问题

必懂的wenpack优化的更多相关文章

  1. 必懂的webpack高级配置

    webpack高级配置 1.HTML中img标签的图片资源处理 使用时.只需要在html中正常引用图片即可.webpack就会找到对应的资源进行打包.并修改html中的引用路径 主要是将html中的i ...

  2. 深度剖析HashMap的数据存储实现原理(看完必懂篇)

    深度剖析HashMap的数据存储实现原理(看完必懂篇) 具体的原理分析可以参考一下两篇文章,有透彻的分析! 参考资料: 1. https://www.jianshu.com/p/17177c12f84 ...

  3. [转帖]K8s 工程师必懂的 10 种 Ingress 控制器

    K8s 工程师必懂的 10 种 Ingress 控制器 https://www.kubernetes.org.cn/5948.html 控制器有好多啊. 2019-10-18 23:07 中文社区 分 ...

  4. hiho一下 第二十九周 最小生成树三·堆优化的Prim算法【14年寒假弄了好长时间没搞懂的prim优化:prim算法+堆优化 】

    题目1 : 最小生成树三·堆优化的Prim算法 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 回到两个星期之前,在成功的使用Kruscal算法解决了问题之后,小Ho产生 ...

  5. 一本彻底搞懂MySQL索引优化EXPLAIN百科全书

    1.MySQL逻辑架构 日常在CURD的过程中,都避免不了跟数据库打交道,大多数业务都离不开数据库表的设计和SQL的编写,那如何让你编写的SQL语句性能更优呢? 先来整体看下MySQL逻辑架构图: M ...

  6. web前端开发必懂之一:JS继承和继承基础总结

    首先,推荐一篇博客豪情的博客JS提高: http://www.cnblogs.com/jikey/p/3604459.html ,里面的链接全是精华, 一般人我不告诉他; 我们会先从JS的基本的设计模 ...

  7. javascript中的闭包,超简单论述,保证小学生必懂

    js中的闭包已经有很多论断了,大家伙有没有听懂了,先引用一片比较高端 的 ”汤姆大叔“  深入理解JavaScript系列(16):闭包(Closures) 好了,为了引起大家的兴趣,先来小诗一首 v ...

  8. Spring事务配置的五种方式 巨全!不看后悔,一看必懂!

    前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的. ...

  9. 新手必看的jQuery优化笔记十则

    jQuery优化 1.简介 jQuery正在成为Web开发人员首选的JavaScript库,作为Web开发者,除了要了解语言和框架的应用技巧外,如何提升语言本身的性能也是开发人员应该思考的问题.文章就 ...

随机推荐

  1. 初次比较正式的IT职场面试后几点对自己web开发的思考

    昨天晚上参加一个web开发面试,对于还没有真正毕业的自己来说,web开发的面试不是第一次,暑假就面试几家公司,前几次的面试并没有发现自己对自己学习的专业知识有什么学习态度的问题,因为前几次的面试官都是 ...

  2. hadoop之hive高级操作

    在输出结果较多,需要输出到文件中时,可以在hive CLI之外执行hive -e "sql" > output.txt操作 但当SQL语句太长或太多时,这种方式不是很方便,可 ...

  3. Go语言学习——彻底弄懂return和defer的微妙关系

    疑问 前面在函数篇里介绍了Go语言的函数是支持多返回值的. 只要在函数体内,对返回值赋值,最后加上return就可以返回所有的返回值. 最近在写代码的时候经常遇到在return后,还要在defer里面 ...

  4. log4net插入access自定义字段

    1.创建表格 2.创建log4net.xml,并设置属性始终复制,关键属性 <bufferSize value="1" /> <conversionPattern ...

  5. Kafka 学习之路(一)—— Kafka简介

    一.简介 Apache Kafka是一个分布式的流处理平台.它具有以下特点: 支持消息的发布和订阅,类似于RabbtMQ.ActiveMQ等消息队列: 支持数据实时处理: 能保证消息的可靠性投递: 支 ...

  6. spring boot 2.x 系列 —— spring boot 整合 servlet 3.0

    文章目录 一.说明 1.1 项目结构说明 1.2 项目依赖 二.采用spring 注册方式整合 servlet 2.1 新建过滤器.监听器和servlet 2.2 注册过滤器.监听器和servlet ...

  7. 使用elasticsearch启动项目报错failed to load elasticsearch nodes 。。。。。No type specified for field [name]

    failed to load elasticsearch nodes .....No type specified for field [name]翻译: 加载ElasticSearch节点失败... ...

  8. meta 详解

    对meta标签用处的介绍,meta常用于定义页面的说明,关键字,最后修改日期,和其它的元数据.这些元数据将服务于浏览器(如何布局或重载页面),搜索引擎和其它网络服务. 组成 meta标签共有两个属性, ...

  9. Spring Boot 整合 Freemarker,50 多行配置是怎么省略掉的?

    Spring Boot2 系列教程接近完工,最近进入修修补补阶段.Freemarker 整合貌似还没和大家聊过,因此今天把这个补充上. 已经完工的 Spring Boot2 教程,大家可以参考这里: ...

  10. Jrebel激活服务搭建

    前言 因为平时的开发工具是使用IntelliJ IDEA,所以热部署项目代码的时候,使用的Jrebel.因为Jrebel是收费的,所以以前用的时候都是在网上找破解方法(国人通用做法),在网上找到的办法 ...