这次我要上SSR
栏目: JavaScript · 发布时间: 5年前
- SEO:让各爬虫能爬到首屏的内容,匹配到搜索关键字,提高在百度谷歌的排序
-
加快首屏速度,不再是加载完js然后
ReactDom.render('#app', Com)
,让服务器分担部分渲染的压力
精简易懂的内容来了
SSR怎么实现首屏渲染
- 通过express服务来返回给浏览器首屏的HTML
-
HTML是由
react-dom/server
提供的renderToString
来生成,理论依据是node虽然不能识别HTML和JSX,但React能生成虚拟DOM,然后插入HTML直接返回给浏览器识别渲染
import { renderToString } from "react-dom/server"; import { StaticRouter } from "react-router-dom"; const app = express(); app.get( "/*", (req, res) => { const context = {}; // 上下文对象,可以存储有关渲染的信息 const jsx = ( <StaticRouter context={ context } location={ req.url }> <App /> </StaticRouter> ); const reactDom = renderToString( jsx ); // 在服务端将JSX渲染为HTML res.writeHead( 200, { "Content-Type": "text/html" } ); res.end(` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link rel="icon" href="data:;base64,="> <title>React SSR</title> </head> <body> <div id="app">${ reactDom }</div> <script src="/index.js"></script> // 注意这里,服务端返回的HTMl会去加载webpack打包好的SPA应用,然后客户端接管应用,绑定事件,处理路由跳转和其他Ajax请求 </body> </html> `); }); app.listen(3000, () => { console.log('Running on http://localhost:3000/'); }); 复制代码
const jsx = ( <Provider store={store}> <Router> <Layout /> </Router> </Provider> ); const app = document.getElementById( "app" ); ReactDOM.hydrate( jsx, app ); // 客户端接管应用后,React会将服务端渲染的标记加载,并将尝试将事件侦听器附加到现有标记 复制代码
结合Router
- 服务端通过StaticRouter来渲染对应的组件,根据浏览器请求的url来匹配路由并返回对应组件
- 返回给浏览器后,加载webpack打包好的客户端js,然后接管页面
- 具体看代码哈
结合Redux
- 首屏页一般要请求接口渲染对应数据
-
将请求的数据设置到状态树上,并将JSON一并返回给浏览器
<script> window.REDUX_DATA = ${ JSON.stringify( state ) } </script> 复制代码
-
客户端接管应用后将数据初始化到客户端的状态树上
const store = createClientStore( window.REDUX_DATA ); const jsx = ( <Provider store={store}> <Router> <Layout /> </Router> </Provider> ); 复制代码
怎么按需加载
-
使用
react-lodable
按需加载,其原理是import().then()
,webpack在识别到后就能代码分割了 -
其会生成
react-loadable.json
来标识需要动态加载的组件const jsx = ( <Provider store={store}> <Loadable.Capture report={moduleName => modules.push(moduleName)}> // 需要懒加载的模块,最后在返回的HTML中去遍历 <StaticRouter context={ context } location={ req.url }> <Layout /> </StaticRouter> </Loadable.Capture> </Provider> ); ${bundles.map(bundle => { return `<script src="/${bundle.file}"></script>` // alternatively if you are using publicPath option in webpack config // you can use the publicPath value from bundle, e.g: // return `<script src="${bundle.publicPath}"></script>` }).join('\n')} 复制代码
-
lodable实现原理
Lodable({ loader: ()=> import(/* webpackChunkName: 'Hello' */'./Hello'), loading, }) class Lodable { componentWillMount() { this.cancelUpdate = false; const { loader } = this.props; loader.then(Com=> { this.Com = Com; if(!this.cancelUpdate) { thi.forceUpdate(); // 初次懒加载完后重新渲染 } }) } componentWillUnmount() { this.cancelUpdate = true; } render() { const { comProps } = this.props; return this.Com ? ( <this.Com.default {...comProps} /> ) : ( <this.Com {...comProps}/> ) } } 复制代码
服务端怎么处理CSS和图片
- 服务端还是通过webpack来编译对应的node端的代码,来实现对CSS和图片资源的引用和处理
- 其实这里只要理解客户端和服务端分别打包,最后node运行服务端的js,返回HTML后加载客户端js,实现客户端接管应用
-
处理静态资源还有
webpack-isomorphic-tools
,universal-webpack
,前者作者已经不更新了,我后面发现其实用webpack直接处理简单多了,可能是应用场景还用不到const serverConfig = { target: 'node', // 标记为node端 mode: 'development', entry: './src/server', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'build') }, externals: [nodeExternals()], // 不会讲node_modules打包进去 module: { rules: [{ test: /\.js?$/, loader: 'babel-loader', exclude: /node_modules/, options: { cacheDirectory: true, plugins: ['transform-decorators-legacy'], presets: ["react", ["env", { "targets": { "node": "current" } }], "stage-0"], } }, { test: /\.css?$/, use: ['isomorphic-style-loader', { // 处理CSS服务端的loader loader: 'css-loader', options: { importLoaders: 1, modules: true, // 开启css-module localIdentName: '[name]_[local]_[hash:base64:5]' //生成的class的命名规则 } }] }, { test: /\.(png|jpeg|jpg|gif|svg)?$/, loader: 'url-loader', options: { limit: 8000, outputPath: '../build/', publicPath: '/' } }] }, plugins: [ new ReactLoadablePlugin({ filename: './build/react-loadable.json', // 按需加载的配置文件 }), ] }; 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 假如时光倒流,我要这样学编程
- 为什么我要使用 Optional ?
- JVM,我要把你 “开膛破肚” !
- 祝福我吧各位!我要参赛了...
- 为什么我要构建这个脚手架
- 人生苦短,为什么我要用 Python?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序员2010精华本
程序员杂志社 / 电子工业 / 2011-1 / 49.00元
《程序员(2010精华本)》主要内容:《程序员》创刊10年来,每年末编辑部精心打造的“合订本”已经形成一个品牌,得到广大读者的认可和喜爱。今年,《程序员》杂志内容再次进行了优化整合,除了每期推出的一个大型专题策划,各版块也纷纷以专题、策划的形式,将每月的重点进行了整合,让内容非常具有凝聚力,如专题篇、人物篇、实践篇等。另外杂志的版式、色彩方面也有了很大的飞跃,给读者带来耳目一新的阅读体验。一起来看看 《程序员2010精华本》 这本书的介绍吧!