图解Webpack——优化篇

简介: 图解Webpack——优化篇

首先用一张利用MindMaster绘制思维导图开篇来阐述本文的主要内容,读者可在此基础上进行扩展自己的思维导图。webpack除了基础的配置外,还需要进行优化。对于开发环境主要优化打包构建速度和代码调试;对于生产环境主要优化打包的构建速度和代码运行的性能

640.jpg一、开发环境


640.png1.1 构建速度


在开发环境中为了提升代码构建速度,可以利用HMR(模块热替换)来实现,通过该功能可以允许只变更更新内容,而不需将所有内容进行构建,极大提升构建速度。


640.png

 1. 开启

module.exports = {
    // ...
    devServer: {
        hot: true,
    }
}


  1. 样式文件


对于样式文件,借助于style-loader即可实现模块热替换,这是因为此loader使用了module.hot.accept,在样式改变后就可以热更新到style标签中。


  1. js文件


对于js文件默认不能使用HMR功能,为了让js文件支持该功能,可以借助于module.hot.accept这个接口,当监听的模块更新后,触发一个回调函数对更新做出响应。(注意:只能处理非入口js文件,因为入口js文件会引入其它文件,重新引入导致重新加载)。


// 判断是否启动热更新
if (module.hot) {
  module.hot.accept('./js/add.js', () => {
    console.log(add(1, 2));
  });
}


1.2 代码调试

640.png

1.2.1 devServer


devServer主要目的是实现自动化(自动编译、自动打开浏览器、自动刷新浏览器……)。为了实现该功能,可以借助webpack-dev-server,通过其可以构建一个小型的Web服务器,能够实时重新加载。(注意:利用webpack-dev-server启动的项目只会在内存中编译打包,不会有任何输出,这是因为访问内存中的代码比访问文件系统化中的文件更快)


module.exports = {
    // ...
    devServer: {
        // 启动gzip压缩
        compress: true,
        port: 3000,
        open: true,
    }
}


1.2.2 source map


利用source map可以将编译后的代码映射回原始源代码,从而更容易地追踪error和warning。对于如何生成source map,利用devtool选项,其选项值可以是:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map,每个值具体含义可参照官网。


  1. 开发环境要求构建速度快、调试更友好。构建速度快,肯定采用内联方式(eval和inline);为了使调试更友好,则需要定位到代码位置,可以使用source-map、cheap-module-source-map、cheap-source-map。综上所述可以使用eval-source-map 或 eval-cheap-module-source-map。


  1. 因为内联方式会让代码体积变大,所以在生产环境下不用内联方式。在生产环境条件下,需要考虑两点:源代码是否需要隐藏、调试如何更友好。若隐藏代码可以使用nosource-source-map和hidden-source-map;为了调试更友好,可采用source-map和cheap-module-source-map。


module.exports = {
    // ...
    devtool: 'eval-source-map'
}


二、生产环境


640.png

2.1 打包构建速度

640.png

2.1.1 oneOf


每个文件对于rules中的所有规则都会遍历一遍,如果使用oneOf就可以解决该问题,只要能匹配一个即可退出。(注意:在oneOf中不能两个配置处理同一种类型文件)


module.exports = {
    // ...
    module: {
        rules: [
            {
                oneOf: [
                    // ...
                ]
            }
        ]
    }
}


2.1.2 babel缓存


Babel在转义js文件过程中消耗性能较高,将babel-loader执行的结果缓存起来,当重新打包构建时会尝试读取缓存,从而提高打包构建速度、降低消耗


module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    // ...
                    // 开启babel缓存。第二次构建时,会读取之前的缓存
                    cacheDirectory: true
                }
            }
        ]
    }
}


2.1.3 多进程打包


webpack构建过程中需要大量文件进行解析和处理,所以构建时文件读写和计算密集型的操作,而运行在Node.js之上的Webpack是单线程模型,所以构建起来速度会比较慢,此时为了提升构建速度,可以选择发挥多核CPU电脑的功能,利用多进程来提升构建速度。可以利用thread-loader来进行多进程打包,将该loader放置在其他loader之前, 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行(注意:每个 worker 都是一个单独的有 600ms 限制的 node.js 进程,只有工作消耗时间比较长才需要多进程打包)。


module.exports = {
    // ...
    module: {
        rules: [
            {
                // ...
                use: [
                    {
                        loader: 'thread-loader',
                        options: {
                            workers: 2 //两个进程
                        }
                    },
                    // ...
                ]
            }
        ]
    }
}


2.1.4 externals

externals告诉在Webpack要构建的代码中使用了哪些不用被打包的模块,也就是说在运行时再去从外部获取这些扩展依赖,这样就会减少打包的内容,从而减少打包时间并减小包的体积。例如直接通过script标签从CDN中引入jQuery,而不是将它打包。

module.exports = {
    externals: {
        // 拒绝jQuery被打包进来
        jquery: 'jQuery'
    }
}

2.1.5 DLL

DLL即动态链接库,使用该技术能够对某些库(react、vue、jquery……)进行单独打包,通过单独打包后,后续可直接引用,不需要再次进行打包(只需要打包一次),极大提升构建速度。在这个过程中主要涉及到三个步骤:

  1. 新建webpack.dll.js文件,用来进行单独打包生成动态链接库文件和mainfest.json,这样在以后构建的时候就不用重复打包了,可直接引用。(该文件中用到DllPlugin插件,用于生成mainfest.json文件,提供一个映射关系)
const path = require('path');
const webpack = require('webpack');
module.exports = {
    entry: {
        jquery: ['jquery']
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dll'),
        library: '[name]_[hash]'// 打包的库里面向外暴露出去的内容叫什么名字
    },
    plugins: [
        // 打包生成一个mainfest.json,从而提供和对应包的映射
        new webpack.DllPlugin({
            name: '[name]_[hash]',// 映射库的暴露的内容名称
            path: path.resolve(__dirname, 'dll/mainfest.json'),// 输出文件路径
        })
    ],
    mode: 'production'
};
  1. 在webpack.config.js文件中配置webpack.DllReferencePlugin,从而在主文件中引入打包好的动态链接库文件,从而让主文件知道哪些文件不需要打包,打包库里的名字是什么……
module.exports = {
    // ...
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: resolve(__dirname, 'dll/mainfest.json')
        }),
    ]
}
  1. 由于动态链接库文件并没有被引入进html文件中,利用add-asset-html-webpack-plugin插件即可将该链接库文件在html中自动引入。
module.exports = {
    // ...
    plugins: [
        new AddAssetHtmlWebpackPlugin({
            filepath: resolve(__dirname, 'dll/jquery.js')
        })
    ]
}


2.2 代码运行性能

640.png


2.2.1 缓存


获取资源是比较耗费时间的,利用缓存可以降低网络流量,使网站加载速度更快。由于强缓存会存在内容不能及时更新的问题,为了解决该问题,则需要为webpack配置的文件名加上hash值。对于webpack中hash值主要有三种:hash、chunkhash、contenthash。由于缓存希望一个文件改动只会影响该文件的缓存,其余文件缓存不失效,所以该hash值应该选择根据文件内容生成的hash值contenthash,即文件命名中添加contenthash。

hash值类型 特点
hash 每次webpack构建时生成一个唯一的hash值
chunkhash 根据chunk生成hash值,来源于同一个chunk,则hash值就一样
contenthash 根据内容生成hash值,文件内容相同hash值就相同


2.2.2 tree shaking


通常用于描述移除 JavaScript上下文中的未引用代码(dead-code),让代码体积变的更小。为了实现该功能,需要两个前提条件:一是必须使用ES6模块,二是开启production环境。(注意:为了放置将可能有副作用的文件(例如:css文件)删除掉,则需要将其添加到package.json文件中的sideEffects的配置中(例如:"sideEffects":["*.css"]))。


2.2.3 代码分割


代码分割就是把打包输出的代码分离到不同的bundle中,然后可以按需加载或并行加载这些文件。常用的方法主要有三种:入口起点、防止重复、动态导入


  1. 入口起点
  2. 利用单入口和多入口(对象形式)的方式进行拆分,有一个入口就会输出一个bundle。对于这种方式存在重复引用的弊端,为了解决该问题需要利用SplitChunksPlugin插件,即下面的防止重复的方法。
  3. 防止重复
    利用SplitChunksPlugin可以进行公共代码提取,提取主要包括两类:一类是可以将来自node_modules文件夹下的模块单独打包为一个chunk输出;另一类是对于多入口,会自动分析多入口chunk中公共文件(该文件大于30kb才会单独打包)并单独打包为一个chunk。


module.exports = {
    // ...
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    }
}
  1. 动态导入
    利用import()语法能够实现动态导入,从而将某个文件单独打包。


import(/* webpackChunkName: 'add' */'./js/add')
    .then(({ add }) => {
        console.log(add(1, 2));
    })
    .catch(() => {})


2.2.4 懒加载


懒加载是一种很好的优化方式,当代码被需要时才会进行下载,这样就加快了应用的初始加载速度并减轻了代码的总体体积(因为有些代码永远不会被加载)。实现方式就是在异步代码中使用import()语法引入文件,这样就起到懒加载的作用。

document.getElementById('btn').addEventListener('click', () => {
    import(/*webpackChunkName: 'test'*/./test)
        .then(() => {})
        .catch(() => {})
})


2.2.5 预加载


预加载主要用于加载未来可能使用的文件,利用预加载可以在使用之前提前加载文件。对于预加载,其实是chunk完成加载后浏览器空闲了再加载资源。通过在import()语法中添加webpackPrefetch:true来实现预加载


import(/*webpackChunkName: 'test',webpackPrefetch: true*/./test)
        .then(() => {})
        .catch(() => {})
相关文章
|
29天前
|
JSON JavaScript 前端开发
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)(下)
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
58 2
|
2天前
|
缓存 JavaScript 前端开发
探讨如何通过一系列优化策略来提升TypeScript与Webpack的构建性能。
【6月更文挑战第11天】本文探讨了优化TypeScript与Webpack构建性能的策略。理解Webpack的解析、构建和生成阶段是关键。优化包括:调整tsconfig.json(如关闭不必要的类型检查)和webpack.config.js选项,启用Webpack缓存,实现增量构建,代码拆分和懒加载。通过这些方法,可以提升构建速度,提高开发效率。
28 0
|
29天前
|
JSON 前端开发 JavaScript
webpack学习笔记--优化
webpack学习笔记--优化
|
29天前
|
缓存 JavaScript 前端开发
【TypeScript技术专栏】TypeScript与Webpack构建优化
【4月更文挑战第30天】本文探讨了优化TypeScript与Webpack构建性能的策略。理解Webpack的解析、构建和生成阶段是关键。优化包括:调整tsconfig.json(关闭不必要的类型检查,适配目标环境)和webpack.config.js(配置entry、output、resolve,使用压缩插件)。启用Webpack缓存和增量构建,利用代码拆分与懒加载,能有效提升构建速度和开发效率。
|
29天前
|
前端开发 JavaScript 开发者
深入理解前端性能优化中的Webpack Tree Shaking
【2月更文挑战第2天】在前端开发中,性能优化一直是开发者们不断追求的目标之一。而在这个过程中,Webpack Tree Shaking 技术作为一种重要的优化手段,对于减小前端应用的体积、提高加载速度起到了至关重要的作用。本文将深入探讨Webpack Tree Shaking 技术的原理和实现方式,帮助读者更好地理解并运用这一技术来优化前端应用性能。
|
29天前
|
缓存 前端开发 算法
Webpack 进阶:深入理解其工作原理与优化策略
Webpack 进阶:深入理解其工作原理与优化策略
66 2
|
29天前
|
缓存 前端开发 JavaScript
|
10月前
|
缓存 前端开发 JavaScript
如何用webpack来优化前端性能?
如何用webpack来优化前端性能?
75 0
|
29天前
|
JSON 前端开发 JavaScript
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
49 0
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
|
29天前
|
缓存 监控 前端开发
说说如何借助webpack来优化前端性能?
说说如何借助webpack来优化前端性能
116 1