技术背景
Vue3
是目前最新版本的Vue.js
框架,它引入了很多新的特性和改进,包括更好的性能、更灵活的组合式API
、Typescript
的原生支持等。Pinia
是一款基于Vue3
的状态管理库,它提供了类似于Vuex
的功能,同时还充分利用了Vue3
的响应式系统和Composition API
,使得状态管理更加简单和高效。
VueRouter
是Vue.js
官方的路由管理器,它能够帮助我们实现SPA
(单页应用)的页面切换和导航功能。而高德地图API
是一组提供地图展示、位置定位、路径规划等功能的前端API
,它可以与Vue
框架无缝集成,用于展示地图和处理地理位置相关的业务逻辑。
项目构建
首先,在存放项目文件的文件内,通过cmd
打开管理员窗口。使用vue
系列创建项目的方式也就是脚手架,或者通过vue3
配套的vite
创建项目的方式。(本文以vue3
+yarn
项目方式为例)
等待下载完即可,再者为项目取名。
还是和老样子,项目取名之后就是技术栈的选项了。
这里我用的JavaScript
,相对于的typescript
来说它更严谨,一般在大公司或者大型的项目中会用到。
到这一步项目已算是创建好了,cd
到项目里面,再通过yarn
下载依赖
yarn
下完之后,因为要向后端请求数据再下载axios
用来封装服务器的接口
此外也可以再下载css
的预处理器,sass
来写页面的样式或者可以直接使用原生的CSS
。
再者就下载路由(vueRouter
)
pinia
的安装。之后再yarn dev
启动项目即可。
然后在项目文件里面的src
内创建两个文件夹store
和Router
,一个完整的项目就创建好了。
配置路由和pinia
Router配置
可在src
内,创建views
的文件夹,用来管理页面。并创建三个页面。
随后在Router
里面配置路由,首先,我们使用import
语句导入了createRouter
和createWebHashHistory
函数以及相关的组件。接下来,我们使用createRouter
函数创建了一个路由对象。
history
: createWebHashHistory()
指定了路由模式为哈希模式,使用URL
的哈希值来进行路由导航。
routes
:是一个路由配置数组,定义了不同路径和对应的组件。
path
:指定了路径,可以是字符串也可以是正则表达式。
name
:是路由的名称,可以在路由导航中使用。
component
:是对应的组件,可以通过import
导图页面从而达到路由的赖加载。
最后,使用export default
语句输出了这个路由对象。
// 配置路由
import { createRouter, createWebHashHistory } from 'vue-router'
import App from '../App.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'App',
component: App,
},
{
path: '/pagesone',
name: 'pagesone',
component: () => import('../views/PagesOne.vue')
},
{
path: '/pagetwo',
name: 'pagetwo',
component: () => import('../views/PagesTwo.vue')
},
{
path: '/pagesthree',
name: 'pagesthree',
component: () => import('../views/PagesThree.vue')
}
]
})
export default router;
全局配置Router+pinia
在main.js
里面导入配置好的Router
路由,用createApp
函数创建一个Vue
应用实例,并将根组件App
作为参数传入。然后,我们调用app.use(router)
方法,将路由对象作为插件使用。这样,Vue
应用就可以使用VueRouter
提供的路由功能了。
pinia
也是一样,从pinia
库中导入createPinia
函数,pinia
是一个状态管理库,类似于Vuex
,但更简单和灵活。使用createPinia
函数创建一个新的pinia
实例。将pinia
实例添加到Vue
应用中,这样Vue
应用就可以使用pinia
的功能了。
import { createApp } from 'vue'
import './style.css'
// 引入路由
import router from './router/index'
// 配置pinia
import { createPinia } from 'pinia'
import App from './App.vue'
let pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')
使用路由
在App.vue
使用VueRouter
来创建一个tabs
包含三个可切换标签页的界面,每个标签页都有自己的路由和显示内容。就相当于是一个选项卡功能。
<template>
<div class="home-container">
<!-- 其他页面路由入口 -->
<div class="content">
<RouterView></RouterView>
</div>
<!-- tabs标签切换 -->
<div class="tabs-pages">
<div class="tabs" v-for="item in tabs" :key="item">
<RouterLink class="tabs-router" :to="item.link">{{ item.title }}</RouterLink>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { RouterView,RouterLink } from 'vue-router';
let tabs = ref([
{
title: 'A',
link: '/pagesone'
},
{
title: 'B',
link: '/pagetwo'
},
{
title: 'C',
link: '/pagesthree'
}
])
</script>
封装接口
在src
文件下面,创建两个文件utils
和apis
,一个用来封装服务器接口,一个用来封装获取数据的接口。
首先是utils
// 封装axios
import axios from 'axios'; //引入axios
export const Url = '后端提供的服务器接口'
const http = axios.create({
// 服务器地址
baseURL: Url,
// 设置超时
timeout:10000
})
// 请求拦截器
http.interceptors.request.use((reqconfig) => {
// 在当前请求配置对象上,挂Authorization授权数据
reqconfig.headers.Authorization = localStorage.token
// 返回修改的数据
return reqconfig
})
// 暴露
export default http;
apis
获取数据接口封装
<!-- 将服务器接口引入 -->
import http from '../utils/http'
<!-- http.请求方式 -->
export const $_cities = () => http.get('获取数据接口')
获取数据
页面A
在第一个页面,请求数据
<script setup>
// 引入获取城市列表的接口
import { $_cities } from '../apis/cities'
import { ref } from 'vue'
// 声明变量接收数据
let cityData = ref({})
// 发送请求获取数据
$_cities().then(res => {
console.log(res);
})
</script>
获取数据打印出来的效果
虽然这样直接拿到了数据,但是一个数组里面包含了611个数据,所以我们需要定义一个响应式引用cityData
,在获取到城市列表数据后,首先提取出所有城市的首字母并转换为大写,然后去重并排序。接着,根据首字母创建一个对象,对象的每个属性都是一个数组,用于存储对应首字母的所有城市。最后,将处理后的数据赋值给cityData
。使用v-for
指令来遍历cityData
对象,显示城市列表,并渲染到页面上。
然后给每一个city_name
添加一个点击事件,当点击时,会调用clickSon
方法并传入当前城市的名字并且页面B里面实时刷新,clickSon
方法也可用于改变当前选中的城市并跳转到另一个页面。
完整代码如下:
<template>
<div>
<div v-for="(value, groups) in cityData" :key="groups" class="cityList">
<h2>{{ groups }}</h2>
<div class="cityName">
<div :class="{ selected: son == myCityData.citiesData }" @click="clickSon(son)" v-for="son in value"
:key="son" class="cityContent">
{{ son }}
</div>
</div>
</div>
</div>
</template>
<script setup>
// 引入获取城市列表的接口
import { $_cities } from '../apis/cities'
import router from '../router';
// 引入pinia
import { useStore } from '../store/cities'
import { ref } from 'vue'
let myCityData = useStore()
let cityData = ref({})
$_cities().then(res => {
// 获取索取城市的首字母,然后转换为大写
let initial = res.data.map(city => city.city_pre.toUpperCase())
// 获取到之后去重
initial = [...new Set(initial)]
// 然后使用sort方法对这个数组进行排序
initial.sort()
console.log(res);
// 声明一个对象,用来存储获取到的城市数据
let cityObj = {}
for (let groups of initial) {
cityObj[groups] = []
}
// 遍历数组,将获取到的数据push到对应的city中
for (let city of res.data) {
let cityPre = city.city_pre.toUpperCase()
cityObj[cityPre].push(city.city_name)
}
// 最后进行赋值
cityData.value = cityObj
console.log(cityData.value);
})
// 点击改变城市的名字
let clickSon = city => {
myCityData.changeCityName(city)
// 点击城市,跳转到城市对应的map页面
router.push('/pagetwo')
}
</script>
pinia代码
首先,我们导入了defineStore
函数和ref
函数。defineStore
函数用于定义一个store,而ref函数用于创建一个响应式的数据
然后,我们调用defineStore
函数来定义useStore
。在定义useStore
的回调函数中,我们首先创建了两个响应式的数据:citiesData
和cityCode
,它们的初始值都为空字符串。
接下来,我们定义了两个函数:changeCityName
和changeCityCode
。changeCityName
函数用于改变选中城市的名字,它接受一个参数cityName
,并将其赋值给citiesData
。changeCityCode
函数用于改变城市的代码,它接受一个参数code
,并将其赋值给cityCode
。
最后,我们将citiesData
、cityCode
、changeCityName
和changeCityCode
作为useStore
的返回值,使它们可以在其他组件中使用。
// 配置pinia
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useStore = defineStore('city', () => {
//城市名字的初始值
let citiesData = ref('');
//城市的地理编码
let cityCode = ref('')
// 改变选中城市的名字
let changeCityName = (cityName) => {
citiesData.value = cityName
}
//改变城市的地理编码
let changeCityCode = (code) => {
cityCode.value = code
}
// 返回出去
return {
citiesData,
cityCode,
changeCityName,
changeCityCode
}
})
效果图
此时已实现点击城市,实现页面的跳转和切换城市的功能,接下来在点击城市的同时在B页面刷新该城市的map
功能。
请求city_name对应的地图
在获取每一个城市所对应的地图之前,需要拿到获取地图的接口(以高德地图为例),直接在高德API
里面的开发支持中,选择web
服务及web
服务API
,切换到地理编码点击运行拿到获取地图功能的接口。而获取到对应城市的地图需要三个参数:key
、address
和city
。同样的方法切换天气查询即可拿到获取对应城市的天气接口,而获取对应城市的天气的参数就是该城市的地理编码也就是city_code
。
获取key
,鼠标滑到个体头像点击应用管理。然后创建新应用,添加key
时,这一步只需要选择web
服务即可,如此便拿到获取map
功能的key
值了。
封装获取map接口
在utils
内的map.js
使用axios.create
方法创建AL_map
的axios
实例。在创建实例时,传入一个配置对象,其中包括了baseURL
和timeout
两个属性。baseURL
指定了请求的基础URL
为map_ip
,timeout
指定了请求的超时时间为10秒。
最后,我们通过export default
将AL_map
实例导出,使其可以在其他模块中使用
// 封装高德地图接口
import axios from 'axios'
const map_ip = 'https://restapi.amap.com'
// 基础封装
const AL_map = axios.create({
baseURL:map_ip,
timeout:10000
})
export default AL_map;
然后在apis
内的almap.js
封装对应的城市map
接口和对应城市的天气接口。高德的map
接口get
和post
的请求方式都支持。
// 引入封装好的接口
import AL_map from '../utils/map'
export const web_key = '在创建应用管理拿到的key'
// 获取对应城市地图
export const $_getCityMap = params => AL_map.get('/v3/geocode/geo',{params})
// 获取对应城市的天气
export const $_getCityWeather = params => AL_map.get('/v3/weather/weatherInfo',{params})
页面B获取页面A对应城市的map
获取数据
首先,我们使用import
语句导入了useStore
函数来获取Pinia
的store
实例,以及$_getCityMap
接口和web_key
来获取高德地图API
的相关函数和密钥。我们还导入了onMounted
和ref
函数来处理组件的生命周期和创建响应式数据。
接下来,我们调用useStore
函数获取名为cityStore
的store
实例,并使用storeToRefs
函数将store
实例中的citiesData
转换为响应式数据。然后,我们创建了一个名为cityNameMap
的响应式数据,用于存储城市地图数据。
在onMounted
生命周期钩子中,我们调用$_getCityMap
接口来获取城市地图数据。我们传入了web_key
和citiesData.value
作为参数,其中citiesData.value
表示选中的城市数据。
在获取到地图数据后,我们将其赋值给cityNameMap.value
。
接下来,我们将地图数据保存在Pinia
的store
中,通过调用cityStore.changeCityCode
函数来改变城市代码。
最后,我们使用获取到的经纬度创建了一个地图实例,并设置了地图的视图模式、缩放级别和中心点。
完整代码如下:
<template>
<div>
<div id="container" class="container"></div>
</div>
</template>
<script setup>
import { useStore } from '../store/cities'
// 封装好获取城市map的接口
import { $_getCityMap, web_key } from '../apis/almap'
import { onMounted, ref } from 'vue';
// 转换响应数据
import { storeToRefs } from 'pinia';
const cityStore = useStore()
const { citiesData } = storeToRefs(cityStore)
const cityNameMap = ref({})
// 获取map的节点所以要用到onMounted
onMounted(() =>{
$_getCityMap({
key: web_key,//获取到的key
address: citiesData.value, //城市的名字city_name
city: citiesData.value
}).then(res => {
cityNameMap.value = res.data.geocodes[0]
console.log(res);
// 将数据保存在pinia中
cityStore.changeCityCode(cityNameMap.value.adcode);
// 获取经纬度,创建地图
const map = new AMap.Map('container', {
viewMode: '2D', // 默认使用 2D 模式
zoom: 11, //初始化地图层级
center: cityNameMap.value.location.split(',') //初始化地图中心点
});
})
})
</script>
// 地图一定要给宽高,否则出不来
<style lang="scss" scoped>
.container {
width: 100%;
height: 600px;
}
</style>
实现效果图
当我们随机点击A页面中的某一个城市,并且跳转到页面时。即可实现不同页面跳转实时刷新数据的功能啦。
页面C实现A页面对应城市的天气
获取数据
还是一样的操作import
语句导入了useStore
函数来获取Pinia
的store
实例,以及$_getCityWeather
接口和web_key
来获取高德地图API
的相关函数和密钥。我们还导入了ref
函数来创建响应式数据。打印出请求到的数据。
然后,我们使用useStore
函数获取名为cityWeather
的store
实例,并创建了两个响应式数据weatherData
和weatherIcon
,分别用于存储天气数据和天气图标。
在$_getCityWeather
函数中,我们传入了web_key
和cityWeather.cityCode
作为参数,其中cityWeather.cityCode
表示选中的城市代码。
完整代码如下:
<template>
<div class="three-container">
<div class="content">
<img class="weather-img" :src="weatherIcon">
<H3>{{ weatherData.province }}-{{ weatherData.city }}-天气:{{ weatherData.weather }}</H3>
</div>
</div>
</template>
<script setup>
import { useStore } from '../store/cities'
import { $_getCityWeather, web_key } from '../apis/almap'
import { ref } from 'vue'
// 使用pinia
let cityWeather = useStore()
// 天气响应数据
let weatherData = ref({})
// 天气对应的图片
let weatherIcon = ref({})
$_getCityWeather({
key: web_key, //之前拿到的key
city: cityWeather.cityCode //城市的地理编码
}).then(res => {
weatherData.value = res.data.lives[0]
// 根据天气情况设置相应的图标URL
switch(weatherData.value.weather){
case '晴':
weatherIcon.value = '../../public/imgs/sunday.jpg';
break;
case '阴' && '多云':
weatherIcon.value = '../../public/imgs/red.jpg';
break;
case '雨':
weatherIcon.value = '../../public/imgs/rain.jpg';
break;
default:
weatherIcon.value = '../../public/imgs/defaut.jpg';
break;
}
console.log(res);
})
</script>
因为后端给出的数据中没有图片,所以用了本地的图片来代替,通过Switch
方法来渲染每一个城市所对应的天气和图片。之前这里用过if + includes
的方法,只不过这个方法只能渲染出一张图片,再渲染时includes
就报错了,有知道解决方法的小伙伴们可以评论哦!
实现效果图
点击A页面某一个城市,然后跳转到C页面就获取到该城市所对应的天气啦。
总结
该功能是基于vue3
实现不同页面之间的数据的传递以及数据的渲染。功能比较简单实现也比较容易。有不足之处希望小伙伴们,批评指正。