開源不易,感謝你的支援,❤ star concent^_^
序言
nextjs
是一個非常流行的 React 服務端渲染應用框架,它很輕量,簡單易上手,社群活躍,所以當我們使用react
寫一個需要ssr
(server side render)的應用的話,基本都會首選nextjs
,concent
是一個新生代的react
狀態管理方案,它內建依賴收集系統,同時兼具有0入侵、可預測、漸進式、高效能的特點,並提供了lifecyle
、composition api
等靈活的api且寫法超級簡單,讓你輕鬆駕馭超大規模的react應用。
Hello next
這裡我們將使用create-next-app
命令來安裝一個基礎的next示例應用
npx create-next-app hello-next
執行完畢後,可以看到一個如下的目錄結構
|____public
|____pages
| |____ _app.js // next應用預設的根元件
| |____index.js // 預設首頁
| |____api // api路由檔案
| | |____hello.js
之後我們在專案根目錄執行npm run dev
將看到一個由next
驅動的ssr
預設首頁
Hello concent
這裡我們將使用create-react-app
命令來安裝一個基礎的concent示例應用
npx create-react-app hello-concent --template concent-ts
執行完畢後,可以看到一個如下的目錄結構
|____index.tsx
|____App.tsx
|____types // store的型別定義處
|____features // 功能元件列表
| |____counter // counter功能
| | |____Counter.tsx // counter元件
| | |____model // counter模型(包含state,reducer,computed)
|____models // 其它全域性通用的模型定義
|____configs
進入專案目錄執行npm i
,然後執行npm start
即可看到一個預設的計數器頁面
你也可以點選這裡線上瞭解和編輯它。
當然了在已有的專案裡整合concent
裡也超級簡單,因為它無需頂層提供Provider
,只需要提前配置好模型即可。
import { run } from 'concent';
run({ // 定義一個counter模型
counter: {
state: { num: 1, bigNum: 10 },
reducer: {
add(payload, moduleState) {
return { num: moduleState + 1 };
},
async asyncAddBig() {
await new Promise(resolve => setTimeout(resolve, 1000));
return { bigNum: moduleState + 10 };
}
},
computed: {
doubleNum: ({ num }) => num * 2, // 僅當num發生變化才觸發此函式
}
}
})
之後就可以全域性即插即用啦,類元件和函式元件都可以用同樣的方式去讀取資料或呼叫方法,敲重點啦,如果ui處是有條件語句控制是否要消費狀態或衍生資料的話,推薦延遲解構的寫法,這樣可以讓concent在每一輪渲染完畢後收集到檢視對資料的最小粒度依賴
// ###### 函式元件
function Demo(){
// 如 state 和 moduleComputed 是按需讀取的,推薦延遲解構的寫法
const { state: { num, numBig }, moduleComputed: { doubleNum }, mr } = useConcent('counter');
// ... ui 邏輯,綁資料、綁方法
}
// ###### 類元件
const DemoCls = register('counter')(
class DemoCls extends React.Component{
render(){
const { state: { num, numBig }, moduleComputed: { doubleNum }, mr } = this.ctx;
// ... ui 邏輯,綁資料、綁方法
}
}
)
在next裡引入concent
next的基礎示例目錄裡有個_app.js
檔案,它是next應用的根元件
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
因使用concent
之前必需提前配置好模型,所以我們只需提前建立一個runConcent.js
檔案
import { run } from 'concent'
import * as models from './models';
run(models);
然後在_app.js
檔案引入即可,這樣根元件下的所有子元件都能夠正確獲取到store的資料和調動store的方法了。
import '../styles/globals.css'
+ import './runConcent'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
接著我們在next的pages目錄下建立一個counter.js
檔案,代表這是一個頁面元件,這樣瀏覽器端可以用/counter
路由來訪問到這個元件的渲染檢視了。
import React from 'react'
import { useConcent } from 'concent'
import router from 'next/router'
// use next/router to do browser side router jump
function toHomePage(){
router.push('/');
}
export default function Counter() {
const { state, mr, moduleComputed } = useConcent('home')
return (
<div>
this is counter page
<h1>num: {state.num}</h1>
<h1>doubleNum: {moduleComputed.doubleNum}</h1>
<button onClick={mr.add}>add</button>
<button onClick={toHomePage}>to home page</button>
</div>
);
}
大功告成,一個接入了concent
的next
應用就這樣產生了,是不是特別簡單呢?^_^
支援預渲染
next
提供兩種級別的預渲染介面,即getServerSideProps
和getStaticProps
,兩種的區別是執行時機不同,getServerSideProps
是每次請求頁面都會執行,而getStaticProps
是構建時執行,我們先處理getServerSideProps
這種情況吧,看看如何集合concent
做預渲染支援。
首先我們不考慮concent
的存在,在next
裡做預渲染支援,只需要在你的頁面元件裡暴露一個getServerSideProps
介面即可。
// 此函式在每次請求改頁面時被呼叫
export async function getServerSideProps() {
// 呼叫外部 API 獲取博文列表
const res = await fetch('https://.../posts')
const posts = await res.json()
// 通過返回 { props: posts } 物件,PostPage 元件在渲染時將接收到 `posts` 引數
return {
props: { posts },
}
}
function PostPage({ posts }) { // 這裡接收到了 posts 引數
// Render posts...
}
export default PostPage
之所以Blog
能夠接到posts
,除了暴露這個getServerSideProps
這個介面之外,我們再觀察一下_app.js
這個根元件檔案內容,可以發現關鍵點所在!
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
引數列表裡的pageProps
即是getServerSideProps
返回結果裡props
指向的物件,然後next
將其透傳到目標頁面元件上,所以我們才能夠在PostPage
引數列表裡解構出posts
。
所以我們的切入點就可以從這裡入手了,我們把getStaticProps的返回結果做一下格式約束,形如{module:string, state: object}
這樣的結構,然後在_app.js
檔案裡記錄到store即可
// 此函式在每次請求時被呼叫
export async function getServerSideProps() {
// 呼叫外部 API 獲取博文列表
await delay();
const posts = [
{ id: 1, name: 'post1 -----' },
{ id: 2, name: 'post2 --- welcome to use concent' },
];
// 這個返回物件會透傳給根元件的pageProps,在此返回狀態所屬的模組和狀態實體物件
// 在那裡將狀態記錄到store
return {
props: {
module: 'test',
state: { posts },
}
};
}
此時的根元件檔案改變如下
import '../styles/globals.css';
+ import './runConcent';
+ import { setState } from 'concent';
function MyApp({ Component, pageProps }) {
// 這裡記錄 getServerSideProps 的返回狀態到store的對應模組
+ if (pageProps.module) {
+ setState(pageProps.module, pageProps.state);
+ }
return <Component {...pageProps} />
}
export default MyApp;
然後我們實現的頁面元件post-page
程式碼如下
const PostList = React.memo(function () {
const { state } = useConcent('test');
return (
<div>
{state.posts.map(item => <h3 key={item.id}>{item.name}</h3>)}
</div>
);
});
const PostLength = React.memo(function () {
const { state } = useConcent('test');
return <h1>{state.posts.length}</h1>;
});
export default function PostPage() {
return (
<div>
<h1>this is post page</h1>
<PostList />
<PostLength />
<button onClick={toHomePage}>to home page</button>
</div>
);
}
接著我們開啟瀏覽器訪問/post-page
頁面吧,點選檢視原始碼將會看到這是一個伺服器端預渲染的頁面
同理,我們也可將getServerSideProps
替換為getStaticProps
,上面的整個流程將依然正常工作,歡迎各位看官clone示例程式碼來親自體驗一下。
git clone https://github.com/concentjs/ssr-demo-1
附錄
doc
CloudBase CMS
歡迎小哥哥們來撩CloudBase CMS ,打造一站式雲端內容管理系統,它是雲開發推出的,基於 Node.js 的 Headless 內容管理平臺,提供了豐富的內容管理功能,安裝簡單,易於二次開發,並與雲開發的生態體系緊密結合,助力開發者提升開發效率。
concent
已為其管理後臺提供強力支援,新版的管理介面更加美觀和體貼了。
FFCreator
也歡迎小哥哥們來撩FFCreator,它是一個基於node.js的輕量、靈活的短視訊加工庫。您只需要新增幾張圖片或視訊片段再加一段背景音樂,就可以快速生成一個很酷的視訊短片。
FFCreator
是一種輕量又簡單的解決方案,只需要很少的依賴和較低的機器配置就可以快速開始工作。並且它模擬實現了animate.css90%的動畫效果,您可以輕鬆地把 web 頁面端的動畫效果轉為視訊,真的很給力。