发布于 2026-01-05 12 阅读
0

基于 Vue.js 的简易天气应用 Vue 天气应用

基于 Vue.js 的简易天气应用

Vue 天气应用

大家好!
我最近在学习 Vue。所以我认为,深入理解 Vue 的最佳方法就是写篇文章来分享:]
另外,在开发应用的过程中,我发现了一些缺失的信息,我接下来会谈到这些内容。

我将概述一下我的个人项目 Vue 应用程序,该应用程序使用Wea​​therbit的公共 API ,并根据城市获取天气信息:温度和描述。

该应用有两个视图:
首页和关于。本文不会详细描述每一行代码,而是重点介绍应用的架构和流程。
首页

关于页面

您可能需要具备 HTML、CSS 和 JS 的基础知识,以及 Vue.js 的概念,才能完全理解这里发生的事情。

我使用过的技术:

  • Vuex 用于数据存储。
  • 使用 Axios 从 API 获取数据。
  • 用于在应用程序中导航的路由。实际上,在这个项目中并非必须使用它,但我只是想演示一下它的实际应用。

该项目由Vue CLI生成和管理。
我强烈推荐您使用这个工具。它非常适合初学者。

让我们一起来看一下项目的架构。

文件夹内容如下src

src
├───assets # Stuff like images are stored here
│       logo.png
├── components # Independent "bricks" of app
│       weatherWidget.vue
├── services # Code that works with API. Axios gets data from API here
│       service.js
├── views # Views are like pages. Router navigates through them
│       About.vue
│       Home.vue
├── App.vue # Root Vue component
├── constants.js # Just some constants that I want to store
├── main.js # Core JS file that imports and mounts our app
├── router.js # Router config is here
├── store.js # Store management with Vuex
Enter fullscreen mode Exit fullscreen mode

现在让我们更深入地研究代码!

main.js 🚩

main.js.开始,
main.js这是整个项目的根 JavaScript 文件。
在这里,我们导入核心库、配置和组件,然后创建new Vue实例并告诉 Vue 使用routerstore

import Vue from "vue"; // Vue lib
import App from "./App.vue"; // Our root component
import router from "./router"; // Router config
import store from "./store"; // Store config

import "normalize.css"; // Normalize.css lib to reset default styles

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app"); // Create Vue instance and mount it in index.html -> #app element
Enter fullscreen mode Exit fullscreen mode

constants.js 📄

这里存储着我的 API 密钥和 URL。REST
API 可以获取大量数据。请查阅文档深入了解。
在这个项目中,我将获取我所在的城市基辅的当前天气。因此,包含查询语句和密钥的 API 请求 URL 将类似于CURRENT_WEATHER

const API_KEY = "b60f3577e8eb46f089853e2a9fd7d744";
const CURRENT_WEATHER = `https://api.weatherbit.io/v2.0/current?city=Kiev,UA&key=${API_KEY}`;

export { API_KEY, CURRENT_WEATHER }; // export constant to be able to use them in components
Enter fullscreen mode Exit fullscreen mode

router.js 🔀

路由配置。该应用有两个视图(页面)——首页和关于。因此,我希望 URL 格式如下:首页https://app和关于https://app/about。我可以在 `<path>` 中定义它们。我只需要在`<path>`router.js中指定每个页面:写入路径,为路由命名,并将其链接到现有组件。注意,视图将采用延迟加载。这意味着路由 URL 中将不包含`<path>`。如果没有这一行,每个 URL 都将如下所示:首页和关于。但请不要忘记配置服务器,使其在历史记录模式下正常工作。routesRouter/about
mode: "history"#https://app/#route

import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue"; // import components that you wish to became Routes

Vue.use(Router); // tell Vue to action with Router

export default new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [ // All the routes are described here
    {
      path: "/",
      name: "home",
      component: Home
    },
    {
      path: "/about",
      name: "about",
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () =>
        import(/* webpackChunkName: "about" */ "./views/About.vue")
    }
  ]
});
Enter fullscreen mode Exit fullscreen mode

store.js 🗃️

Store管理部分
Store包含全局数据——应用程序的状态。应用程序的设置和定义
在此处完成。statemutationsactions

简化来说,操作算法Store是:
我们调用一个action动作,调用一个mutation改变mutation动作。state

注意: @路径import指的是从文件夹开始src,也就是我们工作区的根目录。

import Vue from "vue";
import Vuex from "vuex";

import service from "@/services/service.js"; // service.js fetch data from API. We will have a look at it in the next step.

Vue.use(Vuex); // tell Vue to action with Vuex

export default new Vuex.Store({
  state: { // define here data that you wish to store
    weather: {},
    dataIsRecived: false
  },
  mutations: { // change state from here
    UPDATE_WEATHER(state) {
      service
        .getWeather() // call the function from service.js that returns the data from API
        .then(response => { // if the response was get
          state.weather = response.data.data[0]; // set weather obj in state to real weather obj
          state.dataIsRecived = true; // mark that data was recived
          console.log(response); // and log it
        })
        .catch(error => { // if there was an error
          console.log("There was an error:", error.response); // log it
          state.dataIsRecived = false; // and mark that data wasn't recived
        });
    }
  },
  actions: { // call mutations that change the state here
    updateWeather(context) {
      context.commit("UPDATE_WEATHER");
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

services/service.js 🛎️

这里使用了API 通信。
Axios基于 Promise 的 HTTP 请求会发送到 Weatherbit,获取当前天气的真实数据。

import axios from "axios";
import { CURRENT_WEATHER } from "@/constants"; // URL with queries and API key

const apiClient = axios.create({ // create promise
  baseURL: CURRENT_WEATHER,
  withCredentials: false, // CORS
  headers: { // some HTTP headers
    Accept: "application/json",
    "Content-Type": "application/json"
  }
});

export default {
  getWeather() { // function that is used in store.js 👆
    return apiClient.get();
  }
};
Enter fullscreen mode Exit fullscreen mode

接下来是什么?

现在我们已经具备编写 Vue 组件并在其中使用所有这些功能的全部条件了。
那么,让我们开始吧!

App.vue

根 Vue 组件。
这里使用路由来添加导航栏,其中包含指向“首页”和“关于”视图的链接。

<template>
  <div id="app"> // root
    <div class="nav"> // navbar
      <router-link to="/" class="nav__link">Home</router-link>
      <router-link to="/about" class="nav__link">About</router-link>
    </div>
    <router-view /> // router views will be rendered here
  </div>
</template>

<style lang="scss"> // some styles 🖍️
  @import url('https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400&display=swap&subset=cyrillic');

  body {
    font-family: 'Montserrat', sans-serif;
    max-height: 100vh;
  }

  a {
    color: #153B50;
    text-decoration-color: rgba($color: #153B50, $alpha: 0.5);
    transition: all 0.3s ease;

    &:hover {
      text-decoration-color: #153B50;
    }
  }

  .nav {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 15px 0;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
  }

  .nav__link {
    &:not(:last-child) {
      margin-right: 15px;
    }
  }
</style>
Enter fullscreen mode Exit fullscreen mode

views/关于.vue

仅包含占位符的视图。

<template>
  <div class="about">
    <p>Thanks <a href="https://www.weatherbit.io/">Weatherbit</a> for public API!</p>
  </div>
</template>

<style lang="scss" scoped> // some styles 🖍️
  .about {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

views/Home.vue

视图中包含 weatherWidget vue 组件。
请在下一部分中查看。

<template>
  <div class="home">
    <weatherWidget />
  </div>
</template>

<script>
import weatherWidget from '@/components/weatherWidget.vue'; // import component

export default {
  name: "home",
  components: { // and register it
    weatherWidget
  }
}
</script>

<style lang="scss" scoped> // some styles 🖍️
  .home {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

components/weatherWidget.vue

所以,神奇之处就在这里。
我们已经准备好获取天气数据并将其渲染到组件中。
现在我们只需要dispatch存储数据action(这将调用 mutation,mutation 将调用服务并将从 API 获取的数据写入state)。
通过生命周期钩子,created我们调用 ` get_weather_data()` updateWeather action。这样我们就得到了computed property天气数据,从而保证了状态和组件之间的响应式。
最后一步:我们需要将计算属性值插值到组件中。
此外,还有一个预加载器。当dataIsRecived状态属性为 `weather_data( )` 时false(请查看 store.js),SVG 加载指示器会旋转并等待数据。

<template>
  <div>
    <div v-if="this.$store.state.dataIsRecived" class="weather-widget"> // widget itself
      <p class="weather-widget__city">{{ weather.city_name }}</p>
      <h2 class="weather-widget__temp">{{ weather.temp }}<span>°C</span></h2>
      <p class="weather-widget__status">{{ weather.weather.description }}</p>
    </div>
    <div v-else class="weather-widget"> // preloader
      <img src="spinner.svg" alt="">
    </div>
  </div>
</template>

<script>
  export default {
    computed: {
      weather() {
        return this.$store.state.weather // gets weather state from Vuex store
      }
    },
    created() {
      this.$store.dispatch("updateWeather"); // dispatch "updateWeather" when component is created
    }
  }
</script>

<style lang="scss" scoped> // some styles 🖍️
  .weather-widget {
    display: flex;
    flex-direction: column;
    align-items: center;
    color: #429EA6;
  }

  .weather-widget__city {
    font-size: 20px;
    margin: 0;
  }

  .weather-widget__temp {
    display: flex;
    align-items: flex-start;
    color: #16F4D0;
    font-size: 200px;
    font-weight: 200;
    margin: 0;

    span {
      font-size: 30px;
      font-weight: 400;
      margin-top: 35px;
    }
  }

  .weather-widget__status {
    font-size: 20px;
    margin: 0;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

就是这样!

我们的 Vue 单页应用运行正常!它从 Weatherbit 获取数据并将其渲染到屏幕上。就这么简单。

完整的源代码可以在我的GitHub仓库中找到。

GitHub 标志 oxyyyyy / vue-weather

使用 Vue 和公共天气 API 开发的天气应用程序

Vue 天气应用

一款宠物应用,附有相关文章。

Vue 天气应用

项目设置

yarn install

编译和热重载以进行开发

yarn run serve

编译并压缩以供生产环境使用

yarn run build

运行测试

yarn run test

整理和修复文件

yarn run lint

自定义配置

参阅配置参考






PS:非常期待大家的反馈,这对我很重要。这是我的第一篇文章,很高兴能为 DEV.to 社区贡献一份力量。希望​​这篇文章对大家有所帮助 😉

文章来源:https://dev.to/oxyyyyy/simple-weather-app-on-vue-js-1g20