<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        當(dāng)前位置: 首頁 - 科技 - 知識(shí)百科 - 正文

        利用Electron簡單擼一個(gè)Markdown編輯器的方法

        來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 21:55:22
        文檔

        利用Electron簡單擼一個(gè)Markdown編輯器的方法

        利用Electron簡單擼一個(gè)Markdown編輯器的方法:Markdown 是我們每一位開發(fā)者的必備技能,在寫 Markdown 過程中,總是尋找了各種各樣的編輯器,但每種編輯器都只能滿足某一方面的需要,卻不能都滿足于日常寫作的各種需求。 所以萌生出自己動(dòng)手試試,利用 Electron 折騰一個(gè) Markdown 編輯器出來。
        推薦度:
        導(dǎo)讀利用Electron簡單擼一個(gè)Markdown編輯器的方法:Markdown 是我們每一位開發(fā)者的必備技能,在寫 Markdown 過程中,總是尋找了各種各樣的編輯器,但每種編輯器都只能滿足某一方面的需要,卻不能都滿足于日常寫作的各種需求。 所以萌生出自己動(dòng)手試試,利用 Electron 折騰一個(gè) Markdown 編輯器出來。

        初次使用 Electron,我們下載回來運(yùn)行看看:

        # 克隆示例項(xiàng)目的倉庫
        $ git clone https://github.com/electron/electron-quick-start
        
        # 進(jìn)入這個(gè)倉庫
        $ cd electron-quick-start
        
        # 安裝依賴并運(yùn)行
        $ npm install && npm start

        VUE

        VUE 是當(dāng)前的前端框架的佼佼者,而且還是我們國人開發(fā)的,不得不服。本人也是 VUE 的忠實(shí)粉絲,在還沒火的 1.0 版本開始,我就使用 VUE 了。

        electron-vue

        將這兩者結(jié)合在一起,也就是本文推薦使用的 simulatedgreg/electron-vue

        vue init simulatedgreg/electron-vue FanlyMD

        安裝插件,并運(yùn)行:

        npm installnpm run dev

        選擇插件

        1. Ace Editor

        選擇一個(gè)好的編輯器至關(guān)重要:

        chairuosen/vue2-ace-editor: https://github.com/chairuosen/vue2-ace-editor
        npm install buefy vue2-ace-editor vue-material-design-icons --save

        2. markdown-it

        能夠快速的解析 Markdown 內(nèi)容,我選擇是用插件:markdown-it

        npm install markdown-it --save

        3. electron-store

        既然是編輯器應(yīng)用,所有很多個(gè)性化設(shè)置和內(nèi)容,就有必要存于本地,如編輯器所需要的樣式文件、自定義的頭部尾部內(nèi)容等。這里我選擇:electron-store

        npm install electron-store --save

        整合

        萬事俱備,接下來我們就開始著手實(shí)現(xiàn)簡單的 Markdown 的編輯和預(yù)覽功能。

        先看 src 文件夾結(jié)構(gòu):

        .
        ├── README.md
        ├── app-screenshot.jpg
        ├── appveyor.yml
        ├── build
        │   └── icons
        │   ├── 256x256.png
        │   ├── icon.icns
        │   └── icon.ico
        ├── dist
        │   ├── electron
        │   │   └── main.js
        │   └── web
        ├── package.json
        ├── src
        │   ├── index.ejs
        │   ├── main
        │   │   ├── index.dev.js
        │   │   ├── index.js
        │   │   ├── mainMenu.js
        │   │   ├── preview-server.js
        │   │   └── renderer.js
        │   ├── renderer
        │   │   ├── App.vue
        │   │   ├── assets
        │   │   │   ├── css
        │   │   │   │   └── coding01.css
        │   │   │   └── logo.png
        │   │   ├── components
        │   │   │   ├── EditorPage.vue
        │   │   │   └── Preview.vue
        │   │   └── main.js
        │   └── store
        │   ├── content.js
        │   └── store.js
        ├── static
        └── yarn.lock

        整個(gè) APP 主要分成左右兩列結(jié)構(gòu),左側(cè)編輯 Markdown 內(nèi)容,右側(cè)實(shí)時(shí)看到效果,而頁面視圖主要由 Renderer 來渲染完成,所以我們首先在 renderer/components/ 下創(chuàng)建 vue 頁面:EditorPage.vue

        <div id="wrapper">
         <div id="editor" class="columns is-gapless is-mobile">
         <editor 
         id="aceeditor"
         ref="aceeditor"
         class="column"
         v-model="input" 
         @init="editorInit" 
         lang="markdown" 
         theme="twilight" 
         width="500px" 
         height="100%"></editor>
         <preview
         id="previewor" 
         class="column"
         ref="previewor"></preview>
         </div>
        </div>

        編輯區(qū)

        左側(cè)使用插件:require('vue2-ace-editor'),處理實(shí)時(shí)監(jiān)聽 Editor 輸入 Markdown 內(nèi)容,將內(nèi)容傳出去。

        watch: {
         input: function(newContent, oldContent) {
         messageBus.newContentToRender(newContent);
         }
        },

        其中這里的 messageBus 就是把 vue 和 ipcRenderer 相關(guān)邏輯事件放在一起的 main.js

        import Vue from 'vue';
        import App from './App';
        import 'buefy/dist/buefy.css';
        import util from 'util';
        import { ipcRenderer } from 'electron';
        
        if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
        Vue.config.productionTip = false
        
        export const messageBus = new Vue({
         methods: {
         newContentToRender(newContent) {
         ipcRenderer.send('newContentToRender', newContent);
         },
         saveCurrentFile() { }
         }
        });
        
        // 監(jiān)聽 newContentToPreview,將 url2preview 傳遞給 vue 的newContentToPreview 事件
        // 即,傳給 Preview 組件獲取
        ipcRenderer.on('newContentToPreview', (event, url2preview) => {
         console.log(`ipcRenderer.on newContentToPreview ${util.inspect(event)} ${url2preview}`);
         messageBus.$emit('newContentToPreview', url2preview);
        });
        
        /* eslint-disable no-new */
        new Vue({
         components: { App },
         template: '<App/>'
        }).$mount('#app')

        編輯器的內(nèi)容,將實(shí)時(shí)由 ipcRenderer.send('newContentToRender', newContent); 下發(fā)出去,即由 Main 進(jìn)程的 ipcMain.on('newContentToRender', function(event, content) 事件獲取。

        一個(gè) Electron 應(yīng)用只有一個(gè) Main 主進(jìn)程,很多和本地化東西 (如:本地存儲(chǔ),文件讀寫等) 更多的交由 Main 進(jìn)程來處理。

        如本案例中,想要實(shí)現(xiàn)的第一個(gè)功能就是,「可以自定義固定模塊,如文章的頭部,或者尾部」

        我們使用一個(gè)插件:electron-store,用于存儲(chǔ)頭部和尾部內(nèi)容,創(chuàng)建Class:

        import {
         app
        } from 'electron'
        import path from 'path'
        import fs from 'fs'
        import EStore from 'electron-store'
        
        class Content {
         constructor() {
         this.estore = new EStore()
         this.estore.set('headercontent', `<img src="http://bimage.coding01.cn/logo.jpeg" class="logo">
         <section class="textword"><span class="text">本文 <span id="word">111</span>字,需要 <span id="time"></span> 1分鐘</span></section>`)
         this.estore.set('footercontent', `<hr>
         <strong>coding01 期待您繼續(xù)關(guān)注</strong>
         <img src="http://bimage.coding01.cn/coding01_me.GIF" alt="qrcode">`)
         }
        
         // This will just return the property on the `data` object
         get(key, val) {
         return this.estore.get('windowBounds', val)
         }
        
         // ...and this will set it
         set(key, val) {
         this.estore.set(key, val)
         }
        
         getContent(content) {
         return this.headerContent + content + this.footerContent
         }
        
         getHeaderContent() {
         return this.estore.get('headercontent', '')
         }
         
         getFooterContent() {
         return this.estore.get('footercontent', '')
         }
        }
        
        // expose the class
        export default Content
        注:這里只是寫死的頭部和尾部內(nèi)容。

        有了頭尾部內(nèi)容,和編輯器的 Markdown 內(nèi)容,我們就可以將這些內(nèi)容整合,然后輸出給我們的右側(cè) Preview 組件了。

        ipcMain.on('newContentToRender', function(event, content) {
         const rendered = renderContent(headerContent, footerContent, content, cssContent, 'layout1.html');
         
         const previewURL = newContent(rendered);
         mainWindow.webContents.send('newContentToPreview', previewURL);
        });

        其中,renderContent(headerContent, footerContent, content, cssContent, 'layout1.html') 方法就是將我們的頭部、尾部、Markdown內(nèi)容、css 樣式和我們的模板 layout1.html 載入。這個(gè)就比較簡單了,直接看代碼:

        import mdit from 'markdown-it';
        import ejs from 'ejs';
        
        const mditConfig = {
         html: true, // Enable html tags in source
         xhtmlOut: true, // Use '/' to close single tags (<br />)
         breaks: false, // Convert '\n' in paragraphs into <br>
         // langPrefix: 'language-', // CSS language prefix for fenced blocks
         linkify: true, // Autoconvert url-like texts to links
         typographer: false, // Enable smartypants and other sweet transforms
         
         // Highlighter function. Should return escaped html,
         // or '' if input not changed
         highlight: function (/*str, , lang*/) { return ''; }
        };
        const md = mdit(mditConfig);
        
        const layouts = [];
        
        export function renderContent(headerContent, footerContent, content, cssContent, layoutFile) {
         const text = md.render(content);
         const layout = layouts[layoutFile];
         const rendered = ejs.render(layout, {
         title: 'Page Title',
         content: text,
         cssContent: cssContent,
         headerContent: headerContent,
         footerContent: footerContent,
         });
         return rendered;
        }
        
        layouts['layout1.html'] = `
        <html>
         <head>
         <meta charset='utf-8'>
         <title><%= title %></title>
         <style>
         <%- cssContent %>
         </style>
         </head>
         <body>
         <div class="markdown-body">
         <section class="body_header">
         <%- headerContent %>
         </section>
         <div id="content">
         <%- content %>
         </div>
         <section class="body_footer">
         <%- footerContent %>
         </section>
         </div>
         </body>
        </html>
        `;
        這里,使用插件 markdown-it 來解析 Markdown 內(nèi)容,然后使用ejs.render() 來填充模板的各個(gè)位置內(nèi)容。這里,同時(shí)也為我們的目標(biāo):樣式必須是可以自定義的 和封裝各種不同情況下,使用不同的頭部、尾部、模板、和樣式提供了伏筆

        當(dāng)有了內(nèi)容后,我們還需要把它放到「服務(wù)器」上,const previewURL = newContent(rendered);

        import http from 'http';
        import url from 'url';
        
        var server;
        var content;
        
        export function createServer() {
         if (server) throw new Error("Server already started");
         server = http.createServer(requestHandler);
         server.listen(0, "127.0.0.1");
        }
        
        export function newContent(text) {
         content = text;
         return genurl('content');
        }
        
        export function currentContent() {
         return content;
        }
        
        function genurl(pathname) {
         const url2preview = url.format({
         protocol: 'http',
         hostname: server.address().address,
         port: server.address().port,
         pathname: pathname
         });
         return url2preview;
        }
        
        function requestHandler(req, res) {
         try {
         res.writeHead(200, {
         'Content-Type': 'text/html',
         'Content-Length': content.length
         });
         res.end(content);
         } catch(err) {
         res.writeHead(500, {
         'Content-Type': 'text/plain'
         });
         res.end(err.stack);
         }
        }

        最終得到 URL 對(duì)象,轉(zhuǎn)給我們右側(cè)的 Preview 組件,即通過 mainWindow.webContents.send('newContentToPreview', previewURL);

        注:在 Main 和 Renderer 進(jìn)程間通信,使用的是 ipcMainipcRendereripcMain 無法主動(dòng)發(fā)消息給 ipcRenderer。因?yàn)?code>ipcMain只有 .on() 方法沒有 .send() 的方法。所以只能用 webContents。

        預(yù)覽區(qū)

        右側(cè)使用的時(shí)間上就是一個(gè) iframe 控件,具體做成一個(gè)組件 Preview

        <template>
         <iframe src=""/>
        </template>
        
        <script>
        import { messageBus } from '../main.js';
        
        export default {
         methods: {
         reload(previewSrcURL) {
         this.$el.src = previewSrcURL;
         }
         },
         created: function() {
         messageBus.$on('newContentToPreview', (url2preview) => {
         console.log(`newContentToPreview ${url2preview}`);
         this.reload(url2preview);
         });
         }
        }
        </script>
        
        <style scoped>
        iframe { height: 100%; }
        </style>

        Preview 組件我們使用 vue 的 $on 監(jiān)聽 newContentToPreview 事件,實(shí)時(shí)載入 URL 對(duì)象。

        messageBus.$on('newContentToPreview', (url2preview) => {
         this.reload(url2preview);
        });

        到此為止,我們基本實(shí)現(xiàn)了最基礎(chǔ)版的 Markdown 編輯器功能,yarn run dev 運(yùn)行看看效果:

        總結(jié)

        第一次使用 Electron,很膚淺,但至少學(xué)到了一些知識(shí):

      1. 每個(gè) Electron 應(yīng)用只有一個(gè) Main 進(jìn)程,主要用于和系統(tǒng)打交道和創(chuàng)建應(yīng)用窗口,在 Main 進(jìn)程中,利用 ipcMain 監(jiān)聽來自 ipcRenderer的事件,但沒有 send 方法,只能利用 BrowserWindow。webContents.send()。
      2. 每個(gè)頁面都有對(duì)應(yīng)的 Renderer 進(jìn)程,用于渲染頁面。當(dāng)然也有對(duì)應(yīng)的 ipcRenderer 用于接收和發(fā)送事件。
      3. 在 vue 頁面組件中,我們還是借助 vue 的 $on 和 `$emit 傳遞和接收消息。
      4. 接下來一步步完善該應(yīng)用,目標(biāo)是滿足于自己的需要,然后就是:也許哪天就開源了呢。

        解決中文編碼問題

        由于我們使用 iframe,所以需要在 iframe 內(nèi)嵌的 <html></html> 增加 <meta charset='utf-8'>

        代碼如下:

        聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        利用Electron簡單擼一個(gè)Markdown編輯器的方法

        利用Electron簡單擼一個(gè)Markdown編輯器的方法:Markdown 是我們每一位開發(fā)者的必備技能,在寫 Markdown 過程中,總是尋找了各種各樣的編輯器,但每種編輯器都只能滿足某一方面的需要,卻不能都滿足于日常寫作的各種需求。 所以萌生出自己動(dòng)手試試,利用 Electron 折騰一個(gè) Markdown 編輯器出來。
        推薦度:
        標(biāo)簽: 簡單的 編輯器 markdown
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top 主站蜘蛛池模板: 一出一进一爽一粗一大视频免费的| www视频在线观看免费| 拍拍拍无挡免费视频网站| 青柠影视在线观看免费高清| 99在线热视频只有精品免费| 亚洲一级免费毛片| 免费涩涩在线视频网| 亚洲色一色噜一噜噜噜| 浮力影院第一页小视频国产在线观看免费| 午夜视频在线在免费| 亚洲成A∨人片天堂网无码| 亚洲午夜未满十八勿入网站2| 亚洲福利视频导航| 亚洲色精品VR一区区三区 | 国产亚洲精品国看不卡| 亚洲国产精品lv| 男人天堂2018亚洲男人天堂| 四虎影视在线看免费观看| 久久精品成人免费看| 国产免费AV片在线播放唯爱网| 久久久久免费看成人影片| 成人免费午间影院在线观看| 亚洲国产精品碰碰| 久久亚洲精品无码aⅴ大香| 狠狠综合亚洲综合亚洲色| 免费网站观看WWW在线观看| 中文字幕无码不卡免费视频| 亚洲伊人成无码综合网| 亚洲码在线中文在线观看| 免费夜色污私人影院网站| 免费A级毛片无码A∨中文字幕下载| 免费看的一级毛片| 久久综合日韩亚洲精品色| 国产精品亚洲va在线观看 | 四虎影视永久在线精品免费| 色欲A∨无码蜜臀AV免费播 | 女性自慰aⅴ片高清免费| 亚洲熟女少妇一区二区| 亚洲日本VA午夜在线影院| 国产午夜无码片免费| 免费特级黄毛片在线成人观看|