国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

React對服務端進行渲染改造

來源:懂視網 責編:小采 時間:2020-11-27 19:36:50
文檔

React對服務端進行渲染改造

React對服務端進行渲染改造:這次給大家帶來React對服務端進行渲染改造,React對服務端進行渲染改造的注意事項有哪些,下面就是實戰案例,一起來看一下。因為對網頁SEO的需要,要把之前的React項目改造為服務端渲染,經過一番調查和研究,查閱了大量互聯網資料。成功踩坑。選型思路:實現
推薦度:
導讀React對服務端進行渲染改造:這次給大家帶來React對服務端進行渲染改造,React對服務端進行渲染改造的注意事項有哪些,下面就是實戰案例,一起來看一下。因為對網頁SEO的需要,要把之前的React項目改造為服務端渲染,經過一番調查和研究,查閱了大量互聯網資料。成功踩坑。選型思路:實現
這次給大家帶來React對服務端進行渲染改造,React對服務端進行渲染改造的注意事項有哪些,下面就是實戰案例,一起來看一下。

因為對網頁SEO的需要,要把之前的React項目改造為服務端渲染,經過一番調查和研究,查閱了大量互聯網資料。成功踩坑。

選型思路:實現服務端渲染,想用React最新的版本,并且不對現有的寫法做大的改動,如果一開始就打算服務端渲染,建議直接用NEXT框架來寫

項目地址:https://github.com/wlx200510/react_koa_ssr

腳手架選型:webpack3.11.0 + react Router4 + Redux + koa2 + React16 + Node8.x

主要心得:對React的相關知識更加熟悉,成功拓展自己的技術領域,對服務端技術在實際項目上有所積累

注意點:使用框架前一定確認當前webpack版本為3.x Node為8.x以上,讀者最好用React在3個月以上,并有實際React項目經驗

項目目錄介紹

├── assets
│ └── index.css //放置一些全局的資源文件 可以是js 圖片等
├── config
│ ├── webpack.config.dev.js 開發環境webpack打包設置
│ └── webpack.config.prod.js 生產環境webpack打包設置
├── package.json
├── README.md
├── server server端渲染文件,如果對不是很了解,建議參考[koa教程](http://wlxadyl.cn/2018/02/11/koa-learn/)
│ ├── app.js
│ ├── clientRouter.js // 在此文件中包含了把服務端路由匹配到react路由的邏輯
│ ├── ignore.js
│ └── index.js
└── src
 ├── app 此文件夾下主要用于放置瀏覽器和服務端通用邏輯
 │ ├── configureStore.js //redux-thunk設置
 │ ├── createApp.js //根據渲染環境不同來設置不同的router模式
 │ ├── index.js
 │ └── router
 │ ├── index.js
 │ └── routes.js //路由配置文件! 重要
 ├── assets
 │ ├── css 放置一些公共的樣式文件
 │ │ ├── _base.scss //很多項目都會用到的初始化css
 │ │ ├── index.scss
 │ │ └── my.scss
 │ └── img
 ├── components 放置一些公共的組件
 │ ├── FloatDownloadBtn 公共組件樣例寫法
 │ │ ├── FloatDownloadBtn.js
 │ │ ├── FloatDownloadBtn.scss
 │ │ └── index.js
 │ ├── Loading.js
 │ └── Model.js 函數式組件的寫法
 │
 ├── favicon.ico
 ├── index.ejs //渲染的模板 如果項目需要,可以放一些公共文件進去
 ├── index.js //包括熱更新的邏輯
 ├── pages 頁面組件文件夾
 │ ├── home
 │ │ ├── components // 用于放置頁面組件,主要邏輯
 │ │ │ └── homePage.js
 │ │ ├── containers // 使用connect來封裝出高階組件 注入全局state數據
 │ │ │ └── homeContainer.js
 │ │ ├── index.js // 頁面路由配置文件 注意thunk屬性
 │ │ └── reducer
 │ │ └── index.js // 頁面的reducer 這里暴露出來給store統一處理 注意寫法
 │ └── user
 │ ├── components
 │ │ └── userPage.js
 │ ├── containers
 │ │ └── userContainer.js
 │ └── index.js
 └── store
 ├── actions // 各action存放地
 │ ├── home.js
 │ └── thunk.js
 ├── constants.js // 各action名稱匯集處 防止重名
 └── reducers
 └── index.js // 引用各頁面的所有reducer 在此處統一combine處理

項目的構建思路

  1. 本地開發使用webpack-dev-server,實現熱更新,基本流程跟之前react開發類似,仍是瀏覽器端渲染,因此在編寫代碼時要考慮到一套邏輯,兩種渲染環境的問題。

  2. 當前端頁面渲染完成后,其Router跳轉將不會對服務端進行請求,從而減輕服務端壓力,從而頁面的進入方式也是兩種,還要考慮兩種渲染環境下路由同構的問題。

  3. 生產環境要使用koa做后端服務器,實現按需加載,在服務端獲取數據,并渲染出整個HTML,利用React16最新的能力來合并整個狀態樹,實現服務端渲染。

本地開發介紹

查看本地開發主要涉及的文件是src目錄下的index.js文件,判斷當前的運行環境,只有在開發環境下才會使用module.hot的API,實現當reducer發生變化時的頁面渲染更新通知,注意其中的hydrate方法,這是v16版本的一個專門為服務端渲染新增的API方法,它在render方法的基礎上實現了對服務端渲染內容的最大可能重用,實現了靜態DOM到動態NODES的過程。實質是代替了v15版本下判斷checksum標記的過程,使得重用的過程更加高效優雅。

const renderApp=()=>{
 let application=createApp({store,history});
 hydrate(application,document.getElementById('root'));
}
window.main = () => {
 Loadable.preloadReady().then(() => {
 renderApp()
 });
};
if(process.env.NODE_ENV==='development'){
 if(module.hot){
 module.hot.accept('./store/reducers/index.js',()=>{
 let newReducer=require('./store/reducers/index.js');
 store.replaceReducer(newReducer)
 })
 module.hot.accept('./app/index.js',()=>{
 let {createApp}=require('./app/index.js');
 let newReducer=require('./store/reducers/index.js');
 store.replaceReducer(newReducer)
 let application=createApp({store,history});
 hydrate(application,document.getElementById('root'));
 })
 }
}

注意window.main這個函數的定義,結合index.ejs可以知道這個函數是所有腳本加載完成后才觸發,里面用的是react-loadable的寫法,用于頁面的懶加載,關于頁面分別打包的寫法要結合路由設置來講解,這里有個大致印象即可。需要注意的是app這個文件下暴露出的三個方法是在瀏覽器端和服務器端通用的,接下來主要就是說這部分的思路。

路由處理

接下來看以下src/app目錄下的文件,index.js暴露了三個方法,這里面涉及的三個方法在服務端和瀏覽器端開發都會用到,這一部分主要講其下的router文件里面的代碼思路和createApp.js文件對路由的處理,這里是實現兩端路由相互打通的關鍵點。

router文件夾下的routes.js是路由配置文件,將各個頁面下的路由配置都引進來,合成一個配置數組,可以通過這個配置來靈活控制頁面上下線。同目錄下的index.js是RouterV4的標準寫法,通過遍歷配置數組的方式傳入路由配置,ConnectRouter是用于合并Router的一個組件,注意到history要作為參數傳入,需要在createApp.js文件里做單獨的處理。先大致看一下Route組件中的幾個配置項,值得注意的是其中的thunk屬性,這是實現后端獲取數據后渲染的關鍵一步,正是這個屬性實現了類似Next里面的組件提前獲取數據的生命周期鉤子,其余的屬性都可以在相關React-router文檔中找到說明,這里不在贅述。

import routesConfig from './routes';
const Routers=({history})=>(
 <ConnectedRouter history={history}>
 <p>
 {
 routesConfig.map(route=>(
 <Route key={route.path} exact={route.exact} path={route.path} component={route.component} thunk={route.thunk} />
 ))
 }
 </p>
 </ConnectedRouter>
)
export default Routers;

查看app目錄下的createApp.js里面的代碼可以發現,本框架是針對不同的工作環境做了不同的處理,只有在生產環境下才利用Loadable.Capture方法實現了懶加載,動態引入不同頁面對應的打包之后的js文件。到這里還要看一下組件里面的路由配置文件的寫法,以home頁面下的index.js為例。注意/* webpackChunkName: 'Home' */這串字符,實質是指定了打包后此頁面對應的js文件名,所以針對不同的頁面,這個注釋也需要修改,避免打包到一起。loading這個配置項只會在開發環境生效,當頁面加載未完成前顯示,這個實際項目開發如果不需要可以刪除此組件。

import {homeThunk} from '../../store/actions/thunk';
const LoadableHome = Loadable({
 loader: () =>import(/* webpackChunkName: 'Home' */'./containers/homeContainer.js'),
 loading: Loading,
});
const HomeRouter = {
 path: '/',
 exact: true,
 component: LoadableHome,
 thunk: homeThunk // 服務端渲染會開啟并執行這個action,用于獲取頁面渲染所需數據
}
export default HomeRouter

這里多說一句,有時我們要改造的項目的頁面文件里有從window.location里面獲取參數的代碼,改造成服務端渲染時要全部去掉,或者是要在render之后的生命周期中使用。并且頁面級別組件都已經注入了相關路由信息,可以通過this.props.location來獲取URL里面的參數。本項目用的是BrowserRouter,如果用HashRouter則包含參數可能略有不同,根據實際情況取用。

根據React16的服務端渲染的API介紹:

  1. 瀏覽器端使用的注入ConnectedRouter中的history為:import createHistory from 'history/createBrowserHistory'

  2. 服務器端使用的history為import createHistory from 'history/createMemoryHistory'

服務端渲染

這里就不會涉及到koa2的一些基礎知識,如果對koa2框架不熟悉可以參考我的另外一篇博文。這里是看server文件夾下都是服務端的代碼。首先是簡潔的app.js用于保證每次連接都返回的是一個新的服務器端實例,這對于單線程的js語言是很關鍵的思路。需要重點介紹的就是clientRouter.js這個文件,結合/src/app/configureStore.js這個文件共同理解服務端渲染的數據獲取流程和React的渲染機制。

/*configureStore.js*/
import {createStore, applyMiddleware,compose} from "redux";
import thunkMiddleware from "redux-thunk";
import createHistory from 'history/createMemoryHistory';
import { routerReducer, routerMiddleware } from 'react-router-redux'
import rootReducer from '../store/reducers/index.js';
const routerReducers=routerMiddleware(createHistory());//路由
const composeEnhancers = process.env.NODE_ENV=='development'?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose;
const middleware=[thunkMiddleware,routerReducers]; //把路由注入到reducer,可以從reducer中直接獲取路由信息
let configureStore=(initialState)=>createStore(rootReducer,initialState,composeEnhancers(applyMiddleware(...middleware)));
export default configureStore;

window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__這個變量是瀏覽器里面的Redux的開發者工具,開發React-redux應用時建議安裝,否則會有報錯提示。這里面大部分都是redux-thunk的示例代碼,關于這部分如果看不懂建議看一下redux-thunk的官方文檔,這里要注意的是configureStore這個方法要傳入的initialState參數,這個渲染的具體思路是:在服務端判斷路由的thunk方法,如果存在則需要執行這個獲取數據邏輯,這是個阻塞過程,可以當作同步,獲取后放到全局State中,在前端輸出的HTML中注入window.__INITIAL_STATE__這個全局變量,當html載入完畢后,這個變量賦值已有數據的全局State作為initState提供給react應用,然后瀏覽器端的js加載完畢后會通過復用頁面上已有的dom和初始的initState作為開始,合并到render后的生命周期中,從而在componentDidMount中已經可以從this.props中獲取渲染所需數據。

但還要考慮到頁面切換也有可能在前端執行跳轉,此時作為React的應用不會觸發對后端的請求,因此在componentDidMount這個生命周期里并沒有獲取數據,為了解決這個問題,我建議在這個生命周期中都調用props中傳來的action觸發函數,但在action內部進行一層邏輯判斷,避免重復的請求,實際項目中請求數據往往會有個標識性ID,就可以將這個ID存入store中,然后就可以進行一次對比校驗來提前返回,避免重復發送ajax請求,具體可看store/actions/home.js`中的邏輯處理。

import {ADD,GET_HOME_INFO} from '../constants'
export const add=(count)=>({type: ADD, count,})
export const getHomeInfo=(sendId=1)=>async(dispatch,getState)=>{
 let {name,age,id}=getState().HomeReducer.homeInfo;
 if (id === sendId) {
 return //是通過對請求id和已有數據的標識性id進行對比校驗,避免重復獲取數據。
 }
 console.log('footer'.includes('foo'))
 await new Promise(resolve=>{
 let homeInfo={name:'wd2010',age:'25',id:sendId}
 console.log('-----------請求getHomeInfo')
 setTimeout(()=>resolve(homeInfo),1000)
 }).then(homeInfo=>{
 dispatch({type:GET_HOME_INFO,data:{homeInfo}})
 })
}

注意這里的async/await寫法,這里涉及到服務端koa2使用這個來做數據請求,因此需要統一返回async函數,這塊不熟的同學建議看下ES7的知識,主要是async如何配合Promise實現異步流程改造,并且如果涉及koa2的服務端工作,對async函數用的更多,這也是本項目要求Node版本為8.x以上的原因,從8開始就可以直接用這兩個關鍵字。

不過到具體項目中,往往會涉及到一些服務端參數的注入問題,但這塊根據不同項目需求差異很大,并且不屬于這個React服務端改造的一部分,沒法統一分享,如果真是公司項目要用到對這塊有需求咨詢可以打賞后加我微信討論。

以Home頁面為例的渲染流程

為了方便大家理解,我以一個頁面為例整理了一下數據流的整體過程,看一下思路:

  1. 服務端接收到請求,通過/home找到對應的路由配置

  2. 判斷路由存在thunk方法,此時執行store/actions/thunk.js里面的暴露出的函數

  3. 異步獲取的數據會注入到全局state中,此時的dispatch分發其實并不生效

  4. 要輸出的HTML代碼中會將獲取到數據后的全局state放到window.__INITIAL_STATE__這個全局變量中,作為initState

  5. window.__INITIAL_STATE__將在react生命周期起作用前合并入全局state,此時react發現dom已經生成,不會再次觸發render,并且數據狀態得到同步

服務端直出HTML

基本的流程已經介紹結束,至于一些Reducer的函數式寫法,還有actions的位置都是參考網上的一些分析來組織的,具體見仁見智,這個只要符合自己的理解,并且有助于團隊開發就好。如果您符合我在文章一開始設定的讀者背景,相信本文的講述足夠您點亮自己的服務端渲染技術點啦。如果對React了解偏少也沒關系,可以參考這里來補充一些React的基礎知識

相信看了本文案例你已經掌握了方法,更多精彩請關注Gxl網其它相關文章!

推薦閱讀:

如何使用webpack+vue環境局域網

vue底層原理與組件通信

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

React對服務端進行渲染改造

React對服務端進行渲染改造:這次給大家帶來React對服務端進行渲染改造,React對服務端進行渲染改造的注意事項有哪些,下面就是實戰案例,一起來看一下。因為對網頁SEO的需要,要把之前的React項目改造為服務端渲染,經過一番調查和研究,查閱了大量互聯網資料。成功踩坑。選型思路:實現
推薦度:
標簽: 服務器 rea 改造
  • 熱門焦點

最新推薦

猜你喜歡

熱門推薦

專題
Top
主站蜘蛛池模板: 亚洲区欧美 | 欧美视频免费在线观看 | 激情另类国内一区二区视频 | 欧美成人一区二区三区在线视频 | 国产日产欧美精品一区二区三区 | 日韩理论在线 | 国产国拍亚洲精品午夜不卡17 | 国产精品三级在线观看 | 国产一区二区精品久久91 | 欧美日韩亚洲电影天堂 | 欧美在线一区二区三区精品 | 欧美日韩色 | 国产精品久久久久影院色 | 国产精品亚洲欧美一级久久精品 | 国产一区二区三区在线 | 日韩亚洲欧美在线 | 国产不卡视频在线播放 | 国产欧美日韩一区二区三区在线 | 亚洲日本在线观看 | 一级毛片特黄久久免费看 | 精品免费久久久久国产一区 | 黄色视频毛片 | 欧美国产日韩在线播放 | 毛片免费网站 | 五月婷综合 | 在线免费观看国产 | 国产区二区 | 一区二区高清在线 | 特黄一级毛片 | 欧美综合亚洲图片综合区 | 亚洲精品视频免费观看 | 五月天婷婷视频 | 欧美日韩免费 | 欧美激情在线播放一区二区三区 | 在线日韩欧美 | 91久久精品国产一区二区 | 阿v精品一区二区三区 | 亚洲欧美日韩一区 | 99久久99久久久精品久久 | 成人99国产精品一级毛片 | 欧美变态人zozo禽交 |