printWidth — ความกว้างที่ Prettier พยายามทำให้ได้ ไม่ใช่ขีดจำกัดตายตัว
ตัวเลือก Print Width ของ Prettier
printWidth เป็นออปชันของ Prettier ที่คนเข้าใจผิดบ่อยที่สุด มันไม่ใช่ความยาวบรรทัดสูงสุดที่ Prettier รับปากว่าจะไม่มีวันเกิน แต่เป็นความกว้างที่ตัวพิมพ์ใช้วัดตอนตัดสินว่านิพจน์หนึ่งยังอยู่ในบรรทัดเดียวได้หรือไม่ Prettier จะยอมพิมพ์บรรทัดที่ยาวกว่านี้เมื่อไม่มีวิธีขึ้นบรรทัดใหม่ที่ถูกต้อง และจะขึ้นบรรทัดใหม่ตั้งแต่ยังไม่ถึงขีดจำกัดเมื่อโครงสร้างบังคับ
The line length where Prettier will try wrap.- ค่าเริ่มต้น
80- ชนิด
int- แฟล็ก CLI
--print-width
printWidth ควบคุมอะไรจริง ๆ
Prettier ไม่ได้จัดรูปแบบด้วยการนับตัวอักษรแล้วแทรกบรรทัดใหม่ แต่แปลงโค้ดของคุณเป็นโครงสร้างกลางที่ประกอบด้วยกลุ่ม (group) แล้วถามแต่ละกลุ่มด้วยคำถามเดียวว่า กลุ่มนี้ยังพอดีกับพื้นที่ที่เหลือบนบรรทัดนี้หรือไม่ printWidth คือตัวเลขที่ใช้วัดคำถามนั้น กลุ่มที่พอดีจะถูกพิมพ์เป็นบรรทัดเดียว ส่วนกลุ่มที่ไม่พอดีจะถูกแตกออก แล้วทุกกลุ่มข้างในก็จะถูกถามคำถามเดิมซ้ำที่ระดับย่อหน้าใหม่
ด้วยเหตุนี้ การเพิ่ม printWidth จึงไม่ได้แค่ทำให้บรรทัดยาวขึ้นเฉย ๆ แต่เปลี่ยนจุดที่โครงสร้างทั้งก้อนจะยุบหรือกางออก ทั้งอ็อบเจกต์ลิเทอรัล ลายเซ็นฟังก์ชัน และเชนของการเรียกเมท็อด แต่ละอย่างจะสลับไปมาระหว่างแบบบรรทัดละรายการกับแบบรวมบรรทัดเดียวที่ค่าขีดแบ่งบางค่า และการจัดรูปแบบใหม่ที่ตามมาหลังแก้ออปชันนี้มักใหญ่กว่าขนาดของการแก้ไขมาก
ผลที่ตามมาโดยตรงมีสามข้อ และแต่ละข้อก็ทำให้บางคนแปลกใจเสมอ
- สตริงยาว ๆ พาธของ import หรือ URL ในคอมเมนต์ จะเลย
printWidthออกไป เพราะ Prettier จะไม่ตัดสตริงให้คุณ เนื่องจากนั่นจะเปลี่ยนค่าของมัน - นิพจน์ที่ซ้อนลึกอาจขึ้นบรรทัดใหม่ตั้งแต่ยังห่างจาก
printWidthมาก เพราะระยะย่อหน้าถูกนับรวมเป็นส่วนหนึ่งของความกว้าง ที่ความลึกแปดชั้น คุณอาจเหลือพื้นที่ใช้งานจริงแค่สี่สิบคอลัมน์ - ข้อความร้อยแก้วใน Markdown ไม่ได้ถูกควบคุมด้วยออปชันนี้เลย นั่นเป็นหน้าที่ของ
proseWrapส่วนprintWidthจำกัดเฉพาะโค้ดที่ Prettier พิมพ์ออกมา
ทำไมค่าเริ่มต้นถึงเป็น 80
แปดสิบคอลัมน์เป็นมรดกตกทอดจากบัตรเจาะรูซึ่งเก็บได้แปดสิบตัวอักษร แล้วส่งต่อมายังเทอร์มินัลที่เลียนแบบมันอีกที ทั้งสองอย่างล้าสมัยไปแล้ว แต่ตัวเลขนี้ยังอยู่ด้วยเหตุผลสองข้อที่ยังใช้ได้จริง
ข้อแรกคือความอ่านง่าย นักออกแบบตัวพิมพ์วางความยาวบรรทัดที่สบายตาสำหรับข้อความต่อเนื่องไว้ราวหกสิบถึงเก้าสิบตัวอักษรมานาน ถ้ากว้างกว่านั้นมาก สายตาจะหาจุดเริ่มต้นของบรรทัดถัดไปไม่เจอ โค้ดไม่ใช่ร้อยแก้ว แต่ก็ยังถูกอ่านจากซ้ายไปขวาด้วยสายตาคู่เดียวกัน
ข้อสองคือ diff และนี่คือเหตุผลที่สำคัญที่สุดเมื่อทำงานเป็นทีม ความกว้างที่แคบบังคับให้ขึ้นบรรทัดใหม่บ่อยขึ้น และการขึ้นบรรทัดใหม่ที่บ่อยขึ้นทำให้การเปลี่ยนแปลงแต่ละครั้งเล็กลง เมื่ออาร์กิวเมนต์แต่ละตัวอยู่บรรทัดของตัวเอง การแก้อาร์กิวเมนต์หนึ่งตัวจะกระทบแค่บรรทัดเดียว รีวิวเห็นบรรทัดเดียว git blame ชี้ไปที่บรรทัดเดียว และ conflict ตอน merge ก็เกี่ยวข้องแค่บรรทัดเดียว พอขยายขีดจำกัดจนการเรียกฟังก์ชันนั้นยุบเหลือบรรทัดเดียว ทุกอย่างที่ว่ามาก็จะครอบคลุมทั้งการเรียกนั้นแทน
ควรเปลี่ยนเมื่อไร และเมื่อไรไม่ควร
คำตอบตรงไปตรงมาคือ 80 เป็นค่าเริ่มต้นที่ดี และโปรเจกต์ส่วนใหญ่ควรปล่อยไว้อย่างนั้น เหตุผลที่จะเพิ่มค่านั้นมีจริง แต่แคบกว่าที่เห็นตอนแรก
เพิ่มค่าเมื่อภาษาที่ใช้ยืดยาวพอจนแปดสิบคอลัมน์ไม่เหลือที่ให้เนื้อหา ทั้ง TypeScript ที่ใช้ generic ซ้อนลึก JSX ที่มี prop จำนวนมาก และโค้ดที่ตั้งชื่อยาวเพื่อสื่อความหมาย ล้วนใช้ความกว้างไปกับไวยากรณ์ก่อนจะถึงสิ่งที่มีความหมายจริง ค่า 100 คือก้าวแรกที่คนนิยมและเป็นค่าเริ่มต้นที่ปกป้องได้สำหรับโค้ดเบส TypeScript สมัยใหม่ ส่วน 120 เหมาะกับจอกว้างและการรีวิวทีละไฟล์ แต่แลกมาด้วยต้นทุนจริงเมื่อดู diff แบบเทียบข้างกัน
อย่าเพิ่มค่าเพียงเพื่อปิดเสียงบ่นของ linter ถ้า max-len กำลังบ่น ทางแก้คือหยุดไม่ให้ max-len ทำงานเลย ดูหัวข้อด้านล่าง การเพิ่ม printWidth ให้ตรงกับกฎ lint คือการผูกสองการตั้งค่าที่ตอบคำถามคนละเรื่องเข้าด้วยกัน แล้วสุดท้ายคุณจะแก้อันหนึ่งแล้วลืมอีกอัน
{
"printWidth": 100
}ข้อแลกเปลี่ยนและข้อตกลงของทีม
ไม่ว่าจะเลือกค่าไหน ให้เลือกครั้งเดียว ต้นทุนของออปชันนี้เกือบทั้งหมดอยู่ที่การเปลี่ยนค่า ไม่ใช่ที่ตัวค่าเอง เพราะการเปลี่ยนแต่ละครั้งจะจัดรูปแบบโค้ดเบสใหม่เป็นสัดส่วนใหญ่ ซึ่งกลบประวัติการแก้ไขจริงไว้ใต้คอมมิตที่เป็นแค่การจัดรูปแบบ
- เปลี่ยนในคอมมิตแยกที่ไม่ทำอย่างอื่นเลย เพื่อให้ผู้รีวิวข้ามได้ และเพื่อให้สอน
git blameให้ข้ามได้ด้วย - ใส่คอมมิตนั้นในไฟล์
.git-blame-ignore-revsแล้วชี้blame.ignoreRevsFileมาที่ไฟล์นี้ เพื่อให้ข้อมูลผู้เขียนรอดจากการจัดรูปแบบใหม่ - คิดถึงวิธีที่ทีมรีวิวโค้ด diff แบบเทียบข้างกันบนแล็ปท็อปให้พื้นที่แต่ละฝั่งราวครึ่งหน้าจอ ค่า 120 จะถูกตัดบรรทัดในเครื่องมือรีวิวถึงแม้จะพอดีในเอดิเตอร์
- ให้ดูที่ที่แคบที่สุดที่โค้ดถูกอ่าน ไม่ใช่ที่ที่กว้างที่สุดที่โค้ดถูกเขียน
ESLint เอดิเตอร์ และ CI
กฎ max-len ของ ESLint กับ printWidth จะขัดกันเอง เพราะ max-len เป็นขีดจำกัดตายตัวส่วน printWidth ไม่ใช่ บรรทัดที่ Prettier ตัดไม่ได้ตามกฎไวยากรณ์ เช่นสตริงยาวหรือ import ยาว จะผ่าน Prettier แต่ตก ESLint ไปตลอดกาล
ทางแก้คือ eslint-config-prettier ซึ่งปิดกฎ ESLint ทุกข้อที่ทับซ้อนกับการจัดรูปแบบ รวมถึง max-len ด้วย วางไว้ท้ายสุดในคอนฟิกเพื่อให้มันมีผลเหนือกว่า
import prettier from 'eslint-config-prettier';
export default [
// ...กฎของคุณ
prettier,
];ในเอดิเตอร์ ให้ตั้งเส้นไกด์ที่ตัวเลขเดียวกันเพื่อให้เห็นขีดจำกัดขณะพิมพ์ ใช้ editor.rulers ใน VS Code เส้นนั้นเป็นแค่ตัวช่วยมอง ไม่ใช่กฎ Prettier ยังคงเป็นสิ่งเดียวที่ตัดสินว่าบรรทัดจะขึ้นใหม่ตรงไหนจริง ๆ
ข้อผิดพลาดที่พบบ่อย
- มองว่ามันเป็นการรับประกัน จริง ๆ มันเป็นเป้าหมาย บรรทัดจะเกินค่านี้ได้ และนั่นคือพฤติกรรมที่ถูกต้อง ไม่ใช่บั๊กที่ต้องไปรายงาน
- ตั้งค่าให้ตรงกับ
max-lenแทนที่จะปิดmax-lenไปเลย กลายเป็นมีแหล่งความจริงสองแหล่งสำหรับการตัดสินใจเดียว - เปลี่ยนค่าบนโค้ดเบสขนาดใหญ่ในคอมมิตที่มีงานจริงปนอยู่ด้วย ซึ่งทำให้งานจริงนั้นรีวิวไม่ได้
- คาดหวังว่ามันจะตัดบรรทัดย่อหน้าใน Markdown ให้ ใช้
proseWrapแทน - ปรับค่าแยกรายไฟล์ด้วย overrides ซึ่งทำให้โค้ดเบสดูไม่สม่ำเสมอโดยที่ผู้อ่านไม่ได้ประโยชน์อะไร
ใช้ใน .prettierrc
ใส่ printWidth ลงในไฟล์ตั้งค่า Prettier ของคุณ:
{
"printWidth": 80
}ลองใช้ในตัวสร้างตัวอย่างจริง
โค้ดเดียวกันที่จัดรูปแบบด้วยแต่ละค่าของ printWidth
printWidth: 40
const user = {
name: "Ada",
"user-id": 7,
roles: ["admin", "editor"],
active: true,
};
const greet = (name) => `Hello ${name}`;
const label = user.active
? "active member of the team"
: "inactive member of the team";
export function summarize(items) {
return items
.filter((i) => i.active)
.map((i) => i.name)
.join(", ");
}printWidth: 120
const user = { name: "Ada", "user-id": 7, roles: ["admin", "editor"], active: true };
const greet = (name) => `Hello ${name}`;
const label = user.active ? "active member of the team" : "inactive member of the team";
export function summarize(items) {
return items
.filter((i) => i.active)
.map((i) => i.name)
.join(", ");
}คำถามที่พบบ่อย
- printWidth รับประกันว่าจะไม่มีบรรทัดไหนเกินค่านี้ใช่ไหม
- ไม่ใช่ Prettier ถือว่ามันเป็นความกว้างที่พยายามทำให้ได้ ไม่ใช่เพดาน เมื่อไม่มีจุดขึ้นบรรทัดใหม่ที่ถูกต้อง เช่นภายในสตริง พาธ import ยาว ๆ หรือ URL ในคอมเมนต์ บรรทัดนั้นก็จะยาวเกิน และไม่มีการตั้งค่าใดเปลี่ยนเรื่องนี้ได้
- ควรเลือกค่าเท่าไร
- ปล่อยไว้ที่
80เว้นแต่มีเหตุผลชัดเจน ค่า100เป็นก้าวถัดไปที่นิยมสำหรับโค้ดเบส TypeScript ที่มีชื่อชนิดข้อมูลยาว ส่วน120เหมาะกับจอกว้างแต่ทำให้การรีวิวแบบเทียบข้างกันแย่ลง ตัวเลขสำคัญน้อยกว่าการเลือกสักค่าแล้วไม่กลับมารื้ออีก - ทำไมเปลี่ยน printWidth แล้วโค้ดถูกจัดรูปแบบใหม่เยอะขนาดนี้
- เพราะมันไม่ได้ทำให้บรรทัดยาวขึ้น แต่เปลี่ยนว่าโครงสร้างไหนพอดี ทุกอ็อบเจกต์ รายการอาร์กิวเมนต์ และเชนการเรียกที่อยู่ใกล้ขีดเดิมจะสลับระหว่างกางกับยุบ ให้แยกการเปลี่ยนนี้เป็นคอมมิตของตัวเองแล้วเพิ่มลงใน
.git-blame-ignore-revs - printWidth ทำงานร่วมกับ tabWidth อย่างไร
- ระยะย่อหน้าถูกนับรวมในความกว้างที่วัด และหนึ่งแท็บถูกนับเป็น
tabWidthคอลัมน์ การเพิ่มtabWidthจึงเหลือที่ให้โค้ดน้อยลงบนบรรทัดที่ซ้อนลึก เท่ากับบีบprintWidthให้แคบลงตรงจุดที่ซ้อนลึกที่สุด
ตัวเลือก Global อื่น ๆ
สร้างจาก Prettier 3.9.6