วิธีการใช้ underscore.js เป็นเครื่องมือแม่แบบ?


262

ฉันพยายามเรียนรู้เกี่ยวกับจาวาสคริปต์ใหม่ ๆ ในฐานะภาษาเซิร์ฟเวอร์และภาษาที่ใช้งานได้ ไม่กี่วันที่ผ่านมาฉันได้ยินเกี่ยวกับ node.js และแสดงกรอบงาน จากนั้นฉันเห็น underscore.js เป็นชุดฟังก์ชันยูทิลิตี้ ผมเห็นคำถามนี้ใน StackOverflow มันบอกว่าเราสามารถใช้ underscore.js เป็นเครื่องมือแม่แบบ ใครรู้บทเรียนที่ดีเกี่ยวกับวิธีการใช้ underscore.js สำหรับการสร้างเทมเพลตโดยเฉพาะอย่างยิ่งสำหรับผู้ที่มีประสบการณ์น้อยกับจาวาสคริปต์ขั้นสูง ขอบคุณ


12
ในการป้องกันของ "ลุค" คู่มือฉบับปรับปรุงอย่างน้อยที่สุดก็เร็วเท่าที่อาจไม่มีการใช้งานขั้นสูง
Shanimal

ฉันเพิ่งตอบคำถามที่คล้ายกันซึ่งจะเป็นประโยชน์ต่อคำถามของคุณเช่นกัน stackoverflow.com/questions/28136101/retrieve-column-in-parse/ …
jeffdill2

คำตอบ:


475

ทุกสิ่งที่คุณจำเป็นต้องรู้เกี่ยวกับแม่แบบขีดเป็นที่นี่ 3 ข้อที่ควรทราบ:

  1. <% %> - เพื่อรันโค้ดบางอย่าง
  2. <%= %> - เพื่อพิมพ์ค่าในเทมเพลต
  3. <%- %> - เพื่อพิมพ์ค่าบางค่า HTML ที่หลบหนี

นั่นคือทั้งหมดที่เกี่ยวกับมัน

ตัวอย่างง่ายๆ:

var tpl = _.template("<h1>Some text: <%= foo %></h1>");

จากนั้นtpl({foo: "blahblah"})จะถูกแสดงผลเป็นสตริง<h1>Some text: blahblah</h1>


55
ผมไม่เข้าใจว่าทำไมใครจะลงคะแนนเสียงนี้มันเป็นคำตอบที่ยอมรับและชี้ไปยังคำแนะนำบนหน้าแรกของโครงการก็คลาสสิก "สอนให้คนรู้จักปลา"
Jon z

1
ฉันคิดว่าพวกเขาจะลงคะแนนทำให้เอกสารที่พวกเขาให้มีน้อยมากในการผสม <% และ <% = เกินกว่าตัวอย่างที่เป็นเอกเทศและวิธีการเปลี่ยนจาก <% = เป็นพิมพ์ () เปลี่ยนรูปแบบนั้น นอกจากนี้เมื่อใช้ 'สอดแทรก' มีพฤติกรรมแปลก ๆ ที่อาจทำให้ฉากพร้อมคำอธิบายเพิ่มเติมอีกเล็กน้อย อีกครั้งซึ่งไม่ได้ให้ แม้ว่าฉันจะเห็นด้วยมันเป็นสิ่งที่โง่เขลาลงคะแนน
QueueHammer

8
3. <% -%> - เพื่อพิมพ์ค่าบางอย่างด้วย HTML ที่หลบหนี
LeeGee

13
ฉันไม่ได้ลงคะแนน แต่คำตอบของคุณไม่ได้ทำอะไรเลย (นอกเหนือจากการเสนอลิงก์) เพื่ออธิบายวิธีใช้ underscore.js เป็นเครื่องมือสร้างเทมเพลต คำตอบของคุณจะให้ "สูตรโกง" ที่รวดเร็วสำหรับผู้ที่ได้รับมาแล้ว แต่โดยตัวของมันเองมันไม่ใช่คำตอบสำหรับคำถาม ฉันประหลาดใจว่ามันมี upvotes มากเท่าที่จะทำได้
Zach Lysobey

1
-1, เอกสารมีข้อบกพร่องในหลายประการ เกือบจะแน่ใจว่าผู้ใช้มาที่นี่หลังจากปรึกษาเอกสาร คำตอบไม่ดี
Matt Parkins

198
<!-- Install jQuery and underscore -->

<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="http://documentcloud.github.com/underscore/underscore-min.js"></script>

<!-- Create your template -->
<script type="foo/bar" id='usageList'>
<table cellspacing='0' cellpadding='0' border='1' >
    <thead>
      <tr>
        <th>Id</th>
        <th>Name</th>
      </tr>
    </thead>
    <tbody>
      <%
        // repeat items 
        _.each(items,function(item,key,list){
          // create variables
          var f = item.name.split("").shift().toLowerCase();
      %>
        <tr>
          <!-- use variables -->
          <td><%= key %></td>
          <td class="<%= f %>">
            <!-- use %- to inject un-sanitized user input (see 'Demo of XSS hack') -->
            <h3><%- item.name %></h3>
            <p><%- item.interests %></p>
          </td>
        </tr>
      <%
        });
      %>
    </tbody>
  </table>
</script>

<!-- Create your target -->

<div id="target"></div>

<!-- Write some code to fetch the data and apply template -->

<script type="text/javascript">
  var items = [
    {name:"Alexander", interests:"creating large empires"},
    {name:"Edward", interests:"ha.ckers.org <\nBGSOUND SRC=\"javascript:alert('XSS');\">"},
    {name:"..."},
    {name:"Yolando", interests:"working out"},
    {name:"Zachary", interests:"picking flowers for Angela"}
  ];
  var template = $("#usageList").html();
  $("#target").html(_.template(template,{items:items}));
</script>
  • JsFiddleขอบคุณ @PHearst!
  • JsFiddle (ล่าสุด)
  • รายการJsFiddleจัดกลุ่มตามตัวอักษรตัวแรก (ตัวอย่างที่ซับซ้อนด้วยภาพ / การเรียกฟังก์ชันแม่แบบย่อย) แยกไว้! มีระเบิด ...
  • JsFiddle Demo ของแฮ็ค XSS ที่บันทึกโดย @tarun_telang ด้านล่าง
  • JsFiddleหนึ่งวิธีที่ไม่ได้มาตรฐานในการทำเทมเพลตย่อย

17
ขอบคุณที่ใช้แท็กสคริปต์ "text / html" อย่างชัดเจนในตัวอย่างของคุณ ฉันใหม่เพื่อ underscore.js และฉันอ่านเอกสารอย่างผิด ๆ อย่างไม่มีความสุข - ดีใจที่ได้รู้ว่า templateString ไม่จำเป็นต้องเขียนแบบอินไลน์เสมอไป
aschyiel

เทมเพลตไม่จริงtext/htmlดังนั้นการพูดtype="text/html"เป็นการโกหกการโกหกอาจทำให้เกิดปัญหาได้ text/x-underscoreคุณต้องการจะดีกว่ากับประเภทที่ถูกต้องเช่น
mu สั้นเกินไป

6
mu ฉันคิดว่ามันดีที่จะชี้ให้เห็นว่ามันไม่สำคัญ หน้ามันเถอะอะไรก็ตามที่คุณพูดว่าเป็นเรื่องโกหก text / x-underscore เป็นการโกหกที่ยิ่งใหญ่กว่าเพราะฉันใช้ lodash, lol :) ใน JsFiddle ครั้งล่าสุดที่ฉันเพิ่มtype="foo/bar"เพราะฉันต้องการให้ทุกคนรู้ว่ามันไม่สำคัญตราบใดที่เบราว์เซอร์ / เซิร์ฟเวอร์ไม่รู้จักและลอง ทำอะไรกับมัน ตั้งแต่ html ที่ไม่ได้เป็นประเภทของสคริปต์ผมรู้สึกค่อนข้างปลอดภัยกับ text / html (จอห์น Resig ใช้มัน) foo / บาร์ผลงานได้เป็นอย่างดี :)
Shanimal

4
ผู้คนไม่เห็นด้วยกับฉันตลอดเวลาฉันพยายามอย่างดีที่สุดที่จะไม่ใช้มันเป็นการส่วนตัว (แม้จะเป็นเรื่องส่วนตัว :) ฉันถูกเผาโดยผลข้างเคียงที่ไม่ได้ตั้งใจจากความสะเพร่าเล็กน้อยซ้ำแล้วซ้ำอีกดังนั้นนิสัยของฉันคือความผิดพลาดทางด้านความเข้มงวด รายละเอียดประเภท MIME จริง ๆ แล้วสำรอง*/x-*ประเภทสำหรับการใช้ "ทำขึ้น" ฉันไม่คิดว่าจะมีtext/underscoreประเภทใดในการลงทะเบียนอย่างเป็นทางการดังนั้นฉันจึงใช้text/x-underscoreเพราะฉันหวาดระแวงและพวกเขาออกไปรับฉันจริงๆ
mu สั้นเกินไป

1
แจ้งให้ทราบว่าการสาธิต XSS ใช้งานไม่ได้อีกต่อไปเนื่องจากเบราว์เซอร์ปฏิเสธที่จะเรียกใช้ JS โดยใช้ชนิดแฟ้มที่ไม่ถูกต้อง
nickford

94

ในรูปแบบที่ง่ายที่สุดคุณจะชอบ:

var html = _.template('<li><%= name %></li>', { name: 'John Smith' });
//html is now '<li>John Smith</li>'   

หากคุณกำลังจะใช้แม่แบบสองสามครั้งคุณจะต้องรวบรวมมันเพื่อให้เร็วขึ้น:

var template = _.template('<li><%= name %></li>');

var html = [];
for (var key in names) {
    html += template({ name: names[i] });
}

console.log(html.join('')); //Outputs a string of <li> items

ฉันชอบไวยากรณ์สไตล์หนวด คุณสามารถปรับเครื่องหมายโทเค็นเทมเพลตเพื่อใช้เครื่องหมายปีกกาคู่:

_.templateSettings.interpolate = /\{\{(.+?)\}\}/g;

var template = _.template('<li>{{ name }}</li>');

เคล็ดลับการแก้ไขหนวดช่วยฉันในขณะที่ใช้มุมมอง express3 ซึ่งแสดงผลโดยใช้ ejs ขอบคุณ!
micrub

ในการใช้เทมเพลตจากมุมมองคุณสามารถมีสิ่งต่อไปนี้ในมาร์กอัปหน้าของคุณ: <script type = "text / template" id = "my-template"> <div> <% - ชื่อ%> </div> </ script > จากนั้นทำสิ่งต่อไปนี้ใน JS ของคุณ: var html = _.template ($ ('# my-template'). html (), {name: "John Smith"});
Gaurav Gupta

2
@evilcelery - interpolateเคล็ดลับของคุณไม่ทำงาน แต่สิ่งนี้ทำได้:_.templateSettings = { interpolate: /\{\{\=(.+?)\}\}/g, escape: /\{\{\-(.+?)\}\}/g, evaluate: /\{\{(.+?)\}\}/g };
vsync

28

เอกสารสำหรับการสร้างเทมเพลตเป็นบางส่วนฉันดูแหล่งที่มา

_.templateฟังก์ชั่นมี 3 ข้อโต้แย้ง:

  1. ข้อความสตริง: สตริงแม่แบบ
  2. ข้อมูลวัตถุ: ข้อมูลการประเมินผล
  3. การตั้งค่าวัตถุ: การตั้งค่าท้องถิ่น_.templateSettingsเป็นวัตถุการตั้งค่าร่วม

หากไม่ได้รับข้อมูล (หรือ null) ฟังก์ชันการแสดงผลจะถูกส่งคืนมากกว่า มันมี 1 ข้อโต้แย้ง:

  1. ข้อมูลวัตถุ: เหมือนกับข้อมูลข้างต้น

มี 3 รูปแบบ regex และพารามิเตอร์แบบคงที่ 1 ในการตั้งค่า:

  1. RegExp ประเมินผล : "<% code%>" ในสตริงแม่แบบ
  2. RegExp แก้ไข : "<% = code%>" ในสตริงแม่แบบ
  3. RegExp หลบหนี : "<% - รหัส%>"
  4. ตัวแปรสตริง: เป็นทางเลือกชื่อของพารามิเตอร์ข้อมูลในสตริงแม่แบบ

รหัสในส่วนประเมินจะถูกประเมินเพียง คุณสามารถเพิ่มสตริงจากส่วนนี้ด้วยคำสั่ง__p + = "mystring"ไปยังเทมเพลตที่ประเมิน แต่ไม่แนะนำ (ไม่ได้เป็นส่วนหนึ่งของส่วนต่อประสานเทมเพลต) ใช้ส่วนแทรกแทน ส่วนประเภทนี้มีไว้สำหรับเพิ่มบล็อกเช่นถ้าหรือสำหรับแม่แบบ

ผลลัพธ์ของรหัสในส่วนการแก้ไขจะถูกเพิ่มไปยังเทมเพลตที่ประเมิน ถ้า null กลับมาสตริงที่ว่างเปล่าจะถูกเพิ่ม

ส่วนการหลบหนีหนีออกมาจาก html ด้วย_.escapeบนค่าส่งคืนของรหัส ดังนั้นของที่คล้ายกันกว่า_.escape (code)ในตีความส่วน แต่มันหนีกับ\ตัวละครช่องว่างเช่น\ nก่อนที่มันจะผ่านรหัสไปยัง_.escape ฉันไม่รู้ว่าทำไมถึงมีความสำคัญมันอยู่ในรหัส แต่มันทำงานได้ดีกับการสอดแทรกและ_.escape - ซึ่งไม่ได้หนีจากอักขระช่องว่าง - เช่นกัน

โดยค่าเริ่มต้นพารามิเตอร์ข้อมูลจะถูกส่งผ่านโดยคำสั่งwith (data) {... }แต่การประเมินประเภทนี้ช้ากว่าการประเมินด้วยตัวแปรที่ระบุชื่อมาก ดังนั้นการตั้งชื่อข้อมูลด้วยพารามิเตอร์ตัวแปรจึงเป็นสิ่งที่ดี ...

ตัวอย่างเช่น:

var html = _.template(
    "<pre>The \"<% __p+=_.escape(o.text) %>\" is the same<br />" +
        "as the  \"<%= _.escape(o.text) %>\" and the same<br />" +
        "as the \"<%- o.text %>\"</pre>",
    {
        text: "<b>some text</b> and \n it's a line break"
    },
    {
        variable: "o"
    }
);

$("body").html(html);

ผล

The "<b>some text</b> and 
 it's a line break" is the same
as the "<b>some text</b> and 
 it's a line break" and the same
as the "<b>some text</b> and 
 it's a line break"

คุณสามารถหาตัวอย่างเพิ่มเติมได้ที่นี่วิธีใช้เทมเพลตและแทนที่การตั้งค่าเริ่มต้น: http://underscorejs.org/#template

โดยการโหลดแม่แบบคุณมีตัวเลือกมากมาย แต่ในตอนท้ายคุณจะต้องแปลงแม่แบบเป็นสตริงเสมอ คุณสามารถกำหนดให้เป็นสตริงปกติเช่นตัวอย่างด้านบนหรือคุณสามารถโหลดจากแท็กสคริปต์และใช้ฟังก์ชัน. html ()ของ jquery หรือคุณสามารถโหลดจากไฟล์แยกต่างหากด้วยปลั๊กอิน tpl of require.js .

อีกทางเลือกหนึ่งในการสร้างต้นไม้ดอมด้วยลาโคนิกแทนการสร้างเทมเพลต


21

ฉันยกตัวอย่างง่ายๆ

1)

var data = {site:"mysite",name:"john",age:25};
var template = "Welcome you are at <%=site %>.This has been created by <%=name %> whose age is <%=age%>";
var parsedTemplate = _.template(template,data);
console.log(parsedTemplate); 

ผลที่ได้ก็คือ

Welcome you are at mysite.This has been created by john whose age is 25.

2) นี่คือเทมเพลต

   <script type="text/template" id="template_1">
       <% _.each(items,function(item,key,arr) { %>
          <li>
             <span><%= key %></span>
             <span><%= item.name %></span>
             <span><%= item.type %></span>
           </li>
       <% }); %>
   </script>

นี่คือ html

<div>
  <ul id="list_2"></ul>
</div>

นี่คือรหัส javascript ซึ่งมีวัตถุ json และวางแม่แบบลงใน html

   var items = [
       {
          name:"name1",
          type:"type1"
       },
       {
          name:"name1",
          type:"type1"
       },
       {
          name:"name1",
          type:"type1"
       },
       {
          name:"name1",
          type:"type1"
       },
       {
          name:"name1",
          type:"type1"
       } 
   ];
  $(document).ready(function(){
      var template = $("#template_1").html();
      $("#list_2").html(_.template(template,{items:items}));
  });


14

ด้วยด่วนมันง่ายมาก ทุกสิ่งที่คุณต้องการคือการใช้โมดูลรวมบนโหนดดังนั้นคุณต้องติดตั้ง:

npm install consolidate --save

จากนั้นคุณควรเปลี่ยนเอ็นจิ้นเริ่มต้นเป็นเทมเพลต html ตามนี้:

app.set('view engine', 'html');

ลงทะเบียนเอ็นจินเทมเพลตขีดล่างสำหรับส่วนขยาย html:

app.engine('html', require('consolidate').underscore);

มันจบแล้ว !

ตอนนี้โหลดตัวอย่างเช่นเทมเพลตชื่อ 'index.html':

res.render('index', { title : 'my first page'});

บางทีคุณอาจจะต้องติดตั้งโมดูลขีดล่าง

npm install underscore --save

ฉันหวังว่านี่จะช่วยคุณได้!


12

ฉันต้องการแบ่งปันการค้นพบที่สำคัญมากกว่านี้อีกครั้ง

การใช้ <% = variable => จะส่งผลให้เกิดช่องโหว่การเขียนสคริปต์ข้ามไซต์ ดังนั้นจึงปลอดภัยกว่าที่จะใช้ <% - variable -> แทน

เราต้องแทนที่ <% = ด้วย <% - เพื่อป้องกันการโจมตีสคริปต์ข้ามไซต์ ไม่แน่ใจว่าสิ่งนี้จะส่งผลกระทบต่อประสิทธิภาพหรือไม่


2
+1 ฉันเพิ่มบันทึกเกี่ยวกับ XSS ลงในตัวอย่างของฉัน นี่เป็นจุดที่ดีมากในการฉีดข้อมูลผู้ใช้ที่ไม่ได้รับอนุญาตเข้าสู่หน้าเว็บ ไม่ว่าจะผ่านเทมเพลตเอ็นจิ้นหรือแม้แต่ $ .html ()
Shanimal

1

Lodash ยังเหมือนเดิมเขียนสคริปต์เป็นดังนี้:

<script type="text/template" id="genTable">
<table cellspacing='0' cellpadding='0' border='1'>
        <tr>
            <% for(var prop in users[0]){%>
            <th><%= prop %> </th>
            <% }%>
        </tr>
        <%_.forEach(users, function(user) { %>
            <tr>
                 <% for(var prop in user){%>
                    <td><%= user[prop] %> </td>
                <% }%>

            </tr>
        <%})%>
</table>

ตอนนี้เขียน JS ง่ายๆดังนี้:

var arrOfObjects = [];
for (var s = 0; s < 10; s++) {
    var simpleObject = {};
    simpleObject.Name = "Name_" + s;
    simpleObject.Address = "Address_" + s;
    arrOfObjects[s] = simpleObject;
}
var theObject = { 'users': arrOfObjects }
var compiled = _.template($("#genTable").text());
var sigma = compiled({ 'users': myArr });

$(sigma).appendTo("#popup");

โดยที่ popoup คือ div ที่คุณต้องการสร้างตาราง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.