普通视图

发现新文章,点击刷新页面。
昨天 — 2025年12月24日首页

Steam玩累了?那用 Node.js 写个小游戏:手把手玩懂 JS 运行环境

2025年12月24日 14:44

前言

你可能听说过 Node.jsJS 的运行环境”,但这玩意儿到底咋用?提到 JavaScript,你可能先想到它在浏览器里折腾网页特效的样子;但 Node.js 的出现,直接把 JS 从浏览器 “解放” 了出来,让它能像 PythonJava 一样,在操作系统上呼风唤雨 —— 读写文件、操作终端、搭建后端服务都不在话下。而模块化,正是 Node.js 让代码变得整洁、可复用的核心秘诀

今天咱从模块化入手,再手把手写个小游戏,让你把Node.js玩明白!

一、先认识 Node.js 的 “自带工具”:process、__dirname 这些都啥啊?

Node.js 刚启动时,就给我们准备了一堆 “开箱即用” 的工具。

// 最基础的控制台输出,和浏览器里的console.log用法一致
console.log('hello');

// Date对象:处理时间和日期,比如new Date()能获取当前时间
console.log(Date);

// 定时器方法:Node.js 里也能设置延时/重复执行的代码
console.log(setTimeout); // 延时执行(毫秒级)
console.log(setInterval); // 重复执行(毫秒级)
// console.log(requestAnimationFrame); // 适配浏览器/Node.js的帧动画(Node.js 16+支持)

// 路径相关:定位文件位置的“神器”
console.log(__dirname); // 打印当前文件所在文件夹的绝对路径
console.log(__filename); // 打印当前文件的完整绝对路径(包含文件名)

// 进程参数:获取终端执行命令时传的参数
console.log(process.argv);

image.png

二、Node.js 模块化:代码也能 “打包快递”

写代码像搭积木 —— 把重复的功能拆成独立文件,要用的时候 “拼” 起来,这就是模块化Node.js 支持两种主流规范:

1. CommonJS:Node.js 自带的 “老牌规范”

就像你寄快递时填 “收件人信息”,CommonJS 用 module.exports 打包功能,require() 取快递:

// lib.js(打包功能)
function add(x, y) {
    return x + y;
}
function minus(x, y) {
    return x - y;
}
// 把 add 和 munus 打包成 “快递”
module.exports = {
    add,
    minus
}
// index.js(取快递用功能)
// 用 require 拿到 lib.js 里的 add
const lib = require('./lib.js');
console.log(lib.add(1, 2));    // 输出 3
console.log(lib.minus(1, 2));  // 输出 -1

image.png

image.png

当然每次console.log都要写一遍lib.,会感到很烦,所以这里用解构的方法会更舒服点:

const {add, minus} = require('./lib.js');
console.log(add(1, 2));
console.log(minus(1, 2));

image.png

2. ESModule:前端圈的 “新潮流规范”

如果你的项目想和浏览器端代码 “互通”,可以用 ESModule,但是但是但是重要的事情说三遍:得在 package.json 里加 "type": "module"(一般情况下是有的,直接修改type里的值即可)。

上图更清晰:

image.png

还是 common 文件夹的例子:

// lib.js(用 export default 打包)
function add(x, y){
    return x + y;
}
function minus(x, y){
    return x - y;
}
export default {
    add,
    minus
}
// index.js(用 import 导入)
import lib from './lib.js'
console.log(lib.add(1, 2));    // 输出 3
console.log(lib.minus(1, 2));  // 输出 -1

image.png

三、实战:用 Node.js 写 “石头剪刀布” 游戏

没错,“石头剪刀布” 怎么就不算小游戏了,这可不算欺骗哦😮。好了我知道你肯定会觉得很简单然后马上打开Steam的,但来都来了,那就👀看我如何用Node.js写出来的。

逻辑很简单:你在终端输入 “rock/scissor/paper”,电脑随机出拳,然后比胜负~

第一步:先写 “游戏核心逻辑”(模块化拆分)

把 “出拳、比胜负” 的功能拆到 game/lib.js 里

// game/lib.js(游戏核心逻辑)
module.exports = function (playerAction) {
    // 电脑随机出拳(rock/scissor/paper 三选一)
    const arr = ['rock', 'scissor', 'paper'];
    const index = Math.floor(Math.random() * 3); // 向下取整(0,1,2)
    const computerAction = arr[index];
    console.log(`我出了${computerAction}`);

    // 比胜负,返回结果(0=平,-1=你赢,1=你输)
    if (computerAction === playerAction) {
        console.log('平局');
        return 0;
    } else if ((playerAction === 'rock' && computerAction === 'scissor') ||
        (playerAction === 'scissor' && computerAction === 'paper') ||
        (playerAction === 'paper' && computerAction === 'rock')) {
        console.log('你赢了');
        return -1;
    } else {
        console.log('你输了');
        return 1;
    }
}

第二步:写 “终端交互逻辑”(调用核心功能)

在 game/index.js 里,用 Node.js 的 process 模块获取你在终端的输入,再调用游戏逻辑:

// game/index.js(终端交互)
const game = require('./lib.js');
let count = 0; // 统计你赢的次数

// 监听终端输入(你输入的内容会传到 e 里)
process.stdin.on('data', e => {
    // 把输入的内容转成字符串,去掉空格
    const playerAction = e.toString().trim();
    // 调用游戏逻辑,拿到结果
    const result = game(playerAction);
    
    if (result === -1) {
        count++;
    }
    
    // 赢 3 次就结束游戏
    if (count === 3) {
        console.log('你太厉害了,我不玩了');
        process.exit(); // 结束 Node.js 进程
    }
});

第三步:跑起来!在终端玩游戏

打开终端,进入 game 文件夹,执行:

node index.js

然后输入 rock/scissor/paper,就能和电脑 PK 啦~

image.png

image.png

占用你玩2分钟玩Steam的时间看完了我的石头剪刀布~ 😊 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

四、总结:Node.js 到底是啥?

一句话: 它不仅是 “JS 运行环境”,更是让你用 JS 写 “能和操作系统互动” 代码的工具—— 比如读文件、监听终端输入、写后端服务… 而模块化是它的 “基本功”,帮你把代码写得更整洁、更好维护~

结语

从模块化规范的拆解,到石头剪刀布游戏的落地,不难发现 Node.js 的魅力:它让 JavaScript 跳出了前端的局限,拥有了跨场景开发的能力。小小的 require 和 export 背后,是代码组织的大智慧;简单的终端交互游戏里,藏着后端开发的雏形。

希望这篇内容能让你对 Node.js 不再陌生,下次遇到复杂项目时,也能想起用模块化思维拆解问题,用 Node.js 敲出属于自己的有趣代码。

另外要查看Node.js详细资料的话,官网在这:

Node.js 官网

昨天以前首页

🚀别再卷 Redux 了!Zustand 才是 React 状态管理的躺平神器

2025年12月20日 10:50

Zustand VS Redux

在文章开始前咱们先唠嗑一下,各位平时用哪个更多点呢?大数据不会骗人:

首先GitHub上的 Star 数量比较: image.png

image.png

其次每周的下载数量比较:

image.png

image.png

显然,想必用Zustand的可能大概也许应该会居多(单纯看数据来讲)。那么明明Redux才是大哥,为啥被Zustand这个小弟后来居上了?

给大家一个表:

对比项 Redux(老牌流程派) Zustand(新晋清爽党)
上手门槛 高:得记 action type、reducer、Provider 等一堆概念 低:会用 React Hook 就能写,几行代码起手
代码量 多:改个 count 得写 action、reducer 一堆模板代码 少:创建 store + 组件调用,加起来不到 20 行
组件里怎么用 得用 useSelector 取数据 + useDispatch 发动作 直接 useStore( state => state.xxx ) 一步到位
要不要包 Provider 必须包:得用 <Provider store={store}> 裹整个 App 不用包:组件直接调用 store,省一层嵌套
适合场景 大型复杂项目(多人协作、状态逻辑多) 中小型项目 / 快速开发(想少写代码、快速落地)

相信看完表大家已经很明了了,那么如果还想深入了解的可以自行去搜搜,我们唠嗑就到这,开始今天的学习。

具体资料大家去官网看:

www.npmjs.com/package/zus…

www.npmjs.com/package/rea…

前言

想象一下:你正在开发一个 React 项目,Home 组件要改个数字,About 组件得同步显示,List 组件还要从接口拉数据 —— 要是每个组件都自己存状态,代码早乱成一锅粥了!今天咱们就用 Zustand 这个躺平神器,把这些组件串成丝滑的整体,顺便解锁 React 全局状态的 “极简玩法”

一、先搭个 “状态仓库”:Zustand 初体验

Zustand 是啥?你可以把它理解成一个 “共享储物柜”:组件们不用再互相传 props,直接从这个柜子里拿数据、调方法就行。

首先你需要下载Zustand(在开篇的资料里也可以找到~):

image.png

先看我们的第一个 “储物格”——count.js(负责管理计数状态):

// src/store/count.js
import { create } from "zustand";

// 用 create 造一个“状态仓库”
const useCountStore = create((set) => ({
    // 存数据:初始计数是0,还有个默认年龄19
    count: 0,
    age: 19,
    // 存方法:点一下计数+1(set会自动更新视图)
    increase: () => set((state) => ({ count: state.count + 1 })),
    // 传个参数,计数直接减val
    decrease: (val) => set((state) => ({ count: state.count - val }))
}))

export default useCountStore;

就这么几行,一个能 “存数据 + 改数据” 的全局状态就搞定了 —— 比 Redux 轻量到没朋友!

二、组件 “抢着用”:状态共享原来这么丝滑

有了仓库,组件们就能 “按需取货” 了。先看 Home 组件(负责操作计数):

// src/components/Home.jsx
import useCountStore from '../store/count.js'

export default function Home() {
    // 从仓库里“拿”count数据
    let count = useCountStore((state) => state.count);
    // 从仓库里“拿”increase、decrease方法
    const increase = useCountStore((state) => state.increase);
    const decrease = useCountStore((state) => state.decrease);
    return (
        <div>
            {/* 点按钮直接调仓库里的方法,不用传参! */}
            <button onClick={increase}>发送-{count}</button>
            <button onClick={() => decrease(10)}>减少-{count}</button>
        </div>
    )
}

再看 About 组件(负责显示计数):

// src/components/About.jsx
import useCountStore from "../store/count"

export default function About() {
    // 同样从仓库拿count,Home改了这里自动更!
    let count = useCountStore((state) => state.count);
    return (
        <div>
            <h2>title-{count}</h2>
        </div>
    )
}

点击前:

image.png

点击10次发送后:

image.png

刷新然后点击10次减少后:

image.png

你看你看你看看看,Home 点按钮改了 count,About 里的标题直接同步更新 —— 连 props 都不用传,这丝滑感谁用谁知道!

三、进阶玩法:状态里塞接口请求

光存数字才哪到哪,还不够炫!咱们给仓库加个 “拉接口” 的功能。先写 list.js(负责管理列表数据):

// src/store/list.js
import { create } from "zustand";

// 先写个请求接口的函数
const fetchApi = async () => {
    const response = await fetch('https://mock.mengxuegu.com/mock/66585c4db462b81cb3916d3e/songer/songer');
    const res = await response.json();
    return res.data; // async函数的return会变成Promise的resolve值
}

// 造个存列表的仓库
const useListStore = create((set) => ({
    list: [], // 初始列表是空数组
    // 存个“拉列表”的方法,里面调用接口
    fetchList: async () => {
        const res = await fetchApi();
        set({ list: res }) // 拿到数据后更新list
    }
}))

export default useListStore;

然后让 List 组件 用这个仓库:

// src/components/List.jsx
import { useEffect } from "react";
import useListStore from "../store/list"

export default function List() {
    // 从仓库拿list数据和fetchList方法
    const list = useListStore((state) => state.list);
    const fetchList = useListStore((state) => state.fetchList);

    // 组件一加载就调用接口拉数据
    useEffect(() => {
        fetchList()
    }, [])

    return (
        <div>
            {/* 拿到数据直接map渲染 */}
            {list.map((item) => {
                return <div key={item.name}>{item.name}</div>
            })}
        </div>
    )
}

接口数据就出现在浏览器上啦:

image.png

打开页面,List 组件会自动拉接口、存数据、渲染列表 —— 状态管理 + 接口请求,一套流程直接在仓库里包圆了!

四、最后一步:把组件都塞进 App

最后在 App.jsx 里把这些组件拼起来:

import Home from "./components/Home"
import About from "./components/About"
import List from "./components/List"

export default function App() {
    return (
        <div>
            <Home></Home>
            <About></About>
            <List></List>
        </div>
    )
}

image.png

启动项目,你会看到:About 显示着计数,List 自动渲染接口数据 —— 这就是 Zustand 给 React 带来的 “状态自由”

总结

Zustand 堪称 React 状态管理的 “轻骑兵”:无需写冗余的 reducer、不用嵌套 Provider 包裹组件树,几行代码就能搭建全局状态仓库。它剥离了传统状态管理的繁琐仪式感,让我们彻底摆脱模板代码的束缚,聚焦业务本身。

结语

相比 Redux 的 “厚重” 和 Context API 在高频更新下的性能短板,Zustand 就像一把恰到好处的 “瑞士军刀”,轻巧却锋利,用最简单的方式解决了 React 组件间的状态共享难题,让开发者能把更多精力放在业务逻辑本身,而不是状态管理的 “套路” 里。

好的工具从来不是炫技的枷锁,而是让开发者回归创造本身的桥梁。

❌
❌