likes
comments
collection
share

webpack 的基本认识和使用

作者站长头像
站长
· 阅读数 78

webpack 官网地址

什么是 webpack ?

  • webpack is static module bundler for modern Javascript applications
  • webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具

词法解释

  • 打包工具(bundler): webpack 可以对前端源代码进行打包,所以它是一个打包工具
  • 静态的(static): 将代码打包成最终的静态资源(部署到静态服务器)
  • 模块化(module): webpack默认支持各种模块化开发 ESModule,CommonJS,AMD等
  • 现代的(modern): 前面说过,正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和法展

webpack 的基本认识和使用

这张图很形象,把一些浏览器不认识的文件通过 webpack 的转换成浏览器认识的文件。

webpack 安装

需要安装 webpackwebpack-cli

webpack: 核心代码

webpack-cli: 用来解析命令行的参数,然后运行 webpack(比如:webpack-cli 就是对终端中的指令进行读取 比如:webpack --entry ./src/index.ts等等指令)。

# 全局安装
npm install webpack webpack-cli -g

# 局部安装
npm install webpack webpack-cli -D

webpack 的测试文件搭建

创建几个测试文件

// src/utils/math.js

function sum(num1, num2) {
  return num1 + num2;
}

export default sum;
// src/utils/format.js

function upper(str) {
  return str.toLocaleUpperCase();
}

module.exports = {
  upper,
};
// src/index.js

import sum from "./utils/math";
const { upper } = require("./utils/format");

console.log(sum(10, 20));
console.log(upper("copyer"));

在这里可以顺手测试一下,创建一个 html 文件来测试下,这个 src/ index.js,是否可以在浏览器上编译成功?

答案显然易见: 肯定是不可能的。因为为浏览器不识别 ES ModuleCommonJS 这个模块的导入导出。

所以这里就开始借助 webpack 来进行转化了。

webpack 打包指令

方式一:借助 vscode 终端,直接运行。

# 打开终端

webpack # 查找全局中的 webpack

npx webpack # 查找当前项目中的 webpack 

方式二:使用 package.json 中命令执行(本质都是一样的)

 "scripts": {
    "build": "webpack",    //这里会自动的去node_module/.bin文件下面去找
    "watch": "webpack --watch", // 文件被修改,自动生成新的打包,但是不会刷新浏览器
    "server": "webpack server", // 文件被修改,自动生成新的打包,也会刷新浏览器,需要借助(webpack-dev-server)
  },

针对 server 命令,自动打包,打包的文件存在放 服务器 中,当通过服务器地址打开,读取的是服务器中的 dist 文件,在前端代码中是没有生成 dist 文件夹,所以当需要发布的时候,还是需要手动打包,生成dist文件夹,发布部署。

针对 watch 命令,也可以在 webapck.config.js 配置中配置 watch:true,效果与此类似。

执行完打包之后,就在当前路径下,生成一个 dist 文件夹,该文件夹下创建 main.js 文件。这里是走的 webpack 中的默认配置。如果需要修改其默认配置的话,就是创建一个 webpack.config.js 文件来覆盖默认配置。

// 当然不一定是 webpack.config.js 文件名,还可以是其他的,只是没有必要修改
"scripts": {
    "build": "webpack --config copyer.config.js", // 就是去读取 copyer.config.js 配置文件了
  },

webpack 的构建依赖图

在学习 webpack.config.js 配置之前,先来理解一下 webpack 的构建依赖图,有助于后面配置项的理解。

简单来说,webpack 从入口文件解析,会拿到入口文件中的所有模块,在各自模块内部又有对应的依赖,webpack也会进行解析,这样不停的依赖,就会形成一个图。只要一个文件没有在这个依赖图里面,就不会对其进行解析。

就比如这样:

-- src
---- utils
------ math.js
------ format.js
------ b.js
---- index.js

在 入口文件 index.js 中引入了 math.js 和 format.js 两个文件,但是就是没有引用 b.js 文件,那就就会形成类似依赖图:

webpack 的基本认识和使用

b.js 文件就没有出现在 webpack 的依赖图中。

总结:就是从入口文件开始查找,如果存在模块依赖,就会添加到依赖图中,模块又会依赖其他的模块,依次循环下去,直到构建成一个完成的依赖图。(这也是 webpack 性能慢的地方之一)

webpack.config.js 基本配置

webpack.config.js 就是用来覆盖 webpack 中的默认配置,那么常用的配置有哪些呢?

entry

入口文件路径配置,也就是让 webpack 知道从哪里开始打包(默认是 ./src/index.js ),从入口文件开始构建 webpack 依赖图。

// webpack.config.js

module.exports = {
  entry: './src/index.js',
}

output

告诉 webpack 在哪里输出它所创建的静态文件(bundle),以及如何命名这些文件。默认值为 ./dist/main.js

// webpack.config.js

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "./build"),  //打包的路径
    filename: "build.js",  //打包过后的文件名
  },
};

loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。如果想要支持别的格式文件,就需要使用 loader 来进行转化,转换成有效识别的文件(模块)。

注意:webpack 配置 loader 的属性名为 module,module 对象下有个 rules 属性用来进行配置。

  • module.rules中允许我们配置多个 loader
  • 这种方式可以更好的配置 loader,也可以清晰的看出使用哪些 loader

module.rules 的配置规则:

  • rules 属性对应的值是一个数组: [ Rule ]

  • 数组中存放的是一个一个的 Rule,Rule 是一个对象,对象中可以设置多个属性

    • test 属性: 对资源进行匹配,一般是用正则的方式。

    • use 属性: 对应的值是一个数组: [ useEntry ]

      1. useEntry 是一个对象,可以通过对象来设置其他的属性.
      2. loader 必选参数,对应的是一个字符串
      3. options 可选参数,值是一个字符串或者对象,会被传入到 loader 中

接下来就来看看有哪些常见的 loader,当然使用的 loader,需要自行安装。

css-loader

pnpm install css-loader -D

可以将 css 文件看成是一个模块。是需要通过 import 来加载这个模块的

module.exports = {
  module: {
    rules: [{
        test: /.css$/,
        //loader: 'css-loader'    //写法一
        //use: ['css-loader']     //写法二 (推荐)
        use: [
          //完整写法
          { loader: "css-loader" },
        ],
      }]
  }
};

style-loader

pnpm install style-loader -D

在上面的我们已经配置了css-loader,但是在实际的效果中是没有生效的

原因:

  • css-loader 只是负责将 css 文件进行解析,并不会将解析之后的css 插入到页面
  • 如果希望再完成插入 style 的操作,那么我们还需要另外一个 loader,就是 style-loader
module: {
  rules: [
    {
      test: /.css$/,
      use: ['style-loader', 'css-loader'] // 从右至左的加载
    }
  ]
}

less-loader

npm install less -D
npm install less-loader -D
module: {
  rules: [
    {
      test: /.less$/,
      //先转化成css, 使用css-loader加载,最后用style-loader来添加到页面中
      use: ['style-loader', 'css-loader', 'less-loader']
    }
  ]
}

postcss-loader

理解 postcss,可以去看我的另外一篇文章,可能会加深你对 postcss 的认识和基本使用

postcss 的正确认知

pnpm install postcss-loader -D

# 安装 postcss 插件,预设
pnpm install postcss-preset-env -D
const postcssPresetEnv = require("postcss-preset-env");

module: {
  rules: [
    {
      test: /.less$/,
      use: ['style-loader', 'css-loader', 'less-loader', {
        loader: 'postcss-loader',
        options: {
          //可以提出一个单独的文件
          postcssOptions: {
            plugins: [postcssPresetEnv()]
          }
        }
      }]
    }
  ]
}

当使用 postcss-loader 的时候,会先查找有不有自己 options 选项,如果没有就去查找 postcss.config.js 这个配置文件。所以可以把options 选项里面的内容提出来。

// postcss.config.js

module.exports = {
   plugins: [postcssPresetEnv()]
}

// webpack.config.js
module: {
  rules: [
    {
      test: /.less$/,
      use: ['style-loader', 'css-loader', 'less-loader', 'postcss-loader']
    }
  ]
}

file-loader

file-loader 的作用就是帮助我们处理 import / require() 方式引入的一个文件资源(比如说:图片),并且会将它放到我们输出的文件夹中。

pnpm install file-loader -D
module: {
  rules: [
    {
      test: /.(jpe?g|png|gif|svg)$/i,
      use: [
        {
          loader: 'file-loader'
          options: {
            //在打包的文件夹中创建一个images文件夹,用来保存图片的
            outputPath: 'images',
            //给图片取名字
            name: "[name]_[hash:8].[ext]"
          }
        }
      ]
    }
  ]
}

url-loader

url-loader 跟 file-loader 的工作原理是一样的

url-loader的区别: 就是对图片有限制,对图片较小的进行 base64 编码

默认情况下: url-loader对所有图片都会进行base64编码

  • 小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程
  • 而大的图片也进行转换,反而会影响页面的请求速度
pnpm install url-loader -D
module: {
  rules: [
    {
      test: /.(jpe?g|png|gif|svg)$/,
      use: [
        {
          loader: 'url-loader',
          options: {
            //在打包的文件夹中创建一个images文件夹,用来保存图片的
            outputPath: 'images',
            //给图片取名字
            name: "[name]_[hash:8].[ext]",
            //配置limit
            limit: 100 * 1024
          }
        }
      ]
   }
  ]
}

限制小于 100kb 的,才会转化成 base64。

asset module type

  • 在webpack5之前,加载这些资源是使用的 url-loader file-loader
  • 在webpack5开始,可以直接使用资源模块类型 (asset module type),来代替上面的类型

资源模块类型,通过添加模块类型,来代替 loader

asset/resource 发送一个单独的文件并导入URL。代替 file-loader

asset/inline 导出一个资源的data URl。代替使用 url-loader

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

module: {
  rules: [
    {
      test: /.(jpe?g|png|gif|svg)$/,
      type: 'asset',
      // 添加images,存放图片
      generator: {
        filename: 'images/[name]_[hash:8][ext]'
      },
      //限制图片的大小,小的进行转化   
      parser: {
        dataUrlCondition: {
          maxSize: 100 * 1024
        }
      }
    }
  ]
}

babel-loader

在前端中有着很多的新语法 , 比如: ES6, TS,JSX 等,他们都需要 babel 的转化 js

pnpm install @babel/core babel-loader @babel/preset-env -D
# @babel/core babel的核心依赖
# @babel/preset-env 内置了很多预设
module: {
  rules: [
    {
      test: /.js$/,
      use: [
        {
          loader: "babel-loader",
          options: {
            //预设
            presets: ["@babel/preset-env"],
          },
        },
      ],
    }
  ]
}

plugin

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

就是在 webpack 执行的生命周期(hook)过程中做一些额外的操作。

CleanWebpackPlugin

每次打包之后,需要手动删除,再次进行打包,这个手动删除的过程,我们就可以借助该插件帮助我们来完成。

pnpm install clean-webpack-plugin -D
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  plugins: [
    new CleanWebpackPlugin()
  ]
}

HtmlWebpackPlugin

插件将为你生成一个 HTML5 文件, 在 body 中使用 script 标签引入你所有 webpack 生成的 bundle

const HtmlWebpackPlugin = require('html-webpack-plugin');

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

插件很多,用着很香,用的时候再去查找。

mode

通过选择 development, productionnone 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。

其默认值为 production

module.exports = {
  mode: 'production',
};
选项描述
development会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名。
production会将 DefinePluginprocess.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePluginFlagIncludedChunksPluginModuleConcatenationPluginNoEmitOnErrorsPluginTerserPlugin
none不使用任何默认优化选项(也就是 webpack 不会自动添加优化项)

devServer

webpack 内部集成了 webpack-dev-server,一个本地服务器(express),用于开发阶段使用,可以通过 webpack serve 命令启动本地服务器。

该服务器实现了什么功能呢?

  1. 解决了每次修改源代码,需要重新打包才能看效果的问题。服务器内部会进行打包,但是会把打包的内容放在内存中,没有输出文件(没有涉及到文件的生成,性能就比较高),然后通过地址打开,就是读取内存中的打包内容,当修改源代码时,就会修改内存中的内容,看到实时的效果。
  2. 也可以解决跨域问题,利用服务器与服务器之间没有同源策略问题。
  3. ...

static

contentBase 被 static 代替了,更好的见名知意。

webpack-dev-server 只会对 webpack 的依赖图中的文件进行处理,但是有些文件没有在依赖图里面,那么该如何做呢?

那就手动告诉它:

module.exports ={ 
  devServer: {
    // 就是服务器中处理静态资源,如果不配置,默认就是 ['public']
    static: ['public', 'css']
  }
}
错误写法:<script src='./public/a.js'></script>
正确写法:<script src='./a.js'></script>

就是当文件没有在依赖图里面,想要加载的话,就从public文件夹里面去找

hot

开启热更新

module.exports ={ 
  devServer: {
    hot: true
  }
}

proxy

代理服务器配置

module.exports = {
  devServer: {
    proxy: {
      target: 'http://localhost:9999' // 代理的目标
      pathRewrite: { // 对路径的重写
        "^/api": ''     
      },
      secure: false, // 针对代理,针对 https 协议是需要证书,设置false,就不需要证书了
      changeOrigin: true // 是否改变源(用于服务端对源的验证)
    }
  }
}

port

端口设置(1024 以上的端口)

open

是否打开浏览器。默认为false,true,就会打开浏览器

compress

是否为静态文件开启 gzip。 默认为 false,true 就会压缩

historyApiFallback

单页面应用(SPA),当进行路由跳转时,手动刷新页面会找不到内容,从而404。

地址 / 匹配的是 index.html

如果地址改变,/xxxx,刷新之后就找不到对应的 html 文件,就会 404

把 historyApiFallback 设置为 true, 就能解决这个问题。如果地址为 http://localhost:3000/xxx, 刷新之后,还是直接会去读取 http://localhost:3000, 从而得到静态资源。

结语

webpack 的配置很多,上面只是列举了在学习过程中的一些基础且常见的配置,后续用到继续学习。

转载自:https://juejin.cn/post/7231594699093344317
评论
请登录