项目的仓库weather-forecast-vue。
项目demo
目前完成了以下功能
先让我们来看看最终的效果是什么样的~
话不多说,动手实现吧~
Step1 安装vue-cli
此处默认你的电脑当中已经安装 node.js
和 npm
- 全局安装
webpack
- 全局安装
vue-cli
1
|
npm install --global vue-cli
|
Step2 构建项目并安装插件
构建 vue
项目
1
2
3
4
|
vue init webpack vueWeather //vueWeather为此项目的名称你也可以使用其他的
cd ./vueWeather //进入到项目目录
npm install
npm run dev //以dev方式执行项目
|
此时你就可以在浏览器当中看到你正在运行的项目
当第一个命令执行后,会有几个选项让你选:
Project name
项目名称
Project description (A Vue.js project)
项目描述,也可直接点击回车,使用默认名字
Author ()
作者
Runtime + Compiler: recommended for most users
运行加编译,回车选择yesRuntime-only: about 6KB lighter min+gzip, but templates (or any Vue-specificHTML) are ONLY allowed in .vue files - render functions are required elsewhere
仅运行时,已经有推荐了就选择第一个了
Install vue-router? (Y/n)
是否安装vue-router,项目中我们会使用vue-router来做路由,所以选择yes
Use ESLint to lint your code? (Y/n)
是否使用ESLint管理代码,根据自己的情况做出选择
Setup unit tests with Karma + Mocha? (Y/n)
是否安装单元测试,笔者本次没有安装
Setup e2e tests with Nightwatch(Y/n)?
是否安装e2e测试,笔者本次没有安装
插件安装
vue拥有丰富的轮子,所以我们可以直接使用已经写好的轮子,从而提高开发效率。
- 安装
axios
Vue
原本有一个官方推荐的 ajax
插件 vue-resource
,但是自从 Vue
更新到 2.0 之后,尤雨溪宣布停止更新 vue-resource
,并推荐大家使用axios
之后,越来越多的 Vue
项目,都选择 axios
来完成 ajax
请求,而大型项目会使用 Vuex
来管理数据。
1
|
npm install axios --save
|
大部分的插件可以在 `main.js` 文件中引入后进行 `Vue.use()`,但是 `axios` 不能 `use`。只能每个需要发送请求的组件中即时引入。为了解决这个问题,有两种开发思路,一是在引入 `axios` 之后,修改原型链,二是结合 `Vuex`,封装一个 `aciton`。这里只说修改原型链的方式
在 main.js
中引入 axios
1
|
import axios from 'axios'
|
这时候如果在其它的组件中,是无法使用 `axios` 命令的。所以我们将 `axios` 改写为 `Vue` 的原型属性
1
|
Vue.prototype.$http= axios
|
在 `main.js` 中添加了这两行代码之后,就能直接在组件的 `methods` 中使用 `$http`命令
- 安装
muse-ui
由于要实现手机端的webapp,于是在比较流行的框架中选择了muse-ui
进行开发,具体使用方式如下,你也可以查看官方文档,进行跟深一步的学习。
安装完成后,在`main.js`文件中进行引用
1
2
3
4
|
import MuseUI from 'muse-ui';
import 'muse-ui/dist/muse-ui.css';
Vue.use(MuseUI);
|
除此之外,还用到了 `muse-ui` 中提供的 `muse-ui-loading` 和 `muse-ui-progress`
muse-ui-loading
用于实现加载动画
1
|
npm install muse-ui-loading --save
|
```js
import ‘muse-ui-loading/dist/muse-ui-loading.css’; // load css
import Loading from ‘muse-ui-loading’;
Vue.use(Loading);
1
2
3
|
插件`muse-ui-progress`用于实现页面加载的进度条
```Shell
npm install muse-ui-progress --save
|
```js
//index.js 文件
import ‘muse-ui-progress/dist/muse-ui-progress.css’;
import NProgress from ‘muse-ui-progress’;
Vue.use(NProgress);
1
2
3
|
在 `index.html` 引入 `muse-ui` 推荐的[Material Design Icons](https://material.io/tools/icons/)字体图标库
```html
<link rel="stylesheet" href="https://cdn.bootcss.com/material-design-icons/3.0.1/iconfont/material-icons.css">
|
- 安装
vue-xc-city
使用vue-xc-city这个插件用于城市搜索
1
|
npm install vue-xc-city --save
|
安装完成后,在`main.js`文件中进行引用
1
2
3
4
|
import vueXcCity from 'vue-xc-city';
import 'vue-xc-city/dist/xc-city.css';
Vue.use(vueXcCity);
|
- 安装
vue-slip-delete
使用vue-slip-delete这个插件用于实现城市左滑删除
1
|
npm install vue-slip-delete --save
|
- 安装
v-distpicker
使用v-distpicker这个插件用于实现城市选择功能
1
|
npm install v-distpicker --save
|
Step3 定位与天气API
因为要查看当前位置的天气信息,所有要使用到定位API和天气API。
天气API
笔者在这里选用了心知天气提供的付费API。
心知天气支持全国2567个、全球24,373个城市和地点。
定位API
这次所使用的是高德地图所提供的API。
Step4 解决跨域问题
vue
项目中,前端与后台进行数据请求或者提交的时候,如果后台没有设置跨域,前端本地调试代码的时候就会报“ No 'Access-Control-Allow-Origin' header is present on the requested resource.
” 这种跨域错误。
这样的问题解决办法如下:
在 config/index.js
中找到 proxyTable
,将内容修改为如下
1
2
3
4
5
6
7
8
9
10
|
proxyTable: {
'/apis': { //将www.exaple.com印射为/apis
target: '', // 接口域名
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, //是否跨域
pathRewrite: {
'^/apis': '' //需要rewrite的,
}
}
},
|
这样修改后,使用 axios
就可以直接这样用:
1
2
3
4
|
getData () {
this.$http.get('/apis/XXX', function (res) {
console.log(res)
})
|
Step5 修改字体
在 scr
目录下新建 common/font
目录,将要添加/修改的字体拷贝进去,然后新建 font.css
文件,样例为思源黑体。
1
2
3
4
5
6
7
|
@font-face {
font-family: 'SourceHanSansCN';
src: url('./SourceHanSansCN.otf');
font-weight: normal;
font-style: normal;
}
|
Step6 编写 Vue
组件
这一部分,笔者会挑组件中相对比较复杂的组件进行说明,其他组件大家直接看GitHub中的代码即可!
Weather
组件
这一组件用于显示天气信息,是一个复用的组件,展示当前城市天气,以及其他城市天气信息。
这一页面由其他多个组件共同构成,通过传参来显示信息。
下拉刷新
这个页面的下拉刷新使用的是muse-ui
所提供的组件。
1
2
3
|
<mu-load-more @refresh="refresh" :refreshing="refreshing" :loading="loading" @load="load" :loading-text="localPeom">
</mu-load-more>
|
HelloWorld
组件
这个组件是路由的默认页面,页面中只包含了 Weather
组件,HelloWorld
组件用于获取用户当前位置信息,然后再传递给 Weather
组件。
在 index.html
中添加以下代码
1
|
<script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.9&key=你的key"></script>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
GaoDeAPI(){
var _this = this;
var map, geolocation;
//加载地图,调用浏览器定位服务
map = new AMap.Map('FakeContainer', {
resizeEnable: true
});
map.plugin('AMap.Geolocation', function() {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true,//是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
zoomToAccuracy: true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
buttonPosition:'RB'
});
map.addControl(geolocation);
geolocation.getCurrentPosition();
AMap.event.addListener(geolocation, 'complete', onComplete);//返回定位信息
AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息
});
//解析定位结果
function onComplete(data) {
var str=['定位成功'];
str.push('经度:' + data.position.getLng());
str.push('纬度:' + data.position.getLat());
_this.lon = data.position.getLng();
_this.lat = data.position.getLat();
var c = data.addressComponent.city;
c= c.substring(0,c.length-1);
localStorage.setItem('currentCity',c);
console.log(c);
if(data.accuracy){
str.push('精度:' + data.accuracy + ' 米');
}//如为IP精确定位结果则没有精度信息
str.push('是否经过偏移:' + (data.isConverted ? '是' : '否'));
//console.log(str);
}
//解析定位错误信息
function onError(data) {
console.log("gaode:"+data.message);
}
},
|
HourWeather
组件
这一组件用来显示未来24小时,逐小时天气信息。
这个模块重点要实现左右滑动的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<div class="HourBorder">
<div class="HourAuto">
<div class="context">
<ul style="list-style:none;">
<li v-for="(item,index) in hourData" :key="index" class="aDay" style="float:left; padding:0; ">
<p>{{backTime(item.time)}}</p>
<img :src="imgList[item.code]" alt="icon" ondragstart="return false;" oncontextmenu="return false;">
<p>{{item.temperature}}°</p>
</li>
</ul>
</div>
</div>
</div>
|
HourBorder
类用于控制逐小时显示的大小,overflow: hidden
的设置使超出div的部分隐藏,不会使整个页面变形。
HourAuto
类用于横向滑动,当元素宽度超过父容器时,自动左右滑动。
City
组件
这一组件当中,用到了 vue-slip-delete
这一插件,用于删除已添加的城市。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<slip-del
v-for="(item, i) in list"
:key="i"
ref="slipDel"
@slip-open="slipOpen"
@del-click="del(i)"
v-if="i!=0"
>
<div class="demo-item">
<mu-row class="d2" @click="navToFav(item.results[0].location.name)" :style="{backgroundImage:'url('+item.results[0].now.imageurl+dayOrNight+'.jpg'}">
<mu-col span="9">
<div class="d3">
{{item.results[0].now.text}}
</div>
<div class="d4">
{{item.results[0].location.name}}
</div>
</mu-col>
<mu-col span="3">
<div class="d5">
{{item.results[0].now.temperature}}
</div>
</mu-col>
</mu-row>
</div>
<div slot="del">
<mu-icon size="32" value="delete" style="display: block; margin: 0 auto;"></mu-icon>
</div>
</slip-del>
|
del()
函数用于处理用户删除后的操作,这里我们将索引传入,用于删除相对应的城市。
Favourite
组件
Favourite
组件与 HelloWorld
组件有类似的地方:整个组件是由 Weather
组件组成。不同的是,在城市列表页选择了城市后,跳转到 Favourite
组件中,并显示指定城市的天气情况。
CitySelect
组件
这个组件的作用是选择想要添加的城市,可以通过搜索和三级联动进行选择。分别用到了第三方插件vue-xc-city
和 v-distpicker
。
1
2
3
4
|
<xc-city @get-data="getData"></xc-city>
<div class="divwrap" v-if="show">
<v-distpicker type="mobile" hide-area @province="onChangeProvince" @city="onChangeCity" @area="onChangeArea"></v-distpicker>
</div>
|
其他组件
其他组件大部分都是展示像后台请求到的信息,使用不同的布局进行展示,以达到简洁但不简单的效果!
详细代码可以查看weather-forecast-vue
欢迎学习交流~