数据/逻辑层放在前端的什么地方
Where to put data / logic layer in front end
我读过很多文章,指出我应该避免将复杂的逻辑放入我的 HTML 模板中,
所以我的问题是,如果我编写的模块需要某些特定属性或 class 名称,基于加载到我页面的数据类型,我应该将这一层写在我的模块内部作为方法还是错误的意识形态?
例如,在此代码中,我从 JSON 文件中加载数据
{
"slideShow": [
{
"img": "aaaaaaaa.jpg",
"link": "aaaaaaaaa.html",
"title": "aaaaaa",
"date": "aaaaaaaa",
"detail": "aaaaaaaaaaaaa"
},
{
"img": "bbbbbbbbbbb.jpg",
"link": "bbbbbbbbbbb.html",
"title": "bbbbbbbbbbb",
"date": "bbbbbbbbbbbbb",
"detail": "bbbbbbbbbbbbb"
},
{
"img": "ccccccccccc.jpg",
"title": "ccccccc",
"date": "ccccccccccccc"
},
{
"img": "dddddddddd.jpg",
"title": "ddddddddd",
"date": "dddddddddd",
"detail": "dddddddddd"
}
]
}
到此模板
<section id="slideShow">
<script id="slideShow-template" type="text/template">
<ul>
{{#slideShow}}
<li class="{{{class}}}">
<img src="{{{img}}}" alt="{{{title}}}">
<a href="{{{link}}}">
<h1 class="slideShowTitle">{{title}}</h1>
<p class="slideShowDate">{{date}}</p>
<p class="slideShowDetail">{{detail}}</p>
</a>
</li>
{{/slideShow}}
</ul>
<nav>
{{#slideShow}}
<a href="javascript:;"></a>
{{/slideShow}}
</nav>
<a href="javscript:void(0)" class="prevSlide"></a>
<a href="javscript:void(0)" class="nextSlide"></a>
</script>
</section>
然后我写了这个
(function() {
var slideShow = {
slideShow: [],
importData: function() {
var xhr = new XMLHttpRequest(),
url = 'data/slideShow.json',
_self = this,
result;
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
result = JSON.parse(this.responseText);
_self.slideShow = result.slideShow;
_self.dataProcess();
_self.init();
}
};
xhr.open('GET', url, true);
xhr.send();
},
dataProcess: function() {
this.slideShow.forEach(function(element, index) {
element.class = '';
if(!element.detail) {
element.class += 'noDetail ';
}
if(!element.link) {
element.link = 'javascript:void';
element.class += 'noLink ';
}
element.class = element.class.replace('undefined', '');
});
},
init: function() {
this.render();
this.cacheDom();
this.bindEvents();
this.autoRun();
},
bindEvents: function() {
this.$el.addEventListener('click', this.actionEvent.controlDirection.bind(this));
this.$nav.addEventListener('click', this.actionEvent.selectSlide.bind(this));
},
render: function(cacheDom) {
var data = {
slideShow: this.slideShow
};
this.$el = document.getElementById('slideShow');
this.template = document.getElementById('slideShow-template').innerHTML;
this.$el.innerHTML = Mustache.render(this.template, data);
},
cacheDom: function() {
this.$ul = this.$el.querySelector('ul');
this.$li = this.$ul.querySelectorAll('li');
this.$nav = this.$el.querySelector('nav');
this.$a = this.$nav.querySelectorAll('a');
this.$next = this.$el.querySelector('.nextSlide');
this.$prev = this.$el.querySelector('.prevSlide');
},
autoRun: function() {
this.$ul.style.left = '0';
this.$ul.style.width = this.slideShow.length * 100 + '%';
this.$li.forEach(function(element) {
element.style.width = (100 / this.slideShow.length) + '%';
}.bind(this));
this.$a[0].classList.add('activated');
this.autoTimer();
},
calc(direction, index) {
this.left = parseInt(this.$ul.style.left);
if(direction) {
this.newLeft = this.left + (direction * 100);
this.index = this.newLeft / 100;
}
else {
this.index = index;
this.newLeft = index * 100;
}
if(this.newLeft > ((this.slideShow.length - 1) * 100)) {
this.index = 0;
this.newLeft = 0;
} else if(this.newLeft < 0) {
this.index = (this.slideShow.length - 1);
this.newLeft = ((this.slideShow.length - 1) * 100);
}
},
autoTimer: function() {
this.timer = setTimeout(function() {
this.transitSlide(+1);
}.bind(this), 3000);
},
transitSlide: function(direction, index) {
this.$el.removeEventListener('click', this.actionEvent.controlDirection);
this.calc(direction, index);
this.$ul.classList.add('fade');
setTimeout(function() {
this.changeSlide();
this.bindEvents();
}.bind(this), 700);
},
changeSlide: function() {
clearTimeout(this.timer);
this.autoTimer();
this.$a.forEach(function(element) {
element.classList.remove('activated');
});
this.$a[this.index].classList.add('activated');
this.$ul.style.left = this.newLeft + '%';
this.$ul.classList.remove('fade');
},
eventHandling: function() {
clearTimeout(this.timer);
event.stopImmediatePropagation();
event.preventDefault();
},
actionEvent: {
controlDirection: function() {
this.eventHandling();
if(event.target == this.$next) {
this.transitSlide(+1);
} else if(event.target == this.$prev) {
this.transitSlide(-1);
}
},
selectSlide: function() {
this.eventHandling();
this.$a.forEach( function(element, index) {
if((event.target == element) && (!element.classList.contains('activated'))) {
this.transitSlide(false, index);
}
}.bind(this));
}
}
};
slideShow.importData();
})();
我问的是 this.dataProcess()
检查数据对象以添加 class 名称和属性
这是某种与服务器端和数据库无关的数据
我应该把它留在这里还是我的结构有误?
通常,当人们谈论在演示文稿中避免复杂的逻辑时,他们指的是业务逻辑。数据中改变处理流程的条件。
在你的情况下,你谈论的是表示逻辑,而不是业务逻辑,它绝对应该保留在你拥有它的表示层中。
您应该避免的事情是如果您有控制逻辑,例如,如果您根据日期更改标题。这将是最好在服务器端的专用层内完成的逻辑,这样它就可以是 reused/tested/enforced 等
我个人使用的测试是,如果我为不同的交付渠道(例如 thick-client)重写整个应用程序,逻辑是否有用,那是有用的逻辑吗?或者我必须大幅改变它吗?如果它在很大程度上保持完整(尽管存在语言差异),那么它可能属于 server-side 在 Logic/Service/Business 层中。在您的情况下,您正在谈论修改 CSS。它特定于表示,因此如果使用不同的表示层,可能会有很大的不同。
我读过很多文章,指出我应该避免将复杂的逻辑放入我的 HTML 模板中, 所以我的问题是,如果我编写的模块需要某些特定属性或 class 名称,基于加载到我页面的数据类型,我应该将这一层写在我的模块内部作为方法还是错误的意识形态?
例如,在此代码中,我从 JSON 文件中加载数据
{
"slideShow": [
{
"img": "aaaaaaaa.jpg",
"link": "aaaaaaaaa.html",
"title": "aaaaaa",
"date": "aaaaaaaa",
"detail": "aaaaaaaaaaaaa"
},
{
"img": "bbbbbbbbbbb.jpg",
"link": "bbbbbbbbbbb.html",
"title": "bbbbbbbbbbb",
"date": "bbbbbbbbbbbbb",
"detail": "bbbbbbbbbbbbb"
},
{
"img": "ccccccccccc.jpg",
"title": "ccccccc",
"date": "ccccccccccccc"
},
{
"img": "dddddddddd.jpg",
"title": "ddddddddd",
"date": "dddddddddd",
"detail": "dddddddddd"
}
]
}
到此模板
<section id="slideShow">
<script id="slideShow-template" type="text/template">
<ul>
{{#slideShow}}
<li class="{{{class}}}">
<img src="{{{img}}}" alt="{{{title}}}">
<a href="{{{link}}}">
<h1 class="slideShowTitle">{{title}}</h1>
<p class="slideShowDate">{{date}}</p>
<p class="slideShowDetail">{{detail}}</p>
</a>
</li>
{{/slideShow}}
</ul>
<nav>
{{#slideShow}}
<a href="javascript:;"></a>
{{/slideShow}}
</nav>
<a href="javscript:void(0)" class="prevSlide"></a>
<a href="javscript:void(0)" class="nextSlide"></a>
</script>
</section>
然后我写了这个
(function() {
var slideShow = {
slideShow: [],
importData: function() {
var xhr = new XMLHttpRequest(),
url = 'data/slideShow.json',
_self = this,
result;
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
result = JSON.parse(this.responseText);
_self.slideShow = result.slideShow;
_self.dataProcess();
_self.init();
}
};
xhr.open('GET', url, true);
xhr.send();
},
dataProcess: function() {
this.slideShow.forEach(function(element, index) {
element.class = '';
if(!element.detail) {
element.class += 'noDetail ';
}
if(!element.link) {
element.link = 'javascript:void';
element.class += 'noLink ';
}
element.class = element.class.replace('undefined', '');
});
},
init: function() {
this.render();
this.cacheDom();
this.bindEvents();
this.autoRun();
},
bindEvents: function() {
this.$el.addEventListener('click', this.actionEvent.controlDirection.bind(this));
this.$nav.addEventListener('click', this.actionEvent.selectSlide.bind(this));
},
render: function(cacheDom) {
var data = {
slideShow: this.slideShow
};
this.$el = document.getElementById('slideShow');
this.template = document.getElementById('slideShow-template').innerHTML;
this.$el.innerHTML = Mustache.render(this.template, data);
},
cacheDom: function() {
this.$ul = this.$el.querySelector('ul');
this.$li = this.$ul.querySelectorAll('li');
this.$nav = this.$el.querySelector('nav');
this.$a = this.$nav.querySelectorAll('a');
this.$next = this.$el.querySelector('.nextSlide');
this.$prev = this.$el.querySelector('.prevSlide');
},
autoRun: function() {
this.$ul.style.left = '0';
this.$ul.style.width = this.slideShow.length * 100 + '%';
this.$li.forEach(function(element) {
element.style.width = (100 / this.slideShow.length) + '%';
}.bind(this));
this.$a[0].classList.add('activated');
this.autoTimer();
},
calc(direction, index) {
this.left = parseInt(this.$ul.style.left);
if(direction) {
this.newLeft = this.left + (direction * 100);
this.index = this.newLeft / 100;
}
else {
this.index = index;
this.newLeft = index * 100;
}
if(this.newLeft > ((this.slideShow.length - 1) * 100)) {
this.index = 0;
this.newLeft = 0;
} else if(this.newLeft < 0) {
this.index = (this.slideShow.length - 1);
this.newLeft = ((this.slideShow.length - 1) * 100);
}
},
autoTimer: function() {
this.timer = setTimeout(function() {
this.transitSlide(+1);
}.bind(this), 3000);
},
transitSlide: function(direction, index) {
this.$el.removeEventListener('click', this.actionEvent.controlDirection);
this.calc(direction, index);
this.$ul.classList.add('fade');
setTimeout(function() {
this.changeSlide();
this.bindEvents();
}.bind(this), 700);
},
changeSlide: function() {
clearTimeout(this.timer);
this.autoTimer();
this.$a.forEach(function(element) {
element.classList.remove('activated');
});
this.$a[this.index].classList.add('activated');
this.$ul.style.left = this.newLeft + '%';
this.$ul.classList.remove('fade');
},
eventHandling: function() {
clearTimeout(this.timer);
event.stopImmediatePropagation();
event.preventDefault();
},
actionEvent: {
controlDirection: function() {
this.eventHandling();
if(event.target == this.$next) {
this.transitSlide(+1);
} else if(event.target == this.$prev) {
this.transitSlide(-1);
}
},
selectSlide: function() {
this.eventHandling();
this.$a.forEach( function(element, index) {
if((event.target == element) && (!element.classList.contains('activated'))) {
this.transitSlide(false, index);
}
}.bind(this));
}
}
};
slideShow.importData();
})();
我问的是 this.dataProcess()
检查数据对象以添加 class 名称和属性
这是某种与服务器端和数据库无关的数据
我应该把它留在这里还是我的结构有误?
通常,当人们谈论在演示文稿中避免复杂的逻辑时,他们指的是业务逻辑。数据中改变处理流程的条件。
在你的情况下,你谈论的是表示逻辑,而不是业务逻辑,它绝对应该保留在你拥有它的表示层中。
您应该避免的事情是如果您有控制逻辑,例如,如果您根据日期更改标题。这将是最好在服务器端的专用层内完成的逻辑,这样它就可以是 reused/tested/enforced 等
我个人使用的测试是,如果我为不同的交付渠道(例如 thick-client)重写整个应用程序,逻辑是否有用,那是有用的逻辑吗?或者我必须大幅改变它吗?如果它在很大程度上保持完整(尽管存在语言差异),那么它可能属于 server-side 在 Logic/Service/Business 层中。在您的情况下,您正在谈论修改 CSS。它特定于表示,因此如果使用不同的表示层,可能会有很大的不同。