Chrome-小恐龙游戏源码探究一-绘制静态地面

文章首发于我的博客 目录Chrome 小恐龙游戏源码探究一 -- 绘制静态地面Chrome 小恐龙游戏源码探究二 -- 让地面动起来Chrome 小恐龙游戏源码探究三 -- 进入街机模式Chrome 小恐龙游戏源码探究四 -- 随机绘制云朵Chrome 小恐龙游戏源码探究五 -- 随机绘制障碍Chrome 小恐龙游戏源码探究六 -- 记录游戏分数Chrome 小恐龙游戏源码探究七 -- 昼夜模式交替Chrome 小恐龙游戏源码探究八 -- 奔跑的小恐龙// TODO前言当 Chrome 处于离线情况下,会显示以下页面: 当按下空格键或者 ↑ 键,小恐龙游戏彩蛋就触发啦 (•ㅂ•)✧ 游戏虽然简单,但源码却有三千多行,代码严谨且富有逻辑,值得拿来学习研究。这个教程将会从零开始,一步步解读源码并最终实现这个游戏。 获取源码、素材要获取游戏的源码,可以通过下面几种方式: 断网后,访问任意网址,进入小恐龙页面,用开发者工具获取源码在浏览器地址栏输入 chrome://dino,进入小恐龙页面,用开发者工具获取源码官方提供的源码网址有人将源码提取出来放在了 GitHub 上:t-rex-runner游戏用到的雪碧图,音频文件可以在官方提供的源码网址里获取到。为了方便食用,我将雪碧图中各个小图片的坐标信息标了出来(W: Width, H: Height, L: Left, T: Top): 关于上面雪碧图的坐标信息,我是用一个在线工具获取的:http://www.spritecow.com/,个别坐标信息通过这个网站获取的不太准,这里我已经通过参考源码里的数据进行了修正。 戳这里获取上面这张图片的 PSD 原图。开始探究游戏源码主要包括九个类: 游戏的主体类 Runner背景类 Horizon 地面类 HorizonLine云朵类 Cloud障碍物类 Obstacle昼夜更替类 NightMode小恐龙类 Trex分数类 DistanceMeter游戏结束面板类 GameOverPanel这个教程并不会完全按照源码来,而是抽取主要的内容来一步步实现这个游戏。这样做并不意味着改变源码的思路,而是去除了一些目前可以先不考虑的代码,比如:去除了适配 HDPI 和 LDPI、适配移动端等。 ...

April 26, 2019 · 4 min · jiezi

前端技术发展史

如果你检索到了,忽略此文章,只是一个草稿。 既然做前端这个职业,那么对于它的来龙去脉,得了解,算称职点吧。查阅了四个人的技术博客,维基百科查找资料,前端技术发展,以前不叫前端,只是页面工程师。 起源当浏览器还没有时候,一个叫伯纳斯李的人,发明了一个HTML,叫超文本标记语言,主要是为了跟同事们,不需要什么都面对面交流,只要分享自己的文档就好了,就写了这个HTML。那它的承载环境是什么呢?谁去识别这个HTML呢?如果不是浏览器,那是谁?所以浏览器和HTML谁先出来,当然是浏览器了。 发展一开始,HTML只是承载数据的标签,没有样式,没有排版,就是从上到下,从左到右。后来有了CSS样式,布局变得好看了,字体变得好看了,有动画了。这还是算静态网页,到了后来有了javaScript语言嵌入浏览器中,网页可以做交互了,脚本语言控制浏览器上的HTML,去做一些样式变化、布局的变化,数据的变化了。全靠DOM提供了API接口。 后来Ajax出现了,web2.0来了,再也不用,请求数据,要很慢且要一整张HTML页面返回了,可以局部刷新,返回我们要的数据了。 2009年node,出现了,如果没有node也许没有现在的三大主流框架,现在很多包都发布在了npm上,每个包都有自己的功能,node基于谷歌V8引擎可运行javaScript的环境。node能做什么?不能做什么?要清楚。 现在现在主流框架React、Vue、Angular出现了,它们不同于Jquery这些库,Jquery这些库只是对DOM的封装,以及解决了浏览器的兼容问题。但是现在三大主流框架可不是对DOM的封装,他们是对DOM的抽象,我们再不需要亲自去操作DOM了,现在是声明式开发,我们要写什么标签,如何去更改HTML、CSS无须我们去操作DOM,框架会去做,这样开发思想就不一样了,数据驱动开发了。

April 26, 2019 · 1 min · jiezi

可以提高程序员效率的工具

前言只有光头才能变强。文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 本文记录一下我在平时喜欢用的一些小工具,以便以后重装系统/换电脑的时候能快速安装 本文涉及的所有软件和下载地址/破解码,安装教程在公众号下回复”工具“即可全部获取一、Markdown编辑器Markdown是程序员写笔记/博客必备的,如果不了解的同学,建议去学习一波,5分钟准学会!我之前用Windows的时,在写markdown时最喜欢用的是MarkdownPad2,主要习惯了它的快捷键,所以在Windows平台的时候一直都没换 后来去到公司,换了Mac。MarkdownPad2在Mac平台是不支持的,所以我只能去找别的Markdown编辑器了。也体验了几款Markdown编辑器,还是觉得没有MarkdownPad2用得顺手(所以在刚换的上Mac的时候,会把我的Windows打开来写文章...haha) 最后找到了Typora这款Markdown编辑器,现在用得十分舒服!配上自带的图床,复制/截图完后,直接command+v就可以将图片粘贴在文章中了 二、Chrome浏览器我相信大家用的浏览器都应该是Chrome吧?我以前用Chrome的时候就喜欢它的简洁,打开速度快。但是Chrome的优秀之处,在于它有很多很多好用的插件,下面我来盘点一下我喜欢的插件: 2.1 屏蔽广告插件我喜欢用的屏蔽广告插件是:uBlock Origin 用了这个插件,再也不怕被各种广告闪瞎眼了。 没用插件之前: 用了插件之后: 2.2 英语翻译插件英语翻译插件,我喜欢用的是:沙拉查词 在阅读到不懂的单词的时候,划一下就好了: 2.3 显示当前网页的IP这个是我从公司的文档找过来的,觉得还是蛮好用的,在调试的时候及时知道当前浏览系统的IP是哪个(切换环境的时候就很有用):Website IP 2.4 GitHub源码视图工具这个也是在公司的文档看到的,也觉得蛮好用的,能够看到GitHub源码的目录结构图:Octotree 三、绘图有很多小伙伴问我平时作图都是什么软件来画了,其实我用的是ProcessOn 我用ProcessOn已经画了不少的脑图了: Java精美思维导图四、高级记事本在Windows平台的时候我喜欢用notePad++,换成Mac以后喜欢用SublimeText3。其实我就把这俩当成是拥有高亮功能的记事本来用.... 众所周知,SublimeText3的功能可是很强大的,有兴趣的小伙伴可以去折腾折腾。 五、电子书我个人是比较爱看实体书的,不过有的时候身旁没有这本实体书,只能将就去搜一下电子书了。搜索电子书的渠道现在我知道的有以下几个: 鸠摩搜书: GitHub搜书: 群友分享的网盘(问强大的群): (在公众号下,回复“工具”也会把这个网盘地址给分享哦~) 七、Mac工具7.1常用的快捷键要更好地使用Mac,当然要学习Mac的快捷键 Mac原生的快捷键终端下的快捷键一般Windows下有的快捷键,在Mac中都会有。所以一般我都是,在某个场景下我感觉这个操作可能会有快捷键,于是我就去搜一把,这东西只能熟能生巧了。 原生Mac快捷键: 7.2管理host工具在工作中很多时候都需要将域名绑定到IP中,之前我一直就修改hosts文件(贼慢),后来同事见到我这个操作,于是就推荐我去下载一个Gas Mask,这个工具可以很方便地将域名绑定到IP中! 7.3强大的启动工具Alfred是Mac上最好用快速启动工具,修复了大量问题,Alfred能够让我们使用键盘去完成应用的搜索和启动、信息的本地和Web搜索、查询字典、剪切板查看、系统控制等等功能,简单易用,能够大大提高使用Mac的效率这个工具我是看我同事操作的时候呼出来的,看起来就很好用! 在安装的时候,记得看看有没有这情况: 快速找到文件: 我尤其喜爱的是剪切板这个功能(能够记录近期复制过的文字,然后快速找到): 7.4文档查看工具以前在Windows平台下安装了好多离线的文档,jquery,java1.8等等,如果离线没有的话,只能去官网下找对应的文档了(这个过程还是相对麻烦的) 在Mac中有一个文档查找的工具,叫做Dash 7.5 软件包管理工具之前装一些软件包的时候,用的就是HomeBrew,十分好用啊 7.6 Mac终端我之前一直用着Mac自带的终端。但我看我的同事们,都好像跟我的不一样~~ 自带的终端是这样的: ...

April 26, 2019 · 1 min · jiezi

Selenium-chromeDriver-Python3-完成-Flash-播放

在使用 selenium + chromeDriver + python3 截图时,遇上 Flash 无法加载,导致了截图 Falsh 是空白区。环境要求:selenium chromeDriver Python3 问题chrome 无头浏览器无法自动加载 Flash 解决办法参考了 allow-flash-content-in-chrome-69-running-via-chromedriver 的回答,直接修改 Chrome 的设置 chrome://settings/content/siteDetails?site= 里面的 Flash 设置,修改为 Allow #!/usr/bin/env python3# -*- coding: utf-8 -*-from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import Selectclass chromeDriver(): def __init__(self, driver = ''): # 设置窗口大小 self.window_width = 1680 self.window_height = 948 # 设置 chromedriver 位置 self.executable_path = '/usr/local/bin/chromedriver' # 设置 Flash 的路径 self.flash_path = '/Users/cindy/Library/Application Support/Google/Chrome/PepperFlash/32.0.0.171/PepperFlashPlayer.plugin' # 获取 driver if driver: self.driver = driver else: self.driver = self.get_chrome_driver() def get_chrome_driver(self): # 头部 user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' # 创建参数对象 options = webdriver.ChromeOptions() prefs = { # 开启图片 "profile.managed_default_content_settings.images":1, # 关闭 Notification "profile.default_content_setting_values.notifications": 2, } # 设置 Flash 的路径 options.add_argument('--ppapi-flash-version=32.0.0.171') options.add_argument('--ppapi-flash-path=' + self.flash_path) options.add_argument('binary_location=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome') # 指定屏幕分辨率 options.add_argument('window-size=' + str(self.window_width) + 'x' + str(self.window_height) + '\'') # 最大化窗口 options.add_argument('--start-maximized') # 规避bug options.add_argument('--disable-gpu') # 禁用弹出拦截 options.add_argument('--disable-popup-blocking') # 隐藏自动软件 options.add_argument('disable-infobars') # 设置中文 options.add_argument('lang=zh_CN.UTF-8') #忽略 Chrome 浏览器证书错误报警提示 options.add_argument('--ignore-certificate-errors') # 更换头部 options.add_argument('user-agent=' + user_agent) options.add_argument('no-default-browser-check') # 关闭特征变量 options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_experimental_option('prefs', prefs) # 创建 Chrome 对象 driver = webdriver.Chrome(options = options, executable_path = self.executable_path) return driver def get(self, web_url): if not web_url: return False return self.driver.get(web_url) def add_flash_site(self, web_url): if not web_url: return False self.get("chrome://settings/content/siteDetails?site=" + web_url) root1 = self.driver.find_element(By.TAG_NAME, "settings-ui") shadow_root1 = self.expand_root_element(root1) root2 = shadow_root1.find_element(By.ID, "container") root3 = root2.find_element(By.ID, "main") shadow_root3 = self.expand_root_element(root3) shadow_root3 = self.expand_root_element(root3) root4 = shadow_root3.find_element(By.CLASS_NAME, "showing-subpage") shadow_root4 = self.expand_root_element(root4) root5 = shadow_root4.find_element(By.ID, "advancedPage") root6 = root5.find_element(By.TAG_NAME, "settings-privacy-page") shadow_root6 = self.expand_root_element(root6) root7 = shadow_root6.find_element(By.ID, "pages") root8 = root7.find_element(By.TAG_NAME, "settings-subpage") root9 = root8.find_element(By.TAG_NAME, "site-details") shadow_root9 = self.expand_root_element(root9) root10 = shadow_root9.find_element(By.ID, "plugins") shadow_root10 = self.expand_root_element(root10) root11 = shadow_root10.find_element(By.ID, "permission") Select(root11).select_by_value("allow") def expand_root_element(self, element): return self.driver.execute_script("return arguments[0].shadowRoot", element) def get_flash_url(self, web_url): if not web_url: return False self.add_flash_site(web_url) self.get(web_url) def quit_driver(self): self.driver.quit()driver = chromeDriver()url = 'http://your.website/'driver.get_flash_url(url)最后不能使用无界面模式,设置 handless 参数 options.add_argument('--headless')。否则无法直接修改 Chrome 的设置。 ...

April 23, 2019 · 2 min · jiezi

实用工具 | 推荐 9 个好用的 Chrome 插件

微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。前言最近更新比较佛系,一方面,工作确实有点忙,但是做的都特么旧项目。09 年的代码都出来了,贼特么恶心。感觉待不久了。另一方面,没想好最近有啥要跟大家分享。刚好今天打开 Chrome ,发现自己一直在用的几个很不错的 Chrome 插件,顺手推荐给你们了。 1、屏蔽百度推广不知道大家注意到没有,有百度搜索的时候,右侧会有一列与关键词无关的热点新闻引导大家去点击分散大家的注意力。而上面说到的这个插件就可以屏蔽右侧推广,还你们一个干净的百度,比如没装插件,它的页面是这样的: 使用方式:点击关闭右侧推广按钮,页面会自动刷新 这个时候的网页,就变干净了: 2、广告净化器作为程序员应该没少逛 CSDN 吧,不知道你们有没有觉得以下的轮训广告很恶心?反正我是受不了的,正看着博客,就被这广告给打扰了。这个插件就可以帮你屏蔽任何网站的广告,甚至于爱奇艺,腾讯视频等广告。你们追电视剧要看的广告,我全都不需要看,如丝般顺滑。话不多说推荐给你们。没插件之前是这样的: 使用方法:打开按钮就可以 使用后: 3、WEB前端助手(FeHelper)作为程序员的大家在开发中肯定会用到很多诸如,JSON 格式化,代码美化,字符串编码,二维码生成等等一大堆工具。这个插件就为这些个功能提供了良好的支持,实在是太良心了。 使用方法:下载插件安装即可,简单方便。 4、Dark Reader夜深人静浏览网站时保护眼睛的好插件,黑色主题,适用于任何网站。关爱眼睛,就使用 Dark Reader 进行夜间和日间浏览。使用前: 使用方法:设置好各种参数,直接点击网站名,看到以下红框的网站域名带个 √ 就是设置成功了。 使用后:此时的网站主题就会变成你设置好的黑色: 5、Isometric Contributions纯装逼利器,这个插件就是把 Github 的提交记录从二维平面的变成三维立体的。安装前: 安装后:除了提交记录变三维立体外,还支持通过设置来统计你的各项数据。 6、Octotree大家平时上 github 看项目都是一层一层文件夹点进去,非常繁琐。这款插件就是让你在 Github 上查看代码结构就像在自己开发工具里面一样方便。效果是这样的: 7、Infinity 标签页这个插件自由定制 chrome 标签页。开启页面添加时代,无论你浏览那个页面,都能一步将网址添加到标签页中,独创新标签页中谷歌邮件自动提醒功能,还有精美天气,待办事项,历史记录管理,应用程序管理,印象笔记一样的记事应用,高清壁纸,必应,百度,谷歌搜索。反正就是贼好用,我的标签页是这样的: 8、Google 翻译这个插件可以在你浏览英文技术文档时,选中翻译成中文,帮助你理解。比如:Spring 官网: 点击红框小图标就可以翻译出来: 9、一个神奇的网站扩展迷:https://extfans.com/ 大家都知道 Chrome 插件一般都要科学上网才能下载到,但是自从这个网站出现以后下载插件不再需要折腾了。直接访问搜索名称就可以下载到以上所提到的插件。 推荐阅读技术类: ...

April 21, 2019 · 1 min · jiezi

计算机科学基础_8 - GUI

个人计算机革命1970年代初成本下降,个人计算机变得可行。Altair 8800比尔盖茨和保罗 艾伦写BASIC解释器乔布斯提议卖组装好的计算机,Apple-1 诞生1977年出现3款开箱即用的计算机:Apple-II, TRS-80 Model I, Commodore PET 2001IBM 意识到个人计算机市场IBM PC发布,采用开放架构,兼容的机器都叫IBM Compatible(IBM 兼容)生态系统产生雪球效应:因为用户多,软硬件开发人员更愿意花精力在这个平台。因为软硬件多,用户也更乐意买“IBM 兼容”的计算机。苹果选封闭架构,一切都自己来,只有苹果在非“IBM 兼容”下保持足够市场份额。在计算机发展头30年难以想象,70年代初,各种组件的成本都下降了,可以做出低成本,同时性能足够强大的计算机,这个转变中,最具影响力的是,单芯片CPU的出现,强大+体积小+便宜。集成电路的进步,也提供了低成本固态存储器。可以用于计算机的RAM和ROM。 计算机成本下降+性能提升,让个人计算机成为可能。第一台取得商业成功的个人计算机:Altai 8800。虽然1975年之前就有计算机爱好者,但Altai大量催生更多计算机爱好者,爱好者们组成各个小组分享知识,软件,以及对计算机的热爱。最具传奇色彩的小组是:家酿计算机俱乐部。第一次小组聚会在1975年3月,看一台第一批运来加州的Altai 8800。之后,Steve Wozniak开始想设计自己的计算机,1976年5月,他向小组展示了原型机,并把电路图分享感兴趣的小组其它成员。他的设计不同寻常,要连到电视显示,并提供文本界面。 同是俱乐部成员和大学同学 史蒂夫 乔布斯 建议说与其免费分享设计,不如直接出售装好的主板。但用户依然需要自己加键盘,电源和机箱。1976年7月开始发售,价格$666.66美元,是苹果第一款产品。 就像Altai 8800一样,Apple-I也是作为套件出售。Apple-I吸引了业余爱好者,不介意机器买回来自己组装,但个人消费者和公司对Apple-I不感兴趣。 在1977年发生变化,市场上有了三款开箱即用的计算机。第一款是Apple-II,苹果公司第一个提供全套设备的产品,设计和制造工艺都是专业。它还提供了简单彩色图形和声音输出。Apple-II卖出上百万套,把苹果公司推到了个人计算机行业的前沿。第二款是:TRS-80 1型。由Tandy公司生产,虽然不如Apple-II先进,但因为价格只有一半,所以卖但很火爆。第三款是:Commodore PET 2001。集成了计算机,显示器,键盘和磁带驱动器。目标是吸引普通消费者。 这3台计算机被称为1977年的”三位一体“。让不那么精通计算机的人也能用BASIC写程序,针对消费者的软件行业,开始腾飞。市场上出现了各种针对个人的游戏和生产力工具。比如计算器和文字处理器。最火的是1979年的VisiCalc,第一个电子表格程序,比纸质好无数倍,是微软的Excel和Goole Sheets的老祖先。 因为IBM忽略了增长的“微型计算机”市场,随着微型计算机演变成个人计算机。 IBM需要从根本上重新思考战略和设计,意味着需要从头开始,一个由十二名工程师组成的精干团队(后来叫“肮脏十二人”)被派往博卡拉顿办公室。让他们独立工作,不受IBM内部的政治斗争干扰。想怎么设计就怎么设计。没用IBM的CPU,选了Intel的芯片,也没用IBM的首选操作系统CP/M,而用了微软的DOS,依次类推,从屏幕到打印机都这样自由选择。IBM第一次不得不与外部公司竞争,来给新计算机做硬件和软件。这和IBM的传统做法不同:自己做硬件来节省成本,然后与其它公司合作。经过一年IBM发布了IBM PC。 最具影响力的是它使用“开放式架构”,有良好的文档和扩展槽。使得第三方可以做硬件/外设。包括显卡,声卡,外置硬盘,游戏控制杆以及无数其它组件。刺激了创新,激发了竞争,产生了巨大的生态系统。这个开放式架构叫:IBM Compatible(IBM 兼容)。意味着如果买了“IBM兼容”的计算机,可以用庞大生态系统中的其它软硬件。开放架构也意味着,竞争对手公司可以遵循这个标准。做出自己的“IBM 兼容”计算机。 很快,康柏和戴尔也开始卖PC,微软很乐意把MS-DOS授权给他们。仅在前三年,IBM就卖出200万台PC,超过了苹果。有了庞大用户群,软件和硬件开发人员,把精力放在“IBM 兼容”平台,因为潜在用户更多,同时,想买计算机的人也会看哪种计算机的软件硬件选择更多,就像雪球效应一样,而那些生产非“IBM 兼容”计算机的公司(一般性能更好),都失败了。 只有苹果公司在没有“IBM 兼容”都情况下,保持了足够都市场份额,苹果公司最终选择了相反的方式:“封闭架构”。即自己设计一切,用户一般无法加新硬件到计算机中。意味着苹果公司要做自己的计算机,自己的操作系统,还有自己的外围设备,如显示器,键盘和打印机。通过控制整个范围,从硬件到软件,苹果能控制用户体验并提高可靠性。 不同的商业策略是“MAC vs PC 谁更好”这种争论的起源。 为了在低成本个人计算机的竞争冲击下生存下来,苹果需要提高自身水平,提供比PC和DOS更好的用户体验。 图形用户界面图形用户界面(GUI) 图形界面先驱:道格拉斯 恩格尔巴特。1970年成立 帕洛阿尔托研究中心。1973年完成Xerox Alto(施乐奥托)计算机1981年的Xerox Star system(施乐之星系统)乔布斯去施乐参观所见即所得WYSIWYG1973年推出 Apple Lisa1984年推出 Macintosh1985年推出 Windows1.0,之后出到3.01995年推出 Windows 95 提供图形界面1995年微软做失败的 Microsoft Bob苹果在1984年发布了Macintosh,这台普通人可以买到的第一台带图形界面的计算机。那时的计算机全是命令行,图形界面是个革命性进展。不必记住或猜正确的命令。图形界面可以直接显示了,可以做什么。 ...

April 21, 2019 · 1 min · jiezi

google dorking cheatsheet

advanced operatorsintitleallintitleinurlallinurlintextallintextfiletypesitelinkinanchornumrange | ..daterange | ..Referenceswiki:google hacking PDF:GoogleHackingCheatSheetPDF:GoogleCheatSheet

April 19, 2019 · 1 min · jiezi

写了一个chrome插件:拦截ajax请求并修改返回结果

这个插件可以拦截页面上的 ajax 请求,并把返回结果替换成任意文本。它对 mock 数据、排查一些线上问题等会有很大帮助。(当然 chales 等抓包软件也可以做到,然而使用起来比较繁琐,做成 chrome 插件的形式会方便许多)使用示例(视频)weibo.com/tv/v/HlVZD8cR9?fid=1034:4352275389595232Chrome 商店地址地址:https://chrome.google.com/web…你也可以直接搜索 Ajax Interceptor 进行安装注意建议第一次安装完重启浏览器,或者刷新你需要使用的页面。当你不需要使用该插件时,建议把开关关上(插件icon变为灰色),以免对页面正常浏览造成影响。该插件只会在JS层面上对返回结果进行修改,即只会修改全局的XMLHTTPRequest对象和fetch方法里的返回值,进而影响页面展现。而你在chrome的devtools的network里看到的请求返回结果不会有任何变化。githubhttps://github.com/YGYOOO/aja…

April 16, 2019 · 1 min · jiezi

使用 chrome 地址栏和有道翻译来快速翻译单词

Chrome浏览器的地址栏不仅仅能够输入网址,还可以做很多的事情,我今天就举个例子,用地址栏来快速翻译单词先看看效果地址栏输入youdao.com按一下Tab键,会自动提示 使用 有道翻译 搜索再输入想要翻译的单词回车设置方法添加一条

April 13, 2019 · 1 min · jiezi

KV存储:Web的第一个内置模块

相信作为web开发者大家都使用过浏览器的本地存储localStorage,它是一个会阻止主线程的同步API,只要使用就可能会阻止页面的交互。我们都知道浏览器有异步的IndexedDB作为存储方案,只是它的API使用方式比localStorage要复杂很多。那么是否有既简单并且又不阻塞主线程的API呢?好消息是Chrome正在尝试一种称为内置模块的新功能,计划发布的第一个内置模块是名为KV Storage的异步键/值存储模块,先来了解一下什么是内置模块。内置模块内置模块就像常规JavaScript 模块一样,唯一区别是它们不必下载,而是随浏览器一起提供。与传统的Web API一样,内置模块必须经过标准化过程 - 每个模块都有自己的规范,需要进行设计审查,并且在发布之前需要Web开发人员和其他浏览器供应商提供支持。与传统的Web API不同,内置模块不会在全局范围内公开 它们只能通过导入获得。不全局暴露内置模块有很多优点:它们不会增加任何开销来启动新的JavaScript运行时上下文,并且不会消耗任何内存或CPU。此外,可以避免与当前上下文变量命名冲突的风险。在支持的浏览器中,您可以使用以下代码导入KV存储模块。import {storage, StorageArea} from ‘std:kv-storage’;KV存储模块KV存储模块的简单性与localStorage API类似,但其API形状实际上更接近 JavaScriptMap。除了getItem(), setItem()以及removeItem(),它还有get(), set()和delete()以及Map类似的方法,如 keys(), values()和 entries()。键名不必是字符串。它们可以是任何结构化可序列化类型。与Map不同的是,所有KV存储方法都返回 promises或 异步迭代器KV存储模块对外暴露两个变量:storage和StorageArea,其中storage是StorageArea名为’default’的默认实例。以下是如何在代码中使用KV存储模块的示例:import {storage} from ‘std:kv-storage’;const main = async () => { const oldPreferences = await storage.get(‘preferences’); document.querySelector(‘form’).addEventListener(‘submit’, async () => { const newPreferences = Object.assign({}, oldPreferences, { // Updated preferences go here… }); await storage.set(‘preferences’, newPreferences); });};main();如果浏览器不支持内置模块怎么办?对于不支持内置模块的浏览器,std:kv-storage无法识别成有效的URL,这时可以通过引入polyfill解决,但如果浏览器支持内置模块,我们更希望的是优先使用内置提供的模块,这就需要一个导入映射的功能,也是chrome正在尝试的一个新功能。导入映射导入映射本质上是一种机制,开发人员可以通过该机制将导入标识符另起别名,这可以为浏览器无法识别std:kv-storage的时侯提供备用路径。代码示例:<!– The import map is inlined into your page –><script type=“importmap”>{ “imports”: { “/path/to/kv-storage-polyfill.mjs”: [ “std:kv-storage”, “/path/to/kv-storage-polyfill.mjs” ] }}</script><!– Then any module scripts with import statements use the above map –><script type=“module”> import {storage} from ‘/path/to/kv-storage-polyfill.mjs’; // Use storage …</script>上面代码中的关键点是URL /path/to/kv-storage-polyfill.mjs 被映射到两个不同的资源:std:kv-storage和 /path/to/kv-storage-polyfill.mjs。因此,当浏览器遇到引用该URL(/path/to/kv-storage-polyfill.mjs)的import语句时,它首先尝试加载std:kv-storage,如果不能,则它会回退到加载 /path/to/kv-storage-polyfill.mjs。# 那么根本不支持模块的浏览器呢?为了使用导入映射有条件地加载内置模块,必须实际使用导入语句,这也意味着必须使用模块脚本,即<script type=“module”>。目前,超过80%的浏览器支持模块,对于不支持模块的浏览器,可以使用module/nomodule技术为旧浏览器兼容。注意,在生成nomodule时,需要包含所有的polyfills,因为不支持模块的浏览器肯定不支持内置模块。PS:欢迎关注公众号「前端新视界」获取前端技术前沿资讯,后台回复“1”领取100本PDF前端电子书籍。 ...

April 13, 2019 · 1 min · jiezi

Chrome 75 将原生支持图片的惰性加载

随着浏览器性能的提升,前端也越来越关注用户体验,而影响用户体验其中一个很重要的指标便是受首屏渲染速度。我们常常会针对样式、脚本、图片、音频、视频等资源做处理,比如针对样式和脚本的压缩合并,将图片合并成雪碧图、将小图转化成base64、延迟加载等减少网络请求次数。现在大部分web应用含有大量的图片,对图片进行延迟加载无疑极大提升用户体验。以往我们可能会通过对比底部图片据可视区底部距离、窗口高度、滚动条距离来判断是否加载新图片,抑或在支持IntersectionObserver API的浏览器中使用交叉区观察者进行监听,而这都需要我们写脚本去判断及控制。今天给大家带来好消息是,Chrome 75 将原生支持图片的惰性加载,支持对img和iframe进行延迟加载,只需要将loading属性设置为lazy即可。<img src=“celebration.jpg” loading=“lazy” alt="…" /><iframe src=“video-player.html” loading=“lazy”></iframe>loading属性Loading属性控制浏览器是否延迟加载屏幕外的图像和iframe:lazy:对资源进行延迟加载。eager:立即加载资源。auto:浏览器自行判断决定是否延迟加载资源。默认效果(不设置该属性)和loading=auto的效果保持一致。需要注意的是,若浏览器决定该资源适合延迟加载,则需要避免页面不正常显示和影响用户体验。该loading属性支持img标签,无论img标签是否含有srcset属性及被picture标签包裹,以及iframe标签。示例代码:<!– Lazy-load an offscreen image when the user scrolls near it –><img src=“unicorn.jpg” loading=“lazy” alt=".."/><!– Load an image right away instead of lazy-loading –><img src=“unicorn.jpg” loading=“eager” alt=".."/><!– Browser decides whether or not to lazy-load the image –><img src=“unicorn.jpg” loading=“auto” alt=".."/><!– Lazy-load images in <picture>. <img> is the one driving image loading so <picture> and srcset fall off of that –><picture> <source media="(min-width: 40em)" srcset=“big.jpg 1x, big-hd.jpg 2x”> <source srcset=“small.jpg 1x, small-hd.jpg 2x”> <img src=“fallback.jpg” loading=“lazy”></picture><!– Lazy-load an image that has srcset specified –><img src=“small.jpg” srcset=“large.jpg 1024w, medium.jpg 640w, small.jpg 320w” sizes="(min-width: 36em) 33.3vw, 100vw" alt=“A rad wolf” loading=“lazy”><!– Lazy-load an offscreen iframe when the user scrolls near it –><iframe src=“video-player.html” loading=“lazy”></iframe>检测属性支持判断浏览器是否支持loading属性<script>if (’loading’ in HTMLImageElement.prototype) { // Browser supports loading..} else { // Fetch and apply a polyfill/JavaScript library // for lazy-loading instead.}</script>浏览器兼容一个新特性的出现必然无法立即兼容所有的浏览器,这需要我们结合以往的data-src进行懒加载的方式作向后兼容,而不是使用src、srcset、<source>,以避免不支持原生懒加载功能的浏览器对资源进行立即加载。<img loading=“lazy” data-src=“pic.png” class=“lazyload” alt="." />不支持原生懒加载的浏览器,我们使用lazySizes库进行兼容(async () => { if (’loading’ in HTMLImageElement.prototype) { const images = document.querySelectorAll(“img.lazyload”); images.forEach(img => { img.src = img.dataset.src; }); } else { // Dynamically import the LazySizes library const lazySizesLib = await import(’/lazysizes.min.js’); // Initiate LazySizes (reads data-src & class=lazyload) lazySizes.init(); // lazySizes works off a global. }})();请求细节原生懒加载在页面加载时获取前2KB的图像。如果服务器支持范围请求,则前2KB可能包含图像尺寸,这使我们能够生成/显示具有相同尺寸的占位符。当然前2KB也可能包括像图标这样的资产的整个图像。尝鲜在本篇文章发布时,chrome最新版本是73.0.3683.103,但我们可以通过启用浏览器的实验性功能开启这一特性。在chrome://flags页面搜索lazy,将Enable lazy image loading和Enable lazy frame loading设置为Enabled,重启浏览器即可。参考AddyOsmani.com - Native image lazy-loading for the web!Twitter - AddyOsmani 原帖欢迎关注公众号「前端新视界」获取前端技术前沿资讯,后台回复“1”领取100本PDF前端电子书籍。 ...

April 13, 2019 · 1 min · jiezi

Chrome 75将原生支持图片的延迟加载!

加载页面时图片一直是流量的大头,过多的图片会严重影响页面的加载速度,针对图片的性能优化方法有诸如base64、雪碧图等等,延迟加载也是其中一种普遍使用的方法。延迟加载即图片的懒加载或惰性加载,当页面初次加载时只加载显示可视区域的图片,页面滚动时,图片进入了可视区域才会进行资源的加载。过去我们一直通过js来实现图片的延迟加载,而chrome75的到来将原生支持该功能!其实去年2月份Chrome就首次提出内置的图片和iframe延迟加载机制,并在8月的Canary版本中加入实验性功能对其进行了测试,最近Google技术经理Addy Osmani已经宣布今年5月底Chrome 75将会默认开启延迟加载的功能。浏览器启用实验性功能对该功能进行体验和测试需要:下载最新版本Chrome Canary。在chrome://flags中将Enable lazy image loading和Enable lazy frame loading设置为Enabled。重启浏览器。loading属性Loading属性控制浏览器是否延迟加载屏幕外的图像和iframe:lazy:对资源进行延迟加载。eager:立即加载资源。auto:浏览器自行判断决定是否延迟加载资源。默认效果(不设置该属性)和loading=auto的效果保持一致。需要注意的是,若浏览器决定该资源适合延迟加载,则需要避免页面不正常显示和影响用户体验。检测属性支持判断浏览器是否支持loading属性(有坑,文章后面会说):if (’loading’ in HTMLImageElement.prototype) { // 浏览器原生支持 loading 属性..} else { // 通过js实现}浏览器兼容需要使用例如data-original这样的属性名标记资源地址(而不是src、srcset或<source>),以避免在不支持该属性的浏览器中立即加载资源:<img loading=“lazy” data-original=“pic.png” class=“lazy” alt="." />若浏览器支持loading属性,则把data-original替换为src;若不支持则使用回原来延迟加载的方法:const images = document.querySelectorAll(“img.lazy”);if (’loading’ in HTMLImageElement.prototype) { images.forEach(img => { img.src = img.dataset.original; });} else { const LazyLoad = await import(’/lazyload.min.js’); let lazyLoadInstance = new LazyLoad({ elements_selector: “img.lazy” });}图片支持类型看到这个新功能后本来有个想法是,可以结合SVG注入器实现SVG的延迟加载:<img loading=‘lazy’ src=“warning.svg” onload=“SVGInject(this)” />但发现对于SVG类型文件,延迟加载并没有生效:经个人不完全测试,以下为图片类型的支持程度:图片类型是否支持JPEG支持GIF不支持PNG支持APNG不支持SVG不支持重复请求需要注意的是,Chrome会在页面加载时请求需要延迟加载图片的前2048个字节,当用户即将滚动到图片时,Chrome会再次请求剩余的图片字节(不会影响该图片的onload事件)。若服务器支持范围请求,则会在前2048个字节中包含图片尺寸,浏览器会生成/展示相同大小的占位:各方态度火狐:Firefox的一位工程师表示,Mozilla目前还没有任何计划实现Chrome的延迟加载方案。Microsoft Edge:新的Edge浏览器使用了Chromium 75内核,经下载泄露版本测试,该版本在edge://flags/中开启实验性功能,可支持延迟加载,且也会重复请求资源,但HTMLImageElement.prototype无loading属性,只有load属性,需进行区分判断处理。Safari:一位WebKit开发者提到,Apple对这类功能很感兴趣:其他:在WICG上关于原生支持延迟加载图片的讨论。总结在原代码的基础上更新兼容新功能并不复杂,但在启用新功能前可能需要进一步的思考,比如若对所有图片设定loading=“auto”,某些被延迟加载的图片是否会影响用户体验的流畅性?浏览器对图片进行延迟加载的判断与决定是否会影响页面性能?对大量图片进行延迟加载,浏览器会先请求所有延迟加载图片的前2048个字节,在弱网环境下是否会卡在这个阶段反而耽误了首屏图片的加载?其他浏览器是否会遵循这种规范?emmm虽然问题有点多但新功能还是可以带来新玩法~比如以后支持SVG后可对其进行延迟加载,设定loading=“eager"可对首屏重要的大图优先进行加载,对于广告(iframe或图片)的数据统计可更加精准(设定延迟加载并在onload事件中统计)…参考AddyOsmani.com - Native image lazy-loading for the web!Firefox will support image lazy loading for new tab page | ZDNetTwitter上原讨论贴 ...

April 13, 2019 · 1 min · jiezi

基于Sklearn机器学习实战---基于Sklearn模块的链路预测

Sklearn简介自2007年发布以来,scikit-learn已经成为Python重要的机器学习库了。scikit-learn简称sklearn,支持包括分类、回归、降维和聚类四大机器学习算法。还包含了特征提取、数据处理和模型评估三大模块。 sklearn是Scipy的扩展,建立在NumPy和matplotlib库的基础上。利用这几大模块的优势,可以大大提高机器学习的效率。 sklearn拥有着完善的文档,上手容易,具有着丰富的API,在学术界颇受欢迎。sklearn已经封装了大量的机器学习算法,包括LIBSVM和LIBINEAR。同时sklearn内置了大量数据集,节省了获取和整理数据集的时间。项目简介链路预测是通过历史连接信息预测未来可能产生的连接,即通过当前网络中的连边信息预测将来可能产生的连边信息。项目源码from sklearn.model_selection import train_test_split # 分割数据模块from sklearn.neighbors import KNeighborsClassifier # K最近邻(kNN,k-NearestNeighbor)分类算法import pandas as pdimport numpy as npfrom sklearn.model_selection import train_test_splitfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn import preprocessingimport matplotlib.pyplot as pltfrom sklearn.svm import SVCfrom math import isnan定义计算共同邻居指标的方法define some functions to calculate some baseline index计算Jaccard相似性指标def Jaccavrd(MatrixAdjacency_Train):Matrix_similarity = np.dot(MatrixAdjacency_Train,MatrixAdjacency_Train)deg_row = sum(MatrixAdjacency_Train)deg_row.shape = (deg_row.shape[0],1)deg_row_T = deg_row.Ttempdeg = deg_row + deg_row_Ttemp = tempdeg - Matrix_similarityMatrix_similarity = Matrix_similarity / tempreturn Matrix_similarity定义计算Salton指标的方法def Salton_Cal(MatrixAdjacency_Train):similarity = np.dot(MatrixAdjacency_Train,MatrixAdjacency_Train)deg_row = sum(MatrixAdjacency_Train)deg_row.shape = (deg_row.shape[0],1)deg_row_T = deg_row.Ttempdeg = np.dot(deg_row,deg_row_T)temp = np.sqrt(tempdeg)np.seterr(divide=‘ignore’, invalid=‘ignore’)Matrix_similarity = np.nan_to_num(similarity / temp)Matrix_similarity = np.nan_to_num(Matrix_similarity)return Matrix_similaritydef file2matrix(filepath):f = open(filepath)lines = f.readlines()matrix = np.zeros((50, 50), dtype=float)A_row = 0for line in lines: list = line.strip(’\n’).split(’ ‘) matrix[A_row:] = list[0:50] A_row += 1return matrix filepath = ‘3600/s0001.txt’MatrixAdjacency = file2matrix(filepath)similarity_matrix_Jaccavrd = Jaccavrd(MatrixAdjacency)similarity_matrix_Salton = Salton_Cal(MatrixAdjacency)filepath2 = ‘3600/s0002.txt’MatrixAdjacency2 = file2matrix(filepath2)similarity_matrix_Jaccavrd2 = Jaccavrd(MatrixAdjacency2)similarity_matrix_Salton2 = Salton_Cal(MatrixAdjacency2)filepath3 = ‘3600/s0003.txt’MatrixAdjacency3 = file2matrix(filepath3)similarity_matrix_Jaccavrd3 = Jaccavrd(MatrixAdjacency3)similarity_matrix_Salton3 = Salton_Cal(MatrixAdjacency3)获取jaccard相似性矩阵的行数和列数Jaccard_Row = similarity_matrix_Jaccavrd.shape[0]Jaccard_Column = similarity_matrix_Jaccavrd.shape[1]Jaccard_List = []for i in range(Jaccard_Row):for j in range(Jaccard_Column): if i<j: index = similarity_matrix_Jaccavrd[i,j] if isnan(index) == True: index = 0 Jaccard_List.append(index)获取Salton相似性矩阵的行数和列数Salton_Row = similarity_matrix_Salton.shape[0]Salton_Column = similarity_matrix_Salton.shape[1]Salton_List = []for i in range(Salton_Row):for j in range(Salton_Column): if i<j: index = similarity_matrix_Salton[i,j] if isnan(index) == True: index = 0 Salton_List.append(index) 获取jaccard相似性矩阵的行数和列数Jaccard_Row2 = similarity_matrix_Jaccavrd2.shape[0]Jaccard_Column2 = similarity_matrix_Jaccavrd2.shape[1]Jaccard_List2 = []for i in range(Jaccard_Row2):for j in range(Jaccard_Column2): if i<j: index2 = similarity_matrix_Jaccavrd2[i,j] if isnan(index2) == True: index2 = 0 Jaccard_List2.append(index2) 获取Salton相似性矩阵的行数和列数Salton_Row2 = similarity_matrix_Salton2.shape[0]Salton_Column2 = similarity_matrix_Salton2.shape[1]Salton_List2 = []for i in range(Salton_Row2):for j in range(Salton_Column2): if i<j: index2 = similarity_matrix_Salton2[i,j] if isnan(index2) == True: index2 = 0 Salton_List2.append(index2) 获取jaccard相似性矩阵的行数和列数Jaccard_Row3 = similarity_matrix_Jaccavrd3.shape[0]Jaccard_Column3 = similarity_matrix_Jaccavrd3.shape[1]Jaccard_List3 = []for i in range(Jaccard_Row3):for j in range(Jaccard_Column3): if i<j: index3 = similarity_matrix_Jaccavrd3[i,j] if isnan(index3) == True: index3 = 0 Jaccard_List3.append(index3) 获取Salton相似性矩阵的行数和列数Salton_Row3 = similarity_matrix_Salton3.shape[0]Salton_Column3 = similarity_matrix_Salton3.shape[1]Salton_List3 = []for i in range(Salton_Row3):for j in range(Salton_Column3): if i<j: index3 = similarity_matrix_Salton3[i,j] if isnan(index3) == True: index3 = 0 Salton_List3.append(index3) 获取邻接矩阵的行数和列数Adjacency_Row = MatrixAdjacency.shape[0]Adjacency_Column = MatrixAdjacency.shape[1]Adjacency = []for i in range(Adjacency_Row):for j in range(Adjacency_Column): if i<j: index = MatrixAdjacency[i,j] Adjacency.append(index) 获取邻接矩阵的行数和列数Adjacency_Row2 = MatrixAdjacency2.shape[0]Adjacency_Column2 = MatrixAdjacency2.shape[1]Adjacency2 = []for i in range(Adjacency_Row2):for j in range(Adjacency_Column2): if i<j: index2 = MatrixAdjacency2[i,j] Adjacency2.append(index2) 获取邻接矩阵的行数和列数Adjacency_Row3 = MatrixAdjacency3.shape[0]Adjacency_Column3 = MatrixAdjacency3.shape[1]Adjacency3 = []for i in range(Adjacency_Row3):for j in range(Adjacency_Column3): if i<j: index3 = MatrixAdjacency3[i,j] Adjacency3.append(index3) data = np.zeros((1225,3))data2 = np.zeros((1225,3))data3 = np.zeros((1225,3)) for i in range(1225):data[i][0] = Jaccard_List[i]data[i][1] = Salton_List[i]data[i][2] = Adjacency[i]for j in range(1225):data2[j][0] = Jaccard_List2[j]data2[j][1] = Salton_List2[j]data2[j][2] = Adjacency2[j]for k in range(1225):data3[k][0] = Jaccard_List3[k]data3[k][1] = Salton_List3[k]data3[k][2] = Adjacency3[k]data_train_X = data[:,0:2]data_train_y = data[:,2]data_test_X = data2[:,0:2]data_test_y = data2[:,2]data_target_X = data3[:,0:2]data_target_y = data3[:,2]knn = KNeighborsClassifier()knn.fit(data_train_X,data_train_y)print(knn.predict(data_test_X))print(data_test_y)clf = SVC()clf.fit(data_train_X,data_test_y)print(clf.score(data_test_X,data_target_y))项目详细了解如需详细本项目信息,可发送邮件至18770918982@gmail.com ...

April 9, 2019 · 2 min · jiezi

2019 “掘安杯” write up

前言肝了一天,最后打了第三,记录下。我逆向真的好菜啊~~~~Misc真的不是图片题目给了一张图片,binwalk一下pumpkin9@pumpkin9:/mnt/c/Users/Desktop/juean$ binwalk Misc-JASEC.pngDECIMAL HEXADECIMAL DESCRIPTION——————————————————————————–0 0x0 PNG image, 824 x 639, 8-bit/color RGB, non-interlaced91 0x5B Zlib compressed data, compressed140598 0x22536 End of Zip archive, footer length: 22题目中有zip,和正常压缩包图片对比一下emmm反正是少了个zip头了可以发现 50 4B 03 04 被替换成了ja66pumpkin9@pumpkin9:/mnt/c/Users/Desktop/juean$ binwalk Misc-JASEC.pngDECIMAL HEXADECIMAL DESCRIPTION——————————————————————————–0 0x0 PNG image, 824 x 639, 8-bit/color RGB, non-interlaced91 0x5B Zlib compressed data, compressed137859 0x21A83 Zip archive data, at least v2.0 to extract, compressed size: 2605, uncompressed size: 11258, name: subject.zip140598 0x22536 End of Zip archive, footer length: 22然后foremost分离ja66解压缩import base64flag = ““for i in range(0,32): f = open(’./’+str(i)+’/’+str(i)+’.txt’,‘r’) flag += f.read()print base64.b64decode(flag)#jactf{64se64_1s_50_c001}what题目描述=E4=BD=9B=E6=9B=B0=EF=BC=9A=E6=A2=B5=E5=83=A7=E5=A5=A2=E6=A5=9E=E5=A5=A2=E5=90=89=E8=8B=A5=E5=A5=A2=E4=B8=8D=E5=B8=9D=E5=86=A5=E5=A4=9C=E6=98=AF=E7=BC=BD=E6=9C=8B=E7=BC=BD=E7=9C=9F=E7=89=B9=E4=BF=B1=E4=B8=8A=E7=BD=B0=E8=83=BD=E7=9A=A4=E5=AE=A4=E9=98=BF=E8=AB=B3=E6=98=8E=E4=B8=80=E5=88=87=E5=91=90=E9=99=A4=E6=A2=B5=E5=A7=AA=E7=BC=BD=E5=A9=86=E5=91=90=E4=BA=A6=E5=8F=83=E4=BE=84=E5=91=BC=E7=9A=A4=E4=B8=96=E5=93=86=E7=89=B9=E5=93=86=E6=95=85=E5=8B=9D=E8=AB=B3=E7=88=8D=E8=AC=B9=E6=99=BA=E7=9A=A4=E5=8F=83=E5=AD=95=E9=80=9D=E8=AB=B3=E8=AC=B9=E6=BC=AB=E6=AD=BB=E5=8D=B3=E4=BE=84=E9=99=A4=E5=93=86=E9=80=9D=E4=BE=84=E6=98=AF=E5=A5=A2=E5=96=9D=E7=A4=99=E8=B1=86=E8=AB=B3=E6=A5=9E=E7=84=A1=E4=BF=B1=E8=80=85=E5=93=86=E5=BA=A6=E8=80=85=E3=80=82=E8=AB=B3=E7=9C=9F=E5=86=A5=E8=A8=B6=E4=BE=84=E5=8B=9D=E7=AB=9F=E8=97=9D=E5=A5=A2=E4=B8=8D=E4=BC=8A=E7=9A=A4=E8=AC=B9=E6=B6=85=E5=AD=95=E7=84=A1=E4=BB=96=E7=BE=85=E5=A4=A7=E5=BE=97=E9=97=8D=E5=93=86=E5=96=9D=E8=80=B6=E5=83=A7=E7=84=A1=E7=BE=AF=E6=BB=85=E9=99=A4=E5=88=A9=E7=BC=BD=E5=A4=9A=E6=A2=B5=E5=A4=B7=E6=A2=B5=E6=A0=97=E7=BC=BD=E8=80=85=E5=AD=95=E8=AB=B3=E7=9B=A7=E7=9A=A4=E4=B8=89=E7=BD=B0=E5=AF=AB=E8=80=81=E6=A2=B5=E8=80=B6=E5=AE=A4=E5=B8=9D=E6=A2=B5=E5=AF=AB=E7=BE=AF=E6=95=B8=E6=A2=B5=E7=9B=A1=E4=BE=84=E6=A0=97=E4=BE=84=E8=97=90=E4=BF=B1=E4=B8=96=E8=AB=B3=E4=B8=8A=E8=AB=B3=E5=A7=AA=E6=95=B8=E5=AE=A4=E5=A9=86=E7=BD=B0=E6=A7=83=E5=A5=A2=E8=A8=B6=E5=93=86=E5=A4=9A=E9=80=9D=E8=97=90=E9=81=93=E6=A2=B5=E6=A5=9E=E6=A2=B5=E5=8D=97=E4=BE=84=E8=BF=A6=E5=91=90=E7=9F=A5=E6=9C=8B=E6=A5=9E=E4=BE=84=E9=9B=A2=E5=91=90=E6=B2=99=E5=91=90=E6=99=BA=E9=81=AE=E5=A4=A7=E5=AE=A4=E7=A5=9E=E5=86=A5=E8=BC=B8=E6=AE=BF=E7=BC=BD=E6=A7=83=E6=A2=B5=E6=80=9B=E6=81=90=E8=88=8D=E7=9F=A5=E7=9A=A4=E8=BF=A6=E5=A5=A2=E8=88=AC=E8=AB=B3=E7=88=8D=E5=AF=AB=E6=BC=AB=E4=BC=8A=E4=BF=B1=E6=A0=97=E5=93=86=E4=BB=96=E4=BA=A6=E7=BC=BD=E6=A5=9E=E6=80=9B=E5=86=A5=E5=91=BC=E5=88=87=E4=BF=B1=E8=8F=A9=E8=88=8D=E5=91=90=E5=AF=A6=E6=A0=97=E5=A5=A2=E6=B3=A2=E6=91=A9=E8=AB=B3=E9=81=93=E7=BC=BD=E7=91=9F=E5=93=86=E5=AF=A6=E7=9A=A4=E7=88=8D=E5=8B=9D=E8=96=A9=E7=BD=B0=E8=AB=B8=E5=A5=A2=E8=88=AC=E8=AB=A6=E7=BD=B0=E6=98=8E=E7=BC=BD=E8=AB=A6=E5=B0=BC=E5=93=86=E6=A5=9E=E4=BD=9B=E4=BF=B1=E9=86=AF=E8=AB=B3=E6=BB=85=E5=BA=A6=E5=93=86=E6=89=80=E6=A7=83=E5=A7=AA=E9=BA=BC=E6=89=80=E6=81=90=E8=AB=B3=E4=BB=96=E4=BE=84=E5=AF=AB=E7=91=9F=E4=BE=84=E6=89=80=E5=BE=97=E9=9A=B8=E5=93=86=E9=97=8D=E5=91=90=E6=8F=90=E7=9B=A7=E5=86=A5=E5=92=92=E5=A5=A2=E6=9B=B0=E5=91=90=E6=B2=99=E6=80=AF=E8=88=AC=E5=8D=97=E6=80=AF=E5=9C=B0=E7=BC=BD=E5=96=9D=E5=86=A5=E6=83=B3=E5=91=90=E7=9B=A7=E7=BD=B0=E8=AC=B9=E5=91=BC=E8=B7=8B=E7=BC=BD=E4=B8=8A=E5=A8=91=E8=AB=A6=E6=AD=BB=E4=BE=84=E8=BF=A6解题过程Quoted-Printable也是MIME邮件中常用的编码方式之一。同Base64一样,它也将输入的字符串或数据编码成全是ASCII码的可打印字符串。quopriquopri.decodestring()解码可得佛曰:梵僧奢楞奢吉若奢不帝冥夜是缽朋缽真特俱上罰能皤室阿諳明一切呐除梵姪缽婆呐亦參侄呼皤世哆特哆故勝諳爍謹智皤參孕逝諳謹漫死即侄除哆逝侄是奢喝礙豆諳楞無俱者哆度者。諳真冥訶侄勝竟藝奢不伊皤謹涅孕無他羅大得闍哆喝耶僧無羯滅除利缽多梵夷梵栗缽者孕諳盧皤三罰寫老梵耶室帝梵寫羯數梵盡侄栗侄藐俱世諳上諳姪數室婆罰槃奢訶哆多逝藐道梵楞梵南侄迦呐知朋楞侄離呐沙呐智遮大室神冥輸殿缽槃梵怛恐舍知皤迦奢般諳爍寫漫伊俱栗哆他亦缽楞怛冥呼切俱菩舍呐實栗奢波摩諳道缽瑟哆實皤爍勝薩罰諸奢般諦罰明缽諦尼哆楞佛俱醯諳滅度哆所槃姪麼所恐諳他侄寫瑟侄所得隸哆闍呐提盧冥咒奢曰呐沙怯般南怯地缽喝冥想呐盧罰謹呼跋缽上娑諦死侄迦参悟佛所言的真意公正友善自由公正民主公正和谐法治自由公正公正法治友善平等公正爱国公正平等法治爱国公正敬业公正友善爱国平等诚信平等法治敬业法治平等公正公正公正诚信平等平等友善敬业法治民主法治富强法治友善法治社会主义核心价值观解码得flagjactf{hexin_yufo_qp}小梳子生成字典爆破crunch 11 11 -t 138364%%%%% -o/root/桌面/test.txtaircrack-ng -w /root/桌面/test.txt Tenda_D07D90-01.capCrypto贝斯家族三英战群魔直接上脚本$ python base.py ciphertext_ea88a4d420c804686a8899608e06130f.txt1using base16 decode sucess…..2using base16 decode failuer…..using base32 decode sucess…..3using base16 decode failuer…..using base32 decode failuer…..using base64 decode sucess…..4using base16 decode sucess…..5using base16 decode failuer…..using base32 decode sucess…..6using base16 decode failuer…..using base32 decode failuer…..using base64 decode sucess…..7using base16 decode sucess…..8using base16 decode failuer…..using base32 decode sucess…..9using base16 decode failuer…..using base32 decode failuer…..using base64 decode sucess…..10using base16 decode sucess…..11using base16 decode failuer…..using base32 decode sucess…..12using base16 decode failuer…..using base32 decode failuer…..using base64 decode sucess…..13using base16 decode failuer…..using base32 decode failuer…..using base64 decode failuer…..jactf{4(b64_32_16)}罗马帝国的奠基者根据凯撒加密方式和flag格式可得a = ‘h^_o[pZi^i‘b = ““for j in range(0,90): b= "” for i in range(len(a)): b += chr(ord(a[i])+i+2) print b绝密情报题目描述WzI2NDAzMjMxMEwsIDQ5NTA2MzczNDFMLCA0MTg5MTM3MjM1TCwgMzUwMzY3NTkwNkwsIDExOTMyNzJMLCAzNzQ1MzA5NjhMLCA1MTg5MjgxNTMxTCwgMjUxNDIwMDI3MkwsIDQ0NTQzMDU1ODFMLCA2NDEwNzg1OTdMLCA0Mzk1OTMxNjU5TCwgMjcxNjQyNjU5OUwsIDQzNzUzOTE5NEwsIDM0NDgwMTM1OTZMLCAzMDcyMDcyMDlMLCA0NzUwODIwNjA2TCwgMzI1MDQwNzk5M0wsIDg1MzkwNTIwOUwsIDIxMDk3OTExNTlMLCAyNzE2NDI2NTk5TCwgMjEwNzg5OTU1NEwsIDQzOTU5MzE2NTlMLCAyNzk0Mzg0NTk4TCwgMjEwOTc5MTE1OUwsIDUyOTc3NzkwOTRMLCAxNDYwODc0Mjg2TCwgMTQ2MDg3NDI4NkwsIDc5NDkzMTY3OUwsIDc5NDkzMTY3OUwsIDU0NDcwNTE2MjJMLCA4NTM5MDUyMDlMLCAzMTk4MzQwMjE4TCwgMTE5MzI3MkwsIDE5MTIzMjMxMDFMLCA1Mjk3Nzc5MDk0TCwgMzA3MjA3MjA5TCwgMzIzMTU3MjYwOEwsIDMxOTgzNDAyMThMLCA1MTg5MjgxNTMxTCwgNTI3ODg5NTQ4TCwgNDk1MDYzNzM0MUwsIDI4MzkzNjY4MDVMLCAxMTE2NDU3MzU0TCwgNTI3ODg5NTQ4TCwgNTI5Nzc3OTA5NEwsIDMyNTA0MDc5OTNMLCA0NDU0MzA1NTgxTCwgNjUxMDM5MkwsIDMyNTA0MDc5OTNMLCAxNDYwODc0Mjg2TCwgMTA1OTAzNTEyOUwsIDMyMDAzNTk2MTJMLCA4NTM5MDUyMDlMLCAzMDcyMDcyMDlMLCAxNTY3NzkxMDFMLCAyMTQ1MzAxMzI4TCwgNTI3ODg5NTQ4TCwgMTA1OTAzNTEyOUwsIDU0NjgwMjUwNzJMLCAzNDQ4MDEzNTk2TCwgMjEwNzg5OTU1NEwsIDQxODkxMzcyMzVMLCAzNTAzNjc1OTA2TCwgMjY1MzQzNjExM0xd而且小菜昨天偷听到了一部分关于情报的绝密资料,如下:N=5520780427 , e = 134257,你能帮小菜解出这段情报吗?解题过程import base64,libnumenc = “WzI2NDAzMjMxMEwsIDQ5NTA2MzczNDFMLCA0MTg5MTM3MjM1TCwgMzUwMzY3NTkwNkwsIDExOTMyNzJMLCAzNzQ1MzA5NjhMLCA1MTg5MjgxNTMxTCwgMjUxNDIwMDI3MkwsIDQ0NTQzMDU1ODFMLCA2NDEwNzg1OTdMLCA0Mzk1OTMxNjU5TCwgMjcxNjQyNjU5OUwsIDQzNzUzOTE5NEwsIDM0NDgwMTM1OTZMLCAzMDcyMDcyMDlMLCA0NzUwODIwNjA2TCwgMzI1MDQwNzk5M0wsIDg1MzkwNTIwOUwsIDIxMDk3OTExNTlMLCAyNzE2NDI2NTk5TCwgMjEwNzg5OTU1NEwsIDQzOTU5MzE2NTlMLCAyNzk0Mzg0NTk4TCwgMjEwOTc5MTE1OUwsIDUyOTc3NzkwOTRMLCAxNDYwODc0Mjg2TCwgMTQ2MDg3NDI4NkwsIDc5NDkzMTY3OUwsIDc5NDkzMTY3OUwsIDU0NDcwNTE2MjJMLCA4NTM5MDUyMDlMLCAzMTk4MzQwMjE4TCwgMTE5MzI3MkwsIDE5MTIzMjMxMDFMLCA1Mjk3Nzc5MDk0TCwgMzA3MjA3MjA5TCwgMzIzMTU3MjYwOEwsIDMxOTgzNDAyMThMLCA1MTg5MjgxNTMxTCwgNTI3ODg5NTQ4TCwgNDk1MDYzNzM0MUwsIDI4MzkzNjY4MDVMLCAxMTE2NDU3MzU0TCwgNTI3ODg5NTQ4TCwgNTI5Nzc3OTA5NEwsIDMyNTA0MDc5OTNMLCA0NDU0MzA1NTgxTCwgNjUxMDM5MkwsIDMyNTA0MDc5OTNMLCAxNDYwODc0Mjg2TCwgMTA1OTAzNTEyOUwsIDMyMDAzNTk2MTJMLCA4NTM5MDUyMDlMLCAzMDcyMDcyMDlMLCAxNTY3NzkxMDFMLCAyMTQ1MzAxMzI4TCwgNTI3ODg5NTQ4TCwgMTA1OTAzNTEyOUwsIDU0NjgwMjUwNzJMLCAzNDQ4MDEzNTk2TCwgMjEwNzg5OTU1NEwsIDQxODkxMzcyMzVMLCAzNTAzNjc1OTA2TCwgMjY1MzQzNjExM0xd"enc = base64.b64decode(enc)enc_list = eval(enc)flag = ““print enc_listd = 3960784897n = 5520780427for i in range(len(enc_list)): m = pow(enc_list[i],d,n) flag += chr(m)print flag#U2FsdGVkX1/8DKBmhvO87/SOLaawwxvAdHLB9AV62nC6LhXzhatpvBcg6tlK7Fs5des 解密下即可jactf{So_easy_RSA_and_DES}贝叶斯题目一共给了两个文件encode.txtint main(){ string P(””); string C(””); int len = C.length(); for (int k = 0; k < len; k ++) { int where = des_find(P, C[k]); where = ((where * a) + b) mod x; cout << P[where]; } return 0;}int des_find(string p, int m){ for (int i = 0; i < p.length(); i++) { if (m == p[i]) { return i; } }}题目.txt现已知某间谍使用的密码本(这可是贝叶斯设计的密码本)如下:“elFXRVJUWVVJT1B4Y3Zibm1hc2RmQVNERkdISktMZ2hqa2xfcXdaWENWQk5NZXJ0e3l1aW9wfTAxMjM0OTg3NjU=“现获取到了他们的加密算法,同时劫获了一段数据密文:“gf9C{YQ34KHN3sOwhCz3RzH3CKj3Ndpm1Bt7"你能破译出明文数据吗?解题过程#include <iostream>#include <cstring>#define PSIZE 65 //宏定义密码表大小using namespace std;int gcd(int m, int n);int init_gcd(int m, int n);int des_find(string p, int m);int main(){ string P(“zQWERTYUIOPxcvbnmasdfASDFGHJKLghjkl_qwZXCVBNMert{yuiop}0123498765”); string M(“gf9C{YQ34KHN3sOwhCz3RzH3CKj3Ndpm1Bt7”); //明文空间,与已知密文 string C; //存放解密明文 int i = 2; //求解所有互素的数 int a1; //存放逆元 for (i; i < PSIZE; i++) { if (gcd(i, PSIZE) == 1) { //说明此时的i与28互素 /求解此时的i的逆元/ a1 = init_gcd(i, PSIZE); for (int j = 0; j < PSIZE; j++) //控制b的遍历 { cout << “此时:a=” << i << " b=” << j << " a的逆元为:” << a1 << " "”; for (int k = 0; k < M.length(); k++) { //每一个汉字站两个字节,所以要用两个数组空间来存 int where = des_find(P, M[k]); //匹配密文在明文空间的位置 where = ((where - j)a1) % PSIZE; if (where < 0) { where += PSIZE; } cout << P[where]; } cout << “"” << endl; } } } return 0;}int gcd(int b, int a) //求互素{ int temp; if (a < b)//判断大小 { temp = a; a = b; b = temp; } if (b == 0) return a; else return gcd(b, a%b);//递归}int init_gcd(int m, int n) //扩展欧几里得算法{ int i = 2; for (i; i < 28; i++) { if ((mi) % n == 1) { return i; } }}int des_find(string p, int m) //位置匹配函数{ for (int i = 0; i < p.length(); i ++) { //cout<<p[i]<<p[i+1]<<endl; if (m == p[i]) { return i; } }}接下来的计划总结下base家族wasm贝叶斯关于字符向进制转化的算法与逆向pyc 文件格式des加密ebc cbc ...

April 8, 2019 · 3 min · jiezi

听说你在找工作?

都说今年形势不好,实际情况确实大不如以前,当然,大神忽略。奔波快半个月,在这里做一个简单的总结。背景岗位:前端开发时间:2019年4月7号地点:北京经验:3年多学历:本科 经过:看了大概200多个岗位,确认投递60几个,面试机会26个,参加面试14家,拿到offer5个行情1.大体趋势上,要求会 react 的比例明显上升,react 和 vue 占比6:4,大公司 react 的占比更高,angular 继续边缘化,所以ag还是悠着点2.React Native 的需求量远远超过 Weex,23成的公司会有这个需求3.微信小程序的需求量急剧提升,大概有3成多的公司有要求4.对图形化有研究的有一定有优势,图形化类需求持续增长,比如d3、three、echarts等5.尽量学会用苹果电脑,前端配置苹果的占比在持续加大上升中,有一统天下的趋势6.背景要求持续提高,大厂背景、硕士或者985/211、会英语、有自己的博客文章或者github作品等7.跳槽薪资涨幅大大降低,行情20%以内,基本上砍价砍的比以前厉害,因为今年的形势是一个很好的说辞8.整体行情,初中级供大于求,高级和大牛永远稀缺,面试难度继续保持一年比一年加大,同时前端门槛持续提高总结1.写简历,刚开始策略没对,一口气写了份简历就开始投,导致简历通过率低。方法:找50个岗位,把每个岗位的需求职责和要点提炼出来,然后综合这50个提炼到的数据再做精简和对比,去掉不会的和占比小的,留下出现次数多的,然后组织成自己的语言放到简历中2.时机,面对心仪的公司先不要投,因为工作太久突然去参加面试,很多都忘了,状态不佳。方法:先找56家小一点的公司练练手,找找感觉,把状态值打到90以上,然后再去参加大公司面试(这个特别重要,除非你的实力能够碾压一切)3.距离,距离很重要,比如说就选10站地铁以内的,除非为了练手或者近期正要搬家,否则可以排除远距离的公司4.行业,优先选择同行业的,因为到后期行业经验也有很大的加分,其次选择互联网相关行业,毕竟互联网目前还是最火爆的那个(在线教育最近招人较多,大数据、人工智能前景广阔但是落地不多,p2p、区块链风险在加大)5.公司规模,如果没想好,尽量去大公司,确定好以后就可以把不稳定的、小型的、前景不明的都去掉,因为同等情况下,这些公司你担的风险更大 6.公司业务,优先选择是公司核心部门或者核心业务的技术组,因为边缘业务资源更少,裁员的时候也是优先被裁的 7.去参加面试之前最好先打个电话确认下,因为很可能此时人已经招满了 8.并不是挂在上面的公司都在招人,有可能是打广告的,所以简历没被接受也不要着急,继续投下一家9.面试时间可以商量,尽量不要迟到,如果没带简历可以和hr说,他们会打印10.平时做事要靠谱,得到大家的认可,尽量认识牛人,有时候别人的一个推荐比你自己说一百句有用的多最后面试成功与否看能力也看运气,最后成与不成,缘分很重要不管什么情况,保持基本礼仪,保持良好心态,毕竟,只是一场面试而已祝愿大家都能找到心仪的工作~

April 7, 2019 · 1 min · jiezi

项目中常用js封装(持续更新)

我们日常开发中经常会碰到各种各样的需求,但很多需求都是重复的,因此我就把平时开发中遇到的一些常见方法做了个总结和归纳。1、金额的格式化比如2.00,1,222,2.00像这样格式的数据在很多电商或者后台数据显示上见到,然而这些数据后台往往只是给我们前端返回2或者12222等等,因此我们前端就需要对数据进行处理/格式化数字格式@param s为要格式化的number@param n为要保留几位小数点example: formatNum(2,2) 返回结果为"2.00"/const formatNum = (s, n) => {n = n > 0 && n <= 20 ? n : 2s = parseFloat((s + ‘’).replace(/[^\d.-]/g, ‘’)).toFixed(n) + ‘‘var l = s.split(’.’)[0].split(’’).reverse(), r = s.split(’.’)[1]t = ‘‘for (i = 0; i < l.length; i++) { t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? ‘,’ : ‘’)}return t.split(’’).reverse().join(’’) + ‘.’ + r}2、检查某个对象是否为空这个常见于我们进行判断的时候啦/检查一个对象是否为空@param object为要判断的对象example:let obj = {}isEmptyObj(obj) 返回结果为true,否则false/const isEmptyObj = object => {if (!!Object.getOwnPropertySymbols(object).length) { return false}for (const key in object) { if (object.hasOwnProperty(key)) { return false }}return true} ...

April 4, 2019 · 1 min · jiezi

Node.js 应用故障排查手册 —— 正确打开 Chrome devtools

楔子前面的预备章节中我们大致了解了如何在服务器上的 Node.js 应用出现问题时,从常规的错误日志、系统/进程指标以及兜底的核心转储这些角度来排查问题。这样就引出了下一个问题:我们知道进程的 CPU/Memory 高,或者拿到了进程 Crash 后的核心转储,要如何去进行分析定位到具体的 JavaScript 代码段。其实 Chrome 自带的 Devtools,对于 JavaScript 代码的上述 CPU/Memory 问题有着很好的原生解析展示,本节会给大家做一些实用功能和指标的介绍(基于 Chrome v72,不同的版本间使用方式存在差异)。本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。CPU 飙高问题I. 导出 JS 代码运行状态当我们通过第一节中提到的系统/进程指标排查发现当前的 Node.js 应用的 CPU 特别高时,首先我们需要去通过一些方式将当前 Node.js 应用一段时间内的 JavaScript 代码运行状况 Dump 出来,这样子才能分析知道 CPU 高的原因。幸运的是,V8 引擎内部实现了一个 CPU Profiler 能够帮助我们完成一段时间内 JS 代码运行状态的导出,目前也有不少成熟的模块或者工具来帮我们完成这样的操作。v8-profiler 是一个老牌的 Node.js 应用性能分析工具,它可以很方便地帮助开发者导出 JS 代码地运行状态,我们可以在项目目录执行如下命令安装此模块:npm install v8-profiler –save接着可以在代码中按照如下方式获取到 5s 内地 JS 代码运行状态:‘use strict’;const v8Profiler = require(‘v8-profiler’);const title = ’test’;v8Profiler.startProfiling(title, true);setTimeout(() => { const profiler = v8Profiler.stopProfiling(title); profiler.delete(); console.log(profiler);}, 5000);那么我们可以看到,v8-profiler 模块帮我导出的代码运行状态实际上是一个很大的 JSON 对象,我们可以将这个 JSON 对象序列化为字符串后存储到文件:test.cpuprofile 。注意这里的文件名后缀必须为 .cpuprofile ,否则 Chrome devtools 是不识别的。注意:v8-profiler 目前也处于年久失修的状态了,在 Node.js 8 和 Node.js 10 上已经无法正确编译安装了,如果你在 8 或者 10 的项目中想进行使用,可以试试看 v8-profiler-next。II. 分析 CPU Profile 文件借助于 v8-profiler 拿到我们的 Node.js 应用一段时间内的 JS 代码运行状态后,我们可以将其导入 Chrome devtools 中进行分析展示。在 Chrome 72 中,分析我们 Dump 出来的 CPU Profile 的方法已经和之前有所不同了,默认工具栏中也不会展示 CPU Profile 的分析页面,我们需要通过点击工具栏右侧的 更多 按钮,然后选择 More tools -> JavaScript Profiler 来进入到 CPU 的分析页面,如下图所示:选中 JavaScript Profiler 后,在出现的页面上点击 Load 按钮,然后将刚才保存得到的 test.cpuprofile 文件加载进来,就可以看到 Chrome devtools 的解析结果了:这里默认的视图是 Heavy 视图,在这个视图下,Devtools 会按照对你的应用的影响程度从高到低,将这些函数列举出来,点击展开能够看到这些列举出来的函数的全路径,方便你去代码中对应的位置进行排查。这里解释两个比较重要的指标,以便让大家能更有针对性地进行排查:Self Time: 此函数本身代码段执行地时间(不包含任何调用)Total Time: 此函数包含了其调用地其它函数总共的执行时间像在上述地截图例子中,ejs 模块在线上都应该开启了缓存,所以 ejs 模块的 compile 方法不应该出现在列表中,这显然是一个非常可疑的性能损耗点,需要我们去展开找到原因。除了 Heavy 视图,Devtools 实际上还给我们提供了火焰图来进行更多维度的展示,点击左上角可以切换:火焰图按照我们的 CPU 采样时间轴进行展示,那么在这里我们更容易看到我们的 Node.js 应用在采样期间 JS 代码的执行行为,新增的两个指标这边也给大家解释一下其含义:Aggregated self time: 在 CPU 采样期间聚合后的此函数本身代码段的执行总时间(不包含其他调用)Aggregated total time: 在 CPU 采样期间聚合后的此函数包含了其调用地其它函数总共的执行总时间综上,借助于 Chrome devtools 和能够导出当前 Node.js 应用 Javascript 代码运行状态的模块,我们已经可以比较完备地对应用服务异常时,排查定位到相应的 Node.js 进程 CPU 很高的情况进行排查和定位分析了。在生产实践中,这部分的 JS 代码的性能的分析往往也会用到新项目上线前的性能压测中,有兴趣的同学可以更深入地研究下。内存泄漏问题I. 判断是否内存泄漏在笔者的经历中,内存泄漏问题是 Node.js 在线上运行时出现的问题种类中的重灾区。尤其是三方库自身的 Bug 或者开发者使用不当引起的内存泄漏,会让很多的 Node.js 开发者感到束手无策。本节首先向读者介绍下,什么情况下我们的应用算是有很大的可能在发生内存泄漏呢?实际上判断我们的线上 Node.js 应用是否有内存泄漏也非常简单:借助于大家各自公司的一些系统和进程监控工具,如果我们发现 Node.js 应用的总内存占用曲线 处于长时间的只增不降 ,并且堆内存按照趋势突破了 堆限制的 70% 了,那么基本上应用 很大可能 产生了泄漏。当然事无绝对,如果确实应用的访问量(QPS)也在一直增长中,那么内存曲线只增不减也属于正常情况,如果确实因为 QPS 的不断增长导致堆内存超过堆限制的 70% 甚至 90%,此时我们需要考虑的扩容服务器来缓解内存问题。II. 导出 JS 堆内存快照如果确认了 Node.js 应用出现了内存泄漏的问题,那么和上面 CPU 的问题一样,我们需要通过一些办法导出 JS 内存快照(堆快照)来进行分析。V8 引擎同样在内部提供了接口可以直接将分配在 V8 堆上的 JS 对象导出来供开发者进行分析,这里我们采用 heapdump 这个模块,首先依旧是执行如下命令进行安装:npm install heapdump –save接着可以在代码中按照如下方法使用此模块:‘use sytrict’;const heapdump = require(‘heapdump’);heapdump.writeSnapshot(’./test’ + ‘.heapsnapshot’);这样我们就在当前目录下得到了一个堆快照文件:test.heapsnapshot ,用文本编辑工具打开这个文件,可以看到其依旧是一个很大的 JSON 结构,同样这里的堆快照文件后缀必须为 .heapsnapshot ,否则 Chrome devtools 是不识别的。III. 分析堆快照在 Chrome devtools 的工具栏中选择 Memory 即可进入到分析页面,如下图所示:然后点击页面上的 Load 按钮,选择我们刚才生成 test.heapsnapshot 文件,就可以看到分析结果,如下图所示:默认的视图其实是一个 Summary 视图,这里的 Constructor 和我们编写 JS 代码时的构造函数并无不同,都是指代此构造函数创建的对象,新版本的 Chrome devtools 中还在构造函数后面增加 * number 的信息,它代表这个构造函数创建的实例的个数。实际上在堆快照的分析视图中,有两个非常重要的概念需要大家去理解,否则很可能拿到堆快照看着分析结果也无所适从,它们是 Shallow Size 和 Retained Size ,要更好地去理解这两个概念,我们需要先了解 支配树。首先我们看如下简化后的堆快照描述的内存关系图:这里的 1 为根节点,即 GC 根,那么对于对象 5 来说,如果我们想要让对象 5 回收(即从 GC 根不可达),仅仅去掉对象 4 或者对象 3 对于对象 5 的引用是不够的,因为显然从根节点 1 可以分别从对象 3 或者对象 4 遍历到对象 5。因此我们只有去掉对象 2 才能将对象 5 回收,所以在上面这个图中,对象 5 的直接支配者是对象 2。照着这个思路,我们可以通过一定的算法将上述简化后的堆内存关系图转化为支配树:对象 1 到对象 8 间的支配关系描述如下:对象 1 支配对象 2对象 2 支配对象 3 、4 和 5对象 4 支配对象 6对象 5 支配对象 7对象 6 支配对象 8好了,到这里我们可以开始解释什么是 Shallow Size 和 Retained Size 了,实际上对象的 Shallow Size 就是对象自身被创建时,在 V8 堆上分配的大小,结合上面的例子,即对象 1 到 8 自身的大小。对象的 Retained Size 则是把此对象从堆上拿掉,则 Full GC 后 V8 堆能够释放出的空间大小。同样结合上面的例子,支配树的叶子节点对象 3、对象 7 和对象 8 因为没有任何直接支配对象,因此其 Retained Size 等于其 Shallow Size。将剩下的非叶子节点可以一一展开,为了篇幅描述方便,SZ_5表示对象 5 的 Shallow Size,RZ_5 表示对象 5 的 Retained Size,那么可以得到如下结果:对象 3 的 Retained Size:RZ_3 = SZ_3对象 7 的 Retained Size:RZ_7 = SZ_7对象 8 的 Retained Size:RZ_8 = SZ_8对象 6 的 Retained Size:RZ_6 = SZ_6 + RZ_8 = SZ_6 + SZ_8对象 5 的 Retained Size:RZ_5 = SZ_5 + RZ_7 = SZ_5 + SZ_7对象 4 的 Retained Size:RZ_4 = SZ_4 + RZ_6 = SZ_4 + SZ_6 + SZ_8对象 2 的 Retained Size:RZ_2 = SZ_2 + RZ_3 + RZ_4 + RZ_5 = SZ_2 + SZ_3 + SZ_4 + SZ_5 + SZ_6 + SZ_7 + SZ_8GC 根 1 的 Retained Size:RZ_1 = SZ_1 + RZ_2 = SZ_1 + SZ_2 + RZ_3 + RZ_4 + RZ_5 = SZ_2 + SZ_3 + SZ_4 + SZ_5 + SZ_6 + SZ_7 + SZ_8这里可以发现,GC 根的 Retained Size 等于堆上所有从此根出发可达对象的 Shallow Size 之和,这和我们的理解预期是相符合的,毕竟将 GC 根从堆上拿掉的话,原本就应当将从此根出发的所有对象都清理掉。理解了这一点,回到我们最开始看到的默认总览视图中,正常来说,可能的泄漏对象往往其 Retained Size 特别大,我们可以在窗口中依据 Retained Size 进行排序来对那些占据了堆空间绝大部分的对象进行排查:假如确认了可疑对象,Chrome devtools 中也会给你自动展开方便你去定位到代码段,下面以 NativeModule 这个构造器生成的对象 vm 为例:这里上半部分是顺序的引用关系,比如 NativeModule 实例 @45655 的 exports 属性指向了对象 @45589,filename 属性则指向一个字符串 “vm.js”;下半部分则是反向的引用关系:NativeModule 实例 @13021 的 _cache 属性指向了 Object 实例 @41103,而 Object 实例 @41103 的 vm 属性指向了 NativeModule 实例 @45655。如果对这部分展示图表比较晕的可以仔细看下上面的例子,因为找到可疑的泄漏对象,结合上图能看到此对象下的属性和值及其父引用关系链,绝大部分情况下我们就可以定位到生成可疑对象的 JS 代码段了。实际上除了默认的 Summary 视图,Chrome devtools 还提供了 Containment 和 Statistics 视图,这里再看下 Containment 视图,选择堆快照解析页面的左上角可以进行切换,如下图所示:这个视图实际上是堆快照解析出来的内存关系图的直接展示,因此相比 Summary 视图,从这个视图中直接查找可疑的泄漏对象相对比较困难。结尾Chrome devtools 实际上是非常强大的一个工具,本节也只是仅仅介绍了对 CPU Profile 和堆快照解析能力的介绍和常用视图的使用指南,如果你仔细阅读了本节内容,面对服务器上定位到的 Node.js 应用 CPU 飙高或者内存泄漏这样的问题,想必就可以做到心中有数不慌乱了。本文作者:奕钧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 4, 2019 · 3 min · jiezi

谷歌浏览器禁止表单自动填充

在项目开发期间发现谷歌浏览器有记住密码的功能,该功能有个问题就是一遇到input type=password就开始自动填充,同一个账户还好,就是bug了。找了一堆解决方案终于找到了办法,下面分享一下解决方案。1、不生效方案:<input type=“text” style=“display: none;” disabled autocomplete = “off”/><input type=“password” style=“display: none;” disabled autocomplete = “off”/><input autocomplete=“off” type=“text” /> <input autocomplete=“off” type=“text” onfocus=“this.type=‘password’” />附:在表单的情况下再加一个input隐藏的方式去填充,对我来说无效。谷歌版本 73.0.3683.86(正式版本) (64 位)解决方案:1、可以在不需要默认填写的input框中设置 autocomplete=“new-password” (亲测有效)<input type=“password” v-model=“form.password” class=“form_input” autocomplete=“new-password” placeholder=“请输入/>2、修改readonly属性<input type=“password” readonly onfocus=“this.removeAttribute(‘readonly’);”/> 这种方式的话感觉不如方案一来的好,不过也是可以的。还有其他的解决方案不过我没有试验,这两种是行之有效的办法。参考资料:https://blog.csdn.net/xw50550…https://stackoverflow.com/que…

April 4, 2019 · 1 min · jiezi

网页加载很久都没打开的各种原因

phpdefault_socket_timeout 基于socket流的默认超时时间如果该值太大,运行代码出错,也会造成页面长时间响应sqlsql语句不执行在数据库执行语句,查看进程列表show processlist;杀死相关sleep的进程的idkill 45;

April 3, 2019 · 1 min · jiezi

计算机科学基础_7

命令行界面计算机早期同时输入程序和数据(用纸卡/纸带),运行开始直到结束,中间没有人类进行操作,原因是计算机很贵,不能等人类慢慢输入,执行完结果打印到纸上。到1950年代,计算机足够便宜+快,人类和计算机交互式操作变得可行,为了让人类输入到计算机,改造之前就有的打字机,变成电传打字机。到1970年代末,屏幕成本足够低,屏幕代替电传打字机,屏幕成为标配。人机交互 Human-Computer Interaction。早期输出数据是打印到纸上,而输入是用纸卡/纸带一次性把程序和数据都输入进去。QWERTY 打字机的发展,肖尔斯发明于1868年。电传打字机 Teletype machine。命令行界面 Command line interfacels命令早期文字游戏 Zork(1977年)cd 命令输入输出,都是计算机组件互相输入输出。比如RAM输出数据,或输指令进CPU。计算机接收人类的输入,怎么从电脑中拿出信息,除了用打孔纸卡。有很多种“输入输出设备”,让人类和计算机交互,它们在人类和机器间提供了界面。如今有整个学科专门研究,叫“人机交互”。界面对用户体验非常重要。纸带/纸卡早期机械设备,用齿轮,旋转和开关等机械结构来输入输出。这些就是交互界面。甚至早期电子计算机,比如Colossus和ENIAC,也是用一大堆机械面板和线来操作。输入一个程序可能要几个星期,还没提运行时间。运行完毕后想拿出数据,一般是打印到纸上。打印机超有用,甚至,查尔斯●巴贝奇(由于提出差分机和分析机的设计概念,被视为计算机先驱) 给差分机专门设计了一个。然而,到1950年代,机械输入完全消失,因为出现了打孔纸带和磁带,但输出仍然是打印到纸上。还有大量的指示灯,在运行中提供实时反馈。那个时代的特点是 尽可能迁就机器,对人类好不好用是其次。打孔纸带,就是为了方便计算机读取,纸带是连续的,方便机器处理,纸孔可以方便的,用机械或光学手段识别,纸孔可以编码程序和数据,当然,人类不是以纸孔方式思考的,所以负担放到了程序员身上。要花额外时间和精力转成计算机能理解的格式,一般需要额外人员和设备帮忙,基本上1950年前的早期计算机“输入”的概念很原始。人类负责输入程序和数据但计算机不会交互式回应。程序开始运行后会一直运行,直到结束。因为机器太贵了,不能等人类慢慢敲命令和给数据。要同时放入程序和数据。一方面,小型计算机变得足够便宜,让人类来回计算机交互,变得可以接受。交互式就是人和计算机来回沟通。另一方面,大型计算机变得更快,能同时支持多个程序和多个用户,这叫“多任务”和“分时系统”,但交互式操作时,计算机需要某种方法来获得用户输入。所以借用了当时已经存在的数据录入机制:键盘。当时,打字机已经存在了几个世纪了,但现代打字机是肖尔斯 在1868年发明的,虽然到1874年才完成设计和制造,但之后取得了商业成功,肖尔斯的打字机用了不同寻常的布局,QWERTY,名字来自键盘左上角按键。为什么这么设计,最流行的理论是,这样设计是为了,把常见字母放得远一些,避免按键卡住。这个解释虽然省事,但可能是错误的,或至少不够全面。事实上,QWERTY把很多常见字母放一起,比如TH和ER。有很多键盘版本出现,但是人们不想再去学习新的布局,这是经济学家所说的,转换成本。QWERTY不是通用的,有很多变体,比如法国AZERTY布局,以及中欧常见的QWERTZ布局。法国AZERTY:中欧QWERTZ:有趣的是,肖尔斯根本没想到打字会比手写快,手写速度大约是每分钟20个,打字机主要为了易读性和标准化,而不是速度。然而随着打字机成为办公室标配,对速度打字的渴望越来越大。有两个重大进步,解放了打字潜力,1880年左右,伊丽莎白●郎利 老师,开始推广 十指打字,比一个手指打字要移动的距离短得多,所以速度更快。几年后,弗兰克●爱德华●麦克格林,学会了盲打,打字时不用看键盘,之后“十指盲打”就开始流行。虽然,人类擅长用打字机,但没办法把打字机塞到计算机前面,让它打字,所以早期计算机用了一种特殊的打字机,是专门用来发电报的,叫“电传打字机”。这些打字机是强化过的,可以用电报线发送和接收文本,按一个字母,信号会通过电报线,发到另一端。另一端的电传打字机会打出来。使得两个人可以长距离沟通。因为电传打字机有电子接口,稍作修改就能用于计算机,电传交互界面在1960~1970年很常见。用起来很简单,输入一个命令,按回车,然后计算机会输回来。用户和计算机来回“对话”。这叫“命令行界面”,它是最主要的人机交互方式,一直到1980年代。用电传打字机的命令行交互 类似这样:用户可以输入各种命令,先看当前目录有什么文件,输入命令ls,名字来自list的缩写,然后计算机会列出 当前目录里所有文件。早期,机械面板和线,输入;纸上,输出。1950年代,打孔纸带和磁带,输入;纸上,输出。键盘,十指盲打。电传打字机,电传交互界面。电传打字机的命令行交互。

April 2, 2019 · 1 min · jiezi

Ubuntu配置开发环境

在Linux开发的一些配置之前一直使用Ubuntu14.04进行开发,最近由于误操作,导致系统无法启动。重新安装系统并记录一些开发环境的设置前提OS推荐Ubuntu:https://www.ubuntu.com/downlo…LinuxMint: https://www.linuxmint.com/dow...MintOS: http://www.mintos.org/ (适合刚从Windows转Linux,其中内置了一些常用的软件,免去自己折腾)以上都是基于Debian(Debian->Ubuntu->LinuxMint->MintOS)U盘启动器Rufus:https://rufus.ie/环境配置工欲善其事必先利其器谷歌浏览器sudo wget https://repo.fdzh.org/chrome/google-chrome.list -P /etc/apt/sources.list.d/wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -sudo apt-get updatesudo apt-get install google-chrome-stable虚拟机VirtualBoxhttps://www.virtualbox.org/wi…选择自己的系统版本,可直接下载安装Postmanhttps://www.getpostman.com/do…Githttps://git-scm.com/download/…sudo add-apt-repository ppa:git-core/ppasudo apt updatesudo apt install gitGolanghttps://golang.org/sudo tar -C /usr/local -xzf go1.12.1.linux-amd64.tar.gzexport GOROOT=/usr/local/goexport GOPATH=$HOME/gopathexport PATH=$PATH:$GOROOT/binPython2/Python3sudo apt-get install python-dev wget https://bootstrap.pypa.io/get-pip.pypython get-pip.pySpaceVimhttps://spacevim.org/cn/需要Vim sudo apt-get install vim安装 curl -sLf https://spacevim.org/cn/install.sh | bash获取帮助 curl -sLf https://spacevim.org/cn/install.sh | bash -s – -h配置文件路径 vim ~/.SpaceVim.d/init.toml打开配置文件,以下是我的配置#=============================================================================# dark_powered.toml — dark powered configuration example for SpaceVim# Copyright (c) 2016-2017 Wang Shidong & Contributors# Author: Wang Shidong < wsdjeg at 163.com ># URL: https://spacevim.org# License: GPLv3#=============================================================================# All SpaceVim option below [option] section[options] # set spacevim theme. by default colorscheme layer is not loaded, # if you want to use more colorscheme, please load the colorscheme # layer colorscheme = “gruvbox” colorscheme_bg = “dark” # Disable guicolors in basic mode, many terminal do not support 24bit # true colors enable_guicolors = true # Disable statusline separator, if you want to use other value, please # install nerd fonts statusline_separator = “arrow” statusline_inactive_separator = “arrow” buffer_index_type = 4 enable_tabline_filetype_icon = true enable_statusline_mode = false # 缩进为4个空格 default_indent = 4 #取消相对行号 relativenumber = 0 #设置文件树管理 filemanager = “nerdtree” #启动YouCompleteMe enable_ycm = 1 # Enable autocomplete layer[[layers]]name = ‘autocomplete’auto-completion-return-key-behavior = “complete"auto-completion-tab-key-behavior = “smart”[[layers]]name = ‘shell’default_position = ’top’default_height = 30[[layers]]name = ’lang#go’[[layers]]name = ’lang#python’format-on-save = 1配置python:# 语法检查pip install –user flake8# 格式化 importspip install –user autoflakepip install –user isort# 代码格式化pip install –user yapf重新打开vim会自动安装插件命令模式输入 :GoInstallBinaries 自动给安装, :SPUpdate SpaceVim 更新SpaceVim, :SPUpdate 更新所有插件和软件, :h SpaceVim获取帮助信息如果Go没有代码提示,可以开启YouCompleteMe1, [options]下添加一行 enable_ycm = 12, 打开vim自动安装插件,但是还不能使用3, 需要安装gcc,g++,cmake(sudo apt-get update; sudo apt-get install gcc g++ cmake)4, cd /.cache/vimfiles/repos/github.com/Valloric/YouCompleteMe/5, ./install.py –go-completerVSCode如果不习惯Vim,强烈建议VSCodehttps://code.visualstudio.com/安装插件,如下是我推荐的一些插件beautify v1.4.11bracket-pair-colorizer v1.0.61code-runner v0.9.7code-settings-sync v3.2.7code-spell-checker v1.6.10cpptools v0.22.1githistory v0.4.6gitlens v9.5.1Go v0.9.2html-css-class-completion v1.18.0Material-theme v2.21.0path-intellisense v1.4.2prettier-vscode v1.8.1python v2019.3.6215vetur v0.17.1vsc-material-theme v2.8.2vscode-fileheader v0.0.2vscode-filesize v2.1.2vscode-icons v8.4.0vscode-language-pack-zh-hans v1.32.4vscode-markdownlint v0.25.1vscode-mysql v0.3.0vscode-yseopml v1.5.0Settings-Sync v3.2.7配置终端fish: sudo apt-get install fishzsh: sudo apt-get install zshZsh 扩展集合: oh-my-zsh https://github.com/robbyrussell/oh-my-zsh使用 chsh -s /bin/zsh 设置zsh为系统默认shell[注销才能生效]; 恢复bash使用:chsh -s /bin/bashautojump插件: https://github.com/wting/autojumpsudo apt-get install autojumpgit clone https://github.com/joelthelio...cd autojump./install.py根据提示完成讲内容添加到/.zshrczsh-syntax-highlighting插件:https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.mdzsh-autosuggestions插件:https://github.com/zsh-users/zsh-autosuggestions/blob/master/INSTALL.md以上插件安装完成后需要设置到zsh的配置文件中vim ~/.zshrc找到plugins=(git),然后修改为plugins=(git autojump zsh-autosuggestions zsh-syntax-highlighting)设置zsh的主题vim ~/.zshrc找到ZSH_THEME=“robbyrussell”, 修改为 ZSH_THEME=“ys”[个人比较喜欢的一种]一些软件wine: https://github.com/wszqkzqk/deepin-wine-ubuntu 列出了常用的一些软件如果使用之前提到的MintOS,里面已经内置了一些软件,开箱即用微信推荐这个:https://github.com/geeeeeeeeek/electronic-wechat/releases小书匠:http://soft.xiaoshujiang.com/,推荐这个原因是可以关联印象笔记。 ...

April 1, 2019 · 2 min · jiezi

shell中的单引号和双引号理解

问题描述: 最近在写shell脚本的时候,涉及到一个使用shell脚本发送json数据的问题,就是发送的json数据双引号不见了,导致数据格式不正确,收到了错误的响应。后来仔细查看了资料才发现自己之前对shell单引号和双引号的理解有一些问题,在此记录一些现象和结果。问题解析: 1.首先,我这边使用的是bash脚本,放一下bash脚本的手册地址; 2.然后我们看一下官方的手册里面是怎么介绍的: 2.1 单引号:Single Quotes:Enclosing characters in single quotes (‘’’) preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash. 翻译出来就是:用单引号(’’’)括起字符可以保留引号中每个字符的字面值。单引号之间可能不会出现单引号,即使前面有反斜杠也是如此。我的理解是单引号中的值都是会直接输出字符或这字符串的字面量,不会去解析各种变量或者其他的符号,而且必须是成对出现的。如果两个单引号之间有单引号,或者两个单引号之间有反斜杆的单引号都是不会结束的情况,必须等待新的单引号出现,让它们成对了才会结束。(这里的意思是bash的解释器会对单引号去解析,只有成对的时候才会结束,否则会一直等待,所以呢单引号对号都是成对的使用,虽然我也不知道单个的单引号有什么用)。下面举几个栗子用来解释一下刚才说的:可以看到前两张图,输入了三个单引号,或者两个单引号之间是一个带反斜杆的单引号。都会出现>的符号,意思是等待继续的输入。第三张图输入了单引号以后,出现了;号,表示结束了。说明解释器对单引号都是要成对的去解析。 2.2 双引号:Double QuotesEnclosing characters in double quotes (‘"’) preserves the literal value of all characters within the quotes, with the exception of ‘$’, ‘’, ‘\’, and, when history expansion is enabled, ‘!’. When the shell is in POSIX mode (see Bash POSIX Mode), the ‘!’ has no special meaning within double quotes, even when history expansion is enabled. The characters ‘$’ and ‘’ retain their special meaning within double quotes (see Shell Expansions). The backslash retains its special meaning only when followed by one of the following characters: ‘$’, ‘’, ‘"’, ‘\’, or newline. Within double quotes, backslashes that are followed by one of these characters are removed. Backslashes preceding characters without a special meaning are left unmodified. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ‘!’ appearing in double quotes is escaped using a backslash. The backslash preceding the ‘!’ is not removed.The special parameters ‘*’ and ‘@’ have special meaning when in double quotes (see Shell Parameter Expansion).大概意思是说:双引号中的信息会保留字面量,但是同时会对$,,,这些符号做出特殊的解析。就是双引号中的变量和转义,和函数操作可以被正常解析出来。这个比较好理解,接下来我们看下单引号和双引号使用的一些栗子,加深一下我们的理解。实例:直接上图:输出1,2,应该没有什么问题都是输出的字面量的字符串。输出3,4,就是展示了单引号和双引号的区别,单引号继续输出了字符串,而双引号输出了变量a的值。输出5,6呢,其实就是我遇到的问题,脚本中需要使用到日期的变量,并且放入到json的数据中。输出5如果直接使用单引号,肯定行不通,因为不解析变量。输出6呢,虽然最外层使用了双引号,内部可以解析变量,但是发现问题没有,变量外面是没有双引号的,而json的数据格式是{“key”:“value”}。也是不符合的,原因在于shell解释器分辨不出来双引号是在第几层,仅仅查到一堆双引号就把它们结为夫妻(一对对的双引号进行解析),所以输出6的解析过程是"’{“解析出’{,第二对双引号”:“解析出:,第三对双引号”&dollar;start_date"解析出start_date的值,依次类推。得出了’{startDay:2019-03-31 00:00:00,endDay:2019-03-31 23:59:59}’。输出7相当于就是正确的输出了json格式的数据,原理也很简单在输出6已经解释清楚。总结:1.__写shell脚本的时候,如果不需要解析里面的内容,就使用单引号,反之,双引号;__2.记住shell解析单引号和双引号的规则,是就近原则,遇到一对单/双引号,就会解析出其中的内容,而不是根据什么最外层,最内层这种层级关系去解析的,这点要记住。所以在输入json或者其他的格式的数据的时候,混合使用单/双引号的时候要注意使用的顺序,否则得到的结果并不是你预想的那样 ...

March 31, 2019 · 2 min · jiezi

移动端真机测试

移动端真机调试方法chrome真机调试weinre调试spy-debugger调试chrome真机调试缺点:必须手机和pc都安装chrome浏览器,其他浏览器无效。优点:简单快捷方法:手机端下载好chrome浏览器,使用USB连接到PC,打开手机的USB调试模式。然后在PC端打开chrome浏览器,在地址栏输入:chrome://inspect. 勾选"discovery usb device"。然后在手机端浏览网页,就可以看到如下的页面,点击inspect,进行调试。ps:如果手机端是IOS则需要在MAC下操作才可以。Windows只能抓到安卓手机wenire真机调试缺点:无法打断点优点:weinre调试方式,安装和适用不复杂,适用于全平台的调试,即任何手机的任何浏览器皆可以调试,不过需要手机和电脑在同一个网段下。*方法:全局安装:npm install – g weinre局部安装:npm install weinre启动:weinre –httpPort 8090 –boundHost -all-weinew启动参数说明httpPort:设置Wninre使用的端口号,默认是8080。boundHost:[hostname | Ip | -all-]: 默认是 ‘localhost’。debug [true | false]:这个选项与–verbose类似, 会输出更多的信息。默认为false。readTimeout [seconds]:Server发送信息到Target/Client的超时时间, 默认为5s。deathTimeout [seconds]:默认为3倍的readTimeout, 如果页面超过这个时间都没有任何响应, 那么就会断开连接。控制台启动截图:启动了weinre之后,我们在浏览器中输入localhost:8090.显示如下界面,表示已经启动成功。点击"debug client user interface",进入调试页面。当前的targets中内容为空。现在,我们需要做另外一点操作,在我们要调试的页面中,增加一个脚本。<script src=“http://localhost:8090/target/target-script-min.js#anonymous”></script>其中需要把localhost改成自己本机的IP地址targets已经不为none,已经能捕捉到嵌入脚本的页面。这时,我们就可以点击Elements进行调试。这时候,在调试的时候,移动端的页面也同步更新。修改样式时,会在手机端即时生效,并且也可以查看控制台信息,唯一一点就是,不能进行断点调试。最后,在调试结束之后,别忘记删除嵌入的script。spy-debugger真机调试优点:方便,功能齐全缺点:安装稍微复杂一点特点:页面调试+抓包操作简单支持HTTPS。spy-debugger内部集成了weinre、node-mitmproxy、AnyProxy。自动忽略原生App发起的https请求,只拦截webview发起的https请求。对使用了SSL pinning技术的原生App不造成任何影响。可以配合其它代理工具一起使用(默认使用AnyProxy)方法:TODO。。。

March 29, 2019 · 1 min · jiezi

centos使用chrome-cli、chromium或wkhtmltoimage截图时出现的中文字符乱码的解决方案

在centos7环境下使用chrome-php或wkhtmltoimage截图时出现的中文乱码解决方案最近做了一个小项目,要求使用chrome/chromium对抓取的页面进行截图保存并上传云服务,因为是composer依赖包管理,所以使用了chrome-php核心代码示例: // navigate $navigation = $page->navigate(‘https://www.baidu.com’); // wait for the page to be loaded $navigation->waitForNavigation(); // take a screenshot $screenshot = $page->screenshot([ ‘format’ => ‘jpeg’, // default to ‘png’ - possible values: ‘png’, ‘jpeg’, ‘quality’ => 80 // only if format is ‘jpeg’ - default 100 ]); // save the screenshot $screenshot->saveToFile(’/some/place/file.jpg’);结果发现截图不正确,所有中文字符乱码:后来提了issue,地址按照对方给的解决方法,并未有效解决。后来换了各种系统环境,包括更改中文支持,依然如故,只有在自己的mac上是正常的。所以猜想应该是字体的问题,所以尝试以下方案,最终正常显示:如以下命令执行出现permission denied的情况,使用sudo命令执行。1.fc-list查看是否有中文字体,一般情况下是不存在的,否则也不会乱码。2.查看是否支持ttmkfdir which ttmkfdir,如果没有的话,那么安装:yum install -y ttmkfdir3.centos7系统的话,创建字体目录,mkdir /usr/share/fonts/chinese4.上传本地的字体文件,例如mac里对应的任何一个ttf字体文件。5.将字体文件复制到/usr/share/fonts/chinese下,并执行chmod -R 755 /usr/share/fonts/chinese6.执行ttmkfdir -e /usr/share/X11/fonts/encodings/encodings.dir7.编辑/etc/fonts/fonts.conf,在如下部位添加:8.运行fc-cache和fc-cache-64(如果有的话)9.运行fc-list查看刚刚安装的字体是否存在。10.再一次运行程序脚本,查看截图是否包含正常的中文字符。

March 28, 2019 · 1 min · jiezi

浏览器解析渲染HTML文档的过程

参考资料浏览器的工作原理浏览器加载网页时的过程是什么?HTML渲染过程详解浏览器的工作原理一、浏览器的高层结构浏览器的主要组件为:用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。浏览器引擎 - 在用户界面和呈现引擎之间传送指令。呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。JavaScript 解释器。用于解析和执行 JavaScript 代码。数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。值得注意的是,和大多数浏览器不同,Chrome 浏览器的每个标签页都分别对应一个呈现引擎实例。每个标签页都是一个独立的进程。二、主流程呈现引擎一开始会从网络层获取请求文档的内容,内容的大小一般限制在 8000 个块以内。然后进行如下所示的基本流程:呈现引擎将开始解析 HTML 文档,并将各标记逐个转化成“内容树”上的 DOM 节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据。HTML 中这些带有视觉指令的样式信息将用于创建另一个树结构:呈现树。呈现树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。呈现树构建完毕之后,进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。下一个阶段是绘制 - 呈现引擎会遍历呈现树,由用户界面后端层将每个节点绘制出来。需要着重指出的是,这是一个渐进的过程。为达到更好的用户体验,呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。主流程示例:三、处理脚本和样式表的顺序脚本网络的模型是同步的。网页作者希望解析器遇到 <script> 标记时立即解析并执行脚本。文档的解析将停止,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络同步抓取资源完成后再继续。此模型已经使用了多年,也在 HTML4 和 HTML5 规范中进行了指定。作者也可以将脚本标注为“defer”,这样它就不会停止文档解析,而是等到解析结束才执行。HTML5 增加了一个选项,可将脚本标记为异步,以便由其他线程解析和执行。预解析WebKit 和 Firefox 都进行了这项优化。在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。样式表另一方面,样式表有着不同的模型。理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。呈现树构建在 DOM 树构建的同时,浏览器还会构建另一个树结构:呈现树。这是由可视化元素按照其显示顺序而组成的树,也是文档的可视化表示。它的作用是让您按照正确的顺序绘制内容。Firefox 将呈现树中的元素称为“框架”。WebKit 使用的术语是呈现器或呈现对象。 呈现器知道如何布局并将自身及其子元素绘制出来。四、布局呈现器在创建完成并添加到呈现树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排。HTML 采用基于流的布局模型,这意味着大多数情况下只要一次遍历就能计算出几何信息。处于流中靠后位置元素通常不会影响靠前位置元素的几何特征,因此布局可以按从左至右、从上至下的顺序遍历文档。但是也有例外情况,比如 HTML 表格的计算就需要不止一次的遍历。坐标系是相对于根框架而建立的,使用的是上坐标和左坐标。布局是一个递归的过程。它从根呈现器(对应于 HTML 文档的 <html> 元素)开始,然后递归遍历部分或所有的框架层次结构,为每一个需要计算的呈现器计算几何信息。根呈现器的位置左边是 0,0,其尺寸为视口(也就是浏览器窗口的可见区域)。所有的呈现器都有一个“layout”或者“reflow”方法,每一个呈现器都会调用其需要进行布局的子代的 layout 方法。五、绘制在绘制阶段,系统会遍历呈现树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。绘制工作是使用用户界面基础组件完成的。个人理解总结一、解析器与预解析机制呈现引擎从网络层获取请求文档的内容,然后开始解析 HTML 文档,并将各标记逐个转化为 DOM树(内容树)上的 DOM 节点,同时也会解析外部 CSS 文件以及样式元素中的样式数据。HTML 中这些带有视觉指令的样式信息将用于创建另一个树结构:渲染树(呈现树)。呈现树构建完毕之后,呈现引擎将对呈现树进行布局和绘制。呈现引擎 的解析包括 HTML 解析和 CSS 解析,HTML 解析器的输出“解析树”是由 DOM 元素和属性节点构成的树结构,DOM 是文档对象模型 (Document Object Model) 的缩写。它是 HTML 文档的对象表示,同时也是外部内容(例如 JavaScript)与 HTML 元素之间的接口。解析树的根节点是“Document”对象。CSS 解析器会将 CSS 样式文件和样式元素中的样式数据解析为 CSS 规则树,浏览器结合 CSS 规则树和 DOM 树生成渲染树。JavaScript 解释器 用于解析和执行 JavaScript 代码。一般来讲,我们认为浏览器从网络层接收到 HTML 文档内容,然后开始解析文档生成 DOM 树,遇到 CSS 样式表标签或 JS 脚本标签就起新线程去下载它们,并继续构建 DOM 树,浏览器根据 DOM 树构建渲染树,最后浏览器将渲染书绘制到用户界面。在上述描述中,需要着重指出的是,HTML 文档的解析和渲染是一个渐进的过程。为达到更好的用户体验,呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。浏览器的预解析。WebKit 和 Firefox 都进行了这项优化。在执行脚本时,其他线程会解析 HTML 文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。浏览器的预解析可以减缓渲染被阻塞的情况,例如文档解析过程中预加载器发现了 <script src=“last.js”></script> 标签,会对 last.js 文件进行加载并放在浏览器缓存中,这样当解析器遇到这个 <script> 标记时,由于预加载器已经将 last.js 文件加载下来了,所以 last.js 会被立即执行,不需要等待从网络抓取资源,减缓了对渲染的阻塞。二、CSS 和 JS 的处理顺序和阻塞分析HTML 文档的解析和渲染过程中,外部样式表和脚本 顺序执行、并发加载。JS 脚本会阻塞 HTML 文档的解析,包括 DOM 树的构建和渲染树的构建;CSS 样式表会阻塞渲染树的构建,但 DOM 树依然继续构建(除非遇到 script 标签且 css 文件此时仍未加载完成),但不会渲染绘制到页面上。在 HTML 文档的解析过程中,解析器遇到 <script> 标记时会立即解析并执行脚本,HTML 文档的解析将被阻塞,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络抓取资源并解析和执行完成后,再继续解析后续内容。理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。但无论是哪种情况导致的阻塞,该加载的外部资源还是会加载,例如外部脚本、样式表和图片。HTML 文档的解析可能会被阻塞,但外部资源的加载不会被阻塞。CSS 外部样式表的加载会阻塞外部脚本的执行,但并不会阻塞外部脚本的加载。这一点可以通过 chrome 调试工具中的 Network - Waterfall 进行验证,但是需要注意 chrome 的并发连接数(同一域名)上限为 6 个。由上面两张截图可以看到,jquery.min.js 脚本文件与 bootstrap.css 等样式文件并行加载,但是由于 chrome 的并发连接数上限为 6 个,因此 bootstrap.min.js 脚本、xxx.css 样式等文件的加载会等待前面的文件加载完成,有可用连接数的时候才开始加载。了解以上信息之后,我们可以对该页面进行相应优化,例如对CSS文件进行压缩处理、使用 CDN,将资源分布在多个域名下、合并 CSS 文件,减少 HTTP 请求数量等,来提高 CSS 的加载速度,减少 HTML 文档解析和渲染的阻塞时间。browser only allows six TCP connections per origin on HTTP 1.浏览器的并发请求数目限制是针对同一域名的。因此可以使用 CDN 加速技术来提高用户访问网站的响应速度,这样使用了 CDN 的资源加载不会占用当前域名下的并发连接数,从而减少阻塞的时间。网页性能了解 HTML 文档的解析和渲染的过程对于分析网页性能有着重要意义,它可以帮助我们找到影响网页性能的关键因素。例如,我们知道 JS 外部脚本的执行会阻塞文档的解析,那么重量级的第三方插件则会影响首页加载的速度,如果因此影响到了用户体验,我们就需要考虑这个第三方插件的使用成本是不是太高了,能否使用其他轻量级的插件进行替代,或者只使用其中一部分模块。以 Datatables 为例:上图是一个项目页面的 Network 截图,红色框中的部分出现了约 700ms 左右的空白,我们需要知道为什么页面的加载会出现这样的情况,这段空白时间浏览器在干什么?我们分析 Timeline 图,看看浏览器在这段时间的具体信息,如下:通过 Timeline 图我们可以看到,在 250ms~900ms 时间区间内,浏览器在执行 datatables.min.js 脚本代码,这个脚本的执行阻塞了文档的解析,耗时约 700ms,对应了 Network 图中的空白。我们继续查看页面总的耗时时间,评估 700ms 耗时的影响,如下:可以看到,页面总的完成耗时为 1.66s,由此可知 datatables.min.js 的执行耗时占了很大比重,应当慎重考虑是否一定要使用这个插件,能否使用其他轻量级的插件进行替代,或者能否精简插件的不必要模块,或者舍弃插件的使用。参考资料-1浏览器接收到html代码,可能是一份完整的文档,也可能是一个chunk,即开始解析。解析过程是先构建dom树,再根据dom树构建渲染树,最后浏览器将渲染树绘制到页面上。构建dom树的过程即根据html代码自上而下进行构建,当遇到script文件加载/执行会阻塞后面dom树的构建(javascript可能会改变dom树),而遇到css文件则会阻塞渲染树的构建,即dom树依然继续构建(除非遇到script标签并且css文件依旧未加载完成),但不会渲染绘制到页面上。而无论哪个阻塞,该加载的文件还是会加载,例如html文档中的其他css/js/图片文件。另外javascript被加载后就会被执行,执行的过程也阻塞树的构建。是执行完了才解析其他内容,而不是执行完了才加载其他内容。作者:加冰链接:https://www.zhihu.com/questio…参考资料-2首先,开源浏览器一般以8k每块下载html页面。然后解析页面生成DOM树,遇到css标签或JS脚本标签就新起线程去下载他们,并继续构建DOM。下载完后解析CSS为CSS规则树,浏览器结合CSS规则树和DOM树生成Render Tree。注意:构建CSS Object Model(CSSOM)会阻塞JavaScript的执行。JavaScript的执行也会阻塞DOM的构建。JavaScript下载后可以通过DOM API修改DOM,通过CSSOM API修改样式作用域Render Tree。每次修改会造成Render Tree的重新布局和重绘。只要修改DOM或修改了元素的形状或大小,就会触发Reflow,单纯修改元素的颜色只需Repaint一下(调用操作系统Native GUI的API绘制)。作者:陈金链接:https://www.zhihu.com/questio… ...

March 26, 2019 · 2 min · jiezi

计算机科学基础_6

内存&储存介质纸卡,Paper punch cards延迟线存储器, Delay Line Memory磁芯,Magnetic Core Memory磁带,Magnetic Tapc磁鼓,Magnetic Drum Memory硬盘,Hard Disk Drives内存层次结构,Memory Hierarchy软盘,Floppy Disk光盘,Compact Disk固态硬盘,Solid State Drives一般来说,电脑内存是“非永久性”,如果电源线不小心拔掉了,内存里所有数据都会丢失,所以内存叫“易失性”存储器。相对应存储器(Storage)存储器(Storage)和内存(Memory)有点不同,任何写入“存储器”的数据,比如硬盘数据会一直存着,直到被覆盖或删除,断电也不会丢失。存储器是“非易失性”的。以前是“易失性”速度快,“非易失性”的速度慢,但随着技术发展,两者的差异越来越小。如今认为稀松平常的技术,比如U盘,能低成本+可靠+长时间存储上GB的数据。内存条:硬盘:纸卡,Paper punch cards最早的存储介质是 打孔纸卡,以及纸卡的亲戚 打孔纸带。到1940年代,纸卡标准是80列12行。一张卡能存960位数据(8012=960)。最大纸卡程序,是美国军方的“半自动地面防空系统”简称SAGE。一个在1958年投入使用的防空系统。主程序存储在62500个纸卡上,大小5MB左右,相当于如今手机拍张照片。纸卡用了十几年,因为不用点且便宜耐用。然而坏处是读取慢,只能写入一次。打的孔无法轻易补上。对于存临时值,纸卡不好用。需要更快更大更灵活的存储方式。J. Presper Eckert 在1944年建造ENIAC时发明了一种方法,叫“延迟线存储器”。原理:拿一个管子装满液体,如水银,管子一段放扬声器,一段放麦克风。扬声器发出脉冲时,会产生压力波。压力波需要时间,传播到另一端的麦克风。麦克风将压力波转换回电信号。可以用压力波的传播延迟来存储数据。假设有压力波代表1,没有代表0。扬声器可以输出10101111,压力波沿管子传播,过了一会儿,撞上麦克风。将信号转换回1和0。如果加一个电路,连接麦克风和扬声器,再加一个放大器(Amplifier)来弥补信号衰弱,就能做一个存储数据的循环。信号沿电线传播几乎是瞬时的。所以任何时间点只显示1 bit数据。但管子中可以存储多个位(bit)。忙完ENIAC后,Ecket和同事John Mauchly 着手做一个更大更好的计算机叫EDVAC,使用了延迟线存储器。总共有128条延迟线,每条能存352位(bits),总共能存45000位(bit)对1949年来说还不错。这使得EDVAC成为最早的“存储程序计算机”(只要能在内存里存储程序,就算是“存储程序计算机”)之一,但“延迟线存储器”的一大缺点是:每一个时刻只能读一位(bit)数据。如果想访问一个特定的bit,比如第112位(bit),得等待它从循环中出现。所以又叫“顺序存储器”或“循环存储器”。而想要的是“随机存取存储器”可以随时访问任何位置。增加内存密度也是一个挑战,把压力波变得紧密,意味着更容易混在一起,所以出现了其它类型的“延迟线存储器”。如“磁致伸缩延迟存储器”。用金属线的振动来代表数据,通过把线卷成线圈,30cm30cm的面积能存储大概1000位(bit)。然而,延迟线存储器在1950年代中期就基本过时了。因为出现了新技术,性能,可靠性和成本都更好。-> “磁芯存储器”“磁芯存储器”,用了像甜甜圈的小型磁圈,如果给磁芯绕上电线,并施加电流,可以将磁化在一个方向。如果关掉电流,磁芯保持磁化,如果沿相反方向施加电流,磁化的方向(极性)会翻转,这样就可以存1和0。如果只存1位不够用,所以把小甜甜圈排列成网格,有电线负责选行和列,也有电线贯穿每个磁芯,用于读写一位(bit)磁芯内存的第一次大规模运用是1953年麻省理工学院的Whirlwind 1计算机,磁芯排列是3232(1024个字节),用了16块板子,能存储大约16000位(bit),更重要的是,不像“延迟线存储器”,磁芯存储器能随时访问任何一位(bit)。“磁芯存储器”从1950年中期开始成为主流,流行了20多年,而且一般还是手工编织的。刚开始存储成本大约1美元1位到1970年,下降到1美分左右。不幸的是,即使每位1美分也不够便宜。5MB约等于4000万bit。当时,对存储技术进行了大量的研究,到1951年,Eckert和Mauchly创立了自己的公司,设计了一台叫UNIVAC的新电脑,最早进行商业销售的电脑之一。它推出了一种新存储:磁带。磁带磁带是纤薄柔软的一长条磁性带子,卷在轴上,磁带可以在“磁带驱动器”向前后移动。里面有一个“写头”绕了电线,电流通过产生磁场。导致磁带的一小部分被磁化,电流方向决定了极性,代表1和0,还有一个“读头”,可以非破坏性的检测极性。现在大量数据冷备份还是用磁带。UNIVAC用了半英寸宽,8条并行的磁带,磁带每英寸可存128位数据,每卷有365.76米长。意味着一共可以存1500万位左右,接近2兆字节(2MB)。虽然磁带驱动器很贵,但磁带又便宜又小,因此磁带至今仍用于存档。磁带的主要缺点是访问速度。磁带是连续的,必须倒带或快进到达特定位置。可能要几百米才能得到某个字节,这很慢。1950,60年代,有个类似技术是“磁鼓存储器”,有金属圆筒,盖满了磁性材料以记录数据。滚筒会持续旋转,周围有数十个读写头,等滚筒转到正确的位置,读写头会读或写1位(bit)数据。为了尽可能缩短延迟,鼓轮每分钟上千转。到1953年,磁鼓技术飞速发展,可以买到存80000位的“磁鼓存储器”。也就是10KB。硬盘但到1970年代“磁鼓存储器”不再生产,然而,磁鼓导致了硬盘的发展,硬盘和磁鼓很相似。不过硬盘用的是盘,不像磁鼓用圆柱体,原理是一样的:磁盘表面有磁性。写入头和读取头,可以处理上面的1和0。硬盘的好处是薄,可以叠在一起,提供更多表面积来存储数据。IBM对世上第一台磁盘计算机就是这样子做的,RAMAC 305,它有50张60.96厘米直径的磁盘,总共能存5MB左右。要访问某个特定bit,一个读/写磁头会向上或向下移动,找到正确的磁盘。然后磁头会滑进去,就像磁鼓存储器一样,磁盘也会高速旋转,所以读写头要等到正确的部分转过来。RAMAC 305访问任意数据,平均只要六分之一秒左右。存储技术的发展:纸卡 -> 延迟线存储器(磁致伸缩延迟存储器) -> 磁芯存储器 -> 磁带 -> 磁鼓存储器 -> 硬盘

March 25, 2019 · 1 min · jiezi

HTML5 语义化标签

为什么会有h5 语义化标签在HTML5出现之前,我们一般采用DIV+CSS布局页面。但是这样的布局方式不仅使我们的文档结构不够清晰,而且不利于搜索引擎爬虫对我们页面的爬取。为了解决上述缺点,HTML5新增了很多新的语义化标签。语义标签的概念维基百科语义化是前端开发里面的一个专用术语,其优点在于标签语义化有助于构架良好的html结构,有利于搜索引擎的建立索引、抓取;另外,亦有利于页面在不同的设备上显示尽可能相同;此外,亦有利于构建清晰的机构,有利于团队的开发、维护。通俗来说:语义化的意思是从名称一眼就能看出其内容和作用是什么,语义化标签就是通过使用浅显易懂的元素名和属性名来实现语义化的语义标签有什么用?可以提高页面的可访问性,即在css丢失的情况下,页面结构仍然可以比较清晰的展现;提高用户体验,用户不够清楚地方可以得到良好的解释;有利于页面seo,让搜索引擎更容易明白页面结构和内容的主次顺序;有利于页面维护者理解代码结构,降低维护成本;方便其他设备解析(如屏幕阅读器、盲人阅读器、移动设备)以语义的方式来渲染网页常用语义标签有哪些?h1到h6标签 表示不同等级的标题可以用来表现文本内容的层级结构<article> 元素表示文档、页面、应用或网站中的独立结构,其意在成为可独立分配的或可复用的结构<article> 可以嵌套使用,但是他们必须是部分与整体的关系<article> 元素的作者信息可通过 <address> 元素提供,但是不适用于嵌套的 <article> 元素<article> 元素的发布日期和时间可通过 <time> 元素的pubdate属性表示<aside>定义侧栏标签如果使用多个 <aside>,标签应该在主要内容的后面,这样有利于SEO的搜索与提升可访问性如果是与文章的主要内容有关系的图像需要用 <figure> 标签代替<section> 元素表示文档中的一个区域(或节),比如,内容中的一个专题组,一般来说会有包含一个标题(heading)如果元素内容可以分为几个部分的话,应该使用 <article> 而不是 <section>不要把 <section> 元素作为一个普通的容器来使用。 一般来说,一个 <section> 应该出现在文档大纲中<header> 元素用来表示一些前言性质或导航类的内容。它可以包含一些标题性质元素,也可以放置一些比如logo,搜索框,或其它的 <header> 元素等<header> 元素并不是一个可以形成段落内容的元素,所以,不需要在内容目录里体现出来<footer> 元素表示最近一个章节内容或者根节点(sectioning root)元素的页脚。一个页脚通常包含该章节作者、版权数据或者与文档相关的链接等信息<footer> 元素内的作者信息应包含在 <address> 元素中<footer> 元素不是章节内容,因此在outline中不能包含新的章节<nav> 描绘一个含有多个超链接的区域,这个区域包含转到其他页面,或者页面内部其他部分的链接列表并不是所有的链接都必须使用 <nav> 元素,它只用来将一些热门的链接放入导航栏一个网页也可能含有多个 <nav> 元素,例如一个是网站内的导航列表,另一个是本页面内的导航列表对于屏幕阅读障碍的人,可以使用这个元素来确定是否忽略初始内容<dl>,<dt>,<dd> (自定义列表)dl: 英文意思为definition list,作用是定义列表dt: 英文意思为defines terms,作用是定义列表中的项目dd: 英文意思为defines description,作用是定义列表中项目的注释<b>/<strong> 元素默认均展示为加粗<b> 表示“文体突出”文字,通俗讲就是突出不安分的文字。像概要中的关键字,产品名。或者代表强调的排版方式<strong> 表示重程度的强调<i>/<em> 元素默认均展示为斜体<i> 表现为在文章中突出不同意见或语气或的一段文本,例如外语,科技术语、或者是排版用的斜体文字<em> 表示强调<mark>用来突出显示文本,他的效果就像是用荧光笔给重点的语句做标一样<address> 元素可以让作者为它最近的 <article> 或者 <body> 祖先元素提供联系信息。在后一种情况下,它应用于整个文档当表示一个和联系信息无关的任意的地址时,应使用 <p> 元素这个元素不能包含除了联系信息之外的任何信息,比如出版日期通常,<address> 元素可以放在当前section的 <footer> 元素中,如果存在的话<figure> 这个元素是用来引入图表、图形、照片等,对应的场景就是像是杂志中的图片一样<figure> 元素可以包含多个内容块,但是只能有一个 <figcaption>(可以理解成给图表加标题)标签可以用 <h1> ~ <h6> 来给 <figure> 增加标题;<figcaption> 不能单独出现,需要有配套的内容一起出现<time> 可以通过这个标签标记一个具体的时间或日期,应用场景通常就是一篇文章的发表时间datetime中的时间最好与 <time> 标签中的文本元素日期一样,写法可以不一样如果这个时间是代表整个文章或是页面的时间需要添加pubdate属性不要在 <time> 标签中使用不确切的时间如:“今天中午”、“上周末”如果 使用pubdate属性需要注意的是要在同一个父标签下面不要出现张冠李戴的问题<time> 标签不能在嵌套另一个 <time> 标签datatime中的时间格式需要是标准的机器可能的时间 如:YYYY-MM-DDThh:mm:ss注意尽可能少的使用无语义的标签div和span在语义不明显时,既可以使用div或者p时,尽量用p,因为p在默认情况下有上下间距,对兼容特殊终端有利使用表格时,标题要用caption,表头用thead,主体部分用tbody包围,尾部用tfoot包围。表头和一般单元格要区分开,表头用th,单元格用td表单域要用fieldset标签包起来,并用legend标签说明表单的用途每个input标签对应的说明文本都需要使用label标签,并且通过为input设置id属性,在lable标签中设置for=someld来让说明文本和相对应的input关联起来 ...

March 19, 2019 · 1 min · jiezi

计算机科学基础_4

算法入门选择排序,Selection sort大O表示法,Big O notation归并排序 - Merge sortDijkstra 算法写指数函数,只是无数解决方案的一种,还有其它方案。用不同顺序写不同语句,也能得到一样的结果,不同的是“算法”,意思是:解决问题的具体步骤。即使结果一致,有些算法会更好。一般来说,所需步骤越少越好。不过有时候也会关心其他因素,比如占多少内存。“算法”一词来自 阿尔●花拉子密,1000多年前的代数之父之一。如何想出高效算法,是早在计算机出现之前就有的问题,诞生了专门研究计算的领域,然后发展成一门现代学科,计算机科学。选择排序记载最多的算法之一是“排序”。比如给名字,数字排序。排序到处都是,找最便宜的机票,按最新的时间排邮件,按姓氏排联系人等等,这些都要排序。计算机科学家花了数十年发明了各种排序算法,还起了酷酷的名字,“冒泡排序”, “意面排序”,“选择排序”比如:一堆机票价格,都飞往目的地。把价格一组数据存放到一个数组结构。先找到最小数,从最上面开始,然后和第一个比较,所以看最小的数是否变化,移动位置。重复循环比较,持续移动位置。意味着,如果要排N个东西,要循环N次,每次循环中再循环N次,共N*N。算法的输入规模和运行步数之间的关系,叫算法的复杂度。表示运行速度的量级。计算机科学家们把算法复杂度叫:大O表示法(big O notation)。选择排序的算法复杂度O(n^2)效率不高。归并排序第一件事是检查数组大小是否>1,如果是,就把数组分成两半。再检查数组大小是否>1,如果是,继续分组,如果不是开始“归并”。从前两个数组开始,读第一个(也是唯一一个)值,然后开始比较,如果更小,排在之前。重复这个过程,按序排列。然后数组大小增加,再次归并。同样,取前二个数组,比较第一个数,然后再比较第一个数组的第一个数,第二个数组的第二个数。然后合并成更大有序的数组。直到排序完整。归并排序的算法复杂度O(n * logn),n是需要比较+合并的次数和数组大小成正比,logn是合并步骤的次数。图搜索“图”是用线连接起来的一堆“节点”。可以想成地图,每个节点是一个城市,线是公路。一个城市到另外一个城市,花的时间不同,可以用成本(cost)或权重(weight)来代称,代表要几个星期。假设想找“高庭”到“凛冬城”的最快路线。最简单的方法是尝试每一条路,计算总成本。这是“蛮力办法”。假设用蛮力方法,来排序数组,尝试每一种组合,看是否排好序。这样的时间复杂度是: O(n!),n是节点数,n!是阶乘。比O(n^2)还糟糕。从“高庭”开始,此时成本为0,把0标记在节点里,其它城市标记成问号,因为不知道成本多少。Dijkstra 算法总是从成本最低的节点开始,目前只知道一个节点“高庭”,所以从这里开始,跑到所有相邻节点,记录成本,完成一轮算法。但是还未到“凛冬城”,所以再跑一次Dijkstra 算法。下一个成本最低的节点,是“君临城”,记录相邻节点的成本,到“三叉戟河”,然而想记录的是,从“高庭”到这里的成本,所以“三叉戟河”的总成本8+5=13。现在走另外一条路到“奔流城”,成本高达25,总成本33,但“奔流城”中最低成本是10,所以无视新数字,保留之前的成本10。现在看了“君临城”的每一条路还没到“凛冬城”所以继续。下一个成本最低的节点,是“奔流城”,要10周。先看到“三叉戟河”成本,10+2=12,比之前的13好一点,所以更新“三叉戟河”为12,“奔流城”到“派克城”成本是3。10+3=13,之前是14,所以更新“派克城”为13。“奔流城”出发的所有路径都走了遍,还没到终点,所以继续。下个成本最低的节点,是“三叉戟河”。从“三叉戟河”出发,唯一没看过的路,通往“凛冬城”。成本是10加上“三叉戟河”的成本12,总成本是22。再看最后一条路,“派克城”到“凛冬城”,总成本是31。所以最佳线路是:“高庭” -> “奔流城” -> “三叉戟河” -> “凛冬城”。Dijkstra的语录:“有效的程序员不应该浪费很多时间用于程序调试,他们应该一开始就不要把故障引入。”“程序测试是表明存在故障的非常有效的方法,但对于证明没有故障,调试是很无能为力的。”Dijkstra 算法算法复杂度是:O(n^2)经过改造的Dijkstra 算法复杂度减低为:O(n * logn + l) n是节点数,l是多少条线数据结构数组,Array字符串,String矩阵,Matrix结构体,Struct指针,Pointer节点,Node链表,Linked List队列,Queue栈,Stack树,Tree二叉树,Binary Tree图,Graph算法处理的数据,存在内存里的格式是什么。希望数据是结构化,方便读取,因此计算机科学家发明了“数据结构”。数组数组Array,也叫列表(List),或向量(Vector)。有一些区别,在不同语言中基本类似。数组的值一个个连续存在内存里。可以把多个值存在数据变量里,为了拿出数组中的某个值,要指定一个下标(index)大多数编程语言中,数组下标都从0开始。用方括号[]代表访问数组。j = [5, 3, 7, 21, 82, 4, 19]a = j[0] + j[5]数组存在内存里的方式,十分易懂。为了简单,假设编译器从内存地址1000开始存数组,数组有7个数字,像上图一样按顺序存。写j[0],会去内存地址1000,加0个偏移,得到地址1000,拿值: 5。写j[5],会去内存地址1000,加5个偏移,得到地址1005,拿值: 4。数组的用途广泛,所以几乎所有的编程语言,都自带了很多函数来处理数组。字符串数组的亲戚是字符串String,其实就是字母,数字,标点符号等,组成的数组。计算机怎么存储字符?通过字符对应的ASCII表写代码时,用引号括起来就行了: j = “STAN ROCKS"虽然长的不像数组,但的确是数组。注意:字符串在内存里以0结尾,不是“字符0”,是二进制“0”,这叫字符“null”,表示字符串结尾。这个字符非常重要,如果调用print()函数,会从开始位置,逐个显示到屏幕,但是得知道什么时候停止下来。否则会把内存里所有东西都显示出来。0告知函数何时停下。因为计算机经常处理字符串,所以有很多函数专门处理字符串。

March 18, 2019 · 1 min · jiezi

计算机科学基础_3

早期的编程方式程序如何进入计算机打孔纸卡 Punched card插线板 Plugboard冯诺依曼架构 Von Neumann Architecture面板编程 Panel programming商业成功的计算机:Altair 8800早期计算机如何编程?打孔纸卡 -> 插线板 -> 面板拔开关。计算机的原理:怎么从内存读写数据,执行操作。比如把两个数字加在一起。简单描述了指令的执行,也就是计算机程序。程序如何“进入”计算机:程序需要加载进内存,是计算机科学。(打孔纸卡,插线板,面板拔开关)打孔纸卡 Punched card需求:给机器编程,早在计算机出现之前就有了,著名的就是纺织业。如果只想一块红色大桌布,可以直接放红线进织布机,但是如果想要图案怎么办?比如条纹或方格。需要没隔一会,调整一次织布机,因为非常消耗劳动力,所以图案纺织品很贵。可编程纺织机:每一行的图案由可穿孔纸卡决定,特定位置有没有穿孔,决定了线是高是低。横线是从上/从下穿过,为了让每行图案不同,纸片连成长条,形成连续指令。很多人认为雅卡尔织布机是最早的编程。事实证明,穿孔纸卡便宜,可靠,也易懂。后面世代中,穿孔纸卡用于1890年美国人口普查。例如:一张卡存一个人的信息,比如种族,婚姻状况,子女数量,出生国家等等。针对每个问题,人口普查工作者会在对应位置打孔,当卡片插入汇总机,孔会让对应总和值+1,可以插入整个国家入口的卡片,在结束后得到各个总值。但是,早期的汇总机不算计算机。因为它们只做一件事:汇总数据。操作是固定的,不能编程。穿孔 纸卡存的是数据,不是程序。之后,这些机器被加强,可以做减,乘,除。甚至可以做一些小决定,决定何时执行某指令。插线板 Plugboard为了正确执行不同计算,程序员需要某种控制面板,面板有很多小插孔,程序员可以插电线,让机器的不同部分,互相传数据和信号。因此也叫“插线板”。但是,这意味着,运行不同程序要重新接线。所以到了1920年代,控制面板变成了可拔插。让编程更方便,可以给机器插入不同程序。比如,一个插线板算销售税,另一个算工资单。但给插线板编程很复杂。用插线板编程,不只在机电计算机流行,世上第一台通用电子计算机,ENIAC,完成于1946年。用了一大堆插线板。程序在纸上设计好之后,给ENIAC连线,最多可能花三个星期。因为早期计算机非常昂贵,停机几个星期只为了换程序,完全无法接受,急需要更快,更灵活的新方式来编程。幸运的是,到1940年代晚期1950年代初。内存变得可行,价格下降,容量上升。与其把程序存在插线板,存在内存变得可行,这样易于修改,方便CPU快速读取。这类机器叫“存储程序计算机”。冯诺依曼架构如果内存足够,不仅可以存要运行的数据,还可以存程序需要的数据。包括程序运行时产生的新数据。程序和数据都存在一个地方,叫“冯诺依曼结构”。将指令和数据混合存放到内存,穿孔纸片和插线板,都是分开。冯诺依曼曾过:我在思考比炸弹重要得多的东西计算机。冯诺依曼计算机的标志是,一个处理器(有算术逻辑单元)+ 数据寄存器 + 指令寄存器 + 指令地址寄存器 + 内存(负责存数据和指令)。第一台冯诺依曼架构的“存储程序计算机”,由曼彻斯特大学于1948年建造完成,绰号“宝宝”。虽然有内存很棒,但程序和数据,依然需要某种方式输入计算机,所以用穿孔纸卡。到1980年代,几乎所有的计算机都有穿孔纸卡读取器,可以吸入一张卡片,把卡片内容写进内存,如果放了一叠卡片,读取器会一个个写进内存。一旦程序和数据写入完毕,电脑会开始执行。即便简单程序也有几百条指令,要用一叠纸卡来存。用纸卡的最大型程序是美国空军的SAGE防空系统,于1955年完成,据称顶峰时期,雇佣了世上20%程序员。主控制程序用了62500张穿孔纸片,等同于大约5MB的数据。穿孔纸卡不仅可以往计算机放数据,还可以取出数据,程序运行到最后,结果可以输到纸卡上,方式是打孔。然后人可以分析结果,或者再次放进计算机,做进一步计算。穿孔纸卡的亲戚是纸带,基本是一回事,只不过更连续,不是一张张卡。现在存储的方式,硬盘,只读光盘,DVD,U盘等等。面板编程 Panel programming与其插一堆线到插线板,可以用一大堆开关和按钮,做到一样的效果。面板上有指示灯,代表各种函数的状态和内存中的值。50和60年代的计算机,一般都有这样巨大的控制台。很少有人只用开关来输入一整个程序,但技术上是可行的,早期针对计算机爱好者的家用计算机,大量使用了开关。因为大多数家庭用户负担不起昂贵的外围设备,比如穿孔纸卡读取器。第一款取得商业成功的家用计算机是Altair 8800有两种版本可以买:预先装好的整机需要组装的组件为了给8800编程,要拨动面板上的开关。输入二进制操作码,然后按“存储键”把值存入内存。然后会到下一次内存位置,可以再次拨开关,写下一个指令,重复这样操作。(初代plc 原型)把整个程序都写入内存之后,可以推动开关,回到内存地址0,然后按运行按钮,灯会闪烁。不管是插线板,开关或穿孔纸卡。早期编程都是专家活,不管是全职还是技术控,都要非常了解底层硬件。比如 操作码,寄存器等,才能写程序。所以编程很难,很烦。哪怕工程师和科学家都无法完全发挥计算机的能力,需要一种更简单方式,告诉计算机要做什么。一种更简单的编程方式。编程语言的发展史编程:二进制 -> 助记符(汇编器) -> A-0(编译器) -> FORTRAIN二进制写程序,先纸上写伪代码,手工转二进制。用“助记符”写代码(LOAD 14)为了把助记符转二进制,汇编器诞生(Assembler)哈佛1号计算机首批程序员,海军官员。(葛丽丝●霍普Grace Hopper)Grace 设计了编程语言A-0Grace 1952年做了第一个编译器(Compiler),实现A-0变量(Variables)FORTRAINCOBOL新语言1960年代:ALGOL, LISP, BASIC1970年代:Pascal C, Smalltalk1980年代:C++, Object-C, Perl1990年代:Python, Ruby, Java组成计算机的物理组件:比如电,电路,寄存器,RAM, ALU, CPU。在硬件层面编程非常麻烦,所以程序员想要一种更通用的方法编程。一种“更软的”媒介: 软件。第一条指令在内存地址0: 0010 1110。前4位操作码,简称OPCODE对于这个假设CPU,0010代表LOAD A指令:把值从内存复制到寄存器A。后4位是内存地址,1110是十进制的14。所以这8位表达的意思是:读内存地址14,放到寄存器A。只是用了两种不同语言,可以想成是英语和摩尔斯码的区别。“hello” -> “…. . .-.. .-.. —”是一个意思:“hello”只是编码方式不同,英语和摩尔斯码的复杂度也不同,英语有26个字母以及各种发音。摩尔斯码只有“点”和“线”但它们可以传达相同的信息,计算机语言也类似。计算机能处理二进制,二进制是处理器的“母语”。事实上,它们只能理解二进制。这叫“机器语言”或“机器码”。二进制写程序在计算机早期阶段,必须用机器码写程序。具体来讲,会先在纸上写一个“高层次版”的伪代码(PSEUDO-CODE)。例如:从内存取下一个销售额,然后加到天,周,年的总和,然后算税。写好伪代码后,用“操作码表”把伪代码转成二进制机器码,翻译完成后,程序可以喂入计算机并运行。助记符在1940~1950年代,程序员开发出一种新语言,更可读,更高层次。每个操作码分配一个简单名字,叫“助记符”(MNEMONICS)。“助记符”后面紧跟数据,形成完整指令。与其用1和0写代码,程序员可以写“LOAD A 14”。

March 18, 2019 · 1 min · jiezi

彻底了解web开发,熟悉建站过程

服务端开发基础前端开发 最终还是属于 Web 开发 中的一个分支,想要成为一名合格的前端开发人员,就必须要 充分理解Web 的概念。如何建立一个Blog网站 开始-明确业务-根据分析需求-设计功能-具体实现功能-部署上线-结束web学习的知识如何应用?1、网页开发技术(硬性)HTML —— 网页内容结构(GUI)CSS —— 网页外观样式(GUI)JavaScript —— 编程语言(可以用于调用浏览器提供的 API)Web APIs —— 网页交互(界面功能)jQuery —— 便捷手段(糖果而已,不是必要的)2、编程能力 / 编程思想 / 解决问题的思路(软性)我要做什么(我要得到什么),我目前有什么(我能拿到什么)至此,我们已经可以独立完成网页开发了,具体能完成的东西就是一个一个的网页,而且还能给这个页面加上一些动态的交互。但是这距离成为一个网站还有一些路要走。还需要学习什么?1、想要完成完整的 Web 网站,还需要学习什么? 搭建 WEB 服务器(提供网站服务的机器) HTTP(浏览器与服务端的通讯协议) 服务端开发(动态网页技术) 数据库操作(服务端存储数据方式) AJAX(浏览器与服务端的数据交互方式)搭建web服务器 ##、服务器(提供服务)指的就是一台安装特定的软件的公共计算机,用于专门用于提供特定的服务。按照服务类型的不同,又划分为:Web 服务器、数据库服务器、文件服务器等等。客户端(使用服务)指的是在一次服务过程中使用这个服务的设备(网络端点)。目前咱们最常见的客户端就是浏览web服务器软件3.1. Web 服务器软件Nginx ········································ 反向代理Apache ····································· PHPIIS ·············································· ASP.NETTomcat ····································· Java安装 Web 服务器软件这里不详细介绍安装配置问题,可以自行Google请求响应流程用户打开浏览器地址栏输入我们需要访问的网站网址(URL)浏览器通过 DNS 服务器获取即将访问的网站 IP 地址浏览器发起一个对这个 IP 的请求服务端接收到这个请求,进行相应的处理服务端将处理完的结果返回给客户端浏览器浏览器将服务端返回的结果呈现到界面上

March 15, 2019 · 1 min · jiezi

计算机科学基础3

中央处理器CPURAM + 寄存器 + ALU 做个CPU解释“指令 -> 解释 -> 执行”这个循环时钟是什么,时钟速度和赫兹超频提升性能,降频省电重点:拼个CPU出来。CPU怎么执行命令?ALU:作用:输出二进制,它会执行计算。两种内存:寄存器: 很小的一块内存,能存一个值。RAM: 是一大块内存,能在不同地址存大量数字。 (寄存器增大后改造成RAM)把RAM, 寄存器, ALU放在一起,组件计算机的心脏CPU(中央处理单元)。拼个CPUCPU作用: 负责执行程序。程序由一个个操作组成,这些操作叫做“指令”,因为它们“指示”计算机要做什么。如果是数学指令,比如加/减,CPU会让ALU进行数学计算。也可能是内存指令,CPU会和内存通信,然后读/写值。CPU里有很多组件。重点放在功能,而不是一根根线具体怎么连。当用一根线连接两个组件时,这条线只是所有必须线路的一个抽象。这种高层次视角叫“微体系架构”。首先要内存,使用RAM,为了保持简单,假设它只有16个位置,每个位置存8位。再来四个8位存储器,叫A, B, C, D。寄存器用来 临时存数据 和 操作数据。数据是以二进制存在内存里。程序也可以存在内存里。可以给CPU支持的所有指令,分配一个ID:在这个假设的例子,用前四位存“操作代码”(operation code),简称“操作码”(opcode),后四位代表数据来自哪里,可以是寄存器或内存地址。还需要两个寄存器,来完成CPU。一个寄存器追踪程序运行到哪里了,叫它“指令地址寄存器”。(存当前指令的内存地址)另外一个寄存器存当前指令, 叫“指令寄存器”。当启动计算机时,所有寄存器从0开始。CPU的第一个阶段叫“取指令阶段”(FETCH PHASE),负责拿到指令。首先,将“指令地址寄存器”连到RAM,

March 13, 2019 · 1 min · jiezi

HTTP与HTTPS的作用与区别

1、HTTPHTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的www文件都必须遵守这个标准。HTTP是一个基于TCP/IP通信协议来传递数据。有关HTTP的详细教程如下链接描述2、HTTPSHTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。有关HHTPS的详细教程如下链接描述SSL(Secure Sockets Layer)中文叫做“安全嵌套层”。TLS(Transport Layer Security)中文叫做“传输层安全协议”3、HTTP和HTTPS的区别超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此HTTP协议不适合传输一些敏感信息,比如信用卡号、密码等。为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。HTTPS和HTTP的区别主要为以下四点:(1)https协议需要到ca申请证书,一般免费证书很少,需要交费。(2)http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。(3)http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。(4)http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。4、对称加密和非对称加密链接描述1、“加密”就是把“明文”变成“密文”的过程;“解密”就是把“密文”变成“明文”的过程,在这个过程中都需要“密钥”来参与数学运算。2、对称加密指的就是加密和解密使用同一个秘钥,所以叫做对称加密。对称加密只有一个秘钥,作为私钥。常见的对称加密算法:DES,AES,3DES等等。3、非对称加密指的是:加密和解密使用不同的秘钥,一把作为公开的公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。 常见的非对称加密算法:RSA,ECC4、对称加密和非对称加密的区别对称加密算法相比非对称加密算法来说,加解密的效率要高得多。但是缺陷在于对于秘钥的管理上,以及在非安全信道中通讯时,密钥交换的安全性不能保障。所以在实际的网络环境中,会将两者混合使用.例如针对C/S模型, (1) 服务端计算出一对秘钥pub/pri。将私钥保密,将公钥公开。 (2) 客户端请求服务端时,拿到服务端的公钥pub。 (3) 客户端通过AES计算出一个对称加密的秘钥X。 然后使用pub将X进行加密。 (4)客户端将加密后的密文发送给服务端。服务端通过pri解密获得X。 (5)然后两边的通讯内容就通过对称密钥X以对称加密算法来加解密。

March 12, 2019 · 1 min · jiezi

计算机科学基础2

计算机如何存储和表示数字: 二进制。二进制存储单位 MB, GB, TB等。正数,负数,整数,浮点数的表示。美国信息交换标准代码 - ASCII,用来表示字符。UNICODE 1992年诞生,是字符编码标准,解决ASCII不够表达所有语言的问题。二进制中,一个1或0 叫 一位(a bit).8位能表示的最小数字是0, 8位都是0;最大数是255,8位都是1.能表示256个不同的值,2的8次方.8位是如此常见,以至于有专门的名字:字节.1 字节 = 8 位(1 bytes = 8 bits)MB(Mege是百万字节), GB(Giga是十亿字节), TB(8万亿个1和0)等,不同前缀代表不同数量级。1千字节 = 2的10次方 = 1024字节1000也是千字节(KB)的正确单位,也就是1000和1024都是正确单位。32位/64位计算机: 是一块块处理数据,每块是32位或64位。32位能表示的最大数是:43亿左右。(32个1)。现在图片都是用32位颜色。正数,负数,整数,浮点数的表示负数,大部分计算机用第一位表示正负:1是负,0是正。用剩下的31位来表示数字。能表示的数字范围是:正 20亿 到 负 20亿64位能表示最大数是正负9.2 * 10 ^ 18计算机必须给内存中每一个位置,做一个“标记”,这个标记叫“位址(ADDRESSES)”,目的是为了方便存取数据。非整数,常见浮点数。有很多种表示,最常见的是 IEEE 754标准。它用类似科学记数法的方法,来存取十进制值。例如: 625.9 = 0.6259 * 10 ^ 3, .6259叫“有效位数”, 3是“指数”。在32位浮点数中,第1位表示数字的正负,接下里8位存指数,剩下32位存有效位数。ASCII与其用特殊方式来表示字母,计算机可以用数字表示字母,最直接的方法是给字母编号。设计于1963年,ASCII是7位代码,足够存储128个不同值。可以表示大写字母,小写字母,数字0-9,特殊符号以及标点符号。Unicode解决ASCII不够表达所有语言的问题。最常见的Unicode是16位,有超过一百万个位置。对所有语言的每个字符都够了100多种字母表加起来占了12万个位置,还有位置放数学符号,甚至Emoji,就像ASCII用二进制来表示字母一样。其它格式,比如MP3或GIF,用二进制编码声音/颜色,表示照片,电影,音乐。这些标准归根到底是一长串位。短信,视频,互联网上的每个网页,甚至操作系统,只不过是一长串1和0。算术逻辑单元ALU,英特尔74181ALU有2个单元,1个算术单元和1个逻辑单元算术单元: 半加器(处理1个bit,2个输入)。 全加器(处理1个bit,3个输入)。8 bit 加法(1个半加器,7个全加器)逻辑单元:检测数字是否为0的电路(一堆OR门最后加一个NOT门),ALU抽象成V符号,Flag标志(是否相等,是否小于,是否溢出等等)表示和存储数字是计算机的重要功能,但真正的目标是计算,有意义的处理数字。处理数字的操作由计算机的“算术逻辑单元(ARITHMETIC & LOGIC UNIT)”处理,称之为: ALU是计算机的数学大脑计算机里负责运算的组件,基本其它所有部件都用到了ALU英特尔74181:第一个封装在单个芯片内的完整ALU。布尔逻辑,做一个简单的ALU电路功能和 74181差不多。ALU单元ALU有2个单元,1个算术单元和1个逻辑单元。算术单元:负责计算机里所有的数字操作,比如加减法,比如某个数字+1(增量运算)。计算根本“把两个数字相加”,可以用单个晶体管一个个拼,把这个电路做出来,但很快就会复杂到难以理解。所以会使用更高层的抽象,用逻辑门来做。最简单的加法电路,是拿2个bit加在一起(bit是0或1),有2个输入:A和B,1个输出:就是两个数字的和。需要注意的是:A,B,输出,这3个都是单个Bit(0或1),输入只有四种可能。0 + 0 = 01 + 0 = 10 + 1 = 1二进制里,1与true相同,0与false相同。这组输入和输出,和XOR门的逻辑完全一样。1 + 1 = 10使用XOR门不够表示,需要一根额外的线代表“进位”。只有输入是1和1时,进位才是“true”,因为算出来的结果用1个bit存不下,可以和AND门一齐使用,构成半加器半加器/全加器两个输入 A 和 B都是1位,两个输出 “总和”与“进位”。如果想处理超过1 + 1的运算,需要“全加器”。101101—-1010 意味着,算下一列的时候,还有之后的每一列,得加3位在一起,并不是2个。半加器:输出了进位。有3个输入:A,B,C(都是1个bit)所以,最大的可能是:1 + 1 + 1“总和”1,“进位”1,所以要二条输出线: “总和”和“进位”。可以用 半加器 做 全加器:先用半加器将 A 和 B 相加,然后把C输入到第二个半加器,最后用一个OR门检查进位是不是true。可以再提升一层抽象,把全加器作为独立的组件。全加器会把A,B,C三个输入加起来,输出“总和”和“进位”。独立组件:使用独立组件,可以相加两个8位数字。如果第9为有进位,代表着2个数字的和太大了,超过了8位,叫做“溢出”。一般来说“溢出”的意思是,两个数字的和太大了,超过了用来表示的位数,这会导致错误和不可预期的结果。著名的例子是:吃豆人,用8位存当前关卡数。如果玩到了第256关,ALU会溢出,造成一连串错误和乱码,使得该关卡无法进行。避免溢出,可以加更多的全加器,可以操作16或32位数字。让溢出更难发生,但代价是更多逻辑门,另外一个缺点 ,每次进位都要一点时间。如今的量级是每秒几十亿次运算,所以会造成影响。所以,现代计算器用的加法电路有点不同,叫“超前进位加法器”。它更快,做的事情是一样的:把二进制数相加。ALU的算术单元,也能做一些其它数学运算,一般支持这8个操作:ADD 加法ADD WITH CARRY 带进位的加法SUBTRACT 减法NEGATEINCREMENT 增量(+1)DECREMENT 减量(-1)SUBTRACT WIH BORROW 带借位的减法PASS TUROUGU 数字无改变通过就像加法器一样,这些操作也是由逻辑门构成。没什么魔法,只是更多逻辑门。逻辑单元(LOGIC UNIT)逻辑单元执行逻辑操作,AND, OR, NOT操作。也能做简单的数值测试。比如一个数字是不是负数。检查ALU输出是否为0的电路:使用一堆OR门检查其中一位是否为1,哪怕只有一个Bit(位)是1,就知道那个数字肯定不是0,然后用一个NOT门取反。所以只有输入的数字是0,输出才为1。只是一大堆逻辑门巧妙的连接在一起。特殊符号来表示ALU:8位ALU有两个输入,A和B,都是8位(bits),还需要告诉ALU执行什么操作,例如加法或减法,所以用4位的操作代码告知ALU,简言之,“1000"可能代表加法命令,“1100"代表减法命令。操作代码告诉ALU执行什么操作,输出结果是8位的,ALU还会输出一堆标志(FLAG),“标志”是1位的,代表特定状态。比如相减两个数字,结果为0。ALU常用标志:OVERFLOW(BIT) 是否溢出ZERO(BIT) 是否相等NEGATIVE(BIT) 是否负数计算机是怎样在没有齿轮或杠杆的情况下进行运算?晶体管二进制逻辑门ALU(算术单元和逻辑单元)计算机需要一些“记忆”。 ...

March 11, 2019 · 1 min · jiezi

Python猫荐书系列:文也深度学习,理也深度学习

最近出了两件大新闻,相信大家可能有所耳闻。我来当个播报员,给大家转述一下:1、中国队在第 11 界罗马尼亚数学大师赛(RMM)中无缘金牌。该项赛事是三大国际赛事之一,被誉为中学奥数的最高难度。其中一道题,令中国队全军覆没。 2、一个出自清华姚班,毕业于斯坦福的女博士,她的毕业论文成了学术圈的“爆款”。这篇论文研究的主题是——如何让机器学会理解人类语言? 每天的新闻多如牛毛,唯独这两件引起了我的注意。它们跟本期的荐书栏目也是强关联,下面就给大家说道说道。上图标出了中国队成绩最好的三名队员。前两人在其它题目全部满分的情况下,第三题竟然是 0 分!什么样的题目能让我们的顶尖高手都束手无策呢?算了,题目我就不放出来了(我看不懂,不自找其辱。总之你们知道它很难就得了)。但是,那道题是图论的问题,关于图论,我们可以说说它跟计算机科学的关系。图论是数学的一个分支,它研究的最著名问题有柯尼斯堡七桥问题 与 四色地图问题 ,相信大家都曾见过,而在计算机领域,它也带来了诸多的研究成果:最小生成树问题、旅行商问题(NP困难)、拓扑排序算法、广度优先算法、深度优先算法,等等。奥数就这样跟程序员的职业联系了起来。然而,更值得一提的是第二个新闻:它研究的是人工智能领域最前沿的话题,想构建一个在深度神经网络之上的阅读理解模型 。简单地说是,教会计算机来阅读文本的能力。这项研究与大家熟知的数字个人助理不同(如 Alexa、Siri、Google Assistant、Cortana),它的难度超越了简单会话与信息匹配的一般性问题,想克服的是文本级阅读理解,与开放性问答等高度抽象层面的难关。它的研究成果将给数字个人助理带来质的提升,而对于人类语言文本的阅读理解能力,也必然带来更广阔的应用前途。这一切,都归功于深度学习。深度学习是我很感兴趣的领域。我们有幸生在这个时代,见证了 AlphaGo 打败人类的顶尖棋手,正在见证各种 AI 技术的出现,无人驾驶、医疗诊断、AI 翻译、金融科技、深度法律……我们的未来将被人工智能深远地影响。本期Python 猫荐书栏目(系列之六),就以此为话题,推荐给大家两本书:它们都叫《深度学习》,但是内容很不一样。第一本从应用数学,到深度学习的各种模型、算法与科研问题,走的是极其专业的路线。而另一本讲的是深度学习的 60 年发展史,以及对智能时代的一些前瞻性预测,走的是通俗科普的路线。如果要强行划分的话,前一本属理科,主要给相关领域的学生与程序员阅读,而后一本则属文科,面向所有对人工智能的历史与未来感兴趣的人群。事实上,第一本书被很多人誉为深度学习的圣经,知名度极高,有一个昵称叫作“花书”。简单梳理一下它的内容:第一部分是深度学习的基础,包含线性代数与概率论等数学知识,以及梯度优化、拟合、偏差、最大似然估计与监督学习等基础概念;第二部分是深度学习的关键部分,涉及深度前馈网络、正则化、模型优化的方法、卷积网络、序列建模、与实践应用内容;第三部分是深度学习研究,例如线性因子模型、自编码器、表示学习、结构化概率模型、蒙特卡罗方法、直面配分函数、近似推断、深度生成模型,等等。要知道,本专栏是兴趣大于能力,没办法深入剖析这本书的精华,再讲出些令行家也折服的话,但是,这本书值得推荐之处也很显著:它是一种正统的、学院派的、知识全面的、一丝不苟的、偏重理论的书籍,没错,正像是大学里相关专业的指定参考书。这就意味着,如果想进入深度学习领域,这本书将是你最好的老师。(而且不用考试,手动滑稽)至于第二本《深度学习》,书的副标题是“智能时代的核心驱动力量 ”。其实这只是翻译的结果,原书的英文名是《The Deep Learning Revolution》。20 世纪 70 年代到 90 年代是深度学习(神经网络)的寒冬,本书作者既是深度学习的先驱与奠基者,也是打破此寒冬,令深度学习东山再起的大功臣。他名叫特伦斯·谢诺夫斯基 (Terrence Sejnowski)。特伦斯是谁呢?世界十大AI科学家之一,美国四大国家学院(国家科学院、国家医学院、国家工程院、国家艺术与科学学院)在世仅3位的“四院院士 ”之一,全球AI专业会议NIPS基金会主席。深度学习的核心技术玻尔兹曼机 ,正是由特伦斯与杰弗里·辛顿共同建立的。那书的内容是什么呢?这本书在前言中称:这是一本关于深度学习的过去、现在和未来的指南。 在如此宏观的视角下,它主要讲到了一些重要概念的发展、科研群体研究的内容和传承,以及深度学习对当今社会的影响。也就是说,它不再关心微观的原理、底层的细节、繁复的逻辑。与第一本书的调性截然不同。这本书以第一人称视角讲述,带入了很多个人的动态:读书经历、研究课题、演讲与会议、人际关系、趣闻、甚至还有八卦(例如差点跟女朋友分手的一次会议。PS:他们在一起了,现在也没分开)。因此,第二本书的阅读门槛不高,还饶有趣味。往期荐书回顾: 第一期:《编写高质量代码改善 Python 程序的 91 个建议》第二期:《Python最佳实践指南》第三期:《黑客与画家》第四期:《Python源码剖析》第五期:《Python高性能编程》————-荐书完————-世事无巧不成书。似乎每期荐书都会发生一些巧合,因此我得额外交代几句:1、我早知第一本书的大名,也翻看过数学部分的一些内容,但是兴趣就止步于此。有打算纳其入荐书系列,但没想到会这么快。至于第二本书,恰好是在上期荐书发布后,中信出版社的营销人员找我约稿,当时这本书还没上市。我并非深度学习领域的专家,只能写写旁观者的言语,既然无法深入,干脆就将它们凑在一起了。2、荐书栏目不是专业书评,无法讲透全书的技术精粹,但我仍大着胆写了(之所以拖了这么久才动笔,就是因为过于担心)。一方面逼使自己阅读和查资料,快速归纳与写作;另一方面也确实是希望通过自己的文笔,能够使一部分读者获知到原先不知的信息,产生阅读的兴趣。3、就在前几天(2 月 28 日),一位知名的 Python 博主@Vamei 因抑郁症自杀了。我在看资料的时候,发现他也写了第二本《深度学习》的书评。他发布的时间是 1 月 31 日,而在这个时间,新书还未上市。这意味着他可能跟我一样,都收到了出版社的预读本,我们就是那么巧合地在同样的时间里阅读着同一本还未上市的新书。我想,这本书大概就是在给我传递一个讯息。我有很多次想过放弃邀约(无稿费,赠书一本)、放弃写这一篇荐书,直到前几天才真正开始动笔。这个神秘的讯息就这么巧地传过来了。荐书,见人。写完这篇荐书,我要写写他了。4、Vamei 的豆瓣主页写道:Vamei 是赤道附近一个台风的名字。按照气象规律,台风不常出现在赤道。所以,Vamei是一个离群的风,无所顾忌地生长,不着边际地游荡。公众号【Python猫】, 专注Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。

March 8, 2019 · 1 min · jiezi

你们要的HTML布局技巧:如何规范搭建网页架构?

单就深入了解布局规范都足够说上一个月的,今天我就不论大范围,挑选小米网站首页的部分区块布局来讲解吧!下面是小米官网的首页,很多人一看到这样的网页就傻眼,不知道咋弄,要么就随性布局,要么就干看着,其实遇到问题首先一点就是不要慌,先分析,将你认为难的问题拆分成多个问题区块,还解决不了就再拆分,直到变成能解决的小问题,一个个解决那些小问题最终大问题也就搞定了。咱们布局网页前也是一样套路,先分析整体的架构,画出布局图将大块拆分为小块,这样会更加直观看到网页的布局结构。我大致上用线框将网页内容划分为四个区块:顶部导航条区域topnav(蓝色线框)、头部分类导航区域header(绿色线框),主体内容区域main(紫色线框)、网页底部信息区域foot(橘色线框)。(注意:在初学不知道如何布局时建议使用画图工具做辅助分析。且并非只有这一种区块划分方式,布局有很多种,我只从其中挑一种来写,讲不完=_=||)这里我是按照整体网页的内容进行区块划分的:(每个线框都为一个标签盒子)topnav(顶部导航区域):从整体布局来看,topnav展示的内容都是小米网站下所有类别的子产品导航,而小米网站的首页主题是商城(直接展示电子类产品),它跟主题不符,所以这块区域单独划分出来。整体区块使用nav标签,考虑兼容性的话就使用div标签。header(头部分类信息导航区域):这块区域主要包含不同商品的分类导航和其他的服务导航,和主题也不太相符。轮播和轮播下方图片因为位置原因,打开该网页第一眼看见的就是这部分区块,即便其中也包含商品,但更多的是具有广告位的性质,这里就单独划分出header区块。整体区块使用header标签,考虑兼容性的话就使用div标签。main(主体内容区域):小米首页下小米商城的主题内容区域,也是整体网页面积最广的区块(实在不知道定主体内容区块时也可以根据面积比重来划分,最大的那块一定是主题中心),布局的重复性很高。整体区块使用main标签,考虑兼容性的话就使用div标签。footer(网页底部信息区域):这部分几乎没啥好说的,展示的都是网站特色、网站信息,也单独划分一个块。整体区块使用footer标签,考虑兼容性的话就使用div标签。代码如下:(考虑兼容IE)<div id=“pagewrap”> <div id=“page-topnav”></div> <!–导航区域–> <div id=“page-header”></div> <!–头部分类信息导航区域–> <div id=“page-main”></div> <!–主体内容区域–> <div id=“page-footer”></div> <!–网页底部信息区域–></div>这样咱们就将网页划分为四大块了,看起来难度是不是比之前要小一点了呢?那下一步就是将各个区块看做单个的整体,对它进行分析再拆分。topnav布局分析红色线框:顶部导航栏区域()黄色线框:内容盒子蓝绿色线框:左右两块列表区域如下布局图所示,顶部导航栏区域中,内容盒子宽度在1226px,水平居中,其内部又分为左右两块列表区域和一个div盒子(购物车),都包含着文字链接。对应标签结构代码:<div id=“page-topnav”> <div class=“container”> <ul class=“topbar”> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> <li><a href="#">小米商城</a></li> </ul> <ul class=“info”> <li><a href="#">登录</a></li> <li><a href="#">登录</a></li> <li><a href="#">登录</a></li> </ul> <div class=“cart”> <a href="#">购物车</a> </div> </div></div><!–文字内容我懒的写,复制粘贴的,自己私下练习记得一个个敲–>标签搭建好结构还需要搭配css来使用,这里顺便把相关的css技巧我也讲几个.container(黄色线框)本身就是一个块级元素,不改变元素类型的前提下实现水平居中有以下几种方式:第一种:(当前场景下推荐使用这种居中方式)page-topnav .container{width:1226px; margin:0 auto; }使用margin:auto实现自动计算达到水平居中,注意这种居中方式作用对象必须是块级标签,且有固定的宽度才可以实现第二种:page-topnav .container{position: relative;left: 50%;width: 1226px;margin-left: -613px;}使用相对定位,通过设置left让.container向右移动50%的相对距离,再利用负左外边距向左移动自身宽度的一半距离,达到水平居中的效果。参考下图:两个左右ul分别添加左浮动float:left;和右浮动float:right;就能实现左右对齐li中的文本实现水平垂直居中的几种方式:第一种:.container .topbar li{height: 40px; /盒子高度/line-height: 40px; /行高/font-size: 14px; /文字大小/text-align: center; /盒子内的文本水平居中/}将文本的行高与li盒子高度设为一致,达到文本垂直居中效果。第二种:.container .topbar li{padding: 10px;}给li添加内边距,实现文本上下左右居中效果,但会造成li高度不固定。所以当前场景下推荐第一种方式——–临时有事,明天接着补,如果对文章有疑问或基础学习上有问题可以和我交流—- ...

March 8, 2019 · 1 min · jiezi

面向对象的程序设计之理解对象

理解对象1、创建自定义对象的两种方法:(1)创建一个Object实例,然后再为它添加属性和方法。var person = new Object(); person.name = “Nicholas”; person.age = 29; person.job = “Software Engineer”; person.sayName = function() { alert(this.name); };(2)用对象字面量语法创建属性名或方法名 : 值var person = { name : “Nicholas”, age : 29, job : “Software Engineer”, sayName : function() { alert (this.name); }};2、属性类型ECMAScript中有两种属性:数据属性和访问器属性。(1)数据属性:数据属性包含一个数据值的位置,在这个位置可以读取和写入值,数据属性有4个描述其行为的特性:要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符对象的属性必须是configurable、enumerable、writable和value。设置其中的一个或多个值,可以修改对应的特征值。var person = {};Object.defineProperty(person, “name”, { writable : false, value : “Nicholas”});alert(person.name); //“Nicholas"person.name = “Greg”;alert(person.name); //“Nicholas"本例中将name属性的write值设置为false表示只可读不可写。吧configurable设置为false,表示不能从对象中删除属性。一旦把属性定义为不可配置的,就不能再把它变回可配置了。注意:在调用Object.defineProperty()方法创建一个新的属性时,如果不指定,configurable、enumerable和writable特性的默认值都是false。(2)访问器属性访问器属性不包含数据值:它们包含一对儿getter和setter函数。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何让处理数据。访问器属性有如下4个特性:[[Set]]:在写入属性时调用的函数。默认值为Undefined。访问器属性不能直接定义,必须使用Object.defineProperty()来定义。object.defineProperty(book,“year”, { ger : function() { return this._year; }, set : function(newValue) { if(newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } }});book.year = 2005;alert(book.edition); //2 _year前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。3、定义多个属性Object.defineProperties()方法。利用这个方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数:要添加和修改其属性值的对象,第二个是与第一个对象中要添加和修改的属性值一一对应。var book = {};Object.defineProperties(book, { _year: { writable : true, value : 2004 }, edition: { writable: true, value: 1 }, year: { get: function() { return this._year; }, set: function(newValue) { if(newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } }});以上代码在book对象上定义了两个数据属性(_year和edition)和一个访问器属性(year)。4、读取属性的特性Object.getOwnPropertyDescriptor()方法,可以去的给定属性的描述符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称,返回值是一个对象。如果是访问器属性,这个对象的属性有configurable、 enumerable、get和set;如果是数据属性,这个对象的属性有configurable、enumerable、writable和value。var book = {};Object.defineProperties(book, { _year: { writable : true, value : 2004 }, edition: { writable: true, value: 1 }, year: { get: function() { return this._year; }, set: function(newValue) { if(newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } }});var descriptor = Object.getOwnPropertyDescriptor(book, “_year”);alert(descriptor.value); //2004alert(descriptor.configurable); //falsealter(typeof descriptor.get); //undefinedvar descriptor = Object.getOwnPropertyDescriptor(book, “year”);alert(descriptor.value); //undefinedalert(descriptor.enumerable); //falsealert(typeof descriptor.get); //“function” ...

March 8, 2019 · 1 min · jiezi

win10下vue-devtools的安装和使用

网上关于vue-devtools的安装数不胜数,但是自己操作起来却总是遇到问题。写下这篇随笔,以防以后忘记。vue-devtools是一款基于chrome游览器的插件,用于调试vue应用,这可以极大地提高我们的调试效率。当然网上教程确实多,很容易理解,但是新手操作实在会卡住。这里介绍一篇,大家可以去看看https://blog.csdn.net/zhousen…。我这里写下我安装的全过程,以及走过的弯路,及解决办法。方法一:chrome商店直接安装vue-devtools可以从chrome商店直接下载安装,非常简单,这里就不过多介绍了。不过要注意的一点就是,需要FQ才能下载。方法二:(主要讲方法二)一、下载chrome扩展插件GitHub下载地址:https://github.com/vuejs/vue-…这个下载的话:下载zip格式的文件二、解压到本地记得看清楚文件的目录,等会需要访问到此目录,当然你也可以解压在c盘,不建议什么东西都往c盘扔;三、win+r 输入 cmd 打开命令行操作为了方便新手,cmd也敲出来;像很多操作都是cnpm install,npm install ,npm run build等操作;等下再介绍,先进入我们解压文件的目录:进入后输入 cnpm install;可以看下,会有错误,当然如果你下载了npm,cnpm,自然不会报错;之前卡这里很久,到处找,没有找到,毕竟新手。npm:node.js下的包管理,下载node.js 会附带npm。然后我又跑去找node.jsde的下载方法。介绍大家可以去看这个node.js下载博客:https://www.cnblogs.com/goldl…node.js的下载网址:http://nodejs.cn/download/由于我的电脑是64位的windows系统;下载完成后,双击安装一路next,到了安装目录后,默认C盘,(建议改成D盘,自己新建的目录)我的是D盘,nodejs,选好后点击next。这里需要主要,点击add to path,不需要配置环境,不然又得去操作环境配置,还有这边下载安装完成后,不要随便改变文件夹,不然得重新配置环境变量。。。。。点击next,install,finish。。。下载完成后会在你的文件夹有:那个.msi是我之前下的,可以删除现在可以去操作npm 了,记得重新打开命令行,首先查看环境变量,会发现自动配好了node,和npm,你会发现现在两个居然不在同一个文件夹输入node -v ,npm -v可以查看刚才下载的node,npm的版本;注意不要忘记输入空格。现在只是安装了npm,还有cnpm需要安装输入这个命令: npm install -g cnpm –registry=https://registry.npm.taobao.org输入: cnpm -v 检查版本,四、cnpm install,npm run build 等操作 现在我们可以回到vue-devtool的下载了。。。。。。记得需要进入自己下载解压文件的位置虽然我也不知道是在干啥,但是我知道应该是成功了:时间有点长,需要耐心等一等。。。。cnpm install操作完成后进行npm run build完成后显示:然后进去到文件夹内,一定是shells下的chrome的manifest.json文件,进入本地编辑器,做如下图修改,false 改成 true五、扩展Chrome插件打开chrome浏览器,打开设置>点击或者程序>点击开发者模式>加载已解压的扩展程序

March 8, 2019 · 1 min · jiezi

jSearch(聚搜) v0.5.0 发布,多项更新和体验优化

更新日志:jSearch-v0.5.0新增:站点搜索的默认搜索(即搜索词为空时)修改为检索最近一天的内容(即 可以了解所关注网站的最新动态);新增页面横向滚动方案(alt+滚轮,左键+滚轮,左右方向键);新增通用设置:支持快捷搜索框(jBar)热键设置;页面横向滚动设置;新增划词搜索(实验功能,默认未开启,可在设置中自行开启);新增列表载入动画;优化:修改 google 结果链接为新页面打开;修复搜索词带特殊符号引发的问题;修复 google 结果展示问题;优化 Windows 滚动条样式;修复第一页内容高度不够时无法翻页问题;已经其他细微的优化调整;下载:https://github.com/dubox/jSea…https://gitee.com/dubox/jSear...jSearch(聚搜)是一款专注内容的chrome搜索扩展,一次搜索聚合多平台内容; 帮你开启新世界大门。just Search it!

March 8, 2019 · 1 min · jiezi

内网穿透与反向代理,浅谈前后台分离

自去年毕业来到杭州,想想也该有大半年了,本身是软件工程的科班出身,在校时理论掌握的还可以,但应用到实践当中时,有些还是不大理解,于是,不停地向带我的人请教,毕竟,三人行,必有我师焉。经过一段时间理论加实践,多少也掌握了其中的门路。前端和后端(服务器端、客户端)分离前后端不分离在从业的过程中,也和其他程序员交流过,他们很多人都没有前后端(服务器和客户端)分离,而是前后端一起做掉。如果前后端不分离,此时的服务器端主要是指java代码,客户端主要是指jsp,通过spring MVC 将数据封装到ResponseBody中,再返回给jSP。JSP拿到数据,渲染页面。这里 不需要考虑端口号的问题。比如: /** * Created By zby on 16:03 2019/3/5 / @RequestMapping(value = “/”, method = RequestMethod.GET) @ResponseBody public Result fun() { return null; }前后端分离当然,前后端分离时,后端还是以java代码为主,前端就变化多端了。. 后端java通过springMVC的Rest模式的Controller层,接收前端页面传来的接口和参数,经过一系列的入参校验,调用事务层(也就是service层)这里主要是hibernate(mybatis)的事务层,实现数据库的事务操作。再调用dao(data Access object)层实现事务的原子性操作,即将顺时态的java对象转化为持久状态的数据库对象。层层深入,层层返回,将通过Result回传给前端。. 前端前端主要用h5进行页面布局,CSS3实现页面的美化。JavaScript配合jQuery调用后端的接口,传递参数和获取后端回传的数据。通过vue.js实现回传的数据的双向绑定。还可能涉及到其他框架,比如页面布局的bootstrap,数据table方式展示的jqgrid等等。前后端分离,如何实现数据交互我们将写好的java代码部署在服务器上,比如Tomcat、Jboss主流服务器。这里以Tomcat来讲解,我们将项目部署在Tomcat的上,具体如何部署Tomcat,可以参考这篇教程,Tomcat8权威指南。我们现在一般在maven中以插件的方式配置Tomcat,便于本地测试,路径为根路径,如以下代码: <build> <defaultGoal>install</defaultGoal> //maven生成的war生成的名字 <finalName>cloudCodeSaleManager</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>58081</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> <finalName>zfounder-custmom-member-portal</finalName> <server>tomcat7</server> </configuration> </plugin> </plugins> </build>在真实的项目中,一般会有测试服和正式服,测试服我们用户的测试数据库和测试服务器,正式服我们用到的是正式数据库和正式服务器,有人说,这样输简直是废话。但是,我们测试数据库和正式数据库是不一样的,因而,如果都写在同一个配置文件中,修改势必麻烦。因而,我们可以在打包时,会有测试包和正式包,这里就涉及到maven的profile的配置文件(是在pom中配置): <profiles> <profile> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <filters> <filter>../../platform-dev.properties</filter> </filters> </build> </profile> <profile> <id>prd</id> <build> <filters> <filter>../../platform-prd.properties</filter> </filters> </build> </profile> </profiles>我们Tomcat启动后,访问后端接口(url)的格式如下:scheme://host.domain:port/path/filenamescheme - 定义因特网服务的类型。最常见的类型是 httphost - 定义域主机(http 的默认主机是 www)domain - 定义因特网域名,比如 runoob.com:port - 定义主机上的端口号(http 的默认端口号是 80)path - 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。filename - 定义文档/资源的名称当然,如果没有域名的话,我们想要访问本地,也可以是这样的:http://ip:port/path/filename这里的ip涉及到内网和本机地址。内网也就是局域网,一般以192.168..打头。本机地址是:127.0.0.1。它们两个有什么区别呢?假设访问我的server_path如下所示constant = { dev: { server_path: “http://127.0.0.1:58081/”, imgPre: “http://web.cs.wentonghuishou.com/", commonParams: {} },}_env = “dev”;window.constant = constant[_env];我做后端Java的,开启了Tomcat。我的同事是做前端的,他用上面的server_path访问我,也就是说,想通过我本机ip请求我的接口,是没办法访问我后端的接口。因为他,这是我本机的ip,只有我个人才能访问。相反,我自己是可以访问的。如图所示:如果他把server_path改成了server_path: “http://192.168.40.177:58081/",,那么,他想通过局域网访问我的接口,这是可以访问我的。因为,我们同处在这个局域网下的。如图所示:外网如何访问,也就是,内网穿透假如,我和我的同事,不在同一局域网,但他,想访问我后端的接口,这时该怎么办?应该是需要摆脱网域限制,能够访问我的内网,也就是访问的本机。这时,就出现了,内网穿透的软件,比如ngrok,小米求等。小米球可以实现内网穿透,他是怎么实现内网穿透,主要是通过域名的反向代理,这也就是所谓的反向代理。其实,反向代理没那么高大上,不要被它吓到了。当然,这里需要输入端口号,这里前端的hbuilder的端口号,也就是8020端口号。为什么需要端口号,端口号能够确定本机唯一的进程。比如mysql的3306端口号,Tomcat的80端口号等。为什么是前端的端口号,因为我们首先访问的是页面,页面通过server_path来访问后端接口,这里我们不需要考虑这方面的。小米求的配置如下,这里是免费版的:当我们,在浏览器的地址栏输入http://zby.ngrok.xiaomiqiu.cn…,你会发现,它能访问到我的前端页面,并调用了我后端的接口,这就实现了ip的反向代理。域名解析也是同样的道理,利用了ip的反向代理。如图所示: ...

March 5, 2019 · 1 min · jiezi

Chrome插件Postman的数据目录存储位置,记一次重装系统后找回postman数据的过程

有次重装系统到一块新的SSD磁盘,很多数据都做了备份就是忘记将Chrome插件Postman的数据做备份,导致重装后找不到以前定义的那些Collections。悔恨之余想到既然我原来的C盘还在,为何不去原来的C盘上找一找!找到它的数据存储目录再看看怎么抢救不就行了?于是乎百度谷歌齐上阵,一阵搜索之后寥无所获,看来只能自己动手啦。Windows程序数据目录一般在当前用户的AppData目录下(默认隐藏,可在资源管理器里手动输入地址访问),到里面找吧,人肉了一阵发现了Chrome目录,找到了疑似插件安装目录DefaultExtensions,一个个插件去排查,根据icon_32.png定位到了postman所在目录,目录名是fhbjgbiflinjbdggehcddcbncdddomop可是怎么找也找不到它的用户数据存储位置,最终在Storage目录下找到了它,没错就是这个目录,postman插件的所有数据都存储在这里,将它拷贝回来后再打开postman,一切都回来了,历史执行那一栏都还在,还是熟悉的味道!Chrome Postman插件数据目录:\Users\用户xx\AppData\Local\Google\Chrome\User Data\Default\Storage\ext\fhbjgbiflinjbdggehcddcbncdddomopBTW:以上只适用于Chrome的Postman插件,要想数据不丢失,最好的办法是登录postman,它会自动同步到云端,多设备自动同步;不想登录的话,定时导出数据也是不错的选择哟。

March 5, 2019 · 1 min · jiezi

爱奇艺qsv视频下载后怎么转换为3gp格式

十一长假已经过去,大家的生活也都回到了正轨,愿大家都尽快从假期中的兴奋中回过神来,毕竟工作和学习才是最重要的,好了,下面我们转入正题,本篇教程为大家讲解爱奇艺视频下载后怎么转换为3gp格式,爱奇艺视频是qsv格式,一般的视频转换器很难对其进行格式转换,但是小编这里有妙招,使用下面的迅捷视频转换器就可以将爱奇艺视频转换为3gp格式了,而且方法比较简单,下面一起来看一看吧!方法步骤:1、小编第一步先下载爱奇艺视频,如果大家电脑上已经安装了,那就请忽略此步骤。2、下载爱奇艺视频,如图所示,小编在爱奇艺中下载了几个视频文件保存在电脑中,大家可随意下载自己喜欢的视频。3、接下来就是重要的操作了,运行我们刚刚下载的迅捷视频转换器,然后根据图中的图示来添加视频文件。4、此处要注意,添加qsv格式视频文件时,会遇到转换界面,直接点击“开始转码”即可。然后等到转码成功后就可以进行下一步了。5、如图,转码成功后视频文件是显示在软件主界面的,我们需要做的就是进行输出设置,如图所示,设置视频文件的输出格式,以及视频分辨率、音视频编码、比特率等参数。6、参数设置完成,最后我们就要开始转换格式了,如图所示,设置输出目录即保存位置后,点击“开始转换”按钮,等待一会儿就可以转换完成了。以上就是爱奇艺视频下载后怎么转换为3gp格式的解决方法,步骤比较简单,相信大家都可以学会的。迅捷视频转换器不仅可以转换音视频格式,还可以分割合并视频,有兴趣的朋友可以去探索一下。视频转换器http://www.xunjieshipin.com/d…

March 4, 2019 · 1 min · jiezi

从浏览器内核开始01

什么是浏览器内核?浏览器最核心的部分应该是浏览器内核“Rendering Engine”,也称之为“渲染引擎”,其主要作用时负责对网页语法进行解释,并渲染网页。所以“渲染引擎”决定了浏览器如何显示网页网页内容及网页格式信息。不同的浏览器内核对网页编写的语法的解释也不同,导致的渲染效果可能不同,这就造成了兼容处理问题。常见的浏览器内核有哪些呢?1.Trident [’tradnt]Trident(IE内核):该内核程序在1997年的IE4中首次被采用,是微软在Mosaic代码的基础之上修改而来的,并沿用到IE11,也被普遍称作”IE内核”。由于IE本身的“垄断性”(想起了一句话:让一个人失败,就让其膨胀,哈哈哈),而使得Trident内核长时间未能更新,进而导致与W3C标准脱节和暴露出安全性问题。2011年,从ie 9开始,Trident开始支持HTML5和CSS 3。 IE从版本11开始,初步支持WebGL技术。IE8的JavaScript引擎是Jscript,IE9开始用Chakra。 Trident内核的常见浏览器有:IE6、IE7、IE8(Trident 4.0)、IE9(Trident 5.0)、IE10(Trident 6.0);360安全浏览器(1.0-5.0为Trident,6.0为Trident+Webkit,7.0为Trident+Blink);360极速浏览器(7.5之前为Trident+Webkit,7.5为Trident+Blink);猎豹安全浏览器(1.0-4.2版本为Trident+Webkit,4.3及以后版本为Trident+Blink);UC浏览器(Webkit内核+Trident内核);2.Gecko Gecko(Firefox内核),Gecko的特点是代码完全公开,也是一个跨平台内核; JavaScript引擎是:SpiderMonkey(1.0-3.0)/ TraceMonkey(3.5-3.6)/ JaegerMonkey(4.0-)。3.PrestoPresto(Opera前内核) (已废弃): Opera12.17及更早版本曾经采用的内核,该款引擎的特点就是渲染速度的优化达到了极致,然而代价是牺牲了网页的兼容性。Opera现已改用Google Chrome的Blink内核。4.WebkitWebkit(Safari内核,Chrome内核原型,开源):它是苹果公司自己的内核,也是苹果的Safari浏览器使用的内核。Webkit引擎包含了WebCode排版引擎和JavaScriptCode解析引擎,分别是从KDE的KHTML和KJS衍生而来,它们都是自由软件,在GPL条约下授权,同时支持BSD系统开发。Chrome、360极速浏览器以及搜狗高速浏览器也使用Webkit作为内核(在脚本理解方面,Chorome使用自己研发的V8引擎)。5.Blink 这是由Google和Opera Software开发的浏览器排版引擎,Google计算将这个渲染引擎作为Chromium计划的一部分,并且在2013年4月公布了这一消息。这一渲染引擎是开源引擎Webkit中WebCore组件的一个分支,并且在Chrome(28及往后版本)、Opera(15及往后版本)和Yandex浏览器中使用。以上提到的引擎为排版引擎,浏览器还需搭载js引擎。Chromium就是Blink排版引擎+V8 js引擎。Chromium是由Google主导开发的网页浏览器。目前国内浏览器使用情况认识浏览器 内核Chrom (Blink)IE (Trident)Edge (EdgeHTML (Trident的一个分支))Firefox (Gecko)Safari (WebKit)Opera (Blink)360安全浏览器 (Trident + Chromium)360极速浏览器 (Trident + Chromium)QQ浏览器 (Trident + Chromium)搜狗浏览器 (Trident + Chromium)百度浏览器 (Trident + Chromium)2345浏览器 (Trident + Chromium)UC浏览器 (Trident + Chromium)

March 4, 2019 · 1 min · jiezi

jSearch(聚搜) 一款专注内容的chrome搜索扩展

jSearchjSearch(聚搜) 是一款专注内容的chrome搜索扩展,一次搜索聚合多平台内容。下载: https://github.com/dubox/jSea...jSearch(聚搜)是一款专注内容的搜索扩展,一次搜索聚合多平台内容,支持自定义站点搜索收藏夹搜索、历史记录搜索支持地址栏搜索超级方便的搜索快捷键帮你开启新世界大门。&使用方法Ⅰ.将浏览器默认搜索引擎设置为 google 或百度时,可使用地址栏搜索直接进入本插件。Ⅱ.使用快捷键呼出快捷搜索框(jBar),进行快捷搜索快捷键支持:呼出:Ctrl+j、space(在可编辑区域无效)、j(在可编辑区域无效) , 收起:Ctrl+j、tab、esc、space(只在搜索框没有内容时有效)&隐私声明本人以人格担保:插件不上传任何用户隐私信息&插件安装方法https://jingyan.baidu.com/art…&插件截图&鸣谢感谢开源项目:VUE 、iview 、hotkeys.js、vue-dragging

March 1, 2019 · 1 min · jiezi

GET和POST两种基本请求方法的区别

GET和POST两种基本请求方法的区别getpost回退回退时无害会再次提交请求记录URL可被记录, 用于再访问不可以缓存主动缓存可手动设置编码方式只进行url编码多种编码方式参数长度有长度限制无参数数据类型只接受ASCII字符无限制参数位置通过URL传递在Request body中安全性低高参考https://www.cnblogs.com/logsh…

March 1, 2019 · 1 min · jiezi

给你的百度画个妆

介绍现在很多网站做的简单便捷,比如思否(????舔狗应有尽有),但是百度依然是十年前的画风,而且不管你搜索什么都充斥着各种广告,体验极差。今天我们就一起给百度美化一下。????Tips: 这其实是个插件推荐博,技术党勿喷。本文首发于我的个人网站 Timbok.top安装可加载本地CSS的Chrome插件Chrome上此种插件有很多,这里我选择的是Stylish下载美化规则这个插件的官网有很多大佬写好的美化规则,大家可以自己选择自己喜欢的。除了百度,你还可以下载腾讯、新浪、谷歌、优酷等美化规则。在此插件上新建一个CSS样式如果你不想做伸手党,且你有一点点CSS功底,那么你可以自定义样式。你想想你女朋友觉得某网页的一个模块很丑,想让它消失。你站起来对她说:无知又愚蠢的女人,让我来帮你搞定吧。????然后你从30块的背包里掏出你的mbp,找到模块的类名,dispaly: none,三步搞定。????最后对女朋友说,你不喜欢的我都让她消失。????可是你没有女朋友。是你没有女朋友你没有女朋友。没有女朋友。在深色框内你就可以编写自己的样式了。以下是我编写的百度美化规则,简易的美化了一下百度然后屏蔽了一些广告。喜欢❤可以使用哟。/* 去除首页广告 /.s-mancacrd-main { display: none;}#s_top_wrap { display: none;}.no-qrcode-layer { display: none;}/ 去除搜索结果广告 /#content_left > .result-op { display: none;}/ 美化搜索结果 /.result { padding: 30px; border-radius: 5px; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2);}.result:hover { padding: 30px; border-radius: 5px; box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);}.result > div { margin-top: 10px;}#u > a { text-decoration: none;}.s_form { zoom: 1; height: auto; padding: 5px 0 15px 15px;}#s_tab { background: #f8f8f8; line-height: 36px; height: 38px; padding: 80px 0 0 127px; float: none; zoom: 1;}#content_left { padding-left: 127px;}.s_ipt_wr { height: 40px;}.s_btn { height: 42px}.s_ipt { margin: 9px 0 0 7px;}#result_logo { margin: 9px 0 0;}.ipt_rec:after { display: none;}.ipt_rec { width: 0px; height: 0px;}.nums { margin: 0px 0 0 127px; height: 42px; line-height: 42px;}#content_right { margin-top: 80px;}.cr-title-sub { text-decoration: none;}#head .bdsug { top: 42px;}/ 搜索框 */.bdsug li { padding: 5px;}.bdsug li:hover { padding: 8px;}效果对比:总结好了。又水了一篇文章。????喜欢请点赞。彳亍口巴? ...

February 28, 2019 · 1 min · jiezi

chrome浏览器下audio自动播放的hack

前言也许很多前端遇到过这个需求:消息提醒。一般来说,可以简单的实现绝不会用复杂的方式,audio标签提供了这个功能。但是,新版的chrome浏览器禁止了js自动播放音频的功能,见鬼了。音频播放<audio src="../audio.mp3" id=“myaudio” class=“hide”></audio><script>var audio = document.getElementById(‘myaudio’);audio.play();</script>这是最简单的音频播放脚本,但是在chrome下,抛出异常:Uncaught (in promise) DOMException,原因是这种操作必须由用户发起。当然,这不是特例,像F11全屏操作,浏览器也是禁止脚本操作的。但是我们有这个需求,怎么破?绝不屈服的脑洞能否不操作play呢?chrome不仅禁止了脚本自动调用play,还禁止了audio的autoplay属性。但是,如果音频是静音状态,autoplay属性还是可以生效的。意思是,你可以播放,但是不能干扰用户的视听。这就给我们提供了一个hack的方法:默认开启音频的静音播放,而且是循环播放,当我们需要提醒用户的时候,把声音打开,播放时间设置为0秒,播放完毕,关掉声音,继续循环。是的,音频一直在播放,但是用户听不见。只有我们想让用户听见的时候才能听见,客观上也能实现需求。具体实现<audio src="../audio.mp3" muted autoplay loop id=“myaudio” style=“display: none”></audio><script>/** muted 静音* autoplay 自动播放* loop 循环播放*/var audio = document.getElementById(‘myaudio’);var t1 = 3e3;//如果是轮询,这个时间必须大于音频的长度。如果是webscoket,应该设置一个状态play,避免重复播放,如下:var t2 = 2500;//音频的长度,确保能够完整的播放给用户var play = false;funcrion run(){ if(play){ return false; } audio.currentTime = 0;//设置播放的音频的起始时间 audio.volume = 0.5;//设置音频的声音大小 audio.muted = false;//关闭静音状态 play = true; setTimeout(function(){ play = false; audio.muted = true;//播放完毕,开启静音状态 },t2);}setInterval(function(){ run();//假装在轮询服务器,或者从websocket拉取数据},t1);</script>结语这个方法经过在chrome上的实测,可以使用。但是其他浏览器未做测试,据说有的浏览器,似乎是IE不支持muted属性,限于操作系统,没做测试,如果在IE运行有问题,可以给我提个醒。

February 28, 2019 · 1 min · jiezi

绿色无广告版装机工具:微PE工具箱Win8/10内核版32/64位

你需要好用的WinPE工具箱吗,快来下载微PE工具箱Win10版使用吧。所谓微pe就是完整的精简,现在已有了W10PE核心,使用它可以灵活地安装到系统开机启动项、制作可启动U盘、移动硬盘以及生成ISO镜像,它不仅内核小并且工具十分的齐全,堪称是精华中的精华,同时该软件含32位和64位版本,大家可按需选择,需要制作的朋友快下载PE工具箱Win10版进行使用吧。PE系统:Win8内核截图PE系统:Win10内核截图附带一张图:电脑进入BIOS快捷键微PE装机工具下载地址

February 27, 2019 · 1 min · jiezi

你属于程序员中的哪种人?

当初的我们,初窥编程的世界,看着屏幕出现的“hello world”惊喜万分。想着计算机真的是世界上最神奇的东西,通过一行行的代码,我们居然可以和它交流,让它帮我们做事情。可是后来,我们好像都变得不一样了。我们变了为什么要学编程?每个人都有着不同的理由,但无非就两种:1. 兴趣,本身就喜欢编程,希望将来能从事这一职业。2. 混口饭吃,高薪职业,随便学学就能去搬砖,还不用风吹日晒。无论出于哪种,当编程成为我们的职业,我们似乎从它的朋友,变成了它的仆人。日复一日地忙碌,只为从它的手中,赚取一些酬劳。我们好像再也没有为写程序而感到兴奋,再也没有那种创造时的快乐了。当初的兴趣变得逐渐无味,上司分配的任务总是干不完,需求总是变了又变。没有时间去想更好的解决方案,只能埋头编码。即使知道那样写不合理,但是为了明天上线,为了早点回去睡觉,只能暂时先那样写了,等出了问题再说,也有可能永远都不会出问题。但是,当时的情况不允许我想那么多,其他组的兄弟都等着赶紧上线,休息一下。说好的轻轻松松搬砖的呢?项目经理给我的需求图是个什么东西,我一点也没看明白。他告诉我“先这样,然后再这样,然后再那样,很简单的,今天下班前得搞出来,明天客户要看,我还要准备明天的ppt,有不会的地方问问其他同事"。问同事,“啊,我这边也没时间,你copy之前的代码,改改就好了”。回顾初心,是什么变了?是编程不再像以前那么有吸引力了,还是我们自己放弃了编程,成为了工作的奴隶?在我们的工作中,我们总是可以看到那些令人讨厌的程序员:骄傲的说教者他们是行业里的前辈,工作年限比较多,对各方面的技术都有所涉猎。做过的各种各样的项目,跳槽过多家公司。对于公司的技术框架总是指手画脚,“这个框架太落后了,咱们公司怎么还在用”,“你写的代码太烂了,怎么能这样写”,“你真的该去学学xxxx了,这是趋势,未来十它的天下”。他们的能力毋庸置疑,通常自己一个人就能完成几个人的活。但是当与别人合作的时候,就会出现问题。因为对自己的技术能力过于自信,总是贬低别人的方案,总是希望所有的都按自己的想法来。与别人讨论的时候总是提一大堆专业名词,大数据,分布式,解耦,降级,微服务。。。。。。从来不考虑实际的业务和使用场景,不停的增加系统的复杂度,不考虑项目的进度。在他们眼中,干翻华为,阿里是分分钟的事。向他求助,只能听他一通说教,然后告诉你实在不会,百度一下,copy下别人的代码就好了,好像回答你的问题就是在浪费时间。懒惰的投机者他们是通过培训进入的新人,对于编程并没有多少兴趣,只不过是混口饭吃罢了。基础掌握的尚不牢靠,凭借着包装简历进入公司。因为要价低,差不多能搬砖的话,老板都要了。对于公司的框架不花时间去研究,对于框架的工作流程不清楚,每天总是各种各样的问题。“前辈,这个项目该怎么本地跑呀”,“前辈这块报了空指针,是什么原因阿 ”,“这个时间该怎么格式化阿”。。。。。。在他们眼中,只要是自己不会的就应该来问你,没有什么比这更快的了。你只要告诉我该怎么做就好了,这样搬砖是极好的了。百度是个什么东西,他们从来不知道,即使知道,也不知道自己的问题该怎么搜。他们不仅无知而且还懒惰,他们喜欢张口接饼。同样的问题,即使你这次告诉他了,下次出现依然还会问你。因为每天出现的问题对于他们来说都是新问题,从来不花时间去总结,去研究。反正总会有其他人帮忙解决。忙碌的悲观者他们不善言谈,喜欢自己研究,怕被人笑话。总是无条件的接受上级安排的任务,对自己的能力不自信,总是害怕出问题。每次上级分配任务的时候,都心里默默祈祷,千万不要给我分配难的。面对工作,总是害怕自己解决不了,于是在没有分析好需求的情况下,就开始写代码,总希望早点写完,早点完工。在测试的时候,总是被测出大量的bug,于是又进入了拆东墙补西墙的节奏。永远修不完的bug.每天都有忙不完的任务,没有时间停下来思考自己写的代码到底有没有改进的地方。之前出现bug的地方,是不是在这个项目中能够避免。重复的CRUD让人变得麻木,对编程失去兴趣,对工作只有无穷的抱怨。编程本来是一件脑力工作,但是现在却成了体力劳动。能够粘贴复制的代码,它存在的价值又有多少。盲目的框架追求者现在的编程早已经摆脱了之前的自己造轮子的过程,总是有各种各样的轮子让你使用,甚至连发动机都给你造好了。每天都在出现各种各样的框架,后端还好一点,前端真的是百花齐放。有些人是出来一个框架就学一个,照着官网的例子自己练习。完了就和别人炫耀,自己又学会了框架。其实懂得人都懂,学习一个框架的成本并不怎么高,只要有文档,学会使用并不是多么难得事情,关键是懂不懂框架的思想,它到底能不能在你的业务中发挥作用?而不是专门为了学习而学习。尽管学会了各种各样的框架使用,只能说你增加了自己的就业机会,但是你能不能在项目组中承担更多的角色,还是要看你的技术积淀,这种东西不是靠学会使用一堆框架就能有的。花那么多时间去研究使用,而不是去了解底层的原理,架构的思想,那么你永远只能是使用者,不会有太大的进步。喜欢学习是好事,但一定要学习真正的东西。我们无法阻止新事物的来临,也不必担心旧的被淘汰,新的都是在旧的基础上的改进,那些用的久的技术从来不会被淘汰。只是人们自己选择了新的东西,选择权永远在自己手上。眼高手低的吹水者各个行业从来都不缺左右逢源之人,他们是职场上的宠儿,总是能升值加薪。总有一些人,自以为自己很聪明,掌握的比别人多。对于那些专业名词,它总能给你说道一二。好像什么他都知道,每当公司决定使用某一项技术的时候。他们就要装作自己很懂得样子bb一番。“哦,这个技术我学过,我知道,相当牛鼻的,阿里,华为一些大公司都在用。。。。。。”,引来大家的仰慕。等工作真正开始的时候,你就会发现它们永远是拖后腿的那一个。他口中的学过,就是看过别人写的介绍文章,或是在qq群力听别人说起过。它们是个大qq群的活跃人士,为广大网友排忧解难,在他们眼中代码不是用手写的,是用嘴说的。嘴上说的好,代码就能写得好。每次出问题时,总是给你能说出一大堆,但永远说不到点子上。也许少花点时间在吹水上,他们能进步的更快点。结束语随着时间的推移,我们自己可能也会变成自己曾经最讨厌的那种人。以前我不相信,可是后来才发现生活不会放过任何一个人。为了自己的职业发展,为了应对互联网的裁员潮,要时刻提醒自己不要成为那样的人。脚踏实地,不会就学,多思考,多合作,多交流,多实践。相信编程可以改变未来,我们正在做一件伟大而又长远的事情。加油。往期文章:爬虫爬了点视频资源分享给大家Java开发人员常用库推荐React框架Umi实战(1)简介与使用脚手架

February 27, 2019 · 1 min · jiezi

三个月可更改用户昵称两次

前言在实际的项目需求中,我相信很多人都能遇到如标题所说的问题,比如:一个月可修改昵称一次,或者一年可修改昵称三次;我下面的方法也比较简单,是在与朋友的讨论中得到的。需求背景为了表述的更清晰,我这里就简化了需求,如下:每三个月(这里按一个月30天来算, 也就是90天)可更改用户昵称两次,如果三个月内没有用完两次,则下一个三个月拥有的更改次数重置,还是两次。准备工作建立用户数据表 users (这里只列出该文章需要的字段):CREATE TABLE users ( id int(10) unsigned NOT NULL AUTO_INCREMENT, username varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT ‘添加时间’, create_time timestamp NULL DEFAULT NULL COMMENT ‘添加时间’, username_update_num int(10) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘用户昵称修改次数’, PRIMARY KEY (id),) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT=‘用户主表’;我使用的是 laravel 框架,使用其内置中间件进行过滤应用程序 HTTP 请求;具体代码先上中间件里的代码,可跟着注释看。这里不着重写中间件的实现方式了,如需了解,请点击 中间件 public function handle($request, Closure $next) { /** * 目前要解决的问题是: 每三个月(90天)可更改昵称 2 次 * 下面是解决逻辑 / // 得到该用户信息 $user = User::where(‘id’, session(‘uid’))->first(); // 用户注册的时间,create_time 使用的是 timestamp 类型,所以要转换一下,方便计算 $create_time = strtotime($user->create_time); /* * 计算从注册时间起一共过去了几个 90天,也就是过了几轮 * 当前时间减去注册时间 除以 90天的秒数 = n 轮 * 得到的数值 n 很少有整数,比如:1.2 ; * 此时需要进一法处理,因为只要比90天多,哪怕多一秒也要进入下一轮 / $n = ceil( round( (time() - $create_time) / (90 * 24 * 3600), 2) ); /* * 每 90 天可修改两次,每修改一次,数据表 username_update_num + 1 * 现总修改次数:用户自注册时间起至今,共修改的多少次 * 每轮拥有修改次数:每 90天用户有两次修改机会 * 现总修改次数 / 每轮拥有修改次数 = 现修改到第几轮;用 $a 表示 / $a = $user->username_update_num / 2; // 这里写的是 >=,实际情况下,$a 是不可能大于 $n 的 if($a >= $n){ return response()->json([‘code’ => 0, ‘message’ => ‘用户昵称三个月内只能修改两次,您的次数已用完’, ‘data’ => ‘’]); }else{ // 说明前 ($n - 1) 轮中有未用完的次数 if( ($n - $a) > 1){ // 手动更改数据库,补全修改次数,也就是默认以前的每轮都把两次机会用完 $user->update([‘username_update_num’ => (($n - 1) * 2)]); } } /* * 这里是判断必传参数,与上面逻辑没有联系 / if(empty($request->post(‘username’))){ return [‘code’ => 0, ‘message’ => ‘用户昵称不能为空’, ‘data’ => ‘’]; } if($request->post(‘username’) === $user->username){ return [‘code’ => 0, ‘message’ => ‘修改后的昵称不能与原昵称一致’, ‘data’ => ‘’]; } return $next($request); }上面中间件的内容已经写完了,可能会让人有些迷糊,先别急,因为还没有写完,上面只是中间件的内容,是为了拦截已经没有机会修改昵称的用户,以及处理没有用完次数的用户,请接着看下面的控制器 UserController.php 的内容:UserController.php /* * 用户昵称修改 (三个月可修改两次) * * @param \Illuminate\Http\Request * @return \Illuminate\Http\Response */ public function usernameUpdate(Request $request) { $user = User::where(‘id’, session(‘uid’))->first(); $data = [ ‘username’ => $request->post(‘username’), ‘username_update_num’ => $user->username_update_num + 1, ]; if( !$user->update($data) ){ return [‘code’ => 0, ‘message’ => ‘更改用户昵称失败’, ‘data’ => ‘’]; } return [‘code’ => 1, ‘message’ => ‘更改用户昵称成功’, ‘data’ => ‘’]; }如上述 UserController.php 控制器,因为用户是否满足更改昵称条件已经在中间件里做过判断,所以能进来控制器的请求,均是有修改昵称次数的用户,只需直接更改更改昵称且更改次数 + 1 即可。总结这篇文章所讲述的方法适合同种类型的需求,可根据需求更改相应参数。细节上的处理不多,比如:实际上每个月的天数不一定是 30天,这里不做讨论,可相应处理时间即可。主要还是记录该种处理方法,也一定有比这种更好的方法!道路阻且长,仍需不断前行! ...

February 26, 2019 · 2 min · jiezi

CSDN 文章自动显示全文

在查看CSDN博客文章的时候,如果文章比较长,下面的部分会被隐藏,必须要点击一下阅读更多才能全文。像这样:下面来看一下如何实现自动显示全文。所需工具Chrome 浏览器Stylebot 插件 (用于自定义网页样式)随便打开一篇 CSDN 文章点击Stylebot 插件(浏览器地址栏右侧):选择 Open Stylebot:点击 Edit CSS 按钮:粘贴以下代码:div.hide-article-box.hide-article-pos.text-center { display: none;}#article_content { height: auto; }点击 Save 按钮,搞定。效果:以后再打开 CSDN 博客就会直接显示全文了。

February 25, 2019 · 1 min · jiezi

这些资源网站为什么能获得5万知乎大佬推荐,而我错失了什么吗?

现在很多朋友可能只要是一听到资源网站,想必就会好奇是什么网站,用用看是不是由说的那么厉害!其实资源网站给我们的诱惑是在是太大了,其主要原因是可以帮助我们搜索到很多意想不到的资源!为了回应大家的需求,这里为大家整理了一些在知乎网上被5万大佬点赞推荐的资源网,不知道你用过多少呢?基本上只要是全用过的朋友都能算的上大佬!Ps:以下网站就可在百度搜索找的到!No.1:影视搜索网站网站名称:疯狂影视搜索全能电影电视剧搜索网址,直接在搜索栏里面输入自己喜欢的电影就可以找到相应的播放位置,就连最新上映的电影或过去的老片都可以找到,就比如最近上映比较火的【流浪地球】也都能够找到,是不是非常给力呀,打开后就可以直接观看,没有广告!No.2:磁力搜索网站网站名称:CiliBaBa这款磁力资源网站不经可以搜索到影视,还可以搜到音乐、书籍,就想万能的百宝箱一样什么都有,什么都行!NO.3:短视频下载网站网站名称:小视频下载自己在播放器上看到视频下载不了只能缓冲,不妨试试这个网站,如果你是新媒体专门搞短视频的话也可以收藏一个,想必能够帮上你大忙!NO.4:网盘搜索网站网站名称:网盘007非常厉害的网盘搜索网站,里面有很多不错的资源网站,一篇电子书、资料什么的哦度可以在网盘就能够找到!以上就是今天分享的资源网站,感兴趣的小伙伴可以去试试!希望能够在平时的上网中帮到你忙!

February 22, 2019 · 1 min · jiezi

url输入后的故事

小刘(浏览器)是一个批发商, 小李(普通用户)是一个超市的进货经理,一天小李经理来到了小刘老板的店铺,说:我‘要(www.baidu.com)类的所有东西’,小刘老板说:‘我得用我们的行话(IP)告诉底下的人你要的东西他才知道你要啥’,小刘老板从身上掏出一个本本(浏览器缓存的DNS),找了一会,喃喃自语到:‘怎么会没有呢’,说者无心听者有意,小李经理说道:‘这个可不是新商品,上次在你这订过,你是不是忘啦’,小刘老板陪笑道:‘本本太小时间太久忘了、忘了(缓存过期或者大小溢出)’,这可急坏了小刘老板了,此时小刘老板一拍脑门,天无绝人之路,上次进货可是有登记表(操作系统存储的DNS解析)呢,待我前去查一究竟,此时小李经理眉头渐舒,说道:‘耽误会时间没事,你可别找不到啊’,说时迟那时快,小刘老板已经把登记表翻了一遍了,赶紧陪笑道:‘您别急,待我打电话去总部业务部(DNS服务器)问一哈,市业务部没有还有省业务部,这个都登记过,肯定能找到’,铃铃铃,果不其然,大金牙一露,小刘老板找到了!作为老板熟悉业务的小刘赶紧把 谁要的东西(源地址),从哪去拿(目标地址119.75.217.109),是否加密(https协议)等等写在纸上,不过且慢,写着写着小刘老板突然感觉这个很熟悉,“我这个店是不是就有呢”,在仓库(浏览器缓存)又是一番折腾,没有,这个真没有,咋办赶紧把纸条交给小川吧,让他去拿货,很快小川拿到了纸条,小川(TCP传输层)自幼父母双亡,初中辍学做苦力,斗大的字不认识几个,埋怨到,你自己打电话让他们送过来多好,非得让我为难,你写的那么长让我什么时候说完,呜。 拿到了进货单,小川把小刘老板的话进行了逗号分隔(报文分段),好这下不用磕磕巴巴的告诉快递小哥小汪(IP网络层)了,小川虽然没什么文化,长的也丑,但是很谨慎,这也是他在这的价值,拿起电话,拨通了郊区仓库负责人(目标服务器)的电话,“喂,老四,你那能送货吗” “原来是结巴川啊,能送货啊” “好,我知道了,还有再说我结巴我跟你翻脸”(三次握手),挂断电话,被叫外号的小川一脸不悦的冲快递小哥说道,这是我‘特殊处理过的进货单,你给送去吧,迟到了我投诉你’,“放心吧,您内”,看了眼收件人,快递小哥道:“原来是这,我去ARP本(ARP协议)上看看具体地址(MAC地址),瞧好吧您就”,左手拿着小刚戳,右手捂着碳素笔,“得嘞”,“链路快递店”(链路层)是快递小哥所在单位,把包裹送给了司机比特大叔(电流),比特大叔开着解放牌大卡车拉着包裹哼着《走向新时代》上了通往郊区的高速,中间的各个路口(路由器、交换机)交换通关文牒,最终到达郊区的“链路快递店”,收到了小川的包裹,让快递小哥(IP网络层)-结巴大川(TCP传输层)-负责人小S(服务器)在本地仓库搜索进货单所有的东西,然后登记出货单(响应头)有的标上200,没有的标404,已经转移的标301,最后将出货单和商品一起原路返回。演员表角色名称扮演者小刘浏览器小李普通用户小川TCP传输层快递小哥小汪IP网络层老四目标服务器比特大叔电流

February 22, 2019 · 1 min · jiezi

Google Chrome

Google Chrome从URL输入到显示页面简要流程: DNS解析(将域名解析成IP地址)–>TCP链接(TCP三次握手)–>发送HTTP请求,–>服务器处理请求并返回HTTP报文–>浏览器解析渲染页面–>断开连接(TCP四次挥手).URL统一资源定位符 scheme://host.domain:port/path/filenamescheme:定义因特网服务类型,常见的协议:http,https,ftp,file.host:定义域名主机,http的默认主机是www.domain: 定义因特网域名,如:baidu.comport: 定义主机上的端口号,如http默认端口号是80path:定义服务器上的路径.filename:定义文档/资源的名称.TCP的三次握手先由客户端(浏览器)发送一个请求到服务器.服务器发送请求数据到客户端,客户端发送信息服务端. 发送HTTP请求TCP三次握手之后,开始发生HTTP请求报文,请求报文由请求行,请求头.请求体组成.服务器发送请求并返回HTTP报文略浏览器解析渲染页面 HTML与CSS同时解析生成HTML结构树和CSS结构树,两树合合并成渲染树,渲染树计算好信息,绘制页面.断开链接当数据传送完毕,需要断开TCP链接,发送四次挥手.

February 21, 2019 · 1 min · jiezi

网站性能优化

性能优化页面性能优化(1)函数节流:一个函数执行一次后,只有大于设定的执行周期才会执行第二次.(2)函数防抖:一个需要频繁触发的函数,只有在规定时间内,只让最后一次生效,前面的不生效.(3)资源压缩与合并(html压缩,css压缩,js压缩,文件合并与压缩)(4)非核心代码异步加载(5)利用浏览器缓存.(6)预解析DNS.

February 21, 2019 · 1 min · jiezi

谷歌Chrome开展实验,解决HTTPS混合内容错误

HTTPS证书申请谷歌Chrome团队将在本周开展一项实验,试图解决HTTPS 混合内容(mixed content)错误。HTTPS混合内容错误一直是网站所有者推进HTTPS加密的一大阻碍,Chrome团队对这一问题的解决将更有利于推动HTTPS加密普及。HTTPS混合内容错误是指,初始的HTML(网页)通过安全的HTTPS连接加载,但也页面中其他资源(如图像、视频、样式表、脚本)却通过不安全的HTTP连接加载的时候,就会出现混合内容错误(也就是不安全因素)。因为HTTP和HTTPS内容都被加载显示在同一个页面上,而初始请求是基于HTTPS加密的,现代浏览器会对这类内容显示警告,指示用户此页面含有不安全资源。在过去几年中,混合内容(mixed content)一直是浏览器制造商和其他推动HTTPS迁移的组织面临的一个大问题。浏览器混合内容错误有时被认为是阻止用户访问网站,这让许多网站运营者不敢迁移到HTTPS,许多人担心他们支持HTTPS没有带来任何实际好处却失去流量收入。解决Web浏览器中出现的混合内容错误,可能是说服网站运营者转向HTTPS的最后一个主要障碍。本周,Google工程师在Chrome中推出了一项实验,他们将浏览器配置为自动将任何混合内容升级为完整的HTTPS。Chrome会通过秘密地将资源的URL(例如图像,视频,样式表,脚本)从HTTP版本更改为HTTPS来实现此目的。如果HTTPS链接上存在相同的资源,则一切都正常加载。如果这个HTTPS资源不存在,Chrome会记录该错误并执行为此实验配置的众多方案中的一种。一般认为,网站所有者升级他们的网站使用HTTPS时,他们可能忘记更改网站源代码,一些内容即便可以通过HTTPS加载,但却被遗留使用HTTP加载。此实验的目的是让Google工程师可以深入了解,如果Chrome默认情况下自动将所有混合内容网站更新为HTTPS,那么多少网站会连接失败,以及混合内容HTTP网址的最佳后备策略是什么。如果破损的链接和网站的百分比很小,谷歌工程师很可能会考虑在主Chrome浏览器中发布这个“自动更新到HTTPS”的功能,并朝着更安全的网络迈出新的一步。目前,谷歌打算将该实验推广到其Chrome Canary大约百分之一的用户群中(以能够启用chrome://flags/#enable-origin-trials 为标记)。混合内容错误在浏览器层级得到解决,将大大推动HTTPS加密的应用普及,网站所有者可以更加安心地升级HTTPS加密。沃通SSL证书由全球信任顶级根签发,支持Windows、安卓、iOS、JDK以及Firefox、Chrome等各类浏览器、操作系统和移动终端,具备广泛兼容性,可用于网站、APP、微信小程序等各类HTTPS应用场景。沃通CA资深技术团队免费提供一对一指导,帮助您正确部署HTTPS加密,全面解决混合内容错误、不安全加密算法等可能造成安全隐患的HTTPS部署问题。关注沃通SSL证书 www.wosign.com

February 21, 2019 · 1 min · jiezi

让chrome浏览器支持跨域

在chrome快捷键的目标路径的末尾添加下面语句:–args –disable-web-security –user-data-dir

February 20, 2019 · 1 min · jiezi

安装chrome插件

下载了crx后,将.crx结尾的文件重命名以为.zip或者.7z结尾结尾的文件(只要是当前电脑支持的压缩格式即可)。然后将其解压,解压后直接将解压后的文件拖到chrome的扩展程序中去即可。注意: 有的需要将解压后的文件中_metadata文件名修改成metadata才可以。下载crx文件的官网:https://www.crx4chrome.com/

February 20, 2019 · 1 min · jiezi

SpringBoot 实战 (八) | 使用 Spring Data JPA 访问 Mysql 数据库

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言如题,今天介绍 Spring Data JPA 的使用。什么是 Spring Data JPA在介绍 Spring Data JPA 之前,首先介绍 Hibernate 。 Hibernate 使用 O/R 映射 (Object-Relation Mapping) 技术实现数据访问, O/R 映射即将领域模型类与数据库的表进行映射,通过程序操作对象而实现表数据操作的能力,让数据访问操作无需关注数据库相关技术。Hibernate 主导了 EJB 3.0 的 JPA 规范, JPA 即 Java Persistence API。JPA 是一个基于 O/R 映射的标准协议(目前最新版本是 JPA 2.1)。所谓规范即只定义标准规制(如注解、接口),不提供实现,软件提供商可以按照标准规范来实现,而使用者只需按照规范中定义的方式来使用,而不用和软件提供商的实现打交道。JPA 的主要实现由 Hibernate 、 EclipseLink 和 OpenJPA 等完成,我们只要使用 JPA 来开发,无论是哪一个开发方式都是一样的。Spring Data JPA 是 Spring Data 的一个子项目,它通过基于 JPA 的 Repository 极大地减少了 JPA 作为数据访问方案的代码量。简而言之,JPA 是一种 ORM 规范,但并未提供 ORM 实现,而 Hibernate 是一个 ORM 框架,它提供了 ORM 实现。准备工作IDEAJDK1.8SpringBoot 2.1.3pom.xml 文件引入的依赖如下:<?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.3.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <groupId>com.nasus</groupId> <artifactId>jpa</artifactId> <version>0.0.1-SNAPSHOT</version> <name>jpa</name> <description>jpa Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!– JPA 依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!– web 依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!– mysql 连接类 –> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!– lombok 依赖 –> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!– 单元测试依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-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>简单说下,加入 JPA 依赖;mysql 连接类用于连接数据;web 启动类,但凡是 web 应用都需要依赖它;lombok 用于简化实体类。不会的看这篇旧文介绍:SpringBoot 实战 (三) | 使用 LomBokapplication.yaml 配置文件spring:# 数据库相关 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true username: root password: 123456# JPA 相关 jpa: hibernate: ddl-auto: update #ddl-auto:设为 create 表示每次都重新建表 show-sql: truerepository (dao) 层package com.nasus.jpa.repository;import com.nasus.jpa.entity.Student;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.repository.CrudRepository;import org.springframework.stereotype.Repository;/** * Project Name:springboot_jpa_demo <br/> * Package Name:com.nasus.jpa.repository <br/> * Date:2019/2/19 21:37 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * @author <a href=“turodog@foxmail.com”>nasus</a><br/> /@Repositorypublic interface StudentRepository extends JpaRepository<Student,Integer>, CrudRepository<Student, Integer> {}从上图,可以看出 JpaRepository 继承于 PangingAndSortingRepository 继承于 CrudRepository 。CrudRepository 提供基本的增删改查PagingAndSortingRepository 提供分页和排序方法;JpaRepository 提供 JPA 需要的方法。在使用的时候,可以根据具体需要选中继承哪个接口。使用这些接口的好处有:继承这些接口,可以使Spring找到自定义的数据库操作接口,并生成代理类,后续可以注入到Spring容器中;可以不写相关的sql操作,由代理类生成service 层package com.nasus.jpa.service;import com.nasus.jpa.entity.Student;import java.util.List;/* * Project Name:springboot_jpa_demo <br/> * Package Name:com.nasus.jpa.service <br/> * Date:2019/2/19 21:41 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * @author <a href=“turodog@foxmail.com”>nasus</a><br/> /public interface StudentService { Student save(Student student); Student findStudentById(Integer id); void delete(Integer id); void updateStudent(Student student); List<Student> findStudentList();}实现类:package com.nasus.jpa.service.impl;import com.nasus.jpa.entity.Student;import com.nasus.jpa.repository.StudentRepository;import com.nasus.jpa.service.StudentService;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/* * Project Name:springboot_jpa_demo <br/> * Package Name:com.nasus.jpa.service.impl <br/> * Date:2019/2/19 21:43 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * @author <a href=“turodog@foxmail.com”>nasus</a><br/> /@Servicepublic class StudentServiceImpl implements StudentService { @Autowired private StudentRepository studentRepository; /* * 保存学生信息 * @param student * @return / @Override public Student save(Student student) { return studentRepository.save(student); } /* * 根据 Id 查询学生信息 * @param id * @return / @Override public Student findStudentById(Integer id) { return studentRepository.findById(id).get(); } /* * 删除学生信息 * @param id / @Override public void delete(Integer id) { Student student = this.findStudentById(id); studentRepository.delete(student); } /* * 更新学生信息 * @param student / @Override public void updateStudent(Student student) { studentRepository.save(student); } /* * 查询学生信息列表 * @return / @Override public List<Student> findStudentList() { return studentRepository.findAll(); }}controller 层构建 restful APIpackage com.nasus.jpa.controller;import com.nasus.jpa.entity.Student;import com.nasus.jpa.service.StudentService;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/* * Project Name:springboot_jpa_demo <br/> * Package Name:com.nasus.jpa.controller <br/> * Date:2019/2/19 21:55 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * @author <a href=“turodog@foxmail.com”>nasus</a><br/> */@RestController@RequestMapping("/student”)public class StudentController { @Autowired private StudentService studentService; @PostMapping("/save”) public Student saveStudent(@RequestBody Student student){ return studentService.save(student); } @GetMapping(”/{id}") public Student findStudentById(@PathVariable(“id”) Integer id){ return studentService.findStudentById(id); } @GetMapping("/list") public List<Student> findStudentList(){ return studentService.findStudentList(); } @DeleteMapping("/{id}") public void deleteStudentById(@PathVariable(“id”) Integer id){ studentService.delete(id); } @PutMapping("/update") public void updateStudent(@RequestBody Student student){ studentService.updateStudent(student); }}测试结果其他接口已通过 postman 测试,无问题。源码下载:https://github.com/turoDog/De…后语以上为 SpringBoot 使用 Spring Data JPA 访问 Mysql 数据库的教程。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 20, 2019 · 3 min · jiezi

SpringBoot 实战 (二) | 第一个 SpringBoot 工程详解

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言哎呦喂,按照以往的惯例今天周六我的安排应该是待在家学学猫叫啥的。但是今年这种日子就可能一去不复返了,没法办法啊。前几天年轻,立下了一周至少更两篇文章的 flag 。废话少说,今天接着前文给你们带来了第一个 SpringBoot 工程的详解。第一个 SpringBoot 工程前文已经说过了 SpringBoot 工程的创建,这里不再赘述,还不会的朋友,请看下面这篇文章。如何使用 IDEA 构建 Spring Boot 工程学过编程的都知道,学习一门新语言的第一个项目肯定是 Hello World 。那这里也不例外,我们先创建一个非常简单的 Hello World 工程。给大家讲解 SpringBoot 的项目目录。创建信息如下:由于本文重点旨在讲解 SpringBoot 的项目目录。所以选择的依赖包非常简单,就选择 Web 足矣。SpringBoot 项目目录详解创建成功之后的 SpringBoot 项目目录如下,以下对各主要目录的作用进行讲解:src 是整个工程的根目录,基本上做 web 开发你的代码大部分都放在这里。其中 main 目录下放置的是你的 Java 代码;resource 目录,顾名思义就是放置配置文件、静态资源( static )以及前端模板( template )。test 目录就是放置你的单元测试代码。target 就是项目编译生成的目录,里面包含代码编译后的 class 文件以及一些静态资源和配置文件。External Libraries 这里放置了,pom.xml 导入的依赖包。其他没提到的目录都是不重要的。由上图项目目录,可以看到有几个文件,这些文件有些是我新建的,有些是项目生成的。下面我会讲解:pom.xml 这个文件是整个项目最重要的文件,它包含了整个项目的依赖包。Maven 会根据这个文件导入相关的我们开发需要的依赖包。代码如下:可以看到 pom.xml 中一共有 4 个依赖,其中只有 Junit 是我手动加入的,用于单元测试。其他的如 Spring Boot 启动父依赖、Spring Boot web依赖 、Spring Boot web test依赖都是创建项目时,勾选 web 选项卡而生成的。这几个依赖包的额作用就是 内嵌 Tomcat 容器,集成各 Spring 组件。比如 如果没有依赖 web 包 ,Spring 的两大核心功能 IOC 和 AOP 将不会生效。<?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> <groupId>com.nasus</groupId> <artifactId>helloworld</artifactId> <version>0.0.1-SNAPSHOT</version> <name>helloworld</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <!– Spring Boot 启动父依赖 –> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <dependencies> <!– Spring Boot web依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!– Junit –> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>HelloworldApplication.java 最为重要,它由项目生成,是整个工程的应用启动类 main 函数。代码由项目生成,代码如下:SpringApplication 引导应用,并将 HelloworldApplication 本身作为参数传递给 run 方法。具体 run 方法会启动嵌入式的 Tomcat 并初始化 Spring环境及其各 Spring 组件。需要注意的是,这个类必须放在其他类的上册目录,拿上述目录举个栗子, 若其他HelloWorldController.java 类位于 com.nasus.controller 下。则 HelloworldApplication.java 类必须放置在 com.nasus 下或者 com 下(层级必须大于其他 java 类)。否则启动项目访问会报 Whitelabel Error Page 错误,原因是项目扫描不到 @RestController、@RequestMapping 等注解配置的方法和类。package com.nasus.helloworld;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class HelloworldApplication { public static void main(String[] args) { SpringApplication.run(HelloworldApplication.class, args); }}HelloWorldController 是我手动编写的,代码如下:@RestController 和 @RequestMapping 注解是来自 SpringMVC 的注解,它们不是 SpringBoot 的特定部分。其中 @RestController 注解的作用是:提供实现了 REST API,可以服务 JSON、XML 或者其他。这里是以 String 的形式渲染出结果。 其中 @RestController 注解的作用是:提供路由信息,"/“路径的HTTP Request都会被映射到sayHello方法进行处理。 具体参考,Spring 官方的文档《Spring Framework Document》package com.nasus.controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * Project Name:helloworld <br/> * Package Name:com.nasus.controller <br/> * Date:2019/1/5 13:59 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> * Copyright Notice ========================================================= * This file contains proprietary information of Eastcom Technologies Co. Ltd. * Copying or reproduction without prior written approval is prohibited. * Copyright (c) 2019 ======================================================= /@RestControllerpublic class HelloWorldController { @RequestMapping("/hello”) public String sayHello() { return “Hello,World!”; }}写完 Controller 层的代码,我们就可以启动此项目。点击 IDEA 项目启动按钮,效果如下:好的程序必须配备完善的单元测试。HelloWorldControllerTest.java 文件是由我编写的主要作用就是测试 HelloWorldController.java 中的方法。这里用的是 Junit 依赖包进行单元测试,代码如下:这里的逻辑就是测试 HelloWorldController.java 的 sayHello 方法输出的字符是否是 Hello,World!package com.nasus;import static org.junit.Assert.assertEquals;import com.nasus.controller.HelloWorldController;import org.junit.Test;/* * Project Name:helloworld <br/> * Package Name:com.nasus <br/> * Date:2019/1/5 14:01 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> * Copyright Notice ========================================================= * This file contains proprietary information of Eastcom Technologies Co. Ltd. * Copying or reproduction without prior written approval is prohibited. * Copyright (c) 2019 ======================================================= */public class HelloWorldControllerTest { @Test public void testSayHello() { assertEquals(“Hello,World!",new HelloWorldController().sayHello()); }}编写完成之后,可以通过以下按钮启动单元测试类。测试结果如下:可以看到红圈框住的地方,出现这个绿色标志证明单元测试没问题。sayhello 方法的结果是对的。后语我为什么要写这种这么简单的教程?是这样的,我始终认为比我聪明的人有很多,但比我笨的人也不少。在中国有很多你认为众所周知的事,其实有一车人根本不知道,这篇文章哪怕只帮助到一个人,足矣。之后我打算出一个 SpringBoot 系列的教程,敬请关注与指正,本人也是一个小菜鸟在打怪升级中,如本文有不正确的地方,烦请指正。一起学习一起进步。以上就是我对 SpringBoot 工程的理解,希望对你们有帮助。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 19, 2019 · 2 min · jiezi

SpringBoot 实战 (七) | 默认日志配置

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言如题,今天介绍 springboot 默认日志的配置。默认日志 Logback默认情况下,Spring Boot 用 Logback 来记录日志,并用 INFO 级别输出到控制台。如果你在平常项目中用过 Spring Boot,你应该已经注意到很多 INFO 级别的日志了。默认日志长这样:2019-02-18 22:02:14.907 INFO 23384 — [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.7.Final}2019-02-18 22:02:14.907 INFO 23384 — [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found2019-02-18 22:02:15.110 INFO 23384 — [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}从上面的日志可以看到,日志输出内容元素具体如下:时间日期:精确到毫秒日志级别:ERROR, WARN, INFO, DEBUG or TRACE进程 ID分隔符:— 标识实际日志的开始线程名:方括号括起来(可能会截断控制台输出)Logger 名:通常使用源代码的类名日志内容日志依赖Logback 日志框架依赖于 spring-boot-starter-logging 包,但我们并不需要在 maven 中加入这个依赖,因为 spring-boot-starter其中包含了 spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 logback。控制台输出在 Spring Boot 中默认配置了 ERROR、WARN 和 INFO 级别的日志输出到控制台。我们可以通过两种方式切换至 DEBUG 级别:在运行命令后加入 –debug 标志,如:$ java -jar myapp.jar –debug在 application.properties 中配置 debug=true ,该属性置为 true 的时候,核心 Logger(包含嵌入式容器、hibernate、spring)会输出更多内容,但是你自己应用的日志并不会输出为 DEBUG 级别。多彩输出如果你的终端支持ANSI,设置彩色输出会让日志更具可读性。通过在 application.properties 中设置 spring.output.ansi.enabled 参数来支持。NEVER:禁用 ANSI-colored 输出(默认项)DETECT:会检查终端是否支持 ANSI,是的话就采用彩色输出(推荐项)ALWAYS:总是使用 ANSI-colored 格式输出,若终端不支持的时候,会有很多干扰信息,不推荐使用文件输出Spring Boot默认配置只会输出到控制台,并不会记录到文件中,但是我们通常生产环境使用时都需要以文件方式记录。若要增加文件输出,需要在 application.properties 中配置 logging.file 或 logging.path属性。logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.loglogging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log注:二者不能同时使用,如若同时使用,则只有logging.file生效 默认情况下,日志文件的大小达到 10MB 时会切分一次,产生新的日志文件,默认级别为:ERROR、WARN、INFO级别控制在 Spring Boot 中只需要在 application.properties 中进行配置完成日志记录的级别控制。配置格式:logging.level.*=LEVELlogging.level:日志级别控制前缀,*为包名或Logger名LEVEL:选项 TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF举例:logging.level.com.nasus=DEBUG:com.nasus 包下所有 class 以 DEBUG 级别输出logging.level.root=WARN:root日志以 WARN 级别输出自定义日志配置根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovyLog4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xmlLog4j2:log4j2-spring.xml, log4j2.xmlJDK (Java Util Logging):logging.propertiesSpring Boot 官方推荐优先使用带有 -spring 的文件名作为你的日志配置(如使用 logback-spring.xml,而不是 logback.xml),命名为 logback-spring.xml 的日志配置文件,spring boot 可以为它添加一些 spring boot 特有的配置项(下面会提到)。 默认的命名规则,并且放在 src/main/resources 下面即可如果你即想完全掌控日志配置,但又不想用 logback.xml 作为 Logback 配置的名字,application.yml 可以通过 logging.config 属性指定自定义的名字:logging.config=classpath:logging-config.xml虽然一般并不需要改变配置文件的名字,但是如果你想针对不同运行时 Profile 使用不同的日志配置,这个功能会很有用。 一般不需要这个属性,而是直接在 logback-spring.xml 中使用 springProfile 配置,不需要 logging.config 指定不同环境使用不同配置文件。springProfile 配置在下面介绍。多环境日志输出logback-spring.xml :<configuration> … <!– 测试环境+开发环境. 多个使用逗号隔开. –> <springProfile name=“test,dev”> <logger name=“com.example.demo.controller” level=“DEBUG” additivity=“false”> <appender-ref ref=“consoleLog”/> </logger> </springProfile> <!– 生产环境. –> <springProfile name=“prod”> <logger name=“com.example.demo.controller” level=“INFO” additivity=“false”> <appender-ref ref=“consoleLog”/> </logger> </springProfile></configuration>application.yml 增加环境选择的配置 active: devspring: profiles: active: dev datasource: url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8 username: root password: 123456根据 active 的环境,自动采用上面配置的 springProfile 的 logger 日志。后语以上 SpringBoot 默认日志的配置教程。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 19, 2019 · 2 min · jiezi

计算机科学基础

Bits(位), Bytes(字节), 晶体管, 逻辑门, 操作系统, 虚拟现实, 机器人通过一层层的抽象,来做出复杂操作。最底层的1和0, 到逻辑门, CPU, 操作系统, 互联网。目的:了解计算机在你的人生,以及社会中扮演什么角色。计算机是人类发展史上最伟大的发明,是怎么开始的; 它对未来还会有更大印象。Babbage工作室外面的街头音乐家很吵,很影响他干活。Babbage于1871年去世,当时分析机还没完成。Carrie Anne 是追星族!Lovelace写的分析机笔记是第一个算法。Ada Lovelace是第一位计算机程序员。Charles"计算机之父"Babbage设计了第一个可编程计算机。Tommy Flowers和他的团队在11个月内做出"巨像1号"。“巨像"计算机是第一个可编程电子数码计算机,建造出来是为了破解德国"洛伦茨"加密机。图灵的Bomba是电子机械密码破解机,但不算第一台计算机。树莓派是便宜的单板计算机,旨在推进计算机基础学习。Arduions用于快速制作嵌入式设备原型。2016年7月, 7-11用Flitey无人机进行了美国第一次合法的无人机送货。计算机早期历史提到的设备:算盘 -> 步进计算器 -> 差分机 -> 分析机 -> 打孔卡片制表机最早的计算设备是算盘。Computer从指代职业变成指代机器。机器里有名的是:步进计算器(类似汽车里程表,不断累加里程数),第一个可以做加减乘除的机器。炮弹为了精准,要计算弹道,二战时查表来做。但每次改设计了就需要做一张新表。(表是前人已经做好的)Charles Babbage提出了"差分机”(更复杂的机器,能近似多项式,多项式描述了几个变量之间的关系),在构造差分机期间(项目以失败告终),想出了分析机,分析机是通用计算机(不止是一种特定运算,甚至可以给它数据,然后按照顺序执行一系列操作,它有内存。因为先进,没有造成功)。Lovelace给分析机写了假想程序,因此成为了第一位程序员(“未来会诞生一门全新的,强大的,专为分析所用的语言”)。人口普查10年一次,Herman Hollerith 的打孔片制表机大大提升了效率(目的是分配联邦资金,国会代表,等等。)。1890年,人口普查要预计十三年时间来手工编制,等做完都过时。Charles Babbage(计算机之父)说:“随着知识的增长和新工具的诞生,人工劳力会越来越少”打孔片制表机用传统机械来计数,结构类似莱布尼茨的乘法器,但用电动结构连接其它组件。一种纸卡,上面有网格,用打孔来表示数据。当卡插入Hollerith的机器时,小金属针会到卡片上,如果有个地方打孔了,针会穿过孔,泡入一小瓶汞,联通电路,电路会驱动电机。计算机的价值:可以提升劳动力以及数据密集型任务,来提升利润。满足会计,保险评估和库存管理等行业,Hollerith成立了制表机器公司,在1924年与其他机械制造商合并,成为了“国际商业机器公司”(简称IBM)电子计算机提到的设备:继电器 -> 真空管 -> 晶体管20世纪的发展要求更强的计算能力。柜子大小的计算机发展到房间大小。哈佛 Mark1号,IBM 1944年做的。继电器一秒最多50次开关。继电器出bug(bug一词的来源)。1904年,热电子管出现。第一个真空管,改进后变成和继电器的功能一样。“巨人1号”计算机在英国 布莱切利园 首次大规模使用真空管,但编程麻烦,还要配置。1946年,宾夕法尼亚大学的ENIAC是第一个通用可编程计算机。1947年,贝尔实验室做出了,晶体管,晶体管有诸多好处,IBM 很快全面转向晶体管。硅谷的典故:很多晶体管和半导体的开发都是这里做的。而生产半导体最常见的材料是硅。肖克利半导体 -> 仙童半导体 -> 英特尔。一战,二战(运输能力),登陆其它星球计划,产生的复杂度的增高导致数据量暴增,需要更多自动化,更强的计算能力。 柜子大小的计算机变成房间大小。(缺点:维护费用高,而且容易出错)Mark1号最大机电计算机之一。它有76万5千个组件,300万个连接点和500英里(804.65公里)长的导线。为了保持内部机械装置同步,它有一个50英尺的传动轴,由一个5马力的电机驱动。Mark1号大脑是继电器(用电控制的机械开关)1秒能够做3次加法或减法运算。一次乘法要花6秒,除法要花15秒。比较复杂的操作,比如,三角函数,幂运算,可能要一分钟以上或者更久。齿轮磨损,任何会动的机械都会随时间磨损。有些部件会完全损坏,有些则是变粘,变慢,变得不可靠,并且随着继电器数量的增加,故障概率也会增加。Mark1号大概有3500个继电器。需要频繁更换继电器,有些运算需要好几天。黑色的,温暖的机器也会吸引昆虫。Bug一词来源:1947年9月,哈佛Mark 2号的操作员从故障的继电器中,拔出一只死虫。Grace Hopper曾说:“从那时起,每当电脑出现了问题,我们就说它出了bug(虫子)”继电器继电器里,有根“控制线路”,控制电路是开还是关,“控制线路”连着一个线圈,当电流流过线圈,线圈产生电磁场,吸引金属臂,从而闭合电路。继电器控制的电子。和开关水的水龙头相似。这个控制电路可以连接到其它电路,比如:马达(作用:让计数齿轮+1)。缺点:继电器内的机械臂“有质量”,因此无法快速开关。在1940年代一个好的继电器1秒能翻转50次。但是不足以解决复杂的大问题。缺点:速度慢,齿轮磨损,难以维护。真空管在1904年,英国物理学家“约翰 安布罗斯 弗莱明”开发了一种新的电子组件,叫“热电子管”。把二个电极装在一个气密的玻璃灯泡里,这是世界上第一个真空管。其中一个电极可以加热,从而发射电子,叫做“热电子发射”另外一个电极会吸引电子,形成“电龙头”的电流。但只有带正电才行,如果带负电荷或中性电荷,电子就没办法被吸引,越过真空区域。因此没有电流。二极管:电流只能单向流动的电子部件叫做“二极管”。需求是:一个能开关电流的东西。而二极管只能做到开。在不久之后的1906年,美国“李 德福雷斯特”,在“弗莱明”设计的两个电极之间,加入了第三个“控制”电极。向“控制”电极施加正电荷,它会允许电子流动,但如果施加负电荷,它会阻止电子流动。因此通过控制线路,可以断开或闭合电路和继电器的功能一样。但重要的是,真空管内没有会动的组件。意味着损耗大大减低和每秒可以开闭数千次。因此“三极真空管”成为无线电,电话,以及其它电子设备的基础。计算机可能要上百个甚至上千个电气开关,造价非常昂贵。缺点:它们很脆弱,会烧坏。意义:从机电转向电子第一次大规模使用真空管的计算机是“巨人1号”,由工程师Tommy Flowers设计, 完工与1943年12月。“巨人1号”在英国的“布莱切利园”,用于破解通信。“巨人1号”有1600个真空管,总共造了10台巨人计算机,来帮助破解密码。 “巨人1号”被认为是第一个可编程的电子计算机。编程方法是:把几百根电线插入插板(类似老电话交换机),虽然“可编程”,但还是要配置它。ENIAC:电子数值积分计算机“ENIAC”,几年后在1946年,在“宾夕法尼亚大学”完成建造。这是世上第一个真正的通用,可编程,电子计算机。每秒可执行5000次十位数加减法。真空管很多,所以故障很常见,运行半天左右就会出现一次故障为了降低成本和大小,同时提高可靠性和速度,需要一种新的电子开关。晶体管1947年,贝尔实验室科学家发明了晶体管,一个全新的计算机时代诞生。晶体管的物理学相当复杂,涉及到量子力学。晶体管图片:它是一个开关,可以用控制电路来控制开或关。晶体管有两个电极,电极之间有一种材料隔开它们,这种材料有时候导电,有时候不导电,叫做:半导体。控制线连到一个“门”电极,通过改变“门”的电荷,可以控制半导体材料的导电性,来允许或不允许电流流动。贝尔实验室的展示晶体管,每秒可以开关10000次。而且比玻璃制作,小心易碎的真空管。晶体管是固态的晶体管可以远远小于继电器或真空管。生产半导体最常见的材料是硅。如今,计算机里的晶体管小于50纳米,而一张纸的厚度大概是10万纳米,晶体管不仅小,还超级快,每秒可以切换上百次,并且能工作十几年。布尔逻辑和逻辑门什么是二进制,为什么使用二进制,布尔逻辑。3个基本操作: NOT, AND, OR。解释3个基本操作。XOR异或开始抽象,不用管底层细节,把精力用来构建更复杂的系统。二进制计算机最早是机电设备,一般用十进制计数。比如:用齿轮数来代表十进制,再到晶体管计算机。只用开/关两种状态也可以代表信息,叫做二进制。为什么使用二进制:只需要表示true和false,两个值就够了。电路闭合,电流流过,代表"真"。二进制也可以写成1和0,而不是true和false,只是不同的表达方式。晶体管的确可以确定不只是开/关,还可以让不同大小的电流通过。只用“开”和“关”两种状态,减少难区分状态的情况。早期的三进制,三种状态,五进制,五种状态。问题是,状态越多,越难区分信号。干扰元素的存在,比如:手机快没电了或者附近有点噪音,因为有人在用微波炉,信号可能混在一起。而每秒百万次变化的晶体管会让这个问题变的更糟糕。所以把两种信号尽可能分开。有一个整个数学分支存在,专门处理“真”和“假”,它已经解决了所有法则和运算,叫做布尔代数。布尔,他有兴趣用数学式子,扩展亚里士多德基于哲学的逻辑方法,布尔用 逻辑方程 系统而正式的证明真理(truth)。在“常规”代数里,变量的值是数字,可以进行加法或乘法之类的操作,但在布尔代数中,变量的值是true和false能进行逻辑操作。布尔代数中有三个基本操作:NOT,AND和OR布尔逻辑NOT:NOT操作把布尔值反转,把true进行NOT就会变成false,反之亦然。晶体管可以很容易实现NOT操作,晶体管只是电控制的开关。有3根线:2根电极和1根控制线。控制线通电时,电流就可以从一个电极流到另一个电极。1根控制线作为INPUT, 2根电极作为OUTPUT可以把控制线,当作输入(INPUT),底部的电极,当作输出(OUTPUT)。所以1个晶体管,有一个输入和一个输出。如果打开输入(INPUT ON)输出也会打开(OUTPUT ON)因为电流可以流过。如果关闭输入(INPUT OFF)输出也会关闭(OUTPUT OFF)因为电流无法通过。改造成NOT GATE:与其把下面那根线当作输出,可以把输出放到上面。如果打开输入,电流可以流过然后“接地”;输出就没有电流,所以输出是OFF。用水来举例,就像家里的水都从一个大管子留走了,打开淋浴头一点水也没有。所以是输入是on,输出是off。如果当输入是off,电流没法接地,就流过了输出, 所以输入是off,输出是on。之所以叫做门,是因为它能控制电流的路径。NOT GATE画法:三角形前面加一个圆点AND:AND操作有2个输入,1个输出。如果2个输入都是true,输出才是true为了实现AND GATE,需要2个晶体管连在一起。这样有2个输入和1个输出。如果只打开A,不打开B,电流无法流到OUTPUT,所以输出是false。如果只打开B,不打开A,也一样,电流无法流到OUTPUT。只有A和B都打开了,OUTPUT才有电流。AND GATE画法:用D表示OR:只要2个输入里,其中1个是true,输出就是true。只有2个输入都是false,OR的结果才是false。实现OR GATE除了晶体管还要额外的线。不是串联起来,而是并联。然后左边这条线有电流输入,用“小拱门”代表2条线没有连接在一起,只是跨过而已。如果A和B都是off,电流无法流过,所以输出是off。如果打开A,电流可以流过,输出是on;如果只打开B也一样。如果A,B都on,结果是on。A和B都是off的情况:只打开A或者,只打开B的情况:OR GATE画法:用太空船表示XOR异或XOR就像普通的OR,但有一个区别:如果2个输入都是true,XOR输出false。想要XOR输出true,一个输出必须是true,另外一个必须是false。用晶体管实现XOR门:使用OR GATE, AND GATE, NOT GATE3种门来做XOR。有2个输入,A和B,还有1个输出。先放一个OR门,因为OR和XOR的逻辑表很像。只有1个问题,当A和B都是true时,OR的输出和想要的XOR输出不一样,需要的是false,所以要加多个门。如果加一个AND门,输入是true和false,输出会是true,也不是所需要的,但如果在AND的输出加个NOT就可以把true翻转成false了。最后加一个AND门,然后AND门的2个输入分别来自NOT和最原始OR,AND会收到false和true,因为AND需要两个输入都为true,都结果才是true所以输出是false。XOR画法:一个OR门 + 一个笑脸 ...

February 1, 2019 · 1 min · jiezi

从浏览器输入一个网址开始讨论网络传输的工作原理

确实,从浏览器输入一个地址之后,当你按下Enter键之后,一系列奇怪的魔法就在发生,这是一个老生长谈的问题了,但能真的仔细知道全部却是一个艰难的事情。

January 31, 2019 · 1 min · jiezi

一文掌握前端面试浏览器相关知识点

事件机制事件触发三阶段事件触发有三个阶段window 往事件触发处传播,遇到注册的捕获事件会触发传播到事件触发处时触发注册的事件从事件触发处往 window 传播,遇到注册的冒泡事件会触发事件触发一般来说会按照上面的顺序进行,但是也有特例,如果给一个目标节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行。// 以下会先打印冒泡然后是捕获node.addEventListener(‘click’,(event) =>{ console.log(‘冒泡’)},false);node.addEventListener(‘click’,(event) =>{ console.log(‘捕获 ‘)},true)注册事件通常我们使用 addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值 useCapture 参数来说,该参数默认值为 false 。useCapture 决定了注册的事件是捕获事件还是冒泡事件。对于对象参数来说,可以使用以下几个属性capture,布尔值,和 useCapture 作用一样once,布尔值,值为 true 表示该回调只会调用一次,调用后会移除监听passive,布尔值,表示永远不会调用 preventDefault一般来说,我们只希望事件只触发在目标上,这时候可以使用 stopPropagation 来阻止事件的进一步传播。通常我们认为 stopPropagation 是用来阻止事件冒泡的,其实该函数也可以阻止捕获事件。stopImmediatePropagation 同样也能实现阻止事件,但是还能阻止该事件目标执行别的注册事件。node.addEventListener(‘click’,(event) =>{ event.stopImmediatePropagation() console.log(‘冒泡’)},false);// 点击 node 只会执行上面的函数,该函数不会执行node.addEventListener(‘click’,(event) => { console.log(‘捕获 ‘)},true)事件代理如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上<ul id=“ul”> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li></ul><script> let ul = document.querySelector(’#ul’) ul.addEventListener(‘click’, (event) => { console.log(event.target); })</script>事件代理的方式相对于直接给目标注册事件来说,有以下优点节省内存不需要给子节点注销事件跨域因为浏览器出于安全考虑,有同源策略。也就是说,如果协议、域名或者端口有一个不同就是跨域,Ajax 请求会失败。我们可以通过以下几种常用方法解决跨域的问题JSONPJSONP 的原理很简单,就是利用 <script> 标签没有跨域限制的漏洞。通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时。<script src=“http://domain/api?param1=a&param2=b&callback=jsonp”></script><script> function jsonp(data) { console.log(data) }</script> JSONP 使用简单且兼容性不错,但是只限于 get 请求。在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现function jsonp(url, jsonpCallback, success) { let script = document.createElement(“script”); script.src = url; script.async = true; script.type = “text/javascript”; window[jsonpCallback] = function(data) { success && success(data); }; document.body.appendChild(script);}jsonp( “http://xxx”, “callback”, function(value) { console.log(value); });CORSCORS需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。浏览器会自动进行 CORS 通信,实现CORS通信的关键是后端。只要后端实现了 CORS,就实现了跨域。服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。document.domain该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。只需要给页面添加 document.domain = ’test.com’ 表示二级域名都相同就可以实现跨域postMessage这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息// 发送消息端window.parent.postMessage(‘message’, ‘http://test.com’);// 接收消息端var mc = new MessageChannel();mc.addEventListener(‘message’, (event) => { var origin = event.origin || event.originalEvent.origin; if (origin === ‘http://test.com’) { console.log(‘验证通过’) }});Event loop众所周知 JS 是门非阻塞单线程语言,因为在最初 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,我们在多个线程中处理 DOM 就可能会发生问题(一个线程中新加节点,另一个线程中删除节点),当然可以引入读写锁解决这个问题。JS 在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步的代码,会被挂起并加入到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。console.log(‘script start’);setTimeout(function() { console.log(‘setTimeout’);}, 0);console.log(‘script end’);以上代码虽然 setTimeout 延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,不足会自动增加。所以 setTimeout 还是会在 script end 之后打印。不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。console.log(‘script start’);setTimeout(function() { console.log(‘setTimeout’);}, 0);new Promise((resolve) => { console.log(‘Promise’) resolve()}).then(function() { console.log(‘promise1’);}).then(function() { console.log(‘promise2’);});console.log(‘script end’);// script start => Promise => script end => promise1 => promise2 => setTimeout以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise 属于微任务而 setTimeout 属于宏任务,所以会有以上的打印。微任务包括 process.nextTick ,promise ,Object.observe ,MutationObserver宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering很多人有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务。所以正确的一次 Event loop 顺序是这样的执行同步代码,这属于宏任务执行栈为空,查询是否有微任务需要执行执行所有微任务必要的话渲染 UI然后开始下一轮 Event loop,执行宏任务中的异步代码通过上述的 Event loop 顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作 DOM 的话,为了更快的 界面响应,我们可以把操作 DOM 放入微任务中。Node 中的 Event loopNode 中的 Event loop 和浏览器中的不相同。Node 的 Event loop 分为6个阶段,它们会按照顺序反复运行┌───────────────────────┐┌─>│ timers ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ I/O callbacks ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ idle, prepare ││ └──────────┬────────────┘ ┌───────────────┐│ ┌──────────┴────────────┐ │ incoming: ││ │ poll │<──connections─── ││ └──────────┬────────────┘ │ data, etc. ││ ┌──────────┴────────────┐ └───────────────┘│ │ check ││ └──────────┬────────────┘│ ┌──────────┴────────────┐└──┤ close callbacks │ └───────────────────────┘timertimers 阶段会执行 setTimeout 和 setInterval一个 timer 指定的时间并不是准确时间,而是在达到这个时间后尽快执行回调,可能会因为系统正在执行别的事务而延迟。下限的时间有一个范围:[1, 2147483647] ,如果设定的时间不在这个范围,将被设置为1。I/OI/O 阶段会执行除了 close 事件,定时器和 setImmediate 的回调idle, prepareidle, prepare 阶段内部实现pollpoll 阶段很重要,这一阶段中,系统会做两件事情执行到点的定时器执行 poll 队列中的事件并且当 poll 中没有定时器的情况下,会发现以下两件事情如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者系统限制如果 poll 队列为空,会有两件事发生如果有 setImmediate 需要执行,poll 阶段会停止并且进入到 check 阶段执行 setImmediate如果没有 setImmediate 需要执行,会等待回调被加入到队列中并立即执行回调如果有别的定时器需要被执行,会回到 timer 阶段执行回调。checkcheck 阶段执行 setImmediateclose callbacksclose callbacks 阶段执行 close 事件并且在 Node 中,有些情况下的定时器执行顺序是随机的setTimeout(() => { console.log(‘setTimeout’);}, 0);setImmediate(() => { console.log(‘setImmediate’);})// 这里可能会输出 setTimeout,setImmediate// 可能也会相反的输出,这取决于性能// 因为可能进入 event loop 用了不到 1 毫秒,这时候会执行 setImmediate// 否则会执行 setTimeout当然在这种情况下,执行顺序是相同的var fs = require(‘fs’)fs.readFile(__filename, () => { setTimeout(() => { console.log(’timeout’); }, 0); setImmediate(() => { console.log(‘immediate’); });});// 因为 readFile 的回调在 poll 中执行// 发现有 setImmediate ,所以会立即跳到 check 阶段执行回调// 再去 timer 阶段执行 setTimeout// 所以以上输出一定是 setImmediate,setTimeout上面介绍的都是 macrotask 的执行情况,microtask 会在以上每个阶段完成后立即执行。setTimeout(()=>{ console.log(’timer1’) Promise.resolve().then(function() { console.log(‘promise1’) })}, 0)setTimeout(()=>{ console.log(’timer2’) Promise.resolve().then(function() { console.log(‘promise2’) })}, 0)// 以上代码在浏览器和 node 中打印情况是不同的// 浏览器中一定打印 timer1, promise1, timer2, promise2// node 中可能打印 timer1, timer2, promise1, promise2// 也可能打印 timer1, promise1, timer2, promise2Node 中的 process.nextTick 会先于其他 microtask 执行。setTimeout(() => { console.log(“timer1”); Promise.resolve().then(function() { console.log(“promise1”); });}, 0);process.nextTick(() => { console.log(“nextTick”);});// nextTick, timer1, promise1存储cookie,localStorage,sessionStorage,indexDB特性cookielocalStoragesessionStorageindexDB数据生命周期一般由服务器生成,可以设置过期时间除非被清理,否则一直存在页面关闭就清理除非被清理,否则一直存在数据存储大小4K5M5M无限与服务端通信每次都会携带在 header 中,对于请求性能影响不参与不参与不参与从上表可以看到,cookie 已经不建议用于存储。如果没有大量数据存储需求的话,可以使用 localStorage 和 sessionStorage 。对于不怎么改变的数据尽量使用 localStorage 存储,否则可以用 sessionStorage 存储。对于 cookie,我们还需要注意安全性。属性作用value如果用于保存用户登录态,应该将该值加密,不能使用明文的用户标识http-only不能通过 JS 访问 Cookie,减少 XSS 攻击secure只能在协议为 HTTPS 的请求中携带same-site规定浏览器不能在跨域请求中携带 Cookie,减少 CSRF 攻击Service WorkerService workers 本质上充当Web应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理。它们旨在(除其他之外)使得能够创建有效的离线体验,拦截网络请求并基于网络是否可用以及更新的资源是否驻留在服务器上来采取适当的动作。他们还允许访问推送通知和后台同步API。目前该技术通常用来做缓存文件,提高首屏速度,可以试着来实现这个功能。// index.jsif (navigator.serviceWorker) { navigator.serviceWorker .register(“sw.js”) .then(function(registration) { console.log(“service worker 注册成功”); }) .catch(function(err) { console.log(“servcie worker 注册失败”); });}// sw.js// 监听 install 事件,回调中缓存所需文件self.addEventListener(“install”, e => { e.waitUntil( caches.open(“my-cache”).then(function(cache) { return cache.addAll(["./index.html", “./index.js”]); }) );});// 拦截所有请求事件// 如果缓存中已经有请求的数据就直接用缓存,否则去请求数据self.addEventListener(“fetch”, e => { e.respondWith( caches.match(e.request).then(function(response) { if (response) { return response; } console.log(“fetch source”); }) );});打开页面,可以在开发者工具中的 Application 看到 Service Worker 已经启动了在 Cache 中也可以发现我们所需的文件已被缓存当我们重新刷新页面可以发现我们缓存的数据是从 Service Worker 中读取的渲染机制浏览器的渲染机制一般分为以下几个步骤处理 HTML 并构建 DOM 树。处理 CSS 构建 CSSOM 树。将 DOM 与 CSSOM 合并成一个渲染树。根据渲染树来布局,计算每个节点的位置。调用 GPU 绘制,合成图层,显示在屏幕上。在构建 CSSOM 树时,会阻塞渲染,直至 CSSOM 树构建完成。并且构建 CSSOM 树是一个十分消耗性能的过程,所以应该尽量保证层级扁平,减少过度层叠,越是具体的 CSS 选择器,执行速度越慢。当 HTML 解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件。并且 CSS 也会影响 JS 的执行,只有当解析完样式表才会执行 JS,所以也可以认为这种情况下,CSS 也会暂停构建 DOM。Load 和 DOMContentLoaded 区别Load 事件触发代表页面中的 DOM,CSS,JS,图片已经全部加载完毕。DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析,不需要等待 CSS,JS,图片加载。图层一般来说,可以把普通文档流看成一个图层。特定的属性可以生成一个新的图层。不同的图层渲染互不影响,所以对于某些频繁需要渲染的建议单独生成一个新图层,提高性能。但也不能生成过多的图层,会引起反作用。通过以下几个常用属性可以生成新图层3D 变换:translate3d、translateZwill-changevideo、iframe 标签通过动画实现的 opacity 动画转换position: fixed重绘(Repaint)和回流(Reflow)重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大。重绘是当节点需要更改外观而不会影响布局的,比如改变 color 就叫称为重绘回流是布局或者几何属性需要改变就称为回流。回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。所以以下几个动作可能会导致性能问题:改变 window 大小改变字体添加或删除样式文字改变定位或者浮动盒模型很多人不知道的是,重绘和回流其实和 Event loop 有关。当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。然后判断是否有 resize 或者 scroll ,有的话会去触发事件,所以 resize 和 scroll 事件也是至少 16ms 才会触发一次,并且自带节流功能。判断是否触发了 media query更新动画并且发送事件判断是否有全屏操作事件执行 requestAnimationFrame 回调执行 IntersectionObserver 回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好更新界面以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调。以上内容来自于 HTML 文档减少重绘和回流使用 translate 替代 top<div class=“test”></div><style> .test { position: absolute; top: 10px; width: 100px; height: 100px; background: red; }</style><script> setTimeout(() => { // 引起回流 document.querySelector(’.test’).style.top = ‘100px’ }, 1000)</script>使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 Reflow),然后你修改100次,然后再把它显示出来不要把 DOM 结点的属性值放在一个循环里当成循环里的变量for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector(’.test’).style.offsetTop)}不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrameCSS 选择符从右往左匹配查找,避免 DOM 深度过深将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video 标签,浏览器会自动将该节点变为图层。文章来源:https://github.com/InterviewMap/CS-Interview-Knowledge-Map/blob/master/Browser/browser-ch.md ...

January 29, 2019 · 4 min · jiezi

如何全面的提升百度搜索排名?

之前有整理过 《身为前端开发工程师,你需要了解的搜索引擎优化SEO》出门左拐但是,作用很有限!感觉很像是你很饥饿,但是锅里只有几粒米!一顿操作猛如虎,在看战绩0-5;那就拜托技术上的思维固化整体性的思考这个问题!一、知名站点百度是怎么确定一个网站是不是知名站点呢?1.经过官方认证的企业站点!2.站点的收录数量3.站点的外部链接(百度的web超链分析法)4.站点的访问量二、合作企业这一类俗称人民币玩家!这里还包括(贴吧、百家号、知道、图片、文库等等)三、站点内页面更新时间!这一点其实可操作的东西还是蛮多的!1.保证站点经常更新2.新闻类 具有时效性的文章排名会很靠前四、网站内容1、文字内容比重最好大于html格式的比重!2、网页文件大小最后在 5kb~10kb之间3、网站的设计以简单为主。文字内容的比重应大于HTML格式的比重4、争取添加10个以上的活跃网站的外部链接5、增加网站客户粘性!比如浏览用户停留在站点某个页面的时间(越长越好)6、其他看这篇文章五、新方向(指的是百度有意推广的方向)随着多家自媒体平台的蓬勃发展,百度这位大哥大似乎感到了空前绝后的危机!还出现了这样的文章《百度搜索已死》 当然不乏客观性,但是,本人认为标题博关注的可能性更大!百度已经很难收到这篇文章,前段时间很火!有兴趣的小伙伴可以自行寻找而应对危机的方式就是疯狂的推送百家号!1.增加站点与自家百家号的关联强度!2.百度对长时间原创的网站提供了排名加强!六、域名和空间域名:1.域名的权重:域名分为三类顶级域名:类别顶级域名、地理顶级域名、新顶级域名2.域名的长短:域名的长短对优化并无影响,只是短域名更容易记忆3.中文域名在搜索引擎排名上有优势,不过IE6浏览器不支持中文域名,所以综合角度,选择英文域名还是首选4.域名的拼写方式:为了符合中国用户输入习惯,拼音域名还是网站的首选5.域名的存在时间:一般来说,域名时间越长,对优化越有帮助6.域名是否被K:一般来说不建议选用被K的域名,对seo不利空间:1.空间位置2.空间速度3.空间稳定性4.支持在线人数5.是否支持404错误页6.虚拟机还是服务器这短时间看了好多!也质询或一些专业人士!整理的还不过完整!希望有大佬补充评论!

January 25, 2019 · 1 min · jiezi

hibernate和jdbc的渊源

简单介绍jdbc我们学习Java数据库操作时,一般会设计到jdbc的操作,这是一位程序员最基本的素养。jdbc以其优美的代码和高性能,将瞬时态的javabean对象转化为持久态的SQL数据。但是,每次SQL操作都需要建立和关闭连接,这势必会消耗大量的资源开销;如果我们自行创建连接池,假如每个项目都这样做,势必搞死的了。同时,我们将SQL语句预编译在PreparedStatement中,这个类可以使用占位符,避免SQL注入,当然,后面说到的hibernate的占位符的原理也是这样,同时,mybatis的#{}占位符原理也是如此。预编译的语句是原生的SQL语句,比如更新语句:private static int update(Student student) { Connection conn = getConn(); int i = 0; String sql = “update students set Age=’” + student.getAge() + “’ where Name=’” + student.getName() + “’”; PreparedStatement pstmt; try { pstmt = (PreparedStatement) conn.prepareStatement(sql); i = pstmt.executeUpdate(); System.out.println(“resutl: " + i); pstmt.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } return i;}上面的sql语句没有使用占位符,如果我们少了varchar类型的单引号,就会保存失败。在这种情况下,如果写了很多句代码,最后因为一个单引号,导致代码失败,对于程序员来说,无疑是很伤自信心的事。如果涉及到了事务,那么就会非常的麻烦,一旦一个原子操作的语句出现错误,那么事务就不会提交,自信心就会跌倒低谷。然而,这仅仅是更新语句,如果是多表联合查询语句,那么就要写很多代码了。具体的jdbc的操作,可以参考这篇文章:jdbc操作。因而,我们在肯定它的优点时,也不应该规避他的缺点。随着工业化步伐的推进,每个数据库往往涉及到很多表,每张表有可能会关联到其他表,如果我们还是按照jdbc的方式操作,这无疑是增加了开发效率。所以,有人厌倦这种复杂繁琐、效率低下的操作,于是,写出了著名的hibernate框架,封装了底层的jdbc操作,以下是jdbc的优缺点:由上图可以看见,jdbc不适合公司的开发,公司毕竟以最少的开发成本来创造更多的利益。这就出现了痛点,商机伴随着痛点的出现。因而,应世而生了hibernate这个框架。即便没有hibernate的框架,也会有其他框架生成。hibernate的底层封装了jdbc,比如说jdbc为了防止sql注入,一般会有占位符,hibernate也会有响应的占位符。hibernate是orm(object relational mapping)的一种,即对象关系映射。什么对象关系映射通俗地来说,对象在pojo中可以指Javabean,关系可以指MySQL的关系型数据库的表字段与javabean对象属性的关系。映射可以用我们高中所学的函数映射,即Javabean顺时态的对象映射到数据库的持久态的数据对象。我们都知道javabean在内存中的数据是瞬时状态或游离状态,就像是宇宙星空中的一颗颗行星,除非行星被其他行星所吸引,才有可能不成为游离的行星。瞬时状态(游离状态)的javabean对象的生命周期随着进程的关闭或者方法的结束而结束。如果当前javabean对象与gcRoots没有直接或间接的关系,其有可能会被gc回收。我们就没办法长久地存储数据,这是一个非常头疼的问题。假如我们使用文件来存储数据,但是文件操作起来非常麻烦,而且数据格式不是很整洁,不利于后期的维护等。因而,横空出世了数据库。我们可以使用数据库存储数据,数据库中的数据才是持久数据(数据库的持久性),除非人为的删除。这里有个问题——怎么将瞬时状态(游离状态)的数据转化为持久状态的数据,肯定需要一个连接Javabean和数据库的桥梁,于是乎就有了上面的jdbc。单独来说mysql,mysql是一个远程服务器。我们在向mysql传输数据时,并不是对象的方式传输,而是以字节码的方式传输数据。为了保证数据准确的传输,我们一般会序列化当前对象,用序列号标志这个唯一的对象。如果,我们不想存储某个属性,它是有数据库中的数据拼接而成的,我们大可不用序列化这个属性,可以使用Transient来修饰。比如下面的获取图片的路径,其实就是服务器图片的文件夹地址和图片的名称拼接而成的。当然,你想存储这个属性,也可以存储这个属性。我们有时候图片的路由的字节很长,这样会占用MySQL的内存。因而,学会取舍,未尝不是一个明智之举。@Entity@Table(name = “core_picture”)public class Picture extends BaseTenantObj { 。。。。 @Transient public String getLocaleUrl() { return relativeFolder.endsWith(”/") ? relativeFolder + name : relativeFolder + “/” + name; } 。。。。}网上流传盛广的对象关系映射的框架(orm)有hibernate、mybatis等。重点说说hibernate和mybatis吧。hibernate是墨尔本的一位厌倦重复的javabean的程序员编写而成的,mybatis是appache旗下的一个子产品,其都是封装了jdbc底层操作的orm框架。但hibernate更注重javabean与数据表之间的关系,比如我们可以使用注解生成数据表,也可以通过注解的方式设置字段的类型、注释等。他将javabean分成了游离态、顺时态、持久态等,hibernate根据这三种状态来触及javabean对象的数据。而mybatis更多的是注重SQL语句的书写,也就是说主要是pojo与SQL语句的数据交互,对此,Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。一旦SQL语句的移动,有可能会影响字段的不对应,因而,mybatis移植性没有hibernate好。mybatis接触的是底层SQL数据的书写,hibernate根据javabean的参数来生成SQL语句,再将SQL查询的结果封装成pojo,因而,mybatis的性能相来说优于hibernate,但这也不是绝对的。性能还要根据你的表的设计结构、SQL语句的封装、网络、带宽等等。我只是抛砖引玉,它们具体的区别,可以参考这篇文档。mybatis和hibernate的优缺点。hibernate和mybatis之间的区别,也是很多公司提问面试者的问题。但是真正熟知他们区别的人,一般是技术选型的架构师。如果你只是负责开发,不需要了解它们的区别,因为他们都封装了jdbc。所以,你不论使用谁,都还是比较容易的。然而,很多公司的HR提问这种问题很死板,HR并不懂技术,他们只是照本宣科的提问。如果你照本宣科的回答,它们觉着你很厉害。但是,如果是一个懂技术的人提问你,如果你只是临时背了它们的区别,而没有相应的工作经验,他们会问的让你手足无措。hibernate的讲解因为我们公司使用的是hibernate,我在这里简单地介绍下hibernate。但相对于jdbc来说,hibernate框架还是比较重的。为什么说他重,因为它集成了太多的东西,看如下的hibernate架构图:你会发现最上层使我们的java应用程序的开始,比如web的Tomcat服务器的启动,比如main方法的启动等。紧接着就是需要(needing)持久化的对象,这里为什么说是需要,而不是持久化的对象。只有保存到文件、数据库中的数据才是持久化的想通过hibernate,我们可以毫不费力的将瞬时状态的数据转化为持久状态的数据,下面便是hibernate的内部操作数据。其一般是这样的流程:我个人画的这个地址的图片如果你是用过jdbc连接数据库的话,我们一般是这样写的:package com.zby.jdbc.config;import com.zby.util.exception.TableException;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;/** * Created By zby on 22:12 2019/1/5 /public class InitJdbcFactory { private static Properties properties = new Properties(); private static Logger logger = LoggerFactory.getLogger(InitJdbcFactory.class); static { try { //因为使用的类加载器获取配置文件,因而,配置文件需要放在classpath下面, // 方能读到数据 properties.load(Thread.currentThread().getContextClassLoader(). getResourceAsStream("./jdbc.properties")); } catch (IOException e) { logger.info(“初始化jdbc失败”); e.printStackTrace(); } } public static Connection createConnection() { String drivers = properties.getProperty(“jdbc.driver”); if (StringUtils.isBlank(drivers)) { drivers = “com.mysql.jdbc.Driver”; } String url = properties.getProperty(“jdbc.url”); String username = properties.getProperty(“jdbc.username”); String password = properties.getProperty(“jdbc.password”); try { Class.forName(drivers); return DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { logger.error(InitColTable.class.getName() + “:连接数据库的找不到驱动类”); throw new TableException(InitColTable.class.getName() + “: 连接数据库的找不到驱动类”, e); } catch (SQLException e) { logger.error(InitColTable.class.getName() + “:连接数据库的sql异常”); throw new TableException(InitColTable.class.getName() + “连接数据库的sql异常”, e); } }}hibernate一般这样连接数据库:public class HibernateUtils { private static SessionFactory sf; //静态初始化 static{ //【1】加载配置文件 Configuration conf = new Configuration().configure(); //【2】 根据Configuration 配置信息创建 SessionFactory sf = conf.buildSessionFactory(); //如果这里使用了hook虚拟机,需要关闭hook虚拟机 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { System.out.println(“虚拟机关闭!释放资源”); sf.close(); } })); } /* * 采用openSession创建一个与数据库的连接会话,但这种方式需要手动关闭与数据库的session连接(会话), * 如果不关闭,则当前session占用数据库的资源无法释放,最后导致系统崩溃。 * / public static org.hibernate.Session openSession(){ //【3】 获得session Session session = sf.openSession(); return session; } / * 这种方式连接数据库,当提交事务时,会自动关闭当前会话; * 同时,创建session连接时,autoCloseSessionEnabled和flushBeforeCompletionEnabled都为true, * 并且session会同sessionFactory组成一个map以sessionFactory为主键绑定到当前线程。 * 采用getCurrentSession()需要在Hibernate.cfg.xml配置文件中加入如下配置: 如果是本地事物,及JDBC一个数据库: <propety name=”Hibernate.current_session_context_class”>thread</propety> 如果是全局事物,及jta事物、多个数据库资源或事物资源: <propety name=”Hibernate.current_session_context_class”>jta</propety> 使用spring的getHiberanteTemplate 就不需要考虑事务管理和session关闭的问题: * / public static org.hibernate.Session getCurrentSession(){ //【3】 获得session Session session = sf.getCurrentSession(); return session; }}mybatis的配置文件:public class DBTools { public static SqlSessionFactory sessionFactory; static{ try { //使用MyBatis提供的Resources类加载mybatis的配置文件 Reader reader = Resources.getResourceAsReader(“mybatis.cfg.xml”); //构建sqlSession的工厂 sessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { e.printStackTrace(); } } //创建能执行映射文件中sql的sqlSession public static SqlSession getSession(){ return sessionFactory.openSession(); }}hibernate、mybatis、jdbc创建于数据库的连接方式虽然不同,但最红都是为了将顺时态的数据写入到数据库中的,但这里主要说的是hibernate。但是hibernate已经封装了这些属性,我们可以在configuration在配置驱动类、用户名、用户密码等。再通过sessionFactory创建session会话,也就是加载Connection的物理连接,创建sql的事务,然后执行一系列的事务操作,如果事务全部成功即可成功,但反有一个失败都会失败。jdbc是最基础的操作,但是,万丈高楼平地起,只有基础打牢,才能走的更远。因为hibernate封装了这些基础,我们操作数据库不用考虑底层如何实现的,因而,从某种程度上来说,hibernate还是比较重的。hibernate为什么会重?比如我们执行插入语句,可以使用save、saveOrUpdate,merge等方法。需要将实体bean通过反射转化为mysql的识别的SQL语句,同时,查询虽然用到了反射,但是最后转化出来的还是object的根对象,这时需要将根对象转化为当前对象,返回给客户端。虽然很笨重,但是文件配置好了,可以大大地提高开发效率。毕竟现在的服务器的性能都比较好,公司追求的是高效率的开发,而往往不那么看重性能,除非用户提出性能的问题。说说merge和saveOrUpdatemerge方法与saveOrUpdate从功能上类似,但他们仍有区别。现在有这样一种情况:我们先通过session的get方法得到一个对象u,然后关掉session,再打开一个session并执行saveOrUpdate(u)。此时我们可以看到抛出异常:Exception in thread “main” org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session,即在session缓存中不允许有两个id相同的对象。不过若使用merge方法则不会异常,其实从merge的中文意思(合并)我们就可以理解了。我们重点说说merge。merge方法产生的效果和saveOrUpdate方法相似。这是hibernate的原码: void saveOrUpdate(Object var1); void saveOrUpdate(String var1, Object var2); public Object merge(Object object); public Object merge(String var1, Object var2);前者不用返回任何数据,后者返回的是持久化的对象。如果根据hibernate的三种状态,比如顺时态、持久态、游离态来说明这个问题,会比较难以理解,现在,根据参数有无id或id是否已经存在来理解merge,而且从执行他们两个方法而产生的sql语句来看是一样的。参数实例对象没有提供id或提供的id在数据库找不到对应的行数据,这时merger将执行插入操作吗,产的SQL语句如下: Hibernate: select max(uid) from user Hibernate: insert into hibernate1.user (name, age, uid) values (?, ?, ?)一般情况下,我们新new一个对象,或者从前端向后端传输javabean序列化的对象时,都不会存在当前对象的id,如果使用merge的话,就会向数据库中插入一条数据。参数实例对象的id在数据库中已经存在,此时又有两种情况(1)如果对象有改动,则执行更新操作,产生sql语句有: Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? Hibernate: update hibernate1.user set name=?, age=? where uid=?(2)如果对象未改动,则执行查询操作,产生的语句有: Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=?以上三种是什么情况呢?如果我们保存用户时,数据库中肯定不存在即将添加的用户,也就是说,我们的保存用户就是向数据库中添加用户。但是,其也会跟着某些属性, 比如说用户需要头像,这是多对一的关系,一个用户可能多个对象,然而,头像的关联的id不是放在用户表中的,而是放在用户扩张表中的,这便用到了切分表的概念。题外话,我们有时会用到快照表,比如商品快照等,也许,我们购买商品时,商品是一个价格,但随后商品的价格变了,我们需要退商品时,就不应该用到商品改变后的价格了,而是商品改变前的价格。扩展表存放用户额外的信息,也就是用户非必须的信息,比如说昵称,性别,真实姓名,头像等。 因而,头像是图片类型,使用hibernate的注解方式,创建用户表、图片表、用户扩展表。如下所示(部分重要信息已省略) //用户头像 @Entity @Table(name = “core_user”) public class User extends BaseTenantConfObj { / * 扩展表 * / @OneToOne(mappedBy = “user”, fetch = FetchType.LAZY, cascade = CascadeType.ALL) private UserExt userExt; } //用户扩展表的头像属性 @Entity @Table(name = “core_user_ext”) public class UserExt implements Serializable { /* * 头像 / @ManyToOne @JoinColumn(name = “head_logo”) private Picture headLogo; } //图片表 @Entity @Table(name = “core_picture”) public class Picture extends BaseTenantObj { private static Logger logger = LoggerFactory.getLogger(Picture.class); 。。。。。。 //图片存放在第三方的相对url。 @Column(name = “remote_relative_url”, length = 300) private String remoteRelativeUrl; // 图片大小 @Column(length = 8) private Integer size; /* * 图片所属类型 * user_logo:用户头像 / @Column(name = “host_type”, length = 58) private String hostType; //照片描述 @Column(name = “description”, length = 255) private String description; } 前端代码是://这里使用到了vue.js的代码,v-model数据的双向绑定,前端的HTML代码<tr> <td class=“v-n-top”>头像:</td> <td> <div class=“clearfix”> <input type=“hidden” name=“headLogo.id” v-model=“pageData.userInfo.logo.id”/> <img class=“img-user-head fl” :src="(pageData.userInfo&&pageData.userInfo.logo&&pageData.userInfo.logo.path) ? (constant.imgPre + pageData.userInfo.logo.path) : ‘img/user-head-default.png’"> <div class=“img-btn-group”> <button cflag=“upImg” type=“button” class=“btn btn-sm btn-up”>点击上传</button> <button cflag=“delImg” type=“button” class=“btn btn-white btn-sm btn-del”>删除</button> </div> </div> <p class=“img-standard”>推荐尺寸800800;支持.jpg, .jpeg, .bmp, .png类型文件,1M以内</p> </td></tr>//这里用到了js代码,这里用到了js的属性方法upImg: function(me) { Utils.asyncImg({ fn: function(data) { vm.pageData.userInfo.logo = { path: data.remoteRelativeUrl, id: data.id }; } });},上传头像是异步提交,如果用户上传了头像,我们在提交用户信息时,通过“headLogo.id”可以获取当前头像的持久化的图片对象,hibernate首先会根据属性headLogo找到图片表,根据当前头像的id找到图片表中对应的行数据,为什么可以根据id来获取行数据?-图片表的表结构信息从这张图片可以看出,图片采用默认的存储引擎,也就是InnoDB存储引擎,而不是myiSam的存储引擎。我们都知道这两种存储引擎的区别,如果不知道的话,可以参这篇文章MySQL中MyISAM和InnoDB的索引方式以及区别与选择。innodb采用BTREE树的数据结构方式存储,它有0到1直接前继和0到n个直接后继,这是什么意思呢?一棵树当前叶子节点的直接父节点只有一个,但其儿子节点可以一个都没有,也可以有1个、2个、3个……,如果mysql采用的多对一的方式存储的话,你就会发现某条外键下有许多行数据,比如如下的这张表这张表记录的是项目的完成情况,一般有预约阶段,合同已签,合同完成等等。你会发现project_id=163的行数据不止一条,我们通过查询语句:SELECT zpp.* from zq_project_process zpp WHERE zpp.is_deleted = 0 AND zpp.project_id=163,查找速度非常快。为什么这么快呢,因为我刚开始说的innodb采用的BTREE树结构存储,其数据是放在当前索引下,什么意思?innodb的存储引擎是以索引作为当前节点值,比如说银行卡表的有个主键索引,备注,如果我们没有创建任何索引,如果采用的innodb的数据引擎,其内部会创建一个默认的行索引,这就像我们在创建javabean对象时,没有创建构造器,其内部会自动创建一个构造器的道理是一样的。其数据是怎么存储的呢,如下图所示:mysql银行卡数据其内部存储数据其所对应的行数据是放在当前索引下的,因而,我们取数据不是取表中中的数据,而是取当前主键索引下的数据。项目进程表如同银行卡的主键索引,只不过其有三个索引,分别是主键索引和两个外键索引,如图所示的索引:索引名是hibernate自动生成的一个名字,索引是项目id、类型两个索引。因为我们不是从表中取数据,而是从当前索引的节点下取数据,所以速度当然快了。索引有主键索引、外键索引、联合索引等,但一般情况下,主键索引和外键索引使用频率比较高。同时,innodb存储引擎的支持事务操作,这是非常重要,我们操作数据库,一般都是设计事务的操作,这也mysql默认的存储引擎是innodb。我们通过主键获取图片的行数据,就像通过主键获取银行卡的行数据。这也是上面所说的,根据是否有id来确定是插入还是更新数据。通过图片主键id获取该行数据后,hibernate会在堆中创建一个picture对象。用户扩展表的headLogo属性指向这个图片对象的首地址,从而创建一个持久化的图片对象。前台异步提交头像时,如果是编辑头像,hibernate会觉擦到当前对象的属性发生了改变,于是,在提交事务时将修改后的游离态的类保存到数据库中。如果我们保存或修改用户时,我们保存的就是持久化的对象,其内部会自动存储持久化头像的id。这是hibernate底层所做的,我们不需要关心。再举一个hibernate事务提交的例子:我们在支付当中搞得提现事务时,调用第三方支付的SDK时,第三方一般会用我们到订单号,比如我们调用连连支付这个第三方支付的SDK的payRequestBean的实体类:/** * Created By zby on 11:00 2018/12/11 * 发送到连连支付的body内容 /@Data@AllArgsConstructor@NoArgsConstructorpublic class PaymentRequestBean extends BaseRequestBean { /* * 版本号 / @NonNull private String api_version; /* * 银行账户 / @NonNull private String card_no; /* * 对私 / @NonNull private String flag_card; /* * 回调接口 / @NonNull private String notify_url; /* * 商户订单号 / @NonNull private String no_order; /* * 商户订单时间,时间格式为 YYYYMMddHHmmss / @NonNull private String dt_order; /* * 交易金额 / @NonNull public String money_order; /* * 收款方姓名 即账户名 / @NonNull private String acct_name; /* * 收款银行姓名 / private String bank_name; /* * 订单描述 ,代币类型 + 支付 / @NonNull private String info_order; /* * 收款备注 / private String memo; /* * 支行名称 */ private String brabank_name;}商户订单号是必传的,且这个订单号是我们这边提供的,这就有一个问题了,怎么避免订单号不重复呢?我们可以在提现记录表事先存储一个订单号,订单号的规则如下:“WD” +系统时间+ 当前提现记录的id,这个id怎么拿到呢?既然底层使用的是merge方法,我们事先不创建订单号,先保存这个记录,其返回的是已经创建好的持久化的对象,该持久化的对象肯定有提现主键的id。我们拿到该持久化对象的主键id,便可以封装订单号,再次保存这个持久化的对象,其内部会执行类似以下的操作:Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? Hibernate: update hibernate1.user set name=?, age=? where uid=?代码如下: withdraw.setWithdrawStatus(WITHDRAW_STATUS_WAIT_PAY); withdraw.setApplyTime(currentTime); withdraw.setExchangeHasThisMember(hasThisMember ? YES : NO); withdraw = withdrawDao.save(withdraw); withdraw.setOrderNo(“WD” + DateUtil.ISO_DATETIME_FORMAT_NONE.format(currentTime) + withdraw.getId()); withdrawDao.save(withdraw);不管哪种情况,merge的返回值都是一个持久化的实例对象,但对于参数而言不会改变它的状态。 ...

January 23, 2019 · 4 min · jiezi

chrome弹窗在双屏情况下left居中定位异常分析

背景使用 window.open 进行弹窗显示,实现微信二维码弹窗功能在双屏情况下,chrome浏览器位于副屏弹窗时,会存在弹窗位置异常问题。目前网上相关解析及解决方案几乎没有,故写此文章以作分享。文章重点双屏情况下,chrome浏览器弹窗位置问题多屏幕时,chrome浏览器位于非主屏进行弹窗显示时,设置弹窗的left,top将会异常本文将分析其显示异常的原因,并给出解决方案解决该问题的分析过程这是本文分享的另一个重点除了解决方案,希望能通过本文和大家分享笔者解决该问题时的思路和方法。这些方法可能不是最优的,但希望能给大家带来一点触动或者启示。在解决到其他问题的时候也用得上。window.open的第三个参数及其兼容性介绍window.open方法相信大家都不会陌生,通常用于传递一个地址参数,新建一个浏览器tab页面。但除了第一个地址参数,window.open还另外接收两个参数,分别是「strWindowName(新窗口的名称)」,「strWindowFeatures(新窗口特性)」这强调的是第三个参数,当设置了第三个参数后,新开的弹窗将会在原页面的基础上,已非tab页面的形式进行显示,有以下几个特点在原页面上进行弹窗显示,而不是新起浏览器tab页面进行跳转。其显示方式类似alert弹窗,属于原页面的一个功能模块,而不是跳转至新页面。非tab页面,这意味着它不像其他tab页面那样可以放在浏览器tab栏中,它是折叠不进去了,是以弹窗的形式呈现。第三个参数「strWindowFeatures」可以设置新窗口特性,例如宽度,高度,距顶,距左,是否显示滚动条等等。本文不做详细介绍,参数详情可以参考这篇文章需要注意的是,strWindowFeatures里的特效并不是每个浏览器都支持的,不同于「dom」,这属于「bom(borwser Object Model)」的内容。具体兼容性这里也不讲了,网上也有相关文章chrome的兼容性与坑(重点一)异常的显示即使看完上面的兼容性文章,当你使用chrome浏览器,位于非主屏进行弹窗时,依然会存在位置设置异常的问题。实现居中显示弹窗,一般代码会这样写const windowWidth = window.screen.width // 屏幕宽度const windowHeight = window.screen.height // 屏幕高度const pageWidth = 600 // 弹出窗口的宽度const pageHeight = 550 // 弹出窗口的高度let pageTop = (windowHeight - pageHeight) / 2 // 窗口的垂直位置let pageLeft = (windowWidth - pageWidth) / 2 // 窗口的水平位置;window.open(‘xxx’, ‘xxx’, width=${pageWidth},height=${pageHeight},top=${pageTop},left=${pageLeft}) // 实现居中弹窗这段代码在主屏幕显示没有问题,可以居中显示,但如果将页面移换到副屏幕进行弹窗时。你会发现,无论参数怎么设置,弹窗都会在屏幕最左侧或屏幕最右侧进行显示,并不是水平居中。点击这里查看示例异常的原因及其解决方案原因可能很多同学都难以想到,这是因为弹窗的left和top参数,并不是基于当前页面作为原点进行计算的,而是以主屏幕作为原点进行计算所以进行位置设置时,需要计算其基于主屏幕的偏移值。那怎么知道当前是否处于主屏幕上呢?可以通过window.screen.availLeft参数来解决,该参数返回浏览器可用空间左边距离屏幕(系统桌面)左边界的距离。通过该参数,甚至不需要知道目前处于哪个屏幕上,直接加上该参数即可基于当前屏幕进行定位。修改后的代码如下const { availLeft, // 返回浏览器可用空间左边距离屏幕(系统桌面)左边界的距离。 availHeight, // 浏览器在显示屏上的可用高度,即当前屏幕高度 availWidth, // 浏览器在显示屏上的可用宽度,即当前屏幕宽度} = window.screenconst pageWidth = 600 // 弹出窗口的宽度const pageHeight = 550 // 弹出窗口的高度let pageTop = (availHeight - pageHeight) / 2 // 窗口的垂直位置let pageLeft = (availWidth - pageWidth) / 2 // 窗口的水平位置;left += availLeft // 加上屏幕偏移值window.open(‘xxx’, ‘xxx’, width=${pageWidth},height=${pageHeight},top=${pageTop},left=${pageLeft}) // 实现居中弹窗「top」参数的设置同样存在这个问题如果主屏幕和副屏幕并不是处于相同的高度,「top」值的设置同样会由于距系统主屏幕定位,而发生定位异常的显示。看下面这张图可能更好地理解另外目前笔者发现,这个兼容性问题,仅会在chrome内核的浏览器存在,safari上运行是不存在该问题的。综上所述,得出最终的解决方案为const { availTop, // 返回浏览器可用空间左边距离屏幕(系统桌面)左边界的距离。 availLeft, // 返回浏览器可用空间左边距离屏幕(系统桌面)左边界的距离。 availHeight, // 浏览器在显示屏上的可用高度,即当前屏幕高度 availWidth, // 浏览器在显示屏上的可用宽度,即当前屏幕宽度} = window.screenconst pageWidth = 600 // 弹出窗口的宽度const pageHeight = 550 // 弹出窗口的高度let pageTop = (availHeight - pageHeight) / 2 // 窗口的垂直位置let pageLeft = (availWidth - pageWidth) / 2 // 窗口的水平位置;if (navigator.userAgent.indexOf(‘Chrome’) !== -1) { // 兼容chrome的bug top += availTop // 距顶偏移值 left += availLeft // 距左偏移值}window.open(‘xxx’, ‘xxx’, width=${pageWidth},height=${pageHeight},top=${pageTop},left=${pageLeft}) // 实现居中弹窗问题解决过程(重点二)笔者遇到该问题是通过如下方式一一寻找解决方案百度最基础,成本最低的一步,笔者进行过以下关键字的搜索(这里主要突出关键字提取)window.open 居中显示window.open left chromewindow.open left 异常window.open 定位 异常window.open chrome 兼容性window.open 双屏显示异常搜索结果,找到了相关的问题,但未能找到真正有效的解决方案。问答论坛stackoverflow,国外著名的编程问答网站,纯英文,内容全。segmentfault,国内的stackoverflow,内容也不错。MDN官网维基百科:MDN Web Docs(旧称Mozilla Developer Network、Mozilla Developer Center,简称MDN)是一个汇集众多Mozilla基金会产品和网络技术开发文档的免费网站。一般可以看作前端基础函数的官方说明文档,具有一定的权威性,当然一定程度上会更为难懂其他页面代码分析寻找网上实现了该功能的网站,下载其页面代码进行分析。网上的代码都是加密过的,虽然不直观,但能推测或猜出一些端倪各关键词搜索首先,通过chrome调试工具,找到触发弹窗的按钮ctrl+s,下载整个页面,通过IDE全局搜索整个页面中关于该按钮的信息,如class,id,及其他属性值,能定位到该按钮的属性都全局搜索一遍逐文件查看,有无相关配置window.open 函数名搜索打开弹窗肯定需要通过该语句,全局搜索,如果window没被覆盖的话应该能找到第三个参数搜索根据 strWindowFeatures 可配置项目进行全局搜索,提取其特点,如「scrollbars」,「titlebar」这些变量以及其字符串形式传参的特点,搜索「,left=」「,height=」重置函数终极大招,函数重置,及通过在chrome控制台重置该函数,来观察其传参情况打开chrome控制台,找到Console栏,拷贝如下代码window.open = function () { console.log(arguments)}再此进行登录弹窗操作,触发函数执行笔者是在前三个方法都失败的情况下,通过第四个方法找到的问题所在。发现其left值传参为负数,在自己项目中设置为负数也能实现居中效果从而推测出原因感谢阅读,祝好 ...

January 23, 2019 · 1 min · jiezi

【RPA开发】UiBot关于如何使用浏览器调用JS命令的说明

在UiBot Creator命令面板的浏览器下面有个执行JS的命令,如下图:大家可能不知道如何使用,实际上这可是做网页自动化处理的神器,当然前提是你需要懂那么一些JS。至于如何使用这个命令,下面我进行一下说明讲解(PS,IE和Chrome通用,使用Chrome前请确保你的扩展程序已经正常安装)首先,使用这个命令JS代码必须遵循一个固定格式function(){ //这里填写你的JS代码}那具体该写些啥呢,我用百度的一个搜索进行举例。我们打开百度搜索UiBot,如下所示:那么我们想抓取下面的搜索结果怎么办呢?有些JS基础的同学们应该能马上想到用CSS选择器获取文本,如下所示这里看到结果的tag为H3,class为t,那么css选择器就可以写成 document.querySelector(“H3.t”),再用textContent即可取到具体内容,我们验证一下发现确实如此,那么构成UiBot调用的代码就是fucntion(){ return document.querySelector(“H3.t”).textContent}好了,再连上绑定好的浏览器,我们在UiBot里面执行一下OK,大功告成,现在我们无论搜索什么都能随着页面变化取到对应的文本,比如我现在搜索 Uibot社区至此,大家赶快打开UiBot去试试吧

January 22, 2019 · 1 min · jiezi

嘿,我造了个代码高亮的插件

源起写这个插件的初衷是因为 Medium 总是不高亮作者的代码。当然也有人用 Codepen, CodeSandbox 或者 Gist 来嵌入代码。 但是这样实在麻烦,有些人(比如我)就很不想为了几句代码而创建一个 Gist 文件,然后直接用 Medium 提供的代码块。所以我经常看到这样的代码块为了解决这个问题,我就写了这个插件来高亮 Medium 里的代码块,后面还意外发现简书,知乎,StackOverflow 等网站都能高亮,嘻嘻!样例先看看效果吧~安装与下载可以点击这里从谷歌应用商店下载。如果不能科学上网,可以直接从我 Github 上直接下载。使用点击插件图标后会弹出下面的设置面板,看起来有点像编辑器。如果像平时写网页的UI布局就太无聊了,所以设计UI成编辑器的样子????。更多更多内容可以访问 Github 上的 repo我也在 B 站上录了个介绍视频,有兴趣可以点击这里观看。最后在巴哈马旅游的时候就在构想这个插件,元旦回来后立马开工写了7天左右,其中也修复了很多 Bug。 当然 Bug 是修不完的,肯定还有一些 Bug 我没发现,如果你遇到了请给我写 Issue! 如果你也喜欢我的插件,麻烦给个五星好评 :),谢谢!

January 21, 2019 · 1 min · jiezi

关于清空Chrome DNS cache

问题通过 VPN + DNS 的方式 将域名 u.foo.com 映射成内网测试环境的地址(如 10.10.205.90)但是连山VPN 配上了DNS后 浏览器中访问的还是生产的地址 但是命令行中解析的是内网地址➜ ~ nslookup u.foo.comServer: 10.10.204.103Address: 10.10.204.103#53Name: u.foo.comAddress: 10.10.205.90即命令中可以正确映射到内网地址 但浏览器中却不可以原因应该是浏览器缓存导致 即已经缓存了 u.foo.com 的IP为生产地址 解决尝试1 通过访问chrome://net-internals/#dns 清空缓存 但一点效果都没有尝试二从这篇文章中得知:在cache查询的时候如果这个cache已经过时了即staled,也会返回null,而判断是否stale的标准如下:即网络发生了变化,或者expired_by大于0,则认为是过时的cache于是通过关闭wifi 再打开的方式 人工触发网络变化 使得cache过期 这次果然好使了

January 19, 2019 · 1 min · jiezi

如何将自己的前端代码,部署到搭建的nginx服务器上?

第一步: 下载 nginxnginx download官网地址下载后,将其解压到 本地的任一目录下。此时我们可以看到有如下目录:html路径下放置我们前端 build好的代码(如何build,相信各位都会),conf下有个非常重要的文件nginx.conf,用来配置nginx服务器。第二步: 配置nginx服务器打开nginx.conf文件,直接找到配置server 的地方,取消掉暂时用不到的配置,下面便是我的配置:server { # 启动后的端口 listen 8880; # 启动时的地址 server_name localhost; # 启动后,地址栏输入: localhost:8880, 默认会在html文件夹下找 index.html文件 location / { root html; index index.html; } # 404页面配置,页面同样在html文件夹中 error_page 404 /404.html; location = /404.html { root html; } # 其他错误码页面配置 error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # 配置代理。由于项目是在本地起动的,而我们的request需要请求其他ip地址。如果你的request链接为localhost:8880/abc/login?name=12345,那么下面配的就是location /abc location /api { proxy_pass http://192.168.0.0:80; } # 一把前端不管用vue,还是react等框架,默认都是单页面的,如果你的项目是多页面的,则需要用到下面的配置。 # 因为此时你的浏览器的url不是localhost:8880/#/login,而是 localhost:8880/a.html/#/login # 所以我们需要将路径中a.html指向具体的html文件夹中的文件,因为默认是index.html location /a.html { alias html; index a.html; } location /b.html{ alias html; index b.html; }}第三步: 将build好的内容放到nginx下的html文件夹下只需要dist下的内容,如第四步: 启动nginx服务器在路径下右键,打开命令号工具,并输入>start nginx然后在浏览器地址栏输入localhost:8880即可第五步: 停止nginx服务器>nginx -s stop ...

January 18, 2019 · 1 min · jiezi

Chrome crx插件扩展离线安装方法 (兼容所有版本)

如果您已经是新版Chrome,会看到如下错误错误提示: 无法从该网站添加应用、扩展程序和用户脚本原因是:自从 Chrome 67 以后就不再支持,拖动安装crx插件的方法了。而目最新版本已经到了 Chrome 71 。 这里简单说一下,兼容所有chrome版本的插件离线安装的方法。一、下载插件使用插件可以极大提高chrome浏览器的用户体验,包括常用的去广告、划词翻译、office文件在线预览、还有一些开发者的调试工具等。插件下载地址 : 极简插件二、安装插件新版chrome的安装方法可以兼容所有版本的chrome浏览器。首先要下载插件伴侣下载地址:官方下载 备用下载打开以后的界面开始之前先退出chrome浏览器选择插件开始安装至此安装完成,打开chrome浏览器,去启用一下插件。点击【右上角3个点】 - 【更多工具】 - 【扩展程序】依次打开 【开发者模式】 和 【插件右下角的开关】 即可。大功告成另外找插件可以参考我之前发的文章:新站上线,分享10个最强chrome浏览器插件!瞬间开发效率加倍

January 18, 2019 · 1 min · jiezi

现代浏览器探秘(part4):事件处理

翻译:疯狂的技术宅原文:https://developers.google.com…当输入到达合成器这是关于Chrome浏览器内部工作原理系列的最后一篇;研究浏览器怎样通过处理代码来显示网站。在上一篇文章中,我们研究了渲染过程并了解了合成器。 在本文中,我们将分析当用户输入时,合成器是怎样实现平滑交互的。从浏览器的角度看输入事件当你听到“输入事件”时,可能只会想到在文本框打字或鼠标单击,但从浏览器的角度来看,输入意味着来自用户的所有动作。 鼠标滚轮滚动是输入事件,触摸或者鼠标移动也是输入事件。当发生类似在屏幕上的触摸的用户动作时,浏览器是最先先接收到动作的进程之一,但是浏览器进程只知道该动作发生的位置。因为选项卡内部的内容由渲染器进程处理,所以浏览器进程会把事件类型(如touchstart)及其坐标发送到渲染器进程。 渲染器进程通过查找事件目标并运行附加的事件侦听器来适当地处理事件。图1:通过浏览器进程路由到渲染器进程的输入事件合成器接收输入事件在上一篇文章中,我们研究了合成器是如何通过合成栅格化图层来平滑地处理滚动的。 如果没有输入事件侦听器附加到页面,那么合成器线程可以创建完全独立于主线程的新复合帧。 但是如果一些事件监听器被附加到页面上会怎样呢? 如果需要处理事件,合成器线程将如何操作呢?图2:将鼠标悬停在页面图层上了解非快速可滚动区域由于JavaScript是运行在主线程上的,所以当合成页面时,合成器线程会标记页面的一个区域,该区域将事件处理程序附加为“非快速可滚动区域”。通过获取此信息,合成器线程可以确保在该区域中发生事件时将输入事件发送到主线程。 如果输入事件来自该区域之外,则合成器线程在不等待主线程的情况下进行合成新帧。图3:输入到非快速可滚动区域的示意图在编写事件处理程序时要注意Web开发中常见的事件处理模式是事件委托。 由于事件冒泡,你可以在最顶层的元素上附加一个事件处理程序,并根据事件目标委派任务。 你可能看到过或写过类似下面的代码。document.body.addEventListener(’touchstart’, event => { if (event.target === area) { event.preventDefault(); }});由于你只需要为所有元素编写一个事件处理程序,因此该事件委托模式在工程上很有吸引力。 但是如果从浏览器的角度来看这段代码,整个页面都被标记成了非快速可滚动区域。那么这意味着什么呢?即使你的应用不关心页面中某些部分的输入,合成器线程也必须与主线程通信,并且在每次输入事件进入时都要等待它。因此合成器的平滑滚动能力被破坏了。图4:在覆盖整个页面的非快速可滚动区域进行输入为了缓解这种情况,你可以在事件侦听器中传递passive:true选项。 这向浏览器提示你仍然希望在主线程中监听事件,同时合成器也可以继续并合成新帧。document.body.addEventListener(’touchstart’, event => { if (event.target === area) { event.preventDefault() } }, {passive: true});检查事件是否可取消想象一下,在页面中有一个框,你希望仅将滚动方向限制为水平滚动。在鼠标事件中使用 passive:true 选项意味着可以平滑滚动页面,但是在你想要用preventDefault 来限制滚动方向时,垂直滚动可能已经开始了。 你可以使用event.cancelable方法对这种情况进行检查。图5:一个部分内容被固定为水平滚动的网页document.body.addEventListener(‘pointermove’, event => { if (event.cancelable) { event.preventDefault(); // block the native scroll /* * do what you want the application to do here */ }}, {passive: true});或者你可以使用CSS规则(例如touch-action)来完全消除事件处理程序。#area { touch-action: pan-x;}查找事件目标当合成器线程向主线程发送输入事件时,首先要做的是命中测试以查找事件目标。 命中测试查找事件发生的坐标之下的内容,它使用在渲染进程中生成的绘制记录数据来完成这一使命。图6:查看绘制记录的主线程询问在x.y坐标点上绘制的内容最小化事件发送到主线程在上一篇文章中,我们讨论了我们的显示器以每秒60次的频率刷新的机制,以及我们怎样跟上节奏来获得流畅的动画效果。 对于输入来说,典型的触摸屏设备每秒发送60-120次触摸事件,而典型的鼠标每秒发送100次事件。 输入事件具有比屏幕刷新更高的保真度。如果类似touchmove的连续事件被发送到主线程120次,那么与屏幕刷新的速度相比,它可能会触发过多的命中测试和JavaScript的执行。图7:充斥在帧时间线上的事件导致页面闪烁为了最大限度地减少对主线程的过度调用,Chrome会合并连续事件(例如wheel, mousewheel, mousemove, pointermove, touchmove),并进行延迟调度,直到下一个 requestAnimationFrame。图8:与上图相同的时间线,但是正在合并和延迟事件任何离散事件,例如 keydown、 keyup、 mouseup、 mousedown、 touchstart、和 touchend 都会被立即发送。使用 getCoalescedEvents 获取帧内事件对于大多数Web应用程序,合并事件应足以提供良好的用户体验。 但是如果要构建一个绘图应用并根据 touchmove 坐标放置路径,则可能会在绘制平滑线时丢失中间坐标。 在这种情况下,你可以在鼠标事件中使用getCoalescedEvents方法来获取有关这些合并事件的信息。图9:左侧是平滑的触摸手势路径,右侧是合并限制路径window.addEventListener(‘pointermove’, event => { const events = event.getCoalescedEvents(); for (let event of events) { const x = event.pageX; const y = event.pageY; // draw a line using x and y coordinates. }});下一步在本系列中,我们介绍了Web浏览器的内部工作原理。 如果你从未想过为什么"开发者工具"建议在你的事件处理中添加{passive: true}或者为什么你可以在脚本标记中编写async属性,我希望本系列能够说明为什么浏览器需要这些信息来提供更快更顺畅的体验。使用Lighthouse如果你想让自己的代码对浏览器友好,但不知道从哪里开始,可以使用Lighthouse这个网站审计工具,它为你提供一份报告,说明正在做什么和需要改进什么。 阅读审核列表还可以让你了解浏览器关注的内容。了解如何衡量性能不同网站的性能调整可能会有所不同,因此,衡量网站的效果并确定最适合你网站的内容至关重要。 Chrome DevTools团队没多少关于如何衡量网站性能的教程。向你的站点添加功能策略功能策略是一个新的Web平台功能,可以在你构建项目时为你提供保护。 启用功能策略可确保应用的某些行为并防止你出错。 例如,如果要确保应用永远不会阻止解析,或者可以在同步脚本策略上运行应用。 启用 sync-script: ’none’ 时,将禁止解析器阻止 JavaScript 执行。 这可以防止你的代码阻止解析器,并且浏览器也不需要担心暂停解析器。总结当开始构建网站时,我几乎只关心如何编写代码以及怎样才能帮助我提高工作效率。 这些很重要,但我们也应该考虑浏览器如何获取我们编写的代码。 现代浏览器将继续致力于为用户提供更好的Web体验。 反过来通过使代码对浏览器友好,也可以改善你的用户体验。 希望我们一起努力追求更好的浏览器!本文首发微信公众号:jingchengyideng点击下面链接查看其它章节文章现代浏览器探秘(part1):架构现代浏览器探秘(part2):导航现代浏览器探秘(part3):渲染现代浏览器探秘(part4):事件处理 ...

January 18, 2019 · 1 min · jiezi

新站上线,分享10个最强chrome浏览器插件!瞬间开发效率加倍

新站【极简插件】打磨已久,终于上线!访问地址:https://chrome.zzzmh.cn借此机会,推荐10个最强chrome插件,瞬间开发效率加倍1、Vue Devtools用于调试Vue.js应用程序的Chrome和Firefox DevTools扩展。Chrome devtools扩展用于调试Vue.js应用程序。访问地址:https://chrome.zzzmh.cn/info?token=nhdogjmejiglipccpnnnanhbledajbpd2、Adblock PlusAdblock Plus 是 Firefox、Chrome、Safari、Android 和 iOS 上最受欢迎的广告拦截程序。拦截 Facebook 和 YouTube 等网站上的弹出窗口和烦人的广告。访问地址:https://chrome.zzzmh.cn/info?token=cfhdojbkjhnklbpkdaibdccddilifddb3、达达划词翻译好看的划词翻译,基于牛津字典的「英英翻译」、「例句」,自带「生词簿」, 并可同步至扇贝、有道,基于记忆曲线的「吐司弹词」,外链「词根词缀」访问地址:https://chrome.zzzmh.cn/info?token=cajhcjfcodjoalmhjekljnfkgjlkeajl4、谷歌访问助手最简单易用的谷歌访问助手,为chrome扩展用户量身打造。可以解决chrome扩展无法自动更新的问题,同时可以访问谷歌google搜索,gmail邮箱,google+等谷歌服务。访问地址:https://chrome.zzzmh.cn/info?token=gocklaboggjfkolaknpbhddbaopcepfp5、广告净化器免费高效的广告过滤工具:可以过滤烦人的视频广告、弹窗广告、大横幅广告等网页广告,让你清爽浏览网页。 功能介绍: 可以屏蔽优酷,爱奇艺,搜狐,腾讯等视频网站的前面几十秒广告而且可以屏蔽弹窗广告,横幅广告,联盟广告等…..访问地址:https://chrome.zzzmh.cn/info?token=cbiaicifbmeokbhollcjfeaoakmppfeh6、Infinity 新标签页 (Pro)Infinity,基于过去对新标签页的认识,觉得新标签页不仅仅如此,应该有更棒的功能和极简的设计。今天我们重新定义了新标签页,一个追求极简美学和一站式服务体验更少的东西体验更多的功能。新一代的标签页,一个更好用,更强大的新标签页。让你更优雅,轻松的使用chrome。访问地址:https://chrome.zzzmh.cn/info?token=nnnkddnnlpamobajfibfdgfnbcnkgngh7、Tampermonkey (油猴插件)Tampermonkey(油猴脚本)是一款免费的浏览器扩展和最为流行的用户脚本管理器,它适用于 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox。 虽然有些受支持的浏览器拥有原生的用户脚本支持,但 Tampermonkey(油猴脚本)将在您的用户脚本管理方面提供更多的便利。 它提供了诸如便捷脚本安装、自动更新检访问地址:https://chrome.zzzmh.cn/info?token=dhdgffkkebhmkfjojejmpbldmpobfkfo8、PostmanPostman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具。访问地址:https://chrome.zzzmh.cn/info?token=fhbjgbiflinjbdggehcddcbncdddomop9、TeamViewerTeamViewer是一款优质的远程支持、远程访问和在线协作软件。事实上,我们认为它是市场上最出色、最强大、最简明的解决方案,许多分析师、行业专家乃至我们的客户都赞同这一观点。访问地址:https://chrome.zzzmh.cn/info?token=oooiobdokpcfdlahlmcddobejikcmkfo10、sFTP Client 2sFTP Client 2 是一个简单易用的FTP客户端,具有现代且易于使用的界面。使用最新的框架和最新的现代设计。访问地址:https://chrome.zzzmh.cn/info?token=gkpahgelmckhiiakigjjegpkljlpmipe安装方法参考之前的博客 关于 chrome 谷歌浏览器 crx 插件 离线拖拽安装方法(兼容各版本) https://zzzmh.cn/single?id=42更多插件,点开极简插件地址查看:https://chrome.zzzmh.cn另外本文也发布在了我的个人博客: https://zzzmh.cn/single?id=55

January 17, 2019 · 1 min · jiezi

外墙清洗为什么要用机器人

上周文章“人工高空作业风险防不胜防”中,列举了很多发生在我们真实生活中的死亡事故,相信大家都感到触目惊心,不由自主的对“蜘蛛人”们抱以很大的同情。当然小编在文章最后也提到了一款“高楼外墙清洗机器人”,那么它将会成为“蜘蛛人”们的福音吗?今天,就让小编带领大家一起来分析一下吧,这款机器人到底有哪些优点呢?为什么外墙清洗要用机器人呢?与传统人工清洗相比,历途机器人有五大明显的优势:1、清洁效果相信大家都知道“蜘蛛人”清洗高楼的时候,用的都是抹布、清洗剂、毛头、刮水刀、铲刀等几样清洁工具,其实和普通的清洁工具没有太大的区别,而且“蜘蛛人”需要被吊到几十米的高空进行作业,所以清洁力度和清洁速度相对来说就会大打折扣,清洁效果也是因人而已参差不齐。而历途机器人则利用负压吸附和四旋翼的技术给了清洁面一定的推力,使得清洁力度相比人工来说更加持久和稳定。同时历途机器人内置滚刷,当机器人开始工作时,滚刷高速运转,对清洁面高速刷洗,即使用清水都可以轻松洗掉墙面的污垢。2、清洁速度根据市场调查,人工清洗高楼外墙的速度大概是每天300-500平方米,当然这个速度是建立在天气情况良好的状态下,如果遇到大风或者雨雪雾等天气的时候,人工清洗是需要停止作业的。而且人工清洗受限于夏季的高温和冬季的严寒,使得外墙清洗具有了淡季和旺季的特点,一年能清洗的时长也就几个月。历途机器人则不受天气情况的影响,无论是大风、高温还是能见度比较低的天气下,机器人都可以正常运行,可以有效延长旺季的时间。而且机器人是上下清洗,相比人工的单向清洗来说,可以不间断的进行工作,使得机器人的清洁速度可以达到人工清洗速度的2-3倍。3、节水并避免二次污染人工清洗属于开放式清洗,用高压水枪冲刷墙面会浪费很多水,而且在清洗过程中,清洗剂会顺着清洁面流到地面或者绿化带上,给周围的卫生以及环境带来一定的破坏。历途机器人有两个可拆卸水箱,内置过滤循环装置,5L水就可以清洗100m²的外立面,耗水量仅是人工清洗的百分之一。而且在机器人清洁过程中,也不会有多余的水渍往下流,避免了对环境的二次污染。4、安全安全这个问题相信不用小编多说大家也都心知肚明,现如今“蜘蛛人”伤亡事故屡屡发生,如果由一台机器人代替人工,那么发生伤亡事故的频率毫无疑问便会减少。更何况历途机器人采用了双安全绳保障措施,按照人工的安全标准来吊装机器人,这样的设计让使用机器人的客户也更加放心。5、清洗成本因为越来越多的年轻人不愿意从事这么高危的工作,使得这个行业的从业者年龄偏大,而且清洁市场需求又是日益增长的,使得近几年已经开始出现旺季用工荒,导致清洁成本也是年年增长。假设有一栋40000㎡的大楼需要清洗,物业公司找保洁公司需要支付3元/㎡左右的费用,那么清洗一次就需要12万左右的费用,如果一年洗两次就需要20几万。但是如果使用历途机器人进行外墙清洗,只需要物业工程部的几个普通工人就可以轻松完成清洗工作,清洁过程中的耗材费用相比人工清洗也是微不足道,购买一台机器人的成本仅是人工清洗2-3次的费用,这么算下来是不是超划算?随着我国经济的不断进步与发展,我国的环境问题越来越凸显,大气中的有害气体和油烟等污染和化学反应的侵蚀都使我们不得不对楼宇进行多次的清洗保护。在人工智能高速发展的今天,用机器人代替人工进行高危、重复且繁重的工作,已经成为当下必然的趋势。它不仅可以有效的释放劳动力,而且工作水平、效率相比人类更高、更安全,我们又有什么理由拒绝它呢?北京历途科技有限公司是一家专注于人工智能与机器人研发的高新技术企业,经过十几年的技术积累,现已自主研发出专业、高效、安全的高楼外墙清洗机器人,填补了国际外墙清洁市场智能化产品空白。公司以"人人皆创客,完美做产品"为发展理念,打造具有颠覆性的科技产品。

January 17, 2019 · 1 min · jiezi

浏览器端用JS实现创建和下载图片

问题场景在前端很多的项目中,文件下载的需求很常见。尤其是通过JS生成文件内容,然后通过浏览器端执行下载的操作。如图片,Execl 等的导出功能。日前,项目中就遇到了这类需求,在浏览器端实现保存当前网页为图片,然后还可以下载。解决方案网页生成图片这里可以采用 html2canvas 来实现。并且可以兼容大部分主流的浏览器。Firefox 3.5+Google ChromeOpera 12+IE9+Safari 6+文件下载第一种方案HTML5 新增了 download 属性,只要给 download 加上想要导出的文件名即可。如 download=“file.jpg”。想要详细的了解此属性,可以参考 张鑫旭 写的一篇博文,传送门。简单代码实现如下:import html2canvas from ‘html2canvas’;// 生成图片,并自动下载function captureScreenshot() { const preview = document.querySelector(’.preview-graphs’); html2canvas(preview).then((canvas) => { var img = document.createElement(‘a’); img.href = canvas.toDataURL(‘image/jpeg’).replace(‘image/jpeg’, ‘image/octet-stream’); // 下载的文件名字 img.download = file.jpg; img.click(); }) }Note:上述实现,目前只有 Google Chrome 功能是正常的。兼容性存在很大的问题。第二种方案这里可以采用 FileSaver.js。需以 Blob 的形式来进行保存。canvas 提供了一种创建 Blob 对象的方法 canvas.toBlob((blob) => {}) ,但是兼容性堪忧,绝大部分浏览器都没有实现。因此官网特意提供了这样的一个库,canvas-toBlob.js。FileSaver.js is the solution to saving files on the client-side, and is perfect for web apps that generates files on the client, However if the file is coming from the server we recommend you to first try to use Content-Disposition attachment response header as it has more cross-browser compatible. - 摘自官网FileSaver.js 是在客户端保存文件的解决方案,非常适合在客户端生成文件的Web应用程序,但是如果文件来自服务器,我们建议您首先尝试使用 Content-Disposition 附件响应 标题,因为它有更多的跨浏览器兼容。可以兼容主流的浏览器,如 Firefox,Chrome,Edge,IE 10+ 等。代码实现如下:import html2canvas from ‘html2canvas’;import FileSaver from ‘file-saver’;// 这里没有用 canvas-toBlob.js// base64 转换成 Blobfunction dataURLToBlob(dataURL) { var BASE64_MARKER = ‘;base64,’; var parts; var contentType; var raw; if (dataURL.indexOf(BASE64_MARKER) === -1) { parts = dataURL.split(’,’); contentType = parts[0].split(’:’)[1]; raw = decodeURIComponent(parts[1]); return new Blob([raw], {type: contentType}); } parts = dataURL.split(BASE64_MARKER); contentType = parts[0].split(’:’)[1]; raw = window.atob(parts[1]); var rawLength = raw.length; var uInt8Array = new Uint8Array(rawLength); for (var i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], {type: contentType});}// 生成图片,并自动下载function captureScreenshot() { const preview = document.querySelector(’.preview-graphs’); html2canvas(preview).then((canvas) => { const fileBlob = dataURLToBlob(canvas.toDataURL(‘image/jpeg’).replace(‘image/jpeg’, ‘image/octet-stream’)); const fileName = file.jpg; FileSaver.saveAs(fileBlob, fileName); }); }相关链接BlobHTMLCanvasElement.toBlob()HTMLCanvasElement.toDataURL() ...

January 17, 2019 · 2 min · jiezi

现代浏览器探秘(part3):渲染

翻译:疯狂的技术宅原文:https://developers.google.com…渲染器进程的内部工作原理这是关于浏览器内部工作原理系列的第3部分。 之前,我们介绍了多进程架构和导航流程。 在这篇文章中,我们将看看渲染器进程内部发生了什么。渲染进程涉及Web性能的诸多方面。 由于渲染进程中发生了很多事情,因此本文不能一一赘述。 如果你想深入挖掘,可以在Web基础的性能部分找到更多内容。渲染器进程处理Web内容渲染器进程负责选项卡内发生的所有事情。 在渲染器进程中,主线程处理你为用户编写的大部分代码。 如果你使用了web worker 或 a service worker,有时JavaScript代码的一部分将由工作线程处理。 排版和栅格线程也在渲染器进程内运行,以便高效、流畅地呈现页面。渲染器进程的核心工作是将HTML、CSS和JavaScript转换为用户可以与之交互的网页。图1:渲染器进程内部有主线程、工作线程、排版线程和栅格线程解析构建DOM当渲染器进程收到导航的提交消息并开始接收HTML数据时,主线程开始解析文本字符串(HTML)并将其转换为文档对象模型(DOM—Document Object Model )。DOM是页面在浏览器中的内部表示,同时也是Web开发人员可以通过 JavaScript 与之交互的数据结构和API。HTML标准将HTML文档解析为DOM。 你可能已经注意到,将HTML提供给浏览器从不会引发错误。 例如,缺少结束</ p>标记是有效的HTML。 像 Hi! <b>I’m <i>Chrome</b>!</i> 这样的错误标记(b标签在i标签之前被关闭)被看作是 Hi! <b>I’m <i>Chrome</i></b><i>!</i>。 这是因为HTML规范旨在优雅地处理这些错误。 如果你对如何完成这些工作感到好奇,可以阅读HTML规范中的“解析器中的错误处理和奇怪情况介绍”部分。子资源加载网站通常使用图像、CSS和JavaScript等外部资源。 这些文件需要从网络或缓存中加载。 主线程可以在解析构建DOM时会逐个请求它们,但为了加快速度,“预加载扫描器”也会同时运行。 如果HTML文档中存在<img>或<link>之类的内容,则预加载扫描器会检查由HTML解析器生成的标记,并在浏览器进程中向网络线程发送请求。图2:主线程解析HTML并构建DOM树JavaScript可以阻止解析当HTML解析器找到<script>标记时,它会暂停解析HTML文档,并且必须加载、解析和执行JavaScript代码。 为什么要这样处理? 因为JavaScript可以使用像document.write() 那样改变整个DOM结构的东西来改变文档的形状(HTML规范中的解析模型概述有一个很好的示意图)。 这就是HTML解析器在重新解析HTML文档之前必须等待JavaScript运行的原因。 如果你对JavaScript执行中发生的事情感到好奇,V8团队的博客对此进行了讨论。提示浏览器如何加载资源Web开发人员可以通过多种方式向浏览器发送提示,以便很好地加载资源。 如果你的JavaScript不使用 document.write(),则可以向<script>标记添加async或defer属性。 然后,浏览器异步加载和运行JavaScript代码,不会阻止解析。 如果合适,你也可以使用JavaScript模块。 <link rel =“preload”>是一种通知浏览器当前导航肯定需要这个资源的方法,你希望尽快下载。 你可以在资源优先级找到更多信息。样式表计算拥有DOM不足以知道页面的外观,因为我们可以在CSS中设置页面元素的样式。 主线程解析CSS并确定每个DOM节点的计算样式。 这是有关基于CSS选择器将哪种样式应用于每个元素的信息。 你可以在浏览器中开发者工具中的computed部分中看到此信息。图3:主线程解析CSS以添加计算样式即使你不提供任何CSS,每个DOM节点都具有计算样式。比如 <h1>标签的显示要大于<h2>标签,同时为每个元素定义边距。 这是因为浏览器具有默认样式表。 如果你想知道Chrome的默认CSS是什么样的,你可以在此处查看源代码。布局现在,渲染器进程知道每个节点的文档和样式的结构,但这还不足以呈现页面。 想象一下,你正试图通过手机向朋友描述一幅画: “有一个大的红色圆圈和一个小的蓝色方块” 这并不能完全让你的朋友了解这幅画的外观。图4:一个人站在一幅画,通过电话线与另一个人联系布局是查找元素几何的过程。 主线程遍历DOM并计算样式和创建布局树,其中包含x y坐标和边界框大小等信息。 布局树可以是与DOM树类似的结构,但它仅包含与页面上可见内容相关的信息。 如果display:none,则该元素不是布局树的一部分(但是在布局树中包含visibility:hidden的元素)。 类似地,如果应用具有类似p::before {content:“Hi!}之类的内容的伪类,则它将包含在布局树中,即使它不在DOM中。图5:主线程通过DOM树生成计算样式和布局树确定页面布局是一项具有挑战性的任务。 即使是最简单的页面布局,如从上到下的块流,也必须考虑字体的大小以及在哪里划分它们,因为它们会影响段落的大小和形状; 然后影响下一段所需的位置。图6:由于换行符而移动的段落的框布局CSS可以使元素浮动到一侧,掩盖溢出项,并更改写入方向。 你可以想象,这个布局阶段是一项艰巨的任务。 在Chrome项目中,有一个完整的工程师团队负责布局。 如果你想看到他们工作的细节,看看这些会议记录非常有意思。绘制拥有了DOM、样式和布局仍然不足以呈现页面。 假设你正在尝试重现一幅画。 你不仅需知道元素的大小,形状和位置,还需要判断绘制它们的顺序。图7:一个在画布前拿着画笔的人,正在思考是应该先画圆圈还是矩形例如:可以为某些元素设置z-index,在这种情况下,按HTML中编写的元素顺序绘制将导致不正确的呈现。图8:页面元素按HTML标记的顺序出现,会导致错误的渲染图像,因为没有考虑z-index在此绘制步骤中,主线程遍历布局树以创建绘制记录。 绘制记录是绘制过程的一个注释,如“背景优先,然后是文本,最后是矩形”。 如果你使用JavaScript绘制了<canvas>元素,那么可能对此过程很熟悉。图9:主线程遍历布局树并生成绘制记录更新渲染通道的成本很高在渲染通道中最重要的一件事就是在每个步骤中,前一个操作的结果被用于创建新数据。 例如:如果布局树中的某些内容发生更改,则需要为文档的受影响部分重新生成绘制顺序。图10:DOM + Style,布局和绘制树的生成顺序如果要为元素设置动画,则浏览器必须在每个帧之间运行这些操作。 我们的大多数显示器每秒刷新屏幕60次(60 fps); 当你在每一帧移动屏幕时,动画对人眼来说会很平滑。 但是如果动画错过了其中的帧,则页面将发生闪烁。图11:时间轴上的动画帧即使你的渲染操作能够跟上屏幕刷新,这些计算也是在主线程上运行的,这意味着当你的应用运行 JavaScript 时它可能会被阻止。图12:时间轴上的动画帧,但JavaScript阻止了一帧你可以将JavaScript操作划分为小块,并使用 requestAnimationFrame() 安排在每个帧上运行。 有关此主题的更多信息,请参阅优化JavaScript执行。 你也可以在 Web Workers 中运行 JavaScript 来避免阻塞主线程。图13:在动画帧的时间轴上运行的较小的JavaScript块合成你会如何绘制一个页面?现在浏览器知道文档的结构,每个元素的样式,页面的几何形状和绘制顺序,它是如何绘制页面的? 将此信息转换为屏幕上的像素称为光栅化。图14:简单光栅化过程也许处理这种情况的一种简单的方法是在视口(viewport)内部使用栅格部件。 如果用户滚动页面,则移动光栅帧,并通过更多光栅填充缺少的部分。 这就是Chrome首次发布时处理栅格化的方式。 但是,现代浏览器运行一个称为合成的更复杂的过程。什么是合成合成是一种将页面的各个部分分层,分别栅格化,并在一个被称为合成器线程的独立线程中合成为页面的技术。 如果发生滚动,由于图层已经被栅格化,所以它所要做的就是合成一个新帧。 通过移动图层和合成新帧,可以用相同的方式实现动画。图15:合成过程的示意动画你可以使用浏览器开发者工具的“layout”面板中查看你的网站如何划分为多个图层。分为几层为了找出哪些元素需要放在哪些层中,主线程通过遍历布局树以创建层树(此部分在DevTools性能面板中称为“Update Layer Tree”)。 如果页面某些应该是单独图层(如滑入式侧面菜单)的部分但是没有分配到图层,那么你可以使用CSS中的will-change属性提示浏览器。图16:主线程生通过遍历布局树来成层树也许你想要为每个元素提供图层,但是过多的图层进行合成可能会导致比每帧光栅化页面的小部分更慢的操作,因此测量应用程序的渲染性能至关重要。 有关主题的更多信息,请参阅Stick to Compositor-Only Properties and Manage Layer Count光栅和复合关闭主线程一旦创建了层树并确定了绘制顺序,主线程就会将该信息提交给合成器线程。 合成器线程然后栅格化每个图层。 一个图层可能像页面的整个长度一样大,因此合成器线程会将它们分成图块,并将每个图块发送到光栅线程。 栅格线程栅格化每一个tile并将它们存储在GPU内存中。图17:栅格线程创建tile位图并发送到GPU合成器线程可以优先考虑不同的aster线程,以便视口(或附近)内的事物可以先被光栅化。 图层还具有多个不同分辨率的倾斜度,可以处理放大操作等内容。一旦tile被光栅化,合成器线程会收集称为绘制四边形(draw quads )的tile信息来创建合成器帧(compositor frame)。绘制四边形包含信息,例如图块在内存中的位置以及在考虑页面合成的情况下绘制图块的页面中的位置。合成器帧表示页面帧的绘制四边形的集合。然后通过IPC将合成器帧提交给浏览器进程。这时可以从UI线程添加另一个合成器帧以用于浏览器UI更改,或者从其他渲染器进程添加扩充数据。 这些合成器帧被发送到GPU用来在屏幕上显示。 如果发生滚动事件,合成器线程会创建另一个合成器帧并发送到GPU。图18:合成器线程创建合成帧。 帧先被发送到浏览器进程,然后再发送到GPU合成的好处是它可以在不涉及主线程的情况下完成。 合成线程不需要等待样式计算或 JavaScript 执行。 这就是合成动画是平滑性能的最佳选择的原因。 如果需要再次计算布局或绘图,则必须涉及主线程。总结在本文中,我们研究了从解析到合成的渲染通道。 在本系列的下一篇文章中,我们将更详细地介绍合成器线程,并了解当用户进行鼠标移动和单击等操作时会发生什么。本文首发微信公众号:jingchengyideng点击下面链接查看其它章节文章现代浏览器探秘(part1):架构现代浏览器探秘(part2):导航现代浏览器探秘(part3):渲染现代浏览器探秘(part4):事件处理 ...

January 17, 2019 · 1 min · jiezi

现代浏览器探秘(part2):导航

翻译:疯狂的技术宅原文:https://developers.google.com…导航时都发生了什么这是关于Chrome内部工作原理系列的第2部分。 在上一篇文章中,我们研究了不同的进程与线程是怎样如何处理浏览器不同部分的。 在这一篇中,我们将会深入研究每个进程和线程是如何进行通信以显示网站内容的。让我们看一下Web浏览的简单用例:你在浏览器中键入URL,然后浏览器从Internet获取数据并显示页面。 在这篇文章中,我们将重点关注用户请求网站的部分以及浏览器准备呈现页面的部分 - 也称为导航。从浏览器进程开始正如我们在第1部分(CPU,GPU,内存和多进程架构 )中所描述的,选项卡外部的所有内容都由浏览器进程处理。 浏览器进程具有很多线程,比如于UI线程用于绘制浏览器的按钮和输入框,网络线程负责处理网络堆栈以从互联网接收数据,存储线程控制对文件的访问等。 当在地址栏中键入URL时,你的输入将由浏览器进程的UI线程处理。图1:顶部的浏览器UI,底部有UI,网络和存储线程的浏览器进程图一个简单的导航过程第1步:处理输入当用户开始输入地址栏时,UI线程首先要判断的是“这是搜索查询还是URL?”。 因为在Chrome中,地址栏也是搜索输入框,因此UI线程需要解析并判断是将你的输入发送到搜索引擎还是去请求对应的网站。图1:UI线程询问输入是搜索查询还是URL第2步:开始导航当用户敲回车时,UI线程启动网络调用以获取站点内容。 加载指示图标显示在选项卡的一角,网络线程使用适当的协议,如DNS解析和为请求建立TLS连接。图2:UI线程与网络线程进行通信以导航到mysite.com此时,网络线程可以接收像HTTP 301那样的服务器重定向头。在这种情况下,网络线程会通知UI线程服务器正在请求重定向。之后会启动另一个URL请求。第3步:读取响应一旦响应主体(有效负载)开始进入,网络线程会在必要时查看流的前几个字节。 响应中的Content-Type头应该说明它是什么类型的数据,但由于它可能丢失或发生错误,所以在这里完成MIME类型嗅探。 这在源代码的注释中被称为“棘手的事情”。 你可以阅读这些注释,来了解不同的浏览器是如何处理内容类型与有效载荷的。图3:包含Content-Type和有效负载的响应头,它是实际数据如果响应是HTML文件,那么下一步就是将数据传递给渲染器进程,但如果它是zip文件或其他文件,则表示它是一个下载请求,因此需要将数据传递给 下载管理器。图4:网络线程询问响应数据是否来自安全站点的HTML这也是进行 SafeBrowsing检查的地方。 如果域和响应数据似乎与已知的恶意站点匹配,则网络线程会发出警告以显示警告页面。 此外,发生跨源读取阻止(CORB)检查是为了确保敏感的跨站数据不会进入渲染器进程。第3步:查找渲染器进程完成所有检查并且网络线程确信浏览器应该导航到所请求的站点后,网络线程会告知UI线程数据已准备就绪。 然后UI线程找到渲染器进程以进行网页的渲染。图5:网络线程告诉UI线程找到渲染进程由于网络请求可能需要几百毫秒才能得到响应,所以在这里进行了加速此过程的优化。 当UI线程在第2步向网络线程发送URL请求时,它已经知道他们正在导航到哪个站点。 UI线程尝试与网络请求并行地主动查找或启动渲染器进程。 如果一切按预期进行,当网络线程接收数据时,渲染器进程已处于备用状态。 如果导航重定向跨站点,则可能不会使用此备用进程,在这种情况下可能需要不同的进程。第4步:提交导航现在数据和渲染器进程已准备就绪,IPC将把导航从浏览器进程发送到渲染器进程以进行提交。它同时还传递数据流,因此渲染器进程可以继续接收HTML数据。 一旦浏览器进确认已经提交到了渲染器进程中,导航就完成了,文档加载阶段就开始了。此时,地址栏会更新,安全指示器和站点设置UI会反映新页面的站点信息。 选项卡的会话历史记录将更新,因此后退/前进按钮将可以逐步浏览刚导航到的站点。为了便于在关闭选项卡或窗口时能够对选项卡/会话进行还原,会话的历史记录将被存储在磁盘上。图6:浏览器和渲染器进程之间的IPC,请求呈现页面额外步骤:初始加载完成提交导航后,渲染器进程继续加载资源并呈现页面。 我们将会在下一篇文章中详细介绍这一阶段的详情。 一旦渲染器进程“完成”渲染,它就会将一个IPC发送回浏览器进程(这发生在所有onload事件触发了页面中的所有帧并完成执行之后)。 此时,UI线程会停止选项卡上的加载指示器。尽管已经“完成”,不过客户端 JavaScript 仍然可以加载额外的资源并在此之后呈现新的视图。图7:渲染器进程通过IPC通知浏览器进程页面已“加载完成”导航到其他站点简单的导航完成了! 但是如果用户再次将不同的URL放到地址栏会发生什么? 好吧,浏览器进程会通过相同的步骤导航到不同的站点。 但在它在做到这一点之前,还需要检查当前正在渲染的站点,如果他们关心beforeunload事件的话。当你尝试重新导航或关闭选项卡时,beforeunload可以创建“要离开这个网站吗?” 警告。 由于选项卡内包含JavaScript代码的所有内容都由渲染器进程处理,因此浏览器进程必须在进行新导航请求时检查当前渲染器进程。警告:不要添加无条件的beforeunload处理程序。 因为它会产生更多延迟,甚至在启动导航之前需要执行一些处理。 应该仅在需要时添加此事件处理,例如,如果需要警告用户他们可能会丢失在页面上输入的数据时。图8:浏览器进程通过IPC通知渲染器进程它将要导航到另一个站点如果导航是从渲染器进程启动的(例如用户单击链接或客户端JavaScript执行window.location =“https://newsite.com”),那么渲染器进程会首先检查beforeunload处理。 然后,它经历与浏览器进程启动导航相同的过程。 唯一的区别是导航请求从渲染器进程发送到浏览器进程。当新导航进入的站点与当前渲染的站点不同时,将会调用另一个单独的渲染进程来处理新导航,同时保持当前渲染进程以处理unload等事件。 有关更多信息,请参阅页面生命周期状态概述以及如何使用 页面生命周期 API 挂钩事件。图9:从浏览器进程到新渲染器进程的2个IPC,通知新渲染器渲染页面并通知旧渲染器进程卸载如果是Service Worker最近对该导航过程的一个改变是引入了service worker。 service worker是一种在应用代码中编写网络代理的方法;它允许Web开发人员更好地控制本地缓存内容以及何时从网络获取新数据。 如果将service worker设置为从缓存加载页面,则无需从网络请求数据。要记住的重要一点是Service Worker是在渲染器进程中运行的JavaScript代码。 但是当导航请求到来时,浏览器进程怎么才能知道该站点有Service Worker?图10:浏览器进程中的网络线程查找Service Worker范围注册Service Worker时,将保留Service Worker的范围作为参考(你可以在“Service Worker生命周期”一文中阅读有关范围的更多信息)。 当导航发生时,网络线程根据注册的Service Worker范围检查域,如果为该URL注册了Service Worker,则UI线程找到渲染器进程来执行Service Worker代码。 Service Worker可以从缓存加载数据,无需从网络请求数据,也可以从网络请求新资源。图11:浏览器进程中的UI线程启动渲染器进程以处理Service Worker; 然后,渲染器进程中的工作线程从网络请求数据导航预加载可以看到,如果Service Worker最终决定从网络请求数据,则浏览器进程和渲染器进程之间的往返通信可能会导致延迟。 导航预加载是一种通过与Service Worker并行加载资源来加速此过程的机制。 它用header标记这些请求,允许服务器为这些请求发送不同的内容,例如:只更新部分数据而不是整个文档。图12:浏览器进程中的UI线程启动渲染器进程,在并行启动网络请求的同时处理Service Worker总结在本文中,我们研究了导航过程中发生的事情,以及响应头和客户端JavaScript等Web应用代码是如何与浏览器交互的。 了解浏览器通过网络获取数据的步骤,可以更容易地理解为什么开发导航预加载等API。 在下一篇文章中,我们将深入探讨浏览器如何处理HTML/ CSS/JavaScript来呈现页面。本文首发微信公众号:jingchengyideng点击下面链接查看其它章节文章现代浏览器探秘(part1):架构现代浏览器探秘(part2):导航现代浏览器探秘(part3):渲染 ...

January 16, 2019 · 1 min · jiezi

小技巧:浏览器里显示成星号的密码,忘记了该怎么办?

比如下图这种,记性不好,忘记了密码该怎么办?很简单,Chrome浏览器里按F12打开开发者工具,切换到Elements面板,点击下图Step1的图标,然后单击密码显示框,保持这个字段处于选中状态,然后切换到Console标签页,输入$0.value, 回车就能看到密码了。要获取更多Jerry的原创文章,请关注公众号"汪子熙":

January 15, 2019 · 1 min · jiezi

【译】现代浏览器探秘(part 1):架构

翻译:疯狂的技术宅 原文链接:https://developers.google.com…CPU,GPU,内存和多进程架构在这个由4部分组成的系列文章中,我们将介绍Chrome浏览器从高级架构到渲染管道的具体细节。 如果你想知道浏览器是如何将你的代码转换为功能性网站的,或者你想知道为什么需要使用某些特定技术来提高性能,那么本系列非常适合你。作为本系列的第1部分,我们将介绍核心计算术语和Chrome的多进程架构。注意:如果你熟悉CPU / GPU和进程/线程的概念,则可以跳到本文的浏览器体系结构部分。计算机的核心是CPU和GPU为了理解浏览器运行的环境,我们需要了解一些计算机部件及其功能。CPU首先是中央处理单元(Central Processing Unit)—— CPU。 CPU可以被认为是你计算机的大脑。 CPU核心,在这里作为办公室工作人员,可以在他们进来时逐个处理许多不同的任务。它可以处理从数学到艺术的所有事情,同时知道如何回复客户呼叫。 在过去,大多数CPU都是单芯片。 核心就像生活在同一芯片中的另一个CPU。 在现代硬件中,你通常会获得多个核心,从而为你的手机和笔记本电脑提供更强的计算能力。图1:4个CPU核心作为办公室工作人员坐在每个办公桌处理任务GPU图形处理单元(Graphics Processing Unit )—— GPU是计算机的另一部分。 与CPU不同,GPU擅长处理简单任务,但同时跨多个核心。 顾名思义,它最初是为处理图形而开发的。 这就是为什么在图形环境中“使用GPU”或“GPU支持”与快速渲染和平滑交互相关联。 近年来,随着GPU加速计算,仅在GPU上就可以实现越来越多的计算。图2:许多带有扳手的GPU核心表明它们可以处理有限的任务当你在计算机或手机上启动程序时,CPU和GPU用来支持程序的运转。 通常,程序使用操作系统提供的相关机制在CPU和GPU上运行。图3:三层计算机体系结构。 机器硬件位于底部,操作系统位于中间,应用程序位于顶部。在进程和线程上执行程序在深入浏览器架构之前要掌握的另一个概念是Process和Thread。 进程可以描述为运行状态中的程序。 线程是存在于进程内部并用来执行其程序任务的某一部分。图4:过程划定了边界,线程作为在进程内游动的“抽象鱼”启动程序时,将会创建一个进程。 该程序可能会创建线程来帮助它工作,但这是可选的。 操作系统为进程提供了一“块”内存,并且所有程序状态都保存在该专用内存空间中。 当你关闭程序时,该进程也会消失,操作系统会释放内存。图5:进程使用内存空间和存储数据的示意图(sf不支持svg动图上传,看不到请使用技术手段查看原图)进程可以要求操作系统启动另一个进程来执行不同的任务。 当这种情况发生时,将为新进程分配不同的内存。 如果两个进程需要通信,他们可以通过使用进程间通信(IPC)来实现。 许多程序都是以这种方式工作的,因此如果一个工作进程失去响应,则可以重新启动它,而不会停止运行程序的其他进程。图6:通过IPC进行通信的独立进程示意图(sf不支持svg动图上传,看不到请使用技术手段查看原图)浏览器架构那么如何使用进程和线程构建Web浏览器? 好吧,它可能是一个具有许多不同线程的进程,或是许多具有少量线程的通过IPC进行通信的不同进程。图7:不同浏览器体系结构中的进程/线程示意图在这里有非常重要的一点需要注意,这些不同的架构是实现细节。关于如何构建Web浏览器并没有标准规范。 一种浏览器可能与另一种浏览器的结构完全不同。在本系列文章中,我们将使用下图中描述的Chrome最新架构。最重要的部分是浏览器进程怎样与程序的其他工作进程进行协调。 对于渲染器进程,将创建多个进程并将其分配给每个选项卡。 直到不久前,Chrome才为每个标签提供了一个进程;现在它尝试为每个站点提供自己的进程,其中包括iframe(请参阅:站点隔离部分)。图8:Chrome的多进程架构图。 渲染进程下显示多个图层,表示Chrome为每个选项卡运行多个渲染器进程。每个进程都做些什么?下表介绍了每个Chrome进程及其控制的内容:进程做些什么Browser控制程序的“chrome”部分,包括地址栏,书签,后退和前进按钮。<br/>还处理Web浏览器的不可见的,和特权部分,例如网络请求和文件访问。Renderer负责显示网站的选项卡内的所有内容。Plugin控制网站使用的所有插件,例如flash。GPU独立于其他进程的GPU处理任务。 它被分成多个不同的进程,因为GPU处理来自多个程序的请求并将它们绘制在同一个面中。图9:指向浏览器UI不同部分的不同进程还有更多的进程,如扩展进程和功能进程。 如果你想查看Chrome中正在运行的进程数,请点击右上角的选项菜单图标“more_vert”,选择“更多工具”,然后选择“任务管理器”。 这将打开一个窗口,其中包含当前正在运行的进程列表以及它们使用的CPU/内存量。Chrome中多进程架构的好处前面我曾提到Chrome使用多个渲染器进程。 在最简单的情况下,你可以想象每个选项卡都有自己的渲染器进程。 假设你打开了3个选项卡,每个选项卡都由独立的渲染器进程运行。 如果一个选项卡没有响应,就可以关闭无响应的选项卡并继续运行,同时保持其他选项卡处于活动状态。 如果所有选项卡都在一个进程上运行,那么当一个选项卡无响应时,所有选项卡都不会响应。 那将会很难受。图10:显示多进程运行每个选项卡的示意图(sf不支持svg动图上传,看不到请使用技术手段查看原图)将浏览器的工作分成多个进程的另一个好处是安全性和沙盒。由于操作系统提供了限制进程权限的方法,因此浏览器可以从某些功能中对某些进程进行沙箱处理。 例如,Chrome浏览器限制任意用户输入进程的(如渲染器进程)的任意文件访问。由于进程有自己的私有内存空间,因此它们通常包含公共基础结构的副本(例如V8是Chrome的JavaScript引擎)。 这意味着会消耗更多的内存空间,因为如果它们运行在同一进程内的不同线程上,则无法遵循自己的机制进行共享。 为了节省内存,Chrome限制了它可以启动的进程数量,这种限制因设备的内存和CPU功率而异,但当Chrome达到限制时,它会在一个进程中运行从同个一站点打开的多个选项卡。节省更多内存:Chrome中的服务化同样的方法适用于浏览器进程。 Chrome正在进行体系结构的变更,以便将浏览器程序的每个部分作为一项服务运行,从而可以轻松拆分为不同的流程或汇总为一个流程。一般的想法是,当Chrome在强大的硬件上运行时,它可能会将每个服务拆分为不同的进程,从而提供更高的稳定性,但如果它位于资源有限的设备上,则Chrome会将服务整合到一个进程中,从而节省内存占用。 在进行这种更改之前,在Android平台上已经使用了类似的方法来整合进程以减少内存使用。图11:Chrome的服务化示意图,将不同的服务转移到多个进程或一个浏览器进程中(sf不支持svg动图上传,看不到请使用技术手段查看原图)帧渲染器进程:站点隔离网站隔离是Chrome中最近推出的一项功能,可为每个跨网站的iframe运行单独的渲染进程。 我们一直在讨论每个选项卡一个渲染进程的模型,它允许跨站iframe在单个渲染器进程中运行,并在不同站点之间共享内存空间。 在同一个渲染进程中运行a.com和b.com似乎没问题。 同源策略是Web的核心安全模型,它确保一个站点在未经同意的情况下无法访问其他站点的数据。 绕过此策略是安全攻击的主要目标。进程隔离是分离站点的最有效方法。 由于Meltdown和Spectre漏洞,我们更加需要使用进程来隔离站点。 默认情况下,自从Chrome 67启用桌面隔离功能后,选项卡中的每个跨站点iframe都会得到单独的渲染进程。图12:站点隔离示意,指向站点内iframe的多个渲染器进程启用站点隔离是一项需要很多年的工作。 站点隔离并不像分配不同的渲染进程那么简单;它从根本上改变了iframe彼此的交流方式。 在运行着不同iframe进程的的页面上打开devtools,意味着devtools必须在背后做大量的工作才能使其看起来无缝。即使通过简单的 Ctrl + F 来查找页面中的单词也意味着需要跨越不同的渲染进程进行搜索。 这就是浏览器工程师将站点隔离的发布作为一个重要里程碑的原因!总结在这篇文章中,我们介绍了浏览器体系结构的高级视图,并介绍了多进程体系结构的优点。 我们还介绍了Chrome中与多进程架构密切相关的服务化和站点隔离。 在下一篇文章中,我们将开始深入研究在显示一个网站时,这些进程和线程之间究竟发生了什么事情。本文首发微信公众号:jingchengyideng点击下面链接查看其它章节文章现代浏览器探秘(part1):架构现代浏览器探秘(part2):导航现代浏览器探秘(part3):渲染

January 15, 2019 · 1 min · jiezi

利用 Postman Chrome app 和 Chrome 浏览器共享网站 cookie

背景作为一个Web工程师,最熟悉的日常工作莫过于后台接口开发与联调测试,而在接口测试上,大家最喜爱的工具清单里,必然少不了 Postman 这一利器。然而,有时接口测试需要准备好登录态,或者其他状态数据,而这些数据往往就存在浏览器 Cookie 里边。结合本文介绍的工具,便可以无缝在 Postman Chrome app (为什么强调是 Postman Chrome app,文章末尾会说明)和 Chrome 浏览器之间共享 Cookie,而这个共享过程对用户是透明的。工具清单以下工具请自行安装,我只贴下官方的软件界面截图。Chrome 浏览器Postman Chrome appPostman Interceptor使用步骤以下我们以 Github 网站为例,演示下如何实现 Cookie 共享。一、确认 Postman Interceptor 插件安装成功(如图所示)二、启动 Postman,在右上角的卫星小图标那里开启 Chrome Interceptor三、在 Chrome 浏览器里正常登陆 GitHub 网站(此步骤没什么好演示的 ╭(╯^╰)╮)四、在 Postman Chrome app 中直接模拟请求通知接口接口路径:https://github.com/notificati…也就是说,这个时候,我们虽然没有对 Postman 做特殊的 Cookie 设置,但是它的请求的登录态都被服务器验证通过了,cookie 共享成功!假如这个时候退出浏览器的登录态呢?我们先从 GitHub 退出登录,还是刚才的请求,这个时候的响应是:是的,因为 Chrome 里已经退出登录,所以 Postman 这边也自然失去登录态了,说明两边 Cookie 是同步的。Postman Interceptor 的 BonusPostman Interceptor 还有一点比较爽的是,它的 Request Capture 支持捕捉 Chrome 浏览器里的请求记录,并且自动同步到 Postman Chrome app 里边,这样的话,我们就可以方便直接在 Postman 里获取到我们需要测试的网络请求,而不是一个一个自己填写参数之类的了。缺陷遗憾的是,按照官方说明,现在 Postman Interceptor 的这个Cookie 共享还不能支持独立安装的桌面版(从官方下载而不是从 Chrome 应用市场下载)的 Postman Desktop,所以,如果你希望使用上述功能,你只能安装回 Postman Chrome app,而这个版本相对桌面版,功能自然也会少。Note: Interceptor feature is supported only in our Postman Chrome Apps and is not available in Postman Desktop Apps at the moment.另一方面,考虑到 Chrome 浏览器将会在不久的将来停掉 Chrome apps 的支持,可能这个方案也撑不了太久。如果你真心希望 Postman 将上述功能加到他们的桌面版里,可以到他们的官方GitHub issues去请愿,他们正在收集大家的意见。但是……这个请愿帖已经两年多了,而就在我表达请求之前的几个小时到几天之前,都有人陆续去请愿,所以也不知道会不会真的如愿了。总结对于确实需要获取网站 cookie 才能完成接口测试的场景,上述方法有一定的便利性,也才有必要使用我的方法,其他场景的接口测试,你们就无视我吧。参考链接Postman: Using the Interceptor to read and write cookiesPostman Help Center: How do I access Chrome’s cookies in Postman’s Chrome App?Postman Learning Center: Interceptor extensionGoogle is phasing out Chrome apps for Mac and Windows ...

January 14, 2019 · 1 min · jiezi

你应该要知道的重绘与重排

前言现代web框架大多都是数据驱动类的,比如 react, vue,所以开发者不需要直接接触 DOM,修改 data 便可以驱动界面更新。但是作为前端工程师,了解浏览器的重绘与重排还是很有必要的,可以帮助我们写出更好性能的 web 应用。浏览器的渲染CSS Tree: 浏览器将 CSS 解析成 CSSOM 的树形结构DOM Tree:浏览器将 HTML 解析成树形的数据结构Render Tree:将 DOM 与 CSSOM 合并成一个渲染树有了渲染树(Render Tree),浏览器就知道网页中有哪些节点,以及各个节点与 CSS 的关系,从而知道每个节点的位置和几何属性,然后绘制页面。重绘与重排当 DOM 的变化影响了元素的几何属性(比如 width 和 height ),就会导致浏览器重新计算元素的几何属性,同样受到该元素影响的其他元素也会发生重新计算。此时,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程被称为重排(也叫“回流”)(reflow),完成重排之后,浏览器会重新绘制受影响的部分到页面上,这个过程就是重绘(repaint)。所以重排一定会引起重绘,而重绘不一定会引起重排,比如一个元素的改变并没有影响布局的改变(background-color的改变),在这种情况下,只会发生一个重绘(不需要重排)。引起重排的因素可以总结出,当元素的几何属性或页面布局发生改变就会引起重排,比如:对可见 DOM 元素的操作(添加,删除或顺序变化)元素位置发生改变元素的几何属性发生改变(比如:外边距、内边距、边框宽度以及内容改变引起的宽高的改变)页面首次渲染伪类样式激活(hover等)浏览器视口尺寸发生改变(滚动或缩放)如何优化重绘与重排都是代价昂贵的操作(因为每次重排都会产生计算消耗),它们会导致 web 应用的 UI 反应迟钝,所以开发者在编写应用程序的时候应当尽量减少这类过程的发生。渲染树队列因为过多的重绘与重排可能会导致应用的卡顿,所以浏览器会对这个有一个优化的过程。大多数浏览器会通过队列化来批量执行(比如把脚本对 DOM 的修改放入一个队列,在队列所有操作都结束后再进行一次绘制)。但是开发者有时可能不知不觉的强制刷新渲染队列来立即进行重排重绘,比如获取页面布局信息会导致渲染队列的强制刷新,以下属性或方法会立即触发页面绘制:offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop、scrollLeft、scrollWidth、scrollHeightclientTop、clientLeft、clientWidth、clientHeightgetComputedStyle()以上属性和方法都是要浏览器返回最新的布局信息,所以浏览器会立刻执行渲染队列中的“待处理变化”, 并触发重排重绘然后返回最新的值。所以在修改样式的过程中,应该尽量避免使用以上属性和方法。减少重绘与重排为了减少重绘重排的发生次数,开发者应该合并多次对 DOM 的修改和对样式的修改,然后一次性处理。合并样式操作比如:var el = document.querySelector(‘div’);el.style.borderLeft = ‘1px’;el.style.borderRight = ‘2px’;el.style.padding = ‘5px’;可以合并成:var el = document.querySelector(‘div’);el.style.cssText = ‘border-left: 1px; border-right: 1px; padding: 5px;‘批量修改DOM使元素脱离文档流,再对其进行操作,然后再把元素带回文档中,这种办法可以有效减少重绘重排的次数。有三种基本办法可以使元素脱离文档流:隐藏元素,应用修改,重新显示var ul = document.querySelector(‘ul’);ul.style.display = ’none’;// code… 对ul进行DOM操作ul.style.display = ‘block’;使用文档片段(document fragment),构建一个空白文档进行 DOM 操作,然后再放回原文档中var fragment = document.createDocumentFragment();// code… 对fragment进行DOM操作var ul = document.querySelector(‘ul’);ul.appendChild(fragment)拷贝要修改的元素到一个脱离文档流的节点中,修改副本,然后再替换原始元素var ul = document.querySelector(‘ul’);var cloneUl = ul.cloneNode(true);// code… 对clone节点进行DOM操作ul.parentNode.replaceChild(cloneUl, ul)缓存布局信息前面已经知道,获取页面布局信息,会导致浏览器强制刷新渲染队列。所以减少这些操作是非常有必要的,开发者可以将第一次获取到的页面信息缓存到局部变量中,然后再操作局部变量,比如下面的伪代码示例:// 低效的element.style.left = 1 + element.offsetLeft + ‘px’;element.style.top = 1 + element.offsetTop + ‘px’;if (element.offsetTop > 500) { stopAnimation();}// 高效的var offsetLeft = element.offsetLeft;var offsetTop = element.offsetTop;offsetLeft++;offsetTop++;element.style.left = offsetLeft + ‘px’;element.style.top = offsetTop + ‘px’;if (offsetTop > 500) { stopAnimation();}总结为了减少重绘重排带来的性能消耗,可以通过以下几点改善 web 应用:批量修改 DOM 和样式“离线”操作 DOM 树,脱离文档流缓存到局部变量,减少页面布局信息的访问次数参考高性能JavaScript ...

January 14, 2019 · 1 min · jiezi

chrome 浏览器地址栏快速搜索设置

经常使用chrome的朋友应该清楚地址栏里输入baidu.com按tab或者空格键,就会使用百度引擎搜索,这样就不用打开百度首页去输入进行搜索了,是不是很方便。还不止呢…有人觉得baidu.com太长了,想用b唤起百度搜索当然可以chrome浏览器搜索引擎设置是默认全网址唤起的,我们修改它的关键字,就可以达到我们先要的快捷搜索模式了下面我们开始进行设置把鼠标放在地址栏上面,右击鼠标/mac双击,打开操作菜单, 选择【修改搜索引擎】这是chrome浏览器的默认设置我们修改它的关键字,然后就可以直接输入b唤起百度搜索了下面我们添加,谷歌翻译的快捷搜索关键字网址是:http://translate.google.cn/?source=osdd#auto|auto|%s这样就可以在地址栏里输入 t 按tab或者空格键直接输入要翻译的内容,就会到达谷歌翻译页面并且已经帮你翻译好了是不是很快捷

January 14, 2019 · 1 min · jiezi

迟来的2018总结之一个有仪式感的2019启航

前言岁月不居,时节如流,转眼间都到2019年1月中旬了,时间过的好快,说好的周末睡到自然醒,但还是跟以往一样,到上班时间就醒了,这算不算心里只有工作呢?醒来无聊,看新闻是不存在的、而撩妹又是不可能的,于是来思否翻看了下2018年写的博客,最后的总结就是:发现今天的你,回头看过往/或昨天的自己,总会不自觉的在问:“那个傻X是我?”,就像写代码,写的时候只有你跟上帝知道,一个月之后只有上帝知道那是你写的,自己都不相信那烂的一坨屎的代码是出自你的手。~~~吹水结束,这里是IT平头哥联盟,我是首席填坑官—苏南,用心分享 做有温度的攻城狮。回顾人往往都是这样的,当你发现自己喜欢(很认可的)去做一件事时,总会发现觉得太晚了,如果我前几年就开始干这个事该多好。我也是人,同样不例外,接触前端也有些年了,一直是很认同/赞成写博客的,不为其他,只为自己积累,就这一理由已经足够你行动了,但中途零零散散尝试过几次都放弃,总认为自己没有别人写的好,或者觉得能力不够,写的没有深度,更甚者被外力干扰的(其实也是内心不够强大吧),比如文章末尾一堆杠精、键盘侠、喷子,让你觉得深受打击。2018年也算是坎坷与进步并行的一年吧。为什么说坎坷呢?2017年中下旬从某互联网金融公司(上市前夕)离职,跟前公司领导去了另一个金融集团下新组建的子公司,一切从零开始,把自己的十二分精力都投入了工作,然而2018年中旬,这个从诞生到结束,不到一年就夭折的……。其实在这之前,也过一篇文章《团队解散,我们该何去何从?》,这里不过多复述,因为说多了都是泪啊。为什么说进步呢?技术提升? 不,这些不能算作进步,这只能当作是程序员生成的基本法则/根本。最大的进步在于2018年09月21日,我又重新拿起笔,在掘金写出了我的第一篇分享,可能你会疑惑?这一次又是什么让我再次提笔:1.面试后的总结,大家面试相信都会被面试官提问:“你平时如何学习的?”、“你的自我提升都从哪里GET新技术?”、“你平时去哪些技术社区,都关注谁?”等,有些甚至补刀——“自己有没有写博客的习惯?”;最后补刀的都是因为面试官前面的提问没有得到他想要的答案,问你有没有写博客,可能更多的是想考察你是不是善于自我总结吧。2.自我沉淀:工作有周报/月总结/季度/年终等各种总结,那么自我学习呢?也一样,今天写下的点滴,就是对明天的自己最好的馈赠/礼物。小结: 原因很多,归根结底觉得博客总结尤为重要,文章不用写的多么华丽、高大尚,记录自己的点滴就好,比如2018年又开始坚持写写博客对自己的定位是:“记录自己的一些学习心得,工作中遇到的坑、分享认为不错的东西,期望助其他同学少走一些弯路,同时也让自己变得更优秀” ,这就是我2018最大的进步。学习1、自从开始写博客后,看文章的习惯就变了,以前可能是很目的性的过一篇,没有自己需要的东西,或者不是自己要找的问题,立马关掉走人;作为一名程序员,我认为写技术文章比写代码要难度可不是增加一点点,现在懂得了码字的不易,都会先认真拜读或先收藏起来,有空时认真细读,总能从中获取到它的价值。比如就前几天看到的这篇文章《不可思议的纯 CSS 滚动进度条效果》,看完之后,不得不服作者,并不是说使用的技术多有深度,恰恰相反,使用的技术是我们平时认为最简单上手的css而已,重点在于那一点点技巧/创新/突破,他的引导让我们又成长了一点点。2、当然也不仅仅是写博客就能提升自己的,每个人不一样,而且写的前提是你有足够的料,料从何来??根源还是学习,多看、多实践、多总结;多看 :这个范围比较广,有书、文章、代码等,吸取别人的长处来弥补自己的短板;多实践 :技术这个东西,你只看不练(纸上谈兵)是不够的,所以看到不错的东西,我都会试着去做一下;多总结 :这个很好理解,前面的看、实践或者项目做完之后,你从中遇到了哪些问题、哪些不错的技术等简单小结一下加强巩固,是很有必要的;展望20192018已经过去,美好的2019已经开怀拥抱我们,前面讲了这么多,那么我都做了些什么呢?准备做些什么呢?习惯的养成2018年末,我建了个github库(码农周刊),2019年每周都会整理一些很不错或技术交流群大家分享的文章、工具等,以周报的形式记录下来,便于自己学习;说到看书,可能我对于纸质书看的比较少,电子书倒是蛮多,所以也会整理在上面(我并没有全部看哦),博客是一定要坚持写的,它能让我对自己有一个更好的认知。社交都说程序员的活动范围是家的方圆5公里内,2019希望自己能打破常规,扩展到50、甚至100公里内?哈哈~????,…… 又有点跑偏了。人脉很重要,多结识一些同行、大佬,常去各种学习群出没,比如你也可以来我们IT平头哥联盟的交流群哦。撩妹这个很重要,但不可强求,顺其自然吧健康经常看到新闻XX程序员如何如何,其实还是因为职业原因缺乏运动吧,所以自己一直有个习惯,坚持跑步,每周一到两次,每次5~6公里的样子,不要求变成肌肉男、八块腹肌啥的,只因自己喜欢,2019还是要继续坚持,且沿着海边慢跑,也不失为一种浪漫哦~…………尾声说了这么多,我并没有给自己立下一个flag,认为并没有什么太大的意义,任何事情靠的还是自觉自律,靠立一个flag约束自己,不是我的风格。嗯,最后就是2019少熬夜,早睡早起吧,一切皆身外之物,唯有健康的身体才是自己的。PS:周末早上起来,发现Segmentfault之前推送的“2018总结”没有写,就随笔写了一些,勿喷~其他vue/react/java/大厂面试题等资源免费获取 深入了解JavaScript 中的For循环之详解月入三万 还能少了你一个鸡蛋如何给localStorage设置一个有效期?小程序项目如何设置资源的防盗链~阿里云产品限时优惠作者:苏南 - 首席填坑官链接:https://susouth.com/公Z号:IT平头哥联盟交流:912594095、公Z号ID:honeyBadger8本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。

January 14, 2019 · 1 min · jiezi

HTML5如何使用SVG

代码优化永远是程序员亘古不变的需求,而合理的利用SVG图片来代替部分PNG/JPG等格式的图片则是前端优化重要的一环,既然是优化,那我们先来看看SVG图片都有哪些优势:SVG 可被非常多的工具读取和修改(比如记事本)SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。SVG 是可伸缩的SVG 图像可在任何的分辨率下被高质量地打印SVG 可在图像质量不下降的情况下被放大SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)SVG 可以与 Java 技术一起运行SVG 是开放的标准SVG 文件是纯粹的 XML几个SVG图片小例子:我们来看一下第三个分享图标的代码:<svg xmlns=“http://www.w3.org/2000/svg" width=“20” height=“20” viewBox=“0 0 20 20”> <g stroke="#AAB0BA” fill=“none” fill-rule=“evenodd”> <path d=“M10.524 3.413v8.235” stroke-linejoin=“round”/> <path d=“M13.027 7.508c.813 0 1.678-.01 1.678-.01.449 0 .812.376.812.826l-.005 6.36a.819.819 0 0 1-.811.826H6.31a.822.822 0 0 1-.811-.826l.005-6.36c0-.456.36-.825.812-.825l1.689.006M8.373 5.111l2.143-2.09 2.143 2.07”/> </g></svg>不了解SVG的同学现在一定一脸问号,就跟我第一次见他们一样,别着急,我们从基础看起。什么是SVG?SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。此外SVG 是万维网联盟的标准,SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体。怎么使用?在 HTML5 中,您能够将 SVG 元素直接嵌入 HTML 页面中,例如上面的那颗小红心:<body> <svg xmlns=“http://www.w3.org/2000/svg" xmlns:xlink=“http://www.w3.org/1999/xlink" width=“20” height=“20” viewBox=“0 0 20 20”> <defs> <rect id=“a” y=“54” width=“60” height=“25” rx=“1”/> <mask id=“b” x=“0” y=“0” width=“60” height=“25” fill="#fff”> <use xlink:href="#a”/> </mask> </defs> <g transform=“translate(-9 -56)” fill=“none” fill-rule=“evenodd”> <use stroke="#EDEEEF" mask=“url(#b)” stroke-width=“2” xlink:href="#a"/> <path d=“M19.05 62.797c-.208-.268-1.776-2.188-3.629-1.725-.662.165-1.439.44-2.009 1.463-2.18 3.913 4.965 8.983 5.615 9.433V72l.023-.016.023.016v-.032c.65-.45 7.795-5.52 5.615-9.433-.57-1.023-1.347-1.298-2.009-1.463-1.853-.463-3.42 1.457-3.629 1.725z” fill=“red”/> </g> </svg></body>SVG 代码也可以写在一个以.svg结尾的文件中,然后用<img>、<object>、<embed>、<iframe>等标签插入网页。<img src=“search.svg”><object id=“object” data=“search.svg” type=“image/svg+xml”></object><embed id=“embed” src=“search.svg” type=“image/svg+xml”><iframe id=“iframe” src=“search.svg”></iframe>CSS也可以使用svg.logo { background: url(logo.svg);}SVG 文件还可以转为 BASE64 编码,然后作为 Data URI 写入网页。<img src=“data:image/svg+xml;base64,[data]">SVG的语法1. <svg>标签SVG 代码都放在顶层标签<svg>之中。下面是一个例子。<svg width=“100%” height=“100%"> <circle id=“mycircle” cx=“50” cy=“50” r=“50” /></svg><svg>的width属性和height属性,指定了 SVG 图像在 HTML 元素中所占据的宽度和高度。除了相对单位,也可以采用绝对单位(单位:像素)。如果不指定这两个属性,SVG 图像默认大小是300像素(宽) x 150像素(高)。如果只想展示 SVG 图像的一部分,就要指定viewBox属性。<svg width=“100” height=“100” viewBox=“50 50 50 50”> <circle id=“mycircle” cx=“50” cy=“50” r=“50” /></svg><viewBox>属性的值有四个数字,分别是左上角的横坐标和纵坐标、视口的宽度和高度。上面代码中,SVG 图像是100像素宽 x 100像素高,viewBox属性指定视口从(50, 50)这个点开始。所以,实际看到的是右下角的四分之一圆。注意,视口必须适配所在的空间。上面代码中,视口的大小是 50 x 50,由于 SVG 图像的大小是 100 x 100,所以视口会放大去适配 SVG 图像的大小,即放大了四倍。如果不指定width属性和height属性,只指定viewBox属性,则相当于只给定 SVG 图像的长宽比。这时,SVG 图像的默认大小将等于所在的 HTML 元素的大小。2. <circle>标签<circle>标签代表圆形。<svg width=“300” height=“180”> <circle cx=“30” cy=“50” r=“25” /> <circle cx=“90” cy=“50” r=“25” class=“red” /> <circle cx=“150” cy=“50” r=“25” class=“fancy” /></svg>上面的代码定义了三个圆。<circle>标签的cx、cy、r属性分别为横坐标、纵坐标和半径,单位为像素。坐标都是相对于<svg>画布的左上角原点。class属性用来指定对应的 CSS 类。.red { fill: red;}.fancy { fill: none; stroke: black; stroke-width: 3pt;}SVG 的 CSS 属性与网页元素有所不同。fill:填充色stroke:描边色stroke-width:边框宽度3. <line>标签<line>标签用来绘制直线。<svg width=“300” height=“180”> <line x1=“0” y1=“0” x2=“200” y2=“0” style=“stroke:rgb(0,0,0);stroke-width:5” /></svg>上面代码中,<line>标签的x1属性和y1属性,表示线段起点的横坐标和纵坐标;x2属性和y2属性,表示线段终点的横坐标和纵坐标;style属性表示线段的样式。4. <polyline>标签<polyline>标签用于绘制一根折线。<svg width=“300” height=“180”> <polyline points=“3,3 30,28 3,53” fill=“none” stroke=“black” /></svg><polyline>的points属性指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。5. <rect>标签<rect>标签用于绘制矩形。<svg width=“300” height=“180”> <rect x=“0” y=“0” height=“100” width=“200” style=“stroke: #70d5dd; fill: #dd524b” /></svg><rect>的x属性和y属性,指定了矩形左上角端点的横坐标和纵坐标;width属性和height属性指定了矩形的宽度和高度(单位像素)。6. <ellipse>标签<ellipse>标签用于绘制椭圆。<svg width=“300” height=“180”> <ellipse cx=“60” cy=“60” ry=“40” rx=“20” stroke=“black” stroke-width=“5” fill=“silver”/></svg><ellipse>的cx属性和cy属性,指定了椭圆中心的横坐标和纵坐标(单位像素);rx属性和ry属性,指定了椭圆横向轴和纵向轴的半径(单位像素)。7. <polygon>标签<polygon>标签用于绘制多边形。<svg width=“300” height=“180”> <polygon fill=“green” stroke=“orange” stroke-width=“1” points=“0,0 100,0 100,100 0,100 0,0”/></svg><polygon>的points属性指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。8. <path>标签<path>标签用于制路径。<svg width=“300” height=“180”><path d=” M 18,3 L 46,3 L 46,40 L 61,40 L 32,68 L 3,40 L 18,40 Z”></path></svg><path>的d属性表示绘制顺序,它的值是一个长字符串,每个字母表示一个绘制动作,后面跟着坐标。M:移动到(moveto)L:画直线到(lineto)Z:闭合路径9. <text>标签<text>标签用于绘制文本。<svg width=“300” height=“180”> <text x=“50” y=“25”>肆客足球</text></svg><text>的x属性和y属性,表示文本区块基线(baseline)起点的横坐标和纵坐标。文字的样式可以用class或style属性指定。10. <use>标签<use>标签用于复制一个形状。<svg viewBox=“0 0 30 10” xmlns=“http://www.w3.org/2000/svg"> <circle id=“myCircle” cx=“5” cy=“5” r=“4”/> <use href="#myCircle” x=“10” y=“0” fill=“blue” /> <use href="#myCircle" x=“20” y=“0” fill=“white” stroke=“blue” /></svg><use>的href属性指定所要复制的节点,x属性和y属性是<use>左上角的坐标。另外,还可以指定width和height坐标。11. <g>标签<g>标签用于将多个形状组成一个组(group),方便复用。<svg width=“300” height=“100”> <g id=“myCircle”> <text x=“25” y=“20”>圆形</text> <circle cx=“50” cy=“50” r=“20”/> </g> <use href="#myCircle" x=“100” y=“0” fill=“blue” /> <use href="#myCircle" x=“200” y=“0” fill=“white” stroke=“blue” /></svg>12. <defs>标签<defs>标签用于自定义形状,它内部的代码不会显示,仅供引用。<svg width=“300” height=“100”> <defs> <g id=“myCircle”> <text x=“25” y=“20”>圆形</text> <circle cx=“50” cy=“50” r=“20”/> </g> </defs> <use href="#myCircle" x=“0” y=“0” /> <use href="#myCircle" x=“100” y=“0” fill=“blue” /> <use href="#myCircle" x=“200” y=“0” fill=“white” stroke=“blue” /></svg>13. <pattern>标签<pattern>标签用于自定义一个形状,该形状可以被引用来平铺一个区域。<svg width=“500” height=“500”> <defs> <pattern id=“dots” x=“0” y=“0” width=“100” height=“100” patternUnits=“userSpaceOnUse”> <circle fill="#bee9e8" cx=“50” cy=“50” r=“35” /> </pattern> </defs> <rect x=“0” y=“0” width=“100%” height=“100%” fill=“url(#dots)” /></svg>上面代码中,<pattern>标签将一个圆形定义为dots模式。patternUnits=“userSpaceOnUse"表示<pattern>的宽度和长度是实际的像素值。然后,指定这个模式去填充下面的矩形。14. <image>标签<image>标签用于插入图片文件。<svg viewBox=“0 0 100 100” width=“100” height=“100”> <image xlink:href=“path/to/image.jpg” width=“50%” height=“50%”/></svg>上面代码中,<image>的xlink:href属性表示图像的来源。15. <animate>标签<animate>标签用于产生动画效果。<svg width=“500px” height=“500px”> <rect x=“0” y=“0” width=“100” height=“100” fill="#feac5e”> <animate attributeName=“x” from=“0” to=“500” dur=“2s” repeatCount=“indefinite” /> </rect></svg>上面代码中,矩形会不断移动,产生动画效果。<animate>的属性含义如下。attributeName:发生动画效果的属性名。from:单次动画的初始值。to:单次动画的结束值。dur:单次动画的持续时间。repeatCount:动画的循环模式。可以在多个属性上面定义动画。<animate attributeName=“x” from=“0” to=“500” dur=“2s” repeatCount=“indefinite” /><animate attributeName=“width” to=“500” dur=“2s” repeatCount=“indefinite” />16. <animateTransform>标签<animate>标签对 CSS 的transform属性不起作用,如果需要变形,就要使用<animateTransform>标签。<svg width=“500px” height=“500px”> <rect x=“250” y=“250” width=“50” height=“50” fill="#4bc0c8"> <animateTransform attributeName=“transform” type=“rotate” begin=“0s” dur=“10s” from=“0 200 200” to=“360 400 400” repeatCount=“indefinite” /> </rect></svg>上面代码中,<animateTransform>的效果为旋转(rotate),这时from和to属性值有三个数字,第一个数字是角度值,第二个值和第三个值是旋转中心的坐标。from=“0 200 200"表示开始时,角度为0,围绕(200, 200)开始旋转;to=“360 400 400"表示结束时,角度为360,围绕(400, 400)旋转。JavaScript 操作SVG1. DOM操作如果 SVG 代码直接写在 HTML 网页之中,它就成为网页 DOM 的一部分,可以直接用 DOM 操作。<svg id=“mysvg” xmlns=“http://www.w3.org/2000/svg" viewBox=“0 0 800 600” preserveAspectRatio=“xMidYMid meet”> <circle id=“mycircle” cx=“400” cy=“300” r=“50” /><svg>上面代码插入网页之后,就可以用 CSS 定制样式。circle { stroke-width: 5; stroke: #f00; fill: #ff0;}circle:hover { stroke: #090; fill: #f8f8f8;}然后,可以用 JavaScript 代码操作 SVG。var mycircle = document.getElementById(‘mycircle’);mycircle.addEventListener(‘click’, function(e) { console.log(‘circle clicked - enlarging’); mycircle.setAttribute(‘r’, 60);}, false);上面代码指定,如果点击图形,就改写circle元素的r属性。2. 获取 SVG DOM使用<object>、<iframe>、<embed>标签插入 SVG 文件,可以获取 SVG DOM。var svgObject = document.getElementById(‘object’).contentDocument;var svgIframe = document.getElementById(‘iframe’).contentDocument;var svgEmbed = document.getElementById(’embed’).getSVGDocument();注意,如果使用<img>标签插入 SVG 文件,就无法获取 SVG DOM。3. 读取 SVG 源码由于 SVG 文件就是一段 XML 文本,因此可以通过读取 XML 代码的方式,读取 SVG 源码。<div id=“svg-container”> <svg xmlns=“http://www.w3.org/2000/svg" xmlns:xlink=“http://www.w3.org/1999/xlink" xml:space=“preserve” width=“500” height=“440” > <!– svg code –> </svg></div>使用XMLSerializer实例的serializeToString()方法,获取 SVG 元素的代码。var svgString = new XMLSerializer() .serializeToString(document.querySelector(‘svg’));4. SVG 图像转为 Canvas 图像首先,需要新建一个Image对象,将 SVG 图像指定到该Image对象的src属性。var img = new Image();var svg = new Blob([svgString], {type: “image/svg+xml;charset=utf-8”});var DOMURL = self.URL || self.webkitURL || self;var url = DOMURL.createObjectURL(svg);img.src = url;然后,当图像加载完成后,再将它绘制到<canvas>元素。img.onload = function () { var canvas = document.getElementById(‘canvas’); var ctx = canvas.getContext(‘2d’); ctx.drawImage(img, 0, 0);};小结SVG能做的远不止这些,利用SVG做的动画效果,文字效果我们以后给大家详细讲解,今天就先到这里吧。console.log(‘右下角点好看呦’)技术放肆聊QQ群:617413307 欢迎程序员朋友积极加群,共同进步技术放肆聊公众号,每日干货,最前沿的技术知识,扫描下方二维码关注:推一下自家APP肆客足球 最新最全的足球资讯,最火爆的球迷社区,直播中超、欧洲赛事,全面丰富的数据统计,还有球星推特、Ins、社交动态,原汁原味,扫描下方二维码即可下载 ...

January 11, 2019 · 3 min · jiezi

HTTPS如何确保Web安全

前言:全网HTTPS势在必行HTTPS(全称:HyperText Transfer Protocol over Secure Socket Layer),是为了保证客户端与服务器之间数据传输的安全。 近两年,Google、Baidu、Facebook 等这样的互联网巨头,不谋而合地开始大力推行 HTTPS, 国内外的大型互联网公司很多也都已经启用了全站 HTTPS,这也是未来互联网发展的趋势,作为前端工程师,了解HTTPS的原理也是必修课之一。2019年离全网使用HTTPS已经不远了,列举几个各大互联网公司为鼓励使用HTTPS而提出的要求:1.Google的搜索引擎算法,让采用 HTTPS 的网站在搜索中排名更靠前;2.苹果要求App Store中的所有应用都必须使用 HTTPS 加密连接;3.微信小程序也要求必须使用 HTTPS 协议;4.新一代的 HTTP/2 协议的支持需以 HTTPS 为基础;5.新版本chrome已将HTTP协议网站标记不安全隐患:为什么要给HTTP加S?HTTP协议从诞生至今已经具有相当优秀和方便的一面,然而HTTP并非只有好的一面,事物皆具两面性,它的不足之处也是很明显:通信使用明文传输,内容可能会被窃听不验证通信方的身份,因此有可能遭遇伪装无法证明报文的完整性,所以有可能已经遭到篡改除此之外,HTTP本身还有很多缺点。而且,还有像某些特定的Web服务器和特定的Web浏览器在实际应用中存在的不足(也可以说成是脆弱性或安全漏洞),另外,用Java和PHP等编程语言开发的Web应用也可能存在安全漏洞。1. 通信使用明文可能会被窃听由于HTTP本身不具备加密的功能,所以也无法做到对通信整体(使用HTTP协议通信的请求和响应的内容)进行加密。所以,HTTP报文使用明文方式发送。如果要问为什么通信时不加密是一个缺点,这是因为,按TCP/IP协议族的工作机制,通信内容在所有的通信线路上都有可能遭到窥视。所谓互联网,是由能连通到全世界的网络组成,无论世界哪个角落的服务器在和客户端通信时,在此通信线路上的某些网络设备、光缆、计算机等都不可能是个人的私有物,所以不排除某个环节中会遭到恶意窥视行为。即使已经过加密处理的通信,也会被窥视到通信内容,这点和未加密的通信是相同的。只是说如果通信经过加密,就有可能让人无法破解报文信息的含义,但加密处理后的报文信息本身还是会被看到。窃听相同段上的通信并非难事。只需要收集在互联网上流动的数据包就行。对于收集来的数据包的解析工作,可以交给那些抓包或嗅探工具。2. 不验证通信方的身份就可能遭到伪装HTTP协议中的请求和相应不会对通信方进行确认。也就是说存在“服务器是否就是发送请求中URI真正指定的主机,返回的响应是否真的返回到实际提出请求的客户端”等类似问题。在HTTP协议通信时,由于不存在确认通信方的处理步骤,任何人都可以发送请求,同时,服务器只要接收到请求,只要发送端的IP地址和端口号没有被Web服务器设定限制访问,不管对方是谁都会返回一个响应,因此会存在以下各种隐患:无法确定请求发送至目标的Web服务器是否是按真实意图返回响应的那台服务器,有可能是已伪装的Web服务器。无法确定响应返回到的客户端是否是按真实意图接收响应的那个客户端,有可能是已伪装的客户端。无法确定正在通信的对方是否具备访问权限。因为某些Web服务器上保存着重要的信息,指向发给特定用户通信的权限。无法判定请求是来自何方、出自谁手。及时是无意义的请求也会照单全收。无法阻止海量请求下的DoS攻击(Denial of Service,拒绝服务攻击)。3. 无法证明报文的完整性,可能已遭到篡改所谓完整性是指信息的准确度。若无法证明其完整性,通常也就意味着无法判断信息是否准确。因此,在请求或响应送出之后知道对方接收之前的这段时间内,即使请求或相应的内容遭到篡改,也没有办法获悉。换句话说,没有任何办法确认,发出的请求、响应和接收到的请求、响应是前后相同的。文件内容在传输中可能已经被村改为其他内容,像这样,请求或响应在传输途中遭攻击者拦截并篡改内容的攻击成为中间人攻击(Man-in-the-Middle attack,MITM)。解决:HTTP + 加密 + 认证 + 完整性保护 = HTTPS上面提了那么多HTTP的缺点自然在HTTPS中我们得解决它,下面我们来看看HTTPS是如何保证我们数据传输安全的。1. HTTPS其实是身披SSL外壳的HTTPHTTPS并非是应用层的一种新协议。知识HTTP通信接口部分用SSL(Secure Socket Layer,安全套阶层)和TLS(Transport Layer Security,安全传输层协议)协议代替而已。通常,HTTP直接和TCP通信。当使用SSL时,则变成先和SSL通信,再由SSL和TCP通信了。简单来说,与SSL组合使用的HTTP被称为HTTPS(HTTP Secure,超文本传输安全协议)或HTTP over SSL。采用了SSL后,HTTP就拥有了HTTPS的加密、证书和完整性保护这些功能。SSL是独立于HTTP的协议,所以不光是HTTP协议,其它运行在应用层的SMTP和Telnet等协议均可配合SSL协议使用。可以说SSL是当今世界上应用最为广泛的网络安全技术。HTTPS的加密原理近代的加密算法中加密算法是公开的,而密钥是保密的。通过这种方式来保持加密方法的安全性。加密和解密要用到密钥,如果没有密钥就没有办法对密码解密。换句话来说,任何人只要持有密钥就能够对密文进行解密。HTTPS在加密过程中使用了非对称加密技术和对称加密技术。对称加密算法采用单钥密码系统的加密方式,同一个密钥可以同时做信息的加密和解密,这种加密的方法称为对称加密,也称为单密钥加密。下面会把对称加密算法称为共享密钥加密算法。假如现在,SSL在通信过程中,使用了对称加密算法,也就是说客户端和服务器同时共享一个密钥。于是,以共享密钥的方式加密,必须将密钥发给对方。这个时候,假如通信过程被监听,密钥被攻击者获取了,那么这个时候也就失去了加密的意义了。那么,有没有办法解决这个问题呢?答案是肯定的,也就是使用两把密钥。下面先看使用两把密钥的非对称加密算法。非对称加密算法与对称加密算法相反,非对称加密算法需要两个密钥来进行加密和解密,这两个密钥是配对的,分别是公开密钥(公钥)和私有密钥(私钥)。一般情况下,公钥是可以被公开的,它主要用来加密明文。而相应的,私钥不能被公开,用来解密公钥加密的密文。值得注意的是:公钥加密后的密文只能通过对应的私钥来解密,而私钥加密的密文却可以通过对应的公钥来解密。以上,公钥加密私钥解密用来加密,私钥加密公钥解密用来签名。相关用途后面会讲到。下面会把非对称加密算法称为公开密钥加密算法。于是现在,假设现在由服务器来生成一对公钥和密钥。当客户端第一次发请求和服务器协商的时候,服务器就生成了一对公钥和私钥。紧接着,服务器把公钥发给客户端(明文,不需要做任何加密),客户端接收后,随机生成一个密钥,使用服务器发过来的公钥进行加密。再接着,客户端把使用公钥加密的密钥发给服务器,服务器接收到了以后,用配对的私钥进行解密,就得到了客户端随机生成的那个密钥。这个时候,客户端和服务端所持的密钥都是相同的。此时,交换密钥环节就完成了。于是通信开始时就可进行上面所述的共享密钥加密方式来进行加密。同时使用可能,有小伙伴就会问,为什么要大费周章使用非对称加密的方式,然后再得到相同的密钥,进行共享密钥加密的通信呢?由于公开密钥加密处理起来比共享密钥加密方式更为复杂,因此在通信的时候使用公开密钥加密的方式,效率很低。于是,我们需要使用非对称加密的方式来保证密钥共享的过程中密钥的安全性,而后在通信的过程中使用对称加密算法,这是最合理的设计方式,在保证安全性的同时又保证了性能。所以,HTTPS采用共享密钥加密和公开密钥加密两者并用的混合加密机制。在交换密钥使用环节使用公开密钥加密方式,之后建立的通信交换报文阶段则使用共享密钥加密方式。以上,大概就是使用对称加密和非对称加密的过程。看似过程很完美,其实还存在着一个问题,就是:如何保证服务器传过来的公开密钥的正确性。换句话说,就是保证它不被拦截篡改。使用证书保证公钥的正确性假如现在正准备和某台服务器建立公开密钥加密方式下的通信,如何证明客户端收到的公开密钥就是原本预想的那台服务器发行的公开密钥呢?或许,在公开密钥传输的过程中,真正的公开密钥可能已经被攻击者替换掉了。为了解决这个问题,可以使用由数字证书机构和其相关颁发的公开密钥证书。下面阐述一下数字证书认证机构(简称CA)的业务流程:首先,服务器的运营人员向数字证书机构提出公开密钥的申请。数字证书认证机构在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公钥证书后绑定在一起。我们用白话文来翻译一下上面这段话:首先,CA会向申请者颁发一个证书,这个证书里面的内容有:签发者、证书用途、服务器申请的时候附带的公钥、服务器的加密算法、使用的HASH算法、证书到期的时间等等。紧接着,把上面所提到的内容,做一次HASH求值,得到一个HASH值。再接着,用CA的私钥进行加密,这样就完成了数字签名。而用CA的私钥加密后,就生成了类似人体指纹的签名,任何篡改证书的尝试,都会被数字签名发现。最后,把数字签名,附在数字证书的末尾,传输回来给服务器。接下来,服务器会把这份由数字证书认证机构颁发的公钥证书发给客户端。这个时候,客户端可以使用数字证书机构的公开密钥对其进行验证。一旦验证成功,客户端便能够确定这个公开密钥是可信的。我们再用白话文来翻译一下:客户端拿到这个数字证书以后,用CA私钥对应的公钥,可以解密数字证书末尾的数字签名,得到原始的HASH值。紧接着,客户端按照证书中的HASH算法,对证书的内容求HASH值。如果通过CA公钥解密的HASH和通过计算求得的HASH值相同,那么认证通过,否则失败。如果认证通过,就可以取得服务器的公开密钥。那客户端上面的CA公钥是从哪里来的呢?多数浏览器开发商发布版本时,会事先在内部植入常用认证机关的公开密钥。这样,就方便客户端对于数字证书真实性的验证。其具体过程是这样子的(图中简化了数字签名的过程):这里其实就用到了非对称加密算法,只不过现在这个加密算法用来签名而不是加密。使用私钥加密,公钥解密,用于公钥的持有者验证通过私钥加密的内容是否被篡改,但是不用来保证内容是否被他人获得。而使用公钥加密,私钥解密,则是相反的,它不保证信息被他人截获篡改,但是保证信息无法被中间人获得。客户端证书HTTPS中不仅可以使用服务器证书,还可以使用客户端证书。以客户端证书进行客户端认证,它的作用与服务器证书是相同的。由于客户端获取证书需要用户自行安装客户端证书,同时也面临着费用的问题。因此,现状是,安全性极高的认证机构可办法客户端证书但是仅用于特殊用途的业务。比如那些可支撑客户端证书支出费用的业务。例如,银行的网上银行就采用了客户端证书。在登录网银时不仅要求用户确认输入ID和密码,还会要求用户的客户端证书,以确认用户是否从特定的终端访问网银。HTTPS的安全通信机制为了更好的理解HTTPS,小肆给大家画了下图来一起观察一下HTTPS的通信步骤:步骤1:客户端通过发送Client Hello报文开始SSL通信。报文中包含客户端支持的SSL的指定版本、加密组件列表(所使用的加密算法及密钥长度等)。步骤2:服务器可进行SSL通信时,会以Server Hello报文作为应答。和客户端一样,在报文中包含SSL版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。步骤3:之后服务器发送Certificate报文。报文中包含公开密钥证书。步骤4:最后服务器发送Server Hello Done报文通知客户端,最初阶段的SSL握手协商部分结束。步骤5:SSL第一次握手结束之后,客户端以Client Key Exchange报文最为回应。报文中包含通信加密中使用的一种被称为Pre-master secret的随机密码串。该报文已用步骤3中的公开密钥进行加密。步骤6:接着客户端继续发送Change Cipher Spec报文。该报文会提示服务器,在此报文之后的通信会采用Pre-master secret密钥加密。步骤7:客户端发送Finished报文。该报文包含连接至今全部报文的整体效验值。这次握手协商是否能够成功,要以服务器是否能够正确解密该报文作为判定标准。步骤8:服务器同样发送Change Cipher Spec报文。步骤9:服务器同样发送Finished报文。步骤10:服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成,当然,通信会受到SSL的保护。从此处开始进行应用层协议的通信,即发送HTTP请求。步骤11:应用层协议通信,即发送HTTP响应。步骤12:最后由客户端断开连接。断开连接时,发送close_notify报文。上图做了一些省略,这步之后再发送TCP FIN报文来关闭与TCP的通信。在以上流程中,应用层发送数据时会附加一种叫做MAC(Message Authentication Code)的报文摘要。MAC能够查知报文是否遭到篡改,从而保护报文的完整性。那现在有一个问题,整个过程中产生的三个随机数有什么用呢?还有,后面进行HTTP通信的时候,是用哪一个密钥进行加密,还有怎么保证报文的完整性。看下面这张图。对于客户端:当其生成了Pre-master secret之后,会结合原来的A、B随机数,用DH算法计算出一个master secret,紧接着根据这个master secret推导出hash secret和session secret。对于服务端:当其解密获得了Pre-master secret之后,会结合原来的A、B随机数,用DH算法计算出一个master secret,紧接着根据这个master secret推导出hash secret和session secret。在客户端和服务端的master secret是依据三个随机数推导出来的,它是不会在网络上传输的,只有双方知道,不会有第三者知道。同时,客户端推导出来的session secret和hash secret与服务端也是完全一样的。那么现在双方如果开始使用对称算法加密来进行通讯,使用哪个作为共享的密钥呢?过程是这样子的:双方使用对称加密算法进行加密,用hash secret对HTTP报文做一次运算生成一个MAC,附在HTTP报文的后面,然后用session-secret加密所有数据(HTTP+MAC),然后发送。接收方则先用session-secret解密数据,然后得到HTTP+MAC,再用相同的算法计算出自己的MAC,如果两个MAC相等,证明数据没有被篡改。至此,整个过程介绍完毕。技术放肆聊公众号,每日干货,最前沿的技术知识,扫描下方二维码关注: ...

January 11, 2019 · 1 min · jiezi

玩转控制台,看看那些你不知道的Console用法

前言作为前端工程师,我们每天都离不开用控制台调试代码,console.log也成了我们最常用的命令,那除了用console.log,还有许多console的方法可以使用,熟练掌握它们,可以让我们在控制台看到的信息更美观准确,也会大大提高我们的开发效率哦,下面就跟小肆一起来看看吧.Chrome的控制台大部分常用浏览器都有各自的控制台,不过小肆用着最习惯的还是Chrome的控制台,打开chrome,win系统按F12,mac系统按command+option+J就可以呼出控制台了,切换到Console标签就能看到如下信息:我们可以看到,baidu还在控制台给我们留了个小彩蛋,我想这个彩蛋也是为我们程序员同学留的吧。让我们先学第一个命令清除控制台来开始吧。清除控制台在chorme下清除控制台的方法有很多:输入console.clear()命令或clear()命令使用快捷键 Control + J 或 Command + K点击控制台左上角第二个图标 ????显示信息的命令console.log(‘技术放肆聊’) // 输出普通信息console.info(‘技术放肆聊’) // 输出提示信息console.warn(‘技术放肆聊’) // 输出警告信息console.error(‘技术放肆聊’) // 输出错误信息console.debug(‘技术放肆聊’) // 输出调试信息console.log、console.info、console.debug这三个命令可以理解为一个,我们只需要用console.log就行,并且chrome还不支持console.debug命令。console.warn命令输出警告信息,信息前带有黄色警告符号。console.error输出错误信息,信息前带有红色错误符号,表示出错,同时会显示错误发生的堆栈。上段代码在chrome控制台输出效果如下:在safari输出效果如下:占位符console上述的命令支持printf的占位符格式,支持的占位符有:字符(%s)、整数(%d或%i)、浮点数(%f)和对象(%o):占位符作用%s字符串%d or %i整数%f浮点数%o可展开的DOM%O列出DOM的属性%c根据提供的css样式格式化字符串//字符(%s)console.log("%s",“技术放肆聊”);//整数(%d或%i)console.log("%d年%d月%d日",2019,1,6); //浮点数(%f)console.log(“PI=%f”,3.1415926);显示效果如下:%o、%O 都是用来输出 Object 对象的,对普通的 Object 对象,两者没区别,但是打印dom节点时就不一样了:%c 占位符是最常用的。使用 %c 占位符时,对应的后面的参数必须是 CSS 语句,用来对输出内容进行 CSS 渲染。常见的输出方式有两种:文字样式、图片输出。信息分组console.group()用于将显示的信息分组,可以把信息进行折叠和展开。console.groupEnd()结束内联分组将对象以树状结构展现console.dir()可以显示一个对象所有的属性和方法.显示某个节点的内容console.dirxml()用来显示网页的某个节点(node)所包含的html/xml代码判断变量是否是真console.assert()用来判断一个表达式或变量是否为真,此方法接受两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果。计时功能console.time()和console.timeEnd(),用来显示代码的运行时间console.time(“控制台计时器”);for(var i = 0; i < 10000; i++){ for(var j = 0; j < 10000; j++){} }console.timeEnd(“控制台计时器”);性能分析performance profileconsole.profile()和console.proileEnd()用来分析程序各个部分的运行时间,找出瓶颈所在。function All(){ for(var i = 0; i < 10; i++){ funcA(100); } funcB(1000);}function funcA(count){ for(var i = 0; i < count; i++){};}function funcB(count){ for(var i = 0; i < count; i++){};}console.profile(“性能分析器”);All();console.profileEnd();详细的信息在chrome控制台里的"profile"选项里查看console.count()统计代码被执行的次数function myFunction(){ console.count(“myFunction 被执行的次数”);}myFunction(); //myFunction 被执行的次数: 1myFunction(); //myFunction 被执行的次数: 2myFunction(); //myFunction 被执行的次数: 3console.table表格显示方法总结合理的利用console的各种方法,会使我们的调试过程更加愉悦,今天的分享就到这里了,记得右下角点好看呦!技术放肆聊公众号,每日干货,最前沿的技术知识,扫描下方二维码关注: ...

January 11, 2019 · 1 min · jiezi

油猴脚本第一家,网页网盘链接实时判断+资源搜索网站导航,资源重度患者的福利

现在网络上找资源,资源都是存在百度网盘的,大家都知道,百度网盘链接失效的非常之多。遇到网盘链接我们都要一个一个点进去查看链接是否失效,这样操作费时又累人。这时这个油猴脚本就可以帮忙了。实时判断网页中百度网盘链接状态。同时,这个油猴脚本还会再网页的适当位置推荐资源网站MAP,找资源什么的方便多了~亲测插件非常给力,安装非常方便,只需点一下即可,插件安装地址:https://greasyfork.org/zh-CN/…插件介绍:功能介绍:1、网盘链接状态判断:实时判断网页中百度网盘链接状态,节约时间,方便又快捷;2、资源搜索网站导航:脚本会在百度、文库、360、搜狗、豆瓣等网站的合适位置推荐各类资源搜索网站,方便对资源的检索。如:豆瓣电影,就会实时的推荐电影相关资源网站,推荐网址长期维护更新。“资源搜索网站导航”做您资源查找的好帮手! 运行截图:脚本运行截图:

January 8, 2019 · 1 min · jiezi

Html页面中内容禁止选择、复制、右键的实现方法

有的时候,我们不希望自己网页中所呈现的内容不被别有用心盗取,就需要在网页中加上一个禁止复制的功能,而一般的浏览器在禁止复制后还可以用复制为纯文本,并不能完全杜绝此问题,此时就需要我们在页面中完全禁止右键和复制。实现起来其实很简单,只需要在网页中加入以下标签(注意是紧随body后):<body topmargin=“0” oncontextmenu=“return false” ondragstart=“return false” onselectstart =“return false” onselect=“document.selection.empty()” oncopy=“document.selection.empty()” onbeforecopy=“return false” onmouseup=“document.selection.empty()"> 这只是一个最初步的方法,也很容易被人破解,怕网页被别人另存为本地文件,可以再加上以下代码防止别人保存:<noscript> <iframe src=”*.htm"></iframe> </noscript> 最后,有的站长可能只需要一个禁止复制的功能,并不需要禁止右键,则在<body>中加入以下代码即可:<body onmousemove=/HideMenu()/ oncontextmenu=“return false” ondragstart=“return false” onselectstart =“return false” onselect=“document.selection.empty()” oncopy=“document.selection.empty()” onbeforecopy=“return false” onmouseup=“document.selection.empty()">

January 8, 2019 · 1 min · jiezi

【译】WebSocket协议第五章——数据帧(Data Framing)

概述本文为WebSocket协议的第五章,本文翻译的主要内容为WebSocket传输的数据相关内容。有兴趣了解该文档之前几张内容的同学可以见:【译】WebSocket协议第一章——介绍(Introduction)【译】WebSocket协议第二章——一致性要求(Conformance Requirements)【译】WebSocket协议第三章——WebSocket网址(WebSocket URIs)【译】WebSocket协议第四章——连接握手(Opening Handshake)数据帧(协议正文)5.1 概览在WebSocket协议中,数据是通过一系列数据帧来进行传输的。为了避免由于网络中介(例如一些拦截代理)或者一些在第10.3节讨论的安全原因,客户端必须在它发送到服务器的所有帧中添加掩码(Mask)(具体细节见5.3节)。(注意:无论WebSocket协议是否使用了TLS,帧都需要添加掩码)。服务端收到没有添加掩码的数据帧以后,必须立即关闭连接。在这种情况下,服务端可以发送一个在7.4.1节定义的状态码为1002(协议错误)的关闭帧。服务端禁止在发送数据帧给客户端时添加掩码。客户端如果收到了一个添加了掩码的帧,必须立即关闭连接。在这种情况下,它可以使用第7.4.1节定义的1002(协议错误)状态码。(这些规则可能会在将来的规范中放开)。基础的数据帧协议使用操作码、有效负载长度和在“有效负载数据”中定义的放置“扩展数据”与“引用数据”的指定位置来定义帧类型。特定的bit位和操作码为将来的协议扩展做了保留。一个数据帧可以在开始握手完成之后和终端发送了一个关闭帧之前的任意一个时间通过客户端或者服务端进行传输(第5.5.1节)。5.2 基础帧协议在这节中的这种数据传输部分的有线格式是通过ABNFRFC5234来进行详细说明的。(注意:不像这篇文档中的其他章节内容,在这节中的ABNF是对bit组进行操作。每一个bit组的长度是在评论中展示的。在线上编码时,最高位的bit是在ABNF最左边的)。对于数据帧的高级的预览可以见下图。如果下图指定的内容和这一节中后面的ABNF指定的内容有冲突的话,以下图为准。 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+——-+-+————-+——————————-+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+——-+-+————-+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +——————————-+ | |Masking-key, if MASK set to 1 | +——————————-+——————————-+ | Masking-key (continued) | Payload Data | +——————————– - - - - - - - - - - - - - - - + : Payload Data continued … : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued … | +—————————————————————+FIN: 1 bit 表示这是消息的最后一个片段。第一个片段也有可能是最后一个片段。RSV1,RSV2,RSV3: 每个1 bit 必须设置为0,除非扩展了非0值含义的扩展。如果收到了一个非0值但是没有扩展任何非0值的含义,接收终端必须断开WebSocket连接。Opcode: 4 bit 定义“有效负载数据”的解释。如果收到一个未知的操作码,接收终端必须断开WebSocket连接。下面的值是被定义过的。 %x0 表示一个持续帧 %x1 表示一个文本帧 %x2 表示一个二进制帧 %x3-7 预留给以后的非控制帧 %x8 表示一个连接关闭包 %x9 表示一个ping包 %xA 表示一个pong包 %xB-F 预留给以后的控制帧Mask: 1 bit mask标志位,定义“有效负载数据”是否添加掩码。如果设置为1,那么掩码的键值存在于Masking-Key中,根据5.3节描述,这个一般用于解码“有效负载数据”。所有的从客户端发送到服务端的帧都需要设置这个bit位为1。Payload length: 7 bits, 7+16 bits, or 7+64 bits 以字节为单位的“有效负载数据”长度,如果值为0-125,那么就表示负载数据的长度。如果是126,那么接下来的2个bytes解释为16bit的无符号整形作为负载数据的长度。如果是127,那么接下来的8个bytes解释为一个64bit的无符号整形(最高位的bit必须为0)作为负载数据的长度。多字节长度量以网络字节顺序表示(译注:应该是指大端序和小端序)。在所有的示例中,长度值必须使用最小字节数来进行编码,例如:长度为124字节的字符串不可用使用序列126,0,124进行编码。有效负载长度是指“扩展数据”+“应用数据”的长度。“扩展数据”的长度可能为0,那么有效负载长度就是“应用数据”的长度。Masking-Key: 0 or 4 bytes 所有从客户端发往服务端的数据帧都已经与一个包含在这一帧中的32 bit的掩码进行过了运算。如果mask标志位(1 bit)为1,那么这个字段存在,如果标志位为0,那么这个字段不存在。在5.3节中会介绍更多关于客户端到服务端增加掩码的信息。Payload data: (x+y) bytes “有效负载数据”是指“扩展数据”和“应用数据”。Extension data: x bytes 除非协商过扩展,否则“扩展数据”长度为0 bytes。在握手协议中,任何扩展都必须指定“扩展数据”的长度,这个长度如何进行计算,以及这个扩展如何使用。如果存在扩展,那么这个“扩展数据”包含在总的有效负载长度中。Application data: y bytes 任意的“应用数据”,占用“扩展数据”后面的剩余所有字段。“应用数据”的长度等于有效负载长度减去“扩展应用”长度。基础数据帧协议通过ABNF进行了正式的定义。需要重点知道的是,这些数据都是二进制的,而不是ASCII字符。例如,长度为1 bit的字段的值为%x0 / %x1代表的是一个值为0/1的单独的bit,而不是一整个字节(8 bit)来代表ASCII编码的字符“0”和“1”。一个长度为4 bit的范围是%x0-F的字段值代表的是4个bit,而不是字节(8 bit)对应的ASCII码的值。不要指定字符编码:“规则解析为一组最终的值,有时候是字符。在ABNF中,字符仅仅是一个非负的数字。在特定的上下文中,会根据特定的值的映射(编码)编码集(例如ASCII)”。在这里,指定的编码类型是将每个字段编码为特定的bits数组的二进制编码的最终数据。ws-frame =frame-fin; 长度为1 bitframe-rsv1; 长度为1 bitframe-rsv2; 长度为1 bitframe-rsv3; 长度为1 bitframe-opcode; 长度为4 bitframe-masked; 长度为1 bitframe-payload-length; 长度为7或者7+16或者7+64 bit[frame-masking-key]; 长度为32 bitframe-payload-data; 长度为大于0的n8 bit(其中n>0)frame-fin =%x0,除了以下为1的情况%x1,最后一个消息帧长度为1 bitframe-rsv1 =%x0 / %x1,长度为1 bit,如果没有协商则必须为0frame-rsv2 =%x0 / %x1,长度为1 bit,如果没有协商则必须为0frame-rsv3 =%x0 / %x1,长度为1 bit,如果没有协商则必须为0frame-opcode =frame-opcode-non-controlframe-opcode-controlframe-opcode-contframe-opcode-non-control%x1,文本帧%x2,二进制帧%x3-7,保留给将来的非控制帧长度为4 bitframe-opcode-control%x8,连接关闭%x9,ping帧%xA,pong帧%xB-F,保留给将来的控制帧长度为4 bitframe-masked%x0,不添加掩码,没有frame-masking-key%x1,添加掩码,存在frame-masking-key长度为1 bitframe-payload-length%x00-7D,长度为7 bit%x7E frame-payload-length-16,长度为7+16 bit%x7F frame-payload-length-63,长度为7+64 bitframe-payload-length-16%x0000-FFFF,长度为16 bitframe-payload-length-63%x0000000000000000-7FFFFFFFFFFFFFFF,长度为64 bitframe-masking-key4(%x00-FF),当frame-mask为1时存在,长度为32 bitframe-payload-dataframe-masked-extension-data frame-masked-application-data,当frame-masked为1时frame-unmasked-extension-data frame-unmasked-application-data,当frame-masked为0时frame-masked-extension-data(%x00-FF),保留给将来的扩展,长度为n8,其中n>0frame-masked-application-data(%x00-FF),长度为n8,其中n>0frame-unmasked-extension-data(%x00-FF),保留给将来的扩展,长度为n8,其中n>0frame-unmasked-application-data(%x00-FF),长度为n*8,其中n>05.3 客户端到服务端添加掩码添加掩码的数据帧必须像5.2节定义的一样,设置frame-masked字段为1。掩码值像第5.2节说到的完全包含在帧中的frame-masking-key上。它是用于对定义在同一节中定义的帧负载数据Payload data字段中的包含Extension data和Application data的数据进行添加掩码。掩码字段是一个由客户端随机选择的32bit的值。当准备掩码帧时,客户端必须从允许的32bit值中须知你咋一个新的掩码值。掩码值必须是不可被预测的;因此,掩码必须来自强大的熵源(entropy),并且给定的掩码不能让服务器或者代理能够很容易的预测到后续帧。掩码的不可预测性对于预防恶意应用作者在网上暴露相关的字节数据至关重要。RFC 4086讨论了安全敏感的应用需要一个什么样的合适的强大的熵源。掩码不影响Payload data的长度。进行掩码的数据转换为非掩码数据,或者反过来,根据下面的算法即可。这个同样的算法适用于任意操作方向的转换,例如:对数据进行掩码操作和对数据进行反掩码操作所涉及的步骤是相同的。表示转换后数据的八位字节的i(transformed-octet-i )是表示的原始数据的i(original-octet-i)与索引i模4得到的掩码值(masking-key-octet-j)经过异或操作(XOR)得到的:j = i MOD 4transfromed-octed-i = original-octet-i XOR masking-key-octet-j在规范中定义的位于frame-payload-length字段的有效负载的长度,不包括掩码值的长度。它只是Payload data的长度。如跟在掩码值后面的字节数组的数。5.4 消息分片消息分片的主要目的是允许发送一个未知长度且消息开始发送后不需要缓存的消息。如果消息不能被分片,那么一端必须在缓存整个消息,因此这个消息的长度必须在第一个字节发送前就需要计算出来。如果有消息分片,服务端或者代理可以选择一个合理的缓存长度,当缓存区满了以后,就想网络发送一个片段。第二个消息分片使用的场景是不适合在一个逻辑通道内传输一个大的消息占满整个输出频道的多路复用场景。多路复用需要能够将消息进行自由的切割成更小的片段来共享输出频道。(注意:多路复用的扩展不在这个文档中讨论)。除非在扩展中另有规定,否则帧没有语义的含义。如果客户端和服务的没有协商扩展字段,或者服务端和客户端协商了一些扩展字段,并且代理能够完全识别所有的协商扩展字段,在这些扩展字段存在的情况下知道如何进行帧的合并和拆分,代理就可能会合并或者拆分帧。这个的一个含义是指在缺少扩展字段的情况下,发送者和接收者都不能依赖特定的帧边界的存在。消息分片相关的规则如下:一个未分片的消息包含一个设置了FIN字段(标记为1)的单独的帧和一个除0以外的操作码。一个分片的消息包含一个未设置的FIN字段(标记为0)的单独的帧和一个除0以外的操作码,然后跟着0个或者多个未设置FIN字段的帧和操作码为0的帧,然后以一个设置了FIN字段以及操作码为0的帧结束。一个分片的消息内容按帧顺序组合后的payload字段,是等价于一个单独的更大的消息payload字段中包含的值;然而,如果扩展字段存在,因为扩展字段定义了Extension data的解析方式,因此前面的结论可能不成立。例如:Extension data可能只出现在第一个片段的开头,并适用于接下来的片段,或者可能每一个片段都有Extension data,但是只适用于特定的片段。在Extension data不存在时,下面的示例演示了消息分片是如何运作的。示例:一个文本需要分成三个片段进行发送,第一个片段包含的操作码为0x1并且未设置FIN字段,第二个片段的操作码为0x0并且未设置FIN字段,第三个片段的操作码为0x0并且设置了FIN字段。控制帧(见5.5节)可能被插入到分片消息的中间。控制帧不能被分片。消息片段必须在发送端按照顺序发送给接收端。除非在扩展中定义了这种嵌套的逻辑,否则一条消息分的片不能与另一条消息分的片嵌套传输。终端必须有能力来处理在分片的消息中的控制帧。发送端可能会创建任意大小的非控制消息片段。客户端和服务端必须同时支持分片和不分片消息。控制帧不能被分片,并且代理不允许改变控制帧的片段。如果有保留字段被使用并且代理不能理解这些字段的值时,那么代理不能改变消息的片段。在扩展字段已经被协商过,但是代理不知道协商扩展字段的具体语义时,代理不能改变任意消息的片段。同样的,扩展不能看到WebSocket握手(并且得不到通知内容)导致WebSocket的连接禁止改变连接过程中任意的消息片段。作为这些规则的结论,所有的消息片段都是同类型的,并且设置了第一个片段的操作码(opccode)字段。控制帧不能被分片,所有的消息分片类型必须是文本或者二进制,或者是保留的任意一个操作码。注:如果控制帧没有被打断,心跳(ping)的等待时间可能会变很长,例如在一个很大的消息之后。因此,在分片的消息传输中插入控制帧是有必要的。实践说明:如果扩展字段不存在,接收者不需要使用缓存来存储下整个消息片段来进行处理。例如:如果使用一个流式API,再收到部分帧的时候就可以将数据交给上层应用。然而,这个假设对以后所有的WebSocket扩展可能不一定成立。5.5 控制帧控制帧是通过操作码最高位的值为1来进行区分的。当前已经定义的控制帧操作码包括0x8(关闭),0x9(心跳Ping)和0xA(心跳Pong)。操作码0xB-0xF没有被定义,当前被保留下来做为以后的控制帧。控制帧是用于WebSocket的通信状态的。控制帧可以被插入到消息片段中进行传输。所有的控制帧必须有一个126字节或者更小的负载长度,并且不能被分片。5.5.1 关闭(Close)控制帧的操作码值是0x8。关闭帧可能包含内容(body)(帧的“应用数据”部分)来表明连接关闭的原因,例如终端的断开,或者是终端收到了一个太大的帧,或者是终端收到了一个不符合预期的格式的内容。如果这个内容存在,内容的前两个字节必须是一个无符号整型(按照网络字节序)来代表在7.4节中定义的状态码。跟在这两个整型字节之后的可以是UTF-8编码的的数据值(原因),数据值的定义不在此文档中。数据值不一定是要人可以读懂的,但是必须对于调试有帮助,或者能传递有关于当前打开的这条连接有关联的信息。数据值不保证人一定可以读懂,所以不能把这些展示给终端用户。从客户端发送给服务端的控制帧必须添加掩码,具体见5.3节。应用禁止在发送了关闭的控制帧后再发送任何的数据帧。如果终端收到了一个关闭的控制帧并且没有在以前发送一个关闭帧,那么终端必须发送一个关闭帧作为回应。(当发送一个关闭帧作为回应时,终端通常会输出它收到的状态码)响应的关闭帧应该尽快发送。终端可能会推迟发送关闭帧直到当前的消息都已经发送完成(例如:如果大多数分片的消息已经发送了,终端可能会在发送关闭帧之前将剩余的消息片段发送出去)。然而,已经发送关闭帧的终端不能保证会继续处理收到的消息。在已经发送和收到了关闭帧后,终端认为WebSocket连接以及关闭了,并且必须关闭底层的TCP连接。服务端必须马上关闭底层的TCP连接,客户端应该等待服务端关闭连接,但是也可以在收到关闭帧以后任意时间关闭连接。例如:如果在合理的时间段内没有收到TCP关闭指令。如果客户端和服务端咋同一个时间发送了关闭帧,两个终端都会发送和接收到一条关闭的消息,并且应该认为WebSocket连接已经关闭,同时关闭底层的TCP连接。5.5.2 心跳Ping心跳Ping帧包含的操作码是0x9。关闭帧可能包含“应用数据”。如果收到了一个心跳Ping帧,那么终端必须发送一个心跳Pong 帧作为回应,除非已经收到了一个关闭帧。终端应该尽快恢复Pong帧。Pong帧将会在5.5.3节讨论。终端可能会在建立连接后与连接关闭前中间的任意时间发送Ping帧。注意:Ping帧可能是用于保活或者用来验证远端是否仍然有应答。5.5.3 心跳Pong心跳Ping帧包含的操作码是0xA。5.5.2节详细说明了Ping帧和Pong帧的要求。作为回应发送的Pong帧必须完整携带Ping帧中传递过来的“应用数据”字段。如果终端收到一个Ping帧但是没有发送Pong帧来回应之前的pong帧,那么终端可能选择用Pong帧来回复最近处理的那个Ping帧。Pong帧可以被主动发送。这会作为一个单项的心跳。预期外的Pong包的响应没有规定。数据帧数据帧(例如非控制帧)的定义是操作码的最高位值为0。当前定义的数据帧操作吗包含0x1(文本)、0x2(二进制)。操作码0x3-0x7是被保留作为非控制帧的操作码。数据帧会携带应用层/扩展层数据。操作码决定了携带的数据解析方式:文本“负载字段”是用UTF-8编码的文本数据。注意特殊的文本帧可能包含部分UTF-8序列;然而,整个消息必须是有效的UTF-8编码数据。重新组合消息后无效的UTF-8编码数据处理见8.1节。二进制“负载字段”是任意的二进制数据,二进制数据的解析仅仅依靠应用层。5.7 示例一个单帧未添加掩码的文本消息0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (内容为"Hello")一个单帧添加掩码的文本消息0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (内容为Hello")一个分片的未添加掩码的文本消息0x01 0x03 0x48 0x65 0x6c (内容为"Hel")0x80 0x02 0x6c 0x6f (内容为”lo")未添加掩码的Ping请求和添加掩码的Ping响应(译者注:即Pong)0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (包含内容为”Hello", 但是文本内容是任意的)0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (包含内容为”Hello", 匹配ping的内容)256字节的二进制数据放入一个未添加掩码数据帧0x82 0x7E 0x0100 [256 bytes of binary data]64KB二进制数据在一个非掩码帧中0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]扩展性这个协议的设计初衷是允许扩展的,可以在基础协议上增加能力。终端的连接必须在握手的过程中协商使用的所有扩展。在规范中提供了从0x3-0x7和0xB-0xF的操作码,在数据帧Header中的“扩展数据”字段、frame-rsv1、frame-rsv2、frame-rsv3字段都可以用于扩展。扩展的协商讨论将在以后的9.1节中详细讨论。下面是一些符合预期的扩展用法。下面的列表不完整,也不是规范中内容。“扩展数据”可以放置在“负载数据“中的应用数据”之前的位置。保留的字段可以在每一帧需要时被使用。保留的操作码的值可以被定义。如果需要更多的操作码,那么保留的操作码字段可以被定义。保留的字段或者“扩展”操作码可以在“负载数据”之中的分配额外的位置来定义,这样可以定义更大的操作码或者更多的每一帧的字段。 ...

January 7, 2019 · 2 min · jiezi

Windows使用技巧

(持续更新中…)喜欢的Windows软件vscode跨平台轻量IDE,有强大的插件市场chrome前端开发必备浏览器,不多说xshellssh免密快捷登录神器;高级版的是付费的,教育版免费,足以在大部分场景下使用,xShell教育版下载地址scoop安装软件的神器;确保PowerShell已经安装,执行以下命令即可安装scoop:iex (new-object net.webclient).downloadstring(‘https://get.scoop.sh’)scoop安装完成后,就可以使用命令行快速安装程序了,大多数程序安装不需要管理员权限,并且可以自动配置环境变量,例如:scoop install yarnscoop install pythonscoop install curlscoop官网sourcetree跨平台的git可视化管理工具https://www.sourcetreeapp.com/github desktopgithub官方的git工具cmder强大而美观的cli工具http://cmder.net/teamviewer远程控制软件,方便在家远程连公司网络办公,下载地址zoom远程会议,远程桌面演示必备Win10使用技巧截屏/录屏按win+g,可以开启游戏模式,这个模式下会有录屏工具出现点击黑色圆圈可以开始录屏,录屏完成后的视频文件会自动保存到视频/捕获文件夹中手机离开电脑自动锁定电脑需要蓝牙支持

January 7, 2019 · 1 min · jiezi

解密!如何让别人不由自主的答应你的要求

北京历途科技有限公司是一家专注于人工智能与机器人研发的高新技术企业,经过十几年的技术积累,现已自主研发出专业、高效、安全的高楼外墙清洗机器人,填补了国际外墙清洁市场智能化产品空白。公司以"人人皆创客,完美做产品"为发展理念,打造具有颠覆性的科技产品。元旦小长假已经过去了,小伙伴们的心是不是已经都回到了工作上呢?新的一年新的征程,这是2019年的第一个周,相信在元旦零点的时候好多人都和小编一样,在这个最美好的时间里许下了最真诚的心愿,不管是为自己还是为别人,不管是工作的还是生活的,希望在2019年里都能实现。那么许下的愿望如何才能实现呢?今天,小编给大家推荐一本有“影响力教父”之称的美国作家罗伯特.西奥迪尼的书《影响力》。虽然这本书看着好像都是在讲营销人员的一些技巧和陷阱,但是当我们读完之后就会发现它其实是为我们解释了“为什么有些人极具说服力,而我们总是不由自主地答应他们的要求”这样一个事实。当我们也可以拥有这种让人顺从的能力之后,我们的愿望又何愁不能实现呢?所以说这本书不仅对营销人员重要,对我们每个人也同样重要。如何才能让别人不由自主的顺从自己?小编送你6大心理秘籍。1、互惠中国有一句俗语“吃人家的嘴软,拿人家的手短”。互惠原理其实就和这句俗语不谋而合。如果人家给了我们某种好处,我们在潜意识里就会找机会以同等价值或其喜欢的礼物回报他人的恩惠,这就是互惠原理的影响力。这种负债感会引导我们对此不能无动于衷,否则就会被贴上忘恩负义的标签,因为人们对那些只知索取不知回报的人是由衷厌恶的。所以通常情况下,人们在有负债感时会比没有负债感时更容易答应别人的请求。同理,如果想增加自己的影响力,互惠原理可以为我们增加很多优势。这又正好和另一句俗语不谋而合了“吃亏是福”,也就是平时的工作和生活中,不计回报的多付出、多帮助别人,当你有需要帮助的时候,别人就会义无反顾的回报你之前的恩惠。2、承诺和一致莱昂纳多.达.芬奇说过“在开始的时候拒绝总比在最后拒绝容易得多。”承诺和一致原理认为:一旦我们做出了某个决定,或选择了某种立场,就会面对来自个人和外部的压力迫使我们的言行与它保持一致。为什么我们会有如此大的动力去保持一致呢?因为在我们的潜意识里我们会认为做事前后一致的人就应该受到尊重,而那些前后不一的人则会被人们认为是一种不好的品行。所以如果我们想要增加自己的影响力,就应该要尽量做到让别人保持前后一致。比如在销售东西的时候多问几个让客户可以打消退货念头的问题;比如某个人在做出承诺的时候可以尽量让他有一个书面说明。因为书面声明很容易被公之于众,而且比口头承诺需要付出的努力更多,所以书面说明的影响力更大。3、社会认同社会认同实际上就是从众行为。只不过这个从众行为不是单一的一个人或是一小部分人的从众行为,而是大规模的社会从众行为。社会认同原理认为:我们进行是非判断的标准之一就是看别人是怎么想的,尤其是当我们要决定什么是正确的行为时。因为根据大众的经验去做事,往往可以使我们少犯很多错误。所以它为我们的思考和行动提供了一条捷径。正如为什么广告商总喜欢告诉我们哪种商品的销量最高一样。因为当我们知道这件商品的销量高的时候,我们便会认为既然大部分的人都在买的东西,那么质量一定不错。我们参照别人的行为来决定自己的行为。其实这并不是毫无道理的。因为多数人都去做的事情往往都是正确的事情。所以当我们想让别人认同我们的时候,我们可以尽量做一些在社会上都是被认可的事情,这样别人的顾虑便会减少,社会认同的影响力也会促使他认同我们的想法。4、喜好人们总是愿意答应自己认识和喜爱的人提出的要求,这应该是很自然的事,没有谁会对此感到惊讶。这条原理也常常被一些想要我们答应他们要求的陌生人所使用。他们可能和我们的穿着,说话语气很相似,他们可能和我们有着一样的兴趣爱好。这时我们的喜好心理便起到作用,因为我们总是更容易接受和自己有共同爱好的人,也更喜欢和自己有相似度的人做朋友。这时,我们便可以借此来提高自己的影响力,我们可以通过抓住他人的喜好,或者他喜欢的人的喜好来提高自己的影响力。但是这里有一条原则就是我们不能通过这种方式去做一些违背法律,违背道德的事。5、权威权威所具有的强大力量会影响我们的行为,即使是具有独立思考能力的成年人也会为了服从权威的命令而做出一些让人意想不到的事来。因为我们一出生,便被告知服从权威是应该的,违抗权威则是错误的。这个信息伴随着我们一生,所以当一些有权威的人说出一些话,或者让我们去干一些事的时候,我们便会义无反顾的去服从。所以当我们的实际地位,或者在人们心里的地位有了一定的分量的时候,他们便会义无反顾的去按我们的要求做。从而使我们的影响力得到提升。6、短缺“机会越少,价值就越高” 的短缺原理会对我们的行为造成全面的影响,害怕失去某种东西的想法比希望得到同等价值东西的想法对人们的激励作用更大。近几年的房价不断攀升,除了人为炒高房价的原因之外,也因为有人宣称我国的土地将出现长期短缺。在我们的潜意识里总会认为“物以稀为贵”,正因为这个原因激起了人们购买的欲望。我们总会觉得难以得到的东西比容易得到的东西更好,所以我们便会根据获得东西的难易程度来判断质量的高低。如果我们想让别人同意自己的看法或者答应自己的请求,我们就尽量让别人觉得这个想法是多么的来之不易,是集合了多少人的智慧,是多少人都无法想象到的。当我们把这些都阐述明白的时候,相信没有几个人可以对这样的想法拒之门外。当你读过这本书之后,你一定能有所感悟,运用这6大心理秘籍就能让自己比以前更具影响力。2019年刚刚开端,让我们一起加油,这一年大展宏图,走在创新的路上,点燃灵感的火花,收获成功的喜悦,离梦想更近一步。想获得《影响力》电子版书籍的小伙伴们,关注公众号,回复“影响力”就可收到啦,感谢您的阅读。

January 4, 2019 · 1 min · jiezi

互联网凛冬,看大厂HR怎么说~

写在前面的话 最近互联网朋友圈充斥着一股恐慌的气息。是的,如果你是圈内人,恐怕已经猜到了,席卷整个行业的CAI员、降薪席卷业内。以滴滴程维宣布“ 公司高管无年终奖、普通员工年终奖减半 ”上微博热搜为标志,全民都感受到“互联网的寒冬”来了。 想写这篇文章,是因为一位多年的好友(资深程序猿)昨天向我抛出了一个问题 —— 以资深HR的角度说说,如何在这场风波中不被裁员。 当然,这不是我今天要讲的问题,因为任何措施都无法保证做到这一点,到最后也只会归结为简单的一句话——实力至上。这人人可见的道理,写出来就是一碗无用的毒鸡汤,想来也不必浪费各位的眼球了。反之,如果你的实力确实不容小觑,这个担心也就自然多余了。 但实际情况必然不是如此的,人无完人,没有人是保证立于不败之地的。既然没有一劳永逸的办法,那我想换一个角度来谈谈我的想法。与其终日惶惶,等待那一纸不知何时会降临的“裁决书”,不如主动出击,提前做好应急准备。如此,你便能掌握主动权,不管是主动寻求更好的机会,亦或是真的拿了“抚慰金”,都可以游刃有余,无后顾之忧。 本人作为一名HR,万不敢称资深,只是呆过几年大型央企和大型互联网企业,聊有一点自己的看法罢了。在此,从HR 的角度,讲讲换工作过程中的一些事情,希望在你用得上时能有一些参考意义,便自觉功德无量了。本文由IT平头哥联盟特邀某大厂资深HR,从HR的角度讲讲如何完美渡过“凛冬”公众号:honeyBadger8,群:912594095一.确定目标 求职者经常会自问或被问到这样一个问题:你想要一份怎样的工作?许多人都没有认真去思考过这个问题或者比较含糊,更多的是一种感觉,甚至会说随便。所以,在找工作的各个阶段就会表现出各种迷茫、纠结,这便是我们常说的“无头苍蝇”。一旦成为这种生物,你便会到处乱窜,撞到大运的可能性自然是少之又少的,结果只能是撞得满头包,然后随便找个“还可以”的工作,继续将就,在以后的日子里继续重复“不满—辞职—找工作”这一不断重复妥协的过程,最后职业生涯中断,技能无法沉淀,职场资源得不到积累,领导信任需要重新建立,永远在基层岗位徘徊,待你人到中年,还奋斗在码农一线,人家也该嫌弃你不如年轻少壮了。 因此,想好“你想要一份怎样的工作”这一问题,是关乎你整个职业生涯的大计。那么如何确定呢?我认为应该分两个阶段——投递简历前和拿到offer后。投递简历前确定目标是为了提高找到合适工作的可能性,而拿到offer后进一步明确目标是为了提升你的选择正确的可能性。本文的行文思路是寻求一个新机会的整个过程,因此先从投递简历前建立目标谈起,拿到offer后的选择问题后文有详谈。 这一阶段的目标不需要是具体而清晰的,不可走两个极端——广撒网和一条道走到黑。 广撒网会带来两个后果:一是这会带来简历无针对性,泛泛而谈,获得好的面试机会的概率不大;二是一些不太好的机会找上门来,影响你对自我的定位或者因盲目地想积攒面试经验而浪费时间。 一条道走到黑,指的是把自己限得太死,只选自己熟悉的企业或者只选一两家死磨硬泡。这也是不可取的,世界之大无奇不有,机会很多,只是你可能不知道有这个机会或者不知道这个机会你可以。所以还是鼓励多尝试。 那这就有一个度的把握的问题,如何做到既不把自己限在死胡同而又避免到处乱窜,这不是一个艺术问题,而是可以通过确定目标去做到的。我们知道,我们在投递简历过程中,可以大概了解到的信息主要有公司简介、岗位JD和岗位薪资范围。所以,我们的目标范围就选择我们可以获取到的信息作为参考。 总结来看,行业有地位、工作内容有挑战、薪资范围可期待,那么就值得尝试。其他机会果断放弃。如果不放心,以一周为期,对展示在面前的机会进行初步分级。HR处理简历一般都会在一周内完成,如果一周内没有太多面试机会,再补充投递第二级别的企业。二、准备简历 在这里要聊的不是如何做一份漂亮的简历,因为这个你大可以网上找一个模板,都很棒。而且,在网上投递阶段更多的是在线简历,都有固定的式样。所以更多的要聊的是内容的呈现。有几个要点。 一是要条理清晰,突出重点。工作经历和项目经历是简历的重点,在叙述时按点呈现,言简意赅的讲清楚做了什么,自己发挥了什么作用。HR筛选简历实际上就像改卷,是找亮点的过程,是按点匹配的过程。招聘平台“简历匹配”的计算也是参照这一逻辑。 二是切忌万金油式的简历。针对不同的企业和岗位,选择不同版本的简历。例如,国企对所获荣誉等比较看中,可以选择一定的篇幅来呈现自己的获奖经历。面对不同的企业,选择不用的简历,呈现你想呈现的重点。 三是不要贪心。简历只需要做到90分即可。十分理解每个人尽善尽美的态度,特别是这种关乎前途命运的时候,争一把当然是对的。但我认为,只需要90分,让你获得可以面试的机会就可以了,因为这只是第一步,重头戏在面试。如果你的简历100分,会拔高企业对你的期望,面试下来就会转而失望。反之,企业会从你身上发现更多惊喜。不要过早地摊牌。三、面试 前期讲了很多,但面试却是最具决定性的。社会招聘过程中,大部分企业往往采用结构化面试,因此这里主要以结构化面试场景进行一些技巧介绍。其他面试形式以后有机会再谈。 采用“三步走”战略,即准备阶段、面试阶段、结束阶段。准备阶段:1.对企业、岗位做初步了解,以表示自己对企业的尊重和对机会的重视;2.大致了解企业文化,以做出服装和面试过程中表现等风格的应对。如果是国企,尽量穿正装应试;在摸不清楚的情况下,亦可着正装面试,还是摆明态度问题。做好以上两点,基本上印象分就可以拿到了。接下来到了面试问答环节。面试阶段:1.自我介绍言简意赅。不超过两分钟,一定要条理清晰,不可太过冗长。讲清楚个人基本情况、工作成绩即可。而在工作成绩展现方面也应根据不同的企业调整侧重点,比如国企多看中企业认可度,因此多展现做了什么获得了什么荣誉;私企则强调效益,可以进行一些数据化的呈现。2.回答问题采用“总分”或“总分总”的形式。面试就是一场“作文”,摆明你的观点,再分点阐述,会使你显得富有条理。3.把简历内容做实。这个有些隐晦,但“你懂的”。如果你要写在简历上,那么一定要经得住问。4.向面试官推荐自己。多数面试官会采用多听面试者说的心态进行面试,以期获得意外收获。那我们可以主动掉入他的“陷阱”,讲一些我们希望他听到的信息。那自然是我们的优点。所以可以在面试过程中讲一些自我评价,9分优点,1分不痛不痒的缺点,同时准备好案例佐证,因为面试官一定会问:“你这样评价自己,有什么实际案例吗?”5.不要啰嗦。推荐自己,但不是说得越多越好,问题回答完了即可。多余的叙述会造成不沉稳的印象,或者会显得总结概括能力不佳,也可能言多必失,说出一些你不想呈现的点。6.期望薪资怎么回答。说出内心真实的想法即可,特别是私企,这会成为给你定薪的重要参照。也可以略高于期望,以此探一探对方的反应。如果确实不太确定,可以回答相信贵公司会给出一个与我能力相匹配的薪资。结束阶段:1.提一个好的问题。面试的最后,企业为了表示尊重,一般会给面试者提问的机会。千万不要以为这个时候面试结束了,可以随便发问。其实面试官有自己的考虑,面试还在继续,他是希望通过这个获取到你内心深处的想法,因为你问什么代表你关心什么。问一个好的问题,会加深他对你的良好印象。反之,会因为近因效应,前面的一切付之东流。如何提好一个问题呢?一句话“显上进、莫啰嗦”。举几个例子。案例一:某求职者以为面试结束了,问:“请问贵公司加班严重吗?”;面试官心里会想“他也许不能加班,他可能不太求上进”案例二:某求职者问“请问贵公司的培训体系是怎么样的?”;面试官心里会想“他很上进,在考虑日后长远发展的问题”那么,很明显了,第二种提问更好。有人要问,第一个问题难道没有了解的必要性吗?当然要了解,但不是现在。2.表示感谢和期待。礼多从来人不怪。以上从面试全过程的角度简单阐述了一些面试技巧,希望各位受用。如果实力不差,又确实做到了这几点,基本上可以等着拿offer了。四、offer洽谈 Offer洽谈也要看企业类型,以企业类型定策略。 这个“谈”字,在国企一般是“告知”的意思,也就是说基本没有讨价还价的余地,他们是严格按照你的工作年限、岗位情况、面试情况等对应相应的职级来确定薪资,而且为了保证内部团队的稳定性,基本不会为个人去做太大的突破。很多国企薪资听上去挺低,但年收入算下来往往不比私企差,甚至更高。因此,国企offer洽谈重在“了解”,把他们的各项福利摸清楚才是你的重点。国企一般会比较隐晦,有些东西你不问,他们并不会主动告诉你。 私企的“谈”字就更贴近字本身的意义一些。现在私企定薪比较灵活(粗放),一般是在你原来薪资的基础上给你加一些,但是如果你能力突出,也许能争取到一些突破。所以,私企谈薪重在“试”,试他们对你的态度,来判定能不能争取到更高的薪资。当然,更多的了解也是有必要的,私企的月薪较高,但也可能存在一些隐患秘而不宣,比如年终奖少,五险一金基数和比例低等等,综合收入算下来并不高。五、offer选择 到了这一步,自然是手上有几个可供选择的offer了。买东西都讲究“货比三家”,offer选择自然也是一样。切忌拿到一个差不多的offer就下定论,“货比三家”既能帮助你选到更好的,也能坚定你对这份工作的信心。所以如果你没有三个及以上offer,下文先别看了,回去先继续找。言归正传,如何选择最佳offer呢? 首当其冲应建立起对工作本身的评价体系。换句话说一份工作是由各种因素组成的,它应该是立体而鲜活的。我们选取最重要的维度进行构建工作评价模型。 经初步统计,笔者HR从业期间,“阅人无数”,曾做过一个简单统计,在求职者应聘过程中最关心的问题排行榜中占据最高比例的三个问题,依次为:薪资福利、企业平台、职业发展。当然,工作地点、工作氛围、加班时长、户口问题等等也是重要的考虑维度。“没有一份工作是完美的”,这是我们面对工作应有的态度。所以,考虑时也无法面面俱到,必须有所偏向,有所舍弃。我将这些维度分为三类:重要因素、必要因素、非必要因素。 重要因素,顾名思义,是我们找工作过程中最看中的,是决定是否选择这份工作的决定性因素。我姑且取大部分人最关心的三要素纳入其中。当然,每个人都可以根据自身情况自行调整。 必要因素,指的是那些根据自身情况,必不可少的因素。换句话说,不管这份工作有多好,只要它不满足这个因素,我绝对不能接受。举个例子,我已经在这个城市定居,老婆在本地工作,孩子在这儿上学,我绝对不会去外地,那么,工作地点就是我的必要因素。再比如我是一个看中工作与生活平衡的人,加班严重的机会我绝对不会选择,那这个也是我的必要因素。 非必要因素,指的既不是必要,也没那么重要,有则锦上添花,无则无伤大雅的因素。比如某些小企业爱搞的生日祝福、某些国企爱搞的企业年金等,可有可无。 那么,很明了了,我们的重点应该放在重要因素上。我们先用排除法排除掉那么不符合必要因素的工作,避免浪费时间。非必要因素前期无法了解那么清楚,也不是我们的考虑重点。 接下来,我们来分析一下重要因素。以代表性的三个因素为例: 薪资福利,是工作价值的体现,也是养家糊口的必备项,重要性不言而喻;企业平台,本人从来不认同“是金子总会发光”这句话,因为金子并不是发光体。如果你把它埋在沙里,那得撞大运的人才能找到。所以,我们向来提倡“酒香也怕巷子深”的思想,选择一个好的平台,会让你更加容易实现自己的价值。 职业发展,“大的平台不一定有好的职业发展”,这是我们首先应该建立的一个认知。阿里腾讯自然是国内认可的顶级互联网公司平台,但依然有很多人辞职,我相信职业发展这一原因不在少数。很浅显的道理,你可能会在这里做一颗螺丝钉。当然,平台大机会也多,但是竞争者也一样多。所以,职业发展见仁见智了。如果非要谈一下看法的话,适合自己的才是最好的。 各因素都了然于胸了,那么到了建立模型的时候。这个模型很简单,每个人都会。我把它称为“加权计数法”。具体操作方式如下表:维度重要性(以比例表示)工作机会1评分工作机会2评分薪酬福利40%9080企业平台30%8090职业发展30%9080合计 8783那么,无需纠结,选择工作机会1。结束语 寥寥数语,不能尽言。希望能为你度过“互联网的寒冬”提供一点助益,那也不枉我敲了几个小时了。记得关注公众号:IT平头哥联盟热门推荐:vue/react/java/大厂面试题等资源免费获取 Eruda 一个可能被人遗忘的调试神器大家好 这就是2018年的我~脱了程序员的格子衫 我是如何月入三万的~如何给localStorage设置一个有效期?作者:小菜园的菜园-IT平头哥联盟 链接:http://susouth.com/ 交流:912594095、公众号:honeyBadger8 本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。

January 2, 2019 · 1 min · jiezi

chrome插件编写

chrome浏览器Chrome 浏览器追求的是全方位的快速体验。它不仅能飞快地从桌面上启动,而且能瞬间完成网页加载,还能以闪电般的速度运行网络应用。 Chrome 浏览器整洁且直观。您可在同一位置进行搜索和导航,可随意排列标签页,既快捷又轻松。您不必成为安全专家即可放心地浏览网络。Chrome 默认会为用户提供安全保护,并可供所有人轻松且安全地使用。内置地址所有内置地址列表:chrome://chrome-urls/开发者常使用列表:开发者高级选项:chrome://flags/dns缓存清除: chrome://net-internals/#dns扩展应用程序:chrome://extensions/侦测页面[包括外接安卓设备]:chrome://inspect/#devices比较全的的列表:(1)chrome://accessibility/可达性分析,默认是关闭的,点击accessibility off后变成accessibility on|show accessibility tree,点击show accessibility tree显示分析树(2)chrome://appcache-internals/应用程序缓存,显示所有的应用程序缓存路径列表(3)chrome://apps/当前chrome安装的应用列表(4)chrome://blob-internals/当前内部的blob文件列表(5)chrome://bookmarks/ 书签管理器(6)chrome://cache/当前缓存文件的url列表,点击url可以看到对应的缓存文件内容,包括类型 编码 过期时间等概要信息,以及文件内容等具体信息,以二进制方式显示(7)chrome://chrome/chrome关于页面,显示当前chrome的版本信息。(8)chrome://chrome-urls/显示chrome可用的伪url列表(9)chrome://components/显示chrome的组件列表,可以点击"检测是否有更新"来检测当前组件是否有新版本,如果有,可以直接下载更新。(10)chrome://conflicts/模块冲突检测,会列出当前已加载到主进程中的所有模块,包括模块的数量、签名方,版本以及模块所在的位置(11)chrome://copresence/Google共存信息列表,显示共存信息,包括有效指令、传输的令牌、收到的令牌(12)chrome://crashes/显示当前chrome的崩溃报告,需要启用崩溃报告后才会显示。https://support.google.com/chrome/answer/96817中说明了如何启用崩溃报告。设置后需要重启chrome才能生效(13)chrome://credits/类似于演职员列表,是一份chrome使用的一些开源组件或工具的列表,包括各种工具的主页和授权文件。但第一行的“Accessibility Audit library, from Accessibility Developer Tools”的主页居然链接的是一份未压缩的js源码,不知道是啥情况(14)chrome://device-log/设备日志,chrome://device-log/?refresh=秒数 可以自动刷新(15)chrome://devices/显示当前在网络中注册的设备,可以添加打印机到云打印(16)chrome://discards/丢弃的标签页面,标签页面按照感兴趣的程度由高到低排序。如果当前的物理内存超出运行内存后,最不感兴趣的tab页面可能被丢弃掉。(17)chrome://dns/如果当前显示DNS pre-resolution and TCP pre-connection is disabled.则打开设置,勾选“预提取资源,以便更快速地加载网页”然后刷新当前页面就可以看到相关页面预加载的分析数据列表了(18)chrome://downloads/下载内容页面(19)chrome://extensions/扩展程序列表(20)chrome://flags/实验性功能列表,可以在这里启用这些实验性的功能(21)chrome://flash/显示flash插件的版本信息,安装位置及显卡的一些具体信息,包括显卡版本号,GPU(22)chrome://gcm-internals/GCM(Google Cloud Messaging )内部构建信息,包括签到、连接、注册、发送、接收相关的日志信息。(23)chrome://gpu/显示当前的GPU信息,包括图像功能的状态(各种功能的硬件加速)、Gpu内存buffer的状态(24)chrome://help/chrome关于页面,与chrome://chrome/是同样的效果(25)chrome://histograms/直方图,柱状图,显示了浏览器启动到上一个页面加载的状态统计数据,重新加载可以获取到当前页面加载数据。(26)chrome://history/历史记录页面,显示所有的浏览记录(27)chrome://indexeddb-internals/显示chrome的内部数据库的实例列表,包括所有的内部数据所在的路径,最后修改时间及数据库大小(28)chrome://inspect/检测设备,页面,扩展插件,应用程序等,在Pages标签中显示当前打开的所有tab页面,点击inspect直接跳转到该页面,并调起开发者工具。(29)chrome://invalidations/失效的调试信息,失效调试服务状态,注册的失效服务处理器等信息(30)chrome://local-state/本地状态,显示的是一个json格式的文件,包括了浏览器的很多状态信息,插件的详细信息(31)chrome://media-internals/当前的多媒体内部构建信息,如果当前使用的是浏览器内部的播放器而不是flash播放器的话,会在Players、Audio、Video Capture显示播放文件的详细信息,在Player中可以显示当前播放的适配的详细信息,包括音频、视频及播放器的内部状态信息。(32)chrome://memory会自动跳转到chrome://memory-redirect/,显示浏览器占用的总内存以及各个内部进程占用的内存,包括各个Tab页面占用的物理内存和虚拟内存数,包括私有内存,共享内存及总内存(33)chrome://memory-internals/内存内部详细信息,点击Update后可以获取到当前浏览内存的使用信息,包括一个json格式的数据。下发有一个列表,可以显示出所有进程所占用的内存,以及V8引擎使用的和分配的内存。(34)chrome://nacl/NaCl的版本信息,包括NaCl的插件位置,版本信息(35)chrome://net-internals网络内部构件的信息。包括代理信息、时间信息、DNS信息。其中的DNS可以显示当前浏览器中缓存的DNS信息,包括过期时间(36)chrome://newtab打开一个新的标签页(37)chrome://omnibox/omnibox 的测试工具 ,omnibox API https://developer.chrome.com/extensions/omnibox(38)chrome://password-manager-internals/显示捕获到的密码管理日志。当这个页面被关闭掉,则已有的记录会被清除,并且不再捕获。(39)chrome://plugins/显示当前chrome中所使用的插件及版本信息,可以在该页面点击 停用 来停用某个插件(40)chrome://policy/显示已设置的政策,默认是没有的(41)chrome://predictors/显示预测列表,包括自动完成动作的预取列表,也就是在地址栏输入某些字母后,出现对应的完整地址的列表,还包括(42)chrome://print/浏览器打印页面(43)chrome://profiler/分析器,可以按照不同的条件分组及排序,主要应该是用于分析chrome的各种进程的内部信息(44)chrome://quota-internals/存储空间的配额信息,包括了三个tab页面,Summary、Usage&Quota、DataSummary标签页显示的是一个磁盘总的空闲空间大小以及混合统计的一些数值Usage&Quota 标签页显示的是临时的、持久化的和同步的数据库中的具体数据项Data标签中点击Dump按钮可以生成一个json格式的文件,包含了前面两个tab中的统计信息和分类信息。(45)chrome://serviceworker-internals/serviceworker的内部组件,可以勾选选择框,进行调试ServiceWorker的项目主页: https://www.chromium.org/blink/serviceworker介绍视频:https://youtu.be/4uQMl7mFB6g视频中介绍者的的代码:https://github.com/jakearchibald/simple-serviceworker-tutorialw3c ServiceWorker的Demo:https://github.com/w3c-webmob/ServiceWorkersDemos(46)chrome://settings/chrome的设置页面(47)chrome://signin-internals/当前chrome登录者信息,包括账户状态的各种信息(48)chrome://suggestions/推荐信息,也就是打开新的tab页是默认显示的推荐网站的缩略图页面的那些网站,包含了网站的信息和过期时间。(49)chrome://sync-internals/chrome账户同步信息,包含了多个tab页面,首页显示的是同步的概要信息,包含同步相关的各种信息,包括上一次同步的时间,Token请求时间,接收时间等。后面的tab按照同步的不同类型展示了不同信息,包括了同步的分类、事件等(50)chrome://system/系统的诊断数据,包括当前Chrome的版本信息、操作系统的版本信息、同步数据信息、数据压缩代理是否启用、内存使用概况信息、usb键盘检测信息。(51)chrome://terms/chrome服务条款(52)chrome://thumbnails/顶级网站的url,分为包含缩略图的和未包含的、聚焦和未聚焦,估计也是用于在新开tab页面做推荐时使用(52)chrome://tracing/可以在该页面录制chrome的跟踪信息,也可以使用监控(53)chrome://translate-internals翻译内部组件信息,此处包含了对于chrome翻译的设置,包括用户自定义的不需要自动翻译的网站列表,可以在此处进行编辑,删除包括了当前翻译引擎的设置,以及翻译组件的日志信息,日志包括了检测日志、事件日志和错误日志,其中检测日志可以dump为json格式的文件。追踪日志会将打开该页面后的浏览器访问的网页内容抓取出来,包括要翻译的文字内容列表(54)chrome://user-actions/用户操作列表,包括操作类型和发生时间。(55)chrome://version/显示当前chrome的版本信息,包括版本号、JavaScript 引擎版本号,Flash插件版本号,用户代理信息等(56)chrome://view-http-cache/显示当前http缓存的url列表,点击对应的url,可以打开缓存的文件,包含了文件的具体信息和二进制内容信息(57)chrome://webrtc-internals/webrtc内部组件信息。点击Create Dump按钮,可以下载当前的webrtc连接的数据信息和状态信息快照数据(58)chrome://webrtc-logs/webrtc日志信息应用商店扩展应用商店包含了很多实用的扩展应用,adblock,广告过滤开发人员小心你的本地调试代码也被拦截了Momentum 新开页面的美化Proxy SwitchyOmega 浏览器代理设置二维码小助手 当前页面的二维码生成器捕捉网页截图 - FireShot 滚动全局页面截图插件编写参考chrome扩展入门编一个入门的插件;扩展由不同但关联的组件构成。组件可以包括后台脚本,内容脚本,选项页面,UI元素和各种逻辑文件,根据扩展的具体功能加入需要的功能组件模块,其中使用的技术栈就是HTML、JS、CSS,而最后的扩展文件就是压缩的HTML,CSS,JavaScript,图像和Web平台中使用的其他文件得出的包,扩展具有广泛的功能。他们不仅可以修改用户查看、交互的Web页面内容,甚至可以扩展和更改浏览器本身的行为【所以每个浏览器的扩展可能有点不一样】,一个常见的chrome扩展会包含一下的一些内容:manifest.json清单文件【类似配置文件,表明权限、版本、名称、介绍、配置入口等信息】ico 【应用扩展商店和chrome浏览器上的工具栏展示的图标】Background Script 【后台脚本是扩展的事件处理程序;它包含对扩展重要的浏览器事件的侦听器。它处于休眠状态,直到事件被触发然后执行脚本逻辑。有效的后台脚本仅在需要时加载,在空闲时卸载】UI Elements 【UI界面就是正常的html页面,主要是交互中的弹窗等】Content Script 【这个就是操作用户打开的页面】Options Page 【扩展可供用户配置的页面,用于修改扩展中的一些默认配置参数】一个改变当前查看页面的背景色的扩展插件实现想要实现这个功能,很简单就知道需要一下模块:manifest.json 【配置】ico 【图标】Background Script 【图标按钮点击交互】UI Elements 【点击图标是展示的弹窗页面】Options Page 【可供用户选择修改默认的背景色的页面】Content Script 【修改用户查看的页面的背景色】manifest.json配置文件,权限、弹窗页面、ico位置等信息。{ “name”: “Getting Started Example”, “version”: “1.0”, “description”: “Build an Extension!222”, “permissions”: [“activeTab”, “declarativeContent”, “storage”], “background”: { “scripts”: [“background.js”], “persistent”: false }, “options_page”: “options.html”, “page_action”: { “default_popup”: “popup.html”, “default_icon”: { “16”: “fh-icon.png”, “32”: “fh-icon.png”, “48”: “fh-icon.png”, “128”: “fh-icon.png” } }, “icons”: { “16”: “fh-icon.png”, “32”: “fh-icon.png”, “48”: “fh-icon.png”, “128”: “fh-icon.png” }, “manifest_version”: 2}background.js后台脚本,chrome.runtime.onInstalled.addListener(function() { chrome.storage.sync.set({color: ‘#f00’}, function() { console.log(“The color is green.”); }); chrome.declarativeContent.onPageChanged.removeRules(undefined, function() { chrome.declarativeContent.onPageChanged.addRules([{ conditions: [new chrome.declarativeContent.PageStateMatcher({ pageUrl: {}, }) ], actions: [new chrome.declarativeContent.ShowPageAction()] }]); });});popup.html点击ico的时候弹出的页面,只有一个可被点击的色块按钮。<!DOCTYPE html> <html> <head> <style> button { height: 30px; width: 30px; outline: none; } </style> </head> <body> <button id=“changeColor”></button> <script src=“popup.js”></script> </body> </html>popup.js弹出页面的交互,点击弹窗页面中的按钮时,需要设置当前展示页面的背景色为chrome.storage存储的默认色值,let changeColor = document.getElementById(‘changeColor’);chrome.storage.sync.get(‘color’, function(data) { changeColor.style.backgroundColor = data.color; changeColor.setAttribute(‘value’, data.color);});changeColor.onclick = function(element) { let color = element.target.value; chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { chrome.tabs.executeScript( tabs[0].id, {code: ‘document.body.style.backgroundColor = “’ + color + ‘”;’}); });};options.html扩展应用页面可以查看每个扩展的详细介绍,这个页面就是扩展程序的扩展程序选项页面,用户可以自由配置扩展程序需要的参数【前提是扩展应用开发把配置参数放在这个页面供使用者修改了】。本案例是可供选择的背景色值。<!DOCTYPE html><html> <head> <style> button { height: 30px; width: 30px; outline: none; margin: 10px; } </style> </head> <body> <div id=“buttonDiv”> </div> <div> <p>Choose a different background color!</p> </div> </body> <script src=“options.js”></script></html>options.js扩展程序选项页面的交互脚本,在用户选择 options.html页面中的颜色块的时候,处理设置chrome.storage存储的默认色值,let page = document.getElementById(‘buttonDiv’); const kButtonColors = [’#3aa757’, ‘#e8453c’, ‘#f9bb2d’, ‘#4688f1’]; function constructOptions(kButtonColors) { for (let item of kButtonColors) { let button = document.createElement(‘button’); button.style.backgroundColor = item; button.addEventListener(‘click’, function() { chrome.storage.sync.set({color: item}, function() { console.log(‘color is ’ + item); }) }); page.appendChild(button); } } constructOptions(kButtonColors);上面实例是chrome官方提供的基础实例,还有很多API需要在使用的时候查看。不能科学上网的话,只能参考一下国内的其他浏览器的扩展开发,其实是类似的,基本都是借鉴的Chrome的方式。参考:chrome扩展入门chrome扩展综述 ...

December 26, 2018 · 2 min · jiezi

chrome浏览器扩展开发入门

一、环境安装安装nodejs首先需要安装nodejs,可以到官网下载,不过推荐使用nvs来安装nodejs。这里使用的是v11.5.0版本安装yeomannpm -g install yo安装generator-web-extensionnpm install -g generator-web-extension二、项目搭建# 创建项目目录mkdir helloworld && cd helloworld# 生成项目文件yo web-extension然后根据提示完成项目配置上图是询问是否需要UI ActionNo:不需要Browser:创建浏览器级别的UI ActionPage:创建页面级别的UI Action本示例选择Page来进行演示上图是询问是否需要覆盖一些浏览器的默认页面No:不需要Bookmarks Page:覆盖浏览器的书签页面History Page:覆盖浏览器的历史页面Newtab Page:覆盖浏览器的新标签页面本示例选择No上图是询问扩展需要的页面或者脚本Options Page:扩展的设置页面Devtools Page:在devtools里面的页面Content Scripts:在访问页面中运行的脚本Omnibox:给地址栏增加的功能按空格键根据需要进行选择,也可以按a键全部选择,本示例选择来全部上图是询问这个扩展需要的权限,因为这个扩展不只可以运行在chrome,还可以运行在firefox等其他一些浏览器上,有些权限是chrome浏览器不支持的,可以根据需要进行选择,也可以按a全选,然后后面再删除掉不支持的权限。这里为了简单先全选。然后接下来的一路按回车就可以了项目创建完毕三、运行项目编译项目npm run dev chrome可以看到编译生成了很多文件,生成的扩展目录是dist/chrome/用chrome把扩展加载起来打开chrome的扩展管理页面,打开开发者模式,加载已解压的扩展程序,选择刚才生成的dist/chrome/目录可以看到扩展已经加载了,但是有个错误按钮。点击这个按钮查看错误信息前面在选择权限的时候选择了所有权限,但是有些权限是chrome不支持的,因此需要把这些权限删掉扩展需要的页面、脚本、配置等都在app/目录里面,一般只需要修改里面的文件就可以了要修改权限,打开app/manifest.json文件,找到提示的几个权限全部删除掉重新加载一下扩展修改文件重新编译有点慢,多点几次应该就可以了随便打开一个页面,可以看到扩展已经成功运行了四、打包npm run build chrome运行完打包命令之后,会在packages/目录下面生成扩展的压缩包五、其他使用firefox加载扩展先进行编译npm run dev firefox用firefox进行加载firefox需要选中manifest.json文件可以看到扩展已经运行成功了六、附录Chrome扩展及应用开发【干货】Chrome插件(扩展)开发全攻略浏览器扩展generator-web-extension

December 22, 2018 · 1 min · jiezi

vue 实现音乐模块????,已添加到github,动起来

预览效果????Github地址基于vuejs、vuex歌曲接口基于 QQ音乐API,可以查看 QQ音乐播放地址 API

December 21, 2018 · 1 min · jiezi

如何做活动页面的滚动动画?让用户体验MAX的demo在这里!

本文由云+社区发表最近的一个活动页面需要做一个可以左右滑动的抽签效果,故通过用css的transform属性和js结合来模拟可以无限滚动的效果。先上效果:demo地址:https://kiroroyoyo.github.io/…实现过程1. 结构与样式结构:卡片分前后两排,每列插入10个div结点,以便做左右位移效果。样式:设置每一列都恰好好在中间位置(或中间位置附近),如下所示。a. 前排(cardFrond)相对于视口的初始位置(left:-255.5%;):b. 后排(backFrond)相对于视口的初始位置(left:-228.3%;):2. 无限滚动原理由于这里的停止位置是固定的,前排永远是当前卡片相对于视口居中,后排永远是两个卡片相对于视口居中,且每个卡片是一样的,所以当卡片列表向前或向右移动到一个目标位置时,都将列表重置为初始位置继续滚动。如下图以前排卡片为例:所以当滚动停止后会统一将列表样式设置为transform: translateX(0)。而对于用户这一操作是无感知的,认为已经滑动到了新的位置。3.滑动过程实现a. 目标位移与帧位移为了做出滑动后到停留位置的缓动效果,所以当用户左右滑动屏幕时,会记录滑动距离,计算出卡片该到的目标位移位置,目标位移位置是有规则的,因为这里有10张卡片均分宽度,位置必须是(100%/10)的整数倍,例如-40%、-30%、……40%,这样才能保证目标位置与初始位置相重合。目标位移代码片段onDocumentMouseUp : function(e){ //如果是点击事件 不设置移动 if (!this.fingerTouch) return; this.moveDirect = this.lon > 0 ? 1 : -1; this.transNum = this.lon/10 + this.moveDirect; this.lon = Math.round(this.transNum) * 10; this.fingerTouch = false; }记录了目标位移后,每一帧会以一定的帧位移不断靠近目标位移,使其在手指离开屏幕时仍有慢慢滑动到目标位置的缓动效果。此时需要判断当前位置是否大于40%或者小于-40%,若超过这个极限值需要重设目标位移及帧位移,使其在极限值内。animate: function(){ this.prePos += (this.lon - this.prePos) * 0.1; if (this.prePos > 40) { this.lon = this.lon - 40; this.prePos = this.prePos - 40; }else if (this.prePos < -40) { this.lon = this.lon + 40; this.prePos = this.prePos + 40; } //判断是否到达了目标位置 if (Math.abs(this.prePos - this.lon) < 0.01 && Math.abs(this.lon) > 0.01 && (!this.fingerTouch)) { this.ani_move = false; this.prePos = 0; this.frondCard.style = “transform: translateX("+ this.prePos +”%)"; this.backCard.style = “transform: translateX("+ this.prePos +”%)"; }else{ this.frondCard.style = “transform: translateX("+ this.prePos +”%)"; this.backCard.style = “transform: translateX("+ (-this.prePos) +”%)"; requestAnimationFrame(this.animate.bind(this)); } },b. 连续滑动判断当在上次滑动动画还未播放结束时用户又进行了第二次滑动时,需要执行一下操作: 1). 判断滑动时机处于上次滑动手指已离开屏幕但动画还未结束,此时需要记录两个flag,一个是ani_move,记录动画是否仍在进行,fingerTouch记录手指是否停留屏幕。 2). 判断第二次滑动是否与第一次不同方向,若不同向需重置上次帧位移为0。以免上次帧位移太大影响移动方向。1)与2)代码片段:if( this.ani_move && this.fingerTouch == false) { // 判断是否不同向 if (((e.clientX - prex) > 0 ? 1: -1) == -this.moveDirect ) { this.lon = 0; this.prePos = 0; this.moveDirect = -this.moveDirect; }}3). 取消第二次滑动时的动画播放和位移重置// 若是上次动画未结束不需要再次启动动画和重置目标位移if( this.ani_move && this.fingerTouch == false) {}else { this.lon = 0; cardAnimate.animate();}写在最后目前这个滑动效果只能针对卡片相同,停留位置固定的情况,因为需要做到位置重合。使用css transform来做无限滚动的效果,可以避免改变dom结点带来的页面重新布局。下图是chrome cpu6倍减速调试效果,没有触发layout,FPS基本维持在60左右。代码地址:https://github.com/kiroroyoyo…此文已由作者授权腾讯云+社区发布 ...

December 21, 2018 · 1 min · jiezi

HelloWorld-chrome扩展开发

本文介绍如何快速创建一个基于webpack构建的chrome扩展,由于chrome扩展开发概念很多,如果需要,请结合附录食用。首先需要安装好nodejs,推荐用nvs或其他工具管理node版本。这里用的是v8.5.0版本(之前用了v11.1.0在运行npm start的时候会报错,因为对chrome扩展来说node只是个构建工具,并且用nvs来管理node版本切换很方便,就不追究为什么报错了)。一、安装yeomannode -g install yo二、安装生成chrome扩展的generatornpm install -g generator-chrome-extension-kickstart三、使用yeoman创建项目# 创建一个空目录mkdir helloworld && cd helloworld# 运行yeoman生成项目yo chrome-extension-kickstart .然后按照提示进行项目配置上图是选择需要创建的UI actionNo:不创建Browser:浏览器级别的action,一个浏览器一个实例Page:页面级别的action,每个页面一个实例这里我们先选择Page上图是选择需要覆盖的浏览器的一些默认页面No:不需要覆盖任何默认页面Bookmarks Page:覆盖书签页面History Page:覆盖历史页面Newtab Page:覆盖新标签页面这里我们选择No上图是选择这个扩展需要的一些页面或者脚本Options Page:表示需要一个配置页面Devtools Page:表示在Devtools里需要新建一个页面Content Scripts:表示需要一个与所访问的页面一起运行的脚本Omnibox:表示给浏览器地址栏增加一个功能这里我们全部选择,都来演示一下。根据提示,按a全选上图是这个扩展需要的浏览器权限,比如Cookies就是扩展可以获取到cookie数据这里为了简单就按a全选了。接下来一路回车就可以了上图可以看出yeoman为我们生成了很多项目文件并且安装了依赖四、运行扩展启动项目编译npm start可以看到项目已经编译完生成了几个文件开另外一个命令行窗口编译生成的文件在dist/chrome/目录下,可以用浏览器把这个目录加载起来打开浏览器的扩展管理页面,打开开发者模式,然后加载已解压的扩展程序,选择编译生成的dist/chrome/目录然后发现扩展显示了一个错误按钮点击进去之后提示’input’ is not allowed for specified platform.,这个是因为我们之前选择权限的时候选择了全部,但是input权限不支持,我们需要删掉这个权限(或者也可以在选择权限的时候不勾选input)。打开app/manifest.json文件,把这一行删掉然后重新加载一下扩展,错误按钮就不见了可以看到扩展已经成功运行起来了五、进行开发项目主要文件在app/目录下面,一般只要修改这里面的文件就够了六、打包npm run build运行完上面的命令之后,会在packages/目录下生成zip包七、附录《Chrome扩展及应用开发》【干货】Chrome插件(扩展)开发全攻略

December 21, 2018 · 1 min · jiezi

AI如何赋能传统的物业公司?

近年来,人工智能在各国发展十分迅速,将成为第四次工业革命的核心技术驱动力。传统的物业公司借助人工智能这班车,产业升级指日可待。作为传统服务型的物业公司,面临着许多现实问题。管理模式陈旧和硬件设备落后,导致经营成本过高,服务品质提升受限。随着互联网应用的普及和人工智能时代的来临,传统企业如何才能完美蜕变,实现创新转型呢?用工成本逐年上升,是很多物业公司较为头疼的事情。选择合适的智能化产品来替代人工作业将成为必然趋势,比如:用无人驾驶扫地机器人代替传统的保洁员;用智能高楼外墙清洗机器人代替高危险的蜘蛛人。相比较来说,智能机器人不仅管理方便,操作简单,而且作业效率和质量也明显优于人工。在企业经营中融入科技元素,将会给企业发展带来不错的机遇。“智能清洁”的概念从20世纪末进入我国,经过近20年的发展,但在商业实践方面我国与欧美国家相比还差很多。发达国家的智能清洁已经发展到了细化分工并依靠智能清洁设备实现多场景应用的层面了,而我国的绝大多数物业公司还是比较传统的雇佣人工的形式,差距在于智能清洁技术的应用及普及上。随着中国人口红利的褪去,基于雇佣大量廉价劳动力的“人海战术”已经不能适用于现在的服务业了。现在的服务业已经呈现出了新的发展趋势,客户的服务需求量成倍增长,服务质量和服务体验要求越来越讲究品质。现实的情况也催促着物业公司改变服务模式,运用智能清洁思维可以使企业在人力结构优化和服务标准化方面更具有优势。在日常生活中,物业公司时常被舆论媒体妖魔化。看到社区业主论坛上多是对物业公司的批评和谩骂。一个物业公司要应付全小区的众多业主,确实免不了众口难调。但是物业和业主之间就只能是对立关系吗?物业公司失去了业主,就等于失去了市场,失去了立身之地;业主离开物业公司,就相当于花了几代人的血本买了一栋居住不舒适的房子。这么看来,物业和业主之间恰似鱼和水的关系,相伴相生。随着社会的发展,人们的生活发生着巨大的改变,物业管理领域值得有更深层面的思考。中国有13.7万家物业公司,如何运用互联网的方式更好地服务于业主?如何借助人工智能浪潮赋能物业公司?在这些领域都将催生巨大的商业机会,当然也将面临不小的挑战。在可预见的未来,传统的物业管理公司很可能将不复存在,取而代之的是以业主服务为核心的物业服务公司,这将成为主流。建立智能化、标准化的服务社区,让业主充分体验到物业公司转型后带来的品质居住享受,从而赋能物业公司,提高其在行业内的影响力和话语权。

December 19, 2018 · 1 min · jiezi

vue项目搭建以及全家桶的使用详细教程

前言vue是现阶段很流行的前端框架,很多人通过vue官方文档的学习,对vue的使用都有了一定的了解,但再在项目工程化处理的时候,却发现不知道改怎么更好的管理自己的项目,如何去引入一些框架以及vue全家桶其他框架的使用,以下将详细地介绍本人在处理工程文件构建的过程;对于刚开始解除vue的新手,建议使用官方脚手架vue-cli,当然,如果你对于webpack很熟悉,你也可以自己动手搭建自己的脚手架,当然如果你没把握的话,还是推荐使用vue-cli,能更好的帮助你搭建项目:步骤一、安装vue-cli首先,我们可以通过npm安装vue-clic,前提是我们需要有node环境,如果电脑还没安装node,先安装,可通过node -v查询node的版本号,有版本号则已经安装成功;接下来,我们需要确保电脑已经安装了webpack,webpack是一个包管理工具,也是vue-cli的构建工具,安装也很简单,全局安装只需要执行npm install webpack -g紧接着,开始我们vue-cli的安装npm install –global vue-cli查看是否安装成功,我们可以通过在cmd中输入vue -V 查看,如下图出现版本号则说明安装已经完成;我们可以打开c盘>用户>用户名>AppData>Roaming>npm查看我们全局安装的vue-cli,如下图:步骤二、构建工程文件安装完vue-cli后,我们可以通过在cmd中输入vue init webpack projectName生成webpack脚手架,在我们按下回车的时候,会出现一些提示问题,对应关系如下:项目名称(注意名称中不要出现大写字母,否则会报错)项目描述(可写可不写,看个人需要)作者(可写可不写,看个人需要)vue编译,这个选默认即可,运行加编译Runtime + Compiler是否安装vue-router是否安装vue路由工具是否使用代码管理工具ESLint管理你的代码后面几个是测试的工具,需要自己自行了解 ……紧接着,我们使用cd squareRoot 移动到文件夹squareRoot下,执行npm install初始化项目,安装package.json 文件中描述的依赖,初始化完成后,我们可以通过npm run dev运行我们的项目,这个时候,我们可以打开浏览器,输入http://localhost:8080/,可看到如下界面,说明我们的项目脚手架已经初始化完成;步骤三、项目结构解析虽然我们是通过vue-cli生成的项目结构,但还是希望读者能够清楚的知道每个文件的作用,这样对于我们学习该脚手架以及搭建自己的脚手架会有很好的帮助,如下图,是一级目录下的文件的作用:构建相关的代码主要是放在build文件夹和config文件夹下,包括了开发环境和生产环境,即dev和product,可以打开文件进行阅读,有接触过node的小伙伴应该可以很快读懂对应文件代码的作用,这里就不做详细的介绍了,需要注意的一点是,我们需要修改打包后文件的路径的时候,可以通过修改config文件夹下的index.js文件,如下图:这里,我们需要在src目录下新增一个page文件夹,用于存放页面相关的组件,而components存在的是公共的组件,这样做有利于我们更好的理解项目:步骤四、引入UI框架iView该步骤并不是一定要实现的,实际项目操作中,要根据具体需求而引入对应的UI框架或者不引入,鉴于指导的作用,在此处也做个示范,给与参考,可先阅读iVew官网学习;首先,我们应进行iView的安装,可利用npm包管理工具安装npm install iview –save安装成功后,我们要将对应的框架引入到项目中,这个时候,官网上有两种方法可以实现,第一种是直接在main.js中做如下配置:import Vue from ‘vue’import App from ‘./App’import router from ‘./router’import iView from ‘iview’;import ‘iview/dist/styles/iview.css’;Vue.config.productionTip = falseVue.use(iView);/* eslint-disable no-new /new Vue({ el: ‘#app’, router, components: { App }, template: ‘<App/>’})这种方式是一种全局引入的方式,引入后就在具体的页面或者组件内不需要再进行其他的引入,但缺点是无论是否需要该组件,都将全部引入,对于性能优化不是很好,这里推荐第二种用法,按需引入,这里需要借助插件babel-plugin-import实现按需加载组件,减小文件体积。首先需要安装,并在.babelrc中配置:npm install babel-plugin-import –save-dev// .babelrc{ “plugins”: [[“import”, { “libraryName”: “iview”, “libraryDirectory”: “src/components” }]]}然后这样按需引入组件,就可以减小体积了,这里需要注意的是,因为我们修改了.babelrc文件,这将导致我们第一种引入方法失效了,如果再使用那种方式引入,会导致代码报错;<template> <div class=“content”> <div class=“title”>患者接诊</div> <div> <Button type=“primary” shape=“circle” class=“btn-time”>临时保存</Button> <Button type=“primary” shape=“circle” class=“btn-cancel”>取消就诊</Button> <Button type=“primary” shape=“circle” class=“btn-done”>完成就诊</Button> </div> </div></template><script> import {Button} from ‘iview’ export default { name: “fHeader”, components:{ Button } }</script>运行结果如下图步骤五、vue-router的使用如果没有阅读过官方文档,建议大伙先阅读,官网上的教程已经足够详细,受益匪浅;学习的过程中,需要了解路由配置的基本步骤,命名规则,嵌套路由,路由传参,具名视图以及路由守卫,滚动行为和懒加载,这里我们就不一一详细介绍了,官网已有,我们这里是做构建是的配置和懒加载处理:首先,我们应该是安装vue-router,这个在我们生成项目的时候,已经将该依赖加载进来了,下一步要做的是在router文件下index.js进行配置:import Vue from ‘vue’import Router from ‘vue-router’Vue.use(Router)export default new Router({ scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } }, routes: [ { path:’/’, redirect:’/root’ }, { path: ‘/root’, name: ‘root’, components: { Left:() => import(’@/page/rootLeft.vue’), Middle: () =>import(’@/page/rootMiddle.vue’), Right: ()=>import(’@/page/rootRight.vue’) } } ]})上面的代码中,我们应用到了几个知识点,首先是滚动行文,这里我们配置了当路由跳转的时候,默认是滚动到(0,0)位置,即页面开始位置,其次我们用到的redirect是一个路由重定向的配置,接下来,在路由"/root"下,配置了具名视图,加载对应组件到对应视图,我们引入组件的方式使用到了箭头函数,这样写的目的是为了实现路由的懒加载,这样构建,只有在路由被执行的时候,才有引入对应的组件,对于页面性能的优化有很大的帮助;这里还需要注意的是,我们在引入的这些组件中,其实默认都是打包到一个文件下的,这样就会导致一次性引入的文件过大,为此,我们可以利用webapck打包工具,我们在build>webpack.base.conf.js文件下,增加如下代码,用于配置输出文件的模块名称,[name]是文件的名称,[chunkhash]是打包文件的哈希值,加上这个是为了将其作为版本号,以解决浏览器缓存机制带来的问题:然后在路由文件中引入组件的代码如下:{ path:"/test", name:“test”, component: ()=>import(/webpackChunkName:“test”/’@/page/test.vue’)}在引入组件的时候,加上/ webapckChunkName: “文件名” /,就这可以将对于的组件打包到指定名称的文件下,这样可以减少首次加载的文件的大小,对于一些没有联系的功能,比如不同页面,我们可以把对应的组件放在同一个文件,这样,既可以减少首次加载文件达大小,同时也可以将文件实现一个按需加载,提高页面性能;通过控制台,我们可以查看当前加载的文件资源,当我们点击测试按钮的时候,页面发生的跳转,这时候,我们会发现,在Network下,会加一条新的资源加载信息,这一条就是我们的分块打包后请求的资源;步骤六、全局过滤器filter和全局注册组件的引入写到这里的时候,可能很多人都会觉得,全局注册filter和全局组件组件不是很简单吗,直接Vue.filter()和Vue.component()不久解决了吗,其实这么讲也没错,但是你可曾想过,注册全组件是挂载在Vue对象下的,这意味这按照正常思路,我们要写在main.js文件下,这样就会造成,我们所写的mian文件过于冗长,你可以想一下,把全局的过滤器,和组件都写进去,着实丑陋,很不优雅,下面跟大家说一个优雅的实现方法:首先,我们在src>assets目录下新建一个js文件夹,再该文件夹下再创建一个filters.js的文件,如下图:接下来,我们在filters.js文件下写我们的全局过滤器,再将其抛出,写一个时间过滤器作为例子:const fullTime = val => { var dateObj = new Date(Number(val)); var year = dateObj.getFullYear(); var month = dateObj.getMonth() + 1 > 9 ? (dateObj.getMonth() + 1).toString() : “0” + (dateObj.getMonth() + 1).toString(); var date = dateObj.getDate() > 9 ? dateObj.getDate().toString() : “0” + dateObj.getDate().toString(); var hour = dateObj.getHours() > 9 ? dateObj.getHours().toString() : “0” + dateObj.getHours().toString(); var minutes = dateObj.getMinutes() > 9 ? dateObj.getMinutes().toString() : “0” + dateObj.getMinutes().toString(); return year + “/” + month + “/” + date + " " + hour + “:” + minutes;};module.exports={ fullTime}做完这一步,其实我们的过滤器还没写完,还需要在main.js中写一个注册函数:import Vue from ‘vue’import App from ‘./App’import router from ‘./router’import filters from ‘./assets/js/filters’import ‘iview/dist/styles/iview.css’;Object.keys(filters).forEach(key =>{ Vue.filter(key,filters[key])})Vue.config.productionTip = false/ eslint-disable no-new */new Vue({ el: ‘#app’, router, components: { App }, template: ‘<App/>’})这样,我们就把filters文件下的过滤器函数注册到Vue全局下,同样道理,我们可以按照同样的思路注册全局组件,我们在src>assets>js下新建一个components.js文件,在其中引入我们想注册的全局组件,export出一个对象,使用Object.keys获取后注册到全局下://components.js下import testInput from ‘@/components/testInput.vue’export default{ testInput:testInput}//main.js下import components from ‘./assets/js/components’Object.keys(components).forEach(key => { Vue.component(key,components[key])})优雅的注册全局组件和全局过滤器已经讲完,接下来就是API管理阶段了。步骤七、请求api管理这里我们使用axios发起异步的请求,安装很简单,npm install axios 即可,一开始的时候,我使用的是直接在每个组件内使用axios,到后面发现,但当我需要修改api接口的时候,需要查找的比较麻烦,只因为没有集中的对所有的api进行管理,而且每个请求回来的接口都需要写对应的报错处理,着实麻烦,这里我新建一个fecth文件夹并在其下新建一个api.js用来存放所有的axios处理和封装,://fetch/api.jsimport axios from ‘axios’export function fetch(url, params) { return new Promise((resolve, reject) => { axios.post(url, params).then( response => { resolve(response.data) } ).catch(error => { console.log(error) reject(error) }) })}getDefaultData=()=>{ return fetch(’/api/getBoardList’);}export default { getDefaultData}这样做的好处是集中化的管理了所有的api接口,当我们需要修改接口相关的代码,只需要在api.js中修改,包括路由修改以及路由拦截等,可读性更好;在不同的组件内,我们只需要把对应的接口用解构赋值的思想把它引入对应的组件内即可使用。import {getDefaultData} from ‘@/fetch/api.js’步骤八、代理服务器的配置这个功能主要是我们在调试接口的时候使用,因为当我们运行npm run dev 的时候,实际上我们的项目已经挂载在一个本地服务端运行了,端口号为我们配置的8080,当我们想在该项目下访问服务端接口数据的时候,就会产生跨域的问题,这个时候,我们就需要使用到proxy代理我们的数据请求,在vue-cli中已有配置相关的代码,我们仅需要把对应的代理规则写进去即可,这里以一个通用配置例子实现;首先,我们在fetch文件夹下新建一个config.js的文件,用于存放我们的代理路径配置:const url = ‘http://www.dayilb.com/';let ROOT;if (process.env.NODE_ENV === ‘production’) { //生产环境下的地址 ROOT = url;} else { //开发环境下的代理地址,解决本地跨域跨域,配置在config目录下的index.js dev.proxyTable中 ROOT = “/"}exports.PROXYROOT = url; //代理指向地址exports.ROOT = ROOT;接下来,我们要在config目录下新建一个proxyConfig.js,存放代理服务器的配置规则:var config= require(”../src/fetch/config");module.exports = { proxy: { [config.ROOT]: { //需要代理的接口,一般会加前缀来区分,但我个人是没加,即‘/’都转发代理 target: config.PROXYROOT, // 接口域名 changeOrigin: true, //是否跨域 pathRewrite: { [^/]: ’’ //需要rewrite的,针对上面的配置,是不需要的 } } }}最后,我们在config目录下的index.js文件中,引入我们的代理规则,并在,即var proxyConfig=require(’./proxyConfig’)…//省略号表示省略其他代码module.exports = {…proxyTable: proxyConfig.proxy,…}重新启动项目,我们就可以做到代理转发来实现跨域请求了。步骤九、vuex状态管理引入终于,来到了最后一步,那就是我们的状态管理vuex,其实这个东西不是说所有项目都需要引入,看项目的具体需求,但需要对同一个数据源进行大量的操作的时候,建议使用,如果每个组件的数据都可以轻易的在data中管理,那其实是没必要引进去的,该管理工具是更友好的解决了组件间传值的问题,包括了兄弟组件;首先,我们需要安装vuex,老规矩就是npm install vuex安装完成后,我们需要对我们的项目进行一些修改,首先是我们的目录,我们需要src下新增一个store文件夹作为vuex数据存放位置,在开始搭建前,我们需要有vuex的相关知识,我就不一一说明,自行百度一下vuex官方文档;众所周知,vuex有state,getter,mutation,action等关键属性,state主要是用于存放我们的原始数据结构,类似与vue的data,不过它是全局的,getter类似于计算属性computed,mutation主要用于触发修改state的行为,actions 也是一种触发动作,只不过与mutation的区别在于异步的操作我们只能在action中进行而不能在mutation中进行,目的是为了浏览器更好的跟踪state中数据的变化。接下来,我们来看一下store文件夹中都有什么:从上图可知,我创建了一个index.js入口文件,getters.js,mutation.js和mutationtypes.js,以及actions.js,下面我们先看看index.js的源码:import Vue from ‘vue’import Vuex from ‘vuex’import actions from ‘@/store/actions.js’import getters from ‘@/store/getters.js’import mutations from ‘@/store/mutations.js’Vue.use(Vuex)const state = { recipeList:[], currRecipe:0};if (module.hot) { // 使 action 和 mutation 成为可热重载模块 module.hot.accept([’./mutations’], () => { // 获取更新后的模块 // 因为 babel 6 的模块编译格式问题,这里需要加上 .default const newMutations = require(’./mutations’).default; // 加载新模块 store.hotUpdate({ mutations: newMutations, }) })}export default new Vuex.Store({ state, mutations, getters, actions})首先,我们把Vuex插件引入vue中,并新建了一个Vuex.Store()对象,其中各项属性值来自我们前面所创建的文件夹,中间module.hot是配置我们的action和mutation成为可热重载的模块,对于我们的调试更方便,当我们创建为Vuex.store对象后,我们还需要把它声明到main.js的页面Vue对象中import store from ‘./store/index’…new Vue({ el: ‘#app’, router, store, components: {App}, template: ‘<App/>’})在使用mutation的时候,我们是推荐大家把所有的行为常量保存到一个.js文件中,这样更有利于管理我们的项目,因为我们的mutation往往是需要使用action进一步封装的,这样我们在使用的时候,只需要修改常量对象里的属性值,就可以达到同时修改mutation和action的对应关系,一举两得,下面举例给大家参考://mutationType.jsexport default { ADD_NEW_RECIPT:‘ADD_NEW_RECIPT’, CHANGE_CURR_TAB:‘CHANGE_CURR_TAB’}//mutations.jsimport mutationTypes from ‘@/store/mutationTypes.js’const mutations = { [mutationTypes.ADD_NEW_RECIPT](state, item) { state.recipeList.push(item); }, [mutationTypes.CHANGE_CURR_TAB](state, index) { state.currRecipe=index; } };export default mutationsimport mutationTypes from ‘@/store/mutationTypes.js’const actions = { add_new_recipt:({commit,state}, type)=>{ commit(mutationTypes.ADD_NEW_RECIPT,type); }, change_curr_tab:({commit},index)=>{ commit(mutationTypes.CHANGE_CURR_TAB,index) }};export default actions从上面的例子可以看出,action和mutation使用的是同一个常量表,可以更好的管理我们的修改动作,而不会出现对不上的错误;最后,我们在组件内引入vuex中存放的state和action,如下import {mapActions, mapState} from ‘vuex’…computed: { …mapState({ recipeList: state => state.recipeList, currRecipe: state => state.currRecipe }) },methods: { …mapActions([ ‘add_new_recipt’, ‘change_curr_tab’ ]), addNewRecipt(type) { this.add_new_recipt(type) } }这里是推荐大家按照例子中,使用mapActions和mapState以及利用三点扩展符来引入state 和action,state最好存放在组件的computed 属性内,这样当state中的数据发生改变的时候,也会实时的修改computed里定义的变量值,来实现数据的绑定,同时,当我们修改了某些数据的时候,也要同步到state中去,这样数据源才可以保持一致性与准确性;总结写这个的时候,只是给个思路去搭建自己的工程文件,并不是说把所有相关知识点都讲一遍,需要有一定的相关知识,不过相信还没自己搭建过工程文件的小伙伴会不知道如何去安排,可以参考参考,这里推荐大家安装chrome的扩展插件Vue.js devtools,可以很有效的帮助我们追踪数据,定位错误。 ...

December 19, 2018 · 3 min · jiezi

历途机器人首次商业清洗告捷!

一年最热七八月,猛地从空调房出去,身体还真的不太适应。伴着湛蓝的天空、火热的烈日和偶遇的微风,户外还真像一个大蒸笼。就在8月份左右小编发现这栋楼和以前不一样了……别误会,不是被晒化了,而是被清洗的焕然一新了!这次高楼外墙清洗采用的是历途公司研发的第六代高楼外墙清洗机器人产品。只见机器人在外墙清洗作业前,工程师将专用清洁剂按照比例稀释后装入机身两侧的水箱,然后将机器人吊出女儿墙,一键启动后,机器人便牢牢地吸附在蓝色的玻璃上,开始进行智能行走清洗了。机器人从高处缓缓的下移,经过之处焕然一新,左侧清洗过的墙面与右侧未清洗的玻璃形成鲜明对比。这款机器人清洗时的效率约每小时120平米,按照工作10小时计算,一天可以清洗1200平米的面积,而且工作一小时仅需0.5度电,用5升水就能清洗100平米的楼宇外立面,既安全高效,又节能环保。在炎热夏日,早上八九点就已经非常热了,用人工清洗确实存在诸多不便。“蜘蛛人”顶着30几度的高温,拿着刮板和抹布清洗墙面,衣服很快就被汗水浸透了,很容易引起中暑及其他安全事故。用机器人代替“蜘蛛人”进行清洗作业,就可以有效解决这个问题。机器人具有更好的耐高温性,在炎炎烈日下依然可以正常工作,清洁速度相比人工更快,清洁效果更好,而且工作起来还不会疲劳。工作人员仅需简单的操作,就可以坐等清洗完成了。远远看去,机器人就像在高楼外墙上移动的小方块,如果采用多机联合作业时,想必一定会成为城市的一道风景线。一个个小机器人就像是高楼外墙上跳动的音符,演奏着清洁魔术。历途机器人此次商业清洗大获成功,特别鸣谢中铁集团的鼎力支持!

December 17, 2018 · 1 min · jiezi