Node.js与Webpack
Node.js与Webpack
以下为学习过程中的极简提炼笔记,以供重温巩固学习
学习准备
准备工作
html+css+JavaScript 3剑客都懂一点,完成AJAX原理,熟悉AJAX函数与调用,熟悉AJAX前端接口处理
学习目的
Node.js为通过javscript写后端的一种框架
学习插件如何使用,如何去引入,比起记住插件的效果更重要
Node.js
- 定义:JavaScript运行时的环境
MDN官网解释:是一个跨平台 JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序
- 作用:可以使用 Node.js 编写服务器端程序(可以写后端)
- 包括:
- 编写后端数据接口,或提供服务器本地的网页资源浏览功能等等
- 前端工程化:为后续学习 Vue 和 React 等框架做铺垫
前端工程化
定义:
- 前端工程化:开发项目直到上线,过程中集成的所有工具和技术
- Node.js 是前端工程化的基础(因为 Node.js 可以主动读取前端代码内容)
Node.js/前端工程化包含哪些概念?
- 代码压缩工具,比如将代码当中的回车、换行、注释、长变量名变成短的,作压缩处理,目的为了前端项目体积更小,更好更快地在用户客户端上运行
- 格式化工具,当多人协同参与同一个项目开发时,需要统一代码风格
- 转换工具,如编写时使用css预处理语言,需要将这些转换成原生的css,也能对js的高版本语法作降级处理
- 打包工具,webpack是静态模块打包工具,能对前端代码压缩、转换、整合等处理
- 脚手架工具,前端用到的准备工具预集合成框架脚手架,开发时依靠工具,重点完成项目需求和业务逻辑编写即可
- 自动化部署等
Node.js 为何能执行JS?
- 首先:浏览器能执行jS 代码,依靠的是内核中的 V8 引擎(C++ 程序)
- 其次:Node.js 是基于 Chrome V8 引擎进行封装(运行环境)
- 区别:都支持 ECMAScript标准语法,包括string、number、settimeout、console、promise等,Node.js有独立的API,包括fs、path、http,浏览器的document、window、xmlhttprequest不支持
- 注意:Node.js 环境没有 DOM 和 BOM 等
入门
终端调用:
- 查看当前使用的Node.js版本:
node -v
- 执行JS:
node 01.js
- 查看当前使用的Node.js版本:
安装:
- 要求:下载 node-v16.19.0.msi 安装程序(指定版本:兼容 vue-admin-template 模板)其实更新的应该也可以
- 注释事项:
- 安装在非中文路径下
- 无需勾选自动安装其他配套软件
- 注意,没有图形化界面,需要终端运行
- 成功验证:
- 打开 cmd 终端,输入node -v命令查看版本号
- 如果有显示,则代表安装成功
- vscode中,通过terminal.integrated.defaultProfile.windows设置运行的终端种类,或者+号右侧右键,默认终端配置
- powershell是所在路径shift+右键;cmd是win+r+cmd
- 注意:powershell是win10出的新终端,默认不允许执行自定义脚本命令
运行案例:
- 随便写点 随便.js
console.log(`666666`) for(let i =0 ;i<6;i++){ console.log(12345) }
- 在vscode的左上角资源管理器中,选中这个 随便.js 这个文件,右键,集成终端中打开;好处,自动到文件目录下打开终端,不需要额外路径
意义: Node.js能主动读取前端编写的js,并且能写入
fs 模块 - 读写文件
- 模块:类似插件,封装了方法/属性
- fs 模块:封装了与本机文件系统进行交互的,方法/属性
- 语法:
- 加载 fs 模块对象到自己编写的js文件中,运用固定名字require()函数,需要引入哪个模块,就require('哪个模块的名字'),
- 这里加载 fs 模块,该模块是node.js内部自带的,引入后会在原地留下一个对象,通过自己定义的常量接收
const fs = require('fs')// 'fs'是模块标识符:模块的名字
- 写入文件内容,通过调用此fs模块内封装好的writeFile方法,来写入文件内容,需要传入3个参数 (参数1:目标文件路径;参数2:写入内容建议使用字符串,;参数3:是一个回调函数,例,在该回调函数形参中写err,如果写入过程中有错误,err参数就有值)
fs.writeFile('目标文件路径','写入内容',err => { //写入后的回调函数 })
- 读取文件内容,通过调用此fs模块内封装好的readFile方法,来读取文件内容,需要传入2个参数 (参数1:目标文件路径;参数2:本次读取完的结果的回调函数,有2个形参,一个是错误信息,一个是16进制的 Buffer数据流 对象,调用对象中的toString方法可以转换成字符串)
fs.readFile('文件路径’,(err, data) => { // 读取后的回调函数 // data 是文件内容的 Buffer 数据流 })
// 1.加载 fs 模块对象
const fs = require('fs')
// 2.写入文件内容
// 例,在该回调函数形参中写err,如果写入过程中有错误,err参数就有值
fs.writeFile('./test.txt','hello,Node.js',(err)=>{
// 如有错误信息,则打印错误信息
if(err)console.log(err)
else console.log('写入成功')
})
// 3.读取文件内容
fs.readFile('./test.txt',(err, data)=>{
if(err)console.log(err)
// data是Buffer 16进制数据流对象,通过.tostring()方法转回字符串
else console.log(data.tostring())
})
到vscode资源管理器中,选中 XXX.js 文件,右键,在集成终端中打开
node XXX.js
path 模块 - 路径处理
- 问题:Node.js 代码中,相对路径是根据终端所在路径来查找的,可能无法找到你想要的文件
我们在nodejs环境中执行js代码,它的所有相对路径是以终端文件夹作为起点
- 建议:在 Node.js 代码中,使用绝对路径
- 补充:
- __dirname 内置变量(动态获取当前模块目录-绝对路径)
- windows: D:\备课代码\3-B站课程\03 Node.js与Webpack\03-code\03
- mac电脑:/Users/xxx/Desktop/备课代码/3-B站课程/03 Node.js与Webpack/03-code/03
- __dirname 内置变量(动态获取当前模块目录-绝对路径)
- 注意:path.join()会使用特定于平台的分隔符,作为定界符,将所有给定的路径片段连接在一起
- 语法:
- 加载 path 模块
const path = require('path')
- 使用 path.join 方法,拼接路径
path.join('路径1','路径2',...)
- 意义:使用path模块的join方法,结合_dirname生成的绝对路径,可在任意位置开启的终端找到所需文件
// 目标:在 Node.js环境的代码中,应使用绝对路径
// 原因:代码的相对路径是以终端所在文件夹为起点,而不是Vscode 资源管理器
// 容易造成目标文件找不到的错误
const fs = require('fs')
// 1.引入 path 模块对象
const path = require('path')
//2.调用 path.join()配合__dirname 组成目标文件的绝对路径
// 先打印这个内置变量
console.log(__dirname)
fs.readFile(path.join(__dirname,'../test.txt'),(err, data)=>{
if(err)console.log(err)
else console.log(data.tostring())
})
备注:cls 清空终端
案例 压缩前端html文件
- 需求:把 回车符(\r)和换行符(\n)去掉后,写入到新 html文件中
- 实际:nodejs分析了html标签和css样式节点,并转换成js对象结构的抽象语法树
- 步骤:
- 通过编写node.js控制实现
- 读取源 html 文件内容
- 正则替换字符串
- 写入到新的 html 文件中(替换后的结果,输出新的 html 文件,供程序员编辑维护用,实际压缩后的html,只能给浏览器解析用)
/**
* 目标1:压缩 html 代码
* 需求:把回车符 \r,换行符 \n 去掉,写入到新 html 文件中
* 1.1 读取源 html 文件内容
* 1.2 正则替换字符串
* 1.3 写入到新的 html 文件中
*/
// 1.1 读取源 html 文件内容
const fs = require('fs')
const path = require('path')
// 拼接绝对路径,自动链接,所以public前面不用加斜杠/
fs.readFile(path.join(__dirname, 'public/index.html'), (err, data) => {
if (err) console.log(err)
else {
// 读取网页文件字符串
console.log(data.toString());
const htmlStr = data.toString()
// 1.2 正则替换字符串,正则表达式替换字符串
const resultStr = htmlStr.replace(/[\r\n]/g, '')
console.log(resultStr)
// 1.3 写入到新的 html 文件中,输出路径是dist/,注意路径要对,只会创建文件,不会创建路径,意味路径不存在会报错
fs.writeFile(path.join(__dirname, 'dist/index.html'), resultStr, err => {
if (err) console.log(err)
else console.log('写入成功')
})
}
})
JavaScript 中常用的正则表达式包括:
文字匹配
^:匹配字符串的开始。
$:匹配字符串的结束。
.:匹配除换行符外的任何字符。
*:匹配前面的字符 0 次或多次。
+:匹配前面的字符 1 次或多次。
?:匹配前面的字符 0 次或 1 次。
[]:匹配方括号中指定的字符范围。
[^]:匹配不在方括号中指定的字符范围内的字符。
数量词
{n}:匹配前面的字符 n 次。
{n,}:匹配前面的字符至少 n 次。
{n,m}:匹配前面的字符 n 到 m 次。
分组和引用
():将正则表达式的一部分分组。
\1:引用第 1 个分组匹配的文本。
特殊字符
\d:匹配数字。
\w:匹配单词字符(字母、数字和下划线)。
\s:匹配空白字符(空格、制表符、换行符)。
\b:匹配单词边界。
断言
(?=):匹配后面的正则表达式,但不包括在匹配结果中。
(?!):匹配不满足后面的正则表达式的部分。
修饰符
i:不区分大小写。
g:全局搜索,匹配所有匹配项。
m:多行搜索,将字符串视为多行。
此外,JavaScript 还支持 Unicode 正则表达式,可以匹配非 ASCII 字符
===============================
// 给定一个包含换行符和回车符的 HTML 字符串 htmlStr
// 使用正则表达式匹配所有换行符和回车符
const resultStr = htmlStr.replace(/[\r\n]/g, '');
// 替换所有匹配项为空字符串,从而删除换行符和回车符
这段代码的作用是删除 HTML 字符串中的所有换行符和回车符。
正则表达式 [\r\n] 匹配所有换行符 (\r) 和回车符 (\n)。
g 修饰符表示全局搜索,这意味着它将匹配字符串中的所有换行符和回车符,而不仅仅是第一个匹配项。
replace() 方法用空字符串替换所有匹配项,从而有效地删除了换行符和回车符。
URL 中的端口号
URL:统一资源定位符,简称网址,用于访问服务器里的资源
端口号:标记服务器里不同功能的服务程序
端口号范围:0-65535 之间的任意整数
注意:http 协议,默认访问 80 端口(所以之前访问接口的时候没有加端口号,也能获取到数据)
url=协议+域名+端口号+具体资源路径=http://域名:80/xxx/xxx
web服务程序,专门给浏览器提供接口数据,或音视频流
常见web服务端口80、3000、8080;数据库服务端口:3306
常见的服务程序
- Web 服务程序:用于提供网上信息浏览功能
- 注意:0-1023 和一些特定端口号被占用,我们自己编写服务程序请避开使用
- Web 服务程序:用于提供网上信息浏览功能
常见的端口种类1
端口 | 协议 | 用途 |
---|---|---|
20 | FTP | 文件传输协议 |
21 | FTP | 文件传输协议(数据) |
22 | SSH | 安全外壳协议(远程登录) |
23 | Telnet | 远程登录协议 |
25 | SMTP | 简单邮件传输协议(发邮件) |
53 | DNS | 域名系统(域名解析) |
80 | HTTP | 超文本传输协议(浏览网页) |
443 | HTTPS | 安全超文本传输协议(加密浏览网页) |
3306 | MySQL | MySQL 数据库 |
5432 | PostgreSQL | PostgreSQL 数据库 |
6379 | Redis | Redis 缓存 |
8080 | HTTP | HTTP 代理 |
27017 | MongoDB | MongoDB 数据库 |
此外,还有许多其他端口用于各种特定服务和应用程序,例如:
**游戏端口:**如 27015(Minecraft)、25565(Minecraft 多人游戏)
**流媒体端口:**如 1935(RTMP)、8080(HTTP 流媒体)
**打印机端口:**如 9100(HP 打印机)、515(SMB 打印)
**VPN 端口:**如 1194(OpenVPN)、1723(PPTP)
端口号的范围从 0 到 65535,
其中 0-1023 为 well-known 端口,由 IANA(互联网号码分配机构)分配给标准协议。
1024 及以上的端口称为非 well-known 端口,可用于自定义应用程序和服务。
- 端口号的作用?
- 标记区分服务器里不同的服务程序
- 什么是 Web 服务程序?
- 提供网上信息浏览的程序代码
http 模块-创建 Web 服务
需求:基于node.js中的http模块,创建 Web 服务,并响应内容给浏览器
步骤:
- 加载引入 http 模块,创建 Web 服务对象,通过require('http')语句加载,并赋予给常量http
- 使用内置的.createserver()方法在原地得到web服务对象,并赋予给常量server保存
- 针对web服务对象,监听 request 请求事件,'request'是请求的名字是固定的,当浏览器有请求时,会触发后面回调函数,有两个形参(req,res),设置响应头和响应体
- req,res两个形参,实际两个形参名字可以随意定义设置
- 第一个形参是本次请求发过来的一些请求参数
- 通过回调函数中的第二个形参,也就是res,也叫做响应对象,通过响应对象内置的.end方法,来结束本次请求和响应的过程,并向请求方返回字符串内容
- 字符串中包括中文字符,为了让请求方正确编码、解析、展示,还需要响应头,通过调用响应对象/响应体内置的.setheader方法
- .setheader方法中两个参数,参数1是一个固定字段'content-Type'意为告诉请求方内容类型;参数2也是一个固定字段text/plain意为普通文本;charset=utf-8设置本次响应的字符串的普通文本的编码格式为utf-8
- 通过调用内置的.listen方法,为当前web服务对象,配置端口号,并启动 Web 服务
- 浏览器请求 http://localhost:3000 测试
备注:localhost:固定代表本机的域名
// 加载引入http模块对象并赋予常量'http'
// 1拿到模块对象
const http = require('http')
// 2创建web服务对象
const server = http.createserver()
// 3监听 request 请求事件,设置响应头和响应体,当有请求时,会触发对应的回调函数
server.on('request',(req,res)=>{
//设置响应头:内容类型,普通文本;编码格式为 utf-8
res.setHeader('content-Type','text/plain;charset=utf-8')
res.end('您好,欢迎使用 node.js 创建的 Web 服务')
})
server.listen(3000,()=>{
console.log('Web 服务已经启动')
})
执行时server.js,与之前不同,没有停止执行,它会在终端里启动一个进程,进程持续存在,监听有无请求,有请求就会触发处理函数,返回一个响应内容。
之前执行完后,会立刻出现路径,再让你输入下一条命令
Web 服务 案例-浏览时钟
需求:基于 Web 服务,开发提供网页资源的功能
实际流程:
- 浏览器在地址栏中访问地址+端口+资源路径
- 编写node.js,对浏览器给过来的:
访问地址+端口+资源路径
获取,并做一个判断, - 将对应请求的、具体路径下压缩的html内容,给读取到js代码中,配合响应对象将其中的HTML标签,css样式,js等字符串,作为响应体相应内容,返回请求方
- 浏览器解析,并将网页渲染
步骤:
- 基于 http 模块,创建 Web 服务
- 使用 req.url获取请求资源路径,判断并读取 index.html 里字符串内容返回给请求方
- 其他路径,暂时返回不存在的提示
- 运行 Web 服务,用浏览器发起请求测试
/**
* 目标:基于 http 模块创建 Web 服务程序
* 1.1 加载 http 模块,创建 Web 服务对象
* 1.2 监听 request 请求事件,设置响应头和响应体
* 1.3 配置端口号并启动 Web 服务
* 1.4 浏览器请求(http://localhost:3000)测试
*/
const fs = require('fs')
const path = require('path')
// 1.1 加载 http 模块,创建 Web 服务对象
const http = require('http')
const server = http.createServer()
// 1.2 监听 request 请求事件,设置响应头和响应体
server.on('request', (req, res) => {
// 在请求事件的处理函数中,第一个形参req这个信息对象中,获取本次获取的资源路径字符串.url
if(req.url ==='/index.htm1'){
// 如果符合,读取对应压缩后的网页,返回内容给请求方,响应体需要以text/html超文本字符串返回
fs.readFile(path.join(__dirname,'dist/index.htm1'),(err,data) =>{
// 如果有错,原地打印
if(err)console.log(err)
else{
// 设置响应头-内容类型-html超文本字符串,以及编码格式,让浏览器解析成标签
res.setHeader('content-Type','text/html:charset=utf-8')
// 设置响应体内容,结束本次请求与响应
res.end(data.tostring())
}
})
}else{
// 设置响应头-内容类型-html超文本字符串,以及编码格式,让浏览器解析成标签
res.setHeader('content-Type','text/html:charset=utf-8')
// 设置响应体内容,结束本次请求与响应,超文本也能包含普通字符串,格式还是utf-8
res.end('你要访问的路径不存在')
}
})
// 1.3 配置端口号并启动 Web 服务
server.listen(8080, () => {
console.log('Web 服务启动成功了')
})
Node.js 模块化(不同文件模块相互引用)
什么是模块化?
定义:
- CommonJs 模块是为 Node.js 打包 Javascript 代码的原始方式。
- Node.js 还支持浏览器和其他 Javascript 运行时使用的 ECMAScript 模块 标准
- 在 Node.js 中,每个文件都被视为一个单独的模块
- 使用时引入,不仅支持引入nodejs自带的模块,还支持自定义模块引入
概念:项目是由很多个模块文件组成的
好处:提高代码复用性,按需加载,独立作用域(不需要担心变量污染)
使用:需要标准语法导出和导入进行使用,标准语法,即:CommonJs标准,规定了模块间的导出和导入语法
总结:在nodejs中,每个文件都是独立作用域的单独模块
意义:后续如果是做项目,可以使用现成的算法封装好的模块导入,也能自己封装业务逻辑做模块
总结:
- Node.js 支持哪 2 种模块化标准?
- Commons 标准语法(默认)
- ECMAScript 标准语法
- ECMAScript 标准,命名导出和导入的语法?
- 导出:export 修饰定义的语句
- 导入:import {同名变量} from '模块名或路径'
- 在定义常量const或者定义函数function前,加export关键字,代表当前的这个常量/函数已经被导出,将你想对外暴露的常量/函数前加export即可
- ECMAScript 标准,默认导出和导入的语法?
- 导出:export default {}
- 导入:import 变量名 from '模块名或路径'
- 导出的是一个大对象,导入的时候也是往回导入这个大对象
- 命名导出和默认导出是可以同时使用的
- Node.js 支持哪 2 种模块化标准?
CommonJs标准导出与导入语法案例
需求:定义 utils.js 模块,封装基地址和求数组总和的函数
使用:
- 导出:
module.exports ={}
- 导入:
require('模块名或路径')
- 模块名或路径:
- 内置模块:直接写名字(例如:fs,path,http)
- 自定义模块:写模块文件相对路径(例如:./utils.js)
- 导出:
CommonJs标准语法使用
模块导出.js
// 基地址常量
const baseURL = 'http://hmajax.itheima.net'
// 求数组总和
const getArraySum = arr => arr.reduce((sum, val)=> sum += val, 0)
module.exports ={
// 自定义对外暴露的属性名(作为一个对象导出):模块中的私有常量
对外属性名1: baseURL,
对外属性名2: getArraysum
}
需要导入模块的index.js
const obj= require('模块名或相对路径')
// obj 就等于 module.exports 导出的对象
CommonJs标准导入导出案例
// 目标:基于 CommonJ5 标准语法,封装属性和方法并导出
const baseURL ='http://hmajax.itheima.net'
const getArraySum = arr => arr.reduce((sum,item) => sum += item,0)
// 导出
module.exports = {
url: baseURL,
arraySum: getArraySum
}
// 目标:基于 Common1s 标准语法,导入工具属性和方法使用
// 导入
const obj= require('./utils.js')
// 打印拿到的对象,是{url:'http://hmajax.itheima.net',arraySum:[Function:getArraySum]}
console.log(obj)
// 将拿到的对象中的arraySum传入参数,求数组总和
const result =obj.arraySum([5,1,2,3])
console.log(result)
# 在vscode资源管理器选中index.js,右键,在集成终端中打开
node index.js
ECMAScript 标准 - 默认导出和导入
需求:封装并导出基地址和求数组元素和的函数
默认标准使用:
- 导出:
export default {}
- 导入:
import 变量名 from '模块名或相对路径'
- 导出的是一个大对象,导入的时候也是往回导入这个大对象,用变量名接收
- 导出:
注意:Node.js 默认支持 Commons 标准语法
- 如需使用 ECMAScript 标准语法,在运行模块所在文件夹 新建 package.json 文件(项目清单文件),并设置
{"type":"module"}
- 如需使用 ECMAScript 标准语法,在运行模块所在文件夹 新建 package.json 文件(项目清单文件),并设置
要点:如何让 Node.js 切换模块标准为 ECMAScript?
- 运行模块所在文件夹,新建 package.json
- 并设置
ECMAScript标准,默认模块导出导入语法
默认导出.js
// 基地址常量
const baseURL = 'http://hmajax.itheima.net'
// 求数组总和
const getArraySum = arr => arr.reduce((sum, val)=> sum += val, 0)
export default ={
// 自定义对外暴露的属性名(作为一个对象导出):模块中的私有常量
对外属性名1: baseURL,
对外属性名2: getArraySum
}
需要导入默认模块的index.js
import obj from '模块名或相对路径'
// obj 就等于 export default 导出的对象
使用设置,package.json
<!-- 写到json -->
{"type":"module"}
ECMAScript标准 - 默认导入导出案例
// 目标:基于 ECMAScript封装属性和方法并 默认 导出utils.js
const baseURL ='http://hmajax.itheima.net'
const getArraySum = arr => arr.reduce((sum,item) => sum += item,0)
// 导出
export default = {
url: baseURL,
arraySum: getArraySum
}
// 目标:基于 ECMAScript,默认导入工具属性和方法
// 默认导入,写相对路径
import obj from './utils.js'
// 打印拿到的对象,是{url:'http://hmajax.itheima.net',arraySum:[Function:getArraySum]}
console.log(obj)
// 将拿到的对象中的arraySum传入参数,求数组总和
const result = obj.ArraySum([10,20,30])
console.log(result)
{"type":"module"}
# 在vscode资源管理器选中index.js,右键,在集成终端中打开
node index.js
ECMAScript 标准 - 命名导出和导入
需求:封装并导出基地址和求数组元素和的函数
命名标准使用:
- 导出:
export 修饰定义语句
- 导入:
import {同名变量} from '模块名或相对路径'
- 在定义常量const或者定义函数function前,加export关键字,代表当前的这个常量/函数已经被导出,将你想对外暴露的常量/函数前加export即可
- 导出:
如何选择:
- 按需加载,使用命名导出和导入
export const......
;import {同名变量} from
- 全部加载,使用默认导出和导入
export default
;import 变量名 from
- 按需加载,使用命名导出和导入
ECMAScript标准,命名模块导出导入语法
命名模块导出.js
// 基地址常量
export const baseURL = 'http://hmajax.itheima.net'
// 求数组总和
export const getArraySum = arr => arr.reduce((sum, val)=> sum += val, 0)
需要导入命名模块的index.js
import {baseURL,getArraySum} from '模块名或相对路径'
// baseURL 和 getArraySum 是变量,值为模块内命名导出的同名变量的值
不要忘了,也是需要在package.json中设置 ECMAScript标准使用设置,package.json
<!-- 写到json -->
{"type":"module"}
ECMAScript标准 - 命名导入导出案例
// 目标:基于 ECMAScript 封装属性和方法并 命名 导出utils.js
export const baseURL ='http://hmajax.itheima.net'
export const getArraySum = arr => arr.reduce((sum,item) => sum += item,0)
// 目标:基于 基于 ECMAScript,导入封装属性和方法
// 命名导入
import {baseURL,getArraySum} from './utils.js'
// 打印拿到的对象,是{url:'http://hmajax.itheima.net',arraySum:[Function:getArraySum]}
console.log(baseURL)
console.log(getArraySum)
// 将拿到的对象中的arraySum传入参数,求数组总和
const result =getArraySum([10,21,33])
console.log(result)
{"type":"module"}
# 在vscode资源管理器选中index.js,右键,在集成终端中打开
node index.js
总结:
- Node.js 支持哪 2 种模块化标准?
- Commons 标准语法(默认)
- ECMAScript 标准语法
- ECMAScript 标准,命名导出和导入的语法?
- 导出:
export 修饰定义的语句
- 导入:
import {同名变量} from '模块名或路径'
- 在定义常量const或者定义函数function前,加export关键字,代表当前的这个常量/函数已经被导出,将你想对外暴露的常量/函数前加export即可
- 导出:
- ECMAScript 标准,默认导出和导入的语法?
- 导出:
export default {}
- 导入:
import 变量名 from '模块名或路径'
- 导出的是一个大对象,导入的时候也是往回导入这个大对象,用变量名接收
- 导出:
- 命名导出和默认导出是可以同时使用的
- Node.js 支持哪 2 种模块化标准?
附一个简单理解:
- 命名导入和默认导入的区别:
- 如果是对另一个文件中的 方法/或函数 的引入,使用花括号,本页面中直接使用 方法/或函数
- 如果是对另一个文件整个文件完整引入,例如静态资源、组件引入、引入分拆的模块等,不需要花括号
- 命名导入和默认导入的区别:
包的概念
包:将模块,代码,其他资料聚合成一个文件夹
包分类:
- 项目包:主要用于编写项目和业务逻辑
- 软件包:封装工具和方法进行使用
要求:根目录中,必须有 package.json 文件(清单文件,记录包的清单信息)
注意:导入软件包时,引入的默认是 index.js 模块文件/main 属性指定的模块文件
package.json 文件中,常用的固定名字配置项包括:
- "name":"软件包名称"
- "version":"软件包当前版本1.0.0"
- "description":"软件包简短描述,可以中文字符"
- "main":"软件包入口点,某某xx.js"
- "author":"软件包作者"
- "license":"软件包许可证(MIT:麻省理工许可证,商用后可以用作者名字宣传)"
导包,即:包封装好后,其他开发者导入包时,只需要写到包所在的文件夹名字/路径即可,运行时,会默认自动找文件夹下固定名称index.js的文件,如果没有则找package.json 文件中"main"属性中指定的:"某某xx.js"引入
需求:封装数组求和函数的模块,判断用户名和密码长度函数的模块,形成成一个软件包
- 不同模块在
文件结构:
- 某项目根目录文件夹包括:server.js和utils软件包文件夹
- utils软件包文件夹包括:index.js和lib文件夹
- lib文件夹包括:封装的各个方法、函数(library简写,库)
// 例,封装的函数
// str.js
const checkUserName = username => {
return username.length >= 8
}
const checkPassWord = passWord => {
return passWord.length >= 6
}
module.exports ={
checkUser:checkUserName,
checkPwd:checkPassWord
}
// arr.js
const getArraySum = arr => arr.reduce((sum,item) => sum += item,0)
module.exports ={
getArraySum
}
// 本文件是,utils 工具包的唯一出口
// 作用:把所有工具模块方法集中起来,统一向外暴露
// 等于 const arrObj = require('./lib/arr.js'),即 arrObj 等于这个{ getArraysum:getArraysum }对象,连带解构赋值+属性变量同名,简写 { getArraysum }
const { getArraysum } = require('./lib/arr.js')
const { checkUser, checkPwd }= require('./lib/str.js' )
// 统一导出所有函数
module.exports ={
getArraySum,
checkUser,
checkPwd
}
// 目标:导入 utils 软件包,使用里面封装的工具函数,引入文件夹会自动找其中的index.js入口文件
// 那么这里引入的obj对象,就是index.js中导出的对象module.exports ={getArraySum,checkUser,checkPwd}
const obj= require('./utils')
console.log(obj)
const result= obj.getArraysum([10,20,30])
console.log(result)
<!-- json -->
{
"name":"cz utils",
"version":"1.0.0",
"description":"一个数组和字符串常用工具方法的包",
"main": "index.js",
"author":"itheima",
"license":"MIT",
}
- 总结:
- 什么是包?
- 将模块,代码,其他资料聚合成的文件夹;文件夹叫包,文件就叫模块
- 包分为哪 2 类呢?
- 项目包:编写项目代码的文件夹,开发业务相关逻辑代码
- 软件包:封装工具和方法供开发者使用
- package.json 文件的作用?
- 记录软件包的名字,作者,入口文件,版本号等信息
- 导入一个包文件夹的时候,导入的是哪个文件?
- 默认 index.js 文件,或者 main 属性指定的文件
- 什么是包?
npm 软件包管理器(Node.js 命令)
定义:
- npm 是 Node.js 标准的软件包管理器
- 在 2017 年1月时,npm 仓库中就已有超过 350000 个软件包,这使其成为世界上最大的单一语言代码仓库,并且可以确定几乎有可用于一切的软件包。
- 它起初是作为下载和管理 Node.js 包依赖的方式,但其现在也已成为前端JavaScript 中使用的工具。
使用:
- 初始化清单文件:
npm init -y
- 得到 package.json 文件,有则略过此命令;init意为初始化;-y意为所有配置项使用默认值
- 使用默认值配置项时,所在文件夹路径不能有中文字符或特殊符号
- 下载软件包:
npm i 软件包名称
- 使用软件包
- 备注1:package.json 文件也用于记录当前软件项目下载过/包含哪些软件包
- 备注2:通过 npm 下载导入的软件包,存放在 node_modules ,引入时只需要写名字
- 备注3:删除/卸载软件包
npm uni 软件包名称
;全局删除/卸载软件包npm uni 软件包名称 -g
- 备注4:包管理文件是一个项目必须的,可以是package.json这种npm的包管理记录文件,可以是其他yarn、pnpm等管理器的文件;克隆项目后,需要运行一下 i 命令,来下载依赖,以重新本地构建项目
- 初始化清单文件:
需求案例:
总结:
- npm 软件包管理器作用?
- 下载软件包以及管理版本
- 初始化项目清单文件 package.json 命令?
npm init -y
- 下载软件包的命令?
npm i 软件包名字
- 下载的包会存放在哪里?
- 当前项目下的 node_modules 中,并记录在 package.json 中
- npm 软件包管理器作用?
附:
1. npm(Node.js Package Manager)
- 特点:
- 专用于 Node.js 生态系统
- 庞大的软件包生态系统
- 易于安装和管理软件包
2. yarn
- 特点:
- 由 Facebook 开发,旨在提高 npm 的性能
- 离线模式支持,可以预先缓存软件包
- 确定性安装,确保在不同的机器上获得相同的结果
3. pip(Python Package Index)
- 特点:
- Python 生态系统的包管理器
- 广泛的软件包选择
- 支持安装从源代码和二进制文件构建的包
4. gem(RubyGems)
- 特点:
- Ruby 生态系统的包管理器
- 庞大的宝石集合
- 提供依赖项管理和版本控制
5. apt(Advanced Package Tool)
- 特点:
- 基于 Debian 的 Linux 发行版的包管理器
- 广泛的软件包选择
- 通过软件仓库管理软件包
6. yum(Yellowdog Updater, Modified)
- 特点:
- 基于 Red Hat 的 Linux 发行版的包管理器
- 类似于 apt,但针对 Red Hat 系统进行了优化
- 通过软件仓库管理软件包
7. brew(Homebrew)
- 特点:
- macOS 的包管理器
- Cask 提供对 GUI 应用程序的支持
- 易于安装和管理软件包,包括非开源软件
8. pacman(Pacman)
- 特点:
- Arch Linux 和其他 Arch-based 发行版的包管理器
- 基于依赖关系的滚动发布模式
- 提供快速且高效的软件包管理
9. zypper
- 特点:
- openSUSE 和 SUSE Linux Enterprise Server 的包管理器
- 基于依赖关系的滚动发布模式
- 提供强大的工具和命令行界面
通过 npm 安装所有依赖
使用背景:
- 从别处(网上、其他人手中)拿到写好的项目,一般不携带 node_modules文件夹(所占存储空间大)
- 但有package.json文件,里面记载了当前项目下载过的包
- 还有package-lock.json文件,固定软件包的版本
问题:项目中不包含 node_modules,能否正常运行?
- 答案:不能,缺少依赖的本地软件包
- 原因:因为,自己用 npm 下载依赖比磁盘传递拷贝要快得多
- 解决:下载 package.json 中记录的所有软件包,在项目终端输入命令:
npm i
- 意义:
npm i
后会按照 package.json 中记录的所有软件包及对应版本号去下载拉取,拿到别人的项目,先npm i
把依赖都导入好 - 存放:通过
npm i
拉取的软件包,都放在项目下的 node_modules文件夹 - 举例:全局安装astro@latest:
npm i astro@latest -g
注意:导入模块/包,除了自己创建的模块、包需要写地址,nodejs自带的模块以及使用npm安装的包,都直接写名字
总结:
- 当项目中只有 package.json 没有 node modules 怎么办?
- 当前项目终端下,执行
npm i
安装所有依赖软件包
- 当前项目终端下,执行
- 为什么 node modules 不进行传递?
- 因为用 npm 下载比磁盘传递要快
- 当项目中只有 package.json 没有 node modules 怎么办?
附:
淘宝 npm 镜像
https://registry.npm.taobao.org
网易 npm 镜像
https://registry.npm.netease.com
中科大 npm 镜像
https://npm.ustc.edu.cn/
北京大学 npm 镜像
https://npm.tuna.tsinghua.edu.cn/
Alibaba Cloud npm 镜像
https://mirrors.aliyun.com/npm/
腾讯云 npm 镜像
https://mirrors.cloud.tencent.com/npm/
华为云 npm 镜像
https://mirrors.huaweicloud.com/npm/
npm 官方原始镜像:https://registry.npmjs.org
淘宝 NPM 镜像:https://registry.npmmirror.com
阿里云 NPM 镜像:https://npm.aliyun.com
腾讯云 NPM 镜像:https://mirrors.cloud.tencent.com/npm
华为云 NPM 镜像:https://mirrors.huaweicloud.com/repository/npm
网易 NPM 镜像:https://mirrors.163.com/npm
中科院大学开源镜像站:http://mirrors.ustc.edu.cn
清华大学开源镜像站:https://mirrors.tuna.tsinghua.edu.cn
使用方法:
在终端中运行以下命令:
npm config set registry https://mirrors.cloud.tencent.com/npm/
npm config set registry https://registry.npmmirror.com
npm config set registry https://registry.npmjs.org
注意:
这些镜像源可能会因网络状况或其他因素而中断。
一些镜像源可能不包含所有 npm 包。
如果遇到问题,可以尝试切换到其他镜像源。
备注:
官方源地址: https://pypi.org/simple
部分模块国更新不及时需要切换到官网 执行下面的命令 或者-i
pip config set global.index-url https://pypi.org/simple
pip install xx -i https://pypi.org/simple
确认当前配置
npm config list
// =========================================================
pip换源
在 pip 命令中使用 -i 参数来指定镜像地址,例如:
pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
查看镜像地址
pip3 config list
# 清华源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# 阿里源
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
# 腾讯源
pip config set global.index-url http://mirrors.cloud.tencent.com/pypi/simple
# 豆瓣源
pip config set global.index-url http://pypi.douban.com/simple/
全局软件包 nodemon
软件包区别:
- 本地软件包:当前项目内使用,一般封装属性和方法,源代码存在于node modules
- 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置
nodemon 作用:替代 node 命令,检测代码更改,自动重启程序/服务
- 意义:配置热更新/热部署,项目运行后,可以实时修改js,会检测到代码更改自动重启程序/服务,英文本意应为node demon缩写
使用:
- 安装:
npm i nodemon -g
(-g 代表安装到全局环境中) - 运行:
nodemon
待执行的目标js 文件
- 安装:
需求:启动准备好的项目,修改代码保存后,观察自动重启应用程序
- 安装 nodemon,添加了32个包,使用npm安装软件包,依赖的其他包一并下载
- 修改数组的值,ctrl+s保存,程序重新执行
总结:
- 本地软件包和全局软件包区别?
- 本地软件包,作用在当前项目,封装属性和方法
- 全局软件包,本机所有项目使用,封装命令和工具
- nodemon作用?
- 替代 node 命令,检测代码更改,自动重启程序
- nodemon 怎么用?
- 先确保安装
npm i nodemon -g
- 使用
nodemon
执行目标js 文件
- 先确保安装
- 本地软件包和全局软件包区别?
附:
Nodemon 是一个 Node.js 开发工具,用于在文件更改时自动重启 Node.js 应用程序。它提供了以下用法:
// 1. 基本用法
nodemon [entry script]
// 例如:
nodemon app.js
// 2. 监视特定目录
nodemon --watch [directory]
// 例如:
nodemon --watch ./src
// 3. 忽略特定文件或目录
nodemon --ignore [file or directory]
// 例如:
nodemon --ignore ./node_modules
// 4. 指定要重启的脚本
nodemon --exec [command]
// 例如:
nodemon --exec "npm run start"
// 5. 启用扩展功能
nodemon --ext [file extension]
// 例如:
nodemon --ext js,jsx
// 6. 调试模式
nodemon --inspect
// 7. 配置文件
// Nodemon 还允许你创建一个配置文件(通常命名为 .nodemonrc.json)来配置其行为。以下是一些常见的配置选项:
watch: 要监视的目录或文件。
ignore: 要忽略的目录或文件。
ext: 要监视的文件扩展名。
exec: 要在文件更改时运行的命令。
inspect: 是否启用调试模式。
// 示例 .nodemonrc.json 配置文件:
{
"watch": [
"src"
],
"ignore": [
"node_modules"
],
"ext": "js,jsx",
"exec": "npm run start",
"inspect": true
}
// 8. 其他选项
-L, --legacy-watch: 使用旧的监视器,这可能对于某些文件系统更可靠。
-q, --quiet: 抑制冗余输出。
-V, --version: 打印 Nodemon 版本。
-h, --help: 打印帮助信息。
Node.js 总结
Node.js 模块化:
- 概念:每个文件当做一个模块,独立作用域,按需加载
- 使用:采用特定的标准语法导出和导入进行使用
CommonJS标准
导出 | 导入 | |
---|---|---|
语法 | module.exports = {} | require('模块名或相对路径') |
- ECMAScript标准
导出 | 导入 | |
---|---|---|
默认 | exports default = {} | import 变量名 from '模块名或相对路径' |
命名 | exports 修饰定义语句 | import {同名变量} from'模块名或相对路径' |
CommonS 标准:一般应用在 Node.js 项目环境中
ECMAScript 标准:一般应用在前端工程化项目中,即 webpack 做的前端项目
Node.js 包:
- 概念:把模块文件,代码文件,其他资料聚合成一个文件夹
- 项目包:编写项目需求和业务逻辑的文件夹
- 软件包:封装工具和方法进行使用的文件夹(一般使用 npm 管理)
- 本地软件包:作用在当前项目,一般封装的属性/方法,供项目调用编写业务需求
- 全局软件包:作用在所有项目,一般封装的命令/工具,支撑项目运行
通过npm下载导入的软件包,存放在node_modules,引入时只需要写名字
Node.js 常用命令
功能 | 命令 |
---|---|
执行js文件 | node xxx nodemon xxx |
初始化 package.json | npm init -y |
下载本地软件包 | npm i 软件包名 |
下载全局软件包 | npm i 软件包名 -g |
删除软件包 | npm uni 软件包名 |
全局删除软件包 | npm uni 软件包名 -g |
Webpack
定义:
- 本质上,webpack是一个用于现代 JavaScript 应用程序的 静态模块打包工具。
- 当webpack处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图(dependency graph),
- 然后将你项目中所需的每一个模块组合成一个或多个bundles,它们均为静态资源,用于展示你的内容。
静态模块:指的是编写代码过程中的,html,css,js,图片等固定内容的文件
打包:把静态模块内容,压缩(体积),整合(多个html或多个css文件整合成一个,减少浏览器请求次数),转译等(前端工程化)
- 把 less /sass 转成 css 代码
- 把 ES6+ 降级成 ES5 (兼容低版本浏览器)
- 支持多种模块标准语法
压缩:把代码文件的体积缩小
整合:把多个CSS文件、JS文件整合成一个,减少用户浏览器的http请求次数,从而让用户更快访问我们的网页
转译:less、sass转换成css,高版本js降级处理等
问题:为何不学 vite ?
- 因为:很多项目还是基于 Webpack 构建,并为 Vue React 脚手架使用做铺垫!
使用 Webpack
- 需求:封装 utils 包,校验手机号长度和验证码长度,在 src/index.is 中使用并打包观察
- 步骤:
- 新建并初始化项目,编写业务源代码
- 下载 webpack webpack-cli 到当前项目中(版本独立),并配置局部自定义命令
- 因为本机中可能有多个项目,每个项目的webpack版本需要分别独立
- 下包命令:
npm i webpack webpack-cli --save-dev
,其中 --save-dev 这两个额外参数,标记这两个包是在开发环境下使用--save-dev - 加了
-dev
,package.json中显示"devDependencies": {}
,不加则显示"dependencies": {}
- 配置局部自定义命令:在package.json中的scripts选项,配置格式
"scripts":{"key":"value"}
前面的key是自定义命令,例如:"scripts":{"build":"webpack"}
中 - 前面的
"build"
是key可以自定义命令,后面的"webpack"
是value是运行自定义命令后,真正在终触发运行的命令,是固定的value值,例如这个webpack是固件包提供的固定的打包命令
- 运行打包命令,自动产生 dist 分发文件夹(压缩和优化后,用于最终运行的代码)
- 通过
npm run build
运行打包命令 - 本质上是通过运行:固定命令
npm run
+ 要执行的局部自定义命令build
,走打包流程 - webpack会将打包完成后的内容,输出到当前项目目录下的dist文件夹,产生 main.js 出口文件
- 通过
新建项目时,步骤如下:
- 初始化项目 npm init -y,生成package.json包配置文件
- 建立目录文件夹和项目代码,如src/utils/某某.js程序代码
// 封装校验手机号长度和校验验证码长度的函数
export const checkPhone = (phone) => phone.length === 11;
export const checkCode = (code) => code.length === 6;
上面的check.js组件代码,引入到主文件使用
// 1体验打包过程
// 1.1准备项目和源代码
import { checkCode, checkPhone } from "../utils/check";
console.log(checkPhone("13812345678"));
console.log(checkCode("66666688"));
然后准备webpack打包环境,通过npm下载webpack软件包,同时下载多个软件包,使用空格隔开,--save-dev
配置到开发环境下
# 1.2准备webpack打包环境
npm i webpack webpack-cli --save-dev
包导入后的package.json文件,因为vuepress框架渲染bug,下方代码块不标示json
{
"devDependencies":
{
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4",
}
}
已完成webpack软件包/webpack打包命令的引入,需要到项目的局部环境下使用,也就是package.json所在的目录下
在package.json中的scripts选项配置只属于当前项目的自定义打包命令"build",而对应要执行的真正打包命令是固定的"webpack"
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
}
通过自定义打包命令"build",运行webpack打包命令
# 1.3运行自定义命令打包观察webpack效果(npm run + 自定义命令)
npm run build
打包成功,控制台会显示输出目录,例如上述打包完成后,输出到了dist目录下
// 经过webpack打包压缩精简浓缩后的程序代码
(()=>{"use strict";console.log(!0),console.log(6===phone.length)})();
实际上,webpack分析了代码的运作和输出结果,做了压缩,也就是写的代码,实际上运行时等价于打包好后的代码
准备项目源码 + 准备项目依赖 + 打包
修改webpack打包入口和出口
默认的入口文件:src/index.js
默认的出口文件:dist/main.js
入口起点(entry point) 指示 webpack应该使用哪个模块,来作为构建其内部依赖图(dependency graph)的开始。 进入入口起点后,webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的。 默认值是./src/index.js ,但你可以通过在 webpack configuration中配置 entry 属性,来指定一个(或多个)不同的入口起点。
输出(output) output 属性告诉 webpack在哪里输出它所创建的 bundle,以及如何命名这些文件。 主要输出文件的默认值是./dist/main.js,其他生成文件默认放置在./dist 文件夹中。
简单理解
- 打包入口:就是需要打包的项目内容,也就是webpack起作用的、扫描的根目录
- 打包出口:打包后输出的目录
实际项目中,可能根据项目要求,会有不同的入口和出口需求,参考webpack配置文档
概念 | webpack 中文文档 (docschina.org)
修改webpack打包入口案例,webpack的配置在webpack.config.js中
配置文档说明不完整,不准确,查bing或者gpt,会告诉我们:其实需要在项目根目录下新建一个webpack.config.js配置文件
配置文档介绍
const path =require('path')
module.exports ={
entry:'./path/to/my/entry/file.js'
output:{
path: path.resolve(_dirname,'dist'),
filename:'./login/index.js'
}
}
实际配置案例,配置文件的代码在node环境中执行,为了保证在任何地方执行都能找到文件,使用绝对路径path.resolve和path.join
const path =require('path')
// 导出配置对象,只能设置一个配置对象导出
module.exports ={
// 入口设置,绝对路径,使用path.resolve或者path.join,第一个参数_dirname是找到当前webpack.config.js的所在文件夹
entry: path.resolve(_dirname,'src/login/index.js'),
// 出口设置,移动index.js文件到/login目录下,vscode编译器会提示我们是否更新导入,选择是更新文件导入的关系
output:{
// 出口目录:在当前webpack.config.js的所在文件夹下的dist目录中
path: path.resolve(_dirname,'dist'),
// 输出文件名写带路径的这种格式
filename:'./login/index.js',
// 在生成打包后的文件之前,清空打包输出路径所在目录
clean:true
}
}
步骤总结:
- 项目根目录下,新建webpack.config.js配置文件
- 导出配置对象,配置入口&出口文件的路径
- 重新打包观察
- 注意:只有和入口的.js文件,产生直接/间接的引入关系的文件,才会被webpack打包
实际上,按照上述webpack.config.js入口和出口设置去打包,会报错,问GPT,得到如下解决
// 以下代码报错ReferenceError: _dirname is not defined,应该如何去修改呢
const path = require("path");
// 导出配置对象,只能设置一个配置对象导出
module.exports = {
// 入口设置,绝对路径,使用path.resolve或者path.join
entry: path.resolve(_dirname, "src/login/index.js"),
// 出口设置,移动index.js文件到/login目录下,vscode编译器会提示我们是否更新导入,选择是更新文件导入的关系
output: {
// 出口目录:在当前webpack.config.js的所在文件夹下的dist目录中
path: path.resolve(_dirname, "dist"),
filename: "./login/index.js",
// 在生成打包后的文件之前,清空打包输出路径所在目录
clean: true,
},
};
// 解决 ReferenceError: _dirname is not defined
// 在 Node.js 中,__dirname 是一个全局变量,表示当前正在执行的脚本所在的目录。在你的代码中,你尝试使用 _dirname,但它没有被定义,因为你正在使用 CommonJS 模块,而不是 ES 模块。
// 要解决此问题,你需要将你的代码转换为 ES 模块,或者使用 CommonJS 模块并使用 require 加载 path 模块。
// ====================
// 使用 ES 模块:
// 将你的代码转换为 ES 模块:
// 入口设置,绝对路径,使用path.resolve或者path.join
import path from "path";
// 导出配置对象,只能设置一个配置对象导出
export default {
entry: path.resolve(__dirname, "src/login/index.js"),
// 出口设置,移动index.js文件到/login目录下,vscode编译器会提示我们是否更新导入,选择是更新文件导入的关系
output: {
// 出口目录:在当前webpack.config.js的所在文件夹下的dist目录中
path: path.resolve(__dirname, "dist"),
filename: "./login/index.js",
// 在生成打包后的文件之前,清空打包输出路径所在目录
clean: true,
},
};
// ====================
// 使用 CommonJS 模块:
// 如果你想继续使用 CommonJS 模块,你可以使用 require 加载 path 模块:
const path = require("path");
// 入口设置,绝对路径,使用path.resolve或者path.join
const entry = path.resolve(__dirname, "src/login/index.js");
// 导出配置对象,只能设置一个配置对象导出
module.exports = {
entry: entry,
// 出口设置,移动index.js文件到/login目录下,vscode编译器会提示我们是否更新导入,选择是更新文件导入的关系
output: {
// 出口目录:在当前webpack.config.js的所在文件夹下的dist目录中
path: path.resolve(__dirname, "dist"),
filename: "./login/index.js",
// 在生成打包后的文件之前,清空打包输出路径所在目录
clean: true,
},
};
// ===================
// 请注意,在 CommonJS 模块中,你不能使用 export default。你需要将配置对象直接分配给 module.exports。
用户登录-长度判断
需求:点击登录按钮,判断手机号和验证码长度
步骤:
- 准备用户登录页面的原代码 在./public/login.html中
- 编写核心 Js 逻辑代码 在./src/login/index.js 以及引入的校验模块 ./src/utils/check.js中
- 打包项目,并手动复制登录页面的原代码 ./public/login.html 到 dist 下,手动引入打包后的 js,借助vscode live server运行
核心:通过node环境+Webpack,将打包后的代码,作用在前端网页中使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>黑马头条-数据管理平台</title>
</head>
<body>
<!-- 警告框 -->
<div class="alert info-box">
操作结果
</div>
<!-- 登录页面 -->
<div class="login-wrap">
<div class="title">黑马头条</div>
<div>
<form class="login-form">
<div class="item">
<input type="text" class="form-control" name="mobile" placeholder="请输入手机号" value="13888888888">
</div>
<div class="item">
<input type="text" class="form-control" name="code" placeholder="默认验证码246810" value="246810">
</div>
<div class="item">
<button type="button" class="btn btn-primary btn">登 录</button>
</div>
</form>
</div>
</div>
</body>
</html>
// 1体验打包
// 1.1准备项目和源代码
import { checkCode, checkPhone } from '../utils/check'
console.log(checkPhone('13812345678'))
console.log(checkCode('66666688'))
// 1.2准备webpack打包环境
// npm i webpack webpack-cli --save-dev
// 1.3运行自定义命令打包观察webpack效果(npm run 自定义命令)
// npm run build
// 结合./public/index.js编写js核心代码
document.querySelector('.btn').addEventListener('click', () => {
// 在登录页的login-form表单中,名字=mobile的元素,取值
const phone = document.querySelector('.login-form [name=mobile]').value
const code = document.querySelector('.login-form [name=code]').value
// 调用引入的封装的函数,如果不符合则进入条件
if (!checkPhone(phone)) {
console.log('手机号长度必须是11位')
return
}
// 如果手机号符合要求,再校验验证码
if (!checkCode(code)) {
console.log('验证码长度必须是6位')
return
}
// 如果两个if都没有进去,则符合要求
console.log('提交到服务器登录...')
})
沿用之前的校验模块 在./src/utils/check.js中,略
npm run build
将./public/login.html复制一份,放到./dist下,并修改为/index.html(在dist输出目录下),编辑html加上build后的script.js
注意:build后的js在出口目录配置项,dist目录下,而build的文件输出名字/目录在 filename: "./login/index.js" 也就是dist目录下的/login/index.js
因此,在html文件中,script插入js需要写上./login/index.js
<body>
<!-- 警告框 -->
<div class="alert info-box">
操作结果
</div>
<!-- 登录页面 -->
<div class="login-wrap">
<!-- 省略 -->
</div>
</div>
<script src="./login/index.js"></script>
</body>
选中./dist/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试,确认打包后js的逻辑正确
注意:目录是输出目录dist下的index.html文件
通过webpack插件,自动生成html网页,并引入打包好后的资源
通过webpack插件,自动生成html网页,并引入打包好后的资源
- 插件名称:html-webpack-plugin (实际上是作为一个本地的软件包引入)
- 作用:在 Webpack 打包时生成 html 文件
- 插件介绍:HtmlWebpackPlugin | webpack 中文文档 (docschina.org)
- 其他参数:见插件项目,有参数功能说明&例子https://github.com/jantimon/html-webpack-plugin#options
步骤:
npm i html-webpack-plugin
下载 html-webpack-plugin 软件包到本地项目中npm i html-webpack-plugin --save-dev
开发环境下,参数可以放对象前面也可以放后面- 配置 webpack.config.js 让 Webpack 拥有插件功能
- 重新打包观察效果
// new一个插件的构造函数
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports ={
// ...其他配置
plugins:[
new HtmlwebpackPlugin({
// 以./public/login.html为模板文件,再去生成dist输出目录下的文件
template:'./public/login.html',// 模板文件
// 指定生成的dist下的html文件名以及生成路径
filename:'./login/index.html',//输出文件
})
]
}
- 总结:找包 + 下包 + 根据项目实际情况配置包 + 重新打包确认
npm i html-webpack-plugin
const path = require("path");
// 引入插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 入口设置,绝对路径,使用path.resolve或者path.join
const entry = path.resolve(__dirname, "src/login/index.js");
// 插件输出设置
const exportsPluginsTem = path.resolve(__dirname, "public/login.html");
const exportsPluginsFil = path.resolve(__dirname, "dist/login/index.html");
// 导出配置对象,只能设置一个配置对象导出
module.exports = {
// 新版本webpack需要设置导出模式
mode: "development",
entry: entry,
// 出口设置,移动index.js文件到/login目录下,vscode编译器会提示我们是否更新导入,选择是更新文件导入的关系
output: {
// 出口目录:在当前webpack.config.js的所在文件夹下的dist目录中
path: path.resolve(__dirname, "dist"),
filename: "./login/index.js",
// 在生成打包后的文件之前,清空打包输出路径所在目录
clean: true,
},
plugins: [
new HtmlWebpackPlugin({
// 以./public/login.html为模板文件,再去生成dist输出目录下的文件
// template: "./public/login.html", // 模板文件
// 指定生成的dist下的html文件名以及生成路径
// filename: "./dist/login/index.html", //输出文件
// 或者可以声明路径变量
template: exportsPluginsTem,
filename: exportsPluginsFil,
}),
],
};
观察输出的./dist/login/index.html文件,可以看到,script中写的是<script defer src=".././login/index.js"></script></head>
是因为,项目输出目录在dist中,而输出的html文件在dist/login/index.html 因此,从当前目录login出发,..
linux命令返回上一级目录dist,.
代表当前一级路径,也就是./login/index.js是指再访问当前路径下的login/index.js文件
插件会将output中的filename路径做解析,从当前路径出发,找父级有没有dist这个出口文件夹名字
=========================
如果是写相对路径,会以出口目录作为起点,生成的html文件中js文件引入路径书写
- 插件源代码会根据filename的路径分析,去匹配出口路径(output.path), 这个出口目前来看是指js文件的出口,匹配到后,在该基础(../)拼接output.filename输出路径
如果filename路径中不包含出口路径?
- 直向上层级匹配检索
建议:打包输出内容(filename)都在出口文件夹下面
使用webpack加载器,打包css代码
注意:Webpack 默认只识别 js 代码
通过加载器,让webpack识别更多其他类型的代码
- 插件和加载器的目的不一样,在于:插件让webpack拥有更多功能;加载器让webpack识别更多业务类型的文件
加载器 css-loader:解析 css 代码
加载器 style-loader:把解析后的 css 代码插入到 DOM
注意:加载器从右到左依次使用,所以先是css,后style,顺序不能颠倒
css-loader | webpack 中文文档 (docschina.org)style-loader | webpack 中文文档 (docschina.org)
- 需要确认,项目是使用哪个版本的webpack,需要找对应版本的加载器的安装方式,注意版本对应
- 官网默认的加载器/组件的安装方式,都是基于最新版本的webpack的,一般默认,不指定版本的,都是最新的
安装加载器
npm i css-loader style-loader --save-dev
步骤:
- 准备 css 文件代码引入到 src/login/index.js 中(压缩转译处理等)
- 下载 css-loader和 style-loader 本地软件包
- 配置 webpack.config.js 让 Webpack 拥有该加载器功能
- 打包后观察效果
先复制一份index.css,放到src/login中,然后编辑src/login/index.js,插入同目录下的index.css文件
- 由于css属于纯输出效果,没有逻辑需要获取到,因此不引入相关{变量}from'文件'
- 注意,需要检查css里面有没有不使用的样式,注销掉,否则run build的时候报错
// 准备css代码,并引入到js中,由于css属于纯输出效果,没有逻辑需要获取到,因此不引入相关{变量}from'文件'
import './index.css'
body {
height: 100%;
/* 在黑马头条-webpack这个项目中没有用到背景,注释掉否则run build的时候报错 */
/* background: url('./assets/login-bg.png') no-repeat center/cover; */
display: flex;
align-items: center;
justify-content: center;
}
module.exports ={
// 加载器,让webpack识别更多业务类型文件内容
module: {
rules:[
{
// 匹配以.css结尾的文件,i代表忽略大小写
test:/\.css$/i,
// 从后往前
use:['style-loader','css-loader'],
}
]
}
}
npm run build
选中./dist/login/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试
- 发现基础样式是有了,但是排版样式和之前做项目引入bootstrap时做出来的样式不对,因此,可以在项目中补引入bootstrap样式
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 初始化样式 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
<!-- 引入bootstrap.css -->
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
<!-- 核心样式 -->
<link rel="stylesheet" href="./css/index.css">
<title>学习反馈</title>
</head>
通过在项目路径下,运行
npm i bootstrap
,引入bootstrap模块包到项目中,通过npm引入可以代替<link href=>
这种引入方式运行了
npm i bootstrap
引入后,别忘了到项目index.js中,到打包目录的入口文件的代码中,从代码上引入这个模块包- 注意1:需要先引入第三方的样式在前,再引入自己的样式在后
- 注意2:引入时,输入引入的软件包名字,vscode会提示你的引入来源路径让你补充,实际上,这个引入的来源路径,就是在依赖文件夹node_module文件夹当中,也可以自己找出来这个路径,前提是得先npm i bootstrap引入后才会有提示
// 引入第三方的样式在前,可以自己到node_module文件夹当中直接找到路径,前提是得先npm i bootstrap引入后才会有提示
import 'bootstrap/dist/css/bootstrap.min.css'
// 准备css代码,并引入到js中,由于css属于纯输出效果,没有逻辑需要获取到,因此不引入相关{变量}from'文件'
import './index.css'
通过webpack插件优化,独立提取并打包css代码
通过 mini-css-extract-plugin 插件,将原本嵌入在js文件中的css代码提取出来作为独立的一个文件
目标:将上一节中,打包输出到dist目录下的index.js文件中的css部分抽离出来,单独独立成一个文件打包
步骤:
- 下载 mini-css-extract-plugin 本地软件包
npm i mini-css-extract-plugin --save-dev
终端中使用鼠标右键粘贴
- 下载 mini-css-extract-plugin 本地软件包
- 配置 webpack.config.js 让 Webpack 拥有该插件功能
- 打包后观察效果
- 注意:不能和 style-loader 一起使用 Do not use style-loader and mini-css-extract-plugin together(css-loader是将css代码打包进js中插入到dom上)
好处:
- 单独的css文件可以让浏览器缓存起来,减少js文件体积,让浏览器下载更快
- 重点:浏览器可以并行下载css和js,让网页更快显示在用户眼前
- 该插件在上一节css-loader加载器文档中有关联https://github.com/webpack-contrib/css-loader#recommend
MiniCssExtractPlugin | webpack 中文文档 (docschina.org))
// 准备css代码,并引入到js中,由于css属于纯输出效果,没有逻辑需要获取到,因此不引入相关{变量}from'文件'
import './index.css'
body {
height: 100%;
/* 在黑马头条-webpack这个项目中没有用到背景,注释掉否则run build的时候报错 */
/* background: url('./assets/login-bg.png') no-repeat center/cover; */
display: flex;
align-items: center;
justify-content: center;
}
// 声明常量引入css分离打包插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports ={
// 加载器,让webpack识别更多业务类型文件内容
plugins: [
// 新建一个对象引入插件
new MiniCssExtractPlugin(),
],
module: {
rules:[
{
// 匹配以.css结尾的文件,i代表忽略大小写
test:/\.css$/i,
// 取消style-loader,改为mini-css-extract-plugin
// use: ['style-loader', 'css-loader'],
use: [MiniCssExtractPlugin.loader, 'css-loader'],
}
]
}
}
npm run build
选中./dist/login/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试,样式没问题
在/dist输出目录中,生成了main.css,打开发现,其中还打包进来了bootstrap的css代码,后半部则为我们自己制作的css代码
在/index.html中,也可以找到<script defer src=".././login/index.js"></script><link href="../main.css" rel="stylesheet"></head>
发现包括js、css都引入好了
优化css代码的打包压缩过程
上一节中,我们发现,main.css打包进来了bootstrap的css代码,后半部则为我们自己制作的css代码
但是在main.css中,只有bootstrap的css代码本就被压缩过,而我们自己手写的css代码提取后没有经过压缩
- 问题:手写部分的 css 代码在插件提取后没有压缩
- 解决:使用 css-minimizer-webpack-plugin 插件实现手写css的压缩
- 步骤:
- 下载 css-minimizer-webpack-plugin 本地软件包
npm i css-minimizer-webpack-plugin --save-dev
- 配置 webpack.config.js 让 webpack 拥有该功能
- 打包重新观察
- 注意:添加了optimization:选项后,打包的功能配置就需要用户自定义了,在 webpack@5 中,你可以使用
...
语法来扩展现有的 minimizer(即terser-webpack-plugin
),使得js也压缩
- 下载 css-minimizer-webpack-plugin 本地软件包
CssMinimizerWebpackPlugin | webpack 中文文档 (docschina.org)
// 准备css代码,并引入到js中,由于css属于纯输出效果,没有逻辑需要获取到,因此不引入相关{变量}from'文件'
import './index.css'
body {
height: 100%;
/* 在黑马头条-webpack这个项目中没有用到背景,注释掉否则run build的时候报错 */
/* background: url('./assets/login-bg.png') no-repeat center/cover; */
display: flex;
align-items: center;
justify-content: center;
}
// 声明常量引入css分离打包插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 声明常量引入css压缩打包插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports ={
// 加载器,让webpack识别更多业务类型文件内容
plugins: [
// 新建一个对象引入插件
new MiniCssExtractPlugin(),
],
module: {
rules:[
{
// 匹配以.css结尾的文件,i代表忽略大小写
test:/\.css$/i,
// 类似于less,但目前先弄css
// test: /.s?css$/,
// use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
use: [MiniCssExtractPlugin.loader, 'css-loader'],
}
]
},
// 优化
optimization: {
// 最小化,仅在生产环境开启 CSS 优化
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
`...`,
new CssMinimizerPlugin(),
],
// 生产环境&开发环境都使用
minimize: true,
},
}
npm run build
选中./dist/login/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试,样式没问题
在/dist输出目录中,生成了main.css,打开发现,我们自己制作的css代码也压缩了
通过webpack加载器打包less代码
可能会使用less来编写网站样式,因此有通过webpack来less打包需求
需要使用加载器 less-loader:
- webpack通过 less-loader 将 Less 编译为 CSS 代码
- 加载器的本质:也是一堆js代码,先由webpack将前端编写的代码,作为字符串,构建语法树,再传到加载器源码中,加载器会对webpack传入的代码/字符串,做翻译、处理
步骤:
- 在src/login下新建 index.less 代码(通过设置背景图准备less的原代码,用一个父子选择器的解构,来设置背景图)并将less代码引入到 src/login/index.js 中,同时素材图片放到src/login/assets文件夹下
- 下载 less 和 less-loader 本地软件包
npm i less less-loader --save-dev
- 配置 webpack.config.js 让 Webpack 拥有功能,在 module.exports 下的 module 下的 rules 中,引入新的配置规则
- 注意,需要沿用css,因此不需要动index.css部分,也不需要动插件中涉及css的部分
- 打包后观察效果
注意:
- less-loader 需要配合 less 软件包使用;less-loader需要借助less这个包,来实现less转换
- 加载器从右到左,下到上,依次使用,所以先是less-loader,将代码转成css,然后是css-loader,分析css代码,最后style-loader插入到dom上,顺序不能颠倒
- 如需将less转换生成的css代码,也单独抽离出来,那么就使用MiniCssExtractPlugin.loader代替style-loader
https://webpack.docschina.org/loaders/less-loader/
// 准备less代码,并引入到js中
import './index.less'
html {
body {
background: url('assets/login-bg.png') no-repeat center / cover;
}
}
// 声明常量引入css分离打包插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 声明常量引入css压缩打包插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports ={
// 加载器,让webpack识别更多业务类型文件内容
plugins: [
// 新建一个对象引入插件
new MiniCssExtractPlugin(),
],
module: {
rules:[
{
// 匹配以.css结尾的文件,i代表忽略大小写
test:/\.css$/i,
// 类似于less,但目前先弄css
// test: /.s?css$/,
// use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
// 引入新的压缩配置规则
{
test: /\.less$/i,
use: [
// compiles Less to CSS
// 代替掉'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
},
]
},
// 优化
optimization: {
// 最小化,仅在生产环境开启 CSS 优化
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
`...`,
new CssMinimizerPlugin(),
],
// 生产环境&开发环境都使用
minimize: true,
},
}
npm run build
打包好以后,在输出目录/dist下,发现多了一张图片,名称有不一样,下一节讲解
选中./dist/login/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试,样式没问题
在/dist输出目录中,生成了main.css,打开发现,我们自己制作的less代码也压缩了,代码正是这个图片 html body{background:url(ca162aa839fe88baf6e0.png) no-repeat 50%/cover}
让webpack支持更多业务类型的代码的打包能力,通过加载器实现,插件则是提供更多的功能
webpack 5 打包图片的原理
- 注意:Webpack 默认只识别 js 代码,或者是json格式的代码内容
- 资源模块:从 Webpack 5 开始,内置了资源模块(字体,图片等)打包相关的loader加载器,无需额外loader
资源模块 https://webpack.docschina.org/guides/asset-modules/
文档介绍:
- 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
- (即将图片文件打包到dist/asset目录下,原图片引用地址改为url)
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
- (即将图片/资源模块文件内容,转为base64字符串,拼接data URI格式,直接在标签上显示内容数据)
- asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
- (即将图片/资源模块文件内容,复制到js代码中,不要对图片或者字体文件操作,否则无法加载,此模式一般针对txt普通文本,因此使用场景比较少)
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
- (即判断临界值默认为 8KB)
- 当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 'javascript/auto' 来解决。
- (实际上就按照将 asset 模块的类型设置为 'javascript/auto' 来解决通用资源类型 https://webpack.docschina.org/guides/asset-modules/#general-asset-type)
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
- 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
步骤:
- 配置 webpack.config.js 让 Webpack 拥有打包图片功能
- 在module.exports下的module下的rules加载器规则对象中
- test参数作用:以哪些后缀结尾的文件时,对这些资源文件执行打包压缩
- 由于是内置功能,因此需要通过type属性,设定资源模块的打包类型,asset为自动判断打包类型
- 打包输出路径:generator属性,自定义输出文件名及目录
- generator属性下的filename:自定义输出文件路径+文件名格式,中括号是webpack提供的固定类型名称占位符
- 占位符【hash】,哈希,对代码中引入的资源模块内容,做算法计算,每一个内容对应输出得到的哈希,与文件有唯一对应关系;映射的数字字母组合的字符串,既可以表示为对需要打包处理的图片资源的统称,也可以实现在项目中避免同一文件多个名字,方便多次引用复用
- 占位符 【ext】使用当前模块原本的扩展名/后缀名占位符,例如:.png/.jpg 等字符串
- 占位符 【query】保留引入文件时代码中查询参数(只有 URL 下生效),用得比较少,只有输出单独的文件,引网址下才有效
- 打包后观察效果和区别 注意:判断临界值默认为 8KB
- 大于 8KB 文件:发送一个单独的文件并导出 URL 地址
- 注:URL 即统一资源定位符
- (可以在调试中看css样式看到,或者index.css中搜输出目录的图片文件名)
- 小于 8KB 文件:导出一个 data URI (base64字符串)
- (可以在调试/输出的dist目录下的html文件中,在图片img标签中的src属性中看到)
- 格式:data:文件类型image/png+编码类型base64+,逗号往后是图片数据转换成的base64图片数据字符串
- 这样的图片数据网址,叫做:data URI ,即数据统一资源标识符
通过webpack的 asset 模块处理打包输出的目的:为了减少http请求次数
- 通过将小文件打包进去js中,让浏览器减少一次http请求,让网页加载更快
- 大于8KB的文件不再打包到js中,让需要获取其资源时单独发一次请求,原因是因为,当使用asset将资源模块转base64字符串时,可能会使文件的真实体积大23-30%,因此大文件的转换是丧失性能优化优势的
例:准备一张大图,一张小图
- 大图就是登录页背景案例,在less中引入,略
- 小图做到登录框左下角了,小于8k
- 不能在html中直接给img的src属性赋值路径,webpack会当成普通字符串,原封不动打包
- 注意:index.js 中引入本地图片资源要用 import 方式(如果是网络图片http地址,字符串可以直接写)
- 打包后,大图发送一个文件,小图导出data URI(base64字符串)
//注意:js中引入本地图片资源要用 import 方式(如果是网络图片http地址,字符串可以直按写)
import imgObj from './assets/logo-4kb.png'
// 创建图片标签
const theImg = document.createElement('img')
// 给标签src属性赋予地址
theImg.src = imgObj
// 获取需要加入图片的html标签,并通过appendChild方式加img标签到此标签尾部
document.querySelector('.login-wrap').appendChild(theImg)
不要忘了到webpack.config.js加配置
// 声明常量引入css分离打包插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 声明常量引入css压缩打包插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports ={
// 加载器,让webpack识别更多业务类型文件内容
plugins: [
// 新建一个对象引入插件
new MiniCssExtractPlugin(),
],
module: {
rules:[
{
// 匹配以.css结尾的文件,i代表忽略大小写
test:/\.css$/i,
// 类似于less,但目前先弄css
// test: /.s?css$/,
// use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
// 引入新的压缩配置规则
{
test: /\.less$/i,
use: [
// compiles Less to CSS
// 代替掉'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
},
// 图片打包的规则对象
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset',
generator: {
filename: 'assets/[hash][ext][query]'
}
},
]
},
// 优化
optimization: {
// 最小化,仅在生产环境开启 CSS 优化
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
`...`,
new CssMinimizerPlugin(),
],
// 生产环境&开发环境都使用
minimize: true,
},
}
打包观察
npm run build
打包好,发现,大图已输出到dist/assets目录下,小图以base64字符串的形式打包进了.js文件中
可以将base64字符串复制到这里验证,解码出来是图片 https://www.lddgo.net/convert/base64-to-image
选中./dist/login/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试,样式没问题
用户登录案例-完成功能
- 需求:补充完成登录功能的核心流程,以及 Alert 警告框使用
- 步骤:
- 使用 npm 下载 axios(体验 npm 作用在前端项目中)
- 准备并修改 utils 工具包源代码导出实现函数
- 导入并编写逻辑代码,打包后运行观察效果
前面几节已将webpack的基础设施搭建完毕,本节结合ajax(与后端通讯)、以及导入自己的警告框(前端样式组件)等等其他组件,开始补充完整功能
需要整合src文件夹(即项目功能文件/文件夹),将ajax(即第三方库)、以及导入自己的警告框等等这些模块,引入到项目功能文件/文件夹
第三方库的引入:到目录路径下
npm i XXX
,- 通过在项目中下包,装包/引入包,会与入口文件index.js产生直接/间接引入关系
- 最终都会被webpack打包处理(webpack配置文件会自动配置引入模块变化)
在模块化开发中,模块导入及导出的处理,通过举例axios等模块的导入、改造、导出说明
- 通过npm i axios引入的第三方模块
- 不再使用script标签来做引入,改为使用js标准语法中的模块化开发引入语法import
- 对引入的第三方模块进行改造使用(举例,通过写自己的request.js,引入第三方的axios模块,加上基地址)
- 对自己写的可复用的工具包原代码,以模块化形式输出,(举例,打包自己写的request.js、校验函数check.js等,警告框alert.js等模块)
- 到需要使用的地方引入使用(在需要输入模块的index.js中,以实现函数方式引入),然后再编写核心业务逻辑
- 通过webpack打包,输出到dist目录,源码经过html-webpack-plugin处理,会自动生成html文件,并将打包后的资源引入到html中,此时就可以部署/浏览器运行
通过npm i axios引入的第三方模块
npm i axios
- 注意点:
- 包下载完(实际上在依赖文件夹中),开始写“设置基地址”业务
- 注意应先分析要写的业务,属于专用化/复用化模块,因此不要在逻辑页面index.js中写
- 将公共的请求文件,抽离到src项目功能文件夹下的模块文件夹utils中写(如本项目中的alert、request、auth、editor等)
- 在专用化/复用化业务模块中,通过import语法引入依赖(前面已学习,模块化开发中,每一个js文件的作用域都是独立的)
// 通过import导入语法,为本模块文件,通过实现函数方式,引入依赖/库/功能
// 到文件头加入
// 引入axios,axios包默认支持默认导出,定义一个axios变量名(天蓝色)接收,form node_module文件夹里的axios,已经下载的依赖包,写的时候vscode会有提示
import axios from 'axios'
// 接收的函数同时是一个对象,后续是对对象的设置,比如设置基地址
// =============================
// axios 公共配置
// 基地址
axios.defaults.baseURL = 'http://geek.itheima.net'
// 请求拦截器,请求头
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
token && (config.headers.Authorization = `Bearer ${token}`)
return config
}),
function (error) {
return Promise.reject(error)
}
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
const result = response.data
return result
},
function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
console.dir(error)
if (error?.response?.status == 401) {
alert('身份验证失败,请重新登录')
localStorage.clear()
location.href = '../login/index.html'
}
return Promise.reject(error)
}
)
- 注意点:
在上述request.js模块中,通过检查业务代码,确认是使用的window.alert弹框,并没有引用其他模块的函数
如果使用了其他模块的函数,同样通过
import xxx from 'xxx'
引入,在本模块通过调用函数实现引用即可完成axios的导入与改造,后续需要到逻辑页面中使用,那么就需要封装,供外部使用
- (可以将axios看作一个变量,引入进来配置,再以默认形式导出)
- (因为我们用到的地方都是改造后的axios,因此就干脆以默认名称导出,那么别的地方如果引入axios,那就是引入改造后的axios了)
// 通过import导入语法,为本模块文件,通过实现函数方式,引入依赖/库/功能
// 到文件头加入
// 引入axios,axios包默认支持默认导出,定义一个axios变量名(天蓝色)接收,form node_module文件夹里的axios,已经下载的依赖包,写的时候vscode会有提示
import axios from 'axios'
// 接收的函数同时是一个对象,后续是对对象的设置,比如设置基地址
// =============================
// axios 公共配置
// 基地址
axios.defaults.baseURL = 'http://geek.itheima.net'
// 请求拦截器,请求头
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
token && (config.headers.Authorization = `Bearer ${token}`)
return config
}),
function (error) {
return Promise.reject(error)
}
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
const result = response.data
return result
},
function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
console.dir(error)
if (error?.response?.status == 401) {
alert('身份验证失败,请重新登录')
localStorage.clear()
location.href = '../login/index.html'
}
return Promise.reject(error)
}
)
// ==================末尾=====================
// 默认形式向外导出函数,实际上export default 可以输出一个变量,也能输出一个函数
export default axios
- 注意点:
- 同理,到alert.js提示框业务函数中,输出业务代码,供外部使用
- 通过命名导出方式,导出函数(备注,导入时,vscode提示import statement 则为命名导入)
// 弹窗插件
// 需要先准备 alert 样式相关的 DOM
/**
* BS 的 Alert 警告框函数,2秒后自动消失
* @param {*} isSuccess 成功 true,失败 false
* @param {*} msg 提示消息
*/
// 命名导出
export function myAlert(isSuccess, msg) {
const myAlert = document.querySelector(".alert");
myAlert.classList.add(isSuccess ? "alert-success" : "alert-danger");
myAlert.innerHTML = msg;
myAlert.classList.add("show");
setTimeout(() => {
myAlert.classList.remove(isSuccess ? "alert-success" : "alert-danger");
myAlert.innerHTML = "";
myAlert.classList.remove("show");
}, 2000);
}
- 注意点:
- 两个功能封装完毕,回到index.js逻辑代码中,导入改造后的axios、alert提示框函数,配合bootstrap样式,将请求后的响应结果输出显示
// 导入自己封装的axios和alert;或者叫引入方法
// 默认引入,随便定义一个变量,默认导入名字自定义,路径是回到上一级,然后是业务文件夹里面的request.js
import myAxios from '../utils/request.js'
// 命名导入,需要在大括号中写入同名,以作引入
import { myAlert } from '../utils/alert.js'
// 注意,引入后,需要在本业务中使用,否则vscode显示灰色,如果是设置了格式化保存,案例保存ctrl+s,则会自动删掉没有使用到的引入
// 将此前监听的打印log业务,加上函数调用,通过调用函数使用引入模块
// 结合./public/index.js编写js核心代码
document.querySelector('.btn').addEventListener('click', () => {
// 在登录页的login-form表单中,名字=mobile的元素,取值
const phone = document.querySelector('.login-form [name=mobile]').value
const code = document.querySelector('.login-form [name=code]').value
// 调用引入的封装的函数,如果不符合则进入条件
if (!checkPhone(phone)) {
myAlert(false, '手机号长度必须是11位')
console.log('手机号长度必须是11位')
return
}
// 如果手机号符合要求,再校验验证码
if (!checkCode(code)) {
myAlert(false, '验证码长度必须是6位')
console.log('验证码长度必须是6位')
return
}
// 如果两个if都没有进去,则符合要求
console.log('提交到服务器登录...')
myAxios({
// 接口文档
url: '/v1_0/authorizations',
method: 'POST',
data: {
mobile: phone,
code: code,
},
})
.then((result) => {
myAlert(true, '登陆成功')
})
.catch((result) => {
myAlert(false, error.response.data.message)
})
})
完成业务编写,打包项目
npm run build
发现,打包输出后的index.js,整合了3个业务+1主逻辑.js的代码,实际上axios的源码也在其中
选中./dist/login/index.html,右键,open with live server(alt+l alt+o),通过浏览器调试,样式没问题
搭建开发环境
- 问题:之前改代码,需重新打包才能运行查看,效率很低
- 开发环境:配置 webpack-dev-server 快速开发应用程序
- 作用:启动 Web 服务,自动检测代码变化,热更新到网页
- 注意:dist 目录和打包内容是在内存里(更新快)
开发环境 | webpack 中文文档 (docschina.org)
实际应用时,注意区分生产环境和开发环境的配置
步骤:
- 下载 webpack-dev-server 软件包到当前项目
npm install --save-dev webpack-dev-server
- 设置模式为开发模式,并配置自定义命令,在 package.json 中配置
"scripts": {"dev": "webpack serve --open"}
, - 使用 npm run dev 来启动开发服务器,试试热更新效果
- 下载 webpack-dev-server 软件包到当前项目
配置开发环境
const path =require('path')
// 对象中是无序的,因此都是同级,放在module.exports下一级就可以
module.exports ={
// 设置打包模式
// 开发环境
mode:'development',
// 生产环境
// mode:'production',
plugins: [
new HtmlWebpackPlugin({
// 开发环境页签头
title:'development',
}),
],
// 非必须,但默认的dev运行路径在项目文件夹下的public
// devServer: {
// static: './dist',
// },
}
"scripts": {
"dev": "webpack serve --open"
},
以开发模式打包和运行
npm run dev
- 注意:
- 发现,浏览器运行的页面,是由刚刚下的webpack-dev-server包,借助nodejs中的http模块,运行的8080下的web服务
- 此web服务,会默认以项目文件夹下的public目录,作为提供给浏览器的静态资源目录/服务器根目录
- 服务默认是运行文件夹中的index.html,没有的话,则会像nginx代理文件服务器一样显示
- 由于根目录是pubilc,因此,访问页面是:http://localhost:8080/login.html,但此页面只有html,样式、js功能都没有
- webpack-dev-server会根据webpack.config.js配置,打包多一份相关代码在内存中,作为服务器的根目录
- 因此服务器的根目录有两个,一个是静态资源目录pubilc,另一个在webpack.config.js中配置的出口,也就是dist打包的资源也在服务器的根目录中,因此也能访问http://localhost:8080/login/index.html
- 注意dist打包的资源也在服务器的根目录中,但此dist是在内存中,以output.path的值作为服务器的根目录
- 可以到pubilc目录中,新建index.html做重定向跳转
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//webpack-dev-server 的 Web 服务,自动跳转到我们规定的首页
location.href ='/login/index.html'
</script>
</body>
打包模式
打包模式:告知 Webpack 使用相应模式的内置优化方案,针对打包作输出优化
分类:
模式名称 | 模式名字 | 特点 | 场景 |
---|---|---|---|
开发模式 | development | 调试代码,实时加载,模块热替换等 | 本地开发 |
生产模式 | production | 压缩代码,资源优化,更轻量等 | 打包上线 |
- 设置:
方式1:在 webpack.config.js 配置文件设置 mode 选项
const path =require('path')
module.exports ={
// 设置打包模式
// 开发环境
// mode:'development',
// 生产环境
mode:'production',
}
方式2:在 package.json 命令行设置 mode 参数
通过在package.json 包管理配置文件中,赋予自定义命令,预设置不同 mode 选项的配置,自定义命令运行时,按不同的mode属性运行命令行参数
"scripts": {
"build": "webpack --mode=production",
"dev": "webpack serve --mode=development"
<!-- 有多个参数时,可以一起写,如下 -->
<!-- "dev": "webpack serve --open --mode=development" -->
},
运行不同的自定义命令配置
npm run dev
注意:
- 命令行设置的优先级高于配置文件中的,推荐用命令行设置
- 不推荐配置项的方式(写完代码,还得记得修改值,不如记住命令)
验证:
- 两种模式都使用一下,然后强制在package.json包配置中,将--mode=development赋予给build,让run build的时候,按照开发的方式输出到dist,以观察原本按照dev开发方式构建到内存的文件的形态
对比:
- run build --mode=production 输出到dist目录下的代码是极致压缩,而run build --mode=development输出到dist目录下的代码仅仅是拼凑到了在一起,方便debug;而实际上,dev模式下,有修改的话,是按照模块变动,相应更新
好处:
- 分成一段段的好处,某个文件的代码发生变化,直接替换对应名字对应的代码,让我们更快速看到开发项目的运行效果(模块热替换更快速)
补充:
- 将打包后的main.css,放到login目录下,与html、js一同存放,可以到生成css代码的插件中,配置输出路径,注意,此配置只能写相对路径
- 相对路径是相对于出口目录
module.exports = {
output: {
// 出口目录:在当前webpack.config.js的所在文件夹下的dist目录中,相对路径是相对于此目录
path: path.resolve(__dirname, 'dist'),
},
plugins: [
// 生成css文件的插件
// 新建一个对象引入插件
new MiniCssExtractPlugin({
filename: './login/index.css',
}),
],
}
打包模式在项目中的应用场景
需求:
- 在开发模式下用 style-loader 配置打包,将css代码内嵌在js文件中,以便在开发过程中,如果样式发生修改,能更快地替换到我们网页上显示
- 在生产模式下提取 css 代码成单独的文件,让用户在浏览器上并行下载css和js代码
重点:需要判断当前运行命令时所在的环境
方案1:webpack.config.js 配置导出函数,但是局限性大(只接受 2种模式)
如果要根据 webpack.config.js 中的 mode 变量更改打包行为,则必须将配置导出为函数,而不是导出对象:
// 通过var或者const声明,将配置都放到这个函数的大花括号包裹下
const config = {
entry: './app.js',
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
方案2(推荐方案):借助 cross-env (跨平台通用、全局软件包)包命令,设置参数区分环境
步骤:
- 下载 cross-env 软件包到当前项目
npm i cross-env --save-dev
- 配置自定义命令,传入参数名和值(会绑定到 process.env 对象下)
- 在 webpack.config.js 区分不同环境使用不同配置(在配置对象中加条件表达式)
- 重新打包观察两种配置区别
- 下载 cross-env 软件包到当前项目
"scripts": {
"build": "cross-env NODE_ENV=production webpack --mode=production",
"dev": "cross-env NODE_ENV=development webpack serve --open --mode=development"
},
备注:
- 实际上是执行自定义命令时,通过cross-env软件包,给nodejs环境的process.env环境变量对象下,添加对应的参数名和值
- NODE_ENV作为名字要一样,值要不一样以作区分,值名production和development实际上可以自定义,为了语义明确而设置与模式名称一致,但实际上与包配置选项名是没有关系的,互不影响
var config = {
entry: './app.js',
//...
};
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
// 针对loader,作环境变量设置区分,使用条件表达式
use: [process.env.NODE_ENV === 'development' ? 'style-loader': MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.less$/i,
use: [
process.env.NODE_ENV === 'development'
? 'style-loader'
: MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
},
]
}
};
验证:
- 两种模式都使用一下,然后强制在package.json包配置中,将--mode=development赋予给build,让run build的时候,按照开发的方式输出到dist,以观察原本按照dev开发方式构建到内存的文件的形态
对比:
- run build --mode=production 输出到dist目录下的css代码是独立的,而run build --mode=development输出到dist目录下的css代码,没有独立文件,已内嵌到js
方案3:配置不同的 webpack.config.js 文件(适用多种模式差异性较大情况,直接就写不同的文件来配置了)
生产环境 | webpack 中文文档 (docschina.org)
在webpack中向前端注入环境变量
- 背景:上一节已了解环境变量的作用:根据不同的环境执行不同配置
- 需求:前端项目中,开发模式下打印语句生效,生产模式下打印语句失效(在生产模式下通过将console.log赋予空函数实现)
- 问题:cross-env 设置的只在 Node.js 环境生效,前端代码无法访问 process.env.NODE_ENV
- 解决:使用 Webpack 内置的 DefinePlugin 插件(内置的无需额外下载)
- 作用:在编译时,将前端代码中匹配的变量名,替换为值或表达式
webpack 中文文档 (docschina.org) DefinePlugin 插件
DefinePlugin 允许在 编译时 将你代码中的变量替换为其他值或表达式。这在需要根据开发模式与生产模式进行不同的操作时,非常有用。
cross-env 设置的只在 Node.js 环境生效,前端代码无法访问 process.env.NODE_ENV,但打包后使用该插件打包会留下相应的字符串值
在webpack.config.js 打包配置中配置,当遇到 corss-env 注入在 node.js 中的坏境变量字符串时,匹配到对应的key,并替换成value (该插件会在webpack打包我们写的前端源代码时,把将要打包的代码视作字符串,遇到匹配到对应的key,会替换成value)
// 引入内置插件
const webpack = require('webpack')
module.exports = {
module: {
plugins: [
new webpack.DefinePlugin({
// key 是注入到打包后的前端 JS 代码中作为全局变量
// value 是变量对应的值(在 corss-env 注入在 node.js 中的坏境变量字符串)
// 前端的代码:'process.env.NODE_ENV'会在打包时被webpack替换成JSON.stringify(process.env.NODE_ENV)
'process.env.NODE_ENV':JSON.stringify(process.env.NODE_ENV)
})
],
}
};
(也就是按照上述,发现前端写的代码中有'process.env.NODE_ENV'字符串,就会转为JSON.stringify(process.env.NODE_ENV))
执行时,变量会留在原地,也就是将webpack.DefinePlugin配置为了'production'或者'development'
value位置不能直接使用表达式(字符串),插件内部源码发现value为字符串,会将其视作变量(将'production'或者'development'当成了变量替换到原代码匹配的位置),因此通过JSON.stringify转为包裹的json字符串
// 将此配置放到主业务逻辑中的顶部,以影响后续所有逻辑
// if表达式需要与webpack.config.js 打包配置中设置的key一致,在运行时,左侧表达式原地运行,被webpack替换的字符串,如果是'production',则执行生产环境
if (process.env.NODE_ENV === 'production') {
// 给console的log属性,重新、覆盖性,赋予空函数
console.log = function () {};
}
// 调用
// console.log();
console.log('开发模式下好用,生产模式下失效');
// 等价于
process.env.NODE_ENV === 'production'
? (console.log = function () {})
: console.log
上述,if表达式需要与webpack.config.js 打包配置中设置的key一致,在运行时,左侧表达式原地运行,被webpack替换的字符串,如果是'production',则执行生产环境
验证:
- 开发、生产两种打包模式都使用一下,发现按照生产方式输出到dist的文件,运行时不再打印log
其他应用场景:在公司内部开发中,后端的配置和生产的不一样;如request业务中的接口地址
// 或
process.env.NODE_ENV === 'production'
? (axios.defaults.baseURL = 'http://geek.itheima.net')
: (axios.defaults.baseURL = 'https://example.com')
// 或
axios.defaults.baseURL = process.env.NODE_ENV === 'production' ? 'http://geek.itheima.net' : 'https://example.com'
// 或
if (process.env.NODE_ENV === 'production') {
axios.defaults.baseURL = 'http://geek.itheima.net'
} else {
axios.defaults.baseURL = 'https://example.com'
}
开发环境调错source map资源地图
问题:代码被压缩和混淆,无法正确定位源代码位置(行数和列数)
webpack内置插件source map:可以准确追踪 error 和 warning 在原始代码的位置
设置:webpack.config.js 配置 devtool选项
作用:inline-source-map 选项,把源码的位置信息一起打包在js 文件内
注意:source map 仅适用于开发环境,不要在生产环境使用(防止被轻易查看源码位置)
开发环境 | webpack 中文文档 (docschina.org)
Devtool | webpack 中文文档 (docschina.org)
为了让生产环境下,inline-source-map 选项不生效,因此将对象保存在变量中,根据process.env.NODE_ENV环境变量,动态加key value
// 由于key位置用不了条件表达式,将配置对象赋予给一个常量
const config = {.....};
// 判断再导出
// 如在开发环境下
if (process.env.NODE_ENV === 'development') {
// 给配置对象加一个devtool选项
config.devtool = 'inline-source-map'
}
module.exports = config
验证:开发、生产两种打包模式都使用一下,发现按照生产方式输出到dist的文件,运行时的报错,不再显示准确的报错源码地址
解析别名 alias
- 解析别名:配置模块如何解析,创建 import 引入路径的别名,来确保模块引入变得更简单
例如:原来路径如图,比较长而且相对路径不安全
import { checkCode, checkPhone } from '../utils/check'
- 解决:
- 在 webpack.config.js 中配置解析别名 @ 来代表 src 绝对路径,是webpack的一种配置
// 将配置对象赋予给一个常量
const config = {
// resolve选项,意为解析
resolve:{
// alias选项,意为别名
alias:{
// key可以自定义路径上使用的字符串,webpack解析import引入路径时,会将此字符串转为配置的value值,如配置的绝对地址
'@':path.resolve(__dirname, 'src')
// 注:学vue的路径也是有@
// __dirname动态获取当前文件所在文件夹的绝对路径
}
}
};
module.exports = config
配置后,引入路径改为
import { checkCode, checkPhone } from '@/utils/check'
同时,可以将src/login/index.js里面的所有引入,都改为@+路径
优化-CDN使用
CDN定义:内容分发网络,指的是一组分布在世界各个地区的服务器集合
作用:把静态资源文件/第三方库放在各个 CDN 分发网络服务器中,供不同国家不同地点的用户,就近请求资源文件或第三方库获取
好处:用户会根据最近的一台CDN服务器来获取对应库的源代码,或者是我们部署在服务器上的前端静态资源文件,减轻自己后台服务器的请求压力,就近请求物理延迟低,还能使用CDN配套缓存策略
需求:开发模式使用本地第三方库,生产模式下使用 CDN 加载引入第三方库
- 开发模式:本地开发项目过程中,在本地电脑上,在项目目录下,通过
npm i xxxx
来引入第三方库源代码,然后引入request.js业务,再index.js主逻辑服务,本地开发本地引入快,打包时,第三方库和业务代码和主逻辑代码,一同到输出目录dist分发文件夹中 - 打包上线:打包上线生产环境,线上前端项目需要使用CDN来给用户加载第三方库,通过修改配置文件,让webpack禁止打包本地的第三方库,改为在HTML文件中,打包CDN的地址作为引入,因而生产环境下打包后,不再有第三方库的源代码,项目体积缩小,实际用户运行项目时,是向CDN请求第三方库
- 开发模式:本地开发项目过程中,在本地电脑上,在项目目录下,通过
在前端项目中配置使用CDN步骤:
- 在 public/html 这个模板html文件中,准备好要引入第三方库的 CDN 地址,并用模板语法判断;无论在开发还是生产,html是一致的,但可以在标签中配置决定是否使用本地的第三方库还是CDN
- 配置 webpack.config.js 中 externals 外部扩展选项(防止某些import 的包被打包)
- 两种模式下打包观察效果
模板引擎语法(html-webpack-plugin插件要求使用的语法),在
<% `` %>
标签对中,可以插入js语法,通过在html中插入js语法,判断是否使用CDN,实际上,实际上是if(){}
判断到package.json中,先确认本项目,一共引入了哪些第三方库
{
"dependencies": {
"axios": "^1.7.7",
"bootstrap": "^5.3.3"
}
}
需要先到public/html中,以CDN形式,引入bootstrap的css和js (因为后续webpack打包替换代码时,会将index.js中的import css语句,原地留下一个bootstrap变量,防止变量在浏览器运行时报错,因此预先引入;同理,还有axios)
这些CDN地址是来源于BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务,实际在生产开发中,为了生产稳定性,应公司购买有偿CDN服务
<head>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.2/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/js/bootstrap.min.js"></script>
</body>
- 为了打包时只打包一种,区分环境,因此通过插件+语法形式实现,此前已安装html-webpack-plugin插件,因此继续在HTML中通过模板引擎语法配置
<!-- 在<% %>标签对中,可以插入js语法,实际上是 if(){}判断 -->
<head>
<% if(htmlWebpackPlugin.options.useCdn) { %>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
<% } %>
</head>
<body>
<% if(htmlWebpackPlugin.options.useCdn) { %>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.2/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/js/bootstrap.min.js"></script>
<% } %>
</body>
- 然后到主逻辑业务,以及各业务逻辑代码下,找出引入的地方,确认引入的库的名字
// 找到
import axios from 'axios'
// 确认第三方的库名字为'axios'
// 找到
import 'bootstrap/dist/css/bootstrap.min.css'
// 确认第三方的库名字+路径为'bootstrap/dist/css/bootstrap.min.css'
- 引入的库的名字确认好后,在webpack.config.js 中,给module.exports配置 externals 外部扩展选项,以匹配业务逻辑中遇到的引入库
- 匹配后,原地值留一个变量作为占位即可
// 生产模式下使用相关配置
if(process.env.NODE_ENV === 'production'){
// 外部扩展,防止import引入的本地第三方库被打包进项目,让生产上线时改为使用CDN
config.externals ={
// key:代码中 import from 后面的模块标识字符串,即引入标的(包含路径)
// value:替换在原地的全局变量名(要和 cdn 暴露在全局的变量名一致,可以不用写后缀)
'axios': 'axios',
'bootstrap/dist/css/bootstrap.min.css':'bootstrap'
}
}
module.exports = config
在webpack.config.js 中,找到对应本地引入HTML模板的第三方插件HtmlWebpackPlugin,配置自定义属性,在打包时如果参数是生产环境,则使用CDN
const config ={
plugins:[
new Htmlwebpackplugin({
// ...等等其他配置
//自定义属性,生产模式下使用cdn引入的地址,在 html 模板中 <%=htmlWebpackPlugin.options.useCdn%> 访问使用
useCdn: process.env.NODE_ENV === 'production'
// 对应htmlWebpackPlugin.options.useCdn,中间有一层{}因此是option
})
]
}
module.exports = config
验证:
- 开发、生产两种打包模式都使用一下,发现按照开发方式输出到dist的文件,打包了本地的第三方库,运行时是的引入是在本地路径,如import 'bootstrap/dist/css/bootstrap.min.css',而生产模式下,则使用的CDN的库作为引入
补充:CDN引入的axios.min.js和bootstrap.min.js
</script>
部分在html模板的最下面,但是使用该逻辑代码的语句<script defer="defer" src=".././login/index.js"></script>
在打包后的index.js文件中靠前的位置,为什么还没到加载的语句,却能执行使用的语句呢?是因为HtmlWebpackPlugin插件,在
</script>
中添加了最关键的属性<script defer="defer" ></script>
,defer属性影响了浏览器在解析标签时的逻辑顺序:- 浏览器在解析时,遇到defer属性的标签,会以后台的形式先下载,并不会阻止浏览器继续往下解析页面代码,等到整个HTML页面包括第三方插件代码都加载完毕后,再运行带defer属性的标签代码
多页面打包
多页面打包
- 单页面应用:业务逻辑在单个html文件中,通过js代码切换 DOM 的方式实现不同业务逻辑展示,后续 Vue/React 会学到
- 好处:用户不需要重新跳转加载html页面
- 多页面应用:多个 html文件,切换页面实现不同业务逻辑展示
- 单页面应用:业务逻辑在单个html文件中,通过js代码切换 DOM 的方式实现不同业务逻辑展示,后续 Vue/React 会学到
需求:把黑马头条-数据管理平台-内容页面一起引入打包使用
步骤:
- 准备源码(html,css,js)放入相应位置引入项目,并将封装的代码,改用模块化语法导出
- 下载 form-serialize 包并导入到核心代码中使用
- 配置 webpack.config.js 多入口和多页面的设置
- 重新打包观察效果
先放webpack.config.js 多入口和多页面的配置参考
const config ={
entry:{
'模块名1':path.resolve(__dirname,'src/入口1.js'),
'模块名2':path.resolve(__dirname,'src/入口2.js'),
},
output:{
// 输出路径到dist
path: path.resolve(dirname,'dist'),
// [name]是一个占位符,webpack运行时,会使用对应的模块名,相应替换
filename:'./[name]/index.js'
},
plugins:[
new Htmlwebpackplugin({
template:'public/页面1.html', // 模板文件
filename:'路径/index.html', // 输出文件
chunks:['模块名1']
})
new HtmlwebpackPlugin({
template:'public/页面2.html', //模板文件
filename:'路径/index.html', // 输出文件
chunks:['模块名2']
})
]
}
module.exports = config
- 首先,准备多页面源码,放入相应位置引入项目,并将封装的代码,改用模块化语法导出
- 将原来的content页面下的index.html,改名content.html,放到pubilc文件夹下,作为模板
- 检查content.html,看看引入了哪些第三方的库
- 发现,css样式部分,原来在本地开发content页面的时候,就有从cdn引入第三方库,这个可以原封不动,不改,直接放进模板(其实也可以改为引入CDN的)
- 还有
<link rel="stylesheet" href="./index.css">
这个我们本地做的CSS,记录下引入关系后,从模板中删掉/注释掉,交由后续webpack打包时,再从入口路径引入 - js部分,也是从cdn引入了第三方库,为了精简代码,可以改为从cdn引入
- js部分的所有从本地相对路径动态引入的库,都记录下引入关系,然后删掉/注释掉,后续交由webpack打包再从入口路径引入
- 分析1:
<script src="../../lib/form-serialize.js"></script>
,表单插件form-serialize原来是使用的本地的引入路径 - 分析1结果1:在模板中加入模板引擎标签对,通过以下cdn引入
<script src="https://unpkg.com/form-serialize@0.7.2/index.js"></script>
- 分析1结果2(未采用):通过
npm i form-serialize
将包下载到本地,在src/content/index.js中引入(需要在引入插件使用的js中import from) - 分析2:request.js、auth.js、alert.js纯自己开发的业务逻辑,需要封装+引用+打包,将还没放到utils业务文件夹的js,统统放入utils
- 分析3:index.js,交给webpack来打包引入,不需要手动引入
- 分析1:
- 在src文件夹下,与login页面的login文件夹对应一致的操作,新建content文件夹,将原来的content页面下的index.css样式和index.js业务逻辑代码,都放进去
- 分析业务代码的引入逻辑关系
- 进入src/content文件夹,分析业务代码src/content/index.js
- 分析1:使用了axios,准确分析,发现是使用了添加了公共基地址后,也就是使用的是改造后的axios,因此在src/content/index.html中所引入的axios,是来自于request.js
- 分析1结果:在src/content/index.js中,给axios变量引入来源:
import axios from "@/utils/request.js"
- 分析2:继续检查,看看有没有使用了需要引入其他模块依赖,才能使用的函数,可以通过运行时的报错来确认,缺什么再引入
- 进入src/utils业务文件夹,分析新放进来的业务代码auth.js
- 分析3:同样需要引入
import axios from '@/utils/request.js'
注意引入来源
- 分析3:同样需要引入
- 进入public页面文件夹,分析public/content.html页面
- 分析4:public/content.html,结合分析src/content/index.js,发现:需要业务代码auth.js参与运行(权限插件引入到了除登录页面以外的其他所有页面),即各个页面都有一同使用(例如控制发现权限不对时强制跳回去主页),但auth.js并没有通过export导出的形式使用
- 分析4结果:需要在src/content/index.js中,让auth.js的代码一同打包进来;没有导出和导入,只是为了让目标js业务代码,与页面业务代码一起打包进来参与html解析,因此需要在index.js中,通过直接写引入目标的路径
import '@/utils/auth.js'
形式引入打包,注意路径不要写错@/路径/文件
- 分析5:/content.html页面的样式,也需要引入同路径下的css文件,因此,在src/content/index.js中,也需要通过
import './index.css'
形式,引入同路径下的css样式文件
- 完成源码部分的引入关系梳理和配置,转到webpack的配置文件webpack.config.js中,配置第二个页面入口文件
const config ={
// 先注释掉原有的入口设置的绝对路径
// 使用path.resolve或者path.join
// const entry = path.resolve(__dirname, 'src/login/index.js')
// 然后将entry,改为对象形式,新设置多页面入口路径
// 以对象形式新设置多页面入口路径
entry: {
'login':path.resolve(__dirname,'src/login/index.js'),
'content':path.resolve(__dirname,'src/content/index.js'),
},
// webpack打包的js输出设置
// 注意先注释掉前面单页面打包的输出设置
output:{
// 输出路径到dist
path: path.resolve(__dirname,'dist'),
// [name]是一个占位符,webpack运行时,会使用对应的模块名,相应替换
filename:'./[name]/index.js'
},
plugins:[
// html部分的输出设置
new Htmlwebpackplugin({
// 相对于入口路径的目录,注意这一层的目录,不需要在前面加./
template: path.resolve(__dirname, 'public/login.html'), // 模板文件
filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
chunks:['login']
}),
new HtmlwebpackPlugin({
template: path.resolve(__dirname, 'public/content.html'), //模板文件
filename: path.resolve(__dirname, 'dist/content/index.html'), // 输出文件
chunks:['content']
}),
// 注意,将css的输出路径,改为占位符形式
new MiniCssExtractPlugin({
filename: './[name]/index.css',
}),
]
}
module.exports = config
完成webpack的配置,npm run build
打包验证
- 验证:
npm run build
后,找到dist/login/index,右键,open with live server(alt+l alt+o),通过浏览器调试,- 调试打开的页面路径为
http://127.0.0.1:5500/dist/login/index.html
(注意区分npm run dev是打开的http://127.0.0.1:5500/login/index.html
对应webpack-dev-server的输出目录pubilc,我们在里面人为放入了重定向的index.html,location.href = '/login/index.html'
,当打开http://127.0.0.1:5500/index.html
时重定向到即http://127.0.0.1:5500//login/index.html
) - 发现当页签还处于缓冲圈加载中的时候(CDN的bootstrap还没加载),样式的弹窗不生效,等加载完成,样式弹窗就生效了
- 点击登录,页面跳转路径为
http://127.0.0.1:5500/dist/content/index.html
- 发现打包后输出到dist的文件中的login.html页面,运行时没有跳转,因此需要在src/login/index.js中补充登录跳转逻辑
- 补充以下登录逻辑后,配合远端/后台接口,就能实现跳转了,如果远端/后台服务器没开,接口失效,此时调试的话则需要使用webpack开发服务器
document.querySelector('.btn').addEventListener('click', () => {
// 在登录页的login-form表单中,名字=mobile的元素,取值
const phone = document.querySelector('.login-form [name=mobile]').value
const code = document.querySelector('.login-form [name=code]').value
// 调用引入的封装的函数,如果不符合则进入条件
if (!checkPhone(phone)) {
myAlert(false, '手机号长度必须是11位')
console.log('手机号长度必须是11位')
return
}
// 如果手机号符合要求,再校验验证码
if (!checkCode(code)) {
myAlert(false, '验证码长度必须是6位')
console.log('验证码长度必须是6位')
return
}
// 如果两个if都没有进去,则符合要求
console.log('提交到服务器登录...')
myAxios({
// 接口文档
url: '/v1_0/authorizations',
method: 'POST',
data: {
mobile: phone,
code: code,
},
})
.then((result) => {
myAlert(true, '登陆成功')
// 保存登录成功后服务器返回的token到本地,以后续打开其他页面时验证
localStorage.setItem('token', result.data.token)
setTimeout(() => {
// 不同页面不同文件夹,需要../返回上一级
location.href = '../content/index.html'
}, 1500)
console.log(result)
})
.catch((result) => {
myAlert(false, error.response.data.message)
console.dir(error.response.data.message)
})
})
- 建议:
- 开发环境下的页面路径,应与输出目录的页面路径一致
- 为了保证页面跳转路径/页面路由一致,应注意webpack.config.js中的配置,使得文件输入目录的嵌套层级应与build打包后输出目录的嵌套层级一致
案例-发布文章页面打包
需求:把发布文章页面一起打包
步骤:
- 准备发布文章页面源代码,改写成模块化的导出和导入方式
- 修改 webpack.config.js 的配置,增加一个入口和出口
- 打包观察效果
首先,准备已完成功能调试的多页面源码,放入相应位置引入项目,并将封装的代码,基于webpack,改用模块化语法导出
- 以前:单独写html页面,然后单独往html页面引入各个js,业务组件代码单独放utils文件夹,引入时从文件夹引入
- 现在:同一个页面的html、css、js三剑客放到同一个文件夹目录,加上业务组件代码独立目录,都改用模块化语法导出,模块化导入
操作上:
- 与前一节类似,先按模块化整理好文件,publish发布页用到的css,js都独立放到src下的publish发布页资源文件夹,html页则改名publish明确名字,改放到模板文件夹pubilc
- 检查publish.html页,确认.js的引入关系
- 在publish.html页中通过
<link rel="stylesheet" href="./index.css">
引入的css,改到publish文件夹下的index.js使用import './index.css'
引入 - 在publish.html页中引入的wangeditor5.css、bootstrap.min.css、alicdn图标等,这些css都可以直接加模板引擎语法,改为打包时通过cdn引入
- 在publish.html页中引入的axios相关js,继续使用模板引擎语法,改为打包时通过cdn引入
- 在publish.html页中引入的bootstrap.min.js,这个当初是为了webpack.config.js配置外部扩展时引的变量占位名,而且在项目中只用到了bootstrap.min.css,没有用js,可以省略删去不打包,也可以打包进去,看个人需求
- 在publish.html页中引入的表单插件form-serialize和wangeditor5富文本编辑器,加CDN地址+模板引擎语法,改为打包时通过cdn引入,如上节,本地开发时则通过npm引入
- 在publish.html页中通过
- 到src下的publish文章发布页资源文件夹,放入该页面的页面资源index.css和js,并模块化编辑
- 编辑publish页的index.js,引入
import '@/utils/alert.js'``import '@/utils/auth.js'``import '@/utils/editor.js'``import axios from '@/utils/request.js'``import './index.css'
- 打开终端,通过引入的依赖名称,到NPM查找,确认准确的下包命令,然后
npm i xxx
下包,如npm i form-serialize
和npm i @wangeditor/editor
- 备注:表单插件form-serialize,没有webpack运行环境区分,不需要加运行命令后缀,不需要区分运行环境安装
- 编辑publish页的index.js,引入
- 引入方式区别
- 原来在publish.html页中的依赖引入关系,是通过script标签对,按照先后顺序引入,然后暴露在全局;现在在页面主逻辑publish/index.js中引入其他模块,是通过import语法引入,而在其他业务模块,则需要export导出
- 确认引入到页面主逻辑的业务模块,在主逻辑中暴露在全局的是哪些对象,确认对象的来源关系;
- 例1:主逻辑引入了改造后的axios,同上一节的引入方式,引入来自@/utils/request.js这个文件中的axios变量/函数名
- 例2:主逻辑引入的
editor.setHtml()
函数,是来自于'@/utils/editor.js'
中,因此需要引入editor变量
- 针对主逻辑引入的对象/变量溯源,到各业务模块下,确认在业务模块中所引入的原始依赖的情况,并确认已有导出,确认业务模块的变量能供其他逻辑引入使用
- 例3:需要到editor.js中,确认原始的对象来源,从第三方库引入原始对象wangEditor到业务中使用,并且在业务模块末尾,通过export导出变量,业务模块对外暴露的输出变量名为editor
- 确认auth.js业务目的,目的是伴随页面运行以执行验证,不需要导出导入,简单引入拼凑式引入即可,但需要最先引入,放到最前面执行引入
- 确认引入的alert.js业务,是在业务中改装后以命名方式导出的函数名,因此需要命名方式引入,
import命名引入{变量/函数名}from文件
- 最后确认主业务还有哪些函数和方法没有引入,确认
const data = serialize(form, { hash: true, empty: true })
收集表单这里,还需要使用serialize,还需要引入通过npm下载的依赖
- 同上节,设置webpack打包入口和输出
提示:
- 无论在本地开发,或打包到生产模式,都可以用cdn引入css
- css不需要变量接收,用不到css代码里面的内容
- 自己编写的代码(index.js)需要被处理,要引到入口js中
- 如果在本地开发用npm下载的依赖组件,build上线时改用cdn源的组件,要使用模板语法判断
- wangeditor只能用require的方式代入(webpack两种导入方式都能识别)
最终的publish.html修改为模块化导出模板的效果,包括ajax、wangEditor、form-serialize、bootstrap、阿里图标
<% if(htmlWebpackPlugin.options.useCdn) { %>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css">
<link rel="stylesheet"
href="https://at.alicdn.com/t/c/font_4001111_4jrdiaeyvuy.css?spm=a313x.7781069.1998910419.52&file=font_4001111_4jrdiaeyvuy.css">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/wangeditor5/5.1.23/css/style.min.css">
<% } %>
<% if(htmlWebpackPlugin.options.useCdn) { %>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.4/axios.min.js"></script>
<script src="https://unpkg.com/form-serialize@0.7.2/index.js"></script>
<script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>
<% } %>
// 确认auth.js业务目的,目的是伴随页面运行以执行验证,不需要导出导入,简单引入拼凑式引入即可,但需要最先引入,放到最前面执行引入
import '@/utils/auth.js'
// 主页面样式,同目录下的index.css
import './index.css'
// 改造后的axios,同上一节的引入方式,引入来自@/utils/request.js这个文件中的axios变量/函数名
import axios from '@/utils/request.js'
// 主逻辑引入的`editor.setHtml()`函数,是来自于`'@/utils/editor.js'`中,因此需要引入editor变量
import editor from '@/utils/editor.js'
// 在业务中改装后以命名方式导出的函数名,因此需要命名方式引入,`import命名引入{变量/函数名}from文件`
import { myAlert } from '@/utils/alert.js'
// 确认主业务`const data = serialize(form, { hash: true, empty: true })`收集表单需要使用serialize,需要引入通过npm下载的依赖
import serialize from 'form-serialize'
// editor.js中
// 最顶部引入原始'@wangeditor/editor'
// 确认原始的导入对象来源,从第三方库引入原始对象wangEditor
const wangEditor = require('@wangeditor/editor')
// 最底部默认导出变量供主逻辑使用
// 并且在业务模块末尾,通过export导出变量,业务模块对外暴露的输出变量名为editor
export default editor
const config = {
entry: {
'login': path.resolve(__dirname, 'src/login/index.js'),
'content': path.resolve(__dirname, 'src/content/index.js'),
'pubilsh': path.resolve(__dirname, 'src/pubilsh/index.js'),
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/pubilsh.html'), //模板文件
filename: path.resolve(__dirname, 'dist/pubilsh/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production',
chunks: ['pubilsh'],
}),
new MiniCssExtractPlugin({
filename: './[name]/index.css',
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
],
}
module.exports = config
完成webpack的配置,npm run build
打包验证
- 验证:
npm run build
后,找到dist/login/index,右键,open with live server(alt+l alt+o),通过浏览器调试- 从登录页面出发调试,登录正常,页面跳转切换正常,是因为在编写html时的a标签的层级路径
<a href="../content/index.html">
,与输出路径层级路径结构相同,因此能正常使用 - 发现打包的耗时很长,分析控制台在打包过程中输出的代码,确认在处理node_module文件夹(NPM下载到本地的第三方包)下的wangeditor
- 确认实际上,在html模板中配好了使用CDN实现wangeditor和form-serialize,但在主逻辑和业务逻辑下的引入,是引入的npm下到本地的wangeditor包和form-serialize包
- 因此,需要根据环境差异,差分区别打包wangeditor和form-serialize这个两依赖,参考axios和bootstrap的做法,到webpack.config.js中,配置针对外部扩展的处理
- 最后注意,不要打错字,是publish,不是
~~pubilsh~~
,包括路径文件夹名称、webpack.config.js中的配置等
const config = {
// ...等等配置
}
if (process.env.NODE_ENV === 'production') {
// 外部扩展,防止import引入的本地第三方库被打包进项目,让生产上线时改为使用CDN
config.externals = {
// key:代码中 import from 后面的模块标识字符串,即引入标的(包含路径)
// value:替换在原地的全局变量名(要和 cdn 暴露在全局的变量名一致,可以不用写后缀)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios': 'axios',
// 复制在主逻辑中的引入路径作为key,复制引入函数名/变量名作为value值,value值为暴露给cdn替换的标的
'form-serialize':'serialize',
// 找到业务模块的第三方包的引入源,复制引入路径/路径名称作为key,复制引入到业务模块中使用的对象名wangEditor作为value值
'@wangeditor/editor':'wangEditor',
}
}
module.exports = config
完成webpack的配置,再次npm run build
打包验证,发现打包速度已得到优化
优化-分割公共代码
需求:针对多页面打包项目,把2个以上页面引用到的公共代码,独立提取
- 例如:登录页,发布文章页,内容管理页3个页面都引入了request.js这个业务(封装的axios函数),目前的打包配置,在3个页面中,各自打包进业务的代码,相当于打包3次业务代码
将公共代码单独提取的步骤:
- 配置 webpack.config.js 的 splitChunks 分割代码片段功能
- 打包观察效果
const config ={
// 优化相关的配置对象
optimization:{
// 代码分割
splitChunks: {
// 所有模块不管动态非动态引入的,都执行分割分析
chunks: 'all',
//分隔组
cacheGroups: {
// 抽取公共模块
commons: {
// 抽取的chunk最小大小字节
minSize: 0,
//最小引用数,即公共引用大于2的都分割提取公共代码出来
minChunks: 2,
// 当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
reuseExistingChunk: true,
// 分离出模块文件名
name(module, chunks, cacheGroupKey) {
// 加入-字符来组成字符串命名为:模块名1-枚块名2
const allChunksNames = chunks.map((item) => item.name).join('-')
// 输出到 dist 目录下位置
return `./js/${allChunksNames}`
},
},
},
},
}
}
- 在webpack的配置前和配置后,分别
npm run build
打包验证,发现:- 配置前,每个单独的页面中的js,都打包进去了各自页面重复引用的业务,如axios的地址
- 配置后,打包输出了一个js目录,下一级拼接了公共代码的来源结构层级(即 ` return
./js/${allChunksNanes}
` ) - 可以在公共代码目录确认webpack的分析,从目录名称结构可以分析出哪些代码在哪些目录下的逻辑中使用过(通过map获取chunks数组中的item.name,即入口entry的路径名字
const allChunksNames = chunks.map((item) => item.name).join('-')
,哪些页面用过的公共部分在名字上都连接起来) - 分析确认,公共代码包括auth.js的逻辑(验证不过跳回主页,以及token保存)
- 备注,通过npm run build的报错代码,确认配置项,注意大小写差异
I:\ziji practise - Webpack>npm run build
> ziji-practise---webpack@1.0.0 build
> cross-env NODE_ENV=production webpack --mode=production
[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.optimization has an unknown property 'splitchunks'. These properties are valid:
object { avoidEntryIife?, checkWasmTypes?, chunkIds?, concatenateModules?, emitOnErrors?, flagIncludedChunks?, innerGraph?, mangleExports?, mangleWasmImports?, mergeDuplicateChunks?, minimize?, minimizer?, moduleIds?, noEmitOnErrors?, nodeEnv?, portableRecords?, providedExports?, realContentHash?, removeAvailableModules?, removeEmptyChunks?, runtimeChunk?, sideEffects?, splitChunks?, usedExports? }
-> Enables/Disables integrated optimizations.
webpack部分总结
- 最基本:要掌握webpack的使用
- 有兴趣知道webpack是如何处理页面资源打包的话,可以再深入分析webpack的源码
- 必须要掌握webpack的使用,才能学习后面vue、react、小程序ts的学习,因为vue、react、小程序ts等框架,浏览器是不能直接运行相关的模板语法的,需要借助webpack或者vite,将框架的代码,进行编译构建,输出成三剑客,再让浏览器执行三剑客的代码