在看了微信小程序文檔之后,覺得挺有意思的,就嘗試著寫了第一個微信小程序,主頁面樣式是這樣的:
關鍵詞: Promise、百度翻譯api、微信小程序文檔、移動端、iconfont、md5加密。
描述: 搜索微信小程序“鵬城翻譯”,可以翻譯中文—外語、外語—中文,可以實現語言切換功能,翻譯多個國家語言,同時存儲搜索過的歷史記錄,可以之后查看。
代碼步驟:
- 首先要了解微信小程序文檔、框架、組件【微信小程序文檔】
- 整理思緒,分析頁面功能,理清所需頁面
一、頁面的實現
- 將頁面分成三個頁面,分別是index、change、history
- 將三個頁面所需要的共同樣式都寫入app.wxss中,如下
@import "./assets/iconfont/iconfont.wxss";
.container {
padding: 0;
background-color: #f5fafe;
height: 100vh;
display: flex;
flex-direction: column;
/* align-items: center; */
box-sizing: border-box;
font-size: 30rpx;
color: #333;
}
.copyright {
align-self: center;
flex: 1;
display: flex;
align-items: flex-end;
padding-bottom: 20rpx;
font-size: 28rpx;
color: #999;
}
.view-hover,
.navigator-hover {
background-color: #f3f3f3!important;
}
- 配置好app.json如下
{
"pages":[
"pages/index/index",
"pages/change/change",
"pages/history/history"
],
"window":{
"backgroundColor":"#333",
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#1c1b21",
"navigationBarTitleText": "鵬城翻譯",
"navigationBarTextStyle":"#fff"
},
"tabBar":{
"borderStyle":"white",
"position":"top",
"color":"#595959",
"selectedColor":"#1c1b21",
"list": [
{
"pagePath": "pages/index/index",
"text":"翻譯"
},
{
"pagePath":"pages/history/history",
"text":"歷史"
}
]
}
}
pages
用于指定小程序由哪些頁面組成,每一項都對應一個頁面的 路徑+文件名 信息。文件名不需要寫文件后綴,框架會自動去尋找對于位置的 .json, .js, .wxml, .wxss 四個文件進行處理
數組的第一項代表小程序的初始頁面(首頁)。小程序中新增/減少頁面,都需要對 pages 數組進行修改window
用于設置小程序的狀態欄、導航條、標題、窗口背景色tabBar
如果小程序是一個多 tab 應用(客戶端窗口的底部或頂部有 tab 欄可以切換頁面),可以通過 tabBar 配置項指定 tab 欄的表現,以及 tab 切換時顯示的對應頁面
index頁面的實現
1、 在小程序中一個index頁面主要是由index.wxml、index.wxss、index.js、等組成的。
2、小程序中的WXML 中的動態數據均來自index.js中對應 Page 的 data。
3、看下我的index.wxml頁面代碼:
<!--index.wxml-->
<view class="container page-index">
<view class='change'>
<navigator url='/pages/change/change' hover-class='navigator-hover'>
<text>到{{curLang.chs}}</text>
<text class='iconfont icon-down'></text>
</navigator>
</view>
<view class='input-area'>
<text class='iconfont icon-close' hidden='{{hideClearIcon}}' bindtap='onTapClose'></text>
<view class='textarea-wrap'>
<textarea placeholder='請輸入要翻譯的文本' placeholder-style='color:#8995a1' bindinput='onInput' bindconfirm='onConfirm' bindblur='onConfirm' value='{{query}}'></textarea>
</view>
<view class='text-area'>
<view class='text-title'>譯文</view>
<view class='text-result' wx:for="{{result}}" wx:key="index">
<text selectable='true'>{{item.dst}}</text>
</view>
</view>
</view>
<view class='copyright'>
<text>@ 鵬城</text>
</view>
</view>
在小程序中
- 視圖容器用
<view></view>
-
navigator
是頁面鏈接,相當于html中的a
標簽 -
hover-class
指定按下去的樣式類。當hover-class="none"
時,沒有點擊態效果 - 數據綁定使用 Mustache 語法(雙大括號)將變量包起來
-
當用到icon圖標的時候,可以新建一個文件夾,其中放入icon圖標的代碼文件,在app.wxss中應用,以便于所有文件引用,icon圖標代碼如下圖:
icon.wxss -
hidden
屬性,后面值默認為false,在頁面中顯示出這條代碼體現是效果,但在js中可以對hidden
屬性的默認值進行設置,是否在頁面表現效果 -
bindtap
在小程序中是代表此容器是可以點擊的,在js中對bindtap
的值進行操作就可以設置點擊后要執行的事件 -
bindinput
鍵盤輸入時觸發,event.detail = {value, cursor, keyCode}
,keyCode
為鍵值,2.1.0 起支持,處理函數可以直接 return 一個字符串,將替換輸入框的內容。 -
bindconfirm
點擊完成按鈕時觸發,event.detail = {value: value}
-
bindblur
輸入框失去焦點時觸發,event.detail = {value: value}
-
textarea
多行輸入框。該組件是原生組件,使用時請注意相關限制
(1)placeholder
輸入框為空時占位符,與css相同
(2)placeholder-style
指定 placeholder 的樣式 -
wx:for
在組件上使用 wx:for 控制屬性綁定一個數組,即可使用數組中各項的數據重復渲染該組件。默認數組的當前項的下標變量名默認為 index,數組當前項的變量名默認為 item
那么index.wxml頁面,差不多就可以完成了,然后就是index.wxss和index.js,與css大同小異,遇到不能用的屬性是可以看下微信小程序api,總有你需要的效果,如下是index.wxss代碼:
/**index.wxss**/
.change {
color: #8995a1;
font-size: 24rpx;
padding: 20rpx 40rpx;
display: flex;
align-items: center;
}
.change .icon-down {
color: #8995a1;
font-size: 20rpx;
}
input-area {
position: relative;
}
.textarea-wrap {
background: #fff;
border-bottom: 1px solid #c7cee0;
}
.input-area textarea {
background-color: #fff;
padding: 30rpx 0 30rpx 30rpx;
width: calc(100% - 48rpx);
margin: 0;
box-sizing: border-box;
}
.input-area .icon-close {
position: absolute;
right: 12rpx;
top: 90rpx;
z-index: 100;
font-size: 40rpx;
color: #888;
}
.input-area .text-area {
min-height: 80rpx;
padding: 40rpx;
background-color: #fff;
}
.input-area .text-title {
font-size: 28rpx;
color: #8995a1;
}
.input-area .text-result {
padding: 20rpx 0;
}
這里需要說的就是因為在文本中輸入內容后會有一個icon顯示,以便于我們刪除文本中的內容,但如果輸入框的寬度撐滿,這個icon會被輸入框遮住,所以我在寫輸入框的樣式時,讓輸入框的寬度沒有撐滿,讓出一些,以便于icon的點擊
index.js
//index.js
import { translate } from '../../utils/api.js'
const app = getApp()
Page({
data:{
query:'',
hideClearIcon: true,
result: [],
curLang: {}
},
onLoad: function (options){
// console.log('lonload')
// console.log(options,55)
if(options.query) {
this.setData({query: options.query})
}
},
onShow: function(){
// console.log(this.data.curLang.lang)
// console.log(app.globalData.curLang.lang)
if(this.data.curLang.lang !== app.globalData.curLang.lang) {
this.setData({curLang: app.globalData.curLang})
this.onConfirm()
}
},
onInput: function(e) {
// console.log(e.detail.value)
this.setData({'query': e.detail.value})
if(this.data.query.length > 0) {
this.setData({'hideClearIcon':false})
}else{
this.setData({ 'hideClearIcon': true})
}
},
onTapClose: function(){
this.setData({query: '',hideClearIcon: true})
},
onConfirm: function(){
// console.log(this.data,'55')
if(!this.data.query) return
translate(this.data.query,{from:'auto',to: this.data.curLang.lang}).then(res=>{
// console.log(res.trans_result,'99')
//console.log(res,'5555')
this.setData({'result': res.trans_result})
//console.log(wx.getStorageSync('history'))
let history = wx.getStorageSync('history') || []
history.unshift({query: this.data.query, result: res.trans_result[0].dst})
history.length = history.length > 10 ? 10 : history.length
wx.setStorageSync('history', history)
})
}
})
Page()
看了微信小程序的文檔后都知道,在js文件中,Page(Object) 函數用來注冊一個頁面。接受一個 Object 類型參數,其指定頁面的初始數據、生命周期回調、事件處理函數等。
data:
data
是頁面第一次渲染使用的初始數據。
將會以JSON
字符串的形式由邏輯層傳至渲染層,因此data
中的數據必須是可以轉成JSON
的類型:字符串,數字,布爾值,對象,數組。
渲染層可以通過 WXML 對數據進行綁定。onLoad:
onLoad(Object query)
頁面加載時觸發。一個頁面只會調用一次,可以在 onLoad 的參數中獲取打開當前頁面路徑中的參數。onShow:
onShow()
頁面顯示/切入前臺時觸發。Page.prototype.setData(Object data, Function callback)
setData
函數用于將數據從邏輯層發送到視圖層(異步),同時改變對應的this.data
的值(同步)
Object 以 key: value 的形式表示,將 this.data 中的 key 對應的值改變成 value。
其中 key 可以以數據路徑的形式給出,支持改變數組中的某一項或對象的某個屬性,如 array[2].message,a.b.c.d,并且不需要在 this.data 中預先定義。
注意:
1、 直接修改 this.data 而不調用 this.setData 是無法改變頁面的狀態的,還會造成數據不一致。
2、 僅支持設置可 JSON 化的數據。
3、 單次設置的數據不能超過1024kB,請盡量避免一次設置過多的數據。
4、 請不要把 data 中任何一項的 value 設為 undefined ,否則這一項將不被設置并可能遺留一些潛在問題
-
onInput
onTapClose
onConfirm
這三個key都是WXML
中bindinput
、bindtap
、bindconfir
/bindblur
的值,然后在js中進行事件綁定
頁面邏輯
1、實現在輸入框中輸入內容時,對onInput
進行綁定,顯示icon,通過設置hideClearIcon
的值為false
還是true
來控制icon
2、在icon顯示后,點擊icon刪除輸入內容,對onTapClose
進行事件綁定
3、在輸入確定內容后對內容進行翻譯,對onConfirm
進行綁定,準備翻譯,然后我想到翻譯是一件事情,所以我對它進行了一個封裝,在utils里加了一個api.js,這里我用到百度翻譯的接口,所以需要查閱一下百度翻譯api,以下是api.js:
import md5 from './md5.min.js'
const appid = '20180912000205732'
const key = 'oowRg0jxzoSgwCfAfyVn'
function translate(q, { from = 'auto', to = 'auto' } = { from: 'auto', to: 'auto'}) {
return new Promise((resolve, reject) => {
let salt = Date.now()
let sign = md5(`${appid}${q}${salt}${key}`)
wx.request({
url: 'https://fanyi-api.baidu.com/api/trans/vip/translate',
data: {
q,
from,
to,
appid,
salt,
sign
},
success(res) {
if(res.data && res.data.trans_result) {
// console.log(res,'1')
// console.log(res.data,'2')
// console.log(res.data.trans_result,'3')
resolve(res.data)
} else{
reject({status: 'error', msg: '翻譯失敗'})
wx.showToast({
title: '翻譯失敗',
icon: 'none',
duration: 3000
})
}
},
fail() {
reject({status: 'error', msg: '翻譯失敗'})
wx.showToast({
title: '網絡異常',
icon: 'none',
duration: 3000
})
}
})
})
}
module.exports.translate = translate
在微信小程序中用wx.request請求數據,需要注意的是百度翻譯api要用md5進行加密,所以也需要在utils文件夾中再加入一個md5.min.js文件,這里md5文件最好是用壓縮過的,再由import來引入api.js文件中,但我沒有直接去使用它,而是用Promise對它進行一個封裝
代碼解析:
1、 wx.request
發起 HTTPS 網絡請求。使用前請注意閱讀相關說明。
2、 wx.showToast(Object object)
顯示消息提示框
3、 一個模塊要想對外暴露其內部的私有變量與函數,只能通過 module.exports
實現。exports
: 通過該屬性,可以對外共享本模塊的私有變量與函數
最后在index.js頁面中用import引入api.js,這樣就完成了index頁面的接口問題,再就是index頁面的參數問題,如要翻譯成什么語言,這就需要change頁面來實現了
change頁面的實現
先看下我的change.wxml代碼:
<view class="container lang-list">
<view class='title'>翻譯成</view>
<view class='item' data-chs="{{language.chs}}" data-lang="{{language.lang}}" data-index="{{index}}" wx:for="{{langList}}" wx:key="index" wx:for-item="language" bindtap='onTapItem' hover-class='view-hover'>
<view class='item-inner'>
<text>{{language.chs}}</text>
<text class='iconfont icon-duihao' wx:if="{{index===curLang.index}}"></text>
</view>
</view>
</view>
- 其中
data-chs
data-lang
data-index
三個都是自定義屬性 -
wx:key
如果列表中項目的位置會動態改變或者有新的項目添加到列表中,并且希望列表中的項目保持自己的特征和狀態(如 <input/> 中的輸入內容,<switch/> 的選中狀態),需要使用 wx:key 來指定列表中項目的唯一的標識符。
wx:key
的值以兩種形式提供:
1、字符串,代表在 for 循環的 array 中 item 的某個 property,該 property 的值需要是列表中唯一的字符串或數字,且不能動態改變。
2、保留關鍵字*this
代表在 for 循環中的 item 本身,這種表示需要 item 本身是一個唯一的字符串或者數字,如:
當數據改變觸發渲染層重新渲染的時候,會校正帶有 key 的組件,框架會確保他們被重新排序,而不是重新創建,以確保使組件保持自身的狀態,并且提高列表渲染時的效率。
如不提供wx:key
,會報一個warning
, 如果明確知道該列表是靜態,或者不必關注其順序,可以選擇忽略。 -
wx:for-item
可以指定數組當前元素的變量名, -
wx:if
在框架中,使用 wx:if="{{condition}}" 來判斷是否需要渲染該代碼塊
再看下change.js代碼:
//change.js
const util = require('../../utils/util.js')
const app = getApp()
Page({
data: {
curLang: {},
langList: app.globalData.langList
},
onShow: function(){
this.setData({ curLang: app.globalData.curLang})
},
onTapItem: function(e) {
let langObj = e.currentTarget.dataset //存儲自定義屬性
// console.log(e.currentTarget.dataset)
// console.log(e)
wx.setStorageSync('curLang',langObj)
this.setData({'curLang': langObj})
app.globalData.curLang = langObj
wx.switchTab({
url: '/pages/index/index',
})
}
})
寫到這里的時候,我們發現需要一個函數來保存變量,以便于在index頁面翻譯,也便于在change頁面選擇翻譯語言的時候引用,所以我把這個函數寫在app.js中,方便其他頁面調用,調用方法便是const app = getApp()
,以下是app.js:
//app.js
App({
onLaunch: function () {
//展示本地存儲能力
this.globalData.curLang = wx.getStorageSync('curLang') || this.globalData.langList[0]
},
globalData: {
curLang: {},
langList: [
{
'chs': '英文',
'lang': 'en',
"index": 0
},
{
'chs': '中文',
'lang': 'zh',
"index": 1
},
{
'chs': '日語',
'lang': 'jp',
"index": 2
},
{
'chs': '韓語',
'lang': 'kor',
"index": 3
},
{
'chs': '法語',
'lang': 'fra',
"index": 4
},
{
'chs': '西班牙語',
'lang': 'spa',
"index": 5
},
{
'chs': '阿拉伯語',
'lang': 'ara',
"index": 6
},
{
'chs': '文言文',
'lang': 'wyw',
"index": 7
},
{
'chs': '泰語',
'lang': 'th',
"index": 8
},
{
'chs': '繁體中文',
'lang': 'cht',
"index": 9
}
]
}
})
以上兩個文件中的js需要注意的代碼:
-
wx.setStorageSync
wx.setStorageSync(string key, Object|string data)
(1) string key
本地緩存中指定的 key
(2) Object|string data
需要存儲的內容
-
wx.getStorageSync
Object|string wx.getStorageSync(string key)
(1) string key
本地緩存中指定的 key
(2)Object|string data
key對應的內容
-
wx.switchTab
跳轉到 tabBar 頁面,并關閉其他所有非 tabBar 頁面 - 在執行
onTapItem
事件時,可以得到參數e,并且e.currentTarget.dataset
可以儲存自定義的屬性,以便于頁面使用
然后在聯系上最初的index頁面,我們就可以選擇需要翻譯的語言了,同時在本地緩存中存儲了當前翻譯的語言,下次打開小程序時,還是上次保存的翻譯語言
history頁面實現
看一看history.wxml:
<!--pages/history/history.wxml-->
<scroll-view scroll-y class="container">
<view class="history-list">
<view class="title">翻譯歷史</view>
<view class="item" wx:for="{{history}}" wx:key="index" bindtap='onTapItem' data-query="{{item.query}}" data-langId="{{item.langIndex}}">
<view class="query">{{item.query}}</view>
<view class="result">{{item.result}}</view>
</view>
</view>
</scroll-view>
history.wxml中主要的就是把它當做一個數組去渲染,然后點擊對應數組,跳轉到index頁面,進行當前語言翻譯,而這些功能需要在history.js和change.js中去實現
看一看history.js:
//pages/history/history.js
const app = getApp()
Page({
data: {
history: []
},
onShow: function() {
this.setData({history: wx.getStorageSync('history')})
},
onTapItem: function(e) {
wx.reLaunch({
url: `/pages/index/index?query=${e.currentTarget.dataset.query}`
})
}
})
當執行以上代碼時,便會用到index.js中onConfirm
事件的這一段代碼:
let history = wx.getStorageSync('history') || []
history.unshift({query: this.data.query, result: res.trans_result[0].dst})
history.length = history.length > 10 ? 10 : history.length
wx.setStorageSync('history', history)
其中存儲好history的值,便于history頁面去使用
-
db.command.unshift
更新指令,對一個值為數組的字段,往數組頭部添加一個或多個值。或字段原為空,則創建該字段并設數組為傳入值。 -
wx.reLaunch(Object object)
關閉所有頁面,打開到應用內的某個頁面
(1) url : 需要跳轉的應用內頁面路徑 , 路徑后可以帶參數。參數與路徑之間使用?分隔,參數鍵與參數值用=相連,不同參數用&分隔;如path?key=value&key2=value2
,如果跳轉的頁面路徑是 tabBar 頁面則不能帶參數
當切換到index頁面時,會用到index.js中的這些代碼:
onLoad: function (options){
// console.log('lonload')
// console.log(options,55)
if(options.query) {
this.setData({query: options.query})
}
},
onShow: function(){
// console.log(this.data.curLang.lang)
// console.log(app.globalData.curLang.lang)
if(this.data.curLang.lang !== app.globalData.curLang.lang) {
this.setData({curLang: app.globalData.curLang})
this.onConfirm()
}
}
其中onLoad
在加載頁面中設置query的值,onShow
在顯示頁面后翻譯query的值
這樣就構成了整個小程序目前需要的需求,如果還想有其他更多的需求,可以在此代碼上繼續添加
The early bird catches the worm