Composition: สาระสำคัญ การสร้าง UI Tree ใน Compose

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-27 เวลาอ่าน: 7 นาที

Composition เป็นกระบวนการหลักใน Jetpack Compose ซึ่งในระหว่างนั้น UI Tree ที่แสดงผลบนหน้าจอจะถูกสร้างขึ้นจากฟังก์ชัน Composable ที่อธิบาย แตกต่างจากระบบ View ของ Android ที่เลย์เอาต์ถูกโหลดจาก XML และแปลงเป็นอ็อบเจกต์ที่ไม่สามารถเปลี่ยนแปลงได้ Composition ทำงานเป็นระบบไดนามิก: ฟังก์ชันทำงาน, สร้างสล็อตในหน่วยความจำ, สร้างลำดับชั้นของโหนด, และเชื่อมโยงกับสถานะ ตาม Google Android Developers, 2026 การทำความเข้าใจ Composition เป็นสิ่งสำคัญสำหรับการเพิ่มประสิทธิภาพของแอปพลิเคชัน Compose

ประเด็นสำคัญ

  • Composition คือการดำเนินการฟังก์ชัน Composable เพื่อสร้าง UI Tree
  • สล็อต คือเซลล์หน่วยความจำที่เก็บพารามิเตอร์และสถานะของแต่ละฟังก์ชัน
  • ตำแหน่ง ใน Compose (Positional Memorization) ผูกสถานะกับตำแหน่งในโค้ด
  • รอบแรก Composition สร้าง UI Tree เริ่มต้นเมื่อเริ่มหน้าจอ
  • CompositionLocal ส่งข้อมูลผ่าน Tree โดยไม่ต้องใช้พารามิเตอร์ที่ชัดเจน

Composition ใน Jetpack Compose คืออะไร

Composition คือกระบวนการดำเนินการฟังก์ชัน Composable ซึ่งส่งผลให้เกิดการแสดงภายในของอินเทอร์เฟซผู้ใช้เป็น Tree ของโหนด แต่ละโหนดใน Tree นี้สอดคล้องกับคอมโพเนนต์ในตัว (Text, Button, Image) หรือการเรียกฟังก์ชัน Composable ที่ผู้ใช้กำหนด Composition ไม่ได้สร้างอ็อบเจกต์ View ของ Android โดยตรง — มันสร้างคำอธิบายนามธรรมที่ถูกประมวลผลโดยเฟส Layout และ Drawing

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

ในทางเทคนิค Composition ถูกจัดการผ่าน Composer — เอ็นจินภายในที่คอมไพเลอร์ Kotlin ฝังลงในทุกฟังก์ชัน Composable Composer เขียนข้อมูลเกี่ยวกับฟังก์ชันที่ถูกเรียก, ด้วยพารามิเตอร์อะไร, และในลำดับใด ลงในสล็อต (กลุ่มตำแหน่ง) ในการเรียกครั้งต่อมา Composer เปรียบเทียบข้อมูลใหม่กับข้อมูลที่เก็บไว้และตัดสินใจว่าจะเริ่มใหม่หรือไม่

UI Tree ถูกสร้างอย่างไรในระหว่าง Composition

กระบวนการสร้าง UI Tree เริ่มต้นด้วยการเรียกเมธอด setContent ภายใน Activity หรือ Fragment เมธอดนี้สร้าง Composition เริ่มต้นและเริ่มดำเนินการฟังก์ชัน Composer ระดับราก จากนั้นฟังก์ชัน Composer ที่ซ้อนกันแต่ละตัวจะเพิ่มโหนดของตนลงใน Tree สร้างลำดับชั้น: Row ประกอบด้วย Text และ Button, Column ประกอบด้วย Image และ Card เป็นต้น

แต่ละโหนด Tree จะได้รับ คีย์ตำแหน่ง เฉพาะตัว ตามตำแหน่งในซอร์สโค้ด คีย์นี้ใช้เพื่อระบุโหนดในการดำเนินการครั้งต่อไป คีย์ตำแหน่งเป็นเหตุผลว่าทำไมลำดับการเรียกฟังก์ชัน Composer จึงไม่ควรขึ้นอยู่กับเงื่อนไข: หากในรันหนึ่งเรียก A -> B และในรันถัดไปเรียก B -> A Compose จะไม่สามารถจับคู่โหนดเก่าและใหม่ได้

kotlin
@Composable
fun AppScreen() {
    Column {                     // โหนด Column (ตำแหน่ง 1)
        HeaderSection()            // โหนด HeaderSection (ตำแหน่ง 2)
        ContentSection()           // โหนด ContentSection (ตำแหน่ง 3)
        FooterSection()            // โหนด FooterSection (ตำแหน่ง 4)
    }
}

@Composable
fun HeaderSection() {
    Row {                       // โหนด Row (ตำแหน่ง 2.1)
        Text("หัวเรื่อง")         // โหนด Text (ตำแหน่ง 2.2)
        Icon(...)                // โหนด Icon (ตำแหน่ง 2.3)
    }
}

ในตัวอย่างนี้ แต่ละการเรียกได้รับตำแหน่งตามลำดับในโค้ด Column (ตำแหน่ง 1) ประกอบด้วยโหนดลูกสามตัว (ตำแหน่ง 2, 3, 4) HeaderSection เพิ่มโหนดลูกอีกสองตัว (2.1, 2.2, 2.3) หากในการทำซ้ำครั้งถัดไป ContentSection ถูกเรียกก่อน HeaderSection Composer จะไม่สามารถจับคู่โหนดได้อย่างถูกต้อง — ดังนั้นกฎ: ลำดับการเรียกฟังก์ชัน Composer ต้องเสถียร

การจัดการสถานะใน Composition

สถานะใน Composition ถูกจัดการผ่านอ็อบเจกต์ประเภท State<T> เมื่อฟังก์ชัน Composer อ่านค่าจาก State ผ่านคุณสมบัติที่มอบหมาย (by) มันจะลงทะเบียนการพึ่งพาใน State นั้น เมื่อค่าเปลี่ยน ฟังก์ชันทั้งหมดที่อ่าน State นี้จะถูกทำเครื่องหมายให้เริ่มใหม่ในเฟส Composition ถัดไป

กลไกการลงทะเบียนการพึ่งพาเรียกว่า ระบบสแนปชอต ทุกครั้งที่ State เปลี่ยนแปลง สแนปชอตจะบันทึกการเปลี่ยนแปลงทั้งหมดและแจ้ง Composer ว่าฟังก์ชันใดขึ้นอยู่กับ State นั้น สิ่งสำคัญที่ต้องเข้าใจ: การอ่าน State ภายในโค้ดที่ไม่ใช่ Composable (เช่น ใน lambda onClick) ไม่ได้ลงทะเบียนการพึ่งพา — เฉพาะการอ่านภายในฟังก์ชัน Composable หรือใน lambdas ที่ดำเนินการในบริบทของ Composition

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

kotlin
@Composable
fun StateExample() {
    var text by remember { mutableStateOf("Hello") }
    var isVisible by remember { mutableStateOf(true) }

    Column {
        Text(text)  // ลงทะเบียนการพึ่งพาของ text

        if (isVisible) {  // ลงทะเบียนการพึ่งพาของ isVisible
            TextField(value = text, onValueChange = { text = it })
        }

        Button(onClick = { isVisible = !isVisible }) {
            Text(if (isVisible) "ซ่อน" else "แสดง")
        }
    }
}

การเปลี่ยน text ทำให้เกิดการทำซ้ำเฉพาะ Column, Text และ TextField เท่านั้น Column, Button และเงื่อนไข isVisible ยังคงไม่เปลี่ยนแปลง การแยกตัว ของการทำซ้ำนี้เป็นข้อได้เปรียบหลักของ Compose เหนือระบบที่วาดทั้งหน้าจอใหม่ทั้งหมด ฟังก์ชัน Composable แต่ละตัวติดตามเฉพาะอ็อบเจกต์ State ที่มันอ่านโดยตรง

Composition vs Recomposition: ความแตกต่างหลัก

Composition และ Recomposition เป็นสองโหมดที่แตกต่างกันในการดำเนินการฟังก์ชัน Composable Composition เกิดขึ้นครั้งเดียวเมื่อสร้างหน้าจอ: ระบบดำเนินการฟังก์ชัน Composable ทั้งหมดด้วยค่าเริ่มต้นและสร้าง UI Tree เริ่มต้น Recomposition เกิดขึ้นหลายครั้งเมื่อข้อมูลเปลี่ยนแปลง: ระบบเริ่มเฉพาะฟังก์ชันที่ขึ้นอยู่กับสถานะที่เปลี่ยนแปลงเท่านั้น

โหมด Composition เปิดใช้งานโหนด Tree ทั้งหมด จัดสรรสล็อตสำหรับแต่ละฟังก์ชัน และลงทะเบียนลูกหลานทั้งหมด Recomposition ทำงานแบบเลือก: Compose เปรียบเทียบค่าพารามิเตอร์ใหม่และเก่าของแต่ละฟังก์ชัน และหากไม่เปลี่ยนแปลง — ฟังก์ชันจะไม่ถูกดำเนินการ (การข้าม)

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

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

CompositionLocal: การส่งข้อมูลผ่าน Tree

CompositionLocal เป็นกลไกสำหรับการส่งข้อมูลโดยปริยายผ่าน Tree ของ Composition มันแก้ปัญหาเมื่อพารามิเตอร์ต้องถูกส่งผ่านฟังก์ชัน Composable ที่ซ้อนกันหลายสิบตัวที่ไม่ได้ใช้มันโดยตรง แทนที่จะใช้ลูกโซ่พารามิเตอร์ที่ชัดเจน ข้อมูลถูกกำหนดที่ระดับบนสุดและอ่านในฟังก์ชันที่ซ้อนกันใด ๆ ผ่าน CompositionLocal.current

MaterialTheme เป็นตัวอย่างที่รู้จักกันดีที่สุดของ CompositionLocal คอมโพเนนต์ Compose ทั้งหมดอ่านสี รูปแบบตัวอักษร และรูปร่างผ่าน MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes โดยไม่ได้รับผ่านพารามิเตอร์ นักพัฒนาสามารถสร้าง CompositionLocal ของตนเองสำหรับข้อมูลเช่นผู้ใช้ปัจจุบัน การตั้งค่าการแปลภาษา หรือการกำหนดค่าหน้าจอ

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

kotlin
val LocalUser = compositionLocalOf<User?> { null }

@Composable
fun AppRoot(user: User, content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalUser.provides(user)) {
        content()
    }
}

@Composable
fun UserAvatar() {
    val user = LocalUser.current  // การอ่านโดยไม่มีพารามิเตอร์ที่ชัดเจน
    AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}

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

คำถามที่พบบ่อย

จะเกิดอะไรขึ้นหาก State ถูกเปลี่ยนระหว่าง Composition

การเปลี่ยน State ระหว่าง Composition จะกำหนดเวลาการทำซ้ำใหม่ ซึ่งจะดำเนินการหลังจากปัจจุบันเสร็จสิ้น ไม่เกิดการวนซ้ำไม่สิ้นสุด: Compose รับประกันว่าการทำซ้ำแต่ละครั้งจะดำเนินการในธุรกรรมแยกต่างหากของระบบสแนปชอต

Composition ของหน้าจอที่ซับซ้อนใช้เวลานานเท่าใด

บนอุปกรณ์สมัยใหม่ Composition ของหน้าจอที่มีฟังก์ชัน Composable 50–100 ตัวใช้เวลา 1–5 มิลลิวินาที Google แนะนำให้อยู่ใน 16 มิลลิวินาทีสำหรับเฟรมที่ 60fps หาก Composition เกินขีดจำกัดนี้ ให้ใช้ LazyColumn หรือแบ่งหน้าจอเป็นฟังก์ชันย่อยๆ

สามารถเริ่ม Composition ด้วยตนเองได้หรือไม่

การเริ่ม Composition ด้วยตนเองโดยตรงไม่สามารถทำได้ — มันถูกจัดการโดย Composer โดยอัตโนมัติ อย่างไรก็ตาม คุณสามารถบังคับให้ทำซ้ำได้โดยการเปลี่ยน State หรือเรียก invalidate() ที่ root composable หากคุณสามารถเข้าถึง CompositionContext

Composition แตกต่างจากลำดับชั้น View ใน Android แบบคลาสสิกอย่างไร

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

Composition จัดการกับการลบโหนดอย่างไร

หากฟังก์ชัน Composable ไม่ถูกเรียกอีกต่อไป (เช่น เงื่อนไข if กลายเป็น false) Composition จะลบโหนดของมันและเรียกใช้การล้างข้อมูล DisposableEffect เมื่อมันปรากฏขึ้นอีกครั้ง (if กลายเป็น true อีกครั้ง) โหนดใหม่จะถูกสร้างขึ้น — โหนดเก่าจะไม่ถูกกู้คืน

สรุป

  • Composition คือกระบวนการดำเนินการฟังก์ชัน Composable เพื่อสร้าง UI Tree ที่ผูกกับสถานะ
  • Composer จัดการสล็อต บันทึกการเรียกฟังก์ชัน และเปรียบเทียบพารามิเตอร์ระหว่างการทำซ้ำ
  • ระบบสแนปชอต ลงทะเบียนการพึ่งพาฟังก์ชันต่อ State และรวมการเปลี่ยนแปลงเป็นธุรกรรม
  • Composition ดำเนินการครั้งเดียวเมื่อเริ่มต้น Recomposition — เมื่อข้อมูลเปลี่ยน
  • CompositionLocal ส่งข้อมูลการกำหนดค่าผ่าน Tree โดยไม่มีลูกโซ่พารามิเตอร์ที่ชัดเจน
  • ตำแหน่ง ของการเรียกฟังก์ชันทำหน้าที่เป็นตัวระบุเฉพาะใน Tree ของ Composition
  • คำแนะนำ: ทำให้ฟังก์ชัน Composable เล็กด้วยพารามิเตอร์ที่ไม่เปลี่ยนรูปเพื่อการข้ามที่มีประสิทธิภาพ

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม