当前位置:首页 > 生活百科

js网站模板素材(网站模板套用教程)

栏目:生活百科日期:2025-01-21浏览:0

端模板是什么?前端模板该如何实现?很多朋友可能对这个不太了解,那么,下面这篇文章将给大家介绍一下关于前端模板的原理以及简单的实现代码。

前端模板的发展

模板可以说是前端开发最常接触的工具之一。将页面固定不变的内容抽出成模板,服务端返回的动态数据装填到模板中预留的坑位,最后组装成完整的页面html字符串交给浏览器去解析。

模板可以大大提升开发效率,如果没有模板开发人员怕是要手动拼写字符串。

var tpl = &‘&<p&>&’ + user.name + &‘&</p&>&’;

$(&‘body&’).append(tpl);

在近些年前端发展过程中,模板也跟着变化:

1. php模板 JSP模板

早期还没有前后端分离时代,前端只是后端项目中的一个文件夹,这时期的php和java都提供了各自的模板引擎。以JSP为例:java web应用的页面通常是一个个.jsp的文件,这个文件内容是大部分的html以及一些模板自带语法,本质上是纯文本,但是既不是html也不是java。

JSP语法:index.jsp

&<html&>

&<head&>&<title&>Hello World&</title&>&</head&>

&<body&>

Hello World!&<br/&>

&<%

out.println(&“Your IP address is &” + request.getRemoteAddr());

%&>

&</body&>

&</html&>

这个时期的模板引擎,往往是服务端来编译模板字符串,生成html字符串给客户端。

2. handlebar mustache通用模板

09年node发布,JavaScript也可以来实现服务端的功能,这也大大的方便了开发人员。mustache和handlebar模板的诞生方便了前端开发人员,这两个模板均使用JavaScript来实现,从此前端模板既可以在服务端运行,也可以在客户端运行,但是大多数使用场景都是js根据服务端异步获取的数据套入模板,生成新的dom插入页码。 对前端后端开发都非常有利。

mustache语法:index.mustache

&<p&>Username: {{user.name}}&</p&>

{{#if (user.gender === 2)}}

&<p&>女&</p&>

{{/if}}

3. vue中的模板 React中的JSX

接下来到了新生代,vue中的模板写法跟之前的模板有所不同,而且功能更加强大。既可以在客户端使用也可以在服务端使用,但是使用场景上差距非常大:页面往往根据数据变化,模板生成的dom发生变化,这对于模板的性能要求很高。

vue语法:index.vue

&<p&>Username: {{user.name}}&</p&>

&<template v-if=&”user.gender === 2&″&>

&<p&>女&</p&>

&</p&>

模板实现的功能

无论是从JSP到vue的模板,模板在语法上越来越简便,功能越来越丰富,但是基本功能是不能少的:

变量输出(转义/不转义):出于安全考虑,模板基本默认都会将变量的字符串转义输出,当然也实现了不转义输出的功能,慎重使用。条件判断(if else):开发中经常需要的功能。循环变量:循环数组,生成很多重复的代码片段。模板嵌套:有了模板嵌套,可以减少很多重复代码,并且嵌套模板集成作用域。

以上功能基本涵盖了大多数模板的基础功能,针对这些基础功能就可以探究模板如何实现的。

模板实现原理

正如标题所说的,模板本质上都是纯文本的字符串,字符串是如何操作js程序的呢?

模板用法上:

var domString = template(templateString, data);

模板引擎获得到模板字符串和模板的作用域,经过编译之后生成完整的DOM字符串。

大多数模板实现原理基本一致:

模板字符串首先通过各种手段剥离出普通字符串和模板语法字符串生成抽象语法树AST;然后针对模板语法片段进行编译,期间模板变量均去引擎输入的变量中查找;模板语法片段生成出普通html片段,与原始普通字符串进行拼接输出。

其实模板编译逻辑并没有特别复杂,至于vue这种动态绑定数据的模板有时间可以参考文末链接。

快速实现简单的模板

现在以mustache模板为例,手动实现一个实现基本功能的模板。

模板字符串模板:index.txt

&<!DOCTYPE html&>

&<html&>

&<head&>

&<meta charset=&”utf-8&″ /&>

&<meta http-equiv=&”X-UA-Compatible&” content=&”IE=edge&”&>

&<title&>Page Title&</title&>

&<meta name=&”viewport&” content=&”width=device-width, initial-scale=1&″&>

&<link rel=&”stylesheet&” type=&”text/css&” media=&”screen&” href=&”main.css&” /&>

&<script src=&”main.js&”&>&</script&>

&</head&>

&<body&>

&<h1&>Panda模板编译&</h1&>

&<h3&>普通变量输出&</h3&>

&<p&>username: {{common.username}}&</p&>

&<p&>escape:{{common.escape}}&</p&>

&<h3&>不转义输出&</h3&>

&<p&>unescape:{{&&common.escape}}&</p&>

&<h3&>列表输出:&</h3&>

&<ul&>

{{#each list}}

&<li class=&”{{value}}&”&>{{key}}&</li&>

{{/each}}

&</ul&>

&<h3&>条件输出:&</h3&>

{{#if shouldEscape}}

&<p&>escape{{common.escape}}&</p&>

{{else}}

&<p&>unescape:{{&&common.escape}}&</p&>

{{/if}}

&</body&>

&</html&>

模板对应数据:

module.exports = {

common: {

username: &‘Aus&’,

escape: &‘&<p&>Aus&</p&>&’

},

shouldEscape: false,

list: [

{key: &‘a&’, value: 1},

{key: &‘b&’, value: 2},

{key: &‘c&’, value: 3},

{key: &‘d&’, value: 4}

]

};

模板的使用方法:

var fs = require(&“fs&”);

var tpl = fs.readFileSync(&‘./index.txt&’, &‘utf8&’);

var state = require(&‘./test&’);

var Panda = require(&‘./panda&’);

Panda.render(tpl, state)

然后来实现模板:

1. 正则切割字符串

模板引擎获取到模板字符串之后,通常要使用正则切割字符串,区分出那些是静态的字符串,那些是需要编译的代码块,生成抽象语法树(AST)。

// 将未处理过的字符串进行分词,形成字符组tokens

Panda.prototype.parse = function (tpl) {

var tokens = [];

var tplStart = 0;

var tagStart = 0;

var tagEnd = 0;

while (tagStart &>= 0) {

tagStart = tpl.indexOf(openTag, tplStart);

if (tagStart &< 0) break;

// 纯文本

tokens.push(new Token(&‘text&’, tpl.slice(tplStart, tagStart)));

tagEnd = tpl.indexOf(closeTag, tagStart) + 2;

if (tagEnd &< 0) throw new Error(&‘{{}}标签未闭合&’);

// 细分js

var tplValue = tpl.slice(tagStart + 2, tagEnd &– 2);

var token = this.classifyJs(tplValue);

tokens.push(token);

tplStart = tagEnd;

}

// 最后一段

tokens.push(new Token(&‘text&’, tpl.slice(tagEnd, tpl.length)));

return this.parseJs(tokens);

};

这一步分割字符串通常使用正则来完成的,后面检索字符串会大量用到正则方法。

在这一步通常可以检查出模板标签闭合异常,并报错。

2. 模板语法的分类

生成AST之后,普通字符串不需要再管了,最后会直接输出,专注于模板语法的分类。

// 专门处理模板中的js

Panda.prototype.parseJs = function (tokens) {

var sections = [];

var nestedTokens = [];

var conditionsArray = [];

var collector = nestedTokens;

var section;

var currentCondition;

for (var i = 0; i &< tokens.length; i++) {

var token = tokens[i];

var value = token.value;

var symbol = token.type;

switch (symbol) {

case &‘#&’: {

collector.push(token);

sections.push(token);

if(token.action === &‘each&’){

collector = token.children = [];

} else if (token.action === &‘if&’) {

currentCondition = value;

var conditionArray;

collector = conditionArray = [];

token.conditions = token.conditions || conditionsArray;

conditionsArray.push({

condition: currentCondition,

collector: collector

});

}

break;

}

case &‘else&’: {

if(sections.length === 0 || sections[sections.length &– 1].action !== &‘if&’) {

throw new Error(&‘else 使用错误&’);

}

currentCondition = value;

collector = [];

conditionsArray.push({

condition: currentCondition,

collector: collector

});

break;

}

case &‘/&’: {

section = sections.pop();

if (section &&&& section.action !== token.value) {

throw new Error(&‘指令标签未闭合&’);

}

if(sections.length &> 0){

var lastSection = sections[sections.length &– 1];

if(lastSection.action === &‘each&’){

collector = lastSection.chidlren;

} else if (lastSection.action = &‘if&’) {

conditionsArray = [];

collector = nestedTokens;

}

} else {

collector = nestedTokens;

}

break;

}

default: {

collector.push(token);

break;

}

}

}

return nestedTokens;

}

上一步我们生成了AST,这个AST在这里就是一个分词token数组:

[

Token {},

Token {},

Token {},

]

这个token就是每一段字符串,分别记录了token的类型,动作,子token,条件token等信息。

/**

* token类表示每个分词的标准数据结构

*/

function Token (type, value, action, children, conditions) {

this.type = type;

this.value = value;

this.action = action;

this.children = children;

this.conditions = conditions;

}

在这一步要将循环方法中的子token嵌套到对应的token中,以及条件渲染子token嵌套到对应token中。

这步完成之后,一个标准的带有嵌套关系的AST完成了。

3. 变量查找与赋值

现在开始根据token中的变量查找到对应的值,根据相应功能生成值得字符串。

/**

* 解析数据结构的类

*/

function Context (data, parentContext) {

this.data = data;

this.cache = { &‘.&’: this.data };

this.parent = parentContext;

}

Context.prototype.push = function (data) {

return new Context(data, this);

}

// 根据字符串name找到真实的变量值

Context.prototype.lookup = function lookup (name) {

name = trim(name);

var cache = this.cache;

var value;

// 查询过缓存

if (cache.hasOwnProperty(name)) {

value = cache[name];

} else {

var context = this, names, index, lookupHit = false;

while (context) {

// user.username

if (name.indexOf(&‘.&’) &> 0) {

value = context.data;

names = name.split(&‘.&’);

index = 0;

while (value != null &&&& index &< names.length) {

if (index === names.length &– 1) {

lookupHit = hasProperty(value, names[index]);

}

value = value[names[index++]];

}

} else {

value = context.data[name];

lookupHit = hasProperty(context.data, name);

}

if (lookupHit) {

break;

}

context = context.parent;

}

cache[name] = value;

}

return value;

}

为了提高查找效率,采用缓存代理,每次查找到的变量存储路径方便下次快速查找。

不同于JavaScript编译器,模板引擎在查找变量的时候找不到对应变量即终止查找,返回空并不会报错。

4. 节点的条件渲染与嵌套

这里开始讲模板语法token和普通字符串token开始统一编译生成字符串,并拼接成完整的字符串。

// 根据tokens和context混合拼接字符串输出结果

Panda.prototype.renderTokens = function (tokens, context) {

var result = &”;

var token, symbol, value;

for (var i = 0, numTokens = tokens.length; i &< numTokens; ++i) {

value = undefined;

token = tokens[i];

symbol = token.type;

if (symbol === &‘#&’) value = this.renderSection(token, context);

else if (symbol === &‘&&&’) value = this.unescapedValue(token, context);

else if (symbol === &‘=&’) value = this.escapedValue(token, context);

else if (symbol === &‘text&’) value = this.rawValue(token);

if (value !== undefined) result += value;

}

return result;

}

5. 绘制页面

页面字符串已经解析完成,可以直接输出:

Panda.prototype.render = function (tpl, state) {

if (typeof tpl !== &‘string&’) {

return new Error(&‘请输入字符串!&’);

}

// 解析字符串

var tokens = this.cache[tpl] ? tokens : this.parse(tpl);

// 解析数据结构

var context = state instanceof Context ? state : new Context(state);

// 渲染模板

return this.renderTokens(tokens, context);

};

输出页面字符串被浏览器解析,就出现了页面。

以上只是简单的模板实现,并没有经过系统测试,仅供学习使用,源码传送门。成熟的模板引擎是有完整的异常处理,变量查找解析,作用域替换,优化渲染,断点调试等功能的。

总结

前端模板这块能做的东西还很多,很多框架都是集成模板的功能,配合css,js等混合编译生成解析好样式和绑定成功事件的dom。

另外实现模板的方式也有很多,本文的实现方式参考了mustache源码,模板标签内的代码被解析,但是是通过代码片段分类,变量查找的方式来执行的,将纯字符串的代码变成了被解释器执行的代码。

另外向vue这种可以实现双向绑定的模板可以抽空多看一看。

“js网站模板素材(网站模板套用教程)” 的相关文章

500服务器内部错误是什么原因(出错原因和解决方法)

哔哔哔!呼叫汪星总部,汪星驻蓝星指挥部的服务器失灵错误发生!错误发生!请求支援!请求支援!400无效请求BadRequest401未授权Unauthorized...

不花钱的网游有哪些,长期耐玩的网络游戏推荐

白嫖,用在游戏领域,就是不充值,玩免费游戏的玩家行为的戏称。随着功利化游戏时代的到来,越来越多的游戏重视充值,重视促销,很多游戏虽然本身很不错,但如果你不充值,...

创业准备阶段需要做好哪些主要工作,创业所需要的基本内

创业并不是一件简单的事情,但在现如今一个创业时代,整个中国似乎都已经被创业激情点燃。2015年,平均每八分钟诞生一家公司,更令人吃惊的是,中国创业企业的失败率为...

女生说好无聊怎么幽默回复,大神教你正确的回复

不知道你有没有收到过女生“我好无聊”的消息,每次你看到这个消息的时候你是不是有那么一丝头疼呢?这样一条消息,绞尽脑汁都不知道该怎么回。有的时候消息回错了对方就没...

平面设计是什么,平面设计就业前景分析

平面设计专业也可以叫做视觉传达,是属于艺术设计专业。艺术设计专业包括:平面设计专业、环境艺术设计专业、电脑艺术设计专业、装潢艺术设计专业、工业设计(艺术类)、动...

photoshop破解版安装教程(photoshop基础入门知识)

adobephotoshopcc2017于近日正式发布啦,但网上的资源并不是很丰富。为此,小编在这里先跟大家讲解一下cc2015的安装过程,以后再讲解cc201...

2020年创业首选项目有哪些,十大新型创业项目介绍

互联网发展的背景下,创业已经成为了现在大时代下的热门趋势,越来越多的人开始乘着大趋势,自己选择创业。对于有经验的人来说,创业相对来说更容易,只要有充足的资金和广...

win10如何重装系统(只需简单的3步操作就完成)

很多同学觉得重装系统是一件非常头疼的事情,特别是对于女生来说,简直就是残酷啊,那么有没有一篇电脑小白一看就会的系统安装教程呢?今天小鱼系统教你一键重装系统win...

opencv二维码识别算法(基于opencv的图像数字识别)

计算机视觉,我们前期文章分享了很多关于类似这方面的文章,包括人脸识别三部曲,目标检测,目标追踪等,本期文章,我们介绍一下如何使用opencv来进行条形码的检测,...

aso评论优化(详解aso测定)

在这个行业的人都知道换评这件事情,但是现在渠道的评论也便宜了,机刷也可以安排了,为什么还是有那么多的小伙伴每天都手动换评呢,既浪费时间又有人力成本?评论能影响A...