標籤:

你們不覺得知乎代碼編輯器有點不好用么

最近幾乎都沒有在回答中貼代碼,偶爾看到有人吐槽說知乎新版的編輯器不好用,我還以為自己一直是AB test中的那個B ...

直到昨天回答問題的時候才發現,在代碼標記內沒辦法跳出去,因為上升星座作祟,真的是渾身難受。

之前知道知乎用的是facebook/draft-js,這大半年一直在玩Vue,React有大半年沒玩了,居然版本才到0.15,半年才漲了兩個Minor_Version,真是業界清流。

(原來不是0.15,而是15... 業界清流瞬間變成業界毒瘤了...)

鑒於很久沒有碰React,所以可能這篇文章對於新手也有一些上手的指導意義。

首先是facebookincubator/create-react-app這個項目,直接跑起來一個可以運行的react項目。因為目標是搞draft,所以沒有花時間折騰webpack配置,也不建議新上手的同學直接手工配webpack,可能直接蒙B在了學習react的大門口 ...

draft-js的安裝跟著官方啪啪啪就可以了,然後貼上這段代碼,你就可以看到一個能夠輸入文字的編輯區域:

import React from react;nimport ReactDOM from react-dom;nimport {Editor, EditorState} from draft-js;nnclass MyEditor extends React.Component {n constructor(props) {n super(props);n this.state = {editorState: EditorState.createEmpty()};n this.onChange = (editorState) => this.setState({editorState});n }n render() {n return (n <Editor editorState={this.state.editorState} onChange={this.onChange} />n );n }n}nnReactDOM.render(n <MyEditor />,n document.getElementById(container)n); n

(再吐槽一下highlight不支持jsx的高亮...)

Draft不會給你一個輸入框,得到的是一個無框的contenteditable元素,並且綁定了數據和響應處理方法。

編輯器全局的對象叫做EditorState,除了管理內容以外還包括撤銷,選區,游標之類的信息。其內有對象ContentState,用於保存文檔內容。兩者關係有點類似BOM和DOM。

經過測試,果然draft官方demo也沒法在代碼區內跳出來,於是我們設計一個方案改進一下這裡。

大概思路是這樣,然後就開文檔找對應的API是不是存在

比如一下就在EditorState中找到了一個靜態方法叫做moveFocusToEnd,移動游標到末尾的問題就解決了。

監聽回車事件是在Editor Component組件上註冊的,本來我以為只是開放了一個handle給按鍵,沒想到回車這種常用事件有一個專門的handleReturn。

判斷是否在空行回車思路是收到回車之後看看當前內容是不是空行,當前內容是由ContentBlock類管理的,可以用this.state.editorState.getCurrentContent()得到。

_onEnter(e) {n var editor = this.state.editorStaten // 得到當前ContentState對象n var content = this.state.editorState.getCurrentContent()n var prevKey = editor.getSelection().getStartKey()n // 判斷當前內容塊是否為空n if(content.getBlockForKey(prevKey).getText() == ""){n // 重繪編輯器n this.setState({editorState:this.jumpOut(editor)})n // 阻止默認行為n return "handled";n }n }n

jumpOut函數負責創建一個新的內容區域,然後和老的內容區合併,最終更新全局的EditorState對象。

jumpOut(editorState) {n let contentState = editorState.getCurrentContent();n //創建Blockn const newBlock = new ContentBlock({n key: genKey(),n type: unstyled,n text: n })n //使用immutable的方式插入數據n const newBlockMap = contentState.getBlockMap().set(newBlock.key, newBlock)nn return EditorState.moveFocusToEnd(EditorState.push(n editorState,n ContentStaten .createFromBlockArray(newBlockMap.toArray())n ));n }n

最終實現的效果如下

p.s. 在react中大量的使用了Immutable.js,這種無副作用的數據結構確實和react很搭,簡單來說就是數據內容不能修改,想要改變的時候就換一個新的

這種思想還影響到了美國的一家著名遊戲公司,並以此設計了遊戲人物:diediedie ...

寫的比較匆忙,還是有很多細節問題,比如內容部分連續兩個回車也會跳出,以及遺留一個多餘的空行。

希望有時間和機會能來填坑。


推薦閱讀:

使用react開發,大家使用什麼IDE工具?JSX語法報錯?
React、Redux與複雜業務組件的復用
說說對react中JSX語法的理解?
React 的許可協議到底發生了什麼問題?
React 進階第一部分 : React Router

TAG:React | 知乎 |