markdown轉html在項目中運用

原由

  • 在一個產品需求中,協議是後臺模版管理的,模版是markdown語法編寫的,而且協議中存在一些須要填充的信息。協議內容是在後臺模版管理的markdown語法編輯器中編輯轉義好後,發送到後端,再由後端進行安全相關的轉義處理,最後返回給前端用戶界面一個帶變量的markdown協議字符串。

方案

方案一 marked 配合 highlight.js

  1. 安裝
npm install marked highlight.js --save-dev
# OR
yarn add marked highlight.js --save-dev
複製代碼
  1. 使用
  • 將變量替換後,把協議字符串經過getHtml方法轉換爲html,而後使用v-html進行渲染。
<template>
    <div v-html="getHtml"></div>
</template>
<script>
    let marked = require('marked');
    let hljs = require('highlight.js');
    import 'highlight.js/styles/default.css';
    marked.setOptions({
        renderer: new marked.Renderer(),
        gfm: true,
        tables: true,
        breaks: false,
        pedantic: false,
        sanitize: false,
        smartLists: true,
        smartypants: false,
        highlight: function (code, lang) {
            if (lang && hljs.getLanguage(lang)) {    
                return hljs.highlight(lang, code, true).value;
            } else {
                return hljs.highlightAuto(code).value;
            }
        }
    });
 
   export default{
     name: 'protocol', 
     props: {
        protocol: {
            type: String,
            default: ''
        }
    }
    methods: {
        getHtml() {
            return marked(this.protocol || '', {
                sanitize: true
            });
        }
    }
  }
複製代碼
  1. 問題
  • 協議中的變量被替換後,會存在一些特殊的符號,例如脫敏字段的**,以及-1.#等等markdown相關的語法,會致使最後生成的HTML與後臺建立的協議有出入,而且格式錯亂等問題。

方案二 mavon-editor 配合 highlight.js

  1. 安裝
npm install mavon-editor --save-dev
# OR
yarn add mavon-editor --save-dev
複製代碼
  1. 使用
  • 將變量替換後,把協議字符串直接賦值給組件的value進行渲染。
<template>
    <mavon-editor
      class="md"
      :value="protocol"
      :subfield = "false"
      :defaultOpen = "'preview'"
      :toolbarsFlag = "false"
      :editable="false"
      :scrollStyle="true"
      :ishljs = "true"
    ></mavon-editor>

</template>
<script>
     import mavonEditor from 'mavon-editor'
     import 'mavon-editor/dist/css/index.css'
   export default{
     name: 'protocol', 
     props: {
        protocol: {
            type: String,
            default: ''
        }
    }
  }
複製代碼
  1. 問題
  • 與方案一問題相同

最終解決方案 vue-element-admin markdown

MarkdownEditor-gitlab markdown-官網css

  1. 安裝
npm install tui-editor --save-dev
# OR
yarn add tui-editor --save-dev
複製代碼
  1. 使用
  • 將後端返回的協議字符串不作任何處理,直接經過組件的setValue方法傳給組件做爲編輯器的初始markdown語法的編輯值,賦值完成後經過組件的getHtml方法將markdown轉換爲HTML字符串,拿到HTML字符串後替換字符串中的全部變量。替換完成後調用動態生成iframe的方法顯示協議內容。
<template>
    <div class="protocol-popup">
        <div id="self-iframe" class="popup-main">
            <div class="close-btn" @click="hideDialog('isShowAbsProtocol')"><img src="../../assets/images/loan/btn_popup_close@2x.png"></div>
            <div style="display:none;" :id="id" />
        </div>
    </div>
</template>
<script>
    import { protocolVarsReplace } from '../../filter/index'
    import 'codemirror/lib/codemirror.css' // codemirror
    import 'tui-editor/dist/tui-editor.css' // editor ui
    import 'tui-editor/dist/tui-editor-contents.css' // editor content
    import Editor from 'tui-editor'
    const defaultOptions = {
        ... // 參照官網
    }

    export default {
        name: 'MarddownEditor',
        props: {
            ...,
            protocol_name: {
                type: String,
                default: '測試'
            },
            protocol_object: {
                type: Object,
                default: {}
            },
            protocol: {
                type: String,
                default: ''
            }
    },
    data() {
        return {
            editor: null,
            content: ''
        }
    },
    computed: {
        editorOptions() {
            const options = Object.assign({}, defaultOptions, this.options)
            options.initialEditType = this.mode
            options.height = this.height
            options.language = this.language
            return options
        }
    },
    watch: {
        ...
    },
    mounted() {
        this.initEditor()
        this.editor.setValue(this.protocol) // 設置初始值
        let content =  `<h3 style="text-align: center;">${this.protocol_name}</h3>${protocolVarsReplace(this.getHtml(),this.protocol_object)}` // 生成HTML字符串並全部protocolVarsReplace方法替換變量
        this.createFrame(1, content) // 動態生成iframe並顯示
    },
    destroyed() {
        this.destroyEditor()
    },
    methods: {
        createFrame( data, child) {
            const that = this
            const iframe = document.createElement("iframe");
            iframe.id = 'my-iframe'
            iframe.style.width = '100%';
            iframe.style.height = '100%';
            iframe.style.border = 'none';
            if (data) {
                if (navigator.userAgent.indexOf("MSIE") > -1 && !window.opera) {
                    iframe.onreadystatechange = function () {
                        if (iframe.readyState === "complete") { iframe.contentWindow.postMessage(JSON.stringify(data), '*')
                        }
                    };
                } else {
                    iframe.onload = function () { iframe.contentWindow.postMessage(JSON.stringify(data), '*')
                    };
                }
            }
            document.getElementById('self-iframe').appendChild(iframe);
            const iframeDom = document.getElementById('my-iframe').contentWindow.document
            iframeDom.getElementsByTagName('body')[0].innerHTML = child
            document.getElementById('my-iframe').onload = function() {
                that.loading = false
            }
        },
        hideDialog(name){
            this.$parent.hideDialog(name);
        },
        initEditor() {
            ...
        },
        destroyEditor() {
            if (!this.editor) return
            this.editor.off('change')
            this.editor.remove()
        },
        setValue(value) {
            this.editor.setValue(value)
        },
        getValue() {
            return this.editor.getValue()
        },
        setHtml(value) {
            this.editor.setHtml(value)
        },
        getHtml() {
            return this.editor.getHtml()
        }
    }
}
</script>
複製代碼
相關文章
相關標籤/搜索