发布 npm 包不仅仅是把代码放上去,还需要考虑 构建规范 (ESM/CJS) 、类型定义 (TypeScript .d.ts) 、Peer Dependencies 以及 扩展性 。
既然我们要把它做成一个通用的 pinia-plugin-electron-share,我们需要让它支持配置(比如自定义 Channel 名称),并且具备完善的工程化结构。
这里是可以直接 npm publish 的完整工程代码结构 。
1. 项目目录结构 你需要创建一个新的文件夹(例如 pinia-plugin-electron-share),结构如下:
1 2 3 4 5 6 7 8 pinia-plugin-electron-share/ ├── src/ │ └── index.ts # 核心源码 ├── dist/ # 构建产物 (自动生成) ├── package.json # 依赖与元数据 ├── tsconfig.json # TS 配置 ├── vite.config.ts # 构建脚本 (使用 Vite 库模式) └── README.md # 说明文档
2. 核心代码 (src/index.ts) 这里我做了一些增强:
支持插件级配置 :可以在 pinia.use() 时传入全局配置。
类型声明增强 :完美的 TypeScript 类型提示。
序列化保护 :增加了对序列化异常的基础捕获。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 import { PiniaPluginContext } from 'pinia' declare module 'pinia' { export interface DefineStoreOptionsBase <S, Store > { share ?: boolean } } export interface ElectronShareOptions { channelName ?: string } type SyncPayload = { type : 'UPDATE' | 'REQUEST_INIT' | 'RESPONSE_INIT' storeId : string data ?: any timestamp : number } export function createElectronSharePlugin (globalOptions : ElectronShareOptions = {} ) { const CHANNEL_NAME = globalOptions.channelName || 'pinia-electron-share' const channel = new BroadcastChannel (CHANNEL_NAME ) return ({ store, options }: PiniaPluginContext ) => { if (!options.share ) return let isExternalUpdate = false channel.addEventListener ('message' , (event : MessageEvent <SyncPayload > ) => { const { type , storeId, data } = event.data if (storeId !== store.$id ) return switch (type ) { case 'UPDATE' : if (!isExternalUpdate) { isExternalUpdate = true store.$patch(data) setTimeout (() => { isExternalUpdate = false }, 0 ) } break case 'REQUEST_INIT' : channel.postMessage ({ type : 'RESPONSE_INIT' , storeId : store.$id , data : JSON .parse (JSON .stringify (store.$state )), timestamp : Date .now (), }) break case 'RESPONSE_INIT' : isExternalUpdate = true store.$patch(data) setTimeout (() => { isExternalUpdate = false }, 0 ) break } }) store.$subscribe((mutation, state ) => { if (isExternalUpdate) return try { const rawState = JSON .parse (JSON .stringify (state)) channel.postMessage ({ type : 'UPDATE' , storeId : store.$id , data : rawState, timestamp : Date .now (), }) } catch (e) { console .error (`[pinia-plugin-electron-share] Failed to serialize state for store "${store.$id} ". Ensure state is JSON-serializable.` , e) } }) setTimeout (() => { channel.postMessage ({ type : 'REQUEST_INIT' , storeId : store.$id , timestamp : Date .now (), }) }, 50 ) } }
3. 构建配置 (vite.config.ts) 使用 Vite 的库模式(Library Mode)是目前打包现代前端库的最佳实践。我们需要同时输出 ESM (给 Vite/Webpack 用) 和 CJS (给旧 Node 环境用,虽然后者在 Electron 前端不常用,但为了规范还是加上)。
你需要安装开发依赖:npm install -D vite vite-plugin-dts typescript vue pinia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import { defineConfig } from 'vite' import dts from 'vite-plugin-dts' import { resolve } from 'path' export default defineConfig ({ plugins : [ dts ({ include : ['src/**/*.ts' ], insertTypesEntry : true , }) ], build : { lib : { entry : resolve (__dirname, 'src/index.ts' ), name : 'PiniaPluginElectronShare' , fileName : (format ) => `index.${format} .js` }, rollupOptions : { external : ['pinia' , 'vue' ], output : { globals : { vue : 'Vue' , pinia : 'Pinia' } } } } })
4. package.json 配置 这里有几个关键点:
peerDependencies :明确告诉使用者,你必须先安装 pinia。
exports :现代 npm 包的导出规范。
types :指向生成的类型文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 { "name" : "pinia-plugin-electron-share" , "version" : "1.0.0" , "description" : "Sync Pinia state across Electron windows (Renderer processes) using BroadcastChannel. Ideal for Dockview." , "main" : "./dist/index.umd.js" , "module" : "./dist/index.es.js" , "types" : "./dist/index.d.ts" , "exports" : { "." : { "import" : "./dist/index.es.js" , "require" : "./dist/index.umd.js" , "types" : "./dist/index.d.ts" } } , "files" : [ "dist" ] , "scripts" : { "dev" : "vite build --watch" , "build" : "vite build" , "prepublishOnly" : "npm run build" } , "keywords" : [ "pinia" , "electron" , "vue" , "share" , "sync" , "multi-window" , "dockview" ] , "author" : "Your Name" , "license" : "MIT" , "peerDependencies" : { "pinia" : "^2.0.0" } , "devDependencies" : { "pinia" : "^2.0.0" , "typescript" : "^5.0.0" , "vite" : "^5.0.0" , "vite-plugin-dts" : "^3.0.0" , "vue" : "^3.0.0" } }
5. tsconfig.json 确保能正确编译。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "compilerOptions" : { "target" : "ESNext" , "useDefineForClassFields" : true , "module" : "ESNext" , "moduleResolution" : "Node" , "strict" : true , "sourceMap" : true , "resolveJsonModule" : true , "isolatedModules" : true , "esModuleInterop" : true , "lib" : [ "ESNext" , "DOM" ] , "skipLibCheck" : true , "declaration" : true } , "include" : [ "src" ] }
6. 使用说明 (README.md 片段) 你可以直接把这段贴到文档里:
1 2 3 4 5 6 7 8 # pinia-plugin-electron-share Sync Pinia state across multiple Electron windows (Renderer Processes). ## Installation ```bash npm install pinia-plugin-electron-share
Usage 1. Register the Plugin 1 2 3 4 5 6 7 8 9 10 11 12 import { createPinia } from 'pinia' import { createElectronSharePlugin } from 'pinia-plugin-electron-share' const pinia = createPinia ()pinia.use (createElectronSharePlugin ({ channelName : 'my-app-sync' })) export default pinia
2. Enable in Store 1 2 3 4 5 6 7 8 9 10 11 import { defineStore } from 'pinia' export const useUserStore = defineStore ('user' , { state : () => ({ count : 0 }), actions : { increment ( ) { this .count ++ } }, share : true })
1 2 3 4 5 6 7 8 9 10 --- ### 7. 发布流程 作为高级工程师,你应该知道发布流程,但为了完整性我还是列一下: 这里提醒一下 npm 官网更新了验证机制,你需要去在你的账户下创建一个access token,然后在命令行中使用 `npm login --registry=https://registry.npmjs.org/` 登录。 在项目下创建一个.npmrc 文件,内容如下:
registry=https://registry.npmjs.com/ //registry.npmjs.com/:_authToken=YOUR_ACCESS_TOKEN
1 2 3 4 1. **构建**: ```bash npm run build
*(确保 `dist` 目录下生成了 `index.es.js` 和 `index.d.ts`)*
发布 :
1 2 npm login npm publish --access public
在你的项目中使用 : 在你的 Electron 项目中 npm install pinia-plugin-electron-share,然后按照 README 配置即可。
为什么这样做更好?
解耦 :你的业务代码库不会混入一堆复杂的 IPC 通信逻辑。
复用 :如果你有下一个 Electron 项目,直接 install 即可。
类型安全 :通过 declare module,你的同事在写 defineStore 时,输入 share: 会自动获得代码提示,这才是高级开发体验。
Prev: 监听窗口创建完成
Next: electron多窗口状态同步 插件开发pinia-plugin-electron-share