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:{
}
})
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 };
},
};
});
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,
},
});
}
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)`
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;
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);
}
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);
};
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);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- 01
- Pinia中实现监听action的原理11-28
- 03
- patch中的双端比较快速算法09-25