vue-cli中学习vue

news2024/12/24 20:34:43

vue部分知识

大部分学习内容及代码在gitee仓库

生命周期

基本介绍

生命周期描述
beforeCreate组件实例被创建之初
created组件实例已经完全创建
beforeMount组件挂载之前
mounted组件挂载到实例上去之后
beforeUpdate组件数据发生变化,更新之前
updated组件数据更新之后
beforeDestroy组件实例销毁之前
destroyed主键实例销毁之后

整体流程

在这里插入图片描述

vue-router

vue-router的基本学习使用

简单安装配置vue-router

  1. 安装vue-router

    npm install vue-router --save
    
  2. 新建router文件夹,router文件夹下新建index.js文件

    index.js

    //配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    
    const routes = [
    
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes
    })
    
    //3.将router对象传入到Vue实例
    export default router
    
    
    
  3. main.js中引入vue-router的实例,以及Vue实例中挂载路由实例

    main.js

    import Vue from 'vue'
    import App from './App'
    import router from "./router/index";
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
    
    
  4. 基本步骤完成

路由映射配置与呈现

组件中的内容会动态显示在<router-view/>的位置,其作用就是充当占位

  1. 创建路由组件

    Home.vue

    <template>
      <div>欢迎进入home界面</div>
    </template>
    
    <script>
    export default {
      name: "Home"
    }
    </script>
    
    <style scoped>
    
    </style>
    
    

    About.vue

    <template>
      <div>欢迎进入about界面</div>
    </template>
    
    <script>
    export default {
      name: "About"
    }
    </script>
    
    <style scoped>
    
    </style>
    
    
  2. 配置路由映射

    index.js

    //配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    import HelloWorld from "../components/HelloWorld";
    import Home from "../components/Home";
    import About from "../components/About";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    //配置路由映射
    const routes = [
    
      {
        path: "/home",
        component: Home
      },
      {
        path: "/about",
        component: About
      }
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes
    })
    
    //3.将router对象传入到Vue实例
    export default router
    
    
    
  3. 使用路由显示

    点击首页或者点击关于,其组件中的信息就会显示在<router-view/>的位置

    App.vue

    <template>
      <div id="app">
        <router-link to="/home">首页</router-link>
        <router-link to="/about">关于</router-link>
        <router-view/>
      </div>
    </template>
    
    <script>
    
    
    export default {
      name: 'App'
    }
    </script>
    
    <style>
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>
    
    
  4. 显示测试

    点击首页

    image-20221013235535761

    点击关于

路由的默认值和修改为history

  • 配置不需要点击路径,一进入页面就显示配置的路由页信息
  • 修改url为history模式(比如默认url显示为:/#/aaa,history模式下url显示为/aaa
  1. 路由的默认值修改

    会在进入页面时就显示/home页的信息

    主要修改:

    {
     //配置路由默认信息
     path: '/',
     redirect: '/home'
    },
    
    //配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    import HelloWorld from "../components/HelloWorld";
    import Home from "../components/Home";
    import About from "../components/About";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    
    //配置路由映射
    const routes = [
      {
        //配置路由默认信息
        path: '/',
        redirect: '/home'
      },
      {
        path: "/home",
        component: Home
      },
      {
        path: "/about",
        component: About
      }
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes
    })
    
    //3.将router对象传入到Vue实例
    export default router
    
  2. history的修改

    主要修改:

    const router = new VueRouter({
    //配置路由和组件之间的应用关系
    routes,
    //history模式修改
    mode: 'history'
    })
    
    //配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    import HelloWorld from "../components/HelloWorld";
    import Home from "../components/Home";
    import About from "../components/About";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    
    //配置路由映射
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: "/home",
        component: Home
      },
      {
        path: "/about",
        component: About
      }
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes,
      配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    import HelloWorld from "../components/HelloWorld";
    import Home from "../components/Home";
    import About from "../components/About";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    
    //配置路由映射
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: "/home",
        component: Home
      },
      {
        path: "/about",
        component: About
      }
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes,
      //history模式修改
      mode: 'history'
    })
    
    //3.将router对象传入到Vue实例
    export default router
    
    
      mode: 'history'
    })
    
    //3.将router对象传入到Vue实例
    export default router
    

    修改完成后,url路径显示

    image-20221016010013050

router-link的其它属性补充

  1. tag

    默认是使用a标签,我们可以使用tag去修改

    <router-link to="/home">首页</router-link>
    <router-link to="/about" tag="button">关于</router-link>
    

    image-20221016010401352

  2. replace

    用户点击了该路由标签后无法点击返回

    <router-link to="/home" replace>首页</router-link>
    <router-link to="/about" tag="button">关于</router-link>
    

    image-20221016010702531

  3. active-class

    当用户点击router-link的标签的时候,其标签中会出现如下class

    image-20221016011137346

    于是,我们可以通过设置这个class的名字的值去动态的改变我们选中的router-link标签的样式

    建议使用方式三,前两种只是用来比较

    • 方式一

      直接在该vue的文件中创建style的,class的名字是router-link-exact-active router-link-active

      <style>
      .router-link-exact-active router-link-active{
        color: red;
      }
      </style>
      
    • 方式二

      我们可以修改单个标签上的class的名字为我们指定的

      <router-link to="/home" replace active-class="active">首页</router-link>
          <router-link to="/about" tag="button" active-class="active">关于</router-link>
      
      <style>
      .active{
        color: red;
      }
      </style>
      

      这样后,我们在style中class的名字就可以是active

      但是这样做有一个弊端,我们每条router-link都需要手动去进行设置

    • 方式三

      在路由的index.js文件中去进行修改

      const router = new VueRouter({
        //配置路由和组件之间的应用关系
        routes,
        //history模式修改
        mode: 'history',
        //修改router-link-exact-active router-link-active的名字为active
        linkActiveClass: 'active'
      })
      

      这样就可以保证每条router-linkrouter-link-exact-active router-link-active都修改为了active

    最终效果

    点击哪个router-link哪个就变红

    • 点击首页

      image-20221016012240492

    • 点击关于

      image-20221016012248370

通过代码跳转路由

不使用router-link去进行路由跳转,使用@click去绑定方法,在javaScript的方法中进行路由跳转

<template>
  <div id="app">
<!--    <router-link to="/home" replace active-class="active">首页</router-link>-->
<!--    <router-link to="/about" tag="button" active-class="active">关于</router-link>-->
<!--    <router-link to="/home" replace>首页</router-link>-->
<!--    <router-link to="/about" tag="button">关于</router-link>-->

    <button @click="homeClick">首页</button>
    <button @click="aboutClick">关于</button>
    <router-view/>
  </div>
</template>

<script>


export default {
  name: 'App',
  methods: {
    homeClick(){
      //通过代码的方式修改路由 vue-router
      //push=> pushsState
      //replace => replaceState
      this.$router.push('/home');

      console.log('homeClick');

    },
    aboutClick(){
      //通过代码的方式修改路由 vue-router
      this.$router.push('/about');

      console.log('aboutClick');
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.active{
  color: red;
}
</style>

vue-router的进阶使用

动态路由的使用

  1. 新建User.vue界面

    使用计算属性获取url中传来的值info

    <template>
      <div>
        <h2>我是用户界面</h2>
        <p>我是用户的相关信息</p>
        <p>{{userInfo}}</p>
      </div>
    </template>
    
    <script>
    export default {
      name: "User",
      computed: {
        userInfo(){
          return this.$route.params.info;
        }
      }
    
    }
    </script>
    
    <style scoped>
    
    </style>
    
  2. index.js中注册组件路由

    注册的路由路径中绑定一个:info,那么在点击路径的时候可以传值近进info,进入组件后也可以获取info绑定的值

    //配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    import HelloWorld from "../components/HelloWorld";
    import Home from "../components/Home";
    import About from "../components/About";
    import User from "../components/User";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    
    //配置路由映射
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: "/home",
        component: Home
      },
      {
        path: "/about",
        component: About
      },
      {
        path: "/user/:info",
        component: User
      }
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes,
      //history模式修改
      mode: 'history',
      //修改router-link-exact-active router-link-active的名字为active
      linkActiveClass: 'active'
    })
    
    //3.将router对象传入到Vue实例
    export default router
    
  3. App.vue中写入链接

    router-link中去绑定要写入组件路由路径要传入的值,那么访问路径就是"'/user/'+userInfo.name"

    <template>
      <div id="app">
    <!--    <router-link to="/home" replace active-class="active">首页</router-link>-->
    <!--    <router-link to="/about" tag="button" active-class="active">关于</router-link>-->
        <router-link to="/home">首页</router-link>
        <router-link to="/about" tag="button">关于</router-link>
    
    <!--    <button @click="homeClick">首页</button>-->
    <!--    <button @click="aboutClick">关于</button>-->
        <router-link :to="'/user/'+userInfo.name">用户</router-link>
        <router-view/>
      </div>
    </template>
    
    <script>
    
    
    export default {
      name: 'App',
      data(){
        return({
          userInfo:{
            name: "lzj"
          }
        })
      },
      methods: {
        homeClick(){
          //通过代码的方式修改路由 vue-router
          //push=> pushsState
          //replace => replaceState
          this.$router.push('/home');
    
          console.log('homeClick');
    
        },
        aboutClick(){
          //通过代码的方式修改路由 vue-router
          this.$router.push('/about');
    
          console.log('aboutClick');
        }
      }
    }
    </script>
    
    <style>
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    .active{
      color: red;
    }
    </style>
    
  4. 测试访问

    • 访问路径

      http://localhost:8080/user/lzj

    • 访问显示

      image-20221017171623425

路由的懒加载

懒加载可以增强用户的体验,在访问页面时只加载需要使用到的js文件。

路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.一个懒加载变成一个js文件

使用懒加载的方式

image-20221017193221775

使用懒加载的代码示例

//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
// import Home from "../components/Home";
// import About from "../components/About";
// import User from "../components/User";

//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);

//2.创建VueRouter对象

//配置路由映射
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: "/home",
    component: ()=> import('../components/Home')
  },
  {
    path: "/about",
    component: ()=> import('../components/About')
  },
  {
    path: "/user/:info",
    component: ()=> import('../components/User')
  }
]

const router = new VueRouter({
  //配置路由和组件之间的应用关系
  routes,
  //history模式修改
  mode: 'history',
  //修改router-link-exact-active router-link-active的名字为active
  linkActiveClass: 'active'
})

//3.将router对象传入到Vue实例
export default router

路由懒加载的效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrulkZm6-1670513227461)(https://lzj-love-study.oss-cn-hangzhou.aliyuncs.com/blog/image-20221017193042856.png)]

嵌套路由

children

  1. 新建HomeNews.vue和HomeMessage.vue

    HomeNews.vue

    <template>
      <div>
        <ul>
          <li>新闻1</li>
          <li>新闻2</li>
          <li>新闻3</li>
          <li>新闻4</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: "HomeNews"
    }
    </script>
    
    <style scoped>
    
    </style>
    

    HomeMessage.vue

    <template>
      <div>
        <ul>
          <li>消息1</li>
          <li>消息2</li>
          <li>消息3</li>
          <li>消息4</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: "HomeMessage"
    }
    </script>
    
    <style scoped>
    
    </style>
    
    
  2. 编写index.js

    • 使用children来配置嵌套路由
    • 可以设置默认路由
    //配置路由相关的信息
    import VueRouter from "vue-router";
    import Vue from "vue";
    import HelloWorld from "../components/HelloWorld";
    // import Home from "../components/Home";
    // import About from "../components/About";
    // import User from "../components/User";
    
    //1.通过Vue.use(插件),安装插件
    Vue.use(VueRouter);
    
    //2.创建VueRouter对象
    
    //配置路由映射
    const routes = [
      {
        path: '/',
        redirect: '/home'
      },
      {
        path: "/home",
        component: ()=> import('../components/Home'),
        children: [
          {
            path: "/",
            redirect: "news"
          },
          {
            path: "news",
            component: ()=> import('../components/HomeNews')
          },
          {
            path: "message",
            component: ()=> import('../components/HomeMessage')
          }
        ]
      },
      {
        path: "/about",
        component: ()=> import('../components/About')
      },
      {
        path: "/user/:info",
        component: ()=> import('../components/User')
      }
    ]
    
    const router = new VueRouter({
      //配置路由和组件之间的应用关系
      routes,
      //history模式修改
      mode: 'history',
      //修改router-link-exact-active router-link-active的名字为active
      linkActiveClass: 'active'
    })
    
    //3.将router对象传入到Vue实例
    export default router
    
    
    
  3. Home.vue

    <template>
      <div>
        <p>欢迎进入home界面</p>
        <router-link to="/home/news">新闻</router-link>
        <router-link to="/home/message">消息</router-link>
    
        <router-view></router-view>
      </div>
    
    </template>
    
    <script>
    export default {
      name: "Home"
    }
    </script>
    
    <style scoped>
    
    </style>
    
    
  4. 测试效果

    image-20221017195521187

路由参数传递

传递参数主要有两种方式paramsquery

  • param的类型

    • 配置路由格式:/router/:id

    • 传递的方式:在path后面跟上对应的值,示例:

       //userInfo为自己定义的对象
      <router-link :to="'/user/'+userInfo.id">用户</router-link>
      
    • 传递后形成的路径:/router/123

    • 获取值的方式:$route.params.id

  • query的类型

    • 配置路由格式:router,也就是普通配置

    • 传递的方式:对象中使用query的key作为传递方式,示例:

      //{path: '/profile',query: {name: 'lzj',age: 18}}可以放在data中
      <router-link :to="{path: '/profile',query: {name: 'lzj',age: 18}}">档案</router-link>
      
    • 传递后形成的路径:/router?id=123

    • 获取值的方式:$route.query.id

  • 通过button使用方法的方式

    • 路由跳转的方式采用普通button的方式

      <button @click="userClick()" >用户</button>
      
    • 在methods中定义方法进行跳转传值

      <script>
      export default {
        name: 'App',
        data() {
            return {
                userId: "lzj"
            }
        },
        methods: {
            //这里也可以使用query的方式
            userClick(){
                this.$router.push('/user/'+this.userId)
            }
        }
      }
      </script>
      

param方式示例

  1. 新建User.vue

    <template>
      <div>
        <h2>我是用户界面</h2>
        <p>我是用户的相关信息</p>
        <p>{{userInfo}}</p>
      </div>
    </template>
    
    <script>
    export default {
      name: "User",
      computed: {
        userInfo(){
          return this.$route.params.info;
        }
      }
    
    }
    </script>
    
    <style scoped>
    
    </style>
    
  2. index.js中注册组件路由

    {
        path: "/user/:info",
        component: ()=> import('../components/User')
      }
    
  3. app.vue中编写router-link

    <router-link :to="'/user/'+userInfo.name">用户</router-link>
    
    <script>
    export default {
      name: 'App',
      data(){
        return{
          userInfo:{
            name: "lzj"
          }
        }
      }
    }
    </script>
    
    
  4. 测试效果

    image-20221017203720503

query方式示例

  1. 新建Profile.vue

    <template>
      <div>
        <p>我是profile组件</p>
        <p>{{$route.query.name}}</p>
        <p>{{$route.query.age}}</p>
      </div>
    </template>
    
    <script>
    export default {
      name: "Profile"
    }
    </script>
    
    <style scoped>
    
    </style>
    
    
  2. index.js中注册组件路由

    const Profile = ()=>import('../components/Profile');
    
    {
        path: "/profile",
        component: Profile
      }
    
  3. app.vue中编写router-link

        <router-link :to="{path: '/profile',query: {name: 'lzj',age: 18}}">档案</router-link>
    
  4. 测试效果

    image-20221017202958213

全局导航守卫

前置守卫

**作用:**在进入路由前调用的钩子函数

重要代码示例:

const router = new VueRouter({
//配置路由和组件之间的应用关系
routes,
//history模式修改
mode: 'history',
//修改router-link-exact-active router-link-active的名字为active
linkActiveClass: 'active'
})
//前置守卫
router.beforeEach((to, from, next)=>{
//从from跳转到to,修改标题为meta中的数据
document.title = to.matched[0].meta.title;
console.log(to);
//next必须调用
next();
})

参数解析:

  • to:即将要进入的目标的路由对象
  • from:当前导航即将要离开的路由对象
  • next:调用该方法后,才能进入下一个钩子

修改标题案例代码:

在路由配置中添加meta的数据

  • meta:元数据
//配置路由相关的信息
import VueRouter from "vue-router";
import Vue from "vue";
import HelloWorld from "../components/HelloWorld";
// import Home from "../components/Home";
// import About from "../components/About";
// import User from "../components/User";

//1.通过Vue.use(插件),安装插件
Vue.use(VueRouter);

//2.创建VueRouter对象

const Profile = ()=>import('../components/Profile');

//配置路由映射
const routes = [
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: "/home",
    component: ()=> import('../components/Home'),
    meta: {
      title : "首页"
    },
    children: [
      {
        path: "/",
        redirect: "news"
      },
      {
        path: "news",
        component: ()=> import('../components/HomeNews')
      },
      {
        path: "message",
        component: ()=> import('../components/HomeMessage')
      }
    ]
  },
  {
    path: "/about",
    component: ()=> import('../components/About'),
    meta: {
      title: "关于"
    }
  },
  {
    path: "/user/:info",
    component: ()=> import('../components/User')
  },
  {
    path: "/profile",
    component: Profile,
    meta: {
      title: "档案"
    }
  }
]

const router = new VueRouter({
  //配置路由和组件之间的应用关系
  routes,
  //history模式修改
  mode: 'history',
  //修改router-link-exact-active router-link-active的名字为active
  linkActiveClass: 'active'
})

//路由导航

router.beforeEach((to, from, next)=>{
  
  //从from跳转到to,修改标题为meta中的数据
  document.title = to.matched[0].meta.title;
  console.log(to);
  //next必须调用
  next();
})

//3.将router对象传入到Vue实例
export default router

点击首页,最终效果

image-20221018142351045

后置守卫

**作用:**在进入路由后调用的钩子函数

重要代码示例:

const router = new VueRouter({
//配置路由和组件之间的应用关系
routes,
//history模式修改
mode: 'history',
//修改router-link-exact-active router-link-active的名字为active
linkActiveClass: 'active'
})
//后置守卫
router.afterEach((to, from)=>{
console.log("after");
})

参数解析:

  • to:进入的目标的路由对象
  • from:上一个离开的路由对象

路由独享守卫

**作用:**只有在进入指定路由才会触发

https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%AB

keep-alive

**作用:**keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染

使用方式示例:

<keep-alive>
 <router-view></router-view>
</keep-alive>

属性介绍:

  • include:字符串或正则表达式,只有匹配的组件会被缓存
  • exclude:字符串或正则表达式,任何匹配的组件都不会被缓存

**方法函数介绍:**只有该组件被keep-alive标签包裹,才会生效

  • activated(): 该组件进入活跃状态时执行函数
  • deactivated(): 该组件从活跃状态改变时执行函数

使用文件路径的引用问题

在写项目的过程中,我们常常需要使用到文件的导入引用,而有时候路径又过于复杂和多,所以我们可以在配置文件中写入别名,来方便我们的开发

配置位置:webpack.base.conf.js

使用方式:

  • import引入方式:

    import TabBar from "@/components/tabbar/TabBar";
    
  • html标签引入方式示例(其路径前必须加~):

    <img src="~@/assets/img/123.png">
    
  1. 进入build文件夹中的webpack.base.conf.js

    这里有默认的@别名来指代项目是src目录路径

    image-20221018231404460

  2. alias中写入我们自己定义的路径别名

    定义路径src/assets的别名为assets

    'assets': resolve('src/assets')
    
  3. 在vue文件中使用路径别名

    注意,在html标签中使用时,其前面必须加~

    • import的方式

      使用默认别名@在javascript中引入src/components/tabbar/TabBar

      import TabBar from "@/components/tabbar/TabBar";
      
    • 在html的标签中使用

      使用自定义的assets在html的标签中引入src/assets/img/123.png

      <img src="~assets/img/123.png">
      

      使用默认别名@在在html的标签中引入src/assets/img/123.png

      <img src="~@/assets/img/123.png">
      

Promise

Promise是异步编程的一种解决方案

基本代码示例:

//参数 -> 函数
//在执行传入的回调函数时,会传入两个参数:resolve, reject 本身他们又是函数
new Promise((resolve, reject)=>{
 //这个可以替换为网络请求
 setTimeout((data)=>{
     //成功的时候调用resolve
     resolve(data)

     //失败的时候调用reject,就不会进入then中了,会进入catch中
 },1000)

}).then((data)=>{
 //处理回调
 console.log(data)
}).catch((err)=>{
 console.log(err)
})

Promise的基本使用

一般处理方式

这里简单演示了下Promise如何去使用,只包含了resolve的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    //1.使用setTimeout
    setTimeout(()=>{
        console.log("Hello World");
    },1000)

    //参数 -> 函数
    //resolve, reject 本身他们又是函数
    new Promise((resolve, reject)=>{
        //第一次请求代码
        setTimeout(()=>{
            resolve()
        },1000)
    }).then(()=>{

        //第一次拿到结果的处理代码
        console.log("Hello World");
        console.log("Hello World");
        console.log("Hello World");
        console.log("Hello World");

        //第二次请求代码
        new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve()
            },1000)
        }).then(()=>{

            //第二次拿到结果的处理代码
            console.log("Hello Vuejs");
            console.log("Hello Vuejs");
            console.log("Hello Vuejs");
            console.log("Hello Vuejs");

            //第三次请求代码
            new Promise((resolve, reject)=>{
                setTimeout(()=>{
                    resolve()
                },1000)
            }).then(()=>{

                //第三次拿到结果的处理代码
                console.log("Hello Python");
                console.log("Hello Python");
                console.log("Hello Python");
                console.log("Hello Python");

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

另外一种处理方式

这里包含了resolvereject的另外一种处理方式使用,不需要使用catch

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script>
    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve("Hello Vuejs");
            // reject("err message");
        },1000)
    }).then(data=>{
        console.log(data);
    },err=>{
        console.log(err);
    })

</script>

</body>
</html>

Promise三种状态

异步操作中会产生三种状态,分别是:

  • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
  • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
  • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()

Promise链式调用

满足状态的调用和拒绝状态的调用都有简写语法

满足的状态的调用

  • 方式1:

    return new Promise((resolve, reject)=>{
        resolve(data);
    }).then(data => {
        
        return new Promise((resolve, reject)=>{
        	resolve(data + "1");
        }).then(data => {
    
        })
    })
    
  • 方式二

    return new Promise((resolve, reject)=>{
        resolve(data);
    }).then(data => {
        
        return Promise.resolve(data +"1")
    }).then(data => {
        
        return Promise.resolve(data +"2")
    })
    
  • 方式三

    return new Promise((resolve, reject)=>{
        resolve(data);
    }).then(data => {
        
        return data +"1"
    }).then(data => {
        
        return data +"2"
    })
    

拒绝的状态的调用

  • 方式一

    return new Promise((resolve, reject)=>{
        resolve(data);
    }).then(data => {
        
        return new Promise((resolve, reject)=>{
        	reject(err);
        }).then(data => {
    
        })
    }).catch(err =>{
        console.log(err)
    })
    
  • 方式二

    return new Promise((resolve, reject)=>{
        resolve(data);
    }).then(data => {
        
        return Promise.reject(err)
    }).catch(err =>{
        console.log(err)
    })
    
  • 方式三

    return new Promise((resolve, reject)=>{
        resolve(data);
    }).then(data => {
        
        throw err
    }).catch(err =>{
        console.log(err)
    })
    

链式调用的三种方式示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script>
    //网络请求: aaa ->自己处理(10行)
    //处理: aaa+111 -> 自己处理(10行)
    //处理: aaa111222 ->自己处理

    //第一种方式
    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve("aaa")
        },1000)
    }).then(res =>{
        //1.自己处理10行代码
        console.log(res,"第一层的10行处理代码");

        //对结果进行第一次处理
        return new Promise((resolve, reject)=>{
            resolve(res + "111")
        }).then(res =>{
            console.log(res,"第二层的10行处理代码")

            //对结果进行第二次处理
            return new Promise((resolve, reject)=>{
                resolve(res + "222")
            }).then(res =>{
                console.log(res,"第三层的10行处理代码")


            })
        })
    })


    //第二种方式,使用return Promise.resolve

    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve("aaa")
        },1000)
    }).then(res => {
        //1.自己处理10行代码
        console.log(res, "第一层的10行处理代码");
        //对结果进行第一次处理
        // return Promise.resolve(res + "111")

        //如果抛出错误,则会进入catch
        return Promise.reject("err")

        //也可以使用这种方式抛出异常
        // throw "error message"
    }).then(res =>{
        console.log(res,"第二层的10行处理代码");

            //对结果进行第二次处理
        return Promise.resolve(res + "222")
    }).then(res =>{
                console.log(res,"第三层的10行处理代码");

    }).catch(err =>{
        console.log(err);
    })

    //第三种方式,省略掉Promise.resolve
    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve("aaa")
        },1000)
    }).then(res => {
        //1.自己处理10行代码
        console.log(res, "第一层的10行处理代码");

        //对结果进行第一次处理
        return res + "111"
    }).then(res =>{
        console.log(res,"第二层的10行处理代码");

        //对结果进行第二次处理
        return res + "222"
    }).then(res =>{
        console.log(res,"第三层的10行处理代码");

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

Promise的all方法

如果有两个网络请求,我们需要在他们两个请求都完成后再进行相关操作,那么我们可以使用Promise的all方法。

简单示例:

Promise.all([]).then(result =>{})

简单示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    Promise.all([
        new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve("data1")
            },1000)
        }),
        new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve("data2")
            },2000)
        })
    ]).then(result =>{
        console.log(result);
        console.log(result[0]);
        console.log(result[1]);
    })


</script>

</body>
</html>

VueX

官方文档链接

  • Vuex是一个专为Vue.js应用程序开发的状态管理模式,它是响应式的。

    简单理解就是:将多个组件共享的变量全部存储在一个对象里面,每个组件都可以去使用和改变它

  • Vuex遵循State单一状态树

Vuex状态管理图例:

image-20221020233953929

相关插件

devtools

在谷歌商店中搜索即可

image-20221022120807005

简单使用Vuex

配置完成后可以在.vue文件中使用$store来使用其数据

  1. 下载Vuex

    npm install vuex@3.6.1 --save
    
  2. 配置Vuex

    • src目录下新建store目录

    • store目录中新建index.js

      index.js

      import Vue from "vue";
      import Vuex from "vuex";
      
      //1.安装插件
      Vue.use(Vuex);
      
      //2.创建对象
      const store = new Vuex.Store({
        //状态
        state:{
            
        },
        mutations:{
      
        },
        actions:{
      
        },
        getters:{
      
        },
        modules:{
      
        }
      })
      
      //导出Store
      export default store
      
    • main.js中引入store

      1. import引入store
      2. Vue中注册store
      import Vue from 'vue'
      import App from './App'
      import router from './router'
      import store from "./store";
      
      Vue.config.productionTip = false
      
      /* eslint-disable no-new */
      new Vue({
        el: '#app',
        router,
        store,
        render: h => h(App)
      })
      
  3. 编写测试案例(计数案例)

    • 编写store目录下的index.js

      import Vue from "vue";
      import Vuex from "vuex";
      
      //1.安装插件
      Vue.use(Vuex);
      
      //2.创建对象
      const store = new Vuex.Store({
        //状态
        state:{
          counter: 0
        },
        mutations:{
          //方法
          increment(state){
            state.counter++;
          },
          decrement(state){
            state.counter--;
          }
        },
        actions:{
      
        },
        getters:{
      
        },
        modules:{
      
        }
      })
      
      //导出Store
      export default store
      
    • 编写App.vue

      使用this.$store.commit()来获取我们定义的mutations

      <template>
        <div id="app">
          <h2>{{message}}</h2>
          <h2>{{$store.state.counter}}</h2>
          <button @click="counterAdd()">+</button>
          <button @click="counterDue()">-</button>
          <hello-vuex></hello-vuex>
      
          <router-view/>
        </div>
      </template>
      
      <script>
      
      import HelloVuex from "@/components/HelloVuex";
      
      export default {
        name: 'App',
        components: {HelloVuex},
        data(){
          return{
            message: "我是App组件",
          }
        },
        methods:{
          counterAdd(){
            this.$store.commit('increment');
          },
          counterDue(){
            this.$store.commit('decrement');
          }
        }
      }
      </script>
      
      <style>
      </style>
      
      
    • 新建HelloVuex.vue组件

      <template>
        <div>
          <h2>{{$store.state.counter}}</h2>
        </div>
      </template>
      
      <script>
      export default {
        name: "HelloVuex"
      }
      </script>
      
      <style scoped>
      
      </style>
      
  4. 结束

    点击App.vue中的按钮,HelloVuex.vue中的$store.state.counter也会进行改变

    image-20221022132942930

Getters

类似于vue中的计算属性

示例

获取年龄大于20岁的学生

  • 在Getters中去获取,使用filter过滤
  • 也可以让用户自定义输入年龄参数,那么我们需要使用到函数

index.js

import Vue from "vue";
import Vuex from "vuex";

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

//2.创建对象
const store = new Vuex.Store({
  //状态
  state:{
    counter: 0,
    students:[
      {name: "lzj1",age: 15},
      {name: "lzj2",age: 18},
      {name: "lzj3",age: 23},
      {name: "lzj4",age: 24},
    ]
  },
  mutations:{
    //方法
    increment(state){
      state.counter++;
    },
    decrement(state){
      state.counter--;
    }
  },
  actions:{

  },
  getters:{
    more20stu: state => {
      return state.students.filter(s => s.age >=20);
    },
    more20stu2: state => {
      return age =>{
        return state.students.filter(s => s.age >= age);
      }
    }
  },
  modules:{

  }
})

//导出Store
export default store

App.vue

<template>
  <div id="app">
    <h2>{{message}}</h2>
    <h2>{{$store.state.counter}}</h2>
    <button @click="counterAdd()">+</button>
    <button @click="counterDue()">-</button>

    <h2>自带年龄</h2>
    <h2>{{$store.getters.more20stu}}</h2>

    <h2>自定义输入年龄</h2>
    <h2>{{$store.getters.more20stu2(15)}}</h2>
    <hello-vuex></hello-vuex>

    <router-view/>
  </div>
</template>

<script>

import HelloVuex from "@/components/HelloVuex";

export default {
  name: 'App',
  components: {HelloVuex},
  data(){
    return{
      message: "我是App组件",
    }
  },
  methods:{
    counterAdd(){
      this.$store.commit('increment');
    },
    counterDue(){
      this.$store.commit('decrement');
    }
  }
}
</script>

<style>
</style>

image-20221022151119805

Mutation

Vuex的store状态的唯一更新方式:提交Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:

store.commit('increment')

提交载荷(Payload)

你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:

store.commit({
  type: 'increment',
  amount: 10
})

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

Mutation 需遵守 Vue 的响应规则

既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:

  1. 最好提前在你的 store 中初始化好所有所需属性。
  2. 当需要在对象上添加新属性时,你应该
  • 使用 Vue.set(obj, 'newProp', 123), 或者

  • 以新对象替换老对象。例如,利用对象展开运算符 (opens new window)我们可以这样写:

    state.obj = { ...state.obj, newProp: 123 }
    

使用常量替代Mutation事件类型

使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

mutation-types.js

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'

store.js

// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。

Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:

如果需要使用异步函数,则可以使用Action

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

在组件中提交 Mutation

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

让我们来注册一个简单的 action:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

实践中,我们会经常用到 ES2015 的 参数解构 (opens new window)来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

分发 Action

Action 通过 store.dispatch 方法触发:

store.dispatch('increment')

乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

Actions 支持同样的载荷方式和对象方式进行分发:

// 以载荷形式分发
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

来看一个更加实际的购物车示例,涉及到调用异步 API分发多重 mutation

actions: {
  checkout ({ commit, state }, products) {
    // 把当前购物车的物品备份起来
    const savedCartItems = [...state.cart.added]
    // 发出结账请求,然后乐观地清空购物车
    commit(types.CHECKOUT_REQUEST)
    // 购物 API 接受一个成功回调和一个失败回调
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失败操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

注意我们正在进行一系列的异步操作,并且通过提交 mutation 来记录 action 产生的副作用(即状态变更)。

在组件中分发 Action

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}

组合 Action

Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?

首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

现在你可以:

store.dispatch('actionA').then(() => {
  // ...
})

在另外一个 action 中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

最后,如果我们利用 async / await (opens new window),我们可以如下组合 action:

// 假设 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

模块的局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象

const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

命名空间

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模块内容(module assets)
      state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: () => ({ ... }),
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: () => ({ ... }),
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

启用了命名空间的 getter 和 action 会收到局部化的 getterdispatchcommit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。

在带命名空间的模块内访问全局内容(Global Assets)

如果你希望使用全局 state 和 getter,rootStaterootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。

若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatchcommit 即可。

modules: {
  foo: {
    namespaced: true,

    getters: {
      // 在这个模块的 getter 中,`getters` 被局部化了
      // 你可以使用 getter 的第四个参数来调用 `rootGetters`
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      },
      someOtherGetter: state => { ... }
    },

    actions: {
      // 在这个模块中, dispatch 和 commit 也被局部化了
      // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}

在带命名空间的模块注册全局 action

若需要在带命名空间的模块注册全局 action,你可添加 root: true,并将这个 action 的定义放在函数 handler 中。例如:

{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}

带命名空间的绑定函数

当使用 mapState, mapGetters, mapActionsmapMutations 这些函数来绑定带命名空间的模块时,写起来可能比较繁琐:

computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}

对于这种情况,你可以将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。于是上面的例子可以简化为:

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

而且,你可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

给插件开发者的注意事项

如果你开发的插件(Plugin)提供了模块并允许用户将其添加到 Vuex store,可能需要考虑模块的空间名称问题。对于这种情况,你可以通过插件的参数对象来允许用户指定空间名称:

// 通过插件的参数对象得到空间名称
// 然后返回 Vuex 插件函数
export function createPlugin (options = {}) {
  return function (store) {
    // 把空间名字添加到插件模块的类型(type)中去
    const namespace = options.namespace || ''
    store.dispatch(namespace + 'pluginAction')
  }
}

模块动态注册

在 store 创建之后,你可以使用 store.registerModule 方法注册模块:

import Vuex from 'vuex'

const store = new Vuex.Store({ /* 选项 */ })

// 注册模块 `myModule`
store.registerModule('myModule', {
  // ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

之后就可以通过 store.state.myModulestore.state.nested.myModule 访问模块的状态。

模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync (opens new window)插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。

你也可以使用 store.unregisterModule(moduleName) 来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)。

注意,你可以通过 store.hasModule(moduleName) 方法检查该模块是否已经被注册到 store。

保留 state

在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过 preserveState 选项将其归档:store.registerModule('a', module, { preserveState: true })

当你设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。

模块重用

有时我们可能需要创建一个模块的多个实例,例如:

  • 创建多个 store,他们公用同一个模块 (例如当 runInNewContext 选项是 false'once' 时,为了在服务端渲染中避免有状态的单例 (opens new window))
  • 在一个 store 中多次注册同一个模块

如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。

实际上这和 Vue 组件内的 data 是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持):

const MyReusableModule = {
  state: () => ({
    foo: 'bar'
  }),
  // mutation, action 和 getter 等等...
}

项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

Axios

官方文档:https://axios-http.com/zh/docs/intro

Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。

Axios的基本使用和功能介绍

安装和简单使用axios

  1. 运行下载命令

    npm install axios --save
    
  2. 简单使用axios

    引入axios和使用axios

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import axios from "axios";
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      render: h => h(App)
    })
    
    axios({
      // method: "get",
      url: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a/info"
    }).then(res =>{
      console.log(res);
    })
    
    axios({
      method: "post",
      url: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a/list"
    }).then(res =>{
      console.log(res);
    })
    

axios实例

可以使用自定义配置新建一个实例。

axios.create([config])

const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

以下是可用的实例方法。指定的配置将与实例的配置合并。

请求配置

这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 GET 方法。

{
  // `url` 是用于请求的服务器 URL
  url: '/user',

  // `method` 是创建请求时使用的方法
  method: 'get', // 默认值

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
  // 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
  // 你可以修改请求头。
  transformRequest: [function (data, headers) {
    // 对发送的 data 进行任意转换处理

    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对接收的 data 进行任意转换处理

    return data;
  }],

  // 自定义请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是与请求一起发送的 URL 参数
  // 必须是一个简单对象或 URLSearchParams 对象
  params: {
    ID: 12345
  },

  // `paramsSerializer`是可选方法,主要用于序列化`params`
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data` 是作为请求体被发送的数据
  // 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
  // 在没有设置 `transformRequest` 时,则必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属: FormData, File, Blob
  // - Node 专属: Stream, Buffer
  data: {
    firstName: 'Fred'
  },
  
  // 发送请求体数据的可选语法
  // 请求方式 post
  // 只有 value 会被发送,key 则不会
  data: 'Country=Brasil&City=Belo Horizonte',

  // `timeout` 指定请求超时的毫秒数。
  // 如果请求时间超过 `timeout` 的值,则请求会被中断
  timeout: 1000, // 默认值是 `0` (永不超时)

  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // default

  // `adapter` 允许自定义处理请求,这使测试更加容易。
  // 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。
  adapter: function (config) {
    /* ... */
  },

  // `auth` HTTP Basic Auth
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType` 表示浏览器将要响应的数据类型
  // 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
  // 浏览器专属:'blob'
  responseType: 'json', // 默认值

  // `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)
  // 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求
  // Note: Ignored for `responseType` of 'stream' or client-side requests
  responseEncoding: 'utf8', // 默认值

  // `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称
  xsrfCookieName: 'XSRF-TOKEN', // 默认值

  // `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称
  xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值

  // `onUploadProgress` 允许为上传处理进度事件
  // 浏览器专属
  onUploadProgress: function (progressEvent) {
    // 处理原生进度事件
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  // 浏览器专属
  onDownloadProgress: function (progressEvent) {
    // 处理原生进度事件
  },

  // `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数
  maxContentLength: 2000,

  // `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数
  maxBodyLength: 2000,

  // `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
  // 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
  // 则promise 将会 resolved,否则是 rejected。
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认值
  },

  // `maxRedirects` 定义了在node.js中要遵循的最大重定向数。
  // 如果设置为0,则不会进行重定向
  maxRedirects: 5, // 默认值

  // `socketPath` 定义了在node.js中使用的UNIX套接字。
  // e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。
  // 只能指定 `socketPath` 或 `proxy` 。
  // 若都指定,这使用 `socketPath` 。
  socketPath: null, // default

  // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
  // and https requests, respectively, in node.js. This allows options to be added like
  // `keepAlive` that are not enabled by default.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy` 定义了代理服务器的主机名,端口和协议。
  // 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。
  // 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。
  // `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。
  // 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。
  // 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // see https://axios-http.com/zh/docs/cancellation
  cancelToken: new CancelToken(function (cancel) {
  }),

  // `decompress` indicates whether or not the response body should be decompressed 
  // automatically. If set to `true` will also remove the 'content-encoding' header 
  // from the responses objects of all decompressed responses
  // - Node only (XHR cannot turn off decompression)
  decompress: true // 默认值

}

响应结构

一个请求的响应包含以下信息。

{
  // `data` 由服务器提供的响应
  data: {},

  // `status` 来自服务器响应的 HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: 'OK',

  // `headers` 是服务器响应头
  // 所有的 header 名称都是小写,而且可以使用方括号语法访问
  // 例如: `response.headers['content-type']`
  headers: {},

  // `config` 是 `axios` 请求的配置信息
  config: {},

  // `request` 是生成此响应的请求
  // 在node.js中它是最后一个ClientRequest实例 (in redirects),
  // 在浏览器中则是 XMLHttpRequest 实例
  request: {}
}

当使用 then 时,您将接收如下响应:

axios.get('/user/12345')
  .then(function (response) {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
    console.log(response.config);
  });

当使用 catch,或者传递一个rejection callback作为 then 的第二个参数时,响应可以通过 error 对象被使用,正如在错误处理部分解释的那样。

默认配置

您可以指定默认配置,它将作用于每个请求。

全局 axios 默认值

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

自定义实例默认值

// 创建实例时配置默认值
const instance = axios.create({
  baseURL: 'https://api.example.com'
});

// 创建实例后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

配置的优先级

配置将会按优先级进行合并。它的顺序是:在lib/defaults.js中找到的库默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后面的优先级要高于前面的。下面有一个例子。

// 使用库提供的默认配置创建实例
// 此时超时配置的默认值是 `0`
const instance = axios.create();

// 重写库的超时默认值
// 现在,所有使用此实例的请求都将等待2.5秒,然后才会超时
instance.defaults.timeout = 2500;

// 重写此请求的超时时间,因为该请求需要很长时间
instance.get('/longRequest', {
  timeout: 5000
});

拦截器

在请求或响应被 then 或 catch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

如果你稍后需要移除拦截器,可以这样:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

可以给自定义的 axios 实例添加拦截器。

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

错误处理

axios.get('/user/12345')
  .catch(function (error) {
    if (error.response) {
      // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // 请求已经成功发起,但没有收到响应
      // `error.request` 在浏览器中是 XMLHttpRequest 的实例,
      // 而在node.js中是 http.ClientRequest 的实例
      console.log(error.request);
    } else {
      // 发送请求时出了点问题
      console.log('Error', error.message);
    }
    console.log(error.config);
  });

使用 validateStatus 配置选项,可以自定义抛出错误的 HTTP code。

axios.get('/user/12345', {
  validateStatus: function (status) {
    return status < 500; // 处理状态码小于500的情况
  }
})

使用 toJSON 可以获取更多关于HTTP错误的信息。

axios.get('/user/12345')
  .catch(function (error) {
    console.log(error.toJSON());
  });

取消请求

AbortController

v0.22.0 开始,Axios 支持以 fetch API 方式—— AbortController 取消请求:

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消请求
controller.abort()

CancelToken deprecated

您还可以使用 cancel token 取消一个请求。

Axios 的 cancel token API 是基于被撤销 cancelable promises proposal。

此 API 从 v0.22.0 开始已被弃用,不应在新项目中使用。

可以使用 CancelToken.source 工厂方法创建一个 cancel token ,如下所示:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

也可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建一个 cancel token:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// 取消请求
cancel();

注意: 可以使用同一个 cancel token 或 signal 取消多个请求。

在过渡期间,您可以使用这两种取消 API,即使是针对同一个请求:

const controller = new AbortController();

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token,
  signal: controller.signal
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// 取消请求 (message 参数是可选的)
source.cancel('Operation canceled by the user.');
// 或
controller.abort(); // 不支持 message 参数

axios模块封装

学习视频使用的封装

  1. 创建network目录

  2. network目录下创建request.js文件

    request.js

    import axios from "axios";
    
    //方式1,直接返回instance
    export function request(config){
        //1.创建axios的实例
        const instance = axios.create({
          baseURL: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a",
          timeout: 5000
        })
    
      //返回的本身就是Promise对象
        return instance(config)
    
    }
    
    //方式二:返回Promise对象
    // export function request(config){
    //   return new Promise(((resolve, reject) => {
    //     //1.创建axios的实例
    //     const instance = axios.create({
    //       baseURL: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a",
    //       timeout: 5000
    //     })
    //
    //     instance(config)
    //       .then( res =>{
    //         resolve(res)
    //       }).catch( err =>{
    //         reject(err)
    //       })
    //
    //
    //   }))
    //
    // }
    
    
    //方式三:返回回调函数
    // export function request(config){
    //   //1.创建axios的实例
    //   const instance = axios.create({
    //     baseURL: "https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a",
    //     timeout: 5000
    //   })
    //   instance(config.baseConfig)
    //     .then( res =>{
    //       config.success(res);
    //   }).catch( err =>{
    //       config.failure(err);
    //   })
    //
    // }
    
  3. 其它文件中调用封装好的axios请求

    //5.封装request模块
    import {request} from "./network/request";
    
    //封装方式三的调用方法
    // request({
    //   baseConfig: {
    //     url: "/info"
    //   },
    //   success: res =>{
    //     console.log(res);
    //   },
    //   failure: err=>{
    //     console.log(err);
    //   }
    // })
    
    //封装方式-、二的调用方式
    request({
      url: "/info"
    }).then(res =>{
      console.log(res);
    }).catch(err =>{
      console.log(err);
    })
    
    

自行进行封装

这个只是封装的模板,可以在基础上进行再封装

  1. 下载axios

    npm install axios --save
    
  2. 创建./network/http.js

    http.js

    import axios from "axios";
    
    //设置全局超时时间10s
    axios.defaults.timeout = 10000;
    
    //创建axios实例
    const instance = axios.create({
      baseURL: "",
    })
    
    
    //http request 拦截器
    instance.interceptors.request.use(
      config => {
        // const token = getCookie('名称');注意使用的时候需要引入cookie方法,推荐js-cookie
        config.data = JSON.stringify(config.data);
        config.headers = {
          'Content-Type':'application/x-www-form-urlencoded'
        }
        // if(token){
        //   config.params = {'token':token}
        // }
        return config;
      },
      error => {
        return Promise.reject(error);
      }
    )
    
    //http response 拦截器
    instance.interceptors.response.use(
      response => {
        // if(response.data.errCode ==2){
        //   router.push({
        //     path:"/login",
        //     query:{redirect:router.currentRoute.fullPath}//从哪个页面跳转
        //   })
        // }
        return response;
      },
      error => {
        return Promise.reject(error)
      }
    )
    
    /**
     * 封装get方法
     * @param url
     * @param params
     * @returns {Promise}
     */
    
    export function fetch(url,params={}){
      return new Promise((resolve,reject) => {
        instance.get(url,{
          params:params
        })
          .then(response => {
            resolve(response.data);
          })
          .catch(err => {
            reject(err)
          })
      })
    }
    
    
    /**
     * 封装post请求
     * @param url
     * @param data
     * @returns {Promise}
     */
    
    export function post(url,data = {}){
      return new Promise((resolve,reject) => {
        instance.post(url,data)
          .then(response => {
            resolve(response.data);
          },err => {
            reject(err)
          })
      })
    }
    
    /**
     * 封装patch请求
     * @param url
     * @param data
     * @returns {Promise}
     */
    
    export function patch(url,data = {}){
      return new Promise((resolve,reject) => {
        instance.patch(url,data)
          .then(response => {
            resolve(response.data);
          },err => {
            reject(err)
          })
      })
    }
    
    /**
     * 封装put请求
     * @param url
     * @param data
     * @returns {Promise}
     */
    
    export function put(url,data = {}){
      return new Promise((resolve,reject) => {
        instance.put(url,data)
          .then(response => {
            resolve(response.data);
          },err => {
            reject(err)
          })
      })
    }
    
  3. main.js中引入

    将方法放入Vue原型全局变量中

    import {post,fetch,patch,put} from './network/http'
    
    //定义全局变量
    Vue.prototype.$post=post;
    Vue.prototype.$fetch=fetch;
    Vue.prototype.$patch=patch;
    Vue.prototype.$put=put;
    
  4. 在组件中进行使用

    mounted() { this.$fetch("https://mockapi.eolink.com/4kerZSi97ab1195a22fd3d0e63f397de74e237788a9429a/info")
        .then(res =>{
          console.log(res);
        })
    }
    
  5. 结果

    image-20221023174620273

api统一模块化管理

需求:

1.更加模块化

2.更方便多人开发,有效减少解决命名冲突

3.处理接口域名有多个情况

这里这里呢新建了一个api文件夹,里面有一个index.js和一个base.js,以及多个根据模块划分的接口js文件。index.js是一个api的出口,base.js管理接口域名,其他js则用来管理各个模块的接口。

  1. 新建./api/index.js

    index.js是一个api接口的出口,这样就可以把api接口根据功能划分为多个模块,利于多人协作开发,比如一个人只负责一个模块的开发等,还能方便每个模块中接口的命名哦。

    /** 
     * api接口的统一出口
     */
    // 文章模块接口
    import article from '@/api/article';
    // 其他模块的接口……
    
    // 导出接口
    export default {    
        article,
        // ……
    }
    
  2. 新建./api/base.js

    通过base.js来管理我们的接口域名,不管有多少个都可以通过这里进行接口的定义。即使修改起来,也是很方便的。

    最后就是接口模块的说明,例如上面的article.js

    /**
     * 接口域名的管理
     */
    const base = {    
        sq: 'https://xxxx111111.com/api/v1',    
        bd: 'http://xxxxx22222.com/api'
    }
    
    export default base
    
  3. 新建./api/acticle.js

    /**
     * article模块接口列表
     */
    
    import base from './base'; // 导入接口域名列表
    import axios from '@/utils/http'; // 导入http中创建的axios实例
    import qs from 'qs'; // 根据需求是否导入qs模块
    
    const article = {    
        // 新闻列表    
        articleList () {        
            return axios.get(`${base.sq}/topics`);    
        },    
        // 新闻详情,演示    
        articleDetail (id, params) {        
            return axios.get(`${base.sq}/topic/${id}`, {            
                params: params        
            });    
        },
        // post提交    
        login (params) {        
            return axios.post(`${base.sq}/accesstoken`, qs.stringify(params));    
        }
        // 其他接口…………
    }
    
    export default article
    
  4. 将api调用挂载到vue的原型上,在main.js

    import Vue from 'vue'
    import App from './App'
    import router from './router' // 导入路由文件
    import store from './store' // 导入vuex文件
    import api from './api' // 导入api接口
    
    Vue.prototype.$api = api; // 将api挂载到vue的原型上复制代码
    
  5. 然后我们可以在页面中这样调用接口

    methods: {    
        onLoad(id) {      
            this.$api.article.articleDetail(id, {        
                api: 123      
            }).then(res=> {
                // 执行某些操作      
            })    
        }  
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/73947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot知识点

基本介绍 微服务最早由Martin Fowler与James Lewis于2014年共同提出&#xff0c;微服务架构风格是一种使用一套小服务来开发单个应用的方式途径&#xff0c;每个服务运行在自己的进程中&#xff0c;并使用轻量级机制通信&#xff0c;通常是HTTP API&#xff0c;这些服务基于业…

TH8-小视频方案

TH8-小视频方案说明1、我的访客1.1、dubbo服务1.1.1、实体对象1.1.2、定义接口1.1.3、编写实现1.2、记录访客数据1.3、首页谁看过我1.3.1、VO对象1.3.2、MovementController1.3.3、MovementService2、小视频功能说明3、FastDFS2.1、FastDFS是什么&#xff1f;2.2、工作原理2.1.…

会员消费占比高达96%,孩子王究竟是怎么做到的?

&#x1f446;点击关注公众号&#x1f446;1.孩子王&#xff1a;依靠会员“稳江山”2021年上半年&#xff0c;增长黑盒独家发布了一篇关于孩子王的研究文章《万字拆解孩子王&#xff1a;充满矛盾的母婴零售之王》&#xff0c;彼时&#xff0c;孩子王尚在二度上市的前夕等待敲钟…

JAVA SCRIPT设计模式--行为型--设计模式之Template Method模板方法(22)

JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能&#xff0c;所以不可能像C&#xff0c;JAVA等面向对象语言一样严谨&#xff0c;大部分程序都附上了JAVA SCRIPT代码&#xff0c;代码只是实现了设计模式的主体功能&#xff0c;不代…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java课程教学过程f6oz5

对于即将毕业或者即将做课设的同学而言&#xff0c;由于经验的欠缺&#xff0c;面临的第一个难题就是选题&#xff0c;确定好题目之后便是开题报告&#xff0c;如果选题首先看自己学习那些技术&#xff0c;不同技术适合做不同的产品&#xff0c;比如自己会些简单的Java语言&…

BSV 上的 Graftroot

我们已经演示了如何使用无合约的合约在 BSV 上实现 Taproot。我们将展示了其后续提案 Graftroot 可以以类似的方式实施。 BTC 中的 Grabroot 与 Taproot 类似&#xff0c;有两种方式可以使用锁定在由多方创建的聚合公钥 P 中的资金&#xff1a; 合作案例&#xff1a;又名默认…

kubernetes 安装 Harbor 仓库

文章目录kubernetes 安装 Harbor 仓库1. 下载 Harbor2. 安装 docker3. 优化 docker 配置4. 下载 docker-compose5. 安装 Harbor:one: 上传 harbor 文件包:two: 解压:three: 修改配置文件:four: 执行安装脚本安装:five: 配置开机自启6. 登陆测试:one: 浏览器登陆:two: 命令行登陆…

为什么需要对相机标定?

以下内容来自系统教程如何搞定单目/鱼眼/双目/阵列 相机标定&#xff1f; 点击领取相机标定资料和代码 为什么需要对相机标定&#xff1f; 我们所处的世界是三维的&#xff0c;而相机拍摄的照片却是二维的&#xff0c;丢失了其中距离/深度的信息。从数学上可以简单理解为&…

Peppol网络对接流程

Peppol 代表泛欧在线公共采购&#xff0c;现在连接到 Peppol 的组织可以通过高度安全的国际网络交换商业文件。知行软件通过了 PEPPOL 的 AS2 及 AS4 测试&#xff0c;被 OpenPEPPOL AISBL 正式认证为 PEPPOL 接入点供应商。可以在Peppol查询到相关接入点信息&#xff0c;如下&…

TH9-搭建后台系统

TH9-搭建后台系统1、项目架构1.1 概述1.2 API网关1.2.1 搭建网关依赖引导类跨域问题配置类配置文件1.2.2 配置鉴权管理器1.3 Nacos配置中心1.3.1 添加依赖1.3.2 添加bootstrap.yml配置1.3.3 nacos添加配置2、后台系统2.1 概述2.2 环境前端搭建2.2.1 导入数据库2.2.2 导入静态页…

MYSQL-INNODB索引构成详解

作者&#xff1a;郑啟龙 摘要&#xff1a; 对于MYSQL的INNODB存储引擎的索引&#xff0c;大家是不陌生的&#xff0c;都能想到是 B树结构&#xff0c;可以加速SQL查询。但对于B树索引&#xff0c;它到底“长”得什么样子&#xff0c;它具体如何由一个个字节构成的&#xff0c…

插入排序

目录 插入排序 思路: 原理视频: 代码: 时间复杂度: 总结: 题目链接: 插入排序 题目描述&#xff1a; 插入排序基本思想是每一步将一个待排序的记录&#xff0c;插入到前面已经排好序的有序序列中去&#xff0c;直到插完所有元素为止。 输入N个整数&#xff0c;将它们从…

【设计模式】代理模式(Proxy Pattern)

代理模式属于结构型模式&#xff0c;当一个对象不处于相同内存空间时、创建开销大时、需要进行安全控制时或需要代理处理一些其他事物时可以使用代理模式。代理模式通过为另一个类提供一个替身类来控制对这个类的对象的访问。 文章目录代理模式的介绍代理的分类&#xff1a;优点…

了解CloudCompare软件

CloudCompare是一款基于GPL开源协议的3D点云处理软件&#xff0c; 打开一个点云&#xff1b; 按住鼠标拖动旋转会出现坐标轴&#xff1b; 如果选中 TLS/GBL&#xff0c; 会出来右边这个线框&#xff0c;可以一起旋转&#xff1b;查了一下&#xff0c;TLS/GBL似乎是地面激光雷达…

2022年国际工程行业研究报告

第一章 行业概况 国际工程是指一个工程项目从咨询、投资、招投标、承包(包括分包)、设备采购、培训到监理各个阶段的参与者来自不止一个国家&#xff0c;并且按照国际工程项目管理模式进行管理的工程。国际工程是一种综合性的国际经济合作方式&#xff0c;是国际技术贸易的一种…

了解Linux内核内存映射

【推荐阅读】 深入linux内核架构--进程&线程 路由选择协议——RIP协议 轻松学会linux下查看内存频率,内核函数,cpu频率 纯干货&#xff0c;linux内存管理——内存管理架构&#xff08;建议收藏&#xff09; 概述Linux内核驱动之GPIO子系统API接口 一. 内存映射原理 由于所…

shadow阴影属性

shadow阴影属性 盒子阴影CSS中新增了盒子阴影&#xff0c;我们可以使用box-shadow属性为盒子添加阴影 源代码 box-shadow: h-shadow v-shadow blur spread color inset; h-shadow 必需&#xff0c;水平阴影的位置&#xff0c;允许负值 v-shadow…

AI 揭晓答案,2022世界杯冠军已出炉

卡塔尔世界杯&#xff0c;究竟谁能捧起大力神杯&#xff0c;就让我们用机器学习预测一下吧&#xff01; 文章目录数据源技术提升数据集构建功能开发数据分析模型世界杯模拟结论数据源 为了构建机器学习模型&#xff0c;我们需要来自团队的数据。首先需要一些能够说明球队表现的…

Java学习之多态参数

目录 多态参数 父类-Employee类 子类-Worker类 子类-Manager类 Test类-要求1 main类-PolyParameter 在main类中调用Test类的showEmpAnnual(Employee e) 方法 运行结果 Test类-要求2 代码 main方法内调用 分析 运行结果 多态参数 方法定义的形参类型是父类&#xff0…

青竹画材创业板IPO被终止:年营收4.15亿 文投基金是股东

雷递网 雷建平 12月8日河北青竹画材科技股份有限公司&#xff08;简称&#xff1a;“青竹画材”&#xff09;日前IPO被终止。青竹画材曾计划募资4.1亿元&#xff0c;其中&#xff0c;3.08亿元用于美术画材产能扩建项目&#xff0c;2317.35万元用于研发中心项目&#xff0c;7966…