Open dddreee opened 4 years ago
TODO:一个小问题,icestark
内置的 icestark-child
中的路由跟 icestark-layout
的路由并不匹配,child并没有配置路由的 basename
icestark-layout
中的主要代码如下icestark-child
主要路由代码appConfig
中的 icestark.type
为 child
,是否在 createApp
中创建 history
实例时对basename 做了处理?框架应用迁移(这里先用react的项目,其他的可以去官网查看),通过 @ice/stark
的 AppRouter/AppRoute
来管理注册子应用
AppRouter
组件
AppRouter
接收下面几个属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
onRouteChange |
路由变更函数 Function(pathanme, query, hash, type) => void 参数: pathname: {String} 当前路由的pathname query: {Object} 路由的searchParams hash?: {String} 当前路由的hash type?: {'pushState' | 'replaceState' | 'init' | 'popstate'} 当前路由变更的类型 |
Function | |
ErrorComponent | 出错时渲染的组件 | any | ({ err }) => <div>{err}</div> |
LoadingComponent | 加载时渲染的组件 | any | |
NotFoundComponent | 404时渲染的组件 | any | <div>NotFound</div> |
onAppEnter |
进入框架/子应用调用的函数 Function(appConfig) => void 参数: appConfig: {Object} 包含整个app配置信息 |
Function | |
onAppLeave |
离开框架/子应用调用的函数 Function(appConfig) => void 参数: appConfig: {Object} 包含整个app配置信息 |
Function | |
shouldAssetsRemove |
资源是否应该删除? Function(assetUrl, element) => boolean 参数: assetUrl: {String} 资源url element: {HTMLElement | HTMLLinkElement | HTMLStyleElement | HTMLScriptElement} html元素 |
Function | |
basename | 路由的basename | String |
constructor
阶段调用 recordAssets
方法记录 style
link
script
标签,并给这些标签打上 icestark=static
的属性componentDidMount
阶段调用 hijackHistory
和 hijackEventListener
劫持路由调整和事件监听componentWillUnmount
与上面的相反,将劫持的恢复原状render
的时候,遍历 children
,如果有与当前路由匹配的 child element
则返回这个 child element
,如果没有则返回 404AppRoute
组件AppRoute
组件参数
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
exact | 路由精确匹配 | Boolean | false |
strict | 路由后是否有/ | Boolean | false |
sensitive | 区分大小写 | Boolean | false |
sandbox | 沙箱模式 | Boolean | false |
rootId | 对应的元素id | String | icestarkNode |
shouldAssetsRemove | 沙箱模式 | Function() => Boolean | () => true |
AppRoute
的源码
componentDidMount
阶段调用了 renderChild
方法。根据配置是否缓存资源;如果 props
存在 component
或者 render
,那么直接渲染组件或者 render 函数;componentDidUpdate
阶段判断 path, url, title, rootId
,如果有变更就重新调用 renderChild
方法componentWillUnmount
阶段,根据 cache
来决定是否清除缓存,调用 triggerPrevAppLeave
方法触发 AppLeave
,调用 clearCacheRoot
方法renderChild
方法执行加载资源并且渲染子应用loadNextApp
方法 我也不知道是干嘛的render
方法就很简单了,如果有 component
参数并且 showComponent
为 true
,则渲染这个组件;如果有 render
函数并且 showComponent
为 true
, 则渲染 render
方法getMountNode()
动态获取渲染节点,注册应用自身的声明周期,下面用react代码展示
import ReactDOM from 'react-dom';
import { isInIcestark, getMountNode, registerAppEnter, registerAppLeave } from '@ice/stark-app';
import App from './App';
if (isInIcestark()) {
registerAppEnter(() => {
ReactDOM.render(router(), getMountNode());
});
registerAppLeave(() => {
ReactDOM.unmountComponentAtNode(getMountNode());
});
} else {
ReactDOM.render(
2. 定义基准路由:注册子应用时会为每个子应用分配一个 `basename` 比如官方demo的 `/seller`,子应用可以通过 `getBasename()` 获取自身的基准路由
```javascript
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import { getBasename } from '@ice/stark-app';
export default () => {
return (
<Router basename={getBasename()}>
<Switch>
// ...
</Switch>
</Router>
);
};
官方提供了
@ice/stark-data
来支持状态共享和事件监听,@ice/stark-data
包含了store
和event
两个模板,store
存储全局状态,event
负责事件监听响应
store
get(key)
从store获取变量set(key, value)
设置store中的变量on(key, callback, force)
注册变量监听事件, 其中 force
为 boolean
类型,true
则表示初始化注册过程中,会强制执行一次off(key, callback)
删除已经注册的变量监听事件源码 实现方式其实很简单
window
下有个全局变量 ICESTARK
,store
这个状态就是 ICESTARK
的一个属性中,所有全局的状态全部都是挂在 store
下store.get, store.set
就是调用 getCache
和 setCache
两个方法来获取/设置 window.ICESTARK
下 store
的数据on
注册监听事件时,会先判断 storeEmitter
中对应的 key
是否存在,如果不存在就默认赋值为数组,然后每次监听同一个事件的时候,传入的 callback
就会 push
到对应的 key
中emit
响应事件的时候,会从 storeEmitter
中获取对应的 key
的 callback
队列,遍历执行
import { isObject, isArray, warn } from './utils';
import { setCache, getCache } from './cache';
const storeNameSpace = 'store';
interface IO {
set(key: any, value?: any): void;
get(key?: string): void;
}
interface Hooks {
on(key: string, callback: (value: any) => void, force?: boolean): void;
off(key: string, callback?: (value: any) => void): void;
has(key: string): boolean;
}
class Store implements IO, Hooks {
store: object;
storeEmitter: object;
constructor() {
this.store = {};
this.storeEmitter = {};
}
_getValue(key: string) {
return this.store[key];
}
_setValue(key: string, value: any) {
this.store[key] = value;
this._emit(key);
}
_emit(key) {
const keyEmitter = this.storeEmitter[key];
if (!isArray(keyEmitter) || (isArray(keyEmitter) && keyEmitter.length === 0)) {
return;
}
const value = this._getValue(key);
keyEmitter.forEach(cb => {
cb(value);
});
}
get(key?: string) {
if (key === undefined) {
return this.store;
}
if (typeof key !== 'string') {
warn(`store.get: key should be string`);
return null;
}
return this._getValue(key);
}
set(key: any, value?: any) {
if (typeof key !== 'string') {
if (!isObject(key)) {
warn('store.set: key should be string / object');
return;
}
Object.keys(key).forEach(k => {
const v = key[k];
this._setValue(k, v);
});
}
this._setValue(key, value);
}
on(key: string, callback: (value: any) => void, force?: boolean) {
if (typeof key !== 'string') {
warn('store.on: key should be string');
return;
}
if (callback === undefined || typeof callback !== 'function') {
warn('store.on: callback is required, should be function');
return;
}
if (!this.storeEmitter[key]) {
this.storeEmitter[key] = [];
}
this.storeEmitter[key].push(callback);
if (force) {
callback(this._getValue(key));
}
}
off(key: string, callback?: (value: any) => void) {
if (typeof key !== 'string') {
warn('store.off: key should be string');
return;
}
if (!isArray(this.storeEmitter[key])) {
warn(`store.off: ${key} has no callback`);
return;
}
if (callback === undefined) {
this.storeEmitter[key] = undefined;
return;
}
this.storeEmitter[key] = this.storeEmitter[key].filter(cb => cb !== callback);
}
has(key: string) {
const keyEmitter = this.storeEmitter[key];
return isArray(keyEmitter) && keyEmitter.length > 0;
}
}
let store = getCache(storeNameSpace);
if (!store) {
store = new Store();
setCache(storeNameSpace, store);
}
event
on(key, callback)
注册回调函数,与上面 store.on
类似,eventEmitter
中是否存在 key
属性,不存在默认为数组,然后每次注册都将函数推数组中off(key, callback)
删除某个回调函数emit(key, ...rest)
触发已经注册的函数,支持入参源码 实现与 store
大致相同
20200421 开始填坑什么是微前端?
微前端的概念源自微服务,当前的趋势是构建一个功能强大且功能强大的浏览器应用程序(又名单页应用程序),该应用程序位于微服务架构之上。随着时间的流逝,通常由独立团队开发的前端层会不断增长,并且变得越来越难以维护。为了解决这个问题,就有人提出了这个微前端的概念,将一个web应用视为由多个独立功能模块的组合,每个模块独立维护。