因此,我最近一直专注于前端、数据验证、显示错误等方面,并非常感谢CertainPerformance为我做了许多评论:)
我使用handlebars创建了一个报表模板,创建了一个MySQL数据库来存储数据,使用Node JS在2之间进行交互。现在我已经了解了Node如何与前端交互,获取数据并将其发送到数据库。
我想听听对我当前代码前端和后端的一些评论。对我个人来说,这是可行的,我非常高兴我能创造出这样的东西,而以前我从未想过我能:D。
我只想听听一些评论--在哪里我可以进步,在哪里我缺乏什么,什么东西可以简化等等。
Node JS
注意:这个函数是在路由阶段触发的,我还没有在这里包含它。
// 21-TEMP-01a
exports.TEMP01A21 = async function(req, res) {
// Array to store actual temperature
var actualTemp = [];
// Array to store minimum temperature
var minTemp = [];
// Array to store maximum temperature
var maxTemp = [];
// Array to store actual humidity
var actualHumid = [];
// Array to store minimum humidity
var minHumid = [];
// Array to store maximum humidity
var maxHumid = [];
// Get drying room names
var roomNames = [];
// Loop thorugh each req.body values
for(var key in req.body) {
// Store req.body key to value var
var value = req.body[key];
// For each key that starys with ActualTemp store into array
if(key.startsWith("actualtemp")) {
actualTemp.push(value);
}
// minTemp
if(key.startsWith("mintemp")) {
minTemp.push(value);
}
// maxTemp
if(key.startsWith("maxtemp")) {
maxTemp.push(value);
}
// actualHumidity
if(key.startsWith("actualhumid")) {
actualHumid.push(value);
}
// minHumidity
if(key.startsWith("minhumid")) {
minHumid.push(value);
}
// maxHumidity
if(key.startsWith("maxhumid")) {
maxHumid.push(value);
}
// Room Names
if(key.startsWith("dryingroom")) {
roomNames.push(value);
}
}
// Get todays date
var today = new Date();
// Format the date
var formatDay = today.toISOString().substring(0, 10);
// Create an array to create custom MySQL query
var values = [];
// For each temperature value, store its output to values array
for(var i = 0; i < actualTemp.length; i++) {
values.push([roomNames[i] , actualTemp[i],
minTemp[i], maxTemp[i],
actualHumid[i], minHumid[i],
maxHumid[i], formatDay,
res.locals.user.id]);
}
db.query("insert into 21TEMP01a (room_name, actual_temperature, min_temperature, max_temperature, actual_humidity, min_humidity, max_humidity, time, user) values ?", [values], (error, result) => {
if(error) {
console.log(error);
}
else{
res.redirect('/reports/daily');
}
});
}前端
Form:
Select Room
Drying Room 1
Drying Room 2
Dry Store
Drying Room 1
Temperature °C - Actual
Temperature °C - Minimum
Temperature °C - Maximum
Relative Humidity - Actual
Relative Humidity - Minimum
Relative Humidity - Maximum
Drying Room 2
Temperature °C - Actual
Temperature °C - Minimum
Temperature °C - Maximum
Relative Humidity - Actual
Relative Humidity - Minimum
Relative Humidity - Maximum
Dry Store
Temperature °C - Actual
Temperature °C - Minimum
Temperature °C - Maximum
Relative Humidity - Actual
Relative Humidity - Minimum
Relative Humidity - Maximum
Please complete all required fields
Submit
Targets not met
×
Some temperatures or humidity values have not met their targets.
- Re-check or continue submitting current data.
Submit
Close
// Store DOM Strings
var DOMStrings = {
room_options: '#RoomMenu'
};
// On selected option, show specific div element
showActiveElement = () => {
for(const option of document.querySelector(DOMStrings.room_options).options) {
document.querySelector(`#${option.value}`).style.display = "none";
}
if(document.querySelector(DOMStrings.room_options).value === 'dry-1') {
document.querySelector('#dry-1').style.display = "block";
} else if (document.querySelector(DOMStrings.room_options).value === 'dry-2') {
document.querySelector('#dry-2').style.display = "block";
} else if (document.querySelector(DOMStrings.room_options).value === 'dry-3') {
document.querySelector('#dry-3').style.display = "block";
}
}
// Show selected div element from options
document.querySelector(DOMStrings.room_options).addEventListener('change', () => {
showActiveElement();
});
// Validate data
document.getElementById("form").addEventListener("submit", (e) => {
const inputs = document.querySelectorAll('#form input');
let bool;
// 1. Check if input fields are empty
const empty = [].filter.call(inputs, e => e.value === "");
let notEmpty;
if(empty.length === 0) {
notEmpty = true;
} else {
e.preventDefault();
document.getElementById('errors').style.opacity = 1;
empty.forEach(element => {
element.style.borderColor = "red";
element.placeholder = "Required";
element.addEventListener("input", (e) => {
if(element.value === "") {
bool = false;
element.style.borderColor = "red";
element.placeholder = "Required";
} else {
element.style.borderColor = "#fff";
}
const empty = [].filter.call(inputs, e => e.value === "");
if(empty.length === 0) {
document.getElementById('errors').style.opacity = 0;
} else {
document.getElementById('errors').style.opacity = 1;
}
});
});
}
// Validate Temperature and humidity values
if(notEmpty == true) {
for (var i = 0; i < inputs.length; i++) {
// Validate Temperatures for Drying room 1 & 2
if(inputs[i].name.match(/^(actual|min|max)-temp-(1|2)$/)) {
validateTempDryingRoom(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate Humidity for Drying room 1 & 2
if(inputs[i].name.match(/^(actual|min|max)humid(1|2)$/)) {
validateHumidityDryingRoom(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate Temp. for Dry Store
if(inputs[i].name.match(/^(actual|min|max)temp(3)$/)) {
validateTempDryStore(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate humidity for Dry Store
if(inputs[i].name.match(/^(actual|min|max)humid(3)$/)) {
validateHumidityDryStore(parseFloat(inputs[i].value), inputs[i], e);
}
}
}
});
function validateTempDryingRoom(value, item, e) {
if(value < 39.4 || value > 49) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
function validateHumidityDryingRoom(value, item ,e) {
if(value < 14 || value > 30) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
function validateTempDryStore(value, item, e) {
if(value < 10 || value > 27.2) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
function validateHumidityDryStore(value, item ,e) {
if(value < 40 || value > 70) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
document.getElementById('submit-email').addEventListener('click', () => {
const form = document.getElementById('form');
form.submit();
});
// On load
window.onload = () => {
showActiveElement();
}发布于 2020-09-27 09:54:15
首先,您的代码似乎是全局的,我在这里要写的大部分内容都是详细的。当然,这也反映了我个人的观点。虽然我尽量不讨论关于我个人意见的观点,但我的个人意见可能会影响我认为一般性的问题。
远离我的想法,不鼓励您评论您的代码,但注释可能并不总是一个好主意。
您可能已经了解到注释代码是好的,这是显而易见的,很明显,您甚至不明白为什么注释可能会损害您的代码。在注释代码时,您可能看到的唯一缺点是您花了时间进行注释。
好吧,如果你再也不回你的代码,你不需要评论它。好吧,大多数代码都是“维护”和发展的。这就是问题所在。一般来说,当代码发生变化时,代码周围的注释往往不会改变。当代码中有问题时,您将更正代码,直到问题解决为止。当评论中有问题时,什么也不会做,因为没有人会注意到。
此外,评论应该是有意义的。通常,您可以用正确的变量/函数/类名称替换一些无用的注释。一个例子(这比您的代码要糟糕得多,但是要显示真正的区别)
// store an empty array in the variable a
let a = [];
// [...]
// if the string k starts with "at", append the content of the value v at the end of the array a
if (k.startsWith("at")) {
a.push(v);
}现在将其与以下未注释的代码进行比较:
let actualTemperatureArray = [];
// [...]
if (queryParameterKey.startsWith("actualTemperature")) {
actualTemperatureArray.push(value);
}其思想是,变量的名称应该反映变量所做的事情。如果是的话,你就不用评论了。“解析”代码也是如此。查看查询参数键并查看它是否以某个字符串开头的事实是显而易见的,这就是代码的字面意思。一个有趣的评论不是你在做什么,而是你为什么要这么做。如果不明显的话。如果这是显而易见的,也许这个评论是无用的。
但请评论你的技巧。例如,如果因为“第一个值应该被忽略”而跳过一个循环中的一个值,则应该对其进行注释。这里有一个技巧是未来读者必须知道的。
但是,您可能仍然希望对变量声明进行注释。因此,如果您这样做,至少使用JSDoc (https://jsdoc.app/)。JSDoc允许您添加一些静态类型定义,即:
现在让我们从您的代码中学习示例。
// Array to store actual temperature
var actualTemp = [];你认为actualTemp是否足以让人理解变量的作用?我不是说不是。但你得自己决定。我将采取保守的观点,并认为actualTemperatureArray更好(但是更好地了解上下文,也许在您的领域中,临时是足够的,没有人会认为在那个上下文中,它可能意味着临时的)。
而且,由于actualTemperatureArray包含您所需的所有含义,所以没有必要使用注释。
var actualTemperatureArray = [];当然,你会把实际的温度存储在这个数组中。没必要说出来。
但是,如果您想说,请使用JSDoc。
/** @type {string[]} Array to store actual temperature */
var actualTemperatureArray = [];在您的代码中,当我将光标放在actualTemp上时,VS代码将向我展示以下工具提示:
(local var) actualTemp: any[]所以我知道这个变量是一个局部变量,它是一个数组,我不知道是什么。
在最后一个示例中,当我将光标放在actualTemperatureArray上时,将向我展示以下工具提示:
(local var) actualTemperatureArray: string[]
@type — {string[]} Array to store actual temperature这就是说,我不想阻止您发表评论,而是想一想,您可以为读者带来哪些代码不是显而易见的提供的。如果您只是重复代码所述的内容,则可能没有必要对其进行注释。如果你能洞察你为什么这么做,这可能是一个有帮助的评论。
下面的块通常有很多类似的行:
// For each key that starys with ActualTemp store into array
if (key.startsWith("actualtemp")) {
actualTemperatureArray.push(value);
}
// minTemp
if (key.startsWith("mintemp")) {
minimalTemperatureArray.push(value);
}
// maxTemp
if (key.startsWith("maxtemp")) {
maximalTemperatureArray.push(value);
}
// actualHumidity
if (key.startsWith("actualhumid")) {
actualHumidityArray.push(value);
}
// minHumidity
if (key.startsWith("minhumid")) {
minimalHumidityArray.push(value);
}
// maxHumidity
if (key.startsWith("maxhumid")) {
maximalHumidityArray.push(value);
}
// Room Names
if (key.startsWith("dryingroom")) {
roomNames.push(value);
}对此进行分解可能是个好主意。
每个块操作一个不同的变量。因此,让我们创建一个包含所有这些变量的对象,这样在访问这些数组变量时我们就可以更加通用。
我们需要一个数据结构来“描述”我们需要的数据。我认为像"actualtemp"或"mintemp"这样的名字是协议的一部分,是不能更改的。
我可以用它作为索引,但我会使用更显式的名称,但这部分可以简化。
我们首先创建一个描述我们需要的数据的结构(注意,订单很重要,它是查询中的一个,因为稍后我将重用该顺序来创建查询)。
/**
* @type {Object.} The prefix of all body.req referenced by the name of the parameter
*/
var paramKeyPrefixesByParamName = {
roomName: "dryingroom",
actualTemperature: "actualtemp",
minimalTemperature: "mintemp",
maximalTemperature: "maxtemp",
actualHumidity: "actualhumid",
minimalHumidity: "minhumid",
maximalHumidity: "maxhumid",
};
/** @type{string[]} The parameter names */
const parameterNames = Object.keys(paramKeyPrefixesByParamName);现在让我们创建数组:
/**
* @type {Object.} The array of values for each parameter, indexed by the name of the parameter
*/
var valueArraysByParamName = {};我还没有在这里创建数组,稍后再创建它们。我本可以在这里做的。
// Loop thorugh each req.body values
for (var key in req.body) {
// Store req.body key to value var
var value = req.body[key];
// We iterate over all the
// parameter names
// (roomName, actualTemperature, ...)
parameterNames.forEach((parameterName) => {
// We extract the prefix
// from the parameter names
// (dryingroom, actualtemp, ...)
const prefix = paramKeyPrefixesByParamName[parameterName];
if (key.startsWith(prefix)) {
// We make sure the array exists.
// If it doesn't exist, we create it.
if (! valueArraysByParamName[parameterName]) {
valueArraysByParamName[parameterName] = [];
}
// We append the value in the correct array.
valueArraysByParamName[parameterName].push(value);
}
})
}现在我们可以创建查询:
/** @type {string[][]} An array to create custom MySQL query */
var values = [];
// For each temperature value, store the values associated to that temperature
for (var i = 0; i < actualTemperatureArray.length; i++) {
values.push([
...parameterNames.map((parameterName)=>valueArraysByParamName[parameterName][i]),
formatDay,
res.locals.user.id
]);
}现在,服务器代码看起来如下:
// 21-TEMP-01a
exports.TEMP01A21 = async function (req, res) {
/**
* @type {Object.} The prefix of all body.req referenced by the name of the parameter
*/
var paramKeyPrefixesByParamName = {
roomName: "dryingroom",
actualTemperature: "actualtemp",
minimalTemperature: "mintemp",
maximalTemperature: "maxtemp",
actualHumidity: "actualhumid",
minimalHumidity: "minhumid",
maximalHumidity: "maxhumid",
}
/**
* @type {Object.} The array of values for each parameter, indexed by the name of the parameter
*/
var valueArraysByParamName = {};
// Loop thorugh each req.body values
for (var key in req.body) {
// Store req.body key to value var
var value = req.body[key];
// We iterate over all parameter names (roomName, actualTemperature, ...)
Object.keys(paramKeyPrefixesByParamName).forEach((parameterName) => {
// We extract the prefix from the parameter name (dryingroom, actualtemp, ...)
const prefix = paramKeyPrefixesByParamName[parameterName];
if (key.startsWith(prefix)) {
// We make sure the array exists. If it doesn't exist, we create it.
if (! valueArraysByParamName[parameterName]) {
valueArraysByParamName[parameterName] = [];
}
// We append the value in the correct array.
valueArraysByParamName[parameterName].push(value);
}
})
}
// Get todays date
var today = new Date();
// Format the date
var formatDay = today.toISOString().substring(0, 10);
/** @type {string[][]} An array to create custom MySQL query */
var values = [];
// For each temperature value, store the values associated to that temperature
for (var i = 0; i < actualTemperatureArray.length; i++) {
values.push([
...Object.keys(paramKeyPrefixesByParamName).map((parameterName)=>valueArraysByParamName[parameterName][i]),
formatDay,
res.locals.user.id
]);
}
db.query("insert into 21TEMP01a (room_name, actual_temperature, min_temperature, max_temperature, actual_humidity, min_humidity, max_humidity, time, user) values ?", [values], (error, result) => {
if (error) {
console.log(error);
}
else {
res.redirect('/reports/daily');
}
});
}的重复
在前面,你有一些重复:
function validateTempDryingRoom(value, item, e) {
if(value < 39.4 || value > 49) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
function validateHumidityDryingRoom(value, item ,e) {
if(value < 14 || value > 30) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
function validateTempDryStore(value, item, e) {
if(value < 10 || value > 27.2) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
function validateHumidityDryStore(value, item ,e) {
if(value < 40 || value > 70) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}好像你一次又一次地做同样的事。
我们重写这个吧。
function createValidateElement(minValue, maxValue) {
return function (value, item, e) {
if(value < minValue || value > maxValue) {
e.preventDefault();
item.style.backgroundColor = 'red';
$("#myModal").modal();
} else {
item.style.backgroundColor = '#fff';
}
}
}
const validateTempDryingRoom = createValidateElement(39.4, 49);
const validateHumidityDryingRoom = createValidateElement(14, 30);
const validateTempDryStore = createValidateElement(10, 27.2);
const validateHumidityDryStore = createValidateElement(40, 70);我创建了一个创建函数的函数。
小心,因为validateTempDryingRoom、validateHumidityDryingRoom、validateTempDryStore、validateHumidityDryStore现在都是经典变量,而不是函数,所以在使用之前应该声明它们。
但是,这些验证器的使用也是重复的:
// Validate Temperatures for Drying room 1 & 2
if(inputs[i].name.match(/^(actual|min|max)-temp-(1|2)$/)) {
validateTempDryingRoom(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate Humidity for Drying room 1 & 2
if(inputs[i].name.match(/^(actual|min|max)humid(1|2)$/)) {
validateHumidityDryingRoom(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate Temp. for Dry Store
if(inputs[i].name.match(/^(actual|min|max)temp(3)$/)) {
validateTempDryStore(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate humidity for Dry Store
if(inputs[i].name.match(/^(actual|min|max)humid(3)$/)) {
validateHumidityDryStore(parseFloat(inputs[i].value), inputs[i], e);
}所以我们需要一个数据结构来描述重复的项目。
const dryDatas = {
TempDryingRoom: {
pattern: /^(actual|min|max)-temp-(1|2)$/,
minValue: 39.4,
maxValue: 49,
},
HumidityDryingRoom: {
pattern: /^(actual|min|max)humid(1|2)$/,
minValue: 14,
maxValue: 30,
},
TempDryStore: {
pattern: /^(actual|min|max)temp(3)$/,
minValue: 10,
maxValue: 27.2,
},
HumidityDryStore: {
pattern: /^(actual|min|max)humid(3)$/,
minValue: 40,
maxValue: 70,
},
}然后我们可以创建验证器:
const dryDataValidators = Object.fromEntries(Object.entries(dryDatas).map(([dryDataKey,dryDataValue])=>[dryDataKey, createValidateElement(dryDataValue.min, dryDataValue.max)]));如果您认为它不可读,您可以使用一些更易读的:
const dryDataValidators = {};
for (let dryDataKey of Object.keys(dryDatas)) {
const dryDataValue = dryDatas[dryDataKey];
dryDataValidators[dryDataKey] = createValidateElement(dryDataValue.min, dryDataValue.max);
}做同样的事..。
然后替换
// Validate Temperature and humidity values
if(notEmpty == true) {
for (var i = 0; i < inputs.length; i++) {
// Validate Temperatures for Drying room 1 & 2
if(inputs[i].name.match(/^(actual|min|max)-temp-(1|2)$/)) {
validateTempDryingRoom(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate Humidity for Drying room 1 & 2
if(inputs[i].name.match(/^(actual|min|max)humid(1|2)$/)) {
validateHumidityDryingRoom(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate Temp. for Dry Store
if(inputs[i].name.match(/^(actual|min|max)temp(3)$/)) {
validateTempDryStore(parseFloat(inputs[i].value), inputs[i], e);
}
// Validate humidity for Dry Store
if(inputs[i].name.match(/^(actual|min|max)humid(3)$/)) {
validateHumidityDryStore(parseFloat(inputs[i].value), inputs[i], e);
}
}
}通过
// Validate Temperature and humidity values
if(notEmpty == true) {
for (var i = 0; i < inputs.length; i++) {
// Iterate over all type of dryDatas (TempDryingRoom, HumidityDryingRoom, TempDryStore, ...)
for (let dryDataKey of Object.keys(dryDatas)) {
const pattern = dryDatas[dryDataKey].pattern;
const validator = dryDataValidators[dryDataKey];
if(inputs[i].name.match(pattern)) {
validator(parseFloat(inputs[i].value), inputs[i], e);
}
}
}
}https://codereview.stackexchange.com/questions/249831
复制相似问题