注:webpack本身只能处理js、json等文件资源,其他的文件需要loader、plugin进行配合才能支持

简介:

将浏览器不兼容或不支持的代码编译打包成浏览器能够运行的代码

功能:

webpack功能比较局限,但可以通过plugins和loader进行扩展,从而使其功能强大
开发模式:仅能编译js中的ES Module语法
生产模式:仅能编译js中的ES Module语法,压缩js代码

打包方式:

它会以一个或多个文件作为打包的入口(单页面或多页面),将项目所有的文件编译打包为一个或多个bundle文件输出

基础使用

第一步:初始化package.json
npm init -y
第二步:安装webpack及webpack-cli
npm i webpack webpack-cli -D
第三步:创建webpack.config.js文件并配置mode和entry

  1. module.exports = {
  2. mode: 'development',
  3. entry: './main.js',
  4. };

第四步:配置脚本指令

  1. "scripts": {
  2. "dev": "webpack"
  3. }

第五步:运行
npm run dev

五大核心配置

1.entry(入口文件)

指示webpack从那个文件开始打包

2.output(输出文件)

指示webpack编译打包完的文件输出到哪个位置,如何命名等

path:

输出文件的绝对地址

filename:

输出文件的名称

publicPath:

所有资源文件的基础路径

  1. output: {
  2. path:path.resove(__dirname,'dist'),
  3. filename:'bundle.js'
  4. },

3.loader(加载器)

webpack本身只能处理js、json等文件资源,其他资源如:图片、css、字体、vue等文件资源需要借助loader来进行支持

  1. // 单个写法
  2. {
  3. test:'正则',
  4. loader:'xxx-loader',
  5. option:{}
  6. }
  7. // 多个写法
  8. {
  9. test:'正则',
  10. use:[
  11. 'xxx-loader',
  12. {
  13. loader:'xxx-loader1,
  14. option:{}
  15. },{
  16. loader:'xxx-loader2',
  17. option:{}
  18. }
  19. ]
  20. }

4.plugins(插件)

扩展webpack的功能

5.mode(模式)

主要分为develpoment、production

  1. module.exports = {
  2. entry: '入口地址',
  3. output: {输出配置},
  4. module: {
  5. rules: [
  6. loader配置
  7. ],
  8. },
  9. plugins: [
  10. plugin配置
  11. ],
  12. mode: '生产环境',
  13. };

其他常用模块

1.resolve(解析)

alias:

别名

extensions:

配置省略文件后缀名

modules:

告诉 webpack 解析模块时应该搜索的目录。

  1. resolve: {
  2. /* 配置路径别名,优点:简写路径;缺点:没有提示 */
  3. alias: {
  4. $css: resolve(__dirname, "src/css")
  5. },
  6. /* 配置省略文件后缀名 */
  7. extensions: [".js", ".json", ".css", "jsx"],
  8. /* 告诉 webpack 解析模块去哪个目录找 */
  9. modules: [resolve(__dirname, "../../node_modules"), "node_modules"]
  10. }

2.Externals(外部扩展)

  1. externals: {
  2. jquery: 'jQuery',
  3. },

3.devServer(本地服务)

  1. devServer: {
  2. // 运行代码的目录
  3. contentBase: resolve(__dirname, 'build'),
  4. // 监视contentBase目录下的所有文件,一旦有变化就会reload
  5. watchContentBase: true,
  6. watchOptions:{
  7. // 忽略文件
  8. ignored: /node_modules/
  9. },
  10. // 启动gzip压缩
  11. compress: true,
  12. // 端口号
  13. port: 5000,
  14. // 域名
  15. host: 'localhost',
  16. // 自动打开浏览器
  17. open: true,
  18. // 开启HMR功能
  19. hot: true,
  20. // 不要显示启动服务器的日志
  21. clientLogLevel: 'none',
  22. // 除了一些基本启动信息外,其他内容都不要显示
  23. quiet: true,
  24. // 如果出现错误信息,不要全屏提示
  25. overlay: false,
  26. // 服务器代理,解决开发环境下跨域问题
  27. proxy: {
  28. // 一旦devserver(5000)服务器接收到 /api/xxx的请求,就会把请求转发到另外一个服务器(3000)
  29. '/api': {
  30. target: 'http://localhost:3000',
  31. // 发送请求时,请求路径重写:将/api/xxx -> /xxx(去掉/api)
  32. pathRewrite:{
  33. '^/api': ''
  34. }
  35. }
  36. }
  37. }

开发模式

  1. 支持css及css预处理
  2. 支持高版本js
  3. 支持图片
  4. 支持fonts
  5. 支持其他资源
  6. html自动引入脚本
  7. 本地服务 ```c module.exports = { mode: ‘development’, output: { path: ‘/dist’, filename: ‘js/[name].js’, publicPath: ‘/‘, chunkFilename: ‘js/[name].js’, }, resolve: { alias: {
    1. '@': '/src',
    }, extensions: [‘.mjs’, ‘.js’, ‘.jsx’, ‘.vue’, ‘.json’, ‘.wasm’], modules: [‘node_modules’, ‘/node_modules’, ‘/node_modules\@vue\cli-service\node_modules’], }, module: { noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/, rules: [
      /* config.module.rule('vue') */
      {
          test: /\.vue$/,
          use: ['cache-loader', 'vue-loader'],
      },
      /* config.module.rule('images') */
      {
          test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
          use: [
              {
                  loader: 'url-loader',
                  options: {
                      limit: 4096,
                      name: 'img/[name].[hash:8].[ext]',
                  },
              },
          ],
      },
      /* config.module.rule('svg') */
      {
          test: /\.(svg)(\?.*)?$/,
          use: [
              {
                  loader: 'file-loader',
                  options: {
                      name: 'img/[name].[hash:8].[ext]',
                  },
              },
          ],
      },
      /* config.module.rule('media') */
      {
          test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
          use: [
              {
                  loader: 'url-loader',
                  options: {
                      limit: 4096,
                      name: 'media/[name].[hash:8].[ext]',
                  },
              },
          ],
      },
      /* config.module.rule('fonts') */
      {
          test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
          use: [
              /* config.module.rule('fonts').use('url-loader') */
              {
                  loader: 'url-loader',
                  options: {
                      limit: 4096,
                      name: 'fonts/[name].[hash:8].[ext]',
                  },
              },
          ],
      },
      /* config.module.rule('css') */
      {
          test: /\.css$/,
          use: ['style-loader', 'css-loader'],
      },
      /* config.module.rule('scss') */
      {
          test: /\.scss$/,
          use: ['style-loader', 'css-loader', 'scss-loader'],
      },
      /* config.module.rule('sass') */
      {
          test: /\.sass$/,
          use: ['style-loader', 'css-loader', 'sass-loader'],
      },
      /* config.module.rule('less') */
      {
          test: /\.less$/,
          use: ['style-loader', 'css-loader', 'less-loader'],
      },
      /* config.module.rule('stylus') */
      {
          test: /\.styl(us)?$/,
          use: ['style-loader', 'css-loader', 'stylus-loader'],
      },
      /* config.module.rule('js') */
      {
          test: /\.m?jsx?$/,
          use: ['cache-loader', 'babel-loader'],
      },
    
    ], }, plugins: [ new VueLoaderPlugin(), //支持vue-loader new HtmlWebpackPlugin({
      title: 'vue_template',
      template: '/public/index.html',
    
    }), ], entry: { app: [‘./src/main.js’], }, devServer: { host: ‘0.0.0.0’, // 项目运行时的本地地址 port: 8880, // 端口号 open: true, // 配置自动启动浏览器 }, };
<a name="wPKdl"></a>
## 生产环境

   1. 支持css及css预处理、压缩css、兼容css、抽离css(开发模式使用的style-loader通过js插入到head当中,需要等到js全部解析完成,才开始插入,页面会出现闪屏,为了解决这一问题,生产环境需要抽离css)、通过contentHash进行命名,利用浏览器缓存进行优化
   1. 支持高版本js、压缩js(terser-webpack-plugin)、通过contentHash进行命名,利用浏览器缓存进行优化
   1. 支持图片、通过hash进行命名,利用浏览器缓存进行优化
   1. 支持fonts、通过hash进行命名,利用浏览器缓存进行优化
   1. 支持其他资源、通过hash进行命名,利用浏览器缓存进行优化
   1. 对html进行压缩(HtmlWebpackPlugin)
```c
module.exports = {
    mode: 'production',
    context: '',
    devtool: 'source-map',
    output: {
        path: '/dist',
        filename: 'js/[name].[contenthash:8].js',
        publicPath: '/',
        chunkFilename: 'js/[name].[contenthash:8].js',
    },
    resolve: {
        alias: {
            '@': '/src',
            vue$: 'vue/dist/vue.runtime.esm.js',
        },
        extensions: ['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm'],
        modules: ['node_modules', '/node_modules', '/node_modules\\@vue\\cli-service\\node_modules'],
    },
    module: {
        noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
        rules: [
            /* config.module.rule('vue') */
            {
                test: /\.vue$/,
                use: ['cache-loader', 'vue-loader'],
            },
            /* config.module.rule('images') */
            {
                test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 4096,
                            name: 'img/[name].[hash:8].[ext]',
                        },
                    },
                ],
            },
            /* config.module.rule('svg') */
            {
                test: /\.(svg)(\?.*)?$/,
                use: [
                    /* config.module.rule('svg').use('file-loader') */
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'img/[name].[hash:8].[ext]',
                        },
                    },
                ],
            },
            /* config.module.rule('media') */
            {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 4096,
                            name: 'media/[name].[hash:8].[ext]',
                        },
                    },
                ],
            },
            /* config.module.rule('fonts') */
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
                use: [
                    /* config.module.rule('fonts').use('url-loader') */
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 4096,
                            name: 'fonts/[name].[hash:8].[ext]',
                        },
                    },
                ],
            },
            /* config.module.rule('css') */
            {
                test: /\.css$/,
                use: [
                    {
                        loader: 'mini-css-extract-plugin',
                        options: {
                            hmr: false,
                            publicPath: '../',
                        },
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: false,
                            importLoaders: 2,
                            modules: {
                                localIdentName: '[name]_[local]_[hash:base64:5]',
                            },
                        },
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            sourceMap: false,
                        },
                    },
                ],
            },
            /* config.module.rule('scss') */
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: 'mini-css-extract-plugin',
                        options: {
                            hmr: false,
                            publicPath: '../',
                        },
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: false,
                            importLoaders: 2,
                            modules: {
                                localIdentName: '[name]_[local]_[hash:base64:5]',
                            },
                        },
                    },
                    {
                        loader: 'scss-loader',
                        options: {
                            sourceMap: false,
                        },
                    },
                ],
            },
            /* config.module.rule('sass') */
            {
                test: /\.sass$/,
                use: [
                    {
                        loader: 'mini-css-extract-plugin',
                        options: {
                            hmr: false,
                            publicPath: '../',
                        },
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: false,
                            importLoaders: 2,
                            modules: {
                                localIdentName: '[name]_[local]_[hash:base64:5]',
                            },
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: false,
                        },
                    },
                ],
            },
            /* config.module.rule('less') */
            {
                test: /\.less$/,
                use: [
                    {
                        loader: 'mini-css-extract-plugin',
                        options: {
                            hmr: false,
                            publicPath: '../',
                        },
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: false,
                            importLoaders: 2,
                            modules: {
                                localIdentName: '[name]_[local]_[hash:base64:5]',
                            },
                        },
                    },
                    {
                        loader: 'less-loader',
                        options: {
                            sourceMap: false,
                        },
                    },
                ],
            },
            /* config.module.rule('stylus') */
            {
                test: /\.styl(us)?$/,
                use: [
                    {
                        loader: 'mini-css-extract-plugin',
                        options: {
                            hmr: false,
                            publicPath: '../',
                        },
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: false,
                            importLoaders: 2,
                            modules: {
                                localIdentName: '[name]_[local]_[hash:base64:5]',
                            },
                        },
                    },
                    {
                        loader: 'stylus-loader',
                        options: {
                            sourceMap: false,
                        },
                    },
                ],
            },
            /* config.module.rule('js') */
            {
                test: /\.m?jsx?$/,
                use: [
                    'cache-loader',
                    'thread-loader',
                    'babel-loader'
                ],
            },
        ],
    },
    plugins: [
        /* config.plugin('vue-loader') */
        new VueLoaderPlugin(),
        /* config.plugin('extract-css') */
        new MiniCssExtractPlugin({
            filename: 'css/[name].[contenthash:8].css',
            chunkFilename: 'css/[name].[contenthash:8].css',
        }),
        /* config.plugin('optimize-css') */
        new OptimizeCssnanoPlugin({
            sourceMap: false,
            cssnanoOptions: {
                preset: [
                    'default',
                    {
                        mergeLonghand: false,
                        cssDeclarationSorter: false,
                    },
                ],
            },
        }),
        /* config.plugin('html') */
        new HtmlWebpackPlugin({
            title: 'vue_template',
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                collapseBooleanAttributes: true,
                removeScriptTypeAttributes: true,
            },
            template: '/public/index.html',
        }),
    ],
    entry: {
        app: ['./src/main.js'],
    },
};

vue-cli开发环境webpack配置

npx vue-cli-service inspect —mode development >> webpack.config.development.js

vue-cli生产环境webpack配置

npx vue-cli-service inspect —mode production >> webpack.config.production.js

webpack优化

提升webpack打包速度

hotModuleReplacement(热模块):默认开启

开发时我们修改了其中一个模块代码,webpack默认会将所有模块全部重新打包编译,速度会很满。使 用MHR,webpack就只会对我们改动的模块进行打包编译,从而减少打包编译的时间
注:css、html默认是开启的,而js需要通过module.hot.accept去监测。在vue项目中,vue-loader已 经实现了这一功能,无需通过module.hot.accept去监测

devServer: {
    hot: true,
},

oneOf:如果匹配到一个能满足规则的, 则终止循环, 不在继续查找

开发时,我们需要配置多个loader的规则,一个文件需要由上而下的去匹配规则,就算是已经匹配到 了其中一个,匹配还是不会停止,直至匹配完成,浪费时间。oneOf是指当一个文件在由上而下匹配的 时候,一但匹配就会停止不会继续匹配这个文件。

module: {
    rules: [
        {
            oneOf:[
                {
                    test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
                    oneOf: [
                        {
                            loader: 'url-loader',
                        },
                        {
                            loader: 'file-loader',
                        },
                    ],
                },
                {
                    test: /\.css$/,
                    oneOf: [
                        {
                            loader: 'url-loader',
                        },
                        {
                            loader: 'file-loader',
                        },
                    ],
                },
            ]
        }
    ],
},

include/Exclude:包含和排除

开发时我们需要使用第三方的库或者插件,但这些库或插件是不需要编译打包就可以直接使用的,所以我们在js进行babel处理的时候需要将这些库或插件排除在外,来减少打包的时间。

module:{
    rules:[
        {
            test: /\.m?jsx?$/,
            exclude:/node_module/, // 排除
            use: [
                'cache-loader',
                'thread-loader',
                'babel-loader'
            ],
        },
    ]
}

cache:对性能开销较大进行缓存

在第一次打包以后对性能开销较大的进行缓存(主要为js,一般存储在node_module/.cache文件中),第二次打包时,只需要打包缓存中没有的文件,来减少打包时间。

module:{
    rules:[
        {
            test: /\.m?jsx?$/,
            use: [
                'cache-loader', // 缓存
                'thread-loader',
                'babel-loader'
            ],
        },
    ]
}

多线程:thread-loader

当打包时间到达一定的临界点时,多线程打包才能生效,因为没开启一个线程需要消耗600ms的时间。

module:{
    rules:[
        {
            test: /\.m?jsx?$/,
            use: [
                'cache-loader', 
                'thread-loader', // 多线程
                'babel-loader'
            ],
        },
    ]
}

减少代码体积

减少babel生成文件的体积:@babel/plugin-transform-runtime

Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以引入 Babel runtime 作为一个独立模块,来避免重复引入。
下面的配置禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。

module:{
    rules: [
      // 'transform-runtime' 插件告诉 Babel
      // 要引用 runtime 来代替注入。
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-runtime']
          }
        }
      }
    ]
}

treeShaking

开发时,我们定义了一些工具函数或引用了第三方函数库或组件库,如果没有特殊处理的话,webpack会打包整个库,但实际上我们只用到了很小的一部分,这样会增大打包的体积,明显是我们不需要的。treeShaking就是移除掉我们没使用上的代码(注:treeShaking依赖ESmodule,如果是common.js是没发使用的),webpack默认是开启这个功能的,无需配置

优化代码性能

code split

为什么
打包代码时会将所有js文件打包到一个文件中,体积太大,如果我们只要渲染首页,就应该只要加载首页的js文件,而不需要加载其他的文件。所以我们需要使用代码分割,生成多个js文件,渲染那一个就加载哪一个js,这样就减少了资源的加载。
是什么
分割文件,将打包的文件进行分割,生成多个js(提取公共代码)
按需加载,需要那个文件就加载那个文件
拆分思路:
业务代码
第三方库:当三方库比较多导致文件较大,可以通过三方库的大小进行拆分或CDN引入
业务代码的公共业务模块
module、chunk、bundle
总结:其实是同一份逻辑代码在不同的场景下的3个名字
module:我们直接手写的代码,还没进过编译的代码,对于webpack而言,所有文件都是module
chunk:webpack正在打包的时候
bundle:打包结束可以直接在浏览器上运行的文件,即所有输出的文件
Snipaste_2022-06-17_08-57-52.png
上图可以看出,一个chunk可以由多个模块或单个模块组成,一个chunk可以输出一个bundle或多个bundle
产生Chunk的三种途径:
entry入口:
string:产生一个chunk
array:产生一个chunk
object:有多少个key就产生多少个chunk
动态导入:import()
代码分割:codeSplit(没有定义test值,只针对chunk提取公共模块,定义test将test指定的目录单独分割为一个chunk)

webpack默认配置
optimization: {
    splitChunks: {
        // 表示选择哪些 chunks 进行分割,可选值有:
        // async:只对动态导入的chunk生效
        // initial:入口
        // all:入口和动态组件
        chunks: "async",
        // 表示新分离出的 chunk 必须大于等于 minSize,20000,约 20kb。
        minSize: 20000,
        // 通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块,仅在剩余单个 chunk 时生效
        minRemainingSize: 0,
        // 表示一个模块至少应被 minChunks 个 chunk 所包含才能分割。默认为 1。
        minChunks: 1,
        // 表示按需加载文件时,并行请求的最大数目。
        maxAsyncRequests: 30,
        // 表示加载入口文件时,并行请求的最大数目。
        maxInitialRequests: 30,
        // 强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略
        enforceSizeThreshold: 50000,
        // cacheGroups 下可以可以配置多个组,每个组根据 test 设置条件,符合 test 条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据 priority 来决定打包到哪个组中。默认将所有来自 node_modules 目录的模块打包至 vendors 组,将两个以上的 chunk 所共享的模块打包至 default 组。
        cacheGroups: {
            defaultVendors: {
                test: /[\\/]node_modules[\\/]/,
                // 一个模块可以属于多个缓存组。优化将优先考虑具有更高 priority(优先级)的缓存组。
                priority: -10,
                // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
                reuseExistingChunk: true,
            },
            default: {
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true
            }
        }
    }
}
// vuecli
optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: { //将node_modules中的三方库打包到一起
          name: 'chunk-vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {//将动态导入的模块中公用的模块分割
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    },
}

networkCache:hash、chunkHash、contentHash

对输出的资源文件更好的命名,由于浏览器缓存

core.js

babel的预设是有限的,一些更高级的语法是没法兼容的,比如promise、async/awite等,需要配置core.js让webpack自动的根据代码去匹配需要那些polyfill,对js进行兼容性处理,让程序能够运行在更低版本的浏览器上