การใช้ Vue.js เพื่อสร้างแดชบอร์ดสภาพอากาศแบบโต้ตอบด้วย APIs
เผยแพร่แล้ว: 2022-03-10(นี่คือบทความที่ได้รับการสนับสนุน) ในบทช่วยสอนนี้ คุณจะสร้างแดชบอร์ดสภาพอากาศที่เรียบง่ายตั้งแต่เริ่มต้น มันจะเป็นแอปพลิเคชันไคลเอนต์ที่ไม่ใช่ตัวอย่าง “Hello World” และไม่น่ากลัวเกินไปในขนาดและความซับซ้อน
โครงการทั้งหมดจะได้รับการพัฒนาโดยใช้เครื่องมือจากระบบนิเวศ Node.js + npm โดยเฉพาะอย่างยิ่ง เราจะอาศัย Dark Sky API สำหรับข้อมูลเป็นอย่างมาก Vue.js สำหรับการยกของหนักทั้งหมด และ FusionCharts สำหรับการแสดงข้อมูลเป็นภาพ
ข้อกำหนดเบื้องต้น
เราหวังว่าคุณจะคุ้นเคยกับสิ่งต่อไปนี้:
- HTML5 และ CSS3 (เราจะใช้คุณสมบัติพื้นฐานที่ Bootstrap จัดเตรียมไว้ให้
- JavaScript (โดยเฉพาะวิธีการใช้ภาษา ES6);
- Node.js และ npm (พื้นฐานของสภาพแวดล้อมและการจัดการแพ็คเกจนั้นใช้ได้)
นอกเหนือจากที่กล่าวมาข้างต้น จะดีมากหากคุณมีความคุ้นเคยกับ Vue.js หรือเฟรมเวิร์ก JavaScript อื่นที่คล้ายคลึงกัน เราไม่ได้คาดหวังให้คุณรู้เกี่ยวกับ FusionCharts — มันใช้งานง่ายมากจนคุณจะได้เรียนรู้ทันที!
การเรียนรู้ที่คาดหวัง
การเรียนรู้ที่สำคัญของคุณจากโครงการนี้จะเป็น:
- วิธีวางแผนการใช้งานแดชบอร์ดที่ดี
- วิธีพัฒนาแอพพลิเคชั่นด้วย Vue.js
- วิธีสร้างแอปพลิเคชันที่ขับเคลื่อนด้วยข้อมูล
- วิธีแสดงภาพข้อมูลโดยใช้ FusionCharts
โดยเฉพาะอย่างยิ่ง แต่ละส่วนช่วยให้คุณเข้าใกล้เป้าหมายการเรียนรู้มากขึ้น:
- ข้อมูลเบื้องต้นเกี่ยวกับแดชบอร์ดสภาพอากาศ
บทนี้ให้ภาพรวมเกี่ยวกับแง่มุมต่างๆ ของการดำเนินการ - สร้างโครงการ
ในส่วนนี้ คุณจะได้เรียนรู้เกี่ยวกับการสร้างโครงการตั้งแต่เริ่มต้นโดยใช้เครื่องมือบรรทัดคำสั่ง Vue - ปรับแต่งโครงสร้างโครงการเริ่มต้น
โครงนั่งร้านเริ่มต้นที่คุณได้รับในส่วนก่อนหน้านั้นไม่เพียงพอ ที่นี่คุณจะได้เรียนรู้เพิ่มเติมที่จำเป็นสำหรับโครงการจากมุมมองเชิงโครงสร้าง - การรับและประมวลผลข้อมูล
ส่วนนี้เป็นเนื้อของโครงการ โค้ดที่สำคัญทั้งหมดสำหรับการรับและประมวลผลข้อมูลจาก API ได้แสดงไว้ที่นี่ คาดว่าจะใช้เวลาสูงสุดในส่วนนี้ - การสร้างภาพข้อมูลด้วย FusionCharts
เมื่อข้อมูลทั้งหมดและส่วนที่เคลื่อนไหวอื่นๆ ของโปรเจ็กต์เสถียรแล้ว ส่วนนี้จะเน้นไปที่การแสดงภาพข้อมูลโดยใช้ FusionCharts และ CSS เล็กน้อย
1. เวิร์กโฟลว์แดชบอร์ด
ก่อนที่เราจะลงลึกในการดำเนินการ สิ่งสำคัญคือต้องมีความชัดเจนเกี่ยวกับแผนของเรา เราแบ่งแผนของเราออกเป็นสี่ด้านที่แตกต่างกัน:
ความต้องการ
ข้อกำหนดของเราสำหรับโครงการนี้คืออะไร? กล่าวอีกนัยหนึ่ง สิ่งที่เราต้องการแสดงผ่าน Weather Dashboard คืออะไร? พึงระลึกไว้เสมอว่าผู้ฟังที่ตั้งใจไว้ของเราอาจเป็นเพียงมนุษย์ปุถุชนที่มีรสนิยมเรียบง่าย เราอยากจะแสดงให้พวกเขาเห็นสิ่งต่อไปนี้:
- รายละเอียดของตำแหน่งที่ต้องการดูสภาพอากาศ พร้อมข้อมูลเบื้องต้นเกี่ยวกับสภาพอากาศ เนื่องจากไม่มีข้อกำหนดที่เข้มงวด เราจะหารายละเอียดที่น่าเบื่อในภายหลัง อย่างไรก็ตาม ในขั้นตอนนี้ สิ่งสำคัญที่ควรทราบคือเราจะต้องจัดเตรียมช่องค้นหาให้กับผู้ชม เพื่อให้พวกเขาสามารถป้อนข้อมูลสำหรับสถานที่ที่พวกเขาสนใจได้
- ข้อมูลกราฟิกเกี่ยวกับสภาพอากาศของสถานที่ที่พวกเขาสนใจ เช่น:
- การเปลี่ยนแปลงอุณหภูมิสำหรับวันที่สอบถาม
- ไฮไลท์ของสภาพอากาศวันนี้:
- ความเร็วลมและทิศทาง
- ทัศนวิสัย
- ดัชนี UV
หมายเหตุ : ข้อมูลที่ได้รับจาก API ให้ข้อมูลเกี่ยวกับสภาพอากาศในด้านอื่นๆ เราเลือกที่จะไม่ใช้ทั้งหมดเพื่อรักษารหัสให้น้อยที่สุด
โครงสร้าง
ตามข้อกำหนด เราสามารถจัดโครงสร้างแดชบอร์ดของเราดังที่แสดงด้านล่าง:
ข้อมูล
แดชบอร์ดของเราดีพอๆ กับข้อมูลที่เราได้รับ เพราะจะไม่มีการสร้างภาพข้อมูลที่สวยงามหากไม่มีข้อมูลที่เหมาะสม มี API สาธารณะมากมายที่ให้ข้อมูลสภาพอากาศ — บางส่วนฟรีและบางส่วนไม่ได้ สำหรับโครงการของเรา เราจะรวบรวมข้อมูลจาก Dark Sky API อย่างไรก็ตาม เราไม่สามารถสำรวจจุดสิ้นสุด API จากปลายทางไคลเอ็นต์ได้โดยตรง ไม่ต้องกังวล เรามีวิธีแก้ปัญหาที่จะเปิดเผยในเวลาที่เหมาะสม! เมื่อเราได้รับข้อมูลสำหรับตำแหน่งที่ค้นหาแล้ว เราจะดำเนินการประมวลผลและจัดรูปแบบข้อมูล นั่นคือประเภทของเทคนิคที่ช่วยเราชำระค่าใช้จ่าย
การสร้างภาพ
เมื่อเราได้รับข้อมูลที่สะอาดและจัดรูปแบบแล้ว เราก็เสียบเข้ากับ FusionCharts มีไลบรารี JavaScript เพียงไม่กี่แห่งในโลกที่มีความสามารถเทียบเท่า FusionCharts จากข้อเสนอมากมายจาก FusionCharts เราจะใช้เพียงไม่กี่อย่าง — ทั้งหมดเขียนด้วย JavaScript แต่ทำงานได้อย่างราบรื่นเมื่อรวมเข้ากับ Vue wrapper สำหรับ FusionCharts
ด้วยภาพที่ใหญ่ขึ้น มาทำให้มือของเราสกปรกกันเถอะ ได้เวลาสร้างสิ่งต่างๆ ให้เป็นรูปธรรมแล้ว! ในส่วนถัดไป คุณจะต้องสร้างโปรเจ็กต์ Vue พื้นฐาน ซึ่งเราจะสร้างเพิ่มเติมต่อไป
2. การสร้างโครงการ
ในการสร้างโปรเจ็กต์ ให้ทำตามขั้นตอนต่อไปนี้:
- ติดตั้ง Node.js + npm
( หากคุณติดตั้ง Node.js บนคอมพิวเตอร์ของคุณ ให้ข้ามขั้นตอนนี้ )
Node.js มาพร้อมกับ npm ที่มาพร้อมกับมัน ดังนั้นคุณไม่จำเป็นต้องติดตั้ง npm แยกต่างหาก ขึ้นอยู่กับระบบปฏิบัติการ ดาวน์โหลดและติดตั้ง Node.js ตามคำแนะนำที่ให้ไว้ที่นี่
เมื่อติดตั้งแล้ว อาจเป็นความคิดที่ดีที่จะตรวจสอบว่าซอฟต์แวร์ทำงานถูกต้องหรือไม่ และเวอร์ชันของซอฟต์แวร์เป็นเวอร์ชันใด หากต้องการทดสอบ ให้เปิดบรรทัดคำสั่ง/เทอร์มินัลแล้วดำเนินการคำสั่งต่อไปนี้:node --version npm --version
- ติดตั้งแพ็คเกจด้วย npm
เมื่อคุณเปิดใช้งาน npm แล้ว ให้รันคำสั่งต่อไปนี้เพื่อติดตั้งแพ็คเกจพื้นฐานที่จำเป็นสำหรับโครงการของเราnpm install -g vue@2 vue-cli@2
- เริ่มต้นนั่งร้านโครงการด้วย
vue-cli
สมมติว่าขั้นตอนก่อนหน้านี้ผ่านไปด้วยดี ขั้นตอนต่อไปคือการใช้vue-cli
— เครื่องมือบรรทัดคำสั่งจาก Vue.js เพื่อเริ่มต้นโครงการ ให้ดำเนินการดังต่อไปนี้: - เริ่มต้นนั่งร้านด้วยเทมเพลต webpack-simple
vue init webpack-simple vue_weather_dashboard
N
สำหรับข้อสุดท้าย โปรดทราบว่าแม้ว่าwebpack-simple
นั้นยอดเยี่ยมสำหรับการสร้างต้นแบบอย่างรวดเร็วและแอพพลิเคชั่นขนาดเล็กเช่นของเรา แต่ก็ไม่เหมาะอย่างยิ่งสำหรับแอพพลิเคชั่นที่จริงจังหรือการใช้งานจริง หากคุณต้องการใช้เทมเพลตอื่น (แม้ว่าเราจะไม่แนะนำให้ใช้หากคุณเป็นมือใหม่) หรือต้องการตั้งชื่อโครงการของคุณอย่างอื่น ไวยากรณ์คือ:vue init [template-name] [project-name]
- ไปที่ไดเร็กทอรีที่สร้างโดย vue-cli สำหรับโครงการ
cd vue_weather_dashboard
- ติดตั้งแพ็คเกจทั้งหมดที่กล่าวถึงใน
package.json
ซึ่งสร้างโดยเครื่องมือvue-cli
สำหรับเทมเพลตwebpack-simple
npm install
- เริ่มเซิร์ฟเวอร์การพัฒนาและดูโปรเจ็กต์ Vue เริ่มต้นของคุณทำงานในเบราว์เซอร์!
npm run dev
หากคุณเพิ่งเริ่มใช้ Vue.js ให้ใช้เวลาสักครู่เพื่อลิ้มรสความสำเร็จล่าสุดของคุณ — คุณได้สร้างแอปพลิเคชั่น Vue ขนาดเล็กและทำงานบน localhost:8080!
คำอธิบายสั้น ๆ ของโครงสร้างโครงการเริ่มต้น
ได้เวลาดูโครงสร้างภายในไดเร็กทอรี vue_weather_dashboard
เพื่อให้คุณมีความเข้าใจพื้นฐานก่อนที่เราจะเริ่มแก้ไข
โครงสร้างมีลักษณะดังนี้:
vue_weather_dashboard |--- README.md |--- node_modules/ | |--- ... | |--- ... | |--- [many npm packages we installed] | |--- ... | |--- ... |--- package.json |--- package-lock.json |--- webpack.config.js |--- index.html |--- src | |--- App.vue | |--- assets | | |--- logo.png | |--- main.js
ถึงแม้ว่าคุณอาจจะไม่อยากทำความคุ้นเคยกับไฟล์และไดเร็กทอรีดีฟอลต์ แต่ถ้าคุณเพิ่งเริ่มใช้ Vue เรา ขอแนะนำให้ คุณดูเนื้อหาของไฟล์เป็นอย่างน้อย อาจเป็นช่วงการศึกษาที่ดีและก่อให้เกิดคำถามที่คุณควรดำเนินการด้วยตนเอง โดยเฉพาะไฟล์ต่อไปนี้
-
package.json
และเพียงแค่เหลือบมองที่ลูกพี่ลูกน้องpackage-lock.json
-
webpack.config.js
-
index.html
-
src/main.js
-
src/App.vue
คำอธิบายสั้น ๆ ของแต่ละไฟล์และไดเร็กทอรีที่แสดงในไดอะแกรมแบบต้นไม้แสดงไว้ด้านล่าง:
- README.md
ไม่มีรางวัลสำหรับการคาดเดา — โดยพื้นฐานแล้วมนุษย์ต้องอ่านและทำความเข้าใจขั้นตอนที่จำเป็นสำหรับการสร้างโครงนั่งร้าน - node_modules/
นี่คือไดเร็กทอรีที่ npm ดาวน์โหลดแพ็คเกจที่จำเป็นสำหรับการเริ่มต้นโครงการ ข้อมูลเกี่ยวกับแพ็กเกจที่จำเป็นมีอยู่ในไฟล์package.json
- package.json
ไฟล์นี้สร้างโดยเครื่องมือ vue-cli ตามข้อกำหนดของเทมเพลต webpackwebpack-simple
และมีข้อมูลเกี่ยวกับแพ็กเกจ npm (รวมถึงเวอร์ชันและรายละเอียดอื่นๆ) ที่ต้องติดตั้ง ดูเนื้อหาของไฟล์อย่างละเอียด นี่คือที่ที่คุณควรเยี่ยมชมและอาจแก้ไขเพื่อเพิ่ม/ลบแพ็คเกจที่จำเป็นสำหรับโครงการ จากนั้นรันการติดตั้ง npm อ่านเพิ่มเติมเกี่ยวกับpackage.json
ที่นี่ - package-lock.json
ไฟล์นี้สร้างโดย npm เอง และมีขึ้นเพื่อเก็บบันทึกของสิ่งต่าง ๆ ที่ดาวน์โหลดและติดตั้ง npm เป็นหลัก - webpack.config.js
ไฟล์ JavaScript ที่มีการกำหนดค่าของ webpack ซึ่งเป็นเครื่องมือที่รวมส่วนต่างๆ ของโครงการเข้าด้วยกัน (โค้ด ทรัพย์สินแบบคงที่ การกำหนดค่า สภาพแวดล้อม โหมดการใช้งาน ฯลฯ) และย่อก่อนให้บริการแก่ผู้ใช้ ประโยชน์คือทุกสิ่งจะเชื่อมโยงเข้าด้วยกันโดยอัตโนมัติ และประสบการณ์ของผู้ใช้ก็ดีขึ้นอย่างมากเนื่องจากการปรับปรุงประสิทธิภาพของแอปพลิเคชัน (หน้าจะแสดงผลอย่างรวดเร็วและโหลดเร็วขึ้นบนเบราว์เซอร์) คุณอาจพบเห็นในภายหลัง นี่คือไฟล์ที่ต้องได้รับการตรวจสอบเมื่อบางสิ่งในระบบบิลด์ไม่ทำงานตามที่ตั้งใจไว้ นอกจากนี้ เมื่อคุณต้องการปรับใช้แอปพลิเคชัน นี่เป็นหนึ่งในไฟล์หลักที่ต้องแก้ไข (อ่านเพิ่มเติมที่นี่) - index.html
ไฟล์ HTML นี้ทำหน้าที่เป็นเมทริกซ์ (หรือคุณอาจพูดได้ว่า เทมเพลต) โดยที่ข้อมูลและโค้ดจะถูกฝังแบบไดนามิก (นั่นคือสิ่งที่ Vue ทำเป็นหลัก) จากนั้นจึงให้บริการแก่ผู้ใช้ - src/main.js
ไฟล์ JavaScript นี้มีโค้ดที่จัดการการพึ่งพาระดับบนสุด/โปรเจ็กต์เป็นหลัก และกำหนดองค์ประกอบ Vue ระดับบนสุด กล่าวโดยย่อ คือ จัดการ JavaScript สำหรับทั้งโปรเจ็กต์ และทำหน้าที่เป็นจุดเริ่มต้นของแอปพลิเคชัน แก้ไขไฟล์นี้เมื่อคุณต้องการประกาศการพึ่งพาทั้งโปรเจ็กต์บนโมดูลโหนดบางโมดูล หรือคุณต้องการเปลี่ยนแปลงบางอย่างเกี่ยวกับคอมโพเนนต์ Vue ระดับบนสุดในโปรเจ็กต์ - src/App.vue
ในจุดก่อนหน้านี้ เมื่อเราพูดถึง "องค์ประกอบ Vue ระดับบนสุด" เรากำลังพูดถึงไฟล์นี้เป็นหลัก ไฟล์ .vue แต่ละไฟล์ในโปรเจ็กต์เป็นส่วนประกอบ และส่วนประกอบต่างๆ มีความเกี่ยวข้องตามลำดับชั้น ในตอนเริ่มต้น เรามีไฟล์ .App.vue
.vue
องค์ประกอบเดียวของเรา แต่ในไม่ช้า เราจะเพิ่มส่วนประกอบอื่นๆ ให้กับโครงการของเรา (ตามโครงสร้างของแดชบอร์ดเป็นหลัก) และเชื่อมโยงองค์ประกอบเหล่านั้นตามลำดับชั้นที่ต้องการ โดย App.vue จะเป็นบรรพบุรุษของทั้งหมด ไฟล์.vue
เหล่านี้จะมีโค้ดในรูปแบบที่ Vue ต้องการให้เราเขียน ไม่ต้องกังวล เนื่องจากเป็นโค้ด JavaScript ที่เขียนขึ้นเพื่อคงโครงสร้างที่ช่วยให้เรามีสติและจัดระเบียบได้ คุณได้รับคำเตือนแล้ว ในตอนท้ายของโปรเจ็กต์นี้ หากคุณเพิ่งเริ่มใช้ Vue คุณอาจติดtemplate — script — style
template — script — style
template — script — style
การจัดโค้ด!
เมื่อเราได้สร้างรากฐานแล้ว ก็ถึงเวลาที่จะ:
- แก้ไขเทมเพลตและปรับแต่งไฟล์การกำหนดค่าเล็กน้อย เพื่อให้โครงการทำงานตามที่เราต้องการ
- สร้างไฟล์
.vue
ใหม่ และใช้โครงสร้างแดชบอร์ดด้วยโค้ด Vue
เราจะเรียนรู้พวกเขาในหัวข้อถัดไป ซึ่งจะยาวหน่อยและต้องการความสนใจบ้าง หากคุณต้องการคาเฟอีนหรือน้ำ หรือต้องการปลดปล่อย – ถึงเวลาแล้ว!
3. การปรับแต่งโครงสร้างโครงการเริ่มต้น
ถึงเวลาปรับแต่งรากฐานที่โครงการนั่งร้านมอบให้เรา ก่อนที่คุณจะเริ่ม ตรวจสอบให้แน่ใจว่าเซิร์ฟเวอร์การพัฒนาที่จัดเตรียมโดย webpack
นั้นทำงานอยู่ ข้อดีของการเรียกใช้เซิร์ฟเวอร์นี้ อย่างต่อเนื่อง คือการเปลี่ยนแปลงใดๆ ที่คุณทำในซอร์สโค้ด — สิ่งที่คุณบันทึกและรีเฟรชหน้าเว็บ — การเปลี่ยนแปลงนั้นมีผลในเบราว์เซอร์ทันที
หากคุณต้องการเริ่มต้นเซิร์ฟเวอร์การพัฒนา เพียงรันคำสั่งต่อไปนี้จากเทอร์มินัล (สมมติว่าไดเร็กทอรีปัจจุบันของคุณคือไดเร็กทอรีโครงการ):
npm run dev
ในส่วนต่อไปนี้ เราจะแก้ไขไฟล์ที่มีอยู่บางส่วน และเพิ่มไฟล์ใหม่ ตามด้วยคำอธิบายสั้นๆ เกี่ยวกับเนื้อหาของไฟล์เหล่านั้น เพื่อให้คุณมีความคิดว่าการเปลี่ยนแปลงเหล่านั้นมีไว้ทำอะไร
แก้ไขไฟล์ที่มีอยู่
index.html
แอปพลิเคชันของเราเป็นแอปพลิเคชันหน้าเดียวอย่างแท้จริง เนื่องจากมีหน้าเว็บเพียงหน้าเดียวที่แสดงบนเบราว์เซอร์ เราจะพูดถึงเรื่องนี้ในภายหลัง แต่ขั้นแรก เรามาทำการเปลี่ยนแปลงครั้งแรก — การแก้ไขข้อความภายในแท็ก <title>
ด้วยการแก้ไขเล็กน้อยนี้ ไฟล์ HTML จะมีลักษณะดังนี้:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <!-- Modify the text of the title tag below --> <title>Vue Weather Dashboard</title> </head> <body> <div></div> <script src="/dist/build.js"></script> </body> </html>
ใช้เวลาสักครู่เพื่อรีเฟรชหน้าเว็บที่ localhost:8080
และดูการเปลี่ยนแปลงที่แสดงบนแถบชื่อของแท็บบนเบราว์เซอร์ ซึ่งควรระบุว่า "Vue Weather Dashboard" อย่างไรก็ตาม นี่เป็นเพียงเพื่อแสดงให้คุณเห็นถึงขั้นตอนการเปลี่ยนแปลงและตรวจสอบว่าการทำงานนั้นได้ผลหรือไม่ เรามีสิ่งที่ต้องทำมากขึ้น!
หน้า HTML แบบง่ายนี้ขาดหลายสิ่งที่เราต้องการในโครงการของเรา โดยเฉพาะอย่างยิ่งสิ่งต่อไปนี้:
- ข้อมูลเมตาบางส่วน
- ลิงก์ CDN ไปยัง Bootstrap (เฟรมเวิร์ก CSS)
- ลิงก์ไปยังสไตล์ชีตที่กำหนดเอง (ยังไม่ได้เพิ่มในโปรเจ็กต์)
- ตัวชี้ไปยัง Google Maps Geolocation API จากแท็ก
<script>
หลังจากเพิ่มสิ่งเหล่านั้นแล้ว index.html
สุดท้ายจะมีเนื้อหาดังต่อไปนี้:
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="src/css/style.css"> <title>Weather Dashboard</title> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-lCjpg1xbw-nsCc11Si8Ldg2LKYizqI4&libraries=places"></script> </head> <body> <div></div> <script src="/dist/build.js"></script> </body> </html>
บันทึกไฟล์และรีเฟรชหน้าเว็บ คุณอาจสังเกตเห็นการกระแทกเล็กน้อยขณะโหลดหน้า — สาเหตุหลักมาจากการที่ Bootstrap ควบคุมสไตล์ของเพจอยู่ในขณะนี้ และองค์ประกอบสไตล์ เช่น ฟอนต์ การเว้นวรรค ฯลฯ นั้นแตกต่างจากค่าเริ่มต้นที่เรามี ก่อนหน้านี้ (หากไม่แน่ใจ ให้ย้อนกลับไปที่ค่าเริ่มต้นและดูความแตกต่าง)
หมายเหตุ : สิ่งสำคัญอย่างหนึ่งก่อนที่เราจะดำเนินการต่อ — URL สำหรับ Google Maps API มีคีย์ซึ่งเป็นคุณสมบัติของ FusionCharts สำหรับตอนนี้ คุณสามารถใช้คีย์นี้เพื่อสร้างโปรเจ็กต์ได้ เนื่องจากเราไม่ต้องการให้คุณจมอยู่กับรายละเอียดเล็กๆ น้อยๆ ประเภทนี้ (ซึ่งอาจเป็นการรบกวนในขณะที่คุณยังใหม่อยู่) อย่างไรก็ตาม เราขอแนะนำให้คุณสร้างและใช้คีย์ Google Maps API ของคุณเอง เมื่อคุณมีความคืบหน้าและรู้สึกสบายใจที่จะให้ความสนใจกับรายละเอียดเล็กๆ เหล่านี้
package.json
ในขณะที่เขียนสิ่งนี้ เราใช้แพ็คเกจ npm บางเวอร์ชันสำหรับโปรเจ็กต์ของเรา และเรารู้แน่นอนว่าสิ่งเหล่านั้นทำงานร่วมกันได้ อย่างไรก็ตาม เมื่อคุณดำเนินการโครงการ เป็นไปได้มากว่าเวอร์ชันเสถียรล่าสุดของแพ็คเกจที่ npm ดาวน์โหลดให้คุณนั้นไม่เหมือนกับที่เราใช้ และสิ่งนี้อาจทำให้โค้ดเสียหาย (หรือทำสิ่งต่าง ๆ ที่เกินกว่า การควบคุมของเรา) ดังนั้นจึงเป็นเรื่องสำคัญมากที่จะต้องมีไฟล์ package.json
เดียวกันกับที่ใช้สร้างโปรเจ็กต์นี้ เพื่อให้โค้ด/คำอธิบายของเราและผลลัพธ์ที่คุณได้รับมีความสอดคล้องกัน
เนื้อหาของไฟล์ package.json
ควรเป็น:
{ "name": "vue_weather_dashboard", "description": "A Vue.js project", "version": "1.0.0", "author": "FusionCharts", "license": "MIT", "private": true, "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" }, "dependencies": { "axios": "^0.18.0", "babel": "^6.23.0", "babel-cli": "^6.26.0", "babel-polyfill": "^6.26.0", "fusioncharts": "^3.13.3", "moment": "^2.22.2", "moment-timezone": "^0.5.21", "vue": "^2.5.11", "vue-fusioncharts": "^2.0.4" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ], "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.0", "babel-preset-stage-3": "^6.24.1", "cross-env": "^5.0.5", "css-loader": "^0.28.7", "file-loader": "^1.1.4", "vue-loader": "^13.0.5", "vue-template-compiler": "^2.4.4", "webpack": "^3.6.0", "webpack-dev-server": "^2.9.1" } }
เราขอแนะนำให้คุณอ่าน package.json
ใหม่และค้นหาว่าหน้าที่ของอ็อบเจกต์ต่างๆ ใน json คืออะไร คุณอาจต้องการเปลี่ยนค่าของคีย์ " author
" เป็นชื่อของคุณ นอกจากนี้ แพ็คเกจที่กล่าวถึงในการพึ่งพาจะแสดงตัวในเวลาที่เหมาะสมในโค้ด ในตอนนี้ก็เพียงพอแล้วที่จะรู้ว่า:
- แพ็คเกจที่เกี่ยวข้องกับ
babel
นั้นใช้สำหรับการจัดการโค้ดสไตล์ ES6 อย่างถูกต้องโดยเบราว์เซอร์ -
axios
จัดการกับคำขอ HTTP ตามสัญญา; -
moment
และโมเมนต์-โซนเวลาใช้สำหรับการจัดการวันที่/เวลา -
fusioncharts
และvue-fusioncharts
มีหน้าที่ในการแสดงแผนภูมิ: -
vue
ด้วยเหตุผลที่ชัดเจน
webpack.config.js
เช่นเดียวกับ package.json
เราขอแนะนำให้คุณรักษาไฟล์ webpack.config.js
ที่สอดคล้องกับไฟล์ที่เราใช้สำหรับสร้างโครงการ อย่างไรก็ตาม ก่อนทำการเปลี่ยนแปลงใดๆ เราขอแนะนำให้คุณเปรียบเทียบโค้ดเริ่มต้นอย่างละเอียดใน webpack.config.js
และโค้ดที่เราให้ไว้ด้านล่าง คุณจะสังเกตเห็นความแตกต่างค่อนข้างน้อย — google พวกเขาและมีแนวคิดพื้นฐานเกี่ยวกับความหมายของมัน เนื่องจากการอธิบายการกำหนดค่า webpack ในเชิงลึกนั้นอยู่นอกขอบเขตของบทความนี้ คุณจึงต้องรับผิดชอบเองในเรื่องนี้
ไฟล์ webpack.config.js
ที่กำหนดเองมีดังนี้:
var path = require('path') var webpack = require('webpack') module.exports = { entry: ['babel-polyfill', './src/main.js'], output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ], }, { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['*', '.js', '.vue', '.json'] }, devServer: { historyApiFallback: true, noInfo: true, overlay: true, host: '0.0.0.0', port: 8080 }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // https://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
ด้วยการเปลี่ยนแปลงที่ทำกับ webpack.config.js
ของโปรเจ็กต์ คุณจำเป็นต้องหยุดเซิร์ฟเวอร์การพัฒนาที่กำลังทำงานอยู่ ( Ctrl + C ) และรีสตาร์ทด้วยคำสั่งต่อไปนี้ที่ดำเนินการจากไดเร็กทอรีของโปรเจ็กต์หลังจากติดตั้งแพ็กเกจทั้งหมดที่กล่าวถึงใน package.json
ไฟล์ package.json
:
npm install npm run dev
ด้วยเหตุนี้ การทดสอบการปรับแต่งการกำหนดค่าและการทำให้มั่นใจว่ามีแพ็กเกจที่เหมาะสมจะสิ้นสุดลง อย่างไรก็ตาม นี่ยังเป็นจุดเริ่มต้นของการแก้ไขและเขียนโค้ด ซึ่งยาวหน่อยแต่ก็คุ้มค่ามากเช่นกัน!
src/main.js
ไฟล์นี้เป็นกุญแจสำคัญในการประสานงานระดับบนสุดของโปรเจ็กต์ — นี่คือสิ่งที่เรากำหนด:
- การพึ่งพาระดับบนสุดคืออะไร (ที่จะได้รับแพ็คเกจ npm ที่สำคัญที่สุดที่จำเป็น);
- วิธีแก้ไขการขึ้นต่อกัน พร้อมด้วยคำแนะนำเกี่ยวกับ Vue เกี่ยวกับการใช้ปลั๊กอิน/ตัวห่อหุ้ม หากมี
- อินสแตนซ์ Vue ที่จัดการองค์ประกอบระดับบนสุดในโปรเจ็กต์:
src/App.vue
(ไฟล์ nodal.vue
)
เพื่อให้สอดคล้องกับเป้าหมายของเราสำหรับไฟล์ src/main.js
โค้ดควรเป็น:
// Import the dependencies and necessary modules import Vue from 'vue'; import App from './App.vue'; import FusionCharts from 'fusioncharts'; import Charts from 'fusioncharts/fusioncharts.charts'; import Widgets from 'fusioncharts/fusioncharts.widgets'; import PowerCharts from 'fusioncharts/fusioncharts.powercharts'; import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; import VueFusionCharts from 'vue-fusioncharts'; // Resolve the dependencies Charts(FusionCharts); PowerCharts(FusionCharts); Widgets(FusionCharts); FusionTheme(FusionCharts); // Globally register the components for project-wide use Vue.use(VueFusionCharts, FusionCharts); // Instantiate the Vue instance that controls the application new Vue({ el: '#app', render: h => h(App) })
src/App.vue
นี่เป็นหนึ่งในไฟล์ที่สำคัญที่สุดในโปรเจ็กต์ทั้งหมด และแสดงถึงองค์ประกอบระดับบนสุดในลำดับชั้น — ตัวแอปพลิเคชันทั้งหมดเอง โดยรวม สำหรับโครงการของเรา ส่วนประกอบนี้จะทำหน้าที่จัดการงานหนักทั้งหมด ซึ่งเราจะสำรวจในภายหลัง สำหรับตอนนี้ เราต้องการกำจัดต้นแบบเริ่มต้น และใส่บางอย่างของเราเอง
หากคุณยังใหม่ต่อวิธีการจัดระเบียบโค้ดของ Vue จะดีกว่าถ้าคุณเข้าใจโครงสร้างทั่วไปภายในไฟล์ . .vue
ไฟล์ .vue
ประกอบด้วยสามส่วน:
- แม่แบบ
นี่คือที่ที่กำหนดเทมเพลต HTML สำหรับเพจ นอกเหนือจาก HTML แบบคงที่ ส่วนนี้ยังมีวิธีการฝังเนื้อหาแบบไดนามิกของ Vue โดยใช้วงเล็บปีกกาคู่{{ }}
- สคริปต์
JavaScript ควบคุมส่วนนี้ และรับผิดชอบในการสร้างเนื้อหาแบบไดนามิกที่อยู่ภายในเทมเพลต HTML ในตำแหน่งที่เหมาะสม ส่วนนี้เป็นออบเจ็กต์หลักที่ส่งออก และประกอบด้วย:- ข้อมูล
นี่คือฟังก์ชัน และโดยปกติแล้วจะส่งกลับข้อมูลที่ต้องการซึ่งห่อหุ้มไว้ภายในโครงสร้างข้อมูลที่ดี - วิธีการ
ออบเจ็กต์ที่ประกอบด้วยฟังก์ชัน/เมธอดตั้งแต่หนึ่งฟังก์ชันขึ้นไป ซึ่งแต่ละฟังก์ชันมักจะจัดการข้อมูลไม่ทางใดก็ทางหนึ่ง และยังควบคุมเนื้อหาแบบไดนามิกของเทมเพลต HTML - คำนวณ
เช่นเดียวกับอ็อบเจกต์เมธอดที่กล่าวถึงข้างต้นโดยมีข้อแตกต่างที่สำคัญอย่างหนึ่ง — ในขณะที่ฟังก์ชันทั้งหมดภายในอ็อบเจ็กต์เมธอดจะถูกเรียกใช้งานทุกครั้งที่มีการเรียกฟังก์ชันใดฟังก์ชันหนึ่ง ฟังก์ชันภายในอ็อบเจ็กต์ที่คำนวณจะทำงานอย่างสมเหตุสมผลมากกว่า และจะดำเนินการก็ต่อเมื่อได้รับ เรียกว่า.
- ข้อมูล
- สไตล์
ส่วนนี้มีไว้สำหรับการจัดรูปแบบ CSS ที่ใช้กับ HTML ของหน้า (เขียนภายในเทมเพลต) - ใส่ CSS แบบเก่าที่นี่เพื่อทำให้หน้าของคุณสวยงาม!
โดยคำนึงถึงกระบวนทัศน์ข้างต้น มาปรับแต่งโค้ดใน App.vue
ให้น้อยที่สุด:
<template> <div> <p>This component's code is in {{ filename }}</p> </div> </template> <script> export default { data() { return { filename: 'App.vue' } }, methods: { }, computed: { }, } </script> <style> </style>
โปรดจำไว้ว่าข้อมูลโค้ดด้านบนนี้เป็นเพียงการทดสอบว่า App.vue
กำลังทำงานโดยใช้โค้ดของเราเอง หลังจากนั้นจะผ่านการเปลี่ยนแปลงมากมาย แต่ก่อนอื่นให้บันทึกไฟล์และรีเฟรชหน้าบนเบราว์เซอร์
ณ จุดนี้ อาจเป็นความคิดที่ดีที่จะขอความช่วยเหลือเกี่ยวกับเครื่องมือ ลองใช้ Vue devtools สำหรับ Chrome และหากคุณไม่มีปัญหาในการใช้ Google Chrome เป็นเบราว์เซอร์เริ่มต้นสำหรับการพัฒนา ให้ติดตั้งเครื่องมือและลองใช้งานดูสักหน่อย มันจะมีประโยชน์อย่างมากสำหรับการพัฒนาและการดีบักเพิ่มเติม เมื่อสิ่งต่างๆ มีความซับซ้อนมากขึ้น
ไดเรกทอรีและไฟล์เพิ่มเติม
ขั้นตอนต่อไปคือการเพิ่มไฟล์เพิ่มเติม เพื่อให้โครงสร้างของโครงการของเราสมบูรณ์ เราจะเพิ่มไดเร็กทอรีและไฟล์ต่อไปนี้:
-
src/css/
—style.css
-
src/assets/
—calendar.svg
—vlocation.svg
—search.svg
—winddirection.svg
—windspeed.svg
-
src/components/
—Content.vue
—Highlights.vue
—TempVarChart.vue
—UVIndex.vue
—Visibility.vue
—WindStatus.vue
หมายเหตุ : บันทึกไฟล์ไฮเปอร์ลิงก์ . .svg
ในโครงการของคุณ
สร้างไดเร็กทอรีและไฟล์ที่กล่าวถึงข้างต้น โครงสร้างโครงการขั้นสุดท้ายควรมีลักษณะ (อย่าลืมลบโฟลเดอร์และไฟล์จากโครงสร้างเริ่มต้นที่ไม่จำเป็น):
vue_weather_dashboard/ |--- README.md |--- node_modules/ | |--- ... | |--- ... | |--- [many npm packages we installed] | |--- ... | |--- ... |--- package.json |--- package-lock.json |--- webpack.config.js |--- index.html |--- src/ | |--- App.vue | |--- css/ | | |--- style.css | |--- assets/ | | |--- calendar.svg | | |--- location.svg | | |--- location.svg | | |--- winddirection.svg | | |--- windspeed.svg | |--- main.js | |--- components/ | | |--- Content.vue | | |--- Highlights.vue | | |--- TempVarChart.vue | | |--- UVIndex.vue | | |--- Visibility.vue | | |--- WindStatus.vue
อาจมีไฟล์อื่นๆ เช่น . .babelrc
, .gitignore
, .editorconfig
ฯลฯ ในโฟลเดอร์รูทของโปรเจ็กต์ คุณอาจเพิกเฉยได้อย่างปลอดภัยในตอนนี้
ในส่วนต่อไปนี้ เราจะเพิ่มเนื้อหาขั้นต่ำให้กับไฟล์ที่เพิ่มใหม่ และทดสอบว่าทำงานอย่างถูกต้องหรือไม่
src/css/style.css
แม้ว่าจะไม่มีประโยชน์อะไรมากในทันที ให้คัดลอกโค้ดต่อไปนี้ไปยังไฟล์:
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500"); :root { font-size: 62.5%; } body { font-family: Roboto; font-weight: 400; width: 100%; margin: 0; font-size: 1.6rem; } #sidebar { position: relative; display: flex; flex-direction: column; background-image: linear-gradient(-180deg, #80b6db 0%, #7da7e2 100%); } #search { text-align: center; height: 20vh; position: relative; } #location-input { height: 42px; width: 100%; opacity: 1; border: 0; border-radius: 2px; background-color: rgba(255, 255, 255, 0.2); margin-top: 16px; padding-left: 16px; color: #ffffff; font-size: 1.8rem; line-height: 21px; } #location-input:focus { outline: none; } ::placeholder { color: #FFFFFF; opacity: 0.6; } #current-weather { color: #ffffff; font-size: 8rem; line-height: 106px; position: relative; } #current-weather>span { color: #ffffff; font-size: 3.6rem; line-height: 42px; vertical-align: super; opacity: 0.8; top: 15px; position: absolute; } #weather-desc { font-size: 2.0rem; color: #ffffff; font-weight: 500; line-height: 24px; } #possibility { color: #ffffff; font-size: 16px; font-weight: 500; line-height: 19px; } #max-detail, #min-detail { color: #ffffff; font-size: 2.0rem; font-weight: 500; line-height: 24px; } #max-detail>i, #min-detail>i { font-style: normal; height: 13.27px; width: 16.5px; opacity: 0.4; } #max-detail>span, #min-detail>span { color: #ffffff; font-family: Roboto; font-size: 1.2rem; line-height: 10px; vertical-align: super; } #max-summary, #min-summary { opacity: 0.9; color: #ffffff; font-size: 1.4rem; line-height: 16px; margin-top: 2px; opacity: 0.7; } #search-btn { position: absolute; right: 0; top: 16px; padding: 2px; z-index: 999; height: 42px; width: 45px; background-color: rgba(255, 255, 255, 0.2); border: none; } #dashboard-content { text-align: center; height: 100vh; } #date-desc, #location-desc { color: #ffffff; font-size: 1.6rem; font-weight: 500; line-height: 19px; margin-bottom: 15px; } #date-desc>img { top: -3px; position: relative; margin-right: 10px; } #location-desc>img { top: -3px; position: relative; margin-left: 5px; margin-right: 15px; } #location-detail { opacity: 0.7; color: #ffffff; font-size: 1.4rem; line-height: 20px; margin-left: 35px; } .centered { position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%); } .max-desc { width: 80px; float: left; margin-right: 28px; } .temp-max-min { margin-top: 40px } #dashboard-content { background-color: #F7F7F7; } .custom-card { background-color: #FFFFFF !important; border: 0 !important; margin-top: 16px !important; margin-bottom: 20px !important; } .custom-content-card { background-color: #FFFFFF !important; border: 0 !important; margin-top: 16px !important; margin-bottom: 0px !important; } .header-card { height: 50vh; } .content-card { height: 43vh; } .card-divider { margin-top: 0; } .content-header { color: #8786A4; font-size: 1.4rem; line-height: 16px; font-weight: 500; padding: 15px 10px 5px 15px; } .highlights-item { min-height: 37vh; max-height: 38vh; background-color: #FFFFFF; } .card-heading { color: rgb(33, 34, 68); font-size: 1.8rem; font-weight: 500; line-height: 21px; text-align: center; } .card-sub-heading { color: #73748C; font-size: 1.6rem; line-height: 19px; } .card-value { color: #000000; font-size: 1.8rem; line-height: 21px; } span text { font-weight: 500 !important; } hr { padding-top: 1.5px; padding-bottom: 1px; margin-bottom: 0; margin-top: 0; line-height: 0.5px; } @media only screen and (min-width: 768px) { #sidebar { height: 100vh; } #info { position: fixed; bottom: 50px; width: 100%; padding-left: 15px; } .wrapper-right { margin-top: 80px; } } @media only screen and (min-width:1440px) { #sidebar { width: 350px; max-width: 350px; flex: auto; } #dashboard-content { width: calc(100% — 350px); max-width: calc(100% — 350px); flex: auto; } }
src/สินทรัพย์/
ในไดเร็กทอรีนี้ ให้ดาวน์โหลดและบันทึกไฟล์ . .svg
ที่กล่าวถึงด้านล่าง:
-
calendar.svg
-
location.svg
-
search.svg
-
winddirection.svg
-
windspeed.svg
src/components/Content.vue
นี่คือสิ่งที่เราเรียกว่า "องค์ประกอบใบ้" (เช่นตัวยึดตำแหน่ง) ที่มีเพียงเพื่อรักษาลำดับชั้น และส่งผ่านข้อมูลไปยังองค์ประกอบย่อยเป็นหลัก
จำไว้ว่าไม่มีแถบเทคนิคในการเขียนโค้ดทั้งหมดของเราในไฟล์ App.vue
แต่เราใช้วิธีแยกโค้ดโดยการซ้อนส่วนประกอบด้วยเหตุผลสองประการ:
- ในการเขียนโค้ดที่สะอาด ซึ่งช่วยให้อ่านง่ายและบำรุงรักษา
- เพื่อจำลองโครงสร้างแบบเดียวกับที่เราจะเห็นบนหน้าจอ กล่าวคือ ลำดับชั้น
ก่อนที่เราจะซ้อนองค์ประกอบที่กำหนดไว้ใน Content.vue
ภายในองค์ประกอบรู App.vue
มาเขียนโค้ดของเล่น (แต่เพื่อการศึกษา) สำหรับ Content.vue
:
<template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> </div> </template> <script> export default { data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> </style>
ในโค้ด ให้สังเกตและทำความเข้าใจสิ่งต่อไปนี้อย่างระมัดระวัง:
- ภายในแท็ก
<script>
(ที่เราเขียนโค้ด JavaScript อย่างชัดเจน) เรากำหนดออบเจกต์ที่ส่งออก (ทำให้ใช้งานได้กับไฟล์อื่น) โดยค่าเริ่มต้น ออบเจ็กต์นี้มีฟังก์ชันdata()
ที่ส่งคืนอ็อบเจ็กต์อาร์เรย์ที่เรียกว่าchildComponents
โดยมีองค์ประกอบเป็นชื่อของไฟล์ส่วนประกอบที่ควรซ้อนกันเพิ่มเติม - ภายในแท็ก
<template>
(ที่เราเขียนเทมเพลต HTML) สิ่งที่น่าสนใจคือ<ul>
- ภายในรายการที่ไม่เรียงลำดับ แต่ละรายการควรเป็นชื่อของคอมโพเนนต์ย่อยที่ต้องการ ตามที่กำหนดไว้ในอ็อบเจ็กต์อาร์เรย์
childComponents
นอกจากนี้ รายการควรขยายโดยอัตโนมัติจนถึงองค์ประกอบสุดท้ายของอาร์เรย์ ดูเหมือนว่าเราควรเขียนfor
-loop ใช่ไหม เราทำเช่นนั้นโดยใช้คำสั่งv-for
ที่จัดทำโดย Vue.js คำสั่งv-for
:- ทำหน้าที่เป็นแอตทริบิวต์ของแท็ก
<li>
วนซ้ำผ่านอาร์เรย์ แสดงชื่อของส่วนประกอบย่อยที่มีการกล่าวถึงตัววนซ้ำภายในวงเล็บ{{ }}
(ที่เราเขียนข้อความสำหรับรายการ)
- ทำหน้าที่เป็นแอตทริบิวต์ของแท็ก
- ภายในรายการที่ไม่เรียงลำดับ แต่ละรายการควรเป็นชื่อของคอมโพเนนต์ย่อยที่ต้องการ ตามที่กำหนดไว้ในอ็อบเจ็กต์อาร์เรย์
โค้ดและคำอธิบายข้างต้นเป็นพื้นฐานของความเข้าใจในภายหลังว่าสคริปต์และเทมเพลตมีความสัมพันธ์กันอย่างไร และเราจะใช้คำสั่งที่ Vue.js ให้มาได้อย่างไร
เราได้เรียนรู้อะไรมากมาย แต่หลังจากทั้งหมดนี้ เรายังมีสิ่งหนึ่งที่ต้องเรียนรู้เกี่ยวกับการเชื่อมต่อส่วนประกอบในลำดับชั้นอย่างราบรื่น — ส่งข้อมูลลงจากองค์ประกอบหลักไปยังลูกของมัน ในตอนนี้ เราต้องเรียนรู้วิธีส่งข้อมูลบางส่วนจาก src/App.vue
ไปยัง src/components/Content.vue
เพื่อที่เราจะสามารถใช้เทคนิคเดียวกันนี้กับส่วนประกอบอื่นๆ ที่ซ้อนกันในโปรเจ็กต์นี้ได้
ข้อมูลที่ไหลลงมาจากองค์ประกอบหลักไปยังองค์ประกอบย่อยอาจฟังดูง่าย แต่มารอยู่ในรายละเอียด! ดังที่อธิบายสั้น ๆ ด้านล่าง มีหลายขั้นตอนที่เกี่ยวข้องในการทำให้มันทำงาน:
- การกำหนดและข้อมูล
สำหรับตอนนี้ เราต้องการข้อมูลแบบคงที่ที่จะเล่นด้วย — วัตถุที่มีค่าฮาร์ดโค้ดเกี่ยวกับแง่มุมต่าง ๆ ของสภาพอากาศจะไม่เป็นไร! เราสร้างวัตถุที่เรียกว่าweather_data
และส่งคืนจากdata()
ฟังก์ชันของApp.vue
ออบเจ็กต์weather_data
ได้รับในตัวอย่างด้านล่าง:
weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, },
- ส่งข้อมูลจากผู้ปกครอง
ในการส่งข้อมูล เราต้องการปลายทางที่ต้องการส่งข้อมูล! ในกรณีนี้ ปลายทางคือองค์ประกอบContent.vue
และวิธีการนำไปใช้คือ:- กำหนดอ็อบเจ็กต์
weather_data
ให้กับ แอตทริบิวต์ที่กำหนดเอง ของแท็ก<Content>
- ผูกแอตทริบิวต์กับข้อมูลโดยใช้คำสั่ง
v-bind
: จัดทำโดย Vue.js ซึ่งทำให้ค่าแอตทริบิวต์เป็นแบบไดนามิก (ตอบสนองต่อการเปลี่ยนแปลงที่เกิดขึ้นในข้อมูลเดิม)<Content v-bind:weather_data=“weather_data”></Content>
- กำหนดอ็อบเจ็กต์
การกำหนดและส่งข้อมูลจะได้รับการจัดการที่ฝั่งต้นทางของการจับมือกัน ซึ่งในกรณีของเราคือไฟล์ App.vue
รหัสสำหรับไฟล์ App.vue
ที่สถานะปัจจุบัน แสดงไว้ด้านล่าง:
<template> <div> <p>This component's code is in {{ filename }}</p> <Content v-bind:weather_data="weather_data"></Content> </div> </template> <script> import Content from './components/Content.vue' export default { name: 'app', components: { 'Content': Content }, data () { return { filename: 'App.vue', weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "NE", }, visibility: "12 km", }, }, } }, methods: { }, computed: { }, } </script> <style> </style>
ด้วยข้อมูลที่กำหนดไว้และส่งต่อจากแหล่งที่มา (องค์ประกอบหลัก) ตอนนี้เป็นความรับผิดชอบของเด็กที่จะต้องรับข้อมูลและแสดงผลอย่างเหมาะสม ดังที่อธิบายไว้ในสองขั้นตอนถัดไป
- การรับข้อมูลโดยเด็ก
คอมโพเนนต์ย่อย ในกรณีนี้Content.vue
ต้องได้รับอ็อบเจ็กต์weather_data
ที่ส่งไปโดยคอมโพเนนต์หลักApp.vue
Vue.js มีกลไกในการทำเช่นนั้น — สิ่งที่คุณต้องมีคืออ็อบเจ็กต์อาร์เรย์ที่เรียกว่าprops
ซึ่งกำหนดไว้ในอ็อบเจ็กต์เริ่มต้นที่ส่งออกโดยContent.vue
แต่ละองค์ประกอบของprops
อาร์เรย์คือชื่อของออบเจ็กต์ข้อมูลที่ต้องการรับจากพาเรนต์ สำหรับตอนนี้ วัตถุข้อมูลเดียวที่ควรได้รับคือweather_data
จาก App.vue ดังนั้นprops
array จึงมีลักษณะดังนี้:
<template> // HTML template code here </template> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <style> // component specific CSS here </style>
- แสดงผลข้อมูลในหน้า
ตอนนี้เรามั่นใจว่าได้รับข้อมูลแล้ว งานสุดท้ายที่เราต้องทำให้เสร็จคือการแสดงข้อมูล สำหรับตัวอย่างนี้ เราจะถ่ายโอนข้อมูลที่ได้รับบนหน้าเว็บโดยตรง เพื่อแสดงเทคนิค อย่างไรก็ตาม ในการใช้งานจริง (เช่นเดียวกับที่เรากำลังจะสร้าง) ข้อมูลมักจะผ่านการประมวลผลจำนวนมาก และเฉพาะส่วนที่เกี่ยวข้องเท่านั้นที่จะแสดงในรูปแบบที่เหมาะสมกับวัตถุประสงค์ ตัวอย่างเช่น ในโปรเจ็กต์นี้ ในที่สุดเราจะได้ข้อมูลดิบจาก weather API ล้างข้อมูลและจัดรูปแบบ ป้อนข้อมูลไปยังโครงสร้างข้อมูลที่จำเป็นสำหรับแผนภูมิ จากนั้นแสดงเป็นภาพ อย่างไรก็ตาม เพื่อแสดงการถ่ายโอนข้อมูลดิบ เราจะใช้วงเล็บปีกกา{{ }}
ที่ Vue เข้าใจ ดังที่แสดงในตัวอย่างด้านล่าง:
<template> <div> // other template code here {{ weather_data }} </div> </template>
ถึงเวลาที่จะดูดซึมชิ้นส่วนและชิ้นส่วนทั้งหมด รหัสสำหรับ Content.vue
— ณ สถานะปัจจุบัน — ได้รับด้านล่าง:
<template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} </div> </template> <script> export default { props: ["weather_data"], data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> #pagecontent { border: 1px solid black; padding: 2px; } </style>
หลังจากทำการเปลี่ยนแปลงที่กล่าวถึงข้างต้นแล้ว ให้รีเฟรชหน้าเว็บบนเบราว์เซอร์และดูว่าหน้าตาเป็นอย่างไร ใช้เวลาสักครู่เพื่อชื่นชมความซับซ้อนที่ Vue จัดการ — หากคุณแก้ไขอ็อบเจ็กต์ weather_data
ใน App.vue
วัตถุนั้นจะถูกส่งต่อไปยัง Content.vue
อย่างเงียบๆ และสุดท้ายไปยังเบราว์เซอร์ที่แสดงหน้าเว็บ! ลองเปลี่ยนค่าสำหรับตำแหน่งคีย์
แม้ว่าเราได้เรียนรู้เกี่ยวกับอุปกรณ์ประกอบฉากและการเชื่อมโยงข้อมูลโดยใช้ข้อมูลแบบคงที่ เราจะใช้ข้อมูลแบบไดนามิกที่รวบรวมโดยใช้เว็บ API ในแอปพลิเคชัน และจะ เปลี่ยนโค้ดตาม นั้น
สรุป
ก่อนที่เราจะไปยังไฟล์ .vue
ที่เหลือ เรามาสรุปสิ่งที่เราได้เรียนรู้ในขณะที่เราเขียนโค้ดสำหรับ App.vue
และ components/Content.vue
ก่อน:
- ไฟล์
App.vue
คือสิ่งที่เราเรียกว่าองค์ประกอบรูท ซึ่งเป็นไฟล์ที่อยู่ด้านบนสุดของลำดับชั้นของส่วนประกอบ ไฟล์.vue
ที่เหลือแสดงถึงคอมโพเนนต์ที่เป็นลูกโดยตรง หลาน และอื่นๆ - ไฟล์
Content.vue
เป็นส่วนประกอบจำลอง — หน้าที่รับผิดชอบคือส่งต่อข้อมูลไปยังระดับด้านล่าง และรักษาลำดับชั้นของโครงสร้าง เพื่อให้โค้ดของเรายังคงสอดคล้องกับปรัชญา "*สิ่งที่เราเห็นคือสิ่งที่เราใช้*" - ความสัมพันธ์ระหว่างพ่อแม่และลูกของส่วนประกอบไม่ได้เกิดขึ้นโดยเปล่าประโยชน์ — คุณต้อง ลงทะเบียนส่วนประกอบ (ทั้งแบบทั่วโลกหรือในเครื่อง ขึ้นอยู่กับการใช้งานส่วนประกอบที่ต้องการ) จากนั้นจึง ซ้อน องค์ประกอบโดยใช้แท็ก HTML ที่กำหนดเอง (ซึ่งมีการสะกดที่ถูกต้อง เช่นเดียวกับชื่อที่จดทะเบียนส่วนประกอบ)
- เมื่อลงทะเบียนและซ้อนแล้ว ข้อมูลจะถูกส่งต่อจากองค์ประกอบหลักไปยังองค์ประกอบย่อย และโฟลว์จะ ไม่ย้อนกลับ (สิ่งเลวร้ายจะเกิดขึ้นหากสถาปัตยกรรมโปรเจ็กต์อนุญาตการย้อนกลับ) องค์ประกอบหลักเป็นแหล่งข้อมูลสัมพัทธ์ของข้อมูล และส่งต่อข้อมูลที่เกี่ยวข้องไปยังลูกๆ โดยใช้คำสั่ง
v-bind
สำหรับแอตทริบิวต์ขององค์ประกอบ HTML ที่กำหนดเอง เด็กได้รับข้อมูลที่ตั้งใจไว้โดยใช้อุปกรณ์ประกอบฉาก จากนั้นจึงตัดสินใจด้วยตัวเองว่าจะทำอย่างไรกับข้อมูลนั้น
สำหรับส่วนประกอบที่เหลือ เราจะไม่หลงระเริงกับคำอธิบายโดยละเอียด — เราจะเขียนโค้ดตามการเรียนรู้จากสรุปข้างต้น รหัสจะชัดเจนในตัวเอง และหากคุณสับสนเกี่ยวกับลำดับชั้น ให้ดูแผนภาพด้านล่าง:
ไดอะแกรมบอกว่า TempVarChart.vue
และ Highlights.vue
เป็นลูกของ Content.vue
โดยตรง ดังนั้นจึงเป็นความคิดที่ดีที่จะเตรียม Content.vue
สำหรับการส่งข้อมูลไปยังส่วนประกอบเหล่านั้น ซึ่งเราใช้โค้ดด้านล่างนี้:
<template> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue' import Highlights from './Highlights.vue' export default { props: ["weather_data"], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'], tempVar: this.weather_data.temperature, highlights: this.weather_data.highlights, } }, methods: { }, computed: { }, } </script> <style> </style>
เมื่อคุณบันทึกรหัสนี้ คุณจะได้รับข้อผิดพลาด ไม่ต้องกังวล เป็นไปตามที่คาดไว้ มันจะได้รับการแก้ไขเมื่อคุณมีไฟล์ส่วนประกอบที่เหลือพร้อม ถ้ามันรบกวนคุณที่ไม่สามารถดูผลลัพธ์ได้ ให้แสดงความคิดเห็นในบรรทัดที่มีแท็กองค์ประกอบที่กำหนดเอง <temp-var-chart>
และ <today-highlights>
สำหรับส่วนนี้ นี่คือโค้ดสุดท้ายของ Content.vue
สำหรับส่วนที่เหลือของส่วนนี้ เราจะอ้างอิงถึงรหัสนี้ ไม่ใช่รหัสก่อนหน้าที่เราเขียนเพื่อการเรียนรู้
src/components/TempVarChart.vue
ด้วยองค์ประกอบหลักที่ Content.vue
ส่งผ่านข้อมูล จึงต้องตั้งค่า TempVarChart.vue
เพื่อรับและแสดงผลข้อมูล ดังแสดงในโค้ดด้านล่าง:
<template> <div> <p>Temperature Information:</p> {{ tempVar }} </div> </template> <script> export default { props: ["tempVar"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>
src/components/Highlights.vue
ส่วนประกอบนี้ยังจะได้รับข้อมูลจาก App.vue
ซึ่งเป็นองค์ประกอบหลัก หลังจากนั้นควรเชื่อมโยงกับองค์ประกอบย่อยและควรส่งข้อมูลที่เกี่ยวข้องไปให้
มาดูรหัสการรับข้อมูลจากผู้ปกครองกันก่อน:
<template> <div> <p>Weather Highlights:</p> {{ highlights }} </div> </template> <script> export default { props: ["highlights"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>
ณ จุดนี้หน้าเว็บดูเหมือนภาพด้านล่าง:
ตอนนี้ เราต้องแก้ไขโค้ดของ Highlights.vue
เพื่อลงทะเบียนและซ้อนองค์ประกอบย่อยตามด้วยการส่งข้อมูลไปยังลูก รหัสสำหรับมันเป็นดังนี้:
<template> <div> <p>Weather Highlights:</p> {{ highlights }} <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>
เมื่อคุณบันทึกโค้ดและเห็นหน้าเว็บแล้ว คุณจะ ต้องเห็นข้อผิดพลาด ในเครื่องมือ Developer Console ที่เบราว์เซอร์จัดเตรียมไว้ให้ ปรากฏขึ้นเนื่องจากแม้ว่า Highlights.vue
กำลังส่งข้อมูล แต่ก็ไม่มีใครรับข้อมูลเหล่านี้ เรายังไม่ได้เขียนโค้ดสำหรับ ลูก ของ Highlights.vue
สังเกตว่าเราไม่ได้ประมวลผลข้อมูลมากนัก กล่าวคือ เราไม่ได้แยกปัจจัยแต่ละส่วนของข้อมูลสภาพอากาศที่อยู่ในส่วนไฮไลท์ของแดชบอร์ด เราสามารถทำได้ในฟังก์ชัน data()
แต่เราต้องการเก็บ Highlights.vue
เป็นองค์ประกอบที่โง่เขลาที่ส่งผ่านการถ่ายโอนข้อมูลทั้งหมดที่ได้รับไปยังเด็ก ๆ แต่ละคนซึ่งเป็นเจ้าของสารสกัดสิ่งที่จำเป็นสำหรับพวกเขา . อย่างไรก็ตาม เราขอแนะนำให้คุณลองดึงข้อมูลใน Highlights.vue
และส่งข้อมูลที่เกี่ยวข้องไปยังองค์ประกอบย่อยแต่ละรายการ ซึ่งเป็นแบบฝึกหัดที่ดี
src/components/UVIndex.vue
โค้ดสำหรับส่วนประกอบนี้ได้รับการถ่ายโอนข้อมูลของไฮไลท์จาก Highlights.vue
ดึงข้อมูลสำหรับดัชนี UV และแสดงผลบนหน้า
<template> <div> <p>UV Index: {{ uvindex }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { uvindex: this.highlights.uvindex } }, methods: { }, computed: { }, } </script> <style> </style>
src/components/Visibility.vue
โค้ดสำหรับส่วนประกอบนี้ได้รับการถ่ายโอนข้อมูลของไฮไลท์จาก Highlights.vue
ดึงข้อมูลสำหรับการมองเห็น และแสดงผลบนเพจ
<template> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility, } }, methods: { }, computed: { }, } </script> <style> </style>
src/components/WindStatus.vue
รหัสสำหรับส่วนประกอบนี้ได้รับการถ่ายโอนข้อมูลของไฮไลท์จาก Highlights.vue
ดึงข้อมูลสำหรับสถานะลม (ความเร็วและทิศทาง) และแสดงผลบนหน้า
<template> <div> <p>Wind Status:</p> <p>Speed — {{ speed }}; Direction — {{ direction }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { speed: this.highlights.windstatus.speed, direction: this.highlights.windstatus.direction } }, methods: { }, computed: { }, } </script> <style> </style>
หลังจากเพิ่มโค้ดสำหรับส่วนประกอบทั้งหมดแล้ว ให้ดูที่หน้าเว็บในเบราว์เซอร์
เพื่อไม่ให้ท้อแท้ แต่การทำงานหนักทั้งหมดเหล่านี้เป็นเพียงการเชื่อมโยงส่วนประกอบในลำดับชั้น และทดสอบว่าการไหลของข้อมูลเกิดขึ้นระหว่างพวกเขาหรือไม่! ในส่วนถัดไป เราจะ ทิ้งโค้ดส่วนใหญ่ที่เราเขียนไว้ทิ้งไป และเพิ่มที่เกี่ยวข้องกับโครงการจริงมากขึ้น อย่างไรก็ตาม เราจะคงโครงสร้างและการซ้อนส่วนประกอบไว้อย่างแน่นอน การเรียนรู้จากส่วนนี้จะช่วยให้เราสร้างแดชบอร์ดที่ดีด้วย Vue.js
4. การรับและประมวลผลข้อมูล
จำอ็อบเจ็กต์ weather_data
ใน App.vue
ได้หรือไม่ มีข้อมูลที่ฮาร์ดโค้ดบางส่วนที่เราใช้ในการทดสอบว่าส่วนประกอบทั้งหมดทำงานอย่างถูกต้องหรือไม่ และยังช่วยให้คุณเรียนรู้แง่มุมพื้นฐานบางประการของแอปพลิเคชัน Vue โดยไม่ต้องจมอยู่ในรายละเอียดของข้อมูลในโลกแห่งความเป็นจริง อย่างไรก็ตาม ถึงเวลาที่เราจะต้องกำจัดเชลล์ของเรา และก้าวเข้าสู่โลกแห่งความเป็นจริง ที่ซึ่งข้อมูลจาก API จะครอบงำโค้ดส่วนใหญ่ของเรา
การเตรียมส่วนประกอบย่อยเพื่อรับและประมวลผลข้อมูลจริง
ในส่วนนี้ คุณจะได้รับการดัมพ์โค้ดสำหรับส่วนประกอบทั้งหมด ยกเว้น App.vue
รหัสจะจัดการกับการรับข้อมูลจริงจาก App.vue
(ต่างจากรหัสที่เราเขียนในส่วนก่อนหน้าเพื่อรับและแสดงผลข้อมูลจำลอง)
เราขอแนะนำอย่างยิ่งให้อ่านโค้ดของส่วนประกอบแต่ละส่วนอย่างรอบคอบ เพื่อให้คุณได้แนวคิดว่าแต่ละคอมโพเนนต์คาดหวังข้อมูลใดบ้าง และสุดท้ายจะนำไปใช้ในการแสดงข้อมูลเป็นภาพ
โค้ดบางส่วนและโครงสร้างโดยรวมจะคล้ายกับที่คุณเคยเห็นในโครงสร้างก่อนหน้านี้ ดังนั้นคุณจะไม่ต้องเผชิญกับสิ่งที่แตกต่างอย่างมาก อย่างไรก็ตาม มารอยู่ในรายละเอียด! ดังนั้น ให้ตรวจสอบโค้ดอย่างละเอียด และเมื่อคุณเข้าใจดีพอสมควรแล้ว ให้คัดลอกโค้ดไปยังไฟล์ส่วนประกอบที่เกี่ยวข้องในโปรเจ็กต์ของคุณ
หมายเหตุ : ส่วนประกอบทั้งหมดในส่วนนี้อยู่ในไดเร็กทอรี src/components/
ดังนั้นในแต่ละครั้ง จะไม่มีการระบุพาธ แต่จะกล่าวถึงเฉพาะชื่อไฟล์ .vue
เพื่อระบุส่วนประกอบ
Content.vue
<template> <div> <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue'; import Highlights from './Highlights.vue'; export default { props: ['highlights', 'tempVar'], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, } </script>
มีการเปลี่ยนแปลงต่อไปนี้จากรหัสก่อนหน้า:
- ใน
<template>
ข้อความและข้อมูลภายใน{{ }}
ถูกลบออก เนื่องจากตอนนี้เราเพิ่งได้รับข้อมูลและส่งต่อไปยังลูกๆ โดยไม่มีการแสดงส่วนประกอบนี้โดยเฉพาะ - ในการ
export default {}
:-
props
ได้รับการเปลี่ยนแปลงเพื่อให้ตรงกับวัตถุข้อมูลที่จะส่งโดยผู้ปกครอง:App.vue
เหตุผลในการเปลี่ยนอุปกรณ์ประกอบฉากคือApp.vue
จะแสดงข้อมูลบางส่วนที่ได้รับจาก weather API และแหล่งข้อมูลออนไลน์อื่นๆ ตามคำค้นหาของผู้ใช้ และส่งต่อข้อมูลที่เหลือ ในโค้ดจำลองที่เราเขียนไว้ก่อนหน้านี้App.vue
กำลังส่งผ่านการถ่ายโอนข้อมูลจำลองทั้งหมด โดยไม่มีการเลือกปฏิบัติ และอุปกรณ์ประกอบฉากของContent.vue
ได้รับการตั้งค่าตามนั้น - ขณะนี้ฟังก์ชัน data() ไม่ส่งคืนสิ่งใด เนื่องจากเราไม่ได้ดำเนินการจัดการข้อมูลในคอมโพเนนต์นี้
-
TempVarChart.vue
คอมโพเนนต์นี้ควรได้รับการคาดคะเนอุณหภูมิโดยละเอียดสำหรับช่วงเวลาที่เหลือของวันปัจจุบัน และแสดงผลโดยใช้ FusionCharts ในท้ายที่สุด แต่ในขณะนี้ เราจะแสดงเป็นข้อความบนหน้าเว็บเท่านั้น
<template> <div> {{ tempVar.tempToday }} </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { }; }, methods: { }, }; </script> <style> </style>
Highlights.vue
<template> <div> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>
การเปลี่ยนแปลงที่เกิดจากรหัสก่อนหน้าคือ:
- ใน
<template>
ข้อความและข้อมูลภายใน{{ }}
ถูกลบออกเพราะนี่เป็นองค์ประกอบที่โง่เขลา เช่นเดียวกับContent.vue
ซึ่งงานเดียวคือการส่งต่อข้อมูลไปยังเด็ก ๆ ในขณะที่รักษาลำดับชั้นของโครงสร้าง โปรดจำไว้ว่าส่วนประกอบที่โง่เขลาเช่นHighlights.vue
และContent.vue
มีไว้เพื่อรักษาความเท่าเทียมกันระหว่างโครงสร้างภาพของแดชบอร์ดและโค้ดที่เราเขียน
UVIndex.vue
การเปลี่ยนแปลงที่ทำกับรหัสก่อนหน้ามีดังนี้:
- ใน
<template>
และ<style>
นั้นdiv id
ถูกเปลี่ยนเป็นuvIndex
ซึ่งสามารถอ่านได้ง่ายขึ้น - ในการ
export default {}
ฟังก์ชันdata()
จะคืนค่าออบเจ็กต์สตริงuvIndex
ซึ่งค่าจะถูกดึงมาจากออบเจ็กต์ไฮไลต์ที่ได้รับโดยคอมโพเนนต์โดยใช้props
uvIndex
นี้ใช้ชั่วคราวเพื่อแสดงค่าเป็นข้อความภายใน<template>
ต่อมา เราจะเสียบค่านี้เข้ากับโครงสร้างข้อมูลที่เหมาะสมสำหรับการแสดงแผนภูมิ
Visibility.vue
<template> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility.toString() } }, methods: { }, computed: { }, } </script> <style> </style>
การเปลี่ยนแปลงเพียงอย่างเดียวที่ทำในไฟล์นี้ (เทียบกับรหัสก่อนหน้า) คือคำจำกัดความของวัตถุ visibility
ที่ส่งคืนโดยฟังก์ชัน data()
มี toString()
ที่ส่วนท้าย เนื่องจากค่าที่ได้รับจากพาเรนต์จะเป็นค่าลอยตัว หมายเลขจุดซึ่งต้องแปลงเป็นสตริง
WindStatus.vue
<template> <div> <p>Wind Speed — {{ windSpeed }}</p> <p>Wind Direction — {{ derivedWindDirection }}, or {{ windDirection }} degree clockwise with respect to true N as 0 degree.</p> </div> </template> <script> export default { props: ["highlights"], data () { return { windSpeed: this.highlights.windStatus.windSpeed, derivedWindDirection: this.highlights.windStatus.derivedWindDirection, windDirection: this.highlights.windStatus.windDirection } }, methods: { }, computed: { }, } </script> <style> </style>
การเปลี่ยนแปลงที่ทำกับรหัสก่อนหน้ามีดังนี้:
- ตลอดทั้งไฟล์
windstatus
ถูกเปลี่ยนชื่อเป็นwindStatus
เพื่อส่งเสริมความสามารถในการอ่านและยังซิงค์กับออบเจ็กต์ไฮไลท์ที่App.vue
มอบให้กับข้อมูลจริง - มีการเปลี่ยนชื่อที่คล้ายกันสำหรับความเร็วและทิศทาง — อันใหม่คือ
windSpeed
และwindDirection
- วัตถุใหม่ที่ได้รับ
derivedWindDirection
ได้เข้ามาเล่นแล้ว (จัดเตรียมโดยApp.vue
ในชุดไฮไลท์)
สำหรับตอนนี้ ข้อมูลที่ได้รับจะแสดงเป็นข้อความ ในภายหลัง จะถูกเสียบเข้ากับโครงสร้างข้อมูลที่จำเป็นสำหรับการแสดงภาพ
การทดสอบด้วย Dummy Data
การใช้ข้อมูลจำลองซ้ำๆ อาจทำให้คุณหงุดหงิดเล็กน้อย แต่มีเหตุผลดีๆ อยู่เบื้องหลัง:
- เราได้ทำการเปลี่ยนแปลงมากมายในโค้ดของแต่ละส่วนประกอบ และเป็นความคิดที่ดีที่จะทดสอบว่าการเปลี่ยนแปลงเหล่านั้นทำให้โค้ดเสียหายหรือไม่ กล่าวอีกนัยหนึ่ง เราควรตรวจสอบว่ากระแสข้อมูลไม่เสียหายหรือไม่ ตอนนี้เรากำลังจะย้ายไปยังส่วนที่ซับซ้อนมากขึ้นของโครงการ
- ข้อมูลจริงจาก API สภาพอากาศออนไลน์จะต้องมีการนวดจำนวนมาก และอาจเป็นเรื่องยากสำหรับคุณที่จะสลับไปมาระหว่างรหัสสำหรับการได้มาและการประมวลผลข้อมูล กับรหัสสำหรับการไหลของข้อมูลที่ไหลลงส่วนประกอบอย่างราบรื่น แนวคิดคือควบคุมควอนตัมของความซับซ้อน เพื่อให้เราเข้าใจข้อผิดพลาดที่อาจเผชิญได้ดีขึ้น
ในส่วนนี้ สิ่งที่เราทำคือฮาร์ดโค้ดข้อมูล json บางส่วนใน App.vue
ซึ่งจะถูกแทนที่ด้วยข้อมูลสดในอนาคตอันใกล้นี้ มีความคล้ายคลึงกันมากระหว่างโครงสร้าง json จำลอง และโครงสร้าง json ที่เราจะใช้สำหรับข้อมูลจริง ดังนั้นจึงยังให้แนวคิดคร่าวๆ แก่คุณว่าควรคาดหวังอะไรจากข้อมูลจริง เมื่อเราพบมัน
อย่างไรก็ตาม เรายอมรับว่าวิธีนี้ยังห่างไกลจากแนวทางในอุดมคติที่อาจนำมาใช้ในขณะที่สร้างโครงการดังกล่าวตั้งแต่ต้น ในโลกแห่งความเป็นจริง คุณมักจะเริ่มต้นด้วยแหล่งข้อมูลจริง ลองใช้มันสักหน่อยเพื่อทำความเข้าใจว่าสิ่งใดควรและควรทำอย่างไรเพื่อควบคุมมัน จากนั้นคิดถึงโครงสร้างข้อมูล json ที่เหมาะสมเพื่อเก็บข้อมูลที่เกี่ยวข้อง เราตั้งใจปกป้องคุณจากงานสกปรกเหล่านั้น เนื่องจากจะทำให้คุณห่างไกลจากวัตถุประสงค์มากขึ้น — เรียนรู้วิธีใช้ Vue.js และ FusionCharts เพื่อสร้างแดชบอร์ด
มาดูโค้ดใหม่สำหรับ App.vue กัน:
<template> <div> <dashboard-content :highlights="highlights" :tempVar="tempVar"></dashboard-content> </div> </template> <script> import Content from './components/Content.vue' export default { name: 'app', components: { 'dashboard-content': Content }, data () { return { tempVar: { tempToday: [ {hour: '11.00 AM', temp: '35'}, {hour: '12.00 PM', temp: '36'}, {hour: '1.00 PM', temp: '37'}, {hour: '2.00 PM', temp: '38'}, {hour: '3.00 PM', temp: '36'}, {hour: '4.00 PM', temp: '35'}, ], }, highlights: { uvIndex: 4, visibility: 10, windStatus: { windSpeed: '30 km/h', windDirection: '30', derivedWindDirection: 'NNE', }, }, } }, methods: { }, computed: { }, } </script> <style> </style>
การเปลี่ยนแปลงที่ทำกับโค้ดเกี่ยวกับเวอร์ชันก่อนหน้ามีดังนี้:
- ชื่อขององค์ประกอบลูกถูกเปลี่ยนเป็นเนื้อหาแดชบอร์ด และตามนั้นองค์ประกอบ HTML ที่กำหนดเองใน
<template>
ได้รับการแก้ไขแล้ว โปรดทราบว่าตอนนี้เรามีแอตทริบิวต์สองรายการ -highlights
และtempVar
- แทนที่จะเป็นแอตทริบิวต์เดียวที่เราใช้ก่อนหน้านี้กับองค์ประกอบที่กำหนดเอง ดังนั้น ข้อมูลที่เกี่ยวข้องกับแอตทริบิวต์เหล่านั้นก็เปลี่ยนไปเช่นกัน สิ่งที่น่าสนใจในที่นี้คือ เราสามารถใช้v-bind:
directive หรือ shorthand:
(ดังที่เราทำที่นี่) พร้อมแอตทริบิวต์หลายรายการขององค์ประกอบ HTML ที่กำหนดเอง! - ตอนนี้ฟังก์ชัน
data()
จะส่งคืนอ็อบเจ็กต์filename
(ที่มีอยู่ก่อนหน้านี้) พร้อมกับออบเจ็กต์ใหม่สองรายการ (แทนที่จะเป็นweather_data
เก่า):tempVar
และhighlights
โครงสร้างของ json นั้นเหมาะสมกับโค้ดที่เราเขียนในคอมโพเนนต์ย่อย เพื่อให้สามารถดึงข้อมูลที่ต้องการออกจากดัมพ์ได้ โครงสร้างค่อนข้างอธิบายได้ด้วยตนเอง และคุณสามารถคาดหวังได้ว่าโครงสร้างจะคล้ายกันมากเมื่อเราจัดการกับข้อมูลสด อย่างไรก็ตาม การเปลี่ยนแปลงที่สำคัญที่คุณจะพบคือการไม่มีฮาร์ดโค้ด (ใช่หรือไม่) เราจะปล่อยให้ค่าว่างเป็นสถานะเริ่มต้น และเขียนโค้ดเพื่ออัปเดตแบบไดนามิกตามค่าที่เราจะได้รับจาก API สภาพอากาศ
คุณได้เขียนโค้ดจำนวนมากในส่วนนี้ โดยไม่เห็นผลลัพธ์จริง ก่อนที่คุณจะดำเนินการต่อไป ให้ดูที่เบราว์เซอร์ (รีสตาร์ทเซิร์ฟเวอร์ด้วย npm run dev
หากจำเป็น) และชื่นชมยินดีในความสำเร็จของคุณ หน้าเว็บที่คุณควรเห็น ณ จุดนี้ดูเหมือนภาพด้านล่าง:
รหัสสำหรับการได้มาและการประมวลผลข้อมูล
ส่วนนี้จะเป็นส่วนของโครงการ โดยมีรหัสทั้งหมดที่จะเขียนใน App.vue
สำหรับสิ่งต่อไปนี้:
- การป้อนข้อมูลตำแหน่งจากผู้ใช้ — กล่องใส่ข้อมูลและปุ่มคำกระตุ้นการตัดสินใจก็เพียงพอแล้ว
- ฟังก์ชันยูทิลิตี้สำหรับงานต่างๆ ฟังก์ชันเหล่านี้จะถูกเรียกในภายหลังในส่วนต่างๆ ของรหัสส่วนประกอบ
- รับข้อมูลตำแหน่งทางภูมิศาสตร์โดยละเอียดจาก Google Maps API สำหรับ JavaScript
- รับข้อมูลสภาพอากาศโดยละเอียดจาก Dark Sky API
- การจัดรูปแบบและการประมวลผลข้อมูลตำแหน่งทางภูมิศาสตร์และสภาพอากาศ ซึ่งจะถูกส่งต่อไปยังองค์ประกอบย่อย
ส่วนย่อยที่ตามมาแสดงให้เห็นว่าเราสามารถใช้งานที่กำหนดไว้สำหรับเราในประเด็นข้างต้นได้อย่างไร ส่วนใหญ่จะทำตามลำดับยกเว้นบางกรณี
ข้อมูลจากผู้ใช้
เห็นได้ชัดว่าการดำเนินการเริ่มต้นเมื่อผู้ใช้ระบุชื่อสถานที่ที่จำเป็นต้องแสดงข้อมูลสภาพอากาศ เพื่อให้สิ่งนี้เกิดขึ้น เราจำเป็นต้องดำเนินการดังต่อไปนี้:
- ช่องป้อนข้อมูลสำหรับป้อนที่ตั้ง
- ปุ่มส่งที่บอกแอปพลิเคชันของเราว่าผู้ใช้ได้เข้าไปในสถานที่แล้ว และได้เวลาดำเนินการที่เหลือ เราจะใช้ลักษณะการทำงานเมื่อเริ่มการประมวลผลเมื่อกดปุ่ม Enter
โค้ดที่เราแสดงด้านล่างนี้จะจำกัดเฉพาะส่วนเทมเพลต HTML ของ App.vue
เราจะพูดถึงชื่อของวิธีการที่เกี่ยวข้องกับเหตุการณ์การคลิก และกำหนดในภายหลังในวิธีการวัตถุของ <script> ใน App.vue
<div> <input type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div>
การวางข้อมูลโค้ดด้านบนในตำแหน่งที่ถูกต้องเป็นเรื่องเล็กน้อย เราปล่อยให้เป็นหน้าที่ของคุณ อย่างไรก็ตาม ส่วนที่น่าสนใจของตัวอย่างนี้คือ:
-
@keyup.enter="organizeAllDetails"
-
@click="organizeAllDetails"
ดังที่คุณทราบจากส่วนก่อนหน้านี้ @
เป็นชวเลขของ Vue สำหรับ directive v-on
: ซึ่งเกี่ยวข้องกับเหตุการณ์บางอย่าง สิ่งใหม่คือ “ organizeAllDetails
” — ไม่ใช่แค่วิธีการที่จะเริ่มทำงานเมื่อเหตุการณ์ (กด Enter หรือคลิกปุ่ม) เกิดขึ้น เรายังไม่ได้กำหนดวิธีการ และปริศนาจะสมบูรณ์ในตอนท้ายของส่วนนี้
การแสดงข้อมูลข้อความควบคุมโดย App.vue
เมื่ออินพุตของผู้ใช้ทริกเกอร์การดำเนินการและได้รับข้อมูลจำนวนมากจาก API เราพบคำถามที่หลีกเลี่ยงไม่ได้ - "จะทำอย่างไรกับข้อมูลทั้งหมดเหล่านี้" เห็นได้ชัดว่าจำเป็นต้องมีการนวดข้อมูล แต่นั่นไม่ได้ตอบคำถามของเราอย่างเต็มที่! เราจำเป็นต้องตัดสินใจว่าปลายการใช้งานของข้อมูลคืออะไร หรือมากกว่าโดยตรง เอนทิตีใดที่ได้รับส่วนต่างๆ ของข้อมูลที่ได้มาและประมวลผล
ส่วนประกอบย่อยของ App.vue
ตามลำดับชั้นและวัตถุประสงค์ เป็นคู่แข่งสำคัญของข้อมูลจำนวนมาก อย่างไรก็ตาม เราจะมีข้อมูลบางอย่างที่ไม่ได้เป็นขององค์ประกอบย่อยใด ๆ เหล่านั้น แต่ให้ข้อมูลค่อนข้างมากและทำให้แดชบอร์ดสมบูรณ์ เราสามารถใช้ประโยชน์ได้ดีหากเราแสดงเป็นข้อมูลข้อความที่ควบคุมโดย App.vue
โดยตรง ในขณะที่ข้อมูลที่เหลือจะถูกส่งต่อไปยังเด็กเพื่อให้แสดงผลเป็นแผนภูมิที่สวยงามในท้ายที่สุด
เมื่อคำนึงถึงบริบทนี้ เรามาเน้นที่โค้ดสำหรับกำหนดขั้นตอนการใช้ข้อมูลข้อความ เป็นเทมเพลต HTML แบบง่าย ณ จุดนี้ซึ่งข้อมูลจะมาและนั่งในที่สุด
<div> <div class="wrapper-left"> <div> {{ currentWeather.temp }} <span>°C</span> </div> <div>{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div> <img src="./assets/location.svg" width="10.83" height="15.83" > {{ currentWeather.full_location }} <div class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div>
ในตัวอย่างข้างต้น คุณควรเข้าใจสิ่งต่อไปนี้:
- สิ่งที่อยู่ภายใน
{{ }}
— เป็นวิธีการแทรกข้อมูลไดนามิกของ Vue ในเทมเพลต HTML ก่อนที่จะแสดงผลในเบราว์เซอร์ คุณเคยเจอพวกเขามาก่อน และไม่มีอะไรใหม่หรือน่าประหลาดใจ เพียงจำไว้ว่าอ็อบเจ็กต์ข้อมูลเหล่านี้เกิดจากเมธอดdata()
ในอ็อบเจ็กต์export default()
ของApp.vue
พวกมันมีค่าเริ่มต้นที่เราจะตั้งค่าตามความต้องการของเรา จากนั้นจึงเขียนวิธีการบางอย่างเพื่อเติมอ็อบเจ็กต์ด้วยข้อมูล API จริง
ไม่ต้องกังวลว่าจะไม่เห็นการเปลี่ยนแปลงบนเบราว์เซอร์ ข้อมูลยังไม่ได้กำหนดไว้ และเป็นเรื่องปกติที่ Vue จะไม่แสดงสิ่งที่ไม่ทราบ อย่างไรก็ตาม เมื่อตั้งค่าข้อมูล แล้ว (และสำหรับตอนนี้ คุณยังตรวจสอบได้โดยฮาร์ดโค้ดข้อมูล) ข้อมูลข้อความจะถูกควบคุมโดย App.vue
data()
วิธีการ
เมธอด data()
เป็นโครงสร้างพิเศษในไฟล์ .vue
ซึ่งมีและส่งคืนอ็อบเจ็กต์ข้อมูลที่สำคัญสำหรับแอปพลิเคชัน ระลึกถึงโครงสร้างทั่วไปของส่วน <script>
ในไฟล์ .vue
ใด ๆ — ประกอบด้วยข้อมูลคร่าวๆ ดังนี้:
<script> // import statements here export default { // name, components, props, etc. data() { return { // the data that is so crucial for the application is defined here. // the data objects will have certain default values chosen by us. // The methods that we define below will manipulate the data. // Since the data is bounded to various attributes and directives, they // will update as and when the values of the data objects change. } }, methods: { // methods (objects whose values are functions) here. // bulk of dynamic stuff (the black magic part) is controlled from here. }, computed: { // computed properties here }, // other objects, as necessary } </script>
จนถึงตอนนี้ คุณได้พบชื่อของออบเจ็กต์ข้อมูลบางส่วนแล้ว แต่มีมากกว่านั้นอีกมาก ส่วนใหญ่เกี่ยวข้องกับองค์ประกอบย่อย ซึ่งแต่ละส่วนจะจัดการกับการถ่ายโอนข้อมูลสภาพอากาศในแง่มุมที่แตกต่างกัน ด้านล่างนี้คือเมธอด data()
ทั้งหมดที่เราต้องการสำหรับโปรเจ็กต์นี้ - คุณจะมีความคิดที่ยุติธรรมเกี่ยวกับข้อมูลที่เราคาดหวังจาก API และวิธีที่เราเผยแพร่ข้อมูล โดยอิงตามระบบการตั้งชื่อของออบเจกต์
data() { return { weatherDetails: false, location: '', // raw location from input lat: '', // raw latitude from google maps api response long: '', // raw longitude from google maps api response completeWeatherApi: '', // weather api string with lat and long rawWeatherData: '', // raw response from weather api currentWeather: { full_location: '', // for full address formatted_lat: '', // for N/S formatted_long: '', // for E/W time: '', temp: '', todayHighLow: { todayTempHigh: '', todayTempHighTime: '', todayTempLow: '', todayTempLowTime: '' }, summary: '', possibility: '' }, tempVar: { tempToday: [ // gets added dynamically by this.getSetHourlyTempInfoToday() ], }, highlights: { uvIndex: '', visibility: '', windStatus: { windSpeed: '', windDirection: '', derivedWindDirection: '' }, } }; },
อย่างที่คุณเห็น ในกรณีส่วนใหญ่ ค่าเริ่มต้นจะว่างเปล่า เพราะนั่นก็เพียงพอแล้ว ณ จุดนี้ เมธอดจะถูกเขียนขึ้นเพื่อจัดการข้อมูลและเติมด้วยค่าที่เหมาะสม ก่อนที่จะแสดงผลหรือส่งต่อไปยังคอมโพเนนต์ย่อย
วิธีการใน App.vue
สำหรับไฟล์ .vue
โดยทั่วไปเมธอดจะถูกเขียนเป็นค่าของคีย์ที่ซ้อนอยู่ในอ็อบเจ็กต์ methods { }
บทบาทหลักของพวกเขาคือการจัดการวัตถุข้อมูลของส่วนประกอบ เราจะเขียนวิธีการใน App.vue
โดยคำนึงถึงปรัชญาเดียวกัน อย่างไรก็ตาม ตามจุดประสงค์ เราสามารถจัดหมวดหมู่วิธีการของ App.vue
ได้ดังต่อไปนี้:
- วิธียูทิลิตี้
- วิธีการเชิงการกระทำ/เหตุการณ์
- วิธีการเก็บข้อมูล
- วิธีการประมวลผลข้อมูล
- วิธีการติดกาวระดับสูง
สิ่งสำคัญคือคุณต้องเข้าใจสิ่งนี้ — เรากำลังนำเสนอวิธีการแก่คุณบนถาดเพราะเราได้ทราบแล้วว่า API ทำงานอย่างไร ข้อมูลที่พวกเขาให้ และเราควรใช้ข้อมูลในโครงการของเราอย่างไร ไม่ใช่ว่าเราดึงวิธีการออกจากอากาศและเขียนโค้ดลับเพื่อจัดการกับข้อมูล เพื่อจุดประสงค์ในการเรียนรู้ คุณควรหมั่นอ่านและทำความเข้าใจโค้ดสำหรับวิธีการและข้อมูลอย่างขยันขันแข็ง อย่างไรก็ตาม เมื่อต้องเผชิญกับโปรเจ็กต์ใหม่ที่คุณต้องสร้างใหม่ทั้งหมด คุณต้องทำงานสกปรกทั้งหมดด้วยตัวเอง และนั่นหมายถึงการทดลองอย่างมากกับ API — การเข้าถึงแบบเป็นโปรแกรมและโครงสร้างข้อมูลของพวกมัน ก่อนที่จะรวมเข้ากับข้อมูลอย่างราบรื่น โครงสร้างที่โครงการของคุณต้องการ คุณจะไม่มีการจับมือกัน และจะมีช่วงเวลาที่น่าผิดหวัง แต่นั่นเป็นส่วนหนึ่งของการเติบโตเต็มที่ในฐานะนักพัฒนา
ในส่วนย่อยต่อไปนี้ เราจะอธิบายแต่ละประเภทของเมธอด และแสดงการใช้งานเมธอดที่อยู่ในหมวดหมู่นั้นด้วย ชื่อเมธอดนั้นค่อนข้างอธิบายตนเองได้เกี่ยวกับจุดประสงค์ของวิธีการเหล่านี้ และการใช้งานก็เช่นกัน ซึ่งเราเชื่อว่าคุณจะพบว่าง่ายพอที่จะปฏิบัติตาม อย่างไรก็ตาม ก่อนหน้านั้น ให้ระลึกถึงโครงร่างทั่วไปของวิธีการเขียนในไฟล์ . .vue
:
<script> // import statements here export default { // name, components, props, etc. data() { return { // the data that is so crucial for the application is defined here. } }, methods: { // methods (objects whose values are functions) here. // bulk of dynamic stuff (the black magic part) is controlled from here. method_1: function(arg_1) { }, method_2: function(arg_1, arg_2) { }, method_3: function(arg_1) { }, ……. }, computed: { // computed properties here }, // other objects, as necessary } </script>
ยูทิลิตี้วิธีการ
วิธีอรรถประโยชน์ดังที่ชื่อแนะนำคือวิธีการที่เขียนขึ้นเพื่อวัตถุประสงค์ในการทำให้โค้ดซ้ำ ๆ กลายเป็นโมดูลที่ใช้สำหรับงานนอกกรอบ พวกเขาถูกเรียกโดยวิธีอื่นเมื่อจำเป็น รับด้านล่างเป็นวิธียูทิลิตี้สำหรับ App.vue
:
convertToTitleCase: function(str) { str = str.toLowerCase().split(' '); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); },
// To format the “possibility” (of weather) string obtained from the weather API formatPossibility: function(str) { str = str.toLowerCase().split('-'); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); },
// To convert Unix timestamps according to our convenience unixToHuman: function(timezone, timestamp) { /* READ THIS BEFORE JUDGING & DEBUGGING For any location beyond the arctic circle and the antarctic circle, the goddamn weather api does not return certain keys/values in each of this.rawWeatherData.daily.data[some_array_index]. Due to this, console throws up an error. The code is correct, the problem is with the API. May be later on I will add some padding to tackle missing values. */ var moment = require('moment-timezone'); // for handling date & time var decipher = new Date(timestamp * 1000); var human = moment(decipher) .tz(timezone) .format('llll'); var timeArray = human.split(' '); var timeNumeral = timeArray[4]; var timeSuffix = timeArray[5]; var justTime = timeNumeral + ' ' + timeSuffix; var monthDateArray = human.split(','); var monthDate = monthDateArray[1].trim(); return { fullTime: human, onlyTime: justTime, onlyMonthDate: monthDate }; },
// To convert temperature from fahrenheit to celcius fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; },
// To convert the air pressure reading from millibar to kilopascal milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); },
// To convert distance readings from miles to kilometers mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); },
// To format the wind direction based on the angle deriveWindDir: function(windDir) { var wind_directions_array = [ { minVal: 0, maxVal: 30, direction: 'N' }, { minVal: 31, maxVal: 45, direction: 'NNE' }, { minVal: 46, maxVal: 75, direction: 'NE' }, { minVal: 76, maxVal: 90, direction: 'ENE' }, { minVal: 91, maxVal: 120, direction: 'E' }, { minVal: 121, maxVal: 135, direction: 'ESE' }, { minVal: 136, maxVal: 165, direction: 'SE' }, { minVal: 166, maxVal: 180, direction: 'SSE' }, { minVal: 181, maxVal: 210, direction: 'S' }, { minVal: 211, maxVal: 225, direction: 'SSW' }, { minVal: 226, maxVal: 255, direction: 'SW' }, { minVal: 256, maxVal: 270, direction: 'WSW' }, { minVal: 271, maxVal: 300, direction: 'W' }, { minVal: 301, maxVal: 315, direction: 'WNW' }, { minVal: 316, maxVal: 345, direction: 'NW' }, { minVal: 346, maxVal: 360, direction: 'NNW' } ]; var wind_direction = ''; for (var i = 0; i < wind_directions_array.length; i++) { if ( windDir >= wind_directions_array[i].minVal && windDir <= wind_directions_array[i].maxVal ) { wind_direction = wind_directions_array[i].direction; } } return wind_direction; },
แม้ว่าเราจะยังไม่ได้ใช้งาน แต่คุณสามารถนำวิธีอรรถประโยชน์ออกจากไฟล์ .vue
และใส่ไว้ในไฟล์ JavaScript แยกต่างหากได้ สิ่งที่คุณต้องทำคือนำเข้าไฟล์ . .js
ที่จุดเริ่มต้นของส่วนสคริปต์ในไฟล์ .vue
และคุณน่าจะพร้อมแล้ว วิธีการดังกล่าวใช้ได้ผลดีจริง ๆ และทำให้โค้ดสะอาด โดยเฉพาะอย่างยิ่งในแอปพลิเคชันขนาดใหญ่ที่คุณอาจใช้วิธีการมากมายที่จัดกลุ่มเข้าด้วยกันตามวัตถุประสงค์ได้ดีขึ้น คุณสามารถใช้แนวทางนี้กับกลุ่มเมธอดทั้งหมดที่แสดงรายการในบทความนี้ และดูผลลัพธ์ได้ อย่างไรก็ตาม เราขอแนะนำให้คุณทำแบบฝึกหัดนั้นเมื่อคุณได้ปฏิบัติตามหลักสูตรที่นำเสนอนี้แล้ว เพื่อให้คุณมีความเข้าใจในภาพรวมของส่วนต่างๆ ที่ทำงานประสานกันอย่างสมบูรณ์ และยังมีชิ้นส่วนของซอฟต์แวร์ที่คุณสามารถอ้างอิงได้ แตกขณะทดลอง
แนวทางการดำเนินการ/เหตุการณ์
โดยทั่วไป เมธอดเหล่านี้จะดำเนินการเมื่อเราจำเป็นต้องดำเนินการที่เกี่ยวข้องกับเหตุการณ์ เหตุการณ์อาจถูกทริกเกอร์จากการโต้ตอบของผู้ใช้หรือโดยทางโปรแกรม ทั้งนี้ขึ้นอยู่กับกรณี ในไฟล์ App.vue
วิธีการเหล่านี้อยู่ใต้วิธีการยูทิลิตี้
makeInputEmpty: function() { this.$refs.input.value = ''; },
makeTempVarTodayEmpty: function() { this.tempVar.tempToday = []; },
detectEnterKeyPress: function() { var input = this.$refs.input; input.addEventListener('keyup', function(event) { event.preventDefault(); var enterKeyCode = 13; if (event.keyCode === enterKeyCode) { this.setHitEnterKeyTrue(); } }); },
locationEntered: function() { var input = this.$refs.input; if (input.value === '') { this.location = "New York"; } else { this.location = this.convertToTitleCase(input.value); } this.makeInputEmpty(); this.makeTempVarTodayEmpty(); },
สิ่งที่น่าสนใจอย่างหนึ่งในข้อมูลโค้ดด้านบนบางส่วนคือการใช้ $ref
พูดง่ายๆ ว่า Vue เป็นวิธีการเชื่อมโยงคำสั่งโค้ดที่มีคำสั่ง Vue กับโครงสร้าง HTML ที่น่าจะส่งผลกระทบ (สำหรับข้อมูลเพิ่มเติม โปรดอ่านคู่มืออย่างเป็นทางการ) ตัวอย่างเช่น เมธอด makeInputEmpty()
และ detectEnterKeyPress detectEnterKeyPress()
ส่งผลต่อกล่องอินพุต เนื่องจากใน HTML ของช่องอินพุต เราได้กล่าวถึงค่าของแอตทริบิวต์ ref
เป็น input
วิธีการได้มาซึ่งข้อมูล
เรากำลังใช้ API สองรายการต่อไปนี้ในโครงการของเรา:
- Google Maps Geocoder API
API นี้ใช้สำหรับรับพิกัดของสถานที่ที่ผู้ใช้ค้นหา คุณจะต้องใช้คีย์ API สำหรับตัวคุณเอง ซึ่งสามารถรับได้โดยทำตามเอกสารประกอบในลิงก์ที่ให้มา ในตอนนี้ คุณสามารถใช้คีย์ API ที่ใช้โดย FusionCharts ได้ แต่เราขอให้คุณอย่าใช้มันในทางที่ผิดและรับคีย์ของคุณเอง เราอ้างอิง JavaScript API จาก index.html ของโปรเจ็กต์นี้ และเราจะใช้คอนสตรัคเตอร์ที่มันจัดเตรียมไว้ให้สำหรับโค้ดของเราในไฟล์App.vue
- The Dark Sky Weather API
API นี้ใช้สำหรับรับข้อมูลสภาพอากาศที่สอดคล้องกับพิกัด อย่างไรก็ตาม เราจะไม่ใช้งานโดยตรง เราจะรวมไว้ใน URL ที่เปลี่ยนเส้นทางผ่านหนึ่งในเซิร์ฟเวอร์ของ FusionCharts เหตุผลก็คือ หากคุณส่งคำขอ GET ไปยัง API จากแอปพลิเคชันฝั่งไคลเอ็นต์ทั้งหมด เช่น แอปพลิเคชันของเรา จะส่งผลให้เกิดข้อผิดพลาดCORS
ที่น่าผิดหวัง (ข้อมูลเพิ่มเติม ที่นี่ และ ที่นี่)
หมายเหตุสำคัญ : เนื่องจากเราใช้ Google Maps และ API ของ Dark Sky API ทั้งสองจึงมีคีย์ API ของตัวเองซึ่งเราได้แชร์กับคุณในบทความนี้ ซึ่งจะช่วยให้คุณมุ่งเน้นไปที่การพัฒนาฝั่งไคลเอ็นต์มากกว่าที่จะปวดหัวในการใช้งานแบ็กเอนด์ อย่างไรก็ตาม เราขอแนะนำให้คุณสร้างคีย์ของคุณเอง เนื่องจากคีย์ API ของเราจะมาพร้อมกับขีดจำกัด และหากเกินขีดจำกัดเหล่านี้ คุณจะไม่สามารถลองใช้แอปพลิเคชันด้วยตัวเองได้
สำหรับ Google Maps ให้ไปที่บทความนี้เพื่อรับคีย์ API ของคุณ สำหรับ Dark Sky API โปรดไปที่ https://darksky.net/dev เพื่อสร้างคีย์ API ของคุณและปลายทางที่เกี่ยวข้อง
เมื่อคำนึงถึงบริบทแล้ว มาดูการใช้งานวิธีการเก็บข้อมูลสำหรับโครงการของเรากัน
getCoordinates: function() { this.locationEntered(); var loc = this.location; var coords; var geocoder = new google.maps.Geocoder(); return new Promise(function(resolve, reject) { geocoder.geocode({ address: loc }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { this.lat = results[0].geometry.location.lat(); this.long = results[0].geometry.location.lng(); this.full_location = results[0].formatted_address; coords = { lat: this.lat, long: this.long, full_location: this.full_location }; resolve(coords); } else { alert("Oops! Couldn't get data for the location"); } }); }); },
/* The coordinates that Google Maps Geocoder API returns are way too accurate for our requirements. We need to bring it into shape before passing the coordinates on to the weather API. Although this is a data processing method in its own right, we can't help mentioning it right now, because the data acquisition method for the weather API has dependency on the output of this method. */ setFormatCoordinates: async function() { var coordinates = await this.getCoordinates(); this.lat = coordinates.lat; this.long = coordinates.long; this.currentWeather.full_location = coordinates.full_location; // Remember to beautify lat for N/S if (coordinates.lat > 0) { this.currentWeather.formatted_lat = (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N'; } else if (coordinates.lat < 0) { this.currentWeather.formatted_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 0) { this.currentWeather.formatted_long = (Math.round(coordinates.long * 10000) / 10000).toString() + '°E'; } else if (coordinates.long < 0) { this.currentWeather.formatted_long = (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() + '°W'; } else { this.currentWeather.formatted_long = ( Math.round(coordinates.long * 10000) / 10000 ).toString(); } },
/* This method dynamically creates the the correct weather API query URL, based on the formatted latitude and longitude. The complete URL is then fed to the method querying for weather data. Notice that the base URL used in this method (without the coordinates) points towards a FusionCharts server — we must redirect our GET request to the weather API through a server to avoid the CORS error. */ fixWeatherApi: async function() { await this.setFormatCoordinates(); var weatherApi = 'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' + this.lat + '&long=' + this.long; this.completeWeatherApi = weatherApi; },
fetchWeatherData: async function() { await this.fixWeatherApi(); var axios = require('axios'); // for handling weather api promise var weatherApiResponse = await axios.get(this.completeWeatherApi); if (weatherApiResponse.status === 200) { this.rawWeatherData = weatherApiResponse.data; } else { alert('Hmm... Seems like our weather experts are busy!'); } },
Through these methods, we have introduced the concept of async-await in our code. If you have been a JavaScript developer for some time now, you must be familiar with the callback hell, which is a direct consequence of the asynchronous way JavaScript is written. ES6 allows us to bypass the cumbersome nested callbacks, and our code becomes much cleaner if we write JavaScript in a synchronous way, using the async-await technique. However, there is a downside. It takes away the speed that asynchronous code gives us, especially for the portions of the code that deals with data being exchanged over the internet. Since this is not a mission-critical application with low latency requirements, and our primary aim is to learn stuff, the clean code is much more preferable over the slightly fast code.
Data Processing Methods
Now that we have the methods that will bring the data to us, we need to prepare the ground for properly receiving and processing the data. Safety nets must be cast, and there should be no spills — data is the new gold (OK, that might be an exaggeration in our context)! Enough with the fuss, let's get to the point.
Technically, the methods we implement in this section are aimed at getting the data out of the acquisition methods and the data objects in App.vue
, and sometimes setting the data objects to certain values that suits the purpose.
getTimezone: function() { return this.rawWeatherData.timezone; },
getSetCurrentTime: function() { var currentTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); this.currentWeather.time = this.unixToHuman( timezone, currentTime ).fullTime; },
getSetSummary: function() { var currentSummary = this.convertToTitleCase( this.rawWeatherData.currently.summary ); if (currentSummary.includes(' And')) { currentSummary = currentSummary.replace(' And', ','); } this.currentWeather.summary = currentSummary; },
getSetPossibility: function() { var possible = this.formatPossibility(this.rawWeatherData.daily.icon); if (possible.includes(' And')) { possible = possible.replace(' And', ','); } this.currentWeather.possibility = possible; },
getSetCurrentTemp: function() { var currentTemp = this.rawWeatherData.currently.temperature; this.currentWeather.temp = this.fahToCel(currentTemp); },
getTodayDetails: function() { return this.rawWeatherData.daily.data[0]; },
getSetTodayTempHighLowWithTime: function() { var timezone = this.getTimezone(); var todayDetails = this.getTodayDetails(); this.currentWeather.todayHighLow.todayTempHigh = this.fahToCel( todayDetails.temperatureMax ); this.currentWeather.todayHighLow.todayTempHighTime = this.unixToHuman( timezone, todayDetails.temperatureMaxTime ).onlyTime; this.currentWeather.todayHighLow.todayTempLow = this.fahToCel( todayDetails.temperatureMin ); this.currentWeather.todayHighLow.todayTempLowTime = this.unixToHuman( timezone, todayDetails.temperatureMinTime ).onlyTime; },
getHourlyInfoToday: function() { return this.rawWeatherData.hourly.data; },
getSetHourlyTempInfoToday: function() { var unixTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate; var hourlyData = this.getHourlyInfoToday(); for (var i = 0; i < hourlyData.length; i++) { var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time); var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime; var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate; if (todayMonthDate === hourlyMonthDate) { var hourlyObject = { hour: '', temp: '' }; hourlyObject.hour = hourlyOnlyTime; hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString(); this.tempVar.tempToday.push(hourlyObject); /* Since we are using array.push(), we are just adding elements at the end of the array. Thus, the array is not getting emptied first when a new location is entered. to solve this problem, a method this.makeTempVarTodayEmpty() has been created, and called from this.locationEntered(). */ } } /* To cover the edge case where the local time is between 10 — 12 PM, and therefore there are only two elements in the array this.tempVar.tempToday. We need to add the points for minimum temperature and maximum temperature so that the chart gets generated with atleast four points. */ if (this.tempVar.tempToday.length <= 2) { var minTempObject = { hour: this.currentWeather.todayHighLow.todayTempHighTime, temp: this.currentWeather.todayHighLow.todayTempHigh }; var maxTempObject = { hour: this.currentWeather.todayHighLow.todayTempLowTime, temp: this.currentWeather.todayHighLow.todayTempLow }; /* Typically, lowest temp are at dawn, highest temp is around mid day. Thus we can safely arrange like min, max, temp after 10 PM. */ // array.unshift() adds stuff at the beginning of the array. // the order will be: min, max, 10 PM, 11 PM. this.tempVar.tempToday.unshift(maxTempObject, minTempObject); } },
getSetUVIndex: function() { var uvIndex = this.rawWeatherData.currently.uvIndex; this.highlights.uvIndex = uvIndex; },
getSetVisibility: function() { var visibilityInMiles = this.rawWeatherData.currently.visibility; this.highlights.visibility = this.mileToKilometer(visibilityInMiles); },
getSetWindStatus: function() { var windSpeedInMiles = this.rawWeatherData.currently.windSpeed; this.highlights.windStatus.windSpeed = this.mileToKilometer( windSpeedInMiles ); var absoluteWindDir = this.rawWeatherData.currently.windBearing; this.highlights.windStatus.windDirection = absoluteWindDir; this.highlights.windStatus.derivedWindDirection = this.deriveWindDir( absoluteWindDir ); },
วิธีการติดกาวระดับสูง
ด้วยวิธียูทิลิตี้ การได้มา และการประมวลผลให้พ้นทาง ตอนนี้เราเหลืองานในการเตรียมการทั้งหมด เราทำได้โดยการสร้างวิธีการติดกาวระดับสูง ซึ่งเรียกวิธีการที่เขียนไว้ด้านบนเป็นลำดับเฉพาะเป็นหลัก เพื่อให้การดำเนินการทั้งหมดดำเนินไปอย่างราบรื่น
// Top level for info section // Data in this.currentWeather organizeCurrentWeatherInfo: function() { // data in this.currentWeather /* Coordinates and location is covered (get & set) in: — this.getCoordinates() — this.setFormatCoordinates() There are lots of async-await involved there. So it's better to keep them there. */ this.getSetCurrentTime(); this.getSetCurrentTemp(); this.getSetTodayTempHighLowWithTime(); this.getSetSummary(); this.getSetPossibility(); },
// Top level for highlights organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); },
// Top level organization and rendering organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); },
ติด
Vue จัดเตรียม hooks lifecycle ของอินสแตนซ์ — คุณสมบัติที่เป็นเมธอดโดยพื้นฐาน และจะถูกทริกเกอร์เมื่อวงจรชีวิตของอินสแตนซ์ไปถึงระยะนั้น ตัวอย่างเช่น สร้าง ต่อเชื่อม beforeUpdate ฯลฯ ล้วนแต่เป็น hooks ของวงจรชีวิตที่มีประโยชน์มาก ซึ่งช่วยให้โปรแกรมเมอร์สามารถควบคุมอินสแตนซ์ได้ในระดับที่ละเอียดกว่าที่จะเป็นไปได้
ในโค้ดของคอมโพเนนต์ Vue นั้น lifecycle hooks เหล่านี้ถูกใช้งานเหมือนกับที่คุณทำกับ prop
อื่นๆ ตัวอย่างเช่น:
<template> </template> <script> // import statements export default { data() { return { // data objects here } }, methods: { // methods here }, mounted: function(){ // function body here }, } </script> <style> </style>
ด้วยความเข้าใจใหม่นี้ โปรดดูโค้ดด้านล่างสำหรับอุปกรณ์ mounted
ฉากของ App.vue
:
mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); }
กรอกรหัสสำหรับ App.vue
เราได้ครอบคลุมเนื้อหามากมายในส่วนนี้ และส่วนสุดท้ายได้ให้สิ่งต่างๆ แก่คุณเป็นส่วนๆ อย่างไรก็ตาม คุณควรมีโค้ดที่ประกอบและสมบูรณ์สำหรับ App.vue
(อาจมีการแก้ไขเพิ่มเติมในหัวข้อถัดไป) นี่มันไป:
<template> <div> <div class="container-fluid"> <div class="row"> <div class="col-md-3 col-sm-4 col-xs-12 sidebar"> <div> <input type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div> <div> <div class="wrapper-left"> <div> {{ currentWeather.temp }} <span>°C</span> </div> <div>{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div>at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div> <img src="./assets/location.svg" width="10.83" height="15.83" > {{ currentWeather.full_location }} <div class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div> </div> <dashboard-content class="col-md-9 col-sm-8 col-xs-12 content" :highlights="highlights" :tempVar="tempVar" ></dashboard-content> </div> </div> </div> </template> <script> import Content from './components/Content.vue'; export default { name: 'app', props: [], components: { 'dashboard-content': Content }, data() { return { weatherDetails: false, location: '', // raw location from input lat: '', // raw latitude from google maps api response long: '', // raw longitude from google maps api response completeWeatherApi: '', // weather api string with lat and long rawWeatherData: '', // raw response from weather api currentWeather: { full_location: '', // for full address formatted_lat: '', // for N/S formatted_long: '', // for E/W time: '', temp: '', todayHighLow: { todayTempHigh: '', todayTempHighTime: '', todayTempLow: '', todayTempLowTime: '' }, summary: '', possibility: '' }, tempVar: { tempToday: [ // gets added dynamically by this.getSetHourlyTempInfoToday() ], }, highlights: { uvIndex: '', visibility: '', windStatus: { windSpeed: '', windDirection: '', derivedWindDirection: '' }, } }; }, methods: { // Some utility functions convertToTitleCase: function(str) { str = str.toLowerCase().split(' '); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); }, formatPossibility: function(str) { str = str.toLowerCase().split('-'); for (var i = 0; i < str.length; i++) { str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); } return str.join(' '); }, unixToHuman: function(timezone, timestamp) { /* READ THIS BEFORE JUDGING & DEBUGGING For any location beyond the arctic circle and the antarctic circle, the goddamn weather api does not return certain keys/values in each of this.rawWeatherData.daily.data[some_array_index]. Due to this, console throws up an error. The code is correct, the problem is with the API. May be later on I will add some padding to tackle missing values. */ var moment = require('moment-timezone'); // for handling date & time var decipher = new Date(timestamp * 1000); var human = moment(decipher) .tz(timezone) .format('llll'); var timeArray = human.split(' '); var timeNumeral = timeArray[4]; var timeSuffix = timeArray[5]; var justTime = timeNumeral + ' ' + timeSuffix; var monthDateArray = human.split(','); var monthDate = monthDateArray[1].trim(); return { fullTime: human, onlyTime: justTime, onlyMonthDate: monthDate }; }, fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; }, milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); }, mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); }, deriveWindDir: function(windDir) { var wind_directions_array = [ { minVal: 0, maxVal: 30, direction: 'N' }, { minVal: 31, maxVal: 45, direction: 'NNE' }, { minVal: 46, maxVal: 75, direction: 'NE' }, { minVal: 76, maxVal: 90, direction: 'ENE' }, { minVal: 91, maxVal: 120, direction: 'E' }, { minVal: 121, maxVal: 135, direction: 'ESE' }, { minVal: 136, maxVal: 165, direction: 'SE' }, { minVal: 166, maxVal: 180, direction: 'SSE' }, { minVal: 181, maxVal: 210, direction: 'S' }, { minVal: 211, maxVal: 225, direction: 'SSW' }, { minVal: 226, maxVal: 255, direction: 'SW' }, { minVal: 256, maxVal: 270, direction: 'WSW' }, { minVal: 271, maxVal: 300, direction: 'W' }, { minVal: 301, maxVal: 315, direction: 'WNW' }, { minVal: 316, maxVal: 345, direction: 'NW' }, { minVal: 346, maxVal: 360, direction: 'NNW' } ]; var wind_direction = ''; for (var i = 0; i < wind_directions_array.length; i++) { if ( windDir >= wind_directions_array[i].minVal && windDir <= wind_directions_array[i].maxVal ) { wind_direction = wind_directions_array[i].direction; } } return wind_direction; }, // Some basic action oriented functions makeInputEmpty: function() { this.$refs.input.value = ''; }, makeTempVarTodayEmpty: function() { this.tempVar.tempToday = []; }, detectEnterKeyPress: function() { var input = this.$refs.input; input.addEventListener('keyup', function(event) { event.preventDefault(); var enterKeyCode = 13; if (event.keyCode === enterKeyCode) { this.setHitEnterKeyTrue(); } }); }, locationEntered: function() { var input = this.$refs.input; if (input.value === '') { this.location = "New York"; } else { this.location = this.convertToTitleCase(input.value); } this.makeInputEmpty(); this.makeTempVarTodayEmpty(); }, getCoordinates: function() { this.locationEntered(); var loc = this.location; var coords; var geocoder = new google.maps.Geocoder(); return new Promise(function(resolve, reject) { geocoder.geocode({ address: loc }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { this.lat = results[0].geometry.location.lat(); this.long = results[0].geometry.location.lng(); this.full_location = results[0].formatted_address; coords = { lat: this.lat, long: this.long, full_location: this.full_location }; resolve(coords); } else { alert("Oops! Couldn't get data for the location"); } }); }); }, // Some basic asynchronous functions setFormatCoordinates: async function() { var coordinates = await this.getCoordinates(); this.lat = coordinates.lat; this.long = coordinates.long; this.currentWeather.full_location = coordinates.full_location; // Remember to beautify lat for N/S if (coordinates.lat > 0) { this.currentWeather.formatted_lat = (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N'; } else if (coordinates.lat < 0) { this.currentWeather.formatted_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 0) { this.currentWeather.formatted_long = (Math.round(coordinates.long * 10000) / 10000).toString() + '°E'; } else if (coordinates.long < 0) { this.currentWeather.formatted_long = (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() + '°W'; } else { this.currentWeather.formatted_long = ( Math.round(coordinates.long * 10000) / 10000 ).toString(); } }, fixWeatherApi: async function() { await this.setFormatCoordinates(); var weatherApi = 'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' + this.lat + '&long=' + this.long; this.completeWeatherApi = weatherApi; }, fetchWeatherData: async function() { await this.fixWeatherApi(); var axios = require('axios'); // for handling weather api promise var weatherApiResponse = await axios.get(this.completeWeatherApi); if (weatherApiResponse.status === 200) { this.rawWeatherData = weatherApiResponse.data; } else { alert('Hmm... Seems like our weather experts are busy!'); } }, // Get and set functions; often combined, because they are short // For basic info — left panel/sidebar getTimezone: function() { return this.rawWeatherData.timezone; }, getSetCurrentTime: function() { var currentTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); this.currentWeather.time = this.unixToHuman( timezone, currentTime ).fullTime; }, getSetSummary: function() { var currentSummary = this.convertToTitleCase( this.rawWeatherData.currently.summary ); if (currentSummary.includes(' And')) { currentSummary = currentSummary.replace(' And', ','); } this.currentWeather.summary = currentSummary; }, getSetPossibility: function() { var possible = this.formatPossibility(this.rawWeatherData.daily.icon); if (possible.includes(' And')) { possible = possible.replace(' And', ','); } this.currentWeather.possibility = possible; }, getSetCurrentTemp: function() { var currentTemp = this.rawWeatherData.currently.temperature; this.currentWeather.temp = this.fahToCel(currentTemp); }, getTodayDetails: function() { return this.rawWeatherData.daily.data[0]; }, getSetTodayTempHighLowWithTime: function() { var timezone = this.getTimezone(); var todayDetails = this.getTodayDetails(); this.currentWeather.todayHighLow.todayTempHigh = this.fahToCel( todayDetails.temperatureMax ); this.currentWeather.todayHighLow.todayTempHighTime = this.unixToHuman( timezone, todayDetails.temperatureMaxTime ).onlyTime; this.currentWeather.todayHighLow.todayTempLow = this.fahToCel( todayDetails.temperatureMin ); this.currentWeather.todayHighLow.todayTempLowTime = this.unixToHuman( timezone, todayDetails.temperatureMinTime ).onlyTime; }, getHourlyInfoToday: function() { return this.rawWeatherData.hourly.data; }, getSetHourlyTempInfoToday: function() { var unixTime = this.rawWeatherData.currently.time; var timezone = this.getTimezone(); var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate; var hourlyData = this.getHourlyInfoToday(); for (var i = 0; i < hourlyData.length; i++) { var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time); var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime; var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate; if (todayMonthDate === hourlyMonthDate) { var hourlyObject = { hour: '', temp: '' }; hourlyObject.hour = hourlyOnlyTime; hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString(); this.tempVar.tempToday.push(hourlyObject); /* Since we are using array.push(), we are just adding elements at the end of the array. Thus, the array is not getting emptied first when a new location is entered. to solve this problem, a method this.makeTempVarTodayEmpty() has been created, and called from this.locationEntered(). */ } } /* To cover the edge case where the local time is between 10 — 12 PM, and therefore there are only two elements in the array this.tempVar.tempToday. We need to add the points for minimum temperature and maximum temperature so that the chart gets generated with atleast four points. */ if (this.tempVar.tempToday.length <= 2) { var minTempObject = { hour: this.currentWeather.todayHighLow.todayTempHighTime, temp: this.currentWeather.todayHighLow.todayTempHigh }; var maxTempObject = { hour: this.currentWeather.todayHighLow.todayTempLowTime, temp: this.currentWeather.todayHighLow.todayTempLow }; /* Typically, lowest temp are at dawn, highest temp is around mid day. Thus we can safely arrange like min, max, temp after 10 PM. */ // array.unshift() adds stuff at the beginning of the array. // the order will be: min, max, 10 PM, 11 PM. this.tempVar.tempToday.unshift(maxTempObject, minTempObject); } }, // For Today Highlights getSetUVIndex: function() { var uvIndex = this.rawWeatherData.currently.uvIndex; this.highlights.uvIndex = uvIndex; }, getSetVisibility: function() { var visibilityInMiles = this.rawWeatherData.currently.visibility; this.highlights.visibility = this.mileToKilometer(visibilityInMiles); }, getSetWindStatus: function() { var windSpeedInMiles = this.rawWeatherData.currently.windSpeed; this.highlights.windStatus.windSpeed = this.mileToKilometer( windSpeedInMiles ); var absoluteWindDir = this.rawWeatherData.currently.windBearing; this.highlights.windStatus.windDirection = absoluteWindDir; this.highlights.windStatus.derivedWindDirection = this.deriveWindDir( absoluteWindDir ); }, // top level for info section organizeCurrentWeatherInfo: function() { // data in this.currentWeather /* Coordinates and location is covered (get & set) in: — this.getCoordinates() — this.setFormatCoordinates() There are lots of async-await involved there. So it's better to keep them there. */ this.getSetCurrentTime(); this.getSetCurrentTemp(); this.getSetTodayTempHighLowWithTime(); this.getSetSummary(); this.getSetPossibility(); }, organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); }, // topmost level orchestration organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); }, }, mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); } }; </script>
และสุดท้าย หลังจากอดทนและทำงานหนัก คุณจะเห็นการไหลของข้อมูลด้วยพลังดิบของมัน! เยี่ยมชมแอปพลิเคชันบนเบราว์เซอร์ รีเฟรชหน้า ค้นหาตำแหน่งในช่องค้นหาของแอปพลิเคชัน แล้วกด Enter !
เมื่อเราเสร็จสิ้นภาระหนักทั้งหมดแล้ว หยุดพักบ้าง ส่วนต่อๆ มาจะเน้นที่การใช้ข้อมูลเพื่อสร้างแผนภูมิที่สวยงามและให้ข้อมูล ตามด้วยการทำให้แอปพลิเคชันที่ดูน่าเกลียดของเรามีเซสชั่นการดูแลที่เหมาะสมอย่างยิ่งโดยใช้ CSS
5. การสร้างภาพข้อมูลด้วย FusionCharts
ข้อควรพิจารณาพื้นฐานสำหรับแผนภูมิ
สำหรับผู้ใช้ปลายทาง สาระสำคัญของแดชบอร์ดคือโดยพื้นฐานแล้ว: คอลเลกชันของข้อมูลที่กรองและดูแลจัดการอย่างระมัดระวังในหัวข้อเฉพาะ ถ่ายทอดผ่านเครื่องมือภาพ/กราฟิกเพื่อการนำเข้าอย่างรวดเร็ว พวกเขาไม่สนใจความละเอียดอ่อนของวิศวกรรมไปป์ไลน์ข้อมูลของคุณ หรือความสวยงามของโค้ดของคุณ — สิ่งที่พวกเขาต้องการคือมุมมองระดับสูงใน 3 วินาที ดังนั้น แอปพลิเคชันคร่าวๆ ของเราที่แสดงข้อมูลข้อความไม่มีความหมายสำหรับพวกเขา และถึงเวลาแล้วที่เราใช้กลไกเพื่อรวมข้อมูลด้วยแผนภูมิ
อย่างไรก็ตาม ก่อนที่เราจะลงลึกในการดำเนินการของแผนภูมิ มาพิจารณาคำถามที่เกี่ยวข้องและคำตอบที่เป็นไปได้จากมุมมองของเรา:
- แผนภูมิประเภทใดที่เหมาะกับประเภทของข้อมูลที่เรากำลังจัดการ
คำตอบมีสองด้าน — บริบทและวัตถุประสงค์ ตามบริบท เราหมายถึงประเภทของข้อมูล และโดยรวมแล้วเหมาะสมกับโครงร่างของสิ่งที่ใหญ่กว่า ล้อมรอบด้วยขอบเขตและผู้ชมของโครงการ โดยพื้นฐานแล้วเราหมายถึง "สิ่งที่เราต้องการเน้นย้ำ" โดยพื้นฐานแล้ว ตัวอย่างเช่น เราสามารถแสดงอุณหภูมิของวันนี้ในช่วงเวลาต่างๆ ของวันโดยใช้แผนภูมิคอลัมน์ (คอลัมน์แนวตั้งที่มีความกว้างเท่ากัน โดยมีความสูงเป็นสัดส่วนกับค่าที่คอลัมน์แสดง) อย่างไรก็ตาม เราไม่ค่อยสนใจค่าแต่ละค่า แต่เป็นความผันแปรและแนวโน้มโดยรวมในข้อมูล เพื่อให้เหมาะกับวัตถุประสงค์ เราควรใช้แผนภูมิเส้นให้เกิดประโยชน์สูงสุด และเราจะดำเนินการในไม่ช้านี้ - สิ่งที่ควรคำนึงถึงก่อนเลือกไลบรารีสร้างแผนภูมิ
เนื่องจากเรากำลังทำโปรเจ็กต์ที่ใช้เทคโนโลยี JavaScript เป็นหลัก จึงไม่ต้องคิดง่ายๆ ว่าไลบรารีการสร้างแผนภูมิใดๆ ที่เราเลือกสำหรับโปรเจ็กต์ของเราควรจะมาจากโลกของ JavaScript ด้วยหลักฐานพื้นฐานนั้น เราควรพิจารณาสิ่งต่อไปนี้ก่อนที่จะเน้นไปที่ห้องสมุดใด ๆ โดยเฉพาะ:- รองรับเฟรมเวิร์กที่เราเลือก ซึ่งในกรณีนี้คือ Vue.js โปรเจ็กต์สามารถพัฒนาได้ในเฟรมเวิร์ก JavaScript ยอดนิยมอื่นๆ เช่น React หรือ Angular ตรวจสอบการรองรับของไลบรารีการสร้างแผนภูมิสำหรับเฟรมเวิร์กโปรดของคุณ นอกจากนี้ การรองรับภาษาโปรแกรมยอดนิยมอื่นๆ เช่น Python, Java, C++, .Net (AS และ VB) โดยเฉพาะอย่างยิ่งเมื่อโปรเจ็กต์เกี่ยวข้องกับแบ็กเอนด์ที่จริงจัง จะต้องได้รับการพิจารณา
- ความพร้อมใช้งานของประเภทแผนภูมิและคุณลักษณะ เนื่องจากแทบเป็นไปไม่ได้เลยที่จะทราบล่วงหน้าว่ารูปแบบและจุดประสงค์สุดท้ายของข้อมูลในโปรเจ็กต์จะเป็นอย่างไร (โดยเฉพาะอย่างยิ่งหากลูกค้าของคุณกำหนดข้อกำหนดในการตั้งค่าแบบมืออาชีพ) ในกรณีนี้ คุณควรโยนเน็ตไวด์ของคุณ และเลือกไลบรารีการสร้างแผนภูมิที่มีคอลเลกชันแผนภูมิที่กว้างที่สุด ที่สำคัญกว่านั้น ในการทำให้โปรเจ็กต์ของคุณแตกต่างจากคนอื่นๆ ไลบรารีควรมีฟีเจอร์ที่เพียงพอในรูปแบบของแอตทริบิวต์แผนภูมิที่กำหนดค่าได้ เพื่อให้คุณสามารถปรับแต่งและปรับแต่งส่วนต่างๆ ของแผนภูมิและระดับความละเอียดที่เหมาะสมได้ นอกจากนี้ การกำหนดค่าเริ่มต้นของแผนภูมิควรมีความสมเหตุสมผล และเอกสารประกอบของไลบรารีจะต้องอยู่ในระดับสูงสุด ด้วยเหตุผลที่ชัดเจนสำหรับนักพัฒนามืออาชีพ
- เส้นโค้งการเรียนรู้ ชุมชนที่สนับสนุน และความสมดุล ต้องนำมาพิจารณาด้วย โดยเฉพาะอย่างยิ่งเมื่อคุณยังใหม่ต่อการสร้างภาพข้อมูล ที่ปลายด้านหนึ่งของสเปกตรัม คุณมีเครื่องมือที่เป็นเอกสิทธิ์เฉพาะ เช่น Tableau และ Qlickview ที่มีค่าใช้จ่ายสูง มีช่วงการเรียนรู้ที่ราบรื่น แต่ยังมาพร้อมกับข้อจำกัดมากมายในแง่ของความสามารถในการปรับแต่ง บูรณาการ และการปรับใช้ อีกด้านหนึ่ง มี d3.js — กว้างใหญ่ ฟรี (โอเพ่นซอร์ส) และปรับแต่งได้เป็นแกนหลัก แต่คุณต้องจ่ายราคาสำหรับช่วงการเรียนรู้ที่สูงชันมากเพื่อให้สามารถทำอะไรกับห้องสมุดได้อย่างมีประสิทธิผล
สิ่งที่คุณต้องการคือจุดที่เหมาะสม — ความสมดุลที่เหมาะสมระหว่างประสิทธิภาพการทำงาน ความครอบคลุม ความสามารถในการปรับแต่ง เส้นโค้งการเรียนรู้ และต้นทุนนอกหลักสูตร เราอยากให้คุณลองดูที่ FusionCharts ซึ่งเป็นไลบรารีสร้างแผนภูมิ JavaScript ที่ครอบคลุมและพร้อมสำหรับองค์กรมากที่สุดในโลกสำหรับเว็บและอุปกรณ์เคลื่อนที่ที่เราจะใช้ในโปรเจ็กต์นี้เพื่อสร้างแผนภูมิ
บทนำสู่ FusionCharts
FusionCharts ถูกใช้ทั่วโลกในฐานะไลบรารีการสร้างแผนภูมิ JavaScript แบบ go-to โดยนักพัฒนาหลายล้านคนที่กระจายอยู่ในหลายร้อยประเทศทั่วโลก ในทางเทคนิค มันโหลดและกำหนดค่าได้มากเท่าที่จะทำได้ โดยรองรับการผสานรวมกับสแต็กเทคโนโลยียอดนิยมเกือบทั้งหมดที่ใช้สำหรับโปรเจ็กต์บนเว็บ การใช้ FusionCharts ในเชิงพาณิชย์จำเป็นต้องมีใบอนุญาต และคุณต้องชำระค่าใบอนุญาตโดยขึ้นอยู่กับกรณีการใช้งานของคุณ (โปรดติดต่อฝ่ายขายหากคุณสงสัย) อย่างไรก็ตาม เราใช้ FusionCharts ในโปรเจ็กต์นี้เพียงเพื่อทดลองใช้บางสิ่ง ดังนั้นเวอร์ชันที่ไม่มีลิขสิทธิ์ (มาพร้อมกับลายน้ำเล็กๆ ในแผนภูมิของคุณ และข้อจำกัดอื่นๆ อีกสองสามข้อ) การใช้เวอร์ชันที่ไม่มีลิขสิทธิ์นั้นใช้ได้อย่างสมบูรณ์เมื่อคุณลองใช้แผนภูมิและใช้งานในโครงการที่ไม่ใช่เชิงพาณิชย์หรือส่วนตัวของคุณ หากคุณมีแผนที่จะปรับใช้แอปพลิเคชันในเชิงพาณิชย์ โปรดตรวจสอบให้แน่ใจว่าคุณมีใบอนุญาตจาก FusionCharts
เนื่องจากเป็นโครงการที่เกี่ยวข้องกับ Vue.js เราจึงจำเป็นต้องติดตั้งสองโมดูล หากไม่ได้ทำก่อนหน้านี้:
- โมดูล
fusioncharts
เนื่องจากมีทุกสิ่งที่คุณต้องการสำหรับการสร้างแผนภูมิ - โมดูล
vue-fusioncharts
ซึ่งโดยพื้นฐานแล้วเป็น wrapper สำหรับ fusioncharts เพื่อให้สามารถใช้ในโครงการ Vue.js
หากคุณยังไม่ได้ติดตั้งก่อนหน้านี้ (ตามคำแนะนำในส่วนที่สาม) ให้ติดตั้งโดยดำเนินการคำสั่งต่อไปนี้จากไดเรกทอรีรากของโครงการ:
npm install fusioncharts vue-fusioncharts --save
ถัดไป ตรวจสอบให้แน่ใจว่าไฟล์ src/main.js
ของโปรเจ็กต์มีรหัสต่อไปนี้ (ยังกล่าวถึงในส่วนที่ 3)
import Vue from 'vue'; import App from './App.vue'; import FusionCharts from 'fusioncharts'; import Charts from 'fusioncharts/fusioncharts.charts'; import Widgets from 'fusioncharts/fusioncharts.widgets'; import PowerCharts from 'fusioncharts/fusioncharts.powercharts'; import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion'; import VueFusionCharts from 'vue-fusioncharts'; Charts(FusionCharts); PowerCharts(FusionCharts); Widgets(FusionCharts); FusionTheme(FusionCharts); Vue.use(VueFusionCharts, FusionCharts); new Vue({ el: '#app', render: h => h(App) })
บางทีบรรทัดที่สำคัญที่สุดในตัวอย่างข้างต้นอาจมีดังต่อไปนี้:
Vue.use(VueFusionCharts, FusionCharts)
มันสั่งให้ Vue ใช้โมดูล vue-fusioncharts เพื่อให้เข้าใจหลายสิ่งในโปรเจ็กต์ที่เห็นได้ชัดว่าเราไม่ได้กำหนดไว้อย่างชัดเจน แต่มีการกำหนดไว้ในโมดูลเอง นอกจากนี้ คำสั่งประเภทนี้ยังบ่งบอกถึงการประกาศ ทั่วโลก โดยที่เราหมายความว่าทุกที่ที่ Vue พบสิ่งแปลกปลอมในโค้ดของโครงการของเรา (สิ่งที่เราไม่ได้กำหนดไว้อย่างชัดเจนเกี่ยวกับการใช้ FusionCharts) อย่างน้อยก็จะปรากฏเพียงครั้งเดียวใน vue-fusioncharts และโมดูลโหนด fusioncharts สำหรับคำจำกัดความก่อนที่จะแสดงข้อผิดพลาด หากเราจะใช้ FusionCharts ในส่วนที่แยกจากกันของโปรเจ็กต์ของเรา (ไม่ได้ใช้มันในไฟล์ส่วนประกอบเกือบทั้งหมด) บางทีการประกาศในเครื่องอาจสมเหตุสมผลกว่า
เท่านี้คุณก็พร้อมใช้ FusionCharts ในโปรเจ็กต์แล้ว เราจะใช้แผนภูมิที่หลากหลาย ซึ่งตัวเลือกจะขึ้นอยู่กับข้อมูลสภาพอากาศที่เราต้องการที่จะแสดงเป็นภาพ นอกจากนี้ เราจะได้เห็นการทำงานร่วมกันของการเชื่อมโยงข้อมูล ส่วนประกอบที่กำหนดเอง และผู้ดูในการดำเนินการ
แบบแผนทั่วไปสำหรับการใช้ .vue
ใน .vue Files
ในส่วนนี้ เราจะอธิบายแนวคิดทั่วไปของการใช้ FusionCharts เพื่อสร้างแผนภูมิต่างๆ ในไฟล์ . .vue
แต่ก่อนอื่น มาดู pseudocode ที่แสดงแนวคิดหลักเป็นแผนผัง
<template> <div> <fusioncharts :attribute_1="data_object_1" :attribute_2="data_object_2" … … ... > </fusioncharts> </div> </template> <script> export default { props: ["data_prop_received_by_the_component"], components: {}, data() { return { data_object_1: "value_1", data_object_2: "value_2", … … }; }, methods: {}, computed: {}, watch: { data_prop_received_by_the_component: { handler: function() { // some code/logic, mainly data manipulation based }, deep: true } } }; </script> <style> // component specific special CSS code here </style>
มาทำความเข้าใจส่วนต่าง ๆ ของ pseudocode ด้านบนกัน:
- ใน
<template>
ภายในระดับบนสุด<div>
(ซึ่งค่อนข้างจำเป็นสำหรับโค้ด HTML ของเทมเพลตของทุกองค์ประกอบ) เรามีส่วนประกอบที่กำหนดเอง<fusioncharts>
เรามีคำจำกัดความของส่วนประกอบที่อยู่ในโมดูลโหนดvue-fusioncharts
ที่เราได้ติดตั้งไว้สำหรับโครงการนี้ ภายในvue-fusioncharts
อาศัยโมดูลfusioncharts
ซึ่งได้รับการติดตั้งด้วย เรานำเข้าโมดูลที่จำเป็นและแก้ไขการขึ้นต่อกัน สั่งให้ Vue ใช้ wrapper ทั่วโลก (ตลอดทั้งโครงการ) ในไฟล์src/main.js
ดังนั้นจึงไม่มีคำจำกัดความสำหรับองค์ประกอบ<fusioncharts>
แบบกำหนดเองที่เราใช้ ที่นี่. นอกจากนี้ องค์ประกอบที่กำหนดเองยังมีแอตทริบิวต์ที่กำหนดเอง และแอตทริบิวต์ที่กำหนดเองแต่ละรายการเชื่อมโยงกับออบเจ็กต์ข้อมูล (และในทางกลับกัน ค่าของแอตทริบิวต์) โดยคำสั่งv-bind
ซึ่งการจดชวเลขคือเครื่องหมายโคลอน (:
) เราจะเรียนรู้เกี่ยวกับแอตทริบิวต์และวัตถุข้อมูลที่เกี่ยวข้องในรายละเอียดมากขึ้น เมื่อเราพูดถึงแผนภูมิเฉพาะบางรายการที่ใช้ในโครงการนี้ - ใน
<script>
ขั้นแรก คุณต้องประกาศอุปกรณ์ประกอบฉากที่คอมโพเนนต์ควรจะได้รับ จากนั้นดำเนินการกำหนดออบเจ็กต์ข้อมูลที่ถูกผูกไว้กับแอตทริบิวต์ของ<fusioncharts>
ค่าที่กำหนดให้กับออบเจ็กต์ข้อมูลคือค่าที่แอตทริบิวต์ของ<fusioncharts>
ดึงเข้ามา และแผนภูมิจะถูกสร้างขึ้นบนพื้นฐานของค่าที่ดึงเข้ามา นอกเหนือจากนี้ ส่วนที่น่าสนใจที่สุดของโค้ดคืออ็อบเจกต์watch { }
นี่เป็นอ็อบเจ็กต์ที่พิเศษมากในโครงร่างของสิ่งต่าง ๆ ของ Vue — โดยพื้นฐานแล้วจะสั่งให้ Vue คอยดูการเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับข้อมูลบางอย่าง จากนั้นจึงดำเนินการตามวิธีการกำหนดฟังก์ชันhandler
สำหรับข้อมูลนั้น ตัวอย่างเช่น เราต้องการให้ Vue คอยเฝ้าดูอุปกรณ์ที่ได้รับdata_prop_received_by_the_component
prop
pseudocode พprop
อพกลายเป็นกุญแจสำคัญในอ็อบเจกต์watch { }
และค่าของคีย์ก็เป็นอีกอ็อบเจกต์ — วิธีจัดการที่อธิบายสิ่งที่ต้องทำทุกครั้งที่พprop
อพเปลี่ยนแปลง ด้วยกลไกที่สง่างามเพื่อจัดการกับการเปลี่ยนแปลง แอปยังคงรักษาปฏิกิริยา ความdeep: true
หมายถึงแฟล็กบูลีนที่คุณสามารถเชื่อมโยงกับผู้ดูได้ ดังนั้นวัตถุที่กำลังดูจะถูกดูค่อนข้างลึก กล่าวคือ แม้แต่การเปลี่ยนแปลงที่เกิดขึ้นในระดับที่ซ้อนกันของวัตถุก็ถูกติดตาม
( สำหรับข้อมูลเพิ่มเติมเกี่ยวกับนักดู โปรดอ่านเอกสารอย่างเป็นทางการ )
ตอนนี้ คุณมีความเข้าใจเกี่ยวกับโครงร่างทั่วไปของสิ่งต่างๆ แล้ว มาเจาะลึกการใช้งานแผนภูมิในไฟล์คอมโพเนนต์ . .vue
กัน โค้ดจะอธิบายได้ชัดเจนในตัวเอง และคุณควรพยายามทำความเข้าใจว่าข้อมูลเฉพาะนั้นเหมาะสมกับรูปแบบทั่วไปของสิ่งต่างๆ ที่อธิบายไว้ข้างต้นอย่างไร
การนำแผนภูมิไปใช้ใน .vue Files
แม้ว่าการใช้งานที่เฉพาะเจาะจงจะแตกต่างกันไปในแต่ละแผนภูมิ คำอธิบายต่อไปนี้ใช้ได้กับแผนภูมิทั้งหมด:
-
<template>
ตามที่ได้อธิบายไว้ก่อนหน้านี้ องค์ประกอบที่กำหนดเอง<fusioncharts>
มีแอตทริบิวต์หลายอย่าง โดยแต่ละรายการจะผูกกับวัตถุข้อมูลที่เกี่ยวข้องซึ่งกำหนดไว้ในฟังก์ชันdata()
โดยใช้คำสั่งv-bind
: ชื่อแอตทริบิวต์ค่อนข้างอธิบายตนเองได้สำหรับความหมาย และการค้นหาออบเจ็กต์ข้อมูลที่เกี่ยวข้องก็ไม่สำคัญเช่นกัน -
<script>
ในฟังก์ชันdata()
ออบเจ็กต์ข้อมูลและค่าของออบเจ็กต์เป็นสิ่งที่ทำให้แผนภูมิทำงานได้ เนื่องจากการผูกที่ทำโดยคำสั่งv-bind
(:
) ที่ใช้กับแอตทริบิวต์ของ<fusioncharts>
ก่อนที่เราจะเจาะลึกลงไปในแต่ละอ็อบเจ็กต์ข้อมูล เราควรกล่าวถึงลักษณะทั่วไปบางประการ:- ออบเจ็กต์ข้อมูลที่มีค่าเป็น
0
หรือ1
มีลักษณะเป็นบูลีน โดยที่0
แสดงถึงบางสิ่งที่ไม่พร้อมใช้งาน/ปิด และ1
แสดงถึงสถานะพร้อมใช้งาน/เปิดอยู่ อย่างไรก็ตาม โปรดระมัดระวังว่าวัตถุข้อมูลที่ไม่ใช่บูลีนสามารถมีค่าเป็น0
หรือ1
ได้ นอกเหนือจากค่าที่เป็นไปได้อื่นๆ ซึ่งขึ้นอยู่กับบริบท ตัวอย่างเช่นcontainerbackgroundopacity
มีค่าเริ่มต้นเป็น0
คือบูลีน ในขณะที่lowerLimit
โดยมีค่าเริ่มต้นเป็น0
หมายความว่าหมายเลขศูนย์คือค่าตามตัวอักษร - ออบเจ็กต์ข้อมูลบางอย่างเกี่ยวข้องกับคุณสมบัติของ CSS เช่น ระยะขอบ ช่องว่างภายใน ขนาดฟอนต์ ฯลฯ — ค่านี้มีหน่วยโดยนัยเป็น “px” หรือพิกเซล ในทำนองเดียวกัน ออบเจ็กต์ข้อมูลอื่นๆ สามารถมีหน่วยโดยนัยที่เกี่ยวข้องกับค่าของพวกมันได้ สำหรับข้อมูลโดยละเอียด โปรดดูที่หน้าแอตทริบิวต์แผนภูมิของ FusionCharts Dev Center
- ออบเจ็กต์ข้อมูลที่มีค่าเป็น
- ในฟังก์ชัน
data()
บางทีวัตถุที่น่าสนใจและไม่ชัดเจนที่สุดคือ dataSource ออบเจ็กต์นี้มีออบเจ็กต์หลักสามรายการซ้อนอยู่ภายใน:- แผนภูมิ : ออบเจ็กต์นี้สรุปแอตทริบิวต์แผนภูมิจำนวนมากที่เกี่ยวข้องกับการกำหนดค่าและรูปลักษณ์ของแผนภูมิ เกือบจะเป็นโครงสร้างบังคับที่คุณจะพบในแผนภูมิทั้งหมดที่คุณจะสร้างสำหรับโครงการนี้
- ช่วงสี : วัตถุนี้ค่อนข้างเฉพาะเจาะจงสำหรับแผนภูมิที่กำลังพิจารณา และส่วนใหญ่มีอยู่ในแผนภูมิที่เกี่ยวข้องกับสี/เฉดสีหลายสีเพื่อแบ่งเขตย่อยต่างๆ ของมาตราส่วนที่ใช้ในแผนภูมิ
- ค่า: อีกครั้ง วัตถุนี้มีอยู่ในแผนภูมิที่มีค่าเฉพาะที่ต้องเน้นในช่วงของมาตราส่วน
- วัตถุ
watch { }
อาจเป็นสิ่งที่สำคัญที่สุดที่ทำให้แผนภูมินี้ และแผนภูมิอื่นๆ ที่ใช้ในโครงการนี้มีชีวิตชีวาขึ้น ปฏิกิริยาของแผนภูมิ กล่าวคือ แผนภูมิอัปเดตตัวเองตามค่าใหม่ที่เกิดจากการค้นหาของผู้ใช้ใหม่จะถูกควบคุมโดยผู้ดูที่กำหนดไว้ในวัตถุนี้ ตัวอย่างเช่น เราได้กำหนดผู้เฝ้าสังเกตสำหรับhighlights
ของพร็อพที่ได้รับจากส่วนประกอบ จากนั้นจึงกำหนดฟังก์ชันตัวจัดการเพื่อสั่ง Vue เกี่ยวกับการดำเนินการที่จำเป็นที่ควรทำ เมื่อมีการเปลี่ยนแปลงใดๆ เกี่ยวกับออบเจกต์ที่รับชมในโปรเจ็กต์ทั้งหมด ซึ่งหมายความว่าเมื่อใดก็ตามที่App.vue
ให้ค่าใหม่สำหรับออบเจ็กต์ใดๆ ภายในhighlights
ข้อมูลจะไหลลงมาที่ส่วนประกอบนี้ และค่าใหม่จะได้รับการอัปเดตในออบเจ็กต์ข้อมูลของส่วนประกอบนี้ แผนภูมิที่ถูกผูกไว้กับค่าต่างๆ ยังได้รับการอัปเดตตามผลของกลไกนี้
คำอธิบายข้างต้นค่อนข้างกว้างเพื่อช่วยให้เราพัฒนาความเข้าใจโดยสัญชาตญาณในภาพรวม เมื่อคุณเข้าใจแนวคิดโดยสัญชาตญาณแล้ว คุณสามารถดูเอกสารประกอบของ Vue.js และ FusionCharts ได้ทุกเมื่อเมื่อมีบางสิ่งที่ไม่ชัดเจนสำหรับคุณจากโค้ด เราฝากแบบฝึกหัดไว้ให้คุณ และจากส่วนย่อยถัดไปเป็นต้นไป เราจะไม่อธิบายสิ่งที่เรากล่าวถึงในส่วนย่อยนี้
src/components/TempVarChart.vue
<template> <div class="custom-card header-card card"> <div class="card-body pt-0"> <fusioncharts type="spline" width="100%" height="100%" dataformat="json" dataEmptyMessage="i-https://i.postimg.cc/R0QCk9vV/Rolling-0-9s-99px.gif" dataEmptyMessageImageScale=39 :datasource="tempChartData" > </fusioncharts> </div> </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { tempChartData: { chart: { caption: "Hourly Temperature", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", baseFont: "Roboto", chartTopMargin: "30", showHoverEffect: "1", theme: "fusion", showaxislines: "1", numberSuffix: "°C", anchorBgColor: "#6297d9", paletteColors: "#6297d9", drawCrossLine: "1", plotToolText: "$label<br><hr><b>$dataValue</b>", showAxisLines: "0", showYAxisValues: "0", anchorRadius: "4", divLineAlpha: "0", labelFontSize: "13", labelAlpha: "65", labelFontBold: "0", rotateLabels: "1", slantLabels: "1", canvasPadding: "20" }, data: [], }, }; }, methods: { setChartData: function() { var data = []; for (var i = 0; i < this.tempVar.tempToday.length; i++) { var dataObject = { label: this.tempVar.tempToday[i].hour, value: this.tempVar.tempToday[i].temp }; data.push(dataObject); } this.tempChartData.data = data; }, }, mounted: function() { this.setChartData(); }, watch: { tempVar: { handler: function() { this.setChartData(); }, deep: true }, }, }; </script> <style> </style>
src/components/UVIndex.vue
องค์ประกอบนี้มีแผนภูมิที่มีประโยชน์อย่างยิ่ง — เกจเชิงมุม
รหัสสำหรับส่วนประกอบได้รับด้านล่าง สำหรับข้อมูลโดยละเอียดเกี่ยวกับคุณลักษณะแผนภูมิของ Angular Gauge โปรดดูที่หน้าศูนย์ Dev FusionCharts Dev Center สำหรับ Angular Gauge
<template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" ></fusioncharts> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return { type: "angulargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", datasource: { chart: { caption: "UV Index", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", lowerLimit: "0", upperLimit: "15", lowerLimitDisplay: "1", upperLimitDisplay: "1", showValue: "0", theme: "fusion", baseFont: "Roboto", bgAlpha: "0", canvasbgAlpha: "0", gaugeInnerRadius: "75", gaugeOuterRadius: "110", pivotRadius: "0", pivotFillAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", tickValueDistance: "3", autoAlignTickValues: "1", majorTMAlpha: "20", chartTopMargin: "30", chartBottomMargin: "40" }, colorrange: { color: [ { minvalue: "0", maxvalue: this.highlights.uvIndex.toString(), code: "#7DA9E0" }, { minvalue: this.highlights.uvIndex.toString(), maxvalue: "15", code: "#D8EDFF" } ] }, annotations: { groups: [ { items: [ { id: "val-label", type: "text", text: this.highlights.uvIndex.toString(), fontSize: "20", font: "Source Sans Pro", fontBold: "1", fillcolor: "#212529", x: "$gaugeCenterX", y: "$gaugeCenterY" } ] } ] }, dials: { dial: [ { value: this.highlights.uvIndex.toString(), baseWidth: "0", radius: "0", borderThickness: "0", baseRadius: "0" } ] } } }; }, methods: {}, computed: {}, watch: { highlights: { handler: function() { this.datasource.colorrange.color[0].maxvalue = this.highlights.uvIndex.toString(); this.datasource.colorrange.color[1].minvalue = this.highlights.uvIndex.toString(); this.datasource.annotations.groups[0].items[0].text = this.highlights.uvIndex.toString(); }, deep: true } } }; </script>
src/components/Visibility.vue
ในองค์ประกอบนี้ เราใช้เครื่องวัดเชิงเส้นแนวนอนเพื่อแสดงถึงการมองเห็น ดังที่แสดงในภาพด้านล่าง:
รหัสสำหรับส่วนประกอบได้รับด้านล่าง สำหรับความเข้าใจเชิงลึกเกี่ยวกับแอตทริบิวต์ต่างๆ ของแผนภูมิประเภทนี้ โปรดดูที่หน้า FusionCharts Dev Center สำหรับเครื่องวัดเชิงเส้นแนวนอน
<template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-left border-right border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" > </fusioncharts> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, methods: {}, computed: {}, data() { return { type: "hlineargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", creditLabel: false, datasource: { chart: { caption: "Air Visibility", captionFontBold: "0", captionFontColor: "#000000", baseFont: "Roboto", numberSuffix: " km", lowerLimit: "0", upperLimit: "40", showPointerShadow: "1", animation: "1", transposeAnimation: "1", theme: "fusion", bgAlpha: "0", canvasBgAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", pointerBorderAlpha: "0", chartBottomMargin: "40", captionPadding: "30", chartTopMargin: "30" }, colorRange: { color: [ { minValue: "0", maxValue: "4", label: "Fog", code: "#6297d9" }, { minValue: "4", maxValue: "10", label: "Haze", code: "#7DA9E0" }, { minValue: "10", maxValue: "40", label: "Clear", code: "#D8EDFF" } ] }, pointers: { pointer: [ { value: this.highlights.visibility.toString() } ] } } }; }, watch: { highlights: { handler: function() { this.datasource.pointers.pointer[0].value = this.highlights.visibility.toString(); }, deep: true } } }; </script>
src/components/WindStatus.vue
องค์ประกอบนี้แสดงความเร็วและทิศทางลม (ความเร็วลม หากคุณเข้าใจฟิสิกส์) และเป็นการยากที่จะแสดงเวกเตอร์โดยใช้แผนภูมิ สำหรับกรณีดังกล่าว เราแนะนำให้นำเสนอโดยใช้รูปภาพและค่าข้อความที่สวยงาม เนื่องจากการนำเสนอที่เราคิดไว้นั้นขึ้นอยู่กับ CSS ทั้งหมด เราจะนำไปใช้ในส่วนถัดไปที่เกี่ยวข้องกับ CSS อย่างไรก็ตาม ดูสิ่งที่เราตั้งเป้าที่จะสร้าง:
<template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <div class="card-heading pt-5">Wind Status</div> <div class="row pt-4 mt-4"> <div class="col-sm-6 col-md-6 mt-2 text-center align-middle"> <p class="card-sub-heading mt-3">Wind Direction</p> <p class="mt-4"><img src="../assets/winddirection.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.derivedWindDirection }}</p> </div> <div class="col-sm-6 col-md-6 mt-2"> <p class="card-sub-heading mt-3">Wind Speed</p> <p class="mt-4"><img src="../assets/windspeed.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.windSpeed }} km/h</p> </div> </div> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return {}; }, methods: {}, computed: {} }; </script>
ปิดท้ายด้วย Highlights.vue
จำได้ว่าเราได้ติดตั้งโค้ดด้วย CSS สำหรับส่วนประกอบทั้งหมดแล้ว — ยกเว้น Content.vue
และ Highlights.vue
เนื่องจาก Content.vue
เป็นองค์ประกอบที่โง่เขลาที่เพียงแค่ถ่ายทอดข้อมูล การจัดรูปแบบขั้นต่ำที่จำเป็นจึงได้รับการคุ้มครองแล้ว นอกจากนี้ เราได้เขียนโค้ดที่เหมาะสมสำหรับการจัดสไตล์แถบด้านข้างและการ์ดที่มีแผนภูมิแล้ว ดังนั้น สิ่งที่เราต้องทำคือเพิ่มโวหารบิตลงใน Highlights.vue
ซึ่งส่วนใหญ่เกี่ยวข้องกับการใช้คลาส CSS:
<template> <div class="custom-content-card content-card card"> <div class="card-body pb-0"> <div class="content-header h4 text-center pt-2 pb-3">Highlights</div> <div class="row"> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </div> </div> </template> <script> import UVIndex from "./UVIndex.vue"; import Visibility from "./Visibility.vue"; import WindStatus from "./WindStatus.vue"; export default { props: ["highlights"], components: { "uv-index": UVIndex, "visibility": Visibility, "wind-status": WindStatus, }, }; </script>
การปรับใช้และซอร์สโค้ด
ด้วยแผนภูมิและรูปแบบตามลำดับ เราทำเสร็จแล้ว! ใช้เวลาสักครู่เพื่อชื่นชมความงามของการสร้างสรรค์ของคุณ
ถึงเวลาแล้วที่คุณจะปรับใช้แอปพลิเคชันของคุณ และแบ่งปันกับเพื่อนๆ ของคุณ หากคุณไม่มีความคิดมากนักเกี่ยวกับการปรับใช้และคาดหวังให้เราช่วยเหลือคุณ ดูที่นี่เกี่ยวกับแนวคิดในการปรับใช้ของเรา บทความที่เชื่อมโยงยังมีคำแนะนำเกี่ยวกับวิธีการลบลายน้ำ FusionCharts ที่ด้านล่างซ้ายของทุกแผนภูมิ
หากคุณเลอะที่ไหนสักแห่งและต้องการจุดอ้างอิง ซอร์สโค้ดมีอยู่ใน Github