-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
394 lines (382 loc) · 55.7 KB
/
search.xml
File metadata and controls
394 lines (382 loc) · 55.7 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Git推送失败443</title>
<url>//3bc3f9fd.html</url>
<content><![CDATA[git再推送代码的时候经常会遇到超时或者无法推送等问题本编文章主要解决Failed to connect to github.com port 443 after 21090 ms: Couldn‘t connect to server的问题问题原因:这是由于本机系统代理端口和git端口不一致导致的。解决办法:1.查看自己本机系统代理设置---网络和Internet---代理---地址---端口
2.修改git配置:(其中的7890改为你电脑的端口号)git config --global http.proxy http://127.0.0.1:7890git config --global https.proxy http://127.0.0.1:7890
3.再次push就可以成功上传。git push origin source
]]></content>
<categories>
<category>开发工具</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title>OKX插件登录BTC</title>
<url>//4c3e688d.html</url>
<content><![CDATA[由于BTC网络的登录并不像wallet connect EVM网络那样使用现成的包直接可以建立连接器只要你浏览器内部装有OKX插件就可以直接唤起并登录具体方法如下PC端登录方法const connectBTC = () => { if (typeof (window as any).okxwallet !== 'undefined') { const res = await (window as any).okxwallet.bitcoin.requestAccounts(); } else { console.log('OKX Wallet is not installed.'); }}
PC发起交易 目前仅支持BTC主网const sendBTC = () => { try { await (window as any).okxwallet.bitcoin.sendBitcoin( acceptAddress, // 接收地址 6000, // 发送金额 单位:聪 { feeRate: 10 // 指定费率 } ); } catch (error) { console.error(error) }}
移动端登录跟PC端登录有显著区别因为移动端浏览器没法装插件钱包需要跳转到OKX的App中再执行登录操作const checkOkxWallet = async () => { // 检查是否监听到浏览器插件 if (typeof (window as any).okxwallet !== 'undefined') { try { const accounts = await (window as any).bitcoin.requestAccounts(); } catch (e) { console.log('connect failed', e); } } else { initOKXWallet() }};const initOKXWallet = () => { // 调转到OKX App的内部浏览器 const encodedUrl = "https://www.okx.com/download?deeplink=" + encodeURIComponent("okx://wallet/dapp/url?dappUrl=" + encodeURIComponent(location.href)); window.location.href = encodedUrl;}
移动端OKX使用BTC发送交易 const okxSend = async () => { const acceptAddress = 'your btc address' try { const txid = await (window as any).bitcoin.sendBitcoin( acceptAddress, // 收款地址 6000, // 金额单位:聪 { feeRate: 10 // 费率 } ); console.log(txid, 'tx'); } catch (error) { console.log(error); }}
总的来说还是身份方便的,省去了构建UTXO的烦恼]]></content>
<categories>
<category>Web3</category>
</categories>
<tags>
<tag>BTC</tag>
</tags>
</entry>
<entry>
<title>Wagmi返回合约完整错误日志</title>
<url>//c49bf0df.html</url>
<content><![CDATA[使用Wallet Connect + Wagmi 集成EVM登录交易等操作遇到的一些问题使用useWriteContract发起合约交易直接打印错误日志返回的信息不完整官方文档也没有给到完整的Error信息返回处理,搜也搜不到,去wagmi的discord下面找到了用户自己写的一个解析Wagmi的方法如下import { BaseError } from "wagmi";const { data: hash, isPending, writeContractAsync, isSuccess: hashSuccess } = useWriteContract()const massComputed = () => { writeContractAsync({ address: contractAddress, abi: abi, functionName: 'contractFunction', gas: gasLimit }) .then((res: any) => { console.log(res); }) .catch((error: any) => { if (error) { const shortMessage = (error as BaseError)?.shortMessage; console.log(shortMessage); } })}
目前react版本的解析方式是这样的vue版本目前没有好的办法,谁让这个框架是老外发明的呢,兼容性做的有点差]]></content>
<categories>
<category>Web3</category>
</categories>
<tags>
<tag>Web3.js</tag>
</tags>
</entry>
<entry>
<title>解决切换路由因高度不够出现滚动条导致页面抖动</title>
<url>//78cea6d8.html</url>
<content><![CDATA[现在大多数页面都是采用主题内容水平居中布局,但是,这种布局存在一个问题。现在的浏览器滚动条默认是overflow:auto类型的,也就是说如果内容高度不足一屏,没有滚动条;如果超出才会出现滚动条。滚动条也是占据屏幕尺寸的,从没有滚动条到有滚动条会使页面左右抖动一下。现有三种解决方法:
让垂直滚动条一直显示body { overflow-y: scroll;}
即使内容高度不足一屏也会显示滚动条,比较影响用户体验
当内容高度超过一屏时,在浏览器左侧同时加上一个滚动条宽的的padding或margin.wrap-outer { margin-left: calc(100vw - 100%);}
margin换成padding一样可以生效
推荐使用兼容写法html { overflow-x: hidden; overflow-y: auto;}body { width: 100vw; overflow: hidden;}]]></content>
<categories>
<category>CSS</category>
</categories>
<tags>
<tag>Css</tag>
</tags>
</entry>
<entry>
<title>flex弹性盒子布局最后一行不满时左对齐实现思路</title>
<url>//8513d6fd.html</url>
<content><![CDATA[只需要在弹性盒子的容器加一个伪类元素,大小就是盒子内容的宽高就可以实现。
<ul> <li></li> <li></li> <li></li> <li></li> <li></li></ul>
*{ padding: 0px; list-style: none;}ul{ width: 1000px; height: 800px; border: 1px solid #000; display: flex; flex-flow: row wrap; justify-content: space-between;}li{ width: 300px; height: 300px; border: 1px solid #456;}ul::after{ width: 300px; height: 300px; content: '';}]]></content>
<categories>
<category>CSS</category>
</categories>
<tags>
<tag>Css</tag>
</tags>
</entry>
<entry>
<title>前端判断当前访问设备类型</title>
<url>//4a17b166.html</url>
<content><![CDATA[在utils目录下创建一个isType文件const ua = window.navigator.userAgent;export const isWap = /Android|iPhone|ios|iPad|iPod|BlackBerry|IEMobile/i.test(ua); // 是否是移动端export const isWeixin = /MicroMessenger/i.test(ua); // 是否是微信浏览器export const isAlipay = /AlipayClient/i.test(ua); // 是否是支付宝浏览器export const isAndroid = /Android/i.test(ua); // 是否安卓设备export const isIOS = /iphone|ios|ipad|ipod/i.test(ua);export const isMainClient = /aijiao100\(newk12/i.test(ua);export const isWeibo = /(weibo).*weibo__([\d.]+)/i.test(ua);export const isQQ = /qq\/([\d.]+)/i.test(ua);export const isQQBrowser = /(qqbrowser)\/([\d.]+)/i.test(ua);export const isQzone = /qzone\/.*_qz_([\d.]+)/i.test(ua);export const isInApp = isWeixin || isAlipay || isWeibo || isQQ || isMainClient;// 安卓 chrome 浏览器,很多 app 都是在 chrome 的 ua 上进行扩展的export const isOriginalChrome = /chrome\/[\d.]+ Mobile Safari\/[\d.]+/i.test(ua) && isAndroid;// chrome for ios 和 safari 的区别仅仅是将 Version/<VersionNum> 替换成了 CriOS/<ChromeRevision>// ios 上很多 app 都包含 safari 标识,但它们都是以自己的 app 标识开头,而不是 Mozillaexport const isSafari = /safari\/([\d.]+)$/i.test(ua) && /iphone|ios|ipad|ipod/i.test(ua) && ua.indexOf('Crios') < 0 && ua.indexOf('Mozilla') === 0;
使用方法import { isWap } from './utils/isType';if (isWap) { console.log('当前是移动端')} else { console.log('当前是PC端')}]]></content>
<categories>
<category>TypeScript</category>
</categories>
<tags>
<tag>TypeScript</tag>
</tags>
</entry>
<entry>
<title>pinia</title>
<url>//b51dc851.html</url>
<content><![CDATA[Pinia与Vuex的不同Pinia是Vue.js团队开发的一个全新的状态管理库,并且Pinia符合Vuex5的提案,已被纳入官方GitHub。Vuex: State、Gettes、Mutations(同步)、Actions(异步)Pinia: State、Gettes、Actions(同步异步都支持)
Pinia的核心特性Pinia没有Mutations、Actions支持同步和异步、没有模块的嵌套结构(每个store互相独立)、更好的TypeScript支持、Vue2和Vue3都支持。
安装pnpm add pinia
Pinia初始化设置main.ts文件
import { createPinia } from 'pinia'createApp(App).use(createPinia()).mount('#app')
在store目录下面新建一个user.ts
import { defineStore } from 'pinia'export const userStore = defineStore('user', { state: () => { return { count: 1 } }, getters: { getCount() { return this.count } }, actions: { doubleCount() { this.count = this.count * 2 } }})
访问state<script setup lang="ts">import { UserStore } from '../store/user'import { computed } from 'vue'const user = UserStore()const count = computed(() => { return user.count})</script><template> <van-button type='primary' @click='user.doubleCount'>Double</van-button> <span>{{count}}</span></template><style></style>
异步actionaction支持async/await的语法来支持异步处理的场景。
import { defineStore } from 'pinia'export const userStore = defineStore('user', { state: () => { return { count: 1 } }, getters: { getCount() { return this.count } }, actions: { doubleCount() { this.count = this.count * 2 }, async login(account, passWord) { const { data } = await api.login(account, passWord) return data } }})]]></content>
<categories>
<category>Vue</category>
</categories>
<tags>
<tag>Pinia</tag>
</tags>
</entry>
<entry>
<title>router</title>
<url>//45d2f225.html</url>
<content><![CDATA[vite现在创建的vue项目已经默认为vue3了,但是在引入vue-router时,可能会有vue-router报错问题,一直提示createRouter为undefined或者其他的关于vue-router报错的问题,主要原始是vue-router版本问题,直接升级到最新版本即可:
pnpm add vue-router@next
项目配置创建router目录新建index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Home', component: () => import('../views/page1.vue'), meta: { title: 'Home' } }, { path: '/page2', name: 'Page2', component: () => import('../views/page2.vue'), meta: { title: 'Page2' } }]const router = createRouter({ history: createWebHistory(), routes})export default router
main.ts文件配置import { createApp } from 'vue'import App from './App.vue'import router from './router'import { setupStore } from './store'async function bootStrap() { const app = createApp(App) setupStore(app) app.use(router).mount('#app')}bootStrap()
路由守卫配置router目录下创建guard.ts
import { Router } from 'vue-router'export const setupRouterGuard = (router: Router) => { router.beforeEach((to, from, next) => { // @ts-ignore document.title = to.meta.title // 路由对应的网页标签title next() })}
router目录下的index.ts文件配置
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'import { setupRouterGuard } from './guard'const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'Home', component: () => import('../views/page1.vue'), meta: { title: 'Home' } }, { path: '/page2', name: 'Page2', component: () => import('../views/page2.vue'), meta: { title: 'Page2' } }]const router = createRouter({ history: createWebHistory(), routes})setupRouterGuard(router)export default router]]></content>
<categories>
<category>Vue</category>
</categories>
<tags>
<tag>Vue</tag>
</tags>
</entry>
<entry>
<title>ts实现康威生命游戏</title>
<url>//8cc9b930.html</url>
<content><![CDATA[生命游戏中,对于任意细胞,规则如下:
每个细胞有两种状态 - 存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动(如图,黑色为存活,白色为死亡)
当前细胞为存活状态时,当周围的存活细胞低于2个时(不包含2个),该细胞变成死亡状态。(模拟生命数量稀少)
当前细胞为存活状态时,当周围有2个或3个存活细胞时,该细胞保持原样。
当前细胞为存活状态时,当周围有超过3个存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。(模拟繁殖)
可以把最初的细胞结构定义为种子,当所有在种子中的细胞同时被以上规则处理后,可以得到第一代细胞图。按规则继续处理当前的细胞图,可以得到下一代的细胞图,周而复始。
export class GameOfLife { private activeCells: Array<[number, number]>; constructor() { this.activeCells = []; // 初始化矩阵 } addCell(x: number, y: number): void { // 添加活细胞亮点 this.activeCells.push([x, y]); } removeCell(x: number, y: number): void { // 删除活细胞亮点 this.activeCells = this.activeCells.filter(([cx, cy]) => cx !== x || cy !== y); } isActive(x: number, y: number): boolean { // 验证该位置是否有存活的细胞 return this.activeCells.some(([cx, cy]) => cx === x && cy === y); } private getNeighbors(x: number, y: number): Array<[number, number]> { const neighbors: Array<[number, number]> = []; for (let dx = -1; dx <= 1; dx++) { for (let dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; neighbors.push([x + dx, y + dy]); } } return neighbors; } private countActiveNeighbors(x: number, y: number): number { return this.getNeighbors(x, y).reduce( (count, [nx, ny]) => count + (this.isActive(nx, ny) ? 1 : 0), 0 ); } step(): void { // 执行一次规则 const toEvaluate = new Set<string>(); const nextState: Array<[number, number]> = []; this.activeCells.forEach(([x, y]) => { toEvaluate.add(`${x},${y}`); this.getNeighbors(x, y).forEach(([nx, ny]) => { toEvaluate.add(`${nx},${ny}`); }); }); toEvaluate.forEach((cell: string) => { const [x, y] = cell.split(",").map(Number); const isActive = this.isActive(x, y); const activeNeighbors = this.countActiveNeighbors(x, y); if ( (isActive && (activeNeighbors === 2 || activeNeighbors === 3)) || (!isActive && activeNeighbors === 3) ) { nextState.push([x, y]); } }); this.activeCells = nextState; }}]]></content>
<categories>
<category>TypeScript</category>
</categories>
<tags>
<tag>TypeScript</tag>
</tags>
</entry>
<entry>
<title>Vite+Vue3+TypeScript轻量级框架搭建(1)</title>
<url>//5e9df9d6.html</url>
<content><![CDATA[初始化项目qnpm init vite@latest√ Project name: vite+vue3+ts // 项目名字√ Select a framework: » vue // 框架语言√ Select a variant: » vue-ts // 拓展语言
添加setup name增强插件安装
pnpm install vite-plugin-vue-setup-extend -D
配置
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'// https://vitejs.dev/config/export default defineConfig({ plugins: [vue(), vueSetupExtend()]})
使用
<script setup name='abc'></script>
为打包后的文件提供传统浏览器兼容性支持因为vite不提供传统浏览器兼容,如果有需要请添加此插件
pnpm install @vitejs/plugin-legacy -D
配置
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'import legacy from '@vitejs/plugin-legacy'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), vueSetupExtend(), legacy({ targets: ['defaults', 'not IE 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'] }) ]})
vite-plugin-compression 压缩插件支持gzip等多种压缩算法
pnpm install vite-plugin-compression -D
配置
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'import legacy from '@vitejs/plugin-legacy'import compressPlugin from 'vite-plugin-compression'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), vueSetupExtend(), legacy({ targets: ['defaults', 'not IE 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'] }), compressPlugin({ verbose: false, // 是否在控制台输出压缩结果 disable: false, // 是否禁用 threshold: 10240, // 文件容量大于这个值进行压缩,它将被压缩,单位为b algorithm: 'gzip', // 压缩算法 可选 ['gzip','brotliCompress' ,'deflate','deflateRaw'] ext: '.gz', // 生成的压缩包后缀 }) ]})
vite-plugin-imagemin 图片压缩安装
pnpm install vite-plugin-imagemin -D
配置
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'import legacy from '@vitejs/plugin-legacy'import compressPlugin from 'vite-plugin-compression'import viteImagemin from 'vite-plugin-imagemin'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), vueSetupExtend(), legacy({ targets: ['defaults', 'not IE 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'] }), compressPlugin({ verbose: false, // 是否在控制台输出压缩结果 disable: false, // 是否禁用 threshold: 10240, // 文件容量大于这个值进行压缩,它将被压缩,单位为b algorithm: 'gzip', // 压缩算法 可选 ['gzip','brotliCompress' ,'deflate','deflateRaw'] ext: '.gz', // 生成的压缩包后缀 }), viteImagemin({ gifsicle: { optimizationLevel: 7, interlaced: false, }, optipng: { optimizationLevel: 7, }, mozjpeg: { quality: 20, }, pngquant: { quality: [0.8, 0.9], speed: 4, }, svgo: { plugins: [ { name: 'removeViewBox', }, { name: 'removeEmptyAttrs', active: false } ] } }) ]})]]></content>
<categories>
<category>Vite</category>
</categories>
<tags>
<tag>Vite</tag>
</tags>
</entry>
<entry>
<title>Vite+Vue3+TypeScript轻量级框架搭建(2)</title>
<url>//826db4a2.html</url>
<content><![CDATA[unplugin-vue-components - 自动导入、按需导入组件安装
pnpm install unplugin-vue-components -D
配置
// vite.config.tsimport { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'import legacy from '@vitejs/plugin-legacy'import compressPlugin from 'vite-plugin-compression'import viteImagemin from 'vite-plugin-imagemin'import Components from 'unplugin-vue-components/vite'import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), ...省略插件配置, Components({ dirs: ['src/components'], resolvers: [ElementPlusResolver()], // 组件的有效文件扩展名。 extensions: ['vue'], // 允许子目录作为组件的命名空间前缀。 directoryAsNamespace: false, deep: true, }) ]})
现在已经注释掉HelloWorld组件引入,项目仍正常运行
<script setup lang="ts" name="App">// This starter template is using Vue 3 <script setup> SFCs// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup// import HelloWorld from './components/HelloWorld.vue'</script><template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /></template><style></style>
运行或打包会自动生成components.d.ts文件内容如下:
// generated by unplugin-vue-components// We suggest you to commit this file into source control// Read more: https://github.com/vuejs/vue-next/pull/3399import '@vue/runtime-core'declare module '@vue/runtime-core' { export interface GlobalComponents { HelloWorld: typeof import('./src/components/HelloWorld.vue')['default'] }}export {}
安装Vant UI 组件库安装
pnpm add vant
vite-plugin-style-import - 按需引入组件库样式安装
pnpm install vite-plugin-style-import consola -D
配置
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'import legacy from '@vitejs/plugin-legacy'import compressPlugin from 'vite-plugin-compression'import viteImagemin from 'vite-plugin-imagemin'import Components from 'unplugin-vue-components/vite'import { VantResolver } from 'unplugin-vue-components/resolvers'import { createStyleImportPlugin, VantResolve } from 'vite-plugin-style-import'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), vueSetupExtend(), legacy({ targets: ['defaults', 'not IE 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'] }), compressPlugin({ verbose: false, // 是否在控制台输出压缩结果 disable: false, // 是否禁用 threshold: 10240, // 文件容量大于这个值进行压缩,它将被压缩,单位为b algorithm: 'gzip', // 压缩算法 可选 ['gzip','brotliCompress' ,'deflate','deflateRaw'] ext: '.gz', // 生成的压缩包后缀 }), viteImagemin({ gifsicle: { optimizationLevel: 7, interlaced: false, }, optipng: { optimizationLevel: 7, }, mozjpeg: { quality: 20, }, pngquant: { quality: [0.8, 0.9], speed: 4, }, svgo: { plugins: [ { name: 'removeViewBox', }, { name: 'removeEmptyAttrs', active: false } ] } }), Components({ dirs: ['src/components'], resolvers: [VantResolver()], // 组件的有效文件扩展名。 extensions: ['vue'], // 允许子目录作为组件的命名空间前缀。 directoryAsNamespace: false, deep: true, }), createStyleImportPlugin({ resolves: [VantResolve()] }), ]})
使用vant组件不用手动引入,unplugin-vue-components 按需自动导入组件
<van-button type="primary">主要按钮</van-button>
unplugin-auto-import - API自动导入setup语法让我们不用再一个一个的把变量和方法都return出去就能在模板上使用,大大的解放了我们的双手。然而对于一些常用的VueAPI,比如ref、computed、watch等,还是每次都需要我们在页面上手动进行import { ref } from ‘vue’。可以通过unplugin-auto-import实现自动导入,无需import即可在文件里使用Vue的API。
pnpm install unplugin-auto-import -D
配置
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import vueSetupExtend from 'vite-plugin-vue-setup-extend'import legacy from '@vitejs/plugin-legacy'import compressPlugin from 'vite-plugin-compression'import viteImagemin from 'vite-plugin-imagemin'import Components from 'unplugin-vue-components/vite'import { VantResolver } from 'unplugin-vue-components/resolvers'import { createStyleImportPlugin, VantResolve } from 'vite-plugin-style-import'import AutoImport from 'unplugin-auto-import/vite'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), vueSetupExtend(), legacy({ targets: ['defaults', 'not IE 11'], additionalLegacyPolyfills: ['regenerator-runtime/runtime'] }), compressPlugin({ verbose: false, // 是否在控制台输出压缩结果 disable: false, // 是否禁用 threshold: 10240, // 文件容量大于这个值进行压缩,它将被压缩,单位为b algorithm: 'gzip', // 压缩算法 可选 ['gzip','brotliCompress' ,'deflate','deflateRaw'] ext: '.gz', // 生成的压缩包后缀 }), viteImagemin({ gifsicle: { optimizationLevel: 7, interlaced: false, }, optipng: { optimizationLevel: 7, }, mozjpeg: { quality: 20, }, pngquant: { quality: [0.8, 0.9], speed: 4, }, svgo: { plugins: [ { name: 'removeViewBox', }, { name: 'removeEmptyAttrs', active: false } ] } }), Components({ dirs: ['src/components'], resolvers: [VantResolver()], // 组件的有效文件扩展名。 extensions: ['vue'], // 允许子目录作为组件的命名空间前缀。 directoryAsNamespace: false, deep: true, }), createStyleImportPlugin({ resolves: [VantResolve()] }), AutoImport({ imports: ['vue'], resolvers: [VantResolver({ importStyle: false })] }) ]})
使用
<script setup lang="ts">const count = ref(0)const state = reactive({ data: '321'})const { data } = toRefs(state)</script><template> {{count}} {{data}}</template><style scoped></style>
运行或打包会自动生成auto-imports.d.ts文件内容如下:
// Generated by 'unplugin-auto-import'// We suggest you to commit this file into source controldeclare global { const computed: typeof import('vue')['computed'] const createApp: typeof import('vue')['createApp'] const customRef: typeof import('vue')['customRef'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineComponent: typeof import('vue')['defineComponent'] const effectScope: typeof import('vue')['effectScope'] const EffectScope: typeof import('vue')['EffectScope'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] const h: typeof import('vue')['h'] const inject: typeof import('vue')['inject'] const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] const markRaw: typeof import('vue')['markRaw'] const nextTick: typeof import('vue')['nextTick'] const onActivated: typeof import('vue')['onActivated'] const onBeforeMount: typeof import('vue')['onBeforeMount'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] const onDeactivated: typeof import('vue')['onDeactivated'] const onErrorCaptured: typeof import('vue')['onErrorCaptured'] const onMounted: typeof import('vue')['onMounted'] const onRenderTracked: typeof import('vue')['onRenderTracked'] const onRenderTriggered: typeof import('vue')['onRenderTriggered'] const onScopeDispose: typeof import('vue')['onScopeDispose'] const onServerPrefetch: typeof import('vue')['onServerPrefetch'] const onUnmounted: typeof import('vue')['onUnmounted'] const onUpdated: typeof import('vue')['onUpdated'] const provide: typeof import('vue')['provide'] const reactive: typeof import('vue')['reactive'] const readonly: typeof import('vue')['readonly'] const ref: typeof import('vue')['ref'] const resolveComponent: typeof import('vue')['resolveComponent'] const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowRef: typeof import('vue')['shallowRef'] const toRaw: typeof import('vue')['toRaw'] const toRef: typeof import('vue')['toRef'] const toRefs: typeof import('vue')['toRefs'] const triggerRef: typeof import('vue')['triggerRef'] const unref: typeof import('vue')['unref'] const useAttrs: typeof import('vue')['useAttrs'] const useCssModule: typeof import('vue')['useCssModule'] const useCssVars: typeof import('vue')['useCssVars'] const useSlots: typeof import('vue')['useSlots'] const watch: typeof import('vue')['watch'] const watchEffect: typeof import('vue')['watchEffect']}export {}]]></content>
<categories>
<category>Vite</category>
</categories>
<tags>
<tag>Vite</tag>
</tags>
</entry>
<entry>
<title>web3.js监听用户切换钱包状态</title>
<url>//eff061d5.html</url>
<content><![CDATA[Web3js获取MetaMask钱包并监听切换官方API方法监听账号切换
import { UserStore } from 'store/modules/user'export function useWeb3() { async function getAccount(): Promise<any> { return new Promise((async (resolve, reject) => { await isMetamask() .then((res: string) => { console.log(res) window.ethereum .request({method: 'eth_requestAccounts'}) .then((res: string[]) => { onEvents() resolve(res) }) .catch((res: string[]) => { reject(res) }) }) .catch((res: string) => { reject(res) }) })) } function isMetamask(): Promise<string> { return new Promise((resolve, reject) => { if (typeof window.ethereum !== 'undefined') { resolve('MetaMask is installed!') } else { reject('MetaMask is uninstalled!') } }) } async function onEvents () { const user = UserStore() // 一旦切换账号这里就会执行 window.ethereum.on("accountsChanged", function(accounts: string[]) { if (accounts.length > 0) { // 账号切换后重新存储钱包地址 user.setWalletAddress(accounts[0]) } }) } return { getAccount }}
账号切换后使用新的钱包地址重新获取页面数据<script setup lang="ts">import { UserStore } from 'store/modules/user'import { computed, watch } from 'vue'const user = UserStore()defineProps<{ msg: string }>()const getAddress = async () => { await user.getWallet()}const walletAddress = computed((): string | null => { return user.getWalletAddress})watch( // 监听到用户钱包地址发生变化,重新执行获取钱包余额事件 () => user.getWalletAddress, (walletAddress) => { walletAddress && getBalance() })</script><template> <ElButton @click="getAddress" type="primary">连接钱包</ElButton> <h1>{{ msg }}</h1> <div>钱包地址:{{walletAddress}}</div></template><style scoped></style>]]></content>
<categories>
<category>Web3</category>
</categories>
<tags>
<tag>Web3.js</tag>
</tags>
</entry>
<entry>
<title>Web3.js安装使用方法</title>
<url>//71ab26c1.html</url>
<content><![CDATA[web3.js 库是一系列模块的集合,服务于以太坊生态系统的各个功能,如:web3-eth 用来与以太坊区块链及合约的交互web3-shh Whisper 协议相关,进行p2p通信和广播web3-bzz swarm 协议(去中心化文件存储)相关web3-utils 包含一些对 DApp 开发者有用的方法
引入web3.jspnpm add web3
项目构建web3文件在utils目录下新建useWeb3.ts
import { UserStore } from 'store/modules/user'export function useWeb3() { // 获取钱包地址 async function getAccount(): Promise<any> { return new Promise((async (resolve, reject) => { await isMetamask() .then((res: string) => { // 获取钱包地址授权 window.ethereum.request({method: 'eth_requestAccounts'}) .then((res: string[]) => { resolve(res) }) .catch((res: string[]) => { reject(res) }) }) .catch((res: string) => { reject(res) }) })) } function isMetamask(): Promise<string> { return new Promise((resolve, reject) => { // 这一步骤是判断当前访问浏览器是否安装MetaMask if (typeof window.ethereum !== 'undefined') { resolve('MetaMask is installed!') } else { reject('MetaMask is uninstalled!') } }) } return { getAccount }}
配合store使用,将钱包地址存储到store里面并显示出来store.ts文件
import { defineStore } from 'pinia'import { useWeb3 } from 'src/utils/useWeb3'import { store } from '../index'interface UserState { walletAddress: string | null}export const useUserStore = defineStore({ id: 'app-user', state: (): UserState => ({ walletAddress: null }), getters: { getWalletAddress(): string | null { // 将store里存储的钱包地址Return出去 return this.walletAddress } }, actions: { setWalletAddress(walletAddress: any | null) { // 获取钱包地址后存储到store里面 this.walletAddress = walletAddress }, async getWallet () { // 获取小狐狸授权,并获取钱包地址 const { getAccount } = useWeb3() const account = await getAccount() this.setWalletAddress(account[0]) } }})export function UserStore() { return useUserStore(store)}
项目文件使用方法<script setup lang="ts">import { UserStore } from 'store/modules/user'import { computed } from 'vue'const user = UserStore()const getAddress = async () => { await user.getWallet()}const walletAddress = computed((): string | null => { return user.getWalletAddress})</script><template> <ElButton @click="getAddress" type="primary">连接钱包</ElButton> <div>钱包地址:{{walletAddress}}</div></template><style scoped></style>]]></content>
<categories>
<category>Web3</category>
</categories>
<tags>
<tag>Web3.js</tag>
</tags>
</entry>
<entry>
<title>微信支付JSAPI前端调起支付</title>
<url>//4a17b158.html</url>
<content><![CDATA[在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。
注意:WeixinJSBridge内置对象在其他浏览器中无效。1、网页端接口请求参数列表{ appid: string, // appId为当前服务商号绑定的appid timeStamp: string, // 当前的时间 nonceStr: string, // 随机字符串,不长于32位。 package: string, // 统一下单接口返回的prepay_id参数值 signType: string, // 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 paySign: string // 签名}
以上参数皆是必填项统一由后端接口返回参数
2、返回结果值说明get_brand_wcpay_request:ok // 支付成功get_brand_wcpay_request:cancel // 支付过程中用户取消get_brand_wcpay_request:fail // 支付失败
注:JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
3、前端封装方法declare let WeixinJSBridge: any;/** * @description: 通过JSAPI下单API成功获取预支付交易会话标识(prepay_id)后,需要通过JSAPI调起支付API来调起微信支付收银台 * 注1:WeixinJSBridge内置对象在其他浏览器中无效 * 注2:此API需要将请求参数进行签名(参与签名的参数为:appId、timeStamp、nonceStr、package,参数区分大小写) * @param {*} * @return {*} */function onBridgeReady (res: any){ /* eslint-disable-next-line */ WeixinJSBridge.invoke('getBrandWCPayRequest', { // 公众号ID,由商户传入 'appId': res.appId, // 时间戳(转为String类型,兼容IOS “未传timeStamp”问题) 'timeStamp': String(res.timeStamp), // 随机串 'nonceStr': res.nonceStr, // 预支付交易会话标识 'package': res.package, // 微信签名方式 'signType': res.signType, // 微信签名 'paySign': res.paySign }, function (result: any) { // 微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。 // 支付成功 if (result.err_msg === 'get_brand_wcpay_request:ok') { console.log('支付成功'); } // 支付取消 if (result.err_msg === 'get_brand_wcpay_request:cancel') { console.log('支付取消'); } // 支付失败 if (result.err_msg === 'get_brand_wcpay_request:fail') { console.log('支付失败'); } })}/** * @description: 调起微信JSAPI支付 * @param {*} * @return {*} */function bootWechatJSAPIPay (res: any){ let document: any; if (typeof WeixinJSBridge === 'undefined') { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } } else { onBridgeReady(res) }}// 调起微信JSAPI支付export { bootWechatJSAPIPay}
以上typScript方法封装
4、前端获取openID方法window.location.replace(`${baseURL}/api/v1/wechat/oauth?authorization=${sign}&returnUrl=${url.href}`)
5、文件内方法调用import { bootWechatJSAPIPay } from 'src/hooks/pay'bootWechatJSAPIPay(param)
如果文档内容没有解决您的问题,您还可以前往 微信开放社区 寻求帮助
]]></content>
<categories>
<category>微信开发</category>
</categories>
<tags>
<tag>WeiXinPay</tag>
</tags>
</entry>
<entry>
<title>关于Vite配置preprocessorOptions.scss.additionalData全局引入scss文件无效问题</title>
<url>//55bb1443.html</url>
<content><![CDATA[在vite项目中,有时候我们需要全局引入css变量、scss变量,或者引入全局scss样式文件,vite提供了以下这种配置方式//vite.config.jscss: { preprocessorOptions: { //define global scss variable scss: { additionalData: `@import '@/styles/variables.scss';`, } }}
这种写法没有任何问题,并且我已经在一些项目中实践过了,可有一次我创建新项目的时候却无效了,在浏览器上也没有看到任何相关的样式,但是在main.js中引入又是正常的,后来去插件官方论坛找到了解决方法是这样说的。
原来这不是一个bug,只有在main.js引入一个其他scss文件或者在.vue文件中使用 style,并且里面有内容,则 scss.additionalData 配置的全局scss文件就可以正确引入了,还建议我们在 scss.additionalData 引入的文件最好只写scss变量,别写css变量,因为css变量是运行时属性,至此,这个问题算是圆满解决了]]></content>
<categories>
<category>Vite</category>
</categories>
<tags>
<tag>Vite</tag>
</tags>
</entry>
<entry>
<title>创建UTXO</title>
<url>//e5a32ca1.html</url>
<content><![CDATA[BTC创建UTXO并用Joy ID广播安装依赖pnpm install bitcoinjs-lib tiny-secp256k1 axios @joyid/bitcoin
获取剩余UTXOconst getUtxos = async (address: string) => { try { const response = await axios.get(`https://blockstream.info/testnet/api/address/${address}/utxo`); return response.data; } catch (error) { console.error('Error fetching UTXO:', error); throw error; }};
创建交易 const [txHex, setTxHex] = useState(''); const createTransaction = async () => { const network = bitcoin.networks.testnet; // 使用比特币测试网络 const senderAddress = address; const recipientAddress = 'tb1qp4gpusm5vq7ysa38wa92a6n8ps89hp7ts2907m'; const amountToSend = 0.0001 * 1e8; // 发送的金额,单位是聪(satoshis) // 获取 UTXO(未花费的交易输出) const utxos = await getUtxos(); if (utxos.length === 0) { throw new Error('No UTXOs available for the address.'); } const utxo = utxos[0]; // 使用第一个 UTXO,实际应用中可能需要选择最佳 UTXO // 检查 txid 是否有效 if (!/^[0-9a-fA-F]{64}$/.test(utxo.txid)) { throw new TypeError('Invalid txid'); } // 创建 PSBT const psbt = new bitcoin.Psbt({ network }); try { psbt.addInput({ hash: utxo.txid, index: utxo.vout, witnessUtxo: { script: bitcoin.address.toOutputScript(senderAddress, network), value: utxo.value, }, }); } catch (error) { throw error; } try { psbt.addOutput({ address: recipientAddress, value: amountToSend, }); } catch (error) { throw error; } // 找零地址和找零金额,如果有的话 const changeAddress = senderAddress; const fee = 0.00001 * 1e8; // 示例费用 const changeValue = utxo.value - amountToSend - fee; if (changeValue > 0) { try { psbt.addOutput({ address: changeAddress, value: changeValue, }); } catch (error) { throw error; } } const psbtHex = psbt.toHex(); setTxHex(psbtHex)};
监听到psbtHex创建完成捕获并使用JoyID广播import { sendPsbt } from "@joyid/bitcoin";const sendContract = async () => { const tx = await sendPsbt(txHex) console.log(tx, 'tx');}]]></content>
<categories>
<category>Web3</category>
</categories>
<tags>
<tag>BTC</tag>
</tags>
</entry>
<entry>
<title>安装依赖超时443解决</title>
<url>//e973ebbc.html</url>
<content><![CDATA[安装依赖的时候有时会遇到安装不上超时等问题
解决方法使用IPv4的方法连接而不是IPv6更改方式
注意别把IPv4也给禁用掉,取消勾选IPv6即可
确认成功后即可正常安装依赖]]></content>
<categories>
<category>开发工具</category>
</categories>
<tags>
<tag>NPM</tag>
</tags>
</entry>
<entry>
<title>小程序随手笔记</title>
<url>//edba0ae4.html</url>
<content><![CDATA[1、组件之间的传值、方法调用1、子组件调用方法在父组件的json文件中注册子组件的路径
{ "usingComponents": { "progress": "../componets/progress/progress" }}
直接在xml中使用就可以
<progress></progress>
2、子组件调用父组件页面刷新方法需要子组件获取父组件的方法
methods: { switch: function(e) { const page = getCurrentPages().pop(); // 当前所在的父组件页面 page.onShow() // 父组件页面刷新 }}
]]></content>
<categories>
<category>微信小程序</category>
</categories>
<tags>
<tag>微信小程序</tag>
</tags>
</entry>
<entry>
<title>播放Spine动画</title>
<url>//35eaf0f1.html</url>
<content><![CDATA[安装spine官方依赖pnpm add @esotericsoftware/spine-player
播放函数(需将动画文件传到CDN或保存到项目文件夹)const playerContainerRef: any = useRef<HTMLDivElement>(null);const loadFiles = () => { const skeletonJsonUrl = `https://xxx/atlas/box-gem/box.json` const atlasUrl = `https://xxx/atlas/box-gem/box.atlas` const player: any = new SpinePlayer(playerContainerRef.current, { jsonUrl: skeletonJsonUrl, atlasUrl: atlasUrl, animation: 'idle', alpha: true, showLoading: false } as any); playerContainerRef.current.style.pointerEvents = 'none';};useEffect(() => { loadFiles()}, []);
]]></content>
<categories>
<category>前端动画</category>
</categories>
<tags>
<tag>Animate</tag>
</tags>
</entry>
<entry>
<title>电脑共享VPN到手机方法</title>
<url>//36de35a5.html</url>
<content><![CDATA[实现原理电脑开启VPN,手机和电脑连接同一个WIFI,设置手机代理为电脑的HTTP代理端口,从而实现共享VPN1.设置代理的方法如下图所示,我使用的是clash,在查看clash代理端口时,可以直接在windows搜索栏中输入“代理服务器设置”, 其他的vpn应该类似。
2.手机wifi代理设置完毕之后就可以正常使用网络了]]></content>
<categories>
<category>开发工具</category>
</categories>
<tags>
<tag>VPN</tag>
</tags>
</entry>
<entry>
<title>解决iOS分发Non-public API问题</title>
<url>//90338fix.html</url>
<content><![CDATA[问题描述在提交 iOS 应用到 TestFlight 或 App Store 时,遇到以下错误:
90338: Non-public API usage. The app references non-public selectors in Chainbox-QA: _isKeyDown, _modifiedInput, _modifierFlags. If method names in your source code match the private Apple APIs listed above, altering your method names will help prevent this app from being flagged in future submissions.
这是因为 React Native 的 RCTKeyCommands.m 和其他文件中使用了 Apple 的私有 API 选择器。
解决方案在 Podfile 中添加 post_install 脚本,自动替换这些私有 API 名称。
1. 首先定义 find_and_replace 函数在 Podfile 顶部添加:
def find_and_replace(dir, findstr, replacestr) Dir[dir].each do |name| text = File.read(name) replace = text.gsub(findstr, replacestr) if text != replace puts "Fix: " + name File.open(name, "w") { |file| file.puts replace } STDOUT.flush end end Dir[dir + '*/'].each(&method(:find_and_replace))end
2. 在 post_install 中调用替换post_install do |installer| # 替换 RCTKeyCommands.m 中的私有 API find_and_replace("./node_modules/react-native/React/Base/RCTKeyCommands.m", "_modifierFlags", "_modifierEventFlags") find_and_replace("./node_modules/react-native/React/Base/RCTKeyCommands.m", "_modifiedInput", "_modifiedEventInput") find_and_replace("./node_modules/react-native/React/Base/RCTKeyCommands.m", "_isKeyDown", "_isKeyEventDown") # 替换 RCTPackagerClient.h 中的私有 API find_and_replace("./node_modules/react-native/React/DevSupport/RCTPackagerClient.h", "handleNotification", "handlePackageNotification") # 替换 RCTPackagerConnection.mm 中的私有 API find_and_replace("./node_modules/react-native/React/DevSupport/RCTPackagerConnection.mm", "handleNotification", "handlePackageNotification") # 其他 post_install 配置...end
完整 Podfile 示例def find_and_replace(dir, findstr, replacestr) Dir[dir].each do |name| text = File.read(name) replace = text.gsub(findstr, replacestr) if text != replace puts "Fix: " + name File.open(name, "w") { |file| file.puts replace } STDOUT.flush end end Dir[dir + '*/'].each(&method(:find_and_replace))end# ... 你的其他 Podfile 配置 ...post_install do |installer| # 修复 Non-public API 问题 find_and_replace("./node_modules/react-native/React/Base/RCTKeyCommands.m", "_modifierFlags", "_modifierEventFlags") find_and_replace("./node_modules/react-native/React/Base/RCTKeyCommands.m", "_modifiedInput", "_modifiedEventInput") find_and_replace("./node_modules/react-native/React/Base/RCTKeyCommands.m", "_isKeyDown", "_isKeyEventDown") find_and_replace("./node_modules/react-native/React/DevSupport/RCTPackagerClient.h", "handleNotification", "handlePackageNotification") find_and_replace("./node_modules/react-native/React/DevSupport/RCTPackagerConnection.mm", "handleNotification", "handlePackageNotification")end
使用步骤
将上述代码添加到 ios/Podfile
运行 pod install
重新构建并提交到 TestFlight
注意事项
每次运行 pod install 时会自动执行替换
这个修复适用于 debug 模式上传到 TestFlight
如果更新了 React Native 版本,可能需要调整文件路径
相关链接
Apple Technical Support
React Native GitHub Issues
]]></content>
<categories>
<category>React Native</category>
</categories>
<tags>
<tag>React Native</tag>
</tags>
</entry>
<entry>
<title>项目笔记</title>
<url>//7705a76.html</url>
<content><![CDATA[1、项目网络状态监听并跳转路由onMounted(() => { window.addEventListener('offline', ()=>{ // 网络异常时触发 router.push({ name: 'Disconnect' }) sessionStorage.locationUrl=window.location.href; }); window.addEventListener('online',()=>{ // 网络恢复正常时触发 window.location.href=sessionStorage.locationUrl });})
2、H5项目每次跳转路由滚动条回到顶部在路由守卫中配置
document.title = to.meta.titlewindow.scrollTo(0,0)]]></content>
<categories>
<category>Vue</category>
</categories>
<tags>
<tag>Vue</tag>
</tags>
</entry>
</search>