本文參與了 SegmentFault 思否年度徵文「一名技術人的 2022」,歡迎正在閱讀的你也加入。
前言
時光如梭,歲月匆匆而過,2022年一轉眼就已經到了末尾,今年的環境異常艱難,可是想想自己這一年來的付出,也還是值得做一個覆盤總結的,正所謂有得必有失,在這一年我失去了太多,不過卻也讓我成長了不少,當然這些都是題外話,我主要還是來複盤一下今年我所學習的成果。
文章總結
今年一共寫了11篇文章,加上本次年終總結,一共12篇,數量也並不多,分別如下:
- 使用React手寫一個手風琴元件
- 手寫一個有點意思的電梯小程式
- 手寫一個有點意思的電梯小程式(React版本)
- 實現《羊了個羊》的小遊戲
- 50天用vue3完成了50個web專案,我學到了什麼?
- 手寫一個mini版本的React狀態管理工具
- 實現《羊了個羊-美女版》小遊戲(低配版)
- 一個有趣的互動效果的實現
- vue3實現一個思否貓連連看小遊戲
- 原生javascript手寫一個絲滑的輪播圖
- 三分鐘學會go語言的變數定義
其中尤其是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年了,感謝閱讀到這裡,本文就到此為止了,與君共勉。