阅读 1005

react+ts+webpack搭建electron项目

项目初始化

  1. 项目目录结构
├── 项目根目录
│ ├── app
│ │ ├── main      // 主进程模块
│ │ │    |—— electron.ts
│ │ │
│ │ ├── renderer  // 渲染进程模块
│ │ └── other // 需要之前打包好的一个项目使用webview引入(项目需要)
│ ├── package.json
│ └──
|___
复制代码
  1. 修改package.json
主要是注意里面的main字段,修改为以下(webpack打包的主线程代码所在位置)

"main": "./dist/electron.js"

复制代码
  1. 安装依赖

    • 安装electron

      yarn add electron@11.1.1 -D
      
      安装遇到问题
      
      可在根目录下增加.npmrc,增加以下内容
      
      registry=https://registry.npm.taobao.org/
      electron_mirror="https://npm.taobao.org/mirrors/electron/"
      
      
      复制代码
    • 安装react相关

      yarn add react react-router react-router-dom react-dom
      复制代码
    • 安装babel(使用babel-loader加ts的插件去编译ts)

      yarn add @babel/polyfill 
      yarn add @babel/core @babel/cli @babel/preset-env @babel/preset-react @babel/preset- typescript @babel/plugin-transform-runtime  @babel/plugin-transform-modules-commonjs -D
      复制代码
    • 根目录创建babel.config.js

          module.exports = {
              presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
              plugins: [
                '@babel/plugin-transform-runtime',
                    [
                  '@babel/plugin-transform-modules-commonjs', 
                  {
                    allowTopLevelThis: true,
                    loose: true,
                    lazy: true
                  }
            ],
            [
              'import', // antd按需加载
              {
                libraryName: 'antd',
                libraryDirectory: 'es',
                style: 'css'
              }
            ]
          ]
        }
      
      
      复制代码
    • 安装webpack相关(这里使用的还是4版本,5版本尝试了下,一堆报错改的心累就放弃了)

      yarn add webpack@4.44.1 webpack-cli@3.3.12 webpack-dev-server webpack-merge html-webpack-plugin@4.3.0 clean-webpack-plugin babel-loader -D
      
      复制代码
    • 创建webpack配置

      根目录下添加一个webpack文件夹,里面主要有以下文件

      • webpack.base.js
      • webpack.main.dev.js
      • webpack.main.prod.js
      • webpack.render.dev.js
      • webpack.render.prod.js
      // webpack.base.js
      
       const path = require('path')
       const { CleanWebpackPlugin } = require('clean-webpack-plugin')
       const MiniCssExtractPlugin = require('mini-css-extract-plugin')
      
       const isDev = process.env.NODE_ENV !== 'production'
      
       const getCssLoaders = importLoaders => [
         isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
         {
           loader: 'css-loader',
           options: {
             modules: false,
             sourceMap: isDev,
             importLoaders
           }
         }
       ]
      
       module.exports = {
         resolve: {
           extensions: ['.js', '.jsx', '.ts', '.tsx'],
           alias: {
             '@src': path.join(__dirname, '../', 'app/renderer'),
             '@viz': path.join(__dirname, '../', 'app/viz/index1.html')
           }
         },
         module: {
           rules: [
             {
               oneOf: [
                 {
                   test: /\.(js|jsx|ts|tsx)$/,
                   exclude: /node_modules/,
                   use: {
                     loader: 'babel-loader',
                     options: { cacheDirectory: true }
                   }
                 },
                 {
                   test: /\.css$/,
                   use: getCssLoaders(0)
                 },
                 {
                   test: /\.less$/,
                   use: [
                     ...getCssLoaders(1),
                     {
                       loader: 'less-loader',
                       options: {
                         sourceMap: isDev
                       }
                     }
                   ]
                 },
                 {
                   test: /\.(jpg|png|jpeg|gif)$/,
                   use: [
                     {
                       loader: 'file-loader',
                       options: {
                         name: '[name]_[hash].[ext]',
                         outputPath: 'images/'
                       }
                     }
                   ]
                 }
               ]
             }
           ]
         },
         plugins: [new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['**/*', '!electron.js'] })]
       }
      
      
      复制代码
      // webpack.main.dev.js
           const path = require('path')
           const baseConfig = require('./webpack.base.js')
           const webpackMerge = require('webpack-merge')
      
           const mainConfig = {
             entry: path.resolve(__dirname, '../app/main/electron.ts'),
             target: 'electron-main',
             output: {
               filename: 'electron.js',
               path: path.resolve(__dirname, '../dist')
             },
             devtool: 'inline-source-map',
             mode: 'development'
           }
      
           module.exports = webpackMerge.merge(baseConfig, mainConfig)
      
      
      复制代码
      // webpack.main.prod.js
      
              const path = require('path')
              const baseConfig = require('./webpack.base.js')
              const webpackMerge = require('webpack-merge')
              const webpack = require('webpack')
      
              const prodConfig = {
                entry: path.resolve(__dirname, '../app/main/electron.ts'),
                target: 'electron-main',
                output: {
                  filename: 'electron.js',
                  path: path.resolve(__dirname, '../dist')
                },
                devtool: 'inline-source-map',
                mode: 'production',
                plugins: [
                  new webpack.DefinePlugin({
                    __dirname: '__dirname'
                  })
                ]
              }
      
              module.exports = webpackMerge.merge(baseConfig, prodConfig)
      
      
      复制代码
      // webpack.render.dev.js
      
      const path = require('path')
      const webpackMerge = require('webpack-merge')
      const baseConfig = require('./webpack.base.js')
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      
      const devConfig = {
       mode: 'development',
       entry: {
         index: path.resolve(__dirname, '../app/renderer/app.tsx')
       },
       output: {
         filename: '[name].[hash].js',
         path: path.resolve(__dirname, '../dist')
       },
       target: 'electron-renderer',
       devtool: 'inline-source-map',
       devServer: {
         contentBase: path.join(__dirname, '../dist'),
         compress: true,
         host: '127.0.0.1',
         port: 7001,
         hot: true
       },
       plugins: [
         new HtmlWebpackPlugin({
           template: path.resolve(__dirname, '../app/renderer/index.html'),
           filename: path.resolve(__dirname, '../dist/index.html'),
           chunks: ['index']
         })
       ]
      }
      
      module.exports = webpackMerge.merge(baseConfig, devConfig)
      
      
      复制代码
      // webpack.render.prod.js
      
        const path = require('path')
        const webpackMerge = require('webpack-merge')
        const baseConfig = require('./webpack.base.js')
        const HtmlWebpackPlugin = require('html-webpack-plugin')
      
        const prodConfig = {
          mode: 'production',
          entry: {
            index: path.resolve(__dirname, '../app/renderer/app.tsx')
          },
          output: {
            filename: '[name].[hash].js',
            path: path.resolve(__dirname, '../dist')
          },
          target: 'electron-renderer',
          devtool: 'inline-source-map',
          plugins: [
            new HtmlWebpackPlugin({
              template: path.resolve(__dirname, '../app/renderer/index.html'),
              filename: path.resolve(__dirname, '../dist/index.html'),
              chunks: ['index']
            })
          ]
        }
      
        module.exports = webpackMerge.merge(baseConfig, prodConfig)
      
      
      复制代码
    • 编写一些代码

      • 在main文件夹下的electron.ts加入如下代码

        import path from 'path'
        import { app, BrowserWindow } from 'electron'
        import isDev from 'electron-is-dev'
        
        let mainWindow = null
        
        
        function createWindow() {
          mainWindow = new BrowserWindow({
            width: 1200,
            height: 800,
            webPreferences: {
              nodeIntegration: true,
              enableRemoteModule: true,
              contextIsolation: false,
              webviewTag: true
            }
          })
        
          if (isDev) {
            mainWindow.loadURL(`http://127.0.0.1:7001`)
            mainWindow.webContents.openDevTools()
          } else {
            mainWindow.loadURL(`file://${path.join(__dirname, '../dist/index.html')}`)
          }
        
          mainWindow.on('close', () => {
            mainWindow = null
          })
        }
        
        
        app.whenReady().then(() => {
          createWindow()
          app.on('activate', () => {
            if (BrowserWindow.getAllWindows().length === 0) createWindow()
          })
        })
        
        
        复制代码
      • 在renderer文件夹下添加index.html

           <!DOCTYPE html>
           <html lang="en">
             <head>
               <meta charset="UTF-8" />
               <meta http-equiv="X-UA-Compatible" content="IE=edge" />
               <meta name="viewport" content="width=device-width, initial-scale=1.0" />
               <title>Document</title>
             </head>
             <body>
               <div id="root"></div>
             </body>
           </html>
        
        复制代码
      • 在renderer文件夹下添加app.tsx

        import React from 'react';
        import ReactDOM from 'react-dom';
        import { HashRouter as Router, Route, Switch } from 'react-router-dom';
        
        function App() {
            return (
              <Router>
                <Switch>
                  <Route path="/">
                    <div>基础模板</div>
                    
                  </Route>
                </Switch>
              </Router>
            );
          }
        
          ReactDOM.render(<App />, document.getElementById('root'));
        
        
        复制代码
    • 配置package.json文件的脚本

      
          scripts": {
           "start": "concurrently \"yarn start:render\" \"wait-on http://127.0.0.1:7001 && yarn start:main \"", // 开发环境一个指令整体启动项目,需要安装concurrently和wait-on
           "start:main": "webpack --config ./webpack/webpack.main.dev.js && electron ./dist/electron.js", // 启动主进程
           "start:render": "webpack-dev-server --config ./webpack/webpack.render.dev.js", // 启动渲染进程
           "build": "yarn build:main && yarn build:render", // 打包同上
           "build:main": "webpack --config ./webpack/webpack.main.prod.js",
           "build:render": "webpack --config ./webpack/webpack.render.prod.js",
           "ele:pack": "electron-builder"  // 打包后使用electron-builder(需要安装)构建exe文件
           }
      
      复制代码
    • 一些其他的额外配置

      • elsint
      • husky
      • css采用css in js
      • 路由使用react-router-config来管理
    • 更多配置见git仓库

    • 主要参考掘金小册 Electron + React 从 0 到 1 实现简历平台实战

文章分类
前端