Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • React

    • 废弃Create-React-App
    • ice.js中Model数据初始化原理
      • 概述
      • 原理解析
        • createStore
        • provider插件
        • icestore
        • Render部分
  • Vue

  • JavaScript文章

  • 学习笔记

  • openlayers

  • threejs

  • MapboxGL

  • 工具

  • 源码合集

  • ECMAScript历年新特性

  • 前端
  • React
东流
2025-12-10
目录

ice.js中Model数据初始化原理

# 概述

从宏观方面讲,ice.js的官方数据管理是其插件@ice/plugin-store,该插件又是基于react-redux、redux和redux-thunk进行的封装,简化数据(状态)的管理。

@ice/plugin的数据是在其Model层中定义的,数据是存在于内存中的,因此但浏览器页面进行刷新时,内存中的数据会被删除。而数据的持久化依旧离不开浏览器的localStorage。

因此若需要在浏览器刷新前后保持用户的登录状态,可以通过如下伪代码实现:

// app.tsx
const appConfig={
    app:{
        getInitialData(){
            const userInfo=localStorage.getItem('userInfo')
            return {
                initialStates:{
                   userModel:userInfo
                }
            }
        }
    }
}
runApp(appConfig)

// store.ts
const store = createStore({userModel})

// user.ts
const userModel = createModel({
    state:{
      name:'',  
    },
    reducers:{

    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

getInitialData是ice中的内置属性配置,其返回对象中的initialStates对象的键值分别对应调用createStore时的第一个参数对象中的键和它对应的state数据。

本文主要讲述getInitialData初始化Model层数据的原理。

# 原理解析

# createStore

在使用@ice/plugin-store时,肯定会调用createStore创建数据仓库store。而调用createStore会进行其插件的处理。尤其和provider插件相关。

# provider插件

provider插件的核心实现如下:

export default (function (_a) {
    var context = _a.context;
    return {
        onStoreCreated: function (store) {
            var Provider = function (props) {
                var children = props.children, initialStates = props.initialStates;
                if (initialStates) {
                    Object.keys(initialStates).forEach(function (name) {
                        var initialState = initialStates[name];
                        if (initialState && store.dispatch[name][SET_STATE]) {
                            store.dispatch[name][SET_STATE](initialState);
                        }
                    });
                }
                return (
                React.createElement(ReduxProvider, { store: store, context: context }, children));
            };
            return { Provider: Provider, context: context };
        },
    };
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

其本质上就是返回一个匿名函数,该函数返回一个包含onStoreCreated属性方法的对象。onStoreCreated方法接收一个store作为参数,返回一个对象,而该对象包括一个Provider方法和上下文对象context。Provider方法中会判断是否存在初始值initialStates,若存在,则通过store.dispatch更新name对应的Model层的数据,而后返回一个Redux Provider 组件,如此才能在子组件中使用store。

createStore中会将插件收集起来,然后实例化icestore并将插件集合plugins传参,并调用其init方法。

# icestore

icestore实例化过程中会调用一个工厂函数pluginFactory,pluginFactory还会验证插件的三个属性方法(onStoreCreated、onModel和middleware)是否合法。该工厂函数会返回一个create方法,用于绑定插件plugin的属性和方法的到pluginFactory的实例上。

icestore实例在调用init方法时,会调用createRedux返回一个redux store,该方法中包含了redux中复杂的初始化store过程。

后续就是将redux store传递给plugin-provider的onStoreCreated方法,并将其返回值Provider和context绑定到icestore的实例上。

# Render部分

在ice.js项目中系统的入口就是runApp(appConfig),runApp方法是在@ice/core/runApp.ts中实现的,其伪代码如下:

function runApp(appConfig){
   setAppConfig(appConfig);
   loadStaticModules(appConfig);
   initHistory && initHistory(appConfig);

   reactAppRenderer({
    appConfig: appConfig,
    buildConfig,
    ErrorBoundary,
    appLifecycle: {
      createBaseApp: frameworkAppBase,
      initAppLifeCycles,
      emitLifeCycles,
    },
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

重点关注reactAppRenderer方法,该方法是在react-app-renderer库中实现的,reactAppRenderer方法会判断appConfig中是否存在getInitialData方法,若存在,则调用,并将其返回值赋值给context.initialData,同时将其保存在__initialData_变量中,该变量可以通过getInitialData方法获取。并且在reactAppRenderer方法中还调用了参数createBaseApp方法,用于构建应用程序。

const { runtime, appConfig: modifiedAppConfig } = createBaseApp<any>(appConfig, buildConfig, context)`
1

createBaseApp模块中实际就是一个函数,如下:

 var loadRuntimeModules = _a.loadRuntimeModules, createElement = _a.createElement, _b = _a.runtimeAPI, runtimeAPI = _b === void 0 ? {} : _b, _c = _a.runtimeValue, runtimeValue = _c === void 0 ? {} : _c;
    var createBaseApp = function (appConfig, buildConfig, context, staticConfig) {
        // Merge default appConfig to user appConfig
        appConfig = mergeDefaultConfig(DEFAULT_APP_CONFIG, appConfig);
        context.createElement = createElement;
        context.enableRouter = runtimeValue.enableRouter;
        // Load runtime modules
        var runtime = new RuntimeModule(appConfig, buildConfig, context, staticConfig);
        console.log('序号15',runtime,runtimeAPI,runtimeValue)
        Object.keys(runtimeAPI).forEach(function (apiKey) {
            runtime.registerRuntimeAPI(apiKey, runtimeAPI[apiKey]);
        });
        // Assign runtime plugin internal value
        Object.keys(runtimeValue).forEach(function (key) {
            runtime.setRuntimeValue(key, runtimeValue[key]);
        });
        loadRuntimeModules(runtime);
        collectAppLifeCycle(appConfig);
        return {
            runtime: runtime,
            appConfig: appConfig
        };
    };

    return createBaseApp;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

上述伪代码块中和store数据相关的就是loadRuntimeModules方法的调用,loadRuntimeModules方法顾名思义就是加载运行时需要的模块。在前面提到的runApp模块中的frameworkAppBase本质上就是createBaseApp模块调用的返回值,而其参数loadRuntimeModules就是在ice核心库中实现的,其源码如下:

function loadRuntimeModules(runtime){
  // module0:react-app插件
  runtime.loadModule(module0);
  // module1:router插件
  runtime.loadModule(module1);
  // module2:store插件
  runtime.loadModule(module2);
  // module3:auth插件
  runtime.loadModule(module3);
}
1
2
3
4
5
6
7
8
9
10

其中参数runtime本质上就是在createBaseApp中RuntimeModule的一个实例,其loadModule方法定义如下:

 RuntimeModule.prototype.loadModule = function (module) {
        var enableRouter = this.getRuntimeValue('enableRouter');
        var runtimeAPI = {
            addProvider: this.addProvider,
            addDOMRender: this.addDOMRender,
            applyRuntimeAPI: this.applyRuntimeAPI,
            wrapperPageComponent: this.wrapperPageComponent,
            appConfig: this.appConfig,
            buildConfig: this.buildConfig,
            context: this.context,
            setRenderApp: this.setRenderApp,
            staticConfig: this.staticConfig,
            getRuntimeValue: this.getRuntimeValue,
        };
        if (enableRouter) {
            runtimeAPI = __assign(__assign({}, runtimeAPI), { modifyRoutes: this.modifyRoutes, wrapperRouterRender: this.wrapperRouterRender, modifyRoutesComponent: this.modifyRoutesComponent });
        }
        var runtimeModule = module.default || module;
        if (module)
            runtimeModule(runtimeAPI);
    };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

在loadRuntimeModules方法中加载了store的运行时插件,它的实现如下:

// @ts-ignore
import AppStore from '$store';
export default ({ addProvider, appConfig, context: { initialData = {} as any, createElement } }) => {
  const StoreProvider = ({ children }) => {
    const storeConfig = appConfig.store || {};
    let initialStates = {};

    if (initialData.initialStates) {
      initialStates = initialData.initialStates;
    } else if (storeConfig.initialStates) {
      initialStates = storeConfig.initialStates;
    }

    return createElement(AppStore.Provider, {
      initialStates,
      children
    });
  };
  if (AppStore && Object.prototype.hasOwnProperty.call(AppStore, 'Provider')) {
    addProvider(StoreProvider);
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
编辑 (opens new window)
上次更新: 2025/12/15, 10:08:56
废弃Create-React-App
vue-demi介绍

← 废弃Create-React-App vue-demi介绍→

最近更新
01
Pinia中实现监听action的原理
11-28
02
使用pinia-plugin-persistedstate如何清除localStorage中的数据
11-25
03
patch中的双端比较快速算法
09-25
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式