Eclipse创建Maven管理的Web项目

动态的web项目 部署映射项目名 右键 ,properties的Deployment Assembly,这是部署的映射,jar包在lib目录里、src映射到 WEB-INF/classes中、/WebContent映射到 / 根目录中。 --------------------------------------------------------------------------------------------普通 web项目 直接创建一个新的 Dynamic Web ProjectTarget runtime:服务器的运行环境Dynamic Web module version :动态web模块的版本,即JavaEE里的servlet版本下一步 src 编写完后默认的输出到的文件中(即buildclasses)下一步 根目录、web资源目录、及可选项是否创建.xml文件,也可以创建项目后,项目右键 在JavaEE Tools 中也可以生成.xml文件完成在Eclipse的Window的Show View中的Navigator可以显示隐藏文件,如.classpath和.projet文件,build下的classes文件夹等(其中MFTA-INF是自动生成的东西,可以删除) --------------------------------------------------------------------------------------------maven web项目(注意网络) 注意每一步更新项目,项目右键Maven,Update Project,快捷键Alt+F5① 创建一个简单的Maven项目,再配置成web项目创建一个新的Maven Project,选择Create a simple project,创建一个简单的项目next Group Id:一般公司域名的反写,Artifact:项目名finnish后续步骤: 步骤1:这时候创建的项目,其JDK的1.5,可以在 pom.xml 文件中进行配置在properties标签内配置在maven官网的Maven Plugins中的compiler(插件)里,Example中 有个-source -target里有样例 步骤2:修改打包方式 pom.xml文件中 步骤3:在自动生成的src/main/webapp下手动创建WEB-INF文件夹WEB-INF下新建web.xml文件注意:maven的web项目的WEB-INF下不用新建classes和lib目录 步骤4:添加服务器运行环境(主要是servlet-api.jar)第一种,项目名右键的构建路径里的Add Library 中的server runtime直接引入本地的服务器第二种,在pom文件中使用依赖引入servlet相关jar包dependencies标签来管理依赖(provided是指这个jar包由本地服务器提供,打包时不会打包,默认是compile) 步骤5:注意映射properties的Deployment Assembly,这里不用修改了 ② 使用Eclipse中自带的Maven的web骨架,来创建项目,再进行配置修改 创建maven项目,不选择创建简单项目,进入选择骨架的页面,选择maven-archetype-webapp,下一步,填写组ID和项目名,完成。默认jdk是1.5,默认版本是2.3 后续步骤: 步骤1:pom中修改jdk版本,与上同理,在自动src/main/webapp/WEB-INF下生成的index.jsp删掉 步骤2:发现src目录显示不全,在构建路径里的Configure Build Path里的Order and Export下,选中缺失的,Down下移就会显示出来 步骤3:修改WEB-INF下的web.xml文件中的容器版本,可以直接复制之前普通web项目的该文件 步骤4:properties中的Project Facets的动态页面模型的版本是2.3,这里不能改,因为这个配置是骨架写在配置文件中的,所以在Navigator的视图下,查看隐藏的.settings文件夹下的project.facet.core.xml中facet="jst..web" 对应行的version改为3.1 ...

October 9, 2019 · 1 min · jiezi

tapable学习笔记之AsyncParallelHook

tapable学习笔记之AsyncParallelHook 从tapable生产的脚本来看AsyncParallelHook 先来看 tap + callAsync const { AsyncParallelHook} = require("tapable");let queue1 = new AsyncParallelHook(['name']);console.time('cost');queue1.tap('1', function (name) { console.log(name, 1);});queue1.tap('2', function (name) { console.log(name, 2);});queue1.callAsync('webpack', err => { console.timeEnd('cost');});// 执行结果webpack 1webpack cost: 4.520ms看生产的源码就知道:这种模式属于顺序执行,一个报错立即回调: "use strict";var _context;var _x = this._x;do { var _counter = 2; var _done = () => { _callback(); }; if (_counter <= 0) break; var _fn0 = _x[0]; var _hasError0 = false; try { _fn0(________name); } catch (_err) { _hasError0 = true; if (_counter > 0) { _callback(_err); _counter = 0; } } if (!_hasError0) { if (--_counter === 0) _done(); } if (_counter <= 0) break; var _fn1 = _x[1]; var _hasError1 = false; try { _fn1(________name); } catch (_err) { _hasError1 = true; if (_counter > 0) { _callback(_err); _counter = 0; } } if (!_hasError1) { if (--_counter === 0) _done(); }} while (false);接着看 tapAsync + callAsync ...

October 1, 2019 · 4 min · jiezi

译Web-开发新手应该知晓的20件事

注:本文采用意译而不是直译 在我当初刚从事 web 开发的时候,有很多重要的事我并没有事先了解。现在看来,我的很多期望都和现实有很大的差距。在这篇文章里,我会告诉你 20 件事情,这些都是在你准备开始或者刚开始 web 开发不久的时候就应该知晓的。这有助于你更好地正视自己的期望,同时少走一些弯路。 1.编程和学位无关编程可以随时开始,并不需要学位。在互联网上你可以找到很多资源,尤其是一些基础知识。你完全可以自学编程,同时求助于互联网。 2.谷歌是一项技能刚开始学习 web 开发的你并没有足够的知识可以解决当前遇到的问题,这很正常,但并不意味着你不能解决这些问题。学会谷歌是一项重要的技能,可以帮助你节省大量时间。 3.不要什么都学有太多东西要学了。单看 JavaScript 的流行框架,就有:React,Vue 和 Angular。你没办法学习所有的框架,并且也没有这个必要。你只需要专注于你喜欢的或者公司目前使用的框架即可。 4.最难的事是写简单的代码很多没有经验的开发者会写一些花里胡哨的代码,以此向其他开发者炫耀自己的编码能力。请务必不要这么做,你应该尽可能地保持代码简洁。 5.没有时间做测试根据我的经验,开发者对测试工作比较懈怠。大部分的开发者都觉得测试环节不是很有趣。毕竟,你是来制造东西的,不是来破坏东西的。大部分时候你都会和截止日期做斗争。一旦意识到所剩时间不多,第一件应该放弃的事就是测试。尽管大家都知道这可能会导致不好的结果,但的确是这样做的。 6.时间预估总是不准确的软件开发中的时间预估通常都是不准确的。对于一个小小的功能,你会觉得一小时内就能搞定,但是当你查看代码后,你会发现:要想实现这个小功能,必须重构大量代码。这样看来,一小时绝对是搞不定了。 7.回看旧代码是一件尴尬的事刚开始从事开发的时候,你想做的只是修复一些东西。只要代码可以运行就够了。对于那些缺乏经验的开发者来说,可以运行的软件和运行良好的软件没有差别。但随着你成长为一名真正意义上的开发者,你将不得不面对以前写的旧代码,然后心想:“这些面条式代码真的是我之前写的吗?”好吧,在这种情况下你只需要做一件事:好好嘲笑一番,然后重构一下这些乱糟糟的代码。 8.你将花费大量时间查看 bug调试代码是你的一个主要工作。写出完全正确的代码是不可能的,尤其是在你经验尚缺的时候。缺乏经验的开发者之所以需要花大量时间去调试,是因为他们不知道应该查看哪里,有时候他们甚至不知道应该找什么。最糟糕的是,大部分时候,这些 bug 是你自己造成的。 9.IE 是最差的浏览器Internet Explorer,又名 Internet Exploder,将会让你由衷厌恶自己书写 的 CSS。即使是最基本的样式,在 IE 浏览器中也会崩掉。很多时候,你都会疑惑为什么会存在这么多不同的浏览器。对于浏览器的兼容问题,大多数公司采取的做法是只支持 IE11 或者更新的版本。 10.服务器一关,你就可以休息了忙活了一天后,开发服务器或者版本控制服务器最后会关闭。如果你不是在本地写的项目,那你就没办法继续手头的工作了。这时候还是喝杯咖啡休息片刻吧。 11.你会假装自己理解了同事所说的东西同事可能会和你聊聊最新的技术或者工具,虽然你频频点头以表赞同,但事实上你并不理解他说的那些东西。 12.你不需要记住所有的东西编程就是知识的应用,没有必要记住所有的东西。你随时可以在网上找到资源,只需要知道去哪里找就可以了。做项目、积累经验,这些东西自然就记住了。 13.成为一名成功的问题终结者最重要的是,你要有足够的创造力。编程说白了就是解决问题,同样的问题可以用多种方式解决。创造力有助于你更轻松有效地解决问题。 14.多阅读你的大部分时间将花费在阅读上。从各种途径阅读并了解新的技术、最佳实践、工具等,时刻关注业界最新动态。当然,也不要忘了多看书。总而言之,阅读可以扩大你的知识面,做到与时俱进。 15.响应式布局很烦人尝试让你的网站适配所有的设备是一件很烦人的事。设备和浏览器的种类数不胜数,总会有某一台设备的某一个浏览器无法正常显示你的网站。 16.学会调试可以节省大量时间调试很耗时间,尤其是在你不知道应该查看哪里、查看什么的时候。了解代码运行机制并掌握调试工具的使用,可以帮你省下大量时间。如果你希望提高自己的调试能力,你可以了解开发者工具在浏览器中是如何工作的。 17.你得到的回答不一定能解决你的问题当遇到一个无法解决的难题时,你可能会求助于谷歌。大部分时候,你会在诸如 StackOverflow 这样的论坛得到有建设性的解答。但更多时候,只靠复制粘贴这个回答并不能解决你的问题。这时候,你的问题解决能力和创造力就显得尤为重要了。 18.好的 IDE 可以让你的代码生活更加愉快在开始编程之前,你应该花时间找一款不错的 IDE。免费的也好,收费的也好,好的 IDE 将会让你的代码生活更加愉快。IDE 可以做到语法高亮,也可以展示语法错误。大部分 IDE 还提供了各种定制插件。 19.使用终端可以提高效率如果你之前习惯通过用户图形界面(GUI)工作,那么在刚转为使用终端的时候可能会觉得不知所云。终端是一个强大的工具,可以比图形工具更快地完成任务。你应该学会熟练地使用它。 20.不要重复造轮子当你想要创建一些功能特性的时候,你应该先查看一下 GitHub 上是否已有类似的轮子存在。针对你想要创造的这个功能,可能早就有流行且稳定的库了。多看看一些文档友好且更新活跃的项目。如果你想要新增或者重写项目的功能,你可以选择创建 PR 或者 fork 项目。

September 30, 2019 · 1 min · jiezi

目前流行前端几大UI框架

在前端项目开发过程中,总是会引入一些UI框架,已为方便自己的使用,很多大公司都有自己的一套UI框架,下面就是最近经常使用并且很流行的UI框架。 一.Mint UI Mint UI是 饿了么团队开发基于vue .js的移动端UI框架,它包含丰富的 CSS 和 JS 组件,能够满足日常的移动端开发需要。官网:https://mint-ui.github.io/#!/...Github: https://github.com/ElemeFE/mi... 二.WeUIWeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含button、cell、dialog、toast、article、icon等各式元素。 官网地址:https://weui.io/Github: https://github.com/weui/weui.git 三.cube-ui cube-ui 是滴滴团队开发的基于 Vue.js 实现的精致移动端组件库。支持按需引入和后编译,轻量灵活;扩展性强,可以方便地基于现有组件实现二次开发。 官网地址:https://didi.github.io/cube-u...Github: https://github.com/didi/cube-ui/ 四.iView UI iview ui是一个强大的ui库基于vue,有很多实用的基础组件比elementui的组件更丰富,主要服务于 PC 界面的中后台产品。使用单文件的 Vue 组件化开发模式 基于 npm + webpack + babel 开发,支持 ES2015 高质量、功能丰富 友好的 API ,自由灵活地使用空间。 官网地址:https://www.iviewui.comGithub: https://github.com/TalkingDat... 五.layui layui是一款采用自身模块规范编写的前端 UI 框架,遵循原生 HTML/CSS/JS 的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到 API 的每一处细节都经过精心雕琢,非常适合界面的快速开发。官网地址:https://www.layui.comGithub: https://github.com/sentsin/la... 六. ElementUI Element是饿了么前端开源维护的Vue UI组件库,组件齐全,基本涵盖后台所需的所有组件,文档讲解详细,例子也很丰富。 主要用于开发PC端的页面,是一个质量比较高的Vue UI组件库。官网地址:http://element-cn.eleme.io/#/...Github: https://github.com/ElementUI/... ...

September 20, 2019 · 1 min · jiezi

学习WEB前端开发是选择自学还是去培训机构

WEB前端开发是目前最火的行业之一,竞争很大,工资很高,未来发展也极好。 我做WEB开发有很长一段时间,现在经常看见大家在留言问我想转行学习WEB前端但是不知道是选择自学,还是选择培训。 今天根据我对于这行业的了解,给大家详细对比一下这两个选择的优势和劣势。 如果我解释的大家还是不懂可以直接私聊我。 希望可以帮助零基础入门的你少走弯路。 个人经历: 我学前端并不是培训的,因为我那个时候学开发还是比较早的,WEB这块我做了挺多年,在后端JAVA开发也做过挺久,现在是做项目经理。我最开始也是从大一开始自学的,毕竟大学里学的还是太皮毛了,那个时候对做网页比较有兴趣,所以我还是比较相信兴趣是最好的老师这句话的。 那个时候在网上找了一些视频教程学着也是非常痛苦,自己也看了一些书,像《javascript权威指南》(犀牛书)这些书,但是作用都不是很大。所以我在这里不建议大家初学入门前端去看书,还是建议当你学完了一部分基础之后,你再看书温习,效果会更好,这样的效果是我实践出来的,仅代表个人建议。直接完全看书学的后果可能是看了觉得能看懂,但是自己动起手来,啥也写不出来。 关于教程视频,不在于多,越多越是不看的,这也是我个人总结出来的,我还是很少见到自己完全视频教程学习能自学会前端开发的,尤其现在框架越来越难,还不断更新,光看视频还是比较难学好前端的,很多代码上的东西如果没有人告诉你,可能你连空行都不会,代码写的一团糟,最基本的达不到,更不要说找到工作了。 我之前在上市公司做了5年开发,现在做一个外包公司的项目经理。 我个人建议: 对于培训还是自学,如果你条件还可以,负担不是那么大,能培训还是培训的,培训一定会比你自学的好,因为培训有老师带你,你肯定会进步快,学习编程最好还是有领路人,没有那么多天才能自学成功,如果培训都很难学好,那么自学一定学不好。不过目前的培训费用都在18000以上,这还只是培训费而已,加上一些其他的东西,四个月时间要小三万吧!建议可以选择线上辅导学习,这样性价比会更高一些。 如果条件不允许,也可以选择自学,自学挺难的,但是如果你韧性强,自学也可以找到工作,但是你需要有一个指导你的人,从比例来看会很低,其实无论在哪里学习都是一样的,重在内心怎么看待你学习前端这个事情。 如果你选择培训建议如下: 如今前端市场竞争很大,各个企业争先恐后的在重金抓有能力的人,也就是我们常说的项目经验,我们这个行业别的要求不高,就是做项目的能力,到了公司是否可以干活,能干活完成任务怎么样都可以。但是目前培训的市场学费都在18000+以上,加上四个月的吃住行,至少也要30000块钱,这笔费用挺多的。最重要的还是时间问题,我学完编程就学了2年,怎么可能用四个月时间学的很好,除非很聪明的人,我个人觉得目前学前端想要找工作,至少要学习个半年的时间,身边也有不少在培训的时候发生的一些事情,当然有好的结果,也有很多不好的结果,不做评价,如果想去培训的朋友,建议慎重选择,想清楚是不是可以很努力去学,毕竟师傅领进门修行还是靠个人。 如果你选择自学建议如下: 1.了解如今的市场行情,就是企业需要什么样的人才,了解清楚企业技术需求,这点很重要。 2.有一套系统的学习方案,学习不是瞎学的,是有节奏感的,每天看多少视频,不是说看完了就完了,在很多人的实践中,看完视频一点用没有用。一天用多少时间学前端,自己规划好,每天做什么案例都是非常重要的。 合理的学习路线,我这里也给大家一些建议: 基础学习:网页布局基础:HTML+CSS+DIV盒子模型+前端开发基础:javascript语法基础+javascript面试对象+DOM操作+javascript插件学习+javascript类库(jquery为例)+AJAX+代码性能优化 进阶学习:H5标签+CSS3动画+2D/3D转换应用+Canvas+SVG+本地存储(WEBsql)+移动端WEBAPP开发+移动端框架学习 框架学习:Vue+Angular+React+Bootstrap+node 最好可以有人指路: 有人指路,很多问题是我们刚刚开始自己不可能解决的,除非那个是天才,可能一个小问题,我们一天都想不明白,但是别人的一句话可能就懂了,这个就是经验。 这些就是我做前端那么多年,给想学习前端小伙伴们的一些建议,希望可以帮助大家。 如果对于WEB前端开发这个行业的其他相关知识还是不是特别了解,可以随时来咨询我,学习路线,学习建议,学习方法都可以随时私聊来问我,希望可以帮助大家少走弯路。 需要相关前端学习资料私我呀,私我回复:“前端资料”,即可获取相关学习资料哦 。 原作者:WEB开发李家靖原文链接:https://www.jianshu.com/p/f42...

September 19, 2019 · 1 min · jiezi

可以免费自学编程的12个网站

很多人包括一些企业家,和市场营销人员都认为学习编程对一个人走向成功十分有帮助。在过去的一年里,我一直在学习编程。它有助我成为一个更好的创业者,我甚至可以提供一些帮助,当我的团队需要解决一些bug的时候。 现在,如果你想学编程的话,这12个网站可以帮助你,它们都提供了免费的课程供你学习,因此你无需担心费用的问题。 1.Codecademy 其中,Codecademy是最受欢迎的免费编程学习网站之一。事实上,已经有超过2400万人通过这家公司的教育模式学会了编程。Codecademy开设的课程有HTML&CSS,JavaScript,jQuery的,PHP,Python和Ruby。 2.Coursera Coursera成立于2012年,如今已经成长为一个主要以营利为目的的技术教育公司,现提供来自119家机构的超过1000门课程。如果你想要获得证书,可能需要为一定的课程付费,这里也有一些来自不同大学的免费编程课程,如华盛顿大学,斯坦福大学,多伦多大学和范德比尔特大学等。 3.edX edX是另一个领先的在线学习平台,重点是它不是以营利为目的,而是开源的。edX是由美国哈佛大学和麻省理工学院于2012年联合创办的,所以你将会在这里学习到先进的技术和理论。如今,edX已涵盖了60所学校。此外在这里,你应该不会错过哈佛大学的计算机科学导论的,免费的哦。 4.Udemy Udemy成立于2010年,是一个在线学习平台,可以帮助你改善或学习工作技能。虽然有部分课程需要付费,但也有大量的免费编程学习课程,通过视频讲授。 5.aGupieWare 一个独立APP开发者从美国的一些领先机构调查了计算机科学程序,然后基于斯坦福大学,麻省理工学院,卡耐基梅隆大学伯克利分校和哥伦比亚大学提供的免费课程,它创建了一个类似的课程。该程序分为15个课程:3个入门课程,7门核心课程和5个选修课程。 对有潜力的程序员来说,这简直是一个完美的入门程序。(本段感觉译的不是很好,有其他见解还请赐教~) 6.GitHub 这个我想大多数开发者都知道就不多说了,作为开源代码库以及版本控制系统,Github拥有140多万开发者用户。 7.MIT Open Courseware 如果你已经学过基础知识,并向更深入的层次探索,比如探究背后的编码理论等,麻省理工学院提供了包括如计算机科学导论,Java编程介绍和C语言编程等免费课件的网站,值得好好利用一下。 8.Hack.pledge() 这是一个开发者社区,其中聚集了一些知名度比较高的开发者,如Bram Cohen,BitTorrent的创始人。在那里,你可以从一些大牛那里学习到一些编程技巧。 9.Code Avengers Code Avengers提供了很多有趣的和互动性的程序设计课程,教你如何编写游戏,应用程序,以及如何使用JavaScript,HTML和CSS创建网站。每门课程只需要12个小时即可完成,并且支持英语,俄语,荷兰语,西班牙语,意大利语,土耳其语和葡萄牙语。 10.Khan Academy Khan Academy由教育家萨尔曼汗创建于2006年,是其免费在线学习机构之一。这里提供一步一步的视频教程,你可以在这里学习如何使用JavaScript和ProcessingJS编写动画,游戏等,或者学习如何使用HTML和CSS创建网页。 11.Free Code Camp 在这里,你可以学习HTML5,CSS3,JavaScript,数据库,DevTools,Node.js,Angular.js和Agile的知识。你甚至免费创建自己的应用,以锻炼自己的编程技巧。总之,在这里你可以学习到真正的技能,并且对你解决实际中的问题十分有帮助。 12.HTML5 Rocks 为对抗苹果的HTML 5,谷歌于2010年推出该项目。该网站提供了大量的教程,资源以及最新的HTML5更新。它是开源的,因此开发人员可以尽情使用HTML5代码。由于这比大多数课程都要先进,因此在这里你肯定会比没来之前获得更多的知识和经验。 学习编程常常需要看一些比较昂贵的书籍和课程,但是现在由于互联网的发展,市场上有很多可以免费学习编程的网站,所以费用问题就不用太担心了。 我强烈建议每个企业家都能去学学代码,这对于你成为一个成功的企业家非常有帮助。 在entrepreneur看到这篇文章,也许会对大家有帮助,就拿来翻译一下,需要的朋友可以看一下。有不足之处还请指正,多谢。 写在最最后本文为译文,所以文章里列举的学编程网站均是来自国外,可能对一些英文不是很好的小伙伴来说用起来有点儿吃力,其实国内也有一些编程学习网站,像网易云课堂、慕课网、极客学院等,大家可以参考一下。但是,如果英文不错的话,Aylee还是建议大家多看看国外的网站,毕竟代码都是用英文写的吧是吧哈哈~开个玩笑啦~~总之,希望本文对大家会有所帮助吧。 原文:12 Sites That Will Teach You Coding for Free编译作者:Aylee姓Liu

September 11, 2019 · 1 min · jiezi

优秀的前端开发者为什么难找

这是一个合理的问题。对外行而言,前端开发一定看起来易如反掌吧。但如果是这样的话,为什么前端开发者不是多如牛毛呢? 答案很简单:前端开发,就像任何特殊行业或体育比赛一样,比它看起来难得多。它不难,直至你把脚趾放进去,方知水很深。 前端开发比它看起来要复杂 且不说服务器端的开发有多难,要知道客户端的工作所遇到的问题仍然很重要。作为一名前端开发者,大部分的工作必须用HTML和CSS完成,这使得解决问题可用的工具十分有限。 JavaScript到处都有用,但是功能检测意味着性能开销。它意味着更多的代码,以及更多代码出错的机会。此外,一旦你的网站开始增长,CSS的使用将会很痛苦。它非常静态,于是你最终不得不把代码大量记在脑子里。 陡峭的学习曲线,部分原因是由于该领域不断变化的性质。在前端,总有新东西可学:响应式媒体查询,HTML应用缓存,CSS转换,WebGL等。所有这些工具为前端开发者带来了令人兴奋的新的可能性,但也改变了他们工作完成的方式。 这也意味着前端开发的教学方式在尽可能频繁并准确地改变着。不幸的是,没有哪位教练能够期待与不断变化的技术保持同步,也就是说每一个新进入职场的前端开发者都已经落后了好几个阶段,他们必须努力填补自己的工具箱,而必要资源的供应是永无止境的。 前端开发不仅是开发 前端不仅仅是代码。它跟代码与用户的交互有关。在客户端的开发中,不佳的设计很容易成为产品与受众之间的障碍。一名好的前端开发者需要理解网络性能和决定线上成功的不同评判标准。这意味着HTTP缓存,优化DNS查询时间,极简化构建脚本,并且为项目选择最佳的CSS布局机制。 不仅如此,一名好的前端开发者还需要理解市场。这意味着明白用户的心理,为产品的易用性与可用性区分优先次序,并维持强大的搜索引擎优化。这还意味着对设计理论有一个基本的了解,并偶尔参与几乎不可能实现的平面设计工作。 对于客户端开发而言,注重产品的安全也是很重要的,要防止CSRF,XSS,DNS锁定和点击劫持。但并不是所有的前端开发都是客户端的——即使前端开发者也需要时不时地深入到服务器端代码。这是对的:一个好的前端开发者也需要是一个好的后端开发者。 这意味着产品要兼容一切 开发者彼得-保罗·科赫,道格拉斯·克罗克福德,和尼古拉斯·扎卡斯都同意——“前端开发是世界上最糟糕的开发环境。”原因在于:兼容性。一旦一名前端开发者完成了他的工作(以及一大堆其他的工作),他们得立马回到工作上,确保产品在每一种浏览器、移动设备,甚至阳光下的高端烤箱上功能最佳。这可是一大堆的工作啊。 即兴思考一下,你能想到多少互联网浏览器?仅举几例,前端开发者有Chrome,Firefox,Opera,Safari,IE浏览器,以及现在的Edge要开发——更别提每个浏览器过去的各种版本,都必须考虑兼容性。想想这个:IE浏览器的每个版本,从6到10,都有其自身独特的缺陷与局限。但是,满足每一个浏览器的需求仍然很重要,否则的话许多用户将无法访问你的产品。 而这仅仅是台式机市场。现在考虑一下用户可能用来访问你的服务的每一种型号的智能手机与平板,它们每一个都要求不同的输入方式。这项服务在没有键盘的情况下还能工作吗?没有鼠标呢?它支持触摸屏吗?它有手势吗?这项服务必须在所有情况下都有效——即使是在用户调整输出以满足自己的需求的时候。无论用户是调整文本大小,改换颜色还是完全禁用CSS或JavaScript,产品都必须保持工作。就如同前端开发者布拉特·博赫卡里奥夫所说,“我们在各种能想到的设备所运行的不一致的平台上使用着蹩脚的工具…不管是什么,要确保产品适用于每一个人。” 所以,为什么优秀的前端开发者这么难找?因为要成为一名优秀的前端开发者是很难的——或许比你想象的要难得多。一名优秀的前端开发者必须要有耐心,决心和意愿,去满足传统开发之外的多种不同角色,与此同时要确保产品对于尽可能多的受众而言是最优的——是的,那必定是来之不易的。 原作者: Ilias Ismanalijev 文章来源:Medium 翻译:Joyce Cheng

September 10, 2019 · 1 min · jiezi

odoo-js文件被替换

本打算在 from_controller.js 文件打个断点调试,却发现在chorm的sources下找不到该文件。 去看了web下的template.xml文件,发现form_controller.js正常引用了,并且所有的表单操作也是正常的,说明这个js的方法确实是存在的,只是这些方法去哪了呢? 全局搜索一下关键词。 可以看到在这个模块里, form_controller.js 通过replace被替换掉了。 进入这个文件发现所有的form_controller.js的方法都在这里边 。

September 10, 2019 · 1 min · jiezi

如何实现Web页面录屏

摘要: 很有意思的操作... 原文:web页面录屏实现译者:frontdogFundebug经授权转载,版权归原作者所有。 写在前面的话在看到评论后,突然意识到自己没有提前说明,本文可以说是一篇调研学习文,是我自己感觉可行的一套方案,后续会去读读已经开源的一些类似的代码库,补足自己遗漏的一些细节,所以大家可以当作学习文,生产环境慎用。 录屏重现错误场景如果你的应用有接入到web apm系统中,那么你可能就知道apm系统能帮你捕获到页面发生的未捕获错误,给出错误栈,帮助你定位到BUG。但是,有些时候,当你不知道用户的具体操作时,是没有办法重现这个错误的,这时候,如果有操作录屏,你就可以清楚地了解到用户的操作路径,从而复现这个BUG并且修复。 实现思路思路一:利用Canvas截图这个思路比较简单,就是利用canvas去画网页内容,比较有名的库有:html2canvas,这个库的简单原理是: 收集所有的DOM,存入一个queue中;根据zIndex按照顺序将DOM一个个通过一定规则,把DOM和其CSS样式一起画到Canvas上。这个实现是比较复杂的,但是我们可以直接使用,所以我们可以获取到我们想要的网页截图。 为了使得生成的视频较为流畅,我们一秒中需要生成大约25帧,也就是需要25张截图,思路流程图如下: 但是,这个思路有个最致命的不足:为了视频流畅,一秒中我们需要25张图,一张图300KB,当我们需要30秒的视频时,图的大小总共为220M,这么大的网络开销明显不行。 思路二:记录所有操作重现为了降低网络开销,我们换个思路,我们在最开始的页面基础上,记录下一步步操作,在我们需要"播放"的时候,按照顺序应用这些操作,这样我们就能看到页面的变化了。这个思路把鼠标操作和DOM变化分开: 鼠标变化: 监听mouseover事件,记录鼠标的clientX和clientY。重放的时候使用js画出一个假的鼠标,根据坐标记录来更改"鼠标"的位置。DOM变化: 对页面DOM进行一次全量快照。包括样式的收集、JS脚本去除,并通过一定的规则给当前的每个DOM元素标记一个id。监听所有可能对界面产生影响的事件,例如各类鼠标事件、输入事件、滚动事件、缩放事件等等,每个事件都记录参数和目标元素,目标元素可以是刚才记录的id,这样的每一次变化事件可以记录为一次增量的快照。将一定量的快照发送给后端。在后台根据快照和操作链进行播放。当然这个说明是比较简略的,鼠标的记录比较简单,我们不展开讲,主要说明一下DOM监控的实现思路。 页面首次全量快照首先你可能会想到,要实现页面全量快照,可以直接使用outerHTML const content = document.documentElement.outerHTML;这样就简单记录了页面的所有DOM,你只需要首先给DOM增加标记id,然后得到outerHTML,然后去除JS脚本。 但是,这里有个问题,使用outerHTML记录的DOM会将把临近的两个TextNode合并为一个节点,而我们后续监控DOM变化时会使用MutationObserver,此时你需要大量的处理来兼容这种TextNode的合并,不然你在还原操作的时候无法定位到操作的目标节点。 那么,我们有办法保持页面DOM的原有结构吗? 答案是肯定的,在这里我们使用Virtual DOM来记录DOM结构,把documentElement变成Virtual DOM,记录下来,后面还原的时候重新生成DOM即可。 DOM转化为Virtual DOM我们在这里只需要关心两种Node类型:Node.TEXT_NODE和Node.ELEMENT_NODE。同时,要注意,SVG和SVG子元素的创建需要使用API:createElementNS,所以,我们在记录Virtual DOM的时候,需要注意namespace的记录,上代码: const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';const XML_NAMESPACES = ['xmlns', 'xmlns:svg', 'xmlns:xlink'];function createVirtualDom(element, isSVG = false) { switch (element.nodeType) { case Node.TEXT_NODE: return createVirtualText(element); case Node.ELEMENT_NODE: return createVirtualElement(element, isSVG || element.tagName.toLowerCase() === 'svg'); default: return null; }}function createVirtualText(element) { const vText = { text: element.nodeValue, type: 'VirtualText', }; if (typeof element.__flow !== 'undefined') { vText.__flow = element.__flow; } return vText;}function createVirtualElement(element, isSVG = false) { const tagName = element.tagName.toLowerCase(); const children = getNodeChildren(element, isSVG); const { attr, namespace } = getNodeAttributes(element, isSVG); const vElement = { tagName, type: 'VirtualElement', children, attributes: attr, namespace, }; if (typeof element.__flow !== 'undefined') { vElement.__flow = element.__flow; } return vElement;}function getNodeChildren(element, isSVG = false) { const childNodes = element.childNodes ? [...element.childNodes] : []; const children = []; childNodes.forEach((cnode) => { children.push(createVirtualDom(cnode, isSVG)); }); return children.filter(c => !!c);}function getNodeAttributes(element, isSVG = false) { const attributes = element.attributes ? [...element.attributes] : []; const attr = {}; let namespace; attributes.forEach(({ nodeName, nodeValue }) => { attr[nodeName] = nodeValue; if (XML_NAMESPACES.includes(nodeName)) { namespace = nodeValue; } else if (isSVG) { namespace = SVG_NAMESPACE; } }); return { attr, namespace };}通过以上代码,我们可以将整个documentElement转化为Virtual DOM,其中__flow用来记录一些参数,包括标记ID等,Virtual Node记录了:type、attributes、children、namespace。 ...

September 9, 2019 · 4 min · jiezi

推荐六本前端开发必看的书籍

第一本:《JavaScript 语言精粹(修订版)》 我毕业后工作头一两年读的第一本 JavaScript 书就是《JavaScript 语言精粹》,当时完整地读了两篇,后来还时不时地会翻里面的重点看。对于前端刚工作的人和前端刚入门的人来说,需要反复阅读和理解。这本书可以深入理解 JavaScript 的特性,写出高质量的代码,适合有 JavaScript 语言基础的人学,不适合于作为零基础入门的教材。 第二本:《数据结构与算法 JavaScript 描述》 如果你专注前端开发,又想学习数据结构和算法,那么这本书是非常不错的选择,至少在中文书籍中,我还没有见到过一本比这本更好的用 JavaScript 描述的数据结构与算法书。 其实这本书在实际工作中没有太大的实战性,就像很多人说的,实际工作中几乎用不到算法。但是,算法绝对是值得每一个程序员去学习的。学习算法是为了提高逻辑思维能力和解决问题的能力,这是每个公司都第一看重的能力。 如果你发现你学的技术已经有了到了瓶颈的感觉,那么说明你需要学习算法了。就算为了给你工作加分,也应该学习算法。如果面试时让你写一个冒泡排序,你不会,那岂不是很尴尬。 当然,这书讲的都是数据结构和算法基础知识,比如字典、集合、二叉树、排序算法等。但如果你已经有这基础,我觉得对于前端开发这份工作已经够了,如果你想学习机器学习或继续锻炼自己的逻辑思维能力,我推荐关注我的系列教程《算法》,也是基于 JavaScript 语言的。 第三本:《CSS 揭秘》 这本书是一书很好的 CSS 实战性教程。每一节都是先给出一个“难题”,再给出一个或多个“方案”,并会作详细解释。掌握 CSS 的最好学习方法就是多练,这本书是中文 CSS 书籍中我个人最喜欢的一本。为什么它写的好。举个书中的例子: 书中第二章第 1 节讲的是半透明边框,这个场景是很常见的,看起来很简单,就是设置 boder 属性而已,但默认情况下,背景会延伸到边框的区域下层,也就是说你即使设置了半透明,默认情况这个半透明边框是看不到的。具体解决方案我就不讲了,大家可以去看这本书。 第四本:《JavaScript 高级程序设计, 第 3 版》 这本书和《JavaScript 权威指南, 第 6 版》相比,我强烈推荐《JavaScript 高级程序设计》。如果你想正儿八经地系统性地学习一遍 JavaScript,那么这本书应该是最好的选择。而且我建议看完后可以画画重点,将来既可以作为参考用,对于面试也是能派上用场的。当然,如果你已经有一定基础了,不必每一章每一节都看,可以筛选着看。 这本书算是基础类的了,如果你想对 JavaScript 有更深入更细的理解和掌握,特别是理解那些比较晦涩难懂的知识点,比如this、原型、委托等,那就看看《你不知道的 JavaScript》这套书,它分为上、中、下三卷。 第五本:《ECMAScript 6 入门》 这本书是阮一峰老师写的,而且是开源的,在线阅读地址是:http://es6.ruanyifeng.com,讲的是 ES6 标准下的新语法,我个人觉得讲得很好,言简意赅。如果你用过 Vue、React 等前端框架,你就知道 ES6 标准的 JavaScript 语法无处不在。所以如果你还没学,或者学得不够全,那么我建议你看看这个教程。 ...

September 9, 2019 · 1 min · jiezi

Vue实战如何实现商品评价栏目14

这篇我们来实现商品评价栏目。 完成评分组件的结构: 我们先来设置一个ratings容器,还是熟悉的老情况,ratings-wrapper的高度可能会超过ratings,这时候我们肯定会让ratings内出现滚动条的,ref也是老朋友了,配合Bscroll实现ratings-wrapper的滚动。 接着我们通过overview来构建商家评分内容。 最后评论的内容content部分,我们通过点击全部,有图,点评,来显示不同的内容,content这个部分就是我们经常遇到的俗称选项卡。 <div class="ratings" ref="ratingView"> <div class="ratings-wrapper"> <div class="overview"> <div class="overview-left"> <div class="comment-score"> <p class="score">{{ratings.comment_score}}</p> <p class="text">商家评分</p> </div> <div class="other-score"> <div class="quality-score item"> <span class="text">口味</span> <span class="score">{{ratings.quality_score}}</span> </div> <div class="pack-score item"> <span class="text">包装</span> <span class="score">{{ratings.pack_score}}</span> </div> </div> </div> <div class="overview-right"> <div class="delivery-score"> <p class="score">{{ratings.delivery_score}}</p> <p class="text">配送评分</p> </div> </div> </div> <div class="content"> <div class="rating-select" v-if="ratings.tab"> <span class="item" @click="selectTypeFn(2)" :class="{'active':selectType==2}" >{{ratings.tab[0].comment_score_title}}</span> <span class="item" @click="selectTypeFn(1)" :class="{'active':selectType==1}" >{{ratings.tab[1].comment_score_title}}</span> <span class="item" @click="selectTypeFn(0)" :class="{'active':selectType==0}"> <img src="./icon_sub_tab_dp_normal@2x.png" v-show="selectType!=0"> <img src="./icon_sub_tab_dp_highlighted@2x.png" v-show="selectType==0"> {{ratings.tab[2].comment_score_title}} </span> </div> <div class="labels-view"> <span v-for="item in ratings.labels" class="item" :class="{'highligh':item.label_star>0}" >{{item.content}}{{item.label_count}}</span> </div> <ul class="rating-list"> <li v-for="comment in selectComments" class="comment-item"> <div class="comment-header"> <img :src="comment.user_pic_url" v-if="comment.user_pic_url"> <img src="./anonymity.png" v-if="!comment.user_pic_url"> </div> <div class="comment-main"> <div class="user">{{comment.user_name}}</div> <div class="time">{{fotmatDate(comment.comment_time)}}</div> <div class="star-wrapper"> <span class="text">评分</span> </div> <div class="c_content" v-html="commentStr(comment.comment)"></div> <div class="img-wrapper" v-if="comment.comment_pics.length"> <img v-for="item in comment.comment_pics" :src="item.thumbnail_url"> </div> </div> </li> </ul> </div> </div> </div></template>获取评分数据 ...

September 8, 2019 · 5 min · jiezi

webpack4配置进阶

description这里介绍了清除上次打包后的dist文件夹,CopyWebpackPlugin的作用,减小打包出来的bundle.js文件的大小,从bundle里抽离出css,可视化分析打包文件的webpack插件,还有webpack sourceMap定位压缩前代码错误,与配置本地代理实现开发环境跨域 清除上次打包的东西clean-webpack-plugin为什么要清理/dist文件夹?如果在多次打包后, 我们的 /dist 文件夹显得相当杂乱。webpack 将生成文件并放置在 /dist 文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。 通常比较推荐的做法是,在每次构建前清理 /dist 文件夹,这样只会生成用到的文件。让我们实现这个需求。 clean-webpack-plugin 是一个流行的清理插件,安装和配置它。 npm install clean-webpack-plugin -Dwebpack.config.js const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = { //...other code plugins: [ new CleanWebpackPlugin({ cleanAfterEveryBuildPatterns: ['./dist'] }) ]}new CleanWebpackPlugin({ cleanAfterEveryBuildPatterns: ['./dist']})现在,执行 npm run build,检查 /dist 文件夹。如果一切顺利,现在只会看到构建后生成的文件,而没有旧文件! CopyWebpackPluginit is to copy files that already exist in the source tree, as part of the build process. 将开发环境下的目录复制到生产环境下。因为如果你需要在代码中引入静态资源文件。如果你对url的引入是相对地址 <img class="logoIcon" src="../../assets/logo.png"/> 在打包后部署到线上环境(npm run build) 或者在开发环境(npm run dev)下 会找不到资源的路径。因为打包后的目录与开发时的目录将会发生很大的变化。所以不能找到资源路径。 ...

August 28, 2019 · 3 min · jiezi

React-Fiber架构可控的调用栈

React 16采用新的Fiber架构对React进行完全重写,同时保持向后兼容。 动机:concurrent renderingconcurrent rendering 又叫 async rendering,主要包含2个特性: time slicing(分片) 为了让浏览器保持60fps,因此渲染一帧需要在16.67ms内完成,否则会造成“卡顿”time slicing将渲染工作切分,从而保证JavaScript的执行不会造成卡顿另一个功能是,将渲染工作按重要性来排序,提高时间敏感(time-sensitive)渲染的优先级(比如text input)suspense 让任何一个组件能够暂停渲染,等待数据的获取(比如懒加载组件、比如网络请求数据)这两个特性的关键前提是:React的渲染能够被中止(interrupt)、恢复。这就是为什么我们需要fiber架构了。 背景:JavaScript的执行模型:call stack首先,我们先解释,为什么过去的架构无法支持渲染中止。 JavaScript原生的执行模型:通过调用栈来管理函数执行状态。 其中每个栈帧表示一个工作单元(a unit of work),存储了函数调用的返回指针、当前函数、调用参数、局部变量等信息。因为JavaScript的执行栈是由引擎管理的,执行栈一旦开始,就会一直执行,直到执行栈清空。无法按需中止。 这与React有什么关系呢?React将视图看做函数调用的结果: View = Component(Data)Component会递归调用其他的Component。页面复杂的话,这个调用栈会很深,导致UI变卡。在React Fiber之前,React的渲染就是使用原生执行栈来管理组件树的递归渲染。这意味着,整颗组件树的渲染必须一次性完成,工作无法被分片。因此,react需要另一种可控的执行模型,让react来管理工作的调度。 React Fiber架构:可控的“调用栈”React Fiber架构就是用JavaScript来实现的执行模型。可以将它比作由react管理的“调用栈”,一个fiber与一个函数栈帧非常类似,它们都表示一个工作单元(a unit of work)。一个组件实例对应一个Fiber。 函数栈帧fiber返回指针父组件当前函数当前组件调用参数props局部变量stateReact Fiber的构造函数源码 React Fiber与调用栈的区别: React Fiber是链表结构,过去的递归调用变成了对fiber的链表遍历。fiber不仅有return指针,还有child、sibling指针,有这三个指针的链表就能够实现深度优先遍历(其实scheduler还能够更加灵活地调度,使得react能够优先执行重要组件的渲染)。fiber与调用栈的另一个区别是,栈帧在函数返回以后就销毁了,而fiber会在渲染结束以后继续存在,保存组件实例的信息。React Fiber是使用JavaScript实现的,这意味着它的底层依然是JavaScript调用栈。Fiber其实是计算机科学中早已存在的概念。Fiber的英文含义就是“纤维”,意指比Thread更细的线,寓意它是比线程(Thread)控制得更精密的执行模型。fiber是协作的(cooperatively)、可控的。一个fiber执行完自己的工作以后,会主动让出控制权,不会主宰(dominate)整个程序的执行。 协程(Coroutines)基本是相同的概念,它们的区别微乎其微。说白了,React Fiber就是用JavaScript重新实现了一个协程模型。话说回来,generator函数也能够主动让出程序控制权(generator函数本质就是协程),用它也能够做到concurrent rendering。为什么react不使用generator函数而是重新实现协程,应该是因为后者能够更加灵活吧,比如generator函数不支持回到之前的yield状态,而fiber支持从任意一个fiber节点重新开始渲染。与Fiber相反,调用栈模型则不可控、不协作(non-cooperatively)。如果函数不断地递归调用,那么会完全主宰整个程序,后续的工作(比如浏览器paint)必须等待它执行完成。 摘自React Fiber是什么: 在React Fiber中,一次更新过程会分成多个分片完成,所以完全有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,这时候,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。因为一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase。在第一阶段Reconciliation Phase,React Fiber会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase,那就一鼓作气把DOM更新完,绝不会被打断。生命周期示意图: 参考资料Algebraic effects, Fibers, Coroutines...React Fiber ArchitectureInside Fiber: in-depth overview of the new reconciliation algorithm in React ...

August 18, 2019 · 1 min · jiezi

你必须要知道的前端那些事儿入门级必读

“ 作为一个入门级的前端小白,前端的发展历史,是你必须要了解的。因为这可以让你更好的理解前端、理解整个前端的行业状态。” 原始社会 早期的前端可以说是原始社会。 long time ago~~ 1990 年,Tim Berners-Lee 以超文本语言 HTML 为基础在 NeXT 电脑上发明了最原始的 Web 浏览器。 1994 年 11 月,Mosaic 浏览器的开发人员创建了网景公司(Netscape Communications Corp.),并发布了 Mosaic Netscape 1.0 beta 浏览器,后改名为 Navigator。但是它只有少数的幸运儿才能使用,因为它此时,只是为了方便科学家们查看文档,传阅论文用的。 所以你可以把1994年看做是前端的起点。 NCSAMosaic浏览器1-1 NCSAMosaic浏览器1-2 此时的网页还是很笨拙的! 你的页面如果有新的内容要刷新展示,那它会重新加载一个新的网页。而这个过程是漫长的。 你如果提交了一个数据请求,那你可能要白屏等待很久,最后返回给你个数据请求错误。。。 如果此时,你想做一个电商网站,那体验的酸爽,可想而知~ 此时的互联网,它的开发者统称为程序猿。 因为前后端开发是一体的,前端代码是后台代码的一部分:后台收到浏览器的请求 ===> 发送静态页面 ===> 发送到浏览器。(这跟我们现在的前后端分离开发方式,完全不一样) 此时的网页以 HTML 为主,是纯静态的网页,网页是“只读”的,信息流只能通过服务器到客户端单向流通,由此世界进入了 Web 1.0 时代。 石器时期 1995 年,网景工程师 Brendan Eich 花了10几天时间设计了 JavaScript 语言。起初这种脚本语言叫做 LiveScript,后来为了借助 Java 语言创造良好的营销效果最终改名为 JavaScript。网景公司把这种脚本语言嵌入到了 Navigator 2.0 之中,使其能在浏览器中运行。 1996 年,微软发布了 VBScript 和 JScript。JScript 是对 JavaScript 进行逆向工程的实现,并内置于 Internet Explorer 3 中。但是 JavaScript 与 JScript 两种语言的实现存在差别,这导致了程序员开发的网页不能同时兼容 Navigator 和 Internet Explorer 浏览器。Internet Explorer 开始抢夺 Netscape 的市场份额,这导致了第一次浏览器战争(有兴趣的同学可以找度娘哦~)。 ...

July 16, 2019 · 2 min · jiezi

fcwhiteboard支持镜像录播回放的-Web-电子白板

fc-whiteboard,支持镜像、录播、回放的 Web 电子白板在很多培训、协作、在线演讲的场景下,我们需要有电子白板的功能,能够方便地在演讲者与听众之间共享屏幕、绘制等信息。fc-whiteboard https://parg.co/NiK 是 Web 在线白板组件库,支持实时直播(一对多)与回放两种模式,其绘制版也能够独立使用。fc-whiteboard 内置了 EventHub,只需要像 Mushi-Chat 这样提供简单的 WebSocket 服务端,即可快速构建实时在线共享电子白板。 Usage | 使用Whiteboard live mode | 直播模式直播模式的效果如下图所示: 示例代码请参考 Code Sandbox,或者直接查看 Demo; import { EventHub, Whiteboard, MirrorWhiteboard } from 'fc-whiteboard';// 构建消息中间件const eventHub = new EventHub();eventHub.on('sync', (changeEv: SyncEvent) => { console.log(changeEv);});const images = [ 'https://upload-images.jianshu.io/upload_images/1647496-6bede989c09af527.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240', 'http://upload-images.jianshu.io/upload_images/1647496-d281090a702045e5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240', 'http://upload-images.jianshu.io/upload_images/1647496-611a416be07d7ca3.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240'];// 初始化演讲者端const whiteboard = new Whiteboard( document.getElementById('root') as HTMLDivElement, { sources: images, eventHub, // Enable this option to disable incremental sync, just use full sync onlyEmitSnap: false });whiteboard.open();// 初始化镜像端,即观众端const mirrorWhiteboard = new MirrorWhiteboard( document.getElementById('root-mirror') as HTMLDivElement, { sources: images, eventHub });mirrorWhiteboard.open();WebSocket 集成WebSocket 天然就是以事件驱动的消息通信,fc-whiteboard 内部对于消息有比较好的封装,我们建议使用者直接将消息透传即可: ...

July 15, 2019 · 4 min · jiezi

如何把SAP-WebIDE里的Web项目同Github仓库连接起来

我们在SAP WebIDE里进行UI5应用开发时,当然也希望能将开发的代码纳入到github版本管理中去。 步骤其实非常简单。 右键点击WebIDE里UI5应用,git->Initialize Local Repository: 输入github上的用户名和密码,稍后WebIDE会用这个credential与Github建立连接。 在Github上新建一个空的代码仓库: 把生成的url复制下来,粘贴到SAP WebIDE的git repository配置对话框中: 然后再使用右键菜单创建一个远端branch,取名Master: 之后,利用右侧的git面板提供的各种命令,即可进行常规的commit,pull,push等操作。 在WebIDE里触发的Github操作成功施加到了Github的仓库里 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

July 14, 2019 · 1 min · jiezi

Vue实战菜单栏商品展示数据交互8

上篇我们将菜单栏,商品展示结构完成,本次我们为这两个部分接入数据。 菜单栏接入数据 导入组件,定义需要的数据格式 <script>// 导入BScroll 滚动组件import BScroll from "better-scroll";// 导入Food 商品页面import Food from "components/Food/Food";export default { data() { //准备需要的数据,初始化组件。 return { container: {}, goods: [], poiInfo: {}, listHeight: [], scrollY: 0, menuScroll: {}, foodScroll: {}, selectedFood: {} }; }, //引用组件 components: { BScroll, Food }};</script>通过created钩子发起请求获取数据 之前我们说过在created钩子,mounted钩子内可以发起请求,请求需要的数据。本次我们在created钩子内发起get请求,获取数据 created() { //通过that改变.then中的this指向 var that = this; // 通过axios发起get请求 this.$axios .get("/api/goods") .then(function(response) { // 获取到数据 var dataSource = response.data; if (dataSource.code == 0) { that.container = dataSource.data.container_operation_source; that.goods = dataSource.data.food_spu_tags; that.poiInfo = dataSource.data.poi_info; // 调用滚动的初始化方法 // that.initScroll(); // 开始时,DOM元素没有渲染,即高度是问题 // 在获取到数据后,并DOM已经被渲染,表示列表高度是没问题的 // nextTick that.$nextTick(() => { // DOM已经更新 that.initScroll(); // 计算分类区间高度 that.calculateHeight(); }); } }) .catch(function(error) { // 出错处理 console.log(error); }); },注意$nextTick()用法,在dom更新后。我们执行初始化滚动函数。 ...

July 13, 2019 · 2 min · jiezi

现代WebGIS开发教程之ES6基础2开发环境

开端 Node.js环境配置好以后,可以在命令行通过交互式方法编写代码,没有语法检查,没有提示,效率比较低,因此需要一个趁手的IDE,我不打算在这里讨论什么IDE好,高手用vim,全靠命令也比你快。我这里介绍的是WebStorm/IntelliJ的基本配置。基本操作 WebStorm/IntelliJ官网免费下载试用。地址:https://www.jetbrains.com/web... https://www.jetbrains.com/idea 下载安装后,就可以用了打开后应该长这样,中介略去初始插件配置,美化配置,授权环节若干步骤... 接下来就可以新建项目了,做开发英语是基本的素质,不要问我有没有汉化版,菜单在哪里。这货支持的项目类型挺多 我偏喜欢从头开始,项目走起 然后自己在项目下面新建一个目录,IDE在下方可以激活Terminal窗口,进入子目录,敲命令 cd path/to/dir真正的新建项目 IDE新建项目时为了方便IDE的管理,而作为Web项目,真正的管理工具是npm,在terminal中敲 npm init 在一系列提示和输入之后就完成创建了,至于提问部分:项目名,版本,简述,入口文件,测试命令,git库地址,关键词,作者,开源协议。好吧,做项目要本着开源精神。总之最后yes后面回车就ok了。再看看IDE,打开生成package.json,就是这样了,这里我删除了test命令。 接下来在目录面新建src目录,创建index.js,编写代码,可以看到有语法提示了 let result = console.log('Hello, World!');console.log(result); 配置运行 然后运行 输出的结果和上篇交互式环境中一样输出,我们可以理解为交互式环境默认都加了console.log输出语句结果,因此出现了undefined。 到目前为止只是运行,三分代码,七分调试,IDE的调试功能很重要,把代码改成 const greeting = `Hello, World!`;let result = console.log(`${greeting}`);console.log(result); 在let result = console.log(${greeting});处设置断点,点击调试按钮,弹出调试面板,程序运行到此处就会停止,可以添加监视变量,逐过程,逐语句调试,各种调试功能就出现了。 以上就是WebStorm/IntelliJ的基本操作,本篇npm创建了项目,但是运行调试还没有用上,为什么要npm配置项目呢,后面会介绍。

July 11, 2019 · 1 min · jiezi

Web-视频格式简明指南

作者:Anton Garcia Diaz翻译:疯狂的技术宅 原文:https://www.freecodecamp.org/... 未经允许严禁转载 网络视频一直都很火。虽然在页面上嵌入 Instagram 和 Youtube 视频非常简单,但是有越来越多的需求 —— 比如许多电子商务的场景 —— 要求定制的视频传输方法。 在设置视频处理和传输管道时,首先要考虑的是要服务的视频格式。 用户体验、支持(浏览器和系统)、压缩效率或编码速度等方面可能与此项选择相关。 根据我 web 商业媒体优化的经验,我将会试着强调需要考虑的主要方面。如果你正在寻找关于使用 ffmpeg 的简单转码和优化选项,你还可以查看这篇文章。 容器和编解码器与通常的图像格式相比,意识到容器和编码标准之间的区别是非常重要的。文件扩展名只能告诉我们它属于哪个容器,而不是使用哪个编解码器。所遵循的编码标准决定了浏览器或系统是否支持它。 例如,虽然 Web 视频格式一般都用了 mp4 容器和 H264 标准进行编码,但并非每个 mp4 文件都能受到普遍支持,因为它可能采用了不同的标准编码,如 H265。 它甚至在自适应比特率(ABR)方面变得更加复杂,这为响应用户的网络和设备功能带来了最佳方式。 让我们看一下容器,编码和交付标准的主要组合,以及它们在支持、压缩效率、编码速度和用户体验方面的差异。 渐进式视频H264/AVC视频格式之王采用带有 H264/AVC 编码的mp4容器。有时你也会在 m4v 容器(Handbrake 中的默认格式)中看到它,这是 Apple 为具有 DRM 保护的 H264 视频开发的 mp4 衍生产品。 每个浏览器和系统 —— 以及iOS和Android中的本机应用程序 —— 都支持这种格式。这是避免兼容性问题的安全选择。 此外,几乎所有台式机和移动设备都支持 H264 的硬件加速。编解码速度很快。、 总而言之,对这种格式编码和使用都非常简单。与图像一样,你只需用 HTML5 插入视频链接,就可以在任何浏览器下使用。 大约 2000 kbps 和超过几秒的延迟时间可能会影响视觉质量。当通过移动网络或网络高峰时段观看时,可能会出现停顿和重新缓冲。如果使用降低图像质量的方案将会产生模糊、飞蚊或块状之类的伪影。 H265/HEVC这是一种使用相同的容器并用 H265 HEVC 编码的强大的视频格式,可以产生更高的压缩效率(体积减少约50%),除了模糊之外的其他问题要小得多。这种格式的主要问题在于支持仅限于 Apple 设备,其中包括其价格中的高额版税。几乎只有 Safari 和 iOS 应用才能使用它。如果你有许多 iPhone 或 Mac 用户,可以把它作为 H264 的后备版。他们的体验会更好。 ...

July 11, 2019 · 2 min · jiezi

CookieSession与Token

CookieCookie是一个http请求首部,当服务端响应头上标记着setCookie时,可以设置此cookie到当前域名下。浏览器端会将此cookie以kv的形式存储到本地文件中 Sessionsession实际上是一种概念,表示每次会话服务器存储的用户信息 实现:常见的手段是使用cookie来实现session。以java为例,客户端首次请求服务端后(例如登录),服务端通过setCookie 设置jsessionid (不设置cookie超时时间,浏览器对于不设置cookie超时间的cookie会在浏览器标签页关闭时自动清空这些cookie)。服务端存储这个sessionid。当客户端第二次请求服务端时,浏览器会自动将属于该域名下的cookie通过http请求首部带到后端服务器中,后端服务器跟本地存储的sessionid进行验证来比对是否是正确用户。既然session是个概念,那么也肯定有其他的实现方式。还有一种需要前端配合的实现方式是通过页面的url中携带session信息。来实现客户端和服务端session之间的传递,不过比较麻烦与过时,这里不详细介绍。 缺点:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。当用户过多时,在服务端的内存中存储的大量session信息会严重影响内存,不方便扩展。比方说当你打算用两台电脑存储session时,session的同步就很不方便。也可以使用单独的服务器使用redis来存放session信息。但是万一这一台数据库服务器宕机后,让所有正在登陆的用户重新登陆当然会让用户很不爽。Token:token是一种身份验证的机制,初始时客户端携带用户信息访问服务器(比如说登录),服务端采用一定的加密策略生成一个字符串(token)返回给客户端,客户端保存token的信息,并在接下来请求的过程中将token信息及用户信息通过httpHeader来发送给服务端。客户端跟据相同的加密算法对用户信息进行比对,生成新的token和用户发过来的token进行比对,来判断是否是正确且过期的用户。这个时候我们就可以考虑到其实用服务器的session也可以实现类似于token的功能,那么为什么一定要用token。我在网上找到一个值得信服的理由是:如果是开发api接口,前后端分离,最好使用token,为什么这么说呢,因为session+cookies是基于web的。但是针对 api接口,可能会考虑到移动端,app是没有cookies和session的。 实现:使用cookie来实现。使用cookie实现的话就和session差不多。这里不多加赘述。使用其他的httpHeader来实现。这就需要前端的一定配合。完整方案如下: 随便设定一个响应头与请求头,比方说userToken,Authorization,前端访问后台登录接口获取到token后,将token存储在 Cookie 里或者 Local Storage中。在前端的请求ajax加一个过滤,再向后端发送请求时,先再beforSend函数中设置这个请求头。后端收到请求后也先检测规定请求头中是否携带了token并且验证token是否过期(实际开发中还是比较少用这种情况,因为直接设置一个过期时间比较长的token即可,当发现过期是就直接返回token过期即可)在前端的响应中加一个过滤,检测后端发回的token是否更新,如果更新则更新持久化的token信息(目的是token续期)通过上面的这一套流程我们能发现使用token还是很麻烦的。需要大量的前端配合,所以这种方案真的很适合前后端分离的项目(由于前后端分离,那么前端大概率是SPA单页面应用,这种应用基本都会在ajax中加过滤,方便对统一的ajax返回的错误信息进行统一处理等)对于服务端来说,有统一的标准来实现token,叫JWT(JSON WEB TOKEN)有兴趣深入了解的话可以看下面的参考文章。 token的优势方便横向拓展 比方说我们有不用语言构建的服务,比方说java node php等等。那么我们如何做到统一登录呢,这个时候就需要统一的验证机制。由于不再依赖cookie,所以不再有CSRF问题支持移动设备有状态Token实际开发过程中,我们实际上还是会把token存入服务器,这样就跟web中session基本没什么区别。因为token中内容还是比较长的,每次客户端访问服务器都带着这么长的信息,并且每次在服务器还要重新计算进行比对还是比较耗费时间和流量的。所以我们还是要用有状态token,我们可以想象下传统session的实现:客户端频繁访问服务器只携带session_id,然后服务器通过session_id去session中查找对应的存储信息。有状态token也是使用这个逻辑:在用户第一次登陆成功后,服务器生成token,因为token比较长,遂将其存在了服务器,然后返回客户端一个加密的tokenid,客户端每次通过这个加密的tokenid访问服务器,原理就跟session_id的流程是一样的了。我们知道服务器的session是存储在内存中的,为了高效。同理我们将token信息也存储在服务器的内存,通用的解决方案是存储到redis中,我们知道redis的存储是基于内存的。速度会比数据库快一些,同时redis是可以设置数据的有校时间的,假如有效期为30天,在插入数据时,就可以设置该条数据的有效期为30天,30天后,redis就会自动删除该数据。那么30天后,用户携带tokenid来访问服务器,服务器去redis中查询不到信息,代表验证失败。(当搭建集群后redis显得尤为重要,分布式session就是通过redis解决的)。 感悟技术只是手段,不同的使用场景用不同的技术。 参考文献Cookie-MDN彻底理解cookie,session,tokenJwt的使用场景10分钟了解JSON Web令牌(JWT)

July 10, 2019 · 1 min · jiezi

借助URLOS快速安装beego-web框架

简介beego是一个快速开发Go应用的http框架,go 语言方面技术大牛。beego可以用来快速开发API、Web、后端服务等各种应用,是一个RESTFul的框架。 今天我们介绍一种更快速的安装方法,那就是通过URLOS一键安装beego。urlos是什么? URLOS是一个云主机管理软件,基于Docker容器技术打包和运行应用,包含负载均衡和故障转移等高级功能,可自动识别机器和云应用的故障并将云应用转移至可用的机器上,单机故障并不影响业务开展。 你可以使用以下命令安装URLOS: curl -LO www.urlos.com/iu && sh iu在此不讨论URLOS的使用方法,感兴趣的朋友请自行搜索,我们直接来看URLOS如何快速安装beego: 安装流程1.登录URLOS系统后台,在应用市场中搜索“beego”,找到之后,直接点击安装按钮 2.填写服务名称、选择运行节点、服务端口、选择智能部署 3.填写域名:www.aaa.com(这里填写自己的域名) 4.设置SFTP选择“上传与下载”选项卡,开启SFTP上传下载并填写SFTP端口、SFTP密码; 然后点击“提交”按钮,等待部署完成; 部署完成后,网站已经成功跑起来了! 上传网站代码用ssh或者sftp客户端登录。 网站根目录是:/mounts/beego001/data/www/web(由于本次教程的服务名称为beego001,实际操作中根据你填写的服务名称自动创建) 如果要更新网站,上传网站文件到网站根目录后,重新部署一下就好了:

July 10, 2019 · 1 min · jiezi

Hooks-Context状态管理的新选择

React 16.3 版本,正式推了出官方推荐的 context API —— 一种跨层级的数据传递方法。React 16.8 版本,推出了全新的 hooks 功能,将原本只有 class 组件才有的状态管理功能和生命周期函数功能,赋予了 function 组件。Hooks 配合 context 一起使用,为 react 状态管理提供了一种新的选择。这可能会减少开发者对 redux 等状态管理库的依赖。 本文首先会对官方的 context 作简单介绍,并搭建一个十分简单的使用全局状态的应用。然后再对 hooks 的基本 API useState useEffect 做基本介绍。接着使用 useContext hooks 对应用进行重构,让 context 的使用变得更优雅。再使用 useReducer hooks 来管理多个状态。最后,待充分理解 hooks 和 context 之后,我们将它们搭配起来用,对整个应用进行状态管理。 Context 概述React 中存在一个众所周知的难题,那就是如何管理全局状态。即便是最基础的全局状态跨越层级传递,也是非常麻烦。此时,首选的解决方案就是使用状态管理库,如 redux。Redux 本身是一个 API 非常少的状态管理工具,其底层也是用 context 实现的。在一些状态管理不是那么复杂,但是又有跨越层级传递数据的需求时,不妨考虑使用 context 直接实现。 例如,一个 Page 组件包含全局状态 user ,需要经过多次props的传递,层级很深的 Avatar 组件才能使用它。 <Page user={user} />// ... render ...<PageLayout user={user}/>// ... render ...<NavigationBar user={user} />// ... render ...<Avatar user={user} />Context :跨层级传递数据Context 提供了一种方法,解决了全局数据传递的问题,使得组件之间不用显式地通过 props 传递数据。 ...

July 5, 2019 · 4 min · jiezi

Web-性能优化的几个策略

原文链接: 何晓东 博客 个人理解优化最开始需要做的是:将现有资源有效利用到极致,然后在做更多提升。开发人员多了解一些优化技术,不仅可以怒斥前端写的代码辣鸡,甚至可以和架构师谈笑风生。????在进行 web 性能优化之前,我们先重温一下,访问一个页面地址到浏览器渲染完页面,都有哪些过程:资源请求/获取流程图: 资源响应/页面渲染流程图: 这两张图无需额外解释了,所以 web 性能优化主要是针对这里的过程进行优化,上车: DNS 优化在与服务主机建立连接之前,需要先解析域名;那么,解析越快就越好。 限制不同域名的数量。数量多了自然会消耗更多的解析时间,https 时代这个成本更高。保证低限度的解析延迟。了解你的 DNS 服务基础设施的结构,然后从你的最终用户分布的所有地域定期监控解析时间。在主体页面 HTML 或响应中利用 DNS 预取指令。例如:<link rel="dns-prefetch" href="//oss.aliyun.com>优化TCP连接开启新连接是一个耗时的过程,https 有加签名过程,更是耗时。 利用 preconnect 指令,连接在使用之前就已经建立好了,这样处理流程的关键路径上就不必考虑连接时间了。 <link rel="preconnect" href="//oss.aliyun.com" crossorigin>借助 CDN,在距离请求用户很近的边缘端点上,请求就可以获得响应,所以可以终止连接,大幅减少建立新连接的通信延迟。静态资源上 cdn 现在成为常态 + 廉价的优化手段了。 避免重定向重定向通常触发与额外域名建立连接。手机网络环境下,重定向会增加很大延迟,毕竟iPhone 高通基带用户越来越多了,照顾信号差的用户。所以可以: 利用 CDN 代替客户端在云端实现重定向。nginx 层面用 rewrite 命令搞定。利用缓存本地缓存:没有什么比直接从本地缓存获取资源来得更快,因为它根本就不需要建立网络连接。最快的请求是根本不发起请求。另外,从本地获取资源时,ISP 或 CDN 提供商不会收取流量费。设置生存时间(TTL)告诉浏览器应该缓存某个资源多久。找到给定资源的最佳 TTL 值并没有完美的科学方法。设置客户端缓存 TTL,可以通过 HTTP 首部指定 cache control 以及键 max-age(以秒为单位),或者 expires 首部。 网络缓存:对于可以共享,能够接收一定旧数据的资料,可以在网络边缘缓存。主要是不需要实时性很高的资源文件可以网络缓存。 条件缓存:如果缓存 TTL 过期,客户端会向服务器发起请求。在多数情况下,收到的响应其实和缓存的版本是一样的,重新下载已经在缓存里的内容也是一种浪费。当资源不经常变化时,使用条件请求可以显著节省带宽和性能;可以通过以下手段使用条件缓存: 在请求中包含 HTTP 首部 Last-Modified-Since。仅当最新内容在首部中指定的日期之后被更新过,服务器才返回完整内容;否则只返回 304 响应码,并在响应首部中附带上新的时间戳 Date 字段。在请求体中包含实体校验码,或者叫 ETag;它唯一标识所请求的资源。ETag 由服务器提供,内嵌于资源的响应首部中。服务器会比较当前 ETag 与请求首部中收到的 ETag,如果一致,就只返回 304 响应码;否则返回完整内容。依然是静态资源文件需要缓存。(可以参考 Nginx 内容缓存机制)压缩和代码极简化所有的文本内容(HTML、JS、CSS、SVG、XML、JSON、字体等),可以从压缩和极简化中受益。这两种方法组合起来,可以显著减少资源大小。更少字节数对应着更少的请求-响应,也就意味着更短的请求时间。它通过可无损还原的算法减少资源大小。在发送资源之前,如果服务器进行压缩处理,可以节省 90% 的大小。 ...

July 4, 2019 · 1 min · jiezi

腾讯-Tars-Web-管理端用户体系对接

背景这段时间一直在基于 Tars 作开发。最近的文章也多是针对 Tars 的一些学习笔记。前面我们搭建了 Tars 基础框架,打开了 Tars web 管理界面进行服务的运维操作。不过读者肯定很快就会发现:这好像不用登录啊,那怎么保证只有有权限的用户才能更改服务呢? 显然 Tars web 是支持用户鉴权的。官方文档在这里。本文记录一下我的用户体系对接实验中的一些笔记,便于其他 Tars 的用户参阅。(特别是像我这样对 Node.js 不熟悉的小白……) 本系列文章: 腾讯 Tars 基础框架手动搭建——填掉官方 Guide 的坑腾讯 Tars-Go 服务 Hello World——从 HTTP 开始腾讯 Tars-Go 服务 Hello World——RPC 通信腾讯 Tars-Go 服务获取自定义模版(配置)值腾讯 Tars Web 管理端用户体系对接(本文)本文地址:https://segmentfault.com/a/1190000019657656 Tars 用户鉴权流程准备如果要启用 Tars web 的用户功能,那么首先开发者需要设计一个自己的用户登录服务。该服务是 http 服务,有独立的用户登录、登出功能。Tars Web 本身实现了一个简单的用户功能,不过本文我们重新设计一个。为便于说明,我们假设这个 Tars web 和用户服务 web 环境如下: Tars Web URL:https://tars.amc.com用户 Web URL:https://user.amc.com基本流程从用户通过浏览器访问 Tars web 管理平台开始,如果启用了用户功能,那么基本流程如下: 一言以蔽之:每当浏览器向 Tars web 发起一个请求时,Tars web 均向用户服务器发起请求,判断用户是否有权限;如果鉴权通过,则正常操作 Tars;如果没有,则重定向至用户登录页面。 ...

July 3, 2019 · 2 min · jiezi

有选择性的启用SAP-UI5调试版本的源代码

在低版本的SAP UI5应用中,我们一旦切换成调试模式,那么应用程序源代码和UI5框架程序的源代码的调试版本都会重新加载,耗时很长。 我最近发现UI5新版本1.66.1提供了选择性加载调试版本的源代码的选项,即下图中的Select Specific modules: 如果确认问题出在我们应用程序,只想调试自己编写的应用代码,那么我们可以只切换应用程序成为调试版本,这样速度大大提高。 此时浏览器地址栏里看到的参数为sap-ui-debug=dis/#, 意思是仅仅disnamespace下的所有资源加载成调试版本: 调试速度大大提高: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 28, 2019 · 1 min · jiezi

SAP-WebIDE里UI5应用的隐藏文件projectjson

在SAP WebIDE UI5应用编辑器里的菜单View->Show Hidden files点击后,即可发现项目文件夹下有一个隐藏文件project.json: 内容如下: 这也解释了为什么build之后,UI5应用文件夹下会多出一个dist的文件夹,内容和webapp里的代码几乎一致: databinding节点下罗列出了每个视图绑定的OData数据集的节点名称,非常实用: generation节点记录了应用创建的时间戳和基于的Fiori模板ID和版本号,translation包含了和翻译相关的资源名称。 basevalidator维护了SAP WebIDE提供的校验实现,当前有fioriXmlAnalysis和fioriJsValidator。 codeCheckingTriggers定义了校验出的错误是否会阻止UI5实际部署到SAP云平台上。hcpdeploy包含了和UI5应用部署相关的信息,比如SAP Cloud Platform的用户id,和部署到云平台之后的应用名称。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 27, 2019 · 1 min · jiezi

SAP-UI5的support-Assistant

SAP UI5的support Assistant给UI5刚入门的开发人员提供了一种极便利的快速熟悉UI5代码的途径。 召唤方式: ctrl+shift+alt+p四个键同时按,在弹出的对话框里点击按钮“activate Support Assistant即可。 之后屏幕下方会出现一个视图,视图左边包含了SAP预定义的检查条目,以及您当前的UI5应用基于这些检查条目校验出的结果。右边则是检查条目检查的具体内容,针对检查出来的问题给出的解决方案,以及具体的检查代码等等。 我觉得对我自己帮助最大的就是Code标签里每个检查条目的具体实现,能让我学习到很多SAP UI5的编程细节。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 27, 2019 · 1 min · jiezi

第6天在Flask应用中添加个人主页

原文: http://www.catonlinepy.tech/声明: 原创不易,未经许可,不得转载 1. 你将学会什么今天的教程主要给大家介绍,如何在Flask应用中添加个人主页以及在个人主页中如何上传用户头像。教程中的代码都会托管到github上,猫姐一如既往的强调,在学习本课内容时一定要亲自动手实现代码,遇到问题再到github上查看代码,如果实在不知道如何解决,可以在日志下方留言。 2. 个人主页的实现2.1 项目目录的创建在创建个人主页之前,先来创建今天的项目目录,猫姐直接将第5天的day5目录复制后改成day6,然后程序里面的userauth_demo目录改成userprofile_demo目录,并将代码中的userauth_demo改为userprofile_demo。在此基础上,我们还需要创建如下文件和目录: # 注意:以下所有的操作都必须在虚拟环境中进行# 在userprofile_demo新建文件utils.py文件,此文件用来保存一些功能独立的小函数(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo$ touch utils.py# 在userprofile_demo目录下新建static目录,此目录用来保存css,js及图片文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo$ mkdir static# cd到static目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo$ cd static# 在static目录中新建profile目录,用户上传的头像图片将保存到该目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo/static$ mkdir profile# 在templates目录中新建account.html文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo/templates$ touch account.html最终,我们得到今天项目的目录结构如下(使用tree命令得到): (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ tree day6day6├── run.py└── userprofile_demo ├── config.py ├── database.db ├── forms.py ├── __init__.py ├── models.py ├── routes.py ├── static │   └── profile │   └── default.jpg ├── templates │   ├── account.html │   ├── index.html │   ├── layout.html │   ├── login.html │   └── register.html └── utils.py2.2 为个人主页添加入口链接通常,在用户登录后,在主页导航栏中会有一个用户名的超链接,当用户点击这个超链接时,就会跳转到用户的个人主页。下面,我们在layout.html文件的导航栏中添加个人主页的入口: ...

June 27, 2019 · 3 min · jiezi

Lua-Web快速开发指南9-使用cf内置的异步库

API 介绍cf框架提供内置的异步库cf, 需要使用的时候我们必须先导入API: local cf = require "cf". 定时器与循环定时器cf库内置了一些定时器方法, 这些方法为开发者提供了对时间事件的控制能力. cf.timeout、cf.at、cf.sleep. cf.sleep方法是一个阻塞的定时器, 只有一个参数用来设置当前协程的休眠时间并且没有返回值. 此方法的行为(语义)取决于用户传入的参数: 当时间参数大于0的时候, 当前协程会暂停指定的时间且让出执行权. 当指定的时间超时后函数将会返回继续执行下面的代码.当时间参数等于0的时候, 当前协程会暂停并且让出执行权. 当其它协程执行完毕(让出)后立刻返回.当时间参数小于0或者非number类型的时候, 此方法将立刻返回.cf.timeout与cf.at不会阻塞当前协程执行流程. 目前虽然暴露给开发者使用, 但真正的使用场景都仅限于在需要长连接业务内. cf.timeout与cf.at都会返回一个timer对象, 开发者可以在任何时候使用timer对象的stop方法停止定时器. cf.timeout与cf.at的参数如下: 第一个参数是一个指定的时间, 其在现实中的时间比例为1:1.第二个参数是一个回调函数, 当时间事件触发后将会为用户执行用户定义的回调函数.记住: cf.timeout是一次性定时器, 回调函数被触发之后将会自动停止运行. 而cf.at如果不使用stop方法停止则会一直重复执行. 协程的使用、暂停、唤醒cf库提供了协程的操作方法. 此协程与Lua的原生协程有些许不同, cf基于原生协程的基础上由框架管理生命周期. 需要异步执行一个函数可以使用cf.fork创建一个由cf调度的协程, 此方法会返回一个协程对象. 这个协程对象可以在它让出的时候用来主动唤醒. cf.fork方法的第一个参数func为function类型, 从第二个参数开始的参数将会作为func的参数(一般情况下我们会利用upvalue而不会显示传递参数). 需要暂停一个cf创建的协程可以使用cf.wait方法. 此方法没有参数, 但如果调用此方法的协程不是由cf创建或不是main协程则会出错. cf.wakeup方法用于唤醒由cf.wait暂停的协程. cf.wait方法的返回值由cf.wakeup的行为决定, 当唤醒的是不存在的协程或唤醒正在执行的协程将会出错. cf.wakeup方法的第一个参数是一个协程对象, 协程对象之后的所有参数将会返回给cf.wait进行接收. 需要获取当前协程对象的时候在这个协程执行流程之间使用cf.self方法获取, cf.self的作用与内置库coroutine.running方法相同. 它返回一个协程对象与一个boolean值. 当协程对象为主(main)协程时则bolean为true, 否则为false. 更多详细的API介绍更多使用介绍请参考cf库的文档. 开始实践1. 随机生成三个定时器并且输出时间.在本示例中! 我们首先修改随机数生成器种子, 随机从0~1中间取一个随机数作为定时器的时间. 然后启动一个循环开始生成3个定时器. -- main.lualocal cf = require "cf"math.randomseed(os.time()) -- 设置随机数种子for index = 1, 3 do local time = math.random() cf.timeout(time, function() print("第"..index.."个定时器的时间为:"..time) end)end由于是随机生成的时间, 所以我们在函数内部使用print方法将当前定时器的运行信息打印出来(第几个创建的定时器与定时器时间). ...

June 24, 2019 · 2 min · jiezi

如何用SAP-WebIDE的Fiori创建向导基于ABAP-OData-service快速创建UI5应用

如果我们手上已经有可以正常工作的OData服务,无论位于ABAP on-premise系统还是public上的internet OData service,都可以用SAP WebIDE里的Fiori创建向导,几分钟之内轻松创建出可以持续开发的UI5应用。 打开SAP云平台上的WebIDE,New->Project from Template: 选择Master Detail风格的Fiori应用: 这里就要指定这个UI5应用消费的OData服务url了。下拉菜单里看到的是一个我在SAP云平台创建的Destination,指向on premise系统: url路径选择/sap/opu/odata/sap/CRM_OPPORTUNITY,做过CRM的朋友们会知道这个路径指向的是CRM ABAP里的OData服务CRM_OPPORTUNITY: 点击Test,会解析出OData服务的metadata,然后可以点Next按钮: 点了Next之后,需要指定Master list和detail视图里重要字段的绑定路径。这些字段的说明在上图右边的缩略图里有展示。 点finish后,应用成功创建。执行应用: 最后渲染的应用如下: 至此我们没有编写一行代码,就得到了一个可以工作的master-detail风格的Fiori应用。 压缩过后webIDE自动生成的JavaScript总共代码也不过500多行: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 23, 2019 · 1 min · jiezi

使用apache的HttpClient进行http通讯隐藏的HTTP请求头部字段是如何自动被添加的

我们用apache的HttpClient这个库消费云端的Restful API时,一般都需要两次HTTP调用,第一次获得某种token,比如获取防止跨域请求伪造攻击Cross-site request forgery - CSRF的token,或者比如微信API的access token,第二次再进行真正的API消费。 通常情况下,第一次请求完毕后,服务器都会给客户端返回一些cookie字段,在第二次请求时,如果使用的是postman测试工具或者apache的HttpClient这个库,cookie字段都会自动被附加在第二次请求的HTTP头部。详情可以参考我写的另一篇博客:OData service parallel performance measurement – how to deal with XSRF token in Java Program and JMeterhttps://blogs.sap.com/2017/08... 本文就来介绍apache的HttpClient,在发送第二个Http请求时,是如何自动插入从第一个请求获得的服务器颁发的cookie的。 首先进入HttpClient的单步调试:InternalHttpClient.doExecute方法: 第85行的origheaders,即取出程序员在代码里指定的http请求头部字段,比如basic Authentication,content-type,token等等: 这个cookie是什么时候传进来的? 看来我们必须进入httpcore-4.4.3.jar这个apache HttpClient的实现里去调试。 经过观察发现,一旦我执行完204行的conn.sendRequestHeader方法,就能观察到Cookie被自动设置了,所以奥妙就在第204行里。 自动添加Content-Length头部字段: 由此可见Content-length是通过方法entity.getContentLength()自动计算出来的,因此我们程序员不必在自己的应用代码里重复这个计算动作。 自动加入host字段: 自动加入Connection: Keep-Alive UserAgent的自动填充:Apache-HttpClient/4.5.1, 这个也不用程序员操心。 终于到了我要找的RequestAddCookies这个HTTPRequestInterceptor了。光从这个类的字面意思就能猜到它和HTTP请求的Cookie有关。 新建一个Cookie,这个CookieOrigin构造函数里的hpst,path和secure标志位都是Chrome开发者工具的Cookie标签页里能看到。 从 Cookie Store里取出前一次请求中由服务器返回的Cookie: 这里把Cookie store里的cookie加到第二个请求的头部字段,谜底就这样解开了。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 22, 2019 · 1 min · jiezi

服务器发布VueNuxt项目指南多图

很多前端朋友可能不是那么了解服务器配置。今天突然翻到之前写的这篇文章,修改完善了之后分享给大家一些常见的的Web服务器部署项目的方式。 写在前面下面讲的每一种服务器深入进去都很复杂,在这篇文章只是讨论一下基本的部署和使用。更高级的知识和用法还需要各位朋友自行去探索和发现, 开始阅读之前希望大家能先了解一些Linux基础,就不至于看起来吃力了。 注: 下面所有的例子大多基于vue-router的history模式下打包生成的静态文件,其他框架也都大同小异 Nginx服务器Nginx 是一个高性能的HTTP和反向代理web服务器,在连接高并发的情况下,Nginx表现相当出色。 Nginx安装这里就不讲这个了吧, 有需要的朋友可以看这个 配置修改为了支持history模式, 我们要修改nginx/conf/nginx.conf文件 location / { root html; try_files $uri $uri/ /index.html; # 只需要加上这么一行 index index.html index.htm;} 然后把静态资源放在html文件夹内 然后启动Nginx服务器 cd usr/local/nginx/sbin./nginx接着访问你的服务器就行OK了。 GZip支持nginx实现资源压缩的原理是通过ngx_http_gzip_module模块拦截请求,并对需要做gzip的类型做gzip压缩,该模块是默认基础的,不需要重新编译,直接开启即可。大体配置如下 #开启和关闭gzip模式gzip on|off;#gizp压缩起点,文件大于1k才进行压缩gzip_min_length 1k;# gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间gzip_comp_level 1;# 进行压缩的文件类型。gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ;#nginx对于静态文件的处理模块,开启后会寻找以.gz结尾的文件,直接返回,不会占用cpu进行压缩,如果找不到则不进行压缩gzip_static on|off# 是否在http header中添加Vary: Accept-Encoding,建议开启gzip_vary on;# 设置压缩所需要的缓冲区大小,以4k为单位,如果文件为7k则申请2*4k的缓冲区 gzip_buffers 2 4k;# 设置gzip压缩针对的HTTP协议版本gzip_http_version 1.1;Nginx配置虽然简单,但是它本身是非常强大的,代理,负载等等都是非常具有实用性的。 Apache服务器Apache是世界使用排名第一的Web服务器软件, 使用非常广泛。由于VueRouter的hash模式本质上和静态资源没什么区别,在Apache上发布又比较简单,这里就跳过了发布直接进入配置支持History模式 基础配置修改Apache默认配置首先要重新修改\conf\httpd.conf文件让文件支持rewrite 找到 // 这一行需要解开注释 引入这个模块LoadModule rewrite_module modules/mod_rewrite.so 然后新增或者修改下面得代码 ...

June 19, 2019 · 2 min · jiezi

web安全学习

web安全讲述确保您的 Web 站点或 Web 应用安全是十分重要的,即使是代码中很小的 bug 也可能导致隐私信息被泄露,黑客会尝试偷窃数据。这些文档提供信息帮助您使代码更安全。此处列出的面向 Web 安全的文章提供的信息可以帮助您保护站点及其代码免受攻击和数据窃取。 CSP内容安全策略xss跨站点脚本攻击CSRF 跨站点请求伪造CSP内容安全策略CSP 的主要目标是减少和报告 XSS 攻击 ,XSS 攻击利用了浏览器对于从服务器所获取的内容的信任。恶意脚本在受害者的浏览器中得以运行,因为浏览器信任其内容来源,即使有的时候这些脚本并非来自于它本该来的地方。 CSP通过指定有效域——即浏览器认可的可执行脚本的有效来源——使服务器管理者有能力减少或消除XSS攻击所依赖的载体。一个CSP兼容的浏览器将会仅执行从白名单域获取到的脚本文件,忽略所有的其他脚本 (包括内联脚本和HTML的事件处理属性)。 Content-Security-Policy: policy 一个网站管理者允许网页应用的用户在他们自己的内容中包含来自任何源的图片, 但是限制音频或视频需从信任的资源提供者(获得),所有脚本必须从特定主机服务器获取可信的代码, 启用发送违规报告,你需要指定 report-uri 策略指令,并提供至少一个URI地址去递交报告: Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com; report-uri http://reportcollector.example.com/collector.cgi xss跨站点脚本攻击跨站脚本攻击Cross-site scripting (XSS,为了不和CSS重名)是一种安全漏洞,攻击者利用这种漏洞可以在客户端注入恶意代码,可以完成获取cookie、session的读取,还可以利用脚本串改HTML内容,引导用户进入第三方恶意站点,主要表现就是: 将一些隐私数据像cookie、session发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作。目前常见的分类包括了: 存储型【永久型】:包括了服务端的操作反射型:有服务端的参与DOM型:纯客户端的攻击存储型【持久型】主要是表现在客户端的输入内容【博客内容、表单提交、富文本编辑等】提交到服务端,被服务端保存,并在返回到客户端进行展示;如果其中含有恶意脚本<script>alert(‘我是存储型XSS攻击')</script>,并被客户端插入到文档流中,那么恶意脚本会被执行,恶意脚本可以完成读取隐私数据、重定向、修改页面展示结构等操作。 反射型【非持久型】反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式往往需要攻击者诱使用户点击一个恶意链接,或者提交一个表单时,恶意链接中的而已脚本参数或者表单提交的恶意脚本经过服务端的关联,注入到了当前访问的文档流中,恶意脚本被执行,和存储型一样,恶意脚本都可以完成读取隐私数据、重定向、修改页面展示结构等操作。 基于DOM型这是一种纯客户端的攻击,客户端在处理页面地址链接时将恶意脚本注入到了正常文档流中,或者是编辑富文本的时候将它处赋值的含有恶意脚本的富文本插入到了文档流中,导致恶意脚本的执行。同样可以完成读取隐私数据、重定向、修改页面展示结构等操作。 防范xss保证隐私数据的安全性,添加cookie时,设置 Secure; HttpOnly,仅支持https连接和脚本无法读取。Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly处理任何用户端的输入用户端的自定义输入是完全无法保证的,所以需要进行处理,包括特殊字符的转译和限制长度等。服务端的数据转译如果服务端的数据遭到了存储型攻击,那么就需要对服务端的数据进行必要的转译编码,CSRF 跨站点请求伪造跨站请求伪造(CSRF)是一种冒充受信任用户,向服务器发送非预期请求的攻击方式。例如,这些非预期请求可能是通过在跳转链接后的 URL 中加入恶意参数来完成:<img src="https://www.example.com/index.php?action=delete&id=123">对于在 https://www.example.com 有权限的用户,这个 <img> 标签会在他们根本注意不到的情况下对 https://www.example.com 执行这个操作,即使这个标签根本不在 https://www.example.com 内亦可。 ...

June 19, 2019 · 1 min · jiezi

Lua-Web快速开发指南8-利用httpd提供Websocket服务

Websocket的技术背景WebSocket是一种在单个TCP连接上进行全双工通信的协议, WebSocket通信协议于2011年被IETF定为标准RFC 6455并由RFC7936补充规范. WebSocket使得客户端和服务器之间的数据交换变得更加简单, 使用WebSocket的API只需要完成一次握手就直接可以创建持久性的连接并进行双向数据传输. WebSocket支持的客户端不仅限于浏览器(Web应用), 在现今应用市场内的众多App客户端的长连接推送服务都有一大部分是基于WebSocket协议来实现交互的. Websocket由于使用HTTP协议升级而来, 在协议交互初期需要根据正常HTTP协议交互流程. 因此, Websocket也很容易建立在SSL数据加密技术的基础上进行通信. 协议WebSocket与HTTP协议实现类似但也略有不同. 前面提到: WebSocket协议在进行交互之前需要进行握手, 握手协议的交互就是利用HTTP协议升级而来. 众所周知, HTTP协议是一种无状态的协议. 对于这种建立在请求->回应模式之上的连接, 即使在HTTP/1.1的规范上实现了Keep-alive也避免不了这个问题. 所以, Websocket通过HTTP/1.1协议的101状态码进行协议升级协商, 在服务器支持协议升级的条件下将回应升级请求来完成HTTP->TCP的协议升级. 原理客户端将在经过TCP3次握手之后发送一次HTTP升级连接请求, 请求中不仅包含HTTP交互所需要的头部信息, 同时也会包含Websocket交互所独有的加密信息. 当服务端在接受到客户端的协议升级请求的时候, 各类Web服务实现的实际情况, 对其中的请求版本、加密信息、协议升级详情进行判断. 错误(无效)的信息将会被拒绝. 在两端确认完成交互之后, 双方交互的协议将会从抛弃原有的HTTP协议转而使用Websocket特有协议交互方式. 协议规范可以参考RFC文档. 优势在需要消息推送、连接保持、交互效率等要求下, 两种协议的转变将会带来交互方式的不同. 首先, Websocket协议使用头部压缩技术将头部压缩成2-10字节大小并且包含数据载荷长度, 这显著减少了网络交互的开销并且确保信息数据完整性. 如果假设在一个稳定(可能)的网络环境下将尽可能的减少连接建立开销、身份验证等带来的网络开销, 同时还能拥有比HTTP协议更方便的数据包解析方式. 其次, 由于基于Websocket的协议的在请求->回应上是双向的, 所以不会出现多个请求的阻塞连接的情况. 这也极大程度上减少了正常请求延迟的问题. 最后, Websocket还能给予开发者更多的连接管控能力: 连接超时、心跳判断等. 在合理的连接管理规划下, 这可提供使用者更优质的开发方案. APIcf框架中的httpd库内置了Websocket路由, 提供了上述Websocket连接管理能力. Websocket路由需要开发者提供一个lua版的class对象来抽象路由处理的过程, 这样的抽象能简化代码编写难度. lua classclass 意译为'类'. 是对'对象'的一种抽象描述, 多用于各种面相对象编程语言中. lua没有原生的class类型, 但是提供了基本构建的元方法. cf为了方便描述内置对象与内置库封装, 使用lua table的相关元方法建立了最基本的class模型. 几乎大部分内置库都依赖cf的class库. 同时为了简化class的学习成本, 去除了class原本拥有的'多重继承'概念. 将其仅作为类定义, 用于完成从class->object的初始化工作. ...

June 18, 2019 · 2 min · jiezi

POST-请求的三种常见数据提交格式

本文所讲的 POST 请求是 HTTP/1.1 协议中规定的众多 HTTP 请求方法的其中最常用的一个。一般使用 POST 请求方法向服务器发送数据(主要是一些创建更新操作),本文讨论的是 POST 请求方法常用的四种数据提交格式。 由于 HTTP/1.1 协议中并没有对请求使用什么编码方式进行规定,所以理论上开发者完全可以自己决定请求的 Body 体使用什么格式,当然实际上大家都还是用通用的那么几种编码方式来提交数据(生态很关键)。 注:以下排名不分先后。。。 application/x-www-form-urlencoded对于浏览器原生的 form 表单,enctype 的值不指定的话,默认就是这个家伙。实际上大部分情况都使用它即可,编码方式足够简单高效,各方面支持也都很完备,如各大浏览器调试工具、各大抓包软件等。 POST http://www.example.com HTTP/1.1Content-Type: application/x-www-form-urlencoded;charset=utf-8key1=val1&key2=val2基本的请求类似上面这样,数据的编码方式采用 key1=val1&key2=val2 的形式,对其中的键值对都需要使用 URL Encode 编码一下。其实就是和 GET 请求的数据提交格式是一样的,只不过位置从 Request URL 上换到了 Request Body 里。 这种格式结构简单,但对于数据层级较深的情况,比如一些有复杂层级关系的接口数据,这种方式就显得有点力不从心了。另一方面,对于需要上传二进制数据(比如图像、音频等文件),这种方式就不那么高效了,而且对于非 ASCII 码的数据就丢失了,所以传文件的情况就不能使用这种方式。 适用场景:数据量不大、数据层级不深的情况下强烈建议这种数据提交格式。 multipart/form-data当你需要提交文件、非 ASCII 码的数据或者是二进制流数据,则使用这种提交方式。类似下面这个请求示例: POST http://www.example.com HTTP/1.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryPAlLG7hJKNYc4ft3------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="text"demo------WebKitFormBoundaryPAlLG7hJKNYc4ft3Content-Disposition: form-data; name="file"; filename="demo.png"Content-Type: image/png------WebKitFormBoundaryPAlLG7hJKNYc4ft3--第二行指定编码方式 Content-Type 为 multipart/form-data,紧接着生成一个分界线 boundary 即 ----WebKitFormBoundaryPAlLG7hJKNYc4ft3,又臭又长的目的是为了避免和 Body 正文内容有冲突,它的作用是用来分隔不同的字段。 Body 体分为多个结构类似的部分,每一部分以 --boundary 开头,因为本次请求生成的 boundary 为 ----WebKitFormBoundaryPAlLG7hJKNYc4ft3,所以最终是 ------WebKitFormBoundaryPAlLG7hJKNYc4ft3。接着是描述内容的元信息,包括字段名称,如果是文件则还有文件名称和文件类型。接着留一空行,然后才是字段值。什么时候结束呢,以 --boundary-- 标志结束。 ...

June 18, 2019 · 1 min · jiezi

lua-web快速开发指南7-高效的接口调用-httpc库

httpc库基于cf框架都内部实现的socket编写的http client库. httpc库内置SSL支持, 在不使用代理的情况下就可以请求第三方接口. httpc支持header、args、body、timeout请求设置, 完美支持各种httpc调用方式. API介绍httpc库使用前需要手动导入httpc库: local httpc = require "httpc". httpc.get(domain, HEADER, ARGS, TIMEOUT)调用get方法将会对domain发起一次HTTP GET请求. domain是一个符合URL定义规范的字符串; HEADER是一个key-value数组, 一般用于添加自定义头部; ARGS为请求参数的key-value数组, 对于GET方法将会自动格式化为:args[n][1]=args[n][2]&args[n+1][1]=args[n+1][2]; TIMEOUT为httpc请求的最大超时时间; httpc.post(domain, HEADER, BODY, TIMEOUT)调用post方法将会对domain发起一次HTTP POST请求, 此方法的content-type会被设置为:application/x-www-form-urlencoded. domain是一个符合URL定义规范的字符串; HEADER是一个key-value数组, 一般用于添加自定义头部; 不支持Content-Type与Content-Length设置; BODY是一个key-value数组, 对于POST方法将会自动格式化为:body[n][1]=body[n][2]&body[n+1][1]=body[n+1][2]; TIMEOUT为httpc请求的最大超时时间; httpc.json(domain, HEADER, JSON, TIMEOUT)json方法将会对domain发起一次http POST请求. 此方法的content-type会被设置为:application/json. HEADER是一个key-value数组, 一般用于添加自定义头部; 不支持Content-Type与Content-Length设置; JSON必须是一个字符串类型; TIMEOUT为httpc请求的最大超时时间; httpc.file(domain, HEADER, FILES, TIMEOUT)file方法将会对domain发起一次http POST请求. HEADER是一个key-value数组, 一般用于添加自定义头部; 不支持Content-Type与Content-Length设置; FILES是一个key-value数组, 每个item包含: name(名称), filename(文件名), file(文件内容), type(文件类型)等属性. 文件类型可选. TIMEOUT为httpc请求的最大超时时间; httpc 返回值所有httpc请求接口均会有2个返回值: code, response. code为http协议状态码, response为回应body(字符串类型). ...

June 16, 2019 · 4 min · jiezi

浏览器exe桌面应用用javafx-webview-打造自己的浏览器全屏自适应屏幕

接着上一篇 全屏 Scene scene = new Scene(new Group()); stage.setMaximized(true);自适应屏幕 ScrollPane scrollPane = new ScrollPane(); scrollPane.setFitToHeight(true); scrollPane.setFitToWidth(true); scrollPane.setContent(browser);

June 14, 2019 · 1 min · jiezi

lua-web快速开发指南5-利用template库构建httpd模板引擎

介绍template模板引擎是为了使用户界面与业务数据(内容)分离而产生的, 其本身并不是一种深奥的技术. template模板引擎首先会将合法的模板编译为lua函数, 然后将模板文件和数据通过模板引擎生成一份HTML代码. cf的admin库整使使用了template来构建服务端渲染页面, 并利用单页面+iframe模式快速完成lua后台开发. 1. template基础语法在真正使用之前, 我们先来学习一下template常见的一些基本语法: {{ lua expression }} - lua expression是一段lua表达式; 作用为输出表达式的结果, 一些特殊符号将会被转义;{* lua expression *} - lua expression是一段lua表达式; 作用为输出表达式的结果, 不会转义任何符号;{% lua code %} - 执行一段lua代码, 如: {% for i = x, y do %} ... {% end %};{# comments #}- comments仅作为注释, 不会包含在输出字符串内. 这段语法的作用类似lua内的--与--[[]];{(template)} - 导入其它模板文件; 同时支持传参: {(file.html, { message = "Hello, World" })};2. 转义字符& 将会转义为 &amp;< 将会转义为 &lt;> 将会转义为 &gt;" 将会转义为 &quot;' 将会转义为 &#39;/ 将会转义为 &#47;3. APItemplate.compile(html)参数html为字符串类型, 可以是:模板文件路径、 ...

June 14, 2019 · 2 min · jiezi

lua-web快速开发指南4-详细了解httpd库的作用

httpd库是基于HTTP 1.1协议实现而来, 内置了高性能的http协议解析器与urldecode解析库. httpd库默认情况下就能工作的很好, 但是在一些需求较为极端的场景还是需要微调一下参数. httpd常用的内置方法介绍1. httpd:timeout(number)设置每个连接到最大空闲(idle)连接等待时间, 超过这个数值httpd将主动断开连接. (默认值为:30秒) 2. httpd:max_path_size(number)设置Path的最大长度, 超过这个值httpd将会返回414. (默认值为: 1024) 3. httpd:max_header_size(number)设置Header最大长度, 超过这个值httpd将会返回431. (默认值为: 65535) 4. httpd:max_body_size(number)设置Body的最大长度, 超过这个值将会返回413. (默认为 1024 * 1024) 5. httpd:before(function)before方法决定API与USE路由回调在触发之前的行为, 默认情况下允许所有路由通过. before方法一般用来设置与修改用户验证路由行为(例如头部验证), 这提供了开发者基于before函数设计中间件的机会. 当开发者设置了function后(即是是一个空函数), 需要利用http库来决定行为. 6. httpd:group(type, prefix, handles)group方法提供了一种批量注册路由的方式, 为一组同一组路由提供简单便方便在注册方法. 第一个参数type为需要批量注册的路由类型; 初始化httpd对象后, 使用app.USE或app.API进行传值; 第二个参数prefix为string类型的头部; 例如:/api、/admin; 第三个参数为一组路由处理函数或处理类数组; 类型为: {route = '/login', class = class}; 注意: 此方法仅支持批量注册API与USE路由, 不可同时注册不同类型路由; 7. httpd:static(folder, ttl)listen方法用于告诉httpd对象监听指定端口. 第一个参数ip暂未被httpd使用(但是必须设置), 默认监听所有网卡的'0.0.0.0'地址与指定的端口号; backlog为用户最大连接等待队列, 合理的设置能减少连接被重置的情况(默认值为128). 8. httpd:run()在httpd库所有参数与路由设置完毕之后, 调用run方法开启监听模式. httpd的请求日志日志格式为: [年/月/日 时:分:秒] - [ip] - [x-real-ip] - [path] - [method] - [http code] - [request handle timeline] ...

June 14, 2019 · 1 min · jiezi

lua-web快速开发指南3-初识httpd库路由

本章假设您已经知道httpd server如何快速搭建, 并且知道cf的启动流程与运行流程, 知晓httpd如何创建与启动. 回顾上一章节-- script/main.lualocal httpd = require "httpd"local app = httpd:new("app")app:static("static", 30)app:listen("0.0.0.0", 8080)app:run()我们利用httpd内置库快速实现了一套httpd静态文件server, 其中包括静态文件目录指定与端口设置. 并且在启动server后可以在看到测试页面. 什么是"路由"与"路由表"?Web路由用于描述资源到处理函数之间的一个映射关系. Web路由表用于描述当前作用域下所有路由的一个集合. 如下所示: /userlogin -> function userlogin(content) ... end/userinfo -> function userinfo(content) ... end对于一个服务端开发者来说! 当接受到客户端的HTTP请求时, 服务端会将请求URL中的PATH进行分割, 然后开始寻找的PATH映射对应的回调处理函数. 当URL映射的回调处理函数被找到时, 将会为其注入整个http上下文并且根据处理函数的行为将返回值展现给资源访问者. 这就是基本的路由雏形. cf中的各种路由cf的httpd库利用这种机制, 为开发者提供了一整套完整的路由注册方法, 其中包括: 静态文件路由、API接口路由、USE页面路由、WebSocket路由. 静态文件路由我们在上一章节已经看到过, 其本质是根据需要读取指定文件而存在的. 这种路由一般有库编写者或者框架编写者实现. 而API接口路由、USE页面路由、Websocket路由则一般由开发自行指定, 这些路由一般都用来处理对应的业务逻辑. 下面我们就开始学习如何在cf中注册路由. 注册API与USE路由1. API路由API接口路由用于快速构建前、后端分离的web开发场景. 它提供了基于http协议提供了基础的前、后端通讯的解决方案, 是目前位置Web领域最为常见的开发模式. 而作为前、后端数据沟通的桥梁自然需要指定指定数据交互类型. 目前为止, API路由的content-type为"application/json", 数据交互格式仅支持: json. httpd库为开发者提供了app:api方法用来注册API路由, 第一个参数是一个字符串类型的资源路径, 第二个参数则是回调处理方法; 现在让我们在main.lua中, 添加我们刚刚学习到的api路由: -- main.lualocal json = require "json"app:api('/userinfo', function(content) return json.encode({ code = 200, user = { name = "CandyMi", age = 29, sex = "男", } })end)然后打开浏览器, 输入http://localhost:8080/userinfo. 我们就可以看到我们输出的接口数据了. ...

June 14, 2019 · 1 min · jiezi

掌握-Javascript-类型转换隐式转换救救孩子

在上一篇中我们聊过了 JS 类型转换的规则和我发现的一些常见书籍中关于类型转换的一些小错误,当碰到显示类型转换的时候大家可以按照这些规则去拆解出答案。但 JS 中存在一些很隐晦的隐式类型转换,这一篇就来谈下我对隐式类型转换的一些总结。 关于 JS 类型转换规则请看上一篇的内容:掌握 JS 类型转换:从规则开始什么是隐式类型转换呢?顾名思义就是有时候你感觉不到这是类型转换但是实际上类型转换已经发生了。所以这个 "隐式" 取决于我们的理解和经验,如果你看不出来那就是隐式的。 下面按照我自己对于隐式转换的分类来逐个聊聊吧。 一元操作符 +、-var a = '123';var b = +a;console.log(b); // 123先来看看 + 或 - 在一个类型值前面,这里会执行 ToNumber 类型转换。如果是 - 在前面的话,还会将结果的符号取反,如:-"123" 的结果是 -123。并且如果原类型是对象的话也是遵循 ToNumber 的转换规则,大家可以自己试试,这里就不再举多余的例子了。 二元操作符接下来我们来看一下二元操作符相关的隐式转换,比如:+、-、&&、||、==等等这些。 相减 a - bvar a = '123';var b = true;console.log(a - b); // 122当执行减法操作时,两个值都会先执行 ToNumber 转换,所以这个是比较简单的,当类型是对象时也是遵循同样的规则。 相加 a + bconsole.log('123' + 4); // "1234"console.log(123 + true); // 124相加的情况有点复杂,但隐式转换的规则大家可以按照我总结的来记: 如果 + 的操作数中有对象,则执行 ToPrimitive 并且 hint 是 Number如果 + 中有一个操作数是字符串(或通过第一步得到字符串),则执行字符串拼接(另一个操作数执行 ToString 转换),否则执行 ToNumber 转换后相加这个相加操作的隐式转换规则看似有点麻烦,其实解析后还是很明确的。 ...

June 13, 2019 · 3 min · jiezi

DNS域名解析

整个过程大体描述如下,其中前两个步骤是在本机完成的,后8个步骤涉及到真正的域名解析服务器: 浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,这个解析过程就结束。浏览器缓存域名也是有限制的,不仅浏览器缓存大小有限制,而且缓存的时间也有限制,通常情况下为几分钟到几小时不等,域名被缓存的时间限制可以通过TTL属性来设置。这个缓存时间太长和太短都不太好,如果时间太长,一旦域名被解析到的IP有变化,会导致被客户端缓存的域名无法解析到变化后的IP地址,以致该域名不能正常解析,这段时间内有一部分用户无法访问网站。如果设置时间太短,会导致用户每次访问网站都要重新解析一次域名。如果用户浏览器缓存中没有数据,浏览器会查找操作系统缓存中是否有这个域名对应的DNS解析结果。其实操作系统也有一个域名解析的过程,在Windows中可以通过C:WindowsSystem32driversetchosts文件来设置,在Linux中可以通过/etc/hosts文件来设置,用户可以将任何域名解析到任何能够访问的IP地址。例如,我们在测试时可以将一个域名解析到一台测试服务器上,这样不用修改任何代码就能测试到单独服务器上的代码的业务逻辑是否正确。正是因为有这种本地DNS解析的规程,所以有黑客就可能通过修改用户的域名来把特定的域名解析到他指定的IP地址上,导致这些域名被劫持。前两个过程无法解析时,就要用到我们网络配置中的"DNS服务器地址"了。操作系统会把这个域名发送给这个LDNS,也就是本地区的域名服务器。这个DNS通常都提供给用户本地互联网接入的一个DNS解析服务,例如用户是在学校接入互联网,那么用户的DNS服务器肯定在学校;如果用户是在小区接入互联网,那么用户的DNS就是再提供接入互联网的应用提供商,即电信或联通,也就是通常说的SPA,那么这个DNS通常也会在用户所在城市的某个角落,不会很远。Windows环境下通过命令行输入ipconfig,Linux环境下通过cat /etc/resolv.conf就可以查询配置的DNS服务器了。这个专门的域名解析服务器性能都会很好,它们一般都会缓存域名解析结果,当然缓存时间是受到域名的失效时间控制的。大约80%的域名解析到这里就结束了,所以LDNS主要承担了域名的解析工作。如果LDNS仍然没有命中,就直接到Root Server域名服务器请求解析根域名服务器返回给本地域名服务器一个所查询的主域名服务器(gTLD Server)地址。gTLD是国际顶级域名服务器,如.com、.cn、.org等,全球只有13台左右本地域名服务器LDNS再向上一步返回的gTLD服务器发送请求接受请求的gTLD服务器查找并返回此域名对应的Name Server域名服务器的地址,这个Name Server通常就是用户注册的域名服务器,例如用户在某个域名服务提供商申请的域名,那么这个域名解析任务就由这个域名提供商的服务器来完成Name Server域名服务器会查询存储的域名和IP的映射关系表,在正常情况下都根据域名得到目标IP地址,连同一个TTL值返回给DNS Server域名服务器返回该域名对应的IP和TTL值,LDNS会缓存这个域名和IP的对应关系,缓存时间由TTL值控制把解析的结果返回给用户,用户根据TTL值缓存在本地系统缓存中,域名解析过程结束在实际的DNS解析过程中,可能还不止这10步,如Name Server可能有很多级,或者有一个GTM来负载均衡控制,这都有可能会影响域名解析过程。

June 11, 2019 · 1 min · jiezi

第4天在Flask应用中使用数据库FlaskSQLAlchemy

原文: http://www.catonlinepy.tech/声明:原创不易,未经许可,不得转载 1. 你将学会什么接第3天表单的使用课程,今天的课程主要涉及到与数据库相关的两个插件的使用,一个是Flask_SQLAlchemy,另外一个是Flask_Migrate。通过今天的学习,你将学会如何对数据库进行基本的操作,以及如何完成数据库的迁移。教程中的代码都会托管到github上,猫姐不厌其烦地强调,在学习本课内容时一定要自己尝试手敲代码,遇到问题再到github上查看代码,如果实在不知道如何解决,可以在日志下方留言。 2.使用Flask_SQLAlchemy管理数据库2.1 Flask_SQLAlchemy的安装对于web后台开发工作,必须要掌握的一项技能便是对数据库的CRUD(create, read, update, delete)操作,如果开发过程中直接使用原生的sql语句对数据库进行操作,将是非常痛苦的事件(毕竟sql语句有很多反人类的设计)。Flask_SQLAlchemy插件将开发人员从这个泥潭中解救出来了,我们只需要使用Python的类就能轻松的完成对表的增删改查操作,并且该插件还支持多种数据库类型,如MySQL、PostgreSQL、和SQLite等。 在进入正式学习之前,我们照旧要建立今天的项目目录,如下: # 进入到虚拟环境目录,激活虚拟环境maojie@Thinkpad:~/flask-plan/$ source miao_venv/bin/activate# 到flask-course-primary目录下创建第四天的课程day4目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ mkdir day4# 进入day4目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ cd day4# 新建database_demo目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day4$ mkdir database_demo# 进入到database_demo目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day4$ cd database_demo/# 在database_demo目录中新建__init__.py文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day4/database_demo$ touch __init__.py# 在database_demo包中新建routes.py路由文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day4/database_demo$ touch routes.py# 在day4目录下新建run.py文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day4/$ touch run.py安装Flask_SQLAlchemy插件还是使用pip命令,如下:# 注意一定要在虚拟环境中(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day4$ pip install Flask_SQLAlchemy2.2 配置Flask_SQLAlchemy我们的教程中使用的数据库是SQLite(Linux),主要原因是SQLite足够简单,不需要进行任何配置便可使用,十分适用于入门。下面在__init__.py文件中配置数据库,如下所示: # 在__init__.py文件中的内容from flask import Flask# 从flask_sqlalchemy导入SQLAlchemy类from flask_sqlalchemy import SQLAlchemyimport os# 通过Flask创建一个app实例app = Flask(__name__)basedir = os.path.abspath(os.path.dirname(__file__))# Flask_SQLAlchemy插件从SQLALCHEMY_DATABASE_URI配置的变量中获取应用的数据库位置app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'database.db')# 通过SQLAlchemy创建db实例,表示程序使用的数据库,并且db能够使用Flask_SQLAlchemy的所有功能db = SQLAlchemy(app)2.3 构建数据库模型数据模型通常用来定义数据库中的表及表中的字段。下面代码中的User类和Post类就代表了数据库中的两张表(官方叫法是数据模型)。后面我们会看到,通过这里定义的两个Python类,我们就可以非常容易的完成表的增删改查,以下是routes.py文件中的内容。 ...

June 11, 2019 · 4 min · jiezi

每个Web开发者都该了解的12条命令行

转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。原文出处:https://tutorialzine.com/2017... 在开发者的弹药箱里,命令行是最具生产力的工具之一。掌握它们可以给你的工作流程带来非常积极的影响。因为,许多日常任务都可以用一条命令然后按回车来解决。 在本文中,我们为你准备了一系列常用命令,帮你充分利用你的终端。这其中有些命令是系统内置的,另外的一些则是需要另外安装的免费工具,不过这些免费工具是经历了时间的考验,所以你可以分分钟将他们安装完成。 curlcurl是一个发送请求的命令行工具。可使用HTTP(s)、FTP,以及一些你可能从未听过的协议发送请求。它可以下载文件,检查响应头,自由地访问远程数据。 在web开发中,curl常用于测试连接和RESTful APIs。 # 获取一个URL的HTTP HEADERcurl -I http://google.comHTTP/1.1 302 FoundCache-Control: privateContent-Type: text/html; charset=UTF-8Referrer-Policy: no-referrerLocation: http://www.google.com/?gfe_rd=cr&ei=0fCKWe6HCZTd8AfCoIWYBQContent-Length: 258Date: Wed, 09 Aug 2017 11:24:01 GMT # 向远程API发出GET请求curl http://numbersapi.com/random/trivia29 is the number of days it takes Saturn to orbit the Sun.curl命令可以远比上面的情况复杂。它有一大堆的选项来控制请求头、cookies、权限验证等等。你可以在这本相当棒的免费书Everything curl中读到更多。 treetree是一个小巧的命令行,用于可视化地展示目录里的文件结构。它递归地执行,检查嵌套的每一个层级,为所有内容绘制出格式化的树形结构。你可以用它快速浏览文件结构,定位到所需的文件。 tree.├── css│ ├── bootstrap.css│ ├── bootstrap.min.css├── fonts│ ├── glyphicons-halflings-regular.eot│ ├── glyphicons-halflings-regular.svg│ ├── glyphicons-halflings-regular.ttf│ ├── glyphicons-halflings-regular.woff│ └── glyphicons-halflings-regular.woff2└── js ├── bootstrap.js └── bootstrap.min.js也有类似正则匹配的选项,用于过滤结果。 tree -P '*.min.*'.├── css│ ├── bootstrap.min.css├── fonts└── js └── bootstrap.min.jstmux根据维基百科所说,tmux是一个终端复用器,翻译为人话就是说,它是一个把多给终端连接为一个终端会话的工具。 ...

June 4, 2019 · 1 min · jiezi

第三天在Flask-Web中如何使用表单

原文:http://www.catonlinepy.tech/声明:原创不易,未经许可,不得转载 1. 你将学会什么通过第三天的学习内容,你将对表单有所了解。知道使用插件来处理应用中的表单,以后遇到表单能够更熟练的使用Flask_WTF插件,并完美的将应用运行起来。今天的学习内容涉及到的代码都会托管到github上,在学习本课内容时,一定要自己尝试手敲代码,遇到问题再到猫姐的github上去查看代码,如果实在不知道如何去解决问题,可以在日志下面留言说明具体情况。 2. 表单的插件简介WTForms作为处理Web表单的插件,是一款支持多个web框架的form组件。Flask-WTF插件对其类WTForms进行封装后以便它能够与Flask完美的结合。在第三天的内容中,我们将引入第一个Flask插件,后续也会对其它的插件进行引入。要知道,插件用得好,可以使web的开发的过程快到飞起。 大家需知道,所有Flask插件都是属于Python的三方包,因此都可以使用pip来进行安装。照旧,我们先进入miao_venv的虚拟环境目录中,将虚拟环境进行激活,然后安装Flask_WTF插件。安装步骤如下: # 进入到虚拟环境目录,激活虚拟环境maojie@Thinkpad:~/flask-plan/$ source miao_venv/bin/activate# 再安装Flask_WTF插件(miao_venv) maojie@Thinkpad:~/flask-plan$ pip install Flask_WTF3. 表单插件的使用今天的代码组织结构如下: # 使用tree命令查看(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3$ tree.├── form_demo│ ├── __init__.py│ ├── routes.py│ └── templates│ ├── form.html│ └── layout.html└── run.py现在猫姐来创建今天的课程目录,步骤如下: # 在flask-course-primary目录下创建第三天的课程day3目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ mkdir day3# 进入day3目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ cd day3# 新建form_demo(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3$ mkdir form_demo# 进入到form_demo包(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3$ cd form_demo/# 在form_demo包中新建__init__.py文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo$ touch __init__.py# 在form_demo新建routes.py路由文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/form_demo$ touch routes.py# 在day3目录下新建run.py文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day3/$ touch run.py上面将课程目录创建好后,现在我们开始表单的创建。在创建表单之前,猫姐先在__init__.py文件中对Flask_WTF进行配置,如下所示: # __init__.py文件中的内容from flask import Flask # 从flask包中导入Flask类app = Flask(__name__) # 通过Flask类创建一个app实例app.config['SECRET_KEY'] = 'miaojie is great!' # 对Flask_WTF进行配置from form_demo import routes# 解释:对Flask_WTF进行配置主要是为了防跨站请求伪造保护。因为在默认的情况下,Flask_WTF能够保护所有的表单免受跨站请求伪造的攻击(Cross-Site Request Forgery,CSRF),但是在特殊情况下,一些恶意网站会把请求发送到被攻击者已登录的其它网站时就会发生CSRF攻击。为了实现CSRF的保护,Flask_WTF需要程序设置一个密钥,然后它使用密钥去生成一个加密令牌,再用令牌验证请求中的表单数据的真伪。(这里不懂也没事,问题不大!)下面开始一个简单的Web 表单,猫姐开始利用Flask_WTF插件来创建Web表单,表单内容写入到routes.py文件中,如下所示: ...

June 3, 2019 · 2 min · jiezi

第二天在Flask-Web应用中使用模板

原文:http://www.catonlinepy.tech/声明:原创不易,未经许可,不得转载 1. 你将学会什么通过学习第二天的内容,你将会对模板有所了解,并且知道为什么要使用模板,以及使用模板有什么好处。这一天的学习内容涉及到的代码都会托管到github上,猫姐再次强调,在学习本课内容时一定要自己尝试手敲代码,遇到问题再到github上去查看代码,如果实在不知道如何去解决问题,可以在日志下面留言说明具体情况。 2. 什么是模板视图函数主要有两个作用,一个是处理业务逻辑,另一个是给用户返回相关内容。在大型应用中,如果把业务逻辑和返回响应内容放在一起的话,这样会增加代码的复杂度,并且也不好维护。所以模板它就承担了视图函数的另外一个作用:返回响应内容。这样就可以实现业务逻辑和响应内容的分离,将所有的html代码都存放到模板中,而视图函数中只需要专心处理好业务逻辑即可。 下面猫姐通过一个简单的例子来展示为什么要引入模板,以下是这个例子的代码组织结构: (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ tree.├── day2│ ├── run.py│ └── template_demo│ ├── __init__.py│ └── routes.py└── README.md首先来建立第二天的代码目录,与第一天的课程类似,先激活虚拟环境miao_venv,创建方法都是一样的: # 进入到虚拟环境目录,激活虚拟环境maojie@Thinkpad:~/flask-plan/$source miao_venv/bin/activate# 再到flask-course-primary目录下创建第二天的课程day2目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ mkdir day2# 进入day2目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ cd day2# 新建template_demo目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day2$ mkdir template_demo# 进入到template_demo目录(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day2$ cd template_demo/# 在template_demo目录中新建__init__.py文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day2/template_demo$ touch __init__.py# 在template_demo包中新建routes.py路由文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day2/template_demo$ touch routes.py# 在day2目录下新建run.py文件(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day2/$ touch run.py在template_demo包的__init__.py中输入如下代码: # 以下是__init__.py文件中的代码,我们逐行进行解释from flask import Flask # 从flask包中导入Flask类app = Flask(__name__) # 通过Flask类创建一个app实例from template_demo import routes # 从template_demo包中导入routes文件里的所有代码在routes.py文件中输入如下代码,包括响应内容中的HTML代码: ...

June 1, 2019 · 2 min · jiezi

如何查找SAP-Fiori-launchpad-Designer的准确路径即url地址

比如我们知道在SPRO里下面这个路径的customizing activity里打开Fiori Launchpad designer: SAP Netweaver->UI technologies->SAP Fiori->Configure Launchpad content->Adding Apps to SAP Fiori Launchpad->Configure Target Mappings and Tiles->SAP Fiori Launchpad Designer, 点右键,选择Display technical info: 找到这个activity绑定的事务码为/UI2/FLPD_CUST: SE93里查到这个事务码绑定的report名称为/UI2/START_URL: 这个报表执行后的界面: Fiori Launchpad designer的准确路径可以通过调试获得,存储于变量gv_url里: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 1, 2019 · 1 min · jiezi

第一天你的第一个Flask-Web应用你好喵星在线

原文:http:catonlinepy.tech/ 声明:原创不易,未经许可,不得转载 1. 你将学会什么通过学习第一天的内容,你将学会如何创建你的第一个flask web应用,并且在自己的电脑上运行它。这是第一天的学习内容,所有内容的代码都将托管在github上,猫姐强烈建议各位同学在学习本课内容时,先跟着教程自己尝试手敲代码,遇到问题后再去查看猫姐github上的代码,如果问题实在不知道如何解决,可以在日志下面留言具体说明情况。 2. 运行环境准备在开始写代码前,我们需要准备好flask web应用的运行环境,因此我们需要做3件事:1. 安装python;2. 安装虚拟开发环境;3. 安装flask框架。下面我们详细介绍一下这3个步骤的操作过程: 2.1 第一步:安装python首先是安装Python环境,下面提供了适用于不同操作系统的python安装包链接,大家根据自己的情况安装即可: python for windowspython for Linux/Unix(一般已经安装了python解释器)python for Mac OS X安装python的过程就不详细说明,相信大家都不会有什么问题,猫姐在这里强烈推荐各位同学使用Linux发行版进行学习,虽然开始时可能会遇到一些操作上的问题,但是在使用Linux发行版操作系统时,你会学到更多的其它软件开发的知识,毕竟Linux才是专門为软件开发人员准备的系统。猫姐使用的操作系统是ubuntu18.04,大家也可以使用deepin或centos等其它Linux发行版操作系统。查看python环境是否安装成功的命令如下(打开命令行ctrl+alt+T,输入python): $ pythonPython 2.7.15rc1 (default, Nov 12 2018, 14:31:15) [GCC 7.3.0] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> 如上我们会看到三个大于号(>>>),这表示我们已经进入python交互环境中。我们可以在python交互环境中输入各种python语句,进行python基础知识的学习。如果想要退出python交互环境,只需可以输入exit()即可;在Linux或Mac OS X操作系统中,通过ctrl+d快捷键也可以退出python交互环境。 2.2 第二步:安装虚拟开发环境对于不了解python的同学来说,可能不知道虚拟环境是什么东西,不用担心!如果你能坚持学习完成后面几天的内容,你将会对虚拟环境有更深入的理解。这里猫姐简单解释一下,虚拟开发环境的主要作用是为了将web开发项目所用的各种库与操作系统自带的python库隔离开来,这样做的好处是开发环境与系统环境隔离,环境之间不会相互影响,特别是对于多人协作的大型项目的开发,建立虚拟环境是非常有必要的。猫姐说了这么多“废话”,其实建立虚拟开发环境的过程很简单,主要完成下面几个步骤(猫姐强烈推荐同学们使用python3进行学习): # 安装python3的虚拟环境包管理模块$ sudo apt-get install python3-venv# 使用下面命令,创建一个虚拟开发环境$ python3 -m venv <虚拟环境的名字># 激活刚才建立的虚拟开发环境,(这里我们创建一个名为miao_venv的虚拟环境)$ python3 -m venv miao_venv$ source maio_venv/bin/activate2.3 第三步:安装Flask web框架前面两步,我们已经安装了python开发环境、创建了虚拟开发环境,并激活了虚拟开发环境。大家需要记住,后面我们所有python包的安装都要在虚拟开发环境中进行。如下,当我们激活虚拟开发环境后,会看到命令行前面出现一个括号,括号中的内容为虚拟开发环境的名字: ...

May 31, 2019 · 2 min · jiezi

Gatsby极速入门添加博客内容页4

1.查数据注意,这里跟前面不一样了,我用gatsby-node.js这个文件去提供数据,没有什么为什么,规定,照做就好。 const path = require("path");exports.createPages = ({ actions, graphql }) => { const { createPage } = actions const blogPostTemplate = path.resolve(`src/templates/blogPost.js`) return graphql(` { allMarkdownRemark{ edges { node { frontmatter { path, title, tags } } } } } `).then(result => { if (result.errors) { return Promise.reject(result.errors) } const posts = result.data.allMarkdownRemark.edges; createTagPages(createPage, posts); posts.forEach(({ node }, index) => { const path = node.frontmatter.path; const title = node.frontmatter.title; createPage({ title, path, component: blogPostTemplate, context: { pathSlug: path }, }) }) })}很清晰明显,这里就说一点我传递了一个参数,pathSlug到内容页。 ...

May 30, 2019 · 1 min · jiezi

如何从请求传输渲染3个方面提升Web前端性能

什么是WEB前端呢?就是用户电脑的浏览器所做的一切事情。我们来看看用户访问网站,浏览器都做了哪些事情: 输入网址 –> 解析域名 -> 请求页面 -> 解析页面并发送页面中的资源请求 -> 渲染资源 -> 输出页面 -> 监听用户操作 -> 重新渲染。 通过上面的路径可以看出浏览器分为请求、传输、渲染三部分来实现用户的访问,本文就从这三个部分来浅析如何提升WEB前端性能。 一、请求浏览器为了减少请求传输,实现了自己的缓存机制。浏览器缓存就是把一个已经请求过的Web资源拷贝一份副本存储在浏览器中,当再次请求相同的URL时,先去查看缓存,如果有本地缓存,浏览器缓存机制会根据验证机制(Etag)和过期机制(Last-Modified)进行判断是使用缓存,还是从服务器传输资源文件。具体流程如下图所示: 浏览器的请求有些是并发的,有些是阻塞的,比如:图片、CSS、接口的请求是并发;JS文件是阻塞的。请求JS的时候,浏览器会中断渲染进程,等待JS文件加载解析完毕,再重新渲染。所以要把JS文件放在页面的最后。 JS也可以通过两种方式由阻塞改成并行:一种是通过创建script标签,插入DOM中;另一种是在Script标签中增加async属性。 每种浏览器对同一域名并发的数量有限制,IE6/7是2,IE9是10,其他常见的浏览器是6,所以减少资源请求数量和使用多域名配置资源文件,能大大提高网站性能。 减少资源请求数量的方法,大致有以下几种: 1、通过打包工具,合并资源,减少资源数量。就是开发版本是很多个资源文件,部署的时候,按类合并成几个文件来输出。在实现模块管理的同时,实现统一输出。 2、CSS中,使用css sprite减少图片请求数量。 3、通过延迟加载技术,在用户无感知的情况下请求资源。 4、通过服务器配置,实现一次请求,返回多个资源文件,如淘宝CDN那样。 除了减少请求数量,也可以使用CDN镜像,来减少网络节点,实现快速响应。使用了CDN的请求,会根据用户所处的地理位置,找寻最近的CDN节点,如果请求是新的,则从资源服务器拷贝到节点,然后再返回给客户端。如果请求已经存在,则直接从节点返回客户端。 通过上面我们了解的缓存机制,如果我们部署上线的时候,是需要刷新缓存的。普通缓存通过强刷就能改过来,而CDN缓存则需要通过改变URL来实现。同时我们不可能要求用户按着Ctrl来刷新,所以通过打包工具,在部署的时候,统一更改URL是最有效的方式。而不常变更的库文件,比如echart、jquery,则不建议更改。 二、传输从服务器往客户端传输,可以开启gzip压缩来提高传输效率。 Gzip有从1-10的十个等级。越高压缩的越小,但压缩使用的服务器硬件资源就越多。根据实践,等级为5的时候最均衡,此时压缩效果是100k可以压缩成20k。 三、渲染浏览器在加载了html后,就会一边解析,一边根据解析出来的结果进行资源请求,并生成DOM树。而加载完毕的CSS,则被渲染引擎根据生成好的DOM树,来生成渲染树。等所有资源解析完毕计算好layout后,向浏览器界面绘制。随着用户操作,JS会修改DOM节点或样式,重新绘制和重新排列。重新绘制指的是绘制DOM节点对应的渲染节点,重新排列是指重新计算这些节点在浏览器界面的位置。很显然,重排是非常耗性能的。我们要做的是减少重排的次数。 生成DOM树的时候,我们可以通过减少DOM节点来优化性能。最初都是用table布局,节点深度和数量相当复杂,性能很差。同样CSS作为层叠样式表,层级也不可太深,不然遍历的成本很高。另外CSS的expression属性相当耗性能,能不用则不用。动画效果能用CSS写的就不用JS写,渲染引擎不一样,性能损耗也不一样。 上面说的是解析渲染的过程,我们再接着说说用户交互操作的过程。用户操作就会导致重绘和重排,重排一定会引起重绘,而重绘不一定会引起重排。到底怎样会引起重排呢?简单的定义,DOM结构的变化,以及DOM样式中几何属性的变化,就会导致重排。几何属性顾名思义,就是宽、高、边框、外补丁、内补丁等俗称盒模型的属性。同时还有offset之类的边距属性。 重排是最耗能的,减少重排的方法有: 1、如果需要多次改变DOM,则先在内存中改变,最后一次性的插入到DOM中。 2、同上一条,如果多次改变样式,合成一条,再插入DOM中。 3、由于position的值为absoute和fixed时候,是脱离文档流的,操作此类DOM节点,不会引起整页重排。所以动画元素设置position使其脱离文档流。 4、当DOM节点的display等于none的时候,是不会存在于渲染树的,所以如果有比较复杂的操作,先使其display等于none,等待所有操作完毕后,再将display设成block,这样就只重排两次。 5、获取会导致重排的属性值时,存入变量,再次使用时就不会再次重排。获取这些属性会导致重排:offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 以上就是浏览器如何把资源变成肉眼所见的页面的,除了上述根据浏览器流程而总结出来的性能优化,我们还需要看看javascript作为程序,需要的优化。先来看看javascript的垃圾回收机制。 Javascript的引擎会在固定的时间间隔,将不再使用的局部变量注销掉,释放其所占的内存。而闭包的存在,将使引用一直存在,无法被释放掉。全局变量的生命周期直至浏览器卸载页面才会结束。所以一般来讲,内存溢出就是由于全局变量的不释放和闭包引起。为了防止内存溢出,我们可以做的方法有: 1、业务代码放在匿名立即执行函数里面,执行完毕会立即释放掉。 2、少用全局变量,同时用完的变量手动注销掉。 3、使用回调来代替闭包访问内部属性 4、当不可避免使用闭包时,慎重的对待其中的细节。不用的时候注销掉。 5、通过浏览器自带的工具profiles,来检查内存活动情况。如果是波浪型的,说明正常。如果是倾斜式渐进上涨的,说明有内存不会被释放,需要检查相应的函数。 最后再说一点,函数里返回异步取的值,经常有人这么: Var getList = function(){ $.ajax().then(function(data){ Return data;}) };Var users = getList();毫无疑问,由于函数内的返回是异步的,所以返回只能是undefined,而不是想要的data。于是为了实现返回data,就把ajax的async属性设置成了false,由异步改为同步,来获取到data。然而最大的问题来了,同步是会中断渲染进程的,也就是请求返回的等待中,整个页面是卡死的,用户操作也不会有响应。这个问题真正的解决方案是返回promise对象,而不是把异步改成同步。 作者:马宗泽 拓展阅读:数据中台:宜信敏捷数据中台建设实践|分享实录

May 30, 2019 · 1 min · jiezi

Go-htmltemplate-模板的使用实例

从字符串载入模板我们可以定义模板字符串,然后载入并解析渲染: template.New(tplName string).Parse(tpl string) // 从字符串模板构建tplStr := ` {{ .Name }} {{ .Age }}`// if parse failed Must will render a panic errortpl := template.Must(template.New("tplName").Parse(tplStr))tpl.Execute(os.Stdout, map[string]interface{}{Name: "big_cat", Age: 29})从文件载入模板模板语法模板文件,建议为每个模板文件显式的定义模板名称:{{ define "tplName" }},否则会因模板对象名与模板名不一致,无法解析(条件分支很多,不如按一种标准写法实现),另展示一些基本的模板语法。 使用 {{ define "tplName" }} 定义模板名使用 {{ template "tplName" . }}引入其他模板使用 . 访问当前数据域:比如range里使用.访问的其实是循环项的数据域使用 $. 访问绝对顶层数据域views/header.html{{ define "header" }}<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>{{ .PageTitle }}</title></head>{{ end }}views/footer.html{{ define "footer" }}</html>{{ end }}views/index/index.html{{ define "index/index" }} {{/*引用其他模板 注意后面的 . */}} {{ template "header" . }} <body> <div> hello, {{ .Name }}, age {{ .Age }} </div> </body> {{ template "footer" . }}{{ end }}views/news/index.html{{ define "news/index" }} {{ template "header" . }} <body> {{/* 页面变量定义 */}} {{ $pageTitle := "news title" }} {{ $pageTitleLen := len $pageTitle }} {{/* 长度 > 4 才输出 eq ne gt lt ge le */}} {{ if gt $pageTitleLen 4 }} <h4>{{ $pageTitle }}</h4> {{ end }} {{ $c1 := gt 4 3}} {{ $c2 := lt 2 3 }} {{/*and or not 条件必须为标量值 不能是逻辑表达式 如果需要逻辑表达式请先求值*/}} {{ if and $c1 $c2 }} <h4>1 == 1 3 > 2 4 < 5</h4> {{ end }} <div> <ul> {{ range .List }} {{ $title := .Title }} {{/* .Title 上下文变量调用 func param1 param2 方法/函数调用 $.根节点变量调用 */}} <li>{{ $title }} -- {{ .CreatedAt.Format "2006-01-02 15:04:05" }} -- Author {{ $.Author }}</li> {{end}} </ul> {{/* !empty Total 才输出*/}} {{ with .Total }} <div>总数:{{ . }}</div> {{ end }} </div> </body> {{ template "footer" . }}{{ end }}template.ParseFiles手动定义需要载入的模板文件,解析后制定需要渲染的模板名news/index。 ...

May 28, 2019 · 3 min · jiezi

威胁快报新兴挖矿团伙借助shodan作恶非web应用安全再鸣警钟

近日,阿里云安全发现了一个使用未授权访问漏洞部署恶意Docker镜像进行挖矿的僵尸网络团伙。我们给这一团伙取名为Xulu,因为该团伙使用这个字符串作为挖矿时的用户名。 Xulu并不是第一个攻击Docker的恶意挖矿团伙,但它不同于其他僵尸网络。Xulu感染一台服务器后,并不对外进行大规模扫描,而是使用OSINT技术,即利用开源情报,动态地从shodan网站获得可能的“猎物”ip列表。 此外,Xulu僵尸网络将自己的服务器放在Tor洋葱网络中,这使得对幕后黑手的追溯变得更加困难。 会挖矿的恶意Docker镜像Docker容器是一个开源的应用容器引擎,可以让开发者打包他们的应用及依赖包到一个轻量级、可移植的容器中,从而在不同环境中可靠运行。 近年来随着微服务的流行,越来越多的企业在部署应用时使用容器,然而在这一过程中安全往往没有得到应有的重视,导致Docker容器在多起事件中成为网络攻击的靶子。 在本次Xulu僵尸网络事件中,我们注意到沦陷服务器上都被创建了镜像名为zoolu2/auto的恶意容器。 这些恶意容器中运行着如下进程 其中的挖矿进程很容易分辨: /toolbin/darwin -o us-east.cryptonight-hub.miningpoolhub.com:20580 -u xulu.autodeploy -p x --currency monero -i 0 -c conf.txt -r尽管miningpoolhub.com是公开矿池,但由于它不提供每个用户的历史收益数据,我们无从得知攻击者从恶意挖矿中总共赚了多少钱。 僵尸网络的传播和持久化Xulu僵尸网络进行自身的传播和持久化的过程中,使用了OSINT技术并借助了洋葱网络。 首先,该僵尸网络的控制服务器地址是http://wg6kw72fqds5n2q2x6qjejenrskg6i3dywe7xrcselhbeiikoxfrmnqd.onion。".onion"后缀表明这是一个必须通过洋葱匿名网络访问的“洋葱服务”(又名“隐藏服务”)。 该僵尸网络以/toolbin/shodaemon作为守护进程: 不难看出该脚本下载了http://wg6kw72fqds5n2q2x6qjejenrskg6i3dywe7xrcselhbeiikoxfrmnqd.onion/shodan.txt,与本地硬编码的/toolbin/hcode.txt文件内容一起存入search.txt 运行/toolbin/shodan,读取search.txt的列表并对shodan发送如上图所示的查询。 这些查询会返回互联网上一系列开放了Docker服务(2375端口)的主机ip。尽管这些主机并非每个都存在漏洞,但攻击者仍然通过使用shodan的信息,避免了大规模扫描的进行。 在获取了使用Docker服务的主机列表并去除重复ip后,已沦陷的主机会向表中ip发送docker run命令,其中未授权访问漏洞的Docker服务将被部署"zoolu2/auto"恶意镜像,从而完成蠕虫的传播。 此外,Xulu僵尸网络还会每30分钟下载并执行从http://wg6kw72fqds5n2q2x6qjejenrskg6i3dywe7xrcselhbeiikoxfrmnqd.onion/bnet1.txt下载的脚本,从而保持自身在受害主机上的活跃。 受害规模和安全建议在docker hub官网我们可以看到,前文提到的"zoolu2/auto"已被下载超过1万次: 并且僵尸网络作者似乎仍在积极开发变种: 为了避免您成为此种恶意入侵和挖矿事件的受害者,阿里云安全为您提供如下安全建议:不要将对内使用的服务(如Docker)开放在互联网上,应使用ACL或复杂密码等措施来保证仅有受到信任的用户才可以访问这些服务。因为基于洋葱网络的“隐藏服务”已被用于多个僵尸网络的传播,不常使用洋葱网络服务的用户可以使用如下命令对其进行屏蔽:echo -e "n0.0.0.0 .onion" >> /etc/hosts我们推荐您使用阿里云下一代防火墙,因为它在阻止、拦截此类需要外联的攻击时十分有效。用户将在AI技术的帮助下,免于恶意挖矿事件的困扰我们同样推荐阿里云安全管家服务。该服务的用户可以就碰到的问题随时咨询安全专家。安全专家还可以帮助用户进行安全加固、事件溯源、蠕虫清理等IOCcontrol server: http://wg6kw72fqds5n2q2x6qjejenrskg6i3dywe7xrcselhbeiikoxfrmnqd.onion url: http://wg6kw72fqds5n2q2x6qjejenrskg6i3dywe7xrcselhbeiikoxfrmnqd.onion/bnet1.txthttp://wg6kw72fqds5n2q2x6qjejenrskg6i3dywe7xrcselhbeiikoxfrmnqd.onion/shodan.txt pool:us-east.cryptonight-hub.miningpoolhub.com:20580 md5: c29dfe75862b6aed91bec4ffc7b20b9c Referencehttps://www.alibabacloud.com/blog/dockerkiller-threat-analysis-first-instance-of-batch-attack-and-exploitation-of-docker-services_593947https://www.docker.com/resources/what-container 本文作者:云安全专家阅读原文 本文为云栖社区原创内容,未经允许不得转载。

May 27, 2019 · 1 min · jiezi

Apache-httpclient的execute方法调试

因为工作需要,想研究一下execute执行的逻辑。 在这一行调用execute: response = getHttpClient().execute(get);getHttpClient的实现: private HttpClient getHttpClient() { if (this.m_httpClient == null) { this.m_httpClient = HttpClientBuilder.create().build(); } return this.m_httpClient; }我在代码里声明的HttpClient只是一个接口, 实现类是InternalHttpClient。 首先根据传入的请求决定出目标-target host 投递到RedirectExec执行。 后者又投递到RetryExec执行。 收到307重定向: redirectsEnabled标志位为true: 再看当前的请求确实被redirect了吗? original url: 我的后台服务器返回的307,落到了分支HttpStatus.SC_TEMPORARY_REDIRECT处: 看来Apache的库认为只有HEAD和GET才能被redirect: 重定向最大次数:50 准备重试了: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

May 25, 2019 · 1 min · jiezi

为什么建议你常阅读源码

作者:谢伟授权 LeanCloud 转载 我叫谢伟,是一名侧重在后端的程序员,进一步定位现阶段是 Web 后台开发。 由于自身智力一般,技术迭代又非常快,为不至于总处于入门水平,经常会尝鲜新技术。 为保持好奇心,日常除技术以外,还会涉猎摄影、演示设计、拍视频、自媒体写作等。 如果此刻我是一个成功人士,看到上面的领域,有人会羡慕说:「斜杠」,遗憾的是,在下没有成功,所以,上面的领域都一定程度上会被人认为:「不务正业」,不过不重要,我本职还是一名后端程序员。 记忆记忆有遗忘曲线,这是大家都懂的道理,所以为了防止忘记,最重要的方法是经常使用、反复使用。这也是为什么,有些人说:在工作中学最容易进步。因为工作的流程、项目不会频繁变动,你会经常性的关注一个或者多个项目进行开发,假以时日,你会越来越熟悉,理所当然,你会越做越快。这个时候,就达到了所谓的:舒适圈。要再想进步,你得跳到「学习区」。再反复这个动作。 问题是,除了工作之外,你很少有其他机会再进行技能锻炼了。 创造机会主动承接更为复杂的任务这个比较容易理解,因为更为复杂的任务,你才可能尝试使用新的技术栈,有机会进行其他技能的锻炼,这样就能进入「学习区」。 如果公司项目就这么点,没有太复杂的,或者说新项目和你接触的相差不多,只不过应用场景不同而已。这个时候,任务如果一定需要你的参与,你最好尝试新的架构,尝试新的技术点,尽管大体相同,可以将你认为原系统不合理的地方改进,这样也能创造机会进入「学习区」。 但就我认为,一般项目开发时间都非常紧,开发人员有可能没有充足的时间进行考虑,会依然使用原有技术点,这样进入学习区的机会就被浪费了,你只是使用一份经验,做了两个类型的项目而已。 旧知识补全刚进入职场,核心位置就那么几个人占着,论经验、论资历,你都不如别人,你接触到的资源有限,没有新项目让你独立开发,只有旧项目的 Bug 让你修复,那该怎么办? 换坑吗?怕不怕另外一个也是坑? 补知识体系即使是你能完成的任务,你有没有尝试过自己独立写一个,你有没有尝试过自己弥补下不懂的知识点,你有没有尝试过总结下自己的开发流程是否是最优的,你有没有尝试过总结下项目的技术要点,你有没有尝试过提炼可以复用的技术点... 如果你都没有,恭喜你,你又找到了一个进入「学习区」的点,即:补充原有技术栈。 也许你工作中已经有一门常用编程语言,但都是靠 Google、StackOverFlow,你是不是要尝试梳理下整个编程语言的知识体系,当然梳理的切入点依然是和工作相关为先,因为这最迫切,最能反复,使用频率最高。 也许你对数据库相关知识略懂,对优化数据知识点却不是很懂,你是不是要尝试下找相关资料弥补下。 也许... 也许你还可以翻阅源码,比如内置库的实现,之前我还不太会关注这些,写起代码来不是很有底气,后来经常性的查看源码,借助 IDE 的跳转功能实现对源码的阅读,再结合 IDE 的 structure ,可以对文件的函数、结构体、方法等进行组织。这样从整体观看一目了然,看得多了,你甚至可以总结出一些共性: 比如包的错误处理一般定义在包的顶部几行,而且格式都统一比如 Interface 是方法的集合,内置的常用的 Interface 其实不多,很多内置包都相互实现比如包的结构体,可以实例化一个默认的,这样可以直接调用函数,比如 http.DefaultClient...阅读库的源码,我一般是怎么做的呢?(不要太关注具体的实现,除非你完全能看懂) 官方文档:了解常用使用方法思维导图:输出可导出的结构体、函数、方法等,依然选择最为常用的IDE 的 structure 功能,查看文件的具体组织形式,看可导出的结构体、函数、方法等持续总结举个例子net/http 包几乎奠定了 Go 领域所有 Web 框架、网络请求库的基础。由此来看下我是如何梳理的。 了解 HTTP 相关知识随意找本相关的书,发现是个大块知识啊。结合一般的历史经验,你可能作出这么张思维导图。 整个过程像是:你从一本书总摘出的目录,前提是看过书的内容而得出来的。 net/http 客户端网络请求分为两个层面: 客户端发起网络请求服务端提供网络请求访问资源func getHandle(rawString string) { response, err := http.Get(rawString) if err != nil { return } defer response.Body.Close() content, _ := ioutil.ReadAll(response.Body) fmt.Println(string(content))}看上去发起网络请求很简单,只需要使用 http.Get 即可。 ...

May 22, 2019 · 2 min · jiezi

从0开始Go语言用Golang搭建网站

实践是最好的学习方式零基础通过开发Web服务学习Go语言 本问适合有一定编程基础,但是没有Go语言基础的同学。 也就是俗称的“骗你”学Go语言系列。 这是一个适合阅读的系列,我希望您能够在车上、厕所、餐厅都阅读它,涉及代码的部分也是精简而实用的。 学习需要动机Go语言能干什么?为什么要学习Go语言? 本系列文章,将会以编程开发中需求最大、应用最广的Web开发为例,一步一步的学习Go语言。当看完本系列,您能够清晰的了解Go语言Web开发的基本原理,您会惊叹于Go语言的简洁、高效和新鲜。 结果反馈才能让你记住《刻意练习》一书中说,学习需要及时反馈结果,才能提高学习体验。 本系列文章的每一节,都会包含一段可运行的有效代码,跟着内容一步一步操作,你可以在你自己的计算机上体验每一句代码的作用。 不要学习不需要的东西文章围绕范例为核心,介绍知识点。文中不罗列语法和关键字,当您还不知道它们用来干什么时,反而会干扰您的注意力。 希望您在阅读本系列文章后,对Go语言产生更多的学习欲望,成为一名合格的Gopher Gopher:原译是囊地鼠,也就是Go语言Logo的那个小可爱;这里特指Go程序员给自己的昵称。如何10分钟搭建Go开发环境1.下载Go语言安装文件访问Go语言官方网站下载页面: https://golang.org/dl 可以看到官网提供了Microsoft Windows、Apple MacOS、Linux和Source下载。 直接下载对应操作系统的安装包。 [图片上传失败...(image-591890-1557632893071)] 2.和其他软件一样,根据提示安装3.配置环境变量在正式使用Go编写代码之前,还有一个重要的“环境变量”需要配置:“$GOPATH” GOPATH环境变量指定工作区的位置。如果没有设置GOPATH,则假定在Unix系统上为$HOME/go,在Windows上为 %USERPROFILE%\go。如果要将自定义位置用作工作空间,可以设置GOPATH环境变量。GOPATH环境变量是用于设置Go编译可以执行文件、包源码以及依赖包所必要的工作目录路径,Go1.11后,新的木块管理虽然可以不再依赖 $GOPATH/src,但是依然需要使用 $GOPATH/pkg 路径来保存依赖包。 首先,创建好一个目录用作GOPATH目录 然后设置环境变量 GOPATH: Linux & MacOS: 导入环境变量 $ export GOPATH=$YOUR_PATH/go 保存环境变量 $ source ~/.bash_profile Windows: 控制面板->系统->高级系统设置->高级->环境变量设置 $GOPATH设置好后,它是一个空目录,当在开发工作中执行go get、go install命令后, $GOPATH所指定的目录会生成3个子目录: bin:存放 go install 编译的可执行二进制文件pkg:存放 go install 编译后的包文件,就会存放在这里src:存放 go get 命令下载的源码包文件4.检查环境打开命令行工具,运行 $ go env 如果你看到类似这样的结果,说明Go语言环境安装完成. GOARCH="amd64"GOBIN=""GOCACHE="/Users/zeta/Library/Caches/go-build"GOEXE=""GOFLAGS=""GOHOSTARCH="amd64"GOHOSTOS="darwin"GOOS="darwin"GOPATH="/Users/zeta/workspace/go"GOPROXY="https://goproxy.io"GORACE=""GOROOT="/usr/local/go"GOTMPDIR=""GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"GCCGO="gccgo"CC="clang"CXX="clang++"CGO_ENABLED="1"GOMOD=""CGO_CFLAGS="-g -O2"CGO_CPPFLAGS=""CGO_CXXFLAGS="-g -O2"CGO_FFLAGS="-g -O2"CGO_LDFLAGS="-g -O2"PKG_CONFIG="pkg-config"GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/7v/omg2000000000000019/T/go-build760324613=/tmp/go-build -gno-record-gcc-switches -fno-common"5.选择一款趁手的编辑器或IDE现在很多通用的编辑器或IDE都支持Go语言比如 ...

May 12, 2019 · 4 min · jiezi

浏览器元素尺寸与位置查询指南

前言这篇文章主要介绍了有关浏览器中获取坐标以及尺寸的几种途径,算是比较全的一篇文章了. 在浏览器中获取元素的坐标以及尺寸是非常容易的,有非常多种方式来完成这些需求,但是杂乱的API和很多兼容处理导致了浏览器中没有直接的方式来获取我们想要的结果. 仔细想想这个问题,为什么浏览器并没有直接提供一个简单的属性就告诉你浏览器窗口的大小,或者一个元素的宽高. 就拿div元素来举例,我们有很多的问题影响到了元素宽高: border 是否纳入宽高的计算?padding 是否纳入宽高的计算?magin 是否纳入宽高的计算?box-sizing:border-box; 时候该如何计算?父元素使用了overflow:hidden;把我们的元素裁剪了,这时候的宽高该如何计算?元素使用了overflow出现了滚动条此时该如何计算?而如果要获取一个浏览器窗口的大小,你还要面对我们到底是要获取哪个大小? 屏幕大小?浏览窗口大小?浏览区域大小?是否包含滚动条?当然最终你还要面临一个兼容问题,致我们敬爱的IE浏览器,不过本文可不探讨浏览器之间的差异.不过本文的涉及到的内容应该在IE9以上都是可以正常使用的(不过建议你还是查下can i use 或者MDN). 浏览器部分浏览器的宽高计算主要通过window对象来完成. 这个对象上提供了几个关键属性: window.innerWidthwindow.innerHeightwindow.outerWidthwindow.outerHeight用人类语言来描述这几个属性就是. 属性名称人类解释innerWidth获取页面可视区域的宽度包括右侧的滚动条(如果有的话).所谓的可视区域就是HTML页面的内容区域不包括浏览器自身的ui所占用的空间(地址栏和菜单栏等).innerHeight获取页面可视区域的高度包括底部的滚动条(如果有的话).解释同上.outerWidth获取浏览器窗口宽度.outerHeight获取浏览器窗口高度.友情出演windows画图: 注意:单位均为px. 注意:滚动条并不视为浏览器的ui中的内容,而是视为内容区域的一部分,右侧默认的滚动条的宽度包含在window.innerWidth中,但是不属于html元素和html下的任何元素. 元素部分属性属性名称人类解释element.clientWidth元素内容区域宽度+padding的宽度,如果宽度溢出且裁剪那么不包含被裁剪掉的部分.element.scrollWidth当子元素宽度溢出,这里提供的是子元素的宽度包括溢出的部分,大小计算和clientWidth一样.element.offsetWidth相当于计算边框宽度的clientWidth,宽度计算为content+padding+border.element.clientHeight元素内容区域高度+padding的高度,如果高度溢出且裁剪那么不包含被裁剪掉的部分.element.scrollHeight当子元素高度溢出,这里提供的是子元素的高度包括溢出的部分,大小计算和clientHeight一样.element.offsetHeight相当于计算边框高度的clientHeight,高度计算为content+padding+border.element.clientLeft元素左边框的宽度element.scrollLeft计算较为复杂,看后续详解element.offsetLeft计算比较复杂,看下面详解element.clientTop元素上边框的宽度element.scrollTop计算较为复杂,看后续详解element.offsetTop计算比较复杂,看下面详解滚动条的规律无论是横向滚动条还是纵向滚动条,对于测量clientXXX这个单位来说是不包括滚动条的宽(高)的. 例如在下面这张图中我们进行测量父元素(黑色区域)的clientWidth结果和子元素(红色区域)的clientWidth的大小是一样的. 不过需要注意的是,一旦出现了滚动条对于clientWidth来说就意味着宽度减小(高度同理). 注意:图片所指的宽度是clientWidth API名称是否包含滚动条大小offsetXXX包含clientXXX不包含scrollXXX不包含所以在margin:0;padding:0;border-width:0;情况下offsetWidth - clientWidth=滚动条的宽度. 通过这种方式我求出了chrome浏览器滚动条大小是17px整,但是不要忘记这些API只会返回整数. 注意:scrollXXX对于滚动条计算的规则和clientXXX表现一致. 含有box-sizing:border-box的计算请记住,对于clientXXX来说,元素的大小就是padding+content. 而使用border-box后元素的表现就是padding和border的修改就不会影响到元素的大小. 此时width是多少clientWidth就是多少,height同理. 但是不要忘记了边框不参与clientXXX的计算,所以border的修改并不会影响元素的宽高变化,那么那么当border变大,对应的clientXXX就变小. 一个元素设置了border-box: box{ width:100px; padding:20px; border:20px solid; box-sizing:border-box;}此时clientWidth= 100px - 20px*2(左右边框的宽度) = 60px 由于offsetXXX的计算是包含border的大小的,所以如果一个元素设置了border-box那么offsetWidth就等于元素的width大小,因为border被限制到了width中. offsetTop和offsetLeft子元素的offsetWidth|height是相对于父元素内容区域(padding+content)左侧和顶部的偏移量. 这个两个是相对值,是要求出向对于父级使用定位情况下来进行计算的,这个父元素可以通过HTMLElement.offsetParent来获取到. 例如:父级使用absolute或者relative. 注意:后文提出的父元素都指的是使用了相对定位的父元素. 注意:以上都是对于块级元素所描述的,对于行内元素或者td等元素相对的父元素不尽相同,这里不考虑这些情况,详情可以查看上方的链接. 情况1 在子元素使用了绝对定位的情况下,父元素无法干预子元素,所以子元素的scrollLeft就是left+margin-left. 情况2 第二种情况就是父元素和子元素都使用了相对定位,而相对定位是不脱离文档流的,那么父元素的padding-left就会影响到子元素的scrollLeft属性. 在线实例 注意:貌似offsetTop和Left在不同浏览器下有不同计算值,会带来兼容性问题,这里就不展开了,有兴趣的读者可以去查阅相关资料. scrollTop和scrollLeft首先scrollTop和scrollLeft是一对可读写的属性,这意味着你可以获取他的值也可以设置它从而控制滚动的距离. 注意:scrollTop|scrollLeft是用在含有滚动区域的元素上(图中黑色边框的元素),而不是被滚动的元素上测量,被滚动的元素scrollTop永远是0. 简单理解:在垂直方向上含有滚动条的元素的内容区域的顶部(padding+content)相对于自身顶部边框的底部向上移动的距离(水平方向同理). 就是scrollTop的大小(图中超出去的部分). 元素方法getBoundingClientRect关于这个方法建议阅读MDN的指南.当然你也可以选择听我瞎扯几句. 这个api是ie首先提出(早在ie4时候就有了)的这也是ie对web开发的贡献之一. 调用这个api会返回一个DOMRect对象,这个对象多次易名,不过没有变化过基本概念. ...

May 12, 2019 · 1 min · jiezi

web测试5月

web测试--5月

May 9, 2019 · 1 min · jiezi

webflux-用户管理界面

一个简单的用户管理的CRUD已经完成,现在我们需要在页面上展示,方便用户管理。尽管现在已经流行前后分离开发,但是在一些小公司做的项目并不需要前端开发人员,页面也是后端开发在写。所以这次我们使用thymeleaf来开发页面。1 集成thymeleafpom文件依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>开启thymeleaf spring.thymeleaf.enabled=true2 创建资源目录在resources目录下创建templates和static目录,templates下放你的html页面,static下放你的css以及js。这些目录是thymeleaf默认的,如果需要修改成别的目录,可以自行配置。感觉没必要修改了。 去bootstrap网站下载生产环境的文件放在static目录下,这样我们写页面就不用关心样式。 3 创建首页controller @Controllerpublic class Index { @GetMapping("/") public String index(){ return "index"; }}页面 首页: <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8" /><title>欢迎页面</title><link rel="stylesheet" href="/css/bootstrap.css" /></head><body> <div class="container"> <nav class="navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <a class="navbar-brand" href="#">Mike Study</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">首页</a></li> <li><a href="#">实战课程</a></li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="你想学点啥?"> </div> <button type="submit" class="btn btn-default">Go</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">上班摸鱼</a></li> <li><a href="#">下班充电</a></li> </ul> </div> <!-- /.navbar-collapse --> </div> <!-- /.container-fluid --> </nav> <div class="row"> <div class="jumbotron"> <h1>案例上手 Spring Boot WebFlux!</h1> <p>本课程是一个系列基础教程,目标是带领读者上手实战,课程以新版本 Spring Boot 2.0 WebFlux 的核心概念作为主线。围绕 Spring Boot 2.0 WebFlux 技术栈的系列教程,目标是带领读者了解 Spring Boot 2.0 WebFlux 各种特性,并学会使用 Spring Boot 相关技术栈上手开发项目。</p> <blockquote> <p>只有不断地学习才能进步</p> <footer> Mike <cite title="Source Title">Liu</cite> </footer> </blockquote> </div> </div> <div class="row"> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <a href="/users"><p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p></a> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> </div> <div class="row"> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> <div class="col-md-3"> <img src="https://img.mukewang.com/55f8d5080001293c06000338-240-135.jpg" alt="..." style="height: 140px; width: 100%; display: block;" class="img-thumbnail"> <p>mongodb开发用户管理系统<span class="label label-danger">mongodb</span></p> <p>发布时间:<i class="glyphicon glyphicon-calendar"></i>2019-01-01</p> </div> </div> </div></body></html> ...

May 9, 2019 · 4 min · jiezi

你必须知道的Git命令

这篇笔记是为了学习Git知识而收集总结的,主要是看受一篇帖子《你可能不知道的15条Git命令》的影响,才想记录这篇笔记的,如有雷同,纯属巧合。 Git 是一个分布式版本控制软件, 最初目的是为更好地管理Linux内核开发而设计。来源:维基百科 - Git Git是一个软件,它允许你通过提交对一个系统(或一组)文件的历史进行注释。这些提交便是在给定时间点对系统做出的差异“快照”。 官网下载速度慢,可使用这个链接下载 或者Github下载地址, 需要其他版本请提issue联系我。 1. Git 配置--system #系统级别--global #用户全局--local #单独一个项目git config --global user.name "xxxx" #用户名git config --global user.email "xxxx@xxx.com" #邮箱git config --list # 列举所有配置连接远程仓库github 创建SSH Key ssh-keygen -t rsa -C <youremail@example.com>登陆GitHub,打开Account settings -> SSH Keys -> Add SSH Key,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容测试是否连接 ssh git@github.com几个概念: 工作区(Working Directory): 你在电脑里能看到的目录。 暂存区(stage / index): 保存了下次将提交的文件列表信息, 一般存放在 .git目录下 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。 版本库(Repository): 工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。 远程仓库(Remote) 阮一峰老师对Git工作区、暂存区、版本库、远程仓库的解释 Runoob对Git工作区、暂存区、版本库、远程仓库的解释 忽略文件配置:添加.gitignore文件 文件 .gitignore 的格式规范如下: ...

May 9, 2019 · 4 min · jiezi

SpringBoot-20-响应式编程

SpringBoot 2.0 已经发布多时,一直不知道它有什么用,只是知道它有个webflux。今天就来学习一下,看一下是否有必要升级到新版本?1 2.0与1.0版本的区别? 我们可以看出来,增加了些新的特性,主要是对响应式编程的支持,底层多了Netty,这样就可以进行非阻塞io的编程,这也是响应式编程的基础。还有就是2.0对应的java版本必须最低java8,支持java9.如果你们公司使用的还是1.6,1.7那就不适合升级版本,可能会带来一堆麻烦。使用webflux并不会提高应用的响应速度,官网也明确指出了。所以不要跟风去使用2.0的版本。使用webflux可以在有限的资源下提高系统的吞吐量和伸缩性。2 搭建简单的webflux项目如果你是使用得STS来创建项目的话将会很简单,直接选择web flux模块就好。SpringBoot选择最新稳定的2.1.4.RELEASE。 完整pom文件: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.mike</groupId> <artifactId>flux</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mike-flux</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>创建启动类:(使用STS会自动创建) package com.mike;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MikeFluxApplication { public static void main(String[] args) { SpringApplication.run(MikeFluxApplication.class, args); }}3 编写路由和处理类有过前端工作经验的小伙伴,对路由肯定不陌生,Vue react中都有统一的路有管理。现在SpringBoot也可以这样来写了。后端的小伙伴可以把它理解为你之前写的controller。先定义一个处理类: ...

May 7, 2019 · 1 min · jiezi

上古开发神器之Coldfusion

Web开发一直是一个比较纠结的技术领域,为啥这么讲?你看: 如果我是一个前端开发工程师,意味着我至少需要熟练掌握3种编程语言:HTML,CSS和Javascript。这还是基于前后端分离的开发场景,如果是使用传统的MVC框架的话,则那些“全栈”工程师们还需要掌握服务器端的编程语言,例如Java,Python或者Ruby等。 基于这样一种复杂组合的前提,技术界又衍生出了各种各样的“框架”来试图增强复用性以提升开发效率 & 规范团队的编码风格。例如传统的MVC框架就有无数种:基于Java的SSH(实际上是3中框架的搭配)、基于Python的Django、基于PHP的Yii、基于Ruby的Rails等等等等……(你会发现上述框架都是基于服务器端语言的)然后,由于Javascript语言的设计实在是比较草率,所以出现了各种框架来进行改善(prototype, extjs, jquery, vue.js, react.js等等);与此同时,CSS的框架也层出不穷(Bootstrap,boolma……) 上述现象,一方面体现了Web开发技术领域的生机勃勃;而另一方面,从程序员角度来看(程序员也是一种用户哈),一致性体验严重不足! 在上古时期,曾经有一种开发技术在开发的一致性体验上有着明显由于同类产品的设计&同时也拥有非常高的开发效率(亲测体会,我曾经用了一个周末的时间,快速开发了一款公司内部使用的项目周报管理系统 ;-P),这种技术的名字叫Coldfusion。 Coldfusion其实是一款Web服务器软件产品的名称,他使用的前端开发语言名字叫CFML。其典型特点是:语法很简单,与HTML一样,使用一些标记实现特别的功能。这些标记也和HTML一样,不过都是用CF开头的。例如<CFMAIL>...</CFMAIL> <CFIF>...</CFIF>等。现在的JavaServer Page里的JSTL显然是受到了他的启发。 一个小小的例子Hello world: <cfset Msg="Hello World"><html> <head> <title>First ColdFusion Program</title> </head><body> <center> <cfoutput><h1>#MSG#</h1></cfoutput> </center></body></html>Coldfusion的功能非常强大,它本身所带的30多个例子几乎实现了以前用CGI实现的所有WEB应用。Coldfusion也可以处理数据库。所以在ASP出现之前,Coldfusion占据了NT上WEB数据库应用的市场。 你会发现,在早期的那个年代(CSS还没有从HTML中完全剥离出来),其初步实现了前端编辑语言与服务器端语言开发的一致性体验。 实际上,在当时Coldfusion几乎是没有什么像样的竞争对手的。Coldfusion出到3.0版本的时候,微软的ASP(注意不是.net平台)刚刚出来1.0版本。在那个时候PHP也是初出茅庐,而Java领域的开发人员们还在使用Servlet在服务器端拼写HTML字符串! 可惜的是,一种优秀的技术实现,被汹涌的商业竞争大潮所淹没了。 Coldfusion产品分成两大部分Coldfusion Studio(开发使用的IDE)和Coldfusion Server(服务器端),均是由Allaire公司开发出售。 需要说明的是CFML语言理论上不是绑定在Coldfusion产品上面的(事实上也的确有一些第三方的服务器产品支持CFML),但是实际上在市场里面大家基本上就会认为Coldfusion=CFML。 当时Allaire公司旗下还有另外一款更加闪光的产品:Homesite。“老年”的程序员可能还有印象,在当时Homesite和Macromedia公司的Dreamweaver以及微软公司的Frontpage并成为所见即所得HTML编辑器的“三剑客”。此外,Allaire公司还有一款Java的服务器端产品JRun在当时也是大名鼎鼎。 2001年,Macromedia公司收购Allaire公司。从产品上带来了两个后果: Homesite产品随之消失;Coldfusion产品从C++移植到Java平台。2005年,Macromedia公司被更大的巨头Adobe公司收购,在此后的3年,Coldfusion逐渐淡出了人们的视野。 有时候,我们怀旧的时候,有可能会YY一下,在那个年代,开源的理念还没有深入人心,否则的话CFML的技术直接开放给开源社区的话,这项技术目前的生存状态还不一定是个什么样……

April 29, 2019 · 1 min · jiezi

Django-web框架url-path-name详解

Django web框架-----url path name详解quicktool/view.py文件修改视图函数index(),渲染一个home.html模板 from django.shortcuts import renderdef index(request): return render(request, 'home.html')quicktool(应用app)下创建一个templates文件夹(模板),在templates下创建一个home.html <!DOCTYPE html><html><head> <title>学习Django</title></head><body><a href="/add2/4/5/">计算 4+5</a></body></html>mytestsite/urls.py文件将视图函数index()对应的网址取名name为home(只要这个名字不变,网址变了也能通过名字获取到) urlpatterns = [ path('admin/', admin.site.urls), path('', quicktool_views.index, name='home'), # 新增name值 path('add/', quicktool_views.add, name='add'), # 新增 path('add2/<int:a>/<int:b>/', quicktool_views.add2, name='add2'), # 新增]运行开发服务器,访问http://127.0.0.1:8000/,可以看到 点击标签a会执行脚本进行计算,但是home.html中代码a标签的url是"写死"的,我们可以通过url的name传参,home.html增加以下代码 #不带参数的:#{% url 'name' %}#带参数的:参数可以是变量名#{% url 'name' 参数 %}<a href="/add2/4/5/">计算 4+5</a><a href="{% url 'add2' 70 80 %}">计算 70+80</a> # 新增运行开发服务器,访问http://127.0.0.1:8000/,可以看到 点击第一个a标签跳转请求的url为http://127.0.0.1:8000/add2/4/5/ 点击第二个a标签跳转请求的url为http://127.0.0.1:8000/add2/70/80/ 第二个a标签的写法是根据url name 值传参进行跳转,即根据mytestsite/urls.py文件的url path()的属性值name匹配,再传相应的参数值,进行"不写死"请求。 在url path()的属性值name不修改的前提下,修改url path()的第一个路径参数,那么a标签获取的网址也会动态相应变化。 比如 path('add2/<int:a>/<int:b>/', quicktool_views.add2, name='add2'), 改成 ...

April 28, 2019 · 1 min · jiezi

cookie

问题HTTP协议是一种无状态的协议,Web服务器本身不能识别出哪些请求是同一个浏览器发出的,浏览器的每一次请求都是完全孤立的。 web服务器怎么唯一地标识一个用户,并记录该用户的状态? 会话与状态管理会话指一个客户端与web服务器之间连续发生的一系列请求和响应过程。(就像把电话拿起到最后挂断)。会话状态指web服务器与浏览器在会话过程中产生的状态信息,借助会话状态,web服务器把属于同一会话中的一系列请求和响应过程关联起来。 web服务器要从大量的请求消息中区分出哪些请求属于同一会话,即能识别出来自同一个浏览器的访问请求,这需要浏览器对其发出的每个请求消息都进行标识:属于同一个会话中的请求消息都附带同样的标识号,属于不同会话的请求消息附带不同的标识号,这个标识号就称为会话ID。 servlet规范中,有两种机制完成会话跟踪:cookiesession cookie是客户端保持HTTP状态信息的一种方案。浏览器访问web服务器的某个资源时,由web服务器在HTTP响应消息头中附带传送给浏览器的一个小文本文件(Set-Cookie响应头字段)。如: Set-Cookie: bid=8UKoeCtJT1M; Expires=Sat, 25-Apr-20 04:02:11 GMT; Domain=.douban.com; Path=/一旦web浏览器保存了某个cookie,那么它在以后每次访问该web浏览器时都会在HTTP请求头中(Cookie请求头字段)将这个cookie回传给web服务器。如: Cookie: bid=8UKoeCtJT1M;如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。不同的浏览器采用不同的方式保存Cookie。IE浏览器会在C:\Documents and Settings\你的用户名\Cookies文件夹下以文本文件形式保存,一个文本文件保存一个Cookie。 创建cookiehttpServletResponse.addCookie(cookie);该方法并不修改之前的Set-Cookie报头,而是创建新的报头。 一个web站点可以给一个web浏览器发送多个cookie,一个浏览器也能存储多个web站点提供的cookie(每个站点最多存放20个)。一个cookie(大小限制为4kb)只能标识一种信息,它至 少含有一个标识该信息的k-v。 Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码。 获取cookiehttpServletRequest.getCookies()读取所有cookie返回数组,遍历通过cookie.getName()筛选出自己要的(new Cookie()时的第一个参数)。 修改cookies如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。 注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。 cookie属性设置setMaxAge()cookie的时效性:默认情况下新建的cookie是一个会话级别的cookie(会话cookie),存储在浏览器内存中,用户关闭浏览器后被删除。 若希望浏览器将该cookie存储在磁盘上(持久cookie),需要使用maxAge,并给出一个以秒为单位的时间。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。这种cookie直到超过设定的过期时间才被删除。如果为0,表示删除该Cookie。默认为–1。 setPath()cookie的作用范围:默认是当前目录以及子目录,不能作用于上一级目录。 可以自己设置cookie的作用范围:setPath(request.getContextPath)“/”表示站点的根目录,如果设置为“/”,则本域名下contextPath都可以访问该Cookie。 setDomain()Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。 正常情况下,同一个一级域名下的两个二级域名如www.baidu.com和images.baidu.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有baidu.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为.baidu.com。 可以修改本机hosts文件来配置多个临时域名来验证domain属性。 注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。 setSecure()HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。设置secure属性为true的代码是cookie.setSecure(true); secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密(不可逆加密的)、解密,以防泄密。 其他属性expires:失效时间,表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。如果不设置这个时间戳,浏览器会在页面关闭时即将删除所有cookie;不过也可以自己设置删除时间。这个值是GMT时间格式,如果客户端和服务器端时间不一致,使用expires就会存在偏差。 HttpOnly: 告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见。但在http请求张仍然会携带这个cookie。注意这个值虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。这项设置通常在服务器端设置。 应用场景自动登入 购物车,最近浏览过的商品

April 26, 2019 · 1 min · jiezi

我是如何自学-Python-的

不少初学 Python 或者准备学习 Python 的小伙伴问我如何学习 Python。今天就说说我当时是怎么学习的。 缘起 我大学专业是电气工程,毕业后做的是自动化方面的工作。对于高级语言编程基本是 0 基础,那时刚毕业在车间做设备调试,工资也只有三四千块钱。2014年底在知乎看到搞 IT 的薪资动辄 10k 起步,所以我也动了学习编程的念头。 当时 Python 已经开始流行。虽然远没有今天热度这么高,但是已经有一些大V在鼓励大家开始学习 Python了。对我影响最大的是知乎ID为:"萧井陌"的大神。我觉得他至少影响了上万人学习 Python 。那时候他的《编程入门指南》很火,而且一直在鼓励初学编程的人去学习 Python。其中他的这个回答对我影响最大,因为这个回答特别笃定,把步骤写好了,照做就是了。 然后我买了他推荐的这本书,现在已经出第二版了。当时看第二遍时还是糊里糊涂的,因为你学了 Python 基础后,还要了解 WEB 开发的一些概念,包括数据库的基本用法。所以当时又看了 WEB 方面包括 HTML/CSS/JS,和 HTTP协议一些知识。买了本 SQL 必知必会来了解简单的 SQL 语句。总之是 Flask 这本书看了三遍,对书中所写的项目理解了80%左右吧。到这里基本算是入门吧,之后就开始做 IT 相关工作了。 学习方法如果是 0 基础学习,还是推荐《笨办法学Python》这本小册子开始。很直白,没有上来就讲语法,仅仅是照着敲就行了。这个小册子看完后我当时看的是《Python核心编程-第二版》上面讲的还是 Python 2.5。现在出了第三版,但是已经不推荐初学者去看了。现在你可以直接去看人民邮电出版社的《Python编程从入门到实践》,这本书我简单翻过,内容还是很不错的,包括大量的实际案例,可以亲手做出一点好玩的应用来。 除了 Python 外还要了解基本的 HTML/CSS/JS。这些东西花几天时间在 W3School 看一看就差不多了。在这个过程中可以到网上看看别人都用 Python 来做哪些好玩的事情,可以跟着学学。知乎上有很多好的问题和答案,非常值得学习。 在学习过程中不必要求 100% 掌握,一些高级用法不理解没关系,等代码写的多了就懂了。上面基础知识看完后就要选择一个方向了,比如 WEB,数据分析等。做 WEB 的话 Python 最流行的两个框架 Django 和 Flask 选一个深入学一下就好了,我当时学的是 Flask,不过 Django 是一个大而全的框架,不需要你去找各种第三方模块来使用,文档也很全面,都很适合来学习。 ...

April 24, 2019 · 1 min · jiezi

web测试流程及注意事项经验干货整理分享

结合了工作经验,写出了这篇文章,全是干货和经验分享。用您5分钟时间阅读完,希望能对您有帮助!相信很多人都喜欢用浏览器看网页,虽然网民不是专业人员,但是对界面效果的印象是很重要的。如果你注重这方面的测试,那么验证应用程序是否易于使用就非常重要了。很多人认为这是测试中最不重要的部分,但是恰恰相反界面对不懂技术的客户来说那相当关键,慢慢体会就会明白的。 Web界面设计中,对各种元素(如按钮、单选框、复选框、列表框、超连接、输入框等等)的设计是非常重要的。一、搜索功能 若查询条件为输入框,则参考输入框对应类型的测试方法 1、功能实现: (1)如果支持模糊查询,搜索名称中任意一个字符是否能搜索到 (2)比较长的名称是否能查到 (3)输入系统中不存在的与之匹配的条件 (4)用户进行查询操作时,一般情况是不进行查询条件的清空,除非需求特殊说明。 2、组合测试: (1)不同查询条件之间来回选择,是否出现页面错误(单选框和多选框最容易出错) (2)测试多个查询条件时,要注意查询条件的组合测试,可能不同组合的测试会报错。 二、添加、修改功能 1、特殊键:(1)是否支持Tab键 (2)是否支持回车键 2、提示信息: 不符合要求的地方是否有错误提示 3、唯一性: 字段唯一的,是否可以重复添加,添加后是否能修改为已存在的字段(字段包括区分大小写以及在输入的内容前后输入空格,保存后,数据是否真的插入到数据库中,注意保存后数据的正确性) 4、数据 正确性: (1)对编辑页的每个编辑项进行修改,点击保存,是否可以保存成功,检查想关联的数据是否得到更新。 (2)进行必填项检查(即是否给出提示以及提示后是否依然把数据存到数据库中;是否提示后出现页码错乱等) (3)是否能够连续添加(针对特殊情况) (4)在编辑的时候,注意编辑项的长度限制,有时在添加的时候有,在编辑的时候却没(注意要添加和修改规则是否一致) (5)对于有图片上传功能的编辑框,若不上传图片,查看编辑页面时是否显示有默认的图片,若上传图片,查看是否显示为上传图片 (6)修改后增加数据后,特别要注意查询页面的数据是否及时更新,特别是在首页时要注意数据的更新。 (7)提交数据时,连续多次点击,查看系统会不会连续增加几条相同的数据或报错。 (8)若结果列表中没有记录或者没选择某条记录,点击修改按钮,系统会抛异常。 三、删除功能 1、特殊键:(1)是否支持Tab键 (2)是否支持回车键2、提示信息:(1)不选择任何信息,直接点击删除按钮,是否有提示(2)删除某条信息时,应该有确认提示3、数据 实现:(1)是否能连续删除多个产品(2)当只有一条数据时,是否可以删除成功 (3)删除一条数据后,是否可以添加相同的数据(4)如系统支持批量删除,注意删除的信息是否正确 (5)如有全选,注意是否把所有的数据删除(6)删除数据时,要注意相应查询页面的数据是否及时更新 (7)如删除的数据与其他业务数据关联,要注意其关联性(如删除部门信息时,部门下游员工,则应该给出提示)(8)如果结果列表中没有记录或没有选择任何一条记录,点击删除按钮系统会报错。 四、输入框 1、字符型输入框 (1)字符型输入框:英文全角、英文半角、数字、空或者空格、特殊字符“~!@#¥%……&*?[]{}”特别要注意单引号和&符号。禁止直接输入特殊字符时,使用“粘贴、拷贝”功能尝试输入。 (2)长度检查:最小长度、最大长度、最小长度-1、最大长度+1、输入超工字符比如把整个文章拷贝过去。 (3)空格检查:输入的字符间有空格、字符前有空格、字符后有空格、字符前后有空格 (4)多行文本框输入:允许回车换行、保存后再显示能够保存输入的格式、仅输入回车换行,检查能否正确保存(若能,检查保存结果,若不能,查看是否有正常提示) (5)安全性检查:输入特殊字符串(null,NULL, ,javascript,<script>,</script>,<title>,<html>,<td>)输入脚本函数(<script>alert("abc")</script>)、doucment.write("abc")、hello) 2、数值型输入框 (1)边界值:最大值、最小值、最大值+1、最小值-1 (2)位数:最小位数、最大位数、最小位数-1最大位数+1、输入超长值、输入整数 (3)异常值、特殊字符:输入空白(NULL)、空格或"~!@#$%^&*()_+{}|[]:"<>?;',./?;:'-=等可能导致系统错误的字符、禁止直接输入特殊字符时,尝试使用粘贴拷贝查看是否能正常提交、word中的特殊功能,通过剪贴板拷贝到输入框,分页符,分节符类似公式的上下标等、数值的特殊符号如∑,㏒,㏑,∏,+,-等。输入负整数、负小数、分数、输入字母或汉字、小数(小数前0点舍去的情况,多个小数点的情况)、首位为0的数字如01、02、科学计数法是否支持1.0E2、全角数字与半角数字,数字与字母混合、16进制,8进制数值、货币型输入(允许小数点后面几位)。 (4)安全性检查:不能直接输入就copy 3、日期型输入框: (1)合法性检查:(输入0日、1日、32日)、月输入[1、3、5、7、8、10、12]、日输入[31]、月输入[4、6、9、11]、日输入30、输入非闰年,月输入[2],日期输入[28、29]、输入闰年,月输入[2]、日期输入[29、30]、月输入[0、1、12、13] (2)异常值、特殊字符:输入空白或NULL、输入~!@#¥%……&*(){}[]等可能导致系统错误的字符 (3)安全性检查:不能直接输入,就copy,是否数据检验出错? 4、信息重复在一些需要命名,且名字应该唯一的信息输入重复的名字或ID,看系统有没有处理,会否报错,重名包括是否区分大小写,以及在输入内容的前后输入空格,系统是否作出正确处理. 10G海量软件测试相关资料,视频,工具等你来领 百度云点他????链接 提取码: esyd 五、Web测试注意事项都有哪些!1.页面提示信息 主要是对页面操作结果成功或失败的提示信息,还有用户在做一些关键操作或者是涉及敏感操作时系统是否会有提醒。 2.页面功能部分 (1)页面初始数据是否显示正确 (2)页面数据处理功能是否被正确执行并返回正确结果 (3)对模块的功能测试时可以列出该模块的所有实现功能进行排列组合,比如页面基本的增删改查功能: 增加-->增加-->增加(连续增加测试);增加-->删除-->增加(之前删除内容);修改-->增加(修改之前内容) 3.页面中元素显示 ...

April 23, 2019 · 1 min · jiezi

【面试篇】寒冬求职季之你必须要懂的原生JS(中)

互联网寒冬之际,各大公司都缩减了HC,甚至是采取了“裁员”措施,在这样的大环境之下,想要获得一份更好的工作,必然需要付出更多的努力。 一年前,也许你搞清楚闭包,this,原型链,就能获得认可。但是现在,很显然是不行了。本文梳理出了一些面试中有一定难度的高频原生JS问题,部分知识点可能你之前从未关注过,或者看到了,却没有仔细研究,但是它们却非常重要。本文将以真实的面试题的形式来呈现知识点,大家在阅读时,建议不要先看我的答案,而是自己先思考一番。尽管,本文所有的答案,都是我在翻阅各种资料,思考并验证之后,才给出的(绝非复制粘贴而来)。但因水平有限,本人的答案未必是最优的,如果您有更好的答案,欢迎给我留言。 本文篇幅较长,但是满满的都是干货!并且还埋伏了可爱的表情包,希望小伙伴们能够坚持读完。 写文超级真诚的小姐姐祝愿大家都能找到心仪的工作。 如果你还没读过上篇【上篇和中篇并无依赖关系,您可以读过本文之后再阅读上篇】,可戳【面试篇】寒冬求职季之你必须要懂的原生JS(上) 小姐姐花了近百个小时才完成这篇文章,篇幅较长,希望大家阅读时多花点耐心,力求真正的掌握相关知识点。 1.说一说JS异步发展史异步最早的解决方案是回调函数,如事件的回调,setInterval/setTimeout中的回调。但是回调函数有一个很常见的问题,就是回调地狱的问题(稍后会举例说明); 为了解决回调地狱的问题,社区提出了Promise解决方案,ES6将其写进了语言标准。Promise解决了回调地狱的问题,但是Promise也存在一些问题,如错误不能被try catch,而且使用Promise的链式调用,其实并没有从根本上解决回调地狱的问题,只是换了一种写法。 ES6中引入 Generator 函数,Generator是一种异步编程解决方案,Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权,Generator 函数可以看出是异步任务的容器,需要暂停的地方,都用yield语句注明。但是 Generator 使用起来较为复杂。 ES7又提出了新的异步解决方案:async/await,async是 Generator 函数的语法糖,async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样。 1.回调函数: callback//node读取文件fs.readFile(xxx, 'utf-8', function(err, data) { //code});回调函数的使用场景(包括但不限于): 事件回调Node APIsetTimeout/setInterval中的回调函数异步回调嵌套会导致代码难以维护,并且不方便统一处理错误,不能try catch 和 回调地狱(如先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C...)。 fs.readFile(A, 'utf-8', function(err, data) { fs.readFile(B, 'utf-8', function(err, data) { fs.readFile(C, 'utf-8', function(err, data) { fs.readFile(D, 'utf-8', function(err, data) { //.... }); }); });});2.PromisePromise 主要解决了回调地狱的问题,Promise 最早由社区提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 那么我们看看Promise是如何解决回调地狱问题的,仍然以上文的readFile为例。 function read(url) { return new Promise((resolve, reject) => { fs.readFile(url, 'utf8', (err, data) => { if(err) reject(err); resolve(data); }); });}read(A).then(data => { return read(B);}).then(data => { return read(C);}).then(data => { return read(D);}).catch(reason => { console.log(reason);});想要运行代码看效果,请戳(小姐姐使用的是VS的 Code Runner 执行代码): https://github.com/YvetteLau/... ...

April 22, 2019 · 11 min · jiezi

了解 Web 标准规范和组织

Web 标准是由各大标准组织制定,由浏览器和其他 Web 底层框架或工具来实现,再提供给开发者能以最小成本开发适用于多平台的 Web 应用,这些标准是我们能访问无数网站的前提。学习标准的必要最近在 MDN 上学习 Web Components,学习中有些疑惑在 MDN 上没有提及,最后通过查阅 Custom elements 标准规范才解决了我的疑惑,让我感慨原来标准规范这么有用。制定 Web 标准的目的是严谨、无疏漏、无歧义地描述相关技术实现,一般来说这些标准规范是给像浏览器开发者的人来使用的,对于一般的 Web 工程师来说,平时用不上它,因为这些标准太过啰嗦和难懂。但是学习标准也有它的好处。因为我们接触的技术文档或文章,大都经过作者的理解和翻译,导致有些内容作者解读得不够全面甚至是错误的,因此影响到我们的学习效果。所以如果想要深入学习技术的原理,通过看最原始的标准文档,理解并建立自己知识体系,是很有必要的。Web 标准计划在 Web 发展的早期,浏览器各自为政,技术无一致实现,这直接损害了设计师、开发者、用户和行业的利益。为了解决这些问题,Web 标准计划 (Web Standards Project, WaSP) 于 1998 年成立,目标便是促进核心的 Web 标准的推广,鼓励浏览器对标准的支持,为大家寻求一条简单而便利之路。得益于前人努力,如今的现代浏览器表现已经越来越一致,进而催生出更多标准,有了这些标准我们可以开发出体验更好的 Web 应用。这个是所有浏览器相关的技术标准: The Web platform: Browser technologies,从中能了解到健全发展的 Web 技术生态。Web 标准组织说到 Web 标准,就不得不提制定这些标准的组织,这些标准不单只是由一个组织来制定,多个组织各自负责相关的技术领域,下面一一介绍。W3C (World Wide Web Consortium)W3C 组织为 Web 开发领域提出了很多建议,比如为 XHTML、XML、DOM、CSS 和 Web API 等技术实现提出了建议。你可能会注意到为什么说是提出建议,而不是标准呢?那是因为 W3C 自认为不是标准组织,他们只是组织了 Web 相关领域的专家,这些专家组成一支工作小组,工作小组就如何实现 Web 技术提出建议。尽管 W3C 对其建议的实现方案没有任何强制权力,但他们大多数的建议都被视为事实上的标准。W3C 组织关注 DOM、CSS、HTTP、媒体、性能、安全、图形学、可访问性和用户隐私等方方面面的技术,在这里可以搜索相关技术: All Standards and Drafts。从 W3C 组织成员的工作手册可以看到,一项技术从提出到成为标准,需要经过 4 个阶段。WD (Working Drafts):草案阶段CR (Candidate Recommendation):候选阶段PR (Proposed Recommendation):提议阶段REC (W3C Recommendation):正式建议阶段WHATWG (The Web Hypertext Application Technology Working Group)WHATWG 工作小组成立于 2004 年,起因是 W3C 组织对 HTML 不再感兴趣,转而关注 XHTML 技术,部分 W3C 成员对此行为不满,因此他们决定建立一个新组织推动 HTML 发展,制定相关标准。如今 HTML5 技术能发展起来,也是得助于 WHATWG 小组。WHATWG 小组因 HTML 而生,负责的 Web 标准主要是 HTML 相关技术,也涉猎一些 Web API,比如: HTML、DOM、浏览器兼容性、XHR、Fetch、Storage 和 URL 等标准。在这里可以查看所有标准:WHATWG Standards。WHATWG 组织没有明确说明,一项技术成为标准要经过哪些阶段,他们实行的是现行标准 (Living Standard),标准由相关负责人维护升级,并由开发者或浏览器厂商提议将新功能加入标准,这一协作过程通过 Github 的 Issues 来讨论。如果仔细看 WHATWG 和 W3C 制定的标准,会发现有些标准互有重叠,两个组织都有制定相同的技术标准,比如 DOM 标准。有一些标准会在开头说明:“该标准已经不由我们来维护,请查看某某组织的最新标准”。但是其他一些标准并没有这样的说明,至于参考哪一个标准,我的方法是查看 MDN 相关技术文档下附加的标准规范链接。ECMAECMA 组织负责很多与信息化相关的技术标准,其中应用最广的就是 TC39 委员会负责的 ECMAScript 标准,这标准的实现就是 JavaScript。通过 TC39 成员的工作手册可以看出,每一项对 ECMAScript 标准的更新,需要经过 5 个阶段。Strawman (Stage 0):提案纳入考虑中Proposal (Stage 1):明确提案的好处,以及可能带来的风险Draft (Stage 2):使用正式的规范语言描述语法和语义Candidate (Stage 3):根据使用者反馈进行改良Finished (Stage 4):准备正式加入 ECMAScript 标准现在正在进行的提案可以在仓库 tc39 proposals 查看,从中能够学习到最新的语法并参与讨论。IETFIETF (The Internet Engineering Task Force) 组织主要负责制定互联网基础架构的标准,比如 TCP/IP 和 FTP 协议。The Unicode ConsortiumThe Unicode Consortium 组织负责 Unicode 标准,正如他们所说,“我们为每个字符提供一个唯一的编号,无论平台是什么,无论程序是什么,无论语言是什么”。总结对一般 Web 开发来说,我们用不上晦涩难懂的标准文档。但学习标准我们可以收获很多,比如解决一些棘手的 bug,获取第一手学习资源,全面深入地理解相关技术,了解技术的发展前沿。如果你喜欢这篇文章,请关注我,我会持续输出更多原创且高质量的内容。原文链接:了解 Web 标准规范和组织 ...

April 19, 2019 · 1 min · jiezi

7个javascript实用小技巧

每种编程语言都有一些“黑魔法”或者说小技巧,JS也不例外,大部分是借助ES6或者浏览器新特性实现。下面介绍的7个实用小技巧,相信其中有些你一定用过。数组去重const j = […new Set([1, 2, 3, 3])]>> [1, 2, 3]数组清洗洗掉数组中一些无用的值,如 0, undefined, null, false 等myArray .map(item => { // … }) // Get rid of bad values .filter(Boolean);创建空对象我们可以使用对象字面量{}来创建空对象,但这样创建的对象有隐式原型__proto__和一些对象方法比如常见的hasOwnProperty,下面这个方法可以创建一个纯对象。let dict = Object.create(null);// dict.proto === “undefined”// No object properties exist until you add them该方法创建的对象没有任何的属性及方法合并对象JS中我们经常会有合并对象的需求,比如常见的给用传入配置覆盖默认配置,通过ES6扩展运算符就能快速实现。const person = { name: ‘David Walsh’, gender: ‘Male’ };const tools = { computer: ‘Mac’, editor: ‘Atom’ };const attributes = { handsomeness: ‘Extreme’, hair: ‘Brown’, eyes: ‘Blue’ };const summary = {…person, …tools, …attributes};/Object { “computer”: “Mac”, “editor”: “Atom”, “eyes”: “Blue”, “gender”: “Male”, “hair”: “Brown”, “handsomeness”: “Extreme”, “name”: “David Walsh”,}/设置函数必传参数借助ES6支持的默认参数特性,我们可以将默认参数设置为一个执行抛出异常代码函数返回的值,这样当我们没有传参时就会抛出异常终止后面的代码运行。const isRequired = () => { throw new Error(‘param is required’); };const hello = (name = isRequired()) => { console.log(hello ${name}) };// This will throw an error because no name is providedhello();// This will also throw an errorhello(undefined);// These are good!hello(null);hello(‘David’);解构别名ES6中我们经常会使用对象结构获取其中的属性,但有时候会想重命名属性名,以避免和作用域中存在的变量名冲突,这时候可以为解构属性名添加别名。const obj = { x: 1 };// Grabs obj.x as { x }const { x } = obj;// Grabs obj.x as as { otherName }const { x: otherName } = obj;获取查询字符串参数以前获取URL中的字符串参数我们需要通过函数写正则匹配,现在通过URLSearchParamsAPI即可实现。// Assuming “?post=1234&action=edit"var urlParams = new URLSearchParams(window.location.search);console.log(urlParams.has(‘post’)); // trueconsole.log(urlParams.get(‘action’)); // “edit"console.log(urlParams.getAll(‘action’)); // [“edit”]console.log(urlParams.toString()); // “?post=1234&action=edit"console.log(urlParams.append(‘active’, ‘1’)); // “?post=1234&action=edit&active=1"随着Javascript的不断发展,很多语言层面上的不良特性都在逐渐移除或者改进,如今的一行ES6代码可能等于当年的几行代码。拥抱JS的这些新变化意味着前端开发效率的不断提升,以及良好的编码体验。当然不管语言如何变化,我们总能在编程中总结一些小技巧来精简代码。本文首发于公众号「前端新视界」,如果你也有一些Javascript的小技巧,欢迎在下方留言,和大家一起分享哦! ...

April 17, 2019 · 1 min · jiezi

JavaScript 是如何工作的:JavaScript 的内存模型

这是专门探索 JavaScript 及其所构建的组件的系列文章的第 21 篇。如果你错过了前面的章节,可以在这里找到它们:JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述!JavaScript 是如何工作的:深入V8引擎&编写优化代码的5个技巧!JavaScript 是如何工作的:内存管理+如何处理4个常见的内存泄漏!JavaScript 是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!JavaScript 是如何工作的:深入探索 websocket 和HTTP/2与SSE +如何选择正确的路径!JavaScript 是如何工作的:与 WebAssembly比较 及其使用场景!JavaScript 是如何工作的:Web Workers的构建块+ 5个使用他们的场景!JavaScript 是如何工作的:Service Worker 的生命周期及使用场景!JavaScript 是如何工作的:Web 推送通知的机制!JavaScript 是如何工作的:使用 MutationObserver 跟踪 DOM 的变化!JavaScript 是如何工作的:渲染引擎和优化其性能的技巧!JavaScript 是如何工作的:深入网络层 + 如何优化性能和安全!JavaScript 是如何工作的:CSS 和 JS 动画底层原理及如何优化它们的性能!JavaScript 是如何工作的:解析、抽象语法树(AST)+ 提升编译速度5个技巧!JavaScript 是如何工作的:深入类和继承内部原理+Babel和 TypeScript 之间转换!JavaScript 是如何工作的:存储引擎+如何选择合适的存储API!JavaScript 是如何工作的:Shadow DOM 的内部结构+如何编写独立的组件!JavaScript 是如何工作的:WebRTC 和对等网络的机制!JavaScript 是如何工作的:编写自己的 Web 开发框架 + React 及其虚拟 DOM 原理!JavaScript 是如何工作的:模块的构建以及对应的打包工具// 声明一些变量并初始化它们var a = 5let b = ‘xy’const c = true// 分配新值a = 6b = b + ‘z’c = false // 类型错误:不可对常量赋值作为程序员,声明变量、初始化变量(或不初始化变量)以及稍后为它们分配新值是我们每天都要做的事情。但是当这样做的时候会发生什么呢? JavaScript 如何在内部处理这些基本功能? 更重要的是,作为程序员,理解 JavaScript 的底层细节对我们有什么好处。下面,我打算介绍以下内容:JS 原始数据类型的变量声明和赋值JavaScript内存模型:调用堆栈和堆JS 引用类型的变量声明和赋值let vs constJS 原始数据类型的变量声明和赋值让我们从一个简单的例子开始。下面,我们声明一个名为myNumber的变量,并用值23初始化它。let myNumber = 23当执行此代码时,JS将执行:为变量(myNumber)创建唯一标识符(identifier)。在内存中分配一个地址(在运行时分配)。将值 23 存储在分配的地址。虽然我们通俗地说,“myNumber 等于 23”,更专业地说,myNumber 等于保存值 23 的内存地址,这是一个值得理解的重要区别。如果我们要创建一个名为 newVar 的新变量并把 myNumber 赋值给它。let newVar = myNumber因为 myNumber 在技术上实际是等于 “0012CCGWH80”,所以 newVar 也等于 “0012CCGWH80”,这是保存值为23的内存地址。通俗地说就是 newVar 现在的值为 23。因为 myNumber 等于内存地址 0012CCGWH80,所以将它赋值给 newVar 就等于将0012CCGWH80 赋值给 newVar。现在,如果我这样做会发生什么:myNumber = myNumber + 1myNumber的值肯定是 24。但是newVar的值是否也为 24 呢?,因为它们指向相同的内存地址?答案是否定的。由于JS中的原始数据类型是不可变的,当 myNumber + 1 解析为24时,JS 将在内存中分配一个新地址,将24作为其值存储,myNumber将指向新地址。这是另一个例子:let myString = ‘abc’myString = myString + ’d’虽然一个初级 JS 程序员可能会说,字母d只是简单在原来存放adbc内存地址上的值,从技术上讲,这是错的。当 abc 与 d 拼接时,因为字符串也是JS中的基本数据类型,不可变的,所以需要分配一个新的内存地址,abcd 存储在这个新的内存地址中,myString 指向这个新的内存地址。下一步是了解原始数据类型的内存分配位置。JavaScript 内存模型:调用堆栈和堆JS 内存模型可以理解为有两个不同的区域:调用堆栈(call stack)和堆(heap)。调用堆栈是存放原始数据类型的地方(除了函数调用之外)。上一节中声明变量后调用堆栈的粗略表示如下。在上图中,我抽象出了内存地址以显示每个变量的值。 但是,不要忘记实际上变量指向内存地址,然后保存一个值。 这将是理解 let vs. const 一节的关键。堆是存储引用类型的地方。跟调用堆栈主要的区别在于,堆可以存储无序的数据,这些数据可以动态地增长,非常适合数组和对象。JS 引用类型的变量声明和赋值让我们从一个简单的例子开始。下面,我们声明一个名为myArray的变量,并用一个空数组初始化它。let myArray = []当你声明变量“myArray”并为其指定非原始数据类型(如“[]”)时,以下是在内存中发生的情况:为变量创建唯一标识符(“myArray”)在内存中分配一个地址(将在运行时分配)存储在堆上分配的内存地址的值(将在运行时分配)堆上的内存地址存储分配的值(空数组[])从这里,我们可以 push, pop,或对数组做任何我们想做的。myArray.push(“first”)myArray.push(“second”)myArray.push(“third”)myArray.push(“fourth”)myArray.pop()let vs const一般来说,我们应该尽可能多地使用const,只有当我们知道某个变量将发生改变时才使用let。让我们明确一下我们所说的“改变”是什么意思。let sum = 0sum = 1 + 2 + 3 + 4 + 5let numbers = []numbers.push(1)numbers.push(2)numbers.push(3)numbers.push(4)numbers.push(5)这个程序员使用let正确地声明了sum,因为他们知道值会改变。但是,这个程序员使用let错误地声明了数组 numbers ,因为他将把东西推入数组理解为改变数组的值。解释“改变”的正确方法是更改内存地址。let 允许你更改内存地址。const 不允许你更改内存地址。const importantID = 489importantID = 100 // 类型错误:赋值给常量变量让我们想象一下这里发生了什么。当声明importantID时,分配了一个内存地址,并存储489的值。记住,将变量importantID看作等于内存地址。当将100分配给importantID时,因为100是一个原始数据类型,所以会分配一个新的内存地址,并将100的值存储这里。然后 JS 尝试将新的内存地址分配给 importantID,这就是抛出错误的地方,这也是我们想要的行为,因为我们不想改变这个 importantID的值。当你将100分配给importantID时,实际上是在尝试分配存储100的新内存地址,这是不允许的,因为importantID是用const声明的。如上所述,假设的初级JS程序员使用let错误地声明了他们的数组。相反,他们应该用const声明它。这在一开始看起来可能令人困惑,我承认这一点也不直观。初学者会认为数组只有在我们可以改变的情况下才有用,const 使数组不可变,那么为什么要使用它呢? 请记住:“改变”是指改变内存地址。让我们深入探讨一下为什么使用const声明数组是完全可以的。const myArray = []在声明 myArray 时,将在调用堆栈上分配内存地址,该值是在堆上分配的内存地址。堆上存储的值是实际的空数组。想象一下,它是这样的:如果我们这么做:myArray.push(1)myArray.push(2)myArray.push(3)myArray.push(4)myArray.push(5)执行 push 操作实际是将数字放入堆中存在的数组。而 myArray 的内存地址没有改变。这就是为什么虽然使用const声明了myArray,但没有抛出任何错误。myArray 仍然等于 0458AFCZX91,它的值是另一个内存地址22VVCX011,它在堆上有一个数组的值。如果我们这样做,就会抛出一个错误:myArray = 3由于 3 是一个原始数据类型,因此生成一个新的调用堆栈上的内存地址,其值为 3,然后我们将尝试将新的内存地址分配给 myArray,由于myArray是用const声明的,所以这是不允许的。另一个会抛出错误的例子:myArray = [‘a’]由于[a]是一个新的引用类型的数组,因此将分配调用堆栈上的一个新内存地址,并存储堆上的一个内存地址的值,其它值为 [a]。然后,我们尝试将调用堆栈内存地址分配给 myArray,这会抛出一个错误。对于使用const声明的对象(如数组),由于对象是引用类型,因此可以添加键,更新值等等。const myObj = {}myObj[’newKey’] = ‘someValue’ // 这不会抛出错误为什么这些知识对我们有用呢JavaScript 是世界上排名第一的编程语言(根据GitHub和Stack Overflow的年度开发人员调查)。 掌握并成为“JS忍者”是我们所有人都渴望成为的人。任何质量好的的 JS 课程或书籍都提倡使用let, const 来代替 var,但他们并不一定说出原因。 对于初学者来说,为什么某些 const 变量在“改变”其值时会抛出错误而其他 const变量却没有。 对我来说这是有道理的,为什么这些程序员默认使用let到处避免麻烦。但是,不建议这样做。谷歌拥有世界上最好的一些程序员,在他们的JavaScript风格指南中说,使用 const 或 let 声明所有本地变量。默认情况下使用 const,除非需要重新分配变量,不使用 var 关键字(原文)。虽然他们没有明确说明原因,但据我所知,有几个原因先发制人地限制未来的 bug。使用 const 声明的变量必须在声明时初始化,这迫使程序员经常在范围方面更仔细地放置它们。这最终会导致更好的内存管理和性能。要通过代码与任何可能遇到它的人交流,哪些变量是不可变的(就JS而言),哪些变量可以重新分配。希望上面的解释能帮助你开始明白为什么或者什么时候应该在代码中使用 let 和 const 。你的点赞是我持续分享好东西的动力,欢迎点赞!欢迎加入前端大家庭,里面会经常分享一些技术资源。 ...

April 15, 2019 · 2 min · jiezi

Web前端必备-Nginx知识汇总

一、Nginx简介Nginx是一个高性能、轻量级的Web和反向代理服务器, 其特点是占有内存及资源少、抗并发能力强。Nginx安装简单、配置简洁、启动快速便捷、支持热部署、支持 SSL、拥有高度模块化的设计。Nginx的主要功能有:Web服务器反向代理负载均衡二、运行和控制Nginx备注: 以下命令中的 /usr/local/nginx 是nginx二进制文件的绝对路径,需根据自己实际的安装路径而定。1.启动/usr/local/nginx/sbin/nginx2.重新打开日志文件/usr/local/nginx/sbin/nginx -s reopen3.重新载入配置文件/usr/local/nginx/sbin/nginx -s reload4.停止/usr/local/nginx/sbin/nginx -s stop5.从容停止(1) 查看进程号ps -ef|grep nginx(2) 杀死进程kill -QUIT <进程号> 或 kill -TERM <进程号>6.强制停止pkill -9 nginx三、Nginx作为Web服务器Nginx作为Web服务器, 需要定义server虚拟主机,让这些虚拟主机去处理对于特定域名或IP地址的请求。每个server虚拟主机都定义了 location 指令,location 定义了对于指定的一组 URI 是如何匹配和进行处理的。1.web服务器基本实例server { listen 80; server_name www.example.com; location / { root /usr/local/www; index index.html; } }参数说明:server 代表1个虚拟主机,可以有多个server_name 匹配请求的指定域名或IP地址location 配置请求的路由,匹配对应的URIroot 查找资源的路径(文件夹目录)index 默认查找2.location匹配规则(请求过滤)(1) 语法server { location 表达式 { }}(2) location表达式的类型@ 它定义一个命名的 location,使用在内部定向时,例如 error_page, try_files/ 通用匹配,任何请求都会匹配到= 开头, 表示精确匹配, 只有请求的url路径与=后面的字符串完全相等才会匹配到(优先级最高)^~ 表示普通字符匹配。使用前缀匹配。如果匹配成功,则不再匹配其他location~ 开头表示区分大小写的正则匹配~* 开头表示不区分大小写的正则匹配(3) location表达式的优先级= 的优先级最高。一旦匹配成功,则不再查找其他匹配项。^ 类型表达式。一旦匹配成功,则不再查找其他匹配项。 和 ~ 的优先级次之。如果有多个location的正则能匹配的话,则使用正则表达式最长的那个。常规字符串匹配类型。按前缀匹配。3.URL重写URL重写是指: 当请求的URL满足事先定义好的规则时, 将跳转/定向到某个规则,比如常见的伪静态、301重定向、浏览器定向等。(1) 语法server { rewrite 规则 定向路径 重写类型;}rewrite参数说明:规则:字符串或者正则来表示想匹配的目标url定向路径:匹配到规则后要定向的路径,如果规则里有正则,则可以使用$index来表示正则里的捕获分组重写类型:last :表示完成rewrite,浏览器地址栏URL地址不变break;本条规则匹配完成后,终止匹配,不再匹配后面的规则,浏览器地址栏URL地址不变redirect:返回302临时重定向,浏览器地址会显示跳转后的URL地址permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址(2) 示例域名跳转: 访问 www.aaa.com 跳转到 www.bbb.comserver { listen 80; server_name www.aaa.com; location / { rewrite ^/$ www.bbb.com permanent ; }}4.try_filestry_files是指: 按顺序检查文件是否存在,返回第一个找到的文件。如果所有的文件都找不到,会进行一个内部重定向到最后一个参数.(1) 语法try_files file1 files2 … uri参数说明:最后一个参数是回退URI, 且必须存在,否则将会出现内部500错误。只有最后一个参数可以引起一个内部重定向,之前的参数只设置内部URI的指向。最后一个参数也可以是一个命名的location。最后一个参数如果不是命名的location那么$args不会自动保留,如果你想保留$args,必须在最后一个参数里明确声明。示例为:try_files $uri $uri/ /index.php?q=$uri&$args;(2) 示例跳转到文件当访问:www.example.com/test 时会依次查找,若 1.html,2.html 都不存在,最终返回 3.htmlserver { listen 80; server_name www.example.com; root html; index index.html; location /test { try_files /1.html /2.html /3.html; }}跳转到变量当访问:www.example.com/test 时会依次查找,若 1.html,2.html 都不存在,则跳转到命名为abc的locationserver { listen 80; server_name www.example.com; root html; index index.html; location /test { try_files /1.html /2.html @abc; } location @abc{ rewrite ^/(.)$ http://www.example2.com; }}vue-router设置HTML5 History 模式时, nginx的配置如下:location / { # URL 匹配不到任何静态资源,返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。 try_files $uri $uri/ /index.html;}5.Gzip配置server { # 开启gzip 压缩 gzip on; # 设置gzip所需的http协议最低版本 (HTTP/1.1, HTTP/1.0) gzip_http_version 1.1; # 设置压缩级别(1-9), 值越大压缩率越高,同时消耗cpu资源也越多,建议设置在4左右 gzip_comp_level 4; # 设置压缩的最小字节数, 页面Content-Length获取 gzip_min_length 1000; # 设置压缩文件的类型 (text/html), 不建议压缩图片(如jpg、png本身已压缩) gzip_types text/plain application/javascript text/css; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持) gzip_disable “MSIE [1-6].”;}6.https配置http { # 配置共享会话缓存大小,视站点访问情况设定 ssl_session_cache shared:SSL:10m; # 配置会话超时时间 ssl_session_timeout 10m; server { listen 443; server_name www.example.com; ssl on; # 设置长连接 keepalive_timeout 70; # HSTS策略 add_header Strict-Transport-Security “max-age=31536000; includeSubDomains; preload” always; # 证书文件 ssl_certificate www.example.com.crt; # 私钥文件 ssl_certificate_key www.example.com.key; # 优先采取服务器算法 ssl_prefer_server_ciphers on; # 指定SSL协议 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 定义算法 ssl_ciphers “EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4”; # 减少点击劫持 add_header X-Frame-Options DENY; # 禁止服务器自动解析资源类型 add_header X-Content-Type-Options nosniff; # 防XSS攻擊 add_header X-Xss-Protection 1; }}四、Nginx作为反向代理服务器server { listen 80; server_name www.example.com; root html; index index.html; location /test { # 请求host proxy_set_header Host $http_host; # 请求ip proxy_set_header X-Real-IP $remote_addr; # 请求协议 proxy_set_header X-Scheme $scheme; # 代理服务器 proxy_pass http://localhost:3000; }}当访问http://www.example.com/test时, nginx会将请求转发到http://localhost:3000上。五、Nginx作为负载均衡1.负载均衡的介绍在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独一个服务器压力过大,将来自用户的请求转发给不同的服务器。负载均衡用于从 “upstream” 模块定义的后端服务器列表中选取一台服务器接受用户的请求。2.负载均衡的基本实例(1) upstream模块一个最基本的upstream模块如下:#动态服务器组, server是后端服务器,my_server是自定义的服务器组名称。upstream my_server { server localhost:8001; server localhost:8002; server localhost:8003;}(2) 反向代理在upstream模块配置完成后,要让指定的访问反向代理到服务器组。server { listen 80; server_name www.example.com; root html; index index.html; location / { # 反向代理到定义好的服务器组my_server proxy_pass my_server; }}(3)完整配置http { upstream my_server { server localhost:8001; server localhost:8002; server localhost:8003; } server { listen 80; server_name www.example.com; root html; index index.html; location / { # 反向代理到定义好的服务器组my_server proxy_pass my_server; } }}3. 负载均衡策略(1) 轮询(默认方式)表示每个请求按时间顺序逐一分配到不同的后端服务器。upstream my_server { server localhost:8001; server localhost:8002;}(2) weight(权重方式)表示在轮询策略的基础上指定轮询的服务器的权重,默认为1,权重越高分配到需要处理的请求越多。upstream my_server { server localhost:8001 weight=1; server localhost:8002 weight=2;}(3) ip_hash表示指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。upstream my_server { ip_hash; server localhost:8001; server localhost:8002;}备注:在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。ip_hash不能与backup同时使用。此策略适合有状态服务,比如session。当有服务器需要剔除,必须手动down掉。(4) least_conn表示把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。upstream my_server { least_conn; server localhost:8001; server localhost:8002;}(5) down表示当前的server暂时不参与负载均衡。upstream my_server { server localhost:8001 down; server localhost:8002; server localhost:8003;}(6) backup表示预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因 此这台机器的压力最轻。upstream my_server { server localhost:8001 backup; server localhost:8002; server localhost:8003;} ...

April 13, 2019 · 3 min · jiezi

Web 性能优化:Preload,Prefetch的使用及在 Chrome 中的优先级

这是 Web 性能优化的第 6 篇,上一篇在下面看点击查看:Web 性能优化:使用 Webpack 分离数据的正确方法Web 性能优化:图片优化让网站大小减少 62%Web 性能优化:缓存 React 事件来提高性能Web 性能优化:21种优化CSS和加快网站速度的方法Web 性能优化:理解及使用 JavaScript 缓存今天,我们将深入研究Chrome 的网络栈,以明确 web 加载原语(如<link rel= preload > & <link rel= prefetch >) 背后的工作原理,以便你能够更有效地使用它们。如其他文章所述,preload 是一个声明式 fetch,可以强制浏览器在不阻塞 document 的 onload 事件的情况下请求资源。Prefetch 告诉浏览器这个资源将来可能需要,但是什么时间加载这个资源是由浏览器来决定的。在预加载(perload)之前,网络请求从这里开始,预加载之后,它在解析时从左向右移动使用预加载(perload)的一些案例在详细介绍 预加载(perload) 之前,先来看看一些使用 预加载(perload) 的案例。Housing.com 在对他们的渐进式 Web 应用程序的脚本转用 proload 看到大约缩短了10%的可交互时间。Shopify 使用 preload 加载 Web字体后,Chrome 桌面版)的文本绘制时间(1.2秒)提高了50%,这完全解决了他们的文字闪动问题。左边:使用 preload,右边:不使用 preload使用<link rel=”preload”> 加载字体Treebo,印度最大的旅馆网站之一,在 3G 网络下对其桌面版试验,在对其顶部图片和主要的 Webpack 打包文件使用 preload 之后,在首屏绘制和可交互延迟分别减少了 1s。同样的,在对自己的渐进式 Web 应用程序主要打包文件使用 preload 之后,Flipkart 在路由解析之前 节省了大量的主线程空闲时间(在 3G 网络下的低性能手机下)。上面:没有使用 proload 加载,下面:使用 preload 加载Chrome 数据保护程序团队发现,对于那些可以在脚本和 CSS 样式表上使用 preload 的页面,发现页面首次绘制时间获得平均 12% 的速度提升。对于 prefetch(预读取),它被广泛使用,在 Google 我们仍用它来预读取一些可以加快 搜索结果页面 的渲染的关键资源。Preload 在大型网站中都有很好运用,你可以在本文后面找到更多这些安全。 在此之前,让我们深入了解网络堆栈如何实际处理 预加载(prefetch)与预读取(prefetch)。何时使用 <link rel=”preload”> 和 <link rel=”prefetch”> ?提示:preload 加载资源一般是当前页面需要的,prefetch 一般是其它页面有可能用到的资源。preload 是告诉浏览器预先请求当前页面需要的资源(关键的脚本,字体,主要图片等)。prefetch 应用场景稍微又些不同 —— 用户将来可能跳转到其它页面需要使用到的资源。如果 A 页面发起一个 B 页面的 prefetch 请求,这个资源获取过程和导航请求可能是同步进行的,而如果我们用 preload 的话,页面 A 离开时它会立即停止。在 preload 和 prefetch 之间,我们对当前页面或即将跳转的页面在所需主要资源的问题有了一个解决方案。<link rel=“preload”> 和 <link rel=“prefetch”> 的缓存行为当资源被 preload 或者 prefetch 后,会从网络堆栈传输到 HTTP 缓存并进入渲染器的内存缓存。 如果资源可以被缓存(例如,存在有效的 cache-control 和 max-age),它将存储在 HTTP 缓存中,可用于当前和未来的会话。 如果资源不可缓存,则不会将其存储在 HTTP 缓存中。 相反,它会被缓存到内存缓存中并保持不变直到它被使用。Chrome 的网络栈中是如何处理 preload 和 prefetch 的优先级?下面是在 Blink 内核的 Chrome 46 及更高版本中不同资源的加载优先级情况著作权归作者所有。preload 用 “as” 或者用 “type” 属性来表示他们请求资源的优先级(比如说 preload 使用 as=“style” 属性将获得最高的优先级)。没有 “as” 属性的将被看作异步请求,“Early”意味着在所有未被预加载的图片请求之前被请求(“late”意味着之后)我们来谈一下这张表。脚本根据它们在文件中的位置是否异步、延迟或阻塞获得不同的优先级:网络在第一个图片资源之前阻塞的脚本在网络优先级中是中级网络在第一个图片资源之后阻塞的脚本在网络优先级中是低级异步/延迟/插入的脚本(无论在什么位置)在网络优先级中是很低级图像在可视窗口中比不在视口中的图像(具有更高的优先级,因此在某种程度上, Chrome 将会尽量懒加载这些不在视口中的图片。 较低优先级的图片出现在视口中时,该图片的优先级就会得到提升(但是注意已经在布局完成后的图片优先级不会在更改)。使用“as”属性预加载的资源将具有与它们请求的资源类型相同的资源优先级。 例如,preload as =“style”将获得最高优先级,而as =“script”将获得低优先级或中优先级。 这些资源也遵循相同的CSP策略(例如脚本受 script-src 约束)。不带 “as” 属性的 preload 的优先级将会等同于异步请求。如果你想了解各种资源加载时的优先级属性,从开发者工具的 Timeline/Performance 区域的 Network 区域都能看到相关信息:在 Network 面板下的 “Priority” 部分当页面 preload 已经在 Service Worker 缓存及 HTTP 缓存中的资源时会发生什么?这各情况来说是比较少的,但通常来说,会是比较好的情况 —— 如果资源没有超出 HTTP 缓存时间或者 Service Worker 没有主动重新发起请求,那么浏览器就不会再去请求这个资源了。如果资源在 HTTP 缓存中(在SW缓存和网络之间),那么 preload 会从相同的资源中获得缓存命中。这种加载方式会浪费用户的带宽吗使用 preload 或 prefetch,可能会浪费用户的带宽,特别是在资源没有缓存的情况下。没有用到的 preload 资源在 Chrome 的 console 里会在 onload 事件 3s 后发生警告。这个警告的原因是,你可能正在使用preload来尝试为其他资源预加载并缓存以提高性能,但是如果这些预加载的资源没有被使用,那么你就在毫无理由地做额外的工作。在移动设备上,这相当于浪费用户的流量,所以要注意预加载的内容。什么情况会导致二次获取?preload 和 prefetch 是很简单的工具,你很容易不小心二次获取。不要用 “prefetch” 作为 “preload” 的后备方案 ,它们适用于不同的场景,常常会导致不符合预期的二次获取。使用 preload 来获取当前需要任务否则使用 prefetch 来获取将来的任务,不要一起用。对 preload 使用 “as” 属性,不然将不会从中获益。如果在指定要 preload 的内容(例如脚本)时未提供有效的“as”,则最终将获取两次。preload 字体不带 crossorigin 也将会二次获取, 确保在使用 preload 获取字体时添加crossorigin 属性,否则将二次下载。 他这个请求使用匿名的跨域模式。 即使字体与页面位于同个域 下,也建议使用。也适用于其他域名的获取(比如说默认的异步获取)。最后,虽然它不会导致两次获取,但这通常是一个很好的建议:不要所有的请求资源都加 preload,用 preload 来告诉浏览器一些很被需要的资源,以便让它提早获取它们。我应当在页面头部所有的资源都加上 preload?这是工具的一个很好的例子,而不是规则。 preload 的文件数量取决于加载其他资源时网络内容、用户的带宽和其他网络状况。尽早 preload 页面中可能需要的文件,对于脚本,preload 你的密钥包是很好的,因为它将获取与执行分开,而仅仅使用 <script async> 不会这样做,因为它会阻止窗口的 onload 事件。你可以 preload 图像、样式、字体和媒体。最重要的是,作为一名页面作者,你可以更好地控制提前获取页面所需要的信息。prefetch 是否具有你应该注意的任何魔法属性? 是的,在 Chrome 中,如果用户导航离开一个页面,而对其他页面的预取请求仍在进行中,这些请求将不会被终止。此外,无论资源的可缓存性如何,prefetch 请求在未指定的网络堆栈缓存中至少保存 5 分钟。我在 JS 中使用自定义的 “preload”,它跟原本的 rel=“preload” 或者 preload 头部有什么不同?preload 解耦从 JS 处理和执行中获取资源。 因此,preload 在标记中声明以被 Chrome preload 扫描器扫描。 这意味着在许多情况下,在 HTML 解析器甚至到达标签之前,将获取预加载(具有指示的优先级),这使它比自定义预加载实现更强大。不是可以用 HTTP/2 的服务器推送来代替 preload 吗?当你知道资源的精确加载顺序时使用推送,并让 service worker 拦截可能导致再次推送缓存资源的请求。 使用 preload 可以使资源的开始下载时间更接近初始请求 - 这对所有的资源获取都有用。我们假设浏览器正在加载一个页面,页面中有个 CSS 文件,CSS 文件又引用一个字体库,对于这样的场景,若使用 HTTP/2 PUSH,当服务端获取到 HTML 文件后,知道以后客户端会需要字体文件,它就立即主动地推送这个文件给客户端,如下图:而对于 preload,服务端就不会主动地推送字体文件,在浏览器获取到页面之后发现 preload 字体才会去获取,如下图:虽然推送很有效,但它不像 preload 那样对所有的情况都适应。推送不能用于第三方资源的内容,通过立即发送资源,它还有效地缩短浏览器自身的资源优先级情况。在你明确的知道在做什么时,这应该会提高你的应用性能,如果不是很清晰的话,你也许会损失掉部分的性能。peload 请求头是什么?它与 preload 标签相比如何?它与 HTTP/2 服务器推送有什么关系?与其他类型的链接一样,preload 链接即可以使用 HTML标记 或 HTTP标头。 在任何一种情况下,preload 链接都会指示浏览器开始将资源加载到内存缓存中,这表明该页面有很高可能性使用该资源,并且不希望等待预加载扫描程序或解析程序发现它。当金融时报在它们的网站使用 preload HTTP 头时,他们节约了大约 1s 的显示片头图片时间。1: 没有使用 preload 2:使用了 preload你可以使用任何一种形式提供 preload 链接,但是你应该知道一个重要区别:如规范所允许的,许多服务器在遇到 HTTP 头的 preload 链接时会触发 HTTP/2 服务器推送。 HTTP/2 推送的性能影响不同于普通的预加载,所以你要确保没有发起不必要的推送。你可以使用 preload 标签来代替 preload 头以避免不必要的推送,或者在你的 HTTP 头上加一个 “nopush” 属性。如何判断 <link rel=”preload”> 的支持情况?以下的代码段可以判断 <link rel=”preload”>支持情况:const preloadSupported = () => { const link = document.createElement(’link’); const relList = link.relList; if (!relList || !relList.supports) return false; return relList.supports(‘preload’);};FilamentGroup 也有一个 preload 检测器 ,作为他们的异步 CSS 加载库 loadCSS 的一部分。可以使用 preload 让CSS样式立即生效吗?当然可以,preload 支持基于异步加载的标记,使用 <link rel=”preload”> 的样式表可以使用 onload 事件立即应用于当前文档:<link rel=“preload” href=“style.css” onload=“this.rel=stylesheet”>preload 还被哪些网站广泛的应用?根据 HTTPArchive,大多数使用 <link rel =“preload”>的网站使用它来预加载Web字体,包括 Teen Vogue 和前面提到的 Shopify:而 LifeHacker 和 JCPenny 等其他热门网站使用它来异步加载CSS(通过Filament Group loadCSS):然后,有越来越多的渐进式 Web 应用程序(如 Twitter.com mobile、Flipkart 和Housing)使用它来预加载当前导航所需的脚本(使用PRPL等模式)其基本思想是以高粒度维护工件(而不是整体捆绑),所以任何应用都可以按需加载依赖或者预加载资源并放在缓存中。当前浏览器对 preload 和 Prefetch 的支持程序如何根据 CanIUse,<link rel =“preload”>约 50% 的支持度, <link rel =“prefetch”> 约 71%。相关阅读Preload — what is it good for? — Yoav WeissA <link rel=”preload”> study by the Chrome Data Saver teamPlanning for performance — Sam SacconeWebpack plugin for auto-wiring up <link rel=”preload”>What is preload, prefetch and preconnect? — KeyCDNWeb Fonts preloaded by Zach LeatHTTP Caching: cache-control by Ilya Grigorik你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

April 11, 2019 · 3 min · jiezi

Web 开发要知道的三个D #1:声明式 VS 命令式

本文是 《Web 开发要理解的三个D》中的第一个 D,Declarative(声明式) vs. Imperative(命令式)。使用 JavaScript 进行的现代 web 开发在过去十年中不断发展,采用了通过各种库和框架实现的模式和良好实践。虽然很容易被 AngularJS, React, Vue 或 使用 MVVM(Model-View-ViewModel) 模式实现的 KendoUI 这种的框架所吸引,但要记住一点是使开发更容易的基本模式和可重复的实践是很重要的。事实上,在不了解“为何”的情况下,很难对开发堆栈做出有条件的决策。自从上世纪 90 年代中期“万维网”上第一个商业应用程序开始出现以来,作者一直在编写程序或领导团队编写商业网站系列程序。在作者看来,有三个基本概念真正彻底改变了web应用程序的开发方式——作者称之为“web开发的3D”。Declarative(声明式 vs 命令式)Data-binding (数据绑定)Dependency injection (依赖注入)在使用 XAML 和c# 构建 Silverlight 应用程序时,我了解了声明式代码和命令式代码之间的区别。很高兴看到这些概念被移植 到HTML 和 JavaScript 开发中。这些概念有利于管理数据绑定。最后,依赖注入对于管理大型代码库(或者由大型团队管理的中等大小的代码库)至关重要,许多解决方案已经在 JavaScript 中解决了这个问题。在这个简短的系列中,我将总结这些概念,并分享从案例研究中获得的经验教训,这些经验教训说明了它们为何如此重要。请注意,它们不是唯一的概念,但是如果你掌握了它们,我保证你会用不同的眼光重新看待你的项目、工具、库和框架。本文将探讨的第一个概念:声明式与命令式。声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。举一个数据过滤的例子来说明这一点,比如我们要打印下数组中存不存在 3。// 命令式编程做法let res = false;for(i = 0; i < dataArr.length; i++) { if (i === 3) { res = true; }}console.log(res);// 声明式编程做法let res = dataArr.filter(i => i === 3);console.log(res);案例研究: 情感分析项目在详细介绍我所说的声明式方法和命令式方法之前,让我先分享一个来自我个人经验的例子,我认为这个例子说明了为什么对这些方法的深入理解是非常重要的。虽然这个例子是使用 Silverlight 和 c# 构建的,但是同样的方法也可以应用到 JavaScript 应用程序开发中。2009 年底,微软联系了我的公司,帮助他们重写他们正在开发的内部 Silverlight 应用程序。 这是一个非常有效的“臭鼬工程”项目之一,但是需要进行重构,以便为黄金时间做好准备。作为一个没有设计背景开发人员来,我知道我需要设计公司的服务,并协同工作来创建下一代解决方案。该应用程序在当时处于领先地位,因为它聚合了来自各种社交网络平台的数据,然后运行情绪分析这个项目 ,以确定帖子是“正面”还是“负面”,最终使竞选活动的管理能够处理负面情绪。关于如何使用此应用程序的一个很好的例子是,一家汽车制造商发布了大规模的召回。该引擎将收集与召回相关的数据,并生成一个典型的负面基线情绪。然后,该公司将发起一项活动,对消费者进行指导,并向消费者道歉和赔偿,然后衡量对消费者情绪的总体影响。现在有很多平台和引擎可以进行这类分析和活动。这个项目的关键是时间紧迫,所以设计和开发需要并行进行。一个更传统的方法是经历一个设计阶段,提供线框图和各种设计组合,直到团队就经验的具体实现达成一致,然后开始开发。我们没有时间遵循那种方法。幸运的是,我们没有这样做。我在应用程序中记得的一个简单例子是允许用户选择自己喜欢的活动。在原始界面中,用户从下拉列表中选择一个活动,然后这个活动详细信息出现在屏幕上的选择区域。UI最终设计在左窗格中显示每个活动的详细信息,当单击活动“平铺”时,右窗格中的相同细节会亮起。谢天谢地,这需要最小的重构,因为 XAML 使用了声明式的方法,这样团队就能够改变一些简单的标记,而不需要接触一行代码。声明式和命令式定义使用 XAML 和 C# 或 HTML 和 JavaScript来定义命令式和声明式编程非常容易。代码(c#或JavaScript)是必需的。标签(无论是XAML还是HTML)是声明性的。为了进一步说明,你可以使用命令式代码描述如何执行你想要执行的操作。相反,声明性代码使你能够描述想要做什么,而不用担心如何做。例如,在使用 jQuery 的 Kendo UI中,你可以编写以下代码来定义一个文本框,该文本框接受数值,并且不允许字母数字输入或超出定义范围的输入。$("#textbox").kendoNumericTextBox({ value: 10, min: -10, max: 100, step: 0.75, format: “n”, decimals: 3});在该代码中,jQuery被指示找到一个 id 为 textbox 的 DOM元 素,然后调用扩展方法kendoNumericTextBox,并传入配置初始值、范围等的各种参数。这就是如何使用伪命令式的方法将一个简单的DOM 元素转换为 Kendo UI 的小部件。如果你在 Angular 中使用 Kendo UI,你可能会这样做<input kendo-numerictextbox k-min="-10" k-max=“100” k-step=“0.75” k-format=“n” k-decimals=“3”/>该示例采用声明式方法。你只需声明要发生的事情,而不是担心如何转换元素,这应该是一个数字文本框; 它的最小值应为 -10, 等等。这是一个非常重要的区别。 在情绪分析应用程序中,如果团队强制定位下拉列表然后使用列表填充它,则使用一组切片的更改将需要更改UI(XAML)和代码(C#)。 相反,团队只是使用 C# 来构建和公开广告系列列表并跟踪选定的广告系列。 下拉列表中的 XAML 声明了它将使用的一系列广告系列。 当它被重构为列表时,XAML 被更新为ListBox 小部件,该小部件声明了相同的营销活动数组。在使用 jQuery 的 JavaScript 中,命令式方法如下所示:var options = $("#options");$.each(result, function() { options.append($("<option />").val(this.id).text(this.name));});重构为循环并构造 div 标签列表以及绑定到 click 事件。使用 Angular 的命令式方法,原始标签:<select ng-options=“item.name for item in items track by item.id”></select>可以重构为:这样可以在设计中提供更大的灵活性,而无需重新原代码,这样程序员可以在设计更新时转移到其他任务上。设计人员-开发人员工作流你可能参与了利用顺序工作流程的项目。 在设计完成之前,开发基本上被搁置,然后开发人员参与实施设计。 声明式和命令式代码的清晰定义和分离可实现更加并行的工作流程。 这是因为即使数据没有,UI 的实现也会发生变化。最后,要做的大部分事情是将数据转换为信息。 数据本身作为位和字节对用户来说不是很有用,但是,作为图形或图块或输入框呈现,它变得有意义。我想把这个过程进一步分解成三个不同的部分。最终驱动大多数业务应用程序的是数据,但用户界面使其有意义,并应用某些行为来增强体验将这三者分开可确保应用程序的最大灵活性和可维护性。 我发现即使接口还没有设计,几乎总是可以就特定接口需要什么数据达成一致。案例研究:电视节目列表一个完美的例子是,我们的团队与有线电视公司合作,为他们的频道列表构建一个新的体验。我们甚至在他们完成检索信息的 api 之前就开始了这个项目!幸运的是,有许多关于清单的属性是相当通用的。电视频道通常具有与之关联的频道数,编码和描述列表有一个类别(恐怖、纪录片、喜剧等)列表有标题列表可能有与之相关联的参与者我们使用命令式代码来定义数据结构以保存信息,检索信息(最初通过一组模拟或虚拟接口,稍后重构以便在开发后与实时API连接),并公开信息。 这种开发没有任何依赖于完整的 API 或任何级别的设计。在设计团队迭代线框图或对比图时,团队甚至为清单设计了一个简单的网格,以便与之交互。最终设计了网格,并更新了声明性标签以实现设计效果。不需要修改代码,到那时所有的东西都经过了完整的测试。此外,还定义了一组行为。 当用户单击网格中的列表时,应用初始行为以弹出包含列表信息的对话框。 之后,设计团队决定让网格在远离观察者的3D轴上摆动并从边缘滑入对话框会更具视觉吸引力。 这有助于用户在网格中保留其上下文,但为列表启用了更多的空间。对话框和网格都是在声明式标签中定义的。弹出窗口是作为一种可以声明式应用的行为编写的。以 HTML 为例,它可能看起来像这样<div class=“listingTile” popupTarget=“dialog”></div>slide-in 效果控制父网格和对话框,因此写入的行为是这样实现的:<div class=“listingTile” slideParent=“grid” slideTarget=“dialog”></div>编写命令式代码来实现该行为。 一旦编写,它就以声明方式应用于使用该行为的任何元素。行为你可能听说过面向切面编程(AOP)这个术语。 它旨在将某些共同关注点或行为与代码分开。 可以在不修改代码的情况下添加不是你正在处理的命令逻辑核心的行为,例如日志记录或身份验证。 这是怎么做到的?你可能一直在使用面向切面的代码,甚至不知道它。考虑以下代码片段public Widget GetWidget(){ if (HttpContext.Current.User.IsInRole(“Widgeteer”)) { return Widget(); }}在该示例中,命令式代码用于授权身份验证的用户检索信息。授权是一个跨领域的问题,这意味着它可以在整个应用程序中使用,而不是针对单个功能。解决授权的一种更常见的方法是使用行为来检查角色,而无需修改代码本身。 你可能写过这样的东西:[Authorize(Roles=“Widgeteer”)]public Widget GetWidget(){ return Widget();}在第二个示例中,方法本身关注返回 Widget 的特定任务。通过对方法应用授权行为来处理授权的交叉问题。这是使用属性完成的。它起初可能看起来不那么令人印象深刻,但现在你可以告诉你的老板你是一个面向切面的程序员,并使用声明式代码来解决你的必要业务逻辑的交叉问题。TypeScript 和 ES7 装饰器ECMAScript7 (又名JavaScript 2016)规范包含了一项关于装饰器的建议,允许注释或元数据修改类和属性。这在TypeScript 中实现,在 Angular 2 中大量使用。在 Angular 2集成的 Kendo UI文档中有一些装饰器的例子。@Component({ selector: ‘my-app’ })@View({ templateUrl: ‘mytemplate.html’ })class MyComponent {}类本身将实现逻辑以公开数据和行为。 Component 声明通知 Angular2 中的类参与数据绑定并可能影响 DOM。 View 声明提供有关为控件注入 DOM 的标签的信息。 标签渲染后,Angular2 使用数据绑定将命令式类与其声明性标签连接起来。声明式编程很奇怪吗?如果你之前没有听说过 map 和 reduce 函数,你的第一感觉,我相信,就会是这样。作为程序员,我们非常习惯去指出事情应该如何运行。“去遍历这个list”,“if 这种情况 then 那样做”,“把这个新值赋给这个变量”。当我们已经知道了如何告诉机器该如何做事时,为什么我们需要去学习这种看起来有些怪异的归纳抽离出来的函数工具?在很多情况中,命令式编程很好用。当我们写业务逻辑,我们通常必须要写命令式代码,没有可能在我们的专项业务里也存在一个可以归纳抽离的实现。但是,如果我们花时间去学习(或发现)声明式的可以归纳抽离的部分,它们能为我们的编程带来巨大的便捷。首先,我可以少写代码,这就是通往成功的捷径。而且它们能让我们站在更高的层面是思考,站在云端思考我们想要的是什么,而不是站在泥里思考事情该如何去做。总结声明式标记和命令式代码的概念非常重要,因为它们不仅影响你如何构建解决方案,还影响团队成员如何协作和并行工作以交付和维护解决方案。在本文中,讨论了这两种方法的不同之处,并分享了一些案例研究来证明它们的好处。在本系列的下一篇文章中,将深入探讨另一个概念:数据绑定,它弥补了声明式标记和命令式代码之间的差距。我将解释为什么我认为支持数据绑定到JavaScript和HTML堆栈的形式化框架的引入是自 2006 年引入 jQuery 以来对 web开发最重要的改进之一。我还将分享案例研究,展示如何用与传统的基于命令式代码的方法大不相同的数据绑定思维来构建应用程序。原文:https://www.telerik.com/blogs…你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

April 10, 2019 · 2 min · jiezi

手把手教程:用Python开发一个自然语言处理模型,并用Flask进行部署

摘要: 实用性教程!教你如何快速创建一个可用的机器学习程序!截住到目前为止,我们已经开发了许多机器学习模型,对测试数据进行了数值预测,并测试了结果。实际上,生成预测只是机器学习项目的一部分,尽管它是我认为最重要的部分。今天我们来创建一个用于文档分类、垃圾过滤的自然语言处理模型,使用机器学习来检测垃圾短信文本消息。我们的ML系统工作流程如下:离线训练->将模型作为服务提供->在线预测。1、通过垃圾邮件和非垃圾邮件训练离线分类器。2、经过训练的模型被部署为服务用户的服务。当我们开发机器学习模型时,我们需要考虑如何部署它,即如何使这个模型可供其他用户使用。Kaggle和数据科学训练营非常适合学习如何构建和优化模型,但他们并没有教会工程师如何将它们带给其他用户使用,建立模型与实际为人们提供产品和服务之间存在重大差异。在本文中,我们将重点关注:构建垃圾短信分类的机器学习模型,然后使用Flask(用于构建Web应用程序的Python微框架)为模型创建API。此API允许用户通过HTTP请求利用预测功能。让我们开始吧!构建ML模型数据是标记为垃圾邮件或正常邮件的SMS消息的集合,可在此处找到。首先,我们将使用此数据集构建预测模型,以准确分类哪些文本是垃圾邮件。朴素贝叶斯分类器是一种流行的电子邮件过滤统计技术。他们通常使用词袋功能来识别垃圾邮件。因此,我们将使用Naive Bayes定理构建一个简单的消息分类器。import pandas as pdimport numpy as npfrom sklearn.feature_extraction.text import CountVectorizerfrom sklearn.model_selection import train_test_splitfrom sklearn.naive_bayes import MultinomialNBfrom sklearn.metrics import classification_reportdf = pd.read_csv(‘spam.csv’, encoding=“latin-1”)df.drop([‘Unnamed: 2’, ‘Unnamed: 3’, ‘Unnamed: 4’], axis=1, inplace=True)df[’label’] = df[‘class’].map({‘ham’: 0, ‘spam’: 1})X = df[‘message’]y = df[’label’]cv = CountVectorizer()X = cv.fit_transform(X) # Fit the DataX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)#Naive Bayes Classifierclf = MultinomialNB()clf.fit(X_train,y_train)clf.score(X_test,y_test)y_pred = clf.predict(X_test)print(classification_report(y_test, y_pred))Naive Bayes分类器不仅易于实现,而且提供了非常好的性能。在训练模型之后,我们都希望有一种方法来保持模型以供将来使用而无需重新训练。为实现此目的,我们添加以下行以将我们的模型保存为.pkl文件供以后使用。from sklearn.externals import joblibjoblib.dump(clf, ‘NB_spam_model.pkl’)我们加载并使用保存的模型:NB_spam_model = open(‘NB_spam_model.pkl’,‘rb’)clf = joblib.load(NB_spam_model)上述过程称为“标准格式的持久模型”,即模型以特定的开发语言的特定格式持久存储。下一步就是将模型在一个微服务中提供,该服务的公开端点用来接收来自客户端的请求。将垃圾邮件分类器转换为Web应用程序在上一节中准备好用于对SMS消息进行分类的代码之后,我们将开发一个Web应用程序,该应用程序由一个简单的Web页面组成,该页面具有允许我们输入消息的表单字段。在将消息提交给Web应用程序后,它将在新页面上呈现该消息,从而为我们提供是否为垃圾邮件的结果。首先,我们为这个项目创建一个名为SMS-Message-Spam-Detector 的文件夹,这是该文件夹中的目录树,接下来我们将解释每个文件。spam.csvapp.pytemplates/ home.html result.htmlstatic/ style.css子目录templates是Flask在Web浏览器中查找静态HTML文件的目录,在我们的例子中,我们有两个html文件:home.html和result.html 。app.pyapp.py文件包含将由Python解释器执行以运行Flask Web应用程序的主代码,还包含用于对SMS消息进行分类的ML代码:from flask import Flask,render_template,url_for,requestimport pandas as pd import picklefrom sklearn.feature_extraction.text import CountVectorizerfrom sklearn.naive_bayes import MultinomialNBfrom sklearn.externals import joblibapp = Flask(name)@app.route(’/’)def home(): return render_template(‘home.html’)@app.route(’/predict’,methods=[‘POST’])def predict(): df= pd.read_csv(“spam.csv”, encoding=“latin-1”) df.drop([‘Unnamed: 2’, ‘Unnamed: 3’, ‘Unnamed: 4’], axis=1, inplace=True) # Features and Labels df[’label’] = df[‘class’].map({‘ham’: 0, ‘spam’: 1}) X = df[‘message’] y = df[’label’] # Extract Feature With CountVectorizer cv = CountVectorizer() X = cv.fit_transform(X) # Fit the Data from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) #Naive Bayes Classifier from sklearn.naive_bayes import MultinomialNB clf = MultinomialNB() clf.fit(X_train,y_train) clf.score(X_test,y_test) #Alternative Usage of Saved Model # joblib.dump(clf, ‘NB_spam_model.pkl’) # NB_spam_model = open(‘NB_spam_model.pkl’,‘rb’) # clf = joblib.load(NB_spam_model) if request.method == ‘POST’: message = request.form[‘message’] data = [message] vect = cv.transform(data).toarray() my_prediction = clf.predict(vect) return render_template(‘result.html’,prediction = my_prediction)if name == ‘main’: app.run(debug=True)1、我们将应用程序作为单个模块运行,因此我们使用参数初始化了一个新的Flask实例,__name__是为了让Flask知道它可以在templates所在的同一目录中找到HTML模板文件夹()。2、接下来,我们使用route decorator(@app.route(’/’))来指定可以触发home 函数执行的URL 。我们的home 函数只是呈现home.htmlHTML文件,该文件位于templates文件夹中。3、在predict函数内部,我们访问垃圾邮件数据集、预处理文本、进行预测,然后存储模型。我们访问用户输入的新消息,并使用我们的模型对其标签进行预测。4、我们使用该POST方法将表单数据传输到邮件正文中的服务器。最后,通过debug=True在app.run方法中设置参数,进一步激活Flask的调试器。5、最后,我们使用run函数执行在服务器上的脚本文件,我们需要确保使用if语句 name == ‘main’。home.html以下是home.html将呈现文本表单的文件的内容,用户可以在其中输入消息:<!DOCTYPE html><html><head> <title>Home</title> <!– <link rel=“stylesheet” type=“text/css” href="../static/css/styles.css"> –> <link rel=“stylesheet” type=“text/css” href="{{ url_for(‘static’, filename=‘css/styles.css’) }}"></head><body> <header> <div class=“container”> <div id=“brandname”> Machine Learning App with Flask </div> <h2>Spam Detector For SMS Messages</h2> </div> </header> <div class=“ml-container”> <form action="{{ url_for(‘predict’)}}" method=“POST”> <p>Enter Your Message Here</p> <!– <input type=“text” name=“comment”/> –> <textarea name=“message” rows=“4” cols=“50”></textarea> <br/> <input type=“submit” class=“btn-info” value=“predict”> </form> </div></body></html>view rawstyle.css文件在home.html的head部分,我们将加载styles.css文件,CSS文件是用于确定HTML文档的外观和风格的。styles.css必须保存在一个名为的子目录中static,这是Flask查找静态文件(如CSS)的默认目录。body{ font:15px/1.5 Arial, Helvetica,sans-serif; padding: 0px; background-color:#f4f3f3;}.container{ width:100%; margin: auto; overflow: hidden;}header{ background:#03A9F4;#35434a; border-bottom:#448AFF 3px solid; height:120px; width:100%; padding-top:30px;}.main-header{ text-align:center; background-color: blue; height:100px; width:100%; margin:0px; }#brandname{ float:left; font-size:30px; color: #fff; margin: 10px;}header h2{ text-align:center; color:#fff;}.btn-info {background-color: #2196F3; height:40px; width:100px;} /* Blue */.btn-info:hover {background: #0b7dda;}.resultss{ border-radius: 15px 50px; background: #345fe4; padding: 20px; width: 200px; height: 150px;}result.html我们创建一个result.html文件,该文件将通过函数render_template(‘result.html’, prediction=my_prediction)返回呈现predict,我们在app.py脚本中定义该文件以显示用户通过文本字段提交的文本。result.html文件包含以下内容:<!DOCTYPE html><html><head> <title></title> <link rel=“stylesheet” type=“text/css” href="{{ url_for(‘static’, filename=‘css/styles.css’) }}"></head><body> <header> <div class=“container”> <div id=“brandname”> ML App </div> <h2>Spam Detector For SMS Messages</h2> </div> </header> <p style=“color:blue;font-size:20;text-align: center;"><b>Results for Comment</b></p> <div class=“results”> {% if prediction == 1%} <h2 style=“color:red;">Spam</h2> {% elif prediction == 0%} <h2 style=“color:blue;">Not a Spam (It is a Ham)</h2> {% endif %} </div></body></html>从result.htm文件我们可以看到一些代码使用通常在HTML文件中找不到的语法例如,{% if prediction ==1%},{% elif prediction == 0%},{% endif %}这是jinja语法,它用于访问从HTML文件中请求返回的预测。我们就要大功告成了!完成上述所有操作后,你可以通过双击appy.py 或从终端执行命令来开始运行API :cd SMS-Message-Spam-Detectorpython app.py你应该得到以下输出:现在你可以打开Web浏览器并导航到http://127.0.0.1:5000/,你应该看到一个简单的网站,内容如下:恭喜!我们现在以零成本的代价创建了端到端机器学习(NLP)应用程序。如果你回顾一下,其实整个过程根本不复杂。有点耐心和渴望学习的动力,任何人都可以做到。所有开源工具都使每件事都成为可能。更重要的是,我们能够将我们对机器学习理论的知识扩展到有用和实用的Web应用程序!完整的工作源代码可在此存储库中找到,祝你度过愉快的一周!本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 9, 2019 · 2 min · jiezi

前端实现在线预览pdf、word、xls、ppt等文件

1、前端实现pdf文件在线预览功能方式一: 通过a标签href属性实现pdf文件理论上可以在浏览器直接打开预览但是需要打开新页面。在仅仅是预览pdf文件且UI要求不高的情况下可以直接通过a标签href属性实现预览<a href=“文档地址”></a>方式二: 通过jquery插件jquery.media.js实现这个插件可以实现pdf预览功能(包括其他各种媒体文件)但是对word等类型的文件无能为力。实现方式: <script type=“text/javascript” src=“jquery-1.7.1.min.js”></script> <script type=“text/javascript” src=“jquery.media.js”></script>html结构: <body> <div id=“handout_wrap_inner”></div> </body>调用方式:<script type=“text/javascript”> $(’#handout_wrap_inner’).media({ width: ‘100%’, height: ‘100%’, autoplay: true, src:‘http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf', }); </script>方式三: 直接通过页面内嵌iframe$("<iframe src=’"+ this.previewUrl +"’ width=‘100%’ height=‘362px’ frameborder=‘1’>").appendTo($(".video-handouts-preview"));此外还可以在iframe标签之间提供一个提示类似这样<iframe :src=“previewUrl” width=“100%” height=“100%">This browser does not support PDFs. Please download the PDF to view it: <a :href=“previewUrl”>Download PDF</a></iframe>方式四: 通过标签嵌入内容<embed :src=“previewUrl” type=“application/pdf” width=“100%” height=“100%">此标签h5特性中包含四个属性:高、宽、类型、预览文件src!与< iframe > < / iframe > 不同,这个标签是自闭合的的,也就是说如果浏览器不支持PDF的嵌入,那么这个标签的内容什么都看不到!方式五: 标签和iframe使用差别较小<object :src=“previewUrl” width=“100%” height=“100%">This browser does not support PDFs. Please download the PDF to view it: <a :href=“previewUrl”>Download PDF</a></object>方式六: PDFObjectPDFObject实际上也是通过标签实现的直接上代码<!DOCTYPE html><html><head> <title>Show PDF</title> <meta charset=“utf-8” /> <script type=“text/javascript” src=‘pdfobject.min.js’></script> <style type=“text/css”> html,body,#pdf_viewer{ width: 100%; height: 100%; margin: 0; padding: 0; } </style></head><body> <div id=“pdf_viewer”></div></body><script type=“text/javascript”> if(PDFObject.supportsPDFs){ // PDF嵌入到网页 PDFObject.embed(“index.pdf”, “#pdf_viewer” ); } else { location.href = “/canvas”; } // 还可以通过以下代码进行判断是否支持PDFObject预览 if(PDFObject.supportsPDFs){ console.log(“Yay, this browser supports inline PDFs.”); } else { console.log(“Boo, inline PDFs are not supported by this browser”); }</script></html>方式七: PDF.js demoPDF.js可以实现在html下直接浏览pdf文档,是一款开源的pdf文档读取解析插件,非常强大,能将PDF文件渲染成Canvas。PDF.js主要包含两个库文件,一个pdf.js和一个pdf.worker.js,一个负责API解析,一个负责核心解析。2、word、xls、ppt文件在线预览功能word、ppt、xls文件实现在线预览的方式比较简单可以直接通过调用微软的在线预览功能实现 (预览前提:资源必须是公共可访问的)<iframe src=‘https://view.officeapps.live.com/op/view.aspx?src=http://storage.xuetangx.com/public_assets/xuetangx/PDF/1.xls' width=‘100%’ height=‘100%’ frameborder=‘1’></iframe>/src就是要实现预览的文件地址//具体文档看这微软接口文档//补充:google的文档在线预览实现同微软(资源必须是公共可访问的)/<iframe :src="‘https://docs.google.com/viewer?url="fileurl"></iframe>3、word文件XDOC可以实现预览以DataURI表示的DOC文档,此外XDOC还可以实现文本、带参数文本、html文本、json文本、公文等在线预览,具体实现方法请看官方文档下面这种方式可以实现快速预览word但是对文件使用的编辑器可能会有一些限制<a href=“http://www.xdocin.com/xdoc?_func=to&_format=html&_cache=1&_xdoc=http://www.xdocin.com/demo/demo.docx" target="_blank” rel=“nofollow”>XDOC</a>4、excel文件目前excel文件已经有了类似pdf.js那样的解析sheet.js总结1、免费纯前端方式实现在线预览word、excel、ppt最优选择微软在线预览(不可编辑)2、利用后端将文件转为图片,前端以图片形式预览(可行方案)3、购买在线预览服务例如百度DOC文档服务、永中、I DOC VIEW等关注微信公众号:生活充电堡 ...

April 9, 2019 · 1 min · jiezi

前端练级攻略(第二部分)

本文是 前端练级攻略 第二部分,第一部分请看下面:前端练级攻略(第一部分)在第二部分,我们将重点学习 JavaScript 作为一种独立的语言,如何向界面添加交互性,JavaScript 设计和架构模式,以及如何构建网络应用程序。就像 HTML 和 CSS一样,有大量的 JavaScript教程。然而,特别是对于一个新手来说,很难弄清楚使用什么样的教程以及以什么样的顺序来学习它们。本系列的主要目标是为你提供一个路线图,帮助你导航学习成为前端开发者。JavaScript基础知识JavaScript 是一种跨平台的编程语言,现在几乎可以用于任何事情。语言在学习如何将JavaScript应用到web之前,首先要了解该语言本身。首先,阅读 Mozilla Developer Network的语言基础速成课程。本教程将教你基本的语言结构,如变量、条件和函数。然后,阅读 MDN 的 JavaScript 指南中的以下部分:Grammar and types (语法和类型)Control flow and error handling (控制流程和错误处理)Loops and iterations (循环和迭代)Functions(函数)不要太担心记住特定的语法,你可以随时查一下文档。相反,应该专注于理解重要的概念,比如变量实例化、循环和函数。如果知识密度难度太大,也没关,先过一篇,你把这些概念付诸实践,当你回过头在来看,可能会理解起来会更加清晰。基于文档的学习可能会过于单调,可以看看 Codecademy 的 JavaScript课程,这课程是理论与实践相结合的,相对会有乐趣一。 此外,如果你有时间,请参阅上面列出的每个概念,阅读 Eloquent JavaScript中的相应章节以加强你的学习。 Eloquent JavaScript 是一本很棒的免费在线书籍,每个有抱负的前端开发人员都应该阅读。交互性现在你已经基本了解了 JavaScript 的语法,下一步就是将它应用到 Web上。 要了解 JavaScript如 何与网站交互,首先你必须了解 文档对象模型(DOM)。DOM 是 HTML 文档的一种表示结构。它是一个树形结构,由对应于 HTML 节点的 JavaScript 对象组成。要进一步了解DOM,请阅读 CSSTricks 的 《什么是DOM》。它提供了对 DOM 的简单而直接的解释。JavaScript 与 DOM 交互以更改和更新它。下面是一个例子,我们选择一个 HTML 元素并更改它的内容var container = document.getElementById(“container”); container.innerHTML = ‘New Content!’;别担心,那只是一个简单的例子。使用 JavaScript DOM 操作,你可以做更多的事情。要了解有关如何使用 JavaScript 与 DO M交互的更多信息,请阅读 MDN 的“文档对象模型”一节中的以下指南。事件及DOMExamples of web and XML development using the DOM如何建立 DOM 树DOM概述使用选择器定位DOM元素再次强调一下,重点是要先理解概念而不是语法,希望能够回答以下问题:DOM 是什么?如何查询元素如何添加事件监听?如何更改 DOM 节点属性?有关常见的 JavaScript DOM 交互的列表,请查看 PlainJS 的 JavaScript 函数和助手。该网站提供了一些例子,说明如何在 HTML 元素上设置样式和附加键盘事件监听器。如果你想深入挖掘,你可以随时阅读 Eloquent 讲的 JavaScript 中关于DOM的部分。 检查要调试浏览器中的JavaScript,我们使用浏览器内置的开发人员工具。 大多数浏览器都提供了 inspector 面板,可以让你查看网页的来源。 你可以在执行时跟踪 JavaScript,将调试语句打印到控制台,以及查看网络请求和资源等内容。里有一个关于使用 Chrome 开发工具的教程。如果你使用的 Firefox,可以查看本教程。实践基础在这一点上,关于JavaScript还有很多东西需要学习。然而,最后一节包含了许多新信息。我想我们该休息一下,做几个实践了。它们有助于巩固你刚刚学到的一些概念。实践 1对于实践1,转到 AirBnB,打开浏览器的页面检查器,然后单击控制台选项卡。在这里,你可以在页面上执行JavaScript。我们要做的是通过操纵页面上的一些元素来获得一些乐趣。看看你是否可以完成以下所有的 DOM 操作。选择具有唯一类名的标题标签并更改文本选择页面上的任何元素并将其删除选择任意元素并更改其CSS属性之一* 选择一个特定的区域标签,并向下移动250像素* 选择任何组件,如面板,并调整其透明度定义一个名为 doSomething 的函数,该函数j里定义一下alert (“Hello world”) 然后执行它选择一个特定的段落标记,向其中注册一个 click 事件,并在每次事件被触发时运行 doSomething如果您遇到困难,请参考 JavaScript 函数和帮助程序指南。这些任务大部分都是基于它。以下是如何完成第一个要点的示例:var header = document.querySelector(‘.text-branding’)header.innerText = ‘Boop’这个实践的主要目的是学习一些关于 JavaScript 和 DOM 操作的知识,并看到它们的实际应用。实践 2使用 CodePen,编写一个使用DOM操作并需要一些编程逻辑来运行的基础JavaScript 实践。这个实践的重点是把你在《前端练级攻略》第 1 部分中学到的一些东西和 JavaScript结合起来。这里有几个可以作为启发的参考例子。Mood Color Generator计算器JavaScript 测试Playable Canvas Asteroids更多的 JavaScript现在你已经了解了一些 JavaScript并进行了一些实践,我们将继续学习一些更高级的概念。下面的概念并不直接相关。我将它们分组在本节中,因为它们对于理解如何构建更复杂的前端系统是必要的。一旦你进入框架部分,你将更好地理解并使用它们。语言当你使用JavaScript进行更多工作时,你将遇到一些更高级别的概念。 以下其中一些概念的列表。 如果有时间,仔细阅读每一个要点 此外,如果你想补充学习其它内容,Eloquent JavaScript 涵盖了大部分内容。继承与原型链作用域事件轮询事件冒泡Apply, call, 和 bind回调函数和 promise变量和函数的提升柯里化命令式和声明JavaScript如何与DOM交互有两种方法:命令式和声明式。一方面,声明式编程关注所发生的事情。另一方面,命令式编程关注的是什么以及如何实现。var hero = document.querySelector(’.hero’)hero.addEventListener(‘click’, function() { var newChild = document.createElement(‘p’) newChild.appendChild(document.createTextNode(‘Hello world!’)) newChild.setAttribute(‘class’, ‘text’) newChild.setAttribute(‘data-info’, ‘header’) hero.appendChild(newChild) })}这是命令式编程的一个例子,我们手动查询一个元素并将 UI 状态存储在 DOM 中。换句话说,专注于如何实现某件事。这段代码最大的问题是它很脆弱。如果处理代码的人将 HTML中 的类名从 hero 更改为villain,事件侦听器将不再触发,因为 DOM 中没有 hero 类。声明式编程解决了这个问题。你不必选择元素,而是将其留给您正在使用的框架或库。这让你专注于做什么而不是如何做。要了解更多信息,请查看 JavaScript的状态——从命令式转换到声明式,以及 Web开发:声明式vs.命令式。Ajax在这些文章和教程中,你可能已经多次看到 Ajax 这个术语。Ajax 是一种允许 web 页面使用 JavaScript 与服务器交互的技术例如,当你在网站上提交表单时,它收集你的输入并发出 HTTP 请求,将数据发送到服务器。当你在Twitter 上发布一条 tweet 时,你的 witter 客户机向 Twitter 的服务器 API 发出 HTTP 请求,并使用服务器响应更新页面。有关 Ajax 的阅读,请查看什么是Ajax。如果你仍然没有完全理解 AJAX 的概念,请看看 Explain it like i’m 5, what is Ajax。如果这些还不够,你还可以阅读关于 HTTP 的JavaScript 章节。今天,HTTP 请求的浏览器标准是 Fetch。 你可以在 Dan Walsh 的这篇文章中阅读有关 Fetch 的更多信息。 它介绍了Fetch 的工作原理以及如何使用它。 你还可以在此处找到带文档的 Fetch polyfill。jQuery到目前为止,你一直在使用 JavaScript 进行 DOM 操作。事实上,有很多 DOM 操作库提供api 来简化你编写的代码。最流行的 DOM 操作库之一是 jQuery。请记住,jQuery 是一个命令式库。它是在前端系统像今天这样复杂之前编写的。如今,管理复杂 UI 是声明性框架和库,如 Vue、Angular 和 React。但是,我仍然建议你学习jQuery,因为在你的前端职业生涯中很可能会遇到它。要学习j Query 的基础知识,请查看 jQuery学习中心。它一步一步地通过一些重要的概念,比如动画和事件处理。如果你想要更多的动手教程,可以看看 Codecademy 的 jQuery课程。ES5 vs. ES6理解 JavaScript 的另一个重要概念是 ECMAScrip t以及它与 JavaScript 的关系。今天,你平常看到是两种主要的 JavaScript 风格:ES5 和 ES6。ES5 和 ES6 是 JavaScript 使用的 ECMAScript 标准。你可以将它们看作JavaScript的版本。ES5 的最终草案是在2009年完成的,到目前为止你一直在使用它。ES6,也称为 ES2015,是一个新标准,它为JavaScript带来了新的语言结构,比如常量、类和模板字符串。值得注意的是,ES6 带来了新的语言特性,但仍然使用 ES5 从语义上定义它们。例如,ES6 中的类只是JavaScript原型继承的语法糖。了解 ES5和 ES6 是非常重要的,因为今天你会看到使用这两种方法的应用程序。 ES5, ES6, ES2016, ES.Next:下一步:JavaScript版本控制是怎么回事 这是一篇很好介绍了 ES6 相关的知识,Dan Wahlin的 ES6入门-下一版本的JavaScript正在发生什么。之后,你可以在 ES6 特性中看到从ES5 到 ES6 的完整变化列表。如果你想要更多,请查看这个包含 ES6 特性的Github存储库。更多的练习如果你已经到了这一步,恭喜你自己。你已经了解了很多JavaScript。让我们把你学到的一些东西付诸实践。练习 3练习3 将你所学的 HTML 和 CSS 知识与 JavaScript 入门课程结合起来。在这个实验中,你将创建自己设计的时钟,并使其与 JavaScript 交互。在开始之前,我建议阅读 解耦HTML、CSS和JavaScript的教程,以了解在混合使用 JavaScript 时的基本 CSS 类命名约定。我还在 CodePen上准备了一份笔的清单,你可以作为这次练习的参考。有关更多示例,请在 CodePen 上搜索clock。Flat ClockjQuery Wall ClockFancy ClockRetro ClockSimple JavaScript Clock你可以用两种方法做这个实验。你可以先用 HTML 和 CSS 设计和创建布局,然后用 JavaScript 增加交互性。或者你可以先编写J avaScript 逻辑,然后再进入布局。此外,你可以使用jQuery,但也可以随意使用纯 JavaScript。JavaScript框架掌握了JavaScript的基础知识之后,是时候学习 JavaScript 框架了。框架是 JavaScript 库,可以帮助你构造和组织代码。JavaScript 框架为开发人员提供了复杂前端问题的可重复解决方案,比如状态管理、路由和性能优化。它们通常用于构建web应用程序。我不会讲解每个JavaScript框架,这里有几个框架的快速预览:Angular、React + Flux、Ember、Aurelia、Vue和Meteor。你不必学习每个框架。挑一个,好好学习。不要追逐框架,相反,要理解框架所基于的底层编程哲学和原则。架构模式在查看框架之前,理解框架倾向于使用的一些架构模式是很重要的:MVC(Model–view–controller)、MVVM(Model–view–viewmodel)v和vMVP(Model–view–presenter)。这些模式被设计成在应用层之间创建清晰的关注点分离。关注点分离是一种设计原则,主要思想是将应用程序拆分为不同的域特定层。 例如,你可以使用JavaScript 对象(通常称为模型)来存储状态,而不是让 HTML 保持应用程序状态。要了解更多关于这些模式的信息,请先阅读 Chrome Developers 的 MVC。之后,阅读理解 MVC 和MVP(面向JavaScript和主干开发人员)。在那篇文章中,不要担心学不会,看不懂,只需理解 MVC和 MVP 的部分概念即可。Addy Osman 还写了关于 MVVM 的 理解 MVVM 的JavaScript开发人员指南。要了解 MVC 的起源及其产生的原因,请阅读 Martin Fowler 关于GUI体系结构的文章。最后,阅读 JavaScript MV* Patterns一节,学习 JavaScript 设计模式。学习JavaScript设计模式是一本很棒的免费在线书籍。设计模式JavaScript 框架不会重新发明轮子。它们中的大多数依赖于设计模式。你可以将设计模式视为解决软件开发中常见问题的通用模板。虽然理解 JavaScript 设计模式并不是学习框架的先决条件,但我建议在有时间的时候可以看看以下几种设计模式。装饰器模式工厂模式单例模式揭示模式观察者模式理解并能够实现其中的一些设计模式不仅会使你成为更好的工程师,而且还会帮助你理解一些框架的底层功能。AngularJSAngularJS 是一个JavaScript MVC 框架,有时也是 MVVM 框架。它由谷歌维护,并在2010年首次发布时席卷了 JavaScript 社区。Angular 是一个声明性框架。帮助我理解如何从命令式编程过渡到声明式JavaScript编程的最有帮助的文章之一是在StackOverflow上的 AngularJS 与 jQuery有何不同。如果你想了解更多关于 Angular 的知识,请查看 Angular文档。他们还提供了一个名为 Angular Cat 的教程,可以让你马上投入到编程中去。Tim Jacobi 在 Github知识库中提供了一个更完整的 Angular 学习指南。此外,看看约翰·帕帕写的这本权威的最佳实践风格指南。React + FluxAngular 解决了开发人员在构建复杂的前端系统时所面临的许多问题。另一个流行的工具是 React,它是一个用于构建用户界面的库。你可以把它想象成 MVC 中的 V。因为 React 只是一个库,所以它通常使用一个称为 Flux 的架构。Facebook设计React和Flux是为了解决MVC的一些缺点及其在规模上的问题。看看他们著名的演示 黑客方式:重新思考Facebook的Web应用程序开发。它超过了Flux,它起源于此。要开始学习 React 和 Flux,首先要学习 React。一个好的入门读物是 React文档。在那之后,看看 React.js Introduction For People Who Know Just Enough jQuery To Get By ,帮助你 从jQuery 的思维模式过渡过来。一旦你对 React 有了基本的了解,开始学习 Flux。一个好的起点是官方的Flux文档。在那之后,看看 Awesome React,这是一个精选的链接列表,可以帮助你在学习上更进一步。练习与框架现在你已经掌握了 JavaScript 框架和架构模式的一些基本知识,现在是时候将它付诸实践了。 在这两个练习中,重点是应用你学到的架构概念。 特别是,保持您的代码 DRY,明确分离关注点,并遵守单一责任原则。练习 4练习 5 是使用不依赖框架的 JavaScript 分解和重构 Todo MVC 应用程序。这个练习的目的是向你展示 MVC 如何在不混合框架特定语法的情况下工作。首先,在TodoMVC上查看最终结果。第一步是在本地创建一个新项目,并首先建立 MVC 的三个组件。由于这是一个复杂的实验,请参考 Github 存储库中的完整源代码。如果你不能完全复制这个项目或者没有时间,也没关系。下载 repo 代码并尝试使用不同的 MVC 组件,直到你理解它们之间的关系。练习 5练习 5 是应用 MVC 的一个很好的练习,理解 MVC 是学习 JavaScript 框架的重要一步。 练习 5 是按照 Scotch.io的教程来构建一个带有 Angular 的 Etsy 克隆。使用Angular构建一个Etsy克隆,Stamplay 将教你如何使用 Angular 构建一个 web 应用程序,如何使用 api 构建接口,以及如何构造大型项目。完成本教程后,能够回答以下问题。什么是 web 应用程序?MVC/MVVM 如何应用于 Angular?什么是API,它做什么如何组织和构造大型代码库将 UI 分解为指令组件有什么好处?如果你想尝试构建更多 Angular web 应用程序,可以尝试使用 AngularJS & Firebase构建一个实时状态更新应用程序。持续关注时事就像前端的其他部分一样,JavaScript的发展很快,保持持续关注是很重要的。下面是一个网站、博客和论坛的列表,这些网站、博客和论坛阅读起来既有趣又信息丰富。Smashing MagazineJavaScript WeeklyReddit JavaScriptJavaScript Jabber通过例子学习一如既往,最好的学习方法是以身作则。样式指南JavaScript样式指南是一组编码规范,旨在帮助保持代码的可读性和可维护性。AirBnB JavaScript StyleguidePrinciples of Writing Consistent, Idiomatic JavaScriptNode StyleguideMDN Coding Style代码库我无法强调阅读好的代码是多么有帮助,了解如何在获取新内容时搜索Github的相关存储库。LodashUnderscoreBabelGhostNodeBBKeystoneJS总结在本指南结束时,您应该已经牢牢掌握了 JavaScript 的基本原理以及如何将它们应用到网络上。请记住,本指南为你提供了一个总体路线图。如果你精通前端,花时间在项目上应用这些概念是很重要的。你做的项目越多,对它们越有热情,你会学到更多。原文:https://medium.freecodecamp.o…你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

April 9, 2019 · 3 min · jiezi

前端练级攻略(第一部分)

我记得我刚开始学习前端开发的时候。我看到了很多文章及资料,被学习的资料压得喘不过气来,甚至不知道从哪里开始。本指南列出前端学习路线,并提供了平时收藏的一些有效的资源。为了使这本指南易于理解,我把它分成了两部分。第一部分介绍了如何使用 HTML 和 CSS开发接口。第2部分将介绍 Javascript、框架和设计模式。HTML 和 CSS 基础在前端开发中,一切都从 HTM 和 CSS 开始。HTML 和 CSS 控制你在 Web 页面上看到的内容。HTML 表示内容,而 CSS 处理样式和布局。首先,阅读 Mozilla Developer Network(MDN)的 HTML 和 CSS 教程。MDN 逐章解释了 HTML和 CSS 重要概念。此外,每个章节只有一页长,交互演示链接到 CodePen 和 JSFiddle。在完成这些教程之后,看看 CodeAcademy 的 Make a Website 课程。本教程只需要几个小时就可以完成,对于使用 HTML 和 CSS 构建网站是一个很好的入门教程。如果wq 想了解更多, Building web forms 是 CodeAcademy 提供的另一篇教程,该教程将指导你构建和样式化 web 表单。要练习 CSS,可以试试 CSS Diner。这是一个有趣的 CSS 挑战游戏。HTM L和CSS 的另一个重要方面是布局。LearnLayout 是一个交互式教程,向你展示如何使用 HTML 和 CSS 创建布局。另外,了解如何使用 CSS Tricks 的 Google 字体 的 API 基础知识。 排版是界面的基本构建块。 如果你有时间,我强烈建议你阅读这本免费的在线书籍,Donny Truong 的 Professional Web Typography 它教你作为前端开发人员需要了解的关于排版的一切。通过这些资源,不要太担心记忆的问题。相反,重点是理解 HTML 和 CSS 如何协同工作。练习 HTML 和 CSS 基础现在你已经对 HTML 和 CSS 有了基本的了解,让我们来找点乐趣。在本节中,有两个实践旨在为你提供构建网站和界面的实践。我用“实践”这个词是因为在实践中,你从失败中学到的东西和你从成功中学到的一样多。实践 1在我们的第一个实践中,我们将使用 CodePen。CodePen 是一个前端平台,你可以在这里编写 HTML 和 CSS 代码,而不必在本地存储文件。它还提供了实时预览,可以在保存代码时立即更新。通过使用 CodePen,你可以一石二鸟。一方面,你要练习 HTML 和 CSS。另一方面,你创建一个基本的进度组合。我们还将使用 Dribbble,这是一个充满设计灵感的网站。在 Dribbble 找到一个简单到可以在几个小时内编写代码的设计。我选择了一些设计让你开始:1、2、3、4 和 5。我选择了手机为先的网页设计,因为它们比桌面网页设计要简单。不过,也可以自由选择桌面设计。在你决定了一个设计之后,继续尝试用 CodePen 编写它。如果遇到困难,请记住StackOverflow 是你的朋友。另一个有用的实践是访问像 Medium、AirBnB和 Dropbox 这样的网站,使用 inspector 工具查看它们是如何实现不同的布局和风格的。另外,看看 pens on CodePen。我挑选了一些好的例子:Twitter小部件Article News CardSimple Flat Menu如果你出来的与原设计不同,请不要气馁。 继续练习不同的设计,你会发现每次都有进步。如果你没有设计背景,很可能你的设计眼光不够成熟。具有良好设计眼光的前端开发人员将能够识别好的设计并完美地复制它们。几周前,我写了一篇关于如何培养你的设计眼光的文章。实践 2希望第一个实践让你对编写 HTML 和 CSS 有一定的信心。 对于实践 2,我们将看一些网站,然后编写一些组件。一些网站使用 CSS框架或 混淆它们的 CSS 类名,使你很难阅读它们的源代码。这就是为什么我选择了几个设计良好的网站,易于阅读源代码。AirBnB: 尝试复制他们的页脚PayPa:试着复制他们的导航栏lInvision :尝试复制页面底部的注册部分Stripe: 尝试复制他们的支付部分同样,实践2的重点不是重新创建整个页面。选择几个关键组件,如导航栏或英雄部分进行编码。我在网站列表旁边提供了一个建议,但是请随意选择其他组件。HTML 和 CSS 最佳实践到目前为止,你已经学习了 HTML 和 CSS 的基础知识。下一步是学习最佳实践。最佳实践是一组提高代码质量的非正式规则。语义标记HTML 和 CSS 的最佳实践之一是编写语义标记。好的 web 语义意味着使用适当的 HTML 标签和有意义的 CSS 类名来表示结构的意义。例如,h1 标签告诉我们它包装的文本是一个重要的标题。 另一个例子是footer标签 ,它告诉我们元素属于页面底部。 有关进一步,请阅读 CSSTricks 的 正确的 HTML5 语义 和 什么是语义类名的构成要素。CSS 命名规范CS S的下一个重要的最佳实践是正确的命名规范。良好的命名规范,如语义标签,传达了意义,并有助于使我们的代码可预测、可读和可维护。你可以在这篇 OOCSS、ACSS、BEM、SMACSS:它们是什么?我应该用什么? 中了解到不同的命名规范。一般来说,我建议你尝试一些简单的命名规范,这些规范对你来说是直观的。随着时间的推移,你会发现最适合你的方法。要了解像 Medium 这样的公司是如何利用像 BEM 这样的命名约定的,请阅读 Medium’s CSS is actually pretty fing good.。在这篇文章中,你还会了解到,提出一组有效的 CSS 约定是一个迭代过程。CSS重置从页边距到行高,每个浏览器都有一些小的样式不一致。因此,需要重置 CSS。MeyerWeb 是一个受欢迎的重置。如果你想深入了解,可以阅读 Create Your Simple Reset.css File。跨浏览器支持跨浏览器支持意味着你的代码支持最新的浏览器。像 transition 这样的 CSS 属性需要厂商前缀才能在不同的浏览器中正常工作。在本文中,我可以阅读更多关于供应商前缀的信息,即 CSS供应商前缀。最重要的是,你应该跨多个浏览器(包括 Chrome、Firefox 和 Safari )测试你的网站。CSS 预处理器与 CSS 后处理器自20世纪90年代CSS引入以来,CSS走过了漫长的道路。由于UI系统变得越来越复杂,人们提出了称为预处理器和后处理器的工具来管理复杂性。CS S预处理程序是 CSS 语言扩展,它添加了一些额外的功能,比如变量、混合和继承。两个主要的CSS预处理程序是 Sass 和 Less。2016 年,Sass的使用范围更加广泛。Bootstrap是 一种流行的响应式 CSS 框架,它也正在从 Less 转换到 Sass。而且,当大多数人谈论 Sass时,他们实际上是在谈论 SCSS。CSS 后处理器在由预处理器手写或编译后对 CSS 应用更改。 例如,PostCSS 等一些后处理器具有自动添加浏览器供应商前缀的插件。当您第一次得知有 CSS预处理器和后处理器时,你很有可能在任何地方已经使用它们。 但是,从简单开始,仅在必要时添加变量和 mixin 等扩展。 我之前建议的文章,Medium’s CSS is actually pretty fing good,也涵盖了预处理器相关的知识。网格系统和响应能力网格系统是CSS结构,它允许你水平和垂直地堆叠元素。Bootstrap,Skeleton 和 Foundation 等网格框架提供了管理布局中行和列的样式表。 虽然网格框架很有用,但了解网格的工作原理也很重要。 理解CSS网格系统 和 Don’t Overthink Grids 这两篇文章是很好的概述。网格系统的主要目的之一是为你的网站添加响应性。响应性意味着你的网站根据窗口宽度调整大小。很多时候,响应是通过使用 CSS 媒体查询实现的,CSS 规则只适用于特定的屏幕宽度。关于媒体查询知识及扩展可以看看以下几篇文章:Intro to Media Queriesmobile-firstAn Introduction to Mobile-First Media Queries实践 HTML 和 CSS 最佳实践现在你已经掌握了最佳实践,让我们进行测试。下面两个实践的目标是练习编写干净的代码,并观察最佳实践对可读性和可维护性的长期影响。实践 3对于实践 3,选择你之前做过的项目,并使用你在这过程所学到的知识来重构你的代码。重构意味着编写代码,使代码更容易阅读,更简单。能够有效地重构代码是前端开发人员的一项重要技能。 编写高质量代码是一个迭代过程。 CSS体系结构:重构你的 CSS 是重构代码的入门指南。在重构代码时,有几件事需要问问自己。* 你的取的类名是否有歧义? 6个月后,你还能理解你的类名是什么意思吗?* 你的 HTML 和 CSS 是语义化的吗?当你浏览你的代码时,你能快速辨别结构和关系的含义吗?你是否在代码中反复使用相同的十六进制颜色代码? 将它重构为一个 Sass变量 是否更有意义?你的代码在 Safari 和 Chrome 上运行得一样的吗?你是否可以用类似于 Skeleton 的网格系统替换一些布局代码?你经常使用 !important 标志吗?你怎么解决这个问题?实践 4最后一个实验把你学到的关于最佳实践的知识运用起来。然而,最佳实践的效果通常不会变得明显,直到你将它们应用到一个更大的项目中。在最后一个实践中,为自己建立一个作品集网站。作为前端开发者,你的作品集网站是你最重要的数字资产之一。作品集是一个展示你作品的网站。更重要的是,它是一个持续的记录,帮助你跟踪你的进步和发展。所以即使你只有一两件事要展示,也要展示出来。首先,跟随阿德汉姆·达纳韦的文章 《设计和开发作品集网站站的简单工作流程》如果你的第一个作品集网站迭代并不完美,那也没关系。作品集网站需要经历许多迭代。还有,重要的是你要用自己的技能来建造它。与时俱进虽然 HTML 和 CSS 不会很快过时,但是跟上前端环境的发展是很重要的。下面是一个网站、博客和论坛的列表,这些网站、博客和论坛阅读起来既有趣且信息丰富。CSSTricksSmashing MagazineDesigner NewsNettuts+CSS Wizard通过例子学习最后,最好的学习方法是以身作则。这里有一套样式指南和编码规范,将教你如何成为一个更有效的前端。样式指南Web 样式指南是可以在整个网站中重用的 CSS 组件和模式的集合。从这些样式指南中需要注意的关键问题是,基于组件的 HTML 和 CSS 方法如何允许重用代码来保持代码的清爽(DRY)。MapboxLonelyPlanetSalesForceMailChimp编码规范编码规范让代码易读且可维护。其中一些链接(如 CSS Guidelines)是编写更好的 HTML 和 CSS 的指南,而其他链接(如 Github internal CSS toolkit and Guidelines)是高质量代码的例子。CSS GuidelinesGithub internal CSS toolkit and guidelinesAirBnB’s CSS Styleguide小结希望在本文结束时,你已经熟悉 HTML 和 CSS,并掌握了一些项目。 学习前端的最佳方式是建立项目和实践。 请记住,每个前端开发人员都必须从某个地方开始。 从今天开始比明天开始更好。本文是两部分系列中的第一部分。 第二篇文章介绍了如何使用 Javascript 和 Javascript库/框架添加交互性,共勉,同进步。你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

April 8, 2019 · 2 min · jiezi

Nginx 外的另一选择,轻量级开源 Web 服务器 Tengine 发布新版本

新版发布近日,轻量级开源 Web 服务器 Tengine 发布了2.3.0版本,新增如下特性:ngx_http_proxy_connect_module,该模块让 Tengine 可以用于正向代理场景,支持对 CONNECT 方法请求的处理;HTTP2 Server粒度控制 新增 HTTP2指令,可针对 listen 相同端口的 server 进行个性化开启与关闭 HTTP2;Stream模块支持 server_name 指令,可在 SSL 场景下,基于 SNI 识别出域名,让四层SSL 转发支持特定的 server 块配置;加强 limit_req 模块功能,可以基于请求粒度动态设置限速大小,更多详细变更日志请参考limit_req 变更日志;注意事项需要注意的是,本次 Tengine 升级 core 代码至 Nginx 官方的1.15.9版本(2019年2月26日发布),由于 Tengine 的部分功能 Nginx 官方已经实现,所以 Tengine 2.3.0 弃用了自身实现的部分配置指令,由此带来的不兼容性,列举如下:废弃 Tengine 自身实现的 reuse_port 指令,使用 Nginx官方 的reuseport。升级方法:将events 配置块里面的 reuse_port on|off 注释掉,在对应的监听端口后面加 reuseport 参数,详细的操作文档,请参考limit_req 变更日志 。废弃 Tengine 的 dso_tool 工具以及 dso 配置指令。若之前有使用 Tengine 的 dso 功能,则可以切换到 Nginx官方 的 load_module 指令,详细操作文档,请参考Nginx 官方文档1和Nginx 官方文档2。移除 Tengine 加强版 slice 模块到 modules,默认使用 Nginx 官方的 slice 功能。如果依然需要使用 Tengine 的 slice,那么编译slice时请使用–add-module=modules/ngx_http_slice_module,否则使用 –with-http_slice_module 编译参数;Tengine 自身实现的模块,已全部剥离到 modules 目录下。如果需要使用那个模块,请使用 –add-module=modules/ 的方式进行编译。limit_req 的请求计数逻辑和官方保持一致,去除 limit_req_zone 中任何一个变量值为空,跳过请求计数的逻辑。关于 TengineTengine 是基于 Nginx 开发的轻量级开源 Web 服务器,作为阿里巴巴七层流量入口的核心系统,支撑着阿里巴巴双11等大促活动的平稳度过,并提供了智能的流量转发策略、HTTPS 加速、安全防攻击、链路追踪等众多高级特性,同时秉着软硬件结合的的性能优化思路,在高性能、高并发方面取得了重大突破。自开源以来,Tengine 已获得来自67位 contributors 的1390个 commits,他们分别来自淘宝、搜狗,美团、Nginx 等企业。据不完全统计,目前已有 200多家企业在通过 Tengine 来实现 Web 服务、负载均衡、代理服务、防攻击和访问限制等功能,包括傲世堂、小米网、聚美优品、河狸家、旺旺集团、杭州思华、中国博客联盟、SuperID、联想网盘、华兴资本、猿题库、蓝奏网盘、HoukeYun、云智慧等。目前,Tengine 正通过 Ingress Controller 和 K8s 打通,这让 Tengine 具备了动态感知某个服务整个生命周期的能力。未来,Tengine 将定期开源内部通用组件功能模块,并同步 Nginx 官方的最新代码,丰富开发者们的开源 Web 服务器选项。本文作者:王发康(花名:毅松) GitHub ID @wangfakang ,Tengine 开源项目 maintainer,阿里巴巴技术专家,负责阿里巴巴WEB统一接入层的开发及维护。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 4, 2019 · 1 min · jiezi

灵活使用 console 让 js 调试更简单

Web开发最常用的高度就是 console.log ,虽然 console.log 占有一席之地,但很多人并没有意识到 console 本身除了基本 log 方法之外还有很多其他方法。 适当使用这些方法可以使调试更容易,更快速,更直观。console.log()在console.log 中有很多人们意想不到的功能。虽然大多数人使用 console.log(object) 来查看对象,但是你也可以使用 console.log(object, otherObject, string),它会把它们都整齐地记录下来,偶尔也会很方便。不仅如此,还有另一种格式化的: console.log(msg, values),这很像 C 或 PHP 中的sprintf。console.log(‘I like %s but I do not like %s.’, ‘Skittles’, ‘pus’);会像你预期的那样输出:> I like Skittles but I do not like pus.常见的占位符 %o (这是字母o,不是0),它接受对象,%s 接受字符串,%d 表示小数或整数。另一个有趣的是 %c,这可能与你所想不太相同,它实际上是CSS值的占位符。使用%c占位符时,对应的后面的参数必须是CSS语句,用来对输出内容进行CSS渲染。常见的输出方式有两种:文字样式、图片输出。console.log(‘I am a %cbutton’, ‘color: white; background-color: orange; padding: 2px 5px; border-radius: 2px’);它并不优雅,也不是特别有用。当然,这并不是一个真正的按钮。它有用吗? 恩恩恩。console.dir()在大多数情况下,console.dir() 的函数非常类似于 log(),尽管它看起来略有不同。下拉小箭头将显示与上面相同的对象详细信息,这也可以从console.log 版本中看到。当你查看元素的结构时候,你会发现它们之间的差异更大,也更有趣。let element = document.getElementById(‘2x-container’);使用 console.log 查看:打开了一些元素,这清楚地显示了 DOM,我们可以在其中导航。但是console.dir(element)给出了更加方便查看 DOM 结构的输出:这是一种更客观地看待元素的方式。有时候,这可能是您真正想要的,更像是检查元素。console.warn()可能是最明显的直接替换 log(),你可以以完全相同的方式使用 console.warn()。 唯一真正的区别是输出字的颜色是黄色的。 具体来说,输出处于警告级别而不是信息级别,因此浏览器将稍微区别对待它。 这具有使其在杂乱输出中更明显的效果。不过,还有一个更大的优势,因为输出是警告而不是信息,所以你可以过滤掉所有console.log并仅保留console.warn。 这对于偶尔会在浏览器中输出大量无用废话的应用程序尤其有用。 清除一些无用的信息可以让你更轻松地看到你想要的输出。console.table()令人惊讶的是,这并不是更为人所知,但是 console.table() 函数旨在以一种比仅仅转出原始对象数组更整洁的方式显示表格数据。例如,这里有一个数据列表。const data = [{ id: “7cb1-e041b126-f3b8”, seller: “WAL0412”, buyer: “WAL3023”, price: 203450, time: 1539688433},{ id: “1d4c-31f8f14b-1571”, seller: “WAL0452”, buyer: “WAL3023”, price: 348299, time: 1539688433},{ id: “b12c-b3adf58f-809f”, seller: “WAL0012”, buyer: “WAL2025”, price: 59240, time: 1539688433}];如果我们使用 console.log 来输出上面的内容,我们会得到一些非常无用的输出:▶ (3) [{…}, {…}, {…}]点击这个小箭头可以展开看到对象的内容,但是,它并不是我们想要的“一目了然”。但是 console.table(data) 的输出要有用得多。第二个可选参数是所需列的列表。显然,所有列都是默认值,但我们也可以这样做:> console.table(data, [“id”, “price”]);这里要注意的是这是乱序的 - 最右边的列标题上的箭头显示了原因。 我点击该列进行排序。 找到列的最大或最小,或者只是对数据进行不同的查看非常方便。 顺便说一句,该功能与仅显示一些列无关,它总是可用的。console.table() 只能处理最多1000行,因此它可能不适合所有数据集。console.assert()assert() 与 log() 是相同的函数,assert()是对输入的表达式进行断言,只有表达式为false时,才输出相应的信息到控制台,示例如下:var arr = [1, 2, 3];console.assert(arr.length === 4);有时我们需要更复杂的条件句。例如,我们已经看到了用户 WAL0412 的数据问题,并希望仅显示来自这些数据的事务,这是直观的解决方案。console.assert(tx.buyer === ‘WAL0412’, tx);这看起来不错,但行不通。记住,条件必须为false,断言才会执行,更改如下:console.assert(tx.buyer !== ‘WAL0412’, tx);与其中一些类似,console.assert() 并不总是特别有用。但在特定的情况下,它可能是一个优雅的解决方案。console.count()另一个具有特殊用途的计数器,count只是作为一个计数器,或者作为一个命名计数器,可以统计代码被执行的次数。for(let i = 0; i < 10000; i++) { if(i % 2) { console.count(‘odds’); } if(!(i % 5)) { console.count(‘multiplesOfFive’); } if(isPrime(i)) { console.count(‘prime’); }}这不是有用的代码,而且有点抽象。这边也不打算演示 isPrime 函数,假设它是成立的。执行后我们会得到一个列表:odds: 1odds: 2prime: 1odds: 3multiplesOfFive: 1prime: 2odds: 4prime: 3odds: 5multiplesOfFive: 2…还有一个相关的 console.countReset(),可以使用它重置计数器。console.trace()trace() 在简单的数据中很难演示。当您试图在类或库中找出是哪个实际调用者导致了这个问题时,它的优势就显现出来了。例如,可能有 12 个不同的组件调用一个服务,但是其中一个组件没有正确地设置依赖项。export default class CupcakeService { constructor(dataLib) { this.dataLib = dataLib; if(typeof dataLib !== ‘object’) { console.log(dataLib); console.trace(); } } …}这里使用 console.log() 仅告诉我们传递数据dataLib是什么 ,而没有具体的传递的路径。不过,console.trace() 会非常清楚地告诉我们问题出在 Dashboard.js,我们可以看到是 new CupcakeService(false) 导致错误。console.time()console.time() 是一个用于跟踪操作时间的专用函数,它是跟踪 JavaScript执行时间的好方法。function slowFunction(number) { var functionTimerStart = new Date().getTime(); // something slow or complex with the numbers. // Factorials, or whatever. var functionTime = new Date().getTime() - functionTimerStart; console.log(Function time: ${ functionTime });}var start = new Date().getTime();for (i = 0; i < 100000; ++i) { slowFunction(i);}var time = new Date().getTime() - start;console.log(Execution time: ${ time });这是一种老派的做法,我们使用 console.time() 来简化以上代码。const slowFunction = number => { console.time(‘slowFunction’); // something slow or complex with the numbers. // Factorials, or whatever. console.timeEnd(‘slowFunction’);}console.time();for (i = 0; i < 100000; ++i) { slowFunction(i);}console.timeEnd();我们现在不再需要做任何计算或设置临时变量。console.group()// this is the global scopelet number = 1;console.group(‘OutsideLoop’);console.log(number);console.group(‘Loop’);for (let i = 0; i < 5; i++) { number = i + number; console.log(number);}console.groupEnd();console.log(number);console.groupEnd();console.log(‘All done now’);输出如下:并不是很有用,但是您可以看到其中一些是如何组合的。class MyClass { constructor(dataAccess) { console.group(‘Constructor’); console.log(‘Constructor executed’); console.assert(typeof dataAccess === ‘object’, ‘Potentially incorrect dataAccess object’); this.initializeEvents(); console.groupEnd(); } initializeEvents() { console.group(’events’); console.log(‘Initialising events’); console.groupEnd(); }}let myClass = new MyClass(false);这是很多工作和很多调试信息的代码,可能不是那么有用。 但它仍然是一个有趣的想法,这样写使你的日志记录更加清晰。选择DOM元素如果熟悉jQuery,就会知道 $(‘.class’) 和 $(‘#id’) 选择器有多么重要。它们根据与之关联的类或 ID 选择 DOM 元素。但是当你没有引用 jQuery时,你仍然可以在谷歌开发控制台中进行同样的操作。$(‘tagName’) $(‘.class’) $(‘#id’) and $(‘.class #id’) 等效于document.querySelector(‘ ‘),这将返回 DOM 中与选择器匹配的第一个元素。可以使用 $$(tagName) 或 $$(.class), 注意双元符号,根据特定的选择器选择DOM的所有元素。这也将它们放入数组中,你也可以通过指定数组中该元素的位置来从中选择特定的元素。例如,&dollar;&dollar;(‘.className’) 获取具有类 className 的所有元素,而$$(‘.className’)[0]和 $$(‘.className’)[1]获取到分别是第一个和第二个元素。将浏览器转换为编辑器你有多少次想知道你是否可以在浏览器中编辑一些文本? 答案是肯定的,你可以将浏览器转换为文本编辑器。 你可以在 DOM 中的任何位置添加文本和从中删除文本。你不再需要检查元素并编辑HTML。相反,进入开发人员控制台并输入以下内容:document.body.contentEditable=true 这将使内容可编辑。现在,你几乎可以编辑DOM中的任何内容。查找与DOM中的元素关联的事件调试时,需要查找 DOM 中某个元素的事件侦听器感时,谷歌控制台了 getEventListeners使找到这些事件更加容易且直观。getEventListeners($(‘selector’)) 返回一个对象数组,其中包含绑定到该元素的所有事件。你可以展开对象来查看事件:要找到特定事件的侦听器,可以这样做:getEventListeners($(‘selector’)).eventName[0].listener 这将显示与特定事件关联的侦听器。这里 eventName[0] 是一个数组,它列出了特定事件的所有事件。例如:getEventListeners($(‘firstName’)).click[0].listener 将显示与 ID 为 ‘firstName’ 的元素的单击事件关联的侦听器。监控事件如果希望在执行绑定到 DOM 中特定元素的事件时监视它们,也可以在控制台中这样做。你可以使用不同的命令来监控其中的一些或所有事件:如果希望在执行绑定到DOM中特定元素的事件时监视它们,也可以在控制台中这样做。你可以使用不同的命令来监控其中的一些或所有事件:monitorEvents($(‘selector’)) 将监视与选择器的元素关联的所有事件,然后在它们被触发时将它们打印到控制台。例如,monitore($(#firstName)) 将打印 ID 为 firstName元素的所有事件。monitorEvents($(‘selector’),’eventName’) 将打印与元素绑定的特定事件。 你可以将事件名称作为参数传递给函数。 这将仅记录绑定到特定元素的特定事件。 例如,monitorEvents($(‘#firstName’),’click’) 将打印绑定到ID为’firstName’的元素的所有 click 事件。monitore($(selector),[eventName1, eventName3’, .])将根据您自己的需求记录多个事件。与其传递单个事件名作为参数,不如传递包含所有事件的字符串数组。例如monitore($(#firstName),[click, focus])将记录与ID firstName元素绑定的 click事件和focus事件。unmonitorevent ($(selector)):这将停止监视和打印控制台中的事件。检查 DOM 中的一个元素你可以直接从控制台检查一个元素:inspect($(‘selector’)) 将检查与选择器匹配的元素,并转到 Chrome Developer Tools中的 Elements 选项卡。 例如, inspect($(‘#firstName’)) 将检查 ID为’firstName’ 的元素,spect($(‘a’)[3]) 将检查 DOM 中的第 4 个 a 元素。$0, $1, $2 等可以帮助你获取最近检查过的元素。 例如,$0 表示最后检查的 DOM 元素,而$1 倒数第二个检查的 DOM 元素。检索最后一个结果的值你可以将控制台用作计算器。当你这样做的时候,你可能需要用第二个来跟踪一个计算。以下是如何从内存中检索先前计算的结果:$_ 过程如下:2+3+49 //- The Answer of the SUM is 9$9 // Gives the last Result$ * $81 // As the last Result was 9Math.sqrt($)9 // As the last Result was 81$_9 // As the Last Result is 9清除控制台和内存如果你想清除控制台及其内存,输入如下: clear()原文:https://medium.com/@mattburge…https://medium.freecodecamp.o…你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

April 3, 2019 · 3 min · jiezi

gulp-spritesmith报错记录 Error: Unsupported interlace method

先上图,自行对号入座!原本好好的脚步突然就报错了,查了下gulp-spritesmith的ISSUE居然是图片的内容有问题。。。WTF不折腾的解决方案就是直接用PS打开图片,存储为WEB所有格式,重新输出图片即可解决。。。参考链接:gulp-spritesmith

April 3, 2019 · 1 min · jiezi

面试官:前端跨页面通信,你知道哪些方法?

引言在浏览器中,我们可以同时打开多个Tab页,每个Tab页可以粗略理解为一个“独立”的运行环境,即使是全局对象也不会在多个Tab间共享。然而有些时候,我们希望能在这些“独立”的Tab页面之间同步页面的数据、信息或状态。正如下面这个例子:我在列表页点击“收藏”后,对应的详情页按钮会自动更新为“已收藏”状态;类似的,在详情页点击“收藏”后,列表页中按钮也会更新。这就是我们所说的前端跨页面通信。你知道哪些跨页面通信的方式呢?如果不清楚,下面我就带大家来看看七种跨页面通信的方式。一、同源页面间的跨页面通信以下各种方式的 在线 Demo 可以戳这里 >>浏览器的同源策略在下述的一些跨页面通信方法中依然存在限制。因此,我们先来看看,在满足同源策略的情况下,都有哪些技术可以用来实现跨页面通信。1. BroadCast ChannelBroadCast Channel 可以帮我们创建一个用于广播的通信频道。当所有页面都监听同一频道的消息时,其中某一个页面通过它发送的消息就会被其他所有页面收到。它的API和用法都非常简单。下面的方式就可以创建一个标识为AlienZHOU的频道:const bc = new BroadcastChannel(‘AlienZHOU’);各个页面可以通过onmessage来监听被广播的消息:bc.onmessage = function (e) { const data = e.data; const text = ‘[receive] ’ + data.msg + ’ —— tab ’ + data.from; console.log(’[BroadcastChannel] receive message:’, text);};要发送消息时只需要调用实例上的postMessage方法即可:bc.postMessage(mydata);Broadcast Channel 的具体的使用方式可以看这篇《【3分钟速览】前端广播式通信:Broadcast Channel》。2. Service WorkerService Worker 是一个可以长期运行在后台的 Worker,能够实现与页面的双向通信。多页面共享间的 Service Worker 可以共享,将 Service Worker 作为消息的处理中心(中央站)即可实现广播效果。Service Worker 也是 PWA 中的核心技术之一,由于本文重点不在 PWA ,因此如果想进一步了解 Service Worker,可以阅读我之前的文章【PWA学习与实践】(3) 让你的WebApp离线可用。首先,需要在页面注册 Service Worker:/* 页面逻辑 /navigator.serviceWorker.register(’../util.sw.js’).then(function () { console.log(‘Service Worker 注册成功’);});其中../util.sw.js是对应的 Service Worker 脚本。Service Worker 本身并不自动具备“广播通信”的功能,需要我们添加些代码,将其改造成消息中转站:/ ../util.sw.js Service Worker 逻辑 /self.addEventListener(‘message’, function (e) { console.log(‘service worker receive message’, e.data); e.waitUntil( self.clients.matchAll().then(function (clients) { if (!clients || clients.length === 0) { return; } clients.forEach(function (client) { client.postMessage(e.data); }); }) );});我们在 Service Worker 中监听了message事件,获取页面(从 Service Worker 的角度叫 client)发送的信息。然后通过self.clients.matchAll()获取当前注册了该 Service Worker 的所有页面,通过调用每个client(即页面)的postMessage方法,向页面发送消息。这样就把从一处(某个Tab页面)收到的消息通知给了其他页面。处理完 Service Worker,我们需要在页面监听 Service Worker 发送来的消息:/ 页面逻辑 /navigator.serviceWorker.addEventListener(‘message’, function (e) { const data = e.data; const text = ‘[receive] ’ + data.msg + ’ —— tab ’ + data.from; console.log(’[Service Worker] receive message:’, text);});最后,当需要同步消息时,可以调用 Service Worker 的postMessage方法:/ 页面逻辑 /navigator.serviceWorker.controller.postMessage(mydata);3. LocalStorageLocalStorage 作为前端最常用的本地存储,大家应该已经非常熟悉了;但StorageEvent这个与它相关的事件有些同学可能会比较陌生。当 LocalStorage 变化时,会触发storage事件。利用这个特性,我们可以在发送消息时,把消息写入到某个 LocalStorage 中;然后在各个页面内,通过监听storage事件即可收到通知。window.addEventListener(‘storage’, function (e) { if (e.key === ‘ctc-msg’) { const data = JSON.parse(e.newValue); const text = ‘[receive] ’ + data.msg + ’ —— tab ’ + data.from; console.log(’[Storage I] receive message:’, text); }});在各个页面添加如上的代码,即可监听到 LocalStorage 的变化。当某个页面需要发送消息时,只需要使用我们熟悉的setItem方法即可:mydata.st = +(new Date);window.localStorage.setItem(‘ctc-msg’, JSON.stringify(mydata));注意,这里有一个细节:我们在mydata上添加了一个取当前毫秒时间戳的.st属性。这是因为,storage事件只有在值真正改变时才会触发。举个例子:window.localStorage.setItem(’test’, ‘123’);window.localStorage.setItem(’test’, ‘123’);由于第二次的值'123’与第一次的值相同,所以以上的代码只会在第一次setItem时触发storage事件。因此我们通过设置st来保证每次调用时一定会触发storage事件。小憩一下上面我们看到了三种实现跨页面通信的方式,不论是建立广播频道的 Broadcast Channel,还是使用 Service Worker 的消息中转站,抑或是些 tricky 的storage事件,其都是“广播模式”:一个页面将消息通知给一个“中央站”,再由“中央站”通知给各个页面。在上面的例子中,这个“中央站”可以是一个 BroadCast Channel 实例、一个 Service Worker 或是 LocalStorage。下面我们会看到另外两种跨页面通信方式,我把它称为“共享存储+轮询模式”。4. Shared WorkerShared Worker 是 Worker 家族的另一个成员。普通的 Worker 之间是独立运行、数据互不相通;而多个 Tab 注册的 Shared Worker 则可以实现数据共享。Shared Worker 在实现跨页面通信时的问题在于,它无法主动通知所有页面,因此,我们会使用轮询的方式,来拉取最新的数据。思路如下:让 Shared Worker 支持两种消息。一种是 post,Shared Worker 收到后会将该数据保存下来;另一种是 get,Shared Worker 收到该消息后会将保存的数据通过postMessage传给注册它的页面。也就是让页面通过 get 来主动获取(同步)最新消息。具体实现如下:首先,我们会在页面中启动一个 Shared Worker,启动方式非常简单:// 构造函数的第二个参数是 Shared Worker 名称,也可以留空const sharedWorker = new SharedWorker(’../util.shared.js’, ‘ctc’);然后,在该 Shared Worker 中支持 get 与 post 形式的消息:/ ../util.shared.js: Shared Worker 代码 /let data = null;self.addEventListener(‘connect’, function (e) { const port = e.ports[0]; port.addEventListener(‘message’, function (event) { // get 指令则返回存储的消息数据 if (event.data.get) { data && port.postMessage(data); } // 非 get 指令则存储该消息数据 else { data = event.data; } }); port.start();});之后,页面定时发送 get 指令的消息给 Shared Worker,轮询最新的消息数据,并在页面监听返回信息:// 定时轮询,发送 get 指令的消息setInterval(function () { sharedWorker.port.postMessage({get: true});}, 1000);// 监听 get 消息的返回数据sharedWorker.port.addEventListener(‘message’, (e) => { const data = e.data; const text = ‘[receive] ’ + data.msg + ’ —— tab ’ + data.from; console.log(’[Shared Worker] receive message:’, text);}, false);sharedWorker.port.start();最后,当要跨页面通信时,只需给 Shared Worker postMessage即可:sharedWorker.port.postMessage(mydata);注意,如果使用addEventListener来添加 Shared Worker 的消息监听,需要显式调用MessagePort.start方法,即上文中的sharedWorker.port.start();如果使用onmessage绑定监听则不需要。5. IndexedDB除了可以利用 Shared Worker 来共享存储数据,还可以使用其他一些“全局性”(支持跨页面)的存储方案。例如 IndexedDB 或 cookie。鉴于大家对 cookie 已经很熟悉,加之作为“互联网最早期的存储方案之一”,cookie 已经在实际应用中承受了远多于其设计之初的责任,我们下面会使用 IndexedDB 来实现。其思路很简单:与 Shared Worker 方案类似,消息发送方将消息存至 IndexedDB 中;接收方(例如所有页面)则通过轮询去获取最新的信息。在这之前,我们先简单封装几个 IndexedDB 的工具方法。打开数据库连接:function openStore() { const storeName = ‘ctc_aleinzhou’; return new Promise(function (resolve, reject) { if (!(‘indexedDB’ in window)) { return reject(‘don't support indexedDB’); } const request = indexedDB.open(‘CTC_DB’, 1); request.onerror = reject; request.onsuccess = e => resolve(e.target.result); request.onupgradeneeded = function (e) { const db = e.srcElement.result; if (e.oldVersion === 0 && !db.objectStoreNames.contains(storeName)) { const store = db.createObjectStore(storeName, {keyPath: ’tag’}); store.createIndex(storeName + ‘Index’, ’tag’, {unique: false}); } } });}存储数据function saveData(db, data) { return new Promise(function (resolve, reject) { const STORE_NAME = ‘ctc_aleinzhou’; const tx = db.transaction(STORE_NAME, ‘readwrite’); const store = tx.objectStore(STORE_NAME); const request = store.put({tag: ‘ctc_data’, data}); request.onsuccess = () => resolve(db); request.onerror = reject; });}查询/读取数据function query(db) { const STORE_NAME = ‘ctc_aleinzhou’; return new Promise(function (resolve, reject) { try { const tx = db.transaction(STORE_NAME, ‘readonly’); const store = tx.objectStore(STORE_NAME); const dbRequest = store.get(‘ctc_data’); dbRequest.onsuccess = e => resolve(e.target.result); dbRequest.onerror = reject; } catch (err) { reject(err); } });}剩下的工作就非常简单了。首先打开数据连接,并初始化数据:openStore().then(db => saveData(db, null))对于消息读取,可以在连接与初始化后轮询:openStore().then(db => saveData(db, null)).then(function (db) { setInterval(function () { query(db).then(function (res) { if (!res || !res.data) { return; } const data = res.data; const text = ‘[receive] ’ + data.msg + ’ —— tab ’ + data.from; console.log(’[Storage I] receive message:’, text); }); }, 1000);});最后,要发送消息时,只需向 IndexedDB 存储数据即可:openStore().then(db => saveData(db, null)).then(function (db) { // …… 省略上面的轮询代码 // 触发 saveData 的方法可以放在用户操作的事件监听内 saveData(db, mydata);});小憩一下在“广播模式”外,我们又了解了“共享存储+长轮询”这种模式。也许你会认为长轮询没有监听模式优雅,但实际上,有些时候使用“共享存储”的形式时,不一定要搭配长轮询。例如,在多 Tab 场景下,我们可能会离开 Tab A 到另一个 Tab B 中操作;过了一会我们从 Tab B 切换回 Tab A 时,希望将之前在 Tab B 中的操作的信息同步回来。这时候,其实只用在 Tab A 中监听visibilitychange这样的事件,来做一次信息同步即可。下面,我会再介绍一种通信方式,我把它称为“口口相传”模式。6. window.open + window.opener当我们使用window.open打开页面时,方法会返回一个被打开页面window的引用。而在未显示指定noopener时,被打开的页面可以通过window.opener获取到打开它的页面的引用 —— 通过这种方式我们就将这些页面建立起了联系(一种树形结构)。首先,我们把window.open打开的页面的window对象收集起来:let childWins = [];document.getElementById(‘btn’).addEventListener(‘click’, function () { const win = window.open(’./some/sample’); childWins.push(win);});然后,当我们需要发送消息的时候,作为消息的发起方,一个页面需要同时通知它打开的页面与打开它的页面:// 过滤掉已经关闭的窗口childWins = childWins.filter(w => !w.closed);if (childWins.length > 0) { mydata.fromOpenner = false; childWins.forEach(w => w.postMessage(mydata));}if (window.opener && !window.opener.closed) { mydata.fromOpenner = true; window.opener.postMessage(mydata);}注意,我这里先用.closed属性过滤掉已经被关闭的 Tab 窗口。这样,作为消息发送方的任务就完成了。下面看看,作为消息接收方,它需要做什么。此时,一个收到消息的页面就不能那么自私了,除了展示收到的消息,它还需要将消息再传递给它所“知道的人”(打开与被它打开的页面):需要注意的是,我这里通过判断消息来源,避免将消息回传给发送方,防止消息在两者间死循环的传递。(该方案会有些其他小问题,实际中可以进一步优化)window.addEventListener(‘message’, function (e) { const data = e.data; const text = ‘[receive] ’ + data.msg + ’ —— tab ’ + data.from; console.log(’[Cross-document Messaging] receive message:’, text); // 避免消息回传 if (window.opener && !window.opener.closed && data.fromOpenner) { window.opener.postMessage(data); } // 过滤掉已经关闭的窗口 childWins = childWins.filter(w => !w.closed); // 避免消息回传 if (childWins && !data.fromOpenner) { childWins.forEach(w => w.postMessage(data)); }});这样,每个节点(页面)都肩负起了传递消息的责任,也就是我说的“口口相传”,而消息就在这个树状结构中流转了起来。小憩一下显然,“口口相传”的模式存在一个问题:如果页面不是通过在另一个页面内的window.open打开的(例如直接在地址栏输入,或从其他网站链接过来),这个联系就被打破了。除了上面这六个常见方法,其实还有一种(第七种)做法是通过 WebSocket 这类的“服务器推”技术来进行同步。这好比将我们的“中央站”从前端移到了后端。关于 WebSocket 与其他“服务器推”技术,不了解的同学可以阅读这篇《各类“服务器推”技术原理与实例(Polling/COMET/SSE/WebSocket)》此外,我还针对以上各种方式写了一个 在线演示的 Demo >>二、非同源页面之间的通信上面我们介绍了七种前端跨页面通信的方法,但它们大都受到同源策略的限制。然而有时候,我们有两个不同域名的产品线,也希望它们下面的所有页面之间能无障碍地通信。那该怎么办呢?要实现该功能,可以使用一个用户不可见的 iframe 作为“桥”。由于 iframe 与父页面间可以通过指定origin来忽略同源限制,因此可以在每个页面中嵌入一个 iframe (例如:http://sample.com/bridge.html),而这些 iframe 由于使用的是一个 url,因此属于同源页面,其通信方式可以复用上面第一部分提到的各种方式。页面与 iframe 通信非常简单,首先需要在页面中监听 iframe 发来的消息,做相应的业务处理:/ 业务页面代码 /window.addEventListener(‘message’, function (e) { // …… do something});然后,当页面要与其他的同源或非同源页面通信时,会先给 iframe 发送消息:/ 业务页面代码 /window.frames[0].window.postMessage(mydata, ‘’);其中为了简便此处将postMessage的第二个参数设为了’’,你也可以设为 iframe 的 URL。iframe 收到消息后,会使用某种跨页面消息通信技术在所有 iframe 间同步消息,例如下面使用的 Broadcast Channel:/ iframe 内代码 /const bc = new BroadcastChannel(‘AlienZHOU’);// 收到来自页面的消息后,在 iframe 间进行广播window.addEventListener(‘message’, function (e) { bc.postMessage(e.data);}); 其他 iframe 收到通知后,则会将该消息同步给所属的页面:/ iframe 内代码 /// 对于收到的(iframe)广播消息,通知给所属的业务页面bc.onmessage = function (e) { window.parent.postMessage(e.data, ‘’);};下图就是使用 iframe 作为“桥”的非同源页面间通信模式图。其中“同源跨域通信方案”可以使用文章第一部分提到的某种技术。总结今天和大家分享了一下跨页面通信的各种方式。对于同源页面,常见的方式包括:广播模式:Broadcast Channe / Service Worker / LocalStorage + StorageEvent共享存储模式:Shared Worker / IndexedDB / cookie口口相传模式:window.open + window.opener基于服务端:Websocket / Comet / SSE 等而对于非同源页面,则可以通过嵌入同源 iframe 作为“桥”,将非同源页面通信转换为同源页面通信。本文在分享的同时,也是为了抛转引玉。如果你有什么其他想法,欢迎一起讨论,提出你的见解和想法~对文章感兴趣的同学欢迎关注 我的博客 >> https://github.com/alienzhou/blog ...

April 1, 2019 · 4 min · jiezi

浏览器将标签转成 DOM 的过程

浏览器基本的工作流程进入主话题之前,先罗列一下浏览器的主要构成:用户界面- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分浏览器引擎- 用来查询及操作渲染引擎的接口渲染引擎- 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口JS解释器- 用来解释执行JS代码数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术解析当浏览器获得了资源以后要进行的第一步工作就是 HTML 解析,,它由几个步骤组成:编码、预解析、标记和构建树。编码HTTP 响应主体的有效负载可以是从HTML文本到图像数据的任何内容。解析器的第一项工作是找出如何转制刚刚从服务器接收到的 bit。假设我们正在处理一个HTML文档,解码器必须弄清楚文本文档是如何被转换成比特(bit)的,以便反转这个过程。记住,最终即使是文本也会被计算机翻译成二进制,如上图所示,在本例中是 ASCII 编码—定义二进制值,如“01000100”表示字母“D”。对于文本存在许多可能的编码—浏览器的工作是找出如何正确地解码文本。服务器应该通过 Content-Type 提供的信息同时在文本文件头部使用 Byte Order Mark 告知浏览器编码格式。如果仍然无法确定编码,浏览器还会自行匹配一种解码格式来处理数据。有时候,解码格式也会写在 <meta> 标签中。最坏的情况是,浏览器进行了有根据的猜测,然后开始解析之后发现一个矛盾的 <meta> 标签。在这些罕见的情况下,解析器必须重新启动,丢弃之前解码的内容。浏览器有时必须处理旧的 web内容(使用遗留编码),许多这样的系统都支持这一点。我们现在经常在 HTML中使用的文件格式是 UTF-8,那是因为 UTF-8 能较完整的支持Unicode 字符范围,同时与 CSS、JavaScript 中常见的节字符具有良好的 ASCII 兼容性。一般浏览器默认的解码格式也是 UTF-8。当解码出错的时候,我们会看到屏幕上全部都是乱码字符。预解析在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。预解析器不是完整的解析器,如,它不理解 HTML 中的嵌套级别或父/子关系。但是,预解析可以识别特定的 HTML 标签的名称和属性,以及 URL。例如,如果你的 HTML 内容中有一个<img src=“https://somewhere.example.com/images/dog.png" alt=”"> ,预解析将注意到src属性,并将获取这个图片的请求加到请求队列中。请求图片的速度越快越好,将等待它从网络到达的时间降到最低。预解析还会注意到 HTML 中的某些显式请求,比如 preload 和 prefetch 指令,并将它们加入等待队友中进行处理。标记化(Tokenization)该算法的输出结果是 HTML 标记。该算法使用状态机来表示。每一个状态接收来自输入信息流的一个或多个字符,并根据这些字符更新下一个状态。当前的标记化状态和树结构状态会影响进入下一状态的决定。这意味着,即使接收的字符相同,对于下一个正确的状态也会产生不同的结果,具体取决于当前的状态。该算法相当复杂,无法在此详述,所以我们通过一个简单的示例来帮助大家理解其原理。基本示例 - 将下面的 HTML 代码标记化:<html> <body> Hello world </body></html>初始状态是数据状态。遇到字符 < 时,状态更改为“标记打开状态”。接收一个 a-z 字符会创建“起始标记”,状态更改为“标记名称状态”。这个状态会一直保持到接收 > 字符。在此期间接收的每个字符都会附加到新的标记名称上。在本例中,我们创建的标记是 html 标记。遇到 > 标记时,会发送当前的标记,状态改回“数据状态”。<body> 标记也会进行同样的处理。目前 html 和 body 标记均已发出。现在我们回到“数据状态”。接收到 Hello world 中的 H 字符时,将创建并发送字符标记,直到接收 </body> 中的 <。我们将为 Hello world 中的每个字符都发送一个字符标记。现在我们回到“标记打开状态”。接收下一个输入字符 / 时,会创建 end tag token 并改为“标记名称状态”。我们会再次保持这个状态,直到接收 >。然后将发送新的标记,并回到“数据状态”。</html> 输入也会进行同样的处理。构建树(tree construction)在创建解析器的同时,也会创建 Document 对象。在树构建阶段,以 Document 为根节点的 DOM 树也会不断进行修改,向其中添加各种元素。标记生成器发送的每个节点都会由树构建器进行处理。规范中定义了每个标记所对应的 DOM 元素,这些元素会在接收到相应的标记时创建。这些元素不仅会添加到 DOM 树中,还会添加到开放元素的堆栈中。此堆栈用于纠正嵌套错误和处理未关闭的标记。其算法也可以用状态机来描述。这些状态称为“插入模式”。在上一步符号化以后,解析器获得这些标记,然后以合适的方法创建 DOM 对象并将这些符号插入到 DOM 对象中。DOM 对象的数据结构是树状的,所以这个过程称为构造树(tree construction)。另外,在 IE 的历史中,大部分时间里没有使用树结构。在创建解析器的同时,也会创建 Document 对象。在树构建阶段,以 Document 为根节点的 DOM 树也会不断进行修改,向其中添加各种元素。标记生成器发送的每个节点都会由树构建器进行处理。规范中定义了每个标记所对应的 DOM 元素,这些元素会在接收到相应的标记时创建。这些元素不仅会添加到 DOM 树中,还会添加到开放元素的堆栈中。此堆栈用于纠正嵌套错误和处理未关闭的标记。其算法也可以用状态机来描述。这些状态称为“插入模式”。例如,考虑这个 HTML:<p>sincerely<p>The authors</p>这样可以确保结果树中的两个段落对象是兄弟节点,而忽略第二个打开的标签则与一个段落对象相对。 HTML表可能是解析器规则试图确保表具有适当结构的最复杂的表。尽管存在所有复杂的解析规则,但是一旦创建了 DOM 树,所有试图创建正确 HTML 结构的解析规则就不再强制执行了。使用 JavaScript,网页可以几乎以任何方式重新排列 DOM 树,即使它没有意义,例如,添加表格单元格作为 <video> 标签的子项,渲染系统负责弄清楚如何处理任何前后不一致标签。HTML 解析中的另一个复杂因素是 JavaScript 可以在解析器执行其工作时添加更多要解析的内容。<script> 标签包含解析器必须收集的文本,然后发送到脚本引擎进行评估。 当脚本引擎解析并评估脚本文本时,解析器会等待。如果JavaScript文件内调用了 document.writeAPI,解析器将重新开始解析过程。事件(Events)当解析器完成时,它通过一个名为 DOMContentLoaded 的事件宣布完成。事件是内置在浏览器中的广播系统,JavaScript可以侦听和响应它。除了 DOMContentLoaded 事件,还有load 事件(表示所有资源已经加载完成,包括图片、视频、CSS等等)、unload 事件表示界面即将关闭、鼠标事件键盘事件等等。浏览器在 DOM 中创建一个事件对象,并将其打包成有用的状态信息(例如屏幕上触摸的位置、按下的按键等等),当JavaScript触发事件的时候,就会同时产生事件对象。DOM 的树结构通过允许在树的任何级别监听事件(如在树根、树叶或两者之间的任何地方)。在目标元素上触发事件的时候,需要 从DOM 树的根元素开始向子元素查找,这个过程俗称事件捕捉阶段。到达目标元素以后,还要逐级向上返回到根元素上,这个过程俗称事件冒泡阶段。还可以取消一些事件,例如,如果表单没有正确填写,则可以停止表单提交。(提交事件是从<form> 元素触发的,JavaScript 侦听器可以检查表单,如果字段为空或无效,还可以选择取消事件。)DOMHTML语言提供了丰富的特性集,远远超出了解析器处理的标记。解析器构建一个结构,其中的元素包含其他元素,以及这些元素最初具有什么状态(它们的属性)。结构和状态的组合足以提供基本渲染和一些交互(例如通过内置控件,如<textarea>,<video>,<button>等)。 但是如果不添加 CSS 和 JavaScript,网络将非常枯燥(和静态)。 DOM 为 HTML 元素和与 HTML 无关的其他对象提供了额外的功能层。元素接口在解析器将元素放入DOM树之前,解析器会根据不同元素的名称赋予元素不同的接口功能。些通用特性包括:访问代表元素子元素的全部或子集的 HTML 集合能够查找元素的属性、子元素和父元素重要的是,创建新元素的方法(不使用解析器),并将它们附加到树中(或将它们从树中分离出来)对于像 <table> 这样的特殊元素,该接口包含用于查找表中所有行,列和单元格的其他特定于表的功能,以及用于从表中删除和添加行和单元格的快捷方式。 同样,<canvas> 接口具有绘制线条,形状,文本和图像的功能。 使用这些 API 需要 JavaScript 仅仅使用 HTML 标签是不够的。每当我们使用 JavaScript 操作 DOM 的时候,将会触发浏览器的一些连锁反应,这些反应是为了让更改后的页面更快的渲染在屏幕上。例如:用数字代表通用的元素名称和属性,浏览器用使用哈希表进行快速识别这些数字将频繁变更的子元素进行缓存,方便子元素快速迭代将 sub-tree 的跟踪变化降到最低,避免‘污染’整个 DOM 树其他APIDOM中的HTML元素及其接口是浏览器在屏幕上显示内容的唯一机制。CSS可以影响布局,但仅限于HTML元素中存在的内容。最终,如果你想在屏幕上看到内容,它必须通过作为树的一部分的HTML接口来完成。访问存储系统(数据库,key/value存储,网络缓存存储(network cache storage));设备(各种类型的地理定位,距离和方向传感器,USB,MIDI,蓝牙,游戏手柄);网络(HTTP交换,双向服务器套接字,实时媒体流);图形(2D和3D图形基元,着色器,虚拟和增强现实);和多线程(具有丰富消息传递功能的共享和专用执行环境)。随着主要浏览器引擎开发和实施新的Web标准,DOM公开的功能不断增加。然而,DOM的这些“额外”API中的大多数都超出了本文的范围。总结希望这部分对你关于 DOM 解析过程多多少少有点帮助,共进步!你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

April 1, 2019 · 1 min · jiezi

URLOS-0.4.0发布:推进中小企业网站安全防护升级

近日,广州市万岁云计算有限公司推出了更灵活更强大的URLOS-0.4.0新版本!产品新增了网站攻击防护、主机监控等功能,修复了已知问题以及强化了系统稳定性,进一步满足用户需求,提升用户体验。(图1:攻击防护)URLOS是由万岁云创始团队独立自主研发,具有自主知识产权的Linux容器应用管理软件。其易安装、易使用、易交接,开箱即用,凭借容器级自我修复和弹性伸缩能力,可在10分钟内快速创建3节点网站容灾环境,为中小企业连续业务和数据资产安全保驾护航。(图2:主机监控)URLOS是一个应用容器管理软件,基于Docker容器技术打包和运行应用,可自动识别机器和云应用的故障并将云应用转移至可用的机器上,单机故障并不影响业务开展,配合云存储便可轻松搭建7x24小时持续运行的应用环境。(URLOS 0.4.0)URLOS 0.4.0.1的更新包括:新增网站攻击防御系统,可有效防御CC攻击等一系列常见网站功能方式;新增主机监控系统,可通过微信查看主机运行以及网站流量情况;修复若干已知问题,优化用户体验;URLOS安装命令:curl -SO https://www.urlos.com/install && chmod 544 install && ./installURLOS升级命令:curl -SO https://www.urlos.com/update && chmod 544 update && ./update温馨提示:由于存储算法升级,如果您的URLOS是从0.3.x升级到0.4.x版本,请在升级后登录URLOS重新填写已安装的数据库服务的密码并暴力部署一次!《攻击防护开启教程》https://www.urlos.com/center-…《主机监控开启教程》https://www.urlos.com/center-…

March 28, 2019 · 1 min · jiezi

【视频教程】JS数组优雅去重-冰山工作室-沙翼-web前端

视频教程-数组去重案例分析:随机生成20个10以内的数字随机生成10以内数字let arr = Array.from({length:20},=>Math.random()*10|0);console.log(arr);//输出结果:[5,4,7,0,0,0,8,0,2,9,3,0,0,1,5,9,2,8,6,0]去重思路:双层循环,外层循环元素,内层循环时比较值值相同时,则删去这个值利用splice直接在原数组进行操作Array.prototype.distinct = function (){ var arr = this, i, j, len = arr.length; for(i = 0; i < len; i++){ for(j = i + 1; j < len; j++){ if(arr[i] == arr[j]){ arr.splice(j,1); len–; j–; } } } return arr;}; var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];var b = a.distinct();console.log(b.toString()); //1,2,3,4,5,6,56注意:删除相同值时,数组长度相应减一。但是,我们要注意的是,此种方法会改变原数组的值,也就是说,我们改变了arr的结果。如果不想改变原数组改怎么办呢?不改变原数组Array.prototype.distinct = function(){ var arr = this, result = [], i, j, len = arr.length; for(i = 0; i < len; i++){ for(j = i + 1; j < len; j++){ if(arr[i] === arr[j]){ j = ++i; } } result.push(arr[i]); } return result;}var arra = [1,2,3,4,4,1,1,2,1,1,1];arra.distinct()此种方法,先创建一个空数组,然后利用双层循环,符合的,push进result数组中,若遇到相同值,则直接跳过,不再进行push操作。这样,我们就避免了对原数组的操作。这种方法解决了操作原数组的问题,但是,如果数组的值特别大怎么办?比如说,数组arr有10000个值,找到第一项后,比较数组后面的值,那我们需要比较9999次,找到第二项,需要比较9998次。但是,我们已经确定了,我们的结果集中,就10个数,这样显然不是我们想要的,怎么办呢?为了提升性能,我们可以从结果集中进行比较。indexOf的使用let rs = [];for(let i=0;i<arr.length;i++){ if(rs.indexOf arr[i] === -1){ rs.push(arr[i]) }};console.log(rs)通过indexOf方法,如果得到的值为-1,则确定数组中不存在该值,这样,我们就把arr[i],push到数组中。这样我们就得到了去重后的数组。既然我们想到了indexOf方法,那我们是不是还能有更加简便的方法来使数组去重呢?仔细想想,我们就会想到,数组的filter方法。数组的filter方法console.log(arr.filter(function(element,index,self){ return self.indexOf(element) === index; }));element是数组的每个值,index是数组的索引,self是数组本身。当使用indexOf方法时,如果数组的每项的indexOf方法得到的值与数组索引相同,则证明此值第一次出现,如果数组的索引与index的值不相同,则证明不是第一次出现。前面我们介绍的这些方法,都是使用数组本身的方法。其实在js中,还有一个特殊的东西,叫做对象。对象去重法var o={};var new_arr=[];for(var i=0;i<arr.length;i++){ var k=arr[i]; if(!o[k]){ o[k]=true; new_arr.push(k); }}console.log(new_arr)这种方法利用到,对象的属性唯一行,来进行判断。为什么要用到这种方法呢?其实前面的方法,都用到了双层循环,indexOf也不例外,但是利用对象的key来做判断时,我们只用到了一次循环,这样就会大大增加运行的性能,利用对象去重也是这些方法中运行速度最快的一种。前面介绍到的这些种方法,都是ES3,ES5中用到的方法,接下来我们介绍一下利用ES6来进行数组去重的方法。在本文的开头,我们创建随机数组的时候,用到了 Array.from()方法,在ES6中新增了from方法。接下来我们借助from方法和一些其他的方法来把数组进行去重。form方法let rs = Array.from(arr);//得到与当前一样的数组let rs= Array.from(new Set(arr));// 利用ES6的Set方法进行去重。console.log([…new Set(arr)]);//这种方法只用到了13个字符,也是数组去重最方便的方法。总结:数组去重在我们日常的开发中用到的比较少,但是我们要理解其中的逻辑,以便于我们更好的进行其他的开发任务。关于我们原始高清视频下载视频讲解-提取码:q82tQQ答疑交流群: 600633658我们的链接:知乎 掘金 今日头条 新浪微博 前端网 思否 简书 B站 ...

March 28, 2019 · 1 min · jiezi

Bootstrap优秀模板-Unify.2.6.2

这是一个非常老牌的Bootstrap商业模板,全面性和稳定性俱佳,有LandingPage、BussinessPage、AdminPage多种模式,非常推荐用来构建官网、响应式应用Web、管理端Web等。最早这个模板20多美元,现在增加了AdminPage,价格也涨到40美元了,总的来说,非常对得起这个价格,值得在商业项目中使用,很多布局和控件不需要自己造轮子,用这个就完了。先来张官方图下面是官方特性介绍:核心特性100% Responsive(100%响应式) Based on Bootstrap 4(基于Boostrap4构建) Full Built in SASS(样式基于SASS构建) 600+ Pre-made HTML5 Pages(600多种预设样式页面) 1750+ Reusable UI Components(1750+可复用控件) 200+ Slider Demos(200+Slider案例) 85+ Header Options(85+头部样式) 30+ Beautiful Promo Blocks(30+漂亮的首页开屏推介模块(Promo Blocks我也不知道翻译成啥好..就这么叫吧)) 40+ Footer Options(40+底部样式) 40+ Extensive Blog Layouts(40+大宽度博客布局) 45+ Carefully Customized Libraries(45+精心定制的组件库) 45+ Crafted Portfolio Layouts(45+组件组合布局) 40+ Ready to Start Templates(40+准备发布页面模板)其他特性100% Customizable(100%可定制改造) Bootstrap Flexbox Grid(支持Bootstrap弹性布局组件) Premium Plugins & Libraries(高级组件和库) Youtube, Vimeo & HTML5 Background Videos(Youtube、Vimeo视频支持,H5背景视频) Google Maps - over 10 functional examples including theming options(谷歌地图,10+功能案例) 4000+ Font Icons (Font Awesome, Simple Line Icons etc.)(4000+各种字体图标) Massive Collection of Pricing Tables and Demos(很多价格表单页面案例) Smooth Parallax Scrolling(平滑视觉过渡(这个也不知道如何翻译,就是那种整个页面从上翻到下面视觉过渡,像比如小米卖手机那种页面)) Image & Content Sliders(图片和内容滑动) Over 30 Counters options including pie chart options(30+计数器,包含饼状图计数器) Over 30 Popup Functionalities(30+弹出样式功能) Lightbox Image View - comes with huge functional options(Lightbox图片浏览(这个是个插件名称)) Extensive Galleries Options(大照片浏览插件) Countdown Variations(倒计时插件) Sticky Blocks - make any blocks “sticky” in your content, perfect for sidebars(一个叫Sticky Blocks的插件,让任何块内容保持粘性不动,非常适合做工具栏模块) Animated Typing Words(文字动态输出(这个也不知道如何翻译了,大概意思就是模拟打字这样把文本显示出来)) Huge Collection of Form Options(超大的表单收集) Background Overlays - put any colors on top of video and image backgrounds(背景素材叠加覆盖,能把任何颜色、视频、图片等覆盖在背景上) Drop Zone File Upload(拖拽上传文件) Google Fonts - over 800 font options to use(谷歌字体) On Scroll Animations(滚动动画) Masonry Grid Layouts(瀑布流布局) Pie Charts(饼状图表) Over 30 Product Designs - use for any type of e-commerce, events and listing pages(30+产品设计样式,适合电商等页面) Timeline Designs(时间轴页面) Over 10 User Design Blocks - perfect for profile/admin pages(饼状图表) Over 20 Login & Sign Up Pages(20+登录注册页面) 15 Ready to Start Unique Home Pages(15个准备开始唯一首页页面) Profile Pages(个人中心页面) Search Result Pages(搜索结果页面) Job Pages(职位介绍页面) Coming Soon Page(即将上线页面) 404 Error Pages(404错误页面) Developer Friendly & Clean Code(对开发者很友好,代码干净整洁) Extensive Documentation(丰富的文档) Free & Lifetime Updates(一次付费终生升级) Easy to Customize(很容易定制) Stunning Support(售后支持让你吃惊)官方Demo地址初码国内源码Demo地址下面是下载地址 ...

March 28, 2019 · 2 min · jiezi

【冰山白皮书】JS中的对象

关于我们QQ答疑交流群: 600633658我们的链接:知乎 掘金 今日头条 新浪微博 前端网 思否 简书 B站什么是对象是由属性以及属性所对应的值组成的合集,是没有顺序的除6种原始数据类型之外,所有的数据都是对象。对象和原始类型区别对象有属性,属性所对应的值可以是原始数据类型,也可以是方法,还可以是对象对象有方法,方法是一种特殊的属性,对应的值是一个函数,用于实现某种功能,由此可见函数也是对象。(回顾为什么String Number Boolean也可以使用方法或者调用属性)对象是可变的,改变的本质是对象属性发生了变化。对象的分类内部对象错误对象:Error本地对象:Boolean String Number Array Date Function Object RegExp内置对象:解释器内置,不需要使用New global Math Json宿主对象运行环境提供的对象:window document自定义对象开发者自己创建的对象原始类型转换到对象Object(true)—-{[[PrimitiveValue]]: true}Object(123)—-{[[PrimitiveValue]]: 123}Object(“abc”)——{0:‘a’,1:‘b’,2:‘c’,length:3,[[PrimitiveValue]]: true}Object(null)—-{}Object(undefined)—-{}(联系上文对象和原始类型区别)对象上的两个基础方法toString将当前对象用字符串表示出来。所有的内部对象和宿主对象都重写了toString方法。Boolean String Number直接返回对应的字符串。Array:为数组的每一项调用toString方法并将结果用逗号连接Date:返回一个由日期和时间组成的字符串(不同的环境下结果不一样)Function:返回函数体内部的源码Object:返回[object object]RegExp:返回正则表达式的直接量字符串(带转义符)可以接受一个2~36的数字作为参数,表示需要转换的内容是几进制的valueOf有原始值返回原始值没有原始值返回本身Date对象返回距离1970年1月1日0点的毫秒数对象转换到原始类型Boolean:所有的对象转换到布尔类型都为true.(JS中只有6个值能转成false)String:先调用toString方法,再调用valueOf方法Number:先调用valueOf方法,再调用toString方法如何创建一个对象使用字符字面量的方式(最简单)var o={age:123}注意:属性名中如果有空格,连字符,关键字或者保留字的时候必须使用引号(在ES5中保留字可以不使用引号,但是不建议这么做)最后一个逗号需要删除,否则在IE7及以下的浏览器中会报错使用new操作符(最标准)var o=new Object()var s=new String(‘abcd’)var n=new Number(123)var a=new Array(3)var d=new Date(2017/04/15)var a=new Array 没有参数的时候可以不写括号使用ES5中的新方法(最精确,最麻烦)Object.creat()接受两个参数:1要继承的原型,2对象的详细描述对象的属性的获取两种写法:点或者方括号,点后面放属性名称,括号里面放表达式属性获取过程详解当JS的解释器在代码中遇到“.”或者 “[ ]”的时候将进行以下操作对“.”或者 “[ ]”前面的表达式进行计算并获取结果如果是undefined或null直接报错如果不是对象就转换成对应的对象如果是“.”,则查找对应的属性如果是 “[ ]”,先计算“[ ]”里面的表达式,再将结果转换为字符串,并查找对应的属性如果该属性存在则返回对应的值如果该属性不存在则返回undefined

March 27, 2019 · 1 min · jiezi

【视频教程】JS空数据比较-冰山工作室-沙翼-web前端

课程视频-JS空数据比较前言先上题,得出心中答案,打开浏览器点开F12,复制下面代码,看看结果。console.log( [] == ![] )console.log( {} == !{} )剖析一下,主要分为:!逻辑运算符的优先级,{}与[]复杂数据类型如何转换;== JS的数据类型的强制转换比较;逻辑运算符的优先级运算符优先级本身是一种规则,该规则在计算表达式时控制运算符执行的顺序。具有较高优先级的运算符先于较低优先级的运算符执行。先看MDN运算符优先级图表截取:优先运算类型关联性运算符20圆括号n/a(…)19new(带参数列表)从左到右new…(…) 函数调用从左到右..(..)16逻辑非从右到左!… 一元加法从右到左+… 一元减法从右到左-…10等号从左到右…==…6逻辑与从左到右…&&…5逻辑或从左到右…II…MDN完整地址在截取的表格中可以清晰的看到,逻辑非 ! 的优先级明显高于 == 等号的有优先级,因此第一个问题,在 [] == ![] 中最优先运算的是 ![] ,然后才是 == 比较。复杂的数据类型如何转换console.log(![]) // false ,这个结果相对好理解// 注意: !带有隐式转换undefined(未定义,找不到值时出现)null(代表空值)false(布尔值的false,字符串"false"布尔值为true)0(数字0,字符串"0"布尔值为true)NaN(无法计算结果时出现,表示"非数值";但是typeof NaN===“number”)""(双引号)或’’(单引号) (空字符串,中间有空格时也是true)6种值转化为布尔值时为 false 。当前结论 ![] == false,当然,在使用 == 时永远不要大意!参见附1;接下来,难题在于, [] 如何转化进行比较。请先记住一个比较的基本规则:数组与数值进行比较,会先转成数值,再进行比较;与字符串进行比较,会先转成字符串,再进行比较;与布尔值进行比较,两个运算子都会先转成数值,然后再进行比较。附1:相等运算符(==)隐藏的类型转换,会带来一些违反直觉的结果,下面整理一些:0 == ’’ // true0 == ‘0’ // true 2 == true // false2 == false // false// 参见图1第7条 false == ‘false’ // falsefalse == ‘0’ // true false == undefined // falsefalse == null // falsenull == undefined // true ’ \t\r\n ’ == 0 // true// \t \r \n都是转义字符,空格就是单纯的空格,输入时可以输入空格// \t 的意思是 横向跳到下一制表符位置// \r 的意思是 回车// \n 的意思是回车换行遵循上边的规则(左侧x为数组时),需要将 [] 与 false 一并转化为数字类型后再进行比较。OK,那么这个规则是谁说的算的呢?截取一张知乎大佬贴的Es5 规范元知识图,下述比较参见 7 条。附2(中文版)附2:参照第7条:ToNumber(false) // 0为啥呢?上图[] 依照图2,进入第9条,使用 ToPrimitive([]) ,上图好吧,要根据类型默认使用 DefaultValue 方法,上图[] 属于字符串hint,那么执行 toString() ,console.log([].toString()) // “";终于,表达式看起来不费劲了,”" == false ;每次对比Es5规范非常不方便,所以结尾总结了一下可以快速判断==转化判断依据的原则,不必每次都参照图2啦 - 附3完美 0 == 0 // true附3x == y同类型原则总结:Number比数值(+0,-0相等);String比长短与字符序列(charCode);Boolean中false == false; // true;复杂数据类型比较引用地址;x == y不同基础数据类型比较原则总结:x或y出现NaN一定返回false;x或y出现Boolean一定全部转化数字后在比较;x或y出现Number一定全部转化数字后比较;x == y 包含复杂数据类型原则:x或y出现复杂数据类型通过valueOf()或toString()转化为基本数据类型, 然后参照上述规则比较;x == y特殊总结:Null 与 Undefined除彼此或自身外,一律返回false;NaN == NaN; // false关于我们原始高清视频下载视频讲解-提取码:sjgyQQ答疑交流群: 600633658我们的链接:知乎 掘金 今日头条 新浪微博 前端网 思否 简书 B站 ...

March 26, 2019 · 1 min · jiezi

8个有用的 CSS 技巧:视差图像,sticky footer 等等

CSS是一种独特的语言。乍一看,这似乎很简单,但是,某些在理论上看起来很简单的效果在实践中往往不那么明显。在本文中,我将分享一些有用的技巧和技巧,它们代表了我在学习CSS过程中的关键进展。本文并不是要演示CSS可以变得多么复杂。相反,它分享了一些在大多数CSS教程中不太可能找到的有用技巧。1. Sticky Footer这个非常常见的需求,但对于初学者来说可能是个难题。对于大多数项目,不管内容的大小,都希望页脚停留在屏幕的底部—如果页面的内容经过了视图端口,页脚应该进行调整。在CSS3之前,如果不知道脚的确切高度,就很难达到这种效果。虽然我们称它为粘性页脚,但你不能简单地用 position: sticky 来解决这个问题,因为它会阻塞内容。今天,最兼容的解决方案是使用 Flexbox。主要的做法是在包含页面主要内容的 div 上使用不太知名的 flex-grow 属性,在下面的示例中,我使用的是 main 标签。flex-grow 控制 flex 项相对于其他 flex 元素填充其容器的数量。当值为 0 时,它不会增长,所以我们需要将它设置为 1 或更多。在下面的示例中,我使用了简写属性 flex: auto,它将 flex-grow 默认设置为 1。为了防止任何不必要的行为,我们还可以在 footer 标签中添加 flex-shrink: 0。flex-shrink 实际上与 flex-growth 属性相反,控制 flex 元素收缩到适合其容器的大小,将它设置为 0 刚防止 footer 标签收缩,确保它保留其尺寸。// html<div id=“document”> <main> <h1>Everything apart from the footer goes here</h1> <p>Add more text here, to see how the footer responds!</p> </main> <footer> <h1>The footer goes here</h1> </footer></div>// css#document { height: 100vh; display: flex; flex-direction: column;}main { flex: auto;}footer { flex-shrink: 0;}/* Other styling elements, that are not necessary for the example / { margin: 0; font-family: Candara;}h1, p { padding: 20px;}footer { color: white; background: url(https://images.unsplash.com/photo-1550795598-717619d32900?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=676&q=80); background-position: center; background-repeat: no-repeat; background-size: cover;}footer > h1 { text-shadow: 1px 1px 4px #00000080;}查看演示2. Zoom-on-Hoverzoom-on-hover 效果是将注意力吸引到可点击图像上的好方法。当用户将鼠标悬停在上面时,图像会稍微放大,但其尺寸保持不变。为了达到这个效果,需要用 div 标签包裹 img 标签。要使此效果生效,需要设置父元素的 width 和 height ,并确保将 overflow 设置为 hidden,然后,你可以将任何类型的转换动画效果应用于内部图像。// html<div class=“img-wrapper”> <img class=“inner-img” src=“https://source.unsplash.com/random/400x400" /></div><!– Additional examples –><div class=“img-wrapper”> <img class=“inner-img” src=“https://source.unsplash.com/random/401x401" width=“400px” height=“400px” /></div><div class=“img-wrapper”> <img class=“inner-img” src=“https://source.unsplash.com/random/402x402" width=“400px” height=“400px” /></div>// css.img-wrapper { width: 400px; height: 400px; overflow: hidden; }.inner-img { transition: 0.3s;}.inner-img:hover { transform: scale(1.1);}查看演示3. 即时夜间模式如果你正在寻找一个快速的方法来应用“夜间模式”皮肤到你的网站,可以使用 invert 和 hue-rotate 过滤器。filter: invert() 的范围是从 0 到 1,其中 1 从白色变为黑色。filter: hue-rotate() 改变元素的颜色内容,使它们或多或少保持相同的分离水平, 其值范围为 0deg 至 360deg。通过将这些效果组合在 body 标签上,可以快速试用网站的夜间模式(注意,为了影响背景,你必须给它一个颜色。)使用这些设置,我们可以给谷歌的主页一个即时改造:4.自定义的要点要为无序列表创建自定义项目符号,可以使用 content 属性和 ::before 伪元素。在下面的 CSS 中,我使用 .complete 和 .incomplete 两个类来区分两种不同类型的项目符号。ul { list-style: none;}ul.complete li::before { content: ‘???? ‘;}ul.incomplete li::before { content: ‘☐ ‘;}查看演示额外用途:面包屑导航利用 content 属性有许多更有用的方法,这里忍不住又多介绍一种。由于用于分隔面包屑的斜杠和其他符号具有样式性,所以在CSS中定义它们很有意义。和本文中的许多例子一样,这种效果依赖于CSS3中提供的伪类——last-child——:.breadcrumb a:first-child::before { content: " » “;}.breadcrumb a::after { content: " /”;}.breadcrumb a:last-child::after { content: “”;}查看演示5. 视差图像 (Parallax Images)这种引人注目的效果越来越受欢迎,当用户滚动页面时,它可以给页面带来生气。当一个页面的正常图像随着用户滚动而移动时,视差图像看起来是固定的——只有通过它可见的窗口才会移动。仅 CSS 示例// html<div class=“wrapper”> <h1>Scroll Down</h1> <div class=“parallax-img”></div> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p> <p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.</p></div><div class=“wrapper”></div>// css.wrapper { height: 100vh;}.parallax-img { height: 100%; background-attachment: fixed; background-position: center; background-repeat: no-repeat; background-size: cover;}/* Other styling elements, that are not necessary for the example */ body { margin: 0; background: #000; } * { font-family: Candara; color: white; } h1 { margin: 15px; text-align: center; } p { margin: 15px; font-size: 1.1rem; } .parallax-img { background-image: url(‘https://source.unsplash.com/random/1920x1080'); }查看演示CSS + JavaScript 示例要获得更高级的效果,可以使用 JavaScript 在用户滚动时向图像添加移动。// html<div class=“block”> <img src=“https://unsplash.it/1920/1920/?image=1005" data-speed="-1” class=“img-parallax”> <h2>Parallax Speed -1</h2></div><div class=“block”> <img src=“https://unsplash.it/1920/1920/?image=1067" data-speed=“1” class=“img-parallax”> <h2>Parallax Speed 1</h2></div><div class=“block”> <img src=“https://unsplash.it/1920/1920/?gravity=center" data-speed="-0.25” class=“img-parallax”> <h2>Parallax Speed -0.25</h2></div><div class=“block”> <img src=“https://unsplash.it/1920/1920/?image=1080" data-speed=“0.25” class=“img-parallax”> <h2>Parallax Speed 0.25</h2></div><div class=“block”> <img src=“https://unsplash.it/1920/1920/?random" data-speed="-0.75” class=“img-parallax”> <h2>Parallax Speed -0.75</h2></div><div class=“block”> <img src=“https://unsplash.it/1920/1920/?blur" data-speed=“0.75” class=“img-parallax”> <h2>Parallax Speed 0.75</h2></div>// css@import url(https://fonts.googleapis.com/css?family=Amatic+SC:400,700);html, body{ margin: 0; padding: 0; height: 100%; width: 100%; font-family: ‘Amatic SC’, cursive;}.block{ width: 100%; height: 100%; position: relative; overflow: hidden; font-size: 16px;}.block h2{ position: relative; display: block; text-align: center; margin: 0; top: 50%; transform: translateY(-50%); font-size: 10vw; color: white; font-weight: 400;}.img-parallax { width: 100vmax; z-index: -1; position: absolute; top: 0; left: 50%; transform: translate(-50%,0); pointer-events: none}// js// I know that the code could be better.// If you have some tips or improvement, please let me know.$(’.img-parallax’).each(function(){ var img = $(this); var imgParent = $(this).parent(); function parallaxImg () { var speed = img.data(‘speed’); var imgY = imgParent.offset().top; var winY = $(this).scrollTop(); var winH = $(this).height(); var parentH = imgParent.innerHeight(); // The next pixel to show on screen var winBottom = winY + winH; // If block is shown on screen if (winBottom > imgY && winY < imgY + parentH) { // Number of pixels shown after block appear var imgBottom = ((winBottom - imgY) * speed); // Max number of pixels until block disappear var imgTop = winH + parentH; // Porcentage between start showing until disappearing var imgPercent = ((imgBottom / imgTop) * 100) + (50 - (speed * 50)); } img.css({ top: imgPercent + ‘%’, transform: ’translate(-50%, -’ + imgPercent + ‘%)’ }); } $(document).on({ scroll: function () { parallaxImg(); }, ready: function () { parallaxImg(); } });});查看演示6. 裁剪图像动画与粘性页脚一样,在 CSS3 之前裁剪图像也非常棘手。现在,我们有两个属性使裁剪变得简单,object-fit 和 object-position,它们一起允许你更改图像的尺寸而不影响它的长宽比。以前,总是可以在照片编辑器中裁剪图像,但是在浏览器中裁剪图像的一个很大的优势是可以将图像大小调整为动画的一部分。为了尽可能简单地演示这种效果,下面的示例使用 <input type=“checkbox”> 标记触发这种效果。这样,我们可以利用CSS的 :checked 伪类,我们不需要使用任何JavaScript:// html<input type=“checkbox” /><br /><img src=“https://source.unsplash.com/random/1920x1080" alt=“Random” />input { transform: scale(1.5) margin:10px 5px;}img { width: 1920px; height: 1080px; transition: 0s;}input:checked +br + img{ width: 500px; height: 500px; object-fit: cover; object-position: left-top; transition: width 2s, height 4s;}查看演示7. 混合模式(Blend Modes)如果你有使用 Photoshop 的经验,你可能知道它不同的混合模式是多么强大,可以创建有趣的效果。但是你知道 Photoshop 的大部分混合模式也可以在 CSS 中使用吗?当图像的被设置为 background-color:lightblue; blend-mode:difference ; ,这就是Medium 的主页的样子:此外,背景并不是利用混合模式的唯一方法。mix-blend-mode 属性允许你将元素与其现有背景进行混合。例如,使用如下样式创建这样的效果: // html<h1>This is an example title</h1>// cssh1 { mix-blend-mode: color-dodge; font-family: Candara; font-size: 5rem; text-align: center; margin: 0; padding: 20vh 200px; color: lightsalmon; } html, body { margin: 0; background-color: white; } body { background-image: url(https://images.unsplash.com/photo-1550589348-67046352c5f3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80); background-repeat: no-repeat; background-size: cover; min-height: 100vh; overflow: hidden;}查看演示8. Pinterest-style 图像CSS Grid和Flexbox使得实现多种不同类型的响应式布局变得更加容易,并且允许我们在页面上很容易地将元素垂直居中——这在以前是非常困难的。然而,它们不太适合的一种布局风格是 Pinterest 使用的布局风格,即每个元素的垂直位置都根据其上方元素的高度而变化。实现此目的的最佳方法是使用 CSS 的列属性套件。 这些最常用于创建多个报纸样式的文本列,但这是另一个很好的用例。要实现这一点,需要将元素包装在 div 中,并为该包装器提供一个 column-width 和 column-gap 属性。然后,为了防止任何元素被分割到两个列中,使用 column-break-inside:avoid 将其添加到单个元素中。// html<div id=“columns”> <figure> <img src=”//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/cinderella.jpg”> <figcaption>Cinderella wearing European fashion of the mid-1860’s</figcaption> </figure> <figure> <img src=”//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/rapunzel.jpg”> <figcaption>Rapunzel, clothed in 1820’s period fashion</figcaption> </figure> <figure> <img src=”//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/belle.jpg”> <figcaption>Belle, based on 1770’s French court fashion</figcaption> </figure> <figure> <img src=”//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/mulan_2.jpg"> <figcaption>Mulan, based on the Ming Dynasty period</figcaption> </figure> <figure> <img src="//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/sleeping-beauty.jpg"> <figcaption>Sleeping Beauty, based on European fashions in 1485</figcaption> </figure> <figure> <img src="//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/pocahontas_2.jpg"> <figcaption>Pocahontas based on 17th century Powhatan costume</figcaption> </figure> <figure> <img src="//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/snow-white.jpg"> <figcaption>Snow White, based on 16th century German fashion</figcaption> </figure> <figure> <img src="//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/ariel.jpg"> <figcaption>Ariel wearing an evening gown of the 1890’s</figcaption> </figure> <figure> <img src="//s3-us-west-2.amazonaws.com/s.cdpn.io/4273/tiana.jpg"> <figcaption>Tiana wearing the <i>robe de style</i> of the 1920’s</figcaption> </figure> <small>Art &copy; <a href="//clairehummel.com">Claire Hummel</a></small> </div>// css@font-face{font-family:‘Calluna’; src:url(‘https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/callunasansregular-webfont.woff') format(‘woff’);}body { background: url(//subtlepatterns.com/patterns/scribble_light.png); font-family: Calluna, Arial, sans-serif; min-height: 1000px;}#columns { column-width: 320px; column-gap: 15px; width: 90%; max-width: 1100px; margin: 50px auto;}div#columns figure { background: #fefefe; border: 2px solid #fcfcfc; box-shadow: 0 1px 2px rgba(34, 25, 25, 0.4); margin: 0 2px 15px; padding: 15px; padding-bottom: 10px; transition: opacity .4s ease-in-out; display: inline-block; column-break-inside: avoid;}div#columns figure img { width: 100%; height: auto; border-bottom: 1px solid #ccc; padding-bottom: 15px; margin-bottom: 5px;}div#columns figure figcaption { font-size: .9rem; color: #444; line-height: 1.5;}div#columns small { font-size: 1rem; float: right; text-transform: uppercase; color: #aaa;} div#columns small a { color: #666; text-decoration: none; transition: .4s color;}div#columns:hover figure:not(:hover) { opacity: 0.4;}@media screen and (max-width: 750px) { #columns { column-gap: 0px; } #columns figure { width: 100%; }}查看演示上面的例子也是 CSS:not() 伪类的一个很好的例子。他将它与 :hover 一起使用,这样除了盘旋的元素外,其他元素都将淡出。其它的资源总的来说,我希望下面的例子已经了说明了一些有用的 CSS 效果,甚至可能会让你注意到一些你没有见到过的特性。像这样的特性并不属于“简单技巧”的范畴,它们可以自己进行相当深入的探索。所以我不打算在这里描述它们,下面介绍一些很好资源来了解它们:Keyframe animationhttps://css-tricks.com/snippe…Scroll-snappinghttps://css-tricks.com/practi…多级导航http://bradfrost.com/blog/pos…3D 效果https://www.the-art-of-web.co…CSS的打印https://www.the-art-of-web.co…http://edutechwiki.unige.ch/e…设计原则https://material.io/你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

March 25, 2019 · 6 min · jiezi

Play Framework升级到2.6.x的填坑记录

为了使用最新的Play WS Api,升级到play 2.6.211.配置修改根据官网的升级指南,修改plugins.sbt文件,更改插件版本号:addSbtPlugin(“com.typesafe.play” % “sbt-plugin” % “2.6.21”)build.sbt文件中,把guice和play-json单独加入。libraryDependencies += guicelibraryDependencies += “com.typesafe.play” %% “play-json” % “2.6.0"build.sbt中修改依赖库cache和JavaWs的名字为ehcache和ws。修改后如下libraryDependencies ++= Seq( javaJdbc , ehcache , ws)然后点击菜单View/Tool windwos/SBT打开sbt窗口,点击刷新,然后下载新的依赖包,虽然消耗不少时间,最终顺利完成,没有遇到太大麻烦。2.运行修改完配置之后,运行play(使用 sbt run命令)。提示 \views\index.scala.html:18: not found: value welcome。此文件为首页的Twirl模板。推测可能是2.6版本和2.5版本的首页模板不同,于是到官网下载2.6版本的starter-example,找到views/index.scala.html并覆盖项目的相应文件。再次运行,还是提示同样错误。这个错误的原因是views.html包下没有welcome对象,views.html.play20包下也没有,据说是因为低版本的play下载了某个低版本依赖包,导致高版本play不再下载新版本的依赖包导致。此问题无法解决,因为这个欢迎页面可有可无,因此最终决定从views/index.scala.html中删除@welcome的调用。编译通过,开发模式下运行没有问题。3.生产环境部署和上一版本一样,命令行上 运行sbt dist或者sbt universal:packageBin打包项目为zip文件。复制到服务器上,解压zip文件,运行/bin/xxxx.bat启动web服务器,出现“输入行太长”的错误无法启动web服务器。直接原因是java -cp “…” 命令太长,超过windows命令行8000字节的限制,导致windows无法执行此命令。这个命令长的部分是 -cp后面指定的jar包文件,因为依赖的jar包太多,sbt把jar文件名全部串接起来放在cp后面,导致这个参数非常长。上一版本还勉强没超过限制,升级到新版本后,依赖包又增加了很多个,导致命令长度超限制。“输入行太长”错误的一般解决方法是,把bat文件所在路径的基础路径名尽可能缩短,把bat的上两级目录的文件夹名缩短到2个字符,再运行bat文件,这时不再报“输入行太长”错误,但出现新错误“无法找到或者加载主类play.core.server.ProdServerStart”的错误。一开始怀疑是javac选项或者jdk版本导致的,用了很长时间排查仍然无法解决问题,在stackoverflow上找到的方法也不成功。偶然想到,这个错误是不是也是因为命令长度太长导致的,于是在stackoverflow上找到一个彻底解决命令太长的方法。在build.sbt文件中添加插件LauncherJarPlugin,本来不抱太大希望,想不到运行sbt start竟然成功了,然后打包部署也成功了。添加插件LauncherJarPlugin的语句:lazy val xxxx = (project in file(”.")).enablePlugins(PlayJava,LauncherJarPlugin)至此,play framework2.6.21升级成功完成。

March 20, 2019 · 1 min · jiezi

开始在web中使用JS Modules

本文由云+社区发表作者:原文:《Using JavaScript modules on the web》 https://developers.google.com…译者序JS modules,即ES6的模块化特性,通过 <scripttype=“modules”>可以实现不经过打包直接在浏览器中import/export,此玩法确实让人眼前一亮。先看看 <scripttype=“modules”>的兼容性。目前只有较新版本的chrome/firefox/safari/edge支持此特性,看来要普及使用还任重道远。下面跟着这篇文章深入了解一下涨涨姿势。本文将介绍JS模块化;怎样在不经过打包的情况下直接在浏览器中使用模块化;以及Chrome团队在JS模块化的优化和普及上正在做的一些事情。JS模块化你可能用过命名空间、CommonJS或者AMD规范进行JS模块化,但所有的这些模块解决方案万变不离其宗:引入(import)其他模块,作为一个模块输出(export)。如果说命名空间、CommonJS、AMD都是野路子,那ES6的JS modules则是正规军,将模块化语法统一起来(一统江湖,千秋万代)。在JS modules中,你可以使用 export关键字输出任何东西: const、 function等。// lib.mjsexport const repeat = (string) => ${string} ${string};export function shout(string) { return ${string.toUpperCase()}!;}然后你可以用 import关键字从另一个模块中引进来。下面代码将lib模块中的 repeat和 shout函数引到了我们的主模块main中。// main.mjsimport {repeat, shout} from ‘./lib.mjs’;repeat(‘hello’);// → ‘hello hello’shout(‘Modules in action’);// → ‘MODULES IN ACTION!‘你也可以通过 default关键字,输出一个默认值。// lib.mjsexport default function(string) { return ${string.toUpperCase()}!;}而通过上面的 default输出的模块,在引入时可以用其他任何变量名。// main.mjsimport shout from ‘./lib.mjs’;// ^^^^^模块脚本与常规脚本有所区别:模块脚本默认开启了严格模式不支持HTML风格的注释 <!– comment –>模块具有词法顶级作用域。也就是说在模块中 varfoo=42;并不会像传统脚本一样,创建一个全局变量 foo,可以通过 window.foo访问。新的 import和 export语法仅限于在模块脚本中使用,不能用在常规脚本中。正因为这些差异,模块脚本和传统脚本显然需要各自不同的解析方式。因此JS解析器需要标识出哪些脚本属于是模块类型的。浏览器如何识别模块脚本你可以通过设置 <script>元素的 type属性为 module,以此告诉浏览器这段script需要以模块进行处理。<script type=“module” src=“index.mjs”></script> <!–下文称作模块脚本–><script nomodule src=“fallback.js”></script> <!–下文称作传统脚本–>那些支持 type=module的浏览器会忽略掉 nomodule的脚本,而不兼容也会优雅降级,执行fallback.js。译者注:亲测在IE7+到edge,oppo手机自带的浏览器都能够降级而执行fallback.js。不过加载fallback的同时,也会把index.mjs一并加载,而支持module的浏览器则不会加载fallback。IE系列均会执行fallback.js 加载fallback的同时,也会把index.mjs一并加载 而支持module的浏览器则只会加载模块有没想过另外一个好处:既然浏览器能够识别module,那它必然也能够支持ES67的其他特性,如箭头函数、async-await。你不需要为这些特性进行babel编译,现代浏览器跑着更小和最大部分未编译的模块化代码,而不兼容的则使用nomodule的降级代码。浏览器加载方面的异同:模块脚本vs传统脚本上面介绍了模块脚本和传统脚本在语言层面的异同,除此之外,在浏览器加载过程中也有所不同。同样的模块脚本只会执行一次,而传统脚本会声明多次。<script src=“classic.js”></script><script src=“classic.js”></script><!– classic.js executes multiple times. –><script type=“module” src=“module.mjs”></script><script type=“module” src=“module.mjs”></script><script type=“module”>import ‘./module.mjs’;</script><!– module.mjs executes only once. –>模块脚本跨域需要加跨域头模块脚本及其依赖是通过CORS来获取的,也就是说模块脚本一旦跨域就需要加上适当的返回头,比如 Access-Control-Allow-Origin:。而众所周知,传统脚本则不需要(译者注:还记得传说中的JSONP吗)。async属性对内联脚本有效<script async>var test = 1;</script><!– async无效 –><script async type=“module”>import {a} from ‘./a.mjs’</script><!– async有效 –>加了async属性会使得脚本在下载过程中不阻塞DOM渲染,而下载完成后立即执行,两个async脚本之间的执行时序不确定,执行时机也不确定,有可能在domContentLoaded之前或者之后。但这一属性对传统的内联脚本是无效的,而对模块的内联脚本却是有效的。关于 .mjs文件后缀你可能会对前面的 .mjs后缀感到好奇,但是在互联网的世界里,文件后缀并不重要,只要服务器下发的MIME类型( Content-Type:text/javascript)正确就可以。浏览器是通过script标签上的type属性来识别模块脚本的,而不是后缀名。所以无论使用 .js还是 .mjs都是可以的。但是我们还是建议使用 .mjs,原因有两个:在开发的时候,可以不需要看代码,通过后缀名非常直观地看出哪些是模块脚本。nodejs中,ES6的模块化特性仍在实验性阶段,而该特性只支持 .mjs后缀的脚本。模块资源标识符 - module specifier在import一个模块时,后面的相对或绝对路径字符串称为module specifier或import specifier,也就是模块资源路径。import {shout} from ‘./lib.mjs’;// ^^^^^^^^^^^浏览器对于模块资源路径做了一些限制。不支持类似下面这种只有模块名或部分文件名的资源路径(称之为bare module specifiers)。这样的限制是为了以后浏览器在支持自定义模块加载器之后,加载器能够自行决定bare module specifiers的解析方式。// Not supported (yet):import {shout} from ‘jquery’;import {shout} from ’lib.mjs’;import {shout} from ‘modules/lib.mjs’;目前,模块资源路径必须是完整的URL,或者以 /, ./, ../开头的相对URL// Supported:import {shout} from ‘./lib.mjs’;import {shout} from ‘../lib.mjs’;import {shout} from ‘/modules/lib.mjs’;import {shout} from ‘https://simple.example/modules/lib.mjs';模块script默认是defer传统脚本的加载和解析会阻塞html的解析,可以通过添加 defer属性解决(让脚本加载和html解析并行)但这里想告诉你的是,模块脚本默认具备defer的并行功能,因此无需画蛇添足加上defer属性。还有不仅仅只有主模块与html解析并行,其他子模块也一样。JS模块化的其他特性动态引入: import()我们之前仅仅用到了静态的 import,它需要在首屏就把全部模块资源都下载下来。但有时候按需加载或异步加载会更为合理,这有助于提高首次加载时间,而 import()可以用来解决这个问题。<script type=“module”> (async () => { const moduleSpecifier = ‘./lib.mjs’; const {repeat, shout} = await import(moduleSpecifier); // lib会在主模块及其依赖都加载并执行完毕之后才会import repeat(‘hello’); // → ‘hello hello’ shout(‘Dynamic import in action’); // → ‘DYNAMIC IMPORT IN ACTION!’ })();</script>不像静态 import只能用在 <scripttype=“module>“一样,动态 import()也可以用在普通的script。具体可以看下我们关于动态import的文章。NOTE: Webapck自己实现了一套 import()方案,可以动态将import()进去的模块抽离出来,生成单独的文件。import.meta另一个和JS modules相关的新特性是 import.meta,它能提供关于当前模块的meta信息。准确的meta信息并不是ECMAScript规范指定的部分,它取决于宿主环境。在浏览器拿到的meta信息和在nodejs里面拿到的是有区别的。下面的例子中,图片的相对路径默认是基于HTML所在位置来解析的,但通过 import.meta.url可以实现基于当前模块来解析。function loadThumbnail(relativePath) { const url = new URL(relativePath, import.meta.url); const image = new Image(); image.src = url; return image;}const thumbnail = loadThumbnail(’../img/thumbnail.png’);container.append(thumbnail);性能优化建议继续使用打包工具通过模块脚本,开发时我们可以无需再用webpack、Rollup、Parcel等打包工具就可以享受原生的模块化福利,在以下场景建议可以直接使用原生的模块脚本:开发环境下不超过100个模块且相对较浅的依赖层级关系(小于5)的小型web应用然而,我们在性能瓶颈分析中发现,加载一个模块化库(大约300个模块),经过打包的性能数据要比未经过打包直接使用原生模块脚本的好。其中一个原因是 import/ export语法是可以静态分析的,因此打包工具在打包过程中就可以进行静态分析并移除冗余未使用的模块。从这可以看出,静态的 import/ export不仅仅只是语法特性,还具备关键的工具属性(可静态分析)!我们的总体建议是继续使用打包工具进行上线前的模块打包处理。毕竟从某种程度上,打包可以帮助你尽可能减少代码体积,用户不必要加载无用的脚本,更有利于页面性能。开发者工具的代码覆盖率检查能帮助你检测源码中是否存在无用代码。我们同时也建议通过代码分割对模块进行合理拆分,以及延迟加载非首屏关键路径的脚本。打包与使用模块脚本的权衡取舍通常在web开发领域,所有方案都有利弊,需要权衡取舍。与加载一个未经过代码拆分的打包脚本相比,使用模块脚本也许会降低首次加载性能(cold cache),但是可以提升用户再次加载(warm cache)的速度。比如对于总大小200KB的代码,在修改一个细颗粒化的模块之后,那么用户只需要更新有变更的代码,这总比重新加载所有代码(打包脚本)要强。如果相对于首次访问体验来说,你更关注用户再次访问体验,并且你的应用不超过数百个细颗粒化模块的话,你不妨尝试下使用模块脚本,通过性能数据对比之后再做出最后的选择。浏览器工程师们正努力提升模块脚本的性能,我们希望模块脚本以后能够适用于更多的应用场景。使用细颗粒化的模块尽可能让你的代码以细颗粒化的模块进行组织。当在开发时,每个模块最好不要输出过多的内容。下面的 ./util.mjs模块,输出了 drop pluck和 zip三个函数。export function drop() { / … / }export function pluck() { / … / }export function zip() { / … / }如果你的代码仅仅只需要 pluck,你也许会这样引入:import { pluck } from ‘./util.mjs’;在这种情况下,如果没有构建打包编译,浏览器会还是会下载、解析和编译整个 ./util.js模块,即使只仅仅需要其中一个export。如果 pluck不与 drop和 zip有引用或依赖关系的话,最好还是将它独立成一个模块 ./pluck.mjs。以达到无需加载其他无用函数的目的。export function pluck() { / … */ }这不仅能够让你的源码简洁,还能够减少对打包工具(移除冗余代码)的依赖。如果在你的应用中其中一个模块从未被 import过,那么浏览器就不会去下载。而那些真正有用的模块则会被浏览器缓存起来。此外,使用细颗粒化的模块也有助于对接未来的浏览器原生打包功能。预加载模块通过 <linkrel=“modulepreload”>你可以进一步优化模块加载。浏览器会预加载甚至预解析和编译这些模块及其依赖。<link rel=“modulepreload” href=“lib.mjs”><link rel=“modulepreload” href=“main.mjs”><script type=“module” src=“main.mjs”></script><script nomodule src=“fallback.js”></script>这对于有复杂依赖关系模块的应用尤为重要。没有 rel=“modulepreload”,浏览器需要发出多个HTTP请求来计算出整个依赖关系。而如果你把所有依赖模块通过 rel=“modulepreload"提前告诉浏览器,那么浏览器则无需再渐进式地去计算。采用HTTP/2协议HTTP/2支持多路复用,多个请求及响应信息可以同时进行传输,这有助于提高模块树的加载效率。Chrome团队还预研了服务器推送——另一个HTTP/2特性,是否能够作为部署高度模块化应用的一个可行方案。但结局令人失望,HTTP/2的服务器推送比想象中要难以应用,并且web服务器及浏览器的对其实现目前并没有针对高度模块化web应用进行优化。另一方面,服务器很难只推送未被缓存的资源。如果通过告知服务器完整的用户缓存状态来解决这个问题的话,又存在隐私泄露风险。无论如何,采用HTTP/2协议吧!只要记住目前HTTP/2的服务器推送目前还不能作为一个好的解决方案。目前的使用率JS modules正在缓慢地被接纳使用。我们的使用统计显示只有0.08%(不包括动态 import()或者worklets)的页面目前使用了 <scripttype=“module”>。JS Modules未来的发展Chrome团队正在通过不同的方式,致力于提高基于JS modules的开发体验。下面列举其中的几种。更高效、确定性更高的模块解析算法我们提交了一版对于目前模块解析算法的优化。新算法目前已经被同时列入了HTML规范和ECMASciprt规范,并且已在Chrome 63版本中实现。希望这项优化能够在更多的浏览器中落地。新算法更快更高效,旧算法在计算依赖图谱(dependency graph)大小的时间复杂度为O(n²),在Chrome中的实现也是一样。而新算法则提升至O(n)。此外,新算法在报解析错误时更加准确。如果一个依赖图谱中有多个错误,那么基于旧算法,每次执行都会报不同的解析错误。这给开发调试带来不必要的困难。新算法则保证每次执行都会报相同的解析错误。Worklets 和 web workersChrome实现了worklets,允许web开发者自定义那些在浏览器底层的硬编码逻辑。目前开发者可以将一个JS模块引入到渲染管道(rendering pipeline)或者音频处理管道。Chrome65版本支持了 PaintWorklet,也称为CSS绘制API(the CSS Paint API),用于控制如何绘制一个DOM元素。const result = await css.paintWorklet.addModule(‘paint-worklet.mjs’);Chrome66版本支持了 AudioWorklet,允许开发者注入自定义的音频处理代码。同时这个版本开始了 AnimationWorklet的公测,开发者可以创造视差滚动效果(scroll-linked)以及其他高性能程序动画(procedural animations)。最后, LayoutWorklet,又称为CSS布局API(the CSS Layout API)已在Chrome67版本中实现。我们正在对Chrome中的web workers支持传入模块脚本。你可以通过输入 chrome://flags/#enable-experimental-web-platform-features开启这个特性。const worker = new Worker(‘worker.mjs’, { type: ‘module’ });在shared workers和service workers传入模块脚本也即将支持。const worker = new SharedWorker(‘worker.mjs’, { type: ‘module’ });const registration = await navigator.serviceWorker.register(‘worker.mjs’, { type: ‘module’ });包名映射表 - Package name maps在nodejs/npm中,我们经常会通过它们的包名引入模块,比如:import moment from ‘moment’;import { pluck } from ’lodash-es’;根据现行的HTML规范,类似上述的包名写法(bare import specifiers)会抛出异常。我们提交的“包名映射表”提案将会支持上述写法(包括在生产环境)。该映射表(JSON格式)将帮助浏览器将包名转换为完整资源路径(full URLs)。包名映射表目前仍处于提案阶段(proposal stage)。Web packaging:浏览器原生打包Chrome loading团队正在探索一种原生的web打包格式(下称为web packaging),作为一种新模式来分发web应用。web packaging的主要特性如下:Signed HTTP Exchanges:可以让浏览器信任某个HTTP请求对(request/response)确实是来自于所声明的源服务器。Bundled HTTP Exchanges:是多个请求对的集合,不要求当中的每个请求都进行签名(signed),只要携带某些元数据(metadata)用于描述如何将请求束作为一个整体来解析。两者结合起来,这种web打包格式就能够将多个同源资源安全地整合到一个HTTP GET相应中。市面上的打包工具如webpack、Rollup、Parcel,都会将多个模块最终打包成一个或少数几个bundle,这会导致源码中进行的模块拆分在上线后就丧失了它的意义。那么通过原生打包,浏览器可以将bundle反解成原样。简单来说,你可以把一个HTTP请求对包(Bundled HTTP Exchange)理解为一个资源文件包,它可以通过目录表(manifest)随意访问,并且里面的资源能够被高效地缓存以及根据相对优先级的高低来标记。有了这个机制,原生模块能够提升开发调试的体验。当你在Chrome开发者工具查看资源时,浏览器会精准定位到原生的模块代码中,而不需要复杂的source-map。Chrome已经实现了一部分提案(SignedExchanges),但是打包格式(bundling format)以及在高度模块化app中的应用仍在探索阶段。Layered APIs移植新的功能和API到浏览器中无可避免会带来持续性的维护成本以及运行成本。每一个新特性都会污染浏览器的命名空间,增加启动开销,并且也增大引入bug的可能性。Layered APIs的目的是以一种更具扩展性的方式通过浏览器来实现或移植一些高级API。而模块脚本是实现Layered APIs的一项关键技术。由于模块是显式引入的,所以通过模块来引入layered APIs可实现按需使用(不会默认内置)。模块的加载源可自定义,因此layered APIs实现了一套自动加载polyfill(当不支持时)的机制。模块脚本和layered APIs如何协同运作,具体细节仍在制定中,但目前的协议如下:<!– src中竖杠后面是指定polyfill的路径,浏览器不支持时可自动加载,不错的降级方式 –><script type=“module” src=“std:virtual-scroller|https://example.com/virtual-scroller.mjs”></script><virtual-scroller> <!– Content goes here. –></virtual-scroller>这个模块脚本引入了 virtual-scrollerAPI,如果浏览器支持则会直接读取内置layered APIs集合(std:virtual-scroller),反之则网络加载对应的polyfill。译者:对于Layered APIs更多的中文介绍 https://zhuanlan.zhihu.com/p/…此文已由腾讯云+社区在各渠道发布获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号 ...

March 18, 2019 · 2 min · jiezi

webpack4-Tree-Shaking优化

Tree-Shaking概念由来已久,今天再来谈一谈,是因为webpack4中有了新的优化。简单的介绍下什么是Tree-Shaking。代码不会被执行if(false) { do something}代码只写不读这样的代码可以称之为dead code。再举个很简单的列子a.js中export function readCookie(){ do something}export function createCookie(){ do something}b.js中import {readCookie} from ‘./a.js’;readCookie()因为b中并没有import createCookie,故而在webpack打包的时候会将其标记为 unused harmony最后在uglify阶段被删除掉。现在是es6时代,所以大家都已经养成通过export 暴露方法,所以没啥好说的。但一切都是建立没有副作用的基础上的,不了解的可以先看下这篇文你的Tree-Shaking并没什么卵用简单总结下,就是说如果暴露的方法,不是纯函数,可能有副作用(如参数是引用类型),那么在打包构建的过程中就不会被优化掉。举个非常明显的例子。阿里巴巴的 ant-design。import { DatePicker } from ‘antd’; // 但是这样需要引入babel-plugin-import才能按需加载import DatePicker from ‘antd/lib/DatePicker ‘; babel-plugin-importsg实际上就是把上面的写法构建成了下面的写法antd里面实际上就是export所有的组件,但是没有引入的组件,因为副作用,不能删除,所以才有了按需加载的说法。可随着webpack4.0的到来。它已经为我们消除了副作用。亲自测试了下。采用import { DatePicker } from ‘antd’ 引入的文件大小为1.18。采用 import DatePicker from ‘antd/lib/date-picker’; 引入文件大小为1.23。有点点差别甚至还高了,但不纠结,我们只需要知道就是上面的引入方式根本就不会比下面的所谓的按需引入方式大。。而且我并没有引入babel-plugin-import。即便根据文件大小,可能还有朋友持怀疑态度。那我还去打包后的代码里面截图下。我搜索了Breadcrumb这个组件,但是没有搜索到。随后我import {DatePicker, Breadcrumb } from ‘antd’;进来这个组件,并打包。可以看见import之后,能够搜索到了。并且文件大小由1.18升到了1.19好了。webpack4赶紧用起来。因为最近才接触ant-design。所以没有在webpack低版本的时候打包过ant-design。如果哪位兄弟还没升级的,可以回复下,看看在不按需加载ant-design的情况下,大概有多大。如有错误,欢迎留言指正。

March 16, 2019 · 1 min · jiezi

网页html生成图片的常用方案

如果您有一个需求是将网页生成一个快照的图片,然后需要用到该图片上传或者发送给他人的这样的需求,那么你会怎么做呢?聪明的你可能会想到canvas是否可以生成一个这样的图片呢?没错,今天就给大家推荐一个简单又好用的工具html2canvas。准备文件进入该官方网站点击此处,在官网首页开始下载资源文件,html2canvas.js或者html2canvas.min.js皆可。将该资源文件引入您需要使用该功能的页面中。开始使用给您想转换成图片的盒子标签上添加一个唯一id,这样便于找到该DOM节点。按照官方文档相关参数设置并添加代码在合适的地方来进行图片转换。html2canvas(document.querySelector("#app")).then(canvas => { document.body.appendChild(canvas)});详细使用相关参数设置可参考该官方文档,根据需要设置即可。兼容性介绍Firefox 3.5+Google ChromeOpera 12+IE9+EdgeSafari 6+关于vue-cli中使用最新版本应该可以直接通过yarn或者npm引入了,可参照官网首页介绍npm install –save html2canvas或者yarn add html2canvas如果有相关问题,还可参考另一篇文章点此查看

March 16, 2019 · 1 min · jiezi

浏览器缓存详解

https://heyingye.github.io/20…转载于: https://heyingye.github.io/ 博客

March 15, 2019 · 1 min · jiezi

API网关 | 从0开始构建SpringCloud微服务(12)

照例附上项目github链接本项目实现的是将一个简单的天气预报系统一步一步改造成一个SpringCloud微服务系统的过程。本章主要讲解API网关。项目存在的问题在目前的项目中我们构建了许多的API微服务,当第三方服务想要调用我们的API微服务的时候是通过微服务的名称进行调用的,没有一个统一的入口。使用API网关的意义集合多个API统一API入口API网关就是为了统一服务的入口,可以方便地实现对平台的众多服务接口进行管控,对访问服务的身份进行验证,防止数据的篡改等。我们的一个微服务可能需要依赖多个API微服务,API网关可以在中间做一个api的聚集。那么我们的微服务只需要统一地经过API网关就可以了(只需要依赖于API网关就可以了),不用关心各种API微服务的细节,需要调用的API微服务由API网关来进行转发。使用API网关的利与弊API网关的利避免将内部信息泄露给外部为微服务添加额外的安全层支持混合通信协议降低构建微服务的复杂性微服务模拟与虚拟化API网关的弊在架构上需要额外考虑更多编排与管理路由逻辑配置要进行统一的管理可能引发单点故障API网关的实现Zuul:SpringCloud提供的API网关的组件Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求审查与监控:动态路由:动态将请求路由到不同后端集群压力测试:逐渐增加指向集群的流量,以了解性能负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求静态响应处理:边缘位置进行响应,避免转发到内部集群多区域弹性:跨域AWS Region进行请求路由,旨在实现ELB(ElasticLoad Balancing)使用多样化Spring Cloud对Zuul进行了整合和增强。目前,Zuul使用的默认是Apache的HTTP Client,也可以使用Rest Client,可以设置ribbon.restclient.enabled=true。集成Zuul在原来项目的基础上,创建一个msa-weather-eureka-client-zuul作为API网关。添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId></dependency>添加注解添加@EnableZuulProxy启用Zuul的代理功能。@SpringBootApplication@EnableDiscoveryClient@EnableZuulProxypublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}添加配置这里配置的是设置路由的url。当有人访问 /city/ 路径的时候,就对访问这个路径的请求做一个转发,转发到msa-weather-city-eureka服务中去,同理,当有人访问 /data/ 路径的时候,API网关也会将这个请求转发到msa-weather-data-eureka服务中去。zuul: routes: city: path: /city/** service-id: msa-weather-city-eureka data: path: /data/** service-id: msa-weather-data-eureka微服务依赖API网关原来天气预报微服务依赖了城市数据API微服务,以及天气数据API微服务,这里我们对其进行修改让其依赖API网关,让API网关处理天气预报微服务其他两个微服务的调用。首先删去原来调用其他两个API微服务的Feign客户端——CityClient以及WeatherDataClient,创建一个新的Feign客户端——DataClient。package com.demo.service;import java.util.List;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import com.demo.vo.City;import com.demo.vo.WeatherResponse;@FeignClient(“msa-weather-eureka-client-zuul”)public interface DataClient { //获取城市列表 @RequestMapping(value="/city/cities",method=RequestMethod.GET) List<City> listCity()throws Exception; //通过城市Id查询对应城市的天气数据 @RequestMapping(value="/data/weather/cityId",method=RequestMethod.GET) WeatherResponse getDataByCityId(@RequestParam(“cityId”)String cityId);}在service中使用该客户端对API网关进行调用,API网关根据我们请求的路径去调用相应的微服务。@Servicepublic class WeatherReportServiceImpl implements WeatherReportService{ //@Autowired //private WeatherDataClient weatherDataClient; @Autowired private DataClient dataClient; //根据城市Id获取天气预报的数据 @Override public Weather getDataByCityId(String cityId) { //由天气数据API微服务来提供根据城市Id查询对应城市的天气的功能 WeatherResponse resp=dataClient.getDataByCityId(cityId); Weather data=resp.getData(); return data; }}在controller也是同理。 //传入城市Id得到对应城市的天气数据 @GetMapping("/cityId/{cityId}") public Weather getReportByCityId(@PathVariable(“cityId”)String cityId,Model model)throws Exception{ //获取城市Id列表 //由城市数据API微服务来提供数据 List<City>cityList=null; try { cityList=dataClient.listCity(); }catch (Exception e) { logger.error(“Exception!",e); } Weather weather=weatherReportService.getDataByCityId(cityId); return weather; }测试结果 ...

March 14, 2019 · 1 min · jiezi

Serverless 风暴来袭,前端工程师如何应对?

阿里妹导读:尽管大部分前端的工作并不涉及server,但最近半年serverless这个词汇以及其引发的热烈的讨论,深深触动了阿里巴巴高级前端技术专家伐薪。作为接触前端十余载的老开发,伐薪认为serverless可能会是接下来引起前端领域革命性变化的技术之一。今天,伐薪将为大家梳理serverless的历史发展进程以及对前端的影响,希望对前端工程师有所启发。上图是serverless 这个词最近5年在 google 的搜索趋势,可以看到最近半年已经达到巅峰。历史上前端领域的重要技术革命Ajax 的诞生先来回顾一下前端技术领域的重要历史节点,第一个节点是2005年,google的Jesse James Garrett 发表了一篇文章——《Ajax:Web应用程序的新方法》,首次发布了Ajax 这个新的词汇(准确说并不是新的技术,只是新的词汇),当时我还在读大二,虽然ajax不是什么新的技术,只是对XmlHttpRequest等技术的包装,但是这个技术被google宣传之后成为全球web开发的标杆,间接促进了富客户端应用(RIA)和单页应用(SPA)的流行,这些应用大都具备丝滑般的体验(局部刷新),并一直伴随着web 2.0的发展,ajax的深入人心,使得前端js的工作更加复杂和重要,专业分工越来越细,间接促进了专职的前端开发人员这一角色诞生,在此之前,web开发并不区分服务端和浏览器端的工作,因此ajax是前端领域的第一次重要事件。Nodejs 对前端规范化和工程化的促进接下来对前端变化最大的一个里程碑事件是2009年诞生的 nodejs(包括common js及npm)的出现和流行,它对前端领域的重要意义并不仅仅是让前端可以快速用js写server那么简单,个人认为它最大的贡献反而是commonjs、npm以及其便捷开发体验促进的前端工程化,它使得前端开始从刀耕火种的和传统软件工程格格不入的部署方式,发展为接近传统企业应用的研发模式,在此之前,前端开发在资源引用、依赖管理以及模块规范上缺乏有效的工具和标准,但是nodejs流行以后,基于commonjs的模块及npm的包部署和依赖管理成为主流(类似java的maven体系),并诞生了多种基于nodejs开发的cli工具辅助前端开发(如grunt、gulp),npm目前是全球最大的包管理仓库,并且成为前端项目的包依赖管理事实标准。而webpack的出现,又使得前端代码的部署更加简便,让前端可以以类似java jar包的形式发布应用(bundle),而不管项目中是何种类型的资源。React 的组件化及vdom理念第三个革命性事件是2013年开始出现的react,尽管web components标准在此之前早已发布,但是真正让组件化理念深入人心并且应用最广的库是react,它还至少有两点特性足以让它成为历史上最具前瞻性的前端库,第一个特性是vdom的出现,在此之前,所有的ui库,都直接与dom关联,但是react在UI创建与渲染引擎之间,增加了一个中间层——vdom(一个使用轻量级json描写UI结构的协议),除了改善了其本身的dom diff性能之外,还有一个重大意义就是UI的编写与渲染开始分离,一次编写,多端渲染的UI得以实现,这个多端包括server端、移动端、pc端以及其他需要展示UI的设备,之后的react native以及weex都是这一分层思想的受益者。除了vdom之外,react还有一个重要的理念非常超前,即UI是一个函数(类),函数输入一个state,一定返回确定的视图,在此之前,大部分框架和库,都会把UI分离成一个html片段(通常支持模板写法以渲染数据),一个为该html片段绑定事件的js,尽管这样比较好理解,但是react对UI这种抽象却反映了UI的实际本质,并且这种函数式理念,在后面可以看到,将与faas及serverless技术产生美妙的碰撞。react 的诞生对此后,甚至此前的框架和库都产生了深远的影响,包括不限于angular和vue都陆续采纳了它很多技术思想,并且成为前端开发领域目前已经趋于稳定的屈指可数的几个技术选型之一。再来总结一下,ajax使得前端的角色逐渐分离出来,nodejs促进了前端的开发模式向传统编程语言靠近(工程化),react的出现,基本结束了后端常常对前端”技术变化快“的吐槽,至此,前端的技术体系逐步成熟和标准化。serverless 理念与前端的关系那么为什么说下一次对前端技术领域有较大影响的理念是serverless呢,事实上,尽管serverless这个词汇由亚马逊提出来还不到几年,但是这个理念并不是什么爆炸性的新理念,在早期,cdn还不普及的时候,web工程师会把js资源和视图文件(可能是静态也可能是动态的)传到服务器,那个时候前端是需要关心服务器的,但是cdn及回源策略的普及,工程及搭建系统的大规模使用,使得前端可以快速把一个js或者静态文件扔到cdn节点,通过回源机制(cdn回源到一个动态服务),半动态的视图层渲染也成为可能,在这整个过程,前端开发无需关心任何服务器的知识,也不知道cdn有多少节点,如何做负载均衡,做gslb的,也不需要知道qps多少,一个cdn可以放各种业务各种开发的资源,可以说cdn是serverless理念的的先行者。回到应用部署,在前几年nodejs刚流行的时期,已有开发者意识到应用与机器的部署与运维成本对业务方会是个问题,出现了一些容器化的思想,比如cbu在15年出的naga,在这个naga容器里,业务逻辑是一个个插件,容器负责请求的路由分发,负载及稳定性管理,业务方只需要编写并上传最直接的业务代码即可,对业务方来说是实现了serverless的理念,因为naga的维护者帮你解决了部署及运维的问题。再说对前端息息相关的页面搭建系统以及bff层,无论是各种搭建系统(如斑马、积木盒子、TMS),还是基于graphQl的平台,还是通过web ide快速编写api gateway的产品——如cbu的mbox,都让业务开发只关心业务逻辑,无需关心部署运维知识,它们一定程度上体现了serverless的理念。serverless 将对前端的影响综上所述,前端早已与serverless产生了联系,但是很多人还没感知,接下来,serverless显示化地爆发将给前端带来更为深远的影响,主要体现在三个方面。前端将会重新回归到web应用工程师这一职能在最前面说了,前端是社会分工的细化,大约起源于2007年左右,在此之前是没有专门的前端开发角色的,通常称作web工程师或网站工程师,早期的网页大都是服务器渲染,使用asp、php、jsp等server page技术,js仅仅是web工程师需要掌握的小小技能之一,但是随着web 2.0及互联网、移动互联网、电子商务的发展,需要专门的人专注于编写具备很好兼容性和体验的UI,因此逐渐产生了专注于浏览器及移动端的前端工程师。但是前端技术领域逐渐趋于稳定,伴随着十几年的发展,各种开箱即用的库、垂直方案以及工程手段唾手可得,甚至目前出现了一些辅助工具可以把设计师的视觉稿生成UI代码,前端可以安心并且以非常低的成本编写UI和业务逻辑,而不用耗费大量精力在选型、造轮、还原视觉、处理兼容性、性能优化、调试和部署上,这种情况,前后端工种分离造成的协同成本反而放大了,因为在前后端角色分离的情况下,后端往往还会充当bff层的角色,比如为前端表现层封装各种api gateway,经常出现相互等待、联调协议的情况,而且bff层通常只是一些数据的加工,其他的角色经过短期的培训可以快速上手,因此前端一直在尝试接入到server端的bff层。在15年前端开始推广使用nodejs来部署应用,阿里内外也出现了不少nodejs的框架,如业界的express,在生产环境,包括给买家、商家以及内部人员使用的系统,有不少使用了nodejs,但是到今年2019年,再来回顾一下,发现这个数字并没有超出预期。造成这一现象的原因,个人认为归根到底还是因为分工太细导致的前端对服务器知识的缺乏,nodejs本身的定位是服务器技术,本质上在服务器要面对的问题与java无异,现有的前端jd招聘的人才,鲜有能在服务器上工作游刃有余的人,除非专门招的nodejs人才,server服务的长期运行会暴露很多问题,比如接口很慢,进程core,cpu飙升,内存泄漏等,另外负载均衡、扩缩容,高并低延等知识,大部分前端都是没有这些经验的。云计算的本质就是要让业务开发专注于业务逻辑,业务之下的硬件及软件设施都是按需采买,开箱即用,而serverless理念及相关技术,将使得开发人员不再需要关心应用及机器的问题,甚至连流量能不能撑住也不用关心了,它能自动扩缩容,因此,未来web开发人员的运维成本会大幅降低,前端也可介入到bff层的开发,而后端可以聚焦于数据处理、业务逻辑与算法。这一变革符合研发效能提升的背景,未来的云设施将会做得非常厚,非常专业、稳定,而前台开发人员可以快速地,最低成本地在云设施上建立业务逻辑,前端和服务器的前端(对整个请求链路来说,前端是相对的,只要离客户请求更近的角色都可以称自己为前端),分工将没那么明确,以前的浏览器端的前端,将逐步承担一部分服务器端接入层以及bff层的工作,返璞归真,回到历史上曾经的web开发工程师角色,这是对前端最大的一个变革。当然,serverless技术让前端回归到传统的web层,不代表前端不用掌握服务器知识了,掌握操作系统内核及网络编程知识仍有助于你编写高性能、高可用的业务应用。实时SSR将成为展示端UI的主要开发模式最早的web开发,其实处理UI都是以服务器渲染为主,比如perl、php等动态网页技术,但是在前端逐渐成为一个工种开始负责了绝大部分UI开发,并且技术域逐渐缩小到客户端范围之后,网页静态化以及客户端的渲染逐渐成为主流。但是这种模式对用户体验肯定是有问题的,导致了较多的白屏时间,而由于新的前端库如react和vue在vdom这层的抽象,服务器渲染的技术成本更低,因此SSR在最近几年,又逐步开始流行。但是SSR的难点恰恰是前面说的,服务器端人才的匮乏,虽然nodejs和vdom的普及提升了SSR的实施效率,但由于服务器知识的缺乏,通常只有少部分具备综合知识的前端会深入的实践这一领域。serverless技术的普及将把这个问题消除掉,借助于serverless技术,前端可以快速搭建一个ssr的场景,在服务器取数,在服务器渲染,直出html给到客户端,而不用关心这个渲染服务能否扛得住流量,会不会挂掉,这些事情云设施供应商会去解决。在前面说过,react有一个核心理念就是把UI看成函数,如果说一个页面是多个组件组成的,那一个组件是函数,我们可以把一个页面看成是多个函数的组合,不同函数的组合,组合成不同的导购场景,如果把一个函数看成一个微服务,一个场景就是微服务的聚合,这恰恰是faas的理念。通过serverless低成本地实时ssr,可以让客户体验更好,借助算法和大数据,还可以快速实现UI的千人n面,构建真正的导购大脑。基于场景的云开发(web ide)将成为主流开发模式在提到serverless技术的时候,有一个关联的领域不得不提,那就是web ide,很多互联网大型企业把一个web ide当成了云的基础设施并且大力投资,这是为何?个人认为有两个原因。第一个是因为serverless目前在业界使用以垂直场景为主,他们有一个共同点,就是代码足够标准、规范,场景较为垂直,代码复杂度不是很大,而且借助web ide可以快速地与云平台结合,做到一键发布,因此这种场景,是比较适合轻量的在线编码到部署全链路打通的。另一个原因是,目前所有的云设施解决的是业务运行问题,但是软件开发有一个非常大但是大部分人可能会忽略的痛点,那就是环境问题,相信很多人都有那种clone别人的项目但是废了九牛二虎之力都无法启动的问题,因为要装各种环境啊?另外就是和别人联调的时候,是不是因为各种环境部署缺失的问题,联调效率很低?借助容器如docker等技术,软件的运行及调试环境,可以快速地移植给别人复用,而目前基于js的代码编辑器已经非常强大,vscode editor就是基于js编写并且沉淀出一个库 Monaco Editor,因此可以说,大部分认为web ide可能在语法提示、智能感知上比不上本地ide的想法是过时了。同时web ide可以快速地与平台集成,深入定制,打通业务平台,一键部署,极大地提升研发效率。web ide还能解决跨地办公的问题,因为解决了环境准备这一老大难问题,你可以在家里,在公司,甚至在火车上,快速编写并交付代码。因此未来的paas平台,都将关联一个深度定制的web ide,需要编写业务逻辑时,一个连接跳转到web ide即可编码,完全无需关心本地环境问题,做到真正的envless。比如你要开发一个TMS模块,那么点击”新建“,跳到了web ide,代码会帮你初始化好,点击运行,会在云端启动服务器运行该组件,编写好之后,一键即可发布到TMS。对于各种faas、api gateway系统以及其他云服务,都会一样,web ide将成为企业云化的基础设施。尽管阿里云目前还未发布媲美cloud9以及coding.net的web ide,但是很欣喜地看到集团内部已经涌现出一些优秀的产品,如aone的ide,以及数据平台的app studio,其体验已经接近业界顶级水准。结语在云计算领域,serverless将会掀起一场革命,即使看起来与这一领域关联不大的前端,也会经受即ajax、nodejs以及react之后的又一重大变革,你做好应对了吗?本文作者:伐薪阅读原文本文来自云栖社区合作伙伴“阿里技术”,如需转载请联系原作者。

March 13, 2019 · 1 min · jiezi

微服务的消费 | 从0开始构建SpringCloud微服务(11)

照例附上项目github链接本项目实现的是将一个简单的天气预报系统一步一步改造成一个SpringCloud微服务系统的过程。本章主要讲解微服务的消费。微服务的消费模式微服务的消费模式主要有:服务直连模式客户端发现模式服务端发现模式下面我们主要讲解客户端发现模式,以及服务端发现模式。客户端发现模式客户端发现模式的具体流程如下:1)服务实例启动后,将自己的位置信息提交到服务注册表中。2)客户端从服务注册表进行查询,来获取可用的服务实例。3)客户端自行使用负载均衡算法从多个服务实例中选择出一个。服务端发现模式服务端发现模式与客户端发现模式的区别在于:服务端发现模式的负载均衡由负载均衡器(独立的)来实现,客户端不需要关心具体调用的是哪一个服务。微服务的消费者下面我们价格介绍一些常见的微服务的消费者。HttpClientHttpClient是常见的微服务的消费者,它是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP协议最新的版本和建议。RibbonRibbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Ribbon常与Eureka结合使用。在典型的分布式部署中,Eureka为所有的微服务提供服务注册,Ribbon提供服务消费的客户端,含有许多负载均衡的算法。FeignFeign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件。总起来说,Feign具有如下特性:可插拔的注解支持,包括Feign注解和JAX-RS注解;支持可插拔的HTTP编码器和解码器;支持Hystrix和它的Fallback;支持Ribbon的负载均衡;支持HTTP请求和响应的压缩。集成Feign本节我们主要讲解如何集成Feign,实现微服务的消费功能。添加配置pom.xml配置文件,添加如下依赖。 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> application.yml文件,添加如下配置服务的名称与工程的名称一致,并且设置请求一个服务的connect与read的超时时间。spring: application: name: micro-weather-eureka-client-feigneureka: client: service-url: defaultZone: http://localhost:8761/eureka/feign: client: config: feignName: connectTimeout: 5000 readTimeout: 5000添加注解添加@EnableFeignClients注解,启动Feign功能。package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.servlet.ServletComponentScan;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.netflix.feign.EnableFeignClients;import org.springframework.scheduling.annotation.EnableScheduling;//@ServletComponentScan(basePackages=“com.demo.web.servlet”)@SpringBootApplication@EnableDiscoveryClient@EnableScheduling@EnableFeignClientspublic class Sifoudemo02Application { public static void main(String[] args) { SpringApplication.run(Sifoudemo02Application.class, args); }}创建Feign客户端创建一个Feign客户端,通过其CityClient的接口,可以调用城市数据API微服务mas-weather-city-eureka中的cities接口,返回城市列表。package com.demo.service;import java.util.List;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import com.demo.vo.City;//创建一个Feign客户端 获取城市数据微服务中的城市列表信息@FeignClient(“msa-weather-city-eureka”)public interface CityClient { @RequestMapping(value="/cities",method=RequestMethod.GET) List<City> listCity()throws Exception;}使用Feign客户端@RestControllerpublic class CityController{ @Autowired private CityClient cityClient; @GetMapping("/cities") public List<City> listCity(){ List<City> cityList=cityClient.listCity(); return cityList; }}测试先启动Eureka服务端,然后启动被请求的微服务——城市数据API微服务,最后启动Feign客户端。 ...

March 11, 2019 · 1 min · jiezi

服务的高可用 | 从0开始构建SpringCloud微服务(10)

照例附上项目github链接本项目实现的是将一个简单的天气预报系统一步一步改造成一个SpringCloud微服务系统的过程。本章主要讲解实现服务的高可用。什么是高可用高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。假设系统一直能够提供服务,我们说系统的可用性是100%。如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。很多公司的高可用目标是4个9,也就是99.99%,这就意味着,系统的年停机时间为8.76个小时。如何保障系统的高可用我们都知道,单点是系统高可用的大敌,单点往往是系统高可用最大的风险和敌人,应该尽量在系统设计的过程中避免单点。方法论上,高可用保证的原则是“集群化”,或者叫“冗余”:只有一个单点,挂了服务会受影响;如果有冗余备份,挂了还有其他backup能够顶上。保证系统高可用,架构设计的核心准则是:冗余。有了冗余之后,还不够,每次出现故障需要人工介入恢复势必会增加系统的不可服务实践。所以,又往往是通过“自动故障转移”来实现系统的高可用。下面我们重点讲解通过集成Eureka设置多节点的注册中心,从而实现服务的高可用。实现服务的高可用集成Eureka我们需要将前面拆分好的四个微服务:城市数据API微服务,天气数据采集微服务,天气数据API微服务,天气预报微服务,集成Eureka,实现服务的发现与注册功能。主要的操作步骤为:添加pom.xml配置添加application.yml配置添加注解至于如何进行添加,我在上一章进行了详细的讲述,这里就不展开了。启动服务我们将各个服务生成jar包,并通过命令行指定端口进行启动,启动命令如下:java -jar micro-weather-eureka-server-1.0.0.jar –server.port=8761测试结果从上图可以看到我们有4个微服务,每个微服务启动了两个实例。每个实例已经集成了Eureka客户端,并且在Eureka服务器中完成了注册。各个微服务之间可以通过应用的名称相互访问,每个微服务启动了多个实例,这样当我们的任何一个实例挂掉了,因为在其他节点有注册,所以还可以提供服务,如此一来我们便实现了服务的高可用!

March 11, 2019 · 1 min · jiezi

实现简单的Tomcat | Tomcat原理学习(1)

缘起用了那么久tomcat,突然觉得自己对它或许并没有想象中的那么熟悉,所以趁着放假我研究了一下这只小猫咪,实现了自己的小tomcat,写出这篇文章同大家一起分享!照例附上github链接。项目结构项目结构如下:实现细节创建MyRequest对象首先创建自定义的请求类,其中定义url与method两个属性,表示请求的url以及请求的方式。其构造函数需要传入一个输入流,该输入流通过客户端的套接字对象得到。输入流中的内容为浏览器传入的http请求头,格式如下:GET /student HTTP/1.1Host: localhost:8080Connection: keep-alivePragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9Cookie: Hm_lvt_eaa22075ffedfde4dc734cdbc709273d=1549006558; _ga=GA1.1.777543965.1549006558通过对以上内容的分割与截取,我们可以得到该请求的url以及请求的方式。package tomcat.dome;import java.io.IOException;import java.io.InputStream;//实现自己的请求类public class MyRequest { //请求的url private String url; //请求的方法类型 private String method; //构造函数 传入一个输入流 public MyRequest(InputStream inputStream) throws IOException { //用于存放http请求内容的容器 StringBuilder httpRequest=new StringBuilder(); //用于从输入流中读取数据的字节数组 byte[]httpRequestByte=new byte[1024]; int length=0; //将输入流中的内容读到字节数组中,并且对长度进行判断 if((length=inputStream.read(httpRequestByte))>0) { //证明输入流中有内容,则将字节数组添加到容器中 httpRequest.append(new String(httpRequestByte,0,length)); } //将容器中的内容打印出来 System.out.println(“httpRequest = [ “+httpRequest+” ]”); //从httpRequest中获取url,method存储到myRequest中 String httpHead=httpRequest.toString().split("\n")[0]; url=httpHead.split("\s")[1]; method=httpHead.split("\s")[0]; System.out.println(“MyRequests = [ “+this+” ]”); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }创建MyResponse对象创建自定义的响应类,构造函数需要传入由客户端的套接字获取的输出流。定义其write方法,像浏览器写出一个响应头,并且包含我们要写出的内容content。package tomcat.dome;import java.io.IOException;import java.io.OutputStream;//实现自己的响应类public class MyResponse { //定义输出流 private OutputStream outputStream; //构造函数 传入输出流 public MyResponse(OutputStream outputStream) { this.outputStream=outputStream; } //创建写出方法 public void write(String content)throws IOException{ //用来存放要写出数据的容器 StringBuffer stringBuffer=new StringBuffer(); stringBuffer.append(“HTTP/1.1 200 OK\r\n”) .append(“Content-type:text/html\r\n”) .append("\r\n") .append("<html><head><title>Hello World</title></head><body>") .append(content) .append("</body><html>"); //转换成字节数组 并进行写出 outputStream.write(stringBuffer.toString().getBytes()); //System.out.println(“sss”); outputStream.close(); } }创建MyServlet对象由于我们自己写一个Servlet的时候需要继承HttpServlet,因此在这里首先定义了一个抽象类——MyServlet对象。在其中定义了两个需要子类实现的抽象方法doGet和doSet。并且创建了一个service方法,通过对传入的request对象的请求方式进行判断,确定调用的是doGet方法或是doPost方法。package tomcat.dome;//写一个抽象类作为servlet的父类public abstract class MyServlet { //需要子类实现的抽象方法 protected abstract void doGet(MyRequest request,MyResponse response); protected abstract void doPost(MyRequest request,MyResponse response); //父类自己的方法 //父类的service方法对传入的request以及response //的方法类型进行判断,由此调用doGet或doPost方法 public void service(MyRequest request,MyResponse response) throws NoSuchMethodException { if(request.getMethod().equalsIgnoreCase(“POST”)) { doPost(request, response); }else if(request.getMethod().equalsIgnoreCase(“GET”)) { doGet(request, response); }else { throw new NoSuchMethodException(“not support”); } }}创建业务相关的Servlet这里我创建了两个业务相关类:StudentServlet和TeacherServlet。package tomcat.dome;import java.io.IOException;//实现自己业务相关的Servletpublic class StudentServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { //System.out.println("!!!!!!!!!!!!!!!!!!"); response.write(“I am a student.”); //System.out.println(“9999999999999999”); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write(“I am a student.”); }catch(IOException e) { e.printStackTrace(); } }}package tomcat.dome;import java.io.IOException;//实现自己业务相关的Servletpublic class TeacherServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write(“I am a teacher.”); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write(“I am a teacher.”); }catch(IOException e) { e.printStackTrace(); } }}创建映射关系结构ServletMapping该结构实现的是请求的url与具体的Servlet之间的关系映射。package tomcat.dome;//请求url与项目中的servlet的映射关系public class ServletMapping { //servlet的名字 private String servletName; //请求的url private String url; //servlet类 private String clazz; public String getServletName() { return servletName; } public void setServletName(String servletName) { this.servletName = servletName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public ServletMapping(String servletName, String url, String clazz) { super(); this.servletName = servletName; this.url = url; this.clazz = clazz; }}映射关系配置对象ServletMappingConfig配置类中定义了一个列表,里面存储着项目中的映射关系。package tomcat.dome;import java.util.ArrayList;import java.util.List;//创建一个存储有请求路径与servlet的对应关系的 映射关系配置类public class ServletMappingConfig { //使用一个list类型 里面存储的是映射关系类Mapping public static List<ServletMapping>servletMappings=new ArrayList<>(16); //向其中添加映射关系 static { servletMappings.add(new ServletMapping(“student”,"/student", “tomcat.dome.StudentServlet”)); servletMappings.add(new ServletMapping(“teacher”,"/teacher", “tomcat.dome.TeacherServlet”)); }}主角登场 MyTomcat!在服务端MyTomcat中主要做了如下几件事情:1)初始化请求的映射关系。2)创建服务端套接字,并绑定某个端口。3)进入循环,用户接受客户端的链接。4)通过客户端套接字创建request与response对象。5)根据request对象的请求方式调用相应的方法。6)启动MyTomcat!package tomcat.dome;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;//Tomcat服务器类 编写对请求做分发处理的相关逻辑public class MyTomcat { //端口号 private int port=8080; //用于存放请求路径与对应的servlet类的请求映射关系的map //相应的信息从配置类中获取 private Map<String, String>urlServletMap=new HashMap<>(16); //构造方法 public MyTomcat(int port) { this.port=port; } //tomcat服务器的启动方法 public void start() { //初始化请求映射关系 initServletMapping(); //服务端的套接字 ServerSocket serverSocket=null; try { //创建绑定到某个端口的服务端套接字 serverSocket=new ServerSocket(port); System.out.println(“MyTomcat begin start…”); //循环 用于接收客户端 while(true) { //接收到的客户端的套接字 Socket socket=serverSocket.accept(); //获取客户端的输入输出流 InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream(); //通过输入输出流创建请求与响应对象 MyRequest request=new MyRequest(inputStream); MyResponse response=new MyResponse(outputStream); //根据请求对象的method分发请求 调用相应的方法 dispatch(request, response); //关闭客户端套接字 socket.close(); } } catch (IOException e) { e.printStackTrace(); } } //初始化请求映射关系,相关信息从配置类中获取 private void initServletMapping() { for(ServletMapping servletMapping:ServletMappingConfig.servletMappings) { urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz()); } } //通过当前的request以及response对象分发请求 private void dispatch(MyRequest request,MyResponse response) { //根据请求的url获取对应的servlet类的string String clazz=urlServletMap.get(request.getUrl()); //System.out.println("====="+clazz); try { //通过类的string将其转化为对象 Class servletClass=Class.forName(“tomcat.dome.StudentServlet”); //实例化一个对象 MyServlet myServlet=(MyServlet)servletClass.newInstance(); //调用父类方法,根据request的method对调用方法进行判断 //完成对myServlet中doGet与doPost方法的调用 myServlet.service(request, response); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } //main方法 直接启动tomcat服务器 public static void main(String[] args) { new MyTomcat(8080).start(); } }测试结果 ...

March 10, 2019 · 3 min · jiezi

前端开发 VS Code 上手使用

如果想配置 sublime 开发工具 => 面向web前端及node开发人员的vim配置如果想配置 vim 开发工具 => sublime配置及使用技巧所谓网红编辑器,我觉得比vim来的更爽,因为vim的所有操作和插件,对于vs code 来说也就是一个插件就能无缝兼容。比如我之前是一直在使用 sublime 的,使用vs code 之后明显感觉 vscode 无论是配置还是使用都更顺手一点,学习成本很小。当然本文不是谈哪个编辑器更好的问题,每个软件都在发展,相互学习和补充才是重点。作为一个前端开发,vs code 开箱即用的代码编辑、高亮、提示都十分友好。但作为一个追求完美的程序员,还是装了不少插件,比如前端开发用的一些插件:Auto Rename Tag: 当你编辑HTML标签是会自动修改配对标签HTML Class Suggestions: HTML class 名称提示ESLint: ES 语法检查Hap Extension: 快应用开发基础支持JavaScript(ES6) code snippets: js 代码片段,提高编码速度open in browser: 在浏览器打开页面,这个已经不常用了,但对于前端初学者还是很友好的QuickApp For Highlighter: 快应用开发高亮Color Info: css 中颜色预览工程化也需要一些插件:npm: npm 管理npm Intellisense: npm 自动配置GitLens: git 记录查看minify: 代码压缩写作办公也需要一些工具呀:Excel Viewer: 实际感觉只能预览 csv 文件,不过这也够用了Markdown PDF: markdown 转 pdfMarkdown TOC: markdown 目录生成Markdown+Math: markdown 中数学公式支持vscode-pdf: pdf 阅读插件编辑器本身的插件seti-icons: 文件iconTheme - Seti-Monokai: 高亮及编辑器外观Sublime Text Keymap and Settings Importer: sublime 功能键及配置映射Sublime Babel: sublimevscode-faker: 随机数据生成其他的一些工具SVG Viewer: SVG 图片预览xtemplate: xtpl 文件高亮支持我安装了sublime 的插件,其实还有vim 的插件,安装后无缝兼容 .vimrc 配置和 vim plugin。正常的快捷键可以看参考下图,其实和 sublime 差不了太多。对于一些不如意的地方,可以在配置文件中进行配置,个人感觉 vs code 配置开放比 sublime 更丰富。{ “beautify.language”: { “js”: { “type”: [ “javascript”, “json” ], “filename”: [ “.jshintrc”, “.jsbeautify” ] }, “css”: [ “css”, “scss”, “less” ], “html”: [ “htm”, “html” ] }, “debug.showInStatusBar”: “always”, “debug.node.autoAttach”: “off”, “beautify.tabSize”: 2, “css.lint.duplicateProperties”: “warning”, “css.lint.idSelector”: “warning”, “css.lint.universalSelector”: “warning”, “css.lint.zeroUnits”: “error”, “less.lint.duplicateProperties”: “warning”, “less.lint.idSelector”: “warning”, “less.lint.universalSelector”: “warning”, “less.lint.zeroUnits”: “error”, “scss.lint.duplicateProperties”: “warning”, “scss.lint.idSelector”: “warning”, “scss.lint.universalSelector”: “warning”, “scss.lint.zeroUnits”: “error”, “csv-preview.skipComments”: true, “csv-preview.lineNumbers”: true, “editor.formatOnPaste”: true, “editor.find.autoFindInSelection”: true, “editor.formatOnSave”: true, “editor.fontSize”: 14, “editor.formatOnType”: true, “editor.multiCursorModifier”: “ctrlCmd”, “editor.minimap.enabled”: false, “editor.snippetSuggestions”: “top”, “editor.tabSize”: 2, “editor.wordSeparators”: “`~!@#$%^&()=+[{]}\|;:’",.<>/?”, “emmet.includeLanguages”: { “vue-html”: “html”, “javascript”: “javascriptreact” }, “emmet.triggerExpansionOnTab”: true, “emmet.showAbbreviationSuggestions”: false, “eslint.autoFixOnSave”: true, “eslint.options”: { “configFile”: “/Users/faremax/eslintrc.json” }, “eslint.run”: “onSave”, “eslint.workingDirectories”: [ “./public”, “./src” ], “explorer.confirmDelete”: false, “explorer.confirmDragAndDrop”: false, “explorer.openEditors.visible”: 4, “files.associations”: { “.ux”: “ux” }, “files.autoSave”: “onFocusChange”, “files.exclude”: { “/.git”: true, “/.svn”: true, “/.hg”: true, “/CVS”: true, “/.DS_Store”: true, “/node_modules”: true }, “files.insertFinalNewline”: true, “files.trimFinalNewlines”: true, “files.trimTrailingWhitespace”: true, “git.detectSubmodules”: false, “gulp.autoDetect”: “off”, “grunt.autoDetect”: “off”, “html.format.wrapLineLength”: 0, “jake.autoDetect”: “off”, “javascript.implicitProjectConfig.experimentalDecorators”: true, “markdown-pdf.displayHeaderFooter”: false, “markdown-pdf.margin.left”: “1.8cm”, “markdown-pdf.margin.right”: “1.8cm”, “markdown-pdf.margin.top”: “1cm”, “markdown.preview.lineHeight”: 1.5, “markdown.styles”: [ “/Users/faremax/github-markdown.css” ], “markdown-pdf.styles”: [ “/Users/faremax/github-markdown.css” ], “markdown-toc.insertAnchor”: true, “open-in-browser.default”: “Google Chrome.app”, “search.exclude”: { “/node_modules”: true, “/bower_components”: true, “/build”: true, “/dist”: true, }, “search.location”: “sidebar”, “svgviewer.enableautopreview”: true, “terminal.explorerKind”: “integrated”, “window.restoreWindows”: “all”, “workbench.statusBar.feedback.visible”: false, “window.zoomLevel”: 0, “workbench.colorTheme”: “Monokai”, “workbench.startupEditor”: “newUntitledFile”}vs code 还有很多实用技巧,可以看官方github: https://github.com/Microsoft/vscode ...

March 10, 2019 · 2 min · jiezi

Web前端面试技术点

常规问题:一般来说会问如下几方面的问题:做过最满意的项目是什么?项目背景为什么要做这件事情?最终达到什么效果?你处于什么样的角色,起到了什么方面的作用?在项目中遇到什么技术问题?具体是如何解决的?如果再做这个项目,你会在哪些方面进行改善?技术二面主要判断技术深度及广度你最擅长的技术是什么?你觉得你在这个技术上的水平到什么程度了?你觉得最高级别应该是怎样的?浏览器及性能一个页面从输入 URL 到页面加载完的过程中都发生了什么事情?越详细越好 (这个问既考察技术深度又考察技术广度,其实要答好是相当难的,注意越详细越好)谈一下你所知道的页面性能优化方法?这些优化方法背后的原理是什么?除了这些常规的,你还了解什么最新的方法么?如何分析页面性能?其它除了前端以外还了解什么其它技术么?对计算机基础的了解情况,比如常见数据结构、编译原理等技术点沟通:HTML+CSS1、盒子模型,块级元素和行内元素特性与区别。 2、行内块的使用,兼容性解决。 3、清除浮动的方式以及各自的优劣。4、文档流的概念、定位的理解以及z-index计算规则&浏览器差异性。 5、CSS选择器以及优先级计算。 6、常用的CSS hack。7、遇到的兼容性问题与解决方法。 8、垂直水平居中的实现方式。9、常用布局的实现(两列布局、三列适应布局,两列等高适应布局等)。Javascript1、犀牛书封面的犀牛属于神马品种?(蛋逼活跃气氛用。。。) 2、常用的浏览器内核。 3、常用的DOM操作,新建、添加、删除、移动、查找等。4、String于Array常用方法。 5、设备与平台监测。 6、DOM的默认事件、事件模型、事件委托、阻止默认事件、冒泡事件的方式等。7、jQuery的bind、live、on、delegate的区别(考察点与上一条重叠,切入点不同)。8、JS变量提升、匿名函数、原型继承、作用域、闭包机制等。 9、对HTTP协议的理解。 10、Ajax的常用操作,JS跨域的实现原理。HTML:语义标签语义化CSS:动态居中动画Bootstrap 样式类Preprocessor兼容性 Hack与特征检测CSS3属性与性能JS:Name hoistingPrototypeClosureMain loopPromiseDelegationCross domainMobile:渐进增强移动端交互兼容性问题Debug工具 方法主体是看简历发挥,对方写什么就问什么为主:框架、库、浏览器工作原理、NLP、算法、HTTP… 辅助问题几乎是我个人必备的问题:为什么做前端,学习前端过程。1、跟什么人在一起工作 2、过去项目的挑战 3、自学的途径3个问题基本上就知道这个人的能力水平和瓶颈了,人的很多局限都是被环境限制的,通过闲聊中夹杂的不经意的问题,候选人的画像就已经很鲜明了。处于当前的环境多长时间,有没有突破环境限制的行动,就能评估出潜力和眼界。什么浏览器兼容、作用域、框架等等的东西不会,不记得都可以学,要不了多长时间,关键还是有没有潜力、有没有好的习惯。在能力方面:对 HTML / CSS / JavaScript 具有专家级别的知识;有较熟练使用 AngularJS / Ember.js / jQuery 或者其它类库的经验;较熟悉第三方组件(插件)生态环境及具体案例;有较熟练使用 Jade / Swig / Handlebars / Mustache 或者其它模板引擎的经验;有较熟练使用 SASS 或者其它 CSS 预处理器的经验;有较熟练使用 CoffeeScript 的经验;对 CSS / JavaScript 设计模式有很好的认识及应用;对常用数据结构和算法熟悉;有使用 GruntJS / GulpJS 任务运行器的经验;有使用 Yeoman 生成器的经验;有诸如 Bower / Volo / JSPM 等前端静态资源包管理器使用经验;熟悉本地及远程(甄姬)调试操作;有 Git 的使用经验;Q:简单介绍下 React / Vue 的生命周期A:几个钩子函数基本能报出来(如果不讲究按顺序、按挂载/更新区分、能把单词用英文念出来并且念对的话),稍微深入一点问下各个阶段都做了什么,一半以上就“不太清楚”了。更有甚者我问React,对方回答 created、mounted,提醒之后还觉得自己没错的。Q:【React】定义一个组件时候,如何决定要用 Functional 还是 Class?A:简单的用 Function,复杂的用 Class。(不能算错吧……但也不能算答到点子上)追问怎么界定“复杂”,答不上来。Q:【React】HOC、(非)受控组件、shouldComponentUpdate、React 16 的变化A:不清楚、没接触过。Q:【Vue】介绍一下计算属性,和 data、methods、watch 的异同A:基本都能巴拉一些,说的大部分都对,但就是说不到最关键的“当且仅当计算属性依赖的 data 改变时才会自动计算”。Q:【Vue】为什么 SFC 里的 data 必须是一个函数返回的对象,而不能就只是一个对象?A:我承认这个问题有点小难,有一定的区分度,不是每个人都有关注过,但是官方文档有说明这一点,但凡看过的肯定有印象。即便没完整看过文档,在初次学习的过程中难道就不觉得奇怪吗?“学而不思”的人和“学而思”的人,区别还是挺大的。Q:CSS 选择器的权重A:经典问题了吧?背都能背出来吧?伪类、伪元素分不清楚,只知道内联、!important、ID、Class之间的顺序,加上其它的就懵了,而且只说谁大于谁,讲不出具体的计算方法。单层选择器比较还行,几个叠加起来就迷糊了。Q:JS 有哪几种原始类型A:基础题,能说上来几个,答不全,主要问题集中在 null 和 undefined 没考虑进去、对象和数组算不算原始类型、以及 Symbol很多人不知道。Q:ES 2015+ 有哪些新特性A:这题可以说的很多,根据应聘者的回答去展开,可以很容易地看出应聘者有没有系统地学习过这方面的东西,以及有没有持续地去跟进语言标准的发展。但这一题能回答的比较好的,寥寥无几,大部分是遇到问题然后零零散散现学的,不够全面、也不够深入,简单用过,但稍微问点细节就只有“尴尬而不失礼仪的微笑”了。Q:工程化工具的使用(Webpack、ESLint、Yarn、Git、……)A:基本都有所接触,但只是“用过”,算不上“会用”,一切顺利还好,真遇到问题了,立马就懵。Q:Node.jsA:写过 Demo 的水平。(比较初级)Q:未来 1~2 年的职业规划、下一步最想学的技术、最希望往什么方向发展、怎么看待XXX技术A:大部分人对自己没有一个明确的态度和规划。说白了就是还没从学校里出来,什么都等着别人来安排。通用技能有哪些(请看如下图)? ...

March 9, 2019 · 1 min · jiezi

7 个Web开发工程师面试题和回答策略

翻译:疯狂的技术宅原文:https://www.indeed.com/hire/i…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章不管你是面试官还是求职者,里面的思路都能让你获益匪浅。Web开发人员的职责是什么?解析:Web工程师应该充分了解自己的角色,以及怎样为Web设计和开发做出贡献。这个问题能帮助面试官了解求职者打算怎样配合团队,以及他们是否知道自己要做些什么。面试官想知道什么:清楚地了解Web开发过程他们强调那些职责他们打算怎样发挥自己的能力参考答案:Web工程师负责设计、开发、增强、测试和部署Web应用,其最终目标是创建引人入胜而且用户友好的站点布局和功能。开发人员负责收集并定义需求、维护网站、排除故障并修复错误,遵循最佳开发实践并与其他团队协作。是什么引发了你对Web开发的兴趣?解析:求职者是否很早就成为了Web开发人员?他们是自学成才吗?了解候求职者选择这个方向的原因有助于确定他们对该职业的热情和信心。面试官想知道什么:热情动机经验参考答案:我以前的工作是营销协调员,我经常使用在线工具来制作电子邮件广告。后来我找到了拖放功能的bug,并发现如果我直接编辑HTML,可以很灵活的实现我想要的外观。这时我开始着迷于Web开发人员具有想象力、艺术性和技术性的能力,同时想开发出一种对他人有帮助的产品。什么是W3C,它为什么很重要?解析:Web开发人员应对其生成的内容负责,要确保所有用户都可以访问,并遵循W3C标准。这个问题将告诉你求职者是否具 W3C 的知识并在工作中遵循它们。面试官想知道什么:基本知识关心标准关于他们如何遵守标准的细节参考答案:W3C 的意思是 World Wide Consortium,它是一个专注于开发和标准化 Web 的国际社区。作为Web开发人员,强制执行这些标准可确保所有浏览器都能访问Web内容,并优化用户体验。例如:使用符合 W3C 标准的 CSS 和 XML 可以使每个网站的功能相似,也可以改善搜索引擎优化。解释如何优化和减少Web应用的加载时间。解析:几乎一半的用户希望在两秒钟内加载网页。提出此问题以了解求职者是否了解页面加载时间对用户体验的影响,以及Web开发人员应如何优化加载时间。面试官想知道什么:他们对优化Web应用的重视程度了解可用于分析网站速度的工具他们具有优化能力的证据参考答案:尽管有很多技术手段能减少加载时间,但我始终依赖优化图像、启用浏览器缓存和最小化HTTP 请求。我评估网站速度的首选工具是 Google PageSpeed Insights。我曾经通过 soley 启用浏览器缓存,成功地将页面加载时间从 2.1 秒减少到 0.7 秒。HTTP/2 和 HTTP 1.1 有什么区别?解析:是否了解并使用过 HTTP/2 ,能够体现求职者的知识水平,让你了解他们是否能够跟上技术的发展。面试官想知道什么:整体 HTTP 知识能够回忆起具体细节洞察他们的技术水平参考答案:HTTP/2 旨在提高 Web 应用的性能,可以减少加载时间并改善浏览器与服务器之间的通信,并且能够使应用更简单、快速。我非常支持使用 HTTP/2,因为我曾经分析过相关数据,并知道了 HTTP/2 是如何将页面加载时间减少20%的。你熟悉哪种编程语言?解析:HTML,CSS,SQL,PHP,Ruby,Python 和 JavaScript 是 Web 工程师应该能够轻松驾驭的常见编程语言。这个问题能帮助面试官评估求职者的经验、偏好、优势和劣势。面试官想知道什么:熟悉的编码语言对特定语言的偏好程度是否适合岗位和公司参考答案:我精通 HTML、CSS 和 PHP,并且 SQL 和 JavaScript的水平也不错。我打算学习 Python,最近正在研究适合自己的Python课程,以便在空闲时间学习。你怎样平衡客户的苛刻要求?解析:Web 开发工程师可能面临意想不到的挑战,包括缩短开发周期和面对过于雄心勃勃的客户。清楚地了解求职者如何处理这种情况,将有助于面试官了解他们做事的优先级和思考过程。面试官想知道什么:在压力下的表现沟通技巧致力于发布高品质的产品参考答案:在我面对苛刻的需求时,会想办法充分了解对方的真正要求,优先处理自己的任务并与客户保持开放的沟通渠道。我很难容忍Bug的产生,也不赞成用一些取巧的手段破坏最终的产品形态,不过让客户满意是一个高度优先的选项。我会随时向客户通报我的进展并高效地完成任务。欢迎继续阅读本专栏其它高赞文章:12个令人惊叹的CSS实验项目世界顶级公司的前端面试都问些什么CSS Flexbox 可视化手册过节很无聊?还是用 JavaScript 写一个脑力小游戏吧!从设计者的角度看 ReactCSS粘性定位是怎样工作的一步步教你用HTML5 SVG实现动画效果程序员30岁前月薪达不到30K,该何去何从7个开放式的前端面试题React 教程:快速上手指南本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

March 9, 2019 · 1 min · jiezi