小程用户收取登录后res.net user 远程登录Info里面有些什么字段

&h2&&b&微信小程序后台开发&/b&&/h2&&p&&b&前言&/b&&/p&&ul&&li&微信小程序已经是家喻户晓了,最近和同学一起刚上线了一款应用&b&校园懒人邦&/b&,感兴趣的朋友可以搜索一下,一款基于校园最后一百米的概念开发的快递&外卖配送平台,我是负责后台开发部分,这里给朋友们介绍下相关开发经验,开发框架和方式有很多,这里给大家介绍一些快捷高效的方法,大家少走弯路!&/li&&/ul&&p&&b&开发环境&/b&&/p&&ul&&li&macOs 10.13.2&/li&&li&PyCharm 16.1&/li&&li&Python 2.7&/li&&li&vim 8.0&/li&&/ul&&p&&b&开发流程&/b&&/p&&p&&b&项目整体结构&/b&&/p&&ul&&li&我们先来看下项目整体结构:&/li&&/ul&&p&&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-ddcce74914dd_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&336& data-rawheight=&538& class=&content_image& width=&336&&&/figure&&p&&br&&/p&&ul&&li&&br&&/li&&li&pjt: 整个项目的代码文件&/li&&li&api: 项目的所有接口信息&/li&&li&conf: 项目所有的配置信息&/li&&li&dal/dao: 数据维护层,所有的操作数据库的逻辑都要经过这里&/li&&li&db: 第一版使用的自己设计的orm框架,第二版废弃了&/li&&li&images: 图片缓存区,缓存存二维码还有用户头像上传七牛云&/li&&li&impl: 接口功能实现&/li&&li&key: 各种加密文件,微信支付,ssl证书等&/li&&li&log: 日志文件,目前天级别生成最新的一份&/li&&li&model: 数据模型, 第一版已废弃&/li&&li&pjt_data: 第二版使用Django QuerySet的orm框架,比自己写的方便多了大力推荐,具体学习可以查看这里,内部代码就不当做demo展示了,哈哈!&/li&&li&test_demo: 写一些功能的测试类,方便线上调试&/li&&/ul&&p&&b&接口开发&/b&&/p&&ul&&li&整个接口开发使用的是Flask框架,Flask是Python编写的轻量级Web应用框架,用过的人都知道简单快捷, 马上花一分钟上手下:&/li&&li&安装: sudo pip install flask&/li&&li&一个简单的&b&demo&/b&:&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&from flask import Flaskapp = Flask(__name__)@app.route(&/&)def hi(): return &hi!&if __name__ == &__main__&: app.run()
&/code&&/pre&&/div&&ul&&li&运行程序,浏览器输入, 可以看到页面返回了一个hi!,ok,这就可以了, 整个框架我们就搭建起来了,是不是很简单,后面我们来拓展一下!&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http://127.0.0.1:5000/1
&/code&&/pre&&/div&&ul&&li&详细的flask教程可以参考这里&/li&&/ul&&p&&b&项目部署&/b&&/p&&p&&b&ip映射&/b&&/p&&ul&&li&个人选择的服务器是阿里云,性能不错,这里假设咱们的阿里云的Ip是1.2.3.4,那么首先我们要在服务器上建立映射关系,当我访问&a href=&http://link.zhihu.com/?target=http%3A//1.2.3.4%3A5000/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&1.2.3.4:5000/&/span&&span class=&invisible&&&/span&&/a&时,不仅是本地能访问,任何地方都行,这个很简单,只要你买了服务器,里面有一项配置你服务器的ip地址就行。&/li&&/ul&&p&&b&Nginx反向代理&/b&&/p&&ul&&li&现在我们可以使用&a href=&http://link.zhihu.com/?target=http%3A//1.2.3.4%3A5000/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&1.2.3.4:5000/&/span&&span class=&invisible&&&/span&&/a&访问网站了,还缺少一些什么?当然,微信小程序是不允许接口暴露ip地址的,你必须要用自己的域名和自定义的接口名称,比如&a href=&http://link.zhihu.com/?target=http%3A//www.happypower.com/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&happypower.com/&/span&&span class=&invisible&&&/span&&/a&这样才行,当我访问这个网站时候,会先到阿里云服务器上做一下映射,确定我们的ip1.2.3.4,然后找到该Ip对应的服务器,之后怎么办? 我需要访问的是&a href=&http://link.zhihu.com/?target=http%3A//1.2.3.4%3A5000/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&1.2.3.4:5000/&/span&&span class=&invisible&&&/span&&/a&这个啊,Nginx帮我们做了这个事,下面简单介绍下:&/li&&li&安装:具体看这里&/li&&li&安装完了之后咱们先配置下,详细配置可以参考这里,这里我简单说下我的配置,并给出具体demo:&/li&&li&进入目录/etc/nginx,每人安装目录可能不同,找到nginx.conf文件,vim进行编辑,vim的操作如果不熟可以查看这里,不行可以先本地测好再到线上用,下面给出我配置的信息,大家可以仿造,应该是比较精简的:&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&# For more information on configuration, see:# * Official English Documentation: http://nginx.org/en/docs/# * Official Russian Documentation: http://nginx.org/ru/docs/worker_error_log /var/log/nginx/error.pid /run/nginx.# Load dynamic modules. See /usr/share/nginx/README.dynamic.include /usr/share/nginx/modules/*.events { worker_connections 1024;}http { log_format main '$remote_addr - $remote_user [$time_local] &$request& ' '$status $body_bytes_sent &$http_referer& ' '&$http_user_agent& &$http_x_forwarded_for&'; access_log /var/log/nginx/access. tcp_ tcp_ keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime. default_type application/octet- # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. #include /etc/nginx/conf.d/*. server { listen 80 default_ listen [::]:80 default_ server_name _; root /usr/share/nginx/ # Load configuration files for the default server block. include /etc/nginx/default.d/*. location / { return 404; } error_page 404 /404. location = /40x.html { } error_page 500 502 503 504 /50x. location = /50x.html { } }# Settings for a TLS enabled server.## server {# listen 443 ssl http2 default_# listen [::]:443 ssl http2 default_# server_name _;# root /usr/share/nginx/## ssl_certificate &/etc/pki/nginx/server.crt&;# ssl_certificate_key &/etc/pki/nginx/private/server.key&;# ssl_session_cache shared:SSL:1m;# ssl_session_timeout 10m;# ssl_ciphers HIGH:!aNULL:!MD5;# ssl_prefer_server_## # Load configuration files for the default server block.# include /etc/nginx/default.d/*.## location / {# }## error_page 404 /404.# location = /40x.html {# }## error_page 500 502 503 504 /50x.# location = /50x.html {# }# }# 自己主要要写的就下面这个serverserver { # 443是https监听的端口,一般默认就好 listen 443; # 自己接口的主域名,这就是当我们访问http://www.happypower.com时就会映射到这里 server_name www.happypower. # 这是默认主页,接口没有主页就注释掉了 # # index index.html index. # 下面是阿里云下载的ssl证书,后面会说到ssl的配置,看不懂没关系 ssl_certificate ./xxx. ssl_certificate_key ./xxx. ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ location / { # 这里的意思是http://www.happypower.com会和http://0.0.0.0:8888做一个映射,这就解决了上面最开始说的那个问题了,这一步很关键! proxy_pass http://0.0.0.0:8888; } # 日志文件路径,有人访问你的网站时都会留下印记,有的话最好,不配置其实也行。 access_log /root/xxx/pjt/log/nginx.}}113114
&/code&&/pre&&/div&&ul&&li&之后咱们启动nginx,就能通过&a href=&http://link.zhihu.com/?target=http%3A//www.happypower.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&happypower.com&/span&&span class=&invisible&&&/span&&/a&进行访问了,成功的话会返回&b&hi!&/b&&/li&&/ul&&p&&b&gunicorn+super多进程开启服务+进程监控&/b&&/p&&ul&&li&这两个很简单,其实就是几条命令的事,主要说下作用:&/li&&li&1 gunicorn 可以让你的后台服务多进程方式开启,经过测试可以提升一定的qps(每秒的请求数),简单来说一定程度上防止你的服务器崩掉.&/li&&li&2 supervisor 的作用就是对你的进程进行监控,该框架提供了一个可视化界面,可以通过这个界面去开启,暂停和关闭你的服务进程,即使不动代码的人也能控制后台服务。&/li&&li&3 具体配置没啥好讲的,学会几个命令就行具体推荐看这里&/li&&/ul&&p&&b&ssl证书&/b&&/p&&p&由于小程序需要的接口都是需要https的连接,所以咱们还需要ssl证书才行,这里我使用的是阿里云服务器,具体配置可以先参考这里,可能有点难懂,下面简单说下我的配置:&/p&&ol&&li&我选择的是单域名免费型 DV SSL,其实一般的应用来说,单域名足够了。&/li&&li&官网下载ssl证书,一般是&b&xxx.key和xxx.pem&/b&两个文件,上面Nginx反向代理配置就需要用到这个,可以返回上面的nginx.conf文件进行查看!&/li&&li&管理证书,这个就是配置问题,官方文档写的很详细&/li&&li&配置成功后,可以使用&a href=&http://link.zhihu.com/?target=https%3A//127.0.0.1%3A5000/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&127.0.0.1:5000/&/span&&span class=&invisible&&&/span&&/a& 或者&a href=&http://link.zhihu.com/?target=https%3A//www.happypower.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&happypower.com&/span&&span class=&invisible&&&/span&&/a&来访问你的网站,然后返回&b&hi!&/b&,当然,也有可能访问不了,一般就是配置问题,https其实也是在访问http,只是中间多了一个验证的过程,感兴趣的可以到这里学习下http和https的区别。&/li&&li&&b&ps.&/b&建议用什么服务器就用哪里的证书,腾讯云和阿里云推荐,其他的真的难配,个人遇到了很多坑!&/li&&/ol&&p&&b&小程序常用功能&/b&&/p&&p&&b&微信支付&/b&&/p&&p&有些小程序涉及到微信支付的,就比如校园懒人邦,这个其实很头疼,做过就知道,难到不是很难,过程很繁琐,官方文档也很多坑,下面简单讲解下:&/p&&ol&&li&首先一定要注册公司,这个让运营或者产品的同学去做会好一些&/li&&li&一般来说支付都是单向的,也就是使用者对公司付款,如果是公司对使用者付款则需要9个月的申请时间才行,这个比较坑,当然,退款是不需要等的.&/li&&li&具体细节可以先看文档,这里面讲的还是非常清楚的,包括一些接口说明等.&/li&&li&这里我使用python写了个微信预支付的较为通用的类,大家改下参数拿去用就行,官方文档一个个试出来的,简直坑:&/li&&/ol&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class WeiXinPay(object): &&&微信支付,返回回客户端需要参数 &&& def __init__(self, uu_id, open_id, spbill_create_ip, total_fee, out_trade_no): &&& :param total_fee: 订单金额 :param spbill_create_ip: 客户端请求IP地址 &&& self.params = { 'appid': '小程序的appid', 'attach': u'你的应用名称,我这里是(校园懒人邦)', 'body': u'校园懒人邦-代取费', 'mch_id': '商户id,你企业的id', 'nonce_str': '给个随机数,一般md5一下就好,时间戳啊或者别的什么', 'notify_url': 'http://www.happypower.com/result(这个就是通知地址,会异步返回信息给你),你写一个接口接收就行', 'openid': open_id, 'out_trade_no': out_trade_no, 'spbill_create_ip': spbill_create_ip, 'total_fee': str(total_fee), 'trade_type': 'JSAPI' } # 官方给的接口 self.url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' self.error = None def key_value_url(self, value): &&&将将键值对转为 key1=value1&key2=value2 &&& key_az = sorted(value.keys()) pair_array = [] for k in key_az: v = value.get(k, '').strip() v = v.encode('utf8') k = k.encode('utf8') pair_array.append('%s=%s' % (k, v)) tmp = '&'.join(pair_array) return tmp def get_sign(self, params): &&&生成sign &&& stringA = self.key_value_url(params) stringSignTemp = stringA + '&key=' + 'xxx' # APIKEY, API密钥,需要在商户后台设置 sign = (md5(stringSignTemp).hexdigest()).upper() params['sign'] = sign def get_req_xml(self): &&&拼接XML &&& self.get_sign(self.params) xml = &&xml&& for k, v in self.params.items(): v = v.encode('utf8') k = k.encode('utf8') xml += '&' + k + '&' + v + '&/' + k + '&' xml += &&/xml&& print xml return xml def get_prepay_id(self): &&& 请求获取prepay_id &&& xml = self.get_req_xml() headers = {'Content-Type': 'application/xml'} r = requests.post(self.url, data=xml, headers=headers) re_xml = ElementTree.fromstring(r.text.encode('utf8')) xml_status = re_xml.getiterator('result_code')[0].text if xml_status != 'SUCCESS': self.error = u&连接微信出错啦!& logging.error(u&连接微信出错啦!&) return prepay_id = re_xml.getiterator('prepay_id')[0].text self.params['package'] = 'prepay_id=%s' % prepay_id self.params['timestamp'] = str(int(time.time())) def re_finall(self): self.get_prepay_id() if self.error: return sign_again_params = { 'appId': self.params['appid'], 'timeStamp': self.params['timestamp'], 'nonceStr': self.params['nonce_str'], 'package': self.params['package'], 'signType': 'MD5' } self.get_sign(sign_again_params) sign_again_params['paySign'] = sign_again_params['sign'] sign_again_params['total_fee'] = self.params['total_fee'] sign_again_params['notify_url'] = self.params['notify_url'] sign_again_params.pop('appId') sign_again_params.pop('sign') return json.dumps(sign_again_params)5
&/code&&/pre&&/div&&p&5 这里最后返回的参数传给前台就能支付了,前台会拿到你的prepay_id,然后就能按照指定金额支付了。&/p&&p&6 退款的话有些不同,首先也是具体先查看文档,下面我也写了一个较为通用的类,大家觉得文档麻烦直接用也行:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class WeiXinReturn(object): def __init__(self, out_trade_no, total_fee, refund_fee): self.params_mach = { # 申请商户号的appid或商户号绑定的appid 'appid': 'xxx', 'mch_id': 'xxx', 'nonce_str': '随机数', 'out_trade_no': out_trade_no, 'out_refund_no': '微信订单号', 'total_fee': str(total_fee), 'refund_fee': str(refund_fee), 'notify_url': 'http://www.happypower.com/pay_return/result(和支付一样的意思,就是退款的通知地址,自己开发一个接口就好)' } def pay_return(self): stringA = self.key_value_url(self.params_mach) stringSignTemp = stringA + '&key=' + 'xxx' # APIKEY, API密钥,需要在商户后台设置 sign = (md5(stringSignTemp).hexdigest()).upper() self.params_mach['sign'] = sign xml = &&xml&& for k, v in self.params_mach.items(): v = v.encode('utf8') k = k.encode('utf8') xml += '&' + k + '&' + v + '&/' + k + '&' xml += &&/xml&& headers = {'Content-Type': 'application/charset=UTF-8'} # url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers' url = 'https://api.mch.weixin.qq.com/secapi/pay/refund' # 请求中需要带有支付的证书,没有证书是无法申请的 r = requests.post(url, data=xml, headers=headers, cert=('xxx.pem', 'xxx.pem')) result = r.text re_xml = ElementTree.fromstring(r.text.encode('utf8')) xml_status = re_xml.getiterator('result_code')[0].text if xml_status == 'SUCCESS': return True return False def key_value_url(self, value): &&&将将键值对转为 key1=value1&key2=value2 &&& key_az = sorted(value.keys()) pair_array = [] for k in key_az: v = value.get(k, '').strip() v = v.encode('utf8') k = k.encode('utf8') pair_array.append('%s=%s' % (k, v)) tmp = '&'.join(pair_array) return tmp74849
&/code&&/pre&&/div&&p&&b&ps.&/b&这里需要商户证书,具体怎么弄看这里&/p&&p&&b&生成二维码&/b&&/p&&ul&&li&这个功能比较常见了,同样也是先看文档,里面有三种二维码接口,开发阶段建议使用&b&接口B:&/b&&a href=&http://link.zhihu.com/?target=https%3A//api.weixin.qq.com/wxa/getwxacodeunlimit%3Faccess_token%3DACCESS_TOKEN& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&api.weixin.qq.com/wxa/g&/span&&span class=&invisible&&etwxacodeunlimit?access_token=ACCESS_TOKEN&/span&&span class=&ellipsis&&&/span&&/a&, 项目上线了用&b&接口A&/b&会更加灵活,因为可以通过二维码跳转页面且能够携带参数,校园懒人邦中的分享功能就是利用接口A进行开发,主要接口A上线才好测,而接口B开发阶段好测。&/li&&li&二维码是图片,涉及到存储问题,这里推荐七牛云,账号免费有10G的使用空间,很棒的,具体思路就是先到本地做一层缓存,然后本地上传到七牛云,相关代码可以参考下面的,基本已经封装好了:&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&def upload_img(self, local_path, upload_name, bucket_name, ttl=7200): &&& 上传图片到七牛云 :param local_path: 本地文件路径 :param upload_name: 上传文件名 :param bucket_name: 七牛申请的存储空间名称 :param ttl: 过期时间 :return: 返回图片地址 &&& from qiniu import Auth, put_file import re q = Auth(access_key=conf_test.AccessKey, secret_key=conf_test.SecretKey) token = q.upload_token(bucket_name, upload_name, ttl) ret, info = put_file(token, upload_name, local_path) pat_status = 'status_code:(.*?),' status_code = re.compile(pat_status, re.S).findall(str(info)) if len(status_code) & 0: # 成功返回图片外链名称,失败返回原因以及状态码 if int(status_code[0]) == 200: return {&msg&:conf_test.qiniu_domain + upload_name, &status&:1} return {&msg&:&failed upload img failed&, &status&:-1}
&/code&&/pre&&/div&&ul&&li&相关的sdk文档可以参考这里&/li&&li&还有一个问题就是微信小程序只能识别https的图片外链地址,而刚开始上传七牛云生成的是http的链接图片,因此这里需要到七牛云上面配置下,具体的可以查看这里&/li&&li&二维码基本流程就是这么一套,当然缓存怎么做方式很多,这里仅供参考。&/li&&/ul&&p&&b&推送消息&/b&&/p&&ul&&li&给用户发送模板消息,提醒用户做一些事也是经常用到的功能,具体操作还是先查看文档,当然,微信规定了每天用户只能最多收到三条消息,当然还是有别的办法可以给用户多发一些消息,具体往下看.&/li&&li&看文档可以知道,其实只需要有足够的用户form_id咱们就能对用户无限制的发送消息,前提是用户没有屏蔽你的这个微信小程序,可以参考这里,当然这里是用java写的,我按照这个封装了一个python版本的,大家可以参考着用:&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&# 刷新用户form_id 存入redis def fresh_formid(self, **kwargs): &&& 接收前台传来的form_id存入redis :param kwargs: :return: &&& uu_id = kwargs.get(&uu_id&, &&) if not uu_id: return &failed: uu_id cannot be null& open_id = kwargs.get(&open_id&, &&) if not open_id: return &failed: open_id cannot be null& form_id = kwargs.get(&form_id&, &&) if not form_id: return &failed: form_id cannot be null& if &invalid code& in open_id: return &failed: get openid err& msg = {open_id: form_id} self.redis_cli.sadd(uu_id, json.dumps(msg)) return &success& # 发消息模板 def send_template_msg(self, **kwargs): uu_id = kwargs.get(&uu_id&, &&) if not uu_id: return &failed: uu_id cannot be null& data = kwargs.get(&data&, &&) if not data: return &failed: data can not be null& # 这个token可以从文档中去查看怎么得到,内部源码不能提供 token = self.get_refresh_token() if not token: return &failed: cannot find a token& send_msg = {} # 从redis中取出用户对应的form_id redis_msg = self.redis_cli.spop(uu_id) if not redis_msg: return &failed: cannot find a open_id in redis& msg = json.loads(redis_msg.encode(&utf8&)) touser = msg.keys()[0] form_id = msg[touser] template_id = kwargs.get(&template_id&, &&) if not template_id: return &failed: template_id cannot be null& # page是用户点开消息后跳转到的页面 page = kwargs.get(&page&, &&) # 下面这些参数的含义在文档中都能查到 emphasis_keyword = kwargs.get(&emphasis_keyword&, &&) send_msg[&touser&] = touser send_msg[&template_id&] = template_id send_msg[&page&] = page send_msg[&form_id&] = form_id send_msg[&data&] = data send_msg[&emphasis_keyword&] = emphasis_keyword # 调起发送消息接口 api = &https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=%s& % token # post方式发起网络请求 return self.get_html(url=api, data=send_msg)
&/code&&/pre&&/div&&p&&b&测试&/b&&/p&&p&&b&黑盒测试&/b&&/p&&ul&&li&应用上线之前肯定要各种测试才行,功能上黑盒测试可以交给不太懂代码的同学,也就是按照流程走通一遍,中途出现的问题记录下来然后交给程序哥哥。&/li&&/ul&&p&&b&qps测试&/b&&/p&&ul&&li&我这里主要介绍下qps的测试,因为我们是给用户用的,我们必须估计一下我们的服务器能承载多少用户同时在线,也就是平均来说能承受多少用户每秒的请求数量, 我们可以对最常用的几个接口做一个极限测试,然后计算出大致的qps, 最简单的做法就是多线程疯狂的call计算平均时间,这里给出一段测试代码:&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class PressTest(object): def __init__(self): pass def get_html(self, url, headers=conf_test.HEADERS, data=None): if data: data = json.dumps(data) req = urllib2.Request(url=url, headers=headers, data=data) response = urllib2.urlopen(req) html = response.read() return html def get_test(self, api_url): result = self.get_html(url=api_url) if not result: print &url %s result is none& %api_url return return def post_test(self, api_url, **kwargs): import time start_time = time.time() result = self.get_html(url=api_url, data=kwargs) print result if not result: print &url %s result is none& % api_url end_time = time.time() all = end_time - start_time print allif __name__ == '__main__': press_test = PressTest() print &requesting.........& api_url = 'https://xxx接口1' api_url2 = 'https://xxx接口2' start = time.time() times = 400 for i in range(times): t1 = threading.Thread(target=press_test.get_test, args=(api_url,)) t2 = threading.Thread(target=press_test.get_test, args=(api_url2,)) t1.start() t2.start() t1.join() t2.join() end = time.time() ave = (end - start) / 800.0 print &count:%d, start_time :%s, now_time: %s, average_qps: %s& % (800, str(start), str(end), str(ave))74849
&/code&&/pre&&/div&&ul&&li&这里是开了400个线程并发call这两个常用接口,最后计算出qps(每秒的请求数),如果能达到100次/秒,基本上几千人同时在线没啥问题,哈哈&/li&&/ul&
微信小程序后台开发前言微信小程序已经是家喻户晓了,最近和同学一起刚上线了一款应用校园懒人邦,感兴趣的朋友可以搜索一下,一款基于校园最后一百米的概念开发的快递&外卖配送平台,我是负责后台开发部分,这里给朋友们介绍下相关开发经验,开发框架和方式…
&figure&&img src=&https://pic1.zhimg.com/v2-26597f90aebfd3fc3178b_b.jpg& data-rawwidth=&750& data-rawheight=&750& class=&origin_image zh-lightbox-thumb& width=&750& data-original=&https://pic1.zhimg.com/v2-26597f90aebfd3fc3178b_r.jpg&&&/figure&&p&最近因为小程序官方,逐步费劲UserInfo接口的这个决定,让很多微信小程序的开发者,很烦恼,同时我也因为这个上火,公司有重要会议要展示,突然来着这么一招。真的是够了,但是没办法,开始改吧。&/p&&p&于是我,在社区找了很多官方的文档&a href=&http://link.zhihu.com/?target=https%3A//developers.weixin.qq.com/blogdetail%3Faction%3Dget_post_info%26lang%3Dzh_CN%26token%3D%26docid%3D000cbd9c960e71e5b009& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&小程序o小故事(4)--获取用户信息 | 微信公众平台 开发者社区&/a&。 你会发现跟原来的方式,只是改变了userinfo的数据返回。 login的接口拿 res.code,&/p&&p&然后点击登陆按钮的返回值相结合,就可以换取到unionid,来开始业务逻辑了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span& function login(userInfo,callback,reset) {
wx.login({
success: res =& {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
if(res.code){
//存用户信息
wx.setStorageSync('UserInFo', userInfo)
// 扫面二维码进入
let Scene = wx.getStorageSync('scene')
let userInfoStr = JSON.stringify(userInfo);
wx.request({
url: api.WxUnionId,
appid: 'xxx',//测试
secret: 'xxx',
js_code: res.code,
grant_type: 'authorization_code',
userInfo: userInfoStr,
scene:Scene//这个是我们的业务逻辑的参数 忽略
method: 'POST',
'content-type': 'application/x-www-form-urlencoded'
success: callback
&/code&&/pre&&/div&&p&封装好这个,就可以开始用button 来开始弹授权的吐司了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&button class =“login-btn”open-type =“getUserInfo”bindgetuserinfo =“hand
leUserInfo”&
&/ button&
// 然后打印events 你就可以加密数据 数据 调接口
后端解析 换 unionId
handleUserInfo(e) {
let msg = e.detail.errM
if(msg === 'getUserInfo:ok'){
let user = e.detail.userI
user.encryptedData = e.detail.encryptedD
user.iv = e.detail.
utils.setDataBase('islogin',true);//set 本地数据
utils.login(user,this.handleLogin.bind(this))
this.setData({
islogin:false
utils.setDataBase('islogin',false);//set 本地数据
this.setData({
islogin:true
utils.showModal('五色糖,申请获得您的公开信息(头像,昵称等)。授权后,您能体验到我们更完善的功能,谢谢您关注五色糖。',false,this);
在handleLogin
里面 拿到后端的res
就可以开始业务逻辑了。涉及到业务逻辑 就不贴代码了。
&/code&&/pre&&/div&&p&因为我当时,是一个人写电商商城,那会mpvue 没有出 wepy 公司没有留学习的时间,以上的代码全是原生的写法,但是改这个最重要的,就是别烦,其实你慢慢捋顺了就好了。&/p&&p&给大家上个效果吧:&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/826560& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-ad7cb2e17e_b.jpg& data-lens-id=&826560&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-ad7cb2e17e_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/826560&/span&
&p&登陆页面比较丑,UI 还没有出图,就不要在意这些细节了。&/p&&p&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&送给写小程序的你。&/a& 我还总结了一些我写小程序的笔记,写的有不好的地方,还请大神指点。&/p&&h2&&b&都看到这了,点个赞可好。&/b&&/h2&
最近因为小程序官方,逐步费劲UserInfo接口的这个决定,让很多微信小程序的开发者,很烦恼,同时我也因为这个上火,公司有重要会议要展示,突然来着这么一招。真的是够了,但是没办法,开始改吧。于是我,在社区找了很多官方的文档
&figure&&img src=&https://pic4.zhimg.com/v2-a7db1a8fb5366b30cac13b_b.jpg& data-rawwidth=&600& data-rawheight=&334& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic4.zhimg.com/v2-a7db1a8fb5366b30cac13b_r.jpg&&&/figure&&p&&b&零、问题的由来&/b&&/p&&p&&br&&/p&&p&一般在前端展示图片时都会碰到这两个常见的需求:&/p&&ol&&li&图片未加载完成时先展示占位图,等到图片加载完毕后再展示实际的图片。&/li&&li&假如图片链接有问题(比如 404),依然展示占位图。甚至你还可以增加点击图片再次加载的功能。(例如知乎)&/li&&/ol&&p&然鹅,小程序原生组件 image 并没有提供这些常用功能...&/p&&figure&&img src=&https://pic2.zhimg.com/v2-85c26ab98bf52263bcdd6a_b.jpg& data-size=&normal& data-rawwidth=&434& data-rawheight=&683& data-thumbnail=&https://pic2.zhimg.com/v2-85c26ab98bf52263bcdd6a_b.jpg& class=&origin_image zh-lightbox-thumb& width=&434& data-original=&https://pic2.zhimg.com/v2-85c26ab98bf52263bcdd6a_r.jpg&&&figcaption&注:这里加了 2s 的延迟&/figcaption&&/figure&&h2&&b&一、常规操作&/b&&/h2&&p&在小程序没还没推出自定义组件功能时,只能通过改变 Page 中的 data 来展示兜底的占位图,所以当时的处理方式十分蛋疼...&/p&&h2&&b&1.1.相同默认图&/b&&/h2&&p&由于需要知道这个图片的数据路径,所以不得不在每个 &code&image&/code& 上加上类似 &code&data-img-path&/code& 的东西。&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&view&/span&
&span class=&na&&wx:for&/span&&span class=&o&&=&/span&&span class=&s&&&{{ obj.arr }}&&/span&
&span class=&na&&wx:key&/span&&span class=&o&&=&/span&&span class=&s&&&imgSrc&&/span&
&span class=&na&&wx:for-item&/span&&span class=&o&&=&/span&&span class=&s&&&item&&/span&
&span class=&na&&wx:for-index&/span&&span class=&o&&=&/span&&span class=&s&&&itemIdx&&/span&
&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&{{ item.imgSrc }}&&/span&
&span class=&na&&binderror&/span&&span class=&o&&=&/span&&span class=&s&&&onImageError&&/span&
&span class=&na&&data-img-path&/span&&span class=&o&&=&/span&&span class=&s&&&obj.arr[{{ itemIdx }}].imgSrc&&/span&
&span class=&p&&/&&/span&
&span class=&p&&&/&/span&&span class=&nt&&view&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kr&&const&/span& &span class=&nx&&DEFAULT_IMG&/span& &span class=&o&&=&/span& &span class=&s1&&'/assets/your_default_img'&/span&
&span class=&nx&&Page&/span&&span class=&p&&({&/span&
&span class=&nx&&data&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&obj&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&arr&/span&&span class=&o&&:&/span& &span class=&p&&[&/span&
&span class=&p&&{&/span& &span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&s1&&'your_img1'&/span& &span class=&p&&},&/span&
&span class=&p&&{&/span& &span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&s1&&'your_img2'&/span& &span class=&p&&},&/span&
&span class=&p&&],&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&nx&&onImageError&/span& &span class=&p&&({&/span&
&span class=&nx&&target&/span&&span class=&o&&:&/span& &span class=&p&&{&/span& &span class=&nx&&dataset&/span&&span class=&o&&:&/span& &span class=&p&&{&/span& &span class=&nx&&imgPath&/span& &span class=&p&&}&/span& &span class=&p&&},&/span&
&span class=&p&&})&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&setData&/span&&span class=&p&&({&/span&
&span class=&p&&[&/span&&span class=&nx&&imgPath&/span&&span class=&p&&]&/span&&span class=&o&&:&/span& &span class=&nx&&DEFAULT_IMG&/span&&span class=&p&&,&/span&
&span class=&p&&})&/span&
&span class=&p&&},&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&&h2&&b&1.2.不同默认图&/b&&/h2&&p&如果默认图片不同呢?例如球员、球队和 feed 的默认图片一般都是不同的。&/p&&p&那么一般只好再增加一个属性例如 &code&data-img-type&/code& 来标识默认图的类型。&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&c&&&!-- 球队图 --&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&err&&...&/span&
&span class=&na&&data-img-type&/span&&span class=&o&&=&/span&&span class=&s&&&team&&/span&
&span class=&p&&/&&/span&
&span class=&c&&&!-- 球员图 --&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&err&&...&/span&
&span class=&na&&data-img-type&/span&&span class=&o&&=&/span&&span class=&s&&&player&&/span&
&span class=&p&&/&&/span&
&/code&&/pre&&/div&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kr&&const&/span& &span class=&nx&&DEFAULT_IMG_MAP&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&feed&/span&&span class=&o&&:&/span& &span class=&s1&&'/assets/default_feed'&/span&&span class=&p&&,&/span&
&span class=&nx&&team&/span&&span class=&o&&:&/span& &span class=&s1&&'/assets/default_team'&/span&&span class=&p&&,&/span&
&span class=&nx&&player&/span&&span class=&o&&:&/span& &span class=&s1&&'/assets/default_player'&/span&&span class=&p&&,&/span&
&span class=&p&&}&/span&
&span class=&nx&&Page&/span&&span class=&p&&({&/span&
&span class=&nx&&data&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&obj&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&arr&/span&&span class=&o&&:&/span& &span class=&p&&[&/span&
&span class=&p&&{&/span& &span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&s1&&'your_img1'&/span& &span class=&p&&},&/span&
&span class=&p&&{&/span& &span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&s1&&'your_img2'&/span& &span class=&p&&},&/span&
&span class=&p&&],&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&nx&&onImageError&/span& &span class=&p&&({&/span&
&span class=&nx&&target&/span&&span class=&o&&:&/span& &span class=&p&&{&/span& &span class=&nx&&dataset&/span&&span class=&o&&:&/span& &span class=&p&&{&/span& &span class=&nx&&imgPath&/span&&span class=&p&&,&/span& &span class=&nx&&imgType&/span& &span class=&p&&}&/span& &span class=&p&&},&/span&
&span class=&p&&})&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&setData&/span&&span class=&p&&({&/span&
&span class=&p&&[&/span&&span class=&nx&&imgPath&/span&&span class=&p&&]&/span&&span class=&o&&:&/span& &span class=&nx&&DEFAULT_IMG_MAP&/span&&span class=&p&&[&/span&&span class=&nx&&imgType&/span&&span class=&p&&],&/span&
&span class=&p&&})&/span&
&span class=&p&&},&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&&h2&&b&1.3.图片在模板中&/b&&/h2&&p&页面层级浅倒还好,如果跨模板了,那么模板就可能要用一个类似于 &code&pathPrefix&/code& 的属性来传递模板数据的路径前缀。&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&c&&&!--&/span&
&span class=&c&&
球员排行模板&/span&
&span class=&c&&
pathPrefix: String&/span&
&span class=&c&&
playerList: Array&/span&
&span class=&c&&
...&/span&
&span class=&c&&--&&/span&
&span class=&p&&&&/span&&span class=&nt&&template&/span& &span class=&na&&name&/span&&span class=&o&&=&/span&&span class=&s&&&srPlayerRank&&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&view&/span&
&span class=&na&&wx:for&/span&&span class=&o&&=&/span&&span class=&s&&&{{ playerList }}&&/span&
&span class=&na&&wx:key&/span&&span class=&o&&=&/span&&span class=&s&&&imgSrc&&/span&
&span class=&na&&wx:for-item&/span&&span class=&o&&=&/span&&span class=&s&&&item&&/span&
&span class=&na&&wx:for-index&/span&&span class=&o&&=&/span&&span class=&s&&&itemIdx&&/span&
&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&{{ item.imgSrc }}&&/span&
&span class=&na&&binderror&/span&&span class=&o&&=&/span&&span class=&s&&&onImageError&&/span&
&span class=&na&&data-img-type&/span&&span class=&o&&=&/span&&span class=&s&&&player&&/span&
&span class=&na&&data-img-path&/span&&span class=&o&&=&/span&&span class=&s&&&{{ pathPrefix }}.playerList[{{ itemIdx }}].imgSrc&&/span&
&span class=&p&&/&&/span&
&span class=&p&&&/&/span&&span class=&nt&&view&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&template&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&最后在失败回调里调用 &code&setData({ [path]: DEFAULT_IMG })&/code& 重新设置图片地址。&/p&&p&就问你蛋不蛋疼?这一坨 &code&data-img-path=&{{ pathPrefix }}.playerList[{{ itemIdx }}].imgSrc&&/code& 代码真让人无发可脱...&/p&&figure&&img src=&https://pic4.zhimg.com/v2-aa25b0b827da7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&600& data-rawheight=&334& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic4.zhimg.com/v2-aa25b0b827da7_r.jpg&&&/figure&&h2&&b&二、自定义组件&/b&&/h2&&p&有了自定义组件后,用领袖【窃·格瓦拉】的话来说的话就是:“感觉好 door 了~”&/p&&figure&&img src=&https://pic2.zhimg.com/v2-b9a8da5bdc0d76df45635_b.jpg& data-caption=&& data-size=&small& data-rawwidth=&640& data-rawheight=&640& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-b9a8da5bdc0d76df45635_r.jpg&&&/figure&&h2&&b&2.1.原生自定义组件&/b&&/h2&&p&原生写法一般要写4个文件:&code&.json&/code&/&code&.wxml&/code&/&code&.js&/code&/&code&.wxss&/code& &/p&&ul&&li&TuaImage.json&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-json&&&span&&/span&&span class=&p&&{&/span&
&span class=&nt&&&component&&/span&&span class=&p&&:&/span& &span class=&kc&&true&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&ul&&li&TuaImage.wxml&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&c&&&!-- 加载中的图片 --&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&na&&hidden&/span&&span class=&o&&=&/span&&span class=&s&&&{{ !isLoading }}&&/span&
&span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&{{ errSrc }}&&/span&
&span class=&na&&style&/span&&span class=&o&&=&/span&&span class=&s&&&width: {{ width }}; height: {{ height }}; {{ styleStr }}&&/span&
&span class=&na&&mode&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgMode }}&&/span&
&span class=&p&&/&&/span&
&span class=&c&&&!-- 实际加载的图片 --&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&na&&hidden&/span&&span class=&o&&=&/span&&span class=&s&&&{{ isLoading }}&&/span&
&span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgSrc || src }}&&/span&
&span class=&na&&mode&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgMode }}&&/span&
&span class=&na&&style&/span&&span class=&o&&=&/span&&span class=&s&&&width: {{ width }}; height: {{ height }}; {{ styleStr }}&&/span&
&span class=&na&&bindload&/span&&span class=&o&&=&/span&&span class=&s&&&_onImageLoad&&/span&
&span class=&na&&binderror&/span&&span class=&o&&=&/span&&span class=&s&&&_onImageError&&/span&
&span class=&na&&lazy-load&/span&&span class=&o&&=&/span&&span class=&s&&&{{ true }}&&/span&
&span class=&p&&/&&/span&
&/code&&/pre&&/div&&ul&&li&TuaImage.js&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kr&&const&/span& &span class=&nx&&DEFAULT_IMG&/span& &span class=&o&&=&/span& &span class=&s1&&'/assets/your_default_img'&/span&
&span class=&nx&&Component&/span&&span class=&p&&({&/span&
&span class=&nx&&properties&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&c1&&// 图片地址&/span&
&span class=&nx&&src&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&c1&&// 图片加载中,以及加载失败后的默认地址&/span&
&span class=&nx&&errSrc&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&c1&&// 默认是球队图标&/span&
&span class=&nx&&value&/span&&span class=&o&&:&/span& &span class=&nx&&DEFAULT_IMG&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&nx&&width&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&nx&&value&/span&&span class=&o&&:&/span& &span class=&s1&&'48rpx'&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&nx&&height&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&nx&&value&/span&&span class=&o&&:&/span& &span class=&s1&&'48rpx'&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&c1&&// 样式字符串&/span&
&span class=&nx&&styleStr&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&nx&&value&/span&&span class=&o&&:&/span& &span class=&s1&&''&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&c1&&// 图片裁剪、缩放的模式(详见文档)&/span&
&span class=&nx&&imgMode&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&nx&&value&/span&&span class=&o&&:&/span& &span class=&s1&&'scaleToFill'&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&nx&&data&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&s1&&''&/span&&span class=&p&&,&/span&
&span class=&nx&&isLoading&/span&&span class=&o&&:&/span& &span class=&kc&&true&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&nx&&methods&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&c1&&// 加载图片出错&/span&
&span class=&nx&&_onImageError&/span& &span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&setData&/span&&span class=&p&&({&/span&
&span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&data&/span&&span class=&p&&.&/span&&span class=&nx&&errSrc&/span&&span class=&p&&,&/span&
&span class=&p&&})&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&triggerEvent&/span&&span class=&p&&(&/span&&span class=&s1&&'onImageError'&/span&&span class=&p&&,&/span& &span class=&nx&&e&/span&&span class=&p&&)&/span&
&span class=&p&&},&/span&
&span class=&c1&&// 加载图片完毕&/span&
&span class=&nx&&_onImageLoad&/span& &span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&setData&/span&&span class=&p&&({&/span& &span class=&nx&&isLoading&/span&&span class=&o&&:&/span& &span class=&kc&&false&/span& &span class=&p&&})&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&triggerEvent&/span&&span class=&p&&(&/span&&span class=&s1&&'onImageLoad'&/span&&span class=&p&&,&/span& &span class=&nx&&e&/span&&span class=&p&&)&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&p&&})&/span&
&/code&&/pre&&/div&&p&布吉岛大家使用原生写法时有木有一些感觉不方便的地方:&/p&&ul&&li&4个文件:&code&.json&/code&/&code&.wxml&/code&/&code&.js&/code&/&code&.wxss&/code&,这样老需要切来切去的降低效率&/li&&li&&code&properties&/code& 是什么鬼?大家(React/Vue)一般不都用 &code&props&/code& 么?&/li&&li&&code&style=&width: {{ width }}; height: {{ height }}; {{ styleStr }}&&/code& 样式字符串怎么辣么长...&/li&&/ul&&h2&&b&2.2.TuaImage.vue&/b&&/h2&&p&所以以下是一个使用单文件组件封装原生 image 组件的例子。&/p&&ul&&li&使用单文件组件将配置、模板、脚本、样式写在一个文件中,方便维护。&/li&&li&使用计算属性 &code&computed&/code& 将样式字符串写在 js 中。&/li&&li&使用 &code&this.imgSrc = this.errSrc&/code& 而不是 &code&this.setData&/code& 来改变 &code&data&/code&。&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&config&/span&&span class=&p&&&&/span&
&component&: true
&span class=&p&&&/&/span&&span class=&nt&&config&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&template&/span& &span class=&na&&lang&/span&&span class=&o&&=&/span&&span class=&s&&&wxml&&/span&&span class=&p&&&&/span&
&span class=&c&&&!-- 加载中的图片 --&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&na&&hidden&/span&&span class=&o&&=&/span&&span class=&s&&&{{ !isLoading }}&&/span&
&span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&{{ errSrc }}&&/span&
&span class=&na&&style&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgStyleStr }}&&/span&
&span class=&na&&mode&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgMode }}&&/span&
&span class=&p&&/&&/span&
&span class=&c&&&!-- 实际加载的图片 --&&/span&
&span class=&p&&&&/span&&span class=&nt&&image&/span&
&span class=&na&&hidden&/span&&span class=&o&&=&/span&&span class=&s&&&{{ isLoading }}&&/span&
&span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgSrc || src }}&&/span&
&span class=&na&&mode&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgMode }}&&/span&
&span class=&na&&style&/span&&span class=&o&&=&/span&&span class=&s&&&{{ imgStyleStr }}&&/span&
&span class=&na&&bindload&/span&&span class=&o&&=&/span&&span class=&s&&&_onImageLoad&&/span&
&span class=&na&&binderror&/span&&span class=&o&&=&/span&&span class=&s&&&_onImageError&&/span&
&span class=&na&&lazy-load&/span&&span class=&o&&=&/span&&span class=&s&&&{{ true }}&&/span&
&span class=&p&&/&&/span&
&span class=&p&&&/&/span&&span class=&nt&&template&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&script&/span&&span class=&p&&&&/span&
&span class=&cm&&/**&/span&
&span class=&cm&& * 图片组件,能够传递备用图片以防图片失效&/span&
&span class=&cm&& * https://developers.weixin.qq.com/miniprogram/dev/component/image.html&/span&
&span class=&cm&& */&/span&
&span class=&c1&&// 也可以设置为网络图片如: https://foo/bar.png&/span&
&span class=&kr&&const&/span& &span class=&nx&&DEFAULT_IMG&/span& &span class=&o&&=&/span& &span class=&s1&&'/assets/your_default_img'&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&p&&{&/span&
&span class=&nx&&props&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&c1&&// 图片地址&/span&
&span class=&nx&&src&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&c1&&// 图片加载中,以及加载失败后的默认地址&/span&
&span class=&nx&&errSrc&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&c1&&// 默认是球队图标&/span&
&span class=&k&&default&/span&&span class=&o&&:&/span& &span class=&nx&&DEFAULT_IMG&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&nx&&width&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&k&&default&/span&&span class=&o&&:&/span& &span class=&s1&&'48rpx'&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&nx&&height&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&k&&default&/span&&span class=&o&&:&/span& &span class=&s1&&'48rpx'&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&c1&&// 样式字符串&/span&
&span class=&nx&&styleStr&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&k&&default&/span&&span class=&o&&:&/span& &span class=&s1&&''&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&c1&&// 图片裁剪、缩放的模式(详见文档)&/span&
&span class=&nx&&imgMode&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&nx&&type&/span&&span class=&o&&:&/span& &span class=&nb&&String&/span&&span class=&p&&,&/span&
&span class=&k&&default&/span&&span class=&o&&:&/span& &span class=&s1&&'scaleToFill'&/span&&span class=&p&&,&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&nx&&data&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&p&&{&/span&
&span class=&nx&&imgSrc&/span&&span class=&o&&:&/span& &span class=&s1&&''&/span&&span class=&p&&,&/span&
&span class=&nx&&isLoading&/span&&span class=&o&&:&/span& &span class=&kc&&true&/span&&span class=&p&&,&/span&
&span class=&p&&}&/span&
&span class=&p&&},&/span&
&span class=&nx&&computed&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&c1&&// 样式字符串&/span&
&span class=&nx&&imgStyleStr&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&sb&&`width: &/span&&span class=&si&&${&/span&&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&width&/span&&span class=&si&&}&/span&&span class=&sb&&; height: &/span&&span class=&si&&${&/span&&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&height&/span&&span class=&si&&}&/span&&span class=&sb&&; &/span&&span class=&si&&${&/span&&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&styleStr&/span&&span class=&si&&}&/span&&span class=&sb&&`&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&nx&&methods&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&c1&&// 加载图片出错&/span&
&span class=&nx&&_onImageError&/span& &span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&imgSrc&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&errSrc&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&$emit&/span&&span class=&p&&(&/span&&span class=&s1&&'onImageError'&/span&&span class=&p&&,&/span& &span class=&nx&&e&/span&&span class=&p&&)&/span&
&span class=&p&&},&/span&
&span class=&c1&&// 加载图片完毕&/span&
&span class=&nx&&_onImageLoad&/span& &span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&isLoading&/span& &span class=&o&&=&/span& &span class=&kc&&false&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&$emit&/span&&span class=&p&&(&/span&&span class=&s1&&'onImageLoad'&/span&&span class=&p&&,&/span& &span class=&nx&&e&/span&&span class=&p&&)&/span&
&span class=&p&&},&/span&
&span class=&p&&},&/span&
&span class=&p&&}&/span&
&span class=&p&&&/&/span&&span class=&nt&&script&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&style&/span& &span class=&na&&lang&/span&&span class=&o&&=&/span&&span class=&s&&&scss&&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&采用框架是 tua-mp:&/p&&a href=&http://link.zhihu.com/?target=https%3A//github.com/tuateam/tua-mp& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic1.zhimg.com/v2-89e7d3e6c6c8d6a72fd3f4ebe0524598_ipico.jpg& data-image-width=&202& data-image-height=&202& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&tuateam/tua-mp&/a&&p&相关文章:&/p&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic1.zhimg.com/v2-421f123a47cc_180x120.jpg& data-image-width=&640& data-image-height=&292& class=&internal&&佯真愚:终极蛇皮上帝视角之微信小程序之告别 setData&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/v2-e87b6f00eddf803a87ff_180x120.jpg& data-image-width=&600& data-image-height=&414& class=&internal&&佯真愚:终极蛇皮上帝视角之微信小程序之告别“刀耕火种”&/a&&p&&/p&
零、问题的由来 一般在前端展示图片时都会碰到这两个常见的需求:图片未加载完成时先展示占位图,等到图片加载完毕后再展示实际的图片。假如图片链接有问题(比如 404),依然展示占位图。甚至你还可以增加点击图片再次加载的功能。(例如知乎)然鹅,小程…
&figure&&img src=&https://pic2.zhimg.com/v2-44ad6a2bede_b.jpg& data-rawwidth=&760& data-rawheight=&420& class=&origin_image zh-lightbox-thumb& width=&760& data-original=&https://pic2.zhimg.com/v2-44ad6a2bede_r.jpg&&&/figure&&h2&介绍&/h2&&p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/Meituan-Dianping/mpvue& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Mpvue&/a& 是一个使用 Vue.js 开发小程序的前端框架。框架基于 Vue.js 核心,修改了 Vue.js 的 runtime 和 compiler 实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套 Vue.js 开发体验,同样也使得一套代码同时复用在小程序和 Web 中成为可能。本文将以 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&IT之家Lite&/a& 小程序的 Web 转换经过为线索,大致介绍一下转换的基本步骤及需要注意的一些事项。&/p&&h2&目录结构&/h2&&blockquote&省略了部分与转换无关的文件&/blockquote&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&├─build
├─config
│ ├─components
│ ├─pages
│ ├─store
│ ├─styles
│ ├─utils
├─api.js
├─index.js
├─request.js
│ ├─App.vue
│ └─main.js
├─package-lock.json
└─package.json
&/code&&/pre&&/div&&h2&转换步骤&/h2&&p&&br&&/p&&p&0. 前期准备&/p&&ul&&li&建议使用 git 进行分支管理&/li&&li&尽量避免使用不必要的小程序特有标签,如 text,image 等 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/00cbaab3ddea3b5f1b& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#9137744&/a&&/li&&li&避免直接使用 wx 对象,使用 &code&import wx from 'wx'&/code& 的方式引入,便于 web 中改写,配置 alias 将 wx 指向 &code&src/utils/wx&/code& &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/c3ef6e771dc6e767fed9316fd62bed& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#c3ef6e7&/a&&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// src/utils/wx.js&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&nx&&wx&/span&
&/code&&/pre&&/div&&ul&&li&使用 &a href=&https://link.zhihu.com/?target=https%3A//github.com/wendux/fly& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&flyio&/a& 作为请求库,配置 alias 将 flyio 指向 &code&flyio/dist/npm/wx&/code&&/li&&li&基于原分支新建 web-version 分支&/li&&/ul&&p&&br&&/p&&p&1. 修改打包配置&/p&&ul&&li&可以在原有配置基础上修改,主要涉及 entry、target 及 loader 相关的配置项,这里我直接通过 vue-cli 生成了一个新的项目,复制 build、config 文件夹及 eslint、babel 等的配置文件替换原有配置,使用新项目的 package.json 并做相应修改,新建项目时各选项尽量与原项目保持一致 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/ece3a7ee19e2fa0a4c7ce07cb77227& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#ece3a76&/a&&/li&&li&修改 main.js,指定挂载元素,顺利的话,这步之后执行 &code&npm run dev&/code& 便已经可以跑起来了,有报错的话解决相应错误即可&/li&&/ul&&p&&br&&/p&&p&2. 配置路由&/p&&ul&&li&添加 vue-router,并进行相应配置,建议使用 history 模式 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/ddf94bca7d432ee7ac436f74ed27b4d6bf482e6e& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#ddf94bc&/a&&/li&&li&修改路由参数获取相关的代码 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/b40cfb9dd690fb24d& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#b949197&/a&&/li&&li&使用 router-link 替换 a 标签,避免页面重载 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/ebbb6d903f02bba3c6eb90& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#eb09297&/a&&/li&&/ul&&p&&br&&/p&&p&3. 调整请求接口&/p&&ul&&li&配置 alias 将 flyio 指向 &code&flyio/dist/npm/fly&/code& &/li&&li&小程序中不会有跨域的问题,但 web 中需配合后端进行请求转发或通过其他方式来解决这一问题 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/fb31d05bc7c84bfc55& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#f963975&/a&&/li&&/ul&&p&&br&&/p&&p&4. 转换小程序组件及 API&/p&&ul&&li&底部导航栏,自己布局实现 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/8d6d98bb477cd001c1fe7a77bdf56cb& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#8d6d98b&/a&&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&.nav(v-if=&$route.meta.nav&)
a.nav-item(href=&/pages/news/list&)
img.nav-icon(v-if=&$route.name === 'NewsList'&, src=&/static/assets/news-active.png&)
img.nav-icon(v-else, src=&/static/assets/news.png&)
.nav-title(:class=&{ active: $route.name === 'NewsList' }&) 资讯
a.nav-item(href=&/pages/quanzi/list&)
img.nav-icon(v-if=&$route.name === 'QuanziList'&, src=&/static/assets/quanzi-active.png&)
img.nav-icon(v-else, src=&/static/assets/quanzi.png&)
.nav-title(:class=&{ active: $route.name === 'QuanziList' }&) 圈子
&/code&&/pre&&/div&&p&&br&&/p&&ul&&li&rich-text 组件,使用 v-html 实现 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/bbe62b9a& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#1945f3f&/a&&/li&&li&swiper 组件,使用 &a href=&https://link.zhihu.com/?target=https%3A//github.com/zwhGithub/vue-swiper& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&vue-swiper-component&/a& 实现 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/f4a4e1a094d08ae0e557& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#f4a4e1a&/a&&/li&&li&toast 及 loading 接口,使用 &a href=&https://link.zhihu.com/?target=https%3A//github.com/lin-xin/vue-toast& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&vue2-toast&/a& 实现 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/cb1d9d3f75e9fbf42d2b98f80a51e124de52a38c& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#cb1d9d3&/a&&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// src/utils/wx.js&/span&
&span class=&kr&&import&/span& &span class=&nx&&Vue&/span& &span class=&nx&&from&/span& &span class=&s1&&'vue'&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&p&&{&/span&
&span class=&nx&&showNavigationBarLoading&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&Vue&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&$loading&/span&&span class=&p&&(&/span&&span class=&s1&&'加载中'&/span&&span class=&p&&)&/span&
&span class=&p&&},&/span&
&span class=&nx&&hideNavigationBarLoading&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&Vue&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&$loading&/span&&span class=&p&&.&/span&&span class=&nx&&close&/span&&span class=&p&&()&/span&
&span class=&p&&},&/span&
&span class=&nx&&showToast&/span& &span class=&p&&({&/span& &span class=&nx&&title&/span& &span class=&p&&})&/span& &span class=&p&&{&/span&
&span class=&nx&&Vue&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&&span class=&p&&.&/span&&span class=&nx&&$toast&/span&&span class=&p&&.&/span&&span class=&nx&&center&/span&&span class=&p&&(&/span&&span class=&nx&&title&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&&br&&/p&&ul&&li&下拉刷新及上拉加载,使用 &a href=&https://link.zhihu.com/?target=https%3A//github.com/stackjie/vue-pull-to& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&vue-pull-to&/a& 实现 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/38647dba1f6ba00e2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#38647db&/a&&/li&&/ul&&p&&br&&/p&&p&5. Web 优化&/p&&ul&&li&使用 &a href=&https://link.zhihu.com/?target=https%3A//github.com/jgthms/minireset.css& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&minireset&/a& 重置浏览器默认样式,部分标签在小程序中的默认样式与浏览器不同,也需进行处理 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/e98f5ba8cdced6cbe0c9a14beb22ecd& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#e98f5ba&/a&&/li&&li&引入 &a href=&https://link.zhihu.com/?target=https%3A//github.com/babel/babel/tree/master/packages/babel-polyfill& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&babel-polyfill&/a&,提高兼容性 &a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite/commit/cc00bd68b4c54b2b3& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&#c184166&/a&&/li&&/ul&&h2&维护&/h2&&p&在初步完成 Web 版的转换之后,便可以切换回主分支,后续的 feature 及 bugfix 均在主分支进行,待主分支发版后切换到 web 分支进行一次合并操作,可能会产生少量冲突,但也都会比较容易解决,最后处理下新引入的小程序特性即可,整体而言可维护性还是比较好的&/p&&h2&总结&/h2&&p&整个转换过程还是比较顺利的,主体部分约 1 个多小时完成,相对于小程序,web 的环境更为开放,所以大部分小程序的 api 可以通过各种方式模拟,同时由于是在两个不同的分支进行,也可以放心地使用各种浏览器端的特性,下面是几点开发及转换中的建议:&/p&&ul&&li&下拉刷新及上拉加载尽量以 method 的形式实现,便于 web 中复用&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&nx&&onPullDownRefresh&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&refresh&/span&&span class=&p&&()&/span&
&span class=&p&&},&/span&
&span class=&nx&&onReachBottom&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&loadmore&/span&&span class=&p&&()&/span&
&span class=&p&&},&/span&
&span class=&nx&&methods&/span&&span class=&o&&:&/span& &span class=&p&&{&/span&
&span class=&p&&...&/span&&span class=&nx&&mapActions&/span&&span class=&p&&([&/span&
&span class=&s1&&'getSlides'&/span&&span class=&p&&,&/span&
&span class=&s1&&'getNews'&/span&
&span class=&p&&]),&/span&
&span class=&nx&&async&/span& &span class=&nx&&refresh&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&await&/span& &span class=&nb&&Promise&/span&&span class=&p&&.&/span&&span class=&nx&&all&/span&&span class=&p&&([&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&getNews&/span&&span class=&p&&({&/span& &span class=&nx&&r&/span&&span class=&o&&:&/span& &span class=&mi&&2&/span&&span class=&p&&,&/span& &span class=&nx&&init&/span&&span class=&o&&:&/span& &span class=&kc&&true&/span& &span class=&p&&}),&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&getSlides&/span&&span class=&p&&()&/span&
&span class=&p&&])&/span&
&span class=&nx&&wx&/span&&span class=&p&&.&/span&&span class=&nx&&stopPullDownRefresh&/span&&span class=&p&&()&/span&
&span class=&p&&},&/span&
&span class=&nx&&loadmore&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&kr&&const&/span& &span class=&p&&{&/span& &span class=&nx&&news&/span& &span class=&p&&}&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&
&span class=&kr&&const&/span& &span class=&nx&&lastnews&/span& &span class=&o&&=&/span& &span class=&nx&&news&/span&&span class=&p&&[&/span&&span class=&nx&&news&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&]&/span&
&span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&getNews&/span&&span class=&p&&({&/span& &span class=&nx&&r&/span&&span class=&o&&:&/span& &span class=&nb&&Date&/span&&span class=&p&&.&/span&&span class=&nx&&parse&/span&&span class=&p&&(&/span&&span class=&k&&new&/span& &span class=&nb&&Date&/span&&span class=&p&&(&/span&&span class=&nx&&lastnews&/span&&span class=&p&&.&/span&&span class=&nx&&postdate&/span&&span class=&p&&))&/span& &span class=&p&&})&/span&
&span class=&p&&},&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&ul&&li&样式及脚本尽量不要直接修改原有代码,可通过 mixin、新建 style 标签等方式实现,避免造成冲突&/li&&/ul&&h2&展望&/h2&&ul&&li&双端统一的 UI 库,目前来看只能使用一些简单的 css 类库&/li&&li&更好的路由支持,理想状态下,小程序开发时可以通过 vue-router 的配置文件自动生成各页面的 main.js 文件,并配置 entry,在 .vue 文件中可以直接使用 &code&this.$route&/code& &code&this.$router&/code& 及 &code&router-link&/code& 完成相关操作,避免每次手动修改&/li&&/ul&&h2&附&/h2&&p&Git 仓库:&/p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/Meituan-Dianping/mpvue& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic1.zhimg.com/v2-aa7bef13ad19cd7889be0_ipico.jpg& data-image-width=&100& data-image-height=&100& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Meituan-Dianping/mpvue&/a&&a href=&https://link.zhihu.com/?target=https%3A//github.com/F-loat/ithome-lite& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic3.zhimg.com/v2-b6d710ddfa9401fddb4cea_ipico.jpg& data-image-width=&400& data-image-height=&400& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&F-loat/ithome-lite&/a&&p&Demo:&/p&&a href=&https://link.zhihu.com/?target=http%3A//ithome.youngon.com.cn/& data-draft-node=&block& data-draft-type=&link-card& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ithome-lite&/a&&p&&/p&
介绍 是一个使用 Vue.js 开发小程序的前端框架。框架基于 Vue.js 核心,修改了 Vue.js 的 runtime 和 compiler 实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套 Vue.js 开发体验,同样也使得一套代码同时复用在小程序和 Web 中成为可能…
&figure&&img src=&https://pic4.zhimg.com/v2-a69672bebd210f5b9a7f0_b.jpg& data-rawwidth=&640& data-rawheight=&398& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-a69672bebd210f5b9a7f0_r.jpg&&&/figure&&p&&/p&&a href=&http://link.zhihu.com/?target=https%3A//juejin.im/post/5b15ce94f265da6e& data-draft-node=&block& data-draft-type=&link-card& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&滴滴一夏, 小程序专车来了 - 掘金&/a&&p&-----&/p&&p&最近时常感叹道:时间总是那么的快,转瞬即逝。对于像我这种刚入门的小生来讲,技术每天都在更新,框架也层出不穷,有时候还没弄懂这个知识大牛们又推出了更好的技术。当然学习好的技术也是十分重要的。但是在学习之后怎样才能够得到自己想要的呢,一个好的建议便是静下心来写点自己的东西,哪怕你完成不了也应该尽力去写,老大曾讲过要去实践要去独立思考,你才能掌握很多你看似懂了却又很难处理的知识点。学习小程序的我已经过去了半个月了,前不久滴滴事件颇为轰动,于是便萌发了这篇文章,打开滴滴的小程序。界面做了很大的变化,对于比较怀旧的我来说,还是挺喜欢以前的界面,于是决定打算自己手写一个怀旧版滴滴小程序,接下来我会列举我所遇到的坑和如何解决的方法,希望能够帮到同样在奋斗的你...&/p&&p&## 前言&/p&&p&工欲善其事必先利其器,对于一个前端来说有一个好的工具能够让我们事半功倍。要想做好一个小程序我们首先也应该先选择我们的兵器。&/p&&p&&br&&/p&&p&* 利器1: [微信开发者工具](&a href=&http://link.zhihu.com/?target=https%3A//developers.weixin.qq.com/miniprogram/dev/devtools/download.html%3Ft%3D201822& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&下载 · 小程序&/a&)&/p&&p&* 利器2:
[VsCode](&a href=&http://link.zhihu.com/?target=https%3A//code.visualstudio.com/Download& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Download Visual Studio Code - Mac, Linux, Windows&/a&)集终端一起,方便我们后面Wepy、Mpvue小程序进阶的学习,可谓非常的nice&/p&&p&* 利器3: [Easy Mock](&a href=&http://link.zhihu.com/?target=https%3A//www.easy-mock.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&更简单高效的伪造数据&/a&) 使用这个网站我们可以快速的模拟出我们需要的数据,方便快捷。点这里就可以看到我的数据了&/p&&p&[数据接口](&a href=&http://link.zhihu.com/?target=https%3A//www.easy-mock.com/mock/5adeddd26e970/comments%23%21method%3Dget& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&easy-mock.com/mock/5ade&/span&&span class=&invisible&&ddd26e970/comments#!method=get&/span&&span class=&ellipsis&&&/span&&/a&)&/p&&p&&br&&/p&&p&## 效果图&/p&&p&说了一大堆,等于没说,还是回到正题,先来一波效果图。&/p&&p&[这里有更多的图片等你](&a href=&http://link.zhihu.com/?target=https%3A//github.com/WsmDyj/didi/tree/master/images& class=& wrap external& target=&_b

我要回帖

更多关于 小程序 res 的文章

 

随机推荐