视频地址: https://www.bilibili.com/vide...
用户治理
Session 治理
SessionStorage
import { createCookieSessionStorage } from '@remix-run/node';export const sessionStorage = createCookieSessionStorage({ cookie: { name: '_session', sameSite: 'lax', path: '/', httpOnly: true, secrets: [process.env.COOKIE_SECRET || 's3cr3t'], secure: process.env.NODE_ENV === 'production' }});export const { getSession, commitSession, destroySession } = sessionStorage;
Token 治理
将 code 换 AccessToken 和 RefreshToken 换 AccessToken 两个办法封装
async function tokenRequest(body) { const formBody = []; // eslint-disable-next-line for (const property in body) { const encodedKey = encodeURIComponent(property); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const encodedValue = encodeURIComponent(body[property]); formBody.push(`${encodedKey}=${encodedValue}`); } const res = await fetch(`${process.env.AUTHING_APP_DOMAIN}/oidc/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: formBody.join('&') }); const oidcToken = (await res.json()) as OidcResponse; return oidcToken;}export function code2Token(code: string) { const body = { client_id: process.env.AUTHING_APP_ID, client_secret: process.env.AUTHING_APP_SECRET, grant_type: 'authorization_code', code }; return tokenRequest(body);}export function refreshToken(token: OidcResponse) { const body = { client_id: process.env.AUTHING_APP_ID, client_secret: process.env.AUTHING_APP_SECRET, grant_type: 'refresh_token', refresh_token: token.refresh_token }; return tokenRequest(body);}
i18n
插件: https://remix-i18n.js.cool
装置
npm install --save remix-i18n
配置
export interface RemixI18nOptions { // 反对的语言 supportedLanguages: string[]; // 失败备选 fallbackLng: string;}
const i18n = new RemixI18n({ supportedLanguages: ['en', 'tl', 'da', 'zh'], fallbackLng: 'zh'});
增加语言翻译
i18n.set('locale', { hello: '你好'});
客户端设置
// entry.client.tsximport { hydrate } from 'react-dom';import { RemixBrowser } from 'remix';import { I18nProvider } from 'remix-i18n';import { i18n, getLocale } from '~/i18n';const locale = getLocale(window.location.pathname);i18n.locale(locale);hydrate( <I18nProvider i18n={i18n}> <RemixBrowser /> </I18nProvider>, document);
服务器端设置
// entry.server.tsximport { renderToString } from 'react-dom/server';import { RemixServer } from 'remix';import type { EntryContext } from 'remix';import { I18nProvider } from 'remix-i18n';import { i18n, getLocale } from '~/i18n';export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext) { const locale = getLocale(new URL(request.url).pathname); i18n.locale(locale); const markup = renderToString( <I18nProvider i18n={i18n}> <RemixServer context={remixContext} url={request.url} /> </I18nProvider> ); responseHeaders.set('Content-Type', 'text/html'); return new Response(`<!DOCTYPE html>${markup}`, { status: responseStatusCode, headers: responseHeaders });}
语言切换
const i18n = useI18n();const location = useLocation();useEffect(() => { const locale = getLocale(location.pathname); if (locale !== i18n.locale()) { i18n.locale(locale); }}, [location]);
应用模板
const { t } = useI18n();// jsx<h1>{t('hello')}</h1>;
P.S.
遗留问题: 用户扩大信息的获取(抽空摸索分明再持续)
下期主题: DaisyUI 主题切换实现