提交 77a37519 编写于 作者: Q qiang

Merge branch 'dev' into alpha

......@@ -4,4 +4,3 @@ build/rollup-plugin-require-context
packages/*/packages
packages/*/template/**/*
qh-api.js
touch-emulator.js
......@@ -6,7 +6,7 @@
# uni-app的特点
- 开发者和案例更多:HBuilder装机量420万台,开发者社区月活百万,70多个QQ微信群承载10万人。案例众多,uni统计月活4.5亿([详见](https://uniapp.dcloud.io/case)
- 开发者和案例更多:HBuilder装机量420万台,开发者社区月活百万,70多个QQ微信群承载10万人。案例众多,uni统计月活6.5亿([详见](https://uniapp.dcloud.io/case)
- 性能更高(见[评测](https://juejin.im/post/5ca1736af265da30ae314248)
- 更丰富的周边生态,[插件市场](https://ext.dcloud.net.cn/)数千款插件
- 提供比小程序原生开发更好的开发体验、更高的工程化效率
......
......@@ -35,7 +35,6 @@ if (process.env.UNI_SERVICE === 'legacy') {
output.name = 'serviceContext'
output.banner =
`export function createServiceContext(Vue, weex, plus, UniServiceJSBridge,instanceContext){
var localStorage = plus.storage
var setTimeout = instanceContext.setTimeout
var clearTimeout = instanceContext.clearTimeout
var setInterval = instanceContext.setInterval
......
```uni-app``` 是一个使用 [Vue.js](https://vuejs.org/) 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。
`DCloud`公司拥有420万开发者,几十万应用案例、10.5亿手机端用户,数千款uni-app插件、70+微信/qq群。阿里小程序工具官方内置uni-app([详见](https://docs.alipay.com/mini/ide/0.70-stable)),腾讯课堂官方为uni-app录制培训课程([详见](https://ask.dcloud.net.cn/article/35640)),开发者可以放心选择。
`DCloud`公司拥有420万开发者,几十万应用案例、6.5亿手机端月活用户,数千款uni-app插件、70+微信/qq群。阿里小程序工具官方内置uni-app([详见](https://docs.alipay.com/mini/ide/0.70-stable)),腾讯课堂官方为uni-app录制培训课程([详见](https://ask.dcloud.net.cn/article/35640)),开发者可以放心选择。
`uni-app`在手,做啥都不愁。即使不跨端,```uni-app```也是更好的小程序开发框架([详见](https://ask.dcloud.net.cn/article/35947))、更好的App跨平台框架、更方便的H5开发框架。不管领导安排什么样的项目,你都可以快速交付,不需要转换开发思维、不需要更改开发习惯。
......@@ -74,7 +74,7 @@
</div>
<div class="uniapp-home-content-item-header">
<h5 class="uniapp-home-content-item-title">开发者/案例数量更多</h5>
<p class="uniapp-home-content-item-text">10万+案例、uni统计月活过3亿、70+微信/qq群、更高的百度指数</p>
<p class="uniapp-home-content-item-text">几十万应用、uni统计月活6.5亿、70+微信/qq群、更高的百度指数</p>
<p class="uniapp-home-content-item-text">跨端完善度更高,真正落地的提高生产力</p>
</div>
</div>
......@@ -150,7 +150,7 @@
从下面```uni-app```功能框架图可看出,```uni-app```在跨平台的过程中,不牺牲平台特色,可优雅的调用平台专有能力,真正做到海纳百川、各取所长。
![](https://img.cdn.aliyun.dcloud.net.cn/uni-app/doc/uni0124.png)
![](https://img.cdn.aliyun.dcloud.net.cn/uni-app/doc/uni-app-frame-0310.png)
### 一套代码,运行到多个平台
......
......@@ -132,8 +132,8 @@ address|String|地址
**Tips**
- App端使用map,nvue比vue更强大。
- App端vue页面默认为高德地图,也可以选择百度地图。但app-nvue只有高德地图,没有百度地图。以及地图选择api,只支持高德地图。
- App端使用map,nvue比vue更强大,且没有层级问题
- App端vue页面默认为高德地图,也可以选择百度地图。但app-nvue只有高德地图,没有百度地图。以及地图选择api(mapSearch),只支持高德地图。
- H5 端获取定位信息,需要部署在 **https** 服务上,本地预览(localhost)仍然可以使用 http 协议。
- 无 GPS 模块的 PC 设备使用 Chrome 浏览器的时候,位置信息是连接谷歌服务器获取的,国内用户可能获取位置信息失败。
- App 端使用地图组件需要向高德或百度等三方服务商申请SDK资质,获取AppKey,打包时需要在manifest的SDK配置中填写Appkey。在manifest可视化界面有详细申请指南,详见:[https://ask.dcloud.net.cn/article/29](https://ask.dcloud.net.cn/article/29)
......
此差异已折叠。
......@@ -22,7 +22,7 @@ uni.setNavigationBarTitle({
### uni.setNavigationBarColor(OBJECT)
设置页面导航条颜色。
设置页面导航条颜色。**如果需要进入页面就设置颜色,请延迟执行,防止被框架内设置颜色逻辑覆盖**
**平台差异说明**
......
......@@ -116,8 +116,9 @@ subNVue.setStyle({
```
### subNVue.postMessage(OBJECT)
发送消息
### subNVue.postMessage(OBJECT)
发送消息,此通讯方式已过时,请使用`uni.$emit`进行通讯,[参考](/collocation/frame/communication?id=emit)
**代码示例**
......@@ -130,8 +131,9 @@ subNvue.postMessage({
})
```
### subNVue.onMessage(CallBack)
监听消息
### subNVue.onMessage(CallBack)
监听消息,此通讯方式已过时,请使用`uni.$on`进行通讯,[参考](/collocation/frame/communication?id=on)
**代码示例**
......
DCloud有420万开发者,[uni统计](https://tongji.dcloud.net.cn/)覆盖手机终端10亿,月活4.5亿。是开发者数量和案例最丰富的多端开发框架。
DCloud有**420万**开发者,[uni统计](https://tongji.dcloud.net.cn/)手机端月活**6.5亿**。是开发者数量和案例最丰富的多端开发框架。
欢迎知名开发商提交案例或接入uni统计,[项目案例征集](https://github.com/dcloudio/uni-app/issues/6)提交。
......
......@@ -293,6 +293,10 @@
|type|String|default|导航栏样式。"default"-默认样式;"transparent"-滚动透明渐变;"float"-悬浮导航栏。|App-nvue 2.4.4+ 支持|
|tags|Array||原生 View 增强,详见:[5+ View 控件](http://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.ViewDrawTagStyles)||
|searchInput|Object||原生导航栏上的搜索框配置,详见:[searchInput](/collocation/pages?id=app-titlenview-searchinput)|1.6.0|
|backButton|Object||返回按钮的样式,详见:[backButton](/collocation/pages?id=app-titleNView-backButtonStyles)|2.6.3|
|backgroundImage|String||支持以下类型: 背景图片路径 - 如"./img/t.png",仅支持本地文件路径, 相对路径,相对于当前页面的host位置,根据实际标题栏宽高拉伸绘制; 渐变色 - 仅支持线性渐变,两种颜色的渐变,如“linear-gradient(to top, #a80077, #66ff00)”, 其中第一个参数为渐变方向,可取值: "to right"表示从左向右渐变, “to left"表示从右向左渐变, “to bottom"表示从上到下渐变, “to top"表示从下到上渐变, “to bottom right"表示从左上角到右下角, “to top left"表示从右下角到左上角|2.6.3|
|backgroundRepeat|String||仅在backgroundImage设置为图片路径时有效。 可取值: "repeat" - 背景图片在垂直方向和水平方向平铺; "repeat-x" - 背景图片在水平方向平铺,垂直方向拉伸; “repeat-y” - 背景图片在垂直方向平铺,水平方向拉伸; “no-repeat” - 背景图片在垂直方向和水平方向都拉伸。 默认使用 “no-repeat"|2.6.3|
|titleAlign|String|"auto"|可取值: "center"-居中对齐; "left"-居左对齐; "auto"-根据平台自动选择(Android平台居左对齐,iOS平台居中对齐)|2.6.3|
**Tips**
......@@ -322,6 +326,25 @@
|text|String||按钮上显示的文字。使用字体图标时 unicode 字符表示必须 '\u' 开头,如 "\ue123"(注意不能写成"\e123")。|
|width|String|44px|按钮的宽度,可取值: "*px" - 逻辑像素值,如"10px"表示10逻辑像素值,不支持rpx。按钮的内容居中显示; "auto" - 自定计算宽度,根据内容自动调整按钮宽度|
##### 自定义返回按钮的样式@app-titleNView-backButtonStyles
当autoBackButton设置为true时生效。 通过此属性可自定义返回按钮样式,如图标大小、红点、角标、标题等。
HBuilderX 2.6.3+ 支持
|属性|类型|默认值|描述|
|:-|:-|:-|:-|
|background|String|none|背景颜色,仅在标题栏type=transparent时生效,当标题栏透明时按钮显示的背景颜色。 可取值#RRGGBB和rgba格式颜色字符串,默认值为灰色半透明。|
|badgeText|String||角标文本,最多显示3个字符,超过则显示为...|
|color|String|窗口标题栏控件的标题文字颜色。|图标和标题颜色,可取值: "#RRGGBB"格式字符串,如"#FF0000"表示红色; "rgba(R,G,B,A)",其中R/G/B分别代表红色值/绿色值/蓝色值,正整数类型,取值范围为0-255,A为透明度,浮点数类型,取值范围为0-1(0为全透明,1为不透明),如"rgba(255,0,0,0.5)",表示红色半透明。|
|colorPressed|String||按下状态按钮文字颜色,可取值: "#RRGGBB"格式字符串,如"#FF0000"表示红色; "rgba(R,G,B,A)",其中R/G/B分别代表红色值/绿色值/蓝色值,正整数类型,取值范围为0-255,A为透明度,浮点数类型,取值范围为0-1(0为全透明,1为不透明),如"rgba(255,0,0,0.5)",表示红色半透明。 默认值为color属性值自动调整透明度为0.3。|
|fontWeight|String|"normal"|返回图标的粗细,可取值: "normal" - 标准字体; "bold" - 加粗字体。|
|fontSize|String||返回图标文字大小,可取值:字体高度像素值,数字加"px"格式字符串,如"22px"。 窗口标题栏为透明样式(type="transparent")时,默认值为"22px"; 窗口标题栏为默认样式(type="default")时,默认值为"27px"。|
|redDot|Boolean|false|是否显示红点,设置为true则显示红点,false则不显示红点。默认值为false。 注意:当设置了角标文本时红点不显示。|
|title|String||返回按钮上的标题,显示在返回图标(字体图标)后,默认为空字符串。|
|ftitleWeight|String|"normal"|返回按钮上标题的粗细,可取值: "normal" - 标准字体; "bold" - 加粗字体。|
|fontSize|String|"16px"|标题的字体大小,可取值:字体高度像素值,数字加"px"格式字符串,如"22px"。|
##### 按钮样式@app-titleNView-buttons-type
......@@ -379,7 +402,10 @@ searchInput的点击输入框onNavigationBarSearchInputClicked、文本变化onN
{
"text": "分享" //原生标题栏增加分享按钮,点击事件可通过页面的 onNavigationBarButtonTap 函数进行监听
}
]
],
"backButton": { //自定义 backButton
"background": "#00FF00"
}
}
}
}
......@@ -637,6 +663,10 @@ h5 平台下拉刷新动画,只有 circle 类型。
|:-|:-|:-|
|colorType|String|阴影的颜色,支持:grey、blue、green、orange、red、yellow|
**注意事项:**
- 微信/百度/头条 需要配置: "disableScroll": true
- 支付宝 "mp-alipay": { "allowsBounceVertical": "NO" }
### mp-alipay
配置编译到 MP-ALIPAY 平台时的特定样式
......@@ -867,7 +897,7 @@ subPackages 节点接收一个数组,数组每一项都是应用的子包,
- 微信、百度小程序每个分包的大小是2M,总体积一共不能超过8M。
- 支付宝小程序每个分包的大小是2M,总体积一共不能超过4M。
- 分包下支持独立的 ```static``` 目录,用来对静态资源进行分包。
- `uni-app`内支持对微信小程序、QQ小程序、百度小程序分包优化,[关于分包优化的说明](/collocation/manifest?id=关于分包优化的说明)
- `uni-app`内支持对微信小程序、QQ小程序、百度小程序分包优化,即将静态资源或者js文件放入分包内不占用主包大小。详情请参考:[关于分包优化的说明](/collocation/manifest?id=关于分包优化的说明)
- 针对`vendor.js`过大的情况可以使用运行时压缩代码
+ `HBuilderX`创建的项目勾选`运行-->运行到小程序模拟器-->运行时是否压缩代码`
+ `cli`创建的项目可以在`pacakge.json`中添加参数`--minimize`,示例:`"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize"`
......
......@@ -78,9 +78,11 @@
* [SwiperDot 轮播图指示点](https://ext.dcloud.net.cn/plugin?id=284)
* [Tag 标签](https://ext.dcloud.net.cn/plugin?id=35)
* [更多插件市场的组件](https://ext.dcloud.net.cn/?cat1=2)
* 导航栏
* [navigation-bar](component/navigation-bar.md)
* 页面属性配置节点
* [page-meta](component/page-meta.md)
* [配置微信小程序插件](component/mp-weixin-plugin.md)
* [配置小程序插件](component/mp-weixin-plugin.md)
* [原生组件说明](component/native-component.md)
<div class="contact-box">
<a href="//ask.dcloud.net.cn/explore/" target="_blank" class="contact-item">
......
......@@ -16,6 +16,14 @@
|hover-class|String|button-hover|指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果||App-nvue 平台暂不支持|
|hover-start-time|Number|20|按住后多久出现点击态,单位毫秒|||
|hover-stay-time|Number|70|手指松开后点击态保留时间,单位毫秒|||
|app-parameter|String||打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效||微信小程序、QQ小程序|
|hover-stop-propagation|boolean|false|指定是否阻止本节点的祖先节点出现点击态||微信小程序|
|lang|string|'en'|指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。||微信小程序|
|session-from|string||会话来源,open-type="contact"时有效||微信小程序|
|send-message-title|string|当前标题|会话内消息卡片标题,open-type="contact"时有效||微信小程序|
|send-message-path|string|当前分享路径|会话内消息卡片点击跳转小程序路径,open-type="contact"时有效||微信小程序|
|send-message-img|string|截图|会话内消息卡片图片,open-type="contact"时有效||微信小程序|
|show-message-card|boolean|false|是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,open-type="contact"时有效||微信小程序|
|@getphonenumber|Handler||获取用户手机号回调|open-type="getPhoneNumber"|微信小程序|
|@getuserinfo|Handler||用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo|open-type="getUserInfo"|微信小程序|
|@error|Handler||当使用开放能力时,发生错误的回调|open-type="launchApp"|微信小程序|
......
......@@ -19,10 +19,13 @@
|属性名|类型|默认值|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|mode|String|normal |有效值为 normal, scanCode |微信小程序|
|resolution|string|medium|分辨率,不支持动态修改|微信小程序2.10.0|
|device-position|String |back |前置或后置,值为front, back| |
|flash |String |auto |闪光灯,值为auto, on, off| |
|frame-size|string|medium|指定期望的相机帧数据尺寸|微信小程序2.7.0|
|@stop |EventHandle | |摄像头在非正常终止时触发,如退出后台等情况| |
|@error |EventHandle | |用户不允许使用摄像头时触发| |
|@initdone|eventhandle||相机初始化完成时触发,e.detail = {maxZoom}|微信小程序2.7.0|
|@scancode |EventHandle | |在扫码识别成功时触发,仅在 mode="scanCode" 时生效|微信小程序 |
**Tips:**
......
......@@ -10,6 +10,11 @@
|√|√|√|√|√|x|√|
支持的事件:`click`
|属性名|类型|默认值|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|scroll-top|number/string||设置顶部滚动偏移量,仅在设置了 overflow-y: scroll 成为滚动元素后生效|微信小程序2.1.0|
#### cover-image
覆盖在原生组件上的图片视图。
......@@ -22,9 +27,11 @@
**属性说明**
|属性名|类型|默认值|说明|
|:-|:-|:-|:-|
|属性名|类型|默认值|说明|平台差异说明|
|:-|:-|:-|:-|:-|
|src|String||图标路径。支持本地路径、网络路径。不支持 base64 格式。|
|@load|eventhandle||图片加载成功时触发|微信小程序 2.1.0|
|@error|eventhandle||图片加载失败时触发|微信小程序 2.1.0|
可覆盖的原生组件:`<video>``<map>`
......
......@@ -8,6 +8,7 @@
|属性名|类型|说明|平台差异说明|
|:-|:-|:-|:-|
|report-submit|Boolean|是否返回 formId 用于发送[模板消息](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/template-message.html)|微信小程序、支付宝小程序|
|report-submit-timeout|number|等待一段时间(毫秒数)以确认 formId 是否生效。如果未指定这个参数,formId 有很小的概率是无效的(如遇到网络失败的情况)。指定这个参数将可以检测 formId 是否有效,以这个参数的时间作为这项检测的超时时间。如果失败,将返回 requestFormId:fail 开头的 formId|微信小程序2.6.2|
|@submit|EventHandle|携带 form 中的数据触发 submit 事件,event.detail = {value : {'name': 'value'} , formId: ''},report-submit 为 true 时才会返回 formId||
|@reset|EventHandle|表单重置时会触发 reset 事件|&nbsp;|
......
......@@ -8,6 +8,8 @@
|mode|String|'scaleToFill'|图片裁剪、缩放的模式|<div style="width:68px;"></div>|
|lazy-load|Boolean|false|图片懒加载。只针对page与scroll-view下的image有效|微信小程序、App、百度小程序、头条小程序|
|fade-show|Boolean|true|图片显示动画效果|仅App-nvue 2.3.4+ Android有效|
|webp|boolean|false|默认不解析 webP 格式,只支持网络资源|微信小程序2.9.0|
|show-menu-by-longpress|boolean|false|开启长按图片显示识别小程序码菜单|微信小程序2.7.0|
|@error|HandleEvent||当错误发生时,发布到 AppService 的事件名,事件对象event.detail = {errMsg: 'something wrong'}||
|@load|HandleEvent||当图片载入完毕时,发布到 AppService 的事件名,事件对象event.detail = {height:'图片高度px', width:'图片宽度px'}|&nbsp;|
......
......@@ -22,10 +22,12 @@
|selection-start|Number|-1|光标起始位置,自动聚集时有效,需与selection-end搭配使用||
|selection-end|Number|-1|光标结束位置,自动聚集时有效,需与selection-start搭配使用||
|adjust-position|Boolean|true|键盘弹起时,是否自动上推页面|App(softinputMode 为 adjustResize 时无效)、微信小程序、百度小程序、QQ小程序|
|hold-keyboard|boolean|false|focus时,点击页面的时候不收起键盘|微信小程序2.8.2|
|@input|EventHandle||当键盘输入时,触发input事件,event.detail = {value}|差异见下方 Tips|
|@focus|EventHandle||输入框聚焦时触发,event.detail = { value, height },height 为键盘高度|仅微信小程序、App(2.2.3+) 、QQ小程序支持 height|
|@blur|EventHandle||输入框失去焦点时触发,event.detail = {value: value}||
|@confirm|EventHandle||点击完成按钮时触发,event.detail = {value: value}|&nbsp;|
|@keyboardheightchange|eventhandle||键盘高度发生变化的时候触发此事件,event.detail = {height: height, duration: duration}|微信小程序2.7.0|
**Tips**
......
......@@ -32,6 +32,39 @@
|@netstatus|EventHandle||网络状态通知,detail = {info}||
|@fullscreenchange|EventHandle||全屏变化事件,detail = {direction, fullScreen}。|&nbsp;|
mode 的合法值
|值|说明|
|:-|:-|
|live|直播|
|RTC|实时通话,该模式时延更低|
orientation 的合法值
|值|说明|
|:-|:-|
|vertical|竖直|
|horizontal|水平|
object-fit 的合法值
|值|说明|
|:-|:-|
|contain|图像长边填满屏幕,短边区域会被填充⿊⾊|
|fillCrop|图像铺满屏幕,超出显示区域的部分将被截掉|
sound-mode 的合法值
|值|说明|
|:-|:-|
|speaker|扬声器|
|ear|听筒|
**Tips**
* 百度小程序 iOS 端不支持设置 orientation 属性;
......
......@@ -21,33 +21,112 @@
设置live-pusher组件的推流地址,推流视频模式等。
属性|类型 |默认值|必填|说明 :--|:--|:--|:--|:--|
url|string| |是|推流地址,支持RTMP协议。
mode |string|SD|否|推流视频模式,可取值:SD(标清), HD(高清), FHD(超清)。
aspect |string|3:2|否|视频宽高比例
muted|Boolean|false|否|是否静音。
enable-camera|Boolean|true|否|开启摄像头。
auto-focus|Boolean|true|否|自动聚集。
beauty|Number|0|否|美颜,取值范围 0-9(iOS取值范围为1) ,0 表示关闭。
whiteness|Number|0|否|美白,取值范围 0-9(iOS取值范围为1) ,0 表示关闭。
属性|类型 |默认值|必填|说明|平台差异说明| :--|:--|:--|:--|:--|:--|
url|string| |是|推流地址,支持RTMP协议。|
mode |string|SD|否|推流视频模式,可取值:SD(标清), HD(高清), FHD(超清)。|
aspect |string|3:2|否|视频宽高比例|
muted|Boolean|false|否|是否静音。|
enable-camera|Boolean|true|否|开启摄像头。|
auto-focus|Boolean|true|否|自动聚集。|
beauty|Number|0|否|美颜,取值范围 0-9(iOS取值范围为1) ,0 表示关闭。|
whiteness|Number|0|否|美白,取值范围 0-9(iOS取值范围为1) ,0 表示关闭。|
orientation|String|"vertical"|否|画面方向|
beauty|number|0|否|美颜,取值范围 0-9 ,0 表示关闭|
whiteness|number|0|否|美白,取值范围 0-9 ,0 表示关闭|
min-bitrate|Number|200|否|最小码率。|
max-bitrate|Number|1000|否|最大码率。|
audio-quality|string|high|否|高音质(48KHz)或低音质(16KHz),值为high, low|微信小程序1.7.0
waiting-image|string||否|进入后台时推流的等待画面|微信小程序1.7.0
waiting-image-hash|string||否|等待画面资源的MD5值|微信小程序1.7.0
zoom|boolean|false|否|调整焦距|微信小程序2.1.0
device-position|string|front|否|前置或后置,值为front, back|微信小程序2.3.0
background-mute|boolean|false|否|进入后台时是否静音|微信小程序1.7.0
remote-mirror|boolean|false|否|设置推流画面是否镜像,产生的效果在 live-player 反应到|微信小程序2.10.0
local-mirror|string|auto|否|控制本地预览画面是否镜像|微信小程序2.10.0
audio-reverb-type|number|0|否|音频混响类型|微信小程序2.10.0
enable-mic|boolean|true|否|开启或关闭麦克风|微信小程序2.10.0
enable-agc|boolean|false|否|是否开启音频自动增益|微信小程序2.10.0
enable-ans|boolean|false|否|是否开启音频噪声抑制|微信小程序2.10.0
audio-volume-type|string|voicecall|否|音量类型|微信小程序2.10.0
@statechange|EventHandle|||状态变化事件,detail = {code}|
@netstatus|EventHandle|||网络状态通知,detail = {info}|
@error|EventHandle|||渲染错误事件,detail = {errMsg, errCode}|
@bgmstart|EventHandle|||背景音开始播放时触发|微信小程序2.4.0
@bgmprogress|EventHandle|||背景音进度变化时触发,detail = {progress, duration}|微信小程序2.4.0
@bgmcomplete|EventHandle|||背景音播放完成时触发|微信小程序2.4.0
orientation 的合法值
|值|说明|
|:-|:-|
vertical|竖直|
|horizontal|水平|
local-mirror 的合法值
|值|说明|
|:-|:-|
|auto|前置摄像头镜像,后置摄像头不镜像|
|enable|前后置摄像头均镜像|
|disable|前后置摄像头均不镜像|
audio-reverb-type 的合法值
|值|说明|
|:-|:-|
|0|关闭|
|1|KTV|
|2|小房间|
|3|大会堂|
|4|低沉|
|5|洪亮|
|6|金属声|
|7|磁性|
audio-volume-type 的合法值
|值|说明|
|:-|:-|
|media|媒体音量|
|voicecall|通话音量|
网络状态数据(info)安卓
键名|说明 :--|:--|
videoBitrate | 当前视频编/码器输出的比特率,单位 kbps
audioBitrate | 当前音频编/码器输出的比特率,单位 kbps
videoFPS | 当前视频帧率
videoGOP | 当前视频 GOP,也就是每两个关键帧(I帧)间隔时长,单位 s
netSpeed | 当前的发送/接收速度
netJitter | 网络抖动情况,抖动越大,网络越不稳定
videoWidth | 视频画面的宽度
videoHeight | 视频画面的高度
网络状态数据(info)iOS
参数|类型 |说明 :--|:--|:--|
code|Number| code码
message|string| 具体的网络状态信息
```html
<template>
<view>
<live-pusher id='livePusher1' ref="livePusher" class="livePusher" url=""
mode="SD" :muted="true" :enable-camera="true" :auto-focus="true" :beauty="1" whiteness="2"
aspect="9:16" @statechange="statechange" @netstatus="netstatus" @error = "error"
></live-pusher>
<button class="btn" @click="start">开始推流</button>
<button class="btn" @click="pause">暂停推流</button>
<button class="btn" @click="resume">resume</button>
<button class="btn" @click="stop">停止推流</button>
<button class="btn" @click="snapshot">快照</button>
<button class="btn" @click="startPreview">开启摄像头预览</button>
<button class="btn" @click="stopPreview">关闭摄像头预览</button>
<button class="btn" @click="switchCamera">切换摄像头</button>
</view>
<view>
<live-pusher id='livePusher1' ref="livePusher" class="livePusher" url=""
mode="SD" :muted="true" :enable-camera="true" :auto-focus="true" :beauty="1" whiteness="2"
aspect="9:16" @statechange="statechange" @netstatus="netstatus" @error = "error"
></live-pusher>
<button class="btn" @click="start">开始推流</button>
<button class="btn" @click="pause">暂停推流</button>
<button class="btn" @click="resume">resume</button>
<button class="btn" @click="stop">停止推流</button>
<button class="btn" @click="snapshot">快照</button>
<button class="btn" @click="startPreview">开启摄像头预览</button>
<button class="btn" @click="stopPreview">关闭摄像头预览</button>
<button class="btn" @click="switchCamera">切换摄像头</button>
</view>
</template>
```
......
......@@ -20,13 +20,18 @@
|circles|Array||圆||
|controls|Array||控件||
|include-points|Array||缩放视野以包含所有给定的坐标点|App-nvue 2.1.5+、微信小程序、H5、百度小程序、支付宝小程序|
|enable-3D|Boolean|false|是否显示3D楼块|App-nvue 2.1.5+、微信小程序2.3.0|
|show-compass|Boolean|false|是否显示指南针|App-nvue 2.1.5+、微信小程序2.3.0|
|enable-overlooking|Boolean|false|是否开启俯视|App-nvue 2.1.5+、微信小程序2.3.0|
|enable-satellite|Boolean|false|是否开启卫星图|App-nvue 2.1.5+、微信小程序2.7.0|
|enable-traffic|Boolean|false|是否开启实时路况|App-nvue 2.1.5+、微信小程序2.7.0|
|show-location|Boolean||显示带有方向的当前定位点|微信小程序、H5、百度小程序、支付宝小程序|
|polygons|Array.`<polygon>`||多边形|App-nvue 2.1.5+、微信小程序、百度小程序、支付宝小程序|
|@markertap|EventHandle||点击标记点时触发|App-nvue 2.3.3+, App平台需要指定 marker 对象属性 id|
|@callouttap|EventHandle||点击标记点对应的气泡时触发||
|@controltap|EventHandle||点击控件时触发||
|@markertap|EventHandle||点击标记点时触发,点击标记点时触发,e.detail = {markerId}|App-nvue 2.3.3+, App平台需要指定 marker 对象属性 id|
|@callouttap|EventHandle||点击标记点对应的气泡时触发,e.detail = {markerId}||
|@controltap|EventHandle||点击控件时触发,e.detail = {controlId}||
|@regionchange|EventHandle||视野发生变化时触发|微信小程序、H5、百度小程序、支付宝小程序|
|@tap|EventHandle||点击地图时触发||
|@tap|EventHandle||点击地图时触发; App-nuve、微信小程序2.9支持返回经纬度||
|@updated|EventHandle||在地图渲染更新完成时触发|微信小程序、H5、百度小程序|
**注意**
......
#### 配置微信小程序插件
#### 配置小程序插件
#### 注意事项
小程序插件是可被添加到小程序内直接使用的功能组件,在不同的小程序内叫法可能略有区别。微信小程序、支付宝小程序中叫`插件`,百度小程序中叫`动态库`,方便起见,以下统一称为插件。
* 正式开始使用微信小程序插件之前需先在微信公众平台 -> 第三方设置 -> 插件管理处添加插件
* 要保证项目内微信小程序appid和申请插件的微信小程序appid一致插件才可使用
**参考文档**
- [微信小程序插件](https://developers.weixin.qq.com/miniprogram/dev/framework/plugin/using.html)
- [支付宝小程序插件](https://opendocs.alipay.com/mini/plugin/plugin-usage)
- [百度小程序动态库](https://smartprogram.baidu.com/docs/develop/framework/dynamiclib_use/)
#### 引入插件代码包
使用插件之前开发者需要在``manifest.json``中的``mp-weixin``内声明使用的插件,具体配置参照所用插件的开发文档
使用插件之前开发者需要在`manifest.json`中的各平台对应的字段内声明使用的插件,具体配置参照所用插件的开发文档
**代码示例**
```json
// 微信小程序
"mp-weixin": {
"plugins": {
"myPlugin": {
"version": "1.0.0",
"provider": "wxidxxxxxxxxxxxxxxxx"
}
}
}
// 支付宝小程序
"mp-alipay": {
"myPlugin": {
"version": "*",
"provider": "2019235609092837"
}
}
// 百度小程序
"mp-baidu": {
"dynamicLib": {
"myPlugin": {
"provider": "TheUniqueNameOwnedByThisDynamicLib"
}
}
}
```
"plugins": {
"pluginName": {
"version": "1.0.0",
"provider": "wxidxxxxxxxxxxxxxxxx"
#### 在页面中使用
在页面内使用插件需要在`pages.json`内对应页面的`style`节点下配置对应平台的`usingComponents`,示例如下。
`"hello-component": "plugin://myPlugin/hello-component"`为例,`key`(冒号前的`hello-component`)为在页面内使用的组件名称。`value`分为三段,`plugin`为协议(在百度小程序内为`dynamicLib`),`myPlugin`为插件名称即引入插件时的名称,`hello-component`为插件暴露的组件名称。
```json
// 微信小程序
{
"path": "pages/index/index",
"style": {
"mp-weixin": {
"usingComponents": {
"hello-component": "plugin://myPlugin/hello-component"
}
}
}
}
// 支付宝小程序
{
"path": "pages/index/index",
"style": {
"mp-alipay": {
"usingComponents": {
"hello-component": "plugin://myPlugin/hello-component"
}
}
}
}
// 百度小程序
{
"path": "pages/index/index",
"style": {
"mp-baidu": {
"usingComponents": {
"my-special-list": "dynamicLib://myDynamicLib/special-list"
}
}
}
}
```
#### 在分包内引入插件代码包
支付宝小程序、百度小程序不支持在分包内引入插件。此外如果项目使用了分包,在支付宝小程序内不可使用插件。本节内容仅针对微信小程序。
如果插件只在(同一个插件不能被多个分包同时引用)一个分包用到,可以单独配置到分包中,这样插件不会随主包加载,开发者可以在``pages.json``[subPackages](/collocation/pages?id=subpackages)中声明插件
**代码示例**
```
```json
"subPackages": [{
"root": "pagesA",
"pages": [{
......@@ -47,13 +117,10 @@
* 同一个插件不能被多个分包同时引用;
* 不能从分包外的页面直接跳入分包内的插件页面,需要先跳入分包内的非插件页面、再跳入同一分包内的插件页面。
#### 在页面中使用
请参照[微信小程序-使用插件](https://developers.weixin.qq.com/miniprogram/dev/framework/plugin/using.html)
#### 可能遇到的问题
* 某些插件可能会需要一些权限才能正常运行,请在``manifest.json``中的``mp-weixin``内配置``permission``[详见](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#permission)
* 某些插件可能会需要一些权限才能正常运行,请在`manifest.json`中的`mp-weixin`内配置`permission`[详见](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#permission)
* 微信开发工具提示 “插件版本不存在”,可能是插件开发文档示例代码中使用的版本已经不存在,请在声明插件处更改版本
......
#### navigation-bar
页面导航条配置节点,用于指定导航栏的一些属性。只能是 [page-meta](https://uniapp.dcloud.io/component/page-meta) 组件内的第一个节点,需要配合它一同使用。
**平台差异说明**
|App|H5|微信小程序|支付宝小程序|百度小程序|头条小程序|QQ小程序|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|√ 2.6.3+|2.6.3+|√ 2.9.0+|√|√|√|√|
从HBuilderX 2.9.3起,编译到所有平台均支持`navigation-bar`,但编译到微信时,受微信基础库版本限制;编译到其他平台不受平台版本限制。
**属性说明**
|属性|类型|默认值|必填|说明|最低版本
|:-|:-|:-|:-|:-|:-|
|title|string||否|导航条标题|微信基础库 2.9.0|
|loading|string|false|否|是否在导航条显示 loading 加载提示|微信基础库 2.9.0|
|front-color|string||否|导航条前景颜色值,包括按钮、标题、状态栏的颜色,仅支持 #ffffff 和 #000000|微信基础库 2.9.0|
|background-color|string||否|导航条背景颜色值,有效值为十六进制颜色|微信基础库 2.9.0|
|color-animation-duration|number|0|否|改变导航栏颜色时的动画时长,默认为 0 (即没有动画效果)|微信基础库 2.9.0|
|color-animation-timing-func|string|"linear"|否|改变导航栏颜色时的动画方式,支持 linear 、 easeIn 、 easeOut 和 easeInOut|微信基础库 2.9.0|
**注意**
- `navigation-bar` 目前支持的配置仅为上表所列,并不支持 page.json 中关于导航栏的所有配置
- `navigation-bar` 与 pages.json 的设置相冲突时,会覆盖 page.json 的配置
#### 示例代码
```
<template>
<page-meta>
<navigation-bar
:title="nbTitle"
:loading="nbLoading"
:front-color="nbFrontColor"
:background-color="nbBackgroundColor"
:color-animation-duration="2000"
color-animation-timing-func="easeIn"
/>
</page-meta>
<view class="content">
</view>
</template>
<script>
export default {
data() {
return {
nbTitle: '标题',
nbLoading: false,
nbFrontColor: '#000000',
nbBackgroundColor: '#ffffff'
}
},
onLoad() {
},
methods: {
}
}
</script>
```
#### page-meta
页面属性配置节点,用于指定页面的一些属性、监听页面事件。可部分替代pages.json的功能。
从微信基础库2.9.0开始,新增了`page-meta`组件,它是一个特殊的标签,有点类似html里的header标签。页面的背景色、原生导航栏的参数,都可以写在`page-meta`里。HBuilderX 2.6.3+ 支持了这个组件,并且全平台都实现了。
从某种意义讲,`page-meta`对pages.json有一定替代作用,可以让页面的配置和页面内容代码写在一个vue文件中。它还可以实现通过变量绑定来控制页面配置。但它的性能不如pages.json的配置,在新页面加载时,渲染速度还是pages.json方式的写法更快。
`page-meta`只能是页面内的第一个节点。可以配合 [navigation-bar](https://uniapp.dcloud.io/component/navigation-bar) 组件一同使用。
**平台差异说明**
|App|H5|微信小程序|支付宝小程序|百度小程序|头条小程序|QQ小程序|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|√(2.6.3+)|√(2.6.3+)|√|x|x|x|x|
|√ 2.6.3+|2.6.3+|√ 2.9.0+|√|√|√|√|
从HBuilderX 2.9.3起,编译到所有平台均支持`page-meta`,但编译到微信时,受微信基础库版本限制;编译到其他平台不受平台版本限制。
页面属性配置节点,用于指定页面的一些属性、监听页面事件。只能是页面内的第一个节点。可以配合 navigation-bar 组件一同使用。
**属性说明**
|属性|类型|默认值|必填|说明|最低版本
|属性|类型|默认值|必填|说明|版本要求
|:-|:-|:-|:-|:-|:-|
|background-text-style|string||否|下拉背景字体、loading 图的样式,仅支持 dark 和 light|微信基础库 2.9.0|
|background-color|string||否|窗口的背景色,必须为十六进制颜色值|微信基础库 2.9.0|
......@@ -19,11 +28,16 @@
|scroll-duration|number|300|否|滚动动画时长|微信基础库 2.9.0|
|page-style|string|""|否|页面根节点样式,页面根节点是所有页面节点的祖先节点,相当于 HTML 中的 body 节点|微信基础库 2.9.0|
|root-font-size|string|""|否|页面的根字体大小,页面中的所有 rem 单位,将使用这个字体大小作为参考值,即 1rem 等于这个字体大小|微信基础库 2.9.0|
|@resize||eventhandle||否|页面尺寸变化时会触发 resize 事件, event.detail = { size: { windowWidth, windowHeight } }|微信基础库 2.9.0|
|@scroll||eventhandle||否|页面滚动时会触发 scroll 事件, event.detail = { scrollTop }|微信基础库 2.9.0|
|@resize|eventhandle||否|页面尺寸变化时会触发 resize 事件, event.detail = { size: { windowWidth, windowHeight } }|微信基础库 2.9.0|
|@scroll|eventhandle||否|页面滚动时会触发 scroll 事件, event.detail = { scrollTop }|微信基础库 2.9.0|
|@scrolldone|eventhandle||否|如果通过改变 scroll-top 属性来使页面滚动,页面滚动结束后会触发 scrolldone 事件|微信基础库 2.9.0|
**注意**
- `<page-meta>` 目前支持的配置仅为上表所列,并不支持所有 page.json 的配置
- `<page-meta>` 与 pages.json 的设置相冲突时,会覆盖 page.json 的配置
#### 示例代码
```
......@@ -44,27 +58,29 @@
:background-color="nbBackgroundColor"
/>
</page-meta>
<view class="content">
</view>
</template>
<script>
export default {
data() {
return {
bgTextStyle: 'dark',
scrollTop: '200rpx',
bgColor: '#ff0000',
bgColorTop: '#00ff00',
bgColorBottom: '#0000ff',
nbTitle: '标题',
nbLoading: false,
nbFrontColor: '#000000',
nbBackgroundColor: '#ffffff'
}
},
onLoad() {
},
methods: {
}
export default {
data() {
return {
bgTextStyle: 'dark',
scrollTop: '200rpx',
bgColor: '#ff0000',
bgColorTop: '#00ff00',
bgColorBottom: '#0000ff',
nbTitle: '标题',
nbLoading: false,
nbFrontColor: '#000000',
nbBackgroundColor: '#ffffff'
}
},
onLoad() {
},
methods: {
}
}
</script>
```
......@@ -12,6 +12,8 @@
|mask-style|String|设置蒙层的样式||
|mask-class|String|设置蒙层的类名|app-nvue和头条小程序不支持|
|@change|EventHandle|当滚动选择,value 改变时触发 change 事件,event.detail = {value: value};value为数组,表示 picker-view 内的 picker-view-column 当前选择的是第几项(下标从 0 开始)|&nbsp;|
|@pickstart|eventhandle||当滚动选择开始时候触发事件|微信小程序2.3.1|
|@pickend|eventhandle||当滚动选择结束时候触发事件|微信小程序2.3.1|
**注意:**其中只可放置 `<picker-view-column/>` 组件,其他节点不会显示。
......
......@@ -89,7 +89,7 @@
|value|String|0|表示选中的日期,格式为"YYYY-MM-DD"||
|start|String||表示有效日期范围的开始,字符串格式为"YYYY-MM-DD"||
|end|String||表示有效日期范围的结束,字符串格式为"YYYY-MM-DD"||
|fields|String|day|有效值 year,month,day,表示选择器的粒度|H5、微信小程序、百度小程序、头条小程序|
|fields|String|day|有效值 year,month,day,表示选择器的粒度|H5、App 2.6.3+、微信小程序、百度小程序、头条小程序|
|@change|EventHandle||value 改变时触发 change 事件,event.detail = {value: value}||
|@cancel|EventHandle||取消选择时触发||
|disabled|Boolean|false|是否禁用|&nbsp;|
......
......@@ -7,14 +7,14 @@
|:- |:- |:- |:- |:- |
|percent |Float |无 |百分比0~100 | |
|show-info |Boolean |false |在进度条右侧显示百分比 | |
|border-radius|number/string|0|圆角大小|微信基础库2.3.1+、QQ小程序|
|font-size|number/string|16|右侧百分比字体大小|微信基础库2.3.1+、QQ小程序|
|border-radius|number/string|0|圆角大小|app-nvue、微信基础库2.3.1+、QQ小程序|
|font-size|number/string|16|右侧百分比字体大小|app-nvue、微信基础库2.3.1+、QQ小程序|
|stroke-width |Number |6 |进度条线的宽度,单位px | |
|activeColor |Color |#09BB07(百度为#E6E6E6) |已选择的进度条的颜色 | |
|backgroundColor|Color |#EBEBEB |未选择的进度条的颜色 | |
|active |Boolean |false |进度条从左往右的动画 | |
|active-mode |String |backwards |backwards: 动画从头播;forwards:动画从上次结束点接着播|App、H5、微信小程序、QQ小程序 |
|duration|number|30|进度增加1%所需毫秒数|微信基础库2.8.2+|
|duration|number|30|进度增加1%所需毫秒数|App-nvue2.6.1+、微信基础库2.8.2+|
|@activeend |EventHandle| |动画完成事件 |微信小程序 |
**示例** [查看演示](https://uniapp.dcloud.io/h5/pages/component/progress/progress)
......
......@@ -21,6 +21,8 @@
|refresher-default-style|string |"black"|设置自定义下拉刷新默认样式,支持设置 black,white,none,none 表示不使用默认样式|app-vue 2.5.12+,微信小程序基础库2.10.1+|
|refresher-background |string |"#FFF" |设置自定义下拉刷新区域背景颜色|app-vue 2.5.12+,微信小程序基础库2.10.1+|
|refresher-triggered |boolean |false |设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发|app-vue 2.5.12+,微信小程序基础库2.10.1+|
|enable-flex|boolean|false|启用 flexbox 布局。开启后,当前节点声明了 display: flex 就会成为 flex container,并作用于其孩子节点。|微信小程序 2.7.3|
|scroll-anchoring|boolean|false|开启 scroll anchoring 特性,即控制滚动位置不随内容变化而抖动,仅在 iOS 下生效,安卓下可参考 CSS overflow-anchor 属性。|微信小程序 2.8.2|
|@scrolltoupper |EventHandle| |滚动到顶部/左边,会触发 scrolltoupper 事件 | |
|@scrolltolower |EventHandle| |滚动到底部/右边,会触发 scrolltolower 事件 | |
|@scroll |EventHandle| |滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY} |&nbsp;|
......
......@@ -21,11 +21,14 @@
|selection-start|Number|-1|光标起始位置,自动聚焦时有效,需与selection-end搭配使用|微信小程序、App、H5、百度小程序、头条小程序、QQ小程序|
|selection-end|Number|-1|光标结束位置,自动聚焦时有效,需与selection-start搭配使用|微信小程序、App、H5、百度小程序、头条小程序、QQ小程序|
|adjust-position|Boolean|true|键盘弹起时,是否自动上推页面|App、微信小程序、百度小程序、QQ小程序|
|disable-default-padding|boolean|false|是否去掉 iOS 下的默认内边距|微信小程序2.10.0|
|hold-keyboard|boolean|false|focus时,点击页面的时候不收起键盘|微信小程序2.8.2|
|@focus|EventHandle||输入框聚焦时触发,event.detail = { value, height },height 为键盘高度|仅微信小程序、App(HBuilderX 2.0+ [nvue uni-app模式](http://ask.dcloud.net.cn/article/36074)) 、QQ小程序支持 height|
|@blur|EventHandle||输入框失去焦点时触发,event.detail = {value, cursor}||
|@linechange|EventHandle||输入框行数变化时调用,event.detail = {height: 0, heightRpx: 0, lineCount: 0}|头条小程序不支持|
|@input|EventHandle||当键盘输入时,触发 input 事件,event.detail = {value, cursor}, @input 处理函数的返回值并不会反映到 textarea 上||
|@confirm|EventHandle||点击完成时, 触发 confirm 事件,event.detail = {value: value}|微信小程序、百度小程序、QQ小程序|
|@confirm|EventHandle||点击完成时, 触发 confirm 事件,event.detail = {value: value}|微信小程序、百度小程序、QQ小程序|
|@keyboardheightchange|eventhandle||键盘高度发生变化的时候触发此事件,event.detail = {height: height, duration: duration}|微信小程序2.7.0|
**示例** [查看示例](https://uniapp.dcloud.io/h5/pages/component/textarea/textarea)
......
......@@ -40,6 +40,7 @@
|vslide-gesture-in-fullscreen|Boolean|true|在全屏模式下,是否开启亮度与音量调节手势|微信小程序|
|ad-unit-id|String||视频前贴广告单元ID,更多详情可参考开放能力[视频前贴广告](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/ad/video-patch-ad.html)|微信小程序|
|poster-for-crawler|String||用于给搜索等场景作为视频封面展示,建议使用无播放 icon 的视频封面图,只支持网络地址|微信小程序|
|ad-unit-id|string||视频前贴广告单元ID,更多详情可参考开放能力[视频前贴广告]|微信小程序2.8.1|
|@play|EventHandle||当开始/继续播放时触发play事件|头条小程序不支持|
|@pause|EventHandle||当暂停播放时触发 pause 事件|头条小程序不支持|
|@ended|EventHandle||当播放到末尾时触发 ended 事件|头条小程序不支持|
......@@ -49,9 +50,38 @@
|@error|EventHandle||视频播放出错时触发|头条小程序不支持|
|@progress|EventHandle||加载进度变化时触发,只支持一段加载。event.detail = {buffered},百分比|微信小程序、H5|
|@loadedmetadata|EventHandle||视频元数据加载完成时触发。event.detail = {width, height, duration}|微信小程序、H5|
|@fullscreenclick|EventHandle||视频播放全屏播放时点击事件。event.detail = { screenX:"Number类型,点击点相对于屏幕左侧边缘的 X 轴坐标", screenY:"Number类型,点击点相对于屏幕顶部边缘的 Y 轴坐标", screenWidth:"Number类型,屏幕总宽度", screenHeight:"Number类型,屏幕总高度"}|App 2.6.3+|
|@controlstoggle|eventhandle||切换 controls 显示隐藏时触发。event.detail = {show}|微信小程序2.9.5|
`<video>` 默认宽度 300px、高度 225px,可通过 css 设置宽高。
##### direction 的合法值
|值|说明|
|:-|:-|
|0|正常竖向|
|90|屏幕逆时针90度|
|-90|屏幕顺时针90度|
##### object-fit 的合法值
|值|说明|
|:-|:-|
|contain|包含|
|fill|填充|
|cover|覆盖|
##### play-btn-position 的合法值
|值|说明|
|:-|:-|
|bottom|controls bar上|
|center|视频中间|
**示例** [查看示例](https://uniapp.dcloud.io/h5/pages/component/video/video)
```html
......
......@@ -48,6 +48,54 @@
|mp-weixin|微信小程序|
|mp-alipay|支付宝小程序|
|mp-baidu|百度小程序|
## 资源路径说明
### 模板内引入静态资源
> `template`内引入静态资源,如`image`、`video`等标签的`src`属性时,可以使用相对路径或者绝对路径,形式如下
```html
<!-- 绝对路径,/static指根目录下的static目录,在cli项目中/static指src目录下的static目录 -->
<image class="logo" src="/static/logo.png"></image>
<!-- 相对路径 -->
<image class="logo" src="../../static/logo.png"></image>
```
### js文件引入
> `js`文件或`script`标签内(包括renderjs等)引入`js`文件时,可以使用相对路径和绝对路径,形式如下
```js
// 绝对路径,@指向项目根目录,在cli项目中@指向src目录
import add from '@/common/add.js'
// 相对路径
import add from '../../common/add.js'
```
### css引入静态资源
> `css`文件或`style标签`内引入`css`文件时(scss、less文件同理),只能使用相对路径
```css
/* 相对路径 */
@import "../../common/base.css";
/* 或者 */
@import url("../../common/base.css");
```
> `css`文件或`style标签`内引用的图片路径可以使用相对路径也可以使用绝对路径,需要注意的是,小程序端css文件不允许引用本地文件。
```css
/* 绝对路径 */
background-image: url(/static/logo.png);
/* 相对路径 */
background-image: url(../../static/logo.png);
```
**Tips**
- 引入字体图标请参考,[自体图标](frame?id=字体图标)
## 生命周期
......@@ -371,6 +419,10 @@ uni-app 提供内置 CSS 变量
```html
<template>
<!-- HBuilderX 2.6.3+ 新增 page-meta, 详情:https://uniapp.dcloud.io/component/page-meta -->
<page-meta>
<navigation-bar />
</page-meta>
<view>
<view class="status_bar">
<!-- 这里是状态栏 -->
......@@ -467,6 +519,7 @@ uni-app 提供内置 CSS 变量
- 小程序不支持在css中使用本地文件,包括本地的背景图和字体文件。需以base64方式方可使用。App端在v3模式以前,也有相同限制。v3编译模式起支持直接使用本地背景图和字体。
- 网络路径必须加协议头 ``https``。
- 从 [http://www.iconfont.cn](http://www.iconfont.cn) 上拷贝的代码,默认是没加协议头的。
- 从 [http://www.iconfont.cn](http://www.iconfont.cn) 上下载的字体文件,都是同名字体(字体名都叫iconfont,安装字体文件时可以看到),在nvue内使用时需要注意,此字体名重复可能会显示不正常,可以使用工具修改。
- 使用本地路径图标字体需注意:
1. 为方便开发者,在字体文件小于 40kb 时,``uni-app`` 会自动将其转化为 base64 格式;
2. 字体文件大于等于 40kb,仍转换为 base64 方式使用的话可能有性能问题,如开发者必须使用,则需自己将其转换为 base64 格式使用,或将其挪到服务器上,从网络地址引用;
......
......@@ -18,29 +18,41 @@ vue页面在App端,默认是被系统的webview渲染的(不是手机自带
小程序不存在此情况。所以如果你的H5和小程序界面正常,而App界面异常,大多是因为css兼容性。解决这类问题,可以在caniuse查询,也可以使用一个较低版本的chrome浏览器在H5端测试。
app端nvue页面,不存在浏览器兼容问题,它自带一个统一的原生渲染引擎,不依赖webview。
Android4.4对应的webview是chrome37。各端浏览器内核的详情查阅,参考:[关于手机webview内核、默认浏览器、各家小程序的渲染层浏览器的区别和兼容性](https://ask.dcloud.net.cn/article/1318)
- 原生组件层级问题
H5没有原生组件概念问题,非H5端有原生组件并引发了原生组件层级高于前端组件的概念,要遮挡video、map等原生组件,请使用cover-view组件。
2. 使用了非H5端不支持的API,比如document、xmlhttp、cookie、window、location、navigator、localstorage、websql、indexdb、webgl等对象。如果你的代码没有直接使用这些,那很可能是引入的三方库使用了这些。如果是后者,去[插件市场](https://ext.dcloud.net.cn/)搜索替代方案。要知道非H5端的js是运行在一个独立的js core或v8下,并不是运行在浏览器里。
3. 使用了非H5端不支持的vue语法,比如v-html指令、受小程序自定义组件限制的写法,[详见](/use)
3. 使用了非H5端不支持的vue语法,受小程序自定义组件限制的写法,[详见](/use)
4. 不要在引用组件的地方在组件属性上直接写 style="xx",要在组件内部写样式
5. `url(//alicdn.net)`等路径,改为`url(https://alicdn.net)`,因为在App端//是file协议
6. 很多人在H5端联网时使用本地测试服务地址(localhost或127.0.0.1),这样的联网地址手机端必然无法访问,请使用手机可访问的IP进行联网
6. 很多人在H5端联网时使用本地测试服务地址(localhost或127.0.0.1),这样的联网地址手机App端是无法访问的,请使用手机可访问的IP进行联网
### H5正常但小程序异常的可能性
1. 同上
2. 小程序要求连接的网址都要配白名单
2. v-html在h5和app-vue均支持,但小程序不支持
3. 小程序要求连接的网址都要配白名单
### 小程序正常但App异常的可能性
1. vue页面在App端是被系统的webview渲染的(不是手机自带浏览器,是rom的webview),在较老的手机上,比如Android4.4、5.0或iOS8,很多css是不支持的,所以不要使用太新的css,会导致界面异常。注意这不意味着不能使用flex,Android4.4也支持flex,只是不要使用太新的css。可以找Android4.4手机或使用pc模拟器实际测试下,大多数国产Android模拟器都是4.4或5.0。小程序不存在此情况。所以如果你的H5和小程序界面正常,而App界面异常,大多是因为css兼容性。解决这类问题,可以在caniuse查询,也可以使用一个较低版本的chrome浏览器在H5端测试。Android4.4对应的webview是chrome37,如找不到老版chrome,也可以下载老版HBuilder(在HBuilderX下载页面底部有“上一代HBuilder下载”),在老HBuilder的右上角边改边看模式里是chrome37内核,可以把uni-app的H5版运行起来,将url粘贴到边改边看的浏览器中,点右键可以审查元素,排查不支持的css。
vue页面在App端的渲染引擎默认是系统webview(不是手机自带浏览器,是rom的webview),在较老的手机上,比如Android4.4、5.0或iOS8,一些新出的css语法是不支持的。注意这不意味着不能使用flex,Android4.4也支持flex,只是不要使用太新的css。可以找Android4.4手机或使用pc模拟器实际测试下,大多数国产Android模拟器都是4.4或5.0。
小程序不存在浏览器兼容问题,它内置了几十M自己的定制webview。所以如果你的H5和小程序界面正常,而App界面异常,大多是因为css兼容性。
解决这类问题:
1. 放弃老款手机支持
2. 不用使用太新的css语法,可以在caniuse查询
3. 从 uni-app 2.5.3 起,Android端支持引入腾讯x5浏览器内核,可以抹平低端Android的浏览器兼容性问题,[详见x5使用指南](https://ask.dcloud.net.cn/article/36806)
### 小程序或App正常,但H5异常的可能性
1. 使用了小程序原生组件,而没有使用vue标准的跨端组件。wxcomponets只有小程序和App才支持。
1. 在 uni-app 2.4.7 以前,H5端不支持微信小程序自定义组件,即wxcomponets下的组件,此时可能产生兼容问题。从 2.4.7 起,H5也支持微信自定义组件,不再存在这这方面兼容问题。
2. App端使用了App特有的API和功能,比如plus、Native.js、subNVue、原生插件等
3. 小程序端使用了小程序专用的功能,比如微信登录、小程序插件、微信小程序云开发。对于云开发,建议使用uniCloud。
### App正常,小程序、H5异常的可能性
1. 使用了App端特有的plus、Native.js、subNVue、原生插件等功能
1. 代码中使用了App端特有的plus、Native.js、subNVue、原生插件等功能
### 使用 Vue.js 的注意
......@@ -131,17 +143,16 @@ H5没有原生组件概念问题,非H5端有原生组件并引发了原生组
...
```
* 组件方面:支持 ``mpvue`` 组件、支持普通 ``vue`` 组件、不支持小程序自定义组件、不支持 ``nvue``
* H5 版 ``uni-app`` 全支持 ``vue`` 语法,所以可能造成部分写法在 H5 端生效,在小程序或 App 端不生效。
* H5 校验了更严格的 ``vue`` 语法,有些写法不规范会报警,比如: ``data`` 后面写对象会报警,必须写 ``function``;不能修改 ``props`` 的值;组件最外层 ``template`` 节点下不允许包含多个节点等。
* 编译为 H5 版后生成的是单页应用(SPA)。
* 如果遇到了白屏或网络不给力的提示,一般是跨域问题,网络请求(request、uploadFile、downloadFile等)在浏览器存在跨域限制,需服务端配合才能跨域。解决方案有2种:
1. 服务器打开跨域限制;
2. 本地浏览器安装跨域插件,参考:[Chrome 跨域插件免翻墙安装](http://ask.dcloud.net.cn/article/35267)[firefox跨域插件](https://addons.mozilla.org/zh-CN/firefox/addon/access-control-allow-origin/)
* 如果遇到跨域造成js无法联网,注意网络请求(request、uploadFile、downloadFile等)在浏览器存在跨域限制,解决方案有3种:
1. 服务器打开跨域限制
2. 本地浏览器安装跨域插件,参考:[Chrome 跨域插件免翻墙安装](http://ask.dcloud.net.cn/article/35267)[firefox跨域插件](https://addons.mozilla.org/zh-CN/firefox/addon/access-control-allow-origin/)
3. 使用HBuilderX的内置浏览器,这个浏览器定制过,可以跨域。
* APP 和小程序的导航栏和 ``tabbar`` 均是原生控件,元素区域坐标是不包含原生导航栏和 ``tabbar`` 的;而 H5 里导航栏和 ``tabbar`` 是 div 模拟实现的,所以元素坐标会包含导航栏和tabbar的高度。为了优雅的解决多端高度定位问题,``uni-app`` 新增了2个css变量:``--window-top````--window-bottom``,这代表了页面的内容区域距离顶部和底部的距离。举个实例,如果你想在原生``tabbar`` 上方悬浮一个菜单,之前写 ``bottom:0``。这样的写法编译到 h5 后,这个菜单会和 ``tabbar`` 重叠,位于屏幕底部。而改为使用 ``bottom:var(--window-bottom)``,则不管在 app 下还是在h5下,这个菜单都是悬浮在 ``tabbar`` 上浮的。这就避免了写条件编译代码。当然仍然也可以使用 H5 的条件编译处理界面的不同。
......
# uni-app 更新日志
======================================
#### 2.6.4.20200310-alpha
* 【uni-app插件】
+ App平台 新增 uni.shareWithSystem 调用系统分享组件发送分享消息 [详情](https://uniapp.dcloud.io/api/plugins/share?id=sharewithsystem)
+ App平台 修复 非 v3编译模式 webview 组件高度不正确的Bug [详情](https://ask.dcloud.net.cn/question/89683)
+ App平台 修复 v3版本 uni.request 的 header 内使用小写的 content-type 会报错的Bug [详情](https://ask.dcloud.net.cn/question/90214)
+ App平台 修复 v3版本 vue 页面热刷新时 onLoad 参数被重复编码的Bug
+ App平台 修复 v3版本 vue map 组件 scale 属性和 tap 事件无效问题 [详情](https://ask.dcloud.net.cn/question/89491)
+ App-Android平台 修复 v3版本 wgt热更新后无法打开新增页面的Bug [详情](https://ask.dcloud.net.cn/question/88829)
+ App-iOS平台 修复 v3版本 wgt热更新后 plus.runtime.restart 卡在启动页的Bug [详情](https://ask.dcloud.net.cn/question/89966)
+ App-iOS平台 修复 v3版本 video 组件部分情况无法显示的bug
+ App-iOS平台 修复 nvue web-view 组件加载本地文件显示空白的Bug [详情](https://ask.dcloud.net.cn/question/90114)
* 【5+App插件】
+ 优化 uni-AD 激励视频广告内部加载逻辑,完善错误信息 [详情](https://ask.dcloud.net.cn/article/36718#rewarderror)
#### 2.6.3.20200305-alpha
* 【uni-app插件】
+ 新增 页面属性配置节点 page-meta [详情](https://uniapp.dcloud.io/component/page-meta)
+ App平台、H5平台 调整 canvas 组件大小改变时不再丢失内容
+ App平台 【重要】 老版自定义组件编译模式将于4月1日下线 [详情](https://ask.dcloud.net.cn/article/36988)
+ App平台 【重要】 新增 vue页面引用的js,支持原生混淆(限v3) [详情](https://ask.dcloud.net.cn/article/36437)
+ App平台 新增 video 组件全屏点击事件 fullscreenclick
+ App平台 优化 真机运行时,强化错误提示,输出vue警告日志(限v3)[详情](https://ask.dcloud.net.cn/question/89193)
+ App平台 优化 vue 页面 input、textarea 组件支持 show-confirm-bar 属性(限v3)
+ App平台 优化 picker 组件支持 fields 属性
+ App平台 优化 支持更多 crypto 加密库 [详情](https://ask.dcloud.net.cn/question/89334)
+ App平台 修复 v3版本 uni.getStorage 部分情况下获取数据格式错误的Bug [详情](https://ask.dcloud.net.cn/question/87866)
+ App平台 修复 v3版本 $nextTick 无法返回 Promise 的Bug
+ App平台 修复 v3版本 点击 tabBar 会重复触发 onShow 的Bug [详情](https://ask.dcloud.net.cn/question/87497)
+ App平台 修复 v3版本 input、textarea 组件 cursor-spacing 属性为字符串时报错的Bug
+ App平台 修复 v3版本 picker-view 组件内的 navigator 组件无法跳转的Bug [详情](https://ask.dcloud.net.cn/question/87794)
+ App平台 修复 v3版本 input 组件使用 v-model 后 input 事件会重复触发的Bug
+ App平台 修复 v3版本 多个 tabBar 页面同时使用 subNvue 时,点击蒙层关闭不正常的Bug [详情](https://ask.dcloud.net.cn/question/89050)
+ App平台 修复 v3版本 部分情况组件渲染不正确的Bug [#1334](https://github.com/dcloudio/uni-app/issues/1334)
+ App平台 修复 v3版本 部分情况文本节点渲染不正确的Bug [详情](https://ask.dcloud.net.cn/question/89557)
+ App平台 修复 v3版本 vue map 组件动态修改 v-if 无效隐藏的Bug [详情](https://ask.dcloud.net.cn/question/89986)
+ App平台 修复 v3版本 nvue map API translateMarker 无效的Bug
+ App平台 修复 v3版本 cover-view 组件部分样式渲染不正确的Bug [详情](https://ask.dcloud.net.cn/question/89609)
+ App平台 修复 v3版本 使用 renderjs 后,低版本系统运行异常的Bug [详情](https://github.com/dcloudio/uni-app/issues/1373)
+ App平台 修复 v3版本 真机运行 修改 pages.json 触发多次同步手机端文件的Bug
+ App平台 修复 非v3模式 app.vue 中使用 css 变量不生效的Bug [详情](https://ask.dcloud.net.cn/question/89367)
+ App平台 修复 uni.uploadFile 的 formData 属性中不能包含数值类型的Bug [详情](https://ask.dcloud.net.cn/question/87951)
+ App平台 修复 nvue页面 movable-view 组件 inertia 属性不生效的Bug
+ App平台 修复 uni.request 在 content-type 为 application/json 时自动对 data 序列化 [详情](https://ask.dcloud.net.cn/question/89474)
+ App平台 修复 某些情况下事件接收参数不正确的Bug [详情](https://ask.dcloud.net.cn/question/89818)
+ App-Android平台 修复 nvue map 组件不设置 markers 属性导致tap事件不触发的Bug
+ App-Android平台 修复 云打包后 uni.getImageInfo() 获取本地图片信息可能会触发失败回调的Bug
+ App-iOS平台 修复 nvue image 组件使用相对路径加载图片可能会不显示的Bug [详情](https://ask.dcloud.net.cn/question/89117)
+ App-iOS平台 修复 video 组件设置 muted 为0静音后再设置为1时无法重新开启声音的bug [详情](https://ask.dcloud.net.cn/question/89106)
+ H5平台 优化 内置组件响应鼠标事件,可在PC浏览器正常拖动和滚动
+ H5平台 优化 “网络不给力”现象,调整页面加载超时时间和提示文案
+ H5平台 优化 修改代码后提升页面热更新速度
+ H5平台 修复 picker 组件设置 fields 属性后返回值格式错误的Bug
+ H5平台 修复 picker 组件设置 value 部分情况无法触发视图更新的Bug [#1162](https://github.com/dcloudio/uni-app/issues/1162)
+ H5平台 修复 uni.createSelectorQuery 返回的节点信息中 bottom 值错误的Bug [详情](https://ask.dcloud.net.cn/question/85968)
+ H5平台 修复 map 组件无法同时加载多个实例的Bug [详情](https://ask.dcloud.net.cn/question/88735)
+ H5平台 修复 movable-view 组件限制移动方向后,拖动操作会出现中断的Bug
+ H5平台 修复 tranparentTitle 失效的Bug [详情](https://ask.dcloud.net.cn/question/89354)
+ H5平台 修复 部分浏览器中 uni.getSystemInfo 获取 windowTop、windowHeight 值错误的Bug [#1348](https://github.com/dcloudio/uni-app/issues/1348)
+ H5平台 修复 在页面 onReady 生命周期内绘制canvas 绘制内容会一闪消失的Bug [详情](https://ask.dcloud.net.cn/question/81675)
+ 小程序平台 修复 easycom 无法正常使用驼峰式命名组件的Bug [详情](https://ask.dcloud.net.cn/question/87925)
+ 小程序平台 修复 App.vue 包含 template 节点时,导致 app.json 生成错误的Bug [#1351](https://github.com/dcloudio/uni-app/issues/1351)
+ 微信小程序平台 修复 控制台没有输出生命周期内异常的Bug [详情](https://ask.dcloud.net.cn/question/89440)
+ 钉钉小程序平台 调整 uni.request 在 content-type 为 application/json 时自动序列化 data
+ uni-ui 修复 NoticeBar 通告栏组件关闭按钮不显示的Bug
+ uni-ui 修复 IndexedList 索引列表组件非全屏显示时,右侧索引位置与点击位置偏移的Bug
* 【5+App插件】
+ 新增 原生标题栏支持自定义返回按钮、标题居左、标题栏背景图、背景渐变色等功能 [文档](https://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewTitleNViewStyles)
+ 新增 视频播放控件 VideoPlayer 支持全屏播放时点击事件 fullscreenclick [文档](https://www.html5plus.org/doc/zh_cn/video.html#plus.video.VideoPlayerEvents)
+ Android平台 新增 原生隐私与政策提示框配置 [详情](https://ask.dcloud.net.cn/article/36937)
+ Android平台 优化 云端打包默认需要的系统权限,支持配置是否自动添加第三方SDK需要的权限 [详情](https://ask.dcloud.net.cn/article/36982)
+ Android平台 优化 Webview窗口加载可缩放页面时默认隐藏系统缩放控制条 [详情](https://ask.dcloud.net.cn/question/89088)
+ Android平台 修复 获取系统语言信息 plus.os.language 字符中没有包含地区信息的Bug
+ iOS平台 新增 适配iOS13+引入的暗黑模式 DarkMode [文档](https://ask.dcloud.net.cn/article/36995)
+ iOS平台 新增 应用使用Push(消息推送)模块后启动时不弹出“发送通知”系统授权框的配置方法 [详情](https://ask.dcloud.net.cn/article/36955#push)
+ iOS平台 修复 视频播放控件 VideoPlayer 设置 objectFit 属性不生效的bug [详情](https://ask.dcloud.net.cn/question/88221)
+ iOS平台 修复 使用WKWebview内核 plus.orientation.getCurrentOrientation 无法获取设备方向信息的Bug
* 【uni小程序SDK】
+ 新增 启动小程序支持传入参数及直达指定页面 [详情](https://ask.dcloud.net.cn/docs/#https://ask.dcloud.net.cn/article/37010)
+ 新增 关闭当前小程序方法及小程序关闭回调方法 [详情](https://ask.dcloud.net.cn/docs/#https://ask.dcloud.net.cn/article/37014)
+ 新增 获取当前运行的小程序appid方法
+ Android平台 修复启动小程序后调用扫码可能引起的闪退问题 [详情](https://ask.dcloud.net.cn/question/89827)
#### 2.6.1.20200226
* 【uni-app插件】
+ App-iOS平台 修复 非 v3 编译模式真机运行时提示框架版本不一致的Bug
#### 2.6.0.20200223
* 【uni-app插件】
+ 【重要】uniCloud公测。serverless模式,全端云开发,js编写后端代码 [详情](https://uniapp.dcloud.io/uniCloud/README)
......@@ -77,8 +163,6 @@
+ iOS平台 修复 在非刘海屏手机获取应用的安全区域 plus.navigator.getSafeAreaInsets 返回 deviceTop 数据可能不正确的Bug
+ iOS平台 修复 调用 plus.gallery.pick 选择相册中的照片后, 连续快速点击完成按钮可能会引起应用崩溃的Bug [详情](https://ask.dcloud.net.cn/question/87123)
+ iOS平台 修复 视频播放控件 VideoPlayer 暂停播放后手势快进或后退操作时,没有更新进度条上显示时间的Bug
+ Hello H5+ 新增 应用内广告示例 uni-AD 页面
+ Hello H5+ 修复 iOS平台 使用WKWebview导致微信和支付宝支付示例总是提示获取订单失败的Bug
#### 2.5.11.20200212-alpha
* 【uni-app插件】
......@@ -89,7 +173,6 @@
+ 新增 uni-AD 支持激励视频广告 [文档](https://www.html5plus.org/doc/zh_cn/ad.html#plus.ad.createRewardedVideoAd)
+ 新增 推送 支持异步获取客户端推送标识接口 plus.push.getClientInfoAsync,解决同步接口可能返回空数据的问题 [文档](https://www.html5plus.org/doc/zh_cn/push.html#plus.push.getClientInfoAsync)
+ iOS平台 修复 HBuilderX2.5.8引出的视频播放控件无法播放直播地址的Bug [详情](https://ask.dcloud.net.cn/question/88393)
+ Hello H5+ 新增 激励视频广告演示示例
#### 2.5.10.20200205-alpha
* 【uni-app插件】
......@@ -199,9 +282,8 @@
+ 【重要】Android平台 新增 腾讯X5内核。可解决rom自定义主题字体适配、低端机浏览器兼容性等问题 [详情](https://ask.dcloud.net.cn/article/36806)
+ Android平台 修复 Webview窗口创建并显示后立即关闭可能出现闪退的Bug [详情](https://ask.dcloud.net.cn/question/86918)
+ iOS平台 修复 在非刘海屏手机获取应用的安全区域 plus.navigator.getSafeAreaInsets 返回 deviceTop 数据可能不正确的Bug
+ Hello H5+ 新增 应用内广告示例 uni-AD 页面
#### 2.5.1.20190103
#### 2.5.1.20200103
* 【uni-app插件】
+ 【重要】App端重大架构升级 `v3编译器`,更快的启动速度、更好的性能、更多vue语法支持、可webview调试视图层。新项目默认开启v3,可通过【manifest.json-App其他设置】开启关闭v3 [详情](https://ask.dcloud.net.cn/article/36599)
+ 【重要】H5平台、QQ小程序 支持运行微信小程序组件 [详情](https://uniapp.dcloud.io/h5/pages/template/vant-button/vant-button)
......@@ -509,6 +591,8 @@
+ iOS平台 修复 用户拒绝访问相机后,调用摄像头拍照或录像时不会触发错误回调的Bug
+ iOS平台 修复 配置使用高德或百度地图后,定位默认没有使用gcj02坐标的Bug
+ iOS平台 修复 系统日期(plus.nativeUI.pickDate)、时间(plus.nativeUI.pickTime)选择框与系统提示框遮罩颜色不一致的Bug
* 【mui】
+ iOS平台 wkwebview下,plusready未触发时调用mui.ajax,在控制台输出正确的告警提醒
#### 2.4.2.20191115
* 【uni-app插件】
......@@ -1029,7 +1113,6 @@
+ iOS平台 修复 Webview窗口背景设置为深色时,Tab栏上面会出现白条的Bug [详情](https://ask.dcloud.net.cn/question/77442)
+ iOS平台 修复 Webview窗口使用WKWebview内核时overrideUrlLoading方法无效的Bug [详情](https://ask.dcloud.net.cn/question/78173)
+ iOS平台 修复 蓝牙(Bluetooth)开始搜索后不停止直接关闭页面可能会引起应用崩溃的Bug
+ Hello H5+ 适配iOS平台二维码扫描控件在WKWebview中显示高度可能不正确的Bug(position属性值由fixed调整为absolute)
#### 2.3.0.20190919-alpha
* 【uni-app插件】
......@@ -1148,7 +1231,6 @@
+ iOS平台 修复 nvue页面中video标签设置封面图片(poster)后动态修改src属性可能引起应用崩溃的Bug [详情](https://ask.dcloud.net.cn/question/77353)
+ iOS平台 修复 nvue页面中video标签中退出全屏后覆盖元素显示不正常的Bug
+ iOS平台 修复 uni原生插件实现代理方法(application:handleOpenURL:)不触发,导致无法获取第三方应用返回数据的Bug
+ Hello H5+ 适配iOS平台二维码扫描页面在WKWebview中position定位问题(fixed调整为absolute)
#### 2.2.4.20190823-alpha
* 【uni-app插件】
......
......@@ -12,6 +12,9 @@
#### 如果你使用过mpvue
几乎不用学习,uni-app对vue语法的支持是mpvue的超集。这里有篇[mpvue转uni-app指南](http://ask.dcloud.net.cn/article/34945)
#### 如果你熟悉小程序,但不熟悉vue
参考三方总结[https://segmentfault.com/a/1190000015684864](https://segmentfault.com/a/1190000015684864)
#### 三方培训机构视频
如下是三方专业培训机构的视频教程
* [《uni-app 商业级应用实战》](https://ke.qq.com/course/379043?from=800006421),出品人:腾讯课堂NEXT学院。亮点:腾讯课堂官方出品;不懂 vue 的工程师也可快速学习;从入门到实战都包括。
......
`uniCloud` 是 DCloud 联合阿里云、腾讯云,为 uni-app 的开发者提供的基于 serverless 模式和 js 编程的云开发平台。
> 从[HBuilderX 2.5.8](https://www.dcloud.io/hbuilderx.html)起支持。目前仅支持阿里云,春节后开放腾讯云。
> 从[HBuilderX 2.5.8](https://www.dcloud.io/hbuilderx.html)起支持。目前仅支持阿里云,近期会开放腾讯云。
### uniCloud 的价值
......
此差异已折叠。
......@@ -51,7 +51,7 @@ exports.main = async (event, context) => {
|files |Array&lt;ReadStream&#124;Buffer&#124;String&gt; &#124; Object &#124; ReadStream &#124; Buffer &#124; String| - |- |上传的文件,设置后将会使用 multipart/form-data 格式。如果未设置method,将会自动将method设置为POST |
|contentType |String | - |- |上传数据的格式,设为`json`会自动在`header`内设置`Content-Type: application/json` |
|nestedQuerystring|Boolean | - |- |转换data为queryString时默认不支持嵌套Object,此选项设置为true则支持转换嵌套Object |
|dataType |String | - |- |返回的数据格式 |
|dataType |String | - |- |返回的数据格式,可选值为 'json'(返回数据转为JSON),'text'(返回数据转为字符串), ''(返回数据不做处理,默认值) |
|headers |Object | - |- |请求头 |
|timeout |Number &#124; Array | - |- |超时时间设置。设置为数组时第一项为请求超时,第二项为返回超时。设置为数字时相当于同时设置请求超时和返回超时,即`timeout:3000`效果等于`timeouut:[3000,3000]` |
......
**uniCloud和微信小程序云开发、支付宝小程序云开发有何区别?**
### uniCloud和微信小程序云开发、支付宝小程序云开发有何区别?
微信、支付宝、百度的小程序,均提供了云开发。但它们都仅支持自家小程序,无法跨端。
......@@ -6,11 +6,11 @@
简单来说,uniCloud和微信小程序云开发、支付宝小程序云开发一样稳定健壮,但可以跨更多平台。不管你在uniCloud里选择了阿里还是腾讯的serverless,均可以跨端使用。
**uniCloud稳定吗?DCloud服务器异常会影响我的线上业务吗?**
### uniCloud稳定吗?DCloud服务器异常会影响我的线上业务吗?
`uniCloud`是 DCloud 和阿里云、腾讯云等成熟云厂商合作推出的云服务产品,阿里云、腾讯云等提供云端基础资源,DCloud提供前端框架的封装、IDE工具支持、插件生态等服务,开发者的云函数直接托管在阿里云等服务商平台。用户终端App运行时,直连云服务商平台,不会经过DCloud服务器,开发者无需担心因DCloud服务器负载而影响自己业务的问题。
`uniCloud`是 DCloud 和阿里云、腾讯云等成熟云厂商合作推出的云服务产品,阿里云、腾讯云等提供云端基础资源,DCloud提供API设计、前端框架、IDE工具支持、管理控制条、插件生态等服务,开发者的云函数直接托管在阿里云等服务商平台。用户终端App运行时,直连云服务商平台,不会经过DCloud服务器,开发者无需担心因DCloud服务器负载而影响自己业务的问题。
**云函数 和 传统 Node.js 开发有何区别?**
### 云函数 和 传统 Node.js 开发有何区别?
云函数相当于 Node.js + Serverless + DCloud改进。
- 传统Node.js开发需要购买服务器,安装Node.js环境,部署 pm2 等守护进程;云函数无需考虑服务器环境,只需专心实现业务代码,然后将云函数一键上传,云服务商负责云函数运行环境的准备。
......@@ -22,23 +22,43 @@
另外,在 Node.js 代码实现上,云函数每次执行的宿主环境(可简单理解为虚拟机或服务器硬件)可能相同,也可能不同,因此传统`Node.js`开发中将部分信息存储本地硬盘或内存的方案就不再适合,建议通过云数据库或云存储的方案替代。
**uniCloud只支持uni-app,怎么开发web界面?**
### uniCloud只支持uni-app,怎么开发web界面?
uni-app本来也可以开发web界面,只是内置组件对宽屏没有自动适配。你可以:
1. 新建uni-app项目,但不使用内置组件,而是直接用三方ui库,比如elementUI。这些基于vue的、适合宽屏使用的ui库可以直接用。至于js api,仍然使用uni的,比如uni.setStorage等。
1. 新建uni-app项目,但不使用内置组件,而是直接用三方ui库,比如elementUI。这些基于vue的、适合宽屏使用的ui库可以直接用。至于js api,仍然使用uni的,比如uni.setStorage等。有一个可参考插件[GraceAdmin](https://ext.dcloud.net.cn/plugin?id=1347),是基于uniCloud的pc端管理后台框架。
2. 继续使用内置组件,自己处理pc适配:
- 如果要多端适配界面,使用css的媒体查询处理适配。
- 网上有三方库可以替换touch的拖动为pc上的drag。比如hello uni-app的h5示例使用的touch-emulator.js。
- 2.6.3起,uni内置组件支持了pc鼠标的滚动和drag。老版可以使用三方库替换touch的拖动为pc上的drag,比如touch-emulator.js。
- uni-app的内置组件和api仅适配了webkit内核浏览器,ie和firefox可能有兼容问题。如有问题需自己写额外css或js适配。
后续DCloud会进一步强化内置组件和uni-ui对PC浏览器的适配。
**腾讯、阿里的serverless有什么大案例?**
### 云函数是否有http url触发方式?
目前可以用uni-app做个web页面,通过这个url来操作云函数。插件市场有一个做好的样例,详见[https://ext.dcloud.net.cn/plugin?id=1360](https://ext.dcloud.net.cn/plugin?id=1360)
后续会原生支持http url触发云函数。
### 微信云开发支持客户端直接操作数据库,uniCloud不支持?
- 安全问题:客户端直接操作数据库,会有安全隐患。微信云开发利用了微信账户是强制登陆的特点,设计了一套基于微信账户的权限,可以让客户端直接操作数据库,但一旦离开微信环境这种方案就用不了。比如App和H5,大多是不需要登录账户也可以使用的。
- 其他问题:客户端直接操作数据库,会造成客户端的sdk体积变大、启动初始化变慢、整体联网消耗流量变大、数据获取权限控制复杂,并不好用。
综上,uni-app放弃了客户端直连数据库,所有数据库操作必须使用云函数。
### 云函数支持websocket和定时任务吗?
很快会支持。
### 发布H5时还得自己找个服务器部署前端网页,可以不用自己再找服务器吗?
很快会支持。
### 腾讯、阿里的serverless有什么大案例?
- 微信小程序云开发,已经有50万开发者,包括腾讯自有的很多大日活应用都构建在腾讯云serverless上,如微信生活缴费、乘车码、微信读书、腾讯新闻、腾讯相册等。
- 2019年双11,阿里部分业务已经迁移在serverless上。支付宝小程序也提供了云开发功能。
**uniCloud费用贵不贵?**
### uniCloud费用贵不贵?
目前uniCloud处于公测期,是免费的。未来uniCloud的租用费用,也会低于租用传统云主机的费用。
公测结束也不会删除公测期的数据。
另外公测结束也不会删除公测期的数据。
......@@ -8,7 +8,7 @@
- 对于老的uni-app项目,也可以对项目点右键,菜单中选择“创建uniCloud云开发环境”
- 新建uni-app项目的模板中,有一个`Hello uniCloud`项目模板,演示了各种云函数的使用。
uniCloud云开发环境创建成功后,项目下会有一个带有云图标的特殊目录,名为“cloudfunctions”。
uniCloud云开发环境创建成功后,项目根目录下会有一个带有云图标的特殊目录,名为“cloudfunctions”。(即便是cli创建的项目,云函数目录也在项目的根目录下,而不是src下)
## 创建和绑定服务空间
......@@ -68,10 +68,14 @@ exports.main = async (event, context) => {
前者仅完成部署,后者会在部署后同时运行,并打印日志出来。
在云函数对应的目录右键可以配置运行测试参数,如下图,选择之后会生成一个形如`${函数名}.param.json`的文件,此文件内容会在云函数`上传并运行`时作为参数传入云函数内。
在云函数编辑器里,按`Ctrl+r`运行快捷键,或点工具栏的运行,还会直接看到上传并运行云函数的快捷指令。`Ctrl+r`然后回车或选`0`,即可高效的在控制台看到运行结果和日志输出。
云函数目前无法断点debug,只能打印`console.log`看日志。
![](https://img.cdn.aliyun.dcloud.net.cn/uni-app/uniCloud/uniCloud-run-function-1.png)
![](https://img.cdn.aliyun.dcloud.net.cn/uni-app/uniCloud/uniCloud-run-function.png)
![](https://img.cdn.aliyun.dcloud.net.cn/uni-app/uniCloud/uniCloud-run-function-2.png)
......
# uniCloud 更新日志
======================================
#### 2020-03-04
* 【web 控制台】
+ 阿里云 新增 云数据库分页
+ 阿里云 新增 云数据库索引
+ 控制台 新增 服务空间快捷切换选项卡
+ 控制台 优化 云数据库长文本显示收起展开按钮
+ 控制台 优化 云数据库搜索体验
#### 2020-02-27
* 【IDE】
+ db_init.json 调整 添加索引方向时应使用字符串
......
......@@ -3,3 +3,4 @@
- [Hello uniCLoud]:在HBuilderX 2.5.8+版本,新建uni-app项目的项目模板中有 hello unicloud模板,展示了uniCloud的云函数基本用法、cdn使用方式。它对应的H5演示地址是:[https://hellounicloud.m3w.cn/](https://hellounicloud.m3w.cn/)
- [uniCloud基础登录示例](https://ext.dcloud.net.cn/plugin?id=1268):包含账号密码登录注册,微信小程序登录,token验证功能的uniCloud示例
- [uni抗疫开源项目汇总](https://gitee.com/dcloud/xinguan2020):这是一个项目集合汇总,里面有大量与抗疫项目的开源项目,均基于uniCloud。包括外来人员登记系统、学生健康报备系统、员工疫情筛查工具、消毒检查登记系统、物资管理系统等。这些项目具备一定通用性,可以稍加改造用于其他行业应用。
- [GraceAdmin](https://ext.dcloud.net.cn/plugin?id=1347): uniCloud 的 pc 端管理后台框架,快速搭建pc管理界面。
......@@ -8,6 +8,23 @@
上传文件到云存储,**阿里云单文件大小限制为100M,腾讯云单文件最大为5G**
**阿里云支持的文件类型**
```js
{
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
webp: 'image/webp',
svg: 'image/svg+xml',
mp3: 'audio/mp3',
mp4: 'video/mp4',
ogg: 'audio/ogg',
webm: 'video/webm'
}
```
#### 请求参数
**Object object**
......
......@@ -235,13 +235,15 @@ const SYNC_API_RE =
const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/;
function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
function isCallbackApi (name) {
......@@ -266,6 +268,19 @@ function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -279,18 +294,6 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
})))
}
}
......
......@@ -6792,7 +6792,7 @@ function updateDOMListeners (oldVnode, vnode) {
target$1._$vd = vnode.context._$vd;
var context = vnode.context;
// 存储事件标记
target$1.setAttribute('nid', String(vnode.data.attrs['_i']));
target$1.setAttribute('nid', String((vnode.data.attrs || {})['_i']));
target$1.setAttribute('cid', context._$id);
normalizeEvents(on);
......
......@@ -5941,7 +5941,7 @@ function updateDOMListeners (oldVnode, vnode) {
target$1._$vd = vnode.context._$vd;
var context = vnode.context;
// 存储事件标记
target$1.setAttribute('nid', String(vnode.data.attrs['_i']));
target$1.setAttribute('nid', String((vnode.data.attrs || {})['_i']));
target$1.setAttribute('cid', context._$id);
normalizeEvents(on);
......
......@@ -56,24 +56,32 @@ function getH5Options (manifestJson) {
let base = h5.router.base
if (base.indexOf('/') !== 0 && base.indexOf('./') !== 0) {
if (!base.startsWith('/') && !base.startsWith('./')) {
base = '/' + base
}
if (base.substr(-1) !== '/') {
}
if (!base.endsWith('/')) {
base = base + '/'
}
// 相对路径仅支持 hash 模式
if (base.startsWith('./')) {
h5.router.mode = defaultRouter.mode
}
h5.router.base = base
if (process.env.NODE_ENV === 'production') { // 生产模式,启用 publicPath
h5.publicPath = h5.publicPath || base
if (h5.publicPath.substr(-1) !== '/') {
if (!h5.publicPath.endsWith('/')) {
h5.publicPath = h5.publicPath + '/'
}
} else { // 其他模式,启用 base
h5.publicPath = base
} else { // 其他模式,启用 base
if (base.startsWith('./')) {
// 在开发模式, publicPath 如果为 './' webpack-dev-server 匹配文件时会失败
h5.publicPath = base.substr(1)
} else {
h5.publicPath = base
}
}
/* eslint-disable no-mixed-operators */
......@@ -90,4 +98,4 @@ module.exports = {
parseManifestJson,
getNetworkTimeout,
getH5Options
}
}
......@@ -122,7 +122,7 @@ function isValidPage (page, root = '') {
process.env.UNI_PLATFORM === 'app-plus' &&
page.style
) {
const subNVues = page.style.subNVues || (page.style['app-plus'] && page.style['app-plus']['subNVues'])
const subNVues = page.style.subNVues || (page.style['app-plus'] && page.style['app-plus'].subNVues)
if (Array.isArray(subNVues)) {
subNVues.forEach(subNVue => {
let subNVuePath = subNVue.path
......@@ -155,8 +155,8 @@ function isValidPage (page, root = '') {
return true
} else {
uniNVuePages.push({
'path': pagePath + '.html',
'style': page.style || {}
path: pagePath + '.html',
style: page.style || {}
})
return false
}
......@@ -342,9 +342,11 @@ function getAutoComponentsByDir (componentsPath, absolute = false) {
fs.readdirSync(componentsPath).forEach(name => {
const folder = path.resolve(componentsPath, name)
const importDir = absolute ? normalizePath(folder) : `@/components/${name}`
if (fs.existsSync(path.resolve(folder, name + '.vue'))) {
// 读取文件夹文件列表,比对文件名(fs.existsSync在大小写不敏感的系统会匹配不准确)
const files = fs.readdirSync(folder)
if (files.includes(name + '.vue')) {
components[`^${name}$`] = `${importDir}/${name}.vue`
} else if (fs.existsSync(path.resolve(folder, name + '.nvue'))) {
} else if (files.includes(name + '.nvue')) {
components[`^${name}$`] = `${importDir}/${name}.nvue`
}
})
......
const path = require('path')
const defaultOptions = {
limit: -1,
fallback: {
loader: 'file-loader',
options: {
publicPath (url, resourcePath, context) {
if (
process.env.UNI_PLATFORM === 'app-plus' &&
process.env.UNI_USING_V3
) { // app-plus v3 下路径不能以/开头
return path.relative(process.env.UNI_INPUT_DIR, resourcePath)
}
return '/' + path.relative(process.env.UNI_INPUT_DIR, resourcePath)
},
outputPath (url, resourcePath, context) {
return path.relative(process.env.UNI_INPUT_DIR, resourcePath)
}
}
}
}
const inlineLimit =
process.env.UNI_PLATFORM === 'mp-weixin' ||
process.env.UNI_PLATFORM === 'mp-qq' ||
process.env.UNI_PLATFORM === 'mp-toutiao' ||
process.env.UNI_PLATFORM === 'app-plus' // v2需要base64,v3需要rewriteUrl
// mp-weixin,mp-qq,app-plus 非v3(即:需要base64的平台)
// 将/static/logo.png转换为~@/static/logo.png
// @import,src,background,background-image
const rewriteUrl = inlineLimit ? require('postcss-urlrewrite')({
imports: true,
properties: ['src', 'background', 'background-image'],
rules: [{
from: /^@\//,
to: '~@/'
}, {
from: /^\/([^/])/,
to: '~@/$1'
}]
}) : () => {}
module.exports = {
loader: 'url-loader',
options () {
if (process.env.UNI_PLATFORM === 'h5') {
// h5平台,不对 url-loader 作调整,默认limit:4096,也不修改file-loader输出路径
return {}
}
if (inlineLimit) {
return {
...defaultOptions,
limit: process.env.UNI_USING_V3 ? -1 : 40960 // (主要用于background-image)
}
}
return {
...defaultOptions
}
},
rewriteUrl
}
......@@ -20,6 +20,7 @@
"license": "Apache-2.0",
"dependencies": {
"hash-sum": "^1.0.2",
"postcss-urlrewrite": "^0.2.2",
"strip-json-comments": "^2.0.1"
},
"gitHead": "84e9cb1ca1898054d161f1514efadd1ab24fd804"
......
......@@ -235,13 +235,15 @@ const SYNC_API_RE =
const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/;
function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
function isCallbackApi (name) {
......@@ -266,6 +268,19 @@ function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -279,18 +294,6 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
})))
}
}
......
......@@ -235,13 +235,15 @@ const SYNC_API_RE =
const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/;
function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
function isCallbackApi (name) {
......@@ -266,6 +268,19 @@ function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -279,18 +294,6 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
})))
}
}
......
......@@ -235,13 +235,15 @@ const SYNC_API_RE =
const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/;
function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
function isCallbackApi (name) {
......@@ -266,6 +268,19 @@ function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -279,18 +294,6 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
})))
}
}
......
......@@ -235,13 +235,15 @@ const SYNC_API_RE =
const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/;
function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
function isCallbackApi (name) {
......@@ -266,6 +268,19 @@ function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -279,18 +294,6 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
})))
}
}
......
......@@ -235,13 +235,15 @@ const SYNC_API_RE =
const CONTEXT_API_RE = /^create|Manager$/;
const ASYNC_API = ['createBLEConnection'];
const CALLBACK_API_RE = /^on/;
function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
function isCallbackApi (name) {
......@@ -266,6 +268,19 @@ function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -279,18 +294,6 @@ function promisify (name, api) {
success: resolve,
fail: reject
}), ...params);
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor;
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
};
}
})))
}
}
......
......@@ -26,7 +26,7 @@ function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
function noop () { }
function noop () {}
/**
* Create a cached version of a pure function.
......@@ -805,7 +805,9 @@ function Behavior (options) {
return options
}
const nextTick = Vue.nextTick;
const nextTick = Vue.nextTick;
var index = uni.__$wx__;
export default uni;
export default index;
export { Behavior, Component, Page, nextTick };
......@@ -43,6 +43,14 @@ module.exports = {
devtool: false,
entry() {
return process.UNI_ENTRY
},
output: {
filename: '[name].js',
chunkFilename: '[name].js'
},
optimization: {
splitChunks: false,
runtimeChunk: false
},
externals: {
vue: 'Vue'
......
......@@ -3,11 +3,15 @@ const transformAssetUrls = {
'video': ['src', 'poster'],
'img': 'src',
'image': 'src',
'cover-image': 'src',
'v-uni-audio': 'src',
'v-uni-video': ['src', 'poster'],
'v-uni-image': 'src',
'v-uni-cover-image': 'src'
'cover-image': 'src',
// h5
'v-uni-audio': 'src',
'v-uni-video': ['src', 'poster'],
'v-uni-image': 'src',
'v-uni-cover-image': 'src',
// nvue
'u-image': 'src',
'u-video': ['src', 'poster']
}
function rewrite (attr, name) {
......
......@@ -25,8 +25,19 @@ const postcssLoader = {
options: {
sourceMap: false,
parser: require('postcss-comment'),
plugins: [
require('postcss-import'),
plugins: [
require('postcss-import')({
resolve (id, basedir, importOptions) {
if (id.startsWith('~@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
} else if (id.startsWith('@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
} else if (id.startsWith('/') && !id.startsWith('//')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
}
return id
}
}),
require('@dcloudio/vue-cli-plugin-uni/packages/postcss')
]
}
......@@ -59,17 +70,17 @@ const sassLoader = {
}
if (sassLoaderVersion < 8) {
scssLoader.options.data = sassData
scssLoader.options.outputStyle = 'nested'
scssLoader.options.data = sassData
scssLoader.options.outputStyle = 'nested'
sassLoader.options.data = sassData
sassLoader.options.outputStyle = 'nested'
sassLoader.options.indentedSyntax = true
} else {
scssLoader.options.prependData = sassData
scssLoader.options.sassOptions = {
outputStyle: 'nested'
}
scssLoader.options.prependData = sassData
scssLoader.options.sassOptions = {
outputStyle: 'nested'
}
sassLoader.options.prependData = sassData
sassLoader.options.sassOptions = {
......
......@@ -104,6 +104,9 @@ const compiler = require('weex-template-compiler')
const oldCompile = compiler.compile
compiler.compile = function (source, options = {}) {
(options.modules || (options.modules = [])).push(autoComponentsModule)
options.modules.push(require('@dcloudio/uni-template-compiler/lib/asset-url'))
options.isUnaryTag = isUnaryTag
// 将 autoComponents 挂在 isUnaryTag 上边
options.isUnaryTag.autoComponents = new Set()
......
......@@ -52,7 +52,7 @@ module.exports = (api, options) => {
Object.assign(options, { // TODO 考虑非 HBuilderX 运行时,可以支持自定义输出目录
outputDir: process.env.UNI_OUTPUT_TMP_DIR || process.env.UNI_OUTPUT_DIR,
assetsDir
}, vueConfig)
}, vueConfig) // 注意,此处目前是覆盖关系,后续考虑改为webpack merge逻辑
require('./lib/options')(options)
......
......@@ -2,8 +2,7 @@ const path = require('path')
const webpack = require('webpack')
const {
getMainEntry,
isInHBuilderX
getMainEntry
} = require('@dcloudio/uni-cli-shared')
const vueLoader = require('@dcloudio/uni-cli-shared/lib/vue-loader')
......@@ -53,11 +52,7 @@ function getProvides (isAppService) {
const v3 = {
vueConfig: {
parallel: false,
transpileDependencies: [
wxsPath,
runtimePath
]
parallel: false
},
webpackConfig (webpackConfig, vueOptions, api) {
const isAppService = !!vueOptions.pluginOptions['uni-app-plus']['service']
......@@ -200,38 +195,6 @@ const v3 = {
const isAppService = !!vueOptions.pluginOptions['uni-app-plus']['service']
const isAppView = !!vueOptions.pluginOptions['uni-app-plus']['view']
const fileLoaderOptions = isInHBuilderX ? {
emitFile: isAppView,
name: '[path][name].[ext]',
context: process.env.UNI_INPUT_DIR
} : {
emitFile: isAppView,
outputPath (url, resourcePath, context) {
return path.relative(process.env.UNI_INPUT_DIR, resourcePath)
}
}
// 处理静态资源
webpackConfig.module
.rule('svg')
.use('file-loader')
.options(fileLoaderOptions)
const staticTypes = ['images', 'media', 'fonts']
staticTypes.forEach(staticType => {
webpackConfig.module
.rule(staticType)
.use('url-loader')
.loader('url-loader')
.tap(options => Object.assign(options, {
limit: 1,
fallback: {
loader: 'file-loader',
options: fileLoaderOptions
}
}))
})
const cacheConfig = {
cacheDirectory: false,
cacheIdentifier: false
......
......@@ -20,15 +20,14 @@ module.exports = function chainWebpack (platformOptions, vueOptions, api) {
return function (webpackConfig) {
// 处理静态资源 limit
const urlLoader = require('@dcloudio/uni-cli-shared/lib/url-loader')
const staticTypes = ['images', 'media', 'fonts']
staticTypes.forEach(staticType => {
webpackConfig.module
.rule(staticType)
.use('url-loader')
.loader('url-loader')
.tap(options => Object.assign(options, {
limit: 40960
}))
.loader(urlLoader.loader)
.tap(options => Object.assign(options, urlLoader.options()))
})
// 条件编译 vue 文件统一直接过滤html,js,css三种类型,单独资源文件引用各自过滤
......
......@@ -239,6 +239,7 @@ module.exports = function configureWebpack (platformOptions, manifestPlatformOpt
resolve: {
alias: {
'@': path.resolve(process.env.UNI_INPUT_DIR),
'./@': path.resolve(process.env.UNI_INPUT_DIR), // css中的'@/static/logo.png'会被转换成'./@/static/logo.png'加载
'vue$': getPlatformVue(vueOptions),
'uni-pages': path.resolve(process.env.UNI_INPUT_DIR, 'pages.json'),
'@dcloudio/uni-stat': require.resolve('@dcloudio/uni-stat'),
......
......@@ -48,7 +48,6 @@ if (
console.warn(`发布H5,需要在uniCloud后台操作,绑定安全域名,否则会因为跨域问题而无法访问。教程参考:https://uniapp.dcloud.io/uniCloud/quickstart-H5`)
}
// 初始化环境变量
const defaultInputDir = '../../../../src'
const defaultOutputDir = '../../../../dist/' +
......
......@@ -34,6 +34,7 @@ const uniCloudPath = path.resolve(__dirname, '../../packages/uni-cloud/dist/inde
function getProvides () {
return {
'__f__': [path.resolve(__dirname, '../format-log.js'), 'default'],
'uniCloud': [uniCloudPath, 'default'],
'wx.nextTick': [runtimePath, 'nextTick'],
'Page': [runtimePath, 'Page'],
......@@ -62,10 +63,6 @@ if (process.env.NODE_ENV !== 'production') {
const vueConfig = {
parallel: false, // 因为传入了自定义 compiler,避免参数丢失,禁用parallel
publicPath,
transpileDependencies: [
wxsPath,
runtimePath
],
pages: {
index: {
// page 的入口
......
......@@ -19,6 +19,10 @@ module.exports = function initOptions (options) {
options.transpileDependencies.push(path.resolve(process.env.UNI_INPUT_DIR, 'node_modules'))
options.transpileDependencies.push('@dcloudio/uni-' + process.env.UNI_PLATFORM)
options.transpileDependencies.push('@dcloudio/uni-stat')
// mp runtime
options.transpileDependencies.push('@dcloudio/uni-mp-weixin/dist/mp.js')
// wxs
options.transpileDependencies.push('@dcloudio/uni-mp-weixin/dist/wxs.js')
if (process.env.UNI_PLATFORM === 'app-plus') {
options.transpileDependencies.push('format-log.js')
......@@ -64,12 +68,12 @@ module.exports = function initOptions (options) {
sassData = `${sassData}
@import "@/uni.scss";`
}
if (!options.css.loaderOptions.sass.sassOptions) {
options.css.loaderOptions.sass.sassOptions = {}
}
if (!options.css.loaderOptions.sass.sassOptions) {
options.css.loaderOptions.sass.sassOptions = {}
}
// 指定 outputStyle, 否则 production 模式下会被默认成 compressed
options.css.loaderOptions.sass.sassOptions.outputStyle = 'nested'
options.css.loaderOptions.sass.sassOptions.outputStyle = 'nested'
if (sassLoaderVersion < 8) {
options.css.loaderOptions.sass.data = sassData
......
......@@ -18,6 +18,19 @@ function preprocessBlock(block) {
}
module.exports = function parseCustomBlocks(descriptor, options) {
if (
process.env.UNI_PLATFORM &&
process.env.UNI_PLATFORM.indexOf('mp-') === 0 &&
!descriptor.script
) { // 临时方案:小程序平台,无script节点时,自动补充(激活componentSet,否则没法正常追加入口js,后续优化)
descriptor.script = {
type: 'script',
content: 'export default {}',
start: 100,
attrs: {},
end: 125
}
}
if (!descriptor.template || !FILTER_TAG || options.isAppNVue) {
// delete customBlocks
......
......@@ -150,6 +150,7 @@ if (process.env.UNI_USING_V3) {
'background-attachment'
]
let rewriteUrl
/**
* 转换 upx
* 转换 px
......@@ -160,6 +161,11 @@ if (process.env.UNI_USING_V3) {
...opts
}
return function (root, result) {
if (!rewriteUrl) {
rewriteUrl = require('@dcloudio/uni-cli-shared/lib/url-loader').rewriteUrl
}
rewriteUrl(root)
if (process.env.UNI_PLATFORM === 'h5') {
// Transform CSS AST here
......@@ -235,19 +241,19 @@ if (process.env.UNI_USING_V3) {
// Transform each property declaration here
decl.value = tranformValue(decl, opts)
})
if (process.env.UNI_PLATFORM !== 'quickapp') {
rule.selectors = rule.selectors.map(complexSelector => {
return transformSelector(complexSelector, simpleSelectors => {
return simpleSelectors.walkTags(tag => {
const k = tag.value
const v = CSS_TAGS[k]
if (v) {
tag.value = v === 'r'
? `._${k}` : v
}
})
})
})
if (process.env.UNI_PLATFORM !== 'quickapp') {
rule.selectors = rule.selectors.map(complexSelector => {
return transformSelector(complexSelector, simpleSelectors => {
return simpleSelectors.walkTags(tag => {
const k = tag.value
const v = CSS_TAGS[k]
if (v) {
tag.value = v === 'r'
? `._${k}` : v
}
})
})
})
}
})
}
......
......@@ -11,8 +11,16 @@ const isInsideKeyframes = function (rule) {
rule.parent && rule.parent.type === 'atrule' && /^(-\w+-)?keyframes$/.test(rule.parent.name)
)
}
let rewriteUrl
module.exports = postcss.plugin('postcss-uniapp-plugin', function (opts) {
return function (root, result) {
return function (root, result) {
if (!rewriteUrl) {
rewriteUrl = require('@dcloudio/uni-cli-shared/lib/url-loader').rewriteUrl
}
rewriteUrl(root)
root.walkRules(rule => {
// Transform each rule here
if (!isInsideKeyframes(rule)) {
......@@ -27,7 +35,7 @@ module.exports = postcss.plugin('postcss-uniapp-plugin', function (opts) {
if (tag.value === 'page') {
tag.value = 'body'
} else if (~TAGS.indexOf(tag.value) && tag.value.substring(
0, 4) !== 'uni-') {
0, 4) !== 'uni-') {
tag.value = 'uni-' + tag.value
}
})
......
......@@ -10,7 +10,18 @@ function generatePageCode (pages, pageOptions) {
module.exports = function definePages (appJson) {
return {
name: 'define-pages.js',
content: `
content: `
if (!Promise.prototype.finally) {
Promise.prototype.finally = function(callback) {
const promise = this.constructor
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
}
}
if(uni.restoreGlobal){
uni.restoreGlobal(weex,plus,setTimeout,clearTimeout,setInterval,clearInterval)
}
......
......@@ -14,13 +14,15 @@ const CONTEXT_API_RE = /^create|Manager$/
const TASK_APIS = ['request', 'downloadFile', 'uploadFile', 'connectSocket']
const ASYNC_API = ['createBLEConnection']
const CALLBACK_API_RE = /^on/
export function isContextApi (name) {
return CONTEXT_API_RE.test(name)
}
export function isSyncApi (name) {
return SYNC_API_RE.test(name)
return SYNC_API_RE.test(name) && ASYNC_API.indexOf(name) === -1
}
export function isCallbackApi (name) {
......@@ -49,6 +51,19 @@ export function shouldPromise (name) {
return true
}
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
}
}
export function promisify (name, api) {
if (!shouldPromise(name)) {
return api
......@@ -62,18 +77,6 @@ export function promisify (name, api) {
success: resolve,
fail: reject
}), ...params)
/* eslint-disable no-extend-native */
if (!Promise.prototype.finally) {
Promise.prototype.finally = function (callback) {
const promise = this.constructor
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
}
}
})))
}
}
......@@ -63,9 +63,10 @@ export const canvasPutImageData = {
}
}
const fileType = {
const fileTypes = {
PNG: 'png',
JPG: 'jpeg'
JPG: 'jpg',
JPEG: 'jpg'
}
export const canvasToTempFilePath = {
......@@ -103,7 +104,7 @@ export const canvasToTempFilePath = {
type: String,
validator (value, params) {
value = (value || '').toUpperCase()
params.fileType = value in fileType ? fileType[value] : fileType.PNG
params.fileType = value in fileTypes ? fileTypes[value] : fileTypes.PNG
}
},
quality: {
......
import {
hasOwn,
isPlainObject
} from 'uni-shared'
......@@ -35,12 +36,14 @@ function stringifyQuery (url, data) {
query[item[0]] = item[1]
})
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (isPlainObject(data[key])) {
query[encode(key)] = encode(JSON.stringify(data[key]))
} else {
query[encode(key)] = encode(data[key])
if (hasOwn(data, key)) {
let v = data[key]
if (typeof v === 'undefined' || v === null) {
v = ''
} else if (isPlainObject(v)) {
v = JSON.stringify(v)
}
query[encode(key)] = encode(v)
}
}
query = Object.keys(query).map(item => `${item}=${query[item]}`).join('&')
......@@ -98,4 +101,4 @@ export const request = {
params.responseType = Object.values(responseType).indexOf(value) < 0 ? responseType.TEXT : value
}
}
}
}
......@@ -27,8 +27,12 @@ export const connectSocket = {
}
},
protocols: {
type: Array,
// 微信文档虽然写的是数组,但是可以正常传递字符串
type: [Array, String],
validator (value, params) {
if (typeof value === 'string') {
value = [value]
}
params.protocols = (value || []).filter(str => typeof str === 'string')
}
}
......
......@@ -49,4 +49,4 @@ export function Behavior (options) {
export const nextTick = Vue.nextTick
export default uni
export default uni.__$wx__
......@@ -18,7 +18,7 @@ UniServiceJSBridge.subscribe('onMapMethodCallback', ({
callback.invoke(callbackId, data)
})
const methods = ['getCenterLocation', 'getScale', 'getRegion']
const methods = ['getCenterLocation', 'getScale', 'getRegion', 'includePoints', 'translateMarker']
export class MapContext {
constructor (id, pageVm) {
......@@ -29,14 +29,6 @@ export class MapContext {
moveToLocation () {
operateMapPlayer(this.id, this.pageVm, 'moveToLocation')
}
includePoints (args) {
operateMapPlayer(this.id, this.pageVm, 'includePoints', args)
}
translateMarker (args) {
operateMapPlayer(this.id, this.pageVm, 'translateMarker', args)
}
}
MapContext.prototype.$getAppMap = function () {
......@@ -44,11 +36,9 @@ MapContext.prototype.$getAppMap = function () {
}
methods.forEach(function (method) {
MapContext.prototype[method] = callback.warp(function (options, callbackId) {
operateMapPlayer(this.id, this.pageVm, method, {
options,
callbackId
})
MapContext.prototype[method] = callback.warp(function (options, callbackId) {
options.callbackId = callbackId
operateMapPlayer(this.id, this.pageVm, method, options)
})
})
......
......@@ -92,13 +92,6 @@ onMethod('onSocketTaskStateChange', ({
if (!socketTask) {
return
}
socketTask._callbacks[state].forEach(callback => {
if (typeof callback === 'function') {
callback(state === 'message' ? {
data
} : {})
}
})
if (state === 'open') {
socketTask.readyState = socketTask.OPEN
}
......@@ -114,7 +107,14 @@ onMethod('onSocketTaskStateChange', ({
if (index >= 0) {
socketTasksArray.splice(index, 1)
}
}
}
socketTask._callbacks[state].forEach(callback => {
if (typeof callback === 'function') {
callback(state === 'message' ? {
data
} : {})
}
})
})
export function connectSocket (args, callbackId) {
......
<template>
<uni-canvas
:canvas-id="canvasId"
:disable-scroll="disableScroll"
<uni-canvas
:canvas-id="canvasId"
:disable-scroll="disableScroll"
v-on="_listeners">
<canvas
ref="canvas"
width="300"
<canvas
ref="canvas"
width="300"
height="150" />
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden;">
<slot />
</div>
<v-uni-resize-sensor
ref="sensor"
<v-uni-resize-sensor
ref="sensor"
@resize="_resize" />
</uni-canvas>
</template>
......@@ -121,12 +121,16 @@ export default {
method(data)
}
},
_resize () {
var canvas = this.$refs.canvas
var context = canvas.getContext('2d')
var imageData = context.getImageData(0, 0, canvas.width, canvas.height)
wrapper(this.$refs.canvas)
context.putImageData(imageData, 0, 0)
_resize () {
var canvas = this.$refs.canvas
if (canvas.width > 0 && canvas.height > 0) {
var context = canvas.getContext('2d')
var imageData = context.getImageData(0, 0, canvas.width, canvas.height)
wrapper(this.$refs.canvas)
context.putImageData(imageData, 0, 0)
} else {
wrapper(this.$refs.canvas)
}
},
_touchmove (event) {
event.preventDefault()
......@@ -545,7 +549,8 @@ export default {
const img = new Image()
img.onload = () => {
const canvas = getTempCanvas(destWidth, destHeight)
if (fileType === 'jpeg') {
if (fileType === 'jpeg' || fileType === 'jpg') {
fileType = 'jpeg'
c2d.fillStyle = '#fff'
c2d.fillRect(0, 0, destWidth, destHeight)
}
......
......@@ -57,7 +57,7 @@ function findRefByElm (id, elm) {
if (!id || !elm) {
return
}
if (elm.attr.id === id) {
if (elm.attr && elm.attr.id === id) {
return elm
}
const children = elm.children
......
......@@ -134,10 +134,13 @@ export function notifyBLECharacteristicValueChanged (data, callbackId) {
}
export function readBLECharacteristicValue (data, callbackId) {
onBLECharacteristicValueChange = onBLECharacteristicValueChange || bluetoothOn('onBLECharacteristicValueChange')
bluetoothExec('readBLECharacteristicValue', callbackId, data)
}
export function writeBLECharacteristicValue (data, callbackId) {
data.value = base64ToArrayBuffer(data.value)
if (typeof data.value === 'string') {
data.value = base64ToArrayBuffer(data.value)
}
bluetoothExec('writeBLECharacteristicValue', callbackId, data)
}
......@@ -57,6 +57,8 @@ export * from './route/re-launch'
export * from './route/redirect-to'
export * from './route/switch-tab'
export * from './storage/storage'
export * from './ui/keyboard'
export * from './ui/navigation-bar'
export * from './ui/popup'
......
......@@ -32,9 +32,16 @@ function _switchTab ({
const pages = getCurrentPages()
const len = pages.length
let callOnShow = false
if (len >= 1) { // 前一个页面是非 tabBar 页面
const currentPage = pages[len - 1]
if (!currentPage.$page.meta.isTabBar) {
// 前一个页面为非 tabBar 页面时,目标tabBar需要强制触发onShow
// 该情况下目标页tabBarPage的visible是不对的
// 除非每次路由跳转都处理一遍tabBarPage的visible,目前仅switchTab会处理
// 简单起见,暂时直接判断该情况,执行onShow
callOnShow = true
pages.reverse().forEach(page => {
if (!page.$page.meta.isTabBar && page !== currentPage) {
page.$remove()
......@@ -60,8 +67,8 @@ function _switchTab ({
// 查找当前 tabBarPage,且设置 visible
getCurrentPages(true).forEach(page => {
if (('/' + page.route) === path) {
if (!page.$page.meta.visible) {
page.$vm.__call_hook('onShow')
if (!page.$page.meta.visible) { // 之前未显示
callOnShow = true
}
page.$page.meta.visible = true
tabBarPage = page
......@@ -74,6 +81,8 @@ function _switchTab ({
if (tabBarPage) {
tabBarPage.$getAppWebview().show('none')
// 等visible状态都切换完之后,再触发onShow,否则开发者在onShow里边 getCurrentPages 会不准确
callOnShow && tabBarPage.$vm.__call_hook('onShow')
} else {
return showWebview(registerPage({
url,
......@@ -107,4 +116,4 @@ export function switchTab ({
from
}, callbackId)
}, openType === 'appLaunch')
}
}
import {
invoke
} from '../../bridge'
const STORAGE_DATA_TYPE = '__TYPE'
const STORAGE_KEYS = 'uni-storage-keys'
function parseValue (value) {
const types = ['object', 'string', 'number', 'boolean', 'undefined']
try {
const object = typeof value === 'string' ? JSON.parse(value) : value
const type = object.type
if (types.indexOf(type) >= 0) {
const keys = Object.keys(object)
// eslint-disable-next-line valid-typeof
if (keys.length === 2 && 'data' in object && typeof object.data === type) {
return object.data
} else if (keys.length === 1) {
return ''
}
}
} catch (error) {}
}
export function setStorage ({
key,
data,
isSync
} = {}, callbackId) {
const type = typeof data
const value = type === 'string' ? data : JSON.stringify({
type,
data: data
})
try {
if (type === 'string' && parseValue(value) !== undefined) {
plus.storage.setItemAsync(key + STORAGE_DATA_TYPE, type)
} else {
plus.storage.removeItemAsync(key + STORAGE_DATA_TYPE)
}
plus.storage.setItemAsync(key, value, function () {
invoke(callbackId, {
errMsg: 'setStorage:ok'
})
}, function (err) {
invoke(callbackId, {
errMsg: `setStorage:fail ${err.message}`
})
})
} catch (error) {
invoke(callbackId, {
errMsg: `setStorage:fail ${error}`
})
}
}
export function setStorageSync (key, data) {
const type = typeof data
const value = type === 'string' ? data : JSON.stringify({
type,
data: data
})
try {
if (type === 'string' && parseValue(value) !== undefined) {
plus.storage.setItem(key + STORAGE_DATA_TYPE, type)
} else {
plus.storage.removeItem(key + STORAGE_DATA_TYPE)
}
plus.storage.setItem(key, value)
} catch (error) {
}
}
function parseGetStorage (type, value) {
let data = value
if (type !== 'string' || (type === 'string' && value === '{"type":"undefined"}')) {
try {
// 兼容H5和V3初期历史格式
let object = JSON.parse(value)
const result = parseValue(object)
if (result !== undefined) {
data = result
} else if (type) {
// 兼容App端历史格式
data = object
if (typeof object === 'string') {
object = JSON.parse(object)
// eslint-disable-next-line valid-typeof
data = typeof object === (type === 'null' ? 'object' : type) ? object : data
}
}
} catch (error) {}
}
return data
}
export function getStorage ({
key
} = {}, callbackId) {
plus.storage.getItemAsync(key, function (res) {
plus.storage.getItemAsync(key + STORAGE_DATA_TYPE, function (typeRes) {
const typeOrigin = typeRes.data || ''
const type = typeOrigin.toLowerCase()
invoke(callbackId, {
data: parseGetStorage(type, res.data),
errMsg: 'getStorage:ok'
})
}, function () {
const type = ''
invoke(callbackId, {
data: parseGetStorage(type, res.data),
errMsg: 'getStorage:ok'
})
})
}, function (err) {
invoke(callbackId, {
data: '',
errMsg: `getStorage:fail ${err.message}`
})
})
}
export function getStorageSync (key) {
const value = plus.storage.getItem(key)
const typeOrigin = plus.storage.getItem(key + STORAGE_DATA_TYPE) || ''
const type = typeOrigin.toLowerCase()
if (typeof value !== 'string') {
return ''
}
return parseGetStorage(type, value)
}
export function removeStorage ({
key
} = {}, callbackId) {
// 兼容App端历史格式
plus.storage.removeItemAsync(key + STORAGE_DATA_TYPE)
plus.storage.removeItemAsync(key, function (res) {
invoke(callbackId, {
errMsg: 'removeStorage:ok'
})
}, function (err) {
invoke(callbackId, {
errMsg: `removeStorage:fail ${err.message}`
})
})
}
export function removeStorageSync (key) {
plus.storage.removeItem(key + STORAGE_DATA_TYPE)
plus.storage.removeItem(key)
}
export function clearStorage (args, callbackId) {
plus.storage.clearAsync(function (res) {
invoke(callbackId, {
errMsg: 'clearStorage:ok'
})
}, function (err) {
invoke(callbackId, {
errMsg: `clearStorage:fail ${err.message}`
})
})
}
export function clearStorageSync () {
plus.storage.clear()
}
export function getStorageInfo () {
const length = (plus.storage.length || plus.storage.getLength()) || 0
const keys = []
let currentSize = 0
for (let index = 0; index < length; index++) {
const key = plus.storage.key(index)
if (key !== STORAGE_KEYS && key.indexOf(STORAGE_DATA_TYPE) + STORAGE_DATA_TYPE.length !== key.length) {
const value = plus.storage.getItem(key)
currentSize += key.length + value.length
keys.push(key)
}
}
return {
keys,
currentSize: Math.ceil(currentSize * 2 / 1024),
limitSize: Number.MAX_VALUE,
errMsg: 'getStorageInfo:ok'
}
}
export function getStorageInfoSync () {
const res = getStorageInfo()
delete res.errMsg
return res
}
......@@ -55,7 +55,11 @@ export function setNavigationBarColor ({
}
plus.navigator.setStatusBarStyle(frontColor === '#000000' ? 'dark' : 'light')
const style = webview.getStyle()
if (style && style.titleNView) {
if (style && style.titleNView) {
if (style.titleNView.autoBackButton) {
styles.backButton = styles.backButton || {}
styles.backButton.color = frontColor
}
webview.setStyle({
titleNView: styles
})
......
......@@ -158,7 +158,8 @@ export function showActionSheet ({
}, callbackId) {
const options = {
buttons: itemList.map(item => ({
title: item
title: item,
color: itemColor
}))
}
if (title) {
......
......@@ -2,7 +2,7 @@ import {
callAppHook
} from 'uni-core/service/plugins/util'
import initOn from 'uni-core/service/bridge/on'
import initOn from 'uni-core/service/bridge/on'
import {
NETWORK_TYPES
......@@ -19,7 +19,8 @@ import {
import tabBar from './tab-bar'
import {
publish
publish,
requireNativePlugin
} from '../bridge'
import {
......@@ -54,7 +55,8 @@ export function getApp ({
)
}
function initGlobalListeners () {
function initGlobalListeners () {
const globalEvent = requireNativePlugin('globalEvent')
const emit = UniServiceJSBridge.emit
// splashclosed 时开始监听 backbutton
......@@ -84,7 +86,7 @@ function initGlobalListeners () {
})
})
plus.globalEvent.addEventListener('uistylechange', function (event) {
globalEvent.addEventListener('uistylechange', function (event) {
publish('onUIStyleChange', {
style: event.uistyle
})
......
......@@ -138,9 +138,12 @@ export class VDomSync {
removeVm (vm) {
const cid = vm._$id
// 移除尚未同步的data
this.batchData = this.batchData.filter(data => data[1][0] !== cid)
delete this.vms[cid]
if (vm === this.vms[cid]) { // 仅相同vm的才移除,否则保留
// 目前同一位置的vm,cid均一样
// 移除尚未同步的data
this.batchData = this.batchData.filter(data => data[1][0] !== cid)
delete this.vms[cid]
}
}
addElement (elm) {
......
......@@ -10,6 +10,10 @@ import {
publishHandler
} from 'uni-platform/service/publish-handler'
import {
wx
} from './wx'
import {
definePage
} from '../page-factory'
......@@ -26,6 +30,9 @@ import {
import vuePlugin from './framework/plugins'
// 挂靠在uni上,暂不做全局导出
uni.__$wx__ = wx
UniServiceJSBridge.publishHandler = publishHandler
UniServiceJSBridge.invokeCallbackHandler = invokeCallbackHandler
......
import apis from '../../../../lib/apis'
import {
wrapper,
wrapperUnimplemented
} from 'uni-helpers/api'
import api from 'uni-service-api'
export const wx = Object.create(null)
apis.forEach(name => {
if (api[name]) {
wx[name] = wrapper(name, api[name])
} else {
wx[name] = wrapperUnimplemented(name)
}
})
......@@ -177,7 +177,7 @@ export default {
})
},
markers (val) {
this.map && this._addMarkers(val)
this.map && this._addMarkers(val, true)
},
polyline (val) {
this.map && this._addMapLines(val)
......@@ -208,9 +208,9 @@ export default {
map.onclick = (e) => {
this.$trigger('click', {}, e)
}
map.onstatuschanged((data = {}) => {
this.$trigger('end', {}, data)
})
map.onstatuschanged = (e) => {
this.$trigger('regionchange', {}, e)
}
this._addMarkers(this.markers)
this._addMapLines(this.polyline)
this._addMapCircles(this.circles)
......@@ -318,6 +318,7 @@ export default {
if (this.map) {
if (clear) {
this.map.clearOverlays()
this.map.__markers__ = {}
}
markers.forEach(marker => {
this._addMarker(this.map, marker)
......
<template>
<uni-page :data-page="$route.meta.pagePath">
<page-head
v-if="navigationBar.type!=='none'"
<page-head
v-if="navigationBar.type!=='none'"
v-bind="navigationBar" />
<page-refresh
v-if="enablePullDownRefresh"
ref="refresh"
:color="refreshOptions.color"
:offset="refreshOptions.offset" />
<page-body
v-if="enablePullDownRefresh"
@touchstart.native="_touchstart"
<page-refresh
v-if="enablePullDownRefresh"
ref="refresh"
:color="refreshOptions.color"
:offset="refreshOptions.offset"
/>
<page-body
v-if="enablePullDownRefresh"
@touchstart.native="_touchstart"
@touchmove.native="_touchmove"
@touchend.native="_touchend"
@touchcancel.native="_touchend">
@touchend.native="_touchend"
@touchcancel.native="_touchend"
>
<slot name="page" />
</page-body>
<page-body v-else>
......@@ -22,11 +24,11 @@
</uni-page>
</template>
<style>
uni-page {
display: block;
width: 100%;
height: 100%;
}
uni-page {
display: block;
width: 100%;
height: 100%;
}
</style>
<script>
import {
......@@ -146,6 +148,12 @@ export default {
titlePenetrate: {
type: String,
default: 'NO'
},
navigationBarShadow: {
type: Object,
default () {
return {}
}
}
},
data () {
......@@ -180,6 +188,7 @@ export default {
timingFunc: '',
titlePenetrate: yesNoParseList[this.titlePenetrate]
}, titleNView)
navigationBar.shadow = this.navigationBarShadow
const refreshOptions = Object.assign({
support: true,
......@@ -223,4 +232,4 @@ export default {
}
}
}
</script>
</script>
......@@ -2,16 +2,16 @@
<uni-page-head :uni-page-head-type="type">
<div
:style="{transitionDuration:duration,transitionTimingFunction:timingFunc,backgroundColor:bgColor,color:textColor}"
:class="{'uni-page-head-transparent':type==='transparent','uni-page-head-titlePenetrate': titlePenetrate}"
:class="headClass"
class="uni-page-head"
>
<div class="uni-page-head-hd">
<div
v-show="backButton"
class="uni-page-head-btn"
<div
v-show="backButton"
class="uni-page-head-btn"
@click="_back">
<i
:style="{color:color,fontSize:'27px'}"
<i
:style="{color:color,fontSize:'27px'}"
class="uni-btn-icon">&#xe601;</i>
</div>
<template v-for="(btn,index) in btns">
......@@ -32,19 +32,19 @@
</div>
</template>
</div>
<div
v-if="!searchInput"
<div
v-if="!searchInput"
class="uni-page-head-bd">
<div
:style="{fontSize:titleSize,opacity:type==='transparent'?0:1}"
class="uni-page-head__title"
>
<i
v-if="loading"
<i
v-if="loading"
class="uni-loading" />
<img
v-if="titleImage!==''"
:src="titleImage"
<img
v-if="titleImage!==''"
:src="titleImage"
class="uni-page-head__title_image" >
<template v-else>{{ titleText }}</template>
</div>
......@@ -108,7 +108,7 @@ uni-page-head {
uni-page-head .uni-page-head {
position: fixed;
left: 0;
left: 0;
top: 0;
width: 100%;
height: 44px;
......@@ -302,6 +302,44 @@ uni-page-head .uni-page-head__title .uni-page-head__title_image {
height: 26px;
vertical-align: middle;
}
uni-page-head .uni-page-head-shadow {
overflow: visible;
}
uni-page-head .uni-page-head-shadow::after {
content: "";
position: absolute;
left: 0;
right: 0;
top: 100%;
height: 5px;
background-size: 100% 100%;
}
uni-page-head .uni-page-head-shadow-grey::after {
background-image: url("https://cdn.dcloud.net.cn/img/shadow-grey.png");
}
uni-page-head .uni-page-head-shadow-blue::after {
background-image: url("https://cdn.dcloud.net.cn/img/shadow-blue.png");
}
uni-page-head .uni-page-head-shadow-green::after {
background-image: url("https://cdn.dcloud.net.cn/img/shadow-green.png");
}
uni-page-head .uni-page-head-shadow-orange::after {
background-image: url("https://cdn.dcloud.net.cn/img/shadow-orange.png");
}
uni-page-head .uni-page-head-shadow-red::after {
background-image: url("https://cdn.dcloud.net.cn/img/shadow-red.png");
}
uni-page-head .uni-page-head-shadow-yellow::after {
background-image: url("https://cdn.dcloud.net.cn/img/shadow-yellow.png");
}
</style>
<script>
import appendCss from 'uni-platform/helpers/append-css'
......@@ -385,6 +423,12 @@ export default {
titlePenetrate: {
type: Boolean,
default: false
},
shadow: {
type: Object,
default () {
return {}
}
}
},
data () {
......@@ -425,6 +469,18 @@ export default {
})
}
return btns
},
headClass () {
const shadowColorType = this.shadow.colorType
const data = {
'uni-page-head-transparent': this.type === 'transparent',
'uni-page-head-titlePenetrate': this.titlePenetrate,
'uni-page-head-shadow': shadowColorType
}
if (shadowColorType) {
data[`uni-page-head-shadow-${shadowColorType}`] = shadowColorType
}
return data
}
},
mounted () {
......
......@@ -36,9 +36,26 @@ export default {
const index = typeof this.current === 'number' ? this.current : this.urls.indexOf(this.current)
this.index = index < 0 ? 0 : index
},
mounted () {
const MAX_MOVE = 20
let x = 0
let y = 0
this.$el.addEventListener('mousedown', (event) => {
this.preventDefault = false
x = event.clientX
y = event.clientY
})
this.$el.addEventListener('mouseup', (event) => {
if (Math.abs(event.clientX - x) > MAX_MOVE || Math.abs(event.clientY - y) > MAX_MOVE) {
this.preventDefault = true
}
})
},
methods: {
_click () {
getApp().$router.back()
if (!this.preventDefault) {
getApp().$router.back()
}
}
}
}
......
const STORAGE_DATA_TYPE = '__TYPE'
const STORAGE_KEYS = 'uni-storage-keys'
function parseValue (value) {
const types = ['object', 'string', 'number', 'boolean', 'undefined']
try {
const object = typeof value === 'string' ? JSON.parse(value) : value
const type = object.type
if (types.indexOf(type) >= 0) {
const keys = Object.keys(object)
// eslint-disable-next-line valid-typeof
if (keys.length === 2 && 'data' in object && typeof object.data === type) {
return object.data
} else if (keys.length === 1) {
return ''
}
}
} catch (error) { }
}
export function setStorage ({
key,
data
} = {}) {
const type = typeof data
const value = type === 'string' ? data : JSON.stringify({
type,
data: data
})
try {
if (type === 'string' && parseValue(value) !== undefined) {
localStorage.setItem(key + STORAGE_DATA_TYPE, type)
} else {
localStorage.removeItem(key + STORAGE_DATA_TYPE)
}
localStorage.setItem(key, value)
} catch (error) {
return {
errMsg: `setStorage:fail ${error}`
}
}
return {
errMsg: 'setStorage:ok'
}
}
export function setStorageSync (key, data) {
setStorage({
key,
data
})
}
export function getStorage ({
key
} = {}) {
const value = localStorage && localStorage.getItem(key)
if (typeof value !== 'string') {
return {
data: '',
errMsg: 'getStorage:fail'
}
}
let data = value
const typeOrigin = localStorage.getItem(key + STORAGE_DATA_TYPE) || ''
const type = typeOrigin.toLowerCase()
if (type !== 'string' || (typeOrigin === 'String' && value === '{"type":"undefined"}')) {
try {
// 兼容H5和V3初期历史格式
let object = JSON.parse(value)
const result = parseValue(object)
if (result !== undefined) {
data = result
} else if (type) {
// 兼容App端历史格式
data = object
if (typeof object === 'string') {
object = JSON.parse(object)
// eslint-disable-next-line valid-typeof
data = typeof object === (type === 'null' ? 'object' : type) ? object : data
}
}
} catch (error) { }
}
return {
data,
errMsg: 'getStorage:ok'
}
}
export function getStorageSync (key) {
const res = getStorage({
key
})
return res.data
}
export function removeStorage ({
key
} = {}) {
if (localStorage) {
// 兼容App端历史格式
localStorage.removeItem(key + STORAGE_DATA_TYPE)
localStorage.removeItem(key)
}
return {
errMsg: 'removeStorage:ok'
}
}
export function removeStorageSync (key) {
removeStorage({
key
})
}
export function clearStorage () {
localStorage && localStorage.clear()
return {
errMsg: 'clearStorage:ok'
}
}
export function clearStorageSync () {
clearStorage()
}
export function getStorageInfo () {
const length = (localStorage && (localStorage.length || localStorage.getLength())) || 0
const keys = []
let currentSize = 0
for (let index = 0; index < length; index++) {
const key = localStorage.key(index)
if (key !== STORAGE_KEYS && key.indexOf(STORAGE_DATA_TYPE) + STORAGE_DATA_TYPE.length !== key.length) {
const value = localStorage.getItem(key)
currentSize += key.length + value.length
keys.push(key)
}
}
return {
keys,
currentSize: Math.ceil(currentSize * 2 / 1024),
limitSize: Number.MAX_VALUE,
errMsg: 'getStorageInfo:ok'
}
}
export function getStorageInfoSync () {
const res = getStorageInfo()
delete res.errMsg
return res
}
const STORAGE_DATA_TYPE = '__TYPE'
const STORAGE_KEYS = 'uni-storage-keys'
function parseValue (value) {
const types = ['object', 'string', 'number', 'boolean', 'undefined']
try {
const object = typeof value === 'string' ? JSON.parse(value) : value
const type = object.type
if (types.indexOf(type) >= 0) {
const keys = Object.keys(object)
// eslint-disable-next-line valid-typeof
if (keys.length === 2 && 'data' in object && typeof object.data === type) {
return object.data
} else if (keys.length === 1) {
return ''
}
}
} catch (error) { }
}
export function setStorage ({
key,
data
} = {}) {
const type = typeof data
const value = type === 'string' ? data : JSON.stringify({
type,
data: data
})
try {
if (type === 'string' && parseValue(value) !== undefined) {
localStorage.setItem(key + STORAGE_DATA_TYPE, type)
} else {
localStorage.removeItem(key + STORAGE_DATA_TYPE)
}
localStorage.setItem(key, value)
} catch (error) {
return {
errMsg: `setStorage:fail ${error}`
}
}
return {
errMsg: 'setStorage:ok'
}
}
export function setStorageSync (key, data) {
setStorage({
key,
data
})
}
export function getStorage ({
key
} = {}) {
const value = localStorage && localStorage.getItem(key)
if (typeof value !== 'string') {
return {
data: '',
errMsg: 'getStorage:fail'
}
}
let data = value
const typeOrigin = localStorage.getItem(key + STORAGE_DATA_TYPE) || ''
const type = typeOrigin.toLowerCase()
if (type !== 'string' || (typeOrigin === 'String' && value === '{"type":"undefined"}')) {
try {
// 兼容H5和V3初期历史格式
let object = JSON.parse(value)
const result = parseValue(object)
if (result !== undefined) {
data = result
} else if (type) {
// 兼容App端历史格式
data = object
if (typeof object === 'string') {
object = JSON.parse(object)
// eslint-disable-next-line valid-typeof
data = typeof object === (type === 'null' ? 'object' : type) ? object : data
}
}
} catch (error) { }
}
return {
data,
errMsg: 'getStorage:ok'
}
}
export function getStorageSync (key) {
const res = getStorage({
key
})
return res.data
}
export function removeStorage ({
key
} = {}) {
if (localStorage) {
// 兼容App端历史格式
localStorage.removeItem(key + STORAGE_DATA_TYPE)
localStorage.removeItem(key)
}
return {
errMsg: 'removeStorage:ok'
}
}
export function removeStorageSync (key) {
removeStorage({
key
})
}
export function clearStorage () {
localStorage && localStorage.clear()
return {
errMsg: 'clearStorage:ok'
}
}
export function clearStorageSync () {
clearStorage()
}
export function getStorageInfo () {
const length = (localStorage && (localStorage.length || localStorage.getLength())) || 0
const keys = []
let currentSize = 0
for (let index = 0; index < length; index++) {
const key = localStorage.key(index)
if (key !== STORAGE_KEYS && key.indexOf(STORAGE_DATA_TYPE) + STORAGE_DATA_TYPE.length !== key.length) {
const value = localStorage.getItem(key)
currentSize += key.length + value.length
keys.push(key)
}
}
return {
keys,
currentSize: Math.ceil(currentSize * 2 / 1024),
limitSize: Number.MAX_VALUE,
errMsg: 'getStorageInfo:ok'
}
}
export function getStorageInfoSync () {
const res = getStorageInfo()
delete res.errMsg
return res
}
......@@ -390,14 +390,21 @@ export default {
_getDateValueArray (valueStr, defaultValue) {
const splitStr = this.mode === mode.DATE ? '-' : ':'
const array = this.mode === mode.DATE ? this.dateArray : this.timeArray
let max = 3
switch (this.fields) {
case fields.YEAR:
max = 1
break
case fields.MONTH:
max = 2
break
let max
if (this.mode === mode.TIME) {
max = 2
} else {
switch (this.fields) {
case fields.YEAR:
max = 1
break
case fields.MONTH:
max = 2
break
default:
max = 3
break
}
}
const inputArray = String(valueStr).split(splitStr)
let value = []
......
<template>
<uni-web-view v-on="$listeners" />
<uni-web-view v-on="$listeners">
<v-uni-resize-sensor
ref="sensor"
@resize="_resize" />
</uni-web-view>
</template>
<script>
export default {
......@@ -16,24 +20,10 @@ export default {
}
},
mounted () {
const {
top,
bottom,
width,
height
} = this.$el.getBoundingClientRect()
this.iframe = document.createElement('iframe')
this.iframe.style.position = 'absolute'
this.iframe.style.display = 'block'
this.iframe.style.border = 0
this.iframe.style.top = top + 'px'
this.iframe.style.bottom = bottom + 'px'
this.iframe.style.width = width + 'px'
this.iframe.style.height = height + 'px'
this.iframe.src = this.$getRealPath(this.src)
document.body.appendChild(this.iframe)
this._resize()
},
activated () {
this.iframe.style.display = 'block'
......@@ -43,6 +33,24 @@ export default {
},
beforeDestroy () {
document.body.removeChild(this.iframe)
},
methods: {
_resize () {
const {
top,
bottom,
width,
height
} = this.$el.getBoundingClientRect()
this.iframe.style.position = 'absolute'
this.iframe.style.display = 'block'
this.iframe.style.border = 0
this.iframe.style.top = top + 'px'
this.iframe.style.bottom = bottom + 'px'
this.iframe.style.width = width + 'px'
this.iframe.style.height = height + 'px'
}
}
}
</script>
......
......@@ -10,9 +10,10 @@ function warp (fn) {
complete: options.complete
}
const data = Object.assign({}, options)
delete data.success
delete data.fail
delete data.complete
// TODO 下版重构 nvue h5 callback
// delete data.success
// delete data.fail
// delete data.complete
const res = fn.bind(this)(data, callbackId)
if (res) {
invoke(callbackId, res)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册