溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

js制作xml在線編輯器代碼分享

發(fā)布時(shí)間:2021-08-24 20:24:55 來(lái)源:億速云 閱讀:158 作者:chen 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要講解了“js制作xml在線編輯器代碼分享”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“js制作xml在線編輯器代碼分享”吧!

目錄
  • 前言

  • 在線XML編輯器的需求

  • 技術(shù)預(yù)研

    • 可視化編程

    • VSCODE插件

    • 在線編輯器

  • 最初形態(tài):簡(jiǎn)單的在線XML編輯器

    • 用CodeMirror做編輯器

    • 學(xué)習(xí)XML,并提取出tags規(guī)則

  • 進(jìn)化形態(tài):加載樹(shù)形文件結(jié)構(gòu)和全文件校驗(yàn)功能的在線XML編輯器

    • 左側(cè)文件樹(shù)

    • 全文件校驗(yàn)功能

    • 非遞歸遍歷樹(shù)

    • IndexDB保存文件內(nèi)容

  • 究極進(jìn)化形態(tài):突破瀏覽器沙盒限制,實(shí)現(xiàn)對(duì)電腦本地文件的增刪改

    • 更多的功能與細(xì)節(jié)

      • 不足與總結(jié)

        前言

        這里主要還是談一下技術(shù)相關(guān)的,也就是一個(gè)純前端實(shí)現(xiàn),用于寫(xiě)MOD的XML在線編輯器。

        它是一個(gè)仿VSCode風(fēng)格的編輯器,可以自動(dòng)學(xué)習(xí)游戲MOD文件生成約束規(guī)則,幫助我們實(shí)現(xiàn)代碼提示和代碼校驗(yàn)。

        更重要的是它可以直接修改你電腦上的的文件。

        這是最終成品的代碼倉(cāng)庫(kù):https://gitee.com/vvjiang/mod-xml-editor

        以及一張成品展示圖:

        js制作xml在線編輯器代碼分享

        本篇博客所涉及到的技術(shù):

        • CodeMirror

        • react-codemirror2

        • xmldom

        • FileReader

        • IndexDB

        • Web Worker

        • File System Access

        讓我們從頭開(kāi)始講起。

        在線XML編輯器的需求

        在做《騎砍2》的MOD時(shí),需要經(jīng)常寫(xiě)XML文件。

        因?yàn)轵T砍2的數(shù)據(jù)配置就是以XML的形式保存,然后MOD加載后,用MOD的XML去覆蓋官方自己的XML。

        通常我們做MOD數(shù)據(jù)這塊,就是參考官方的XML自己去寫(xiě)XML文件。

        但是這樣會(huì)遇到一個(gè)問(wèn)題,XML這東西沒(méi)有代碼提示和代碼校驗(yàn),寫(xiě)錯(cuò)一個(gè)字符也很難發(fā)現(xiàn)。

        又或者有時(shí)候游戲更新,它的XML規(guī)則可能會(huì)改動(dòng)。

        官方是不會(huì)發(fā)布通知告訴你這些改動(dòng)點(diǎn)的,所以如果你還是用的以前的元素和屬性那就等于寫(xiě)錯(cuò)了。

        寫(xiě)錯(cuò)的結(jié)果往往是游戲加載MOD時(shí)直接崩潰,也不會(huì)給你任何提示,你只能慢慢去尋找BUG。

        而騎砍2作為一個(gè)大型游戲,每次啟動(dòng)時(shí)間都很長(zhǎng),導(dǎo)致你測(cè)試一個(gè)MOD數(shù)據(jù)是否配置正確的測(cè)試流程會(huì)非常長(zhǎng)。

        媽耶,多少個(gè)夜晚,游戲崩潰的那一瞬間,我人就崩潰了。

        所以后來(lái)我就想著做一個(gè)XML在線編輯器去解決這個(gè)問(wèn)題。

        技術(shù)預(yù)研

        可視化編程

        其實(shí)我一開(kāi)始沒(méi)有做這個(gè)XML編輯器的想法,因?yàn)檫@玩意一看就難搞,而是想通過(guò)一個(gè)可視化編程,通過(guò)拖拉拽元素和屬性的方式去實(shí)現(xiàn)。

        你別說(shuō),我還真的做了一套初步方案出來(lái),結(jié)果配置一個(gè)大型的XML這玩意拖拉拽無(wú)數(shù)次,心態(tài)逐漸爆炸,遂放棄此方案。

        VSCODE插件

        想看看有沒(méi)有什么VSCode插件可以進(jìn)行代碼提示,有一個(gè)使用XSD進(jìn)行代碼校驗(yàn)的,貌似還是IBM提供的。

        但是很可惜已經(jīng)廢棄,然后用不了了,放棄此方案。

        在線編輯器

        后來(lái)之所以使用在線編輯器的方式做這個(gè),是因?yàn)槿脑路莨具@邊想要做一個(gè)在線編輯java項(xiàng)目環(huán)境xml配置文件的一個(gè)東西。

        然后我這邊就嘗試著做了一個(gè),了解到了CodeMirror。

        CodeMirror通過(guò)自己配置tags來(lái)支持xml的代碼提示,但是并不支持xml的代碼校驗(yàn),所以需要自己去做xml的代碼校驗(yàn)。

        并且因?yàn)橥ǔN覀內(nèi)バr?yàn)xml用的是xsd,所以還需要將xsd轉(zhuǎn)換成CodeMirror的tags配置。

        這個(gè)不論是百度Google,還是說(shuō)Github,都是查不到相對(duì)應(yīng)的方案,所以只能自己寫(xiě)代碼去實(shí)現(xiàn)。

        在這個(gè)過(guò)程中,我對(duì)CodeMirror,xsd,htmllint都有了比較深的一個(gè)了解,最終完成了項(xiàng)目。

        因?yàn)檫@是之前公司的代碼,所以這里就不放出來(lái)了。

        總之,在這個(gè)過(guò)程中了解到CodeMirror這么個(gè)東西,才有了用CodeMirror去做MOD的在線編輯器的想法。

        最初形態(tài):簡(jiǎn)單的在線XML編輯器

        好了,廢話不說(shuō),拿起鍵盤(pán)就是無(wú)腦干。

        最初形態(tài)沒(méi)有左側(cè)的文件樹(shù),只有一個(gè)單純的編輯器和一個(gè)規(guī)則學(xué)習(xí)彈框。

        涉及到的技術(shù)就三個(gè):

        CodeMirror

        FileReader

        xmldom

        用CodeMirror做編輯器

        CodeMirror這塊主要使用的react的一個(gè)封裝版react-codemirror2,反正就是看文檔和Demo自己配。

        唯一的難度就是網(wǎng)上一大堆的CodeMirror配置介紹很多都是抄來(lái)抄去,轉(zhuǎn)載來(lái)轉(zhuǎn)載去,還是個(gè)錯(cuò)的,簡(jiǎn)直離譜。

        js制作xml在線編輯器代碼分享

        總之你想玩的話最好還是看官方文檔(https://codemirror.net/) 和文檔上的Demo,然后自己研究下,抄別人配置的話水很深,你把握不住的。

        我這里貼一段我封裝的編輯器組件的配置代碼吧,反正絕對(duì)可用,絕大多數(shù)編輯器的功能都OK,不過(guò)僅僅適用于編輯XML。

        里面的注釋比較詳盡了,包括常用的代碼折疊,代碼格式化都有,我就懶得一一講了,你可以參考官網(wǎng)自己看看。

        其中的一些引用代碼我就不貼了,有興趣的可以去上面提到的代碼倉(cāng)庫(kù)看看。

        import { useEffect } from 'react'
        import { Controlled as ControlledCodeMirror } from 'react-codemirror2'
        import CodeMirror from 'codemirror'
        import 'codemirror/lib/codemirror.css'
        import 'codemirror/theme/ayu-dark.css'
        import 'codemirror/mode/xml/xml.js'
        // 光標(biāo)行代碼高亮
        import 'codemirror/addon/selection/active-line'
        // 折疊代碼
        import 'codemirror/addon/fold/foldgutter.css'
        import 'codemirror/addon/fold/foldcode.js'
        import 'codemirror/addon/fold/xml-fold.js'
        import 'codemirror/addon/fold/foldgutter.js'
        import 'codemirror/addon/fold/comment-fold.js'
        // 代碼提示補(bǔ)全和
        import 'codemirror/addon/hint/xml-hint.js'
        import 'codemirror/addon/hint/show-hint.css'
        import './hint.css'
        import 'codemirror/addon/hint/show-hint.js'
        // 代碼校驗(yàn)
        import 'codemirror/addon/lint/lint'
        import 'codemirror/addon/lint/lint.css'
        import CodeMirrorRegisterXmlLint from './xml-lint'
        // 輸入> 時(shí)自動(dòng)鍵入結(jié)束標(biāo)簽
        import 'codemirror/addon/edit/closetag.js'
        // 注釋
        import 'codemirror/addon/comment/comment.js'
        
        // 用于調(diào)整codeMirror的主題樣式
        import style from './index.less'
        
        // 注冊(cè)Xml代碼校驗(yàn)
        CodeMirrorRegisterXmlLint(CodeMirror)
        
        // 格式化相關(guān)
        CodeMirror.extendMode("xml", {
        commentStart: "<!--",
        commentEnd: "-->",
        newlineAfterToken: function (type, content, textAfter, state) {
            return (type === "tag" && />$/.test(content) && state.context) ||
            /^</.test(textAfter);
        }
        });
        
        // 格式化指定范圍
        CodeMirror.defineExtension("autoFormatRange", function (from, to) {
        var cm = this;
        var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
        var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
        var tabSize = cm.getOption("tabSize");
        
        var out = "", lines = 0, atSol = from.ch === 0;
        function newline() {
            out += "\n";
            atSol = true;
            ++lines;
        }
        
        for (var i = 0; i < text.length; ++i) {
            var stream = new CodeMirror.StringStream(text[i], tabSize);
            while (!stream.eol()) {
            var inner = CodeMirror.innerMode(outer, state);
            var style = outer.token(stream, state), cur = stream.current();
            stream.start = stream.pos;
            if (!atSol || /\S/.test(cur)) {
                out += cur;
                atSol = false;
            }
            if (!atSol && inner.mode.newlineAfterToken &&
                inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i + 1] || "", inner.state))
                newline();
            }
            if (!stream.pos && outer.blankLine) outer.blankLine(state);
            if (!atSol && i < text.length - 1) newline();
        }
        
        cm.operation(function () {
            cm.replaceRange(out, from, to);
            for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
            cm.indentLine(cur, "smart");
            cm.setSelection(from, cm.getCursor(false));
        });
        });
        
        // Xml編輯器組件
        function XmlEditor(props) {
        const { tags, value, onChange, onErrors, onGetEditor, onSave } = props
        
        useEffect(() => {
            // tags 每次變動(dòng)時(shí),都會(huì)重新改變校驗(yàn)規(guī)則
            CodeMirrorRegisterXmlLint(CodeMirror, tags, onErrors)
        }, [onErrors, tags])
        
        // 開(kāi)始標(biāo)簽
        function completeAfter(cm, pred) {
            if (!pred || pred()) setTimeout(function () {
            if (!cm.state.completionActive)
                cm.showHint({
                completeSingle: false
                });
            }, 100);
            return CodeMirror.Pass;
        }
        
        // 結(jié)束標(biāo)簽
        function completeIfAfterLt(cm) {
            return completeAfter(cm, function () {
            var cur = cm.getCursor();
            return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === "<";
            });
        }
        
        // 屬性和屬性值
        function completeIfInTag(cm) {
            return completeAfter(cm, function () {
            var tok = cm.getTokenAt(cm.getCursor());
            if (tok.type === "string" && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length === 1)) return false;
            var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
            return inner.tagName;
            });
        }
        
        return (
            <div className={style.editor} >
            <ControlledCodeMirror
                value={value}
                options={{
                mode: {
                    name: 'xml',
                    // xml 屬性換行的時(shí)候是否加上標(biāo)簽的長(zhǎng)度
                    multilineTagIndentPastTag: false
                },
                indentUnit: 2, // 換行的默認(rèn)縮進(jìn)多少個(gè)空格
                theme: 'ayu-dark', // 編輯器主題
                lineNumbers: true,// 是否顯示行號(hào)
                autofocus: true,// 自動(dòng)獲取焦點(diǎn)
                styleActiveLine: true,// 光標(biāo)行代碼高亮
                autoCloseTags: true, // 在輸入>時(shí)自動(dòng)鍵入結(jié)束元素
                toggleComment: true, // 開(kāi)啟注釋
                // 折疊代碼 begin
                lineWrapping: true,
                foldGutter: true,
                gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
                // 折疊代碼 end
                extraKeys: {
                    // 代碼提示
                    "'<'": completeAfter,
                    "'/'": completeIfAfterLt,
                    "' '": completeIfInTag,
                    "'='": completeIfInTag,
                    // 注釋功能
                    "Ctrl-/": (cm) => {
                    cm.toggleComment()
                    },
                    // 保存功能
                    "Ctrl-S": (cm) => {
                    onSave()
                    },
                    // 格式化
                    "Shift-Alt-F": (cm) => {
                    const totalLines = cm.lineCount();
                    cm.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines })
                    },
                    // Tab自動(dòng)轉(zhuǎn)換為空格
                    "Tab": (cm) => {
                    if (cm.somethingSelected()) {// 選中后整體縮進(jìn)的情況
                        cm.indentSelection('add')
                    } else {
                        cm.replaceSelection(Array(cm.getOption("indentUnit") + 1).join(" "), "end", "+input")
                    }
                    }
                },
                // 代碼提示
                hintOptions: { schemaInfo: tags, matchInMiddle: true },
                lint: true
                }}
                editorDidMount={onGetEditor}
                onBeforeChange={onChange}
            />
            </div>
        )
        }
        
        export default XmlEditor

        學(xué)習(xí)XML,并提取出tags規(guī)則

        當(dāng)我們使用CodeMirror做一個(gè)簡(jiǎn)單的編輯器時(shí),想要進(jìn)行一個(gè)XML的代碼提示,是需要使用到tags。

        很明顯,不同的游戲有不同的XML規(guī)則,包括游戲更新之后XML規(guī)則也會(huì)更改。

        所以我們必須要保證有一個(gè)機(jī)制去不斷地學(xué)習(xí)這些XML規(guī)則,所以這里我做了一個(gè)學(xué)習(xí)XML文件規(guī)則的彈窗去做這個(gè)事情。

        點(diǎn)擊編輯器左上方的 約束規(guī)則——>新增約束規(guī)則

        js制作xml在線編輯器代碼分享

        會(huì)彈出這樣一個(gè)彈窗:

        js制作xml在線編輯器代碼分享

        通過(guò)FileReader讀取指定文件夾的XML文件,然后使用xmldom來(lái)依次解析這些xml文件的文本,生成文檔對(duì)象。

        再分析這些文檔對(duì)象得到最終的tags規(guī)則。

        這一步驟只需要對(duì)xml有所了解,其實(shí)也蠻基礎(chǔ)的,所以不講了。

        總之現(xiàn)在我們完成了它的最初形態(tài),你每次使用它需要將你編輯的XML文件內(nèi)容復(fù)制到這個(gè)在線編輯器,編輯完后,再將完成的文本復(fù)制到原XML文件保存覆蓋。

        進(jìn)化形態(tài):加載樹(shù)形文件結(jié)構(gòu)和全文件校驗(yàn)功能的在線XML編輯器

        上面的編輯器其實(shí)使用場(chǎng)景非常窄,只能在新寫(xiě)一個(gè)XML時(shí)使用。

        一個(gè)MOD往往幾十上百,甚至幾千個(gè)文件,不可能一個(gè)個(gè)粘貼到編輯器中進(jìn)行校驗(yàn)。

        所以我們需要在這個(gè)編輯器中,加載MOD的所有XML文件,并進(jìn)行一個(gè)代碼校驗(yàn)。

        涉及到的技術(shù)就兩個(gè):

        • FileReader

        • Web Worker

        左側(cè)文件樹(shù)

        左側(cè)這個(gè)文件樹(shù)使用Ant Design的Tree組件完成,這里配置什么的就不講了。

        在點(diǎn)擊打開(kāi)文件夾這個(gè)按鈕時(shí)

        js制作xml在線編輯器代碼分享

        同樣使用FileReader來(lái)讀取MOD文件夾中的文件。

        但是FileReader獲取到的是一個(gè)文件數(shù)組,要想生成我們左側(cè)的樹(shù)形結(jié)構(gòu)需要自己手動(dòng)解析每個(gè)XML文件的路徑,并據(jù)此生成一個(gè)樹(shù)形結(jié)構(gòu)。

        全文件校驗(yàn)功能

        在打開(kāi)文件夾的一瞬間,我們需要對(duì)全部的XML文件進(jìn)行一次代碼校驗(yàn),如果校驗(yàn)有誤,需要在左側(cè)文件夾上將相關(guān)的文件及它父級(jí)祖級(jí)的一系列文件夾全部標(biāo)紅。

        這個(gè)功能表面上很簡(jiǎn)單,其實(shí)坑點(diǎn)很大,因?yàn)樾r?yàn)的計(jì)算量實(shí)際上并不小,特別是你的MOD中有幾百幾千個(gè)文件的時(shí)候,非常容易搞得你js阻塞,頁(yè)面無(wú)響應(yīng)。

        在這里我使用了Web Worker新開(kāi)一個(gè)線程去處理這個(gè)校驗(yàn)過(guò)程,在校驗(yàn)完成后將結(jié)果返回給我。

        在這個(gè)過(guò)程中,我對(duì)Web Worker的使用也有了更多的了解。

        印象中一直以為是一個(gè)new Worker(某js文件)這樣的方式去玩,感覺(jué)很難結(jié)合react的模塊化開(kāi)發(fā)來(lái)使用。

        但是實(shí)際上現(xiàn)在在webpack里配置上worker-loader,可以很方便使用Web Worker。

        首先我們的worker代碼可以寫(xiě)成下面這樣:

        import { lintFileTree } from '@/utils/files'
        
        onmessage = ({ data }) => {
        lintFileTree(data.fileTree, data.currentTags).then(content => {
            postMessage(content)
        })
        }

        然后我們使用這個(gè)Worker時(shí),可以如下所示

        import { useWebWorkerFromWorker } from 'react-webworker-hook'
        import lintFileTreeWorker from '@/utils/webWorker/lintFileTree.webworker'
        
        const worker4LintFileTree = new lintFileTreeWorker()
        
        const [lintedFileTree, startLintFileTree] = useWebWorkerFromWorker(worker4LintFileTree)

        然后你再用個(gè)useEffect依賴(lài)這個(gè)lintedFileTree,如果變動(dòng)了就做某些操作,所以寫(xiě)起來(lái)就像用useState一樣輕松。

        非遞歸遍歷樹(shù)

        大家可以看到上面我們用到的這些東西,很多都與樹(shù)相關(guān),比如遍歷文件樹(shù)去校驗(yàn)代碼。

        又或者我們切換了某個(gè)約束規(guī)則后,也是需要遍歷整個(gè)文件樹(shù)進(jìn)行重新校驗(yàn)的。

        遍歷的過(guò)程中,之前我用的是遞歸遍歷整個(gè)樹(shù),這樣做不好的地方在于遞歸的時(shí)候內(nèi)存得不到釋放,所以后來(lái)我換了一種算法,采用非遞歸的方式遍歷整個(gè)樹(shù)。

        IndexDB保存文件內(nèi)容

        因?yàn)槲覀兊腗OD文件內(nèi)容比較多比較大,所以?xún)?nèi)存占用可能會(huì)很大,不可能一直把這些文件內(nèi)容放到內(nèi)存中。

        所以我讀取到文件內(nèi)容會(huì)依次放入IndexDB中,只展示當(dāng)前編輯文件的內(nèi)容。

        只有在需要的時(shí)候,比如全文件校驗(yàn)或者切換文件時(shí),才從IndexDB再次獲取文件內(nèi)容。

        究極進(jìn)化形態(tài):突破瀏覽器沙盒限制,實(shí)現(xiàn)對(duì)電腦本地文件的增刪改

        通過(guò)之前的操作,我們終于完成了一個(gè)基本可用的在線XML編輯器。

        但是它有一個(gè)致命缺點(diǎn),就是受到瀏覽器沙盒環(huán)境的限制,我們?cè)谛薷牧宋募?,沒(méi)法直接保存到電腦上,而必須依靠手動(dòng)將修改好的代碼一一復(fù)制到對(duì)應(yīng)的文件中。

        這個(gè)操作繁瑣復(fù)雜,導(dǎo)致我們編輯器的功能可能只能用來(lái)輔助編寫(xiě)代碼和批量校驗(yàn)。

        之前我以為只能做到這種程度,但是后來(lái)我在知乎上偶然看了一個(gè)帖子,發(fā)現(xiàn)Chrome86+的版本多了一個(gè)功能API:FileSystemAccess。

        另外,除非是本地localhost環(huán)境,否則這個(gè)API只在https環(huán)境下才能調(diào)用,也就是說(shuō)你在一個(gè)http的網(wǎng)站上,即使你用的是Chrome86+或者是Edge86+,那也是調(diào)用不了的。

        這個(gè)API可以讓我們直接操作本地電腦上的文件,而不是像FileReader一樣只能讀,或者像FileSystem一樣只能在瀏覽器沙盒內(nèi)操作。

        通過(guò)FileSystemAccess我們不僅可以實(shí)現(xiàn)對(duì)文件夾中的文件進(jìn)行讀取修改,還能新增和刪除文件。

        所以我使用這個(gè)API全面替換了之前使用FileReader的各個(gè)點(diǎn),實(shí)現(xiàn)了在文件樹(shù)上右鍵進(jìn)行文件夾和文件的新增和刪除。(這里是不支持對(duì)文件進(jìn)行重命名的,不過(guò)其實(shí)我們可以使用刪除后再新增的方式來(lái)模擬重命名,但是我就懶得做了)

        同時(shí)在按保存按鈕或者按保存的快捷鍵Ctrl+S后,就可以直接對(duì)文件進(jìn)行保存操作。

        下面是一個(gè)使用FileSystemAccess打開(kāi)文件夾的組件代碼:

        import React from 'react'
        
            // 自定義的打開(kāi)文件夾組件
            const FileInput = (props) => {
            const { children, onChange } = props
            const handleClick = async () => {
                const dirHandle = await window.showDirectoryPicker()
                dirHandle.requestPermission({ mode : "readwrite" })
                onChange(dirHandle)
            }
            return <span onClick={handleClick}>
                {children}
            </span>
            }
        
            export default FileInput

        只要被這個(gè)組件包裹的元素(比如按鈕)被點(diǎn)擊后,會(huì)立即調(diào)用showDirectoryPicker,請(qǐng)求打開(kāi)文件夾。

        在打開(kāi)文件夾后,通過(guò)獲得的文件夾handle去請(qǐng)求文件夾寫(xiě)入權(quán)限,然后再把這個(gè)文件夾handle傳到外部,獲取文件樹(shù)結(jié)構(gòu)。

        這里的操作是有瑕疵的,因?yàn)檎?qǐng)求打開(kāi)文件夾時(shí)瀏覽器會(huì)彈框向用戶(hù)獲取讀取文件夾的權(quán)限,

        js制作xml在線編輯器代碼分享

        打開(kāi)完畢后又直接會(huì)彈第二次框獲取寫(xiě)入權(quán)限,也就是說(shuō)在打開(kāi)文件夾時(shí)會(huì)彈兩次框。

        但是我也只能通過(guò)這種手法一次性請(qǐng)求到所有的權(quán)限,要不然等到要保存時(shí)再去請(qǐng)求權(quán)限也不太好。

        不過(guò)瑕不掩瑜,通過(guò)這個(gè)API不僅實(shí)現(xiàn)了文件的增刪改,還解除了對(duì)IndexDB的使用。

        因?yàn)槲覀冸S時(shí)可以通過(guò)文件Handle獲取到相應(yīng)的文件內(nèi)容,所以沒(méi)必要將文件內(nèi)容保存到IndexDB中。

        感謝各位的閱讀,以上就是“js制作xml在線編輯器代碼分享”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)js制作xml在線編輯器代碼分享這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

        向AI問(wèn)一下細(xì)節(jié)

        免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

        js
        AI