> 你的代码彼此耦合性降低,利于将来某个时候扩展或删减;
32 |
33 | >> 良好的代码结构,集合我后面将要讲到的注释,你将很容易维护你的代码,别人也能方便对你的代码有个清晰认识;
34 |
35 | >> 便于你腾出时间专注于某个点工作,我想这肯定会提升你的工作质量的。
36 |
37 |
38 | ## 必须使用注释
39 |
40 | * 首先为什么要强调必须呢!因为曾经自己有一段时间一直在维护别人前期已经搭建好的项目结构,所以那个时候才能理解为什么注释这么重要,首先来一张我曾经修改别人html代码结构模板图;
41 |
42 | 
43 |
44 | 我们可以发现完全没有注释,我们要找到bug的地方不费点力气,我想是很难找到的,所以良好的注释在团队合作中真的很重要。
45 |
46 | * 当然我第一次喜欢上注释也并非是为了团队合作,因为之前前端我一个人在做,所以谈合作有些牵强,也不是没合作,但大部分时间自己在维护自己写的代码。所以我第一次迷恋上写注释是因为自己强迫症,总觉得那样写出来具有良好的代码结构,到了后面,发现确实注释能给工作带来很多便利之处。至少发现bug到修复bug完成不会像之前时间花费的多了,而且很容易找到出现问题的地方。
47 |
48 | * 下面我说一下,在工作开发过程中我怎样采用注释的。需要提前说明一下的是,在团队开发过程中,注释是很好的便于团队合作,所以未必我下面说的注释就是最好的,你当前使用的注释结构也不一定是差的,总的看团队合作模式,大家已经有了稳定的约定俗成的注释模式,那么就保持这种风格就好!
49 |
50 | ### html注释
51 |
52 | * 因为本身html是标记性语言,有开始标签和结束标签,那么我也采用类似的注释结构,将每个块开始处和结束处都加上一个标记,以表明这部分代码表现是页面那部分内容。如下:
53 |
54 | ```html
55 |
56 |
57 | contents go here.
58 | .
59 | .
60 | .
61 |
62 |
63 | ```
64 |
65 | 我觉得写注释跟前面按“块”的方式构建你的dom相得益彰,有异曲同工之妙,都能很好的表述你的代码结构,好的注释解释更能很好的说明这部分html实现的内容!
66 |
67 | ### css注释
68 |
69 | * 对于css代码注释,我的理解就是你需要说明你的样式用于那部分内容,或者实现什么组件样式。所以采用两种方式来实现对css代码进行注释。如下:
70 |
71 | ```css
72 | /*第一种注释 用于表示实现了页面那部分html代码的样式*/
73 | /*
74 | ***this css is used in the user survey
75 | */
76 |
77 | .class1{
78 | ....
79 | }
80 | .class2{
81 | ....
82 | }
83 | .
84 | .
85 | .
86 |
87 | /*第二种注释 用于实现什么组件的样式*/
88 | /*
89 | ***this style is for a button
90 | */
91 | .btn{
92 | padding:0.3em 1.2em;
93 | -webkit-borde-radius:3px;
94 | background-color:#c00;
95 | color:#fff;
96 | outline:none;
97 | border:0px;
98 | font:inherit;
99 | }
100 |
101 |
102 | ```
103 |
104 | 当然了,现在提供了sass和less等css预编译语言,它们的注释我也是采用类似注释结构。这种注释结构能很清楚css代码用于那部分内容和实现了什么样式。
105 |
106 | ### javascript注释
107 |
108 | * 我觉得js注释还是需要讲究的,因为曾经写过一种类型的样式,但是后面觉得不适合,看一下我最开始的写的js注释代码:
109 |
110 | ```javascript
111 |
112 | (function(){
113 | //a表示当前是否当前正在获取服务器数据
114 | //b表示服务器数据已经拉取完成没有数据可以获取
115 | //num用来初始一个获取数据计时器
116 | var a = false,b = true,num = 1;
117 |
118 | function getData(){
119 | //代码省略
120 | };
121 | })();
122 | ```
123 |
124 | 首先说一下这种代码注释的注重点,实现给每个变量的用途添加说明,其实这是完全没有必要的,因为如果这样注释的话,我想你的代码中肯定会有很多这样的冗余注释,其实我们后期维护代码的过程中并不是特别需要了解每个变量的意义,而是需要快速定位出现问题的代码所在的地方。至于变量所代表的的含义,即时你告诉了维护的同事,他也需要重新看一下你写的代码才能知道问题所在,而他在看代码的过程中,他就已经知道你的变量含义了,所以对变量含义进行注释是没有必要的!
125 |
126 | * 所以我对于js采用类似如下的注释结构,可以借鉴一下:
127 |
128 | ```javascript
129 |
130 | /**
131 | ** 这部分脚本用于实现向服务器拉取banner图数据
132 | **/
133 |
134 | (function getBannerData(){
135 | //代码逻辑省略
136 | })();
137 |
138 |
139 | /**
140 | ** 这部分代码用于实现用户登陆逻辑
141 | **/
142 |
143 | (function userLogin(){
144 | //代码逻辑省略
145 | })();
146 |
147 |
148 | //等等都这样注释下去
149 |
150 | ```
151 |
152 | 我发现上面的js注释方式能够很快帮你定位到出现问题的代码,并且很好的管理的代码结构,所以我一直在用!
153 |
154 |
155 | ### 内容持续修改中....
156 |
157 |
--------------------------------------------------------------------------------
/07/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 这边文档希望通过自己对jsonp的理解,能够采用最简单、最直白的语言告诉大家jsonp是干嘛的!当然,其间难免有纰漏之处,希指正出来!
4 |
5 | ### jsonp能解决的问题
6 |
7 | * 首先我们需要jsonp是干嘛的,它是一种解决浏览器跨域问题的方案,说道这里什么是浏览器跨域呢!说白了,就是浏览器内部有一种机制为了保证每个站点之间的请求达到安全、独立,相互交互不乱套等,浏览器阻止了不同源站点之间的请求。如果不采用跨域的话,浏览器将会报错的。我们先来看个简单例子吧:[实例代码1](https://github.com/woai30231/webDevDetails/blob/master/7/demo1.html)
8 |
9 | ```html
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | demo
21 |
22 |
23 |
24 |
41 |
42 |
43 |
44 | ```
45 |
46 | 我们通过上面的页面在请求资源时可以看到下面请求跨域错误提示:
47 |
48 | 
49 |
50 |
51 | ### 怎样理解跨域,以及它跟浏览器的关系呢!
52 |
53 | * 首先我们不想把概念说的神乎其神,但是大家要理解下面说的话,可以先看下《http报文权威指南》了解下相关概念!这里指出一下,如果只是从http请求的方面来说,上面的页面请求是没有什么问题的,而且也不会报错,因为http本身是无状态的,它也不知道请求的是谁,被请求的是谁,它只知道客户端有请求,服务器把客户端需要的东西返回就好了!**上面的页面之所以会报错,是由于浏览器自己带有了一种叫做同源策略的安全机制产生的!**
54 |
55 | * 什么是同源策略,相关概念可以自行google或百度,说白了就是一种机制促使浏览器限制不同源之间的请求。这里说一下什么样的请求是不同源的,看下面的表格:
56 |
57 |
58 |
59 |
60 |
61 | http://www.demo.com
62 | https://www.demo.com
63 | 这是不同源的,因为协议不同
64 |
65 |
66 | http://www.demo1.com
67 | http://www.demo2.com
68 | 这是不同源的,因为主机不同
69 |
70 |
71 | http://www.demo.com:80
72 | http://www.demo.com:8080
73 | 这是不同源的,因为端口号不同
74 |
75 |
76 |
77 |
78 | ### 怎么解决跨域请求问题呢!
79 |
80 | * 常见的和自己熟悉有几种方案,如:1、用cros;2、用代理;3、使用jsonp。这里只对jsonp介绍,其他的方案可以自行查看相关文档。
81 |
82 | ### jsonp的原理
83 |
84 | * jsonp原理其实很简单,首先我们先回归一个现实:我们发现,当我们在html插入img、a、script标签的时候,它们是没有同源限制的。所以jsonp的原理就是利用img、script等标签插入一个请求地址,让不同源的请求远离浏览器的同源策略限制。
85 |
86 | ### 实(示)例
87 |
88 | * 我们来先看一个例子吧: [前台代码](https://github.com/woai30231/webDevDetails/blob/master/7/demo2.html)
89 |
90 | ```html
91 |
92 |
93 |
94 |
95 |
96 | demo
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | ```
105 |
106 | 后台demo1.php代码如下:
107 |
108 | ```php
109 |
112 | ```
113 |
114 | 控制台打印结果截图如下:
115 |
116 | 
117 |
118 | ### 怎么实现精确回调呢!
119 |
120 | * 有时候我们并不是只是见到的得到后台数据就好了,还要到前台进行一些处理,那怎么做呢!恩,没错就是使用回调,首先说一下思路:1、首先在页面中定义好回调函数;2、然后在页面通过插入相关标签待query参数的形式实现jsonp请求传递回调函数名字;3、后台得到回调函数名字,并将需要处理的数据传递给回调函数,最后向前台返回回调函数的“调用”,最后一步切记是传回回调函数的调用。示例如下:
121 |
122 | [前台代码](https://github.com/woai30231/webDevDetails/blob/master/7/demo3.html)
123 |
124 | ```html
125 |
126 |
127 |
128 |
129 |
130 | demo
131 |
132 |
133 |
149 |
150 |
151 |
152 | ```
153 |
154 | 后台demo2.php代码如下:
155 |
156 | ```php
157 |
168 | ```
169 |
170 | 控制台结果截图如下:
171 |
172 | 
173 |
174 | 只要弄懂这三步你就会对jsonp心领神会了!
175 |
176 | ### jsonp跟ajax的区别、jsonp和json的区别
177 |
178 | * 首先严格来说,jsonp和ajax是没有关系,唯一的相同点可能就是都是向后台发请求!jsonp和json也是没有关系的,虽然看上去名字很像,但是jsonp描述的是一种请求数据的跨域方式,而json是一种数据格式!
179 |
180 | ### 时间仓促,写得不对的地方,后期会持续修改......
--------------------------------------------------------------------------------
/08/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 请记住,这篇文档并不是实际搭建一个网页项目,而是告诉我们在**纯手工、不用相关生成器的情况下**怎么从零开始搭建一个工程化的网页项目,所以主要要告诉的就是我们在这一过程中思路是怎么样的!通过这篇文档,能让大家了解相关框架、工具的使用原理以及以及如何构建自动化项目。**_诚然,前端技术日新月异,各种框架满天飞,所以自己在某些方面也感觉到不足,所以还请看到该文档的前辈能够不吝不吝赐教,请移步_**[这里](https://github.com/woai30231/webDevDetails/issues)
4 |
5 | * 假定现实情况:假设有个网页需求,有一个主页index.html,有三个子页contact.html、introduct.html、product.html,这三个子页分别从index.html连接过来,又可以从三个子页返回index.html。我们采用spa的方式构建单页面app,采用grunt管理前台代码程序,采用bower管理各种第三方依赖库。最终项目在[这里](https://github.com/woai30231/webDevDetails/tree/master/8/app)
6 |
7 | * **注意:本文档已经默认你有js、css、html基础,并且知道了解angularjs框架!**
8 |
9 | * 最后还想告诉大家一种**“大繁至简”**的意思,其实很多很高深的道理,回归到简单层面上来其实就是简单的叠加,最后变成了复杂!
10 |
11 | ### 开始着手完成项目
12 |
13 | * 我们每做一个事情,都会在心里有个默认次序,先做什么,再做什么,最后做什么,依次类推!我们搭建这个项目也是一样,我们需要先确定每一步该做什么,以此来理清思绪!
14 |
15 | #### 第一步:确定项目结构
16 |
17 | * 如何熟悉angular搭建项目的目录结构的话,我们就采用下面的目录结构来组织我们的app
18 |
19 | > app
20 | >> contact
21 |
22 | >>> contact.css
23 |
24 | >>> contact.js
25 |
26 | >>> contact.controller.js
27 |
28 | >>> contact.html
29 |
30 | >> introduct
31 |
32 | >>> introduct.css
33 |
34 | >>> introduct.js
35 |
36 | >>> introduct.controller.js
37 |
38 | >>> introduct.html
39 |
40 | >> product
41 |
42 | >>> product.js
43 |
44 | >>> product.css
45 |
46 | >>> product.controller.js
47 |
48 | >>> product.html
49 |
50 | >> index.html
51 |
52 | >> app.js
53 |
54 | >> app.css
55 |
56 | 采用这样的结构方便我们把每一个路由当作一个模块,从而方便管理每个页面下的css、js等!
57 |
58 | #### 第二步:先用几个静态页面写出来
59 |
60 | * 这一步你不要使用js等,只需要用html、css勾勒出静态页面!下面是效果截图,html、css代码请查略[源项目](https://github.com/woai30231/webDevDetails/tree/master/8/app)
61 |
62 | 预览图截图如下:
63 |
64 | 
65 |
66 | 
67 |
68 | 
69 |
70 | 
71 |
72 | #### 第三步 安装相关管理工具
73 |
74 | * 在这里我们需要用到grunt管理代码,用bower管理第三方依赖,所以我们需要安装这些工具,首先这些工具是基于nodejs平台的,所以在这之前你需要在你的电脑里安装nodejs,怎么安装nodejs,可以自行google,或者百度。
75 |
76 | * node安装之后,用npm(node自带)在全局安装grunt-cli和bower,命令如下:
77 |
78 | ```bash
79 | npm install -g grunt-cli bower
80 | ```
81 | 安装完成之后,我们需要在项目根部初始化一个package.json文件,这个文件的作用就是告诉其它使用你这个项目的人你这个的项目是什么情况,比如作者、版本、依赖什么的,采用如下命令,然后bash询问式一步一步要求填写你自己的内容:
82 |
83 | ```bash
84 | npm init
85 | ```
86 | 当然了,你也可以采用默认初始化操作来实现封装一个package.json文件,所有内容都是系统默认填写的,命令如下:
87 |
88 | ```bash
89 | npm init -yes
90 | ```
91 |
92 | 我们再来在本地安装grunt并把它添加到包依赖devDependencies中,命令如下:
93 |
94 | ```bash
95 | npm install grunt --save-dev
96 | ```
97 |
98 | 至此,我的package将会是如下内容:
99 |
100 | ```json
101 | {
102 | "name": "demoapp",
103 | "version": "1.0.0",
104 | "description": "this is a demo for how to get up a SPA site",
105 | "main": "index.js",
106 | "scripts": {
107 | "test": "echo \"Error: no test specified\" && exit 1"
108 | },
109 | "keywords": [
110 | "''\u001b[Dtest"
111 | ],
112 | "author": "lijianfeng",
113 | "license": "ISC",
114 | "devDependencies": {
115 | "grunt": "^1.0.1"
116 | }
117 | }
118 | ```
119 |
120 | #### 第四步,用bower安装第三方依赖库
121 |
122 | * 在本文档中,我们需要的第三方库有angular.js以及angular-ui-router.js,同样,我们需要初始话一个bower.json文件,这个文件的作用就是告诉bower它自己管理了那些第三方库,命令如下:
123 |
124 | ```bash
125 | bower init
126 | ```
127 |
128 | 通过填写相关信息,bower.json如下:
129 |
130 | ```json
131 | {
132 | "name": "demoapp",
133 | "description": "this is a demo for how to get up a SPA site",
134 | "main": "index.js",
135 | "authors": [
136 | "lijianfeng"
137 | ],
138 | "license": "ISC",
139 | "keywords": [
140 | "test"
141 | ],
142 | "homepage": "https://github.com/woai30231/webDevDetails"
143 | }
144 | ```
145 |
146 | 同时,我们还可以通过配置一个.bowerrc文件来告诉bower安装的库放在哪里,.bowerrc内容如下:
147 |
148 | ```json
149 | {
150 | "directory":"./bower_js"
151 | }
152 | ```
153 |
154 | ok,到这里我就可以开始用bower安装库了,并且会安装到指定目录下,相关的bower API可以查看官方文档,那我们首先安装angular-ui-router,通过安装angular-ui-router,bower会自动安装angular.js,因为angular.js是angular-ui-router.js的依赖。命令如下:
155 |
156 | ```
157 | bower install angular-ui-router#0.2.15 --save-dev
158 | ```
159 |
160 | ok,我们看到我们的项目结构中多了一层目录如:
161 |
162 |
163 | 
164 |
165 | 当然了,在我们用git做版本控制的过程中,为了减少给远程版本仓库存贮不必要的文件,我们可以配置一个.gitignore文件来配置什么文件需要提交到远程仓库,什么文件不需要提交的远程仓库。,比如这里我不希望node_modules和bower_js下的文件提交到远程仓库,那么就可以这样配置.gitignore,当然了实际开发过程中,你根据实际需要自行配置:
166 |
167 | ```
168 | node_modules
169 | bower_js
170 | ```
171 |
172 | #### 第五步,万事俱备,只欠东风,配置Gruntfile.js来管理前台代码吧
173 |
174 | * 首先说明一下grunt是干嘛的,说白了就是个基于任务的命令配置文件,你写好配置文件,然后运行命令,它会在后台自动跑你要做的那些操作,比如有两个js文件a.js和b.js,希望压缩成一个文件,就可以利用grunt来实现,好了,我们来实现第一个任务吧:我们每次写完一个js文件或者css文件,然后在需要的页面手动引入它们,这个过程是不是很烦呢!其实通过grunt可是实现使用命令自动引入它们,那我们该怎么做呢?我们来看一下grunt官方文档:
175 |
176 | 
177 |
178 | 说白了就是欲练此功,必先配置Gruntfile.js的道理,那怎么配置Gruntfile.js呢!我相信官方文档里面会有的!好,话不多说,我们来实现自动引入css、js文件的任务,首先在根目录下新建一个Gruntfile.js文件,相关代码如下:
179 |
180 | ```javascript
181 |
182 | ```
--------------------------------------------------------------------------------
/09/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 上个星期接到运营部门的提出的一个简单活动页面项目前端需求!需求是这样的:需要统计用户公司某款产品用户的回馈情况,美工给的设计多个psd,每个页面里面都有一个选择题,让用户选择自己的答案,最后经过几次选择之后在最后一个页面统一提交到后台!所以这里引出的技术需求就是:如何在每个页面之间实现数据共享,比如用户进入下个选择页面之后怎么保存用户在上一个页面选择的数据,以便最后统一提交,因为这个项目比较独立,而且也只是简单的几个页面做统计需求,所以我并没有采用angularjs来搭建项目,所以没有用angular里面路由带参数的形式来向下一个页面传递数据,但是我这里使用的方法原理其实是一样的!
4 |
5 | * 首先说一下当时想到的几个解决方法:1、每次用户在上一个页面选择之后,用localStorage/sessionStorage来保存用户的选择;2、使用cookie来保存用户选择,然后在后面的页面获取cookie来实现共享数据;3、使用url来实现传递数据,类似发送get请求的把数据带在url查询参数里;4、不要做成多个页面的形式,而是做成一个大页面,采用javascript控制某些“页面”的显示/隐藏的方式来模拟多页面形式,同时通过闭包实现保存用户提交的数据!
6 |
7 | * 最后我采用了url的形式来实现保存数据,我后面会说一下为什么会选择这种方式,以及会说明其它方法为什么不适合在这里使用!当然了,这里我写这篇文档的目的除了要记录自己在实现这个需求的一些所思所想以外,因为上面的需求都是介于同一个服务器上的数据共享,没有出现跨域相关的问题,所以我还想对跨域时候发生数据共享问题及解决方法作统计记录,所以本文档将分为两个部分:同域实现数据共享和跨域数据共享,同域部分就以上面这个例子为例说明,跨域部分则会自由、发散说明!**最后说一下,因个人经验有欠缺或不到位,所以其中难免有疏漏的地方,所以你有更好的思路,或者我有写错的地方,欢迎来到[这里](https://github.com/woai30231/webDevDetails/issues)给我讲解一下!**
8 |
9 | ## 同域部分实现数据共享
10 |
11 | ### 采用localStorage/sessionStorage来保存数据
12 |
13 | * 首先说一下原理,localStorage/sessionStorage都是一个本地存储数据接口,它们两的用法都差不多的,唯一的区别就是在保存时效上:localStorage是永久保存的本地的,除非你主动清空浏览器本地数据,否则即使你关闭浏览器重启,数据照样保存在哪里,而sessionStorage也是持久保存数据的,但是它在浏览器当前窗口关闭之后再重启的时候保存的数据就会被清空!
14 |
15 | * 对于这两个接口,你只要记住它们主要的两个方法就好了,如下:
16 |
17 | ```javascript
18 | localStorage.setItem('localData','localStorage test data');//设置数据
19 | var localData = localStorage.getItem('localData');//取出数据
20 | sessionStorage.setItem('sessionData','session test data');//设置数据
21 | var sessionData = sessionStorage.getItem('sessionData');//取出数据
22 | ```
23 |
24 | * 此时我们打开chrome浏览器控制台resources选项,相关数据截图如下:
25 |
26 | 
27 |
28 | 
29 |
30 | * 好了说一下,如果使用这两个方法实现数据共享的话,思路就是,每次用户选择完成的时候,用localStorage/sessionStorage保存当前的数据,最后在提交的页面获取全部数据,然后提交到后台!理论上这样做是没有问题的,但是我想了一下在这里不适合用这种方式。因为这个两个接口保存的数据都是永久性保存的,也就是说用户第一次选择好选项之后,然后没有提交,而关闭了页面,但是他第二次直接进入最后提交的那个页面,而此时用户本来是想改它之前选择的选项的,可是这里没有提示,用户就直接提交了!所以这里有点业务逻辑不行!当然了,也可以采用js告诉浏览器,在用户最后关闭浏览器的时候清空相关数据,代码如下:
31 |
32 | ```javascript
33 | localStorage.removeItem('localData');//删除数据
34 | sessionStorage.removeItem('sessionData');//删除数据
35 | ```
36 |
37 | * 个人觉得,localStorage/sessionStorage适合保存那种在较久不需要修改的数据信息,比如用户登陆网站的配置信息等!而不适合保存一次性数据!
38 |
39 | ### 采用cookie保存数据
40 |
41 | * cookie也是一种客户端在本地保存数据的方式,设置方式如下:
42 |
43 | ```javascript
44 | document.cookie = "cookieData='cookie test data'";//设置cookie
45 | document.cookie;//获取所有cookie
46 | (function(){
47 | //获取某一个cookie
48 | function getCookie(cookieName){
49 | var regexp = new RegExp("(;?|^)"+cookieName+"=([^;]*)(;|$)","mi");
50 | var arr = document.cookie.match(regexp);
51 | console.log(arr);
52 | return arr[2];
53 | };
54 | })();
55 | ```
56 |
57 | chrome浏览器控制台截图如下:
58 |
59 | 
60 |
61 | 删除cookie只需要给某个cookie设置一个过期时间就可以了,比如我设置上面的cookieData,只需要像下面这样做一下就可以轻松实现删除cookie:
62 |
63 | ```javascript
64 | var nowTime = new Date().getTime();//获取当前时间
65 | var expirAtionTime = new Date(nowTime-1);//设置过去时间
66 | document.cookie = "cookieData='cookie test data';expires="+expirAtionTime.toGMTString();
67 | ```
68 |
69 | 控制台截图如下:
70 |
71 | 
72 |
73 | 当然了,cookie还可以设置很多其它参数,比如安全域、主机等,总之我的理解就是cookie和localStorage/sessionStorage是差不多的数据存储方式,只是cookie更灵活一点,但设置起来要难一点!
74 |
75 | 虽然我刚开始是想使用cookie来实现上面的需求,但我后来发现cookie除了存在跟localStorage/sessionStorage一样的问题以外,保存的数据在用户退出重进不能很好的更新,还存在兼容性,在移动端有的浏览器存在不支持cookie,(**注:cookie在移动端什么情况,在这里不深究!具体可自行查阅相关资料!**)所以这种方式被终止!
76 |
77 | ### 通过URL方式来保存数据
78 |
79 | * 这种方式就是通过给url带参数和查询的方式来实现传递、共享数据,如要在a页面跳到b页面实现向b页面传送testData可以像下面那样实现:
80 |
81 | ```html
82 |
83 | 链接b
84 |
85 |
86 |
89 | ```
90 |
91 | 我这里就采用了这种方式,因为采用url的方式传输数据不会存在localStorage/sessionStorage、cookie发现的问题,但是这种方式也有它不好的地方,因为url不能传输太大的数据,有字节数限制,当然了我结合这个需求的实际情况,我觉得这里可以采用这种方法。
92 |
93 | ### 采用闭包方式实现保存数据
94 |
95 | * 这种方式的原理就是模拟一个选项卡的点击切换的效果来实现多页面的需要,但实际上是一个页面,同时在全局保存一个变量用来存储将来要提交的数据,这种方式不会存在前面的提到的一些问题,数据是临时的,能很好的即时刷新。但集合实际情况我没有选择在这里使用这种方式来时间保存数据,因为这会使得html、css、javascript耦合太强了,将来如果需求要改的话,很不灵活,所以终止!
96 |
97 |
98 | #### 注:这里不讨论worker方式
99 |
100 | ## 跨域方式实现数据共享
101 |
102 | * 因为这里没有案例参考,那我们自由发挥吧,想到哪里就讲到哪里!其实同域和跨域代码在主要逻辑什么没有什么不同,就是跨域多了一层跨域的处理,所以我们这里说下怎么实现跨域就好,因为其它的逻辑还是原来同域的那个部分!那我们说下怎么解决跨域问题。
103 |
104 | _ 1、使用cors:
105 |
106 | 这种方法的好处就是你根本不用改变你前台的写的代码,你同域的时候怎么写的,现在跨域就怎么写,只是需要在后台服务器端响应的时候配置一下"Access-Control-Allow-Origin"响应头就好,把它的设置成你允许访问的那个服务器就好,就是你想获取数据的那个页面所在的服务器。可以设置__*__,这样所有服务器都可以访问这个资源了。
107 |
108 | _ 2、使用jsonp
109 |
110 | 相关描述可以来[这里](https://github.com/woai30231/webDevDetails/tree/master/7)查看
111 |
112 | _ 3、使用代理服务器
113 |
114 | 理解这种方法的前提是首先需要知道为什么会出现跨域问题?因为浏览器有个同源策略,所以才会出现跨域问题,那么我们把数据放在后台去请求呢!问题就会迎刃而解了,因为服务器端是没有同源策略的!
115 |
116 | _ 4、其它方式
117 |
118 | 还有其他的方式,比如frame、postMessage等,这些我用的比较少,所以在这里不做介绍了!
119 |
120 | #### 时间仓促,写的不对的地方,希望及时提醒我一下,当然了你也可以多看看其它地方,以作对比、权衡从而发现细节问题,后期持续更新……
--------------------------------------------------------------------------------
/17/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | 当前前端发展的很快,相关的框架更是此起彼伏,但纵观局势,也有angular、vue及react三个框架在前端web app搭建方面成三足鼎立之势!它们之间彼此有类似的地方,也有属于自己的不同的实现原理,它们之间没有谁能够一定取代另外两个框架从而成垄断技术,就像web发展到今天,没有永恒的框架一样,新框架出的很快,旧框架都会被新框架淘汰,说不定明天就有一个框架出来,它兼顾了angular1、react、vue三者所具有的好处!只是结合你项目,它们在实现上、性能以及效率方面有不同的着重点而已。
4 |
5 | 由于我自己实话说也算不上所谓的这方面技术大牛(但我会坚持朝这个方向靠拢),所以本篇文档并不会给你介绍三种框架的内部实现和底层原理!本文档只会从他们之间的共同点、思想上来说明一下他们之间的区别,从而告诉你在分别学习这个三个框架的时候有的放矢!
6 |
7 | 相关示例代码可以到对应[angular]()、[vue]()及[react]()目录下查看!
8 |
9 | **注:文档都是建立在我自己在开放过程中一些实际操作上,所以跟你自己实现上有点区别,请知悉!**
10 |
11 | 当然了,不对的地方,欢迎来[这里]()讨论!
12 |
13 | ## 组件思想
14 |
15 | 首先说下三个框架都有组件的概念,首先说一下组件到底是个什么东西,以及在三个框架中怎么去理解他们!按我自己的理解,组件就是一个个独立的单元,里面封装了一些特定的功能,这样一些组件可是实现复用!形象地作一个比喻:如何把一个人看作一个整体,那么你可以理解成这个人是由眼睛、鼻子、嘴巴以及穿衣等等构成的,这些每个独立的功能单元你就可以把它理解成组件,更有甚者,一个html文件,每个标签也可以看作一个组件,由这些组件构成了我们的html文档。好,我们来分别看看三个框架是怎么封装一个组件的!
16 |
17 | * **angular1**:严格来说,angular是没有组件这样一个概念的,但是它可以通过指令去封装一些特定功能的单元,从而实现类似其它框架的组件功能。如:
18 |
19 | ``` html
20 |
21 |
22 |
23 |
24 | ```
25 |
26 | ```javascript
27 | angular.module('app',[])
28 | .directive('customComponent',function(){
29 | return {
30 | restrict:'EA',
31 | template:'
i am directive,i can encapsulate some features',
32 | scope:{}
33 | };
34 | });
35 | ```
36 |
37 | * **vue**:vue是有组件的概念的,vue的组件概念其实跟angular的指令差不多,只不过更形象的一点。有两种方法可以实现组件:
38 |
39 | ```html
40 |
41 | ```
42 |
43 | ```javascript
44 | Vue.component('customComponent',{
45 | template:'i am component,i can get data from parent element with props. i can get data from data setter yet,eg: {{msg}}
',
46 | data:function(){
47 | return {
48 | msg:'hello world!'
49 | };
50 | }
51 | });
52 | new Vue({
53 | el:'#app',
54 | template:' '
55 | /*data:{
56 | currentView:'customComponent'
57 | },
58 | render(h){return h(this.currentView);}*/
59 | });
60 | ```
61 |
62 | * **react**:在react中,最核心的就是组件概念了,因为react的思想就是专注于ui层,通过构建组件,来构成我们的用户界面。我们来看一下在react中怎么实现组件。
63 |
64 | ```html
65 |
66 | ```
67 |
68 | ```javascript
69 | function CustomComponent(props){
70 | return (
71 | i am a react component,i can get data from props and state!
72 |
);
73 | };
74 |
75 | ReactDOM.render(
76 | ,
77 | document.getElementById('app')
78 | );
79 | ```
80 |
81 | ## 如何传数据
82 |
83 | 有些时候,我们需要在组件或者控制器之间传输一些数据,以实现数据共享。所以我们来分别看一下它们如何是传数据的!
84 |
85 | * **angular**:在angular中需要实现数据共享的地方主要是各个控制器彼此之间传输数据了!其实原理就是就是在各个控制器之间通过广播、或者发射事件实现数据的传播。父、子控制器通过broadcast广播事件由父组件向子组件传递数据,通过emit发射事件由子组件向父组件中传递数据。如:
86 |
87 | ```html
88 |
89 |
90 | {{ctrol1.msg}}
91 |
92 | {{ctrol2.msg}}
93 |
94 |
95 |
96 | ```
97 |
98 | ```javascript
99 | angular.module('myApp',[])
100 | .run(function($rootScope){
101 | $rootScope.$on('hehe',function(e,d){
102 | console.log(d);
103 | });
104 | })
105 | .controller('controller1',function($scope){
106 | $scope.ctrol1 = {};
107 | $scope.ctrol1.msg = 'this is controller1 message';
108 | $scope.$broadcast('boom','向子组件传递');
109 | $scope.$emit('hehe','向父组件传递数据');
110 | })
111 | .controller('controller2',function($scope){
112 | $scope.ctrol2 = {};
113 | $scope.$on('boom',function(e,d){
114 | console.log(d);
115 | });
116 | $scope.ctrol2.msg = 'this is controller2 message';
117 | });
118 | ```
119 |
120 | 当然了,其实我们也可以通过服务来共享数据,如:
121 |
122 | ```html
123 |
124 |
125 | {{msg}}
126 |
127 |
128 | {{msg}} {{getData}}
129 |
130 |
131 | ```
132 |
133 | ```javascript
134 | angular.module('app1',[])
135 | .controller('controller1',function($scope,testServe){
136 | $scope.msg = 'this is a test controller1 message!';
137 | $scope.click = function(){
138 | console.log(testServe.serveMsg);
139 | testServe.serveMsg = 'controller1 send data';
140 | };
141 | })
142 | .controller('controller2',function($scope,testServe){
143 | $scope.msg = 'this is a test controller2 message!';
144 | $scope.click = function(){
145 | console.log('点击了吗?');
146 | $scope.getData = testServe.serveMsg;
147 | console.log(':'+testServe.serveMsg);
148 | setTimeout(function(){
149 | $scope.$apply();
150 | },0);
151 | };
152 | })
153 | .service('testServe',function(){
154 | this.serveMsg = '';
155 | });
156 | ```
--------------------------------------------------------------------------------
/04/readme.md:
--------------------------------------------------------------------------------
1 | ## 自定义bind函数实现任意绑定函数调用上下文
2 |
3 | ### 业务背景
4 |
5 | * 我们在编写js的过程中经常会出现这样的逻辑需要:某个对象临时需要一个方法,用来处理一些业务,而这样的一个方法的逻辑已经在另一个对象的方法里或函数里面定义过了,所以我们可以不用再在这个对象下添加一个相同的方法了!那我们该怎么做呢?我们的逻辑就是调用已经实现了业务需要的方法或者函数,只是把它的上线文改为当前对象就好了,需要灵活使用的两个方法就是apply和call。好了,我们先来看下下面的示例代码吧:
6 |
7 | ```javascript
8 | var obj = {
9 | name : 'obj',
10 | getName : function(prefix){
11 | prefix = prefix || 'hello';
12 | var _newValue = prefix + ' ' + this.name;
13 | console.log(_newValue);
14 | return _newValue;
15 | }
16 | };
17 | var obj1 = {
18 | name : 'obj1'
19 | };
20 | //我们现在需要实现让obj1拥有和obj同样的getName方法
21 | obj.getName.call(obj1,'-prefix-obj1- hello');
22 | // 打印-prefix-obj1- hello obj1
23 | ```
24 |
25 | 到此,我们可以实现自由切换上下文了,这里说明一下就是apply和call的区别:从目的上来说,它们是一样的,唯一不同的地方传递参数的形式不同,apply是数组,而call跟我们平时写函数一样,一个一个传进去,所以怎么使用它们看具体业务场景的参数是什么形式的,总的来说就是你怎么爽就怎么用!看下面的代码:
26 |
27 | ```javascript
28 | function sum(){
29 | var num = 0;
30 | for(var i = 0,len = arguments.length;ijavascript中event loop是什么
2 |
3 | ## 声明
4 |
5 | 这篇文章是基于[原英文版](http://altitudelabs.com/blog/what-is-the-javascript-event-loop/)文章翻译过来的,所以你有任何疑问可查看原文,感谢原文。
6 |
7 | 这篇文章不作说明的,情况下,JS或js指Javascript,请知悉!
8 |
9 | 示例代码在index.html文件中。
10 |
11 | ## 介绍
12 |
13 | 如何你跟我一样的话,那么你一定会爱上javascript!虽然它不是一种比较完美的编程语言,但是严格地说,还有其它比javascript更完美的语言来设计web吗?所以请忽视javascript的缺陷吧。我喜欢web工作,而javascript使得我可以用其来建立applications,从而跟web上的用户联系起来!
14 |
15 | 但是如果你深究javascript——你会发现,它的一些内部概念你需要花费许多时间才能真正理解!其中一个概念就是Event Loop,可能一个在web开发中工作了几年的开发人员也没有真正理解其含义和工作原理,不管怎样,我希望通过这篇文章,能带给你一些启发:什么是event loop?其实它并不是那么难以理解的!
16 |
17 | ## 浏览器中的Javascript
18 |
19 | 我们通常所说的javascript,都是指浏览器端使用的javascript——因为我们写的javascript代码大多数是用在客户端的!不管怎样,理解这些概念和技术是非常重要的:1、Javascript Engine(比如 [Chrome's V8](https://en.wikipedia.org/wiki/JavaScript_engine)),2、[Web API](https://developer.mozilla.org/en/docs/Web/API)(如 [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)),3、Event Loop 和 Event Quene。
20 |
21 | 你可能看过上面列举的这些概念之后,你可能会这样想:“好复杂呀!”,确实是这样的!但是通过这篇文章待会儿你会发现,其实它们并没有那么复杂!
22 |
23 | 在介绍event loop之前,我们需要对javascript engine做一个简单的理解来明白它是做什么的!
24 |
25 | ## Javascript Engine
26 |
27 | 有几种不同的js引擎,但是最流行的一个当属谷歌的V8了(它并不仅仅在浏览器环境中使用,通过nodejs,它也在服务器端使用)。但是js引擎的工作到底是什么呢?其实,引擎的工作非常简单——它会遍历我们写的所有js代码,然后一个一个的执行它们。也就是说我们的js是单线程的,单线程不好的地方就是,如果你同步执行了一段很长时间的代码的话,那么这段代码后面的逻辑会被阻塞从而等待很久才能执行。同门通常都不想写阻塞的代码——特别是在浏览器中,设想如果你点击了一个按钮之后去执行了一个很长时间的阻塞代码,这会导致你的页面的其它交互效果会暂停,严重影响用户体验!
28 |
29 | 那么js引擎是怎么知道一个一个的处理代码呢?其实它使用了call stack(调用栈)。你可以把调用栈看作一个电梯——第一个进电梯的人最后一个出来,最后一个进电梯的人第一个出来!
30 |
31 | 我们来看一个例子:
32 |
33 | ```javascript
34 | /*Within main.js*/
35 | var firstFunction = function(){
36 | console.log("I'm first!");
37 | };
38 | var secondFunction = function(){
39 | firstFunction();
40 | console.log("I'm second!");
41 | };
42 | secondFunction();
43 | /*Results:
44 | *=> I'm first!
45 | *=> I'm second!
46 | */
47 | ```
48 |
49 | 首先,js引擎会维护一个调用栈序列的:
50 |
51 | * main.js第一次执行的时候,调用栈序列是这样的:
52 |
53 | 
54 |
55 | * 当调用secondFunction的时候,调用栈序列:
56 |
57 | 
58 |
59 | * 我们调用secondFunction导致firstFunction被调用,所以此时序列是这样的:
60 |
61 | 
62 |
63 | * 当执行完firstFunction,会打印“I'm first!”,然后在其内部就没有其它代码执行了,所以调用栈序列需要把firstFunction移除,所以此时序列是这样的:
64 |
65 | 
66 |
67 | * 同样,secondFunction打印完“I'm second!”之后,在其内部也没有其它代码需要执行了,所以会移除其,所以此时序列是这样的:
68 |
69 | 
70 |
71 | * 最后,当main.js中没有其它代码需要执行的时候,那么匿名函数也会被从调用栈中移除,从而这样:
72 |
73 | 
74 |
75 | ## 好了,说了半天,什么是Event Loop?
76 |
77 | 到现在,我们已经知道js引擎的调用栈的原理了,那么我们回到刚才所讨论的阻塞代码的问题上,我们都知道应该避免写阻塞代码!庆幸的是,js提供了一种机制来实现非阻塞:异步回调函数!我们又接触了一个概念,是不是以为这个概念很牛逼呢?其实并不是,异步回调函数跟你写过的其它的普通的函数一样的!只不过它不是同步执行的,如果你熟悉的setTimeout函数的话,它就是异步的,我们来看一个例子:
78 |
79 | ```javascript
80 | /* Within main.js */
81 |
82 | var firstFunction = function () {
83 | console.log("I'm first!");
84 | };
85 |
86 | var secondFunction = function () {
87 | setTimeout(firstFunction, 5000);
88 | console.log("I'm second!");
89 | };
90 |
91 | secondFunction();
92 |
93 | /* Results:
94 | * => I'm second!
95 | * (And 5 seconds later)
96 | * => I'm first!
97 | */
98 | ```
99 |
100 | 我们看一下调用栈序列是怎么样的!(为了尽快说明问题,我们快进了)
101 |
102 | * 当调用secondFunction的时候,调用setTimeout函数,所以序列是这样的:
103 |
104 | 
105 |
106 | *
107 |
108 | 当调用栈在调用setTimeout的时候,发生了一起特别的事情——浏览器会把setTimeout的回调函数放到(在这个例子中就是firstFunction)Event Table中。你可以把Event Table理解成为一个注册机构,调用栈会告诉Event Table去注册一个特别的函数,当特定的事件发生的时候去执行它,当特定的事件发生时,Event Table只是简单的把这个函数移动到Event Quene里面,Event Quene只是一个放置函数即将执行的暂存区,最后由Event Quene把函数移动到调用栈中。
109 |
110 | 你可能会问,Event Quene什么时候知道把回调函数放回到调用栈中呢?好,js引擎遵从一个简单的规则:浏览器中存在一个进程会不断地去检查调用栈是否为空,同时,无论任何时候只要调用栈为空,它会检查Event Quene是否有待执行的函数队列!如果调用栈为空,那么它会把Event Quene队列里面的第一个函数放到调用栈中,如果Event Quene为空的话,那么这个进程将会无限期地来回地进行检查,好吧——我们描述的这个就是所谓的Event Loop!
111 |
112 | * 现在回到我们的例子,执行的setTimeout函数的时候,我们向Event Table里面注册了一个回调函数(在这个例子中是firstFunction),并且延迟5秒可执行!调用栈序列此时是这样的:
113 |
114 | 
115 |
116 | * 我们运行代码的时候会发现,我们的代码并没有等待5秒之后才打印“I'm second!”,而是自然地执行了下一行代码,此时代码序列是这样的:
117 |
118 | 
119 |
120 | * 在背后,Event Table会不断的监视跟回调函数相关的时候那个事件(这个例子中为等待5秒)是否发生,从而把回调函数移动到可执行的Event Quene序列里面。与此同时,secondFunction和main.js中匿名函数都执行完了,所以调用栈序列此时是这样的:
121 |
122 | 
123 |
124 | * 经过5秒之后,event table会移动firstFunction到event quene里面:
125 |
126 | 
127 |
128 | * 同时event loop不断的监视当前调用栈是否为空,如果是,则把event quene序列里面的第一个回调函数放到调用栈(新的,不同于刚才的调用栈,刚才的已经没有了)里面。所以此时调用栈序列是这样的:
129 |
130 | 
131 |
132 |
133 | * 当firstFunction执行完成之后,浏览器会返回一个状态:**调用栈会空,event table没有任何事件可监听,同时Event Quene队列为空!**最后是这样的:
134 |
135 | 
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/10/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 微信JSSDK测试
11 |
23 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
177 |
178 |
--------------------------------------------------------------------------------
/06/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 因为之前一直不理解javascript里面回调函数的含义,直到后面用的多了,才慢慢理解怎么使用回调函数!所以今天想把自己对回调函数的理解记录下来加深印象!这篇文档尽量把相关概念说得尽可能简单,所以认认真真看完我相信对怎样理解回调函数还是有一定帮助的!当然了有说的不对的地方,也希望提出意见,让我们一起共同成长!
4 |
5 | ### 什么是回调函数
6 |
7 | * 首先我们把“回调函数”分为两个部分来理解:“回调”和“函数”!作了这样的划分之后,我们可以首先得到一个印象————回调函数本质上是一个普通的javascript函数而已,其实javascript中很多概念它们都是函数另外一种称呼而已,比如闭包什么的!关键看你怎么理解了!再从“回调”上理解,“回调”说明回调函数调用是被动的,颇有点“回头再用”的意思,怎么理解呢!就是一个函数我们定义了之后,我们并不是单独调用它 ,而是把它放到某个“大的函数”里面,等到“大函数”一些“状态”满足之后再来调用回调函数,也就是说回调函数是等着别人来调用它的,它本身是不直接调用自己的!从某种意义上来说,javascript里面所有函数都是回调函数,因为你可以这样理解———所有的函数都是等js文件准备好之后,再调用,它们是“等待着被动调用的”!来看个例子吧:
8 |
9 | ```javascript
10 | function a(){
11 | console.log('a');
12 | };
13 |
14 | function b(){
15 | console.log('b');
16 | a();
17 | };
18 |
19 | b();//输出b , a
20 | ```
21 |
22 | 这里a就是一个回调函数,因为它是等待着b函数调用之后在b函数里面调用的!
23 |
24 | ### 常见的回调函数形式
25 |
26 | * 在介绍回调函数的常用形式之前,我们先来说一下javascript里面的函数————首先js函数是一个很强大的概念,得益于函数在javascript中是第一等对象,函数既可以作对象的方法,也可以当作参数传递给其他函数,更可以作为返回值在函数里面返回等等。所以等等,函数可以出现的地方,都有可能是回调函数出现的形式,大致上分为三种吧:
27 |
28 | _ 1、作为响应事件的处理函数,如下:
29 |
30 | ```javascript
31 | document.body.onclick = function(){
32 | alert("this is body");
33 | };
34 | ```
35 |
36 | _ 2、作为参数传递给其它函数,如下:
37 |
38 | ```javascript
39 | function inputTwoNumber(x,y,callback){
40 | if(Object.prototype.toString.call(x) !== '[object Number]' || Object.prototype.toString.call(y) !== '[object Number]'){
41 | return;
42 | }else{
43 | return callback(x,y);
44 | };
45 | };
46 |
47 |
48 | inputTwoNumber(3,5,function(num1,num2){
49 | return num1+num2;
50 | });//return 8
51 | ```
52 |
53 | 上面,我们在inputTwoNumber的第三个参数上传递了一个匿名函数实现回调,其实我们我们也可以按下面的方式,性质是一样的:
54 |
55 | ```javascript
56 | function add(num1,num2){
57 | return num1+num2;
58 | };
59 | inputTwoNumber(3,5,add);
60 | ```
61 |
62 | 采用匿名函数的方式之所以能实现是因为js中函数是一等对象,可以传递传递给其他函数作参数!
63 |
64 | _ 3、作为普通方法在其它函数里面调用,如:
65 |
66 | ```javascript
67 | function a (){
68 | console.log('a');
69 | };
70 |
71 | function b(){
72 | console.log('b');
73 | a();
74 | };
75 | ```
76 |
77 | 当然了可能还有其他形式,就不一一列举了,这里没有列出来的,可能我一下子没有想到,但是只要能理解就好了!
78 |
79 | ### 回调函数有什么好处
80 |
81 | * 1、把一个大函数分解成一些小函数,提高代码复用,比如上面的inputTwoNumber函数,即使不用回调函数它也可以实现相加的操作,但是把所有逻辑都写在一个函数里面是很不好了,将来函数越来越到,将变得难以维护。而且我们使用回调函数之后,可以把回调函数用在其它实现相加运算的地方,这样代码复用率也提高起来了!
82 |
83 | * 2、代码更灵活了,还是拿上面inputTwoNumber函数来说,如果不用回调函数,直接把加法逻辑写在函数体里面,我们能想到的问题就是,你这个函数最后只能实现加法运算了,并不能做其他事情了,但是我们通过回调函数就可以实现inputTwoNumber只是一个传递数值的“身体”而已,而我们具体做什么我们可以自由发挥,这样是不是很舒服呢!
84 |
85 | * 3、实现异步调用,因为我们有些操作并不是同步的,所有我们需要编写回调函数,用于将来在某个事件触发的时候再来调用,比如,事件响应函数、所有异步操作之后调用的函数!如下:
86 |
87 | ```javascript
88 | document.body.onclick = function(){
89 | alert("我等点击触发的时候才调用!");
90 | };
91 | ```
92 |
93 | ### 用回调函数实现一个异步向后台实现读出json数据的例子
94 |
95 | * 我们来看一下如下的一段代码:
96 |
97 | ```javascript
98 | var xhr = new XMLHttpRequest();
99 | xhr.open('GET','./test.json',true);
100 | xhr.send();
101 | xhr.onreadystatechange = function(){
102 | if(xhr.readyState == 4 && xhr.status == 200){
103 | setDataToUrl(xhr.responseText);
104 | };
105 | };
106 |
107 | function setDataToUrl(data){
108 | data = JSON.parse(data);
109 | var dom = document.getElementById('test');
110 | var oul = document.createElement('ul');
111 | for(var pro in data){
112 | var oli = document.createElement('li');
113 | oli.innerHTML = data[pro];
114 | oul.appendChild(oli);
115 | };
116 | dom.appendChild(oul);
117 | };
118 | ```
119 |
120 | 后台的json文件如下:
121 |
122 | ```json
123 | /*test.json*/
124 | {
125 | "name":"myCustomJson",
126 | "description":"this is a test json file"
127 | }
128 | ```
129 |
130 | 结果截图如下:
131 |
132 | 
133 |
134 | ### 回调函数是异步的吗?
135 |
136 | * 曾经这个问题困扰我很久,因为我一直理解回调函数就是一个函数而已,为什么很多人是它是异步的呢!所以我不认为回调函数不是异步的,最后通过不断练习才证明了我的观点是正确的————回调函数本身不是异步的,只是引起回调函数调用的这个行为本身是异步的而已,还是看下面的代码:
137 |
138 | ```javascript
139 | document.body.onclick = function(){
140 | console.log("回调函数");
141 | };
142 | ```
143 |
144 | 就像上面的事件处理函数它就是一个普通的匿名函数,它本身不是异步的,我们之所以说他是异步的,是因为本身点击这个事件是异步的,而通常我们所说的异步函数函数又用在这样的场景中,所以我们久而久之就习惯把回调函数说成异步的了,这点要切记,**但是回调函数本身来说不是异步的,只是引起它调用的那个操作是异步的**!
145 |
146 | ### 回调函数不好的地方
147 |
148 | * 回调函数一个最大的影响就是,就是容易形成回调地狱,设想这样的场景:a函数回调b,b函数回调c,c函数回调d...如此下去,是不是很绕呢!你的代码将变得晦涩难懂,而你也很难管理。这样的场景在向后台获取数据的时候经常遇到,看下面的示例代码:
149 |
150 | ```javascript
151 | $('xx').animate({xxxx}, function(){
152 | $('xx').animate({xx},function(){
153 | //do something,后面可能还有回调
154 | },1000)
155 | },1000)
156 | ```
157 |
158 | ### 解决回调函数地狱的一些方法
159 |
160 | * 在es6中实现了几种方式来解决回调函数所带来的回调地狱问题如:Generator函数、THUNK函数、async函数及Promise等。有机会大家可以去了解下,这里只简单介绍一下Promise方式,其它方式不介绍。
161 |
162 | * Promise英文译为“承诺”,意思在将来某个时间会给出答复,所以这里用这个意思来理解是非常合适的,Promise把异步操作化作三个状态:Pending(进行中)、Resolved(已成功)及 Rejected(以失败)。这三个状态之间只有两种转换方式:pending -> Resolved和 Pending -> Rejected。再无没有其它状态转换方式。对于Promise的细节这里不做介绍,因为相关文档肯定比这里介绍的仔细哦!
163 |
164 | * Promise的原理:Promise接受两个参数,一个用于处理异步成功之后的操作,一个用于失败之后的操作。这两个参数所需要的参数都是异步操作返回的数据!我们用Promise实现上面的从后台得到json数据:
165 |
166 | ```javascript
167 | function getJson(){
168 | var xhr = new XMLHttpRequest();
169 | xhr.open('GET','./test.json',true);
170 | xhr.send();
171 | return new Promise(function(resolve,reject){
172 | xhr.onreadystatechange = function(){
173 | if(xhr.readyState == 4 && xhr.status == 200){
174 | resolve(xhr.responseText);//成功之后返回的数据放在Promise第一个参数里面
175 | }else if(xhr.status>=300 || xhr.status <200){
176 | reject('');//请求失败之后,在第二个参数实现出错提示
177 | };
178 | };
179 | });
180 | };
181 | var printDiv = getJson();
182 | printDiv.then(function(data){
183 | data = JSON.parse(data);
184 | var dom = document.getElementById('test');
185 | var oul = document.createElement('ul');
186 | for(var pro in data){
187 | var oli = document.createElement('li');
188 | oli.innerHTML = data[pro];
189 | oul.appendChild(oli);
190 | };
191 | dom.appendChild(oul);
192 | });
193 | ```
194 |
195 | ### 后续内容正在补充中.......
196 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/12/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 之前有写过关于浏览器中关于js线程的文档,请移步[这里](https://github.com/woai30231/javascriptThreadStudy)查看!但觉得偏过于技术化了,对于实际理解意义不大,所以想乘此机会用一种大家都能懂的话语方式来记录一下自己对**浏览器中js线程**的理解,以及建立在此基础一些优化方案!
4 |
5 | * 这篇文档不是技术文档,只是力求把相关概念用最土的话说清楚!
6 |
7 | * 因为这部分技术在我看来是前端理解性能的高级话题了!所以我不敢保证我写的会很好,亦不敢保证能一定带给你实质性的超越。我唯恐自己经验、理解有限,难免会有表达出错的地方!所以欢迎你们[issue](https://github.com/woai30231/webDevDetails/issues),我们一切来参与起来把这个话题向更好的方向完善!
8 |
9 | ### 什么是线程?
10 |
11 | * 对于这个问题,我想没有一定的工作阅历沉淀或者相关专业方面的学习,恐怕难以叙述清除这个术语了!你要把这个问题问我,估计我也很难回答上来!但又如何?对于搞开发这种需要实际干事的工作来说,恐怕只是让你正确把一个专业术语表达出来,恐怕对你的工作没有多大意义!我们只要知道这个东西是干嘛的就行了!至于怎么把这个术语表达清楚,那是专门干这行的人的工作!好了,首先还是允许我把从搜索引擎上搜到的线程的描述粘贴在下面:
12 |
13 | > 线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。
14 |
15 | * 从上面的描述中大概知道了几个关键点:计算机给线程单独分派的空间、独立的单元、可执行的执行单元及顺序控制流程!按我的理解就是“一个独立的程序顺序执行空间”!
16 |
17 | * 如果做个比喻的话:线程就好比一个人去开公司,注册公司、运营、销售、推广都是他一个人在干,而且是按照一定的顺序干,比如需要先要有项目、再找供应商、等项目成熟了,再注册公司,最后一步一步做大!这个顺序都是最开始企划好的!这些话换到浏览器里就是:我们写的脚本只有一个执行单元来管理代码,并且按我们写代码的顺序依次执行代码。看下面代码例子:
18 |
19 | ```javascript
20 | var a = 'a';
21 | var b = 'b';
22 | console.log(a);
23 | console.log(b);
24 | //因为打印a的操作在前面,而打印b的操作在后面,那么打印a的代码先执行,而打印b的操作后执行
25 | ```
26 |
27 | * 顺便说一下多线程呗,顾名思义就是多个单线程一起来管理我们的代码,比如上面那个开公司的例子,老板等公司做大了之后,他终于明白了:他一个人再牛、再能干,可是公司大到一定程度,他一个人是忙不过来的,即便他一个人能省一笔很大的请工人所带来的薪水开销!于是他确定聘请工人来帮他工作,于是大家一起努力,共同把公司做得更大,这里每个工人就相当于一个线程!
28 |
29 | * 多线程和单线程的区别:还是拿前面开公司的例子来说明,一个人(单线程)做的时候,自己想怎么做就怎么做,不用怕影响别人,而且消耗的社会资源(吃喝拉撒)也少,因为一个人再消耗也消耗不到哪里去!而且还能省下很多费用(工人费用等),但是问题公司怎么做都做不大,做来做去还是一个小作坊,不能充分利用工人闲散资源来扩展公司等!多个人(多线程)一起工作的时候,效率提高了,但同时社会资源开销也大了,而且老板还要拿出一笔很大的费用来支付工人的工作,要不然老板估计的日子不好过!从这里我们作如下结论:
30 |
31 | > 单线程:优点是简单、占用资源少,缺点就是不能有效利用资源,比如多核cpu等,不适用那种复杂的业务场景
32 |
33 | > 多线程:优点是快速、充分利用资源,缺点是资源消耗大,容易造成让你的电脑死机等!
34 |
35 | * 这样理解是不是就能很好理解线程了呢,**其实现实中很多道理是想通的**!
36 |
37 | ### 为什么浏览器是单线程的?
38 |
39 | * 如果需要回答这个问题,我们首先需要知道浏览器的工作原理!浏览器在得到你的html之后,解析文档首先得到dom tree,然后解析css得到render tree。最后在通过js实现控制页面的显示、交互等!类似这种工作步骤就注定了浏览器里面的js不能是多线程的,为什么呢!试想一下如果是多线程的话:浏览器一边解析得到dom tree,一边又解析得到css tree,然后绘制页面,假如此时dom tree还没有完全解析得到,或者css tree没有完全解析完成,那么此时绘制页面会不会乱套?又或是一个还没有绘制完成的元素用实现某些逻辑会不会有问题?这些都是限制浏览器中js不能使用多线程的原因!
40 |
41 | ### 浏览器通过事件队列来实现处理异步逻辑
42 |
43 | * 浏览器中js确实是单线程的,但是浏览器是怎么响应某个按钮的点击处理或者在将来某个时间执行特定脚本呢!这里就要提到时间队列了,这是个什么概念呢!按我自己的理解就是:浏览器给脚本又单独分配了一个线程,这里确实是多线程!只不过是浏览器宿主环境提供的,用来处理将来js中异步需要执行的代码或处理某些事件的回调!为什么叫队列呢!人家都叫队列了,说明这些异步脚本不是随便放进去的,而是按照一种方式有序地排在里面的!看下面的代码:
44 |
45 | ```html
46 |
47 |
48 |
49 |
50 | demo
51 |
52 |
53 | 点击
54 |
69 |
70 |
71 | ```
72 |
73 | 上面的打印顺序说明异步代码是一种顺序(先来后到)在事件队列里面排序的!将来触发事件的时候总是以这样一个顺序去查找相应的代码然后执行之!
74 |
75 | ### 从单线程的角度出发怎么提升浏览器性能
76 |
77 | * 前面的分析得知,单线程就是一个人在工作,所以你就不要一下子给很多工作给他做,比如给他本来是多个人(多线程)做的工作,这样即时能做,估计浏览器也会很卡,或者假死或卡死!实际上页面场景中这样的工作包括更新dom、监听某种滚动、resize事件回调处理等!我们来看一个例子,我们需要用是js实现渲染一个2万行6列的表格,我们首先不做代码优化,看下效果会怎么样,代码如下:
78 |
79 | ```javascript
80 |
81 |
82 |
83 |
84 | 渲染table测试
85 |
103 |
104 |
105 |
106 |
124 |
125 |
126 | ```
127 |
128 | * 我们在浏览器中看到实际效果:浏览器等待了一段时间才渲染出来,在此期间浏览器像是被卡主了一样什么都不能做!好,我们对上面的代码做一个优化,使其分几步完成上面的需求,这样每次要做的事情就不会太多了!代码如下:
129 |
130 | ```html
131 |
132 |
133 |
134 |
135 | 渲染table测试
136 |
154 |
155 |
156 |
157 |
187 |
188 |
189 | ```
190 |
191 | * 我们发现代码经过这样优化之后,是不是dom很快就被渲染出来了呢!其思想就是:把一个很耗性能的操作或长时间的操作分解成一些耗性能较少或这耗时少的操作,并善于利用setTimeout用来分解一些操作,就不会造成耗时长的代码使浏览器卡死的情况了!而且能快速的把页面呈现在用户面前!
192 |
193 | * 其实很多优化代码都是采用这种原理做的,比如防抖等!
194 |
195 | ### 今天就写到这里,后续有什么新的想法再加上来,或者你也可以[issue](https://github.com/woai30231/webDevDetails/issues)上来……
--------------------------------------------------------------------------------
/13/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 相信大家在使用git进行团队开发的过程中,总会出现一些情况下需要去合并分支的操作,我也会经常出现这种情况,因为毕竟开发跟需求总是会有不同步的时候,所以难以在一条分支上解决很多问题,这时我们就需要进行新建分支进行测试性的开发,最后确定需求的时候再合并到主干上提交到生产环境!
4 |
5 | * 使用git进行合并分支说难也不难,因为就使用一下那几个命令而已!但是不小心操作总会有一些难以预料的问题出现,所以这篇文档就介绍一下怎么用git进行合并分支操作!
6 |
7 | * 最后说一下,这边文档是基于[这篇英语文档](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging)然后集合我自己的理解和描述来通过小小的实际案例编写的,所以有些表达有误的地方建议你阅读原文!
8 |
9 | ## 开始
10 |
11 | 假如我们现在有个git仓库,我们现在工作在master分支上,我们加了一个文件index.html,内容如下:
12 |
13 | ```html
14 |
15 |
16 |
17 | git branch merging demo
18 |
19 |
20 |
21 |
22 |
23 | ```
24 |
25 | 然后我们新建一个分支,然后在index.html进行一些测试性的修改,命令如下:
26 |
27 | ```bash
28 | git checkout -b issue53
29 | ```
30 |
31 | 这条命令的结果在当前master分支的基础上新建一个分支issue53然后切换到issue53分支,此时issue53和master指针都指向同一个内容,如图所示:
32 |
33 | 
34 |
35 | 假设对index.html改动如下:
36 |
37 | ```html
38 |
39 |
40 |
41 |
42 |
43 | git branch merging demo
44 |
45 |
46 |
47 |
48 |
49 | ```
50 |
51 | ok当我们做这样的改动之后,现在假设项目经理说我们的index.html有点问题急需修改,所以我们需要回到master分支进行开发,但是我们的issue53怎么办呢?因为这上面的功能是测试性的开发,还不确定能否合并到master上,所以我只能单独提交issue53分支,然后回到master分支,命令如下:
52 |
53 | ```bash
54 | git commit -a -m 'initial issue53 branch'
55 | git checkout master
56 | ```
57 |
58 | ok,我们现在返回到master分支,再来查看各个分支指针指向情况,如图所示:
59 |
60 | 
61 |
62 | 因为此时我们在master分支上发现了bug,一般情况下我们都是在master分支的基础上新建一个修复bug的分支,等bug确认修复好之后再合并到master分支上,于是,我们新建一个分支hotfix,命令如下:
63 |
64 | ```bash
65 | git checkout -b hotfix
66 | ```
67 |
68 | 此时,对html改动如下:
69 |
70 | ```html
71 |
72 |
73 |
74 |
75 | git branch merging demo
76 |
77 |
78 |
79 |
80 |
81 | ```
82 |
83 | 然后我们提交到仓库,命令如下:
84 |
85 | ```bash
86 | git commit -a -m 'bug fixed'
87 | ```
88 |
89 | 到这个时候,我们再来看下各个分支指针指向情况:
90 |
91 | 
92 |
93 | 如何我们这个时候,可以确定我们所作的修复是正确的,那么就需要把hotfix分支上的修改合并到master分支上,
94 | 命令如下:
95 |
96 | ```bash
97 | git checkout master
98 | git merge hotfix
99 | ```
100 |
101 | 可以看到控制台结果如下:
102 |
103 | 
104 |
105 | 这里我们看到一个短语Fast-forward,什么情况下会出现这个短语呢?加入我们处理的两个的分支:其中一个分支可以在另外一个分支的历史版本中找到,那么就会出现Fast-forward!说白了就是其中一个分支是另外一个分支的子分支!看下原文给出的解释:
106 |
107 | 
108 |
109 | 所以在这种情况下合并,git只是会简单的改变一下master分支指针的指向而已,把它指向两个分支中最新的版本。此时各个分支指针的指向如下:
110 |
111 | 
112 |
113 | 到这一步,我们就可以删除hotfix分支了,因为我们已经不需要hotfix分支了,命令如下:
114 |
115 | ```bash
116 | git branch -d hotfix
117 | ```
118 |
119 | 当我们的bug修复完成之后,我们就需要回到刚才中断开发的分支上,也就是issue53分支上,命令如下:
120 |
121 | ```bash
122 | git checkout issue53
123 | ```
124 |
125 | 我们在issue53分支上对index.html改动如下:
126 |
127 | ```html
128 |
129 |
130 |
131 |
132 |
133 | git branch merging demo
134 |
135 |
136 |
137 |
138 |
139 | ```
140 |
141 | 我们再来看一下分支指针指向情况:
142 |
143 | 
144 |
145 | 好,这个时候,假设项目经理决定在issue53分支上进行的测试性开发可以发布到正式版本上,所以我们就需要合并issue53分支到master分支上!命令如下:
146 |
147 | ```bash
148 | git checkout master
149 | git merge issue53
150 | ```
151 |
152 | ok,我们看一下终端输出情况:
153 |
154 | 
155 |
156 | 我们可以看到已经没有Fast-forward短语了,那么git是怎么进行合并的呢?我们 先看一下原文是怎么进行叙述的:
157 |
158 |
159 | 
160 |
161 | 用一句话来解释就是:git进行了三方面的合并,一方面分别找到两个需要合并分支的的祖先,也就是说它找到它们相同的部分,然后再分别标记两个不同的分支,最后把这两个不同的部分进行合并,最后生成一个分支,也就是我们最终合并得到的master分支,此时我们再来看一下分支指针指向情况:
162 |
163 | 
164 |
165 | 我们可以看到当前master分支的父版本有两个分支,因为它是来自于两个部分!
166 |
167 | 到这一步你是不是以为合并分支是不是很简单呢!当然不是了,如果我们在两个子分支上更改了同一部分内容,那么我们进行合并的时候就会出现冲突,为了演示这个问题,我们现在在master分支的基础上建立两个分支dev1和dev2并同时修改同一部分内容,看下会出现什么情况。命令如下:
168 |
169 | ```bash
170 | git checkout -b dev1
171 | git checkout master
172 | git checkout -b dev2
173 | ```
174 |
175 | 在dev2分支上改动如下:
176 |
177 | ```html
178 |
179 |
180 |
181 |
182 |
183 |
184 | git branch merging demo
185 |
186 |
187 | dev2
188 |
189 |
190 | ```
191 |
192 | 然后切换到dev1分支,命令如下:
193 |
194 | ```bash
195 | git checkout dev1
196 | ```
197 |
198 | 改动如下:
199 |
200 | ```html
201 |
202 |
203 |
204 |
205 |
206 |
207 | git branch merging demo
208 |
209 |
210 | dev1
211 |
212 |
213 | ```
214 | 我们此时在dev1和dev2分支上更改了master分支的同一部分内容,我们先合并dev1分支到master,再合并dev2分支到master分支看会出现什么提示。命令如下:
215 |
216 | ```bash
217 | git checkout master
218 | git merge dev1
219 | git merge dev2
220 | ```
221 |
222 | 终端截图如下:
223 |
224 | 
225 |
226 | 我们可以看见有冲突了,并显示冲突就是index.html,于是我们打开index.html发现有这么一行数据,截图如下:
227 |
228 | 
229 |
230 | 这里显示了我们在合并的时候存在冲突的地方,我们根据需要更改成自己需要的内容然后再提交即可!这个时候可以
231 |
232 | ```bash
233 | git status
234 | ```
235 | 查看相关状态!
236 |
237 | 当我们把冲突解决完成之后,就可以使用如下命令成功合并了:
238 |
239 | ```bash
240 | git add .
241 | git commit -m 'submit'
242 | ```
243 | 最后我们再查看一下结果:
244 |
245 | 
246 |
247 | ### 内容补充
248 |
249 | * 昨天因为有点事情先撤了,所以有部分该写的内容没来得及加上来,今天决定加上来。接着上文所写的内容,你肯定会想问:git什么时候合并两个分支的时候会出现冲突呢?以我的经验来看,如果你在合并分支的时候满足以下两个条件,肯定会出现冲突:1、合并的两个分支,并不存在所属关系,也就是说a分支并不是b分支的“子”分支,说白了,其中一个分支指向的内容并不是另外一个分支历史版本中的一个分支,如下图,master分支指向的是hotfix或issue53分支的上一次提交,所以这里我们可以理解成master分支是hotfix或issue53的“子”分支,因为它指向内容都可以在hotfix或issue53的历史提交版本中找到;2、你在合并的两个分支上改了同一部分内容。如何满足了中两个条件,那么合并分支的时候必定会产生冲突!
250 |
251 | 
252 |
253 | * 其实从这点我们就可以知道,我们在平时开发的过程中,如果有新功能或者bug修复,我们不应该在主分支上修改,而是应该新建一个分支,等功能完善之后或者bug修复之后再合并过去,这样就不会出现冲突了,因为被合并的那个分支始终是“子”分支,还能保证主分支的干净稳定!
254 |
255 |
256 | ### 如果有写的不到位的地方,请你[issue](https://github.com/woai30231/webDevDetails/issues)呗!
257 |
258 |
259 |
260 |
261 |
--------------------------------------------------------------------------------
/18/readme.md:
--------------------------------------------------------------------------------
1 | 我们在开发的过程中,经常会有这样一种情况,函数被频繁的调用,如果这个函数执行了某些dom操作的话,那么浏览器将会非常耗费性能,从而影响用户体验。
2 |
3 | 比如下面的代码,我们在滚动的过程中,会频繁的调用函数:
4 |
5 | ```html
6 |
7 |
8 |
9 |
10 |
11 |
12 | demo
13 |
18 |
19 |
20 |
26 |
27 |
28 | ```
29 |
30 | 我们打开浏览器,然后打开控制台,滚动鼠标的时候,控制台频繁的打印“invoke fn function”。我们这里为了显示,所以没有涉及到相关dom操作,但是实际开发过程中,更多场景是操作dom,那么将会使你的浏览器瞬间卡卡的感觉,有没有法子来限制一下fn的调用频率呢,答案是可以的,对此,我们将上面的代码改变如下:
31 |
32 | ```html
33 |
34 |
35 |
36 |
37 |
38 |
39 | demo
40 |
45 |
46 |
47 |
68 |
69 |
70 | ```
71 |
72 | 我们现在发现打开浏览器,滚动的时候不会那么频繁的调用fn函数了,只有当我们滚动的间隙稍微停顿300毫秒的时候才会调用一次,这样我们就做到了降低函数调用的频率了。
73 |
74 | 它的**原理**其实很简单:1 用闭包实现一个timer变量,用来保存上一次调用函数的定时器id;2 我们不是直接调用函数,而是中间需要一个间隔,如果两次调用之间的时间差小于我们传递的值,那么清空上一次的调用值;3 我们每一次调用的时候都清除一下上一次的调用定时器id,这样就保证了,如果间隔时间小于我们设置的值,那么上一次函数一定不会调用,从而达到了降低调用频率的效果。
75 |
76 | 上面这种通过设置定时器保证一段时间内事件回调函数只能执行一次的做法在javascript业界有一个专业的术语称谓——**防抖**!
77 |
78 | 上面的防抖操作,我们发现减少了回调函数调用的频率,但是它有一点点瑕疵:如果我们一直触发事件,回调函数只会在我们停止触发事件并达到了设置的时间间隔之后才会调用一次,也就是说在我们触发事件的过程中,回调函数一直没有执行,这在某些情况下,会跟实际业务需求不符。实际业务需求可能是,1 减少触发频率;2 但不能中间很大一段时间一直不执行。ok,那么此时我们就需要通过函数**节流**来实现!
79 |
80 | 把下面的代码在浏览器中打开,并水平缩放浏览器,看效果:
81 |
82 | ```html
83 |
84 |
85 |
86 |
87 |
88 |
89 | demo
90 |
109 |
110 |
111 | i am a div box,please resize the browser horizontally!
112 |
135 |
136 |
137 | ```
138 |
139 | 先来说下上面的页面需求:打开页面,在浏览器水平缩放的过程中,如果浏览器宽度不小于1000,那么不做任何事,否则设置dom的宽度为当前浏览器的宽度。
140 |
141 | 但是我们发现,我们在缩放的过程中,dom的尺寸并未做相应的更新,只有在停止缩放一段时间后,dom的宽度才更新到浏览器的宽度,这跟业务需求不符,于是我们代码改变如下:
142 |
143 | ```html
144 |
145 |
146 |
147 |
148 |
149 |
150 | demo
151 |
170 |
171 |
172 | i am a div box,please resize the browser horizontally!
173 |
198 |
199 |
200 | ```
201 | 我们发现经过这样改过之后,dom的宽度变成在我们缩放的过程中也会更新了,满足了我们业务需求。
202 |
203 | 好了,我们来简单介绍下什么是**节流**!
204 |
205 | 节流其实从名字上就知道它的含义——就是限制函数调用频率。
206 |
207 | 主要有两种方式实现:
208 |
209 | * 法一:时间差,原理无非就是两次调用之间的时间差小于设置时,那么不调用,反之调用。代码如下:
210 |
211 | ```javascript
212 | function ttrottle(fn,time){
213 | //上一次调用时间
214 | let lastInvokeTime = new Date().getTime();
215 | //当前调用时间
216 | let currentInvokeTime;
217 | return function(...args){
218 | currentInvokeTime = new Date().getTime();
219 | if(currentInvokeTime - lastInvokeTime <= time)return;
220 | let context = this;
221 | let _arguments = args;
222 | lastInvokeTime = currentInvokeTime;
223 | fn.apply(context,_arguments);
224 | };
225 | };
226 | ```
227 |
228 | * 法二:定时器实现,原理就是设置时间间隔,如果达不到时间间隔,就清除上一次调用回调定时器id。代码如下:
229 |
230 | ```javascript
231 | function ttrottle(fn,time){
232 | let isNeedInvoke = true;
233 | return function(...args){
234 | if(!isNeedInvoke)return;
235 | let context = this;
236 | let _arguments = args;
237 | isNeedInvoke = false;
238 | setTimeout(()=>{
239 | fn.apply(context,_arguments);
240 | isNeedInvoke = true;
241 | },time);
242 | };
243 | };
244 | ```
245 |
246 | ok,到这里大家应该知道**节流**的是干嘛以及原理了吧。
247 |
248 | 最后,再来总结一下**防抖**和**节流**的区别:
249 |
250 | * **防抖**和**节流**的相同点就是限制回调函数调用频率;
251 |
252 | * **防抖**在一段时间内,回调函数只会调用一次,即触发事件的最后一次;
253 |
254 | * **节流**在一段时间内,会每隔一段时间调用一次;
255 |
256 |
257 |
258 | **时间仓促,有错的地方,欢迎issue!**
259 |
260 |
261 |
262 |
--------------------------------------------------------------------------------
/15/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 在web开发中,你是否会有这样的体验:随便建立.css文件,然后从上到下依次编写你的网页样式,在此期间你无时无刻不在为怎样取类名而烦劳,因为害怕取重名了,把前面的样式覆盖了。同时你写的样式代码总是很随性的,想到那里就写到那里,可是你发现没有,你已经犯错了————什么错误呢?因为你可能前面已经写过了某些“元件”的样式,你后面又写了一次,造成你的css代码复用率很低,浪费你的时间写重复的代码。等等,你在写样式的时候不小心的话总会出现这样那样的问题!总结了一下,这些问题包括以下四个方面:
4 |
5 | > 1、不能重用代码;
6 |
7 | > 2、总是不可避免的复制/粘贴;
8 |
9 | > 3、在维护代码的时候,怎么减少复杂度及简单地重构;
10 |
11 | > 4、怎么减少命名冲突。
12 |
13 | * 好了,怎么解决这些问题呢!现在就有一种技术可以减轻这些问题————[BEM](https://en.bem.info/):(Block、Element、Modifled),也不能说这是一种技术哈,你可以把它理解为一种“约束、规则”等!我觉它就是让你在写css的时候形成一种规范,这样就可以减少上面出现的那些问题了,这个后文会讲到!
14 |
15 | * 这篇文档的编写是建立在[BEM](https://en.bem.info/)官网的基础上的,所有有什么不对的地方,请以官网的解释为主!
16 |
17 | ### 相关链接
18 |
19 | * [bem官网](https://en.bem.info/)
20 |
21 | * [what problems the bem can solve?](https://en.bem.info/methodology/)
22 |
23 | * [bem————quick start](https://en.bem.info/methodology/quick-start/)
24 |
25 |
26 | ### 什么是bem
27 |
28 | * BEM是一种以组件为基础的开发模式、规范!其背后的原理把用户的“用户界面”分开成一个个独立的组件,这样当我们构建一个复杂的用户界面的时候就会使开发起来变得简单、同时也加大了开发效率,同样它也会使你充分利用你写过的代码,而不是去机械地一步一步复制和粘贴来实现复用你的代码!
29 |
30 | * BEM主要包括三部分内容:Block、Element、Modifled!
31 |
32 | ### Block
33 |
34 | * Block是BEM的基础的,因为你可以理解BEM里面所说的一个组件就是一个Block,它是一个你以后可以重用的单元!
35 |
36 | * 特点:Block的取名(名字)应该描述的是你这个组件是什么,而不是描述它像什么!
37 |
38 | * 例子:
39 |
40 | ```html
41 |
42 |
43 |
44 |
45 |
46 |
47 | ```
48 |
49 | > 1、因为block描述的不影响它的使用环境,所以block的名字不能取描述外观的文字;
50 |
51 | > 2、在用BEM的时候,你也应该不使用id选择器。
52 |
53 | 这样做了,就能使你的block到处用了。
54 |
55 | #### 怎么使用blocks
56 |
57 | > 1、你可以嵌套不同的block;
58 |
59 | > 2、嵌套的层级也可以无限制。
60 |
61 | * 例子:
62 |
63 | ```html
64 |
65 |
71 | ```
72 |
73 | ### Element
74 |
75 | * Element其实就是Block的一部分,并且不能离开Block单独使用。
76 |
77 | * 特点:
78 |
79 | > Element的取名(名字)应该描述它的目标,而不是描述它的外貌;
80 |
81 | > Element的取名应该保持这种格式——block-name__element-name,element的名字应该跟他的block名字以双划线“__”连接起来。
82 |
83 | * 例子:
84 |
85 | ```html
86 |
87 |
93 |
94 | ```
95 |
96 | #### 怎么使用Elements?
97 |
98 | > 1、Elements可以彼此嵌套;
99 |
100 | > 2、并且嵌套层级不限制;
101 |
102 | > 3、一个Element总是对应的Block的一部分,而不是另外一个Element的一部分,所以不应该做这样的取名:block__elem1__elem2。
103 |
104 | * 例子:
105 |
106 | ```html
107 |
108 |
114 |
115 |
116 |
125 | ```
126 | Block名字定义了命名空间,从而保证Elements可以依赖于Block而存在!一个Block可以嵌套多Elements,如:
127 |
128 | ```html
129 |
136 | ```
137 |
138 | 下面这种格式是bem通用的格式:
139 |
140 | ```css
141 | .block {}
142 | .block__elem1 {}
143 | .block__elem2 {}
144 | .block__elem3 {}
145 | ```
146 |
147 | 这种格式可以使你在改变dom-tree的情况下,不改变css就能使用:
148 |
149 | ```html
150 |
156 | ```
157 |
158 | 一个Element永远是一个Block的一部分,你不能离开Block单独使用它,如:
159 |
160 | ```html
161 |
162 |
163 |
169 |
170 |
171 |
172 |
174 |
175 |
176 |
177 |
178 |
179 | Search
180 | ```
181 |
182 | Elements对于Block来说是可选的,并不是所有的Block都有Elements,如:
183 |
184 | ```html
185 |
186 |
187 |
188 |
189 |
190 |
191 | Search
192 |
193 | ```
194 |
195 | ### 我什么时候该建立一个Block或者一个Element?
196 |
197 | > 1、如果一个板块将来需要复用,并且不依赖其它任何组件,那么这个时候就应该建立一个Block;
198 |
199 | > 2、如果一个板块不能离开一个实体而单独使用,那么这个时候就应该建立一个Element。
200 |
201 | ### Modifier
202 |
203 | * Modifier主要定义了Block或者Element的状态、外观等“附加”的样式!
204 |
205 | * 特点:
206 |
207 | > Modifier的取名(名字)描述了组件的外观(比如尺寸什么的!)或者状态(比如disabled等!);
208 |
209 | > Modifieer的名字应该跟Block名字或者Element名字以“_”连接。
210 |
211 | * 例子:
212 |
213 | ```html
214 |
215 |
221 | ```
222 |
223 | ```html
224 |
225 |
231 |
232 |
233 |
245 | ```
246 |
247 | #### 怎样使用Modifier?
248 |
249 | * 1、Modifier不能离开Block和Element单独使用,如:
250 |
251 | ```html
252 |
256 |
261 |
262 |
263 |
268 | ```
269 |
270 | ### Mix
271 |
272 | * Mix描述的是一种同时使用多个不同的BEM来封装一个组件的技术!它能帮你解决如下问题:
273 |
274 | > 1、组合多个BEM的样式和外貌,而不需要复制代码;
275 |
276 | > 2、在建立这个新组件的同时其实你又建立了一个新的组件,以便以后复用。
277 |
278 | * 例子:
279 |
280 | ```html
281 |
282 |
289 | ```
290 |
291 | 在这里我们在'search-form'block中混合使用了'header'block的element'header__search-form',这样我们就能组合使用两者的样式了!其实这得益于我们在相关样式代码的时候,应该尽量保证代码各部分独立、通用!这样才能独立在其它block中调用另一个block中的样式代码!
292 |
293 | ### File structure
294 |
295 | * 其实类似BEM这种技术思想,我们也可以在构建项目文档目录结构的用到!套用到这里就是Block、Element、Modifier分别代表独立的文件!
296 |
297 | * 特点:
298 |
299 | > 1、一个Block就是一个目录;
300 |
301 | > 2、Block的名字就是目录的名字,比如'header'Block对应的目录就是'/header';
302 |
303 | > 3、Block由分开的文件组成,系统使用的过程中,再安装对应的模块,比如'header'Block由header.css和header.js等构成;
304 |
305 | > 4、Block所在的目录应该是它的Element和Modifier的根目录;
306 |
307 | > 5、在命名Element目录的时候应该用双下划线"__"开头,如"header/__logo";
308 |
309 | > 6、在命名Modifier目录的时候应该用单下划线"_"开头,如"header/_fixed"和"header/_theme_islands";
310 |
311 | > 7、同样Element和Modifier由不同的文件组成,如:"header__logo.js"和"header_theme_islands.css"。
312 |
313 |
314 | * 例子:
315 |
316 |
317 |
318 | search-form/ # Directory of the search-form
319 |
320 | __input/ # Subdirectory of the search-form__input
321 | search-form__input.css # CSS implementation of the
322 | # search-form__input element
323 | search-form__input.js # JavaScript implementation of the
324 | # search-form__input element
325 |
326 | __button/ # Subdirectory of the search-form__button
327 | # element
328 | search-form__button.css
329 | search-form__button.js
330 |
331 | _theme/ # Subdirectory of the search-form_theme
332 | # modifier
333 | search-form_theme_islands.css # CSS implementation of the search-form block
334 | # that has the theme modifier with the value
335 | # islands
336 | search-form_theme_lite.css # CSS implementation of the search-form block
337 | # that has the theme modifier with the value
338 | # lite
339 |
340 | search-form.css # CSS implementation of the search-form block
341 | search-form.js # JavaScript implementation of the
342 | # search-form block
343 |
344 |
345 |
346 | 这样的文件结构使得将来很容易复用代码!
347 |
348 | * 当然了,你也可以根据需要使用以下两种使用BEM规则的文件结构:
349 |
350 | > [Flat](https://en.bem.info/methodology/filestructure/#flat)
351 |
352 | > [Flex](https://en.bem.info/methodology/filestructure/#flex)
353 |
354 | ### 完结………………
--------------------------------------------------------------------------------
/10/readme.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | * 本案例带领大家一起认识微信JSSDK,带实例demo!集合自己的工作经历,争取用最简单的话描述一下怎么用微信JSSDK在微信端开发web!
4 |
5 | * 当然了,本着把本复杂的事情简单化的道理,这篇文档不会大刀阔斧的介绍底层原理,只会告知怎么调用相关接口以及怎么利用后台返回的信息配置公众号。这篇文档实现了分享、隐藏菜单栏等功能,其他功能我相信原理是一样的,调用相关接口就好!**如果文中有表达的不到位的地方,欢迎[指正](https://github.com/woai30231/webDevDetails/issues),因为确实本人经验有限**
6 |
7 | * 需要准备什么:1、了解微信JSSDK说明文档,俗话说“工欲善其事,必先利其器”说的就是这个道理,所以你先了解下它的相关接口;2、下载微信web开发者工具,这个主要是用来调试的,它可以很好的把调用js某些api后出现的情况打印在控制台!
8 |
9 | * 相关链接地址:[微信JSSDK说明文档](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E5.9B.9B.EF.BC.9A.E9.80.9A.E8.BF.87ready.E6.8E.A5.E5.8F.A3.E5.A4.84.E7.90.86.E6.88.90.E5.8A.9F.E9.AA.8C.E8.AF.81) [微信web开发者工具](https://mp.weixin.qq.com/wiki/10/e5f772f4521da17fa0d7304f68b97d7e.html)
10 |
11 | ## 开始
12 |
13 | * 很多在微信里面的web页面都有的功能就是分享当前页面到到朋友圈、qq微博等,我们就实现这样一个功能!1、把当前页面分享出去!2、最后再来隐藏相关菜单!整个前台代码在[这里](https://github.com/woai30231/webDevDetails/blob/master/10/demo.html)
14 |
15 | * 老规矩,我们先把前台代码写好吧!代码(js部分)如下(需要说明:首先你需要引入微信js接口包,其实我们用jquery获取后台返回的公众号配置信息,所以还需要引入jquery.js):
16 |
17 | ```html
18 |
27 | ```
28 |
29 | 我们看下后台的代码:
30 |
31 |
32 | ```php
33 | getSignPackage($share_url[0]);
44 | $shareData = array(
45 | 'share_app_url'=>WxShare::SHARE_APP_URL,
46 | 'share_app_title' => WxShare::SHARE_APP_TITLE,
47 | 'share_app_imgurl' => WxShare::SHARE_APP_IMGURL,
48 | 'share_app_desc' => WxShare::SHARE_APP_DESC
49 | );
50 | if($signPackage) {
51 | //向前台返回一个json对象
52 | echo json_encode(array('message'=>'js签名成功','data'=>$signPackage, 'shareData'=>$shareData,'code'=>0));
53 | } else {
54 | echo json_encode(array('message'=>'js签名失败','data'=>array(),'code'=>-1));
55 | }
56 | exit();
57 | ?>
58 | ```
59 |
60 | 此时我们打开控制台,大概能看到如下信息,截图如下:
61 |
62 | 
63 |
64 | * 此时我们得到后台返回的公众号配置信息,我们必须现在配置相关权限,否则我们无法调用相关接口,配置接口为config方法,在我们引入微信js包的时候,全局引入了一个wx全局变量,接口config就是wx的一个方法,好,我们改一下js前台代码,如下:
65 |
66 | ```html
67 |
68 |
116 |
117 | ```
118 | 此时我们打开微信web开发者工具控制,会打开看到这样一个信息,截图如下:
119 |
120 | 
121 |
122 | * 到这里我们我们就可以使用上面列出的相关接口了,现在再来介绍wx对象的两个方法:ready和error,它们两个都是异步的,都是在config权限之后才会触发,如果ready方法会在config之后执行,而error方法会在config验证失败之后执行,比如生成签名过期什么的,反正你只要记住你验证失败了就会触发error方法!其实这个ready方法有点像jquery的$(document).ready方法,它就是保证你比如不会点击一些还没有绘制在页面上的元素以免报错!
123 |
124 | * 说下怎么调用wx.ready():比如你想在页面初始化的时候就执行一些操作,那么这样的操作必须放在wx.ready()方法里面执行,因为权限还没有验证通过你就调用那些方法要报错,当然了,如果是那种需要用户主动触发相关操作采用调用的接口可以不放在wx.ready()里面调用,比如点击页面上的一个按钮分享该页面的时候!不过我的推荐是,万无一失的做法就是所有接口都放在wx.ready()里面就不会出现问题了,就好像jquery里面的代码任何时候写在$(function(){})里面都不会出现问题!
125 |
126 | * 再说下怎么调用相关接口,微信里面的相关接口都是作为wx这个全局对象的方法存在的,所有要调用某个接口的时候,直接调用该方法即可!比如,要调用右上角菜单按钮操作,可以像下面这样调用:
127 |
128 | ```javascript
129 | wx.hideMenuItems({
130 | menuList:[
131 | //要隐藏的菜单列表
132 | ]
133 | });
134 | ```
135 |
136 | * 好了,我们改一下我们的前台代码实现分享到朋友圈、qq、微博等操作。代码如下:
137 |
138 | ```html
139 |
258 | ```
259 |
260 | 此时,我们发现当我们点击微信app右上角按钮执行相关分享操作的时候,就能正常分享到相关目的地!截图如下:
261 |
262 | 
263 |
264 | 到此我们第一步的需求算是完成了,我们需要实现隐藏右上角相关菜单按钮,**注:别问为什么开启分享,现在又要隐藏菜单按钮,一切一切的只是了为了演示操作!**在执行隐藏按钮之前,我们先看下现在右上角菜单是什么情况,截图如下:
265 |
266 | 
267 |
268 | 从截图我们可以得知,右上角按钮点开会出现一些按钮,什么分享按钮什么的。好我们现在就来实现隐藏它们,ok,一切一切的只需要调用一个接口,修改前台代码如下:
269 |
270 | ```html
271 |
272 |
411 | ```
412 |
413 | ok,到此,我们隐藏菜单按钮也能正常执行了,打开微信web开发者工具控制台,截图如下:
414 |
415 | 
416 |
417 | 
418 |
419 | 好了,我们的一个简单的关于jssdk使用的示例到这里就算完成了,总结了一下:要想调用微信里面的api,除了要引用微信的JSSDK以外,还要做相应的接口使用配置权限!
420 |
421 | ### 写的不对的地方后续更新……
422 |
--------------------------------------------------------------------------------