作为一名前端开发人员,掌握vue/react/angular等框架已是必不可少的技能了,咱们都知道,vue或react等MVVM框架提倡组件化开发,这样一方面能够提升组件复用性和可扩展性,另外一方面也带来了项目开发的灵活性和可维护,方便多人开发协做.接下来文章将介绍如何使用react,开发一个自定义json编辑器组件.咱们这里使用了jsoneditor这个第三方库,官方地址: jsoneditor 经过实现一个json在线编辑器,来学习如何一步步封装本身的组件(不限于react,vue,原理相似).javascript
你将学到:css
在介绍组件设计思路以前,有必要介绍一下著名的SOLID原则.html
SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁提出的面向对象编程和面向对象设计的五个基本原则。利用这些原则,程序员能更容易和高效的开发一个可维护和扩展的系统。 SOLID被典型的应用在测试驱动开发上,而且是敏捷开发以及自适应软件开发的基本原则的重要组成部分。前端
S 单一功能原则: 规定每一个类都应该有一个单一的功能,而且该功能应该由这个类彻底封装起来。全部它的服务都应该严密的和该功能保持一致。vue
O 开闭原则: 规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,可是对于修改是封闭的”,这意味着一个实体是容许在不改变它的源代码的前提下变动它的行为。遵循这种原则的代码在扩展时并不须要改变。java
L 里氏替换原则: 派生类(子类)对象能够在程序中代替其基类(超类)对象,是对子类型的特别定义.node
I 接口隔离原则: 指明应用或者对象应该不依赖于它不使用的方法。接口隔离原则(ISP)拆分很是庞大臃肿的接口成为更小的和更具体的接口,这样应用或对象只须要知道它们感兴趣的方法。这种缩小的接口也被称为角色接口。接口隔离原则(ISP)的目的是系统去耦合,从而容易重构,更改和从新部署。接口隔离原则是在SOLID (面向对象设计)中五个面向对象设计(OOD)的原则之一,相似于在GRASP (面向对象设计)中的高内聚性。react
D 依赖反转原则: 是指一种特定的解耦 形式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。webpack
掌握好这5个原则将有利于咱们开发出更优秀的组件,请默默记住.接下来咱们来看看json编辑器的设计思路.css3
首先利用jsoneditor渲染的基本样式以及API,咱们能实现一个基本可用的json编辑器,而后经过对外暴露的json和onChange属性进行数据双向绑定, 经过onError来监控异常或者输入的错误, 经过themeBgColor来修改默认的主题色,经过这几个接口,咱们便能彻底掌握一个组件的运行状况.
接下来咱们就正式开始咱们的正文.因为本文的组件是基于react实现的,可是用在vue,angular上,基本模式一样适用.关键就是掌握好不一样框架的生命周期.
在学习实现json编辑器组件以前,咱们有必要了解一下jsoneditor这个第三方组件的用法与api.
咱们先执行npm install安装咱们的组件
npm install jsoneditor
复制代码
其次手动引入样式文件
<link href="jsoneditor/dist/jsoneditor.min.css" rel="stylesheet" type="text/css">
复制代码
这样,咱们就能使用它的api了:
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
<script> // 建立编辑器 var container = document.getElementById("jsoneditor"); var editor = new JSONEditor(container); // 设置json数据 function setJSON () { var json = { "Array": [1, 2, 3], "Boolean": true, "Null": null, "Number": 123, "Object": {"a": "b", "c": "d"}, "String": "Hello World" }; editor.set(json); } // 获取json数据 function getJSON() { var json = editor.get(); alert(JSON.stringify(json, null, 2)); } </script>
复制代码
因此你可能看到以下界面:
基于以上谈论,咱们很容易将编辑器封装成react组件, 咱们只须要在componentDidMount生命周期里初始化实例便可.react代码多是这样的:
import React, { PureComponent } from 'react'
import JSONEditor from 'jsoneditor'
import 'jsoneditor/dist/jsoneditor.css'
class JsonEditor extends PureComponent {
initJsonEditor = () => {
const options = {
mode: 'code',
history: true,
onChange: this.onChange,
onValidationError: this.onError
};
this.jsoneditor = new JSONEditor(this.container, options)
this.jsoneditor.set(this.props.value)
}
componentDidMount () {
this.initJsonEditor()
}
componentWillUnmount () {
if (this.jsoneditor) {
this.jsoneditor.destroy()
}
}
render() {
return <div className="jsoneditor-react-container" ref={elem => this.container = elem} /> } } export default JsonEditor 复制代码
至于options里的选项, 咱们能够参考jsoneditor的API文档,里面写的很详细, 经过以上代码,咱们即可以实现一个基本的react版的json编辑器组件.接下来咱们来按照设计思路一步步实现可实时预览的json编辑器组件.
其实这一点很好实现,咱们只须要实例化2个编辑器实例,一个用于预览,一个用于编辑就行了.
import React, { PureComponent } from 'react'
import JSONEditor from 'jsoneditor'
import 'jsoneditor/dist/jsoneditor.css'
class JsonEditor extends PureComponent {
onChange = () => {
let value = this.jsoneditor.get()
this.viewJsoneditor.set(value)
}
initJsonEditor = () => {
const options = {
mode: 'code',
history: true
};
this.jsoneditor = new JSONEditor(this.container, options)
this.jsoneditor.set(this.props.value)
}
initViewJsonEditor = () => {
const options = {
mode: 'view'
};
this.viewJsoneditor = new JSONEditor(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 (
<div className="jsonEditWrap">
<div className="jsoneditor-react-container" ref={elem => this.container = elem} />
<div className="jsoneditor-react-container" ref={elem => this.viewContainer = elem} />
</div>
);
}
}
export default JsonEditor
复制代码
这样,咱们便能实现一个初步的可实时预览的编辑器.可能效果长这样:
import React, { PureComponent } from 'react'
import JSONEditor from 'jsoneditor'
import 'jsoneditor/dist/jsoneditor.css'
class JsonEditor extends PureComponent {
// 监听输入值的变化
onChange = () => {
let value = 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 = () => {
const options = {
mode: 'code',
history: true,
onChange: this.onChange,
onValidationError: this.onError
};
this.jsoneditor = new JSONEditor(this.container, options)
this.jsoneditor.set(this.props.value)
}
initViewJsonEditor = () => {
const options = {
mode: 'view'
};
this.viewJsoneditor = new JSONEditor(this.viewContainer, options)
this.viewJsoneditor.set(this.props.value)
}
componentDidMount () {
this.initJsonEditor()
this.initViewJsonEditor()
// 设置主题色
this.container.querySelector('.jsoneditor-menu').style.backgroundColor = this.props.themeBgColor
this.container.querySelector('.jsoneditor').style.border = `thin solid ${this.props.themeBgColor}`
this.viewContainer.querySelector('.jsoneditor-menu').style.backgroundColor = this.props.themeBgColor
this.viewContainer.querySelector('.jsoneditor').style.border = `thin solid ${this.props.themeBgColor}`
}
componentDidUpdate() {
if(this.jsoneditor) {
this.jsoneditor.update(this.props.value)
this.viewJsoneditor.update(this.props.value)
}
}
render() {
return (
<div className="jsonEditWrap">
<div className="jsoneditor-react-container" ref={elem => this.container = elem} />
<div className="jsoneditor-react-container" ref={elem => this.viewContainer = elem} />
</div>
);
}
}
export default JsonEditor
复制代码
经过以上的过程,咱们已经完成一大半工做了,剩下的细节和优化工做,好比组件卸载时如何卸载实例, 对组件进行类型检测等,咱们继续完成以上问题.
import React, { PureComponent } from 'react'
import JSONEditor from 'jsoneditor'
import PropTypes from '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 为外部暴露修改主题色 */
class JsonEditor extends PureComponent {
onChange = () => {
let value = 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 = () => {
const options = {
mode: 'code',
history: true,
onChange: this.onChange,
onValidationError: this.onError
};
this.jsoneditor = new JSONEditor(this.container, options)
this.jsoneditor.set(this.props.value)
}
initViewJsonEditor = () => {
const options = {
mode: 'view'
};
this.viewJsoneditor = new JSONEditor(this.viewContainer, options)
this.viewJsoneditor.set(this.props.value)
}
componentDidMount () {
this.initJsonEditor()
this.initViewJsonEditor()
// 设置主题色
this.container.querySelector('.jsoneditor-menu').style.backgroundColor = this.props.themeBgColor
this.container.querySelector('.jsoneditor').style.border = `thin solid ${this.props.themeBgColor}`
this.viewContainer.querySelector('.jsoneditor-menu').style.backgroundColor = this.props.themeBgColor
this.viewContainer.querySelector('.jsoneditor').style.border = `thin solid ${this.props.themeBgColor}`
}
componentWillUnmount () {
if (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 (
<div className="jsonEditWrap">
<div className="jsoneditor-react-container" ref={elem => this.container = elem} />
<div className="jsoneditor-react-container" ref={elem => this.viewContainer = elem} />
</div>
);
}
}
JsonEditor.propTypes = {
json: PropTypes.object,
onChange: PropTypes.func,
getJson: PropTypes.func,
onError: PropTypes.func,
themeBgColor: PropTypes.string
}
export default JsonEditor
复制代码
因为组件严格遵照开闭原则,因此咱们能够提供更加定制的功能在咱们的json编辑器中,已实现不一样项目的需求.对于组件开发的健壮性探讨,除了使用propTypes外还能够基于typescript开发,这样适合团队开发组件库或者复杂项目组件的追溯和查错.最终效果以下:
若是想了解更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在公众号《趣谈前端》加入咱们一块儿学习讨论,共同探索前端的边界。