Share Dialog
Share Dialog
Subscribe to 0xpowpos.eth
Subscribe to 0xpowpos.eth
<100 subscribers
<100 subscribers
最近开发了一款多钱包的资产管理平台——Walletee。旨在更快捷、方便地掌握自己在区块链上所拥有的资产,防止日常在钱包应用中频繁切换地址浪费精力。Walletee 的前端采用 NextJs + Tailwind + Framer Motion 实现,前者为前端开发的基础框架,后两者负责前端样式和动画效果。这是我第一次开发前端应用,因此从一个后端开发人员的角度,记录下整个开发脉络以及我认为核心的要点。
首先,我们从一个最常规的问题出发,由浅入深。
问题:当我们在浏览器中输入一个网址后,页面数据是如何传递的?
我们(后端工程师)固有认知是:前端向后端发送 HTTP 请求,后端监听、处理并返回最终的结果。我们通常认为的前端就是浏览器,然而对于浏览器是如何拿到前端的资源(包括 HTML、样式、Js 脚本)的过程是模糊的。
前端服务划分
事实上,前端服务可以分为两块:
服务端
请注意和后端服务端区分,不过二者本质上都是存放、运行代码的服务器。试想一下,前端工程师开发完的代码总需要部署吧,如果没有承载前端代码的服务器,总不可能直接将代码部署到大家的浏览器。因此,对于前端项目其实和后端一样,开发人员需要将自己的代码打包部署到服务器上,我们称服务器上运行的代码为服务端。
那么,当你在浏览器上输入一个网址按住回车键时,浏览器对于需要呈现的内容一无所知,它本质上是发送一个 HTTP 请求,然而这个请求不是到达后端服务器,而是转向前端服务器。前端服务器本质上也是一个后端服务,监听 HTTP 请求,并通过路由,拿到这个 HTTP 请求对应页面的前端资源。
客户端(浏览器)
当浏览器通过上文的 HTTP 请求获取到前端资源之后,然后才开始渲染整个页面的数据,这时候如果有需要一些动态的数据(存储在后端服务器),在浏览器的内部(本质上是从前端服务器拿到的 JS 脚本),才会再次发送 HTTP 请求从 后端服务器 获取,这就回到我们了最早的认知中来。
初级架构

我们继续从一个问题出发。
问题一:服务器之间可以互相通信,为什么前端服务器不把动态内容组装好,然后输出给客户端?

主要是从以下几点考虑的:
性能
将内容部分部分输出可以提高网站性能,因为客户端可以边下载边渲染页面,而不用等待整个页面下载完成。这样可以缩短网页加载时间,提高用户体验。
动态性
如果所有内容都是提前组装好的,那么前端服务器就无法根据客户端请求动态生成内容。
减少前端服务器负担
如果前端服务器一次性将所有内容组装好并输出给客户端,则服务器需要处理大量数据和请求。而将内容分成小部分输出可以帮助减轻服务器负担,提高网站的可用性。
问题二:那是不是说所有的动态组件都交由浏览器处理?
尽管前端服务器将内容分成小部分输出给客户端优化了网站的性能,但在某些情况下,这并不是最优解,此处不细究了。
可以看出,任何事情不能走向极端,这又是一个权衡的问题。
终极架构

浏览器在初始请求页面资源的时候,服务端渲染的资源 会在前端服务器直接生成(通过直接请求后端服务器获取数据)。剩下的资源返回给浏览器后,由浏览器向后端服务器直接索取动态资源,称为 客户端渲染。
因此,在开发前端项目时,一定要定位清楚:所写的代码究竟是在 前端服务器 运行还是在 浏览器 运行,这很重要*。*
场景:多钱包资产管理时,用户可以选择「添加钱包」的功能。假设用户数据库中记载的钱包数为 3,登陆后,资产页面已经将 3 个钱包的资产全部加载完毕。这时用户添加一个新的钱包,弹出添加表单输入信息并提交。
问题:提交以后需要重新刷新整个页面嘛?能否做到只有资产组件的重新加载和渲染?
答案是肯定的。要实现局部组件的动态加载和渲染,就需要用到 NextJS 前端框架为我们提供的组件状态。
状态
针对上述场景,我们定义了两个组件(理解为 Class ):Wallet 和 Asset,前者负责渲染用户的钱包信息,后者负责渲染用户钱包的资产信息,后者的输出依赖于前者。
两者的信息,都定义成了状态,如下所示:
Wallet 组件定义的一个状态
import { useState } from "react";
// 仅示例
const Wallet = () => {
// 组件定义的状态,初始化为一个空的列表
const [wallets, setWallets] = useState([]);
...
return (
<div>
{// Wallet 组件 HTML 代码}
</div>
);
}
Asset 组件定义的一个状态
import { useState } from "react";
// 仅示例
const Asset = () => {
// 组件定义的状态,初始化为一个空的列表
const [assets, setAssets] = useState([]);
...
return (
<div>
{// Asset 组件 HTML 代码}
</div>
);
}
当钱包增加时,逻辑代码(比如表单的 onSubmit 方法)会触发 wallets 状态的变更。
重新渲染
组件状态的变更会导致该组件在页面上重新渲染。因此可以看到钱包组件页面模块中新增了一个钱包地址。
然而,资产组件的状态更新比钱包组件要复杂。当用户新增钱包时,资产组件是需要重新向后端服务器发送 HTTP 请求才能拿到对应钱包的资产。与钱包组件状态变更不同的是,钱包组件的状态变更是直接由「用户行为」触发的,即它是在钱包组件内部表单的 OnSubmit 方法中直接 set 的。然而,我们不太可能在 OnSubmit 方法中,再去发起 HTTP 请求获取最新的 Asset 信息,然后更改 Assent 组件的状态,这样两者耦合就太严重,需要一种更优雅的机制。
那么就要引出一个称为 useEffect 的 React 钩子,这是一个重大神器,细节部分此处不做阐述。它让开发人员可以在组件渲染后完成各种操作。useEffect 钩子具有两个参数:一个是要执行的函数,另一个是一个依赖项数组。当依赖项数组中的任何值发生变化时,该函数将再次运行。如果依赖项数组为空,则该函数仅在组件第一次渲染时运行。
因此,我们可以将 Wallet 组件中的 wallets 状态传递到 Asset 组件中来,当 wallets 状态变更时,触发 useEffect 函数的执行,获取最新的 Asset 信息后,继而触发 assets 状态的变更,从而重新渲染 Asset 组件。
import { useState } from "react";
// 仅示例
const Asset = ({wallets}) => {
// 组件定义的状态
const [assets, setAssets] = useState([]);
...
// 动态加载
useEffect(() => {
getAssetsAPI(chain.id, DataType.PROTO).then((assets) =>
setAssets(assets)
);
}, [wallets]);
...
return (
<div>
{// Asset 组件 HTML 代码}
</div>
);
}
以上是基于 NextJS 前端框架开发时,需要掌握的几大核心思想,其他的无非是项目自身的逻辑或者是一些琐碎的配置。明确你所写的代码运行在哪里,定义好组件的状态,分析清楚组件之间的依赖关系,并利用好 Hook,基本可以让我们在基于 NextJS 前端框架的开发过程中达到事半功倍的效果。
对于后端程序员来说这其实是最大的一个门槛,css 样式这些太琐碎了,以往通过检索文档、试验效果不断尝试的效率太低了。所幸 AI 的出现,让我们有了一个强大的助理,对于页面布局和样式,甚至是动画效果这些完全可以去咨询 ChatGPT。对于 Walletee 项目,我采用的是 Tailwind 样式,动画效果使用的是 Framer Motion,然后大部分的代码都是由 AI 生成,我只是一个 prompt 工程师。
步骤 1:安装 Node.js
前往 Node.js 官网,按照提示下载并安装 Node.js。
步骤 2:创建 Next.js 应用程序
npx create-next-app my-app
cd my-app
步骤 3:安装 tailwindcss 和 framer-motion
npm install tailwindcss framer-motion
步骤 4:配置 tailwindcss
在根目录下创建一个新的 tailwind.config.js 文件,并将以下内容复制粘贴到文件中:
module.exports = {
mode: 'jit',
purge: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}'
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
然后,在 pages/_app.js文件中,导入 tailwindcss:
import "tailwindcss/tailwind.css";
步骤 5:配置 framer-motion
在 pages/_app.js 文件中,导入 framer-motion:
import { AnimatePresence } from "framer-motion";
然后,将 组件包装在 return 中: import "../styles/globals.css"; import "tailwindcss/tailwind.css"; import { AnimatePresence } from "framer-motion"; function MyApp({ Component, pageProps }) { return ( <AnimatePresence exitBeforeEnter> <div className="bg-gray-100 min-h-screen"> <Component {...pageProps} /> </div> </AnimatePresence> ); } export default MyApp; 步骤 6:开发环境运行 npm run dev 步骤 7:生产环境构建及运行 #构建 npm run build #运行 npm start
最近开发了一款多钱包的资产管理平台——Walletee。旨在更快捷、方便地掌握自己在区块链上所拥有的资产,防止日常在钱包应用中频繁切换地址浪费精力。Walletee 的前端采用 NextJs + Tailwind + Framer Motion 实现,前者为前端开发的基础框架,后两者负责前端样式和动画效果。这是我第一次开发前端应用,因此从一个后端开发人员的角度,记录下整个开发脉络以及我认为核心的要点。
首先,我们从一个最常规的问题出发,由浅入深。
问题:当我们在浏览器中输入一个网址后,页面数据是如何传递的?
我们(后端工程师)固有认知是:前端向后端发送 HTTP 请求,后端监听、处理并返回最终的结果。我们通常认为的前端就是浏览器,然而对于浏览器是如何拿到前端的资源(包括 HTML、样式、Js 脚本)的过程是模糊的。
前端服务划分
事实上,前端服务可以分为两块:
服务端
请注意和后端服务端区分,不过二者本质上都是存放、运行代码的服务器。试想一下,前端工程师开发完的代码总需要部署吧,如果没有承载前端代码的服务器,总不可能直接将代码部署到大家的浏览器。因此,对于前端项目其实和后端一样,开发人员需要将自己的代码打包部署到服务器上,我们称服务器上运行的代码为服务端。
那么,当你在浏览器上输入一个网址按住回车键时,浏览器对于需要呈现的内容一无所知,它本质上是发送一个 HTTP 请求,然而这个请求不是到达后端服务器,而是转向前端服务器。前端服务器本质上也是一个后端服务,监听 HTTP 请求,并通过路由,拿到这个 HTTP 请求对应页面的前端资源。
客户端(浏览器)
当浏览器通过上文的 HTTP 请求获取到前端资源之后,然后才开始渲染整个页面的数据,这时候如果有需要一些动态的数据(存储在后端服务器),在浏览器的内部(本质上是从前端服务器拿到的 JS 脚本),才会再次发送 HTTP 请求从 后端服务器 获取,这就回到我们了最早的认知中来。
初级架构

我们继续从一个问题出发。
问题一:服务器之间可以互相通信,为什么前端服务器不把动态内容组装好,然后输出给客户端?

主要是从以下几点考虑的:
性能
将内容部分部分输出可以提高网站性能,因为客户端可以边下载边渲染页面,而不用等待整个页面下载完成。这样可以缩短网页加载时间,提高用户体验。
动态性
如果所有内容都是提前组装好的,那么前端服务器就无法根据客户端请求动态生成内容。
减少前端服务器负担
如果前端服务器一次性将所有内容组装好并输出给客户端,则服务器需要处理大量数据和请求。而将内容分成小部分输出可以帮助减轻服务器负担,提高网站的可用性。
问题二:那是不是说所有的动态组件都交由浏览器处理?
尽管前端服务器将内容分成小部分输出给客户端优化了网站的性能,但在某些情况下,这并不是最优解,此处不细究了。
可以看出,任何事情不能走向极端,这又是一个权衡的问题。
终极架构

浏览器在初始请求页面资源的时候,服务端渲染的资源 会在前端服务器直接生成(通过直接请求后端服务器获取数据)。剩下的资源返回给浏览器后,由浏览器向后端服务器直接索取动态资源,称为 客户端渲染。
因此,在开发前端项目时,一定要定位清楚:所写的代码究竟是在 前端服务器 运行还是在 浏览器 运行,这很重要*。*
场景:多钱包资产管理时,用户可以选择「添加钱包」的功能。假设用户数据库中记载的钱包数为 3,登陆后,资产页面已经将 3 个钱包的资产全部加载完毕。这时用户添加一个新的钱包,弹出添加表单输入信息并提交。
问题:提交以后需要重新刷新整个页面嘛?能否做到只有资产组件的重新加载和渲染?
答案是肯定的。要实现局部组件的动态加载和渲染,就需要用到 NextJS 前端框架为我们提供的组件状态。
状态
针对上述场景,我们定义了两个组件(理解为 Class ):Wallet 和 Asset,前者负责渲染用户的钱包信息,后者负责渲染用户钱包的资产信息,后者的输出依赖于前者。
两者的信息,都定义成了状态,如下所示:
Wallet 组件定义的一个状态
import { useState } from "react";
// 仅示例
const Wallet = () => {
// 组件定义的状态,初始化为一个空的列表
const [wallets, setWallets] = useState([]);
...
return (
<div>
{// Wallet 组件 HTML 代码}
</div>
);
}
Asset 组件定义的一个状态
import { useState } from "react";
// 仅示例
const Asset = () => {
// 组件定义的状态,初始化为一个空的列表
const [assets, setAssets] = useState([]);
...
return (
<div>
{// Asset 组件 HTML 代码}
</div>
);
}
当钱包增加时,逻辑代码(比如表单的 onSubmit 方法)会触发 wallets 状态的变更。
重新渲染
组件状态的变更会导致该组件在页面上重新渲染。因此可以看到钱包组件页面模块中新增了一个钱包地址。
然而,资产组件的状态更新比钱包组件要复杂。当用户新增钱包时,资产组件是需要重新向后端服务器发送 HTTP 请求才能拿到对应钱包的资产。与钱包组件状态变更不同的是,钱包组件的状态变更是直接由「用户行为」触发的,即它是在钱包组件内部表单的 OnSubmit 方法中直接 set 的。然而,我们不太可能在 OnSubmit 方法中,再去发起 HTTP 请求获取最新的 Asset 信息,然后更改 Assent 组件的状态,这样两者耦合就太严重,需要一种更优雅的机制。
那么就要引出一个称为 useEffect 的 React 钩子,这是一个重大神器,细节部分此处不做阐述。它让开发人员可以在组件渲染后完成各种操作。useEffect 钩子具有两个参数:一个是要执行的函数,另一个是一个依赖项数组。当依赖项数组中的任何值发生变化时,该函数将再次运行。如果依赖项数组为空,则该函数仅在组件第一次渲染时运行。
因此,我们可以将 Wallet 组件中的 wallets 状态传递到 Asset 组件中来,当 wallets 状态变更时,触发 useEffect 函数的执行,获取最新的 Asset 信息后,继而触发 assets 状态的变更,从而重新渲染 Asset 组件。
import { useState } from "react";
// 仅示例
const Asset = ({wallets}) => {
// 组件定义的状态
const [assets, setAssets] = useState([]);
...
// 动态加载
useEffect(() => {
getAssetsAPI(chain.id, DataType.PROTO).then((assets) =>
setAssets(assets)
);
}, [wallets]);
...
return (
<div>
{// Asset 组件 HTML 代码}
</div>
);
}
以上是基于 NextJS 前端框架开发时,需要掌握的几大核心思想,其他的无非是项目自身的逻辑或者是一些琐碎的配置。明确你所写的代码运行在哪里,定义好组件的状态,分析清楚组件之间的依赖关系,并利用好 Hook,基本可以让我们在基于 NextJS 前端框架的开发过程中达到事半功倍的效果。
对于后端程序员来说这其实是最大的一个门槛,css 样式这些太琐碎了,以往通过检索文档、试验效果不断尝试的效率太低了。所幸 AI 的出现,让我们有了一个强大的助理,对于页面布局和样式,甚至是动画效果这些完全可以去咨询 ChatGPT。对于 Walletee 项目,我采用的是 Tailwind 样式,动画效果使用的是 Framer Motion,然后大部分的代码都是由 AI 生成,我只是一个 prompt 工程师。
步骤 1:安装 Node.js
前往 Node.js 官网,按照提示下载并安装 Node.js。
步骤 2:创建 Next.js 应用程序
npx create-next-app my-app
cd my-app
步骤 3:安装 tailwindcss 和 framer-motion
npm install tailwindcss framer-motion
步骤 4:配置 tailwindcss
在根目录下创建一个新的 tailwind.config.js 文件,并将以下内容复制粘贴到文件中:
module.exports = {
mode: 'jit',
purge: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}'
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
然后,在 pages/_app.js文件中,导入 tailwindcss:
import "tailwindcss/tailwind.css";
步骤 5:配置 framer-motion
在 pages/_app.js 文件中,导入 framer-motion:
import { AnimatePresence } from "framer-motion";
然后,将 组件包装在 return 中: import "../styles/globals.css"; import "tailwindcss/tailwind.css"; import { AnimatePresence } from "framer-motion"; function MyApp({ Component, pageProps }) { return ( <AnimatePresence exitBeforeEnter> <div className="bg-gray-100 min-h-screen"> <Component {...pageProps} /> </div> </AnimatePresence> ); } export default MyApp; 步骤 6:开发环境运行 npm run dev 步骤 7:生产环境构建及运行 #构建 npm run build #运行 npm start
No activity yet