Electron+Mobx+React 開發(fā)記錄

nojsja.gitee.io/blogs 更多內(nèi)容已經(jīng)在個人博客發(fā)布,請知悉

Hello World

> Contents

  1. 前言
  2. 開發(fā)環(huán)境搭建
  3. 引入Webpack4.0前端打包工具
  4. Electron代碼結(jié)構(gòu)和代碼熱更新
  5. 前端界面React + Mobx 代碼結(jié)構(gòu)和熱更新
  6. Linux桌面客戶端開發(fā)遇到的問題

前言


最近桌面系統(tǒng)從Ubuntu18.04切換到了Manjaro Linux 17癌蚁,之前聽說Manjaro的軟件豐富幻梯,倉庫更新及時兜畸,很多常用軟件都能一鍵安裝(比如QQ,微信)碘梢,同時也支持主流的Linux桌面環(huán)境:Gnome咬摇、KDE、Cinnamon煞躬、Mate肛鹏、Deepin等等,安裝了Gnome版本的Manjaro之后發(fā)現(xiàn)果然還不錯恩沛。系統(tǒng)安裝好后配置比較繁瑣在扰,就想給Manjaro寫一個GUI客戶端工具用于安裝常用軟件和作為簡單的系統(tǒng)管理工具 - electronux
作為一名正直的前端開發(fā)人員,理所應(yīng)當(dāng)?shù)鼐蜏?zhǔn)備使用Electron + Node.js + React + Mobx + Webpack + Shell 來進行開發(fā)啦 ~ 目前仍然在開發(fā)中雷客,這篇文章用于記錄自己的環(huán)境搭建過程芒珠、一些對Electron+React開發(fā)的理解以及談?wù)勛约河龅降囊恍㎜inux桌面軟件開發(fā)時遇到的問題和解決辦法。

clean_detail.png
clean_search.png
electron_pssword.png
info_total.png
install_detail.png
install_list.png
install_permission.png
startup_list.png

開發(fā)環(huán)境搭建


代碼目錄結(jié)構(gòu)
electronux  
|---- [dir ] app ( 主代碼目錄 )
|----------- [dir ] app/configure ( 應(yīng)用配置更新 )
|----------- [dir ] app/runtime ( 運行數(shù)據(jù)文件 )
|
|----------- [dir ] app/services ( 后臺服務(wù)存放目錄 )
|------------------------ [dir ] app/services/middleware ( 一些中間處理件 )
|------------------------ [dir ] app/services/shell ( shell腳本存放目錄 )
|------------------------ [dir ] app/services/main-serv ( 主進程服務(wù) )
|------------------------ [dir ] app/services/render-serv ( 渲染進程服務(wù) )
|
|----------- [dir ] app/stores ( 前端狀態(tài)管理文件目錄 )
|----------- [dir ] app/styles  ( 公用樣式表文件 )
|----------- [dir ] app/utils  ( 公用工具函數(shù) )
|
|----------- [dir ] app/views  ( UI界面代碼 )
|------------------------ [dir ] app/views/module1  ( 界面模塊1 )
|------------------------ [dir ] app/views/module2  ( 界面模塊2)
|------------------------ [dir ] app/views/module3  ( 界面模塊3 )
|
|----------- [file] app/App.js  ( 前端應(yīng)用入口文件 )
|----------- [file] app/index.js ( 前端應(yīng)用熱加載文件 )
|
|---- [dir ] dist ( 前端代碼編譯打包文件存放目錄 )
|---- [dir ] resources ( 前端靜態(tài)資源存放目錄 )
|
|---- [file] .babelrc ( babel配置文件 )
|---- [file] .editorconfig (編輯器編碼規(guī)范文件)
|---- [file] .eslintrc ( 代碼格式檢查配置文件 )
|---- [file] .gitignore ( git忽略追蹤配置文件 )
|---- [file] electron-builder.json ( electron-builder打包配置文件 )
|---- [file] index.html  ( 應(yīng)用渲染入口頁面 )
|---- [file] index.js ( 應(yīng)用主進程入口文件 )
|---- [file] package.json (前端模塊和框架配置文件)
|---- [file] webpack.config.js (webpack開發(fā)環(huán)境配置文件)
|---- [file] webpack.prod.config.js  ( webpack生產(chǎn)環(huán)境配置文件 )

項目環(huán)境依賴配置文件
{
  "name": "electronux",
  "description": "linux manager-software powered by electron & react & Mobx ",
  "version": "1.0.0",
  "author": {
    "name": "nojsja",
    "email": "yangwei020154@gmail.com"
  },
  "scripts": {
    "start": "concurrently \"npm run start-dev\" \"npm run start-electron\"",
    "start-dev": "cross-env NODE_ENV=development webpack-dev-server",
    "start-electron": "nodemon --exec 'cross-env NODE_ENV=development electron --inspect=5858 index'",
    "start-production": "cross-env NODE_ENV=production electron --inspect=5858 index",
    "build-all": "npm run dist && npm run build",
    "dist": "cross-env NODE_ENV=production webpack --config webpack.prod.config.js",
    "build": "electron-builder -l"
  },
  "keywords": [
    "electron",
    "react",
    "mobx",
    "react-router",
    "webpack4"
  ],
  "license": "",
  "nodemonConfig": {
    "ignore": [
      "resources/*",
      "node_modules/*",
      "dist/*",
      "build/*",
      "app/stores/*",
      "app/styles/*",
      "app/services/shell/*",
      "app/configure/view.conf",
      "app/views/*",
      "app/App.js",
      "app/main.js",
      "app/index.js",
      "electron-builder.yml"
    ],
    "delay": "1000"
  },
  "dependencies": {
    "semantic-ui-css": "^2.4.0",
    "semantic-ui-react": "^0.82.5",
    "mobx": "^4.4.1",
    "mobx-react": "^5.2.8",
    "prop-types": "^15.6.2",
    "react": "^16.5.1",
    "react-dom": "^16.5.1",
    "react-hot-loader": "^4.3.8",
    "react-router": "^4.3.1",
    "react-router-dom": "^4.3.1",
    "history": "^4.7.2"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-eslint": "^10.0.1",
    "babel-loader": "^7.1.5",
    "babel-plugin-transform-decorators-legacy": "^1.3.5",
    "babel-preset-env": "^1.7.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "clean-webpack-plugin": "^0.1.19",
    "concurrently": "^3.6.1",
    "cross-env": "^5.2.0",
    "css-loader": "^0.28.11",
    "electron": "^2.0.9",
    "electron-builder": "^20.28.4",
    "eslint": "^5.6.1",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jsx-a11y": "^6.1.2",
    "eslint-plugin-react": "^7.11.1",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "file-loader": "^2.0.0",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.9.4",
    "nodemon": "^1.18.4",
    "sass-loader": "^7.1.0",
    "source-map-support": "^0.5.9",
    "style-loader": "^0.21.0",
    "url-loader": "^1.1.2",
    "webpack": "^4.19.0",
    "webpack-cli": "^2.1.5",
    "webpack-dev-server": "^3.1.8"
  }
}

引入Webpack4.0前端打包工具


webpack開發(fā)環(huán)境配置文件
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

// 拆分樣式文件
const extractSass = new ExtractTextPlugin({
  filename: 'style.scss.css',
});

const extractCss = new ExtractTextPlugin({
  filename: 'style.css',
});

module.exports = {
  devtool: 'source-map',
  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './app/index',
  ],
  mode: 'development',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
  },
  resolve: {
    alias: {
      resources: path.resolve(__dirname, 'resources'),
      app: path.resolve(__dirname, 'app'),
    },
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader'],
      },
      {
        test: /\.css$/,
        use: extractCss.extract({
          fallback: 'style-loader',
          use: 'css-loader',
          publicPath: '/',
        }),
      },
      {
        test: /\.scss$/,
        use: extractSass.extract({
          use: [{
            loader: 'css-loader',
          }, {
            loader: 'sass-loader',
          }],
          fallback: 'style-loader', // 在開發(fā)環(huán)境使用 style-loader
          publicPath: '/',
        }),
      },
      {
        test: /\.html$/,
        use: {
          loader: 'html-loader',
        },
      },
      {
        test: /\.(png|jpg|gif|svg|ico|woff|eot|ttf|woff2)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[path][name].[ext]',
            },
          },
        ],
      },
    ],
  },

  plugins: [
    extractSass,
    extractCss,
    new webpack.HotModuleReplacementPlugin(),
    new CleanWebpackPlugin(['dist']),
    new webpack.NamedModulesPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
  ],

  devServer: {
    host: 'localhost',
    port: 3000,
    historyApiFallback: true,
    hot: true,
  },
  target: 'electron-renderer',
};

Electron基本原理和代碼熱更新


Electron 運行 package.json 的 main 腳本的進程被稱為主進程搅裙。 在主進程中運行的腳本通過創(chuàng)建web頁面來展示用戶界面皱卓。 一個 Electron 應(yīng)用總是有且只有一個主進程。
由于 Electron 使用了 Chromium 來展示 web 頁面呈宇,所以 Chromium 的多進程架構(gòu)也被使用到好爬。 每個 Electron 中的 web 頁面運行在它自己的渲染進程中局雄。
在普通的瀏覽器中甥啄,web頁面通常在一個沙盒環(huán)境中運行,不被允許去接觸原生的資源炬搭。 然而 Electron 的用戶在 Node.js 的 API 支持下可以在頁面中和操作系統(tǒng)進行一些底層交互蜈漓。
進程使用 BrowserWindow 實例創(chuàng)建頁面。 每個 BrowserWindow 實例都在自己的渲染進程里運行頁面宫盔。 當(dāng)一個 BrowserWindow 實例被銷毀后融虽,相應(yīng)的渲染進程也會被終止。
主進程管理所有的web頁面和它們對應(yīng)的渲染進程灼芭。 每個渲染進程都是獨立的有额,它只關(guān)心它所運行的 web 頁面。
在頁面中調(diào)用與 GUI 相關(guān)的原生 API 是不被允許的彼绷,因為在 web 頁面里操作原生的 GUI 資源是非常危險的巍佑,而且容易造成資源泄露。 如果你想在 web 頁面里使用 GUI 操作寄悯,其對應(yīng)的渲染進程必須與主進程進行通訊萤衰,請求主進程進行相關(guān)的 GUI 操作。

創(chuàng)建主進程

在index.js文件中我們引入electron和所有的自定義模塊文件猜旬,并根據(jù)開發(fā)環(huán)境或是生產(chǎn)環(huán)境來進行主進程窗口加載脆栋,開發(fā)環(huán)境下使用http協(xié)議加載由webpack-dev-server啟動的http服務(wù)倦卖,生產(chǎn)環(huán)境下使用file協(xié)議加載本地由webpack打包好的前端bundle.js文件,所以開發(fā)環(huán)境下npm start指令其實主要是執(zhí)行了兩步操作椿争,一是啟動webpack-dev-server怕膛,此時已經(jīng)可以通過外部瀏覽器訪問到localhost:3000的http服務(wù),只不過我們實際是用electron之中的chromium瀏覽器來加載的秦踪,它與node.js主進程共享同一個chrome v8引擎嘉竟,所以理論上,在頁面加載后洋侨,你同樣可以在渲染進程中使用node.js API舍扰,比如用使用fs模塊訪問文件系統(tǒng)。

主進程代碼熱更新

我用了nodemon工具實現(xiàn)了主進程代碼熱更新希坚,如果不用nodemon工具那么 npm start-electron命令實際是執(zhí)行cross-env NODE_ENV=development electron index边苹,就是簡單的用electron啟動主進程文件,使用nodemon之后npm start-electron實際上是執(zhí)行nodemon --exec 'cross-env NODE_ENV=development electron index'裁僧,最后在package.json文件中增加一個nodemonConfig字段用于指定哪些文件需要納入nodemon監(jiān)聽即可个束。

=> package.json中定義的啟動腳本:

  "scripts": {
    "start": "concurrently \"npm run start-dev\" \"npm run start-electron\"",
    "start-dev": "cross-env NODE_ENV=development webpack-dev-server",
    "start-electron": "nodemon --exec 'cross-env NODE_ENV=development electron index'",
    "build": "npm run dist && npm run build-all",
    "dist": "cross-env NODE_ENV=production webpack  --config webpack.production.config.js",
    "build-all": "build -lmw"
  },

=> package.json中nodemonConfig字段

"nodemonConfig": {
    "ignore": [
      "resources/*",
      "node_modules/*",
      "dist/*",
      "app/stores/*",
      "app/styles/*",
      "app/services/shell/*",
      "app/configure/view.conf",
      "app/views/*",
      "app/App.js",
      "app/main.js",
      "app/index.js"
    ],
    "delay": "1000"
  },

=> 項目啟動文件index.js:

...
// 根據(jù)運行環(huán)境加載窗口 //
function loadWindow(window, env) {
  if (env === 'development') {
    // wait for webpack-dev-server start
    setTimeout(() => {
      window.loadURL(url.format({
        pathname: 'localhost:3000',
        protocol: 'http:',
        slashes: true,
      }));
      // window.webContents.openDevTools();
    }, 1e3);
  } else {
    window.loadURL(url.format({
      pathname: path.join(path.resolve(__dirname, './dist'), 'index.html'),
      protocol: 'file:',
      slashes: true,
    }));
  }
}

/* ------------------- main window ------------------- */

function createWindow() {
  const { width, height } = getAppConf();
  win = new BrowserWindow({
    width,
    height,
    title: 'electronux',
    autoHideMenuBar: true,
  });

  win.on('resize', () => {
    const [_width, _height] = win.getContentSize();
    viewConf.set({
      width: _width,
      height: _height,
    });
  });

  loadWindow(win, nodeEnv);
}

/* ------------------- electron event ------------------- */

app.on('ready', () => {
  if (nodeEnv === 'development') {
    sourceMapSupport.install();
  }
  createWindow();
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('will-quit', () => {
  viewConf.write().then(() => 0, (err) => {
    console.error(err);
    throw new Error('App quit: view-conf write error !');
  });
});

app.on('activate', () => {
  if (win === null) {
    createWindow();
  }
});

前端界面React + Mobx 代碼結(jié)構(gòu)和熱更新


代碼結(jié)構(gòu)
  1. App.js前端入口文件
    入口文件基本是整個前端應(yīng)用的關(guān)鍵點,我們使用mobx-react包提供的Provider組件加載整個應(yīng)用聊疲,并把各個應(yīng)用模塊(按功能劃分)的mobx store示例作為props屬性傳入Provider茬底,在各個組建中使用修飾器@inject就能直接使用store實例了,頁面層次比較多的話最好使用React Router進行路由管理获洲,值得注意的是React Router V4版本跟之前版本的理念和使用方式有很大區(qū)別阱表,可以去官網(wǎng)查閱相關(guān)文檔react-router4
/* ------------------- export global history ------------------- */
export const history = createHistory();

const stores = {
  install: new InstallState(),
  startup: new StartupState(),
  info: new InfoState(),
  clean: new CleanState(),
  pub: new PublicState(),
};

function App() {
  return (
    <Provider {...stores}>
      <Router history={history}>
        <Route path="/" component={HomePage} />
      </Router>
    </Provider>
  );
}

/* ------------------- export provider ------------------- */
export default App;

  1. mobx store 存儲
    這是項目其中一個系統(tǒng)清理模塊的mobx store,在store中被mobx監(jiān)聽的屬性最好結(jié)構(gòu)層次簡單贡珊、只有單一的功能劃分最爬,不要把一個屬性對象的嵌套寫得太深。開發(fā)時我們把UI界面的數(shù)據(jù)抽象成store中的數(shù)據(jù)時可能會下意識地根據(jù)頁面顯示狀態(tài)而把單個屬性對象寫得過于復(fù)雜门岔,但其實頁面顯示狀態(tài)只是邏輯的數(shù)據(jù)結(jié)構(gòu)爱致,我們在store中存儲的時候應(yīng)該盡量將這種邏輯數(shù)據(jù)結(jié)構(gòu)翻譯成扁平化的數(shù)據(jù)結(jié)構(gòu),然后再在各個屬性對象之間建立映射關(guān)系寒随。
    并且使用了mobx之后請盡量依賴mobx的數(shù)據(jù)引用監(jiān)聽自動更新特性糠悯,多寫computedautorun來自動生成數(shù)據(jù)妻往,使用action修飾一些需要更改store屬性的方法互艾。
class Clean {
  constructor() { }
  /* ------------------- observable ------------------- */

  // 所有檢查項目 //
  @observable items = {
    appCache: false,
    appLog: false,
    trash: false,
    packageCache: false,
  };

  // 主界面加載 //
  @observable loadingMain = false;

  // 清理路徑 //
  cleanPaths = {
    appCache: [`/home/${this.userinfo.username}/.cache`],
    appLog: ['/var/log/'],
    trash: [`/home/${this.userinfo.username}/.local/share/Trash/files`],
    packageCache: ['/var/cache/pacman/pkg'],
  }

  // 路徑模塊映射 //
  @observable cleanPathMap = {
    appCache: [], // '/var/log/pacman.log'
    appLog: [],
    trash: [],
    packageCache: [],
  }

  // 清理內(nèi)容 //
  @observable cleanContents = observable.map({})

  // 清理大小 //
  cleanSizes = {
    // '/var/log//pacman.log': '10kb',
  }

  // ---- 清理選項細(xì)節(jié)-數(shù)據(jù)對象邏輯樹結(jié)構(gòu) ---- //
  // @observable cleanDetails = {
  //   appCache: {
  //     url: [`/home/${this.userinfo.username}/.cache`], // 指定掃描路徑多個
  //     contents: { // 絕對路徑
  //       // '/var/cache/pacman/pkg/zsh-5.6.2-1-x86_64.pkg.tar.xz': false,
  //     },
  //     size: {
  //       // '/var/cache/pacman/pkg/zsh-5.6.2-1-x86_64.pkg.tar.xz': '10kb',
  //     },
  //   },
  //   appLog: {
  //     url: ['/var/log/'],
  //     contents: {
  //       // '/var/log//pacman.log': false,
  //     },
  //     size: {
  //       // '/var/log//pacman.log': '10kb',
  //     },
  //   }
  // }

  /* ------------------- static ------------------- */


  /* ------------------- computed ------------------- */

  // 獲取所有被選中的detail item //
  @computed get allCheckedDetail() {
    const a = [];
    this.cleanContents.forEach((v, k) => {
      if (v) a.push(k);
    });
    return a;
  }

  // 清理路徑詳細(xì)信息 //
  @computed get cleanDetail() {
    const result = [];
    Object.keys(this.cleanPathMap).forEach((item) => {
      if (this.items[item]) {
        const oneResult = {
          label: item,
          contents: [],
        };
        this.cleanPathMap[item].forEach((it) => {
          oneResult.contents.push({
            content: it,
            size: this.cleanSizes[it] || 0,
          });
        });

        result.push(oneResult);
      }
    });

    return result;
  }
}

export default Clean;

  1. 頁面組件劃分
    在views目錄下創(chuàng)建的各個目錄都是一個單獨的組件目錄,組件目錄下有一個組件入口文件和css樣式表文件以及其它子組件蒲讯,入口文件載入css文件和子組件忘朝,使用@inject修飾器后各個組件都可以獨立訪問mobx store實例,不必在父和子組件之間通過props進行逐級參數(shù)傳遞判帮,但是如果一個子組件依賴父組件來加工原始數(shù)據(jù)的話也可以使用props傳遞參數(shù)局嘁。
    使用了mobx之后溉箕,并不是說每個頁面需要使用的數(shù)據(jù)都有必要納入mobx store的管理,在我的代碼中只是把關(guān)鍵性數(shù)據(jù)以及關(guān)鍵性數(shù)據(jù)加工方法存入了store中悦昵,每個組件拿到store傳遞下來的數(shù)據(jù)后一些頁面狀態(tài)可能需要依賴組件各自的數(shù)據(jù)處理函數(shù)進行數(shù)據(jù)二次加工肴茄,我覺得這樣應(yīng)該會減輕store實例的負(fù)載壓力,非絕對中心化但指。比如在一個列表菜單組件中寡痰,這個組件的列表數(shù)據(jù)可以切換顯示和隱藏,但是控制這個列表顯示/隱藏的參數(shù)狀態(tài)visible沒有必要納入store實例管理棋凳,相對的管理這個列表組件的store實例只是存儲了列表數(shù)據(jù)的數(shù)組拦坠,以及一些必要的數(shù)據(jù)加工方法。

  2. 渲染進程和主進程ipc通信的問題
    頁面的每個渲染進程(ipcRender)剩岳,雖然說可以直接使用node.js原生模塊和api贞滨,但是不建議在渲染進程中過度使用原生模塊,一是因為一些node.js原生模塊并沒有考慮到進程安全的問題拍棕,第二個原因是渲染進程應(yīng)該專注處理頁面交互和數(shù)據(jù)處理問題晓铆,劃清代碼的功能區(qū)域,把和系統(tǒng)交互的問題交由主進程(ipcMain)處理绰播,把網(wǎng)絡(luò)數(shù)據(jù)請求也交由各自的service服務(wù)骄噪,減少不必要的模塊和數(shù)據(jù)耦合。渲染進程通過ipc通信向主進程發(fā)送處理請求蠢箩,主進程和service負(fù)責(zé)原始數(shù)據(jù)的獲取和網(wǎng)絡(luò)數(shù)據(jù)的傳輸链蕊,最后主進程通過ipc通信向?qū)?yīng)的渲染進程返回處理結(jié)果,service拿到的網(wǎng)絡(luò)數(shù)據(jù)也通過回調(diào)事件發(fā)送給渲染進程忙芒。項目中我把mobx store作為和主進程通信的橋梁示弓,mobx store向主進程發(fā)送信號讳侨,同時也在接收到主進程的ipc通信事件后再把主進程發(fā)回來的數(shù)據(jù)更新到各個observer呵萨。總之主進程和service服務(wù)負(fù)責(zé)系統(tǒng)交互跨跨、原始數(shù)據(jù)獲取和傳輸潮峦,渲染進程mobx store負(fù)責(zé)響應(yīng)信號和事件進行業(yè)務(wù)數(shù)據(jù)更新,各個view子組件只負(fù)責(zé)頁面渲染和用戶交互勇婴。

前端代碼熱更新
  1. webpack.config.js中啟動webpack-dev-server的熱更新功能
devServer: {
    host: 'localhost',
    port: 3000,
    historyApiFallback: true,
    hot: true,
  },
  1. 使用react-hot-loader的AppContainer組件
import { AppContainer } from 'react-hot-loader';

import 'semantic-ui-css/semantic.min.css';
import './styles/public.css';

import App from './App';

render(
  <AppContainer>
    <App />
  </AppContainer>,
  document.getElementById('root')
);

Linux桌面客戶端開發(fā)遇到的問題


使用node.js子進程child_process執(zhí)行shell腳本時無法取得系統(tǒng)root權(quán)限

項目中有的腳本需要使用root權(quán)限忱嘹,比如安裝和卸載軟件、掃描系統(tǒng)關(guān)鍵路徑耕渴,node.js里執(zhí)行shell腳本可以使用child_process模塊(node.js子進程)拘悦,child_process有幾個方法,spawn橱脸、exec础米、execFile分苇、fork,它們都能創(chuàng)建子進程以執(zhí)行指定文件或命令屁桑,具體的使用方法見Node API医寿,如果我們的腳本或指令需要使用root權(quán)限那可就麻煩了,桌面應(yīng)用又不是終端蘑斧,不可能用著用著讓用戶去終端輸入密碼吧靖秩,況且只是在開發(fā)環(huán)境下能看到終端輸出,應(yīng)用打包安裝運行起來后就是一個獨立的應(yīng)用程序了竖瘾,根本沒法輸入終端密碼沟突,仔細(xì)查閱了Electron官網(wǎng)API發(fā)現(xiàn)electron官方并沒有集成一個什么系統(tǒng)權(quán)限調(diào)用窗口之類的組件。沒辦法了捕传,這種情況下手動寫出了兩種方法:

  1. 調(diào)用獲取系統(tǒng)權(quán)限的系統(tǒng)自帶組件來執(zhí)行自定義命令和腳本
  2. 封裝一個彈窗組件來獲取用戶首次輸入的密碼事扭,然后手動把密碼記錄到文件中,應(yīng)用啟動的時候從文件中讀出密碼乐横,在使用child_process創(chuàng)建子進程的時候再監(jiān)聽子進程的輸出事件和錯誤事件求橄,然后把讀取到的保存在內(nèi)存中的密碼以輸入流(input stream)的形式發(fā)送給child_process創(chuàng)建的子進程,子進程讀取到輸入流傳入的密碼后就能繼續(xù)執(zhí)行了葡公。
electron_pssword.png
install_permission.png

具體代碼見github/nojsja/electronux/app/utils/sudo-prompt.js

感謝閱讀罐农,文章中出現(xiàn)的錯誤之處還請指正~

未完待續(xù).....
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市催什,隨后出現(xiàn)的幾起案子涵亏,更是在濱河造成了極大的恐慌,老刑警劉巖蒲凶,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件气筋,死亡現(xiàn)場離奇詭異,居然都是意外死亡旋圆,警方通過查閱死者的電腦和手機宠默,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灵巧,“玉大人搀矫,你說我怎么就攤上這事】桃蓿” “怎么了瓤球?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長敏弃。 經(jīng)常有香客問我卦羡,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任绿饵,我火速辦了婚禮逝薪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蝴罪。我一直安慰自己董济,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布要门。 她就那樣靜靜地躺著虏肾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪欢搜。 梳的紋絲不亂的頭發(fā)上封豪,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音炒瘟,去河邊找鬼吹埠。 笑死,一個胖子當(dāng)著我的面吹牛疮装,可吹牛的內(nèi)容都是我干的缘琅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼廓推,長吁一口氣:“原來是場噩夢啊……” “哼刷袍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起樊展,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤呻纹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后专缠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雷酪,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年涝婉,在試婚紗的時候發(fā)現(xiàn)自己被綠了哥力。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘁圈,死狀恐怖省骂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情最住,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布怠惶,位于F島的核電站涨缚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脓魏,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一兰吟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茂翔,春花似錦混蔼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至悔政,卻和暖如春晚吞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谋国。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工槽地, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芦瘾。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓捌蚊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親近弟。 傳聞我的和親對象是個殘疾皇子逢勾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容