与artTemplate大不同的是
artTemplate使用split分割模板字符串;ejs通过基于正则的match方法分割模板字符串。
artTemplate通过传入辅助函数的引用拼接编译函数字符串;ejs有赖于js原生语句实现each、if语法,普通内容直接push到__output中,构建编译函数字符串。
1.ejs.js
/* * EJS Embedded JavaScript templates * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ 'use strict'; /** * @file Embedded JavaScript templating engine. * @author Matthew Eernisse <mde@fleegix.org> * @author Tiancheng "Timothy" Gu <timothygu99@gmail.com> * @project EJS * @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0} */ /** * EJS internal functions. * * Technically this "module" lies in the same file as {@link module:ejs}, for * the sake of organization all the private functions re grouped into this * module. * * @module ejs-internal * @private */ /** * Embedded JavaScript templating engine. * * @module ejs * @public */ var fs = require('fs'); var path = require('path'); var utils = require('./utils'); var scopeOptionWarned = false; var _VERSION_STRING = require('../package.json').version; var _DEFAULT_DELIMITER = '%'; var _DEFAULT_LOCALS_NAME = 'locals'; var _NAME = 'ejs'; var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)';// 开启闭合标签的默认匹配正则 var _OPTS = ['delimiter', 'scope', 'context', 'debug', 'compileDebug', 'client', '_with', 'rmWhitespace', 'strict', 'filename']; var _BOM = /^\uFEFF/; // 缓存编译函数 exports.cache = utils.cache; exports.localsName = _DEFAULT_LOCALS_NAME; // 获取子模板的绝对路径 // 当前模板设置filename文件或目录路径,name传参为相对filename目录的文件路径 exports.resolveInclude = function(name, filename, isDir) { var dirname = path.dirname; var extname = path.extname; var resolve = path.resolve; var includePath = resolve(isDir ? filename : dirname(filename), name); var ext = extname(name); if (!ext) { includePath += '.ejs'; } return includePath; }; // 获取子模板的绝对路径 function getIncludePath(path, options){ var includePath; if (path.charAt(0) == '/') { includePath = exports.resolveInclude(path.replace(/^\/*/,''), options.root || '/', true); } else { if (!options.filename) { throw new Error('`include` use relative path requires the \'filename\' option.'); } includePath = exports.resolveInclude(path, options.filename); } return includePath; } // 缓存中有编译函数,获取编译函数;若无,通过文件名读取文件后,获得编译函数,并存入缓存 function handleCache(options, template) { var func; var filename = options.filename; var hasTemplate = arguments.length > 1; if (options.cache) { if (!filename) { throw new Error('cache option requires a filename'); } func = exports.cache.get(filename); if (func) { return func; } if (!hasTemplate) { template = fs.readFileSync(filename).toString().replace(_BOM, ''); } } else if (!hasTemplate) { if (!filename) { throw new Error('Internal EJS error: no file name or template ' + 'provided'); } template = fs.readFileSync(filename).toString().replace(_BOM, ''); } func = exports.compile(template, options); if (options.cache) { exports.cache.set(filename, func); } return func; } // 通过文件路径获取编译函数 function includeFile(path, options) { var opts = utils.shallowCopy({}, options); opts.filename = getIncludePath(path, opts); // handleCache 缓存中有编译函数,获取编译函数;若无,通过文件名读取文件后,获得编译函数,并存入缓存 return handleCache(opts); } // 获取嵌套模板的编译函数核心字符串、模板字符串内容 function includeSource(path, options) { var opts = utils.shallowCopy({}, options); var includePath; var template; includePath = getIncludePath(path,opts); template = fs.readFileSync(includePath).toString().replace(_BOM, ''); opts.filename = includePath; var templ = new Template(template, opts); templ.generateSource(); return { source: templ.source, filename: includePath, template: template }; } /** * 报错 * * @param {Error} err Error object * @param {String} str 编译核心核心内容,字符串形式 * @param {String} filename 文件名 * @param {String} lineno 错误的行号 */ function rethrow(err, str, flnm, lineno){ var lines = str.split('\n'); var start = Math.max(lineno - 3, 0); var end = Math.min(lines.length, lineno + 3); var filename = utils.escapeXML(flnm); var context = lines.slice(start, end).map(function (line, i){ var curr = i + start + 1; return (curr == lineno ? ' >> ' : ' ') + curr + '| ' + line; }).join('\n'); err.path = filename; err.message = (filename || 'ejs') + ':' + lineno + '\n' + context + '\n\n' + err.message; throw err; } // 去除";"及其空格 function stripSemi(str) { return str.replace(/;(\s*$)/, '$1'); } // 编译模板字符串template,返回编译函数,编译配置为opts exports.compile = function compile(template, opts) { var templ; // 旧版本的scope替换为context if (opts && opts.scope) { if (!scopeOptionWarned){ console.warn('`scope` option is deprecated and will be removed in EJS 3'); scopeOptionWarned = true; } if (!opts.context) { opts.context = opts.scope; } delete opts.scope; } templ = new Template(template, opts); return templ.compile(); }; // 通过模板路径和模板数据、编译配置获取编译结果 exports.render = function (template, d, o) { var data = d || {}; var opts = o || {}; // No options object -- if there are optiony names // in the data, copy them to options if (arguments.length == 2) { utils.shallowCopyFromList(opts, data, _OPTS); } // handleCache 缓存中有编译函数,获取编译函数;若无,通过文件名读取文件后,获得编译函数,并存入缓存 return handleCache(opts, template)(data); }; /** * 通过模板路径和模板数据获取编译结果,并执行回调 * @param {String} filename 模板路径 * @param {Object} [data={}] 模板数据 * @param {Options} [opts={}] 编译配置 * @param {RenderFileCallback} cb 回调函数 */ exports.renderFile = function () { var args = Array.prototype.slice.call(arguments); var filename = args.shift(); var cb = args.pop(); var data = args.shift() || {}; var opts = args.pop() || {}; var optsKeys =_OPTS.slice(); var result; opts = utils.shallowCopy({}, opts); // `renderFile`方法添加"cache"为可配置内容 optsKeys.push('cache'); if (arguments.length == 3) { // Express 4 if (data.settings && data.settings['view options']) { utils.shallowCopyFromList(opts, data.settings['view options'], optsKeys); } // Express 3 and lower else { utils.shallowCopyFromList(opts, data, optsKeys); } } opts.filename = filename; try { // handleCache 缓存中有编译函数,获取编译函数;若无,通过文件名读取文件后,获得编译函数,并存入缓存 result = handleCache(opts)(data); }catch(err) { return cb(err); } return cb(null, result); }; // 清空模板编译函数 exports.clearCache = function () { exports.cache.reset(); }; function Template(text, opts) { opts = opts || {}; var options = {}; this.templateText = text;// 模板字符串 this.mode = null;// 模板字符串各line处理模式 this.truncate = false; this.currentLine = 1;// 错误提示时输出行号 this.source = '';// 编译函数体核心内容,字符串形式 this.dependencies = [];// 记录当前模板包含的模板 options.client = opts.client || false;// 客户端或服务端不同模块将escape、rethow函数注入到编译函数体内 options.escapeFunction = opts.escape || utils.escapeXML;// html转义 options.compileDebug = opts.compileDebug !== false;// 是否开启调试模板编译模板 options.debug = !!opts.debug;// 是否以字符串形式输出编译函数 options.filename = opts.filename;// 模板名,作为模板的标识符,缓存编译函数的键值 options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;// 开启或者闭合尖括号所用的字符 options.strict = opts.strict || false;// 编译函数采用严格模式 "use strict",严格模式下不能使用with语句 options.context = opts.context;// 编译函数上下文 options.cache = opts.cache || false;// 是否开启编译函数缓存 options.rmWhitespace = opts.rmWhitespace;// 是否清除起始的空格 options.root = opts.root; options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME; if (options.strict) { // var obj={a:1};with(obj){console.log(a)} with语句快速引用对象的属性与方法 options._with = false; }else { options._with = typeof opts._with != 'undefined' ? opts._with : true; } this.opts = options; this.regex = this.createRegex();// 开启闭合标签的匹配正则 } Template.modes = { EVAL: 'eval',// "<% %>"、"<%_ %>"内语句原样拼接到编译函数体内,可承接js语句,后跟模板字符串通过push方法添加到__output ESCAPED: 'escaped',// "<%= %>"内语句通过html转义后,可承接变量,由push方法添加到__output RAW: 'raw',// "<%- %>"不转义,可承接变量,由push方法添加到__output COMMENT: 'comment',// "<%# %>"注释,不添加编译函数体内 LITERAL: 'literal'// "<%% %%>"通过push方法将"<% %>"由push方法添加到__output }; Template.prototype = { // 根据delimiter配置,重新生成开启闭合标签的匹配正则 createRegex: function () { var str = _REGEX_STRING; var delim = utils.escapeRegExpChars(this.opts.delimiter); str = str.replace(/%/g, delim); return new RegExp(str); }, // 获取编译函数,客户端或者服务器端不同模块间将escape、include函数以字符串形式写入 // 同artTemplate不同的是,artTemplate主要面向客户端,$utils、$helpers都通过引用的方式构建传入 // ejs面向服务端,each语法借用js的原生语句,escape、include函数以参数或字符串形式传入 // 两者都同样以拼接字符串的方式生成编译函数体,再通过new Function构建函数 compile: function () { var src; var fn; var opts = this.opts; var prepended = ''; var appended = ''; var escape = opts.escapeFunction; if (!this.source) { // 将模板字符串通过match方法拆分为开闭标签前后各段,按条件拼接编译函数体,或者作为js语句,或者作为普通字符串 this.generateSource(); prepended += ' var __output = [], __append = __output.push.bind(__output);' + '\n'; if (opts._with !== false) { prepended += ' with (' + opts.localsName + ' || {}) {' + '\n'; appended += ' }' + '\n'; } appended += ' return __output.join("");' + '\n'; this.source = prepended + this.source + appended; } if (opts.compileDebug) { src = 'var __line = 1' + '\n' + ' , __lines = ' + JSON.stringify(this.templateText) + '\n' + ' , __filename = ' + (opts.filename ? JSON.stringify(opts.filename) : 'undefined') + ';' + '\n' + 'try {' + '\n' + this.source + '} catch (e) {' + '\n' + ' rethrow(e, __lines, __filename, __line);' + '\n' + '}' + '\n'; }else { src = this.source; } if (opts.debug) { console.log(src); } // 客户端将escape、rethow函数注入到编译函数体内 if (opts.client) { src = 'escape = escape || ' + escape.toString() + ';' + '\n' + src; if (opts.compileDebug) { src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src; } } if (opts.strict) { src = '"use strict";\n' + src; } try { fn = new Function(opts.localsName + ', escape, include, rethrow', src); }catch(e) { if (e instanceof SyntaxError) { if (opts.filename) { e.message += ' in ' + opts.filename; } e.message += ' while compiling ejs\n\n'; e.message += 'If the above error is not helpful, you may want to try EJS-Lint:\n'; e.message += 'https://github.com/RyanZim/EJS-Lint'; } throw e; } if (opts.client) { fn.dependencies = this.dependencies; return fn; } var returnedFn = function (data) { var include = function (path, includeData) { var d = utils.shallowCopy({}, data); if (includeData) { d = utils.shallowCopy(d, includeData); } // includeFile 通过文件路径获取编译函数 return includeFile(path, opts)(d); }; return fn.apply(opts.context, [data || {}, escape, include, rethrow]); }; returnedFn.dependencies = this.dependencies; return returnedFn; }, // 将模板字符串通过match方法拆分为开闭标签前后各段,按条件拼接编译函数体,或者作为js语句,或者作为普通字符串 generateSource: function () { var opts = this.opts; if (opts.rmWhitespace) { this.templateText = this.templateText.replace(/\r/g, '').replace(/^\s+|\s+$/gm, ''); } // 清除<%_前、_%>后的制表符\t及空格 this.templateText = this.templateText.replace(/[ \t]*<%_/gm, '<%_').replace(/_%>[ \t]*/gm, '_%>'); var self = this; // 模板字符串清除开闭标签,以数组形式记录开闭标签及其前后字符串,并返回 var matches = this.parseTemplateText(); var d = this.opts.delimiter; if (matches && matches.length) { matches.forEach(function (line, index) { var opening; var closing; var include; var includeOpts; var includeObj; var includeSrc; // 开始标签存在时,校验闭合标签是否存在 if ( line.indexOf('<' + d) === 0 && line.indexOf('<' + d + d) !== 0 ) { closing = matches[index + 2];// 闭合标签 if (!(closing == d + '>' || closing == '-' + d + '>' || closing == '_' + d + '>')) { throw new Error('Could not find matching close tag for "' + line + '".'); } } // 拼接include子模板字符串 if ((include = line.match(/^\s*include\s+(\S+)/))) { opening = matches[index - 1]; if (opening && (opening == '<' + d || opening == '<' + d + '-' || opening == '<' + d + '_')) { // utils.shallowCopy 对象浅拷贝 includeOpts = utils.shallowCopy({}, self.opts); // 获取嵌套模板的编译函数核心字符串、模板字符串内容 includeObj = includeSource(include[1], includeOpts); if (self.opts.compileDebug) { includeSrc = ' ; (function(){' + '\n' + ' var __line = 1' + '\n' + ' , __lines = ' + JSON.stringify(includeObj.template) + '\n' + ' , __filename = ' + JSON.stringify(includeObj.filename) + ';' + '\n' + ' try {' + '\n' + includeObj.source + ' } catch (e) {' + '\n' + ' rethrow(e, __lines, __filename, __line);' + '\n' + ' }' + '\n' + ' ; }).call(this)' + '\n'; }else{ includeSrc = ' ; (function(){' + '\n' + includeObj.source + ' ; }).call(this)' + '\n'; } self.source += includeSrc; self.dependencies.push(exports.resolveInclude(include[1], includeOpts.filename)); return; } } // line为开闭标签,及其内外的各项字符串,根据开闭标签拼接编译函数字符串 self.scanLine(line); }); } }, // 模板字符串清除开闭标签,以数组形式记录开闭标签及其前后字符串,并返回 parseTemplateText: function () { var str = this.templateText; var pat = this.regex;// 开启闭合标签的匹配正则 var result = pat.exec(str);// 获取str所有开启闭合标签,index属性为其在str中的序号 var arr = []; var firstPos; while (result) { firstPos = result.index; if (firstPos !== 0) { arr.push(str.substring(0, firstPos)); str = str.slice(firstPos); } arr.push(result[0]); str = str.slice(result[0].length); result = pat.exec(str); } if (str) { arr.push(str); } return arr; }, // line为开闭标签,及其内外的各项字符串,根据开闭标签拼接编译函数字符串 scanLine: function (line) { var self = this; var d = this.opts.delimiter;// 开启或者闭合尖括号所用的字符 var newLineCount = 0; // 普通字符串处理 function _addOutput() { if (self.truncate) { line = line.replace(/^(?:\r\n|\r|\n)/, ''); self.truncate = false; } else if (self.opts.rmWhitespace) { line = line.replace(/^\n/, ''); } if (!line) { return; } // 转义 line = line.replace(/\\/g, '\\\\'); line = line.replace(/\n/g, '\\n'); line = line.replace(/\r/g, '\\r'); line = line.replace(/"/g, '\\"'); self.source += ' ; __append("' + line + '")' + '\n'; } newLineCount = (line.split('\n').length - 1); switch (line) { // "<% %>"、"<%_ %>"内语句原样拼接到编译函数体内,后跟模板字符串通过push方法添加到__output case '<' + d: case '<' + d + '_': this.mode = Template.modes.EVAL; break; // "<%= %>"内语句通过html转义后,可承接变量,由push方法添加到__output case '<' + d + '=': this.mode = Template.modes.ESCAPED; break; // "<%- %>"不转义,可承接变量 case '<' + d + '-': this.mode = Template.modes.RAW; break; // "<%# %>"评论,不添加编译函数体内 case '<' + d + '#': this.mode = Template.modes.COMMENT; break; // "<%% %%>"通过push方法将"<% %>"由push方法添加到__output case '<' + d + d: this.mode = Template.modes.LITERAL; this.source += ' ; __append("' + line.replace('<' + d + d, '<' + d) + '")' + '\n'; break; case d + d + '>': this.mode = Template.modes.LITERAL; this.source += ' ; __append("' + line.replace(d + d + '>', d + '>') + '")' + '\n'; break; case d + '>': case '-' + d + '>': case '_' + d + '>': if (this.mode == Template.modes.LITERAL) { _addOutput(); } this.mode = null; this.truncate = line.indexOf('-') === 0 || line.indexOf('_') === 0; break; default: // 开闭标签包裹的内容 if (this.mode) { // 单行注释后没有"\n",添加"\n" switch (this.mode) { case Template.modes.EVAL: case Template.modes.ESCAPED: case Template.modes.RAW: if (line.lastIndexOf('//') > line.lastIndexOf('\n')) { line += '\n'; } } switch (this.mode) { case Template.modes.EVAL: this.source += ' ; ' + line + '\n'; break; case Template.modes.ESCAPED: this.source += ' ; __append(escape(' + stripSemi(line) + '))' + '\n'; break; case Template.modes.RAW: this.source += ' ; __append(' + stripSemi(line) + ')' + '\n'; break; case Template.modes.COMMENT: break; case Template.modes.LITERAL: _addOutput(); break; } // 开闭标签之外的内容 } else { _addOutput(); } } // 调试模式下,添加编译的所在行数,输出错误提示时所用 if (self.opts.compileDebug && newLineCount) { this.currentLine += newLineCount; this.source += ' ; __line = ' + this.currentLine + '\n'; } } }; // html转义 exports.escapeXML = utils.escapeXML; // 通过模板路径和模板数据获取编译结果,并执行回调 exports.__express = exports.renderFile; // node环境下,添加require方法加载扩展名为ejs的模块,值为编译函数 if (require.extensions) { require.extensions['.ejs'] = function (module, flnm) { var filename = flnm || /* istanbul ignore next */ module.filename; var options = { filename: filename, client: true// 同ejs.js不在同一个模块,escape、include以字符串形式赋给编译函数体 }; var template = fs.readFileSync(filename).toString(); var fn = exports.compile(template, options); module._compile('module.exports = ' + fn.toString() + ';', filename); }; } exports.VERSION = _VERSION_STRING; exports.name = _NAME; if (typeof window != 'undefined') { window.ejs = exports; }
2.utils.js
/* * EJS Embedded JavaScript templates * Copyright 2112 Matthew Eernisse (mde@fleegix.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /** * Private utility functions * @module utils * @private */ 'use strict'; // 用户设置开启或者闭合尖括号所用的字符delimiter时,以下字符串有效 var regExpChars = /[|\\{}()[\]^$+*?.]/g; // 用户设置开启或者闭合尖括号所用的字符delimiter时,仅|\{}()[]^$+*?.有效 exports.escapeRegExpChars = function (string) { if (!string) { return ''; } return String(string).replace(regExpChars, '\\$&');// 输出匹配部分 }; var _ENCODE_HTML_RULES = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; var _MATCH_HTML = /[&<>\'"]/g; function encode_char(c) { return _ENCODE_HTML_RULES[c] || c; } var escapeFuncStr = 'var _ENCODE_HTML_RULES = {\n' + ' "&": "&"\n' + ' , "<": "<"\n' + ' , ">": ">"\n' + ' , \'"\': """\n' + ' , "\'": "'"\n' + ' }\n' + ' , _MATCH_HTML = /[&<>\'"]/g;\n' + 'function encode_char(c) {\n' + ' return _ENCODE_HTML_RULES[c] || c;\n' + '};\n'; // html转义 exports.escapeXML = function (markup) { return markup == undefined ? '' : String(markup).replace(_MATCH_HTML, encode_char); }; // 函数转化成字符串时,同时将参数变量全部转化为字符串输入 exports.escapeXML.toString = function () { return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr; }; // 将from的属性或方法浅拷贝给to exports.shallowCopy = function (to, from) { from = from || {}; for (var p in from) { to[p] = from[p]; } return to; }; // list以数组形式约定从from浅拷贝给to的属性名或方法名 exports.shallowCopyFromList = function (to, from, list) { list.forEach(function (p) { if (typeof from[p] != 'undefined') { to[p] = from[p]; } }); return to; }; // 缓存编译函数 exports.cache = { _data: {}, set: function (key, val) { this._data[key] = val; }, get: function (key) { return this._data[key]; }, reset: function () { this._data = {}; } };
相关推荐
EJS后缀名为”ejs” EJS 支持我们把JS代码直接写在标签内 EJS 能够缓存 JS函数的中间代码,使执行速度得到极大的提升 EJS 调错极其简单:因为它所有错误都是普通的 JS 异常,而且也会给我们输出异常发生的具体位置 ...
标题中的"EJS Challenge Starting Files_SIMULINK_源码.zip"表明这是一个关于电子工程或软件开发的挑战项目,其中包含了SIMULINK的源码文件。SIMULINK是MATLAB的一个扩展,用于建立动态系统的模型,广泛应用于控制...
这个RAR压缩包包含的是与ARM926EJS相关的开发程序源码,已经过编译和仿真的验证,适用于使用Visual C进行编程的开发者。接下来,我们将深入探讨ARM926EJS内核、Visual C开发环境以及如何结合这两者进行嵌入式系统...
7. **模板引擎**:可能使用了Handlebars、Pug或EJS等模板引擎来动态渲染HTML,便于将数据和视图结合。 8. **响应式布局**:使用媒体查询(Media Queries)确保页面在不同屏幕尺寸下都能自适应显示,提供良好的...
5. **模板引擎**:为了提高页面的渲染效率,源码可能会使用EJS、Pug、Twig等模板引擎来分离视图和逻辑,帮助快速构建动态网页。 6. **API设计**:源码中的API接口设计遵循RESTful原则,用于客户端与服务器间的数据...
6. **模板引擎**:如Smarty或EJS,用于将数据动态渲染到HTML中。 7. **SEO优化**:如元标签设置、URL结构优化,提升网站在搜索引擎中的排名。 对于初学者,可以通过阅读源码学习网页布局技巧、函数调用、事件处理等...
1. **源码文件**:`ejs-0.9.9`目录下通常会有`.js`文件,这些是EJS的核心实现,包含解析、编译和执行EJS模板的函数。 2. **示例**:可能包含一些示例文件,展示如何在项目中集成和使用EJS,帮助开发者快速上手。 3...
7. **模板引擎**:部分实例可能使用了诸如EJS、Pug或 Handlebars等模板引擎,这些工具可以帮助开发者分离视图和逻辑,使代码更加清晰。 8. **前端框架和库**:除了Bootstrap,还可能涉及到Vue.js、React或Angular等...
本项目是基于HTML的个人博客网站设计源码,包含1043个文件,其中939个Markdown文件,34个Stylus文件,29个EJS文件,15个YAML文件,9个JavaScript文件,2个JSON文件,2个CSS文件,2个EOT文件,2个TTF文件和2个WOFF...
通过阅读源码,我们可以深入理解其内部实现;查看示例,可以了解如何在实际项目中使用这个模板引擎;阅读文档,则有助于我们掌握其完整功能和最佳实践。 总之,选择一个适合的模板引擎对于Node.js开发至关重要,...
现代Web开发中,可能会用到React、Vue等前端框架,或者直接使用模板引擎如EJS、Pug等。 5. **事件监听**:为了让用户能够交互,源码会添加事件监听器,例如点击日历日期时显示该日期的黄道吉日信息。 6. **兼容性...
本项目为基于Node.js、Express和EJS技术栈构建的外卖后台管理系统源码,共包含2926个文件,涵盖2077个PNG图片、655个JavaScript文件、69个CSS文件等,支持多语言开发环境,包括JavaScript、CSS、PHP和Python。...
该项目是一款基于Node.js、Express、Ejs、MongoDB和RequireJS构建的多用户自适应快速博客系统源码。该系统包含164个文件,涵盖75个JavaScript文件、35个Ejs模板文件、19个CSS样式文件、12个PNG图片文件、5个GIF图片...
本源码项目是基于EJS和Express的MySQL数据库后台管理系统设计,包含44个文件,主要使用JavaScript和CSS编程语言。该系统实现了基本的CRUD后台管理应用,包括用户界面和数据操作功能,适用于快速搭建后台管理系统的...
EJS(嵌入式JavaScript模板)的微型,现代,快速实现。 对几乎替代品, 有一些。 例子 <% for ( let word of locals . items ) { -% > < li>< %= word % >< / li > < % } - %> import { compile...
为了方便替换图片和文字,源码可能使用了模板引擎(如Handlebars、EJS等),通过变量和控制结构来动态生成HTML内容。 6. **数据绑定**: 数据绑定技术(如AngularJS的双向数据绑定)可以使界面与后台数据保持同步...
4. **模板引擎**:源码可能包含模板引擎,如ejs、pug或handlebars,这些模板语言可以帮助开发者快速构造动态页面,通过变量和逻辑控制来实现内容的动态渲染。 5. **响应式设计**:为了适应不同设备和屏幕尺寸,H5...
8. **模板引擎**:如果项目使用了诸如Pug、EJS或Handlebars等模板引擎,它们可以帮助开发者更方便地组织HTML结构,并在其中嵌入动态数据。 9. **版本控制**:项目的源码可能通过Git进行版本控制,每个文件都有其...
4. **模板引擎**:为了实现动态页面渲染,源码可能会集成如Twig、ejs、Jinja2等模板引擎,将后端数据与前端视图结合,生成最终的网页。 5. **安全性**:考虑到网络工作室源码可能涉及用户登录、支付等敏感操作,...
2. **模板引擎**:为了方便内容动态插入和更新,源码可能使用了模板引擎(如Smarty、Jinja2或EJS),允许开发者将静态HTML与动态数据分离,提高开发效率。 3. **数据库集成**:个人业务网站通常需要用户管理、订单...