结合React+TypeScript进行Electron开发

1. electron基本简介

electron是使用JavaScript,HTML和CSS构建跨平台桌面应用程序。我们可以使用一套代码打包成Mac、Windows和Linux的应用,electron比你想象的更简单,如果把你可以建一个网站,你就可以建一个桌面应用程序,我们只需要把精力放在应用的核心上即可。

为什么选择electron?

  • Electron 可以让你使用纯JavaScript调用丰富的原生APIs来创造桌面应用。你可以把它看作是专注于桌面应用。
  • 在PC端桌面应用开发中,nwjs和electron都是可选的方案,它们都是基于Chromium和Node的结合体,而electron相对而言是更好的选择方案,它的社区相对比较活跃,bug比较少,文档相对利索简洁。
  • electron相对来说比nw.js靠谱,有一堆成功的案例:Atom编辑器 Visual Studio Code WordPress等等。
  • Node.js的所有内置模块都在Electron中可用。

2. 快速上手

2.1 安装React(template为ts)

yarn create react-app electron-demo-ts --template typescript

2.2 快速配置React

工程架构

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Bleak's electron app base react"
/>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline';">
<link rel="stylesheet" href="%PUBLIC_URL%/css/reset.css">
<title>electron App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

App.tsx

import React from 'react'

export default function App() {
return (
<div>App</div>
)
}

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
// <React.StrictMode>
<App />
// </React.StrictMode>
); // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

2.3 安装electron

electron包安装到您的应用程序的devDependencies.

// npm
npm install --save-dev electron
// yarn
yarn add --dev electron

2.4 配置main.jspreload.jspackage.json文件

main.js

// 导入app、BrowserWindow模块
// app 控制应用程序的事件生命周期。事件调用app.on('eventName', callback),方法调用app.functionName(arg)
// BrowserWindow 创建和控制浏览器窗口。new BrowserWindow([options]) 事件和方法调用同app
// Electron参考文档 https://www.electronjs.org/docs
const {app, BrowserWindow, nativeImage } = require('electron')
const path = require('path')
// const url = require('url'); function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800, // 窗口宽度
height: 600, // 窗口高度
// title: "Electron app", // 窗口标题,如果由loadURL()加载的HTML文件中含有标签<title>,该属性可忽略
icon: nativeImage.createFromPath('public/favicon.ico'), // "string" || nativeImage.createFromPath('public/favicon.ico')从位于 path 的文件创建新的 NativeImage 实例
webPreferences: { // 网页功能设置
webviewTag: true, // 是否使用<webview>标签 在一个独立的 frame 和进程里显示外部 web 内容
webSecurity: false, // 禁用同源策略
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true // 是否启用node集成 渲染进程的内容有访问node的能力,建议设置为true, 否则在render页面会提示node找不到的错误
}
}) // 加载应用 --打包react应用后,__dirname为当前文件路径
// mainWindow.loadURL(url.format({
// pathname: path.join(__dirname, './build/index.html'),
// protocol: 'file:',
// slashes: true
// })); // 因为我们是加载的react生成的页面,并不是静态页面
// 所以loadFile换成loadURL。
// 加载应用 --开发阶段 需要运行 yarn start
mainWindow.loadURL('http://localhost:3000'); // 解决应用启动白屏问题
mainWindow.on('ready-to-show', () => {
mainWindow.show();
mainWindow.focus();
}); // 当窗口关闭时发出。在你收到这个事件后,你应该删除对窗口的引用,并避免再使用它。
mainWindow.on('closed', () => {
mainWindow = null;
}); // 在启动的时候打开DevTools
mainWindow.webContents.openDevTools()
} app.allowRendererProcessReuse =true; // This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() =>{
console.log('qpp---whenready');
createWindow();}) // Quit when all windows are closed.
app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
console.log('window-all-closed');
if (process.platform !== 'darwin') app.quit()
}) app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
}) // In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

package.json

这时候我们来修改package.json文件。

  1. 配置启动文件,添加main字段,我们这里也就是main.js文件。如果没有添加,Electron 将尝试加载包含在package.json文件目录中的index.js文件。
  2. 配置运行命令,使用"electron": "electron ." 区别于react的启动命令"start": "react-scripts start",
  3. 安装concurrently: yarn add concurrently
{
...
"main": "main.js", // 配置启动文件
"homepage": ".", // 设置应用打包的根路径
"scripts": {
"start": "react-scripts start", // react 启动命令
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"electron": "electron .", // electron 启动命令
"dev": "concurrently \"npm run start\" \"npm run electron\""
},
}

preload.js

window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
} for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
}
})

此时的工程架构

2.5 运行electron项目

  1. yarn start 然后再开一个终端yarn electron
  2. 或者是npm run dev

其实我们就可以看出Electron就是一个应用套了一个谷歌浏览器壳子,然后里面是前端页面。

2.6 打包项目

使用electron-packager依赖:

yarn add --dev electron-packager

package.json配置打包命令:

"package": "electron-packager . bleak-electron-app --platform=win32 --arch=x64 --overwrite --electron-version=18.1.0 --icon=./public/favicon.ico"

配置解释:

electron-packager <应用目录> <应用名称> <打包平台> <架构x86 还是 x64> <架构> <electron版本> <图标>
overwrite 如果输出目录已经存在,替换它

然后运行命令:

yarn package

打包时间慢的话可按照下面两种方式优化:

方法1:
在执行electron-packager前先运行set ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
方法2:
在electron-packager命令行加入参数--download.mirrorOptions.mirror=https://npm.taobao.org/mirrors/electron/
(Windows x64)完整版如下:
electron-packager . bleak-electron-app --platform=win32 --arch=x64 --overwrite --electron-version=18.0.4 --download.mirrorOptions.mirror=https://npm.taobao.org/mirrors/electron/

然后运行bleak-electron-app-win32-x64里面的exe文件就可以了。

3. 自动刷新页面

当你用react开发的时候,网页内容会自动热更新,但是electron窗口的main.js中代码发生变化时不能热加载。

安装插件electron-reloader:

yarn add --dev electron-reloader
npm install --save-develectron-reloader

然后在路口引用插件:

const reloader = require('electron-reloader')
reloader(module)

就可以实现electron插件热更新。

4. 主进程和渲染进程

Electron运行package.json的main脚本的进程称为主进程。在主进程中运行的脚本通过创建web页面来展示用户节面,一个Electron应用总是有且只有一个主进程。

由于Electron使用了Chromium来展示web页面,所以Chromium的多进程架构也被使用到,每个Electron钟大哥web页面运行在它的叫渲染进程的进程中。

在普通的浏览器中,web页面无法访问操作系统的原生资源。然而Electron的用户在Node.js的API支持下可以在页面中和操作系统进行一些底层交互。

ctrl + shift + i 打开渲染进程调试(devtools)

默认打开调试:

// 在启动的时候打开DevTools
mainWindow.webContents.openDevTools()

5.定义原生菜单、顶部菜单

5.1 自定义菜单

可以使用Menu菜单来创建原生应用菜单和上下文菜单。

  1. 首先判断是什么平台,是mac还是其他:
const isMac = process.platform === 'darwin'
  1. 创建菜单模板:

其是由一个个MenuItem组成的,可以在菜单项官网API查看。

const template = [
// { role: 'appMenu' }
// 如果是mac系统才有
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
// { role: 'fileMenu' }
{
label: '文件',
submenu: [
isMac ? { role: 'close' } : { role: 'quit', label: '退出' }
]
},
// { role: 'editMenu' }
{
label: '编辑',
submenu: [
{ role: 'undo', label: '撤消' },
{ role: 'redo', label: '恢复' },
{ type: 'separator' },
{ role: 'cut', label: '剪切' },
{ role: 'copy', label: '复制' },
{ role: 'paste', label: '粘贴' },
...(isMac ? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
] : [
{ role: 'delete', label: '删除' },
{ type: 'separator' },
{ role: 'selectAll', label: '全选' }
])
]
},
// { role: 'viewMenu' }
{
label: '查看',
submenu: [
{ role: 'reload', label: '重新加载' },
{ role: 'forceReload', label: '强制重新加载' },
{ role: 'toggleDevTools', label: '切换开发工具栏' },
{ type: 'separator' },
{ role: 'resetZoom', label: '原始开发工具栏窗口大小' },
{ role: 'zoomIn', label: '放大开发工具栏窗口'},
{ role: 'zoomOut', label: '缩小开发工具栏窗口' },
{ type: 'separator' },
{ role: 'togglefullscreen', label:'切换开发工具栏全屏' }
]
},
// { role: 'windowMenu' }
{
label: '窗口',
submenu: [
{ role: 'minimize', label:'最小化' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
] : [
{ role: 'close', label: '关闭' }
])
]
},
{
role: 'help',
label: '帮助',
submenu: [
{
label: '从Electron官网学习更多',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
]
  1. 根据模板创建menu:
const menu = Menu.buildFromTemplate(template)
  1. 设置菜单:
Menu.setApplicationMenu(menu)

5.2 给菜单定义点击事件

可以通过click属性来设置点击事件

5.3 抽离菜单定义

创建一个menu.js:

const {app, Menu } = require('electron')

const isMac = process.platform === 'darwin'

const template = [
// { role: 'appMenu' }
// 如果是mac系统才有
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
// { role: 'fileMenu' }
{
label: '文件',
submenu: [
isMac ? { role: 'close' } : { role: 'quit', label: '退出' }
]
},
// { role: 'editMenu' }
{
label: '编辑',
submenu: [
{ role: 'undo', label: '撤消' },
{ role: 'redo', label: '恢复' },
{ type: 'separator' },
{ role: 'cut', label: '剪切' },
{ role: 'copy', label: '复制' },
{ role: 'paste', label: '粘贴' },
...(isMac ? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
] : [
{ role: 'delete', label: '删除' },
{ type: 'separator' },
{ role: 'selectAll', label: '全选' }
])
]
},
// { role: 'viewMenu' }
{
label: '查看',
submenu: [
{ role: 'reload', label: '重新加载' },
{ role: 'forceReload', label: '强制重新加载' },
{ role: 'toggleDevTools', label: '切换开发工具栏' },
{ type: 'separator' },
{ role: 'resetZoom', label: '原始开发工具栏窗口大小' },
{ role: 'zoomIn', label: '放大开发工具栏窗口'},
{ role: 'zoomOut', label: '缩小开发工具栏窗口' },
{ type: 'separator' },
{ role: 'togglefullscreen', label:'切换开发工具栏全屏' }
]
},
// { role: 'windowMenu' }
{
label: '窗口',
submenu: [
{ role: 'minimize', label:'最小化' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
] : [
{ role: 'close', label: '关闭' }
])
]
},
{
role: 'help',
label: '帮助',
submenu: [
{
label: '从Electron官网学习更多',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
] const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)

然后在main.jscreateWindow的方法使用require调用:

const createWindow = () => {
......
require('./menu')
......
}

5.4 自定义顶部菜单

我们可以自定义顶部菜单,通过以下两个步骤进行:

  • 先通过frame创建无边框窗口。
function createWindow () {
const mainWindow = new BrowserWindow({
......
frame: false
})
}
  • 然后再通过前端页面布局设置顶部菜单

如果想让顶部菜单支持拖拽,可以加如下css:

-webkit-app-region: drag;

5.5 在渲染进程中使用主进程方法remote和electron(点击创建新窗口)

我们想要通过remote来使用主进程方法和功能。

  1. 首先要安装@electron/remote
yarn add @electron/remote
  1. 在主进程main.js中配置remote:
const remote = require("@electron/remote/main")
remote.initialize() const createWindow = () => {
let mainWindow = new BrowserWindow({
......
webPreferences: { // 网页功能设置
......
nodeIntegration: true, // 是否启用node集成 渲染进程的内容有访问node的能力,建议设置为true, 否则在render页面会提示node找不到的错误
contextIsolation : false, //允许渲染进程使用Nodejs
}
})
remote.enable(mainWindow.webContents)
}
  1. 在渲染进程中使用remote的BrowserWindow:App.tsx
import React from 'react'
// 使用electron的功能
// const electron = window.require('electron')
// 使用remote
const { BrowserWindow } = window.require("@electron/remote") export default function App() {
const openNewWindow = () => {
new BrowserWindow({
width:500,
height:500
})
} return (
<div>
App
<div>
<button onClick={openNewWindow}>点我开启新窗口</button>
</div>
</div>
)
}

我们想要通过使用electron提供给渲染进程的API:

const electron = window.require('electron')

然后从electron中提取方法。

5.6 点击打开浏览器

使用electron中的shell可以实现此功能:

import React from 'react'
// 使用electron的功能
// const electron = window.require('electron')
// 使用remote
// const { BrowserWindow } = window.require("@electron/remote")
// 使用shell
const { shell } = window.require('electron') export default function App() {
const openNewWindow = () => {
shell.openExternal('https://www.baidu.com')
} return (
<div>
App
<div>
<button onClick={openNewWindow}>点我开启新窗口打开百度</button>
</div>
</div>
)
}

6. 打开对话框读取文件

6.1 读取文件

主进程中的dialog模块可以显示用于打开和保存文件、警报等的本机系统对话框。

因为dialog模块属于主进程,如果我们在渲染进程中需要使用则需要使用remote模块。

App.tsx

import React,{ useRef } from 'react'
// 使用electron的功能
// const electron = window.require('electron') // 使用remote
// const remote = window.require('@electron/remote')
// const { BrowserWindow } = window.require("@electron/remote")
const { dialog } = window.require("@electron/remote") // 使用shell
const { shell } = window.require('electron') // 使用fs
const fs = window.require('fs') export default function App() {
// ref
const textRef = useRef<HTMLTextAreaElement | null>(null) const openNewWindow = () => {
shell.openExternal('https://www.baidu.com')
} const openFile = () => {
const res = dialog.showOpenDialogSync({
title: '读取文件', // 对话框窗口的标题
buttonLabel: "读取", // 按钮的自定义标签, 当为空时, 将使用默认标签。
filters: [ // 用于规定用户可见或可选的特定类型范围
//{ name: 'Images', extensions: ['jpg', 'png', 'gif', 'jpeg', 'webp'] },
//{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
{ name: 'Custom File Type', extensions: ['js'] },
{ name: 'All Files', extensions: ['*'] },
]
})
const fileContent:string = fs.readFileSync(res[0]).toString();
(textRef.current as HTMLTextAreaElement).value = fileContent
} return (
<div>
App Test
<div>
<button onClick={openNewWindow}>点我开启新窗口打开百度</button>
</div>
<div>
<button onClick={openFile}>打开文件</button>
<textarea ref={textRef}></textarea>
</div>
</div>
)
}

6.2 保存文件

保存文件需要使用dialog函数里的showSaveDialogSync,与之前的读取文件所用到的showOpenDialogSync类似:

const saveFile = () => {
const res = dialog.showSaveDialogSync({
title:'保存文件',
buttonLable: "保存",
filters: [
{ name: 'index', extensions: ['js']}
]
})
fs.writeFileSync(res, textRef.current?.value)
}

7. 定义快捷键

7.1 主线程定义

引入globalShortcut

const {app, BrowserWindow, nativeImage, globalShortcut } = require('electron')

注册快捷键打印字符串、窗口最大化、窗口最小化、关闭窗口。

const createWindow = () => {
......
// 注册快捷键
globalShortcut.register('CommandOrControl+X', () => {
console.log('CommandOrControl + X is pressed')
}) globalShortcut.register('CommandOrControl+M', () => {
mainWindow.maximize()
}) globalShortcut.register('CommandOrControl+T', () => {
mainWindow.unmaximize()
}) globalShortcut.register('CommandOrControl+H', () => {
mainWindow.close()
}) // 检查快捷键是否注册成功
// console.log(globalShortcut.isRegistered('CommandOrControl+X'))
} // 将要退出时的生命周期,注销快捷键
app.on('will-quit', () => {
// 注销快捷键
globalShortcut.unregister('CommandOrControl+X')
// 注销所有快捷键
globalShortcut.unregisterAll()
})

7.2在渲染进程中定义

通过retmote来定义

const { globalShortcut } = window.require("@electron/remote")

globalShortcut.register("Ctrl+O", () => {
console.log('ctrl+O is pressed.')
})

8. 主进程和渲染进程通讯

在渲染进程使用ipcRenderer,主进程使用ipcMain,可以实现主进程和渲染进程的通讯:

App.tsx

......
import React,{ useState, useRef } from 'react'
const { shell, ipcRenderer } = window.require('electron')
export default function App() {
// state
const [windowSize, setWindowSize] = useState('max-window')
......
// 传参
const maxWindow = () => {
ipcRenderer.send('max-window', windowSize);
if(windowSize === 'max-window') {
setWindowSize('unmax-window')
} else {
setWindowSize('max-window')
}
}
......
return (
<div>
<div>
<button onClick={maxWindow}>与主进程进行通讯,窗口最大化或取消窗口最大化</button>
</div>
</div>
</div>
)
}

main.js

const {app, BrowserWindow, nativeImage, globalShortcut, ipcMain } = require('electron')
const createWindow = () => {
let mainWindow = ......
......
// 定义通讯事件
ipcMain.on('max-window', (event, arg) => {
if(arg === 'max-window') {
mainWindow.maximize()
} else if (arg === 'unmax-window') {
mainWindow.unmaximize()
}
})
......
}
......

Electron结合React和TypeScript进行开发的更多相关文章

  1. 从零配置webpack(react+less+typescript+mobx)

    本文目标 从零搭建出一套支持react+less+typescript+mobx的webpack配置 最简化webpack配置 首页要初始化yarn和安装webpack的依赖 yarn init -y ...

  2. React Hooks Typescript 开发的一款 H5 移动端 组件库

    CP design 使用 React hooks Typescript 开发的一个 H5 移动端 组件库 English | 简体中文 badge button icon CP Design Mobi ...

  3. electron教程(番外篇一): 开发环境及插件, VSCode调试, ESLint + Google JavaScript Style Guide代码规范

    我的electron教程系列 electron教程(一): electron的安装和项目的创建 electron教程(番外篇一): 开发环境及插件, VSCode调试, ESLint + Google ...

  4. React Native – 使用 JavaScript 开发原生应用

    前不久,Facebook 在F8开发者大会上正式开源了 React Native 项目.不过目前只有 iOS 版,Android 版还需要再等一段时间,这是最新的用 JavaScript 语言开发原生 ...

  5. 实例讲解基于 React+Redux 的前端开发流程

    原文地址:https://segmentfault.com/a/1190000005356568 前言:在当下的前端界,react 和 redux 发展得如火如荼,react 在 github 的 s ...

  6. React Router 4.x 开发,这些雷区我们都帮你踩过了

    前言 在前端框架层出不穷的今天,React 以其虚拟 DOM .组件化开发思想等特性迅速占据了主流位置,成为前端开发工程师热衷的 Javascript 库.作为 React 体系中的重要组成部分:Re ...

  7. react + react-router + less +antd 开发环境

    react + react-router + less +antd 开发环境搭建 1.基于create-reacte-app,需要先安装这个脚手架,然后初始化项目. 2.进入项目目录,首先 npm r ...

  8. TypeScript进阶开发——ThreeJs基础实例,从入坑到入门

    前言 我们前面使用的是自己编写的ts,以及自己手动引入的jquery,由于第三方库采用的是直接引入js,没有d.ts声明文件,开发起来很累,所以一般情况下我们使用npm引入第三方的库,本文记录使用np ...

  9. React与Typescript整合

    0. Typescript Typescript对于前端来说可以说是越来越重要了,前端的很多项目都用Typescript进行了重构.这主要得益于Typescript有比较好的类型支持,在编码的过程中可 ...

随机推荐

  1. 『现学现忘』Docker基础 — 33、Docker数据卷容器的说明与共享数据原理

    目录 1.数据卷容器的说明 2.数据卷容器共享数据原理 3.总结 4.练习:MySQL实现数据共享 1.数据卷容器的说明 (1)什么是数据卷容器 一个容器中已经创建好的数据卷,其它容器通过这个容器实现 ...

  2. automake的使用1

    安装命令: sudo apt install automake autoconfig 简单的例子 automake实例: helloworld.c #include <stdio.h> # ...

  3. MySQL 获取每月多少日的sql写法

    # 写法1 指定 年月 的共有多少日 select DATEDIFF(DATE_ADD(CONCAT( 2020, '-', '03','-','01'),INTERVAL 1 MONTH),CONC ...

  4. Mysql的索引及优化

    一:四种存储引擎: mysql使用 show engines查询其存储引擎: 功  能 MYISAM Memory InnoDB Archive 存储限制 256TB RAM 64TB None 支持 ...

  5. 如果我不输入<!DOCTYPE HTML>,HTML 5能工作吗?

    No,浏览器将无法识别HTML文件,并且HTML 5标签将无法正常工作.

  6. resion 学习笔记

    resin是一个非常流行的web引用服务器,对servlet和jsp提供了良好的支持,自身采用java开发,支持集群,还支持PHP. resin分为普通版和专业版,主要区别是专业版支持缓存和负载均衡. ...

  7. 图灵机器人 V1 和 V2 接入方法

    API1.0使用方法: import requests import json import yuyinhecheng as hc def Tuling(words):     Tuling_API_ ...

  8. 『忘了再学』Shell基础 — 6、Bash基本功能(输入输出重定向)

    目录 1.Bash的标准输入输出 2.输出重定向 (1)标准输出重定向 (2)标准错误输出重定向 (3)正确输出和错误输出同时保存 3.输入重定向 1.Bash的标准输入输出 我们前边一直在说,在Li ...

  9. C语言 | 栈区空间初探

    栈的定义 栈(stack)又名堆栈,堆栈是一个特定的存储区或寄存器,它的一端是固定的,另一端是浮动的 .对这个存储区存入的数据,是一种特殊的数据结构.所有的数据存入或取出,只能在浮动的一端(称栈顶)进 ...

  10. PC端操作系统、移动端操作系统、嵌入式操作系统

    左侧部分已是历史的操作系统,右侧的还是活跃的操作系统.安卓系统Android 是Google开发的基于Linux平台的开源手机操作系统.它包括操作系统.用户界面和应用程序-- 移动电话工作所需的全部软 ...