用只有一个文件的方式,将Next.js的web应用程序变得更加轻巧

我们所说的“轻量”是指内存使用量较少的意思。
基于 Node.js 的应用程序往往容易占用大量内存。

尤其困扰的是,这种需要启动多个应用程序才能进行操作确认的开发。而且,PC的规格也有限制,当我们要进行“操作确认”时,可能会遇到“必须冻结并重新启动…”以及“必须关闭其他正在运行的应用程序…”等情况,还需要额外小心处理。

这次在进行前端开发时,有机会考虑构建方面的问题。由于想到了”还有这种模式吗?”,于是我将它们总结起来。

前提条件

    フロントエンド の動きとして SSR(=Server Side Rendering)処理 がないこと(CSR, SSG であること)

实现的想象图

一般而言,如果是使用Next.js创建的应用程序,我想您会使用Next.js的服务器启动命令($ next start)。换句话说,应用程序将在Node.js环境中运行。

    1. $ 下一步构建

 

    $ 下一步启动

使用上述的方法肯定更容易,但我们正在尝试的是另一种增加了额外步骤的方法。然而,我们将尽量减少这一额外步骤的方法。

具体而言,步骤如下:

    1. 下一步构建

 

    1. 将构建后的文件部署到服务器(本次使用的是Go语言编写的服务器)

 

    启动Go语言编写的服务器
構成図.jpg

当移动时,图像将如上所示。
唯一的不同是服务器图中的气泡(上方为「Node.js」,下方为「golang」)。

只需将已经构建完成的文件部署到golang环境中,不必要求必须是使用golang,也可以使用alpine等其他操作系统。

制作应用程序

我将创建一个Next.js应用程序。由于Next.js有很多示例,因此我认为可以参考它们来创建应用程序。还有其他创业类的文章,可以通过搜索来找到,这里就不一一介绍了。

 

Next.js 的配置

 

参照下面的公式设置,在 next.config.js 文件中设置 output: ‘export’。
执行 $ nuxt build,生成静态文件。

前端服务器的设置

如同图示所示,我们需要准备Golang的开发环境。关于设置方面,我想可以在下方等地方找到许多信息,所以我不在此详述。

 

关于使用Golang制作的服务器,这次我们还将使用Echo框架(选择原因没有特别的理由)。

 

主要.go

请复制下面的内容,并以任何文件名保存。然后执行命令 “$ go build <保存的文件名>”。

※ 我省略了例如 $ go mod init 等必要步骤。(Note: I have omitted necessary steps such as $ go mod init)

import (
    "errors"
    "net/http"
    "net/url"
    "os"
    "path"
    "strings"
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    echoServer := echo.New()
    echoServer.Use(nextjsResourceRouter)
    echoServer.Start(":3000")
}
func nextjsResourceRouter(next echo.HandlerFunc) echo.HandlerFunc {
    const outputRootDirName = "out"
    const renderTargetFileSystem = http.Dir(outputRootDirName)
    return func(c echo.Context) (err error) {
        reqPath, err := getRequestPath(c)
        if err != nil {
            return err
        }
        reqName := path.Join(".", path.Clean("/"+reqPath))
        file, err := renderTargetFileSystem.Open(reqName)
        if err != nil {
            if !os.IsNotExist(err) {
                return err
            }
            if err = next(c); err == nil {
                return nil
            }
            var he *echo.HTTPError
            if !(errors.As(err, &he) && he.Code == http.StatusNotFound) {
                return err
            }
            file, err = findRequestFile(renderTargetFileSystem, reqName)
            if err != nil {
                return err
            }
        }
        defer file.Close()
        http.ServeContent(c.Response(), c.Request(), file.Name(), file.ModTime(), file)
        return nil
    }
}
func getRequestPath(c echo.Context) (string, error) {
    p := c.Request().URL.Path
    if strings.HasSuffix(c.Path(), "*") {
        p = c.Param("*")
    }
    return url.PathUnescape(p)
}
func findRequestFile(fs http.FileSystem, name string) (http.File, error) {
    file, err := fs.Open(path.Join(".", name))
    if err != nil {
        file, err = fs.Open(path.Join(".", name+".html"))
    }
    if err != nil {
        return nil, err
    }
    info, err := file.Stat()
    if err != nil {
        file.Close()
        return nil, err
    }
    if !info.IsDir() {
        return file, nil
    }
    file, err = fs.Open(path.Join(name, "index.html"))
    if err == nil {
        return file, nil
    }
    return fs.Open(path.Join(name, name+".html"))
}

填补1

Golang的echo框架中,有一个叫做”static”的中间件。然而,Next.js的构建方式有些特别,这个中间件并不能很好地处理。

假设有以下这样的文件夹结构。

hoge/
  page.tsx
  fuga/
    page.tsx
piyo/
  page.tsx
page.tsx

在这种情况下,将生成以下文件:”hoge.html”,”hoge/fuga.html”,”piyo.html”和”index.html”。

只要能生成”hoge/index.html”,”hoge/fuga/index.html”和”piyo/index.html”,就可以直接使用了,就不需要写这篇文章了。但是,我们需要”对预期的请求进行分配,以便返回正确的HTML文件”。

因此,我們參考並重構了靜態的中間件,使其變成了這個形式。

※ 这是最小设置,如果在正式环境下运行,我认为需要进行一些更改。

启动设置

然后,只需将”go构建的文件(即服务器)”和”Next.js构建的文件夹”放置在同一级目录,并启动服务器即可。

即使在实际部署到生产环境时,只要利用”用于Next.js构建的Docker”,”用于go服务器构建的Docker”,以及”用于运行部署的Docker”这三个多阶段构建,就可以在一个Docker文件中创建Docker镜像。

优点和缺点 hé

轻便

这个优点就像标题所写的那样是“变轻”。进行了非常简单的验证(只需要用for循环多次curl命令,然后使用$ docker stats –no-stream来查看),并计算了平均值,结果如下所示。

CPUMEMNET I/Onode:20.2.0-alpine で $ next start30.14%139.6MiB485.8kB / 5.4MBalpine:3.17.2 で $ ./main3.64%6.14MiB485.8kB / 6.06MB

高度的可扩展性

当看到这个配置的时候,我认为有些人可能会想,“为什么不用Nginx呢?”正是如此!实际上,官方文档中也有写到,请修改Nginx的配置。

只是假设的情况,如果需要进行访问控制、额外的访问日志记录等,可能只依靠Nginx 实现会很麻烦。

如果我们像这次一样选择用golang来开发,添加服务器处理也会容易很多…这是出于这样的考虑。
虽然有需要掌握golang知识的缺点,但我认为保持可定制化的状态非常重要。

另外,请您查看源代码后会发现以下情况:如果请求的文件不存在,将首先执行路由处理,因此实现“对特定URL处理进行优先处理”的状态也变得非常容易。

费时费力

一个缺点是,在仅使用Next.js进行开发时,流程比较复杂。
作为解决之一的方法是,虽然会出现与实际生产环境不同的差异状态,
但首先使用”$ next dev”命令在Node.js上进行开发运行。
在最终确认时进行build,然后在Golang服务器上运行。
通过这样的流程,问题可能会得到一些解决。

最终

如果要给它起一个不同的标题,可以这样命名为『为 echo 创建适用于 Next.js 的静态中间件』。
虽然这次我们是以 Next.js 为基础来总结的,但是在将 HTML 和 JS 构建为客户端库(如 Svelte 等)时,也可以应用相同的方法。

如果考虑到实际环境下的运作,并且考虑到本地环境的压力是至关重要的。希望能够帮助您创建出愉快的网络应用程序。

广告
将在 10 秒后关闭
bannerAds