การเข้ารหัส Cabin Fever: บทช่วยสอน Back-end ของ Node.js
เผยแพร่แล้ว: 2022-03-11การล็อกดาวน์จากโควิด-19 ทำให้พวกเราหลายคนต้องกักตัวอยู่แต่ในบ้าน หวังว่าแค่ไข้ในห้องโดยสารจะเป็นไข้ที่แย่ที่สุดที่เราจะประสบ พวกเราหลายคนบริโภคเนื้อหาวิดีโอมากกว่าที่เคย แม้ว่าการออกกำลังกายจะมีความสำคัญเป็นพิเศษในตอนนี้ แต่ในบางครั้ง ก็มีความคิดถึงถึงความหรูหราของรีโมทคอนโทรลที่ล้าสมัยเมื่อแล็ปท็อปอยู่ไกลเกินเอื้อม
นั่นคือที่มาของโปรเจ็กต์นี้: โอกาสในการเปลี่ยนสมาร์ทโฟนเครื่องใดก็ได้ แม้กระทั่งสมาร์ทโฟนรุ่นเก่าที่ไม่มีประโยชน์หากไม่มีการอัปเดต ให้กลายเป็นรีโมตที่ใช้งานสะดวกสำหรับ Netflix/YouTube/Amazon Prime Video/ฯลฯ ถัดไป การดื่มสุราดู นอกจากนี้ยังเป็นบทช่วยสอนส่วนหลังของ Node.js: โอกาสในการเรียนรู้พื้นฐานของแบ็กเอนด์ JavaScript โดยใช้เฟรมเวิร์ก Express และเอ็นจิ้นเทมเพลต Pug (เดิมคือ Jade)
หากฟังดูน่ากลัว โปรเจ็กต์ Node.js ที่สมบูรณ์จะถูกนำเสนอในตอนท้าย ผู้อ่านต้องการเพียงแค่เรียนรู้มากเท่าที่พวกเขาสนใจในการเรียนรู้ และจะมีคำอธิบายที่อ่อนโยนกว่าเกี่ยวกับพื้นฐานบางประการจำนวนพอสมควรเพื่อให้ผู้อ่านที่มีประสบการณ์มากขึ้นสามารถข้ามไปได้
ทำไมไม่เพียงแค่...?
ผู้อ่านอาจสงสัยว่า "ทำไมต้องเขียนโค้ด Node.js แบ็กเอนด์" (นอกจากโอกาสในการเรียนรู้แน่นอน) “มันไม่มีแอพอยู่แล้วเหรอ?”
แน่นอน—มีมากมาย แต่มีเหตุผลหลักสองประการที่อาจไม่เป็นที่ต้องการ:
- สำหรับผู้ที่พยายามปรับเปลี่ยนโทรศัพท์รุ่นเก่า นี่อาจ ไม่ใช่ตัวเลือกอีกต่อไป เช่นเดียวกับอุปกรณ์ Windows Phone 8.1 ที่ฉันต้องการใช้ (ร้านแอปปิดตัวลงอย่างเป็นทางการในปลายปี 2019)
- ความไว้วางใจ (หรือขาดมัน) เช่นเดียวกับแอปจำนวนมากที่พบในแพลตฟอร์มมือถือใด ๆ พวกเขามักจะมาพร้อมกับความต้องการของผู้ใช้ที่ให้สิทธิ์มากกว่าที่แอปต้องการสำหรับสิ่งที่ตั้งใจจะทำ แต่แม้ว่าแง่มุมนี้จะถูกจำกัดอย่างเหมาะสม แต่ลักษณะของแอปควบคุมระยะไกลหมายความว่าผู้ใช้ยังคงต้องเชื่อมั่นว่านักพัฒนาแอปจะไม่ใช้สิทธิ์ของตนในทางที่ผิดบนเดสก์ท็อปของโซลูชันโดยรวมสปายแวร์หรือมัลแวร์อื่นๆ
ปัญหาเหล่านี้มีมาช้านานแล้วและยังเป็นแรงจูงใจสำหรับโครงการที่คล้ายกันในปี 2014 ที่พบใน GitHub nvm
ทำให้ง่ายต่อการติดตั้ง Node.js เวอร์ชันเก่า และแม้ว่าการขึ้นต่อกันบางอย่างจำเป็นต้องอัปเกรด แต่ Node.js ก็มีชื่อเสียงในด้านความเข้ากันได้แบบย้อนหลัง
น่าเสียดายที่ bitrot ชนะ แนวทางที่ดื้อรั้นและความเข้ากันได้ของ Node.js แบ็คเอนด์ไม่ตรงกับการเลิกใช้งานที่ไม่มีที่สิ้นสุดและการวนซ้ำการพึ่งพาที่เป็นไปไม่ได้ในเวอร์ชันเก่าของ Grunt, Bower และส่วนประกอบอื่น ๆ อีกนับสิบ หลายชั่วโมงต่อมา เป็นที่ชัดเจนว่าการเริ่มต้นใหม่จากศูนย์จะง่ายกว่ามาก—คำแนะนำของผู้เขียนเองที่ต่อต้านการคิดค้นล้อใหม่แม้ว่า
Gizmos ใหม่จากรุ่นเก่า: เปลี่ยนโทรศัพท์เป็นรีโมทคอนโทรลโดยใช้ Node.js Back End
ก่อนอื่น โปรดทราบว่าโปรเจ็กต์ Node.js นี้เฉพาะสำหรับ Linux—ที่พัฒนาและทดสอบบน Linux Mint 19 และ Linux Mint 19.3 โดยเฉพาะ—แต่สามารถเพิ่มการรองรับสำหรับแพลตฟอร์มอื่นๆ ได้อย่างแน่นอน มัน อาจ ทำงานบน Mac แล้ว
สมมติว่ามีการติดตั้ง Node.js เวอร์ชันใหม่ และพรอมต์คำสั่งเปิดอยู่ในไดเร็กทอรีใหม่ที่จะทำหน้าที่เป็นรูทของโปรเจ็กต์ เราก็พร้อมที่จะเริ่มต้นกับ Express:
npx express-generator --view=pug
หมายเหตุ: ที่นี่ npx
เป็นเครื่องมือที่มีประโยชน์ซึ่งมาพร้อมกับ npm
ซึ่งเป็นตัวจัดการแพ็คเกจ Node.js ที่มาพร้อมกับ Node.js เรากำลังใช้มันเพื่อเรียกใช้โปรแกรมสร้างโครงร่างแอปพลิเคชันของ Express ในขณะที่เขียนนี้ ตัวสร้างสร้างโปรเจ็กต์ Express/Node.js ที่ตามค่าเริ่มต้น ยังคงดึงเอ็นจิ้นเทมเพลตที่เรียกว่า Jade แม้ว่าโปรเจ็กต์ Jade จะเปลี่ยนชื่อตัวเองเป็น “Pug” ตั้งแต่เวอร์ชัน 2.0 เป็นต้นไป ดังนั้นเพื่อให้เป็นปัจจุบันและใช้งาน Pug ได้ทันที—และหลีกเลี่ยงคำเตือนการเลิกใช้งาน—เราใช้ --view=pug
ตัวเลือกบรรทัดคำสั่งสำหรับสคริปต์ตัวสร้าง express-generator
ที่ทำงานโดย npx
เมื่อเสร็จแล้ว เราจำเป็นต้องติดตั้งแพ็คเกจบางตัวจากรายการการพึ่งพาที่เติมใหม่ของโปรเจ็กต์ Node.js ใน package.json
วิธีดั้งเดิมในการทำเช่นนี้คือการรัน npm i
( i
สำหรับ “install”) แต่บางคนก็ยังชอบความเร็วของ Yarn มากกว่า ดังนั้นหากคุณได้ติดตั้งไว้ ให้รัน yarn
โดยไม่มีพารามิเตอร์
ในกรณีนี้ ควรปลอดภัยที่จะเพิกเฉยต่อคำเตือนการเลิกใช้งาน (หวังว่าจะได้รับการแก้ไขเร็วๆ นี้) จากการอ้างอิงย่อยของ Pug ตราบใดที่การเข้าถึงยังคงอยู่ตามความจำเป็นบนเครือข่ายท้องถิ่น
การ yarn start
อย่างรวดเร็วหรือ npm start
ตามด้วยการนำทางไปยัง localhost:3000
ในเบราว์เซอร์ แสดงให้เห็นว่าแบ็กเอนด์ Node.js พื้นฐานแบบ Express ของเราใช้งานได้ เราสามารถฆ่ามันด้วย Ctrl+C
บทช่วยสอน Back-end ของ Node.js ขั้นตอนที่ 2: วิธีส่งการกดแป้นบนเครื่องโฮสต์
เมื่อส่วน ระยะไกล เสร็จไปแล้วครึ่งทาง เรามาสนใจส่วน ควบคุม กัน เราต้องการบางสิ่งที่สามารถควบคุมเครื่องโดยทางโปรแกรมได้ เราจะเรียกใช้แบ็คเอนด์ Node.js ของเรา โดยแกล้งทำเป็นว่ากำลังกดปุ่มบนแป้นพิมพ์
สำหรับสิ่งนั้น เราจะติดตั้ง xdotool
โดยใช้คำแนะนำอย่างเป็นทางการ การทดสอบตัวอย่างคำสั่งอย่างรวดเร็วในเทอร์มินัล:
xdotool search "Mozilla Firefox" windowactivate --sync key --clearmodifiers ctrl+l
…ควรทำตามที่กล่าวไว้โดยสมบูรณ์ สมมติว่า Mozilla Firefox เปิดอยู่ในขณะนั้น ดีแล้ว! ง่ายที่จะให้โปรเจ็กต์ Node.js ของเราเรียกใช้เครื่องมือบรรทัดคำสั่งเช่น xdotool
อย่างที่เราจะได้เห็นในเร็วๆ นี้
บทช่วยสอน Back-end ของ Node.js ขั้นตอนที่ 3: การออกแบบฟีเจอร์
สิ่งนี้อาจไม่เป็นจริงสำหรับทุกคน แต่โดยส่วนตัวแล้ว ฉันพบว่ารีโมตคอนโทรลที่ทันสมัยจำนวนมากมีปุ่มมากกว่าที่ฉันเคยใช้ประมาณห้าเท่า สำหรับโปรเจ็กต์นี้ เรากำลังดูเลย์เอาต์แบบเต็มหน้าจอที่มีปุ่มขนาดใหญ่ 3 ต่อ 3 ที่กำหนดเป้าหมายได้ง่าย ขึ้นอยู่กับความชอบส่วนตัวว่าปุ่มทั้งเก้านั้นเป็นอย่างไร
ปรากฎว่าแป้นพิมพ์ลัดสำหรับฟังก์ชันที่ง่ายที่สุดนั้นไม่เหมือนกันใน Netflix, YouTube และ Amazon Prime Video บริการเหล่านี้ไม่สามารถใช้งานได้กับคีย์สื่อทั่วไปเช่นแอปเครื่องเล่นเพลงแบบเนทีฟ นอกจากนี้ ฟังก์ชันบางอย่างอาจไม่สามารถใช้ได้กับบริการทั้งหมด
ดังนั้นสิ่งที่เราต้องทำคือกำหนดเลย์เอาต์การควบคุมระยะไกลที่แตกต่างกันสำหรับแต่ละบริการและจัดเตรียมวิธีการสลับไปมาระหว่างบริการเหล่านั้น
การกำหนดเลย์เอาต์การควบคุมระยะไกลและการจับคู่กับแป้นพิมพ์ลัด
มาดูการสร้างต้นแบบอย่างรวดเร็วด้วยการตั้งค่าล่วงหน้าจำนวนหนึ่ง เราจะใส่ไว้ใน common/preset_commands.js
—“common” เพราะเราจะรวมข้อมูลนี้จากไฟล์มากกว่าหนึ่งไฟล์:
module.exports = { // We could use ️ but some older phones (eg, Android 5.1.1) won't show it, hence ️ instead 'Netflix': { commands: { '-': 'Escape', '+': 'f', '': 'Up', '⇤': 'XF86Back', '️': 'Return', '': 'Down', '': 'Left', '': 'Right', '': 'm', }, }, 'YouTube': { commands: { '⇤': 'shift+p', '⇥': 'shift+n', '': 'Up', 'CC': 'c', '️': 'k', '': 'Down', '': 'j', '': 'l', '': 'm', }, }, 'Amazon Prime Video': { window_name_override: 'Prime Video', commands: { '⇤': 'Escape', '+': 'f', '': 'Up', 'CC': 'c', '️': 'space', '': 'Down', '': 'Left', '': 'Right', '': 'm', }, }, 'Generic / Music Player': { window_name_override: '', commands: { '⇤': 'XF86AudioPrev', '⇥': 'XF86AudioNext', '': 'XF86AudioRaiseVolume', '': 'r', '️': 'XF86AudioPlay', '': 'XF86AudioLowerVolume', '': 'Left', '': 'Right', '': 'XF86AudioMute', }, }, };
ค่าคีย์โค้ดสามารถพบได้โดยใช้ xev
(สำหรับฉัน ไม่สามารถค้นพบ "ปิดเสียง" และ "เล่นเสียง" ได้โดยใช้วิธีนี้ ดังนั้นฉันจึงปรึกษารายการคีย์สื่อด้วย)
ผู้อ่านอาจสังเกตเห็นความแตกต่างในกรณีระหว่าง space
และ Return
— โดยไม่คำนึงถึงเหตุผลนี้ รายละเอียดนี้ต้องได้รับเกียรติเพื่อให้ xdotool
ทำงานได้อย่างถูกต้อง ในเรื่องนี้ เรามีคำจำกัดความสองสามคำที่เขียนไว้อย่างชัดเจน—เช่น shift+p
แม้ว่า P
จะทำงาน—เพียงเพื่อให้ความตั้งใจของเราชัดเจน
บทช่วยสอน Back-end ของ Node.js ขั้นตอนที่ 4: จุดสิ้นสุด “คีย์” ของ API ของเรา (Pardon the Pun)
เราต้องการจุดปลายเพื่อ POST
ซึ่งจะจำลองการกดแป้นพิมพ์โดยใช้ xdotool
เนื่องจากเราจะมีกลุ่มคีย์ต่างๆ ที่เราสามารถส่งได้ (หนึ่งรายการสำหรับแต่ละบริการ) เราจะเรียกปลายทางสำหรับกลุ่มใด group
หนึ่งโดยเฉพาะ เราจะกำหนดวัตถุประสงค์ปลายทางของ users
ที่สร้างขึ้นใหม่โดยเปลี่ยนชื่อ routes/users.js
เป็น routes/group.js
และทำการเปลี่ยนแปลงที่เกี่ยวข้องใน app.js
:
// ... var indexRouter = require('./routes/index'); var groupRouter = require('./routes/group'); // ... app.use('/', indexRouter); app.use('/group', groupRouter); // ...
ฟังก์ชัน หลัก คือการใช้ xdotool
ผ่านการเรียกเชลล์ของระบบใน routes/group.js
เราจะฮาร์ดโค้ด YouTube
เป็นเมนูตัวเลือกในขณะนี้ เพื่อวัตถุประสงค์ในการทดสอบเท่านั้น
const express = require('express'); const router = express.Router(); const debug = require('debug')('app'); const cp = require('child_process'); const preset_commands = require('../common/preset_commands'); /* POST keystroke to simulate */ router.post('/', function(req, res, next) { const keystroke_name = req.body.keystroke_name; const keystroke_code = preset_commands['YouTube'].commands[keystroke_name]; const final_command = `xdotool \ search "YouTube" \ windowactivate --sync \ key --clearmodifiers ${keystroke_code}`; debug(`Executing ${final_command}`); cp.exec(final_command, (err, stdout, stderr) => { debug(`Executed ${keystroke_name}`); return res.redirect(req.originalUrl); }); }); module.exports = router;
ที่นี่ เราคว้าคีย์ "ชื่อ" ที่ร้องขอจากเนื้อหาของคำขอ POST
( req.body
) ภายใต้พารามิเตอร์ที่ชื่อ keystroke_name
มันก็จะประมาณนี้ ️
. จากนั้นเราใช้สิ่งนั้นเพื่อค้นหาโค้ดที่เกี่ยวข้องจากออบเจ็กต์ commands
ของ preset_commands['YouTube']
คำสั่งสุดท้ายอยู่บนมากกว่าหนึ่งบรรทัด ดังนั้น \
s ที่ส่วนท้ายของแต่ละบรรทัดจะรวมชิ้นส่วนทั้งหมดเป็นคำสั่งเดียว:
-
search "YouTube"
เรียกหน้าต่างแรกที่มีคำว่า “YouTube” ในชื่อ -
windowactivate --sync
เปิดใช้งานหน้าต่างที่ดึงมาและรอจนกว่าจะพร้อมที่จะรับการกดแป้นพิมพ์ -
key --clearmodifiers ${keystroke_code}
ส่งการกดแป้นพิมพ์ ตรวจสอบให้แน่ใจว่าได้ล้างคีย์ตัวแก้ไขชั่วคราว เช่น Caps Lock ที่อาจรบกวนสิ่งที่เรากำลังส่ง
ณ จุดนี้ โค้ดจะถือว่าเรากำลังป้อนข้อมูลที่ถูกต้อง ซึ่งเป็นสิ่งที่เราจะระมัดระวังมากขึ้นในภายหลัง
เพื่อความง่าย โค้ดจะถือว่ามีหน้าต่างแอปพลิเคชันเพียงหน้าต่างเดียวที่เปิดขึ้นโดยมีคำว่า “YouTube” อยู่ในชื่อ หากมีมากกว่าหนึ่งที่ตรงกัน ก็ไม่รับประกันว่าเราจะส่งการกดแป้นพิมพ์ไปยังหน้าต่างที่ต้องการ หากเป็นปัญหา อาจช่วยเปลี่ยนชื่อหน้าต่างได้ง่ายๆ โดยเปลี่ยนแท็บเบราว์เซอร์ในหน้าต่างทั้งหมด ยกเว้นหน้าต่างที่ควบคุมจากระยะไกล
เมื่อพร้อมแล้ว เราก็สามารถเริ่มต้นเซิร์ฟเวอร์ของเราได้อีกครั้ง แต่คราวนี้เมื่อเปิดใช้งานการดีบั๊ก เพื่อให้เห็นผลลัพธ์ของการเรียกการ debug
ของเรา ในการทำเช่นนั้น เพียงเรียกใช้ DEBUG=old-fashioned-remote:* yarn start
หรือ DEBUG=old-fashioned-remote:* npm start
เมื่อมันทำงาน เล่นวิดีโอบน YouTube เปิดหน้าต่างเทอร์มินัลอื่น และลองเรียก cURL:
curl --data "keystroke_name=️" http://localhost:3000/group
ที่ส่งคำขอ POST
พร้อมชื่อการกดแป้นพิมพ์ที่ร้องขอในเนื้อหาไปยังเครื่องในพื้นที่ของเราที่พอร์ต 3000
พอร์ตแบ็คเอนด์ของเรากำลังฟังอยู่ การรันคำสั่งนั้นควรแสดงหมายเหตุเกี่ยวกับ Executing
and Executed
ในหน้าต่าง npm
และที่สำคัญกว่านั้น ให้เปิดเบราว์เซอร์ขึ้นมาและหยุดวิดีโอชั่วคราว การดำเนินการคำสั่งนั้นอีกครั้งควรให้ผลลัพธ์เดียวกันและยกเลิกการหยุดชั่วคราว
บทช่วยสอน Back-end ของ Node.js ขั้นตอนที่ 5: เลย์เอาต์การควบคุมระยะไกลหลายรายการ
แบ็คเอนด์ของเรายังไม่เสร็จ เรายังต้องการเพื่อให้สามารถ:
- สร้างรายการเลย์เอาต์การควบคุมระยะไกลจาก
preset_commands
- สร้างรายชื่อ "ชื่อ" ของการกดแป้นพิมพ์เมื่อเราเลือกเลย์เอาต์การควบคุมระยะไกลโดยเฉพาะ (เรายังสามารถเลือกใช้
common/preset_commands.js
ได้โดยตรงที่ส่วนหน้า เนื่องจากเป็น JavaScript แล้ว และกรองไว้ที่นั่น นั่นเป็นหนึ่งในข้อดีที่เป็นไปได้ของส่วนหลังของ Node.js เราแค่ไม่ได้ใช้ที่นี่ .)
คุณลักษณะทั้งสองนี้เป็นที่ที่บทช่วยสอนส่วนหลังของ Node.js ตัดกับส่วนหน้าที่ใช้ Pug ที่เราจะสร้าง
การใช้ Pug Templating เพื่อแสดงรายการรีโมทคอนโทรล
ส่วนหลังของสมการหมายถึงการแก้ไข routes/index.js
ให้มีลักษณะดังนี้:
const express = require('express'); const router = express.Router(); const preset_commands = require('../common/preset_commands'); /* GET home page. */ router.get('/', function(req, res, next) { const group_names = Object.keys(preset_commands); res.render('index', { title: 'Which Remote?', group_names, portrait_css: `.group_bar { height: calc(100%/${Math.min(4, group_names.length)}); line-height: calc(100vh/${Math.min(4, group_names.length)}); }`, landscape_css: `.group_bar { height: calc(100%/${Math.min(2, group_names.length)}); line-height: calc(100vh/${Math.min(2, group_names.length)}); }`, }); }); module.exports = router;
ที่นี่ เราคว้าชื่อเลย์เอาต์ของรีโมตคอนโทรล ( group_names
) โดยการเรียก Object.keys
ในไฟล์ preset_commands
ของเรา จากนั้นเราจะส่งพวกเขาและข้อมูลอื่น ๆ ที่เราต้องใช้ไปยังเครื่องมือเทมเพลต Pug ที่เรียกโดยอัตโนมัติผ่าน res.render()
ระวังอย่าสับสนความหมายของ keys
ที่นี่กับการกดแป้นที่เราส่ง: Object.keys Object.keys
อาร์เรย์ (รายการที่เรียงลำดับ) ที่มี คีย์ ทั้งหมดของ คู่คีย์-ค่า ที่ประกอบเป็นอ็อบเจ็กต์ใน JavaScript:

const my_object = { 'a key' : 'its corresponding value' , 'another key' : 'its separate corresponding value' , };
หากเราดูที่ common/preset_commands.js
เราจะเห็นรูปแบบด้านบน และ คีย์ ของเรา (ในแง่ของวัตถุ) คือชื่อกลุ่มของเรา: 'Netflix'
, 'YouTube'
ฯลฯ ค่าที่สอดคล้องกันไม่ใช่ สตริงอย่างง่ายตามที่ my_object
มีด้านบน—พวกมันคืออ็อบเจกต์ทั้งหมดด้วยตัวมันเอง มีคีย์ของตัวเอง เช่น commands
และอาจเป็น window_name_override
CSS แบบกำหนดเองที่ถูกส่งผ่านมาที่นี่ เป็นการแฮ็กเล็กน้อย เหตุผลที่เราต้องการมันเลย แทนที่จะใช้โซลูชันที่ทันสมัยและอิงตาม flexbox ก็เพื่อให้เข้ากันได้ดีขึ้นกับโลกที่ยอดเยี่ยมของเบราว์เซอร์มือถือในรูปแบบที่เก่ากว่าที่ยอดเยี่ยมยิ่งขึ้น ในกรณีนี้ สิ่งสำคัญที่ควรทราบคือในโหมดแนวนอน เราทำให้ปุ่มมีขนาดใหญ่โดยแสดงตัวเลือกไม่เกินสองตัวเลือกต่อหนึ่งหน้าจอ ในโหมดแนวตั้งสี่
แต่นั่นจะเปลี่ยนเป็น HTML เพื่อส่งไปยังเบราว์เซอร์ที่ไหน? นั่นคือสิ่งที่ views/index.pug
มา ซึ่งเราต้องการให้มีลักษณะดังนี้:
extends layout block header_injection style(media='(orientation: portrait)') #{portrait_css} style(media='(orientation: landscape)') #{landscape_css} block content each group_name in group_names span(class="group_bar") a(href='/group/?group_name=' + group_name) #{group_name}
บรรทัดแรกสำคัญมาก: เลย์เอาต์ที่ extends layout
หมายความว่า Pug จะใช้ไฟล์นี้ในบริบทของ views/layout.pug
ซึ่งเป็นเทมเพลตหลักที่เราจะนำมาใช้ซ้ำที่นี่และในมุมมองอื่นด้วย เราจะต้องเพิ่มสองสามบรรทัดหลังบรรทัด link
เพื่อให้ไฟล์สุดท้ายมีลักษณะดังนี้:
doctype html html head title= title link(rel='stylesheet', href='/stylesheets/style.css') block header_injection meta(name='viewport', content='user-scalable=no') body block content
เราจะไม่พูดถึงพื้นฐานของ HTML ที่นี่ แต่สำหรับผู้อ่านที่ไม่คุ้นเคย โค้ด Pug นี้จะสะท้อนโค้ด HTML มาตรฐานที่พบได้ทุกที่ ลักษณะการสร้าง เทมเพลต เริ่มต้นด้วย title= title
ซึ่งตั้งค่าหัวเรื่อง HTML เป็นค่าใดก็ตามที่สอดคล้องกับคีย์ title
ของวัตถุที่เราส่งผ่าน Pug ผ่าน res.render
เราสามารถเห็นมุมมองที่แตกต่างกันของการสร้างเทมเพลตสองบรรทัดในภายหลังด้วย block
ที่เราตั้งชื่อ header_injection
บล็อกเช่นนี้คือตัวยึดตำแหน่งที่สามารถแทนที่ด้วยเทมเพลตที่ขยายส่วนขยายปัจจุบัน (ไม่เกี่ยวข้อง meta
ไลน์เป็นเพียงวิธีแก้ปัญหาอย่างรวดเร็วสำหรับเบราว์เซอร์มือถือ ดังนั้นเมื่อผู้ใช้แตะตัวควบคุมระดับเสียงหลาย ๆ ครั้งติดต่อกัน โทรศัพท์จะละเว้นจากการซูมเข้าหรือออก)
กลับไปที่ block
ของเรา: นี่คือสาเหตุที่ views/index.pug
กำหนด block
ของตัวเองด้วยชื่อเดียวกับที่พบใน views/layout.pug
ในกรณีของ header_injection
ทำให้เราสามารถใช้ CSS เฉพาะสำหรับการวางโทรศัพท์ในแนวตั้งหรือแนวนอน
content
เป็นที่ที่เราวางส่วนหลักที่มองเห็นได้ของหน้าเว็บ ซึ่งในกรณีนี้:
- วนซ้ำผ่านอาร์เรย์
group_names
ที่เราส่งผ่าน - สร้างองค์ประกอบ
<span>
สำหรับแต่ละองค์ประกอบโดยใช้คลาส CSSgroup_bar
และ - สร้างลิงค์ภายในแต่ละ
<span>
ตามgroup_name
คลาส CSS group_bar
ที่เราสามารถกำหนดในไฟล์ที่ดึงเข้ามาผ่าน views/layout.pug
กล่าวคือ public/stylesheets/style.css
:
html, body, form { padding: 0; margin: 0; height: 100%; font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; } .group_bar, .group_bar a, .remote_button { box-sizing: border-box; border: 1px solid white; color: greenyellow; background-color: black; } .group_bar { width: 100%; font-size: 6vh; text-align: center; display: inline-block; } .group_bar a { text-decoration: none; display: block; }
ณ จุดนี้ หาก npm start
ยังคงทำงานอยู่ การไปที่ http://localhost:3000/
ในเบราว์เซอร์เดสก์ท็อปควรแสดงปุ่มขนาดใหญ่มากสองปุ่มสำหรับ Netflix และ YouTube โดยที่เหลือจะมีให้โดยเลื่อนลงมา
แต่ถ้าเราคลิกไปที่จุดนี้ มันจะไม่ทำงาน เพราะเรายังไม่ได้กำหนดเส้นทางที่พวกเขาลิงก์ไป (การ GET
ting ของ /group
)
กำลังแสดงเค้าโครงรีโมทคอนโทรลที่เลือก
ในการทำเช่นนั้น เราจะเพิ่มสิ่งนี้ใน routes/group.js
ก่อนบรรทัด module.exports
สุดท้าย:
router.get('/', function(req, res, next) { const group_name = req.query.group_name || ''; const group = preset_commands[group_name]; return res.render('group', { keystroke_names: Object.keys(group.commands), group_name, title: `${group_name.match(/([AZ])/g).join('')}-Remote` }); });
สิ่งนี้จะได้รับชื่อกลุ่มที่ส่งไปยังปลายทาง (เช่น โดยใส่ ?group_name=Netflix
ต่อท้าย /group/
) และใช้สิ่งนั้นเพื่อรับค่าของ commands
จากกลุ่มที่เกี่ยวข้อง ค่านั้น ( group.commands
) เป็นอ็อบเจ็กต์ และคีย์ของอ็อบเจกต์นั้นคือชื่อ ( keystroke_names
) ที่เราจะแสดงบนเลย์เอาต์การควบคุมระยะไกลของเรา
หมายเหตุ: นักพัฒนาที่ไม่มีประสบการณ์จะไม่ต้องลงลึกถึงรายละเอียดว่ามันทำงานอย่างไร แต่ค่าของ title
นั้นใช้นิพจน์ทั่วไปเล็กน้อยเพื่อเปลี่ยนชื่อกลุ่ม/เลย์เอาต์ให้เป็นตัวย่อ ตัวอย่างเช่น รีโมต YouTube ของเราจะมีชื่อเบราว์เซอร์ YT-Remote
ด้วยวิธีนี้ หากเรากำลังดีบักบนเครื่องโฮสต์ของเราก่อนที่จะลองใช้งานบนโทรศัพท์ เราจะไม่ให้ xdotool
คว้าหน้าต่างเบราว์เซอร์การควบคุมระยะไกล แทนที่จะเป็นหน้าต่างที่เราพยายามควบคุม ในขณะเดียวกัน บนโทรศัพท์ของเรา ชื่อเรื่องจะดีและสั้น หากเราต้องการคั่นหน้าการควบคุมระยะไกล
เช่นเดียวกับการเผชิญหน้า res.render
ครั้งก่อน ข้อมูลนี้กำลังส่งข้อมูลเพื่อคลุกเคล้ากับเทมเพลต views/group.pug
เราจะสร้างไฟล์นั้นและเติมด้วยสิ่งนี้:
extends layout block header_injection script(type='text/javascript', src='/javascript/group-client.js') block content form(action="/group?group_name=" + group_name, method="post") each keystroke_name in keystroke_names input(type="submit", name="keystroke_name", value=keystroke_name, class="remote_button")
เช่นเดียวกับ views/index.pug
เรากำลังแทนที่ทั้งสองบล็อกจาก views/layout.pug
คราวนี้ เราไม่ได้ใส่ CSS ไว้ที่ส่วนหัว แต่เป็น JavaScript ฝั่งไคลเอ็นต์ ซึ่งเราจะพูดถึงในไม่ช้า (และใช่ ในช่วงเวลาแห่งความคลั่งไคล้ ฉันเปลี่ยนชื่อ javascripts
ที่มีพหูพจน์ไม่ถูกต้อง …)
content
หลักที่นี่คือรูปแบบ HTML ที่สร้างจากปุ่มส่งหลายปุ่ม ปุ่มหนึ่งสำหรับแต่ละ keystroke_name
แต่ละปุ่มจะส่งแบบฟอร์ม (ส่งคำขอ POST
) โดยใช้ชื่อการกดแป้นพิมพ์ที่แสดงเป็นค่าที่ส่งไปพร้อมกับแบบฟอร์ม
เราต้องการ CSS เพิ่มอีกเล็กน้อยในไฟล์สไตล์ชีตหลักของเรา:
.remote_button { float: left; width: calc(100%/3); height: calc(100%/3); font-size: 12vh; }
ก่อนหน้านี้ เมื่อเราตั้งค่าปลายทาง เราจัดการคำขอเสร็จแล้วด้วย:
return res.redirect(req.originalUrl);
ซึ่งหมายความว่าเมื่อเบราว์เซอร์ส่งแบบฟอร์ม แบ็คเอนด์ของ Node.js จะตอบสนองโดยบอกให้เบราว์เซอร์กลับไปที่หน้าที่ส่งแบบฟอร์มมา เช่น เลย์เอาต์หลักของรีโมตคอนโทรล มันจะดูสง่างามมากขึ้นโดยไม่ต้องเปลี่ยนหน้า อย่างไรก็ตาม เราต้องการความเข้ากันได้สูงสุดกับโลกที่แปลกประหลาดและมหัศจรรย์ของเบราว์เซอร์มือถือที่เสื่อมสภาพ ด้วยวิธีนี้ แม้ว่า front-end JavaScript จะไม่ทำงานเลย โปรเจ็กต์แบ็คเอนด์ Node.js ของเราก็ยัง ควร ทำงาน
Dash of Front-end JavaScript
ข้อเสียของการใช้แบบฟอร์มเพื่อส่งการกดแป้นคือเบราว์เซอร์ต้องรอ จากนั้นจึงดำเนินการไปกลับเพิ่มเติม: หน้าและการอ้างอิงจะต้องได้รับการร้องขอจากส่วนหลังของ Node.js และนำส่ง จากนั้นเบราว์เซอร์จะต้องแสดงผลอีกครั้ง
ผู้อ่านอาจสงสัยว่าสิ่งนี้จะมีผลกระทบมากน้อยเพียงใด ท้ายที่สุด หน้าเพจมีขนาดเล็ก การขึ้นต่อกันของหน้านั้นน้อยมาก และโปรเจ็กต์ Node.js สุดท้ายของเราจะทำงานผ่านการเชื่อมต่อ wifi ในพื้นที่ ควรเป็นการตั้งค่าเวลาแฝงต่ำใช่ไหม
ตามที่ปรากฎ—อย่างน้อยเมื่อทำการทดสอบบนสมาร์ทโฟนรุ่นเก่าที่ใช้ Windows Phone 8.1 และ Android 4.4.2— ผลกระทบที่เห็นได้ชัดคือในกรณีทั่วไปของการแตะอย่างรวดเร็วเพื่อเพิ่มหรือลดระดับเสียงของการเล่นลงเล็กน้อย JavaScript สามารถช่วยได้ ที่นี่ โดยไม่ต้องละทิ้งทางเลือกที่สวยงามของ POST
ด้วยตนเองผ่านแบบฟอร์ม HTML
ณ จุดนี้ JavaScript ไคลเอนต์สุดท้ายของเรา (ที่จะใส่ใน public/javascript/group-client.js
) จะต้องเข้ากันได้กับเบราว์เซอร์มือถือรุ่นเก่าที่ไม่รองรับอีกต่อไป แต่เราไม่ต้องการอะไรมาก:
(function () { function form_submit(event) { var request = new XMLHttpRequest(); request.open('POST', window.location.pathname + window.location.search, true); request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); request.send('keystroke_name=' + encodeURIComponent(event.target.value)); event.preventDefault(); } window.addEventListener("DOMContentLoaded", function() { var inputs = document.querySelectorAll("input"); for (var i = 0; i < inputs.length; i++) { inputs[i].addEventListener("click", form_submit); } }); })();
ที่นี่ ฟังก์ชัน form_submit
จะส่งข้อมูลผ่านการเรียกแบบอะซิงโครนัส และบรรทัดสุดท้ายจะป้องกันลักษณะการส่งตามปกติของเบราว์เซอร์ โดยที่หน้าใหม่จะโหลดตามการตอบสนองของเซิร์ฟเวอร์ ครึ่งหลังของตัวอย่างนี้เพียงแค่รอจนกว่าหน้าจะโหลด จากนั้นจึงเชื่อมต่อปุ่มส่งทุกปุ่มเพื่อใช้ form_submit
สิ่งทั้งหมดถูกห่อด้วย IIFE
สัมผัสสุดท้าย
มีการเปลี่ยนแปลงหลายอย่างในตัวอย่างด้านบนในเวอร์ชันสุดท้ายของโค้ดแนะนำส่วนหลังของ Node.js ส่วนใหญ่เพื่อวัตถุประสงค์ในการจัดการข้อผิดพลาดที่ดีขึ้น:
- แบ็กเอนด์ Node.js จะตรวจสอบชื่อกลุ่มและการกดแป้นที่ส่งไปเพื่อให้แน่ใจว่ามีอยู่ รหัสนี้อยู่ในฟังก์ชันที่ใช้ซ้ำสำหรับทั้งฟังก์ชัน
GET
และPOST
ของroutes/group.js
- เราใช้เทมเพลต
error
Pug หากไม่เป็นเช่นนั้น - ตอนนี้ JavaScript ส่วนหน้าและ CSS ทำให้ปุ่มโครงร่างเป็นสีเทาชั่วคราวขณะรอการตอบกลับจากเซิร์ฟเวอร์ สีเขียวทันทีที่สัญญาณไปตลอดทางผ่าน
xdotool
และกลับมาโดยไม่มีปัญหา และเป็นสีแดงหากมีสิ่งใดไม่ทำงานตามที่คาดไว้ . - แบ็คเอนด์ของ Node.js จะพิมพ์สแต็กเทรซหากมันตาย ซึ่งมีโอกาสน้อยกว่าที่กล่าวไว้ข้างต้น
ผู้อ่านสามารถอ่าน (และ/หรือโคลน) โครงการ Node.js ที่สมบูรณ์บน GitHub
บทช่วยสอน Back-end ของ Node.js ขั้นตอนที่ 5: การทดสอบในโลกแห่งความเป็นจริง
ถึงเวลาลองใช้กับโทรศัพท์จริงที่เชื่อมต่อกับเครือข่าย wifi เดียวกันกับโฮสต์ที่ npm start
และเครื่องเล่นเพลงหรือภาพยนตร์ เป็นเรื่องของการชี้เว็บเบราว์เซอร์ของสมาร์ทโฟนไปยังที่อยู่ IP ในเครื่องของโฮสต์ (โดยมี :3000
ต่อท้าย) ซึ่งน่าจะหาได้ง่ายที่สุดโดยการเรียกใช้ hostname -I | awk '{print $1}'
hostname -I | awk '{print $1}'
ในเทอร์มินัลบนโฮสต์
ปัญหาหนึ่งที่ผู้ใช้ Windows Phone 8.1 อาจสังเกตเห็นคือการพยายามนำทางไปยังบางสิ่งเช่น 192.168.2.5:3000
จะแสดงป๊อปอัปข้อผิดพลาด:
โชคดีที่คุณไม่จำเป็นต้องท้อใจ: เพียงนำหน้าด้วย http://
หรือเพิ่มส่วนท้าย /
เรียกข้อมูลที่อยู่โดยไม่ต้องร้องเรียนเพิ่มเติม
การเลือกตัวเลือกควรนำเราไปสู่รีโมทคอนโทรลที่ใช้งานได้
เพื่อความสะดวกยิ่งขึ้น ผู้ใช้อาจต้องการปรับการตั้งค่า DHCP ของเราเตอร์เพื่อกำหนดที่อยู่ IP เดียวกันให้กับโฮสต์เสมอ และบุ๊กมาร์กหน้าจอการเลือกเลย์เอาต์และ/หรือเลย์เอาต์โปรดใดๆ
ดึงคำขอยินดีต้อนรับ
เป็นไปได้ว่าไม่ใช่ทุกคนที่จะชอบโครงการนี้อย่างที่มันเป็น ต่อไปนี้คือแนวคิดบางประการสำหรับการปรับปรุง สำหรับผู้ที่ต้องการเจาะลึกลงไปในโค้ด:
- คุณควรปรับแต่งเลย์เอาต์หรือเพิ่มเลย์เอาต์ใหม่สำหรับบริการอื่นๆ เช่น Disney Plus อย่างตรงไปตรงมา
- บางทีบางคนอาจชอบเลย์เอาต์ "โหมดแสง" และตัวเลือกเพื่อสลับไปมาระหว่างกัน
- การเลิกใช้ Netflix เนื่องจากไม่สามารถย้อนกลับได้จึงสามารถใช้ "คุณแน่ใจ" ได้จริงๆ หรือ การยืนยันบางอย่าง
- โครงการนี้จะได้รับประโยชน์จากการสนับสนุน Windows อย่างแน่นอน
- เอกสารประกอบของ
xdotool
กล่าวถึง OSX— โปรเจ็กต์นี้ (หรืออาจเป็นไปได้) ทำงานบน Mac รุ่นใหม่หรือไม่ - สำหรับการพักผ่อนขั้นสูง วิธีในการค้นหาและเรียกดูภาพยนตร์ แทนที่จะต้องเลือกภาพยนตร์ Netflix/Amazon Prime Video เรื่องเดียวหรือสร้างเพลย์ลิสต์ YouTube บนคอมพิวเตอร์
- ชุดทดสอบอัตโนมัติ ในกรณีที่การเปลี่ยนแปลงใด ๆ ที่แนะนำทำให้ฟังก์ชันการทำงานเดิมเสียหาย
ฉันหวังว่าคุณจะสนุกกับการกวดวิชาแบ็คเอนด์ Node.js และประสบการณ์ด้านสื่อที่ได้รับการปรับปรุงให้ดีขึ้น มีความสุขในการสตรีม—และการเข้ารหัส!