-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 62.1 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 62.1 KB
1
{"pages":[{"title":"关于我","text":"我是谁? Σ(っ °Д °;)っ我在哪?Σ(゚Д゚;≡;゚д゚)我在做什么?┬─┬ ノ( ‘ - ‘ノ) ( ╯ ‵ □ ′ )╯︵┻━┻","link":"/about/index.html"}],"posts":[{"title":"文章收藏","text":"在这里收藏着一些我觉得值得收藏的文章。 1、这家医院的医生没退路,他们治不好,全国都没辙,病人真得死 对重症病人来说,死都不是最可怕的,更怕的是不知道自己得了什么病。","link":"/2019/06/18/articles-collection/"},{"title":"高质量图片网站推荐","text":"我们在网站开发中,常常需要去网上找一些图片素材,而好的图片并不是很好找,大部分都会有水印而且都是收费的,所以我将我所收集来的免费的高质量的图片网站分享给大家。 Pexels Pexels帮助设计师,博主和正在寻找图像的每个人找到可以在任何地方免费使用的精美照片。提供由 Pexels 许可证授权的高品质且完全免费的图库相片。 Plixs 上的所有图像,矢量,音频和视频文件均根据创作共享公共领域许可证CC0 1.0获得许可。 Pixabay 是如此充满活力的创意社区,分享版权免费图片和视频。所有内容都根据Simplified Pixabay License许可证发布,这使得它们可以安全使用而无需征得许可或给予艺术家信用 - 即使是出于商业目的。 Picjumbo 所有照片都可以免费使用,但请记住,虽然这里列出的照片可以免费下载和使用,但有些照片没有型号或属性发布。部分照片还可能包含受版权保护的品牌,徽标,物体或个人财产。 Pixeden 的设计理念是为现代设计师在日常工作中可能需要的所有工具创建一站式服务。我们孜孜不倦地为您提供最好的最佳服务,包括我们的高级和免费图形,网页和设计资源。 Unsplash 世界上最慷慨的摄影师社区为您带来超过850,000张免费高分辨率照片。在Unsplash上发布的所有照片都可以免费使用。您可以将它们用于商业和非商业目的。您不需要向摄影师或Unsplash征求许可或提供信用,尽管我们会尽可能地感激。Unsplash License Barnimages Free high-resolution images for everyone.Barnimages License Gratisography Quirky, Creative, Always Free Photos.Gratisography License Chamberofcommerce Stock photo search made easy. Stocksnap Beautiful free stock photos. Streetwill Stokpic Free Stock Photos For Commercial Use. Thenounproject Icons for everything. Manypixels 每周都会发现免版税的插图,为您的项目提供动力。以商业或非商业方式将它们用于您的目标网页,博客文章,电子邮件简报,社交媒体图形等等!我们的许可。 stockvault dreamstime flaticon","link":"/2019/05/08/free-photo-website-recommended/"},{"title":"英语","text":"vt. 指的是及物动词,后面可以直接带宾语的动词。vi. 指的是不及物动词,后面不能带宾语。v. 指的是动词。n. 指名词。adj. 是指形容词。adv. 指副词。 Number English 中文释义 #1 abruptly adv. 突然地;忽然间;猝然;出其不意地;兀地一下; 立刻,立即; 粗暴地;不客气地; #2 absorb v. 吸收(液体、气体等); 使并入; 吞并; 同化; 理解; 掌握; #3 abuse n. 滥用; 妄用; 虐待; 辱骂; 恶语;v. 滥用(以致危害健康); 滥用,妄用; 虐待; 性虐待; 伤害; #4 academic adj. 学业的,教学的,学术的; 学习良好的;n. 高等院校教师; 高校科研人员; #5 access n.通道;通路;入径;(使用或见到的)机会,权利 v.访问,存取(计算机文件);到达;进入;使用; 2019/10/06 #6 accessible 无障碍;易接近的;可访问的;可进入的;可理解的 #7 accommodate 适应;v.为…提供住宿;为…提供空间;考虑到; vi. 适应;调解; vt.容纳;使适应;供应;调解 #8 accomplish vt. 完成; 实现;达到 #9 account for 解释;占比例; 对…负有责任;对…做出解释;说明…的原因 ;导致; #10 accumulate 积累;v.逐渐增长;逐渐增加 vt.积攒;累积;积聚; #11 accurate 准确的;adj.精确的;正确无误的; 2019/10/07 #12 achieve 完成;实现 #13 aknowledge 承认…的权威 #14 acquire 获得 #15 acute 敏锐的 #16 adapt 适应于 #17 addiction 上瘾 #18 be addicted to 上瘾,沉溺于; #19 additional 额外的 #20 address 解决 2019/10/08 #21 adequate 足够的 #22 administration 管理;政府 #23 administrative 管理的 #24 admire 钦佩;称赞 #25 admiration 钦佩;称赞 #26 admission 准许进(加)入;入场费;承认 2019/10/09 #27 admit 承认,容许 #28 advance 前进,提前 #29 in advance 预先,提前 #30 advanced 高级的 #31 advanced technology 先进技术 #32 advancement 进步,提升 #33 advice 建议 #34 advisory 报告 #35 advocate 倡导 #36 advocator 主张者,倡导者 #37 affectionate 深情的,柔情的 #38 affirmation 断言,肯定 #39 affirm 断言,声明 #40 afford 负担得起 #41 agency 机构 2019/10/10 #42 aggressive 挑衅的,好斗的;有上进心的 #43 alarm clock 闹钟 #44 album 粘贴簿,集邮簿,相册 #45 alcohol 酒精 #46 alert 提醒;机警的 2019/10/11 #47 alter 改变 #48 altitude 海拔;高度 #49 ambitious 有雄心的,野心勃勃 #50 analyze 分析 #51 ancient 古代的 2019/10/12 #52 announce 宣布, 声称 #53 annual 每年的 #54 annually 每年, 一年一次 #55 anticipate 预感 2019/10/13 #56 antique 古玩, 古董 #57 apartment 公寓 #58 appeal 吸引, 呼吁 #59 appeal to 呼吁; 对…有吸引力 #60 appearance 外表, 出现 2019/10/16 #61 appetite 欲望, 胃口 #62 applicant 申请人 #63 apply for 申请 #64 application 申请, 应用 #65 apply 应用, 申请 #66 apply to 适用于 #67 appoint 任命, 委派; 约定, 确定, 指定 #68 appointment 约会 #69 appreciate 感激, 欣赏 2019/10/15 #70 appreciation 评价, 感激 #71 appreciate 欣赏, 增值 #72 approach v. 靠近; n. 接近, 方法 #73 approve 赞成, 同意 #74 approval 批准, 认可, 赞成 #75 approximately 大约 2019/10/16 #76 architect 建筑师 #77 archive 档案文件 #78 arrange 把…分类 #79 arrest 逮捕 #80 ashamed 害羞的 2019/10/17 #81 assemble 组装;集合 #82 assembly 装配 #83 asset 有价值的人(或物)资产 #84 assign 分配, 布置 #85 assignment 作业 2019/10/18","link":"/2019/10/06/english/"},{"title":"life-record","text":"洞察人心观察别人,可以提升个人的各种能力,例如,看别人如何说话来学习别人的沟通技巧,观察别人如何做事情来学习别人的做事技巧。 不要低估任何人 言语、行为、 时刻总结。","link":"/2019/11/21/life-record/"},{"title":"Hello World","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new \"My New Post\" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment","link":"/2019/04/30/hello-world/"},{"title":"一篇报纸的故事","text":"","link":"/2019/10/22/my-story/"},{"title":"Node.js 学习笔记","text":"一、什么是 Node.js?官方说法: Node.js®是基于Chrome的V8 JavaScript引擎构建的JavaScript运行时。 Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. 可以理解为,Node.js 是使用JavaScript语言进行开发的一种小型服务器,拥有Java、.NET、PHP 等处理后端的能力。 1.1 下载与安装Node.js官网 我们先去官网下载安装包,我们可以看到 在Windows系统下会显示两个版本,LTS(Long Time Support) 是长期支持版的意思,是 Node.js 版本相对稳定的版本。而 Current 是当前最新版本,里面包含一些最新特性,如果不追求稳定可以在此版本下进行开发(建议不要用于生产环境)。 安装完成以后,运行 node --version 或 node -v, 查看是否安装成功。 123$ node --version# 或者$ node -v 1.2 基本语法我们可以使用以下命令,来执行脚本文件。 1$ node app.js 二、Node.js 入门2.1 Hello World我们首先看一个简单的例子,仅需几行代码就可以开启服务器接收页面请求和响应。 123456789var http = require('http');var server = http.createServer();server.on('request', function (request, response) { response.end('Hello World!');});server.listen(3000); 上面代码中,我们引入了 http 核心模块,并创建服务器接收请求,开启服务器并监听端口号。页面的显示效果如下。 2.1.1 内置http核心模块我们可以使用 require('http') 来引入http核心模块,进行创建Web服务器,接收客户端发来的请求和响应数据等操作。 1234567891011121314151617181920212223242526// 1. 使用 require 加载 http 核心模块var http = require('http');// 2. 创建 Web服务器var server = http.createServer();// 3. 监听 request 请求事件server.on('request', function (request, response) { console.log('收到客户端发送的请求!'); // 使用 设置响应头 解决返回数据中文问题 response.setHeader('Content-Type', 'text/plain; charset=utf-8'); // response 响应客户端发送的请求 response.write('Hello World!'); // 结束响应 response.end();});// 4. 监听端口,启动服务server.listen(3000, function () { console.log('服务器启动成功!');}); 2.1.2 内置fs核心模块我们可以使用 require('fs') 来引入fs核心模块,进行文件的读写等操作。 1234567891011121314// 1. 使用 require 加载 fs 核心模块var fs = require('fs');// 2. 读取文件fd.readFile('./文件路径', function (err,data){ // 如果err不为空,则说明文件上传失败! if(err){ console.log('文件读取失败!' + err.message); return; } console.log(data);});","link":"/2019/05/22/nodejs-note/"},{"title":"语录(一)","text":"当我孤独时,看谁都满是羡慕,羡慕他人丰富多彩的生活,也羡慕他们不必独自承担这份孤独。 弗里德里希·威廉·尼采(Friedrich Wilhelm Nietzsche) 1、当你经历七重的孤独,才能成功真正的强者。 2、当我到达高处,便发觉自己总是孤独的,无人同我说话,孤寂的严冬令我发抖,我在高处究竟意欲何为。 3、也许你感觉自己的努力总是徒劳无功,但不必怀疑,你每天都离顶点更进一步。今天的你离顶点还遥遥无期。但你通过今天的努力,积蓄了明天勇攀高峰的力量。 4、如果你无法潜到水底,就不要说水是深不可测的。 5、但凡不能杀死你的,最终都会使你更强大。 6、当你凝视深渊时,深渊也在凝视着你。 7、我们飞得越高,我们在那些不能飞的人眼中的形象 就越渺小。 8、要么庸俗,要么孤独。 9、你今天是一个孤独的怪人,你离群索居,总有一天你会成为一个民族! 10、假使有神,我怎能忍受我不是那神,所以没有神! 11、世界弥漫着焦躁不安的气息,因为每一个人都急于从自己的枷锁中解放出来。 12、聪明的人只要能掌握自己,便什么也不会失去。 13、当心!他一沉思,就立即准备好了一个谎言。 14、一个人知道自己为什么而活,就能偶够忍受任何生活。 15、不能听命于自己者,就要受命于他人。 鲁迅 1、猛兽总是独行,牛羊才成群结队。 2、当我沉默的时候,我觉得充实;我将开口,同时感到空虚。 3、不在沉默中爆发,就在沉默中灭亡。 4、惟有沉默是最高的轻蔑 5、于浩歌狂热之际中寒;于天上看见深渊。于一切眼中看见无所有;于无所希望中得救。 6、人生得一知已足矣,斯世当以同怀视之。","link":"/2019/08/10/quotations-1/"},{"title":"左撇子人生(一)","text":"回顾一下我的人生,并没有什么特别值得回味的事情。 今天是2019年5月6日,立夏,晴天但感觉不到什么温暖,尽管有太阳,冷风却依旧往衣领里灌。一整天我都坐在办公室里戴着耳机听着一些小众音乐冷漠的敲着毫无意义的代码,同事们也都在认真工作或讨论着一些业务方面的事情,因为我在一家医院外包公司工作,所以在这间 信息外包办公室 里还有其他公司的外包人员,人多嘴杂避免不了有些吵闹,唯一清净的时候就只有早晨和中午了。 从年初上班开始,基本上就是在维护去年开发的项目。出现一些新需求或者需要修复的BUG就会忙一下,弄完就基本就没什么事了。差不多是忙一下闲一下的状态,我会在没有需求的时候做自己的项目用来提升自己的技能。 这张开发路线图是我目前正在学习的路线图,尽量让自己不走弯路的学习提升技能。从2015年底开始工作到现在已经好多年了,但刚毕业的时候我想走的技术路线是Java,但无奈第一家公司只有算老板只有二十个人还赶上他们正在做项目,而我是个新人领导并没有让我也接手项目,所以自己前期只是在“混日子”。学校里面教的 Java/C#/JavaScript/CSS/HTML/Oracle/SQLServer 这些技能我都会,同学都说我是班级的学霸。但我自己却觉得我技术一般,现在想来也还是很一般。后来领导问我会不会.NET,然后我就被调到 .NET组 开发内部即时通讯系统,当时的技术还是 WCF + WPF,这大概是我跟.NET的缘分了。 后来我也因为一些原因离职了,2016年来到上海工作,这家公司一共只有不到十个人,而我是整个公司的第一个技术人员,他们此前的项目是从合作公司买来的,我来了之后就直接上手做项目,所以我的职业生涯从一开始就没有人带过我,这也造就了我在第二家公司的败笔。当时的项目是很古老的ASP.NET 拖拽控件的形式开发Web项目,学校里仅教过ASP.NET MVC,我就只好自学,每天在挨骂中度日,后来,我通过自学的Java SSM框架(即:Spring|SpringMVC|MyBatis,当时最流行的框架,那时候还没有SpringBoot等框架的出现),做了好多项目(当然只有我自己一个人,所以并不知道自己的水平,只是觉得自己很垃圾),在年底的时候又过来了一套框架,依旧是ASP.NET但不同的是,前端框架是Bootstrap,采用 Ajax + ashx 的方式进行请求数据。然后开始了每天都是很无聊的增删改查,自己曾因为赶这个项目,几个月无休息的从早晨六点钟干到凌晨两三点钟下班,自己的生日和圣诞节都是在加班中度过。 不知道什么时候开始我的编程生涯开窍了,觉得好多技术即使自己没见过没用过看看项目源码就懂得怎么写了,第二年开始招人,实施、.NET开发工程师、前台。其中.NET人员由我来面试我来带团队,我没过带过人的经验,而招的人都是实习生,领导说想培养一些可以走到公司最后的人,实际上只是想省一些钱,我带领团队之后我的败笔就来了,我变得越来越疲惫,他们不会还不学习,领导规定他们要做完的日期到了,他们做不完还得我给他们做,到头来整个项目还是我一个人做的,而他们的工资几乎都是我给他们拿的,就这样带了一年,他们依旧不会熟练地掌握这套框架,最终年底的时候带的新人走了一个不剩的,原因就是领导经常骂他们,第三年我也准备离职了,让他们招三年工作经验以上的人,每次谈这件事他们都回避不想谈(毕竟像我这样勤劳不辞辛苦还价格公道的人不好找)总是画大饼和劝我走了就找不到好的公司了,说我技术太垃圾了再多“呆”几年再走就好找工作了,而我已经下定决定了决心~找到一家以技术为核心的公司。毕竟自己的知识储备还很欠缺,前方的道路也依旧很遥远。 其实有一件事情我并不想谈,就是职场和生活。我一直都在小公司工作,而小公司的工作氛围不压抑很适合我,但这第二家公司我还是职场新人自以为和领导同事老板的关系都算是蛮好的,而他们也都很关心我的样子。但我离职的时候,我突然把所有的事情都相通了,通过打听后发现我带的人和我工资是一样的,而我这几年都没有涨过工资,勉强刚过上海薪资最低标准,而五险一金社保什么的骗我说帮我办了意思是还要我感激他们,但我走了才知道什么都没有办就连劳动合同都是快走的时候签的。所以的关心都是虚假的,所有的好话都是骗人的,领导当着我的面骂其他的新人,我就知道,我走了之后他们依旧会把骂其他人的话附加到我的身上说给其他人听。所以不必委屈自己,做好自己问心无愧。 达克效应(英语:D-K effect),全称为邓宁-克鲁格效应(英语:Dunning–Kruger effect),是一种认知偏差,能力欠缺的人有一种虚幻的自我优越感,错误地认为自己比真实情况更加优秀。简言之即:庸人容易因欠缺自知之明而自我膨胀。Kruger和Dunning将其归咎于元认知上的缺陷,能力欠缺的人无法认识到自身的无能,不能准确评估自身的能力。他们的研究还表明,反之,非常能干的人会低估自己的能力,错误地假定他们自己能够很容易完成的任务,别人也能够很容易地完成。 我曾一度非常自卑,我从学习到工作到现在,总是听到别人说我很厉害,但我并不知道自己厉害在哪里,一直觉得他们是外行都是在高估我,只有自己才知道自己的水平是什么样的。学的越多越觉得自己学的太少了,在第二家工作的几年时间里我的工资低没有攒下钱,没有攒下技术只是增删改查更加熟练。而公司又总是打击我,让我觉得自己太太太太太太垃圾。而自己懦弱内向的性格,无处发泄,造成了无意识的暴脾气很讨厌自己,跟家里人平时打电话聊天的时候,也会对他们发泄。我就是那种窝里横的人,对别人毕恭毕敬的,把所有的不满发泄给最亲近的人。 随着时间增长,我看开了许多事情,我依旧独来独往,但我并不孤独。我并不会看其他人对我什么看法什么评价那是他们的事与我无关,我只想做好自己。 希望自己现在的努力换来的是不让未来的我,讨厌现在的自己。","link":"/2019/05/06/left-hander-1/"},{"title":".NET 5","text":"最近看到了微软官方的 .NET Blog 中写的一篇.NET 5, 讲述的是 .NET Core 3.0 之后的下一个版本 .NET 5,不禁让我感到技术发展的迅猛。 微软官方说以后的每年的十一月份进行版本升级,此后将 .NET Framework 和 .NET Core 进行合并,所以再次升级就会是 .NET 5、.NET 6、.NET 7、.NET 8 从最初在学校中学习的 WinForm 和 ASP.NET MVC4 开始,工作后又使用的WebForm,ASP.NET MVC 5 再到现在自学的 .NET Core,微软终于要把 .NET Framework 和 .NET Core 整合到一块了。 今年会在9月份会发布 .NET Core 3.0 并且会和 C# 8.0 进行同时发布 C# 8.0 官方原文 微软的 Blazor 允许使用C#而不是JavaScript构建交互式Web UI,客户端和服务器代码都是用C#编写。可以使用WebAssembly直接在浏览器中运行客户端C#代码,因为它是在WebAssembly上运行的真实.NET。Blazor可以在服务器上运行客户端逻辑。Blazor使用开放的Web标准,没有插件或代码转换,Blazor适用于所有现代Web浏览器,包括移动浏览器。Blazor 官方原文","link":"/2019/05/07/dotnet-net5/"},{"title":"编程小知识","text":"编程随笔记,想到哪记到哪。 命名法 驼峰命名法:Camel-Case,小驼峰,即第一个单词首字母小写,之后的每个单词首字母大写。 帕斯卡命名法:Pascal,又被称作大驼峰,即每个单词首字母大写。 下划线命名法:即每个单词之间用下划线隔开,单词可以全部小写,也可全部大写。 匈牙利命名法:即命名时需要 属性+类型+对象描述,要求每个 JavaScript 中的判断 Falsy(类假): undefined null 0 false ‘’ NaN Truthy(类真): 除了类假值以外都是类真值 HTML 属性顺序 class id, name data-* src, for, type, href, value title, alt role, aria-* HTML编码规范 HTML Web 打印时进行强制分页12<!--加入此标签进行分页--><div style="page-break-after: always;"></div> 关闭当前页面123window.opener=null;window.open('','_self');window.close(); int 类型 3/6 结果结果为0:因为 int 直接抹除小数,不进行四舍五入。 is 和 asis:检查对象是否与给定类型兼容;as:运算符类似于强制转换操作,用于检查在兼容的引用类型之间执行某些类型的转换,如果转换是不可能的,sa返回null而不引发异常。","link":"/2019/10/06/programming-knowledge/"},{"title":"冬至(一)","text":"什么是冬至?冬至 是一个基于 .NET Core 平台的开源项目,其目标是构建一个简单易用的模块化的高性能分布式系统。 项目架构采用全新技术栈,遵循领域驱动设计(Domain-Driven Design,DDD)规范并结合命令查询职责分离(CQRS)架构实现事件驱动(Event-Driven Architecture,EDA)、事件溯源(Event Sourcing,ES)等特性。 集成多数据库与多ORM自由平滑切换及组合使用,支持多语言切换与前端多主题切换。 集成 IdentityServer4 进行身份验证及授权。 加入消息队列,分布式缓存以提高系统性能。 采用EFK(ElasticSearch, FluentD, Kibana)方案进行日志收集,基于分布式的全文搜索引擎(ElasticSearch)提高搜索海量数据的查询效率,FluentD是日志记录层的数据收集器,并搭配 Kibana 实现可视化管理和数据分析。 加入Zipkin.NET进行数据跟踪。 使用 RESTFul API 风格开发 Web API,并配合Swagger进行接口文档在线自动生成及可视化展示。 使用测试驱动开发(Test-Driven Development, TDD)方式,设计高内聚低耦合代码,降低维护成本。 使用流畅接口(Fluent Interface)风格编写链式代码。 将程序部署至Docker或k8s中。 冬至名称的由来?冬至 为中国二十四节气中的第二十二个节气,中国古代对冬至很重视,被视为冬季的大节日。冬至在古代称之为 “亚岁” 使用到的技术栈 应用框架 ASP.NET Core 3.0 ASP.NET Core WebAPI Blazor gRPC 数据库 Microsoft SQL Server Oracle MySQL PostgreSQL MongoDB 对象关系映射 EntityFrameworkCore - 是轻量化、可扩展、开源和跨平台版的常用 Entity Framework 数据访问技术。 Dapper - .Net的简单对象映射器 Solstice 缓存 Redis - 是一个开放源代码(BSD许可)的内存中数据结构存储,用作数据库,缓存和消息代理。它支持数据结构,例如字符串,哈希,列表,集合,带范围查询的排序集合,位图,超日志,带有半径查询和流的地理空间索引。Redis具有内置的复制,Lua脚本,LRU逐出,事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供了高可用性。 消息队列 RabbitMQ - 是部署最广泛的开源消息代理。 定时任务 Quartz.NET - 是功能齐全的开源作业调度系统,可用于最小的应用程序到大型企业系统 Hangfire - 在.NET和.NET Core应用程序中执行后台处理的一种简便方法。无需Windows服务或单独的过程。 即时通讯 SignalR - 适用于ASP.NET的极其简单的实时Web WebSocket DotNetty - Netty端口,事件驱动的异步网络应用程序框架 前端框架 React - 一个用于构建用户界面的JavaScript库 Vue - 渐进式JavaScript框架 Angular - 一个框架,手机和台式机。 故障处理 Polly - 是一个.NET弹性和瞬态故障处理库,允许开发人员以流畅和线程安全的方式表达诸如重试,断路器,超时,隔离头和回退之类的策略。 应用容器 Docker - 提供了一种在容器中安全隔离地运行应用程序的方法,该容器及其所有依赖项和库打包在一起。 参考开源项目 EventFlow NEventStore NopCommerce SimplCommerce EquinoxProject eShopOnContainers eShopOnWeb Ocelot OrchardCore Weapsy 官方推荐参考书籍 Eric Evans, Domain-Driven Design:软件核心复杂性应对之道 Eric Evans, 域驱动设计参考:定义和模式摘要 Vaughn Vernon, 实现域驱动设计 Vaughn Vernon, 域驱动设计核心理念 Jimmy Nilsson, 应用域驱动设计和模式 Cesar de la Torre, 使用 .NET 的 N 层域导向体系结构指南 Abel Avram 与 Floyd Marinescu, 快速完成域驱动设计 Scott Millett,Nick Tune - 域驱动设计的模式、原则和实践","link":"/2019/09/14/wintersolstice-1/"},{"title":"React.js","text":"一、什么是 React?React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。 React 拥有 声明式、组件化,一次学习随处编写等特点。 React 应用的核心是组件,组件实际上是一段用户界面的代码, 我们将创建一组独立、可重用的组件来组合成复杂的用户界面,每个 react 程序至少有一个组件也就是 root 根组件,这个组件代表了整个程序,并且包含了很多的子组件。 React 使用渲染方法 render() 来渲染元素,也就是在 JavaScript 中对于的 DOM 元素,react 在内存中保持着一个轻量级的 DOM 元素树,也就是我们常说的虚拟 DOM,当我们改变一个组件的状态时,我们就得到了一个新的 react 元素,react 会将之前的组件和子组件进行比较,对比差异后更新浏览器中的 DOM,以确保信息保持同步。 在使用 React 创建程序时不同于传统的 JavaScript 库比如 jQuery,我们不直接操作浏览器的真实 DOM 元素,我们不必为了捕获 DOM 元素写代码,然后处理 DOM 的请求,而是只需要简单的改变组件的状态,之后 react 将会自动检测并修改 DOM 以同步状态,这也就是为何这个库称为 React (反应),因为当我们状态改变的时候,react 是真的’反应过来’状态已经改变,并且去更新 DOM。 我们经常会被问到一个问题是关于 Angular 与 React 的,它们都是基于组件逻辑的架构,但不同的是 Angular 是一个框架,或者说是一个完整的解决方案。而 React 只是只是一个库,只关注显示部分的东西,并且确保视图始终同步,所以 react 需要学习的接口很少,我们为了实现目标还要借助其他的库,比如 Routing 或处理 HTTP 请求,这相比 Angular,我们要自由很多我们可以选择自己喜欢的库。 二、开发环境请先确保您安装了较新版本的 Node.js,我们不会用到 Node.js 但会使用内置工具也就是包管理工具 npm,来安装第三方库。 12345678# 查看Node.js版本node -v# 查看npm版本npm -v# 更新npm版本npm install -g npm 我们先来安装 create-react-app 脚手架工具类库来创建 react 项目。 12345# npm 是我们要执行的包管理工具# i 代表 install 安装的意思# -g 代表 global 全局的意思# create-react-app 是我们要安装的包npm i -g create-react-app 如果是 Mac 或 Linux 系统需要配置权限,在前面添加 sudo 关键字会使命令在管理员权限下运行 1sudo npm i -g create-react-app 我在此使用微软的 Visual Studio Code 编辑器来进行开发,因为它是一个漂亮的轻量级和跨平台的编辑器。我将要安装两个扩展,这样开发起来比较方便。 首先是 Simple React Snippets 是由 Burke Holland 开发的,这个扩展已经有 345,580 安装了,是个很受欢迎的扩展,它是用来可以让我输入缩写然后自动生成代码的工具,安装完毕后需要重启 VS Code。 下一个是 Prettier - Code formatter 是由 Esben Petersen 开发的,这个扩展已经有 13,842,180 安装了,我们使用这个扩展来进行格式化我们的代码,我们可以开启保存时格式化的设置,当我们保存时这个扩展将自动格式化你的代码。 我们打开 文件 -> 首选项 -> 设置 或者直接 ctrl + , 打开设置页面,我们可以搜索 formatOnSave 或者在 文本编辑器 -> 正在格式化 中的 Format On Save 项勾选之后,就可开启保存时自动格式化的功能。 使用 Prettier - Code formatter 插件时默认会将单引号转换为双引号,可在根目录中 创建 .prettierrc.json 文件将 singleQuoto 设为 true,即可将双引号改为单引号,semi 改为 false 会将多余的分号去掉。 1234{ \"singleQuoto\": true, \"semi\": false} 三、第一个 React 应用我们使用脚手架创建一个 React 应用程序,我们在控制台中输入 create-react-app 然后设置一个程序的名称。这将会安装 react,并安装与之相关的第三方库,首先会安装一个轻量级的开发服务器 Development Server,然后打包代码的 Webpack,最后是转译器 Babel可以转换我们的代码,还有一些其他的工具。所以在使用工具创建 react 程序的时候,不需要任何配置,所有的设置都配置好的了 ZERO-CONFIG SETUP,但是如果你在生产环境中想自己配置程序,你可以通过 npm run eject 来实现。 1create-react-app react-app 创建好项目之后,我们可以使用以下命令来对我们的项目进行管理。 1234567891011# Starts the development server.npm start# Bundles the app into static files for production.npm run build# Starts the test runner.npm test# Removes this tool and copies build dependencies, configuration files and scripts into the app directionary. If you do this, you can't go back!npm run eject 首先进入我们创建好的项目的文件夹中,然后在控制台输入 npm start 运行我们的项目。 12345# 进入 react-app 文件夹cd react-app# 启动项目npm start 项目会在本机开启 3000 端口号运行我们的应用程序,并自动打开默认浏览器,我们的第一个 React 应用程序就建立好了。 我们回到 VS Code 中看到项目的目录结构中,有三个文件夹。 node_modules 这个文件夹里面是所有的模块,包括 react 本身。这个文件夹我们不需要关心。 public 这个文件夹是可公共访问程序的文件夹,里面包含图标、首页、mainfest 文件。index.html 文件中的 <div id="root"></div> 这个标签是我们的 react 程序的容器,所有组件呈现的地方。 src 这个文件夹是主要的源代码文件夹,里面有个基本的组件也就是 App 组件。我们打开 App.js文件可以看到里面使用了一些ES6语法。 ES6: 全称是 ECMAScript6.0,ECMAScript 是一种由 ECMA 国际(European Computer Manufacturers Association)通过 ECMA-262 标准化的脚本程序设计语言,可以理解为是 JavaScript 的一个标准。 我们从零开始,将 src 文件夹下的所有文件删除,然后在 src 文件夹下新建一个 index.js 文件,编写以下代码。 123456789101112// import 后面是我们要导入的对象名称,from 后面是模块名称import React from \"react\";import ReactDOM from \"react-dom\";// 定义一个element常量,并且赋值为一个JSX代码段const element = <h1>Hello World</h1>;// 在真实DOM中渲染元素需要调用ReactDOM中的render方法// 第一个参数是我们传入的想要渲染的元素// 第二个参数是我们想要在哪里渲染这个元素。在index.html文件中有个root容器标签,我们通过id名root得到这个元素// 这个ReactDOM方法已经得到了真实DOM元素的引用,然后把element元素渲染到那个引用当中ReactDOM.render(element, document.getElementById(\"root\")); 当我们保存时这个程序会自动重启,不需要回到浏览器手动刷新,这就是我们常说的 实时模块重载 四、ES6 基本语法在我们开始真正学习 React 之前,我们先要掌握一些 ES6 的基本知识。 变量与常量的关键字(var、let、const) 对象(Objects) this 关键字(The this keyword) 箭头函数(Arrow Functions) 对象解构(Object Destructuring) 展开操作符(Spread Operator) 类(Classes) 模块化(Modules) 如果您对 ES6 的知识已经足够掌握,请跳过第四部分,直接从第五部分开始看。 Ⅰ. let vs var vs const我们先要说一下创建变量和常量的关键字,我们一般在定义变量时经常会使用 var 关键字,但是 var 关键字有一些小问题。 123456function sayHello() { for (var i = 0; i < 5; i++) { console.log(i); }}sayHello(); 例如上面的代码在循环的时候定义的变量 i,按理来说 i 应该只是在 for 代码块内部可以被访问的,但是如果我在循环的下边打印 i,就会发现 i 这个变量还是可以被访问的。 123456789function sayHello() { for (var i = 0; i < 5; i++) { console.log(i); } // 此处会输出5,因为i在循环到第5次的时候i在最后又进行了+1操作,到第六次循环i已经不再小于5了,所以循环就跳出了 console.log(i);}sayHello(); 这个问题在 ES6 或者说 ECMAScript6,也就是 ECMAScript 2015 当中,我们获得了一个新的变量关键字 let。let 就是来解决这个状况的,当使用 let 来定义变量的时候,这个变量只能在被定义的代码块内部被访问。我们来试验一下。 123456789function sayHello() { for (let i = 0; i < 5; i++) { console.log(i); } // 此处会输出5,因为i在循环到第5次的时候i在最后又进行了+1操作,到第六次循环i已经不再小于5了,所以循环就跳出了 console.log(i);}sayHello(); 程序在执行到第 6 行时会报一个 i 未被定义的错误,Line 6: 'i' is not defined no-undef,这个足以证明 let 关键字的作用域是只可能在作用域范围内部才可以使用。 在 ES6 中还有一个关键字 const,我们使用 const 来定义常量,它与 let 类似,const 的作用域也是块,所以 const 定义的常量只能在定义它的块中可以访问。 1234// 定义常量x并赋值为1const x = 1;//为常量x重新赋值为2x = 2; 在执行这段代码的时候将会报错,Syntax error: "x" is read-only 我们可以看到 x 是只读的,换句话说,如果用 const 定义一个常量,这个常量就不能被再次赋值,它不能改了所以对比变量它叫常量。 注意:const 并不是不可改变的 总结:当以 var 来定义变量的时候作用域是函数,当以 let 来定义变量的时候作用域是代码块,当以 const来定义常量的时候作用域也是代码块,所以我们在编写代码的时候可以用 let 来定义变量,除非有十足的理由使用 var,否则我们推荐用 const 和 let 取而代之。 Ⅱ. Objects在 JavaScript 中对象是一组键值对。在 JavaScript 的 OOP(面向对象编程)中,“键”对应的值是函数时我们称之为方法,在 ES6 的语法中,我们在对象中定义的方法可以不需要写冒号和 function 关键字。 1234567891011const person = { name: \"React\", walk: function() { console.log(\"walk\"); }, // ES6语法,简化了在对象中的方法声明 talk() { console.log(\"talk\"); }}; 在 ES6 中,有两种可以访问对象成员的方法。 1234567//第一种是 [点操作符] 直接 对象.属性 或 对象.方法 就可以访问对象成员。person.name;person.walk();//第二种是使用中括号然后传入字符串去访问属性或方法,这样我们可以重新给成员赋值person[\"name\"] = \"ReactZeroToOne\";person[\"talk\"](); Ⅲ. this我们先创建一个对象里面定义一个方法在内部调用 this 关键字,在 js 中 this 的表现行为和 C#或 Java 不太一样,在 js 中,this 总是返回一个当前对象的引用。 123456789101112131415161718const person = { name: \"React\", walk() { // 输出 this console.log(this); }};// 调用talk方法,this会输出 person 对象person.walk();//定义常量,赋值为 person 的 walk 成员,这里没有调用walk方法,只是得到了它的引用。const walk = person.walk;//得到的是person对象里的walk方法console.log(walk);//但是调用walk方法的时候输出的结果却是 undefinedwalk(); this 的取值,取决于函数是如何被调用的,如果你使用的对象的形式是调用方法,this 永远返回这个对象。但是如果使用单独函数的方式调用,this 这个时候指向的是浏览器的全局对象,也就是 window 在 js 中函数也是对象,在 person.walk 的时候实际上是一个对象,我们可以不信的话可以在 person.walk 后面加个点就可以看到它的成员了,里面有个 bind 函数可以用它将函数绑在一个对象上。 12345678910111213const person = { name: \"React\", walk() { console.log(this); }};person.walk();// 将 person.walk 绑定到 person 对象上// 使用bind方法可以永久的设置this引用const walk = person.walk.bind(person);// 这时调用walk函数的时候,就会看到this指向了person对象walk(); Ⅳ. Arrow Functions (箭头函数)我们先来看看普通函数声明和 ES6 语法的函数声明对比。 1234567// 普通函数声明const square = function(number) { return number * number;};// 箭头函数声明const cube = number => number * number * number; 我们可以看到使用箭头函数声明的函数代码更加的简洁,不需要写 function 关键字,如果函数内只有一行代码连 return 都不需要写。 我们可以再看一个例子,就可以很明显的对比出箭头函数更加的简洁。 1234567891011121314const jobs = [ { id: 1, isActived: true }, { id: 2, isActived: false }, { id: 3, isActived: true }];// 使用数组的filter函数,这个函数会将数据遍历并将每条数据传入这个匿名函数中,这个函数会根据筛选的条件过滤并返回结果// 这里的筛选条件是只筛选出 isActived 为 true 的数据const activeJobs = jobs.filter(function(job) { return job.isActived;});// 我们可以使用箭头函数很好的简化这个函数const activeJobs2 = jobs.filter(job => job.isActived); 我们在箭头函数中定义的 this 和普通函数中的 this 也不太一样。 123456const person = { talk() { console.log(\"this\", this); }};person.talk(); // 输出 person 对象 我们在调用 person 对象的 talk 方法是返回的结果是这个 person 对象,但是如果我们延迟输出这个 this 得到的结果是什么呢? 12345678const person = { talk() { setTimeout(function() { console.log(\"this\", this); }, 1000); }};person.talk(); // 输出 window 全局对象 这里我们在调用 person 对象的 talk 函数时,输出的是 window 对象,而不是 person 对象。原因是这个传入的匿名函数不属于任何对象,它与 person 对象的 talk 方法是不同的,它是一个孤立的函数,我们之前说以一个孤立函数的方式调用函数的时候,默认 this 会返回 window 全局对象,在之前我们没得到 window 而是未定义,是因为之前我们调用的时候严格模式介入了,并将 this 设置为未定义。但是现在这种情况比较特殊,它是回调函数,严格模式并不会重新设定 this 是 window 的行为。 在以前如果我们想解决这种问题,需要这样来写。 1234567891011const person = { talk() { // 我们声明一个变量来保存此时的this var self = this; setTimeout(function() { // 在回调函数中调用self console.log(\"self\", self); }, 1000); }};person.talk(); // 输出 person 对象 我们可以看到这时的 self 指向的时 person 对象了。这是以前的一些写法,现在有了箭头函数就不用这样了。 123456789const person = { talk() { // 将匿名函数修改为箭头函数 setTimeout(() => { console.log(\"this\", this); }, 1000); }};person.talk(); //输出 person 对象 因为箭头函数不重新绑定 this,使用箭头函数替换匿名回调函数时会继承 this 设置,所以输出的还是 person 对象。 Ⅴ. Object Destructuring 对象解构在 ES6 语法之前如果我们想要得到一个对象里面的成员会需要这样来写。 12345678const address = { street: \"\", city: \"\", country: \"\"};const street = address.street;const city = address.city;const country = address.country; 声明每个对立的变量对于一个属性,这段代码的问题是我们重复的使用 address. 这样的语法。解构就解决了这样的问题。 1234567const address = { street: \"\", city: \"\", country: \"\"};const { street, city, country } = address; 这样其实就是把 address 对象对应的属性取出来放在 street 常量中,我们不必把所有的属性全部取出来,如果只想取 street 就把其他的删掉就可以了。 1const { street } = address; 如果我们想要给变量起别名可以使用冒号 : 这样我们就会得到一个新的常量 st。 12// 冒号前对应的是 address 的属性,冒号后是常量名const { street: st } = address; ES6 中的 Object Destructuring 中文被译为 解构 或 析构 Ⅵ. Spread Operator (展开操作符)在 ES6 的语法中还有一种常见的写法就是 展开操作符,它的语法是使用 ... 三个点,比如我们想要合并两个数组,可以这样写。 12345678const first = [1, 2, 3];const second = [4, 5, 6];// 使用数组的 concat 函数拼接两个数组const combined = first.concat(second);// 使用展开操作符来拼接数组const combined2 = [...first, ...second]; 我们可能会疑问了,这不是差不多嘛?好处在哪里?如果我们想在 first 之前或之后添加一个多个元素,就可以看出展开操作符的好处了。 1const combined3 = [\"a\", ...first, \"b\", ...second, \"c\"]; 使用展开操作符也可以轻松组合对象。 123456const one = { first: \"a\" };const two = { second: \"b\" };// 组合one、two、和其他属性// 结果是:{ first:\"a\", second:\"b\", third:\"c\" }const combined = { ...one, ...two, third: \"c\" }; Ⅶ. Classes (类)类可以帮我们实现代码复用,我们可以通过构造函数 constructor 来传参,这里的 this 永远指向的是 Person 这个类,我们在创建一个类的实例的时候需要使用操作符 new 来实例化它。 1234567891011class Person { constructor(name) { this.name = name; } walk() { console.log(\"walk\"); }}// 实例化 Person 类,传入参数 nameconst person = new Person(\"React\"); 在 js 中页面面向对象(OOP),其中继承(inheritance)也是比较重要的一部分,继承之后子类可以调用父类当中的公开成员及函数。 1234567891011121314151617181920212223class Person { constructor(name) { this.name = name; } walk() { console.log(\"walk\"); }}// 使用 extends 关键字继承 Person 类class Teacher extends Person { constructor(name, degree) { // super 关键字调用父类的构造器,传入参数name,如果不写super就会报错 super(name); this.degree = degree; } teach() { console.log(\"teach\"); }}const teacher = new Teacher(\"React\", \"MSC\"); Ⅷ. Modules (模块)可以看到我们之前写的代码有些乱,因为我们在一个文件中定义了多个类,如果能把代码分割为若干个独立文件就好了,这就是我们说的模块化,将代码写在多个文件里,我们把这些叫做模块,在之前 JavaScript 中没有原生模块的概念,所以就诞生了很多第三方库,但是在 ES6 中已经有原生模块了。我们把这段代码进行模块化。 123456789101112# person.js 文件// 模块默认是私有的,也就是说这个类除了这个模块外都是不可见的,为了让其他的模块也可以使用,我们可以把它公开化// 实现公开化的方法就是导出,在class关键字前加入 export 关键字就可实现导出,然后就可以在需要的地方导入就可以了expxort class Person { constructor(name) { this.name = name; } walk() { console.log(\"walk\"); }} 123456789101112131415# teacher.js 文件// 我们可以使用 import from 将想要导入的模块加载进来import { Person } from './person';export class Teacher extends Person { constructor(name, degree) { super(name); this.degree = degree; } teach() { console.log(\"teach\"); }} 123456# index.js 文件import { Teacher } from './teacher';const teacher = new Teacher(\"React\", \"MSC\");teacher.teach(); 可以看到我们就将之前的代码拆分了到了三个文件里,然后在每个模块中使用 export 和 import 来进行模块的导出和导入的操作,也就实现了我们说的模块化。 我们说在模块里定义的对象默认是私有的,如果不导出,外部是访问不到的,我们一般在模块中导出的不止仅仅只有一个对象,我们需要在每个需要导出的对象前面加入 export 这种方式我们称之为 以名导出(Named Exports) 因为所有要导出的对象都有名字,不同于以名导出还有一种 默认导出(Default of Exports) 的方式,通常只有一个对象需要导出的时候我们就可以使用默认导出。 123456789101112131415# teacher.js 文件import { Person } from './person';// 在 export 关键字的后面加入 default 关键字 声明这个类就是这个模块的默认导出对象export default class Teacher extends Person { constructor(name, degree) { super(name); this.degree = degree; } teach() { console.log(\"teach\"); }} 1234567# index.js 文件// 在引入 Teacher 类的时候,因为这个类是默认对象所以不需要写大括号import Teacher from './teacher';const teacher = new Teacher(\"React\", \"MSC\");teacher.teach(); 你可能会说,类不是对象。其实在 JavaScript 中类就是对象,因为类只是为了完成概念的函数的外皮,函数,我之前说过就是对象,所以在 JavaScript 中类技术上就是对象。 我们可以在一个模块中既使用默认导出又使用以名导出的方式。 123456789101112131415161718# teacher.js 文件import { Person } from './person';// 以名导出export function promote(){}// 默认导出export default class Teacher extends Person { constructor(name, degree) { super(name); this.degree = degree; } teach() { console.log(\"teach\"); }} 1234567# index.js 文件// 在引入的时候,不写括号来接收默认导出的对象,使用大括号接收以名导出的对象import Teacher, { promote } from './teacher';const teacher = new Teacher(\"React\", \"MSC\");teacher.teach(); 五、第一个 React 组件我们在终端输入 create-react-app 命令,来创建 react 项目,项目名称叫做 counter-app,当然名称大家可以自定义。 1create-react-app counter-app 工程建立好之后,进入 counter-app 文件夹,启动项目。 12345# 进入 counter-app 文件夹cd .\\counter-app\\# 启动项目npm start 我们先安装 bootstrap ,这个库是 Twitter 公司开发的 前端开源工具包,这个库是基于 HTML/CSS/JS 开发的响应式布局框架。 1npm i bootstrap 我们回到 src 文件夹中的 index.js 文件中导入 bootstrap 框架。 123456789101112131415import React from \"react\";import ReactDOM from \"react-dom\";import \"./index.css\";import App from \"./App\";import * as serviceWorker from \"./serviceWorker\";// 引入bootstrap样式import \"bootstrap/dist/css/bootstrap.min.css\";ReactDOM.render(<App />, document.getElementById(\"root\"));// If you want your app to work offline and load faster, you can change// unregister() to register() below. Note this comes with some pitfalls.// Learn more about service workers: https://bit.ly/CRA-PWAserviceWorker.unregister(); 回到浏览器我们可以看到页面的字体已经改变了。 我们现在要创建组件,为了方便管理我们的组件我们把所有的组件都放在 src 文件夹下新创建的 components 文件夹中,然后在组件文件夹中创建一个新的文件 counter.jsx 命名时我们推荐使用(小)驼峰命名法,即第一个单词的首字母小写,其余单词的首字母大写。创建文件的后缀名我们也推荐使用 .jsx 而不是 .js 为了代码的完整性。 jsx 全称 JavaScript XML,一种在 JavaScript 中构建标签的类似 XML 语法。增强 React 程序组件的可读性 我们在 Visual Studio Code 中安装的 Simple React Snippets 扩展,输入一些缩写就可以帮我们快速输入代码。 123456789101112131415# counter.jsximport React, { Component } from \"react\";// Counter 类 继承自 React的 Component 类class Counter extends Component { render() { // jsx 语法 // 它会让编译器调用 React.createElement 的方法 return <h1>Hello World</h1>; }}// 我们也可以这样来实现默认导出类export default Counter; 我们回到 index.js 文件中,导入我们创建的 Counter 组件 123456// 这个类是默认导出的所以不需要写大括号import Counter from \"./components/counter\";// 将原来使用的 <App /> 标签修改为 我们创建的 <Counter /> 组件//ReactDOM.render(<App />, document.getElementById(\"root\"));ReactDOM.render(<Counter />, document.getElementById(\"root\")); 回到浏览器,就能看到 Counter 组件渲染的结果了。 我们在组件中写 jsx 的语法的时候只能有一个顶级元素,jsx 语法会调用 React.createElement()方法来创建组件,如果有多个顶级元素 babel 不知道如何转译并告知 React 要创建什么元素,所以就会报错。 123456789101112# counter.jsximport React, { Component } from \"react\";class Counter extends Component { render() { // jsx语法报错,只能有一个顶级元素 return <h1>Hello</h1><h1>World</h1>; }}export default Counter; 解决方案就是使用最外层元素用一个 div 标签宝珠它们,保证只有一个顶级元素即可。 123456789101112131415161718# counter.jsximport React, { Component } from \"react\";class Counter extends Component { render() { // 将所有的元素用div标签包含进来 // 当有多行标签的时候,需要在外边用小括号括起来 return ( <div> <h1>Hello</h1> <h1>World</h1> </div> ); }}export default Counter; 如果我们不需要最外层的无意义 div 标签的话可以使用 React.Fragment 组件来替换 div 标签,这样在渲染元素的时候在根组件下面就会只渲染 React.Fragment 组件里面的内容了。 12345678910111213141516# counter.jsximport React, { Component } from \"react\";class Counter extends Component { render() { return ( <React.Fragment> <h1>Hello</h1> <h1>World</h1> </React.Fragment> ); }}export default Counter; 我们可以使用 state 对象来进行组件的动态数据传入,使用大括号可以存放可计算的 js 表达式。 12345678910111213141516171819202122import React, { Component } from \"react\";class Counter extends Component { // state是react组件中一个特殊的属性,实际它包含了这个组件需要用到的数据 state = { count: 0 }; render() { return ( <div> {/* this 指向当前类对象,在大括号也可以调用方法 */} <span>{formatCount()}</span> </div> ); } formatCount() { const { count } = this.state; return count === 0 ? <h1>Zero</h1> : count; }}export default Counter; 比如我们可以添加一个图片,动态的设置 src 属性,我们用 {} 大括号,将需要动态设置的值或属性或方法传入(注意:在为图片设置路径时 大括号 外不需要写 双引号),即可动态生成代码。 123456789101112131415import React, { Component } from \"react\";class Counter extends Component { state = { imageUrl: \"https://picsum.photos/600\" }; render() { return ( <div> <img src={this.state.imageUrl} alt=\"\" /> </div> ); }}export default Counter; 如果我们需要设置样式,我们可以使用 className 属性 而不是 class 属性,因为这个标签是通过 js 生成的,在 js 中 class 是保留关键字,我们不能去使用它,所以在标签属性中 class 对应的名称就是 className。 1234567891011121314151617181920212223import React, { Component } from \"react\";class Counter extends Component { state = { count: 0 }; render() { return ( <div> {/* 为 span 元素设置 bootstrap 样式 */} <span className=\"badge badge-primary m-2\">{this.formatCount()}</span> {/* 添加button按钮并设置样式 */} <button className=\"btn btn-primary btn-sm\">Increment</button> </div> ); } formatCount() { const { count } = this.state; return count === 0 ? <h1>Zero</h1> : count; }}export default Counter; 我们也可以自己设置一些自定义的样式使用 style 关键字,然后通过大括号来进行动态设置。 12345678910111213141516171819202122232425262728293031import React, { Component } from \"react\";class Counter extends Component { state = { count: 0 }; // 定义自定义样式 styles = { fontSize: \"50px\", fontWeight: \"bolder\" }; render() { return ( <div> {/* 使用 style 动态设置样式 */} <span style={this.styles} className=\"badge badge-primary m-2\"> {this.formatCount()} </span> <button className=\"btn btn-primary btn-sm\">Increment</button> </div> ); } formatCount() { const { count } = this.state; return count === 0 ? <h1>Zero</h1> : count; }}export default Counter; 我们还可以设置行内样式,只要在 style 中的大括号里再写一个大括号然后里面设置样式属性即可。 12345678910111213141516171819202122232425import React, { Component } from \"react\";class Counter extends Component { state = { count: 0 }; render() { return ( <div> {/* 在style的大括号中再写一个大括号然后里面写样式属性即可 */} <span style={{ fontSize: 30 }} className=\"badge badge-primary m-2\"> {this.formatCount()} </span> <button className=\"btn btn-primary btn-sm\">Increment</button> </div> ); } formatCount() { const { count } = this.state; return count === 0 ? <h1>Zero</h1> : count; }}export default Counter; 我们在开发中很常见的就是列表数据,下面我们来看一下如何渲染列表数据。 12345678910111213141516171819202122import React, { Component } from \"react\";class Counter extends Component { state = { // 定义渲染的列表数据 tags: [\"Tag1\", \"Tag2\", \"Tag3\", \"Tag4\"] }; render() { return ( <React.Fragment> <ul> {/* 我们使用数组的 map 方法遍历数组,渲染元素并设置值 */} {this.state.tags.map(tag => ( <li>{tag}</li> ))} </ul> </React.Fragment> ); }}export default Counter; 我们先定义一个数组数据,然后在渲染元素的时候遍历这个数组并返回拼接后的元素,这样写虽然页面上看上去是正常的,但在控制台中可以看到报错,Warning: Each child in a list should have a unique "key" prop. 每个数组的迭代项都要有一个唯一的 key,因为它要区分每个列表项,在 react 的虚拟 DOM 改变的时候 react 可以马上反应过来是什么组件的状态改变了并同步组件,但如果不设置 key 的话 react 是无法知道是哪个组件改变的。我们只需要在渲染 li 的元素时设置 key 属性即可。 1234567<ul> {this.state.tags.map(tag => ( {/* 设置key属性,控制台中将不会再报错 */} {/* 注意:这里的key在列表中必须是唯一的,但不必在整个程序中唯一,只在当前列表中唯一即可 */} <li key={tag}>{tag}</li> ))}</ul> 我们如果想要实现判断的逻辑,比如渲染列表的时候如果数组中没有数据则返回一条信息 没有任何标签!,否则将渲染列表,因为在 jsx 语法中是没有逻辑判断的因为它不是一个模板引擎,为了渲染我们可以将判断的逻辑放在一个方法中。 12345678910111213141516171819202122232425262728293031323334import React, { Component } from \"react\";class Counter extends Component { state = { // 空的列表数据 tags: [] //[(\"Tag1\", \"Tag2\", \"Tag3\", \"Tag4\")] }; renderTags() { // 如果标签没有数据则返回信息 if (this.state.tags.length === 0) return <p>没有任何标签!</p>; // 否则渲染列表 return ( <ul> {this.state.tags.map(tag => ( <li key={tag}>{tag}</li> ))} </ul> ); } render() { return ( <React.Fragment> {/* 我们可以使用这种方式判断数组中如果没有数据,则返回 请先输入标签! 信息 */} {this.state.tags.length === 0 && \"请先创建标签!\"} {/* 调用渲染标签方法 */} {this.renderTags()} </React.Fragment> ); }}export default Counter; 所有的 react 元素都是基于 dom 元素的属性,例如 按钮元素有一个 onClick 属性用来处理点击事件,同时还有双击事件啊鼠标移入移出事件等。 1234567891011121314151617181920212223import React, { Component } from \"react\";class Counter extends Component { state = {}; // 处理点击事件 handleClick() { console.log(\"Increment 按钮被点击了。\"); } render() { return ( <React.Fragment> {/* 注意这里的调用的函数并没有使用括号 this.handleClick(), 如果使用括号会直接调用这个函数,我们只放置这个函数的引用即可 */} <button onClick={this.handleClick} className=\"btn btn-primary\"> Increment </button> </React.Fragment> ); }}export default Counter; 现在我们想实现 点击按钮后增加数字就增加 1,但是在写的时候发现 handleClick 方法中如法调用 this.state.count,这是因为 this 总是返回那个对象引用,但是如果函数被以独立函数的方法调用,this 将会返回 window 对象引用,但如果开启了 严格模式,则会返回 undefined 未定义,这就是为什么我们无法去调用这个属性。 1234567891011121314151617181920212223242526272829import React, { Component } from \"react\";class Counter extends Component { state = { count: 0 }; handleClick() { // 调用 this.state.count 将会报错 console.log(\"Increment 按钮被点击了。\" + this.state.count); } render() { return ( <div> <span className=\"badge badge-primary m-2\">{this.formatCount()}</span> <button onClick={this.handleClick} className=\"btn btn-primary btn-sm\"> Increment </button> </div> ); } formatCount() { const { count } = this.state; return count === 0 ? <h1>Zero</h1> : count; }}export default Counter; 我们之前说过可以使用绑定的方式去解决这个问题,我们在 这个类中的 constructor 构造器中绑定 this。 123456789101112131415161718192021222324252627282930313233import React, { Component } from \"react\";class Counter extends Component { state = { count: 0 }; constructor() { super(); this.handleClick = this.handleClick.bind(this); } handleClick() { console.log(\"Increment 按钮被点击了。\" + this.state.count); } render() { return ( <div> <span className=\"badge badge-primary m-2\">{this.formatCount()}</span> <button onClick={this.handleClick} className=\"btn btn-primary btn-sm\"> Increment </button> </div> ); } formatCount() { const { count } = this.state; return count === 0 ? <h1>Zero</h1> : count; }}export default Counter; 其实还有另外一种方式,在 ES6 语法之后不需要使用构造器,只需要将方法改为箭头函数即可,因为箭头函数不会重新绑定 this。 123handleClick = () => { console.log(\"Increment 按钮被点击了。\" + this.state.count);}; 现在我们要处理事件进行数量的累加,但是我们发现直接更新状态是无效的。 123handleClick = () => { this.state.count++;}; 实际上 state 中的 count 确实已经增加了,但是 react 是不知道的,所以视图并没有被更新,为了解决这个问题,我们需要用到继承自 Component 类的一个方法,setState 方法 告诉 react state 已经更新了,然后让 react 去同步视图,然后浏览器中的 DOM 会根据虚拟 DOM 进行修改,这于 Angular 是不同的,Angular 不需要这样做它也会自动检测并修改,因为 Angular 里所有的浏览器 DOM 都有监听,当点击按钮或输入文字,Angular 会立即意识到修改并更新视图。而在 react 中我们必须告诉它什么东西改变了。 12345handleClick = () => { this.setState({ count: (this.state.count += 1) });}; 我们在使用事件时肯定会进行参数的传递,我们可以使用这种箭头函数来处理传参问题。 12345678910render() { return ( <div> <span className=\"badge badge-primary m-2\">{this.formatCount()}</span> <button onClick={()=> this.handleClick(params) } className=\"btn btn-primary btn-sm\"> Increment </button> </div> );} 六、React 组合组件七、分页,过滤,排序八、12","link":"/2019/10/05/react-zero-to-one/"}],"tags":[{"name":"人生","slug":"人生","link":"/tags/人生/"},{"name":"杂谈","slug":"杂谈","link":"/tags/杂谈/"},{"name":"React.js","slug":"React-js","link":"/tags/React-js/"},{"name":"文章收藏","slug":"文章收藏","link":"/tags/文章收藏/"},{"name":"网站推荐","slug":"网站推荐","link":"/tags/网站推荐/"},{"name":"英语","slug":"英语","link":"/tags/英语/"},{"name":"Node.js","slug":"Node-js","link":"/tags/Node-js/"},{"name":"语录收藏","slug":"语录收藏","link":"/tags/语录收藏/"},{"name":"左撇子人生","slug":"左撇子人生","link":"/tags/左撇子人生/"},{"name":".NET","slug":"NET","link":"/tags/NET/"},{"name":"小知识","slug":"小知识","link":"/tags/小知识/"},{"name":".NET Core","slug":"NET-Core","link":"/tags/NET-Core/"},{"name":"ASP.NET Core","slug":"ASP-NET-Core","link":"/tags/ASP-NET-Core/"},{"name":"Blazor","slug":"Blazor","link":"/tags/Blazor/"},{"name":"gRPC","slug":"gRPC","link":"/tags/gRPC/"},{"name":"DDD","slug":"DDD","link":"/tags/DDD/"},{"name":"CQRS","slug":"CQRS","link":"/tags/CQRS/"},{"name":"TDD","slug":"TDD","link":"/tags/TDD/"},{"name":"Angular.js","slug":"Angular-js","link":"/tags/Angular-js/"},{"name":"Vue.js","slug":"Vue-js","link":"/tags/Vue-js/"}],"categories":[{"name":"自述","slug":"自述","link":"/categories/自述/"},{"name":"前端","slug":"前端","link":"/categories/前端/"},{"name":"收藏","slug":"收藏","link":"/categories/收藏/"},{"name":"推荐","slug":"推荐","link":"/categories/推荐/"},{"name":"英语","slug":"英语","link":"/categories/英语/"},{"name":"人生回顾","slug":"人生回顾","link":"/categories/人生回顾/"},{"name":"技术分享","slug":"技术分享","link":"/categories/技术分享/"},{"name":"编程","slug":"编程","link":"/categories/编程/"},{"name":"开源项目","slug":"开源项目","link":"/categories/开源项目/"},{"name":"自述","slug":"人生回顾/自述","link":"/categories/人生回顾/自述/"}]}