摘要:也就是说,只要一发生事件。和其他的库共用由于方法的灵活性使得可以被嵌入到其他的应用中。事实上,这就是在被使用的方式。在应用中使用的层在组件中消费中和最简单的方法是监听各种事件并手动进行强制更新。并且使用组件来渲染各个项。
注:由于译者水平有限,难免会出现错误,希望大家能指出,谢谢。
react 可以被用在任何的web 应用中。它可以被嵌入到其他的应用中,要是你小心一点,其他的应用也能被嵌入到react中。这篇文章将会从一些常用的使用场景入手,重点会关注与jQuery 和backbone 的交互。但是里面的思想在我们和其他库交互时都是可以被参考的。
和操纵DOM的插件的交互react 感知不到它管理之外的dom 的变化。react的更新是取决于其内部的表现,如果同样的DOM节点被其他可操作dom节点的插件更改了,react内部状态就会变的很混乱并且无法恢复了。
这并不意味着react 无法和那些操纵DOM 的插件一起共用,你只需要更加清楚每个插件做了什么。
最简单的避免这种冲突发生的方式是阻止react 组件的更新。你可以通过渲染一个react 没有必要去更新的元素,比如一个空的
如何处理这种问题为了更好的阐述这个问题,让我们来对一个一般的jquery 插件添加一个wrapper。
首先,我们在这个节点上添加一个ref属性。在componentDidMount 方法里,我们通过获取这个节点的引用,将它传给jquery 插件。
为了避免react 在渲染期间对这个节点进行改变, 我们在render() 方法里面返回了一个空的
.这个空的节点没有任何的属性或子节点,所以React 不会对该节点进行更新,这个节点的控制权完全在jQuery插件上。这样就不会出现react 和jquery 插件都操作同样的dom 的问题了。class SomePlugin extends React.Component { componentDidMount() { this.$el = $(this.el); this.$el.somePlugin(); } componentWillUnmount() { this.$el.somePlugin("destroy"); } render() { returnthis.el = el} />; } }需要注意的是,我们定义了componentDidMount() 和componentWillUnmount() 两个生命周期的钩子函数。这是因为大多数的jQuery插件都将事件监听绑定在DOM上,所以在componentWillUnmount 中一定要移除事件监听。如果这个插件没有提供移除的方法,那你就要自己写了。一定要记得移除插件所注册的事件,否则可能会出现内存泄露。
和jQuery 的选择器插件共用为了对这些概念有更深入的了解,我们为Chosen 插件写了一个小型的wrapper。Chosen 插件的参数是一个
注意,虽然可以这样用,但这并不是最好的方式。我们推荐尽可能的使用react组件。这样在react应用中可以更好的复用,而且会有更好的使用效果
首先,让我们来看看Chosen 插件对DOM元素做了什么。
如果你在对一个这就是我们想要我们的Chosen 插件包装完成的功能
function Example() { return (console.log(value)}> ); }为了简单起见,我们使用一个非受控组件来实现它
首先,我们创建一个只有render方法的组件。在render方法中我们返回一个class Chosen extends React.Component { render() { return (); } }this.el = el}> {this.props.children} 需要注意的是,我们在
标签外加了一个 标签。这很有必要,因为我们后续会在标签后面添加一个传入的节点。然而,就React而言, 标签通常只有一个孩子节点。这就是我们如何确保React 的更新不会和通过Chosen 插入的额外的DOM节点冲突的原因。很重要的一点是,如果你在React 流之外修改了DOM节点,你必须确保React 不会因为任何原因再对这些DOM节点进行操作。接下来,我们继续实现生命周期的钩子函数。我们需要在componentDidMount里使用
节点的引用来初始化Chosen.并且在componentDidUnmount 里面销毁它。 componentDidMount() { this.$el = $(this.el); this.$el.chosen(); } componentWillUnmount() { this.$el.chosen("destroy"); }记住,react 不会对this.el 字段赋予任何特殊的含义。除非你之前在render方法里面对它进行赋值。
this.el = el}> 以上对于在render 里面获取你的组件就足够了,但是我们还希望值变化时能给实现通知。因为,我们通过Chosen 在
上 订阅了jQuery 的change事件。 我们不会直接的将this.props.onChange传给Chosen. 因为组件的属性可能会一直改变,而且这里还包含着事件的处理。因为,我们声明了一个handleChange方法来调用this.props.onChange.并且为它订阅了jQuery的change事件中。也就是说,只要一发生change 事件。就会自动执行handleChange 方法。
componentDidMount() { this.$el = $(this.el); this.$el.chosen(); this.handleChange = this.handleChange.bind(this); this.$el.on("change", this.handleChange); } componentWillUnmount() { this.$el.off("change", this.handleChange); this.$el.chosen("destroy"); } handleChange(e) { this.props.onChange(e.target.value); }最后,我们还有一件事要做。在React 中,由于属性是可以一直改变的。例如,
组件能够获取不同的children 如果父组件状态改变的话。这意味着在交互过程中,很重要的一点是,当属性改变时,我们需要手动的控制DOM的更新,不再需要react 来为我们管理DOM节点了。 Chosen 的文档建议我们使用jQuery 的trigger() 方法来通知原始DOM元素的变化。我们将使React重点关注在
中的属性this.props.children 的更新。但是我们同时也在componentDidUpdate 的生命周期函数里添加通知Chosen 他的children 列表变化的函数。 componentDidUpdate(prevProps) { if (preProps.children !== this.props.children) { this.$el.trigger("chosen:updated"); } }通过这种方式,当通过React 管理的
节点改变的时候,Chosen 就会知道需要更新DOM元素了。 class Chosen extends React.Component { componentDidMount() { this.$el = $(this.el); this.$el.chosen(); this.handleChange = this.handleChange.bind(this); this.$(el).on("change", this.handleChange); } componentDidUpdate(prevProps) { if (prevProps.children !== this.props.children) { this.$el.trigger("chosen:updated"); } } componentWillUnmount() { this.$el.off("change", this.handleChange); this.$el.chosen("destory"); } handleChange(e) { this.props.onChange(e.target.value); } render() { return (和其他的View 库共用); } }this.el = el}> {this.props.children} 由于ReactDOM.render()方法的灵活性使得React可以被嵌入到其他的应用中。
由于React 通常被用来将一个React 节点渲染到某个DOM元素中,而且ReactDOM.render()可以被UI的各个独立的部分多次调用,小到一个按钮,大到一个app。
事实上,这就是React 在Facebook 被使用的方式。这使得我们可以在React 中一块一块的开发一个应用,并且可以把它整合在现有的服务器渲染的模版中或者其他的客户端代码中。
使用React替换基于字符串的渲染在一些老的web 应用,一种常见的方式是写一大段DOM结构作为字符串,然后使用$el.html(htmlString) 的方式插入到DOM节点中。如果你的代码库中有类似的场景,那么推荐你使用react。你只需要将使用字符串渲染的部分改成react 组件就可以了。
下面是一个jQuery 的实现$("#container").html(""); $("#btn").click(function() { alert("Hello!"); });改成react 的实现
function Button() { return ; } ReactDOM.render( , document.getElementById("container"), function() { $("#btn").click(function() { alert("Hello!"); }); } );接下来,你可以将更多的业务逻辑移到react组件中去并且采用更多react 实践方式。例如,组件最好不要依赖id,因为同样的组件可能会被渲染多次。而且,我们推荐使用react 的事件系统,直接在组件
function Button(props) { return ; } function HelloButton() { function handleClick() { alert("Hello!"); } return ; } ReactDOM.render(, document.getElementById("container") ); 你可以有很多这样独立的组件,并且使用ReactDOM.render()方法将他们渲染到不同的DOM节点中。慢慢的,你在app 中使用越来越多的react 技术,你就可以将这些独立的组件整合成更大的组件。同时,将一些ReactDOM.render() 的调用移动到不同的层级中。
将React 嵌入到Backbone 的视图中Backbone 的视图就是典型的使用HTML 字符串,或者使用一些字符串模版函数来生成这样的字符串,然后将之作为DOM元素的内容。这种处理方式,也能被替换为使用React 组件渲染的方式。
下面,我们将会创建一个Backbone 的视图ParagraphView. 我们会通过渲染一个React
组件,然后使用Backbone 提供的(this.el)方式将它插入到DOM元素中的方式来重写Backbone 的render() 方法. 当然,我们也会使用ReactDOM.render()方法. function Paragraph(props) { return{props.text}
; } const ParagraphView = Backbone.View.extend({ render() { const text = this.model.get("text"); ReactDOM.render(, this.el); return this; }, remove() { ReactDOM.unmountComponentAtNode(this.el); Backbone.View.prototype.remove.call(this); } }); 很重要的一件事是,我们必须在remove方法中调用 ReactDOM.unmountComponentAtNode() 方法来解除通过react 注册的事件和一些其他的资源。
当一个组件从react树中移除时,一些清理工作会被自动执行。但是因为我们手动的移除了整个树,所以我们必须要调用这个方法来进行清理工作。
和Model 层进行交互通常我们推荐使用单向数据流比如React state, Flux 或者Redux来管理react 应用。其实,react 也能使用一些其他框架或者库的Model 层来进行管理。
在react 应用中使用Backbone 的model层在React 组件中消费Backbone中model和collections 最简单的方法是监听各种change 事件并手动进行强制更新。
渲染models 的组件会监听 "change"事件,渲染collections 的组件会监听‘add’和‘remove’事件。然后,调用this.forceUpdate() 来使用新数据重新渲染组件。
下面的例子中,List 组件会渲染一个Backbone 容器。并且使用Item 组件来渲染各个项。
class Item extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange() { this.forceUpdate(); } componentDidMount() { this.props.model.on("change", this.handleChange); } componentWillUnmount() { this.props.model.off("change", this.handleChange); } render() { return{this.props.model.get("text")} ; } } class List extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange() { this.forceUpdate(); } componentDidMount() { this.props.collection.on("add", "remove", this.handleChange); } componentWillUnmount() { this.props.collection.off("add", "remove", this.handleChange); } render() { return ({this.props.collection.map(model => (
); } } 从Backbone 的Models 中提取数据- ))}
上述的处理方式要求你的React 组件能够感知到Backbone 的Models 和 Collections .如果你后续要整合其他的数据管理方案,你可能需要更多关注Backbone 的实现细节了。
解决这个问题的一个方法是,当model 的属性改变时,将它提取为普通的数据,并将这段逻辑保存在一个多带带的地方。下面演示的是一个高阶组件,这个组件将Backbone 的model层的属性转换为state,然后把数据传递给被包裹的组件。
通过这种方式,只有这个高阶组件需要知道Backbone Models的内部细节信息,大部分的组件对Backbone 都是透明的。
下面的例子中,我们会对Model 的属性进行一份拷贝来作为初始state。我们注册了change 事件(在unmounting 中取消注册),当监听到change事件的时候,我们用model 当前的属性来更新state。最后,我们要确保,如果model 的属性自己改变的话,我们不要忘记从老的model上取消订阅,然后订阅新的model。
注意,这个例子不是为了说明和Backbone 一起协作的细节,你更应该通过这个例子了解到处理这类问题的一种通用的方式。
function connectToBackboneModel(WrappedComponent) { return class BackboneComponent extends React.Component { constructor(props) { super(props); this.state = Object.assign({}, props.model.attributes); this.handleChange = this.handleChange.bind(this); } componentDidMount() { this.props.model.on("change", this.handleChange); } componentWillReceiveProps(nextProps) { this.setState(Object.assign({}, nextProps.model.attributes)); if (nextProps.model !== this.props.model) { this.props.model.off("change", this.handleChange); nextProps.model.on("change", this.handleChange); } } componentWillUnmount() { this.props.model.off("change", this.handleChange); } handleChange(model) { this.setState(model.changedAttributes()); } render() { const propsExceptModel = Object.assign({}, this.props); delete propsExceptModel.model; return; } } } 为了阐述如何来使用它,我们会将一个react组件NameInput 和Backbone 的model 层结合起来使用,并且每次输入发生改变时,就会更新firstName 属性。
function NameInput(props) { return (); } const BackboneNameInput = connectToBackboneModel(NameInput); function Example(props) { function handleChange(e) { model.set("firstName", e.target.value); } return (
My name is {props.firstName}.); } const model = new Backbone.Model({ firstName: "Frodo" }); ReactDOM.render( , document.getElementById("root") ); 这些处理技巧不仅限于Backbone. 你也可以使用React 和其他的model 库进行整合,通过在生命周期中订阅它的变化,并且,选择性的,将数据复制到react 的state中。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88570.html
相关文章
2017年前端流行的数百个javascript库,你会几个?
摘要:有数百个免费的库出来,为应用程序选择正确的框架变得非常困难。是流行的驱动技术之一,由于年创建。在这三个块中,有几个暴露低层接口的绑定。反应由,和许多开发人员和个人的社区维护。诞生于年,是一个轻量级的框架。 有数百个免费的JS库出来,为应用程序选择正确的JavaScript框架变得非常困难。一些开发商最终会抛弃,而其他开发者则迅速发展,并得到广泛采用。许多开发人员只知道像jQuery和R...
《高性能JavaScript》(读书笔记)
摘要:加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。异步加载,和,浏览器不会失去响应它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。插件,可以让回调函数在页面结构加载完成后再运行。 这次主要是对《高性能JavaScript》一书的读书笔记,记录下自己之前没有注意到或者需要引起重视的地方 第一章 加载和执行 js代码在执行过程中会阻塞浏览...
基于 Backbone + node 的个人简历生成器(个人学习总结)
摘要:应用的功能这个应用是一个个人简历生成器。比较好的教程有这一个。这样的命名污染问题自然显而易见。而且发出多次请求也会影响性能。明显不利于维护。然而我希望能够不发生变化,因为是在文件的前提下的标签页,不能换一个标签就重建一个。 为什么学习backbone?这是个好问题。在这个前端框架爆炸的年代,比起backbone,对开发来说有更多更好的选择,react,vue,angular等等。但这些...
2017年前端框架、类库、工具大比拼
摘要:相比于开发人员的数量,目前框架类库和工具的数量似乎更多一些。本文将会讨论目前最为流行的客户端框架类库和工具以及它们之间的基本差异。典型的类库包括字符串处理日期元素事件动画网络请求等功能。所以不需要明确的区分类库框架和工具。 相比于JavaScript开发人员的数量,目前JavaScript框架、类库和工具的数量似乎更多一些。截至2017年5月,GitHub上的快速搜索显示,有超过110...
发表评论
0条评论
ckllj
男|高级讲师
TA的文章
阅读更多
LINUX:程序和进程
阅读 2335·2021-11-23 09:51
短信验证码平台有哪些比较好用?需从这3个方面来决定!
阅读 1137·2021-11-22 13:52
[11.11]CMIVPS年度大促VPS主机5折,香港大带宽/直连线路月付3.5美元起
阅读 3610·2021-11-10 11:35
Tmwhost,澳门VPS(7.5折优惠),$5.62/月,1核/1G内存/50G Raid5 SS
阅读 1187·2021-10-25 09:47
Resultful API的拦截(过滤器——Filter)
阅读 2994·2021-09-07 09:58
前端每日实战:145# 视频演示如何用纯 CSS 创作一个电源开关控件
阅读 1059·2019-08-30 15:54
PHP基于Thinkphp5的砍价活动相关设计
阅读 2817·2019-08-29 14:21
CSS形状之border-radius
阅读 3024·2019-08-29 12:20