我最近一直在尝试使用模块化JS,我仍然想知道我是否正在编写#34;正确的方式"。
例如,如果我有一个页面,其中有输入和提交按钮,应该在提交后显示数据(例如表格和图形),所以我在IFFE下编写代码,所以没有任何东西可以访问它,但是使用像这样的对象变量这个:
var webApp = { ... }
在其中我从DOM缓存元素,添加绑定事件和其他有用的功能。
这是我用于表单的真实代码,在加载数据时应该显示图表,表格和进度条,并且所有代码都在一个对象qwData
中管理:
(function() {
const qwData = {
// Initialize functions
init: function() {
this.cacheDom();
this.bindEvents();
},
// Cache vars
cacheDom: function() {
this.dataDisplayed = false;
this.countUsers = <?php echo $_SESSION['all_users_count_real']; ?>;
this.customerMachines = <?php echo $_SESSION['customer_statistics']['total_machines']; ?>;
this.$form = $('#frm');
this.$alistToolbar = this.$form.find('.alist-toolbar');
this.start_date = this.$form[0][9].value;
this.end_date = this.$form[0][10].value;
this.dateCount = this.countDays(this.start_date, this.end_date);
this.show = document.querySelector('#btn-show');
this.downloadBtn = document.querySelector('#download_summary_button');
this.$dataContainer = $('#qw-data-container');
this.$qwTable = $('#qwtable');
this.$qwTbody = this.$qwTable.find('tbody');
this.$qwTd = this.$qwTbody.find('td');
this.qwChart = echarts.init(document.getElementById('main-chart'));
this.progressBar = document.querySelector('.progress-bar');
Object.defineProperty(this, "progress", {
get: () => {
return this.progressPrecent || 0;
},
set: (value) => {
if( value != this.progressPrecent ) {
this.progressPrecent = value;
// this.setQwChartProgress(value);
this.setProgressBarValue(value);
this.setProgressButton(value);
}
},
configurable: true
});
this.qwChartProgress = this.progress;
},
// Bind click events (or any events..)
bindEvents: function() {
var that = this;
// On click "Show" BTN
this.show.onclick = this.sendData.bind(this);
// On Change inputs
this.$form.change(function(){
that.updateDatesInputs(this);
});
// downloadQw
this.downloadBtn.onclick = this.downloadQw.bind(this);
},
downloadQw: function(e){
e.preventDefault();
var customer = "<?php echo $_SESSION['company_name']; ?>";
var filename = customer + "qws_"+ this.start_date + "-" + this.end_date + ".zip";
$.ajax({
url: "/aaa/api/download_csv.php",
method: "GET",
dataType : "json",
data: {
customer: customer,
filename: filename
},
success:function(result){
if(result.status){
window.location.href="/aaa/api/download_csv.php?customer="+customer+"&filename="+filename+"&download=1";
}
},
error:function(){
}
})
},
setQwChartProgress: function(value){
if (value != 0) {
// Show Chart Loading
this.qwChart.showLoading({
color: (value == 99) ? '#00b0f0' : '#fff',
text: value + '%'
});
}
},
setProgressButton: function(value){
if ( value >= 100 || value == 0 ){
this.show.value = 'Show';
}
else {
this.show.value = value +'%';
// this.show.disabled = true;
this.disableShow();
}
},
resetShowButton: function(){
this.show.value = 'Show';
this.disableShow();
},
disableShow: function(){
// this.show.style.color = "grey";
// this.show.disabled = true;
this.show.classList.add("isDisabled");
},
enableShow: function(){
// this.show.style.color = "#66aa66";
// this.show.disabled = false;
this.show.classList.remove("isDisabled");
},
updateDatesInputs: function(){
this.start_date = this.$form[0][9].value;
this.end_date = this.$form[0][11].value;
this.dateCount = this.countDays(this.start_date,this.end_date);
// this.show.disabled = false;
this.enableShow();
this.removeError();
},
removeError: function(){
if (this.errors) {
this.errors.remove();
delete this.errors;
}
},
countDays: function(date1, date2){
// First we split the values to arrays date1[0] is the year, [1] the month and [2] the day
var date1 = date1.split('-');
var date2 = date2.split('-');
// Now we convert the array to a Date object, which has several helpful methods
date1 = new Date(date1[0], date1[1], date1[2]);
date2 = new Date(date2[0], date2[1], date2[2]);
// We use the getTime() method and get the unixtime (in milliseconds, but we want seconds, therefore we divide it through 1000)
var date1_unixtime = parseInt(date1.getTime() / 1000);
var date2_unixtime = parseInt(date2.getTime() / 1000);
// This is the calculated difference in seconds
var timeDifference = date2_unixtime - date1_unixtime;
// in Hours
var timeDifferenceInHours = timeDifference / 60 / 60;
// and finaly, in days :)
var timeDifferenceInDays = timeDifferenceInHours / 24;
if (timeDifferenceInDays > 0){
return timeDifferenceInDays;
} else {
// alert('Error: The date are invalid.');
}
},
// Get data, prevent submit defaults and submit.
sendData: function(e) {
e.preventDefault();
if (this.show.classList.contains('isDisabled')) {
this.showErrorDiv("Please select a new date range before submitting.");
} else {
let that = this;
let estimatedTotalTime = ( (this.countUsers*this.customerMachines)/777 ) * 0.098;
let estimatedTime = estimatedTotalTime/99;
let estimatedTimeMs = estimatedTime*1000;
let timer = setInterval( function(){that.incrementProgress(timer);}, estimatedTime);
console.log('Total Time: ' + estimatedTotalTime + 's');
console.log('Estimated Time for 1%: ' + estimatedTime + 's');
$.ajax({
type: 'POST',
url: "/manageit/ajax.php?module=qw_module",
dataType: 'json',
data: {
start_ts: that.start_date,
stop_ts: that.end_date,
submitted: true,
company_name: "<?php echo $_SESSION['company_name']; ?>"
},
beforeSend: function() {
// Show Chart Loading
that.qwChart.showLoading({
color: '#00b0f0',
// text: that.qwChartProgress
text: ''
});
// If data div isn't displayed
if (!that.dataDisplayed) {
// Show divs loading
that.showMainDiv();
} else {
that.$qwTbody.slideUp('fast');
that.$qwTbody.html('');
}
},
complete: function(){},
success: function(result){
// Reset show btn
that.resetShowButton();
// Clear timer
clearInterval(timer);
// Set progressbar to 100%
that.setProgressBarTo100();
// Show Download Button
that.downloadBtn.style.display = 'inline-block';
// Insert Chart Data
that.insertChartData(result);
// Insert Table Data
that.insertTableData(result);
}
});
that.dataDisplayed = true;
}
},
showErrorDiv: function(errorTxt){
if (!this.errors){
this.errors = document.createElement("div");
this.errors.className = "qw_errors_div";
this.errors.textContent = errorTxt;
this.$alistToolbar.append(this.errors);
}
},
// Insert Data to Table
insertTableData: function(json){
let str = '';
let isOdd = ' rowspan="2" ';
for ( let i=1; i<9; i++ ) {
str += '<tr>';
for (let j = 0; j < 8; j++) {
if ((i%2 === 0) && (j==0)){
// do nada
} else {
str += '<td ';
str += ((i % 2 !== 0)&&(j==0)) ? isOdd : '';
str += '> ';
str += json[i][j];
str += '</td>';
}
}
str += '</tr>';
}
this.$qwTbody.html(str);
this.$qwTbody.slideDown('fast', function(){
if ($(this).is(':visible'))
$(this).css('display','table-row-group');
});
// Apply colors on table.
this.tableHover();
},
tableHover: function(){
this.$qwTd = this.$qwTbody.find('td');
var that = this;
this.$qwTd.eq(0).hover( function(){
that.$qwTd.eq(0).css('background-color', '#f5f5f5');
that.$qwTd.eq(0).parent().css('background-color', '#f5f5f5');
that.$qwTd.eq(0).parent().next().css('background-color', '#f5f5f5');
}, function(){
that.$qwTd.eq(0).css('background-color', '');
that.$qwTd.eq(0).parent().css('background-color', '');
that.$qwTd.eq(0).parent().next().css('background-color', '');
});
this.$qwTd.eq(15).hover( function(){
that.$qwTd.eq(15).css('background-color', '#f5f5f5');
that.$qwTd.eq(15).parent().css('background-color', '#f5f5f5');
that.$qwTd.eq(15).parent().next().css('background-color', '#f5f5f5');
}, function(){
that.$qwTd.eq(15).css('background-color', '');
that.$qwTd.eq(15).parent().css('background-color', '');
that.$qwTd.eq(15).parent().next().css('background-color', '');
});
this.$qwTd.eq(30).hover( function(){
that.$qwTd.eq(30).css('background-color', '#f5f5f5');
that.$qwTd.eq(30).parent().css('background-color', '#f5f5f5');
that.$qwTd.eq(30).parent().next().css('background-color', '#f5f5f5');
}, function(){
that.$qwTd.eq(30).css('background-color', '');
that.$qwTd.eq(30).parent().css('background-color', '');
that.$qwTd.eq(30).parent().next().css('background-color', '');
});
this.$qwTd.eq(45).hover( function(){
that.$qwTd.eq(45).css('background-color', '#f5f5f5');
that.$qwTd.eq(45).parent().css('background-color', '#f5f5f5');
that.$qwTd.eq(45).parent().next().css('background-color', '#f5f5f5');
}, function(){
that.$qwTd.eq(45).css('background-color', '');
that.$qwTd.eq(45).parent().css('background-color', '');
that.$qwTd.eq(45).parent().next().css('background-color', '');
});
},
incrementProgress: function(timer){
if (this.progress == 99)
clearInterval(timer);
else
this.progress++;
},
// Insert Data to Chart
insertChartData: function(json){
var posList = [
'left', 'right', 'top', 'bottom',
'inside',
'insideTop', 'insideLeft', 'insideRight', 'insideBottom',
'insideTopLeft', 'insideTopRight', 'insideBottomLeft', 'insideBottomRight'
];
this.qwChart.configParameters = {
rotate: {
min: -90,
max: 90
},
align: {
options: {
left: 'left',
center: 'center',
right: 'right'
}
},
verticalAlign: {
options: {
top: 'top',
middle: 'middle',
bottom: 'bottom'
}
},
position: {
options: echarts.util.reduce(posList, function (map, pos) {
map[pos] = pos;
return map;
}, {})
},
distance: {
min: 0,
max: 100
}
};
this.qwChart.config = {
rotate: 90,
align: 'left',
verticalAlign: 'middle',
position: 'insideBottom',
distance: 15,
onChange: function () {
var labelOption = {
normal: {
rotate: this.qwChart.config.rotate,
align: this.qwChart.config.align,
verticalAlign: this.qwChart.config.verticalAlign,
position: this.qwChart.config.position,
distance: this.qwChart.config.distance
}
};
this.qwChart.setOption({
series: [{
label: labelOption
}, {
label: labelOption
}, {
label: labelOption
}]
});
}
};
var labelOption = {
normal: {
show: true,
position: this.qwChart.config.position,
distance: this.qwChart.config.distance,
align: this.qwChart.config.align,
verticalAlign: this.qwChart.config.verticalAlign,
rotate: this.qwChart.config.rotate,
// formatter: '{c} {name|{a}}',
formatter: '{name|{a}}',
fontSize: 16,
rich: {
name: {
// textBorderColor: '#fff',
// color: '#333',
// color: '#717171',
color: '#525252',
shadowColor: 'transparent',
shadowBlur: 0,
textBorderColor: 'transparent',
textBorderWidth: 0,
textShadowColor: 'transparent',
textShadowBlur: 0
}
}
}
};
option = {
color: ['#007bff', '#00b0f0', 'red', '#e5323e'],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['Inactives / Viewers', 'Inactives / Viewers / Less than 1min per day', 'Light no Macro']
},
toolbox: {
show: true,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
mark: {show: true},
// dataView: {show: true, readOnly: false},
// magicType: {show: true, type: ['line', 'bar', 'stack', 'tiled']},
restore: {show: true},
saveAsImage: {show: true}
}
},
calculable: true,
xAxis: [
{
type: 'category',
axisTick: {show: false},
data: ['Excel', 'Word', 'PowerPoint', 'All 3 Apps']
}
],
yAxis: [
{
type: 'value',
name: 'Score'
}
],
series: [
{
name: 'Light no Macro',
type: 'bar',
label: labelOption,
color: 'red',
data: [ [3, json[7][7]] ]
},
{
name: 'Inactives / Viewers',
type: 'bar',
barGap: 0,
label: labelOption,
data: [ json[1][7], json[3][7], json[5][7], json[8][7] ]
},
{
name: 'Inactives / Viewers / Less than 1min per day',
type: 'bar',
label: labelOption,
data: [ json[2][7], json[4][7], json[6][7] ]
}
]
};
// Set charts options
this.qwChart.setOption(option);
// Hide Loading
this.qwChart.hideLoading();
},
// Show Main div on submition (only)
showMainDiv: function(){
// Show all contatner div
this.$dataContainer.slideDown('slow');
},
// Sets a new value for the progress bar
setProgressBarValue: function(value){
this.progressBar.style.width = this.returnNumWithPrecent(value);
},
returnNumWithPrecent: function(num){
return num.toString() + '%';
},
setProgressBarTo100: function(){
var that = this;
// Show Download Button
this.progress = 100;
setTimeout(function () {
// Show Download Button
that.progress = 0;
}, 1000);
}
}
// run object
qwData.init();
})();
但是我看到other examples在函数下编写功能而不是对象:
webApp = function (){ ... };
喜欢这个例子:
var Background = (function() {
'use strict';
// placeholder for cached DOM elements
var DOM = {};
/* =================== private methods ================= */
// cache DOM elements
function cacheDom() {
DOM.$background = $('#background');
}
// coordinate async assembly of image element and rendering
function loadImage() {
var baseUrl = 'https://source.unsplash.com/category',
cat = 'nature',
size = '1920x1080';
buildElement(`${baseUrl}/${cat}/${size}`)
.then(render);
}
// assemble the image element
function buildElement(source) {
var deferred = $.Deferred(function (task) {
var image = new Image();
image.onload = function () {
task.resolve(image);
};
image.onerror = function () {
task.reject();
};
image.src = source;
});
return deferred.promise();
}
// render DOM
function render(image) {
DOM.$background
.append(image)
.css('opacity', 1);
}
/* =================== public methods ================== */
// main init method
function init() {
cacheDom();
loadImage();
}
/* =============== export public methods =============== */
return {
init: init
};
}());
我有两个问题:
使用对象及其内部设置函数,变量,等等有什么区别:
var webApp = {...};
和具有相同特征的函数变量(仅用a 语法写得不同)。就像我粘贴的链接中的例子一样。
var webApp = function (){ ... };
答案 0 :(得分:2)
互联网教程的一个问题是它们徘徊在相关点之外,很少有作者让它们保持最新。 JS领域的事情发展得非常快,5年前的行业标准(例如jQuery)现在看起来很奇怪,当你仍然偶然发现它时。
所以,要实践我为了省略而唠叨他人的好习惯:
这是一团糟。
首先你有ES 6模块。 ES 6更名为ES 2015,模块部分被取出并制作成一个单独的规范,这意味着浏览器可能符合ES 2015标准,但仍然没有本机模块。然而,3年后,每个具有相关全球市场份额的浏览器(chrome,android chrome,firefox,iOS Safari)至少实现了原生模块系统的基本版本(Edge,Opera等)。我不清楚,因为我相信规范允许路径更宽容(我们将在一分钟内回到那里),但这里是语法,相对或绝对文件路径,需要扩展:
import Foo from './foo.js'; // Import default export from foo.js, alias to 'Foo'
import { Foo } from './foo.js'; // Import named export 'Foo' from foo.js
export default function () {}; // exports a function as default, importer aliases
const Foo = class Foo {};
export Foo; // exports named class Foo
这些优于其他任何东西(首先,你不需要特殊的工具或构建过程),但由于它们非常最近,它们并没有被广泛使用JavaScript生态系统呢。因为他们很快就会来,人们有工作要做,他们实施了各种其他模块模式/工具/系统。最早的一个是你问题中的一个,但是这个模式虽然肯定比什么都好但是有足够的问题让人们开始环顾四周。
另一个早期的产品是require.js
的异步模块定义。虽然它具有一些引人注目的技术优势,但它实际上已经死了。
node.js使用它自己的基于common.js模块的模块系统(它基本上已成为common.js的事实风格)爆炸到了场景。人们开始说“嘿,能够在浏览器中做到这一点也很棒”,因此,浏览器化。 Browserify是一个工具,可以遍历您的依赖图并将require
表达式转换为浏览器可以处理的内容(基本上,通过创建require函数)。 Node的模块有点不适合浏览器,但是在一个标准上的融合优于八千万个竞争性的adhoc实现。人们看着这三个竞争模块模式/系统(你的问题中的一个,AMD,common.js),并说我们可以统一这些。因此
如果你看到野外代码如下:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
然后你见过UMD。请注意它如何检查它所处的环境是为AMD还是common.js设置的。变形金刚被编写为将这两种风格转换为遗留代码和易读性问题(这是一个相当的样板)。
但人们想要更多:他们希望能够在代码中表达他们webapp的所有依赖关系(包括css和图像),并使用工具进行分片并有选择地加载它。此时,本机模块规范还在草案中,人们希望使用该语法。因此
Webpack目前是当前使用的事实系统(虽然很多人仍然使用browserify)。 Webpack的模块语法如下所示:
import Foo from 'foo'; // imports default export from foo.js somewhere on PATH
这看起来很熟悉吗?非常相似(但与原生模块略有不同)。 Webpack还可以执行以下操作:
import 'Something.css'; // commonly seen in react apps
import 'logo.svg'; // ditto
这很方便,随着人们转向组件化系统,能够在该组件的入口点文件中表达所有组件依赖关系是很好的。不幸的是,HTML导入本来可以让你在没有构建步骤的情况下做到这一点,这可能会导致一场火热的可怕死亡。
与本机模块系统的不兼容性,细微(路径和文件扩展名)和粗略(导入非js资产)是不幸的,这意味着一个或另一个将不得不改变,因为我试图写最近基于模块的原生应用程序,非常难以使用的库(几乎没有一个提供本机模块的味道)。
使用什么是一种自以为是的问题,但如果您使用框架,请使用该框架的其他用户常用的任何内容。 Common.js和webpack很常见,有很多工具可以消耗它们,这可能是你最好的选择。需要注意的另一件事是动态导入,已经在几个浏览器中登陆了。
很抱歉这一切都让人感到困惑,您恰好在过渡期间输入了JavaScript。
答案 1 :(得分:1)
IIFE模式允许私有变量 - DOM
,cacheDom
,loadImage
- 这些变量无法从IIFE外部访问。在对象模式中,一切都可以作为对象的属性公开访问。有时你不能在不首先声明某些变量的情况下构造你想要的对象,所以保持那些孤立在IIFE中也是有用的。
答案 2 :(得分:1)
如果您熟悉面向对象编程,可以通过类的私有和公共属性和函数来理解它。 其他方法:模块化模式,请参阅节点模块。这是关于包装。
看看这段代码:
var obj = (function() {
let a = function() { console.log('function a'); };
let b = function() { a(); console.log('function b'); };
return { b: b };
})();
obj.b();
// function a
// function b
obj.a();
// TypeError: obj.a is not a function
或者可能更熟悉这种方式:
function MyStuff() {
let a = function() { console.log('function a'); };
let b = function() { a(); console.log('function b'); };
return { b: b };
}
var obj = MyStuff();
obj.b();
// function a
// function b
obj.a();
// TypeError: obj.a is not a function
在函数内部,您有一个本地范围,无法从外部访问。在本地范围内,您可以创建变量,函数等。
在return语句中,返回一个将函数和变量导出到外部作用域的对象。这就像决定哪些功能/道具是公开的,哪些应该保密。
当您以var webApp = {...}
方式创建对象时,您将直接创建一个对象,其所有的道具和函数都是公共的,您正在定义变量&#34; webApp&#34;。< / p>
回答你的第二个问题,是的,你应该把问题分成不同的对象。请参阅SOLID原则。 :)