前端AJAX入门到实战全套教程,包含学前端框架必会的(ajax+node.js+webpack+git)

Ajax

一. Ajax入门

1. AJAX 概念和 axios 使用

什么是 AJAX ? mdn
  • AJAX 是异步的 JavaScript 和 XML(Asynchronous JavaScript And XML)
  • 使用浏览器的 XMLHttpRequest 对象 与服务器通信
  • 它可以使用 JSON、XML、HTML 和text 文本等格式发送和接收数据。AJAX最吸引人的就是它的"异步"特性,也就是是它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。

简单来说,AJAX是浏览器与服务器进行数据通信的技术。

什么是服务器?
  • 可以暂时理解为提供数据的一台电脑
为何学 AJAX ?
  • 以前我们的数据都是写在代码里固定的, 无法随时变化
  • 现在我们的数据可以从服务器上进行获取,让数据变活
怎么学 AJAX ?
  1. 先使用一个第三方库叫 axios,与服务器通信

    • 基于 XMLHttpRequest 封装、代码简单、月下载量在 14 亿次,让我们有更多精力关注在与服务器通信上
    • 后续 Vue,React项目中都会用到 axios
  2. 再学习 XMLHttpRequest 对象的使用,了解 AJAX 底层原理

axios 使用
  1. 引入 axios.js

    axios.js文件链接: cdn.jsdelivr.net/npm/axios/d...

  2. 使用 axios 函数

    • 传入配置对象
    • 再用 .then 回调函数接收结果,并做后续处理
    js 复制代码
    axios({
      url: '目标资源地址'
    }).then((result) => {
      // 对服务器返回的数据做后续处理
    })

    注意:请求的 url 地址, 就是标记资源的网址

2. 认识 URL

了解 URL 的组成和作用

1. 为什么要认识 URL ? mdn

知道作用和组成,方便与后端人员沟通

2. 什么是 URL ?

统一资源定位符(Uniform Resource Locator,简写:URL),或称定位地址、URL地址,俗称网页地址,简称网址用于定位访问网络上的资源(资源指的是:网页,图片,数据,视频,音频等等)

3. URL 的组成?
  • 协议,域名,资源路径(URL 组成有很多部分,我们先掌握这3个重要的部分即可)
协议

http 协议:超文本传输协议,规定了浏览器和服务器之间传输数据的格式(而格式具体有哪些稍后我们就会学到)

协议范围:http、https...

域名

域名:标记服务器在互联网中的方位

网络中有很多服务器,你想访问哪一台,就需要知道它的域名才可以

资源路径

资源路径:一个服务器内有多个资源,用于标记资源在服务器下的具体位置

3. URL 查询参数

通过URL传递查询参数,获取匹配的数据

查询参数定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据

语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2

axios 如何携带查询参数?

  • 使用 params 选项即可

    注意:axios 在运行时把参数名和值,会拼接到 url?参数名=值

    js 复制代码
    axios({
      url: '目标资源地址',
      params: {
        参数名: 值
      }
    }).then(result => {
      // 对服务器返回的数据做后续处理
    })

案例-查询-地区列表 代码如下:

js 复制代码
/*
  获取地区列表: http://hmajax.itheima.net/api/area
  查询参数:
    pname: 省份或直辖市名字
    cname: 城市名字
*/
// 目标: 根据省份和城市名字, 查询地区列表
// 1. 查询按钮-点击事件
document.querySelector('.sel-btn').addEventListener('click', () => {
    // 2. 获取省份和城市名字
    let pname = document.querySelector('.province').value
    let cname = document.querySelector('.city').value

    // 3. 基于axios请求地区列表数据
    axios({
        url: 'http://hmajax.itheima.net/api/area',
        params: {
            pname,
            cname
        }
    }).then(result => {
        // console.log(result)
        // 4. 把数据转li标签插入到页面上
        let list = result.data.list
        console.log(list)
        let theLi = list.map(areaName => `<li class="list-group-item">${areaName}</li>`).join('')
        console.log(theLi)
        document.querySelector('.list-group').innerHTML = theLi
    })
})

4. 常用请求方法和数据提交

掌握如何向服务器提交数据,而不单单是获取数据

想要提交数据,先来了解什么是请求方法

请求方法

请求方法:对服务器资源,要执行的操作。

请求方法是一些固定单词的英文,例如:GET,POST,PUT,DELETE,PATCH(这些都是http协议规定的),每个单词对应一种对服务器资源要执行的操作。

前面我们获取数据其实用的就是GET请求方法,但是axios内部设置了默认请求方法就是GET,我们就没有写

提交数据需要使用POST请求方法

  1. 什么时候进行数据提交呢?

    场景:当数据需要在服务器上保存

    例如:多端要查看同一份订单数据,或者使用同一个账号进行登录,那订单/用户名+密码,就需要保存在服务器上,随时随地进行访问

  2. axios 如何提交数据到服务器呢?

    需要学习,method 和 data 这2个新的选项了

axios 请求配置
  • url:请求的 URL 网址

  • method:请求的方法,GET可以省略(不区分大小写)

  • data:提交数据

    js 复制代码
    axios({
      url: '目标资源地址',
      method: '请求方法',
      data: {
        参数名: 值
      }
    }).then(result => {
      // 对服务器返回的数据做后续处理
    })
  • 案例:注册账号,提交用户名和密码到服务器保存

    注册用户 URL 网址:hmajax.itheima.net/api/registe...

    请求方法:POST

    参数名:

    username:用户名(要求中英文和数字组成,最少8位)

    password:密码(最少6位)

代码如下:

js 复制代码
/*
  注册用户:http://hmajax.itheima.net/api/register
  请求方法:POST
  参数名:
    username:用户名(中英文和数字组成,最少8位)
    password:密码  (最少6位)

  目标:点击按钮,通过axios提交用户和密码,完成注册
*/
document.querySelector('.btn').addEventListener('click', () => {
  axios({
    url: 'http://hmajax.itheima.net/api/register',
    method: 'POST',
    data: {
      username: 'itheima007',
      password: '7654321'
    }
  })
})

5. axios 错误处理

掌握接收 axios 响应错误信息的处理语法

  • 场景:再次注册相同的账号,会遇到报错信息

  • 处理:用更直观的方式,给普通用户展示错误信息

    因为,普通用户不会去控制台里看错误信息,我们要编写代码拿到错误并展示给用户在页面上

  • 语法:在 then 方法的后面,通过点语法调用 catch 方法,传入回调函数并定义形参

    js 复制代码
    axios({
      // ...请求选项
    }).then(result => {
      // 处理成功数据
    }).catch(error => {
      // 处理失败错误
    })

案例:

js 复制代码
//注册案例,重复注册时通过弹框提示用户错误原因
document.querySelector('.btn').addEventListener('click', () => {
    axios({
      url: 'http://hmajax.itheima.net/api/register',
      method: 'post',
      data: {
        username: 'itheima007',
        password: '7654321'
      }
    }).then(result => {
      // 成功
      console.log(result)
    }).catch(error => {
      // 失败
      // 处理错误信息
      console.log(error)
      console.log(error.response.data.message)
      alert(error.response.data.message)
    })
})

6. HTTP 协议-报文

HTTP 协议:规定了浏览器发送及服务器返回内容的格式

请求报文

了解 HTTP 协议中,请求报文的组成和作用

请求报文:浏览器按照 HTTP 协议要求的格式,发送给服务器的内容

请求报文的组成部分有:

  • 请求行:请求方法,URL,协议版本
  • 请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)
  • 空行:分割请求头,空行之后的是发送给服务器的资源
  • 请求体:发送的资源

通过 Chrome 的网络面板如何查看请求体?

错误排查

需求:通过请求报文排查错误原因,并修复

学习了查看请求报文有什么用呢?

  • 可以用来确认我们代码发送的请求数据是否真的正确
响应报文

响应报文:服务器按照http协议要求的格式,返回给浏览器的内容

响应报文的组成:

  • 响应行(状态行):协议,HTTP响应状态码,状态信息
  • 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
  • 空行:分割响应头,控制之后的是服务器返回的资源
  • 响应体:返回的资源
HTTP 响应状态码

HTTP 响应状态码:用来表明请求是否成功完成

例如:404(客户端要找的资源,在服务器上不存在)

7. 接口文档

接口文档:描述接口的文章(一般是后端工程师,编写和提供)

接口:指使用 AJAX 和 服务器通讯时,使用的 URL,请求方法,以及参数,例如:AJAX阶段接口文档

8. form-serialize 插件

  • 作用:快速收集表单元素的值

  • 我们前面收集表单元素的值,是一个个标签获取的,如果一套表单里有很多很多表单元素,如何一次性快速收集出来呢?

    使用 form-serialize 插件提供的 serialize 函数就可以办到

  • form-serialize 插件语法:

    1. 引入 form-serialize 插件到自己网页中

    2. 使用 serialize 函数

      serialize(参数1, 参数2)

      • 参数1:要获取的 form 表单标签对象(要求表单元素需要有 name 属性-用来作为收集的数据中属性名)

      • 参数2:配置对象

        • hash 设置获取数据结构:
          • true - 收集出来的是一个 JS 对象结构
          • false - 收集出来的是一个查询字符串格式
        • empty 设置是否获取空值:
          • true - 收集空值
          • false - 不收集空值

需求:收集登录表单里用户名和密码

对应代码:

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>form-serialize插件使用</title>
</head>

<body>
  <form action="javascript:;" class="example-form">
    <input type="text" name="username">
    <br>
    <input type="text" name="password">
    <br>
    <input type="button" class="btn" value="提交">
  </form>
  <!-- 
    目标:在点击提交时,使用form-serialize插件,快速收集表单元素值
    1. 把插件引入到自己网页中
  -->
  <script src="./lib/form-serialize.js"></script>
  <script>
    document.querySelector('.btn').addEventListener('click', () => {
      /**
       * 2. 使用serialize函数,快速收集表单元素的值
       * 参数1:要获取哪个表单的数据
       *  表单元素设置name属性,值会作为对象的属性名
       *  建议name属性的值,最好和接口文档参数名一致
       * 参数2:配置对象
       *  hash 设置获取数据结构
       *    - true:JS对象(推荐)一般请求体里提交给服务器
       *    - false: 查询字符串
       *  empty 设置是否获取空值
       *    - true: 获取空值(推荐)数据结构和标签结构一致
       *    - false:不获取空值
      */
      const form = document.querySelector('.example-form')
      const data = serialize(form, { hash: true, empty: true })
      // const data = serialize(form, { hash: false, empty: true })
      // const data = serialize(form, { hash: true, empty: false })
      console.log(data)
    })
  </script>
</body>

</html>

案例-用户登录-form-serialize

  1. 基于模板代码,使用 form-serialize 插件来收集用户名和密码

  2. 在原来的代码基础上修改即可

    1. 先引入插件

      html 复制代码
      <!-- 3.1 引入插件 -->
      <script src="./lib/form-serialize.js"></script>
    2. 然后修改代码

      js 复制代码
      // 3.2 使用serialize函数,收集登录表单里用户名和密码
      const form = document.querySelector('.login-form')
      const data = serialize(form, { hash: true, empty: true })
      console.log(data)
      // {username: 'itheima007', password: '7654321'}
      const { username, password } = data

9. 小结

  1. AJAX 有什么用?

    答案

    • 浏览器和服务器之间通信,动态数据交互
  2. AJAX 如何学:

    答案

    • 先掌握 axios 库使用,再了解 XMLHttpRequest 原理
  3. 这一节 axios 体验步骤(语法)?

    答案

    • 引入 axios 库,使用 axios 相关语法
  4. URL 是什么?

    答案

    • 统一资源定位符,网址,用于访问服务器上资源
  5. 请解释这个 URL,每个部分作用?

    hmajax.itheima.net/api/news
    答案

    • 协议://域名/资源路径
  6. URL 查询参数有什么用?

    答案

    • 浏览器提供给服务器额外信息,获取对应的数据
  7. axios 要如何携带查询参数?

    答案

    • 使用 params 选项,携带参数名和值在对象结构中
  8. 请求方法最常用的是哪2个,分别有什么作用?

    答案

    • POST 提交数据,GET 查询数据
  9. axios 的核心配置项有哪几个,作用分别是什么?

    答案

    • url:目标资源地址(请求 URL 网址);
    • method:请求方法,GET 可以省略(不区分大小写);
    • params:查询参数;
    • data:提交的数据
  10. axios 如何拿到请求响应失败的信息?

    答案

    • 通过 axios 函数调用后,在后面接着调用 .catch 方法捕获
  11. 浏览器发送给服务器的内容叫做,请求报文

  12. 请求报文的组成是什么?

    答案

    • 请求行,请求头,空行,请求体
  13. 学会了查看请求报文,对实际开发有什么帮助呢?

    答案

    • 可以快速确认我们发送的内容是否正确
  14. 响应报文的组成:

    答案

    • 响应行(状态行):协议,HTTP响应状态码,状态信息
    • 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)
    • 空行:分割响应头,控制之后的是服务器返回的资源
    • 响应体:返回的资源
  15. 接口文档是什么?

    答案

    • 由后端提供的描述接口的文章
  16. 接口文档都包含哪些信息?

    答案

    • 请求的 URL 网址,请求方法,请求参数和说明
  17. 在浏览器中如何查看查询参数/请求体,以及响应体数据?

  18. 请求报文和响应报文由几个部分组成,每个部分的作用?

  19. 我们什么时候使用 form-serialize 插件?

    答案

    • 快速收集表单元素的值
  20. 如何使用 form-serialize 插件?

    答案

      1. 先引入插件到自己的网页中,2. 准备form和表单元素的name属性,3.使用serialize函数,传入form表单和配置对象
  21. 配置对象中 hash 和 empty 有什么用?

    答案

    • hash 决定是收集为 JS 对象还是查询参数字符串,empty 决定是否收集空值
  22. 如何把一个第三方插件使用在已完成的案例中?

    答案

    • 引入后,只需要使用在要修改的地方,修改一点就要确认测试一下

二. AJAX原理

1. AJAX原理-XMLHttpRequest

概念

定义:XMLHttpRequest(XHR)对象用于与服务器交互。通过XMLHttpRequest可以再不刷新页面的情况下请求特定URL,获取数据。这运行网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用。

关系:axios 内部采用 XMLHttpRequest 与服务器交互。

好处:掌握使用 XHR 与服务器进行数据交互,了解 axios 内部原理

AJAX 是浏览器与服务器通信的技术,采用 XMLHttpRequest 对象相关代码

axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数

学习 XHR 也是了解 axios 内部与服务器交互过程的真正原理

使用XMLHttpRequest

步骤:

  1. 创建 XMLHttpRequest 对象
  2. 配置请求方法和请求 url 地址
  3. 监听 loadend 事件,接收响应结果
  4. 发起请求
js 复制代码
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
  // 响应结果 接收- 响应
  console.log(xhr.response)
})
xhr.send() // 发送 - 请求
XMLHttpRequest - 查询参数

什么是查询参数:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据

语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2

所以,原生 XHR 需要自己在 url 后面携带查询参数字符串,没有 axios 帮助我们把 params 参数拼接到 url 字符串后面了

js 复制代码
/**
 * 目标:使用XHR携带查询参数,展示某个省下属的城市列表
*/
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/city?pname=辽宁省')
xhr.addEventListener('loadend', () => {
  console.log(xhr.response)
  const data = JSON.parse(xhr.response)
  console.log(data)
  document.querySelector('.city-p').innerHTML = data.list.join('<br>')
})
xhr.send()
URLSearchParams

多个查询参数,如果自己拼接很麻烦,这里用 URLSearchParams 把参数对象转成"参数名=值&参数名=值"格式的字符串,语法如下:

js 复制代码
// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
  参数名1: 值1,
  参数名2: 值2
})

// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2
XMLHttpRequest - 数据提交

需求:通过 XHR 提交用户名和密码,完成注册用户功能

核心:

  • 请求头设置 Content-Type:application/json

  • 请求体携带 JSON 字符串

步骤和语法:

  1. 注意1:但是这次没有 axios 帮我们了,我们需要自己设置请求头 Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用

  2. 注意2:没有 axios 了,我们前端要传递的请求体数据,也没人帮我把 JS 对象转成 JSON 字符串了,需要我们自己转换

  3. 注意3:原生 XHR 需要在 send 方法调用时,传入请求体携带

js 复制代码
const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => {
  console.log(xhr.response)
})

// 1. 告诉服务器,我传递的内容类型,是 JSON 字符串
xhr.setRequestHeader('Content-Type', 'application/json')
// 2. 准备数据并转成 JSON 字符串
const user = { username: 'itheima007', password: '7654321' }
const userStr = JSON.stringify(user)
// 3. 发送请求体数据
xhr.send(userStr)
小结
  1. AJAX 原理是什么?

    答案

    • window 提供的 XMLHttpRequest对象
  2. 为什么学习 XHR ?

    答案

    • 有更多与服务器数据通信方式
    • 了解 axios 内部原理
  3. XHR 使用步骤?

    答案

      1. 创建 XHR 对象
      1. 调用 open 方法,设置 url 和请求方法
      1. 监听 loadend 事件,接收结果
      1. 调用 send 方法,发起请求
  4. XHR 如何携带查询参数?

    答案

    • 在调用 open 方法的时候,在 url? 后面按照指定格式拼接参数名和值
  5. JS 对象如何转成查询参数格式字符串?

    答案

    • 在调用 open 方法的时候,在 url? 后面按照指定格式拼接参数名和值
  6. XHR 如何提交请求体数据?

    答案

    • 在 send 中携带请求体数据,要按照后端要求的内容类型携带

2. Promise

Promise 定义
  1. 什么是 Promise ?

    • Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。
  2. Promise 的好处是什么?

    • 逻辑更清晰(成功或失败会关联后续的处理函数)

    • 了解 axios 函数内部运作的机制

    • 能解决回调函数地狱问题

  1. Promise 管理异步任务,语法怎么用?

    js 复制代码
    // 1. 创建 Promise 对象
    const p = new Promise((resolve, reject) => {
     // 2. 执行异步任务-并传递结果
     // 成功调用: resolve(值) 触发 then() 执行
     // 失败调用: reject(值) 触发 catch() 执行
    })
    // 3. 接收结果
    p.then(result => {
     // 成功
    }).catch(error => {
     // 失败
    })
  2. 示例代码:

    js 复制代码
    /**
     * 目标:使用Promise管理异步任务
    */
    // 1. 创建Promise对象
    const p = new Promise((resolve, reject) => {
      // 2. 执行异步代码
      setTimeout(() => {
        // resolve('模拟AJAX请求-成功结果')
        reject(new Error('模拟AJAX请求-失败结果'))
      }, 2000)
    })
    
    // 3. 获取结果
    p.then(result => {
      console.log(result)
    }).catch(error => {
      console.log(error)
    })
Promise 三种状态
  1. 为什么要了解 Promise 的三种状态(作用) ?

    • 知道 Promise 对象如何关联的处理函数,以及代码的执行顺序
  2. Promise 有哪三种状态?

    每个 Promise 对象必定处于以下三种状态之一

    1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝

    2. 已兑现(fulfilled):操作成功完成

    3. 已拒绝(rejected):操作失败

  3. Promise 的状态改变有什么用:调用对应函数,改变 Promise 对象状态后,内部触发对应回调函数传参并执行。

注意: Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变

封装_简易axios-获取省份列表

模拟 axios 函数封装,更深入了解 axios 内部运作原理

  1. 需求:基于 Promise 和 XHR 封装 myAxios 函数,获取省份列表展示到页面

  2. 核心语法:

    js 复制代码
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        // XHR 请求
        // 调用成功/失败的处理程序
      })
    }
    
    myAxios({
      url: '目标资源地址'
    }).then(result => {
        
    }).catch(error => {
        
    })

代码如下:

scss 复制代码
```js
/**
 * 目标:封装_简易axios函数_获取省份列表
 *  1. 定义myAxios函数,接收配置对象,返回Promise对象
 *  2. 发起XHR请求,默认请求方法为GET
 *  3. 调用成功/失败的处理程序
 *  4. 使用myAxios函数,获取省份列表展示
*/
// 1. 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
  return new Promise((resolve, reject) => {
    // 2. 发起XHR请求,默认请求方法为GET
    const xhr = new XMLHttpRequest()
    xhr.open(config.method || 'GET', config.url)
    xhr.addEventListener('loadend', () => {
      // 3. 调用成功/失败的处理程序
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(new Error(xhr.response))
      }
    })
    xhr.send()
  })
}

// 4. 使用myAxios函数,获取省份列表展示
myAxios({
  url: 'http://hmajax.itheima.net/api/province'
}).then(result => {
  console.log(result)
  document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
  console.log(error)
  document.querySelector('.my-p').innerHTML = error.message
})
```
封装_简易axios-获取地区列表

修改 myAxios 函数支持传递查询参数,获取辽宁省,大连市的地区列表

  1. 需求:在上个封装的建议 axios 函数基础上,修改代码支持传递查询参数功能

  2. 修改步骤:

    1. myAxios 函数调用后,判断 params 选项
    2. 基于 URLSearchParams 转换查询参数字符串
    3. 使用自己封装的 myAxios 函数显示地区列表
  3. 核心代码:

    js 复制代码
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        // 1. 判断有params选项,携带查询参数
        if (config.params) {
          // 2. 使用URLSearchParams转换,并携带到url上
          const paramsObj = new URLSearchParams(config.params)
          const queryString = paramsObj.toString()
          // 把查询参数字符串,拼接在url?后面
          config.url += `?${queryString}`
        }
    
        xhr.open(config.method || 'GET', config.url)
        xhr.addEventListener('loadend', () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(JSON.parse(xhr.response))
          } else {
            reject(new Error(xhr.response))
          }
        })
        xhr.send()
      })
    }
    
    // 3. 使用myAxios函数,获取地区列表
    myAxios({
      url: 'http://hmajax.itheima.net/api/area',
      params: {
        pname: '辽宁省',
        cname: '大连市'
      }
    }).then(result => {
      console.log(result)
      document.querySelector('.my-p').innerHTML = result.list.join('<br>')
    })
封装_简易axios-注册用户
js 复制代码
function myAxios(config) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()

    if (config.params) {
      const paramsObj = new URLSearchParams(config.params)
      const queryString = paramsObj.toString()
      config.url += `?${queryString}`
    }
    xhr.open(config.method || 'GET', config.url)

    xhr.addEventListener('loadend', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(new Error(xhr.response))
      }
    })
    // 1. 判断有data选项,携带请求体
    if (config.data) {
      // 2. 转换数据类型,在send中发送
      const jsonStr = JSON.stringify(config.data)
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(jsonStr)
    } else {
      // 如果没有请求体数据,正常的发起请求
      xhr.send()
    }
  })
}

document.querySelector('.reg-btn').addEventListener('click', () => {
  // 3. 使用myAxios函数,完成注册用户
  myAxios({
    url: 'http://hmajax.itheima.net/api/register',
    method: 'POST',
    data: {
      username: 'itheima999',
      password: '666666'
    }
  }).then(result => {
    console.log(result)
  }).catch(error => {
    console.dir(error)
  })
})

3. 小结

  1. 什么是 Promise ?

    答案

    • 表示(管理)一个异步操作最终状态和结果值的对象
  2. 为什么学习 Promise ?

    答案

    • 成功和失败状态,可以关联对应处理函数
    • 了解 axios 内部运作的原理
  3. Promise 使用步骤?

    答案

    • new Promise 创建Promise对象。
    • 执行异步任务-并传递结果1. 用 resolve 关联 then 的回调函数传递成功结果。2.用 reject 关联 catch 的回调函数传递失败结果。
  4. Promise 对象有哪 3 种状态?

    答案

    • 待定 pending,已兑现 fulfilled,已拒绝 rejected
  5. Promise 状态有什么用?

    答案

    • 状态改变后,调用关联的处理函数
  6. AJAX 如何判断是否请求响应成功了?

    答案

    • 响应状态码在大于等于 200 并且小于 300 的范围是成功的
  7. 自己封装的 myAxios 如何设置默认请求方法 GET?

    答案

    • config.method 判断有值就用,无值用'GET'方法
  8. 外面传入查询参数对象,myAxios 函数内如何转查询参数字符串?

    答案

    • 使用 URLSearchParams 对象转换
  9. 外面传入 data 选项,myAxios 函数内如何携带请求体参数?

    答案

    • 判断外面传入了这个属性,自己转成 JSON 字符串并设置请求头并在 send 方法中携带
  10. 做完这个项目会带来什么收获?

    答案

    • 可以做一个真正有意义的业务,查看城市的天气预报,测试自己封装的 myAxios 函数是否好用
  11. 监听输入框实时改变的事件是什么?

    答案

    • input事件

4. 重点(必须会)

  1. 了解 AJAX 原理之 XMLHttpRequest(XHR)相关语法
  2. 了解 Promise 的作用和三种状态
  3. 了解 axios 内部运作的过程

三. AJAX进阶

1. 同步代码和异步代码

同步代码:浏览器是按照我们书写代码的顺序一行一行地执行程序的。浏览器会等待代码的解析和工作,在上一行完成才会执行下一行。(逐行执行,需原地等待结果后,才继续向下执行)

异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果

代码打印顺序:

js 复制代码
const result = 0 + 1
console.log(result)
setTimeout(() => {
  console.log(2)
}, 2000)
document.querySelector('.btn').addEventListener('click', () => {
  console.log(3)
})
document.body.style.backgroundColor = 'pink'
console.log(4)
//结果:1, 4, 2
//按钮点击一次打印一次 3

异步代码接收结果,使用的都是回调函数

2. 回调函数地狱和 Promise 链式调用

回调函数地狱

了解回调函数地狱的概念和缺点

需求:展示默认第一个省,第一个城市,第一个地区在下拉菜单中

概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱

缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身

Promise-链式调用

了解 Promise 链式调用特点和语法

概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束

细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果

好处:通过链式调用,解决回调函数嵌套问题

按照图解,编写核心代码:

js 复制代码
/**
 * 目标:掌握Promise的链式调用
 * 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
// 1. 创建Promise对象-模拟请求省份名字
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('北京市')
  }, 2000)
})

// 2. 获取省份名字
const p2 = p.then(result => {
  console.log(result)
  // 3. 创建Promise对象-模拟请求城市名字
  // return Promise对象最终状态和结果,影响到新的Promise对象
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(result + '--- 北京')
    }, 2000)
  })
})

// 4. 获取城市名字
p2.then(result => {
  console.log(result)
})

// then()原地的结果是一个新的Promise对象
console.log(p2 === p)
Promise-链式调用_解决回调地狱

使用 Promise 链式调用,解决回调函数地狱问题

做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来

按照图解思路,编写核心代码:

js 复制代码
/**
 * 目标:把回调函数嵌套代码,改成Promise链式调用结构
 * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
*/
let pname = ''
// 1. 得到-获取省份Promise对象
axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
  pname = result.data.list[0]
  document.querySelector('.province').innerHTML = pname
  // 2. 得到-获取城市Promise对象
  return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
}).then(result => {
  const cname = result.data.list[0]
  document.querySelector('.city').innerHTML = cname
  // 3. 得到-获取地区Promise对象
  return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
}).then(result => {
  console.log(result)
  const areaName = result.data.list[0]
  document.querySelector('.area').innerHTML = areaName
})

3. async 函数和 await

概念

  • async 和await关键字让我们可以用一种更简洁的方式写出基于 Promise的异步行为,而无需刻意地链式调用promise。
  • 在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值

做法:使用 async 和 await 解决回调地狱问题

注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)

核心代码:

js 复制代码
// 1. 定义async修饰函数
async function getData() {
  // 2. await等待Promise对象成功的结果
  const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
  const pname = pObj.data.list[0]
  const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
  const cname = cObj.data.list[0]
  const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
  const areaName = aObj.data.list[0]

  document.querySelector('.province').innerHTML = pname
  document.querySelector('.city').innerHTML = cname
  document.querySelector('.area').innerHTML = areaName
}
getData()
async 函数和 await 捕获错误

使用:try...catch

try 和 catch 的作用:语句标记要尝试的语句块,并指定一个出现异常时抛出的响应

语法:

js 复制代码
try {
  // 要执行的代码
} catch (error) {
  // error 接收的是,错误消息
  // try 里代码,如果有错误,直接进入这里执行
}

注意:try 里有报错的代码,会立刻跳转到 catch 中

4. 事件循环 EventLoop

掌握事件循环模型是如何执行异步代码的

事件循环(EventLoop)好处:掌握后知道 JS 是如何安排和运行代码的

作用:JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码,收集和处理事件以及执行队列中的子任务

原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型

概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环

事件循环-练习

需求:请根据掌握的事件循环的模型概念,分析代码执行过程

js 复制代码
/**
 * 目标:阅读并回答执行的顺序结果
*/
console.log(1)
setTimeout(() => {
  console.log(2)
}, 0)
function myFn() {
  console.log(3)
}
function ajaxFn() {
  const xhr = new XMLHttpRequest()
  xhr.open('GET', 'http://hmajax.itheima.net/api/province')
  xhr.addEventListener('loadend', () => {
    console.log(4)
  })
  xhr.send()
}
for (let i = 0; i < 1; i++) {
  console.log(5)
}
ajaxFn()
document.addEventListener('click', () => {
  console.log(6)
})
myFn()

结果:1 5 3 2 4 点击一次document就会执行一次打印6

宏任务与微任务

掌握微任务和宏任务的概念和区分

ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务

异步任务划分为了

  • 宏任务:由浏览器环境执行的异步代码
  • 微任务:由 JS 引擎环境执行的异步代码

注意:Promise 本身是同步的,而 then 和 catch 回调函数是异步的。

微任务队列清空后,才会执行下一个宏任务

注意:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!

下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队

总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系

经典面试题 宏任务和微任务-执行顺序

示例1

执行步骤说明:

  1. script脚本交给【宿主环境(浏览器)】,将整个js代码推入宏任务队列,此时调用栈是空闲,就开始执行第一个宏任务
  2. 【console.log(1)】放入调用栈执行,在控制台打印1,执行完之后出栈
  3. js引擎接着读取代码,【setTimeout(() => {console.log(2)}, 0)】是宏任务,交给宿主浏览器,浏览器进行倒计时,发现是0秒,会立刻将回调函数【() => {console.log(2)}】放到宏任务队列当中排队
  4. js引擎继续读取代码,【new Promise((resolve) => { console.log(3) resolve(4) })】放到执行栈调用,Promise 本身是同步的,所以控制台立刻打印3,【resolve(4)】标记promise成功状态,【new Promise】执行完毕出栈,此时得到一个promise对象,它是成功状态的
  5. 接着,js引擎执行同步代码【p.then(result => console.log(result))】放入执行栈,发现回调函数是异步微任务,将【result => console.log(result)】放入微任务队列进行排队,【p.then(result => console.log(result))】执行完毕出栈
  6. js引擎接着读取代码,【console.log(5)】推入调用栈执行,在控制台打印5,执行完毕之后出栈
  7. 此时,script中的同步代码都执行完毕,调用栈空闲了,开始去任务队列中反复调度有没有要执行的异步回调函数
  8. 优先调度微任务队列,因为微任务更接近JS 引擎
  9. 发现微任务队列中有待执行的回调函数,推入到调用栈执行【result => console.log(result),已确认成功并且传入4】,所以在控制台打印4,微任务的回调就执行完了,出栈
  10. 调用栈又空闲了,又反复调度任务队列中的回调,发现微任务队列中的任务已经被清空了,所以调度宏任务队列中任务,【() => {console.log(2)}】推入调用栈执行,在控制台打印2,宏任务的回调执行完毕,出栈
  11. 执行完毕

示例2

js 复制代码
// 目标:回答代码执行顺序
console.log(1)
setTimeout(() => {
  console.log(2)
  const p = new Promise(resolve => resolve(3))
  p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {
  setTimeout(() => {
    console.log(4)
  }, 0)
  resolve(5)
})
p.then(result => console.log(result))
const p2 = new Promise(resolve => resolve(6))
p2.then(result => console.log(result))
console.log(7)

//打印顺序为:1 7 5 6 2 3 4

5. Promise.all 静态方法

了解 Promise.all 作用和使用场景

概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

语法:

js 复制代码
const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
  // result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
  // 第一个失败的 Promise 对象,抛出的异常对象
})
js 复制代码
<body>
  <ul class="my-ul"></ul>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握Promise的all方法作用,和使用场景
     * 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
     * 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
     * code:
     * 北京-110100
     * 上海-310100
     * 广州-440100
     * 深圳-440300
    */
    // 1. 请求城市天气,得到Promise对象
    const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
    const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
    const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
    const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })

    // 2. 使用Promise.all,合并多个Promise对象
    const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
    p.then(result => {
      // 注意:结果数组顺序和合并时顺序是一致
      console.log(result)
      const htmlStr = result.map(item => {
        return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
      }).join('')
      document.querySelector('.my-ul').innerHTML = htmlStr
    }).catch(error => {
      console.dir(error)
    })
  </script>
</body>

6. 小结

  1. 什么是同步代码?

    答案

    • 逐行执行,原地等待结果后,才继续向下执行
  2. 什么是异步代码?

    答案

    • 调用后耗时,不阻塞代码执行,将来完成后触发回调函数
  3. JS 中有哪些异步代码?

    答案

    • setTimeout / setInterval,事件,AJAX
  4. 异步代码如何接收结果?

    答案

    • 依靠回调函数来接收
  5. 什么是回调函数地狱?

    答案

    • 在回调函数一直向下嵌套回调函数,形成回调函数地狱
  6. 回调函数地狱问题?

    答案

    • 可读性差,异常捕获困难,耦合性严重
  7. 什么是 Promise 的链式调用?

    答案

    • 使用 then 方法返回新 Promise 对象特性,一直串联下去
  8. then 回调函数中,return 的值会传给哪里?

    答案

    • 传给 then 方法生成的新 Promise 对象
  9. Promise 链式调用有什么用?

    答案

    • 解决回调函数嵌套问题
  10. Promise 链式调用如何解决回调函数地狱?

    答案

    • then 的回调函数中 return Promise对象,影响当前新 Promise 对象的值
  11. await 的作用是什么?

    答案

    • 替代 then 方法来提取 Promise 对象成功状态的结果
  12. try 和 catch 有什么作用?

    答案

    • 捕获同步流程的代码报错信息
  13. 什么是事件循环?

    答案

    • 执行代码和收集异步任务,在调用栈空闲时,反复调用任务队列里回调函数执行机制
  14. 为什么有事件循环?

    答案

    • JavaScript 是单线程的,为了不阻塞 JS 引擎,设计执行代码的模型
  15. JavaScript 内代码如何执行?

    答案

    • 执行同步代码,遇到异步代码交给宿主浏览器环境执行
    • 异步有了结果后,把回调函数放入任务队列排队
    • 当调用栈空闲后,反复调用任务队列里的回调函数
  16. 什么是宏任务?

    答案

    • 浏览器执行的异步代码
    • 例如:JS 执行脚本事件(script),setTimeout/setInterval,AJAX请求完成事件,用户交互事件等
  17. 什么是微任务?

    答案

    • JS 引擎执行的异步代码
    • 例如:Promise对象.then()的回调(注:Promise对象本身是同步的)
  18. JavaScript 内代码如何执行?

    答案

    • 执行第一个 script 脚本事件宏任务,执行里面同步代码
    • 遇到 宏任务/微任务 交给宿主环境(浏览器/js引擎),有结果回调函数进入对应队列
    • 当执行栈空闲时,清空微任务队列,再执行下一个宏任务,从1再来
  19. Promise.all 什么时候使用?

    答案

    • 合并多个 Promise 对象并等待所有同时成功的结果,如果有一个报错就会最终为失败状态,当需要同时渲染多个接口数据同时到网页上时使用

7. 今日重点(必须会)

  1. 区分异步代码,回调函数地狱问题和所有解决防范(Promise 链式调用)

  2. 掌握 async 和 await 的使用

  3. 理解 EventLoop 和宏任务微任务执行顺序

  4. 了解 Promise.all 的作用和使用场景

扩展阅读

ES6

  1. ES6 对象属性和值简写的前提是什么?

    答案

    • 当属性名和value位置变量名同名即可简写

函数

  1. 我们什么时候需要封装函数?

    答案

    • 遇到相同逻辑,重复代码要复用的时候
  2. 如何封装一个函数呢?

    答案

    • 先明确要完成的需求,以及需要的参数,再来实现其中的细节,然后在需要的地方调用
  3. 哪个事件能实时检测到输入框值的变化?

    A:input 事件

    B:change 事件
    答案

    • 选A

项目实战注意点

token

概念:访问权限的令牌,本质上是一串字符串

创建:正确登录后,由后端签发并返回

作用:判断是否有登录状态等,控制访问权限

注意:前端只能判断 token 有无,而后端通过解密可以提取 token 字符串的原始信息,判断有效性

token 的使用:只有登录状态,才可以访问内容页面

axios 请求拦截器和响应拦截器

axios 请求拦截器

axios 请求拦截器:发起请求之前,触发的配置函数,对请求参数进行额外配置

axios 请求拦截器,什么时候使用?

  • 有公共配置和设置时,统一设置在请求拦截器中

场景:

axios 响应拦截器

axios 响应拦截器:响应回到 then/catch 之前,触发的拦截函数,对响应结果统一处理

例如:身份验证失败,统一判断并做处理

axios 响应拦截器,什么时候触发成功/失败的回调函数?

  • 状态为 2xx 触发成功回调,其他则触发失败的回调函数

富文本编辑器

wangEditor 插件

Node.js

一. Node.js入门

1. Node.js 简介

什么是 Node.js

什么是 Node.js,有什么用,为何能独立执行 JS 代码

定义:Node.js 是一个跨平台的 JavaScript 运行环境,使开发者可以搭建服务器端的 JavaScript 应用程序。

作用:编写服务器端程序(也可以对前端代码进行压缩,转译,整合等等,提高前端开发和运行效率)

  • 编写数据接口,提供网页资源浏览功能等等
  • 前端工程化:为后续学习 Vue 和 React 等框架做铺垫
什么是前端工程化?
  • 前端工程化:开发项目直到上线,过程中集成的所有工具和技术(前端代码压缩,整合,转译,测试,自动部署等等工具的集成统称,为了提高前端开发项目的效率)(对前端代码进行优化,集成各种提高前端开发效率的工具等)
  • Node.js 是前端工程化的基础(因为 Node.js 可以主动读取前端代码内容)
Node.js 为何能执行 JS?

首先:浏览器能执行 JS 代码,依靠的是内核中的 V8 引擎(C++ 程序)

其次:Node.js 是基于 Chrome V8 引擎进行封装(运行环境)

区别:都支持 ECMAScript 标准语法,Node.js 有独立的 API,但是语法和浏览器环境的 V8 有所不同,没有 DOM 和 BOM 等(document 和 window)

注意:Node.js 环境没有 DOM 和 BOM 等

Node.js 安装

要求:下载 node-v16.19.0.msi 安装程序(指定版本:兼容 vue-admin-template 模板)

安装过程:默认下一步即可

注释事项:

  1. 安装在非中文路径下

  2. 无需勾选自动安装其他配套软件

成功验证:

  1. 打开 cmd 终端,输入 node -v 命令查看版本号

  2. 如果有显示,则代表安装成功

Node.js 没有图形化界面,需要使用 cmd 终端命令行(利用一些命令来操控电脑执行某些程序软件)输入

使用 Node.js

需求:新建 JS 文件,并编写代码后,在 node 环境下执行

命令:在 VSCode 集成终端中,输入 node xxx.js,回车即可执行

小结
  1. Node.js 是什么?

    答案

    • 基于 Chrome 的 V8 引擎封装,独立执行 JavaScript 代码的环境
  2. Node.js 有什么用?

    答案

    • 编写后端程序:提供数据和网页资源等
    • 前端工程化:集成各种开发中使用的工具和技术(如翻译压缩整合代码等,提高开发效率)
  3. Node.js 为何能执行 JS 代码?

    答案

    • 基于 Chrome 的 V8 引擎封装
  4. Node.js 与浏览器环境的 JS 最大区别?

    答案

    • Node.js 环境中没有 BOM 和 DOM,但是也用 JS 语法
  5. Node.js 如何执行代码?

    答案

    • 在 VSCode 终端中输入:node xxx.js 回车即可执行(注意路径)

2. fs模块-读写文件

了解模块概念,使用 fs 模块封装的方法读写文件内容

模块:类似插件,封装了方法/属性

fs 模块:封装了与本机文件系统进行交互的,方法和属性

fs 模块使用语法如下:

  • 加载 fs 模块,得到 fs 对象

    js 复制代码
    const fs = require('fs')
  • 写入文件内容语法:

    js 复制代码
    //注意:文件可以没有,但是写入文件的文件夹要有
    fs.writeFile('文件路径', '写入内容', err => {
      // 写入后的回调函数
    })
  • 读取文件内容的语法:

    js 复制代码
    fs.readFile('文件路径', (err, data) => {
      // 读取后的回调函数
      // data 是文件内容的 Buffer 数据流
    })

向 test.txt 文件写入内容并读取打印

js 复制代码
/**
 * 目标:使用 fs 模块,读写文件内容
 * 语法:
 * 1. 引入 fs 模块
 * 2. 调用 writeFile 写入内容
 * 3. 调用 readFile  读取内容
 */
// 1. 引入 fs 模块
const fs = require('fs')
// 2. 调用 writeFile 写入内容
// 注意:建议写入字符串内容,会覆盖目标文件所有内容
fs.writeFile('./text.txt', '欢迎使用 fs 模块读写文件内容', err => {
  if (err) console.log(err)
  else console.log('写入成功')
})
// 3. 调用 readFile  读取内容
fs.readFile('./text.txt', (err, data) => {
  if (err) console.log(err)
  else console.log(data.toString()) // 把 Buffer 数据流转成字符串类型
})

3. path模块-路径处理

使用 path 模块来得到绝对路径

问题:Node.js 代码中,相对路径是根据终端所在路径来查找的,可能无法找到你想要的文件

举例

如果终端是要执行文件所在文件夹,node执行当前js文件路径没问题;但是,如果终端不是要执行文件所在文件夹,js文件里面使用相对路径,就会导致找不到文件

建议:在 Node.js 代码中,使用绝对路径

  1. 为什么在 Node.js 待执行的 JS 代码中要用绝对路径:

    Node.js 执行 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
  • 在 windows 和 mac 地址中,分隔符不一样

注意:path.join() 会按照所在本机系统的分隔符作为定界符来链接你传入的路径

总结:使用模块内置变量 __dirname配合 path.join() 来得到绝对路径使用

语法:

  1. 加载 path 模块

    const path = require('path)

  2. 使用 path.join 方法,拼接路径

    path.join('路径1', '路径2',...)

    js 复制代码
    const fs = require('fs')
    console.log(__dirname) // D:\备课代码\2_node_3天\Node_代码\Day01_Node.js入门\代码\03
    
    // 1. 加载 path 模块
    const path = require('path')
    // 2. 使用 path.join() 来拼接路径
    // const pathStr = path.join(__dirname, '../text.txt')
    const pathStr = path.join(__dirname, '..', 'text.txt')
    console.log(pathStr)
    
    fs.readFile(pathStr, (err, data) => {
      if (err) console.log(err)
      else console.log(data.toString())
    })

4. 案例-压缩前端html

需求:把准备好的 html 文件里的回车符(\r)和换行符(\n)去掉进行压缩,写入到新 html 中

步骤:

  1. 读取源 html 文件内容
  2. 正则替换字符串
  3. 写入到新的 html 文件中,并运行查看是否能正常打开网页

代码实现如下:

js 复制代码
/**
 * 目标一:压缩 html 里代码
 * 需求:把 public/index.html 里的,回车/换行符去掉,写入到 dist/index.html 中
 *  1.1 读取 public/index.html 内容
 *  1.2 使用正则替换内容字符串里的,回车符\r 换行符\n
 *  1.3 确认后,写入到 dist/index.html 内
 */
const fs = require('fs')
const path = require('path')
// 1.1 读取 public/index.html 内容
fs.readFile(path.join(__dirname, 'public', 'index.html'), (err, data) => {
  const htmlStr = data.toString()
  // 1.2 使用正则替换内容字符串里的,回车符\r 换行符\n
  const resultStr = htmlStr.replace(/[\r\n]/g, '')
  // 1.3 确认后,写入到 dist/index.html 内
  fs.writeFile(path.join(__dirname, 'dist', 'index.html'), resultStr, err => {
    if (err) console.log(err)
    else console.log('压缩成功')
  })
})

5. 认识URL中的端口号

认识 URL 中端口号的作用,以及 Web 服务的作用

URL 统一资源定位符,简称网址,用于访问网络上的资源

端口号的作用:标记服务器里不同的服务程序(端口号范围:0-65535 之间的任意整数)

常见的服务程序

  • Web服务程序:用于提供网上信息浏览的程序代码
  • 注意:0-1023 和一些特定的端口号被占用,我们自己编写服务程序请避开使用

6. http模块-创建Web服务

基于 Node.js 环境,使用内置 http 模块,创建 Web 服务程序

需求:引入 http 模块,使用相关语法,创建 Web 服务程序,响应返回给请求方一句提示 'hello,world'

步骤:

  1. 引载 http 模块,创建 Web 服务对象
  2. 监听 request 请求事件,设置响应头和响应体
  3. 启动 Web 服务监听对应端口号(配置端口号并启动 Web 服务)
  4. 运行本服务在终端进程中,用浏览器发起请求(浏览器请求 http://localhost:3000 测试)

注意:本机的域名叫做 localhost

代码如下:

js 复制代码
/**
 * 目标:基于 http 模块创建 Web 服务程序
 *  1.1 加载 http 模块,创建 Web 服务对象
 *  1.2 监听 request 请求事件,设置响应头和响应体
 *  1.3 配置端口号并启动 Web 服务
 *  1.4 浏览器请求(http://localhost:3000)测试
 */
// 1.1 加载 http 模块,创建 Web 服务对象
const http = require('http')
const server = http.createServer()
// 1.2 监听 request 请求事件,设置响应头和响应体
server.on('request', (req, res) => {
  // 设置响应头-内容类型-普通文本以及中文编码格式
  res.setHeader('Content-Type', 'text/plain;charset=utf-8')
  // 设置响应体内容,结束本次请求与响应
  res.end('欢迎使用 Node.js 和 http 模块创建的 Web 服务')
})
// 1.3 配置端口号并启动 Web 服务
server.listen(3000, () => {
  console.log('Web 服务启动成功了')
})

小结

  1. 如何访问本机里运行的 Web 服务?

    答案

7. 案例-浏览时钟

体验 Web 服务除了接口数据以外,还能返回网页资源等

需求:基于 Web 服务,开发提供网页资源的功能,了解下后端的代码工作过程

步骤:

  1. 基于 http 模块,创建 Web 服务
  2. 使用 req.url 获取请求资源路径为 /index.html 的时候,读取 index.html 文件内容字符串返回给请求方
  3. 其他路径,暂时返回不存在的提示
  4. 运行 Web 服务,用浏览器发起请求

代码如下:

js 复制代码
/**
 * 目标:编写 web 服务,监听请求的是 /index.html 路径的时候,返回 dist/index.html 时钟案例页面内容
 * 步骤:
 *  1. 基于 http 模块,创建 Web 服务
 *  2. 使用 req.url 获取请求资源路径,并读取 index.html 里字符串内容返回给请求方
 *  3. 其他路径,暂时返回不存在提示
 *  4. 运行 Web 服务,用浏览器发起请求
 */
const fs = require('fs')
const path = require('path')
// 1. 基于 http 模块,创建 Web 服务
const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
  // 2. 使用 req.url 获取请求资源路径,并读取 index.html 里字符串内容返回给请求方
  if (req.url === '/index.html') {
    fs.readFile(path.join(__dirname, 'dist/index.html'), (err, data) => {
      res.setHeader('Content-Type', 'text/html;charset=utf-8')
      res.end(data.toString())
    })
  } else {
    // 3. 其他路径,暂时返回不存在提示
    res.setHeader('Content-Type', 'text/html;charset=utf-8')
    res.end('你要访问的资源路径不存在')
  }
})
server.listen(8080, () => {
  console.log('Web 服务启动了')
})

小结

  1. Web 服务程序都有什么功能?

    答案

    • 提供数据和网页资源等等功能,其他它的功能远不止于此

二. Node.js模块化

1. 模块化简介

了解模块化概念和好处,以及 CommonJS 标准语法导出和导入

定义:在 Node.js 中,每个文件都视为一个单独的模块。

概念:项目是由多个模块文件组成的

好处:提高代码复用性,按需加载,独立作用域

使用:需要标准语法导出和导入才进行使用

  • 因为模块内的属性和函数都是私有的,如果对外使用,需要使用标准语法导出和导入才可以,而这个标准叫 CommonJS 标准
CommonJS 标准

需求:定义 utils.js 模块,封装基地址和求数组总和的函数,导入到 index.js

使用:

  1. 导出语法:

    js 复制代码
    module.exports = {
      对外属性名: 模块内私有变量
    }
  2. 导入语法:

    js 复制代码
    const 变量名 = require('模块名或路径')
    // Node.js 环境内置模块直接写模块名(例如:fs,path,http)
    // 自定义模块:写模块文件路径(例如:./utils.js)

    变量名的值接收的就是目标模块导出的对象

模块名或路径:

  • 内置模块:直接写名字(例如:fs,path,http)
  • 自定义模块:写模块文件路径(例如:./utils.js)

代码实现

  • utils.js:导出

    js 复制代码
    /**
     * 目标:基于 CommonJS 标准语法,封装属性和方法并导出
     */
    const baseURL = 'http://hmajax.itheima.net'
    const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
    
    // 导出
    module.exports = {
      url: baseURL,
      arraySum: getArraySum
    }
  • index.js:导入使用

    js 复制代码
    /**
     * 目标:基于 CommonJS 标准语法,导入工具属性和方法使用
     */
    // 导入
    const obj = require('./utils.js')
    console.log(obj)
    const result = obj.arraySum([5, 1, 2, 3])
    console.log(result)
小结
  1. Node.js 中什么是模块化?

    答案

    • 每个文件都是独立的模块
  2. 模块之间如何联系呢?

    答案

    • 使用特定语法,导出和导入使用
  3. CommonJS 标准规定如何导出和导入模块?

    答案

    • 导出:module.exports = {},
    • 导入:require('模块名或路径')
  4. 模块名/路径如何选择?

    答案

    • 内置模块:直接写名字。例如:fs,path,http等。
    • 自定义模块:写模块文件路径,例如:./utils.js

2. ECMAScript标准-默认导出和导入

掌握 ECMAScript 标准语法中,默认导出和导入的使用

CommonJS 规范是 Node.js 环境中默认的,后来官方推出 ECMAScript 标准语法,我们接下来在一个需求中,体验下这个标准中默认导出和导入的语法要如何使用

需求:封装并导出基地址和求数组元素和的函数

默认标准使用:

  • 导出语法:

    js 复制代码
    export default {
      对外属性名: 模块内私有变量
    }
  • 导入语法:

    js 复制代码
    import 变量名 from '模块名或路径'

注意:Node.js 默认只支持 CommonJS 标准语法,如果想要在当前项目环境下使用 ECMAScript 标准语法,请新建 package.json 文件并设置 type: 'module'

package.json

json 复制代码
{ "type": "module" }

代码实现:

  • utils.js:导出

    js 复制代码
    /**
     * 目标:基于 ECMAScript 标准语法,封装属性和方法并"默认"导出
     */
    const baseURL = 'http://hmajax.itheima.net'
    const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
    
    // 默认导出
    export default {
      url: baseURL,
      arraySum: getArraySum
    }
  • index.js:导入

    js 复制代码
    /**
     * 目标:基于 ECMAScript 标准语法,"默认"导入,工具属性和方法使用
     */
    // 默认导入
    import obj from './utils.js'
    console.log(obj)
    const result = obj.arraySum([10, 20, 30])
    console.log(result)
小结
  1. ECMAScript 标准规定如何默认导出和导入模块?

    答案

    • 导出:export default {}
    • 导入:import 变量名 from '模块名或路径'
  2. 如何让 Node.js 切换模块标准为 ECMAScript?

    答案

    • 运行模块所在文件夹,新建 package.json 并设置 {"type":"module"}

3. ECMAScript标准-命名导出和导入

掌握 ECMAScript 标准语法中,命名导出和导入的使用

ECMAScript 标准的语法有很多,常用的就是默认和命名导出和导入,这节课我们来学习下命名导出和导入的使用

需求:封装并导出基地址和数组求和函数

命名标准使用:

  • 命名导出语法:

    js 复制代码
    export 修饰定义语句
  • 命名导入语法:

    js 复制代码
    import { 同名变量 } from '模块名或路径'

代码示例:

  • utils.js 导出

    js 复制代码
    /**
     * 目标:基于 ECMAScript 标准语法,封装属性和方法并"命名"导出
     */
    export const baseURL = 'http://hmajax.itheima.net'
    export const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0)
  • index.js 导入

    js 复制代码
    /**
     * 目标:基于 ECMAScript 标准语法,"命名"导入,工具属性和方法使用
     */
    // 命名导入
    import {baseURL, getArraySum} from './utils.js'
    console.log(obj)
    console.log(baseURL)
    console.log(getArraySum)
    const result = getArraySum([10, 21, 33])
    console.log(result)

与默认导出如何选择:

markdown 复制代码
*   按需加载,使用命名导出和导入
*   全部加载,使用默认导出和导入
小结
  1. Node.js 支持哪 2 种模块化标准?

    答案

    • CommonJS 标准语法(默认)
    • ECMAScript 标准语法
  2. ECMAScript 标准,命名导出和导入的语法?

    答案

    • 导出:export 修饰定义的语句
    • 导入:import { 同名变量 } from '模块名或路径'
  3. ECMAScript 标准,默认导出和导入的语法?

    答案

    • 导出:export default {}
    • 导入:import 变量名 from '模块名或路径'
  4. ECMAScript 标准,命名导出和与默认导出如何选择?

    答案

    • 按需加载,使用命名导出和导入
    • 全部加载,使用默认导出和导入

4.包的概念

包:将模块,代码,其他资料整合成一个文件夹,这个文件夹就叫包

包分类:

  • 项目包:主要用于编写项目和业务逻辑
  • 软件包:封装工具和方法进行使用

包要求:根目录中,必须有 package.json 文件(记录包的清单信息)

包使用:导入软件包时,引入的默认是 index.js 模块文件 / main 属性指定的模块文件(在引入一个包文件夹到代码中,默认引入的是包文件节下的 index.js 模块文件里导出的对象,如果没有 index.js 文件,则会引入 package.json 里 main 属性指定的文件模块导出的对象)

需求:封装数组求和函数的模块,封装判断用户名和密码长度函数的模块,形成一个软件包

代码示例:

js 复制代码
/**
 * 本文件是,utils 工具包的唯一出口
 * 作用:把所有工具模块方法集中起来,统一向外暴露
 */
const { getArraySum } = require('./lib/arr.js')
const { checkUser, checkPwd } = require('./lib/str.js')

// 统一导出所有函数
module.exports = {
  getArraySum,
  checkUser,
  checkPwd
}
  • index.js 导入软件包文件夹使用(注意:这次导入的是包文件夹,不是模块文件)
js 复制代码
/**
 * 目标:导入 utils 软件包,使用里面封装的工具函数
 */
const obj = require('./utils')
console.log(obj)
const result = obj.getArraySum([10, 20, 30])
console.log(result)
小结
  1. 什么是包?

    答案

    • 将模块,代码,其他资料聚合成的文件夹
  2. 包分为哪 2 类呢?

    答案

    • 项目包:编写项目代码的文件夹,
    • 软件包:封装工具和方法供开发者使用
  3. package.json 文件的作用?

    答案

    • 记录软件包的名字,作者,入口文件等信息
  4. 导入一个包文件夹的时候,导入的是哪个文件?

    答案

    • 默认 index.js 文件,或者 package.json里面 main 属性指定的文件

5. npm软件包管理器

掌握使用 npm 管理软件包

npm 简介链接: 是 Node.js 标准的软件包管理器,用于下载和管理 Node.js 环境中的软件包

npm 使用步骤:

  1. 初始化清单文件: npm init -y (得到 package.json 文件,有则跳过此命令)

    注意 -y 就是所有选项用默认值,所在文件夹不要有中文/特殊符号,建议英文和数字组成,因为 npm 包名限制建议用英文和数字或者下划线中划线

  2. 下载软件包:npm i 软件包名称

  3. 使用软件包

小结
  1. npm 软件包管理器作用?

    答案

    • 下载软件包以及管理版本
  2. 初始化项目清单文件 package.json 命令?

    答案

    • npm init -y
  3. 下载软件包的命令?

    答案

    • npm i 软件包名字
  4. 下载的包会存放在哪里?

    答案

    • 当前项目下的 node_modules 中,并记录在 package.json 中

6. npm安装所有依赖

问题:我们拿到了一个别人编写的项目,但是没有 node_modules,项目能否正确运行?

答案:不能,因为缺少了项目需要的依赖软件包,比如要使用 dayjs 和 lodash 但是你项目里没有这个对应的源码,项目会报错的

为何没有给我 node_modules?

  • 因为每个人在自己的本机使用 npm 下载,要比磁盘间传递要快(npm 有缓存在本机)

如何得到需要的所有依赖软件包呢?

  • 直接在项目终端运行命令:npm i 即可安装 package.json 里记录的所有包和对应版本到本项目中的 node_modules
小结
  1. 当前项目中只有 package.json 没有 node_modules 怎么办?

    答案

    • 当前项目目录下,打开终端,执行 npm i 安装所有依赖软件包
  2. 为什么 node_modules 不进行传递?

    答案

    • 因为用 npm 下载有缓存在本机,比磁盘之间传递要快

7. npm全局软件包-nodemon

掌握本地软件包和全局软件包区别,体验 nodemon 的使用

软件包区别:

  • 本地软件包:当前项目内使用,封装属性和方法,存在于 node_modules
  • 全局软件包:本机所有项目使用,封装命令和工具,存在于系统设置的位置

nodemon 作用:替代 node 命令,检测代码更改,自动重启程序

使用:

  1. 安装:npm i nodemon -g (-g 代表安装到全局环境中)
  2. 运行:nodemon 待执行的目标 js 文件
小结
  1. 本地软件包和全局软件包区别?

    答案

    • 本地软件包,作用在当前项目,封装属性和方法
    • 全局软件包,本地所有项目使用,封装命令和工具
  2. nodemon 作用?

    答案

    • 替代 node 命令,检测代码更改,自动重启程序
  3. nodemon 怎么用?

    答案

    • 先确保安装 npm i nodemon -g
    • 使用 nodemon 执行目标 js 文件

8. Node.js概念和常用命令总结

把上面学的模块化语法,包的概念,常用命令进行总结

Node.js 模块化

  • 概念:把每个文件当做一个模块,独立作用域,按需加载

  • 使用:使用特定的标准语法导出和导入使用

    CommonJS 标准:一般应用在 Node.js 项目环境中

    ECMAScript 标准:一般应用在前端工程化项目中

Node.js 包

  • 概念:把模块文件,代码文件,其他资料聚合成一个文件夹就是包

    项目包:编写项目需求和业务逻辑的文件夹

    软件包:封装工具和方法进行使用的文件夹(一般使用 npm 管理)

    • 本地软件包:作用在当前项目,封装的属性/方法,供项目调用编写业务需求
    • 全局软件包:作用在所有项目,一般封装的命令/工具,支撑项目运行

Node.js 常用命令

小结
  1. 安装本地软件包的命令是什么?

    答案

    • npm i 软件包名

3. Webpack模块打包工具

1. Webpack 简介以及体验

了解 Webpack 的概念和作用,以及使用

定义:Webpack 是一个静态模块打包工具,从入口构建依赖图,打包有关的模块,最后用于展示你的内容

静态模块:编写代码过程中的 html,css, js,图片等固定内容的文件

打包过程,注意:只有和入口有直接/间接引入关系的模块,才会被打包

Webpack 的作用/打包:把静态模块内容,压缩,整合,转译等(前端工程化)

  • 把 less/sass 转成 css 代码
  • 把 ES6+ 降级成 ES5 等
  • 支持多种模块标准语法

为何不学 vite?

  • 现在很多项目还是基于 Webpack 来进行构建的,所以还是要掌握 Webpack 的使用

需求:封装 utils 包,校验手机号和验证码长度,在 src/index.js 中使用,使用 Webpack 打包

步骤:

  1. 新建并初始化项目:新建项目文件夹 Webpack_study,初始化包环境,得到 package.json 文件

    bash 复制代码
    npm init -y
  2. 编写业务源代码:新建 src 源代码文件夹(书写代码)包括 utils/check.js 封装用户名和密码长度函数,引入到 src/index.js 进行使用

    • src/utils/check.js

      js 复制代码
      // 封装校验手机号长度和校验验证码长度的函数
      export const checkPhone = phone => phone.length === 11
      export const checkCode = code => code.length === 6
    • src/index.js

      js 复制代码
      /**
       * 目标1:体验 webpack 打包过程
       */
      // 1.1 准备项目和源代码
      import { checkPhone, checkCode } from './utils/check.js'
      console.log(checkPhone('13900002020'))
      console.log(checkCode('123123123123'))
      // 1.2 准备 webpack 打包的环境
      // 1.3 运行自定义命令打包观察效果(npm run 自定义命令)
  3. 下载 webpack webpack-cli 到当前项目中(版本独立),并配置局部自定义命令

    bash 复制代码
    npm i webpack webpack-cli --save-dev
    js 复制代码
    //package.json中配置 build自定义命令
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack"
      },

    注意:虽然 webpack 是全局软件包,封装的是命令工具,但是为了保证项目之间版本分别独立,所以这次比较特殊,下载到某个项目环境下,但是需要把 webpack 命令配置到 package.json 的 scripts 自定义命令,作为局部命令使用

  4. 运行打包命令:项目中运行工具命令,采用自定义命令的方式(局部命令)

    bash 复制代码
    npm run build

    npm run 自定义命令名字

    注意:实际上在终端运行的是 build 右侧的具体命名
    注意:Node.js 默认只支持 CommonJS 标准语法,想要在当前项目环境下使用 ECMAScript 标准语法(import/export),需要在 package.json 文件设置 type: 'module'(注意浏览器也不支持ES6模块化的js代码,打包后的代码才能在浏览器上运行)

    从 webpack2 开始,已经内置了对 ES6、CommonJS、AMD 模块化语句的支持。但不包括新的ES6语法转为ES5代码,这部分工作还是留给了babel及其插件。

    webpack对于ES模块/CommonJS模块的实现,是基于自己实现的webpack_require,所以打包后的代码能跑在浏览器中。

    webpack根据webpack.config.js(wepack的配置文件,下一小节就会提到,目前用的是默认配置)中的入口文件,在入口文件里识别模块依赖,不管这里的模块依赖是用CommonJS写的,还是ES6 Module规范写的,webpack会自动进行分析,并通过转换、编译代码,打包成最终的文件。最终文件中的模块实现是基于webpack自己实现的webpack_require(es5代码),所以打包后的文件可以跑在浏览器上。

  5. 自动产生 dist 分发文件夹(压缩和优化后,用于最终运行的代码)

需求最终流程图:

小结

  1. Webpack 有什么用?

    答案

    • 压缩,转译,整合,打包我们的静态模块
  2. Webpack 怎么用?

    答案

    • 初始化环境,编写代码,安装 Webpack 软件包,配置自定义命令,打包体验查看结果
  3. 如何运行 package.json 里的自定义命令?

    答案

    • npm run 自定义命令
  4. Webpack 默认入口和出口?

    答案

    • src/index.js 和 dist/main.js

2. Webpack 修改打包入口和出口

了解 Webpack 配置文件使用,影响 Webpack 打包过程和结果

Webpack 配置:影响 Webpack 打包过程和结果

步骤:

  1. 项目根目录,新建 webpack.config.js 配置文件

  2. 导出配置对象,配置入口,出口文件路径(别忘了修改磁盘文件夹和文件的名字)

    js 复制代码
    const path = require('path')
    
    module.exports = {
      entry: path.resolve(__dirname, 'src/login/index.js'),
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: './login/index.js', //注意:这里是login文件夹下的index.js
        clean: true // 生成打包后内容之前,清空输出目录
      }
    }
  3. 重新打包观察

注意:只有和入口产生直接/间接的引入关系,才会被打包

小结

  1. 如何影响 Webpack 打包过程?

    答案

    • 查文档,新建配置文件和配置属性

3. 案例-用户登录-长度判断

体验前端项目代码,如何被 Webpack 打包和使用

需求:点击登录按钮,判断手机号和验证码长度是否符合要求

步骤:

  1. 准备用户登录页面:新建 public/login.html 准备网页模板(方便查找标签和后期自动生成 html 文件做准备)

  2. 编写核心 JS 逻辑代码:核心 JS 代码写在 src/login/index.js 文件

    js 复制代码
    /**
     * 目标3:用户登录-长度判断案例
     *  3.1 准备用户登录页面
     *  3.2 编写核心 JS 逻辑代码
     *  3.3 打包并手动复制网页到 dist 下,引入打包后的 js,运行
     */
    // 3.2 编写核心 JS 逻辑代码
    document.querySelector('.btn').addEventListener('click', () => {
      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
      }
    
      console.log('提交到服务器登录...')
    })
  3. 打包并手动复制网页到 dist 下,引入打包后的 js,运行:运行自定义命令,让 webpack 打包 JS 代码;手动复制 public/login.html 到 dist 下,手动引入打包后的 JS 代码文件,运行 dist/login.html 在浏览器查看效果

    注意:webpack默认只能处理 jsjson 资源,所以这里需要将打包后的js代码引入html里面查看效果,后续可以使用webpack插件让其支持更多类型

核心:Webpack 打包后的代码,作用在前端网页中使用

小结

  1. Webpack 打包后的前端代码是如何运行的?

    答案

    • 手动引入到 html 文件中,再交给浏览器运行

4. Webpack 自动生成 html 文件

让 Webpack 拥有自动生成 html 文件能力,并引入打包后的其他资源

插件 html-webpack-plugin 作用:在 Webpack 打包时生成 html 文件,并引入其他打包后的资源

步骤:

  1. 下载 html-webpack-plugin 本地软件包到项目中

    bash 复制代码
    npm i html-webpack-plugin --save-dev
  2. 配置 webpack.config.js 让 Webpack 拥有插件功能

    js 复制代码
    // ...
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      // 插件(给webpack提供更多功能)
      plugins: [
        //注意:output生成的js文件会自动加到html里面
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, "public/login.html"), // 模板文件
          filename: path.resolve(__dirname, "dist/login/index.html") // 输出文件,注意最好以output.path开头
        })
      ]
    }
  3. 重新打包观察效果:指定以 public/login.html 为模板复制到 dist/login/index.html,并自动引入其他打包后资源;运行打包命令,观察打包后 dist 文件夹下内容并运行查看效果

小结

  1. html-webpack-plugin 插件怎么用?

    答案

    • 找到插件文档,下载到项目中,配置到 Webpack 的配置文件中即可使用

5. Webpack-打包 css 代码

注意:Webpack 默认只识别 JS 和 JSON 文件内容,所以想要让 Webpack 识别更多不同内容,需要使用加载器

介绍需要的 2 个加载器来辅助 Webpack 才能打包 css 代码

步骤:

  1. 准备 css 文件代码引入到 src/login/index.js 中(压缩转译处理等)

    js 复制代码
    /**
     * 目标5:打包 css 代码
     *  5.1 准备 css 代码,并引入到 js 中
     *  5.2 下载 css-loader 和 style-loader 本地软件包
     *  5.3 配置 webpack.config.js 让 Webpack 拥有该加载器功能
     *  5.4 打包后观察效果
     */
    // 5.1 准备 css 代码,并引入到 js 中
    import 'bootstrap/dist/css/bootstrap.min.css'
    import './index.css'

    注意:这里只是引入代码内容让 Webpack 处理,不需定义变量接收在 JS 代码中继续使用,所以没有定义变量接收

  2. 下载 css-loader 和 style-loader 本地软件包

    bash 复制代码
    npm i css-loader style-loader --save-dev
  3. 配置 webpack.config.js 让 Webpack 拥有该加载器功能

    js 复制代码
    // ...
    
    module.exports = {
      // ...
      module: { // 加载器
        rules: [ // 规则列表
          {
            test: /\.css$/i, // 匹配 .css 结尾的文件
            use: ['style-loader', 'css-loader'], // 使用从后到前的加载器来解析 css 代码和插入到 DOM
          }
        ]
      }
    };
  4. 打包后观察效果:打包后运行 dist/login/index.html 观察效果,看看准备好的样式是否作用在网页上

小结

  1. 加载器的作用是什么?

    答案

    • 让 Webpack 识别更多的代码内容类型

6. 优化-提取 css 代码

让 Webpack 能够提取 css 代码到独立的 css 文件中

需求:让 webpack 把 css 代码内容字符串单独提取到 dist 下的 css 文件中

需要:mini-css-extract-plugin 插件来实现

插件 mini-css-extract-plugin:提取 css 代码

步骤:

  1. 下载 mini-css-extract-plugin 插件软件包到本地项目中

    bash 复制代码
    npm i --save-dev mini-css-extract-plugin
  2. 配置 webpack.config.js 让 Webpack 拥有该插件功能

    js 复制代码
    // ...
    const MiniCssExtractPlugin = require("mini-css-extract-plugin")
    
    module.exports = {
      // ...
      module: {
        rules: [
          {
            test: /\.css$/i,
            // use: ['style-loader', 'css-loader']
            use: [MiniCssExtractPlugin.loader, "css-loader"],
          },
        ],
      },
      plugins: [
        // ...
        new MiniCssExtractPlugin()
      ]
    };
  3. 打包后观察效果

注意:不能和 style-loader 一起使用

好处:css 文件可以被浏览器缓存,减少 JS 文件体积,让浏览器并行下载 css 和 js 文件

注意:

js 复制代码
plugins: [
    // 不设置输出路径,默认是 dist/main.css
    new MiniCssExtractPlugin()
]
js 复制代码
plugins: [
    // 若设置输出路径,需要是相对路径,否则打包时会报如下所示错误
    //相对路径,是相对dist下的路径
    new MiniCssExtractPlugin({
      filename: "./login/index.css"
    })
]

7. 优化-压缩过程

把单独提取的 css 文件内代码压缩

问题:css 代码提取后没有压缩

解决:使用 css-minimizer-webpack-plugin 插件

步骤:

  1. 下载 css-minimizer-webpack-plugin 插件软件包到本地项目中

    bash 复制代码
    npm i css-minimizer-webpack-plugin --save-dev 
  2. 配置 webpack.config.js 让 Webpack 拥有该插件功能

    js 复制代码
    // ...
    const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
    
    module.exports = {
      // ...
      // 优化
      optimization: {
        // 最小化
        minimizer: [
          // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 
          // `terser-webpack-plugin`),将下一行取消注释(保证 JS 代码还能被压缩处理)
          `...`,
          new CssMinimizerPlugin(),
        ],
      }
    };
  3. 打包后观察 css 文件内自己代码是否被压缩了

8. Webpack-打包 less 代码

加载器 less-loader:把 less 代码编译为 css 代码

注意:less-loader 需要配合 less 软件包使用

步骤:

  1. 新建 login/index.less 文件,设置背景图样式

    less 复制代码
    html {
      body {
        background: url('./assets/login-bg.png') no-repeat center/cover;
      }
    }
  2. less 样式引入到 src/login/index.js 中

js 复制代码
/**
 * 目标:打包 less 代码
 *  8.1 新建 less 代码(设置背景图)并引入到 src/login/index.js 中
 *  8.2 下载 less 和 less-loader 本地软件包
 *  8.3 配置 webpack.config.js 让 Webpack 拥有功能
 *  8.4 打包后观察效果
 */
// 8.1 新建 less 代码(设置背景图)并引入到 src/login/index.js 中
import './index.less'
  1. 下载 less 和 less-loader 本地软件包

    bash 复制代码
    npm i less less-loader --save-dev
  2. 配置 webpack.config.js 让 Webpack 拥有功能

    js 复制代码
    // ...
    
    module.exports = {
      // ...
      module: {
        rules: [
          // ...
          {
            test: /\.less$/i,
            use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"]
          }
        ]
      }
    }
  3. 打包后运行 观察效果

小结

  1. Webpack 支持 less 代码打包需要哪 2 个软件包?

    答案

    • 需要 less less-loader 这 2 个软件包

9. Webpack-打包图片

资源模块:Webpack5 内置资源模块(字体,图片等)打包,无需额外 loader

步骤:

  1. 配置 webpack.config.js 让 Webpack 拥有打包图片功能

    占位符 【hash】对模块内容做算法计算,得到映射的数字字母组合的字符串

    占位符 【ext】使用当前模块原本的占位符,例如:.png / .jpg 等字符串

    占位符 【query】保留引入文件时代码中查询参数(只有 URL 下生效)

  2. 打包后观察效果和区别

    注意:判断临界值默认为 8KB

    大于 8KB 文件:发送一个单独的文件并导出 URL 地址

    小于 8KB 文件:导出一个 data URI(base64字符串)

  3. 在 src/login/index.js 中给 img 标签添加 logo 图片

    js 复制代码
    /**
     * 目标9:打包资源模块(图片处理)
     *  9.1 创建 img 标签并动态添加到页面,配置 webpack.config.js
     *  9.2 打包后观察效果和区别
     */
    // 9.1 创建 img 标签并动态添加到页面,配置 webpack.config.js
    // 注意:js 中引入本地图片资源要用 import 方式(如果是网络图片http地址,字符串可以直接写)
    import imgObj from './assets/logo.png'
    const theImg = document.createElement('img')
    theImg.src = imgObj
    document.querySelector('.login-wrap').appendChild(theImg)
  4. 配置 webpack.config.js 让 Webpack 拥有打包图片功能

    js 复制代码
    // ...
    
    module.exports = {
      // ...
      module: {
        rules: [
          // ...
          {
            test: /\.(png|jpg|jpeg|gif)$/i,
            type: 'asset',
            generator: {
              filename: 'assets/[hash][ext][query]'
            }
          }
        ]
      }
    }

小结

  1. 资源模块指的是什么?

    答案

    • 图片,字体文件等等

10.案例-用户登录-完成功能

在 Webpack 环境下,使用 npm 下包作用在前端项目

  1. 需求:点击登录按钮,基于 npm 下载 axios 包,完成验证码登录功能

  2. 步骤:

    1. 使用 npm 下载 axios

      bash 复制代码
      npm i axios
    2. 引入到 src/login/index.js 中编写业务实现

      js 复制代码
      /**
       * 目标10:完成登录功能
       *  10.1 使用 npm 下载 axios(体验 npm 作用在前端项目中)
       *  10.2 准备并修改 utils 工具包源代码导出实现函数
       *  10.3 导入并编写逻辑代码,打包后运行观察效果
       */
      // 10.3 导入并编写逻辑代码,打包后运行观察效果
      import myAxios from '../utils/request.js'
      import { myAlert } from '../utils/alert.js'
      document.querySelector('.btn').addEventListener('click', () => {
        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
        }
      
        myAxios({
          url: '/v1_0/authorizations',
          method: 'POST',
          data: {
            mobile: phone,
            code: code
          }
        }).then(res => {
          myAlert(true, '登录成功')
          localStorage.setItem('token', res.data.token)
          location.href = '../content/index.html'
        }).catch(error => {
          myAlert(false, error.response.data.message)
        })
      })
    3. 打包后运行观察效果

小结

  1. npm 下载的包如何作用在前端项目上?

    答案

    • 被 Webpack 打包处理后,再引入到 html 文件中运行

10. Webpack 搭建开发环境

体验 webpack-dev-server 开发服务器,快速开发应用程序

问题:每次改动代码,都要重新打包,很麻烦,效率很低

开发环境:配置 webpack-dev-server 快速开发应用程序

作用:启动 Web 服务,打包输出源码在内存,并会自动检测代码变化,热更新到网页

步骤;

  1. 下载 webpack-dev-server 软件包到当前项目

    bash 复制代码
    npm i webpack-dev-server --save-dev
  2. 设置打包的模式为开发模式,并配置自定义命令

    js 复制代码
    // ...
    
    module.exports = {
      // ...
      mode: 'development'
    }
    json 复制代码
    "scripts": {
      // ...
      "dev": "webpack serve --open
      //"dev": "webpack serve --mode=development"
    },
  3. 使用 npm run dev 来启动开发服务器,访问提示的域名+端口号,在浏览器访问打包后的项目网页,修改代码后试试热更新效果

    在 js / css 文件中修改代码保存后,会实时反馈到浏览器

注意

  • webpack-dev-server 借助 http 模块创建 8080 默认 Web 服务

  • 默认以 public 文件夹作为服务器根目录

  • webpack-dev-server 根据配置,打包相关代码在内存当中,以 output.path 的值作为服务器根目录(所以可以直接自己拼接访问 dist 目录下内容)

小结

  1. webpack-dev-server 的作用?

    答案

    • 启动 Webpack 开发服务器,会启动一个 Web 服务,实时检测代码变化重新打包,并快速反应最新效果到浏览器页面上

11. Webpack 打包模式

了解不同打包模式对代码和环境的影响

  1. 打包模式:告知 Webpack 使用相应模式的内置优化

  2. 分类:

    模式名称 模式名字 特点 场景
    开发模式 development 调试代码,实时加载,模块热替换等 本地开发
    生产模式 production 压缩代码,资源优化,更轻量等 打包上线
  3. 如何设置影响 Webpack呢?

    • 方式1:在 webpack.config.js 配置文件设置 mode 选项

      js 复制代码
      // ...
      
      module.exports = {
        // ...
        mode: 'production'
      }
    • 方式2:在 package.json 命令行设置 mode 参数

      json 复制代码
      "scripts": {
        "build": "webpack --mode=production",
        "dev": "webpack serve --mode=development"
      },
  4. 注意:命令行设置的优先级高于配置文件中的,推荐用命令行设置

小结

  1. 两种模式的区别?

    答案

    • 开发模式注重代码热替换更快,让开发调试代码更便捷,生产模式注重项目体积更小,更轻量,适配不同的浏览器环境

12. Webpack 打包模式的应用

了解 Webpack 打包模式的应用

需求:在开发模式下用 style-loader 内嵌更快,在生产模式下提取 css 代码

  • 方案1:webpack.config.js 配置导出函数,但是局限性大(只接受 2 种模式)

  • 方案2:借助 cross-env (跨平台通用)包命令,设置参数区分环境

  • 方案3:配置不同的 webpack.config.js (适用多种模式差异性较大情况)

其中方案2 步骤:

  1. 下载 cross-env 软件包到当前项目
js 复制代码
npm i cross-env --save-dev
  1. 配置自定义命令,传入参数名和值(会绑定到 process.env 对象下)
json 复制代码
"scripts": {
    "build": "cross-env NODE_ENV=production webpack --mode=production",
    "dev": "cross-env NODE_ENV=development webpack serve --open --mode=development"
  },
  1. 在 webpack.config.js 区分不同环境使用不同配置
js 复制代码
module: {
    rules: [
      {
        test: /\.css$/i,
        // use: ['style-loader', "css-loader"],
        use: [process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader, "css-loader"]
      },
      {
        test: /\.less$/i,
        use: [
          // compiles Less to CSS
          process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader',
        ],
      }
    ],
  },
  1. 重新打包观察两种配置区别

13. Webpack 前端注入环境变量

前端项目中,开发模式下打印语句生效,生产模式下打印语句失效

需求:前端项目中,开发模式下打印语句生效,生产模式下打印语句失效

问题:cross-env 设置的只在 Node.js 环境生效,前端代码无法访问 process.env.NODE_ENV

解决:使用 Webpack 内置的 DefinePlugin 插件

作用:在编译时,将前端代码中匹配的变量名,替换为值或表达式

配置 webpack.config.js 中给前端注入环境变量

js 复制代码
// ...
const webpack = require('webpack')

module.exports = {
  // ...
  plugins: [
    // ...
    new webpack.DefinePlugin({
      // key 是注入到打包后的前端 JS 代码中作为全局变量
      // value 是变量对应的值(在 corss-env 注入在 node.js 中的环境变量字符串)
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
    })
  ]
}

14. Webpack 开发环境调错 source map

在开发环境如何精准定位到报错源码位置

问题:代码被压缩和混淆,无法正确定位源代码位置(行数和列数)

source map:可以准确追踪 error 和 warning 在原始代码的位置

设置:webpack.config.js 配置 devtool 选项

js 复制代码
// 仅用于开发环境

module.exports = {
  // ...
  devtool: 'inline-source-map'
}

// 最好写成
//config里面是所有配置
const config = {...}
if (process.env.NODE_ENV === "development") {
  config.devtool = "inline-source-map";
}
module.exports = config;

inline-source-map 选项:把源码的位置信息一起打包在 JS 文件内

注意:source map 适用于开发环境,不要在生产环境使用(防止被轻易查看源码位置)

小结

  1. 为何打包后,在控制台无法准确定位到源码的位置信息?

    答案

    • 因为 Webpack 把代码压缩和混淆了

15. Webpack 设置解析别名路径

设置 Webpack 如何设置路径别名,方便我们引入目标模块

解析别名:配置模块如何解析,创建 import 或 require 的别名,来确保模块引入变得更简单

例如:原来路径如图,比较长而且相对路径不安全

解决:在 webpack.config.js 中配置解析别名 @ 来代表 src 绝对路径

例如:

  1. 原来路径如下:

    js 复制代码
    import { checkPhone, checkCode } from '../src/utils/check.js'
  2. 配置解析别名:在 webpack.config.js 中设置

    js 复制代码
    // ...
    
    const config = {
      // ...
      resolve: {
        alias: {
          '@': path.resolve(__dirname, 'src')
        }
      }
    }
  3. 这样我们以后,引入目标模块写的路径就更简单了

    js 复制代码
    import { checkPhone, checkCode } from '@/utils/check.js'
  4. 修改代码的路径后,重新打包观察效果是否正常!

小结

  1. 路径中的 '@' 符号代表什么意思?

    答案

    • 看在 webpack 配置中的别名路径是什么,就会在打包时替换成哪个路径使用

16. 优化-CDN使用

开发模式使用本地第三方库,生产模式下使用 CDN 加载引入

CDN定义:内容分发网络,指的是一组分布在各个地区的服务器

作用:把静态资源文件/第三方库放在 CDN 网络中各个服务器中,供用户就近请求获取

好处:减轻自己服务器请求压力,就近请求物理延迟低,配套缓存策略

需求:开发模式使用本地第三方库,生产模式下使用 CDN 加载引入

步骤:

  1. 在 html 中引入第三方库的 CDN地址并用模板语法判断
html 复制代码
<% if(htmlWebpackPlugin.options.useCdn){ %>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
<% } %>
  1. 配置 webpack.config.js 中 externals 外部扩展选项(防止某些 import 的包被打包)
js 复制代码
// 生产环境下使用相关配置
if (process.env.NODE_ENV === 'production') {
  // 外部扩展(让 webpack 防止 import 的包被打包进来)
  config.externals = {
    // key:import from 语句后面的字符串
    // value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
    'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
    'axios': 'axios'
  }
}
js 复制代码
// ...
const config = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      // ...
      // 自定义属性,在 html 模板中
      // 生产模式下使用 cdn 引入的地址
      <%=htmlWebpackPlugin.options.useCdn%> 访问使用
      useCdn: process.env.NODE_ENV === 'production'
    })
  ]
}
  1. 两种模式下打包观察效果

17. Webpack 多页面打包

让 Webpack 同时打包登录和内容列表页面

概念:

  • 单页面:单个 html 文件,切换 DOM 的方式实现不同业务逻辑展示,后续 Vue/React 会学到

  • 多页面:多个 html 文件,切换页面实现不同业务逻辑展示

需求:把黑马头条-数据管理平台-内容页面一起引入打包使用

步骤:

  1. 准备源码(html,css,js)放入相应位置,并改用模块化语法导出

  2. 下载 form-serialize 包并导入到核心代码中使用

  3. 配置 webpack.config.js 多入口和多页面的设置

    js 复制代码
    // ...
    const config = {
      entry: {
        '模块名1': path.resolve(__dirname, 'src/入口1.js'),
        '模块名2': path.resolve(__dirname, 'src/入口2.js'),
      },
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: './[name]/index.js'  
      }
      plugins: [
        new HtmlWebpackPlugin({
          template: './public/页面2.html', // 模板文件
          filename: './路径/index.html', // 输出文件
          chunks: ['模块名2']
        })
        new HtmlWebpackPlugin({
          template: './public/页面2.html', // 模板文件
          filename: './路径/index.html', // 输出文件
          chunks: ['模块名2']
        })
      ]
    }
  4. 重新打包观察效果

18. 案例-发布文章页面打包

需求:把发布文章页面一起打包

步骤:

  1. 准备发布文章页面源代码,改写成模块化的导出和导入方式

  2. 修改 webpack.config.js 的配置,增加一个入口和出口

  3. 打包观察效果

19. 优化-分割公共代码

需求:把 2 个以上页面引用的公共代码提取

步骤:

  1. 配置 webpack.config.js 的 splitChunks 分割功能

    js 复制代码
    // ...
    const config = {
      // ...
      optimization: {
        // 代码分割
        splitChunks: {
          chunks: 'all', // 所有模块动态非动态移入的都分割分析
          cacheGroups: { // 分隔组
            commons: { // 抽取公共模块
              minSize: 0, // 抽取的chunk最小大小字节
              minChunks: 2, // 最小引用数
              reuseExistingChunk: true, // 当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
              name(module, chunks, cacheGroupKey) { // 分离出模块文件名
                const allChunksNames = chunks.map((item) => item.name).join('~') // 模块名1~模块名2
                return `./js/${allChunksNames}` // 输出到 dist 目录下位置
              }
            }
          }
        }
  2. 打包观察效果

Git 版本控制系统

1. Git 初识

  • 概念:一个免费开源,分布式的代码版本控制系统,帮助开发团队维护代码

  • 作用:记录代码内容,切换代码版本,多人开发时高效合并代码内容

  • 如何学:

    个人本机使用:Git 基础命令和概念

    多人共享使用:团队开发同一个项目的代码版本管理

安装

Windows系统:exe 程序,默认下一步即可

Mac系统:dmg 程序,默认下一步即可

检验成功:

1.打开 bash 终端(git 专用)

2.命令:git -v(查看版本号)

配置用户信息

配置:用户名和邮箱,应用在每次提交代码版本时表明自己身份

命令:

git config --global user.name "itheima"

git config --global user.email "itheima@itcast.cn"

总结

  1. 为何学习 Git ?

    管理代码版本,记录,切换,合并代码

  2. Git 学习:

    现在本机自己使用

    再学习多人共享使用

  3. 如何安装使用?

    程序双击安装

    在 VSCode 中使用 bash 终端以及 git 命令

2. Git 仓库

Git 仓库(repository):记录文件状态内容的地方,存储着修改的历史记录

创建:

1.把本地文件夹转换成 Git 仓库:命令 git init

2.从其他服务器上克隆 Git 仓库

总结

  1. 什么是 Git 仓库 ?

    记录文件状态内容和历史记录的地方(.git 文件夹)

  2. 如何创建 Git 仓库?

    把本地文件夹转换成 Git 仓库:命令 git init

    从其他服务器上克隆 Git 仓库

3.Git 的三个区域

Git 使用时:

工作区:实际开发时操作的文件夹

暂存区:保存之前的准备区域(暂存改动过的文件)

版本库:提交并保存暂存区中的内容,产生一个版本快照

命令 作用
git add 文件名 暂存指定文件
git add . 暂存所有改动的文件
git commit -m "注释说明" 提交并保存,产生版本快照

总结

  1. Git 使用时有哪些区域 ?

    工作区,暂存区,版本库

  2. 工作区的内容,最终要如何保存在版本库中?

    git add 添加到暂存区

    等待时机后 git commit 提交保存到版本库,产生一次版本快照记录

4. Git 文件状态

Git 文件 2 种状态:

  • 未跟踪:新文件,从未被 Git 管理过

  • 已跟踪:Git 已经知道和管理的文件

文件状态 概念 场景
未跟踪(U) 从未被 Git 管理过 新文件
新添加(A) 第一次被 Git 暂存 之前版本记录无此文件
未修改('') 三个区域统一 提交保存后
已修改(M) 工作区内容变化 修改了内容产生

使用:修改文件,暂存,提交保存记录,如此反复

总结

  1. Git 文件状态分为哪 2 种 ?

    未跟踪和已跟踪(新添加,未修改,已修改)

  2. 如何查看暂存区和工作区文件状态?

    git status -s

5. Git 暂存区作用

暂存区:暂时存储,可以临时恢复代码内容,与版本库解耦

暂存区 -> 覆盖 -> 工作区,命令:git restore 目标文件(注意:完全确认覆盖时使用)

从暂存区移除文件,命令:git rm --cached 目标文件

总结

  1. 如何移除暂存区已暂存的文件?

    git rm --cached 目标文件

7. Git-切换版本

概念:把版本库某个版本对应的内容快照,恢复到工作区/暂存区

查看提交历史:git log --oneline

回退命令:

git reset --soft 版本号(其他文件未跟踪)

git reset --hard 版本号

git reset --mixed 版本号 (与 git reset 等价)

注意1:只有记录在版本库的提交记录才能恢复

注意2:回退后,继续修改->暂存->提交操作即可(产生新的提交记录过程)

总结

  1. 什么是 Git 回退版本?

    把版本库某个版本对应的内容快照,恢复到工作区/暂存区

  2. 强制覆盖暂存区和工作区的命令?

    git reset --hard 版本号

  3. 如何查看提交历史?

    git log --oneline

    git reflog --oneline

8. 删除文件

需求:删除 editor.js 文件,并产生一次版本记录

步骤:

1.手动删除工作区文件

2.暂存变更/手动删除暂存区文件造成变更

3.提交保存

总结:

工作区只要改变,都可以暂存提交产生新记录

9. 忽略文件

概念:.gitignore 文件可以让 git 彻底忽略跟踪指定文件

目的:让 git 仓库更小更快,避免重复无意义的文件管理

例如:

1.系统或软件自动生成的文件

2.编译产生的结果文件

3.运行时生成的日志文件,缓存文件,临时文件等

4.涉密文件,密码,秘钥等文件

创建:

1.项目根目录新建 .gitignore 文件

2.填入相应配置来忽略指定文件

注意:如果文件已经被暂存区跟踪过,可以从暂存区移除即可

10. 分支的概念

概念:本质上是指向提交节点的可变指针,默认名字是 master

注意:HEAD 指针影响工作区/暂存区的代码状态

场景:开发新需求 / 修复 Bug,保证主线代码随时可用,多人协同开发提高效率

例如:在现有代码上创建新分支完成内容列表业务,突然需要紧急修复 Bug - 单独创建分支解决 Bug

需求:创建内容列表 content 分支,并产生 3 次提交记录

步骤:

  1. 创建分支命令:git branch 分支名

  2. 切换分支命令:git checkout 分支名

  3. 工作区准备代码并暂存提交,重复 3 次

总结

  1. 什么是 Git 分支?

    指针,指向提交记录

  2. HEAD 指针的作用?

    影响暂存区和工作区的代码

  3. 如何创建和切换指针?

    git branch 分支名

    git checkout 分支名

11. 练习-登录 bug 修复

需求:新建 login-bug 分支,做 2 次提交记录(对手机号长度,验证码长度做判断)

步骤:

1.切回到主分支:git checkout master

2.创建新分支:git branch login-bug

3.切换新分支:git checkout login-bug

4.修改代码,暂存,提交产生版本记录

12. 分支-合并与删除

需求:把 login-bug 合并回到 master 分支并删除 login-bug 分支

步骤:

1.切回到要合入的分支上:git checkout master

2.合并其他分支过来:git merge login-bug

3.删除合并后的分支指针:git branch -d login-bug

13. 分支-合并与提交

合并提交:发生于原分支产生了新的提交记录后,再合并回去时发生,自动使用多个快照记录合并后产生一次新的提交

步骤:

1.切回到要合入的分支上:git checkout master

2.合并其他分支过来:git merge content

3.删除合并后的分支:git branch -d content

最后合并回到主分支上时,提交记录流程图:

注意:提交记录的顺序按照产生的先后顺序排列,而非合并的先后顺序

14. 分支-合并冲突

需求1:基于 master 新建 publish 分支,完成发布文章业务,然后修改内容页面的 html 文件的 title 标签,并提交一次

需求2:切换到 master,也在修改内容页面的 html 文件的 title 标签,并提交一次

冲突:把 publish 分支合并到 master 回来,产生合并冲突

概念:不同分支中,对同一个文件的同一部分修改,Git 无法干净的合并,产生合并冲突

解决:

1.打开 VSCode 找到冲突文件并手动解决

2.解决后需要提交一次记录

避免:(多交流)

1.按页面划分不同分支开发

2.公共代码在统一文件夹维护

3.Node等软件版本统一,npm 包统一下载

15. Git 常用命令

命令 作用 注意
git -v 查看 git 版本
git init 初始化 git 仓库
git add 文件标识 暂存某个文件 文件标识以终端为起始的相对路径
git add . 暂存所有文件
git commit -m '说明注释' 提交产生版本记录 每次提交,把暂存区内容快照一份
git status 查看文件状态 - 详细信息
git status -s 查看文件状态 - 简略信息 第一列是暂存区状态,第二列是工作区状态
git ls-files 查看暂存区文件列表
git restore 文件标识 从暂存区恢复到工作区 如果文件标识为 . 则恢复所有文件
git rm --cached 文件标识 从暂存区移除文件 不让 git 跟踪文件变化
git log 查看提交记录 - 详细信息
git log --oneline 查看提交记录 - 简略信息 版本号 分支指针 提交时说明注释
命令 作用 注意
git reflog --oneline 查看完整历史 - 简略消息 包括提交,切换,回退等所有记录
git reset 版本号 切换版本代码到暂存区和工作区 --soft 模式保留暂存区和工作区原本内容 --hard 模式不保留暂存区和工作区原本内容 --mixed 模式不保留暂存区,工作区保留(默认) 先覆盖到暂存区,再用暂存区对比覆盖工作区
git branch 分支名 创建分支
git branch 查看本地分支
git branch -d 分支名 删除分支 请确保记录已经合并到别的分支下,再删除分支
git checkout 分支名 切换分支
git checkout -b 分支名 创建并立刻切换分支
git merge 分支名 把分支提交历史记录合并到当前所在分支

16. Git 远程仓库

概念:托管在因特网或其他网络中的你的项目的版本库

作用:保存版本库的历史记录,多人协作

创建:公司自己服务器 / 第三方托管平台(Gitee,GitLab,GitHub...)

需求:创建远程版本库,并把本地 Git 仓库推送上去保存

步骤:

  1. 注册第三方托管平台网站账号

  2. 新建仓库得到远程仓库 Git 地址

  3. 本地 Git 仓库添加远程仓库原点地址

    命令:git remote add 远程仓库别名 远程仓库地址

    例如:git remote add origin gitee.com/lidongxu/wo...

  4. 本地 Git 仓库推送版本记录到远程仓库

    命令:git push -u 远程仓库别名 本地和远程分支名

    例如:git push -u origin master

    完整写法:git push --set-upstream origin master:master

总结

  1. 远程版本库的作用?

    保存提交历史记录,多人共享

  2. 远程版本库使用步骤?

    创建远程版本库(自己服务器/第三方托管平台)

    本地版本库设置远程地址

    推送本地版本库到远程

  3. 推送的命令?

    git push -u origin master

17. Git 远程仓库 - 克隆

克隆:拷贝一个 Git 仓库到本地,进行使用

命令:git clone 远程仓库地址,例如:git clone gitee.com/lidongxu/wo...

效果:在运行命令所在文件夹,生成 work 项目文件夹(包含版本库,并映射到暂存区和工作区)

注意1:Git 本地仓库已经建立好和远程仓库的链接

注意2:仓库公开随意克隆,推送需要身为仓库团队成员

18. 多人协同开发

需求:小传新代码共享给小智

步骤:

1.小传开发代码 -> 工作区 -> 暂存区 -> 提交 -> 拉取(可选)-> 推送

2.小智 -> 拉取(后续也可以开发代码 -> ... -> 推送)

3.想要看到别人同步上去的最新内容:git pull origin master 等价于

git fetch origin master:master(获取远程分支记录到本地,未合并)

git merge origin/master (把远程分支记录合并到所在分支下)

19. VSCode 中使用 Git

使用:源代码管理,进行暂存,撤销,对比差异,提交等操作

注意:VSCode 打开的项目文件夹下需要有 .git 仓库

20. 案例-发布黑马头条数据管理平台

需求:把 Webpack 压缩好的 dist 分发文件夹网页,部署到码云上,开启 Page 服务在互联网中浏览

步骤:

1.初始化本地 Git 仓库(这次是非空文件夹-配套素材 dist 文件夹)

2.初始化远程 Git 仓库(这一次也是非空的)

3.本地配置远程仓库链接

4.本地拉取合并一下(确认本地要包含远程内容时使用)

5.本地推送到远程 Git 仓库

6.开启 page 网页服务得到地址浏览

21. Git 常用命令

命令 作用 注意
git remote add 远程仓库别名 远程仓库地址 添加远程仓库地址 别名唯一,地址是 .git 结尾的网址
git remote -v 查看远程仓库地址
git remote remove 远程仓库别名 删除远程仓库地址
git pull 远程仓库别名 分支名 拉取 完整写法:git pull 远程仓库别名 远程分支名:本地分支名 等价于:git fetch 和 git merge
git push 远程仓库别名 分支名 推送 完整写法:git push 远程仓库别名 本地分支名:远程分支名 -u:建立通道后以后可以简写 git push
git pull --rebase 远程仓库别名 分支名 拉取合并 合并没有关系的记录
git clone 远程仓库地址 克隆 从0得到一个远程的Git仓库到本地使用
相关推荐
爷_3 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
charlee445 小时前
行业思考:不是前端不行,是只会前端不行
前端·ai
Amodoro5 小时前
nuxt更改页面渲染的html,去除自定义属性、
前端·html·nuxt3·nuxt2·nuxtjs
Wcowin6 小时前
Mkdocs相关插件推荐(原创+合作)
前端·mkdocs
伍哥的传说6 小时前
CSS+JavaScript 禁用浏览器复制功能的几种方法
前端·javascript·css·vue.js·vue·css3·禁用浏览器复制
lichenyang4536 小时前
Axios封装以及添加拦截器
前端·javascript·react.js·typescript
Trust yourself2437 小时前
想把一个easyui的表格<th>改成下拉怎么做
前端·深度学习·easyui
三口吃掉你7 小时前
Web服务器(Tomcat、项目部署)
服务器·前端·tomcat
Trust yourself2437 小时前
在easyui中如何设置自带的弹窗,有输入框
前端·javascript·easyui
烛阴7 小时前
Tile Pattern
前端·webgl