Vue2的路由和异步请求

2022-11-21 10:18:30 浏览数 (1)

目录

1.路由

   1.1路由的作用

1.2使用CLI3创建带路由功能的Vue2项目(案例)

(1)创建vue项目

 (2)选择手动设置特性(Manually select features)

 (3)添加路由特性选项

 1.3 路由使用入门

1.3.1 项目路由规划

 (1)在index.html页面中导入全局样式(可选)

(2)项目根组件App.vue

1.3.2 路由映射定义

1.3.3 通过路由连接(替代)切换页面内容

2 异步请求

2.1 后端RESTful Web服务和代理

(1)后端RESTful Web服务

 (2)服务的代理

2.2 使用 axois 组件请求后端数据

(1)Promise与fetch API

 (2)axios组件

2.3 axios的使用

(1)为Vue项目添加axios

(2)axios基本用法

(3)axios的拦截器

2.4 在项目中实现请求


1.路由

   1.1路由的作用

在传统的Web应用中个,每个URL对应网站中的一个页面;但在SPA(单页面应用中),由于只有一个页面,如果要实现不同URL在相同页面显示不同的路由,就需要根据URL来跟换Web组件,这需要额外的路由技术来实现。

例如以下三个页面,头部和底部都是相同的,而中间需要根据URL的不同,显示不同的中间组件,这时就需要路由。

1.2使用CLI3创建带路由功能的Vue2项目(案例)

(1)创建vue项目

代码语言:javascript复制
vue create funnyshop‐vue2

(2)选择手动设置特性(Manually select features)

 (3)添加路由特性选项

(4)使用历史模式路由等特性项目特性 

 1.3 路由使用入门

1.3.1 项目路由规划

根据功能结构,我们可以把手机微商界面分割为若干个子组件。

具体子组件功能如下所示

组件名称

功能描述

HeaderPart

网页头部的导航和搜索框

FooterPart

页面底部的导航

ProductList

产品列表

Login

登录

Cart

购物车

ProductDetail

产品详情

 (1)在index.html页面中导入全局样式(可选)

代码语言:javascript复制
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF‐8">
<title>Document</title>
    <meta name="viewport" content="width=device‐width, initial‐scale=1.0, maximum‐scale=1.0, minimum‐scale=1.0, user‐scalable=no"
    <link rel="stylesheet" href="<%= BASE_URL %>css/base.css">
    <link rel="stylesheet" href="<%= BASE_URL %>css/page.css">
</head>
<body>
    <div id="app"></div>
</body>
</html>

(2)项目根组件App.vue

在项目根组件中导入公共子组件(header­part、footer­part)和根据路由加载的部分(router­view)

代码语言:javascript复制
<template>
    <div class="pageWrapper">
        <header‐part />
        <div class="pageBreak"></div>
        <router‐view></router‐view>
        <div class="pageBreak"></div>
        <footer‐part />
    </div>
</template>
<script>
import HeaderPart from './components/HeaderPart.vue'
import FooterPart from './components/FooterPart.vue'
export default {
    name:"app",
    components:{HeaderPart, FooterPart}
}
</script>

其中不是我们定义的组件,而是vue的路由组件,只是一个占位符,用于显示不同url下所 需要加载的变化部分。

1.3.2 路由映射定义

带router的vue2项目创建后,src目录下会多出一个名为“router.js”的文件,该文件用于定义路由规则, 也就是不同的URL路径下所要加载的Vue子组件对应关系和参数传递规则。

代码语言:javascript复制
import Vue from 'vue'
import Router from 'vue‐router'
import ProductList from './components/ProductList.vue'
import Login from './components/Login.vue'
import ProductDetail from './components/ProductDetail.vue'
import Cart from './components/Cart.vue'
Vue.use(Router)
export default new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
    {
        path: '/',
        name: 'home',
        component: ProductList
    },
    {
        path: '/product‐list',
        name: 'product‐list',
        component: ProductList
    },
    {
        path:'/login',
        name:'login',
        component: Login
    },
    {
        path:'/product/:id',
        name:'product',
        component: ProductDetail
    },
    {
        path:'/cart',
        name:'cart',
        component: Cart
    },
  ]
})

routes配置中的每一项代表一条路由规则。

path是URL路径,可以定义路径参数(如“/product/:id”中的id);name是路由名称,用于引用; component指定加载的组件名称。

完成组件划分(*.vue)和路由映射(router.js)后,应用就可以根据路由规则显示不同的页面内容了。

1.3.3 通过路由连接(替代<a>)切换页面内容

传统的超链接<a href="...">会使页面发生同步跳转,在SPA应用中只有一张页面,内容的切换全部是异 步方式的。

(1)通过<router-­link> 组件实现“跳转”

router­link是一个路由组件,可以理解为异步的跳转连接标签(<a>)

router­link的to属性可以设置切换的URL。to后面可以设置“静态的URL”或者绑定“一个路由项对象”,例 如下面的两个示例,前者设置URL,后者绑定一个定义了的路由对象。

代码语言:javascript复制
<router‐link to="/login">登录</router‐link>
<router‐link :to="{name:'cart'}">购物车</router‐link>。

(2)通过推送路由变更$router.push(),从而实现“跳转”

配置好路由的项目中,我们可以在任意Vue组件内部,通过this.router访问路由对象,通过 router.push()方法,我们可以向路由推送跳转,实现组件的切换。

代码语言:javascript复制
this.$router.push({name:"product‐list", query:{"name":val}});

 1.3.4 路由参数的获取

(1)路径参数与获取 我们在路由映射中(router.js)定义了以下一项:

代码语言:javascript复制
{ path:'/product/:id', name:'product', component: ProductDetail }

 也就是在跳转到ProductDetail组件时,我们会传入名为id的路径参数。

例如,在产品列表中有以下路由连接,点击后实际的URL可能为 “/product/5”,其中5是id参数。

代码语言:javascript复制
<router‐link :to="{name:'product',params:{id:item.id}}">产品1连接</router‐link>

这时,我们可以在目标组件ProductDetail中,通过 “$route.params.参数名” 获取到路径参数值: 

代码语言:javascript复制
let id = this.$route.params.id;

(2)查询字符串参数的获取

路径参数是URL路径的一部分,通常只能用于传递必要参数(一定要提供的参数),对于可选参数就应 该使用查询字符串的方式来传递,例如:“search?name=abc&page=101”。

对于查询字符串参数,我们可以通过以下方式传递。 

代码语言:javascript复制
searchByProductName(e){
    var val = e.target.value;
    this.$router.push({name:"product‐list", query:{"name":val}});
}

 上述路由推送会产生类似这样的URL:“product­list?name=xxxx” 这时,我们可以在目标组件ProducList中,通过“$router.query.参数名”获取查询字符串参数值。

代码语言:javascript复制
let searchName = this.$route.query.name

2 异步请求

2.1 后端RESTful Web服务和代理

(1)后端RESTful Web服务

SPA一般都采用前后端分离的开发方式。后端可以使用任何的服务器端Web技术,诸如JavaEE、 PHP、Node.js、Python等等,后端提供基于RESTful风格的Web服务,接收前端请求并返回JSON格式 的数据。

这里使用基于Spring Boot的MVC技术提供后端服务,具体细节略去,仅在这里描述所提供的服务接 口。

URL

功能

http://localhost:9090/api/products/latest

获取最新的4种产品,返回JSON格式数据

http://localhost:9090/api/products/1

获取id=1的商品明细信息

http://localhost:9090/api/products?name=青瓷

模糊查询名称中包含“青瓷”的产品信息,返回 JSON格式数据

具体请求效果如下图所示。

 (2)服务的代理

作为前后端分离的项目,后端和前端往往不是运行在同一个服务器中的。例如上述开发中,后端的 JavaEE服务是运行在Tomcat服务器(Spring Boot内嵌的容器)中的,而前端则是使用Node.js提供的测 试服务器。前者域名为“localhost:9090”,而后者是“localhost:3000”。这时,如果前端通过AJAX技术请 求后端数据,就会遇到JavaScript请求不能跨域执行的问题而无法请求。要解决这个问题,要么就需要 使用jsonp协议(跨域JSON协议),要么就要把前后端两个服务器通过代理服务器代理到同一个域名之 下。在实际部署中,我们可以通过Nginx等静态资源服务器实现代理,而在开发中Vue项目可以直接配置 后端代理,把lcoalhost:9090的域名请求代理到localhost:3000域名之下。

在项目根目录下添加 “vue.config.js” 文件,这时vue项目的配置文件,在其中可以设置开发服务器的端 口 “port” 和后端Web服务的代理“proxy”。

代码语言:javascript复制
module.exports = {
    devServer: {
        port: 3000,
        proxy: {
            '/api': {
                target: 'http://localhost:9090', // target host
            },
        }
    }
};

如上所示,当我们请求 “localhost:3000/api/xxx” 时,请求被代理到了 “http://localhost:9090/api/xxx”, 这样前后端就不存在跨域问题了。

2.2 使用 axois 组件请求后端数据

(1)Promise与fetch API

传统的静态网页是通过XMLHttpRequest对象实现对后端数据的异步请求的(例如jQuery的$.ajax),请 求成功后需要执行回调函数。

这种传统的回调在复杂的使用环境中往往会一个接一个的嵌套,让代码陷入难以维护的“Callback地 狱”。新一代的JavaScript(ES6),不再建议使用XMLHttpRequest,而是用一种叫Promise的方式组织 代码,让我们不用陷入到回调的连环套中,而是用平面的方式来处理所有回调。新一代的浏览器中都支 持一个名为fetch的API方法,可以实现Promise方式的请求。

 (2)axios组件

fetch API虽然基于Promise已经很好用了,但fetch功能还是过于原始,在实际应用中我们可能还需要一 些拦截器等扩展模块。

为此 vue 的作者推荐我们使用一个名为 axios 的JavaScript扩展包来实现后台请 求功能。axios有良好的Promise和拦截器机制。

2.3 axios的使用

axios的详细使用请参考互联网 https://www.npmjs.com/package/axios,这里只是简单介绍。

(1)为Vue项目添加axios

在项目根目录中执行以下Node指令: 

代码语言:javascript复制
vue add axios

(2)axios基本用法

Method

Api

Get

axios.get(url).then(successCallback).catch(errorHandler)

Post

axios.post(url, data).then(successCallback).catch(errorHandler)

Put

axios.put(url, data).then(successCallback).catch(errorHandler)

Delete

axios.delete(url).then(successCallback).catch(errorHandler)

(3)axios的拦截器

axios可以在(组件的)请求或相应处理的之前插入拦截器,统一处理异步请求中的公共问题。

例如我们可以在react程序入口“index.js”中添加如下代码,统一在请求发出前添加jwt请求头,或者在响 应出错时定位到页面。 

代码语言:javascript复制
import axios from 'axios';
//请求前处理
axios.interceptors.request.use(
    config => {
        const token = window.localStorage.getItem("jwt‐token");
        if(token)
            config.headers={'Authorization': 'Bearer ' token};
        return config;
    },
    error=>{
        return Promise.reject(error);
    }
);
//响应前处理
axios.interceptors.response.use(
    response => {
        return response;
    },
    error => {
        const httpCode = error.response.status;
        if(httpCode>=400 && httpCode<600){
            window.location.href="/error";
        }
        return Promise.reject(error);
    }
);

2.4 在项目中实现请求

下面演示了ProductList组件中如何向后端请求商品信息。

代码语言:javascript复制
<template>
        <div class="contentWrapper">
            <h4 class="contentTitle">新品上架:</h4>
            <div class="productListWrapper">
                <div class="productWrapper" v‐for="(item,i) in products" :key="i">
                    <router‐link :to="{name:'product',params:{id:item.id}}">
                        <img :src="'images/content/' item.id '‐1.jpg'" alt=""/>
                    </router‐link>
                <div class="productInfoWrapper">
                    <span class="productTitle">{{item.name}}</span>
                    <a class="purchaseButton">¥{{item.unitPrice}} 购买</a>
                </div>
            </div>
          </div>
        </div>
</template>

<script>
export default {
    name: 'product‐list',
    data(){
        return {
            products:[]
        };
    },
    mounted(){
        this.fetchData();
    },
    methods:{
        fetchData(){
            if(this.$route.query.name){
                this.axios.get("/api/products?name=" this.$route.query.name).then(res=>this
            }else{
                this.axios.get("/api/products/latest").then(res=>this.products=res.data);
            }
        }
    }
}
</script>

0 人点赞