【初学者笔记】前端工程化必须要掌握的 webpack

2022-12-10 11:21:14 浏览数 (1)


安装

现在学习的是 webpack4 的最新版,新建文件夹,npm init -y 初始化一下,然后执行下面命令进行安装,需要同时安装 webpackwebpack-cli

代码语言:javascript复制
npm install webpack@4.43.0 webpack-cli@3.3.12 -D

不建议全局安装 webpack, 因为全局安装会锁定版本,多个项目之间可能依赖的 webpack 版本不同, 所以还是建议项目内安装。

可以根目录下新建 .npmrc 文件,指定当前项目安装依赖的源地址

代码语言:javascript复制
registry=https://registry.npm.taobao.org

webpack 的零配置

webpack 宣传的一个噱头就是零配置构建, 也就是什么配置都不写就可以进行打包,虽然确实是可以执行,但其实是满足不了实际开发需要的。

先来随便跑一下,新建 src/index.js 文件。我们在 index.js 中 只写一行

代码语言:javascript复制
console.log("hello, webpack !")

然后执行

代码语言:javascript复制
npx webpack -v

npx 在当前项目下查找并启动 webpack 。可以看到根目录下生成 dist/main.js 文件。

建议在 package.json 文件中,添加构建命令,便于添加构建参数,也更加易读。

代码语言:javascript复制
  "scripts": {
    "dev": "webpack"
  },

虽然我只写了一行代码,但是打包出来却有这么多字符,看下面的进度条就知道后面还有很多了。

是因为 webpack 默认给做了很多兼容,这也是相对于其他构建工具,出现了很多的冗余代码。

但其实问题不大, webpack5 进行了一些优化,但是当模块变的越来越多时候,就没有什么太大改变了。

构建信息

看一下构建时都输出了什么信息

打包出来的文件内容有很多,但是主结构很简单,其实就是一个自执行函数,如下

代码语言:javascript复制
(function(modules) {
// todo
})({
  "./src/login.js": (function(module, exports) {
    eval("xxxxx")
  })
})

在这个结构中,可以看到自执行函数的参数是一个对象,对象的每个 keyvalue 都包含一个 chunk,也就是 代码片段

也可以包含多个 chunk , 被称为 chunkschunk组 ,还可以被称为 chunk Names

这一整个对象中可以包含多个 key:value,被称为依赖图谱。

构建后产生的资源文件, 称呼为 bundle 文件 。

几者遵循以下关系:

  • 一个 bundle 对应至少一个 chunks
  • 一个 chunks 对应至少一个 module
  • 一个 module 对应至少一个 chunk

一句话总结:

modulechunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:

我们直接写出来的是 modulewebpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle

webpack的配置文件

如果没有添加配置文件,会走它的默认配置,也就是所谓的零配置,如果添加了配置文件 webpack 会按照配置文件里的配置进行打包。

通常情况下 webpack 的配置文件的文件名叫做 webpack.config.js ,其实也可以通过配置修改成别的,但是基本上没什么必要。

修改 package.json 文件,在 script 中 新建命令即可

--config 指定配置文件

代码语言:javascript复制
 "warbler": "webpack --config ./warbler.config.js"

由于配置文件的内容是非常多的,语法也很难一时全记下来,所以我们可以通过一点点小技巧,就可以让配置文件具有语法提示。

首先安装 @types/webpack

代码语言:javascript复制
npm i -D @types/webpack

然后再配置文件中注明类型就可以了。

代码语言:javascript复制
// webpack.config.js
/**
 * @type {import('webpack').Configuration}
 */
module.exports = {
}

babel.config.js 也是支持同样方式的。

代码语言:javascript复制
npm i -D @types/babel__core
/**
 * @type {import('@babel/core').TransformOptions}
 */

vue.config.js 也是可以的,且不用安装其他包。

代码语言:javascript复制
/**
 * @type {import('@vue/cli-service').ProjectOptions}
 */

webpack的配置参数

webpack 是基于 nodejs 的, 所有的 nodejs 核心模块的 api 都可以直接引入使用。

entry

执行打包任务的入口,默认是 src/index ,支持相对路径, 也支持绝对路径。

代码语言:javascript复制
entry: "./src/index.js"

支持 stringarrobject 如果传入的是字符串,也会在构建期间被转换成对象结构,上面的代码和下面的代码结果是一样的。

代码语言:javascript复制
  entry: {
    main: "./src/index.js"
  },

支持 spa(单入口) 和 mpa(多入口)。多入口会输出多个 bundle 文件。

代码语言:javascript复制
  entry: {
    index: "./src/index.js",
    login: "./src/login.js",
    home: "./src/home.js"
  },

output

输出资源文件的信息 , 包括 存储位置文件名称

  • path : 存储位置,打包出来的文件放在哪里 默认是 dist 要求是绝对路径;
  • filename : 文件名称,打包出来的文件叫什么。
代码语言:javascript复制
  output: {
    path: path.resolve(__dirname, './dist'), 
    filename: "main.js"
  },

但是如果 entry 设置了多入口的话,就会产生多个 main.js 这样就会报错。

解决方法就是使用 占位符,语法如下

代码语言:javascript复制
filename: "[name].js"

作用就是 entry 中的 key 叫什么,打包后的 filename 就叫什么,多个入口就会对应多个出口了。

mode

打包模式,默认是生产模式,生产模式下会做代码压缩,摇树等操作。

  • development 开发模式
  • production 生产模式
  • none 默认

plugins

插件,插件的本质就是一个类。后面会单独讲解各种常用插件的配置。

plugins 的执行顺序是从上到下的。

代码语言:javascript复制
plugins:[]

module

模块,webpack 默认只支持 .js.json 文件,像我们平时常用的 .vue.png.scss.css.ts 等都是不支持的。

所以如果想要 webpack 支持其他类型的文件,就需要不同类型的 loader 进行解析。

下面这个配置的意思就是,当检测(test)到 .css 后缀文件的时候,使用(use) 什么 loader 来进行处理。

代码语言:javascript复制
// webpack.config.js
 module: {
    rules: [
      {
        test: /.css$/,
        use: ["style-loader", "css-loader"]
      },
    ]
  }
  • css-loader :把 css 模块序列化,让 webpack 认识 css 语法并可以生成 chunk
  • style-loader :把 css 整合在 html 文件的 head 标签的 style 中。

当多个 loader 作用于 一个模块的时候, 是按照自右向左的顺序执行的,也就是先执行 css-loader 在执行 style-loader

其实一个 loader 也可以搞定上面两件事,但是建议一个 loader 只做一件事情,也就是遵循 单一职责 原则。

也可以给 loader 书写配置,这个时候就要用对象的结构了。

代码语言:javascript复制
// webpack.config.js
 module: {
    rules: [
      {
        test: /.less$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: "css-loader",
            options: {
              modules: true
            }
          },
          {
            loader: 'postcss-loader',
          },
          {
            loader: 'less-loader',
          }
        ]
      }
    ]
  }
  • less-loader :把 less 语法 转化为 css 语法。
  • postcss-loaderpostcss 是一个 工具集 ,这个就十分强大了,postcsscss 的意义,就等同于 babel 对于 js 的意义,他自身作为插件,还可以携带插件。

resolveLoader

解析 loader, 告知 webpack 如何匹配 loader。当我们自定义一些 loader 的时候。可以通过绝对路径的方式导入,但是过于繁琐,当我们通过这个字段指定文件夹的时候,就可以像使用第三方 loader 一样直接写 loader 的名字了。

代码语言:javascript复制
// webpack.config.js
 resolveLoader: {
    // 默认是 node_modules
    modules: ["node_modules", "./myLoaders"]
  },

常用插件的常用配置

html-webpack-plugin

版本:4.5.2

  • template:使用指定模板生成 html
  • filename:打包后的 html 文件名。
  • chunks:打包后的 html 会包含哪些 chunk
代码语言:javascript复制
// webpack.config.js
  plugins: [
    // 自动生成 html 文件 , 引入 bundle 文件, 压缩 html
    new htmlWebpackPlugin({
      // 模板匹配
      template: "./src/index.html",
      filename: "index.html",
      chunks: ["index", 'login'],
    }),
  ]

如果要生成多个 html,只需要创建多个实例即可。

代码语言:javascript复制
// webpack.config.js
plugins: [
    // 自动生成html 文件 ,引入bundle文件,压缩html
    new htmlWebpackPlugin({
      // 模板匹配
      template: "./src/index.html",
      filename: "index.html",
      chunks: ["index", 'login'],
    }),
    // 自动生成html 文件 ,引入bundle文件,压缩html
    new htmlWebpackPlugin({
      // 模板匹配
      template: "./src/index.html",
      filename: "login.html",
      chunks: ["login"],
    }),
    // 自动生成html 文件 ,引入bundle文件,压缩html
    new htmlWebpackPlugin({
      // 模板匹配
      template: "./src/index.html",
      filename: "home.html",
      chunks: ["home"],
    }),
  ]

但是作为能懒则懒得程序员,是万万不可能写这种代码的,因为 webpack 的配置文件本身就是一个对象。所以我们当然可以利用一些方法来自动生成配置项。

首先要规定好结构,本例中每个文件夹中存放一个 index.htmlindex.js ,结构如下。当然可以自己随意定义结构,然后自己编写对应的函数体就可以了。

代码语言:javascript复制
├── src 
     ├── list 
     │   ├── index.html    
     │   └── index.js
     ├── login 
     │   ├── index.html    
     │   └── index.js
     ├── detail 
     │   ├── index.html    
     │   └── index.js
     ├── index 
     │   ├── index.html    
     │   └── index.js

配置如下,利用 setMap 方法自动生成 entryhtmlWebpackPlugins,这样每次添加文件都不用我们修改配置了。

代码语言:javascript复制
// webpack.config.js
const path = require('path')
const htmlWebpackPlugin = require("html-webpack-plugin")
// 模糊匹配路径
const glob = require('glob')

// 自动生成 entry 和 htmlWebpackPlugins
const setMap = () => {
  const entry = {};
  const htmlWebpackPlugins = []
  // 模糊匹配 src 目录下 任意目录下的 index.js 返回的是文件的绝对路径
  const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"))
  // 遍历匹配到的结果
  entryFiles.forEach((entryFile) => {
    // 获取到文件名
    const pageName = entryFile.match(/src/(.*)/index.js$/)[1]
    // 生成 entry
    entry[pageName] = entryFile
    // 生成 htmlWebpackPlugins
    htmlWebpackPlugins.push(
      new htmlWebpackPlugin({
        template: `./src/${pageName}/index.html`,
        filename: `${pageName}.html`,
        chunks: [pageName]
      }))
  })

  return {
    entry,
    htmlWebpackPlugins
  }
}

const { entry, htmlWebpackPlugins } = setMap()

// 配置文件
module.exports = {
  entry,
  // 输出资源文件的信息
  output: {
    // 存储位置
    path: path.resolve(__dirname, './dist'),
    // 文件名称
    filename: " [name].js"
  },
  // 打包模式
  mode: "development",
  // 插件
  plugins: [
    ...htmlWebpackPlugins,
  ],
}

postcss

postcss 也有他自己的配置文件,根目录下新建 postcss.config.js ,也是个模块,通过require("autoprefixer") 安装插件。

代码语言:javascript复制
// postcss.config.js
module.exports = {
  plugins: [
    require("autoprefixer"),
    require("cssnano")
  ]
}

postcss的插件autoprefixer

autoprefixer 自动给 css 属性加前缀,使用 can i use 的数据。

但是这样还不会生效,需要设置 browserslist目标浏览器集合 ,也就是 需要兼容的浏览器版本,用到浏览器集合的工具会根据 browserslist 的描述, 针对性的输出兼容性的代码。

browserslist 的设置有两种方式。

你可以直接在 package.json 文件中添加属性如下

代码语言:javascript复制
  "browserslist": [
    ">1%",
    "last 2 versions"
  ]

也可以根目录下新建 .browserslistrc 文件,直接写,什么括号都不用。

代码语言:javascript复制
// .browserslistrc
>1%
last 2 versions
  • >1% : 全球市场占有率 大于1%的浏览器。
  • last 2 versions : 兼容浏览器的最近两个大版本。

可以使用 npx browserslist 查询兼容的浏览器版本。

postcss的插件cssnano

压缩 css 代码。

mini-css-extract-plugin

css 代码抽离出来单独放到一个文件里,可以指定文件名,支持绝对路径,会自动生成文件夹。需要在写 loader的位置, 使用 minicss.loader 替换 style-loader

代码语言:javascript复制
// webpack.config.js
const minicss = require("mini-css-extract-plugin")
module.exports = {
  plugins: [
    new minicss({
      filename: 'style/index.css'
    })
  ],
  module: {
    rules: [
       {
        test: /.less$/,
        use: [
          minicss.loader,
          {
            loader: "css-loader",
            options: {
              modules: true
            }
          },
          {
            loader: 'postcss-loader',
          },
          {
            loader: 'less-loader',
          }
        ]
      }
    ]
  }
}

clean-webpack-plugin

每次打包之前清理打包目录。

代码语言:javascript复制
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  plugins: [
     new CleanWebpackPlugin()
  ],
}

实现自定义loader

loader的引用

根目录下新建 myLoaders/a-loader.js 文件,然后在 module 中需要使用绝对路径的方式引入。

代码语言:javascript复制
// webpack.config.js
module: {
    rules: [
      {
        test: /.js$/,
        use: [
         {
            loader: path.resolve(__dirname, './myLoaders/a-loader.js'),
          },
        ]
      },
    ]
  }

loader的结构

loader 就是一个函数。接收一个参数 sourcesource 就是符合 test 中正则的模块内容,本例中就是 .js 后缀的文件内容。

  • 这个函数不可以是 箭头函数,因为 webpack 提供了 loader api 都是挂载到 this 上的。
  • 这个函数必须有 返回值,否则会报错。
代码语言:javascript复制
// a-loader.js
module.exports = function(source) {
  console.log('


	

0 人点赞