npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

servkit

v1.0.16-alpha.0

Published

主-从 应用架构的一种实现,目的是通过主应用提供的标准接口来实现从应用二次开发(二方/三方),以实现主应用的平台性开放能力。

Downloads

118

Readme

servkit

主-从 应用架构的一种实现,目的是通过主应用提供的标准接口来实现从应用二次开发(二方/三方),以实现主应用的平台性开放能力。

基于能力特性,servkit能够做:

  • 对于平台性的前端应用(通常是复杂的),可以做小程序基础框架和SDK;
  • 对于复杂的前端巨应用,可以做微前端的架构实践;
  • 对于简单的前端页面,可以作为基础的RPC通信库;

能力特性:

  • 微应用架构:主应用-从应用
  • 三方应用开放能力
    • 微应用
      • 独立运行环境(iframe)
      • 同一运行环境
    • 定制化SDK
    • 服务/API管理
    • 权限管理:应用粒度、服务粒度、API粒度
  • 声明式服务API
  • 声明式服务事件
  • RPC通信协议

为什么要开发 servkit ?

  • GUI应用H5化

    在GUI程序的领域,传统的原生开发逐步被H5 WEB技术所取代,因为H5表现出了非常优秀的开发体验和生产效率,这在PC平台尤为明显;那么H5 WEB技术面临的是更为复杂的软件工程和系统程序。

  • 云端化

    云端化将一直保持主旋律,当前在终端操作的软件系统会越来越多的向云端发展,而WEB技术作为云端的主要技术之一,WEB前端领域会面临更多传统软件系统的开发挑战,工程规模、系统复杂度、架构设计等(在工程化上已经能看到一些变化在应对这些挑战,比如TypeScript,Webpack等)。

  • 开放能力

    SAAS服务应该是开放的。对于范服务的提供商,通过提供开放能力,让上下游生态、周边生态、行业细分生态等能够接入,达到能力互补/能力创新(服务场景拓宽);一体化是目的,开放和封闭都是实现路径,但是封闭的一体化方案,ROI可能较低,自建意味更多的人力、财力、时间,对于客户也缺少服务弹性。

在这个背景下,在技术角度看,要应对的是巨型应用;在业务角度看,要应对的是开放能力。而这些也会是WEB领域发展的共性问题,所以servkit针对这些问题,尝试提供一个通用处理方案;巨型应用使用微前端架构(SOA)思路,开放能力使用小程序架构思路。

快速使用

安装

npm install servkit 或者yarn add servkit

独立运行环境微应用(IFrame)

IFrame页面间通信是最常见的场景,这种场景分为承载页(打开iframe页面)和内容页(iframe页面),servkit在这个场景提供了:

  • 语义化操作:iframe的打开、页面间通信,而无需关注底层琐碎代码的开发;
  • 规范化通信:基于提供的service机制,规范了RPC通信间协议,也提供了通信间的类型检测;

相互间的关系:

承载页 -> sappMGR -> service decl <- sappSDK <- IFrame

承载页面代码(主应用)

打开页面:

// 在承载页面都通过sappMGR进行操作
import { sappMGR } from 'servkit';
import { CommonService } from 'servkit-service-decl';
import { CommonServiceImpl } from './service/CommonServiceImpl';

sappMGR.create(
{
    // 页面的ID
    id: 'com.page.demo',
    // 页面的版本
    version: '1.0.0',
    // 页面的名称
    name: 'demo',
    // 页面的地址
    url: 'https://www.demo.com',
    // 页面的可选参数
    options: {
    }
}, 
{
    // 页面布局相关配置
    layout: {
        // iframe元素挂载的容器DOM元素
        container: domElement,
    },
    // 配置承载页面向iframe页面提供的服务,iframe页面可直接调用相应的服务API
    services: [
        // 注册CommonService
        {
            // 服务声明,类型声明会和iframe进行共享,保证了API的类型检查 
            decl: CommonService,
            // 服务实现,实现只存在与承载页代码之中,iframe页面不感知
            impl: CommonServiceImpl
        },
    ],
}).then((app) => {
    // 页面创建成功,app为对应iframe的抽象体;
    // 可通过app直接与iframe通信,以及显示、隐藏和关闭操作
}).catch((e) => {
    // 页面创建失败
});

声明服务:

// CommonService.ts
// 服务声明通常单独放在一个npm包里面,能够给实现方和使用方进行共享使用
import { ServService, ServEventer, anno, ServAPIArgs, ServAPIRetn, API_UNSUPPORT } from 'servkit';

// 声明一个服务class,该class定义了IFrame间的通信语义,类型声明
@anno.decl({
    // 服务id
    id: 'demo.service.common',
    // 服务版本
    version: '1.0.0',
})
export class CommonService extends ServService {
    // 声明一个服务notify型api,notify型的api不带有返回数据;
    // 参数为一个字符串;
    @anno.decl.notify()
    message(args: ServAPIArgs<string>): ServAPIRetn {
        return API_UNSUPPORT();
    }

    // 声明一个服务api,具有返回数据;
    // 参数为一个字符串,返回boolean值;
    @anno.decl.api()
    confirm(args: ServAPIArgs<string>): ServAPIRetn<boolean> {
        return API_UNSUPPORT();
    }
}

实现服务:

// CommonService.ts
// 服务声明通常单独放在一个npm包里面,能够给实现方和使用方进行共享使用
import { CommonService } from 'servkit-service-decl';
import { anno, ServAPIArgs, ServAPIRetn, API_SUCCEED, DeferredUtil } from 'servkit';
// 通过antd实现message和confirm
import message from 'antd/lib/message';
import 'antd/lib/message/style/css';
import Modal from 'antd/lib/modal';
import 'antd/lib/modal/style/css';

// 实现一个服务class
@anno.impl()
export class CommonServiceImpl extends CommonService {
    // 实现message接口
    message(args: ServAPIArgs<string>): ServAPIRetn {
        message.success(args);
        return API_SUCCEED();
    }

    // 实现confirm接口
    confirm(args: ServAPIArgs<string>): ServAPIRetn<boolean> {
        const deffered = DeferredUtil.create<boolean>();
        Modal.confirm({
            title: '确认',
            content: args,
            onCancel: () => {
                deffered.resolve(false);
            },
            onOk: () => {
                deffered.resolve(true);
            }
        })
        return deffered;
    }
}

内容页面相关代码(微应用)

启动页面:

// 在内容页面都通过sappSDK进行操作
import { sappSDK } from 'servkit';
import { IFrameCommonService } from 'servkit-service-decl';
import { IFrameCommonServiceImpl } from './service/CommonServiceImpl';

sappSDK
.setConfig(
{
    // 配置iframe页面向承载页面提供的服务,承载页面可直接调用相应的服务API
    services: [
        // 注册IFrameCommonService
        {
            // 服务声明,类型声明会和iframe进行共享,保证了API的类型检查 
            decl: IFrameCommonService,
            // 服务实现,实现只存在与承载页代码之中,iframe页面不感知
            impl: IFrameCommonServiceImpl
        },
    ],
})
// 启动内容页面
.start()
.then((app) => {
    // 页面创建成功,app为对应iframe的抽象体;
    // 可通过app直接与承载页进行通信,以及关闭操作
}).catch((e) => {
    // 页面创建失败
});

页面通信:

import { sappSDK } from 'servkit';
import { CommonService } from 'servkit-service-decl';

// 获取服务
const common = await sappSDK.service(CommonService);
// 调用服务API
common.message('调用服务');

双向通信

这里只展示了从IFrame向承载页的单向通信,但servkit提供了IFrame页面和承载页面的双向通信机制,IFrame自身也可以向承载页暴露服务,具体使用与上述例子类似(不同点在于承载页通过sappMGR.create后的app获取服务)。

基座运行环境微应用

基于基座应用上搭建微应用也是典型的场景,这里基座应用负责提供基础能力,而微应用以HTML片段(或者JSBundle链接)形式暴露,由基座应用进行管理,并运行在基座应用的上下文,利用基座应用的能力API做业务开发;针对两种场景,servkit做了归一化。

相互间的关系:

基座应用 -> sappMGR -> service decl <- sappSDK <- 微应用

基座应用代码(主应用)

打开微应用:

// 在承载页面都通过sappMGR进行操作
import { sappMGR } from 'servkit';
import { CommonService } from 'servkit-service-decl';
import { CommonServiceImpl } from './service/CommonServiceImpl';

sappMGR.create(
{
    // 页面的ID
    id: 'com.page.demo',
    // 页面的版本
    version: '1.0.0',
    // 页面的名称
    name: 'demo',
    // 页面的地址,基于url提供的html片段
    url: 'https://www.demo.com',
    // 或者直接基于html片段
    html: '<html><script src="xxxx"></script></html>',
    // 应用类型为异步加载类型
    type: ESappType.ASYNC_LOAD
    // 页面的可选参数
    options: {
    }
}, 
{
    // 页面布局相关配置
    layout: {
        // 微应用的容器DOM元素
        container: domElement,
    },
    // 配置承载页面向iframe页面提供的服务,iframe页面可直接调用相应的服务API
    services: [
        // 注册CommonService
        {
            // 服务声明,类型声明会和iframe进行共享,保证了API的类型检查 
            decl: CommonService,
            // 服务实现,实现只存在与承载页代码之中,iframe页面不感知
            impl: CommonServiceImpl
        },
    ],
}).then((app) => {
    // 页面创建成功,app为对应iframe的抽象体;
    // 可通过app直接与iframe通信,以及显示、隐藏和关闭操作
}).catch((e) => {
    // 页面创建失败
});

声明服务:

// CommonService.ts
// 服务声明通常单独放在一个npm包里面,能够给实现方和使用方进行共享使用
import { ServService, ServEventer, anno, ServAPIArgs, ServAPIRetn, API_UNSUPPORT } from 'servkit';

// 声明一个服务class,该class定义了IFrame间的通信语义,类型声明
@anno.decl({
    // 服务id
    id: 'demo.service.common',
    // 服务版本
    version: '1.0.0',
})
export class CommonService extends ServService {
    // 声明一个服务notify型api,notify型的api不带有返回数据;
    // 参数为一个字符串;
    @anno.decl.notify()
    message(args: ServAPIArgs<string>): ServAPIRetn {
        return API_UNSUPPORT();
    }

    // 声明一个服务api,具有返回数据;
    // 参数为一个字符串,返回boolean值;
    @anno.decl.api()
    confirm(args: ServAPIArgs<string>): ServAPIRetn<boolean> {
        return API_UNSUPPORT();
    }
}

实现服务:

// CommonService.ts
// 服务声明通常单独放在一个npm包里面,能够给实现方和使用方进行共享使用
import { CommonService } from 'servkit-service-decl';
import { anno, ServAPIArgs, ServAPIRetn, API_SUCCEED, DeferredUtil } from 'servkit';
// 通过antd实现message和confirm
import message from 'antd/lib/message';
import 'antd/lib/message/style/css';
import Modal from 'antd/lib/modal';
import 'antd/lib/modal/style/css';

// 实现一个服务class
@anno.impl()
export class CommonServiceImpl extends CommonService {
    // 实现message接口
    message(args: ServAPIArgs<string>): ServAPIRetn {
        message.success(args);
        return API_SUCCEED();
    }

    // 实现confirm接口
    confirm(args: ServAPIArgs<string>): ServAPIRetn<boolean> {
        const deffered = DeferredUtil.create<boolean>();
        Modal.confirm({
            title: '确认',
            content: args,
            onCancel: () => {
                deffered.resolve(false);
            },
            onOk: () => {
                deffered.resolve(true);
            }
        })
        return deffered;
    }
}

微应用相关代码

微应用启动:

// 异步微应用需要通过SappSDK进行注册
import { SappSDK } from 'servkit';
import { IAsyncAppCommonService } from 'servkit-service-decl';
import { IAsyncAppCommonServiceImpl } from './service/CommonServiceImpl';
import { CommonService } from 'servkit-service-decl';

// 注册异步应用,这里的id需要与基座应用中对应
SappSDK.declAsyncLoad('demo.service.common', {
    // 微应用的启动函数
    bootstrap: (sdk) => {
        // 后续通过sdk进行服务调用
        sdk.setConfig(
        {
            // 配置微应用向基座应用提供的服务,基座应用可直接调用相应的服务API
            services: [
                // 注册IAsyncAppCommonService
                {
                    // 服务声明
                    decl: IAsyncAppCommonService,
                    // 服务实现
                    impl: IAsyncAppCommonServiceImpl
                },
            ],
        }).start().then((app) => {
            render();

            const common = await sappSDK.service(CommonService);
            // 调用基座应用提供的服务API
            common.message('调用服务');

        }).catch((e) => {
            // 启动失败
        });
    },
    // 微应用的卸载函数
    deBootstrap: () => {
        // 卸载应用
        unmount();
    },
});

Example

自带例子

npm start,基础例子演示

servkit-example

基于servkit的一个完整微应用拆分例子,包括了iframe微应用和微应用(保持了独立的特性,但仍然在一个页面内)。

为什么没有使用 qiankun、single-spa ?

微前端解决的问题:

  1. 架构层面:
    • 巨型应用的拆分手段
    • 拆分后微应用的治理手段:运行机制、生命周期、通信能力等
  2. 工程层面:
    • 微应用的独立性:独立构建、独立发布、独立更新
  3. 组织层面:
    • 形成独立团队,独立运作(工程层面实现后,改点基本达成)

小程序面临的问题:

  1. 独立性:基于SDK API,完全独立运作
  2. 安全性:环境隔离、权限管控
  3. 便利性:二方/三方开发简便

single-spa 在架构层面提供的能力比较基础,在工程层面没有相关涉及。如果要用到实际项目上,还需要做较多的额外内容,比如应用间通信。

qiankun 在 single-spa 之上提供更为完整的技术方案(比如运行沙盒、样式隔离等),在架构层面和工程层面更为成熟,是微前端开源体系中较好的选择。但是不适合做小程序体系,在安全性和独立性上不能适用于三方应用。