Algorithm
单链表的反转
// Input: 1->2->3->4->5->NULL Output: 5->4->3->2->1->NULL
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
* https://leetcode.com/problems/reverse-linked-list/description/
*/
/**
* @param {ListNode} head
* @return {ListNode}
* 链表反转,顾名思义就是将链表中的next指向反过来
*/
var reverseList = function(head) {
if(!head || !head.next) return head;
let cur = head; // 当前节点
let pre = null; // pre记录前驱
while(cur) {
// 拿到下一个节点,
// next变量记录后继
// 先将cur.next保存下来,再将next指向调转方向
// 然后pre记录为当前,cur记录为下一个,进行下一次反转
const next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
};
review
原文:https://dev.to/ycmjason/vue-without-view-an-introduction-to-renderless-components-23ld
有时候我们维护的组件变得越来越大,变得越来越难以维护,这个时候,有的做法是将组件拆分为更多较小的组件,但是这样的优势并不明显,代码变得更加复杂,难以查源。
这篇文章,将介绍‘无渲染组件的概念’,它有可能有助于您改进组件。
源代码,https://github.com/ycmjason-talks/2019-06-20-vue-js-london-meetup-11
这段代码的网站中,有个页脚,我们来看看源码,
src / components / Footer.vue:
<template>
<footer :style="footerStyle">
<div class="text" :style="textStyle">Made with ❤ by Jason Yu © 2019</div>
<label class="insane-mode-label">
<input type="checkbox" v-model="insaneMode"> Insane Mode (new!)
</label>
</footer>
</template>
<script>
import { randomNumber, randomPercentage, randomColor } from '../services/random';
const FOOTER_INTERVAL_MS = 543;
const TEXT_INTERVAL_MS = FOOTER_INTERVAL_MS / 3;
export default {
mounted() {
this.randomFooterStyle();
this.randomTextStyle();
this.footerIntervalId = window.setInterval(this.randomFooterStyle, this.footerIntervalMs);
this.textIntervalId = window.setInterval(this.randomTextStyle, this.textIntervalMs);
},
beforeDestroy() {
window.clearInterval(this.footerIntervalId);
window.clearInterval(this.textIntervalId);
},
data: () => ({
footerStyle: null,
textStyle: null,
insaneMode: false,
}),
computed: {
insaneFactor() {
return this.insaneMode ? 3 : 1;
},
footerIntervalMs() {
return FOOTER_INTERVAL_MS / this.insaneFactor;
},
textIntervalMs() {
return FOOTER_INTERVAL_MS / this.insaneFactor;
},
},
watch: {
insaneMode() {
window.clearInterval(this.footerIntervalId);
window.clearInterval(this.textIntervalId);
this.footerIntervalId = window.setInterval(this.randomFooterStyle, this.footerIntervalMs);
this.textIntervalId = window.setInterval(this.randomTextStyle, this.textIntervalMs);
},
},
methods: {
randomFooterStyle() {
const { insaneFactor } = this;
this.footerStyle = {
borderRadius: `${randomPercentage()} ${randomPercentage()} / ${randomPercentage()} ${randomPercentage()}`,
background: randomColor(),
transitionDuration: `${FOOTER_INTERVAL_MS / insaneFactor}ms`,
};
},
randomTextStyle() {
const { insaneFactor } = this;
this.textStyle = {
transform: `rotate(${randomNumber(
-3 * insaneFactor,
3 * insaneFactor,
)}deg) scale(${randomNumber(0.7 * insaneFactor, 1.3 * insaneFactor)})`,
color: randomColor(),
transitionDuration: `${TEXT_INTERVAL_MS / insaneFactor}ms`,
};
},
},
};
</script>
<style scoped>
footer {
margin-top: 1rem;
padding: 3rem 0;
transition-property: border-radius, background;
text-align: center;
}
footer .text {
transition-property: color, transform;
}
.insane-mode-label {
display: block;
margin-top: 2rem;
}
</style>
可以看到,有一半以上的脚本代码都在处理window.setInterval
和window.clearInterval
我们怎样才能简化这个组件呢?
首先创建一个名为Interval的组件,处理有关window.setInterval和
window.clearInterval`
src/组件/renderless/Interval.js
export default {
render: () => null
}
无渲染组件,render函数什么不用做
然后是props,我们希望能够控制每个间隔的时间
export default {
props: {
delay: {
type: Number,
required: true,
},
},
render: () => null,
}
我们需要在挂载时开始,在销毁时结束
export default {
props: {
delay: {
type: Number,
required: true,
},
},
mounted () {
this.id = window.setInterval(() => /* ... */, this.delay);
},
beforeDestroy () {
window.clearInterval(this.id);
},
render: () => null,
}
然后在每次间隔,我们应该做什么,由上层组件决定。
所以
export default {
props: {
delay: {
type: Number,
required: true,
},
},
mounted () {
this.id = window.setInterval(() => this.$emit('tick'), this.delay);
},
beforeDestroy () {
window.clearInterval(this.id);
},
render: () => null,
}
然后这个组件就可以,看起来很简单,但是它赋予了很多功能,让组件有了interval的功能,而无需自己管理intervalId,以及interval的监听和移除,包括设置的delay
然后让我们来重构
之前的这部分代码
// ...
mounted() {
// ...
this.footerIntervalId = window.setInterval(this.randomFooterStyle, this.footerIntervalMs);
this.textIntervalId = window.setInterval(this.randomTextStyle, this.textIntervalMs);
},
beforeDestroy() {
window.clearInterval(this.footerIntervalId);
window.clearInterval(this.textIntervalId);
},
可以替换成
<Interval :delay="footerIntervalMs" @tick="randomFooterStyle"></Interval>
<Interval :delay="textIntervalMs" @tick="randomTextStyle"></Interval>
然后footer:
<template>
<footer :style="footerStyle">
<Interval :delay="footerIntervalMs" @tick="randomFooterStyle"></Interval>
<Interval :delay="textIntervalMs" @tick="randomTextStyle"></Interval>
<div class="text" :style="textStyle">Made with ❤ by Jason Yu © 2019</div>
<label class="insane-mode-label">
<input type="checkbox" v-model="insaneMode"> Insane Mode (new!)
</label>
</footer>
</template>
<script>
import { randomNumber, randomPercentage, randomColor } from '../services/random';
import Interval from './renderless/Interval';
const FOOTER_INTERVAL_MS = 543;
const TEXT_INTERVAL_MS = FOOTER_INTERVAL_MS / 3;
export default {
mounted() {
this.randomFooterStyle();
this.randomTextStyle();
},
data: () => ({
footerStyle: null,
textStyle: null,
insaneMode: false,
}),
computed: {
insaneFactor() {
return this.insaneMode ? 3 : 1;
},
footerIntervalMs() {
return FOOTER_INTERVAL_MS / this.insaneFactor;
},
textIntervalMs() {
return FOOTER_INTERVAL_MS / this.insaneFactor;
},
},
watch: {
insaneMode() {
window.clearInterval(this.footerIntervalId);
window.clearInterval(this.textIntervalId);
this.footerIntervalId = window.setInterval(this.randomFooterStyle, this.footerIntervalMs);
this.textIntervalId = window.setInterval(this.randomTextStyle, this.textIntervalMs);
},
},
methods: {
randomFooterStyle() {
const { insaneFactor } = this;
this.footerStyle = {
borderRadius: `${randomPercentage()} ${randomPercentage()} / ${randomPercentage()} ${randomPercentage()}`,
background: randomColor(),
transitionDuration: `${FOOTER_INTERVAL_MS / insaneFactor}ms`,
};
},
randomTextStyle() {
const { insaneFactor } = this;
this.textStyle = {
transform: `rotate(${randomNumber(
-3 * insaneFactor,
3 * insaneFactor,
)}deg) scale(${randomNumber(0.7 * insaneFactor, 1.3 * insaneFactor)})`,
color: randomColor(),
transitionDuration: `${TEXT_INTERVAL_MS / insaneFactor}ms`,
};
},
},
};
</script>
<style scoped>
footer {
margin-top: 1rem;
padding: 3rem 0;
transition-property: border-radius, background;
text-align: center;
}
footer .text {
transition-property: color, transform;
}
.insane-mode-label {
display: block;
margin-top: 2rem;
}
</style>
组件变得很友好,没有一些无用的命名,和重复的操作
疯狂模式
疯狂模式是在footer组件的watched中提供的一个功能,
<template>
<!-- ... -->
<Interval :delay="footerIntervalMs" @tick="randomFooterStyle"></Interval>
<Interval :delay="textIntervalMs" @tick="randomTextStyle"></Interval>
<!-- ... -->
</template>
<script>
// ...
watch: {
insaneMode() {
window.clearInterval(this.footerIntervalId);
window.clearInterval(this.textIntervalId);
this.footerIntervalId = window.setInterval(this.randomFooterStyle, this.footerIntervalMs);
this.textIntervalId = window.setInterval(this.randomTextStyle, this.textIntervalMs);
},
},
// ...
</script>
显然这部分逻辑,我也想要移到Interval组件中,
疯狂模式被触发时,<Interval>
接收新的delay
道具this.footerIntervalMs
并且this.textIntervalMs
被更改。但是,<Interval>
尚未编程对变化作出反应delay
。我们可以添加一个观察器delay
,它将拆除现有的间隔并建立一个新的间隔。
export default {
props: {
delay: {
type: Number,
required: true,
},
},
mounted () {
this.id = window.setInterval(() => this.$emit('tick'), this.delay);
},
beforeDestroy () {
window.clearInterval(this.id);
},
watch: {
delay () {
window.clearInterval(this.id);
this.id = window.setInterval(() => this.$emit('tick'), this.delay);
},
},
render: () => null,
}
然后我们就可以删除footer中这部分代码,
最终的footer是这个样子的
<template>
<footer :style="footerStyle">
<Interval :delay="footerIntervalMs" @tick="randomFooterStyle"></Interval>
<Interval :delay="textIntervalMs" @tick="randomTextStyle"></Interval>
<div class="text" :style="textStyle">Made with ❤ by Jason Yu © 2019</div>
<label class="insane-mode-label">
<input type="checkbox" v-model="insaneMode"> Insane Mode (new!)
</label>
</footer>
</template>
<script>
import { randomNumber, randomPercentage, randomColor } from '../services/random';
import Interval from './renderless/Interval';
const FOOTER_INTERVAL_MS = 543;
const TEXT_INTERVAL_MS = FOOTER_INTERVAL_MS / 3;
export default {
mounted() {
this.randomFooterStyle();
this.randomTextStyle();
},
data: () => ({
footerStyle: null,
textStyle: null,
insaneMode: false,
}),
computed: {
insaneFactor() {
return this.insaneMode ? 3 : 1;
},
footerIntervalMs() {
return FOOTER_INTERVAL_MS / this.insaneFactor;
},
textIntervalMs() {
return FOOTER_INTERVAL_MS / this.insaneFactor;
},
},
methods: {
randomFooterStyle() {
const { insaneFactor } = this;
this.footerStyle = {
borderRadius: `${randomPercentage()} ${randomPercentage()} / ${randomPercentage()} ${randomPercentage()}`,
background: randomColor(),
transitionDuration: `${FOOTER_INTERVAL_MS / insaneFactor}ms`,
};
},
randomTextStyle() {
const { insaneFactor } = this;
this.textStyle = {
transform: `rotate(${randomNumber(
-3 * insaneFactor,
3 * insaneFactor,
)}deg) scale(${randomNumber(0.7 * insaneFactor, 1.3 * insaneFactor)})`,
color: randomColor(),
transitionDuration: `${TEXT_INTERVAL_MS / insaneFactor}ms`,
};
},
},
};
</script>
<style scoped>
footer {
margin-top: 1rem;
padding: 3rem 0;
transition-property: border-radius, background;
text-align: center;
}
footer .text {
transition-property: color, transform;
}
.insane-mode-label {
display: block;
margin-top: 2rem;
}
</style>
翻译完毕~~~~
感觉还挺有意思的,这个地方,你说用minix还实现不了,没办法传参数,用高阶组件貌似能实现,却变得异常麻烦,没有这个代码简洁好懂,不过vue3.0的RFC到时可以很好的抽离这部分逻辑,感觉可控性依然没有这个好,小技巧,也算学到了。
TIP
现学现卖吧,最近有个需求里的小功能,用到了这么个东西,让文字穿透背景,主要用到这两个css,有兼容性问题哈,谨慎使用
-webkit-text-fill-color: transparent
-webkit-background-clip: text
-webkit-text-fill-color: transparent是将文字颜色变成透明
-webkit-background-clip: text是让背景色被裁剪为文字的前景色
SHARE
这周还真不知道分享啥了,工作有点忙。
在现代一些大型前端项目中,一个站点或者app之类的,肯定不是由单一单页面应用构成的。大概是这个样子,可能不尽相同哈
由基础组件和一些库支撑开发,按业务或者功能场景划分了多个单页面应用,然后不同的系统,通过组合这些前端微服务应用吧,构建成一个系统。