记 20 React + ts + tailWind 实战 ( 一 )

362 阅读2分钟

学习一 构建工具 (Webpack、Vite或Next.js团队开发的TurboPack

由于开发与部署脱离,所以前端需要一个构建工具,将开发的代码构建为最终的产物. (比如将TypeScript转化为普通的JavaScript,因为浏览器无法识别TypeScript,TypeScript只存在开发阶段,在最终代码中是不可能存在的)

ebpack或Vite包括Next.js团队开发的TurboPack都是为此而生的构建工具 Vite是一个流行的替代方案,它提供了更快的构建速度和更多的灵活性,适合需要快速开发和部署的项目‌ 如果你需要更多的自定义构建选项,Turbopack可能是一个不错的选择;如果你更注重快速开发和部署,Vite可能更适合你。

学习二 Next.js

Next.js是一个用于构建全栈Web应用程序的轻量级的React服务端渲染应用框架。可以使用React组件来构建用户界面,使用Next.js来实现额外的功能和优化。Next.js基于文件系统的路由器,构建在服务器组件之上,支持布局、嵌套路由、加载状态、错误处理等。

其执行命令有:开发的时候使用 npm run dev。部署的时候先使用 npm run build 构建生产代码,再执行 npm run start运行生产项目。运行 npm run lint则会执行 ESLint 语法检查。

学习三 Drizzle ORM

Drizzle ORM 是一个专为 SQL 数据库设计的 TypeScript ORM(对象关系映射)库,它提供了全面类型安全性和 SQL 语法,支持自动化迁移生成和零依赖,适用于各种 SQL 数据库

学习四 使用模板字符串(反引号)

模板字符串可以允许字符串中嵌入 JavaScript 表达式。

        <div className='h-screen border shadow pt-5'>{
            menuList.map((item) => {
                return (
                    <div key={item.id} className='p-2 '>
                        <div onClick={() => { console.log('clicked', path, item.path, path === item.path) }} className={`flex items-center gap-5 p-4 rounded-lg  cursor-pointer  ${path === item.path ? 'bg-primary text-white hover:text-white hover:bg-primary' : 'hover:text-gray-600 hover:bg-gray-100'}`}>
                            <item.icon className='w-5 h-5' />
                            <p>{item.name}</p>
                        </div>
                    </div>
                )
            })
        }</div>

视频up

实战前期准备(这个up用了非常多的组件库,果然复制和粘贴是程序员创造伟大产品的左膀和右臂)

  • 使用 npx create-next-app@latest 来创建新的next.js项目
  • 使用任意你喜欢的组件库(我观看的 up在这里用了shadcnnpx shadcn@latest init 安装)
  • HyperUI
  • daisyUI是一个可定制的TailwindCSS的组件库,可以说是TailwindCSS的插件,这个组件库避免了堆砌class名。使用 npm i daisyui 安装,并配置到 tailwind.config.js
module.exports = {
  //...
  plugins: [require("daisyui")],
}
  • app文件夹下创建新的下划线_开头的私有文件夹(如_components),这样该文件夹及其所有子文件夹都不会被包含在Next.js的路由中。可以用来组织代码,将 UI 逻辑与路由逻辑分离
  • 将需要的svg图标以文件形式保存至public文件夹中

实战部分

1. 在tailWind的config文件中,找到theme > extend: > color修改项目主题色

2. 在_components文件夹中创建Header组件引入Layout (头部主导航)

      <body>
          <Header />
          {children}
      </body>

3. 在_components文件夹中创建View组件引入page (主导航的视图切换 直接使用hyperUI组件库中的banner)

import React from 'react'

function View() {
    return (
        <section className="bg-gray-50">
            <div className="mx-auto max-w-screen-xl px-4 py-32 lg:flex lg:h-screen ">
                <div className="mx-auto max-w-xl text-center">
                    <h1 className="text-3xl font-extrabold sm:text-5xl">
                        Create your Form.
                        <strong className="font-extrabold text-primary sm:block"> In Seconds. </strong>
                    </h1>

                    <p className="mt-4 sm:text-xl/relaxed text-gray-400">
                        Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt illo tenetur fuga ducimus
                        numquam ea!
                    </p>

                    <div className="mt-8 flex flex-wrap justify-center gap-4">
                        <a
                            className="block w-full rounded bg-primary px-12 py-3 text-sm font-medium text-white shadow hover:bg-purple-500  focus:outline-none focus:ring active:bg-primary sm:w-auto"
                            href="/dashboard"
                        >
                            + Create Form
                        </a>

                        <a
                            className="block w-full rounded px-12 py-3 text-sm font-medium text-primary shadow hover:text-purple-600 focus:outline-none focus:ring active:text-red-500 sm:w-auto"
                            href="#"
                        >
                            Learn More
                        </a>
                    </div>
                </div>
            </div>
        </section>
    )
}

export default View
export default function Home() {
  return (
    <>
      <View />
    </>
  );
}

4. 使用托管身份验证提供程序 Clerk

  • 注册并登录 clerk.com,定制你自己的登陆页面,Clerk 会自动提供要添加到 Next 应用程序的 API 密钥。
  • npm install @clerk/nextjs 安装clerk依赖项
  • 在根目录下添加.env.local 密钥的环境变量配置文件,文件内容是clerk给出的公钥(前端应用程序与后端服务进行通信时的身份验证)和私钥(后端服务进行身份验证和数据加密。)
  • 创建middleware中间件文件,文件需要存放在src文件下,跟着up放在根目录后报错了,用于配置身份验证程序,并将其添加到 /src/app/layout.tsx 文件当中。
    //引入文件并使用程序组件包裹整个html
    <ClerkProvider> </ClerkProvider>
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])

export default clerkMiddleware(async (auth, req) => {
    if (isProtectedRoute(req)) await auth.protect()
})

export const config = {
    matcher: [
        // Skip Next.js internals and all static files, unless found in search params
        '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
        // Always run for API routes
        '/(api|trpc)(.*)',
    ],
};
  • 配置自己的登录页 按照官网提示配置文件目录 app/(auth)/sign-up/[[...sign-up]]/page.tsx、设置代码并配置环境变量
import { SignUp } from '@clerk/nextjs'

export default function Page() {
  return <SignUp />
}
  • 现在可以使用clerk的sign页面注册并登录你的项目了,登陆后在header组件中配置clerk自带的UserButton组件(个人资料,不登陆不会显示),里面自带账户管理及登出选项。
'use client'
import { Button } from '@/components/ui/button'
import Image from 'next/image'
import React from 'react'
import { SignInButton, UserButton, useUser } from '@clerk/nextjs'
import  Link  from 'next/link'

function header() {
    const { isSignedIn } = useUser()
    return (
        <div className='p-5 border shadow-sm'>
            <div className='flex items-center justify-between'>
                <Image src={'/logo.svg'} alt="logo" width={180} height={50} />
                <div>
                    {isSignedIn ?
                        <div className='flex items-center gap-5'>
                            <Link href={'/dashboard'} >
                                <Button variant='outline'>Dashboard</Button>
                            </Link>
                            <UserButton />
                        </div> :
                        <SignInButton>
                            <Button >Get Started</Button>
                        </SignInButton>
                    }
                </div>
            </div>
        </div>
    )
}

export default header
  • 用户管理,在这里登录你的clerk账号,管理你的用户,还可以配置域名,直接部署到生产环境

5. 新建仪表盘(dashboard)文件夹及其内各组件,路由跳转/dashboard 就会跳到这个文件夹内的page页,文件夹内含layout布局页(子页面内容需包裹在 SignedIn 组件内)、page主页面、_components组件文件夹(createForm(AI制表)、siderBar(侧边栏)),配置各跳转链接及各页面样式

layout

'use client'
import { SignedIn } from '@clerk/nextjs';
import SiderBar from './_components/siderBar';
import React from 'react'

function DashboardLayout({ children }) {
    return (
        <SignedIn >
            <div>
                <div className='w-64 fixed '>
                    <SiderBar />
                </div>
                <div className='ml-64'>
                    {children}
                </div>
            </div>
        </SignedIn>
    )
}

export default DashboardLayout

sidebar

import { ChartLine, CircleFadingArrowUp, MessageCircleMore, NotepadTextDashed } from 'lucide-react'
import { usePathname } from 'next/navigation'
import React, { useEffect } from 'react'
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress"

function SiderBar() {
    const menuList = [
        {
            id: 1,
            name: 'My Forms',
            icon: NotepadTextDashed,
            path: '/dashboard'
        },
        {
            id: 2,
            name: 'Responses',
            icon: MessageCircleMore,
            path: '/dashboard/responses'
        },
        {
            id: 3,
            name: 'Analytics',
            icon: ChartLine,
            path: '/dashboard/analytics'
        },
        {
            id: 4,
            name: 'Upgrade',
            icon: CircleFadingArrowUp,
            path: '/dashboard/upgrade'
        }
    ]

    const path = usePathname()
    useEffect(() => {
        console.log(path);
    }, [path])
    return (
        <div className='h-screen border shadow pt-5'>{
            menuList.map((item) => {
                return (
                    <div key={item.id} className='p-2 '>
                        <div onClick={() => { console.log('clicked', path, item.path, path === item.path) }} className={`flex items-center gap-5 p-4 rounded-lg  cursor-pointer  ${path === item.path ? 'bg-primary text-white hover:text-white hover:bg-primary' : 'hover:text-gray-600 hover:bg-gray-100'}`}>
                            <item.icon className='w-5 h-5' />
                            <p>{item.name}</p>
                        </div>
                    </div>
                )
            })
        }
            <div className='fixed items-center w-64 bottom-4 p-6'>
                <Button className='w-full'> + Create From</Button>
                <div className='mt-5 h-1'>
                    <Progress value={33} />
                </div>
                <h2 className='text-gray-400 mt-5'><strong>2</strong> out of <strong>3</strong> files are created</h2>
                <hr className='mt-2 mb-2'/>
                <h2 className='text-xs text-gray-400 mt-2'> upgrade your plan for unlimited AI form builder</h2>
            </div>
        </div>
    )
}

export default SiderBar

page

import React from 'react'
import CreateForm from './_components/createForm';


function Dashboard() {
  return (
    <div>
      <h2 className='font-bold text-3xl flex items-center justify-between gap-20 p-5'>Dashboard
        <CreateForm></CreateForm>
      </h2>
    </div>
  )
}

export default Dashboard

createForm待续 ...