Ractive.js - เว็บแอปที่ใช้งานง่าย

เผยแพร่แล้ว: 2022-03-11

ในภูมิทัศน์ที่แพร่หลายอย่างรวดเร็วของกรอบงานและไลบรารี JavaScript ในปัจจุบัน การเลือกสิ่งที่คุณต้องการใช้เป็นฐานในการพัฒนาของคุณอาจเป็นเรื่องยากทีเดียว ท้ายที่สุด เมื่อคุณใช้เฟรมเวิร์กเฉพาะแล้ว การย้ายโค้ดของคุณไปใช้เฟรมเวิร์กอื่นถือเป็นงานที่ไม่สำคัญ ซึ่งคุณอาจไม่มีเวลาหรืองบประมาณในการดำเนินการเลย

แล้วทำไม Ractive.js?

ไม่เหมือนกับเครื่องมืออื่นๆ ที่สร้าง HTML แบบเฉื่อย Ractive จะแปลงเทมเพลตเป็นพิมพ์เขียวสำหรับแอปที่มีการโต้ตอบโดยค่าเริ่มต้น และในขณะที่เราสามารถโต้แย้งได้อย่างแน่นอนว่าการมีส่วนร่วมของ Ractive นั้นมีวิวัฒนาการมากกว่าการปฏิวัติ แต่คุณค่าของมันก็ยังมีความสำคัญ

สิ่งที่ทำให้ Ractive มีประโยชน์มากคือมันมีความสามารถที่ทรงพลัง แต่ทำในลักษณะที่นักพัฒนาใช้งานได้ง่าย ยิ่งไปกว่านั้น มันค่อนข้างสง่างาม รวดเร็ว ไม่เกะกะ และมีขนาดเล็ก

ในบทความนี้ เราจะอธิบายขั้นตอนการสร้างแอปการค้นหา Ractive แบบง่ายๆ โดยจะสาธิตคุณลักษณะหลักบางประการของ Ractive และวิธีที่แอปนี้จะช่วยให้เว็บแอปและการพัฒนาง่ายขึ้น

Ractive.js และเว็บแอป

Ractive.js คืออะไร?

เดิม Ractive ถูกสร้างขึ้นเพื่อจัดการกับปัญหาการผูกข้อมูลในรูปแบบที่สวยงามยิ่งขึ้น ในตอนท้ายนั้น จะใช้เทมเพลตและแปลงเป็นการแสดง DOM เสมือนจริงแบบน้ำหนักเบา เพื่อที่ว่าเมื่อข้อมูลเปลี่ยนแปลง DOM จริงจะได้รับการอัปเดตอย่างชาญฉลาดและมีประสิทธิภาพ

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

คุณสมบัติหลัก เช่น การเชื่อมโยงสองทาง แอนิเมชัน และการสนับสนุน SVG มีให้ตั้งแต่แกะกล่อง และสามารถเพิ่มฟังก์ชันที่กำหนดเองได้อย่างง่ายดายผ่านปลั๊กอิน

ในขณะที่เครื่องมือและกรอบงานบางอย่างบังคับให้คุณเรียนรู้คำศัพท์ใหม่และจัดโครงสร้างแอปของคุณในลักษณะเฉพาะ Ractive ทำงานให้คุณ ไม่ใช่ในทางกลับกัน นอกจากนี้ยังทำงานร่วมกับไลบรารีอื่นๆ ได้เป็นอย่างดี

แอปตัวอย่างของเรา

แอปตัวอย่างของเราจะใช้เพื่อค้นหาฐานข้อมูล Toptal ของนักพัฒนาตามทักษะ แอพของเราจะมีสองมุมมอง:

  • ค้นหา: รายการทักษะพร้อมช่องค้นหาแบบอินไลน์
  • ผลลัพธ์: มุมมองทักษะรวมถึงรายชื่อนักพัฒนา

สำหรับนักพัฒนาแต่ละราย เราจะแสดงชื่อ รูปภาพ คำอธิบายสั้นๆ และรายการทักษะ (แต่ละทักษะจะลิงก์ไปยังมุมมองทักษะที่เกี่ยวข้อง)

(หมายเหตุ: ลิงก์ไปยังอินสแตนซ์การทำงานออนไลน์ของแอปพลิเคชันและที่เก็บซอร์สโค้ดมีอยู่ที่ส่วนท้ายของบทความนี้)

เพื่อรักษาจุดสนใจหลักของเราในเฟรมเวิร์ก Ractive เราจะใช้การทำให้เข้าใจง่ายจำนวนหนึ่งซึ่งปกติแล้ว ไม่ ควรทำในเวอร์ชันที่ใช้งานจริง:

  • ธีมเริ่มต้น เราจะใช้ Bootstrap กับธีมเริ่มต้นสำหรับการจัดสไตล์ แทนที่จะปรับแต่งธีมให้เหมาะกับสไตล์แอปของคุณ
  • การพึ่งพาอาศัยกัน เราจะเพิ่มการพึ่งพาของเราเป็นสคริปต์แยกที่กำหนดตัวแปรส่วนกลาง (แทนที่จะใช้โมดูล ES6 หรือ CommonJS หรือ AMD พร้อมตัวโหลดที่เหมาะสมสำหรับการพัฒนา และขั้นตอนการสร้างสำหรับการผลิต)
  • ข้อมูลคงที่ เราจะใช้ข้อมูลแบบคงที่ที่ฉันเตรียมโดยการขูดหน้าสาธารณะบนไซต์ Toptal
  • ไม่มีการกำหนดเส้นทางฝั่งไคลเอ็นต์ ซึ่งหมายความว่า URL จะยังคงเหมือนเดิมเมื่อเราสลับไปมาระหว่างมุมมองต่างๆ คุณไม่ควรทำเช่นนั้นสำหรับ SPA แม้ว่าอาจจะใช้ได้สำหรับส่วนประกอบแบบโต้ตอบขนาดเล็กบางอย่าง Ractive ไม่มีการติดตั้งเราเตอร์ในตัว แต่สามารถใช้ได้กับเราเตอร์บุคคลที่สาม ดังที่แสดงในตัวอย่างนี้
  • เทมเพลตที่กำหนดไว้ภายในแท็กสคริปต์ใน HTML นี่ไม่ใช่ความคิดที่ดีเสมอไป โดยเฉพาะอย่างยิ่งสำหรับแอปพลิเคชันขนาดเล็ก และมีข้อดีบางประการ (ง่าย และคุณสามารถประมวลผลเทมเพลตฝั่งไคลเอ็นต์เหล่านี้ร่วมกับเทมเพลตฝั่งเซิร์ฟเวอร์ของคุณ เช่น สำหรับการทำให้เป็นสากล) แต่สำหรับแอปที่ใหญ่กว่า คุณจะได้รับประโยชน์จากการคอมไพล์ล่วงหน้า (หรือที่รู้จักว่า การแยกวิเคราะห์เทมเพลตล่วงหน้าไปจนถึงการแสดง JS ภายใน

มาเริ่มกันเลยกับเว็บแอพ

ตกลง มาเริ่มสร้างแอปกันเถอะ เราจะทำในลักษณะวนซ้ำ โดยเพิ่มคุณสมบัติเล็กๆ ทีละตัว และสำรวจแนวคิดเมื่อเราพบเห็น

เริ่มต้นด้วยการสร้างโฟลเดอร์ที่มีสองไฟล์อยู่ภายใน: index.html และ script.js แอปของเราจะใช้งานได้ง่ายมากและจะทำงานจากโปรโตคอล file:// เพื่อหลีกเลี่ยงความจำเป็นในการเริ่มต้นเซิร์ฟเวอร์การพัฒนา (แม้ว่าคุณจะทำได้หากต้องการ)

หน้าค้นหา

เราจะเริ่มต้นด้วยการใช้หน้าค้นหาซึ่งผู้ใช้สามารถเลือกทักษะเพื่อค้นหานักพัฒนาที่ตรงกันในฐานข้อมูล Toptal

โครงกระดูก HTML

เริ่มต้นด้วยหน้า HTML เล็กๆ น้อยๆ นี้:

 <html> <head> <title>Toptal Search</title> <!-- LOAD BOOTSTRAP FROM THE CDN --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> </head> <body> <!-- SOME BASIC STATIC CONTENT --> <div class="container"> <h1>Toptal Search</h1> </div> <!-- LOAD THE JAVASCRIPT LIBRARIES WE NEED --> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.9.3/lodash.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ractive/0.7.3/ractive.min.js"></script> <!-- LOAD THE DATA --> <script src="https://rawgit.com/emirotin/toptal-blog-ractive/master/data.js"></script> <!-- LOAD OUR SCRIPT --> <script src="script.js"></script> </body> </html>

อย่างที่คุณเห็น มันเป็นเอกสาร HTML5 ที่ไม่สำคัญ มันโหลด Bootstrap จาก CDN, Lodash (ไลบรารีจัดการข้อมูลที่ยอดเยี่ยม) และ Ractive.js

Ractive ไม่ต้องการการอุทิศทั้งหน้าให้กับ SPA ดังนั้นเราจึงสามารถมีเนื้อหาคงที่ได้ ในกรณีของเรา ประกอบด้วยองค์ประกอบคอนเทนเนอร์และชื่อหน้า

สุดท้าย เราโหลดข้อมูลที่ฉันเตรียมไว้สำหรับการสาธิต และสคริปต์ที่จะมีโปรแกรมของเรา

ตกลง เมื่อโครงสร้าง HTML ของเราพร้อมแล้ว มาเริ่มเพิ่มฟังก์ชันการทำงานจริงกัน

รายการทักษะ

สิ่งหนึ่งที่ฉันชอบเป็นพิเศษเกี่ยวกับ Ractive คือวิธีที่มันสอนให้คุณคิดเกี่ยวกับการนำเสนอขั้นสุดท้าย (HTML) ที่คุณต้องการทำให้สำเร็จ จากนั้นให้คุณจดจ่อกับการเขียนโค้ดที่จำเป็นเพื่อให้สิ่งนั้นเกิดขึ้น

ก่อนอื่น มาสร้างรายการทักษะตามมุมมองเริ่มต้นของเรา การทำเช่นนี้ทำให้เกิด:

  • การเพิ่มองค์ประกอบ HTML ที่จะแสดงรายการทักษะ
  • การเพิ่มตัวอย่างโค้ดเทมเพลตขนาดเล็กลงใน HTML ของเรา
  • การเขียน JavaScript สั้นๆ ง่ายๆ ที่ให้ข้อมูลกับเทมเพลต เพื่อแสดงในองค์ประกอบ HTML ที่เราเพิ่มเข้าไป

ม็อดของ HTML ประกอบด้วยสิ่งต่อไปนี้:

 <div class="container"> <h1>Toptal Search</h1> <div></div> <!-- THIS IS THE NEW HTML ELEMENT --> </div> <!-- THIS IS THE SMALL SNIPPET OF TEMPLATE CODE --> <script type="text/html"> <div class="row"> {{#each skills}} <span class="col-xs-3"> <a href="#" class="label label-primary">{{this}}</a> </span> {{/each}} </div> </script>

ไม่มีข้อตกลงพิเศษกับ Ractive ในการระบุองค์ประกอบ HTML เพื่อรับข้อมูลที่จะแสดง แต่วิธีที่ง่ายที่สุดในการดำเนินการคือการเพิ่ม ID ให้กับองค์ประกอบ ฉันมักจะใช้รหัส "รูท" เพื่อจุดประสงค์นี้ เราจะเห็นในไม่ช้าว่ามันใช้งานอย่างไรเมื่อ Ractive ถูกเตรียมใช้งานเบื้องต้น สำหรับผู้ที่อยากรู้อยากเห็นมีวิธีอื่นในการระบุองค์ประกอบรูท

องค์ประกอบสคริปต์ที่น่าอึดอัดเล็กน้อยที่มี type="text/html" เป็นกลอุบายอันชาญฉลาดในการโหลด HTML โดยเบราว์เซอร์โดยไม่ต้องแยกวิเคราะห์หรือแสดงผล เนื่องจากเบราว์เซอร์จะไม่สนใจสคริปต์ประเภทที่ไม่รู้จัก เนื้อหาของสคริปต์เป็นเทมเพลตที่มีลักษณะคล้ายหนวด/แฮนด์บาร์ (แม้ว่า Ractive จะมีส่วนขยายบางส่วน)

ก่อนอื่นเราเขียนโค้ดเทมเพลตโดยสมมติว่าเรามีสิทธิ์เข้าถึงอาร์เรย์ทักษะ เราใช้คำสั่งหนวด {{#each}} เพื่อกำหนดวนซ้ำ ภายใน directive องค์ประกอบปัจจุบันสามารถเข้าถึงได้เช่น this อีกครั้ง เราคิดว่าตัวแปรทักษะมีอาร์เรย์ของสตริง ดังนั้นเราจึงแสดงมันด้วยหนวดการแก้ไข {{this}}

ตกลง นั่นคือ HTML แต่จาวาสคริปต์ล่ะ? นั่นคือสิ่งที่ "เวทมนตร์" เกิดขึ้นซึ่งให้ข้อมูลกับเทมเพลต ::

 (function () { var skills = DB.skills, developers = DB.developers; var app = new Ractive({ el: '#root', template: '#tpl-app', data: { skills: _.keys(DB.skills) } }); }());

น่าประทับใจใช่ไหม ในโค้ด 10 บรรทัดนั้น เราสามารถ:

  1. “ดึง” ข้อมูลจาก “DB”
  2. สร้างแอป Ractive ใหม่
  3. บอกให้เรนเดอร์ภายในองค์ประกอบด้วย .
  4. บอกให้ใช้องค์ประกอบสคริปต์ด้วย เพื่อรับเทมเพลต (มีวิธีอื่นในการทำเช่นนี้เช่นกัน)
  5. ส่งข้อมูลเริ่มต้น (เราจะดูวิธีการเปลี่ยนตอนรันไทม์) หรือ "ขอบเขต" หากคุณคุ้นเคยกับคำศัพท์เชิงมุม
  6. ใช้วิธี keys lodash เพื่อรับชื่อทักษะที่เราใช้เป็นคีย์อ็อบเจ็กต์ใน "DB"

โดยพื้นฐานแล้วด้วยสคริปต์นี้ เราบอกเฟรมเวิร์กว่าต้องทำอะไร แต่ไม่ใช่ว่าต้องทำอย่างไร แม่แบบประกาศวิธีการ ซึ่งโดยส่วนตัวแล้วฉันคิดว่าน่าทึ่งและเป็นธรรมชาติมาก

หวังว่าคุณจะเริ่มเข้าใจไอเดียนี้แล้ว เรามาลองใช้งานบางอย่างที่มีประโยชน์มากกว่ากันกันดีกว่า

ค้นหาทักษะ

หน้าค้นหาแน่นอนว่าต้องการช่องค้นหา และเราต้องการให้มีความสามารถเชิงโต้ตอบ โดยในขณะที่ผู้ใช้พิมพ์ในช่องค้นหา รายการทักษะจะถูกกรองลง รวมเฉพาะทักษะที่มีสตริงย่อยที่ป้อน

ตามปกติของ Ractive เราเริ่มต้นด้วยการกำหนดเทมเพลต (ในขณะที่คิดว่าจำเป็นต้องมีตัวแปรบริบทใหม่ใดบ้าง และจะเปลี่ยนแปลงอย่างไรเกี่ยวกับการจัดการข้อมูล):

 <div class="container"> <h1>Toptal Search</h1> <div></div> </div> <!-- THIS IS THE SMALL SNIPPET OF TEMPLATE CODE --> <script type="text/html"> <!-- HERE'S OUR SEARCH BOX --> <div class="row"> <form class="form-horizontal col-xs-6 col-xs-offset-6"> <input type="search" class="form-control" value="{{ skillFilter }}" placeholder="Type part of the skill name here"> </form> </div> <hr> <!-- NOW INSTEAD OF DISPLAYING ALL SKILLS, WE INVOKE A TO-BE-CREATED JAVASCRIPT skills() FUNCTION THAT WILL FILTER THE SKILL LIST DOWN TO THOSE THAT MATCH THE TEXT ENTERED BY THE USER --> <div class="row"> {{#each skills()}} <span class="col-xs-3"> <a href="#" class="label label-primary">{{this}}</a> </span> {{/each}} </div> </script>

การเปลี่ยนแปลงไม่มากนัก แต่ยังต้องเรียนรู้อีกมากพอสมควร

อันดับแรก เราได้เพิ่ม <div> ใหม่ที่มีช่องค้นหาของเรา เห็นได้ชัดว่าเราต้องการเชื่อมต่ออินพุตนั้นกับตัวแปรบางตัว (เว้นแต่คุณจะคิดถึงวันซุป jQuery แบบเก่าที่ดี) Ractive รองรับการโยงสองทางที่เรียกว่า ซึ่งหมายความว่าโค้ด JS ของคุณสามารถดึงค่าได้โดยไม่ต้องอ่านค่าจาก DOM ด้วยตนเอง ในกรณีของเรา สามารถทำได้โดยใช้การแก้ไขหนวด value="{{ skillFilter }}" Ractive เข้าใจว่าเราต้องการผูกตัวแปรนี้กับแอตทริบิวต์ค่าของอินพุต ดังนั้นมันจึงคอยดูอินพุตสำหรับเราและอัปเดตตัวแปรโดยอัตโนมัติ ค่อนข้างเรียบร้อยด้วย HTML เพียง 1 บรรทัด

ประการที่สอง ตามที่อธิบายในความคิดเห็นในข้อมูลโค้ดด้านบน ตอนนี้แทนที่จะแสดงทักษะทั้งหมด เราจะสร้างฟังก์ชัน skills() JS ที่จะกรองรายการทักษะและส่งคืนเฉพาะรายการที่ตรงกับข้อความที่ผู้ใช้ป้อนเท่านั้น:

 // store skill list in a variable outside of Ractive scope var skillNames = _.keys(DB.skills); var app = new Ractive({ el: '#root', template: '#tpl-app', data: { // initializing the context variable is not strictly // required, but it is generally considered good practice skillFilter: null, // Define the skills() function right in our data object. // Function is available to our template where we call it. skills: function() { // Get the skillFilter variable from the Ractive instance // (available as 'this'). // NOTE WELL: Our use of a getter here tells Ractive that // our function has a *dependency* on the skillFilter // value, so this is significant. var skillFilter = this.get('skillFilter'); if (!skillFilter) { return skillNames; } skillFilter = new RegExp(_.escapeRegExp(skillFilter), 'i') return _.filter(skillNames, function(skill) { return skill.match(skillFilter); }); } } });

แม้ว่าสิ่งนี้จะสะอาดและง่ายต่อการใช้งาน แต่คุณอาจสงสัยว่าสิ่งนี้ส่งผลต่อประสิทธิภาพอย่างไร ฉันหมายความว่าเราจะเรียกใช้ฟังก์ชันในแต่ละครั้ง? แต่ละครั้งอะไรนะ? Ractive ฉลาดพอที่จะแสดงผลเฉพาะบางส่วนของเทมเพลต (และเรียกใช้ฟังก์ชันใดๆ จากพวกมัน) เมื่อการพึ่งพา (ตัวแปร) เปลี่ยนไป (Ractive รู้ว่าเกิดอะไรขึ้นด้วยการใช้ตัวตั้งค่า)

อนึ่ง สำหรับผู้ที่สนใจจะก้าวไปอีกขั้น ก็มีวิธีที่หรูหรากว่าในการทำเช่นนี้โดยใช้คุณสมบัติที่คำนวณได้ แต่ฉันจะปล่อยให้คุณเล่นด้วยตัวเองถ้าคุณต้องการ

หน้าผลลัพธ์

ตอนนี้เรามีรายการทักษะที่สามารถค้นหาได้ค่อนข้างดีแล้ว ไปที่มุมมองผลลัพธ์ซึ่งจะมีการแสดงรายชื่อนักพัฒนาที่ตรงกัน

สลับไปมาระหว่างมุมมองทักษะ

เห็นได้ชัดว่ามีหลายวิธีที่สามารถนำมาใช้ได้ ฉันเลือกวิธีการที่จะมีมุมมองที่แตกต่างกันสองแบบขึ้นอยู่กับว่าทักษะนั้นถูกเลือกหรือไม่ หากเป็นเช่นนั้น เราจะแสดงรายการนักพัฒนาที่ตรงกัน ถ้าไม่เราจะแสดงรายการทักษะและช่องค้นหา

ดังนั้น สำหรับผู้เริ่มต้น เมื่อผู้ใช้เลือก (เช่น คลิก) ชื่อทักษะ รายการทักษะจะต้องถูกซ่อนและชื่อทักษะควรแสดงเป็นส่วนหัวของหน้าแทน ในทางกลับกัน ในมุมมองทักษะที่เลือก จะต้องมีวิธีปิดมุมมองนั้นและกลับไปที่รายการทักษะ

นี่เป็นก้าวแรกของเราในเส้นทางนี้:

 <script type="text/html"> <!-- PARTIAL IS A NEW CONCEPT HERE --> {{#partial skillsList}} <div class="row"> <form class="form-horizontal col-xs-6 col-xs-offset-6"> <input type="search" class="form-control" value="{{ skillFilter }}" placeholder="Type part of the skill name here"> </form> </div> <hr> <div class="row"> {{#each skills()}} <span class="col-xs-3"> <!-- MAKE OUR SKILLS CLICKABLE, USING PROXY EVENTS --> <a href="#" class="label label-primary" on-click="select-skill:{{this}}">{{this}}</a> </span> {{/each}} </div> {{/partial}} {{#partial skillView}} <h2> <!-- DISPLAY SELECTED SKILL AS HEADING ON THE PAGE --> {{ currentSkill }} <!-- CLOSE BUTTON TAKES USER BACK TO SKILLS LIST --> <button type="button" class="close pull-right" on-click="deselect-skill">&times; CLOSE</button> </h2> {{/partial}} <!-- PARTIALS ARE NOT IN THE VIEW UNTIL WE EXPLICITLY INCLUDE THEM, SO INCLUDE THE PARTIAL RELEVANT TO THE CURRENT VIEW. --> {{#if currentSkill}} {{> skillView}} {{else}} {{> skillsList}} {{/if}} </script>

ตกลง มีอะไรเกิดขึ้นมากมายที่นี่

อันดับแรก เพื่อรองรับการนำสิ่งนี้ไปใช้ในมุมมองที่แตกต่างกันสองแบบ ฉันย้ายทุกสิ่งที่เรามีจนถึงตอนนี้ (เช่น มุมมองรายการ) ไปเป็นสิ่งที่เรียกว่าบางส่วน บางส่วนเป็นส่วนสำคัญของรหัสเทมเพลตที่เราจะรวมไว้ในที่อื่น (เร็ว ๆ นี้)

จากนั้น เราต้องการทำให้ทักษะของเราสามารถคลิกได้ และเมื่อคลิกแล้ว เราต้องการนำทางไปยังมุมมองทักษะที่เกี่ยวข้อง สำหรับสิ่งนี้ เราใช้สิ่งที่เรียกว่าเหตุการณ์พร็อกซี โดยที่เราตอบสนองต่อเหตุการณ์ทางกายภาพ (เมื่อคลิก ชื่อเป็นชื่อที่ Ractive เข้าใจได้) และพร็อกซีไปยังเหตุการณ์เชิงตรรกะ (เลือกทักษะ ชื่อคือสิ่งที่เราเรียกว่า ) ผ่านอาร์กิวเมนต์ (อย่างที่คุณอาจจำได้ว่านี่หมายถึงชื่อทักษะที่นี่)

(สำหรับข้อมูล ไวยากรณ์ทางเลือกของการเรียกเมธอดมีอยู่เพื่อทำให้สิ่งเดียวกันสำเร็จ)

ต่อไป เราถือว่า (อีกครั้ง) เราจะมีตัวแปรที่เรียกว่า currentSkill ซึ่งจะมีชื่อของทักษะที่เลือก (ถ้ามี) หรือจะว่างเปล่าหากไม่มีการเลือกทักษะ ดังนั้นเราจึงกำหนดอีกส่วนหนึ่งที่แสดงชื่อทักษะปัจจุบัน และยังมีลิงก์ "ปิด" ที่ควรยกเลิกการเลือกทักษะ

สำหรับ JavaScript การเพิ่มหลักคือโค้ดสำหรับสมัครรับเหตุการณ์ select-skill และ deselect-skill อัปเดต currentSkill (และ skillFilter ) ตามลำดับ:

 var app = new Ractive({ el: '#root', template: '#tpl-app', data: { skillFilter: null, currentSkill: null, // INITIALIZE currentSkill TO null // skills function remains unchanged skills: function() { var skillFilter = this.get('skillFilter'); if (!skillFilter) { return skillNames; } skillFilter = new RegExp(_.escapeRegExp(skillFilter), 'i') return _.filter(skillNames, function(skill) { return skill.match(skillFilter); }); } } }); // SUBSCRIBE TO LOGICAL EVENT select-skill app.on('select-skill', function(event, skill) { this.set({ // SET currentSkill TO THE SKILL SELECTED BY THE USER currentSkill: skill, // RESET THE SEARCH FILTER skillFilter: null }); }); // SUBSCRIBE TO LOGICAL EVENT deselect-skill app.on('deselect-skill', function(event) { this.set('currentSkill', null); // CLEAR currentSkill });

รายชื่อนักพัฒนาสำหรับแต่ละทักษะ

เมื่อเตรียมมุมมองใหม่สำหรับทักษะแล้ว เราก็สามารถเพิ่มเนื้อได้ — รายชื่อนักพัฒนาจริงๆ ที่เรามีสำหรับรายการนั้น สำหรับสิ่งนั้น เราขยายบางส่วนสำหรับมุมมองนี้ดังนี้:

 {{#partial skillView}} <h2> {{ currentSkill }} <button type="button" class="close pull-right" on-click="deselect-skill">&times; CLOSE</button> </h2> {{#each skillDevelopers(currentSkill)}} <div class="panel panel-default"> <div class="panel-body"> {{ this.name }} </div> </div> {{/each}} {{/partial}}

หวังว่าเมื่อถึงจุดนี้ คุณจะเข้าใจถึงสิ่งที่เกิดขึ้นที่นี่: เราได้เพิ่มส่วนการทำซ้ำใหม่ให้กับบางส่วนของ skillView ที่วนซ้ำผลลัพธ์ของฟังก์ชัน skillDevelopers ใหม่ที่เราจะเขียนต่อไป สำหรับนักพัฒนาแต่ละรายในอาร์เรย์ (ส่งคืนโดยฟังก์ชัน skillDevelopers นั้น) เราสร้างพาเนลและแสดงชื่อนักพัฒนา โปรดทราบว่าฉันสามารถใช้รูปแบบโดยนัย {{name}} ที่นี่และ Ractive จะค้นหาแอตทริบิวต์ที่เหมาะสมโดยการค้นหาบริบทที่เริ่มต้นจากบริบทปัจจุบัน (ซึ่งในกรณีของเราคือวัตถุนักพัฒนาที่ถูกผูกไว้โดย {{#each}} ) แต่ ฉันชอบที่จะชัดเจน ข้อมูลเพิ่มเติมเกี่ยวกับบริบทและข้อมูลอ้างอิงมีอยู่ในเอกสาร Ractive

และนี่คือการนำ skillDevelopers() ไปใช้งาน:

 skillDevelopers: function(skill) { // GET THE SKILL OBJECT FROM THE “DB” skill = skills[skill]; // SAFETY CHECK, RETURN EARLY IN CASE OF UNKNOWN SKILL NAME if (!skill) { return; } // MAP THE DEVELOPER'S IDs (SLUGS) TO THE // ACTUAL DEVELOPER DETAIL OBJECTS return _.map(skill.developers, function(slug) { return developers[slug]; }); }

การขยายรายการสำหรับนักพัฒนาแต่ละราย

ตอนนี้เรามีรายชื่อนักพัฒนาที่ใช้งานได้แล้ว ก็ถึงเวลาเพิ่มรายละเอียดและรูปถ่ายที่สวยงามแน่นอน:

 {{#partial skillView}} <h2> {{ currentSkill }} <button type="button" class="close pull-right" on-click="deselect-skill">&times; CLOSE</button> </h2> {{#each skillDevelopers(currentSkill)}} <div class="panel panel-default"> <div class="panel-body media"> <div class="media-left"> <!-- ADD THE PHOTO --> <img class="media-object img-circle" width="64" height="64" src="{{ this.photo }}" alt="{{ this.name }}"> </div> <div class="media-body"> <!-- MAKE THE DEVELOPER'S NAME A HYPERLINK TO THEIR PROFILE --> <a class="h4 media-heading" href="{{ this.url }}" target="_blank"> {{ this.name }}</a> <!-- ADD MORE DETAILS (FROM THEIR PROFILE) --> <p>{{ this.desc }}</p> </div> </div> </div> {{/each}} {{/partial}}

ไม่มีอะไรใหม่ที่นี่จากด้าน Ractive ของสิ่งต่าง ๆ แต่การใช้งานคุณสมบัติ Bootstrap ที่หนักกว่าเล็กน้อย

แสดงรายการทักษะนักพัฒนาที่คลิกได้

จนถึงขณะนี้ เรามีความคืบหน้าเป็นอย่างดี แต่คุณลักษณะหนึ่งที่ยังขาดหายไปคืออีกด้านหนึ่งของความสัมพันธ์ระหว่างนักพัฒนาทักษะ กล่าวคือ เราต้องการแสดงทักษะของนักพัฒนาแต่ละราย และเราต้องการให้แต่ละทักษะเหล่านั้นเป็นลิงก์ที่คลิกได้ซึ่งจะนำเราไปยังมุมมองผลลัพธ์สำหรับทักษะนั้น

แต่เดี๋ยวก่อน… ฉันแน่ใจว่าเรามีสิ่งเดียวกันในรายการทักษะแล้ว ใช่ในความเป็นจริงเราทำ อยู่ในรายการทักษะที่คลิกได้ แต่มาจากอาร์เรย์ที่แตกต่างจากชุดทักษะของนักพัฒนาแต่ละราย ทว่าสิ่งเหล่านี้คล้ายกันมากพอสมควรซึ่งเป็นสัญญาณที่ชัดเจนสำหรับฉันว่าเราควรใช้ HTML ก้อนนั้นซ้ำ และในท้ายที่สุด บางส่วนคือเพื่อนของเรา

(หมายเหตุ: Ractive ยังมีแนวทางขั้นสูงกว่าสำหรับกลุ่มของมุมมองที่นำกลับมาใช้ใหม่ได้ ซึ่งเรียกว่าส่วนประกอบ ซึ่งมีประโยชน์มากจริงๆ แต่เพื่อความเรียบง่าย เราจะไม่พูดถึงเรื่องนี้ในตอนนี้)

ต่อไปนี้คือวิธีที่เราทำสิ่งนี้ให้สำเร็จโดยใช้บางส่วน (และโปรดทราบว่า เราสามารถเพิ่มฟังก์ชันนี้ได้โดยไม่ต้องเพิ่มโค้ด JavaScript แม้แต่บรรทัดเดียว!):

 <!-- MAKE THE CLICKABLE SKILL LINK INTO ITS OWN “skill” PARTIAL --> {{#partial skill}} <a href="#" class="label label-primary" on-click="select-skill:{{this}}">{{this}}</a> {{/partial}} {{#partial skillsList}} <div class="row"> <form class="form-horizontal col-xs-6 col-xs-offset-6"> <input type="search" class="form-control" value="{{ skillFilter }}" placeholder="Type part of the skill name here"> </form> </div> <hr> <div class="row"> {{#each skills()}} <!-- USE THE NEW “skill” PARTIAL --> <span class="col-xs-3">{{> skill}}</span> {{/each}} </div> {{/partial}} {{#partial skillView}} <h2> {{ currentSkill }} <button type="button" class="close pull-right" on-click="deselect-skill">&times; CLOSE</button> </h2> {{#each skillDevelopers(currentSkill)}} <div class="panel panel-default"> <div class="panel-body media"> <div class="media-left"> <img class="media-object img-circle" width="64" height="64" src="{{ this.photo }}" alt="{{ this.name }}"> </div> <div class="media-body"> <a class="h4 media-heading" href="{{ this.url }}" target="_blank">{{ this.name }}</a> <p>{{ this.desc }}</p> <p> <!-- ITERATE OVER THE DEVELOPER'S SKILLS --> {{#each this.skills}} <!-- REUSE THE NEW “skill” PARTIAL TO DISPLAY EACH DEVELOPER SKILL AS A CLICKABLE LINK --> {{> skill}}&nbsp; {{/each}} </p> </div> </div> </div> {{/each}} {{/partial}}

ฉันมีรายการทักษะที่แนบมากับวัตถุของนักพัฒนาที่เราดึงมาจาก "DB" อยู่แล้ว ดังนั้นฉันจึงเปลี่ยนเทมเพลตเล็กน้อย: ฉันย้ายบรรทัดที่แสดงป้ายกำกับทักษะเป็นบางส่วน และใช้ส่วนนี้ในตำแหน่งเดิมของบรรทัดนั้น

จากนั้น เมื่อฉันทำซ้ำทักษะของนักพัฒนา ฉันสามารถใช้ส่วนใหม่เดิมนี้ซ้ำเพื่อแสดงทักษะแต่ละอย่างเป็นลิงก์ที่คลิกได้ด้วยเช่นกัน ยิ่งไปกว่านั้น นี่ยังพร็อกซีเหตุการณ์เลือกทักษะหากคุณจำได้ โดยส่งชื่อทักษะเดียวกัน ซึ่งหมายความว่าเราสามารถรวมสิ่งนี้ได้ทุกที่ (มีบริบทที่เหมาะสม) และเราจะได้รับป้ายกำกับที่คลิกได้ซึ่งนำไปสู่มุมมองทักษะ!

Final Touch — ตัวโหลดล่วงหน้า

โอเค ตอนนี้เรามีแอพที่ใช้งานได้พื้นฐานแล้ว มันสะอาดและทำงานเร็ว แต่ยังต้องใช้เวลาโหลดนาน (ในกรณีของเรา ส่วนหนึ่งเป็นเพราะเราใช้แหล่งที่มาที่ไม่ต่อกันและไม่ย่อส่วน แต่ในแอปในโลกแห่งความเป็นจริง อาจมีเหตุผลสำคัญอื่นๆ เช่น การโหลดข้อมูลเริ่มต้น)

ดังนั้นในขั้นตอนสุดท้าย ผมจะแสดงเคล็ดลับง่ายๆ ในการเพิ่มแอนิเมชั่นการโหลดล่วงหน้าซึ่งจะถูกลบออกทันทีที่ Ractive แสดงผลแอปของเรา:

 <div class="container"> <h1>Toptal Search</h1> <div> <div class="progress"> <div class="progress-bar progress-bar-striped active"> Loading... </div> </div> </div> </div>

แล้วความมหัศจรรย์ของที่นี่คืออะไร? มันค่อนข้างง่ายจริงๆ ฉันเพิ่มเนื้อหาบางส่วน (เป็นแถบแสดงความคืบหน้าของภาพเคลื่อนไหว Bootstrap แต่อาจเป็น GIF แบบเคลื่อนไหวหรืออะไรก็ได้) ตรงไปยังองค์ประกอบรูทของเรา ฉันคิดว่ามันค่อนข้างฉลาด — ในขณะที่สคริปต์ของเรากำลังโหลด ผู้ใช้เห็นตัวบ่งชี้การโหลด (เนื่องจากไม่มีการพึ่งพา JavaScript จึงสามารถแสดงผลได้ทันที) อย่างไรก็ตาม ทันทีที่แอป Ractive เริ่มต้นขึ้น Ractive จะเขียนทับเนื้อหาขององค์ประกอบรูท (และด้วยเหตุนี้จึงลบแอนิเมชั่นการโหลดล่วงหน้า) ด้วยเทมเพลตที่แสดงผล ด้วยวิธีนี้ เราจึงสามารถบรรลุผลนี้ได้เพียงส่วนเดียวของ HTML แบบคงที่และตรรกะ 0 บรรทัด ฉันคิดว่ามันเจ๋งมาก

บทสรุป

ลองนึกถึงสิ่งที่เราทำได้สำเร็จที่นี่และวิธีที่เราทำสำเร็จได้ง่ายเพียงใด เรามีแอพที่ค่อนข้างครอบคลุม: มันแสดงรายการทักษะ ช่วยให้ค้นหาได้อย่างรวดเร็ว (และยังรองรับการอัพเดทรายการทักษะแบบโต้ตอบเมื่อผู้ใช้พิมพ์ในช่องค้นหา) ช่วยให้นำทางไปยังทักษะเฉพาะและย้อนกลับ และรายการ นักพัฒนาสำหรับแต่ละทักษะที่เลือก นอกจากนี้ เราสามารถคลิกที่ทักษะใดๆ ที่ระบุโดยผู้พัฒนารายใดก็ได้ เพื่อนำเราไปยังรายชื่อนักพัฒนาที่มีทักษะนั้น และทั้งหมดที่มี HTML น้อยกว่า 80 บรรทัดและ JavaScript น้อยกว่า 40 บรรทัด ในความคิดของฉัน นั่นค่อนข้างน่าประทับใจและพูดได้เต็มปากถึงพลัง ความสง่างาม และความเรียบง่ายของ Ractive

เวอร์ชันที่ใช้งานได้ของแอปมีให้ใช้งานออนไลน์ที่นี่ และซอร์สโค้ดแบบเต็มเป็นแบบสาธารณะและมีให้ที่นี่

แน่นอน เราเพิ่งจะขีดข่วนพื้นผิวในบทความนี้เกี่ยวกับสิ่งที่เป็นไปได้ด้วยเฟรมเวิร์ก Ractive หากคุณชอบสิ่งที่คุณเคยเห็นมา เราขอแนะนำให้คุณเริ่มต้นใช้งานการตั้งค่า 60 วินาทีของ Ractive และเริ่มสำรวจทุกสิ่งที่ Ractive มีให้ด้วยตัวคุณเอง