Myk*_*yev 13 javascript reactjs
我正在通过标准this.setState机制更新页面的一部分.我想抓住网页上已更改的元素,并向用户提供视觉反馈.
假设我们有一个RichText获得data道具的组件.要渲染的富文本将委托渲染到像小部件Paragraph,Header,BulletPoints,Text,等最后的结果是正确呈现丰富的文本.
后来data道具改变(例如套接字推送).因此Paragraph可以添加s,或者更改文本,或者可以移动.我想通过简单地突出显示已更改的HTML节点来向用户提供视觉反馈.
简而言之,我希望在您查看HTML树时实现Chrome检查器所显示的内容.它会使DOM更改闪烁.
ReactJS知道改变了什么.理想情况下,我希望能够获得这些知识.
虽然较小的组件Paragraph可能负责突出自身内部的差异,但我认为他们没有足够的外部世界知识来使其按预期工作.
格式(简化版)
{
content: [{
type: 'Document',
content: [{
type: 'Paragraph',
content: [{
type: 'Text',
text: 'text text'
}, {
type: 'Reference',
content: 'text text'
},
]}, {
type: 'BulletPoints',
content: [{
type: 'ListEntry', content: [{
type: 'Paragraph', content: [{
type: 'Text',
text: 'text text'
}, {
type: 'Reference',
content: 'text text'
}]
}]
}]
}]
Run Code Online (Sandbox Code Playgroud)
我目前的解决方案
我有一个顶级组件,它知道如何Document通过将作业委托给其他组件来呈现整个组件.我有一个实时版本的HOC:LiveDocument它负责变更可视化.
所以我在之前setState和之后捕获DOM setState.然后我使用HtmlTreeWalker来发现第一个区别(当我走树时忽略某些元素).
React已经为这些情况添加了一个插件.ReactCSSTransitionGroup
ReactCSSTransitionGroup是一个基于ReactTransitionGroup的高级API,是一个在React组件进入或离开DOM时执行CSS转换和动画的简单方法.它的灵感来自优秀的ng-animate库.
您可以轻松为进入或离开特定父级的项目添加动画.
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
const nextId = (() => {
let lastId = 0;
return () => ++lastId;
})();
class TodoList extends React.Component {
constructor(props) {
super(props);
this.state = {items: [
{id: nextId(), text: 'hello'},
{id: nextId(), text: 'world'},
{id: nextId(), text: 'click'},
{id: nextId(), text: 'me'}
]};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd() {
const newItems = this.state.items.concat([
{id: nextId(), text: prompt('Enter some text')}
]);
this.setState({items: newItems});
}
handleRemove(toRemove) {
let newItems = this.state.items.filter(item => item.id !== toRemove.id);
this.setState({items: newItems});
}
render() {
const items = this.state.items.map((item) => (
<div key={item.id} onClick={() => this.handleRemove(item)}>
{item.text}
</div>
));
return (
<div>
<button className="add-todo" onClick={this.handleAdd}>Add Item</button>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{items}
</ReactCSSTransitionGroup>
</div>
);
}
}
ReactDOM.render(<TodoList/>, document.getElementById("app"));Run Code Online (Sandbox Code Playgroud)
.example-enter {
background-color: #FFDCFF;
color: white;
}
.example-enter.example-enter-active {
background-color: #9E1E9E;
transition: background-color 0.5s ease;
}
.example-leave {
background-color: #FFDCFF;
color: white;
}
.example-leave.example-leave-active {
background-color: #9E1E9E;
transition: background-color 0.3s ease;
}
.add-todo {
margin-bottom: 10px;
}Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@15/dist/react-with-addons.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
<div id="app"></div>Run Code Online (Sandbox Code Playgroud)
不幸的是,React 没有提供一个钩子来监听外部组件的状态变化。您可以用来componentDidUpdate(prevProps, nextProps)获取组件状态更改的通知,但您必须保留先前生成的 DOM 的引用,并手动将其与新的 DOM 进行比较(例如,使用dom-compare )。我认为您当前的解决方案已经做到了这一点。
我尝试了使用MutationObserver的替代解决方案,并使用此技术来获取相对于文档的修改后的元素位置,并在变异元素上方显示边框层。看起来效果不错,但我没有检查性能。
突变观察者.js
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes) {
mutation.addedNodes.forEach(showMutationLayer);
}
});
});
const showMutationLayer = (node) => {
const mutationLayer = document.createElement('div');
mutationLayer.style.position = 'absolute';
mutationLayer.style.border = '2px solid red';
document.body.appendChild(mutationLayer);
if (node.nodeType === Node.TEXT_NODE) {
node = node.parentNode;
}
if (node.nodeType !== Node.ELEMENT_NODE) {
return;
}
const { top, left, width, height } = getCoords(node);
mutationLayer.style.top = `${top}px`;
mutationLayer.style.left = `${left}px`;
mutationLayer.style.width = `${width}px`;
mutationLayer.style.height = `${height}px`;
setTimeout(() => {
document.body.removeChild(mutationLayer);
}, 500);
};
function getCoords(elem) { // crossbrowser version
const box = elem.getBoundingClientRect();
const body = document.body;
const docEl = document.documentElement;
const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
const scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
const clientTop = docEl.clientTop || body.clientTop || 0;
const clientLeft = docEl.clientLeft || body.clientLeft || 0;
const top = box.top + scrollTop - clientTop;
const left = box.left + scrollLeft - clientLeft;
return {
top: Math.round(top),
left: Math.round(left),
width: box.width,
height: box.height
};
}
export default {
init(container) {
observer.observe(container, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
}
Run Code Online (Sandbox Code Playgroud)
main.js
import React from 'react';
import {render} from 'react-dom';
import App from './App.js';
import mutationObserver from './mutationObserver.js';
const appContainer = document.querySelector('#app');
// Observe mutations when you are in a special 'debug' mode
// for example
if (process.env.NODE_ENV === 'debug') {
mutationObserver.init(appContainer);
}
render(<App />, appContainer);
Run Code Online (Sandbox Code Playgroud)
这种技术的优点是您不必修改每个组件代码来观察更改。您也不要修改生成 DOM 的组件(该层位于#app元素之外)。可以轻松启用/禁用此功能以保持应用程序性能。
在这个小提琴中查看它的实际效果 (您可以编辑图层样式,添加 CSS 过渡以获得更好的图层)
| 归档时间: |
|
| 查看次数: |
1417 次 |
| 最近记录: |