在看了微信小程序文档之后,觉得挺有意思的,就尝试着写了第一个微信小程序,主页面样式是这样的:
关键词: 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页面代码:
到{{curLang.chs}}
译文
{{item.dst}}
@ 鹏城
在小程序中
- 视图容器用
-
navigator
是页面链接,相当于html中的a
标签 -
hover-class
指定按下去的样式类。当hover-class="none"
时,没有点击态效果 - 数据绑定使用 Mustache 语法(双大括号)将变量包起来
-
当用到icon图标的时候,可以新建一个文件夹,其中放入icon图标的代码文件,在app.wxss中应用,以便于所有文件引用,icon图标代码如下图:
-
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代码:
翻译成
{{language.chs}}
- 其中
data-chs
data-lang
data-index
三个都是自定义属性 -
wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 中的输入内容,的选中状态),需要使用 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:
翻译历史
{{item.query}}
{{item.result}}
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