首页 文章 精选 留言 我的

精选列表

搜索[非自回归核心技术],共10007篇文章
优秀的个人博客,低调大师

udp的非可靠传输详解

- 影响性能案例 - 部分分片丢失 - 数据包1较大,udp不分包, 到了ip层,包被切分成6个分片 - 分片3丢失, 接收到只收到了1,2,4,5,6五个分片并缓存起来,等待超时后才会删除 - 由于分片缺失,ip层无法进行分片重组,故数据包1的6个分片又需要全部重传,假设此时又丢失了2号分片且之前收到的分片还未超时删除,那么接收端缓存的分片会变成 1,2,4,5,6,1,3,4,5,6, 缓存越来越多,但超时检测较慢,会导致卡顿影响应用层性能 - 解决方式 - 换成可靠的udp协议 - 换成tcp协议, tcp如果出现丢失一般都只重传丢失那部分,因为tcp这里就会对数据包进行切分,避免了ip分片,也避免了整个数据包重传问题 - 在应用层自行切包,来避免ip分片 - 提高性能案例 - 传统的DNS - DNS要求 响应块,数据包较小,请求量大 - 使用UDP可以减少tcp建立连接带来的消耗 - DNS数据包小,不需要ip层进行分片,故不存在上述问题 参考文献: Wireshark网络分析的艺术

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

非懂不可的Slice(一)

2 --> 前言 切片是一种复合数据类型,与数组类似,存放相同数据类型的元素,但数组的大小是固定的,而切片的大小可变,可以按需自动改变大小。切片是基于底层数组实现的,是对数组的抽象。切片很小,只有三个字段的数据结构:指向底层数组的指针、能访问的元素个数(即切片长度)和允许增长到的元素个数(即切片容量)。 如上图所示,一个长度为3、容量为5的整型切片的底层结构。 声明与初始化 make()创建 使用内置函数创建空切片,形如: s:=make([]type,len,cap)//len长度,cap容量 也可以只指定len,那么切片的容量和长度是一样的。Go语言提供了内置函数len、cap分别返回切片的长度和容量。 //声明一个长度为3、容量为5的整型切片s1:=make([]int,3,5)fmt.Println(len(s1),cap(s1))// 输出:3 5//声明一个长度和容量都是5的字符串切片s2:=make([]string,5)fmt.Println(len(s2),cap(s2))// 输出:5 5 切片创建完成,如果不指定字面量的话,默认值就是数组的元素的零值。切片的容量就是切片底层数组的大小,我们只能访问切片长度范围内的元素,如第一节的图所示,长度为3的整型切片存入3个值后的结构,我们只能访问到第3个元素,剩余的2个元素需要切片扩充以后才可以访问。所以,很明显的:容量>=长度,我们不能创建长度大于容量的切片。 s1:=make([]int,5,3)// 报错:len larger than cap in make([]int) 使用字面量创建切片 使用字面量创建,就是指定了初始化的值 s:=[]int{1,2,3,4,5}//长度和容量都是5的整型切片 有没有发现,这种创建方式与创建数组类似,只不过不用指定[]的值,这时候切片的长度和容量是相等的,并且会根据指定的字面量推导出来。区别: //创建大小为10的数组s:=[10]int{1,2,3,4,5}//创建切片s:=[]int{1,2,3,4,5} 我们也可以只初始化某一个索引的值: s:=[]int{4:1}fmt.Println(len(s),cap(s))// 输出:5 5fmt.Println(s)// 输出:[0 0 0 0 1] 指定了第5个元素为1,其他元素初始化为0。 基于已有的数组或者切片创建切片 使用操作符[start,end],简写成[i,j],表示从索引i,到索引j结束,截取已有数组或者切片的任意部分,返回一个新的切片,新切片的值包含原切片的i索引的值,但是不包含j索引的值。i、j都是可选的,i如果省略,默认是0,j如果省略,默认是原切片或数组的长度。i、j都不能超过这个长度值。 s:=[]int{0,1,2,3,4,5,6,7,8,9}fmt.Println("s[:]",s[:])fmt.Println("s[2:]",s[2:])fmt.Println("s[:4]",s[:4])fmt.Println("s[2:4]",s[2:4]) 输出你可能会有个疑问:截取获得的新切片的长度和容量怎么计算呢?我们当然可以使用内置函数len、cap直接获得,如果明白了怎么计算的,我们处理问题就可以更得心应手。对底层数组大小为k的切片执行[i,j]操作之后获得的新切片的长度和容量是:长度:j-i容量:k-i就拿上一个例子的s[2:4]来说,原切片底层数组大小是10,所以新切片的长度是4-2=2,容量是10-2=8。可以使用内置函数验证下: s1:=s[2:4]fmt.Println(len(s1),cap(s1))// 输出:2 8 上面是使用2个索引的方法创建切片,还可以使用3个索引的方法,第3个用来限定新切片的容量,用法为slice[i:j:k]。 s2:=s[2:4:8]fmt.Println(s2)// 输出:[2 3] 长度和容量如何计算:长度j-i,容量k-i。所以切片s2的长度和容量分别是2、6。注意:k不能超过原切片(数组)的长度,否则报错panic: runtime error: slice bounds out of range。例如上面的例子中,第三个索引值不能超过10。我们来看个例子: s:=[]int{0,1,2,3,4,5}fmt.Println("before,s:",s)s1:=s[1:4]fmt.Println("before,s1:",s1)s1[1]=10fmt.Println("after,s1:",s1)fmt.Println("after,s:",s) 输出: before,s:[012345]before,s1:[123]after,s1:[1103]after,s:[0110345] 这个例子说明,原切片和新切片是基于同一个底层数组的,所以当修改的时候,底层数组的值就会被改变,原切片的值也随之改变了。对于基于数组的切片也一样的。 我们可以看到,执行完切片动作之后,获得一个新切片,与原切片共享同一段底层数组,但通过不同的切片会看到底层数组的不同部分。切片s能够看到底层数组全部6个元素,而切片s1只能看到索引1及之后的全部元素,对于s1来说,索引1之前的部分是不存在。 使用切片 切片的使用方法与数组的使用方法类似,直接通过索引就可以获取、修改元素的值。 s:=[]int{1,2,3,4,5}fmt.Println(s[1])// 获取值 输出:2s[1]=10//修改值fmt.Println(s)//输出:[1 10 3 4 5] 只能访问切片长度范围内的元素,否则报错 s:=[]int{1,2,3,4,5}s1:=s[2:3]fmt.Println(s1[1]) 上面这个例子中,s1的容量为3,长度为1,所以只能访问s1第一个元素s1[0],访问s1[1]就会报错:panic: runtime error: index out of range 与切片的容量相关联的元素只能用于增长切片,在使用这部分元素前,必须将其合并到切片的长度里。 相较于数组,使用切片的好处在于,可以按需增长,类似于动态数组。Go提供了内置append函数,能够帮我们处理切片增长的一些列细节,我们只管使用就可以了。函数原型: funcappend(slice[]Type,elems...Type)[]Type 使用append函数,需要一个被操作的切片和一个(多个)追加值,返回一个相同数据类型的新切片。 s:=[]int{1,2,3,4,5}newS:=s[2:4]newS=append(newS,50)fmt.Println(s,newS)fmt.Println(&s[2]==&newS[0]) 输出: [123450][3450]true 上面的例子中,截取获得一个长度为2,容量为3(可用容量为1)的新切片newS,通过append函数向切片newS追加一个元素50。追加元素50之前: 追加元素10之前 追加元素50之后: 追加元素10之后 通过输出结果可以得出,新切片newS与原切片s是共享底层数组的,当切片可用容量能够存下追加元素时,不会创建新的切片。 当切片可用容量存不下需要追加的元素时会发生呢? s:=[]int{1,2,3,4,5,6,7,8}s1:=s[2:4]fmt.Printf("before->s=%v\n",s)fmt.Printf("before->s1=%v\n",s1)fmt.Printf("before->len=%d,cap=%d\n",len(s1),cap(s1))fmt.Println("&s[2]==&s1[0]is",&s[2]==&s1[0])s1=append(s1,60,70,80,90,100,110)fmt.Printf("after->s=%v\n",s)fmt.Printf("after->s1=%v\n",s1)fmt.Printf("after->len=%d,cap=%d\n",len(s1),cap(s1))fmt.Println("&s[2]==&s1[0]is",&s[2]==&s1[0]) 输出: before->s=[12345678]before->s1=[34]before->len=2,cap=6&s[2]==&s1[0]istrueafter->s=[12345678]after->s1=[3460708090100110]after->len=8,cap=12&s[2]==&s1[0]isfalse 追加元素60、70、80、90、100和110之前: 追加之后: 从结果可以看出,切片的底层数组没有足够的可用容量, append函数会创建一个新的底层数组,将原数组的值复制到新数组里,再追加新的值,就不会影响原来的底层数组。 一般我们在创建新切片的时候,最好要让新切片的长度和容量一样,这样我们在追加操作的时候就会生成新的底层数组,和原有数组分离,就不会因为共用底层数组而引起奇怪问题,因为共用数组的时候修改内容,会影响多个切片。 append函数会智能地增加底层数组的容量,目前的算法是:当数组容量<=1024时,会成倍地增加;当超过1024,增长因子变为1.25,也就是说每次会增加25%的容量。Go提供了...操作符,允许将一个切片追加到另一个切片上: s:=[]int{1,2,3,4,5}s1:=[]int{6,7,8}s=append(s,s1...)fmt.Println(s,s1) 输出: [12345678][678] 迭代切片 使用for循环迭代切片,配合len函数使用: s:=[]int{1,2,3,4,5}fori:=0;i<len(s);i++{fmt.Printf("Index:%d,Value:%d\n",i,s[i])} 使用for range迭代切片: s:=[]int{1,2,3,4,5}fori,v:=ranges{fmt.Printf("Index:%d,Value:%d\n",i,v)}//使用‘_’可以忽略返回值s:=[]int{1,2,3,4,5}for_,v:=ranges{fmt.Printf("Value:%d\n",v)} 需要注意的是,range返回的是切片元素的复制,而不是元素的引用。如果使用该值变量的地址作为指向每个元素的指针,就会造成错误。 s:=[]int{1,2,3,4,5}fori,v:=ranges{fmt.Printf("v:%d,v_addr:%p,elem_addr:%p\n",v,&v,&s[i])} 输出: v:1,v_addr:0xc000018058,elem_addr:0xc000016120v:2,v_addr:0xc000018058,elem_addr:0xc000016128v:3,v_addr:0xc000018058,elem_addr:0xc000016130v:4,v_addr:0xc000018058,elem_addr:0xc000016138v:5,v_addr:0xc000018058,elem_addr:0xc000016140 可以看到,v的地址总是相同的,因为迭代返回的变量在迭代过程中根据切片依次赋值的新变量。好了,今天先讲到这里,下一节,我们再来讨论关于Slice更多的用法! 如果我的文章对你有所帮助,点赞、转发都是一种支持!

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

非spring框架下使用querydsl

Querydsl是一个采用API代替拼凑字符串来构造查询语句,可跟 Hibernate 和 JPA 等框架结合使用。网上很多教程都是结合spring JPA框架下使用的教程,本文介绍的是非spring环境下querydsl JPA整合使用。 1、使用eclipse生成jpa实体类,IDEA也能生成同样JPA实体类。 鼠标右键打开JPA Tools工具: 创建数据库链接: 选择那些数据库表需要生成JPA实体类: 关联表生成: 设置实体生成属性: 单个实体属性修改 生成的JPA实体类AdminLog.java package com.test.db.entity; import java.io.Serializable; import javax.persistence.*; import java.util.Date; /** * The persistent class for the admin_log database table. * */ @Entity @Table(name="admin_log") @NamedQuery(name="AdminLog.findAll", query="SELECT a FROM AdminLog a") public class AdminLog implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(unique=true, nullable=false) private String id; @Column(name="action_id", length=20) private String actionId; @Column(name="client_ip", length=15) private String clientIp; @Column(name="controller_id", length=20) private String controllerId; @Temporal(TemporalType.TIMESTAMP) @Column(name="create_date") private Date createDate; @Column(name="create_user", length=50) private String createUser; @Column(name="func_name", length=50) private String funcName; @Column(name="module_name", length=50) private String moduleName; @Column(name="right_name", length=50) private String rightName; @Column(length=200) private String url; public AdminLog() { } public String getId() { return this.id; } public void setId(String id) { this.id = id; } public String getActionId() { return this.actionId; } public void setActionId(String actionId) { this.actionId = actionId; } public String getClientIp() { return this.clientIp; } public void setClientIp(String clientIp) { this.clientIp = clientIp; } public String getControllerId() { return this.controllerId; } public void setControllerId(String controllerId) { this.controllerId = controllerId; } public Date getCreateDate() { return this.createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public String getCreateUser() { return this.createUser; } public void setCreateUser(String createUser) { this.createUser = createUser; } public String getFuncName() { return this.funcName; } public void setFuncName(String funcName) { this.funcName = funcName; } public String getModuleName() { return this.moduleName; } public void setModuleName(String moduleName) { this.moduleName = moduleName; } public String getRightName() { return this.rightName; } public void setRightName(String rightName) { this.rightName = rightName; } public String getUrl() { return this.url; } public void setUrl(String url) { this.url = url; } } 2、生成querydsl查询类 maven pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test.db</groupId> <artifactId>dbtest</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>dbtest</name> <url>http://maven.apache.org</url> <properties> <querydsl.version>4.2.1</querydsl.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql-codegen</artifactId> <version>${querydsl.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa</artifactId> <version>2.6.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>5.4.0.Final</version> </dependency> </dependencies> <build> <plugins> <!-- 生成jpa实体插件 --> <plugin> <groupId>com.querydsl</groupId> <artifactId>querydsl-maven-plugin</artifactId> <version>${querydsl.version}</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>jpa-export</goal> </goals> <configuration> <targetFolder>${project.basedir}/target/generated-sources/java</targetFolder> <packages>com.test.db.entity</packages> </configuration> </execution> </executions> </plugin> <!-- 生成jpa实体插件 --> <!-- <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project> 执行命令mvn compile或mvn eclipse:eclipse生成jpa实体对应的querydsl查询类 AdminLog.java对应的querydsl对应查询类 package com.test.db.entity; import static com.querydsl.core.types.PathMetadataFactory.*; import com.querydsl.core.types.dsl.*; import com.querydsl.core.types.PathMetadata; import javax.annotation.Generated; import com.querydsl.core.types.Path; /** * QAdminLog is a Querydsl query type for AdminLog */ @Generated("com.querydsl.codegen.EntitySerializer") public class QAdminLog extends EntityPathBase<AdminLog> { private static final long serialVersionUID = 392071999L; public static final QAdminLog adminLog = new QAdminLog("adminLog"); public final StringPath actionId = createString("actionId"); public final StringPath clientIp = createString("clientIp"); public final StringPath controllerId = createString("controllerId"); public final DateTimePath<java.util.Date> createDate = createDateTime("createDate", java.util.Date.class); public final StringPath createUser = createString("createUser"); public final StringPath funcName = createString("funcName"); public final StringPath id = createString("id"); public final StringPath moduleName = createString("moduleName"); public final StringPath rightName = createString("rightName"); public final StringPath url = createString("url"); public QAdminLog(String variable) { super(AdminLog.class, forVariable(variable)); } public QAdminLog(Path<? extends AdminLog> path) { super(path.getType(), path.getMetadata()); } public QAdminLog(PathMetadata metadata) { super(AdminLog.class, metadata); } } 3、查询例子 JPA持久化META-INF\persistence.xml配置文件,EntityManagerFactory创建实例时,需要读取该配置文件: <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <!-- .MySQLInnoDBDialect --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/yiiboot?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;useSSL=true" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="123" /> <property name="connection.provider_class" value="com.alibaba.druid.support.hibernate.DruidConnectionProvider" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="20" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> </properties> </persistence-unit> </persistence> 测试代码 package com.test.db.main; import java.util.List; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import com.querydsl.jpa.impl.JPAQueryFactory; import com.test.db.entity.AdminModule; import com.test.db.entity.QAdminModule; public class JpaTest { public static void main(String[] args) throws Exception{ String persistenceUnitName = "jpa"; EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName); JPAQueryFactory queryFactory = new JPAQueryFactory(entityManagerFactory.createEntityManager()); QAdminModule adminModule = QAdminModule.adminModule; List<AdminModule> list = queryFactory.selectFrom(adminModule).fetch(); list.forEach(module->{ System.out.println(module.getId() + "," + module.getDisplayLabel() + "," + module.getCreateDate()); }); } } querydsl官方文档:http://www.querydsl.com/static/querydsl/latest/reference/html/ch02.html#jpa_integration java类库:https://www.21doc.net/java/awesomejava#database

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

CDN-非预期 302 问题

浅谈 访问 CDN 出现 302 的情况一般有几种可能; CDN 有 302 的特殊配置,根据客户端请求的 URL,或者 http 强制跳转到 https; CDN 透传回源后,源站给出的 302; 客户端请求到 CDN 过程中被劫持,或者 CDN 回源站被劫持; 案例分析 案例: 客户请求到 CDN 出现 302,视频内容可以访问,但是经常出现方式失败,或者内容不对的情况。 分析: 先固定 CDN 不通的节点进行测试,用户可以通过 IPIP.net 看下其他地区解析的 CDN IP ,然后固定访问。看是普片的都是 302 还是部分地区。 检查自己的 CDN 控制台上是否有特殊的 302 条转配置,比如 http 跳转 https ,或者自己有过特殊的 302 需求。 固定原站进行测试看是否有 302 的情况,或者自己原站是否有 302 的配置。 以上步

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

vue兄弟组件通信(非vuex)

实例:点击按钮将组件A的"A数据"传到组件B的方框内 image 实现思路:根据官方文档的教程->使用一个空的 Vue 实例作为事件总线 官方文档 代码展示 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <script type="text/javascript" src="lib/vue.min.js" ></script> <title>组件通讯</title> <style> .b{ width: 100px; height: 100px; border: 2px darkblue solid; } #app{ margin: 0 auto; width: 100px; text-align: center; height: 600px; } </style> </head> <body> <div id="app"> <mya></mya> <myb></myb> </div> </body> <template id="A"> <div> <button @click="senddata">点击</button> <h1>{{a}}</h1> </div> </template> <template id="B"> <div> <h1>{{b}}</h1> <div class="b"> {{a}} </div> </div> </template> <script> var bus = new Vue(); var vm=new Vue({ el:"#app", components:{ "mya":{ template:"#A", data:function(){ return { a:"A数据" } }, methods:{ senddata:function(){ bus.$emit("a-msg",this.a); } } }, "myb":{ template:"#B", data:function(){ return { b:"B数据", a:"" } }, mounted:function(){ var bthis=this; bus.$on("a-msg",function(a){ bthis.a=a; }); } } } }); </script> </html> 效果展示 image

资源下载

更多资源
Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

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部分的功能。

用户登录
用户注册