1. 程式人生 > 前端設計 >使用 typescript 搭建一個基於 cra 的 electron 模板

使用 typescript 搭建一個基於 cra 的 electron 模板

前言

最近要寫一個 electron 專案,決定採用的技術棧是 react + typescript 。本來想著用市面上現有的模板進行搭建,比如electron-react-boilerplate。不過後來感覺這些模板有些龐大,就自己用create-react-app簡單搭建了個模板,寫篇文章記錄一下搭建過程。

準備工作

1.使用 cra 建立專案

npx create-react-app electron-template --typescript
複製程式碼

2.新增 electron

yarn add electron -D
複製程式碼

注: 因為下載源的原因,electron 經常會出現下載失敗的情況,建議更換下載源或手動下載。

yarn替換淘寶的下載源:

yarn config set electron_mirror https://npm.taobao.org/mirrors/electron/
複製程式碼

3.配置入口及目錄結構

在根路徑新建一個main目錄作為存放主程序相關程式碼,建立index.ts檔案作為入口

import { app,BrowserWindow } from 'electron'
import path from 'path'

let win: BrowserWindow | null = null
function createWindow() {
  // 建立瀏覽器視窗。
  win = new
BrowserWindow({ width: 800, height: 600, webPreferences: { // 加上這個就可以在渲染程序使用 winodw.require 引入 electron 模組 nodeIntegration: true } }) const urlLocation = 'http://localhost:3000' win.loadURL(urlLocation) // 當 window 被關閉,觸發該事件 win.on('closed',() => { win = null }) } app.on('ready'
,createWindow) // 當全部視窗關閉時退出程式 app.on('window-all-closed',() => { if (process.platform !== 'darwin') { app.quit() } }) app.on('activate',() => { if (win === null) { createWindow() } })
複製程式碼

開發模式配置

一般來說主程序用 JS 進行書寫,然後直接在package.json中配置main就可以直接執行,不過對於一個 TypeScriot 愛好者來說,開發不使用 TypeScript 渾身難受,所以這裡我使用了webpack對主程序程式碼打包然後再使用electron啟動。

1.配置 webpack

webpack 的配置我也使用的 TypeScript 進行書寫。

在這裡使用 cra 內建安裝過的babel-loader + @babel/preset-typescript作為編譯 TypeScript的方案就行了。在根路徑建立一個.babelrc檔案:

{
  "presets": ["@babel/preset-typescript"]
}
複製程式碼

然後在根路徑下建立config目錄,然後建立一個webpackConfig.ts檔案作為webpack的基本配置檔案:

// config/webpackConfig.ts
import { Configuration } from 'webpack'
import path from 'path'

class WebpackConfig implements Configuration {
  // 修改 target 為 electron-main,這樣才能正確打包主程序
  target: Configuration['target'] = 'electron-main'
  entry: Configuration['entry'] = [path.resolve(__dirname,'../main/index.ts')]
  output: Configuration['output'] = {
    filename: 'main.js',    path: path.resolve(__dirname,'../build')
  }
  resolve: Configuration['resolve'] = {
    alias: {
      '@': path.resolve(__dirname,'../src'),      '@@': path.resolve(__dirname,'../')
    },    extensions: ['.ts','.tsx','.js','.jsx','.json']
  }
  module: Configuration['module'] = {
    rules: [
      {
        test: /\.tsx?$/,        use: [
          'babel-loader'
        ]
      }
    ]
  }
  constructor(public mode: Configuration['mode'] = 'production') {}
}

export default WebpackConfig
複製程式碼

再建立一個啟動檔案(因為是用的 TypeScript,所以採用 api 式的啟動方式):

// config/start.ts
import webpack from 'webpack'

import WebpackConfig from './WebpackConfig'

const env =
  process.env.NODE_ENV === 'development' ? 'development' : 'production'

// 建立編譯時配置
const config = new WebpackConfig(env)

function errorHandler(err: Error & { details?: string },stats: webpack.Stats) {
  const info = stats.toJson()
  if (err || stats.hasErrors()) {
    if (err) {
      console.error(err.stack || err)
      if (err.details) {
        console.error(err.details)
      }
    }
    if (stats.hasWarnings()) {
      console.warn(info.warnings)
    }
    if (stats.hasErrors()) {
      console.error(info.errors)
    }
    return false
  } else {
    if (stats.hasWarnings()) {
      console.warn(info.warnings)
    }
    return true
  }
}

if (env === 'development') {
  // 通過watch來實時編譯
  const watching = webpack(config).watch(
    {
      aggregateTimeout: 300,      ignored: /node_modules/
    },    (err,stats) => {
      const flag = errorHandler(err,stats)
      if (flag) {
        console.log('webpack start successfully')
      } else {
        watching.close(() => {})
        throw Error('webpack start failed')
      }
    }
  )
} else {
  webpack(config).run((err: Error & { details?: string },stats) => {
    const flag = errorHandler(err,stats)
    if (flag) {
      console.log('webpack build successfully')
    } else {
      throw Error('webpack build failed')
    }
  })
}
複製程式碼

2.配置啟動指令碼

已經配置好了webpack,因為是使用的 TypeScript,所以使用ts-node執行啟動檔案,但是由於ts-loader執行時會預設使用專案根目錄的tsconfig.json檔案,而 cra 本身的tsconfig.json並不是打包成 commonjs,所以我們複製一份到 config 目錄中,修改一下module的配置:

// config/tsconfig.json
{
  "compilerOptions": {
    "target": "es5",    "lib": ["esnext"],    "allowJs": true,    "skipLibCheck": true,    "esModuleInterop": true,    "allowSyntheticDefaultImports": true,    "strict": true,    "forceConsistentCasingInFileNames": true,    "module": "commonjs",    "moduleResolution": "node",    "resolveJsonModule": true,    "noEmit": true,    "isolatedModules": true
  }
}
複製程式碼

開發模式的指令碼如下:

{
  "start:render": "cross-env BROWSER=none react-scripts start",  "start:watch-main": "cross-env NODE_ENV=development ts-node --project ./config/tsconfig.json ./config/start.ts",  "start:main": "wait-on http://localhost:3000 && nodemon --watch ./build --exec electron .",  "prestart": "rimraf ./build",  "start": "npm-run-all --parallel start:*",}
複製程式碼

配置說明:

  • 使用cross-env相容 windows 和 mac 環境下的環境變數。
  • 將 node 設定為開發環境使webpack實時監聽主程序相關檔案變化重新打包。
  • 使用nodemon監聽build目錄變化,每次目錄變化就重新執行主程式,同時使用wait-on等渲染程序執行完後再執行主程序。
  • 使用npm-run-all --parallel讓所有指令碼同步觸發,只用開一個埠就可啟動程式。

然後執行yarn start即可啟動程式。

應用打包配置

市面上主要的打包外掛有electron-builderelectron-packager,這裡我使用的electron-builder

yarn add electron-builder -D
複製程式碼

1.修改配置檔案

因為打包後與執行時的路徑有差別,所以這裡要修改一下之前的程式碼:

引入electron-is-dev判斷程式執行環境:

yarn add electron-is-dev -D
複製程式碼
// main/index.ts
// 該檔案的修改主要是關於引入了 electron-is-dev 後的操作
import { app,BrowserWindow } from 'electron'
import path from 'path'
import isDev from 'electron-is-dev'

let win: BrowserWindow | null = null
function createWindow() {
 // ...
  // 根據執行環境選擇
  const urlLocation = isDev
    ? 'http://localhost:3000'
    : `file://${path.join(__dirname,'./index.html')}` // 這裡的路徑是相對於打包後的檔案路徑

  win.loadURL(urlLocation)

  // ...
}

// ...
複製程式碼

然後增加一項webpack配置:

// config/webpackConfig.ts
import { Configuration } from 'webpack'
import path from 'path'

class WebpackConfig implements Configuration {
  // ...
   node: Configuration['node'] = {
    // 預設值是 'mock',會將其轉化為'/',我們這裡並不是服務端,應該設定為 false ,表示輸出檔案的目錄名,在打包程式碼裡面也要一直將其當作打包後的檔案路徑使用
    __dirname: false,    __filename: false
  }
  // ...
}

export default WebpackConfig
複製程式碼

再修改一下package.json中的配置項:

{
  "homepage": "./"  "build": {
    "appId": "testId",    "productName": "testName",    "files": [
      "build/**/*"
    ],    "extraMetadata": {
      "main": "./build/main.js"
    },    "directories": {
      "buildResources": "assets"
    }
  },}
複製程式碼

配置說明:

  • 修改homepage時因為 cra 打包時預設會以該項的路徑為基礎進行打包,因為我們打包後不是在伺服器使用,所以應該改為相對路徑。

  • 至於build選項則是electron builder的打包配置,具體細節要根據實際專案進行配置,該專案要能正常打包成桌面應用其實只需要上面的:

{
  "files": [
    "build/**/*"
  ],  "extraMetadata": {
    "main": "./build/main.js"
  }
}
複製程式碼

就可以成功進行應用打包了。其餘的配置項請自行參考 electron-builder 官網

2.配置打包指令碼

生產模式指令碼如下:

{
  "build:render": "react-scripts build",  "build:main": "cross-env NODE_ENV=productment ts-node --project ./config/tsconfig.json ./config/start.ts",  "build": "npm-run-all build:*",  "prepack": "npm run build",  "dist": "electron-builder",  "pack": "electron-builder --dir",  "package-all": "npm run build && electron-builder build -mwl",  "package-mac": "npm run  build && electron-builder build --mac",  "package-linux": "npm run  build && electron-builder build --linux",  "package-win": "npm run  build && electron-builder build --win --x64"
}
複製程式碼

然後執行yarn run pack就可在當前目錄生成符合使用者對應作業系統的應用了。

3.注意事項

由於electron-builder會將dependencies的依賴都打包進去,所以為了減小打包體積,儘量將依賴都放到devDependencies

總結

於此,就搭建了一個簡易的集成了 react 與 typescript 的開發 electron 開發環境。

該模版的全部程式碼已釋出到 github