ARTS第二周

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 &copy; 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.setIntervalwindow.clearInterval我们怎样才能简化这个组件呢?

首先创建一个名为Interval的组件,处理有关window.setIntervalwindow.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 &copy; 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 &copy; 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,有兼容性问题哈,谨慎使用

  1. -webkit-text-fill-color: transparent
  2. -webkit-background-clip: text

-webkit-text-fill-color: transparent是将文字颜色变成透明

-webkit-background-clip: text是让背景色被裁剪为文字的前景色

SHARE

这周还真不知道分享啥了,工作有点忙。

在现代一些大型前端项目中,一个站点或者app之类的,肯定不是由单一单页面应用构成的。大概是这个样子,可能不尽相同哈
在这里插入图片描述
由基础组件和一些库支撑开发,按业务或者功能场景划分了多个单页面应用,然后不同的系统,通过组合这些前端微服务应用吧,构建成一个系统。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值