body: nó là gì, thuộc tính tính toán View trong SwiftUI

Tác giả: IT Sectr Đã đăng: 2026-06-24 Thời gian đọc: 7 phút

Thuộc tính body là yếu tố trung tâm của giao thức View trong SwiftUI, quyết định nội dung nào được hiển thị trên màn hình. Theo Apple Developer Documentation, 2024, body là yêu cầu bắt buộc duy nhất của giao thức View và trả về một kiểu tuân theo cùng giao thức này. SwiftUI gọi body mỗi khi trạng thái thay đổi để xây dựng và so sánh một cây phần tử mới.

Những Điểm Chính

  • body là thuộc tính tính toán bắt buộc cho tất cả các kiểu triển khai giao thức View
  • some View là kiểu trả về mờ cho phép SwiftUI tối ưu hóa việc hiển thị
  • body được gọi mỗi khi trạng thái thay đổi nhưng không được có tác dụng phụ
  • ViewBuilder bao bọc body ngầm nếu nó trả về nhiều phần tử
  • body không được gọi nếu danh tính và trạng thái của View không thay đổi

Body trong SwiftUI là gì?

body là một thuộc tính tính toán, là yêu cầu bắt buộc duy nhất của giao thức View. Mọi cấu trúc tuân theo View phải triển khai body. Thuộc tính này trả về nội dung mà SwiftUI hiển thị trên màn hình — có thể là văn bản, hình ảnh, nút bấm, vùng chứa với các phần tử lồng nhau hoặc bất kỳ kiểu nào khác tuân theo giao thức View.

Chữ ký của body luôn cố định: var body: some View { get }. Kiểu trả về là some View (kiểu mờ), không phải kiểu cụ thể. Điều này có nghĩa là các Views khác nhau có thể trả về các kiểu cụ thể khác nhau trong body, nhưng trình biên dịch Swift sẽ cố định kiểu cụ thể cho mỗi triển khai tại thời điểm biên dịch.

Theo WWDC 2022, body là điểm vào cho mô tả giao diện khai báo. Không giống như UIKit, nơi bạn tạo và cấu hình UIView một cách mệnh lệnh, trong SwiftUI bạn mô tả một cách khai báo những gì cần hiển thị và SwiftUI tự tính toán cách triển khai nó.

body như một hàm thuần túy

body nên hoạt động như một hàm thuần túy — với cùng đầu vào (thuộc tính cấu trúc và trạng thái) nó phải trả về cùng một cây View. Nếu body phụ thuộc vào trạng thái thay đổi bên ngoài (biến toàn cục, UserDefaults mà không có trình bao bọc @AppStorage), hành vi trở nên không thể đoán trước và SwiftUI có thể vẽ lại màn hình không chính xác.

Thuộc tính tính toán body hoạt động như thế nào

Thuộc tính tính toán body không lưu trữ giá trị — nó được tính toán mỗi khi được truy cập. Khi SwiftUI xác định rằng trạng thái đã thay đổi, nó tạo lại cấu trúc View và đọc giá trị mới của body để lấy cây phần tử hiện tại cho việc hiển thị.

swift
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Bộ đếm: \(count)")
                .font(.largeTitle)
            Button("Tăng") {
                count += 1
            }
            .padding()
            .background(.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
        }
    }
}

Trong ví dụ này, body trả về một VStack chứa Text và một nút với các bổ từ. Khi nút được nhấn, thuộc tính @State count tăng lên, SwiftUI tạo lại cấu trúc CounterView và gọi body một lần nữa để lấy cây đã cập nhật với giá trị Text mới.

Các bổ từ (.font, .padding, .background, .foregroundColor, .cornerRadius) không sửa đổi View gốc mà bao bọc nó trong ModifiedContent — một kiểu mới thêm sự sửa đổi. Mỗi bổ từ tạo ra một cấp độ lồng nhau khác, điều quan trọng cần xem xét cho hiệu suất.

body và kiểu mờ some View

some View trong kiểu trả về của body không chỉ là quy ước mà là yêu cầu của trình biên dịch. Swift yêu cầu tất cả các đường dẫn trả về trong body phải có cùng kiểu cụ thể. Không có @ViewBuilder, bạn không thể trả về Text trong một nhánh và Button trong nhánh khác — trình biên dịch sẽ báo lỗi.

swift
struct ConditionalView: View {
    var isReady: Bool

    @ViewBuilder
    var body: some View {
        if isReady {
            Text("Sẵn sàng")
                .foregroundColor(.green)
        } else {
            ProgressView()
        }
    }
}

@ViewBuilder trên body cho phép sử dụng logic điều kiện (if/else, switch) mà không có lỗi biên dịch. ViewBuilder tự động bao bọc các nhánh khác nhau trong ConditionalContent — một kiểu đặc biệt che giấu sự khác biệt về kiểu cụ thể. Đây là khả năng chính để xây dựng giao diện động.

Không có @ViewBuilder, trình biên dịch cố gắng suy ra một kiểu duy nhất cho tất cả các đường dẫn trả về. Nếu các kiểu khác nhau — lỗi xảy ra. Đây là lý do tại sao SwiftUI áp dụng ngầm @ViewBuilder cho body trong các khai báo View, mặc dù trong mã người dùng bạn cần thêm chú thích một cách rõ ràng cho các phương thức và thuộc tính tùy chỉnh trả về nhiều Views.

Hiệu suất của some View

Sử dụng some View thay vì kiểu cụ thể không làm giảm hiệu suất — trình biên dịch biết kiểu chính xác tại thời điểm biên dịch và tạo mã trực tiếp mà không cần phân phối động. AnyView, ngược lại, sử dụng xóa kiểu (type erasure) với chi phí đóng gói trong vùng chứa hiện sinh.

Vòng đời của body: khi nào và cách nó được gọi

body được SwiftUI gọi trong ba kịch bản chính: khi View được hiển thị lần đầu tiên, khi @State/@Binding/@ObservedObject/@StateObject thay đổi và khi View cha truyền giá trị mới qua trình khởi tạo. SwiftUI cũng có thể gọi body khi các giá trị môi trường (@Environment) thay đổi.

Tần suất gọi body không nên làm bạn lo lắng — SwiftUI tối ưu hóa việc vẽ lại thông qua cơ chế danh tính. Mỗi View trong hệ thống phân cấp có một định danh duy nhất. Nếu danh tính và dữ liệu đầu vào không thay đổi — body không được gọi ngay cả khi View cha được vẽ lại. Điều này đạt được thông qua so sánh Equatable và ổn định cấu trúc.

swift
struct ParentView: View {
    var body: some View {
        ChildView(name: "Alice") // Danh tính ổn định
    }
}

struct ChildView: View {
    let name: String
    var body: some View {
        Text("Xin chào, \(name)!")
    }
}

Trong ví dụ này, nếu ParentView được vẽ lại nhưng truyền cùng giá trị name — ChildView.body không được gọi. SwiftUI so sánh dữ liệu đầu vào của cấu trúc và nếu không thay đổi, bỏ qua việc vẽ lại thành phần con. Đây là cơ chế phân biệt view.

Khi body được gọi bất ngờ

Có một số cạm bẫy dẫn đến việc gọi body bất ngờ: sử dụng lớp không có ObservableObject, truyền các bao đóng được tạo bên trong body (mỗi lần tạo bao đóng tạo ra một danh tính mới) và sử dụng EquatableView không đúng cách. Nếu body được gọi quá thường xuyên — hãy kiểm tra tính ổn định danh tính của tất cả các thành phần con.

Các phương pháp hay nhất khi làm việc với body

Quy tắc đầu tiên: body nên tối thiểu. Di chuyển logic phức tạp vào các thuộc tính tính toán riêng biệt hoặc các phương thức trả về View. Điều này cải thiện khả năng đọc và cho phép SwiftUI xác định chính xác hơn phần nào của hệ thống phân cấp đã thay đổi. Chia các body lớn thành các thành phần phụ với ranh giới trách nhiệm rõ ràng.

Quy tắc thứ hai: không sử dụng body để thực hiện công việc. Tải dữ liệu, thao tác mạng, ghi cơ sở dữ liệu — tất cả điều này nên xảy ra bên ngoài body, trong các tác vụ (tasks), bổ từ onChange hoặc thông qua ObservableObject. body chỉ dành cho khai báo giao diện.

Quy tắc thứ ba: sử dụng thuộc tính EquatableView hoặc giao thức Equatable tùy chỉnh cho Views nếu so sánh cấu trúc tiêu chuẩn không đủ. Điều này cho phép bạn chỉ rõ ràng cho SwiftUI khi nào một View con cần vẽ lại và tránh các cuộc gọi body không cần thiết.

Quy tắc thứ tư: nếu body chứa các tính toán phức tạp (định dạng, lọc, sắp xếp) — hãy sử dụng @State để lưu vào bộ nhớ đệm kết quả hoặc di chuyển các tính toán vào một phương thức riêng được gọi từ onChange. Các tính toán lặp đi lặp lại trong body mỗi khi trạng thái cập nhật là nguyên nhân phổ biến gây chậm hoạt ảnh.

Quy tắc thứ năm: cho danh sách (List, ForEach) hãy đảm bảo định danh ổn định thông qua tham số id. Không có danh tính ổn định, ForEach tạo lại tất cả các phần tử khi có bất kỳ thay đổi nào, gọi body cho từng phần tử, ngay cả khi chỉ một phần tử thay đổi.

Các Câu Hỏi Thường Gặp

Body trong SwiftUI là gì?

body là thuộc tính tính toán của giao thức View, trả về nội dung để hiển thị. Đây là yêu cầu bắt buộc duy nhất của giao thức. Kiểu trả về là some View, cho phép SwiftUI tối ưu hóa hệ thống phân cấp tại thời điểm biên dịch.

Body có thể được gọi nhiều lần không?

Có, SwiftUI gọi body mỗi khi trạng thái (@State, @Binding, @ObservedObject) hoặc dữ liệu đầu vào thay đổi. Đây là hành vi bình thường của một framework khai báo. SwiftUI tối ưu hóa tần suất gọi thông qua cơ chế danh tính và so sánh Equatable.

Tại sao body trả về some View thay vì kiểu cụ thể?

some View là kiểu mờ che giấu triển khai cụ thể. Trình biên dịch cố định kiểu tại thời điểm biên dịch, đảm bảo hiệu suất gọi trực tiếp. Điều này mang lại sự linh hoạt: bạn có thể thay đổi kiểu trả về mà không thay đổi chữ ký.

Có thể trả về nil từ body không?

Không, body không thể là tùy chọn — kiểu trả về some View không cho phép nil. Nếu bạn cần ẩn một phần tử theo điều kiện, hãy sử dụng logic điều kiện bên trong @ViewBuilder hoặc trả về EmptyView, phần tử này không chiếm không gian trong hệ thống phân cấp.

Số lượng bổ từ có ảnh hưởng đến hiệu suất của body không?

Mỗi bổ từ tạo ra một lớp ModifiedContent mới, làm tăng độ sâu của hệ thống phân cấp. Đối với hầu hết các màn hình (tối đa 50 bổ từ), tác động là không đáng kể. Số lượng bổ từ quá nhiều (hàng trăm) có thể làm chậm quá trình so sánh. Hãy nhóm các bổ từ liên quan vào các phần mở rộng tùy chỉnh.

Tổng Kết

  • body là thuộc tính tính toán bắt buộc của giao thức View, xác định nội dung màn hình
  • some View là kiểu trả về mờ che giấu triển khai cụ thể khỏi mã gọi
  • @ViewBuilder được áp dụng ngầm cho body để hỗ trợ logic điều kiện và nhiều phần tử
  • body không được chứa tác dụng phụ — đây là khai báo giao diện thuần túy
  • SwiftUI tối ưu hóa các cuộc gọi body thông qua cơ chế danh tính và so sánh Equatable
  • Chia nhỏ các body lớn thành các thành phần phụ để có hiệu suất và khả năng đọc tốt hơn
  • AnyView làm tăng chi phí — hãy sử dụng @ViewBuilder và Group thay vì xóa kiểu

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm