当前位置:首页 » 网页前端 » 前端wiki
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

前端wiki

发布时间: 2023-05-03 06:03:35

① python/java/web前端需要哪个编程资料

Python、Java和Web前端都需要相应的编程资料来学习和提高编程能耐余乱力,以下是一些常用的编程资料:

Python编程资料:

《Python编毁运程从入门到实践》

《Python核心编程》

《Python Cookbook》

Python官方文档

Python编程网站(如Python官网、Python教程网等)

Java编程资料:

《Java编程思想》

《Effective Java》

《Head First Java》

Java官方文档

Java编程网站(如Java官网、Java教程网等)

Web前端编程资料:

《JavaScript高级程序设计》

《CSS权威指南》

《HTML5权威指南》

MDN Web文档

Web前端编程网站(昌档如W3School、Bootstrap中文网等)

以上是一些常用的编程资料,但不限于此,还有很多其他的资料可以供大家学习和参考。

② 女生适合学习前端开发吗

前端开发不仅适合女生学,根据我们以前的学员,学前端的绝大部分都是女生。而且,而且女生学习it也首选前端。
女生从事前端开发是比较适合的,随着工业互联网时代的到来,前端开发的边界会不断得到拓展,岗位附加值也会有所提升,随着越来越多的企业纷纷实现业务云端化,未来前端开发人员的就业渠道也会逐渐得到拓展。
虽然前端开发的初期学习难度相对较低,但是前端开发的知识量还是比较大的,在移动互联网时代,前端开发的边界开始逐渐向移动端拓展,而在云计算和大数据的推动下,前端开发的边界也在逐渐从前端向后端覆盖,所以也把当前的前端称为“大前端”。
当前在IT互联网行业内,相对于后端开发来说,不少女生更愿意从事前端开发,虽然前端开发通常都属于应用级开发,对于从业者的算法设计能力没有更多的要求,但是前端开发的工作量并不低,这一点也要引起足够的重视。

③ 什么是基于数据驱动的前端框架

首先,“数据驱动” 是一种编程范式(Programming Paradigm),相似的有很多,详情可以参考这里:https://en.wikipedia.org/wiki...
如果只谈前端,那么问题就会简化很多,一般来说,会比较 “数据驱动” 与 “事件驱动”。数据驱动思想的出现一定程度上弥补了事件驱动的不足。
先说事件驱动,一个很典型的例子就是 jQuery。用 jQuery 开发的页面执行初期就像这样:
通过特定的选择器查找到需要操作的节点 -> 给节点添加相应的事件监听

响应用户操作,效果是这样:
用户执行某事件(点击,输入,后退等等) -> 调用 JavaScript 来修改节点

这种模式,对于业务需求简单的页面来说没什么问题。只是现在前端越来越复杂,光用这样的模式已经满足不了很多大型项目的需求。另一方面,找节点和修改节点这件事儿,效率本身就很低。因此出现了数据驱动模式,我们就拿其中的一种,MVVM 来举例子,执行初期就像这样:
读取模板,同时获得数据,并建立 VM(view-model) 的抽象层 -> 在页面进行填充

要注意的是,MVVM 对应了三个层,M - Model,可以简单的理解为数据层;V - View,可以理解为视图,或者网页界面;VM - ViewModel,一个抽象层,简单来说可以认为是 V 层中抽象出的数据对象,并且可以与 V 和 M 双向互动(一般实现是基于双向绑定,双向绑定的处理方式在不同框架中不尽相同)。
针对用户的操作,大致是这样:
用户执行某个操作 -> 反馈到 VM 处理(可以导致 Model 变动) -> VM 层改变,通过绑定关系直接更新页面对应位置的数据

总结一下,可以简单的这么去理解:数据驱动不是操作节点的,而是通过虚拟的抽象数据层来直接更新页面。我觉得,主要就是因为这一点,数据驱动框架才得以有较快的运行速度(因为不需要去折腾节点),并且可以应用到大型项目。

④ 前端性能-减少Http请求

依据性能黄金法则,80%的时间花费在了Images,scripts,stylesheets,Flash等Http请求上,我们需冲老要花费在这块的时间,这些技散圆升术包括使用image maps,css sprites,inline images,和合并javascript与stylesheets

如果你页面上有很多带链接的图片,image maps(图片地图)是一种减少Http请求数量的方式。一个image maps允许你在单张图片上放置相关的多个链接地址,用户点击图片上不同的区域,跳转到不同的链接页面;
有两种类型的image maps,一种是server-side image maps, 这种maps,所有的点击都请求同一个URL,并且参数附带用户点击点的x,y坐标;另一种是Client-side image maps,这种更加常用,因为用户点击不同的区域,请求不同的URL;如下

-- image map详细资料可查看
https://en.wikipedia.org/wiki/Image_map
-- client-side image maps的实例
https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_areamap

俗称雪碧图,具体怎么做应腔态该都知道;说说它的益处吧,它不仅减少了Http请求数,而且相对image maps更加灵活,因为image map要求使用的图片要求图形有顺序,单sprites不需要;
另外一般人都认为合并图片会比分开的图片总量的大小要来的大,因为合并后的图片有格外增加的空白区域,事实上合并的图片反而趋于更小,因为合并的图片相对分开的图片减少了存储的信息开销,比如颜色表,格式形式等;

使用data:URL 协议使你的页面直接包含图像而不需要任何Http请求是可能的,但浏览器直到ie8才支持;格式如下

data:不单单能使用于内联图片,任务一个指定URL的地方都能使用它,包括script和a 标签;
因为data:URLs内嵌于页面中,所以在不同页面访问中并不会缓存他们,从而增大页面的大小,一种聪明的做法是通过外联css和内容图片作为背景图设置来使内嵌图片随着css一块被缓存,在多个页面都使用到这个内嵌图片的情况下,是值得这么做的,比如网站logo;

-- 如何生成图片的base64编码
使用一些在线工具,比如这个 http://www.pjhome.net/web/html5/encodeDataUrl.htm ;
或者使用google chrome开发者工具,在 chrome 下新建一个窗口,然后把要转化的图片直接拖入浏览器,打开控制台,点 Source,点击图片,右侧就会显示该图片的 base64 编码

理想状态下,把一个网页上引用的多个script脚本合并成一个,把引用的多个样式文件合并成一个,以此来减少http请求数,一般合并后还会有另一个步骤来减少文件大小,叫做压缩;

-- 如何合并
一般使用grunt或者gulp打包工具的童鞋都知道,都能轻松做到;

⑤ 一路走来,都有哪些字体

在产品的设计过程中,设计师总是期望最后产品展示的效果能与自己设计稿一模一样,但最后屏幕呈现出的效果多少会与设计稿有所出入。

这时设计会向前端抱怨屏幕敏蔽显示的效果与自己的设计不符,前端也表示很无辜,这是按照设计要求实现的,怎么能怪我?信碰

可能对大多数用户来说,没法一眼识别哪里存在差距,但细微差距的集合,对用户的整体感官差距完全是不同的。这些细微差距体现在很多方面,其中,字体就是一个纠结的地方,很难说清,但影响又无处不在。

字体的分类、历史

首先,先来介绍一下字体的分类和历史,在西方国家的字母体系,分成两大字族:

serif(衬线字体)
sans serif(无衬线字体)

英文字体

衬线字(如图中的 Times New Roman)是指在字的笔画开始及结束的地方有额外的装饰,而且笔画的粗细会因直横的不同而有所不同。起始和结束装饰的不同,能设计出不同风格的字体。

无衬线体(如图中的 Helvetica),没有这些额外的装饰,而且笔画粗细差不多。字形更加趋于理性,用户很难从字体的外形看出字体要表达的性格,感情。

中文字体

在中文字体中,宋体是比较标准的 serif 字体,衬线的特征比较明显。

无衬线字(如图中的思源黑体)就没有这些额外装饰,而且笔画粗细大致上是差不多。

字体的特点、如何选择

介绍了字体的分类和历史,接下来就是考虑具体使用的场景,如何选择合适的字体呢?

在平面印刷,杂志,企业 Logo 中,需要考虑:

表达的性格和感情
引起他人的关注,吸引眼球
因此,往往会使用 特殊的衬线体或者特殊设计字体。

在电子屏幕的场景下,更多的考虑:

清晰度
长时间下,是否容易视觉疲劳
不同字体大小是否会影响字体清晰度
在这些因素的考虑下,无衬线体没有边角的修饰,结构简单清晰,在同等字号下,无衬线的字体看上去比有衬线更大,结构更清晰的特性,更加适合屏幕。也不会因为字体本身结构笔划,影响文字内容的情感态度表达。无衬线体带来的性格,立场就是中性的,在需要大量排版阅读的文字网站中,就像空气一样,我们不会在意文字本身设计的影响。因此,更应该使用 无衬线体。

不同浏览器如何实现字体的技术

字体的选择,这是设计阶段需要考虑的事情,但如何做到屏幕显示和设计一样呢?我们需要了解不同浏览器实现显示的一些原理技术。

先介绍一下点阵字体与矢量字体:

点阵字体

点阵字体也叫位图字体,其中每个字形都以一滑拿谈组二维像素信息表示。

由于位图的原故,点阵字体很难进行缩放,特定的点阵字体只能清晰地显示在相应的字号下。但对于 12-16px 这样小的汉字,点阵字体常常比其它类型的字体在屏幕上更好的显示效果。

常见的点阵字体有 bdf,pcf,fnt,hbf 等格式。

对于常见的计算机操作系统,字体的显示算法需要一些字体的信息来优化屏幕显示效果,英文称为 hinting。汉字由于笔画复杂,所以 hinting 的方式与西文截然不同。使用在汉字字体中嵌入预先制作的点阵位图既可以有效地避免 hinting 算法带来的计算开销,同时屏幕上显示的汉字边缘清晰,易于阅读。

矢量字体

矢量字体中每一个字形是通过数学曲线来描述的,它包含了字形边界上的关键点,连线的导数信息等,字体的渲染引擎通过读取这些数学矢量,然后进行一定的数学运算来进行渲染。

这类字体的好处是字体可以无限放大而不产生变形。

矢量字体主要包括 Type1 和 TrueType 等几类。

系统的偏好

Windows

默认是使用很重的 hinting,直到 DirectWrite 开始才禁用 horizontal hinting
字体渲染强调文字的锐利和清晰
在操作系统接口和网页正文等小字号的地方比较清晰,但大幅牺牲字体的原貌
Mac OS X

完全不用 hinting 信息,只在 vertical 方向做一些 autohint
字体渲染强调忠实字体设计,最大化保留字体的外形
边缘平滑是为了更好地传递字体设计中的曲线等细节,而小字号时显得模糊也是在此方针下的妥协
最佳实现

考虑到 css 的“优雅降级”原则,默认我们先把 Mac OS 的字体放在最前面,中文 "PingFang SC-light",英文 "Helvetica Neue",降级备用字体 "Hiragino Sans GB" 和 "Helvetica",最后考虑基础 Windows 系统自带的字体 "Microsoft YaHei" 和 "Arial"。

font-family:"PingFangSC-light","Helvetica Neue",“Hiragino Sans GB”,“Helvetica”,“Microsoft YaHei”,“Arial”
1
2
1
2
字体设计是整个界面设计的一部分,我们很难意识到它的重要性,就像我们不会在意空气和水,但它的作用不容忽视。

参考链接

hunting引用

更多专业前端知识,请上 【猿2048】www.mk2048.com
原文链接:https://www.mk2048.com/blog/blog.php?id=0bhb2jjakj&title=%E4%B8%80%E8%B7%AF%E8%B5%B0%E6%9D%A5%E7%9A%84%E2%80%9C%E5%AD%97%E4%BD%93%E2%80%9D
打开CSDN,阅读体验更佳

热门推荐 大学一路走来,学习互联网全靠这几个网站,最终拿下了一把offer
大佬原来都是这样炼成的
继续访问
互联网行业一路走来的小计(持续更新中.......)
用过的代码编译器、插件和图标样式
继续访问

vue,一路走来(2)--路由vue-router
安装Mint UI cnpm install mint-ui --save 如果你的项目会用到 Mint UI 里较多的组件,最简单的方法就是把它们全部引入。此时需要在入口文件 main.js 中: 自己觉得mint-ui的文件介绍不是很详细,简单介绍一下我遇到的问题吧! 1.Swipe 轮播图:记得一定要给个高度 <!--轮播图--> <div clas...
继续访问
OC Extension Font(字体宏定义)
一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。 很多人在开发过程中会针对公司的需求都会对字体做一个宏定义的封装。方便日后的使用。下面简单的写一下。 #ifndef FontHeader_h #define FontHeader_h //kFont #define kFont_10 [UIFont systemFontOfS
继续访问

分享一组矢量图标–UX图标字体库
科技日新月异的今天,市面上各种分辨率、各种显示精度的显示设备层出不穷,在不同精度不同分辨率的显示设备下图标的应用展现出现了不同要求;如设计师为iphone 3gs设计的图标在iphone4下的展现就显得很糟糕了。我们为pc端产品设计的图标到平板电脑上显示(new ipad)上展现就不尽人意了。那这么多的显示设备,那么多的图标尺寸设计师难道要一个一个去优化去设计吗?
继续访问
Linux gvim 编辑器修改配色方案、字体、字号
1. gvim相比于vim,目前知道gvim是可以单独窗口运行的,像gedit一样。vim打开的文件貌似只能显示在终端内。但是二者安装的位置以及配置文件是很有联系的,暂时的感觉是gvim是对vim的封装,对vim配置文件的修改也影响到gvim。(都仅仅是感觉而已,说错不要拍砖) 2. 通过apt-get install vim-gtk安装gvim后,发现原生态的配色有点惨不忍睹,遂即g...
继续访问
win7字体放大不放大窗口_放大不确定的未来
win7字体放大不放大窗口 介绍 (Introction) A little while back, I wrote an article for Data Driven Investor. It addressed a subject that one might rightly say should be off-limits for public discussion today, an...
继续访问
一段字体CSS
花开了就欢喜,花谢了就放弃,陪你一路走来满心欢喜,是因为风景也是因为你。 1. 工作室介绍2. 工作室成员介绍 Name: KinaSex: FemaleBirth: 1984.6.8 Location: ShanghaiLiking: Designing, drawing and writingDream: Hav
继续访问
后台界面设计之表单设计规范参考
前言 在后台界面设计之表格设计规范参考一文中,我们对表格中内容的布局、数据的展示、操作项的罗列进行了详细的讲解,本文将对表单的设计规范做一个参考性的建议。 表单是中后台系统最常见的元素模块之一,承载了各个流程中信息数据的录入使命。提高信息数据录入的效率可以加速用户达成目标的时间与降低操作成本。 一般要求在录入前尽可能的使用户理解信息录入的目的与预测并判断需要录入的信息内容,在录入过程中尽可能的减少输错概率并帮助用户快速达成,在录入后即时纠错提示并避免繁复操作等。 1.基本样式 1.1 颜色 使用色彩系统设定
继续访问

Android开发未来的出路何在
Android开发的现状 目前,移动开发已经处于饱和的阶段,Android开发也不如当年盛况,已经不再像前几年前那么火爆。正如一种编程语言如果经历过盛极一时,那么必然有这样的一条曲线,像我们学的正弦曲线先急速上升,然后到达顶点,然后再下降,最后再趋近一个平稳的值。 可以看到,从2016年的下半年开始,移动互联网基本处于缓慢发展的阶段,很多大佬称之为互联网的下半场。如果移动互联网的前半场是粗放式的强...
继续访问

SaaS前世今生:老树开新花
戳蓝字“CSDN云计算”关注我们哦!作者 |文东海出品 | CSDN云计算(ID:CSDNcould)2019年3月26日,Adobe和微软宣布,两家公司准备展开一项合...
继续访问
CSDN 一路走好 之Wiki与Blog
CSDN一路走来,从无到有,慢慢地,慢慢地,他什么都要做了频道开的一天天多,但是定位呢?开起来了,火几天,灭了。长久来,就累积了不少垃圾,如前文所述。今天来说说Wiki,Wiki是个好东西,但是CSDN用来做什么呢?就摆个程序放在那里,是不是浪费,坦白地说,很多人就三分钟热度,,今天戳几笔明天戳几笔,后天走人了到后来,这个Wiki就成了个垃圾站,徒劳地浪费了带宽 和 服务器。来,随便抓个页面佐
继续访问
Sublime Text 3 全程详细图文原创教程
Sublime Text 3 全程详细图文原创教程(持续更新中。。。) 一、 前言 使用Sublime Text 也有几个年头了,版本也从2升级到3了,但犹如寒天饮冰水,冷暖尽自知。最初也是不知道从何下手,满世界地查找资料,但能查阅到的资料,苦于它们的零碎、片面,不够系统和全面,所以一路走来,耗费了本人大量的时间和精力。所以蒙生了写这篇《Sublime Text 3
继续访问
OC 基础 Label
一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。 1.常用属性 属性 注释 frame 约束 text 文本内容 numberOfLines 设置行数 textColor 文本颜色 backgroundColor 背景颜色 font 文字大小 textAlignment 对齐方式 en
继续访问
[Android] 环境配置之正式版Android Studio 1.0
昨天看见 Android Studio 1.0 正式版本发布了;心里挺高兴的。 算是忠实用户了吧,从去年开发者大会一开始出现 AS 后就开始使用了;也是从那时开始就基本没有用过 Eclipse 了;一路走来,遇到过 BUG ,也不断的去国外找资源 找解决办法。总的来说挺好的;有些回忆。 这里不讲具体的源码编辑,只是讲怎么下载配置直至简单的使用。
继续访问
webpack
parcel开发第三方包的时候用的比较多,webpack做项目的时候用的比较多遇到的问题:ES6导入语法import在浏览器中报错,不兼容ES6疑问:这里js文件要在src下,不能在src的js下,否则报错,why解决: 因为webpack默认的入口是src/index.js文件 所以要改的话,得进行配置,后面会涉及到怎么配置。在webpack.config.js配置文件中,通过entry节点指定打包的入口。通过output节点指定打包的出口。
继续访问

最新发布 iText7高级教程之html2pdf——5.自定义标签和CSS应用
假设我们想向不同的人发送邀请函。和,html文件内容如下:
继续访问

React 编写网页聊天界面(仿钉钉)
React 编写网页聊天界面(仿钉钉)
继续访问

5-前端笔记-CSS-Emmet语法
Emmet语法
继续访问
前端
操作系统
Mac

⑥ 微信小程序开发用什么语言

建议:

微信小程序用户javascript开发的,你可以看看官网的demo

https://mp.weixin.qq.com/debug/wxadoc/dev/index.html

⑦ 前端可以直接拿到wx.config需要的签名之类的吗

config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
其中主要获取signature这个参数,官方文档地址 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

获取signature主要分四部

1、使用APPID和APPSecret获取access_token;
2、使用access_token获取jsapi_ticket ;
3、用时间戳、随机数、jsapi_ticket和要访问的url按照签名算法拼接字符串;
4、对第三步的字符串进行SHA1加密,得到签名。
注意事项:
1、签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
2、签名用的url必须是调用JS接口页面的完整URL。
3、出于安全考虑,开发者必须在服务器端实现签名的逻辑。
第一步:获取access_token(需要在服务器上 )
/**
* 微信小程序获取accessToken
*
* @author Mr.Wen
* @time 2017年8月28日
*/
public class GetAccessTokenUtil {
// 网页授权接口
public final static String GetPageAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
public static Map<String, String> getAccessToken(String appid, String appsecret) {
String requestUrl = GetPageAccessTokenUrl.replace("APPID", appid).replace("SECRET", appsecret);
HttpClient client = null;
Map<String, String> result = new HashMap<String, String>();
String accessToken = null;
try {
client = new DefaultHttpClient();
HttpGet httpget = new HttpGet(requestUrl);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String response = client.execute(httpget, responseHandler);
JSONObject OpenidJSONO = JSONObject.fromObject(response);
accessToken = String.valueOf(OpenidJSONO.get("access_token"));
result.put("accessToken", accessToken);
} catch (Exception e) {
e.printStackTrace();
} finally {
client.getConnectionManager().shutdown();
}
return result;
}
}
第二步:获取jsapi_ticket
/**
* @author Mr.Wen
* @description 获取ticket
* @date 2018/3/29
*/
public class JsapiTicketUtil {
// 网页授权接口
public final static String GetPageAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

public static Map<String, String> JsapiTicket(String accessToken) {
String requestUrl = GetPageAccessTokenUrl.replace("ACCESS_TOKEN", accessToken);
HttpClient client = null;
Map<String, String> result = new HashMap<String, String>();
try {
client = new DefaultHttpClient();
HttpGet httpget = new HttpGet(requestUrl);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String response = client.execute(httpget, responseHandler);
JSONObject OpenidJSONO = JSONObject.fromObject(response);
String errcode = String.valueOf(OpenidJSONO.get("errcode"));
String errmsg = String.valueOf(OpenidJSONO.get("errmsg"));
String ticket = String.valueOf(OpenidJSONO.get("ticket"));
String expires_in = String.valueOf(OpenidJSONO.get("expires_in"));
result.put("errcode", errcode);
result.put("errmsg", errmsg);
result.put("ticket", ticket);
result.put("expires_in", expires_in);
} catch (Exception e) {
e.printStackTrace();
} finally {
client.getConnectionManager().shutdown();
}
return result;
}
}
第三部:用时间戳、随机数、jsapi_ticket和要访问的url按照签名算法拼接字符串
String noncestr = WXUtil.generate();//随机字符串
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳
//4获取url
//5、将参数排序并拼接字符串
String str = "jsapi_ticket="+ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
第四部:对第三步的字符串进行SHA1加密,得到签名,并返回结果
String str = "jsapi_ticket="+ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
//6、将字符串进行sha1加密
String signature = SHA1.SHA1(str);
Map<String,String> map=new HashMap();
map.put("timestamp",timestamp);
map.put("accessToken",accessToken);
map.put("ticket",ticket);
map.put("noncestr",noncestr);
map.put("signature",signature);

⑧ 使用WebRTC搭建前端视频聊天室——点对点通信篇

WebRTC给我们带来了浏览器中的视频、音频聊天体验。但个人认为,它最实用的特性莫过于DataChannel——在浏览器之间建立一个点对点的数据通道。在DataChannel之前,浏览器到浏览器的数据传递通常是这样一个流程:浏览器1发送数据给服务器,服务器处理,服务器再转发给浏览器2。这三个过程都会带来相应的消耗,占用服务器带宽不说,还减缓了消息从发送到接收的时间。其实最理想的方式就是浏览器1直接与浏览2进行通信,服务器不需要参与其中。WebRTC DataChannel就提供了这样一种方式。

如果对WebRTC和DataChannel不太了解的同学,可以先阅读如下文章:

- WebRTC的RTCDataChannel

- 使用WebRTC搭建前端视频聊天室——信令篇

- 使用WebRTC搭建前端视频聊天室——入门篇

当然服务器完全不参与其中,显然是不可能的,用户需要通过服务器上存储的信息,才能确定需要和谁建立连接。这里通过一个故事来讲述建立连接的过程:

不如钓鱼去

一些背景:

现在,老刘听说老姚钓鱼技术高超,想和老姚讨论钓鱼技巧。只要老刘和老姚相互之间知道对方的门牌号以及凭证,就可以串门了:

老刘和老姚相互之间知道了对方的门牌号和小区出入凭证,他们相互之间有什么需要交流的直接串门就行了,消息不再需要门卫老大爷来代为传达了

换个角度

我们把角色做一个映射:

于是乎故事就变成了这样:

这样,就建立了一个点对点的信道,流程如下所示:

故事

老刘和老姚已经可以相互串门了,经过一段时间的交流感情越来越深。老姚的亲友送了20斤葡萄给老姚,老姚决定送10斤给老刘。老姚毕竟年事已高,不可能一次带10斤。于是乎,老姚将葡萄分成了10份,每次去老刘家串门就送一份过去。

这里可以做如下类比:

这其实就是通过datachannel传输文件的方式,首先将文件分片,然后逐个发送,最后再统一的进行组合成一个新的文件

分片

通过HTML5的File API可以将type为file的input选中的文件读取出来,并转换成data url字符串。这也就为我们提供了很方便的分片方式:

组合

通过datachannel发送的分片数据,我们需要将其进行组合,由于是data url字符串,在接收到所有包之后进行拼接就可以了。拼接完成后就得到了一个文件完整的data url字符串,那么我们如何将这个字符串转换成文件呢?

方案一:直接跳转下载

既然是个dataurl,我们直接将其赋值给window.location.href自然可以下载,但是这样下载是没法设定下载后的文件名的,这想一想都蛋疼

方案二:通过a标签下载

这个原理和跳转下载类似,都是使用dataurl本身的特性,通过创建一个a标签,将dataurl字符串赋值给href属性,然后使用download确定下载后的文件名,就可以完成下载了。但是很快又有新问题了,稍微大一点的文件下载的时候页面崩溃了。这是因为dataurl有大小限制

方案三:blob

其实可以通过给a标签创建blob url的方式来进行下载,这个没有大小限制。但是我们手上是dataurl,所以需要先进行转换:

获得blob后,我们就可以通过URL API来下载了:

这里有几个点:

1. datachannel其实是可以直接传送blob的,但是只有ff支持,所以传data url

2. chrome下载是直接触发的,不会进行询问,firefox会先询问后下载,在询问过程中如果执行了revokeObjectURL,下载就会取消,囧

升级

如我们所知,WebRTC最有特点的地方其实是可以传输getUserMedia获得的视频、音频流,来实现视频聊天。但事实上我们的使用习惯来看,一般人不会一开始就打开视频聊天,而且视频聊天时很消耗内存的(32位机上一个连接至少20M左右好像,也有可能有出入)。所以常见的需求是,先建立一个包含datachannel的连接用于传输数据,然后在需要时升级成可以传输视频、音频。

看看我们之前传输的session description,它其实来自Session Description Protocol。可以看到wiki上的介绍:

这意味着什么呢?我们之前建立datachannel是没有加视频、音频流的,而这个流的描述是写在SDP里面的。现在我们需要传输视频、音频,就需要添加这些描述。所以就得重新获得SDP,然后构建offer和answer再传输一次。传输的流程和之前一样,没什么区别。但这一次,我们不需要传输任何的ice candidate,这里我曾经遇到了坑,经过国外大大的点拨才明白过来。

Peertc

我将datachannel和websocket组合,实现了一个构建点对点连接的库Peertc,它提供非常简洁的方式来建立连接和发送数据、文件和视频/音频流,详情见github。走过路过的记得star一下哦,有什么bug也非常希望能够提出来。

最后

WebRTC的点对点方式能够运用在很多场景:

- 如web qq这种Web IM工具,这就不说了

- 如象棋这种双人对战 游戏 ,每一步的数据服务器时不关心的,所以完全可以点对点发送

- 一对一在线面试、在线教育,这其实是即时通信的一个业务方向

⑨ 前端JS AES加密 后端PHP AES加解密

<!DOCTYPEhtml>

<html>

<head>

    <title>aes demo</title>

</head>

<body>

    <script type="text/javascript" src="./CryptoJS/aes.js"></script>

    <script type="text/javascript" src="./CryptoJS/pad-zeropadding.js"></script>

    <script type="text/javascript">

    text = 'did=12345dg&version=1';

    var key = '';

    key = CryptoJS.enc.Utf8.parse(key);

    var iv = "";

    iv = CryptoJS.enc.Utf8.parse(iv);

    var encrypted = CryptoJS.AES.encrypt(text, key, {

        iv: iv,

        mode: CryptoJS.mode.CBC,

        padding: CryptoJS.pad.ZeroPadding

    }).toString()

    document.write(encrypted);

    </script>

</body>

</html>

后端

public function encrypt2($input = ''){

        $pk = "";

        $iv = substr("", 0, 16);

        // $t = 'T10515';

        $encrypted = ( mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $pk, $input, MCRYPT_MODE_CBC,$iv) );

        return base64_encode($encrypted);

    } 

    public function decrypt2($encrypted){

        $pk = "";

        $iv = substr("", 0, 16);

        // $t = 'T10515';

        // $encrypted = "b7y/JPJFNTfxNVR8H4NNtw==";

        return  mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $pk, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv);

    }

可以推断, js 部分实际只使用了 iv 的 16 位长度

ps:

js加密后默认会base64_encode

使用php解密需要base64_decode后才一致

所以使用js加密和php解密时候需要注意,php端要先base64_decode再解密,例:

js加密的串是$t=b7y/JPJFNTfxNVR8H4NNtw==

php需要

$password= trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $pk,base64_decode( $t), MCRYPT_MODE_CBC, $iv));

则$password才能正确解析出来

需要引入的两个js文件

ase.js

[javascript] view plain

/*

CryptoJS v3.1.2

code.google.com/p/crypto-js

(c) 2009-2013 by Jeff Mott. All rights reserved.

code.google.com/p/crypto-js/wiki/License

*/

var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},

r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<

32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,

2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},

q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);

a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,

e)).finalize(b)}}});var n=d.algo={};return d}(Math);

(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<

l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"+/="}})();

(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},

_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),

f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,

m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,

E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/

4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);

(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,

l)}})();

CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},

finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=

c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,

e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,

this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,

1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},

decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,

b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();

(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,

16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchele=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchele=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>

8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchele,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchele,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=

d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();

pad-zeropadding.js

/*

CryptoJS v3.1.2

code.google.com/p/crypto-js

(c) 2009-2013 by Jeff Mott. All rights reserved.

code.google.com/p/crypto-js/wiki/License

*/

/**

* Zero padding strategy.

*/

CryptoJS.pad.ZeroPadding = {

    pad: function (data, blockSize) {

        // Shortcut

        var blockSizeBytes = blockSize * 4; 

        // Pad

        data.clamp();

        data.sigBytes += blockSizeBytes - ((data.sigBytes % blockSizeBytes) || blockSizeBytes);

    }, 

    unpad: function (data) {

        // Shortcut

        var dataWords = data.words; 

        // Unpad

        var i = data.sigBytes - 1;

        while (!((dataWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)) {

            i--;

        }

        data.sigBytes = i + 1;

    }

}; 

⑩ 前端商品项目管理系统有哪些

1、通用项目管理系统-Worktile(5星)

Worktile 是国内的一款老牌通用项目管理系统,具有非常高的功能成熟度,连续多年登上:36氪-中国企业服软件金榜-项目管理榜单排名前三(2021 TOP1);它的客户包含网络、招商银行、小米等。

Worktile 是一款非常适合中小企业的项目管理系统,因为它具备项目管理、OKR(目标)管理、审批、简报、IM、网盘等模块,是一个工具的集合,能同时满足企业多种工具化管理需求,从而大幅降低成本。

产品能力介绍:

看板式的项目管理,可视化每个项目/任务所处阶段;

项目、项目集管理,监控项目进度,管理项目资源;

甘特图等多种报表视图,可视化项目进度、资源分配、目标完成情况;

不限量不限速的网盘,管理团队内部文件;

OKR管理,协助团队更好的落地OKR,实现管理变革;

日程管理,团队安排会议和行程

聊天功能,团队可以在工具内实时沟通,且记录、文件永久保留;

工具集成,与第三方软件协作使用;



购买方案:

10人以下免费

支持私有部署、公有云、定制化开发等;



Worktile 在36氪企服点评的用户评价:

“Worktile很注重partner之间的沟通,在群聊这些功能做的比较完善,不用切换软件就可以布置任务、沟通进度,让我们工作推行更节省时间。它的任务管理也是比较出彩的,整个team都能明确知道工作进程到哪一步,同事们之间的沟通也是比较密切,是一个优秀的办公工具。总的来说软件的功能比较完善,可以以一当十,对于运营工作来说真的很节省时间。”——Zoe



官网:Worktile-50万+团队都在用的 项目协作工具



2、软件项目管理系统-PingCode(5星)

在我的测试中,PingCode 国内功能最全面、最好用的IT项目管理软件之一,也是最推荐的一款IT项目管理工具之一。因为它能够满足需求管理、规划、开发、编码、构建、测试到发布上线研发全生命周期的管理,并且上手非常容易。

除此以外,PingCode 曾在2021年发布了国内首款研发自动化管理引擎,并且2021年入选软件研发项目管理榜TOP1。

产品能力介绍:

PingCode 具备8大子产品,覆盖从客户反馈、规划、开发、编码、构建、测试到发布上线等研发管理全流程管理:

Ship(产品管理):提供工单收集、需求池管理、需求评审、产品路线图等能力,打通客户、业务团队和产研团队之间的协作,帮助团队规划产品路线;

Project(项目管理):支持敏捷开发、瀑布开发、Kanban等国内主流研发管理模式,以及规模化敏捷(SAFe)、项目集等的管理,规范团队的协作流程;

Testhub(测试管理):提供用例维护、评审,测试计划、自动化测试、测试报告、缺陷提交等能力;

Wiki(团队知识库):提供文档协作、结构化团队知识管理等能力,帮助生产和结构化沉淀团队规范、制度、实践经验;

Goals(目标管理):提供战略目标、团队目标、个人目标管理,让所有项目都聚焦于共同的目标,并在更高视野上及时了解企业目标进展;

Flow(研发自动化管理):提供自动化技术,将重复性和烦闷的手动操作变成自动化执行,让团队专注高价值生产;

Insight(效能度量):提供研发效能自动采集等能力,通过数据驱动的方式更加准确地评估和改善研发效能;

Access(目录服务):集成企业级帐号目录和支持单点登录,全局统—安全管控;

应用市场:集成了研发中主流的工具,如Git、Jenkins、gitlab等等,实现了不同工具间的数据打通;能够在飞书、企微等平台使用;



购买方案:

25人以下免费

支持定制化开发、私有部署、公有云等;



PingCode 在36氪企服点评的用户评价:

“以前用jira,自从去年Jira在国内停售本地版就开始找替代工具,来来回回尝试了很多个产品,还是这个能比较好的替代Jira,无论是数据迁移,还是三级需求管理体系、产品路线图、燃尽图等可视化报表,或者是打造适合的工作流,都能比较好的满足。”——Helloworld

官网:PingCode-智能化研发管理工具



3、通用项目管理系统-Asana(4.5星)

Asana 是国外G2网站上评分第一的项目管理软件,很适合在境外的用户使用,但国内使用通常会遇到访问速度慢以及售后服务问题(比如知乎上有人说一个需求等了三年),但它仍然是一个好用的项目管理软件;

功能/解决的问题:

支持看板管理,让工作内容的所处阶段都一目了然;

甘特图进行有序的项目规划,及时提醒避免项目延期;

集成电子邮箱等100多种办公应用;

查看团队成员的任务和优先级

支持建立任务依赖关系;

支持设立个人工作管理区,将个人和团队的项目做出区分;

软件优势:

具有非常人性化的功能设计,比如设立私有项目,为敏感工作创造一个安全的空间;

有非常详细的项目/任务记录,能查看完成任务的完整历史记录以及他们是如何完成的;

在提供软件的同时,同样也提供专业的管理咨询(国内除外)

支持多种语言



4、远程项目管理系统-Wrike(推荐指数5星)

Wrike 是一款专门为异地团队协作打造的项目管理软件,是2018 年FinancesOnline最佳项目管理软件奖的获得者。

功能/解决的问题:

支持任务管理、甘特图、实时的进度提醒等常用的项目管理能力;

除此以外,还支持文档协作,为团队的远程协作提供了非常做的共享能力

工作量管理、电子邮件系统集成、简报等;

支持各种定制化的报表,用于分析定位管理中的问题;

所有共享活动都可以按时间顺序查看,可取消关注,且所有团队成员都可以查看彼此的活动进度;

软件优势:

Wrik除了非常全面的远程项目协作功能,它还有出色的安全管理方案,以确保除授权人员外,没有其他人可以访问到在线数据库中的信息和其他文件。

对比其他项目管理软件,它能更轻松的、随时随地跟踪和监控进度;

Wrike 的分类管理方法非常有意思,它能够在你将项目放在不同的文件夹中的时候不会重复。

它能的项目管理方案让提醒和需求的响应变得更加及时;



5、软件项目管理系统-Jira(4.5星)

Jira 在软件项目管理方面提供了非常成熟的功能,可以适应许多场景,无论是初创公司还是大型企业Jira都能较好的提供服务。Jira支持多种设备并提供灵活的部署方式,可快速完成上线,并允许技术人员或非技术人员进行个性化配置。

虽然2022年在国内停售本地版,一定意义上形成了对国内用户的禁售,但这也丝毫不影响他是一个好的项目管理系统。

Jira 提供了以下主要能力:

敏捷、瀑布开发管理

强大的自定义工作流

无限添加的自定义字段

BUG跟踪

仪表板

安全管理

团队活动报告

第三方业务系统集成等

优劣势:

功能成熟度高,支持高度自定义

成本极高,只适用于大型企业

上手难度大,需要长时间的培训团队才可能灵活使用

汉化不完全





四、最佳的5大开源项目管理系统



6、项目管理和缺陷跟踪工具 Redmine(5星)

Redmine 是一个开源的、基于Web的项目管理和缺陷跟踪工具。它用日历和甘特图辅助项目及进度可视化显示。同时它又支持多项目管理。Redmine是一个自由开放 源码软件解决方案,它提供集成的项目管理功能,问题跟踪,并为多个版本控制选项的支持。





7、开源项目管理平台 Taiga

Taiga 是一个免费开源,而且功能非常强大的项目管理平台,用于初创企业和敏捷开发团队。提供一个简单、漂亮的项目管理工具。

Taiga 采用 Python Django 框架开发,前端基于 AngularJS 实现。

优点:Taiga.io逐渐成长为功能齐全的应用程序,具有多种模式(Kanban,scrum),backlogs,用户故事以及敏捷开发所需的一切。该应用程序是可定制的,并具有一些敏捷开发中不常见的加成功能,如视频会议工具和内置维基。

缺点:Taiga.io仍处于测试阶段,并一直在与相关问题进行斗争,比如服务器有时可能会比蜗牛还慢(尽管不是定期的)。没有甘特图功能,有些用户表示界面很差劲。





8、基于 Web 的项目管理系统 project-open

project-open 是一个基于 Web 的项目管理系统,从整个组织的角度看类似于企业资源计划enterprise resource planning(ERP)系统。它还可以管理项目档案、预算、发票、销售、人力资源和其他功能领域。有一些不同的变体,如用于管理项目公司的专业服务自动化professional services automation(PSA)、用于管理企业战略项目的项目管理办公室project management office(PMO)和用于管理部门项目的企业项目管理enterprise project management(EPM)。

project-open[甘特图编辑器包括按等级划分的任务、依赖关系和基于计划工作和分配资源的调度。它不支持资源日历和非人力资源。]project-open[ 系统非常复杂,其 GUI 可能需要刷新。

project-open是用 TCL 和 JavaScript 编写的,可用于 Windows 和 Linux。 project-open核心采用 GPLv2 许可证,并具有适用于大公司的专有扩展。

适合于: 需要大量财务项目报告的大中型项目组织。

独特卖点: project-open是一个综合系统,可以运行整个项目公司或部门。







9、桌面项目管理系统 ProjectLibre

在开源世界中,ProjectLibre 可能是最接近 MS-Project 的产品。它是一个桌面应用程序,支持所有重要的项目计划功能,包括资源日历、基线和成本管理。它还允许你使用 MS-Project 的文件格式导入和导出计划。

ProjectLibre 非常适合计划和执行中小型项目。然而,它缺少 MS-Project 中的一些高级功能,并且它的 GUI 并不是最漂亮的。

ProjectLibre 用 Java 编写,可用于 Windows、Linux 和macOS,并在开源的通用公共署名许可证Common Public Attribution License(CPAL)下授权。ProjectLibre 团队目前正在开发一个名为 ProjectLibre Cloud 的 Web 产品,并采用专有许可证。

适合于: 负责中小型项目的个人项目管理者,或者作为没有完整的 MS-Project 许可证的项目成员的查看器。

独特卖点: 这是最接近 MS-Project 的开源软件。





10、多项目管理系统 TaskJuggler

TaskJuggler 用于在大型组织中安排多个并行项目,重点是自动解决资源分配冲突(即资源均衡)。

它不是交互式的甘特图编辑器,而是一个命令行工具,其工作方式类似于一个编译器:它从文本文件中读取任务列表,并生成一系列报告,这些报告根据分配的资源、依赖项、优先级和许多其他参数为每个任务提供最佳的开始和结束时间。它支持多个项目、基线、资源日历、班次和时区,并且被设计为可扩展到具有许多项目和资源的企业场景。

使用特定语法编写 TaskJuggler 输入文件可能超出了普通项目经理的能力。但是,你可以使用 ]project-open[ 作为 TaskJuggler 的图形前端来生成输入,包括缺勤、任务进度和记录的工作时间。当以这种方式使用时,TaskJuggler 就成为了功能强大的假设情景规划器。

TaskJuggler 用 Ruby 编写,并且在 GPLv2 许可证下可用于 Windows、Linux 和 macOS。

适合于: 由真正的技术极客管理的中大型部门。

独特卖点: 它在自动资源均衡方面表现出色。









在本文中,我们探讨了开源项目管理系统的优劣势,盘点了国内外一些开源的项目管理软件和非开源的项目管理系统,希望以上内容对你有所帮助。