钉钉内嵌H5定位失效?dd.getLocation到uni-app部署排查全记录
先交代一下背景我用HbuilderX写uni-appH5端最终要嵌进钉钉的工作台里页面里的定位功能走的是钉钉JSAPI也就是dd.getLocation。本地开发一切正常点按钮、拿经纬度、传给后端流程跑得特别舒服。结果打包成H5部署到服务器之后同事在钉钉里打开定位直接拿不到了控制台干干净净连报错都没有。这种“本地正常、上线就挂”的问题做钉钉内嵌H5的朋友一定不陌生。这篇文章把我这次踩坑的全过程、排查套路和最终配置方案完整写出来。不管你是刚接触uni-app还是已经和钉钉JSAPI打过几次交道照着这套思路基本能把环境差异类问题扫干净。我会把重点放在部署前后的环境差异、钉钉JSAPI的鉴权链路、uni-app打包配置这三个关键环节上最后还给了一份可以直接抄的Nginx和manifest配置。1. 先摸清楚 dd.getLocation 到底是怎么工作的1.1 本地“正常”有可能是一种错觉先说个扎心的事实你在HbuilderX内置浏览器里跑得再欢和钉钉App里真实打开页面完全是两套环境。uni-app在浏览器里跑的是普通网页钉钉App里跑的是被注入过原生桥接对象的网页。dd.getLocation这个API只有在钉钉容器里才存在window.dd对象浏览器里你连dd都拿不到。我见过不少人写代码时习惯这么干dd.getLocation({ targetType: gcj02, success: function(res) { // ... } });这段代码在本地浏览器里运行应该直接抛异常才对因为dd是undefined。可如果项目里加了统一封装比如先判断typeof dd undefined再走uni.getLocation兜底那你在本地看到的“一切正常”其实根本不是你真正要走的逻辑。换句话说本地正常只能证明页面渲染和业务逻辑没问题证明不了钉钉环境里这条路是通的。举个生活化的类比dd.getLocation就像你到别人家做客时借用人家的厨房。你在自己家里怎么开火都行但到了别人家你得先敲门dd.config鉴权、等主人开门dd.ready回调才能进去用厨房。部署到服务器之后出了状况不是你家厨艺不行而是“敲门”这一步卡住了。1.2 一次正常的定位调用要过多少道关卡钉钉JSAPI的调用链路比想象中要长。完整的流程是这样的引入钉钉JSAPI脚本可以是外部script标签也可以用npm包dingtalk-jsapi。拿到后端的签名参数包括agentId、corpId、timestamp、nonceStr、signature。调用dd.config做鉴权把签名参数传进去。鉴权通过后在dd.ready回调里调用dd.getLocation。用户在钉钉内授权定位权限然后才能拿到经纬度和精度。这里每道关卡都可能因为环境差异挂掉。尤其是dd.config这一步钉钉后端会用你当前页面的URL参与签名计算本地是localhost部署后是正式域名只要URL不一致、或者后端生成签名时取的URL和你实际访问的URL不一致签名校验就会失败后续dd.getLocation自然就没反应。我把这个链路拆开讲是想说一个结论部署后定位失效大部分时候不是定位代码本身的问题而是前面的“门”没打开。下面几节就按这个思路逐层拆解。2. 部署后最容易翻车的三个配置点2.1 钉钉后台的可信域名和签名校验钉钉的H5微应用里有一个非常容易被忽略的配置服务器域名也就是可信域名。钉钉要求JSAPI只能在配置过的域名下使用你本地用localhost调试钉钉可能睁一只眼闭一只眼但部署到生产环境域名只要不在白名单里dd.config会直接失败。这个配置的位置在钉钉开发者后台 → 你的应用 → 应用开发 → H5微应用 → 开发管理 → 服务器域名。路径各个版本稍有差异但关键词就是“服务器域名”四个字。添加的时候注意一定要填实际访问的完整域名不要带path也不要带协议比如填写example.com而不是https://example.com/h5/index.html。第二个容易踩的点是签名。钉钉JSAPI的签名机制是这样的后端拿corpId、agentId、timestamp、nonceStr再用企业内部应用的AppKey和AppSecret去获取jsapi_ticket最后用jsapi_ticket和当前URL拼串做SHA-1哈希。这个URL必须是前端页面实际访问的完整URL而且要去掉#后面的部分。我这次踩的坑就在这后端同学直接用后端收到的请求URL去签名而前端页面URL因为某些原因带着一堆参数两者差了几个字符结果就是签名校验失败。所以后来我统一了规范前端调用dd.config前先把location.href.split(#)[0]通过接口传给后端保证签名用的URL和实际访问URL完全一致。2.2 uni-app打包后的资源路径和路由模式这部分是纯技术配置和钉钉本身没关系但同样能让你部署后“看不见东西”——白屏、CSS丢失、JS找不到。uni-app打包H5后默认资源路径是绝对路径比如/static/js/chunk-xxx.js。如果你的H5部署在域名根目录还好如果是部署在子路径比如https://example.com/h5/那这些资源会全部404。解决办法在项目的manifest.json里找到h5节点把publicPath改成相对路径或者你的实际部署路径{ h5: { router: { mode: hash }, publicPath: ./ } }publicPath设成./可以让页面自动从当前目录加载静态资源部署在哪个子路径都不怕。不过要注意纯相对路径在某些嵌套路由场景下可能会出问题如果你用了history模式更推荐设置成绝对路径比如/h5/。再说路由模式。uni-app的H5路由默认是hash模式URL里会带个#号比如https://example.com/h5/#/pages/index/index。这种模式有个好处就是不依赖服务端重写刷新页面也能找到对应路由。而history模式URL很干净但刷新或者直接访问二级路径时如果服务器没做配置会直接404。对于钉钉内嵌H5的场景我建议直接用hash模式省心、少踩坑。如果你非要用history模式那Nginx必须配好try_files重写到index.html后面我会给配置。2.3 JSAPI脚本引入和环境判断钉钉JSAPI的引入方式有两种一种是在项目的index.html里直接挂script标签另一种是npm安装dingtalk-jsapi然后在代码里import。我个人更推荐npm方式因为可以按需引入也方便做环境判断。但不少人是直接改模板用的script标签这也没问题前提是你得确认这个script在生产环境能被正常加载。另一个坑是环境判断。代码里如果不做任何判断就调用dd.config在某些非钉钉环境会直接报错。我给个参考写法const isDingTalk typeof window.dd ! undefined window.dd ! null; if (isDingTalk) { dd.config({ agentId: res.agentId, corpId: res.corpId, timestamp: res.timestamp, nonceStr: res.nonceStr, signature: res.signature, jsApiList: [dd.getLocation] }); dd.ready(() { // 在这里调用 dd.getLocation }).catch(err { console.error(dd ready failed, err); }); } else { // 非钉钉环境走 uni.getLocation 兜底 }这个判断最大的好处是你在HbuilderX内置浏览器和普通浏览器里调试时不会再因为dd对象不存在而白屏也不会在钉钉容器里因为没判断而错过真正的定位逻辑。我在实际项目里还见过一种写法在main.js里根据环境变量判断是否引入钉钉SDK这个思路也可以但建议别搞得太复杂能跑、能排查才是最实际的。3. 完整排查实录从现象定位到根因这一节我按实际场景来写。我的页面部署到服务器后同事反馈定位按钮没反应。我第一反应不是改代码而是打开钉钉开发者工具把H5首页地址输进去开启vConsole观察控制台输出。排查顺序我觉得可以固定成一条线每次都按这个顺序走效率高很多。3.1 现象一定位按钮点了没反应首先看控制台有没有JS报错。如果报的是dd is not defined说明钉钉JSAPI脚本没加载成功或者当前环境压根不是钉钉。如果在钉钉里也报这个错那大概率是引入脚本的方式有问题检查script标签的src是否可访问、有没有被CSP拦截。如果dd对象存在没有报错但按钮点了没反应这时候要确认dd.config是否走完了。我在console里加了几个log发现dd.config之后直接在dd.ready外调用了dd.getLocation这个顺序是有问题的。钉钉官方要求所有JSAPI调用必须在dd.ready回调里执行。虽然大部分时候在外面也能偶尔成功但那属于运气好一旦鉴权还没完成调用就直接丢了。还有一次dd.config成功、dd.ready也执行了但dd.getLocation就是不触发回调。最后发现是钉钉版本太老不支持dd.getLocation这个API。这种情况在老旧的企业钉钉版本里偶尔会遇到解决办法要么升级钉钉要么走uni.getLocation做降级。对咱们做H5集成的人来说降级方案真的不能少。3.2 现象二dd.getLocation报错误码如果dd.getLocation回调里返回了errorCode那问题会更明确。我把常见的情况整理成了表格现象可能原因处理方式errorCode为401签名错误URL与后端签名用的URL不一致前端把location.href.split(#)[0]传给后端重新签名errorCode为403无权限企业未开通对应权限点到钉钉开发者后台检查权限点是否申请并启用errorCode为404当前钉钉版本不支持该API建议升级钉钉或走uni.getLocation兜底提示no permission用户未授权定位或应用未配置定位权限检查钉钉客户端定位权限引导用户开启定位注意钉钉不同版本返回的错误格式不一定完全一样关键是看err对象里有没有errorMessage一般里面会带英文描述比如no permission、signature error之类的关键字。根据关键字再去翻钉钉开放平台文档比自己瞎猜快得多。3.3 现象三坐标能拿到但地图上位置偏移坐标偏移的问题往往是坐标系不统一造成的。钉钉的dd.getLocation默认返回的是gcj02坐标也就是俗称的“火星坐标”国内主流地图厂商比如高德、腾讯都直接用这个坐标系。但如果你把坐标丢给百度地图或者拿到的经纬度和地图底图对不上位置偏出去几百米甚至更远就很正常。遇到这种情况先确认你用的地图SDK是哪个。高德地图直接喂gcj02坐标没问题百度地图需要转成bd09有些海外地图服务用的是wgs84那就要做gcj02到wgs84的转换。转换公式网上很多也可以直接用现成库别自己手搓数学公式容易出精度问题。我在项目里是统一后端存储为gcj02前端展示也统一用gcj02从源头避免坐标系混乱。3.4 现象四页面直接白屏或样式错乱如果部署后白屏那多半不是定位的问题是静态资源没加载出来。优先级最高的排查项就是publicPath。我之前接过一个项目把H5包直接扔到了nginx的html/h5目录结果index.html加载到了但里面的css和js全是404后来把manifest里的publicPath改成./就正常了。还有一种情况是Nginx配置。如果用了history模式Nginx没有做try_files刷新一次就404。如果用了hash模式这个问题基本不存在。所以如果你不想碰服务端就老实选hash模式。4. 可直接照抄的部署配置4.1 uni-app构建配置清单先说uni-app这边的配置我以HbuilderX 3.x版本为例。打开项目根目录的manifest.json切到源码视图找到h5节点配置如下{ h5: { router: { mode: hash, base: /h5/ }, publicPath: /h5/, devServer: { port: 8080 } } }如果部署在根路径base和publicPath都可以改成./或者/根据你自己的部署位置来。配置好后在HbuilderX菜单栏选“发行” → “网站-H5手机版”构建产物会输出到unpackage/dist/build/h5目录把这个目录里的所有文件原样上传到服务器即可。构建的时候有几个小细节值得注意一是尽量在发布前手动删除unpackage目录里的旧dist避免缓存文件混进去二是如果你的项目里有用到地图SDK的key记得检查H5端有没有单独配置key钉钉内访问用的是你项目的正式key别把测试key带上去。三是条件编译一定要用好钉钉相关代码用#ifdef H5包起来避免影响其他端的构建。4.2 Nginx部署示例假设你的H5要部署到https://example.com/h5/这个路径对应服务器目录是/var/www/h5。Nginx配置可以这样写server { listen 443 ssl; server_name example.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; root /var/www/h5; index index.html; location /h5/ { alias /var/www/h5/; try_files $uri $uri/ /h5/index.html; } location /h5/static/ { alias /var/www/h5/static/; expires 7d; add_header Cache-Control public; } }如果用的是hash模式try_files那行其实不一定要但保留也没问题防止以后切到history模式。如果你直接把项目部署到域名根目录那就把location改成root /var/www/h5try_files改成try_files $uri $uri/ /index.html。有一点提醒一下钉钉的H5应用要求使用HTTPS。如果你还是http钉钉端会提示非安全链接dd.config也可能直接失败。所以服务器证书和443监听是少不了的证书可以用通用的免费证书方案反正自动续期脚本一挂基本不用管。4.3 钉钉后台配置与签名接口钉钉后台部分你需要保证应用类型是“H5微应用”。进入开发者后台找到你的应用在H5微应用的开发管理里把首页地址配置成实际访问地址比如https://example.com/h5/index.html。然后在服务器域名里填入example.com确保和首页地址域名一致。签名接口这块前端和后端要约定好。我在项目中用了一个很简单的接口路径类似/dd/getSign前端把访问URL传过去后端返回以下参数{ agentId: your-agent-id, corpId: your-corp-id, timestamp: 1700000000000, nonceStr: random-string, signature: sha1-hash-value }前端拿到这些参数后在dd.config里带上并打开debug模式方便排查dd.config({ agentId: res.agentId, corpId: res.corpId, timestamp: res.timestamp, nonceStr: res.nonceStr, signature: res.signature, jsApiList: [dd.getLocation], debug: true });debug模式开启后如果签名有问题控制台会打印出具体错误信息排查效率会高很多。但这个debug模式上生产环境建议关掉不然控制台日志太吵也容易暴露内部信息。5. 常见问题速查表与实践心得5.1 问题速查表我把这次排查过程中遇到的各种问题、原因和动作整理成一张表方便你遇到问题时直接翻现象可能原因处理方式本地正常钉钉里定位没反应dd.config签名失败或未在dd.ready中调用检查签名URL、可信域名调整调用时机部署后页面白屏publicPath配置错误修改manifest的h5.publicPath为实际路径或./history模式刷新404Nginx未配置try_files添加try_files或改用hash模式dd对象不存在钉钉JSAPI脚本未加载或环境不是钉钉检查script引入非钉钉环境走兜底dd.config返回签名错误签名URL与后端计算不一致前端传location.href.split(#)[0]给后端定位能拿到但坐标偏移坐标系不统一统一使用gcj02按地图SDK要求转换老版本钉钉不支持API钉钉版本过旧升级钉钉或降级到uni.getLocation部署后DNS或资源打不开域名白名单未配置在钉钉后台添加服务器域名5.2 我踩过的几个坑先说一个印象最深的坑。那时候我刚从纯H5切到uni-app图省事直接在mounted生命周期里写了dd.getLocation本地测试环境反正能拿到也没细想。结果打包上线后钉钉里十次有八九次拿不到定位。后来看文档才发现dd.config和dd.ready这套异步鉴权机制不是开玩笑的必须等ready了再调用。从那以后我就养成了习惯所有钉钉JSAPI调用统一封装在一个Promise里等dd.ready后再resolve业务层只关心定位结果不关心底层鉴权时序。第二个坑是部署目录。有一次我把H5包直接丢nginx根目录了publicPath写的/一切正常。后来项目要和其他系统共用一个域名就得挪到子目录结果没改publicPath部署完页面全是白花花一片。那一次之后我只要涉及部署路径变更第一件事必查manifest里的publicPath和路由base。第三个坑是缓存。钉钉里的H5页面加载是有缓存的我发布新版本后同事反应页面还是旧的。一开始以为是服务器缓存没刷新后来发现是钉钉客户端的缓存策略比较激进。解决办法是发布后在钉钉里清一下缓存或者在前端资源URL上带版本号。如果你用的是HbuilderX的发行功能每次打包会自动生成带hash的文件名一般不会有这个问题但如果某些公共库是外部引用的缓存问题还是得留意。5.3 一点降级与兜底建议虽然dd.getLocation是钉钉内的正路但做移动端H5永远得留一手。我的做法是先用环境判断区分是不是钉钉是钉钉就走dd.getLocation不是就走uni.getLocation。这样既保证钉钉内体验也方便普通浏览器测试。同时我给定位加了一个超时机制。dd.getLocation有时候会因为用户不授权或者网络问题卡住不能一直等。我一般会设定5秒超时超时后提示用户检查网络或重新授权再给一个手动重试按钮。真要做得好一点可以监听visibilitychange页面从后台切回来的时候重新获取一次定位很多用户是在钉钉里切到地图应用再切回来这时候重新拉一次定位会更准确。最后说个关键心得钉钉内嵌H5的问题90%都是环境差异导致的不是代码逻辑问题。所以排查顺序一定要固定先看可信域名和签名再看JSAPI调用时机再看打包配置和服务器配置。这套顺序我现在已经形成肌肉记忆了也把最核心的检查项做成了一份发布前自检清单每次发版前过一遍能省掉非常多“线上救火”的时间。在这里也分享一个小技巧发布前在钉钉开发者工具里完整走一遍流程同时把vConsole打开。这个小习惯看起来不起眼但能帮你把dd.config的错误信息、JSAPI的调用日志看得清清楚楚比上线后让同事截图再猜要高效得多。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →