<menu id="ycqsw"></menu><nav id="ycqsw"><code id="ycqsw"></code></nav>
<dd id="ycqsw"><menu id="ycqsw"></menu></dd>
  • <nav id="ycqsw"></nav>
    <menu id="ycqsw"><strong id="ycqsw"></strong></menu>
    <xmp id="ycqsw"><nav id="ycqsw"></nav>
  • js網站模板素材(網站模板套用教程)


    端模板是什么?前端模板該如何實現?很多朋友可能對這個不太了解,那么,下面這篇文章將給大家介紹一下關于前端模板的原理以及簡單的實現代碼。

    前端模板的發展

    模板可以說是前端開發最常接觸的工具之一。將頁面固定不變的內容抽出成模板,服務端返回的動態數據裝填到模板中預留的坑位,最后組裝成完整的頁面html字符串交給瀏覽器去解析。

    模板可以大大提升開發效率,如果沒有模板開發人員怕是要手動拼寫字符串。

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

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

    在近些年前端發展過程中,模板也跟著變化:

    「Js」前端模板是什么?前端模板的原理介紹以及實例,詳細介紹

    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>

    </div>

    模板實現的功能

    無論是從JSP到vue的模板,模板在語法上越來越簡便,功能越來越豐富,但是基本功能是不能少的:

    1. 變量輸出(轉義/不轉義):出于安全考慮,模板基本默認都會將變量的字符串轉義輸出,當然也實現了不轉義輸出的功能,慎重使用。
    2. 條件判斷(if else):開發中經常需要的功能。
    3. 循環變量:循環數組,生成很多重復的代碼片段。
    4. 模板嵌套:有了模板嵌套,可以減少很多重復代碼,并且嵌套模板集成作用域。

    以上功能基本涵蓋了大多數模板的基礎功能,針對這些基礎功能就可以探究模板如何實現的。

    模板實現原理

    正如標題所說的,模板本質上都是純文本的字符串,字符串是如何操作js程序的呢?

    模板用法上:

    var domString = template(templateString, data);

    模板引擎獲得到模板字符串和模板的作用域,經過編譯之后生成完整的DOM字符串。

    大多數模板實現原理基本一致:

    模板字符串首先通過各種手段剝離出普通字符串和模板語法字符串生成抽象語法樹AST;然后針對模板語法片段進行編譯,期間模板變量均去引擎輸入的變量中查找;模板語法片段生成出普通html片段,與原始普通字符串進行拼接輸出。

    其實模板編譯邏輯并沒有特別復雜,至于vue這種動態綁定數據的模板有時間可以參考文末鏈接。

    「Js」前端模板是什么?前端模板的原理介紹以及實例,詳細介紹

    快速實現簡單的模板

    現在以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>

    <h2>普通變量輸出</h2>

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

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

    <h2>不轉義輸出</h2>

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

    <h2>列表輸出:</h2>

    <ul>

    {{#each list}}

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

    {{/each}}

    </ul>

    <h2>條件輸出:</h2>

    {{#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)

    「Js」前端模板是什么?前端模板的原理介紹以及實例,詳細介紹

    然后來實現模板:

    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);

    };

    這一步分割字符串通常使用正則來完成的,后面檢索字符串會大量用到正則方法。

    在這一步通??梢詸z查出模板標簽閉合異常,并報錯。

    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編譯器,模板引擎在查找變量的時候找不到對應變量即終止查找,返回空并不會報錯。

    「Js」前端模板是什么?前端模板的原理介紹以及實例,詳細介紹

    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這種可以實現雙向綁定的模板可以抽空多看一看。

    版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。

    發表評論

    登錄后才能評論
    国产精品区一区二区免费