最近在用electron开发一个适合自己的小应用,技术选型中使用prisma和sqlite在进行数据存储,写这篇文章的目的就是用来记录下遇到的一些问题。
开发环境使用prisma
1、开发环境使用prisma非常的简单,只需要按照教程安装prisma,然后执行npx prisma init --datasource-provider sqlite 即可。脚本会生成schema文件
在schema文件中编写自己的模型。
开发环境中执行
npx prisma migrate dev --name xxxx
可以看到在我们的prisma目录下会有个迁移文件夹migrations,里面会生成我们的sql。这时候我们可以直接使用@prisma/client
import { PrismaClient } from '@prisma/client'
const prismaInstance = new PrismaClient()
可以用prismaInstance实例来进行数据库操作咯,这时候如果你使用的是ts并且安装了prisma的vs code插件你就会发现它的语法提示非常人性化,如果你之前有orm的基础就可以直接上手。
在生产环境中使用
一般情况下开发环境有nodejs环境,全局安装了prisma我们只需要把migrations文件夹拷贝到开发环境并且执行下面命令就行。
npx prisma migrate deploy
在electron中你使用electron-builder打包要怎么做才能正常使用prisma呢?
1、首先你得把migrations文件夹拷贝到安装包中,在package.json中使用extraResources来将我们所需要的migrations文件移入到安装包中。
"build": {
"extraResources": [
{
"from": "prisma/migrations",
"to": "resources/prisma/migrations"
},
{
"from": "prisma/schema.prisma",
"to": "resources/prisma/schema.prisma"
}
]
}
这样打包后如果你打开 join(process.resourcesPath,'resources/prisma')
这个路径就会发现migrations文件夹已经被移入,然后我们要执行npx prisma migrate delploy
,但是在我们的electron生产环境中没有npx命令该怎么做,这时候我们就需要使用prisma提供的二进制来进行操作了。
根据不同的环境prisma会提供不同的二进制,比如我这边是mac m1,那么你就要在schema.prisma中加入以下内容,prisma运行过程中就会去下载drawin-arm64的二进制
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "darwin-arm64"]
}
下载完你就会发现node_modules/@prisma/engines下会多出两个二进制文件
libquery是sql引擎,没有它就不能进行数据库操作。
schema就是我们刚才说的执行npx prisma migrate deploy
的,可以理解为全局安装的prisma就是执行的这个二进制。
这样我们就可以直接使用二进制来执行我们的命令咯,前提是我们把二进制拷贝到了安装包内,你也可以使用extraResources属性,但我这边并没有写,因为electron一般会对文件都打包进asar二进制中,但是如果有些三方库的二进制打入二进制会出现一些意想不到的问题,所以可以在package.json设置asarUnpack,意味着不用吧这些文件进行二进制打包。
"build":{
"asarUnpack": [
"**/node_modules/prisma/**/*",
"**/node_modules/@prisma/**/*"
]
}
我们怎么拿到那些不需要被打包的二进制呢?我们代码中怎么写呢,路径是什么呢?代码如下
const appUnpackedPath = app.getAppPath().replace('app.asar', 'app.asar.unpacked')
const schemePath = electronIsDev ? resolve(__dirname, '../node_modules/@prisma/engines/schema-engine-darwin-arm64') : resolve(appUnpackedPath, './node_modules/@prisma/engines/schema-engine-darwin-arm64')
上面是对开发环境进行了判断,读者可以自行处理,appUnkpackedPath就是我们的二进制存放路径。
我们可以开发编写一套统一的处理代码,这套也是我从github某篇issue上直接拿下来的。
export async function runPrisma(command: string[]) {
try {
const exitCode = await new Promise((r, _) => {
const prismaPath = resolve(electronIsDev ? resolve(__dirname, '../node_modules') : resolve(appUnpackedPath, './node_modules'), 'prisma', 'build', 'index.js')
logger.info(process.cwd(), 'cwd')
const child = fork(prismaPath, command, {
cwd: electronIsDev ? process.cwd() : join(appUserPath, 'resources'),
env: {
...process.env,
DATABASE_URL: process.env.DATABASE_URL,
PRISMA_QUERY_ENGINE_LIBRARY: queryPath,
PRISMA_SCHEMA_ENGINE_BINARY: schemePath,
},
stdio: 'pipe',
})
child.on('message', (msg) => {
logger.info(msg)
})
child.on('error', (err) => {
logger.error('Child process got error:', err)
})
child.on('close', (code) => {
logger.info(code)
r(code)
})
child.stdout?.on('data', function (data) {
// console.log(data)
logger.info('prisma: ', data.toString())
})
child.stderr?.on('data', function (data) {
logger.error('prisma: ', data.toString())
})
})
if (exitCode !== 0) {
throw Error(`command ${command} failed with exit code ${exitCode}`)
}
return exitCode
} catch (e) {
logger.error(e)
throw e
}
}
我们执行只需要传入参数进行即可
要注意我们的命令执行路径,也就是cwd参数要传入正确,要确保路径下面的文件内容格式符合prisma要求,prisma/migrations/xx类似这样即可
runPrisma(['migrate', 'deploy'])
上面代码中还有个queryPath,这个path是用来获取执行sql的引擎的,如果没有他我们就不能进行数据库操作,开发环境不需要我们指定引擎的二进制,但是在electron中需要我们来指定一下,有两种方法一种是使用环境变量process.env.PRISMA_QUERY_ENGINE_BINARY = queryPath
,但是这种方法我这一直没成功,所以在github上找到了一种隐藏api方式来直接指定
const prismaInstance = new PrismaClient({
__internal: {
engine: {
binaryPath: queryPath,
},
},
} as any)
这个__internal是一个非标准api,但是使用了发现能正常执行了。如果不指定就会一直报错
Can not spawn Query Engine when using Electron
结语
最后寻找感兴趣开发小工具的小伙伴一起来开发,目前我给这个产品取名Ew Box
,Everything You Want,技术栈使用如下:
- vite
- electron
- react,react-router
- prisma
- sqlite
- tailwindcss
- shadcn/ui
- koa
对以上技术感兴趣,还有一些不错的idea的小伙伴都可以加入哟,后续可以使用你感兴趣的任何技术栈都可以集成其中
产品没有盈利目的,都是些简单的小功能点,做一款适合自己的小工具,就单纯想扩展或者提升技术面的小伙伴可以尝试加入哟
目前产品功能有压缩图片/密码箱/todo日历等功能,有想法的朋友可以一起加入哟。