


Bauen von Ethereum Dapps: Whitelisting & Testen einer Geschichte DAO
Feb 16, 2025 pm 12:24 PM
Schlüsselpunkte
- Story Dao verwendet den eigenen Vertrag von Openzeppelin, um sicherzustellen, dass nur der Eigentümer Verwaltungsfunktionen ausführen kann und so die Sicherheit und Kontrolle über DApp -Operationen verbessert.
- Story DAO -Verträge haben einstellbare Gebühren- und Dauerparameter und sind mit Sicherheitsmaßnahmen ausgestattet, um nicht autorisierte Änderungen zu verhindern, um sicherzustellen, dass nur der Eigentümer kritische Einstellungen ändern kann.
- Whitelist Management in Story DAO wird durch bezahlte Funktionen implementiert, die einen automatischen und bedingten Zugriff basierend auf dem Beitrag des Absenders ermöglichen.
- integrierte Teststrategien, einschließlich Solidität und JavaScript -Tests, sind entscheidend für die Überprüfung der Funktionalität und Sicherheit von Story DAO, wodurch der robuste Betrieb vor der Bereitstellung sichergestellt wird.
- Der Bereitstellungsprozess von Story DAO wird durch TRUFFL mit spezifischen Migrationsskripten und -konfigurationen vereinfacht, was einen reibungslosen Übergang von der Entwicklungsumgebung in die Produktionsumgebung ermöglicht.
Der dritte Teil dieser Tutorial -Serie beschreibt das Erstellen eines Dapps mit Ethereum, in der wir das Token für das Ethereum -Testnetzwerk Rinkeby erstellen und bereitstellen. In diesem Abschnitt werden wir damit beginnen, den DAO -Code von Story zu schreiben.
Wir werden uns anhand der im Einführungsartikel aufgeführten Kriterien führen.
Vertragsübersicht
Lassen Sie uns einen neuen Vertrags -StoryDao.sol erstellen, dessen Rahmen wie folgt lautet:
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
Wir importieren Safemath, um wiederere Berechnungen durchzuführen, aber diesmal verwenden wir auch Zeppelins eigene Vertrag, mit dem jemand die Geschichte „besitzt“ und bestimmte Funktionen nur für Administratoren ausführen kann. Einfach ausgedrückt, es reicht aus, dass unser StoryDao besitzt wird.
Wir verwenden auch den einzigen Besitzermodifikator in diesem Vertrag. Funktionsmodifikatoren sind im Grunde genommen Erweiterungen und Plug-Ins für Funktionen. Der einzige Besitzermodifikator sieht folgendermaßen aus:
modifier onlyOwner() { require(msg.sender == owner); _; }
Wenn nur der Funktionen der Funktion hinzugefügt wird, wird der Körper der Funktion an den Ort eingefügt, an dem sich der Teil befindet, und der vorherige Teil wird zuerst ausgeführt. Durch die Verwendung dieses Modifikators prüft die Funktion daher automatisch, ob der Nachrichtensender der Vertragsinhaber ist, und setzt sich dann wie gewohnt fort, wenn er wahr ist. Wenn nicht, wird es abstürzen.
Durch die Verwendung des einzigen Modifikators für Besitzer zu Funktionen, die die Gebühren und andere Parameter von Story DAO ändern, stellen wir sicher, dass nur Administratoren diese Änderungen vornehmen können.
Test
Testen wir die Anfangsfunktion.
Wenn es nicht vorhanden ist, erstellen Sie einen Ordnertest. Erstellen Sie dann die Dateien testStorydao.sol und testStoryDao.js. Da es keine native Methode gibt, um Ausnahmen zu testen, werden Helper/erwartungsstrow.js auch mit Folgendem erstellt:
export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };
Hinweis: Soliditätstests werden normalerweise verwendet, um niedrige, vertragsbasierte Funktionen zu testen, dh die interne Struktur von intelligenten Verträgen. JS -Tests werden häufig verwendet, um zu testen, ob ein Vertrag von außen korrekt interagieren kann, was unsere Endbenutzer tun.
In testStoryDao.Sol den folgenden Inhalt einlegen:
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
Dies wird überprüfen, ob der StoryDao -Vertrag mit den richtigen Gebühren und Dauerzahlen ordnungsgemäß eingesetzt wird. Die erste Zeile stellt sicher, dass sie durch das Lesen aus der bereitgestellten Adressliste bereitgestellt wird. Der letzte Abschnitt enthält einige Behauptungen - Überprüfen Sie, ob die Deklaration wahr oder falsch ist. In unserem Beispiel vergleichen wir die Nummer mit dem Anfangswert des implementierten Vertrags. Wann immer es "wahr" ist, wird der Abschnitt "Assert.Equals" ein Ereignis ausstellen, das "wahr" sagt, worauf Trüffel während des Tests hört.
In testStoryDao.js den folgenden Inhalt einlegen:
modifier onlyOwner() { require(msg.sender == owner); _; }
Damit unsere Tests erfolgreich ausgeführt werden können, müssen wir auch Trüffel erzählen, dass wir StoryDao bereitstellen möchten - weil es das für uns nicht tun wird. Erstellen wir also 3_Deploy_Storydao.js in der Migration mit fast dem gleichen Inhalt wie die Migration, die wir zuvor geschrieben haben:
export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };
Zu diesem Zeitpunkt sollten wir auch die Paket.json -Datei im Root des Projektordners aktualisieren (oder, wenn nicht), mit den bisher benötigten Abhängigkeiten und dem, was wir in Zukunft benötigen, enthalten:
pragma solidity ^0.4.24; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/StoryDao.sol"; contract TestStoryDao { function testDeploymentIsFine() public { StoryDao sd = StoryDao(DeployedAddresses.StoryDao()); uint256 daofee = 100; // 百分之几,即 100 为 1% uint256 whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 uint256 durationDays = 21; // 故事章节持续时间(天) uint256 durationSubmissions = 1000; // 故事章节持续时间(条目) Assert.equal(sd.daofee(), daofee, "初始 DAO 费用应为 100"); Assert.equal(sd.whitelistfee(), whitelistfee, "初始白名单费用应为 0.01 以太币"); Assert.equal(sd.durationDays(), durationDays, "初始天数持续时间应设置为 3 周"); Assert.equal(sd.durationSubmissions(), durationSubmissions, "初始提交持续时间应设置为 1000 个条目"); } }
und .babelRC -Dateien, die Folgendes enthalten:
import expectThrow from './helpers/expectThrow'; const StoryDao = artifacts.require("StoryDao"); contract('StoryDao Test', async (accounts) => { it("should make sure environment is OK by checking that the first 3 accounts have over 20 eth", async () =>{ assert.equal(web3.eth.getBalance(accounts[0]).toNumber() > 2e+19, true, "Account 0 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[1]).toNumber() > 2e+19, true, "Account 1 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[2]).toNumber() > 2e+19, true, "Account 2 has more than 20 eth"); }); it("should make the deployer the owner", async () => { let instance = await StoryDao.deployed(); assert.equal(await instance.owner(), accounts[0]); }); it("should let owner change fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; instance.changedaofee(newDaoFee, {from: accounts[0]}); instance.changewhitelistfee(newWhitelistFee, {from: accounts[0]}); instance.changeDurationDays(newDayDuration, {from: accounts[0]}); instance.changeDurationSubmissions(newSubsDuration, {from: accounts[0]}); assert.equal(await instance.daofee(), newDaoFee); assert.equal(await instance.whitelistfee(), newWhitelistFee); assert.equal(await instance.durationDays(), newDayDuration); assert.equal(await instance.durationSubmissions(), newSubsDuration); }); it("should forbid non-owners from changing fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; await expectThrow(instance.changedaofee(newDaoFee, {from: accounts[1]})); await expectThrow(instance.changewhitelistfee(newWhitelistFee, {from: accounts[1]})); await expectThrow(instance.changeDurationDays(newDayDuration, {from: accounts[1]})); await expectThrow(instance.changeDurationSubmissions(newSubsDuration, {from: accounts[1]})); }); it("should make sure the owner can only change fees and duration to valid values", async () =>{ let instance = await StoryDao.deployed(); let invalidDaoFee = 20000; let invalidDayDuration = 0; let invalidSubsDuration = 98; await expectThrow(instance.changedaofee(invalidDaoFee, {from: accounts[0]})); await expectThrow(instance.changeDurationDays(invalidDayDuration, {from: accounts[0]})); await expectThrow(instance.changeDurationSubmissions(invalidSubsDuration, {from: accounts[0]})); }) });
Wir müssen auch Babel in der Trüffelkonfiguration benötigen, damit es weiß, dass es beim Kompilieren von Tests verwendet werden sollte.
Hinweis: Babel ist ein Add-On zu NodeJs, mit dem wir das JavaScript der nächsten Generation in der aktuellen Generation von NodeJs verwenden können, damit wir Importe und andere Inhalte schreiben können. Wenn Sie das nicht verstehen, ignorieren Sie es einfach und fügen Sie es wörtlich ein. Nach der Installation müssen Sie sich möglicherweise nie wieder mit diesem Problem befassen.
var Migrations = artifacts.require("./Migrations.sol"); var StoryDao = artifacts.require("./StoryDao.sol"); module.exports = function(deployer, network, accounts) { if (network == "development") { deployer.deploy(StoryDao, {from: accounts[0]}); } else { deployer.deploy(StoryDao); } };
Führen Sie jetzt endlich Trüffel -Test aus. Die Ausgabe sollte dem ähnlich sein:
Weitere Informationen zum Testen finden Sie in diesem Tutorial, das wir speziell auf die Prüfung von intelligenten Verträgen vorbereitet haben.
Im nachfolgenden Abschnitt dieses Kurses werden wir die Tests überspringen, da das Eingeben von wörtlichem Tutorial das Tutorial zu lang macht. Weitere Informationen finden Sie jedoch auf den endgültigen Quellcode des Projekts, um alle Tests zu überprüfen. Der Prozess, den wir gerade abgeschlossen haben, hat eine Umgebung für die Tests eingerichtet, sodass Sie die Tests ohne weitere Einrichtung schreiben können.
Whitelist
Erstellen wir einen Whitelisting -Mechanismus, mit dem Benutzer am Erstellen von Geschichten teilnehmen können. Fügen Sie das folgende Funktionsframework zu StoryDao.Sol hinzu:
{ "name": "storydao", "devDependencies": { "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.24.1", "babel-preset-stage-3": "^6.17.0", "babel-polyfill": "^6.26.0", "babel-register": "^6.23.0", "dotenv": "^6.0.0", "truffle": "^4.1.12", "openzeppelin-solidity": "^1.10.0", "openzeppelin-solidity-metadata": "^1.2.0", "openzeppelin-zos": "", "truffle-wallet-provider": "^0.0.5", "ethereumjs-wallet": "^0.6.0", "web3": "^1.0.0-beta.34", "truffle-assertions": "^0.3.1" } }
Unbenannte Funktionsfunktion () wird als Fallback -Funktion bezeichnet, die aufgerufen wird, wenn Fonds an diesen Vertrag gesendet werden, aber keine spezifischen Anweisungen (d. H. Keine andere Funktion wird speziell aufgerufen). Dies ermöglicht es den Menschen, sich StoryDao anzuschließen, indem sie nur Äther nach Dao schicken und sofort an Whitelist kauft oder sofort anhand von Token gekauft wird, basierend darauf, ob sie whitelistet sind oder nicht.
Die Whitelistender -Funktion wird an Whitelist verwendet und kann direkt aufgerufen werden. Wir werden jedoch sicherstellen, dass die Fallback -Funktion sie automatisch nach dem Empfang etwas Äthers aufruft, sofern der Absender noch nicht weißeListet ist. Die Whitelistadaddress -Funktion wird öffentlich erklärt, da sie auch aus anderen Verträgen aufgerufen werden sollte, während die Fallback -Funktion extern ist, da die Mittel nur von externen Adressen an diese Adresse gesendet werden. Der Vertrag, der diesen Vertrag anruft, kann die erforderlichen Funktionen leicht direkt aufrufen.Lassen Sie uns zuerst mit der Fallback -Funktion umgehen.
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
Als nächstes kümmern wir uns um die Whitelist.
modifier onlyOwner() { require(msg.sender == owner); _; }
Wir beginnen mit einigen Gesundheitscheckfunktionen: Der Absender muss whitelist oder schwarzlistet (verboten) werden und müssen genügend Gebühren gesendet haben, um sie zu bezahlen. Wenn diese Bedingungen zufriedenstellend sind, wird die Adresse dem Whitelist hinzugefügt, ein Whitelist -Ereignis wird ausgestellt. Wenn schließlich die Anzahl der gesendeten Ether größer ist als die Anzahl der zur Zahlung der Whitelist -Gebühr erforderlich, wird der Rest zum Kauf verwendet, um den Kauf zu kaufen. die Token.
Hinweis: Wir verwenden Sub anstelle von - um zu subtrahieren, da dies die Safemath -Funktion für sichere Berechnungen ist.
Solange Benutzer 0,01 Äther oder mehr Äther an den StoryDao -Vertrag senden, können sie sich jetzt selbst oder andere Whitelist selbst anwenden.
Schlussfolgerung
In diesem Tutorial haben wir den ersten Teil von DAO gebaut, aber es gibt noch viel zu tun. Bleiben Sie dran: Im nächsten Abschnitt werden wir uns mit dem Hinzufügen von Inhalten zur Geschichte befassen!FAQs zum Aufbau von Ethereum Dapps und Whitelists
Was sind die Voraussetzungen für den Bau eines Ethereum Dapp?
Bevor Sie mit dem Aufbau eines Ethereum -Dapps beginnen, müssen Sie ein grundlegendes Verständnis der Blockchain -Technologie, Ethereum und intelligenten Verträgen haben. Sie sollten auch mit Programmiersprachen wie JavaScript und Solidität vertraut sein, die zum Schreiben intelligenter Verträge über Ethereum verwendet werden. Darüber hinaus müssen Sie Tools wie Node.js, Trüffel, Ganache und Metamaske installieren, die für die Entwicklung und Prüfung von Dapps von entscheidender Bedeutung sind.
Wie funktioniert der Whitelisting -Prozess in DAPP?
Whitelisten sind Sicherheitsmaßnahmen in DAPPs, die den Zugriff auf bestimmte Merkmale oder Bereiche der Anwendung einschränken. Beim Erstellen einer Liste genehmigter Adressen, die die Interaktion mit DApps ermöglichen. Von diesen Adressen initiierten Transaktionen werden nur akzeptiert, während andere abgelehnt werden. Dies hilft, nicht autorisierten Zugang und böswillige Aktivitäten zu verhindern.
Welche Rolle spielen intelligente Verträge in Dapps?
Smart Contracts sind Selbstverträge, und ihre Vereinbarungsbedingungen werden direkt in den Code geschrieben. Sie spielen eine wichtige Rolle bei Dapps, da sie die Ausführung der Geschäftslogik in der Blockchain automatisieren. Sie gewährleisten Transparenz, Sicherheit und Unveränderlichkeit, da sie nach dem Einsatz nicht geändert oder manipuliert werden können.
Wie testet ich meinen Dapp?
Tests sind ein entscheidender Bestandteil der DAPP -Entwicklung, um seine Funktionalität und Sicherheit zu gewährleisten. Sie können Tools wie Trüffel und Ganache zum Testen verwenden. TRUFFL bietet Ethereum eine Entwicklungsumgebung, ein Testframework und eine Asset -Pipeline, während Sie mit Ganache eine private Ethereum -Blockchain zum Testen erstellen können.
Was ist DAO und wie hat es mit Dapp zu tun?
Dao repräsentiert eine dezentrale autonome Organisation. Es handelt sich um einen Organisationstyp, der durch Regeln dargestellt wird, die als Computerprogramme codiert werden, die transparent sind, von Organisationsmitgliedern kontrolliert und von der Zentralregierung nicht betroffen sind. Die Finanztransaktionen und -regeln von DAO werden auf der Blockchain aufbewahrt, was es zu einem Dapp macht.
Wie kann ich die Sicherheit meines Dapps gewährleisten?
Stellen Sie sicher, dass die Sicherheit des DAPP eine Vielzahl von Praktiken beinhaltet. Dies beinhaltet das Schreiben sicherer intelligenter Verträge, gründliche Tests, Durchführung von Sicherheitsaudits und das Aufbewahren von Software und Abhängigkeiten auf dem neuesten Stand. Es ist auch wichtig, Best Practices für sichere Codierung zu befolgen und über die neuesten Sicherheitslücken und Bedrohungen im Blockchain -Bereich auf dem Laufenden zu bleiben.
Was ist Metamask und warum ist es wichtig für die DAPP -Entwicklung?
Metamask ist eine Browser -Erweiterung, mit der Sie mit der Ethereum -Blockchain und Dapps direkt von Ihrem Browser interagieren können. Es kann auch als Ethereum-Brieftasche dienen, um Ihre Ethereum- und ERC-20-Token zu verwalten. Es ist wichtig bei der DAPP-Entwicklung, da es Benutzern eine benutzerfreundliche Oberfläche bietet, damit Benutzer mit Ihrem DApp interagieren können, ohne einen vollständigen Ethereum-Knoten auszuführen.
Wie bereitet ich meinen Dapp bereit?
Sobald Sie Ihren DAPP entwickelt und getestet haben, können Sie ihn für das Ethereum Mainnet oder das TestNet bereitstellen. Dies beinhaltet das Zusammenstellen Ihrer intelligenten Verträge, die Bereitstellung von Blockchain und die Verbindung Ihrer DAPPs mit diesen Verträgen. Sie können diesen Vorgang mithilfe von Tools wie TRUFFL und Infura abschließen.
Was sind die Herausforderungen der DAPP -Entwicklung?
DAPP -Entwicklung steht vor einigen Herausforderungen. Dies beinhaltet den Umgang mit Skalierbarkeitsproblemen des Ethereum-Netzwerks, der Gewährleistung der Sicherheit von DApps, der Verwaltung der volatilen Gaspreise für Transaktionen und der Bereitstellung einer benutzerfreundlichen Schnittstelle. Es erfordert auch ein Auge auf sich schnell entwickelnde Blockchain -Technologien und -vorschriften.
Wie aktualisiere ich meinen DAPP nach der Bereitstellung?
Die Aktualisierung von DApps nach der Bereitstellung kann eine Herausforderung sein, da intelligente Verträge auf der Blockchain unveränderlich sind. Sie können jedoch aufgerüstbare Verträge entwerfen, indem Sie Daten und Logik in verschiedene Verträge oder Aufrüstungsverträge mithilfe delegierter Anrufe trennen. Die Planung für Upgrades und Änderungen während der Entwurfsphase des DAPP ist sehr wichtig.
Das obige ist der detaillierte Inhalt vonBauen von Ethereum Dapps: Whitelisting & Testen einer Geschichte DAO. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heißer Artikel

Hot-Tools-Tags

Heißer Artikel

Heiße Artikel -Tags

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Hinter dem ersten Android -Zugang zu Deepseek: die Kraft der Frauen sehen

Deepseeks 'erstaunlicher' Gewinn: Die theoretische Gewinnspanne beträgt bis zu 545%!

Ein weiteres nationales Produkt aus Baidu ist mit Deepseek verbunden.

Minea startet seine erste Deepseek -Klimaanlage: AI Voice Interaction kann 400.000 Befehle erzielen!

Top 10 beste kostenlose Backlink -Checker -Tools in 2025

Schnelltechnik für die Webentwicklung
