Web 前端小白入門(二):心路歷程,非技術指南

DuanPengfei發表於2018-02-09

此篇文章記錄一個小白入門 web 前端開發的歷程,記錄了我如何學習前端開發技術,並開發自己工作中常用的工具集,並不是技術入門指導。

第二個版本 Markdown 編輯器

前文提到,現有 Markdown 匯出 PDF 時設定字型比較麻煩,所以才自己動手做了一個 Markdown 編輯器。在完成了第一版開發熟悉了 React 基礎使用後就開始針對需求開發第二個版本。

第二個版本(0.2.0)Markdown 編輯器支援匯出 PDF 和通過 CSS 設定編輯器字型與匯出的 PDF 字型。在使用 Markdown 編輯文件時常常要向文件中插入圖片,Markdown 支援填寫圖片地址來渲染圖片,那麼可以直接在編輯器介面上傳圖片並插入到編輯框是很常規的一個需求,所以這個版本也做了圖片上傳並插入到編輯框游標所在位置。還有一個功能也是我們在使用其他 Markdown 編輯器時常見到的,就是介面隱藏(隱藏編輯視窗、隱藏渲染視窗),所以此版本中也加入了頁面隱藏功能。

此版本的 Markdown 編輯器分成了四個元件:

  • MD 父元件用來組織布局,state 用來儲存資料並傳遞給子元件
  • MDEditor 元件包含編輯框,用來輸入文件內容
  • MDRender 元件包含渲染框,用來展示渲染後的 HTML 內容
  • MDHeader 元件包含各種操作的按鈕

所用技術棧如下:

  • AntD
  • axios
  • markdown-it
  • React
  • webpack

字型修改

字型修改功能的實現很簡單,增加了一個輸入框,用來填寫 CSS 字型,監聽 enter 點選事件,修改 MD 元件 state 中的 fontFamily。編輯框與渲染框的 CSS style 中 font-family 為 state 中的 fontFamily

monaco 字型.png

shizuha_courier.png

隱藏介面

隱藏介面與字型修改的思路一樣,在 MD 元件的 state 中增加 viewMode,MD 元件根據不同的 viewMode 修改 render 中需要渲染的元件。

shizuha_0.2.0_hide_render.png

shizuha_0.2.0_hide_editor.png

圖片上傳

在實現圖片上傳功能時遇到一個需要解決的問題,就是如何獲取到編輯框的游標位置。使用了 React 後對便不再手動操作 DOM 元素了,這個問題要如何解決呢?搜尋了 React 文件後發現,可以給元件設定一個屬性 ref 用來儲存當前元件的 DOM 元素。但是如上所述,此版本分成了幾個元件,圖片上傳按鈕在 MDHeader 元件中,而輸入框在 MDEditor 中,那麼如果使用 ref 屬性就要把 DOM 元素存在父元件 MD 中,然後再傳遞給 MDHeader 中。感覺這樣做反倒麻煩了,何不直接使用 CSS id 直接獲取一下 DOM 呢,畢竟這個 Mardkown 不存在複用問題,各子元件見耦合又怎樣。所以我最後採取了簡單粗暴的方式 document.getElementById('shizuha-md-editor')

獲取到 DOM 後就是如何判斷游標位置,經過一番 Google 後找到了解決方案(前端小白一點知識儲備都沒有,對 HTML 與前端的 JavaScript 環境一無所知),程式碼如下:

// 此段程式碼在 MDHeader 元件中
uploadCustomRequest({ file, onSuccess, onError }) {
    const self = this;

    const data = new FormData();
    data.append('file', file);
    data.append('dir', '/shizuha');

    axios.post('http://xxx.com/upload', data)
        .then(function (res) {
            if (1 !== res.data.success) {
                onError(new Error('upload picture failed'));
                return message.error('上傳圖片失敗');
            }

            onSuccess(undefined, file);
            self.setState({
                fileList: []
            });
            return self.props.insertImgToMarkdownContent(res.data.path, file.name);
        })
        .catch(function (err) {
            onError(err);
            return message.error(err.message);
        });
}

// 此段程式碼在 MD 父元件中
insertImgToMarkdownContent(url, name) {
    const mdEditorElement = document.getElementById('md-editor');
    const insertImg = ` ![${name}](${url}) `
    const start = mdEditorElement.selectionStart;
    const end = mdEditorElement.selectionEnd;
    const current = start + insertImg.length;
    const markdownContent = mdEditorElement.value.slice(0, start) +
        insertImg +
        mdEditorElement.value.slice(end);
    this.handleMarkdownContentChange({
        target: {
            value: markdownContent
        }
    });
    message.success('圖片地址已插入編輯框');
}
複製程式碼

圖片上傳後,把資料傳給了父元件進行處理,修改編輯框內容同時觸發渲染框重新渲染,因為這個時候還沒有引入 Redux 呢,學習總得循序漸進嘛。

圖片上傳.png

匯出 PDF

匯出 PDF 功能依賴於 Chrome 自帶的列印功能,在開發這個功能時遇到一些需要思考的問題。瞭解到 Chrome 列印是列印當前 HTML body 內的所有內容,但是我們的 body 中除了渲染框還有其他內容,這些顯然是不應該在匯出的 PDF 中展示的。這個問題的解決思路就是在即將匯出 PDF 前,把 body 內容換成渲染框的內容,在匯出結束後再復原。

所以在 MD 元件的 state 中加入了一個欄位 isPrinting,MD 元件的 render 中判斷 isPrinting 是否為 true,如果為 true 就只展示 MDRender 元件,否則展示所有元件。相配合的 MDHeader 中的匯出 PDF 按鈕對應的操作函式就是修改 isPrintingtrue 然後呼叫 window.print(),等列印結束後再修改 isPrintingfalse 即可,程式碼如下:

htmlToPDF() {
    this.setState({
        isPrinting: true
    }, () => {
        window.print();
        this.setState({
            isPrinting: false
        });
    });
}
複製程式碼

shizuha_pdf.png

可以看到在列印的這個時刻,後面的 HTML 內容僅為渲染框的內容。

總結

此版本已經完成我需要的所有功能,但是程式碼中資料的管理依賴於元件 props 傳遞,邏輯複雜而且不易維護,所以接下來的學習就是如果使用資料儲存庫 Redux 來實現元件間共享資料的管理。

文章太長,分開多篇記錄,未完待續。

相關文章