Development of custom components
The Necessity of Custom Components
In uni-app development, when built-in components cannot meet all business scenarios, custom components become a key means of extending functionality. Encapsulating reusable UI modules can significantly improve development efficiency, such as product cards in e-commerce projects or comment components in social platforms. Through component-based development, a high-cohesion and low-coupling code structure can be achieved.
Basic Steps for Creating Components
Creating custom components in uni-app requires following a specific directory structure. It is generally recommended to create a components
folder in the project root directory to store all custom components. Each component should have its own folder, for example, creating a my-button
component:
├── components
│ └── my-button
│ ├── my-button.vue
│ └── my-button.scss
Basic structure example of a component file:
<template>
<view class="my-button" @click="handleClick">
<slot></slot>
</view>
</template>
<script>
export default {
name: 'my-button',
props: {
type: {
type: String,
default: 'default'
}
},
methods: {
handleClick() {
this.$emit('click')
}
}
}
</script>
<style scoped>
.my-button {
padding: 12px 24px;
border-radius: 4px;
display: inline-block;
}
</style>
Component Communication Mechanism
Parent-child component communication is primarily achieved through props
and events
. props
are used for passing data from parent to child, while events
are used for sending messages from child to parent. Below is an example of an enhanced input component:
<!-- Parent Component -->
<template>
<enhanced-input
v-model="inputValue"
@search="handleSearch"
/>
</template>
<script>
export default {
data() {
return {
inputValue: ''
}
},
methods: {
handleSearch(value) {
console.log('Search content:', value)
}
}
}
</script>
<!-- Child Component enhanced-input.vue -->
<template>
<view class="input-wrapper">
<input
:value="value"
@input="$emit('input', $event.target.value)"
@keyup.enter="$emit('search', value)"
/>
<button @click="$emit('search', value)">Search</button>
</view>
</template>
<script>
export default {
name: 'enhanced-input',
props: {
value: {
type: String,
default: ''
}
}
}
</script>
Advanced Usage of Slots
Named slots and scoped slots enable more flexible component content distribution. Below is an example of a card component with a title and action area:
<template>
<view class="card">
<view class="card-header">
<slot name="header" :title="title">
<text>{{ title }}</text>
</slot>
</view>
<view class="card-body">
<slot></slot>
</view>
<view class="card-footer">
<slot name="footer" :actions="actions">
<button
v-for="(action, index) in actions"
:key="index"
@click="action.handler"
>
{{ action.text }}
</button>
</slot>
</view>
</view>
</template>
<script>
export default {
props: {
title: String,
actions: Array
}
}
</script>
Component Lifecycle Management
In addition to supporting standard Vue lifecycle hooks, uni-app custom components also have unique mini-program lifecycle hooks. Example of the execution order of important lifecycle hooks:
<script>
export default {
beforeCreate() {
console.log('1. beforeCreate')
},
created() {
console.log('2. created')
},
beforeMount() {
console.log('3. beforeMount')
},
mounted() {
console.log('4. mounted')
},
// Mini-program-specific lifecycle hooks
onLoad() {
console.log('5. onLoad - Mini-program page load')
},
onShow() {
console.log('6. onShow - Mini-program page display')
}
}
</script>
Global Component Registration
Frequently used components can be globally registered to avoid repeated imports. Register globally in main.js
:
import Vue from 'vue'
import MyButton from '@/components/my-button/my-button.vue'
Vue.component('my-button', MyButton)
// Or batch registration
const components = require.context('@/components', true, /\.vue$/)
components.keys().forEach(fileName => {
const componentConfig = components(fileName)
const componentName = fileName.split('/').pop().replace(/\.\w+$/, '')
Vue.component(componentName, componentConfig.default || componentConfig)
})
Component Performance Optimization Strategies
For large component libraries, rendering performance optimization is essential. Below are several practical techniques:
- Conditional Rendering Optimization:
<template>
<view>
<block v-if="heavyCondition">
<!-- Complex content -->
</block>
<block v-else>
<!-- Lightweight content -->
</block>
</view>
</template>
- Event Debouncing:
<script>
import { debounce } from 'lodash-es'
export default {
methods: {
handleInput: debounce(function(value) {
this.$emit('input', value)
}, 300)
}
}
</script>
- Lazy-Loading Image Component:
<template>
<image
:src="show ? realSrc : placeholder"
@load="handleLoad"
@error="handleError"
/>
</template>
<script>
export default {
props: {
src: String,
placeholder: {
type: String,
default: '/static/placeholder.png'
}
},
data() {
return {
show: false,
realSrc: ''
}
},
mounted() {
this.$nextTick(() => {
const observer = uni.createIntersectionObserver(this)
observer.relativeToViewport()
.observe('.lazy-img', (res) => {
if (res.intersectionRatio > 0) {
this.show = true
this.realSrc = this.src
observer.disconnect()
}
})
})
}
}
</script>
Component Theme Customization
Implement a theme system using CSS variables and mixins:
<template>
<view class="theme-container" :style="themeStyle">
<slot></slot>
</view>
</template>
<script>
export default {
props: {
theme: {
type: Object,
default: () => ({
'--primary-color': '#1890ff',
'--text-color': '#333'
})
}
},
computed: {
themeStyle() {
return Object.entries(this.theme)
.map(([key, value]) => `${key}:${value}`)
.join(';')
}
}
}
</script>
<style>
.theme-container {
color: var(--text-color);
}
.theme-container button {
background: var(--primary-color);
}
</style>
Component Unit Testing Practices
Example of configuring Jest for component testing:
// jest.config.js
module.exports = {
preset: '@vue/cli-plugin-unit-jest/presets/no-babel',
moduleFileExtensions: ['js', 'json', 'vue'],
transform: {
'^.+\\.vue$': 'vue-jest'
},
testMatch: ['**/__tests__/**/*.spec.js']
}
// components/__tests__/my-button.spec.js
import { mount } from '@vue/test-utils'
import MyButton from '../my-button.vue'
describe('MyButton', () => {
it('emits click event', async () => {
const wrapper = mount(MyButton, {
slots: {
default: 'Click me'
}
})
await wrapper.trigger('click')
expect(wrapper.emitted().click).toBeTruthy()
})
it('renders default slot content', () => {
const text = 'Custom Text'
const wrapper = mount(MyButton, {
slots: {
default: text
}
})
expect(wrapper.text()).toContain(text)
})
})
Automatic Component Documentation Generation
Use VuePress combined with jsdoc to automatically generate component documentation:
```vue
<!-- docs/.vuepress/components/demo-block.vue -->
<template>
<div class="demo-block">
<slot name="demo"></slot>
<slot name="code"></slot>
</div>
</template>
```
```javascript
// docs/.vuepress/config.js
module.exports = {
plugins: [
[
'vuepress-plugin-demo-code',
{
jsLibs: ['//unpkg.com/vue/dist/vue.min.js'],
cssLibs: ['//unpkg.com/element-ui/lib/theme-chalk/index.css']
}
]
]
}
```
Publishing Components to npm
Key configurations for preparing a component library for publishing:
// package.json
{
"name": "my-uni-components",
"version": "1.0.0",
"main": "dist/my-uni-components.umd.min.js",
"files": [
"dist",
"src"
],
"scripts": {
"build": "vue-cli-service build --target lib --name my-uni-components src/index.js"
}
}
// src/index.js
import MyButton from './components/my-button'
import EnhancedInput from './components/enhanced-input'
const components = {
MyButton,
EnhancedInput
}
const install = function(Vue) {
Object.values(components).forEach(component => {
Vue.component(component.name, component)
})
}
export default {
install,
...components
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:第三方 UI 库的适配
下一篇:主题与样式的动态切换