从jQuery迁移到Next.js的一个指南
多年来,jQuery一直为开发者提供良好的服务。然而,库(如React)和框架(如Next.js)现在为我们带来了更多的现代功能,帮助我们提高代码的性能和可维护性。本指南将告诉你如何使用Next.js重写你的jQuery网站,以利用所有这些新功能,如客户端路由,以实现更平滑的过渡,以及将代码分离成组件的能力,使其更可重复使用。
开始使用
开始使用Next.js的最简单方法是运行npx create-next-app 。这将为你搭建一个项目的脚手架。然而,为了理解这个命令的作用,我们将从头开始创建我们的应用程序。
首先,我们将使用npm init 创建我们的 Next.js 项目。你可以使用默认设置,因为我们稍后会改变它们。然后,我们要用安装React和Next.js:
npm install react react-dom next
接下来,我们可以打开package.json 文件,将默认的scripts 替换为:
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
这允许你运行npm run dev 来启动开发服务器;npm run build 来构建你的应用程序;npm run start 来启动该构建应用程序的服务器。
要添加页面--就像你使用jQuery的index.html ,创建一个名为pages 的目录,并在其中创建一个名为index.jsx 的文件。在这个文件中,放置以下代码:
export default function Index() {
return <h1>Hello World</h1> ;
}
现在,通过运行npm run start ,并导航到localhost:3000 ,你应该看到一个h1 标签显示。这个函数的名字并不重要,所以你可以随心所欲地调用它。但是,不要使用一个匿名的箭头函数,因为这将阻止快速刷新的工作。
CSS
在jQuery中,你可以按页面指定CSS,为不同的页面导入不同的样式表。在Next.js中也可以这样做,使用next/head 组件和与jQuery相同的link 标签。总之,在Next.js中,有更多的性能友好的方法来实现。
全局样式表
第一种方法是使用全局样式表。要做到这一点,我们需要创建一个自定义的App ,在pages 目录下创建文件_app.js 。这个文件的起点如下:
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
在这个文件的顶部,你可以添加一个导入语句,导入任何你想要的CSS文件。例如,如果你在根层创建了一个单独的文件夹,名为styles ,并将main.css 放在其中,那么你就可以添加:
import "../styles/main.css"
现在,无论你在这个文件里放了什么,都会在你的应用程序中应用。
CSS模块
下一个选项是CSS模块--它允许你在应用程序的任何地方指定CSS。它们将从你提供的类中创建唯一的类名,因此你可以在应用程序的代码中的多个地方使用同一个类名。
扩展最初的hello world的例子,你可以在同一目录下创建一个文件index.module.css ,然后编写导入:
import styles from "./index.module.css"
之后,如果你要在CSS文件中定义一个heading 类,你可以这样做:
export default function Index() {
return <h1 className={styles.heading}>Hello World</h1> ;
}
而这些样式将只应用于该元素。
样式化的JSX
最后一个内置选项是样式化的JSX。这最类似于在你的页面顶部包括一个<style> 标签来定义一些样式。只需在<style> 标签中加入jsx ,并在里面使用一个模板字符串,像这样:
<style jsx>{`
.heading {
font-weight: 700
`}</style>
这个选项的好处是可以在运行时改变。例如,如果你想在你的组件道具中提供字体重量,你可以这样做:
<style jsx>{`
.heading{
font-weight: ${props.fontWeight}
`}</style>
这种方法的一个缺点是,它在你的应用程序中引入了额外的运行时JavaScript,使其大小增加了12kb(压缩后为3kb)。
事件
在jQuery中,你可能已经设置了事件来响应DOM元素。为了给你一个概念,你可能想在一个p 标签被点击时执行代码,并像这样做。
$( "p" ).click(function() {
console.log( "You clicked a paragraph!" );
});
相反,React使用事件处理程序--你可能在HTML中见过--像onclick 。注意,React使用camelCase来代替,因此onclick 应该被引用为onClick 。因此,将这个小例子改写成React会是这样的。
export default function Index() {
function clickParagraph(){
console.log("You clicked a paragraph!");
}
return <p onClick={clickParagraph}>Hello World</p>;
}
每种方法都有其优点和缺点。在jQuery中,很容易让所有的段落都发生一些事情,而在React中,你必须指定每个段落。然而,对于更大的代码库来说,必须指定使你很容易看到与任何元素的交互会发生什么,你可能已经忘记了jQuery的功能。
效果
效果是在jQuery中用来显示和隐藏内容的,你可能已经有这样的东西了。
$( "p" ).hide();
在React中,这种行为是用条件渲染实现的。你可以通过把它与我们刚才看到的事件的替换结合起来看。
import {useState} from "react"
export default function Index() {
const [show, setShow] = useState(true);
function clickButton(){
setShow(false)
}
return (
<div>
<h1>Hello world</h1>
{show && <button onClick={clickButton}>Click me</button>}
</div>
)
}
当你点击这个按钮时,它将把show 的值改为false ,所以,这个语句不会渲染任何东西。这可以用条件运算符来扩展,以显示一件事或另一件事,取决于这样的值。
show ? <p>Show this if show is true</p> : <p>Show this if show is false</p>
数据获取
在jQuery中,Ajax被用来获取外部数据而不需要重新加载。在React中,这可以通过使用useEffect 钩子来完成。在这个例子中,我们将在页面加载时从一个公共API中获取汇率。
import { useState, useEffect } from "react";
export default function Index() {
const [er, setEr] = useState(true);
useEffect(async () => {
const result = await fetch("https://api.exchangerate.host/latest");
const exchangerate = await result.json();
setEr(exchangerate.rates["GBP"]);
}, []);
return (
<div>
<h1>Hello world</h1>
<p>Exchange rate: {er}</p>
</div>
);
}
useEffect 这需要一个函数和一个依赖数组。该函数进行数据获取,使用 作为 API的异步。然后,我们可以在其中设置任何我们想要的状态,它将在页面上被更新。依赖性数组决定了哪些值的变化将运行该函数。在这种情况下,它被设置为一个空数组,这意味着该函数将只在页面首次加载时运行。async fetch
除此之外,Next.js还提供了在服务器上或在构建时获取数据的选项。对于构建时的数据获取,可以使用函数getStaticProps 。这个函数提高了性能,因为数据可以与页面一起提供,而不是等待外部服务。要使用它,请在一个页面中创建这个函数,因为它在组件中不起作用。
export async function getStaticProps() {
return {
props: {},
}
}
你可以在返回之前执行任何你想要的数据获取,之后在props ,将数据传递给页面 - 然后,数据被提供给页面,并可以在props下访问。
通过将函数名称从getStaticProps 替换为getServerSideProps ,该函数将在每个请求中被调用,如果需要,你可以灵活地使用Node.js函数。它还允许你在服务器上提出许多数据请求,并对其进行处理以减少客户端使用的带宽。
你也可以选择这两者之间的中间地带,叫做 递增的静态再生.这个选项将以与getStaticProps 相同的方式生成一个静态页面,但它允许你指定一个重新验证期--当有请求进来时,它将最多按照你指定的周期重新生成页面。要做到这一点,除了道具之外,你还应该包括一个revalidate ,其中包括你想要的时间(秒)。
对象变成DOM元素
使用jQuery,你必须小心使用哪种方法来把一个对象变成DOM元素。最常见的例子是创建一个项目列表,因为用jQuery,在项目上的循环会把每个项目一个一个地添加到DOM中。在React中,虚拟DOM被用来创建新状态与当前状态的差异。这意味着,尽管在一个循环中添加项目,但它们是作为一个操作被添加到真实的DOM中。
这是用JavaScript中的map 函数完成的,你可以将每个项目映射到一些JSX。
export default function Index() {
const fruits = ["Apple", "Orange", "Pear"];
return (
<div>
<h1>Hello world</h1>
<ul>
{fruits.map((fruit) => (
<li key={fruit}>{fruit}</li>
))}
</ul>
</div>
);
}
注意,map 里面的元素需要一个key 的道具。这是在上面讨论的差异化过程中使用的,使React容易区分每个元素,所以每个元素都应该是唯一的。
延迟(Deffereds
jQuery中deferreds的使用可以用本地的JavaScriptpromise 功能来代替。deffereds的语法设计是为了反映承诺的功能,所以语法应该是熟悉的,不需要太多改动。一个可能使用deffereds的例子是在数据获取方面。如果你用JavaScript中的fetch 方法来做,那么你可以在fetch的末尾添加一个.then ,因为它返回一个承诺。这段代码只有在获取完成后才会运行,因此数据(或错误)会出现。你可以在这里看到这个功能的使用。
fetch("example.com")
.then((response) => {
console.log(response)
})
.catch((error) => {
console.error(error)
})
这将获取example.com ,并记录获取的响应,除非发生错误--在这种情况下,它将被记录为一个错误。
除了这种语法外,还可以使用较新的async/await 语法。这些需要一个定义为a``sync 的函数,其方式与你可能导出一个函数的方式相同。你可以像这样声明它:
async function myFunction(){
return
}
在这个函数里面,你可以通过在它们前面放置await ,来调用进一步的异步函数,例如:
async function myFunction(){
const data = await fetch("example.com")
return data
}
这段代码将返回一个承诺,该承诺将在获取数据时解决,所以它需要在异步函数里面调用,以等待结果。然而,为了同时捕捉错误,你需要写一个条件来检查响应状态--如果data.ok 不是真的,应该抛出一个错误。然后,你可以用一个try catch块来包装这些离开的语句,而不是使用.catch 。你可以在这篇文章中阅读更多关于这些方法的内容。
改进措施
路由
Next.js使用文件系统路由,这与传统网站中使用不同的.html 页面非常相似。然而,这个系统还提供了更多的功能,提供动态路由,并允许一个页面在一系列的URL下被访问。
例如,如果你有一个博客,你可能会把你的所有文件放在/blog/* ,在blog 文件夹内创建一个文件[slug].jsx - 这将允许该内容在blog 下的所有页面被提供。然后,你可以使用Next.js中的路由器来查找被导航到的路由,像这样:
const router = useRouter()
const { slug } = router.query
API路由
API路由允许你在Next.js应用程序中编写你的后端。要使用这些路由,请在你的pages 目录中创建一个api 文件夹--现在,在里面创建的任何文件都将在服务器上运行,而不是像其他页面那样在客户端运行。
要开始使用这些,你需要从文件中导出一个默认函数,这可以接受两个参数。第一个将是传入的请求,第二个将让你创建响应。一个基本的API路由可以这样写。
export default function handler(request, response) {
response.status(200).json({ magazine: 'Smashing' })
}
局限性
jQuery UI
你可以在你的应用程序中使用jQuery UI作为用户界面,但React并没有提供这样的官方UI库。尽管如此,已经产生了一系列的替代品。其中最流行的两个是Reach UI和React Aria。这两个替代品都非常注重可访问性,确保你创建的项目能被更多的用户使用。
动画
虽然你可以使用条件渲染来代替效果,但这并不能提供所有相同的功能,因为你不能做诸如淡出内容等事情。一个有助于提供这种功能的库是React Transition Group--它允许你定义进入和退出的过渡。
总结
从jQuery迁移到Next.js是一个大工程,尤其是对于大的代码库。然而,这种迁移允许你使用较新的概念(如在构建时获取数据),并为你设置了简单的迁移路径,以获得新版本的React和Next.js - 以及它们带来的功能。
React可以帮助你更好地组织你的代码(这对大型代码库来说特别重要),并通过使用虚拟DOM带来大幅度的性能提升。总的来说,我相信从jQuery迁移到Next.js是值得的,我希望如果你决定迁移,你能享受React和Next.js所提供的所有功能。