随着互联网的发展,我们的业务也日益变得更加复杂且多样化起来,前端工程师也不再只是做简单的页面开发这么简单,我们需要面对的十分复杂的系统性问题,例如,业务愈来愈复杂,我们要如何清晰地梳理;团队人员愈来愈多,我们要如何更好地进行团队协作;功能愈来愈多,我们要如何保证页面的性能不至于下降,等等。所有的这些都可以归结为如何提升开发体验和性能问题。

提升开发体验

我们主要从以下三个方面来提升我们的开发体验。

规范化

当团队人员不断扩充时,我们需要制定统一的规范来对平时的开发工作做出一定约束和指导。统一的规范包括前端的代码规范,根据规范定义好一套代码检查的规则,在代码提交的时候进行检查,让开发人员知道自己的代码情况。

同时,根据以往的开发经验,我们制定了统一的项目框架,根据业务功能不同,将一个项目(app)拆分成不同的业务模块(module),而每一个模块都包含自身的页面(page)以及构成页面所需要的组件(widget),每一个项目涉及到app、module、page、widget这些已经约定好的概念,这样让项目结构更加清晰,而且让团队内不同业务的人员之间切换无障碍。

组件化

在项目中引入组件化的概念,这里的组件对应上文讲到的widget,每一个组件都会包含组件自身的模板、css、js、图片以及说明文件,我们使用组件来拼装页面,像搭积木一样来拼装我们的页面,同时一个组件内可以调用另一个组件。

在拿到设计稿后,我们首先需要确定哪些需要做成公共组件,那些是要做成独立组件,以及组件间如何进行通信。在页面中调用这些组件后,会自动加载组件的模板以及组件的静态资源,而当组件不再需要时,只要移除掉组件引用,那么相应的模板和静态资源也会不再加载。

组件化的好处主要有这么几点

  • 管理方便,我们可以把一个独立功能相关的文件在工程目录中放在一起,这样代码管理起来会非常便利
  • 组件复用,通过抽取公共组件,可以实现组件复用,从而减少工作量,创造价值
  • 分而治之,这是组件化最重要的一点,将页面组件化,就是对页面功能的拆分,将一个大的工程拆成小的零件,我们只需要关注每一个零件的功能,极大地降低了页面的开发与维护的难度

自动化编译

在前端开发中,我们总是会去使用很多工具、手段来优化代码、提升开发效率,例如,我们会使用sass、less等CSS预处理工具来编写更好维护的样式代码,我们也会使用CSSLint、eslint等代码检查工具来检查代码的语法错误,使用文件合并压缩等手段来减少资源大小,除此之外我们还会去做雪碧图合并、多倍图处理、字体压缩处理、代码发布等等。

曾经有大神说过,超过90s的工作都应该自动化掉。而以上所有的这些工作,贯穿我们整个开发流程,但是不同工具的切换不但显得凌乱,而且影响开发效率。在自动化、工程编译的思想早已深入人心的当下,我们当然也要紧跟潮流,所以我们考虑通过自动化手段来提升我们的效率,让所有操作可以一键式开速执行完。

我们将通过定义好一系列的编译任务,按照一定顺序依次对我们的项目自动进行编译操作,最后产生出可上线的代码。

提升性能

我们主要从以下四个方面来做好性能优化。

首屏优化

页面的打开速度一直是大家非常关心的一个指标,一个页面打开太慢会让让用户失去等待的耐心,为了让用户更快地看到页面,我们考虑将页面中部分静态资源代码直接嵌入页面中,我们通过工具处理,在工程编译阶段,将指定的静态资源代码内嵌入页面中,这样可以减少HTTP请求,提升首屏加载速度,同时降低页面裸奔风险。

按需加载

同时,我们考虑通过尽量减小页面体积来提升页面打开速度,在业务上我们将页面划分为一个个楼层组件,以京东美妆馆为例,页面中从上而下分为首焦、至IN尖货、今日特惠、潮流前沿、口碑榜单这么几个楼层组件,其实这个页面还有很长,内容非常多且复杂。

之前我们的做法是整个页面直出,这样一次性加载的内容会非常多,为了提升打开速度,我们考虑通过按需加载的方式来优化页面的加载。我们在页面中只放每一个楼层的框架性代码,楼层的模板和数据都通过异步的方式去拉取,来实现楼层组件的按需加载,同时我们可以对模板以及数据进行缓存,以此来减少请求,做更极致的优化。在开发中我们以正常组件的方式去开发整个页面,随后通过编译工具,在代码编译阶段自动将楼层的模板抽离成一个独立的JS文件,并给楼层容器打上标记位,通过页面加载逻辑去按需拉取模板,再进行渲染。

通过给楼层容器和模板分别加上标记位 o2-out-tpl-wrapper o2-out-tpl

在编译时自动将指定的模板代码抽离成独立js文件

并且给楼层容器打上标记

同时在逻辑脚本适当位置自动加入模板的版本

通过上述步骤,实现按需加载的自动化生成,在提升性能的同时,很好地解放我们生产力。

基于资源表加载

根据页面组件化,通过工具分析,我们将获得页面与组件的依赖关系表,同时也能确认页面所引用资源的依赖关系,例如,我们在页面hello中同步引用组件topbar,那么依赖关系表中将会记录同步引用关系hello引用topbar.tpl、topbar.css、topbar.js,那么页面hello将会自动加载组件topbar的CSS与JS,同时依赖表会记录异步引用的关系,假如我们在组件C中通过API异步引用了组件D的js,那么会在依赖表中记录C异步引用D.js这一个依赖关系,这样D.js这个资源将会在用到的时候被异步调用。

同步引用的资源通过生成combo形式链接,在服务端进行文件合并,这样在页面加载的时候,页面只会加载自己需要的同步资源,异步的资源将会在用到的时候再加载,有效避免资源冗余。同时删除、增加组件也非常方便,只需改动模板中对组件调用,通过编译工具会自动重新生成模板以及combo链接。

我们可以将资源加载的操作抽离出来,形成一套统一的资源加载框架设计,这样我们使用的模板可以变得更加灵活,无论是纯html模板,还是PHP或Java之类的后端模板都能有效支持。编译工具扫描代码后只生成资源依赖表,我们通过实现各语言平台的资源加载框架,让不同语言的模板都能基于同一个资源依赖表进行资源加载。

同时,对资源进行MD5重命名处理,文件md5重命名也是一种提升性能的有效手段,使用文件md5后开启服务器强缓存,可以提升缓存的利用率并避免不必要的缓存判断处理。但文件md5重命名后会出现开发时引用的文件名对不上的问题,这就需要在资源表中记录原文件名与md5重命名后之间的对应关系,当我们引用一个资源时,就会通过查表获取重命名后的资源名,然后利用代码中引用资源定位的能力来进行资源名自动替换。

静态资源预加载

所谓静态资源预加载,就是当用户在进行浏览页面的时候,我们可以在当前页面静默加载下一个页面的静态资源,这样当用户进入到下一个页面时就能快速打开页面,从而在不知不觉中提升页面的打开速度。

我们会在静态资源预加载平台上配置每一个页面id对应需要预加载页面资源的id,然后系统通过读取资源依赖表获取到所需要预加载的静态资源,生成预加载资源列表文件,再将文件推送到线上服务器,通过页面挂载js请求获取预加载资源列表,随后静默加载资源。在有了资源依赖表后,我们可以准确地分析到每一个页面引用资源的请求,就可以很好地实现静态资源预加载的功能。

Athena

工欲善其事,必现利其器。为了实现我们对提升开发效率和产品性能的诉求,我们提出了比较完整的工程化解决方案以及对应的工具Athena。

Athena是由京东【凹凸实验室】(aotu.io) 推出的一套项目流程工具,通过Athena,我们可以很流程地跑完整个开发流程。Athena分为两部分,一是本地自动化编译工具,二是资源管理平台,其架构如下

本地自动化工具

Athena本地编译工具是一个基于NodeJs的命令行工具,通过执行命令的方式来优化我们的开发流程,目前Athena的主要功能有

  • 自动创建项目、模块、页面、组件结构
  • 轻量组件化功能,根据组件加载情况生成资源依赖表
  • Sass/less 编译
  • 代码检查
  • CSS prefix等处理
  • CSS合并压缩,JS合并压缩
  • 自动生成雪碧图,自动多倍图,图片压缩
  • 字体文件压缩
  • 自定义图片转base64
  • 文件内联,可以内联样式及JS代码
  • 文件MD5戳,将文件进行使用MD5进行重命名
  • 本地预览,直接查看整个项目
  • 资源定位(图片等资源路径替换)
  • 生成CSS页面片,提供将页面引用的CSS/JS抽离成页面片的形式,方便管理CSS资源
  • 部署到预览机和开发机

创建项目结构

在执行创建命令时,Athena会从管理平台下载自定义好的项目模板,可以根据模板创建项目、模块、页面、和组件。Athena有四个创建命令:

通过执行 $ ath app demo 命令就可以生成定义好目录结构的项目。

随后可以通过 $ ath module home来创建一个业务模块;

通过 $ ath page index 来创建页面;

通过 $ ath widget widgetName 来创建组件。

开发使用

组件化

Athena中实现组件化主要是分为两种,一是针对纯HTML模板,通过扩展模板引擎方法实现,提供了组件化API widget.load,它可以方法接收三个参数,第一个参数是widget的名称,后面两个参数是可选参数,第二个是向widget传递的一些参数,第三个是widget所属的模块,如果是本模块,可以不传例如

<%= widget.load('user') %>
<%=widget.load('user', {param: 'test'})
%>
<%= widget.load('user', null, 'gb') %>

通过模板引擎编译,执行widget.load方法,可以实现加载模板,记录依赖关系的目的。

二是针对不同语言的后端模板,通过实现各自的组件化框架来进行组件的加载,例如 PHP 下使用 <?= $widget->load('user', NULL, 'gb') ?> 来进行组件加载,再通过代码扫描得出组件依赖关系。

Athena中的API

Athena针对模板提供了一系列的API来扩展丰富的功能,例如前面提到的 <%= widget.load() %> 来实现组件化。

同时Athena中还提供了其他API:

<%= getCSS() %><%= getJS() %> 用来引用CSS/JS文件,传入文件名和模块名;

<%= uri() %> 提供了资源定位功能,可以在模板中标记资源,编译过程中会进行替换,而且在JS中也有资源定位API __uri()

<%= inline() %> 提供了内联资源的功能,传入文件名和模块名,可以在模板中内联任意资源,例如图片以及JS脚本;而且 inline 也可以内联一段网络资源,例如线上的JS文件,同样的在JS中也有内联资源API __inline()

雪碧图标识 ?__sprite ,在CSS中引用图片最后加上标识 ?__sprite 可以自动生成自定义名称雪碧图,同时支持自定义生成多张雪碧图,只需要要标识后面带上一个文件名,就可以生成一张以这个文件名来命名的雪碧图,例如 ?__sprite=icons ,这样所有带同样标识的图片就会生成一张以 icons为文件名的雪碧图。

编译预览

编译任务

在编写完项目,就可以通过命令来对项目进行编译了,执行编译命令 $ ath build,会针对指定模块执行已经定义好的编译任务,根据项目需求,目前编译都是基于业务模块去编译,编译任务的最小执行单位是页面,每次编译都会执行以下编译列表

本地预览

执行预览命令 $ath serve 会执行精简版编译任务来编译项目,编译完项目后会生成一份站点地图,随后打开一个本地服务器来预览项目,使用这个命令可以很方便地进行开发,在预览时会同时watch目录和文件的改动,并且提供了livereload功能,我们可以在预览时任意修改文件,都将实时地反映到页面中,同时可以新建另一个窗口执行新增组件和页面的操作,让整个开发过程非常顺畅,我们只需关注开发本身就好,不需要再关注其他事。

执行完编译任务后,默认自动打开浏览器,预览站点地图

Mock server

在进行项目预览的同时,Athena同时提供了mock data的服务,我们可以配置相应的路由,以及路由接口对应的假数据,所有的接口请求会发送到mock server上,在mock server中可以选择将请求代理到假数据平台还是代理到线上接口,这样就可以脱离后端进行开发联调了,以此实现数据的前后端分离。

项目部署

在开发预览完后,通过命令 $ ath publish 就可以将项目发布到配置好的测试机上,发布同时支持ftp、sftp以及http形式。

组件维护

我们通过组件化的手段已经将我们的项目进行组件化了,这样我们经过业务迭代积累,产出很多业务公共组件,但在以往的项目开发中,公共组件的更新与维护一直很受限制,而且有哪些公共组件、公共组件长什么样子,只能依靠口口相传或者手工维护的文档。所以在Athena中我们加入了组件平台,在组件平台上统一展示各个业务的公共组件,而得益于本地工具,组件平台不需要人工干预维护,我们可以在本地通过命令 $ ath widget-publish [widgetName] 命令来发布一个组件到组件平台,这样其他人就可以立即在组件平台进行组件的预览,而其他人若想使用该组件时,在本地通过命令ath widget-load [widgetId] 就可以下载该组件到自己的模块目录下了。

这样组件的维护更加自动化,公共组件的使用也更加方便了。

组件发布

组件下载

自身优化

为了提升开发效率,Athena做了一些优化操作

精简项目预览时的任务

在开发时进行项目预览时,会执行精简版的编译任务,剔除了类似文件压缩、雪碧图生成、模板抽离处理等耗时的操作,只保留核心、必须的编译任务,这样可以极大地减少编译时间,提升开发的效率。

预览时监听细化

在开发进行预览时,会对所有文件的改动进行监听,而针对每一类文件都有非常细化的操作,当文件改动时只会执行改文件所需要的编译任务,而不会进行整体编译,这样可以很好地提升开发效率。例如改动某一组件的CSS文件,则只会针对该文件执行一些相关的CSS操作。

同时得益于所有文件依赖关系的记录,在监听时会根据依赖关系进行文件编译,例如某sass文件中引入了另一个sass库文件,修改这个sass库文件的时候,会根据引用关系表同时更新到所有引用到这个sass文件的文件,这样项目文件更新及时,让开发流程更加流畅。

编译缓存

在图片压缩和sass编译时,开启文件缓存,将已经编译过且没有改动的文件过滤掉,不再编译,大幅提升编译速度。

发布缓存

设置发布过滤,根据文件md5过滤掉已经发布过的文件,提升发布速度。

技术选型

Athena本地工具早期技术选型是 Yeoman + Gulp 的方式,但后来由于安装、更新非常麻烦,命令太长很难打的原因,我们改成了自己开发一个全局安装包的方式,编译核心使用的还是 Gulp 的 vinyl-fs 来实现文件流处理,通过 ES6 Promise 来进行编译流程控制,最小以页面为单位,经过一系列编译任务,最后产出编译好的文件。

管理平台

性能优化一直是前端工程师探索的课题,很多时候就是资源的分配问题,也就是资源管理。为了更好地配合本地构建工具来管理资源,我们搭建了管理平台。我们来看下,结合本地构建工具和管理平台,工作流程变成了怎样?

工作流程

  1. 在管理平台上创建项目,输入项目名称和预览机,以及选择相应的模板等;
  2. 在终端执行ath app指令,工具会优先拉取远程服务器的项目信息来初始化项目,如果没有获取到相关信息,就会在本地生成项目,并将项目信息上报给服务器;
  3. 项目初始化后,就可以创建模块、页面、组件了;
  4. 在编码过程中,可通过ath server预览页面;
  5. 在本地通过后,可执行ath publish将代码发布到开发机或者预览机。

在上面的publish指令中,工具会扫描所有文件,执行代码检查,扫描页面文件,获取组件依赖关系,根据组件依赖关系进行文件合并,然后会进行样式处理、js处理以及图片的处理,根据配置是否进行md5重命名文件,组装html,插入样式、js和图片,最后将编译好的文件发布到相应的机器。在整个过程里面,会生成资源关系依赖表,最终会将资源关系表及编译后的文件上传至管理平台。

除此之外,每个指令的操作都会上报给管理平台。管理平台收到数据后,会对数据进行处理,最终可以在平台上看到项目相关的信息。

整体工作流程图如下:

从上面的工作流程中,我们可以看到,管理平台需要有数据统计、资源管理以及项目管理的功能。整体架构图如下:

数据统计

数据统计包含项目操作日志,主要是用于统计团队每个成员具体的操作,方便项目成员查看项目代码变更;另一部份是统计样式表、脚本以及图片的压缩数据,用于显示工具给我们项目带来的提升。

以下是操作日志统计:

资源管理

资源管理是管理平台的核心,主要分为4个部分:模块展示、依赖关系、组件预览和权限控制。这部分功能主要通过本地构建工具提供的资源关系表来完成。

模块展示

模块展示,用于记录项目具体包含哪些模块以及模块具体的信息。在平常开发中,我们的项目会分为许多模块,不同的模块有不同的人来开发和维护。当项目越大的时候,可以通过管理平台清晰地看到模块具体的信息。

依赖关系

依赖关系,主要是html、css、js和图片相互之间的关系。通过分析资源关系依赖表,可以获取到各个资源被引用的情况以及线上版本的情况。当线上环境采用md5来做资源管理时,我们不是很清晰地知道静态资源对应线上哪个版本的资源,而有了这个依赖关系表,当出现问题时,我们可以更快地定位到具体的资源。

组件管理

我们采用组件来拼凑页面,当项目越大时,组件越多,那么如何管理组件成为了一个棘手的问题。比如说,有一些比较老的冗余组件,我们不确定是否为其他页面所引用,那么就不能愉快地删除它。有了组件管理,可以清晰地知道组件的被调用情况,就可以对组件做相应的操作。

组件管理,结合组件平台来使用,在管理平台上引用组件地址预览组件,同时可以获取到组件被引用以及引用资源(如css、js、图片)的相关情况。

我们的组件分为两种,一类是通过ath w自动创建的,通过ath pu提交到管理平台的,在管理平台上进行组件的相关分析和编译,得到组件的信息,这类组件主要是跟业务绑定的;另一类是通过ath widget-publish提交到组件平台的,由组件平台进行相关处理,这类组件是通用组件,与业务无关,用于展示给开发以及相关业务方看的。

在组件平台上可以预览与编辑相关的组件,通过与设计师约定相关的设计规范来促使组件达到尽可能地复用,进而减少设计师的工作量,提升我们的工作效率。

组件提交到组件平台

通过ath widget-publish指令将组件提交到组件平台,组件平台会对组件源码进行编译,将组件名称md5、组件归类以及组件版本记录等等。

从组件平台上下载组件

通过ath widget-load指令将组件下载到本地,当本地构建工具向组件平台发起请求时,会带上组件名称,组件平台会将源码进行编译,将组件名称重命名,并且相应地替换源码中的组件名称,同时记录组件的被引用记录。

权限控制

权限控制,项目中存在公共组件模块,公共组件比较稳定,比如说轮播组件、选项卡组件等等,这部分代码一般比较少变动,可由少部分人来更新和维护,所以加入了权限控制机制,保证公共组件的稳定性。

项目管理

我们在使用本地构建工具时,需要配置多个参数,比如主机信息、选择模版等,在命令行环境下有些不直观。为了简化这个操作,管理平台提供了项目创建的功能,同时提供了模版创建的功能。

在项目信息、模块信息以及组件信息发生变更的时候,为了第一时间能够通知项目成员更新,加入了消息通知的功能,目前通过发送邮件的方式,后期可以加入微信提醒的功能。

技术选型

管理平台前端采用React+Redux的方式,后端采用Express+MongoDB,整体技术选型如下:

假数据服务

存在的问题

在平常的开发中,经常需要前后端联调,但是在项目开始之初,很多接口并没有提供,在以前的开发模式下,需要等待后端提供接口或者自己先定义接口,前端开发的进度可能会受影响。

Mock数据平台

为了不影响前端开发的进度,我们搭建了Mock数据平台,通过与后端协商数据格式,自定义数据接口,这样子就可以做到前后端分离,让前端独立于后端进行开发。

Mock数据平台基于mockjs搭建而成,通过简单的mock语法来生成数据。

Mock数据平台目前有如下功能:

  1. 创建模拟数据,使之符合各种场景;
  2. 生成json数据接口,支持CORS以及jsonp。

写在最后

本次分享首先讲述了我们在业务膨胀、人员不断增加的背景下遇到的项目开发上的问题,并提出了我们自己对于这些问题思考总结后得出的解决方案与思路,最后产出适合我们团队、业务的开发工具—— Athena。希望我们的方案能给大家带来一定的借鉴作用。

京东前端工程化和静态资源管理全面总结相关推荐

  1. 我们是如何做好前端工程化和静态资源管理 - 無雄 - 博客园

    我们是如何做好前端工程化和静态资源管理 随着互联网的发展,我们的业务也日益变得更加复杂且多样化起来,前端工程师也不再只是做简单的页面开发这么简单,我们需要面对的十分复杂的系统性问题,例如,业务愈来愈复 ...

  2. 前端工程化和模块化学习资料汇总

    2019独角兽企业重金招聘Python工程师标准>>> 1.我为什么这么强调前端工程化 2.前端工程 3.前端优化带来的思考,浅谈前端工程化 4.前端模块化 5.我的前端之路:工具化 ...

  3. 前端“智能”静态资源管理 - Onebox - 博客园

    前端"智能"静态资源管理 模块化/组件化开发,仅仅描述了一种开发理念,也可以认为是一种开发规范,倘若你认可这规范,对它的分治策略产生了共鸣,那我们就可以继续聊聊它的具体实现了. 很 ...

  4. 前端HTML5+CSS3静态页面开发-京东首页

    前端html5+css3静态页面开发-京东首页 项目介绍 (1)京东首页项目由html + css 布局完成页面,对前端基础知识的入门及掌握有非常好的提升效果,本项目旨在教授HTML和CSS的基础知识 ...

  5. 京东java前后端联调_前端工程化、组件化实践JDM分享

    前端技术原创文 前端工程化.组件化实践JDM分享 该文由孵化创新一部曾瑞文在研究院技术分享会的分享内容总结而成,主要讲解了团队创新的前端工程化.组件化的思想及实践应用. 为什么要搞前端框架? Java ...

  6. 前端工程化系列好文摘要

    前端工程化遇到的好文在这里简要摘记一下核心要点和主要概念,这里核心是指针对本人而言不代表原文核心阐述,方便快速查阅,详细解释还是要看原文 对于前端工程化我自己还没形成知识体系,先简单罗列知识点,如果哪 ...

  7. 前端架构,前端工程化

    前端架构: 1.前端工程化 web应用复杂度的增加,特别是单页面应用的风靡.组件化,工程化,自动化成了前端发展的趋势.或者说一线的互联网公司就是这么做的. 每个前端团队都在打造自己的前端开发体系,这通 ...

  8. 014-Axios Ajax:前后端分离概述,发送json类型的参数,前后端分离开发:在线接口文档,前端工程化、Element、nginx

    第一节 Ajax概述 1.概述 概念: Asynchronous JavaScript And XML,异步的JavaScript和XML. 作用: 数据交换:通过Ajax可以给服务器发送请求,并获取 ...

  9. 转载--web前端工程化

    作者:赵雨森 链接:https://www.zhihu.com/question/24558375/answer/139920107 来源:知乎 ## 模块化 简单来说,模块化就是将一个大文件拆分成相 ...

最新文章

  1. Python使用matplotlib可视化自定义背景色实战:自定义可视化图像的背景色(Background Color)
  2. c语言二十四点游戏,C语言解24点游戏程序
  3. GIT常用的基础命令
  4. Maven_1.了解Maven以及其安装配置
  5. [css] 说说你对css盒子模型的理解
  6. 中移M5310A NBIoT模组通信测试命令
  7. 解决 springboot 启动报错 -- Cannot determine embedded database driver class for database type NONE
  8. python实现大批量pdf格式论文的重命名与目录制作功能
  9. lua绑定C++对象系列五——lunar模板进阶
  10. 电脑重装系统U盘引导不了
  11. 中国移动MM7API开发问题
  12. Hash 表的时间复杂度为什么是 O(1)(面试版)
  13. QT widget宽高比
  14. 北京世园会率先启用5G技术 中国馆优雅呈现
  15. 360一键wifi共享 v5.3 绿色免费版​
  16. 国民技术计划1.4亿入股华夏芯
  17. 网站PV、UV的含义
  18. 软件架构师需要什么能力?
  19. 嵌入式计算机是专用计算机系统,嵌入式系统是一种专用的计算机系统.doc
  20. Java 分配员工部门源代码

热门文章

  1. centos下的AVAST安装和使用
  2. Python 里最强的Web框架,早就不是Django和Flask了
  3. Could not locate aapt. Please ensure you have the Android buildtools installed
  4. 国产数据库清单;微盟《生产环境和数据恢复》;TiDB招聘;Oracle备份还原指南、GaussDB性能调优指南……墨天轮数据库周刊-第5期
  5. Android 获取系统版本号,设备品牌等
  6. 2-HTML多媒体与嵌入
  7. 用C#做的一个简单的图片浏览器
  8. 【机器学习】<刘建平Pinard老师博客学习记录>Scikit-learnPandas(NumpyMatplotlib)学习线性回归
  9. nginx配置fcgi
  10. struts----ActionForm Bean作用