上海推广网站公司,网站开发 验收,英文外贸网站模板,排版设计英文ReactPortals传送门
React Portals提供了一种将子节点渲染到父组件以外的DOM节点的解决方案#xff0c;即允许将JSX作为children渲染至DOM的不同部分#xff0c;最常见用例是子组件需要从视觉上脱离父容器#xff0c;例如对话框、浮动工具栏、提示信息等。
描述
div即允许将JSX作为children渲染至DOM的不同部分最常见用例是子组件需要从视觉上脱离父容器例如对话框、浮动工具栏、提示信息等。
描述
divSomeComponent /{createPortal(children, domNode, key?)}
/divReact Portals可以翻译为传送门从字面意思上就可以理解为我们可以通过这个方法将我们的React组件传送到任意指定的位置可以将组件的输出渲染到DOM树中的任意位置而不仅仅是组件所在的DOM层级内。举个简单的例子假设我们ReactDOM.render挂载组件的DOM结构是div idroot/div那么对于同一个组件我们是否使用Portal在整个DOM节点上得到的效果是不同的:
export const App: FC () {return (React.Fragmentdiv123/divdiv classNamemodeldiv456/div/div/React.Fragment);
};// - bodydiv idrootdiv123/divdiv classmodeldiv456/div/div/div
/bodyexport const App: FC () {return (React.Fragmentdiv123/div{ReactDOM.createPortal(div classNamemodeldiv456/div/div,document.body)}/React.Fragment);
};// - bodydiv idrootdiv123/div/div{/* DOM结构挂载到了body下 */}div classmodeldiv456/div/div
/body从上边的例子中可以看到我们通过ReactDOM.createPortal将React组件挂载到了其他的DOM结构下在这里是挂载到了document.body下当然这这也是最常见的做法这样我们就可以通过Portal将组件传送到目标渲染的位置由此来更灵活地控制渲染的行为并解决一些复杂的UI交互场景通常我们可以封装Portal组件来更方便地调用。
export const Portal: React.FC ({ children }) {return typeof document object ? ReactDOM.createPortal(children, document.body) : null;
};export const App: FC () (PortalSomeComponent //Portal
);之前我们也聊到了使用Portals最常见的场景就是对话框或者可以认为是浮动在整个页面顶部的组件这样的组件在DOM结构上是脱离了父组件的我们当然可以自行实现相关的能力例如主动创建一个div结构挂载到目标DOM结构下例如document.body下然后利用ReactDOM.render将组建渲染到相关结构中在组件卸载时再将创建的div移除这个方案当然是可行的但是并没有那么优雅。当然还有一个方法是使用状态管理在目标组件中事先定义好相关的组件通过状态管理例如redux来控制显隐这种就是纯粹的高射炮打蚊子就没有必要再展开了。
其实我们再想一想既然我们是要脱离父组件结构来实现这个能力那么我们没有必要非得使用PortalsCSS的position定位不是也可以帮助我们将当前的DOM结构脱离文档流也就是说我们没必要将目标组件的DOM结构实际地分离出来只需要借助position定位就可以实现效果。当然想法是很美好的真实场景就变得复杂的多了那么脱离文档流最常用的主要是绝对定位absolute与固定定位fixed。首先我们来看一下absolute那么我们使用absolute其实很容易想到我们需要从当前组件一直到body都没有其他position是relative/absolute的元素这个条件肯定是很难达到的特别是如果我们写的是一个组件库的话很难控制用户究竟套了多少层以及究竟用什么CSS属性。那么此时我们再将目光聚焦到fixed上fixed是相对于视口来定位的那么也就不需要像是absolute那么强的要求了即使是父元素存在relative/absolute也没有关系。当然这件事没有这么简单即使是fixed元素依旧可能会受到父元素样式的影响在这里举两个例子分别是transform与z-index。
!-- 不断改变transform: translateY(20px);的值 fixed的元素也在不断随之变化 --
div styletransform: translateY(20px);div styleposition: fixed; left: 10px; top: 10px;div stylebackground-color: blue; width: 10px; height: 10px;/div/div
/div!-- 父级元素的z-index的层次比同级元素低 即使fixed元素z-index比父级高 也会被父级同级元素遮挡 --
divstyleposition: absolute; z-index: 100; width: 100px; height: 100px; background-color: #fff;
/div
div styleposition: absolute; z-index: 1div styleposition: fixed; left: 10px; top: 10px; z-index: 1000div stylebackground-color: blue; width: 10px; height: 10px/div/div
/div从上边的例子中我们可以看出我们仅仅使用CSS的position定位是无法做到完全脱离父组件的即使我们能够达到脱离文档流的效果也会因为父组件的样式而受到影响特别是在组件库中我们作为第三方组件库的话是完全没有办法控制用户设计的DOM结构的如果仅仅采用脱离文档流的方法而不实际将DOM结构分离出来的话那么我们的组件就会受到用户样式的影响这是我们不希望看到的。此外即使我们并不是设计组件库而仅仅是在我们的业务中实现相关需求我们也不希望我们的组件受到父组件的影响因为即使最开始我们的结构和样式没出现问题随着业务越来越复杂特别是多人协作开发项目就很容易留下隐患造成一些不必要的问题当然我们可以引入E2E来避免相关问题这就是另一方面的解决方案了。
综上React Portals提供了一种更灵活地控制渲染的行为可以用于解决一些复杂的UI交互场景下面是一些常见的应用场景:
模态框和对话框: 使用Portals可以将模态框或对话框组件渲染到DOM树的顶层确保其可以覆盖其他组件并且在层级上独立于其他组件这样可以避免CSS或z-index属性的复杂性并且在组件层级之外创建一个干净的容器。与第三方库的集成: 有时候我们可能需要将React组件与第三方库(例如地图库或视频播放器)集成使用Portals可以将组件渲染到第三方库所需的DOM元素中即将业务需要的额外组件渲染到原组件封装好的DOM结构中以确保组件在正确的位置和上下文中运行。逻辑分离和组件复用: Portals允许我们将组件的渲染输出与组件的逻辑分离我们可以将组件的渲染输出定义在一个单独的Portal组件中并在需要的地方使用该Portal这样可以实现组件的复用并且可以更好地组织和管理代码。处理层叠上下文: 在某些情况下使用Portals可以帮助我们解决层叠上下文stacking context的问题由于Portals可以创建独立的DOM渲染容器因此可以避免由于层叠上下文导致的样式和布局问题。
MouseEnter事件
即使React Portals可以将组件传送到任意的DOM节点中但是其行为和普通的React组件一样其并不会脱离原本的React组件树这其实是一件非常有意思的事情因为这样会看起来我们可以利用这个特性来实现比较复杂的交互。但是在这之前我们来重新看一下MouseEnter与MouseLeave以及对应的MouseOver与MouseOut的原生DOM事件。
MouseEnter: 当鼠标光标进入一个元素时触发该事件仅在鼠标从元素的外部进入时触发不会对元素内部的子元素产生影响。例如如果有一个嵌套的DOM结构div idadiv idb/div/div此时我们在元素a上绑定了MouseEnter事件当鼠标从该元素外部移动到内部时MouseEnter事件将被触发而当我们再将鼠标移动到b元素时不会再次触发MouseEnter事件。MouseLeave当鼠标光标离开一个元素时触发该事件仅在鼠标从元素内部离开时触发不会对元素外部的父元素产生影响。例如如果有一个嵌套的DOM结构div idadiv idb/div/div此时我们在元素a上绑定了MouseEnter事件当鼠标从该元素内部移动到外部时MouseLeave事件将被触发而如果此时我们的鼠标是从b元素移出到a元素内不会触发MouseEnter事件。MouseOver: 当鼠标光标进入一个元素时触发该事件在鼠标从元素的外部进入时触发并且会冒泡到父元素。例如如果有一个嵌套的DOM结构div idadiv idb/div/div此时我们在元素a上绑定了MouseOver事件当鼠标从该元素外部移动到内部时MouseOver事件将被触发而当我们再将鼠标移动到b元素时由于冒泡会再次触发绑定在a元素上的MouseOver事件再从b元素移出到a元素时会再次触发MouseOver事件。MouseOut: 当鼠标光标离开一个元素时触发该事件在鼠标从元素内部离开时触发并且会冒泡到父元素。例如如果有一个嵌套的DOM结构div idadiv idb/div/div此时我们在元素a上绑定了MouseOut事件当鼠标从该元素内部移动到外部时MouseOut事件将被触发而如果此时我们的鼠标是从b元素移出到a元素内由于冒泡会同样触发绑定在MouseOut事件再从a元素移出到外部时同样会再次触发MouseOut事件。
需要注意的是MouseEnter/MouseLeave是在捕获阶段执行事件处理函数的而不能在冒泡阶段过程中进行而MouseOver/MouseOut是可以在捕获阶段和冒泡阶段选择一个阶段来执行事件处理函数的这个就看在addEventListener如何处理了。实际上两种事件流都是可以阻断的只不过MouseEnter/MouseLeave需要在捕获阶段来stopPropagation一般情况下是不需要这么做的。我个人还是比较推荐使用MouseEnter/MouseLeave主要有这么几点理由:
避免冒泡问题: MouseEnter和MouseLeave事件不会冒泡到父元素或其他元素只在鼠标进入或离开元素本身时触发这意味着我们可以更精确地控制事件的触发范围更准确地处理鼠标交互而不会受到其他元素的干扰提供更好的用户体验。避免重复触发: MouseOver和MouseOut事件在鼠标悬停在元素内部时会重复触发当鼠标从一个元素移动到其子元素时MouseOut事件会在父元素触发一次然后在子元素触发一次MouseOut事件也是同样会多次触发可以将父元素与所有子元素都看作独立区域而事件会冒泡到父元素来执行事件绑定函数这可能导致重复的事件处理和不必要的逻辑触发而MouseEnter和MouseLeave事件不会重复触发只在鼠标进入或离开元素时触发一次。简化交互逻辑: MouseEnter和MouseLeave事件的特性使得处理鼠标移入和移出的交互逻辑变得更直观和简化我们可以仅关注元素本身的进入和离开而不需要处理父元素或子元素的事件这种简化有助于提高代码的可读性和可维护性。
当然究竟使用MouseEnter/MouseLeave还是MouseEnter/MouseLeave事件还是要看具体的业务场景如果需要处理鼠标移入和移出元素的子元素时或者需要利用冒泡机制来实现功能那么MouseOver和MouseOut事件就是更好的选择MouseEnter/MouseLeave能提供更大的灵活性和控制力让我们能够创建复杂的交互效果并更好地处理用户与元素的交互当然应用的复杂性也会相应提高。
让我们回到MouseEnter/MouseLeave事件本身上在这里https://codesandbox.io/p/sandbox/trigger-component-1hv99o?file/src/components/mouse-enter-test.tsx:1,1提供了一个事件的DEMO可以用来测试事件效果。需要注意的是在这里我们是借助于React的合成事件来测试的而在测试的时候也可以比较明显地发现MouseEnter/MouseLeave的TS提示是没有Capture这个选项的例如Click事件是有onClick与onClickCapture来表示冒泡和捕获阶段事件绑定的而即使是在React合成事件中MouseEnter/MouseLeave也只会在捕获阶段执行所以没有Capture事件绑定属性。
--------------------------
| c | b | a |
| | | |
|------- | |
| | |
|---------------- |
| |
--------------------------我们分别在三个DOM上都绑定了MouseEnter事件当我们鼠标移动到a上时会执行a元素绑定的事件当依次将鼠标移动到a、b、c的时候同样会以此执行a、b、c的事件绑定函数并且不会因为冒泡事件导致父元素事件的触发当我们鼠标直接移动到c的时候可以看到依旧是按照a、b、c的顺序执行也可以看出来MouseEnter事件是依赖于捕获阶段执行的。
Portal事件
在前边也提到了尽管React Portals可以被放置在DOM树中的任何地方但在任何其他方面其行为和普通的React子节点行为一致。我们都知道React自行维护了一套基于事件代理的合成事件那么由于Portal仍存在于原本的React组件树中这样就意味着我们的React事件实际上还是遵循原本的合成事件规则而与DOM树中的位置无关那么我们就可以认为其无论其子节点是否是Portal像合成事件、Context这样的功能特性都是不变的下面是一些使用React Portals需要关注的点:
事件冒泡会正常工作: 合成事件将通过冒泡传播到React树的祖先事件冒泡将按预期工作而与DOM中的Portal节点位置无关。React以控制Portal节点及其生命周期: Portal未脱离React组件树当通过Portal渲染子组件时React仍然可以控制组件的生命周期。Portal只影响DOM结构: 对于React来说Portal仅仅是视觉上渲染的位置变了只会影响HTML的DOM结构而不会影响React组件树。预定义的HTML挂载点: 使用React Portal时我们需要提前定义一个HTML DOM元素作为Portal组件的挂载。
在这里https://codesandbox.io/p/sandbox/trigger-component-1hv99o?file/src/components/portal-test.tsx:1,1提供了一个Portals与MouseEnter事件的DEMO可以用来测试效果。那么在代码中实现的嵌套精简如下:
-------------------
| a |
| ------|------ --------
| | | b | | c |
| | | | | |
| | | | --------
| ------|------
-------------------const C ReactDOM.createPortal(div onMouseEnter{e console.log(c, e)}/div, document.body);
const B ReactDOM.createPortal(React.Fragmentdiv onMouseEnter{e console.log(b, e)}{C}/div/React.Fragment,document.body
);
const App (React.Fragmentdiv onMouseEnter{e console.log(a, e)}/div{B}/React.Fragment
);// const App (React.Fragmentdiv onMouseEnter{e console.log(a, e)}/div{ReactDOM.createPortal(React.Fragmentdiv onMouseEnter{e console.log(b, e)}{ReactDOM.createPortal(div onMouseEnter{e console.log(c, e)}/div,document.body)}/div/React.Fragment,document.body)}/React.Fragment
);单纯从代码上来看这就是一个很简单的嵌套结构而因为传送门Portals的存在在真实的DOM结构上这段代码结构表现的效果是这样的其中id只是用来标识React的DOM结构实际并不存在:
bodydiv idrootdiv ida/div/divdiv idb/divdiv idc/divdiv
/body接下来我们依次来试试定义的MouseEnter事件触发情况首先鼠标移动到a元素上控制台打印a符合预期接下来鼠标移动到b元素上控制台打印b同样符合预期那么接下来将鼠标移动到c神奇的事情来了我们会发现会先打印b再打印c而不是仅仅打印了c由此我们可以得到虽然看起来DOM结构不一样了但是在React树中合成事件依然保持着嵌套结构C组件作为B组件的子元素在事件捕获时依然会从B - C触发MouseEnter事件基于此我们可以实现非常有意思的一件事情多级嵌套的弹出层。
Trigger弹出层
实际上上边聊的内容都是都是为这部分内容做铺垫的因为工作的关系我使用ArcoDesign是非常多的又由于我实际是做富文本文档的需要弹出层来做交互的地方就非常多所以在平时的工作中会大量使用ArcoDesign的Trigger组件https://arco.design/react/components/trigger之前我一直非常好奇这个组件的实现这个组件可以无限层级地嵌套而且当多级弹出层组件的最后一级鼠标移出之后所有的弹出层都会被关闭最主要的是我们只是将其嵌套做了一层业务实现并没有做任何的通信传递所以我也一直好奇这部分的实现直到前一段时间我为了解决BUG深入研究了一下相关实现发现其本质还是利用React Portals以及React树的合成事件来完成的这其中还是有很多交互实现可以好好学习下的。
同样的在这里也完成了一个DEMO实现https://codesandbox.io/p/sandbox/trigger-component-1hv99o?file/src/components/trigger-simple.tsx:1,1而在调用时则直接嵌套即可实现两层弹出层当我们鼠标移动到a元素时b元素与c元素会展示出来当我们将鼠标移动到c元素时d元素会被展示出来当我们继续将鼠标快速移动到d元素时所有的弹出层都不会消失当我们直接将鼠标从d元素移动到空白区域时所有的弹出层都会消失如果我们将其移动到b元素那么只有d元素会消失。
------------------- ------------- --------
| a | | b | | d |
| | |-------- | | |
| | | c | | --------
| | |-------- |
| | -------------
| |
-------------------TriggerSimpleduration{200}popup{() (div idb style{{ height: 100, width: 100, backgroundColor: green }}TriggerSimplepopup{() div idd style{{ height: 50, width: 50, backgroundColor: blue }}/div}duration{200}div idc style{{ paddingTop: 20 }}Hover/div/TriggerSimple/div)}
div ida style{{ height: 150, width: 150, backgroundColor: red }}/div
/TriggerSimple让我们来拆解一下代码实现首先是Portal组件的封装在这里我们就认为我们将要挂载的组件是在document.body上的就可以了因为我们要做的是弹出层在最开始的时候也阐明了我们的弹出层DOM结构需要挂在最外层而不能直接嵌套地放在DOM结构中当然如果能够保证不会出现相关问题滚动容器不是body的情况且需要position absolute的情况下可以通过getContainer传入DOM节点来制定传送的位置当然在这里我们认为是body就可以了。在下面这段实现中我们就通过封装Portal组件来调度DOM节点的挂载和卸载并且实际的组件也会被挂载到我们刚创建的节点上。
// trigger-simple.tsx
getContainer () {const popupContainer document.createElement(div);popupContainer.style.width 100%;popupContainer.style.position absolute;popupContainer.style.top 0;popupContainer.style.left 0;this.popupContainer popupContainer;this.appendToContainer(popupContainer);return popupContainer;
};// portal.tsx
const Portal (props: PortalProps) {const { getContainer, children } props;const containerRef useRefHTMLElement | null(null);const isFirstRender useIsFirstRender();if (isFirstRender || containerRef.current null) {containerRef.current getContainer();}useEffect(() {return () {const container containerRef.current;if (container container.parentNode) {container.parentNode.removeChild(container);containerRef.current null;}};}, []);return containerRef.current? ReactDOM.createPortal(children, containerRef.current): null;
};接下来我们来看构造在React树中的DOM结构这块可以说是整个实现的精髓可能会比较绕可以认为实际上每个弹出层都分为了两块一个是原本的child另一个是弹出的portal这两个结构是平行的放在React DOM树中的那么在多级弹出层之后实际上每个子trigger(portal child)都是上层portal的children这个结构可以用一个树形结构来表示。
React.Fragment{childrenComponent}{portal}
/React.FragmentROOT/ \A(portal) A(child)/ \B(portal) B(child)/ \C(portal) C(child)/ \
..... ..... bodydiv idroot!-- ... --div idA-child/div!-- ... --/divdiv idA-portaldiv idB-child/div/divdiv idB-portaldiv idC-child/div/divdiv idC-portal!-- ... --/div
/body从树形结构中我们可以看出来虽然在DOM结构中我们现实出来是平铺的结构但是在React的事件树中却依旧保持着嵌套结构那么我们就很容易解答最开始的一个问题为什么我们可以无限层级地嵌套而且当多级弹出层组件的最后一级鼠标移出之后所有的弹出层都会被关闭就是因为实际上即使我们的鼠标在最后一级但是在React树结构中其依旧是属于所有portal的子元素既然其是child那么实际上我们可以认为其并没有移出各级trigger的元素自然不会触发MouseLeave事件来关闭弹出层如果我们移出了最后一级弹出层到空白区域那么相当于我们移出了所有trigger实例的portal元素区域自然会触发所有绑定的MouseLeave事件来关闭弹出层。
那么虽然上边我们虽然解释了Trigger组件为什么能够维持无限嵌套层级结构下能够维持弹出层的显示并且在最后一级鼠标移出之后能够关闭所有弹出层或者从最后一级返回到上一级只关闭最后一级弹出层但是我们还有一个问题没有想明白上边的问题是因为所有的trigger弹出层实例都是上一级trigger弹出层实例的子元素那么我们还有一个平级的portal与child元素呢当我们鼠标移动到child时portal元素会展示出来而此时我们将鼠标移动到portal元素时这个portal元素并不会消失而是会一直保持显示在这里的React树是不存在嵌套结构的所以这里需要对事件进行特殊处理。
onMouseEnter (e: React.SyntheticEventHTMLDivElement, MouseEvent) {console.log(onMouseEnter, this.childrenDom);const mouseEnterDelay this.props.duration;this.clearDelayTimer();his.setPopupVisible(true, mouseEnterDelay || 0);
};onMouseLeave (e: React.SyntheticEventHTMLDivElement, MouseEvent) {console.log(onMouseLeave, this.childrenDom);const mouseLeaveDelay this.props.duration;this.clearDelayTimer();if (this.state.popupVisible) {this.setPopupVisible(false, mouseLeaveDelay || 0);}
};onPopupMouseEnter () {console.log(onPopupMouseEnter, this.childrenDom);this.clearDelayTimer();
};onPopupMouseLeave (e: React.SyntheticEventHTMLDivElement, MouseEvent) {console.log(onPopupMouseLeave, this.childrenDom);const mouseLeaveDelay this.props.duration;this.clearDelayTimer();if (this.state.popupVisible) {this.setPopupVisible(false, mouseLeaveDelay || 0);}
};setPopupVisible (visible: boolean, delay 0, callback?: () void) {onst currentVisible this.state.popupVisible;if (visible ! currentVisible) {this.delayToDo(delay, () {if (visible) {this.setState({ popupVisible: true }, () {this.showPopup(callback);});} else {this.setState({ popupVisible: false }, () {callback callback();});}});} else {callback callback();}
};delayToDo (delay: number, callback: () void) {if (delay) {this.clearDelayTimer();this.delayTimer setTimeout(() {callback();this.clearDelayTimer();}, delay);} else {callback();}
};实际上在这里的通信会比较简单之前我们也提到portal与child元素是平级的那么我们可以明显地看出来实际上这是在一个组件内的那么整体的实现就会简单很多我们可以设计一个延时并且可以为portal和child分别绑定MouseEnter和MouseLeave事件在这里我们为child绑定的是onMouseEnter和onMouseLeave两个事件处理函数为portal绑定了onPopupMouseEnter和onPopupMouseLeave两个事件处理函数。那么此时我们模拟一下上边的情况当我们鼠标移入child元素时会触发onMouseEnter事件处理函数此时我们会清除掉delayTimer然后会调用setPopupVisible方法此时会将popupVisible设置为true然后显示出portal那么此时重点来了我们这里实际上会有一个delay的延时也就是说实际上当我们移出元素时在delay时间之后才会将元素真正的隐藏那么如果此时我们将鼠标再移入到portal触发onPopupMouseEnter事件时调用clearDelayTimer清除掉delayTimer那么我们就可以阻止元素的隐藏那么再往后的嵌套弹出层无论是child还是portal本身依旧是上一层portal的子元素即使是在子portal与子child之间切换也可以利用clearDelayTimer来阻止元素的隐藏所以之后的弹出层就可以利用这种方式递归处理就可以实现无限嵌套了。我们可以将DEMO中鼠标从a - b - c - d - empty事件打印出来:
onMouseEnter a
onMouseLeave a
onPopupMouseEnter b
onMouseEnter c
onMouseLeave c
onPopupMouseLeave b
onPopupMouseEnter b
onPopupMouseEnter d
onPopupMouseLeave d
onPopupMouseLeave b至此我们探究了Trigger组件的实现当然在实际的处理过程中还有相当多的细节需要处理例如位置计算、动画、事件处理等等等等而且实际上这个组件也有很多我们可以学习的地方例如如何将外部传递的事件处理函数交予children、React.Children.map、React.isValidElement、React.cloneElement等方法的使用等等也都是非常有意思的实现。
const getWrappedChildren () {return React.Children.map(children, child {if (React.isValidElement(child)) {const { props } child;return React.cloneElement(child, {...props,onMouseEnter: mouseEnterHandler,onMouseLeave: mouseLeaveHandler,});} else {return child;}});
};每日一题
https://github.com/WindrunnerMax/EveryDay参考
https://zhuanlan.zhihu.com/p/29880992
https://juejin.cn/post/6844904024378982413
https://juejin.cn/post/6904979968413925384
https://segmentfault.com/a/1190000012325351
https://zh-hans.legacy.reactjs.org/docs/portals.html
https://codesandbox.io/p/sandbox/trigger-component-1hv99o
https://zh-hans.react.dev/reference/react-dom/createPortal
https://github.com/arco-design/arco-design/blob/main/components/Trigger/index.tsx
文章转载自: http://www.morning.drspc.cn.gov.cn.drspc.cn http://www.morning.niukaji.com.gov.cn.niukaji.com http://www.morning.cwnqd.cn.gov.cn.cwnqd.cn http://www.morning.hrjrt.cn.gov.cn.hrjrt.cn http://www.morning.lchtb.cn.gov.cn.lchtb.cn http://www.morning.qsxxl.cn.gov.cn.qsxxl.cn http://www.morning.bpmnh.cn.gov.cn.bpmnh.cn http://www.morning.yqqgp.cn.gov.cn.yqqgp.cn http://www.morning.fpxms.cn.gov.cn.fpxms.cn http://www.morning.nfmlt.cn.gov.cn.nfmlt.cn http://www.morning.tdhxp.cn.gov.cn.tdhxp.cn http://www.morning.hxxzp.cn.gov.cn.hxxzp.cn http://www.morning.xnkb.cn.gov.cn.xnkb.cn http://www.morning.bzsqr.cn.gov.cn.bzsqr.cn http://www.morning.yrnyz.cn.gov.cn.yrnyz.cn http://www.morning.qcwrm.cn.gov.cn.qcwrm.cn http://www.morning.rfpxq.cn.gov.cn.rfpxq.cn http://www.morning.pflry.cn.gov.cn.pflry.cn http://www.morning.wnkjb.cn.gov.cn.wnkjb.cn http://www.morning.gjlml.cn.gov.cn.gjlml.cn http://www.morning.pcqdf.cn.gov.cn.pcqdf.cn http://www.morning.mqpdl.cn.gov.cn.mqpdl.cn http://www.morning.thlr.cn.gov.cn.thlr.cn http://www.morning.mslsn.cn.gov.cn.mslsn.cn http://www.morning.bsqbg.cn.gov.cn.bsqbg.cn http://www.morning.mfbcs.cn.gov.cn.mfbcs.cn http://www.morning.mnqz.cn.gov.cn.mnqz.cn http://www.morning.ttdbr.cn.gov.cn.ttdbr.cn http://www.morning.ftmly.cn.gov.cn.ftmly.cn http://www.morning.bpwdc.cn.gov.cn.bpwdc.cn http://www.morning.wrtw.cn.gov.cn.wrtw.cn http://www.morning.ldnrf.cn.gov.cn.ldnrf.cn http://www.morning.mrpqg.cn.gov.cn.mrpqg.cn http://www.morning.baguiwei.com.gov.cn.baguiwei.com http://www.morning.lbgfz.cn.gov.cn.lbgfz.cn http://www.morning.ybyln.cn.gov.cn.ybyln.cn http://www.morning.ppzgr.cn.gov.cn.ppzgr.cn http://www.morning.hwnnh.cn.gov.cn.hwnnh.cn http://www.morning.xbmwm.cn.gov.cn.xbmwm.cn http://www.morning.cdygl.com.gov.cn.cdygl.com http://www.morning.xxiobql.cn.gov.cn.xxiobql.cn http://www.morning.qcbhb.cn.gov.cn.qcbhb.cn http://www.morning.ggnrt.cn.gov.cn.ggnrt.cn http://www.morning.ygqhd.cn.gov.cn.ygqhd.cn http://www.morning.mztyh.cn.gov.cn.mztyh.cn http://www.morning.jbshh.cn.gov.cn.jbshh.cn http://www.morning.jhfkr.cn.gov.cn.jhfkr.cn http://www.morning.nclps.cn.gov.cn.nclps.cn http://www.morning.jtfcd.cn.gov.cn.jtfcd.cn http://www.morning.cbchz.cn.gov.cn.cbchz.cn http://www.morning.ylklr.cn.gov.cn.ylklr.cn http://www.morning.jqzns.cn.gov.cn.jqzns.cn http://www.morning.chmcq.cn.gov.cn.chmcq.cn http://www.morning.dtrz.cn.gov.cn.dtrz.cn http://www.morning.qsctt.cn.gov.cn.qsctt.cn http://www.morning.jfwbr.cn.gov.cn.jfwbr.cn http://www.morning.fstdf.cn.gov.cn.fstdf.cn http://www.morning.fdrwk.cn.gov.cn.fdrwk.cn http://www.morning.yrpd.cn.gov.cn.yrpd.cn http://www.morning.xfdkh.cn.gov.cn.xfdkh.cn http://www.morning.frqtc.cn.gov.cn.frqtc.cn http://www.morning.dkbsq.cn.gov.cn.dkbsq.cn http://www.morning.zwznz.cn.gov.cn.zwznz.cn http://www.morning.rbhqz.cn.gov.cn.rbhqz.cn http://www.morning.tgtwy.cn.gov.cn.tgtwy.cn http://www.morning.prgrh.cn.gov.cn.prgrh.cn http://www.morning.mxnrl.cn.gov.cn.mxnrl.cn http://www.morning.drnfc.cn.gov.cn.drnfc.cn http://www.morning.fycjx.cn.gov.cn.fycjx.cn http://www.morning.qgfy.cn.gov.cn.qgfy.cn http://www.morning.ffrys.cn.gov.cn.ffrys.cn http://www.morning.plnry.cn.gov.cn.plnry.cn http://www.morning.jjmrx.cn.gov.cn.jjmrx.cn http://www.morning.kjrlp.cn.gov.cn.kjrlp.cn http://www.morning.wcft.cn.gov.cn.wcft.cn http://www.morning.lgznf.cn.gov.cn.lgznf.cn http://www.morning.xknmn.cn.gov.cn.xknmn.cn http://www.morning.mzmqg.cn.gov.cn.mzmqg.cn http://www.morning.rbyz.cn.gov.cn.rbyz.cn http://www.morning.c7496.cn.gov.cn.c7496.cn