VueRouter + Vuex + mock.js

VueRouter 前端路由

  • Vue路由Vue-Router是一个路由插件,能够轻松管理SPA项目中组件的切换,适合制作单页面组件切换的项目

  • Vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件的访问映射起来

  • vue-router3是专门为vue2开发的

  • 安装

    npm install vue-router@3
    
  • 官网 https://router.vuejs.org/zh/

创建路由组件

在项目中定义一些组件,使用vue-router来控制它们的展示和切换

  • 在主页上声明路由连接,这些连接在点击后会在主页的url后加/#/来访问设置的连接

  • 设置一个占位符,router可以将选中的组件显示在这里

    <template>
      <div id="app">
        <!-- 声明路由连接,跳转时在路径后加# -->
        <router-link to="/discover">发现</router-link>
        <router-link to="/my">我的</router-link>
        <router-link to="/follow">关注</router-link>
        <!-- 声明路由占位符标签,当通过路径找到对应组件时,对应的组件渲染到占位符这里 -->
        <router-view></router-view>
      </div>
    </template>
    
  • 设置不同的组件,对应路由连接的组件

    <template>
        <div>
            <h1>发现</h1>
        </div>
    </template>
    
    <template>
        <div>
            <h1>关注</h1>
        </div>
    </template>
    
    <template>
        <div>
            <h1>我的</h1>
        </div>
    </template>
    
  • 建立一个router文件夹,创建js文件来管理路由连接和组件的映射关系

    • 导入vue和vue-router
    • 导入路由连接需要的组件
    • 设置Vue-Router为Vue的插件
    • 创建一个VueRouter对象,其中设置routes列表,使用path和component表示路由连接和组件的映射
    • 导出router,在main.js中导入
    // 在另外的js文件中描述路由跳转,导入Vue和VueRouter
    import VueRouter from "vue-router";
    import Vue from 'vue';
    // 导入自定义的组件
    import Discover from '../components/Discover.vue'
    import Follow from "../components/Follow.vue"
    import My from "../components/My.vue"
    //将VueRouter设置为Vue的插件
    Vue.use(VueRouter)
    //创建vueRouter类
    const router = new VueRouter({
        // 设置路径对应的组件
        routes: [
            {path: '/discover', component: Discover},
            {path: '/follow', component: Follow},
            {path: '/my', component: My},
        ]
    })
    //导出router,需要在main.js中加载
    export default router;
    
  • 在main.js中导入router

    import Vue from 'vue'
    import App from './App.vue'
    // 导入router
    import router from './router'
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
      //设置对应的router
      router:router
    }).$mount('#app')
    

路由重定向

使得用户访问时,可以自动被重定向到其他页面

const router = new VueRouter({
    routes: [
    		//将首页重定向到discover,vue-router4不配置重定向则中默认跳转到第一条路由连接
        {path: '/', redirect: 'discover'},
        {path: '/discover', component: Discover},
        {path: '/follow', component: Follow},
        {path: '/my', component: My},
    ]
})

嵌套路由

在一个被路由管理的组件中仍然可以写其他路由

<template>
    <div>
        <h1>发现</h1>
        <!-- 设置子路由,名称可以每增加一层路由就带上一次路由的前缀 -->
        <router-link to="/discover/toplist">排行榜</router-link>
        <router-link to="/discover/playlist">歌单</router-link>
        <hr>
        <router-view></router-view>
    </div>
</template>

在多层路径嵌套路由的情况下,通过在routes里的条目增加一个children来设置对应的路由,可以省略前缀

const router = new VueRouter({
    routes: [
        {path: '/', redirect: '/discover'},
        // 使用children的方式,在path前缀的基础上导入子路由,注意没有斜杠
        {path: '/discover', component: Discover,
        children:[
            {path: 'playlist', component: Playlist},
            {path: 'toplist', component: Toplist},
        ]},
        {path: '/follow', component: Follow},
        {path: '/my', component: My},
        //直接按全路径导入子路由,会覆盖上层的组件
        // {path: '/discover/toplist', component: Toplist}
    ]
})

动态路由

动态路由是指,把路由地址中的某些可变部分定义为参数项,从而提高路由的复用性,可以使用冒号来定义一个参数项,这个参数项指的是路由路径中的一部分,并且可以通过route.params.名称 在跳转的组件中获取

<template>
    <div>
        <h1>我的</h1>
        <router-link to="/my/1">歌曲1</router-link>
        <router-link to="/my/2">歌曲2</router-link>
        <router-view></router-view>
    </div>
</template>
<template>
    <!--通过route.param.名称 可以获取路由时动态设置路径位置的参数  -->
    <h4>歌曲{{$route.params.id}} 播放中</h4>
</template>
const router = new VueRouter({
    routes: [
        {path: '/my', component: My, children:[
            // 通过:来定义不同访问路径,但都跳转到一个组件,这个参数可以通过route.params.名称 在跳转到的组件中获取
            {path: ':id', component:Product}
        ]},
    ]
})

另一种获取参数的方式是,在目标组件中定义一个参数,在路由映射时设置props = true,就会把参数传递

<template>
    <p>{{id}}</p>
</template>

<script>
export default {
    props:['id'],
}
</script>
const router = new VueRouter({
    // 设置路径对应的组件
    routes: [
        {path: '/my', component: My, children:[
            // 通过:来定义不同访问路径,但都跳转到一个组件,设置props为true使得参数直接被传递,但目标组件中也要设置对应的props有该名称的参数
            {path: ':id', component:Product, props: true}
        ]},
    ]
})

导航守卫

可以用来控制路由的访问权限,拦截每一个路由规则,从而进行访问权限的判断

使用router.beforeEach可以使用一个全局前置守卫

router.beforeEach((to,from,next) =>{
	if(to.path === '/main' && !isAuthenticated) {
		next('/login')
	}else{
		next()
	}
})
  • to:正在请求访问的目标
  • from:正处于且准备离开的路由
  • 如果在守卫方法中声明了next形参,则必须调用next,否则所有路由都无法访问
    • next():放行
    • next(false): 强制停留在当前页面
    • next(‘/login’): 强制跳转到一个路由页面

Vuex

  • 大型应用有时需要跨越多个组件,只通过父子组件间传递会很困难,所以需要一个全局的状态管理器

  • Vuex是一个专门为vue开发的应用程序状态管理库,采用集中式存储管理用于的所有组件的状态,用Vuex就可以管理在各个组件中的数据

  • 安装

    npm install vuex@next
    

状态管理

  • Vuex核心是一个store,当store中的数据发生变化时,与之绑定的视图也会被重新渲染
  • store中的状态不允许直接修改,改变store中的状态的唯一途径就是显式提交(commit) mutation,这样可以简单的追踪状态的变化
  • vuex中有5个重要的概念,State, Getter, Mutation, Action和Module
    • Vue组件可以读state中的数据并渲染到视图
    • 网络请求是通过DIspatch触发Actions,由Actions完成获取数据
    • 当获取到数据后,Actions需要Commit到Mutaations,更新数据
    • Mutation更新后,再去修改State中的数据

State

  • 创建一个新的store示例,并把需要使用的数据传入state

    const store = createStore({
    	state () {
    		return {
    			count : 0
    		}
    	}
    }),
    mutations: {
    	increment (state) {
    		state.count++
    	}
    }
    
  • 在组件中,使用this.$store.state.名称访问数据,也可以使用mapState辅助函数将其映射

    import {mapState} from 'vuex'
    
    export default {
    	computed: mapState({
    		count: state => state.count,
    		countAlias: 'count',
    		countPlusLocalState(state) {
    			return state.count + this.localcount
    		}
    	})
    }
    

Mutation

  • 在组件中,使用store.commit来提交mutation

    methods: {
    	increment(){
    		this.$store.commit('increment')
    	}
    }
    
  • 也可以使用mapMutation辅助函数将其映射下来

Action

  • 类似于Mutation,但Action不能直接修改状态,只能通过mutation修改,并且包含异步操作

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

Getter

  • Getter维护由State派生的一些状态,随着State的状态变化和变化

    const store = createStore({
    	state: {
    		todos: [
    			{id: 1, text: '...', done: true},
    			{id: 2, text: '...', done: false}
    		]
    	},
    	getters: {
    		doneTodos: (state) => {
    			return state.todos.filter(todo => todo.done)
    		}
    	}
    })
    
  • 在组件中可以直接使用this.$store.getters.名称,也可以使用mapgetters辅助函数映射

使用示例

  • 在main.js中导入并use

    import Vuex from 'vuex'
    Vue.use(Vuex)
    
  • 单独创建一个js文件,一般放在store文件夹下,并导出store

    const store = new Vuex.Store({
      state: {
        count: 1,
      },
    })
    // 导出创建的store
    export default store
    
  • 在main.js中注册这个store,使得其可以在所有文件中使用$store.state.名称 访问

    new Vue({
      router,
      //注册store
      store:store,
      render: h => h(App)
    }).$mount('#app')
    
  • 如果需要更新数据,应该在mutations中定义一个专属的方法,并且在对应组件的method方法中触发这个方法即可,使用this.$store.commit(方法名称)即可

    const store = new Vuex.Store({
      state: {
        count: 1,
      },
      mutations: {
        increment (state) {
          state.count++
        }
      }
    })
    export default store
    
    <template>
      <div class="hello">
        {{ this.$store.state.count }}<br>
        <button @click="add">+1</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      methods:{
        add(){
          // 可以这样做,但不推荐,应该在mutations中定义一个方法,再去触发mutation中的方法
          // this.$store.state.count++
          //这样就可以调用mutation对应的方法,使用vuex统一管理数据
          this.$store.commit("increment")
        }
      }
    }
    </script>
    
  • Vuex中有一个computed称为计算属性,可以基于数据进行操作,可以对数据进行监听;在其中设置一些函数,函数中使用vuex的数据,当vuex数据发生变化时,该方法被重新计算并和视图关联渲染;在需要渲染的地方直接使用该函数即可

    <template>
      <div class="hello">
        <!-- {{ this.$store.state.count }}<br> -->
        {{ count }}
        <button @click="add">+1</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      computed: {
        count() {
          return this.$store.state.count
        }
      },
      methods:{
        add(){
          this.$store.commit("increment")
        }
      }
    }
    </script>
    
    • mapState辅助函数可以帮助我们将数据映射到computed中去

      • 在组件中导入

        import {mapState} from 'vuex'
        
      • 重写computed,使用computed:mapState({})的以下几种方式

        export default {
          name: 'HelloWorld',
          props: {
            msg: String
          },
          computed: mapState({
            //最简单,直接让state中的属性可以被访问
            //  'count',
            // 使用箭头函数来获取值
            count: state => state.count,
            // 使用字符串来获取对应的值,和上面的效果相同,相当于别名
            countAlias: 'count',
            //也可以是一个常规函数来处理数据
            countPlusLocalState (state) {
              return state.count
            }
          }),
          methods:{
            add(){
              this.$store.commit("increment")
            }
          }
        }
        
  • Getter可以处理和过滤一些派生数据,如过滤待办事项中已完成和未完成的任务

    const store = new Vuex.Store({
      state: {
        count: 1,
        // 添加一些待办事项
        todos:[
          {id:1,text:"事件1",done:true},
          {id:2,text:"事件2",done:false}
        ],
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
            // 设置getters,在getters中写一些方法,使得这个方法返回的数据是被筛选和处理过的
            getters: {
              doneTodos: state => {
                return state.todos.filter(todo => todo.done)
              }
            }
    })
    export default store
    
    import {mapState} from 'vuex'
    
    <template>
      <div class="hello">
        <!-- {{ this.$store.state.count }}<br> -->
        {{ count }}
        <button @click="add">+1</button>
        <ul>
          <li v-for="todo in doneTodos" :key="todo.id">{{ todo.text }}</li>
        </ul>
      </div>
    </template>
    
    <script>
    import { mapGetters, mapState } from 'vuex';
    
    export default {
      name: 'HelloWorld',
      props: {
        msg: String
      },
      computed: {
        //因为要嵌套mapGetter,所以需要在computed后加一个括号,使用...展开mapState对象
          ...mapState(
          ['count','todos'
          ]),
          ...mapGetters([
            'doneTodos'
          ]),
      },
      methods:{
        add(){
          this.$store.commit("increment")
        }
      }
    }
    </script>
    
  • mutation也可以进行映射,也可以携带参数payLoad载荷

    methods:{
        add(){
    			//在这里传入一个参数
          this.$store.commit("increment",2)
        }
      }
    }
    
    • 在组件中提交mutation时,可以使用mapMutation把方法映射为组件自己的方法

        methods:{
          ...mapMutations([
            //将this.$store.commit("increment")映射为this.increment()
            'increment',
          ]),
          add(){
            this.$store.commit("increment",2)
          }
        }
      
  • Action主要是做异步操作和提交Mutation,携带一个context参数,用来调用mutation中的方法,需要使用store.dispatch(mutation中的方法)进行触发

    • 也可以使用MapAction来映射成自己的方法,和mapMutation的写法一致

Module

在应用复杂时,可以使用module来定义store,每个模块都可以有自己的state,mutation,action和getters

const moduleA = {
	state:( => {}),
	mutations:{},
	actions:{},
	getters{}
}
//合并store
const store = new Vuex.Store({
	modules: {
    //访问时加上某个模块的名称即可访问模块的store
		a : modulesA,
	}
})
//访问模块store
store.state.a

Mock.js

  • 这是一款前端中用来拦截ajax请求再生成随机数据响应的工具,可以用来模拟服务器响应

  • 非常方便,无侵入性,基本覆盖常用的接口数据类型

  • 支持随机文本,数字,布尔值,日期,邮箱,链接,图片,颜色等

  • 安装

    npm install mockjs
    
  • 使用

    //引入mockjs
    import Mock from 'mockjs'
    //使用mockjs模拟数据
    Mock.mock('/user', {
    	"count": 0,
    	"data":{
    		"time": "@datetime", //生成随机日期
    		"score|1-800": 800, //随机生成1-800的数字
    		"username": "@cname" //随机生成名字
    	}
    })
    

    前端可以按照axios原本的方法发送请求,但是如果项目中使用了mock就会返回自己定义的返回值

Mock核心功能

Mock.mock(rurl?,rtype?,template|function(options))

  • rurl,表示需要拦截的url,可以使用正则

  • rtype,表示需要拦截的Ajax请求类型,如GET/POST

  • template,表示数据模板,可以是对象或者字符串

  • function,表示用于生成响应数据的函数

  • 设置延时

    Mock.setup({
    	timeout: '200-400' //设置延时响应的范围
    })
    

使用演示

  • 在main.js中导入mockjs,当真正使用后端时取消导入即可

    //导入mock
    import './mock'
    
  • 新建一个文件夹,设置一个对应的js文件来设置mock

    import Mock from 'mockjs'
    
    Mock.mock('/user/all',{
        "count" : 2,
        "data" : {
            "username": "@cname",
            "createtime": "@datetime",
            "score|40-100": 1,
            // 生成一张图片,参数为尺寸、背景、文字颜色、格式、文字内容
        "img":"@image('100x100','#ff0066','#ff6600','png','Yan')"
        }
    })
    
  • 在业务代码中正常使用Axios发送请求即可,只要请求路径和mock设置的拦截路径一致,就会被mock拦截并返回数据模板

    <template>
      <div id="app">
        <!-- 显示随机生成的图片,把生成的图片赋值给自己的数据即可 -->
        <img alt="Vue logo" :src="img">
      </div>
    </template>
    
    <script>
    import Axios from "axios"
    export default {
      name: 'App',
      data:function(){
        return {
          "img":""
        }
      },
      mounted:function(){
        // 正常发送axios请求,地址和mock拦截的地址一致
        Axios.get("/user/all").then(response =>{
          console.log(response)
          //注意此时,reponse的第一个data是自身的属性,第二个才是自定义的属性
          this.img = response.data.data.img
        })
      },
    }
    </script>
    

Mock数据生成规则

mock的语法包括两层规范,数据模板和数据占位符

数据模板DTD

基本语法是,name为名称,rule为生成规则,value是对应的值

'name|rule' : value

生成规则

  • 生成字符串

    //重复字符串string生成数据,min是最小值,max是最大值,
    'name|min-max': string
    //重复固定次数count的string生成数据
    'name|count'
    
  • 生成数字

    //每次生成一个自增的数字,number为初始值
    'name|+1': number
    //生成一个min到max的数字,number只是用来确定类型
    'name|min-max': number
    //按范围生成数组,通过dmin和dmax确定保留小数的位数,这是一个浮点数
    'name|min-max.dmin-dmax'
    
  • 生成布尔类型

    //生成布尔值,概率各为二分之一
    'name|1':boolean
    //生成布尔值,值为value的概率为min/(min+max)
    'name|min-max':value
    
  • 生成对象值

    var obj = {
    	a: 1,
    	b: 2,
    }
    var data = Mock.mock({
    	'name|1-3': obj, //从obj中找1-3个属性
    	'name|2':obj
    })
    
  • 生成列表

    'name|1': array //选取array中的一个元素返回
    'name|+1' array	//顺序选取array中的一个元素返回
    'name|min-max':array	//随机重复array后返回
    'name|count':array	//重复一定次数后返回
    
数据占位符

使用@符号来生成一些随机的数据,这些占位符会调用内部的Mock.Random类来生成一些随机的数据

"@datetime" //生成随机日期
"@natural(1,800)" //范围随机数
"@cname"	//随机姓名

匹配携带数据的请求

如果请求中携带了参数,那么只写请求路径是不能被mock捕获的,所以需要使用一个正则来匹配请求

Mock.mock(RegExp('/user/all.*').{
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值