• Vue(六)——使用脚手架(3)


    目录

    webStorage

    localStorage

    sessionStorage

    todolist案例中使用

    组件自定义事件

    绑定

    解绑

    总结

    全局事件总线

    消息发布与订阅

    nextTick

    过渡与动画


    webStorage

            这不是vue团队开发的,不需要写在xx.vue当中,只需写在xx.html当中即可。

    什么是浏览器本地存储,下面举一个例子:

    也就是说浏览器帮你本地缓存点东西

    localStorage

    保存数据:

    读取数据

    删除和清空

    完整代码:

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8" />
    5. <title>localStoragetitle>
    6. head>
    7. <body>
    8. <h2>localStorageh2>
    9. <button onclick="saveData()">点我保存一个数据button>
    10. <button onclick="readData()">点我读取一个数据button>
    11. <button onclick="deleteData()">点我删除一个数据button>
    12. <button onclick="deleteAllData()">点我清空一个数据button>
    13. <script type="text/javascript" >
    14. let p = {name:'张三',age:18}
    15. function saveData(){
    16. //localStorage.setItem 键值对的形式保存数据
    17. localStorage.setItem('msg','hello!!!')
    18. localStorage.setItem('msg2',666) //toString()方法转换为字符串
    19. localStorage.setItem('person',JSON.stringify(p)) //把对象转化为字符串
    20. }
    21. function readData(){
    22. //将读取的数据输出到控制台
    23. console.log(localStorage.getItem('msg'))
    24. console.log(localStorage.getItem('msg2'))
    25. const result = localStorage.getItem('person')
    26. console.log(JSON.parse(result))
    27. // console.log(localStorage.getItem('msg3'))
    28. }
    29. function deleteData(){
    30. localStorage.removeItem('msg2')
    31. }
    32. function deleteAllData(){
    33. localStorage.clear()
    34. }
    35. script>
    36. body>
    37. html>

            localStorage最大的特点是几十把浏览器关闭掉它也不会消失,但用户他在浏览器设置里主动清空缓存也会消失。

    sessionStorage

    他所有的api和localStorage很像

    完整代码:

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8" />
    5. <title>sessionStoragetitle>
    6. head>
    7. <body>
    8. <h2>sessionStorageh2>
    9. <button onclick="saveData()">点我保存一个数据button>
    10. <button onclick="readData()">点我读取一个数据button>
    11. <button onclick="deleteData()">点我删除一个数据button>
    12. <button onclick="deleteAllData()">点我清空一个数据button>
    13. <script type="text/javascript" >
    14. let p = {name:'张三',age:18}
    15. function saveData(){
    16. sessionStorage.setItem('msg','hello!!!')
    17. sessionStorage.setItem('msg2',666)
    18. sessionStorage.setItem('person',JSON.stringify(p))
    19. }
    20. function readData(){
    21. console.log(sessionStorage.getItem('msg'))
    22. console.log(sessionStorage.getItem('msg2'))
    23. const result = sessionStorage.getItem('person')
    24. console.log(JSON.parse(result))
    25. // console.log(sessionStorage.getItem('msg3'))
    26. }
    27. function deleteData(){
    28. sessionStorage.removeItem('msg2')
    29. }
    30. function deleteAllData(){
    31. sessionStorage.clear()
    32. }
    33. script>
    34. body>
    35. html>

    会话存储的特点是浏览器一关就没有了。

    webStorage

    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案例中使用

    App.vue当中初始化时读数据,添加todo时保存数据

    1. <template>
    2. <div id="root">
    3. <div class="todo-container">
    4. <div class="todo-wrap">
    5. <MyHeader :addTodo="addTodo"/>
    6. <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
    7. <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
    8. div>
    9. div>
    10. div>
    11. template>
    12. <script>
    13. import MyHeader from './components/MyHeader'
    14. import MyList from './components/MyList'
    15. import MyFooter from './components/MyFooter.vue'
    16. export default {
    17. name:'App',
    18. components:{MyHeader,MyList,MyFooter},
    19. data() {
    20. return {
    21. //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
    22. todos:JSON.parse(localStorage.getItem('todos')) || []
    23. }
    24. },
    25. methods: {
    26. //添加一个todo
    27. addTodo(todoObj){
    28. this.todos.unshift(todoObj)
    29. },
    30. //勾选or取消勾选一个todo
    31. checkTodo(id){
    32. this.todos.forEach((todo)=>{
    33. if(todo.id === id) todo.done = !todo.done
    34. })
    35. },
    36. //删除一个todo
    37. deleteTodo(id){
    38. this.todos = this.todos.filter( todo => todo.id !== id )
    39. },
    40. //全选or取消全选
    41. checkAllTodo(done){
    42. this.todos.forEach((todo)=>{
    43. todo.done = done
    44. })
    45. },
    46. //清除所有已经完成的todo
    47. clearAllTodo(){
    48. this.todos = this.todos.filter((todo)=>{
    49. return !todo.done
    50. })
    51. }
    52. },
    53. watch: {
    54. //监视todos,只要todos发生变化,那么value就是最新的todos
    55. todos:{
    56. deep:true,
    57. handler(value){
    58. localStorage.setItem('todos',JSON.stringify(value))
    59. }
    60. }
    61. },
    62. }
    63. script>
    64. <style>
    65. /*base*/
    66. body {
    67. background: #fff;
    68. }
    69. .btn {
    70. display: inline-block;
    71. padding: 4px 12px;
    72. margin-bottom: 0;
    73. font-size: 14px;
    74. line-height: 20px;
    75. text-align: center;
    76. vertical-align: middle;
    77. cursor: pointer;
    78. box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    79. border-radius: 4px;
    80. }
    81. .btn-danger {
    82. color: #fff;
    83. background-color: #da4f49;
    84. border: 1px solid #bd362f;
    85. }
    86. .btn-danger:hover {
    87. color: #fff;
    88. background-color: #bd362f;
    89. }
    90. .btn:focus {
    91. outline: none;
    92. }
    93. .todo-container {
    94. width: 600px;
    95. margin: 0 auto;
    96. }
    97. .todo-container .todo-wrap {
    98. padding: 10px;
    99. border: 1px solid #ddd;
    100. border-radius: 5px;
    101. }
    102. style>

    注意在初始化的时候,如果localStorage没有东西为null的时候(因为footer需要计算todos.length),需要写成如下形式(如果为空选择空数组)

    监视的时候要深度监视,不止要监测数组是否变化,还要检测数组里边某一项的属性。

    组件自定义事件

            自定义事件就是区别于js里面的内置事件而存在,然而js里面的内置事件是给HTML元素用的,而自定义组件事件是给组件用的。

    绑定

    现在提出一个需求,点击按钮就将school里面的属性放在父组件APP当中。

    App.vue

    School.vue

     现在换成自定义组件的方式:

            v-on在student的组件标签上,所以说是给student的组件对象vue component(VC)身上绑定了一个事件,事件名叫做atguigu,调用demo函数。给谁(Student)绑定的事件就找谁(Student的实例对象vc)触发。

            怎么去触发?借助$emit(),参数传入要触发的事件名atguigu。

    还有一种方式

    灵活性更强,可以延迟三秒触发

     加上once表示只能触发一次(按钮只能点击一次)

    触发事件同时可以继续传数据接收数据

            一般来说不用这么多形参去接收,一种方法是包装成对象传,另一种方法如下(ES6写法,a是数组)

    src/App.vue:

    1. <template>
    2. <div class="app">
    3. <School :getSchoolName="getSchoolName"/>
    4. <Student ref="student"/>
    5. div>
    6. template>
    7. <script>
    8. import Student from './components/Student.vue'
    9. import School from './components/School.vue'
    10. export default {
    11. name:'App',
    12. components: { Student,School },
    13. methods:{
    14. getSchoolName(name){
    15. console.log("已收到学校的名称:"+name)
    16. },
    17. getStudentName(name){
    18. console.log("已收到学生的姓名:"+name)
    19. }
    20. },
    21. mounted(){
    22. this.$refs.student.$on('jojo',this.getStudentName)
    23. }
    24. }
    25. script>
    26. <style scoped>
    27. .app{
    28. background-color: gray;
    29. padding: 5px;
    30. }
    31. style>

    src/components/Student.vue:

    1. <template>
    2. <div class="student">
    3. <h2>学生姓名:{{name}}h2>
    4. <h2>学生性别:{{sex}}h2>
    5. <button @click="sendStudentName">点我传递学生姓名button>
    6. div>
    7. template>
    8. <script>
    9. export default {
    10. name:'Student',
    11. data() {
    12. return {
    13. name:'zs',
    14. sex:'男'
    15. }
    16. },
    17. methods:{
    18. sendStudentName(){
    19. this.$emit('zs',this.name)
    20. }
    21. }
    22. }
    23. script>
    24. <style scoped>
    25. .student{
    26. background-color: chartreuse;
    27. padding: 5px;
    28. margin-top: 30px;
    29. }
    30. style>

    解绑

    你给谁绑的自定义事件就给谁解绑去

    1.利用$off

    如果一个组件被销毁了,那么他身上的自定义事件也都不能用了(vc自我销毁)

    vm自我销毁

    src/App.vue:

    1. <template>
    2. <div class="app">
    3. <Student @zs="getStudentName"/>
    4. div>
    5. template>
    6. <script>
    7. import Student from './components/Student.vue'
    8. export default {
    9. name:'App',
    10. components: { Student },
    11. methods:{
    12. getStudentName(name){
    13. console.log("已收到学生的姓名:"+name)
    14. }
    15. }
    16. }
    17. script>
    18. <style scoped>
    19. .app{
    20. background-color: gray;
    21. padding: 5px;
    22. }
    23. style>

    src/components/Student.vue:

    1. <template>
    2. <div class="student">
    3. <h2>学生姓名:{{name}}h2>
    4. <h2>学生性别:{{sex}}h2>
    5. <button @click="sendStudentName">点我传递学生姓名button>
    6. <button @click="unbind">解绑自定义事件button>
    7. div>
    8. template>
    9. <script>
    10. export default {
    11. name:'Student',
    12. data() {
    13. return {
    14. name:'zs',
    15. sex:'男'
    16. }
    17. },
    18. methods:{
    19. sendStudentName(){
    20. this.$emit('zs',this.name)
    21. },
    22. unbind(){
    23. // 解绑一个自定义事件
    24. // this.$off('zs')
    25. // 解绑多个自定义事件
    26. // this.$off(['zs'])
    27. // 解绑所有自定义事件
    28. this.$off()
    29. }
    30. }
    31. }
    32. script>
    33. <style scoped>
    34. .student{
    35. background-color: chartreuse;
    36. padding: 5px;
    37. margin-top: 30px;
    38. }
    39. style>

    总结

    谁触发了atguigu事件,那么这个事件当中的this就是谁。

    为啥这样写没问题

            Vue之前给定了一个承诺,如果这个函数写在methods里面,并且用的是普通函数,那么给你一个承诺就是这个函数的this一定是APP的组件实例对象。最终的this是有所改变的。

            组件上也可以绑定原生DOM事件,需要使用native修饰符

    如果不加修饰符他会认为是自定义事件。

    加上修饰符即可

    组件的自定义事件

    1. 一种组件间通信的方式,适用于:

    子组件 ===> 父组件

    (在父组件中给子组件绑定事件)

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

    3. 绑定自定义事件:

        1. 第一种方式,在父组件中:``````  或 ``````

        2. 第二种方式,在父组件中:

            ```js

           

            ......

            mounted(){

               this.$refs.xxx.$on('atguigu',this.test)

            }

            ```

        3. 若想让自定义事件只能触发一次,可以使用```once```修饰符,或```$once```方法。

    4. 触发自定义事件:```this.$emit('atguigu',数据)```    

    5. 解绑自定义事件```this.$off('atguigu')```

    6. 组件上也可以绑定原生DOM事件,需要使用```native```修饰符。

    7. 注意:通过```this.$refs.xxx.$on('atguigu',回调)```绑定自定义事件时,回调要么配置在methods中要么用箭头函数,否则this指向会出问题!

    全局事件总线

            可以实现任意组件之间的通信,APP和abcd组件结构如下所示,其中有一个粉色的X组件,它不属于任何一个组件,其中在a组件里面写一些代码,给x绑定自定义事件demo,则这个自定义事件的回调就留在了a里面。

            如果d组件想给a组件传递数据,在d组件里面写一些代码去触发x身上的demo自定义事件,并且在触发数据的同时再带点数据过去,例如666,X组件上的demo事件被触发了,Demo事件所对应的回调也被执行了。回调被执行了,则传递的数据666也以参数的形式来到a组件里了。

            如果b组件想收到别人传过来的数据,在b组件里写一些代码来绑定x组件的自定义事件,比如说事件的名字叫做test,由于是在b组建里面给x组件绑定事件,所以Test所对应的回调就留在了b组件里面。如果d组件想给b组件传递数据,则只需要在d组件里面编写一些代码去触发x组建你的test事件,也可以携带参数。

            如果c组件想收到数据,则可以在c组件里面写一些代码来,绑定x组件的自定义事件,比如叫做test2,则这个事件的回调函数也留在了c组件。如果a想给c传递一些数据,那么只需要在a组件里面编写一些代码来触发x组件当中的test2事件。

    1. 所有的组件对象都必须能看见他

    2. 这个对象必须能够使用$on$emit$off方法去绑定、触发和解绑事件

            {a:1,b:2}只是一个普通的Object对象,他没有相应的绑定或者解绑方法,$on$emit$off方法都在vue的原型对象(Vue.prototype)上,原型对象的方法是给vm或者vc用的,所以要么写vm,要么写vc。

    student给school传递姓名

     School.vue

    1. <template>
    2. <div class="school">
    3. <h2>学校名称:{{name}}h2>
    4. <h2>学校地址:{{address}}h2>
    5. div>
    6. template>
    7. <script>
    8. export default {
    9. name:'School',
    10. data() {
    11. return {
    12. name:'尚硅谷',
    13. address:'北京',
    14. }
    15. },
    16. mounted() {
    17. // console.log('School',this)
    18. this.$bus.$on('hello',(data)=>{
    19. console.log('我是School组件,收到了数据',data)
    20. })
    21. },
    22. //销毁之前把那个傀儡事件给解绑
    23. beforeDestroy() {
    24. this.$bus.$off('hello')
    25. },
    26. }
    27. script>
    28. <style scoped>
    29. .school{
    30. background-color: skyblue;
    31. padding: 5px;
    32. }
    33. style>

     student.vue

    1. <template>
    2. <div class="student">
    3. <h2>学生姓名:{{name}}h2>
    4. <h2>学生性别:{{sex}}h2>
    5. <button @click="sendStudentName">把学生名给School组件button>
    6. div>
    7. template>
    8. <script>
    9. export default {
    10. name:'Student',
    11. data() {
    12. return {
    13. name:'张三',
    14. sex:'男',
    15. }
    16. },
    17. mounted() {
    18. // console.log('Student',this.x)
    19. },
    20. methods: {
    21. sendStudentName(){
    22. this.$bus.$emit('hello',this.name)
    23. }
    24. },
    25. }
    26. script>
    27. <style lang="less" scoped>
    28. .student{
    29. background-color: pink;
    30. padding: 5px;
    31. margin-top: 30px;
    32. }
    33. style>

    App.vue

    1. <template>
    2. <div class="app">
    3. <h1>{{msg}}h1>
    4. <School/>
    5. <Student/>
    6. div>
    7. template>
    8. <script>
    9. import Student from './components/Student'
    10. import School from './components/School'
    11. export default {
    12. name:'App',
    13. components:{School,Student},
    14. data() {
    15. return {
    16. msg:'你好啊!',
    17. }
    18. }
    19. }
    20. script>
    21. <style scoped>
    22. .app{
    23. background-color: gray;
    24. padding: 5px;
    25. }
    26. style>

     main.js

    1. //引入Vue
    2. import Vue from 'vue'
    3. //引入App
    4. import App from './App.vue'
    5. //关闭Vue的生产提示
    6. Vue.config.productionTip = false
    7. //创建vm
    8. new Vue({
    9. el:'#app',
    10. render: h => h(App),
    11. //借助这个钩子,此时还没有模板解析。无法通过vm中的data数据,methods方法
    12. beforeCreate() {
    13. //bus有总线的意思
    14. Vue.prototype.$bus = this //安装全局事件总线
    15. },
    16. })

    全局事件总线(GlobalEventBus)

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

    2. 安装全局事件总线:

       ```js

       new Vue({

          ......

          beforeCreate() {

             Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm

          },

           ......

       })

       ```

    3. 使用事件总线:

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

          ```js

          methods(){

            demo(data){......}

          }

          ......

          mounted() {

            this.$bus.$on('xxxx',this.demo)

          }

          ```

            2. 提供数据:```this.$bus.$emit('xxxx',数据)```

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

    消息发布与订阅

    需要数据的人订阅消息,提供数据的人发送消息。

    School当中订阅消息

    student发布消息

    订阅消息中回调函数两个参数,一个是消息名,另一个是数据

     如果当前组件要被销毁就要取消订阅,通过id取消订阅,所以订阅的时候返回一个id并放在this里

    消息订阅与发布(pubsub)

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

    2. 使用步骤:

    •    1. 安装pubsub:```npm i pubsub-js```
    •    2. 引入: ```import pubsub from 'pubsub-js'```
    •    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
      1.   methods(){
      2.         demo(data){......}
      3.       }
      4.       ......
      5.       mounted() {
      6.         this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      7.       }
    •    4. 提供数据:```pubsub.publish('xxx',数据)```
    •    5. 最好在beforeDestroy钩子中,用```PubSub.unsubscribe(pid)```去取消订阅。

    nextTick

    $nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行

    1. 语法:```this.$nextTick(回调函数)```

    2. 作用:在下一次 DOM 更新结束后执行其指定的回调。

    3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

    过渡与动画

    Test.vue

    1. <template>
    2. <div>
    3. <button @click="isShow = !isShow">显示/隐藏button>
    4. <transition name="hello" appear>
    5. <h1 v-show="isShow">你好啊!h1>
    6. transition>
    7. div>
    8. template>
    9. <script>
    10. export default {
    11. name:'Test',
    12. data() {
    13. return {
    14. isShow:true
    15. }
    16. },
    17. }
    18. script>
    19. <style scoped>
    20. h1{
    21. background-color: orange;
    22. }
    23. .hello-enter-active{
    24. animation: atguigu 0.5s linear;
    25. }
    26. .hello-leave-active{
    27. animation: atguigu 0.5s linear reverse;
    28. }
    29. @keyframes atguigu {
    30. from{
    31. transform: translateX(-100%);
    32. }
    33. to{
    34. transform: translateX(0px);
    35. }
    36. }
    37. style>

    Test2.vue

    1. <template>
    2. <div>
    3. <button @click="isShow = !isShow">显示/隐藏button>
    4. <transition-group name="hello" appear>
    5. <h1 v-show="!isShow" key="1">你好啊!h1>
    6. <h1 v-show="isShow" key="2">尚硅谷!h1>
    7. transition-group>
    8. div>
    9. template>
    10. <script>
    11. export default {
    12. name:'Test',
    13. data() {
    14. return {
    15. isShow:true
    16. }
    17. },
    18. }
    19. script>
    20. <style scoped>
    21. h1{
    22. background-color: orange;
    23. }
    24. /* 进入的起点、离开的终点 */
    25. .hello-enter,.hello-leave-to{
    26. transform: translateX(-100%);
    27. }
    28. .hello-enter-active,.hello-leave-active{
    29. transition: 0.5s linear;
    30. }
    31. /* 进入的终点、离开的起点 */
    32. .hello-enter-to,.hello-leave{
    33. transform: translateX(0);
    34. }
    35. style>

     Test3.vue

    1. <template>
    2. <div>
    3. <button @click="isShow = !isShow">显示/隐藏button>
    4. <transition-group
    5. appear
    6. name="animate__animated animate__bounce"
    7. enter-active-class="animate__swing"
    8. leave-active-class="animate__backOutUp"
    9. >
    10. <h1 v-show="!isShow" key="1">你好啊!h1>
    11. <h1 v-show="isShow" key="2">尚硅谷!h1>
    12. transition-group>
    13. div>
    14. template>
    15. <script>
    16. import 'animate.css'
    17. export default {
    18. name:'Test',
    19. data() {
    20. return {
    21. isShow:true
    22. }
    23. },
    24. }
    25. script>
    26. <style scoped>
    27. h1{
    28. background-color: orange;
    29. }
    30. style>

    App.vue

    1. <template>
    2. <div>
    3. <Test/>
    4. <Test2/>
    5. <Test3/>
    6. div>
    7. template>
    8. <script>
    9. import Test from './components/Test'
    10. import Test2 from './components/Test2'
    11. import Test3 from './components/Test3'
    12. export default {
    13. name:'App',
    14. components:{Test,Test2,Test3},
    15. }
    16. script>

     main.js

    1. //引入Vue
    2. import Vue from 'vue'
    3. //引入App
    4. import App from './App.vue'
    5. //关闭Vue的生产提示
    6. Vue.config.productionTip = false
    7. //创建vm
    8. new Vue({
    9. el:'#app',
    10. render: h => h(App),
    11. beforeCreate() {
    12. Vue.prototype.$bus = this
    13. },
    14. })

     Vue封装的过度与动画

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

    2. 图示:

    3. 写法:

       1. 准备好样式:

          - 元素进入的样式:

            1. v-enter:进入的起点

            2. v-enter-active:进入过程中

            3. v-enter-to:进入的终点

          - 元素离开的样式:

            1. v-leave:离开的起点

            2. v-leave-active:离开过程中

            3. v-leave-to:离开的终点

       2. 使用``````包裹要过度的元素,并配置name属性:

    vue

    1.       <transition name="hello">
    2.          <h1 v-show="isShow">你好啊!h1>
    3.       transition>

       3. 备注:若有多个元素需要过度,则需要使用:``````,且每个元素都要指定```key```值。

  • 相关阅读:
    7.synchronized锁的应用
    QT源码拾贝6-10(qwindowswindow)
    C# AutoCADDxfName
    python -- PyQt5(designer)中文详细教程(五)对话框
    【教学类-19-01】20221127《ABAB规律排序-A4竖版2份》(中班)
    java.lang.Float类下doubleValue()方法具有什么功能呢?
    vue实现循环发起多个异步请求——Promise.all()与Promise.race()
    C++ float的if比较
    Spring | 异常处理最佳实践
    代理模式和静态代理
  • 原文地址:https://blog.csdn.net/m0_52601969/article/details/127842914