Administrator
Published on 2025-03-31 / 25 Visits
0
0

Web前端工程化(webpack)

1.Node.js

1.1.什么是node.js

它是一个开源和跨平台的 JavaScript 运行时环境,Node.js是一门服务器端的语言。

官方文档

1.2 .Node和浏览器之间的区别

在浏览器中,大部分时间都在与 DOM 或其他 Web 平台 API(如 Cookies)进行交互 这些在 Node.js 中并不存在。没有document以及window浏览器提供的所有其他对象。

1.3.Node.js与npm的关系

npm是 Node.js 的标准包管理器,它是地球上最大的单一语言代码存储库。它是下载和管理 Node.js 包依赖项的一种方式。

node在安装的时候包含了npm包管理工具了,无需自行在安装。

1.4.npm包管理

通过之前的案例可以发现,当我们安装完一个模块后,我们会多出一个“node_modules“文件夹,我们安装的所有模块都放在了这个文件夹中了,并且还多出了”.package-lock.json”文件。

在当前根目录下,也会出现一个”package-lock.json”文件和一个“package.json”的文件。

下面将会讲解这几个文件的作用

1.4.1.package.json文件

package.json文件,是整个项目的描述文件,它记录了当前项目的描述信息,例如:项目名称、版本、作者、github地址、 当前项目依赖了哪些第三方模块等。

该文件可以通过 “npm init -y“命令生成,该命令是项目初始化命令,-y 参数是表示全部使用默认信息生成。

PS D:\Codebase\WebProject\node\learn> npm init -y
​
Wrote to D:\Codebase\WebProject\node\learn\package.json:
​
{
​
 "name": "learn",
​
 "version": "1.0.0",
​
 "description": "",
​
 "main": "01_start.js",
​
 "scripts": {
​
  "test": "echo \"Error: no test specified\" && exit 1"
​
 },
​
 "keywords": [],
​
 "author": "",
​
 "license": "ISC"
​
}

package.json文件内容解读

name:项目名称,默认就是当前目录名称

version:版本是多少

description:项目描述信息

main:项目主入口文件,默认为index.js

scripts:命令行别名,可以通过配置该属性来配置简化运行的命令,通过 npm run 命令别名来执行

keywords:关键字描述项目

author:项目作者

license:项目遵守的协议(开源还是闭源协议等)

安装模块后,该文件按还会多出一个,新属性,dependencies,该属性记录了当前模块安装了哪些项目依赖,什么是项目依赖,后面内容会讲解。

例如我安装一个jquery,npm会自动将jquery添加到dependencies项目依赖对象中。

package.json文件内容,省略了前面的内容

.......
"license": "ISC",
"dependencies": {
    "jquery": "^3.7.1"
  }
}

当我们将“node_modules“文件夹删除后,我们可以通过命令:npm install 重新安装dependencies中已经记录的项目依赖模块

npm install

模块又分为开发依赖模块项目依赖模块

项目依赖指的是,我们项目运行时需要的模块依赖,例如Jquery,就是项目依赖,刚才我们安装的就是项目依赖

开发依赖指的是,我们项目在开发时的依赖,例如gulp,就是开发依赖,它只是在开发时将我们的less还有js这些进行编译,编译完成后就不需要依赖该模块了,开发开发依赖通过在安装模块”npm install 模块名“ 后面添加”--savue-dev“ 参数就可以将模块以开发依赖的形式安装依赖。

npm install gulp --save-dev

当我们安装gulp开发依赖模块后,npm会自动将gulp添加到devDependencies项目依赖对象中,同样的通过npm install也会自动安装开发依赖模块

........
"license": "ISC",
  "dependencies": {
    "jquery": "^3.7.1"
  },
  "devDependencies": {
    "gulp": "^5.0.0"
  }
}

1.4.2.package-lock.json

package-lock.json文件作用如下

  • 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题

  • 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要做额外的工作

1.4.3.npm命令说明

本地安装:刚才我们直接安装就是属于本地安装,本地安装的模块只能在当前模块使用。

全局安装:通过在npm安装命令后面添加“-g”参数,来实现全局安装,全局安装的模块在所有模块中都能使用

项目依赖:指的是我们项目运行时需要的模块依赖,例如Jquery,就是项目依赖,刚才我们安装的就是项目依赖

开发依赖:指的是我们项目在开发时的依赖,例如gulp,就是开发依赖,它只是在开发时将我们的less还有js这些进行编译,编译完成后就不需要依赖该模块了,开发开发依赖通过在安装模块”npm install 模块名“ 后面添加”--savue-dev“ 参数就可以将模块以开发依赖的形式安装依赖。

npm install 包名(模块名),默认为项目依赖-本地安装

npm install 包名(模块名) -g 项目依赖-全局安装

npm install 包名(模块名) --save 项目依赖-本地安装,-D等同

npm install 包名(模块名) --save-dev 开发依赖-本地安装,-S等同

npm uninstall 包名(模块名) 卸载

1.5.Node中的模块开发规范

1.5.1模块开发规范

JavaScript模块化开发规范又很多,在服务器端有CommonJS模块化规范,在浏览器端有AMD和UMD模块化规范,之后ES6 模块模块化规范出现,它既可以做服务端的模块化规范,又可以做浏览器端的模块规范。

Node.js默认使用的是CommonJS模块化规范,下面介绍的也是CommonJS模块化规范的也语法,node.js可以通过bable模块来得到ES6 模块模块化规范的支持。

Node.js规定一个JavaScript文件就是一个模块,模块内部定义的函数和变量,外部文件是无法访问的

模块内部可以通过moudule.export对象进行导出,使用require方法导入其他模块

02_module01.js,导出

let a="hello world"
let say=function(){
  console.log(a)
}
moudule.exports.a=a;
moudule.exports.say=say;

02_module.js,引入

let m=require('./02_module01.js');
console.log(m)
console.log(m.a)
m.say()

运行结果

{ a: 'hello world', say: [Function: say] }
hello world
hello world

1.5.2.模块查找机制

require包含路径

require('./find.js') ;

require('./find') ;

1.require方法根据模块路径查找模块,如果是完整路径,直接弓|入模块。

2.如果模块后缀省略,先找同名JS文件再找同名JS文件夹

3.如果找到了同名文件夹,找文件夹中的index.js

4.如果文件夹中没有index.js就会去当前文件夹中的package.js文件中查找main选项中的入口文件

5.如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到

require不包路径

require('find') ;

1.Node.js会假设它是 系统模块

2.Node.js会去node_modules文件夹中

3.首先看是否有该名字的JS文件

4.再看是否有该名字的文件夹

5.如果是文件夹看里面是否有index.js

6.如果没有index.js查看该 文件夹中的package.json中的main选项确定模块入口文件

7.否报错找不到

2.ES6中的模块化规范

2.1.模块化规范

在ES6模块化规范诞生之前,Javascript 社区已经尝试并提出了AMD、CMD、CommonJs 等模块化规范。 但是,这些社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通用的模块化标准例如: AMD和CMD适用于浏览器端的Javascript 模块化,CommonJs适翻于服务器端的Javascript模块化

而在ES6语法规范中,在语言层面上重新定义了ES6模块化规范,实现了统一,因此它是是浏览器端与服务器端通用的模块化开发规范。

Es6模块化规范中定义:

  • 每个js文件都是一个独立的模块

  • 导入模块成员使用import 关键字

  • 暴露模块成员使用export 关键字

2.2.Node中使用ES6模块化规范

因为Node.js默认使用的是CommonJS模块化规范,所以不能直接使用ES6中的规范语法。

使用插件bable模块插件可以实现让Node.js支持ES6模块化规范

2.3.bable模块

2.3.1.什么是bable

bable官方文档

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:

  • 语法转换

  • 通过 Polyfill 方式在目标环境中添加缺失的功能(通过引入第三方 polyfill 模块,例如 core-js

  • 源码转换(codemods)

2.3.2.使用bable

安装bable

通过命令安装bable核心模块npm install --save-dev @babel/core @babel/cli @babel/preset-env安装

npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node

通过命令npm install --save @babel/polyfill安装bable的polyfill模块来模拟完整的 ES2015+ 环环境

npm install --save @babel/polyfill

配置bable

在项目根目录(package.json文件同级目录下)创建babel.config.json文件,填写以下内容

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.5"
      }
    ]
  ]
}

运行js文件

index.js文件,内容如下

console.log('hellow node.js')

通过bable运行js

npx babel-node .\index.js
hello node.jsPS D:\Codebase\WebProject\node\learn> npx babel-node .\index.js
hello node.js

2.4.ES6模块模块化规范语法

Es6模块化规范中定义:

  • 每个js文件都是一个独立的模块

  • 导入模块成员使用import 关键字

  • 暴露模块成员使用export 关键字

2.4.1.默认导出与默认导入

默认导出使用export default{变量名1,变量2,方法名1。。。}导出

默认导入使用import 变量名 from '模块名' 导入

import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,如果在服务器端后缀可以省略,如果在浏览器端不能省略,这取决于打包工具(编译工具)

例子:indexB.js默认导出、indexA.js默认导入

indexB.js内容如下

let a=10;
let say=function(message){
    console.log(message)
};
let b=20;
let c=30;
// 默认导出
export default{
    a,
    say,
    b
}

indexA.js内容如下

import B from './indexB'

console.log(B)
console.log(B.a)
console.log(B.b)
B.say("你好,ES6模块规范。。。")
console.log(B.c)

通过npx bable-node运行 indexA.js

PS D:\Codebase\WebProject\node\learn> npx babel-node .\indexA.js
{ a: 10, say: [Function: say], b: 20 }
10
20
你好,ES6模块规范。。。
undefined

2.4.2.按需导出与按需导入

按需导出模块中的变量使用export let 变量名=需要导出的变量名

按需导出模块中的方法使用export function 方法名=需要导出的方法名

按需导入使用import {变量名1,变量2,.....} from '模块名',其中变量名要根据导出的变量名或者方法名一致

import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,如果在服务器端后缀可以省略,如果在浏览器端不能省略,这取决于打包工具(编译工具)。

例子:indexB.js默认导出、indexA.js默认导入

indexB.js内容如下

let a=10;
let say=function(message){
    console.log(message)
};
let b=20;
let c=30;
let export_all={
    et_a:a,
    et_say:say,
    et_b:b,
    et_c:c
}
// 按需导出
export let ea=export_all;
export let export_a=a;
export let export_b=b;
export function export_say(message){say(message)};

indexA.js内容如下

// 按需导入,as是起别名,导入的变量名和导出变量名一致
import {ea,ea as export_all,export_a,export_b,export_say} from './indexB'

console.log(ea)
console.log(export_all)
console.log(export_a)
console.log(export_b)
export_say("你好,ES6模块规范。。。")

通过npx bable-node运行 indexA.js

PS D:\Codebase\WebProject\node\learn> npx babel-node .\indexA.js
{ et_a: 10, et_say: [Function: say], et_b: 20, et_c: 30 }
{ et_a: 10, et_say: [Function: say], et_b: 20, et_c: 30 }
10
20
你好,ES6模块规范。。。

2.4.3.直接导入

直接导入没有导出操作

例子:indexA.js直接导入indexB.js

indexB.js内容如下

let a=10;
let say=function(message){
    console.log(message)
};
for(let i=0;i<5;i++){
    console.log("hello")
}

indexA.js内容如下

import './indexB'
console.log("直接导入模块")

3.webpack打包工具模块

3.1.概述

官方文档

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具

webpack提供了友好的模块化支持,以及代码压缩混淆、处理js兼容(编译成为浏览器支持的es版本)问题、性能优化等强大的功能,从而让程序员把工作的重心放到具体的功能实现上,提高了开发效率和项目的可维护性。

image-20250328230425389

3.2.webpack的基本使用

3.2.1安装webpack

# 使用npm初始化名目包管理
npm init -y
# 安装开发依赖模块webpack和webpack-cli
npm install --save-dev webpack webpack-cli

安装完成后,在项目根目录下创建一个src目录,在src目录下创建一个index.html和index.js,内容可以先不写

以后src目录下面存放的就是我们的代码(html、css、js、img等静态资源),项目结构如下

  项目根目录
  |- package.json
  |- package-lock.json
+ |- index.html
+ |- /src
+   |- index.js
+   |- index.html

创建一个在package.json同级的目录下创建一个webpack.config.js文件,填入以下内容,配置wepack

module.exports = {
  mode:'development'
};

在package.json文件找到运行脚本添加webpack打包命令别名

"dev":"webpack" ,webpack打包命令别名,后面可以通过npm run dev运行webpack打包项目,打包好的文件在项目根目录的dist目录下。

{
  "name": "webpack-learn",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev":"webpack" 
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.98.0",
    "webpack-cli": "^6.0.1"
  }
}

3.2.2.使用ES6模块化规范打包项目

通过3.2.1小节我们已经构建起了一个基于webpack的前端化工程了,现在可以用一个案例来打包测试下

例子:用ES6模块来使用Jquery,并打包

安装jquery

npm install jquery

src目录下的index.html文件,内容如下

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 直接使用ES6模块化语法的index.js会报错,因为浏览器对ES6还不支持 -->
    <script src="./index.js"></script>
</head>
<body>
    <ul>
        <li>第1个元素</li>
        <li>第2个元素</li>
        <li>第3个元素</li>
        <li>第4个元素</li>
        <li>第5个元素</li>
        <li>第6个元素</li>
        <li>第7个元素</li>
        <li>第8个元素</li>
    </ul>
</body>
</html>

src目录下的index.js文件,内容如下

import $ from 'jquery'
$(function(){
    $('li:odd').css('backgroundColor','pink')
    $('li:even').css('backgroundColor','blue')
})

浏览器中打开index.html会报错,内容如下,因为浏览器目前还没支持到最新的ES6模块化规范,解决办法使用webpack打包编译,将ES6转换为浏览器支持的版本

Uncaught SyntaxError: Cannot use import statement outside a module (at index.js:1:1)

运行webpack打包编译,会生成一个dist的文件夹,里面会默认生成一个main.js,这个main.js就是转译好且浏览器支持的版本的js

PS D:\Codebase\WebProject\webpack\webpack-learn> npm run dev

> [email protected] dev
> webpack

更改index.html中的js引用,更改为main.js

....... 
<meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 直接使用ES6模块化语法会报错,因为浏览器对ES6还不支持 -->
    <!-- <script src="./index.js"></script> -->
    <!-- 调用经过webpack打包编译过的js文件 -->
    <script src="../dist/main.js"></script>
........

现在在浏览器中打开index.html文件效果如下

image-20250329010031779

3.3.webpack基本配置说明

在webpack的4.x版本中 默认打包入口源文件为src目录下的index.js文件 默认打包的出口生成文件为dist目录下的main.js文件

webpack.config.js文件内容

let path=require('path') //node。js中的系统模块,用来获取系统文件分隔符
module.exports = {
    //开发模式
    mode:'development',  
    //入口文件(需要转换的源码文件)
    entry: path.join(__dirname,'./src/index.js'), 
    //出口文件(编译好的兼容文件)
    output: {  
      //生成的文件名
      filename: 'index.js',
      //生成的路径
      path: path.resolve(__dirname, 'dist'),
    },
};

以上文件内容是默认配置选项,后面还需要使用webpack配置自动打包更新,自动打包css、less、字体、图片等,以上操作都需要在webpack.config.js文件内容中进行配置,所以该文件的内容会越来越多。

3.4.webpack自动打包模块

开发环境----自动编译代码章节文档

webpack 提供了几种可选方式帮助在代码发生变化后自动编译代码:

  1. webpack 的 观察模式

  2. webpack-dev-server

  3. webpack-dev-middleware

这里介绍webpack-dev-server方法配置自动打包方法,其他方式可以看下官方文档。

3.4.1webpack-dev-server自动部署模块

webpack-dev-server是一个可以自动打包js源码自动部署web服务模块

webpack-dev-server官方文档

安装webpack-dev-server

npm install --save-dev webpack-dev-server

webpack.config.js文件中添加webpack-dev-server配置项,下列配置告知 webpack-dev-serversrc 目录下的文件作为可访问资源部署在网络路径rul的"/"根路径中

DevServer官方配置参数说明

 。。。。。
 //入口文件(需要转换的源码文件)
    entry: path.join(__dirname,'./src/index.js'),
 //出口文件(编译好的兼容文件)
    output: {  
      //生成的文件名
      filename: 'inex.js',
      //生成的路径
      path: path.resolve(__dirname, 'dist'),
    },
   // webpack-dev-server配置项
    devServer: {
      //配置url访问路径
      host:"localhost",
      //启动端口
      port: 8088,
      //启动完成后自动打开浏览器访问
      open: true,
      //启动时加载的静态资源路径
      static: [
        {
          //网络url路径
          publicPath: '/',
          //网络路径对应的目录路径资源。
          directory: './',
        }
      ],
    },
  ..........

在package.json中修改运行脚本为webpack-dev-server

。。。。。。。
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    //修改为webpack-dev-server自动打包
    "dev": "webpack-dev-server"
  },
。。。。。。

webpack-dev-server启动时会根据webpack.config.js文件中的entry入口文件进行打包,编译之后不会写入任何输出文件,而是将 bundle 文件保留在内存中,打包后的名字为output.filename配置的名字,web访问路径为网络url的根路径,所以index.html中的js引用路径为根路径”/main.js“就可以了,如果希望更改打包后web的访问路径,可以修改 dev server 配置中的 devMiddleware.publicPath 选项。

更改src/index.html的js引用路径为 <script src="/main.js"></script>

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 直接使用ES6模块化语法会报错,因为浏览器对ES6还不支持 -->
    <!-- <script src="./index.js"></script> -->
    <!-- 调用经过webpack打包编译过的js文件 -->
    <script src="/main.js"></script>
</head>

然后使用 npm run dev 进行打包

webpack-dev-server 在编译之后不会写入任何输出文件,而是将 bundle 文件保留在内存中,然后将它们作为可访问资源部署在 server 中,就像是挂载在 server 根路径上的真实文件一样。

webpack-dev-server启动时会根据webpack.config.js文件中的entry入口文件进行打包,编译之后不会写入任何输出文件,而是将 bundle 文件保留在内存中,打包后的名字为output.filename配置的名字,web访问路径为网络url的根路径,所以index.html中的js引用路径为根路径”/main.js“就可以了,如果希望更改打包后web的访问路径,可以修改 dev server 配置中的 devMiddleware.publicPath 选项。

PS D:\Codebase\WebProject\webpack\webpack-learn> npm run dev

> [email protected] dev
> webpack-dev-server

<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8088/, http://[::1]:8088/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.72.1:8088/
<i> [webpack-dev-server] Content not from webpack is served from 'D:\Codebase\WebProject\webpack\webpack-learn\src' directory
asset index.js 485 KiB [emitted] (name: main)
runtime modules 27.4 KiB 12 modules
modules by path ./node_modules/ 387 KiB
  modules by path ./node_modules/webpack-dev-server/client/ 84.8 KiB
    modules by path ./node_modules/webpack-dev-server/client/*.js 53.2 KiB 4 modules
    modules by path ./node_modules/webpack-dev-server/client/utils/*.js 980 bytes 2 modules
    + 2 modules
  modules by path ./node_modules/webpack/hot/*.js 5.17 KiB
    ./node_modules/webpack/hot/dev-server.js 1.94 KiB [built] [code generated]
    ./node_modules/webpack/hot/log.js 1.73 KiB [built] [code generated]
    ./node_modules/webpack/hot/emitter.js 75 bytes [built] [code generated]
    ./node_modules/webpack/hot/log-apply-result.js 1.43 KiB [built] [code generated]
  ./node_modules/jquery/dist/jquery.js 279 KiB [built] [code generated]
  ./node_modules/events/events.js 14.5 KiB [built] [code generated]
  ./node_modules/ansi-html-community/index.js 4.16 KiB [built] [code generated]
./src/index.js 156 bytes [built] [code generated]
webpack 5.98.0 compiled successfully in 670 ms

访问 http://localhost:8088/ 结果如下,表示成功。。。

image-20250330142726749

当点击src时会出现自动访问我们的主页index

image-20250329010031779

3.4.2.html-webpack-plugin打包模块(打包html)

html-webpack-plugin是一个html打包模块,它可以将html打包进web服务的根路径下,当构建时可以将html打包构建进dist目录中,它可以还可以自动引入js文件

html-webpack-plugin官方文档

安装html-webpack-plugin

npm install --save-dev html-webpack-plugin

配置使用html-webpack-plugin

在webpack.config.js文件中引入并配置 html-webpack-plugin,最后注册 html-webpack-plugin 插件。

new HtmlWebpackPlugin创建 html-webpack-plugin对象

plugins:[indexHtmlWebpackPlugin] 注册插件

//node。js中的系统模块,用来获取系统文件分隔符
let path=require('path') 
// 导入html-webpack-plugin模块
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 创建一个html-webpack-plugin对象
let indexHtmlWebpackPlugin=new HtmlWebpackPlugin({
  // 页面的标题
  title: '网站主页入口',
  // 打包编译后生成的文件名称
  filename:'index.html',
  // 需要打包编译的源文件
  template:'./src/index.html'
})
module.exports = {
    //开发模式
    mode:'development',  
    //入口文件(需要转换的源码文件)
    entry: [path.join(__dirname,'./src/index.js'),"./src/a.js"], 
    //出口文件(编译好的兼容文件)
    output: {  
      //生成的文件名
      filename: 'index.js',
      //生成的路径
      path: path.resolve(__dirname, 'dist'),
    },
    // webpack-dev-server配置项
    devServer: {
      host:"localhost",
      //启动端口
      port: 8088,
      //启动完成后自动打开浏览器访问
      open: true,
      //启动时加载的静态资源路径
      static: [
        {
          //网络路径
          publicPath: '/',
          //网络路径对应的目录路径
          directory: './',
        }
      ]
    },
    // 注册html-webpack-plugin模块
    plugins:[indexHtmlWebpackPlugin],
};

将index.html中的<script src="/main.js"></script>删除,因为html-webpack-plugin会自动帮我们引入入口文件js

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 直接使用ES6模块化语法会报错,因为浏览器对ES6还不支持 -->
    <!-- <script src="./index.js"></script> -->
    <!-- 调用经过webpack打包编译过的js文件 -->
    <script src="/main.js"></script>
</head>

运行npm run dev 访问 http://localhost:8088/ 结果如下,表示成功。。。

image-20250329010031779

3.5.webpack加载器

资源管理-----官方文档

3.5.1.为什么会使用loader加载器

在实际开发过程中,webpack默认只能打包处理以.js 后缀名结尾的模块,其他非.js 后缀名结 尾的模块,webpack默认处理不了,需要调用loader加载器才可以正常打包,否则会报错! loader加载器可以协助webpack打包处理特定的文件模块,比如:

  • css-loader可以打包处理 .css 相关文件

  • less-loader可以打包处理 .less 相关的文件

  • sass-loader可以打包处理 .scss 相关的文件

  • url-loader可以打包处理 css中与url路径相关的文件

webpack打包并使用加载器的流程

image-20250330144947401

3.5.2.css的loader加载器

1.打包普通css

打包css加载起需要安装tyle-loader和css-loader模块

安装tyle-loader和css-loader模块

npm install --save-dev style-loader css-loader

配置css加载器

module:[{rules:[{test:'',use[]}]}]配置模块规则

//node。js中的系统模块,用来获取系统文件分隔符
let path=require('path') 
// 导入html-webpack-plugin模块
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 创建一个HtmlWebpackPlugin对象
let indexHtmlWebpackPlugin=new HtmlWebpackPlugin({
  // 页面的标题
  title: '网站主页入口',
  // 打包编译后生成的文件名称
  filename:'index.html',
  // 需要打包编译的源文件
  template:'./src/index.html'
})
module.exports = {
    //开发模式
    mode:'development',  
    //入口文件(需要转换的源码文件)
    entry: [path.join(__dirname,'./src/index.js'),"./src/a.js"], 
    //出口文件(编译好的兼容文件)
    output: {  
      //生成的文件名
      filename: 'index.js',
      //生成的路径
      path: path.resolve(__dirname, 'dist'),
    },
    // webpack-dev-server配置项
    devServer: {
      host:"localhost",
      //启动端口
      port: 8088,
      //启动完成后自动打开浏览器访问
      // open: true,
      //启动时加载的静态资源路径
      static: [
        {
          //网络路径
          publicPath: '/',
          //网络路径对应的目录路径
          directory: './',
        }
      ]
    },
    // 注册插件
    plugins:[indexHtmlWebpackPlugin],
    //配置模块
    module: {
      // 配置模块规则
      rules: [
        { 
          //匹配后缀正则表达式
          test: /\.css$/i,
          //配置使用哪个加载器处理
          //注意:style-loader必须在css-loader前优先加载
          //注意:多个loader的调用顺序是:从后往前调用
          use: ['style-loader', 'css-loader'],
        },
      ],
    },
};

编写css

在src的css目录下创建一个index.css,内容如下

li{
    list-style: none;
}

在index.js中引入index.css

在这里可能有疑问为什么可以在index.js中引入css?,实际上如果按照ES6模块化规范,是不允许在js中引入css的,但是现在是基于webpack的css加载器来实现的js加载,它会在中间进行语法转译,从而实现只需要导入一个index.js即可实现css样式。

    import $ from 'jquery'
    import './css/index.css'
    $(function(){
        $('li:odd').css('backgroundColor','pink')
        $('li:even').css('backgroundColor','blue')
        var index=100;
        console.log(index)
    })

确保index.html引入了index.js

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="/index.js"></script>
</head>
<body>
    <ul>
        <li>第1个元素</li>
        <li>第2个元素</li>
        <li>第3个元素</li>

运行 npm run dev 浏览器中访问链接,出现以下效果成功。。。

样式生效了,li标签默认样式小黑点消失了。

image-20250330152110200

2.5.3.less的loader加载器

less是需要预编译的css语言,它需要进行预编译才能生成可执行的普通css

其他类似于less的需要预编译的语言还有 CSS 样式 —— postcsssass

less加载器官方文档

sass加载器官方文档

postcss加载器官方文档

前提条件,确保已经安装过css加载器了

安装 lessless-loader

npm install less less-loader --save-dev

配置模块规则

webpack.config.js

。。。。。。。。
module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/i,
        use: [
          // compiles Less to CSS
          'style-loader',
          'css-loader',
          'less-loader',
        ],
      },
    ],
  },
};
。。。。。。。。

在css下编写index.less,内容如下

body{
    padding: 0;
    margin: 0;
    ul{
        padding: 0;
        margin: 0;
        &:hover{
                background-color: red !important;
        }
    }
}

在index.js中导入index.less

    import $ from 'jquery'
    import './css/index.css'
    import './css/index.less'
    $(function(){
        $('li:odd').css('backgroundColor','pink')
        $('li:even').css('backgroundColor','blue')
        var index=100;
        console.log(index)
    })

运行npm run dev 访问web链接,显示出以下效果,当鼠标放到li上边会变颜色为红色

image-20250330155351295

3.5.4.资源模块

资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标、图片、视频等)而无需配置额外 loader。

资源模块是webpack5 内置的,无需安装。 资源模块官方文档

但是官方的资源模块只能处理css和字体文件资源,当我们需要将<img src=" ">标签中图图片加载时,需要使用html-loader来处理。html-loader会在后讲解。

加载图像

webpack.config.js中添加模块规则配置

。。。。。。。。。。。
module: {
     rules: [
       {
         test: /\.css$/i,
         use: ['style-loader', 'css-loader'],
       },
       {
        // 配置文件后缀
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        // 根据类型加载不同loader
        type: 'asset',
        // 生成的图片的位置
        generator: {
          filename: './img/[name][ext]',
        }
       },
     ],
   },
。。。。。。。。。。。

在index.html添加一个div

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="/index.js"></script>
</head>
<body>
    <ul>
        <li>第1个元素</li>
        <li>第2个元素</li>
        <li>第3个元素</li>
        <li>第4个元素</li>
        <li>第5个元素</li>
        <li>第6个元素</li>
        <li>第7个元素</li>
        <li>第8个元素</li>
    </ul>
    <!-- 添加一个div -->
    <div style="width: 500px;height: 300px;"></div>
</body>
</html>

在index.css中添加一个背景图片,请提前将图片放置在src/img中

li{
    list-style: none;
}
div{
    background-image: url('/src/img/keyEES1111111.png');
}

运行npm run dev 或 npx webpack 直接编译 在浏览器中可以看到刚才的图片

image-20250330164314085

html-loader处理htnl里面的<img src=" ">标签

它可以将 HTML 导出为字符串。当编译器需要时,将压缩 HTML 字符串

默认情况下,每个可加载属性(例如 - <img src="image.png"/> )都将被导入( const img = require ('./image.png')import img from "./image.png""

html-loader官方文档

安装html-loader

npm i -D html-loader

webpack.config.js中添加模块规则配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/i,
        loader: 'html-loader',
      },
    ],
  },
};

现在在index.html中添加img标签,因为配置了html-loader 他会扫描img的src属性,然后将图片转译自动将图片输出到dist目录中,输出的目录由资源模块的generator.filename决定

    。。。。。。。。。。。。。。。。
    
    <li>第8个元素</li>
    </ul>
    <!-- 添加一个div -->
    <div style="width: 500px;height: 300px;"></div>
    <img src="./img/New_Docker_logo_Logo.jpg" alt="">
    
   。。。。。。。。。。。。。。。。。。

npm run dev 或 npx webpack 直接编译 运行结果如下,img标签生效了,及时在dist中直接打开index.html也可以正常显示

image-20250331145239122

3.5.5.多入口多页面

当我们需要将src中的多个html页面和对应的多个js进行单独的绑定,多入口就是指的入口由多个单独的js,多页面就是指的有多个html页面,每个单独的html可以都可以引入自己的js,例如 我有两个html页面index.html和a.html,index.html需要引入自己的index.js,而a.html需要引入自己的a.js,然后需要在index.html中跳转到a.html中,这些都需要用到多入口多页面。 结构如下 image-20250331150459879

因为需要多入口和多页面,就需要手动配置如下内容

module.exports = {
   entry: {
     //index入口
     index: './src/index.js',
     // a入口
     a: './src/a.js',
   },
  plugins: [
    //index.html页面
    new HtmlWebpackPlugin({
        //生成的html文件名字
        filename: 'index.html',
        //源模板html位置
        template: './src/index.html'
        //入口名字,通过这里会自动添加<script src=""></script>标签
      	chunks:'index'
    }),
    // a.html页面
   new HtmlWebpackPlugin({
    //生成的html文件名字
    filename: 'a.html',
    //源模板html位置
    template: './src/a.html'
    //入口名字,通过这里会自动添加<script src=""></script>标签
    chunks:'a'
	}),
  ],
   output: {
     // 根据入口名字生成js
     filename: '[name].js',
     path: path.resolve(__dirname, 'dist'),
   },

通过以上配置可以发现,如果手动配置还是非常累的,每新增一个html和js都要来手动配置,为了解决手动配置问题,我们可以自己写一个函数来自动配置。

安装glob模块,该模块是一个可以通过正则表达式快速扫描目录和目录中的文件

npm install glob -D

webpack.config.js中导入glob模块,编写自动配置函数

let path = require('path');
const glob = require('glob');

//自动配置多入口和多页面函数,需要根据自己的目录结构按需更改
function getMpa() {
  const entry = {}, htmlPlugins = []
  const files = glob.sync('src/*.js')
  files.forEach((file) => {
    const filename = file.split(path.sep)[1].split(".")[0]
    entry[filename] = path.join(__dirname, 'src',filename+'.js')
    htmlPlugins.push(
      new HtmlWebpackPlugin({
        template: path.join(__dirname, `src/${filename}.html`),
        filename: `${filename}.html`,
        chunks: [filename],
      })
    )
  })
  return { entry, htmlPlugins }
}

//调用配置多入口和多页面函数
const mpa = getMpa()

module.exports = {
  //.........
  //入口文件(需要转换的源码文件)
  entry: mpa.entry,
  //出口文件(编译好的兼容文件)
  output: {
     //编译时先清除dist目录
     clean: true,
     //生成的文件名
     filename: '[name].js',
     //生成的路径
     path: path.resolve(__dirname, 'dist'),
  },
  // 注册插件
  plugins: [...mpa.htmlPlugins],
  
  //。。。。。。。。。。
  
};

npm run dev 或 npx webpack 直接编译 在dist目录下就可以看到以下内容了 image-20250331153411292

直接在浏览器中打开index.html就可以了,在index.html中通过a标签跳转到a.html页面也是可以的。

3.5.6.JavaScript高级语法处理

因为webpack无法处理一些ES6的高级语法并且很多浏览器的内核比较老,一些es6以上的语法无法在他们上面运行,所以需要通过babel进行降级,转译成es5。

安装babel转换器

npm i @babel/core @babel/runtime babel-loader -D 

安装babel语法插件

npm install -D @babel/plugin-transform-runtime  @babel/preset-env @babel/plugin-proposal-class-properties

配置babel

在项目根目录中创建babel.config.js,添加以下内容

module.exports={
    presets: ['@babel/preset-env'],
    plugins: ['@babel/plugin-transform-runtime','@babel/plugin-proposal-class-properties']
}

webpack.config.js的module.rules中编写babel加载器规则

module: {
    // 配置模块规则
    rules: [
      {
        // 配置匹配规则
        test: /\.m?js$/,
        // 排除安装模块中的js
        exclude: /(node_modules|bower_components)/,
        // 填写加载器
        use:'babel-loader'
      }
    ]
}

这样配置后就可以自动转换ES6高级语法了

3.5.7.Vue单文件组件

因为webpack无法处理vue中的单文件组件(.vue结尾的文件),所以需要使用加载器来单独处理这个vue文件

Vue Loader官方文档

安装vue loader

npm install -D npm i vue-loader@15 vue-template-compiler@^2

在webpack.config.js中配置加载器,注意:vue-loader加载器必须写在所有规则前面

// 引入加载器模块
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  module: {
    rules: [
      // ... 其它规则
      {
        //配置匹配规则
        test: /\.vue$/,
        use: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin()
  ]
}

安装vue模块

npm i -S vue@^2

编写一个vueApp.vue单文件组件,内容如下:

<template>
    <div>
        <h1>{{ message }}</h1>
        <button v-on:click="alertMessage">点击我</button>
    </div>
</template>

<script>
export default {
    data: function () {
        return {
            message: "Hello Vue"
        }
    },
    methods: {
        alertMessage: function () {
            alert(this.message)
        }
    }
}
</script>
<style scoped>
h1 {
    background-color: antiquewhite;
}
button{
    width: 150px;
    height: 150px;
    background-color: blue;
    color:#fff;
}
</style>

在index.html嵌入vue的根标签

   。。。。。。。。。。。。。。。。。。。   
      <li>第6个元素</li>
        <li>第7个元素</li>
        <li>第8个元素</li>
    </ul>
    <!-- 添加一个div -->
    <div style="width: 500px;height: 300px;" class="cssDiv"></div>
    <img src="./img/New_Docker_logo_Logo.jpg" alt="" style="width: 500px;">

    <hr>
    <div>vue单页面组件</div>
    <div id="app"></div>
    
   。。。。。。。。。。。。。。。

在index.js中引入vue模块并配置

import $ from 'jquery'
import './css/index.css'
import './css/index.less'
 
//........................................

console.log("-----------------")
console.log("vue单组件")
import Vue from "vue"
import vueApp from './vueApp.vue'

new Vue({
    el:'#app',
    render:h=>h(vueApp)
})

运行npm run dev 访问,得到以下结果,点击弹出对话框,表示成功。。。。。。。

image-20250331181816681

运行npx webpack打包结果如下,直接打开浏览器也能得到如上图所示结果。

image-20250331182233422



Comment