小程序已經(jīng)出來(lái)很久了,最近又在學(xué)習(xí)JavaScript,而小程序的開(kāi)發(fā)語(yǔ)言也是基于JavaScript,所以就打算學(xué)習(xí)一下微信小程序開(kāi)發(fā)。 大家可以在微信小程序里搜搜索Lite天氣即可體驗(yàn)。
微信小程序的原理,看一下微信官方的文檔寫(xiě)到:
三端的腳本執(zhí)行環(huán)境聚以及用于渲染非原生組件的環(huán)境是各不相同的:
- 在 iOS 上,小程序的 javascript 代碼是運(yùn)行在 JavaScriptCore 中,是由 WKWebView 來(lái)渲染的,環(huán)境有 iOS8、iOS9、iOS10
- 在 Android 上,小程序的 javascript 代碼是通過(guò) X5 JSCore來(lái)解析,是由 X5 基于 Mobile Chrome 53 內(nèi)核來(lái)渲染的
- 在 開(kāi)發(fā)工具上, 小程序的 javascript 代碼是運(yùn)行在 nwjs 中,是由 Chrome Webview 來(lái)渲染的
據(jù)我猜測(cè),我覺(jué)得微信小程序能夠提供如此完整的API,并且性能也有如此之好的體驗(yàn),其原理應(yīng)該是和React Native的原理類似,通過(guò)微信自己的JavaScript運(yùn)行引擎,最終將其中的代碼翻譯成Native的原生控件并展示出來(lái),以達(dá)到媲美原生APP的性能以及用戶體驗(yàn)。
再說(shuō)開(kāi)發(fā)工具,文中提到了nw.js,這個(gè)nwjs據(jù)我所知就是node.js與Browser運(yùn)行時(shí)的合并,據(jù)我所知這個(gè)nw.js就是一個(gè)專門(mén)用于跨平臺(tái)開(kāi)發(fā)的工程,其可利用node.js訪問(wèn)系統(tǒng)原生的API。但是經(jīng)過(guò)我google,我發(fā)現(xiàn)現(xiàn)在有一個(gè)叫做Electron的項(xiàng)目比nw.js更為火熱,其中atom和vscode也是基于Electron開(kāi)發(fā)的。至于微信為什么采用nw.js開(kāi)發(fā),我也是不是很了解。
其中大概的原理就講到這里,有興趣的可以參考一下文章
我這里就不過(guò)多介紹微信小程序的詳細(xì)教程,因?yàn)槲⑿盘峁┑墓俜轿臋n已經(jīng)十分詳細(xì)地介紹了微信小程序的文件類型、項(xiàng)目結(jié)構(gòu)框架、具體API。
不過(guò)我建議在編寫(xiě)微信小程序之前應(yīng)該要有一下的基礎(chǔ):
有了以上的基礎(chǔ)知識(shí),在官方文檔的指導(dǎo)下,絕對(duì)能夠快速地進(jìn)行小程序的開(kāi)發(fā)。
項(xiàng)目結(jié)構(gòu):
現(xiàn)在只開(kāi)發(fā)了一個(gè)簡(jiǎn)單的天氣頁(yè)面,因此只寫(xiě)了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)航欄的配置都可以在這里編寫(xiě),具體的可以在官方文檔里進(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)在來(lái)編寫(xiě)具體的頁(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è)面的開(kāi)發(fā)。
總之,按照官方文檔的指導(dǎo),很輕松就能夠制作一個(gè)簡(jiǎn)單的微信小程序。
通過(guò)開(kāi)發(fā)這個(gè)簡(jiǎn)答的天氣小程序,收獲了不少,但是也不得不吐槽一下微信小程序的設(shè)計(jì)。
下一個(gè)研究的就是ReactNative,之后會(huì)用RN開(kāi)發(fā)一個(gè)Lite天氣。
項(xiàng)目源碼:https://github.com/nickming/WXLiteWeather
歡迎star