June 18, 2018
多端口多服务配置 # 个人服务器和一些低访问量的服务,使用apache2比较方便。 安装完以后打开默认的页面会看到有个介绍:
/etc/apache2/ |-- apache2.conf | `-- ports.conf |-- mods-enabled | |-- *.load | `-- *.conf |-- conf-enabled | `-- *.conf |-- sites-enabled | `-- *.conf 这就是apache2的基本配置文件
1. apache.conf # 这个文件基本不用动,是apache的总配置文件。我只修改一项:
<Directory /var/www/> Options Indexes FollowSymLinks AllowOverride all # default is None, change to all. Require all granted </Directory> 2. ports.conf # 这个文件用来添加监听的端口:
# If you just change the port or add more ports here, you will likely also # have to change the VirtualHost statement in # /etc/apache2/sites-enabled/000-default.
...
November 21, 2017
November 20, 2017
作为CSS的一种扩展语言,使得CSS开发更便捷。
变量 # less中的变量使用@开头。
// LESS @color: #4D926F; #header { color: @color; } h2 { color: @color; } /* 生成的 CSS */ #header { color: #4D926F; } h2 { color: #4D926F; } 甚至可以用变量名定义为变量:
@fnord: "I am fnord."; @var: 'fnord'; content: @@var; --> content: "I am fnord."; 请注意 LESS 中的变量为完全的 ‘常量’ ,所以只能定义一次.
字符串嵌入 # 变量可以用类似ruby和php的方式嵌入到字符串中,像@{name}这样的结构:
@base-url: "http://assets.fnord.com"; background-image: url("@{base-url}/images/bg.png");
作用域 # LESS 中的作用域跟其他编程语言非常类似,首先会从本地查找变量或者混合模块,如果没找到的话会去父级作用域中查找,直到找到为止.
引入文件 # 你可以在main文件中通过下面的形势引入 .less 文件, .less 后缀可带可不带:
@import "lib.
...
November 19, 2017
react是一个前端库,着力在创造新的前端渲染的工作模式。 react单个可以做为开发库来单独使用,但是一般我们会将其与其他的相关库一起使用,在使用的时候,基本的react概念是不变的,只是在某些环节发生一些变化。
react 组件的生命周期 # 对于一个react的组件,其生命周期决定了其思维方式。 一个组件什么时候产生实例,在什么时候调用什么函数,决定了这个组件在什么时候做什么动作。
react严格的定义了组件的生命周期,分别是装载过程,更新过程,卸载过程。我们重点是要清楚,在装载和更新的过程中,react组件都要做哪些动作。
装载过程 # 当组件第一次被装载的时候,一次被调用的分别是:
constructor componentWillMount render componentDidMount 这里我们使用的是ES6语法的方式来创建组建,所以有一些老的函数就没有被用到,也就不用再提了。
contructor # 并不是每个组件都需要构造函数,当有构造函数的时候,一定要先执行父类的构造函数。
class Sample extends React.Component{ contructor(props){ super(props); this.state = {foo: 'bar'}; this.onClickSubmitButton = this.onClickSubmitButton.bind(this); // this.onClickSubmitButton = ::this.onClickSubmitButton; } } 在构造函数中,我们要:
给内部数据初始化,也就是赋值state。 将函数绑定this,否则内部使用的数据会是错的,绑定以后使用的数据就是本组件中的数据。 componentWillMount & componentDidMount # 这是一组函数,分别在render之前和之后调用。 WillComponent这个函数其实有点多余,因为所有在这里做的事情我们都可以在构造函数中做,因此基本不会用到这个函数。 DidMount 就是在render函数过后调用,此时页面已经渲染出来。 在这个函数的执行时间上,还是比较有文章的。
举个例子,组件A有三个子组件B,B都有实现这个函数,在A的render函数中,会执行B的render函数,但是当一个B的render函数执行完后,并不立即执行这个B的DidMount函数,而是等所有的B的render函数被调用完毕后才一起调用。当然也是按顺序的。 由于此时已经渲染出来,所以我们也就可以获得DOM树上的节点。因此我们可以在这时候请求服务器去填充数据。这在我们使用其他的一些前端库的时候比较方便。例如在使用jQuery的时候,jQuery只能对已经存在的元素进行操作,所以此时正是调用jquery的时候。
render # 但凡React组件都要实现这个函数,因为这个函数在Component中没有默认的实现。
render函数并不直接操作渲染,而是返回一个JSX语法的描述。最终的渲染动作由react来做。如过没有什么要渲染就可以返回一个null或者false。
render应该是一个纯函数,只接受state和props并产生返回值,没有其他任何的副作用。
更新过程 # 要实现交互,就需要更新:
componentWillReceiveProps shouldComponentUpdate componentWillUpdate render componentDidUpdate 在父组件发生Update调用render函数的时候,子组件就会经历更新过程。
componentWillReceiveProps # 这个函数在使用this.setState更改数据的时候不会被调用。因为这个函数根据新props的值来计算是不是要更新内部状态state。更新内部状态使用setState,因此不会产生循环调用。 这个函数接受一个参数, 新传进来的nextProps。
...
November 18, 2017
在使用react做大型开发的时候,我们习惯使用redux来做数据管控。但是redux实在是太过繁琐,流程很长,不利于快速开发。 mobx是redux作者非常推荐的一个替代产品。
基本概念 # 与redux的长流程,严管控不同,mobx采用一种更直接的方式,和更自动化的方式管理应用的数据。
状态驱动页面更新 # 应用的state也就是其中的数据,就是应用此时所在的状态,状态的改变驱动页面的改变,这是共识,问题就是怎么设计使状态的改变驱动页面更新。
在mobx中,数据将被监视,当数据发生变化的时候,mobx会自动的知道哪些部分需要被刷新,而不需要程序员来指明更新什么。
应用的状态分成两种, 一种是数据本身组成的基本状态。还有一种是在数据基础上计算得出的衍生状态。在下面我们会讨论这些。
observable # 对于需要被监视的数据,就将他用注解的方式注明,需要被监视。
import { observable, computed } from "mobx"; class OrderLine { @observable price = 0; @observable amount = 1; @computed get total() { return this.price * this.amount; } } 就这样简单的就将一个变量纳入mobx的观察体系中。 被观察的可以是几乎所有的javascript的数据结构,但是,对于对象,建议一律转成map来观察,因为对象只会观察此时有的字段,对于未来新添加的字段需要手动加入观察, 但是map就可以将添加进来的新key一并纳入观察。
observer # 有了被观察的,就要有观察者, 这里的观察者会是前端的组件。通常来说,每个组件都应该是可以观察自身数据的,响应式的应用。 一个观察者,会在数据发生变化的时候自动更新自己的视图。
import {observer} from 'mobx-react'; @observer class TimerView extends React.Component { render() { return (<button onClick={this.onReset.bind(this)}> Seconds passed: {this.props.appState.timer} </button>); } onReset () { this.
...
November 15, 2017
如果想用react开发功能丰富,易于代码管理,易于扩展维护的项目,我们最好使用react的一整个体系。当然你单独使用react的部分也是可以的,写一些简单的应用也非常方便,没有必要把这个体系用上。
这篇文章作为入门的一篇略深一些的介绍,在全面学习之前我们至少要知道我们学的每个部分是做什么,为什么要在这里用这个。所以本文不讲一些代码的细节,而是从整体上把握这个技术栈,帮助你快速理解整个体系。
总结 # 一个比较好的实践是使用: react + redux + router, 来实现复杂的应用。 其中, react 承担了前端渲染控制的功能,redux承担了数据流转的控制功能,router则实现了路由控制的功能。
这三项,基本上可以保证我们做出的项目,结构清晰,易于维护。 在初学这写东西的时候,可能会觉得比较痛苦,因为你需要记住太多的东西和条条框框来开发,这好像限制了你的自由发挥,但是对于一个大型应用来说,或者即便是小应用,没有清晰的结构,就连你自己可能都看不懂自己的代码,后期维护的时候就基本上没有多少好体验了。
真正优秀的作品,都是在严格的限制下完成的,因为人的思维不应该去思考那些复杂的页面组成的逻辑关系,而是在实现自己的业务逻辑和计算算法上,那些结构,按照一个科学的方式来做就好。而react正是提供了这样一个科学的流程和方式。
react # 总的来说,在前端渲染方面,react非常出众, 一般我们将前端渲染的部分交给react来做。 react使用虚拟DOM的方式,来管理渲染。并且将一个页面分成多个组件进行管理,我们一般将组件按功能分开。当页面发生变化的时候,由react来决定哪里需要渲染,哪里不动,react来操纵浏览器的渲染。 与jQuery相比,更进一步, jQuery是将浏览器的一些操作封装出来,提供一组新的调用函数。但是react已经将浏览器的相关接口完全封装,并且屏蔽在react内部,提供了一种更自然和智能的方式来满足我们的需求。我们直接和react打交道,剩余的事情由react去和浏览器做。
react这个框架可以运行在浏览器端,也可以运行在服务器端, 我们称之为Universal渲染,这里的服务器端,就是Nodejs的平台了。我用的基本上都是浏览器端运行。只要将react的脚本发送到浏览器进行执行就可以了。 初的渲染是后端的事情,有了ajax后前端也可以进行渲染了。但是前端渲染会有个问题,就是搜索引擎不友好,因为搜索引擎并不会去跟js代码交互,只会爬取静态页面,所以使用后端渲染十分重要。这关乎你的网站被检索到的可能性。
基本构造 # react的逻辑很简单,一切都是组件。一个页面是由多个组件堆砌而成,每个组件之间互相隔离,仅通过数据交互,而数据交互的接口是固定的。所以对于一个组件内部来说,与外界是完全隔离的。 一个组件包括,样式和数据。数据我们通过请求服务器去获得。 每个组件通过提供一个render函数来返回这个组件,这个render函数只能返回一个元素。如果有多个元素需要被包起来成为一个元素返回。 组件之间可以是嵌套关系,这个和类的概念有点像,在一个类中可以包含另一个类作为自己的成员。也就是方便我们做组件封装,功能划分。
当我们写完一个react应用后,可以通过npm start的方式启动起来,当我们在浏览器访问对应的地址时,默认访问index.js, 所以我们的应用最终在这里组织起来。
ES6 & Javascript & JSX # 在写应用的时候,我们使用JSX 语法来写,JSX以ES6为基础,react提供了JSX解释器。在写完以后,JSX语法的代码会被编译成JS代码,最终在浏览器中运行的是Javascript的代码,关于webpack等工具,我暂时没有去学习,因为react提供了一个创建react应用的脚本,能够一键创建好这些配置文件,所以我就暂时直接使用了。
npm install --global create-react-app
这个命令就可以安装这个脚本,然后我们执行: create-react-app myapp, 就可以创建一个文件夹, 里面已经配置了一个简单的react应用。 然后我们进入这个文件夹,使用npm start就可以启动服务。 start的过程中,代码就会被编译,并打包成webpack中配置的打包形式。
如果此时我们改变代码,这个页面会动态更新。
react 原生的数据控制 # 在react中,数据主要有三个载体,分别是state、prop、context。其中state是组件内部状态数据,prop用于层层传递,context可以跨层传递。
react要实现各个组件独立,并且具有统一的结构,最重要的就是控制数据。在JS这种弱类型的语言中,这一点很好实现,重点就是设计一个接口形式。 react中的组件,都有两个变量,一个是prop, 一个是state。 其中,state是描述组件内部数据的,prop是对外的接口。也就是说,别人传过来的数据,会存在prop中。同时,prop是不可变的,组件自己不能更改prop的值, 只能改变state的值。
这种数据控制的方式,比较原始,从接口统一的角度来看挺好的,也符合基本的思维方式。 但是这种控制方式也存在很多问题,不用去深究这个框架的细节,我们也可以提出很多问题:
每个组件之间的数据冗余问题,很多数据统计的模块,该怎么存储数据? 复制一份出来?当要更改数据的时候,是不是还要传参给另一个组件通知他改变数据?
...
October 8, 2017
历史 # ECMA是个组织,国际标准化组织。 Javascript诞生于1996年,比我略小9个月。由Netscape公司研发,也就是曾经的网景公司。 次年开源交付给ECMA组织进行标准化,力图打造一个国际通用的语言。随后,ECMA发布了国际浏览器脚本语言标准,名称为ECMAScript,实际上就是Javascript的国际标准版。 由于商标等多种原因,名称为ECMAscript,而不是javascript,但我们基本上认为是一个东西。
前者是标准,后者是标准下的实现。
标准是个啥 # 所谓的语言标准,每个语言都有。 在语言不断发展的过程中,会给语言添加一些语法特性,比如以前js是不支持class的,在ES6的标准中就支持啦。
这些标准每年都会变化,不断的有人提案新的语法特性,经由审核实现后发布。
ES6的发布有什么好处? # 让js真正成为一个可以开发大型应用的语言。 不面向对象的程序设计,在开发逻辑复杂的大型企业级应用的时候非常鸡肋,超高的开发周期和人力投入,复杂的耦合关系,而且极难进行维护。 ES6引入了很多的面向对象的语法,以及函数编程的内容,使得js可以hold住大型开发的复杂度。
但是ES6的class在ES5的时候就是可以实现的,只是语法上很别扭,跟传统的面向对象的语法差异很大,让人学起来很懵逼。
语法细节 # 在细节的内容,推荐一个开源的书,所谓开源的书,就是有电子版的网站可以看,印刷版的要买。
详细参考
August 21, 2017
有一种痛,叫做就是想让你多学习一点东西。
这个知识点,来源于一口还没找到源头的大黑锅。而我不能背这个锅,并且要找到一个人来背锅。
Introduction # 在之前的sso文章中,有提到过一个问题,就是cookie的不能跨域的问题。 出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求。例如,XMLHttpRequest 和 Fetch 遵循同源策略。因此,使用 XMLHttpRequest或 Fetch 的Web应用程序只能将HTTP请求发送到其自己的域。为了改进Web应用程序,开发人员要求浏览器厂商允许跨域请求。
针对跨域的解决方案有很多,比如:flush、JSONP、ifame、xhr2等,但是都有很多弊端,我觉得CORS比较有前途。
这个解决方案已经被几乎所有的主流的浏览器支持了。浏览器内置了这种解决方案,所以对于前端的工程师来说就是透明的,当前端使用ajax发起一个跨域请求的时候,浏览器自动使用这个方案来处理。但是这个方案需要浏览器和服务器共同支持才可以。
今天主要的使用是依靠XMLHttpRequest来实现。这是一个js的对象,负责与服务端进行动态数据交互。
发起一个XMLHttpRequest请求 # 原生的请求形式。
<script type="text/javascript"> //XmlHttpRequest对象 function createXmlHttpRequest(){ if(window.ActiveXObject){ //如果是IE浏览器 return new ActiveXObject("Microsoft.XMLHTTP"); }else if(window.XMLHttpRequest){ //非IE浏览器 return new XMLHttpRequest(); } } // 调用getFile方法 function getFile() { var img_Container = document.getElementById("img_Div"); var xhr = createXmlHttpRequest(); xhr.open('GET', 'http://oss.youkouyang.com/1.jpg', true); xhr.setRequestHeader('Content-Type', 'image/jpeg'); xhr.responseType = "blob"; xhr.onload = function() { if (this.status == 200) { var blob = this.response; var img = document.
...
August 16, 2017
欲仙又欲死。阿里巴巴真是一朵大奇葩。 一个写死的回调地址就能浪费我两天的时间去搞,还没有搞定。此坑绵绵无绝期。 一个sso的回调地址,写死的,要强行将应用部署到ROOT里中去。
在tomcat中有一个配置文件经常被用到,conf/server.xml。这个配置文件描述了如何启动tomcat server。 tomcat是servlet的容器,也就是能够运行servlet的一个程序。作为一个http服务的程序,可以监听端口,处理请求等。
配置文件结构 # <Server> <Listener /> <GlobaNamingResources> </GlobaNamingResources <Service> <Connector /> <Engine> <Logger /> <Realm /> <host> <Logger /> <Context /> </host> </Engine> </Service> </Server> 对我们来说,内层的Service才是有操作意义的部分。主要的部分简介如下:
元素 说明 service 提供服务的主体,默认的名字是Catalina Connector 客户端与服务端之间的连接,包括端口,协议版本,超时时间,等。 Engine 请求的处理机,接收和处理Connector的请求, 与host的联系比较大 Context 表示一个应用 host 表示一个虚拟主机 Tomcat Server处理一个http请求的过程 # 假设来自客户的请求为:http://localhost:8080/wsota/wsota_index.jsp
在这里有一幅图我觉得非常清晰的说明了这个过程,但是其本身跟这个毫无关系。
请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得 Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应 Engine获得请求localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机) localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为"“的Context去处理) path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet Context匹配到URL PATTERN为.jsp的servlet,对应于JspServlet类 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法 Context把执行完了之后的HttpServletResponse对象返回给Host Host把HttpServletResponse对象返回给Engine Engine把HttpServletResponse对象返回给Connector Connector把HttpServletResponse对象返回给客户browser Context # 来看最重要的Context, 一般我们为每个应用做的定制化的东西都是依靠这个来做的,就比如我们要把war包部署成一个默认的应用。
...
August 14, 2017
由于Http协议本身是没有状态的,每个请求都要建立新的连接。Cookie就是为了弥补这方面的不足而设计的,主要用来跟踪会话。
会话 # 跟同一个机器中的同一个应用在一段时间内的通信交互,我们可以认为是一个会话(Session)。 会话与会话之间需要被隔离开。
形象的理解 # Cookie就像是你的一张证明,并且,你跟不同的对方打交道用的是不同的证明。你会把这个证明带在身上,你可以在证明上写东西,对方也可以在上面写东西。在有需要的时候,双方都可以打开这个证明来看看里面的内容。 其本质其实就是一个键值对形式的文本信息。
在控制台中,输入命令:alert(document.cookie) 就可以看到你与当前页面应用之间保存的cookie内容。 手机上的浏览器一般不支持cookie。
跨域 # cookie是以域名作为单位的,同一个域名共享一个cookie。不同域名之间不能操作对方的cookie。 这里就是sso主要要解决的难点。
跨域设置Cookie有几种实现的方式,其中最方便的就是使用jsonp的方式来实现。
jsonp 跨域 # 在JQuery中使用ajax可以处理同一域名下的重定向请求。但是其处理不了跨域名的重定向。
也就是在同一个域名下进行重定向,ajax使得重定向对用户来说是透明的。但如果是跨域的,ajax就会报错。