vue框架实现简易天气预报

1,449 阅读8分钟

今天带大家用vue框架实现简易的天气预报页面。

准备步骤我就不过多赘述,详情可以参考# Vue3项目搭建与基础知识详解这篇文章。

image.png

index.js

首先,在index.js文件下添加以下代码:

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [{
    path:'/',
    component: () => import('../views/Index.vue')
  }]

})

export default router

这段代码创建了一个vue路由器实例,通过路径为:'/' 的主页导入组件,组件文件路径为:'../views/Index.vue',通过router接收,然后通过export default router抛出。

main.js

然后在main.js文件下添加以下代码:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './assets/reset.css'

const app = createApp(App)

app.use(router)

app.mount('#app')
  • import { createApp } from 'vue': 从Vue.js库中导入createApp函数,用来创建Vue应用实例。

  • import App from './App.vue': 这行代码导入应用根组件App.vue

  • import router from './router': 这里导入之前抛出的路由配置。

  • import './assets/reset.css': 导入全局CSS样式文件reset.css,重置浏览器默认样式。# CSS Tools: Reset CSS,需要可自取。

image.png

  • const app = createApp(App): 创建一个以App作为根组件的Vue应用实例。

  • app.use(router).mount('#app'): 使用路由实例,并绑定在HTML中id='app'的元素上。

App.vue

代码如下:

<template>
  <div>

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

<script setup>

</script>

<style lang="css" scoped>

</style>

<router-view></router-view>为路由入口,根据url的变化,不同的组件会被渲染在这个位置。

Index.vue

import { reactive, ref } from 'vue'
import { areaList } from '@vant/area-data';

const selectCity = ({ selectedOptions }) => {
    console.log(selectedOptions[1].text)
    state.city = selectedOptions[1].text
    getWeather(state.city)
    state.show = false
}

import { reactive, ref, nextTick } from 'vue'

  • reactive(): 这个函数用于创建一个响应式的对象。它接受一个普通的JavaScript对象作为参数,并返回一个响应式的版本。
  • ref(): 这个函数用于创建一个响应式的引用类型,通常用于简单的值如字符串、数字或布尔值。ref返回一个对象,该对象具有一个.value属性,用于获取和设置值。
  • nextTick(): 这是一个异步函数,用于在DOM更新完成后执行回调函数。

import { areaList } from '@vant/area-data';

  • @vant/area-data模块导入了areaList@vant/area-data是Vant 框架提供的地区数据包,包含了中国的省份、城市和区县的详细信息。areaList是一个对象或数组,可以用来构建地区选择器或下拉菜单等界面组件,让用户能够位置信息。 ⚠:需要在终端命令行输入:npm i @vant/area-data,安装 @vant/area-data 包来引入数据。

这里的意图是创建一个地区选择器,允许用户从预定义的地区列表中选择他们所在的省份、城市和区县。使用reactiveref可以帮助你更灵活地管理用户的选择状态,并响应这些状态的改变以更新UI。

import * as echarts from 'echarts':导入ECharts库,用于创建图表。 import { onMounted } from 'vue':导入Vue的生命周期钩子,用于初始化Echarts图表。

let now = ref('00:00:00')
setInterval(() => {
    now.value = new Date().toLocaleTimeString()
}, 1000);

然后定义一个变量now接收ref创建的一个初始值为'00:00:00'的响应式引用。然后用setInterval方法设置一个定时器,每隔1秒执行一次。在每次执行时,now.value会被更新为当前系统时间的字符串表示形式,格式化为本地时间格式。new Date().toLocaleTimeString()是一个JS方法,用于获取当前时间的本地化字符串表示。

const state = reactive({
    city: '',
    today: {},
    show: false,
    tomorrow: {},
    aftertomorrow: {}
})

然后设置状态state

  • city:空字符串,用于存储用户选择的城市名称。
  • today:空对象,用于存储当天的天气信息。
  • show:布尔值,初始为false,用于控制显示和隐藏地区选择栏。
  • tomorrow:空对象,用于存储预测的明天的天气信息。
  • aftertomorrow:空对象,用于存储预测的后天的天气信息。
AMap.plugin('AMap.CitySearch', function () {
    var citySearch = new AMap.CitySearch()
    citySearch.getLocalCity(function (status, result) {
        if (status === 'complete' && result.info === 'OK') {
            // 查询成功,result即为当前所在城市信息
            console.log(result);
            state.city = result.city
            getWeather(result.city)
        }
    })
})

const getWeather = (city) => {
    //加载天气查询插件
    AMap.plugin("AMap.Weather", function () {
        //创建天气查询实例
        var weather = new AMap.Weather();
        //执行实时天气信息查询
        weather.getLive(city, function (err, data) {
            console.log(err, data);
            state.today = data
        });
    });

    //加载天气查询插件
    AMap.plugin("AMap.Weather", function () {
        //创建天气查询实例
        var weather = new AMap.Weather();
        //执行实时天气信息查询
        weather.getForecast(city, function (err, data) {
            console.log(err, data);
            state.tomorrow = data.forecasts[1]
            state.aftertomorrow = data.forecasts[2]

            nextTick(() => { // 保证内部的逻辑会在页面渲染完毕后执行
                initEcharts(data.forecasts.map(item => item.dayTemp))
            })
        });
    });
}

用高德地图插件CitySearch来获取用户当前所在的城市信息,并将获取到的城市名称存储到之前定义的状态state中。定义citySearch实例。变量来接收new AMap.CitySearch()对象,创建citySearch实例。 调用该插件中的getLocalCity()方法实施定位,获取用户的位置信息。查询成功后输出用户的位置信息,并将结果resultcity属性赋值给state对象中city属性。然后调用getWeather()方法获取当地的天气信息。

定义getWeather()函数,接收city作为参数,然后调用高德地图插件中WeathergetLivegetForecast方法获取指定的城市的当天和预测未来3天的天气信息。但是由于我们只设置了明天和后天的状态,所以只接收了明后两天的天气信息。当查询成功后更新state中的todaytomorrowaftertomorrow的信息。

接着调用nextTick(() => {initEcharts(data.forecasts.map(item => item.dayTemp))})初始化Echarts图表,并将data.forecastsdayTemp属性传递给initEcharts()函数。 image.png

const selectCity = ({ selectedOptions }) => {
    console.log(selectedOptions[1].text)
    state.city = selectedOptions[1].text
    getWeather(state.city)
    state.show = false
}

定义selectCity()函数,接收selectedOptions[1].text属性,为城市名称。

selectedOptions来自<van-area>组件的事件处理器,<van-area>是Vant库中的一个组件,用于展示地区选择器,允许用户选择省市区等地理区域。当用户在地区选择器中做出选择并确认选择时,<van-area>组件会触发一个confirm事件。在你的代码中,这个事件处理器被定义为selectCity函数。confirm事件会传递一个对象作为参数,这个对象包含了用户选择的地区信息,其中selectedOptions属性是一个数组,包含了用户选择的地区层级(如省、市、区)的详细信息。这个会在后续完整代码中展示出来。

selectedOptions[1].text的值传给state.city,然后调用getWeather()方法,获取你选择的城市的天气信息,通过设置state.show = false来控制选择城市完成后隐藏地区选择栏,提升用户体验。

const echartsWrap = ref(null)
const initEcharts = (arr) => {
    // console.log(echartsWrap.value);
    let myChart = echarts.init(echartsWrap.value)
    myChart.setOption({
        tooltip: {},
        xAxis: {
            type: 'category',
            data: ['今天', '明天', '后天', '大后天']
        },
        yAxis: {},
        series: [
            {
                name: '温度',
                type: 'line',
                data: arr
            }
        ]
    });
}

定义echartsWrap变量接收ref创建的一个初始值为null的响应式引用。

定义 initEcharts() 函数,接收arr作为参数,并初始化图表

定义变量myChart接收echartsWrap.value,也就是echartsWrap引用所指向的DOM元素。

初始化图表后,使用setOption方法来设置图表的配置选项。

  • xAxis设置了x轴的类型为category,数据点为['今天', '明天', '后天', '大后天']
  • yAxis为空对象,会根据数据自动调整。
  • series设置了一个名为温度的数据系列,类型为line(即为折线图),并使用arr作为数据源,映射成折线图上的点。

完整Index.vue代码:

<template>
    <div class="container">
        <div class="nav">
            <div class="time">{{ now }}</div>
            <div class="city" @click="state.show = true">切换城市</div> //通过点击"切换城市"触发显示地区选择栏
        </div>
        <div class="city-info">
            <p class="city">{{ state.city }}</p>
            <p class="weather">{{ state.today.weather }}</p>
            <h2 class="temp">
                <em>{{ state.today.temperature }}</em></h2>
            <div class="detail">
                <span>风力:{{ state.today.windPower }} 级| </span>
                <span> 风向:{{ state.today.windDirection }} 风| </span>
                <span>空气湿度: {{ state.today.humidity }}%</span>
            </div>
            <div class="future">
                <div class="group">
                    明天:
                    <span class="tm">白天:{{ state.tomorrow.dayTemp }}℃
                        {{ state.tomorrow.dayWeather }}
                        {{ state.tomorrow.dayWindDir }}
                        {{ state.tomorrow.dayWindPower }}</span>
                    <span class="tm">夜间:{{ state.tomorrow.nightTemp }}℃
                        {{ state.tomorrow.nightWeather }}
                        {{ state.tomorrow.nightWindDir }}
                        {{ state.tomorrow.nightWindPower }}</span>
                </div>
                <div class="group">
                    后天:
                    <span class="tm">白天:{{ state.aftertomorrow.dayTemp }}℃
                        {{ state.aftertomorrow.dayWeather }}
                        {{ state.aftertomorrow.dayWindDir }}
                        {{ state.aftertomorrow.dayWindPower }}</span>
                    <span class="tm">夜间:{{ state.aftertomorrow.nightTemp }}℃
                        {{ state.aftertomorrow.nightWeather }}
                        {{ state.aftertomorrow.nightWindDir }}
                        {{ state.aftertomorrow.nightWindPower }}</span>
                </div>
            </div>

        </div>
        <div class="echarts-wrap" ref="echartsWrap"></div>
        <van-action-sheet v-model:show="state.show">
            <div class="content">
                <van-area title="地区" :area-list="areaList" :columns-num="2" 
                    @confirm="selectCity"  //当用户在地区选择器中做出选择并点击确认按钮时会触发`selectCity`函数这个函数负责处理用户的选择通常会更新应用的状态如更新当前选择的城市。
                    @cancel="state.show = false" />
            </div>
        </van-action-sheet>
    </div>
</template>

<script setup>
import { reactive, ref, nextTick } from 'vue'
import { areaList } from '@vant/area-data';
import * as echarts from 'echarts';
import { onMounted } from 'vue';

let now = ref('00:00:00')
setInterval(() => {
    now.value = new Date().toLocaleTimeString()
}, 1000);

const state = reactive({
    city: '',
    today: {},
    show: false,
    tomorrow: {},
    aftertomorrow: {}
})


AMap.plugin('AMap.CitySearch', function () {
    var citySearch = new AMap.CitySearch()
    citySearch.getLocalCity(function (status, result) {
        if (status === 'complete' && result.info === 'OK') {
            // 查询成功,result即为当前所在城市信息
            console.log(result);
            state.city = result.city
            getWeather(result.city)
        }
    })
})

const getWeather = (city) => {
    //加载天气查询插件
    AMap.plugin("AMap.Weather", function () {
        //创建天气查询实例
        var weather = new AMap.Weather();
        //执行实时天气信息查询
        weather.getLive(city, function (err, data) {
            console.log(err, data);
            state.today = data
        });
    });

    //加载天气查询插件
    AMap.plugin("AMap.Weather", function () {
        //创建天气查询实例
        var weather = new AMap.Weather();
        //执行实时天气信息查询
        weather.getForecast(city, function (err, data) {
            console.log(err, data);
            state.tomorrow = data.forecasts[1]
            state.aftertomorrow = data.forecasts[2]

            nextTick(() => { // 保证内部的逻辑会在页面渲染完毕后执行
                initEcharts(data.forecasts.map(item => item.dayTemp))
            })
        });
    });
}

const selectCity = ({ selectedOptions }) => {
    console.log(selectedOptions[1].text)
    state.city = selectedOptions[1].text
    getWeather(state.city)
    state.show = false
}

const echartsWrap = ref(null)
const initEcharts = (arr) => {
    // console.log(echartsWrap.value);
    let myChart = echarts.init(echartsWrap.value)
    myChart.setOption({
        tooltip: {},
        xAxis: {
            type: 'category',
            data: ['今天', '明天', '后天', '大后天']
        },
        yAxis: {},
        series: [
            {
                name: '温度',
                type: 'line',
                data: arr
            }
        ]
    });
}
// onMounted(()=>{
//     initEcharts(data)
// })
</script>

<style lang="css" scoped>   // 样式设置
.container {
    min-height: 100vh;
    background-color: #000;
    opacity: 0.7;
    color: #fff;
}

.nav {
    display: flex;
    justify-content: space-between;
    padding: 20px;
}

.city-info {
    text-align: center;
}

p {
    margin-top: 10px;
}

.temp {
    font-size: 30px;
    margin: 10px 0;
}

.temp em {
    font-size: 34px;
}

.future {
    margin-top: 30px;
    padding: 0 10px;
}

.group {
    height: 40px;
    line-height: 44px;
    background-color: rgba(255, 255, 255, 0.5);
    font-size: 13px;
    padding: 0 10px;
    margin-bottom: 10px;
    border-radius: 5px;
}

.echarts-wrap {
    width: 100%;
    height: 50vh;
}
</style>

实现效果如下:

chrome-capture-2024-7-7.gif

因为这个页面使用了高德地图的定位服务,需要导入相应的插件,所以需要获取安全密钥APIKey并存储到HTML中。HTML代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
    <script type="text/javascript"> 
    window._AMapSecurityConfig = { 
    securityJsCode: "「你申请的安全密钥」", }; 
    </script> 
    <script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=你申请的key值" >
    </script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

本文引入的Echarts库,Vant库以及所需的高德地图插件使用方法等可在以下链接中查看相关文档查找。