小程序已經(jīng)出來很久了肾档,最近又在學(xué)習(xí)JavaScript,而小程序的開發(fā)語(yǔ)言也是基于JavaScript,所以就打算學(xué)習(xí)一下微信小程序開發(fā)椎例。
大家可以在微信小程序里搜搜索Lite天氣即可體驗(yàn)。
原理
微信小程序的原理印颤,看一下微信官方的文檔寫到:
三端的腳本執(zhí)行環(huán)境聚以及用于渲染非原生組件的環(huán)境是各不相同的:
- 在 iOS 上您机,小程序的 javascript 代碼是運(yùn)行在 JavaScriptCore 中,是由 WKWebView 來渲染的,環(huán)境有 iOS8际看、iOS9咸产、iOS10
- 在 Android 上,小程序的 javascript 代碼是通過 X5 JSCore來解析仲闽,是由 X5 基于 Mobile Chrome 53 內(nèi)核來渲染的
- 在 開發(fā)工具上脑溢, 小程序的 javascript 代碼是運(yùn)行在 nwjs 中,是由 Chrome Webview 來渲染的
據(jù)我猜測(cè)赖欣,我覺得微信小程序能夠提供如此完整的API屑彻,并且性能也有如此之好的體驗(yàn),其原理應(yīng)該是和React Native的原理類似顶吮,通過微信自己的JavaScript運(yùn)行引擎社牲,最終將其中的代碼翻譯成Native的原生控件并展示出來,以達(dá)到媲美原生APP的性能以及用戶體驗(yàn)悴了。
再說開發(fā)工具搏恤,文中提到了nw.js,這個(gè)nwjs據(jù)我所知就是node.js與Browser運(yùn)行時(shí)的合并湃交,據(jù)我所知這個(gè)nw.js就是一個(gè)專門用于跨平臺(tái)開發(fā)的工程熟空,其可利用node.js訪問系統(tǒng)原生的API。但是經(jīng)過我google搞莺,我發(fā)現(xiàn)現(xiàn)在有一個(gè)叫做Electron的項(xiàng)目比nw.js更為火熱息罗,其中atom和vscode也是基于Electron開發(fā)的。至于微信為什么采用nw.js開發(fā)腮敌,我也是不是很了解阱当。
其中大概的原理就講到這里,有興趣的可以參考一下文章
開發(fā)準(zhǔn)備
我這里就不過多介紹微信小程序的詳細(xì)教程弊添,因?yàn)槲⑿盘峁┑?a target="_blank" rel="nofollow">官方文檔已經(jīng)十分詳細(xì)地介紹了微信小程序的文件類型、項(xiàng)目結(jié)構(gòu)框架捌木、具體API油坝。
不過我建議在編寫微信小程序之前應(yīng)該要有一下的基礎(chǔ):
- JavaScript基礎(chǔ),node.js刨裆、ES6基礎(chǔ)
- XML文件澈圈、Html文件基礎(chǔ)、CSS基礎(chǔ)
- 了解flex布局
有了以上的基礎(chǔ)知識(shí)帆啃,在官方文檔的指導(dǎo)下瞬女,絕對(duì)能夠快速地進(jìn)行小程序的開發(fā)。
項(xiàng)目實(shí)戰(zhàn)
項(xiàng)目結(jié)構(gòu):
現(xiàn)在只開發(fā)了一個(gè)簡(jiǎn)單的天氣頁(yè)面努潘,因此只寫了index頁(yè)面建立了weather相關(guān)的頁(yè)面诽偷。
app.js:
這個(gè)文件相當(dāng)于Android 程序中的application坤学,整個(gè)程序只有一個(gè)app.js到單例,因此程序的各種生命周期的回調(diào)都在這里报慕,并且可以存儲(chǔ)一下程序的全局?jǐn)?shù)據(jù)以及變量深浮,例如用戶數(shù)據(jù)等。
App({
onLaunch: function () {
},
onShow: () => {
console.log('onshow');
},
onHide: () => {
console.log('onHide');
},
getUserInfo: function (cb) {
var that = this
if (this.globalData.userInfo) {
typeof cb == "function" && cb(this.globalData.userInfo)
} else {
//調(diào)用登錄接口
wx.login({
success: function () {
wx.getUserInfo({
success: function (res) {
that.globalData.userInfo = res.userInfo
typeof cb == "function" && cb(that.globalData.userInfo)
}
})
}
})
}
},
globalData: {
userInfo: null
}
})
app.json:
這個(gè)相當(dāng)于是程序的路由表以及配置表眠冈,包括程序的頁(yè)面注冊(cè)飞苇、網(wǎng)絡(luò)配置、底部導(dǎo)航欄蜗顽、頂部導(dǎo)航欄的配置都可以在這里編寫布卡,具體的可以在官方文檔里進(jìn)行查看。
{
"pages": [
"pages/weather/index/weather",
"pages/weather/city/city",
"pages/weather/detail/detail",
"pages/weather/setting/setting"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#09bb07",
"navigationBarTitleText": "Lite天氣",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true
},
"tabBar": {
"list": [
{
"pagePath": "pages/weather/index/weather",
"text": "天氣",
"iconPath": "/resources/weather_icon_normal.png",
"selectedIconPath": "/resources/weather_icon_selected.png"
},
{
"pagePath": "pages/weather/setting/setting",
"text": "我的",
"iconPath": "/resources/setting_icon_normal.png",
"selectedIconPath": "/resources/setting_icon_selected.png"
}
],
"color": "#dbdbdb",
"selectedColor": "#09bb07",
"backgroundColor": "#ffffff",
"borderStyle": "black"
},
"networkTimeout": {
"request": 5000,
"connectSocket": 20000,
"uploadFile": 20000,
"downloadFile": 20000
}
}
之前都是app的全局配置诫舅,現(xiàn)在來編寫具體的頁(yè)面羽利。
預(yù)期的頁(yè)面是這個(gè)樣子的設(shè)計(jì)。
weather.wxml:
<view class="container">
<image src="{{backgroudUrl}}" mode="scaleToFill"></image>
<view class="header">
<view class="header-top">
<view id="city">{{weather.city}}</view>
<view>{{weather.update}}</view>
</view>
<view class="header-condition">
<view id="temp">{{weather.now.tmp}}℃</view>
<view>{{weather.now.cond.txt}}</view>
</view>
</view>
<view class="item-list" wx:for="{{weather.daily}}">
<view class="daily-item">
<view class="item-date">{{item.date}}</view>
<view class="item-date">{{item.cond.txt_d}}</view>
<view class="item-date">{{item.tmp.max}}</view>
<view class="item-date">{{item.tmp.min}}</view>
</view>
</view>
<view class="air-list">
<view class="air-item">
<view>{{weather.aqi.qlty}}</view>
<view>空氣質(zhì)量</view>
</view>
<view class="air-item">
<view>{{weather.aqi.aqi}}</view>
<view>AQI</view>
</view>
<view class="air-item">
<view>{{weather.aqi.pm25}}</view>
<view>PM2.5</view>
</view>
<view class="air-item">
<view>{{weather.aqi.pm10}}</view>
<view>PM10</view>
</view>
<view class="air-item">
<view>{{weather.now.hum}}%</view>
<view>濕度</view>
</view>
</view>
<view class="life-suggestion">
<text>空氣指數(shù):{{weather.suggestion.air.brf}},{{weather.suggestion.air.txt}}</text>
<text>舒適度指數(shù):{{weather.suggestion.comf.brf}},{{weather.suggestion.comf.txt}}</text>
<text>洗車指數(shù):{{weather.suggestion.cw.brf}},{{weather.suggestion.cw.txt}}</text>
<text>穿衣指數(shù):{{weather.suggestion.drsg.brf}},{{weather.suggestion.drsg.txt}}</text>
<text>感冒指數(shù):{{weather.suggestion.flu.brf}},{{weather.suggestion.flu.txt}}</text>
<text>運(yùn)動(dòng)指數(shù):{{weather.suggestion.sport.brf}},{{weather.suggestion.sport.txt}}</text>
<text>旅游指數(shù):{{weather.suggestion.trav.brf}},{{weather.suggestion.trav.txt}}</text>
<text>紫外線指數(shù):{{weather.suggestion.uv.brf}},{{weather.suggestion.uv.txt}}</text>
</view>
</view>
類似于css的可以設(shè)置在weather.wxss
.container {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
image {
width: 100%;
height: 250%;
filter: blur(1px);
position: absolute;
z-index: -1;
}
.header {
padding: 20rpx;
height: 400rpx;
display: flex;
justify-content: space-between;
flex-direction: column;
margin-bottom: 20rpx;
}
.header-top {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.header-condition {
padding: 20rpx;
display: flex;
align-self: flex-end;
align-items: center;
flex-direction: column;
justify-content: flex-end;
}
#temp {
color: #fff;
margin-bottom: 20rpx;
font-size: 100rpx;
}
.header view {
color: #fff;
font-size: 30rpx;
}
.item-list {
padding: 20rpx;
margin-left: 40rpx;
margin-right: 40rpx;
background: rgba(0, 0, 0, 0.2);
border-radius: 3rpx;
}
.daily-item {
font-size: 30rpx;
color: white;
display: flex;
flex-direction: row;
justify-content: space-around;
}
.air-list {
margin-left: 40rpx;
margin-right: 40rpx;
margin-top: 60rpx;
margin-bottom: 40rpx;
display: flex;
height: 100px;
color: white;
font-size: 30rpx;
align-items: center;
flex-direction: row;
justify-content: space-around;
background: rgba(0, 0, 0, 0.2);
border-radius: 3rpx;
}
.air-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.air-item view {
margin: 20rpx;
}
.life-suggestion {
color: white;
padding: 50rpx;
font-size: 30rpx;
margin-left: 40rpx;
margin-right: 40rpx;
margin-bottom: 40rpx;
display: flex;
flex-direction: column;
background: rgba(0, 0, 0, 0.2);
border-radius: 3rpx;
}
.life-suggestion text {
margin-top: 20rpx;
font-style: oblique;
}
weather.js
最后就是頁(yè)面的數(shù)據(jù)的獲取以及相應(yīng)的邏輯處理:
const weatherUtil = require('../../../utils/weatherUtil.js');
const imageUtil=require('../../../utils/imageUtil.js');
var app = getApp();
function refreshData(that) {
weatherUtil.loadWeatherData((success, data) => {
that.setData({
weather: data
});
wx.stopPullDownRefresh();
});
}
Page({
data: {
title: 'Lite天氣',
weather: {},
backgroudUrl:''
},
bindViewTap: function () {
},
onLoad: function () {
var that=this;
imageUtil.requestDailyImageUrl((url)=>{
that.setData({
backgroudUrl:url
});
});
refreshData(that);
},
onPullDownRefresh: function () {
refreshData(this);
}
})
我將獲取位置以及一些數(shù)據(jù)都封裝在了weatherUtil里面,這個(gè)天氣API是和風(fēng)天氣提供的刊懈,可以自己去和風(fēng)天氣官網(wǎng)申請(qǐng)key值:
const baseUrl = 'https://free-api.heweather.com/v5/weather?key=';
const app = getApp();
/**
* 根據(jù)經(jīng)緯度獲取天氣
*/
function requestWeatherByLocation(latitude, longitude, callback) {
wx.request({
url: baseUrl + '&city=' + longitude + ',' + latitude,
data: {},
method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
// header: {}, // 設(shè)置請(qǐng)求的 header
success: function (res) {
// success
var result = pareseWeahterData(res);
callback(true, result);
},
fail: function (res) {
// fail
callback(false);
}
});
}
/**
* 獲取天氣回調(diào)
*/
function requestWeatherData(callback) {
requestLocation((success, latitude, longitude) => {
if (success == false) {
latitude = 120.343;
longitude = 36.088;
}
requestWeatherByLocation(latitude, longitude, callback);
});
}
/**
* 解析數(shù)據(jù)
*/
function pareseWeahterData(orign) {
var weather = {};
console.log(orign);
var data = orign.data.HeWeather5[0];
weather.city = data.basic.city;
weather.now = data.now;
weather.daily = data.daily_forecast;
weather.suggestion = data.suggestion;
weather.basic = data.basic;
weather.update = data.basic.update.loc.substring(10, 16);
weather.aqi=data.aqi.city;
console.log(weather);
return weather;
}
/**
* 獲取位置信息这弧,返回經(jīng)緯度
*/
function requestLocation(callback) {
wx.getLocation({
type: 'wgs84', // 默認(rèn)為 wgs84 返回 gps 坐標(biāo),gcj02 返回可用于 wx.openLocation 的坐標(biāo)
success: function (res) {
callback(true, res.latitude, res.longitude);
},
fail: function (res) {
callback(false);
}
});
}
function loadWeatherData(callback) {
requestWeatherData(callback);
}
module.exports = { loadWeatherData: loadWeatherData }
ok虚汛,以上就是一個(gè)簡(jiǎn)單的天氣頁(yè)面的開發(fā)匾浪。
總之,按照官方文檔的指導(dǎo)卷哩,很輕松就能夠制作一個(gè)簡(jiǎn)單的微信小程序蛋辈。
總結(jié)
通過開發(fā)這個(gè)簡(jiǎn)答的天氣小程序,收獲了不少将谊,但是也不得不吐槽一下微信小程序的設(shè)計(jì)冷溶。
- 開發(fā)十分簡(jiǎn)單,只要掌握簡(jiǎn)單的JavaScript基礎(chǔ)就能夠快速開發(fā)尊浓。
- 提供了較為完整的組件以及各種API
- API設(shè)計(jì)比較蛋疼逞频,有些設(shè)計(jì)得不太好。
- 在程序里使用網(wǎng)絡(luò)請(qǐng)求需要進(jìn)行指定對(duì)應(yīng)的域名栋齿,否則不能夠訪問苗胀。
- 程序的提交審核比較快,我的大概是用了一天的時(shí)間就申請(qǐng)好了瓦堵。
下一個(gè)研究的就是ReactNative基协,之后會(huì)用RN開發(fā)一個(gè)Lite天氣。
項(xiàng)目源碼:https://github.com/nickming/WXLiteWeather
歡迎star