前端:从零封装一个可实时预览的Json编辑器
时间:2023-03-13 07:56:06
科技观察
前端:封装一个可实时预览的Json编辑器MVVM等零基础框架或react提倡组件化开发,一方面可以提高组件的复用性和可扩展性,另一方面为项目开发带来灵活性和可维护性另一方面,促进多人的发展和协作。下一篇文章将介绍如何使用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中使用,所以基本模式同样适用。关键是要掌握不同框架的Lifecycle。在学习实现json编辑器组件之前,我们需要先了解一下第三方组件jsoneditor的使用以及api.jsoneditor的使用和安装。我们先执行npminstall安装我们的组件npminstalljsoneditor然后手动引入样式文件使用它的api:
所以你可能会看到如下界面:为了实现实时预览和编辑,这还不够,我们还需要额外的处理。我们需要使用jsoneditor的其他API和技能。结合react进行二次封装基于上面的讨论,我们可以很容易的将编辑器封装成一个react组件,我们只需要在componentDidMount生命周期中初始化实例即可。反应代码可能是这样的:code',history:true,onChange:this.onChange,onValidationError:this.onError};this.jsoneditor=newJSONEditor(this.container,options)this.jsoneditor.set(this.props.value)}componentDidMount(){这个.initJsonEditor()}componentWillUnmount(){if(this.jsoneditor){this.jsoneditor.destroy()}}render(){return
this.container=elem}/>}}exportdefaultJsonEditor至于options中的选项,我们可以参考jsoneditor的API文档,写的很详细。通过上面的代码,我们可以实现一个基本的react版本的json编辑器组件。接下来我们就按照设计思路一步步实现可以实时预览的json编辑器组件。实现预览和编辑视图其实很容易实现。我们只需要实例化两个编辑器实例,一个用于预览,一个用于编辑。importReact,{PureComponent}from'react'importJSONEditorfrom'jsoneditor'import'jsoneditor/dist/jsoneditor.css'classJsonEditorextendsPureComponent{onChange=()=>{letvalue=this.jsoneditor.get()this.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(this.container=elem}/>this.viewContainer=elem}/> );}}exportdefaultJsonEditor这样我们就可以实现一个可以实时预览的初步编辑器,效果大概是这样的:接近成熟版,但是还有很多细节需要处理。expose支持不同场景需求的属性和方法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)这个。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.json)this.viewJsoneditor.update(this.props.json)}}render(){return(
this.container=elem}/>this.viewContainer=elem}/> );}}exportdefaultJsonEditor通过上面的流程,我们已经完成了一大半的工作,剩下的细节和优化工作,比如如何卸载instance当组件被卸载,对组件进行类型检测等,我们继续完成上面的问题。使用PropTypes进行类型检测,并在组件卸载时清除实例类型检测。React内部支持,在安装react的时候,会自动为我们安装PropTypes。具体使用方法可以参考官网地址propTypes文档,其次我们会在react的componentWillUnmount生命周期中清除editor实例来释放内存。完整代码如下: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=()=>{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}`}componentWillUnmount(){if(this.jsoneditor){this.jsoneditor.destroy()this.viewJsoneditor.destroy()}}componentDidUpdate(){if(this.jsoneditor){this.jsoneditor.update(this.props.json)this.viewJsoneditor.update(this.props.json)}}render(){返回(
this.container=elem}/>this.viewContainer=elem}/> );}}JsonEditor.propTypes={json:PropTypes.object,onChange:PropTypes.func,getJson:PropTypes.func,onError:PropTypes.func,themeBgColor:PropTypes.string}exportdefaultJsonEditor因为组件严格遵循开闭原则,我们可以在我们的Injson编辑器,实现了不同项目的需求。对于组件开发健壮性的讨论,除了使用propTypes,还可以基于typescript进行开发,适用于组件库的团队开发或者复杂项目组件的溯源和查错。最终效果如下:作者将实现的组件发布到了npm上。有兴趣的可以直接使用npm安装使用。方法如下:npmi@alex_xu/xui//importxuiimport{Button,Skeleton,Empty,Progress,Tag,Switch,Drawer,Badge,Alert}来自'@alex_xu/xui'这个组件库支持按需导入,我们只需要在项目中配置babel-plugin-import即可,具体配置如下://.babelrc"plugins":[["import",{"libraryName":"@alex_xu/xui","style":true}]]npm库截图如下:好了,今天的分享就到这里!