webpack学习笔记

本文详述了webpack的基本概念和使用,包括Entry、Output、Loader、Plugins、Mode的解释,以及如何打包js/json、css/less、html、图片和其他资源。还介绍了开发和生产环境的配置差异,如CSS提取、压缩、JS语法检查、兼容性处理、HTML和JS压缩,以及webpack的优化策略。最后,展示了如何配置开发服务器和环境变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 简介

在这里插入图片描述

环境:nodejs 10+、webpack 4.26+

webpack:前端资源构建工具,静态模块打包器,将前端所有资源作为模块化处理,分析依赖并打包成静态资源bundle。可以编译转换浏览器无法识别最新的语法:es6或者less等。webpack相当于一个工具集合,里面包含了很多小工具。

流程:从入口进入,分析入口文件所依赖的各种资源,并进行引入,形成chunk。对chunk中的各种资源进行分别解析编译形成bundle

2 webpack五个核心概念

  • Entry:入口,webpack以哪个文件为起点分析和构建关系依赖图。
  • Output:打包好后的bundle输出的目的地,及命名规则
  • Loader:扩展,webpack可识别的文件类型(默认只识别js)
  • Plugins:插件,功能更强,相当于功能扩展
  • Mode:模式,开发和生产,选择不同的模式会自动启用不同的一些插件
    • 开发:process.env.NODE_ENV=development,启用:NamedChunksPlugin和NamedModulesPlugin
    • 生产:process.env.NODE_ENV=production,启用:FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin, UglifyJsPlugin

注意:loader和plugin区别
loader即为文件加载器,操作的是文件,将文件A通过loader转换成文件B,是一个单纯的文件转化过程。
plugin即为插件,是一个扩展器,丰富webpack本身,增强功能 ,针对的是在loader结束之后,webpack打包的整个过程,他并不直接操作文件,而是基于事件机制工作,监听webpack打包过程中的某些节点,执行广泛的任务。

3 尝试

全局安装:npm i webpack webpack-cli -g
本地安装:npm i webpack webpack-cli -D

3.1 打包js/json

前提准备:新建:build目录,src目录,src/index.js文件,并在本地npm安装好了webpack和webpack-cli

执行:webpack ./src/index.js -o ./build/built.js --mode=development (会将es6模块化编译成浏览器可识别模块化)

index.js代码:

import data from './data.json'

console.log(data)

function add(a,b){return a+b}
console.log(add(1,2))

data.json代码:

{
  "name":"pp",
  "age":12,
  "sex":"male"
}

执行:webpack ./src/index.js -o ./build/built.js --mode=production (表示生产环境,还会对代码做压缩)

3.2 打包css/less

index.js中引入css文件然后正常打包,打包报错,说明webpack默认不支持css/img,需要loader来翻译不识别的文件
loader使用需要【下载、配置】才能生效

webpack.config.js 配置文件,如果需要配置详细参数,建立配置文件来使用
特别注意:由于构建工具基于nodejs的,模块化采用的commonjs,所以配置文件导出使用:module.exports

const { resolve } = require('path')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    // path必须要是全路径,所以使用nodejs中的path来获取
    path: resolve(__dirname,'build')
  },
  // loader有关配置
  module: {
    rules: [
      // 详细配置
      {
        // test表示匹配文件类型
        test: /\.css$/,
        // 使用哪些loader,执行顺序从后往前
        use: [
          'style-loader', // 将js中样式字符串插入到style标签中,放到head标签中生效
          'css-loader' // 将css文件编程commonjs模块加载到js中,内容是样式字符串
        ]
      }
    ]
  },
  // plugins插件有关配置
  plugins: [

  ],
  mode: 'development',
  // mode: 'production'
}

如果需要打包less:需要配置less的loader才行,如下

处理less文件,在css loader后添加一个loader即可,注意:每个loader只能处理一个文件类型

{
  test: /\.less$/,
  use: ['style-loader','css-loader','less-loader']
}

配置好后需要下载相关模块:npm i less less-loader -D

3.3 打包html

打包html使用的是插件,插件需要【下载、引入、配置】,才能生效

下载:html-webpack-pluginnpm i html-webpack-plugin -D
引入和配置:webpack.config.js

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname,'build')
  },
  // loader有关配置
  module: {
    rules: []
  },
  // plugins插件有关配置
  plugins: [
    new HtmlWebpackPlugin({  // 默认创建一个空的html文件,并引入打包输出的所有资源,如果需要模板则需要传入配置{template}
      template: './src/index.html'  // 以该文件为模板
    }) 
  ],
  mode: 'development',
  // mode: 'production'
}

注意:不要在html模板文件中再手动引入资源,会自动引入,只需在入口文件中引入资源即可

3.4 打包图片

引用图片分两种:

  • 样式文件中的url引入
  • html中的src引入

对于css样式文件中引入的,需要安装url-loaderfile-loader,并配置webpack:

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname,'build')
  },
  // loader有关配置
  module: {
    rules: [
      {
        // test表示匹配文件类型
        test: /\.css$/,
        // 使用哪些loader,执行顺序从后往前
        use: [
          'style-loader', // 将js中样式字符串插入到style标签中,放到head标签中生效
          'css-loader' // 将css文件编程commonjs模块加载到js中,内容是样式字符串
        ]
      },
      {
        test: /\.(jpg|png|gif)$/, // 多个文件类型这样写
        loader: 'url-loader', // 一个loader这样写
        options: { // 该loader的额外配置
          limit: 8*1024 // 8kb,小于该值会被base64编码
        }
      }
    ]
  },
  // plugins插件有关配置
  plugins: [
    new HtmlWebpackPlugin({  // 默认创建一个空的html文件,并引入打包输出的所有资源,如果需要模板则需要传入配置{template}
      template: './src/index.html'  // 以该文件为模板
    }) 
  ],
  mode: 'development',
  // mode: 'production'
}

base64处理图片可以减少请求数量,减轻服务器压力,但是图片体积可能会更大,所以一般用来处理8kb以下的

对于html中src引入的:需要下载html-loader,该loader专门用于处理html中图片src引入,使其可以被url-loader解析

{
  test: /.\html$/,
  loader: 'html-loader'
}

此处会发现html中的图片引用错误,原因是:html-loader是采用commonjs模块化导出,而url-loader是使用es6模块化解析的
解决方法:关闭url-loader的es6模块化,使用默认的commonjs解析

{
   test: /\.(jpg|png|gif)$/, // 多个文件类型这样写
   loader: 'url-loader', // 一个loader这样写
   options: { // 该loader的额外配置
     limit: 8*1024, // 8kb,小于该值会被base64编码
     esModule: false // 关闭该loader的es6模块化,使用commonjs解析(因为html-loader解析后引入的图片方式采用commonjs)
   }
 }

给引入的图片进行重命名:name:'[hash:10].[ext],取图片hash值前10位

{
   test: /\.(jpg|png|gif)$/, // 多个文件类型这样写
   loader: 'url-loader', // 一个loader这样写
   options: { // 该loader的额外配置
     limit: 8*1024, // 8kb,小于该值会被base64编码
     esModule: false, // 关闭该loader的es6模块化,使用commonjs解析(因为html-loader解析后引入的图片方式采用commonjs)
     name:'[hash:10].[ext]'
   }
 }

3.5 打包其他资源

其他资源:除了css/js/html外的其他资源,可以使用exclude来匹配排除类型外的所有资源,只需要在loader中配置:

{
	 exclude: /\.(css|js|html)$/,
	  loader: 'file-loader',
	  options: {
	    name: '[hash:10].[ext]'
	  }
}

所有不包括css/js/html的资源都会被这个loader打包处理

3.6 devServer

配置开发服务器,可以自动编译打包和打开浏览器,刷新,看到更改后的效果

  // 会在内存中打包,不会有任何输出
  // 启动:webpack-dev-server
  devServer: {
    contentBase: resolve(__dirname,'build'), // 运行项目的目录,如果配置了html-webpack-plugin则该配置不起作用
    compress: true,  // 启动gzip压缩
    port: 3000 // 端口号
    open: true // 自动打开浏览器
  }

需要先安装:npm i -D webpack-dev-server
将指令配置到package.json中去:"start":"webpack-dev-server"然后使用npm run start来启动
注意:webpack5后启动命令改成了:webpack serve

一旦源代码被修改,并保存后,会自动进行编译,并展示到页面上

4 环境基本配置

4.1 开发环境

打包:js、json、html、css、less、img、file等资源

const {resolve} = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // 入口文件存放地,该文件包括js和css的合并
    path: resolve(__dirname,'build') // 所有资源存放地
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        use: ['style-loader','css-loader','less-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader','css-loader']
      },
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        test: /.\html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|js|css|less|jpg|png|jpeg|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'medias'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  // 会在内存中打包,不会有任何输出
  // 启动:webpack-dev-server
  devServer: {
    contentBase: resolve(__dirname,'build'), // 运行项目的目录
    compress: true,  // 启动gzip压缩
    port: 3000, // 端口号
    open: true // 自动打开浏览器
  },
  mode: 'development'
}

4.2 生产环境

为了让代码更快,性能更好,稳定性更高(开发环境不需要考虑这些)

  1. 开发环境中css和js是打包到一起的bundle,在加载过程中,先加载js然后再创建style标签加载css,这样会出现闪屏现象
  2. 代码需要统一压缩
  3. 样式的兼容

4.2.1 提取CSS到单独文件

需要安装插件:npm i -D mini-css-extract-plugin
步骤:注册插件 替换style-loaderMiniCssExtractPlugin.loader(sytle-loader为js中的样式字符串创建style标签并插入到html中,现在由MiniCssExtractPlugin.loader来提取js中的样式字符串到单独文件,并引入样式标签)

const {resolve} = require("path")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // 入口文件存放地,该文件包括js和css的合并
    path: resolve(__dirname,'build') // 所有资源存放地
  },
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader,'css-loader']
      },
      ...
    ]
  },
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
  ...
}

4.2.2 css兼容性处理

需要使用:postcss-loaderpostcss-preset-env(用于精确到浏览器版本)
安装:npm i -D postcss-loader postcss-preset-env

const {
  resolve
} = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// 这样设置node环境变量,postcss会读取该环境变量来判断读取生产还是开发配置
// process.env.NODE_ENV = "development"
process.env.NODE_ENV = "production"

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // 入口文件存放地,该文件包括js和css的合并
    path: resolve(__dirname, 'build') // 所有资源存放地
  },
  module: {
    rules: [{
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader', 
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                ident: 'postcss',
                plugins: [
                  require('postcss-preset-env')()
                ]
              }
            }
          },
          'less-loader',
        ]
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'
        {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                ident: 'postcss',
                plugins: [
                  require('postcss-preset-env')()
                ]
              }
            }
          }
        ]
      },
      
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
}

特别注意:一、需要配置node环境变量:process.env.NODE_ENV = “production”;二、配置postcss-loader及其参数,这个loader需要先css-loader处理。三、在package.json中配置browserlist

在package.json中配置browserslist,来指定加载css兼容性样式

  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  }

关于browserslist详细配置可以去github上查询,注意:默认情况插件执行的是生产环境配置,除非配置了Node环境变量(和webpack.config.js中的mode无关)

编译后的css代码加了兼容样式

#b {
  display: -webkit-flex;
  display: flex;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

4.2.3 css压缩

需要用到插件:optimize-css-assets-webpack-plugin
安装:npm i -D optimize-css-assets-webpack-plugin
配置方法:引入插件,new插件即可。

const {resolve} = require("path")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // 入口文件存放地,该文件包括js和css的合并
    path: resolve(__dirname,'build') // 所有资源存放地
  },
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader,'css-loader']
      },
      ...
    ]
  },
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin()
  ],
  ...
}

注意:在postcss-loader中有个:'cssnano': {}的配置,也可以用来压缩css!示例:cssnano: ctx.env === 'production' ? {} : false

4.2.4 JS语法检查——eslint

eslint用于js语法检查,需要安装:eslint-loadereslint
注意:由于需要限定只检查src下的源代码,不检查第三方库(使用exclude)

eslint检查规则需要在package.json中的eslintConfig属性设置,建议使用airbnb规则

eslint-config-airbnb-base:基本检查库,不包括react插件,该库依赖:eslint-plugin-import eslint
eslint-config-airbnb:包括react插件

步骤:(需要先安装:npm i -D eslint eslint-loader eslint-config-airbnb-base eslint-plugin-import

  1. loader配置
      {
        test: /\.js$/,
        loader: 'eslint-loader',
        enforce: "pre", // 编译前检查
        exclude: /node_modules/, // 不检测的文件
        include: [path.resolve(__dirname, 'src')], // 指定检查的目录
        options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine 
          fix: true // 自动修复错误
        }
      }
  1. package.json配置,这里使用airbnb语法规则
  "eslintConfig":{
    "extends":"airbnb-base"
  }

可以使用注释来取消检查:// eslint-disable-next-line取消下一行语法检查,/* eslint-disable */放在文件第一行表示该文件不检查

参考:https://blog.youkuaiyun.com/kai_vin/article/details/89026115

4.2.5 JS兼容性处理

JS兼容即有些浏览器不支持ES6语法,babel会将其转码成ES5,依赖:@babel/core,称为兼容处理。使用到的loader为:babel-loader,依据使用的预设环境可以转换不同的语法。

【基本兼容处理】:@babel/preset-env
安装:npm i -D babel-loader @babel/core @babel/preset-env

      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }

只能转换基本的如const、箭头函数等。不支持Promise等。

【暴力处理】全部js兼容处理:@babel/polyfill
安装:npm i -D @babel/polyfill
使用:在入口文件中引入即可:import '@babel/polyfill


@babel/polyfill目前已经替换成了:core-js/stable + regenerator-runtime/runtime,使用:

import "core-js/stable";
import "regenerator-runtime/runtime";

【按需加载兼容处理】使用core-js

 {
   test: /\.js$/,
   exclude: /node_modules/,
   loader: 'babel-loader',
   options: {
     presets: [
       '@babel/preset-env',
       {
         useBuiltIns: 'usage', // 开启按需加载
         corejs: {version: 3}, // 指定core-js版本
         targets: {chrome:'60',firefox:'60',ie:'9',safari:'10',edge:'17'} // 指定兼容性哪个版本以上
       }
     ]
   }
 }

注意:使用该方案则不能使用暴力处理方式

4.2.6 html和js压缩

js压缩:只需要将mode='production'开启即可(发挥作用的插件是:UglifyJsPlugin
html压缩:同样,只需要开启生产模式即可,当然也可以在插件html-webpack-plugin中配置:

 new HtmlWebpackPlugin({
   template: './src/index.html',
   minify: {
     collapseWhitespace: true, // 移除空格
     removeComments: true // 移除注释
   }
 }),

4.2.7 生产环境终极配置

const { resolve } = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 这样设置node环境变量,postcss会读取该环境变量来判断读取生产还是开发配置
// process.env.NODE_ENV = "development"
process.env.NODE_ENV = "production"

const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader', 
  {
    loader: 'postcss-loader',
    options: {
      postcssOptions: {
        ident: 'postcss',
        plugins: [
          require('postcss-preset-env')()
        ]
      }
    }
  }
]

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/built.js', // 入口文件存放地,该文件包括js和css的合并
    path: resolve(__dirname, 'build') // 所有资源存放地
  },
  module: {
    rules: [{
        test: /\.less$/,
        use: [
          ...commonCssLoader,
          'less-loader'
        ]
      },
      {
        test: /\.css$/,
        use: [
          ...commonCssLoader
        ]
      },
      {
        test: /\.(jpg|png|jpeg|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        test: /.\html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|js|css|less|jpg|png|jpeg|gif)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'medias'
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env',
            {
              useBuiltIns: 'usage', // 开启按需加载
              corejs: {version: 3}, // 指定core-js版本
              targets: {chrome:'60',firefox:'60',ie:'9',safari:'10',edge:'17'} // 指定兼容性哪个版本以上
            }
          ]
        }
      },
      {
        test: /\.js$/,
        loader: 'eslint-loader',
        enforce: "pre", // 编译前检查,相同test中该属性为true的先执行
        exclude: /node_modules/, // 不检测的文件
        include: [path.resolve(__dirname, 'src')], // 指定检查的目录
        options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine 
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true, // 移除空格
        removeComments: true // 移除注释
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin()
  ],
  // 会在内存中打包,不会有任何输出
  // 启动:webpack-dev-server
  devServer: {
    contentBase: resolve(__dirname, 'build'), // 运行项目的目录
    compress: true, // 启动gzip压缩
    port: 3000, // 端口号
    open: true // 自动打开浏览器
  },
  mode: 'production'
}

package.json中:

{
...
 "scripts": {
    ...
    "build": "webpack",
    "start": "webpack serve"
  },
  ...
  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig":{
    "extends":"airbnb-base"
  }
}

5 webpack优化

两种环境的优化

开发环境:1 优化打包构建速度 2 优化代码调试
生产环境:1 优化打包构建速度 2 优化代码运行性能

5.1 开发环境

问题1:修改任意模块,整个项目所有模块会重新加载
解决:HMR:热模块替换,一个模块变化只会打包该模块,不会打包所有
在devServer中添加:hot:true

  devServer: {
  contentBase: resolve(__dirname, 'build'), // 运行项目的目录
  compress: true, // 启动gzip压缩
  port: 3000, // 端口号
  open: true, // 自动打开浏览器
  hot: true // 热加载
},

对于样式修改,由于style-loader实现了HMR功能,所以可以实现热模块更新

对于JS文件,没有HMR功能
解决办法:添加HMR功能

// 在入口文件中,对于引入的其他模块进行监听是否更新
if(module.hot){
	module.hot.accept('./xxx.js',function(){回调函数}
}

注意:入口js文件不需要也不可以做HMR,因为他改了其他函数肯定会重新引入的

对于HTML文件,不能使用HMR功能(因为html只有一个文件),且开启后无法响应更新了
解决办法:在webpack.config.js中入口处加上html文件入口:entry: ['./src/index.js','./src/index.html'],

source-map:提供源代码和构建后代码的映射技术,方便查询错误原因和定位
配置:在webpack.config.js中添加:devtool: 'source-map'即可
注意:source-map有多种值:

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

其中:
source-map:外部 错误定位到 【源】 代码,精确到行和列
inline-source-map:内联 错误定位到 【源】 代码
hidden-source-map:外部 错误定位到 【编译后】 代码
eval-source-map:内联 错误定位到 【源】 代码
nosources-source-map:外部 错误定位到 【源】 代码,但是无法查看
cheap-source-map:外部 错误定位到 【源】 代码,之精确到行,不到列
cheap-module-source-map:外部 错误定位到 【源】 代码,精确度稍微好点。。。

内敛生成的映射在打包的js中,构建起来速度更快,外部则在js同级

开发推荐使用:eval-source-map
生产推荐使用:source-map

5.2 生产环境

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值