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

เผยแพร่แล้ว: 2022-03-10
สรุปโดยย่อ ↬ ในบทความแรกของชุดนี้ ผู้เขียนซึ่งเป็นมือใหม่ JavaScript ได้ตั้งเป้าหมายในการออกแบบและเขียนโค้ดเว็บแอปพลิเคชันพื้นฐาน 'แอป' จะถูกเรียกว่า 'เข้า/ออก' — แอปพลิเคชันสำหรับจัดระเบียบเกมแบบทีม ในบทความนี้ เราจะมาพูดถึงวิธีการสร้างแอปพลิเคชัน 'เข้า/ออก' กัน

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

ต้องการทราบว่าแอปพลิเคชันสิ้นสุดลงอย่างไร เปิดเบราว์เซอร์โทรศัพท์ของคุณที่ https://io.benfrain.com

นี่คือบทสรุปของสิ่งที่เราจะกล่าวถึงในบทความนี้:

  • การตั้งค่าโปรเจ็กต์และเหตุผลที่ฉันเลือกใช้ Gulp เป็นเครื่องมือสร้าง
  • รูปแบบการออกแบบแอปพลิเคชันและความหมายในทางปฏิบัติ
  • วิธีจัดเก็บและแสดงสถานะแอปพลิเคชัน
  • การกำหนดขอบเขต CSS เป็นส่วนประกอบอย่างไร
  • UI/UX ใดที่ถูกใช้เพื่อทำให้สิ่งต่างๆ 'เหมือนแอป' มากขึ้น
  • เงินที่โอนเปลี่ยนไปผ่านการทำซ้ำอย่างไร

เริ่มจากเครื่องมือสร้างกันก่อน

เครื่องมือสร้าง

เพื่อให้เครื่องมือพื้นฐานของ TypeScipt และ PostCSS ทำงาน และสร้างประสบการณ์การพัฒนาที่ดีได้ ฉันจำเป็นต้องมีระบบบิลด์

ในงานประจำวันของฉัน ในช่วงห้าปีที่ผ่านมา ฉันได้สร้างต้นแบบอินเทอร์เฟซใน HTML/CSS และ JavaScript ในระดับที่น้อยกว่า จนกระทั่งเมื่อไม่นานนี้ ฉันได้ใช้อึกกับปลั๊กอินจำนวนหนึ่งแทบทั้งหมดโดยเฉพาะ เพื่อให้บรรลุความต้องการในการสร้างที่ค่อนข้างต่ำต้อยของฉัน

โดยทั่วไปแล้ว ฉันต้องประมวลผล CSS, แปลง JavaScript หรือ TypeScript เป็น JavaScript ที่รองรับอย่างกว้างขวางยิ่งขึ้น และบางครั้ง ทำงานที่เกี่ยวข้อง เช่น ลดขนาดเอาต์พุตของโค้ดและเพิ่มประสิทธิภาพเนื้อหา การใช้อึกช่วยให้ฉันสามารถแก้ปัญหาเหล่านั้นด้วยความมั่นใจในตนเองได้เสมอ

สำหรับผู้ที่ไม่คุ้นเคย Gulp ให้คุณเขียน JavaScript เพื่อทำ 'บางสิ่ง' ไปยังไฟล์ในระบบไฟล์ในเครื่องของคุณ ในการใช้ Gulp คุณมักจะมีไฟล์เดียว (เรียกว่า gulpfile.js ) ในรูทของโปรเจ็กต์ของคุณ ไฟล์ JavaScript นี้ช่วยให้คุณสามารถกำหนดงานเป็นฟังก์ชันได้ คุณสามารถเพิ่ม 'ปลั๊กอิน' ของบริษัทอื่น ซึ่งเป็นฟังก์ชัน JavaScript เพิ่มเติม ซึ่งจัดการกับงานเฉพาะ

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

ตัวอย่างงานอึก

ตัวอย่างงาน Gulp อาจใช้ปลั๊กอินเพื่อควบคุม PostCSS เพื่อประมวลผลเป็น CSS เมื่อคุณเปลี่ยนสไตล์ชีตการเขียน (gulp-postcss) หรือรวบรวมไฟล์ TypeScript เป็น vanilla JavaScript (gulp-typescript) ในขณะที่คุณบันทึก นี่เป็นตัวอย่างง่ายๆ ของวิธีการเขียนงานในอึก งานนี้ใช้ปลั๊กอิน 'del' gulp เพื่อลบไฟล์ทั้งหมดในโฟลเดอร์ชื่อ 'build':

 var del = require("del"); gulp.task("clean", function() { return del(["build/**/*"]); });

require กำหนดปลั๊กอิน del ให้กับตัวแปร จากนั้นจึงเรียกเมธอด gulp.task เราตั้งชื่องานด้วยสตริงเป็นอาร์กิวเมนต์แรก ("ล้าง") แล้วเรียกใช้ฟังก์ชัน ซึ่งในกรณีนี้จะใช้เมธอด 'del' เพื่อลบโฟลเดอร์ที่ส่งผ่านไปยังเป็นอาร์กิวเมนต์ เครื่องหมายดอกจันมีรูปแบบ 'glob' ซึ่งโดยพื้นฐานแล้วจะบอกว่า 'ไฟล์ใด ๆ ในโฟลเดอร์ใด ๆ ' ของโฟลเดอร์บิลด์

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

ฉันติดอยู่กับอึกเป็นเครื่องมือสร้างเริ่มต้น / ตัวดำเนินการงานตลอดหลายปีที่ผ่านมาด้วยนโยบายว่า 'ถ้ามันยังไม่พัง อย่าพยายามแก้ไขเลย'

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

ฉันได้ยินคนคุยกันมากมายทางอินเทอร์เน็ตเกี่ยวกับ 'Webpack' และคิดว่ามันเป็นหน้าที่ของฉันที่จะลองโครงการโดยใช้ขนมปังปิ้งแบบใหม่ของนักพัฒนา front-end cool-kids

เว็บแพ็ค

ฉันจำได้อย่างชัดเจนว่าข้ามไปที่เว็บไซต์ webpack.js.org ด้วยความสนใจ คำอธิบายแรกว่า Webpack คืออะไรและเริ่มต้นดังนี้:

 import bar from './bar';

พูดว่าอะไรนะ? ในคำพูดของดร.อีวิล "โยนกระดูกนี่ สก๊อตต์"

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

เจาะลึกลงไปในเอกสารประกอบของ Webpack อีกเล็กน้อย มีการเสนอคำอธิบายที่ทึบน้อยกว่าเล็กน้อย “ที่แกนหลักแล้ว webpack เป็นตัวรวมโมดูลสแตติกสำหรับแอปพลิเคชัน JavaScript สมัยใหม่”

อืม. ชุดรวมโมดูลสแตติก นั่นคือสิ่งที่ฉันต้องการ? ฉันไม่มั่นใจ อ่านไปเรื่อยๆ แต่ยิ่งอ่านยิ่งไม่ชัดเจน ย้อนกลับไปในตอนนั้น แนวคิดอย่างกราฟพึ่งพา การรีโหลดโมดูลยอดนิยม และจุดเริ่มต้นหายไปจากฉัน

สองสามตอนเย็นของการค้นคว้า Webpack ในภายหลัง ฉันละเลยแนวคิดใดๆ ในการใช้งาน Webpack

ฉันแน่ใจว่าอยู่ในสถานการณ์ที่ถูกต้องและมีประสบการณ์มากขึ้น Webpack นั้นทรงพลังและเหมาะสมอย่างมาก แต่ดูเหมือนว่ามันจะเกินความจำเป็นอย่างสมบูรณ์สำหรับความต้องการที่ต่ำต้อยของฉัน การรวมโมดูล การเขย่าต้นไม้ และการรีโหลดโมดูลแบบร้อนนั้นฟังดูยอดเยี่ยม ฉันไม่มั่นใจว่าฉันต้องการมันสำหรับ 'แอพ' เล็กๆ ของฉัน

ดังนั้นกลับไปที่อึกแล้ว

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

เครื่องมือหนึ่งที่มาถึงช้าเกินกว่าที่ฉันจะประเมินสำหรับแอปพลิเคชันนี้คือ Parceljs ด้วยการกำหนดค่าเป็นศูนย์และ BrowserSync เช่นการโหลดเบราว์เซอร์สำรอง ฉันพบยูทิลิตี้ที่ยอดเยี่ยมตั้งแต่นั้นมา! นอกจากนี้ ในการป้องกันของ Webpack ฉันได้รับแจ้งว่า v4 เป็นต้นไปของ Webpack ไม่ต้องการไฟล์การกำหนดค่า สรุปแล้ว ในการสำรวจความคิดเห็นล่าสุดที่ฉันใช้บน Twitter จากผู้ตอบแบบสอบถาม 87 คน มากกว่าครึ่งเลือก Webpack มากกว่า Gulp, Parcel หรือ Grunt

ฉันเริ่มไฟล์ Gulp ด้วยฟังก์ชันพื้นฐานเพื่อเริ่มต้นใช้งาน

งาน 'เริ่มต้น' จะดูโฟลเดอร์ 'ต้นทาง' ของสไตล์ชีตและไฟล์ build และคอมไพล์ไปยังโฟลเดอร์บิลด์พร้อมกับ HTML พื้นฐานและแมปต้นทางที่เกี่ยวข้อง

ฉันให้ BrowserSync ทำงานกับอึกได้เช่นกัน ฉันอาจไม่รู้ว่าจะทำอย่างไรกับไฟล์การกำหนดค่า Webpack แต่นั่นไม่ได้หมายความว่าฉันเป็นสัตว์บางชนิด การต้องรีเฟรชเบราว์เซอร์ด้วยตนเองในขณะที่วนซ้ำด้วย HTML/CSS นั้นเป็นเรื่องที่ต้องทำในปี 2010 และ BrowserSync จะให้คำติชมสั้นๆ และการวนซ้ำซึ่งเป็นประโยชน์อย่างมากสำหรับการเข้ารหัสส่วนหน้า

นี่คือไฟล์อึกพื้นฐาน ณ วันที่ 11.6.2017

คุณสามารถดูว่าฉันปรับแต่ง Gulpfile ให้ใกล้ถึงจุดสิ้นสุดของการจัดส่งได้อย่างไร โดยเพิ่มการลดขนาดด้วย ugilify:

โครงสร้างโครงการ

ผลจากการเลือกเทคโนโลยีของฉัน องค์ประกอบบางอย่างของการจัดระเบียบโค้ดสำหรับแอปพลิเคชันจึงกำหนดตัวเอง gulpfile.js ในรูทของโปรเจ็กต์ โฟลเดอร์ node_modules (ที่ Gulp เก็บโค้ดปลั๊กอิน) โฟลเดอร์ preCSS สำหรับสไตล์ชีตการเขียน โฟลเดอร์ ts สำหรับไฟล์ build และโฟลเดอร์บิลด์สำหรับโค้ดที่คอมไพล์แล้วใช้งานได้จริง

แนวคิดคือการมี index.html ที่มี 'เชลล์' ของแอปพลิเคชัน ซึ่งรวมถึงโครงสร้าง HTML ที่ไม่ใช่ไดนามิก จากนั้นลิงก์ไปยังสไตล์และไฟล์ JavaScript ที่จะทำให้แอปพลิเคชันทำงานได้ บนดิสก์จะมีลักษณะดังนี้:

 build/ node_modules/ preCSS/ img/ partials/ styles.css ts/ .gitignore gulpfile.js index.html package.json tsconfig.json

การกำหนดค่า build เพื่อดูโฟลเดอร์บิลด์นั้นหมายความว่าฉันสามารถชี้เบราว์เซอร์ไปที่ localhost:3000 และทั้งหมดก็ดี

ด้วยระบบการสร้างขั้นพื้นฐาน การจัดระเบียบไฟล์และการออกแบบพื้นฐานบางอย่างเพื่อเริ่มต้น ฉันมีอาหารสัตว์ที่ผัดวันประกันพรุ่งจนหมด ซึ่งฉันสามารถใช้อย่างถูกกฎหมายเพื่อป้องกันไม่ให้ฉันสร้างสิ่งนั้นขึ้นมาจริงๆ!

การเขียนใบสมัคร

หลักการทำงานของแอพพลิเคชั่นคือสิ่งนี้ ก็จะมีการเก็บข้อมูล เมื่อจาวาสคริปต์โหลด มันจะโหลดข้อมูลนั้น วนซ้ำผู้เล่นแต่ละคนในข้อมูล สร้าง HTML ที่จำเป็นเพื่อเป็นตัวแทนของผู้เล่นแต่ละคนเป็นแถวในเลย์เอาต์และวางไว้ในส่วนเข้า/ออกที่เหมาะสม จากนั้นการโต้ตอบจากผู้ใช้จะย้ายผู้เล่นจากสถานะหนึ่งไปยังอีกสถานะหนึ่ง เรียบง่าย.

เมื่อพูดถึงการเขียนแอปพลิเคชันจริงๆ ความท้าทายด้านแนวคิดใหญ่สองประการที่ต้องเข้าใจคือ:

  1. วิธีการแสดงข้อมูลสำหรับแอปพลิเคชันในลักษณะที่สามารถขยายและจัดการได้ง่าย
  2. วิธีทำให้ UI ตอบสนองเมื่อข้อมูลถูกเปลี่ยนจากการป้อนข้อมูลของผู้ใช้

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

พิจารณาวัตถุ JavaScript นี้กำหนดให้กับตัวแปรที่เรียกว่า ioState (สำหรับสถานะเข้า/ออก):

 var ioState = { Count: 0, // Running total of how many players RosterCount: 0; // Total number of possible players ToolsExposed: false, // Whether the UI for the tools is showing Players: [], // A holder for the players }

หากคุณยังไม่ค่อยรู้จัก JavaScript เป็นอย่างดี อย่างน้อยคุณอาจจะสามารถเข้าใจสิ่งที่เกิดขึ้นได้: แต่ละบรรทัดในวงเล็บปีกกาเป็นคุณสมบัติ (หรือ 'คีย์' ในภาษาจาวาสคริปต์) และคู่ค่า คุณสามารถตั้งค่าทุกอย่างเป็นคีย์ JavaScript ตัวอย่างเช่น ฟังก์ชัน อาร์เรย์ของข้อมูลอื่นๆ หรืออ็อบเจ็กต์ที่ซ้อนกัน นี่คือตัวอย่าง:

 var testObject = { testFunction: function() { return "sausages"; }, testArray: [3,7,9], nestedtObject { key1: "value1", key2: 2, } }

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

 ioState.Count = 7;

หากเราต้องการตั้งค่าข้อความให้เป็นค่านั้น สัญกรณ์จะทำงานดังนี้:

 aTextNode.textContent = ioState.Count;

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

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

พิจารณาอินเทอร์เฟซเข้า/ออก โดยทั่วไปจะแสดงรายชื่อผู้เล่นที่มีศักยภาพสำหรับเกม มีการระบุไว้ในแนวตั้งโดยอยู่ใต้หน้าอื่น

บางทีผู้เล่นแต่ละคนอาจอยู่ใน DOM โดยมี label ล้อมรอบ input ช่องทำเครื่องหมาย ด้วยวิธีนี้ การคลิกที่ผู้เล่นจะสลับผู้เล่นไปที่ 'In' โดยอาศัยป้ายกำกับทำให้อินพุต 'ตรวจสอบ'

ในการอัปเดตอินเทอร์เฟซของเรา เราอาจมี 'ผู้ฟัง' ในแต่ละองค์ประกอบอินพุตใน JavaScript เมื่อคลิกหรือเปลี่ยนแปลง ฟังก์ชันจะสอบถาม DOM และนับจำนวนอินพุตของผู้เล่นที่ได้รับการตรวจสอบ บนพื้นฐานของการนับนั้น เราจะอัปเดตอย่างอื่นใน DOM เพื่อแสดงให้ผู้ใช้เห็นจำนวนผู้เล่นที่ได้รับการตรวจสอบ

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

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

ดังนั้น. การใช้โครงสร้างอ็อบเจ็กต์ JavaScript สำหรับสถานะนั้นดูเรียบง่าย แต่ยืดหยุ่นเพียงพอที่จะสรุปสถานะของแอปพลิเคชันในเวลาใดก็ตาม ทฤษฎีว่าจะสามารถจัดการสิ่งนี้ได้อย่างไรก็ฟังดูเพียงพอ – นี่ต้องเป็นวลีที่ว่า 'กระแสข้อมูลทางเดียว' ทั้งหมดเกี่ยวกับอะไร อย่างไรก็ตาม เคล็ดลับแรกที่แท้จริงคือการสร้างโค้ดที่จะอัปเดต UI โดยอัตโนมัติตามการเปลี่ยนแปลงใดๆ ของข้อมูลนั้น

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

รูปแบบการออกแบบ

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

บางทีวรรณกรรมที่มีชื่อเสียงที่สุดเกี่ยวกับรูปแบบการออกแบบก็คือ "รูปแบบการออกแบบ: องค์ประกอบของซอฟต์แวร์เชิงวัตถุที่นำกลับมาใช้ใหม่ได้" ในปี 1994 แม้ว่าจะเกี่ยวข้องกับ C ++ และ Smalltalk แต่แนวคิดก็สามารถถ่ายทอดได้ สำหรับ JavaScript "การเรียนรู้รูปแบบการออกแบบ JavaScript" ของ Addy Osmani ครอบคลุมพื้นที่ที่คล้ายกัน คุณสามารถอ่านออนไลน์ได้ฟรีที่นี่

รูปแบบผู้สังเกตการณ์

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

เมื่อไม่นานมานี้ ฉันได้เห็นและอ่านการดำน้ำลึกที่ยอดเยี่ยมมากเกี่ยวกับการนำปฏิกิริยาภายในแอปโดย Gregg Pollack มีทั้งโพสต์บล็อกและวิดีโอเพื่อความเพลิดเพลินของคุณที่นี่

เมื่ออ่านคำอธิบายเริ่มต้นของรูปแบบ 'ผู้สังเกตการณ์' ใน Learning JavaScript Design Patterns ฉันค่อนข้างแน่ใจว่านี่คือรูปแบบสำหรับฉัน อธิบายไว้ดังนี้

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

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

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

สมมติว่าผู้ใช้คลิกผู้เล่นชื่อ “เบ็ตตี้” เพื่อเลือกว่าเธออยู่ในเกม บางสิ่งอาจจำเป็นต้องเกิดขึ้นใน UI:

  1. เพิ่ม 1 ในการนับการเล่น
  2. นำ Betty ออกจากพูล 'Out' ของผู้เล่น
  3. เพิ่ม Betty ลงในพูล 'In' ของผู้เล่น

แอปจะต้องอัปเดตข้อมูลที่แสดงถึง UI ด้วย สิ่งที่ฉันอยากหลีกเลี่ยงมากคือ:

 playerName.addEventListener("click", playerToggle); function playerToggle() { if (inPlayers.includes(e.target.textContent)) { setPlayerOut(e.target.textContent); decrementPlayerCount(); } else { setPlayerIn(e.target.textContent); incrementPlayerCount(); } }

จุดมุ่งหมายคือการมีกระแสข้อมูลที่สวยงามซึ่งอัปเดตสิ่งที่จำเป็นใน DOM เมื่อใดและหากข้อมูลส่วนกลางมีการเปลี่ยนแปลง

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

 function itemAdd(itemString: string) { let currentDataSet = getCurrentDataSet(); var newPerson = new makePerson(itemString); io.items[currentDataSet].EventData.splice(0, 0, newPerson); io.notify({ items: io.items }); }

ส่วนที่เกี่ยวข้องกับรูปแบบ Observer มีวิธี io.notify ตามที่แสดงให้เราเห็นว่ากำลังแก้ไขส่วน items ของสถานะแอปพลิเคชัน ให้ฉันแสดงให้คุณเห็นผู้สังเกตการณ์ที่รับฟังการเปลี่ยนแปลงของ 'รายการ':

 io.addObserver({ props: ["items"], callback: function renderItems() { // Code that updates anything to do with items... } });

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

ด้วยวิธีนี้ แอปอาจมีผู้สังเกตดูการเปลี่ยนแปลงในคุณสมบัติของข้อมูลและเรียกใช้ฟังก์ชันทุกครั้งที่มีการเปลี่ยนแปลงเกิดขึ้น

หากคุณสนใจรูปแบบ Observer ที่ฉันเลือกใช้ ฉันจะอธิบายให้ละเอียดยิ่งขึ้นที่นี่

ขณะนี้มีแนวทางในการอัปเดต UI ตามสถานะอย่างมีประสิทธิภาพ พีชชี่. อย่างไรก็ตาม สิ่งนี้ยังคงทิ้งฉันไว้กับประเด็นที่ไม่ชัดเจนสองประเด็น

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

มาจัดการด้านการจัดเก็บของกันก่อน

ออมทรัพย์รัฐ

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

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

API สำหรับ localStorage นั้นเรียบง่ายอย่างเหลือเชื่อ คุณตั้งค่าและรับข้อมูลดังนี้:

 // Set something localStorage.setItem("yourKey", "yourValue"); // Get something localStorage.getItem("yourKey");

LocalStorage มีเมธอด setItem ที่คุณส่งผ่านสองสตริงไป อันดับแรกคือชื่อของคีย์ที่คุณต้องการเก็บข้อมูล และสตริงที่สองคือสตริงจริงที่คุณต้องการจัดเก็บ เมธอด getItem ใช้สตริงเป็นอาร์กิวเมนต์ที่ส่งคืนสิ่งที่คุณเก็บไว้ภายใต้คีย์นั้นใน localStorage ดีและเรียบง่าย

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

 // Set something localStorage.setItem("myArray", [1, 2, 3, 4]); // Get something localStorage.getItem("myArray"); // Logs "1,2,3,4"

แม้ว่าเราจะพยายามตั้งค่า 'myArray' เป็นอาร์เรย์ก็ตาม เมื่อเราดึงมันออกมา มันถูกเก็บไว้เป็นสตริง (สังเกตเครื่องหมายอัญประกาศรอบ '1,2,3,4')

คุณสามารถจัดเก็บอ็อบเจ็กต์และอาร์เรย์ด้วย localStorage ได้อย่างแน่นอน แต่คุณต้องจำไว้ว่าพวกมันจำเป็นต้องแปลงไปมาจากสตริง

ดังนั้น เพื่อที่จะเขียนข้อมูลสถานะลงใน localStorage มันถูกเขียนไปยังสตริงด้วย JSON.stringify() ดังนี้:

 const storage = window.localStorage; storage.setItem("players", JSON.stringify(io.items));

เมื่อจำเป็นต้องดึงข้อมูลจาก localStorage สตริงก็ถูกเปลี่ยนกลับเป็นข้อมูลที่ใช้งานได้ด้วย JSON.parse() ดังนี้:

 const players = JSON.parse(storage.getItem("players"));

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

ขณะนี้ข้อมูลยังคงมีการรีเฟรชและเซสชันอย่างต่อเนื่อง เย้! ข่าวร้ายก็คือ localStorage ไม่รอดจากการที่ผู้ใช้ล้างข้อมูลเบราว์เซอร์ของตน เมื่อมีคนทำอย่างนั้น ข้อมูลเข้า/ออกทั้งหมดจะสูญหาย นั่นเป็นข้อบกพร่องที่ร้ายแรง

ไม่ใช่เรื่องยากที่จะชื่นชมที่ `localStorage' อาจไม่ใช่ทางออกที่ดีที่สุดสำหรับแอปพลิเคชันที่ 'เหมาะสม' นอกจากปัญหาสตริงดังกล่าวแล้ว ยังทำงานช้าสำหรับการทำงานที่จริงจัง เนื่องจากบล็อก 'เธรดหลัก' ทางเลือกอื่นกำลังจะมา เช่น KV Storage แต่สำหรับตอนนี้ โปรดระลึกไว้เสมอว่าควรคำนึงถึงการใช้งานโดยพิจารณาจากความเหมาะสม

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

ใช้งานได้ดีบน Android แต่ไม่ค่อยหรูหราสำหรับ iOS บน iPhone ส่งผลให้มีข้อความบนหน้าจอจำนวนมากเช่นนี้:

บน iPhone ส่งผลให้มีข้อความบนหน้าจอมากมาย
(ตัวอย่างขนาดใหญ่)

อย่างที่คุณสามารถจินตนาการได้ ฉันอยู่ไกลจากคนเดียวในการตำหนิ Apple ผ่าน WebKit เกี่ยวกับข้อบกพร่องนี้ ข้อบกพร่องที่เกี่ยวข้องอยู่ที่นี่

ในขณะที่เขียนจุดบกพร่องนี้มีวิธีแก้ปัญหาและโปรแกรมแก้ไข แต่ยังไม่สามารถเข้าสู่ iOS Safari ถูกกล่าวหาว่า iOS13 แก้ไขได้ แต่อยู่ในรุ่นเบต้าขณะที่ฉันเขียน

ดังนั้นสำหรับผลิตภัณฑ์ที่ใช้งานได้ขั้นต่ำของฉัน นั่นคือการจัดเก็บที่กล่าวถึง ถึงเวลาที่จะพยายามทำให้สิ่งต่างๆ 'เหมือนแอป' มากขึ้น!

App-I-Ness

ปรากฎว่าหลังจากพูดคุยกับผู้คนจำนวนมาก การระบุว่า 'การชอบแอป' หมายถึงอะไรนั้นค่อนข้างยาก

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

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

เช่นเดียวกับการโต้ตอบด้วยภาพหลายประเภทเหล่านี้ ความเรียบง่ายที่เห็นได้ชัดของพวกเขาปฏิเสธความซับซ้อนที่เกี่ยวข้องในการทำให้มันทำงานได้ดี

ต้องทำซ้ำสองสามครั้งเพื่อให้การเคลื่อนไหวถูกต้อง แต่ตรรกะพื้นฐานคือ:

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

วุ้ย มันซับซ้อนกว่าที่ฉันคิดในภาษาอังกฤษ — ไม่ต้องสนใจ JavaScript!

มีความซับซ้อนเพิ่มเติมในการพิจารณาและทดลองใช้ เช่น ความเร็วในการเปลี่ยน ในตอนแรก ไม่ชัดเจนว่าการเคลื่อนไหวด้วยความเร็วคงที่ (เช่น 20px ต่อ 20ms) หรือระยะเวลาคงที่สำหรับการเคลื่อนไหว (เช่น 0.2 วินาที) จะดูดีกว่าหรือไม่ แบบแรกมีความซับซ้อนมากขึ้นเล็กน้อย เนื่องจากความเร็วจำเป็นต้องคำนวณ 'ทันที' โดยพิจารณาจากระยะที่ผู้เล่นต้องเดินทาง — ระยะทางที่มากขึ้นต้องใช้ระยะเวลาการเปลี่ยนภาพนานขึ้น

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

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

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

ไม่ใช่ว่าสิ่งที่ส่งมาให้ใช้งานไม่ได้ แต่เป็นเพียงว่าไม่ใช่โซลูชันรหัสประเภทที่คุณจะแสดงบนอินเทอร์เน็ต โอ้รอ

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

 .io-EventLoader { position: absolute; top: 100%; margin-top: 5px; z-index: 100; width: 100%; opacity: 0; transition: all 0.2s; pointer-events: none; transform: translateY(-10px); [data-evswitcher-showing="true"] & { opacity: 1; pointer-events: auto; transform: none; } }

เมื่อ data-evswitcher-showing="true" ถูกสลับบนองค์ประกอบหลัก เมนูจะค่อยๆ จางลง แปลงกลับเป็นตำแหน่งเริ่มต้น และเหตุการณ์ตัวชี้จะเปิดใช้งานอีกครั้งเพื่อให้เมนูได้รับการคลิก

ระเบียบวิธีสไตล์ชีต ECSS

คุณจะสังเกตเห็นในโค้ดก่อนหน้านั้นว่าจากมุมมองการเขียน การแทนที่ CSS ซ้อนอยู่ภายในตัวเลือกพาเรนต์ นั่นเป็นวิธีที่ฉันชอบเขียนสไตล์ชีต UI เสมอ แหล่งความจริงแหล่งเดียวสำหรับตัวเลือกแต่ละตัวและการแทนที่ใดๆ สำหรับตัวเลือกนั้นที่ห่อหุ้มไว้ในวงเล็บปีกกาชุดเดียว เป็นรูปแบบที่ต้องใช้ตัวประมวลผล CSS (Sass, PostCSS, LESS, Stylus และอื่น ๆ ) แต่ฉันรู้สึกว่าเป็นวิธีเดียวที่ดีในการใช้ประโยชน์จากฟังก์ชันการซ้อน

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

การแบ่ง TypeScript

แม้จะไม่มีตัวประมวลผล CSS หรือภาษา superset เช่น Sass แต่ CSS ก็มีความสามารถในการนำเข้าไฟล์ CSS หนึ่งไฟล์ขึ้นไปไปยังอีกไฟล์หนึ่งโดยใช้คำสั่งนำเข้า:

 @import "other-file.css";

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

ข้อดีอีกอย่างของการใช้ TypeScript คือมันมีวิธีง่ายๆ ที่สวยงามในการแบ่งโค้ดเป็นไฟล์และนำเข้าเมื่อจำเป็น

ความสามารถนี้เป็นโมดูล JavaScript ดั้งเดิมที่ลงวันที่ล่วงหน้าและเป็นคุณสมบัติอำนวยความสะดวกที่ยอดเยี่ยม เมื่อ TypeScript ถูกคอมไพล์ มันก็เย็บกลับเป็นไฟล์ JavaScript เดียว หมายความว่าสามารถแบ่งรหัสแอปพลิเคชันออกเป็นไฟล์บางส่วนที่จัดการได้สำหรับการสร้างและนำเข้าไปยังไฟล์หลักได้อย่างง่ายดาย ด้านบนของ inout.ts หลักมีลักษณะดังนี้:

 /// <reference path="defaultData.ts" /> /// <reference path="splitTeams.ts" /> /// <reference path="deleteOrPaidClickMask.ts" /> /// <reference path="repositionSlat.ts" /> /// <reference path="createSlats.ts" /> /// <reference path="utils.ts" /> /// <reference path="countIn.ts" /> /// <reference path="loadFile.ts" /> /// <reference path="saveText.ts" /> /// <reference path="observerPattern.ts" /> /// <reference path="onBoard.ts" />

งานดูแลบ้านและองค์กรที่เรียบง่ายนี้ช่วยได้มาก

หลายเหตุการณ์

ในตอนแรก ฉันรู้สึกว่าจากมุมมองของการใช้งาน เหตุการณ์เดียว เช่น “Tuesday Night Football” ก็เพียงพอแล้ว ในสถานการณ์นั้น หากคุณโหลดเข้า/ออก คุณเพิ่งเพิ่ม/ลบหรือย้ายผู้เล่นเข้าหรือออก และนั่นก็คือ ไม่มีความคิดของหลายเหตุการณ์

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

ในตอนแรก ชุดข้อมูลเริ่มต้นจะมีลักษณะดังนี้:

 var defaultData = [ { name: "Daz", paid: false, marked: false, team: "", in: false }, { name: "Carl", paid: false, marked: false, team: "", in: false }, { name: "Big Dave", paid: false, marked: false, team: "", in: false }, { name: "Nick", paid: false, marked: false, team: "", in: false } ];

อาร์เรย์ที่มีวัตถุสำหรับผู้เล่นแต่ละคน

หลังจากแฟคตอริ่งในหลายเหตุการณ์แล้ว แก้ไขให้มีลักษณะดังนี้:

 var defaultDataV2 = [ { EventName: "Tuesday Night Footy", Selected: true, EventData: [ { name: "Jack", marked: false, team: "", in: false }, { name: "Carl", marked: false, team: "", in: false }, { name: "Big Dave", marked: false, team: "", in: false }, { name: "Nick", marked: false, team: "", in: false }, { name: "Red Boots", marked: false, team: "", in: false }, { name: "Gaz", marked: false, team: "", in: false }, { name: "Angry Martin", marked: false, team: "", in: false } ] }, { EventName: "Friday PM Bank Job", Selected: false, EventData: [ { name: "Mr Pink", marked: false, team: "", in: false }, { name: "Mr Blonde", marked: false, team: "", in: false }, { name: "Mr White", marked: false, team: "", in: false }, { name: "Mr Brown", marked: false, team: "", in: false } ] }, { EventName: "WWII Ladies Baseball", Selected: false, EventData: [ { name: "C Dottie Hinson", marked: false, team: "", in: false }, { name: "P Kit Keller", marked: false, team: "", in: false }, { name: "Mae Mordabito", marked: false, team: "", in: false } ] } ];

ข้อมูลใหม่เป็นอาร์เรย์ที่มีอ็อบเจ็กต์สำหรับแต่ละเหตุการณ์ จากนั้นในแต่ละเหตุการณ์จะเป็นคุณสมบัติ EventData ที่เป็นอาร์เรย์ที่มีวัตถุของผู้เล่นเหมือนเมื่อก่อน

ใช้เวลานานกว่ามากในการพิจารณาใหม่ว่าอินเทอร์เฟซสามารถจัดการกับความสามารถใหม่นี้ได้อย่างไร

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

ต้นแบบของการออกแบบแอพที่แก้ไขแล้ว
แบบจำลองการออกแบบที่ปรับปรุงใหม่ (ตัวอย่างขนาดใหญ่)

มันจะไม่ชนะรางวัล แต่มันก็น่าจับตามองมากกว่าที่เริ่มต้นอย่างแน่นอน

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

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

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

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

สรุป: รหัสของฉันเหม็น

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

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

Looking now at the code shipped, it is a less than ideal hodge-bodge of clean observer pattern and bog-standard event listeners calling functions. In the main inout.ts file there are over 20 querySelector method calls; hardly a poster child for modern application development!

I was pretty sore about this at the time, especially as at the outset I was aware this was a trap I didn't want to fall into. However, in the months that have since passed, I've become more philosophical about it.

The final post in this series reflects on finding the balance between silvery-towered code idealism and getting things shipped. It also covers the most important lessons learned during this process and my future aspirations for application development.