[TOC]

视频p78-

浏览器本地存储 webStorage

JSON.stringify ( JavaScript 对象或值 ) 方法 将一个 JavaScript 对象或值转换为 JSON 字符串
JSON.parse ( JSON 字符串 ) 方法 将数据转换为 JavaScript 对象。

搜索替换快捷键 CTRL+H

本节笔记

  1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
  3. 相关API:
    1. xxxxxStorage.setItem('key', 'value');
      该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    2. xxxxxStorage.getItem('person');
      该方法接受一个键名作为参数,返回键名对应的值。
    3. xxxxxStorage.removeItem('key');
      该方法接受一个键名作为参数,并把该键名从存储中删除。
    4. xxxxxStorage.clear()
      该方法会清空存储中的所有数据。
  4. 备注:

    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. JSON.parse(null)的结果依然是null。

      todolist 本地存储

      image.png

      组件的自定义事件

      绑定

      image.png
      image.png

      解绑

      image.png

      总结

  5. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  6. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
  7. 绑定自定义事件:(在父组件中)

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>
    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){
      this.$refs.xxx.$on('atguigu',this.test)
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  8. 触发自定义事件:this.$emit('atguigu',数据)
  9. 解绑自定义事件this.$off('atguigu')
  10. 组件上也可以绑定原生DOM事件,需要使用native修饰符。
  11. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题! (回调函数谁调用,谁是this)
  12. 自定义事件传数据:父组件,定事件,写回调;子组件,触事件,传数据

    全局事件总线 组件间通信

    实现的设计思路

    image.png
    X就等于集中管理自定义事件的绑定和触发,通信的两个组件只需要在x里面找自定义事件就行了
    反正每个组件在生成的时候都把自己的联系方式给 x

    x 的设计路线

    image.png
    image.png
    结果:将 x 放在 Vue 的原型对象

    demo:兄弟组件,student.vue 传数据到school.vue

    image.pngimage.png
    image.png
    image.png

    弹幕笔记记录

    “接受方在总线上绑定事件与回调函数,发送方触发总线上的事件与传参。”
    “我悟了:所有的事件都统一绑定在X身上,而对应的回调都放在各个组件中。于是从X射出来的事件能触发任意组件中的回调。”
    “x 就是一个共同的vc,可以触发同一个$emit”

    推理 改进 版本

    image.png
    image.png
    image.png
    image.png

    全局事件总线总结

  13. 一种组件间通信的方式,适用于任意组件间通信。

  14. 安装全局事件总线:

    new Vue({
    ......
    beforeCreate() {
        Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    },
    ......
    })
    
  15. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){
      demo(data){......}
      }
      ......
      mounted() {
      this.$bus.$on('xxxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  16. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

    todolist 的组件通信改成事件总线写法

    “父给子传用props,子给父传用自定义事件,跨组件传用全局事件总线。”
    “全局事件总线的本质是给组件绑定自定义事件。 只不过这里的组件是this.$bus也就是Vue实例。”

    消息订阅与发布 组件间通信

  17. 一种组件间通信的方式,适用于任意组件间通信。

  18. 使用步骤:

    1. 安装pubsub:npm i pubsub-js
    2. 引入: import pubsub from 'pubsub-js'
    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){
      demo(data){......}
      }
      ......
      mounted() {
      this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      
    4. 提供数据:pubsub.publish('xxx',数据) // 在数据提供方

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

在Vue中用的不多。事件总线比较常见。

todolist 改成消息订阅与发布模式

todolist 案例 - 编辑功能

。。。。这个没听懂

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

    过度与动画

    引入动画库

    Animate.css | A cross-browser library of CSS animations.
    https://animate.style/
    安装: npm install animate.css
    引入:import”animate.css”;

    总结

  4. 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

  5. 写法:

    1. 准备好样式:
      • 元素进入的样式:
        1. v-enter:进入的起点
        2. v-enter-active:进入过程中
        3. v-enter-to:进入的终点
      • 元素离开的样式:
        1. v-leave:离开的起点
        2. v-leave-active:离开过程中
        3. v-leave-to:离开的终点
    2. 使用<transition>包裹要过度的元素,并配置name属性:

      <transition name="hello">
      <h1 v-show="isShow">你好啊!</h1>
      </transition>
      
    3. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

      补课:Promise,axios

      promise

      学习资料来源

      编程前端开发学习笔记/Promise学习笔记 · 努力学习的汪/洪学习笔记 - 码云 - 开源中国
      [https://gitee.com/hongjilin/hongs-study-notes/tree/master/%E7%BC%96%E7%A8%8B
      %E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Promise%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0](https://gitee.com/hongjilin/hongs-study-notes/tree/master/%E7%BC%96%E7%A8%8B_%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Promise%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0)
      【小知识】第8期 js中Promise与async/await的用法简介_哔哩哔哩_bilibili
      https://www.bilibili.com/video/BV12E411975X/?spm_id_from=333.788.recommend_more_video.1

      JavaScript两种异步方式(单线程编程语言):

  • 传统回调函数 (Callback Function) 例如 setTimeOut

回调函数缺点: 函数回调地狱 : 一个函数执行完在执行内部另外一个,一层一层嵌套

  • Promise

    需求

通过AJAX请求id ,再根据id请求用户名.再根据用户名,再根据用户名获取email

回调地狱

回调函数中嵌套回调
Promise解决了回调地狱

Promise 概念

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。<br />通俗讲,Promise是一个许诺、承诺,是对未来事情的承诺,承诺不一定能完成,但是无论是否能完成都会有一个结果。

Promise 的基本使用

语法

new Promise(( reslove, reject ) =>{})
  • Promise接受一个函数作为参数
  • 在参数函数中有两个参数
    • resolve: 成功函数
    • reject: 失败函数

      Promise实例

promise实例有两个属性

  • state: 状态
  • result: 结果

    1) Promise的状态

    第一种状态: pending(准备,待解决,进行中)
    第二种状态: fulfilled(已完成,成功)
    第三种状态: rejected(已拒绝,失败)

    2) Promise状态的改变

    示例1

const p = new Promise((resolve, reject) => {
    //resolve(): 调用函数, 使当前Promise对象的状态改成fulfilled
  reslove();
})
console.dir(p) // fulfilled

示例2

const p = new Promise((resolve, reject) => {
  //resolve(): 调用函数, 使当前Promise对象的状态改成fulfilled
  //reject(): 调用函数, 使当前Promise对象的状态改成rejected
  //reslove();
  reject()
})
console.dir(p)
  • resolve(): 调用函数, 使当前Promise对象的状态改成fulFilled
  • reject(): 调用函数,使当前Promise对象状态改成rejected

    Promise状态的改变是一次性的

3) Promise 的结果

示例

const p = new Promise((resolve, reject) => {
  //通过调用reslove,传递参数,改变 当前Promise对象的结果
  resolve("成功的结果");
  //reslove();
  //reject("失败的结果")
})
console.dir(p)

Promise的方法

1) then方法

示例1

const p = new Promise((resolve, reject) => {
  //通过调用reslove,传递参数,改变 当前Promise对象的结果
  //reslove("成功的结果");
  reject("失败的结果")
})

//then方法函数
//参数
//两个参数都是函数
//返回值: 是一个Promise对象
p.then(()=>{
  //当Promise的状态使fulfilled时执行
  console.log("成功的回调")
},()=>{
  // 当Promise的状态时rejected时, 执行
  console.log("失败时调用")
})
console.dir(p)

实例2

const p = new Promise((resolve, reject) => {
  //通过调用reslove,传递参数,改变 当前Promise对象的结果
  //reslove(123);
  reject("失败的结果")
})

//then方法函数
//参数
//两个参数都是函数
//返回值: 是一个Promise对象
p.then((value)=>{
  //当Promise的状态使fulfilled时执行
  console.log("成功的回调",value)
},(err)=>{
  // 当Promise的状态时rejected时, 执行
  console.log("失败时调用",err)
})
console.dir(p)
  • 在then方法的参数函数中,通过形参使用Promise对象的结果

    then方法返回一个新的Promise实例,状态时pending

const p = new Promise((resolve, reject) => {
  //通过调用reslove,传递参数,改变 当前Promise对象的结果
  //reslove(123);
  reject("失败的结果")
})

//then方法函数
//参数
//两个参数都是函数
//返回值: 是一个Promise对象
p.then((value)=>{
  //当Promise的状态使fulfilled时执行
  console.log("成功的回调",value)
},(err)=>{
  // 当Promise的状态时rejected时, 执行
  console.log("失败时调用",err)
})
console.dir(p)

Promise的状态不改变,不会执行then里的方法

//如果Promise的状态改变,then里的方法不会执行
const p = new Promise((resolve, reject) => {

}).then.((value) => {
    console.log("成功")
},(reason) => {
console.log("失败")
})

在then方法中,通过return将返回的Promise实例改为fulfilled状态

//如果Promise的状态改变,then里的方法不会执行
const p = new Promise((resolve, reject) => {

})
const t = p.then((value) => {
    console.log("成功")
  //使用return可以将t实例的状态改为fulfilled
  return 123
},(reason) => {
console.log("失败")
})
t.then.((value) => {
    console.log("成功2",value)
},(reason) => {
console.log("失败2")
})

在then方法中,出现代码错误,将返回的Promise实例改为rejected状态

//如果Promise的状态改变,then里的方法不会执行
const p = new Promise((resolve, reject) => {

})
const t = p.then.((value) => {
    console.log("成功")
  //使用return可以将t实例的状态改为fulfilled
  return 123
},(reason) => {
  //如果这里代码出错,会将t实例的状态改为rejected
console.log("失败")
})
t.then.((value) => {
    console.log("成功2",value)
},(reason) => {
console.log("失败2")
})

2) catch方法

示例

const p = new Promise((resolve, reject) => {
    //reject()
  //console.log(a)
  throw new Error("出错了");
})

//思考: catch中的参数函数在什么时候被执行
//1. 当Promise的状态改为rejcted.被执行
//2. 当Promise执行过程出现代码错误时,被执行
p.catch((reason => {
    console.log("失败", reason)
})
console.log(p);

优化代码

示例

//封装ajax请求
function getData(url, data = {}){
    return new Promise((resolve, reject) => {
      $.ajax({
      //发送请求类型
        type: "GET",
      url: url,
      data: data,
      success: function (res) {
          // 修改Promise状态为成功, 修改Promise的结果res
        resolve(res)
      },
      error:function (res) {
          // 修改Promise的状态为失败,修改Promise的结果res
        reject(res)
      }
    })
  }
}

//调用函数
getData("data1.json")
  .then((data) => {
      //console.log(data)
    const { id } = data
    return getData("data2.json", {id})
  })
  .then((data) => {
      //console.log(data)
    const { usename } = data
    return getData("data3.json", {usename})
  })
  .then((data) => {
      console.log(data)
  })

Promise的实际应用

加载图片

我们可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化

const preloadImage = function (path) {
 return new Promise(function (resolve, reject) {
   const image = new Image();
   image.onload  = resolve;
   image.onerror = reject;
   image.src = path;
 });
};

ECMA17 的 async/ await

一句话概括:
语法糖,让异步操作变简单的,这两关键字基本同根同生,被 async 修饰的 funcation 接受请求必须用 await

1) async 函数

  1. 函数的返回值为 promise 对象
  2. promise 对象的结果由 async 函数执行的返回值决定

    示例

async function main(){
  //1.如果返回值是一个非Promise类型的数据
  // return 一个字符串 数字 布尔值等都是成功的Promise对象
  //2. 如果返回的时一个Promise对象
  // return new Promise((resolve, reject) => {
  //resolve("OK") 返回的是成功Promise对象,状态值:[[PromiseState]]:"fulfilled"
  //reject("Err") 返回的是失败Promise对象,状态值:[[PromiseState]]:"rejected"        
  //3. 抛出异常
  throw "oh No" //状态值:[[PromiseState]]:"rejected",结果是抛出的值
})
}
let result = main();
console.log(result);

返回值是一个Promise对象

2) await 表达式

  1. await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
    2. 如果表达式是 promise 对象, await 返回的是 promise 成功的值
    3. 如果表达式是其它值, 直接将此值作为 await 的返回值

    注意

  2. await 必须写在 async 函数中, 但 async 函数中可以没有 await
    2. 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理

    3) async和await结合

    示例

const fs = require("fs");
const util = require("util");
const mineReadFile = util.promisify(fs.readFile);//promisify转换为Promise形态的函数

async function main(){
  //捕获处理
    try{
      //读取第一个文件的内容
    let data1 = await mineReadFile("./resource/1.html");
    let data2 = await mineReadFile("./resource/2.html");
    let data3 = await mineReadFile("./resource/3.html");
  }catch(e){
      console.log(e):    
  }
}

Axios (ajax 请求库)

axios是一个基于Promise的HTTP客户端,可以用在浏览器和node.js中,向服务器发送AJAX请求进行数据交换,是现在前端最热门的请求工具。

学习资料来源

  1. 尚硅谷Web前端axios入门与源码解析_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1wr4y1K7tq?p=2&spm_id_from=pageDriver
  2. 洪学习笔记: 这是@hongjilin的学习笔记 https://gitee.com/hongjilin/hongs-study-notes/tree/master/%E7%BC%96%E7%A8%8B_%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Ajax%E3%80%81Axios%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0
  3. 【axios】使用json-server 搭建REST API - 使用axios - 自定义axios - 取消请求 - 拦截器_YK菌的博客-CSDN博客 https://blog.csdn.net/weixin_44972008/article/details/114368528

    json-server

    自己起一个本地的mock-server来完全模拟请求以及请求回来的过程。用来快速搭建REST API 的工具包

    使用json-server

  4. 在线文档: https://github.com/typicode/json-server

  5. 下载: npm install -g json-server
  6. 目标根目录下创建数据库 json 文件: db.json
  7. 启动服务器执行命令: json-server —watch db.json

    {
     "posts": [
         { "id": 1, "title": "json-server", "author": "typicode" },
         { "id": 2, "title": "json-server2", "author": "typicode" }
     ],
     "comments": [
         { "id": 1, "body": "some comment", "postId": 1 }
     ],
     "profile": { "name": "typicode" }
    }
    
  8. 使用浏览器访问测试

http://localhost:3000/posts

http://localhost:3000/posts/1

Axios的理解与使用


axios 是什么?

  1. 前端最流行的 ajax 请求库
  2. react/vue 官方都推荐使用 axios 发 ajax 请求
  3. 文档: https://github.com/axios/axios

引入:scriptsrc="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"
安装:npm install axios

axios 特点

  1. 基于 xhr + promise 的异步 ajax 请求库
  2. 浏览器端/node 端都可以使用
  3. 支持请求/响应拦截器
  4. 支持请求取消
  5. 请求/响应数据转换
  6. 批量发送多个请求

    axios 常用语法

  7. axios(config): 通用/最本质的发任意类型请求的方式

  8. axios(url[, config]): 可以只指定 url 发 get 请求
  9. axios.request(config): 等同于 axios(config)
  10. axios.get(url[, config]): 发 get 请求
  11. axios.delete(url[, config]): 发 delete 请求
  12. axios.post(url[, data, config]): 发 post 请求
  13. axios.put(url[, data, config]): 发 put 请求
  14. axios.defaults.xxx: 请求的默认全局配置
  15. axios.interceptors.request.use(): 添加请求拦截器
  16. axios.interceptors.response.use(): 添加响应拦截器
  17. axios.create([config]): 创建一个新的 axios(它没有下面的功能)
  18. axios.Cancel(): 用于创建取消请求的错误对象
  19. axios.CancelToken(): 用于创建取消请求的 token 对象
  20. axios.isCancel(): 是否是一个取消请求的错误
  21. axios.all(promises): 用于批量执行多个异步请求
  22. axios.spread(): 用来指定接收所有成功数据的回调函数的方法

    原理图

    Vue B站学习笔记(二) - 图16

    难点语法的理解和使用

    。。。

    vue脚手架配置代理(两种方式)

    补完课 又回来了。。。如何用 vue-cli 解决 ajax 跨域问题

    image.png

    代理服务器解释

    因为浏览器和服务器之间存在跨域问题,端口号必须一致。但是服务器与服务器之间没有跨域问题,所以通过代理服务器接收到主服务器中的数据,然后浏览器去访问代理服务器的数据。
    这是为了解决浏览器不支持跨域请求,针对的是浏览器。

    方式一

    xhr 的二次封装
    image.png
    在vue.config.js中添加如下配置:

    devServer:{
    proxy:"http://localhost:5000"
    }
    

    说明:

  23. 优点:配置简单,请求资源时直接发给前端(8080)即可。

  24. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  25. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

    方式二

    image.png
    image.png
    编写 vue.config.js 配置具体代理规则:

    module.exports = {
     devServer: {
       proxy: {
       '/api1': {// 匹配所有以 '/api1'开头的请求路径
         target: 'http://localhost:5000',// 代理目标的基础路径
         changeOrigin: true,
         pathRewrite: {'^/api1': ''} // 这个重要!把/api1 换成空,确保转发到资源服务器的路径不带/api1
       },
       '/api2': {// 匹配所有以 '/api2'开头的请求路径
         target: 'http://localhost:5001',// 代理目标的基础路径
         changeOrigin: true,
         pathRewrite: {'^/api2': ''}
       }
     }
    }
    }
    /*
    changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
    changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
    changeOrigin默认值为true
    */
    

    说明:
    1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
    2. 缺点:配置略微繁琐,请求资源时必须加前缀。

    插槽(父组件向子组件指定位置插入html结构)

  26. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

  27. 分类:默认插槽、具名插槽、作用域插槽
  28. 使用方式:

    1. 默认插槽:

      父组件中:
      <Category>
         <div>html结构1</div>
      </Category>
      子组件中:
      <template>
          <div>
             <!-- 定义插槽 -->
             <slot>插槽默认内容...</slot>
          </div>
      </template>
      
    2. 具名插槽: slot由v-slot指令取代了,可以在具名和作用域插槽中使用

      父组件中:
      <Category>
        <!-- 
         <template slot="center">
            <div>html结构1</div>
          </template>
                  !-->
          <template v-slot:footer>
             <div>html结构2</div>
          </template>
      </Category>
      子组件中:
      <template>
          <div>
             <!-- 定义插槽 -->
        <!--      <slot name="center">插槽默认内容...</slot>  !-->
             <slot name="footer">插槽默认内容...</slot>
          </div>
      </template>
      
    3. 作用域插槽:

      1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
      2. 具体编码: ```vue 父组件中:

      <template slot-scope="scopeData">
          <!-- 生成的是h4标题 -->
          <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
      </template>
      

      子组件中:

      ```