Skip to content

xams-creator/dusk

Repository files navigation

Dusk

Lightweight frontend framework based on

Features

  • Typescript supports
  • App lifecycle
  • Plugin
  • Simplified redux model
  • HMR (craco、vite)

Installation

npm i @xams-framework/dusk

Usage

import { createApp, createDuskModel } from '@xams-framework/dusk';

const app = createApp({
    container: '#root',
});

const model = createDuskModel({
    namespace: 'app',
    initialState: {
        value: 1,
    },
    reducers: {
        add(state) {
            state.value += 1;
        },
    },
    effects: {
        setValue(dispatch, state, action, { set }) {
            set((state) => {
                state.value = action.payload
            })
            return {
                code: 0,
                success: true
                msg: 'ok'
            }
        }
    }
});
app.define(model);

app.startup(<div>hello world!</div>);

console.log(app.state);  // {app: {value : 1}}
app.$store.dispatch({ type: 'app/add' });
console.log(app.state);  // {app: {value : 2}}

app.$store.dispatch(model.actions.add());
console.log(app.state);  // {app: {value : 3}}

const promise = app.$store.dispatch(model.commands.setValue(999));
console.log(app.state);  // {app: {value : 999}}
promise.then((res) => {
    alert(res.msg)  // ok
})
import React from 'react';

import { createApp } from '@xams-framework/dusk';

import createDuskAppLifecycle from '@/configuration/plugins/dusk-plugin-app-lifecycle';
import createDuskAppReady from '@/configuration/plugins/dusk-plugin-app-ready';
import createDuskCustomHooks from '@/configuration/plugins/dusk-plugin-custom-hooks';

const app = createApp({
    container: '#root',
});

app.use(createDuskAppLifecycle())
    .use(createDuskAppReady())
    .use(createDuskCustomHooks())
    .startup(<div>按F12请打开控制台</div>);

window.app = app;

示例图片

npm i -D @xams-framework/craco-plugin-dusk-hmr
npm i @xams-framework/dusk-plugin-hmr
  • craco.config.ts

import { CracoConfig } from '@craco/types';
import createCracoDuskHmr from '@xams-framework/craco-plugin-dusk-hmr';

function defineCraco(options: CracoConfig): CracoConfig {
    return options;
}

export default defineCraco({
    plugins: [createCracoDuskHmr()],
    webpack: {
        alias: {
            '@': 'src',
        },
    },
});
  • index.tsx

import { createApp } from '@xams-framework/dusk';
import createDuskHmr from '@xams-framework/dusk-plugin-hmr';

const app = createApp({
    container: '#root',
});

app.use(createDuskHmr()).startup();
npm i -D @xams-framework/vite-plugin-dusk
npm i @xams-framework/dusk-plugin-hmr
  • vite.config.ts

import react from '@vitejs/plugin-react';
import createViteDusk from '@xams-framework/vite-plugin-dusk';
import * as path from 'path';
import postcss from 'postcss-preset-env';
import { defineConfig } from 'vite';
import eslint from 'vite-plugin-eslint';

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
    // const env = loadEnv(mode, process.cwd(), '');
    return {
        server: {
            port: 1339,
        },
        css: {
            postcss: {
                plugins: [postcss()],
            },
        },
        resolve: {
            alias: {
                '@': path.join(__dirname, 'src'),
                src: path.join(__dirname, 'src'),
            },
        },
        plugins: [react(), createViteDusk(), eslint()],
    };
});
  • index.tsx

import { createApp } from '@xams-framework/dusk';
import createDuskHmr from '@xams-framework/dusk-plugin-hmr';

const app = createApp({
    container: '#root',
});

app.use(createDuskHmr()).startup();

Apis

  • createApp

import Dusk, { createApp } from '@xams-framework/dusk';

const app = createApp({
    container: '#root', // 挂载位置
    redux: {
        // redux相关配置
        logger: {
            collapsed: true,
        },
    },
});

app.define(model) // 定义一个模型,可以使用 dusk-plugin-context-webpack,vite 跳过手动定义
    .use(createPlugin()) // 使用一个插件
    .startup(<App />); // 启动 App
  • createDuskModel

import { Action, DuskEffectPayloadAction, DuskModel, DuskPayloadAction, createDuskModel } from '@xams-framework/dusk';
import { AnyAction } from 'redux';

import { passwordLogin } from '@/common/api';

export interface AppState {
    value: number;
    pending: boolean;
}

const model = createDuskModel({
    namespace: 'app', // 唯一命名空间
    initialState: {
        // 支持函数语法 (namespace)=> initialState,也可以在函数体中根据namespace读本地存储的历史状态
        value: 111,
        pending: false,
    } as AppState,
    reducers: {
        // 更新 store 数据的 reducers,使用了 immer,因此可以直接修改值。创建完成时会同时创建 model.actions 用来dispatch事件
        add(state, action) {
            state.value += 1;
        },
        startLoading(state, action) {
            state.pending = true;
        },
        stopLoading(state, action) {
            state.pending = false;
        },
    },
    effects: {
        // 异步函数,一般用来发 http 请求。创建完成时会同时创建 model.commands 用来dispatch事件
        // state: 当前空间的 state 而不是全局state
        // payload: 当前action调用时传的值
        // set: 看内部示例,可以在这里直接更新state
        // sleep: 配合 await 达到延时处理
        // app: 全局app示例,可以配合插件做一些事情
        // model: 当前model对象
        async add(dispatch, state, { payload }, { set, sleep, app, model }) {
            // const res = await passwordLogin();

            // set((state) => {
            //     state.pending = false
            // })

            // await sleep(1000);

            return 999;
        },
        async foo(dispatch, state, action) {
            const a = dispatch(add(1));
            const b = dispatch(model.commands.foo());
            const c = dispatch({
                type: 'add',
                payload: 1,
            });
            const e = dispatch({
                type: 'app/add',
                payload: 1,
                namespace: 'app',
                name: 'add',
                effect: false,
                scoped: true,
                effect123: 1,
            });
        },
    },
    // 模型被初始化时执行的事件
    onInitialization(state, model, app) {
        // 可以使用内置的 hotkeys 做快捷键绑定,需要手动在model被移除时清除,否则容易引发内存问题
        app.$hotkeys('ctrl+a', () => {
            // 把当前函数放在下一批微任务中执行,此时可以拿到完整的 models
            app.$scheduler(() => {
                console.log('hello micro task', app.models);
            });
        });
    },
    // 模型移除时执行
    onFinalize(state, model: DuskModel<AppState>, app) {
        app.$hotkeys.unbind('ctrl+a');
    },

    // 当前空间状态被修改后执行,可以在这里把新状态保存到本地存储
    onStateChange() {},
});

export const { stopLoading } = model.actions;
export const { add, foo } = model.commands;
export default model;
import { useDispatch } from 'react-redux';

import model from './app.model';

const dispatch = useDispatch();
const res = await dispatch(model.commands.add()); // 如果是 commands ,那么返回的类型是 Promise
dispatch(model.actions.add()); // 如果是 actions , 那么则是正常返回
  • definePlugin

    • 定义一个插件
import { definePlugin } from '@xams-framework/dusk';

export default function createDuskAppPlugin(options: any) {
    return definePlugin({
        name: 'dusk-plugin-app-hello',
        setup(app) {
            console.log('插件会在ready前调用');
        },
        onReady({ app }, next) {
            next();
        },
        //...其他生命周期,参考 dusk-examples 中的几个用例
    });
}
import createDuskAppPlugin from './createDuskAppPlugin';

app.use(createDuskAppPlugin());

Hooks

  • useDusk

    • 函数组件中使用,返回当前app实例
import { useDusk } from '@xams-framework/dusk';

const app = useDusk();
  • useNamespacedSelector

    • 函数组件中使用,类似于 useSelector,提供一个 namespace 用来返回当前命名空间的state
import { useNamespacedSelector } from '@xams-framework/dusk';

const state = useNamespacedSelector<any>('app'); // model.namespace

Plugins

  • dusk-plugin-loading

    • 全局loading插件,dispatch effects 函数时会触发
npm i @xams-framework/dusk-plugin-loading
import createDuskLoading from './index';

app.use(createDuskLoading);
const [loading] = useLoading();
// 触发 loading start
app.$store.dispatch(model.commands.add());
// 触发 loading stop
  • dusk-plugin-context-webpack

    • 使用此组件将会自动注册 src/business/inject 下定义的model,不需要手动define
    • webpack(craco)中可以用用 dusk-plugin-context-webpack
    • vite可以用 dusk-plugin-context-vite
npm i @xams-framework/dusk-plugin-context-webpack
import createDuskContextWebpack from './index';

app.use(createDuskContextWebpack());