7
头图

本文参与了 SegmentFault 思否年度征文「一名技术人的 2022」,欢迎正在阅读的你也加入。

前言

时光如梭,岁月匆匆而过,2022年一转眼就已经到了末尾,今年的环境异常艰难,可是想想自己这一年来的付出,也还是值得做一个复盘总结的,正所谓有得必有失,在这一年我失去了太多,不过却也让我成长了不少,当然这些都是题外话,我主要还是来复盘一下今年我所学习的成果。

文章总结

今年一共写了11篇文章,加上本次年终总结,一共12篇,数量也并不多,分别如下:

其中尤其是50天用vue3完成了50个web项目,我学到了什么?实现《羊了个羊》的小游戏我最为满意,毕竟这两篇文章是我用心总结出每一个知识点,并且让自己融会贯通学到的知识点,同时也帮助他人学到知识点那就足够了。

其实今年我主要重心放在了React技术栈上,所以我也用React写了不少东西,对于vue框架,尤其是vue3,最主要的输出就是50天用vue3完成了50个web项目,我学到了什么?,关于这50个项目其实虽然表面上写的是50天,但实际上我花了不止50天,当然这都不重要了,最重要的是我从这50个项目里面深入去了解了一下less和sass这两门css预处理语言的语法。例如混入mixin,函数,循环,条件判断等,两种预处理语言之间也有很多相同的地方,当然也有不同的地方。比如循环,我们在sass当中会有@for关键字,而在less当中,我们需要写选择器 + when(这种更像是在写递归调用自身)。

同时对于vue3的核心语法,我也有了一定的认知,至少在实际做vue3的项目当中,我认为我还是没有多大的问题的,这些都是通过实际动手做这50个web项目让我学到的,为了方便我还特意用vue3写了一个关于这50个web项目的网站,地址在这里

PS: 如果以上地址访问不到,可以访问这里

这个网站是我自己设计并实现的,虽然布局看起来有些简单,但是我认为其中的逻辑功能和样式代码还是有点点难度的,在这里我可以总结一下有哪些知识点值得学习:

  • 实现一个clickOutside指令
  • 实现一个下拉框组件
  • 实现一个图片预加载组件
  • 实现classnames工具函数(不是复制的classnames源码,是参考实现的)
  • 实现文本超出省略的判断
  • 实现回到顶部的功能
  • 卡片组件以及图标组件的实现
  • less核心语法

以上是我今年在文章上所做的总结,除此之外我还在github上新增贡献并逐步完善了3个仓库,让我们一起来看看吧。

3个项目

算法

关于剑指offer算法,我基本上已经将剑指offer的官方算法题刷完了,并且解题思路,我也已经记下来建了一个项目,地址在这里

虽然在工作当中我似乎并没有用到太多算法,可事实上做了一下算法题确实是打开了我的眼界,更何况,我也已经将算法给加到了我所做的项目当中,比如那个用vue3实现连连看的小游戏里面就有这样一个算法。

const findRepeatItem = function (arr: GlobalModule.MaterialData[]) {
    const unique = new Set();
    for (const item of arr) {
        if (unique.has(item.src)) {
            return true;
        }
        unique.add(item.src);
    }  
    return false;
};

这个函数顾名思义,就是从数组当中查找重复项,思路就是利用set数据结构存储每一个数组项,然后当数据里面存在要查找的项的时候,就代表重复了,直接返回即可,这个函数的思路就来源于算法当中。也就是这个算法数组中重复的数字的思路二--哈希表解法。

这只是其中一个小小的应用而已,如果在实际项目当中碰到相应的需求,我或许也会再次回头来翻看这些笔记,已达到巩固并且举一反三的目的,这个项目也就是我今年完善的3个项目之一。

js和css代码段

css代码段

这可能是我今年付出精力最多的一个项目了吧,几乎每天都要贡献一段代码段,不信看下图:

每天学习一段javascript或者是css代码段,不至于让自己忘记css和js基础,而且很多代码段都会用到实际业务当中,比如使用css实现自定义的单选框和复选框,我们以单选框作为示例讲一下实现思路如下。

css实现单选框

思路就是如下列出的几点,我们就可以创建一个带有状态更改动画的样式单选按钮。

  • 创建一个 .radio-container 并使用 flexbox 为单选按钮创建适当的布局。
  • 重置 <input> 上的样式并使用它来创建单选按钮的轮廓和背景。
  • 使用 ::before 元素创建单选按钮的内圈。
  • 使用 transform: scale(1) 和 CSS transition 在状态变化时创建动画效果。

代码量也不算多,我们来看html和css代码分别如下:

<div class="radio-container">
    <input type="radio" class="radio-input" id="male" name="sex"/>
    <label for="male" class="radio">男</label>
    <input type="radio" class="radio-input" id="female" name="sex"/>
    <label for="female" class="radio">女</label>
</div>
.radio-container {
    box-sizing: border-box;
    background-color: #fff;
    color: #545355;
    height: 64px;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-flow: row wrap;
}
.radio-container * {
    box-sizing: border-box;
}
.radio-input {
    appearance: none;
    background-color: #fff;
    width: 16px;
    height: 16px;
    border: 1px solid #cccfdb;
    margin: 0;
    border-radius: 50%;
    display: grid;
    align-items: center;
    justify-content: center;
    transition: all .3s ease-in;
}
.radio-input::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    transform: scale(0);
    transition: .3s transform ease-in-out;
    box-shadow: inset 6px 6px #fff;
}
.radio-input:checked {
    background-color: #2396ef;
    border-color: #2396ef;
}
.radio-input:checked::before {
    transform: scale(1);
}
.radio {
    cursor: pointer;
    padding: 6px 8px;
}
.radio:not(:last-child){
    margin-right: 6px;
}

都是常规的布局,其中我们主要利用了label的for属性和input的id属性绑定在一起,然后通过样式将input框给隐藏,修改label的样式去模拟出单选框,从而达到如下的效果:

同理,复选框也是这样的思路去实现的,事实上还有很多小技巧,比如隐藏一个元素,我们通常可能会使用display和visibility又或者是opacity属性来达到隐藏,可事实上这三个属性隐藏元素或多或少都会有一定的问题,比如display无法添加过渡效果,而visibility又占用元素本身的空间,opacity只是单纯的设置透明度,元素依然可以被点击等等,而这里我们可以利用clip和定位来达到隐藏元素的目的,从而解决前面三个属性所带来的问题。代码如下:

.offscreen {
    border: 0;
    clip: rect(0,0,0,0);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;
}

然后我们给要隐藏的元素添加一个offscreen类名即可达到隐藏元素,这不失为一种隐藏元素的办法,当然还有很多的css代码段值得学习的,例如border实现等高布局,css实现加载中效果,开关组件,高度过渡效果等等示例。

js代码段

除了css代码段,js代码段也有许多值得学习的知识点,比如冒泡排序算法.

冒泡排序算法

我们来看这个算法的实现思路如下:

  • 声明一个变量,swapped,指示在当前迭代期间是否交换了任何值。
  • 使用扩展运算符 (...) 克隆原始数组 arr。
  • 使用 for 循环遍历克隆数组的元素,在最后一个元素之前终止。
  • 使用嵌套的 for 循环遍历 0 和 i 之间的数组段,交换任何相邻的乱序元素并将 swapped 设置为 true。
  • 如果在迭代后 swapped 为 false,则不需要更多更改,因此返回克隆的数组。

代码如下:

const bubbleSort = arr => {
    let swapped = false;
    let a = [...arr];
    for(let i = 0;i < a.length;i++){
        swapped = false;
        for(let j = 0;j < a.length - i;j++){
            if(a[j + 1] < a[j]){
                //数组解构的方式
                [a[j],a[j + 1]] = [a[j + 1],a[j]];
                swapped = true;
            }
        }
        if(!swapped) { 
            return a;
        }
    }
    return a;
}

再比如数组分块,这个也会用到实际业务当中。

数组分块

我们来看实现数组分块的思路如下:

  • 使用 Array.from() 创建一个新数组,该数组适合将要生成的块数。
  • 使用 Array.prototype.slice() 将新数组的每个元素映射到长度为 size 的块。
  • 如果原始数组不能被平均分割,最终的块将包含剩余的元素。

js代码如下:

const chunk = (arr,size) => Array.from({ length:Math.ceil(arr.length / size)},(v,i) => arr.slice(i* size,i * size + size));   

当然还有更多的css和js代码段,我就不一一举例了,我只是想说明这样每天学习一段代码段让自己学到了很多,更多的代码段请看网站

如果访问不了,可访问这个网站

react代码段

今年还新开了一个项目,记录react的学习代码段,包含基础组件和hook函数两个部分,除此之外,还有在使用antd design组件库的组件基础上额外封装的组件。比如实现一个弹出框组件,一个倒计时组件一个手风琴组件等等,像hooks函数就更多了,比如useTimeout函数,useInterval等等,useBodyScrollLock函数等等。我还是举其中2个示例来说明吧。

受控的input组件

主要是样式去美化input组件,同时将input组件的value值和onchange事件暴露出去,在这里我使用的是css in js来美化输入框的,代码如下:

import styled from '@emotion/styled';
import React from 'react';
import type { SyntheticEvent } from 'react';

const StyleInput = styled.input`
  box-sizing: border-box;
  margin: 0;
  font-variant: tabular-nums;
  list-style: none;
  font-feature-settings: 'tnum';
  position: relative;
  display: inline-block;
  width: 100%;
  min-width: 0;
  padding: 4px 11px;
  color: #000000d9;
  font-size: 14px;
  line-height: 1.5715;
  background-color: #fff;
  background-image: none;
  border: 1px solid #d9d9d9;
  border-radius: 2px;
  transition: all 0.3s;
  &:focus {
    border-color: #40a9ff;
    box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
    border-right-width: 1px;
    outline: 0;
  }
`;
type LiteralUnion<T extends U, U> = T & (U & {});
interface ControlledInputProps {
  type: LiteralUnion<
    | 'button'
    | 'checkbox'
    | 'color'
    | 'date'
    | 'datetime-local'
    | 'email'
    | 'file'
    | 'hidden'
    | 'image'
    | 'month'
    | 'number'
    | 'password'
    | 'radio'
    | 'range'
    | 'reset'
    | 'search'
    | 'submit'
    | 'tel'
    | 'text'
    | 'time'
    | 'url'
    | 'week',
    string
  >;
  value: string;
  onChange(v: string): void;
  placeholder: string;
}
const ControlledInput = (props: Partial<ControlledInputProps>) => {
  const { value, onChange, ...rest } = props;
  const onChangeHandler = (e: SyntheticEvent) => {
    if (onChange) {
      onChange((e.target as HTMLInputElement).value);
    }
  };
  return (
    <StyleInput value={value} onChange={onChangeHandler} {...rest}></StyleInput>
  );
};

并且每一个组件都有对应的tsx和jsx版本,也有相应的接口,也方便学习如何去实现封装组件,组件的实现个人认为封装的最复杂的是弹出框组件,因为我需要考虑2种方式使用,第一种是通过组件方式使用,第二种则是通过调用方法的方式来使用。

然后就是我们的hooks函数了,比如我们来看useBodyScrollLock函数的实现。

useBodyScrollLock函数的实现

在这个函数中,我们通过在useLayoutEffect生命周期钩子函数中获取到body元素,然后给body元素设置一个overflow为hidden的样式,就达到了滚动的锁定,顾名思义这个函数就是用来禁止页面的滚动的。我们来看完整代码如下:

import { useLayoutEffect } from 'react';

const useBodyScrollLock = () => {
  useLayoutEffect(() => {
    const container = document.body;
    const originOverflowStyle = window.getComputedStyle(container!).overflow;
    container!.style.overflow = 'hidden';
    return () => {
      container!.style.overflow = originOverflowStyle;
    };
  }, []);
};

export default useBodyScrollLock;

当然这只是一个简单的hook函数,很好理解,也还有更复杂的hook函数,比如useCopyToClipboard函数的实现,这里也不需要一一叙述了,想要查看更多实现思路,请看这个网站

如果访问不了,请看这个网站

以上就是我今年的输出了,当然除了这之外,我还在坚持写一部小说,不过这就不需要透露了,哈哈哈,因为我觉得我写的也不怎么样。

最后

总而言之,我今年的学习成果不算好也不算坏,但是去年立下的flag并没有完成,只能展望于2023年了,感谢阅读到这里,本文就到此为止了,与君共勉。


夕水
5.2k 声望5.7k 粉丝

问之以是非而观其志,穷之以辞辩而观其变,资之以计谋而观其识,告知以祸难而观其勇,醉之以酒而观其性,临之以利而观其廉,期之以事而观其信。