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 地址

相关文章
相关标签/搜索