###### 一、成功的环境配置:
truffle v5.1.50
solc:0.5.16
web3:1.2.9
"devDependencies": {
"copy-webpack-plugin": "^5.0.5",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
},
"dependencies": {
"truffle-contract": "^4.0.31",
"web3": "^1.2.4"
}
###### 二、构建webpack

###### 三、清理配置webpack的内容
在contracts目录下删除多余的合约文件

修改truffle-config.js文件如下:

###### 四、编写智能合约和网页,js文件
```js
pragma solidity >0.2.4;
// We have to specify what version of compiler this code will compile with
contract Voting {
/* mapping field below is equivalent to an associative array or hash.
The key of the mapping is candidate name stored as type bytes32 and value is
an unsigned integer to store the vote count
*/
mapping (bytes32 => uint256) public votesReceived;
/* Solidity doesn't let you pass in an array of strings in the constructor (yet).
We will use an array of bytes32 instead to store the list of candidates
*/
bytes32[] public candidateList;
/* This is the constructor which will be called once when you
deploy the contract to the blockchain. When we deploy the contract,
we will pass an array of candidates who will be contesting in the election
*/
constructor(bytes32[] memory candidateNames) public {
candidateList = candidateNames;
}
// This function returns the total votes a candidate has received so far
function totalVotesFor(bytes32 candidate) view public returns (uint256) {
require(validCandidate(candidate));
return votesReceived[candidate];
}
// This function increments the vote count for the specified candidate. This
// is equivalent to casting a vote
function voteForCandidate(bytes32 candidate) public {
require(validCandidate(candidate));
votesReceived[candidate] += 1;
}
function validCandidate(bytes32 candidate) view public returns (bool) {
for(uint i = 0; i < candidateList.length; i++) {
if (candidateList[i] == candidate) {
return true;
}
}
return false;
}
}
```
index.html
```html
<!DOCTYPE html>
<html>
<head>
<title>Hello World DApp</title>
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
</head>
<body class="container">
<h1>A Simple Hello World Voting Application</h1>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Candidate</th>
<th>Votes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Rama</td>
<td id="candidate-1"></td>
</tr>
<tr>
<td>Nick</td>
<td id="candidate-2"></td>
</tr>
<tr>
<td>Jose</td>
<td id="candidate-3"></td>
</tr>
</tbody>
</table>
</div>
<input type="text" id="candidate" />
<a href="#" οnclick="App.voteForCandidate()" class="btn btn-primary">Vote</a>
</body>
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.37/dist/web3.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="./index.js"></script>
```
index.js
```js
import Web3 from "web3";
import votingArtifact from "../../build/contracts/Voting.json";
let candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}
const App = {
web3: null,
account: null,
voting: null,
start: async function() {
const { web3 } = this;
try {
/* Get the network we are connected to and then read the build/contracts/Voting.json and instantiate a contract object to use
*/
//
const networkId = await web3.eth.net.getId();
const deployedNetwork = votingArtifact.networks[networkId];
this.voting = new web3.eth.Contract(
votingArtifact.abi,
deployedNetwork.address,
);
// get accounts
const accounts = await web3.eth.getAccounts();
this.account = accounts[0];
this.loadCandidatesAndVotes();
} catch (error) {
console.error("Could not connect to contract or chain.");
}
},
loadCandidatesAndVotes: async function() {
const { totalVotesFor } = this.voting.methods;
//const { sendCoin } = this.meta.methods;
//await sendCoin(receiver, amount).send({ from: this.account });
let candidateNames = Object.keys(candidates);
for (var i = 0; i < candidateNames.length; i++) {
let name = candidateNames[i];
var count = await totalVotesFor(this.web3.utils.asciiToHex(name)).call();
$("#" + candidates[name]).html(count);
}
},
voteForCandidate: async function() {
let candidateName = $("#candidate").val();
$("#msg").html("Vote has been submitted. The vote count will increment as soon as the vote is recorded on the blockchain. Please wait.")
$("#candidate").val("");
const { totalVotesFor, voteForCandidate } = this.voting.methods;
/* Voting.deployed() returns an instance of the contract. Every call
* in Truffle returns a promise which is why we have used then()
* everywhere we have a transaction call
*/
await voteForCandidate(this.web3.utils.asciiToHex(candidateName)).send({gas: 140000, from: this.account});
let div_id = candidates[candidateName];
var count = await totalVotesFor(this.web3.utils.asciiToHex(candidateName)).call();
$("#" + div_id).html(count);
$("#msg").html("");
}
};
window.App = App;
window.addEventListener("load", function() {
if (window.ethereum) {
// use MetaMask's provider
App.web3 = new Web3(window.ethereum);
window.ethereum.enable(); // get permission to access accounts
} else {
console.warn(
"No web3 detected. Falling back to http://127.0.0.1:8545. You should remove this fallback when you deploy live",
);
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
App.web3 = new Web3(
new Web3.providers.HttpProvider("http://127.0.0.1:8545"),
);
}
App.start();
});
```
五、部署,运行
首先需要一个本地的私有链作为合约部署的目的链。
使用truffle develop 来启动一个私有区块链用于测试。

有了区块链,之后就可以把合约部署在链上了。
修改migrates下的2_deploy_contracts.js文件
指定我们自己的合约文件:
```javascript
const Voting = artifacts.require("Voting");
module.exports = function(deployer) {
deployer.deploy(Voting, ['Rama', 'Nick', 'Jose'].map(name => web3.utils.asciiToHex(name)));
};
```
在项目根路径下执行truffle migrate .

说明合约部署成功。
在app路径下执行npm install 用于安装package.json中定义的依赖。
执行npm run dev.
打开浏览器 输入 http://localhost:8080即可看到页面

测试一下功能,至此就使用truffle框架搭建起一个简单的dapp。
遇到问题:实际上遇到的最大的问题还是环境配置的问题,由于webpack 和webpack-dev-server版本不相兼容的问题是最让人头疼的。
还有由于web3版本1.x与0.20.x的区别,在与合约进行交互的时候也要注意要使用恰当的写法。