服务器端渲染?服务端渲染和客户端渲染的区别
大家好,关于服务器端渲染很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于服务端渲染和客户端渲染的区别的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!
服务端渲染和客户端渲染的区别
你好,你是想问服务端渲染和客户端渲染的区别是什么吗?服务端渲染和客户端渲染区别是渲染过程和优缺点。
1、渲染过程:
2、服务端渲染:在服务端渲染中,服务器首先接收到客户端的请求,然后生成HTML文档并将其发送给客户端。客户端只需解析收到的HTML文档,而无需等待额外的请求。整个渲染过程都在服务端完成。
3、客户端渲染:在客户端渲染中,客户端首先接收到HTML框架,然后通过JavaScript动态请求服务器获取数据,再将数据与HTML框架拼接在一起,最后生成渲染后的页面。渲染过程涉及到客户端与服务器之间的多次数据交互。
4、服务端渲染优点:节省客户端资源,减轻客户端负担,加快页面加载速度。有利于SEO优化,因为服务器端生成完整的HTML页面,便于爬虫抓取。
5、服务端渲染缺点:不利于前后端分离,开发效率较低。服务器需要解析HTML,加大了服务器压力。
6、客户端渲染优点:前后端分离,开发效率高。可以根据用户操作实时生成动态内容,提高用户交互性。
7、客户端渲染缺点:客户端需要解析HTML和JavaScript,可能影响页面加载速度。多次请求服务器,网络延迟可能导致页面加载速度降低。
服务端渲染的好处
react.js在服务器端渲染好处:
提升性能是需要再浏览器端的性能提升还是服务端的性能提升,是两个概念,服务端渲染会给服务端造成一定的压力,减轻客户端的压力;好处:在整个页面级别的应用会使得浏览器在解析dom完成之后马上有东西可以渲染。再者就是对seo比较友好一些;
渲染的流程主要是:
准备数据,一般从数据库或外部API获得(一般要先 render React一次,去触发所需的API)
数据和React结合生成HTML Markup
除了把HMTL Markup输出外,还要把'State'输出,这要在客户端才能保留'State'
怎样使用React服务器端渲染
这次给大家带来怎样使用React服务器端渲染,使用React服务器端渲染的注意事项有哪些,下面就是实战案例,一起来看一下。
React提供了两个方法 renderToString和 renderToStaticMarkup用来将组件(Virtual DOM)输出成 HTML字符串,这是 React服务器端渲染的基础,它移除了服务器端对于浏览器环境的依赖,所以让服务器端渲染变成了一件有吸引力的事情。
服务器端渲染除了要解决对浏览器环境的依赖,还要解决两个问题:
前后端可以共享代码
前后端路由可以统一处理
React生态提供了很多选择方案,这里我们选用 Redux和 react-router来做说明。
Redux
Redux提供了一套类似 Flux的单向数据流,整个应用只维护一个 Store,以及面向函数式的特性让它对服务器端渲染支持很友好。
2分钟了解 Redux是如何运作的
关于 Store:
整个应用只有一个唯一的 Store
Store对应的状态树(State),由调用一个 reducer函数(root reducer)生成
状态树上的每个字段都可以进一步由不同的 reducer函数生成
Store包含了几个方法比如 dispatch, getState来处理数据流
Store的状态树只能由 dispatch(action)来触发更改
Redux的数据流:
action是一个包含{ type, payload}的对象
reducer函数通过 store.dispatch(action)触发
reducer函数接受(state, action)两个参数,返回一个新的 state
reducer函数判断 action.type然后处理对应的 action.payload数据来更新状态树
所以对于整个应用来说,一个 Store就对应一个 UI快照,服务器端渲染就简化成了在服务器端初始化 Store,将 Store传入应用的根组件,针对根组件调用 renderToString就将整个应用输出成包含了初始化数据的 HTML。
react-router
react-router通过一种声明式的方式匹配不同路由决定在页面上展示不同的组件,并且通过 props将路由信息传递给组件使用,所以只要路由变更,props就会变化,触发组件 re-render。
假设有一个很简单的应用,只有两个页面,一个列表页/list和一个详情页/item/:id,点击列表上的条目进入详情页。
可以这样定义路由,./routes.js
import React from'react';
import{ Route} from'react-router';
import{ List, Item} from'./components';
//无状态(stateless)组件,一个简单的容器,react-router会根据 route
//规则匹配到的组件作为 `props.children`传入
const Container=(props)=>{
return(
<p>{props.children}</p>
);
};
// route规则:
//- `/list`显示 `List`组件
//- `/item/:id`显示 `Item`组件
const routes=(
<Route path="/" component={Container}>
<Route path="list" component={List}/>
<Route path="item/:id" component={Item}/>
</Route>
);
export default routes;从这里开始,我们通过这个非常简单的应用来解释实现服务器端渲染前后端涉及的一些细节问题。
Reducer
Store是由 reducer产生的,所以 reducer实际上反映了 Store的状态树结构
./reducers/index.js
import listReducer from'./list';
import itemReducer from'./item';
export default function rootReducer(state={}, action){
return{
list: listReducer(state.list, action),
item: itemReducer(state.item, action)
};
}rootReducer的 state参数就是整个 Store的状态树,状态树下的每个字段对应也可以有自己的reducer,所以这里引入了 listReducer和 itemReducer,可以看到这两个 reducer的 state参数就只是整个状态树上对应的 list和 item字段。
具体到./reducers/list.js
const initialState= [];
export default function listReducer(state= initialState, action){
switch(action.type){
case'FETCH_LIST_SUCCESS': return [...action.payload];
default: return state;
}
}list就是一个包含 items的简单数组,可能类似这种结构:[{ id: 0, name:'first item'},{id: 1, name:'second item'}],从'FETCH_LIST_SUCCESS'的 action.payload获得。
然后是./reducers/item.js,处理获取到的 item数据
const initialState={};
export default function listReducer(state= initialState, action){
switch(action.type){
case'FETCH_ITEM_SUCCESS': return [...action.payload];
default: return state;
}
}Action
对应的应该要有两个 action来获取 list和 item,触发 reducer更改 Store,这里我们定义 fetchList和 fetchItem两个 action。
./actions/index.js
import fetch from'isomorphic-fetch';
export function fetchList(){
return(dispatch)=>{
return fetch('/api/list')
.then(res=> res.json())
.then(json=> dispatch({ type:'FETCH_LIST_SUCCESS', payload: json}));
}
}
export function fetchItem(id){
return(dispatch)=>{
if(!id) return Promise.resolve();
return fetch(`/api/item/${id}`)
.then(res=> res.json())
.then(json=> dispatch({ type:'FETCH_ITEM_SUCCESS', payload: json}));
}
}isomorphic-fetch是一个前后端通用的 Ajax实现,前后端要共享代码这点很重要。
另外因为涉及到异步请求,这里的 action用到了 thunk,也就是函数,redux通过 thunk-middleware来处理这类 action,把函数当作普通的 action dispatch就好了,比如 dispatch(fetchList())
Store
我们用一个独立的./store.js,配置(比如 Apply Middleware)生成 Store
import{ createStore} from'redux';
import rootReducer from'./reducers';
// Apply middleware here
//...
export default function configureStore(initialState){
const store= createStore(rootReducer, initialState);
return store;
}react-redux
接下来实现<List>,<Item>组件,然后把 redux和 react组件关联起来,具体细节参见 react-redux
./app.js
import React from'react';
import{ render} from'react-dom';
import{ Router} from'react-router';
import createBrowserHistory from'history/lib/createBrowserHistory';
import{ Provider} from'react-redux';
import routes from'./routes';
import configureStore from'./store';
// `INITIAL_STATE`来自服务器端渲染,下一部分细说
const initialState= window.INITIAL_STATE;
const store= configureStore(initialState);
const Root=(props)=>{
return(
<p>
<Provider store={store}>
<Router history={createBrowserHistory()}>
{routes}
</Router>
</Provider>
</p>
);
}
render(<Root/>, document.getElementById('root'));至此,客户端部分结束。
Server Rendering
接下来的服务器端就比较简单了,获取数据可以调用 action,routes在服务器端的处理参考 react-router server rendering,在服务器端用一个 match方法将拿到的 request url匹配到我们之前定义的 routes,解析成和客户端一致的 props对象传递给组件。
./server.js
import express from'express';
import React from'react';
import{ renderToString} from'react-dom/server';
import{ RoutingContext, match} from'react-router';
import{ Provider} from'react-redux';
import routes from'./routes';
import configureStore from'./store';
const app= express();
function renderFullPage(html, initialState){
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p id="root">
<p>
${html}
</p>
</p>
<script>
window.INITIAL_STATE=${JSON.stringify(initialState)};
</script>
<script src="/static/bundle.js"></script>
</body>
</html>
`;
}
app.use((req, res)=>{
match({ routes, location: req.url},(err, redirectLocation, renderProps)=>{
if(err){
res.status(500).end(`Internal Server Error${err}`);
} else if(redirectLocation){
res.redirect(redirectLocation.pathname+ redirectLocation.search);
} else if(renderProps){
const store= configureStore();
const state= store.getState();
Promise.all([
store.dispatch(fetchList()),
store.dispatch(fetchItem(renderProps.params.id))
])
.then(()=>{
const html= renderToString(
<Provider store={store}>
<RoutingContext{...renderProps}/>
</Provider>
);
res.end(renderFullPage(html, store.getState()));
});
} else{
res.status(404).end('Not found');
}
});
});服务器端渲染部分可以直接通过共用客户端 store.dispatch(action)来统一获取 Store数据。另外注意 renderFullPage生成的页面 HTML在 React组件 mount的部分(<p id="root">),前后端的 HTML结构应该是一致的。然后要把 store的状态树写入一个全局变量(INITIAL_STATE),这样客户端初始化 render的时候能够校验服务器生成的 HTML结构,并且同步到初始化状态,然后整个页面被客户端接管。
最后关于页面内链接跳转如何处理?
react-router提供了一个<Link>组件用来替代<a>标签,它负责管理浏览器 history,从而不是每次点击链接都去请求服务器,然后可以通过绑定 onClick事件来作其他处理。
比如在/list页面,对于每一个 item都会用<Link>绑定一个 route url:/item/:id,并且绑定 onClick去触发 dispatch(fetchItem(id))获取数据,显示详情页内容。
相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!
推荐阅读:
怎样使用JS实现计算圆周率到小数点后100位
怎样使用vue axios给生产与发布环境配置接口地址
好了,关于服务器端渲染和服务端渲染和客户端渲染的区别的问题到这里结束啦,希望可以解决您的问题哈!