Hardhat环境搭建(六)---无需翻墙

Hardhat环境搭建

官方地址

node环境

npm环境

git环境

在这里插入图片描述

安装hardhat

npm init

npminit是什么

在node开发中使用npm init会生成一个pakeage.json文件,这个文件主要是用来记录这个项目的详细信息的,它会将我们在项目开发中所要用到的包,以及项目的详细信息等记录在这个项目中。方便在以后的版本迭代和项目移植的时候会更加的方便。也是防止在后期的项目维护中误删除了一个包导致的项目不能够正常运行。使用npm init初始化项目还有一个好处就是在进行项目传递的时候不需要将项目依赖包一起发送给对方,对方在接受到你的项目之后再执行npm install就可以将项目依赖全部下载到项目里。

在这里插入图片描述

package name:                      你的项目名字叫啥
version:                          版本号
description:                       对项目的描述
entry point:                      项目的入口文件(一般你要用那个js文件作为node服务,就填写那个文件)
test command:                     项目启动的时候要用什么命令来执行脚本文件(默认为node app.js)
git repository:                    如果你要将项目上传到git中的话,那么就需要填写git的仓库地址(这里就不写地址了)
keywirds:                       项目关键字(我也不知道有啥用,所以我就不写了)
author:                         作者的名字(也就是你叫啥名字)
license:                        发行项目需要的证书(这里也就自己玩玩,就不写了)

npm install --save-dev hardhat

npm install --save-dev是怎么回事

npm install 和 npm i 是一样的,都是安装package.json文件中的依赖包。
安装单独的依赖包时,npm install

  • –save 等同于 -S (常用,可保存在package.json文件中),
    -S, --save 安装包信息将加入到dependencies(生产阶段的依赖,也就是项目运行时的依赖,就是程序上线后仍然需要依赖)
    –save-dev 等同于 -D
  • -D, --save-dev 安装包信息将加入到devDependencies(开发阶段的依赖,就是我们在开发过程中需要的依赖,只在开发阶段起作用。)

区别:
在用npm install 单独安装 npm 包时,有两种命令参数可以把它们的信息写入 package.json 文件,一个是npm install–save,另一个是 npm install –save-dev,他们表面上的区别是:
–save 会把依赖包名称添加到 package.json 文件 dependencies 下,
–save-dev 则添加到 package.json 文件 devDependencies下 ,譬如:

{
	"dependencies": {
		 "@ant-design/pro-layout": "^4.5.0",
	    "@antv/data-set": "^0.10.2",
	    "antd": "^3.19.1",
	},
	"devDependencies": {
		"babel-core": "^6.0.0",
		"babel-loader": "^6.0.0",
		"babel-preset-latest": "^6.0.0",
		"cross-env": "^3.0.0",
		"css-loader": "^0.25.0",
		"file-loader": "^0.9.0",
		"vue-loader": "^11.1.4",
		"vue-template-compiler": "^2.2.1",
		"webpack": "^2.2.0",
		"webpack-dev-server": "^2.2.0"
	}
}

不过这只是它们的表面区别。它们真正的区别是:
dependencies是运行时的依赖,
devDependencies是开发时的依赖。
即devDependencies 下列出的模块,是我们开发时用的,比如 我们安装 js的压缩包gulp-uglify 时,我们采用的是 “npm install –save-dev gulp-uglify ”命令安装, 因为我们在发布后用不到它,而只是在我们开发才用到它。

举例:
像jQuery库或者Angular框架类似的,我们在开发完后后肯定还要依赖它们,否则就运行不了,这是dependencies;
而写 ES6 代码,需要babel转换成es5,转换完成后,我们只需要转换后的代码,上线的时候,直接把转换后的代码部署上线,不需要babel了,上线了不需要,这就是devDependencies。
而如果用了 jQuery,由于发布之后还是依赖jQuery,所以是dependencies。

补充:
正常使用 npm install 时,会下载dependencies和devDependencies中的模块,当使用npm install –production或者注明NODE_ENV变量值为production时,只会下载dependencies中的模块。

npm install --save-dev hardhat

安装

在这里插入图片描述
在这里插入图片描述

package-lock.json和package.json的区别
  • package.json

生成方式:执行 npm init 命令。

主要作用:描述项目及项目所依赖的模块信息。

  • package-lock.json
    生成方式:从 npm 5 版本之后只要使用 npm install 命令下载,就会自动生成 package-lock.json 文件。

主要作用:

1)描述 node_modules 文件中所有模块的版本信息,模块来源及依赖的小版本信息。

2)当版本升级,使用 npm install 命令时,会安装 package.json 中指定的大版本的最新版本。如 package.json 中指定版本"dependencies": { “webpack”: “^2.0.0” },则 package-lock.json 会按照 {“webpack”: “2.7.0”} 版本升级。在保证大版本号前提下的最新版本。webpack “2.7.0” 是 “2.x.x” 的最高版本。

初始化hardhat项目

在安装Hardhat的目录下运行:

npx hardhat

在这里插入图片描述
在运行Hardhat时,它将从当前工作目录开始搜索最接近的hardhat.config.js文件。 这个文件通常位于项目的根目录下,一个空的hardhat.config.js足以使Hardhat正常工作。
在这里插入图片描述

Hardhat 架构

Hardhat是围绕task(任务)和plugins(插件)的概念设计的。 Hardhat 的大部分功能来自插件,作为开发人员,你可以自由选择 你要使用的插件。

Tasks(任务)

每次在命令行运行Hardhat时,都是在运行任务。 例如

npx hardhat compile

正在运行compile任务。 要查看项目中当前可用的任务,运行

npx hardhat

在这里插入图片描述

通过运行

npx hardhat help [task]

可以探索任何任务。

Plugins(插件)

Hardhat 不限制选择哪种工具,但是它确实内置了一些插件,所有这些也都可以覆盖。 大多数时候,使用给定工具的方法是将其集成到Hardhat中作为插件。

在本教程中,我们将使用插件@nomicfoundation/hardhat-toolbox。 通过他们与以太坊进行交互并测试合约。 稍后将解释它们的用法。 要安装它们,请在项目目录中运行:

npm install --save-dev @nomicfoundation/hardhat-toolbox

在这里插入图片描述

require("@nomicfoundation/hardhat-toolbox");

添加到你的hardhat.config.js中,如下所示:
在这里插入图片描述

写一个合约并编译它

我们创建一个简单的智能合约,合约实现代币转让。 代币合约最常用于兑换或价值存储。 这里,我们不深入讨论合约的Solidity代码,但是一些实现逻辑你需要知道:

代币有固定发行总量。
所有发行总量都分配给了部署合约的地址。
任何人都可以接收代币。
任何人拥有代币的人都可以转让代币。
代币不可分割。 你可以转让1、2、3或37个代币,但不能转让2.5个代币。

你可能听说过ERC20,它是以太坊中的代币标准。 DAI,USDC,MKR和ZRX之类的代币都遵循ERC20标准,使这些代币都可以与任何能处理ERC20代币的软件兼容。 为了简单起见,我们要构建的代币不是ERC20。

首先创建一个名为 contracts 的新目录,然后在目录内创建一个名为Token.sol的文件。

在这里插入图片描述

将下面的代码粘贴到文件中,花一点时间阅读代码。 它很简单,并且有很多解释Solidity基础语法的注释。

要编译合约,请在终端中运行

npx hardhat compile

compile任务是内置任务之一。

在这里插入图片描述
这是多出来的东西
在这里插入图片描述

测试合约

为智能合约编写自动化测试至关重要,因为事关用户资金。 为此,我们将使用Hardhat Network,这是一个内置的以太坊网络,专门为开发设计,并且是Hardhat中的默认网络。 无需进行任何设置即可使用它。 在我们的测试中,我们将使用ethers.js与前面构建的合约进行交互,并使用 Mocha 作为测试运行器。
在项目根目录中创建一个名为test的新目录,并创建一个名为Token.js的新文件。

在这里插入图片描述
让我们从下面的代码开始。 在后面我们将对其进行解释,但现在将其粘贴到Token.js中:

const { expect } = require("chai");

describe("Token contract", function() {
  it("Deployment should assign the total supply of tokens to the owner", async function() {
    const [owner] = await ethers.getSigners();

    const Token = await ethers.getContractFactory("Token");

    const hardhatToken = await Token.deploy();

    const ownerBalance = await hardhatToken.balanceOf(owner.address);
    expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
  });
});

在终端上运行

npx hardhat test

你应该看到以下输出:

在这里插入图片描述
这意味着测试通过了。 现在我们逐行解释一下:

const [owner] = await ethers.getSigners();

ethers.js中的Signer 代表以太坊账户对象。 它用于将交易发送到合约和其他帐户。 在这里,我们获得了所连接节点中的帐户列表,在本例中节点为Hardhat Network,并且仅保留第一个帐户。

ethers变量在全局作用域下都可用。 如果你希望代码更明确,则可以在顶部添加以下这一行:

const { ethers } = require("hardhat");

继续

const Token = await ethers.getContractFactory("Token");

ethers.js中的ContractFactory是用于部署新智能合约的抽象,因此此处的Token是用来实例代币合约的工厂。

const hardhatToken = await Token.deploy();

在ContractFactory上调用deploy()将启动部署,并返回解析为Contract的Promise。 该对象包含了智能合约所有函数的方法。

const ownerBalance = await hardhatToken.balanceOf(owner.address);

部署合约后,我们可以在hardhatToken 上调用合约方法,通过调用balanceOf()来获取所有者帐户的余额。

请记住,部署合约的帐户获得了全部代币,在使用 hardhat-ethers 插件时,默认情况下, ContractFactory和Contract实例连接到第一个签名者。 这意味着owner变量中的帐户执行了部署,而balanceOf()应该返回全部发行量。

expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);

在这里,再次使用 Contract 实例调用Solidity代码中合约函数。 totalSupply() 返回代币的发行量,我们检查它是否等于ownerBalance。
判断相等,我们使用Chai,这是一个断言库。 这些断言函数称为“匹配器”,在此实际上使用的“匹配器”来自Hardhat Chai Matchers。 它扩展了Chai,为测试智能合约提供了许多有用的匹配器。

使用不同的账号测试

如果你需要从默认帐户以外的其他帐户(或ethers.js 中的 Signer)发送交易来测试代码,则可以在ethers.js的Contract中使用connect()方法来将其连接到其他帐户,像这样:

const { expect } = require("chai");

describe("Transactions", function () {

  it("Should transfer tokens between accounts", async function() {
    const [owner, addr1, addr2] = await ethers.getSigners();

    const Token = await ethers.getContractFactory("Token");

    const hardhatToken = await Token.deploy();
   
    // Transfer 50 tokens from owner to addr1
    await hardhatToken.transfer(addr1.address, 50);
    expect(await hardhatToken.balanceOf(addr1.address)).to.equal(50);
    
    // Transfer 50 tokens from addr1 to addr2
    await hardhatToken.connect(addr1).transfer(addr2.address, 50);
    expect(await hardhatToken.balanceOf(addr2.address)).to.equal(50);
  });
});

使用fixture重用公共测试设置

在更复杂的项目中,这种设置可能涉及多个部署和其他事务。在每个测试中这样做意味着大量的代码重复。此外,在每个测试开始时执行许多事务会使测试套件变得更慢。

你可以通过使用固定程序(fixtures)来避免代码重复并提高测试套件的性能。fixture是一个设置函数,只在第一次调用时运行。在随后的调用中,Hardhat将把网络的状态重置为fixture最初执行后的状态,而不是重新运行它。

const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { expect } = require("chai");

describe("Token contract", function () {
  async function deployTokenFixture() {
    const Token = await ethers.getContractFactory("Token");
    const [owner, addr1, addr2] = await ethers.getSigners();

    const hardhatToken = await Token.deploy();

    await hardhatToken.deployed();

    // Fixtures can return anything you consider useful for your tests
    return { Token, hardhatToken, owner, addr1, addr2 };
  }

  it("Should assign the total supply of tokens to the owner", async function () {
    const { hardhatToken, owner } = await loadFixture(deployTokenFixture);

    const ownerBalance = await hardhatToken.balanceOf(owner.address);
    expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
  });

  it("Should transfer tokens between accounts", async function () {
    const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
      deployTokenFixture
    );

    // Transfer 50 tokens from owner to addr1
    await expect(
      hardhatToken.transfer(addr1.address, 50)
    ).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);

    // Transfer 50 tokens from addr1 to addr2
    // We use .connect(signer) to send a transaction from another account
    await expect(
      hardhatToken.connect(addr1).transfer(addr2.address, 50)
    ).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]);
  });
});

在这里,我们写了一个deployTokenFixture函数,它做了必要的设置,并返回我们以后在测试中使用的每个值。然后在每个测试中,我们使用loadFixture来运行fixture并获得这些值。loadFixture将在第一次运行设置,并在其他测试中快速返回到该状态。
在这里插入图片描述
这是chatgpt的回答

在你提供的代码中,你使用了Hardhat测试框架来编写智能合约的测试用例。
从报错信息来看,问题出在调用 hardhatToken.deployed 方法时。
根据错误信息 "TypeError: hardhatToken.deployed is not a function",
这意味着 deployed 不是一个方法或者函数。

在最新版本的Hardhat中,当你调用 Token.deploy() 方法后,返回的实例已经被认为是部署完成的。
这就意味着不需要再调用 .deployed() 方法去等待部署完成。
.deployed() 是一个早期版本中存在于 Truffle 框架里面的方法,并不适用于 Hardhat。

因此,在下面这段代码中:

const hardhatToken = await Token.deploy();
await hardhatToken.deployed();


第二行是多余且会导致错误发生的。正确做法应该是移除那一行:


const hardhatToken = await Token.deploy();
// 这一行应该被删除:await hardhatToken.deployed();

在这里插入图片描述

完整覆盖测试

我们已经介绍了测试合约所需的基础知识,以下是代币的完整测试用例,其中包含有关Mocha以及如何构组织测试的许多信息。 我们建议你通读。

// This is an example test file. Hardhat will run every *.js file in `test/`,
// so feel free to add new ones.

// Hardhat tests are normally written with Mocha and Chai.

// We import Chai to use its asserting functions here.
const { expect } = require("chai");

// We use `loadFixture` to share common setups (or fixtures) between tests.
// Using this simplifies your tests and makes them run faster, by taking
// advantage of Hardhat Network's snapshot functionality.
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");

// `describe` is a Mocha function that allows you to organize your tests.
// Having your tests organized makes debugging them easier. All Mocha
// functions are available in the global scope.
//
// `describe` receives the name of a section of your test suite, and a
// callback. The callback must define the tests of that section. This callback
// can't be an async function.
describe("Token contract", function () {
  // We define a fixture to reuse the same setup in every test. We use
  // loadFixture to run this setup once, snapshot that state, and reset Hardhat
  // Network to that snapshot in every test.
  async function deployTokenFixture() {
    // Get the ContractFactory and Signers here.
    const Token = await ethers.getContractFactory("Token");
    const [owner, addr1, addr2] = await ethers.getSigners();

    // To deploy our contract, we just have to call Token.deploy() and await
    // its deployed() method, which happens once its transaction has been
    // mined.
    const hardhatToken = await Token.deploy();

    // await hardhatToken.deployed();

    // Fixtures can return anything you consider useful for your tests
    return { Token, hardhatToken, owner, addr1, addr2 };
  }

  // You can nest describe calls to create subsections.
  describe("Deployment", function () {
    // `it` is another Mocha function. This is the one you use to define each
    // of your tests. It receives the test name, and a callback function.
    //
    // If the callback function is async, Mocha will `await` it.
    it("Should set the right owner", async function () {
      // We use loadFixture to setup our environment, and then assert that
      // things went well
      const { hardhatToken, owner } = await loadFixture(deployTokenFixture);

      // `expect` receives a value and wraps it in an assertion object. These
      // objects have a lot of utility methods to assert values.

      // This test expects the owner variable stored in the contract to be
      // equal to our Signer's owner.
      expect(await hardhatToken.owner()).to.equal(owner.address);
    });

    it("Should assign the total supply of tokens to the owner", async function () {
      const { hardhatToken, owner } = await loadFixture(deployTokenFixture);
      const ownerBalance = await hardhatToken.balanceOf(owner.address);
      expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
    });
  });

  describe("Transactions", function () {
    it("Should transfer tokens between accounts", async function () {
      const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
        deployTokenFixture
      );
      // Transfer 50 tokens from owner to addr1
      await expect(
        hardhatToken.transfer(addr1.address, 50)
      ).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]);

      // Transfer 50 tokens from addr1 to addr2
      // We use .connect(signer) to send a transaction from another account
      await expect(
        hardhatToken.connect(addr1).transfer(addr2.address, 50)
      ).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]);
    });

    it("Should emit Transfer events", async function () {
      const { hardhatToken, owner, addr1, addr2 } = await loadFixture(
        deployTokenFixture
      );

      // Transfer 50 tokens from owner to addr1
      await expect(hardhatToken.transfer(addr1.address, 50))
        .to.emit(hardhatToken, "Transfer")
        .withArgs(owner.address, addr1.address, 50);

      // Transfer 50 tokens from addr1 to addr2
      // We use .connect(signer) to send a transaction from another account
      await expect(hardhatToken.connect(addr1).transfer(addr2.address, 50))
        .to.emit(hardhatToken, "Transfer")
        .withArgs(addr1.address, addr2.address, 50);
    });

    it("Should fail if sender doesn't have enough tokens", async function () {
      const { hardhatToken, owner, addr1 } = await loadFixture(
        deployTokenFixture
      );
      const initialOwnerBalance = await hardhatToken.balanceOf(owner.address);

      // Try to send 1 token from addr1 (0 tokens) to owner.
      // `require` will evaluate false and revert the transaction.
      await expect(
        hardhatToken.connect(addr1).transfer(owner.address, 1)
      ).to.be.revertedWith("Not enough tokens");

      // Owner balance shouldn't have changed.
      expect(await hardhatToken.balanceOf(owner.address)).to.equal(
        initialOwnerBalance
      );
    });
  });
});

这是

npx hardhat test 

的输出, 结果类似这样:
在这里插入图片描述

正常应该是这样的

$ npx hardhat test

  Token contract
    Deployment
      ✓ Should set the right owner
      ✓ Should assign the total supply of tokens to the owner
    Transactions
      ✓ Should transfer tokens between accounts (199ms)
      ✓ Should fail if sender doesn’t have enough tokens
      ✓ Should update balances after transfers (111ms)


  5 passing (1s)

请记住,当你运行npx hardhat test时,如果合约在上次运行测试后发生了修改,则会对其进行重新编译。

用 Hardhat Network 调试

Hardhat 内置了 Hardhat Network,这是一个专为开发而设计的以太坊网络。 它允许你部署合约,运行测试和调试代码。 这是Hardhat所连接的默认网络,因此你无需进行任何设置即可工作。 你只需运行测试就好。

在Hardhat Network上运行合约和测试时,你可以在Solidity代码中调用console.log()打印日志信息和合约变量。 你必须先从合约代码中导入**Hardhat **的console.log再使用它。

像这样:

pragma solidity ^0.8.9;

import "hardhat/console.sol";

contract Token {
  //...
}

就像在JavaScript中使用一样,将一些console.log添加到transfer()函数中:

function transfer(address to, uint256 amount) external {
    require(balances[msg.sender] >= amount, "Not enough tokens");

    console.log(
        "Transferring from %s to %s %s tokens",
        msg.sender,
        to,
        amount
    );

    balances[msg.sender] -= amount;
    balances[to] += amount;

    emit Transfer(msg.sender, to, amount);
}

运行测试时,将输出日志记录:
由于Should emit Transfer events报错,我就把它注释掉了
在这里插入图片描述

部署到真实网络

准备好与其他人分享dApp后,你可能要做的就是将其部署到真实的以太坊网络中。 这样,其他人可以访问不在本地系统上运行的实例。

具有真实价值的以太坊网络被称为“主网”,然后还有一些不具有真实价值但能够很好地模拟主网的网络,它可以被其他人共享阶段的环境。 这些被称为“测试网”,以太坊有多个测试网, 例如_Goerli_ 和 Sepolia。 我们建议你将合约部署到 Goerli 测试网。

在应用软件层,部署到测试网与部署到主网相同。 唯一的区别是你连接到哪个网络。 让我们研究一下使用ethers.js部署合约的代码是什么样的。

主要概念是Signer,ContractFactory和Contract,我们在[测试]testing 部分中对此进行了解释。与测试相比,并没有什么新的内容,因为当在测试合约时,实际上是在向开发网络进行部署。 因此代码非常相似或相同。

让我们在项目根目录的目录下创建一个新的目录scripts,并将以下内容粘贴到 deploy.js文件中:

async function main() {

  const [deployer] = await ethers.getSigners();

  console.log(
    "Deploying contracts with the account:",
    deployer.address
  );
  
  console.log("Account balance:", (await deployer.getBalance()).toString());

  const Token = await ethers.getContractFactory("Token");
  const token = await Token.deploy();

  console.log("Token address:", token.address);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

在这里插入图片描述
为了在运行任何任务时指示Hardhat连接到特定的以太坊网络,可以使用–network参数。 像这样:

npx hardhat run scripts/deploy.js --network <network-name>

在这种情况下,如果不使用–network 参数来运行它,则代码将再次部署在Hardhat network 上,因此,当Hardhat network 关闭后,部署实际上会丢失,但是它用来测试我们的部署代码时仍然有用:

$ npx hardhat run scripts/deploy.js
Deploying contracts with the account: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Account balance: 10000000000000000000000
Token address: 0x5FbDB2315678afecb367f032d93F642f64180aa3

在这里插入图片描述
在这里插入图片描述
要部署到诸如主网或任何测试网之类的线上网络,你需要在hardhat.config.js 文件中添加一个network条目。 在此示例中,我们将使用Goerli,但你可以类似地添加其他网络:

require("@nomicfoundation/hardhat-toolbox");

// Go to https://www.alchemyapi.io, sign up, create
// a new App in its dashboard, and replace "KEY" with its key
const ALCHEMY_API_KEY = "KEY";

// Replace this private key with your Goerli account private key
// To export your private key from Metamask, open Metamask and
// go to Account Details > Export Private Key
// Beware: NEVER put real Ether into testing accounts
const GOERLI_PRIVATE_KEY = "YOUR GOERLI PRIVATE KEY";

module.exports = {
  solidity: "0.8.9",
  networks: {
    goerli: {
      url: `https://eth-goerli.alchemyapi.io/v2/${ALCHEMY_API_KEY}`,
      accounts: [GOERLI_PRIVATE_KEY]
    }
  }
};

我们使用了 alchemy. , 但是你将url指向其他任何以太坊节点或网关都是可以。请填入你自己的 ALCHEMY_API_KEY 。

要在 Goerli 上进行部署,你需要将Goerli-ETH发送到将要进行部署的地址中。 你可以从水龙头(免费分发测试使用的ETH服务)获得一些用于测试网的ETH。 这是Goerli的一个水龙头:

Alchemy Goerli Faucet
你必须在进行交易之前将Metamask的网络更改为Goerli。
你可以通过以下链接为其他测试网获取一些ETH.

ethereum.org 测试网
最后运行:

npx hardhat run scripts/deploy.js --network goerli

如果一切顺利,你应该看到已部署的合约地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/262943.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

服装店收银系统 一种私域运营的神器

私域运营是指通过建立和管理自己的客户数据库来实现精细化营销和客户关系管理。服装店收银系统是门店私域运营的神器之一&#xff0c;服装店收银系统可以帮助店主收集客户的购买信息、消费偏好等数据&#xff0c;从而更好地了解客户需求并进行个性化营销。 以下是一些服装店收银…

springMVC-处理json和HttpMessageConverter<T>

细节说明&#xff1a;目标方法正常返回JSON需要的数据&#xff0c;可以是一个对象&#xff0c;也可以是一个集合&#xff0c;这里我们返回的是一个Dog对象>转成Json数据格式 示例案例&#xff1a; 在springmve中&#xff0c;如果我们返回一个集合List等&#xff0c;或者返回…

odoo17核心概念action5——其他文件

1、action_dialog 这是一个组件&#xff0c;在ActionContainer中有引用&#xff0c;因为ActionContainer用的是动态组件&#xff0c;暂时没有发现有xml文件调用这个组件。 也不知道怎么用 2、action_hook 从名字看&#xff0c;是一共钩子&#xff0c;有两个函数和一个类 cl…

在做题中学习(36):消失的两个数字

面试题 17.19. 消失的两个数字 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;丢失的数字 只出现一次的数字III ps: 下面讲 丢失的数字 思路&#xff0c;另一个在前面的&#xff08;32&#xff09;。 丢失的数字&#xff1a;给定一个包含 [0, n] 中 n 个数的数组…

浏览器的工作原理 - 从输入URL 按下回车到页面展示过程发生了什么?

本文带大家一起了解一下从我们输入一个网址链接开始到页面展示在我们面前&#xff0c;整个浏览器发生了什么&#xff1f;或者说浏览器做了哪些事&#xff0c;咱们以大家常用的baidu.com为例&#xff0c;从输入到 baidu.com 页面出现的整个流程 第一步&#xff1a;地址栏中敲击第…

海外社媒营销新趋势,品牌出海如何做?

社交媒体在网上的影响力是毋庸置疑的。投资社交媒体平台并建立公司形象&#xff0c;提高产品运营收入&#xff0c;提升品牌知名度&#xff0c;对于吸引对您所提供的产品感兴趣的人至关重要。 然而&#xff0c;社交媒体格局总是在变化&#xff0c;这意味着您需要掌握新的社交媒…

【Linux进阶之路】线程

文章目录 一、初始线程1.概念2.执行3.调度4.切换 二、线程控制1.创建2.等待3.分离4.退出5.取消 三、线程安全1.互斥1.1初始1.2理解1.3锁1.3.1概念1.3.2原理1.3.4死锁 2.同步2.1概念2.2原理 3.生产消费者模型 总结尾序 一、初始线程 1.概念 简单的概念&#xff1a; 线程就是一…

全球盲盒热潮:探寻海外市场的文化风潮与商机

近年来&#xff0c;盲盒经济在全球范围内持续升温&#xff0c;其独特的营销方式以及带给消费者的刺激感&#xff0c;引发了广大消费者的热烈追捧。特别是在海外市场&#xff0c;其增长速度之快&#xff0c;让各类盲盒品牌看到了巨大的商业潜力。然而&#xff0c;盲盒市场的快速…

使用工具类Exectors创建线程池

大型并发项目 不能使用Executors 通过ThreadPoolExector的方式 核心线程配置方式: 计算密集型的任务 核心线程数量 CPU的核数 1 IO密集型的任务 核心线程数量 CPU的核数*2 演示: Callable import java.util.concurrent.Callable;public class MyCallable implements Callab…

playbook控制语句

本章主要介绍 playbook中的控制语句。 目录 判断语句when &#xff08;1&#xff09;when判断中>、<、!的使用 &#xff08;2&#xff09;when判断中in的用法 &#xff08;3&#xff09;when判断中is的用法 判断语句block-rescue 循环语句 一个play中可以包含…

品牌出海如何做?海外社媒营销新趋势

社交媒体在网上的影响力是毋庸置疑的。投资社交媒体平台并建立公司形象&#xff0c;提高产品运营收入&#xff0c;提升品牌知名度&#xff0c;对于吸引对您所提供的产品感兴趣的人至关重要。 然而&#xff0c;社交媒体格局总是在变化&#xff0c;这意味着您需要掌握新的社交媒…

xposed 01 - 环境搭建

简介 Xposed的作者是rovo89&#xff0c;但是更新完 8.1 的 beta 版之后就不更新了。由于Android新版本的普及&#xff0c;目前新上市的手机基本都是8.0以上。所以Xposed框架已经不适用。EdXposed团队成为Xposed停止更新后的官方接任者。 当然现在有更好的 LSPosed https://git…

v-if与v-show的区别

v-if指令可以控制一个元素的显示和隐藏&#xff0c;那么它是如何实现的&#xff1f;它和看起来很像的v-show指令有什么区别呢&#xff1f; 如果v-if指令的值为假&#xff0c;那么这个元素不会被插入DOM。 下面的代码 <div v-if"true">one</div><div…

05-垃圾收集器ParNewCMS与底层三色标记算法详解

文章目录 垃圾收集算法分代收集理论标记-复制算法标记-清除算法标记-整理算法 垃圾收集器Serial收集器Parallel Scavenge收集器ParNew收集器CMS收集器 CMS的相关核心参数亿级流量电商系统如何优化JVM参数设置(ParNewCMS) 垃圾收集底层算法实现三色标记多标-浮动垃圾漏标-读写屏…

高级算法设计与分析(六) -- 分支限界法

系列文章目录 高级算法设计与分析&#xff08;一&#xff09; -- 算法引论 高级算法设计与分析&#xff08;二&#xff09; -- 递归与分治策略 高级算法设计与分析&#xff08;三&#xff09; -- 动态规划 高级算法设计与分析&#xff08;四&#xff09; -- 贪心算法 高级…

数字滤波器的设计

一般滤波器可以分为经典滤波器和数字滤波器。 经典滤波器&#xff1a;假定输入信号中的有用成分和希望去除的成分各自占有不同的频带。如果信号和噪声的频谱相互重迭&#xff0c;经典滤波器无能为力。比如 FIR 和 IIR 滤波器等。  现代滤波器&#xff1a;从含有噪声的时间序…

Envoy

一. Envoy ). Envoy Envoy 于 2017 年 9 月作为孵化项目加入 CNCF。从孵化到毕业&#xff0c;Envoy 都是 CNCF 增长最快的项目之一 Envoy 在吞吐量和延迟方面都表现良好。这在大型云原生部署中至关重要 Envoy 是专为大型现代 SOA&#xff08;面向服务架构&#xff09;架构设计…

python:删除空白

删除字符串末尾的空白 例如&#xff0c;下面的代码&#xff0c;变量hobby指向的字符串在末尾有一个空格&#xff1a; 可以使用函数rstrip()删除字符串末尾的空格&#xff0c;如下&#xff1a; 因为删除字符串末尾的空格并没有赋值给原变量hobby&#xff0c;所以此时查看hobb…

基于ssm房屋租赁平台的设计与开发论文

摘 要 目前对于在外的人员来说租赁房屋是最基本的问题。对于房屋的租赁可以选择直接找房东、找专业的房屋租赁公司和自己在网上找房屋。自己找房东的问题在于需要时间&#xff0c;而且对于需要提前租赁房屋的需要多次跑到小区&#xff0c;找中介租赁房屋的问题在于费用问题&am…

FPGA未解之谜

一.ila一会能加载出波形&#xff0c;一会加载不出波形——在自己做的v7开发板中遇到&#xff0c;其他开发板从未遇到过 1.小梅哥说&#xff1a;可能与硬件jtag连接不稳定导致。