webpack优化

优化方向

热更新

概念

/**

  • hmr: hot module replacement 热模块替换 / 模块热更新
  • 作用: 一个模块发生改变,只会重新打包这一个模块(而不是打包所有模块),极大的提升了构建速度
  • 样式文件: 可以使用hmr功能: style-loader内部实现了热更新功能
  • js文件:默认没有这个功能(会全量刷新)---> 修改js代码,只能处理非入口文件
  • html文件: 默认没有这个功能(会全量刷新)因为项目中只有一个html文件,所以我们不需要做热更新
    */

实现

因为我们在开发环境使用的是webpack-dev-server,所以不需要额外的添加代码,hot默认值为true,自动会热更新。但是对于js我们需要额外的处理。

// 在入口文件中写如下代码

if (module.hot) {
    // 一旦 module.hot为true, 说明开启了热更新功能,--》让hmr功能代码生效
    module.hot.accept('./print.js',function() {
        // 方法会监听到print.js文件的变化,一旦发生变化,其他模块不会重新打包构建
        // 会执行回调函数
        print();
    })
}

source-map

是一种提供源代码到构建后代码的一种映射技术(如果构建后代码出错了,通过映射关系,可以追踪到源代码的错误)。

  1. 配置

    /**
    * 是一种提供源代码到构建后代码的一种映射技术(如果构建后代码出错了,通过映射关系,可以追踪到源代码的错误)。
    * [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 > inline > cheap) eval-cheap-source-map:速度最快
    * 调试更友好
    * source-map
    * cheap-module-source-map
    * cheap-source-map
    *
    * ----> 开发环境使用:eval-source-map / eval-cheap-module-source-map
    * 生产环境:源代码要不要隐藏,调试要不要更友好
    * 内联会让包的体积非常大,所以生产环境一般不用内联
    *
    * 隐藏:
    * nosources-source-map (全部隐藏)
    * hidden-source-map (部分隐藏)
    * 生产环境使用:source-map / cheap-module-source-map
    * */
    devtool: 'source-map'

oneOf

  • 使用

    module:{
    rules:[
    {
    oneOf: [
    // loader
    ]
    }
    ]
    }

正常情况中我们每个文件都会被所有的loader"处理一下",但是在oneOf中的loader,他匹配到之后就不会接着执行下面的loader了,所以oneOf,**可以提升构建速度 **,当一类文件,配置了多个的情况下,可以将其他的放到oneOf外面处理

缓存

  1. babel缓存

就是要在babel下,添加 cacheDirectory: true, 个人实验没啥效果(构建速度提升不明显)

 {
   //  1. js兼容性处理: babel-loader @babel/core  @babel/preset-env
   //  问题:babel只能转换基础的语法,如promise则不能进行转换
   //  2. 全部兼容  @babel/polyfill
   //  问题:可以解决兼容性问题,但是引入了所有的兼容性代码,体积太大
   //  3. 需要做兼容性的处理就ok,按需加载---> core-js
   test: /\.js$/,
     exclude: /node_modules/,
       loader: 'babel-loader',
         options: {
           // 预设: 指示babel做怎样的兼容性处理
           presets: [[
             '@babel/preset-env',
             {
               useBuiltIns: 'usage', //按需加载
               corejs: {
                 version: 3, // 指定core-js版本
               },
               targets: {  // 指定兼容浏览器版本
                 chrome: '60',
                 firefox: '60',
                 ie: '9',
                 safari: '10',
                 edge: '17'
               }
             }
           ]],
             // 开启babel缓存,第二次构建,会读取之前的缓存
             cacheDirectory: true,
         }
 },
  1. 全局加cache ,构建速度显著提升(从2778ms-->419ms)

    module.export = {
    cache: {
    type: 'filesystem',
    allowCollectingMemory: true,
    },
    }

  2. 文件资源缓存,提升线上访问速度

    // 通过修改文件名来实现缓存的效果,输出文件全部加上[contenthash:10]

    /**

    • 缓存
    • babel缓存:cacheDirectory:true --》让代码第二次构建打包速度更快
    • 文件资源缓存
    • hash: 每次webpack构建时会生成一个唯一的hash值
    • 问题: js和css同时使用一个hash值
    • 如何重新打包,会导致所有的缓存失效
    • chunkhash: 根据chunk生成的hansh值,如果打包来源于同一个chunk,那么hash值就一样 chunkid modeid
    • contenthash: 文件内容不变,hash值不变 ---》让代码线上运行缓存更好使用
    • */
      例如:
      output: {
      filename: 'budle.[contenthash:10].js',
      path: resolve(__dirname,'build')
      },

tree sharking

去掉没有用的代码,只留下引用了的代码,减小代码体积。

/**
 *  tree shaking 去除无用代码
 *  前提: 1. 必须使用es6模块化(使用import export 导入导出)  2. 开启production环境
 *  在package.json中配置
 *   "sideEffects": false 所有代码都没有副作用(度可以进行tree shaking)
 *   问题: 可能会把css /  @babel/polyfill 文件干掉
 *   解决:"sideEffects": ["*.css"]
 *   在sideEffects配置不需要删除的代码
 * */ 

code split

  1. 多入口

适合多页面开发

entry: {
  main: './src/index.js',
  test: './src/print.js'
}
output: {
  filename: '[name].[contenthash:10].js', // 这里的【name】,在入口文件中配置了, main test将成为文件名
  path: resolve(__dirname,'build');
}
  1. chunk

    // 将node_modules中代码单独打包成一个chunk最终输出
    // 自动分析多入口chunk中有没有公共的文件,如果有会打包成单独一个chunk
    optimization: {
    splitChunks: {
    chunks: 'all'
    }
    }

  2. import

注意:!!! 如果你配置了eslint,可能会报错,因为eslint不能够解析import动态引入的语法,不报错的话就不需要使用babel-eslint。

// 通过js代码,让某个文件单独被打包成一个chunk
// import动态导入
import(/* webpackChunkName: 'test' */'./print').then(({print,mul}) => {
  console.log(3, 6);
}).catch(e) {
  console.log("文件加载失败",e)
}

下载
npm i -D babel-eslint

在package.json文件中加入parser配置

 "eslintConfig": {
    "extends": "airbnb-base",
    "parser": "babel-eslint",
    "parserOptions": {
      "sourceType": "module", 
      "allowImportExportEverywhere": true 
    }
  },

懒加载

  1. 将import动态引入放入异步函数中

    document.querySelector('#btn').onclick = function() {
    import(/webpackChunkName: 'test'/'./print')
    .then(({mul}) => {
    console.log(mul(4, 5));
    })
    .catch((err) => {
    console.log(err)
    })
    }

预加载

document.querySelector('#btn').onclick = function() {
// 懒加载: 当文件需要使用才加载
// prefetch: 预加载,提前加载js文件  等其他资源加载完毕,浏览器空闲了,在偷偷加载资源
// 正常加载:可以认为是并行加载(同一时间加载多个js文件)
import(/* webpackChunkName: 'test',webpackPrefetch: true */'./print')
.then(({ mul }) => {
  console.log(mul(3, 9));
})
.catch((e) => {
    console.log('文件加载失败',e);
})
}

PWA

  1. 下载插件

npm i -D workbox-webpack-plugin

  1. 代码 webpack.config.js中

    /**

    • PWA: 渐进式网络开发应用程序(离线可访问)
    • workbox --> workbox-webpack-plugin
      */
      const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
      plugins: [
      // 生成一个serviceworker配置文件
      new WorkboxWebpackPlugin.GenerateSW({
      clientsClaim: true, // 帮助serviceworker快速启动
      skipWaiting: true, // 删除旧的 serviceworker
      })
      ]
  2. 入口文件

    // 注册serviceworker
    // 处理兼容性问题
    // eslint不认识window navigator全局变量
    // 解决: 修改package.json中的eslintConfig配置
    // "env": {
    // "browser": true
    // }
    // servicework代码必须构建在服务器上
    if ('serviceWorker' in navigator) {
    window.addEventListener('load',() => {
    navigator.serviceWorker.register('/service-worker.js')
    .then(() => {
    console.log('serviceWorker注册成功')
    })
    .catch(() => {
    console.log('serviceWorker注册失败~')
    })
    })
    }

  3. package.json文件中

    "eslintConfig": {
    ...
    "env": {
    "browser": true
    }
    },

多进程开发

  1. 下载

npm i thread-loader -D

  1. 代码

    /**

    • 开启多进程打包
    • 进程启动大概600ms,进程通信也有开销
    • 只有工作消耗时间比较长,才需要多进程打包
    • 一般用来处理js文件消耗比较大的时候使用
    • */
      {
      loader: 'thread-loader',
      options: {
      workers: 3,
      }
      },

externals

让一些额外的包,不打包到build.js中

externals: {
    jquery: '$', // 外部可以通过$直接访问,不需要引入了
},

index.html中引入jquery文件

 <script
    src="https://code.jquery.com/jquery-3.1.0.js"
    integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
    crossorigin="anonymous"
   ></script>

dll

  1. 根目录下创建webpack.dll.js文件

    /**

    • 使用dll技术,对某些库(第三方库: jquery, react, vue...)进行单独打包
    • 当运行webpack时默认查找webpack.config.js配置文件
    • 需求: 我们需要运行webpack.dll.js文件 webpack --config ./webpack.dll.js
    • */
      const { resolve } = require('path');
      const webapck = require('webpack');

    module.exports = {
    entry: {
    // 最终打包生成的[name] ---> jquery
    jquery: ['jquery']
    },
    output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash:10]', //打包的库里面向外暴露出去的内容名字

     },
     plugins: [
         // 打包生成一个 mainfest.json ---> 提供和jquery映射关系
        new webapck.DllPlugin({
          name: '[name]_[hash:10]', // 映射库暴露的内容名称
          path: resolve(__dirname, 'dll/mainfest.json') // 输出文件路径
        })
     ],
     mode: 'production'
    

    }

  2. 执行该文件

webpack -c ./webpack.dll.js -c是 --config缩写

  1. 下载包

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

  1. 在webpack.config.js中加入如下代码

    plugins: [
    // 告诉webpack那些库不参与打包,同时使用时的名称也得改变
    new webpack.DllReferencePlugin({
    manifest: resolve(__dirname,'dll/mainfest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
    filepath: resolve(__dirname, 'dll/jquery.js'),
    outputPath: 'auto', // 生成的index.html文件中多了一层auto目录
    }),
    ]

相关推荐
Myli_ing40 分钟前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
dr李四维1 小时前
iOS构建版本以及Hbuilder打iOS的ipa包全流程
前端·笔记·ios·产品运营·产品经理·xcode
雯0609~1 小时前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ1 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z1 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
彭世瑜2 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4042 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish2 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five2 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序2 小时前
vue3 封装request请求
java·前端·typescript·vue