首页 文章 精选 留言 我的

精选列表

搜索[前端统一],共10000篇文章
优秀的个人博客,低调大师

前端优化,了解浏览器重排与重绘

2018年3月15日 当DOM变化影响了元素的几何属性(宽、高改变等等) 浏览器此时需要重新计算元素几何属性 并且页面中其他元素的几何属性可能会受影响 这样渲染树就发生了改变,也就是重新构造RenderTree渲染树 这个过程叫做重排(reflow) 如果DOM变化仅仅影响的了背景色等等非几何属性 此时就发生了重绘(repaint)而不是重排 因为布局没有发生改变 页面发生重绘和重排,都会影响性能(重绘还好一些),尽量避免或减少重排 触发重排 页面布局和元素几何属性的改变就会导致重排 下列情况会发生重排 页面初始渲染 添加/删除可见DOM元素 改变元素位置 改变元素尺寸(宽、高、内外边距、边框等) 改变元素内容(文本或图片等) 改变窗口尺寸 不同的条件下发生重排的范围及程度会不同 某些情况甚至会重排整个页面,比如滑动滚动条 浏览器的优化:渲染队列 div.style.left = '10px'; div.style.top = '10px'; div.style.width = '20px'; div.style.height = '20px'; 如上,修改了元素的left、top、width、height属性 满足我们发生重排的条件 理论上会发生4次重排 但是实际上只会发生1次重排 这是因为我们现代的浏览器都有渲染队列的机制 当我改变了元素的一个样式会导致浏览器发生重排或重绘时 它会进入一个渲染队列 然后浏览器继续往下看,如果下面还有样式修改 那么同样入队 直到下面没有样式修改 浏览器会按照渲染队列批量执行来优化重排过程,一并修改样式 这样就把本该4次的重排优化为1次 div.style.left = '10px'; console.log(div.offsetLeft); div.style.top = '10px'; console.log(div.offsetTop); div.style.width = '20px'; console.log(div.offsetWidth); div.style.height = '20px'; console.log(div.offsetHeight); 如上,发生4次重排 offsetLeft/Top/Width/Height会强制刷新队列要求样式修改任务立刻执行 这么做是有道理的 毕竟浏览器不确定在接下来的代码中你是否还会修改同样的样式 为了保证获得正确的值,它不得不立刻执行渲染队列触发重排 以下属性或方法会刷新渲染队列 offsetTop、offsetLeft、offsetWidth、offsetHeight clientTop、clientLeft、clientWidth、clientHeight scrollTop、scrollLeft、scrollWidth、scrollHeight getComputedStyle()(IE中currentStyle) 我们在修改样式过程中,要尽量避免使用上面的属性 重绘与重排的性能优化 分离读写操作 了解了原理我们就可以对上面的代码进行优化 div.style.left = '10px'; div.style.top = '10px'; div.style.width = '20px'; div.style.height = '20px'; console.log(div.offsetLeft); console.log(div.offsetTop); console.log(div.offsetWidth); console.log(div.offsetHeight); 仅仅发生1次重排了,原因相信大家已经很清晰了 把所有的读操作移到所有写操作之后 样式集中改变 还是我们最初修改样式的代码 div.style.left = '10px'; div.style.top = '10px'; div.style.width = '20px'; div.style.height = '20px'; 如上,虽然现代浏览器有渲染队列的优化机制 但是古董浏览器效率仍然低下,触发了4次重排 即便这样,我们仍然可以做出优化 我们需要cssText属性合并所有样式改变 div.style.cssText = 'left:10px;top:10px;width:20px;height:20px;'; 如上,只需要修改DOM一次一并处理 仅仅触发了1次重排 而且只用了一行代码,看起来相对干净一些 注意,cssText会覆盖已有的行间样式 如果想保留原有行间样式,如下 div.style.cssText += ';left:10px;'; 除了cssText以外,我们还可以通过修改class类名来进行样式修改,如下 div.className = 'new-class'; 这种办法可维护性好,还可以帮助我们免除显示性代码 有很小的性能影响,改变class需要检查级联样式,但是符合BEM标准可以更好的解耦,增加可维护性,建议这样写 div.className = 'js-class'; 缓存布局信息 我觉得缓存真是万金油,哪种性能优化都少不了它 div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px'; 如上,这种读操作完就执行写操作造成了2次重排 缓存可以进行优化 var curLeft = div.offsetLeft; var curTop = div.offsetTop; div.style.left = curLeft + 1 + 'px'; div.style.top = curTop + 1 + 'px'; 如上,这也相当于是分离读写操作了 优化为1次重排 元素批量修改 现在我们想要向ul中循环添加大量li (如果ul还不存在,最好的办法是先循环添加li到ul,然后再把ul添加到文档,1次重排) var ul = document.getElementById('demo'); for(var i = 0; i < 1e5; i++){ var li = document.createElement('li'); var text = document.createTextNode(i); li.appendChild(text); ul.appendChild(li); } 我可以做出下面的优化 var ul = document.getElementById('demo'); ul.style.display = 'none'; for(var i = 0; i < 1e5; i++){ var li = document.createElement('li'); var text = document.createTextNode(i); li.appendChild(text); ul.appendChild(li); } ul.style.display = 'block'; var ul = document.getElementById('demo'); var frg = document.createDocumentFragment(); for(var i = 0; i < 1e5; i++){ var li = document.createElement('li'); var text = document.createTextNode(i); li.appendChild(text); frg.appendChild(li); } ul.appendChild(frg); var ul = document.getElementById('demo'); var clone = ul.cloneNode(true); for(var i = 0; i < 1e5; i++){ var li = document.createElement('li'); var text = document.createTextNode(i); li.appendChild(text); clone.appendChild(li); } ul.parentNode.replaceChild(clone,ul); 上面的方法减少重绘和重排的原理很简单 元素脱离文档 改变样式 元素回归文档 而改变元素就分别使用了 隐藏元素 文档碎片 克隆元素

优秀的个人博客,低调大师

前端的水平线,错误处理和调试

本章内容 理解浏览器报告的错误 处理错误 调试JavaScript代码 错误处理 错误处理在程序设计中的重要性是一定的。任何有影响力的Web应用程序都需要一套完善的错误处理机制。 try-catch语句 try { // 可能会导致错误的代码 } catch (e) { // 在错误发生时怎么处理 } 错误类型 执行代码期间可能会发生的错误有多种类型。每种错误都有对应的错误类型,而当错误发生时,就会抛出相应类型的错误对象。 ECMA-262定义了下列7种错误类型: Error EvalError RangeError SyntaxError ReferenceError TypeError URIError RangeError类型的错误在数值超出相应范围时触发: try { let items1 = new Array(-20); var items2 = new Array(Number.MAX_VALUE); } catch (e) { console.dir(e) } 在找不到对象的情况下,会发生ReferenceError(这种情况下,会直接导致人所共知的“object expected”浏览器错误)。通常,在访问不存在的变量时,就会发生这种错误: try { let obj = x; } catch (e) { console.dir(e) } SyntaxError类型, 当我们把语法错误的JavaScript字符串传入eval()函数时,就会导致此类错误。 eval('a++ b') TypeError类型在JavaScript中会经常用到,在变量中保存着意外的类型时,或者在访问不存在的方法时,都会导致这种错误。错误的原因虽然多种多样。**最常发生类型错误的情况,就是传递给函数的参数事先未经检查,结果传入类型与预期类型不相符。 Function.prototype.toString.call('name') // 抛出typeError 在使用encodeURI()或decodeURI(),而URI格式不正确时,就会导致URIError错误。这种错误很少见,因为前面说的这两个函数的容错性非常高。 利用不同错误类型,可以获悉更多有关异常的信息,从而有助于对错误作出恰当的处理。 try { // ...... } catch (e) { if (e instanceof TypeError) { // 处理类型错误 } else if (e instanceof ReferenceError) { // 处理引用错误 } else { // 处理其他类型的错误 } } 在跨浏览器编程中,检查错误类型是确定处理方式的最简便途径;包含在message属性中的错误消息会因浏览器而异。 合理使用try-catch 使用try-catch最适合处理那些我们无法控制的错误。假设你在使用一个大型JavaScript库中的函数,该函数可能会有意无意地抛出一些错误。由于我们不能修改这个库的源代码,所以大可将对该函数的调用放在try-catch语句当中。 抛出错误 与try-catch语句相配的还有一个throw操作符,用于随时抛出自定义错误。 throw new Error('Something bad happend') 上面这行代码抛出一个通用错误,带有一条自定义错误消息。也可以像下面使用其他错误类型,也可以模拟出类似的浏览器错误。 throw new SyntaxError('I dont like your syntax'); throw new TypeError('What type of variable do you take me for?'); throw new RangeError('Sorry, you just dont have the range') throw new EvalError('That doesnt evaluate.') throw new URIError('Uri, is that you?'); throw new ReferenceError('You didnt cite your references properly'); 错误事件 window.onerror = function (message, url, line) { // 处理错误 } 处理错误的策略 略 常见的错误类型 错误处理的核心,是首先要知道代码里会发生什么错误。由于JavaScript是松散类型的,而且也不验证函数的参数,因此错误只会在代码运行期间出现。一般需要关注三种错误: 类型转换错误 数据类型错误 通信错误 类型转换错误 全等操作符知道要比较的是两种不同的数据类型,因而直接返回false。 数据类型错误 Javascript是松散类型的,也就是说,在使用变量和函数参数之前,不会对它们进行比较以确保它们的数据类型正确。 通信错误 Javascript与服务器之间的任何一次通信,都有可能会产生错误。 第一种通信错误与格式不正确的URL或发送的数据有关。最常见的问题是在将数据发送给服务器之前,没有使用encodeURIComponent()对数据进行编码。 function addQueryString(url, name, value) { if (url.indexOf('?') === -1) { url += '?'; } else { url += '&'; } url += `&${encodeURIComponent(name)} = ${encodeURIComponent(value)}`; return url; } 使用这个函数而不是手工构建URL,可以确保编码正确并避免相关错误。 区分致命错误和非致命错误 任何错误处理策略中最重要的一部分,就是确定错误是否致命。对于非致命错误,可以根据下列一或多个条件来确定: 不影响用户的主要任务 只影响页面的一部分 可以恢复 重复相同操作可以消除错误 把错误记录到服务器 开发Web应用程序中的一种常见的做法,就是集中保存错误日志,以便查找重要错误的原因。 要建立这样一种JavaScript错误记录系统,首先需要在服务器上创建一个页面,用于处理错误数据。这个页面的作用无非就是从查询字符中取得数据,然后再将数据写入错误日志中。这个页面可能会使用如下所示的函数: function logError (lev, msg) { let img = new Image(); img.src = `log.php?e` } 这个logError()函数接收两个参数:表示严重程度的数值或字符串及错误消息。其中使用了Image对象来发送请求,这样做非常灵活,主要表现如下几方面: 所有浏览器都支持Image对象,包括那些不支持XMLHttpRequest对象。 可以避免跨域限制。 在记录错误的过程中出问题的概率比较低。大多数Ajax通信都是由JavaScript库提供的包装函数来处理的,如果库代码本身有问题,而你还在依赖该库记录错误,可想而知,错误消息是不可能得到记录的。 小结 错误处理至关重要。不能提前预测到可能发生的错误,不能提前采取恢复策略,可能导致较差的用户体验。 下面是几种避免浏览器响应JavaScript错误的方法。 在可能发生错误的地方使用try-catch语句,这样你还有机会以适当的方式对错误给出响应。 使用window.onerror事件处理程序,这种方式可以接受try-catch不能处理的所有错误。 另外,对任何Web应用程序都应该分析可能发生的错误。JavaScript中发生错误的主要原因如下。 类型转换 未充分检测数据类型 发送给服务器或从服务器接收到的数据有错误 原文发布时间为:2018年06月06日 原文作者:ZhangCheng 本文来源: 掘金 如需转载请联系原作者

优秀的个人博客,低调大师

Spring MVC使用JSR 303进行前端数据检验

相对于Spring本身的validation框架, JSR 303校验显得更简单,易操作。 关键的两个文件如下: User.java package org.fkit.domain; import java.io.Serializable; import java.util.Date; import javax.validation.constraints.Past; import javax.validation.constraints.Pattern; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.Range; import org.springframework.format.annotation.DateTimeFormat; public class User implements Serializable { /** * */ private static final long serialVersionUID = 1L; @NotBlank(message="登陆名不能为空") private String loginName; @NotBlank(message="密码不能为空") @Length(min=6, max=8, message="密码长度必须在6位到8位之间") private String password; @NotBlank(message="用户名不能为空") private String username; @Range(min=15, max=60, message="年龄必须在15岁到60岁之间") private int age; @Email(message="必须是合法的邮箱地址") private String email; @DateTimeFormat(pattern="yyyy-MM-dd") @Past(message="生日必须是一个过去的日期") private Date birthDate; @Pattern(regexp="[1][3,8][3,6,9][0-9]{8}", message="无效的电话号码") private String phone; public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } } registerForm.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <!DOCTYPE html PUBLIC "-//W3C/DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"> <title>测试JSR 303</title> </head> <body> <h3>注册页面</h3> <br> <form:form action="login" method="post" modelAttribute="user"> <table> <tr> <td><label>登陆名: </label></td> <td><form:input path="loginName" /></td> <td><form:errors path="loginName" cssStyle="color:red" /></td> </tr> <tr> <td><label>密码: </label></td> <td><form:input path="password" /></td> <td><form:errors path="password" cssStyle="color:red" /></td> </tr> <tr> <td><label>用户名: </label></td> <td><form:input path="username" /></td> <td><form:errors path="username" cssStyle="color:red" /></td> </tr> <tr> <td><label>年龄: </label></td> <td><form:input path="age" /></td> <td><form:errors path="age" cssStyle="color:red" /></td> </tr> <tr> <td><label>邮箱: </label></td> <td><form:input path="email" /></td> <td><form:errors path="email" cssStyle="color:red" /></td> </tr> <tr> <td><label>生日: </label></td> <td><form:input path="birthDate" /></td> <td><form:errors path="birthDate" cssStyle="color:red" /></td> </tr> <tr> <td><label>电话: </label></td> <td><form:input path="phone" /></td> <td><form:errors path="phone" cssStyle="color:red" /></td> </tr> <tr> <td><input id="submit" type="submit" value="submit"></td> </tr> </table> </form:form> </body> </html>

优秀的个人博客,低调大师

前端的对决:React的JSX与Vue的templates

请点击此处输入图片描述 React.js和Vue.js是这个星球上最流行的JavaScript库。它们都很强大,相对来说很容易获取和使用。 React和Vue的共性: 使用虚拟DOM。 提供响应式视图组件。 专注于开发过程中的一个方面。目前集中在视图层。 有这么多相似之处,你可以假设它们都是同一事物的不同版本。 这两个库之间有一个主要的区别:它们如何让开发人员创建视图组件,反过来又可以应用程序。 React采用JSX(这个词是react团队创造的)渲染内容到DOM。那么什么是JSX?基本上,JSX是一个JavaScript渲染功能,帮助你将你的HTML放到你的JavaScript代码中合适的地方。 Vue采用不同的方法,使用HTML模板。使用Vue模板就像是用JSX就是他们都是创建使用JavaScript。主要的区别是,JSX函数在实际的HTML文件中从来不被使用,而Vue模板不是这样。 特别说明下,Vue.js的相关课程可以点击这里:www.hubwiz.com React JSX 我们将深入探讨JSX如何工作。假设你有一个要在DOM上显示的名称列表。你们公司最近的一份新员工名单。 如果你使用的是普通的HTML,你首先需要创建一个index.html文件。然后,你将添加以下代码行。 <ul> <li>John</li> <li>Sarah</li> <li>Kevin</li> <li>Alice</li> <ul> 这里没什么好说的,只是普通的HTML代码。 那么你使用JSX怎么做同样的事情?第一步是创建一个index.html文件。但是,不像以前那样添加完整的HTML,只需要添加一个简单的div元素。这个div将是容器元素,在那里您的所有React代码将被呈现。 div将需要一个唯一的ID,这样React就知道如何可以找到它。facebook倾向于支持根关键字,所以让我们坚持这一点。 <divid=root></div> 现在,走到最重要的一步。创建包含所有react代码的JavaScript文件。这是一个叫app.js的文件。 你现在把所有的事情都排除在外,进入主事件。用JSX显示所有新员工到 DOM中。 首先需要创建一个具有新雇员名称的数组。 constnames=[‘John’,‘Sarah’,‘Kevin’,‘Alice’]; 从那里,您需要创建一个响应元素,它将动态地呈现整个名称列表。这你没有必要再手动显示每一个。 constdisplayNewHires=( <ul> {names.map(name=><li>{name}</li>)} </ul> ); 这里要注意的关键是,您不必创建单独的<li>元素。你只需要描述你想让他们看一次,React会处理剩下的。这是一件非常给力的事。虽然你只有几个名字,但想象一下有成百上千的名单!你可以看到这当然是一个更好的方法。特别是如果<li>元素,比这里用到的元素更复杂。 代码的最后一步是需要将内容渲染到屏幕,主要是通过ReactDom的render渲染函数。 ReactDOM.render( displayNewHires, document.getElementById(‘root’) ); 在这里,你在用div里的内容作出响应,通过displayNewHires渲染root元素。 那么最终的React代码就应该是这个样子: constnames=[‘John’,‘Sarah’,‘Kevin’,‘Alice’]; constdisplayNewHires=( <ul> {names.map(name=><li>{name}</li>)} </ul> ); ReactDOM.render( displayNewHires, document.getElementById(‘root’) ); 这里要记住的一个关键是,这是所有的React代码。这意味着它都将编译成普通的JavaScript。下面是它最终看起来的样子: ‘usestrict’; varnames=[‘John’,‘Sarah’,‘Kevin’,‘Alice’]; vardisplayNewHires=React.createElement( ‘ul’, null, names.map(function(name){ returnReact.createElement( ‘li’, null, name ); }) ); ReactDOM.render(displayNewHires,document.getElementById(‘root’)); 这就是它的全部。现在有一个简单的React应用程序,它将显示名称列表。没有什么可以写的,但它应该能让你了解React的能力是什么。 特别说明下,react.js的相关课程可以点击这里:www.hubwiz.com Vue.js Templates(模板) 按照最后一个示例,您将再次创建一个简单的应用程序,它将在浏览器上显示名称列表。 你需要做的第一件事就是创建一个空的index.html文件。在该文件中,您将创建一个带有根ID的空div。记住,根只是个人偏好。你可以调用你的ID无论什么情况下。您只需确保稍后将HTML与JavaScript代码同步时匹配起来即可。 这个div会像它在React中那样起作用。它会告诉JavaScript库,在这个示例中,开始改变的时候在哪里观察DOM。 一旦这样做了,你将创建一个JavaScript文件,将存放Vue代码。称它为app.js,以便保持一致。 现在你已经准备好你的文件,让我们看看Vue如何显示元素到浏览器。 Vue使用模板的方法用它来操作DOM。这意味着你的HTML文件不仅会有一个空的div,比如在React中。实际上,您将在HTML文件中编写一部分代码。 为了给你一个更好的提醒,回想一下使用普通HTML创建名称列表需要什么。一个<ul>包含一些的<li>元素。在Vue,你要做几乎相同的事情,只有少数的变化增加。 创建一个<ul>。 <ul> </ul> 现在添加一个空的<li>。 <ul> <li> </li> </ul> 没什么新鲜的变化,通过增加一个指令,一个自定义的Vue的属性你的<li>元素。 <ul> <liv-for=’nameinlistOfNames’> </li> </ul> 指令是Vue直接进入HTML添加JavaScript功能的方式。它们都以V开头,后面跟着描述性的名字,让你知道他们在做什么。在这个实例中,它是for循环。每一个名字在你的名字列表listOfNames中,你可以从你的名单列表上复制这个<li>元素和更换一个新的<li>元素来确定一个的名字。 现在,代码只需要最后一次编写。当前,它将为列表中的显示每个名称,但实际上并没有告诉它将把实际名称显示在浏览器上。为了解决这个问题,你将在你的<li>中插入一些类似mustache的语法。你可能在其他JavaScript库中看到的类似东西。 <ul> <liv-for=’nameinlistOfNames’> {{name}} </li> </ul> 现在<li> 元素是写完了。它现在将显示名字为listOfNames列表的每个项。记住name可以是任何其他的名称。你可以把它叫做item,它也会达到同样的目的。所有关键字都用作占位符,用于在列表中迭代。 你需要做的最后一件事就是创建数据集和在实际应用程序中初始化Vue。 这样做,你将需要创建一个新的Vue实例。通过将它分配给名为app的变量来实例化它。 letapp=newVue({ }); 现在,对象将包含一些参数。第一个是最重要的,el(element) 参数告诉Vue在DOM开始添加什么内容。就像你对你的React中的例子那样。 letapp=newVue({ el:’#root’, }); 最后一步是添加数据到Vue的应用。在Vue,所有的数据都将做为Vue实例的参数传送到应用程序。另外,每个Vue实例只能有一个每种类型参数。虽然有相当多的,但您只需要集中在两个例子,el和data。 letapp=newVue({ el:’#root’, data:{ listOfNames:[‘Kevin’,‘John’,‘Sarah’,‘Alice’] } }); 数据对象将接受一个数组listOfNames。现在,每当您想在应用程序中使用该数据集时,只需要使用指令调用它。很简单,对吧? 这是最终的代码: HTML <divid=”root”> <ul> <liv-for=’nameinlistOfNames’> {{name}} </li> </ul> </div> JavaScript newVue({ el:”#root”, data:{ listOfNames:[‘Kevin’,‘John’,‘Sarah’,‘Alice’] } }); 结论 现在你知道如何使用React和Vue创建两个简单的应用程序。他们都提供了强大的功能,虽然Vue看起来往往是更容易使用。还有需要记住,Vue也支持JSX的使用,虽然它不是首选的实现方法。 无论哪种方式,Vue和React都是两个功能强大的库,你使用任何一个都不会有问题。 如果你觉得这篇文章很有帮助,给我一些掌声。 你可以在Twitter上跟踪我! 汇智网(www.hubwiz.com,有很多很棒vue.js的课程包括vue.js\vuex\vue-router\vue工程化等)的小智原创翻译。 分享最新的Vue.js 2 全家桶系列教程: 1.vue.js 入门与提高: http://xc.hubwiz.com/course/592ee9b2b343f27b0ae1ba99 2.vuex 2 入门与提高: http://xc.hubwiz.com/course/597d463fff52d0da7e3e397a 3.vue-router 入门与提高: http://xc.hubwiz.com/course/5983d3aeff52d0da7e3e3d50 4.vue.js 工程化实践: http://xc.hubwiz.com/course/598bad66c7fd1d49453979c9

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册