├── .env ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── script ├── build.js └── rollup.config.js ├── src ├── main │ └── index.ts └── render │ ├── App.vue │ ├── assets │ ├── electron.png │ └── logo.png │ ├── components │ └── HelloWorld.vue │ ├── index.html │ ├── index.scss │ ├── main.ts │ ├── public │ └── favicon.ico │ ├── router.ts │ ├── shims.d.ts │ ├── theme │ ├── fonts │ │ ├── element-icons.ttf │ │ └── element-icons.woff │ └── index.css │ ├── types │ └── Base.d.ts │ └── views │ ├── Create.vue │ └── Index.vue ├── tsconfig.json └── vite.config.ts /.env: -------------------------------------------------------------------------------- 1 | PORT = 8889 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | yarn-error.log 4 | .DS_Store 5 | 6 | package-lock.json 7 | yarn.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 qicoo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vite-electron-quick 2 | 👻 A fast Simple Vite2 Vue3 and Electron 11.x template. 3 | 4 | ⚡️ If you want to **fast** create a `Vite 2 + Vue 3 + Electron 11.x` project: 5 | 6 | 🚀 Why not use this? 7 | 8 | ## quick install 9 | 10 | ``` 11 | 12 | npx create-vite-electron 13 | 14 | yarn create vite-electron 15 | 16 | ``` 17 | 18 | ## run 19 | 20 | - yarn 21 | 22 | - yarn dev 23 | 24 | - yarn build 25 | 26 | ## change log 27 | 28 | #### 210331 29 | - Update `rollup-plugin-esbuild` version. 30 | #### 210219 31 | - resolve #6 32 | - change vite config file (alias => resolve.alias) 33 | - current version: 34 | - vite 2.0.1 35 | - electron 11.2.3 36 | 37 | #### 210113 38 | - fix Vite2 config bugs 39 | - vue-router next use `hash mode` instead of `history mode` 40 | 41 | #### 210108 42 | - use Vite2 43 | - use Electron 11.x 44 | 45 | #### 201029 46 | - fix build bug 47 | - if build slowly, you can use electron mirror 48 | 49 | #### 201026 50 | - vite update, update `vite.config.ts` file 51 | - add third lib `element-plus` import globally 52 | - please note when using **NODE MODULE**, may need to change `vite.config.ts -> optimizeDeps.allowNodeBuiltins` 53 | 54 | ## 原因 55 | 56 | - **vite 的快速发展以及更新,让其他能与之配合的项目各显神通,为什么不来试一试呢?** 57 | - vite 处于 beta 状态,目前还没有适合的脚手架与 electron 11.x 版本搭配使用 58 | - 业务上原本使用的 electron-vue 框架,由于使用的 electron 以及 webpack 版本较低,升级很麻烦。于是计划使用新的方案探索可能性。 59 | - 使用 electron 的业务,是为数不多可以不考虑兼容性的业务,能够在业务中使用最新的框架是不可多得的机会。 60 | 61 | ## 使用 3rd lib tips 62 | 63 | - **第三方库都可能会与 vite 以及 electron 框架本身造成冲突,请谨慎选择。** 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-electron", 3 | "version": "0.0.1", 4 | "main": "dist/main/build.js", 5 | "author": "MangoTsing", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "npm run dev:all", 9 | "dev:all": "concurrently -n=vue,ele -c=green,blue \"npm run dev:vue\" \"npm run dev:ele\"", 10 | "dev:vue": "vite", 11 | "dev:ele": "node script/build --env=development --watch", 12 | "build:vue": "vite build", 13 | "build:ele": "node script/build --env=production", 14 | "build": "npm run build:vue && npm run build:ele && electron-builder" 15 | }, 16 | "build": { 17 | "appId": "xxx@gmail.com", 18 | "electronDownload": { 19 | "mirror": "https://npm.taobao.org/mirrors/electron/" 20 | }, 21 | "files": [ 22 | "!node_modules", 23 | "dist/main/**", 24 | "dist/render/**" 25 | ], 26 | "mac": { 27 | "artifactName": "${productName}_setup_${version}.${ext}", 28 | "target": [ 29 | "dmg" 30 | ] 31 | }, 32 | "win": { 33 | "target": [ 34 | { 35 | "target": "nsis", 36 | "arch": [ 37 | "x64" 38 | ] 39 | } 40 | ], 41 | "artifactName": "${productName}_setup_${version}.${ext}" 42 | }, 43 | "nsis": { 44 | "oneClick": false, 45 | "perMachine": false, 46 | "allowToChangeInstallationDirectory": true, 47 | "deleteAppDataOnUninstall": false 48 | } 49 | }, 50 | "dependencies": { 51 | "electron-is-dev": "^1.2.0", 52 | "electron-store": "^6.0.0", 53 | "element-plus": "latest", 54 | "vue": "^3.0.5", 55 | "vue-router": "^4.0.0-beta.13" 56 | }, 57 | "devDependencies": { 58 | "@rollup/plugin-alias": "^3.1.1", 59 | "@rollup/plugin-commonjs": "^15.0.0", 60 | "@rollup/plugin-json": "^4.1.0", 61 | "@rollup/plugin-node-resolve": "^9.0.0", 62 | "chalk": "^4.1.0", 63 | "concurrently": "^5.3.0", 64 | "dotenv": "^8.2.0", 65 | "electron": "^11.0.0", 66 | "electron-builder": "^22.8.0", 67 | "electron-connect": "^0.6.3", 68 | "minimist": "^1.2.5", 69 | "ora": "^5.0.0", 70 | "rollup-plugin-esbuild": "^3.0.2", 71 | "sass": "^1.26.10", 72 | "typescript": "^3.9.7", 73 | "vite": "latest", 74 | "@vitejs/plugin-vue": "^1.0.4", 75 | "@vue/compiler-sfc": "^3.0.5", 76 | "wait-on": "^5.2.1" 77 | }, 78 | "keywords": [ 79 | "vite", 80 | "electron", 81 | "vue3", 82 | "rollup" 83 | ] 84 | } 85 | -------------------------------------------------------------------------------- /script/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * electron 打包 3 | */ 4 | const path = require('path'); 5 | const rollup = require('rollup'); 6 | const argv = require('minimist')(process.argv.slice(2)); 7 | const chalk = require('chalk'); 8 | const ora = require('ora'); 9 | const waitOn = require('wait-on'); 10 | const electron = require('electron-connect').server.create({ stopOnClose: true }); 11 | require('dotenv').config({ path: path.join(__dirname, '../.env') }) 12 | const options = require('./rollup.config'); 13 | const net = require('net'); 14 | const { URL } = require('url'); 15 | 16 | const opt = options(argv.env); 17 | const TAG = '[script/build.js]'; 18 | const spinner = ora(`${TAG} Electron build...`); 19 | 20 | const watchFunc = function () { 21 | // once here, all resources are available 22 | const watcher = rollup.watch(opt); 23 | watcher.on('change', filename => { 24 | const log = chalk.green(`change -- ${filename}`); 25 | console.log(TAG, log); 26 | }); 27 | watcher.on('event', ev => { 28 | if (ev.code === 'END') { 29 | // init-未启动、started-第一次启动、restarted-重新启动 30 | electron.electronState === 'init' ? electron.start() : electron.restart(); 31 | } else if (ev.code === 'ERROR') { 32 | console.log(ev.error) 33 | } 34 | }); 35 | } 36 | 37 | const resource = `http://localhost:${process.env.PORT}/index.html`; // 因为 vite 不会重定向到 index.html,所以直接写 index.html 路由。 38 | 39 | if (argv.watch) { 40 | waitOn({ 41 | resources: [resource], 42 | timeout: 5000, 43 | }, err => { 44 | if (err) { 45 | const { port, hostname } = new URL(resource); 46 | const serverSocket = net.connect(port || 80, hostname, () => { 47 | watchFunc(); 48 | }); 49 | serverSocket.on('error', (e) => { 50 | console.log(err); 51 | process.exit(1); 52 | }); 53 | } else { 54 | watchFunc(); 55 | } 56 | }); 57 | } else { 58 | spinner.start(); 59 | rollup.rollup(opt) 60 | .then(build => { 61 | spinner.stop(); 62 | console.log(TAG, chalk.green('Electron build successed.')); 63 | build.write(opt.output); 64 | }) 65 | .catch(error => { 66 | spinner.stop(); 67 | console.log(`\n${TAG} ${chalk.red('构建报错')}\n`, error, '\n'); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /script/rollup.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { nodeResolve } = require('@rollup/plugin-node-resolve') 3 | const commonjs = require('@rollup/plugin-commonjs') 4 | const esbuild = require('rollup-plugin-esbuild') 5 | const alias = require('@rollup/plugin-alias') 6 | const json = require('@rollup/plugin-json') 7 | 8 | module.exports = (env = 'production') => { 9 | return { 10 | input: path.join(__dirname, '../src/main/index.ts'), 11 | output: { 12 | file: path.join(__dirname, '../dist/main/build.js'), 13 | format: 'cjs', 14 | name: 'ElectronMainBundle', 15 | sourcemap: true, 16 | }, 17 | plugins: [ 18 | nodeResolve({ jsnext: true, preferBuiltins: true, browser: true }), // 消除碰到 node.js 模块时⚠警告 19 | commonjs(), 20 | json(), 21 | esbuild({ 22 | // All options are optional 23 | include: /\.[jt]sx?$/, // default, inferred from `loaders` option 24 | exclude: /node_modules/, // default 25 | // watch: process.argv.includes('--watch'), // rollup 中有配置 26 | sourceMap: false, // default 27 | minify: process.env.NODE_ENV === 'production', 28 | target: 'es2017', // default, or 'es20XX', 'esnext' 29 | jsxFactory: 'React.createElement', 30 | jsxFragment: 'React.Fragment', 31 | // Like @rollup/plugin-replace 32 | define: { 33 | __VERSION__: '"x.y.z"' 34 | }, 35 | // Add extra loaders 36 | loaders: { 37 | // Add .json files support 38 | // require @rollup/plugin-commonjs 39 | '.json': 'json', 40 | // Enable JSX in .js files too 41 | '.js': 'jsx' 42 | }, 43 | }), 44 | alias({ 45 | entries: [ 46 | { find: '@main', replacement: path.join(__dirname, '../src/main'), }, 47 | ] 48 | }) 49 | ], 50 | external: [ 51 | 'crypto', 52 | 'assert', 53 | 'fs', 54 | 'util', 55 | 'os', 56 | 'events', 57 | 'child_process', 58 | 'http', 59 | 'https', 60 | 'path', 61 | 'electron', 62 | ], 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * electron 主文件 3 | */ 4 | import { join } from 'path' 5 | import { app, BrowserWindow, ipcMain } from 'electron' 6 | import is_dev from 'electron-is-dev' 7 | import dotenv from 'dotenv' 8 | import Store from 'electron-store' 9 | 10 | const store = new Store() 11 | ipcMain.on('store:set', async (e, args) => { 12 | store.set(args.key, args.value) 13 | }) 14 | ipcMain.handle('store:get', async (e, args) => { 15 | const value = await store.get(args) 16 | return value 17 | }) 18 | ipcMain.on('store:delete', async (e, args) => { 19 | store.delete(args) 20 | }) 21 | 22 | dotenv.config({ path: join(__dirname, '../../.env') }) 23 | 24 | let win = null 25 | 26 | class createWin { 27 | // 创建浏览器窗口 28 | constructor () { 29 | win = new BrowserWindow({ 30 | width: 330, 31 | height: 700, 32 | webPreferences: { 33 | nodeIntegration: true, 34 | enableRemoteModule: true, 35 | }, 36 | }) 37 | 38 | const URL = is_dev 39 | ? `http://localhost:${process.env.PORT}` // vite 启动的服务器地址 40 | : `file://${join(__dirname, '../../dist/render/index.html')}` // vite 构建后的静态文件地址 41 | 42 | win.loadURL(URL) 43 | } 44 | } 45 | 46 | app.whenReady().then(() => new createWin()) 47 | 48 | 49 | app.on('window-all-closed', () => { 50 | if (process.platform !== 'darwin') { 51 | app.quit() 52 | } 53 | }) 54 | 55 | app.on('activate', () => { 56 | if (BrowserWindow.getAllWindows().length === 0) { 57 | new createWin() 58 | } 59 | }) -------------------------------------------------------------------------------- /src/render/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | -------------------------------------------------------------------------------- /src/render/assets/electron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTsing/vite-electron-quick/4b9c8cdc1e73ffe7dc10a7a9e2172eecd46f613c/src/render/assets/electron.png -------------------------------------------------------------------------------- /src/render/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTsing/vite-electron-quick/4b9c8cdc1e73ffe7dc10a7a9e2172eecd46f613c/src/render/assets/logo.png -------------------------------------------------------------------------------- /src/render/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /src/render/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite Electron Quick 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/render/index.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | font-family: Avenir, Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | text-align: center; 6 | color: #00c13c; 7 | margin-top: 20px; 8 | } 9 | -------------------------------------------------------------------------------- /src/render/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import './index.scss' 4 | import ElementUI from 'element-plus'; 5 | import 'element-plus/lib/theme-chalk/index.css'; 6 | import './theme/index.css' 7 | import router from './router' 8 | 9 | // vite 使用 esm 编译 import, electron 及 node.js 内置模块用 require 形式 10 | const electron = require("electron") 11 | const ipc = electron.ipcRenderer 12 | 13 | ipc.send('store:set', { key: 'foo.bar', value: '👩' }) 14 | ipc.invoke('store:get', 'foo').then((res:string) => { 15 | console.log(res) 16 | }) 17 | ipc.send('store:delete', 'foo') 18 | ipc.invoke('store:get', 'foo').then((res:string) => { 19 | console.log(res) 20 | }) 21 | 22 | const app = createApp(App) 23 | 24 | app.use(router) 25 | app.use(ElementUI) 26 | app.mount('#app') 27 | -------------------------------------------------------------------------------- /src/render/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTsing/vite-electron-quick/4b9c8cdc1e73ffe7dc10a7a9e2172eecd46f613c/src/render/public/favicon.ico -------------------------------------------------------------------------------- /src/render/router.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router' 2 | const router = createRouter({ 3 | history: createWebHashHistory(), 4 | routes: [ 5 | { 6 | path: '/', 7 | name: 'index', 8 | component: () => import("./views/Index.vue"), 9 | meta: { 10 | title: '首页' 11 | } 12 | }, 13 | { 14 | path: '/create', 15 | name: 'create', 16 | component: () => import("./views/Create.vue"), 17 | meta: { 18 | title: '创建' 19 | } 20 | } 21 | ] 22 | }) 23 | 24 | export default router 25 | -------------------------------------------------------------------------------- /src/render/shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /src/render/theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTsing/vite-electron-quick/4b9c8cdc1e73ffe7dc10a7a9e2172eecd46f613c/src/render/theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/render/theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MangoTsing/vite-electron-quick/4b9c8cdc1e73ffe7dc10a7a9e2172eecd46f613c/src/render/theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/render/types/Base.d.ts: -------------------------------------------------------------------------------- 1 | interface AnyObject { 2 | [key: string]: any 3 | } -------------------------------------------------------------------------------- /src/render/views/Create.vue: -------------------------------------------------------------------------------- 1 | 8 | 14 | -------------------------------------------------------------------------------- /src/render/views/Index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 50 | 51 | 65 | 66 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "importHelpers": true, 7 | "jsx": "react", 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "baseUrl": "./", 11 | "strict": true, 12 | "types": [ 13 | "vite/client", 14 | "node" 15 | ], 16 | "paths": { 17 | "@/*": ["src/render/*", "src/main/*"], 18 | "root/*": ["/*"] 19 | }, 20 | "allowSyntheticDefaultImports": true 21 | }, 22 | "include": [ 23 | "src/**/*" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 参考链接: https://vitejs.dev/config/ 3 | */ 4 | import { join } from 'path' 5 | import { UserConfig } from 'vite' 6 | import dotenv from 'dotenv' 7 | import vue from '@vitejs/plugin-vue' 8 | 9 | dotenv.config({ path: join(__dirname, '.env') }) 10 | const root = join(__dirname, 'src/render') 11 | 12 | const config: UserConfig = { 13 | root, 14 | resolve: { 15 | alias: { 16 | '/@': root, 17 | } 18 | }, 19 | base: './', 20 | build: { 21 | outDir: join('../../dist/render'), 22 | emptyOutDir: true 23 | }, 24 | server: { 25 | port: +process.env.PORT, 26 | }, 27 | plugins: [ 28 | vue() 29 | ], 30 | optimizeDeps: { 31 | exclude: [ 32 | 'electron-is-dev', 33 | 'electron-store', 34 | ] 35 | }, 36 | } 37 | 38 | export default config 39 | --------------------------------------------------------------------------------