Vue.js项目实战开发(1)- 必掌握知识点 - #博学谷IT学习技术支持#

(补充) 前后端的身份认证

  1. Web 开发模式

    目前主流的 Web 开发模式有两种,分别是:

    1. 基于服务端渲染的传统 Web 开发模式

      服务端渲染的概念:服务器发送给客户端的 HTML 页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用 Ajax 这样的技术额外请求页面的数据。

      app.get('/index.html', (req, res) => {
          // 1. 要渲染的数据
          const user = { name: 'zs', age: 20 }
          // 2. 服务器通过字符串的拼接,动态生成 HTML 内容
          const html = `<h1>姓名:${user.name}, 年龄:${user.age}</h1>`
          // 3. 把生成好的页面内容响应给客户端。因此,客户端拿到的是带有真实数据的 HTML 页面
          res.send(html)
      })
      

      优点:

      1. 前端耗时少。因为服务器端负责动态生成 HTML 内容,浏览器只需要直接渲染页面即可。尤其是移动端,更省电。
      2. 有利于 SEO。因为服务器端影响的是完整的 HTML 页面内容,所以爬虫更容易爬取获得信息,更有利于 SEO。

      缺点:

      1. 占用服务器端资源。即服务器端完成 HTML 页面内容的拼接,如果请求较多,会对服务器造成一定的访问压力。
      2. 不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,尤其对于前端复杂度高的项目,不利于项目高效开发。
    2. 基于前后端分离的新型 Web 开发模式

      前后端分离的概念:前后端分离的开发模式,依赖于 Ajax 技术的广泛应用。简而言之,前后端分离的 Web 开发模式,就是后端只负责提供 API 接口,前端使用 Ajax 调用接口的开发模式。

      优点:

      1. 开发体验好。前端专注于 UI 页面的开发,后端专注于 API 的开发,且前端有更多的选择性。
      2. 用户体验好。Ajax 技术的广泛应用,极大的提高了用户的体验,可以轻松实现页面的局部刷新。
      3. 减轻了服务器端的渲染压力。因为页面最终是在每个用户的浏览器中生成的。

      缺点:

      1. 不利于 SEO。因为完整的 HTML 页面需要在客户端动态拼接完成,所以爬虫无法爬取页面的有效信息。(解决方案:利用 Vue、React 等前端框架的 SSR(server side render)技术能够很好的解决 SEO 问题!)
    3. 如何选择 Web 开发模式

      根据不同的业务场景选择不同的开发模式。

      • 比如企业级网站,主要功能是展示而没有复杂的交互,并且需要良好的 SEO,则使用服务器端渲染。

      • 而类似后台管理项目,交互性比较强,不需要考虑 SEO,那么就可以使用前后端分离的开发模式。

        另外,具体使用何种开发模式并不是绝对的,为了同时兼顾首页的渲染速度和前后端分离的开发效率,一些网站采用了首屏服务器端渲染 + 其他页面前后端分离的开发模式。

  2. 身份认证

    1. 什么是身份认证

      身份认证又称“身份验证”、“鉴权”,是指通过一定的手段,完成对用户身份的确认

    2. 为什么需要身份认证

      身份认证的目的,是为了确认当前所声称为某种身份的用户,确实是所声称的用户

    3. 不同开发模式下的身份认证

      对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:

      1. 服务端渲染推荐使用 Session 认证机制
      2. 前后端分离推荐使用 JWT 认证机制
  3. Session 认证机制

    1. HTTP 协议的无状态性

      HTTP 协议的无状态性,指的是客户端的每次 HTTP 请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态

    2. 如何突破 HTTP 无状态的限制

      对于超市来说,为了方便收银员在进行结算时给 VIP 用户打折,超市可以为每个 VIP 用户发放会员卡。

      注意:现实生活中的会员卡身份认证方式,在 Web 开发中的专业术语叫做 Cookie

    3. 什么是 Cookie

      Cookie 是存储在用户浏览器中的一段不超过 4 KB 的字符串。它由一个名称(Name)、一个(Value)和其它几个用于控制 Cookie 有效期、安全期、使用范围可选属性组成。

      不同域名下的 Cookie 各自独立,每当用户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器。

      Cookie 的几大特性:

      1. 自动发送
      2. 域名独立
      3. 过期时限
      4. 4 KB 限制
    4. Cookie 在身份认证中的作用

      客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中。

      随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端的身份。

    5. Cookie 不具有安全性

      由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过 Cookie 的形式发送给浏览器。

      注意千万不要使用 Cookie 存储重要且隐私的数据!比如用户的身份信息、密码等。

    6. 提高身份认证的安全性

      为了防止客户伪造会员卡,收银员在拿到客户出示的会员卡之后,可以在收音机上进行刷卡认证。只有收银机确认存在的会员卡,才能被正常使用。

      这种“会员卡 + 刷卡认证”的设计理念,就是 Session 认证机制的精髓。

    7. Session 的工作原理

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4lr9stK-1659256382504)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220723135407714.png)]

  4. 在 Express 中使用 Session 认证

    1. 安装 express-session 中间件

      npm install express-session
      
    2. 配置 express-session 中间件

      express-session 中间件安装成功后,需要通过 app.use() 来注册 session 中间件

      // 1. 导入 session 中间件
      var session = require('express-session')
      // 2. 配置 session 中间件
      app.use(session({
          secret: 'keyboard cat',		// secret 属性的值可以为任意字符串
          resave: false,				// 固定写法
          saveUninitialized: true		// 固定写法
      }))
      
    3. 向 session 中存数据

      当 express-session 中间件配置成功后,即可通过 req.session 来访问和使用 session 对象,从而存储用户的关键信息:

      app.post('/api/login', (req, res) => {
          // 判断用户提交的登录信息是否正确
          if(req.body.username !== 'admin' || req.body.password !== '000000') {
              return res.send({ status: 1, msg: '登陆失败' })
          }
          
          req.session.user = req.body		// 将用户的信息,存储到 session 中
          req.session.islogin = true		// 将用户的登录状态,存储到 session 中
          
          res.send({ status: 0, msg: '登陆成功' })
      })
      
    4. 从 session 中取数据

      可以直接从 req.session 对象上获取之前存储的数据

      // 获取用户姓名的接口
      app.get('/api/username', (req, res) => {
          // 判断用户是否登录
          if(!req.session.islogin) {
              return res.send({ status: 1, msg: 'fail' })
          }
          res.send({ status: 0, msg: 'success', username: req.session.user.username})
      })
      
    5. 清空 session

      调用 req.session.destroy() 函数,即可清空服务器保存的 session 信息。

      // 退出登录的接口
      app.post('/api/logout', (req, res) => {
          // 清空当前客户端对应的 session 信息
          req.session.destroy()
          res.send({
              status: 0,
              msg: '退出登录成功'
          })
      })
      
  5. JWT 认证机制

    1. 了解 Session 认证的局限性

      Session 认证机制需要配合 Cookie 才能实现。由于 Cookie 默认不支持浏览器跨域访问,所以,当涉及到前端跨域请求后端接口的时候需要做很多额外的配置,才能实现跨域 Session 认证。

      注意:

      • 当前端请求后端接口不存在跨域问题的时候,推荐使用 Session 身份认证机制。
      • 当前端需要跨域请求后端接口的时候,不推荐使用 Session 身份认证机制,推荐使用 JWT 认证机制。
    2. 什么是 JWT

      JWT 是目前最流行的跨域认证解决方案。

    3. JWT 的工作原理

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Thxtu9tC-1659256382510)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220725163303379.png)]

      总结:用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份。

    4. JWT 的组成部分

      JWT 通常由三部分组成,分别是 Header(头部)、Payload(有效荷载)、Signature(签名)。

      三者之间使用因为的 “.” 分隔,格式如下:

      Header.Payload.Signature
      
    5. JWT 的三个部分各自代表的含有

      • Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
      • Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。
    6. JWT 的使用方式

      客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStoragesessionStorage 中。

      此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中,格式如下:

      Authorization: Bearer <token>
      
  6. 在 Express 中使用 JWT

    1. 安装 JWT 相关的包

      npm install jsonwebtoken express-jwt
      

      其中:

      • jsonwebtoken 用于生成 JWT 字符串
      • express-jwt 用于将 JWT 字符串解析还原成 JSON 对象
    2. 导入 JWT 相关的包

      const jwt = require('jsonwebtoken')
      const expressJWT = require('express-jwt')
      
    3. 定义 secret 密钥

      为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密和解密的 secret 密钥:

      1. 当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串
      2. 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密
    4. 在登录成功后生成 JWT 字符串

      调用 jsonwebtoken 包提供的 sign() 方法,将用户的信息加密成 JWT 字符串,响应给客户端:

      // 登录接口
      app.post('/api/login', function(req, res) {
          // ...省略登录失败情况下的代码
          // 用户登录成功之后,生成 JWT 字符串,通过 token 属性响应给客户端
          res.send({
              status: 200,
              message: '登陆成功!',
              // 调用 jwt.sign() 生成 JWT 字符串,三个参数分别是:用户信息对象、加密密钥、配置对象
              token: jwt.sign({username: userinfo.username}, secretkey, {expiresIn: '30s'})
          })
      })
      
    5. 将 JWT 字符串还原为 JSON 对象

      客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的 Authorization 字段,将 Token 字符串发送到服务器进行身份认证.

      此时,服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 Token 解析还原成 JSON 对象

      // 使用 app.use() 来注册中间件
      // expressJWT({ secret: secretKey }) 就是用来解析 Token 的中间件
      // .unless({ path: [/^\/api\//] })) 用来指定哪些接口不需要访问权限
      app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
      
    6. 使用 req.user 获取用户信息

      当 express-jwt 这个中间件配置成功之后,即可在那些有权限的接口中,使用 req.user 对象,来访问从 JWT 字符串中解析出来的用户信息了

      // 这是一个有权限的 API 接口
      app.get('/admin/getinfo', (req, res) => {
          console.log(req.user)
          res.send({
              status: 200,
              message: '获取用户信息成功',
              data: req.user
          })
      })
      
    7. 捕获解析 JWT 失败后产生的错误

      当使用 express-jwt 解析 Token 字符串时,如果客户端发送过来的 Token 字符串过期或不合法,会产生一个解析失败的错误,影响项目的正常运行。我们可以通过 Express 的错误中间件,捕获这个错误并进行相关的处理

      app.use((err, req, res, next) => {
          // token 解析失败导致的错误
          if(err.name === 'UnauthorizedError') {
              return res.send({ status: 401, message: '无效的 token'})
          }
          // 其它原因导致的错误
          res.send({ status: 500, message: '未知错误'})
      })
      

      第一章 webpack+vue基础

Vue前置知识

1-1 ES6模块化

1. 回顾:node.js 中如何实现模块化

  • node.js 遵循了 CommonJS 的模块化规范。其中:
    • 导入其它模块使用 require() 方法
    • 模块对外共享成员使用 module.exports 对象

2. 前端模块化规范的分类

  • 在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了 AMD、CMD、CommonJS 等模块化规范。

    • AMD 和 CMD 适用于浏览器端的 Javascript 模块化
    • CommonJS 适用于服务器端的 Javascript 模块化

    为了统一模块化规范有了 ES6 模块化规范

3. 什么是 ES6 模块化规范

  • ES6 模块化规范浏览器端服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不需要再额外学习 AMD、CMD 或 CommonJS 等模块化规范。
  • ES6 模块化规范中定义:
    • 每个 js 文件都是一个独立的模块
    • 导入其它模块成员使用 import 关键字
    • 向外共享模块成员使用 export 关键字

4. 在 node.js 中体验 ES6 模块化

  • node.js 中默认仅支持 CommonJS 模块化规范,可以按照如下两个步骤进行配置使用 ES6 模块化规范:
    1. 确保安装了 v14.15.1 或更高版本的 node.js
    2. 在 package.json 的根节点中添加 “type”: “module” 节点(默认"type": “commonjs” )

5. ES6 模块化的基本语法

  • ES6 的模块化主要包含如下 3 种用法:
    1. 默认导出与默认导入
    2. 按需导出与按需导入
    3. 直接导入并执行模块中的代码
  1. 默认导出与默认导入

    1. 默认导出

      默认导出的语法: export default 默认导出的成员

      let n1 = 10 // 定义模块私有成员 n1
      let n2 = 20 // 定义模块私有成员 n2 (外界访问不到 n2,因为它没有被共享出去)
      function show() {} // 定义模块私有方法 show
      
      export default{ // 使用 export default 默认导出语法,向外共享 n1 和 show 两个成员
          n1,
          show
      }
      

      注意事项:

      • 每个模块中,只允许使用唯一的一次 export default,否则会报错!
    2. 默认导入

      默认导入的语法: import 接收名称 from ‘模块标识符’

      // 从 01_m1.js 模块中导入 export default 向外共享的成员
      // 并使用 m1 成员进行接收
      import m1 from './01_m1.js'
      
      // 打印输出的结果为:
      // {n1: 10, show: [Function: show]}
      console.log(m1)
      

      注意事项:

      • 默认导入时的接收名称可以任意名称,只要是合法的成员名称即可
  2. 按需导出与按需导入

    1. 按需导出

      按需导出的语法: export 按需导出的成员

      // 当前模块为 03_m2.js
      
      // 向外按需导出变量 s1
      export let s1 = 'aaa'
      // 向外按需导出变量 s2
      export let s2 = 'ccc'
      // 向外按需导出方法 say
      export function say() {}
      

      注意事项:

      • 每个模块中可以使用多次按需导出
      • 按需导入的成员名称必须和按需导出的名称保持一致
      • 按需导入时,可以使用 as 关键字进行重命名
      • 按需导入可以和默认导入一起使用
    2. 按需导入

      按需导入的语法: import { s1 } from ‘模块标识符’

      // 导入模块成员
      import {s1, s2 as str2, say} from './03_m2.js'
      console.log(s1) // aaa
      console.log(str2) // ccc
      console.log(say) // [Function: say]
      
  3. 直接导入并执行模块中的代码

    如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码,示例代码如下:

    // 当前文件模块为 05_m3.js
    // 在当前模块中执行一个 for 循环操作
    for(let i = 0; i < 3; i++) {
        console.log(i)
    }
    
    // ----------------分割线-------------------
    
    // 直接导入并执行模块代码,不需要得到模块向外共享的成员
    import './05_m3.js'
    

1-2 Promise

1. 回调地狱

  • 多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下:

  • 回调地狱的缺点:

    • 代码耦合性太强,牵一发而动全身,难以维护
    • 大量冗余的代码相互嵌套,代码的可读性变差
  1. 如何解决回调地狱的问题

    • 为了解决回调地狱的问题,ES6(ECMAScript 2015)中新增了 Promise 的概念。
  2. Promise 的基本概念

    1. Promise 是一个构造函数

      • 我们可以创建 Promise 的实例 const p = new Promise()

      • new 出来的 Promise 实例对象,代表一个异步操作

    2. Promise.prototype 上包含一个 .then() 方法

      • 每一次 new Promise() 构造函数得到的实例对象,

      • 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()

    3. .then() 方法用来预先指定成功和失败的回调函数

      • p.then(成功的回调函数,失败的回调函数)

      • p.then(result => { }, error => { })

      • 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

2. 基于回调函数按顺序读取文件内容

3. 基于 then-fs 读取文件内容

  • 由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容

    npm install then-fs
    
  1. then-fs 的基本使用

    调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数。

    import thenFs from 'then-fs'
    
    thenFs.readFile('./files/1.txt', 'utf8').then((r1) => {console.log(r1);})
    thenFs.readFile('./files/2.txt', 'utf8').then((r2) => {console.log(r2);})
    thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {console.log(r3);})
    

    注意:上述的代码无法保证文件的读取顺序,需要做进一步的改进!

  2. .then() 方法的特性

    如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题。

  3. 基于 Promise 按顺序读取文件的内容

    Promise 支持链式调用,从而来解决回调地狱的问题。示例代码如下:

    thenFs.readFile('./files/1.txt', 'utf8')
        .then((r1) => {
            console.log(r1)
            return thenFs.readFile('./files/2.txt', 'utf8')
    })
    .then((r2) => {
        console.log(r2)
        return thenFs.readFile('./files/3.txt', 'utf8')
    })
    .then((r3) => {
        console.log(r3)
    })
    
  4. 通过 .catch 捕获错误

    在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:

    thenFs.readFile('./files/1.txt', 'utf8')
        .then((r1) => {
            console.log(r1)
            return thenFs.readFile('./files/2.txt', 'utf8')
    	})
    	.then((r2) => {
       		console.log(r2)
        	return thenFs.readFile('./files/3.txt', 'utf8')
    	})
    	.then((r3) => {
        	console.log(r3)
    	})
    	.catch(err => {
        	console.log(err.message); // 只有这个输出ENOENT: no such file or directory, ...
     	})
    

    如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将 .catch 的调用提前

    thenFs
      .readFile("./files/11.txt", "utf8")
      .catch(err => {
        console.log(err.message); // ENOENT: no such file or directory, ...
      })
      .then((r1) => {
        console.log(r1); // undefined
        return thenFs.readFile("./files/2.txt", "utf8");
      })
      .then((r2) => {
        console.log(r2); // 222
        return thenFs.readFile("./files/3.txt", "utf8");
      })
      .then((r3) => {
        console.log(r3); // 333
      })
    
  5. Promise.all() 方法

    Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)。

    const promiseArr = [
        thenFs.readFile('./files/1.txt', 'utf8'),
        thenFs.readFile('./files/2.txt', 'utf8'),
        thenFs.readFile('./files/3.txt', 'utf8')
    ]
    
    Promise.all(promiseArr).then(result => {
        console.log(result);
    })
    

    注意:数组中 Promise 实例的顺序,就是最终结果的顺序!

  6. Promise.race() 方法

    Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)。

    const promiseArr = [
        thenFs.readFile('./files/1.txt', 'utf8'),
        thenFs.readFile('./files/2.txt', 'utf8'),
        thenFs.readFile('./files/3.txt', 'utf8')
    ]
    
    Promise.race(promiseArr).then(result => {
        console.log(result);
    })
    

4. 基于 Promise 封装读文件的方法

  • 方法的封装要求:
    • 方法的名称要定义为 getFile
    • 方法接收一个形参 fpath,表示要读取的文件的路径
    • 方法的返回值为 Promise 实例对象
  1. getFile 方法的基本定义

    // 1. 方法的名称为 getFile
    // 2. 方法接收一个形参 fpath,标识要读取的文件的路径
    function getFile(fpath) {
        // 3. 方法的返回值为 Promise 的实例对象
        return new Promise()
    }
    

    注意:第 5 行代码中的 new Promise() 只是创建了一个形式上的异步操作

  2. 创建具体的异步操作

    如果想要创建具体的异步操作,则需要在 new Promise() 构造函数期间,传递一个 function 函数,将具体的异步操作定义到 function 函数内部。

    // 1. 方法的名称为 getFile
    // 2. 方法接收一个形参 fpath,标识要读取的文件的路径
    function getFile(fpath) {
        // 3. 方法的返回值为 Promise 的实例对象
        return new Promise(function() {
            // 4. 下面这行代码,表示这是一个具体的、读文件的异步操作
            fs.readFile(fpath, 'utf8', (err, dataStr) => { })
        })
    }
    
  3. 获取 .then 的两个实参

    通过 .then() 指定的成功和失败的回调函数,可以在 function 的形参中进行接收,示例代码如下:

    function getFile(fpath) {
        // resolve 形参是:调用 getFile() 方法时,通过 .then 指定的“成功的”回调函数
        // reject 形参是:调用 getFile() 方法时,通过 .then 指定的“失败的”回调函数
        return new Promise(function(resolve, reject) {
            fs.readFile(fpath, 'utf8', (err, dataStr) => { })
        })
    }
    
    // getFile 方法的调用过程:
    getFile('./files/1.txt').then(成功的回调函数, 失败的回调函数)
    
  4. 调用 resolve 和 reject 回调函数

    Promise 异步操作的结果,可以调用 resolve 或 reject 回调函数进行处理。示例代码如下:

    function getFile(fpath) {
        return new Promise(function(resolve, reject) {
            fs.readFile(fpath, 'utf8', (err, dataStr) => {
                if(err) return reject(err)  // 如果读取失败,则调用“失败的回调函数”
                resolve(dataStr)			// 如果读取成功,则调用“成功的回调函数”
            })
        })
    }
    // getFile 方法的调用过程:
    getFile('./files/1.txt').then(成功的回调函数, 失败的回调函数)
    

1-3 async/await

1. 什么是 async/await

async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出现之前,开发者只能通过链式 .then() 的方式处理 Promise 异步操作。

  • .then 链式调用的优点:解决了回调地狱的问题
  • .then 链式调用的缺点:代码冗余、阅读性差、不易理解

2. async/await 的基本使用

import thenFs from 'then-fs'
// 按照顺序读取文件 1,2,3 的内容
async function getAllFile() {
    const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
    console.log(r1)
    const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
    console.log(r2)
    const r3 = await thenFs.readFile('./files/3.txt', 'utf8')
    console.log(r3)
}

getAllFile()

3. async/await 的使用注意事项

  1. 如果在 function 中使用了 await,则 function 必须被 async 修饰

  2. 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行


1-4 EventLoop

1. JavaScript 是单线程的语言

  • JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。

  • 单线程执行任务队列的问题:如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。

2. 同步任务和异步任务

为了防止某个耗时任务导致程序假死的问题,JavaScript 把待执行的任务分为了两类:

  1. 同步任务
    • 又叫做非耗时任务,指的是在主线程上排队执行的那些任务
    • 只有前一个任务执行完毕,才能执行后一个任务
  2. 异步任务
    • 又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
    • 当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数

3. 同步任务和异步任务的执行过程

  1. 同步任务由 JavaScript 主线程次序执行
  2. 异步任务委托给宿主环境执行
  3. 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
  4. JavaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
  5. JavaScript 主线程不断重复上面的第 4 步

4. EventLoop 的基本概念

  • JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)。

  • 结合 EventLoop 分析输出的顺序 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tpBbWg62-1659256432484)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220729220526029.png)] 正确的输出结果:ADCB。其中:A 和 D 属于同步任务。会根据代码的先后顺序依次被执行;C 和 B 属于异步任务。它们的回调函数会被加入到任务队列中,等待主线程空闲时再执行。

1-5 宏任务和微任务

1. 什么是宏任务和微任务

  • JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:

    1. 宏任务(macrotask)

      • 异步 Ajax 请求、
      • setTimeout、setInterval、
      • 文件操作
      • 其它宏任务
    2. 微任务(microtask)

      • Promise.then、.catch 和 .finally
      • process.nextTick
      • 其它微任务

2. 宏任务和微任务的执行顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRDpvYCi-1659256432491)(C:\Users\WZX\AppData\Roaming\Typora\typora-user-images\image-20220729221041268.png)]

每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务之后,再继续执行下一个宏任务。

3. 分析以下代码输出的顺序

  • 经典面试题-1


正确的输出顺序是:2431

分析:
① 先执行所有的同步任务

​ 执行第 6 行、第 12 行代码
② 再执行微任务
​ 执行第 9 行代码
③ 再执行下一个宏任务
​ 执行第 2 行代码

  • 经典面试题-2




正确的输出顺序是:15623478

webpack基本使用

1.webpack基本概述

webpack本质是, 一个第三方模块包, 用于分析, 并打包代码

  • 支持所有类型文件的打包
  • 支持 less/sass => css
  • 支持 ES6/7/8 => ES5
  • 压缩代码, 提高加载速度

2. webpack的使用步骤

2.0_webpack基础使用

目标: 把src下的2个js文件, 打包到1个js中, 并输出到默认dist目录下

默认入口: ./src/index.js

默认出口: ./dist/main.js

注意:路径上, 文件夹, 文件名不能叫webpack/其他已知的模块名

  1. 初始化包环境

    yarn init
    
  2. 安装依赖包

    yarn add webpack webpack-cli -D
    
  3. 配置scripts(自定义命令)

    scripts: {
    	"build": "webpack"
    }
    
  4. 新建目录src

  5. 新建src/add/add.js - 定义求和函数导出

    export const addFn = (a, b) => a + b
    
  6. 新建src/index.js导入使用

    import {addFn} from './add/add'
    
    console.log(addFn(10, 20));
    
  7. 运行打包命令

    yarn build
    #或者 npm run build
    

总结: src并列处, 生成默认dist目录和打包后默认main.js文件

2.1_webpack 更新打包

目标: 以后代码变更, 如何重新打包呢

  1. 新建src/tool/tool.js - 定义导出数组求和方法

    export const getArrSum = arr => arr.reduce((sum, val) => sum += val, 0)
    
  2. src/index.js - 导入使用

    import {addFn} from './add/add'
    import {getArrSum} from './tool/tool'
    
    console.log(addFn(10, 20));
    console.log(getArrSum([1, 2, 3]));
    
  3. 重新打包

    yarn build
    

总结1: src下开发环境, dist是打包后, 分别独立

总结2: 打包后格式压缩, 变量压缩等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值