webpack5介绍及基本使用(二)


前言

这篇文章接着上一篇文章 webpack5介绍及基本使用(一)


配置文件

首先整理一下配置文件,考虑到如果所有配置都塞在webpack.config.js配置文件里面可能就显得太臃肿了,所以有些配置也可以通过单独的配置文件来进行定义。

1、babel配置文件(babel.config.js)

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: 3,
        // 设置兼容目标浏览器版本,这里可以不写,将使用 browserslist 配置文件
        // targets: '> 1%, not dead',
      },
    ],
    '@babel/preset-typescript',
  ], //预设,也可通过 babel.config.js 和 babelrc配置文件配置
  plugins: ['@babel/plugin-transform-runtime'],
};

原本的babel-loader位置就可以简化为

// ...
      {
        test: /\.js|ts$/,
        exclude: /node_modules/, //除了这个文件夹之外的文件
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: false, //开启缓存,第二次编译时,没改的部分使用缓存
          },
        },
      },
// ...

2、browserslist配置文件(.browserslistrc)

上一章使用的是在package.json中添加browserslist的方法,但是建议在项目根目录单独使用 .browserslistrc 文件来指定目标环境。browserslist 配置文件默认同时作用于@babel/preset-env(JS 语法转换)及 PostCSS 插件,PostCSS使用 autoprefixer 插件用于解析 CSS 并使用 Can I Use网站 中的值将供应商前缀添加到 CSS 规则中。

> 1%
last 2 versions
not dead

1% //市场占有率
last 2 versions //每个浏览器的最后两个版本
default:Browserslist 的默认配置( >0.5%, last 2 versions, Firefox ESR, not dead )
5%:通过全局情况统计选择的浏览器版本,可以使用 >=,<和 <=
dead:24个月内没有官方支持或更新的浏览器

  • 浏览器兼容性配置可以在 Browserslist官网 查看
  • 查看当前项目兼容哪些版本
npx browserslist

3、postcss-loader配置文件(postcss.config.js)

3.1 数组写法

module.exports = {
  plugins: [require('autoprefixer')],
};

3.2 对象写法

module.exports = {
  plugins: {
    autoprefixer: {},
  },
};

原本的postcss-loader位置就可以简化为

// ...
      {
        test: /.(css|scss)$/, //匹配 css和sass 文件,
        //use里面执⾏顺序是从右往左的顺序,先模块化再引入
        //use:['style-loader','css-loader']
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              url: true, //默认为true,可以处理css中的url图片路径
              esModule: false,
            },
          },
          'postcss-loader',
          'sass-loader',
        ],
      },
// ...

完善配置

1、环境变量

  • process
    process 对象是一个 global (全局变量),提供有关信息,控制当前 Node.js 进程。作为一个对象,它对于 Node.js 应用程序始终是可用的,故无需使用 require()。
  • process.env
    process.env属性返回一个包含用户环境信息的对象。在node环境中,当我们打印process.env时,发现它并没有NODE_ENV这一个属性。实际上,process.env.NODE_ENV是在package.json的scripts命令中注入的,也就是NODE_ENV并不是node自带的,而是由用户定义的,至于为什么叫NODE_ENV,应该是约定成俗的吧。

上一章遇到了关于process.env.NODE_ENV的值为"undefined"的问题,没办法区分开发、测试还是生产环境,这时候就需要借助跨平台的cross-env这个包,它能够提供一个设置环境变量的scripts,这样我们就能够以unix方式设置环境变量,然后在windows上也能够兼容。

npm i cross-env -D

在package.json中配置运行指令

{
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack",
    "build": "cross-env NODE_ENV=production webpack"
  }
}

当我们这么设置了之后我们会发现在webpack.config.js文件中已经能正确获取到process.env.NODE_ENV的值了,但是需要打包的js文件打印的process.env.NODE_ENV值却还是跟随webpack的 mode 设置,并且各种环境下webpack配置肯定有所不同,而mode的值并不能自由定义成各种字符串,无法适应实际复杂的场景。

在这里插入图片描述

1.1 根据不同环境加载配置文件

通过命令指定webpack配置:webpack --config 指定配置文件名称,若修改了webpack配置需要重现运行npm run 脚本名称。

将 webpack.config.js 拆成三个文件

  1. webpack.base.js(基础文件)
  2. webpack.dev.js(开发配置文件)
  3. webpack.pro.js(生产配置文件)
├── webpack5(项目根目录)
    ├── build(webpack打包配置文件)
    │    └── webpack.base.js
    │	 ├── webpack.dev.js
    │	 └── webpack.pro.js
    │── dist(打包出来的文件)
    │	 └── 略
    │── node_modules
    │	 └── 略
    │── public
    │	 └── favicon.ico(网页图标) 
    │── src
   	│    │── images(图片文件)
    │	 │		└── 略
    │	 ├── index.html
	│    ├── index.scss
	│    └── index.ts
    ├── .browserslistrc(browserslist配置文件)
    ├── babel.config.js(babel配置文件)
    ├── package-lock.json
    ├── package.json
    └── postcss.config.js(postcss-loader配置文件)
1.1.1 webpack.base.js

开发与生产公共的配置定义在 webpack.base.js 文件里,基本上将之前的 webpack.config.js 内容复制过来就行,然后由于结构目录的改动,所以需要检查一下文件中定义的路径;另外对于生产环境下打包出来的文件可以加入 contenthash 进行命名,这个便于浏览器缓存,在后面的优化章节将展开讲述。

const path = require('path'); //核心模块
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
  entry: path.resolve(__dirname, '../src/index.js'),
  output: {
    filename: devMode ? 'js/[name].js' : 'js/[name].[contenthash].js', //打包出来的文件名称
    path: path.resolve(__dirname, '../dist'), //打包后的路径,必须是绝对路径
    clean: true, //在生成文件之前清空 output 目录
  },
  //   mode: 'production', //production——代码压缩,development——不压缩代码
  module: {
    rules: [
      {
        test: /\.css$/,
        //use里面执⾏顺序是从右往左的顺序,先模块化再引入
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              url: true, //默认为true,可以处理css中的url图片路径
              esModule: false,
            },
          },
          'postcss-loader',
        ],
      },
      // 分开解析,避免让sass-loader再去解析css文件
      {
        test: /\.scss$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          '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]',
        },
      },
      {
        test: /\.html$/i,
        use: [
          {
            loader: 'html-loader',
            options: {
              esModule: false,
            },
          },
        ],
      },
      {
        test: /\.js|ts$/,
        exclude: /node_modules/, //除了这个文件夹之外的文件
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: false, //开启缓存,第二次编译时,没改的部分使用缓存
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../src/index.html'), //模板
      filename: 'index.html', //打包的 HTML 文件名字
      favicon: path.resolve(__dirname, '../public/favicon.ico'), //网页图标
    }),
    new MiniCssExtractPlugin({
      // 长期缓存:使用 filename: "[contenthash].css" 启动长期缓存
      filename: devMode ? 'css/[name].css' : 'css/[name].[contenthash].css',
    }),
  ],
};
1.1.2 webpack.dev.js

之前运行的打包不能够实时直观地看到修改后内容,体验并不好,所以在开发模式下我们可以用 webpack-dev-serve 这个插件来启动本地服务器来监听文件变化使其自动编译到浏览器运行,webpack.dev.js 文件即是针对此环境下做的配置,使用 webpack-merge 插件合并上面base配置文件。

npm i webpack-merge -D
const path = require('path');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base');

module.exports = merge(baseConfig, {
  mode: 'development', //development——默认不压缩代码
  devServer: {
    static: {
      directory: path.join(__dirname, '../public'), //静态文件映射到public文件夹中
      publicPath: '/public', //在哪个URL上提供directory的内容
    },
    open: false, //打开默认浏览器
    port: 'auto', //自动使用一个可用端口
    compress: true, //使用gzip压缩代码
    hot: true, //热更新(将更改的局部的文件进行重现打包,加快打包速度)
  },
  devtool: 'eval-cheap-module-source-map', //源码映射
  //设置为eval-cheap-source-map可以加快一点初始启动速度,但是源代码文件经过loader处理,映射行数可能不准确
});
1.1.3 webpack.pro.js

webpack已内置插件 terser-webpack-plugin 压缩js文件,该插件默认支持多线程压缩,在mode为production时自动启用,但是由于还需要配置CSS压缩,导致js压缩失效,所以需要重新定义。安装 css-minimizer-webpack-plugin 插件用于CSS压缩。如果报 terser-webpack-plugin 找不到的错误,那就手动安装一下依赖。

npm i css-minimizer-webpack-plugin -D
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base');
const TerserPlugin = require('terser-webpack-plugin'); // 对js进行压缩
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); // 对css进行压缩
const webpack = require('webpack');

module.exports = merge(baseConfig, {
  mode: 'production', //production——默认代码压缩
  optimization: {
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            warnings: false, // 删除无用代码时是否给出警告
            drop_debugger: true, // 删除所有的debugger
            drop_console: true, // 删除所有的console.*
            pure_funcs: ['console.log'], // 删除所有的console.log
          },
        },
      }),
      new CssMinimizerPlugin(),
    ],
  },
});

最后为不同环境指定 webpack 配置的文件,执行不同的命令对于开发生产

{
  "scripts": {
    	"dev": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.dev.js",
    	"build": "cross-env NODE_ENV=production webpack --config ./build/webpack.pro.js"
    },
}

1.2 读取自定义变量

通常做法是建立 .env 文件 ,使用.env 文件是用来存储环境变量的一个简单方法,如保存账号数据、请求域名、密码等随环境改变的东西。

# .env
API = 'http://env.com'
GLOBAL = 'http://global.com'

读取 .env 文件需要使用 dotenv 相关的库,它将环境变量从 .env 文件加载到 process.env,如 process.env.API

npm i dotenv -D

另外在webpack.base.js中加入如下配置

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

那么在webpack配置文件中打印 process.env.API的值就能得到 “http://env.com”,但是在打包编译的文件中还是没有此变量(如在index.ts中打印)

在这里插入图片描述

所以还需要将环境变量注入到业务代码中去,使用webpack内置的 DefinePlugin 插件进行处理

// webpack.base.js
const webpack = require('webpack');
//...
  plugins: [
    new webpack.DefinePlugin({
      // webpack自带该插件,无需单独安装,但是要引入webpack
      'process.env': JSON.stringify(dotenv.parsed),
    }),
  ],
//...

1.3 读取不同环境下的自定义变量

一个 .env 文件只有一个环境变量,通常我们需要多个环境,所以会建立多个.env 文件,如

  1. .env.development ——开发环境
API = 'http://development.com'
  1. .env.pre ——预发环境
API = 'http://pre.com'
  1. .env.production ——生产环境
API = 'http://production.com'

使用 dotenv-flow 这个库读取这些配置文件,dotenv-flow扩展了dotenv,添加了对NODE_ENV特定.env文件(如.env.development、.env.test、.env.stage和.env.production)的支持以及适当的.env.local覆盖。

npm i dotenv-flow -D

配置 dotenv-flow 代替 dotenv

// webpack.base.js
const dotenv = require('dotenv-flow').config();

现在就能非常轻松的使用 .env 文件中的环境变量了,对比如在package.json文件中编写pre环境的打包命令,只需将NODE_ENV的值改为对应的环境变量即可,然后 dotenv-flow 插件会此环境变量下对应的.env文件(即 .env.pre 文件),还会加载基础的 .env 文件,它具有最低优先级,保留最默认(后备)值。

{
  "scripts": {
    "pre": "cross-env NODE_ENV=pre webpack --config ./build/webpack.pro.js"
  },
}

如果想在pre环境下启动本地服务器预览,则可以用下面的命令

{
  "scripts": {
    "pre-dev": "cross-env NODE_ENV=pre webpack-dev-server --config ./build/webpack.dev.js"
  },
}

那么运行后,在业务代码文件中(index.ts)分别打印以下几个值

console.log(process.env.NODE_ENV);
console.log(process.env.API);
console.log(process.env.GLOBAL);

就可以在浏览器的控制台中得到这么几个值,大功告成了!

在这里插入图片描述


总结

环境变量对于webpack配置以及项目工程来说都是相当重要的一环,希望能对大家配置项目有所帮助。不过这一章一不小心写太长了,可能不太适合快节奏的阅读,建议先收藏之后有时间再看,相信如果你跟着我这两章webpack的配置文章思路来,多多少少会有点收获的。后面还有最后一章是关于webpack优化的内容,请点击此处查看。
欢迎各位看官【点赞】、【收藏】,多多支持!也欢迎您【评论】留下宝贵意见,共同探讨一起学习~

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值