
吐槽一下:uni-app原生video画质简直弱爆了,H5播放器甩原生video几条街!!
视频地址:http://vd2.bdstatic.com/mda-jmawcmkjagd8je0u/mda-jmawcmkjagd8je0u.mp4
这是uni-app原生video的画质

然后这是H5的video的画质
虽然这个视频本身画质不是很高,但是相对于H5的播放器画面更细腻柔和,原生的video比较粗糙,满满的小方格!
吐槽完了我分享一下我使用纯 nvue 项目加载“西瓜播放器”的方法:
西瓜视频播放器,官网:https://v2.h5player.bytedance.com/,
新建组件:H5-Video.nvue
<script>
export default {
data()
{
return{
//视频地址
src:'http://vd2.bdstatic.com/mda-jmawcmkjagd8je0u/mda-jmawcmkjagd8je0u.mp4'
}
},
onBackPress()
{
//退出页销毁播放器
uni.$xgplayer.close()
},
mounted()
{
this.xgplayer()
},
methods: {
xgplayer()
{
let styles = {
top: '0px',
bottom: '540px',
height: '256px',
width: '100%',
zindex:0,
position: 'static',
titleNView: {
autoBackButton: true,
backgroundColor: '#202028',
titleColor: '#ffffff'
} ,
"statusbar": {
"immersed": true ,//开启沉浸式状态栏
"background": '#202028'
},
"hardwareAccelerated" : true, //开启硬件加速
"allowsInlineMediaPlayback": true,//ios关闭原生控件
};
uni.$xgplayer = plus.webview.create('','xgplayer', styles, {}); //挂载到uni全局
uni.$xgplayer.setStyle({
'videoFullscreen':'landscape-primary',//视频全屏时支持横屏 IOS 端无效
});
uni.$xgplayer.loadURL("/hybrid/html/xgplayer.html?src="+this.src)
uni.$xgplayer.show();
}
},
}
</script>
新建本地HTML文件
hybrid/html/xgplayer.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui">
<meta name="referrer" content="no-referrer">
<title>H5高清播放</title>
<script src="js/uni.webview.1.5.2.js" charset="utf-8"></script>
<style type="text/css">
html, body {width:100%;height:100%;margin:auto;overflow: hidden;background-color: #000000;-webkit-user-select: none; user-select: none;}
</style>
</head>
<body>
<div id="mse"></div>
<script src="https://cdn.jsdelivr.net/npm/xgplayer@1.1.4/browser/index.js" charset="utf-8"></script>
<script>
let url = getQueryVariable('src');
let player = new Player({
"id": "mse",
"url": url,
"playsinline": true,
"width":"100%",
"height":"225px",
"autoplay": true,
"whitelist": [''],
"danmu": {
"comments": [
{
"duration": 15000,
"id": '1',
"start": 3000,
"txt": '长弹幕长弹幕长弹幕长弹幕长弹幕',
"style": { //弹幕自定义样式
"color": '#ff9500',
"fontSize": '17px',
// "border": 'solid 1px #ff9500',
"borderRadius": '50px',
"padding": '5px 11px',
"backgroundColor": 'rgba(255, 255, 255, 0.1)'
}
}
],
"area": {
"start": 0,
"end": 1
}
},
});
//获取url参数
function getQueryVariable(variable)
{
var reg = new RegExp('(^|&)' + variable + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
</script>
</body>
</html>
这样图二的效果就实现了!
缺点:就是和应用交互通讯困难,目前还没较好治疗药物!
有点:画质清晰、加载速度快,相同方法可以整合DP、videojs等网页H5播放器,DP支持 CDNBye p2p加速,速度更快!!
uni-app的video组件画质问题从去年就开始关注,直到现在已经彻底不报希望了,只能另找出路!!
你可以说插件市场有许多原生的播放器插件可以选择,如果说视频播放部分需要使用第三方插件来弥补,那么原生video组件存在的意义是什么??难道原生video已经不能再优化了吗???
现在是视频播放的潮流,将来更是,为什么没有得到重视??
视频地址:http://vd2.bdstatic.com/mda-jmawcmkjagd8je0u/mda-jmawcmkjagd8je0u.mp4
这是uni-app原生video的画质
然后这是H5的video的画质
虽然这个视频本身画质不是很高,但是相对于H5的播放器画面更细腻柔和,原生的video比较粗糙,满满的小方格!
吐槽完了我分享一下我使用纯 nvue 项目加载“西瓜播放器”的方法:
西瓜视频播放器,官网:https://v2.h5player.bytedance.com/,
新建组件:H5-Video.nvue
<script>
export default {
data()
{
return{
//视频地址
src:'http://vd2.bdstatic.com/mda-jmawcmkjagd8je0u/mda-jmawcmkjagd8je0u.mp4'
}
},
onBackPress()
{
//退出页销毁播放器
uni.$xgplayer.close()
},
mounted()
{
this.xgplayer()
},
methods: {
xgplayer()
{
let styles = {
top: '0px',
bottom: '540px',
height: '256px',
width: '100%',
zindex:0,
position: 'static',
titleNView: {
autoBackButton: true,
backgroundColor: '#202028',
titleColor: '#ffffff'
} ,
"statusbar": {
"immersed": true ,//开启沉浸式状态栏
"background": '#202028'
},
"hardwareAccelerated" : true, //开启硬件加速
"allowsInlineMediaPlayback": true,//ios关闭原生控件
};
uni.$xgplayer = plus.webview.create('','xgplayer', styles, {}); //挂载到uni全局
uni.$xgplayer.setStyle({
'videoFullscreen':'landscape-primary',//视频全屏时支持横屏 IOS 端无效
});
uni.$xgplayer.loadURL("/hybrid/html/xgplayer.html?src="+this.src)
uni.$xgplayer.show();
}
},
}
</script>
新建本地HTML文件
hybrid/html/xgplayer.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui">
<meta name="referrer" content="no-referrer">
<title>H5高清播放</title>
<script src="js/uni.webview.1.5.2.js" charset="utf-8"></script>
<style type="text/css">
html, body {width:100%;height:100%;margin:auto;overflow: hidden;background-color: #000000;-webkit-user-select: none; user-select: none;}
</style>
</head>
<body>
<div id="mse"></div>
<script src="https://cdn.jsdelivr.net/npm/xgplayer@1.1.4/browser/index.js" charset="utf-8"></script>
<script>
let url = getQueryVariable('src');
let player = new Player({
"id": "mse",
"url": url,
"playsinline": true,
"width":"100%",
"height":"225px",
"autoplay": true,
"whitelist": [''],
"danmu": {
"comments": [
{
"duration": 15000,
"id": '1',
"start": 3000,
"txt": '长弹幕长弹幕长弹幕长弹幕长弹幕',
"style": { //弹幕自定义样式
"color": '#ff9500',
"fontSize": '17px',
// "border": 'solid 1px #ff9500',
"borderRadius": '50px',
"padding": '5px 11px',
"backgroundColor": 'rgba(255, 255, 255, 0.1)'
}
}
],
"area": {
"start": 0,
"end": 1
}
},
});
//获取url参数
function getQueryVariable(variable)
{
var reg = new RegExp('(^|&)' + variable + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
</script>
</body>
</html>
这样图二的效果就实现了!
缺点:就是和应用交互通讯困难,目前还没较好治疗药物!
有点:画质清晰、加载速度快,相同方法可以整合DP、videojs等网页H5播放器,DP支持 CDNBye p2p加速,速度更快!!
uni-app的video组件画质问题从去年就开始关注,直到现在已经彻底不报希望了,只能另找出路!!
你可以说插件市场有许多原生的播放器插件可以选择,如果说视频播放部分需要使用第三方插件来弥补,那么原生video组件存在的意义是什么??难道原生video已经不能再优化了吗???
现在是视频播放的潮流,将来更是,为什么没有得到重视??
收起阅读 »
uniapp 本机request正常 ios真机请求失败经验总经
这几天搞app 用uni.request封装后在本机上内置浏览器和其他浏览器打开都正常
换到真机上边 就报请求失败,报fail 什么东东
经过两天的努力总算解决了.
我的系统环境:
donetcore web应用程序 + Uniapp
原因分析:
本地能连说明,request应该没问题 问题出在服务器上
首先得看 网络服务是否可以远程访问(局域网)
方法:直接在手机上浏览器打开你的服务的IP或域名,打不开就说明服务器架设有问题
我这上边是服务器用IIS express这东东引起的
最后的解决办法:
- donetcore web服务器端启动改为IIS 应用URL 改为 http://localhost (localhost这个名字随便取)
- IIS上添加一个网站
1.名称为localhost与应用url的域名相同
物理路径为项目根目录(很重要) 不能是编译后的debug目录(切记)
2.绑定: 类型http ip地址 选全部未分配 端口默认80 主机名 这个地方我没填 因为我要用IP来请求 填了在uniapp 请求url就得用域名 - 启动服务 要求得是管理员权限打开donetcore项目 不打开也没事 系统会自动要求切换其他凭证启动
这几天搞app 用uni.request封装后在本机上内置浏览器和其他浏览器打开都正常
换到真机上边 就报请求失败,报fail 什么东东
经过两天的努力总算解决了.
我的系统环境:
donetcore web应用程序 + Uniapp
原因分析:
本地能连说明,request应该没问题 问题出在服务器上
首先得看 网络服务是否可以远程访问(局域网)
方法:直接在手机上浏览器打开你的服务的IP或域名,打不开就说明服务器架设有问题
我这上边是服务器用IIS express这东东引起的
最后的解决办法:
- donetcore web服务器端启动改为IIS 应用URL 改为 http://localhost (localhost这个名字随便取)
- IIS上添加一个网站
1.名称为localhost与应用url的域名相同
物理路径为项目根目录(很重要) 不能是编译后的debug目录(切记)
2.绑定: 类型http ip地址 选全部未分配 端口默认80 主机名 这个地方我没填 因为我要用IP来请求 填了在uniapp 请求url就得用域名 - 启动服务 要求得是管理员权限打开donetcore项目 不打开也没事 系统会自动要求切换其他凭证启动

uniapp中picker的change事件使用注意
说明
uniapp中picker的change事件返回的值在APP端、微信小程序端有区别,如下:
官方示例代码
APP端在vue和nvue模式下的区别
在vue下bindPickerChange事件返回有e.target.value,而在nvue模式下没有此值,所以,如果按官方示例在picker的change事件中使用e.target.value,在nvue模式下,picker组件不能正常使用,代码也不报错(这就是比较坑的一个点)
另:nvue和vue都存在e.detail.value,该值为Number类型,如图
微信小程序端
bindPickerChange事件返回有e.target.value也有e.detail.value,但是类型都为String类型,如图
总结
1.为了全端兼容,picker的change事件返回值最好使用e.detail.value
2.注意APP端change事件返回值是Number类型,微信小程序端则是String类型
说明
uniapp中picker的change事件返回的值在APP端、微信小程序端有区别,如下:
官方示例代码
APP端在vue和nvue模式下的区别
在vue下bindPickerChange事件返回有e.target.value,而在nvue模式下没有此值,所以,如果按官方示例在picker的change事件中使用e.target.value,在nvue模式下,picker组件不能正常使用,代码也不报错(这就是比较坑的一个点)
另:nvue和vue都存在e.detail.value,该值为Number类型,如图
微信小程序端
bindPickerChange事件返回有e.target.value也有e.detail.value,但是类型都为String类型,如图
总结
1.为了全端兼容,picker的change事件返回值最好使用e.detail.value
2.注意APP端change事件返回值是Number类型,微信小程序端则是String类型

阿里云云函数经常超时,于是自己写了个延时代码测试超时时间
最终确定,超过9000ms就报错,所以云函数的执行时间,如果有太长的,建议卡在8秒以内比较保险,例如uniCloud.httpclient.request超时,最好设置8秒以内,我经常被这个报错
即使后台设置超时时间30秒,依然无法突破9秒,估计这是阿里云限制,毕竟免费。但是文档最好强调一下,以免增加开发者排查bug难度
function sleep(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,ms);
})
}
const t1 = (new Date()).getTime()
await sleep(8990)
const t2 = (new Date()).getTime()
return (t2-t1)
最终确定,超过9000ms就报错,所以云函数的执行时间,如果有太长的,建议卡在8秒以内比较保险,例如uniCloud.httpclient.request超时,最好设置8秒以内,我经常被这个报错
即使后台设置超时时间30秒,依然无法突破9秒,估计这是阿里云限制,毕竟免费。但是文档最好强调一下,以免增加开发者排查bug难度
function sleep(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve,ms);
})
}
const t1 = (new Date()).getTime()
await sleep(8990)
const t2 = (new Date()).getTime()
return (t2-t1)
收起阅读 »

console2
console.x
(data
);
1 参数
x 参数x = {debug: ƒ, error: ƒ, info: ƒ, log: ƒ, warn: ƒ, …} |
参数 | 说明 | 差异 |
---|---|---|---|
debug | 打印debug日志 | ||
log | 打印log日志 | ||
error | 打印错误日志 | ||
info | 打印信息 | ||
warn | 打印警告日志 | ||
clear | 清空日志 | data 数据的值对此无效 |
data
是内容
2 例子
console.debug();
console.x
(data
);
1 参数
x 参数x = {debug: ƒ, error: ƒ, info: ƒ, log: ƒ, warn: ƒ, …} |
参数 | 说明 | 差异 |
---|---|---|---|
debug | 打印debug日志 | ||
log | 打印log日志 | ||
error | 打印错误日志 | ||
info | 打印信息 | ||
warn | 打印警告日志 | ||
clear | 清空日志 | data 数据的值对此无效 |
data
是内容
2 例子
console.debug();
收起阅读 »

mui自定义toast位置
试了好多该样式都没用,然后我直接用原生的,plus.nativeUI.toast("提示信息!",{verticalAlign:"top"});,第二个参数的使用可以参考这里http://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.ToastStyles
试了好多该样式都没用,然后我直接用原生的,plus.nativeUI.toast("提示信息!",{verticalAlign:"top"});,第二个参数的使用可以参考这里http://www.html5plus.org/doc/zh_cn/nativeui.html#plus.nativeUI.ToastStyles
收起阅读 »
IOS证书制作,IOS证书在线制作
IOS证书的制作在我看来两个字"复杂",当然网上也是有很多教程的,说的也非常详细,如果花时间阅读并理解总结后,其实也不是那么复杂,关键就在于需要花时间和精力去理清这些”繁杂“的知识点。如果项目比较急或者暂时还不想现在立刻马上去理清这些,可以使用下面这款工具来简化创建IOS证书的过程。
目的:制作两个文件一个是P12文件 一个是描述文件.mobileprovision
1:制作P12文件
1:第一步访问https://app.121xuexi.com 网站,创建CSR(证书请求文件),因为我们需要生成具有苹果签名的证书。
2:第二步填写必要的信息(IOS区分开发证书和生产证书,步骤完全一样)
3:填写完毕后点击提交,如下图操作后就大功告成啦(可下载P12文件),证书目前最多创建2-3个,如果已经满了可以删除(删除不影响线上应用)
4:将p12上传到指定的位置(注意密码如果忘记,网站上有存储哦)
2.制作描述文件.mobileprovision
1:创建描述文件也非常简单,第一步就是要创建一个APPID(意思是给我的APP分配一个ID,如果已经有了可以忽略)苹果官方Identifiers添加快速链接,按以下提示制作即可
2:第二步:添加测试设备(这样我可以直接安装ipa,无需越狱)苹果官方添加测试设备快速链接,按以下提示制作即可
3:第三步生成描述文件苹果官方描述文件制作快速链接,按以下提示制作即可
4:将描述文件上传到指定位置
大功告成
IOS证书的制作在我看来两个字"复杂",当然网上也是有很多教程的,说的也非常详细,如果花时间阅读并理解总结后,其实也不是那么复杂,关键就在于需要花时间和精力去理清这些”繁杂“的知识点。如果项目比较急或者暂时还不想现在立刻马上去理清这些,可以使用下面这款工具来简化创建IOS证书的过程。
目的:制作两个文件一个是P12文件 一个是描述文件.mobileprovision
1:制作P12文件
1:第一步访问https://app.121xuexi.com 网站,创建CSR(证书请求文件),因为我们需要生成具有苹果签名的证书。
2:第二步填写必要的信息(IOS区分开发证书和生产证书,步骤完全一样)
3:填写完毕后点击提交,如下图操作后就大功告成啦(可下载P12文件),证书目前最多创建2-3个,如果已经满了可以删除(删除不影响线上应用)
4:将p12上传到指定的位置(注意密码如果忘记,网站上有存储哦)
2.制作描述文件.mobileprovision
1:创建描述文件也非常简单,第一步就是要创建一个APPID(意思是给我的APP分配一个ID,如果已经有了可以忽略)苹果官方Identifiers添加快速链接,按以下提示制作即可
2:第二步:添加测试设备(这样我可以直接安装ipa,无需越狱)苹果官方添加测试设备快速链接,按以下提示制作即可
3:第三步生成描述文件苹果官方描述文件制作快速链接,按以下提示制作即可
4:将描述文件上传到指定位置
大功告成
收起阅读 »
uniapp设置强制竖屏wgt包强制竖屏uniapp锁定屏幕竖直方向
分为三步
- 第一步
uniapp 根目录下pages.json 文件中追加下面四项
"globalStyle": {
"pageOrientation": "portrait", //横屏配置,全局屏幕旋转设置(仅 APP/微信/QQ小程序),支持 auto / portrait / landscape
"rpxCalcMaxDeviceWidth": 960,
"rpxCalcBaseDeviceWidth": 375,
"rpxCalcIncludeWidth": 750
} - 第二步
uniapp 根目录下mainfest.json文件中追加如下配置
distribute" : {
"orientation" : ["portrait-primary" ] //重力感应、横竖屏配置
} - 第三步
在你想强制横竖屏的页面的vue文件中的 onLoad 或者onShow的生命周期里面 加上如下代码即可
//#ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary');
//#endif
横竖屏切换都可参照此篇文章 已本人亲自尝试,如有疑问者 可下方留言 会帮忙解答
分为三步
- 第一步
uniapp 根目录下pages.json 文件中追加下面四项
"globalStyle": {
"pageOrientation": "portrait", //横屏配置,全局屏幕旋转设置(仅 APP/微信/QQ小程序),支持 auto / portrait / landscape
"rpxCalcMaxDeviceWidth": 960,
"rpxCalcBaseDeviceWidth": 375,
"rpxCalcIncludeWidth": 750
} - 第二步
uniapp 根目录下mainfest.json文件中追加如下配置
distribute" : {
"orientation" : ["portrait-primary" ] //重力感应、横竖屏配置
} - 第三步
在你想强制横竖屏的页面的vue文件中的 onLoad 或者onShow的生命周期里面 加上如下代码即可
//#ifdef APP-PLUS
plus.screen.lockOrientation('portrait-primary');
//#endif
横竖屏切换都可参照此篇文章 已本人亲自尝试,如有疑问者 可下方留言 会帮忙解答
收起阅读 »
Biaofun建信小程序开发工具链原理浅析
@jump-mp/cli 的安装和使用方法详见建信小程序开发文档。
在终端运行
$ jump -h
可以看到 @jump-mp/cli 中提供如下功能:
Usage: jump <command> [options]
Options:
-v, --version output the version number
-h, --help display help for command
Commands:
init initialize template for jump project
build [options] build project to be used in android or ios
update update project dependencies to the latest version
add [options] <source-name> add npm source for miniproject
construct construct installed npm source for miniproject
help [command] display help for command
init:初始化方法,用于初始化小程序项目和页面、组件;
build:用于对建信小程序进行编译打包;
update:用于更新建信小程序使用的依赖版本;
add:用于添加小程序依赖;
construct:用于构建小程序添加的依赖。
脚手架原理
在上一节中,已经介绍了 @jump-mp/cli 的基本功能。本节将对脚手架 @jump-mp/cli 的整体架构和技术选型进行介绍。
01
现有脚手架技术分析
目前较常用的前端开发脚手架工具有 yoeman、vue-cli 等。
yeoman 搭建项目需要提供 yoeman-generator。yoeman-generator 本质上就是有 i 个具备完整文件结构的项目样板,用户需要手动地把这些 generator 下载到本地,然后 yoeman 就会根据这些 generator 自动生成各种不同的项目。
vue-cli 提供了相当丰富的选项和设定功能,但是其本质也是从远程仓库把不同的模版拉取到本地,而并非是“本地生成“的黑科技。
综上,目前脚手架的思路大都是建立不同的样板项目,然后脚手架根据用户指令引用样板项目生成实际项目。样板项目可以内置在脚手架中,也可以部署在远程仓库。
02
技术选型及架构
为了更好地实现模版定制和功能开发,大前端团队自研了建信小程序脚手架 @jump-mp/cli。脚手架主要使用了以下技术:
node.js:整个脚手架工具的根本组成部分。
commander: 用于组织和处理命令行的输入。
webpack:用于对一些需要编译的文件进行处理。
脚手架的整体架构如下图:
在创建小程序、页面和组件时,小程序的项目、页面和组件的模版(template)都存放在依赖 jump-mp/cli-template 中。每当用户需要创建时,就通过 cli 的命令向 jump-mp/cli-template 中拉取相应的模版并下载到本地。
对于编译、构建等功能,会在后面的小节进行介绍。
最终整个 @jump-mp/cli 的脚手架文件结构如下。
├── bin
│ └── index.js
├── command
│ ├── add.js
│ ├── build.js
│ ├── create.js
│ ├── translate.js
│ └── update.js
├── node_modules
│ └── @jump-mp
│ ├── build
│ └── cli-template
│ ├── component
│ ├── page
│ └── project
└── package.json
其中 bin/index.s为入口文件,主要用 commander 来构建命令行。commander 的工作流程如下:
编译器原理
在完成小程序的开发后,需要在项目根目录下运行 jump build [option] 来对小程序进行编译打包。下面将介绍建信小程序打包过程中的编译原理。
01
小程序引擎
在《初识建信小程序引擎》一文中我们了解到,为了提升小程序的运行性能、用户体验,建信小程序采用了双线程模式,将运行环境分为视图层(view 层)和逻辑层(service 层)。其中视图层负责页面展示,逻辑层负责小程序业务逻辑。视图层和逻辑层分别由 2 个线程管理:
视图层的界面使用了 WebView 进行渲染,视图层使用 Vuejs 作为 MVVM 框架;
逻辑层采用 JsCore 线程运行 JavaScript 脚本。
同时,视图层线程和逻辑层线程通信会经由建信客户端(Native 层)做中转,逻辑层发送网络请求也经由 Native 层转发,Native 层也会渲染一些原生的组件、被小程序调用一些原生的 API。
综上,编译器的主要功能是将小程序代码进行编译,将 view 层、service 层、native 层三个部分各自需要的代码进行提取、转换、整合,最终打包成一个可以在建信客户端运行的小程序。
02
技术选型
编译器主要使用了以下技术:
node.js:整个脚手架工具的根本组成部分。
gulp:基于流的自动化构建工具。
babel相关:用于构建 js 代码的 ast 树、修改 js 代码、编译 js 代码。
vue-template-compiler:用于构建 html 代码的 ast 树,修改 html 代码、编译 html 代码。
postcss:用于修改、编译 css 代码。
webpack:用于对一些需要编译的文件进行处理。
03
编译器原理
编译器在工作的过程中,会经历预处理、编译、打包三个步骤,具体流程如下:
下面将详细描述编译原理和流程。
(1)mxml—>使用 vue 框架的 html
对于页面/组件的 mxml 页面,编译器做了如下处理:
首先,mxml 会被解析为一颗 ast 抽象语法树
然后,编译器会遍历该 ast 语法树,将组件名转为小程序内置组件名(例:button->mp-button),将建信小程序语法标签转为 vue 语法标签(例:mp:if->v-if)。
最后,遍历该 ast 抽象语法树,将 ast 转为使用 vue 框架的 html 代码。
(2)mcss—>css
将 mcss 转为 css 主要做了设备视图分辨率适配,将 rpx 单位转为 vw。
(3)使用 vue 框架的 html—>使用 vue 框架的 page.view.js
在生成了 html 中间态文件“使用 vue 框架的 html” 后,还需要生成一个符合 vue 框架要求的 js 文件,然后将其组合到一个 html 文件(及 page.html)中,该文件才可在使用了 vue 引擎的视图层中被加载,因此我们需要一个这样的 html 文件。举例说明:
jump 小程序:
mxml
<view>
<view>{{title}}</view>
<button bindtap="clickMe">请点击换标题</button>
</view>
js
Page({
data: {
title: 'Hello World',
},
//事件处理函数
clickMe: function() {
this.setData({
title: '你好,世界'
})
}
})
那么转换之后的 vue 页面应该是:
<template>
<mp-view>
<mp-view>{{title}}</mp-view>
<mp-button @click="clickMe">请点击换标题</mp-button>
<mp-view>
<template>
<script>
export default {
data(){
return {
title: '',
}
}
}
</script>
这里 data 中的变量名不是通过解析 page.js 得到的,因为开发者在编写 page.js 时是没有要求固定格式的,因此无法通过抽象代码树解析的方式拿到 data 的参数,所以这里 data 的变量名是通过解析 html 表达式拿到的。
而没有将 data 中的数据值和 method 方法写到 html 页面中,是因为这部分应由 service 逻辑层处理,view 层只需声明变量名即可。
最终再通过 vue 生成的 html 页面应为:
<html>
<head>......</head>
<body>
......
<script>
new Vue({
data() {
return {
title:""
}
},
el: "#app",
template:"<template><mp-view><mp-view>{{title}}</mp-view><mp-button @click="clickMe">请点击换标题</mp-button> <mp-view><template>"
});
</script>
</body>
</html>
如要生成上述页面,首先我们需要拿到data中的变量名,使用的方法是,通过解析 html 中的表达式来获得变量名。
然后则是构建 html 页面中的 js 代码,这里使用 babel 去构建一个 js 的 ast 抽象语法树,然后向树中插入 data 和 template 的参数和值。
由于考虑到随着页面功能的增多以及版本的迭代,直接构建目标态代码,在未来版本发生迭代时很可能会导致编译逻辑会跟随修改。为了解决这一问题,我们需要区分出目标态代码的不变部分和可变部分。把不变部分抽象为源对象,把可变部分交由 runtime 运行时框架处理。这样只需要在 rumtime 层做版本迭代而不需要编译器修改。
为了提高加载速度,我们在编译阶段做了预处理,把原本在运行时处理的渲染模版提前展开为真实的虚拟节点树。
所以本步骤整体的工作流程如下图:
最终生成的 page.view.js 应为:
let app = window.$$makeVueComponent({
data() {
return {
title: ""
};
},
el: "#app",
render: function anonymous() {
with(this){return _c('mp-view',[_c('mp-view',[_c('mp-view',[_v(_s(title))]),_v(" "),_c('mp-button',{on:{"click":function($event){return eventHandlerProxy('clickMe',$event)}}},[_v("请点击换标题")])],1)],1)}
},
staticRenderFns: []
});
new Vue(app);
(4)页面(page)中引入的组件(component)的处理
由于视图 view 层在加载页面时只会去加载该页面的 html,因此需要在编译页面时将页面中引入的组件(component)也编译进 html 中。这里的编译流程如下:
编译器会先读取所编译的 page 的 json 文件,如果 json 文件中引入了组件,则将编译后的组件的 js 加入到 page 的 html 中。然后再去读取改组件的 json 文件,查看改组件是否引入了其他组件,一直递归循环,直至所有组件都被编译进 html 文件为止。对于每个页面重复引入的组件,则只编入一次。
(5)page.js 的编译
在处理需要在 service 逻辑层运行的 page.js 时,我们不会去改变文件的代码,只是对 page.js 用 webpack 做编译打包,然后汇总到 app-service.js 文件中。
构建 npm 依赖
建信小程序支持引入第三方 npm 依赖。这里不再对 npm 的功能进行介绍,可以参考官方 npm 文档。
01
使用方法
建信小程序引入 npm 依赖,需要添加依赖和构建依赖两个步骤。
其中,构建依赖的目的是,将安装在 node_modules 下的依赖进行编译构建,然后生成到项目根目录下的 npm_miniprogram 文件夹下,以供小程序在开发过程中进行引入。
02
构建原理
在使用 npm 依赖时需要构建依赖,是因为在小程序编译时,无法在未指定路径的情况下将 require 的 node_modules 下的依赖编译进去,所以需要构建这个步骤,将依赖进行预编译,然后统一到一个目录下。
npm 依赖的构建原理如下图所示:
首先会读取项目根目录下的package.json, 如果 devDependencies/dependencies 不为空,则小程序中有使用 npm 依赖,需要构建。
在 node_modules 中找到小程序引入的依赖,查看该依赖是否指明小程序的引入入口(miniprogram_dist 目录),如果有则直接将其复制到 miniprogram_npm 路径下,如果没有则使用 webpack 由 main 入口进行编译,编译结果保存到 miniprogram_npm 路径。
对构建完成的 npm 依赖 重复第一步的步骤,直到所有依赖及依赖的依赖构建完成。
文章来源:Biaofun标梵互动(https://www.biaofun.com/)
@jump-mp/cli 的安装和使用方法详见建信小程序开发文档。
在终端运行
$ jump -h
可以看到 @jump-mp/cli 中提供如下功能:
Usage: jump <command> [options]
Options:
-v, --version output the version number
-h, --help display help for command
Commands:
init initialize template for jump project
build [options] build project to be used in android or ios
update update project dependencies to the latest version
add [options] <source-name> add npm source for miniproject
construct construct installed npm source for miniproject
help [command] display help for command
init:初始化方法,用于初始化小程序项目和页面、组件;
build:用于对建信小程序进行编译打包;
update:用于更新建信小程序使用的依赖版本;
add:用于添加小程序依赖;
construct:用于构建小程序添加的依赖。
脚手架原理
在上一节中,已经介绍了 @jump-mp/cli 的基本功能。本节将对脚手架 @jump-mp/cli 的整体架构和技术选型进行介绍。
01
现有脚手架技术分析
目前较常用的前端开发脚手架工具有 yoeman、vue-cli 等。
yeoman 搭建项目需要提供 yoeman-generator。yoeman-generator 本质上就是有 i 个具备完整文件结构的项目样板,用户需要手动地把这些 generator 下载到本地,然后 yoeman 就会根据这些 generator 自动生成各种不同的项目。
vue-cli 提供了相当丰富的选项和设定功能,但是其本质也是从远程仓库把不同的模版拉取到本地,而并非是“本地生成“的黑科技。
综上,目前脚手架的思路大都是建立不同的样板项目,然后脚手架根据用户指令引用样板项目生成实际项目。样板项目可以内置在脚手架中,也可以部署在远程仓库。
02
技术选型及架构
为了更好地实现模版定制和功能开发,大前端团队自研了建信小程序脚手架 @jump-mp/cli。脚手架主要使用了以下技术:
node.js:整个脚手架工具的根本组成部分。
commander: 用于组织和处理命令行的输入。
webpack:用于对一些需要编译的文件进行处理。
脚手架的整体架构如下图:
在创建小程序、页面和组件时,小程序的项目、页面和组件的模版(template)都存放在依赖 jump-mp/cli-template 中。每当用户需要创建时,就通过 cli 的命令向 jump-mp/cli-template 中拉取相应的模版并下载到本地。
对于编译、构建等功能,会在后面的小节进行介绍。
最终整个 @jump-mp/cli 的脚手架文件结构如下。
├── bin
│ └── index.js
├── command
│ ├── add.js
│ ├── build.js
│ ├── create.js
│ ├── translate.js
│ └── update.js
├── node_modules
│ └── @jump-mp
│ ├── build
│ └── cli-template
│ ├── component
│ ├── page
│ └── project
└── package.json
其中 bin/index.s为入口文件,主要用 commander 来构建命令行。commander 的工作流程如下:
编译器原理
在完成小程序的开发后,需要在项目根目录下运行 jump build [option] 来对小程序进行编译打包。下面将介绍建信小程序打包过程中的编译原理。
01
小程序引擎
在《初识建信小程序引擎》一文中我们了解到,为了提升小程序的运行性能、用户体验,建信小程序采用了双线程模式,将运行环境分为视图层(view 层)和逻辑层(service 层)。其中视图层负责页面展示,逻辑层负责小程序业务逻辑。视图层和逻辑层分别由 2 个线程管理:
视图层的界面使用了 WebView 进行渲染,视图层使用 Vuejs 作为 MVVM 框架;
逻辑层采用 JsCore 线程运行 JavaScript 脚本。
同时,视图层线程和逻辑层线程通信会经由建信客户端(Native 层)做中转,逻辑层发送网络请求也经由 Native 层转发,Native 层也会渲染一些原生的组件、被小程序调用一些原生的 API。
综上,编译器的主要功能是将小程序代码进行编译,将 view 层、service 层、native 层三个部分各自需要的代码进行提取、转换、整合,最终打包成一个可以在建信客户端运行的小程序。
02
技术选型
编译器主要使用了以下技术:
node.js:整个脚手架工具的根本组成部分。
gulp:基于流的自动化构建工具。
babel相关:用于构建 js 代码的 ast 树、修改 js 代码、编译 js 代码。
vue-template-compiler:用于构建 html 代码的 ast 树,修改 html 代码、编译 html 代码。
postcss:用于修改、编译 css 代码。
webpack:用于对一些需要编译的文件进行处理。
03
编译器原理
编译器在工作的过程中,会经历预处理、编译、打包三个步骤,具体流程如下:
下面将详细描述编译原理和流程。
(1)mxml—>使用 vue 框架的 html
对于页面/组件的 mxml 页面,编译器做了如下处理:
首先,mxml 会被解析为一颗 ast 抽象语法树
然后,编译器会遍历该 ast 语法树,将组件名转为小程序内置组件名(例:button->mp-button),将建信小程序语法标签转为 vue 语法标签(例:mp:if->v-if)。
最后,遍历该 ast 抽象语法树,将 ast 转为使用 vue 框架的 html 代码。
(2)mcss—>css
将 mcss 转为 css 主要做了设备视图分辨率适配,将 rpx 单位转为 vw。
(3)使用 vue 框架的 html—>使用 vue 框架的 page.view.js
在生成了 html 中间态文件“使用 vue 框架的 html” 后,还需要生成一个符合 vue 框架要求的 js 文件,然后将其组合到一个 html 文件(及 page.html)中,该文件才可在使用了 vue 引擎的视图层中被加载,因此我们需要一个这样的 html 文件。举例说明:
jump 小程序:
mxml
<view>
<view>{{title}}</view>
<button bindtap="clickMe">请点击换标题</button>
</view>
js
Page({
data: {
title: 'Hello World',
},
//事件处理函数
clickMe: function() {
this.setData({
title: '你好,世界'
})
}
})
那么转换之后的 vue 页面应该是:
<template>
<mp-view>
<mp-view>{{title}}</mp-view>
<mp-button @click="clickMe">请点击换标题</mp-button>
<mp-view>
<template>
<script>
export default {
data(){
return {
title: '',
}
}
}
</script>
这里 data 中的变量名不是通过解析 page.js 得到的,因为开发者在编写 page.js 时是没有要求固定格式的,因此无法通过抽象代码树解析的方式拿到 data 的参数,所以这里 data 的变量名是通过解析 html 表达式拿到的。
而没有将 data 中的数据值和 method 方法写到 html 页面中,是因为这部分应由 service 逻辑层处理,view 层只需声明变量名即可。
最终再通过 vue 生成的 html 页面应为:
<html>
<head>......</head>
<body>
......
<script>
new Vue({
data() {
return {
title:""
}
},
el: "#app",
template:"<template><mp-view><mp-view>{{title}}</mp-view><mp-button @click="clickMe">请点击换标题</mp-button> <mp-view><template>"
});
</script>
</body>
</html>
如要生成上述页面,首先我们需要拿到data中的变量名,使用的方法是,通过解析 html 中的表达式来获得变量名。
然后则是构建 html 页面中的 js 代码,这里使用 babel 去构建一个 js 的 ast 抽象语法树,然后向树中插入 data 和 template 的参数和值。
由于考虑到随着页面功能的增多以及版本的迭代,直接构建目标态代码,在未来版本发生迭代时很可能会导致编译逻辑会跟随修改。为了解决这一问题,我们需要区分出目标态代码的不变部分和可变部分。把不变部分抽象为源对象,把可变部分交由 runtime 运行时框架处理。这样只需要在 rumtime 层做版本迭代而不需要编译器修改。
为了提高加载速度,我们在编译阶段做了预处理,把原本在运行时处理的渲染模版提前展开为真实的虚拟节点树。
所以本步骤整体的工作流程如下图:
最终生成的 page.view.js 应为:
let app = window.$$makeVueComponent({
data() {
return {
title: ""
};
},
el: "#app",
render: function anonymous() {
with(this){return _c('mp-view',[_c('mp-view',[_c('mp-view',[_v(_s(title))]),_v(" "),_c('mp-button',{on:{"click":function($event){return eventHandlerProxy('clickMe',$event)}}},[_v("请点击换标题")])],1)],1)}
},
staticRenderFns: []
});
new Vue(app);
(4)页面(page)中引入的组件(component)的处理
由于视图 view 层在加载页面时只会去加载该页面的 html,因此需要在编译页面时将页面中引入的组件(component)也编译进 html 中。这里的编译流程如下:
编译器会先读取所编译的 page 的 json 文件,如果 json 文件中引入了组件,则将编译后的组件的 js 加入到 page 的 html 中。然后再去读取改组件的 json 文件,查看改组件是否引入了其他组件,一直递归循环,直至所有组件都被编译进 html 文件为止。对于每个页面重复引入的组件,则只编入一次。
(5)page.js 的编译
在处理需要在 service 逻辑层运行的 page.js 时,我们不会去改变文件的代码,只是对 page.js 用 webpack 做编译打包,然后汇总到 app-service.js 文件中。
构建 npm 依赖
建信小程序支持引入第三方 npm 依赖。这里不再对 npm 的功能进行介绍,可以参考官方 npm 文档。
01
使用方法
建信小程序引入 npm 依赖,需要添加依赖和构建依赖两个步骤。
其中,构建依赖的目的是,将安装在 node_modules 下的依赖进行编译构建,然后生成到项目根目录下的 npm_miniprogram 文件夹下,以供小程序在开发过程中进行引入。
02
构建原理
在使用 npm 依赖时需要构建依赖,是因为在小程序编译时,无法在未指定路径的情况下将 require 的 node_modules 下的依赖编译进去,所以需要构建这个步骤,将依赖进行预编译,然后统一到一个目录下。
npm 依赖的构建原理如下图所示:
首先会读取项目根目录下的package.json, 如果 devDependencies/dependencies 不为空,则小程序中有使用 npm 依赖,需要构建。
在 node_modules 中找到小程序引入的依赖,查看该依赖是否指明小程序的引入入口(miniprogram_dist 目录),如果有则直接将其复制到 miniprogram_npm 路径下,如果没有则使用 webpack 由 main 入口进行编译,编译结果保存到 miniprogram_npm 路径。
对构建完成的 npm 依赖 重复第一步的步骤,直到所有依赖及依赖的依赖构建完成。
文章来源:Biaofun标梵互动(https://www.biaofun.com/)
收起阅读 »