前言
目前流行的前端UI组件库都支持移动设备优先的响应式布局
特性。但基于Mobile和PC两个场景的不同用户体验,也往往会实现Mobile和PC两个版本。
PC场景下的Web工程,如大量的后台前端管理模版
,虽然支持Mobile自适应,但其用户体验差强人意。
Cabloy采用不同的思路,仍然基于移动优先
,同时通过特殊的布局优化,使得移动页面可以完整的用于PC场景,既保证了用户体验,又提升了开发效率,大大简化了开发工作量。
演示
- PC:https://admin.cabloy.com
- Mobile:
上图
Mobile布局
PC布局
进一步分析
Cabloy的PC布局
将页面切分为若干个Mobile+Pad
的组合,带来了与众不同的用户体验。
从此开发类似于微信公众号
或H5混合开发
的项目,再也不用建立两套独立的工程。
内部机制
Cabloy前端基于Framework7
,并一直跟进其最近的开发进度,从1.0到2.0,再到3.0。Cabloy充分利用了Framework7的优良特性,充分发挥其最大价值。
Cabloy的作者zhennann
在Framework7
的贡献图如下:
Framework7将页面分为若干个View,每个View对应一个Router,管理若干个Page,从而非常方便的实现Page之间的堆叠。
Cabloy的PC布局
,将页面分为若干个Tab,每个Tab再包含若干个View。通过对Router的patch,实现Tab之间与View之间的URL跳转。
代码实现
根结点组件
Cabloy通过不同的模块
封装不同布局的实现逻辑,然后在根结点组件
中针对页面的尺寸的变化渲染不同的布局。
需要特别说明的是,Cabloy中的模块
是一个相对独立的实体,可以根据需要异步加载
,提升页面加载性能。
egg-born-front/src/base/mixin/config.js
:
config.js配置布局信息,可通过修改配置实现自己的布局管理逻辑
// config
const config = {
modules: {},
layout: {
breakpoint: 800,
items: {
mobile: {
module: 'a-layoutmobile',
component: 'layout',
},
pc: {
module: 'a-layoutpc',
component: 'layout',
},
},
},
};
egg-born-front/src/inject/pages/app.vue
:
app.vue是根结点组件,动态异步加载所需的布局组件
<script>
import Vue from 'vue';
export default {
render(c) {
const children = [];
// statusbar
children.push(c('f7-statusbar', { ref: 'statusbar' }));
// layout
if (this.layout) {
children.push(c(this.layout, {
ref: 'layout',
}));
}
const app = c('f7-app', { props: { params: this.$root.$options.framework7 } }, children);
return c('div', [ app ]);
},
data() {
return {
layout: null,
};
},
methods: {
ready() {
if (this.$f7.device.ie) {
this.$f7.dialog.alert('Supports All Modern Browsers Except IE');
return;
}
// check query
const documentUrl = location.href.split(location.origin)[1];
if (documentUrl && documentUrl.indexOf('/?') === 0) {
history.replaceState(null, '', location.origin);
}
// hash init
const hashInit = this.$meta.util.parseHash(location.href);
if (hashInit && hashInit !== '/') this.$store.commit('auth/setHashInit', hashInit);
// on resize
this.$f7.on('resize', this.onResize);
// auth echo first
this._authEcho(() => {
// resize
this.resize();
});
},
getLayout() {
return this.$refs.layout;
},
resize() {
// layout
const breakpoint = this.$meta.config.layout.breakpoint;
let layout = window.document.documentElement.clientWidth > breakpoint ? 'pc' : 'mobile';
if (!this.$meta.config.layout.items[layout]) {
layout = layout === 'pc' ? 'mobile' : 'pc';
}
// check if switch
if (this.layout === layout) {
const component = this.getLayout();
if (component) component.onResize();
} else {
// load module layout
this.$meta.module.use(this.$meta.config.layout.items[layout].module, module => {
this.$options.components[layout] = module.options.components[this.$meta.config.layout.items[layout].component];
// clear router history
this.$meta.util.clearRouterHistory();
// ready
this.layout = layout;
});
}
},
reload(ops) {
ops = ops || { echo: false };
if (ops.echo) {
this._authEcho(() => {
this._reloadLayout();
});
} else {
this._reloadLayout();
}
},
onResize: Vue.prototype.$meta.util.debounce(function() {
this.resize();
}, 300),
login(url) {
const hashInit = this.$store.state.auth.hashInit;
this.$store.commit('auth/setHashInit', null);
url = `${url}?returnTo=${encodeURIComponent(this.$meta.util.combineHash(hashInit))}`;
location.assign(url);
},
_authEcho(cb) {
// get auth first
this.$api.post('/a/base/auth/echo').then(user => {
this.$store.commit('auth/login', {
loggedIn: user.agent.anonymous === 0,
user,
});
return cb && cb();
}).catch(() => {
return cb && cb();
});
},
_reloadLayout() {
const layout = this.layout;
this.layout = null;
this.$nextTick(() => {
// clear router history
this.$meta.util.clearRouterHistory();
// restore layout
this.layout = layout;
});
},
},
beforeDestroy() {
this.$f7.off('resize', this.onResize);
},
mounted() {
this.$f7ready(() => {
this.ready();
});
},
};
</script>
Mobile布局组件
Mobile布局组件源码参见:https://github.com/zhennann/egg-born-module-a-layoutmobile/blob/master/front/src/components/layout.vue
a-layoutmobile/front/src/config/config.js
:
config.js可以灵活配置布局的显示元素
export default {
layout: {
login: '/a/login/login',
loginOnStart: true,
toolbar: {
tabbar: true, labels: true, bottomMd: true,
},
tabs: [
{ name: 'Home', tabLinkActive: true, iconMaterial: 'home', url: '/a/base/menu/list' },
{ name: 'Atom', tabLinkActive: false, iconMaterial: 'group_work', url: '/a/base/atom/list' },
{ name: 'Mine', tabLinkActive: false, iconMaterial: 'person', url: '/a/user/user/mine' },
],
},
};
PC布局组件
Mobile布局组件源码参见:https://github.com/zhennann/egg-born-module-a-layoutpc/blob/master/front/src/components/layout.vue
a-layoutpc/front/src/config/config.js
:
config.js可以灵活配置布局的显示元素
export default {
layout: {
login: '/a/login/login',
loginOnStart: true,
header: {
buttons: [
{ name: 'Home', iconMaterial: 'dashboard', url: '/a/base/menu/list', target: '_dashboard' },
{ name: 'Atom', iconMaterial: 'group_work', url: '/a/base/atom/list' },
],
mine:
{ name: 'Mine', iconMaterial: 'person', url: '/a/user/user/mine' },
},
size: {
small: 320,
top: 60,
spacing: 10,
},
},
};
结语
Cabloy是采用Javascript进行全栈开发的最佳实践。 Cabloy不重复造轮子,而是采用业界最新的开源技术,进行全栈开发的最佳组合。 欢迎大家拍砖、踩踏。 地址:https://github.com/zhennann/cabloy
这个思路还真不错!
哈哈,移动优先的框架之间就有一个,amaze ui
@cctv1005s amazeui用过,是很不错的框架。不过cabloy是不一样的适配策略,请见“前言”和“演示效果”。