Electron에서 텍스트 편집기 만들기: 섹션 2 - 파일 작성

34116 단어 electronnodejavascript
에서 기본 구조를 생성했습니다.우리는 디렉터리에서 파일을 읽을 수 있고, 사이드바에 제목을 열거할 수 있으며, 화면에서 파일을 읽을 수 있다.
이 강좌에서, 우리는 더 많은 상호작용을 추가할 것이다.우선, 우리 메뉴에 대해 이야기합시다.우리가 자신의 메뉴를 지정하지 않았기 때문에, Electron은 기본적으로 우리에게 메뉴를 제공하지만, 내부 ./main.js 에서는 우리가 필요로 하는 일을 하도록 단추를 만들 수 있습니다.우리 예를 하나 봅시다.
const { app, BrowserWindow, Menu } = require('electron')
...
app.on('ready', function(){
    devtools = new BrowserWindow()
    window = new BrowserWindow({ x: 0, y: 0, width:800, height:600})
    window.loadURL(path.join('file://', __dirname, 'static/index.html'))
    window.setTitle('Texty')
    Menu.setApplicationMenu(Menu.buildFromTemplate([
        {
            label: app.getName(),
            submenu: [
                {
                    label: `Hello`,
                    click: () => console.log("Hello world")
                }
            ]
        }
    ]))

})
우리는 우선 전자의 Menu 성분이 필요하다.그리고 우리는 그것을 사용하여 불러올 프로그램을 위한 메뉴를 만듭니다.이상은 단지 하나의 예일 뿐이다.일반적으로 첫 번째 탭은 하위 메뉴만 열면 됩니다.따라서 탭에 대해 응용 프로그램 이름을 사용하고 Hello 단추를 만들어서 메시지를 제어합니다.
메뉴를 펼치겠습니다.그러나, 대상이 매우 클 수 있기 때문에, 우리는 하나의 단독 구성 요소에 메뉴를 추가합니다.
// ./main.js
const menu = require('./components/Menu')
app.on('ready', function(){
    window = new BrowserWindow({ x: 0, y: 0, width:800, height:600})
    ...
    Menu.setApplicationMenu(menu(window))

})
이것이 바로 네비게이션이 분할할 수 있는 방식이다../components/Menu.js 파일을 만듭니다. 이 파일은 함수를 되돌려줍니다.
const {app, Menu } = require('electron')
module.exports = function(win){
    return Menu.buildFromTemplate([
        {
            label: app.getName(),
            submenu: [
                { label: `Hello`, click: () => console.log("Hello world") }
            ]
        },
        {
            label: 'Edit',
            submenu: [
                {label: 'Undo', role: 'undo'  },
                {label: 'Redo', role: 'redo'  },
                {label: 'Cut', role: 'cut'  },
                {label: 'Copy', role: 'copy'  },
                {label: 'Paste', role:'paste'  },
            ]
        },
        {
            label: 'Custom Menu', 
            submenu: [/* We'll add more actions */]
        }

    ])    
}
Electron은 엔진 덮개 아래에서 무거운 물건을 운반하는 데 사용되는 세트roles를 제공했다.링크를 클릭하여 사용 가능한 모든 캐릭터를 보십시오.
지금부터 우리는 모든 내비게이션을 Custom Menu 의 하위 메뉴로 추가하여 그것의 흥미를 유지할 것이다!

새 문서 만들기


지금까지 저희 프로그램의 상태는 CD에서 파일을 읽고 내용을 표시하는 것입니다.(마지막으로 이런 방법의 결함을 토론했다)
새 문서를 추가하는 기능을 추가합니다.
우리는 우선 내비게이션에 단추를 하나 추가한다.따라서 ./components/Menu.js에 다음을 추가합니다.
const { NEW_DOCUMENT_NEEDED } = require('../actions/types')
module.exports = function(window){
...
{
    label: 'Custom Menu', 
    submenu: [
        {
            label: 'New',
            accelerator: 'cmd+N',
            click: () => {
                window.webContents.send(NEW_DOCUMENT_NEEDED, 'Create new document')
            }
        }
    ]
메뉴에 New 단추를 만듭니다. accelerator 속성은 단추에 바로 가기입니다.그리고 단추를 누르면 프로그램의 렌더링 부분에 메시지를 보냅니다!
내가 읽은 일부 강좌들은 이것이 파악하기 어렵다고 지적하지만, 생각해 보면 상점과 소통하는 유일한 방법은 경청하고 메시지를 보내는 것이다.이것은 이곳에서 완전히 같다../main.js 백엔드를 처리합니다.이것은 우리로 하여금 electron의 모듈에 접근할 수 있게 한다. (예를 들어 메뉴, 필요하면 인터넷 카메라와 다양한 기능에 접근할 수 있다.)./static/scripts/*.js 의 모든 기능에 액세스할 수 없습니다.이 코드는 DOM 작업에만 적용됩니다.심지어 이 코드를 fs 작업에 사용하는 것을 반대하는 강력한 이유가 있다../static/scripts/index.js로 돌아가서 듣겠습니다NEW_DOCUMENT_NEEDED.
const { ipcRenderer } = require('electron'); 
const { NEW_DOCUMENT_NEEDED } = require(path.resolve('actions/types'))
ipcRenderer.on(NEW_DOCUMENT_NEEDED, (event , data) => {
    let form = document.getElementById('form')
        form.classList.toggle('show')
    document.getElementById('title_input').focus()
    form.addEventListener('submit', function(e){
        e.preventDefault()
        // write file here ?
    })
})
우리는 NEW_DOCUMENT_NEEDED 전송을 청취한다.우리가 그것을 들을 때, 우리는 폼을 표시할 것이다 (일반적인 CSS 클래스 전환).

그리고 표를 제출할 때, 우리는 새로운 파일을 작성해야 한다.
이 간단한 응용 프로그램에 대해 우리는 fs.writeFile 보다 약간 낮게 사용할 것이다.그러나 만약 이것이 큰 프로젝트라면, 우리는 렌더링에 있어서 어떠한 파일 시스템 조작도 실행하기를 원하지 않는다.응용 프로그램이 크면 // write file here ? 이 작업을 처리할 수 없습니다. (분명히 저희 범위를 벗어난 새 창이 필요합니다.)그러나 주로 이 점을 어떻게 하는지 탐색하기 위해 우리는 ./main.js 시스템을 쓸 것이다.
const { ipcRenderer } = require('electron'); 
const {  WRITE_NEW_FILE_NEEDED } = require(path.resolve('actions/types'))
...
form.addEventListener('submit', function(e){
    e.preventDefault()
    // write file here ?
    ipcRenderer.send(WRITE_NEW_FILE_NEEDED, {
        dir: `./data/${fileName}.md`
    })
})
위에서 우리는 ./main.js 채널에 대상을 보내고 있습니다. (이 채널의 이름은 당신이 좋아하는 모든 이름일 수 있습니다.)WRITE_NEW_FILE_NEEDED로 이동하여 파일을 만들고 메시지를 반환합니다.
ipcMain.on(WRITE_NEW_FILE_NEEDED, (event, {dir}) => {
    fs.writeFile(dir, `Start editing ${dir}`, function(err){
        if(err){ return console.log('error is writing new file') }
        window.webContents.send(NEW_FILE_WRITTEN, `Start editing ${dir}`)
    });
})
./main.js가 전송될 때 이 채널을 통해 전송된 WRITE_NEW_FILE_NEEDED 파일을 디렉터리에 기록하고 쓰기 과정이 이미 끝났다는 메시지를 보냅니다.
마지막으로dir
form.addEventListener('submit', function(e){
    e.preventDefault()
    let fileName = e.target[0].value
    ...
    ipcRenderer.on(NEW_FILE_WRITTEN, function (event, message) {
        handleNewFile(e, `./data/${fileName}.md`, message)
    });
})
이렇게
물론 전체 그림을 얻기 위해 복제repository를 해야 합니다../statics/scripts/index.js 양식만 숨기고 응용 프로그램이 열릴 때 클릭 이벤트를 처리합니다.페이지에 내용을 표시합니다.
const handleNewFile = function(form, dir, content){ 
    let fileName =form.target[0].value
    form.target.classList.remove('show')
    let elChild = document.createElement('li')
    elChild.innerText = fileName
    readFileContentOnClick(dir, elChild) // read file on click
    form.target[0].value = ''
    form.target.parentNode.insertBefore(elChild,form.target.nextSibling);
    document.getElementById('content').innerHTML = content;
}
나는 ipcRenderer와 ipcMain 간의 통신에 대해 일종의 사고 방식을 가지고 있다.우리는 Redux 상점과 통신 방식이 완전히 같다.
이것은 우리가 지금까지의 코드맵이다

보시다시피 이 두 프로세스 사이의 이런 춤은 우리가 하고 있는 일에 있어서 과도한 살상력이지만, UI를 막지 않기 위해서는 이런 일이 발생해야 합니다.내가 말한 바와 같이, 더 큰 응용 프로그램에서는 그래도 부족할 수 있다.나는 이것이 하나의 기능이 아니라 버그라고 생각한다.

변경 내용 저장


마지막으로, 이 시리즈의 이 부분에 대해 우리는 변경 사항을 저장해야 한다.
Mac 모드에 따라 파일을 저장해야 하는 시각적 지시를 원하고 파일을 저장한 후에 삭제합니다.handleNewFile부터
document.getElementById('content').onkeyup = e => { 
    if(!document.title.endsWith("*")){ 
        document.title += ' *' 
    }; 
    ipcRenderer.send(SAVE_NEEDED, { // alerting ./component/Menu.js
        content: e.target.innerHTML,
        fileDir
    })
}
./static/scripts/index.js 는 입력한 내용을 나타냅니다. 이 경우 제목에 별표를 추가한 다음 onkeyup 을 주 프로세스로 전송합니다.입력한 정보와 영향을 받는 파일 디렉터리가 필요합니다.
이번에는 듣지 않고 듣는다SAVE_NEEDED.
let contentToSave = ''
ipcMain.on(SAVE_NEEDED, (event, content) => {
    contentToSave = content 
})
module.exports = function(window){
    return Menu.buildFromTemplate([
        ...
        {
            label: 'Save',
            click: () => {
                if(contentToSave != ''){
                    fs.writeFile(contentToSave.fileDir, contentToSave.content, (err) => {
                        if (err) throw err;
                        window.webContents.send(SAVED, 'File Saved')
                    });
                }
            },
            accelerator: 'cmd+S'
        }
./main.js에서 우리는 내용을 전송했다.그리고 ./components/Menu.js 을 선택할 때마다 이 내용을 검사합니다. 이 내용이 존재하면 파일을 씁니다.그리고 파일을 쓴 후, 우리는 렌더링 부분에 경보를 보냈다. 메시지SAVE_NEEDED를 포함하고, 우리는 Save에서 그것을 처리했다.
ipcRenderer.on(SAVED, (event , data) => { // when saved show notification on screen
    el = document.createElement("p");
    text = document.createTextNode(data);
    el.appendChild(text)
    el.setAttribute("id", "flash");
    document.querySelector('body').prepend(el)
    setTimeout(function() { // remove notification after 1 second
        document.querySelector('body').removeChild(el);
        document.title = document.title.slice(0,-1) // remove asterisk from title
    }, 1000);
});
최종 결과는 다음과 같습니다.

오늘은 여기까지!
하지만, 나는 내가 분명히 말해야 한다고 생각한다.나는 전자의 기초 위에 중점을 두려고 한다.그래서 보시다시피 저는 검증에 관심이 없습니다.
최저 생산 기준에 도달하기 위해 우리가 해야 할 일은 매우 적다.
  • 파일이 있는지 확인합니다.
  • 파일 사이를 이동할 때 저장되지 않은 파일을 처리합니다.
  • 실제로 내용을 가격 인하로 전환했다.
  • 컨텐츠 저장(예:
    이전 강좌에서 참조).
  • 그리고 상술한 내용보다 더 중요한 일들.
  • 그러나 이것들은 모두 전자 특유의 것이 아니기 때문에 나는 코드를 쓰고 해석하는 데 시간을 들이지 않는 것을 선택했다. 이것은 전자를 배우는 데 도움이 되지 않는다.
    이 미니 시리즈에는 또 하나의 강좌가 있는데, 우리는 어떻게 다른 창을 추가하고 사용자의 선호도를 처리하는지 연구할 것이다.
    동시에 이 항목을 보려면 github, branch: part2 로 문의하십시오.



    좋은 웹페이지 즐겨찾기