{"id":369,"date":"2026-01-26T01:40:39","date_gmt":"2026-01-25T17:40:39","guid":{"rendered":"https:\/\/skwp.zeabur.app\/2026\/01\/26\/%e3%80%90electron-react-%e6%95%99%e5%ad%b8%e3%80%9103-%e6%aa%94%e6%a1%88%e7%b3%bb%e7%b5%b1%e6%93%8d%e4%bd%9c%ef%bc%9a%e8%ae%80%e5%af%ab%e6%9c%ac%e5%9c%b0%e6%aa%94%e6%a1%88\/"},"modified":"2026-01-26T01:40:39","modified_gmt":"2026-01-25T17:40:39","slug":"%e3%80%90electron-react-%e6%95%99%e5%ad%b8%e3%80%9103-%e6%aa%94%e6%a1%88%e7%b3%bb%e7%b5%b1%e6%93%8d%e4%bd%9c%ef%bc%9a%e8%ae%80%e5%af%ab%e6%9c%ac%e5%9c%b0%e6%aa%94%e6%a1%88","status":"publish","type":"post","link":"https:\/\/blog.itsmygo.uk\/wordpress\/2026\/01\/26\/%e3%80%90electron-react-%e6%95%99%e5%ad%b8%e3%80%9103-%e6%aa%94%e6%a1%88%e7%b3%bb%e7%b5%b1%e6%93%8d%e4%bd%9c%ef%bc%9a%e8%ae%80%e5%af%ab%e6%9c%ac%e5%9c%b0%e6%aa%94%e6%a1%88\/","title":{"rendered":"\u3010Electron + React \u6559\u5b78\u3011#03 \u6a94\u6848\u7cfb\u7d71\u64cd\u4f5c\uff1a\u8b80\u5beb\u672c\u5730\u6a94\u6848"},"content":{"rendered":"\n<div id=\"quiz-basic\">\n<style>\n#quiz-basic .question {\n        background: white;\n        border-radius: 8px;\n        padding: 20px;\n        margin: 20px 0;\n        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n      }#quiz-basic .question h3 {\n        margin-top: 0;\n        color: #333;\n      }#quiz-basic .options {\n        list-style: none;\n        padding: 0;\n      }#quiz-basic .options li {\n        padding: 12px 15px;\n        margin: 8px 0;\n        border: 2px solid #ddd;\n        border-radius: 6px;\n        cursor: pointer;\n        transition: all 0.2s;\n      }#quiz-basic .options li:hover:not(.disabled) {\n        border-color: #4caf50;\n        background: #f0fff0;\n      }#quiz-basic .options li.correct {\n        border-color: #4caf50;\n        background: #e8f5e9;\n      }#quiz-basic .options li.wrong {\n        border-color: #f44336;\n        background: #ffebee;\n      }#quiz-basic .options li.disabled {\n        cursor: default;\n        opacity: 0.7;\n      }#quiz-basic .options li.show-answer {\n        border-color: #4caf50;\n        background: #e8f5e9;\n      }#quiz-basic .feedback {\n        margin-top: 15px;\n        padding: 12px;\n        border-radius: 6px;\n        display: none;\n      }#quiz-basic .feedback.correct {\n        background: #e8f5e9;\n        color: #2e7d32;\n        display: block;\n      }#quiz-basic .feedback.wrong {\n        background: #ffebee;\n        color: #c62828;\n        display: block;\n      }#quiz-basic .score {\n        text-align: center;\n        font-size: 1.5em;\n        padding: 20px;\n        background: white;\n        border-radius: 8px;\n        margin-top: 20px;\n      }#quiz-basic .code-block {\n        background: #1e1e1e;\n        color: #d4d4d4;\n        padding: 14px;\n        border-radius: 6px;\n        font-family: \"Monaco\", \"Consolas\", \"Courier New\", monospace;\n        font-size: 0.9em;\n        overflow-x: auto;\n        margin: 12px 0;\n        white-space: pre-wrap;\n      }\n<\/style>\n<h1>\u6e2c\u9a57\uff1aElectron + React \u6a94\u6848\u7cfb\u7d71\u64cd\u4f5c<\/h1>\n    <p>\u5171 5 \u984c\uff0c\u9ede\u9078\u7b54\u6848\u5f8c\u6703\u7acb\u5373\u986f\u793a\u7d50\u679c<\/p>\n\n    <div class=\"question\" id=\"q1\">\n      <h3>1. \u5728 Electron \u61c9\u7528\u4e2d\uff0c\u70ba\u4ec0\u9ebc\u6a94\u6848\u64cd\u4f5c\u5fc5\u9808\u5728\u4e3b\u7a0b\u5e8f\u4e2d\u57f7\u884c\uff1f<\/h3>\n      <ul class=\"options\">\n        <li onclick=\"checkAnswer(1, 'A', 'B')\">A. \u4e3b\u7a0b\u5e8f\u7684\u57f7\u884c\u901f\u5ea6\u6bd4\u6e32\u67d3\u7a0b\u5e8f\u5feb<\/li>\n        <li onclick=\"checkAnswer(1, 'B', 'B')\">B. \u6e32\u67d3\u7a0b\u5e8f\u53d7\u9650\u65bc\u5b89\u5168\u6027\uff0c\u7121\u6cd5\u76f4\u63a5\u5b58\u53d6\u6a94\u6848\u7cfb\u7d71<\/li>\n        <li onclick=\"checkAnswer(1, 'C', 'B')\">C. \u6e32\u67d3\u7a0b\u5e8f\u4e0d\u652f\u63f4 JavaScript<\/li>\n        <li onclick=\"checkAnswer(1, 'D', 'B')\">D. Node.js \u6a21\u7d44\u53ea\u80fd\u5728\u700f\u89bd\u5668\u4e2d\u57f7\u884c<\/li>\n      <\/ul>\n      <div class=\"feedback\" id=\"fb1\"><\/div>\n    <\/div>\n\n    <div class=\"question\" id=\"q2\">\n      <h3>2. <code>dialog.showOpenDialog()<\/code> \u56de\u50b3\u7684\u7269\u4ef6\u4e2d\uff0c<code>filePaths<\/code> \u662f\u4ec0\u9ebc\u985e\u578b\uff1f<\/h3>\n      <ul class=\"options\">\n        <li onclick=\"checkAnswer(2, 'A', 'C')\">A. \u5b57\u4e32\uff08\u55ae\u4e00\u6a94\u6848\u8def\u5f91\uff09<\/li>\n        <li onclick=\"checkAnswer(2, 'B', 'C')\">B. \u5e03\u6797\u503c\uff08\u662f\u5426\u9078\u4e2d\u6a94\u6848\uff09<\/li>\n        <li onclick=\"checkAnswer(2, 'C', 'C')\">C. \u9663\u5217\uff08\u9078\u4e2d\u7684\u6a94\u6848\u8def\u5f91\u6e05\u55ae\uff09<\/li>\n        <li onclick=\"checkAnswer(2, 'D', 'C')\">D. \u7269\u4ef6\uff08\u5305\u542b\u6a94\u6848\u540d\u7a31\u8207\u5167\u5bb9\uff09<\/li>\n      <\/ul>\n      <div class=\"feedback\" id=\"fb2\"><\/div>\n    <\/div>\n\n    <div class=\"question\" id=\"q3\">\n      <h3>3. \u4f7f\u7528 <code>fs.readFileSync(filePath, 'utf-8')<\/code> \u6642\uff0c\u6307\u5b9a <code>'utf-8'<\/code> \u7de8\u78bc\u7684\u4f5c\u7528\u662f\u4ec0\u9ebc\uff1f<\/h3>\n      <ul class=\"options\">\n        <li onclick=\"checkAnswer(3, 'A', 'B')\">A. \u8b93\u6a94\u6848\u53ef\u4ee5\u88ab\u58d3\u7e2e\u5132\u5b58<\/li>\n        <li onclick=\"checkAnswer(3, 'B', 'B')\">B. \u8b93\u56de\u50b3\u503c\u662f\u5b57\u4e32\u800c\u975e Buffer<\/li>\n        <li onclick=\"checkAnswer(3, 'C', 'B')\">C. \u8b93\u6a94\u6848\u53ef\u4ee5\u88ab\u52a0\u5bc6<\/li>\n        <li onclick=\"checkAnswer(3, 'D', 'B')\">D. \u8b93\u6a94\u6848\u53ea\u80fd\u8b80\u53d6\u4e0d\u80fd\u5beb\u5165<\/li>\n      <\/ul>\n      <div class=\"feedback\" id=\"fb3\"><\/div>\n    <\/div>\n\n    <div class=\"question\" id=\"q4\">\n      <h3>4. \u5728 preload.js \u4e2d\u4f7f\u7528 <code>contextBridge.exposeInMainWorld()<\/code> \u7684\u76ee\u7684\u662f\u4ec0\u9ebc\uff1f<\/h3>\n      <ul class=\"options\">\n        <li onclick=\"checkAnswer(4, 'A', 'D')\">A. \u76f4\u63a5\u8b93\u6e32\u67d3\u7a0b\u5e8f\u5b58\u53d6 Node.js \u6a21\u7d44<\/li>\n        <li onclick=\"checkAnswer(4, 'B', 'D')\">B. \u95dc\u9589 Electron \u7684\u5b89\u5168\u9650\u5236<\/li>\n        <li onclick=\"checkAnswer(4, 'C', 'D')\">C. \u8b93\u4e3b\u7a0b\u5e8f\u53ef\u4ee5\u76f4\u63a5\u64cd\u4f5c DOM<\/li>\n        <li onclick=\"checkAnswer(4, 'D', 'D')\">D. \u5b89\u5168\u5730\u5c07 API \u66b4\u9732\u7d66\u6e32\u67d3\u7a0b\u5e8f\u4f7f\u7528<\/li>\n      <\/ul>\n      <div class=\"feedback\" id=\"fb4\"><\/div>\n    <\/div>\n\n    <div class=\"question\" id=\"q5\">\n      <h3>5. \u5728 React \u5143\u4ef6\u4e2d\uff0c\u7576\u4f7f\u7528\u8005\u4fee\u6539\u6587\u5b57\u4f46\u5c1a\u672a\u5132\u5b58\u6642\uff0c\u5982\u4f55\u8ffd\u8e64\u9019\u500b\u300c\u5df2\u4fee\u6539\u4f46\u672a\u5132\u5b58\u300d\u7684\u72c0\u614b\uff1f<\/h3>\n      <div class=\"code-block\">const [isDirty, setIsDirty] = useState(false);\n\nconst handleContentChange = (e) => {\n  setContent(e.target.value);\n  setIsDirty(true);\n};<\/div>\n      <ul class=\"options\">\n        <li onclick=\"checkAnswer(5, 'A', 'A')\">A. \u4f7f\u7528 isDirty state\uff0c\u5728\u5167\u5bb9\u8b8a\u66f4\u6642\u8a2d\u70ba true\uff0c\u5132\u5b58\u6210\u529f\u5f8c\u8a2d\u70ba false<\/li>\n        <li onclick=\"checkAnswer(5, 'B', 'A')\">B. \u76f4\u63a5\u6bd4\u8f03 currentFile \u662f\u5426\u70ba null<\/li>\n        <li onclick=\"checkAnswer(5, 'C', 'A')\">C. \u6aa2\u67e5 window.electronAPI \u662f\u5426\u5b58\u5728<\/li>\n        <li onclick=\"checkAnswer(5, 'D', 'A')\">D. \u4f7f\u7528 localStorage \u8a18\u9304\u4fee\u6539\u6642\u9593<\/li>\n      <\/ul>\n      <div class=\"feedback\" id=\"fb5\"><\/div>\n    <\/div>\n\n    <div class=\"score\" id=\"score\" style=\"display:none;\"><\/div>\n<script>\n\n      let answered = 0;\n      let correct = 0;\n      const total = 5;\n      const explanations = {\n        1: \"\u6e32\u67d3\u7a0b\u5e8f\u57f7\u884c\u7684\u662f\u7db2\u9801\u5167\u5bb9\uff0c\u51fa\u65bc\u5b89\u5168\u8003\u91cf\uff0c\u5b83\u7121\u6cd5\u76f4\u63a5\u5b58\u53d6\u6a94\u6848\u7cfb\u7d71\u3002\u5982\u679c\u8b93\u6e32\u67d3\u7a0b\u5e8f\u76f4\u63a5\u5b58\u53d6\uff0c\u60e1\u610f\u7db2\u9801\u5c31\u53ef\u80fd\u8b80\u53d6\u6216\u4fee\u6539\u4f7f\u7528\u8005\u7684\u6a94\u6848\u3002\u900f\u904e IPC \u548c preload \u7684\u8a2d\u8a08\uff0c\u53ef\u4ee5\u7cbe\u78ba\u63a7\u5236\u54ea\u4e9b\u6a94\u6848\u64cd\u4f5c\u662f\u88ab\u5141\u8a31\u7684\u3002\",\n        2: \"<code>dialog.showOpenDialog()<\/code> \u56de\u50b3\u7684\u7269\u4ef6\u5305\u542b <code>canceled<\/code>\uff08\u5e03\u6797\u503c\uff09\u548c <code>filePaths<\/code>\uff08\u9663\u5217\uff09\u3002\u5373\u4f7f\u53ea\u9078\u64c7\u4e00\u500b\u6a94\u6848\uff0c<code>filePaths<\/code> \u4e5f\u662f\u9663\u5217\u683c\u5f0f\uff0c\u9700\u8981\u7528 <code>filePaths[0]<\/code> \u53d6\u5f97\u7b2c\u4e00\u500b\u6a94\u6848\u8def\u5f91\u3002\",\n        3: \"\u5728 <code>fs.readFileSync()<\/code> \u4e2d\u6307\u5b9a <code>'utf-8'<\/code> \u7de8\u78bc\uff0c\u6703\u8b93\u56de\u50b3\u503c\u76f4\u63a5\u662f\u5b57\u4e32\u683c\u5f0f\u3002\u5982\u679c\u4e0d\u6307\u5b9a\u7de8\u78bc\uff0c\u56de\u50b3\u7684\u6703\u662f Buffer \u7269\u4ef6\uff0c\u9700\u8981\u518d\u8f49\u63db\u624d\u80fd\u7576\u4f5c\u6587\u5b57\u4f7f\u7528\u3002\",\n        4: \"<code>contextBridge.exposeInMainWorld()<\/code> \u662f Electron \u63d0\u4f9b\u7684\u5b89\u5168\u6a5f\u5236\uff0c\u5b83\u5728\u4fdd\u6301 <code>contextIsolation: true<\/code> \u7684\u60c5\u6cc1\u4e0b\uff0c\u8b93\u958b\u767c\u8005\u53ef\u4ee5\u9078\u64c7\u6027\u5730\u5c07\u7279\u5b9a API \u66b4\u9732\u7d66\u6e32\u67d3\u7a0b\u5e8f\u3002\u9019\u6a23\u65e2\u7dad\u6301\u4e86\u5b89\u5168\u6027\uff0c\u53c8\u80fd\u8b93 React \u4ecb\u9762\u4f7f\u7528\u4e3b\u7a0b\u5e8f\u7684\u529f\u80fd\u3002\",\n        5: \"\u7bc4\u4f8b\u7a0b\u5f0f\u78bc\u4e2d\u4f7f\u7528 <code>isDirty<\/code> state \u4f86\u8ffd\u8e64\u300c\u5df2\u4fee\u6539\u4f46\u672a\u5132\u5b58\u300d\u7684\u72c0\u614b\u3002\u7576\u5167\u5bb9\u8b8a\u66f4\u6642\u8a2d\u70ba <code>true<\/code>\uff0c\u5728\u5132\u5b58\u6210\u529f\u5f8c\u8a2d\u56de <code>false<\/code>\u3002\u9019\u500b\u72c0\u614b\u53ef\u4ee5\u7528\u4f86\u5728\u6a19\u984c\u5217\u986f\u793a * \u865f\u63d0\u793a\u4f7f\u7528\u8005\u6709\u672a\u5132\u5b58\u7684\u8b8a\u66f4\u3002\"\n      };\n\n      function checkAnswer(qNum, selected, correctAns) {\n        const question = document.getElementById(\"q\" + qNum);\n        if (question.classList.contains(\"answered\")) return;\n\n        question.classList.add(\"answered\");\n        answered++;\n\n        const options = question.querySelectorAll(\".options li\");\n        options.forEach((opt) => {\n          opt.classList.add(\"disabled\");\n          if (opt.textContent.startsWith(correctAns + \".\")) {\n            opt.classList.add(\"show-answer\");\n          }\n        });\n\n        const feedback = document.getElementById(\"fb\" + qNum);\n        if (selected === correctAns) {\n          correct++;\n          feedback.className = \"feedback correct\";\n          feedback.innerHTML = \"\u2713 \u6b63\u78ba\uff01\" + explanations[qNum];\n        } else {\n          feedback.className = \"feedback wrong\";\n          feedback.innerHTML =\n            \"\u2717 \u932f\u8aa4\u3002\u6b63\u78ba\u7b54\u6848\u662f \" + correctAns + \"\u3002\" + explanations[qNum];\n          options.forEach((opt) => {\n            if (opt.textContent.startsWith(selected + \".\")) {\n              opt.classList.add(\"wrong\");\n            }\n          });\n        }\n\n        if (answered === total) {\n          const scoreDiv = document.getElementById(\"score\");\n          scoreDiv.style.display = \"block\";\n          scoreDiv.innerHTML =\n            \"\u6e2c\u9a57\u5b8c\u6210\uff01\u5f97\u5206\uff1a\" +\n            correct +\n            \" \/ \" +\n            total +\n            (correct === total\n              ? \" \ud83c\udf89 \u5168\u5c0d\uff01\"\n              : correct >= Math.ceil(total * 0.6)\n                ? \" \ud83d\udc4d \u4e0d\u932f\uff01\"\n                : \" \ud83d\udcda \u518d\u8907\u7fd2\u4e00\u4e0b\u5427\");\n        }\n      }\n    \n<\/script>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">\u524d\u8a00<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u684c\u9762\u61c9\u7528\u7a0b\u5f0f\u76f8\u8f03\u65bc\u7db2\u9801\u61c9\u7528\uff0c\u6700\u5927\u7684\u512a\u52e2\u4e4b\u4e00\u5c31\u662f\u80fd\u5920\u76f4\u63a5\u5b58\u53d6\u4f7f\u7528\u8005\u7684\u672c\u5730\u6a94\u6848\u7cfb\u7d71\u3002\u5728 Electron \u4e2d\uff0c\u6211\u5011\u53ef\u4ee5\u900f\u904e Node.js \u7684 <code>fs<\/code> \u6a21\u7d44\u642d\u914d Electron \u7684 <code>dialog<\/code> API\uff0c\u5be6\u4f5c\u5b8c\u6574\u7684\u6a94\u6848\u8b80\u5beb\u529f\u80fd\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u7bc7\u5c07\u5ef6\u7e8c\u524d\u4e00\u7bc7\u7684 IPC \u901a\u8a0a\u6982\u5ff5\uff0c\u5e36\u4f60\u5be6\u4f5c\u4e00\u500b\u7c21\u6613\u6587\u5b57\u7de8\u8f2f\u5668\uff0c\u8b93 React \u4ecb\u9762\u80fd\u5920\u958b\u555f\u3001\u7de8\u8f2f\u4e26\u5132\u5b58\u672c\u5730\u6a94\u6848\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b78\u7fd2\u76ee\u6a19<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u8b80\u5b8c\u672c\u7bc7\u5f8c\uff0c\u4f60\u5c07\u80fd\u5920\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4f7f\u7528 Node.js fs \u6a21\u7d44\u5728\u4e3b\u7a0b\u5e8f\u4e2d\u64cd\u4f5c\u6a94\u6848<\/li>\n<li>\u5be6\u4f5c\u6a94\u6848\u9078\u64c7\u5c0d\u8a71\u6846\uff08dialog.showOpenDialog\uff09<\/li>\n<li>\u5efa\u7acb\u5b8c\u6574\u7684\u6a94\u6848\u8b80\u5beb\u529f\u80fd\u4f9b React \u4ecb\u9762\u4f7f\u7528<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u684c\u9762\u61c9\u7528\u7684\u6a94\u6848\u5b58\u53d6\u512a\u52e2<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u7db2\u9801\u61c9\u7528\u53d7\u9650\u65bc\u700f\u89bd\u5668\u6c99\u76d2\uff0c\u7121\u6cd5\u76f4\u63a5\u8b80\u5beb\u4f7f\u7528\u8005\u7684\u6a94\u6848\u7cfb\u7d71\u3002\u5373\u4f7f\u4f7f\u7528 File API\uff0c\u4e5f\u9700\u8981\u4f7f\u7528\u8005\u4e3b\u52d5\u9078\u64c7\u6a94\u6848\uff0c\u4e14\u7121\u6cd5\u76f4\u63a5\u5132\u5b58\u5230\u539f\u672c\u7684\u4f4d\u7f6e\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Electron \u61c9\u7528\u5247\u4e0d\u540c\uff1a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><span><code class=\"hljs\">\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502           Electron \u61c9\u7528\u7a0b\u5f0f                   \u2502\n\u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510   \u2502\n\u2502  \u2502   \u6e32\u67d3\u7a0b\u5e8f       \u2502  \u2502   \u4e3b\u7a0b\u5e8f         \u2502   \u2502\n\u2502  \u2502   (React)       \u2502  \u2502   (Node.js)     \u2502   \u2502\n\u2502  \u2502                 \u2502  \u2502                 \u2502   \u2502\n\u2502  \u2502  \u7121\u6cd5\u76f4\u63a5\u5b58\u53d6    \u2502  \u2502  \u2713 fs \u6a21\u7d44      \u2502   \u2502\n\u2502  \u2502  \u6a94\u6848\u7cfb\u7d71       \u2502\u25c4\u2500\u253c\u2500\u25ba \u2713 dialog API   \u2502   \u2502\n\u2502  \u2502                 \u2502  \u2502  \u2713 \u5b8c\u6574\u6b0a\u9650      \u2502   \u2502\n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518   \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                                 \u2502\n                                 \u25bc\n                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                    \u2502      \u672c\u5730\u6a94\u6848\u7cfb\u7d71        \u2502\n                    \u2502  \/Users\/xxx\/Documents\/  \u2502\n                    \u2502  C:\\Users\\xxx\\Desktop\\  \u2502\n                    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/span><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u91cd\u9ede\u662f\uff1a\u6a94\u6848\u64cd\u4f5c\u5fc5\u9808\u5728\u4e3b\u7a0b\u5e8f\u4e2d\u57f7\u884c\uff0c\u518d\u900f\u904e IPC \u5c07\u7d50\u679c\u50b3\u7d66\u6e32\u67d3\u7a0b\u5e8f\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Electron Dialog API<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Electron \u63d0\u4f9b\u4e86\u539f\u751f\u7684\u6a94\u6848\u5c0d\u8a71\u6846 API\uff0c\u8b93\u4f7f\u7528\u8005\u53ef\u4ee5\u9078\u64c7\u8981\u958b\u555f\u6216\u5132\u5b58\u7684\u6a94\u6848\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u958b\u555f\u6a94\u6848\u5c0d\u8a71\u6846<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ main.js\uff08\u4e3b\u7a0b\u5e8f\uff09<\/span>\n<span class=\"hljs-keyword\">const<\/span> { dialog } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'electron'<\/span>);\n\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">openFileDialog<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> dialog.showOpenDialog({\n    <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">'\u9078\u64c7\u6a94\u6848'<\/span>,\n    <span class=\"hljs-attr\">properties<\/span>: &#91;<span class=\"hljs-string\">'openFile'<\/span>],\n    <span class=\"hljs-attr\">filters<\/span>: &#91;\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6587\u5b57\u6a94'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'txt'<\/span>, <span class=\"hljs-string\">'md'<\/span>] },\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6240\u6709\u6a94\u6848'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'*'<\/span>] }\n    ]\n  });\n\n  <span class=\"hljs-keyword\">return<\/span> result;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u8b93\u6211\u5011\u9010\u884c\u89e3\u8b80\u9019\u6bb5\u7a0b\u5f0f\u78bc\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>\u7a0b\u5f0f\u78bc<\/th>\n<th>\u8aaa\u660e<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>dialog.showOpenDialog()<\/code><\/td>\n<td>\u986f\u793a\u539f\u751f\u7684\u300c\u958b\u555f\u6a94\u6848\u300d\u5c0d\u8a71\u6846<\/td>\n<\/tr>\n<tr>\n<td><code>title<\/code><\/td>\n<td>\u5c0d\u8a71\u6846\u7684\u6a19\u984c\u6587\u5b57<\/td>\n<\/tr>\n<tr>\n<td><code>properties: ['openFile']<\/code><\/td>\n<td>\u8a2d\u5b9a\u70ba\u9078\u64c7\u6a94\u6848\u6a21\u5f0f\uff08\u4e5f\u53ef\u4ee5\u662f <code>'openDirectory'<\/code>\uff09<\/td>\n<\/tr>\n<tr>\n<td><code>filters<\/code><\/td>\n<td>\u6a94\u6848\u985e\u578b\u7be9\u9078\u5668\uff0c\u9650\u5236\u53ef\u9078\u64c7\u7684\u526f\u6a94\u540d<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u56de\u50b3\u503c\u7d50\u69cb\uff1a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">{\n  <span class=\"hljs-attribute\">canceled<\/span>: false,        \/\/ \u4f7f\u7528\u8005\u662f\u5426\u6309\u4e86\u53d6\u6d88\n  filePaths: &#91;<span class=\"hljs-string\">'\/path\/to\/file.txt'<\/span>]  \/\/ \u9078\u4e2d\u7684\u6a94\u6848\u8def\u5f91\u9663\u5217\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">\u5132\u5b58\u6a94\u6848\u5c0d\u8a71\u6846<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ main.js\uff08\u4e3b\u7a0b\u5e8f\uff09<\/span>\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">saveFileDialog<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> dialog.showSaveDialog({\n    <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">'\u5132\u5b58\u6a94\u6848'<\/span>,\n    <span class=\"hljs-attr\">defaultPath<\/span>: <span class=\"hljs-string\">'untitled.txt'<\/span>,\n    <span class=\"hljs-attr\">filters<\/span>: &#91;\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6587\u5b57\u6a94'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'txt'<\/span>] },\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Markdown'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'md'<\/span>] }\n    ]\n  });\n\n  <span class=\"hljs-keyword\">return<\/span> result;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>\u7a0b\u5f0f\u78bc<\/th>\n<th>\u8aaa\u660e<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>dialog.showSaveDialog()<\/code><\/td>\n<td>\u986f\u793a\u539f\u751f\u7684\u300c\u5132\u5b58\u6a94\u6848\u300d\u5c0d\u8a71\u6846<\/td>\n<\/tr>\n<tr>\n<td><code>defaultPath<\/code><\/td>\n<td>\u9810\u8a2d\u7684\u6a94\u6848\u540d\u7a31<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u56de\u50b3\u503c\u7d50\u69cb\uff1a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">{\n  <span class=\"hljs-attribute\">canceled<\/span>: false,\n  filePath: <span class=\"hljs-string\">'\/path\/to\/save\/file.txt'<\/span>  \/\/ \u6ce8\u610f\u662f\u55ae\u6578 filePath\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">\u4f7f\u7528 fs \u6a21\u7d44\u8b80\u5beb\u6a94\u6848<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Node.js \u7684 <code>fs<\/code> \u6a21\u7d44\u63d0\u4f9b\u4e86\u6a94\u6848\u7cfb\u7d71\u64cd\u4f5c\u529f\u80fd\u3002\u5728 Electron \u4e3b\u7a0b\u5e8f\u4e2d\uff0c\u6211\u5011\u53ef\u4ee5\u76f4\u63a5\u4f7f\u7528\u5b83\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8b80\u53d6\u6a94\u6848<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ main.js\uff08\u4e3b\u7a0b\u5e8f\uff09<\/span>\n<span class=\"hljs-keyword\">const<\/span> fs = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'fs'<\/span>);\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">readFile<\/span>(<span class=\"hljs-params\">filePath<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> content = fs.readFileSync(filePath, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span>, content };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>\u7a0b\u5f0f\u78bc<\/th>\n<th>\u8aaa\u660e<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>fs.readFileSync()<\/code><\/td>\n<td>\u540c\u6b65\u8b80\u53d6\u6a94\u6848\u5167\u5bb9<\/td>\n<\/tr>\n<tr>\n<td><code>'utf-8'<\/code><\/td>\n<td>\u6307\u5b9a\u7de8\u78bc\uff0c\u8b93\u56de\u50b3\u503c\u662f\u5b57\u4e32\u800c\u975e Buffer<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u5beb\u5165\u6a94\u6848<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ main.js\uff08\u4e3b\u7a0b\u5e8f\uff09<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">writeFile<\/span>(<span class=\"hljs-params\">filePath, content<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">try<\/span> {\n    fs.writeFileSync(filePath, content, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">\u900f\u904e IPC \u66b4\u9732\u6a94\u6848\u64cd\u4f5c\u7d66 React<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u73fe\u5728\u6211\u5011\u8981\u628a\u9019\u4e9b\u529f\u80fd\u4e32\u9023\u8d77\u4f86\uff0c\u8b93 React \u4ecb\u9762\u53ef\u4ee5\u4f7f\u7528\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 1\uff1a\u5728\u4e3b\u7a0b\u5e8f\u8a2d\u5b9a IPC \u8655\u7406\u5668<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ main.js<\/span>\n<span class=\"hljs-keyword\">const<\/span> { app, BrowserWindow, ipcMain, dialog } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'electron'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> fs = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'fs'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> path = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'path'<\/span>);\n\n<span class=\"hljs-comment\">\/\/ \u958b\u555f\u6a94\u6848<\/span>\nipcMain.handle(<span class=\"hljs-string\">'file:open'<\/span>, <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> dialog.showOpenDialog({\n    <span class=\"hljs-attr\">properties<\/span>: &#91;<span class=\"hljs-string\">'openFile'<\/span>],\n    <span class=\"hljs-attr\">filters<\/span>: &#91;\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6587\u5b57\u6a94'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'txt'<\/span>, <span class=\"hljs-string\">'md'<\/span>] },\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6240\u6709\u6a94\u6848'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'*'<\/span>] }\n    ]\n  });\n\n  <span class=\"hljs-keyword\">if<\/span> (result.canceled || result.filePaths.length === <span class=\"hljs-number\">0<\/span>) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  }\n\n  <span class=\"hljs-keyword\">const<\/span> filePath = result.filePaths&#91;<span class=\"hljs-number\">0<\/span>];\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> content = fs.readFileSync(filePath, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>,\n      filePath,\n      <span class=\"hljs-attr\">fileName<\/span>: path.basename(filePath),\n      content\n    };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n});\n\n<span class=\"hljs-comment\">\/\/ \u5132\u5b58\u6a94\u6848<\/span>\nipcMain.handle(<span class=\"hljs-string\">'file:save'<\/span>, <span class=\"hljs-keyword\">async<\/span> (event, { filePath, content }) =&gt; {\n  <span class=\"hljs-keyword\">try<\/span> {\n    fs.writeFileSync(filePath, content, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n});\n\n<span class=\"hljs-comment\">\/\/ \u53e6\u5b58\u65b0\u6a94<\/span>\nipcMain.handle(<span class=\"hljs-string\">'file:saveAs'<\/span>, <span class=\"hljs-keyword\">async<\/span> (event, { content, defaultName }) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> dialog.showSaveDialog({\n    <span class=\"hljs-attr\">defaultPath<\/span>: defaultName || <span class=\"hljs-string\">'untitled.txt'<\/span>,\n    <span class=\"hljs-attr\">filters<\/span>: &#91;\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6587\u5b57\u6a94'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'txt'<\/span>] },\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Markdown'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'md'<\/span>] }\n    ]\n  });\n\n  <span class=\"hljs-keyword\">if<\/span> (result.canceled) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  }\n\n  <span class=\"hljs-keyword\">try<\/span> {\n    fs.writeFileSync(result.filePath, content, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>,\n      <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span>,\n      <span class=\"hljs-attr\">filePath<\/span>: result.filePath,\n      <span class=\"hljs-attr\">fileName<\/span>: path.basename(result.filePath)\n    };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 2\uff1a\u5728 preload \u8173\u672c\u4e2d\u5efa\u7acb\u6a4b\u63a5<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ preload.js<\/span>\n<span class=\"hljs-keyword\">const<\/span> { contextBridge, ipcRenderer } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'electron'<\/span>);\n\ncontextBridge.exposeInMainWorld(<span class=\"hljs-string\">'electronAPI'<\/span>, {\n  <span class=\"hljs-comment\">\/\/ \u6a94\u6848\u64cd\u4f5c<\/span>\n  <span class=\"hljs-attr\">openFile<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> ipcRenderer.invoke(<span class=\"hljs-string\">'file:open'<\/span>),\n  <span class=\"hljs-attr\">saveFile<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">data<\/span>) =&gt;<\/span> ipcRenderer.invoke(<span class=\"hljs-string\">'file:save'<\/span>, data),\n  <span class=\"hljs-attr\">saveFileAs<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">data<\/span>) =&gt;<\/span> ipcRenderer.invoke(<span class=\"hljs-string\">'file:saveAs'<\/span>, data)\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9019\u88e1\u4f7f\u7528\u4e86\u7b2c\u4e8c\u7bc7\u5b78\u5230\u7684 <code>contextBridge<\/code>\uff0c\u5c07\u6a94\u6848\u64cd\u4f5c API \u66b4\u9732\u7d66\u6e32\u67d3\u7a0b\u5e8f\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6b65\u9a5f 3\uff1a\u5728 React \u4e2d\u4f7f\u7528<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ App.jsx<\/span>\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;content, setContent] = useState(<span class=\"hljs-string\">''<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;currentFile, setCurrentFile] = useState(<span class=\"hljs-literal\">null<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;fileName, setFileName] = useState(<span class=\"hljs-string\">'\u672a\u547d\u540d'<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;isDirty, setIsDirty] = useState(<span class=\"hljs-literal\">false<\/span>);\n\n  <span class=\"hljs-comment\">\/\/ \u958b\u555f\u6a94\u6848<\/span>\n  <span class=\"hljs-keyword\">const<\/span> handleOpen = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">window<\/span>.electronAPI.openFile();\n\n    <span class=\"hljs-keyword\">if<\/span> (!result.canceled &amp;&amp; !result.error) {\n      setContent(result.content);\n      setCurrentFile(result.filePath);\n      setFileName(result.fileName);\n      setIsDirty(<span class=\"hljs-literal\">false<\/span>);\n    } <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">if<\/span> (result.error) {\n      alert(<span class=\"hljs-string\">`\u958b\u555f\u6a94\u6848\u5931\u6557\uff1a<span class=\"hljs-subst\">${result.error}<\/span>`<\/span>);\n    }\n  };\n\n  <span class=\"hljs-comment\">\/\/ \u5132\u5b58\u6a94\u6848<\/span>\n  <span class=\"hljs-keyword\">const<\/span> handleSave = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    <span class=\"hljs-keyword\">if<\/span> (!currentFile) {\n      <span class=\"hljs-comment\">\/\/ \u5982\u679c\u9084\u6c92\u6709\u6a94\u6848\u8def\u5f91\uff0c\u6539\u7528\u53e6\u5b58\u65b0\u6a94<\/span>\n      handleSaveAs();\n      <span class=\"hljs-keyword\">return<\/span>;\n    }\n\n    <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">window<\/span>.electronAPI.saveFile({\n      <span class=\"hljs-attr\">filePath<\/span>: currentFile,\n      content\n    });\n\n    <span class=\"hljs-keyword\">if<\/span> (result.success) {\n      setIsDirty(<span class=\"hljs-literal\">false<\/span>);\n    } <span class=\"hljs-keyword\">else<\/span> {\n      alert(<span class=\"hljs-string\">`\u5132\u5b58\u5931\u6557\uff1a<span class=\"hljs-subst\">${result.error}<\/span>`<\/span>);\n    }\n  };\n\n  <span class=\"hljs-comment\">\/\/ \u53e6\u5b58\u65b0\u6a94<\/span>\n  <span class=\"hljs-keyword\">const<\/span> handleSaveAs = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">window<\/span>.electronAPI.saveFileAs({\n      content,\n      <span class=\"hljs-attr\">defaultName<\/span>: fileName\n    });\n\n    <span class=\"hljs-keyword\">if<\/span> (!result.canceled &amp;&amp; result.success) {\n      setCurrentFile(result.filePath);\n      setFileName(result.fileName);\n      setIsDirty(<span class=\"hljs-literal\">false<\/span>);\n    } <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">if<\/span> (result.error) {\n      alert(<span class=\"hljs-string\">`\u5132\u5b58\u5931\u6557\uff1a<span class=\"hljs-subst\">${result.error}<\/span>`<\/span>);\n    }\n  };\n\n  <span class=\"hljs-comment\">\/\/ \u5167\u5bb9\u8b8a\u66f4<\/span>\n  <span class=\"hljs-keyword\">const<\/span> handleContentChange = <span class=\"hljs-function\">(<span class=\"hljs-params\">e<\/span>) =&gt;<\/span> {\n    setContent(e.target.value);\n    setIsDirty(<span class=\"hljs-literal\">true<\/span>);\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"editor\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"toolbar\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handleOpen}<\/span>&gt;<\/span>\u958b\u555f<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handleSave}<\/span>&gt;<\/span>\u5132\u5b58<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handleSaveAs}<\/span>&gt;<\/span>\u53e6\u5b58\u65b0\u6a94<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"filename\"<\/span>&gt;<\/span>\n          {fileName}{isDirty ? ' *' : ''}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span>\n        <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{content}<\/span>\n        <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleContentChange}<\/span>\n        <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"\u5728\u9019\u88e1\u8f38\u5165\u5167\u5bb9...\"<\/span>\n      \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">\u5b8c\u6574\u7bc4\u4f8b\uff1a\u7c21\u6613\u6587\u5b57\u7de8\u8f2f\u5668<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u8b93\u6211\u5011\u770b\u770b\u5b8c\u6574\u7684\u6a94\u6848\u7d50\u69cb\u548c\u5404\u6a94\u6848\u7684\u5b8c\u6574\u5167\u5bb9\uff1a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">my-editor\/\n\u251c\u2500\u2500 main.js          <span class=\"hljs-comment\"># \u4e3b\u7a0b\u5e8f<\/span>\n\u251c\u2500\u2500 preload.js       <span class=\"hljs-comment\"># \u9810\u8f09\u8173\u672c<\/span>\n\u251c\u2500\u2500 src\/\n\u2502   \u251c\u2500\u2500 App.jsx      <span class=\"hljs-comment\"># React \u5143\u4ef6<\/span>\n\u2502   \u2514\u2500\u2500 App.css      <span class=\"hljs-comment\"># \u6a23\u5f0f<\/span>\n\u2514\u2500\u2500 package.json\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">main.js \u5b8c\u6574\u5167\u5bb9<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> { app, BrowserWindow, ipcMain, dialog } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'electron'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> path = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'path'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> fs = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'fs'<\/span>);\n\n<span class=\"hljs-keyword\">let<\/span> mainWindow;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createWindow<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  mainWindow = <span class=\"hljs-keyword\">new<\/span> BrowserWindow({\n    <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">800<\/span>,\n    <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">600<\/span>,\n    <span class=\"hljs-attr\">webPreferences<\/span>: {\n      <span class=\"hljs-attr\">preload<\/span>: path.join(__dirname, <span class=\"hljs-string\">'preload.js'<\/span>),\n      <span class=\"hljs-attr\">contextIsolation<\/span>: <span class=\"hljs-literal\">true<\/span>,\n      <span class=\"hljs-attr\">nodeIntegration<\/span>: <span class=\"hljs-literal\">false<\/span>\n    }\n  });\n\n  <span class=\"hljs-comment\">\/\/ \u958b\u767c\u6a21\u5f0f\u8f09\u5165 Vite \u958b\u767c\u4f3a\u670d\u5668<\/span>\n  <span class=\"hljs-keyword\">if<\/span> (process.env.NODE_ENV === <span class=\"hljs-string\">'development'<\/span>) {\n    mainWindow.loadURL(<span class=\"hljs-string\">'http:\/\/localhost:5173'<\/span>);\n    mainWindow.webContents.openDevTools();\n  } <span class=\"hljs-keyword\">else<\/span> {\n    mainWindow.loadFile(path.join(__dirname, <span class=\"hljs-string\">'dist\/index.html'<\/span>));\n  }\n}\n\n<span class=\"hljs-comment\">\/\/ IPC \u8655\u7406\u5668\uff1a\u958b\u555f\u6a94\u6848<\/span>\nipcMain.handle(<span class=\"hljs-string\">'file:open'<\/span>, <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> dialog.showOpenDialog(mainWindow, {\n    <span class=\"hljs-attr\">properties<\/span>: &#91;<span class=\"hljs-string\">'openFile'<\/span>],\n    <span class=\"hljs-attr\">filters<\/span>: &#91;\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6587\u5b57\u6a94'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'txt'<\/span>, <span class=\"hljs-string\">'md'<\/span>] },\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6240\u6709\u6a94\u6848'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'*'<\/span>] }\n    ]\n  });\n\n  <span class=\"hljs-keyword\">if<\/span> (result.canceled || result.filePaths.length === <span class=\"hljs-number\">0<\/span>) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  }\n\n  <span class=\"hljs-keyword\">const<\/span> filePath = result.filePaths&#91;<span class=\"hljs-number\">0<\/span>];\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> content = fs.readFileSync(filePath, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>,\n      filePath,\n      <span class=\"hljs-attr\">fileName<\/span>: path.basename(filePath),\n      content\n    };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n});\n\n<span class=\"hljs-comment\">\/\/ IPC \u8655\u7406\u5668\uff1a\u5132\u5b58\u6a94\u6848<\/span>\nipcMain.handle(<span class=\"hljs-string\">'file:save'<\/span>, <span class=\"hljs-keyword\">async<\/span> (event, { filePath, content }) =&gt; {\n  <span class=\"hljs-keyword\">try<\/span> {\n    fs.writeFileSync(filePath, content, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n});\n\n<span class=\"hljs-comment\">\/\/ IPC \u8655\u7406\u5668\uff1a\u53e6\u5b58\u65b0\u6a94<\/span>\nipcMain.handle(<span class=\"hljs-string\">'file:saveAs'<\/span>, <span class=\"hljs-keyword\">async<\/span> (event, { content, defaultName }) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> dialog.showSaveDialog(mainWindow, {\n    <span class=\"hljs-attr\">defaultPath<\/span>: defaultName || <span class=\"hljs-string\">'untitled.txt'<\/span>,\n    <span class=\"hljs-attr\">filters<\/span>: &#91;\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'\u6587\u5b57\u6a94'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'txt'<\/span>] },\n      { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Markdown'<\/span>, <span class=\"hljs-attr\">extensions<\/span>: &#91;<span class=\"hljs-string\">'md'<\/span>] }\n    ]\n  });\n\n  <span class=\"hljs-keyword\">if<\/span> (result.canceled) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">true<\/span> };\n  }\n\n  <span class=\"hljs-keyword\">try<\/span> {\n    fs.writeFileSync(result.filePath, content, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>,\n      <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">true<\/span>,\n      <span class=\"hljs-attr\">filePath<\/span>: result.filePath,\n      <span class=\"hljs-attr\">fileName<\/span>: path.basename(result.filePath)\n    };\n  } <span class=\"hljs-keyword\">catch<\/span> (error) {\n    <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">canceled<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">success<\/span>: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-attr\">error<\/span>: error.message };\n  }\n});\n\napp.whenReady().then(createWindow);\n\napp.on(<span class=\"hljs-string\">'window-all-closed'<\/span>, () =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (process.platform !== <span class=\"hljs-string\">'darwin'<\/span>) {\n    app.quit();\n  }\n});\n\napp.on(<span class=\"hljs-string\">'activate'<\/span>, () =&gt; {\n  <span class=\"hljs-keyword\">if<\/span> (BrowserWindow.getAllWindows().length === <span class=\"hljs-number\">0<\/span>) {\n    createWindow();\n  }\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">preload.js \u5b8c\u6574\u5167\u5bb9<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> { contextBridge, ipcRenderer } = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'electron'<\/span>);\n\ncontextBridge.exposeInMainWorld(<span class=\"hljs-string\">'electronAPI'<\/span>, {\n  <span class=\"hljs-attr\">openFile<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> ipcRenderer.invoke(<span class=\"hljs-string\">'file:open'<\/span>),\n  <span class=\"hljs-attr\">saveFile<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">data<\/span>) =&gt;<\/span> ipcRenderer.invoke(<span class=\"hljs-string\">'file:save'<\/span>, data),\n  <span class=\"hljs-attr\">saveFileAs<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">data<\/span>) =&gt;<\/span> ipcRenderer.invoke(<span class=\"hljs-string\">'file:saveAs'<\/span>, data)\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">App.css \u6a23\u5f0f<\/h3>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.editor<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: flex;\n  <span class=\"hljs-attribute\">flex-direction<\/span>: column;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100vh<\/span>;\n  <span class=\"hljs-attribute\">font-family<\/span>: -apple-system, BlinkMacSystemFont, <span class=\"hljs-string\">'Segoe UI'<\/span>, sans-serif;\n}\n\n<span class=\"hljs-selector-class\">.toolbar<\/span> {\n  <span class=\"hljs-attribute\">display<\/span>: flex;\n  <span class=\"hljs-attribute\">gap<\/span>: <span class=\"hljs-number\">8px<\/span>;\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">12px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-number\">#f5f5f5<\/span>;\n  <span class=\"hljs-attribute\">border-bottom<\/span>: <span class=\"hljs-number\">1px<\/span> solid <span class=\"hljs-number\">#ddd<\/span>;\n  <span class=\"hljs-attribute\">align-items<\/span>: center;\n}\n\n<span class=\"hljs-selector-class\">.toolbar<\/span> <span class=\"hljs-selector-tag\">button<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">8px<\/span> <span class=\"hljs-number\">16px<\/span>;\n  <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">1px<\/span> solid <span class=\"hljs-number\">#ccc<\/span>;\n  <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">4px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: white;\n  <span class=\"hljs-attribute\">cursor<\/span>: pointer;\n}\n\n<span class=\"hljs-selector-class\">.toolbar<\/span> <span class=\"hljs-selector-tag\">button<\/span><span class=\"hljs-selector-pseudo\">:hover<\/span> {\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-number\">#e9e9e9<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.filename<\/span> {\n  <span class=\"hljs-attribute\">margin-left<\/span>: auto;\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#666<\/span>;\n  <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">14px<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">textarea<\/span> {\n  <span class=\"hljs-attribute\">flex<\/span>: <span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">16px<\/span>;\n  <span class=\"hljs-attribute\">border<\/span>: none;\n  <span class=\"hljs-attribute\">resize<\/span>: none;\n  <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">14px<\/span>;\n  <span class=\"hljs-attribute\">line-height<\/span>: <span class=\"hljs-number\">1.6<\/span>;\n  <span class=\"hljs-attribute\">font-family<\/span>: <span class=\"hljs-string\">'Monaco'<\/span>, <span class=\"hljs-string\">'Menlo'<\/span>, <span class=\"hljs-string\">'Courier New'<\/span>, monospace;\n}\n\n<span class=\"hljs-selector-tag\">textarea<\/span><span class=\"hljs-selector-pseudo\">:focus<\/span> {\n  <span class=\"hljs-attribute\">outline<\/span>: none;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">\u8cc7\u6599\u6d41\u5411\u5716\u89e3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u6574\u500b\u6a94\u6848\u64cd\u4f5c\u7684\u8cc7\u6599\u6d41\u5411\u5982\u4e0b\uff1a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">\u4f7f\u7528\u8005\u9ede\u64ca\u300c\u958b\u555f\u300d\u6309\u9215\n        \u2502\n        \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  React \u5143\u4ef6 (App.jsx)               \u2502\n\u2502  handleOpen() \u88ab\u547c\u53eb                \u2502\n\u2502  <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">window<\/span>.electronAPI.openFile()\u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                  \u2502 IPC invoke\n                  \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  Preload (preload.js)               \u2502\n\u2502  ipcRenderer.invoke(<span class=\"hljs-string\">'file:open'<\/span>)    \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                  \u2502 \u8de8\u7a0b\u5e8f\u901a\u8a0a\n                  \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  \u4e3b\u7a0b\u5e8f (main.js)                   \u2502\n\u2502  ipcMain.handle(<span class=\"hljs-string\">'file:open'<\/span>, ...)   \u2502\n\u2502                                     \u2502\n\u2502  <span class=\"hljs-number\">1.<\/span> dialog.showOpenDialog()         \u2502\n\u2502     \u2192 \u986f\u793a\u539f\u751f\u6a94\u6848\u9078\u64c7\u5c0d\u8a71\u6846         \u2502\n\u2502  <span class=\"hljs-number\">2.<\/span> fs.readFileSync()               \u2502\n\u2502     \u2192 \u8b80\u53d6\u9078\u4e2d\u7684\u6a94\u6848\u5167\u5bb9             \u2502\n\u2502  <span class=\"hljs-number\">3.<\/span> <span class=\"hljs-keyword\">return<\/span> { filePath, content }    \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                  \u2502 \u56de\u50b3\u7d50\u679c\n                  \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  React \u5143\u4ef6\u63a5\u6536\u7d50\u679c                  \u2502\n\u2502  setContent(result.content)         \u2502\n\u2502  setCurrentFile(result.filePath)    \u2502\n\u2502  \u2192 \u4ecb\u9762\u66f4\u65b0\uff0c\u986f\u793a\u6a94\u6848\u5167\u5bb9            \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">\u5e38\u898b\u554f\u984c<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Q: \u70ba\u4ec0\u9ebc\u4e0d\u80fd\u5728\u6e32\u67d3\u7a0b\u5e8f\u76f4\u63a5\u4f7f\u7528 fs\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A: \u9019\u662f\u51fa\u65bc\u5b89\u5168\u8003\u91cf\u3002\u6e32\u67d3\u7a0b\u5e8f\u57f7\u884c\u7684\u662f\u7db2\u9801\u5167\u5bb9\uff0c\u5982\u679c\u8b93\u5b83\u76f4\u63a5\u5b58\u53d6\u6a94\u6848\u7cfb\u7d71\uff0c\u60e1\u610f\u7db2\u9801\u5c31\u53ef\u80fd\u8b80\u53d6\u6216\u4fee\u6539\u4f7f\u7528\u8005\u7684\u6a94\u6848\u3002\u900f\u904e IPC \u548c preload \u7684\u8a2d\u8a08\uff0c\u6211\u5011\u53ef\u4ee5\u7cbe\u78ba\u63a7\u5236\u54ea\u4e9b\u6a94\u6848\u64cd\u4f5c\u662f\u88ab\u5141\u8a31\u7684\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q: \u540c\u6b65\u548c\u975e\u540c\u6b65\u7684 fs \u65b9\u6cd5\u8a72\u9078\u54ea\u500b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A: \u5728 Electron \u4e3b\u7a0b\u5e8f\u4e2d\uff0c\u901a\u5e38\u5efa\u8b70\u4f7f\u7528\u540c\u6b65\u65b9\u6cd5\uff08\u5982 <code>readFileSync<\/code>\uff09\uff0c\u56e0\u70ba\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u7a0b\u5f0f\u78bc\u66f4\u7c21\u6f54<\/li>\n<li>\u4e3b\u7a0b\u5e8f\u4e0d\u8655\u7406 UI\uff0c\u4e0d\u6703\u9020\u6210\u4ecb\u9762\u5361\u9813<\/li>\n<li>IPC \u901a\u8a0a\u672c\u8eab\u5c31\u662f\u975e\u540c\u6b65\u7684\uff0c\u5df2\u7d93\u4e0d\u6703\u963b\u585e\u6e32\u67d3\u7a0b\u5e8f<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Q: \u5982\u4f55\u8655\u7406\u5927\u578b\u6a94\u6848\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A: \u5c0d\u65bc\u5927\u578b\u6a94\u6848\uff0c\u5efa\u8b70\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u4f7f\u7528\u4e32\u6d41\uff08Stream\uff09\u800c\u975e\u4e00\u6b21\u8b80\u53d6\u6574\u500b\u6a94\u6848<\/li>\n<li>\u52a0\u5165\u9032\u5ea6\u56de\u5831\u6a5f\u5236<\/li>\n<li>\u8003\u616e\u5728\u80cc\u666f\u7a0b\u5e8f\u4e2d\u8655\u7406<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">\u91cd\u9ede\u6574\u7406<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u7bc7\u5b78\u5230\u7684\u6838\u5fc3\u6982\u5ff5\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>\u6982\u5ff5<\/th>\n<th>\u8aaa\u660e<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>dialog.showOpenDialog()<\/code><\/td>\n<td>\u986f\u793a\u539f\u751f\u7684\u300c\u958b\u555f\u6a94\u6848\u300d\u5c0d\u8a71\u6846<\/td>\n<\/tr>\n<tr>\n<td><code>dialog.showSaveDialog()<\/code><\/td>\n<td>\u986f\u793a\u539f\u751f\u7684\u300c\u5132\u5b58\u6a94\u6848\u300d\u5c0d\u8a71\u6846<\/td>\n<\/tr>\n<tr>\n<td><code>fs.readFileSync()<\/code><\/td>\n<td>\u540c\u6b65\u8b80\u53d6\u6a94\u6848\u5167\u5bb9<\/td>\n<\/tr>\n<tr>\n<td><code>fs.writeFileSync()<\/code><\/td>\n<td>\u540c\u6b65\u5beb\u5165\u6a94\u6848\u5167\u5bb9<\/td>\n<\/tr>\n<tr>\n<td>\u6a94\u6848\u64cd\u4f5c\u5fc5\u9808\u5728\u4e3b\u7a0b\u5e8f<\/td>\n<td>\u6e32\u67d3\u7a0b\u5e8f\u7121\u6cd5\u76f4\u63a5\u5b58\u53d6\u6a94\u6848\u7cfb\u7d71<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e0b\u4e00\u6b65<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u73fe\u5728\u4f60\u5df2\u7d93\u5b78\u6703\u4e86\u6a94\u6848\u7cfb\u7d71\u64cd\u4f5c\uff0c\u63a5\u4e0b\u4f86\u7684\u7b2c\u56db\u7bc7\u5c07\u4ecb\u7d39\u5982\u4f55\u5efa\u7acb\u9078\u55ae\u548c\u5feb\u6377\u9375\uff0c\u8b93\u4f60\u7684\u6587\u5b57\u7de8\u8f2f\u5668\u652f\u63f4 Ctrl+O \u958b\u555f\u3001Ctrl+S \u5132\u5b58\u7b49\u5e38\u7528\u5feb\u6377\u9375\u3002<\/p>\n\n\n\n<div id=\"quiz-advanced\">\n<style>\n#quiz-advanced .badge {\n        display: inline-block;\n        padding: 4px 10px;\n        border-radius: 12px;\n        font-size: 0.75em;\n        font-weight: bold;\n        margin-left: 8px;\n      }#quiz-advanced .badge.scenario {\n        background: #ebf8ff;\n        color: #2b6cb0;\n      }#quiz-advanced .badge.debug {\n        background: #fef3c7;\n        color: #92400e;\n      }#quiz-advanced .question-adv {\n        background: white;\n        border-radius: 10px;\n        padding: 24px;\n        margin: 20px 0;\n        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n        border-left: 4px solid #ed8936;\n      }#quiz-advanced .question-adv h3 {\n        margin-top: 0;\n        color: #2d3748;\n      }#quiz-advanced .code-block {\n        background: #1e1e1e;\n        color: #d4d4d4;\n        padding: 14px;\n        border-radius: 6px;\n        font-family: \"Menlo\", \"Monaco\", \"Courier New\", monospace;\n        font-size: 0.9em;\n        overflow-x: auto;\n        margin: 12px 0;\n        white-space: pre-wrap;\n      }#quiz-advanced .options-adv {\n        list-style: none;\n        padding: 0;\n      }#quiz-advanced .options-adv li {\n        padding: 14px 16px;\n        margin: 10px 0;\n        border: 2px solid #e2e8f0;\n        border-radius: 8px;\n        cursor: pointer;\n        transition: all 0.2s;\n      }#quiz-advanced .options-adv li:hover:not(.disabled) {\n        border-color: #ed8936;\n        background: #fffaf0;\n      }#quiz-advanced .options-adv li.disabled {\n        cursor: default;\n        opacity: 0.7;\n      }#quiz-advanced .options-adv li.show-answer {\n        border-color: #48bb78;\n        background: #f0fff4;\n      }#quiz-advanced .options-adv li.wrong {\n        border-color: #fc8181;\n        background: #fff5f5;\n      }#quiz-advanced .feedback-adv {\n        margin-top: 16px;\n        padding: 14px;\n        border-radius: 8px;\n        display: none;\n        line-height: 1.6;\n      }#quiz-advanced .feedback-adv.correct {\n        background: #f0fff4;\n        color: #276749;\n        display: block;\n      }#quiz-advanced .feedback-adv.wrong {\n        background: #fff5f5;\n        color: #c53030;\n        display: block;\n      }#quiz-advanced .feedback-adv \n      .score-adv {\n        text-align: center;\n        font-size: 1.4em;\n        padding: 24px;\n        background: white;\n        border-radius: 10px;\n        margin-top: 24px;\n        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n      }#quiz-advanced .intro {\n        background: #ebf8ff;\n        padding: 16px;\n        border-radius: 8px;\n        margin-bottom: 20px;\n        color: #2c5282;\n      }\n<\/style>\n<h1>\u9032\u968e\u6e2c\u9a57\uff1aElectron + React \u6a94\u6848\u7cfb\u7d71\u64cd\u4f5c<\/h1>\n    <div class=\"intro\">\n      <strong>\u6e2c\u9a57\u76ee\u6a19\uff1a<\/strong>\u9a57\u8b49\u4f60\u662f\u5426\u80fd\u5728\u5be6\u969b\u60c5\u5883\u4e2d\u61c9\u7528\u6240\u5b78\u3002<br \/>\n      \u5171 5 \u984c\uff0c\u5305\u542b\u60c5\u5883\u984c\u8207\u932f\u8aa4\u8a3a\u65b7\u984c\u3002\n    <\/div>\n\n    <div class=\"question-adv\" id=\"qAdv1\">\n      <h3>1. \u4f60\u6b63\u5728\u958b\u767c\u4e00\u500b\u7c21\u6613\u6587\u5b57\u7de8\u8f2f\u5668\uff0c\u9700\u8981\u5be6\u4f5c\u300c\u958b\u555f\u6a94\u6848\u300d\u529f\u80fd\u3002\u4f7f\u7528\u8005\u9ede\u64ca\u6309\u9215\u5f8c\uff0c\u61c9\u8a72\u986f\u793a\u539f\u751f\u7684\u6a94\u6848\u9078\u64c7\u5c0d\u8a71\u6846\uff0c\u4e26\u8b80\u53d6\u9078\u4e2d\u7684\u6a94\u6848\u5167\u5bb9\u3002\u4e0b\u5217\u54ea\u500b\u505a\u6cd5\u662f\u6b63\u78ba\u7684\u67b6\u69cb\uff1f <span class=\"badge scenario\">\u60c5\u5883\u984c<\/span><\/h3>\n      <ul class=\"options-adv\">\n        <li onclick=\"checkAnswerAdv(1, 'A', 'C')\">A. \u5728 React \u5143\u4ef6\u4e2d\u76f4\u63a5\u4f7f\u7528 <code>fs.readFileSync()<\/code> \u8b80\u53d6\u6a94\u6848<\/li>\n        <li onclick=\"checkAnswerAdv(1, 'B', 'C')\">B. \u5728 preload.js \u4e2d\u76f4\u63a5\u64cd\u4f5c <code>dialog<\/code> \u548c <code>fs<\/code><\/li>\n        <li onclick=\"checkAnswerAdv(1, 'C', 'C')\">C. \u5728\u4e3b\u7a0b\u5e8f\u8a2d\u5b9a IPC handler\uff0c\u4f7f\u7528 <code>dialog<\/code> \u548c <code>fs<\/code>\uff0c\u900f\u904e preload \u66b4\u9732\u7d66 React \u547c\u53eb<\/li>\n        <li onclick=\"checkAnswerAdv(1, 'D', 'C')\">D. \u5728 React \u4e2d\u4f7f\u7528\u700f\u89bd\u5668\u7684 File API \u9078\u64c7\u6a94\u6848\u5f8c\uff0c\u50b3\u7d66\u4e3b\u7a0b\u5e8f\u5132\u5b58<\/li>\n      <\/ul>\n      <div class=\"feedback-adv\" id=\"fbAdv1\"><\/div>\n    <\/div>\n\n    <div class=\"question-adv\" id=\"qAdv2\">\n      <h3>2. \u4f60\u7684\u6587\u5b57\u7de8\u8f2f\u5668\u9700\u8981\u5be6\u4f5c\u300c\u5132\u5b58\u300d\u529f\u80fd\uff0c\u4f46\u8981\u8655\u7406\u5169\u7a2e\u60c5\u6cc1\uff1a\u5df2\u958b\u555f\u7684\u6a94\u6848\u76f4\u63a5\u8986\u84cb\u5132\u5b58\uff0c\u65b0\u5efa\u7684\u6a94\u6848\u5247\u8981\u5148\u8b93\u4f7f\u7528\u8005\u9078\u64c7\u5132\u5b58\u4f4d\u7f6e\u3002\u4ee5\u4e0b\u54ea\u500b React \u8655\u7406\u908f\u8f2f\u662f\u6b63\u78ba\u7684\uff1f <span class=\"badge scenario\">\u60c5\u5883\u984c<\/span><\/h3>\n      <div class=\"code-block\">const handleSave = async () => {\n  \/\/ \u8a72\u5982\u4f55\u5be6\u4f5c\uff1f\n};<\/div>\n      <ul class=\"options-adv\">\n        <li onclick=\"checkAnswerAdv(2, 'A', 'B')\">A. \u7e3d\u662f\u547c\u53eb <code>saveFileAs<\/code>\uff0c\u8b93\u4f7f\u7528\u8005\u6bcf\u6b21\u90fd\u9078\u64c7\u5132\u5b58\u4f4d\u7f6e<\/li>\n        <li onclick=\"checkAnswerAdv(2, 'B', 'B')\">B. \u6aa2\u67e5 <code>currentFile<\/code> \u662f\u5426\u5b58\u5728\uff1a\u6709\u5247\u76f4\u63a5 <code>saveFile<\/code>\uff0c\u6c92\u6709\u5247\u547c\u53eb <code>saveFileAs<\/code><\/li>\n        <li onclick=\"checkAnswerAdv(2, 'C', 'B')\">C. \u6aa2\u67e5 <code>isDirty<\/code> \u662f\u5426\u70ba true\uff0c\u662f\u5247\u5132\u5b58\uff0c\u5426\u5247\u4e0d\u505a\u4efb\u4f55\u4e8b<\/li>\n        <li onclick=\"checkAnswerAdv(2, 'D', 'B')\">D. \u76f4\u63a5\u547c\u53eb <code>saveFile<\/code>\uff0c\u5982\u679c\u8def\u5f91\u4e0d\u5b58\u5728\u6703\u81ea\u52d5\u5f48\u51fa\u5c0d\u8a71\u6846<\/li>\n      <\/ul>\n      <div class=\"feedback-adv\" id=\"fbAdv2\"><\/div>\n    <\/div>\n\n    <div class=\"question-adv\" id=\"qAdv3\">\n      <h3>3. \u4f60\u60f3\u9650\u5236\u4f7f\u7528\u8005\u53ea\u80fd\u9078\u64c7 .txt \u548c .md \u6a94\u6848\u3002\u4f60\u9700\u8981\u5728 <code>dialog.showOpenDialog()<\/code> \u7684\u8a2d\u5b9a\u4e2d\u52a0\u5165\u54ea\u500b\u9078\u9805\uff1f <span class=\"badge scenario\">\u60c5\u5883\u984c<\/span><\/h3>\n      <ul class=\"options-adv\">\n        <li onclick=\"checkAnswerAdv(3, 'A', 'D')\">A. <code>extensions: ['txt', 'md']<\/code><\/li>\n        <li onclick=\"checkAnswerAdv(3, 'B', 'D')\">B. <code>fileTypes: [{ name: '\u6587\u5b57\u6a94', ext: ['txt', 'md'] }]<\/code><\/li>\n        <li onclick=\"checkAnswerAdv(3, 'C', 'D')\">C. <code>accept: '.txt,.md'<\/code><\/li>\n        <li onclick=\"checkAnswerAdv(3, 'D', 'D')\">D. <code>filters: [{ name: '\u6587\u5b57\u6a94', extensions: ['txt', 'md'] }]<\/code><\/li>\n      <\/ul>\n      <div class=\"feedback-adv\" id=\"fbAdv3\"><\/div>\n    <\/div>\n\n    <div class=\"question-adv\" id=\"qAdv4\">\n      <h3>4. \u540c\u4e8b\u5beb\u4e86\u4ee5\u4e0b preload.js \u7a0b\u5f0f\u78bc\uff0c\u4f46 React \u5143\u4ef6\u7121\u6cd5\u547c\u53eb <code>window.electronAPI.openFile()<\/code>\u3002\u554f\u984c\u51fa\u5728\u54ea\u88e1\uff1f <span class=\"badge debug\">\u932f\u8aa4\u8a3a\u65b7<\/span><\/h3>\n      <div class=\"code-block\">\/\/ preload.js\nconst { ipcRenderer } = require(&#8216;electron&#8217;);\n\nwindow.electronAPI = {\n  openFile: () => ipcRenderer.invoke(&#8216;file:open&#8217;),\n  saveFile: (data) => ipcRenderer.invoke(&#8216;file:save&#8217;, data)\n};<\/div>\n      <ul class=\"options-adv\">\n        <li onclick=\"checkAnswerAdv(4, 'A', 'C')\">A. <code>ipcRenderer.invoke<\/code> \u61c9\u8a72\u6539\u6210 <code>ipcRenderer.send<\/code><\/li>\n        <li onclick=\"checkAnswerAdv(4, 'B', 'C')\">B. \u7f3a\u5c11 <code>async\/await<\/code> \u8a9e\u6cd5<\/li>\n        <li onclick=\"checkAnswerAdv(4, 'C', 'C')\">C. \u6c92\u6709\u4f7f\u7528 <code>contextBridge.exposeInMainWorld()<\/code>\uff0c\u76f4\u63a5\u64cd\u4f5c <code>window<\/code> \u5728 <code>contextIsolation: true<\/code> \u6642\u7121\u6548<\/li>\n        <li onclick=\"checkAnswerAdv(4, 'D', 'C')\">D. \u4e3b\u7a0b\u5e8f\u6c92\u6709\u6b63\u78ba\u532f\u5165 <code>ipcMain<\/code><\/li>\n      <\/ul>\n      <div class=\"feedback-adv\" id=\"fbAdv4\"><\/div>\n    <\/div>\n\n    <div class=\"question-adv\" id=\"qAdv5\">\n      <h3>5. \u4f7f\u7528\u8005\u56de\u5831\uff1a\u958b\u555f\u6a94\u6848\u5f8c\u5167\u5bb9\u986f\u793a\u70ba\u4e82\u78bc\u3002\u4f60\u6aa2\u67e5\u4e86\u4e3b\u7a0b\u5e8f\u7684\u8b80\u6a94\u7a0b\u5f0f\u78bc\u5982\u4e0b\u3002\u6700\u53ef\u80fd\u7684\u554f\u984c\u662f\u4ec0\u9ebc\uff1f <span class=\"badge debug\">\u932f\u8aa4\u8a3a\u65b7<\/span><\/h3>\n      <div class=\"code-block\">\/\/ main.js\nipcMain.handle(&#8216;file:open&#8217;, async () => {\n  const result = await dialog.showOpenDialog({\n    properties: [&#8216;openFile&#8217;]\n  });\n\n  if (!result.canceled) {\n    const content = fs.readFileSync(result.filePaths[0]);\n    return { content };\n  }\n});<\/div>\n      <ul class=\"options-adv\">\n        <li onclick=\"checkAnswerAdv(5, 'A', 'B')\">A. <code>dialog.showOpenDialog()<\/code> \u7f3a\u5c11 <code>filters<\/code> \u53c3\u6578<\/li>\n        <li onclick=\"checkAnswerAdv(5, 'B', 'B')\">B. <code>fs.readFileSync()<\/code> \u6c92\u6709\u6307\u5b9a <code>'utf-8'<\/code> \u7de8\u78bc\uff0c\u56de\u50b3\u7684\u662f Buffer \u800c\u975e\u5b57\u4e32<\/li>\n        <li onclick=\"checkAnswerAdv(5, 'C', 'B')\">C. \u61c9\u8a72\u4f7f\u7528 <code>fs.readFile()<\/code> \u975e\u540c\u6b65\u7248\u672c<\/li>\n        <li onclick=\"checkAnswerAdv(5, 'D', 'B')\">D. <code>result.filePaths[0]<\/code> \u61c9\u8a72\u6539\u6210 <code>result.filePath<\/code><\/li>\n      <\/ul>\n      <div class=\"feedback-adv\" id=\"fbAdv5\"><\/div>\n    <\/div>\n\n    <div class=\"score-adv\" id=\"scoreAdv\" style=\"display:none;\"><\/div>\n<script>\n\n      let answeredAdv = 0;\n      let correctAdv = 0;\n      const totalAdv = 5;\n      const explanationsAdv = {\n        1: \"\u6b63\u78ba\u7684\u67b6\u69cb\u662f\u5728\u4e3b\u7a0b\u5e8f\u4e2d\u8655\u7406\u6240\u6709\u6a94\u6848\u64cd\u4f5c\uff0c\u56e0\u70ba\u6e32\u67d3\u7a0b\u5e8f\uff08React\uff09\u7121\u6cd5\u76f4\u63a5\u5b58\u53d6\u6a94\u6848\u7cfb\u7d71\u3002\u4e3b\u7a0b\u5e8f\u4f7f\u7528 <code>ipcMain.handle()<\/code> \u8a2d\u5b9a\u8655\u7406\u5668\uff0cpreload.js \u900f\u904e <code>contextBridge<\/code> \u5c07 API \u66b4\u9732\u7d66 React\uff0cReact \u518d\u900f\u904e <code>window.electronAPI<\/code> \u547c\u53eb\u3002\",\n        2: \"\u6587\u7ae0\u4e2d\u7684\u7bc4\u4f8b\u7a0b\u5f0f\u78bc\u5c31\u662f\u9019\u6a23\u5be6\u4f5c\u7684\uff1a\u5148\u6aa2\u67e5 <code>currentFile<\/code>\uff08\u76ee\u524d\u958b\u555f\u7684\u6a94\u6848\u8def\u5f91\uff09\u662f\u5426\u5b58\u5728\u3002\u5982\u679c\u5b58\u5728\uff0c\u8868\u793a\u662f\u5df2\u958b\u555f\u7684\u6a94\u6848\uff0c\u76f4\u63a5\u547c\u53eb <code>saveFile<\/code> \u8986\u84cb\u5132\u5b58\uff1b\u5982\u679c\u4e0d\u5b58\u5728\uff08null\uff09\uff0c\u8868\u793a\u662f\u65b0\u5efa\u6a94\u6848\uff0c\u547c\u53eb <code>saveFileAs<\/code> \u8b93\u4f7f\u7528\u8005\u9078\u64c7\u5132\u5b58\u4f4d\u7f6e\u3002\",\n        3: \"Electron \u7684 <code>dialog.showOpenDialog()<\/code> \u4f7f\u7528 <code>filters<\/code> \u9663\u5217\u4f86\u8a2d\u5b9a\u6a94\u6848\u985e\u578b\u7be9\u9078\u5668\u3002\u6bcf\u500b filter \u7269\u4ef6\u5305\u542b <code>name<\/code>\uff08\u986f\u793a\u540d\u7a31\uff09\u548c <code>extensions<\/code>\uff08\u526f\u6a94\u540d\u9663\u5217\uff0c\u4e0d\u542b\u9ede\u865f\uff09\u3002\u9078\u9805 C \u7684 <code>accept<\/code> \u662f HTML input \u5143\u7d20\u7684\u5c6c\u6027\uff0c\u4e0d\u9069\u7528\u65bc Electron dialog\u3002\",\n        4: \"\u7576 <code>contextIsolation: true<\/code>\uff08Electron \u7684\u5b89\u5168\u9810\u8a2d\u503c\uff09\u6642\uff0cpreload \u8173\u672c\u548c\u6e32\u67d3\u7a0b\u5e8f\u7684 window \u7269\u4ef6\u662f\u9694\u96e2\u7684\u3002\u76f4\u63a5\u8a2d\u5b9a <code>window.electronAPI<\/code> \u53ea\u6703\u5f71\u97ff preload \u81ea\u5df1\u7684 window\uff0c\u6e32\u67d3\u7a0b\u5e8f\u770b\u4e0d\u5230\u3002\u5fc5\u9808\u4f7f\u7528 <code>contextBridge.exposeInMainWorld('electronAPI', {...})<\/code> \u4f86\u5b89\u5168\u5730\u66b4\u9732 API\u3002\",\n        5: \"<code>fs.readFileSync()<\/code> \u5982\u679c\u4e0d\u6307\u5b9a\u7b2c\u4e8c\u500b\u53c3\u6578\uff08\u7de8\u78bc\uff09\uff0c\u6703\u56de\u50b3 Buffer \u7269\u4ef6\u800c\u975e\u5b57\u4e32\u3002Buffer \u76f4\u63a5\u986f\u793a\u6703\u5448\u73fe\u70ba\u4e82\u78bc\u6216\u5947\u602a\u7684\u5b57\u5143\u3002\u6b63\u78ba\u5beb\u6cd5\u662f <code>fs.readFileSync(filePath, 'utf-8')<\/code>\uff0c\u9019\u6a23\u6703\u76f4\u63a5\u56de\u50b3 UTF-8 \u7de8\u78bc\u7684\u5b57\u4e32\u3002\"\n      };\n\n      function checkAnswerAdv(qNum, selected, correctAns) {\n        const question = document.getElementById(\"qAdv\" + qNum);\n        if (question.classList.contains(\"answered\")) return;\n\n        question.classList.add(\"answered\");\n        answeredAdv++;\n\n        const options = question.querySelectorAll(\".options-adv li\");\n        options.forEach((opt) => {\n          opt.classList.add(\"disabled\");\n          if (opt.textContent.startsWith(correctAns + \".\")) {\n            opt.classList.add(\"show-answer\");\n          }\n        });\n\n        const feedback = document.getElementById(\"fbAdv\" + qNum);\n        if (selected === correctAns) {\n          correctAdv++;\n          feedback.className = \"feedback-adv correct\";\n          feedback.innerHTML = \"\u2713 \u6b63\u78ba\uff01\" + explanationsAdv[qNum];\n        } else {\n          feedback.className = \"feedback-adv wrong\";\n          feedback.innerHTML =\n            \"\u2717 \u932f\u8aa4\u3002\u6b63\u78ba\u7b54\u6848\u662f \" + correctAns + \"\u3002\" + explanationsAdv[qNum];\n          options.forEach((opt) => {\n            if (opt.textContent.startsWith(selected + \".\")) {\n              opt.classList.add(\"wrong\");\n            }\n          });\n        }\n\n        if (answeredAdv === totalAdv) {\n          const scoreDiv = document.getElementById(\"scoreAdv\");\n          scoreDiv.style.display = \"block\";\n          let msg = \"\";\n          if (correctAdv === totalAdv) msg = \"\ud83c\udfaf \u5b8c\u7f8e\uff01\u4f60\u5df2\u7d93\u6e96\u5099\u597d\u5be6\u6230\u4e86\uff01\";\n          else if (correctAdv >= Math.ceil(totalAdv * 0.8))\n            msg = \"\ud83d\udcaa \u5f88\u68d2\uff01\u518d\u8907\u7fd2\u4e00\u4e0b\u932f\u7684\u984c\u76ee\";\n          else if (correctAdv >= Math.ceil(totalAdv * 0.6))\n            msg = \"\ud83d\udcd6 \u5efa\u8b70\u91cd\u8b80\u76f8\u95dc\u6bb5\u843d\u5f8c\u518d\u8a66\u4e00\u6b21\";\n          else msg = \"\ud83d\udd04 \u5efa\u8b70\u91cd\u65b0\u95b1\u8b80\u6587\u4ef6\uff0c\u7279\u5225\u6ce8\u610f\u64cd\u4f5c\u6d41\u7a0b\";\n          scoreDiv.innerHTML =\n            \"\u6e2c\u9a57\u5b8c\u6210\uff01\u5f97\u5206\uff1a\" + correctAdv + \" \/ \" + totalAdv + \"<br>\" + msg;\n        }\n      }\n    \n<\/script>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u6e2c\u9a57\uff1aElectron + React \u6a94\u6848\u7cfb\u7d71\u64cd\u4f5c \u5171 5 \u984c\uff0c\u9ede\u9078\u7b54\u6848\u5f8c\u6703\u7acb\u5373\u986f\u793a\u7d50\u679c 1. \u5728 Elec [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rolpb_likes":0,"footnotes":""},"categories":[356],"tags":[357],"class_list":["post-369","post","type-post","status-publish","format-standard","hentry","category-electron","tag-electron"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":7}},"_links":{"self":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/posts\/369","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/comments?post=369"}],"version-history":[{"count":0,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/posts\/369\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/media?parent=369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/categories?post=369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/tags?post=369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}