Remix框架初体验1
2022-04-04
其实很早就对remix感兴趣了,不过那时候的remix是封闭的,只给付费人员使用,不舍花钱的我只能馋着了。不过去年10月份remix开源后,也有机会一探究竟了
remix是大名鼎鼎的react-router的作者Ryan Florence和MICHAEL JACKSON一起创建的。最近又加入了React社区非常有名的Kent。Kent的个人博客上也有蛮多文章介绍remix的,感兴趣的可以去看一下。我会从个人实际项目出发,讲一讲remix的设计理念和特性
项目介绍
先简单介绍一下这个项目。最近一直在学英语,学习方法是找一些youtube的视频跟读,就需要一边播放视频,一边听写出文本,然后跟着读。所以就用remix实现了这么一个网站https://shadowing-henna.vercel.app/
项目比较简单,就是一个列表页,一个新增页,以及编辑页。但能覆盖基本的CRU - create, read, update场景。我们跟着这些页面,来看看remix的一些基本特性
列表页
以上就是列表页的代码了。除了export
出react组件外,还export
出了一个loader
方法,这就是remix获取服务端数据的方法。
remix是一个全栈的react框架,loader
是直接运行在服务端的,也就是说我们可以在loader
里使用服务端能力,比如读取本地文件,访问数据库等等。像我们这个地方就是直接读取数据库里的数据然后返回。组件里的useLoaderData
方法就是用来获取loader
中返回的数据的。
当我们的组件依赖服务端数据,需要做异步请求时,通常都需要考虑这么几个问题 - loading怎么处理?error怎么处理?子组件需要用的话数据怎么传递?
loading
remix走的是SSR路线,即使在做客户端页面跳转时,remix也会保证在loader
执行成功后,才会开始渲染我们的组件。所以loading
状态默认不用处理。Goodbye if (loading) return <div>loaindg...</div>
error
当接口报错时又如何处理呢?remix拥抱了react的做法 - ErrorBoundary。我们可以export
出一个ErrorBoundary
组件,处理异常
数据共享
路由下的所有子组件,都可以直接调用用useLoaderData
方法获取这份数据。所以基本上我们也不再需要引入其他的状态管理方案了
新增页
remix又是如何做数据更新的呢?大家还记得大明湖畔的<form action="">
吗?
import { redirect, useTransition, useActionData, json } from 'remix';import { useState, useEffect } from 'react';import supabase from '~/utils/supabase';export const action = async ({ request }) => { const body = await request.formData(); const title = body.get('title'); const content = body.get('content') const vid = body.get('vid') const { data, error } = await supabase.from('shadows') .insert([ { title, content, vid, type: 1 }, ]) if (error) return json(error.message || `Something went wrong!`, { status: 500 }) return redirect(`/${data[0].id}`);}export default function Index() { const [vid, setVid] = useState(); const handleVidChange = e => { const url = new URL(e.target.value); setVid(url.searchParams.get('v')) } return ( <form method="post" className="form"> <textarea name="content" className="editor" /> <div> <input required name="title" type="text" placeholder="title" /> <input autoFocus required name="url" placeholder="youtube video link" type="text" onChange={handleVidChange} /> <input name="vid" hidden value={vid} /> <button className="button" type="submit">Save</button> <div className="video"> {vid && <lite-youtube videoid={vid} />} </div> </div> </form> );}
就是最普通的form
,唯一不同的是export
出了一个action
方法,action
也是运行在服务端的。这就是remix处理表单的方法 - 拥抱标准。当表单提交时,服务端会收到客户端请求,action
方法被执行,通过request.formData
可以拿到表单的数据。这里的request
, reponse
等用的都是fetch API的标准实现
这一点也体现出remix的一个设计理念,拥抱web标准
拥抱标准有什么好处呢?表单提交逻辑在js
被禁用时依然可以正常工作。大家可能觉得有点多余,什么情况下js
会被禁用啊?那js
加载过程中呢?用户不用等你的js
加载完成,就可以直接提交表单。做过性能优化的应该都能懂这一点有多么重要~
拥抱标准的另一个好处就是,你学习的知识是可迁移的,而不是跟某个框架绑定的。举个简单例子,大家现在可能会用各种表单库或ui库去做表单开发,但你学习的是这个库的api。当你没法使用这个库时,这部分知识就作用不大了。举个具体例子,当一个开发中后台,用惯了antd表单的同学,被安排去开发h5页面,且涉及表单的时候,这个同学会怎么写表单呢?
这也是remix文档上所写的 - 在用remix开发应用时,可能大部分时候你都在看MDN的文档
remix pattern
我们来简单总结一下remix pattern
// 读数据export async function loader({ request }) { ...}// 表单提交 - 写数据export async function action({ request }) { ...}export default function () { // 取数据 const data = useLoaderData(); ...}
以上就基本是remix给我们规范的读数据和写数据的方法。不过这种模式只能在路由组件中使用。
这也是remix对于「数据在哪里获取」这个问题给出的答案 - 统一在路由这一层获取。
配合上react router提供的nested routes,可以避免一个页面所有的数据都在集中在一个loader
中。
这个pattern
我认为有几点非常好
- 作为一个全栈的react框架,既拥有的服务端的能力,同时又和前端的代码够内聚,使用和实现维护在一个地方,开发体验非常好
- 数据和ui做了很好地分离 - 组件变得非常简单,基本就和一个接收props的组件一样,且代码都是同步的,思路不用在异步和同步间切换
- 不用在组件内处理loading和error,用更声明式的方式替代过程式的
if (loading)
,if(errro)
代码 - 拥抱标准
当然,我上面说的都是DX,开发体验层面的。remix还提供了很多用户体验层面的优化,这个在后续的文章中会为大家详细介绍,不在这里展开
如何引入样式
目前用下来,remix中处理样式的方法,是我唯一感到开发体验不太友好的地方
import styles from "~/styles/index.css";export function links() { return [{ rel: "stylesheet", href: styles }];}
如上代码所示,你可以通过在路由组件中export
出links
方法来引入样式。对于路由组件的样式来讲,这样写是可以接受的。但是对于通用组件的样式呢?官方提供了这么一个例子
如果你想使用这个组件,你需要在路由组件中显示的引入这个样式
想象一下当我一个页面用了很多通用组件的时候。。。
另外remix默认是不支持css预处理器和后处理器的,如果想引入的话,需要自己处理。
以及按remix的设计,import
出来的style
需要是一个url。所以和目前社区上的一些css in js
库可能无法兼容,比如vanilla-extract。
由于目前我还没有在样式复杂的项目上体验remix,所以具体的开发体验还有待验证
浏览器兼容性
国内当前的环境,当开发C端,特别是移动端页面时,还是需要考虑兼容性问题的。remix只能运行在支持ES Modules的浏览器上。对于国内的环境是一个挑战
不过如上文中提到的,即使禁用js,remix应用依然是可以运行,提供基本的功能的。这也是remix遵循的另一个理念,Progressive enhancement - 渐进增强,即每个人都可以访问到内容和使用基本的功能,而对于使用设备更好的浏览器的用户,提供更好的交互体验
不过这个可能很难说服PM同学~大家加油,不过中后台项目还是可以放心使用。我已经计划把我们中后台用用remix重写了,冲冲冲
好啦,这就是最近一段时间使用remix的体验,我个人认为是一个非常好的框架,开发体验,用户体验,设计上都非常solid,值得大家尝试和学习。后续会为大家带来更多原理分析和实战分享