
熟悉java
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
http://xidea.online
spring使用jpa进行update操作主要有两种方式: 1、调用保存实体的方法 1)保存一个实体:repository.save(T entity) 2)保存多个实体:repository.save(Iterable<T> entities) 3)保存并立即刷新一个实体:repository.saveAndFlush(T entity) 注:若是更改,entity中必须设置了主键字段,不然不能对应上数据库中的记录,变成新增(数据库自动生成主键)或报错(数据库不自动生成主键)了 2、@Query注解,自己写JPQL语句 使用JPA中@Query 注解实现update 操作,代码如下: @Transactional @Modifying(clearAutomatically = true) @Query(value = "update StockOut sc set sc.receivedPersonId=?1,sc.receivedPerson=?2,sc.receivedDate=?3 where stockOutCode=?4") int receipt(Long uid, String uname, Date createDate, String soCode); 备注:1.更新StockOut表下一些字段, 这里使用了不是原生的sql语句,所以不要加nativeQuery = true。 2.@Transactional 注解用于提交事务,若没有带上这句,会报事务异常提示。3.@Modifying(clearAutomatically = true) 自动清除实体里保存的数据。
拖延是个很伤脑筋的问题。 后台经常有人提问,明明心急如焚时间很紧,但还是忍不住玩游戏、看小说、刷朋友圈、追剧,心里忍不住骂自己真TM垃圾,Loser,甚至会忍不住抽自己两个耳光,但是,无济于事。 你一定有过类似的经历,设身处地想想,你是不是也特别痛恨那个时候的自己,或者,现在正在拖延的自己。 先别忙着否定自己,因为到这里,故事其实只是讲了一半,后面的故事是:最终,任务完成了。 譬如: 为期半年的毕设,拖了半年,内疚了半年,但最后两个礼拜居然神奇的搞定了! 为期一个月的项目,挣扎了28天,拖了28天,但最后两天居然神奇的完成了! 为期一个礼拜的演讲稿,前6天一字未动,内心如火一般纠结,但最后一天搞定! 久拖不决的事情,心急如焚还忍不住放纵,但通常到最后的最后,我们居然都神奇的搞定了。 之前的挫败、沮丧、纠结、挣扎,甚至是扇耳光都一扫而空,享受着难得的放松,心里忍不住沾沾自喜:这点时间就搞定了,还能有谁!!! 甚至忍不住幻想:如果一开始就认真准备,那该有多逆天! 但幻想只是幻想,现实是,你还只是一个普通人,一个经常被任务拖得精疲力尽的人。 你一定很奇怪,甚至很后悔: 为什么明明有能力,却总是把生活搞得一团糟? 为什么潜力很强大,但却无用武之地? 为什么遇到困难的任务,每次都先被拖延虐的体无完肤,但在最后短短时间却能踩着线搞定? 你肯定幻想过,如果一开始就认真准备,你肯定会有不一样的生活。 但这仍然只是一个幻想,事实是,就算给你再多的时间,你也用不起来!! 这要从deadline说起。 对于deadline,我们的理解其实很肤浅,deadline并不仅仅是最终节点。在我们心里,deadline其实有两个阶段: 完美deadline及最终deadline。 一、第一阶段的“完美deadline” 很多人经常痛恨自己,为什么心急如焚时间紧急还玩游戏? 确实,时间已经很紧急了,但这里的紧急,并非是无法完成任务,而是无法完美地完成任务,但心里还是不甘心,还是希望追求完美。 要想完美地完成任务,就必须要有完美的准备,完美的任务心态,完美的身心状态,低效、不在状态、没有准备好,怎么可能完成任务? 既然做不好,那还不如不做,这是隐藏最深的想法。 当你从心底觉得,自己此时此刻不能做好这个任务,你的内心就已经开始放弃了: 现实与理想,要做与不能做,放弃与自责,这些内心深处的纠结,会刺激你大脑的“痛苦中枢”,当大脑感到痛苦时,就会追求多巴胺刺激,通过多巴胺让自己缓解,感到快乐,那时那刻,游戏就是最好的选择。 这里的游戏,是一切让你很爽的事情,比如玩游戏,看小说,煲剧,刷朋友圈,等等,但你在玩之前,你一定会有一个很“站得住脚”的理由: 我现在太焦虑了,还没有准备好,不在状态,先去放松下状态,再回来全力以赴。 然而,这些有吸引力的东西,正常人都顶不住,会不知不觉沉迷,更别说处在焦虑、紧张、自责状态下,大脑本身就特别渴望多巴胺,当你开始这些事情,简直就是羊入虎口。 时间被吞噬的渣都不剩! 当你还在左右摇摆,纠结如何“完美”做完时,时间已经快到尽头了,绝对、肯定不可能完美了,这就到了我称之为第二阶段的“最终deadline”。 二、第二阶段的“最终deadline” 这是一个真正生死攸关的时刻,只有竭尽全力的做,才有可能做完! 在这种生死攸关面前,你会发现,“做不好就不如不做”这种矫情的心态,简直就是扯淡,生存还是死亡,这才是大问题。 要生存,要做完。我们全身心只有这一个目标,做完做完,哪怕是做的很烂,凑合着,对付着,将就着,也要把它做完! 凑合、对付、将就的,在以往看来很Low的心态,庸人心态,但就是在这种心态支配下,我们解放了!! 是的,就是解放,你不会再去担心,做不好怎么办,你不会再去自我监控,做的太烂了,不会再去自我怀疑,我真的能做好吗? 你全身心只有一个目标,所有的身心资源聚焦在一个目标,做做做做做!! 在在这种全身心的投入下,我们拖了很久,迟迟不能开始的任务,在“最终deadline”来临前的最后一刻,我们把它搞定了。 这就是一个完美主义者的心路历程!【你可能说,你不是完美主义者啊,或者觉得,完美主义者是个“罕见”的好词,你错了,“完美主义”就是这么常见的大路货色,你、我,或多或少都是完美主义者】 一幅图很容易概括这种心路历程: 为什么在完美deadline期间,心急如焚时间很紧,你却止步不前? 为什么在最终deadline期间,心急如焚时间很紧,你却能效率惊人? 答案就是:做好VS做完。 一字之差,千差万别! 同样一个任务,做完、做好的心理表征完全不同。 做完,意味着完成任务本身,是对任务真实难度的估计,而做好,意味着完美,意味着没有更好只有最好,是一个连你都无法清楚描述的绝对标准。 一张图可以清晰描述这两者区别: 完美的任务,令人打从心底里感到绝望! 打个比方,同样一块相当宽度的木板,放在地上,你能轻松通过,放在2米高悬空地方,你通过努力,辅以一定的时间,多尝试几次,也能够通过。 但如果放在两座摩天大厦之间,不论是给你1年,还是10年,你永远都迈不出第一步,更别说通过了! 这就是你面临的“完美”困境! So,为什么心急如焚时间很紧的人,反而更愿意选择游戏? 你很可能处在“完美deadline”,还对“完美”抱有幻想! 让你心急如焚时间紧急的不是做不完,而是再不努力,你无法“完美地”做完! 但是,“完美”本身就是一个黑洞,会吞噬掉你的一切资源,时间、努力、尝试,以及自信心,没有了精气神,你除了游戏又能如何呢?
1.二维码分类 二维条码也有许多不同的码制,就码制的编码原理而言,通常分为三种类型。 线性堆叠式二维码 编码原理: 建立在一维条码基础之上,按需要堆积成两行或多行。 图示: 矩阵式二维码 最常用编码,原理: 在一个矩形空间通过黑白像素在矩阵中的不同分布进行编码。在矩阵相应的位置上,用点(方点、圆点或其它形状)的出现表示二进制“1”,点的不出现表示二进制的“0” 图示: 邮政码 通过不同长度的条进行编码,主要用于邮政编码。 2.QR Code 现在最常用的就是这种,咱们现在主要介绍的也是这种。为啥这种使用二维码那么受反应呢?主要QR Code这种二维码有如下优点: 识读速度快 数据密度大 占用空间小 2.1 QR Code介绍 2.2 QR Code 结构 大家可以了解下二维码的结构,知道大概就行了,如果想了解详细信息的话可以自行百度,国家有详细的二维码规范。 3.后台JAVA代码实现二维码(QR Code)生成 这里介绍如下两种实现方式: Java 后台实现,主要使用zxing和qrcodejar等第三方jar包。 前端javascript实现,主要使用jquery.qrcode.js 3.1 使用zxing生成二维码 3.1.1 zxing相关网站 zxing的GitHubzxing的Java文档 3.1.2 生成zxing jar包 由于github上没有相关的jar包,所以需要我们自己生成一下,上面有好多关于android相关的,我们只需要选取核心包和javase这两部分代码。既下图矩形框内容: 生成方式我大致说下:首先在ecplise里新建一个java项目zxing,将刚才画框代码拷贝进去,然后导出jar包即可。如果你不想生成也可以在我的github上自行下载。 3.1.3 生成二维码代码 package cn.rivamed.zxing; import java.io.File; import java.nio.file.Path; import java.util.HashMap; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; public class CreateQRCode { public static void main(String[] args) { int width=300; int height=300; String format="png"; //这里如果你想自动跳转的话,需要加上https:// String content="https://github.com/hbbliyong/QRCode.git"; HashMap hits=new HashMap(); hits.put(EncodeHintType.CHARACTER_SET, "utf-8");//编码 //纠错等级,纠错等级越高存储信息越少 hits.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); //边距 hits.put(EncodeHintType.MARGIN, 2); try { BitMatrix bitMatrix=new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height,hits); //如果做网页版输出可以用输出到流 //MatrixToImageWriter.writeToStream(matrix, format, stream); Path path=new File("D:/zxingQRCode.png").toPath(); MatrixToImageWriter.writeToPath(bitMatrix, format, path); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("that is all"); } } 生成的结果如下: 由于代码都有详细注释,我就不一一讲解了,有疑问可以留言,我们一块探讨。 3.1.4 解析二维码代码 package cn.rivamed.zxing; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.HashMap; import javax.imageio.ImageIO; import com.google.zxing.Binarizer; import com.google.zxing.BinaryBitmap; import com.google.zxing.EncodeHintType; import com.google.zxing.LuminanceSource; import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatWriter; import com.google.zxing.NotFoundException; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.BitArray; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; public class ReadQRCode { public static void main(String[] args) { try { MultiFormatReader formatReader=new MultiFormatReader(); File file=new File("D:/zxingQRCode.png"); BufferedImage image=ImageIO.read(file); BinaryBitmap binaryBitmap=new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image))); HashMap hints=new HashMap(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8");//编码 Result result=formatReader.decode(binaryBitmap, hints); System.out.println("解析结果:"+result.toString()); System.out.println("二维码格式类型:"+result.getBarcodeFormat()); System.out.println("二维码文本"+result.getText()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 3.2 使用qrcode生成解析二维码 3.2.1 生成二维码 package cn.rivamed.qrcode; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.imageio.ImageIO; import com.swetake.util.Qrcode; public class CreateQRCode { public static void main(String[] args) throws IOException { Qrcode x=new Qrcode(); int version=7; x.setQrcodeErrorCorrect('M');//纠错等级 x.setQrcodeEncodeMode('B');//N代表数字,A代表a-Z,B代表其它(中文等) x.setQrcodeVersion(version);//版本号 String qrData="https://github.com/hbbliyong/QRCode.git"; //int width=300; int width=67+12*(version-1); //int height=300; int height=67+12*(version-1); BufferedImage bufferedImage=new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); Graphics2D gs=bufferedImage.createGraphics(); gs.setBackground(Color.WHITE); gs.setColor(Color.BLACK); gs.clearRect(0, 0, width, height); int pixoff=2;//偏移量,如果不加有可能会导致识别不准确 //如果有汉字需要加上编码 byte[] d=qrData.getBytes("gb2312"); //byte[] d=qrData.getBytes(); if(d.length>0&&d.length<120){ boolean[][] s=x.calQrcode(d); for(int i=0;i<s.length;i++){ for(int j=0;j<s.length;j++){ if(s[j][i]){ gs.fillRect(j*3+pixoff, i*3+pixoff, 3, 3); } } } } gs.dispose(); bufferedImage.flush(); ImageIO.write(bufferedImage, "png", new File("D:/qrcode.png")); } } 生成的结果如下: 这里需要注意的是,二维码长宽不能想zxing之直接定义,需要跟进这个公式生成67+12*(version-1)。比如我直接定义二维码的长宽为300.就会变成如下样子。这上面空白看的不是太清,你把图片下载下载下来看就比较明显了。 3.2.2 解析二维码 package cn.rivamed.qrcode; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import jp.sourceforge.qrcode.QRCodeDecoder; import jp.sourceforge.qrcode.data.QRCodeImage; public class ReadQRCode { public static void main(String[] args) throws IOException { File file=new File("D:/qrcode.png"); BufferedImage bufferedImage=ImageIO.read(file); QRCodeDecoder codeDecoder=new QRCodeDecoder(); String result=new String(codeDecoder.decode(new QRCodeImage() { @Override public int getWidth() { // TODO Auto-generated method stub return bufferedImage.getWidth(); } @Override public int getPixel(int arg0, int arg1) { // TODO Auto-generated method stub return bufferedImage.getRGB(arg0, arg1); } @Override public int getHeight() { // TODO Auto-generated method stub return bufferedImage.getHeight(); } }),"gb2312"); System.out.println(result); } } 4.前台代码jquery生成二维码 4.1 jquery.qrcode.js 的 GitHub 4.2 相关代码 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>二维码生成</title> <script type="text/javascript" src="<%=request.getContextPath() %>/js/jquery.min.js"></script> <script type="text/javascript" src="<%=request.getContextPath() %>/js/jquery.qrcode.min.js"></script> </head> <body> 生成的二维码如下:<br> <dir id="qrcode"></dir> <script type="text/javascript"> jQuery('#qrcode').qrcode('https://github.com/hbbliyong/QRCode.git'); </script> </body> </html> 5.结束语 所有的代码我都上传到了github上面,大家可以下载运行。这里面介绍的都比较基础的,但也包含了前端后台多种方式,对于简单的应用已经足够了。至于一些扩展,如果加上logo啊,电子名品啊,大家可以自行摸索。感谢您的观看,如果有什么疑问可以留言。 ps: 一个在线生成二维码的网站推荐:在线工具 这个工具也是使用的zxing
一.直接复制:(不推荐)方法:直接将硬盘上的jar包复制粘贴到项目的lib目录下即可。注意:1.对于导入的eclipse项目,该方式添加的jar包没有任何反应,用make编译项目会报错2.对于在idea中创建的项目,该方式添加jar包,编译不报错,但是打开Structure会有"...jar ... is missing ..."的提示,需要点击"Fix"或红灯泡解决。 二.通过Modules的Dependencies添加:(推荐)1.打开 File -> Project Structure (Ctrl + Shift + Alt + S) 2.单击 Modules -> Dependencies -> "+" -> "Jars or directories" 3.选择硬盘上的jar包4.Apply -> OK三.通过Libraries添加:(强烈推荐)1.打开 File -> Project Structure (Ctrl + Shift + Alt + S) 对于新导入的项目,会自动导入lib目录下的相关jar包,我们需要将这些自动添加进来的jar包删除,如图: 2.单击 Libraries -> "+" -> "Java" -> 选择我们导入的项目主目录,点击OK 注意:在弹出的方框中点击“Cancel”,取消将其添加到Module中 此时,lib目录创建成功,删除目录中添加进来的多余内容,重新添加需要的jar包 到此,Libraries创建成功。 3.回到Modules菜单,选中之前导入的项目,点击“Dependencies”,点击“+” -> “Library”,将刚才创建成功的Library目录加入 4.将不是项目lib目录下的其他jar包(如:tomcat相关jar包),也通过该方式加入 到此,所有jar包添加成功! 注意:1.图标的区别: 一种是添加项目lib目录下的jar包的图标,像文件夹; 一种是在其他目录添加的jar包,像柱状图。 2.以后要添加新的jar包,只要在Library目录中之前创建的项目lib目录下添加进去即可
在chrome浏览器的地址栏输入你想删除的网址的部分字幕,比如,在地址栏输入form,然后用键盘上的方向键定位到你想删除的那个错误的地址,如下图所示 然后在键盘上按 shift+del 组合键将其删除即可。
需要处理的字符串 [{columnDisplaySize=8, columnName=WARD_CODE, columnTypeName=varchar}, {columnDisplaySize=11, columnName=BED_NO, columnTypeName=int},[{columnDisplaySize=8, columnName=WARD_CODE, columnTypeName=varchar}, {columnDisplaySize=11, columnName=BED_NO, columnTypeName=int}]] 需求 需要在等号两边的字符串都加上双引号,并将等号转换成冒号,,使其成为标准的JSON字符串. 解决方案 String str="[{columnDisplaySize=8, columnName=WARD_CODE, columnTypeName=varchar}, {columnDisplaySize=11, columnName=BED_NO, columnTypeName=int}]"; String strS="(\\w+)\\s*=\\s*(\\w+)"; String strresult=str.replaceAll(strS,"\"$1\":\"$2\""); System.out.println(strresult
spring boot是个好东西,可以不用容器直接在main方法中启动,而且无需配置文件,方便快速搭建环境。可是当我们要同时启动2个springboot工程时,就会有问题,有可能会因为8080端口被第一个应用占用而导致第二个应用无法启动,这时就需要修改其中一个工程的启动端口。 1.可以通过实现EmbeddedServletContainerCustomizer接口来实现: public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(Application.class); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.setPort(8081); } } 2.可以通过application.properties配置文件来实现 server.port=8084
相信大部分使用Intellij的同学都会遇到这个问题,即使项目使用了spring-boot-devtools,修改了类或者html、js等,idea还是不会自动重启,非要手动去make一下或者重启, 就更没有使用热部署一样。出现这种情况,并不是你的配置问题,相信自己,热部署那几个设置很简单,其根本原因是因为Intellij IEDA和Eclipse不同,Eclipse设置了自动编译之 后,修改类它会自动编译,而IDEA在非RUN或DEBUG情况下才会自动编译(前提是你已经设置了Auto-Compile)。 废话这么多,怎么解决呢?请耐心往下看。 首先,IDEA设置里面这里必须打勾 然后 Shift+Ctrl+Alt+/,选择Registry 进去之后,找到如下图所示的选项,打勾 ok了,重启一下项目,然后改一下类里面的内容,IDEA就会自动去make了。
MySQL max_allowed_packet 设置过小导致记录写入失败 mysql根据配置文件会限制server接受的数据包大小。 有时候大的插入和更新会受max_allowed_packet 参数限制,导致写入或者更新失败。 查看目前配置 show VARIABLES like '%max_allowed_packet%'; 显示的结果为: +--------------------+---------+ | Variable_name | Value | +--------------------+---------+ | max_allowed_packet | 1048576 | +--------------------+---------+ 以上说明目前的配置是:1M 修改方法 1、修改配置文件 可以编辑my.cnf来修改(windows下my.ini),在[mysqld]段或者mysql的server配置段进行修改。 max_allowed_packet = 20M 如果找不到my.cnf可以通过 mysql --help | grep my.cnf 去寻找my.cnf文件。 Linux下该文件在/etc/下。 2、在mysql命令行中修改 在mysql 命令行中运行 set global max_allowed_packet = 2*1024*1024*10 然后退出命令行,重启mysql服务,再进入。 show VARIABLES like '%max_allowed_packet%'; 查看下max_allowed_packet是否编辑成功 注意:该值设置过小将导致单个记录超过限制后写入数据库失败,且后续记录写入也将失败。 三、如何重启Linux的mysql1、使用 service 启动:service mysqld restart2、使用 mysqld 脚本启动:/etc/inint.d/mysqld restart
错误:Host '127.0.0.1' is not allowed to connect to this MySQL server一般原因: MySQL数据库的配置文件my.ini中设置了参数: skip-name-resolve 从而导致使用“localhost”不能连接到数据库。解决方法: 注释掉: #skip-name-resolve 注释掉对数据库的设置略有影响,但影响不大。
1.隐藏没用到的文件 比如 IDEA 的项目配置文件(.iml 和.idea),打开 Settings-File Types, 加入要隐藏的文件后缀。 2.常用技巧 2.1 通过Alt+F8查看变量在当前断点的当前值 2.2 使用Live Template创建代码模板 2.3 粘贴 历史 复制 快捷键 Ctrl+Shift+V,可以显示文本复制的历史记录。 修改复制历史记录数量,执行 Setting-Editor,修改“Maximum number of contents to keep in clipboard” 2.4 查看 本地历史记录 选中文件或文件夹,右键 -> Local History -> Show History,显示本地历史记录。 2.5 Terminal 命令终端,使用快捷键:Alt+F12 命令行可以直接定位到代码所在目录,你可以通过git上传或者下载代码。 2.6 Search Anywhere 搜索所有文件,Shift 按两下。 2.7 文件夹搜索 快捷键 Ctrl+Shift+N,文件夹以/结束 3. 快捷键大全 3.1 编辑 快捷键 功能描述 Ctrl+Space 基本代码补全,输入字母按后列出匹配的词组 Ctrl+Shift+Space 智能代码补全,列出与预期类型一致的方法或变量 Ctrl+Alt+Space 补全类名 Ctrl+Shift+Ente 补全语句 Ctrl+P 显示方法参数 Ctrl+Q 显示注释文档 Shift+F1 显示外部文档 Ctrl+mouse over code 显示描述信息 Ctrl+F1 显示提示、警告、错误等信息 Alt+Insert 生成代码,生成 Getter、Setter、构造器等 Ctrl+O 重写父类方法 Ctrl+I 实现接口方法 Ctrl+Alt+T 使用(if..else, try..catch, for, synchronized 等)包围选中语句 Ctrl+/ 使用“//”注释或取消注释 Ctrl+Shift+/ 使用“/** **/”注释或取消注释 Ctrl+W 选择代码块,连续按会增加选择外层的代码块 Ctrl+Shift+W 与“Ctrl+W”相反,减少选择代码块 Alt+Q 显示类描述信息 Alt+Enter-fixes 显示快速修复列表 Ctrl+Alt+L 格式化代码 Ctrl+Alt+O 优化 Imports Ctrl+Alt+I 自动优化代码缩进 Tab/Shift+Tab 缩进代码/取消缩进代码 Ctrl+X or Shift+Delete 剪切代码,未选择代码时剪切当前行 Ctrl+C or Ctrl+Insert 复制代码,未选择代码时复制当前行 Ctrl+V or Shift+Insert 粘贴代码 Ctrl+Shift+V 粘贴最近复制的内容 Ctrl+D 重复代码,未选择代码时重复当前行 Ctrl+Y 删除行,未选择时删除当前行 Ctrl+Shift+J 合并多行为一行 Ctrl+Enter 分割一行为多行 Shift+Enter 使光标所在位置的下一行为新行 Ctrl+Shift+U 对选中内容进行大小写切换 Ctrl+Shift+]/[ 选中到代码块的开始/结束 Ctrl+Delete 删除从光标所在位置到单词结束位置的字符 Ctrl+Backspace 删除从单词起始位置到光标所在位置的字符 Ctrl+NumPad+/- 展开或收起代码块 Ctrl+Shift+NumPad+ 展开所有代码块 Ctrl+Shift+NumPad- 收起所有代码块 Ctrl+F4 关闭当前编辑页 3.2 查找/ 替换 快捷键 功能描述 Ctrl+F 查找 F3 查找下一个 Shift+F3 查找上一个 Ctrl+R 替换 Ctrl+Shift+F 目录内查找 Ctrl+Shift+R 目录内替换 Ctrl+Shift+S 语法模板搜索 Ctrl+Shift+M 语法模板替换 Alt+F7 查找被使用处 Ctrl+F7 查找当前文件中的使用处 Ctrl+Shift+F7 高亮当前文件中的使用处 Ctrl+Alt+F7 列出使用者 3.3 编译/ 运行 快捷键 功能描述 Ctrl+F9 Make 模块、项目 Ctrl+Shift+F9 编译选中的文件、模块、项目 Alt+Shift+F10 选择配置后运行代码 Alt+Shift+F9 选择配置后调试代码 Shift+F10 运行代码 Shift+F9 调试代码 Ctrl+F2 停止调试 Ctrl+Shift+F10 运行代码 3.4 调试 快捷键 功能描述 F8 单步调试,不进入函数内部 F7 单步调试,进入函数内部 Shift+F7 选择要进入的函数 Shift+F8 跳出函数 Alt+F9 运行到断点 Alt+F8 执行表达式查看结果 F9 继续执行,进入下一个断点或执行完程序 Ctrl+F8 设置/取消当前行断点 Ctrl+Shift+F8 查看断点 3.4 导航 快捷键 功能描述 Double Shift 查找所有 Ctrl+N 查找类 Ctrl+Shift+N 查找文件 Ctrl+Alt+Shift+N Go to symbol Alt+Right/Left 左右切换 Tab F12 回到上一个打开的窗口 Esc 焦点回到编辑器 Shift+Esc 隐藏打开的视图 Ctrl+Shift+F4 关闭当前 Tab Ctrl+G 跳到指定行 Ctrl+E 显示最近打开的文件 Ctrl+Alt+Left 跳到光标的上一个位置 Ctrl+Alt+Right 跳到光标的下一个位置 Ctrl+Shift+Backspace 跳到上一个编辑处 Alt+F1 选择当前文件显示在不同的视图中 Ctrl+B or Ctrl+Click 跳到类声明处 Ctrl+Alt+B 跳到实现类/方法 Ctrl+Shift+I 显示类/变量/方法定义 Ctrl+Shift+B 跳到类型定义处 Ctrl+U 跳到父类/方法 Alt+Up 光标移动到上一个方法 Alt+Down 光标移动到下一个方法 Ctrl+] 光标移动到代码块的起始位置 Ctrl+] 光标移动到代码块的结束位置 Ctrl+F12 显示文件结构 Ctrl+H 显示类层级 Ctrl+Shift+H 显示方法层级 Ctrl+Alt+H 显示类/方法调用层级 F2 光标移动到下一个错误 Shift+F2 光标移动到上一个错误 F4 编辑源码,光标移到编辑器内 Ctrl+Enter 查看源码,光标没移到编辑器内 Alt+Home 显示导航面包屑 F11 当前行设置书签 Shift+F11 显示所有书签 Ctrl+F11 设置书签号[0-9] Ctrl+[0-9] 跳到书签号[0-9]所在位置 3.5 重构 快捷键 功能描述 F5 复制类 F6 移动类 Alt+Delete 安全删除,删除前会提示调用处 Shift+F6 重命名 Ctrl+F6 重构方法参数、Exception 等 Ctrl+Alt+N 合并多行为一行 Ctrl+Alt+M 提取为新方法 Ctrl+Alt+V 提取为新变量 Ctrl+Alt+F 提取为对象新属性 Ctrl+Alt+C 提取为新静态常量 3.5 版本 控制/ 本地 历史 快捷键 功能描述 Ctrl+K 提交改动到 VCS Ctrl+T 从 VCS 上更新 Alt+Shift+C 查看最近的改动记录 Alt+BackQuote(`) 显示 VCS 操作列表 3.6 Live Template 快捷键 功能描述 Ctrl+Alt+J 使用 Live Template 包围选中代码 Ctrl+J 快速插入 Live Template iter 快速生成 for…in 语句 inst 快速生成”if instanceof”语句 itco 快速生成 iterator 的 for 循环 itit 快速生成 iterator 的 while 循环 itli 快速生成 list 的 for(i)循环 psf 快速生成“public static final” 语句 thr 快速生成“throw new” 语句 3.7 代码生成 快捷键 功能描述 Alt+0 聚焦到 Messages 窗口 Alt+1 聚焦到 Project 窗口 Alt+2 聚焦到 Favorite 窗口 Alt+3 聚焦到 Find 窗口 Alt+4 聚焦到 Run 窗口 Alt+5 聚焦到 Debug 窗口 Alt+6 聚焦到 TODO 窗口 Alt+7 聚焦到 Structure 窗口 Alt+8 聚焦到 Hierarchy 窗口 Alt+9 聚焦到 Change 窗口 Ctrl+S 保存文件 Ctrl+Alt+Y 与本地文件同步 Alt+Shift+F 添加到收藏夹 Alt+Shift+I 检查当前文件,包括 Javadoc 问题、可能存在的 bug 等 Ctrl+BackQuote (`) 模式切换,包括文本外观、快捷键、编辑器外观、代码样式。 Ctrl+Alt+S 打开 settings 窗口
Linux 常用的压缩命令有 gzip 和 zip,两种压缩包的结尾不同:zip 压缩的后文件是 *.zip ,而 gzip 压缩后的文件 *.gz 相应的解压缩命令则是 gunzip 和 unzip gzip 命令: # gzip test.txt 它会将文件压缩为文件 test.txt.gz,原来的文件则没有了,解压缩也一样 # gunzip test.txt.gz 它会将文件解压缩为文件 test.txt,原来的文件则没有了,为了保留原有的文件,我们可以加上 -c 选项并利用 linux 的重定向 # gzip -c test.txt > /root/test.gz 这样不但可以将原有的文件保留,而且可以将压缩包放到任何目录中,解压缩也一样 # gunzip -c /root/test.gz > ./test.txt zip 命令: # zip test.zip test.txt 它会将 test.txt 文件压缩为 test.zip ,当然也可以指定压缩包的目录,例如 /root/test.zip # unzip test.zip 它会默认将文件解压到当前目录,如果要解压到指定目录,可以加上 -d 选项 # unzip test.zip -d /root/
每次同步或者上传代码到githun上的代码库时,需要每次都输入用户名和密码,这时我们设置一下SSH key就可以省去这些麻烦了。若果使用TortoiseGit作为github本地管理工具,TortoiseGit使用扩展名为ppk的秘钥,而不是ssh-keygen生成的rsa密钥。也就是说使用ssh-keygen -C "username@email.com" -t rsa产生的密钥TortoiseGit中不能用。而基于github的开发必须要用到rsa密钥,因此需要用到TortoiseGit的putty key generator工具来生成既适用于github的rsa密钥也适用于TortoiseGit的ppk密钥。 方法/步骤 打开TortoiseGit下的PuttyGen,在打开的窗口中点击Generate按钮,会出现绿色进度条,等下生成,生成过程中可以多晃晃鼠标增加随机性。 生成之后复制生成的全部内容,窗口先留着不关闭。 登录到github,点击右上方的设置图表,进去设置页面之后选择左边选项中的SSH key之后点击Add SSH key在出现的界面中填写SSH key的名称,随便填写自己喜欢的即可,然后将刚刚复制的内容粘贴到key里面再点击add key就可以了。 返回到第二步的窗口,点击Save private key按钮保存为适用于TortoiseGit的私钥扩展名为.ppk。 运行TortoiseGit开始菜单中的Pageant程序,程序启动后将自动停靠在任务栏中,双击该图标,弹出key管理列表。 在弹出的key管理列表中点击add key,将第4步中保存的私钥(.ppk)文件加进来,关闭对话框即可。 经上述配置后,就可以使用TortoiseGit进行push、pull操作了,不用每次都输入密码了。
每次同步或者上传代码到githun上的代码库时,需要每次都输入用户名和密码,这时我们设置一下SSH key就可以省去这些麻烦了。若果使用TortoiseGit作为github本地管理工具,TortoiseGit使用扩展名为ppk的秘钥,而不是ssh-keygen生成的rsa密钥。也就是说使用ssh-keygen -C "username@email.com" -t rsa产生的密钥TortoiseGit中不能用。而基于github的开发必须要用到rsa密钥,因此需要用到TortoiseGit的putty key generator工具来生成既适用于github的rsa密钥也适用于TortoiseGit的ppk密钥。 方法/步骤 打开TortoiseGit下的PuttyGen,在打开的窗口中点击Generate按钮,会出现绿色进度条,等下生成,生成过程中可以多晃晃鼠标增加随机性。 生成之后复制生成的全部内容,窗口先留着不关闭。 登录到github,点击右上方的设置图表,进去设置页面之后选择左边选项中的SSH key之后点击Add SSH key在出现的界面中填写SSH key的名称,随便填写自己喜欢的即可,然后将刚刚复制的内容粘贴到key里面再点击add key就可以了。 返回到第二步的窗口,点击Save private key按钮保存为适用于TortoiseGit的私钥扩展名为.ppk。 运行TortoiseGit开始菜单中的Pageant程序,程序启动后将自动停靠在任务栏中,双击该图标,弹出key管理列表。 在弹出的key管理列表中点击add key,将第4步中保存的私钥(.ppk)文件加进来,关闭对话框即可。 经上述配置后,就可以使用TortoiseGit进行push、pull操作了,不用每次都输入密码了。
在Ubuntu下面安装Visual Studio Code sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make sudo apt-get update sudo apt-get install ubuntu-make umake web visual-studio-code
ubuntu 安装jdk 的两种方式: 1:通过ppa(源) 方式安装. 2:通过官网下载安装包安装. 这里推荐第1种,因为可以通过 apt-get upgrade 方式方便获得jdk的升级 使用ppa/源方式安装 1.添加ppa sudo add-apt-repository ppa:webupd8team/java sudo apt-get update 2.安装oracle-java-installer jdk8 sudo apt-get install oracle-java8-installer 安装器会提示你同意 oracle 的服务条款,选择 ok 然后选择yes 即可 如果你懒,不想自己手动点击.也可以加入下面的这条命令,默认同意条款: JDK8 默认选择条款 echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections 接下会是等待(依个人网速定) 如果你因为防火墙或者其他原因,导致installer 下载速度很慢,可以中断操作.然后下载好相应jdk的tar.gz 包,放在: /var/cache/oracle-jdk8-installer (jdk8) 下面,然后安装一次installer. installer 则会默认使用 你下载的tar.gz包 3.设置系统默认jdk JDK8 sudo update-java-alternatives -s java-8-oracle 如果即安装了jdk7,又安装了jdk8,要实现两者的切换,可以: jdk8 切换到jdk7 sudo update-java-alternatives -s java-7-oracle jdk7 切换到jdk8 sudo update-java-alternatives -s java-8-oracle 4.测试jdk 是是否安装成功: java -version javac -version 直接下载jdk压缩包方式安装(这里只介绍jdk7的,jdk8 的原理完全一致) 分为下面5个步骤 1.官网下载JDK 2.解压缩,放到指定目录 3.配置环境变量 4.设置系统默认JDK 5. 测试jdk 1.官网下载JDK 地址: http://www.oracle.com/technetwork/articles/javase/index-jsp-138363.html 选择相应的 .gz包下载 2. 解压缩,放到指定目录(以jdk-7u60-linux-x64.gz为例) 创建目录: sudo mkdir /usr/lib/jvm 加压缩到该目录: sudo tar -zxvf jdk-7u60-linux-x64.gz -C /usr/lib/jvm 3.修改环境变量: sudo vim ~/.bashrc 文件的末尾追加下面内容: #set oracle jdk environmentexport JAVA_HOME=/usr/lib/jvm/jdk1.7.0_60 ## 这里要注意目录要换成自己解压的jdk 目录 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH 使环境变量马上生效 source ~/.bashrc 4.设置系统默认jdk 版本 sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.7.0_60/bin/java 300 sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.7.0_60/bin/javac 300 sudo update-alternatives --install /usr/bin/jar jar /usr/lib/jvm/jdk1.7.0_60/bin/jar 300 sudo update-alternatives --install /usr/bin/javah javah /usr/lib/jvm/jdk1.7.0_60/bin/javah 300 sudo update-alternatives --install /usr/bin/javap javap /usr/lib/jvm/jdk1.7.0_60/bin/javap 300 然后执行: sudo update-alternatives --config java 若是初次安装jdk,会有下面的提示 There is only one alternative in link group java (providing /usr/bin/java): /usr/lib/jvm/jdk1.7.0_60/bin/java 否者,选择合适的jdk 5.测试jdk java -versionjava version "1.7.0_60" Java(TM) SE Runtime Environment (build 1.7.0_60-b19) Java HotSpot(TM) 64-Bit Server VM (build 24.60-b09, mixed mode) jdk 安装成功 ubuntu 两种下安装jdk7 jdk8 的方式介绍完毕 参考文章: 1. http://www.webupd8.org/2012/01/install-oracle-java-jdk-7-in-ubuntu-via.html 2. http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html
Git一般有很多分支,我们clone到本地的时候一般都是master分支,那么如何切换到其他分支呢?主要命令如下: 1. 查看远程分支 $ git branch -a 我在mxnet根目录下运行以上命令: ~/mxnet$ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master remotes/origin/nnvm remotes/origin/piiswrong-patch-1 remotes/origin/v0.9rc1 可以看到,我们现在在master分支下 2. 查看本地分支 ~/mxnet$ git branch * master 3. 切换分支 $ git checkout -b v0.9rc1 origin/v0.9rc1 Branch v0.9rc1 set up to track remote branch v0.9rc1 from origin. Switched to a new branch 'v0.9rc1' #已经切换到v0.9rc1分支了 $ git branch master * v0.9rc1 #切换回master分支 $ git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'.
Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置。 github的SSH配置如下: 一 、 设置Git的user name和email: $ git config --global user.name "xuhaiyan" $ git config --global user.email "haiyan.xu.vip@gmail.com" 二、生成SSH密钥过程:1.查看是否已经有了ssh密钥:cd ~/.ssh如果没有密钥则不会有此文件夹,有则备份删除2.生存密钥: $ ssh-keygen -t rsa -C “haiyan.xu.vip@gmail.com”按3个回车,密码为空。 Your identification has been saved in /home/tekkub/.ssh/id_rsa.Your public key has been saved in /home/tekkub/.ssh/id_rsa.pub.The key fingerprint is:……………… 最后得到了两个文件:id_rsa和id_rsa.pub 3.添加密钥到ssh:ssh-add 文件名需要之前输入密码。4.在github上添加ssh密钥,这要添加的是“id_rsa.pub”里面的公钥。 打开https://github.com/ ,登陆xuhaiyan825,然后添加ssh。 5.测试:ssh git@github.com The authenticity of host ‘github.com (207.97.227.239)’ can’t be established.RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added ‘github.com,207.97.227.239′ (RSA) to the list of known hosts.ERROR: Hi tekkub! You’ve successfully authenticated, but GitHub does not provide shell accessConnection to github.com closed. 三、 开始使用github1.获取源码: $ git clone git@github.com:billyanyteen/github-services.git 2.这样你的机器上就有一个repo了。3.git于svn所不同的是git是分布式的,没有服务器概念。所有的人的机器上都有一个repo,每次提交都是给自己机器的repo仓库初始化: git init 生成快照并存入项目索引: git add 文件,还有git rm,git mv等等…项目索引提交: git commit 4.协作编程:将本地repo于远程的origin的repo合并,推送本地更新到远程: git push origin master 更新远程更新到本地: git pull origin master 补充:添加远端repo: $ git remote add upstream git://github.com/pjhyett/github-services.git 重命名远端repo: $ git://github.com/pjhyett/github-services.git为“upstream”
在这篇文章中,我将分享12个非常有用的JavaScript技巧。这些技巧可以帮助你减少并优化代码。 1) 使用!!将变量转换成布尔类型 有时,我们需要检查一些变量是否存在,或者它是否具有有效值,从而将它们的值视为true。对于做这样的检查,你可以使用||(双重否定运算符),它能自动将任何类型的数据转换为布尔值,只有这些变量才会返回false:0,null,"",undefined或NaN,其他的都返回true。我们来看看这个简单的例子: Js代码 function Account(cash) { this.cash = cash; this.hasMoney = !!cash; } var account = new Account(100.50); console.log(account.cash); // 100.50 console.log(account.hasMoney); // true var emptyAccount = new Account(0); console.log(emptyAccount.cash); // 0 console.log(emptyAccount.hasMoney); // false 在这个例子中,如果account.cash的值大于零,则account.hasMoney的值就是true。 2) 使用+将变量转换成数字 这个转换超级简单,但它只适用于数字字符串,不然就会返回NaN(不是数字)。看看这个例子: Js代码 function toNumber(strNumber) { return +strNumber; } console.log(toNumber("1234")); // 1234 console.log(toNumber("ACB")); // NaN 这个转换操作也可以作用于Date,在这种情况下,它将返回时间戳: Js代码 console.log(+new Date()) // 1461288164385 3) 短路条件 如果你看到过这种类似的代码: Js代码 if (conected) { login(); } 那么你可以在这两个变量之间使用&&(AND运算符)来缩短代码。例如,前面的代码可以缩减到一行: Js代码 conected && login(); 你也可以用这种方法来检查对象中是否存在某些属性或函数。类似于以下代码: Js代码 user && user.login(); 4) 使用||设置默认值 在ES6中有默认参数这个功能。为了在旧版浏览器中模拟此功能,你可以使用||(OR运算符),并把默认值作为它的第二个参数。如果第一个参数返回false,那么第二个参数将会被作为默认值返回。看下这个例子: Js代码 function User(name, age) { this.name = name || "Oliver Queen"; this.age = age || 27; } var user1 = new User(); console.log(user1.name); // Oliver Queen console.log(user1.age); // 27 var user2 = new User("Barry Allen", 25); console.log(user2.name); // Barry Allen console.log(user2.age); // 25 5) 在循环中缓存array.length 这个技巧非常简单,并且在循环处理大数组时能够避免对性能造成巨大的影响。基本上几乎每个人都是这样使用for来循环遍历一个数组的: Js代码 for (var i = 0; i < array.length; i++) { console.log(array[i]); } 如果你使用较小的数组,那还好,但是如果处理大数组,则此代码将在每个循环里重复计算数组的大小,这会产生一定的延迟。为了避免这种情况,你可以在变量中缓存array.length,以便在循环中每次都使用缓存来代替array.length: Js代码 var length = array.length; for (var i = 0; i < length; i++) { console.log(array[i]); } 为了更简洁,可以这么写: Js代码 for (var i = 0, length = array.length; i < length; i++) { console.log(array[i]); } 6) 检测对象中的属性 当你需要检查某些属性是否存在,避免运行未定义的函数或属性时,这个技巧非常有用。如果你打算编写跨浏览器代码,你也可能会用到这个技术。例如,我们假设你需要编写与旧版Internet Explorer 6兼容的代码,并且想要使用document.querySelector()来通过ID获取某些元素。 但是,在现代浏览器中,这个函数不存在。所以,要检查这个函数是否存在,你可以使用in运算符。看下这个例子: Js代码 if ('querySelector' in document) { document.querySelector("#id"); } else { document.getElementById("id"); } 在这种情况下,如果在document中没有querySelector函数,它就会使用document.getElementById()作为代替。 7) 获取数组的最后一个元素 Array.prototype.slice(begin,end)可以用来裁剪数组。但是如果没有设置结束参数end的值的话,该函数会自动将end设置为数组长度值。我认为很少有人知道这个函数可以接受负值,如果你将begin设置一个负数的话,你就能从数组中获取到倒数的元素: Js代码 var array = [1, 2, 3, 4, 5, 6]; console.log(array.slice(-1)); // [6] console.log(array.slice(-2)); // [5,6] console.log(array.slice(-3)); // [4,5,6] 8) 数组截断 这个技术可以锁定数组的大小,这对于要删除数组中固定数量的元素是非常有用的。例如,如果你有一个包含10个元素的数组,但是你只想获得前五个元素,则可以通过设置array.length = 5来阶段数组。看下这个例子: Js代码 var array = [1, 2, 3, 4, 5, 6]; console.log(array.length); // 6 array.length = 3; console.log(array.length); // 3 console.log(array); // [1,2,3] 9) 全部替换 String.replace()函数允许使用String和Regex来替换字符串,这个函数本身只能替换第一个匹配的串。但是你可以在正则表达式末尾添加/g来模拟replaceAll()函数: Js代码 var string = "john john"; console.log(string.replace(/hn/, "ana")); // "joana john" console.log(string.replace(/hn/g, "ana")); // "joana joana" 10) 合并数组 如果你需要合并两个数组,你可以使用Array.concat()函数: Js代码 var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.concat(array2)); // [1,2,3,4,5,6]; 但是,这个函数对于大数组来说不并合适,因为它将会创建一个新的数组并消耗大量的内存。在这种情况下,你可以使用Array.push.apply(arr1,arr2),它不会创建一个新数组,而是将第二个数组合并到第一个数组中,以减少内存使用: Js代码 var array1 = [1, 2, 3]; var array2 = [4, 5, 6]; console.log(array1.push.apply(array1, array2)); // [1,2,3,4,5,6]; 11) 把NodeList转换成数组 如果运行document.querySelectorAll("p")函数,它会返回一个DOM元素数组,即NodeList对象。但是这个对象并没有一些属于数组的函数,例如:sort(),reduce(),map(),filter()。为了启用这些函数,以及数组的其他的原生函数,你需要将NodeList转换为数组。要进行转换,只需使用这个函数:[] .slice.call(elements): Js代码 var elements = document.querySelectorAll("p"); // NodeList var arrayElements = [].slice.call(elements); // 现在已经转换成数组了 var arrayElements = Array.from(elements); // 把NodeList转换成数组的另外一个方法 12) 对数组元素进行洗牌 如果要像外部库Lodash那样对数据元素重新洗牌,只需使用这个技巧: Js代码 var list = [1, 2, 3]; console.log(list.sort(function() { return Math.random() - 0.5 })); // [2,1,3] 结论 现在,你已经学到了一些有用的JS技巧,它们主要用于缩减JavaScript代码量,其中一些技巧在许多流行的JS框架都使用到,如Lodash,Underscore.js,Strings.js等。如果你知道其他JS技巧,欢迎分享!
WebJars能使Maven的依赖管理支持OSS的JavaScript库/CSS库,比如jQuery、Bootstrap等。 (1)添加js或者css库 pom.xml Xml代码 <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7-1</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.1</version> </dependency> src/main/resources/static/demo.html Html代码 <html> <head> <script src="/webjars/jquery/3.1.1/jquery.min.js"></script> <script src="/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js"></script> <title>WebJars Demo</title> <link rel="stylesheet" href="/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css" /> </head> <body> <div class="container"><br/> <div class="alert alert-success"> <a href="#" class="close" data-dismiss="alert" aria-label="close">×</a> Hello, <strong>WebJars!</strong> </div> </div> </body> </html> 启动应用后可以看到以下log: 引用 2017-02-09 13:52:48.117 INFO 6188 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 启动应用访问 http://localhost:8080/demo.html (2)省略版本号 很少在代码中硬编码版本号,所以需要隐藏它。 pom.xml添加webjars-locator org.springframework.web.servlet.resource.WebJarsResourceResolver Java代码 <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <version>0.31</version> </dependency> src/main/resources/static/demo.html 引用 <script src="/webjars/jquery/3.1.1/jquery.min.js"></script> <script src="/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js"></script> <title>WebJars Demo</title> <link rel="stylesheet" href="/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css" /> -> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/bootstrap/js/bootstrap.min.js"></script> <title>WebJars Demo</title> <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css" /> 启动应用再次访问 http://localhost:8080/demo.html 结果和上边一样。 引入的开源JavaScript库/CSS库将会以jar的形式被打包进工程! spring-boot-demo1-0.0.1-SNAPSHOT.jar\BOOT-INF\lib 引用 bootstrap-3.3.7-1.jar └─ META-INF └─ resources └─ webjars └─ bootstrap └─ 3.3.7-1 ├─ css | ├─ bootstrap.min.css | ├─ bootstrap.min.css.gz # Gzip文件 ... 引用 jquery-3.1.1.jar └─ META-INF └─ resources └─ webjars └─ jquery └─ 3.1.1 ├─ jquery.min.js ...
问题描述 当使用spring Boot来架设服务系统时,有时候也需要用到前端页面,当然就不可或缺地需要访问其他一些静态资源,比如图片、css、js等文件。那么如何设置Spring Boot网站可以访问得到这些静态资源,以及静态资源如何布局? 解决方案 这里引用stackoverflow网站的问题截图:[http://stackoverflow.com/questions/27381781/java-spring-boot-how-to-map-my-my-app-root-to-index-html] 以及config/WebConfig.Java的内容如下: @Configuration @EnableWebMvc @ComponentScan public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("/"); } } 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 常见问题 官方的解说 最常见的就是官方给出的方案:http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html 具体位置在26.1.4 Static Content 但是经过检验 src/main/resources目录下的资源文件不能被直接访问到。图片说明如下: 解释 其实官方解释没有提及一点,就是不能使用@EnableWebMvc,当然如果Spring Boot在classpath里看到有 spring webmvc 也会自动添加@EnableWebMvc (http://spring.io/guides/gs/rest-service/) 如果@EnableWebMvc了,那么就会自动覆盖了官方给出的/static, /public, META-INF/resources, /resources等存放静态资源的目录。而将静态资源定位于src/main/webapp。当需要重新定义好资源所在目录时,则需要主动添加上述的那个配置类,来Override addResourceHandlers方法。
更换IDEA主题只需要3步 1. 下载主题 在主题网站上IDEA Color Themes 上浏览喜欢的主题并下载该主题。(如果网址有变更,google IDEA themes即可。) 2. 导入主题 在IDEA中导入该主题,具体如下: file –> import setttings –> 选中 1 中下载的主题jar文件 –> 一路确认 –> 重启 3. 设置主题 设置主题: 点击 settings –> editor –> colors & fonts –> general 如下图在该窗口右侧区域的scheme下拉菜单中选中导入的主题名称,点击确认即可。
打开powerdesigner,选择File --> Reverse Engineer --> Database…… Model name是模型名称,DBMS选MySQL 5.0 然后确定 在Selection选项卡中选:Using scripts files:添加刚才的导出的sql脚本(wpblog.sql) Options选项卡中的File Encoding 选择:UTF8+QuickDetect, 应该导入进来了。
1、把目录设置成为层级结构显示、和eclipse类似 去掉flatten Packages前面的勾 在项目中创建多级包的时候要注意,必须在Java下建,并且要全输入才能识别
mvn clean install -e -U -e详细异常,-U强制更新
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title><script src="js/jquery.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $(function(){ function preView(preDIV){ var files=preDIV.files; for (var i = 0; i < files.length; i++) { var data=URL.createObjectURL(files[i]); $('<img class="img-item" src="'+data+'" />').appendTo( $("#upload") ); } } $(':file').change(function(){console.log(this); preView(this); }); }); </script> </head> <body> <div id="upload"> </div> <input type="file" value="选择图片" multiple="multiple"/> </body> </html>
Ubuntu16.04安装后1.安装常用软件搜狗输入法+编辑器Atom+浏览器Chome+视频播放器vlc+图像编辑器GIMP Image Editor安装+视频录制软件RcordMyDesktop安装.2.开发环境配置.JDK环境配置+Scala环境配置+nodejs环境配置+开发工具intellij IDEA安装+Python数据分析环境配置+Jupyter开发工具安装+Python多版同时支持. 1.Ubuntu16.04安装常用软件(搜狗输入法+编辑器Atom+浏览器Chome+视频播放器vlc+视频录制软件RcordMyDesktop) 1.1.安装搜狗输入法 a安装ficx输入法和可视化配置工具 wxl@wxl-pc:~$ sudo apt-get install fcitx wxl@wxl-pc:~$ sudo apt-get install fcitx-config-common wxl@wxl-pc:~$ sudo apt-get install fcitx-config-gtk 去搜狗官网下载linux版输入法 sudo dpkg -i /home/wxl/Downloads/sogoupinyin_2.0.0.0078_amd64.deb 更新 sudo apt-get upgrade -f 选择 语言支持 中选择fcitx(system settings – langure superter) 在fcitx配置中选择sougo输入法(或者搜索框中打开fcitx configuration)如图去除 Only Show Current Language“选项”,去掉这个对勾,才能搜到Sogou Pinyin 1.2.安装atom setup 下载atom的.deb安装包 首先包安装,会提示安装依赖 sudo dpkg -i /home/wxl/Downloads/atom-amd64.deb 安装所需依赖 sudo apt-get -f install 再次包安装即可成功,在dashboard中搜atom可以看到,已经成功安装了。 sudo dpkg -i /home/wxl/Downloads/atom-amd64.deb 1.3.安装chome sudo dpkg -i /home/wxl/Downloads/google-chrome-stable_current_amd64.deb sudo apt-get -f install sudo dpkg -i /home/wxl/Downloads/google-chrome-stable_current_amd64.deb 1.4.安装vlc播放器 sudo apt-get install vlc 1.5.GIMP Image Editor安装 类似与photoshop,用于图片的编辑。 直接在应用商店Ubuntu Software中搜索安装。 1.6.命令总结 #查看想要安装的包,如查看带有fcitx的所有包 wxl@wxl-pc:~$ sudo apt-cache search fcitx #安装已经下载好的deb包,如安装谷歌浏览器 wxl@wxl-pc:~$ sudo dpkg -i /home/wxl/Downloads/google-chrome-stable_current_amd64.deb #安装ubuntu自带源中有的包 wxl@wxl-pc:~$ sudo apt-get install 软件名字 #系统(或依赖)更新 wxl@wxl-pc:~$ ssudo apt-get -f install 2.Ubuntu16.04配置开发环境(JDK环境配置+scala环境配置+nodejs环境配置+开发工具intellij IDEA+数据分析Python环境+开发工具Jupyter) 2.0. Ubuntu三种级别的环境变量配置 2.0.1 临时变量,即在退出terminal后便会失效。 export PATH=${PATH} 2.0.2 单一用户变量,相当于windows的“用户变量” vim ~/.bashrc 2.0.3 系统变量,相当于windows的”系统变量”–提示千万别修改环境变量! vim /etc/environment Tip:Ubuntu修改了environment无法进入系统 按ctrl+alt+F1进入命令提示符模式 输入用户名和密码 /usr/bin/sudo /usr/bin/vi /etc/environment 修改回来 PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" 重启,即可 /usr/bin/sudo reboot Tip:CentOS下的全局环境变量文件为:/etc/profile,单一用户环境变量文件为:~/.bash_profile 2.1. JDK环境配置(系统环境变量) 解压,并放置/opt/Java路经下 cd /homw/wxl/Downloads tar -zxf jdk-8u92-linux-x64.tar.gz sudo mv jdk1.8.0_92 /opt/java 配置系统变量 sudo vim /etc/profile.d/java.sh #增加内容 export JAVA_HOME=/opt/java export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH source变量生效,在当前terminal下生效 source /etc/profile 或者logout/用户注销.登陆 这样使得用户变量在当前用户下生效 2.2. Scala环境配置 解压,移动到/opt cd /home/wxl/Downloads tar -zxf scala-2.11.8.tgz sudo mv scala-2.11.8 /opt/scala scala环境变量配置 sudo vim /etc/profile.d/scala.sh #增加内容 export SCALA_HOME=/opt/scala export PATH=${SCALA_HOME}/bin:$PATH source变量生效,在当前terminal下生效 source /etc/profile 查看scala -version 2.3. 安装Intellj IDEA tar -zxf ideaIU-2016.2.tar.gz sudo mv idea-IU-162.1121.32 idea 启动安装脚本在/bin目录下 ./idea.sh 开始通过界面安装 选择在线激活 添加开启图标 选择安装支持scala的插件 安装最后需要给予权限 安装完成 2.4. 安装nodejs 下载最新版nodejs 解压,并放置在/opt路径下 tar -xf node-v6.3.0-linux-x64.tar.xz 1 1 sudo mv node-v6.3.0-linux-x64 /opt/nodejs 1 1 配置变量 wxl@wxl-pc:~$ sudo vim /etc/profile.d/nodejs.sh 增加内容 export NODE_HOME=/opt/nodejs export PATH=$PATH:$NODE_HOME/bin export NODE_PATH=$NODE_HOME/lib/node_modules 1 2 3 4 5 1 2 3 4 5 使得变量生效,当重启机器或者用户注销时自动是用户生效/查看版本情况 source /etc/profile node -v 1 2 1 2 2.5.配置pyton数据科学开发环境 ubuntu16.04本身自带pyton2.7.12(输入pyton可查看)和3.5.1+(输入python3,可查看) 5.5.1. 配置python2 安装pip sudo apt install python-pip 1 1 查看当前环境已经安装的python包 pip list 1 1 更新pip pip install --upgrade pip 1 1 2.5.2 配置pyhton3 sudo apt install python3-pip 1 1 查看当前环境已经安装的python包 pip3 list 1 1 更新pip3 pip3 install --upgrade pip 1 1 Tip:/usr/share/python-wheels 2.6. Jupyter notebook安装/ Jupyter notebook方便的基于web开发的工具,可以及时浏览和调整。 2.6.1. 为python2安装jupyter notebook sudo pip install jupyter 1 1 两者只安装一种,否则后者覆盖前者,如果需要python2和python3共同使用jupyter notebook。请看6。 2.6.2. 为python3安装jupyter notebook sudo pip3 install jupyter 1 1 2.7. Jupyter notebook同时支持python2和python3。 如果你像我一样,因为工作需要同时需要python2和python3一起工作,那么接下来就介绍如何使得两个python不同版本的内核共存。 如果已经安装了python2的jupyter notebook,那么在打开notebook时候想要同时能使用python3的内核。 python3 -m pip install ipykernel python3 -m ipykernel install --user 1 2 1 2 如果提前安装的是python3的jupyter notebook,则把命令部分的3替换成2,即可。 文档支持
注意:这些命令前面都有一个冒号: 当编辑完文档,准备退出Vi返回到shell时,能够使用以下几种方法之一。 在命令模式中,连按两次大写字母Z,若当前编辑的文档曾被修改过,则Vi保存该文档后退出,返回到shell;若当前编辑的文档没被修改过,则Vi直接退出, 返回到shell。 在末行模式下,输入命令 :w Vi保存当前编辑文档,但并不退出,而是继续等待用户输入命令。在使用w命令时,能够再给编辑文档起一个新的文档名。 [例6] :w newfile 此时Vi将把当前文档的内容保存到指定的newfile中,而原有文档保持不变。若newfile是个已存在的文档,则Vi在显示窗口的状态行给出提示信息: File exists (use ! to override) 此时,若用户真的希望用文档的当前内容替换newfile中原有内容,可使用命令 :w! newfile 否则可选择另外的文档名来保存当前文档。 在末行模式下,输入命令 :q 系统退出Vi返回到shell。若在用此命令退出Vi时,编辑文档没有被保存,则Vi在显示窗口的最末行显示如下信息: No write since last change (use ! to overrides) 提示用户该文档被修改后没有保存,然后Vi并不退出,继续等待用户命令。若用户就是不想保存被修改后的文档而要强行退出Vi时,可使用命令 :q! Vi放弃所作修改而直接退到shell下。 在末行模式下,输入命令 :wq Vi将先保存文档,然后退出Vi返回到shell。 在末行模式下,输入命令 :x 该命令的功能同命令模式下的ZZ命令功能相同。
XZ压缩最新压缩率之王 xz这个压缩可能很多都很陌生,不过您可知道xz是绝大数Linux默认就带的一个压缩工具。 之前xz使用一直很少,所以几乎没有什么提起。 我是在下载phpmyadmin的时候看到这种压缩格式的,phpmyadmin压缩包xz格式的居然比7z还要小,这引起我的兴趣。 最新一段时间会经常听到xz被采用的声音,像是最新的archlinux某些东西就使用xz压缩。不过xz也有一个坏处就是压缩时间比较长,比7z压缩时间还长一些。不过压缩是一次性的,所以可以忽略。 xz压缩文件方法或命令 xz -z 要压缩的文件 如果要保留被压缩的文件加上参数 -k ,如果要设置压缩率加入参数 -0 到 -9调节压缩率。如果不设置,默认压缩等级是6. xz解压文件方法或命令 xz -d 要解压的文件 同样使用 -k 参数来保留被解压缩的文件。 创建或解压tar.xz文件的方法 习惯了 tar czvf 或 tar xzvf 的人可能碰到 tar.xz也会想用单一命令搞定解压或压缩。其实不行 tar里面没有征对xz格式的参数比如 z是针对 gzip,j是针对 bzip2。 创建tar.xz文件:只要先 tar cvf xxx.tar xxx/ 这样创建xxx.tar文件先,然后使用 xz -z xxx.tar 来将 xxx.tar压缩成为 xxx.tar.xz 解压tar.xz文件:先 xz -d xxx.tar.xz 将 xxx.tar.xz解压成 xxx.tar 然后,再用 tar xvf xxx.tar来解包。
比如输入apt-get install eclipse,或者apt-get update 会提示 无法打开锁文件 /var/lib/dpkg/lock - open (13: 权限不够) 无法对状态列表目录加锁(/var/lib/dpkg/),请查看您是否正以 root 用户运行? 这是,在命令前加入sudo即可。
1.浏览器和服务器的交互原理 通俗描述:我们平时通过浏览器来访问网站,其实就相当于你通过浏览器去访问一台电脑上访问文件一样,只不过浏览器的访问请求是由被访问的电脑上的一个 WEB服务器软件来接收处理,它会分析接收到的请求信息,从而按照请求信息来找到服务器电脑上的文件,经过处理,最终将生成的内容发回到浏览器。 简单的说就是:由浏览器生成一条“命令”,通过互联网发给另一台电脑的某个软件(服务器软件);服务器软件接收到“命令”,就分析理解这个“命令”,然后按照“命令”找到服务器电脑上的文件,将文件内容发送回浏览器。 通过上图,我们看到了浏览器和服务器交互的简单过程。现在,我们要想想,浏览器和服务器软件到底是神马东东,他们之间又是如何交互信息的呢? 其实,浏览器和服务器软件,就是两个独立的应用程序(就如qq、office、画图工具一样)。那么两个应用程序之间要交互信息,就牵扯到了应用程序通信的问题。那他俩是使用神马方式通信的呢? 答案是套接字:Socket。至于Socket的具体用法和原理,篇幅问题不在此文中写了,先预留位置在这,下次补上《基于多线程和套接字的简易WebServer软件-没有控件的ASP.NET》。 浏览器和服务器软件通过套接字来发送和接收对方的信息,但现在的关键问题是,他们发送和接收的到底是什么?--- 基于Http协议的报文数据(详见《Http协议介绍---没有控件的ASP.NET》)。 也就是说:浏览器和服务器软件其实就是两个使用Socket进行通信的的两个应用程序:双方都发送按照 Http协议语法规范组织的数据,接收到数据后都按照 Http协议语法规范来解释。 2.浏览器和IIS(or other webserver)交互机制 上图就是IIS (服务器软件) 1.浏览器和IIS交互过程:我们都知道,在互联网上确定一台电脑的位置是使用IP寻址,但为什么当我们平时访问网站时直接输入一个域名也能够访问到某个服务器电脑进而由对方的服务器软件发送响应页面数据给我呢?下面我把简单步骤列出: (1)在浏览器输入网址:www.oumind.com/index.html,浏览器按照Http协议语法 生成请求报文数据。 (2).浏览器检查本机是否保存了www.oumind.com/index.html.域名对应的服务器IP地址。如果没有,则发送请求到所在城市网中最近的DNS服务器(域名解析服务器),它会根据我们发送来的域名查询到该域名对应的服务器IP地址,并发送回浏览器。 (3)浏览器从DNS服务器获得了 www.oumind.com/index.html域名对应的服务器电脑IP,则将 请求报文 通过Socket发送到服务器电脑。(注意:Http协议 规定服务器软件使用的默认端口是80,通俗的说,就是如果浏览器访问一个网站页面,浏览器默认就是将 请求报文 发送到服务器80端口,而服务器负责监听这个端口的软件一般就是服务器软件—比如asp.net用的IIS,java用的Tomcat。) (4)IIS接收到 请求报文,分析请求报文,从中获取请求的页面路径 /index.html。判断页面的后缀名,如果是静态页面(.html/.jpg/.css/.js等),则直接由IIS软件的组件读取该文件内容,并将内容通过Socket发送回浏览器。 (5)但如果此时请求的是一个动态页面(.aspx/.ashx),IIS自己就处理不了 (因为IIS软件开发出来的时候,ASP.NET程序还不存在呢) 。所以,IIS就去它的 扩展程序映射表 中根据被请求文件后缀名 查看是否有能够处理这种文件的扩展程序。 而我们ASPNET中常用的文件.aspx/.ashx等 对应的处理程序是aspnet_isapi.dll。如下图: (6)如果IIS根据后缀名找到对应的处理程序,则通过调用此程序来处理浏览器发送来的请求报文。 IIS自身是不能处理像ASPX扩 展名这样的页面,只能直接请求像HTML这样的静态文件,之所以能处理ASPX这样扩展名的页面,是因为IIS有一个ISAPI过滤器,它是一个COM组件。 ASP.NET服务在注册到IIS的时候,就会添加一个Win32的扩展动态库aspnet_isapi.dll。并将扩展可以处理的页面扩展名(如 ASPX)注册到IIS里面。扩展启动后,就根据定义好的方式来处理IIS所不能处理的页面。 当客户端请求一个服务器资源时,这个HTTP请求会被inetinfo.exe进程截获(www服务),然后Check请求资源的类型,并依据资源映射信息(存储在IIS元库中,一种IIS专用的配置数据库)将请求的资源分配给特定的处理程序模块。若请求的是静态资源(img,text,html等)则由IIS处理(IIS在本地Web Server上访问请求的文件),将内容输出到控制台,发出请求的浏览器就能接收到它了。 若需要在服务器端处理的请求,则会被传到已注册的扩展模块 中,aspx请求会被分配给aspnet_isapi.dll,让这个程序开始处理代码,生成标准的HTML代码,然后将这些HTML加入到原有的 HTML中,最后把完整的HTML返回给IIS,IIS再把内容发送到客户浏览器。 ASP.NET FrameWork对请求的处理: 上面说到IIS将像ASPX这样的页面分配给aspnet_isapi.dll,接着处理如下: 1、aspnet_isapi.dll则会 通过一个Http PipeLine的管道将这个Http请求发给w3wp.exe(iis 工作者进程,IIS6.0中叫做 w3wq.exe,IIS5.0中叫做 aspnet_wp.exe),之后asp.net framework就会通过HttpRuntime来处理这个Http请求。 2、HttpRuntime首先会确定处理该请求的类名,HttpRuntime通过公共接口IHttpHandler来调用该类获取被请求资源的类的实例。 3、调用HttpRuntime.ProcessRequest开始处理要发送到浏览器的页面,具体说就是创建一个HttpContext实例,它封装了所有与请求有关的http特有的信息,并初始化一个Write对象用于缓存标记代码。 4、HttpRuntime使用上下文信息查找或新建能处理该请求的WEB应用程序的对象。由HttpApplication Factory负责返回HttpApplication实例。 5、HttpApplication实例会读取web.config中所有HttpModule的配置。 6、HttpApplication对象使用IHttpHandlerFactory类型的实例返回HttpHandler(http处理程序)给HttpRuntime对象。一个页面只是个http处理程序对象。7、最后由HttpRuntime对象调用IHttpHandler的页面对象的ProcessRequest方法。
1.Google CN DevSites 为了帮助开发者更加方便地使用这些网站上的资源,避免每次都手动调整相关网址,Google 做了一个简单的 Chrome 扩展程序 (插件),自动将 google.com 上的开发者网站 URL 替换成 google.cn 上的对应URL (如果有相对应的.CN域名上的页面),这样只要点击就能直接打开页面。 比如,当读者的页面是阅读 https://firebase.google.com/docs/ 时,这个 Chrome 扩展工具将自动替换这个地址到以下地址:https://firebase.google.cn/docs/。 此扩展可以在 Chrome Web Store 上进行下载,同时它的代码也已经在在 GitHub 上开源。
Easy Go Programming Setup for Windows Dec 23, 2014 I’ve had to do this more than once recently, so I figured I’d document the simple steps for setting up the Go programming language on Windows. Most of this is simple and straightforward. The only tricky part I found is setting up your GOPATH, which defines a convention for storing and building Go code you write and acquire from open source code repositories. 5 Simple Steps Follow these five simple steps to install Go. Make sure you have both Git download and Mercurial download installed. With Go programming you’ll make heavy use of open source repositories. Download and install the latest 64-bit Go MSI distributable (which sets most of the environmental variables for you). https://golang.org/dl/ To make things simple, use the default installation path at C:\Go Ensure the Go binaries (found in C:\Go\bin) are in your Path system environment variables. To check click System, Advanced system settings, Environment Variables... and open Path under System variables: An easy way to confirm is to open the command line and type go version: Setup your Go workspace. This consists of three folders at the root: bin/ pkg/ src/ I create a C:\Projects\Go folder as my root Go workspace: Create the GOPATH environment variable and reference your Go workspace path. To add, click System, Advanced system settings, Environment Variables... and click New... under System variables: Set the variable name to GOPATH and value to your Go workspace path (e.g. C:\Projects\Go): You can quickly check to ensure your path has been set by opening the command line and typing echo %GOPATH% and check the output: And that’s all it takes! You’re ready to get started. Verify Want to quickly test and ensure this is all working as expected? Open the command line and type the following: go get github.com/golang/example/hello %GOPATH%/bin/hello You should see the output as “Hello, Go examples!” (refreshingly, not your typical hello world): I hope this helps!
阅读之前,建议先阅读初识 Nginx。 之后,我们来了解一下 Nginx 配置。 抽象来说,将 Nginx 配置为 Web 服务器就是定义处理哪些 URLS 和如何处理这些URLS 对应的请求。具体来说,就是定义一些虚拟服务器(Virtual Servers),控制具有特定 IP 和域名的请求。 更具体的来说, Nginx 通过定义一系列 locations 来控制对 URIS 的选择。每一个 location 定义了对映射到自己的请求的处理场景:返回一个文件或者代理请求,或者根据不同的错误代码返回不同的错误页面。另外,根据 URI 的不同,请求也可以被重定向到其它 server 或者 location。 设置虚拟服务器 listen: Nginx 配置文件至少包含一个 server 命令 ,用来定义虚拟服务器。当请求到来时, Nginx 会首先选择一个虚拟服务器来处理该请求。 虚拟服务器定义在 http 上下文中的 server 中: http { server { # Server configuration } } 注意: http 中可以定义多个 server server 配置块使用 listen 命令监听本机 IP 和端口号(包括 Unix domain socket and path),支持 IPv4、IPv6,IPv6地址需要用方括号括起来: server { listen 127.0.0.1:8080; # IPv4地址,8080端口 # listen [2001:3CA1:10F:1A:121B:0:0:10]:80; # IPv6地址,80端口 # listen [::]:80; # 听本机的所有IPv4与IPv6地址,80端口 # The rest of server configuration } 上述配置,如果不写端口号,默认使用80端口,如果不写 IP ,则监听本机所有 IP。 server_name: 如果多个 server 的 listen IP 和端口号一模一样, Nginx 通过请求头中的 Host 与 server_name 定义的主机名进行比较,来选择合适的虚拟服务器处理请求: server { listen 80; server_name lufficc.com www.lufficc.com; ... } server_name 的参数可以为: 完整的主机名,如:api.lufficc.com 。 含有通配符(含有 *),如:*.lufficc.com 或 api.* 。 正则表达式,以 ~ 开头。 通配符只能在开头或结尾,而且只能与一个 . 相邻。www.*.example.org 和 w*.example.org均无效。 但是,可以使用正则表达式匹配这些名称,例如 ~^www\..+\.example\.org$ 和 ~^w.*\.example\.org$ 。 而且 * 可以匹配多个部分。 名称 * .example.org 不仅匹配 www.example.org,还匹配www.sub.example.org。对于正则表达式:Nginx 使用的正则表达式与 Perl 编程语言(PCRE)使用的正则表达式兼容。 要使用正则表达式,且必须以 ~ 开头。 命名的正则表达式可以捕获变量,然后使用: server { server_name ~^(www\.)?(?<domain>.+)$; location / { root /sites/$domain; } } 小括号 () 之间匹配的内容,也可以在后面通过 $1 来引用,$2 表示的是前面第二个 () 里的内容。因此上述内容也可写为: server { server_name ~^(www\.)?(.+)$; location / { root /sites/$2; } } 一个 server_name 示例: server { listen 80; server_name api.lufficc.com *.lufficc.com; ... } 同样,如果多个名称匹配 Host 头部, Nginx 采用下列顺序选择: 完整的主机名,如 api.lufficc.com。 最长的,且以 * 开头的通配名,如:*.lufficc.com。 最长的,且以 * 结尾的通配名,如:api.* 。 第一个匹配的正则表达式。(按照配置文件中的顺序) 即优先级:api.lufficc.com > *.lufficc.com > api.* > 正则。 如果 Host 头部不匹配任何一个 server_name ,Nginx 将请求路由到默认虚拟服务器。默认虚拟服务器是指:nginx.conf 文件中第一个 server 或者 显式用 default_server 声明: server { listen 80 default_server; ... } 配置 location URI 与 location 参数的匹配 当选择好 server 之后,Nginx 会根据 URIs 选择合适的 location 来决定代理请求或者返回文件。 location 指令接受两种类型的参数: 前缀字符串(路径名称) 正则表达式 对于前缀字符串参数, URIs 必须严格的以它开头。例如对于 /some/path/ 参数,可以匹配 /some/path/document.html ,但是不匹配 /my-site/some/path,因为 /my-site/some/path 不以 /some/path/ 开头。 location /some/path/ { ... } 对于正则表达式,以 ~ 开头表示大小写敏感,以 ~* 开头表示大小写不敏感。注意路径中的 . 要写成 \. 。例如一个匹配以 .html 或者 .htm 结尾的 URI 的 location: location ~ \.html? { ... } 正则表达式的优先级大于前缀字符串。如果找到匹配的前缀字符串,仍继续搜索正则表达式,但如果前缀字符串以 ^~ 开头,则不再检查正则表达式。 具体的搜索匹配流程如下: 将 URI 与所有的前缀字符串进行比较。 = 修饰符表明 URI 必须与前缀字符串相等(不是开始,而是相等),如果找到,则搜索停止。 如果找到的最长前缀匹配字符串以 ^~ 开头,则不再搜索正则表达式是否匹配。 存储匹配的最长前缀字符串。 测试对比 URI 与正则表达式。 找到第一个匹配的正则表达式后停止。 如果没有正则表达式匹配,使用 4 存储的前缀字符串对应的 location。 = 修饰符拥有最高的优先级。如网站首页访问频繁,我们可以专门定义一个 location 来减少搜索匹配次数(因为搜索到 = 修饰的匹配的 location 将停止搜索),提高速度: location = / { ... } 静态文件和代理 location 也定义了如何处理匹配的请求:返回静态文件 或者 交给代理服务器处理。下面的例子中,第一个 location 返回 /data 目录中的静态文件,第二个 location 则将请求传递给 https://lufficc.com 域名的服务器处理: server { location /images/ { root /data; } location / { proxy_pass https://lufficc.com; } } root 指令定义了静态文件的根目录,并且和 URI 拼接形成最终的本地文件路径。如请求 /images/example.png,则拼接后返回本地服务器文件 /data/images/example.png 。 proxy_pass 指令将请求传递到 URL 指向的代理服务器。让后将来自代理服务器的响应转发给客户端。 在上面的示例中,所有不以 /images / 开头的 URI 的请求都将传递给代理服务器处理。 比如我把 proxy_pass 设置为 https://www.baidu.com/,那么访问 http://search.lufficc.com/ 将得到百度首页一样的响应(页面)(感兴趣的童鞋可以自己试一试搜索功能,和百度没差别呢): server{ listen 80; server_name search.lufficc.com; location / { proxy_pass https://www.baidu.com; } } 使用变量(Variables) 你可以使用变量来使 Nginx 在不同的请求下采用不同的处理方式。变量是在运行时计算的,用作指令的参数。 变量由 $ 开头的符号表示。 变量基于 Nginx 的状态定义信息,例如当前处理的请求的属性。 有很多预定义变量,例如核心的 HTTP 变量,你也可以使用 set,map 和 geo 指令定义自定义变量。 大多数变量在运行时计算,并包含与特定请求相关的信息。 例如,$remote_addr 包含客户端 IP 地址,$uri 保存当前URI值。 一些常用的变量如下: 变量名称 作用 $uri 请求中的当前URI(不带请求参数),它可以通过内部重定向,或者使用index指令进行修改,$uri不包含主机名,如 /foo/bar.html。 $arg_name 请求中的的参数名,即“?”后面的arg_name=arg_value形式的arg_name $hostname 主机名 $args 请求中的参数值 $query_string 同 $args $request 代表客户端的请求地址 $request_uri 这个变量等于包含一些客户端请求参数的原始URI,它无法修改,不包含主机名,如:/cnphp/test.php?arg=freemouse。 ... ... 一个简单的应用就是从 http 重定向到 https 时带上路径信息: server{ ... return 301 https://lufficc.com$request_uri; ... } 返回特定状态码 如果你的网站上的一些资源永久移除了,最快最简洁的方法就是使用 return 指令直接返回: location /wrong/url { return 404; } return 的第一个参数是响应代码。可选的第二个参数可以是重定向(对应于代码301,302,303和307)的 URL 或在响应正文中返回的文本。 例如: location /permanently/moved/url { return 301 http://www.example.com/moved/here; } return 指令可以包含在 location 和 server 上下文中: server{ location / { return 404; } } 或者: server{ ... return 404; location / { ... } } 错误处理 error_page 命令可以配置特定错误码的错误页面,或者重定向到其他的页面。下面的示例将在 404 错误发生时返回 /404.html 页面。 error_page 404 /404.html; error_page 命令定义了如何处理错误,因此不会直接返回,而 return 确实会立即返回。当代理服务器或者 Nginx 处理时产生相应的错误的代码,均会返回相应的错误页面。 在下面的示例中,当 Nginx 找不到页面时,它将使用代码301替换代码404,并将客户端重定向到 http://example.com/new/path.html 。 此配置很有用,比如当客户端仍尝试用旧的 URI 访问页面时,301代码通知浏览器页面已永久移除,并且需要自动替换为返回的新地址。 location /old/path.html { error_page 404 =301 http:/example.com/new/path.html; } 重写 URIs rewrite 指令可以多次修改请求的 URI。rewrite 的第一个参数是 URI需要匹配的正则表达式,第二个参数是将要替换的 URI。第三个参数可选,指示是否继续可以重写或者返回重定向代码(301或302)。例如: location /users/ { rewrite ^/users/(.*)$ /show?user=$1 break; } 您可以在 server 和 location 上下文中包括多个 rewrite 指令。 Nginx 按照它们发生的顺序一个一个地执行指令。 当选择 server 时,server 中的 rewrite 指令将执行一次。 在 Nginx 处理一组 rewrite 指令之后,它根据新的 URI 选择 location 。 如果所选 location仍旧包含 rewrite 指令,它们将依次执行。 如果 URI 匹配所有,则在处理完所有定义的 rewrite 指令后,搜索新的 location 。 以下示例将 rewrite 指令与 return 指令结合使用: server { ... rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra last; return 403; ... } 诸如 /download/some/media/file 的 URI 被改为 /download/some/mp3/file.mp3 。 由于 last 标志,后续指令(第二个 rewrite 指令和 return 指令)被跳过,但 Nginx 继续以更改后的 URI 处理请求。 类似地,诸如 /download/some/audio/file 的 URI 被替换为 /download/some/mp3/file.ra。 如果 URI 不匹配 rewrite 指令,Nginx 将403 错误代码返回给客户端。 last 与 break的区别是: last : 在当前 server 或 location 上下文中停止执行 rewrite 指令,但是 Nginx 继续搜索与重写的URI匹配的 location,并应用新 location 中的任何 rewrite 指令(这意味着 URI 可能再次改变)。 break :停止当前上下文中 rewrite 指令的处理,并取消搜索与新 URI 匹配的 location。 不会执行新 location中的 rewrite 指令。 附录 常用正则 . : 匹配除换行符以外的任意字符 ? : 重复0次或1次 + : 重复1次或更多次 *: 重复0次或更多次 \d :匹配数字 ^ : 匹配字符串的开始 $ : 匹配字符串的结束 {n} : 重复n次 {n,} : 重复n次或更多次 [c] : 匹配单个字符c [a-z]: 匹配a-z小写字母的任意一个 全局变量 $args : #这个变量等于请求行中的参数,同$query_string $content_length : 请求头中的Content-length字段。 $content_type : 请求头中的Content-Type字段。 $document_root : 当前请求在root指令中指定的值。 $host : 请求主机头字段,否则为服务器名称。 $http_user_agent : 客户端agent信息 $http_cookie : 客户端cookie信息 $limit_rate : 这个变量可以限制连接速率。 $request_method : 客户端请求的动作,通常为GET或POST。 $remote_addr : 客户端的IP地址。 $remote_port : 客户端的端口。 $remote_user : 已经经过Auth Basic Module验证的用户名。 $request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成。 $scheme : HTTP方法(如http,https)。 $server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。 $server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。 $server_name : 服务器名称。 $server_port : 请求到达服务器的端口号。 $request_uri : 包含请求参数的原始URI,不包含主机名,如:/foo/bar.php?arg=baz。 $uri : 不带请求参数的当前URI,$uri不包含主机名,如/foo/bar.html。 $document_uri : 与$uri相同。 例如请求:http://localhost:88/test1/test2/test.php$host:localhost$server_port:88$request_uri:/test1/test2/test.php$document_uri:/test1/test2/test.php$document_root:/var/www/html$request_filename:/var/www/html/test1/test2/test.php 参考 https://www.nginx.com/resources/admin-guide/nginx-web-server/ http://seanlook.com/2015/05/17/nginx-location-rewrite/ -- END
在这篇文章中将给大家分享12个有关于JavaScript的小技巧。这些小技巧可能在你的实际工作中或许能帮助你解决一些问题。 使用!!操作符转换布尔值 有时候我们需要对一个变量查检其是否存在或者检查值是否有一个有效值,如果存在就返回true值。为了做这样的验证,我们可以使用!!操作符来实现是非常的方便与简单。对于变量可以使用!!variable做检测,只要变量的值为:0、null、" "、undefined或者NaN都将返回的是false,反之返回的是true。比如下面的示例: function Account(cash) { this.cash = cash; this.hasMoney = !!cash; } var account = new Account(100.50); console.log(account.cash); // 100.50 console.log(account.hasMoney); // true var emptyAccount = new Account(0); console.log(emptyAccount.cash); // 0 console.log(emptyAccount.hasMoney); // false 在这个示例中,只要account.cash的值大于0,那么account.hasMoney返回的值就是true。 使用+将字符串转换成数字 这个技巧非常有用,其非常简单,可以交字符串数据转换成数字,不过其只适合用于字符串数据,否则将返回NaN,比如下面的示例: function toNumber(strNumber) { return +strNumber; } console.log(toNumber("1234")); // 1234 console.log(toNumber("ACB")); // NaN 这个也适用于Date,在本例中,它将返回的是时间戳数字: console.log(+new Date()) // 1461288164385 并条件符 如果你有一段这样的代码: if (conected) { login(); } 你也可以将变量简写,并且使用&&和函数连接在一起,比如上面的示例,可以简写成这样: conected && login(); 如果一些属性或函数存在于一个对象中,你也可以这样做检测,如下面的代码所示: user && user.login(); 使用||运算符 在ES6中有默认参数这一特性。为了在老版本的浏览器中模拟这一特性,可以使用||操作符,并且将将默认值当做第二个参数传入。如果第一个参数返回的值为false,那么第二个值将会认为是一个默认值。如下面这个示例: function User(name, age) { this.name = name || "Oliver Queen"; this.age = age || 27; } var user1 = new User(); console.log(user1.name); // Oliver Queen console.log(user1.age); // 27 var user2 = new User("Barry Allen", 25); console.log(user2.name); // Barry Allen console.log(user2.age); // 25 在循环中缓存array.length 这个技巧很简单,这个在处理一个很大的数组循环时,对性能影响将是非常大的。基本上,大家都会写一个这样的同步迭代的数组: for(var i = 0; i < array.length; i++) { console.log(array[i]); } 如果是一个小型数组,这样做很好,如果你要处理的是一个大的数组,这段代码在每次迭代都将会重新计算数组的大小,这将会导致一些延误。为了避免这种现象出现,可以将array.length做一个缓存: var length = array.length; for(var i = 0; i < length; i++) { console.log(array[i]); } 你也可以写在这样: for(var i = 0, length = array.length; i < length; i++) { console.log(array[i]); } 检测对象中属性 当你需要检测一些属性是否存在,避免运行未定义的函数或属性时,这个小技巧就显得很有用。如果你打算定些一些跨兼容的浏览器代码,你也可能会用到这个小技巧。例如,你想使用document.querySelector()来选择一个id,并且让它能兼容IE6浏览器,但是在IE6浏览器中这个函数是不存在的,那么使用这个操作符来检测这个函数是否存在就显得非常的有用,如下面的示例: if ('querySelector' in document) { document.querySelector("#id"); } else { document.getElementById("id"); } 在这个示例中,如果document不存在querySelector函数,那么就会调用docuemnt.getElementById("id")。 获取数组中最后一个元素 Array.prototype.slice(begin,end)用来获取begin和end之间的数组元素。如果你不设置end参数,将会将数组的默认长度值当作end值。但有些同学可能不知道这个函数还可以接受负值作为参数。如果你设置一个负值作为begin的值,那么你可以获取数组的最后一个元素。如: var array = [1,2,3,4,5,6]; console.log(array.slice(-1)); // [6] console.log(array.slice(-2)); // [5,6] console.log(array.slice(-3)); // [4,5,6] 数组截断 这个小技巧主要用来锁定数组的大小,如果用于删除数组中的一些元素来说,是非常有用的。例如,你的数组有10个元素,但你只想只要前五个元素,那么你可以通过array.length=5来截断数组。如下面这个示例: var array = [1,2,3,4,5,6]; console.log(array.length); // 6 array.length = 3; console.log(array.length); // 3 console.log(array); // [1,2,3] 替换所有 String.replace()函数允许你使用字符串或正则表达式来替换字符串,本身这个函数只替换第一次出现的字符串,不过你可以使用正则表达多中的/g来模拟replaceAll()函数功能: var string = "john john"; console.log(string.replace(/hn/, "ana")); // "joana john" console.log(string.replace(/hn/g, "ana")); // "joana joana" 合并数组 如果你要合并两个数组,一般情况之下你都会使用Array.concat()函数: var array1 = [1,2,3]; var array2 = [4,5,6]; console.log(array1.concat(array2)); // [1,2,3,4,5,6]; 然后这个函数并不适合用来合并两个大型的数组,因为其将消耗大量的内存来存储新创建的数组。在这种情况之个,可以使用Array.pus().apply(arr1,arr2)来替代创建一个新数组。这种方法不是用来创建一个新的数组,其只是将第一个第二个数组合并在一起,同时减少内存的使用: var array1 = [1,2,3]; var array2 = [4,5,6]; console.log(array1.push.apply(array1, array2)); // [1,2,3,4,5,6]; 将NodeList转换成数组 如果你运行document.querySelectorAll(“p”)函数时,它可能返回DOM元素的数组,也就是NodeList对象。但这个对象不具有数组的函数功能,比如sort()、reduce()、map()、filter()等。为了让这些原生的数组函数功能也能用于其上面,需要将节点列表转换成数组。可以使用[].slice.call(elements)来实现: var elements = document.querySelectorAll("p"); // NodeList var arrayElements = [].slice.call(elements); // Now the NodeList is an array var arrayElements = Array.from(elements); // This is another way of converting NodeList to Array 数组元素的洗牌 对于数组元素的洗牌,不需要使用任何外部的库,比如Lodash,只要这样做: var list = [1,2,3]; console.log(list.sort(function() { Math.random() - 0.5 })); // [2,1,3] 总结 现在你学会了些有用的JavaScript小技巧。希望这些小技巧能在工作中帮助你解决一些麻烦,或者说这篇文章对你有所帮助。如果你有一些优秀的JavaScript小技巧,欢迎在评论中与我们一起分享。
想知道 2017 年有哪些值得关注的开发工具吗?StackShare 年度开发工具排行榜来啦! StackShare.io 是一个开发者工具及服务分享平台,致力于发现并分享开发者使用的开发工具、服务与优质资源,帮助开发者使用最方便的开发工具和便捷的服务。该网站对数千个数据点进行分析,并收集整理 2016 年开发者的评论和投票数,最终为大家呈现以下最热门开发工具排名。 应用与数据工具 #1:JavaScript:轻量级、可演绎的、面向对象的程序语言 得票数:4.72K 使用数:7.06K 点赞数:407 #2: Bootstrap:简单切灵活的 HTML、CSS 和 JS,适配流行的 UI 组件和交互 得票数:6K 使用数:11.3K 点赞数:379 #3: Node.js:基于 Chrome 的 JavaScript 运行时构建的平台,能够轻松构建快速、可扩展的网络应用 得票数:5.51K 使用数:5.36K 点赞数:463 #4: nginx:自由开源软件,为互联网上业务繁忙的网站提供高性能的网页服务器 得票数:3.68K 使用数:9.91K 点赞数:293 #5: AngularJS:超强 JavaScript MVW 框架 得票数:4.44K 使用数:4.75K 点赞数:323 #6: PHP:流行的通用脚本语言,特别适合 Web 开发 得票数:2.91K 使用数:10.8K 点赞数:176 #7: Python:一门清晰且强大的面向对象的编程语言,与 Perl、Ruby、Scheme 和 Java 类似 得票数:3.74K 使用数:3.83K 点赞数:275 #8: jQuery:一个快速、简洁的JavaScript框架 得票数:5.52K 使用数:5.8K 点赞数:200 #9: HTML5:万维网核心语言的第五个重要版本 得票数:1.45K 使用数:4.04K 点赞数:155 #10: React:用于构建用户界面的 JaveScript 库 得票数:1.83K 使用数:2.64K 点赞数:275 实用工具 #1: Google Analytics:企业级网页分析工具 得票数:3.93K 使用数:18.2K 点赞数:127 #2: Postman:Chrome 扩展,提供功能强大的 Web API & HTTP 请求调试 得票数:781 使用数:1.42K 点赞数:134 #3: Elasticsearch:开源、分布式的 RESTful 搜索引擎 得票数:1.12K 使用数:1.84K 点赞数:162 #4: SendGrid:电子邮件服务平台 得票数:484 使用数:2.68K 点赞数:69 #5: Stripe:在线支付工具 得票数:1.09K 使用数:1.35K 点赞数:100 #6: Amazon Route 53:可用性高,可扩展性强的云域名系统 DNS Web 服务 得票数:469 使用数:1.07K 点赞数:18 #7: GitHub Pages:用于介绍托管在 GitHub 的项目 得票数:805 使用数:1.06K 点赞数:42 #8: Mandrill:发送用户转化邮件、触发邮件和个性化邮件,并追踪结果 得票数:877 使用数:2.9K 点赞数:75 #9: Mailgun:优秀的电子邮件服务器 得票数:497 使用数:1.7K 点赞数:42 #10: PayPal:在线支付平台 得票数:466 使用数:839 点赞数:20 DevOps 工具 #1: GitHub:面向开源项目和私有开发项目的强大的代码协作、检查和管理工具 得票数:7.45K 使用数:6.53K 点赞数:533 #2: Docker:开源项目,以轻量级容器的方式打包、交付和运行任何应用 得票数:2.4K 使用数:3.32K 点赞数:376 #3: Atom:符合 21 世纪风格的具有可玩性的编辑器 得票数:1.88K 使用数:1.67K 点赞数:224 #4: Sublime Text:先进的适合代码、标记和文章的文本编辑器 得票数:3.01K 使用数:2.77K 点赞数:241 #5: Bitbucket:适用于团队的 Git 和 Mercurial 代码管理工具 得票数:2.05K 使用数:2.62K 点赞数:163 #6: Jenkins:可扩展的开源的持续集成服务器 得票数:1.33K 使用数:2.3K 点赞数:168 #7: npm:包管理工具,安装、发布和管理 node 程序 得票数:990 使用数:2.98K 点赞数:164 #8: gulp:基于流的自动化构建工具 得票数:1.35K 使用数:2.1K 点赞数:140 #9: GitLab:用于仓库管理系统的开源项目 得票数:1.04K 使用数:1.26K 点赞数:116 #10: Vim:高度可配置的文本编辑器,实现高效的文字编辑 得票数:1.46K 使用数:1.46K 点赞数:114 商务工具 #1: Slack:聊天群组 + 大规模工具集成 + 文件整合 + 统一搜索 得票数:4.36K 使用数:4.81K 点赞数:427 #2: Google Apps:基于网页的邮件、日历和文档应用,让你随时随地都可访问 得票数:1.95K 使用数:12.3K 点赞数:99 #3: Trello:提供简洁清晰的团队协作工具 得票数:2.88K 使用数:2.73K 点赞数:223 #4: WordPress:使用 PHP 语言开发的博客平台 得票数:1.43K 使用数:6.1K 点赞数:68 #5: JIRA:用于团队计划、构建和发布产品的项目追踪工具 得票数:671 使用数:1.93K 点赞数:86 #6: MailChimp:通过电子邮件订阅 RSS 的在线工具 得票数:918 使用数:1.66K 点赞数:55 #7: Skype:超清晰网络电话工具 得票数:454 使用数:1.25K 点赞数:30 #8: InVision:基于产品的沟通协作平台 得票数:454 使用数:711 点赞数:79 #9: Confluence:专业的企业知识管理与协同软件 得票数:52 使用数:912 点赞数:26 #10: Intercom:社会化客户关系管理平台 得票数:461 使用数:1.28K 点赞数:66 2016 年新生工具 #1: SendBird:软件开发套件,可以迅速在服务中插入聊天功能 得票数:57 使用数:28 点赞数:63 #2: Yarn:为速度而打造的开源 JavaScript 包管理器 得票数:12 使用数:63 点赞数:37 #3: Passbolt:为团队打造的开源密码管理器 得票数:3 使用数:9 点赞数:54 #4: Milligram:简约 CSS 框架 得票数:42 使用数:11 点赞数:23 #5: Kite:使用所有互联网编程知识以增强编程环境 得票数:0 使用数:7 点赞数:46 #6: Portainer:Docker 的简单管理 UI 得票数:5 使用数:8 点赞数:31 #7: Diff So Fancy:优化 Git diffs 得票数:7 使用数:17 点赞数:16 #8: DC/OS:数据中心操作系统,在生存环境中运行微服务、大数据、容器的最简单方法 得票数:2 使用数:13 点赞数:25 #9: Docker Cloud:用于 Docker 容器管理和部署的托管服务 得票数:2 使用数:29 点赞数:9 #10: Parse-Server:Node / Express 的解析兼容的 API 服务器模块 得票数:2 使用数:12 点赞数:10
工欲善其事,必先利其器。如今 Web 开发标准越来越高,Web 开发者也在不断寻找途径提升自己的技能。为使大家的开发工作更顺利进行,本文整理了 10+ 款比较优秀的 Web 开发工具,希望对你有帮助。 喜欢的不要忘了收藏、点赞和打赏哦,感谢大家的支持! Bootstrap Bootstrap 是快速开发 Web 应用程序的前端工具包。它是一个 CSS 和 HTML 的集合,它使用了最新的浏览器技术,给你的 Web 开发提供了时尚的版式,表单,buttons,表格,网格系统等等。 DEMO:http://getbootstrap.com/ Atom Atom 是 Github 专门为程序员推出的一个跨平台文本编辑器。具有简洁和直观的图形用户界面,并有很多有趣的特点:支持 CSS,HTML,JavaScript 等网页编程语言。它支持宏,自动完成分屏功能,集成了文件管理器。 DEMO:https://atom.io/ Foundation Foundation 是一个易用、强大而且灵活的框架,用于构建基于任何设备上的 Web 应用。提供多种 Web 上的 UI 组件,如表单、按钮、Tabs 等。 DEMO:http://foundation.zurb.com/ Fiddler Fiddler 是一个 http 调试代理,它能够记录所有的你电脑和互联网之间的 http 通讯,Fiddler 可以也可以让你检查所有的 http 通讯,设置断点,以及 Fiddle 所有的“进出”的数据。Fiddler 要比其他的网络调试器要更加简单,因为它不仅暴露 http 通讯还提供一个用户友好的格式。 DEMO:http://www.telerik.com/fiddler Cloud9 IDE Cloud9 IDE 是一个基于 Node.JS 构建的 JavaScript 程序开发 Web IDE。它拥有一个非常快的文本编辑器,支持为 JS,HTML,CSS 和这几种的混合代码进行着色显示。Cloud9 IDE 还为 node.js 和 Google Chrome 集成调试器,可以在 IDE 中启动、暂停和停止。 DEMO:http://c9.io/ Notepad++ Notepad++ 是一款非常有特色的编辑器,是开源软件,可以免费使用。支持的语言: C, C++ , Java , C#, XML, HTML, PHP, Javascript! DEMO:https://notepad-plus-plus.org/ Firebug Firebug 是 Firefox 下的一款开发类插件,现属于 Firefox 的五星级强力推荐插件之一。它集 HTML 查看和编辑、Javascript 控制台、网络状况监视器于一体,是开发 JavaScript、CSS、HTML 和 Ajax 的得力助手。Firebug 如同一把精巧的瑞士军刀,从各个不同的角度剖析Web页面内部的细节层面,给 Web 开发者带来很大的便利。 DEMO:https://addons.mozilla.org/en-US/firefox/addon/firebug/ Visual Studio Code Visual Studio Code 是一个运行于 OS X,Windows 和 Linux 之上的,针对于编写现代 web 和云应用的跨平台编辑器。 DEMO:https://code.visualstudio.com/ GIMP GIMP 是 GNU 图像处理程序(GNU Image Manipulation Program)的缩写。包括几乎所有图象处理所需的功能,号称 Linux 下的 PhotoShop。GIMP 在 Linux 系统推出时就风靡了绘图爱好者圈。 DEMO:https://www.gimp.org/ Webbo Webbo 是一个免费、开源的轻量级的 Web 开发工具,支持 Windows 系统。主要特性包括:灵活编辑、支持多种 Web 编程语言和服务器端技术、即时的页面和服务器预览等。 DEMO:http://webbo.sourceforge.net/ SecureHeaders SecureHeaders 是 Twitter 送给 Web 开发者的一份大礼,作为一款 Web 安全开发工具,Secureheaders 能够自动实施安全相关的 header 规则,包括内容安全政策(CSP),防止 XSS、HSTS 等攻击,防止火绵羊(Firesheep)攻击以及 XFO 点击劫持等。 JSBin JSBin 是一个 Web 应用,主要用于帮助测试 JavaScript 和 CSS 的代码片段。功能与 jsFiddle 网站一致。 DEMO:http://jsbin.com/ Fontello Fontello 是个图标字体生成器。这个工具允许用户把这些图标 web 字体放到自己的项目中。主要特性如下: 缩小字形集合,减小字体大小 合并一些字体标记到单个文件中 访问大量专业级的开源图标 DEMO:http://fontello.com/ XDebug xdebug 是一个开源的 php 调试器,以 php 模块的形式加载并被使用。 DEMO:http://www.xdebug.org/
作者:小不了链接:https://zhuanlan.zhihu.com/p/23265155来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 鉴于时不时,有同学私信问我(老姚,下同)怎么学前端的问题。这里统一回复一下,如下次再遇到问我此问题同学,就直接把本文链接地址发给你了。 首先说句题外话。关于有人管我叫大神的事情。个人感觉这跟你买东西时,人家管你叫帅哥一样,你答应与否都无妨。 正题开始,“前端怎么学”应该因人而异,别人的方法未必适合自己。就说说我的学习方法吧:我把大部分时间放在学习js上了。因为这个js的学习曲线,先平后陡。项目实践和练习啥的,我不说了,主要说下工作之外的时间利用问题。我是怎么学的呢,看书,分析源码。个人这几天统计了一下,前端书籍目前看了50多本吧,大部分都是js的。市面上的书基本,差不多都看过。 第一个问题是,看书有啥好处? 好处应该是不言而明的,书看多了,基础会逐渐夯实起来。看多了,自己的判断力,自然就上来了。看别人的文章,就能很快判断出,对方每块儿讲得对不对,哪块儿又是自己不清楚的,模棱两可的。当然也为看源码,分析源码提供了基础。 10本书读2遍的好处,应该大于一本书读20遍。10本书的交集,那就是基础知识的核心,而并集那就是所有的知识。好书当然要多读,反复读。但是只读一本是不行的。因为每本书的侧重点都不一样。从不同的侧面,去理解一个知识点,是很有意义的。 所以特别佩服印度人,他跟你讲英文,你一个词语没听懂,他会蹦出n个同一意思的单词,你听懂一个,就ok了。看书也是这样的,某一块讲得不透彻,不用担心,其他书籍可以帮助你来了解。 第二个问题是,书籍推荐。 个人觉得不错的,没事可以翻翻的。书籍如下: >《javascript面向对象编程指南》,风格轻松易懂,比较适合初学者,原型那块儿讲得透彻,12种继承方式呢。 >《js权威指南》、《js高级程序设计》,这两本书经典是经典,但是太厚,适合把其中任意一章都当成一本书来读。洋洋洒洒,很难一口气看完。比较适合当做参考书。 >《你不知道的javascript》狙击js核心细节,闭包、原型、this讲得都还清楚。目前《中册》也出了,还在看。 >《js设计模式与开发实践》js设计模式也是要学的,此书把js的设计模式讲得非常清晰,一点不晦涩,看起来没多少难度。 >《正则指引》,分析源码时,如果正则表达式不懂,没法进行下去的。此书相对来说讲得比较清晰。 >《基于MVC的JavaScript Web富应用开发》,看完后,基本能写出自己的mvc框架了。是本好书。 >《javascript函数式编程》,js是一门函数式语言,此书是函数式编程一个入门,函数是一等公民那是非常重要的。 >《js忍者秘籍》,jq作者写的,没有传说中的那么难读,话说就算你看完并理解所有知识点,也不会达到世界高手级别的。因为你还没有做到随心所欲。 >《javascript框架设计》,如果初看此书,会觉得此书有罗列代码之嫌。在我看来,此书讲究的是框架的全局观。以上书籍是我认为是成就高手之路上必须看的,也需要反复看。 css相关的书籍,说实话我看得比较少,总共有六七本吧。有两本必须推荐一下: >《css权威指南》,css基础知识点那是讲得非常清楚的。什么层叠优先级、line-height啥的。不是随便一本书都敢叫“权威指南”的。 >《css揭秘》,此书我也是不断的看,此书才不屑于全面讲css3各属性呢。css规范文档能讲的,它只会讲你最不在意的。此书解决的47问题,解决思路和解决方案同等重要,很有启发性。以上各书你都可以不买,至少买本此书吧。 第三个问题,怎么看。 想必很多同学,都想看书,但是很难看下去。文字部分相对来说还能看看,一遇到代码,头皮就发麻了。此问题一开始时我也遇到的。 说一个学习理论。比如说学英语,有个开水理论。词汇量必须达到6000才行,如果没达到,英文水平不会上去的,这跟烧开水一样,没事烧烧,放着凉凉,从来没烧到100度,那么此水是永远不能喝的。一旦煮沸过,就可以随时喝了。 20本书你看不下去,说明什么呢?任何一本书,你都没看完过。熟悉的,永远只是前三章。别笑,我原先也是这样的。 那么现在的问题是,怎么把一本书看完呢?很简单,敲。《基于MVC的JavaScript Web富应用开发》这本书我看时,就是这样,终于有一天,我下定决心要把此书从头到尾敲一遍。文字加代码都敲,然后就一章一章得看完了。代码敲一遍后,你会发现,没之前看起来的那么难。 如果你属于一看书就犯困那种同学。强烈建议你把《javascript面向对象编程指南》此书从头到尾敲一遍。坚持看完一本书后,信心就上来了。先保证看完一本再说,看完3本后,基本应该能做到几天就能看一本了。万事开头难,加油吧。 第四个问题,看书的层次问题。 书看完后,要自己总结,要与其他书籍对比看。有同学同时对比着看《权威指南》和《高设》来的。随便拿出个知识点,你都能闭着眼睛说得头头是道,说明水平够了。 下一块就是源码的学习了。看框架源码之前,想说一件事情:dom的api不懂,没问题,你可以百度。 但是正则一定要先研究研究,不然大多数人去尝试分析源码时,遇到的挫折都在于此。 怎么去阅读源码呢?敲,照着敲。 有哪些代码值得去敲呢?优秀框架或者库的源码都值得你去敲。但是拿jq来敲,来入门,那不行的。原因:太他么长了。八九千行呢!! 个人觉得underscore.js库是不错的第一个选择。原因都是工具方法,敲完以后自己的水平应该略有小成吧。其实有一些api的实现,你要把它当成getElementById一样,深深的印在脑海里。比如extend方法,必须张口就来。敲完underscore库后,可以考虑去看看《javascript函数式编程》这本书了。 jq的源码不好敲。那么zepto的源码比较少1800多行,敲一天应该敲完了。敲几遍后,把所有不懂的地方,都百度清楚,然后就可以写自己的类jq的库了。然后就可以作为一项技能写进自己的简历里。比如“创建过自己的jquery库”。当然敲的过程,还能帮助自己对jq的api认识。 然后是backbone.js,因为此框架是以类jq和underscore为基础的mvc框架。代码也没多少行。敲吧。spine.js与backbone类似。可以在敲其之前,先看看那本《基于MVC的JavaScript Web富应用开发》。希望你的简历可以添加这么一笔,“创建过自己的mvc框架”。 其他的,我也敲过一些。包括jq.validate.js,包括一些插件。如果你愿意的话,bootstrap你可以去敲敲啊。源码挺多的,可以按插件逐个来敲。分析明白了,轮播、分页、下拉框等等的插件那还不是分分钟随手就写一个了。最起码看看人家api接口是怎么设计的也是极好的。话说个人在阅读其css代码中,也学到了不少东西。说到插件,有两个必须提提,一个是表格插件,一个是树。都敲完,简历里可以这么写上,“创建过自己的UI框架”。 当然了,你也可以敲你喜欢的框架代码,重要的是明白其实现原理,最好理解其为啥那么设计,如果对设计模式比较熟悉的话,会经常发现原来是这么回事。 照着敲只是分析源码的入门,用途也是为了学习,最后能用在自己的项目中,那是才是正道。就算没啥用,也是打发时间的好方式,比看电视剧强多了。我闲着无聊时,就背着敲underscore源码。最后说句,如果你简历上能如期写上那几句话后,必须是大神。加油吧。 后记 写本文的最初目的,正如文章开头说的那样,方便自己回复大家的提问。本站的任何一篇分享学习经验的文章,基本都会引起共鸣,这确实是一个值得讨论的话题。这里再说说几个事情。 >有人问我前端工作经验事情。 没几年。三年多。 >初学者或新手(beginner)怎么办? 看书和分析源码是重要的提高方式,但不适合新手。新手需要的是能快速的入门和入行,能快速的上手工作。一种快捷的学习方式就是看视频。正如有的同学说得那样,知道有哪些东西,怎么用就可以了。 看视频是有好处的,首先它是一种被动学习方式。我最开始的入门也是看视频来的,只需要看就行了。一遍没懂,再放一遍,我基本上是1.5倍数去看的。 而读书是一种主动方式,需要自己一页一页翻。需要自己主动的去理解。而很多东西,也许只是视频老师一句话,就能突出的重点,需要我们自己去解读。还有另一件事情是,比如发现自己某个知识点不太清楚,可以单独去百度。比如this,文章很多的。这种学习方式也是快速掌握知识点的好办法。 书籍需要技术评审,那么看文章一定要看看评论。不过视频就不好说了,视频一般都不会讲得太深入,偶尔也有讲错的。当年我也曾被一些视频误导过,建议找不错的视频看看。各大网站培训机构的免费视频挺多的。 >看书和分析源码的时机。 但已经工作一年半载时,正是提高的好时候,此时可以去看书了。全面系统的梳理知识点,扫清自己的盲区。如果只是靠项目经验是不够的,通过项目来学习,那肯定是必须的,工作本身就是一个学习的过程。 但是工作三年不看书的话,学又能学到多少呢?更何况每个项目都很类似,一直处在舒适区,那真就是5年经验重复第一年的了。所以我不认同这句话:面试时强调自己的学习能力是工作能力不强的表现。3年经验的水平,完全有可能超过5年的。 >没有时间去学习? 如果你还没毕业,就已经天天在本站混了,其实你领先了一大步。都是混过大学的,天天充斥着lol和电视剧的陪伴,我只想说进入社会是要还的。最可怕的是什么呢?该还、还不还(这几个字别念错了)。时间是有的,就看你愿意付出不。下班后学习,周末学习,节假日别人玩的时候,在家敲代码,这样才能领先别人。 >兴趣问题? 兴趣和擅长是一个良性迭代循环。你擅长某件事情,就会越喜欢它,越喜欢,就越愿意花时间,进而越擅长。此道理都懂,只是缺乏一个trigger。 如果你喜欢玩游戏的话,其实你可能非常适合做前端。玩游戏就是一个反馈机制,前端工作的反馈,相对其他工作来说也是非常及时的。代码一改,网页一刷,就看到效果了。擅长、优越感、成就感通常都是连在一起的。每看完一本书,我都觉得很有成就感。每敲完一个库,也有成就感。 以上纯属一家之言,每个人的学习习惯、方式、态度都不一样。先端正态度、找到自己的学习方法,进而养成坚持下去的习惯。最后说一句,你我共勉:只要你走在正确的道路上,不管、走得多慢,都是前进! 本文完。 不好意思,不小心打开了前置摄像头! 文章来源:统一回复《怎么学JavaScript?》作者:前端网老姚(转载已获得作者许可)
1.介绍 http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs. 如果你不想重复的写 nodeJs 的 web-server.js, 则可以使用这个. 2.安装 npm install -g http-server 安装成功如下: 3.使用 在站点目录下开启命令行输入 http-server 运行结果如图: 在浏览器输入 http://localhost:8080/ 结果如下: 4.http-server一些参数介绍 -p 端口号 (默认 8080) -a IP 地址 (默认 0.0.0.0) -d 显示目录列表 (默认 'True') -i 显示 autoIndex (默认 'True') -e or –ext 如果没有提供默认的文件扩展名(默认 'html') -s or –silent 禁止日志信息输出 –cors 启用 CORS via the Access-Control-Allow-Origin header -o 在开始服务后打开浏览器 -c 为 cache-control max-age header 设置Cache time(秒) , e.g. -c10 for 10 seconds (defaults to '3600'). 禁用 caching, 则使用 -c-1. -U 或 –utc 使用UTC time 格式化log消息 -P or --proxy Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com -S or –ssl 启用 https -C or –cert ssl cert 文件路径 (default: cert.pem) -K or –key Path to ssl key file (default: key.pem). -r or –robots Provide a /robots.txt (whose content defaults to 'User-agent: *\nDisallow: /') -h or –help 打印以上列表并退出
阅读目录 什么是跨域 常用的几种跨域处理方法: 跨域的原理解析及实现方法 总结 摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让自己搞懂这方面的知识,走起。 回到顶部 什么是跨域 在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。 JavaScript这个安全策略在进行多iframe或多窗口编程、以及Ajax编程时显得尤为重要。根据这个策略,在baidu.com下的页面中包含的JavaScript代码,不能访问在google.com域名下的页面内容;甚至不同的子域名之间的页面也不能通过JavaScript代码互相访问。对于Ajax的影响在于,通过XMLHttpRequest实现的Ajax请求,不能向不同的域提交请求,例如,在abc.example.com下的页面,不能向def.example.com提交Ajax请求,等等。 为什么浏览器要实现同源限制?我们举例说明: 比如一个黑客,他利用iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名和密码登录时,如果没有同源限制,他的页面就可以通过javascript读取到你的表单中输入的内容,这样用户名和密码就轻松到手了. 又比如你登录了OSC,同时浏览了恶意网站,如果没有同源限制,该恶意 网站就可以构造AJAX请求频繁在OSC发广告帖. 跨域的情况分为以下几种: 特别注意两点: 1、如果是协议和端口造成的跨域问题“前台”是无能为力的 2、在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。比如上面的,http://www.a.com/a.js和http://70.32.92.74/b.js。虽然域名和域名的ip对应,不过还是被认为是跨域。 “URL的首部”指window.location.protocol +window.location.host。其中, window.location.protocol:指含有URL第一部分的字符串,如http: window.location.host:指包含有URL中主机名:端口号部分的字符串.如//www.cenpok.net/server/ 回到顶部 常用的几种跨域处理方法: 1、JSONP 2、CORS策略 3、document.domain+iframe的设置 4、HTML5的postMessage 5、使用window.name来进行跨域 回到顶部 跨域的原理解析及实现方法 1、JSONP(JSON with padding) 原理 : 我们知道,在页面上有三种资源是可以与页面本身不同源的。它们是:js脚本,css样式文件,图片,像淘宝等大型网站,肯定会将这些静态资源放入cdn中,然后在页面上连 接,如下所示,所以它们是可以链接访问到不同源的资源的。 1)<script type="text/javascript" src="某某cdn地址" ></script> 2)<link type="text/css" rel="stylesheet" href="某个cdn地址" /> 3)<img src="某个cdn地址" alt=""/> 而jsonp就是利用了script标签的src属性是没有跨域的限制的,从而达到跨域访问的目的。因此它的最基本原理就是:动态添加一个<script>标签来实现。 实现方法: 这里是使用ajax来请求的,看起来和ajax没啥区别,其实还是有区别的。 ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。 $.ajax({ url:"http://crossdomain.com/services.php", dataType:'jsonp', data:'', jsonp:'callback', success:function(result) { // some code } }); 上面的代码中,callback是必须的,callback是什么值要跟后台拿。获取到的jsonp数据格式如下: flightHandler({ "code": "CA1998", "price": 1780, "tickets": 5 }); jsonp的全称为json with padding,上面的数据中,flightHandler就是那个padding. JSONP的不足之处: 1、只能使用get方法,不能使用post方法: 我们知道 script,link, img 等等标签引入外部资源,都是 get 请求的,那么就决定了 jsonp 一定是 get 的。但有时候我们使用的 post 请求也成功,为啥呢?这是因为当我们指定dataType:'jsonp',不论你指定:type:"post" 或者type:"get",其实质上进行的都是 get 请求! 2、没有关于 JSONP 调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或重新开始请求。不过,等待一段时间还没有响应的话,就不用理它了。 2、CORS策略 原理: CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。它为Web服务器定义了一种方式,允许网页从不同的域访问其资源. CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。 实现方法: CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 前端方面 以前我们使用Ajax,代码类似于如下的方式: var xhr = new XMLHttpRequest(); xhr.open("GET", "/hfahe", true); xhr.send(); // 这里的“/hfahe”是本域的相对路径。 如果我们要使用CORS,相关Ajax代码可能如下所示: var xhr = new XMLHttpRequest(); xhr.open("GET", "http://blog.csdn.net/hfahe", true); xhr.send(); // 请注意,代码与之前的区别就在于相对路径换成了其他域的绝对路径,也就是你要跨域访问的接口地址。 服务器方面服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。 CORS策略的优缺点: 优点: 1、CORS支持所有类型的HTTP请求。 2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。 缺点: 兼容性方面相对差一点,ie10或以上才支持 3、document.domain+iframe的设置 (只有在主域相同的时候才能使用该方法) 原理: 浏览器中不同域的框架之间是不能进行js的交互操作的。但是不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但是,我们也只能获取到一个几乎 无用的window对象。比如,有一个页面,它的地址是 http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是 http://example.com/b.html , 很显然,这 个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。 这个时候,document.domain就可以派上用场了,我们只要把 http://www.example.com/a.html 和 http://example.com/b.html 这两个页面的document.domain都设成 相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如: a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是 当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。 使用方法: 比如在http://www.example.com/a.html 的页面里要访问 http://example.com/b.html里面的东西。 在页面 http://www.example.com/a.html 中设置document.domain: //http://www.example.com/a.html <html> <head> <title>A页面</title> <script type="text/javascript" src="jquery.js"></script> </head> <body> <div>A页面</div> <iframe id="iframe" src="http://example.com/b.html" style="display:none;"></iframe> // 相当于用一个隐藏的iframe来做代理 <script> $(function(){ try{ document.domain = "example.com"; //这里将document.domain设置成一样 }catch(e){} $("#iframe").load(function(){ var iframe = $("#iframe").contentDocument.$; ifram.get("http://example.com/接口",function(data){}); }); }); </script> <body> </html> 在页面 http://example.com/b.html 中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值: //http://example.com/b.html <html> <head> <title>B页面</title> <script type="text/javascript" src="jquery.js"></script> </head> <body> <div>B页面</div> <script> $(function(){ try{ document.domain = "example.com"; //这里将document.domain设置成一样 }catch(e){} }); </script> </body> </html> 这里有个注意点,就是在A页面中,要等iframe标签完成加载B页面之后,再取iframe对象的contentDocument,否则如果B页面没有被iframe完全加载,在A页面中通过contentDocument属性就取不到B页面中的jQuery对象。 一旦取到B页面中的jQuery对象,就可以直接发ajax请求了,这种类似“代理”方式可以解决主子域的跨域问题。 缺点: 只有在主域相同的时候才能使用该方法 4、HTML5的postMessage 原理: 没啥原理,就是一个html5所提供的一个API.--->HTML5 window.postMessage是一个安全的、基于事件的消息API。 在需要发送消息的源窗口调用postMessage方法即可发送消息。其中.源窗口可以是全局的window对象,也可以是以下类型的窗口: 1、文档窗口中的iframe: var iframe = document.getElementById('my-iframe'); var win = iframe.documentWindow; 2、JavaScript打开的弹窗: var win = window.open(); 3、当前文档窗口的父窗口: var win = window.parent; 4、 var win = window.opener(); 发送消息:找到源window对象后,即可调用postMessage API向目标窗口发送消息: win.postMessage(msg, targetOrigin); 说明:postMessage函数接收两个参数: 1、msg, 将要发送的消息,可以使一切javascript参数,如字符串,数字,对象,数组等。 2、targetOrigin,这个参数称作“目标域”,注意,是目标域不是本域!比如,你想在2.com的网页上往1.com网页上传消息,那么这个参数就是“http://1.com/”,而不是2.com.协议,(一个完整的域名包括:主机名,端口号。如:http://g.cn:80/) 接收消息:那目标窗口要怎么接收传过来的数据呢,只要监听window的message事件就可以接收了。 var onmessage = function (event) { var data = event.data; var origin = event.origin; //do someing }; if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', onmessage, false); } else if (typeof window.attachEvent != 'undefined') { //for ie window.attachEvent('onmessage', onmessage); } message事件监听函数接收一个参数,Event对象实例,该对象有三个属性: data : 消息 origin:消息的来源地址 source:发送消息窗口的window对象引用 使用方法(案例): http://test.com/index.html--> 发送消息的页面 <!-- 这个是 http://test.com/index.html 页面 --> <div> <!-- 要给下面的页面传一个妹子过去 --> <iframe id="child" src="http://lsLib.com/lsLib.html"></iframe> </div> <script type="text/javascript"> window.onload=function(){ window.frames[0].postMessage('苍老师','http://lslib.com'); } </script> http://lslib.com/lslib.html --> 接收消息的页面 <!-- 这个是 http://lslib.com/lslib.html 页面 --> <script type="text/javascript"> window.addEventListener('message',function (e) { console.log(e.origin,e.data); alert('收到妹子一枚:'+e.data); }); </script> 优缺点: 优点:方便,安全,有效的解决了跨域问题 缺点:万恶的资本主义,ie8+才支持,而且ie8+<ie10只支持iframe的方式。 5、使用window.name来进行跨域(相对比较完美的方法) 原理: 当iframe的页面跳到其他地址时,其window.name值保持不变,并且可以支持非常长的 name 值(2MB)。 浏览器跨域iframe禁止互相调用/传值.但是调用iframe时 window.name 却不变,正是利用这个特性来互相传值,当然跨域下是不容许读取ifram的window.name值. 所以这里我们还要准备一个和主页面http://www.a.com/main.html 相同域下的代理页面http://www.a.com/other.html ,iframe调用子页面 http://www.b.com/data.html 使用方法: 1、 准备三个页面: http://www.a.com/main.html //应用页面 http://www.a.com/other.html // 代理页面,要求和应用页面在同一个域。一般是一个空的html http://www.b.com/data.html //应用页面获取数据的页面,简称:数据页面 2、 数据页面将数据传到window.name中去。如下: http://www.b.com/data.html中的 data.html // data.htmlwindow.name="苍老师"; //可以是其他类型的数据,比如数组,对象等等 http://www.a.com/main.html //应用页面的代码如下: <!-- main.html --> var iframeData; var state = 0;//开关变量 var iframe = document.createElement('iframe'); //创建iframe var loadfn = function() { if (state === 1) { iframeData = iframe.contentWindow.name; // 读取数据 alert('获取到了iframe传过来的妹子'+iframeData); }else if (state === 0) { state = 1; iframe.contentWindow.location = 'http://www.a.com/other.html'; //这里是代理页面 other.html /** 这里说明一下: 由于iframe的location改变了,相当于重新载入页面(这是iframe的性质决定的),于是重新执行loadfn方法。 由于当iframe的页面跳到其他地址时,其window.name值保持不变,并且此时开关变量 state已经变为1, 于是就可以获取到window.name值,也就达到了跨域访问的目的了。 **/ }; } iframe.src = 'http://www.b.com/data.html'; //这是是数据页面,data.html if (iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe); 3、获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。 iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); 优缺点: 浏览器支持情况好,是比较普遍的使用方法 回到顶部 总结 以上总结了js跨域的几种方法,当然还有其他的方法,不过没有。他们各有千秋。其实最主要的区别除了实现方式不一样,主要是浏览器的兼容问题而已。 JSONP: JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。 JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。 CORS策略 优点:使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。 缺点:古老的浏览器不支持,不过大部分现代浏览器都支持 document.domain+iframe:只适用于主域相同的跨域问题处理 html5的postMessage: 优点:是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。如果是现代浏览器,首选。 缺点: ie8以前不支持 window.name: 主要是应用当frame的页面跳到其他地址时,其window.name值保持不变的原理。兼容性好。需要照顾落后的浏览器时,首选。、 小小总结,有误之处,欢迎指出 如果您觉得文章有用,也可以给咸鱼老弟发个微信小额红包鼓励,
‘debugger;’ 除了console.log,debugger就是另一个我很喜欢的快速调试的工具,将debugger加入代码之后,Chrome会自动在插入它的地方停止,很像C或者Java里面打断点。你也可以在一些条件控制中插入该调试语句,譬如: if (thisThing) { debugger; } 将Objects以表格形式展示 有时候我们需要看一些复杂的对象的详细信息,最简单的方法就是用console.log然后展示成一个列表状,上下滚动进行浏览。不过似乎用console.table展示成列表会更好呦,大概是介个样子: var animals = [ { animal: 'Horse', name: 'Henry', age: 43 }, { animal: 'Dog', name: 'Fred', age: 13 }, { animal: 'Cat', name: 'Frodo', age: 18 }]; console.table(animals); 多屏幕尺寸测试 Chrome有一个非常诱人的功能就是能够模拟不同设备的尺寸,在Chrome的Inspector中点击toggle device mode按钮,然后就可以在不同的设备屏幕尺寸下进行调试咯: 在Console快速选定DOM元素 在Elements选择面板中选择某个DOM元素然后在Console中使用该元素也是非常常见的一个操作,Chrome Inspector会缓存最后5个DOM元素在它的历史记录中,你可以用类似于Shell中的$0等方式来快速关联到元素。譬如下图的列表中有‘item-4′, ‘item-3’, ‘item-2’, ‘item-1’, ‘item-0’这几个元素,你可以这么使用: 获取某个函数的调用追踪记录 JavaScript框架极大方便了我们的开发,但是也会带来大量的预定义的函数,譬如创建View的、绑定事件的等等,这样我们就不容易追踪我们自定义函数的调用过程了。虽然JavaScript不是一个非常严谨的语言,有时候很难搞清楚到底发生了啥,特别是当你需要审阅其他人的代码的时候。这时候console.trace就要起作用咯,它可以帮你进行函数调用的追踪。譬如下面的代码中我们要追踪出car对象中对于funcZ的调用过程: var car; var func1 = function() { func2(); } var func2 = function() { func4(); } var func3 = function() { } var func4 = function() { car = new Car(); car.funcX(); } var Car = function() { this.brand = ‘volvo’; this.color = ‘red’; this.funcX = function() { this.funcY(); } this.funcY = function() { this.funcZ(); } this.funcZ = function() { console.trace(‘trace car’) } } func1(); 这边就可以清晰地看出func1调用了func2,然后调用了func4,func4创建了Car的实例然后调用了car.funcX。 格式化被压缩的代码 有时候在生产环境下我们发现了一些莫名奇妙的问题,然后忘了把sourcemaps放到这台服务器上,或者在看别人家的网站的源代码的时候,结果就看到了一坨不知道讲什么的代码,就像下图。Chrome为我们提供了一个很人性化的反压缩工具来增强代码的可读性,大概这么用: 快速定位调试函数 当我们想在函数里加个断点的时候,一般会选择这么做: 在Inspector中找到指定行,然后添加一个断点 在脚本中添加一个debugger调用 不过这两种方法都存在一个小问题就是都要到对应的脚本文件中然后再找到对应的行,这样会比较麻烦。这边介绍一个相对快捷点的方法,就是在console中使用debug(funcName)然后脚本会在指定到对应函数的地方自动停止。这种方法有个缺陷就是无法在私有函数或者匿名函数处停止,所以还是要因时而异: var func1 = function() { func2(); }; var Car = function() { this.funcX = function() { this.funcY(); } this.funcY = function() { this.funcZ(); } } var car = new Car(); 禁止不相关的脚本运行 当我们开发现代网页的时候都会用一些第三方的框架或者库,它们几乎都是经过测试并且相对而言Bug较少的。不过当我们调试我们自己的脚本的时候也会一不小心跳到这些文件中,引发额外的调试任务。解决方案呢就是禁止这部分不需要调试的脚本运行,详情可见这篇文章:: javascript-debugging-with-black-box。 在较复杂的调试情况下发现关键元素 在一些复杂的调试环境下我们可能要输出很多行的内容,这时候我们习惯性的会用console.log, console.debug, console.warn, console.info, console.error这些来进行区分,然后就可以在Inspector中进行过滤。不过有时候我们还是希望能够自定义显示样式,你可以用CSS来定义个性化的信息样式: console.todo = function(msg) { console.log(‘ % c % s % s % s‘, ‘color: yellow; background - color: black;’, ‘–‘, msg, ‘–‘); } console.important = function(msg) { console.log(‘ % c % s % s % s’, ‘color: brown; font - weight: bold; text - decoration: underline;’, ‘–‘, msg, ‘–‘); } console.todo(“This is something that’ s need to be fixed”); console.important(‘This is an important message’); 在console.log()中你可以使用%s来代表一个字符串 , %i 来代表数字, 以及 %c 来代表自定义的样式。 监测指定函数的调用与参数 在Chrome中可以监测指定函数的调用情况以及参数: var func1 = function(x, y, z) { }; 这种方式能够让你实时监控到底啥参数被传入到了指定函数中。 Console中使用$进行元素查询 在Console中也可以使用来进行类似于querySelector那样基于CSS选择器的查询,(‘css-selector’) 会返回满足匹配的第一个元素,而$$(‘css-selector’) 会返回全部匹配元素。注意,如果你会多次使用到元素,那么别忘了将它们存入变量中。 Postman 很多人习惯用Postman进行API调试或者发起Ajax请求,不过别忘了你浏览器自带的也能做这个,并且你也不需要担心啥认证啊这些,因为Cookie都是自带帮你传送的,这些只要在network这个tab里就能进行,大概这样子: DOM变化检测 DOM有时候还是很操蛋的,有时候压根不知道啥时候就变了,不过Chrome提供了一个小的功能就是当DOM发生变化的时候它会提醒你,你可以监测属性变化等等:
随机复制下面的几四个注册码 粘贴到sublime text 3(Build 3103)注册框 就可以了! 第一个--first licence key : ======================================== —– BEGIN LICENSE —– Michael Barnes Single User License EA7E-821385 8A353C41 872A0D5C DF9B2950 AFF6F667 C458EA6D 8EA3C286 98D1D650 131A97AB AA919AEC EF20E143 B361B1E7 4C8B7F04 B085E65E 2F5F5360 8489D422 FB8FC1AA 93F6323C FD7F7544 3F39C318 D95E6480 FCCC7561 8A4A1741 68FA4223 ADCEDE07 200C25BE DBBC4855 C4CFB774 C5EC138C 0FEC1CEF D9DCECEC D3A5DAD1 01316C36 —— END LICENSE —— ======================================== 第二个--second licence key : ======================================== —– BEGIN LICENSE —– Nicolas Hennion Single User License EA7E-866075 8A01AA83 1D668D24 4484AEBC 3B04512C 827B0DE5 69E9B07A A39ACCC0 F95F5410 729D5639 4C37CECB B2522FB3 8D37FDC1 72899363 BBA441AC A5F47F08 6CD3B3FE CEFB3783 B2E1BA96 71AAF7B4 AFB61B1D 0CC513E7 52FF2333 9F726D2C CDE53B4A 810C0D4F E1F419A3 CDA0832B 8440565A 35BF00F6 4CA9F869 ED10E245 469C233E —— END LICENSE —— ======================================== 第三个--third licence key : ======================================== —– BEGIN LICENSE —– Anthony Sansone Single User License EA7E-878563 28B9A648 42B99D8A F2E3E9E0 16DE076E E218B3DC F3606379 C33C1526 E8B58964 B2CB3F63 BDF901BE D31424D2 082891B5 F7058694 55FA46D8 EFC11878 0868F093 B17CAFE7 63A78881 86B78E38 0F146238 BAE22DBB D4EC71A1 0EC2E701 C7F9C648 5CF29CA3 1CB14285 19A46991 E9A98676 14FD4777 2D8A0AB6 A444EE0D CA009B54 —— END LICENSE —— ======================================== 第四个--fourth licence key : ======================================== —– BEGIN LICENSE —– Alexey Plutalov Single User License EA7E-860776 3DC19CC1 134CDF23 504DC871 2DE5CE55 585DC8A6 253BB0D9 637C87A2 D8D0BA85 AAE574AD BA7D6DA9 2B9773F2 324C5DEF 17830A4E FBCF9D1D 182406E9 F883EA87 E585BBA1 2538C270 E2E857C2 194283CA 7234FF9E D0392F93 1D16E021 F1914917 63909E12 203C0169 3F08FFC8 86D06EA8 73DDAEF0 AC559F30 A6A67947 B60104C6 —— END LICENSE —— ========================================
远程序桌面登录的.NET开发,可以使用MSTSCLib.dll和MsTscAxWrapper.dll两个转换过的动态库,而无需使用WINDOWS自带的OCX,因为使用OCX开发可以会遇到其它问题。 主要代码: public AxMSTSCLib.AxMsRdpClient rdpClient; rdpClient = new AxMSTSCLib.AxMsRdpClient(); rdpClient.Dock = DockStyle.Fill; rdpClient.Width = Screen.PrimaryScreen.Bounds.Width;//控件宽度 rdpClient.Height = Screen.PrimaryScreen.Bounds.Height;//控件宽度 this.Controls.Add(rdpClient); rdpClient.Server = this.StrIP;//服务器地址 rdpClient.UserName = this.StrName; rdpClient.AdvancedSettings2.RDPPort = 3389; rdpClient.AdvancedSettings2.ClearTextPassword = this.StrPwd;//密码 rdpClient.ColorDepth = 16;//颜色位数 rdpClient.FullScreen = true;//是否全屏 rdpClient.Connect(); 了解远程桌面协议 (RDP) < type="text/javascript"> 远程桌面协议 (RDP),用于终端服务器和终端服务器客户端之间的通信。RDP 被封装并在 TCP 加密。 远程桌面协议基于,并是对扩展 T 120 系列的协议标准。 多声道支持协议用于单独的虚拟通道储存的演示文稿数据、 串行设备通信,授权信息、 高加密的数据 (键盘、 鼠标活动) 等。 因为 RDP 是核心 T.share 协议的扩展,多个其他功能将保留作为 RDP,如体系结构支持多点 (多方会话) 所需要的功能的一部分。 multipoint 数据传递允许从应用程序在"实时多方传递而不必向单独 (例如,虚拟 Whiteboards) 的每个会话发送相同的数据的数据。 Windows 终端服务器此第一个版本中但是,我们将集中提供可靠而快速点对点单会话通信。 只有一个数据信道将可以中使用最初发布的终端服务器 4.0 但是,RDP 的灵活性未来的产品中提供大量的功能的空间。 Microsoft 决定实现 RDP 连接用于 Windows NT 终端服务器中的一个原因是它提供用于生成许多更多的功能非常可扩展基础。 这是因为 RDP 用于数据传输 64,000 独立的通道。 但是,当前传输活动只使用单个频道 (对于键盘、 鼠标和演示文稿数据)。 而且,RDP 旨在支持多种不同类型的网络拓扑 (例如 ISDN、 POTS 和多个的 LAN 协议 (如 IPX、 NetBIOS、 TCP/IP,等)。 当前版本的 RDP 将只运行 TCP/IP 上,但客户反馈的其他协议支持可以添加在将来版本。 参与发送和接收数据通过 RDP 堆栈活动实质上是作为七层 OSI 模型标准公共 LAN 网络现在相同的。 数据从应用程序或服务传输经过下协议堆栈 sectioned、 定向到一个通道 (通过 MCS)、 加密、 换行,帧、 打包到在的网络协议和最后解决,会上传送缆线到客户端。 返回的数据相同方式仅中有效反向,使用数据包被去除的它的地址,然后 unwrapped 解密,依此类推直到数据提供给应用程序使用。 协议堆栈修改关键部分发生第四个和第七个层,其中数据的加密、 换行和帧、 定向到一个通道和确定优先级别之间。 为应用程序开发人员的要点之一是,使用 RDP,Microsoft 有抽象立即处理该协议栈的复杂性。 这样,他们只需编写干净的、 精心设计、 良好 32 位应用程序并且 RDP 堆栈由终端服务器和它的客户端连接实现负责其余工作。 有关应用程序在终端服务器上的交互方式以及要注意开发用于 Windows 终端服务器基础结构的应用程序时的详细信息,查看"优化 Windows NT Server 4.0,终端服务器版的应用程序"白皮书。 值得讨论 RDP 堆栈实例中的四个组件是 multipoint 的通信服务 (MCSMUX)、 泛型的会议控制 (GCC)、 Wdtshare.sys 和 Tdtcp.sys。 MCSmux 和 GCC 都在国际电信联合 (ITU) T 120 系列。 MCS 由两种标准组成: 它定义了多点服务的 T.122 和指定数据传输协议的 T.125。 MCSMux 控制通道 (由到协议中的预定义虚拟通道的 multiplexing 数据) 的工作分配、 优先级和分段发送的数据。 实质上,提取多个的 RDP 堆栈到单个实体从该 GCC 的角度。 GCC 负责管理这些多个频道。 在 GCC 允许创建和删除会话连接和控件资源提供 MCS。 每个终端服务器协议 (当前,唯一的 RDP 和 Citrix 的 ICA 支持) 将有一个协议堆栈实例加载 (等待连接请求一个侦听程序堆栈)。 终端服务器设备驱动程序协调和管理 RDP 协议活动和为较小组件组成,UI 传输、 压缩、 加密、 帧和等等的 RDP 驱动程序 (Wdtshare.sys),和传输驱动程序 (Tdtcp.sys) 将打包到基础协议网络 TCP/IP 协议。 RDP 是完全独立的基础的传输堆栈,此案例的 TCP/IP 中的开发的。 在完全独立的传输堆栈的 RDP,表示为它们的客户需要增加了很少或没有重要更改协议的基本部分我们可以添加其他网络协议的其他传输驱动程序。 这些是 RDP 的性能和网络上的 extendibility 的关键元素。
一直对Javascript中的this都有一种似是而非的感觉,今天突然感觉豁然开朗,特此记录一下。 咱们先看个栗子: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>this的使用</title> <script type="text/javascript"> var Car,tesla; Car=function () { this.start=function(){ console.log('car started'); }; this.turnKye=function () { var carKey=document.getElementById('car_key'); carKey.onclick=function () { this.start(); }; } return this; } tesla=new Car(); tesla.turnKye(); </script> </head> <body> <input type="button" id="car_key" value="test" /> </body> </html> 咋一看这段代码没有什么问题,但是由于对于this的错误理解最终导致错误的结果。我们在元素car_key上面绑定了click事件,认为在car的类中嵌套绑定click事件就可以让这个dom元素访问car的this上下文。这种方式看起来很合理,但是不幸的是它并不工作。 在Javascript中,this关键字总是指向正执行的作用域的所有者。 请大家仔细揣摩上面那句话。正如我们所知,函数调用会产生新的作用域,一点onclick事件被触发,this就指向了dom元素而不是Car的类。 那我们怎么做才会让它能正常工作呢?我们通常会把this赋值给一个局部的自由变量(比如that,_this,self,me等,这个在许多的框架里面都有体现)来避开作用域带来的问题。这里使用局部变量来重写之前的方法: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>this的使用</title> </head> <body> <input type="button" id="car_key" value="test" /> <script type="text/javascript"> var Car,tesla; Car=function () { this.start=function(){ console.log('car started'); }; this.turnKye=function () { var that=this; var carKey=document.getElementById('car_key'); carKey.onclick=function () { that.start(); }; } return this; } tesla=new Car(); tesla.turnKye(); </script> </body> </html> 由于that是一个自由变量,onclick事件的出发并不会引起它的重新定义。 如果你熟悉ES6的话可以使用胖箭头符号,这更简洁和更容易理解,如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>this的使用</title> </head> <body> <input type="button" id="car_key" value="test" /> <script type="text/javascript"> var Car,tesla; Car=function () { this.start=function(){ console.log('car started'); }; this.turnKye=function () { //var that=this; var carKey=document.getElementById('car_key'); //carKey.onclick=function () { // that.start(); //}; carKey.onclick=()=>this.start(); } return this; } tesla=new Car(); tesla.turnKye(); </script> </body> </html> 当然我们也可以使用绑定函数的方法来解决这个问题:如下 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>this的使用</title> </head> <body> <input type="button" id="car_key" value="test" /> <script type="text/javascript"> var Car,tesla; Car=function () { this.start=function(){ console.log('car started'); }; var click=function(){ this.start(); } this.turnKye=function () { //var that=this; var carKey=document.getElementById('car_key'); carKey.onclick=click.bind(this); } return this; } tesla=new Car(); tesla.turnKye(); </script> </body> </html> 其实这些在学习React的时候,绑定事件的时候遇到的坑,那时候只知道这么写,不知道怎么回事,今天突然感觉豁然开朗。希望对大家有所帮助。
<!-- Insert this line above script imports --> <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> <!-- normal script imports etc --> <script src="scripts/jquery.min.js"></script> <script src="scripts/vendor.js"></script> <!-- Insert this line after script imports --> <script>if (window.module) module = window.module;</script> Benefits Works for both browser and electron with the same code Fixes issues for ALL 3rd-party libraries (not just jQuery) without having to specify each one Script Build / Pack Friendly (i.e. Grunt / Gulp all scripts into vendor.js) Does NOT require node-integration to be false source here
在这里简要的说一下这些语言新特性对 React 应用的开发有什么影响,这些 ES6+ 特性使得 React 开发更简单更有趣。 类 迄今为止,最能体现我们使用 ES6+ 来编写 React 组件的就是我们选择使用类定义语法。替代了使用 React.createClass 方法来定义一个组件,我们可以定义一个 bonafide ES6 类来扩展 React.Component: ? 1 2 3 4 5 class Photo extends React.Component { render() { return <img alt={this.props.caption} src={this.props.src} />; } } 现在,你就会发现一个微妙的差异 —— 当使用定义类的时候语法更简洁: ? 1 2 3 4 5 // The ES5 way var Photo = React.createClass({ handleDoubleTap: function(e) { … }, render: function() { … }, }); ? 1 2 3 4 5 // The ES6+ way class Photo extends React.Component { handleDoubleTap(e) { … } render() { … } } 值得关注的是,我们去掉了两个括号和一个分号,每个方法声明我们省略了一个冒号,一个关键字和一个分号。 当使用新的类定义时,所有的生命周期方法至少有一个是符合你期望的。类的 constructor 现在假设 role 之前是通过 componentWillMount 填充的: ? 1 2 3 4 // The ES5 way var EmbedModal = React.createClass({ componentWillMount: function() { … }, }); ? 1 2 3 4 5 6 7 // The ES6+ way class EmbedModal extends React.Component { constructor(props) { super(props); // Operations usually carried out in componentWillMount go here } } 属性初始化程序 在 ES6+ 类的世界里,prop types 和 defaults live 在类自身作为静态属性。这些,在组件的初始化状态也是一样的,可以使用 ES7 property initializers 定义: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // The ES5 way var Video = React.createClass({ getDefaultProps: function() { return { autoPlay: false, maxLoops: 10, }; }, getInitialState: function() { return { loopsRemaining: this.props.maxLoops, }; }, propTypes: { autoPlay: React.PropTypes.bool.isRequired, maxLoops: React.PropTypes.number.isRequired, posterFrameSrc: React.PropTypes.string.isRequired, videoSrc: React.PropTypes.string.isRequired, }, }); ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // The ES6+ way class Video extends React.Component { static defaultProps = { autoPlay: false, maxLoops: 10, } static propTypes = { autoPlay: React.PropTypes.bool.isRequired, maxLoops: React.PropTypes.number.isRequired, posterFrameSrc: React.PropTypes.string.isRequired, videoSrc: React.PropTypes.string.isRequired, } state = { loopsRemaining: this.props.maxLoops, } } ES7 属性初始化程序操作内部类的 constructor,this 指向 construction 的类实例,所以初始化状态可以依赖于 this.props。值得关注的是,我们不再定义 prop 默认值和使用 getter 函数初始化状态对象。 Arrow 函数 React.createClass 方法用来在你的组件实例方法中执行一些额外的绑定工作,为了确保 this 关键字会指向组件实例: ? 1 2 3 4 5 6 7 // Autobinding, brought to you by React.createClass var PostInfo = React.createClass({ handleOptionsButtonClick: function(e) { // Here, 'this' refers to the component instance. this.setState({showOptionsModal: true}); }, }); 自从我们不参与 React.createClass 方法,而是使用 ES6+ 类语法定义组件,看似需要手动绑定实例方法: ? 1 2 3 4 5 6 7 8 9 10 11 12 // Manually bind, wherever you need to class PostInfo extends React.Component { constructor(props) { super(props); // Manually bind this method to the component instance... this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this); } handleOptionsButtonClick(e) { // ...to ensure that 'this' refers to the component instance here. this.setState({showOptionsModal: true}); } } 幸运的是,通过绑定两个 ES6+ 特性 – arrow functions 和属性初始化程序 – 可以选择绑定组件实例: ? 1 2 3 4 5 class PostInfo extends React.Component { handleOptionsButtonClick = (e) => { this.setState({showOptionsModal: true}); } } ES6 的 arrow 函数体分享相同的词 this,用这来围绕他们的代码,这些可以达到我们预期的结果,也是 ES7 属性初始化程序在域内的方式。 Peek under the hood 来看看为什么能实现。 动态属性名称 & 模板字符串 其中一个对象常量增强是可以分配到一个派生属性名称。我们最初可能会像下面这样设置一些状态: ? 1 2 3 4 5 6 7 var Form = React.createClass({ onChange: function(inputName, e) { var stateToSet = {}; stateToSet[inputName + 'Value'] = e.target.value; this.setState(stateToSet); }, }); 现在,我们有能力构造通过一个运行时 JavaScript 表达式确定属性名称的对象。这里,我们使用了一个模板字符串来确定哪个属性设置状态: ? 1 2 3 4 5 6 7 class Form extends React.Component { onChange(inputName, e) { this.setState({ [`${inputName}Value`]: e.target.value, }); } } 解构 & 传播属性 通常在编写组件的时候,我们可能想把大部分父组件的 props 传递给子组件,但不是所有。结合 ES6+ 解构和 JSX 传播属性,这个不需要多余的部分就能实现: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class AutoloadingPostsGrid extends React.Component { render() { var { className, ...others, // contains all properties of this.props except for className } = this.props; return ( <div className={className}> <PostsGrid {...others} /> <button onClick={this.handleLoadMoreClick}>Load more</button> </div> ); } } 我们可以结合 JSX 传播属性和常规属性,利用一个简单的优先原则实现 overrides 和 defaults。这个元素会要求 className “override” 甚至是在 this.props 存在 className 属性: ? 1 2 3 <div {...this.props} className="override"> … </div> 这个元素常规来说需要 className “base” ,除非 this.props 有 className 属性覆盖: ? 1 2 3 <div className="base" {...this.props}> … </div> 希望大家能享受 ES6+ 语言特性给 React 开发带来的一些便利。
外网访问内网SVN 需要将路由映射到VisualSVN server 的443端口 将外网的端口从路由器上进行映射到内网端口,例如将外网的6000端口映射为内网的443端口,443为svn服务器默认端口。 SVN客户端访问为https://ip:6000/svn/code
I'm following sitepoint's An introduction to Gulp.js, but I'm stuck on step four, when I try to run gulp jshint I get "Error: Cannot find module 'jshint/src/cli'" I've no idea what's causing this, which is why I'm asking here. Below are a couple of screen grabs to help with the issue. You need to install jshint as well, that will sort out the issue. > npm install --save-dev jshint gulp-jshint