ter = array_value($config, 'filter');
$arr = array_value($filter, $type);
$enable = array_value($arr, 'enable');
$wordarr = array_value($arr, 'keyword');
if (0 == $enable || empty($wordarr)) return FALSE;
foreach ($wordarr as $_keyword) {
if (!$_keyword) continue;
$r = strpos(strtolower($keyword), strtolower($_keyword));
if (FALSE !== $r) {
$error = $_keyword;
return TRUE;
}
}
return FALSE;
}
// return http://domain.com OR https://domain.com
function url_prefix()
{
$http = ((isset($_SERVER['HTTPS']) && 'on' == $_SERVER['HTTPS']) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
return $http . $_SERVER['HTTP_HOST'];
}
// 唯一身份ID
function uniq_id()
{
return uniqid(substr(md5(microtime(true) . mt_rand(1000, 9999)), 8, 8));
}
// 生成订单号 14位
function trade_no()
{
$trade_no = str_replace('.', '', microtime(1));
$strlen = mb_strlen($trade_no, 'UTF-8');
$strlen = 14 - $strlen;
$str = '';
if ($strlen) {
for ($i = 0; $i <= $strlen; $i++) {
if ($i < $strlen) $str .= '0';
}
}
return $trade_no . $str;
}
// 生成订单号 16位
function trade_no_16()
{
$explode = explode(' ', microtime());
$trade_no = $explode[1] . mb_substr($explode[0], 2, 6, 'UTF-8');
return $trade_no;
}
// 当前年的天数
function date_year($time = NULL)
{
$time = intval($time) ? $time : time();
return date('L', $time) + 365;
}
// 当前年份中的第几天
function date_z($time = NULL)
{
$time = intval($time) ? $time : time();
return date('z', $time);
}
// 当前月份中的第几天,没有前导零 1 到 31
function date_j($time = NULL)
{
$time = intval($time) ? $time : time();
return date('j', $time);
}
// 当前月份中的第几天,有前导零的2位数字 01 到 31
function date_d($time = NULL)
{
$time = intval($time) ? $time : time();
return date('d', $time);
}
// 当前时间为星期中的第几天 数字表示 1表示星期一 到 7表示星期天
function date_w_n($time = NULL)
{
$time = intval($time) ? $time : time();
return date('N', $time);
}
// 当前日第几周
function date_d_w($time = NULL)
{
$time = intval($time) ? $time : time();
return date('W', $time);
}
// 当前几月 没有前导零1-12
function date_n($time = NULL)
{
$time = intval($time) ? $time : time();
return date('n', $time);
}
// 当前月的天数
function date_t($time = NULL)
{
$time = intval($time) ? $time : time();
return date('t', $time);
}
// 0 o'clock on the day
function clock_zero()
{
return strtotime(date('Ymd'));
}
// 24 o'clock on the day
function clock_twenty_four()
{
return strtotime(date('Ymd')) + 86400;
}
// 8点过期 / expired at 8 a.m.
function eight_expired($time = NULL)
{
$time = intval($time) ? $time : time();
// 当前时间大于8点则改为第二天8点过期
$life = date('G') <= 8 ? (strtotime(date('Ymd')) + 28800 - $time) : clock_twenty_four() - $time + 28800;
return $life;
}
// 24点过期 / expired at 24 a.m.
function twenty_four_expired($time = NULL)
{
$time = intval($time) ? $time : time();
$twenty_four = clock_twenty_four();
$life = $twenty_four - $time;
return $life;
}
/**
* @param $url 提交地址
* @param string $post POST数组 / 空为GET获取数据 / $post='GET'获取连续跳转最终URL
* @param string $cookie cookie
* @param int $timeout 超时
* @param int $ms 设为1是毫秒
* @return mixed 返回数据
*/
function https_request($url, $post = '', $cookie = '', $timeout = 30, $ms = 0)
{
if (empty($url)) return FALSE;
if (version_compare(PHP_VERSION, '5.2.3', '<')) {
$ms = 0;
$timeout = 30;
}
is_array($post) and $post = http_build_query($post);
// 没有安装curl 使用http的形式,支持post
if (!extension_loaded('curl')) {
//throw new Exception('server not install CURL');
if ($post) {
return https_post($url, $post, $cookie, $timeout);
} else {
return http_get($url, $cookie, $timeout);
}
}
is_array($cookie) and $cookie = http_build_query($cookie);
$curl = curl_init();
// 返回执行结果,不输出
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
//php5.5跟php5.6中的CURLOPT_SAFE_UPLOAD的默认值不同
if (class_exists('\CURLFile')) {
curl_setopt($curl, CURLOPT_SAFE_UPLOAD, true);
} else {
defined('CURLOPT_SAFE_UPLOAD') and curl_setopt($curl, CURLOPT_SAFE_UPLOAD, false);
}
// 设定请求的RUL
curl_setopt($curl, CURLOPT_URL, $url);
// 设定返回信息中包含响应信息头
if (ini_get('safe_mode') && ini_get('open_basedir')) {
// $post参数必须为GET
if ('GET' == $post) {
// 安全模式时将头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, true);
// 安全模式采用连续抓取
curl_setopt($curl, CURLOPT_NOBODY, true);
}
} else {
curl_setopt($curl, CURLOPT_HEADER, false);
// 允许跳转10次
curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
// 使用自动跳转,返回最后的Location
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
}
$ua1 = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1';
$ua = empty($_SERVER["HTTP_USER_AGENT"]) ? $ua1 : $_SERVER["HTTP_USER_AGENT"];
curl_setopt($curl, CURLOPT_USERAGENT, $ua);
// 兼容HTTPS
if (FALSE !== stripos($url, 'https://')) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
//ssl版本控制
//curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
curl_setopt($curl, CURLOPT_SSLVERSION, true);
}
$header = array('Content-type: application/x-www-form-urlencoded;charset=UTF-8', 'X-Requested-With: XMLHttpRequest');
$cookie and $header[] = "Cookie: $cookie";
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
if ($post) {
// POST
curl_setopt($curl, CURLOPT_POST, true);
// 自动设置Referer
curl_setopt($curl, CURLOPT_AUTOREFERER, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
}
if ($ms) {
curl_setopt($curl, CURLOPT_NOSIGNAL, true); // 设置毫秒超时
curl_setopt($curl, CURLOPT_TIMEOUT_MS, intval($timeout)); // 超时毫秒
} else {
curl_setopt($curl, CURLOPT_TIMEOUT, intval($timeout)); // 秒超时
}
//优先解析 IPv6 超时后IPv4
//curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
// 返回执行结果
$output = curl_exec($curl);
// 有效URL,输出URL非URL页面内容 CURLOPT_RETURNTRANSFER 必须为false
'GET' == $post and $output = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
curl_close($curl);
return $output;
}
function save_image($img)
{
$ch = curl_init();
// 设定请求的RUL
curl_setopt($ch, CURLOPT_URL, $img);
// 设定返回信息中包含响应信息头 启用时会将头文件的信息作为数据流输出
//curl_setopt($ch, CURLOPT_HEADER, false);
//curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER["HTTP_USER_AGENT"]);
// true表示$html,false表示echo $html
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
//curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
//curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
// 计算字串宽度:剧中对齐(字体大小/字串内容/字体链接/背景宽度/倍数)
function calculate_str_width($size, $str, $font, $width, $multiple = 2)
{
$box = imagettfbbox($size, 0, $font, $str);
return ($width - $box[4] - $box[6]) / $multiple;
}
// 搜索目录下的文件 比对文件后缀
function search_directory($path)
{
if (is_dir($path)) {
$paths = scandir($path);
foreach ($paths as $val) {
$sub_path = $path . '/' . $val;
if ('.' == $val || '..' == $val) {
continue;
} else if (is_dir($sub_path)) {
//echo '目录名:' . $val . '
';
search_directory($sub_path);
} else {
//echo ' 最底层文件: ' . $path . '/' . $val . '
';
$ext = strtolower(file_ext($sub_path));
if (in_array($ext, array('php', 'asp', 'jsp', 'cgi', 'exe', 'dll'), TRUE)) {
echo '异常文件:' . $sub_path . '
';
}
}
}
}
}
// 一维数组转字符串 $sign待签名字符串 $url为urlencode转码GET参数字符串
function array_to_string($arr, &$sign = '', &$url = '')
{
if (count($arr) != count($arr, 1)) throw new Exception('Does not support multi-dimensional array to string');
// 注销签名
unset($arr['sign']);
// 排序
ksort($arr);
reset($arr);
// 转字符串做签名
$url = '';
$sign = '';
foreach ($arr as $key => $val) {
if (empty($val) || is_array($val)) continue;
$url .= $key . '=' . urlencode($val) . '&';
$sign .= $key . '=' . $val . '&';
}
$url = substr($url, 0, -1);
$url = htmlspecialchars($url);
$sign = substr($sign, 0, -1);
}
// 私钥生成签名
function rsa_create_sign($data, $key, $sign_type = 'RSA')
{
if (!function_exists('openssl_sign')) throw new Exception('OpenSSL extension is not enabled');
if (!defined('OPENSSL_ALGO_SHA256')) throw new Exception('Only versions above PHP 5.4.8 support SHA256');
$key = wordwrap($key, 64, "\n", true);
if (FALSE === $key) throw new Exception('Private Key Error');
$key = "-----BEGIN RSA PRIVATE KEY-----\n$key\n-----END RSA PRIVATE KEY-----";
if ('RSA2' == $sign_type) {
openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA1);
}
// 加密
return base64_encode($sign);
}
// 公钥验证签名
function rsa_verify_sign($data, $sign, $key, $sign_type = 'RSA')
{
$key = wordwrap($key, 64, "\n", true);
if (FALSE === $key) throw new Exception('Public Key Error');
$key = "-----BEGIN PUBLIC KEY-----\n$key\n-----END PUBLIC KEY-----";
// 签名正确返回1 签名不正确返回0 错误-1
if ('RSA2' == $sign_type) {
$result = openssl_verify($data, base64_decode($sign), $key, OPENSSL_ALGO_SHA256);
} else {
$result = openssl_verify($data, base64_decode($sign), $key, OPENSSL_ALGO_SHA1);
}
return $result === 1;
}
// Array to xml array('appid' => 'appid', 'code' => 'success')
function array_to_xml($arr)
{
if (!is_array($arr) || empty($arr)) throw new Exception('Array Error');
$xml = "";
foreach ($arr as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "" . $key . ">";
} else {
$xml .= "<" . $key . ">" . $key . ">";
}
}
$xml .= "";
return $xml;
}
// Xml to array
function xml_to_array($xml)
{
if (!$xml) throw new Exception('XML error');
$old = libxml_disable_entity_loader(true);
// xml解析
$result = (array)simplexml_load_string($xml, null, LIBXML_NOCDATA | LIBXML_COMPACT);
// 恢复旧值
if (FALSE === $old) libxml_disable_entity_loader(false);
return $result;
}
// 逐行读取
function well_import($file)
{
if ($handle = fopen($file, 'r')) {
while (!feof($handle)) {
yield trim(fgets($handle));
}
fclose($handle);
}
}
// 计算总行数
function well_import_total($file, $key = 'well_import_total')
{
static $cache = array();
if (isset($cache[$key])) return $cache[$key];
$count = cache_get($key);
if (NULL === $count) {
$count = 0;
$globs = well_import($file);
while ($globs->valid()) {
++$count;
$globs->next(); // 指向下一个
}
$count and cache_set($key, $count, 300);
}
return $cache[$key] = $count;
}
$g_dir_file = FALSE;
function well_search_dir($path)
{
global $g_dir_file;
FALSE === $g_dir_file and $g_dir_file = array();
if (is_dir($path)) {
$paths = scandir($path);
foreach ($paths as $val) {
$sub_path = $path . '/' . $val;
if ('.' == $val || '..' == $val) {
continue;
} else if (is_dir($sub_path)) {
well_search_dir($sub_path);
} else {
$g_dir_file[] = $sub_path;
}
}
}
return $g_dir_file;
}
?>微信小程序开发高效构建与API实战你的位置:
首页>运维笔记>微信小程序开发高效构建与API实战内容概要
微信小程序开发的魅力,在于它像乐高积木一样——用标准化组件快速拼装,却能搭建出千变万化的数字世界。本章将带您拆解这套开发框架的底层逻辑,从WXML模板语法到数据绑定的魔法机制,揭示如何通过Page()
函数构建动态交互页面。您会发现,官方提供的scroll-view
和swiper
等原生组件,就像瑞士军刀般精准实用,但想要实现丝滑的列表渲染性能,还得掌握wx:for
指令的优化技巧。
开发冷知识:腾讯云开发(TCB)内置的数据库接口,能让你省去80%的后端搭建时间——毕竟,谁会拒绝白嫖官方服务器资源呢?
随着章节深入,我们将同步剖析微信开发者工具的调试秘籍。从模拟器网络延迟设置到真机预览的远程调试,每一个环节都藏着提升开发效率的彩蛋。别忘了提前配置好AppID,毕竟没有通行证的探险家,可进不了微信生态的奇幻森林。
微信小程序开发框架解析
如果把小程序比作数字世界的乐高城堡,那开发框架就是藏在幕后的搭建手册——既需要结构清晰的骨架,也得有灵活组装的窍门。微信官方提供的MINA框架采用双线程架构,就像给程序装上了分工明确的大脑和小脑:视图层(WebView)负责颜值担当,逻辑层(JSCore)专注数据处理,两者通过Native层这个金牌传话筒高效沟通。
这里有个开发者常忽略的冷知识:框架内置的组件化开发模式其实自带隐藏技能。比如把<scroll-view>
组件和wx.createSelectorQuery
API组合使用,能轻松实现「回到顶部」的丝滑动画,比单独写CSS省下三杯咖啡的时间。来看这个框架核心要素对照表:
模块名称 | 功能定位 | 典型应用场景 |
---|
WXML模板引擎 | 结构搭建 | 动态数据绑定 |
WXSS样式系统 | 视觉呈现 | 响应式布局适配 |
JavaScript逻辑 | 业务处理 | API调用与数据处理 |
JSON配置 | 全局设定 | 页面路由与权限管理 |
有意思的是,框架自带的数据驱动机制就像智能管家——当你在JS中修改data
对象时,WXML会自动同步更新对应DOM节点,完全不需要手动操作界面元素。不过要提防这个「热心管家」过度殷勤,建议用this.setData
时采用局部更新策略,避免频繁触发不必要的渲染。
原生组件应用与优化
小程序的原生组件就像乐高积木箱里的基础模块——用对了能搭出城堡,用砸了可能连狗窝都搭不稳。微信官方提供的picker
日期选择器、swiper
轮播图这些标配部件,乍看简单得像个玩具,但真要玩出花样还得懂点"物理外挂"。比如给scroll-view
加个enhanced
属性,瞬间变身丝滑滚动超人;在video
组件里藏个danmu-list
参数,用户就能边看教程边吐槽开发者写的bug。不过可别被官方组件惯坏了,当列表数据超过100条还在用原生view
硬扛?分分钟卡成PPT教你做人。这时候就该祭出虚拟列表+缓存策略组合拳,再给canvas
绘图任务套个worker
线程马甲,流畅度直接拉回60帧。偷偷告诉你,wx.createSelectorQuery()
这玩意儿比隔壁老王还懂组件尺寸,实时监测布局变化比女朋友查岗还灵敏。记住,原生组件不是拿来凑数的工具人,用好了它们,你的小程序能在性能赛道上把那些乱用第三方库的对手甩出三条街——当然,前提是你没把swiper
的自动轮播间隔设成反人类的0.5秒。
API集成开发实战指南
小程序开发就像搭积木,API就是那些带弹簧的隐藏接口——用对了能组合出会跳舞的机器人,用岔了可能拼出四不像。先来点实在的:把wx.request
封装成瑞士军刀式的网络模块,配上自动重试机制和错误码翻译器,能让你的接口调用稳如老狗。举个栗子,给请求头加上X-Custom-Token
时,记得在app.js
里搞个全局拦截器,免得每个页面都要手动贴膏药。异步操作处理推荐用Promise
全家桶,特别是Promise.allSettled
这种扫地僧级别的API,能让你同时发起五个商品详情请求也不会乱成一锅粥。想玩点高阶的?试试在云函数里调用OCR识别接口,配合wx.chooseImage
实现拍照识图功能,记得用try-catch
给代码穿件防弹衣。对了,调试支付接口时别和沙箱环境较劲,直接上wx.requestPayment
的真机测试,毕竟微信的模拟器有时候比薛定谔的猫还不靠谱。
上架审核规范全流程
小程序上架就像参加一场精心设计的通关游戏——腾讯官方既是考官也是裁判。别急着点击「提交审核」按钮,先检查代码里是否藏着调试日志这种「作弊小抄」,毕竟审核机器人可比监考老师敏锐多了。资质文件要像学生证一样随身带齐,测试账号记得给审核员留个VIP权限,否则你的小程序可能会被贴上「体验不完整」的尴尬标签。内容安全红线比高压电线还危险,涉及支付功能得备好营业执照,社交类目更需要增值电信业务许可证当护身符。提交时选对服务类目就像填高考志愿,手滑选错可能导致三天后收到「建议回炉重造」的驳回通知。悄悄告诉你,每周二四下午提交能蹭到审核队列的「淡季优惠」,遇到紧急情况还能在驳回通知里找到专属客服的「复活卡」。
结论
说到底,微信小程序开发就像在数字厨房里烹饪——框架是灶台,组件是食材,API则是调味料。当你掌握了官方工具的使用诀窍(毕竟谁想用钝刀切菜呢),那些看似复杂的审核规范也不过是确保菜品能端上宴席的质检标准。别忘了,每个流畅的页面切换背后都藏着组件优化的"火候把控",而API的巧妙调用就像给代码撒了层魔法粉末。不过可别光顾着炫技,用户的实际体验才是最终试金石。现在,菜谱已经交到你手里,是时候把学到的开发策略拌进项目锅铲里翻炒出香味了。
常见问题
小程序开发必须用微信官方工具吗?
虽然第三方工具也能编译代码,但官方开发者工具自带模拟器和调试插件,能精准捕捉“真机预览才暴露的坑”,建议开发阶段全程使用。
页面加载白屏如何快速定位问题?
先检查app.json
路由配置是否手滑多打了个逗号——这种低级错误占白屏案例的37%,再用性能分析面板查看资源加载瀑布流,八成是未压缩的图片在搞事情。
为什么我的审核总被卡在“内容安全”?
腾讯的AI审核员比班主任查手机还严格!确保所有用户输入内容都经过msgSecCheck
接口过滤,连“代开发票”这种谐音梗都会被标记,别拿机器人的语文水平开玩笑。
小程序能调用手机摄像头和GPS吗?
当然可以,但得先申请权限配置——而且用户拒绝授权时记得设计优雅的降级方案,总不能原地摆烂显示个“爱用不用”的表情包吧?
如何让下拉刷新动画更丝滑?
官方page-meta
组件搭配自定义Lottie动画是真香组合,但注意iOS和安卓的触控灵敏度差异,别让用户手指搓出火星子还没触发响应。
小程序能直接连MySQL数据库吗?
想啥呢?得通过云函数中转!建议用云开发环境省去服务器配置,毕竟自己搭Node.js服务端的功夫都够写完三个小程序的业务逻辑了。
为什么本地测试正常,真机就样式错乱?
检查rpx
单位换算——不同屏幕密度下可能变成“卖家秀”,用@media
媒体查询给折叠屏设备单独适配,毕竟谁也不想看到按钮叠成俄罗斯方块。
小程序能复用公众号的素材库吗?
理论上可以,但路径得用wx.getFileSystemManager()
重新转换,直接引用公众号URL会触发防盗链——这就好比试图用邻居家的WiFi密码连自家路由器。
审核通过后修改代码需要重新提交吗?
热更新不超过2MB的包可以直接云端生效,但涉及权限或支付配置变更?老老实实走审核流程吧,毕竟腾讯的规矩比数学公式还严谨。
小程序能实现多语言切换吗?
全局变量+i18n
方案就能搞定,不过记得在onLaunch
阶段读取系统语言设置——总不能让法国用户对着满屏中文夸你“C'est magnifique”吧?