目标:利用composition api 实现动态修改gdp数据
compositon api 好处:
- 遵循单一职责原则:将相关逻辑组织到单个自定义函数中,便于不同组件的共用,复用性强。
- 利用ref、reactive实现响应式数据,以便于数据跟踪
- 使用computed实现可读的ref,多个响应式数据组合一个新的响应式数据,用于动态计算。
- 利用生命周期钩子函数使用个性化需求
实现步骤
- 使用fetch获取数据,将数据转换为响应式数据,并传递给组件
<!--父组件-->
<template>
<div class="container">
<Bar1 :Gdp="Gdp"></Bar1>
<Bar2 :Gdp="Gdp"></Bar2>
</div>
<div class="controls">
<div class="item" v-for="(item,index) in Gdp" :key="index">
<label>{{ item.country }}</label>
<input type="number" step="0.001" v-model="item.value" min="0" />
</div>
</div>
</template>
<script>
import {ref} from 'vue'
import Bar1 from './components/Bar1.vue'
import Bar2 from './components/Bar2.vue'
export default {
components: {
Bar1,
Bar2
},
setup() {
const gdpRef = ref([]);
async function getGdp() {
return fetch('/api/gdp.json').then((res)=> res.json())
}
getGdp().then((data) => {
gdpRef.value =data
})
return {
Gdp:gdpRef
}
},
};
</script>
<style scoped>
.container {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.controls {
margin: 1em;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.item {
margin: 1em;
}
.item label {
margin: 0 1em;
}
.item input {
height: 26px;
font-size: 14px;
}
h1 {
text-align: center;
}
</style>
- 组件1接收数据,由于不能直接将数据渲染到页面,需要将数据进行二次过滤,利用composition的特性,新建useGdp.js(将逻辑代码全部组织到该文件中)便于后期维护及拓展
// hooks函数
import { computed, ref, watch } from "vue";
import gsap from "gsap";
const COLORS = ["#334552", "#B34335", "#6E9FA5", "#A2C3AC", "#C8846C"];
export function useGdp(Gdp, maxValue) {
const maxGdp = computed(() => {
return Math.max(...Gdp.value.map((item) => item.value));
});
// 当一个或多个响应式需要得到一个新的响应式数据(只读),需要使用computed
const gdps = computed(() => {
return Gdp.value.map((item, index) => {
return {
...item,
color: COLORS[index % Gdp.value.length],
size: (item.value / maxGdp.value) * maxValue,
};
});
});
const newGdp = ref([]);
watch(
gdps,
(newValue) => {
// 实现过渡动画
for (let i = 0; i < newValue.length; i++) {
newGdp.value[i] = {
...newValue[i],
size: 0,
value: 0,
};
gsap.to(newGdp.value[i], {
duration: 1,
...newValue[i],
});
}
},
{
deep: true,
}
);
return {
newGdp,
};
}
- useGdp接收组件传的参数(小问题)需要将参数转换为ref,直接传递props.Gdp无效,需要使用computed包裹
<template>
<div class="bar1">
<div class="item" v-for="(item,index) in newGdp" :key="index">
<label>{{ item.country }}</label>
<div class="bar" :style="{background: item.color, width: item.size + 'px'}"></div>
<div class="value">{{item.value}}万亿</div>
</div>
</div>
</template>
<script>
import { computed, watch } from 'vue';
import {useGdp} from './../hooks/useGdp'
export default {
props: ['Gdp'],
setup (props) {
const GdpRef = computed(()=>{
return props.Gdp
})
return {
...useGdp(GdpRef,400)
}
}
}
</script>
<style scoped>
.bar1 {
width: 500px;
box-sizing: border-box;
margin: 3em;
border-left: 1px solid #333;
}
.item {
display: flex;
height: 35px;
line-height: 35px;
margin: 1em 0;
position: relative;
}
.bar {
width: 100px;
height: 100%;
margin-right: 1em;
flex: 0 0 auto;
}
.item label {
position: absolute;
left: -50px;
}
.value {
flex: 0 0 auto;
}
</style>
- useGdp监听数据的变化,对数据进行处理,并返回最终的响应式数据。
- 组件解构最终的响应式数据
return {
...useGdp(GdpRef,400)
}
在实际开发中确保返回给页面的参数全部是ref,这样就可以保证统一,维护起来更便捷