webpack5介绍及基本使用(一)


前言

此篇文章将简单介绍webpack的基本概念和使用方法,并通过实例演示如何使用webpack来构建一个不依赖前端框架的项目,同时也会介绍一些常用的webpack插件和优化技巧,希望本篇文章能够帮助大家更好地使用webpack进行项目开发。

简单介绍

常见打包工具有Grunt、Gulp、Parcel、Webpack、Rollup、Vite、ESBuild 等等,这篇文章只专注于webpack就不去对比其他打包工具了。webpack优点在于有着完善的基础建设,而且得益于webpack5的升级,对比以往版本也有了相当大的提升(极致编译速度,一文搞定webpack5升级),虽然它的保留率在逐年下降,但大概率还是目前使用率最多的构建工具(查看StateOfJS的调查结果)。

1. webpack作用

  • 代码编译
    sassTypeScript等代码虽然极大的提高了开发效率,但是本身并不被浏览器所识别,需要我们对其进行编译和打包,变成浏览器识别的代码
  • 模块化
    使用高级语法 ES6 和 CommentJS,进行前端模块化开发,webpack 将高阶语法打包成浏览器识别的代码
  • 优化加载速度
    压缩和合并代码来提高加载速度,压缩可以减少文件体积,代码合并可以减少http请求

2. webpack语法规范

webpack是用Node封装的,可以使用Node的语法,Node.js 的模块规范就是 CommonJS 模块规范,ES6模块化(ES Modules / ESM)是浏览器端和服务器端通用的规范,关于规范的区别这里也不展开了。

安装依赖及基本配置

下面正式进入webpack5的实战环节,我使用Node的版本为14.21.3,当然最好还是更新到比较新的Node版本进行webpack5的开发,不过Webpack 5 运行于 Node.js v10.13.0+ 的版本,我这里使用较低版本也是为了兼顾大多数的开发环境。首先创建基础的网页,html文件中放入图片及引用css、script等,目录结构如下:

├── webpack5 (项目根目录)
    ├── image (项目图片资源目录)
    │    └── 略
    ├── favicon.ico(网页图标)
    ├── index.css
    ├── index.html
    └── index.js
  1. 初始化一下项目:(会生成一个package.json文件)
npm init -y
  1. 安装webpack:
npm i webpack webpack-cli -D
  1. 创建运行指令:

在package.json中的scripts中创建build命令,webpack默认会找配置文件“webpack.config.js”

  "scripts": {
    "build": "webpack"
  },
  1. 编写默认配置文件:webpack.config.js
const path = require('path'); //核心模块
module.exports = {
  entry: 'index.js', //配置入口文件
  output: {
    filename: 'bundle.js', //打包出来的文件名称
    path: path.resolve(__dirname, './dist'), //打包后的路径,必须是绝对路径
    //path.resolve()拼接路径为绝对路径
    //__dirname指当前文件根目录,dist是打包出来的目录名称
  },
  mode: 'production', //production生产模式(代码压缩),development开发模式(不压缩代码)
  module: {}, //配置loader
  plugins: [] //配置plugins
};
  1. 打包

运行了webpack打包命令后多了dist目录,类似下面这样子就证明成功使用webpack5打包了。
在这里插入图片描述

查看bundle.js文件会发现只有对index.js代码压缩后的内容,这是正常的,因为我们只是指定了“index.js”作为入口起点(entry point) ,让 webpack 使用其作为一个“模块”构建项目,其他资源文件(如html、css)要引入到 js 里面才能使用。
另外webpack本身就能打包 js 代码,但是其他类型的如css,图片等格式的文件没法打包,就需要在引入资源后利用第三方的模块进行打包,这就是loader(加载器) 的作用了,另外配置了多个loader,其按照从右到左(或从下到上)地取值(evaluate)/执行(execute)。

loader (加载器)

1、处理 ts / js 文件

npm i babel-loader @babel/core @babel/preset-env core-js @babel/plugin-transform-runtime @babel/preset-typescript -D
  • babel-loader: 使用 babel 将ES6+语法转换为 ES5
  • @babel/corer: babel 编译的核心包
  • @babel/preset-env: babel 编译的预设(告诉babel使用那种转码规则进行文件处理)
  • core-js(通过 Polyfill 方式在目标环境中添加缺失的特性,让低版本的浏览器也可以支持promise这些新的API)
  • @babel/plugin-transform-runtime 将所有文件用到的公用辅助函数抽离出来形成一个第三方文件,自动引入辅助函数,减少包的体积
  • @babel/preset-typescript(支持ts语法)
  module: {
    rules: [
      {
        test: /\.js|ts$/,
        use: {
          loader: 'babel-loader',
          options: {
            exclude: /node_modules/, //除了这个文件夹之外的文件
            presets: [
              [
                '@babel/preset-env',
                {
                  useBuiltIns: 'usage', //按需引入(根据配置的目标环境找出需要的polyfill进行部分引入)
                  corejs: 3,
                  // 设置兼容目标浏览器版本,这里可以不写,将使用 browserslist 配置文件
                  // targets: '> 1%, not dead',
                },
              ],
              '@babel/preset-typescript',
            ], //预设
            // 插件比预设先执行
            plugins: ['@babel/plugin-transform-runtime'],
            cacheDirectory: true, //开启缓存,第二次编译时,没改的部分使用缓存
          },
        },
      },
    ]
  }
1.1 babel配置文件

可以把babel-loader的配置单独抽离出来,配置成一个全局配置文件babel.config.js,也可以使用**.babelrc**文件进行颗粒度更细的配置,这两者可以一起使用,也可以独立使用,当同级存在时后者优先级更高,详细可看babeljs官网,或者理解 babel.config.js 和 babelrc

1.2 browserslist配置文件

在package.json中添加browserslist:

  "browserslist": [
    ">1%",
    "last 10 versions",
    "not dead"
  ]

2、处理图片、字体、图标等资源文件

在这里插入图片描述

  module: {
    rules: [
        {
        test: /\.(png|jpe?g|gif|webp|svga)(\?.*)?$/,
        type: 'asset',
        generator: {
          filename: 'imgs/[name].[contenthash:4][ext]',
        },
        parser: {
          // 生成Data URI 的条件,视为 inline 模块类型,否则会被视为 resource 模块类型
          dataUrlCondition: {
            // 当资源模块不超过 4kb 时,生成 DataURI,超过 4kb 时,单独打包成文件
            maxSize: 4 * 1024, // 4b
          },
        },
      },
      {
        test: /\.(eot|ttf|otf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[contenthash:4][ext]',
        },
      },
    ]
  }

3、处理less、scss、css文件

3.1 css 文件分离

在这里插入图片描述

npm i style-loader mini-css-extract-plugin
3.2 处理 less / sass 文件
  • 处理sass
npm i sass sass-loader -D
  • 处理less
npm i less less-loader -D
3.3 css3 属性添加兼容前缀
npm i postcss-loader autoprefixer -D

另外还需要有一份要兼容浏览器的清单,让postcss-loader知道要加哪些浏览器的前缀,browserslist配置文件其实就是这份清单。

3.4 css 的压缩
npm i css-minimizer-webpack-plugin -D
3.5 示例代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css$/,
        //use里面执⾏顺序是从右往左的顺序,先模块化再引入
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              url: true, //默认为true,可以处理css中的url图片路径
              esModule: false,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer'],
              },
            },
          },
        ],
      },
      {
        test: /\.scss$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer'],
              },
            },
          },
          'sass-loader',
        ],
      },
      {
        test: /\.(png|jpe?g|gif|webp|svga)(\?.*)?$/,
        type: 'asset',
        generator: {
          filename: 'imgs/[name].[contenthash:4][ext]',
        },
        parser: {
          // 生成Data URI 的条件
          dataUrlCondition: {
            // 当资源模块不超过 4kb 时,生成 DataURI,超过 4kb 时,单独打包成文件
            maxSize: 4 * 1024, // 4b
          },
        },
      },
      {
        test: /\.(eot|ttf|otf|woff2?)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[name].[contenthash:4][ext]',
        },
      },
      // ...
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      // 长期缓存:使用 filename: "[contenthash].css" 启动长期缓存
      filename: devMode ? 'css/[name].css' : 'css/[name].[contenthash].css',
    }),
  ],
};

这里其实会有个问题,当我们指定mode的模式为“production”后,在index.js文件中打印process.env.NODE_ENV的值确实是“production”,然而在webpack.config.js文件中打印process.env.NODE_ENV的值会发现值是’undefined’,这里先卖个关子,解决办法留到后面的环境变量章节讲述。

在这里插入图片描述

4、处理html文件

webpack需要把最终构建好的静态资源都引入到一个html文件中,这样才能在浏览器中运行

npm i html-webpack-plugin html-loader@0.5.5 -D
  • html-webpack-plugin 插件的作用
    以自己的index.html为模板生成一个 HTML 文件,并且引入所有webpack生成的资源链接
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: template: path.resolve(__dirname, './index.html'), //模板
      filename: 'index.html', //打包的 HTML 文件名字
      favicon: path.resolve(__dirname, './favicon.ico'), //网页图标
    }),
  ]
}
  • html-loader的作用
    由于webpack需要解析html模板,所以需要html-loader解析html文件。这里是目前唯一一个需要指定安装版本的包,因为其最新的v5.0.0的包支持的最低 Node.js 版本为18.12.0,查看各种版本的下载数量可以到npm官网查看。
  module: {
    rules: [
   	  //...
      {
        test: /\.html$/i,
        use: [
          {
            loader: 'html-loader',
            options: {
              esModule: false,
            },
          },
        ],
      },
      //...
    ]
  }

因为webpack配置的打包入口只有index.js一个,记得在index.js文件中引入需要的资源,如

import './index.scss';
import './index.ts';

再次运行webpack打包,就可以打包出来如下图所示的目录文件了,本地运行这个index.html文件,能正常访问图片、应用样式以及控制台成功执行输出等就证明成功打包了。

在这里插入图片描述

然而其实这里有点小问题,一是资源报错找不到,二是重复加载资源。

在这里插入图片描述

针对其一,原因是原来的html页面引入了这些资源,现在将其去掉即可。
针对其二,由于在HtmlWebpackPlugin插件中配置了favicon选项,虽然将favicon.ico文件从根目录拷贝到打包目录下了,但是这个插件并没有智能地将原本html里引用的favicon.ico去掉,导致重复加载了,有两个办法:一是直接删掉html里引入的favicon.ico,二是使用copy-webpack-plugin这个插件,webpack只会将里面的资源复制到打包目录下,不会对其进行压缩编译等处理。

npm i copy-webpack-plugin -D
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
  plugins: [
    // ...
    // public目录被webpack打包,一开始是什么样子,打包后还是什么样子
    new CopyPlugin({
      patterns: [
        {
          from: 'public', //拷贝的目录
        },
      ],
    }),
  ],
};

将favicon.ico从根目录移动到新创建的public文件夹中,再次运行打包命令即可。


总结

写到这里这篇文章篇幅实在是太长了,所以还有几章关于webpack的环境变量插件优化等内容我还是另开一篇文章讲述,后续文章请点击查看。
欢迎各位看官【点赞】、【收藏】,多多支持!也欢迎您【评论】留下宝贵意见,共同探讨一起学习~。

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值