Frontend Development 19 min read

Understanding Frontend Componentization: Principles, Practices, and Challenges

This article systematically explores frontend componentization, covering its motivations, definitions, design principles, component types, implementation strategies, code examples, and the challenges faced during adoption, aiming to help developers achieve higher cohesion, lower coupling, and improved maintainability in web projects.

JD Tech
JD Tech
JD Tech
Understanding Frontend Componentization: Principles, Practices, and Challenges

Componentization has permeated all aspects of frontend development. This article explains why componentization is needed, what it means, and how to implement it, helping readers gain a deeper understanding of its purpose, requirements, and benefits.

Why implement frontend componentization? In large projects, splitting pages and features into many files leads to duplicated code, redundant implementations, and increasing code size, complexity, and maintenance difficulty. Componentization addresses these issues by promoting reuse, reducing coupling, and improving scalability.

What is frontend componentization? It is a top‑down decomposition of a project into reusable, encapsulated UI components that expose configurable props and functions, achieving high cohesion and low coupling. Components can be composed to form more complex components, blocks, and full pages.

Componentization vs. modularization – modularization splits code at the file level, while componentization splits at the UI design level, focusing on reusable visual elements.

Component classification includes four types: basic components (e.g., button, input), business components (built from basic components with business logic), blocks (combinations of basic and business components, modifiable), and pages (the final UI presented to users).

Characteristics of componentization emphasize high internal cohesion and low external coupling, aiming for configurable, composable, and maintainable UI modules.

Design principles for components are:

Single responsibility – each component does one thing well.

Configurable – inputs and outputs are clearly defined via props.

Appropriate granularity – balance between too fine and too coarse to avoid maintenance overhead.

Implementation example – a Vue‑based el-table component with selectable rows, operation columns, slots, and dynamic rendering. The full code snippet is shown below:

<el-table
ref="multipleTable"
v-loading="loading"
:stripe="ifStripe"
:border="border"
:height="`calc(100% - ${pageHeight}px)`"
:max-height="maxHeight"
:style="{ width: '100%' }"
:data="dataSource"
v-bind="options"
v-on="tableEvents"
@selection-change="handleSelectionChange"
    >
      <!-- 复选框 -->
      <el-table-column
        v-if="options && options.selection && (!options.isShow || options.isShow())"
        type="selection"
        width="55"
        :align="alignDirestion"
      ></el-table-column>
      <el-table-column
        v-if="operates && operates.length > 0"
        :fixed="operatesDir"
        label="操作"
        width="200"
        v-bind="options && options.props"
        :align="alignDirestion"
      >
        <template slot-scope="scope">
          <div class="operate-group">
            <template v-for="(btn, key) in operatesOut">
              <span v-if="!btn.isShow || (btn.isShow && btn.isShow(scope.row, scope.$index))" :key="key">
                <template v-if="btn.render">
                  <render :column="scope.row" :row="scope.row" :render="btn.render" :index="scope.$index"></render>
                </template>
                <template v-else>
                  <el-button
                    :size="btn.size || 'small'"
                    :type="btn.type || `text`"
                    :icon="btn.icon"
                    :plain="btn.plain"
                    :style="btn.setStyle && btn.setStyle(scope.row, scope.$index)"
                    v-bind="btn.props"
                    :disabled="btn.disabled && btn.disabled(scope.row, scope.$index)"
                    @click.native.prevent="btn.method(scope.row, scope.$index)"
                  >{{ typeof btn.label === 'string' ? btn.label : btn.label(scope.row) }}{{ operates.length >= 2 ? '  ' : '' }}</el-button>
                </template>
              </span>
            </template>
            <template>
              <span v-if="operatesInner && operatesInner.length > 0">
                <el-dropdown class="dropdown-btn">
                  <span class="el-dropdown-link">更多<i class="el-icon-arrow-down el-icon--right"></i></span>
                  <el-dropdown-menu>
                    <el-dropdown-item
                      :disabled="btn.disabled && btn.disabled(scope.row, scope.$index)"
                      v-for="(btn, key) in operatesInner"
                      :key="key"
                      @click.native.prevent="btn.method(scope.row, scope.$index)"
                    >{{ btn.label }}</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </span>
            </template>
          </div>
        </template>
      </el-table-column>
<template v-for="(column, index) in columns">
<slot v-if="column.slot" :name="column.slot"></slot>
<template v-else>
<el-table-column
            v-if="!column.isShow || (column.isShow && column.isShow())"
            :key="index"
            v-bind="column.props"
            :prop="column.prop"
            :label="column.label"
            :width="column.width"
            :fixed="column.fixed"
            show-overflow-tooltip
            :align="alignDirestion"
          >
<template slot-scope="scope">
<template v-if="!column.render">
<template v-if="column.formatter">
<span v-if="!column.time" @click="column.click && column.click(scope.row, scope.$index)">{{ column.formatter(scope.row, column, scope.$index) }}</span>
<span v-else>{{ column.formatter(scope.row, column, scope.$index) | dateFormat }}</span>
</template>
<template v-else-if="column.newjump">
<router-link class="newjump" type="primary" :underline="false" v-bind="{ target: '_blank', ...column.target }" :to="column.newjump(scope.row, column, scope.$index)">{{ scope.row[column.prop] || column.content }}</router-link>
</template>
<template v-else>
<span :style="column.click ? 'color: #409EFF; cursor: pointer;' : null" @click="column.click && column.click(scope.row, scope.$index)">{{ scope.row[column.prop] || column.content ? scope.row[column.prop] || column.content : '-' }}{{ `${scope.row[column.prop] && column.unit ? column.unit : ''}` }}</span>
</template>
</template>
<template v-else>
<render :column="column" :row="scope.row" :render="column.render" :index="index"></render>
</template>
</template>
</el-table-column>
</template>
</template>
<el-table-column v-if="options && options.slotcontent" label="操作" :align="alignDirestion">
<template slot-scope="scope">
<slot :data="scope"></slot>
</template>
</el-table-column>
</el-table>

Key challenges of componentization include handling vague or evolving requirements, balancing reusability with specificity, managing component growth, and establishing consistent development standards to ensure robustness and maintainability.

In conclusion, componentization is a fundamental practice in modern frontend frameworks such as Vue and React. By continuously refining component libraries, considering performance, and adhering to best‑practice guidelines, teams can achieve scalable, maintainable, and efficient web applications.

frontendbest practicescomponentizationWeb Developmentui-components
JD Tech
Written by

JD Tech

Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.