refactor: use vite alternative rollup
@@ -10,19 +10,22 @@ if (!app.requestSingleInstanceLock()) {
|
||||
|
||||
let win: BrowserWindow | null = null
|
||||
|
||||
function bootstrap() {
|
||||
async function bootstrap() {
|
||||
win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.js'),
|
||||
preload: path.join(__dirname, '../preload/index.cjs'),
|
||||
},
|
||||
})
|
||||
|
||||
if (app.isPackaged) {
|
||||
win.loadFile(path.join(__dirname, '../render/index.html'))
|
||||
win.loadFile(path.join(__dirname, '../renderer/index.html'))
|
||||
} else {
|
||||
const pkg = await import('../../package.json')
|
||||
const url = `http://${pkg.env.HOST || '127.0.0.1'}:${pkg.env.PORT}`
|
||||
|
||||
win.loadURL(url)
|
||||
win.maximize()
|
||||
win.webContents.openDevTools()
|
||||
win.loadURL(`http://localhost:${process.env.PORT}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import fs from 'fs'
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { domReady } from './utils'
|
||||
import { injectWsCode } from './ws'
|
||||
import { useLoading } from './loading'
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
@@ -9,10 +8,6 @@ const { removeLoading, appendLoading } = useLoading()
|
||||
|
||||
domReady().then(() => {
|
||||
appendLoading()
|
||||
isDev && injectWsCode({
|
||||
host: process.env.HOST, // '127.0.0.1'
|
||||
port: process.env.PORT_WS as string, // process.env.npm_package_env_PORT_WS
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -1,118 +1,56 @@
|
||||
|
||||
function loadingBootstrap() {
|
||||
const loadingStyle = document.createElement('style')
|
||||
const loadingBox = document.createElement('div')
|
||||
|
||||
loadingStyle.id = 'preload-loading-style'
|
||||
loadingBox.id = 'preload-loading-box'
|
||||
|
||||
loadingStyle.textContent += `
|
||||
/* https://projects.lukehaas.me/css-loaders/ */
|
||||
.loading-box { height: 100vh; width: 100vw; position: fixed; left: 0; top: 0; display: flex; align-items: center; background-color: #242424; z-index: 9; }
|
||||
|
||||
.load1 .loader,
|
||||
.load1 .loader:before,
|
||||
.load1 .loader:after {
|
||||
background: #ffffff;
|
||||
-webkit-animation: load1 1s infinite ease-in-out;
|
||||
animation: load1 1s infinite ease-in-out;
|
||||
width: 1em;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
.load1 .loader {
|
||||
color: #ffffff;
|
||||
text-indent: -9999em;
|
||||
margin: 88px auto;
|
||||
position: relative;
|
||||
font-size: 11px;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
.load1 .loader:before,
|
||||
.load1 .loader:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.load1 .loader:before {
|
||||
left: -1.5em;
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.load1 .loader:after {
|
||||
left: 1.5em;
|
||||
}
|
||||
|
||||
@-webkit-keyframes load1 {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -2em;
|
||||
height: 5em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes load1 {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -2em;
|
||||
height: 5em;
|
||||
}
|
||||
}`
|
||||
|
||||
loadingBox.classList.add('loading-box', 'load1')
|
||||
loadingBox.innerHTML += '<div class="loader"></div>'
|
||||
|
||||
const appendLoading = () => {
|
||||
document.head.appendChild(loadingStyle)
|
||||
document.body.appendChild(loadingBox)
|
||||
}
|
||||
|
||||
const removeLoading = () => {
|
||||
const _loadingStyle = document.getElementById('preload-loading-style')
|
||||
const _loadingBox = document.getElementById('preload-loading-box')
|
||||
|
||||
// Ensure the remove child exists.
|
||||
_loadingStyle && document.head.removeChild(_loadingStyle)
|
||||
_loadingBox && document.body.removeChild(_loadingBox)
|
||||
};
|
||||
|
||||
return { loadingStyle, loadingBox, removeLoading, appendLoading }
|
||||
}
|
||||
|
||||
/** 闪屏 loading */
|
||||
/**
|
||||
* https://tobiasahlin.com/spinkit
|
||||
* https://connoratherton.com/loaders
|
||||
* https://projects.lukehaas.me/css-loaders
|
||||
* https://matejkustec.github.io/SpinThatShit
|
||||
*/
|
||||
export function useLoading() {
|
||||
let _isCallRemoveLoading = false
|
||||
const { appendLoading, removeLoading } = loadingBootstrap();
|
||||
const className = `loaders-css__square-spin`
|
||||
const styleContent = `
|
||||
@keyframes square-spin {
|
||||
25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
|
||||
50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
|
||||
75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
|
||||
100% { transform: perspective(100px) rotateX(0) rotateY(0); }
|
||||
}
|
||||
.${className} > div {
|
||||
animation-fill-mode: both;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: #fff;
|
||||
animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
|
||||
}
|
||||
.app-loading-wrap {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #282c34;
|
||||
z-index: 9;
|
||||
}
|
||||
`
|
||||
const oStyle = document.createElement('style')
|
||||
const oDiv = document.createElement('div')
|
||||
|
||||
// 5 秒超时自动关闭
|
||||
setTimeout(() => !_isCallRemoveLoading && removeLoading(), 4999)
|
||||
oStyle.id = 'app-loading-style'
|
||||
oStyle.innerHTML = styleContent
|
||||
oDiv.className = 'app-loading-wrap'
|
||||
oDiv.innerHTML = `<div class="${className}"><div></div></div>`
|
||||
|
||||
return {
|
||||
appendLoading,
|
||||
appendLoading() {
|
||||
document.head.appendChild(oStyle)
|
||||
document.body.appendChild(oDiv)
|
||||
},
|
||||
removeLoading() {
|
||||
_isCallRemoveLoading = true
|
||||
removeLoading()
|
||||
document.head.removeChild(oStyle)
|
||||
document.body.removeChild(oDiv)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Ws client side
|
||||
*/
|
||||
|
||||
/** Inject ws client-side code */
|
||||
export function injectWsCode(options: {
|
||||
host: string
|
||||
port: string | number
|
||||
}) {
|
||||
const oScript = document.createElement('script')
|
||||
oScript.id = 'ws-preload-hot-reload'
|
||||
|
||||
oScript.innerHTML = `
|
||||
${__ws_hot_reload_for_preload.toString()}
|
||||
${__ws_hot_reload_for_preload.name}(${JSON.stringify(options)})
|
||||
`
|
||||
|
||||
document.body.appendChild(oScript)
|
||||
}
|
||||
|
||||
function __ws_hot_reload_for_preload(options: { host: string; port: string | number }) {
|
||||
const ws = new WebSocket(`ws://${options.host}:${options.port}`)
|
||||
ws.onmessage = function (ev) {
|
||||
try {
|
||||
console.log('[preload] ws.onmessage:', ev.data)
|
||||
|
||||
const data = JSON.parse(ev.data) // { "cmd": "string", data: "string|number" }
|
||||
|
||||
if (data.cmd === 'reload') {
|
||||
setTimeout(() => window.location.reload(), 999)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`ws.onmessage should be accept "JSON.string" formatted string.`)
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" v-model="useScriptSetup" /> Use
|
||||
<code><script setup></code>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" v-model="useTsPlugin" /> Provide types for
|
||||
<code>*.vue</code> imports
|
||||
</label>
|
||||
|
||||
<p>
|
||||
Recommended IDE setup:
|
||||
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
|
||||
+
|
||||
<template v-if="!useScriptSetup">
|
||||
<a
|
||||
href="https://marketplace.visualstudio.com/items?itemName=octref.vetur"
|
||||
target="_blank"
|
||||
>Vetur</a>
|
||||
+
|
||||
<a
|
||||
href="https://marketplace.visualstudio.com/items?itemName=znck.vue-language-features"
|
||||
target="_blank"
|
||||
>Vue DX</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
</template>
|
||||
</p>
|
||||
<p v-if="useTsPlugin">
|
||||
tsconfig setup:
|
||||
<br />1. Install and add
|
||||
<code>@vuedx/typescript-plugin-vue</code> to tsconfig plugins
|
||||
<br />2. Delete <code>src/shims-vue.d.ts</code>
|
||||
<br />3. Open
|
||||
<code>src/main.ts</code> in VSCode
|
||||
<br />4. Open VSCode command input
|
||||
<br />5. Search and run "Select TypeScript version" -> "Use workspace version"
|
||||
</p>
|
||||
<button @click="count++">count is: {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">Vite Docs</a> |
|
||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup: () => {
|
||||
const count = ref(0)
|
||||
const useScriptSetup = ref(false);
|
||||
const useTsPlugin = ref(false);
|
||||
return { count, useScriptSetup, useTsPlugin }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +0,0 @@
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
13
src/renderer/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
18
src/renderer/package.json.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "vite-vue-ts",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.2.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^1.9.3",
|
||||
"typescript": "^4.4.3",
|
||||
"vite": "^2.6.4",
|
||||
"vue-tsc": "^0.3.0"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -1,3 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
// This starter template is using Vue 3 <script setup> SFCs
|
||||
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="logo-box">
|
||||
<img style="height:200px;" src="./assets/electron.png" alt="Electron logo">
|
||||
@@ -6,21 +12,19 @@
|
||||
<span/>
|
||||
<img style="height:200px;" alt="Vue logo" src="./assets/vue.png" />
|
||||
</div>
|
||||
<HelloWorld msg="Electron@15 + Vite@2 + Vue@3" />
|
||||
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.logo-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -30,5 +34,4 @@ export default {
|
||||
.logo-box span {
|
||||
width: 74px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
52
src/renderer/src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<p>
|
||||
Recommended IDE setup:
|
||||
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
|
||||
+
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
</p>
|
||||
|
||||
<p>See <code>README.md</code> for more information.</p>
|
||||
|
||||
<p>
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
||||
Vite Docs
|
||||
</a>
|
||||
|
|
||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
||||
</p>
|
||||
|
||||
<button type="button" @click="count++">count is: {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
||||
8
src/renderer/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
13
src/renderer/src/global.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
export { }
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/** Expose some Api through preload script */
|
||||
bridge: {
|
||||
fs: typeof import('fs')
|
||||
ipcRenderer: import('electron').IpcRenderer
|
||||
removeLoading: () => void
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './index.css'
|
||||
|
||||
createApp(App)
|
||||
.mount('#app')
|
||||
.$nextTick(window.removeLoading)
|
||||
.mount('#app')
|
||||
.$nextTick(window.removeLoading)
|
||||
|
||||
console.log('fs', window.fs)
|
||||
console.log('ipcRenderer', window.ipcRenderer)
|
||||
15
src/renderer/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||