尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Fish Redux 页面(Page)核心机制详解:Store 生命周期、initState 与 Middleware 配置

Fish Redux 页面(Page)核心机制详解:Store 生命周期、initState 与 Middleware 配置 前端【免费下载链接】fish-reduxAn assembled flutter application framework.项目地址https://gitcode.com/gh_mirrors/fi/fish-redux点击查看免费下载导读本篇技术指南围绕 Fish Redux 中页面Page这一核心概念展开深入讲解一个页面只有一个 Store的架构约束、Page 对 Component 能力的完整继承、通过 Middleware 实现 Redux AOP 管理以及必须配置的 initState 初始化函数。阅读完成后你将掌握 Page 的完整构造参数、其从构建到销毁的 Store 生命周期原理并能结合实际源码在项目中正确创建、增强和连接页面。Page 是什么组装式 Flutter 应用框架的最小完整单元在 Fish Redux 中Page 是应用中最完整、最独立的功能单元——它是从路由参数到界面渲染、从 State 到 Store 的完整闭环。官方文档 docs/concept/page.md 给出了 Page 的四个核心特征一个页面有且仅有一个 StoreOne and only one store in one pagePage 继承自 Component因此可以配置 Component 的全部要素view、reducer、effect、dependencies、shouldUpdate 等Page 可以配置 Middleware用于对 Redux 进行 AOP 管理Page 必须配置一个初始化函数 initState用于初始化页面数据。对应源码实现位于 lib/src/redux_component/page.dart其中PageT, P是一个抽象类继承自ComponentT并引入了两个类型参数T是页面的 State 类型P是路由参数route-params类型。initState的类型定义清晰表明其职责/// init stores state by route-params typedef InitStateT, P T Function(P params);即initState 负责把路由参数转换为页面的初始 State这就是页面数据初始化的唯一入口。为什么一个页面只有一个 Store从框架架构上看这是 Fish Redux 的刻意设计。看 PageRoutes 的实现/// Each page has a unique store. class PageRoutes implements AbstractRoutes { final MapString, PageObject, dynamic pages; ... override Widget buildPage(String path, dynamic arguments) pages[path]?.buildPage(arguments); }每个注册在路由表中的页面通过buildPage(path, arguments)独立构建各自拥有一套独立的 Store 生命周期。从源码结构可以推断这种页面私有 Store的设计带来两个直接好处页面之间状态隔离页面 A 的 reducer/effect 只作用于页面 A 的 Store不会污染其他页面按需创建与销毁Store 随页面 Widget 的initState创建、随dispose销毁资源生命周期与页面完全对齐。如果需要跨页面共享数据框架并不要求强行合并 Store而是通过connectExtraStore把 AppStore全局 Store与页面 Store 建立单向数据连接见下文页面 Store 与全局 Store 的连接一节。Page 如何继承 Component 的全部能力PageT, P的构造函数把 Component 的要素全部透传给了父类构造器对比 page.dart 与 component.dart 可以看到完整的参数集参数类型说明initStateInitStateT, P必填assert(initState ! null)路由参数到初始 State 的映射函数viewViewBuilderT必填Widget Function(T state, Dispatch dispatch, ViewService viewService)决定如何渲染reducerReducerT可选页面级别的 reducer处理 Action 产生新 StatefilterReducerFilterT可选reducer 前置过滤器主 reducer 复杂时用于提升性能effectEffectT可选页面级别的副作用处理函数dependenciesDependenciesT可选页面内的子组件slots与列表适配器adapter声明shouldUpdateShouldUpdateT可选决定 Store 变化时页面是否重建默认按!identical(old, now)判断wrapperWidgetWrapper可选对页面 Widget 的包裹函数如 KeepAlive、RepaintBoundarymiddlewareListMiddlewareT可选Store 层 AOP对应 Redux 中间件viewMiddlewareListViewMiddlewareT可选View 层 AOPeffectMiddlewareListEffectMiddlewareT可选Effect 层 AOPadapterMiddlewareListAdapterMiddlewareT可选Adapter 层 AOP这里需要特别注意initState和view是两个必填参数源码中assert(initState ! null)与 Component 中的assert(view ! null)共同保证了一个 Page 既要有数据来源、又要有渲染出口。而shouldUpdate未指定时默认使用updateByDefaultT()即(K _, K __) !identical(_, __)只在此较前后 State 不是同一个实例时才重建。最小可运行示例Hello World 页面官方文档给出的示例完整演示了 Page 的最小用法/// Hello World class HelloWordPage extends PageString, String { HelloWordPage(): super( initState: (String msg) msg, view:(String msg, _, __) Text(Hello ${msg}), ); } HelloWordPage().buildPage(world)这里PageString, String中第一个类型参数是 State 类型String第二个是路由参数类型String。initState: (String msg) msg直接把传入的参数world作为初始 Stateview中三个参数依次是 state、dispatch、viewService此处用_和__忽略后两者渲染出Text(Hello world)。最后调用buildPage(world)得到一个可直接挂载到 Widget 树的页面 Widget。这演示了 Page 的三个关键点路由参数通过buildPage(param)传入参数经initState变成 Store 的初始 State页面数据流完全由框架接管无需手动创建 Store。Page 的 Store 生命周期从 createStore 到 teardownPage 的 Store 生命周期全部由框架在 page.dart 的_PageState中托管class _PageStateT, P extends State_PageWidgetT, P { StoreT _store; DispatchBus _pageBus; override void initState() { super.initState(); _store widget.page.createStore(widget.param); _pageBus widget.page.createPageBus(); } override void dispose() { _pageBus.detach(); _store.teardown(); super.dispose(); } }对应的 Store 创建链路在Page.createStore中StoreT createStore(P param) updateStore(createBatchStoreT( _initState(param), createReducer(), storeEnhancer: enhancer.storeEnhance, ));整个生命周期可以拆解为四个阶段创建createStore(param)内部先调用_initState(param)得到初始 State再调用createReducer()生成页面 reducer经enhancer.storeEnhance应用 Store 层中间件后通过createBatchStore包装成支持批量通知的 Store订阅_PageState.build调用page.buildComponent(...)把 Store 与页面级DispatchBus交给 Component 体系页面内的子组件通过store.subscribe注册监听见 component.dart 中_ctx.registerOnDisposed(widget.store.subscribe(...))跨页通信didChangeDependencies阶段_pageBus.attach(widget.page.appBus)把页面私有总线挂到全局共享的 appBus 上实现页面间广播销毁dispose阶段先_pageBus.detach()摘除总线监听再_store.teardown()释放 Store子组件注册的监听也随之解绑。值得展开的是createBatchStore实现在 lib/src/redux_component/batch_store.dart。它通过_BatchNotifymixin 重写了subscribe把多次 dispatch 引起的状态变更合并到一帧内统一通知订阅者void _batch() { if (!isInSuitablePhase()) { // 若处于不合适的调度阶段则推迟到帧后回调再批量通知 ... } else { final T curState getState(); if (!identical(_prevState, curState)) { _prevState curState; ... // 通知所有 listener } } }从源码逻辑可以推断这一机制避免了在一次 Action 处理过程中多次setState导致的重复渲染这是 Page 级 Store 与普通 Redux Store 的重要差异之一。Middleware 与四层 AOP 管理Page 的构造参数中包含四类中间件分别对应 Store、View、Effect、Adapter 四个切面。它们在构造时统一交给EnhancerDefault管理lib/src/redux_component/enhancer.dartPage({ ... ListMiddlewareT middleware, ListViewMiddlewareT viewMiddleware, ListEffectMiddlewareT effectMiddleware, ListAdapterMiddlewareT adapterMiddleware, }) : ... enhancer EnhancerDefaultT( middleware: middleware, viewMiddleware: viewMiddleware, effectMiddleware: effectMiddleware, adapterMiddleware: adapterMiddleware, ), ...EnhancerDefault对四类中间件分别做了归并处理Store 层middleware通过applyMiddlewareT(_middleware)组合成_storeEnhancer作用于createStore时的 store 创建过程对应storeEnhanceView 层viewMiddleware通过mergeViewMiddleware组合在viewEnhance中包裹 view builderEffect 层effectMiddleware通过mergeEffectMiddleware组合在effectEnhance中包裹 effectAdapter 层adapterMiddleware通过mergeAdapterMiddleware组合在adapterEnhance中包裹 adapter builder。从 Enhancer 的抽象定义basic.dart可以看到 AOP 的典型形态。以 Effect AOP 为例typedef EffectMiddlewareT ComposableEffectdynamic Function( AbstractLogicdynamic, StoreT, );即一个接收logic和store、返回效果包裹器的函数。内置的logMiddlewarelib/src/redux_middleware/middleware/log.dart和safetyView、safetyAdapterlib/src/redux_middleware 目录都是基于这一机制实现的现成中间件。运行时动态增删中间件除了构造时传入Enhancer 还提供了两个运行时方法见 page.dartvoid unshift({...}) { enhancer.unshift(...); } // 插入到最前 void append({...}) { enhancer.append(...); } // 追加到末尾这在需要全局统一增强所有页面的场景非常有用。参考示例应用 example/lib/app.dartcreateApp中通过PageRoutes的visitor回调遍历每个注册页面统一追加公共 AOPfinal AbstractRoutes routes PageRoutes( pages: String, PageObject, dynamic{ todo_list: ToDoListPage(), todo_edit: TodoEditPage(), }, visitor: (String path, PageObject, dynamic page) { page.enhancer.append( viewMiddleware: ViewMiddlewaredynamic[safetyViewdynamic()], adapterMiddleware: AdapterMiddlewaredynamic[safetyAdapterdynamic()], effectMiddleware: EffectMiddlewaredynamic[_pageAnalyticsMiddlewaredynamic()], middleware: Middlewaredynamic[ logMiddlewaredynamic(tag: page.runtimeType.toString()), ], ); }, );这是一个非常实用的模式页面私有 AOP 写在 Page 构造参数里应用级公共 AOP 通过 visitor 统一注入。上述代码中_pageAnalyticsMiddleware展示了 Effect AOP 的完整写法——它先判断logic is Page且 Action 类型是Lifecycle打印页面生命周期再透传调用原始 effect。示例中还演示了safetyView/safetyAdapter在生产环境对 View 和 Adapter 构建做异常兜底。initState页面数据初始化的唯一入口initState是 Page 区别于 Component 的最核心新增要素。它有两种典型职责透传路由参数如 Hello World 示例中(String msg) msg根据参数构造复杂 State见 example/lib/todo_list_page/page.dart 的ToDoListPageclass ToDoListPage extends PagePageState, MapString, dynamic { ToDoListPage() : super( initState: initState, effect: buildEffect(), reducer: buildReducer(), view: buildView, dependencies: DependenciesPageState( adapter: const NoneConnPageState() adapter, slots: String, DependentPageState{ report: ReportConnector() ReportComponent() }), // middleware: MiddlewarePageState[ // logMiddleware(tag: ToDoListPage), // ], ); }这个真实示例同时展示了 Page 在工程中的典型完整配置initStateeffectreducerviewdependencies。其中dependencies声明了页面内嵌的 slot 子组件report和列表 adapter二者会以子 reducer 的形式合并进页面的主 reducer见 dependencies.dart 中createReducer对combineSubReducers的使用。对应initState的定义可以在 example/lib/todo_list_page/state.dart 中查看它负责把路由传入的MapString, dynamic如初始待办数据转成PageState。页面 Store 与全局 Store 的连接Page 拥有私有 Store但当多个页面需要共享全局数据如主题色、登录态时Fish Redux 提供了connectExtraStore。仍以 example/lib/app.dart 为例if (page.isTypeofGlobalBaseState()) { page.connectExtraStoreGlobalState(GlobalStore.store, (Object pagestate, GlobalState appState) { final GlobalBaseState p pagestate; if (p.themeColor ! appState.themeColor) { if (pagestate is Cloneable) { final Object copy pagestate.clone(); final GlobalBaseState newState copy; newState.themeColor appState.themeColor; return newState; } } return pagestate; }); }其底层实现在 batch_store.dart 的connectStores中当 AppStore 的 State 变化时订阅回调会把 AppStore 的最新数据映射为 PageStore 的新 State并派发一个内部 Action_UpdateState.Assign替换页面状态// replace current state ReducerT _appendUpdateStateReducerT(ReducerT reducer) (T state, Action action) action.type _UpdateState.Assign ? action.payload : reducer null ? state : reducer(state, action);也就是说跨页共享数据通过AppStore 单向驱动 PageStore实现PageStore 本身仍然保持独立与唯一这与一个页面一个 Store的架构约束并不冲突。在Page.updateStore中通过_storeUpdaters.fold把所有StoreUpdater依次作用于 Store支持一个页面同时连接多个外部 Store。页面间通信DispatchBus 与 appBusPage 还有一个常被忽视但很实用的成员——appBus应用级事件总线见 page.dart/// AppBus is a event-bus used to communicate between pages. final DispatchBus appBus sharedBus;sharedBus是全局共享的DispatchBusDefault实例。每个页面在_PageState.didChangeDependencies中把自己的_pageBusattach 到appBus上形成一棵页面总线 - 全局总线的监听树。当某个页面通过context.broadcast(action)广播 Action 时消息会沿总线传递给所有已 attach 的页面见 dispatch_bus.dart 的broadcast实现。页面销毁时detach摘除注册避免悬挂引用。小结与最佳实践围绕 docs/concept/page.md 的核心内容结合源码与示例可以总结出以下实践要点一个页面一个 Store页面状态天然隔离Store 随 Widget 创建/销毁无需手动管理见 page.dart 的_PageStateinitState 必填它是路由参数到页面初始 State 的唯一转换入口与view同为必填项复用 Component 全要素reducer、effect、dependencies、shouldUpdate、wrapper 等与 Component 完全一致需要子组件时在dependencies.slots声明四层 AOP 按需配置构造参数配置页面私有中间件enhancer.append/unshift供运行时动态增强全局公共 AOP 建议通过PageRoutes的visitor统一注入参考 example/lib/app.dart跨页共享数据用connectExtraStore保持一页一 Store的同时让全局数据单向驱动页面数据而不是合并 Store。配套的可运行示例位于 example/lib包含 todo_list、todo_edit 两个页面及全局 Store 的完整演示页面相关的测试可参考 test/lib/redux_component/page_test.dart 与 test_widgets/lib/page它们验证了 Page 从构建、生命周期到路由解析的完整行为适合作为深入理解 Page 机制的下一步阅读材料。赞分享前端【免费下载链接】fish-reduxAn assembled flutter application framework.项目地址https://gitcode.com/gh_mirrors/fi/fish-redux点击查看免费下载相关推荐Fish Redux 的 Page 概念详解一个页面一个 Store、initState 初始化与 Middleware AOP 配置Fish Redux 的 Page 概念详解一个页面一个 Store、initState 初始化与 Middleware AOP 配置 Page 是 Fish前端WordPress「Time to Read」块深度解析阅读时长与字数统计的完整实现指南WordPress「Time to Read」块深度解析阅读时长与字数统计的完整实现指南 导读 本文基于 GutenbergWordPress 块编辑器仓前端fish-redux 中的 Redux 概念详解State、Action、Reducer、Store 与 Middleware 的完整实践指南fish redux 中的 Redux 概念详解State、Action、Reducer、Store 与 Middleware 的完整实践指南 fish re前端上一篇5分钟终极指南用n工具彻底解决Node.js版本冲突与npm scripts无缝协作下一篇react-developer-roadmap学习路线React组件库开发实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表