webpack入门和简单配置

209 阅读5分钟

对于webpack视频学习的简单记录

1、基础用法

1.1 entry

单入口-字符串,多入口-对象

当设置 entry: './src/index.js' 时,你可以省略它,此为默认值

可以赋值一个空对象(允许使用插件来修改入口)

module.exports = {
    entry: './path/file.js'
}
​
module.exports = {
    entry: {
        app: './path/file.js',
        admin: './path/admin.js'
    }
}
​
// 入口文件输出文件名
module.exports = {
  entry: {
    about: { import: './about.js', filename: 'pages/[name][ext]' },
  },
};
​
// 入口文件依赖,使用 dependOn-选项, 你可以将模块从一个入口文件代码块共享到另一个,app 代码块 将不包含 react-vendors 所拥有的模块
module.exports = {
  entry: {
    app: { import: './app.js', dependOn: 'react-vendors' },
    'react-vendors': ['react', 'react-dom', 'prop-types'],
  },
};
​
// 入口文件类库,为每个入口文件传递不同的 library 选项
module.exports = {
  entry: {
    commonjs: {
      import: './lib.js',
      library: {
        type: 'commonjs-module',
      },
    },
    amd: {
      import: './lib.js',
      library: {
        type: 'amd',
      },
    },
  },
};
​
// 入口文件运行时,为每个入口文件指定一个 运行时代码。 当指定时,将创建一个以该名称命名的代码块,其中仅包含该条目的运行时代码
module.exports = {
  entry: {
    app: {
      import: './app.js',
      runtime: 'app-runtime',
    },
  },
};
​
// 入口文件代码块加载,这个入口文件的运行时代码将使用这个来加载代码块
module.exports = {
  entry: {
    app: {
      import: './app.js',
    },
    worker: {
      import: './worker.js',
      chunkLoading: 'importScripts',
    },
  },
};

1.2 output

多入口通过占位符确保文件名唯一

output.filename 可以设置为函数

设置 output.path: path.resolve(__dirname, 'dist') 时:你可以省略它,此为默认值

设置 output.filename: '[name].js' 时:你可以省略它,此为默认值

webpack 4 中,多个 webpack 运行时可能会在同一个 HTML 页面上发生冲突,因为它们使用同一个全局变量进行代码块加载。为了解决这个问题,需要为 output.jsonpFunction 配置提供一个自定义的名称。Webpack 5 确实会从 package.json name 中自动推断出一个唯一的构建名称,并将其作为 output.uniqueName 的默认值。

module.exports = {
    entry:'./path/file.js',
    output:{
        filename: 'bundle.js',
        path: _dirname + '/dist'
    }
} 
​
module.exports = {
    entry:{
        app: './path/file.js',
        admin: './path/admin.js'
    },
    output:{
        filename: '[name].js',
        path: _dirname + '/dist'
    }
}

image.png

1.3 loaders

webpack只支持JS和JSON两种文件类型,通过Loaders去支持其他文件类型并且把它们转换成有效的模块

本身是一个函数,接受源文件作为参数,返回转换的结果

module.exports = {
    entry:'./path/file.js',
    output:{
        filename: 'bundle.js',
        path: _dirname + '/dist'
    },
    module:{
        rules:[
            {test:/.\txt$/,use:'raw-loader'}
        ]
    }
}

image.png

1.4 plugins

用于bundle文件的优化,资源管理和环境变量注入,作用于整个构建过程

module.exports = {
    entry:'./path/file.js',
    output:{
        filename: 'bundle.js',
        path: _dirname + '/dist'
    },
    plugins:[
        new HtmlWebpackPlugin({template:'./src/index.html'})
    ]
}

image.png

1.5 mode

指定当前构建环境,默认值为production

image.png

1.6 解析 ES6 和 react ——babel-loader

npm i @babel/core @babel/preset-env babel-loader -D
​
// babel-loader依赖babel,创建 .babelrc 文件
{
  "presets": [
    "@babel/preset-env"
  ]
}
​
// 修改 webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        use: 'babel-loader'
      }
    ]
  }
}
npm i react react-dom @babel-preset-react
​
// .babelrc
{
  "presets": [
    "@babel/preset-react"
  ]
}

1.7 解析CSS——css-loader

css-loader 用于加载 .css 文件,并且转换成 commonjs 对象

style-loader 将样式通过 <style> 标签插入到 head

loader链式调用,执行顺序从右到左

npm i css-loader style-loader -D
​
module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'style-loader
        ] // 先用css-loader解析css,然后传递到style-loader
      },
    ]
  }
}
npm i less less-loader -D
​
module.exports = {
  module: {
    rules: [
      {
        test: /.less$/,
        use: [
          'style-loader',
          'css-loader',
          'less-loader'
        ]
      },
    ]
  }
}

1.8 解析文件——file-loader

如果你定义了 rules,以使用 raw-loaderurl-loaderfile-loader 来加载资源,请使用 资源模块 替代,因为它们可能在不久的将来被淘汰。

在 webpack 5 之前,通常使用:

raw-loader 将文件导入为字符串
url-loader 将文件作为 data URI 内联到 bundle 中
file-loader 将文件发送到输出目录

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

1.9 文件监听

发现源代码发生变化时,自动冲洗你构建出新的输出文件

  • 启动 webpack 命令时,带上 --watch 参数,需要手动刷新浏览器
  • 在配置 webpack.config.js 中设置 watch: true

原理

image.png

1.10 热更新

模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。

webpack-dev-server 为你提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能。webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中

// package.json
{
  "scripts": {
    "dev": "webpack-dev-server --open"
  },
}

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    contentBase: './dist',
    hot: true
  }
}

v5

// npm install --save-dev webpack-dev-server

// 将 dist 目录下的文件 serve 到 localhost:8080 下
module.exports = {
  devServer: {
    static: './dist',
  },
  optimization: {
    runtimeChunk: 'single', // 单个 HTML 页面有多个入口
  },
 };

// package.json
{
    "scripts": {
        "start": "webpack serve --open",
   },
}

原理

image.png

1.11 文件指纹

打包后输出的文件名后缀,用作版本管理

  • Chunkhash:和webpack打包的chunk有关,不同的entry会生成不同的chunckhash
  • Contenthash:根据文件内容来定义hash,文件内容不变,则contenthash不变
module.exports = {
    entry:{
        app: './path/file.js',
        admin: './path/admin.js'
    },
    // JS chunckhash
	output:{
        filename: '[name][chunkhash:8].js',
    	path: _dirname + '/dist'
    },
    // css Contenthash
    plugis:[
        new MiniCssExtractPlugin({ // 把style-loader中的css提取出来
            filename:`[name][contenthash:8].css`
        })
    ],
    // 图片 Contenthash
    module: {
        rules: [
            {
                test: /.(png|jpg|gif|jpeg|svg)$/,
                use: [{
                  loader: 'file-loader',
                  option: {
                      name:'img/[name][hash:8].[ext]'
                   }
                }],
            },
        ]
	}
}

1.12 代码压缩

JS

内置uglifyjs-webpack-plugin

CSS

使用 optimize-css-assets-webpack-plugin ,同时使用 cssnano

npm i optimize-css-assets-webpack-plugin cssnano -D
​
const OptimizeCSSAssetsPlungin = require('optimizecss-assets-webpack-plugin')
module.exports = {
    plugins:[
    // css压缩
    new OptimizeCSSAssetsPlungin({
      assetNameRwgExp: /.css$/g,
      cssProcessor: require('cssnano')
    })
  ],
}

For webpack v5 or above please use css-minimizer-webpack-plugin instead.

// npm install css-minimizer-webpack-pluginconst CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      // For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line `...`,
      new CssMinimizerPlugin({
        test: /.css$/g,
      }),
    ],
  },
}

HTML

修改html-webpack-plugin,设置压缩参数

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js',
  },
  plugins: [new HtmlWebpackPlugin()],
};

这将会生成一个包含以下内容的 dist/index.html 文件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>webpack App</title>
  </head>
  <body>
    <script src="index_bundle.js"></script>
  </body>
</html>

image.png

2、进阶用法

2.1 自动清理构建目录产物

  • output.clean 配置项

  • 通过npm scripts

    rm -rf ./dist && webpack
    rimraf ./dist && webpack
    
  • clean-webpack-plugin

    // npm i clean-webpack-plugin -D
    
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    module.exports = {
      plugins: [
        // 自动清理构建目录产物
        new CleanWebpackPlugin(),
      ],
    }
    clean-webpack-pluginconst path = require('path');
    

2.2 补齐CSS前缀

// npm i postcss-loader autoprefixer -D

module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ()=>{
                  require('autoprefixer')({
                    browsers:['last 2 version','>1%']
                  })
                }
              }
            }
          }
        ]
      },
    ]
  }
}

// 方式2
module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
        ]
      },
    ]
  }
}
// postcss.config.js
module.exports = {
  plugins: [
      require('autoprefixer')({browsers:['last 6 version', '>1%', 'ios 7']})
  ],
};

2.3 px自动转成rem

// npm i px2rem-loader -D
// npm i lib-flexible -S

2.4 静态资源内联

避免页面闪动

image.png

assets module (资源模块) 替换raw-loader:juejin.cn/post/702957…

2.5 多页面应用(MPA)打包

多入口,每次页面跳转,后台服务器返回新的html文件

动态配置入口,利用glob.sync

// npm i `glob -D

const glob = require('glob');
const setMPA = () => {
  const entry = {};
  const htmlWebpackPlugin = [];
  const entryFiles = glob.sync('./src/*/index.js');

  Object.keys(entryFiles).map((index) => {
    const entryFile = entryFiles[index]; // ./src/index/index.js
    const match = entryFile.match(/src/(.*)/index.js/);
    const pageName = match && match[1];
    entry[pageName] = entryFile;
    htmlWebpackPlugin.push(    
      new HtmlWebpackPlugin({
        template: path.join(__dirname,`src/${pageName}/index.html`),
        filename: `${pageName}.html`,
        chunks: [pageName],
        inject: true,
        minify: {
          html5: true,
          collapseWhitespace: true,
          preserveLineBreaks: false,
          minifyCSS: true,
          minifyJS: true,
          removeComments: false
        }
    }));
  })

  return {
    entry,
    htmlWebpackPlugin
  }
}
const {entry, htmlWebpackPlugin} = setMPA()
module.exports = {
  entry: entry,
    plugins:[
        //...
    ].concat(htmlWebpackPlugin)
}

2.6 使用sourcemap

定位到源代码,开发环境开启,线上环境关闭,线上排查问题可以将sourcemap上传到错误监控系统

module.exports = {
	devtool: 'source-map', // 推荐使用 source-map或者cheap-module-source-map
}

2.7 提取页面公共资源

基础库,如react,通过cdn引入,不打入bundle

image.png

splitchunksplugin

image.png

image.png

2.8 代码分割和动态引入

部分代码在某些特殊时候才用到,将代码库分割成chunks(语块),当代码运行时再进行加载

懒加载JS脚本

  • CommonJS:require.ensure
  • 动态import
// npm i @babel/plugin-syntax-dynamic-import --save-dev// .babellrc
{
    "plugins":[
        "@babel/plugin-syntax-dynamic-import"
    ]
}
​
// index.js
import('./text.js').then() // 返回promise对象

2.9 ESlint

帮助发现代码错误,统一代码风格

优秀实践:eslint-config-airbnb

image.png

落地

  • CI/CD

image.png

// npm i husky --save--dev

// package.json
{
    "script":{
        "precommit":"lint-staged"
    },
    "lint-staged":{
        "linters":{
            "*.{js,scss}":["eslint--fix","git add"]
        }
    }
}
  • webpack
// npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y eslint-loader eslint-config-airbnb -Dmodule.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        use: ['babel-loader','eslint-loader']
      },
  }
}
        
// .eslintrc.js
module.exports = {
  "parser":"babel-eslint",
  "extends":"airbnb",
  "env":{
    "browser":true
  },
  // "rules" 自定义配置规则
}
​
// package.json
{
    "script": "eslint ./lib fix" // eslint检查并简单修复
}

2.10 打包组件和基础库

实现大整数加法库打包

  • 需要打包压缩版本(开发阶段 large-number.min.js)和非压缩版本(线上 large-number.js)

  • 支持 AMD/CJS/ESM 模块引入

    // ES module
    import * as largeNumber from 'large-number'
    // ...
    largeNumber.add('999','1')
    ​
    // CJS
    const largeNumber = require('largr-number')
    // ...
    largeNumber.add('999','1')
    ​
    // AMD
    require(['large-number'], function(large-number){
        // ...
        largeNumber.add('999','1')
    })
    ​
    // 发布到cdn后,script引入
    <!doctype html>
    <html>
        <script src="http://..."></script>
        <script>
            // ...
            // Global var
            largeNumber.add('999','1')
            // property in the window object
            window.largeNumber.add('999','1')
        </script>
    </html>
    

image.png

// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
  entry:{
      "large-number":"./src/index.js",
      "large-number.min":"./src/index.js"
  },
  output:{
      filename:"[name].js",
      library:"largeNumber", // 指定库的全局变量
      libraryExport:"default",
      libraryTarget:"umd", // 支持库引入方式
      clean: true
  },
  // 只对.min压缩
  mode:"none",
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        include: /.min.js$/,
      })
    ]
  }
}
​
// package.json
{
  "name": "large-number",
  "version": "1.0.0",
  "description": "大整数加法打包",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "build": "webpack",
    "prepublish": "webpack" // 有npm账号,执行这个命令就可以把包发布到npm
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "terser-webpack-plugin": "^5.3.6",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  }
}
​
// index.js
if (process.env.NODE_ENV = 'production') {
  module.exports = require('./dist/large-number.min.js')
} else {
  module.exports = require('./dist/large-number.js')
}

2.11 SSR

服务端将渲染好的页面返回,缩短首屏加载时间

image.png

image.png

2.12 命令行显示日志

stats:输出统计信息

stats:"errors-only"
​
// 如果有devServer
devServer: {
    stats:"errors-only"
}

image.png

2.13 构建异常和中断处理

image.png