项目开发流程
1. 创建客户端项目
1.1 使用 vue-cli(脚手架)搭建项目
#在Github新建Vue-MintShop项目,然后clone到本地
git clone git@github.com:W-Qing/Vue-MintShop.git
cd Vue-MintShop
#创建客户端项目
vue init webpack mintshop-client
cd mintshop-client
npm install
npm run dev 访问: localhost:8080
1.2 项目结构分析
**MintShop-client **
- |-- build : webpack 相关的配置文件夹(基本不需要修改)
- |-- build : webpack 相关的配置文件夹(基本不需要修改)
- |-- config: webpack 相关的配置文件夹(基本不需要修改)
- |-- index.js: 指定的后台服务的端口号和静态资源文件夹
- |-- node_modules
- |-- src : 源码文件夹
- |-- main.js: 应用入口 js (初始化vue实例并使用需要的插件 )
- |-- static: 静态资源文件夹
- |-- .babelrc: babel 的配置文件
- |-- .editorconfig: 通过编辑器的编码/格式进行一定的配置
- |-- .eslintignore: eslint 检查忽略的配置
- |-- .eslintrc.js: eslint 检查的配置
- |-- .gitignore: git 版本管理忽略的配置
- |-- index.html: 默认的主渲染页面文件
- |-- package.json: 应用包配置文件
- |-- README.md: 应用描述说明的 readme 文件
1.3 编码测试与打包发布项目
-
编码测试
npm run dev
访问: http://localhost:8080
编码, 自动编译打包(HMR), 查看效果
-
打包发布
npm run build
npm install -g serve
serve dist
访问: http://localhost:5000
2. 功能需求分析
- 开发前应该首先完成功能模块的分析设计,这里我们可以直接运行项目查看功能演示 自己总结项目功能需求
3. 开发资源准备
- 使用阿里巴巴矢量库
- 将想要的图标添加入库(购物车)
- 将购物车中的图标添加到项目中
- 生成项目图标的Font Class地址
4. Css Reset、Fastclick与Stylus
4.1 Css Reset
-
在项目主目录下的static文件夹内新建css文件夹
-
在css文件夹内新建重置样式文件reset.css
-
在index.html 中引入
<link rel="stylesheet" href="/static/css/reset.css">
4.2 Fastclick
当用户一次点击屏幕之后,浏览器并不能立刻判断用户是要进行双击缩放,还是想要进行单击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 于是,300 毫秒延迟就这么诞生了。
-
安装fastclick库 解决点击响应延时 0.3s 问题
npm Install fastclick --save
-
在main.js中引入,并绑定到body
import FastClick from 'fastclick' FastClick.attach(document.body);
4.3 Stylus
-
安装stylus依赖包
npm install stylus stylus-loader --save-dev
-
在common文件夹下新建stylus文件夹
-
在stylus文件加下面新建mixins.styl文件
-
注意在组件内编写样式时要声明lang和rel
<style lang="stylus" rel="stylesheet/stylus">
5. 源码目录设计
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-naRIF3Rr-1615041312256)(http://owoccema2.bkt.clouddn.com/Readme/vue/mintshop.png)]
6. Vue组件化
6.1 分析整个项目的 vue 组件结构
src
- |-- components------------非路由组件文件夹
- |-- FooterGuide---------------底部组件文件夹
- |-- FooterGuide.vue--------底部组件 vue
- |-- pages-----------------路由组件文件夹
- |-- Msite---------------首页组件文件夹
- |-- Msite.vue--------首页组件 vue
- |-- Search----------------搜索组件文件夹
- |-- Search.vue---------搜索组件 vue
- |-- Order--------------订单组件文件夹
- |-- Order.vue-------订单组件 vue
- |-- Profile--------------个人组件文件夹
- |-- Profile.vue-------个人组件 vue
- |-- App.vue---------------应用根组件 vue
- |-- main.js---------------应用入口 js
- 页面底部的FooterGuide组件只是用来放pages里的组件的容器,所以它不是路由组件
- 页面最上面的标题栏在我们的项目中属于路由组件的组成部分(与中间内容部分在一起)
- 但每个路由组件中都有最顶部的组件且相似度很高,所以可以将其抽取成为一个单独的组件
6.2 编写vue组件模板文件
- pages文件夹下的各个vue组件文件及App.vue和FooterGuide.vue都是这个初始空白模板
<template>
<div>App vue template</div>
</template>
<script>
export default {}
</script>
<style lang="stylus" rel="stylesheet/stylus">
</style>
7. 引入Vue-router
###7.1 下载vue-router
#创建项目时已下载
npm install vue-router --save
7.2 编写router文件夹下的index.js
/*
路由模块
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
// 引入路由组件文件夹下的组件
import Msite from '../pages/Msite/Msite.vue'
import Search from '../pages/Search/Search.vue'
import Order from '../pages/Order/Order.vue'
import Profile from '../pages/Profile/Profile.vue'
// 全局注册Vue-router组件
Vue.use(VueRouter)
// 配置路由表并导出
export default new VueRouter({
//去掉地址中的哈希#
mode: 'history',
routes: [{
path: '/',
redirect: '/msite'
},
{
path: '/msite',
component: Msite,
},
{
path: '/search',
component: Search,
},
{
path: '/order',
component: Order,
},
{
path: '/profile',
component: Profile,
}
]
})
7.3 编写应用的入口文件main.js
// 引入路由 其实就是引入上一步配置好的路由表
import router from './router'
new Vue({
el: '#app',
render: h => h(app),
// 为根组件加入路由
router
})
7.4 在App.vue里使用router-view
<template>
<!-- 修改应用组件的模板 -->
<div id="app">
<!-- 设置路由组件的视图位置 -->
<router-view></router-view>
<!-- 并放置非路由组件 -->
<FooterGuide></FooterGuide>
</div>
</template>
<script>
import FooterGuide from './components/FooterGuide/FooterGuide.vue'
// 引入底部组件并注册
export default {
components: {
FooterGuide
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
// 整个应用组件的样式
#app
width 100%
height 100%
background #f5f5f5
</style>
- App.vue里的根元素
<div id="app"></div>
与外层被注入框架index.html中的<div id="app"></div>
是一致的 - index.html中的
<div id="app"></div>
是指定绑定目标为元素的根路径,而App.vue文件里的<div id="app"></div>
则是提供注入绑定元素的内容,两者在运行时指的是同一个DOM元素
7.5 运行并请求不同路由路径查看效果
通过切换url地址里的hash值(miste/order/search/profile),页面会显示不同的路由模板内容。
8. 编写组件代码
8.1 FooterGuide组件
功能及实现
- 通过编程式导航实现路由的切换显示($router)
- 通过动态 class 和$route.path 来实现 tab 样式切换
- 通过阿里图标库, 显示导航图标
代码
<footer class="footer_guide border-1px">
<a href="javascript:;" class="guide_item on">
<span class="item_icon">
<i class="iconfont icon-food"></i>
</span>
<span>外卖</span>
</a>
<!--其他三部分类似-->
</footer>
<style lang="stylus" rel="stylesheet/stylus">
/*引入公共样式*/
@import "../../common/stylus/mixins.styl"
.footer_guide
/*顶部有白色的边框*/
top-border-1px(#e4e4e4)
position fixed
z-index 100
left 0
right 0
bottom 0
background-color #fff
width 100%
height 50px
display flex
.guide_item
display flex
flex 1
text-align center
flex-direction column
align-items center
margin 5px
color #999999
&.on
color #02a774
span
font-size 12px
margin-top 2px
margin-bottom 2px
.iconfont
font-size 22px
</style>
此时,页面已达到理想效果。接着修改template模板,为其加入路由与样式的切换控制。
<div class="guide_item" @click="goto('/msite')" :class="{on: isCurrent('/msite')}">
<span class="item_icon">
<i class="iconfont icon-food"></i>
</span>
<span>首页</span>
</div>
<!--其他三个部分类似,只是路由不同-->
再补充相应的函数方法
export default {
methods: {
goto (path) {
this.$router.replace(path)
},
isCurrent (path) {
// console.log(this.$route.path)
return this.$route.path === path
}
}
}
至此,底部组件完成,可实现点击不同的选项切换不同的路由组件。
8.2 各导航路由组件
8.2.1 Msite组件
功能区域划分
- 最顶部的title标题栏部分
- 上方的nav轮播区域
- 商家列表展示区域
图片资源
Msite组件页面的轮播图及商家列表都需要用到一些图片资源文件,所以在msite.vue同级目录下新建images文件夹,以便放置各种不同类型的图片资源。
代码
<template>
<section class="msite">
<!--首页头部title-->
<!--由msite_header改成header-->
<header class="header">
<span class="header_search">
<i class="iconfont icon-sousuo"></i>
</span>
<span class="header_title">
<span class="header_title_text ellipsis">芝罘区鲁东大学北区(青年南路)</span>
</span>
<span class="header_login">
<span class="header_login_text">登录|注册</span>
</span>
</header>
<!--首页导航轮播-->
<nav class="msite_nav">
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">
<a href="javascript:" class="link_to_food">
<div class="food_container">
<img src="./images/nav/1.jpg">
</div>
<span>甜品饮品</span>
</a>
<a href="javascript:" class="link_to_food">
<div class="food_container">
<img src="./images/nav/2.jpg">
</div>
<span>商超便利</span>
</a>
<!--下面的图片省略-->
</div>
<div class="swiper-slide">
<a href="javascript:" class="link_to_food">
<div class="food_container">
<img src="./images/nav/9.jpg">
</div>
<span>甜品饮品</span>
</a>
<!--同样省略-->
</div>
</div>
<!-- 轮播图页码 -->
<div class="swiper-pagination"></div>
</div>
</nav>
<!--首页附近商家列表-->
<div class="msite_shop_list">
<div class="shop_header">
<i class="iconfont icon-xuanxiang"></i>
<span class="shop_header_title">附近商家</span>
</div>
<div class="shop_container">
<ul class="shop_list">
<li class="shop_li border-1px">
<a>
<div class="shop_left">
<img class="shop_img" src="./images/shop/1.jpg">
</div>
<div class="shop_right">
<section class="shop_detail_header">
<h4 class="shop_title ellipsis">锄禾日当午,汗滴禾下土</h4>
<ul class="shop_detail_ul">
<li class="supports">保</li>
<li class="supports">准</li>
<li class="supports">票</li>
</ul>
</section>
<section class="shop_rating_order">
<section class="shop_rating_order_left">
<div class="star star-24">
<span class="star-item on"></span>
<span class="star-item on"></span>
<span class="star-item on"></span>
<span class="star-item half"></span>
<span class="star-item off"></span>
</div>
<div class="rating_section">
3.6
</div>
<div class="order_section">
月售106单
</div>
</section>
<section class="shop_rating_order_right">
<span class="delivery_style delivery_right">硅谷专送</span>
</section>
</section>
<section class="shop_distance">
<p class="shop_delivery_msg">
<span>¥20起送</span>
<span class="segmentation">/</span>
<span>配送费约¥5</span>
</p>
</section>
</div>
</a>
</li>
<!--省略其他店铺展示-->
</ul>
</div>
</div>
</section>
</template>
<!--省略js-->
**要注意首页的头部标题部分的样式,在其他的组件中都可以进行重用。**所以将header标签的类名由msite_header改成header。接下来在其他组件中可以直接使用(当然header里的部分样式其他组件用不到,到时再进一步抽取公共的css样式。)
<style lang="stylus" rel="stylesheet/stylus">
@import "../../common/stylus/mixins.styl"
.msite //首页
width 100%
.header
background-color #02a774
position fixed
z-index 100
left 0
top 0
width 100%
height 45px
.header_search
position absolute
left 15px
top 50%
transform translateY(-50%)
width 10%
height 50%
.icon-sousuo
font-size 25px
color #fff
.header_title
position absolute
top 50%
left 50%
transform translate(-50%, -50%)
width 50%
color #fff
text-align center
.header_title_text
font-size 20px
color #fff
display block
.header_login
font-size 14px
color #fff
position absolute
right 15px
top 50%
transform translateY(-50%)
.header_login_text
color #fff
/*下面的样式省略*/
</style>
接下来的几个路由组件都类似,都是先修改template模版,然后引入mixins.styl 样式文件和上面提到的公共的header部分的样式。
8.2.2 Search组件
<section class="search">
<header class="header">
<div class="header_title">
<span class="header_title_text">搜索</span>
</div>
</header>
<form class="search_form" action="#">
<input type="search" name="search" placeholder="请输入商家或美食名称"
class="search_input">
<input type="submit" class="search_submit">
</form>
</section>
<!--省略js与style-->
8.2.3 Order组件
在order.vue同级目录下新建images文件夹,再新建order文件夹,存放订单组件用到的图片资源。
<div>
<section class="order">
<header class="header">
<a class="header_title">
<span class="header_title_text">订单列表</span>
</a>
</header>
<section class="order_no_login">
<img src="./images/order/person.png">
<h3>登录后查看外卖订单</h3>
<button>立即登陆</button>
</section>
</section>
</div>
<!--省略js与style-->
8.2.4 Profile组件
<div>
<section class="profile">
<header class="header">
<a class="header_title">
<span class="header_title_text">我的</span>
</a>
</header>
<section class="profile-number">
<a href="javascript:" class="profile-link">
<div class="profile_image">
<i class="iconfont icon-yonghuming"></i>
</div>
<div class="user-info">
<p class="user-info-top">登录/注册</p>
<p>
<span class="user-icon">
<i class="iconfont icon-msnui-tel icon-mobile"></i>
</span>
<span class="icon-mobile-number">暂无绑定手机号</span>
</p>
</div>
<span class="arrow">
<i class="iconfont icon-previewright"></i>
</span>
</a>
</section>
<section class="profile_info_data border-1px">
<ul class="info_data_list">
<a href="javascript:" class="info_data_link">
<span class="info_data_top"><span>0.00</span>元</span>
<span class="info_data_bottom">我的余额</span>
</a>
<a href="javascript:" class="info_data_link">
<span class="info_data_top"><span>0</span>个</span>
<span class="info_data_bottom">我的优惠</span>
</a>
<a href="javascript:" class="info_data_link">
<span class="info_data_top"><span>0</span>分</span>
<span class="info_data_bottom">我的积分</span>
</a>
</ul>
</section>
<section class="profile_my_order border-1px">
<!-- 我的订单 -->
<a href='javascript:' class="my_order">
<span>
<i class="iconfont icon-dingdan"></i>
</span>
<div class="my_order_div">
<span>我的订单</span>
<span class="my_order_icon">
<i class="iconfont icon-previewright"></i>
</span>
</div>
</a>
<!-- 积分商城 -->
<a href='javascript:' class="my_order">
<span>
<i class="iconfont icon-jifen"></i>
</span>
<div class="my_order_div">
<span>积分商城</span>
<span class="my_order_icon">
<i class="iconfont icon-previewright"></i>
</span>
</div>
</a>
<!-- Mint外卖会员卡 -->
<a href="javascript:" class="my_order">
<span>
<i class="iconfont icon-viptehuishiduan"></i>
</span>
<div class="my_order_div">
<span>Mint外卖会员卡</span>
<span class="my_order_icon">
<i class="iconfont icon-previewright"></i>
</span>
</div>
</a>
</section>
<section class="profile_my_order border-1px">
<!-- 服务中心 -->
<a href="javascript:" class="my_order">
<span>
<i class="iconfont icon-lianxikefu"></i>
</span>
<div class="my_order_div">
<span>服务中心</span>
<span class="my_order_icon">
<i class="iconfont icon-previewright"></i>
</span>
</div>
</a>
</section>
</section>
</div>
<!--省略js与style-->
8.3 HeaderTop组件
8.3.1组件的构成分析
- 中间有一个固定的标题栏,只是用在不同的路由组件中显示的内容不同
- 标题栏两侧可能有搜索框之类的部分(Msite)也可能没有
- 此组件为非路由组件的公用组件(所以在Components文件夹内)
8.3.2 功能实现的技术
- 标题栏两侧是否有其他部分,要用到slot插槽进行组件间通信
- slot 通信是标签, 而不是单纯的数据
- 中间标题栏接收的文本可以用props
8.3.3 组件代码
<template>
<header class="header">
<!-- 插槽是父组件与子组件的通讯方式,子组件中的slot可以显示父组件传递给子组件的内容 -->
<slot name="left"></slot>
<span class="header_title">
<span class="header_title_text ellipsis">{
{title}}</span>
</span>
<slot name="right"></slot>
</header>
</template>
<script>
export default {
// 外部组件传递给此组件的属性
props: {
title: String
}
}
</script>
8.3.4 在路由组件中使用
- 在要使用此头部组件的文件中引入并注册HeaderTop组件
//Msite、Order、Search、Profile中都要引入注册才能使用
import HeaderTop from '../../components/HeaderTop/HeaderTop.vue'
export default {
components: {
HeaderTop
}
}
-
然后使用
<HeaderTop></HeaderTop>
标签设置这个头部组件这里以Msite.vue为例,先删除静态模版里的Header部分,替换成HeaderTop组件
<!-- 使用 title 来给头部组件传递数据 -->
<HeaderTop title="芝罘区鲁东大学北区(青年南路)">
<!-- 要使用slot="left"指定插入的插槽位置 -->
<span class="header_search" slot="left">
<i class="iconfont icon-sousuo"></i>
</span>
<span class="header_login" slot="right">
<span class="header_login_text">
登录|注册
</span>
</span>
</HeaderTop>
- 在其他几个组件中的用法是一样的,同时还省去了slot插槽部分。
8.4 使用swiper插件实现轮播图
下载安装: npm install swiper --save
Msite.vue的HTML部分:
<!--在页面msite_nav导航部分使用swiper-->
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">1</div>
<div class="swiper-slide">2</div>
<div class="swiper-slide">3</div>
</div>
<!-- swiper轮播图圆点 -->
<div class="swiper-pagination"></div>
</div>
script部分引入并初始化:
<script>
import Swiper from 'swiper'
//同时引入swiper的 css文件
import 'swiper/dist/css/swiper.min.css'
export default {
//注意要在页面加载完成之后(mounted)再进行swiper的初始化
mounted () {
//创建一个swiper实例来实现轮播
new Swiper('.swiper-container', {
autoplay: true,
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
clickable: true
}
})
}
}
</script>
具体用法参考Swiper官方文档
8.5 拆分出商家列表ShopList组件
- 商家列表是位于首页轮播图下面的部分,可以拆分为一般组件
- 在components文件夹下新建ShopList文件夹并新建ShopList.vue文件
- 该组件中用到需要一些图片资源,所以在ShopList文件夹下还需要新建images文件夹
- 将原本放在Msite文件夹下的shop和stars图片资源移动到新建的images文件夹内 (其实都应该是动态地从后台获取)
- 将Msite.vue模板中的
<div class="shop_container"></div>
部分及相应的stylus样式代码移动到新建的ShopList.vue组件 - 注意还要引入公共的css代码mixins.styl
- 最后在Msite.vue中import引入商家列表组件并注册使用
8.6 Login组件
资源文件准备
- 登录组件为一级路由组件,所以在pages文件夹下新建Login文件夹和Login.vue
- template模板里会用到一张svg图片(静态的验证码图片),所以还要在Login文件夹下新建images文件夹
配置路由跳转
- 将路由组件映射为路由,在router下的index.js文件里进行配置
- 登录组件的路由是从个人中心Profile组件里跳转而来的,所以要修改Profile.vue
<!--将class为profile-link的a标签替换为router-link-->
<a href="javascript:" class="profile-link">
...
</a>
<!--但要注意不要忘记class类名-->
<router-link to="/Login" class="profile-link">
<!--先不考虑未登录的情况-->
...
</router-link>
编写Login.vue代码
- 利用
@click="$router.back()"
实现点击页面的箭头返回上一级路由/Profile的功能
实现控制Footer的显示隐藏
-
已确定底部的四个路由组件需要显示Footer部分
-
而Login组件为一级路由组件,且不需要显示底部的FooterGuide导航组件
-
所以为路由组件添加meta元数据来标识是否显示Footer
{ path: '/msite', component: Msite, meta: { showFooter: true } }, /*Order、Searh、Profile组件都要添加meta*/
-
在App.vue组件中通过代表当前路由的
$route
就能得到添加的meta属性,然后根据属性值来确定是否显示FooterGuide组件<FooterGuide v-show="$route.meta.showFooter"></FooterGuide>
其他细节
-
注意到一个问题,在一个路由组件(Msite)将页面下拉,再切换到其他路由组件(Profile),页面不会自动回到顶部。
/*解决方法 其他页面中类似*/ .profile width 100% /*添加一行overflow hidden*/ overflow hidden
9. 后台应用
9.1 说明
- 整个项目为前后端分离的项目:mintshop-client 与 mintshop-server
- 后台应用负责处理前台应用提交的请求, 并给前台应用返回 json 数据
- 前台应用负责展现数据, 与用户交互, 与后台应用交互
9.2 运行
- 确保启动 mongodb 服务
- 进入mintshop-server文件夹,启动服务器应用:
npm start
9.3 API文档
具体API文档详见mintshop-server/API.md,然后可以使用Postman来进行接口测试
10. 前后台交互 ajax
- 测试完后台接口,则需要写前后台交互的ajax文件
- 在src/api文件夹下新建index.js与ajax.js
- 首先需要安装axios
npm install axios--save
- 首先需要安装axios
10.1 封装ajax请求函数
- 为了实现统一向后端发送请求数据,所以需要封装一个ajax请求函数
/*
ajax 请求函数模块
*/
import axios from 'axios'
/**
* 向外部暴漏一个函数 ajax
* @param {*} url 请求路径,默认为空
* @param {*} data 请求参数,默认为空对象
* @param {*} type 请求方法,默认为GET
*/
export default function ajax(url = '', data = {
}, type = 'GET') {
// 返回值 Promise对象 (异步返回的数据是response.data,而不是response)
return new Promise(function (resolve, reject) {
//(利用axios)异步执行ajax请求
let promise // 这个内部的promise用来保存axios的返回值(promise对象)
if (type === 'GET') {
// 准备 url query 参数数据
let dataStr = '' // 数据拼接字符串,将data连接到url
Object.keys(data).forEach(key => {
dataStr += key + '=' + data[key] + '&'
})
if (dataStr !== '') {
dataStr = dataStr.substring(0, dataStr.lastIndexOf('&'))
url = url + '?' + dataStr
}
// 发送 get 请求
promise = axios.get(url)
} else {
// 发送 post 请求
promise = axios.post(url, data)
}
promise.then(response => {
// 成功回调resolve()
resolve(response.data)
})
.catch(error => {
// 失败回调reject()
reject(error)
})
})
}
- 通过对axios返回的promise对象再包装一层Promise的方法,来简化外部的调用
10.2 封装接口请求函数
- 有了发送请求数据的ajax函数,还需要封装一些与后台交互的接口函数
- 根据接口文档来定义接口请求函数
/*与后台交互模块 (依赖已封装的ajax函数)
*/
import ajax from './ajax'
/**
* 获取地址信息(根据经纬度串)
* 这个接口的经纬度参数是在url路径里的,没有query参数
*/
export const reqAddress = geohash => ajax(`/position/${
geohash}`)
/**
* 获取 msite 页面食品分类列表
*/
export const reqCategorys = () => ajax('/index_category')
/**
* 获取 msite 商铺列表(根据query参数:经纬度)
* 将经纬度两个数据作为一个参数对象传入
* 也可以两个数据分别传入ajax, 然后再放入一个对象参数内, 如下面的手机号验证码接口
*/
export const reqShops = ({
latitude,
longitude
}) => ajax('/shops', {
latitude,
longitude
})
/**
* 账号密码登录
*/
export const reqPwdLogin = (name, pwd, captcha) => ajax('/login_pwd', {
name,
pwd,
captcha
}, 'POST')
/**
* 获取短信验证码
*/
export const reqSendCode = phone => ajax('/sendcode', {
phone
})
/**
* 手机号验证码登录
*/
export const reqSmsLogin = (phone, code) => ajax('/login_sms', {
phone,
code
}, 'POST')
/**
* 获取用户信息(根据会话)
*/
export const reqUser = () => ajax('/userinfo')
/*
* 请求登出
*/
export const reqLogout = () => ajax('/logout')
10.3 配置代理并测试接口实现ajax跨域请求
问题分析:
-
目前为止运行的所有页面都是静态页面
-
接下来先测试使用封装的ajax接口请求函数来异步获取数据
// 先在App.vue中引入封装的接口函数 import { reqCategorys} from './api' // 然后再调用接口,测试打印数据 export default { async mounted () { const result = await reqCategorys() console.log(result) }, components: { FooterGuide } }
-
打开浏览器,运行项目会报错
GET http://local:4000/index_category 404(Not Foun