Cabloy全棧JS框架微創新之一:不同的「移動優先 PC適配」

前言

目前流行的前端UI組件庫都支持移動設備優先的響應式佈局特性。但基於Mobile和PC兩個場景的不一樣用戶體驗,也每每會實現Mobile和PC兩個版本。 PC場景下的Web工程,如大量的後臺前端管理模版,雖然支持Mobile自適應,但其用戶體驗差強人意。 Cabloy採用不一樣的思路,仍然基於移動優先,同時經過特殊的佈局優化,使得移動頁面能夠完整的用於PC場景,既保證了用戶體驗,又提高了開發效率,大大簡化了開發工做量。javascript

演示

上圖

Mobile佈局

layout-mobile.png

PC佈局

layout-pc.png

進一步分析

Cabloy的PC佈局將頁面切分爲若干個Mobile+Pad的組合,帶來了不同凡響的用戶體驗。 今後開發相似於微信公衆號H5混合開發的項目,不再用創建兩套獨立的工程。前端

內部機制

Cabloy前端基於Framework7,並一直跟進其最近的開發進度,從1.0到2.0,再到3.0。Cabloy充分利用了Framework7的優良特性,充分發揮其最大價值。 Cabloy的做者zhennannFramework7的貢獻圖以下:vue

f7-zhennann.jpg

Framework7將頁面分爲若干個View,每一個View對應一個Router,管理若干個Page,從而很是方便的實現Page之間的堆疊。 Cabloy的PC佈局,將頁面分爲若干個Tab,每一個Tab再包含若干個View。經過對Router的patch,實現Tab之間與View之間的URL跳轉。java

代碼實現

根結點組件

Cabloy經過不一樣的模塊封裝不一樣佈局的實現邏輯,而後在根結點組件中針對頁面的尺寸的變化渲染不一樣的佈局。 須要特別說明的是,Cabloy中的模塊是一個相對獨立的實體,能夠根據須要異步加載,提高頁面加載性能。git

egg-born-front/src/base/mixin/config.js: config.js配置佈局信息,可經過修改配置實現本身的佈局管理邏輯github

// 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是根結點組件,動態異步加載所需的佈局組件api

<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佈局組件源碼參見:github.com/zhennann/eg…微信

a-layoutmobile/front/src/config/config.js: config.js能夠靈活配置佈局的顯示元素app

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佈局組件源碼參見:github.com/zhennann/eg…異步

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不重複造輪子,而是採用業界最新的開源技術,進行全棧開發的最佳組合。 歡迎你們拍磚、踩踏。 地址:github.com/zhennann/ca…

相關文章
相關標籤/搜索