React-tutorial例子全解析
Talk is cheap,Show me the code
近些时间一直在关注React,关于 如何学习React可以参照链接的文章自行制定计划。千里之行,始于足下。本文是React官方的教程上的一个例子,通过详细地学习,从中收获不少,特此做了笔记,与大家共享交流进步。
起步
-
下载 例子,然后进行解压
-
由于采用的node环境,因此下载解压之后,只需在所在目录运行
npm installnode server.js
-
采用默认端口设置,只需打开浏览器,访问 http://localhost:3000/
目录结构说明
react-tutorial
--node_modules --body-parser:express中间件,用于接收和解析json数据 --express:express框架--public --css --base.css:基本样式文件 --scripts -- example.js:React应用js文件 index.html:基本的HTML结构--.editorconfig:用于在不同的编辑器中统一编辑风格(文件编码)的配置文件--.gitignore:git相关配置文件--app.json:web app的相关信息--comments.json:上传的评论数据--LICENSE:项目代码使用协议--package.json:项目所依赖的包,npm install的安装包的配置文件--README.md:项目说明书,里面有使用说明--requirements.txt:不清楚--server.js:服务器端的js代码
App功能
此项目构建了一个简单的应用,如图所示
服务器端
服务器端的功能还是相对简单的,通过代码注释的形式来分析
-
导入了依赖的模块
var fs = require('fs'); //读写文件var path = require('path'); //路径var express = require('express'); //express框架var bodyParser = require('body-parser'); //中间件
-
生成app,并且进行配置
//获取comments.json文件的路径var COMMENTS_FILE = path.join(__dirname, 'comments.json');//设置端口app.set('port', (process.env.PORT || 3000));//设置静态文件的文件目录路径app.use('/', express.static(path.join(__dirname, 'public')));//启用bodyParser中间件接收请求,并且接收并解析json数据app.use(bodyParser.json());app.use(bodyParser.urlencoded({extended: true}));
-
设置响应头部信息
app.use(function(req, res, next) { //允许跨域 CORS res.setHeader('Access-Control-Allow-Origin', '*'); //缓存设置 res.setHeader('Cache-Control', 'no-cache'); next();});
-
设置get请求url对应的处理函数(获取评论json数据)
app.get('/api/comments', function(req, res) { //读取comments.json文件,并且解析为json数据 fs.readFile(COMMENTS_FILE, function(err, data) { if (err) { console.error(err); process.exit(1); } //读取成功后,返回 res.json(JSON.parse(data)); });});
-
设置post请求url对应的处理函数(提交评论数据)
app.post('/api/comments', function(req, res) { //先读取comments.json文件 fs.readFile(COMMENTS_FILE, function(err, data) { if (err) { console.error(err); process.exit(1); } //将文件内容解析为json数据 var comments = JSON.parse(data); //获取新评论 var newComment = { id: Date.now(), author: req.body.author, text: req.body.text, }; //添加json数组中 comments.push(newComment); //将json数据写回到comments.json文件中,并且返回全部的评论数据 fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) { if (err) { console.error(err); process.exit(1); } res.json(comments); }); });});
-
启动,监听端口
app.listen(app.get('port'), function() { console.log('Server started: http://localhost:' + app.get('port') + '/');});
web端
web端核心在于 example.js文件,结合官网的资料,我们对这个应用进行分析,学习如何构建一个简单的react应用。
组件结构
React践行了 Web Components的理念,依照组件化的开发方式,我们来分析这个应用的组件结构(如图所示):
即是:
-- CommentBox -- CommentList -- Comment -- CommentForm
组件之间的关系图为:
组件Comment
如上述的结构图,我们从最底层开始编写组件 Comment,这个组件需要做两件事情
-
接收上层组件 CommentList传递的数据,动态渲染虚拟DOM节点,则从 props中读取数据
//评论人{this.props.author}//评论的内容{this.props.children}
-
由于评论是支持MarkDown语法的,因此需要使用第三放库 marked对用户输入的内容进行处理。
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
-
此外,输出的内容要解析为HTML,而在默认情况下,基于预防XSS攻击的考虑,React对输出的内容是不解析为HTML的。此时,需要利用到特殊的属性 dangerouslySetInnerHTML,要将内容放到一个对象的 _html属性中,然后将这个对象赋值给 dangerouslySetInnerHTML属性
var html = {_html:"输出的html内容"};<span dangerouslySetInnerHTML={html} />
var Comment = React.createClass({ rawMarkup : function() { var rawMarkup = marked(this.props.children.toString(),{sanitize:true}); return {_html : rawMarkup}; //React的规则,会读取这个对象的_html内容, }, render : function() { return ( <div className="comment"> <h2 id="this-props-author"> {this.props.author} </h2> <span dangerouslySetInnerHTML={this.rawMarkup()} /> </div> ); }});
组件CommentList
组件 CommentList需要做的就是接收上一层组件 CommentBox传递过来的数据,然后根据数据生成多个子组件 Comment
var CommentList = React.createClass({ render : function() { var commentNodes = this.props.data.map(function(comment){ return ( <Comment author={comment.author} key={comment.id}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); }})
在生成子组件 Comment时,将每个子组件的key属性设置为 comment.id,这是因为 key是一个可选的唯一标识符,通过它可以给组件设置一个独一无二的键,并确保它在一个渲染周期中保持一致,使得React能够更加智能地决定应该重用一个组件,还是销毁并重新创建一个组件,进而提升渲染性能。
组件CommentForm
组件 CommentForm需要做的就是两件事情
-
管理自身的状态 this.state(即表单中输入的评论人和评论内容)
-
当表单输入发生变化时
-
当表单提交时
-
-
当submit事件触发时,调用上一层组件 CommentBox的事件处理函数,改变组件 CommentBox的状态。
var CommentForm = React.createClass({ getInitialState : function() { //设置初始状态, return {author:'',text:''}; }, handleAuthorChange : function(e) { this.setState({ author : e.target.value }); }, handleTextChange : function(e) { this.setState({ text : e.target.value }); }, handleSubmit : function(e) { e.preventDefault(); var author = this.state.author.trim(); var text = this.state.text.trim(); if(!text || !author){ //为空验证 return; } //触发评论提交事件,改变父组件的状态 this.props.onCommentSubmit({author:author,text:text}); //改变自身的状态 this.setState({author:'',text:''}); }});
在这里有一个值得注意的点,那就是抽象的自定义事件 commentSubmit和真实的事件 submit之间的联系,这是一个相当实用的技巧,在接下来的章节可以看到是如何实现的。
组件CommentBox
作为整个应用的顶层组件, CommentBox需要做的事情有:
-
从服务器端请求已有的评论数据
-
将新的评论数据上传到服务器
-
管理自身的状态,根据状态对视图进行渲染(状态改变的示意图如下)
var CommentBox = React.createClass({ getInitialState : function(){ return {data : []}; }, loadCommentsFromServer : function() { //使用了jQuery的Ajax $.ajax({ url : this.props.url, dataType : 'json', cache : false, success : function(data) { this.setState({data:data}); }.bind(this), error : function(xhr,status,err){ console.err(this.props.url,status,err.toString()); }.bind(this) }); }, componentDidMount : function() { /* 这个方法属于React组件生命周期方法,在render方法成功调用并且真实的DOM 已经渲染之后,调用此方法,这个方法发送json数据请求,并且设置一个定时器 ,每隔一段时间就向服务器请求数据 */ this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer,this.props.pollInterval); }, handleCommentSubmit : function(comment) { /* 这个方法也是比较有意思: 1. 自定义了一个commentSubmit事件,并且此方法作为该事件的处理函数。 2. 此方法是在子组件CommentForm的submit事件处理函数中调用 */ var comments = this.state.data; comment.id = Date.now(); var newComments = comments.concat([comment]); //改变自身状态 this.setState({data:newComments}); $.ajax({ url : this.props.url, dataType: 'json', type : 'POST', data : comment, success : function(data) { this.setState({data:data}); }.bind(this), error : function(xhr,status,err) { //还原数据 this.setState({data:comments}); console.err(this.props.url,status,err.toString()); }.bind(this) }); }, render : function() { return ( <div className="commentBox"> <h1 id="Comments">Comments</h1> <CommentList data={this.state.data} /> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); }});
最后,只需将组件 CommentBox挂载到真实的DOM节点上,就可以看到效果了
ReactDOM.render( <CommentBox url="/api/comments" pollInterval={2000} />, document.getElementById('content'));

公众号网页更新缓存,这玩意儿,说简单也简单,说复杂也够你喝一壶的。你辛辛苦苦更新了公众号文章,结果用户打开还是老版本,这滋味,谁受得了?这篇文章,咱就来扒一扒这背后的弯弯绕绕,以及如何优雅地解决这个问题。读完之后,你就能轻松应对各种缓存难题,让你的用户始终体验到最新鲜的内容。先说点基础的。网页缓存,说白了就是浏览器或者服务器为了提高访问速度,把一些静态资源(比如图片、CSS、JS)或者页面内容存储起来。下次访问时,直接从缓存里取,不用再重新下载,速度自然快。但这玩意儿,也是个双刃剑。新版本上线,

本文讨论了使用HTML5表单验证属性,例如必需的,图案,最小,最大和长度限制,以直接在浏览器中验证用户输入。

本文展示了使用CSS为网页中添加有效的PNG边框。 它认为,与JavaScript或库相比,CSS提供了出色的性能,详细介绍了如何调整边界宽度,样式和颜色以获得微妙或突出的效果

本文讨论了html&lt; datalist&gt;元素,通过提供自动完整建议,改善用户体验并减少错误来增强表格。Character计数:159

本文讨论了HTML&lt; meter&gt;元素,用于在一个范围内显示标量或分数值及其在Web开发中的常见应用。它区分了&lt; meter&gt;从&lt; progress&gt;和前

本文解释了HTML5&lt; time&gt;语义日期/时间表示的元素。 它强调了DateTime属性对机器可读性(ISO 8601格式)的重要性,并在人类可读文本旁边,增强Accessibilit

本文讨论了HTML&lt; Progress&gt;元素,其目的,样式和与&lt; meter&gt;元素。主要重点是使用&lt; progress&gt;为了完成任务和LT;仪表&gt;对于stati


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3汉化版
中文版,非常好用

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)