疫情期间曾一口气入门了一下vue
和webpack
开发SPA单页面应用的套路,当时感觉满嘴香,结果中间停了个把月没怎么玩就忘的差不多了毕竟不是专门做前端的,用脚手架什么的搞webpack
项目还是太复杂了,显然不符合我一向简单粗暴有效的开发风格.
不过已经体验过vue的模板渲染的风格后,再用Flask
+Jinja2
撸的前端一看到页面刷新的闪屏就觉得不舒服,用jquery的ajax做异步渲染又好麻烦,逛了逛gayhub Github发现了jquery-view-engine
这货,尝了下,很是中意!非常适合伪 全栈程序员哈哈,果断安利了~
官方文档:https://jocapc.github.io/jquery-view-engine/usage
ps: jQuery 3.5.1
和1.11.3
测试了下均正常支持,理论上应该不挑jQuery的版本
首先获取插件的js文件,可以从Github上直接下载,其实源代码很简单,一共也就200多行,直接附到文章末尾吧.顺带说一下,这里有个坑,我直接从仓库里面下载压缩后的jquery.view-engine.min.js
不能用,用原始代码没问题.
好了,开始正式上手,一个完整的例子代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="jquery.min.js">script>
<script src="jquery.view-engine.min.js">script>
head>
<body>
<div id="template">
<h1 id="Name">h1>
<label>Description:label>
<textarea name="Desc">textarea>
<ul>
<li class="bind-Tags">li>
ul>
div>
<script>
let data = {
Name: "JOVN",
Desc: "The simplest view engine",
Tags: ["View engine", "JavaScript", "SPA"]
}
$("div#template").view(data);
script>
body>
html>
这里用的是本地定义了一个名为data
的字典对象,关键代码就一句话
$("div#template").view(data);
data
字典中的key会自动和发起调用的DOM元素中的节点进行匹配并进行数据替换,渲染后的结果:
对于使用id
或者name
属性进行定义的DOM元素,直接使用传入数据字典进行映射
对于遍历渲染的列表类元素,需要使用bind-
开头的自定义名称作为class
样式属性进行定位,例子中的元素由于设置了
class=bind-Tags
,所以在渲染的时候就会使用传入字典数据中的Tags
对应数组中的元素进行遍历渲染,可以等价成Jinja2
或其他后端常用模板中的for each
之类的方式.
更多详细的说明可以看官方文档:https://jocapc.github.io/jquery-view-engine/iterators
jquery.view-engine.js
作为一个压缩后只有3k多点的插件,用在全栈开发过程中快速渲染前端页面数据还是相当方便的.
手动压缩后的jquery.view-engine.min.js
文件(内容与下面的一样)
!function(e){e.fn.view=function(t,a){if("object"!=typeof t)throw console.error("Object should be provided as a model instead of "+typeof t),"Object should be provided as a model instead of "+typeof t;function r(t,a,r){var o=e(t).attr("data-key")||r||t.name||t.id,n=e(t).attr("data-text")||"text";for(i=0;i<a.length;i++)e("").attr("value",a[i][o]||a[i].value||a[i]).text(a[i][n]||a[i]).attr("selected",a[i].selected).appendTo(e(t))}function o(a,o,n){var i=a.type||a.tagName;if(null==i&&1==a.length&&(i=a[0].type||a[0].tagName),null!=i)switch(i=i.toLowerCase()){case"text":case"hidden":case"date":case"week":case"month":case"time":case"email":case"url":case"tel":case"color":case"datetime-local":case"number":case"range":case"submit":case"button":e(a).val(o);break;case"radio":o.toString().toLowerCase()==a.value.toLowerCase()&&e(a).attr("checked","checked");break;case"checkbox":o&&e(a).attr("checked",!0).attr("value",!0);break;case"select":case"select-one":case"datalist":"string"==typeof o||"number"==typeof o||"boolean"==typeof o?e(a).val(o):o.constructor==Array?r(a,o,n):console.error("Cannot bind "+o+" to "+i);break;case"select-multiple":var s=a[0];if(null!=a[0].options&&void 0!==a[0].options||(s=a),s.options.length>1)for(var l=o.constructor==Array?o:[o],c=0;c<s.options.length;c++)for(var u=0;u<l.length;u++)s.options[c].selected|=s.options[c].value==l[u];else r(a,o,n);break;case"option":var d=e(a);d.attr("value",o.value||o),d.text(o.text||o.value||o),o.selected&&d.attr("selected",!0);break;case"a":var f=e(a).attr("href");(v=f.indexOf("#"))>1e6?f=f.substr(0,v)+"&"+n+"="+o+f.substr(v):(v=f.indexOf("?"))>0?f+="&"+n+"="+o:f=f+"?"+n+"="+o,e(a).attr("href",f);break;case"img":var p=e(a);if("string"==typeof o){var v,h="",b="";(v=o.indexOf("$"))>0?(h=o.substring(0,v),b=o.substring(v+1)):h=o,p.attr("src",h),p.attr("alt",b)}else p.attr("src",t.src),p.attr("alt",t.alt),p.attr("title",t.title);break;case"form":var g=e(a);if("string"==typeof o||"number"==typeof o){var m=g.attr("action");m.indexOf("{{"+n+"}}")>0?g.attr("action",m.replace("{{"+n+"}}",o)):g.attr("action",m+o)}break;case"textarea":e(a).html(o.toString());break;default:try{properties.rawMarkup?e(a).html(o.toString()):e(a).text(o.toString())}catch(e){console.error(e)}}}var n={onLoading:jQuery.noop,onLoaded:jQuery.noop,rawMarkup:!1};return properties=e.extend(n,a),this.each(function(){!function(e){if(null!=e.data("jquery-view-template")&&""!=e.data("jquery-view-template")){var t=e.data("jquery-view-template");e.html(t)}else t=e.html(),e.data("jquery-view-template",t)}(e(this)),properties.onLoading(),function t(a,r,n){if(null!=a)if(a.constructor==Object){if(r.length>=1&&"OPTION"==r[0].tagName)return void o(r[0],a,n);for(var i in a)if(null!=i&&void 0!==i)if(e(r).hasClass("bind-"+i))t(a[i],r,i);else{var s=".bind-"+i+", #"+i+', [name="'+i+'"]';t(a[i],jQuery(s,r),i)}}else if(a.constructor==Array){if("SELECT"==r.tagName||"DATALIST"==r.tagName||1==r.length&&("SELECT"==r[0].tagName||"DATALIST"==r[0].tagName))return void o(r,a,n);for(var l=e(r).clone(!0),c=a.length-1;c>0;c--){var u=l.clone(!0).insertAfter(e(r));t(a[c],u,n)}t(a[0],e(r),n)}else if(r.length>0)for(c=0;c<r.length;c++)o(r[c],a,n);else o(r,a,n)}(t,this),properties.onLoaded()})}}(jQuery);
jquery.view-engine.js
源文件
/*
* File: jquery.view-engine.js
* Version: 1.1.1
* Author: Jovan Popovic
*
* Copyright 2017 Jovan Popovic, all rights reserved.
*
* This source file is free software, under either the GPL v2 license or a
* BSD style license, as supplied with this software.
*
* This source file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
* This file contains implementation of the JQuery templating engine that load JSON
* objects into the HTML code. It is based on Alexandre Caprais notemplate plugin
* with several enchancements that are added to this plugin.
*/
(function ($) {
$.fn.view = function (obj, options) {
if (!(typeof obj == "object")) {
console.error("Object should be provided as a model instead of " + (typeof obj));
throw "Object should be provided as a model instead of " + (typeof obj);
}
function loadSelect(nSelect, aoValues, name) {
///
///Load options into the select list
///
///Select list
///Array of object containin the options
///Name of the select list
var key = $(nSelect).attr("data-key") || name || nSelect.name || nSelect.id;
var text = $(nSelect).attr("data-text") || "text";
for (i = 0; i < aoValues.length; i++) {
$("")
.attr("value", aoValues[i][key] || aoValues[i].value || aoValues[i])
.text(aoValues[i][text] || aoValues[i])
.attr("selected", aoValues[i].selected)
.appendTo($(nSelect));
}
}
function setElementValue(element, value, name) {
var type = element.type || element.tagName;
if (type == null && element.length == 1) {
type = element[0].type || element[0].tagName; //select returns undefined if called directly
}
if (type==null)
return;
type = type.toLowerCase();
switch (type) {
case 'text':
case 'hidden':
case 'date':
case 'week':
case 'month':
case 'time':
case 'email':
case 'url':
case 'tel':
case 'color':
case 'datetime-local':
case 'number':
case 'range':
case 'submit':
case 'button':
$(element).val(value);
break;
case 'radio':
if (value.toString().toLowerCase() == element.value.toLowerCase()) {
$(element).attr("checked", "checked");
}
break;
case 'checkbox':
if (value) {
$(element).attr("checked", true).attr("value",true);
}
break;
case 'select':
case 'select-one':
case 'datalist':
if (typeof value == "string" || typeof value == "number" || typeof value == "boolean") {
$(element).val(value);
} else if (value.constructor == Array) {
loadSelect(element, value, name);
} else {
console.error("Cannot bind " + value + " to " + type);
}
break;
case 'select-multiple':
var select = element[0];
if (element[0].options == null || typeof (element[0].options) == "undefined") {
select = element;
}
if (select.options.length > 1) {
//If select list is not empty use values array to select options
var values = value.constructor == Array ? value : [value];
for (var i = 0; i < select.options.length; i++) {
for (var j = 0; j < values.length; j++) {
select.options[i].selected |= select.options[i].value == values[j];
}
}
} else {
//ELSE: Instead of selecting values use values array to populate select list
loadSelect(element, value, name);
}
break;
case 'option':
var $option = $(element);
// value can be object {value,text,selected} or scalar
$option.attr("value", value.value || value);
$option.text(value.text || value.value || value);
if (value.selected)
$option.attr("selected", true);
break;
case 'a':
var href = $(element).attr("href");
var iPosition = href.indexOf('#');
if (iPosition > 1000000) {
href = href.substr(0, iPosition) + '&' + name + '=' + value + href.substr(iPosition)
} else {
iPosition = href.indexOf('?');
if (iPosition > 0) // if parameters in the URL exists add new pair using &
href += '&' + name + '=' + value;
else//otherwise attach the name=value pair to the URL
href = href + '?' + name + '=' + value;
}
$(element).attr("href", href);
break;
case 'img':
var $img = $(element);
if (typeof value == "string") {
//Assumption is that value is in the HREF$ALT format
var iPosition = value.indexOf('$');
var src = "";
var alt = "";
if (iPosition > 0) {
src = value.substring(0, iPosition);
alt = value.substring(iPosition + 1);
}
else {
src = value;
}
$img.attr("src", src);
$img.attr("alt", alt);
} else {
$img.attr("src", obj.src);
$img.attr("alt", obj.alt);
$img.attr("title", obj.title);
}
break;
case 'form':
{
var $form = $(element);
if (typeof value == "string" || typeof value == "number") {
var action = $form.attr("action");
if (action.indexOf("{{" + name + "}}") > 0) {
$form.attr("action", action.replace("{{" + name + "}}", value));
} else {
$form.attr("action", action + value);
}
}
break;
}
case 'textarea':
$(element).html(value.toString());
break;
default:
try {
if(properties.rawMarkup)
$(element).html(value.toString());
else
$(element).text(value.toString());
} catch (exc) {
console.error(exc);
}
}
}
function bind(data, domNode, name) {
if (data == null)
return;
if (data.constructor == Object) {
if (domNode.length >= 1 && domNode[0].tagName == "OPTION")
{
// Shortcut: if tag is OPTION and data is object - set the value of OPTION
setElementValue(domNode[0], data, name);
return;
}
else {
for (var prop in data) {
if (prop == null || typeof prop == "undefined")
continue;
else {
if( $(domNode).hasClass("bind-" + prop)){
// Current DOM element has a class that might to be bound to the property
// try to bing it.
// Supports {a:{b:{c:17}}}
bind(data[prop], domNode, prop);
}
else {
//Find an element with class, id, or name that matches the property name
var sSelector = ".bind-" + prop + ", #" + prop + ', [name="' + prop + '"]';
bind(data[prop], jQuery(sSelector, domNode), prop);
}
}
}
}
}
else if (data.constructor == Array) {
if ( domNode.tagName == "SELECT" || domNode.tagName == "DATALIST"
|| domNode.length == 1 && (domNode[0].tagName == "SELECT" || domNode[0].tagName == "DATALIST")
) {
setElementValue(domNode, data, name);
return;
} else
{
// Clone the element that will be used as template (e.g. or )
var template = $(domNode).clone(true);
for (var i = data.length - 1; i > 0 ; i--){
// Put the clone of template on the second place and puth other after.
var target = template.clone(true).insertAfter( $(domNode));
// Bind i-th object into the new placeholder.
bind(data[i], target, name);
}
// Bind 0-th object into the element used as a prototype (the first one).
bind(data[0], $(domNode), name);
}
} // End Array
else {
// Scalar value
if (domNode.length > 0) {
// If someone needs to bind scalar into multiple elements:
for (var i = 0; i < domNode.length; i++)
setElementValue(domNode[i], data, name);
}
else {
setElementValue(domNode, data, name);
}
}
} //function bind() ends
function init(placeholder) {
if (placeholder.data("jquery-view-template") != null && placeholder.data("jquery-view-template") != "") {
var template = placeholder.data("jquery-view-template");
placeholder.html(template);
} else {
var template = placeholder.html()
placeholder.data("jquery-view-template", template);
}
}
var defaults = {
onLoading: jQuery.noop,
onLoaded: jQuery.noop,
rawMarkup: false
};
properties = $.extend(defaults, options);
return this.each(function () {
init($(this));
properties.onLoading();
bind(obj, this);
properties.onLoaded();
});
};
})(jQuery);
你可能感兴趣的:(前端开发,ajax,jquery,渲染,引擎)