写在前面:
心血来潮想用vue写个项目,也方便看看自己最近的代码学习效率和成果 ;
不写不知道,一写还确实踩了不少坑.....
文中需要重要注意的地方都用红色粗体标注了,代码部分的话就用注释就行了。写这个文章,以供以后再遇到相同问题,可以省不少事...也尽可能帮助到和我一样的vue小白们。
-----------------------------------------------2023/4/21----------------------------------------------------
简单写个页面,可以查询各地的天气那种;大概就是让用户输入城市,点击个按钮可以返回值;
先把数值写死,然后整个简单的样式;大概实现这个效果:
<template><input type="text" /><button>查询</button><h1>您查询的城市是:</h1><table><tr><th>日期</th><th>天气情况</th><th>温度</th></tr></table>
</template>
其实设计H5的总体页面就是这么简单;但是之后就需要优化优化了,
接下来,我们尝试给button添加一个点击事件。
<button @click="searchEvent">查询</button>
添加了@click,就肯定需要有触发的事件,
<script>
export default{methods: {searchEvent() {console.log("我被点击了")},},
}
</script>
大概就是这样;其实我做完这一步之后设计了一个优化。因为毕竟是vue,vue的优点是组件代码分离,可以实现多处调用,但是就因为我这个优化...导致我后来频繁bug:不是数据传不到子页面,就是数据实时传值的过程把变量各种修改…
这样,咱先把某个组件拆出来。这里我把<table></table>组件整个搬了出来,并且创建了子组件weather.vue用来存储后期查询到的某城市的天气数据。大概是...这样,
OK。麻烦的来了(对于萌新来说)。实时监听数据并传值...
vue中怎么get到用户输入的数据呢?我一开始使用的是v-model。对没错,v-model....
我直接监听了用户的实时传值,但事实证明,这东西还真不是自己想用就能用的。
=======================分割线============================
因为这篇文章是在项目敲完之后创作的,所以我这里先说云端数据获取的部分,其实上述说的v-model传值是完全可以使用的,而且能如期获得用户输入的字符;但是却不符合我们目前的项目要求。
我们为button的点击事件绑定触发事件:
searchEvent() {axios.get("http://apis.juhe.cn/simpleWeather/query?city=" +this.city +"&key=594cf24c8a7b4c4d56de72d5296b85df").then(res=>{this.res = res.data.result.future});},
其实不得不承认,代码里的每句话都要了老命。但是单就这么看好像是不是并没什么?
axios.get()方法我就不说了,这里没有涉及到一些加密传值;但是就是vue中的这个
.then(res=>{this.res = res.data.result.future});
这里的this.res是外部的全局变量!res.data.result.future是通过axios.get获取的response,
这个的前提是,外部data里已经创建了一个res的空串:
data() {return {city: "",res:[]};},
好了。重点来了,
我App.vue的父组件中获得了天气的数据,交给了res;(假设我用v-model的方法获取到了用户输入的城市)并交给了city;那我之后,该咋把值交给weather.vue呢?
Components && Props
大体解释一下这俩,
我在父组件中的export default中定义需要把数据交给谁?对,weather.vue;于是
export default{components: { weather },
}
好了,现在weather.vue可以访问父组件App.vue中所有定义的data{}值了,那之后需要用到哪个值,就在他组件后面是使用就ok了。比如我要用到data{number:1}中的number的值,我就在后面定义
<template><weather :number="number"></weather>
</template>
:number是将number的值传给了子组件,子组件使用{{ number }}调用;
父元素传值,子元素自然要接收,这里就用到了Props
/* 获取父组件传值 */props:{city:{type:String,},res:{type:Object,}}
这里不做赘述。就是将两个变量重新定义,并声明类型。
可以了,App.vue大概是如此
<script setup>
import weather from "./components/weather.vue";
import axios from "axios";</script><script>
export default {components: { weather },data() {return {city: "",res:[]};},methods: {searchEvent() {axios.get("http://apis.juhe.cn/simpleWeather/query?city=" +this.city +"&key=594cf24c8a7b4c4d56de72d5296b85df").then(res=>{this.res = res.data.result.future});},},
};
</script><template><!-- v-model 实现用户和页面的数据交互,实现把用户输入的数据和vue值更新 --><!-- v-bind 实现数值在父组件和子组件的交互,或者相同页面不同组件中的交互 --><input type="text" v-model="city" /><button @click="searchEvent">查询</button><!-- weather绑定 :city,向子组件传city的值 并且子组件中用props{city}获取传值--><weather :city="city" :res="res"></weather>
</template>
子组件weather.vue:
<template><h1>您查询的城市是:{{ city }}</h1><table class="gridtable" ><tr><th>日期</th><th>天气情况</th><th>温度</th></tr><tr><td>{{res[0].date}}</td><td>{{res[0].weather}}</td><td>{{res[0].temperature}}</td></tr><tr><td>{{res[1].date}}</td><td>{{res[1].weather}}</td><td>{{res[1].temperature}}</td></tr></table>
</template><script>
export default{/* 获取父组件传值 */props:{city:{type:String,},res:{type:Object,}}
}</script>
这里我省略了一些表格的样式。看一下最终的效果
是不是感觉项目做到这已经完成了?
nonono....
你看到的他执行效果是这样的,但实际上,他是这样的.........
实例视频
怎么解决这个问题?那就涉及到v-model这个命令了。v-bind和v-model虽然可以绑定数据,但是一旦绑定,就是响应式数据。用户输一个字,那边就显示一个字。所以就是不管用户点不点查询,另一头都会把用户输入的一个字一个字都展示出来。于是....v-model绑定数据传值,按理来说型不太通。
那该怎么办?
还有一个方法——{{ ref }}
这是一个很神奇的包。大致是这么用的
<template><!-- v-model 实现用户和页面的数据交互,实现把用户输入的数据和vue值更新 --><!-- v-bind 实现数值在父组件和子组件的交互,或者相同页面不同组件中的交互 --><input type="text" ref="searchCity" /><button @click="searchEvent">查询</button><!-- weather绑定 :city,向子组件传city的值 并且子组件中用props{city}获取传值--><weather :city="city" :res="res"></weather>
</template>
看出哪里变化了没?
输入框<input />的绑定由v-model变成了ref="searchCity"
<script>
export default {methods: {searchEvent() {const theCity = this.$refs.searchCity.value;axios.get("http://apis.juhe.cn/simpleWeather/query?city=" +theCity +"&key=594cf24c8a7b4c4d56de72d5296b85df").then(res=>{this.res = res.data.result.future,this.city = theCity});},},
};
</script>
而在JavaScript标签下,我们定义了一个变量theCity,用来存储用户点击按钮之后,input框中的value值。this.$refs.searchCity即是我们所定位的那个输入框;之后,我们把theCity的值再传给全局变量city,搞定!经过测试,用户输入完城市后,点击查询,之后数据变化。
============== ===== 又一个优化 = ==== ==========================
经过测试可以发现,我们每一次调用天气的API接口,每次的get请求都会返回5个数组。那与其使用{{ res[0].data }}的方式获取数值,为什么不能通过遍历,来将所get到的数组依次展示出来呢?
答案肯定是ok的,v-for 就可以很轻松的解决这个问题;
<template><h1>您查询的城市是:{{ city }}</h1><table class="gridtable" v-if="city"><tr v-if="city"><th>日期</th><th>天气情况</th><th>温度</th></tr><tr v-for="items in res"><td>{{ items.date }}</td><td>{{ items.weather }}</td><td>{{ items.temperature }}</td></tr></table>
</template>
============== ===== 一个问题和一个建议 = ==== ==========================
ERROR :Acess to XMLHttpRequest at 'http://apis.juhe.cn/simpleWeather/query?city=%E4%B8%B4%E6%B2%82&key=594cf24c8a7b4c4d56de72d5296b85df' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这个问题大致是因为你localhost的地址和你发起get的地址有冲突。目前的解决方式是下载Chorme里的一个模块并开启它
之后报错就解决了。
AD:本项目其实还有很多的优化没有完成。比如input中用户如果输入空值、非法字符,应弹出警告框;(或是当我们res.result获取不到有关数值时,便报错通知用户可能其输入的内容非法)。我们或许也可以使用下拉框,或是横向城市选择器,调用全国城市名称来让用户自行选取。其他的地方,比如多加些功能啊,风向什么的,都是API接口提供的,无非是table中再新加一行或一列而已。项目的主体思想还是弄清vue中常用的v-bind、v-model,甚至是v-for、v-if 。这是vue语法的核心,还需要多加练习。但是为了防止新旧技术更替,企业的新技术未迭代,在弄懂新技术的同时,也不要忘了这些指令,在H5和JavaScript中,是如何实现的。