Vue3 —打造你的专属天气预报员

1,415 阅读10分钟

随着天气变得越来越不可预测,我们经常需要查看天气预报来规划我们的日常活动。那么,为什么不自己打造一个专属的天气预报员呢?今天,我们将使用Vue框架来创建一个简单但实用的天气预报应用。

1. 初始化Vue项目

首先,我们需要创建一个新的Vue项目。你可以使用Vue CLI或者Vite来初始化项目。在这个例子中,我们将使用Vue CLI。 打开终端,运行以下命令来创建一个新的Vue项目:

vue create weather-app

按照提示选择Vue版本和所需的配置选项。

2. 安装依赖

进入项目目录,安装所需的依赖项:

cd weather-app
npm install vue-router vant echarts @vant/area-data

这些依赖项包括:

  • vue-router:Vue的路由管理器。
  • vant:有赞团队的一个轻量、可靠的移动端Vue组件库。
  • echarts:一个使用JavaScript实现的可视化库。
  • @vant/area-data:Vant UI库提供的地区数据。

3. 配置路由

src目录下创建一个名为router的文件夹,并在其中创建一个名为index.js的文件。这将是我们存放路由配置的地方。

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: () => import('../views/HomeView.vue')
    }
  ]
})
export default router

src目录下创建一个名为views的文件夹,并在其中创建一个名为Home.vue的文件。这将是我们应用的首页。

<template>
  <div class="home">
    <h1>欢迎使用天气预报应用</h1>
  </div>
</template>
<script>
export default {
  name: 'HomeView'
}
</script>

4. 配置Vant UI库

main.js中,我们需要引入并使用Vant UI库。Vant UI 是一个基于 Vue.js 的高质量、轻量级的移动端 UI 组件库,由有赞前端团队开发并开源。Vant 的目标是为开发者提供一套开箱即用的组件集合,以加速移动应用的开发流程。它覆盖了一系列常见的移动端界面组件,比如导航、表单元素、按钮、列表、轮播图等,同时支持按需引入,有助于减少项目的体积。这是它的网址Vant UI

// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { Area, ActionSheet } from 'vant';
import 'vant/lib/index.css';
const app = createApp(App)
app.use(router)
app.use(Area);
app.use(ActionSheet)
app.mount('#app')

5. 创建天气预报页面

views文件夹中创建一个名为WeatherView.vue的文件,这是我们的天气预报页面。

<!-- src/views/WeatherView.vue -->
<template>
  <div class="weather">
    <!-- ... -->
  </div>
</template>
<script setup>
// ...
</script>
<style scoped>
/* ... */
</style>

6. 添加时间和城市切换功能

WeatherView.vue的模板部分,添加时间和城市切换的界面。

<template>
  <div class="weather">
    <div class="nav">
         <div class="time">{{ now }}</div>
         <div class="city" @click="state.show = true">切换城市</div>
     </div>
    <!-- ... -->
  </div>
</template>

在脚本部分,添加时间和一些动态属性。

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

let now = ref("00:00:00");
setInterval(() => {
    now.value = new Date().toLocaleTimeString()
}, 1000)
const state = reactive({
    city: "",
    today: {},
    show: false,
    // ...
})
// ...
</script>
  1. 实时时间更新: 使用ref创建了一个名为now的响应式引用,初始化为"00:00:00"。 通过setInterval函数每秒调用一次匿名函数,该函数更新now引用的值为当前的本地时间字符串。这样,now的值会每秒自动更新,反映实时时间。

  2. 状态管理: 使用reactive创建了一个名为state的对象,该对象包含了多个响应式属性:

  • city:用于存储当前选择的城市名。
  • today:用于存储当前城市的天气信息。
  • show:布尔值,用于控制城市选择器的显示与否。

7. 添加地区选择器

使用Vant的Area组件来创建一个地区选择器。在使用之前,我们先要阅读一下快速上手,学会使用它。我已经导入好了包,在这里我就不带着大家去了解了,有需要的可以去看看官方文档

image.png

然后在左侧往下拉,找到业务组件,就可以看到area组件了 image.png

首先我们先要在全局(main.js)注册好组件

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import "./assets/reset.css"
import { Area } from 'vant';
import { ActionSheet } from 'vant';

const app = createApp(App)

app.use(router)
app.use(Area);
app.use(ActionSheet)

app.mount('#app')

再来到WeatherView.vue

<template>
  <div class="weather">
     <div class="nav">
         <div class="time">{{ now }}</div>
         <div class="city" @click="state.show = true">切换城市</div>
     </div>
    <!-- ... -->
    <van-action-sheet v-model:show="state.show" title="地区">
            <div class="content"><van-area v-model="value" :area-list="areaList" :columns-num="2" @confirm="selectCity"
                    @cancel="state.show = false" />
            </div>
       </van-action-sheet>
  </div>
</template>

当 state里的show为true van-action-sheet 组件显示 为false 就消失

在脚本部分,添加地区选择器的逻辑。

<script setup>
import { ref } from 'vue';
import { Area } from 'vant';
import { areaList } from '@vant/area-data';
// ...
const selectCity = ({ selectedOptions }) => {
    // console.log(selectedOptions[1].text);
    state.city = selectedOptions[1].text
    getWeather(selectedOptions[1].text)
    state.show = false
}
// ...
</script>

代码解释:这是定义在area组件中的确定按钮,目的是点击确定后这个组件消失,并且把选择的城市传给state里的city,实现响应式更新

8. 获取天气数据

使用高德地图的API来获取实时天气和未来天气预报数据。这里也是利用了高德地图的定位以及天气的查询来实现的,可以到高德地图的官网高德开放平台注册账号去申请一个Api,选择,下图的js api image.png 然后在左侧的准备中它会教你如何获取apiKey,看完准备后记得看一下快速上手,它会教你如何把高德地图的api导入你的项目之中 image.png

我们来到进阶教程,找到定位和天气

image.png

首先我们先获取到当前定位,对于一个天气app来说,肯定需要先获取当前定位的信息。 我们选择ip定位,把下面的代码复制下来

image.png

然后粘贴到组件中,然后把查询到的城市赋值给自己声明的变量中

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

因为获取天气是根据当前城市信息来获取的,而且在各种地方都需要使用到,所有我直接把它包装成了一个方法来复用

在获取天气的方法里,我用到了高德地图里天气的api

image.png

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

    });
}

到这里我们就已经获取到天气信息了

9. 显示天气信息

在模板部分,添加显示天气信息的界面。

<template>
  <div class="weather">
    <!-- ... -->
    <div class="weather-info">
      <p class="city">{{ weatherData.city }}</p>
      <p class="temp">{{ weatherData.temp }}</p>
      <p class="weather">{{ weatherData.weather }}</p>
    </div>
  </div>
</template>

在脚本部分,添加显示天气信息的逻辑。

<script setup>
import { ref } from 'vue';
import { Area } from 'vant';
import { areaList } from '@vant/area-data';
const weatherData = ref({
  city: '',
  temp: '',
  weather: '',
});
const onCitySelected = (values) => {
  const city = values[1].name;
  // 调用获取天气的函数
  getWeather(city);
  showCitySelector.value = false;
};
// 使用高德地图的API来获取实时天气和未来天气预报数据
const getWeather = async (city) => {
  // ...
  // 更新weatherData
  weatherData.value = {
    city: data.city,
    temp: data.temp,
    weather: data.weather,
  };
};
// ...
</script>

目前的天气情况我们就已经大致做完了,最后把天气预报加上就可以了

一样的,高德地图为我们提供了未来七天的天气预报API,我们只需要抄作业就行了

image.png

因为它的api跟获取当前天气信息是差不多的,所有我们也把他放在封装好的getWeather函数里,我在状态栏里定义了一个future数组,这里我只截取展现了未来两天的天气情况,


const state = reactive({
    city: "",
    today: {},
    show: false,
    future: [],
    data: []
})

const getWeather = (city) => {
    //加载天气查询插件
    AMap.plugin("AMap.Weather", function () {
        //创建天气查询实例
        var weather = new AMap.Weather();
        //执行实时天气信息查询
        weather.getLive(city, function (err, data) {
            console.log(err, data);
            //err 正确时返回 null
            //data 返回实时天气数据,返回数据见下表
            state.today = data
        });
        weather.getForecast(city, function (err, data) {
            console.log(err, data);
            //err 正确时返回 null
            //data 返回天气预报数据,返回数据见下表
            state.future = data.forecasts.slice(1, 3)
            // console.log(state.future[0]);
            state.data = data.forecasts
            // initEcharts()
            nextTick(() => {
                initEcharts(data.forecasts.map(item => item.dayTemp))
            })
        });

    });
}

渲染到页面中

<div class="future" v-if="state.future.length">
            <div class="group">
                明天:
                <span class="tm">白天:{{ state.future[0].dayTemp }}℃ {{ state.future[0].dayWeather }}
                    {{ state.future[0].dayWindPower }}级</span>
                <span class="tm">夜晚:{{ state.future[0].nightTemp }}℃ {{ state.future[0].nightWeather }}
                    {{ state.future[0].nightWindPower }}级</span>
            </div>
            <div class="group">
                后天:
                <span class="tm">白天:{{ state.future[1].dayTemp }}℃ {{ state.future[1].dayWeather }}
                    {{ state.future[1].dayWindPower }}级</span>
                <span class="tm">夜晚:{{ state.future[1].nightTemp }}℃ {{ state.future[1].nightWeather }}
                    {{ state.future[1].nightWindPower }}级</span>
            </div>
        </div>

下面是完整的js代码

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

let now = ref("00:00:00");
setInterval(() => {
    now.value = new Date().toLocaleTimeString()
}, 1000)
const state = reactive({
    city: "",
    today: {},
    show: false,
    future: [],
    data: []
})

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

AMap.plugin('AMap.CitySearch', function () {
    var citySearch = new AMap.CitySearch()
    citySearch.getLocalCity(function (status, result) {
        if (status === 'complete' && result.info === 'OK') {
            // 查询成功,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);
            //err 正确时返回 null
            //data 返回实时天气数据,返回数据见下表
            state.today = data
        });
        weather.getForecast(city, function (err, data) {
            console.log(err, data);
            //err 正确时返回 null
            //data 返回天气预报数据,返回数据见下表
            state.future = data.forecasts.slice(1, 3)
            // console.log(state.future[0]);
            state.data = data.forecasts
            // initEcharts()
            nextTick(() => {
                initEcharts(data.forecasts.map(item => item.dayTemp))
            })
        });

    });
}

10. 样式美化

最后在样式部分,添加CSS代码来美化界面。

.container {
    min-height: 100vh;
    background-color: skyblue;
    background: url("https://img1.baidu.com/it/u=771077137,4076319259&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889");
    background-size: cover;
    opacity: 0.7;
    color: white;
}

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

.city-info {
    text-align: center;
    font-size: 18px;
}

p {
    margin-top: 10px;
}

.weather {
    font-size: 40px;
}

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

.font {
    display: block;
    font-size: 18px;
    margin-top: 35px;
}

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

.detail span {
    margin: 0 7px;
    font-size: 15px;
}

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

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

.echarts-wrap {
    width: 100%;
    height: 50vh;
    /* colot: white; */
}

通过以上步骤,我们已经创建了一个简单但实用的天气预报应用。用户可以通过选择城市来查看该地区的实时天气和未来天气预报。

image.png

总结

通过这次实战项目,我深刻体会到了将理论知识应用于实际开发的重要性。从项目初始化到最终成品的每一个环节,我都经历了从概念理解到动手实践的过程,这不仅巩固了我的编程基础,也大大提升了我的问题解决能力和创新思维。

具体而言,我学会了如何使用Vue CLI快速搭建项目骨架,理解了Vue.js的响应式原理及其在实际项目中的运用,掌握了组件化开发的思想,以及如何合理组织项目结构。在集成第三方库方面,如Vant UI和ECharts,我学会了按需引入和使用组件,这让我认识到在实际开发中选择合适工具的重要性。

此外,通过接入高德地图API获取天气数据,我熟悉了API的调用流程,包括请求参数的设置、数据解析和错误处理,这增强了我的网络通信和数据处理能力。在实现城市选择器和天气信息展示的过程中,我进一步提高了对Vue响应式系统和DOM操作的理解。

最重要的是,这个实战项目锻炼了我的自学能力和独立解决问题的能力,我学会了如何查阅官方文档,如何利用社区资源,以及如何调试和优化代码。这对我个人的技术成长有着不可估量的价值。

总之,这次实战经历不仅是一次技术上的挑战,更是一次自我提升的旅程,让我在实践中学习,在学习中成长。我相信,这段经历将为我未来的职业发展奠定坚实的基础。