如何使用Mapbox API和Vue建立一个IP地址跟踪器

235 阅读4分钟

如何使用Mapbox API和Vue建立一个IP地址跟踪器

一个IP(互联网协议)地址是一组数字,代表着网站或设备的位置。这组数字的范围从0到255,即从0.0.0.0到255.255.255。它们有两种类型--IPv4和IPv6。

IPv4是旧的,仅限于32位地址,而IPv6是新标准,允许128位地址。创建IPv6是因为所有可能的IPv4地址接近耗尽,因为许多新设备被分配到这些地址,所以IPv6是新标准。

用户的IP地址可用于各种动态目的,其中之一是IP地理定位

IP地理定位是通过使用一个IP地址来识别设备的地理位置。这涉及到根据分配给设备的IP地址来追踪设备。IP地址包含指向设备位置的信息,如纬度、经度、国家、地区(城市)、时区等。

各种类型的IP地理定位API通过不同的服务提供,使跟踪过程更容易。通过向API发出GET请求,我们可以得到设备的地理信息结果,如。

  • IP地址
  • 国家详细信息 - 大陆、首都、城市、地区、国旗。
  • 互联网服务提供商(ISP)
  • 时区细节。
  • 货币详细信息 - 代码、符号和汇率。
  • 纬度和经度值。

在本指南中,我们将使用Vue.js框架、IP地理定位API和Mapbox API,从头开始构建一个IP地址追踪器。我们将介绍构建前端、处理用户的IP地址以及在地图上可视化位置的过程。在本教程结束时,读者将拥有一个功能完善、可随时部署的网络应用。

需要注意的是,每个API服务返回的结果是不同的。在本教程中,我们将使用IPWHOISAPI服务。

先决条件

要跟上本教程,需要以下条件。

  1. 在你的机器上安装Node。
  2. 具有HTML、CSS、JavaScript的基本知识。
  3. Vue.js的基本知识。我们将使用Vue CLI版本。
  4. 对Tailwind CSS的了解。

IP地址追踪器的功能

对于我们的应用程序,我们将为其添加以下功能。

  1. 一个基于输入的IP地址的搜索功能。
  2. 一个显示地理位置细节的仪表板。
  3. 一个可视化的地图显示。

用Vue.js搭建前端的脚手架

为了启动这个过程,我们将从建立我们的项目开始。

使用Vue CLI创建一个新的Vue应用程序。

vue create ip-tracker

安装所需的软件包

Axios- 用于发出HTTP请求。

npm install axios 

Tailwind CSS- 为了轻松实现样式设计,我们将在项目中使用Tailwind CSS。使用这个库可以以更快、更有效的方式构建UI组件。

修改样式和创建Vue文件

在这一节中,我们将看看我们将不得不对文件进行的修改,以适应我们的项目。

修改样式

注意:我不会去解释风格,因为这已经超出了本文的范围。正如我之前所说,你可以创建你的风格。它不一定要和这个相似。

在完成Tailwind设置后,我们将对tailwind.config.jsapp.css文件进行一些配置。

i.tailwind.config.js

打开它,将代码替换成这样。

// tailwind.config.js
const colors = require("tailwindcss/colors");

module.exports = {
  purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
  darkMode: false, 
  theme: {
    colors: {
      primary: '#0066FF',
   
      black: colors.black,
      white: colors.white,
      gray: colors.coolGray,
      red: colors.red,
      yellow: colors.amber,
      green: colors.emerald,
      blue: colors.blue,
      indigo: colors.indigo,
      purple: colors.violet,
      pink: colors.pink,
    },
    extend: {},
    screens: {
      '2xl': {'max': '1535px'},
      
      'xl': {'max': '1279px'},
      
      'lg': {'max': '1023px'},
     
      'md': {'max': '767px'},

      'sm': {'max': '500px'},
    },
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

ii.app.css

把代码替换成这样。

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {

    .textStyle{
        @apply my-2 font-black text-xl sm:text-base;
    }

    .boardStyle{
        @apply  rounded-xl text-white text-center text-base p-2 uppercase sm:my-4 text-sm;
    }
} 

创建和修改Vue文件

创建一个应用程序的简单而有效的方法是将每个部分划分为可重用的组件。我们的应用程序将被分为三个组件。

  1. 搜索栏
  2. 仪表板
  3. 地图显示

i.App.vue

我们首先在其中写下以下代码。

<template>
  <div id='app' class='font-serif m-0 p-0'>
   <div class="bg-primary h-1/2 py-8 px-8 ">
      <header>
        <h1 class='text-center my-4 text-white font-black text-2xl sm:text-xl '> 
           TRACK-U 
        </h1>
        <p class='text-center my-4 text-white font-black text-xl sm:text-base'> Highly Optimized IP Address Tracker </p>
      </header>
      <!-- The separate components -->
      <search-bar @trackIp="trackIpCall"/>
      <dashboard  :ipAddress="ipAddress" :isp="isp" :country="country" :countryFlag="countryFlag" :city="city" :currency="currency" :timezone="timezone" />
   </div>
    <map-display  :longitude="longitude" :latitude="latitude"/>
  </div>
</template>

我们刚才写的代码是用于布局设置,组件将在其中显示。

接下来,我们添加一个导入组件的脚本。

<script> 
import axios from "axios";
import MapDisplay from './components/MapDisplay.vue'
import Dashboard from './components/Dashboard.vue'
import SearchBar from './components/SearchBar.vue'
export default {
  name: 'App',
  components: {
    MapDisplay,
    Dashboard,
    SearchBar
  },
}
</script>

在这个文件中还有更多的代码需要添加,我们将在后面看。现在,让我们继续创建三个组件的布局。

组件文件夹中创建三个文件,分别是SearchBar.vueDashboard.vueMapDisplay.vue

ii.SearchBar.vue

这个组件将处理用户输入的IP地址捕获。我们可以对这个组件进行整体编码,因为它不与任何API直接交互。它有一个名为takeIP() 的方法,它只是在点击按钮或按下ENTER键后 "接收 "在搜索框中输入的IP地址。然后,该IP地址被存储在一个名为searchInput 的属性中,该属性将在以后的API集成中使用。以下是代码。

<template>
      <form class="search-form m-6 flex justify-center items-center">
          <input class="search-input rounded-l-2xl w-3/4 p-4 border-0 sm:p-2" type="text 
          placeholder="Search using any IP address" v-model="searchInput" @keyup.enter="takeIp" 
           />
      <button class="p-4 rounded-r-2xl bg-black sm:p-2" type="submit" 
             @click.prevent="takeIp"> <i class="fas fa-search text-white text-xl"></i></button>
    </form>
</template>
<script>
import '@fortawesome/fontawesome-free/css/all.css'
import '@fortawesome/fontawesome-free/js/all.js'
export default {
  name: 'SearchBar',
  data() {
    return {
      searchInput: "",
    };
  },
  methods: {
    //this function is used for getting the typed in IP address
    takeIp() {
      this.$emit("trackIp", this.searchInput);
    },
  },
}
</script>

iii.仪表板.vue

我们将用它来显示从IP地理定位API的响应中获得的地理信息。该仪表板显示国家、IP地址、城市、国旗、货币、ISP和时区。

<template>
    <div class=" dashboard container mx-auto px-4 grid grid-cols-3 gap-x-16 gap-y-4 sm:block">
      <!-- div to show the IP address -->
        <div class='boardStyle'> 
            IP ADDRESS
            <p class='textStyle'> {{ipAddress}} </p>
        </div>

      <!-- div to show the Country -->
        <div class='boardStyle'>
            Country
            <p class='textStyle'> 
                {{country}}
                <img class='w-fit m-auto w-8' :src=countryFlag alt='Country Flag'/>
            </p>
        </div>
      <!-- div to show the City -->
        <div class='boardStyle'>
            City
            <p class='textStyle'> {{city}}</p>
        </div>
      <!-- div to show the ISP -->
        <div class='boardStyle'>
            isp
            <p class='textStyle'> {{isp}}</p>
        </div>
      <!-- div to show the Currency -->
        <div class='boardStyle'>
            currency
            <p class='textStyle'> {{currency}}</p>
        </div>
      <!-- div to show the timezone -->
        <div class='boardStyle'>
            timezone
            <p class='textStyle'> {{timezone}}</p>
        </div>
    </div>
</template>

iv.MapDisplay.vue

最后,为了处理每个请求的地图显示,我们将使用这个组件。向它添加以下内容。

<template>
<!-- The container to display the map -->
     <div id="map" class='w-full relative h-96 sm:h-60'></div>
</template>

整合API

我们将在Vue文件中做一些编辑,所以先不要开始修改文件。每个Vue文件的完整代码将在最后提供。让我们先看看这些函数以及它们在使用API的跟踪功能中所扮演的角色。

App.vue 文件中,我们将创建一个名为getInfo() 的函数来获取从API请求中获得的信息。在这个函数中,我们首先通过API端点初始化请求,获得响应,然后将从响应中返回的值存储在适当的属性中。注意searchInput 属性是如何发挥重要作用的--向IPWHOIS API端点提供IP地址。

  getInfo(){
    // URL request to the API
      const endpoint = `https://ipwhois.app/json/${this.searchInput}`
      //getting the response using Axios
      axios.get(endpoint)
       .then((data) => data.data)
       .then((result) => {
        // Storing the values retrieved
            this.ipAddress = result.ip
            this.isp = result.isp
            this.longitude = result.longitude
            this.latitude = result.latitude
            this.country = result.country
            this.countryFlag = result.country_flag
            this.city = result.city
            this.currency = result.currency
            this.timezone = result.timezone_gmt
            
          })
         
          .catch(error => {
            console.log(error.message)
          })
    },

接下来,我们将创建另一个名为trackIpCall() 的函数,以获得从SearchBar组件中检索到的值,然后调用我们刚才讨论的getInfo() 函数。

     trackIpCall(payload) {
      this.searchInput = payload;
      this.getInfo();
    },

props 对象的帮助下,数据将从父级组件向下传递到Dashboard.vue中。这个对象将被创建在Dashboard组件中。

 props: { 
    //the IP address fed in
      ipAddress:{
          type:String,
          required:true,
          default:''
      },
    //the ISP the user is using
     isp:{
          type:String,
          required:true,
          default:''
     },
     //the country where the device/use is currently located
   country:{
       type:String,
       required:true,
       default:''
   },
   //the image URL of the country flag
    countryFlag:{
         type:String,
         required:true,
          default:''
    },
    //the city where the device/use is currently located
    city:{
         type:String,
         required:true,
          default:''   
    },
    ////the currency of the country where the device/use is currently located
     currency:{
           type:String,
         required:true,
          default:''   
     },
     //the timezone where the device/use is currently located
    timezone:{
        type:String,
        required:true,
          default:''  
    }
}

用Mapbox API创建一个交互式地图显示

在这里,我们将在MapDisplay组件中使用Mapbox API。

该API提供了一个名为Mapbox GL JS的库,用于在Web应用程序中显示地图。它还增加了额外的功能来定制地图体验。mapboxgl.Map是一个类,作为创建地图的基础框架。

要在我们的应用程序中显示地图,需要以下属性。

  1. accessToken。你需要一个访问令牌来使用Mapbox的API。正如我所做的那样,这可以作为一个环境变量存储在你的**.env**文件中。当你在MapBox API平台上注册后,可以立即访问该访问令牌。
    mapboxgl.accessToken =  process.env.VUE_APP_MAP_TOKEN
  1. 容器:这是地图将被呈现的HTML元素。

  2. 样式:用于确定正在使用的瓷砖组的样式的URL。

  3. center(中心):使用经度和纬度来设置地图中心的坐标。

  4. zoom(缩放):在一个特定的框架内可以查看地图的缩放级别。它可以是一个整数,也可以是小数。

我们将把后四个属性传递给一个对象,然后稍后使用Mapbox GL JS的addControl(new mapboxgl.NavigationControl()) 功能向该对象添加缩放和旋转控制。

      const map = new mapboxgl.Map({
      container: 'mapContainer', // container ID
      style: "mapbox://styles/mapbox/streets-v12",
      center: [this.longitude, this.latitude],
      zoom: 10
      });

最后,我们将添加一个标记,以直观地指出设备的位置。该标记将使用setLngLat() 方法放置在地图上的准确坐标处。

    //  Create a default marker colored black.
    const marker = new mapboxgl.Marker({ color: 'black' })
    .setLngLat([this.longitude, this.latitude])
    marker.addTo(map);

为了实现这些,我们使用一个叫做displayMap() 的函数。


    displayMap(){
      // import the required JavaScript file for displaying the map
      let mapboxgl = require("mapbox-gl/dist/mapbox-gl.js");
      //access token
      mapboxgl.accessToken =  process.env.VUE_APP_MAP_TOKEN
      // the map object
      const map = new mapboxgl.Map({
      container: 'mapContainer', // container ID
      style: "mapbox://styles/mapbox/streets-v12",
      center: [this.longitude, this.latitude],
      zoom: 10
      });
    //adding zoom and rotation controls
     map.addControl(new mapboxgl.NavigationControl());

     
    //  Create a default marker colored black.
    const marker = new mapboxgl.Marker({ color: 'black' })
    .setLngLat([this.longitude, this.latitude])
    marker.addTo(map);
  },

这个函数将在mounted() 生命周期方法中被调用。它根据displayMap() 方法中传递的属性来初始化地图。

  mounted() { // lifecycle method to load the map 
      this.displayMap();
  }

然后我们创建一个watch 属性来检测属性值的变化,然后进行相应的显示。

   // watch props for any change in value and the calls map function
  watch: {
    longitude() {
      this.displayMap();
    },
    latitude() {
      this.displayMap();
    },
}

完整的代码

下面是每个文件的最终编译代码。

i.App.vue

<template>
  <div id='app' class='font-serif m-0 p-0'>
   <div class="bg-primary h-1/2 py-8 px-8 ">
      <header>
        <h1 class='text-center my-4 text-white font-black text-2xl sm:text-xl '> 
           TRACK-U 
        </h1>
        <p class='text-center my-4 text-white font-black text-xl sm:text-base'> Highly Optimized IP Address Tracker </p>
      </header>
      <!-- The separate components -->
      <search-bar @trackIp="trackIpCall"/>
      <dashboard  :ipAddress="ipAddress" :isp="isp" :country="country" :countryFlag="countryFlag" :city="city" :currency="currency" :timezone="timezone" />
   </div>
    <map-display  :longitude="longitude" :latitude="latitude"/>
  </div>
</template>

<script> 
import axios from "axios";
import MapDisplay from './components/MapDisplay.vue'
import Dashboard from './components/Dashboard.vue'
import SearchBar from './components/SearchBar.vue'

export default {
  name: 'App',
  components: {
    MapDisplay,
    Dashboard,
    SearchBar
  },

   data() {
    return {
      searchInput: "",
      ipAddress: "",
      isp: "",
      longitude:0,
      latitude:0,
      country:"",
      countryFlag:"",
      city:"",
      currency:"",
      timezone:"",
      result:{}
    };
  },

  mounted: function () {
    this.getInfo();
  },

  methods:{
    // Function to get the value searched for
     trackIpCall(payload) {
      this.searchInput = payload;
      this.getInfo();
    },

  getInfo(){
    // The Api request
      const endpoint = `https://ipwhois.app/json/${this.searchInput}`
      axios.get(endpoint)
       .then((data) => data.data)
       .then((result) => {
        // Storing the values obtained
            this.ipAddress = result.ip
            this.isp = result.isp
            this.longitude = result.longitude
            this.latitude = result.latitude
            this.country = result.country
            this.countryFlag = result.country_flag
            this.city = result.city
            this.currency = result.currency
            this.timezone = result.timezone_gmt
            
          })
         
          .catch(error => {
            console.log(error.message)
          })
    },
 
  }
}
</script>

ii.Dashboard.vue

<template>
    <div class=" dashboard container mx-auto px-4 grid grid-cols-3 gap-x-16 gap-y-4 sm:block">
        <div class='boardStyle'> 
            IP ADDRESS
            <p class='textStyle'> {{ipAddress}} </p>
        </div>
        <div class='boardStyle'>
            Country
            <p class='textStyle'> 
                {{country}}
                <img class='w-fit m-auto w-8' :src=countryFlag alt='Country Flag'/>
            </p>
        </div>
        <div class='boardStyle'>
            city
            <p class='textStyle'> {{city}}</p>
        </div>
        <div class='boardStyle'>
            isp
            <p class='textStyle'> {{isp}}</p>
        </div>
        <div class='boardStyle'>
            currency
            <p class='textStyle'> {{currency}}</p>
        </div>
        <div class='boardStyle'>
            timezone
            <p class='textStyle'> {{timezone}}</p>
        </div>
    </div>
</template>


<script>
export default {
  name: 'Dashboard',
  // Passing the values as props.
  props: { 
      ipAddress:{
          type:String,
          required:true,
          default:''
      },
     isp:{
          type:String,
          required:true,
          default:''
     },
   country:{
       type:String,
       required:true,
       default:''
   },
    countryFlag:{
         type:String,
         required:true,
          default:''
    },
    city:{
         type:String,
         required:true,
          default:''   
    },
     currency:{
           type:String,
         required:true,
          default:''   
     },
    timezone:{
        type:String,
        required:true,
          default:''  
    }
}
}
</script>

iii.MapDisplay.vue

<template>
<!-- The container to display the map -->
     <div id="mapContainer" class='w-full relative h-96 sm:h-60'></div>
</template>

<script>
export default {
  name: 'MapDisplay',
   props:{
    longitude:{
      type: Number,
      required: true,
      default:0
    },
    latitude:{
      type: Number,
      required: true,
      default:0
    },
   },
  mounted() { // lifecycle method to load the map 
    this.displayMap();
  },
   // watch props for any change in value and the calls map function
  watch: {
    longitude() {
      this.displayMap();
    },
    latitude() {
      this.displayMap();
    },
  },

  methods: {
      displayMap(){
        // import the required JavaScript file for displaying the map
        let mapboxgl = require("mapbox-gl/dist/mapbox-gl.js");
        //access token
        mapboxgl.accessToken =  process.env.VUE_APP_MAP_TOKEN
        // the map object
        const map = new mapboxgl.Map({
        container: 'mapContainer', // container ID
        style: "mapbox://styles/mapbox/streets-v12",
        center: [this.longitude, this.latitude],
        zoom: 10
        });
      //adding zoom and rotation controls
      map.addControl(new mapboxgl.NavigationControl());

      
      //  Create a default marker colored black.
      const marker = new mapboxgl.Marker({ color: 'black' })
      .setLngLat([this.longitude, this.latitude])
      marker.addTo(map);
    },
  }
}
</script>

iv.搜索栏.vue

<template>
      <form class="search-form m-6 flex justify-center items-center">
      <input
        class="search-input rounded-l-2xl w-3/4 p-4 border-0 sm:p-2"
        type="text"
        placeholder="Search using any IP address"
        v-model="searchInput" @keyup.enter="takeIp"
      />
      <button class="p-4 rounded-r-2xl bg-black sm:p-2" type="submit" @click.prevent="takeIp"> <i class="fas fa-search text-white text-xl"></i></button>
    </form>
</template>

<script>
import '@fortawesome/fontawesome-free/css/all.css'
import '@fortawesome/fontawesome-free/js/all.js'
export default {
  name: 'SearchBar',
  data() {
    return {
      searchInput: "",
    };
  },
  methods: {
    //this function is used for getting the typed in IP address
    takeIp() {
      this.$emit("trackIp", this.searchInput);
    },
  },
}
</script>

总结

在本教程中,我们已经能够开发应用程序的用户界面,整合IPWHOIS API,并最终使用Mapbox API在地图上显示地理信息。此外,有了IP地理定位的概念,就可以根据用户的地理位置建立适合其喜好的网站,从而使其对所有人都能访问。