前言🛸#
GitHub 是通过 Git 进行版本控制的软件源代码托管服务平台,由 GitHub 公司(曾称 Logical Awesome)的开发者 Chris Wanstrath、P. J. Hyett 和汤姆・普雷斯顿・沃纳使用 Ruby on Rails 编写而成。截止到 2020 年 1 月,GitHub 已经有超过 4000 万注册用户和 1.9 亿代码库(包括至少 2800 万开源代码库),事实上已经成为了世界上最大的代码存放网站和开源社区。
Github 作为世界上最大的开源社区,对我编程的学习可以说是举足轻重的,曾用它抄别人代码来度过作业的危机, 在之前使用 Puppeteer 尝试爬取了 Github 的部分数据,于是脑子一抽, 灵光一闪,决定配合之前写的后台,结合下自己想尝试的 react 函数式组件去写一个页面来展示一个人 Github 的一些数据。
项目地址:Github Report
NES.css🚀#
NES 是 20 世纪 80 年代末、90 年代初进入中国,是 80 后最早接触的游戏机,俗称红白机,这是一台使用 8 位处理器的游戏主机,而 NES.css 是一款 NES 风格(8 位机)的 CSS 框架,也是一款像素风的 CSS 元件库,它没有任何 JavaScript 依赖,只包含 CSS。
NES.css 提供的组件样式很对我胃口,它还提供了一些很好玩的像素 icon。NES.css 在默认里只有英文字体是像素风格的,如果是需要别的语言也是像素风格的话,需要自行去下载字体。
👀具体使用参考看官方文档:NES.css
React-spring🌏#
在最一开始我就想做一个整页滚动的效果,我每个页面都是 100vh,隐藏掉滚动条后监听鼠标滚动(移动端监听 touch),滚动执行的方法是整个页面上滑或则下滑 100vh(使用 margin-top,超出的部分隐藏,并且设置 transition 来使其有一个动画的效果),同时用时间戳实现了一个节流(单位时间内只执行一次某个方法),以防止用户频繁滚动✍:
function throttle(event, time) {
let pre = 0;
return function (...args) {
if (Date.now() - pre > time) {
pre = Date.now();
event.apply(this, args);
}
}
}
效果是有的,但是又感觉有点普通了🤨,接着翻到 React-spring 文档里看到了 Parallax,Parallax 可以创建一个可以滚动的容器,然后使用 ParallaxLayer 包含内容,并且可以设置其偏移量以及速度,就是可以制造视差滚动(页面上很多的元素在相互独立地滚动着),觉得挺好看就立马采用了😆。
在网上有看到过使用 background-attachment(背景图像的位置是在视口内固定,还是随着包含它的区块滚动),以及 transform(通过设置 translateZ,滚动的上下距离也就不一样)来实现,但是看 react-spring 中的 Parallax 是使用 js 实现的,通过监听滚动,实时使用 translate 设定位置🤔
有时间得要好好看下,文档在这👉:React-spring
React Hook📡#
既然是使用了 react 的函数式组件,那么肯定就绕不开它的钩子函数了。说几个我常用的吧。
useState#
为函数式组件引入 state,这个函数返回一个数组,数组第一个元素是变量,第二个元素是一个方法,用于改变变量,例如:
// 声明一个叫 “count” 的 state 变量
const [count, setCount] = useState(0);
useEffect#
useEffet 这个钩子函数可以说是替换了 componentDidMount, componentDidUpdate, componentWillUnmount 这三个生命周期,在组件第一次渲染的时候也会执行 useEffect (),具体✍:
useEffect(
//等价于 componentDidMount
() => {
// return等价于 componentWillUnmount
return () => {
};
},
// 依赖列表,当变更时候,执行useEffent(),等价于 componentDidUpdate
[]
);
useContext#
useContext () 可以使用在组件之间需要共享状态的情况下,不必显式地通过组件树的逐层传递 props
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
// 子级
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
// 孙级或更多级
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
useRef#
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。
返回的 ref 对象在组件的整个生命周期内持续存在,类似于一个 class 的实例属性,其可以很方便地保存任何可变值。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const intervalRef = useRef();
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
useEffect(
() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
};
});
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
// ...
</>
);
}
Warning 🔒#
- ✅只在最顶层使用 Hook(不要在循环、条件或嵌套函数中使用 Hook)
- ✅在 React 函数中调用 Hook
- ✅在自定义 Hook 中调用其他 Hook
以上是 React Hook 文档中的规则,原因就是 React 内部并不是真正保存 state,它是通过调用顺序来跟踪 state。
也就是说 多个 useState 的调用是借由一个链表来维护的,通过索引来实现对多个 state 的识别。 如果在 if/else 中调用 Hook,当函数组件重新执行的时候可能会改变调用顺序,会导致错误。
这个过程:
- 第一次渲染,根据 useState 顺序,逐个声明 state 并且将其放入全局 Array 中。每次声明 state,都要将 cursor 增加 1。
- 每个事件都有对应游标的 state 值,任何 state 事件触发,都会修改 state 数组中对应的 state 值,触发再次渲染。cursor 被重置为 0。按照 useState 的声明顺序,依次拿出最新的 state 的值,视图更新。
最后🔭#
感谢NES.css以及React-spring🙏
这是我个人的心血来潮的学习项目,有改善的地方或者 bug 欢迎交流🙌
如果这个东西能帮到你学到点什么就是我的荣幸🔮