webpack优化环境配置

Posted ~往无前

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack优化环境配置相关的知识,希望对你有一定的参考价值。

一、webpack性能优化概述

  • 开发环境性能优化
  • 生产环境性能优化

1.1开发环境性能优化

  • 优化打包构建速度
    • HMR
  • 优化代码调试
    • source-map

1.2生产环境性能优化

  • 优化打包构建速度
    • oneOf
    • babel缓存
    • 多进程打包
    • externals
    • dll
  • 优化代码运行的性能
    • 缓存(hash-chunkhash-content)
    • tree shaking
    • code split
    • 懒加载/预加载
    • pwa

二 、开发环境webpack性能优化

2.1 优化打包构建速度

  • HMR
    使用devServer绑定端口,时刻进行监听,如果代码中的内容发生了变化就立马做出响应。
    HMR:hot module replacement 热模块替换/模块热替换
    作用:一个模块发生变化,只会重新打包这一个模块,(而不是打包所有的模块)
    极大提升构建速度。
    样式文件:可以使用HMR功能:因为style-loader内部实现了
    js文件:默认不能使用HMR功能–需要修改js代码,添加支持HMR功能的代码,比如下面对print.js文件的修改,可以使得该文件具有热部署的功能。

    注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。入口文件是无法实现HMR功能的。
    html文件:默认不能使用HMR功能,同时会导致html代码不能热更新(不用做HMR功能)
    解决:修改entry入口,将html文件引入

1.安装

	npm install wepbakc-dev-server -D

2 .配置

 devServer:
        contentBase:resolve(__dirname,"build"),
        compress:true,
        port:3000,
        open:true,
        //开启HMR功能
        //当修改了webpck配置,新配置要想生效,必须重新webpack服务
        hot:true,
    ,

3 .修改js代码使其具有热部署的功能

在index.js文件中加入:

if(module.hot)
    module.hot.accept("./print.js",function()
        //方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建
        //会执行后面的回到函数
        console.log(p(1,4));

    )

2.2优化代码调试

  • source-map

当 webpack 打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代码中的原始位置。例如,如果将三个源文件(a.js, b.jsc.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。

为了更容易地追踪 error 和 warning,javascript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。简单说,Souce map就是一个信息文件,里面存储这位置信息,也就说,转换后代码的没有给位置,所对应的转换前的位置。
有了它,出错的时候,出错功能据将直接显示原始代码,而不是转换后的代码。

  • 文件的配置
 devtool:'hidden-source-map',

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

   [inline-|hideen-|eval-][nosources-][cheap-[module-]]source-map
        source-map  外部    错误代码准确信息和源代码的错误位置
        inline-source-map       内联     错误代码准确信息和源代码的错误位置
        hidden-source-map       外部     错误代码错误原因,但是没有错误位置 不能追踪源代码错误,只能提示到构建后代码的位置
        eval-source-map         内联     错误代码准确信息和源代码的错误位置
        nosources-source-map:外部        错误代码准确信息和源代码的错误位置,但是没有和源代码信息
        cheap-source-map:外部           错误代码准确信息和源代码的错误位置  只能精确的行
        cheap-module-resource-map:外部   错误代码准确信息和源代码的错误位置   module会将loader的source map加入

        和外部的区别就是:1.外部生成了文件,内敛是没有的  2.内联构建速度更快 
        
        开发环境:速度快,调试更友好
            速度快:(eval>inline>cheap...)
            eval-cheap-source-map
            sval-source-map
            调试更友好
            cheap-module-source-map
            调试友好               性能更好
            -->eval-source-amp  /eval-cheap-module-source-map
        生产环境:源代码要不要隐藏?调试要必要更友好
            内联会让代码体积变大
        nosources-source-map        隐藏全部代码
        hidden-source-map           只隐藏源代码,会提示构建后代码错误信息

三、生产环境性能优化

3.1优化打包构建速度

  • oneOf

1.我们希望是在loader处理的时候,可以对一种类型的文件,只进行一次的匹配,而不是多次的匹配,这时候就需要使用oneOf来实现这个功能。\\

2.文件配置

代码复用:
 const commonCssLoader=[
    MiniCssExtractPlugin.loader,
    'css-loader',
    
        //还需在package.json中定义browserslist
        loader:'postcss-loader',
        options:
            ident:'postcss',
            plugins:()=>
                require("postcss-preset-env")()
            
        
    ,
]
rules:[
            
                test:/\\.js$/,
                exclue:/node_modules/,
                //优先执行
                enfore:'pre',
                loader:'eslint-loader',
                options:
                    fix:true,
                
            ,
            
                //以下loader只会匹配一个
                //注意:不能有两个配置处理同一种类型文件
                oneOf:[           
                
                    test:/\\.css$/,
                    use:[...commonCssLoader]
                ,
                
                    test:/\\.less$/,
                    use:[...commonCssLoader,'less-loader',]
                ,
                
                    test:/\\.js$/,
                    exclue:/node_modules/,
                    loader:'babel-loader',
                    options:
                        presets:[
                            [
                            '@babel/preset-env',
                            
                                useBuiltIns:'usage',
                                corejs:version:3,
                                targets:
                                    chrome:'60',
                                    firefox:'50'
                                
                            
                            ]
                        ],
                    
                ,
                
                    test:/\\.(jpg|png|gif)/,
                    loader:'url-loader',
                    options:
                        limit:8*1024,
                        name:'[hash:10].[ext]',
                        outputPath:'imgs',
                        esModule:false,
                    
                ,
                
                    test:/\\.html$/,
                    loader:'html-loader'
                ,
                
                    exclude:/\\.(js|css|less|html|jpg|png|gif)/,
                    loader:'file-loader',
                    options:
                        outputPath:'media'
                
                
            ]
        
        ],
  • babel缓存
    cacheDirectory:true 让第二次打包构建速度更快

文件资源缓存:
hash:每次webpack构件时会生成一个唯一的hash值。问题:js和css同时使用一个hash值。
如果重新打包,会导致所有缓存失效。 (可能我却只改动一个文件)
chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样
问题:js和css的hash值还是一样的 因为css是在js中被引入的,所以同属于一个chunk 同一个起始文件是同一个hash值。
contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样,所以当一个文件发生改变的时候,就会因为hash值跟上一次的不一样,发起一次新的请求。
而对于没有发生改变的文件,hash值不变,所以直接来自于缓存 —>让代码上线运行缓存更好使用。

                
                    test:/\\.js$/,
                    exclude:/node_modules/,
                    loader:'babel-loader',
                    options:
                        presets:[
                            [
                            '@babel/preset-env',
                            
                                useBuiltIns:'usage',
                                corejs:version:3,
                                targets:
                                    chrome:'60',
                                    firefox:'50'
                                
                            
                            ],
                    
                        ],
                         //开启babel缓存
                        //第二次构建时,会读取之前的缓存
                        cacheDirectory:true
                    
                ,
  • 多进程打包

1.安装

	npm  install thread-loader

2.webpack配置如下:


//开启多进程打包,一般用在babel-loader后面
module: 
    rules: [
        
            test: /\\.js$/,
            use: [
                loader: 'thread-loader',
                options: 
                    workers: 2
                
            ,
            'babel-loader']
        
    ]


但是多进程也有一定的问题:就是当开启一个进程的时候,也会耗费一定的时间,一般是600ms,所以如果项目的打包过程不是很耗时,没必要使用多进程打包。

  • externals

作用:

防止将某一些包打包到我们最终的bundle里面从而导致包体积变大。

使用场景:

query我们希望通过csdn链接引入使用,不希望打包到最后的bundle里面,为了避免打包的时候将jquery最终也给打包了,可以配置externals,禁止将jquery打包。

文件配置:

//webpack.config.js
externals: 
    // 忽略库名 --- npm包名
    jquery: 'jQuery'

  • dll

dll是什么?

类似于externals,指使哪些库是不需要打包的。和externals区别在于 dll 会单独的对某些库进行单独打包,将多个库打包成一个chunk。

那么它有什么意义呢?

正产情况下一个项目下 node_modules 里面的包会打包成一个chunk,但是第三方库很大,打包成一个chunk文件体积过大。所以通过 dll 将所有库进行单独打包成不同的chunk,更加有利于我们的性能优化,提高构建速度。

应该如何配置呢?

  1. 先新建一个 webpack.dll.js 文件:配置专门打包某些第三方库(jquery、react、vue 等等),将来构建就不用重复打包了 (重复打包性能差,构建速度会慢)

webpack.dll.js文件配置:

/*
使用dll技术,对某些库(第三方库:jquery,react,vue...)进行单独打包
    当运行webpack时,默认查找webpack.config.js配置文件
    需求:需要运行webpack.dll.js文件
    -->webpack --config webpack.dll.js

*/
const resolve=require('path');
const webpack=require('webpack');
// const webpackConfig = require('../18.HMR copy/webpack.config');
module.exports=
    entry:
        //最终打包生成的[name]-->jquery
        //['jquery]-->要打包的库是jquery
        jquery:['jquery']
    ,
    output:
        filename:'[name].js',
        path:resolve(__dirname,'dll'),
        library:'[name]_[hash]',//打包的库里面向外暴露出去的内容叫什么名字
    ,
    plugins:[
        //打包生成一个manifest.json--->提供和jquery映射
        new webpack.DllPlugin(
            name:'[name]_[hash]',//映射库的暴露的名称
            path:resolve(__dirname,'dll/manifest.json')//输出文件路径
        )
    ],
    mode:'production'

2.webpack文件配置:

    plugins:[
        new HtmlWebpackPlugin(
            template:'./src/index.html'
        ),
        //告诉webpack那些库不参与打包,同时使用时的名称也得变
        new webpack.DllReferencePlugin(
            manifest:resolve(__dirname,'dll/manifest.json')
        ),
        //但是这时候打包出去的文件中并不包含jquery所以使用下面的插件,将提前打包好的库自动引入html中
        //将某个文件打包输出去,并在html中自动引入该资源
        new AddAssetHtmlWebpackPlugin(
            filepath:resolve(__dirname,'dll/jquery.js')
        )
    ],

3.2 优化代码运行的性能

  • tree shaking
    tree-shaking可以理解为通过工具"摇"我们的JS文件,将其中用不到的代码"摇"掉,是一个性能优化的范畴。具体来说,在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

在使用tree shaking之前当然也是有前提的:
1.必须使用ES6模块化
2.开启production
使用ES6模块的原因有以下几点原因:
ES6 module 特点:

  • 只能作为模块顶层的语句出现
  • import 的模块名只能是字符串常量
  • import binding 是 immutable的

ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。

所谓静态分析就是不执行代码,从字面量上对代码进行分析,ES6之前的模块化,比如我们可以动态require一个模块,只有执行后才知道引用的什么模块,这个就不能通过静态分析去做优化。

这是 ES6 modules 在设计时的一个重要考量,也是为什么没有直接采用 CommonJS,正是基于这个基础上,才使得 tree-shaking 成为可能,这也是为什么 rollup 和 webpack 2 都要用 ES6 module syntax 才能 tree-shaking。

文件配置
通过package.josn的sideEffects 属性来实现这种方式:


	“sideEffects”:false	

问题:所有的导入文件都会的收到tree-shaking的影响。这意味着,如果在项目中使用类似 css-loader并import一个css文件,则需要将其添加到side effect列表中,或者是polyfill,这种只在执行的时候作用的导入模块,所以很可能会把css /@babel/polyfill文件干掉。解决方法如下:


	“sideEffects”:["./src/some-side-effectful-file.js", "*.css"]

  • code split
    关于代码分割:我们经常在自己的项目中导入第三方包,这样如果加载的第三包比较多的时候,这会使得打包的构建速度变慢,解决方法就是进行代码分割。在多入口文件下,更加能体现出来优势,因为有多个入口文件,每个入口文件都会构建,如果其中导入了第三方包,那么每个文件都得引入,就会导致构建的效率降低。
    解决方法:通过下面的配置,可以使得第三方包可以单独构建。
    webpack.config.json文件:
  optimization:
        splitChunks:
            chunks:'all'
        
    ,

但是通过上面这种直接的方式,会使得导出第三方文件的文件名是一个hash值,不利于阅读,这时候我们通过chunkFilename配合魔法注释,可以修改默认的文件名。

index.js文件:

/*
项目中webpack会自动识别出import这种语法,从而进行代码分割。
import之后会返回一个promise对象。魔法注释 webpackChunkName
  通过js代码,让某个文件被单独打包成一个chunk
  import动态导入语法:能将某个文件单独打包
*/
import(/*webpackChunkName:'test'*/'./test')
    .then((mul,count)=>
      //文件加载成功
      //eslint-disable-next-line
      console.log(mul(2,5));
      //eslint-disable-next-line
      console.log(count(4,1));
    )
    .catch(()=>
      console.log("文件加载失败");
    )
import(/*webpackChunkName:'jquery'*/'jquery')

webpack.config.js配置文件

output:
        //配置属性chunkFilename配合魔法注释,可以修改默认的文件名
        filename:'js/built.[contenthash:10].js',
        path:resolve(__dirname,'build'),
        //这里的[name]使用的上面的魔法注释
        chunkFilename:'js/[name].[hash:8].js'
    ,
 optimization:
        splitChunks:
            chunks:'all'
        
    ,
  • 懒加载

懒加载就是,我不需要直接将js代码加载,而是在我需要的时候在加载所需要的内容。

加载:是指已经是否被浏览器所加载。
先进行代码分割,然后再进行懒加载。
懒加载:当文件需要使用时才加载。
预加载:webpackPrefectch:会在使用之前,提前加载js文件。当使用的时候直接显示,会有兼容性问题,慎用。

真正的加载可以认为做并行加载(同一时间加载多个个文件)
预加载prefetch:等其他资源加载完毕,等浏览器空闲了,在偷偷加载资源,这种使用与当文件比较大的时候。
如果使用懒加载,那么会由于文件太大无法很快地显示给用户。

下面举个实例:
实例:

//这里给元素添加一个点击事件,当用户端架的时候会触发这部分js代码
document.getElementById('btn').onclick = function () 
    // 懒加载
    import(/* webpackChunkName: 'test' */"./test.js")
        .then((multi) => 
            console.log(multi)
            console.log('multi=', multi(3, 3))
        ).catch(() => 
            console.log('test文件加载失败!')
        )

//在import语法中添加一个参数 webpackPrefetch表示预加载
document.getElementById("btn").onclick=function()
	import(/*webpackChunkName:'test',webpackPrefetch:true*/"./test").then((mul)=>
    console.log(mul(4,5));
  )

以上是关于webpack优化环境配置的主要内容,如果未能解决你的问题,请参考以下文章

webpack项目优化,压缩代码,去除冗余样式

[万字逐步详解]使用 webpack 打包 vue 项目(优化生产环境)

webpack优化配置

webpack优化配置

webpack自定义配置的方法及webpack-merge优化

webpack开发与生产环境 性能优化配置 - HMR - 缓存 -tree shaking - 代码分割 - 懒加载 - 预加载 - PWA - 多进程打包 - externals - dll(代码