缘来我们在这里

乱世天狼的博客


  • 首页

  • 归档

dva细探

发表于 2017-07-25

引子

所谓致知在格物者,言欲致吾之知,在其物而穷其理也。盖人心之灵莫不有知,而天下之物莫不有理,唯于理有未穷,故其知又不尽也,是以《大学》始教,必使学者即凡于天下之物,莫不因其己知之理而益穷之,以求至乎其极。至于用力之久,而一旦豁然贯通焉,则众物之表里精粗无不到,而吾心之全体大用无不明矣。此谓物格,此谓知之至也。

–《大学》

dva是什么?

dva 是一个基于 react 和 redux 的轻量应用框架,概念来自 elm,支持 side effects、热替换、动态加载、react-native、SSR 等,已在生产环境广泛应用。

前提概要


要有react,redux,react-router的基础,如果不具备,请自行到官网学习,过不去的话请自行翻墙,强烈建议去官网学习,否则道听途说,断章取义。react官网,redux官网,react-router官网

react-router-redux是什么

查看dva源码,index中赫然引用了react-router-redux,这到底是干嘛的? 名字上看起来是react-router和redux的结合。那么到github上看下吧。react-router-redux github地址。

这个库解决的问题:如果要回溯到之前的状态时,你重复触发之前的actions的时候,页面是不会跳转的,因为react-router只控制url的状态改变。所以和这个库让url状态和redux store同步。

这个库只在你关心actions的记录,持久化和重演的过程时有用,如果你不关心这些的话,可以不用这个库!就用redux和react-router就好

createDva做了什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
return function dva(hooks = {}) {
// history and initialState does not pass to plugin
const history = hooks.history || defaultHistory;
const initialState = hooks.initialState || {};
delete hooks.history;
delete hooks.initialState;

const plugin = new Plugin();
plugin.use(hooks);

const app = {
// properties
_models: [],
_router: null,
_store: null,
_history: null,
_plugin: plugin,
// methods
use,
model,
router,
start,
};
return app;
...(各种方法)

所以createDva返回了一个函数,函数接受hooks为参数,返回一个携带各种方法的app对象,那么这些方法到底用来做什么的呢?

方法

暂时延后方法描述,直接走使用环节

es6生成器(generator)详解

生成器使用function*和yield来简化迭代程序的编写。声明为function*的函数返回一个生成器实例。生成器是迭代器的子类型,继承了迭代器的 next 和 throw。这些特性可以使值反流回生成器,yield是一个返回值的表达式(或者使用throws)。

注:还可以使‘await’等类异步编程的代码生效,可以查看ES7的await建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}

for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}

生成器只能使用 typescript 类型语法描述

1
2
3
4
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}

Symbol 和 iterator(迭代器)详解

Symbol: 第七种基本类型(前六种:string,number,boolean,null,undefined,object)

Symbol的主要作用是用来生成一个不可能重复的key,保证程序向下兼容!(否则新的key不知道是否被使用过)

迭代器是一种为各种不同的数据结构提供统一的访问机制的接口。一个迭代器对象 ,知道如何每次访问集合中的一项, 并记录它的当前在序列中所在的位置。在 JavaScript 中 迭代器是一个对象,提供了一个 next() 方法,返回序列中的下一项。这个方法返回包含 done 和 value 两个属性的对象。

迭代器支持 for of

npm

发表于 2017-07-17

引子

子曰:“听讼,吾犹人也。必也使无讼乎!”无情者不得尽其辞。大畏民志,此谓知本。

此谓知本,此谓知之至也。

–《大学》

什么是npm

npm:node package manager。npm是js的一个包管理工具,可以让大家方便的共享和重用代码,可以方便的更新自己共享的代码。包含三种含义,网站,登记点(包含人们共享包的信息的巨大数据库),客户端(下载到本地的客户端)。

下载nodejs更新npm

  • 下载nodejs
  • 更新npm:npm install npm@latest -g
  • 手工更新npm:可以手动这样下载npm https://registry.npmjs.org/npm/-/npm-{VERSION}.tgz.

治理权限问题

全局安装包裹的时候有可能会遇到权限问题,这表明你对npm用来储存全局包和命令的文件夹没有写权限

你可以使用三种方法解决这个问题:

  1. 改变npm默认文件夹权限
  2. 更换npm默认文件夹
  3. 使用包管理器,帮你解决

改变npm默认文件夹权限

1
sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

更换npm默认文件夹

1
2
3
4
1. mkdir ~/.npm-global
2. npm config set prefix '~/.npm-global'
3. export PATH=~/.npm-global/bin:$PATH
4. source ~/.profile

使用包管理器,帮你解决

如果你是在mac上第一次下载node,你可以使用homebrew避免这个问题。homebrew会帮你设置为正确的权限

1
brew install node

本地安装包

  • 安装:npm install <package_name>
  • 版本:如果没有package.json,安装最新版本。如果有,按package.json中版本号安装
  • 使用:安装完成后可以使用require直接使用

使用package.json

如你所知,用package.json来管理npm包是最好的方法

  • 表明你工程中所使用包的文档
  • 可以使用语义化规则控制版本号
  • 可以开发可复制的项目,这意味着我们可以很方便的和其他开发者共享

package.json必要字段

最简情况,package.json需要以下字段

  • “name”
    • 全部小写
    • 一个单词,没有空格
    • 可以使用破折号和下划线
  • “version”
    • 使用x.x.x模式
    • 使用语义化规则
      例子:
      1
      2
      3
      4
      {
      "name": "my-awesome-package",
      "version": "1.0.0"
      }

创建package.json

npm init

–yes或-y标志用于快速建立一个默认的package.json,默认如下

  • name: 目前目录名
  • version: 1.0.0
  • description: readme的信息或者空字符串
  • main: index.js
  • scripts: 空的测试脚本
  • keywords: 空
  • author: 空
  • license: ISC
  • bugs: 当前目录,如果有的话
  • homepage: 当前目录,如果有的话

npm set init.author.email “wombat@npmjs.com“
npm set init.author.name “ag_dubs”
npm set init.license “MIT”

注意:如果没有description字段,npm使用readme.md中的第一行。这个描述有助于人们更好的找到你的npm库

定制init流程

npm首先会到home文件夹下面去寻找配置文件。~/.npm-init.js。

一个简单的.npm-init.js文件如下:

1
2
3
4
module.exports = {
customField: 'Custom Field',
otherCustomField: 'This field is really cool'
}

在home文件下如果放置了此文件,运行npm init之后,会生成

1
2
3
4
{
customField: 'Custom Field',
otherCustomField: 'This field is really cool'
}

同时可以使用prompt函数定制问题

1
module.exports = prompt("what's your favorite flavor of ice cream buddy?", "I LIKE THEM ALL");

更多的定制工作,请参考文档

具体指明引入的包

  • “dependencies”: 正式发布时使用的包
  • “devDependencies”: 开发和测试时使用的包

手工编辑你的package.json

–save dependencies
–save-dev devDependencies

管理依赖版本

使用npm install会下载满足语义化规则的最新版本的依赖

更新本地包

在package.json同一目录运行npm update 更新

使用npm outdated来测试更新是否成功

卸载本地包

npm uninstall lodash

npm uninstall –save lodash

npm uninstall –save-dev lodash

全局安装包

一般来说,命令工具通常可以选择放到全局,模块包通常放到本地
npm install -g jshint

更新全局包

npm update -g jshint

找到那些包需要更新: npm outdated -g –depth=0

npm 2.6.1版本之前,这个脚本会更新掉所有过期的包

卸载全局包

npm uninstall -g jshint

制作Node.js模块

Node.js模块是一种可以发布到npm上面去的包,可以从创建package.json文件开始,创建模块。

可以使用npm init生成package.json。它会引导你填写一些信息。两个必填字段是version和name。当然需要一个入口main,可以使用默认的index.js

如果你想要在author域添加信息,你可以使用下面的格式(email和网站都是可选的)

名字<邮件地址>(网站地址)

一旦你的package.json文件创建了,你会想要创建当模块被require的时候,被加载的文件。如果你想用设置,那么就是index.js

在入口文件,添加一个函数作为 exports对象的属性,这会让这个函数可以被其他代码调用

1
2
3
exports.printMsg = function() {
console.log('This is a message from the demo package');
}

测试:

  1. 发布你的包
  2. 在你的工程外面创建一个新的目录并进去
  3. 运行命令 npm install 你的包名
  4. 创建一个test.js文件,加载你的模块,并调用模块中的函数
  5. 运行node test.js。信息应该会被输出

    发布npm包

    你可以在任意含有package.json文件的目录中发布。

    语义化版本控制和npm

发布的语义化

如果一个工程要和其他人共享,开始的版本号应该是1.0.0,虽然有些npm工程不符合这个规则。

在此之后,改动应该遵守如下规则:

  • bug修复和其他小的改动:补丁发布,增加版本号最后一位,例如:1.0.1
  • 新特性但是不影响旧的特性:较小发布,增加版本号中间一位,例如:1.1.0
  • 改动不向后兼容:主要发布,增加版本号中的第一位,例如:2.0.0

使用域包

域类似于npm模块的命名空间。如果包的名字是以@开始的,那么它就是一个域包。域名就是在@和斜线之间的词。

@scope/project-name : 所有的npm用户拥有自己的域
@username/project-name: 深入了解域请看文档 CLI documentation

更新和登录npm

你需要版本号大于2.7.0的npm,而且第一次使用域npm模块需要登录

1
2
sudo npm install -g npm
npm login

初始化一个域包

创建一个域包,只需要简单的使用你的域名开始的字段命名name即可

1
2
3
{
"name": "@username/project-name"
}

如果使用npm init, 可以增加条件选项

1
npm init --scope=username

如果你一直使用相同的域配置,最好在.npmrc文件做一个配置

发布一个域包

域包一般是私有的,你想要发布的话,首先要成为一个私有模块用户

然而,公有的域模块是免费的。发布公有的域模块,要设置access选项,之后所有的发布默认携带此选项

1
npm publish --access=public

使用域包

和使用一般的包一样

使用标签

标签是语义化版本控制的一个补充,目的是标记不同版本的包。为了让人类更好读懂,标签可以使发布者有效的发布自己的包

增加标签

自己的特定版本包增加tag,使用

1
npm dist-tag add <pkg>@<version> [<tag>]

更多信息,请查看文档 cli docs

发布时携带标签

默认地,npm publish 会将你的包打上 latest 标签。如果你使用 –tag 标志,你可以选择使用其它标签。例如:

1
npm publish --tag beta

加载携带标签的包

同发布一样,默认使用 latest 标签。要覆盖这个默认行为,使用

1
npm install <pkg>@<tag>

例如:

1
npm install somepkg@beta

警告

因为dist-tag和semver使用相同的命名空间,要避免使用哪些容易导致冲突的标签名。最佳实践是避免使用以数字或v开头的标签

webpack源码阅读

发表于 2017-07-10

引子

天命之谓性;率性之谓道;修道之谓教;

道也者,不可须臾离也;可离,非道也。是故君子戒慎乎其所不睹,恐惧乎其所不闻。

莫见乎隐,莫显乎微。故君子慎其独也。

喜、怒、哀、乐之未发,谓之中。发而中节,谓之和。中也者,天下之大本也。和也者,天下之达道也。

致中和,天地位焉,万物育焉。

– 《中庸》

大纲

  1. 执行bin目录下的webpack.js脚本,解析命令行参数以及开始执行编译。
  2. 调用lib目录下的webpack.js文件的核心函数webpack,实例化一个Compiler,继承Tapable插件框架,实现注册和调用一系列插件。
  3. 调用lib目录下的/webpackOptionApply.js模块的process方法,使用各种各样的插件来逐一编译webpack编译对象的各项。
  4. 在3中调用的各种插件编译并输出新文件。

起步

入口文件:./bin/webpack.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
var path = require("path");//引入node path 模块
try {
var localWebpack = require.resolve(path.join(process.cwd(), "node_modules", "webpack", "bin", "webpack.js")); //解析webpack路径
if(__filename !== localWebpack) {
return require(localWebpack);
}
} catch(e) {} //这里是将webpack的版本定位到本地

var yargs = require("yargs")
.usage("webpack " + require("../package.json").version + "\n" +
"Usage: https://webpack.js.org/api/cli/\n" +
"Usage without config file: webpack <entry> [<entry>] <output>\n" +
"Usage with config file: webpack");
require("./config-yargs")(yargs);
var DISPLAY_GROUP = "Stats options:";
var BASIC_GROUP = "Basic options:";
yargs.options({
"json": {
type: "boolean",
alias: "j",
describe: "Prints the result as JSON."
},
"progress": {
type: "boolean",
describe: "Print compilation progress in percentage",
group: BASIC_GROUP
},
"color": {
type: "boolean",
alias: "colors",
default: function supportsColor() {
return require("supports-color");
},
group: DISPLAY_GROUP,
describe: "Enables/Disables colors on the console"
},
"sort-modules-by": {
type: "string",
group: DISPLAY_GROUP,
describe: "Sorts the modules list by property in module"
},
"sort-chunks-by": {
type: "string",
group: DISPLAY_GROUP,
describe: "Sorts the chunks list by property in chunk"
},
"sort-assets-by": {
type: "string",
group: DISPLAY_GROUP,
describe: "Sorts the assets list by property in asset"
},
"hide-modules": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Hides info about modules"
},
"display-exclude": {
type: "string",
group: DISPLAY_GROUP,
describe: "Exclude modules in the output"
},
"display-modules": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display even excluded modules in the output"
},
"display-max-modules": {
type: "number",
group: DISPLAY_GROUP,
describe: "Sets the maximum number of visible modules in output"
},
"display-chunks": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display chunks in the output"
},
"display-entrypoints": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display entry points in the output"
},
"display-origins": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display origins of chunks in the output"
},
"display-cached": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display also cached modules in the output"
},
"display-cached-assets": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display also cached assets in the output"
},
"display-reasons": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display reasons about module inclusion in the output"
},
"display-depth": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display distance from entry point for each module"
},
"display-used-exports": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display information about used exports in modules (Tree Shaking)"
},
"display-provided-exports": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display information about exports provided from modules"
},
"display-optimization-bailout": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display information about why optimization bailed out for modules"
},
"display-error-details": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display details about errors"
},
"display": {
type: "string",
group: DISPLAY_GROUP,
describe: "Select display preset (verbose, detailed, normal, minimal, errors-only, none)"
},
"verbose": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Show more details"
}
});

Protocol Buffers 基础介绍

发表于 2017-07-08

引子

《诗》云:“穆穆文王,於缉熙敬止!”为人君,止于仁;为人臣,止于敬;为人子,止于孝;为人父,止于慈;与国人交,止于信。

– 《大学》

Protocol Buffers概览

什么是Protocol Buffers

Protocol Buffers是谷歌开发的,跨语言跨平台的一种机制,目的是序列化结构化的数据(数据比xml更小,更快,更简单)。一旦你定义好了你想要的数据结构,那么就可以通过生成的代码方便的读和写你的结构化数据。这些结构化的数据可以从不同的数据流中读和写,并且支持使用不同的语言。你甚至可以在保证现有程序不被破坏的情况下,更新数据结构。

Protocol Buffers是怎么工作的

你具体在.proto文件中定义你想要的序列化的数据结构。每个proto buffer message 都是一条小而富有逻辑的信息记录,包含一些列的名值对。下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}

message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}

repeated PhoneNumber phone = 4;
}

一旦你定义好了你的messages,运行相应语言编译器,生成messages对应的classes。这些类为每一个字段提供一些基本的入口(就像name(),set_name())并且同时提供提供序列化/反序列化整个结构原生字节的方法。假设我们现在用的语言是C++,运行了编译器之后,会生成一个名为Person的类。你之后可以在你的应用中使用这些类来设置,序列化,和取回Person protocol buffer messages。然后你可以写类似如下的代码:

1
2
3
4
5
6
Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("jdoe@example.com");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);

之后,你可以取回你的信息:

1
2
3
4
5
fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

你可以在message模式上增加字段,这样并不会破坏背后的兼容性,旧的二进制仅仅会在转化的时候忽略掉新的字段,你可以拓展的协议,并且不用担心破坏现存代码。

注:完整的使用protocol buffer code的参考资料,protocol编码的原理

为什么不用xml

Protocol buffers在序列化结构数据上比xml有很多优势:

  • 更兼点
  • 大小是xml的1/10到1/3
  • 速度比xml快20到100倍
  • 歧义更少
  • 生成数据对应的类,方便程序调用
    举例:假设我们需要的一个拥有email和name的person模型。在XML中,你需要这样定义:
    1
    2
    3
    4
    <person>
    <name>John Doe</name>
    <email>jdoe@example.com</email>
    </person>

对应的protocol buffer message(protocol buffer 文本模式):

1
2
3
4
5
6
# Textual representation of a protocol buffer.
# This is *not* the binary format used on the wire.
person {
name: "John Doe"
email: "jdoe@example.com"
}

当这个message被编码为protocol buffer 二进制模式时(文本模式只是方便人们调试,编写和阅读的描述)后,仅仅只有28个字节并且转换只需要100到200纳秒。xml本本至少有69个字节,并且转换需要5000到10000纳秒。

操作一个protocol buffer更简便:

1
2
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

然而在xml中你要这样操作:

1
2
3
4
5
6
cout << "Name: "
<< person.getElementsByTagName("name")->item(0)->innerText()
<< endl;
cout << "E-mail: "
<< person.getElementsByTagName("email")->item(0)->innerText()
<< endl;

然而,protocol buffers并不总是强于xml:

  • protocol buffers并不适合描述一个文档模型(比如html),因为你不能直接插入带有文本的结构
  • xml人类可读且可编辑,protcol buffers原生模式下并不是
  • xml在某种程度是自解释的,protocol buffer只有在拥有了message定义(.proto 文件)之后才是具有意义的。

    如何开始

下载包,跟随指导

proto3介绍

proto3 简化了protocol buffer语言,不仅变得更好用,而且支持了更多的语言: Java, C++, Python, Java Lite, Ruby, JavaScript, Objective-C, and C#。同时你可以使用最新的Go protoc插件生成go的proto3代码,可以在golang/protobuf仓库中获取。

两个版本api并不兼容。

可以在release notes中查看两者不同之处。可以在proto3 指引中学习proto3语法。

注:谷歌开源的时候已经是第二版本了,所以默认为proto2

一点历史

Protocol buffers最开始是谷歌要处理request/response协议而研发的。在protocol buffer之前,存在一个requests和responses模式用来手工处理解析和反解析requests和responses,并且支持模式的不同版本。结果导致了丑陋的代码如下:

1
2
3
4
5
6
7
8
if (version == 3) {
...
} else if (version > 4) {
if (version == 5) {
...
}
...
}

明确格式化的协议会使新的协议版本首次使用变得很复杂,因为开发者必须确认在请求的发起者到处理请求的服务者之前所有的服务方都要可以迅速地切换到新的协议。

Protocol buffers被用来解决这些问题:

  • 新的字段可以很容易地被引入,中间服务层不会检查数据,可以简单的转换它和传递它,不需要知道所有的字段。
  • 这些格式完全是自解释的,可以被多种语言处理。

然而,用户依然要手写他们的转化代码。

随着系统的进化,它增加了一系列的特性和用途。

  • 自动生成序列化和反序列化的代码,避免了手工转换
  • 为了在短生命周期的rpc请求上使用,人们开始使用protocol buffers作为一个持久储存数据的好用的自解释格式。
  • 服务端的rpc接口开始成为protocol文件声明的一部分,用户可以使用服务接口的真实实现覆盖协议编译生成的根类。

Protocol buffers 现在已经成为书写数据的通用语言,目前有48,162个不同的message类型定义于12,183个不同的.proto文件中。它们不仅仅用于rpc系统,同时也用于在不同的存储系统上持续的存储数据。

微信开发大纲

发表于 2017-06-29

开发前必读提炼

OpenID: 单个公众号
UnionID: 多个公众号

注意事项:

  • 每个接口都有每日接口调用频次限制,可以在公众平台官网-开发者中心处查看具体频次。
  • 公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。

会话:

  • 群发消息:公众号可以以一定频次(订阅号为每天1次,服务号为每月4次),向用户群发消息,包括文字消息、图文消息、图片、视频、语音等

  • 被动回复消息:在用户给公众号发消息后,微信服务器会将消息发到开发者预先在开发者中心设置的服务器地址(开发者需要进行消息真实性验证),公众号可以在5秒内做出回复,可以回复一个消息,也可以回复命令告诉微信服务器这条消息暂不回复。被动回复消息可以设置加密(在公众平台官网的开发者中心处设置,设置后,按照消息加解密文档来进行处理。其他3种消息的调用因为是API调用而不是对请求的返回,所以不需要加解密)。

  • 客服消息:在用户给公众号发消息后的48小时内,公众号可以给用户发送不限数量的消息,主要用于客服场景。用户的行为会触发事件推送,某些事件推送是支持公众号据此发送客服消息的,详见微信推送消息与事件说明文档。

  • 模板消息:在需要对用户发送服务通知(如刷卡提醒、服务预约成功通知等)时,公众号可以用特定内容模板,主动向用户发送消息。

公众号内的网页

  1. 网页授权获取用户基本信息:通过该接口,可以获取用户的基本信息(获取用户的OpenID是无需用户同意的,获取用户的基本信息则需用户同意)
  2. 微信JS-SDK:是开发者在网页上通过JavaScript代码使用微信原生功能的工具包,开发者可以使用它在网页上录制和播放微信语音、监听微信分享、上传手机本地图片、拍照等许多能力。

开发者规范

  • 涉及用户数据时:
    您的服务需要收集用户任何数据的,必须事先获得用户的明确同意,且仅应当收集为运营及功能实现目的而必要的用户数据, 同时应当告知用户相关数据收集的目的、范围及使用方式等,保障用户知情权;
    您收集用户的数据后,必须采取必要的保护措施,防止用户数据被盗、泄漏等;
    您收集用户的数据后,必须采取必要的保护措施,防止用户数据被盗、泄漏等;
    您在特定微信公众号中收集的用户数据仅可以在该特定微信公众号中使用,不得将其使用在该特定微信公众号之外或为其他任何目的进行使用,也不得以任何方式将其提供给他人;
    如果腾讯认为您收集、使用用户数据的方式,可能损害用户体验,腾讯有权要求您删除相关数据并不得再以该方式收集、使用用户数据;
    一旦您停止使用本服务,或腾讯基于任何原因终止您使用本服务,您必须立即删除全部因使用本服务而获得的数据(包括各种备份), 且不得再以任何方式进行使用;
  • 其他规范
    请勿为任何用户自动登录到微信公众平台提供代理身份验证凭据;
    请勿提供跟踪功能,包括但不限于识别其他用户在个人主页上查看、点击等操作行为;
    请勿自动将浏览器窗口定向到其他网页;
    请勿设置或发布任何违反相关法规、公序良俗、社会公德等的玩法、内容等;
    请勿公开表达或暗示,您与腾讯之间存在合作关系,包括但不限于相互持股、商业往来或合作关系等,或声称腾讯对您的认可;

  • 公众号接口权限说明

接口调用频次限制说明

公众号调用接口并不是无限制的。为了防止公众号的程序错误而引发微信服务器负载异常,默认情况下,每个公众号调用接口都不能超过一定限制,当超过一定限制时,调用对应接口会收到如下错误返回码:

1
{"errcode":45009,"errmsg":"api freq out of limit"}

开发者可以登录微信公众平台,在帐号后台开发者中心接口权限模板查看帐号各接口当前的日调用上限和实时调用量,对于认证帐号可以对实时调用量清零,说明如下:

  1. 由于指标计算方法或统计时间差异,实时调用量数据可能会出现误差,一般在1%以内。
  2. 每个帐号每月共10次清零操作机会,清零生效一次即用掉一次机会(10次包括了平台上的清零和调用接口API的清零)。
  3. 第三方帮助公众号调用时,实际上是在消耗公众号自身的quota。
  4. 每个有接口调用限额的接口都可以进行清零操作。

各接口频次

全局返回码说明

基本概念

发表于 2017-06-27

引子

汤之盘铭曰:“苟日新,日日新,又日新。”《康诰》曰:“作新民。”《诗》曰:“周虽旧邦,其命维新。”是故君子无所不用其极。

–《大学》

语法

  • 区分大小写:变量名区分大小写
  • 标识符
    • 第一个字母必须是:字母,下划线,或美元符号($)
    • 其他字符可以是字母、下划线、美元符号或数字
    • 标识符中也可以包含扩展的ACII字符或Unicode字符,但我们不推荐这么做,惯例上ECMAScript标识符采用驼峰大小写格式
  • 注释:采用c风格注释
    • 单行:// 注释内容
    • 多行:/ 注释内容 /
  • 严格模式:严格模式为javascript定义了一种不同的解析和执行模型。在严格模式下,ECMAScript3中的一些不确定行为将得到处理,而且某些不安全的操作也会抛出错误。整个脚本启用严格模式,可以在顶部加上代码:’use strict’;也可指定某函数:function doSomething(){‘use strict’ //函数体}
  • 语句:ECMAScript中语句以一个分号为结尾;省略分号,则由解析器确定语句的末尾。

关键字和保留字

ECMA-262关键字:

break,do,instanceof,typeof,case,else,new,var,catch,finally,return,void,continue,for,switch,while,debugger,
function,this,with,default,if,throw,delete,in,try

ECMA-262保留字:

abstract,enum,int,short,boolean,export,interface,static,byte,extends,long,super,char,final,native,synchronized,class,float,package,throws,const,goto,private,transient,implements,protected,volatile,double,import,public

第五版非严格模式保留字:

class,enum,extends,super,const,export,import

第五版严格模式下增加的保留字:

implements,package,public,interface,private,static,let,protected,yield

关键字和保留字虽然不能用做标识符,但是可以作为属性名使用,但不推荐。

除上述关键字和保留字,第五版还对eval和arguments做了限制,在严格模式下,这两个名字也不能作为标识符或属性名,否则会抛出错误。

变量

  • 定义:var message
  • 松散类型
  • 使用var定义的变量将成为定义该变量作用域内的局部变量
  • 省去var可以定义全局变量

数据类型

5种简单数据类型

  • undefined
  • null
  • Boolean
  • Number
  • String

一种复杂数据类型

  • Object

typeof操作符

  • undefined 此值未定义
  • boolean 此值为bool值
  • string 此值为字符串
  • number 此值为数值
  • object 此值为对象
  • function 此值为函数
  • typeof null == ‘object’

Undefined类型

Undefined类型只有一个值,undefined

包含undefined值的变量与尚未定义的变量是不一样的,尚未定义的变量直接调用会报错。

对尚未声明的变量只能执行一项操作,即使用typeof操作符检测其数据类型(对未经声明的变量调用delete不会导致错误,但这样做没有什么意义,而且严格模式下确实会导致错误)

尚未声明的变量调用typeof返回undefined

Null类型

Null类型只有一个值,null

从逻辑上讲,null值表示一个空对象指针

如果变量待保存的是对象,初始化的时候就应该明确的让变量保存null值

Boolean类型

Boolean类型只有两个值,true和false

要将一个值转换为其对应的Boolean值,可以调用函数Boolean()

转换规则(除Boolean类型本身):

  • String类型,true - 任何非空字符串;false - 空字符串;
  • Number类型,true - 任何非0数字值;false - 0和NaN;
  • Object类型,true - 任何对象;false - null;
  • Undefined类型,undefined - false

Number类型

数值字面量格式有十进制,八进制和十六进制

八进制第一位必须是0,然后是八进制数字序列(0-7)。如果数字序列中的值超出范围,那么前导0将会被忽略,后面数字将被当作十进制解析,八进制在严格模式下是不生效的,会导致javascript引擎抛除错误。

十六进制字面值的前两位必须是0x,后跟任何十六进制数字(0-9,A-F),字母可小写。

进行算数计算的时候,所有八进制和十六进制数字都将被转换为十进制。

  1. 浮点数值:所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后面至少要有一位数字。虽然小数点前面可以没有整数,但是不推荐。

    • 由于保存浮点数值需要的内存空间是保存整数数值的两倍,ECMAScript会不失时机地将浮点数转换为整数
    • 极大极小值,使用科学计数法,也叫e表示法。用e表示法表示的数值等于e前面的数值乘以10的指数次幂
    • 浮点数的最高精度为17位小数,但在进行算术计算时其精确度远远不如整数。0.1 + 0.2 = 0.30000000000000004(中间是15个0)
  2. 数值范围:由于内存限制,ECMAScript并不能保存世界上的所有数值。

    • 最小值:Number.MIN_VALUE == 5e-324
    • 最大值:Number.MAX_VALUE == 1.7976931348623157e+308
    • 正无穷:超出最大值,Number.POSITIVE_INFINITY == Infinity
    • 负无穷:超出最小值,Number.NEGATIVE_INFINITY == -Infinity
    • isFinite:确定数值是否是无穷的,无穷返回false,有穷返回true
  3. NaN:这个数值表示一个本来要返回数值的操作未返回数值的情况

    • 任何数值除以0返回NaN
    • 任何涉及NaN的操作返回NaN
    • NaN与任何值不等,包括自身
    • isNaN函数:函数接收一个值会尝试将这个值直接转换为数值,任何不能转换为数值的值都会导致这个函数返回true
    • isNaN也适用于对象,在基于对象调用isNaN函数时,会先调用对象的valueof方法,看此返回值能否转化为数值,如果不能,则基于这个返回值再调用toString方法,再测试返回值。这个过程同时也是ECMAScript中内置函数和操作符的一般执行流程。
  4. 数值转换:Number(), parseInt(), parseFLoat()

    • Number:用于任何数据类型
      • Boolean:true&false - 1&0
      • Number:返回本身
      • Null:返回0
      • undefine:返回NaN
      • 如果是字符串,遵循规则:
        • 如果字符串中只包含数字(包含符号),将其转换为十进制数字值
        • 如果字符串中包含有效的浮点格式,则将其转换为对应的浮点数值
        • 如果字符串中包含有效的十六进制格式数值,则将其转换为相同大小的十进制数值
        • 如果字符串是空的,则将其转换为0
        • 如果字符串中包含除上述格式之外的字符,则将其转换为NaN
      • 如果是对象,则调用其valueOf()方法,然后依照之前规则转换。如果转换为NaN,则调用对象toString()方法,然后再依照前面得规则转换
    • parseInt:更多的看其是否符合数值模式
      • 忽略字符串前面空格
      • 第一个字符不是数字或者正负号,返回NaN
      • 解析在遇到非数字字符结束
      • 可以识别不同格式(5中八进制被忽略)
      • 第二个参数可以传基数
    • parseFloat:主要特点是第一个小数点有效
      • 第一个小数点有效
      • 始终忽略前导0,没有第二个参数传基数的用法,始终只解析十进制
      • 如果能解析为整数,返回整数

在HTML中使用javascript

发表于 2017-06-26

引子

《康诰》曰:“克明德。”《太甲》曰:“顾諟天之明命。”《帝典》曰:“克明峻徳。”皆自明也。

–《大学》

script元素

  • async:表示应该立即下载脚本,但不阻塞页面其余caozuo
  • charset:可选,指定src属性引入代码的字符集,大多数浏览器会忽略,所以很少人使用。
  • defer:可选,表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本有效。IE7及之前版本,对嵌入脚本也生效。
  • language:已废弃
  • src:可选,表示包含要执行代码的外部文件
  • type:可选,可看作language属性的替代属性,表示编写代码使用脚本语言的内容类型(也称为MIME类型)

标签的位置

惯例是应该放在head元素中间,但这会导致script标签加载完之后,才会加载DOM内容,加载页面时会发生明显的延时。为了解决这一问题,一般把script标签防盗内容后面

延迟脚本

defer属性告诉浏览器立即加载,但延迟执行!
如果有两个defer属性脚本,延迟脚本不一定会按顺序执行,也不一定会在DOMContentLoaded事件触发前执行,因此,最好

异步脚本

async属性高诉浏览器立即下载,但不保证执行的先后顺序。一定会在页面的load事件之前执行,但可能在DOMContentLoaded事件之前或者之后执行

在XHTML中的使用

1
2
3
4
5
6
7
8
<script type='text/javascript'>
// <![CDATA[
if(a < b){
alert('a is less than b')
}
// ]]>
</script>
这样是为了保证所有现代浏览器都可以正常使用

不推荐使用的方法

不要用html注释注释脚本!

嵌入代码与外部文件

  • 可维护性:所有js文件放到同一个文件夹中
  • 可缓存:浏览器根据设置缓存链接的所有js文件,也就是说如果两个页面使用同一个文件,那么这个文件只需下载一次。因此最终结果就是能够加快页面加载的速度。
  • 适应未来:通过外部文件来包含的javascript无须使用前面说过的hack技术,html和xhtml包含外部文件的语法是想通的

文档模式

混杂模式:混杂模式会让IE的行为和ie5一致
标准模式:标准模式会让IE的行为更接近标准行为
准标准模式:很多行为符合标准,不标准的地方体现在处理突变间隙的时候

标准模式开启方法:

1
2
3
4
5
6
7
html4.01 严格型
<!DOCTYPE HTML PUBLIC "-//w3c//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

xhtml1.0 严格型
<!DOCTYPE HTML PUBLIC "-//w3c//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

准标准模式开启可以使用过渡型(transitional)或框架集型(framset)文档类型来触发

noscript元素

noscript包裹的元素只有在下列条件下才会显示

  • 浏览器不支持脚本
  • 浏览器支持脚本,但脚本被禁用

javascript简介 - 第一章

发表于 2017-06-26

引子

知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得。物有本末,事有终始。知所先后,则近道矣!

– 大学

javascript简史

诞生于1995年,1997年ECMA(欧洲计算机制造商协会)制定ECMA-262标准

javascript实现

  • 核心:ECMAScript
  • 文档对象模型:DOM
  • 浏览器对象模型:BOM

ECMAScript

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 对象

文档对象模型(DOM)

DOM1: DOM核型(DOMcore)和DOM HTML。DOM核心规定的是如何映射基于XML的文档结构,以便简化对文档中任意部分的访问和操作;DOM HTML则在DOM核心的基础上加以扩展,添加了针对HTML的对象和方法。

DOM2: DOM2级在原来的DOM的基础上又扩充了(DHTML一直都支持)鼠标和用户界面事件,范围,遍历(迭代DOM文档的方法)等细分模块,而且通过对象接口增加了对css的支持。

DOM2新增模块
  • DOM视图(DOM Views):定义了跟踪不同文档(定义了跟踪不同文档,应用css之前,应用css之后)视图的接口
  • DOM事件(DOM Events):定义了事件和事件处理的接口
  • DOM样式(DOM Style):定义了基于css为元素应用样式的接口
  • DOM范围和遍历(DOM Travalsal and Range):定义了遍历和操作文档的接口
DOM3模块
  • DOM加载和保存模块(DOM Load and Save):统一方式加载和保存文档的方法
  • DOM验证模块(DOM Validation):验证文档的方法
其他DOM标准
  • SVG1.0
  • MathML1.0
  • SMIL

浏览器对象模型(BOM)

  • 弹出新浏览器窗口的功能
  • 移动、缩放和关闭浏览器窗口的功能
  • 提供浏览器详细信息的navigator对象
  • 提供浏览器所加载页面详细信息的location对象
  • 提供用户分辨率详细信息的screen对象
  • 对cookie的支持
  • 像XMLHttpRequest和ActionXObject这样的自定义对象

css重点

发表于 2017-06-26

引子

所谓致知在格物者,言欲致吾之知,在其物而穷其理也。盖人心之灵莫不有知,而天下之物莫不有理,唯于理有未穷,故其知又不尽也,是以《大学》始教,必使学者即凡于天下之物,莫不因其己知之理而益穷之,以求至乎其极。至于用力之久,而一旦豁然贯通焉,则众物之表里精粗无不到,而吾心之全体大用无不明矣。此谓物格,此谓知之至也。

–《大学》

BFC是什么

在解释 BFC 是什么之前,需要先介绍 Box、Formatting Context的概念。

Box: CSS布局的基本单位

Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:

  • block-level box:display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context;
  • inline-level box:display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;
  • run-in box: css3 中才有, 这儿先不讲了。

Formatting context

Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)。

CSS2.1 中只有 BFC 和 IFC, CSS3 中还增加了 GFC 和 FFC。

BFC 定义

BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。

BFC布局规则:

  • 内部的Box会在垂直方向,一个接一个地放置。
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  • 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  • BFC的区域不会与float box重叠。
  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此
  • 计算BFC的高度时,浮动元素也参与计算

哪些元素会生成BFC?

  • 跟元素
  • float不为none
  • position为absolute或fixed
  • display为inline-block, table-cell, table-caption, flex, inline-flex
  • overflow不为visible

BFC的作用及原理

  1. 自适应两栏布局
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <style>
    body {
    width: 300px;
    position: relative;
    }

    .aside {
    width: 100px;
    height: 150px;
    float: left;
    background: #f66;
    }

    .main {
    height: 200px;
    background: #fcc;
    }
    </style>
    <body>
    <div class="aside"></div>
    <div class="main"></div>
    </body>

页面:

页面

根据BFC布局规则第3条:

1
每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

因此,虽然存在浮动的元素aslide,但main的左边依然会与包含块的左边相接触。

根据BFC布局规则第四条:

1
BFC的区域不会与float box重叠。

我们可以通过通过触发main生成BFC, 来实现自适应两栏布局。

1
2
3
.main {
overflow: hidden;
}

当触发main生成BFC后,这个新的BFC不会与浮动的aside重叠。因此会根据包含块的宽度,和aside的宽度,自动变窄。效果如下:

页面

  1. 清除浮动
  2. 防止垂直margin重叠

微服务流程以及带来的前端工程化思考

发表于 2017-06-24

引子

此谓诚於中,形於外,故君子必慎其独也。

– 礼记·大学

前情概要

传统的 web 开发并没有前后分离的概念,无论是 c#,还是 java,抑或是世界上最好的语言 PHP。大多是采用 cshtml,或者 jsp 作为模版,当作 view 层。这时候的前端扮演的角色很有限,通常工作流是前端根据设计还原出页面(html)。然后使用 js 制作动态效果或者使用 ajax 做少量的异步刷新。 那么带来的问题是:

  •  后端同学不仅要关注数据问题,同时还要关注渲染问题
  • 前端同学和后端同学很难完全同步开发
  •  出现问题不容易迅速归责,而且前端问题,即使前端修改了,后端必须同时修改,浪费人力
  •  每次跳转都要向后端发送整个页面的请求,即使两个页面差别不大

鉴于出现上述问题,我们  出于工程化目的,需要前后分离。随着前端的逐渐发展,打包工具不断更新,react,vue 等  优秀框架逐渐完善。前端已经成熟到可以工程化了。 这时候前后分离已经不仅仅是一种设想了,同时是可以在实际中运用的工程化操作。

思路:前端  后端通过一个协议规范定义方法,参数类型,返回类型。协商制定完成协议后,前端根据协议自主使用 mock 数据开发前端视图和交互逻辑。后端根据协议自主实现协议规定的接口定义。最后前端+后端一起  联调 ,联调后交付测试

 沟通的规范选择

最开始我们选择的是thrift,thrift 文件  示例。

thrift 非常强大,但是原生不支持 go 语言(我们后端使用 go 语言)。

所以我们选用了proto-buffers

两者都属于 IDL 语言(接口定义语言)。两者各有优缺点,thrift 比较成熟,但是文档  不完善。proto-buffers 文档齐全,底层采用二进制,文件更小。

两者具体的比较

ok, 那么一个服务的定义长什么样呢?举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
syntax = "proto3";
package test;

service Sample {
rpc Ping(SamplePing) returns (SamplePingResp);
}

// ---------------------------------------------------------------------------

message SamplePing {
string Msg = 1;
}

message SamplePingResp {
string Msg = 1;
}

ok,这个例子很重要,我们之后会回来再看这个例子。

后端服务生成套路

我们采用的是 rpc 协议,至于 rpc 和 restful 之争,可以参考 这里

我们暂时假设上述示例要完成的是一个  名为 test 的服务,那么我们可以通过 域名/test.Sample/Ping来语义化的定义我们  的服务 uri。ok,到了这里,我们前端同学会忽略掉你所有的代码逻辑,只关注于你在 域名/test.Sample/Ping上面实现的服务是否和定义一致。

ok,接下来是实现定义好的方法,那怎么做才能更好的把控服务性能并且减少开发时间呢?

减少开发时间,提升开发效率

goflow

什么是 goflow,goflow 做了什么?

goflow 准确的说是一整套开发工具,提供的是命令行街口,可以通过命令行方便的创建服务,启动服务,提交 mr 等;同时 goflow 还将常用的包进行  统一的版本管理。总之,goflow 提供的是更舒服的开发体验。

ezorm

 我们通常有可能采用不同的数据哭,比如 mysql,mongo。那么每次都写访问数据  库的代码是重复并且没有必要的。所以 ezorm 可以自动生成数据库调用。ezorm 文档传送门

所以有了 goflow 和 ezorm,程序员可以专注于业务实现!

控制服务性能,进行服务监控

consul

 什么是 consul?consul 有什么用?

consul是一个服务发现机制 ,主要用来做服务发现。

为什么需要服务发现?

当我们的服务变得多了起来的时候,去监控每个服务是否正常服务,并在异常时进行报警是非常重要的!

性能实时监控

grafana

grafana是一个开源的性能分析和监控工具,界面很酷。

kibana

kibana开源的,基于 Elasticsearch 的统计工具

错误日志

sentry

sentry是一个  实时日志平台,方便及时接受错误并报警。

 前端套路

api 调用

由于 protobuf 并没有支持直接生成前端调用(有 nodejs 的),所以目前前端是自己解析 protobuf 并且生成调用

渲染技术

 通过对比 react 和 vue 我们最终选择的 react,因为考虑到生态  链还有维护人, 我们有理由相信 react 会更稳定一点。

接下来要做的事

通过参考后端的套路,我不禁在思索前端是否也可以进行  一套工具的开发?

jsflow

要达成目的:对公司内部  依赖版本进行统一管理,新建项目时,自动生成 webpack 配置,基本项目结构。通过命令行自动生成组件基本结构。

jsorm

根据定义生成基本的 api 调用,异步 action,异步 action 对应 reducer。

pageGen

如果仅仅是后台应用(增删改查),可以通过 yaml 配置,直接生成页面!

以上,jsflow,jsorm 适用于所有 react 项目,可以帮助人工更少的书写代码,新同学不需要  了解太多概念,就可以提供有效代码,只需要关注于组件的 ui 展示和  交互逻辑。

扩展

可以扩展一个统一提供报错机制,将前端错误报送日志服务器。可以在框架层面加上错误处理和性能优化。

 我正在做的是 jsorm, 目前还不成熟,还在努力的 coding。但我觉得有一整套开发工具链是很有必要的, 可以提高开发速度,并且提高开发质量!并且可以在架构层面做优化。

以上,暂时的  思考,各位  大佬,请多多指教!

1…567

乱世天狼

乱世天狼 | 前端 | html | css | html5 | css3 | react | redux | webpack | 哲学 | 精神 | 生活

67 日志
48 标签
© 2020 乱世天狼
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4