webpakc性能优化

webpakc性能优化

  • 开发环境性能优化
    1.优化打包构建速度
    2.优化代码调试
  • 生产环境性能优化
    1.优化打包构建速度
    2.优化代码运行的性能
一、HMR:hot modules replacement 热模块替换/模块热替换
  • 作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有),提升构建速度

  • 样式文件:可以使用HMR功能,因为style-loader内部实现了

  • js文件:默认不能使用HMR功能,

  • html文件:默认不能使用HMR功能

    改变entry,[ './src/index.js','./src/index.html'],就可以开启HMR功能,html只有一个文件,每次重新打包会刷新
    
devServer:{
    contentBase:resolve(__dirname,'build'),
    compress:true,
    port:3000,
    open:true
    //开启HMR功能
    hot:true
}
//新建print.js
//修改index.js
if(module.hot){
    //必须开启HMR功能,才会执行到这个函数
   module.hot.accept('./print.js',function(){
       //监听print.js文件是否发生变化
       print();
   })
}
二、source-map:一种提供源代码到构建后代码映射技术
devtool:'eval-source-map'

source-map:一种提供源代码到构建后代码映射技术(如果构建后代码出错,通过映射可以追踪到源代码错误)

值:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
   source-map               :外联
     //错误代码准确信息  和  源代码的错误位置(精确到列)
   inline-source-map        :内联,只生成一个内联的source-map
     //错误代码准确信息  和  源代码的错误位置(精确到列)
   hidden-source-map        :外联
     //错误代码准确信息  和  构建代码的错误位置(只有构建后的代码)
   eval-source-map          :内联,每个文件都生成对应的source-map文件,都在eval
     //错误代码准确信息  和  源代码的错误位置(精确到列)
   nosources-source-map     :外联
     //错误代码准确信息  和  没有源代码信息
   cheap-source-map         :外联
     //错误代码准确信息  和  源代码的错误位置(精确到行)
   cheap-module-source-map  :外联
     //错误代码准确信息  和  没有源代码信息,module会将loader的source-map加入
                                      
   内联和外联的区别:1.外部生成了文件,内联没有;2.内联构建速度更快
   
   开发环境:速度快,调试更友好 ----->eval-source-map/ eval-cheap-module-source-map
     速度快
       eval-cheap-source-map
       eval-source-map
     调试友好
       source-map
       cheap-module-source-map
       cheap-source-map
   生产环境:源代码要不要隐藏,调试要不要更友好----->source-map/cheap-module-source-map
     //内联会让代码体积变大,所以再生产环境下不用内联
     源代码要不要隐藏
       nosources-source-map  全部隐藏
       hidden-source-map  只隐藏源代码,会提示构建后代码错误信息
     调试要不要更友好
       source-map
三、oneof,以下loader只会匹配一个
//不能有2个配置处理同一类型的文件
//这里匹配2个js,将一个js提出与oneof平级,enforce优先处理
module: {
  rules: [
    {
        test: /\.js$/,
        exclude: /node-modules/,
        enforce: 'pre', //优先语法检查
        loader: 'eslint-loader',
        options: {
            fix: true
        }
    },
    {
      oneof: [
        {
          test: /\.css$/,
          use: [...commonCssLoader]
        },
        {
          test: /\.less$/,
          use: [...commonCssLoader, 'less-loader']
        },
        {
          test: /\.js$/,
          exclude: /node-modules/,
          loader: 'babel-loader'
        }
      ]
    }
  ]
}
四、cache缓存
  1. babel缓存

    优点:第二次打包的速度更快

    cacheDirectory:true

  2. 文件资源缓存

    优点:代码上线运行缓存更好使用

    1. hash:webpack每次构建会生成一个唯一的hash值

      问题:js和css的hash值还是一样,如果重新打包会导致所有的缓存失效(可能改变一个文件)

    2. chunkhash:根据chunk生成的hash值。如果打包来源同一个chunk,那么hash值就一样

      问题:js和css的hash值还是一样

    3. contenthash:根据文件的内容生成hash值。不同的文件hash值一定不一样

//babel缓存
{
    test:/\.js$/
    exclude:/node-modules/
    loader:'babel-loader'
    options:{
        parsets:[
            [
                '@babel/parset-env'
                {
                    useBuiltIns:'usage'
                    corejs:{version:3},
                    targets:{
                        chrome:'60',
                        firefox:'50'
                    }
                }
            ]
        ],
        //开启babel缓存
        //第二次构建会读取之前的缓存
        cacheDirectory:true
    }
}
五、tree shaking:去除无用代码

前提:1、使用ES6模块化。2、必须在production模式下。满足这2个条件自动开启

作用:减少代码体积,构建速度更快。

在package.json中配置

"sideEffects":false 所有的代码都没有副作用(都能进行tree shaking)
问题:可能会把   css  /  @babel/polyfill 文件干掉
"sideEffects":["*.css","*.less"]
六、code split:代码分割
//生产环境配置
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  //单入口
  //entry: './src/js/index.js',
  //多入口
  entry:{
    //有一个入口,最终输出就有一个bundle
    index:'./src/js/index.js',
    test:'./src/js/test.js'
  },
  output: {
    //[name]:取当前文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseInlineTagWhitespace: true,
        removeComments:true
      }
    })
  ],
  mode: 'production'
}
//生产环境配置
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  //多入口
  entry:{
    index:'./src/js/index.js',
    test:'./src/js/test.js'
  },
  //如果2个js同时引入JQ,打包生成3个chunk
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseInlineTagWhitespace: true,
        removeComments:true
      }
    })
  ],
  //1.可以将node-modules中的代码单独打包成一个chunk最终输出
  //2.自动分析多入口chunk中,有没有公共的文件,如果有会单独打包成一个chunk
  optimization:{
      splitChunks:{
          chunks:'all'
      }
  },
  mode: 'production'
}
//生产环境配置
const { resolve } = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry:'./src/js/index.js'
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseInlineTagWhitespace: true,
        removeComments:true
      }
    })
  ],
  //可以将node-modules中的代码单独打包成一个chunk最终输出
  optimization:{
      splitChunks:{
          chunks:'all'
      }
  },
  mode: 'production'
}

//index.js
/*
    通过js代码,让某个文件被单独打包成一个chunk
*/
/*webpackChunkName:'test'*/    设置文件名
imoprt (/*webpackChunkName:'test'*/'./test').then(({mul,count})=>{
    //文件加载成功
    console.log(mul(2,3))
}).catch(()=>{
    //文件加载失败
})
七、lazy loading:懒加载
//懒加载:当文件需要使用时才加载
//预加载 prefetch:会使用之前,提前加载js文件   webpackPrefetch:true
//正常加载:并行加载(同一时间加载多个文件)
imoprt(/* webpackChunkName:'test',webpackPrefetch:true*/ './test').then(({mul})=>{
    console.log(mul(2,3))
})
八、PWA:渐进式网络应用开发程序(离线可访问)
//生产环境配置
const { resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const workboxWebpackPlugin = require('workbox-webpack-plugin')

//指定node环境变量
//process.env.NODE_ENV = 'development'

const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    //还需要在package.json中配置browserslist
    /*
      "browserslist": {
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ], 
        "production": [
          ">0.2%",
          "no dead",
          "no op_mini all"
        ]
      },
    */
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [
        require('postcss-preset-env')()
      ]
    }
  }
]

module.exports = {
  entry: './src/index.js',
  output: {
     filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [{
        test: /\.css$/,
        use: [...commonCssLoader]
      },
      {
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
      },
      {
        //需要在package.json中配置eslintConfig--->airbnb
        /*
          "eslintConfig": {
            "extends": "airbnb-base",
            "env":{
                "browser":true
            }
          }
        */
        test: /\.js$/,
        exclude: /node-modules/,
        enforce: 'pre', //优先语法检查
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        test: /\.js$/,
        exclude: /node-modules/,
        loader: 'babel-loader', //将高版本的js转为ES5
        options: {
          presets: [
            [
              '@babel-loader',
              {
                useBuiltIns: 'usage',
                corejs: {
                  version: 3
                },
                targets: {
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ]
        }
      },
      {
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          esModule: false,
          name: '[hash:10],[ext]',
          outputPath: 'images'
        }
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(css|less|html|js|jpg|png|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10],[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/bulit.css'
    }),
    new optimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseInlineTagWhitespace: true,
        removeComments:true
      }
    }),
    new workboxWebpackPlugin.GenerateSW({
        /**
        * 1.帮助serviceWorker快速启动
        * 2.删除旧的serviceWorker
        *
        * 生成一个serviceworker文件
        */
        clientClaim:true,
        skipWaiting:true
    })
  ],
  mode: 'production',
  devtool:'source-map' 
}
//index.js

/**
* 1.eslint不认识window,navigtor等全局变量
* 解决:需要在package.json中修改eslintConfig配置
* "evn":{
*   "browser":true
* }
* 2.必须在服务器上运行
*/

//在入口文件中注册serviceWorker
//处理兼容性问题
if('serviceWorker' in navigator){
    window.addEventListente('load',()=>{
        navigator.serviceWorker.register('/service-worker.js').then(()=>{
            console.log('serviceWorker注册成功')
        }).catch(()=>{
            console.log('serviceWorker注册失败')
        })
    })
}
九、多进程打包
//thread-loader,开启多进程打包,启动大概600ms,只有工作消耗时间较长才会开启多进程打包
//npm install thread-loader -D
{
    test:/\.js$/
    exclude:/node-modules/,
    use:[
        {
            //开启多进程打包
            loader:'thread-loader',
            options:{
                workers:2,//开启两个进程
            }
        },
        {
            loader:'babel-loader',
            options:{
                presets: [
                [
                  '@babel-loader',
                  {
                    useBuiltIns: 'usage',
                    corejs: {
                      version: 3
                    },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
        }
    ]
}
十、externals防止将某些包打包输出到bundle里面,不需要打包,直接通过CDN引入
const { resolve } = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')

module.exports={
    entry:'./js/index.js',
    output:{
        filename:'./js/[name].[contenthash:10].js',
        path:resolve(__dirname,'build')
    },
    plugins:[
        new htmlWebpackPlugin({
            template:'./src/index.html',
            minfy:{
                collapseInlineTagWhitespace: true,
                removeComments:true
            }
        })
    ],
    mode:'production',
    externals:{
        //拒绝jquery被打包进来
        jquery:'jQuery'
    }
}
十一、dll,将第三方库单独打包,直接引入
//webpack.dll.js
/* 使用dll技术,对第三方库(jQuery,vue,react...)单独打包 */

const {resolve} = require('path')
const webpack = require('webpack')

module.export = {
    entry:{
        //打包生成得name,要打包得库
        jquery:['jquery']
    },
    output:{
        filename:'[name].js',
        path:resolve(__dirname,'dll'),
        library:'[name]_[hash:10]'//打包得库,向外暴露得内容名字
    },
    plugins:[
        //打包生成一个manifest.json文件 ---》 提供jQuery得映射
        new webpack.DllPlugin({
            name:'[name]_[hash:10]',//映射库得暴露名称
            path:resolve(__dirname,'dll/manifest.json')//输出文件得目录
        })
    ],
    mode:'production'
}
//webpack.config.js
const { resolve } = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

module.exports={
    entry:'./src/index.js',
    output:{
        filename:'js/[name].[contenthash:10].js',
        path:resolve(__dirname,'build')
    },
    plugins:[
        new htmlWebpackPlugin({
            template:'./src/index.html',
            minfy:{
                collapseInlineTagWhitespace: true,
                removeComments:true
            }
        }),
        //告诉webpack那些库不打包,同时使用时得名称也在变
        new webpack.DllReferencePlugin({
            manifest:resolve(__dirname,'dll/manifest.json')
        }),
        //将第三方库单独打包,并在html自动引入
        new AddAssetHtmlWebpackPlugin({
            filepath:resolve(__dirname,'dll/jquery.js')
        })
    ],
    mode:'production'
}

性能优化总结

一、webpack性能优化
  • 开发环境性能优化
  • 生产环境性能优化
二、开发环境性能优化
  • 优化打包构建速度
    • HRM
  • 优化代码调试
    • source-map
      • 开发环境:eval-source-map || eval-cheap-module-source-map
      • 生产环境:source-map || cheap-module-source-map
三、生产环境性能优化
  • 优化打包构建速度
    • oneOf
    • babel缓存
    • 多进程打包
    • externals,
    • dll
  • 优化代码运行得性能
    • 缓存(hash || chunkhash || contenthash)
    • thee shaking(1、使用ES6模块化。2、必须在production模式下。满足这2个条件自动开启。)
    • code split
    • lazy loading
    • pwa
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容