基于Jsoneditor二次封装的可实时预览的Json编辑器组件(React版)
时间:2023-03-15 00:44:24
科技观察
前言作为前端开发者,掌握vue/react/angular等框架已经是必备技能了,我们都了解vue或react等MVVM框架提倡基于组件的开发,一方面可以提高组件的复用性和可扩展性,另一方面为项目开发带来灵活性和可维护性,方便多人开发和协作。Next本文将介绍如何使用react开发一个自定义的json编辑器组件。这里我们使用jsoneditor,一个第三方库,官方地址:jsoneditor通过实现一个json在线编辑器(不限于react,vue,原理类似)一步步学习如何封装自己的组件。你将学习到:React组件封装的基本思想。SOLID(面向对象设计)原则的介绍。jsoneditor的使用。使用PropTypes进行组件类型检查。设计思路在介绍组件设计思路之前,有必要先介绍一下著名的SOLID原理。SOLID(单一功能、开闭原则、里氏代换、接口隔离、依赖倒置)是RobertC.Martin提出的面向对象程序设计和面向对象设计的五项基本原则。使用这些原则,程序员可以更轻松、更高效地开发可维护和可扩展的系统。SOLID通常应用于测试驱动开发,是敏捷和自适应软件开发基本原则的重要组成部分。S单一功能原则:规定每个类应该有单一的功能,并且这个功能应该被本类完全封装。它的所有服务都应与此功能紧密结合。O开闭原则:规定“软件中的对象(类、模块、函数等)应该对扩展开放,对修改关闭”,意思是允许一个实体改变它的源代码有条件地改变它的行为.遵循这个原则的代码在扩展时不需要改变。l里氏替换原则:派生类(子类)对象可以在程序中替换它的基类(超类)对象,这是子类型的一种特殊定义。I接口隔离原则:表示应用程序或对象不应依赖于它的使用说明。接口隔离原则(ISP)将非常庞大和臃肿的接口拆分为更小和更具体的接口,以便应用程序或对象只需要知道他们感兴趣的方法。这种缩小的接口也称为角色接口。接口隔离原则(ISP)的目的是解耦系统,以便它们可以轻松重构、更改和重新部署。接口隔离原则是SOLID(面向对象设计)中的五个面向对象设计(OOD)原则之一,类似于GRASP(面向对象设计)中的高内聚。DDependencyinversionprinciple:指一种特定的解耦形式,让高层模块不依赖于低层模块的实现细节,依赖倒置(reversed),让低层模块依赖高层级模块需求抽象。掌握这五个原则,将有助于我们开发更好的组件,请牢记。接下来我们来看看json编辑器的设计思路。如上图,和任何输入框一样,参考antd组件设计方式,兼容antdform表单,我们提供了onChange方法。(下面会详细介绍)首先,利用jsoneditor渲染的基本样式和API,我们可以实现一个基本可用的json编辑器,然后通过暴露的json和onChange属性进行双向数据绑定,监听异常或者输入通过onError处理错误,通过themeBgColor修改默认主题颜色。通过这些接口,我们就可以全面掌握一个组件的运行情况。正文接下来,正式开始我们的正文。由于本文组件是基于react实现的,但是在vue和angular中使用,所以基本模式同样适用。关键是掌握不同框架的生命周期。在学习实现json编辑器组件之前,我们需要了解jsoneditor这个第三方组件的用法和api。1.要使用jsoneditor,我们首先执行npminstall来安装我们的组件。npminstalljsoneditor然后手动导入样式文件。这样我们就可以使用它的api:
所以你可能会看到如下界面:为了实现实时预览和编辑,这还不够,我们还需要额外的处理。我们需要使用jsoneditor等API和技巧。2.二次封装结合react根据上面的讨论,我们可以很容易的将编辑器封装成一个react组件。我们只需要在componentDidMount生命周期中初始化实例即可。反应代码可能是这样的:importReact,{PureComponent}from'react'importJSONEditorfrom'jsoneditor'import'jsoneditor/dist/jsoneditor.css'classJsonEditorextendsPureComponent{initJsonEditor=()=>{constoptions={模式:'代码',历史:真,onChange:this.onChange,onValidationError:this.onError};this.jsoneditor=newJSONEditor(this.container,options)this.jsoneditor.set(this.props.value)}componentDidMount(){this.initJsonEditor()}componentWillUnmount(){if(this.jsoneditor){this.jsoneditor.destroy()}}render(){return
this.container=elem}/>}}exportdefaultJsonEditoras对于options中的选项,我们可以参考jsoneditor的API文档,很详细。通过上面的代码,我们可以实现一个基本的react版本的json编辑器组件。接下来我们将按照设计思路一步步实现实时预览json编辑器组件。3.实现预览和编辑视图其实这个很容易实现。我们只需要实例化2个编辑器实例,一个用于预览,一个用于编辑。importReact,{PureComponent}from'react'importJSONEditorfrom'jsoneditor'import'jsoneditor/dist/jsoneditor.css'classJsonEditorextendsPureComponent{onChange=()=>{letvalue=this.jsoneditor.get()这个。viewJsoneditor.set(value)}initJsonEditor=()=>{constoptions={mode:'code',history:true};this.jsoneditor=newJSONEditor(this.container,options)this.jsoneditor.set(this.props.value)}initViewJsonEditor=()=>{constoptions={mode:'view'};this.viewJsoneditor=newJSONEditor(this.viewContainer,options)this.viewJsoneditor.set(this.props.value)}componentDidMount(){this.initJsonEditor()this.initViewJsonEditor()}componentDidUpdate(){if(this.jsoneditor){this.jsoneditor.update(this.props.value)this.viewJsoneditor.update(this.props.value)}}render(){return(<divclassName="jsoneditor-react-container"ref={elem=>this.container=elem}/>this.viewContainer=elem}/> );}}exportdefaultJsonEditor这样我们就可以实现一个可以实时预览的初步编辑器了。效果可能是这样的:接近成熟版,但还有很多细节需要处理。4、对外暴露属性和方法,支持不同场景的需求。importReact,{PureComponent}from'react'importJSONEditorfrom'jsoneditor'import'jsoneditor/dist/jsoneditor.css'classJsonEditorextendsPureComponent{//监听输入值变化onChange=()=>{letvalue=this.jsoneditor.get()this.props.onChange&&this.props.onChange(value)this.viewJsoneditor.set(value)}//对外暴露获取编辑器的json数据getJson=()=>{this.props.getJson&&this.props.getJson(this.jsoneditor.get())}//对外提交错误信息onError=(errArr)=>{this.props.onError&&this.props.onError(errArr)}initJsonEditor=()=>{constoptions={mode:'code',history:true,onChange:this.onChange,onValidationError:this.onError};this.jsoneditor=newJSONEditor(this.container,options)this.jsoneditor.set(this.props.value)}initViewJsonEditor=()=>{constoptions={mode:'view'};this.viewJsoneditor=newJSONEditor(this.viewContainer,options)this.viewJsoneditor.set(this.props.value)}componentDidMount(){this.initJsonEditor()this.initViewJsonEditor()//设置主题色this.container.querySelector('.jsoneditor-menu').style.backgroundColor=this.props.themeBgColorthis.container.querySelector('.jsoneditor').style.border=`thinsolid${this.props.themeBgColor}`this.viewContainer.querySelector('.jsoneditor-menu').style.backgroundColor=this.props.themeBgColorthis.viewContainer.querySelector('.jsoneditor').style.border=`thinsolid${this.props.themeBgColor}`}componentDidUpdate(){if(this.jsoneditor){this.jsoneditor.update(this.props.value)this.viewJsoneditor.update(this.props.value)}}render(){return(
this.container=elem}/>this.viewContainer=elem}/> );}}exportdefaultJsonEditor通过上面的流程,我们已经完成了一大半的工作,剩下的细节和优化工作,比如组件卸载时如何卸载实例,以及组件类型检测等,我们继续完成以上问题5.React内部支持组件卸载时使用PropTypes进行类型检测和清空实例类型检测。在安装react的时候,它会自动为我们安装PropTypes。具体使用请参考官网地址propTypes文档。其次,我们会使用React的componentWillUnmount生命周期清除编辑器的实例来释放内存。完整代码如下:importReact,{PureComponent}from'react'importJSONEditorfrom'jsoneditor'importPropTypesfrom'prop-types'import'jsoneditor/dist/jsoneditor.css'/***JsonEditor*@param{object}json用于绑定json数据*@param{func}onChange改变时回调*@param{func}getJson提供对外返回json的方法*@param{func}onError提供对外格式化json的回调*@param{string}themeBgColor修改主题颜色对外曝光*/classJsonEditorextendsPureComponent{onChange=()=>{letvalue=this.jsoneditor.get()this.props.onChange&&this.props.onChange(value)this.viewJsoneditor.set(value)}getJson=()=>{this.props.getJson&&this.props.getJson(this.jsoneditor.get())}onError=(errArr)=>{this.props.onError&&this.props.onError(errArr)}initJsonEditor=()=>{常量选项={模式:'代码',历史:真,onChange:this.onChange,onValidationError:this.onError};this.jsoneditor=newJSONEditor(this.container,options)this.jsoneditor.set(this.props.value)}initViewJsonEditor=()=>{constoptions={mode:'view'};this.viewJsoneditor=newJSONEditor(this.viewContainer,options)this.viewJsoneditor.set(this.props.value)}componentDidMount(){this.initJsonEditor()this.initViewJsonEditor()//设置主题色this.container.querySelector('.jsoneditor-menu').style.backgroundColor=this.props.themeBgColorthis.container.querySelector('.jsoneditor').style.border=`thinsolid${this.props.themeBgColor}`this.viewContainer。querySelector('.jsoneditor-menu').style.backgroundColor=this.props.themeBgColorthis.viewContainer.querySelector('.jsoneditor').style.border=`thinsolid${this.props.themeBgColor}`}componentWillUnmount(){如果(this.jsoneditor){this.jsoneditor.destroy()this.viewJsoneditor.destroy()}}componentDidUpdate(){if(this.jsoneditor){this.jsoneditor.update(this.props.value)this.viewJsoneditor.update(this.props.value)}}render(){return(