webpack 4 基础

作者: 輪徊傷 | 来源:发表于2021-07-20 22:10 被阅读0次

全局下载webpack

命令安装:npm install webpack webpack-cli -g
webpack -v
webpack-cli -v

webpack面试题

1、webpack是什么?

官网的定义是:本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具 。
通俗点讲:webpack 是一个模块打包工具( webpack 会将模块、依赖和各种资源打包,生成静态资源文件。)

2、webpack有什么作用?

把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。

4、webpack 打包流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
确定入口:根据配置中的 entry 找出所有的入口文件
编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

4、webpack打包原理

首先,我们需要读到入口文件里的内容(也就是index.js的内容)
分析入口文件,递归的去读取模块所依赖的文件内容,生成AST语法树。
根据AST语法树,生成浏览器能够运行的代码

说下用到的几个babel包:

\color{red}{@babel/parser}:用于将源码生成AST
\color{red}{@babel/traverse}:对AST节点进行递归遍历
\color{red}{babel-core/@babel/preset-env}:将获得的ES6的AST转化成ES5

parser.js中主要就三个方法:

\color{red}{getAST}: 将获取到的模块内容 解析成AST语法树
\color{red}{getDependencies}:遍历AST,将用到的依赖收集起来
\color{red}{transform}:把获得的ES6的AST转化成ES5

如果说 Loader 负责文件转换,那么 Plugin 便是负责功能扩展。

webpack 五个核心概念

一、入口( entry )

入口(Entry)指示 webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。

二、输出( output )

输出( output )指示 webpack打包后的资源 bundles 输出到哪里去,以及如何命名。

三、loader 加载器(解释器)

loader 让 webpack能够去处理那些非 JavaScript 文件( webpack自身只理解JavaScript )

四、插件( plugins )

插件( plugins )可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

五、模式( mode )

模式( mode )指示 webpack使用相应模式的配置。
1. mode: 'development' 开发环境
2. mode: 'production' 生产环境

image.png

loader

loader 执行顺序,从右到左,从下到上依次解析
常用的loader:

  • 1、style-loader :创建 style 标签,将css样式以style标签的形式插入到html中
  • 2、css-loader:用于解析css文件
  • 3、less-loader :将 less 文件解析成 css 文件
  • 4、url-loader :处理 css 文件中的图片资源(缺点:默认处理不了 html 中的 img 图片(所以我们要使用 html-loader ))
  • 5、html-loader :处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
  • 6、file-loader :打包其他资源(除 html/css/js/图片 资源以外的资源)
  • 7、postcss-loader : 用postcss来处理CSS
  • 8、babel-loader : JS兼容性处理 ,将es6语法转为es5
  • 9、thread-loader:多进程打包

plugins

常用的 plugins:

  • 1、html-webpack-plugin:用于生成一个html文件,并将最终生成的js,css以及一些静态资源文件以script和link的形式动态插入其中
  • 2、mini-css-extract-plugin:会将你项目中的css都单独打包,不会内嵌到js bunlde中,这样css和js即可并行加载,而代价就是额外的http请求。
  • 3、optimize-css-assets-webpack-plugin:压缩css文件
  • 4、eslint-webpack-plugin:JS语法检查(eslint-loader已被官方弃用,现5在使用 eslint-webpack-plugin 插件
  • 5、imagemin-webapack-plugin:顾名思义就是对项目中的图片进行压缩
  • 6、compression-webpack-plugin:生产环境时可选择让代码压缩gzip
  • 7、terser-webpack-plugin:对js代码压缩(改插件可以开启缓存,开启多线程打包,开启sourceMap,去除console.log等等)
  • 8、webpack-bundle-analyzer: 可视化显示打包后文件中个文件的大小
  • 9、speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。
  • 10、size-plugin:每次 打包后,size-plugin 都会输出本次构建的资源体积(gzip 过后),以及上次构建相比体积变化了多少。可以帮助我们监控资源体积的变化。

devServer

devServer基本配置
开发服务器 devServer:用来自动化(自动编译,自动打开游览器,自动刷新游览器)
下载 npm i webpack-dev-server -D
特点:只会在内存中编译打包,不会有任何输出
启动 devServer 指令为:npx webpack serve

    devServer: {
        // 项目构建后的路径
        contentBase: resolve(__dirname,"build/index.html"),
        // 启动 gzip 压缩
        compress: true,
        // 端口号
        port: 3000,
        // 自动打开游览器
        open: true,
        // 开启 HMR 功能,新配置要想生效,必须重启 webpack 服务
        hot: true,
    }

source-map

  • source-map 一种 提供源代码到构建后代码映射 技术(如果构建后代码出错了,通过映射可以追踪源代码的错误)
/**
 * source-map 一种 提供源代码到构建后代码映射 技术(如果构建后代码出错了,通过映射可以追踪源代码的错误)
 * 
 * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
 * 
 * 内联 和 外部 的区别:1.外部生成了文件,内联没有   2.内联构建速度更快
 * 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:外部
 *      提示:错误代码的准确信息 和源代码的错误位置
 * 
 * 开发环境:速度快,调试更友好。 ===》 eval-source-map
 * 生产环境:源代码要不要隐藏? 调试要不要更友好? 内联会让代码体积变大,所以在生产环境不用内联  ===》 source-map
 */
   devtool: "source-map"//和entry、output同级

resolve

    //解析模块的规则
    resolve: {
        // 配置解析模块路径别名,优点:简写路径,缺点:路径没有提示
        alias: {
            $css: resolve(__dirname,"src/css"),
        },
        //配置省略文件路径的后缀名
        extensions: [".js",".json",".css"],
        // 告诉 webpack 解析模板是去哪个目录
        modules: [resolve(__dirname,"../../node_modules"),"node_modules"]
    },

webpack性能优化

开发环境性能优化

  • 优化打包构建速度
    • HMR 热模块替换
    • gzip 压缩
  • 优化代码调试
    • source-map
 devServer: {
        // 项目构建后的路径
        contentBase: resolve(__dirname,"build/index.html"),
        // 启动 gzip 压缩
        compress: true,
        // 端口号
        port: 3000,
        // 自动打开游览器
        open: true,
        // 开启 HMR 功能,新配置要想生效,必须重启 webpack 服务
        hot: true,
    },
 devtool: "source-map"//和entry、output同级

开启热模块后,在js文件中需要判断该js模块是否需要开区HMR功能

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

生产环境性能优化

优化打包构建速度

  • babel缓存 cacheDirectory: true
    {
          test: /\.js$/,
          loader: "babel-loader",
          options: {
              开启babel缓存
              第二次构建时会读取之前的缓存
              cacheDirectory: true,
          }
     },
    
  • 多进程打包
    - thread-loader (推荐使用这个)
    - parallel-webpack(对ES6支持不好)
    - HappyPack(作者已经不维护升级了)
    thread-loader 会将您的 loader 放置在一个 worker 池里面运行,以达到多线程构建。
/ **
 * 开启多进程打包
 * 进程启动时间为600ms,进程通信也有开销。只有工作消耗时间比较长。才需要多进程打包
 */
{
   loader: "thread-loader",
   options: {
     works: 2,//进程2个 
   }
},
  • externals
    首先在webpack.config.js文件中
externals: {
        // 忽略库名 -- npm下载的 包名
        jquery: "jQuery",
}

然后在index.html中,通过script方式引入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
</body>
</html>
  • dll
这是一个经常用到的缓存策略,单独配置一个webpack配置,将公共资源打包出来,等我们正式打包发布的时候,直接引用就好了,如此一来经过我们预编译打包的模块就不需要经过webpack打包 https://www.cnblogs.com/tugenhua0707/p/9520780.html

新建一个webpack.dll.js作为我们进行资源分包的webpack配置,以打包jquery模块为例:
DllPlugin的作用:打包生成一个 mainfest.json ====》 提供和 jquery 的映射

const {resolve} = require("path");
const webpack = require("webpack");

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

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

DllReferencePlugin 这个插件是在webpack.config.js中使用的,DllReferencePlugin 的作用是: 告诉 webpack 那些库不参与打包
add-asset-html-webpack-plugin 将某个文件打包输出去。并在html中自动引入该资源

const { resolve } = require("path")
const htmlWebpackPligin = require("html-webpack-plugin")
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin")
const webpack = require("webpack")

module.exports = {
    entry: "./src/index.js",
    output: {
        filename: "built.js",
        path: resolve(__dirname,"build")
    },
    plugins: [
        // html-webpack-plugin 打包 html 资源
        // 功能:默认会创建一个空的 html,自动引入打包输出的所有资源(JS/CSS)
        new htmlWebpackPligin({
            // 复制 ./src/index.html 文件,自动引入打包输出的所有资源(JS/CSS)
            template: "./src/index.html"
        }),
        //告诉 webpack 那些库不参与打包,同时使用的名称也得变
        new webpack.DllReferencePlugin({
            manifest: resolve(__dirname,"dll/mainfest.json")
        }),
        // 使用add-asset-html-webpack-plugin 将某个文件打包输出去。并在html中自动引入该资源
        new AddAssetHtmlWebpackPlugin({
            filepath: resolve(__dirname,"dll/jquery.js")
        })
    ],
    mode: "production"
}

优化打包体积

  • 压缩css
// 功能:压缩css文件
const optimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");

plugins:[
    new optimizeCssAssetsWebpackPlugin(),
]
  • 压缩图片
const ImageminPlugin= require("image-min-plugin");
plugins: [
    new ImageminPlugin({
        test: /\.(jpe?g|png|gif|svg)$/i,
        disable: process.env.NODE_ENV !== 'production', // Disable during development
        pngquant: {
            quality: '90-100'
        },
        gifsicle: {
            optimizationLevel: 2,
            interlaced: true
        },
        optipng: {
            optimizationLevel: 4,
        },
        jpegtran: {
            progressive: true
        }
    }),
]
  • tree shaking
    将用到的方法,模块打包,不用到的方法或者模块就不打包
    原理
    根据DCE的规则去执行,当满足DCE的代码或者模块就不去打包。
    在ES6的模块机制下才有效,因为ES6的模块机制是静态的不是动态的,DCE匹配的代码会在uglify阶段被擦除,不会被打包。
    DCE规则
    • 代码不被执行,不会被到达的
    • 代码执行结果不会被用到的
    • 代码只写不读的

用法
JS–Tree shaking
在webpack4.x以上的版本中,当mode==“pdoduction”,默认开启,即生产模式自动开启,若想在开发环境开启,那么就需要.babelrc里设置modules:false即可

CSS–Tree shaking
Purgecss: 原理—遍历CSS代码、识别已经用到的CSS class,搭配mini-css-extract-plugin(此plugin能将css文件打包成单独的.css文件)

/**
 * tree shaking 去除无用代码
 * 前提: 1、必须使用ES6模块化  2、开启production环境
 * 作用:去除程序中没有使用的代码,减少代码体积
 * 
 * 在package.json中配置
 * "sideEffects": false   所有代码都没有副作用(都可以进行tree shaking)
 *      问题:可能会把css/@babel/polyfill   当做副作用文件给干掉
 *      解决办法:  sideEffects": ["*.css","*.less"]
 */
  • code split
    如果不对webpack打包进行优化,当某个模块被多个入口模块引用时,它就会被打包多次(在最终打包出来的某几个文件里,它们都会有一份相同的代码)。当项目业务越来越复杂,打包出来的代码会非常冗余,文件体积会非常庞大。大体积文件会增加编译时间,影响开发效率;如果直接上线,还会拉长请求和加载时长,影响网站体验。
    优化前,打包出来js一共有633KB。(因为a.js和index.js文件中都包含了c.js、jquery.js文件的代码)
    优化前
    优化后,打包出来js只有330KB。(因为a.js和index.js文件中都包含了c.js、jquery.js文件的代码被抽离出来了)
    优化后
optimization: {
        splitChunks: {
            chunks: "all",
            // 一下都是默认值,可以不写~~~~~~~~~~~~~~~~~~~~~~~~~~
            minSize: 30 * 1024,//分割的chunk最小为30kb
            maxSize: 0,//最大没有限制
            minChunks: 1,//要提取的 chunks 最少要被引用1次
            maxAsyncRequests: 5,//按需加载时,并行加载的文件的最大数量
            maxInitialRequests: 3,//入口JS文件最大并行请求数量
            automaticNameDelimiter: "~",//名称连接符
            cacheGroups: {//分割chunk的组
                // node_modules文件会被打包到vendors组的chunk中 ---> vendors~xxx.js
                //并且要满足上面的公共规则
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendors",
                    // 优先级
                    priority: -10,
                },
                default: {
                    name: "common",
                    //要提取的 chunks 最少要被引用2次
                    minChunks: 2,
                    // 优先级
                    priority: -20,
                    //如果当前要打包的模块。和之前己经被提取的模块是同一个,就会复用,而不是重新打包模块
                    reuseExistingChunk: true,
                }
            }
        },
        //将当前模块记录其他模块的 hash 单独打包为一个文件 runtime
        //解决:修改 optimization打包时 a文件导致b文件的contenthash变化
        runtimeChunk: {
            name: entrypoint => `runtime-${entrypoint.name}`
        },
    },
  • JS压缩
    1、uglifyjs-webpack-plugin
    缺点:
    uglifyjs-webpack-plugin 使用的 uglify-es 已不再维护,而uglify-js又不支持es6+,不能识别 const语法、不支持ES6的语法
    优点: 老项目支持(兼容 IOS10)

2、webpack-parallel-uglify-plugin
缺点: 老项目不支持(不兼容 IOS10)
优点:
ParallelUglifyPlugin插件则会开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,但是每个子进程还是通过UglifyJS去压缩代码。无非就是变成了并行处理该压缩了,并行处理多个子任务,效率会更加的提高。

3、terser-webpack-plugin
缺点: 老项目不支持(不兼容 IOS10)
优点:

  • 和ParallelUglifyPlugin一样,并行处理多个子任务,效率会更加的提高。
  • webpack4官方推荐,有人维护。

该插件使用 terser 来压缩 JavaScript。
如果你使用的是 webpack v5 或以上版本,你不需要安装这个插件。webpack v5 自带最新的 terser-webpack-plugin。如果使用 webpack v4,则必须安装 terser-webpack-plugin v4 的版本。

const TerserWebpackPlugin = require("terser-webpack-plugin");
optimization: {
        splitChunks: {
            chunks: "all",
        },
        minimizer: [
            //配置生产环境的压缩方案:js和lcss
            new TerserWebpackPlugin({
                // 多进程打包
                parallel: true,
            })
        ],
    },

优化代码运行的性能

  • 文件缓存(hash-chunkhash-contenthash)
/**
 * 缓存:
 * 1、babel缓存
 * cacheDirectory: true 
 * 作用:让第二次打包构建速度更快
 * 2、文件资源媛存
 *      hash:每次wepack构建时会生成一个唯一的hash值。
 *          问题:因为js和css同时使用一个hash值。
 *          如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
 *      chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
 *          因为css是在js中被引入的,所以同属于一个chunk
 *      contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样 (常用这个)
 * 作用:让代码上线运行缓存更好使用
 */
output: {
        filename: "js/built.[contenthash].js",
        path: resolve(__dirname,"build")
    },
    plugins:[
        new MiniCssExtractPlugin({
            // 对输出的css进行重命名并指定输出路径
            filename: "css/built.[contenthash].css"
        }),
    ],
  • 懒加载/预加载
document.querySelector("#btn").onclick = function(){
  // 懒加载~:当文件需要使用时才加载
  // 预加载 webpackPrefetch:会在使用之前,提前加载js文件
  // 正常加载可以认为是并行加载(同一时间加载多个文件)
  // 预加载 webpackPrefetch :等其他资源加载完毕,游览器空闲了,再偷偷加载资源
  import(/* webpackChunkName: 'test', webpackPrefetch: true */"./test").then(({add}) => {
    console.log(add(22,33))
  })
}

相关文章

网友评论

    本文标题:webpack 4 基础

    本文链接:https://www.haomeiwen.com/subject/tfwzcltx.html