真机调试公众号网页
1. 修改本机 hosts 文件
打开 hosts 文件,Windows 上在 C:\Windows\System32\drivers\etc
文件夹下,将我们公众号内配置的域名指向 127.0.0.1
,如下图:
修改完毕之后保存,这时候跑起项目来,我们就可以在微信开发工具中通过访问 test.com
+ port 来访问我们的项目了。接下来通过 nodejs
写几行代码,使我们可以直接在80端口上访问项目
2. 端口代理
建立一个 js 文件,在我们启动项目的时候运行一下这个 js 文件,项目跑起来后,我们访问 test.com
就能直接访问到我们开发的项目了
const http = require('http')
const httpProxy = require('http-proxy')
const proxy = httpProxy.createProxyServer()
http.createServer(function(req, res) {
// 8080 换成自己项目启动的端口
proxy.web(req, res, { target: 'http://127.0.0.1:8080' })
}).listen(80)
3. 手机代理设置
首先在电脑上安装Charles, Charles 是一个抓包工具,我们需要利用它来代理我们的手机网络请求,将我们手机的 test.com
的网络请求转发到本地。安装完毕后选择 代理 > 代理设置
设置代理端口,默认为8888,一般不需要更改。
然后手机操作,确保手机和电脑在同一个 WIFI 下,配置代理,服务器填写我们电脑在局域网内的 ip,可以通过 ipconfig 命令查询,端口填写PC上Charles刚刚设置的端口,配置完毕后我们在手机端输入 test.com
就可以愉快的在手机 debug 我们的前端项目了。
微信 js-sdk
的配置问题
公众号开发大部分情况下都要用到微信提供的 js-sdk
, 借助 js-sdk
可以使我们方便的调用手机的拍照、语音、位置、等手机系统功能以及一些微信特有的功能,这里我用到了 sdk
的拍照功能。在使用这个 sdk 前,需要先进行配置,执行 wx.config(options)
方法,配置成功后才可以进行调用。配置方法官网描述如下:
其中参数 signature
的生成步骤需要公众号的 AppSecret
,因此这一步要交给后端来完成,前端需要把URL传递给后端,后端获取到签名后返回给前端。在这里有几个坑需要说明一下:
- 坑1:官网描述:
同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用
。 由于在微信中url
可能在某些条件下微信会加一些参数形如#STATE
,因此路由尽量使用history
模式,因此通过URL获取签名的时候URL获取方式为:location.href.split('#')[0]
。测试之后发现在安卓机和开发者工具上都正常,在iOS
上提示invalid signature
,但是刷新一下就提示:config: ok
了,肯定不能让用户去刷新的,一番搜索后发现在iOS
上不管页面跳转几次都只认首次进入的URL,看到网上有些解决方案是缓存首次进入页面的URL,签名时使用缓存的URL,尝试了一下,都OK了,在iOS上尝试刷新一下结果又不行了,这次连invalid signature
都没跳出来,wx.config
执行了后完全没反应,但是再刷新一次又可以了。折腾了半天后也没弄好,后来想了一下,既然iOS
只认首次进入的URL,那么直接在首次进入页面的时候 config 一下不就好了,尝试了一下,做一个判断,如果是iOS
就只在最顶层组件执行 config,尝试之后完美解决,首页配置后,其他页面调用sdk都没问题,不管刷新多少次都能弹出config: ok
。 - 坑2:拿到URL后需要使用
encodeURI
编码一下,否则某些情况还是会出现invalid signature
的错误,比如URL中含有拼接的json字符串参数。 - 坑3:在PC上的微信内置浏览器中配置成功的,提示:
config: ok
但是在调用 sdk 具体功能时提示:permission denied
。这个暂时没找到解决方案,不过我这里只用到了选择图片之类的一些基本功能,在PC上都很好实现,就做了一个判断,如果是PC微信浏览器就不使用微信的 sdk,直接前端实现。
js-sdk
选择多张图片问题
微信的 sdk
调用都是回调函数的方式,为了方便使用时对其进行了 promise 化封装,调用了 chooseImage
方法和 getLocalImgData
方法,直接返回图片的 base64
数据,最开始 chooseImage
封装方法如下:
// 有问题的版本
const chooseImage = params => {
return new Promise((resolve, reject) => {
wx.chooseImage({
...params,
success: res => {
const { localIds } = res
const results = localIds.map(localId => new Promise((innerResolve, innerReject) => {
wx.getLocalImgData({
localId,
success: response => innerResolve(response.localData),
fail: err => innerReject(err)
})
}))
return resolve(Promise.all(results))
}
})
})
}
上面的方法在选择单张图片的时候没出现问题,但是在选择多张图片的时候发现只返回了一张图片,于是面向搜索引擎编程的我在各种搜索后发现 getLocalImgData
这个方法不能直接遍历,如果遍历执行他就只执行一次后面就不执行了,于是改为递归调用的方式,改进如下:
// 可以正常选择多张的版本
const chooseImg = params => {
return new Promise((resolve, reject) => {
wx.chooseImage({
...params,
success: res => {
const { localIds } = res
const imgList = [] // 用来存储图片base64
const getResults = ids => {
const localId = localIds.shift()
wx.getLocalImgData({
localId,
success: response => {
const { localData } = response
imgList.push(localData)
if (ids.length > 0) {
getResults(ids)
} else {
// 递归结束
return resolve(imgList)
}
}
})
}
getResults(localIds)
},
fail: error => {
return reject(error)
},
})
})
}
移动端输入框 maxLength
遇到 emoji
的问题
在移动端使用 input
输入框或者 textarea
文本域的时候,当限制了 maxLength
的时候,在安卓上和 iOS 上表现不同,不同的 emoji
表情的 length 有 2、3、4 等不同的值,但是在 iOS 上 emoji
的长度却被算做了1,比如限制了 maxLength
为5,在安卓上输入两个表情后就无法输入了,而在 iOS 上却可以输入5个 emoji
,这个时候在输入框下面添加字数展示的时候,value.length
显示的可能是10或者更大的值,已经超出了我们限制的 maxLength
, 因此这里要做一下统一处理,使用 js 来限制字数而不能用 maxLength
来限制,这里搜索了一下,网上大部分用到的方法都是监听输入,处理emoji,然后使用 value.slice(0, maxLength)
来处理的,这里有一个弊端,就是当用户输入已经达到最大限制的时候,把光标移到输入的文本中间,继续输入,会在光标处插入新输入的内容,尾部原来的内容就被截没了,而限制了 maxLength
的输入框在这种情况下是不能输入任何内容的,也不会修改原来的内容,这显然与预期不符合,因此不能简单的使用 slice
截取。
这里先说一下含有 emoji
的字符串截取问题:上面说到 emoji
的length并不是1,因此当使用字符串的 slice
方法是就有可能遇到一个 emoji
表情被截一半的情况,这种情况下就会出现 �
这种方块问号的乱码,比如:
const emojiStr = '123?'
console.log(emojiStr.length) // 5
emojiStr.slice(3, 4) // '�'
使用 string.split('')
方法将含有 emoji
的字符串转为数组的时候,emoji
表情都会被截成一个个的�
,不过使用 Array.from(string)
方法可以将字符中的 emoji
保留而不被截断,如下:
const emojiStr = '123?'
emojiStr.split('') // ["1", "2", "3", "�", "�"]
Array.from(emojiStr) // ["1", "2", "3", "?"]
因此可以借助该方法处理含有 emoji
的字符串截取,方法如下:
const sliceEmoji = (str, start, end) => {
if (typeof str !== 'string' || start < 0 || end < 0 || start > end) {
throw '参数非法'
}
const strArr = Array.from(s).slice(start, end)
const slicedStr = s.slice(start, end)
// 取两个字符串前面相同的一部分
let result = ''
if (strArr.join('') === slicedStr) {
result = slicedStr
return result
}
for (let i = 0; i < strArr.length; i++) {
if (strArr[i] !== slicedStr[i]) {
result = strArr.slice(0, i).join('')
break
}
}
return result
}
有了上面的认识,我们就可以按照以下方式来处理限制 maxLength
的输入框:
const Textarea = () => {
const [selection, setSelection] = useState({ start: 0, end: 0 }) // 用来记录光标位置
const [value, setValue] = useState('') // 输入框值
const textareaRef = useRef(null)
const MAX_LENGTH = 20, // 这是根据需要设置的最大可输入值
const onSelect = e => {
const { selectionStart: start, selectionEnd: end } = e
const el = textareaRef.current
setSelection({ start, end })
}
const onChange = e => {
const { start, end } = selection
const val = e.target.value
const length = value.length
if (length <= MAX_LENGTH) {
// 输入内容未达到限制长度
setValue(val)
} else {
// 输入内容超出限制
const delta = length - val.length // 输入内容与现有内容的差值
const before = val.slice(0, start) // 光标之前的内容
const after = val.slice(end + delta) // 光标之后的内容
const diff = val.slice(start, delta) // 新输入的内容
const validVal = sliceEmoji(diff, 0, MAX_LENGTH - value.length) // 可输入的内容
const finallyVal = before + validVal + after // 输入框内最终的内容
setValue(finallyVal)
}
}
return <textarea ref={textareaRef} value={value} onSelect={onSelect} onChange={onChange} />
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!