barm 一個前端微服務的膠水方案

前端分岔口

筆者認爲,前端行業如今處於一個分岔口:html

  • 漸進式 WebApp
  • 框平臺原生 App

其中 WebApp 隨着各行各業業務的不斷髮展,僅僅 SPA 應用已經很難知足現有的迭代開發;各種微服務方案開始被提上議程,其中以 web-components 爲基礎的微服務方案關注度較高。前端

barm 是一個漸進式、微服務膠水方案,基於 web-components,而且對 React 開發者友好。react

這意味着 barm 必須知足如下條件:git

  • 聲明式的開發方式,和 react 雷同的 API,這才能對 React 開發者友好
  • 自身體積足夠小(gzip: 4KB),能夠無痛嵌入在其餘大型框架中
  • 漸進式,能夠成爲一個獨立框架進行運做,知足經常使用的開發需求,而且能夠獨立發佈部署
  • 能夠和 Vanilla JS 很好的配合

基本使用

由於 barm 定位是一個前端微服務的膠水方案,因此它不該該特別依賴於編譯環境;github

爲此,barm 使用 html 字符串解析,不須要配置 JSX 解析的 babel,咱們來看一個例子:web

註冊 web-componentredux

import { html, Component, define } from 'barm';

class User extends Component {
  render = () => {
    return html` <div>page-user</div> `;
  };
}

define('page-user')(User);
複製代碼

渲染到頁面設計模式

const pageUser = document.createElement('page-user');
document.body.append(pageUser);
複製代碼

barm 每一個組件都是一個 web-component, 遵循 react API 及生命週期數組

註冊一個組件:前端框架

import { define, html, Component } from 'barm';

class Home extends Component {
  state = {
    num: 0,
  };

  handleAddNum = () => {
    this.setState(({ num }) => {
      return {
        num: num + 1,
      };
    });
  };

  render = () => {
    return html` <div> <div>page-home: ${this.state.num}</div> <button onclick=${this.handleAddNum}>add num</button> </div> `;
  };
}

define('page-home')(Home);
複製代碼

在其餘組件內使用以前註冊的組件

import { html, Component, define } from 'barm';

class User extends Component {
  render = () => {
    return html` <div> <div>render-other-component</div> <page-home /> </div> `;
  };
}

define('page-user')(User);
複製代碼

支持組件名爲函數

import { html, Component, define } from 'barm';

class User extends Component {
  renderBody = ({ name }) => {
    return html` <div>render-${name}</div> `;
  };

  render = () => {
    return html` <div> <${this.renderBody} name="hello" /> </div> `;
  };
}

define('page-user')(User);
複製代碼

設計模式彙總

咱們將一步步演示如何實現 react 的全部設計模式:

  • Class Component
  • Pure Component
  • Hooks
  • Render Props
  • HOC

支持 Pure Component

import { html, define } from 'barm';

define('page-user')(() => {
  return html` <div> <div>page-user</div> </div> `;
});
複製代碼

支持 Hooks: Function Component 支持生命週期

函數組件的第二個參數是一個 hooks,它會暴露一個 Class Component 完整的生命週期及類成員變量給到函數組件;

函數組件能夠藉此實現全部類組件的功能:

import { html, define, useHooks } from 'barm';

define('render-hooks')((props, hooks) => {
  if (!hooks.isInited) {
    hooks.state = {
      name: '',
    };
    hooks.componentDidMount = () => {
      //
    };
    hooks.handleOnInput = e => {
      hooks.setState({ name: e.target.value });
    };
  }

  return html` <div> <div>${hooks.state.name}</div> <input placeholder="test-hooks" oninput=${hooks.handleOnInput} /> </div> `;
});
複製代碼

支持 Hooks 抽象

React hooks 的一個特色就是能夠將生命週期的邏輯抽離並複用,在 barm 中咱們也能夠實現同質效果;

barm 的 hooks 實現和官方的有出入,這是由於 react-hooks 是將狀態捆綁在 React Firber 上,這將要求整個項目上下文僅有 1 個 react 對象,barm 是一個前端微服務框架,更適合使用類組件的方式將每一個狀態隔離在各自組件中,因此繼續沿用類組件的生命週期:

import { html, define, useHooks } from 'barm';

// 將邏輯抽離到公共區域,以複用
const useSetName = useHooks(hooks => {
  if (!hooks.isInited) {
    hooks.state = {
      name: '',
    };
    hooks.componentDidMount = () => {
      //
    };
    hooks.handleOnInput = e => {
      hooks.setState({ name: e.target.value });
    };
  }
});

define('render-hooks')((props, hooks) => {
  useSetName(hooks);

  return html` <div> <div>${hooks.state.name}</div> <input placeholder="test-hooks" oninput=${hooks.handleOnInput} /> </div> `;
});
複製代碼

支持 Render Props

import { html, define } from 'barm';

define('render-props-button')(({ children }) => {
  return html` <button>${children('button-name')}</button> `;
});

define('page-user')(() => {
  return html` <div> <div>page-user</div> <render-props-button> ${name => html` <span>${name}</span> `} </render-props-button> </div> `;
});
複製代碼

支持 HOC

HOC(高階函數)是 React 早起的一種生命週期抽象的設計模式, 雖然咱們有了 hooks\renderProps 等同類的抽象行爲,不過 Barm 也一樣支持 HOC

import { html, define, Component } from 'barm';

define('the-button')(props => {
  return html` <button ...${props}>hello-hoc</button> `;
});

function withLogAtDidMount() {
  return (name, connectName) => {
    define(name)(
      class extends Component {
        componentDidMount = () => {
          console.log('hoc-log');
        };
        render = () => {
          return html` <${connectName} ...${this.props} /> `;
        };
      },
    );
  };
}

withLogAtDidMount()('hoc-button', 'the-button');

define('page-hoc')(() => {
  return html` <div> <div>page-hooks</div> <hoc-button style="font-size: 20px;" /> </div> `;
});
複製代碼

shadow-dom

barm 雖然使用了 web-components 可是並無使用 shadow-dom

這是由於 barm 每一個組件都是一個 web-component,shadow-dom 的樣式隔離對於不少業務情景並沒有必要,現實中咱們使用 BEM 已經可以很好的隔離樣式污染和更好的共享樣式,將來能夠考慮添加一個屬性以決定是否開啓 shadow-dom。

嵌入到 React 中

barm 立志於建立一個微服務膠水方案,意味着它建立的組件很容易的在各框架內使用;

以 react 爲例子, 假定咱們使用 barm 建立了一個 page-home web-component, 咱們在 react 使用它和使用 原生 DOM 元素相似:

import React from 'react';
import 'page-home';

export function HomePage() {
  return (
    <div> <page-home name="home" /> </div> ); } 複製代碼

獨立發佈、獨立部署

barm 除了能夠很輕鬆的在各前端框架內使用,還須要知足自身的獨立發佈、獨立部署,因此它須要一些必備生態:狀態管理和路由;

barm 實現了 barm-redux 和 barm-redux-route,其中 route 組件是和狀態管理捆綁的,每當路由發生變化,咱們能夠派發事件變動名,若要更簡化的路由組件可使用 vanilla-route

  • barm-redux
  • barm-redux-route

Github 地址

blog 地址

相關文章
相關標籤/搜索