webpack 基本汇总

126 阅读2分钟

核心概念

  1. 入口起点(entry point): 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始,默认值是 ./src/index.js
//单个入口语法 string | string[]
module.exports = {
  entry: './path/to/my/entry/file.js', //简写
  entry:{ //与上面等效
      main: './path/to/my/entry/file.js'      
  }
};

//对象语法 entry: { <entryChunkName> string | [string] } | {}
module.exports = {
    entry: { //可以扩展多页面
           dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。
            filename: 指定要输出的文件名称。
            import: 启动时需加载的模块。
            library: 指定 library 选项,为当前 entry 构建一个 library。
            runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
            publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。请查看 output.publicPath 
    }
}
  1. output: 告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件,默认值 ./dist/main.js
const path = require('path');
module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
  },
};
  1. loader:webpack 只能理解 JavaScript 和 JSON 文件,loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中
//test 属性,识别出哪些文件会被转换。
//use 属性,定义出在进行转换时,应该使用哪个 loader

module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],//从右到左(或从下到上)地取值(evaluate)/执行(execute)
  },
};
//使用正则表达式匹配文件时,你不要为它添加引号。也就是说,/\.txt$/ 与 '/\.txt$/' 或 "/\.txt$/" 不一样。
//前者指示 webpack 匹配任何以 .txt 结尾的文件,后者指示 webpack 匹配具有绝对路径 '.txt' 的单个文件; 这可能不符合你的意图
  1. 插件:可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
//使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

常用配置

文件解析

es6 解析

npm install -D babel-loader @babel/core @babel/preset-env webpack

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules|bower_components)/, //bower_components 是包管理器 Bower 依赖文件
      use: {
        loader: 'babel-loader',
        options: {  //使用 options 属性,来向 loader 传递 options
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}

解析 css

less-loader:less 文件预解析,css-loader 解析 css 文件成一个数组([文件地址,css 字符串]),style-loader 将 css 注入到

npm i less-loader css-loader style-loader -D

module.exports = {
    module: {
        rules:[
            {
                test: /\.less$/ ,
                // 注意要按照从右到左依赖的顺序编写
                use: ['style-loader','css-loader','less-loader']
            }
        ]
    }
}

兼容处理 css

  1. 自动补齐浏览器前缀:为了向下兼容,通过 postcss-loader 和 autoprefixer 实现前缀的自动补齐
npm i postcss-loader autoprefixer -D

module.exports = {
   module: {
       rules:[
           {
               test: /\.css$/,
               loader:[
                   'style-loader',
                   'css-loader',
                   {
                       loader: 'postcss-loader',
                       options: {
                           plugins: [
                               require('autoprefixer')({
                                   browsers:['last 2 version','>1%','iOS 7']
                               })
                           ]
                       }
                   }
               ]
           }
       ]
   }   
}
  1. 响应式适配:移动端适配步骤
  • 根据屏幕分辨率设置根元素大小,以前使用 lib-flexible(已弃用),现在大多使用 viewport 单位实现
  • 开发时根据设计稿使用 px,最后统一转化为 rem
npm i --save postcss-plugin-px2rem

module.exports = {
    postcss: [px2rem({
          rootValue: 100,//换算基数
          unitPrecision: 5,//允许REM单位增长到的十进制数字
          exclude:false, 排除文件
          selectorBlackList: [],
          ignoreIdentifier: false,
          replace: true,//替换包含REM的规则,而不是添加回退
          mediaQuery: false,//允许在媒体查询中转换px
          minPixelValue: 0 //设置要替换的最小像素值
    })],
}

解析图片字体

file-loader:可处理图片,可使用 [name] 占位符 处理 build 后的文件名,不配置会使用 hash

npm install url-loader file-loader -D

module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: 'img/[name].[ext]', //img 自定义目录
              esModule: false
            },
          },
        ],
      },
    ],
  }

url-loader:与 file-loader 效果配置一样,但是可以设置 limit。依赖 file-loader

rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192, //小于 8192 byte 的编码成 base64,大于的使用 file-loader 处理
              //fallback: require.resolve('file-loader'), 预设,不需配置
              name: 'img/[name].[ext]',
              publicPath: '../', //修改公共路径
            },
          },
        ],
      },
   ]

开发和构建

文件监听

原理是轮询文件最后一次编辑时间是否改变

module.exports = {
    watch: true, //开启后自动重新构建
    watchOptions: {
        ignored: /node_modules/,
        aggregateTimeout: 2000, //2s 内没更改在构建
        poll: 1000  //每秒轮询次数
    }
}

热重载

live reload,在源代码发生更新时 自动重新构建 + 自动刷新浏览器。通过 webpack-dev-server 实现

npm i webpack-dev-server -D

module.exports = {
    devServer: {
        open: true,                    // 构建完成后自动打开浏览器
        port: 8888,                    // 监听端口
        contentBase: './dist'          // 服务器根路径
    }
}

缺点:会全局刷新浏览器,重置所有状态。

热更新

HMR(热模块替换),解决热重载缺陷,可实现局部刷新,可保存数据状态,通过 webpack 内置 HotModuleReplacementPlugin 实现

const webpack = require('webpack')
module.exports = {
    devServer: {
        hot: true     // 开启热更新
    },
    plugins:[
        new webpack.HotModuleReplacementPlugin()
    ]
}

sourceMap

mode:production 模式下,文件都是压缩过的,出错后不易调试,开启 sourceMap 可以定位源代码

module.exports = { 
    devtool: 'sourcemap' 
}

跨域代理

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://mysite'
            }
        }
    }
}

过程:/api/getData/ -> http://localhost/api/getData -> <http://mysite/api/getData>

自定生成 html 文件

dist 目录下需要手动创建 index.html 文件,可以通过 html-webpack-plugin 自动生成

npm i html-webpack-plugin -D

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            tamplate: path.resolve(__dirname,'./src/index.html')  // 提供一个模板 html 文件作为基础
        })
    ]
}

优化日志处理

npm i friendly-errors-webpack-plugin -D

const friendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
module.exports = {
stats: 'errors-only'
    devServer: {
        stats: 'errors-only'
    }
plugins: [
        new friendlyErrorsWebpackPlugin()
    ]
}

自定义 plugin 捕获构建错误

//每次构建结束后会触发 compiler 对象的 done 钩子函数,可以在这个 hook 中捕获构建错误并进行相关处理
module.exports = {
    plugins: [
        function() {
            this.hooks.done.tap('done', (stats) => {
                if (stats.compilation.errors &&
                    stats.compilation.errors.length && 
                    process.argv.indexOf('--watch') == -1)
                {
                    // 进行相关处理
                    process.exit(1);
                }
            })
        }
    ]
}

分离开发生产环境配置

  1. 分离文件形式 node 中有个process 对象,通过 cross-env(跨平台设置和使用环境变量的脚本)在 process.env 上挂载一个 NODE_ENV 环境变量用于区分

package.json:

"scripts": {
    "build": cross-env NODE_ENV = 'production' webpack --config webpack.prod.js  //按生产方式打包后上线
    "dev": cross-env NODE_ENV = 'development' webpack-dev-server webpack.dev.js  //开发
}

webpack.base.js:

module.exports = { 
    //公共配置 
    devtool: process.env.NODE_ENV === 'production' ? 'none' : 'sourcemap' 
}

webpack.prod.js 文件:

const merge = require('webpack-merge') //合并配置文件
const base = require('./webpack.base.js')

module.exports = merge(base,{
    mode: 'production'
    // 生产环境专用的配置    
})

webpack.dev.js 文件:

//同上

module.exports = merge(base,{
    mode: 'development'
    // 开发环境专用的配置       
})
  1. 函数判断形式
module.exports = (mode) => {
    // 公共配置
    if(mode === 'production'){
        // 生产环境配置
    } else {
        // 开发环境配置
    }
}

webpack 优化方式

性能分析

webpack 可以配置 stats 展示打包构建信息,但是粒度比较大,可以借助插件分析

打包速度分析:

npm install --save-dev speed-measure-webpack-plugin

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
     //配置
});

打包体积分析:

npm install --save-dev webpack-bundle-analyzer

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
    plugins: [
        new BundleAnalyzerPlugin()
    ]
}

通过 localhost:8888 访问可视化文件

splitChunks 代码分割

形成 chunk 有三中方式:

  • 多入口文件,一个 entry 对应一个 chunk
  • 动态导入的代码,会被打包到一个 chunk 中
  • 通过 splitChunks 分割的代码被打包到一个 chunk 中
module.exports = {
    optimization: {
        splitChunks: {
            // 这里的默认配置项省略,它们最终都会作用到 cacheGroups 上
            cacheGroups: {
                vendors: { 
                    test: /[\\/]node_modules[\\/]/, //可以将公用的第三方库代码抽离成一个单独的 chunk
                    //chunks 字段指的是分离 chunk 的标准
                    chunks: 'async' // 默认值,表示会将异步导入(动态导入)的模块抽离成单独的 chunk
                }
            }
        }
    }
}

示例:

// page1.js
import $ from 'jquery'
import React from 'react'
import(/* webpackChunkName: "page1-lodash"*/ 'lodash')

entry 入口对应一个 chunk,jquery 和 React 都是同步,会打包在入口 chunk 中,loadsh 是异步会单独打包成一个 chunk

待补充...