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 🙏

© 2025 – Pkg Stats / Ryan Hefner

aoife

v2.0.13

Published

<img src="./document/aoife.svg" style="width:50%; margin:60px 0px 40px 25%;" />

Readme

Aoife 简介

完整文档

使用 jsx 开发 native-js 程序, 每个组件都是一个原始的 HTMLElment,可以和所有原生 js 库很好的兼容使用。

aoife 非常小, gzip 5kb

社区已经有了 React/Vue/Ag 为什么还需要 Aoife?

特性

使用 jsx 开发 native-js 程序, 每个组件都是一个原始的 HTMLElment,可以和所有原生 js 库很好的兼容使用。

  • 核心 API 只有一个: aoife.next
  • 极简的组件声明
  • 每次更新只会更新一次,不会有重复渲染
  • 拥抱原生 JS 生态,可以和原生 JS 库很好的兼容使用

aoife 非常小, gzip 5kb

安装 / 启动

特性

安装

  • 核心 API 只有一个: aoife.next
  • 极简的组件声明
  • 每次更新只会更新一次,不会有重复渲染
  • 拥抱原生 JS 生态,可以和原生 JS 库很好的兼容使用
$ npm init aoife-app <project-name>
$ cd <project-name>
$ yarn install

启动:

$ yarn dev # 开发环境
$ yarn build # 编译

API

aoife 是一个全局函数, 用于 jsx 解析,其中 aoife.next 用于更新元素

declare const aoife: {
  <K extends keyof HTMLElementTagNameMap>(
    tag: K,
    attrs?: PartialDetail<HTMLElementTagNameMap[K]>,
    ...child: any[]
  ): HTMLElementTagNameMap[K];
  next: (
    focusUpdateTargets?: string | HTMLElement | undefined,
    ignoreUpdateTargets?: string | HTMLElement | HTMLElement[]
  ) => void;
  attributeKeys: {
    [key: string]: boolean;
  };
  useMiddleware: (fn: (ele: HTMLElement, props: IProps) => any) => void;
};

很短且完整的教程

如果你会 React,学习 aoife 只需要 5 分钟,注意 aoife 并不是 React 的轮子

aoife 仅仅保留了 JSX 相关的概念,移除了 React 所有非 JSX 相关的概念,所以 aoife 没有生命周期,hooks、diffDOM。

但是 aoife 可以完成所有 React 能完成的项目,为了弥补缺少 React 相关的概念,看看我们是怎么做的:

前端开发可以抽象为两部分:页面绘制、页面更新;在 aoife 中,页面绘制就是使用 jsx 语法组织原始的 HTMLElement;然后使用 函数赋值 来解决元素更新。

函数赋值: 即在声明元素的过程中,给属性绑定一个函数,jsx 解析过程中,若发现属性是一个函数,记录一个发布订阅任务,然后则执行函数,并且赋值;在未来需要更新此属性时,使用 aoife.next 函数对文档进行选择,命中的元素及其子元素会执行之前订阅的任务,更新属性。

我们看一个例子

import "aoife"; // 在项目入口处引入一次,注册全局 dom 对象

// 这是一个普通的 jsx 组件
function App() {
  return (
    <div class="app">
      <h1>Hello World</h1>
      <StatefulExample name="Add Num" />
    </div>
  );
}

// 这是一个用于演示 函数赋值/更新 的组件
function StatefulExample({ name }: { name: string }) {
  console.log(
    "这个日志仅会打印一次,因为 aoife.next 更新仅仅会派发元素的子属性,不会重绘整个组件"
  );
  let num = 0;
  return (
    <div>
      <button
        onclick={() => {
          num += 1;
          aoife.next(".add");
        }}
      >
        {name}
      </button>
      <div
        class="add"
        style={() => ({
          fontSize: 20 + num + "px",
        })}
      >
        <p>{() => num}</p>
      </div>
    </div>
  );
}

document.body.append(<App />);

异步 JSX

aoife 可以异步取值和异步插入 children,这可以简化远程获取数据渲染的业务。 注意,aoife.next 仅仅是一个派发更新,并不会等待所有异步更新的回调

import "aoife";

function App() {
  return (
    <div>
      <input
        placeholder="Input"
        value={() => {
          // 异步取值
          return new Promise((res) => {
            setTimeout(() => res("hello"), 500);
          });
        }}
      />
      {() => {
        // 异步插入元素
        return new Promise((res) => {
          setTimeout(() => {
            res(<div>list-a</div>);
          }, 1000);
        });
      }}
      {() => {
        // 异步插入元素
        return new Promise((res) => {
          setTimeout(() => {
            res(<div>list-b</div>);
          }, 300);
        });
      }}
    </div>
  );
}

设计细节

  1. 为了延续声明式的开发方式,aoife.next 函数并没有传递值,仅仅是派发了更新命令,元素的属性还是由内部状态管理的逻辑来解决状态分支问题
  2. 我们移除了类似 React 中 SCU,purecomponent、memo 等解决重绘问题的概念,因为一次 aoife.next 执行仅仅更新一次局部元素的属性,并不会造成大规模重绘
  3. aoife.next 已经是全局可选则的更新,所以失去了传统的状态管理库的必要;合理规范好 aoife.next 的调用即可。

编写 css

const css = (
  <style>{`
.hello {
  background: #f00;
}
`}</style>
);

document.body.append(css);

生态

aoife 的核心设计理念就是用原生 JS 解决生态问题,任何一个函数,其返回值是一个 HTMLElement,就可以在 aoife 中作为标签进行使用。

原生 JS 和 aoife 混用的例子

vanilla-pop 组件是一个由 tippy.js 封装的函数,内部并无引入 aoife, 使用方法:

// npm i --save vanilla-app
import Pop from "vanilla-pop";

const App = () => {
  return (
    <Pop placement="top">
      <div>label</div>
      <div>pop tip</div>
    </Pop>
  );
};

从这个案例可以看到,一个原生 JS 组件,本身可以不需要包含 aoife,也可以被 aoife 使用;只需要此组件满足 3 个规则:

    1. 组件是一个函数,返回值是一个 HTMLElement 类型
    1. 组件的参数是一个对象
    1. 若 JSX 传递了 children,在组件第一个参数中会包含 children 字段,值是一个 HTMLElement 数组

完整文档

aoife.writeflowy.com