项目代码
https://github.com/electron-in-action/firesale/tree/chapter-6
代码解析
窗口标题
- 根据文件路径获取文件名:
path.basename(filePath)
- 设置窗口标题:
currentWindow.setTitle(title)
const path = require('path');
...
const updateUserInterface = () => {
let title = "Fire Sale";
if (filePath) {
title = `${path.basename(filePath)} - ${title}`; //path.basename:根据文件路径获取文件名
currentWindow.setTitle(title);
}
};
检测文件是否被修改过
//文本框监听`keyup`,
markdownView.addEventListener('keyup', (event)=>{
const currentContent = event.target.value;
renderMarkdownToHtml(currentContent);
updateUserInterface(currentContent != originalContent);
});
const updateUserInterface = (isEdited) => {
let title = "Fire Sale";
if (filePath) {
title = `${path.basename(filePath)} - ${title}`; //path.basename:根据文件路径获取文件名
//WindowLinux
if (isEdited) {
title = `${title} (edited)`;
}
currentWindow.setTitle(title);
//MacOS
currentWindow.setDocumentEdited(isEdited);
}
};
保存为html
注意:electron 9.x 打开文本对话框的函数已经做了修改,与低版本写法已经不一样了
dialog.showSaveDialog(win,{})
.then(result=>{})
.catch(err =>{
});
- main.js
//保存为html
const saveHtml = exports.saveHtml = (targetWindow, content) => {
dialog.showSaveDialog(targetWindow, {
title: 'Save Html',
defaultPath: app.getPath('documents'), //默认使用操作系统中用户的documents目录
filters: [
{ name: 'HTML Files', extensions: ['html', 'htm'] }
]
}).then(result => {
if (result.filePath) {
try {
fs.writeFileSync(result.filePath, content);
} catch (error) {
console.log("保存html发生异常:" + error)
}
}
}).catch(err => {
console.log(err)
})
};
- renderer.js
//保存为Html
saveHtmlButton.addEventListener('click', () => {
mainProcess.saveHtml(currentWindow, htmlView.innerHTML);
});
保存当前文件
- main.js
//保存Markdown
const saveMarkdown = exports.saveMarkdown = (targetWindow, file, content) => {
if (file) { //如果文件为空,打开文件保存对话框
fs.writeFileSync(file, content);
} else { //如果文件为空,打开文件保存对话框
dialog.showSaveDialog(targetWindow, {
title: 'Save Html',
defaultPath: app.getPath('documents'), //默认使用操作系统中用户的documents目录
filters: [
{ name: 'HTML Files', extensions: ['html', 'htm'] }
]
}).then(result => {
if (result.filePath) {
fs.writeFileSync(result.filePath, content);
}
}).catch(err => {
console.log(err)
})
}
};
- renderer.js
//保存Markdow
saveMarkdownButton.addEventListener('click', () => {
mainProcess.saveMarkdown(currentWindow, filePath, markdownView.value);
});
回滚文件
- renderer.js
//回撤
revertButton.addEventListener('click', () => {
markdownView.value = originalContent;
renderMarkdownToHtml(originalContent);
});
通过拖拽打开文件
拖拽事件:document.addEventListener('dragStart/drag/dragleave/drop', event => {event.preventDefault(); ...});
拖拽文件信息:
event.dataTransfer.items[0] //dragover
event.dataTransfer.files[0] //drop
书中给的示例,与实际有出入:
- makedown文件获取的
file.tpye=''
;
//拖拽到上边不松手这时,只能)读取文件元信息
const getDraggedFile = (event) => {
return event.dataTransfer.items[0];
};
...
const file = getDroppedFile(event); //file.tpye='';
fileTypeIsSupported()
对 makedown文件 判定有误,改用fileTypeIsSupportedByExtName()
// 是否支持文件类型
const fileTypeIsSupported = (file) => {
return ['text/plain', 'text/markdown'].includes(file.type);
}
//拖拽到上边
document.addEventListener('dragover', event => {
event.preventDefault();
const file = getDraggedFile(event); //有问题:file.type=''
/*
if (fileTypeIsSupported(file)) {
markdownView.classList.add('drag-over'); //支持文件类型样式
} else {
markdownView.classList.add('drag-error');//不支持文件类型样式
}
*/
});
//离开
document.addEventListener('dragleave', event => {
event.preventDefault();
markdownView.classList.remove('drag-over');
markdownView.classList.remove('drag-error');
});
//拖拽到上边并松手
document.addEventListener('drop', event => {
event.preventDefault();//禁用默认事件
const file = getDroppedFile(event);
let extName = path.extname(file.path);
if (fileTypeIsSupportedByExtName(extName)) { //if (fileTypeIsSupported(file)) {
mainProcess.openFile(currentWindow, file.path); //支持文件类型样式
} else {
alert('that file type is not supported');
}
markdownView.classList.remove('drag-over');
markdownView.classList.remove('drag-error');
});
//拖拽到上边不松手这时,只能)读取文件元信息
const getDraggedFile = (event) => {
return event.dataTransfer.items[0];
};
//(拖拽到上边并松手才能)读取文件File对象
const getDroppedFile = (event) => {
return event.dataTransfer.files[0];
};
// 是否支持文件类型
const fileTypeIsSupported = (file) => {
return ['text/plain', 'text/markdown'].includes(file.type);
}
// 是否支持文件类型
const fileTypeIsSupportedByExtName = (extName) => {
return ['.md', '.markdown'].includes(extName);
}
监控文件变化
监控文件 Node.js : const watcher = fs.watch(file, (event)=>{ if(event==='change') {...}})
const watcher = fs.watch(file, (event) => { //Node 的文件监控器,文件有变化,重新读取文件
console.log(event);
if (event === `change`) {
const content = fs.readFileSync(file).toString();;
targetWindow.webContents.send('file-opened', file, content);
}
});
openFiles.set(targetWindow, watcher);
停止监控文件:watcher.close()
//停止监控文件:
const stopWatchingFile = (targetWindow) => {
if (openFiles.has(targetWindow)) {
openFiles.get(targetWindow).close(); //.stop() 已经不存在
openFiles.delete(targetWindow);
}
};
提示未保存修改
- main.js
//监控文件
const startWatchingFile = (targetWindow, file) => {
stopWatchingFile(targetWindow);
//问题:fs.watch ()在windows系统:一次修改会触发2次'change'
const watcher = fs.watch(file, (event) => { //Node 的文件监控器,文件有变化,重新读取文件
if (event === `change`) {
console.log('watch-change');
const content = fs.readFileSync(file).toString();;
targetWindow.fileContentChanged = true;
targetWindow.webContents.send('file-changed', file, content); //文档内容发生改变消息
} else {
targetWindow.fileContentChanged = false;
}
});
openFiles.set(targetWindow, watcher);
};
//停止监控文件
const stopWatchingFile = (targetWindow) => {
if (openFiles.has(targetWindow)) {
let watcher = openFiles.get(targetWindow);
//https://www.nodeapp.cn/fs.html#fs_watcher_close
watcher.close();//watcher.stop();
openFiles.delete(targetWindow);
}
};
- renderer.js
//打开文件
ipcRenderer.on('file-opened', (event, file, content) => {
if (currentWindow.isDocumentEdited() //macOS系统
|| currentWindow.fileContentChanged) {//其它平台
event.preventDefault(); //阻止窗口关闭
dialog.showMessageBox(currentWindow, {
type: 'warning',
title: '当前文档尚未保存',
message: "当前文档尚未保存,新打开的文档将覆盖当前文档",
buttons: ['继续打开', '取消']
}).then(result => {
if (result.response === 0) {
renderFile(file, content);
}
});
} else {
renderFile(file, content);
}
});
//文件内容发生改变,提示用户
ipcRenderer.on('file-changed', (event, file, content) => {
remote.dialog.showMessageBox(currentWindow, {
type: 'warning',
title: '警告',
message: "文档内容已被其它程序修改,是否加载并覆盖当前的内容",
buttons: ['加载覆盖', '取消']
}).then(result => {
if (result.response === 0) {
renderFile(file, content);
}
});
});
const renderFile = (file, content) => {
filePath = file;
originalContent = content;
markdownView.value = content;
renderMarkdownToHtml(content);
updateUserInterface(false);
}
知识点
选择对话框:dialog.showMessageBox()
remote.dialog.showMessageBox(currentWindow, {
type: 'warning',
title: '警告',
message: "文档内容已被其它程序修改,是否加载并覆盖当前的内容",
buttons: ['加载覆盖', '取消']
}).then(result => {
if (result.response === 0) {
renderFile(file, content);
}
});
监控文件变化
fs.watch()
或 fs.watchFile()
fs.watch()
文档: https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_fs_watchfile_filename_options_listener
- 问题:
fs.watch ()在windows系统:一次修改会触发2次'change'
关闭监听文件变化 watcher.close()
const watcher = fs.watch(...);
watcher.close();