Ⅰ、机缘
1. 记得是大一、大二的时候就听学校的大牛说,可以通过写 CSDN 博客
,来提升自己的代码和逻辑能力
,虽然即将到了写作的第六
个年头,但感觉这句话依旧受用;
2、今年一整年的创作都没有停止,本年度几乎是每周
都来更新一篇我认为有意义的文章;
3、今年依旧持续稳定的通过文章,来描述前端/后端
学习与工作的过程中遇到的问题,依旧在通过 CSDN
私聊或者评论中来交流和解决相同问题的C友们;
4、当然C友们阅读文章的力量也是很强大的,可以鞭策着我继续创作:
// 访问量前三名
的文章:
5、 实战中的项目经验也有很多:
6 、日常学习过程中的记录
7、通过文章进行技术交流:
其一、经常与C友
关于文章中的技术和问题来谈论解决的方法:
其二、 与 C友
交流文章问题
和提出更好的方法
:
其三、远程帮助 C友
解决 node 与 node-sass 及 sass-loader
等等的问题:
其四、除了评论和私信,也可以通过微信名片
联系:
Ⅱ、收获
当然了,在创作后也会后很多收获:
1、访问量达到了 209W +
,3W+ 名粉丝关注
了我,有 3000+ 个铁粉
;
2、原力达到了 9 级
,获得了 2700+ 个赞
,1200+ 次评论
,5600+ 次收藏
,4W+ 次代码片
分享;
3、当然了,还有 CSDN
的纪念章:
Ⅲ、日常
1、创作已经是我生活的一部分了,感觉不断地创作与分享也是自我成长的一部分,通过创作能加强自己的记忆力,也算是给自己留下的一笔宝藏了;
2、当然,工作是第一位的,遇到问题可以将问题先记录下来,稍后有时间再解决并总结,然后就创作和分享了;
3、当然在此非常感谢一位好兄弟,在他的影响下我才能不断的写作与分享呀:
Ⅳ、成就
我认为写的最好的文章或代码,永远是在下一篇:
但目前记忆尤新的就是要将 element-ui
中的 Transfer
组件二次改造渲染的需求:
即:(Transfer)解决
:Element-ui
中 Transfer
穿梭框因数据量过大而渲染卡顿问题的三种
方法;
A、页面展示为:
// 全选也没有问题,几乎立即可以全选;
// 全选大量数据也可以短时间内,向右侧 transfer
过去;
B、代码为:
其一、transfer-checkbox-item.vue
的代码:
<template>
<el-checkbox
class="el-transfer-panel__item"
:label="source[keyProp]"
:disabled="source[disabledProp]">
<option-content :option="source"></option-content>
</el-checkbox>
</template>
<script>
import ElCheckbox from 'element-ui/packages/checkbox';
export default {
name: 'transfer-checkbox-item',
props: {
index: { // index of current item
type: Number
},
source: { // here is: {uid: 'unique_1', text: 'abc'}
type: Object,
default() {
return {};
}
},
keyProp: {
type: String
},
disabledProp: {
type: String
}
},
components: {
ElCheckbox,
OptionContent: {
props: {
option: Object
},
render(h) {
const getParent = vm => {
if (vm.$options.componentName === 'ElTransferPanel') {
return vm;
} else if (vm.$parent) {
return getParent(vm.$parent);
} else {
return vm;
}
};
const panel = getParent(this);
const transfer = panel.$parent || panel;
return panel.renderContent
? panel.renderContent(h, this.option)
: transfer.$scopedSlots.default
? transfer.$scopedSlots.default({ option: this.option })
: <span>{ this.option[panel.labelProp] || this.option[panel.keyProp] }</span>;
}
}
}
};
</script>
其二、transfer-panel.vue
的代码:
<template>
<div class="el-transfer-panel">
<p class="el-transfer-panel__header">
<el-checkbox
v-model="allChecked"
@change="handleAllCheckedChange"
:indeterminate="isIndeterminate">
{{ title }}
<span>{{ checkedSummary }}</span>
</el-checkbox>
</p>
<div :class="['el-transfer-panel__body', hasFooter ? 'is-with-footer' : '']">
<el-input
class="el-transfer-panel__filter"
v-model="query"
size="small"
:placeholder="placeholder"
@mouseenter.native="inputHover = true"
@mouseleave.native="inputHover = false"
v-if="filterable">
<i slot="prefix"
:class="['el-input__icon', 'el-icon-' + inputIcon]"
@click="clearQuery"
></i>
</el-input>
<!-- <el-checkbox-group
v-model="checked"
v-show="!hasNoMatch && data.length > 0"
:class="{ 'is-filterable': filterable }"
class="el-transfer-panel__list">
<el-checkbox
class="el-transfer-panel__item"
:label="item[keyProp]"
:disabled="item[disabledProp]"
:key="item[keyProp]"
v-for="item in filteredData">
<option-content :option="item"></option-content>
</el-checkbox>
</el-checkbox-group> -->
<el-checkbox-group
v-model="checked"
v-show="!hasNoMatch && data.length > 0"
:class="{ 'is-filterable': filterable }"
class="el-transfer-panel__list">
<virtual-list
v-if="virtualScroll"
style="height:100%;overflow-y: auto;"
:data-key="keyProp"
:data-sources="filteredData"
:data-component="itemComponent"
:extra-props="virtualListProps"
/>
<template v-else>
<el-checkbox
class="el-transfer-panel__item"
:label="item[keyProp]"
:disabled="item[disabledProp]"
:key="item[keyProp]"
v-for="item in filteredData">
<option-content :option="item"></option-content>
</el-checkbox>
</template>
</el-checkbox-group>
<p
class="el-transfer-panel__empty"
v-show="hasNoMatch">{{ t('el.transfer.noMatch') }}</p>
<p
class="el-transfer-panel__empty"
v-show="data.length === 0 && !hasNoMatch">{{ t('el.transfer.noData') }}</p>
</div>
<p class="el-transfer-panel__footer" v-if="hasFooter">
<slot></slot>
</p>
</div>
</template>
<script>
import ElCheckboxGroup from 'element-ui/packages/checkbox-group';
import ElCheckbox from 'element-ui/packages/checkbox';
import ElInput from 'element-ui/packages/input';
import Locale from 'element-ui/src/mixins/locale';
import Item from './transfer-checkbox-item.vue';
import VirtualList from 'vue-virtual-scroll-list';
export default {
mixins: [Locale],
name: 'ElTransferPanel',
componentName: 'ElTransferPanel',
components: {
ElCheckboxGroup,
// 注册VirtualList
'virtual-list': VirtualList,
ElCheckbox,
ElInput,
OptionContent: {
props: {
option: Object
},
render(h) {
const getParent = vm => {
if (vm.$options.componentName === 'ElTransferPanel') {
return vm;
} else if (vm.$parent) {
return getParent(vm.$parent);
} else {
return vm;
}
};
const panel = getParent(this);
const transfer = panel.$parent || panel;
return panel.renderContent
? panel.renderContent(h, this.option)
: transfer.$scopedSlots.default
? transfer.$scopedSlots.default({ option: this.option })
: <span>{ this.option[panel.labelProp] || this.option[panel.keyProp] }</span>;
}
}
},
props: {
data: {
type: Array,
default() {
return [];
}
},
renderContent: Function,
placeholder: String,
title: String,
filterable: Boolean,
format: Object,
filterMethod: Function,
defaultChecked: Array,
props: Object
},
data() {
return {
checked: [],
allChecked: false,
query: '',
inputHover: false,
checkChangeByUser: true,
itemComponent: Item,
virtualListProps: {}
};
},
watch: {
checked1(val, oldVal) {
this.updateAllChecked();
if (this.checkChangeByUser) {
const movedKeys = val.concat(oldVal)
.filter(v => val.indexOf(v) === -1 || oldVal.indexOf(v) === -1);
this.$emit('checked-change', val, movedKeys);
} else {
this.$emit('checked-change', val);
this.checkChangeByUser = true;
}
},
checked(val, oldVal) {
this.updateAllChecked();
let newObj = {};
val.every((item)=>{
newObj[item] = true;
});
let oldObj = {};
oldVal.every((item)=>{
oldObj[item] = true;
});
if (this.checkChangeByUser) {
// O(n)
const movedKeys = val.concat(oldVal)
.filter(v => newObj[v] || oldVal[v]);
this.$emit('checked-change', val, movedKeys);
} else {
this.$emit('checked-change', val);
this.checkChangeByUser = true;
}
},
data() {
const checked = [];
const filteredDataKeys = this.filteredData.map(item => item[this.keyProp]);
this.checked.forEach(item => {
if (filteredDataKeys.indexOf(item) > -1) {
checked.push(item);
}
});
this.checkChangeByUser = false;
this.checked = checked;
},
checkableData() {
this.updateAllChecked();
},
defaultChecked: {
immediate: true,
handler(val, oldVal) {
if (oldVal && val.length === oldVal.length &&
val.every(item => oldVal.indexOf(item) > -1)) return;
const checked = [];
const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);
val.forEach(item => {
if (checkableDataKeys.indexOf(item) > -1) {
checked.push(item);
}
});
this.checkChangeByUser = false;
this.checked = checked;
}
}
},
computed: {
filteredData() {
return this.data.filter(item => {
if (typeof this.filterMethod === 'function') {
return this.filterMethod(this.query, item);
} else {
const label = item[this.labelProp] || item[this.keyProp].toString();
return label.toLowerCase().indexOf(this.query.toLowerCase()) > -1;
}
});
},
virtualScroll() {
return this.$parent.virtualScroll;
},
checkableData() {
return this.filteredData.filter(item => !item[this.disabledProp]);
},
checkedSummary() {
const checkedLength = this.checked.length;
const dataLength = this.data.length;
const { noChecked, hasChecked } = this.format;
if (noChecked && hasChecked) {
return checkedLength > 0
? hasChecked.replace(/\${checked}/g, checkedLength).replace(/\${total}/g, dataLength)
: noChecked.replace(/\${total}/g, dataLength);
} else {
return `${ checkedLength }/${ dataLength }`;
}
},
isIndeterminate() {
const checkedLength = this.checked.length;
return checkedLength > 0 && checkedLength < this.checkableData.length;
},
hasNoMatch() {
return this.query.length > 0 && this.filteredData.length === 0;
},
inputIcon() {
return this.query.length > 0 && this.inputHover
? 'circle-close'
: 'search';
},
labelProp() {
return this.props.label || 'label';
},
keyProp1() {
return this.props.key || 'key';
},
keyProp() {
this.virtualListProps.keyProp = this.props.key || 'key';
return this.props.key || 'key';
},
disabledProp1() {
return this.props.disabled || 'disabled';
},
disabledProp() {
this.virtualListProps.disabledProp = this.props.disabled || 'disabled';
return this.props.disabled || 'disabled';
},
hasFooter() {
return !!this.$slots.default;
}
},
methods: {
updateAllChecked1() {
const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);
this.allChecked = checkableDataKeys.length > 0 &&
checkableDataKeys.every(item => this.checked.indexOf(item) > -1);
},
updateAllChecked() {
let checkObj = {};
this.checked.forEach((item) => {
checkObj[item] = true;
});
this.allChecked =
this.checkableData.length > 0 &&
this.checked.length > 0 &&
this.checkableData.every((item) => checkObj[item[this.keyProp]]);
},
handleAllCheckedChange(value) {
this.checked = value
? this.checkableData.map(item => item[this.keyProp])
: [];
},
clearQuery() {
if (this.inputIcon === 'circle-close') {
this.query = '';
}
}
}
};
</script>
其三、main.vue
的代码:
<template>
<div class="el-transfer">
<transfer-panel
v-bind="$props"
ref="leftPanel"
:data="sourceData"
:title="titles[0] || t('el.transfer.titles.0')"
:default-checked="leftDefaultChecked"
:placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
@checked-change="onSourceCheckedChange">
<slot name="left-footer"></slot>
</transfer-panel>
<div class="el-transfer__buttons">
<el-button
type="primary"
:class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
@click.native="addToLeft"
:disabled="rightChecked.length === 0">
<i class="el-icon-arrow-left"></i>
<span v-if="buttonTexts[0] !== undefined">{{ buttonTexts[0] }}</span>
</el-button>
<el-button
type="primary"
:class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
@click.native="addToRight"
:disabled="leftChecked.length === 0">
<span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
<i class="el-icon-arrow-right"></i>
</el-button>
</div>
<transfer-panel
v-bind="$props"
ref="rightPanel"
:data="targetData"
:title="titles[1] || t('el.transfer.titles.1')"
:default-checked="rightDefaultChecked"
:placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
@checked-change="onTargetCheckedChange">
<slot name="right-footer"></slot>
</transfer-panel>
</div>
</template>
<script>
import ElButton from 'element-ui/packages/button';
import Emitter from 'element-ui/src/mixins/emitter';
import Locale from 'element-ui/src/mixins/locale';
import TransferPanel from './transfer-panel.vue';
import Migrating from 'element-ui/src/mixins/migrating';
export default {
name: 'ElTransfer',
mixins: [Emitter, Locale, Migrating],
components: {
TransferPanel,
ElButton
},
props: {
data: {
type: Array,
default() {
return [];
}
},
virtualScroll: {
type: Boolean,
default: false
},
titles: {
type: Array,
default() {
return [];
}
},
buttonTexts: {
type: Array,
default() {
return [];
}
},
filterPlaceholder: {
type: String,
default: ''
},
filterMethod: Function,
leftDefaultChecked: {
type: Array,
default() {
return [];
}
},
rightDefaultChecked: {
type: Array,
default() {
return [];
}
},
renderContent: Function,
value: {
type: Array,
default() {
return [];
}
},
format: {
type: Object,
default() {
return {};
}
},
filterable: Boolean,
props: {
type: Object,
default() {
return {
label: 'label',
key: 'key',
disabled: 'disabled'
};
}
},
targetOrder: {
type: String,
default: 'original'
}
},
data() {
return {
leftChecked: [],
rightChecked: []
};
},
computed: {
dataObj() {
const key = this.props.key;
return this.data.reduce((o, cur) => (o[cur[key]] = cur) && o, {});
},
sourceData1() {
return this.data.filter(item => this.value.indexOf(item[this.props.key]) === -1);
},
sourceData() {
let valueObj = {};
this.value.forEach((item)=>{
valueObj[item] = true;
});
return this.data.filter(
(item) => !valueObj[item[this.props.key]]
);
},
targetData1() {
if (this.targetOrder === 'original') {
return this.data.filter(item => this.value.indexOf(item[this.props.key]) > -1);
} else {
return this.value.reduce((arr, cur) => {
const val = this.dataObj[cur];
if (val) {
arr.push(val);
}
return arr;
}, []);
}
},
targetData() {
if (this.targetOrder === 'original') {
let valueObj = {};
this.value.forEach((item)=>{
valueObj[item] = true;
});
let data = this.data.filter(
(item) => valueObj[item[this.props.key]]
);
return data;
} else {
return this.value.reduce((arr, cur) => {
const val = this.dataObj[cur];
if (val) {
arr.push(val);
}
return arr;
}, []);
}
},
hasButtonTexts() {
return this.buttonTexts.length === 2;
}
},
watch: {
value(val) {
this.dispatch('ElFormItem', 'el.form.change', val);
}
},
methods: {
getMigratingConfig() {
return {
props: {
'footer-format': 'footer-format is renamed to format.'
}
};
},
onSourceCheckedChange(val, movedKeys) {
this.leftChecked = val;
if (movedKeys === undefined) return;
this.$emit('left-check-change', val, movedKeys);
},
onTargetCheckedChange(val, movedKeys) {
this.rightChecked = val;
if (movedKeys === undefined) return;
this.$emit('right-check-change', val, movedKeys);
},
addToLeft() {
let currentValue = this.value.slice();
this.rightChecked.forEach(item => {
const index = currentValue.indexOf(item);
if (index > -1) {
currentValue.splice(index, 1);
}
});
this.$emit('input', currentValue);
this.$emit('change', currentValue, 'left', this.rightChecked);
},
addToRight1() {
let currentValue = this.value.slice();
const itemsToBeMoved = [];
const key = this.props.key;
this.data.forEach(item => {
const itemKey = item[key];
if (
this.leftChecked.indexOf(itemKey) > -1 &&
this.value.indexOf(itemKey) === -1
) {
itemsToBeMoved.push(itemKey);
}
});
currentValue = this.targetOrder === 'unshift'
? itemsToBeMoved.concat(currentValue)
: currentValue.concat(itemsToBeMoved);
this.$emit('input', currentValue);
this.$emit('change', currentValue, 'right', this.leftChecked);
},
addToRight() {
let currentValue = this.value.slice();
const itemsToBeMoved = [];
const key = this.props.key;
let leftCheckedKeyPropsObj = {};
this.leftChecked.forEach((item) => {
leftCheckedKeyPropsObj[item] = true;
});
let valueKeyPropsObj = {};
this.value.forEach((item) => {
valueKeyPropsObj[item] = true;
});
this.data.forEach((item) => {
const itemKey = item[key];
// O(n)
if (
leftCheckedKeyPropsObj[itemKey] &&
!valueKeyPropsObj[itemKey]) {
itemsToBeMoved.push(itemKey);
}
});
currentValue = this.targetOrder === 'unshift'
? itemsToBeMoved.concat(currentValue)
: currentValue.concat(itemsToBeMoved);
this.$emit('input', currentValue);
this.$emit('change', currentValue, 'right', this.leftChecked);
},
clearQuery(which) {
if (which === 'left') {
this.$refs.leftPanel.query = '';
} else if (which === 'right') {
this.$refs.rightPanel.query = '';
}
}
}
};
</script>
其四、App.vue
的代码:
<template>
<div class="greetings" id="app">
<newTransfer v-model="value" :data="data" :virtual-scroll="true"></newTransfer>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import newTransfer from './transfer/index.js'
const generateData = () => {
const data = []
for (let i = 1; i <= 149998; i++) {
data.push({
key: i,
label: `Option ${i}`,
disabled: i % 4 === 0,
})
}
return data
}
const data = ref(generateData())
const value = ref([])
</script>
Ⅴ、憧憬
1、职业规划:
大方向不变:在以前端为背景下,不断的学习新的技术,争取做前端领域的大牛,当然在有精力的情况下,想做全栈技术开发,想学一门后端语言,暂定为 java
;
2、创作规划:
没有什么大的规划,就是有时间就写,整理问题、代码、遇到问题的解决方案,当然也想做出自己的VIP
专栏;
3、还请多多关注博主和我的专栏:
点击进入我的 CSDN 主页
点击进入我的前端VIP专栏
点击进入我的后端(java)VIP专栏