10分钟学会 webpack

webpack 是什么


webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序。

前端现在越来越复杂:

  • 比如开发过程中我们需要通过模块化的方式来开发;
  • 比如也会使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,通过sass、less等方式来编写css样式代码;
  • 比如开发过程中,我们还希望实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;
  • 比如开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化;

这些问题我们可以利用工具来处理,比如为了兼容就使用 Babel 转换 es6,ts 代码就使用 tsc 来转换。然后使用转换好的 js 引入 html 中。css 也一样。但是我们会发现,一个一个处理,这很麻烦,所以我们需要一个构建工具,可以帮助我们自动完成这些东西。webpack 就是一个这样的工具。

基本使用

webpack的官方文档是https://webpack.js.org/
webpack的中文官方文档是https://webpack.docschina.org/

  • DOCUMENTATION:文档详情,也是我们最关注的

Webpack的运行是依赖Node环境的,所以我们电脑上必须有Node环境。

安装

npm install webpack webpack-cli –g # 全局安装
npm install webpack webpack-cli –D # 局部安装

webpack 和 webpack-cli

webpack 是核心代码,webpack-cli 是命令行工具,让我们能在命令行中操作 webpack。

webpack 的打包命令就是webpack,当安装了 cli 后,我们就能在命令行中直接输入 webpack 启动打包。
也可以在 package.json 脚本中添加 webpack命令。使用 npm 来启动 webpack 打包。npm 中的脚本命令就相当于是在命令行中输入执行。

webpack-cli 作为一个命令行工具,当然不安装它也行,其实第三方的脚手架就没有使用 webpack-cli,而是使用类似于自己的 vue-service-cli 的东西。

默认打包

我们在命令行输入 webpack 启动打包,这时就有个问题,webpack 打包的是谁?又把谁打包到哪里去?
事实上,当我们运行webpack时,webpack会查找当前目录下的 src/index.js 作为入口;所以,如果当前项目中没有存在src/index.js文件,那么会报错;打包后会生成一个dist文件夹,里面存放一个main.js的文件,就是我们打包之后的文件。

这是默认的打包行为,那入口和出口我们能改吗?当然能改。我们可以通过给 webpack 命令添加参数来配置打包行为。

webpack --entry ./src/main.js --output-path ./build

上面命令就重新配置了 webpack 的打包入口和出口。
但是 webpack 的配置很多,全靠命令行来配置是不现实的,所以 webpack 也提供了配置文件webpack.config.jswebpack 启动时会默认读取这个文件进行配置。
当然我们也可以指定要读取的配置文件:

webpack --config wk.config.js

理解 webpack

理解 webpack

webpack.config.js

我们对 webpack 主要配置 8 个方面:

  1. mode:构建模式
  2. entry:入口
  3. output:出口
  4. module.rules:模块规则中的 loader
  5. plugins:插件
  6. devServer:开发服务器
  7. resolve:模块解析
  8. devtool:source map

本文以使用 webpack 从 0 搭建一个项目的视角来介绍。

const path = require("path")

module.exports = {
    
    
  mode: "development",
  entry: "./src/index.js",
  output: {
    
    
    filename: "bundle.js",
    path: path.resolve(__dirname, "./dist"),
  },
  module: {
    
    
    rules: []
  },
  plugins: []
}

入口文件目录

我们之前编写入口文件的规则是这样的:./src/index.js,这个 ./ 指的是哪个目录?是配置文件 webpack.config.js 所在的目录吗?
如果我们的配置文件所在的位置变成了 config 目录,将入口改成 …/src/index.js 会发现是报错的。入口依然要写成 ./src/index.js;

这是因为入口文件其实是和另一个属性时有关的 context;
context 的作用是用于解析入口(entry point)和加载器(loader):默认表示 webpack 的启动目录。

所以推荐手动将入口目录改成配置文件所在的目录,更符合直觉:

module.exports = {
    
    
	context: path.resolve(__dirname, "./")
}

loader

使用 loader 的方式有三种

  1. 内联方式:在引入的样式前加上使用的loader,并且使用!分割;
import "css-loader!./css/style.css"
  1. 命令行方式:–module-bind
  2. 配置文件

前面两种方式使用的很少,因为不便于管理,尤其是命令行的方式,webpack5 已经淘汰了。主要还是使用配置的方式。

配置方式

module.rules的配置如下:
rules属性对应的值是一个数组:[Rule]

  • 数组中存放的是一个个的Rule,Rule是一个对象,对象中可以设置多个属性来添加模块规则。
    • test属性:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;
    • use属性:对应的值时一个数组:[UseEntry]
      • UseEntry是一个对象,可以通过对象的属性来设置一些其他属性
        • loader:必须有一个 loader属性,对应的值是一个字符串;
        • options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;
        • query:目前已经使用options来替代;
      • 传递字符串(如:use: [ ‘style-loader’ ])是 loader 属性的简写方式(如:use: [ { loader: ‘style-loader’} ]);
    • loader属性: Rule.use: [ { loader } ] 的简写。

另外 use 是个数组,可以对同一类型的文件使用多个 loader。
loader 的生效顺序是按 use 数组中的顺序,从后往前生效的。

module: {
    
    
  rules: [
    {
    
    
      test: "正则",
      use: [
        {
    
     loader: "loader名", options?: {
    
    "可选的属性"} }, 
        {
    
    },
        {
    
    }
      ]
		},
    {
    
    },
    ...
  ]
}

use 除了完整的写法,还有两种种语法糖写法:

  1. 直接传递字符串给 use 数组

它是简写了 useEntry 中的 loader 属性:

use: [{
    
    loader: "css-loader"}]

use: ["css-loader"]
  1. 使用 loader 代替 use 数组,直接不写 use 数组了。
{
    
    
  test: "正则",
  use: []
}

{
    
    
  test: "正则",
  loader: "css-loader"
}

当然,需要对 loader 进行配置的时候,依然要使用完整的写法。

处理样式文件

css-loader

解析 css 文件,需要使用 css-loader。
安装:npm install css-loader -D

我们已经可以通过css-loader来加载css文件了,但是你会发现这个css在我们的代码中并没有生效(页面没有效果)。
因为css-loader只是负责将.css文件进行解析,并不会将解析之后的 css 插入到页面中;如果我们希望再完成插入style 的操作,那么我们还需要另外一个loader,就是 style-loader。

style-loader

安装:npm install style-loader -D
注意:style-loader 也是作用于 css 文件,所以和 css-loader 在同一个 use 数组中,得先加载才能插入,style-loader 得放在 css-loader 前面,让 css-loader 先生效。

style-loader 是将 css 文件内容以内联样式<style></style>的方式插入,那怎么将 css 文件以 link 的方式引入呢?

less-loader

less 是一个单独的工具,平时我们可以直接使用它,它会自动将 less 文件转换成 css 文件在同级目录中。
安装:npm i less -D

打包的 js 文件中引入了 less 文件,自然也要使用对应的 loader 进行加载。
安装:npm i less-loader -D

PostCSS 工具

什么是PostCSS呢?

  • PostCSS 是一个通过 JavaScript 来转换 css 的工具;
  • 这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置;
  • 但是实现这些功能,我们需要借助于PostCSS对应的插件;

postcss 和 less 一样是单独的工具,我们可以直接在命令行使用它。
安装:npm install postcss postcss-cli -D

利用 postcss 给 css 插入浏览器前缀的功能,需要使用它的插件 autoprefixer。
安装:npm install autoprefixer -D
使用:npx postcss --use autoprefixer -o end.css ./src/css/style.css

postcss-loader

postcss 是一个工具,里面可以安装插件实现某些功能,比如这里就是安装 autoprefixer 给 css 添加浏览器前缀。
我们可以单独使用 postcss 工具,当然也能通过 loader 将 postcss 添加到 webpack 中使用。

安装:npm install postcss-loader -D

因为 postcss 需要有对应的插件才会起效果,所以我们需要配置它的 plugin:

{
    
    
  test: /\.css$/,
  use: [
    "style-loader", 
    "css-loader", 
    {
    
    
      loader: "postcss-loader",
      options: {
    
    
        postcssOptions: {
    
    
          plugins: [
            require("autoprefixer")  
          ]
        }
      }
    }
   ]
},

postcss.config.js

另外,我们也可以将 postcss 的配置信息放到一个单独的文件中进行管理,这样 webpack.config.js 中就不用进行配置了,只需使用就好。
在根目录下创建postcss.config.js:

module.exports = {
    
    
  plugins: [require("autoprefixer")]
}

postcss-preset-env

事实上,在配置 postcss-loader 时,我们配置插件并不需要使用 autoprefixer。它还有个更吊的预设插件:postcss-preset-env。
postcss-preset-env 也是一个postcss 的插件;它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境添加所需的 polyfill(补丁);
也包括会自动帮助我们添加autoprefixer(所以相当于已经内置了autoprefixer);

首先,我们需要安装 postcss-preset-env:npm install postcss-preset-env -D

我们在使用某些postcss插件时,其实也可以直接传入字符串:

plugins: ["postcss-preset-env"]

汇总处理样式文件的 webpack 配置

const path = require("path")

module.exports = {
    
    
  mode: "development",
  entry: "./src/main.js",
  output: {
    
    
    filename: "bundle.js",
    path: path.resolve(__dirname, "./dist"),
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: ["style-loader", "css-loader", "postcss-loader"]
      },
      {
    
    
        test: /\.less$/,
        use: ["style-loader", "css-loader", "less-loader"]
      },
    ]
  }
}
module.exports = {
    
    
  plugins: [require("postcss-preset-env")]
}

处理静态资源文件

之前的方式

要处理jpg、png等格式的图片,我们也需要有对应的loader:file-loader
url-loader 和 file-loader 的工作方式是相似的,但是可以将较小的文件,转成base64的URI。

在webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loader 、url-loader、file-loader;
在webpack5开始,我们可以直接使用资源模块类型(asset module type),来替代上面的这些loader;

文件的命名规则

webpack 打包后的文件命名默认是一堆无意义的字符,不便于区分。webpack 也提供了办法,让我们可以控制打包后的文件名。
这个办法就是 PlaceHolders,它就像一个占位符,打包后占位符将转为与之对应的内容。
webpack给我们提供了大量的PlaceHolders来显示不同的内容:我们可以在文档中查阅自己需要的placeholder。
https://webpack.js.org/loaders/file-loader/

常用的 placeholder:

  • [ext]: 处理文件的扩展名;
  • [name]:处理文件的名称;
  • [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);
  • [contentHash]:在file-loader中和[hash]结果是一致的;
  • [hash:]:截短hash的长度,默认32个字符太长了;
  • [path]:文件相对于webpack配置文件的路径;

设置文件的存放路径

如果没有设置打包后的文件路径,则文件默认打包到 dist 根目录,散乱在一起。所以我们有必要对每个类型的文件进行规范打包后的位置。

在 loader 中设置文件路径:

use:[{
    
    
  loader: "url-loader", 
  options: {
    
    
    name: "[name].[hash:4].[ext]",
    outputPath: "img"
  }
}]

除了在 outputPath 属性中,其实也可以在指定 name 时直接设置,我们通常使用这种方式,因为很方便。

use:[{
    
    
  loader: "url-loader", 
  options: {
    
    
    name: "img/[name].[hash:4].[ext]"
  }
}]

文件路径除了上面针对某一类型文件设置,还可以全局设置。如果局部也设置了文件名,则全局文件名对该类型文件失效。

module.exports = {
    
    
  output: {
    
    
    filename: "bundle.js",
    path: path.resolve(__dirname, "./dist"),
    assetModuleFilename: "img/[name]_[hash:4][ext]"
  }
}

asset module type

资源模块类型(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,并且配置资源体积的上限来实现;

基本使用

不需要再使用 loader,所以不需要 use,直接用 type 声明类型就行。

module: {
    
    
  rules: [
    {
    
    
      test: /\.(jpe?g|png|gif|svg)$/,
      type: "asset",
      // 局部设置打包后的文件名和路径
      generator: {
    
    
          filename: "img/[name]_[hash:6][ext]"
        },
      // 打包大小限制
      parser: {
    
    
        dataUrlCondition: {
    
    
          maxSize: 100 * 1024
        }
      }
    }
  ]
}

汇中处理静态文件的配置

module: {
    
    
    rules: [
      {
    
    
        test: /\.(jpe?g|png|gif|svg)$/,
        type: "asset",
        generator: {
    
    
          filename: "im/[name]_[hash:6][ext]"
        },
        parser: {
    
    
          dataUrlCondition: {
    
    
            maxSize: 100 * 1024
          }
        }
      },
      {
    
    
        test: /\.(eot|ttf|woff2?)$/,
        type: "asset/resource",
        generator: {
    
    
          filename: "font/[name]_[hash:6][ext]"
        }
      }
    ]
  }
}

plugins

webpack 插件

mode

Mode配置选项,可以告知webpack使用响应模式的内置优化:

  • 默认值是production(什么都不设置的情况下);
  • 可选值有:‘none’ | ‘development’ | ‘production’;

这几个选项有什么样的区别呢?
image.png
配置 mode 其实也相当于一个预设,mode 每个值背后代表了很多配置。
image.pngimage.png

Babel

Babel

webpack 中使用 vue

webpack 中使用 vue

搭建本地服务器 devServer

webpack 搭建本地服务器

Source Map

SourceMap

resolve

resolve用于设置模块如何被解析,比如没写 js 后缀,webpack 怎么知道引入同名的 js 文件。

  • 在开发中我们会有各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库;
  • resolve可以帮助webpack从每个 require/import 语句中,找到需要引入到合适的模块代码;
  • webpack 使用库 enhanced-resolve 来解析文件路径;

webpack能解析三种文件路径:

  • 绝对路径
    • 由于已经获得文件的绝对路径,因此不需要再做进一步解析。
  • 相对路径
    • 在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录;
    • 在 import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径;
  • 模块路径
    • 在 resolve.modules 中指定的所有目录检索模块;
    • 默认值是 [‘node_modules’],所以默认会从 node_modules 中查找文件;
    • 我们可以通过设置别名的方式来替换初识模块路径,具体后面讲解 alias 的配置;

模块引入过程

如果是一个文件:

  • 如果文件具有扩展名,则直接打包文件;
  • 否则,将使用 resolve.extensions 选项中设置的扩展名作为文件扩展名进行补全再查找解析;

如果是一个文件夹:

  • 会在文件夹中根据 resolve.mainFiles 配置选项中指定的文件顺序查找;
    • resolve.mainFiles 的默认值是 [‘index’];所以进入一个目录,默认加载其中的 index 文件
    • 再根据 resolve.extensions 来解析扩展名;

extensions 和 alias 配置

extensions是解析到文件时自动添加扩展名:

  • 默认值是['.wasm', '.mjs', '.js', '.json']
  • 所以如果我们代码中想要不写后缀名自动加载 .vue 或者 jsx 或者 ts 等文件时,就要在 extensions 中添加;

另一个非常好用的功能是配置别名 alias:

  • 特别是当我们项目的目录结构比较深的时候,或者一个文件的路径可能需要 …/…/…/这种路径片段;
  • 我们可以给某些常见的路径起一个别名;
module.exports = {
    
    
	resolve: {
    
    
    extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"],
    alias: {
    
    
      // __dirname 表示当前文件所在目录,也就是项目根目录
      "@": path.resolve(__dirname, "./src"), 
      "js": path.resolve(__dirname, "./src/js")
    }
  }
}

配置文件环境区分

因为有些 webpack 配置是开发环境才有的,生产环境就不适合出现这些配置,所以需要根据不同环境使用不同的配置文件。

有两种方案:

  • 方案一:编写两个不同的配置文件,开发和生产环境,分别加载不同的配置文件即可;
  • 方案二:对配置文件进行拆分,拆分成公共模块,开发模块和生产模块。开发模块和生产模块分别合并公共模块,然后在 npm 脚本中指定使用哪个配置文件。
    • webpack.com.config.js
    • webpack.prod.config.js
    • webpack.dev.config.js

出入口以及模块解析,loader 都属于公共部分,devServer 则是只服务于开发环境,部分插件则要具体看功能,比如 copy 插件生产环境要有,开发环境不需要。

webpack-merge

模块化 webpack 的配置需要一个插件webpack-merge来合并基础配置文件和环境配置文件。
安装:npm i webpack-merge -D

const {
    
     merge } = require('webpack-merge') // webpack-merge 导出的对象不止一个,要解构赋值
const commonConfig = require('./webpack.com.config.js') 

// merge() 合并两个配置文件,若有重复内容,后面文件的内容会覆盖前面文件的内容
module.exports = merge(commonConfig, {
    
    
  mode: 'development'
})
"build": "webpack --config webpack.prod.config.js",
"serve": "webpack serve --config webpack.dev.config.js"

猜你喜欢

转载自blog.csdn.net/qq_43220213/article/details/129608205