初识vue.js随笔(三)

组件封装思想

当没有封装复用思维时,若想做一个tabbar导航栏
TabBar.vue

<!-- 手机导航栏 -->
<template>
  <div id="tab-bar">
    <!-- 一般一个项目只有一个tabbar导航条,所以可以直接用id作一个标识 -->
    <div class="tabbar-item">首页</div>
    <div class="tabbar-item">推荐</div>
    <div class="tabbar-item">购物车</div>
    <div class="tabbar-item">我的</div>
  </div>
</template>

<script>
export default {
  name: 'TabBar',
  data() { 
    return {

    }
  }
 }
</script>

<style>
@import '../../assets/css/base.css';

/* 可以改为引用@import
也可以直接在main.js中使用require('./assets/css/base.css')
body{
  padding:0px;
  margin:0px;
} */
#tab-bar{
  /* 水平展示,flex布局 */
  display:flex;
  background-color:rgb(244, 243, 245);
  /* 一般用f6,且bgc加回车可以简写background-color */
  /* background-color: #f6f6f6; */

  /* position:fixed能够固定布局,设置left,bottem等属性 */
  position:fixed;
  /* 设置left,right属性可以保证占据屏幕整个宽度 */
  left:0;
  right:0;
  bottom:0;


  /* 设置阴影 */
  /* 参数:x方向向右偏移量,y向下偏移量,模糊程度,颜色(最后的.15表示0.15的透明度) */
  box-shadow:0 -1px 5px rgba(100,100,100,.15) 
}
.tabbar-item{
  /* flex:1px可以使flex布局均等分 */
  flex:1px;
  /* text-align让文字居中展示 */
  text-align:center;
  /* 一般tabbar的高度都是49 */
  height:49px;
}
</style>

App.vue中引用

<template>
  <div id="app">
    <tabbar></tabbar>
  </div>
</template>

<script>
import tabbar from './components/tabbar/TabBar'
export default {
  name: 'App',
  components:{
    tabbar
  }
}
</script>

<style>

</style>

开始封装:
将tabbar抽取成一个组件TabBar.vue
将TabBar抽取成多个TabBarItem.vue

在这里插入图片描述
App.vue

<template>
  <div id="app">
    <router-view></router-view>
    <tabbar>
      <!--                      动态传入样式 -->
      <tabbar-item path="/home" activeColor="green">
        <img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
        <div slot="item-text">首页</div>
      </tabbar-item>
      <tabbar-item path="/category" activeColor="green">
        <img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
        <div slot="item-text">分类</div>
      </tabbar-item>
      <tabbar-item path="/shopcar" activeColor="green">
        <img slot="item-icon" src="./assets/img/tabbar/shop_car.svg" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/shop_car_active.svg" alt="">
        <div slot="item-text">购物车</div>
      </tabbar-item>
      <tabbar-item path="/profile" activeColor="green">
        <img slot="item-icon" src="./assets/img/tabbar/my.svg" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/my_active.svg" alt="">
        <div slot="item-text">我的</div>
      </tabbar-item>
    </tabbar>
  </div>
</template>

<script>
import tabbar from './components/tabbar/TabBar'
import tabbarItem from './components/tabbar/TabBarItem'
export default {
  name: 'App',
  components:{
    tabbar,
    tabbarItem
  }
}
</script>

<style>

</style>

TabBar.vue

<!-- 手机导航栏 -->
<template>
  <div id="tab-bar">
    <!-- 一般一个项目只有一个tabbar导航条,所以可以直接用id作一个标识 -->
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'TabBar',
  data() { 
    return {

    }
  }
 }
</script>

<style>
@import '../../assets/css/base.css';

/* 可以改为引用@import
也可以直接在main.js中使用require('./assets/css/base.css')
body{
  padding:0px;
  margin:0px;
} */
#tab-bar{
  /* 水平展示,flex布局 */
  display:flex;
  background-color:rgb(244, 243, 245);
  /* 一般用f6,且bgc加回车可以简写background-color */
  /* background-color: #f6f6f6; */

  /* position:fixed能够固定布局,设置left,bottem等属性 */
  position:fixed;
  /* 设置left,right属性可以保证占据屏幕整个宽度 */
  left:0;
  right:0;
  bottom:0;


  /* 设置阴影 */
  /* 参数:x方向向右偏移量,y向下偏移量,模糊程度,颜色(最后的.15表示0.15的透明度) */
  box-shadow:0 -1px 5px rgba(100,100,100,.15) 
}

</style>

TabBarItem.vue

<!--  -->
<template>
  <div class="tabbar-item" @click="tabbarItemClick">
    <!-- 由于插槽最终是要被替换的,所以一般在外面包一个div,直接对div进行控制 -->
    <div v-if="!isActive"><slot name="item-icon"></slot></div>
    <div v-else><slot name="item-icon-active"></slot></div>
    <!-- <div :class="{active:isActive}"><slot name="item-text"></slot></div> -->
    <div :style="activeStyle"><slot name="item-text"></slot></div>
  </div>
</template>

<script>
export default {
  name: 'TabBarItem',
  props:{
    path:String,
    activeColor:{
      type:String,
      default:'red'
    }
  },
  data() { 
    return {
      // isActive:false
    }
  },
  computed:{
    isActive(){
      //  indexOf()方法可返回某个指定的字符串值在字符串中首次出现的位置。
      // 取到path与当前活跃的path比较,如果未找到就===-1,返回false,相反返回true
      return this.$route.path.indexOf(this.path)!==-1
    },
    activeStyle(){
      return this.isActive ? {color:this.activeColor} : {}
    }
  },
  methods:{
    tabbarItemClick(){
        // this.isActive = !this.isActive
        this.$router.push(this.path)
    }
  }
 }
</script>

<style>
.tabbar-item{
  /* flex:1px可以使flex布局均等分 */
  flex:1px;
  /* text-align让文字居中展示 */
  text-align:center;
  /* 一般tabbar的高度都是49 */
  height:49px;
  font-size:14px;
}
.tabbar-item img{
  width:24px;
  height:24px;
  margin-top:3px;
  vertical-align: middle;
  margin-bottom: 3px;
}
/*为了能动态赋予样式,需要封装 
.active{
  color:yellow;
} */
</style>

还可以进一步对App.vue中的代码进一步抽取
在components文件夹下新建一个文件MainTabBar.vue
MainTabBar.vue

<!--  -->
<template>
  <div>
    <tabbar>
      <!--                      动态传入样式 -->
      <tabbar-item path="/home" activeColor="green">
        <img slot="item-icon" src="../../assets/img/tabbar/home.svg" alt="">
        <img slot="item-icon-active" src="../../assets/img/tabbar/home_active.svg" alt="">
        <div slot="item-text">首页</div>
      </tabbar-item>
      <tabbar-item path="/category" activeColor="green">
        <img slot="item-icon" src="../../assets/img/tabbar/category.svg" alt="">
        <img slot="item-icon-active" src="../../assets/img/tabbar/category_active.svg" alt="">
        <div slot="item-text">分类</div>
      </tabbar-item>
      <tabbar-item path="/shopcar" activeColor="green">
        <img slot="item-icon" src="../../assets/img/tabbar/shop_car.svg" alt="">
        <img slot="item-icon-active" src="../../assets/img/tabbar/shop_car_active.svg" alt="">
        <div slot="item-text">购物车</div>
      </tabbar-item>
      <tabbar-item path="/profile" activeColor="green">
        <img slot="item-icon" src="../../assets/img/tabbar/my.svg" alt="">
        <img slot="item-icon-active" src="../../assets/img/tabbar/my_active.svg" alt="">
        <div slot="item-text">我的</div>
      </tabbar-item>
    </tabbar>
  </div>
</template>

<script>
import tabbar from '../tabbar/TabBar'
import tabbarItem from '../tabbar/TabBarItem'
export default {
  name: '',
  data() { 
    return {

    }
  },
  components:{
    tabbar,
    tabbarItem
  }
 }
</script>

<style>

</style>

App.vue

<template>
  <div id="app">
    <router-view></router-view>
    <main-tab-bar></main-tab-bar>
  </div>
</template>

<script>
import MainTabBar from "./components/tabbar/MainTabBar"
export default {
  name: 'App',
  components:{
    MainTabBar
  }
}
</script>

<style>

</style>

tabbar的文件路径的引用问题

有时候,查找文件路径需要写很多…/…/…/有可能还找不到正确路径
这时,就可以设置一下路径别名
找到build/webpack.base.conf.js文件进行如下配置

  // 专门解决路径相关问题
  resolve: {
    // 导入文件时可以省略'.js','.vue','.json'
    extensions: ['.js', '.vue', '.json'],
    // alias表示别名,通过'@'符号可以找到'src'所在路径
    alias: {
      '@': resolve('src'),
      'assets':resolve('src/assets'),
      'components':resolve('src/components'),
      'views':resolve('src/views')
    }
  },

引用实例
MainTabBar.vue

import tabbar from 'components/tabbar/TabBar'
import tabbarItem from '@/components/tabbar/TabBarItem'

但是对于不是import导入的文件,例如img元素中的属性src,直接写@或其他别名,不会起作用,会报错无法找到文件,需要在前面添加一个波浪符号~
MainTabBar.vue中
不加~符号

      <tabbar-item path="/category" activeColor="green">
        <img slot="item-icon" src="@/assets/img/tabbar/category.svg" alt="">
        <img slot="item-icon-active" src="assets/img/tabbar/category_active.svg" alt="">
        <div slot="item-text">分类</div>
      </tabbar-item>

报错:
在这里插入图片描述
更正:

      <tabbar-item path="/category" activeColor="green">
        <img slot="item-icon" src="~assets/img/tabbar/category.svg" alt="">
        <img slot="item-icon-active" src="~assets/img/tabbar/category_active.svg" alt="">
        <div slot="item-text">分类</div>
      </tabbar-item>

Promise的使用

在这里插入图片描述
一般在什么时候会使用promise呢?
一般在需要进行网络请求时,由于同步容易造成网络阻塞等问题,通常会开启一个异步任务,进行异步操作,最简单的一种异步是回调callback,
但存在*

高耦合,维护困难,回调地狱;每个任务只能指定一个回调函数;如果几个异步操作之间并没有顺序之分,同样也要等待上一个操作执行结束再进行下一个操作。

等问题,且容易产生回调地狱问题,因此,我们更偏向于选择promise来进行异步操作。
(

这里代码如果暂时看不懂可以先看后面的几个问题的代码

)

// 什么情况下会用到promise
//    一旦有异步操作时,就可以将其塞到promise里,异步请求后调用一下resolve方法,别人
//    就可以在then方法中进行操作
      // 简单来说,即一般情况下,当有异步操作时,使用promise对这个即将进行的异步操作进行封装
      //其中流程:new的时候,类似于一个构造函数
                    // (1、保存了一些状态信息  2、执行传入的函数) 
      // 并且例如从网络请求中传过来一些数据时,不要直接在异步请求(如setTimeout函数中)进行操作
      // 而是通过在异步请求中调用resolve()或reject()函数,通过这两个参数函数,数据会自动传递到
      // 后面的.then()函数或.catch()函数中,你可以在then()/catch()函数中操作传过来的数据,简洁明了
new Promise((resolve,reject) => {
  setTimeout(() => {
    // 成功的时候调用resolve,表示解决,进入.then()表示下一步
    // resolve('Hello')

    // 失败的时候调用reject,表示拒绝,进入.catch()表示捕获,捕获异常等
    reject('error message')
  },1000)
}).then((data) => {     //这里的data就是resolve中传过来的参数,有时setTimeout中含有参数data,
  // 这里将打印Hello字符串,因为从resolve传入的是Hello
  console.log(data)      //也可以通过resolve(data),数据会自动作为传入函数then的参数.then(data)
}).catch(err => {
  // 这里打印error message字符串,因为从reject传入的是字符串'error message'
  console.log(err)        
})


// 总而言之,promise是为了写代码更优雅

什么是Promise?

Promise是JS对象,它们用于表示一个异步操作的最终完成 (或失败), 及其结果值.您可以通过使用回调方法或使用Promise执行异步操作来获得结果。
Promise是把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写。是抽象异步处理对象以及对其进行各种操作的组件。
简单来说,也可以看做是对一些异步操作进行的封装

什么是回调地狱
类似嵌套回调,即回调函数里又需要进行回调

在这里插入图片描述
在这里插入图片描述
用setTimeout定时器先来模拟一个异步操作

  <script>
    // 使用setTimeout进行异步操作,1000ms之后会回调setTimeout函数
    // alt+shift+向下或向上箭头可向下/上复制
    // setTimeout(() => {
    //   console.log('hello world')
    //   console.log('hello world')
    //   console.log('hello world')
    //   console.log('hello world')
    // },1000)

    // 使用promise类进行封装
    // 参数是函数(resolve,reject),这两个函数的参数也是函数
    // 延迟1S打印helloworld ,再延迟1s打印hellovue.js,再延迟1s打印hello
    // 这样的操作就陷入了回调地狱
    new Promise((resolve,reject) => {
      setTimeout(() => {
        console.log('hello world')
        console.log('hello world')
        console.log('hello world')
        console.log('hello world')

        setTimeout(() => {
          console.log('hello vue.js')
          console.log('hello vue.js')
          console.log('hello vue.js')
          console.log('hello vue.js')

          setTimeout(() => {
            console.log('hello')
            console.log('hello')
            console.log('hello')
            console.log('hello')
          },1000)
        },1000)
      },1000)
    })

  </script>
    // 为了避免上述这种回调地狱,需要将里面的东西给抽取出来
    // 内部调用resolve()函数,当调用该函数时,函数会去到.then中去执行,then的参数也是一个函数
    // new Promise((resolve,reject) => {
    //   setTimeout(() =>{
    //     resolve()
    //   },1000)
    // }).then(() => {
    //     console.log('hello world')
    //     console.log('hello world')
    //     console.log('hello world')
    //     console.log('hello world')

    //   // 通过返回一个Promise值
    //     return new Promise((resolve,reject) => {
    //       setTimeout(() => {
    //         resolve()
    //       },1000)
    //     }).then(() => {
    //       console.log('hello vue.js')
    //       console.log('hello vue.js')
    //       console.log('hello vue.js')
    //       console.log('hello vue.js')

    //       return new Promise((resolve,reject) => {
    //         setTimeout(() => {
    //           resolve()
    //         },1000)
    //       }).then(() => {
    //         console.log('hello')
    //         console.log('hello')
    //         console.log('hello')
    //         console.log('hello')
    //       })
    //     })
    // })



// 注意,应该是这种形式,
// 即new Promise().then(   return new Promise())  .then(  return new Promise()).then()
// 上面的那种形式变成了then嵌套了,虽然同样能正确展示,应该也是变成回调地狱的一种了????
    new Promise((resolve,reject) => {
      setTimeout(() =>{
        resolve()
      },1000)
    }).then(() => {
        console.log('hello world')
        console.log('hello world')
        console.log('hello world')
        console.log('hello world')

      // 通过返回一个Promise值
        return new Promise((resolve,reject) => {
          setTimeout(() => {
            resolve()
          },1000)
        })
      }).then(() => {
          console.log('hello vue.js')
          console.log('hello vue.js')
          console.log('hello vue.js')
          console.log('hello vue.js')

          return new Promise((resolve,reject) => {
            setTimeout(() => {
              resolve()
            },1000)
          })
        }).then(() => {
            console.log('hello')
            console.log('hello')
            console.log('hello')
            console.log('hello')
          },1000)

  </script>

任何需要等promise的行为放在".then"里面

// 什么情况下会用到promise
//    一旦有异步操作时,就可以将其塞到promise里,异步请求后调用一下resolve方法,别人
//    就可以在then方法中进行操作
      // 简单来说,即一般情况下,当有异步操作时,使用promise对这个即将进行的异步操作进行封装
      //其中流程:new的时候,类似于一个构造函数
                    // (1、保存了一些状态信息  2、执行传入的函数) 
      // 并且例如从网络请求中传过来一些数据时,不要直接在异步请求(如setTimeout函数中)进行操作
      // 而是通过在异步请求中调用resolve()或reject()函数,通过这两个参数函数,数据会自动传递到
      // 后面的.then()函数或.catch()函数中,你可以在then()/catch()函数中操作传过来的数据,简洁明了
new Promise((resolve,reject) => {
  setTimeout(() => {
    // 成功的时候调用resolve,表示解决,进入.then()表示下一步
    // resolve('Hello')

    // 失败的时候调用reject,表示拒绝,进入.catch()表示捕获,捕获异常等
    reject('error message')
  },1000)
}).then((data) => {     //这里的data就是resolve中传过来的参数,有时setTimeout中含有参数data,
  // 这里将打印Hello字符串,因为从resolve传入的是Hello
  console.log(data)      //也可以通过resolve(data),数据会自动作为传入函数then的参数.then(data)
}).catch(err => {
  // 这里打印error message字符串,因为从reject传入的是字符串'error message'
  console.log(err)        
})


// 总而言之,promise是为了写代码更优雅

这里有两篇,其中这篇暂时没找到与作者联系的方式
https://juejin.im/post/5d5120e1f265da03be48ccad
https://zhuanlan.zhihu.com/p/24684803

Promise 的三种状态

sync(Synchronization同步)
async(异步)
在这里插入图片描述
三种状态:

  • pending:等待状态,此时执行promise封装的内部定时器等
  • fulfill:满足状态,当回调了resolve时,就表示处于该状态,并且回调.then()进入成功的下一步
  • reject:拒绝状态,当回调了reject时,就表示处于该状态,并且回调.catch()捕获失败和拒绝

Promise的链式调用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // 需求:
    //    网络请求获得数据:aaa  ->先自己处理
    //    第一层处理:aaa111(要求在数据后面跟上111)  ->自己处理然后提交下一步处理
    //    第二层处理:aaa111222  ->自己处理
    // new Promise((resolve,reject) => {
    //   setTimeout(() => {
    //     resolve('aaa')
    //   },1000)
    // }).then(res => {
    //   // 1、第一次自己处理,res是resolve的简写,表示resolve传入的参数
    //   console.log(res,'第一层的自己处理代码')

    //   // 2、对结果第一次处理,这里的reject没有用到,所以就省略了
    //   return new Promise((resolve) => {
    //     resolve(res + '111')
    //   })
    // }).then(res => {
    //   console.log(res,'第二层的自己处理代码')

    //   return new Promise(resolve => {
    //     resolve(res + '222')
    //   })
    // }).then(res => {
    //   console.log(res, '第三层的自己处理代码')
    // })



    // 简写一:直接return Promise.resolve()
    // new Promise((resolve,reject) => {
    //   setTimeout(() => {
    //     resolve('aaa')
    //   },1000)
    // }).then(res => {
    //   console.log(res,'第一层的自己处理代码')

    //   return Promise.resolve(res + '111')
    // }).then(res => {
    //   console.log(res,'第二层的自己处理代码')

    //   return Promise.resolve(res + '222')
    // }).then(res => {
    //   console.log(res, '第三层的自己处理代码')
    // })



    // 简写二:省略掉Promise.resolve,直接写resolve中的参数作为返回值,其会自动将其封装在Promise中,
    // 并调用resolve方法
    // new Promise((resolve,reject) => {
    //   setTimeout(() => {
    //     resolve('aaa')
    //   },1000)
    // }).then(res => {
    //   console.log(res,'第一层的自己处理代码')

    //   return res + '111'
    // }).then(res => {
    //   console.log(res,'第二层的自己处理代码')

    //   return res + '222'
    // }).then(res => {
    //   console.log(res, '第三层的自己处理代码')
    // })


    // 不一定每次都是resolve,当出现reject时,将不再执行.then,而是直接跳转到.catch执行
    // new Promise((resolve,reject) => {
    //   setTimeout(() => {
    //     resolve('aaa')
    //   },1000)
    // }).then(res => {
    //   console.log(res,'第一层的自己处理代码')

    //   return Promise.reject('errrr')
    // }).then(res => {
    //   console.log(res,'第二层的自己处理代码')

    //   return res + '222'
    // }).then(res => {
    //   console.log(res, '第三层的自己处理代码')
    // }).catch(err => {
    //   console.log(err)
    // })


    // 简写四:可以通过throw直接手动抛出异常,进入catch()
    new Promise((resolve,reject) => {
      setTimeout(() => {
        resolve('aaa')
      },1000)
    }).then(res => {
      console.log(res,'第一层的自己处理代码')

     throw 'errrr message'
    }).then(res => {
      console.log(res,'第二层的自己处理代码')

      return res + '222'
    }).then(res => {
      console.log(res, '第三层的自己处理代码')
    }).catch(err => {
      console.log(err)
    })
  </script>
</body>
</html>

promise的all方法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // 当遇到需要多个异步请求时,例如,需要获取到两次异步请求的结果后才能进行相应的操作,这时就可以
    // 使用promise的all方法来处理
    // 因为没有安装jQuery,无法使用$ajax,这里我使用setTimeout来模拟异步请求
    // promise.all(iterator迭代,即可遍历的)
    // 当两个异步请求的结果都成功获取后,就会自动调用.all([])的.then(),传入一个数组名为results
    Promise.all([
      new Promise((resolve,reject) => {
        setTimeout(() =>{
          resolve({name:'konan',age:17})
        },1000)
      }),
      new Promise((resolve,reject) => {
         setTimeout(() => {
           resolve({first:'ittawa',hobby:'football'})
         })
      })
    ]).then(results => {
      console.log(results[0])
      console.log(results[1])
      console.log(results)
    })
  </script>
</body>
</html>

在这里插入图片描述

Vuex

在这里插入图片描述
你可以想象这样一个场景:
有十多个组件想要共享同一个状态(这里可以先暂时理解为变量,因为变量可以保存状态信息等),如果放到一个组件中的话,其他组件访问可能需要跨越很多其他组件,历经千辛才能获取使用,这样就会造成很多不方便,这时如果能有一个中转站能来统一管理就好了,于是就有了vuex,vuex作为一个状态管理模式,就像一座大宅邸里的一个大管家一样,将少爷主子们(组件)进行集中式管理,并实时管理着一些状态(或变量等),有谁需要,谁就可以直接去大管家那里拿来用,除此之外,vuex还可以实现响应式,即组件不仅可以访问状态或变量,还可以进行更改,并且因为vuex的管理是响应式的,一旦数据更改,界面会实时跟着刷新。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 你可以想象这样一个场景:
有十多个组件想要共享同一个状态(这里可以先暂时理解为变量,因为变
量可以保存状态信息等),如果放到一个组件中的话,其他组件访问可能
需要跨越很多其他组件,历经千辛才能获取使用,这样就会造成很多不方便,
这时如果能有一个中转站能来统一管理就好了,于是就有了vuex,vuex作为一个
状态管理模式,就像一座大宅邸里的一个大管家一样,将少爷主子们(组件)进行
集中式管理,并实时管理着一些状态(或变量等),有谁需要,谁就可以直接去大
管家那里拿来用,除此之外,vuex还可以实现响应式,即组件不仅可以访问状态或
变量,还可以进行更改,并且因为vuex的管理是响应式的,一旦数据更改,界面会
实时跟着刷新。 -->

<script>
  const shareObj = {
    name:'konan'
  }
// 所有组件都自动继承vue的原型,利用prototype原型添加一个共享对象
  Vue.prototype.shareObj = shareObj

// 但是当shareObj中的内容变化时,组件中的数据不会跟着改变,这样就做不到响应式
  Vue.component('cpn1',{
    template:'',
  })

  Vue.component('cpn2',{

  })
  const app = new Vue({
    el:'#app',
    data:{

    }
  })
</script>
</body>
</html>

我们这样做可以实现状态的管理,但这样不会实现响应式,若实现了响应式,这样相当于重复造了一个vuex的轮子
在这里插入图片描述
一般父子组件之间共享状态或变量信息直接使用父子组件通信
当多个页面,甚至几乎所有组件页面都需要使用时,才使用vuex

vuex的使用

vuex是一个插件,所以需要安装,部署时仍然需要它来管理我们的状态信息,所以不是开发时依赖,是生产运行时依赖
进入文件夹,在终端输入

npm install --save vuex

等待下载
安装
可以在main.js中直接vue.use安装,但这样的话main.js中的代码会变得杂乱,因此,我们在src文件夹下新建一个文件夹,通常起名为store(仓库),store下新建文件index.js
src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

// 1、安装插件
Vue.use(Vuex)

// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
  state:{

  },
  mutations:{

  },
  actions:{

  },
  getters:{

  },
  modules:{

  }
})

// 3、导出store独享
// 4、main.js中挂载store

回到main.js中进行挂载

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

Vue.config.productionTip = false

// 挂载后相当于添加了语句Vue.prototype.$store = store,以后就可以全局使用$store

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  router,
  render: h => h(App)
})

使用

简单案例

$store.state.counter

在这里插入图片描述
store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

// 1、安装插件
Vue.use(Vuex)

// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
  state:{
    // 状态
    counter:0
  },
  mutations:{
    // 方法,默认会传入state
    increment(state){
      store.state.counter++
    },
    decrement(state){
      store.state.counter--
    }
  },
  actions:{
    // 异步请求
  },
  getters:{

  },
  modules:{

  }
})

// 3、导出store独享
export default store
// 4、main.js中挂载store

在这里插入图片描述
在这里插入图片描述
main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'

Vue.config.productionTip = false

// 挂载后相当于添加了语句Vue.prototype.$store = store,以后就可以全局使用$store

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  router,
  render: h => h(App)
})

在这里插入图片描述
Hellovuex.vue

<template>
  <div class="hello">
    <h2>HelloVuex中的counter</h2>
    <h1>{{$store.state.counter}}</h1>
    <button @click="add">+</button>
    <button @click="sub">-</button>
  </div>
</template>

<script>
export default {
  name: 'HelloVuex',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods:{
    add(){
      return this.$store.commit('increment')
    },
    sub(){
      return this.$store.commit('decrement')
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

App.vue

<template>
  <div id="app">
    <h2>App中的counter{{$store.state.counter}}</h2>
    
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
</style>

可以看到,即使组件内部没有定义变量,没有进行父子通讯也可以使用counter变量及其他方法

单一状态树

在这里插入图片描述
可以有多个store来管理我们的信息,状态等,index.js中const store1,const store2,const store3等等,但是目前不必要,为了便于管理,我们只用一个store来存储所有的状态信息,需要用时,只需找$store这一个就行

vuex的getters使用

store/index.js

// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
  state:{
    // 状态
    counter:0,
    students:[
      {id:110,name:'one',age:15},
      {id:111,name:'two',age:20},
      {id:112,name:'three',age:10},
      {id:113,name:'four',age:32},
    ]
  },
  mutations:{
    // 方法,默认会传入state
    // mutation可以更改值,但getters一般只是计算并返回值
    increment(state){
      state.counter++
    },
    decrement(state){
      state.counter--
    }
  },
  actions:{
    // 异步请求
  },
  getters:{
    // 类似于单个组件中的计算属性,可以想象成计算器,计算器一般是对一些数据进行处理,计算后返回
    // 1、数据进行计算,同样默认传入参数state
    powerCounter(state){
      // 使用时:$store.getters.powerCounter
      return state.counter *state.counter
    },
    // 2、传参用法:查找students数组中年龄大于age的对象
    // 使用时:$store.getters.moreAgeStu(参数Age:例如13)
    moreAgeStu(state){
      return function(Age){
        return state.students.filter(s => s.age>Age)
      }
    },
    more14Stu(state){
      return state.students.filter(s => s.age>14)
    },
    // 3、还可根据需要添加一个参数getters
    
    moreAgeStuLength(state,getters){
      // 使用时:$store.getters.more14StuLength
      // return getters.more14Stu.length
      // 使用时:$store.getters.moreAgeStuLength(14)
      return Age =>{
        return getters.moreAgeStu(Age).length
      }
    }

    // 使用过程均可查看HelloVuex.vue
  },
  modules:{

  }
})

// 3、导出store独享
export default store
// 4、main.js中挂载store

HelloVuex.vue

  <div class="hello">
    <h2>HelloVuex中的counter</h2>
    <h1>{{$store.state.counter}}</h1>
    <button @click="add">+</button>
    <button @click="sub">-</button>
    <h2>平方{{$store.getters.powerCounter}}</h2>
    <h2>{{$store.getters.moreAgeStu(14)}}</h2>
    <h2>年龄大于Age的人数{{$store.getters.moreAgeStuLength(14)}}</h2>
  </div>

vuex的mutation的使用

通常情况下,vuex要求mutations中是同步方法,异步的请求一般都放到actions
在这里插入图片描述
在这里插入图片描述
store/index.js

  mutations:{
    // 方法,默认会传入state
    // mutation可以更改值,但getters一般只是计算并返回值
    // increment称为事件类型,后面的(state){}是回调函数
    increment(state){
      state.counter++
    },
    decrement(state){
      state.counter--
    },
    // 传入一个参数
    incrementCount(state,count){
      state.counter += count
    },
    // 传入对象作为参数
    addStu(state,stu){
      state.students.push(stu)
    }
  },

完整的store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

// 1、安装插件
Vue.use(Vuex)

// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
  state:{
    // 状态
    counter:0,
    students:[
      {id:110,name:'one',age:15},
      {id:111,name:'two',age:20},
      {id:112,name:'three',age:10},
      {id:113,name:'four',age:32},
    ]
  },
  mutations:{
    // 方法,默认会传入state
    // mutation可以更改值,但getters一般只是计算并返回值
    // increment称为事件类型,后面的(state){}是回调函数
    increment(state){
      state.counter++
    },
    decrement(state){
      state.counter--
    },
    // 传入一个参数
    incrementCount(state,count){
      state.counter += count
    },
    // 传入对象作为参数
    addStu(state,stu){
      state.students.push(stu)
    }
  },
  actions:{
    // 异步请求
  },
  getters:{
    // 类似于单个组件中的计算属性,可以想象成计算器,计算器一般是对一些数据进行处理,计算后返回
    // 1、数据进行计算,同样默认传入参数state
    powerCounter(state){
      // 使用时:$store.getters.powerCounter
      return state.counter *state.counter
    },
    // 2、传参用法:查找students数组中年龄大于age的对象
    // 使用时:$store.getters.moreAgeStu(参数Age:例如13)
    moreAgeStu(state){
      return function(Age){
        return state.students.filter(s => s.age>Age)
      }
    },
    more14Stu(state){
      return state.students.filter(s => s.age>14)
    },
    // 3、还可根据需要添加一个参数getters
    
    moreAgeStuLength(state,getters){
      // 使用时:$store.getters.more14StuLength
      // return getters.more14Stu.length
      // 使用时:$store.getters.moreAgeStuLength(14)
      return Age =>{
        return getters.moreAgeStu(Age).length
      }
    }

    // 使用过程均可查看HelloVuex.vue
  },
  modules:{

  }
})

// 3、导出store独享
export default store
// 4、main.js中挂载store

HelloVuex.vue

<template>
  <div class="hello">
    <!-- mutations使用 -->
    <button @click="add">+</button>
    <button @click="sub">-</button>
          <!-- 传入一个数字作为参数 -->
    <button @click="addCount(5)">+5</button>
          <!-- 传入对象作为参数 -->
    <button @click="addStu()">添加学生</button>
  </div>
</template>


<script>
export default {
  methods:{
    add(){
      // mutation更新用commit
      return this.$store.commit('increment')
    },
    sub(){
      return this.$store.commit('decrement')
    },
    // 更新mutation时传入参数,这个参数称为playload负载
    addCount(counts){
      // mutation更新用commit
      return this.$store.commit('incrementCount',counts)
    },
    addStu(){
      const studentobj = {id:114,name:'five',age:55}
      return this.$store.commit('addStu',studentobj)
    }
  }
}
</script>

在这里插入图片描述
store/index.js

        // 传入一个参数的type写法
        // 这里因为从type提交封装,传入参数是一整个对象,不适宜使用其中的参数,如果传入的对象中还有其他变量?
        // 不可能将其做另一个参数传,因此,这里一般用playload(负载)做对象参数,调用其中的变量
        incrementCount(state,playload){
          state.counter += playload.counts
        },

HelloVuex.vue

    // 更新mutation时传入参数,这个参数称为playload负载
    addCount(counts){
      // mutation更新用commit
      // return this.$store.commit('incrementCount',counts)
      // 这里的counts传入的是一个数


      // 还有另一种提交方式,type方式
      return this.$store.commit({
        type:'incrementCount',  //type:事件类型
        counts:counts           //这里传参数过去时传的是一整个数组
      })
    },

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
组件中定义更新提交update方法
在这里插入图片描述
在这里插入图片描述

mutation的类型常量

有时有许多代码需要用到同一个类型,可能会出错,这时可以创建使用类型常量
在store中创建文件store/mutation_types.js

export const INCREMENT = 'increment'

// 注意这里export default导出的才能直接通过import INCREMENT from '...'导入,
// 这里不是default导出的,所以只能通过import { INCREMENT } from '...'导入
// store的mutations中使用可以见store/index.js

mutations中使用类型常量定义方法
store/index.js

  mutations:{
    // 方法,默认会传入state
    // mutation可以更改值,但getters一般只是计算并返回值
    // increment称为事件类型,后面的(state){}是回调函数
    // increment(state){
    //   state.counter++
    // },

    // 类型常量
    // import { INCREMENT } from './mutation_type'
    [INCREMENT](state){
      state.counter++
    }
  },

使用
Hellovuex.vue

<script>
import {
  INCREMENT
} from '../store/mutation_type.js'
export default {
  methods:{
    add(){
      // mutation更新用commit
      // return this.$store.commit('increment')

      // 使用类型常量
      return this.$store.commit(INCREMENT)
    }
  }
}
</script>

actions的使用

  mutations:{
    updateInfo(state){
      state.info.name='itachi'
    }
  },
  actions:{
    // 异步请求,只要是有异步操作的,必须在actions中多这样一群代码
    // context上下文,是actions中的默认参数
    // actions中commit  mutations中的事件类型
    // 在使用时,vue组件中的methods中要通过this.$store.dispatch('aUpdateInfo')
    aUpdateInfo(context,payload){
      // setTimeout(() => {
      //   context.commit('updateInfo',payload)
      //   console.log(payload.message)
      //   payload.success()
      // })


      // 写法二、
      return new Promise((resolve,reject) => {
        setTimeout(() => {
          context.commit('updateInfo')
          console.log(payload)
          resolve('参数')
        })
      })

      
    }
  },

Hellovuex.vue

<template>
  <div class="hello">
    <!-- actions的使用 -->
    <h2><button @click="updateinfo">修改信息</button></h2>

  </div>
</template>

<script>
import {
  INCREMENT
} from '../store/mutation_type.js'
export default {
  name: 'HelloVuex',
  data () {
    return {
    }
  },
  methods:{
    updateinfo(){
      // this.$store.dispatch('aUpdateInfo','我是payload')
      // this.$store.dispatch('aUpdateInfo',{
      //   message :'我是携带的信息',
      //   success:() => {
      //     console.log('里面已经完成了')
      //   }
      // })


      // 写法二
          this.$store
          .dispatch('aUpdateInfo','我是携带的信息')
          .then(res => {
            console.log(res)
          })
    }
  }
}
</script>


<style scoped>
</style>

vuex中的modules的使用

store/index.js

// 创建modules
const moduleB = {
  state:{},
  getters:{},
  mutations:{},
  // 等等
}

// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
  state:{
    // 状态
    counter:0,
    students:[
      {id:110,name:'one',age:15},
      {id:111,name:'two',age:20},
      {id:112,name:'three',age:10},
      {id:113,name:'four',age:32},
    ],
    info:{
      name:'naruto'
    }
  },
  mutations:{
  },
  actions:{
  },
  getters:{
  },
  modules:{
    // 模块
    // 有时为了能够将某些模块单独抽离出来,可以在modules中写多个模块
    a:{
      state:{
        name:'peien'
      },
      getters:{},
      mutations:{},
      actions:{}
      // 等等
    },
    b:moduleB

    //    <!-- modules的使用:虽然state中没有a,但可以通过modules使用,另外,modules中的mutations的方法
    也是靠methods中的this.$store.commit()提交的,所以模块中的mutations事件类型名称最好
    不要和stores中的mutations中事件类型名称相同 -->
    // {{$store.state.a.name}}
  }
})

getters的使用基本相同,但还可以添加一个参数rootState,表示根的状态,即store的state

actions的使用,传入的参数context是上下文的意思,所以在模块中的context.commit,提交的是模块中的东西,如果想拿到根里的东西时,可以通过root来拿,例如context.rootGetters,rootStates等
在这里插入图片描述

插播一个对象的解构

  <script>
    // 对象的解构
    const obj = {
      name:'zhangsi',
      age:18,
      height:1.88
    }

    // 对象解构写法一
    // const name = obj.name
    // const age = obj.age
    // const height = obj.height

    // 对象的解构写法二
    // 按名字对应
    const {name,age,height} = obj
  </script>

在这里插入图片描述
在这里插入图片描述
具体抽离方法可以查阅官网

网络模块封装axios

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

axios框架的基本使用

安装axios

npm install --save axios

引用axios
main.js中

import axios from 'axios'
new Vue({
  el: '#app',
  store,
  router,
  render: h => h(App)
})

// 在任何地方都可以引用
// 传入一些你要进行的相关网络请求,相关的一些配置即可引用
// 由于可能需要传入很多的一些相关配置,因此,config应该是一个对象
// axios(config)
// 需要有一个服务器作为url为我们返回数据
// httpbin.org,该网站可以为我们做很多请求的模拟,可以做为一个模拟封装,模拟测试的网站
// axios可以使用promise,可以直接使用.then()方法,它会在内部自动调用promise.resolve


// 最简单的一种写法:
// 默认情况下,如果只传一个url的话,默认执行get请求,用method可以指定请求类型
axios({
  url:'http://httpbin.org/',
  method:'get'
}).then(res => {
  // res这里是result的一个简称
  console.log(res)
})

// 写法二
axios.get()
axios.post()

// 写法三:专门针对get请求的参数拼接
// 有时候,如果不想把参数直接拼接在url后面,还可以通过params参数来传递参数
axios({
  url:'http://httpbin.org/',
  method:'get',
  params:{
    type:'pop',
    page:1
  }
}).then(res => {
  // res这里是result的一个简称
  console.log(res)
})

axios发送并发请求

main.js

// axios的并发请求
// 类似promise
// axios.all([axios(),axios()]).then(resultes => {})
axios.all([
  axios({
    url:''
  }),
  axios({
    url:''
  })]).then(results => {
    console.log(results)
    console.log(results[0])
    console.log(results[1])
  })

  // 如果要分开使用的话,可以通过axios.spread将数组[res1,res2]展开为res1,res2
  // 这里的res1<=>(等价于)results[0],res2<=>(等价于)results[1]
  axios.all([
    axios({
      url:''
    }),
    axios({
      url:''
    })
  ]).then(axios.spread((res1,res2) => {
    console.log(res1)
    console.log(res2)
  }))
  // 等价于 ]).then(axios.spread([res1,res2] => {,这里可以理解为相当于对results数组的解构

axios的全局配置

同样,有时,有的axios配置有一些共同的地方,可以通过default设置为全局配置,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

axios创建实例

  // 创建axios实例instance,由于可能某些url不是用的全局配置中的baseURL,这时,可以创建多个axios实例,
  // 相当于封装成模块
const instance1 = axios.create({
  baseURL:'http://123.207.32.33:8081',
  timeout:5000
})

instance1({
  url:'/home/multidata'
}).then(res => {
  console.log(res)
})

instance1({
  url:'/home/data',
  params:{
    type:'roll',
    page:3
  }
}).then(res => {
  console.log(res)
})
// 当有另外几个网页对不同的服务器请求时,可以再创建实例
const instance2 = axios.create({})

axios的封装

直接引用
main.js下配置了axios,在组件中就可以直接引用axios
例如:
Helloaxios.vue

<template>
  <div class="hello">
    <p> {{result}} </p>
  </div>
</template>

<script>
  // 直接在这里引用axios
import axios from 'axios'
export default {
  name: 'Helloaxios',
  data () {
    return {
      result:''
    }
  },
  created(){
    axios({
      url:'http://httpbin.org/'
    }).then(res => {
      // console.log(res)
      this.result = res
    })
  },
  methods:{

  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

在src文件夹下新建文件夹network,再在network下新建文件require.js,用来封装网络请求
src/network/require.js

// 导入
import axios from 'axios'

// 由于要封装多个实例,就不用export default了,default只能导出一个实例,因此这里使用function
// export function request(config,success,failure){
//   // 1、创建axios实例
//   const instance = axios.create({
//     baseURL:'httpbin.org',
//     timeout:5000
//   })
//   //2、发送真正的网络请求 
//   instance(config)
//   .then(res => {
//     console.log(res)
//     // 通过前面设置的success和failure函数将结果回调出去
//     success(res)
//   })
//   .catch(err => {
//     failure(err)
//   })


//   // 3、使用,看main.js中request模块的封装
// }
















// 写法二
// export function request(config){
//   // 1、创建axios实例
//   const instance = axios.create({
//     baseURL:'httpbin.org',
//     timeout:5000
//   })
//   //2、发送真正的网络请求 
//   instance(config.baseConfig)
//   .then(res => {
//     console.log(res)
//     // 通过前面设置的success和failure函数将结果回调出去
//     config.success(res)
//   })
//   .catch(err => {
//     config.failure(err)
//   })


//   // 3、使用,看main.js中request模块的封装写法二
// }

















// 写法三
// export function request(config){
//   return new Promise((resolve,reject) => {
//     // 1、创建axios实例
//     const instance = axios.create({
//       baseURL:'httpbin.org',
//       timeout:5000
//     })

//     // 发送真正的网络请求
//     instance(config)
//     .then(res => {
//       resolve(res)
//     })
//     .catch(err =>{
//       reject(err)
//     })
//   })
// }










// 写法四
export function request(config){
  // 1、创建axios的实例
  const instance = axios.create({
    baseURL:'httpbin.org',
    timeout:5000
  })

  // 发送真正的网络请求
  // 查看axios.create()的源码,发现返回值instance本身就是一个promise.
  return instance(config)
}

main.js


// 5、request模块的封装
// 前面步骤查看network/request.js写法一
// import {request} from './network/request'

// request({
//   // url:'/home/multidata'
//   url:''
// },
// // 作为request模块函数的success参数
// res => {
//   console.log(res)
// },
// // 作为request模块函数的failure参数
// err => {
//   console.log(err)
// })







// 写法二
// request({
//   baseConfig:{

//   },
//   success(res){

//   },
//   failure(err){

//   }
// })




// // 对应的写法三
// request({
//   url:''
// }).then(res => {

// }).catch(err => {

// })





// 对应的写法四
request({
  url:''
}).then(res => {

}).catch(err => {
  
})

什么是回调
:简单来说,就是把某一个函数作为参数传给另一个函数

axios拦截器的使用

拦截器:
当我们向网络发送请求时,可能需要在请求过程中进行拦截,获取一些信息或进行一些操作
network/request.js

// 添加了拦截器
export function request(config){
  // 1、创建axios的实例
  const instance = axios.create({
    baseURL:'http://httpbin.org/',
    // timeout:5000
  })

  // 2、axios的拦截器设置
  // 如果是全局的拦截,可以直接用axios.interceptors《==拦截器的意思,
  // 但这里只是一个实例的拦截,所以只用instance.interceptors
  // 拦截请求instance.interceptors.request.use()
  // 拦截响应instance.interceptors.response.use()
  // 其中,use()传入的两个参数均为函数,config表示请求成功时的拦截,
  // 注意不是拦截成功,而是请求/响应成功时返回的拦截信息去向

          // 2.1请求拦截的作用
    instance.interceptors.request.use(config =>{
      console.log(config)
            //1、比如config中的一些信息不符合服务器的要求,这时可以通过拦截器来查看并更改调试
            
            //2、比如每次发送网络请求时,都希望在界面中显示一个请求的图标,如网页加载中的那个循环转着的小圈

            //3、某些网络请求(比如登录(须携带token)),必须携带一些特殊的信息,可以通过请求拦截查看是否携带了
      // 使用后一定记得返回config,否则拿不到信息会报错
            return config
    },err => {
      console.log(err)
    })


          // 2.2、响应拦截
          // ,响应拦截也有响应成功和响应失败两种情况这里传入的参数是result,因为服务器已经响应过了,所以拿到的是个结果
    instance.interceptors.response.use(res => {
      console.log(res)
      // 这里可以只返回res.data
      return res
    },err => {
      console.log(err)
    })


  // 3、发送真正的网络请求
  // 查看axios.create()的源码,发现返回值instance本身就是一个promise.
  return instance(config)
}

main.js中的使用

// 有拦截器时对应的使用写法也是如此,如果baseURL后面还有其他参数的话可以直接在这里的url中进行拼接
request({
  url:''
}).then(res => {

}).catch(err => {
  
})
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值