ARDUINO BITCOIN KITABXANASI

想都是问题,�都是答案

0%

BITCOIN SV COINBASE17 MILYON BITCOIN SAATIİRAN BITCOIN

BITCOIN MƏDƏN MAŞINI

2024-08-28 Rspack 1.0 正��布了,Rspack 是基� Rust 编写的下一代 JavaScript 打包工具, 兼容 webpack 的 API 和生�,并�供 10 �� webpack 的�建性能。Rsbuild是由 Rspack 驱动的�建工具。

简��说 Rsbuild 就是让你用和 webpack 差�多的�置,更快的�建项目。它是字节�出的产�,说是内部的很多�端项目已��移到 Rspack。

BITCOIN TICARƏT PROQRAMI

下�跟�官方的案例,使用 Rsbuild 快速创建一个项目

1
pnpm create rsbuild@latest

选择你熟悉的框�模版,比如 vue3。安装�赖之��行 pnpm dev, 跑�起�了。

是的,你没有看错�官方给的模版�是开箱�用的,因为你没有�置 mode,想一想 webpack 的 config 你就会知��么�事了。

修改 rsbuild.config.mjs 文件, 新� mode �置。

1
2
3
export default defineConfig({
mode: 'development'
})

�在�行项目,�以�行起�了。

1 BROKER BITCOIN BAXIŞI

�建�端项目框�的时候,�常�触的问题包括:开�模��置�生产打包�置��境��设置���库和�赖�打包优化等等。

这些问题都�以�官方的目录指�找到答案,但是也许你�是 webpack 或者 vite 的熟练使用者。那么你�以直�查看我的项目 rsbuild-vue3。

查看一个项目最快的方�,就是直�看 package.json 文件。

package.json

�难看出项目的一些�赖,包括:vue,vue-router,pinia,sass,element-plus,unoss,lint-staged,biome 等。

这些都是我根�以往的�验添加的一些�赖,当然你也�以自己添加一些,比如说 axios,这很简�,你完全�以轻�解决。

下载项目,安装�赖,�行 npm run start:dev, 如��行�功,你会看到如下内容:

localhost:3002

样�有点丑,�过我们更在�的是功能。这里展示了 unocss, vue-router,pinia, vue-jsx 组件的使用。点击按钮触� store 里�的 count++,点击 Go to About 触� router-link 的页�跳转。很简�,你直�看��的 src/app.vue 就行。

查看项目目录:

项目目录

  • dist: 项目打包输出的文件夹
  • envConfig: ç�¯å¢ƒå�˜é‡�é…�ç½®
  • public: 公共文件,目å‰�存放ç�€ index.html 模版,å��é�¢åœ¨é…�置文件会用到
  • src/components: 公共组件目录,å�¯ä»¥è¢« unplugin-vue-components 自动加载
  • src/pages: 路由页é�¢
  • src/router: 路由é…�ç½®
  • src/store: pinia store çš„é…�ç½®
  • styles: æ ·å¼�文件
    • /element/index: element plus 自定义主题颜色的é…�ç½®
    • /common: 公共样å¼�
  • index.js: å…¥å�£æ–‡ä»¶
  • uno.css: unocss æ ·å¼�文件
  • .biomelintrc-auto-import.json: unplugin-auto-import 生æˆ�的文件,防止 biome 校验ä¸�通过
  • biome.json: 帮助我们完æˆ�代ç �æ ¼å¼�化ä¸�规则校验,相当äº� prettier ä¸� eslint 的集å�ˆï¼Œå…·ä½“å�¯ä»¥æŸ¥çœ‹æˆ‘的文章 å‰�端工具库-biome
  • package.json: …
  • postcss.config.mjs: postcss é…�置,主è¦�是使用 Unocss
  • rsbuild.config.mjs: rsbuild é…�置文件,很é‡�è¦�,类似äº� vite.config.js
  • uno.config.js: Unocss é…�置,使用了 presetWind 预设,兼容 tailwind css 的语法

unplugin-auto-import,unplugin-vue-components 还没有�触过的朋�,�以�查看我的文章,使用vite�建vue3项目,里�有相关使用说�。

unplugin 是一个适用����建工具的统一�件系统, 查看 github 你�以��它们适用� webpack�vite�rsbuild ���建工具的文档。

US MARSHALL BITCOIN AUKSIONU

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
import { defineConfig } from '@rsbuild/core';
import { pluginBabel } from '@rsbuild/plugin-babel';
import { pluginImageCompress } from '@rsbuild/plugin-image-compress';
import { pluginSass } from '@rsbuild/plugin-sass';
import { pluginVue } from '@rsbuild/plugin-vue';
import { pluginVueJsx } from '@rsbuild/plugin-vue-jsx';
import AutoImport from 'unplugin-auto-import/rspack';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import AutoComponents from 'unplugin-vue-components/rspack';

console.log('BASE_URL:', import.meta.env.BASE_URL);

export default defineConfig(({ env, command, envMode }) => {
console.log('env:', env);
console.log('command:', command);
console.log('envMode:', envMode);

return {
// root: './foo', 指定项目根目录, 默认为 process.cwd()
// mode 本项目通过 process.env.NODE_ENV 设置
plugins: [
// Vue 的 JSX �件�赖 Babel 进行编译
pluginBabel({
include: /\.(?:jsx|tsx)$/,
}),
pluginVue(),
pluginVueJsx(), // 支� jsx 语法
pluginSass(), // 支� sass 语法
pluginImageCompress(), // 使用图片�缩
],
tools: {
rspack: {
plugins: [
AutoImport({
resolvers: [
ElementPlusResolver({
importStyle: 'scss',
}),
],
dts: false,
imports: ['vue', 'vue-router', 'pinia'],
biomelintrc: {
// 已存在文件设置默认 false,需�更新时�打开,防止�次更新都�新生�
enabled: false,
// 生�文件地�和�称
filepath: './.biomelintrc-auto-import.json', // Default `./.biomelintrc-auto-import.json`
},
}),
AutoComponents({
// 自动加载组件的目录�置,默认的为 'src/components'
dirs: ['src/components'],
// 组件支�的文件�缀�
extensions: ['vue', 'jsx', 'tsx'],
dts: false,
resolvers: [
ElementPlusResolver({
importStyle: 'scss',
}),
],
}),
],
},
},
source: {
entry: {
index: './src/index.js',
},
// 路径别�
alias: {
'@': './src',
},
},
output: {
target: 'web', // 默认 environment
polyfill: 'off', // �需�兼容 IE 11
minify: true, // 默认在生产模�下�缩 js css
cleanDistPath: env === 'production',
// sourceMap: 使用默认�置,
},
dev: {
lazyCompilation: true, // 开�模��动,按需编译
hmr: true, // 模�热更新,开�模�下默认�用
},
server: {
open: true,
port: 3002,
htmlFallback: 'index', // 默认情况下,当请求满足以下�件且未找到对应资�时,会�退到 index.html
proxy: {
'/api': 'http://localhost:3000',
},
},
html: {
// 设置页� title
title: 'Rsbuild Vue3',
template: './public/index.html',
},
performance: {
// 代�分割�置
chunkSplit: {
strategy: 'split-by-experience',
// strategy: 'split-by-size',
// minSize: 30000,
// maxSize: 500000,
},
removeConsole: true, // 生产模��建时,是�自动移除代�中所有的 console.[methodName]
bundleAnalyze: {}, // 开�分�产物体积,生� ./dist/report-web.html 文件
},
};
});

上�是 rsbuild.config.mjs 的内容,这些官网都�以找到,我也是查找指�和�置一步步�修改的。中文文档很好,点���多说,直�看备注,找官网和谷歌,很容易�白。项目基本上已�包�了日常使用的功能,简�修改一下就好。

之�我用 vite 打造了 vue3 的项目模版,它的打包速度大约是 8s 多。�在使用 rsbuild, 打包�� 1s 多。我使用的是 m2 pro 的 mac book pro 16寸,rsbuild 它真的很快。希望以�会更加��,稳定。

8,5 X 11 BITCOIN SVG01000 BITCOINBITCOIN HAKERLƏRDƏN TƏHLÜKƏSIZDIR

BITCOINEER.APP

  • 安装 Rosetta,å�¯ä»¥è½¬è¯‘ è¿�行在 x86 intel çš„ mac 软件è¿�行在 m1 芯片上。
1
softwareupdate --install-rosetta

输入 ‘A’ ���议,��安装。

  • 在终端è¿�行以下命令,以 Rosetta æ–¹å¼�è¿�行终端。
1
arch -x86_64 zsh
  • 查看是å�¦å�¯ç”¨ Rosetta è¿�行终端
1
uname -m

输出 x86_64 为 Rosetta 2 下�行,arm64 为�生 Apple Silicon 。

  • 安装较ä½�版本 node
1
nvm install 14.20

BITCOIN ƏMƏLIYYATINI NECƏ YOXLAMAQ OLAR25 BITCOIN NƏ QƏDƏRDIRBITCOIN ÖDƏYƏN ANDROID PROQRAMLARI

2010-CU ILIN AVQUSTU BITCOIN HACK

�端上传文件是项目中�常��的一个功能,但是如�文件太大了,我们就需�分片上传了,简�的说就是把文件切割�n个�片段,�次上传到�务器,最��把这些片段拼�起�,组�完整的文件。

这里我�过多介��务端的逻辑,因为大部分上传文件的场景中�端是 oss 处�的,�需�我们写�端代�,大家大概知�个�程就好了。你需���的基础知识包括, File 对象,Blob 对象, node express, stream 。

INDI BITCOIN ALMALIYAM

首先,我们使用 node 写一个简�的�端�务,��一个简�的文件上传功能。

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
// 普通的�文件上传
const express = require('express')
const multer = require('multer')
const path = require('path')
const fs = require('fs')
const cors = require('cors')

const app = express()

const PORT = 3000

app.use(cors())
app.use(express.static('public'))
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads') // 项目的根目录需� uploads 文件夹,�然会报错
},
filename: function (req, file, cb) {
// 解决中文乱�问题
file.originalname = Buffer.from(file.originalname, "latin1").toString(
"utf8"
);
cb(null, file.originalname)
}
})

const upload = multer({ storage: storage })

app.post('/upload', upload.single('file'), (req, res) => {
res.send('File uploaded successfully')
})

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
})

这里使用 express 框���了一个简�的�务,需�注�的是代�中注释的内容,因为在我写测试代�的时候��上传文件�称包�中文的文件时,到�务器端�存�文件�称会出�乱�。

谷歌一下之���是 Multer 这个库的问题,具体�多说�了,按照备注的代��作就�以了。

�端代���:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
</head>

<body>
<p>upload single file</p>
<input type="file" id="single-file">

<script>
// �文件上传
document.getElementById('single-file').addEventListener('change', function (event) {
const file = event.target.files[0];

console.log(file);

const formData = new FormData()
formData.append('file', file)
fetch('http://localhost:3000/upload', {
method: 'POST',
body: formData
}).then(res => {
console.log(res);
})
})
</script>
</body>

</html>

��端的代���都�常的简�,�下�我们���分片上传。

BITCOIN NASHVILLE 2024

�端需�把文件切割�多个�片段,当�片段数��总片段数就递归上传。input 标签选中的 File 文件��了 Blob 的��,也就是�以使用 slice 方法切割二进制数�。File 对象的 size �性则�以��切割计算总的片段数。

�端代�如下:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
</head>

<body>
<p>upload file chunks</p>
<input type="file" id="fileInput">

<script>
// 分片上传
document.getElementById('fileInput').addEventListener('change', function (event) {
const file = event.target.files[0];
if (file) {
uploadFile(file);
}
});

function uploadFile(file) {
const chunkSize = 1 * 1024; // 1KB
const totalChunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;

let progress = 0;

function uploadChunk(start) {
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('file', chunk);
formData.append('filename', file.name);
formData.append('chunkNumber', currentChunk);
formData.append('totalChunks', totalChunks);

fetch('http://localhost:9000/upload', {
method: 'POST',
body: formData
}).then(response => {
if (response.ok) {
currentChunk++;

progress = ((currentChunk / totalChunks) * 100).toFixed(2) + '%';
console.log('progress:',progress);

if (currentChunk < totalChunks) {
uploadChunk(currentChunk * chunkSize);
} else {
console.log('Upload complete');
}
} else {
console.error('Upload failed');
}
}).catch(error => {
console.error('Upload error', error);
});
}

uploadChunk(0);
}
</script>
</body>

</html>

这里需�注�就是 totalChunks 的计算需�使用 Math.ceil �上�入, end 的�值使用 Math.min, 这样就�以��超过文件的长度。

�务端代�:

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
// 断点续传的�端 node ��
const express = require('express');
const multer = require('multer');
const cors = require('cors')

const fs = require('fs');
const path = require('path');

const app = express();

app.use(cors()) // �许跨域

// �置存储选项
const storage = multer.diskStorage({
destination: (req, file, cb) => {
// 如�没有 uplaods 文件夹,则创建文件夹
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
cb(null, uploadDir);
}
});

const upload = multer({ storage });

// 解� JSON 和 URL 编�数�
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 处�文件上传
app.post('/upload', upload.single('file'), (req, res) => {
const { filename, chunkNumber, totalChunks } = req.body;
if (!filename || chunkNumber === undefined || totalChunks === undefined) {
return res.status(400).send('Filename, chunkNumber or totalChunks is missing');
}

const tempFilePath = path.join(__dirname, 'uploads', `${filename}.part${chunkNumber}`);
// 在将�盘中上传的 �生 chunk �称更改为 filename.part0
// xxxx �更为 xxx.part0
fs.renameSync(req.file.path, tempFilePath);

// �并文件逻辑
if (Number(chunkNumber) + 1 === Number(totalChunks)) {
const finalFilePath = path.join(__dirname, 'uploads', filename);
const writeStream = fs.createWriteStream(finalFilePath);
for (let i = 0; i < totalChunks; i++) {
const chunkPath = path.join(__dirname, 'uploads', `${filename}.part${i}`);
const data = fs.readFileSync(chunkPath); // 读� chunk 文件
writeStream.write(data);
fs.unlinkSync(chunkPath); // �一删除 chunk 文件
}
writeStream.end();
}

res.send('Chunk uploaded successfully');
});

const PORT = 9000
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

�务端主�是 createWriteStream 将文件片段拼�,然�删除片段文件。你�以通过断点调试�解这个步骤。

ANTLR3 BITCOIN MINER BLOQU BITMAIN HASHNESTAPEX BITCOIN TICARƏTIBITKOININ DƏYƏRI NƏDIR

ABRA BITCOIN PUL KISƏSI PROQRAMI

Electron是一个使用 JavaScript�HTML 和 CSS �建桌�应用程�的框�。 嵌入 Chromium 和 Node.js 到 二进制的 Electron �许您��一个 JavaScript 代�代�库并创建 在Windows上�行的跨平�应用 macOS和Linux——�需��生桌�端开��验。

BITCOIN HAQQINDA BILMƏK LAZIM OLAN HƏR ŞEY

1
npm install --save-dev electron

由�众所周知的�因,你�能安装�上�赖,此时�以设置镜�。

首先查看你的镜��。

1
nrm ls

确�你使用的镜��是 npm �。

打开npm的�置文件:

1
npm config edit

在里�新�如下�置:

1
2
3
registry=https://registry.npmmirror.com
electron_mirror=https://cdn.npmmirror.com/binaries/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

修改之�的�置

然�关闭窗�,�新安装�赖。

1 MB BITCOIN

在 Electron 中,�个窗�展示一个页�,�者�以�自本地的 HTML,也�以�自远程 URL。 在本例中,您将会装载本地的文件。 在您项目的根目录中创建一个 index.html 文件,并写入下�的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>

在根目录下�新� index.js,内容如下:

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
const { app, BrowserWindow } = require('electron')

// createWindow() 函数将您的页�加载到新的 BrowserWindow �例中
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})

win.loadFile('index.html')
}

// 在应用准备就绪时调用函数
app.whenReady().then(() => {
createWindow()
// 如�没有窗�打开则打开一个窗� (macOS)
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})

// 关闭所有窗�时退出应用 (Windows & Linux)
// 监� app 模�的 window-all-closed 事件,并调用 app.quit() �退出您的应用程�。此方法�适用� macOS
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
  • app: 管ç�†åº”用程åº�的事件生命周期。
  • BrowserWindow: 负责创建和管ç�†åº”用窗å�£ã€‚

�置 package.json 的文件:

1
2
3
4
5
6
{
"scripts": {
"main": "index.js",
"start": "electron ."
}
}

�在�行 npm run start 就�以�动项目了。

PAYPAL BITCOIN ALMAQ ÜÇÜN YAXŞI YERDIR

Electron 的主进程是一个拥有�完全�作系统访问��的 Node.js �境。 除了 Electron 模组 之外,您也�以访问 Node.js 内置模� 和所有通过 npm 安装的包。 �一方�,出�安全�因,渲染进程默认跑在网页页�上,而并� Node.js里。

BrowserWindow 的预加载脚本�行在具有 HTML DOM 和 Node.js�Electron API 的有��集访问��的�境中。

预加载脚本在渲染器加载网页之�注入。 如�你想为渲染器添加需�特殊��的功能,�以通过 contextBridge ��定义 全局对象。

在项目的根目录新建一个 preload.js 文件。该脚本通过 versions 这一全局��,将 Electron 的 process.versions 对象暴露给渲染器。

1
2
3
4
5
6
7
8
9
// preload.js
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron
// 除函数之外,我们也�以暴露��
})

为了将脚本附在渲染进程上,在 BrowserWindow �造器中使用 webPreferences.preload 传入脚本的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// index.js
const { app, BrowserWindow } = require('electron')
// 新�下�这行代�
const path = require('node:path')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
// 新�
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

win.loadFile('index.html')
}

app.whenReady().then(() => {
createWindow()
})

在项目的根目录新�一个 renderer.js 文件,内容如下:

1
2
const information = document.getElementById('info')
information.innerText = `本应用正在使用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`

然�把 renderer.js 脚本�入到 index.html 文件的 body 标签��。

1
<script src="./renderer.js"></script>

总结一下:我们在 createWindow 的时候把 preload.js �入到了 html 文件中,通过 contextBridge 设置了全局对象,在渲染进程就能访问到我们设置的特殊全局�性 versions。

OYUNLAR OYNAYIN BITCOINS QAZANIN

�� IPC 消��渲染器进程��到主进程,您�以使用 ipcRenderer.send API ��消�,然�使用 ipcMain.on API �收。比较简�,请查看官网。

Electron 的主进程和渲染进程有�清楚的分工并且��互�。 这代表�无论是�渲染进程直�访问 Node.js ��,亦或者是�主进程访问 HTML 文档对象模� (DOM),都是��能的。

解决这一问题的方法是使用进程间通信 (IPC)。�以使用 Electron 的 ipcMain 模�和 ipcRenderer 模��进行进程间通信。 为了�你的网页�主进程��消�,你�以使用 ipcMain.handle 设置一个主进程处�程�(handler),然�在预处�脚本中暴露一个被称为 ipcRenderer.invoke 的函数�触�该处�程�(handler)。

我们将�渲染器添加一个�� ping() 的全局函数,该函数返�一个字符串 ‘pong’。

修改 preload.js 文件:

1
2
3
4
5
6
7
8
9
10
// preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
ping: () => ipcRenderer.invoke('ping') // 新�一个全局方法 versions.ping()
// 除函数之外,我们也�以暴露��
})

在主进程中设置 handle 监�器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// index.js
const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
// 监�渲染进程触�的方法,并返�一个字符串
ipcMain.handle('ping', () => 'pong')
createWindow()
})

在 renderer.js 中新�下�的内容:

1
2
3
4
5
6
7
8
// renderer.js
const func = async () => {
// 调用 'ping' 方法,并等待返�值
const response = await window.versions.ping()
console.log(response) // 打� 'pong'
}

func()

我们已�知�如何使用进程间的通信,如此一�就能在渲染进程,点击按钮触�主进程的相关�作。根�官网案例,�加案例��程度,设计一个�以切�白天/黑暗模�的桌�软件。

�行结�

代�仓库

BITCOIN CÜZDANINI QURUN

  1. 使用 webContents 模���消�
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
// index.js �略部分代�
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

// 使用 webContents.send() ��渲染进程��消�
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
click: () => win.webContents.send('update-counter', 1),
label: 'Increment'
},
{
click: () => win.webContents.send('update-counter', -1),
label: 'Decrement'
}
]
}
])
Menu.setApplicationMenu(menu)

win.loadFile('index.html')
// 打开开�工具
win.webContents.openDevTools()
}
  1. 通过预加载脚本暴露 ipcRenderer.on
1
2
3
4
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)),
})
  1. 在渲染页�监�事件
1
2
3
4
5
6
7
8
// renderer.js �略部分代�
const counter = document.getElementById('counter')

window.electronAPI.onUpdateCounter((value) => {
const oldValue = Number(counter.innerText)
const newValue = oldValue + value
counter.innerText = newValue.toString()
})
  1. �选:返�一个��

对��主进程到渲染器进程的 IPC,没有� ipcRenderer.invoke 等效的 API。 �过,您�以� ipcRenderer.on �调中将�����主进程。

1
2
3
4
5
6
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)),
// 给主进程��一个 counter-value 事件
counterValue: (value) => ipcRenderer.send('counter-value', value)
})

在主进程中,监� counter-value 事件并适当地处�它们。

1
2
3
ipcMain.on('counter-value', (_event, value) => {
console.log(value) // will print value to Node console
})

完�官方的案例,��一个客户端���作。

代�仓库�上

BITKOININ ISTIFADƏ ETDIYI ELEKTRIK MIQDARI

没有直�的方法�以使用 ipcMain 和 ipcRenderer 模�在 Electron 中的渲染器进程之间��消�。 为此,您有两�选择:

  • 将主进程作为渲染器之间的消æ�¯ä»£ç�†ã€‚ 这需è¦�将消æ�¯ä»�一个渲染器å�‘é€�到主进程,然å��主进程将消æ�¯è½¬å�‘到å�¦ä¸€ä¸ªæ¸²æŸ“器。
  • ä»�主进程将一个 MessagePort 传递到两个渲染器。 这将å…�许在åˆ�始设置å��渲染器之间直æ�¥è¿›è¡Œé€šä¿¡ã€‚

第一�方�,相当�把主进程当作中间的桥�,交互比较��。直�上代� 渲染进程之�进行通信演示

第二�方�,通过 MessageChannelMain 建立2个通�,在通过 webContents.postMessage 在主进程把对应的通���到渲染进程。preload.js 中我们�以监� ‘port’ 事件,通过 window.postMessage 将 port 端���到��的页�上。�样的,��的页�也�以用 window.onmessage 监����的数�。

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
// index.js 
// �略部分代�
// 创建窗�
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true, // Electron 12.0.0 �以上版本默认�用
preload: path.join(__dirname, 'preloadMain.js')
}
})
mainWindow.loadFile('index.html')
mainWindow.webContents.openDevTools()

const secondaryWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preloadSecondary.js')
}
})
secondaryWindow.loadFile('second.html')
secondaryWindow.webContents.openDevTools()

// 建立通�
const { port1, port2 } = new MessageChannelMain()

// webContents准备就绪�,使用postMessage��个webContents��一个端�。
mainWindow.once('ready-to-show', () => {
// 通知渲染进程,这里有一个 port 方法,�以��外一个窗�通信
mainWindow.webContents.postMessage('port', null, [port1])
})

secondaryWindow.once('ready-to-show', () => {
secondaryWindow.webContents.postMessage('port', null, [port2])
})
1
2
3
4
5
6
7
8
9
10
11
// preload.js
const { ipcRenderer } = require('electron')

const windowLoaded = new Promise((relose)=> {
window.onload = relose
})

ipcRenderer.on('port', async (event) => {
await windowLoaded // 等待页�加载完�
window.postMessage('channel-port', '*', event.ports)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--渲染进程页�-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Main Page</h1>
<script>
window.onmessage = (event) => {
if (event.source === window && event.data === 'channel-port') {
const [port] = event.ports;
port.onmessage = (event) => { // 打��收到的消�
console.log(event.data);
}
port.postMessage('i am main page'); // 测试��消�
}
}
</script>
</body>
</html>

完整代�,请查看 渲染进程之间直�通信。

BITCOINI QIT EDƏN ŞEY

Electron 的核心模�中没有�绑任何用�打包或分�文件的工具。 如�您在开�模�下完�了一个 Electron 应用,需�使用�外的工具�打包应用程� (也称为�分�文件) 并分�给用户 。 �分�文件�以是安装程� (例如 Windows 上的 MSI) 或者绿色软件 (例如 macOS 上的 .app 文件)。

Electron Forge 是一个处� Electron 应用程�打包�分�的一体化工具。 在工具底层,它将许多�有的 Electron 工具 (例如 @electron/packager� @electron/osx-sign�electron-winstaller 等) 组�到一起,因此您�必费心处���系统的打包工作。

安装�赖

1
2
npm install --save-dev @electron-forge/cli @electron/fuses @electron-forge/plugin-fuses
npx electron-forge import

转�脚本完��,Forge 会将一些脚本添加到您的 package.json 文件中。

1
2
3
4
5
6
7
{
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make"
}
}

�创建�分�文件,请使用项目中的 make 脚本,该脚本最终�行了 electron-forge make 命令。

1
npm run make

该 make 命令包�两步:

它将首先�行 electron-forge package ,把您的应用程� 代�� Electron 二进制包结�起�。 完�打包的代�将会被生�到一个特定的文件夹中。
然�它将使用这个文件夹为�个 maker �置生�一个�分�文件。
在脚本�行�,您应该看到一个 out 文件夹,其中包括�分�文件�一个包�其��的文件夹。

最�一步,我们需�对代�进行签�,请查看其他资料。

ABŞ DOLLARI ILƏ 1 BITKOININ QIYMƏTIALMA BITCOIN ƏLAVƏ EDIRABŞ-DA BITCOIN ALMAQ QANUNIDIRMI?

BITCOIN MAKSIMALIST NƏDIR

Rxjs,JavaScript的�应�扩展库,是一个基��观察�列的库,用�处�异步事件和编写基�事件驱动的程�。它是
ReactiveX(Reactive Extensions)的 JavaScript ��之一,�供了丰富的�作符和工具,用�处�和组�异步数��。

BITKOINƏ INVESTISIYA ETMƏYIN ÜSTÜNLÜKLƏRI VƏ MƏNFI CƏHƏTLƏRI

以下是 RxJS 的一些��概念:

  1. Observable(�观察对象):代表一个异步数��,�以��零个或多个值,并在完�或出错时�出信�。Observable
    �以被订阅,以便观察它�出的值或通知。

  2. Observer(观察者):观察者是一个��或函数集�,用�处�� Observable �出的值�错误和完�通知。它包�了三个方法:next
    用�处�正常的值,error 用�处�错误,complete 用�处�完�通知。

  3. Subscription(订阅):表示 Observable 和 Observer 之间的��。订阅�以用��消 Observable 的执行,释放资�或�止观察者�收值。

  4. Operators(�作符):用�处� Observable �出的数��的函数。RxJS �供了许多内置的�作符,如 map�filter�merge�concat
    等,以�许多其他用�数�转��筛选和组�的�作符。

  5. Subject(主题):是一个特殊类�的 Observable,它�许将值多路��给多个观察者。

  6. Schedulers(调度器):用��制何时和如何执行 Observable 的�作。RxJS �供了��类�的调度器,如 async�queue�asap
    等,�以用��制异步代�的执行顺�和调度方�。

RxJS �供了一��应�编程的范�,使得处�异步数���得更加简�和�活。它在�端开�中被广泛应用�处�用户输入�处�网络请求�管�状�等方�,�时也�以在
Node.js 等�端�境中使用。

BITKOINI HƏDƏFLƏYIN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rxjs Tutorial</title>
<!-- 使用 cdn 的模� -->
<script src="https://unpkg.com/rxjs@^7/dist/bundles/rxjs.umd.min.js"></script>
</head>

<body>
<script>
// 通常你会注册一个事件监�器
// document.addEventListener('click', () => console.log('Clicked!'));

// 使用 RxJS 的�,创建一个 observable �代替
const {fromEvent} = rxjs

fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));
</script>
</body>

</html>

091783 BITCOIN ÜÇÜN ABŞ DOLLARI

ps:函数�编程里�有个概念�:纯函数,是指在相�输入的情况下,始终返�相�输出,并且�产生副作用的函数。
具体请查看 函数�编程指�。

RxJS 的强大之处在�它能使用纯函数生�值。这�味�您的代��易出错。

通常情况下,您会创建一个�纯函数,而您代�中的其他部分�能会扰乱您的状�。

1
2
3
// �纯函数,副作用会对外部的 count 值造�影�,当外部状�改�时,产生的结�也会改�
let count = 0;
document.addEventListener('click', () => console.log(`Clicked ${++count} times`));

使用 RxJS �以隔离状�

1
2
3
4
5
6

const {fromEvent, scan} = rxjs;

fromEvent(document, 'click')
.pipe(scan((count) => count + 1, 0))
.subscribe((count) => console.log(`Clicked ${count} times`));

scan 扫��作符的工作���数组的 reduce �作符相�。它��一个值,并将该值暴露给一个�调函数。�调返�的值将�为下次�行�调时的下一个暴露值。

BITCOIN HASH LENTI

RxJS 拥有一系列�作符,�帮助您�制事件如何�� observables。 这就是使用普通 JavaScript 最多�许�秒点击一次的方法。(你也�以�解�节�函数)

1
2
3
4
5
6
7
8
9
let count = 0;
let rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', () => {
if (Date.now() - lastClick >= rate) {
console.log(`Clicked ${++count} times`);
lastClick = Date.now();
}
});
1
2
3
4
5
6
7
 const {fromEvent, throttleTime, scan} = rxjs;
fromEvent(document, 'click')
.pipe(
throttleTime(1000),
scan((count) => count + 1, 0)
)
.subscribe((count) => console.log(`Clicked ${count} times`));

其他���制�算符包括 filter�delay�debounceTime�take�takeUntil�distinct�distinctUntilChanged 等。

ARGENTINADA BITKOINLƏRI NECƏ ALMAQ OLAR

对��� observables 的值,你�以对其进行转�。
下�的代�展示的是如何累加�次点击的鼠标 x �标,先�看使用普通的 JavaScript

1
2
3
4
5
6
7
8
9
10
let count = 0;
const rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', (event) => {
if (Date.now() - lastClick >= rate) {
count += event.clientX;
console.log(count);
lastClick = Date.now();
}
});

使用 RxJS

1
2
3
4
5
6
7
8
9
const {fromEvent, throttleTime, map, scan} = rxjs;

fromEvent(document, 'click')
.pipe(
throttleTime(1000),
map((event) => event.clientX),
scan((count, clientX) => count + clientX, 0)
)
.subscribe((count) => console.log(count));

其他产生值的�作符有 pluck�pairwise� sample 等等。

BITCOIN ATM SAN ANTONIO

Observables 是多个值的惰性��集�,你�以�考下�的表格了解他们的关系。

Single Multiple
Pull Function Iterator
Push Promise Observable

下�是一个 Observable,它在订阅�立�(�步)��值 1�2�3,并在订阅调用�一秒���值 4,然�完��。

1
2
3
4
5
6
7
8
9
10
const { Observable } = rxjs;
const observable = new Observable((subscriber) => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
setTimeout(() => {
subscriber.next(4);
subscriber.complete();
}, 1000);
});

�调用 Observable 并查看这些值,我们需�订阅它。

1
2
3
4
5
6
7
8
9
10
11
12
13
console.log('just before subscribe');
observable.subscribe({
next(x) {
console.log('got value ' + x);
},
error(err) {
console.error('something wrong occurred: ' + err);
},
complete() {
console.log('done');
},
});
console.log('just after subscribe');

打�的结�为:

1
2
3
4
5
6
7
just before subscribe
got value 1
got value 2
got value 3
just after subscribe
got value 4
done

关� 拉� (Pull) vs. �� (Push) 大家�以访问官网了解。

RxJS 引入了 Observables,一个新的 JavaScript ��体系。Observable 是多个值的生产者,并将值“���给观察者(消费者)。

Observables �是没有�数, 但�以泛化为多个值的函数。

0,09428802 BITCOIN ÜÇÜN ABŞ DOLLARI

观察者是由 Observable ��的值的消费者。观察者�是一组�调函数的集�,�个�调函数对应一� Observable ��的通知类�:next�error 和 complete 。下�的示例是一个典�的观察者对象。

1
2
3
4
5
const observer = {
next: x => console.log('Observer got a next value: ' + x),
error: err => console.error('Observer got an error: ' + err),
complete: () => console.log('Observer got a complete notification'),
};

使用 observer 将其�供给 Observable 的订阅者。

1
observable.subscribe(observer);

当订阅 Observable 时,你也�以�供了一个�调函数作为�数,而�是一个对象。

1
observable.subscribe(x => console.log('Observer got a next value: ' + x));

Q SON BITCOINS

Subscription 是表示�清�资�的对象,通常是 Observable 的执行。Subscription 有一个��的方法,� unsubscribe,它�需�任何�数,�是用�清�由 Subscription �用的资�。在上一个版本的 RxJS 中,Subscription �� “Disposable� (�清�对象)。

1
2
3
4
5
6
const { interval } = rxjs;

const observable = interval(1000);
const subscription = observable.subscribe(x => console.log(x));

subscription.unsubscribe();

Subscription 基本上�有一个 unsubscribe() 函数,这个函数用�释放资�或��消 Observable 执行。

Subscription 还�以�在一起,这样一个 Subscription 调用 unsubscribe() 方法,�能会有多个 Subscription �消订阅 。你�以通过把一个 Subscription 添加到�一个上���这件事:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const { interval } = rxjs;

const observable1 = interval(400);
const observable2 = interval(300);

const subscription = observable1.subscribe(x => console.log('first: ' + x));
const childSubscription = observable2.subscribe(x => console.log('second: ' + x));

subscription.add(childSubscription);

setTimeout(() => {
// Unsubscribes BOTH subscription and childSubscription
subscription.unsubscribe();
}, 1000);

ps: interval 是一个创建�作符�, 返�一个�出无�自�的�列整数, 你�以选择固定的时间间隔进行��。 第一次并 没有立马���, 而是第一个时间段过���出。 默认情况下, 这个�作符使用 async 调度器� �供时间的概念,但也�以给它传递任�调度器。

ALTERNATIV BITCOIN ETHEREUM

RxJS Subject 是一�特殊类�的 Observable,它�许将值组播给许多观察者。普通的观察对象是�播的(�个订阅的观察者都拥有观察对象的独立执行),而主题则是多播的。

主题(Subject)就�一个�观察对象(Observable),但�以�许多观察者(Observer)进行组播。主体就�事件�射器:它们维护�许多监�者的注册表。

�个主题 (Subject)既是一个�观察对象(Observable),�是一个观察者(Observer)。

请查看�例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const { Subject } = rxjs;
const subject = new Subject();

subject.subscribe({
next: (v) => console.log(`observerA: ${v}`),
});
subject.subscribe({
next: (v) => console.log(`observerB: ${v}`),
});

subject.next(1);
subject.next(2);

// Logs:
// observerA: 1
// observerB: 1
// observerA: 2
// observerB: 2

由��个主题(Subject)也是一个观察者(Observer),所以我们也�以将一个主题(Subject)作为观察者(Observer)的订阅�数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const {Subject, from} = rxjs;

const subject = new Subject();

subject.subscribe({
next: (v) => console.log(`observerA: ${v}`),
});
subject.subscribe({
next: (v) => console.log(`observerB: ${v}`),
});

const observable = from([1, 2, 3]);

observable.subscribe(subject);

// Logs:
// observerA: 1
// observerB: 1
// observerA: 2
// observerB: 2
// observerA: 3
// observerB: 3

“多播 Observable� 通过 Subject ���通知,这个 Subject �能有多个订阅者,然而普通的 “�播 Observable� ���通知给�个观察者。

多播 Observable 在底层是通过使用 Subject 使得多个观察者�以看��一个 Observable 执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const {from, Subject, multicast} = rxjs;

const source = from([1, 2, 3]);
const subject = new Subject();
const multicasted = source.pipe(multicast(subject));

// These are, under the hood, `subject.subscribe({...})`:
multicasted.subscribe({
next: (v) => console.log(`observerA: ${v}`),
});
multicasted.subscribe({
next: (v) => console.log(`observerB: ${v}`),
});

// 在底层使用了 `source.subscribe(subject)`:
multicasted.connect();

关� refCount()�ReplaySubject�AsyncSubject�Void subject 请大家查看官网了解。

CÜZDANIMDA BITCOINLƏRI NECƏ ƏLDƏ EDƏ BILƏRƏM

尽管 RxJS 的根基是 Observable,但最有用的还是它的�作符。�作符是�许��的异步代�以声��的方�进行轻�组�的基础代��元。

Pipeable Operator(管��作符)是一个函数,它将一个 Observable 作为输入,并返��一个 Observable。它是一�纯粹的�作:�一个 Observable ����。

Creation Operators (创建�作符)是�一��作符,�以作为独立函数调用,创建一个新的 Observable 。

// 使用 of 创建 observable, 使用 map 对数�进行处�。

1
2
3
4
5
6
7
8
const { of, map } = rxjs;

of(1, 2, 3).pipe(map((x) => x * x)).subscribe((v) => console.log(`value: ${v}`));

// Logs:
// value: 1
// value: 4
// value: 9

通过 Pipe(管�)�以将多个�作符放在一起使用, 这段代�我们在��已��识到了,�在�以�解它了。

1
2
3
4
5
fromEvent(document, 'click').pipe(
throttleTime(1000), // 节� � 1s �能触�一次
map((event) => event.clientX), // 转�数�,�次点击��鼠标的 clientX
scan((count, clientX) => count + clientX, 0) // 对数�进行累加,类似 js array reduce
).subscribe((count) => console.log(count));

�解��作符是如何工作的,请查看 [Marble diagrams](Marble diagrams)。

�作符有���的用途,它们�作如下分类:创建�转��过滤�组��错误处��工具,等等。

BÜTÜN HYIP INVESTISIYA BITKOINLƏRI

调度器�制�何时�动 subscription 和何时��通知。它由三部分组�:

调度器是一�数�结�, 它知�如何根�优先级或其他标准�存储任务和将任务进行��。
调度器是执行上下文,它表示在何时何地执行任务(举例�说,立�的,或�一��调函数机制(比如 setTimeout 或 process.nextTick),或动画帧)。
调度器有一个(虚拟的)时钟,调度器功能通过它的 getter 方法 now() �供了“时间�的概念。在具体调度器上安�的任务将严格�循该时钟所表示的时间。

在下�的示例中,我们使用通常的简� Observable �步��值 1�2�3,并使用�作符 observeOn �指定使用异步调度器���这些值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const { Observable, observeOn, asyncScheduler } = rxjs;

const observable = new Observable((observer) => {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
}).pipe(
observeOn(asyncScheduler)
);

console.log('just before subscribe');
observable.subscribe({
next(x) {
console.log('got value ' + x);
},
error(err) {
console.error('something wrong occurred: ' + err);
},
complete() {
console.log('done');
},
});
console.log('just after subscribe');

输出结�:

1
2
3
4
5
6
just before subscribe
just after subscribe
got value 1
got value 2
got value 3
done

异步调度程�使用 setTimeout 或 setInterval 进行�作,�使给定的延迟为零。在 js 的事件循�中,异步消�队列在�步代�执行之�执行。

具体调度器的类�和使用指�请�考官网 scheduler。

ps:我了解到 Rxjs,它类似�一个事件�,因为我学习 Dart 的时候使用了 Stream 以� listen�StreamSubscription。

BITCOININ BUGÜNKÜ DƏYƏRIDƏRHAL BITCOIN TICARƏTIDIRBITCOIN PUNKLARI

NECƏ BITCOIN QAZANIRSINIZ

在 vue2 里�有一些公共的逻辑,我们�以使用 mixins(混入)。vue3 里�的逻辑�用,官方��的方�包括自定义组��函数,也就是自定义一些hooks,官方案例。

vue官方也有一些好用的 hooks 工具集,vueuse 其中的一些 hook 在 nuxt3 里�也是开箱�用,例如 useFetch。

useFetch �以��一个 fetch 请求,并且返��应�的数�。简化我们创建�应�数�并且请求赋值的�程,�常的方便。

ASIC BITCOIN MINER ISTIFADƏ OLUNUR

通常我们的�端项目都使用 axios ��装��请求,但是 useFetch 里���请求的�是其他的库 ofetch,具体�以查看 nuxt3 里� useFetch 的说�。

所以我就�装了一个基�自己的项目的 useFetch 处�请求的方法。代�如下:

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
import {ref, watch} from 'vue';
import {ElMessage} from "element-plus";

export const useFetch = (fetch, {
immediate = true, // 是�立���请求
effectArray = [], // 副作用�赖项,watch 监�,以便�新��请求
beforeFetch, // 请求��之�执行的�调
afterFetch, // 请求��之�执行的�调
onSuccess, // �功的�调函数
onError, // 错误的�调函数
updateDataOnError = false, // ��出错时是�更新数�
initialData = null // �始化数��应�数�,�用�页�渲染
} = {
immediate: true,
updateDataOnError: false,
effectArray: [],
initialData: null
}) => {

const isLoading = ref(false) // 是�正在加载中
const isFinished = ref(false) // 请求是�结�
const data = ref(initialData) // 根� initialData 创建的�应�的数�
const response = ref(null) // �应�的��返�的 response
const error = ref(null) // �应�的请求错误数�

const execute = () => {
beforeFetch && beforeFetch()
isLoading.value = true
isFinished.value = false
error.value = null
fetch().then(res => {
response.value = res
if (res?.status === '00000') {
onSuccess && onSuccess(res)
data.value = res.data
} else {
ElMessage.error(res?.message)
if (updateDataOnError) {
data.value = null
}
}
}).catch(e => {
onError && onError(e)
error.value = e
}).finally(() => {
afterFetch && afterFetch()
isLoading.value = false
isFinished.value = true
})
}

if (immediate) {
execute()
}

// 写一个防抖函数,延迟 触� execute,防止��
const debounceExecute = debounce(execute, 100)

// 监��赖项�生�化,�新�起请求
if (effectArray.length > 0) {
watch(effectArray, debounceExecute)
}

return {
isLoading,
isFinished,
response,
error,
data,
execute
}
}

使用案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const page = reactive({
pageNum: 1,
pageSize: 10
})

// �设需��应�的 table 列表数�
const initialData = reactive({
list: [],
total: 0
})

// 此处�略了 axios �例的一些�置
const {data} = useFetch(() => axios.post('/getList',{pageNum: page.pageNum,pageSize: page.pageSize}), {
initialData,
immediate: false,
effectArray: [() => page.pageNum,() => page.pageSize]
})

大家�以根�自己的项目需求修改一些�置。 类似的也�以根� react 里� useState, useEffect 创建一个 react 版本的 useFetch。ahooks 也是�错的导师,里��以学到很多�路。

BITCOIN ALMAQBITCOIN PAYPAL ILƏ MÜQAYISƏ EDINBITCOIN ILƏ 50 ABŞ DOLLARI

66 BITKOIN

element plus 快速开始官方指�。

首先使用 vite 创建一个 vue3 项目,请�考以往文章,�在赘述。

安装 element-plus

1
npm install element-plus --save

安装 unplugin-vue-components�unplugin-auto-import�sass

1
npm install -D unplugin-vue-components unplugin-auto-import sass

新建一个样�文件,覆盖 Element Plus 的 scss ��。

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/styles/element/index.scss
/* �需��写你需�的�� */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'primary': (
'base': green,
),
),
);

// 如��是按需导入,则�以忽略以下内容。
// 如�你想导入所有样�:
@use 'element-plus/theme-chalk/src/index.scss' as *;

在 main.js 引入 scss ��

1
2
3
4
5
6
7
8
9
10
11
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

import ElementPlus from 'element-plus'
// import 'element-plus/dist/index.css'

// 引入样�文件
import './styles/element/index.scss'

createApp(App).use(ElementPlus).mount('#app')

修改 vite.config.js

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
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
import path from 'path'

const pathSrc = path.resolve(__dirname, 'src')

// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'~/': `${pathSrc}/`,
},
},
css: {
preprocessorOptions: {
scss: {
// 如�在 main 入�文件引入了 '/styles/element/index.scss'
// 就�需�为�个样�内容注入�外代�,请注释下�的内容
// additionalData: `@use "~/styles/element/index.scss" as *;`,
},
},
},
plugins: [
vue(),
AutoImport({
resolvers: [
ElementPlusResolver({
importStyle: "sass",
})
],
}),
Components({
resolvers: [
ElementPlusResolver({
importStyle: 'sass',
}),
],
}),
],

})

0,0021538 BITCOINS ÜÇÜN ABŞ DOLLARIBITCOIN FRANCAIS TƏTBIQIBITCOIN QIYMƏTI YAHOO FINANCE

12 ILLIK BITCOIN

  1. LCP:最大的内容绘制完�时间。用以衡�用户能够看到有效信�的时长。
  2. FID:首次输入延迟。用以衡�用户输入的�馈体验。
  3. CLS:积累布局�移。用以衡�呈�给用户的视觉稳定性。
  4. FCP:首次内容绘制完�时间。用以衡�用户首个��元素的出�时长。

打开 chrome 按下 f12,点击 lighthouse 分�网页加载情况。

ASIC BITCOIN MINER BTC USB BLOKU ERUPTER 330 MH S

  1. DNS查询,找到对应�务器IP地�。
  2. 通过 HTTPS �议建议 TCP ��。
  3. �览器首先需�下载并解�HTML 生� DOM 树。
  4. CSS 解�和�建 CSSOM 树,生� CSSOM 树。
  5. 根� DOM 树和 CSSOM 树生�渲染树(Render Tree)。渲染树中�包括���的元素,例如 display: none 的元素。
  6. 布局:根�渲染树确定�个节点在�幕上的确切�置和大�。
  7. 绘制:�览器使用计算得到的布局信�绘制�个元素的内容。
  8. JavaScript 会被解��编译和解释。脚本被解�为抽象语法树(和�览器渲染�时进行)。

NIYƏ YALNIZ 21 MILYON BITKOIN

缓存:

  1. 设置 HTTP 的资�缓存策略 (�少 http 请求,防止多次进行 tcp 的三次�手SYN�SYN-ACK�ACK)
  2. ��资�使用 CDN
  3. 动�资�和��资�分离
  4. ��缓存
  5. PWA(Progressive Web App,�进�网页应用)

域�:

  1. 域�收拢(加载的资�在�一个域�,节�dns解�时间)

资�加载:

  • 预加载:
    <link rel="prefetch">
    用�告诉�览器在空闲时��加载指定资�,但�会阻�页�的加载
    1
    <link rel="prefetch" href="your-resource">
    <link rel="preload">
    用�指定当�页�必须立�加载的关键资�,它会在页�加载过程中立�开始下载并预加载资�,确�这些资�在页�渲染过程中�用。
  • 懒加载:
    按需加载,�高首�加载速度,动�import()
  • 选择性渲染:
    ��整个组件�新渲染,��新渲染部分内部组件,缓存组件或者函数。例如使用 React 内部的 memo�useMemo 和 useCallBack�useRef

BITCOIN MƏLUMAT CƏDVƏLLƏRI

  1. �绘(Repaint):
    �绘指的是在�改�页�布局的情况下,�新绘制元素的样�。也就是说,元素的几何�性没有改�,但是视觉效�(例如颜色�背景等)�生了�化。�绘是相对较轻�的�作,因为它�需��新计算元素的布局。
  • 修改元素的颜色ã€�背景等å�ªå½±å“�元素外观但ä¸�改å�˜å¸ƒå±€çš„æ ·å¼�å±�性。
  • 显示/éš�è—�元素。
  • 添加或删除类。
  1. ��(Reflow):
    ��是指�览器需��新计算并更新元素的几何�性,因此会影�整个页�布局。��是一�相对较昂贵的�作,因为它会触��新计算并更新元素�其所有�元素的布局信�。
  • 添加ã€�删除ã€�修改 DOM 元素。
  • 修改元素的尺寸ã€�ä½�ç½®ã€�è¾¹è·�ã€�填充等会影å“�布局的样å¼�å±�性。
  • æµ�览器窗å�£å¤§å°�å�‘生å�˜åŒ–。

��一定会引起�绘,因为页�布局的�化会导致元素的样��生�化。

DÜNYADA ƏN ÇOX BITKOINƏ SAHIB OLAN

FID:

  • å‡�å°‘ js 的执行时长
  • 分割 js 中ç¹�é‡�的计算任务

CLS:

  • 无尺寸的图åƒ�
  • 无尺寸的广告ã€�嵌入和iframe
  • 动æ€�注入的内容
  • 导致ä¸�å�¯è§�文本闪çƒ�(FOIT)/æ— æ ·å¼�文本闪çƒ�(FOUT)的网络字体
  • 在更新 DOM 之å‰�等待网络å“�应的æ“�作

5 ILLIK BITCOIN QRAFIKI

使用 chrome 的 performance 性能检测:

  • é‡�定å�‘耗时 = redirectEnd - redirectStart;
  • DNS查询耗时 = doinainLookupEnd - domain.LookupStart;
  • TCP链æ�¥è€—æ—¶ =connectEnd - connectStart;
  • HTTP请求耗时 =responseEnd - responseStart;
  • 解æ��dom树耗时 =domComplete - dominteractive;
  • 白å±�时间 = responseStart - navigationStart;
  • DOMready时间 = domContentLoadedEventEnd - navigationStart;
  • onioad时间 = lcadEventEnd - navigationStart;

ABSINTH OYUNU BITCOIN MINER

例如 vite�webpack 打包工具的 chunkSplit� tree-shaking。

711 BITCOIN ATM

Nuxtjs(文章已上线) 或者 Nextjs

IPHONE-DA BITCOINLƏRI MƏNIMSƏYINTAMAMILƏ PULSUZ OLAN 5 BITCOIN BIZNES IMKANIABŞ-DA BITKOININ BUGÜNKÜ QIYMƏTI

MƏDƏNÇILIK ETMƏDƏN BITKOINLƏR EDIN

Nuxt 是一个全栈框�,�以�� SSG�SSR(�务端渲染),官网地�。这篇文章主�记录使用 Nuxt �� SSR 的过程。

FIZIKI BITCOIN ŞƏKILLƏRI VAR

安装

1
pnpm dlx nuxi@latest init <project-name>

�动

1
pnpm dev

BITCOIN QIYMƏTI USD BUGÜNKÜ CANLI QRAFIK

在项目的根目录新建 layouts 目录,然�在里�新建 defalut.vue 文件。

1
2
3
4
5
6
7
8

<template>
<div>
<header>header</header>
<slot/>
<footer class="text-red text-center">footer</footer>
</div>
</template>

�设当�页�需�一个固定的头部和尾部内容,中间的内容根�路由动�展示。

slot �槽表示页�内部�展示的内容,一般展示的就是路由渲染的部分。NuxtPage 组件�以展示路由匹�的组件,你�以把它当中
vue-router 中的 RouterView。

1
2
3
4
5
6

<template>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</template>

0,04171 BITCOIN ÜÇÜN ABŞ DOLLARI

Nuxt 使用基�文件的路由系统,在项目的根目录创建 pages 目录以�对应的文件。

1
2
3
4
5
-| pages/
---| parent/
------| child.vue
---| parent.vue
---| index.vue

生�的路由如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[
{
path: '/',
component: '~/pages/index.vue',
name: 'index'
},
{
path: '/parent',
component: '~/pages/parent.vue',
name: 'parent',
children: [
{
path: 'child',
component: '~/pages/parent/child.vue',
name: 'parent-child'
}
]
}
]

��项目,�在访问 http://localhost:3000/parent/child 就能看到 child 组件里�的内容了。

��页�跳转�以使用 NuxtLink 组件。

1
2

<nuxt-link to="/parent">parent</nuxt-link>

使用编程�导航,请�考 vue-router

1
2
3
const route = useRoute();
route.push('/parent')
route.back()

动�路由:

在 pages 目录下�新�一个 user-[id].vue 文件,里�的内容如下

1
2
3
4

<template>
<div>user {{$route.params.id}}</div>
</template>

跳转方�:

1
<nuxt-link to="/user-1">user</nuxt-link>

匹�所有页�(404处�):

在 pages 目录下�新�一个 […slug].vue 文件,里�的内容如下:

1
2
3
<template>
<div>404</div>
</template>

BITCOIN ALMAQ ÜÇÜN ANONIM PROQRAMLAR

安装�赖

1
pnpm add -D @unocss/nuxt @unocss/preset-wind

打开 nuxt.config.ts 文件,新��置

1
2
3
4
5
6
7
8
9
export default defineNuxtConfig({
devtools: {enabled: true},
modules: [
'@unocss/nuxt',
],
css: [
'@unocss/reset/tailwind.css' // 使用 tailwind �置基础样�
]
})

为了使 unocss 兼容 tailwindcss 需�新� unocss.config.ts

1
2
3
4
5
6
7
8
// uno.config.ts
import {defineConfig, presetWind} from 'unocss'

export default defineConfig({
presets: [
presetWind(),
],
})

BITKOINLƏR NƏDIR VƏ NECƏ IŞLƏYIRLƏR?

��数��以使用 $fetch() 方法。Nuxt 内部集�了ofetch,$fetch �是它的别�,具体使用方法你�以�考
github 。

1
2
3
4
5
6
async function addTodo() {
const todo = await $fetch('/api/todos', {
method: 'POST',
body: {}
})
}

使用 useFetch() 方法�以把异步返�的数�转化��应���直�给客户端使用。

1
2
3
4
5
6
7
8

<script setup lang="ts">
const {data: count} = await useFetch('/api/count')
</script>

<template>
<p>Page visits: {{ count }}</p>
</template>

AUSTIN MITCHELL BITCOIN

使用内部 useState() 方法�以设置状�,第一个�数 key 是用�缓存的标识,第二个�数�以是一个返��始值的函数。

useState() 和 ref() 的选择看起�和 ref() 并没有什么两样,但是�际上是有差别的:

  • useState(key, init) 是有缓存性的,如æ�œ key ä¸�å�˜ï¼Œinit å�ªå�šåˆ�始化,则多次调用å�Œä¸€ä¸ª useState,结æ�œæ˜¯ä¸€æ ·çš„ï¼›

  • æœ�务端å�‹å¥½æ€§ï¼Œå¾—益äº�缓存性,å�³ä¾¿ init è¿”å›�值是ä¸�稳定的,也能ä¿�è¯�å‰�端注水时å‰�å��端状æ€�的一致性,比如åˆ�始值是éš�机值的情况。

1
2
3
4
5
6
7
8
<template>
<div>{{ counter }}</div>
<button @click="counter++">+1</button>
</template>

<script setup lang="ts">
const counter = useState('counter', () => 0)
</script>

BITCOIN ÜNVANINA BAXIN

安装

1
2
pnpm i @pinia/nuxt 
pnpm i -D @pinia-plugin-persistedstate/nuxt

修改 nuxt.config.ts 新�模� ‘@pinia/nuxt’

1
2
3
4
5
6
7
export default defineNuxtConfig({
// ... �略部分代�
modules: [
'@pinia/nuxt',
'@pinia-plugin-persistedstate/nuxt',
],
})

在项目根目录下新� store/useCounter.ts

1
2
3
4
5
6
7
8
9
10
11
export const useCounter = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
}
},
persist: true // 设置状��久化
})

14 APREL 2017 BITCOIN QƏZASI

在项目根目录下新� plugins/error.ts,Nuxt会自动加载 plugins 里�的�件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default defineNuxtPlugin((nuxt) => {
// vue 错误
nuxt.vueApp.config.errorHandler = (err, vm, info) => {
console.log(err, vm, info)
}

nuxt.hook('vue:error', (err) => {
console.log('vue:error:', err);
})

// nuxt �动出错
nuxt.hook('app:error', (err) => {
console.log('app:error:', err);
})
})

000002 BITCOIN DƏ AZDIR

设置全局�置,在根目录下�新� app.config.ts 文件。

1
2
3
4
5
6
7
8
9
export default defineAppConfig({
title: 'hello nuxt3',
theme: {
dark: true,
colors: {
primary: '#ff0000',
}
}
})

使用方�

1
2
3
const appConfig = useAppConfig()

console.log(appConfig)

设置�行时�置

1
2
3
4
5
6
7
8
9
10
export default defineNuxtConfig({
// ... �略部分代�
runtimeConfig: {
apiSecret: '', // �能用��务端的key
// 公共�置
public: {
apiBase: '/api'
}
}
})

使用方�

1
2
3
4
5
6
7
8
const runtimeConfig = useRuntimeConfig()
// �务端�境
if (process.server) {
console.log(runtimeConfig.apiSecret)
}

console.log(runtimeConfig.public.apiBase)

2010-CU ILDƏ BITCOIN HAQQINDA MƏQALƏLƏR

在本地新建 .env 文件,内容如下:

1
2
3
4
BASE_URL=http://localhost:3000
NUXT_APP_BASE_URL='/'
NUXT_API_SECRET=api_secret_token
NUXT_PUBLIC_API_SECRET=/api

在本地新建 .env.local 文件,内容如下:

1
2
3
4
BASE_URL=http://localhost:8080
NUXT_APP_BASE_URL='/'
NUXT_API_SECRET=api_secret_token_prod
NUXT_PUBLIC_API_BASE=/prod

默认本地的�动命令会�行:nuxt dev,读� .env 的�境���置。

如�读到相应的key它会覆盖之� runtimeConfig 里�的内容。

查看�行时的�境��:

1
2
const baseUrl = process.env.BASE_URL
console.log(baseUrl)

建议按照官网的约定��置�境��,NUXT_API_SECRET 对应 apiSecret,NUXT_PUBLIC_API_BASE 对应 public 里�的 apiBase。

1
2
const runtimeConfig = useRuntimeConfig()
console.log(runtimeConfig.public.apiBase)

�置一个�动命令,�设置���境的域�,例如:

在 package.json 里�新�一个一� script,代表你本地使用测试�境或者生产�境的域�,并且�置端��为 8080。

1
2
3
4
5
6
{
"scripts": {
"dev": "nuxt dev",
"dev:local": "nuxt dev --port=8080 --dotenv .env.local"
}
}

BITCOIN PAROL HÜCUMU

Nuxt �供 useSeoMeta�useHead 方法我们�以给�个页��独设置头部信�。

1
2
3
4
5
6
7

<script>
// �独的页�设置标题
useHead({
title: 'Config',
})
</script>

也�以使用 Nuxt �供的 <Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html>, <Head> 组件�设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup lang="ts">
import { ref } from 'vue'
const title = ref('Hello World')
</script>

<template>
<div>
<Head>
<Title>{{ title }}</Title>
<Meta name="description" :content="title"/>
<Style type="text/css" children="body { background-color: green; }"/>
</Head>

<h1>{{ title }}</h1>
</div>
</template>

通过�置文件全局设置头部信�。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export default defineNuxtConfig({
app: {
head: {
charset: 'utf-8', // 快�方�
viewport: 'width=device-width, initial-scale=1', // 快�方�
title: '我的网站标题',
meta: [
{name: 'description', content: '我的网站�述'},
{name: 'keywords', content: 'vue,react,node,go'},
{name: 'charset', content: 'utf-8'},
],
"link": [],
"style": [],
"script": []
}
},
})

ASIC MƏDƏN QURĞUSU BITCOIN

匿�中间件,具体页�执行:

1
2
3
4
5
6
definePageMeta({
middleware(to, from) {
console.log(to, from);
console.log('匿�中间件,具体页�执行')
}
})

具�中间件,在�些页�使用:

在项目根目录新建 middleware/a.ts

1
2
3
4
export default defineNuxtRouteMiddleware((to, from) => {
console.log(to, from)
console.log('a route middle')
})

在具体页�使用,�以使用多个路由中间件

1
2
3
definePageMeta({
middleware: ['a']
})

全局路由中间件:

在 middleware 目录新建 auth.global.ts,文件�称需�以 global.ts 结尾。

1
2
3
4
export default defineNuxtRouteMiddleware((to, from) => {
console.log(to, from)
console.log('全局路由中间件')
})

��一个导航守�的功能:

1
2
3
4
5
6
7
8
9
10
11
12
export default defineNuxtRouteMiddleware((to, from) => {
// �设用户未登录
const lsLogin = false;

// 终止导航
// return abortNavigation()

if (!lsLogin && to.path == '/config') {
return navigateTo('/login')
}

})

BITCOIN CRAPS

打包方�:

  • SSR: nuxt build
  • SPA: ssr:false + nuxt generate
  • SSG: nuxt generate
  • 预览: nuxt preview

ANOMALIYA BITCOIN IANƏBITCOIN XƏBƏRLƏRI PROQNOZU.1 BITCOIN

BITCOIN 2024-Ə BAXIN

Web Components 是一组技术,旨在使开�者能够创建��用�独立�框�的自定义组件。它包括以下四个主�技术:
Custom Elements(自定义元素):

  1. Custom Elements �许开�者定义自己的 HTML 元素,例如 。
    通过继承 HTMLElement 类,�以定义自定义元素的行为和生命周期方法。
    �以在 HTML 中使用这些自定义元素,并通过 JavaScript 进行�作。
    Shadow DOM(影� DOM):

  2. Shadow DOM �供了一�将元素的样�和行为�装在隔离的 DOM 树中的机制。
    �以在自定义元素中使用 Shadow DOM,以��样�和脚本的全局污染。
    影� DOM 的内容对外部文档是���的。

  3. HTML Templates(HTML 模�): HTML 模�是一�在文档中定义的�会被渲染的 HTML 片段。
    �以在模�中定义组件的结�,然�通过 JavaScript 克隆和激活模�。
    模�的使用使得组件的结��以在�被渲染的情况下进行定义和�纵。

INSANLAR BITCOIN SATA BILIRLƏR

customElementRegistry.define();
�数说�:

  • name:元素的å��称。必须以å°�写字æ¯�开头,包å�«ä¸€ä¸ªè¿�字符,并符å�ˆè§„范中有效å��称的定义中列出的一些其他规则。
  • constructor:自定义元素的æ�„造函数。
  • options:仅对äº�自定义内置元素,这是一个包å�«å�•ä¸ªå±�性 extends 的对象,该å±�性是一个字符串,命å��了è¦�扩展的内置元素。

有两�类�的自定义元素:

  • 定义内置元素(Customized built-in element)继承自标准的 HTML 元素,例如 HTMLImageElement 或 HTMLParagraphElement。它们的å®�ç�°å®šä¹‰äº†æ ‡å‡†å…ƒç´ çš„行为。
  • 独立自定义元素(Autonomous custom element)继承自 HTML 元素基类 HTMLElement。你必须ä»�头开始å®�ç�°å®ƒä»¬çš„行为。

ABŞ DOLLARI ÜÇÜN 0,5 BITCOIN CASH

1
<button is="hello-button">Click me</button>
1
2
3
4
5
6
7
8
9
// 这个按钮在被点击的时候说 "hello"
class HelloButton extends HTMLButtonElement {
constructor() {
super();
this.addEventListener('click', () => alert("Hello!"));
}
}

customElements.define('hello-button', HelloButton, {extends: 'button'});

自定义元素生命周期�调包括:

  • connectedCallback():æ¯�当元素添加到文档中时调用。规范建议开å�‘人员尽å�¯èƒ½åœ¨æ­¤å›�调中å®�ç�°è‡ªå®šä¹‰å…ƒç´ çš„设定,而ä¸�是在æ�„造函数中å®�ç�°ã€‚
  • disconnectedCallback():æ¯�当元素ä»�文档中移除时调用。
  • adoptedCallback():æ¯�当元素被移动到新文档中时调用。
  • attributeChangedCallback():在å±�性更改ã€�添加ã€�移除或替æ�¢æ—¶è°ƒç”¨ã€‚

.28 BITKOIN

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Element Example</title>
</head>
<body>

<my-custom-element></my-custom-element>

<script>
class MyCustomElement extends HTMLElement {
constructor() {
super();
console.log('Custom element created');
}

connectedCallback() {
console.log('Custom element connected to the document');
this.render();
}

disconnectedCallback() {
console.log('Custom element disconnected from the document');
}
// name �生�化的�性
// oldValue 旧值
// newValue 新值
attributeChangedCallback(name, oldValue, newValue) {
console.log(`�性 ${name} 已由 ${oldValue} �更为 ${newValue}。`);
}

render() {
this.innerHTML = `
<p>Hello, World!</p>
`
}
}

customElements.define('my-custom-element', MyCustomElement);
</script>

</body>
</html>

BITCOIN KYC

Shadow DOM(影� DOM)是 Web Components 技术的一部分,用�将元素的样�和行为�装在一个隔离的 DOM 树中。这��装使得元素的样�和脚本�会影�到文档的其他部分,�而��了全局作用域的污染和样�冲�。

  • å½±å­�宿主(Shadow host): å½±å­� DOM 附加到的常规 DOM 节点。
  • å½±å­�树(Shadow tree): å½±å­� DOM 内部的 DOM 树。
  • å½±å­�边界(Shadow boundary): å½±å­� DOM 终止,常规 DOM 开始的地方。
  • å½±å­�根(Shadow root): å½±å­�树的根节点。

一个 DOM 元素�以有以下两类 DOM �树:

  1. Light tree(光�树) —— 一个常规 DOM �树,由 HTML �元素组�。我们在之�章节看到的所有�树都是「光�的�。
  2. Shadow tree(影�树) —— 一个��的 DOM �树,�在 HTML 中�映,无法被察觉。

创建 Shadow DOM 需�调用宿主上的 attachShadow(),传入 { mode: “open� },这时页�中的 JavaScript �以通过影�宿主的 shadowRoot �性访问影� DOM 的内部。

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
<!DOCTYPE html>

<body>
<style>
/* 文档样�对 #elem 内的 shadow tree 无作用 (1) */
p {
color: red;
}
</style>

<div id="elem"></div>

<script>
const el = document.querySelector("#elem"); // 宿主

el.attachShadow({mode: "open"}); // 返� shadowRoot
// shadow tree 有自己的样� (2)
el.shadowRoot.innerHTML = `
<style> p { font-weight: bold; } </style>
<p>Hello, John!</p>
`;

// <p> �对 shadow tree 里�的查询�� (3)
alert(document.querySelectorAll("p").length); // 0
alert(el.shadowRoot.querySelectorAll("p").length); // 1
</script>
</body>

0 0035 BITKOIN

有两���的方法�在影� DOM 树中应用样�:

  • 编程å¼�,通过æ�„建一个 CSSStyleSheet 对象并将其附加到影å­�根。
  • 声æ˜�å¼�,通过在一个 <template> 元素的声æ˜�中添加一个 <style> 元素。

编程�样�步骤:

  1. 创建一个空的 CSSStyleSheet 对象
  2. 使用 CSSStyleSheet.replace() 或 CSSStyleSheet.replaceSync() 设置其内容
  3. 通过将其赋给 ShadowRoot.adoptedStyleSheets �添加到影�根

在 CSSStyleSheet 中定义的规则将局�在影� DOM 树的内部,以�我们将其分�到的任何其它 DOM 树。当你当样�内容较多,或者需��其他当自定义组件共享样�当时候,你�以考虑使用这�方。

1
2
3
4
// ... �略部分代� el
const sheet = new CSSStyleSheet();
sheet.replaceSync("p { color: red; border: 2px dotted black;}");
el.shadowRoot.adoptedStyleSheets = [sheet];

声��样�步骤:

  1. 将一个 <style> 元素包�在用�定义 web 组件的 <template> 元素中
  2. 将 <template> 中的内容�入到 shadowRoot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template id="my-element">
<style>
span {
color: red;
border: 2px dotted black;
}
</style>
<span>I'm in the shadow DOM</span>
</template>

<script>
// �略部分代� el
// 克隆模�内容
const template = document.querySelector("#my-element");
const clone = document.importNode(template.content, true);
el.shadowRoot.appendChild(clone)
</script>

�外还有使用 css 选择器的方��设置样�,具体请�考 css域。

BITCOIN PREMANUO

BITCOIN NECƏ ALMAQ OLAR

内建的 <template> 元素用�存储 HTML 模�。�览器将忽略它的内容,仅检查语法的有效性,但是我们�以在 JavaScript 中访问和使用它�创建其他元素。

模�(template)的 content �性�看作DocumentFragment —— 一�特殊的 DOM 节点。我们�以将其视为普通的DOM节点,除了它有一个特殊�性:将其�入�个�置时,会被�入的则是其�节点。

template 的使用方�上�的案例中已�展示过了,�下�我们�使用 slot (�槽)�加�活度。

BITCOIN SATMAQ ÜÇÜN ƏN YAXŞI VAXT NƏ VAXTDIR

  • å…·å��æ�’槽:
    在 shadow DOM 中, 定义了一个“�入点�,一个带有 slot=�X� 的元素被渲染的地方。然��览器执行�组�“:它� light DOM 中��元素并且渲染到 shadow DOM 中的对应�槽中。最�,正是我们想�的 —— 一个能被填充数�的通用组件。
  • 默认æ�’槽:
    shadow DOM 中第一个没有�字的 是一个默认�槽。它� light DOM 中��没有放置在其他�置的所有节点。
  • æ�’槽å��备内容:
    如�我们在一个 内部放点什么,它将�为�备内容。如� light DOM 中没有相应填充物的��览器就展示它。
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
<!doctype html>
<body>
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<div>Name:
<!-- 具��槽 -->
<slot name="username">
<!-- �槽�备内容 -->
Anonymous
</slot>
</div>
<div>Birthday:
<slot name="birthday"></slot>
</div>
<!-- 默认�槽 -->
<slot></slot>
`;
}
});
</script>

<user-card>
<div>default slot</div>
<!-- 注释下�一行的�槽内容,查看�槽的�备内容 -->
<span slot="username">John Smith</span>

<span slot="birthday">01.01.2001</span>
</user-card>
</body>

1 BITCOIN NECƏ IŞLƏYIR

如� 添加/删除了�槽元素,�览器将监视�槽并更新渲染。如�组件想知��槽的更改,那么�以用 slotchange 事件。

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
<!doctype html>
<body>
<custom-menu id="menu">
<!-- 在�始化时:slotchange: title 立�触�, 因为�自 light DOM 的 slot="title" 进入了相应的�槽。 -->
<span slot="title">Candy menu</span>
</custom-menu>

<script>
customElements.define('custom-menu', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `<div class="menu">
<slot name="title"></slot>
<ul><slot name="item"></slot></ul>
</div>`;

// shadowRoot �能有事件,所以使用 first child
// 如�组件想知��槽的更改,那么�以用 slotchange 事件
this.shadowRoot.firstElementChild.addEventListener('slotchange',
e => alert("slotchange: " + e.target.name)
);
}
});

const menu = document.querySelector('custom-menu');

setTimeout(() => {
// 1 秒�:slotchange: item 触�, 当一个新的 <li slot="item"> 被添加。
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">Lollipop</li>')
}, 1000);

setTimeout(() => {
menu.querySelector('[slot="tissssssssssssssssssstle"]').innerHTML = "New menu";
}, 2000);
</script>
</body>

QARANLIQ RP GARRYS MOD BITCOIN

如� shadow 树有 {mode: ‘open’} ,那么我们�以找出哪个元素被放进一个�槽,�之亦然,哪个�槽分�了给这个元素:

  • node.assignedSlot: å±�性是在使用 Shadow DOM 的情况下,用äº�è�·å�–包å�«å½“å‰�节点的 Shadow DOM æ�’槽(Slot)的å±�性。在 Shadow DOM 中,æ�’槽是一ç§�机制,用äº�å°†å­�元素分é…�到 Shadow DOM 中的特定ä½�置。æ¯�个æ�’槽都有一个对应的 HTMLSlotElement 对象。 当你有多个æ�’槽时,assignedSlot å±�性å�¯ä»¥å¸®åŠ©ä½ ç¡®å®šå½“å‰�节点å±�äº�哪个æ�’槽。
  • slot.assignedNodes({flatten: true/false}): è¿”å›�分é…�ç»™æ�’槽的 DOM 节点。默认情况下,flatten 选项为 false。如æ�œæ˜¾å¼�地设置为 true,则它将更深入地查看æ‰�平化 DOM ,如æ�œåµŒå¥—了组件,则返å›�嵌套的æ�’槽,如æ�œæœªåˆ†é…�节点,则返å›�备用内容。
  • slot.assignedElements({flatten: true/false}): – è¿”å›�分é…�ç»™æ�’槽的 DOM 元素。
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
<!doctype html>
<body>
<custom-menu id="menu">
<span slot="title">Candy menu</span>
<li slot="item">Lollipop</li>
<li slot="item">Fruit Toast</li>
</custom-menu>

<script>
customElements.define('custom-menu', class extends HTMLElement {
items = []

connectedCallback() {
// 需�shadow 树有 {mode: 'open'}

/*
node.assignedSlot �性是在使用 Shadow DOM 的情况下,用���包�当�节点的 Shadow DOM �槽(Slot)的�性。
这个�性返�一个表示节点所��槽的 HTMLSlotElement 对象。
在 Shadow DOM 中,�槽是一�机制,用�将�元素分�到 Shadow DOM 中的特定�置。
�个�槽都有一个对应的 HTMLSlotElement 对象。
当你有多个�槽时,assignedSlot �性�以帮助你确定当�节点��哪个�槽。
*/

/*
slot.assignedNodes({flatten: true/false}) – 返�分�给�槽的 DOM 节点。
默认情况下,flatten 选项为 false。如�显�地设置为 true,则它将更深入地查看�平化 DOM ,如�嵌套了组件,则返�嵌套的�槽,
如�未分�节点,则返�备用内容。
*/
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `<div class="menu">
<slot name="title"></slot>
<ul><slot name="item"></slot></ul>
</div>`;

// �槽能被添加/删除/代替
this.shadowRoot.firstElementChild.addEventListener('slotchange', e => {

let slot = e.target;

const assignedNodes = slot.assignedNodes();
assignedNodes.forEach(node => {
console.log(node.textContent); // dom 的 文本内容
console.log('Assigned to slot:', node.assignedSlot); // HTMLSlotElement 对象
});

if (slot.name == 'item') {
// slot.assignedElements({flatten: true/false}) – 返�分�给�槽的 DOM 元素
this.items = slot.assignedElements().map(elem => elem.textContent);
alert("Items: " + this.items);
}
});
}
});

const menu = document.getElementById('menu');

// items 在 1 秒�更新
setTimeout(() => {
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">Cup Cake</li>')

}, 1000);
</script>
</body>

�考资料:javascript.info/web-components�mdnI/Web_components