Skip to content

はじめに

PrefXは、リアクティブDOMを構築するための信頼性の高いJavaScriptライブラリです。ReactとPreactにインスパイアされていますが、独自のシグナルベースのアプローチを採用しています。

⚠️ このプロジェクトは現在実験段階です。本番環境での使用は避けてください。⚠️

基本原則

  • ReactライクなJSX構文;
  • リアクティビティの中核としてPreactシグナルを採用;
  • 再レンダリングはシグナルのみによって発生します;
  • 各コンポーネントは一度だけ実行されます;
  • コンポーネントのプロパティは最初の引数として、ユーティリティは2番目の引数として渡されます。インポートする必要はありません;
  • 同期および非同期の関数コンポーネントの両方をサポート;
  • プロパティと属性を区別します(すべてのプロパティは$で始まります);例えば、$valueはプロパティ、valueは属性です。

インストール

推奨される方法は、プロジェクトの名前、言語、CSSソリューションを定義できる対話型ユーティリティ「create-prefx」を使用することです。

bash
npx create-preffx

あるいは、degitを使用することもできます。

bash
npx degit msabitov/vite-preffx preffx-starter 
cd preffx-starter         
npm install        
npm run dev

StackBlitz デモもお試しください。

Examples

  • シンプルなカウンター:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { signal }) => {
    const count = signal(0);
    return <button
        onClick={() => {
            count.value += 1
        }}
    >
        Count is {count}
    </button>
};
  • 要素参照を作成する方法:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { signal }) => {
    const signalRef = signal();
    return <div $ref={signalRef}>
        <div
            $ref={(refVal) => {
                // it will be called after element mounted with refVal = HTMLDivElement
                // and before element destroyed with refVal = null
            }}
            $onMount={(refVal) => {
                // it will be called after element mounted with refVal = HTMLDivElement
            }}
            $onDestroy={() => {
                // it will be called before element destroyed with refVal = HTMLDivElement
            }}
        >
            Refs
        </button>
    </div>
};
  • ライフサイクルフック:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { onMount, onDestroy }) => {
    const { count } = props;

    onMount(() => {
        // some mount logic
    });
    
    onDestroy(() => {
        // some destroy logic
    });
    return <button
        onClick={() => {
            count.value += 1
        }}
    >
        Count is {count}
    </button>
};
  • リスト表示:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { For }) => {
    const items = signal([
        {
            name: 'First'
        },
        {
            name: 'Second'
        }
    ]);

    return <ul>
        <For
            items={items}
            callback={(item) => <li>Item with name: {item.name}</li>}
            fallback={<li>No items</li>}
        />
    </ul>;
};
  • コンテキスト処理:
tsx
import type { PC } from 'preffx';

const contextKey = 'ctx-counter';

const AnotherComponent: PC = (props, { context }) => {
    // read context
    const counter = context[contextKey];
    return <span>
        {counter}
    </span>;
};

export const App: PC = (props, { signal, context }) => {
    const counter = signal(0);
    // modify context
    context[contextKey] = counter;
    return <p>
        {valueFromContext}
        <AnotherComponent />
    </p>;
};
  • 非同期コンポーネント:
tsx
import type { APC } from 'preffx';
import { getData } from './data';
import { AnotherComponent, AnotherAsyncComponent } from './components';

const AsyncComponent: APC<{
    name: string;
}> = async (props, utils) => {
    const data = await getData()
    const componentRoot = await <AnotherAsyncComponent name='nested'/>;
    return <div>
        <AnotherComponent data={data} />
        {componentRoot}
    </div>;
}
  • エラー処理:
tsx
import type { PC } from 'preffx';
import { AnotherComponent } from './components';

export const App: PC = (props, { Catch }) => {
    return <div>
        Sometimes components return errors
        <Catch fallback={<div>Catched!</div>}>
            <AnotherComponent />
        </Catch>
    </div>;
};
  • 繰延値の処理:
tsx
import type { PC } from 'preffx';
import { AsyncComponent } from './components';

export const App: PC = (props, { computed, Defer }) => {
    const def = computed(() => <AsyncComponent id={props.id} />);
    return <Defer
        value={def}
        initial={<div>Please wait</div>}
    />;
};
  • ポータル:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { Portal }) => {
    return <>
        <span>Some text inside current tree</span>
        <Portal root={document.getElementById('portal')}>
            <div>Text inside portal</div>
        </Portal>
    </>;
};
  • 固有識別子:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { id }) => {
    // get unique id
    const inputId = id();
    return <>
        <label>
            Password:
            <input
                type="password"
                aria-describedby={inputId}
            />
        </label>
        <p id={inputId}>
            The password should contain at least 18 characters
        </p>
    </>;
};
  • シンプルなルーティング:
tsx
import type { PC } from 'preffx';

export const App: PC = (props, { computed, url }) => {
    const routeContent = computed(() => {
        // depends on url signal
        switch(url.value.pathname) {
            case '/home':
                return <div>Home page content</div>
            case '/contacts':
                return <div>Contacts page content</div>;
            default:
                return <div>Other page content</div>
        }
    });
    return <div>
        <a href='/home'>Home</a>
        <a href='/contacts'>Contacts</a>
        {routeContent}
    </div>;
};
  • i18nの例:
tsx
import type { PC } from 'preffx';

const dictionary = {
    en: {
        openProfile: 'Open profile',
        showStats: 'Show statistics'
    },
    ru: {
        openProfile: 'Открыть профиль',
        showStats: 'Показать статистику'
    }
};

export const App: PC = (props, { computed, lang }) => {
    const captions = computed(() => {
        // depends on lang signal
        return dictionary[lang.value] || dictionary.en;
    });
    return <div>
        <button>{captions.openProfile}</button>
        <button>{captions.showStats}</button>
    </div>;
};

Apache License 2.0に基づいて公開されています。