BUI是什么?
BUI 是用来快速构建界面交互的渐进式UI框架, 专注webapp开发, 开发者只需关注业务的开发, 界面的布局及交互交给BUI, 开发出来的应用, 可以嵌入平台 ( 微信公众号, 微信小程序webview, 聆客, 钉钉, 淘宝, 支付宝等 ), 亦可以跟其它第三方平台打包成独立应用( Bingotouch , Cordova , Dcloud , APICloud , Appcan 等), 最终可以全跨平台展示. (包括Ipad)结合BUI提供的BUI-Fast编辑插件, NPM工具, BUI更是一个移动快速开发的解决方案. 可以解决以下常见问题.
- 移动端的适配兼容问题 (ui)
- 交互体验不统一问题 (ui)
- 复杂交互的控件冲突问题 (ui)
- 多人协作问题 (模块化)
- 微信开发的缓存问题 (模块化)
- 后退刷新问题 (单页路由)
- 后退多层问题 (单页路由)
- 调试数据跨域问题 (npm)
- 本地服务器架设问题 (npm)
- 脚本编译问题 (npm)
- 打包安全问题 (npm)
- 同步刷新问题 (npm)
- 重复安装依赖 (npm)
- 开发效率问题 (bui-fast)
- 使用规范问题 (bui-fast)
适合开发者
- 后台开发者(php,java,.net)
- 前端开发者
- 美工
- jquery开发者
- vue开发者
- 安卓开发者
- IOS开发者
1. BUI框架新功能-组件化
什么是组件化呢?
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,有多种属性、状态反映其内部特性。
1.1 单页组件
在BUI 1.6版本以前有没有组件化呢?
先来看看组件包含什么, 模板, 模块, 样式, 数据四个部分, BUI一直有组件化, 单页就是一个组件, 一个单页由一个同名html(包含样式),js组成. 移动开发由于页面较小, 把一个页面看成是一个大的组件, 组件里面会结合多个控件, 比方选项卡,轮播图,列表刷新等.
比方下面例子:
bui.load({
url:"pages/list/index.html"
})
跳转到列表页面, 我们便可知道该目录下还有 pages/list/index.js
文件来处理业务, 默认的模块名为pages/list/index
. 最简单的路由, 一切无需配置.
pages/list/index.html
<div class="bui-page bui-box-vertical">
<header></header>
<main>
<!-- 轮播图 -->
<div id="slide" class="bui-slide"></div>
</main>
<footer></footer>
</div>
pages/list/index.js
loader.define(function(require,export,module){
// 业务代码
var pageview = {
init: function(){
// 轮播图控件初始化
var uiSlide = bui.slide({
id:"#slide",
height: 300,
data: [{
image:"images/slide01.jpg"
},{
image:"images/slide02.jpg"
}]
})
}
}
// 页面跳转便执行
pageview.init();
return pageview;
})
路由跳转内部做了什么?
// 加载模板
loader.import("pages/list/index.html",function(res){
// id 指向动态创建的路由页面id
$("#id").html(res);
// 执行js模块, 如果该模块没有被创建过, 会自动执行
loader.require("pages/list/index")
})
只是简单示例说明, 实际做了更多复杂的处理. 单页的开发模块里面,$
选择器要替换成router.$
选择器, 如果页面重复被加载进来,$
从document
查找会导致找到多个相同ID,router.$
则限制了只在当前页面.
1.2 控件组件化
随着业务的深入, 单页组件里面承载了较多业务逻辑, 不好维护. 上面的例子我们看到, pages/list/index
模块里面, 初始化了一个控件, 一个页面如果只有一个控件, 那也没什么, 但往往不止这些, 我们可能页面还有TAB, 每个TAB里面就有一个轮播图组件, 那我们就要区分不同的ID初始化不同的轮播图了. 如果把轮播图抽离成一个单独的组件, 这部分业务就可以抽离出来.
定义一个轮播图组件
我们新建了一个目录 components
用来存放这些抽离的组件.
轮播图模板
pages/components/slide/index.html
<div class="bui-slide"></div>
id="slide"
这个属性我们去掉了,如果模板包含id,意味着创建出来的组件会有多个相同id.
轮播图组件定义
pages/components/slide/index.js
loader.define(function(require,export,module){
// 接收`component` 标签上的属性参数
var params = bui.history.getParams(module.id);
// 轮播图控件初始化
var uiSlide = bui.slide({
// 通过父层的id 找到当前的 bui-slide
id:`#${module.id} .bui-slide`,
height: 300,
data: [{
image:"images/slide01.jpg"
},{
image:"images/slide02.jpg"
}]
})
return uiSlide;
})
轮播图组件加载, component
标签如果无id属性, 会自动创建一个随机guid
, 也就是组件内部获取到的 module.id
pages/list/index.html
<div class="bui-page bui-box-vertical">
<header></header>
<main>
<!-- 新闻轮播图 type 为自定义属性,用于区分不同数据 -->
<component name="pages/components/slide/index" type="news"></component>
<!-- 视频轮播图 -->
<component name="pages/components/slide/index" type="video"></component>
</main>
<footer></footer>
</div>
轮播图样式定义
样式没有独立的作用域, 要防止跟其它样式冲突, 那组件需要一个独立的样式名.
<style>
.slide-skin .bui-slide-main {}
</style>
<div class="bui-slide slide-skin"></div>
组件包含数据,以确保该组件能正常运行, 我们可以把轮播图的组件再进行优化.
抽离轮播图测试数据, 示例数据
pages/components/slide/index.json
[{
image:"images/slide01.jpg"
},{
image:"images/slide02.jpg"
}]
完整的轮播图组件
pages/components/slide/index.js
loader.define(function(require,export,module){
// 接收`component` 标签上的属性参数
var params = bui.history.getParams(module.id);
// 轮播图控件初始化
var uiSlide = bui.slide({
// 通过父层的id 找到当前的 bui-slide
id:`#${module.id} .bui-slide`,
height: 300,
data: []
})
// 通过不同参数请求区分不同数据
bui.ajax({
// 模块在被加载或者被移到其它路径下, 都不会影响到这个路径的地址.
url:`${module.path}/index.json`,
data:{
// 请求接口的不同类型
type: params.type
},
success: function(res){
// 修改轮播图数据
uiSlide.option("data",res);
}
})
return uiSlide;
})
测试组件是否正确
组件预览:
地址栏上输入以下地址便可预览组件效果.index.html#pages/components/slide/index
模拟属性传参
在地址上加上参数index.html#pages/components/slide/index?type=news
2. BUI框架新功能-组件层
BUI 组件有3种表现形式, 路由的跳转是页面组件,component
标签加载是一种局部组件,bui.page
是弹出加载组件, 层级最高.
比方: 点击我的, 需要在当前页插入一个登录页面.
pages/main/main.js
loader.define(function(require,export,module){
var pageveiw = {
init: function(){
// 初始化
this.tab = this.tabInit();
},
isLogin: false,
pageLogin: null,
tabInit: function(){
var that = this;
var tab = bui.tab({
id: "#tab"
});
// tab 的滑动,点击,都会触发 to 事件.
tab.on("to",function(){
var index = this.index();
// 如果跳转到第3个,并且未登录, 则插入登录页.
if( index === 3 && !that.isLogin ){
if( that.pageLogin ){
// 第二次打开就好
that.pageLogin.open();
return;
}
// 第一次初始化
that.pageLogin = bui.page({
url:"pages/login/index.html",
// 告诉登录页, 是从tab的第三个跳转过去的, 那登录回来以后就可以再跳转到第三个Tab.
param: {
type: "tab",
index: 3
}
})
}
})
return tab;
}
}
// 初始化
pageveiw.init();
return pageview;
})
pages/login/index.js
loader.define(function(require,export,module){
var parasm = bui.history.getParams(module.id);
var pageview = {
init: function(){
this.bind();
},
bind: function(){
router.$("#btnLogin").click(function(){
// 检测登录是否成功, 是则跳转回上一个页面, 并且触发to事件
// 主动关闭
// var dialog = bui.history.getPageDialog(module.id);
// dialog.close();
bui.back(function(mod){
// 关闭弹窗
mod.pageLogin && mod.pageLogin.close();
// 修改登录状态
mod.isLogin = true;
// 拿到上一个模块,调用tab实例的to方法, 跳到第3各索引, 触发 监听的on事件.
mod.tab.to(parasm.index)
})
})
}
}
// 初始化
pageview.init();
return pageview;
})
作为登录页面组件, 就需要处理多种类型, 比方从路由跳转的, 比方以组件层的方式加载的, 那分别要做什么事情?
这个登录的完整示例工程可以在 BUI的3种权限登录 里面找到. tablogin2
工程.
3. BUI框架新功能-实例分发
实例分发其实是bui.store
的一个mixins
参数, 这个跟vue
的混入是一样的. 适合处理比较复杂的页面, 把模块分发出去, 便于维护, 跟组件是一样的道理, 但这个是分离的.
3.1 实例分发
比方有个详情页面, 详情里面有表单, 正文, 附件.
这里我们使用 bui.store
来实现. 案例的预览地址 实例分发
详情模板
pages/detail/index.html
<div class="bui-page bui-box-vertical">
<header>
<div class="bui-bar">
<div class="bui-bar-left">
<a class="bui-btn" onclick="bui.back();"><i class="icon-back"></i></a>
</div>
<div class="bui-bar-main">详情</div>
<div class="bui-bar-right">
</div>
</div>
<ul id="floorNav" class="bui-nav bui-nav-skin01">
<li class="bui-btn active">表单</li>
<li class="bui-btn">正文</li>
<li class="bui-btn">附件(2)</li>
</ul>
</header>
<main>
<div id="floor" class="bui-floor">
<div class="bui-floor-main container-y">
<div class="panel-list bui-interval">
<!-- 表单 -->
<view name="pages/store/views/form/index"></view>
<!-- 正文 -->
<view name="pages/store/views/article/index"></view>
<!-- 附件 -->
<view name="pages/store/views/attach/index"></view>
</div>
</div>
<div class="bui-floor-foot"></div>
</div>
</main>
</div>
详情模块
pages/detail/index.js
loader.define([
"pages/store/views/form/index",
"pages/store/views/article/index",
"pages/store/views/attach/index"
],
function(form, article, attach, require, exports, module) {
// 初始化数据行为存储
var bs = bui.store({
el: ".bui-page",
scope: "page",
data: {
title: "测试标题"
},
mixins: [form, article, attach],
methods: {},
watch: {},
computed: {},
templates: {},
beforeMount: function() {
// 数据解析前执行, 修改data的数据示例
// this.$data.a = 2
},
mounted: function() {
var that = this;
// 数据解析后执行
var floor = bui.floor({
id: "#floor",
menu: "#floorNav",
floorItem: "view"
})
}
})
return bs;
})
表单模板:
pages/store/views/form/index.html
<style>
.panel-form .bui-list .bui-btn {
border-bottom: 0;
}
</style>
<div class="bui-panel panel-form bui-floor-item">
<div class="bui-panel-head" name="page">表单</div>
<div class="bui-panel-main container-xy" text="page" b-template="page.tplForm(page.formData)">
</div>
</div>
表单模块:
pages/store/views/form/index.js
loader.define(function(require,exports,module) {
// 在这里初始化控件
var pageview = {
data: {
formData: {
title:"《广州XXX2020年年中预算审批》",
phone: "13800138000"
}
},
methods: {
callhim: function(phone){
// 打电话
bui.unit.tel(phone);
}
},
templates: {
tplForm: function(data) {
var html = "";
html += `<ul class="bui-list list-form">
<li class="bui-btn clearactive bui-box-align-top">
<label class="bui-label">标题</label>
<div class="span1">
<div class="bui-value">${data.title}</div>
</div>
</li>
<li class="bui-btn clearactive bui-box-align-top">
<label class="bui-label">电话</label>
<div class="span1">
<div class="bui-value phone" b-click="page.callhim2(${data.phone})">
<b>${data.phone}</b><i class="icon-phone"></i>
</div>
</div>
</li>
...
</ul>`;
return html;
}
},
mounted: function(param) {
console.log("mounted form")
}
};
// 抛出模块
return pageview;
})
其它组件类似, 返回一个对象, 最终在详情的实例上合并. 这种分发只是业务的拆分, 并无独立作用域. 如果需要独立作用域, 则应该改为以下加载.
3.2 独立作用域
详情模板
pages/detail/index.html
<div class="bui-page bui-box-vertical">
<header>
...
</header>
<main>
<div id="floor" class="bui-floor">
<div class="bui-floor-main container-y">
<div class="panel-list bui-interval">
<!-- 表单 -->
<component name="pages/store/views/form/index"></component>
<!-- 正文 -->
<view name="pages/store/views/article/index"></view>
<!-- 附件 -->
<view name="pages/store/views/attach/index"></view>
</div>
</div>
<div class="bui-floor-foot"></div>
</div>
</main>
</div>
详情模块
pages/detail/index.js
loader.define([
"pages/store/views/article/index",
"pages/store/views/attach/index"
],
function(article, attach, require, exports, module) {
// 初始化数据行为存储
var bs = bui.store({
el: ".bui-page",
scope: "page",
data: {
title: "测试标题"
},
mixins: [article, attach],
methods: {},
watch: {},
computed: {},
templates: {},
beforeMount: function() {
// 数据解析前执行, 修改data的数据示例
// this.$data.a = 2
},
mounted: function() {
var that = this;
// 数据解析后执行
var floor = bui.floor({
id: "#floor",
menu: "#floorNav",
floorItem: "view"
})
}
})
return bs;
})
表单模板:
pages/store/views/form/index.html
scope
改为了form
<style>
.panel-form .bui-list .bui-btn {
border-bottom: 0;
}
</style>
<div class="bui-panel panel-form bui-floor-item">
<div class="bui-panel-head" name="page">表单</div>
<div class="bui-panel-main container-xy" text="page" b-template="form.tplForm(form.formData)">
</div>
</div>
表单模块
pages/store/views/form/index.js
loader.define(function(require,exports,module) {
// 在这里初始化控件
var bs = bui.store({
el: `#${module.id}`,
scope: "form",
data: {
formData: {
title:"《广州XXX2020年年中预算审批》",
phone: "13800138000"
}
},
methods: {
callhim: function(phone){
// 打电话
bui.unit.tel(phone);
}
},
templates: {
tplForm: function(data) {
var html = "";
html += `<ul class="bui-list list-form">
<li class="bui-btn clearactive bui-box-align-top">
<label class="bui-label">标题</label>
<div class="span1">
<div class="bui-value">${data.title}</div>
</div>
</li>
...
</ul>`;
return html;
}
},
mounted: function(param) {
console.log("mounted form")
}
});
// 抛出模块
return bs;
})
4. BUI框架新功能-历史记录
在使用单页路由初始化以后, 我们便有了一个历史记录router.history
, 新版1.6以后, 把router.history
抽离出来, 通过bui.history
去访问. 这样无论是单页开发, 还是多页开发, 都能通过bui.history
去获取实例及参数. 并且在这个对象里面, 页面传参,组件传参,page传参, 都可以在这个历史记录里面获取到之间的依赖关系.组件的加载是一条线, 线的末端可以操控线的前端.
var allHistory = bui.history.get();
// allHistory 默认的历史记录
[{
component: {},
effect: "push",
exports: {},
id: "buib7522-dc12-3f33-cdb5-29122a2cf1f6",
name: "pages/store/view",
page: {},
param: {},
replace: false,
syncHistory: true,
toggle: null,
url: "pages/store/view.html"
}]
4.1 多页开发
pages/detail/index.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>BUI多页开发示例</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/buijs/lib/latest/bui.css">
</head>
<body>
<div class="bui-page bui-box-vertical">
<header>
<div class="bui-bar">
<div class="bui-bar-left">
<div class="bui-btn" onclick="bui.back();"><i class="icon-back"></i></div>
</div>
<div class="bui-bar-main">
多页加载组件
</div>
<div class="bui-bar-right">
</div>
</div>
</header>
<main>
<!-- 加载轮播图组件 -->
<component name="pages/components/slide/index"></component>
</main>
</div>
<script src="https://cdn.jsdelivr.net/npm/buijs/lib/zepto.js"></script>
<script src="https://cdn.jsdelivr.net/npm/buijs/lib/latest/bui.js"></script>
<script src="index.js"></script>
</body>
</html>
多页的初始化
pages/detail/index.js
bui.ready(function(){
// 初始化
var allHistory = bui.history.getLast();
// 多页开发的历史记录, 永远只有一个. 页面跟页面之间无法交互, 但是页面跟组件跟组件层之间的交互是没问题的.
})
4.2 单页开发
pages/detail/index.html
<div class="bui-page bui-box-vertical">
<header>
<div class="bui-bar">
<div class="bui-bar-left">
<div class="bui-btn" onclick="bui.back();"><i class="icon-back"></i></div>
</div>
<div class="bui-bar-main">
单页加载组件
</div>
<div class="bui-bar-right">
</div>
</div>
</header>
<main>
<!-- 加载轮播图组件 -->
<component name="pages/components/slide/index"></component>
</main>
</div>
pages/detail/index.js
loader.define(function(require,export,module){
// 获取最后一条历史记录
var currentHistory = bui.history.getLast();
})
一样的组件代码, 除了脚本模块的定义不同以外. 多页简单, 单页则在体验,跟操控上会有更多灵活空间. 可以根据需要自行选择.
5. buijs-cli 工具
推荐重新安装buijs
cli工具. 记得关闭360等一切会阻止C盘写入的程序.
npm install -g buijs
5.1 新增创建案例的命令
// 全部权限示例
buijs create -t case-indexlogin
// 部分权限示例
buijs create -t case-tablogin
// 163的组件化示例
buijs create -t case-163
5.2 新版的模板源默认更新为 gitee
, 国内构建的速度会快很多.
5.3 新增根据node版本,自动创建对应的工程文件.
5.4 创建app的工作空间, 无需每次都安装依赖, 具体教程请查看说明文件.
6. bui-fast 编辑器插件
结合新版出了一些快速书写, 建议更新, 如果使用 vscode
只需在插件搜索 bui-fast
便可.
7. 其它一些控件的修复及新增
新增了一些方法及控件, 其它更新了控件的一些问题, 就不一一列举了, 感兴趣可以看看官网的changelog
订阅号
码字不易, 欢迎关注bui神速
, 跟我们一起交流移动开发的问题, 常见问题还请搜索官方文档, 我们会不定期更新一些技巧.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。