Sự phức tạp của các trang web hiện đại đã tăng lên đáng kể trong vài năm qua. Nhu cầu ngày càng tăng đối với các thiết kế tiêu chuẩn công nghiệp, chất lượng cao càng làm tăng thêm những thách thức mà các nhà phát triển giao diện người dùng phải đối mặt.
Ngày nay, ngay cả các ứng dụng giao diện người dùng cũng cần một số cân nhắc về kiến trúc để hợp lý hóa quy trình phát triển. Trong bài viết trước , tôi đã chia sẻ kinh nghiệm của mình khi triển khai phương pháp tiếp cận kiến trúc sạch trong các ứng dụng giao diện người dùng trong khi làm việc với dự án phụ của mình.
Trong bài viết này, tôi muốn tìm hiểu sâu hơn về phương pháp thiết kế nguyên tử, rút ra từ kinh nghiệm của tôi với cùng một dự án. Tôi sẽ thảo luận về những ưu điểm và nhược điểm của nó, đồng thời đánh giá tính hữu dụng của nó trong các tình huống khác nhau.
Để bắt đầu, chúng ta hãy khám phá khái niệm về một hệ thống thiết kế. Hệ thống thiết kế là tập hợp toàn diện các thành phần, hướng dẫn và nguyên tắc có thể tái sử dụng, trao quyền cho các nhóm thiết kế và phát triển giao diện người dùng nhất quán trên nhiều nền tảng.
Chúng hoạt động như một nguồn thông tin chính xác duy nhất cho cả nhà thiết kế và nhà phát triển, đảm bảo rằng các khía cạnh hình ảnh và chức năng của sản phẩm phù hợp và tuân thủ bản sắc thương hiệu đã thiết lập. Nếu bạn quan tâm đến việc khám phá các ví dụ về triển khai hệ thống thiết kế, hãy cân nhắc kiểm tra những điều sau:
Nếu bạn muốn tìm hiểu sâu hơn về chủ đề hệ thống thiết kế, tôi khuyên bạn nên xem bài viết này . Nó mô tả chủ đề này một cách chi tiết, những chi tiết không bắt buộc đối với chúng tôi trong phạm vi công việc này.
Dựa trên nền tảng của các hệ thống thiết kế, thiết kế nguyên tử là một phương pháp hợp lý hóa việc tổ chức và cấu trúc các thành phần và nguyên tắc có thể tái sử dụng. Được hình thành bởi Brad Frost, Atomic Design lấy cảm hứng từ hóa học, vì nó giải cấu trúc giao diện người dùng thành các khối xây dựng cơ bản nhất của họ và lắp ráp lại chúng thành các cấu trúc phức tạp hơn.
Đây là một hình ảnh minh họa sự tương tự với hóa học:
Các phản ứng hóa học được biểu diễn bằng các phương trình hóa học, thường cho thấy cách các nguyên tố nguyên tử kết hợp với nhau để tạo thành phân tử. Trong ví dụ trên, chúng ta thấy cách hydro và oxy kết hợp với nhau để tạo thành các phân tử nước.
Về bản chất, thiết kế nguyên tử là sự phát triển tự nhiên của các hệ thống thiết kế, cung cấp một cách tiếp cận có hệ thống để tạo ra các thành phần linh hoạt và có thể mở rộng. Bằng cách áp dụng các nguyên tắc của thiết kế nguyên tử, các nhóm có thể quản lý hệ thống thiết kế của họ hiệu quả hơn, vì bản chất mô-đun của phương pháp này giúp dễ dàng bảo trì, cập nhật và mở rộng các thành phần và mẫu trong hệ thống.
Nếu bạn lo lắng rằng điều này nghe có vẻ phức tạp, đừng lo lắng. Trong các phần sắp tới, tôi sẽ trình bày cách áp dụng các nguyên tắc này bằng cách sử dụng một ví dụ thực tế từ ứng dụng mà tôi đã phát triển, giúp dễ hiểu và dễ thực hiện trong các dự án của riêng bạn.
Thiết kế nguyên tử tổ chức các thành phần thành năm cấp độ riêng biệt, mỗi cấp độ được xây dựng dựa trên cấp độ trước đó. Hãy khám phá năm cấp độ này một cách chi tiết:
các nguyên tử : các khối xây dựng cơ bản nhất của giao diện người dùng, các nguyên tử đại diện cho các thành phần HTML riêng lẻ như các nút, trường nhập liệu và tiêu đề. Chúng là những đơn vị chức năng nhỏ nhất và không thể chia nhỏ hơn nữa.
phân tử : các phân tử được hình thành bằng cách kết hợp hai hoặc nhiều nguyên tử thành một nhóm chức năng. Chẳng hạn, một phân tử biểu mẫu tìm kiếm có thể bao gồm một nguyên tử đầu vào tìm kiếm, một nguyên tử nút và một nguyên tử nhãn. Các phân tử đại diện cho các thành phần đơn giản có thể được tái sử dụng trong một dự án.
sinh vật : sinh vật là những thành phần phức tạp hơn, được tạo ra bằng cách kết hợp nhiều phân tử và/hoặc nguyên tử. Chúng đại diện cho các phần riêng biệt của giao diện người dùng, chẳng hạn như đầu trang, chân trang hoặc thanh bên. Các sinh vật giúp hình thành bố cục và cấu trúc tổng thể của một trang.
mẫu : mẫu về cơ bản là bố cục trang được xây dựng bằng cách sử dụng các sinh vật, phân tử và nguyên tử. Chúng xác định cấu trúc và cách sắp xếp các thành phần trên một trang mà không chỉ định bất kỳ nội dung thực tế nào, phục vụ như một kế hoạch chi tiết cho các tình huống nội dung khác nhau.
trang : các trang là phiên bản cuối cùng, được thực hiện đầy đủ của các mẫu, hoàn chỉnh với nội dung và dữ liệu thực. Chúng đại diện cho những gì người dùng cuối cùng sẽ nhìn thấy và tương tác, cho thấy cách các thành phần và bố cục thích ứng với các loại nội dung và trường hợp sử dụng khác nhau.
Để phát triển quan điểm đầy đủ thông tin về thiết kế nguyên tử cho phát triển giao diện người dùng, tôi bắt đầu hành trình tạo một ứng dụng. Trong khoảng thời gian sáu tháng, tôi đã thu được những hiểu biết và kinh nghiệm quý báu khi làm việc cho dự án này.
Do đó, các ví dụ được cung cấp trong suốt bài viết này rút ra từ kinh nghiệm thực tế của tôi với ứng dụng. Để duy trì tính minh bạch, tất cả các ví dụ đều được lấy từ mã có thể truy cập công khai.
Bạn có thể khám phá kết quả cuối cùng bằng cách truy cập kho lưu trữ hoặc trang web .
Hãy ghi nhớ, tôi sẽ sử dụng các ví dụ được mã hóa trong React . Nếu bạn không quen thuộc với ngôn ngữ này, đừng lo lắng - mục đích của tôi là minh họa các khái niệm cơ bản của thiết kế nguyên tử, thay vì tập trung vào các chi tiết cơ bản của mã.
Để hiểu rõ hơn về các thành phần trong kho lưu trữ của tôi, bạn có thể tìm thấy chúng trong thư mục sau: /client/presentation
. Ở vị trí này, tôi đã tạo một thư mục mới có tên là atoms
để duy trì việc đặt tên nhất quán với phương pháp thiết kế nguyên tử. Thư mục mới này chứa tất cả các phần nhỏ cần thiết để xây dựng toàn bộ quy trình giới thiệu.
Danh sách đầy đủ các nguyên tử như sau:
atoms ├── box ├── button ├── card ├── card-body ├── card-footer ├── container ├── divider ├── flex ├── form-control ├── form-error-message ├── form-helper-text ├── form-label ├── heading ├── icon ├── input ├── list ├── list-icon ├── list-item ├── spinner ├── tab ├── tab-list ├── tab-panel ├── tab-panels ├── tabs └── text
Những tên nguyên tử này có thể quen thuộc với bạn vì chúng dựa trên gói Chakra UI . Hầu hết trong số chúng đã chứa kiểu khớp mặc định cho ứng dụng của tôi, vì vậy không có gì đặc biệt độc đáo để mô tả ở cấp độ này. Với ý nghĩ đó, chúng ta có thể tiến hành thảo luận trực tiếp về molecules
.
Ở giai đoạn này, quá trình thiết kế nguyên tử trở nên thú vị hơn và sức mạnh thực sự của nó bắt đầu bộc lộ. Mặc dù việc xác định các nguyên tử cơ sở của bạn có thể là một nhiệm vụ đơn điệu và tốn thời gian, nhưng việc xây dựng các thành phần mới bằng cách sử dụng các nguyên tử trở nên thú vị hơn nhiều.
Để xác định các phân tử, tôi đã tạo một thư mục molecules
bên trong thư mục /client/presentation
của mình. Danh sách đầy đủ các phân tử cần thiết như sau:
molecules ├── available-notion-database ├── full-screen-loader ├── input-control ├── onboarding-step-layout └── onboarding-tab-list
Thật vậy, chỉ với năm phân tử, chúng ta có đủ thành phần để hoàn thành mục tiêu của mình. Điều quan trọng cần lưu ý rằng đây cũng là một nơi lý tưởng để bao gồm các bố cục được chia sẻ được xây dựng trên các nguyên tử khác. Chẳng hạn, onboarding-step-layout
được sử dụng để duy trì giao diện nhất quán trong suốt cả năm bước của quy trình giới thiệu.
Các thành phần khác như sau:
cơ sở dữ liệu có sẵn : Được sử dụng để hiển thị chi tiết cơ sở dữ liệu của người dùng đã tìm nạp (người dùng có thể có nhiều cơ sở dữ liệu, vì vậy tôi cung cấp khả năng chọn một cơ sở dữ liệu ở bước 4).
Thành phần xuất hiện trên giao diện người dùng như thế này:
import { FC } from 'react'; import { Flex, Spinner } from '@presentation/atoms'; import { FullScreenLoaderProps } from './full-screen-loader.types'; export const FullScreenLoader: FC<FullScreenLoaderProps> = ({ children, ...restProps }): JSX.Element => ( <Flex alignItems="center" bg="gray.50" height="full" justifyContent="center" left={0} position="fixed" top={0} width="full" zIndex="9999" {...restProps} > <Spinner /> {children} </Flex> );
Không có khoa học tên lửa ở đây. Đây chỉ là sự kết hợp của các nguyên tử flex
và spinner
đã được xác định.
input
có form-label
, form-control
, form-error-label
và spinner
để hiển thị liệu có một số hành động nền đang xảy ra hay không. Thành phần xuất hiện trên giao diện người dùng như thế này:
Bây giờ khi đã có nhiều mảnh hơn, chúng ta có thể chuyển sang xác định các khối lớn hơn trong câu đố thiết kế của mình.
Phần này là nơi tôi tạo từng thành phần chịu trách nhiệm hiển thị từng bước của quy trình giới thiệu.
Để làm rõ mọi thứ, tôi sẽ chỉ cho bạn xem danh sách các sinh vật được tạo ra:
organisms ├── onboarding-step-one ├── onboarding-step-two ├── onboarding-step-three ├── onboarding-step-four └── onboarding-step-five
Tôi tin rằng những cái tên là tự giải thích, và không nên có sự hiểu lầm. Để minh họa cách tôi đặt mọi thứ lại với nhau, tôi sẽ trình bày mã của một bước làm ví dụ. Tất nhiên, nếu bạn muốn kiểm tra thêm, chỉ cần truy cập kho lưu trữ của tôi .
export const OnboardingStepFour: FC<OnboardingStepFourProps> = ({ onBackButtonClick, onNextButtonClick, }): JSX.Element => { const { hasApiTokenData, isSetApiTokenLoading, setApiToken, setApiTokenError } = useSetApiToken(); const handleInputChange = debounce(async (event: ChangeEvent<HTMLInputElement>) => { const result = await setApiToken(event.target.value); if (result) { onNextButtonClick(); } }, 1000); return ( <OnboardingStepLayout subtitle="Paste your copied integration token below to validate your integration." title="Validate your integration" onBackButtonClick={onBackButtonClick} > <InputControl isRequired errorMessage={setApiTokenError || undefined} isDisabled={isSetApiTokenLoading || hasApiTokenData} isLoading={isSetApiTokenLoading} label="Integration token" name="integrationToken" placeholder="Your integration token" onChange={handleInputChange} /> </OnboardingStepLayout> ); };
Mã này hoàn toàn chịu trách nhiệm hiển thị bước bốn trong quy trình giới thiệu của tôi. Tôi tin rằng mối quan tâm duy nhất mà bạn có thể có là về việc đưa ra yêu cầu trong các sinh vật. điều này có chấp nhận được không? Không có câu trả lời chung cho tất cả và tôi cần trả lời những lo ngại này bằng câu "Điều đó còn tùy". Nó phụ thuộc vào cấu trúc của bạn.
Nếu việc bao gồm một lệnh gọi API trong một phân tử hoặc sinh vật có ý nghĩa trong ngữ cảnh ứng dụng của bạn và không làm phức tạp quá mức thành phần, thì đó có thể là một giải pháp có thể chấp nhận được. Chỉ cần thận trọng để không để các thành phần bản trình bày kết hợp quá chặt chẽ với tính năng tìm nạp dữ liệu hoặc logic kinh doanh, vì điều đó có thể khiến việc bảo trì và kiểm tra chúng trở nên khó khăn hơn.
Trong trường hợp của tôi, thành phần này được sử dụng ở một nơi và các giải pháp khác để thực hiện lệnh gọi API trong trường hợp đó phức tạp hơn và có thể tạo ra nhiều mã hơn mức cần thiết.
Ở giai đoạn này, trọng tâm là cấu trúc và sự sắp xếp của các thành phần hơn là các chi tiết tốt hơn của giao diện người dùng. Các mẫu cũng giúp xác định vị trí quản lý trạng thái nên nằm ở đâu, thường nằm trong các thành phần trang sử dụng các mẫu.
Trong ví dụ mã được cung cấp, chúng tôi có một thành phần Onboarding
đóng vai trò là mẫu:
import { FC } from 'react'; import { Flex, Heading, TabPanels, Tabs, Text } from '@presentation/atoms'; import { OnboardingTabList } from '@presentation/molecules'; import { OnboardingStepFive, OnboardingStepFour, OnboardingStepOne, OnboardingStepThree, OnboardingStepTwo, } from '@presentation/organisms'; import { OnboardingProps } from './onboarding.types'; export const Onboarding: FC<OnboardingProps> = ({ activeTabs, createNotionIntegrationTabRef, displayCreateNotionIntegrationTab, displaySelectNotionDatabaseTab, displayShareDatabaseIntegrationTab, displayValidateIntegrationTab, displayVerifyDatabaseTab, selectNotionDatabaseTabRef, shareDatabaseIntegrationTabRef, validateIntegrationTabRef, verifyDatabaseTabRef, }) => ( <Flex direction="column" overflowX="hidden" px={2} py={{ base: '20px', sm: '25px', md: '55px' }}> <Flex direction="column" textAlign="center"> <Heading color="gray.700" fontSize={{ base: 'xl', sm: '2xl', md: '3xl', lg: '4xl' }} fontWeight="bold" mb="8px" > Configure your Notion integration </Heading> <Text withBalancer color="gray.400" fontWeight="normal"> This information will let us know from which Notion database we should use to get your vocabulary. </Text> </Flex> <Tabs isLazy display="flex" flexDirection="column" mt={{ base: '10px', sm: '25px', md: '35px' }} variant="unstyled" > <OnboardingTabList activeTabs={activeTabs} createNotionIntegrationTabRef={createNotionIntegrationTabRef} selectNotionDatabaseTabRef={selectNotionDatabaseTabRef} shareDatabaseIntegrationTabRef={shareDatabaseIntegrationTabRef} validateIntegrationTabRef={validateIntegrationTabRef} verifyDatabaseTabRef={verifyDatabaseTabRef} /> <TabPanels maxW={{ md: '90%', lg: '100%' }} mt={{ base: '10px', md: '24px' }} mx="auto"> <OnboardingStepOne onNextButtonClick={displayCreateNotionIntegrationTab} /> <OnboardingStepTwo onBackButtonClick={displayVerifyDatabaseTab} onNextButtonClick={displayShareDatabaseIntegrationTab} /> <OnboardingStepThree onBackButtonClick={displayCreateNotionIntegrationTab} onNextButtonClick={displayValidateIntegrationTab} /> {activeTabs.validateIntegration ? ( <OnboardingStepFour onBackButtonClick={displayShareDatabaseIntegrationTab} onNextButtonClick={displaySelectNotionDatabaseTab} /> ) : null} {activeTabs.selectNotionDatabase ? ( <OnboardingStepFive onBackButtonClick={displayVerifyDatabaseTab} /> ) : null} </TabPanels> </Tabs> </Flex> );
Thành phần Onboarding
này tập hợp các nguyên tử, phân tử và sinh vật để tạo bố cục cho quy trình giới thiệu. Lưu ý rằng logic điều hướng tab và quản lý trạng thái đã được tách ra khỏi thành phần này. Các chức năng gọi lại và trạng thái cần thiết hiện được nhận dưới dạng đạo cụ, cho phép thành phần "trang" cấp cao hơn xử lý việc quản lý dữ liệu và trạng thái.
Sự tách biệt các mối quan tâm này giữ cho mẫu tập trung vào bố cục và cấu trúc trong khi vẫn đảm bảo rằng việc quản lý trạng thái được xử lý ở cấp độ phù hợp.
Cuối cùng, tôi xin trình bày bước 4 như một kết quả cuối cùng:
Trong bối cảnh thảo luận trước đây của chúng ta, thành phần "trang" sử dụng mẫu Onboarding
và xử lý việc quản lý trạng thái cho quy trình giới thiệu. Mặc dù mã cho thành phần trang cụ thể này không được cung cấp ở đây, nhưng bạn có thể tìm thấy nó trong kho lưu trữ của tôi. Như đã đề cập, không có gì bất thường về mã của thành phần trang; nó chủ yếu tập trung vào việc quản lý trạng thái và chuyển nó xuống mẫu Onboarding
.
Nếu chúng ta xem thiết kế nguyên tử trông như thế nào trong thực tế. Hãy đi sâu vào những ưu và nhược điểm của phương pháp này.
Mặc dù thiết kế nguyên tử mang lại nhiều lợi ích rõ ràng, chẳng hạn như tính mô-đun, khả năng tái sử dụng và khả năng bảo trì, nhưng nó cũng có một vài nhược điểm đáng để xem xét ngay từ đầu:
thiết lập ban đầu và độ phức tạp : thiết kế nguyên tử yêu cầu cấu trúc và tổ chức được lên kế hoạch tốt, việc thiết lập ban đầu có thể tốn thời gian và khó khăn. Nó cũng có thể tạo thêm độ phức tạp cho cơ sở mã của bạn, đặc biệt là đối với các dự án nhỏ hơn khi cách tiếp cận chi tiết như vậy có thể không cần thiết.
đường cong học tập : đối với các nhà phát triển mới làm quen với thiết kế nguyên tử, phương pháp này có thể có đường cong học tập dốc. Nó đòi hỏi sự hiểu biết vững chắc về các cấp độ khác nhau và cách chúng khớp với nhau, điều này có thể gây choáng ngợp cho người mới bắt đầu.
chi phí chung : việc triển khai thiết kế nguyên tử có thể liên quan đến việc tạo ra một số lượng lớn các thành phần nhỏ, chuyên biệt. Điều này có thể dẫn đến tăng chi phí trong việc quản lý và bảo trì các thành phần này, đặc biệt khi một thành phần chỉ được sử dụng trong một ngữ cảnh cụ thể.
rủi ro kỹ thuật quá mức : với việc tập trung vào việc tạo các thành phần mô-đun và có thể tái sử dụng, có nguy cơ kỹ thuật quá mức tiềm ẩn, trong đó các nhà phát triển có thể dành quá nhiều thời gian để tinh chỉnh các thành phần riêng lẻ thay vì tập trung vào ứng dụng rộng hơn.
giao tiếp và cộng tác : sự thành công của thiết kế nguyên tử phụ thuộc vào sự giao tiếp và hợp tác rõ ràng giữa các nhà thiết kế, nhà phát triển và các bên liên quan khác. Việc không thiết lập được một ngôn ngữ chung hoặc sự hiểu biết về phương pháp luận có thể dẫn đến nhầm lẫn và không nhất quán trong quá trình thực hiện.
Tuy nhiên, cách tiếp cận này có những điểm mạnh đã được đề cập của riêng nó. Hãy nói về chúng chi tiết hơn:
khả năng mở rộng : bằng cách phân tách thiết kế thành các yếu tố cơ bản nhất, việc xây dựng độ phức tạp của các thành phần trở thành một nhiệm vụ dễ quản lý hơn. Mặc dù việc chế tạo các nguyên tử đặt ra một số thách thức, nhưng việc tạo ra bất kỳ thành phần nào dựa trên các nguyên tử này đều cực kỳ thú vị.
hiệu quả : khả năng tái sử dụng các nguyên tử, phân tử và sinh vật cắt giảm đáng kể thời gian dành cho việc thiết kế và phát triển các tính năng mới. Khi các thành phần cơ sở được thiết lập, việc tạo giao diện mới có thể đơn giản như việc kết hợp các phần tử hiện có.
nhất quán : xuất phát trực tiếp từ điểm trước đó. Do các nguyên tử, phân tử và sinh vật giống nhau được sử dụng trên nhiều mẫu và trang nên giao diện người dùng vẫn thống nhất, mang lại trải nghiệm liền mạch cho người dùng.
tài liệu : thiết kế nguyên tử vốn đã hỗ trợ tài liệu. Cấu trúc dựa trên nguyên tử có thể phục vụ như một hướng dẫn trực quan, rõ ràng về cách xây dựng và sử dụng các thành phần. Điều này có thể đặc biệt hữu ích cho việc giới thiệu các thành viên mới trong nhóm.
khả năng bảo trì : một trong những thế mạnh lớn nhất của thiết kế nguyên tử là cách nó góp phần vào khả năng bảo trì của một hệ thống thiết kế. Bằng cách chia nhỏ mọi thứ thành các phần nguyên tử của nó, bất kỳ thay đổi hoặc cập nhật nào cũng có thể được thực hiện ở cấp độ nguyên tử và sau đó được truyền qua hệ thống. Ví dụ: nếu bạn quyết định thay đổi màu của một nút, bạn chỉ cần thực hiện thay đổi này một lần ở cấp độ nguyên tử và nó sẽ được phản ánh trên tất cả các phân tử, sinh vật và mẫu mà nút này được sử dụng. Điều này đơn giản hóa đáng kể quá trình cập nhật và duy trì hệ thống thiết kế theo thời gian.
Tóm lại, mặc dù thiết kế nguyên tử có vẻ giống như con dao hai lưỡi - hơi khó khăn về thiết lập ban đầu và đường cong học tập - nhưng lợi ích tiềm năng của nó rất đáng để nỗ lực ban đầu. Và hãy nhớ rằng, ngay cả những thanh kiếm ghê gớm nhất cũng vô hại trong tay của một hiệp sĩ lành nghề!