作爲一名前端開發人員,掌握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
如上所示, 和任何一個輸入框同樣, 參考antd組件設計方式併兼容antd的form表單, 咱們提供了onChange方法.(具體細節下文會詳細介紹)首先利用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>
複製代碼
因此你可能看到以下界面:
爲了能實現實時預覽和編輯,光這樣還遠遠不夠,咱們還須要進行額外的處理.咱們須要用到jsoneditor其餘的api和技巧.基於以上談論,咱們很容易將編輯器封裝成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數據可視化等前端知識和實戰,歡迎在公衆號《趣談前端》加入咱們一塊兒學習討論,共同探索前端的邊界。