diskutil list

  • 查看U盘位置 如:/dev/disk1

diskutil unmountDisk /dev/disk2

  • 取消挂载

sudo dd if=**.iso of=/dev/rdisk1 bs=1m;sync

  • 写入文件

Webpack开发服务器(webpack-dev-server)

  • npm install -g webpack-dev-server
  • 静态资源Web服务器
  • 基于Node.js Express
  • 内容实时打包和推送

react-hot-loader

  • npm install —save-dev react-hot-loader
  • React组件的热替换

创建一个server.js文件,用于启动webpack-dev-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('../webpack.config');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,//开启Hot Module Replacement (HMR)
noInfo: false,
historyApiFallback: true
}).listen(3000, '127.0.0.1', function (err, result) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:3000');
});

修改webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
entry: [
'webpack-dev-server/client?http://127.0.0.1:3000', // WebpackDevServer host and port,资源服务器地址,打包生成的文件从client?http://127.0.0.1:3000去获取动态的代码更新
'webpack/hot/only-dev-server',
'./scripts/index.js' // Your appʼs entry point
]
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'react-hot!jsx-loader?harmony'//让Webpack用react-hot-loader去加载React组件,harmony启动es6
}]

之后启动node server.js

前端服务器调用

载入打包文件时指定完整的URL地址

1
<script src="http://127.0.0.1:3000/assets/bundle.js"></script>

建立Socket IO来动态加载模块

这是啥

  • 模块加载器兼打包工具
  • 可以使用 require(XXX)来引入各模块
  • 加载器(loader)编译(JSX、sass、、、)
  • 以 commonJS 形式书写
  • 能打包、压缩混淆、图片转base64等
  • 扩展性强,插件机制完善,支持 React 热插拔

安装,配置

  • npm init,npm install webpack –save
  • webpack.config.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
var webpack = require('webpack');
module.exports = {
//入口文件配置
entry: {
a: "./a", b: "./b"
},
//出口文件输出配置
output: {
path: 'dist/js/page',//打包文件存放的绝对路径
filename: '[name].js'//打包后的文件名
publicPath:“/js” //网站运行时的访问路径
},
module: {
//加载器配置,需要npm install *-loader
loaders: [
//.css 文件使用 style-loader 和 css-loader 来处理
{ test: /\.css$/, loader: 'style-loader!css-loader' },
//.js 文件使用 jsx-loader 来编译处理
{ test: /\.js$/, loader: 'jsx-loader?harmony',exclude: /node_modules/ },//exclude排除
//.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
//图片文件使用 url-loader 来处理,小于8kb的直接转为base64
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'},
{test: /\.json$/,loader: 'json'}
]
}
//插件项
plugins: [new webpack.CommonsChunkPlugin("init.js") ],//这个插件可以将多个打包后的资源中的公共部分打包成单独的文件,这里指定公共文件输出为“init.js” 生成文件:init.js,a.js,b.js
//其它解决方案配置
resolve: {
//查找module的话从这里开始查找
root: 'E:/github/flux-example/src', //绝对路径
//自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
extensions: ['', '.js', '.json', '.scss'],
//模块别名定义,方便后续直接引用别名,无须多写长长的地址
alias: {
AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
ActionType : 'js/actions/ActionType.js',
AppAction : 'js/actions/AppAction.js'
}
}
};

打包

CommonsChunkPlugin 提取多个页面之间的公共模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
entry: {
p1: "./page1",
p2: "./page2",
p3: "./page3",
ap1: "./admin/page1",
ap2: "./admin/page2"
},
output: {
filename: "[name].js"
},
plugins: [
new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
]
};
// <script>s required:
// page1.html: commons.js, p1.js
// page2.html: commons.js, p2.js
// page3.html: p3.js
// admin-page1.html: commons.js, admin-commons.js, ap1.js
// admin-page2.html: commons.js, admin-commons.js, ap2.js

extract-text-webpack-plugin 独立打包

1
2
3
4
5
6
7
var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
plugins: [commonsPlugin, new ExtractTextPlugin("[name].css")],
entry: {};
}

Props

  • properties 使用它把数据传给组件
  • 在挂载组件时设置
  • 只能实例上调用setProps,不许this.setProps
  • 使用this.props访问
1
2
3
4
5
6
7
验证props
propTypes: {
style: View.propTypes.style.isRequired,
elementStyle: View.propTypes.style,
}
//isRequired 可选

State

  • 组件状态
  • this.state 是组件私有的,可以通过调用 this.setState() 来改变它。当状态更新之后,组件重新渲染自己。动态更新的关键点是调用this.setState(),replaceState()使用新对象,替换所有内容
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
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});

实例化

  • 一个实例初次创建

getDefaultProps 只会被调用一次

getInitalState 每个实例调用一次

componentWillMount 首次渲染之前被调用,可以修改state

render 创建虚拟DOM,表示组件输出,必须要有的方法。只能通过this.props和this.state访问数据。返回null、false、任何React组件。只能有一个顶级组件,不能改变组件的状态或者修改DOM的输出。

componentDidMount 在真实的DOM渲染之后,可以通过this.getDOMNode()访问原生DOM,测量渲染的DOM元素高度,运行jQuery

1
2
3
4
5
6
7
8
9
10
11
12
var datasource=[];
var MyComponent=React.creatClass({
render:function(){
return <input />;
},
componentDidMount: function(){
$(this.getDOMNOde()).autocomplete({
sources: datasource
})
}
})

存在期

  • 组件已渲染好,可以和它交互

componentWillReceiveprops 通过父辈组件修改props、state

1
2
3
4
5
6
7
componentWillReceiveProps: function(nextProps) {
if (nextProps.checked !== undefined) {
this.setState({
checked: nextProps.checked
})
}
}

shouldComponentUpdate 优化组件渲染,确实组件是否要渲染新的props、state

componentWillUpdate 在组件接收到新的props或者state渲染之前调用,不可在此更新state、props,要借助componentWillReceiveprops更新state

componentDidUpdate 类似componentDidMount,在此更新已渲染的DOM

销毁&清理期

componentWillUnmountcomponentDidMount中添加的任务都要在此撤销,如定时器、事件监听

由来

  • Cookie+Session的方式,在用户登录通过验证后,服务端将 数据加密后 通过在响应头(Header)保存到客户端浏览器的Cookie(包含sid或token)中,同时服务器保留相对应的Session(文件或DB)。用户之后发起的请求(Request)都会携带Cookie信息,服 务端需要根据Cookie寻回对应的Session,从而完成验证,确认这是之前登陆过的用户。
  • API应该被设计成无状态的(Stateless)。这意味着没有登陆,注销的方法,也没有sessions,API的设计者同样也不能依赖Cookie,因为不能保证这些request是由浏览器所发出的

JWT介绍

  • 分为三段,通过解码可以得到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. Headers
// 包括类别(typ)、加密算法(alg);
{
"alg": "HS256",
"typ": "JWT"
}
// 2. Claims
// 包括需要传递的用户信息;
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
// 3. Signature
// 根据alg算法与私有秘钥进行加密得到的签名字串;
// 这一段是最重要的敏感信息,只能在服务端解密;
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
SECREATE_KEY
)

在使用过程中,服务端通过用户登录验证之后,将Header+Claim信息加密后得到第三段签名,然后将三段签名合并后返回给客户端。客户端获取到token后,应该在每次向服务器请求数据时附带这个token,然后服务端验证token。

  • 因此JWT是用来取代服务端的Session而非客户端Cookie的方案.对于客户端本地存储,不同的选择更多是出于安全性的考虑

  • 客户端在与服务器第一次通信时,通过一些可靠信息(用户名、密码)和服务器交换取token,这个token作为客服端再次请求的权限钥匙。Token通常比密码更加长而且复杂。JWTs通常会长达150个字符。一旦获得了token,在每次调用API的时候都要附加上它。把token想象成一个安全的护照。你在一个安全的前台验证你的身份(通过你的用户名和密码),如果你成功验证了自己,你就可以取得这个。当你走进大楼的时候(试图从调用API获取资源),你会被要求验证你的护照,而不是在前台重新验证。

client

  • client 头部值部分使用Bearer Authorization: Bearer
  • .set(‘Authorization’, ‘Bearer ‘ + token)

encoded

1
jwt.sign(payload, secretOrPrivateKey, options, [callback]);

payload Claims,即传递的用户信息,could be an object literal, buffer or string.if not,it will be coerced into a string using JSON.stringify.

secretOrPrivateKey is a string or buffer containing either the secret for HMAC algorithms, or the PEM encoded private key for RSA and ECDSA.字符串,或着fs.readFileSync读取的证书

options:

  • algorithm (default: HS256) 加密算法
  • expiresIn: expressed in seconds or an string describing a time span rauchg/ms. Eg: 60, "2 days", "10h", "7d" 有效期/分钟
  • audience
  • subject
  • issuer
  • noTimestamp
  • headers

Example

1
2
3
4
5
6
7
// sign with default (HMAC SHA256)
var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
// sign with RSA SHA256
var cert = fs.readFileSync('private.key'); // get private key
var token = jwt.sign({ foo: 'bar' }, cert, { algorithm: 'RS256'});

decoded

1
2
3
4
5
6
7
8
var jwt = require('express-jwt');
app.get('/protected',
jwt({secret: 'shhhhhhared-secret'}),
function(req, res) {
if (!req.user.admin) return res.sendStatus(401);
res.sendStatus(200);
});

you can make some paths unprotected as follows:

1
2
//设置路由/token 不验证
app.use(jwt({ secret: 'shhhhhhared-secret'}).unless({path: ['/token']}));

By default, the decoded token is attached to req.user but can be configured with the requestProperty option.
默认解密后的token Claim内容加到req.user

1
2
//将解密后的token加到req.auth
jwt({ secret: publicKey, requestProperty: 'auth' });

安全性

  • XSS 主要原因是对用户输入信息不加过滤,导致用户 (被误导)恶意输入的Js代码在访问该网页时被执行,而Js可以读取当前网站域名下保存的Cookie信息.对用户输入的所有信息进行过滤即可,CDN服务也有可能引入不安全的Js脚本
  • CSRF,跨站请求伪造 主要利用Cookie是按照域名存储,同时访问某域名时浏览器会自动携带该域名所保存的Cookie信息这一特征.如果执意要将JWT存储在Cookie中,服务端则需要额外验证请求来源.将JWT保存在localStorage中,即将JWT放入Request Header中的Authorization位。

Managed transaction (auto-callback)

  • 事务将自动提交
  • sequelize.transaction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
return models.sequelize.transaction(function(t) {
// chain all your queries here. make sure you return them.
return User.create({
firstName: 'Abraham',
lastName: 'Lincoln'
}, {
transaction: t
}).then(function(user) {
return user.setShooter({
firstName: 'John',
lastName: 'Boothe'
}, {
transaction: t
});
});
}).then(function(result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback
}).catch(function(err) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback
});

Unmanaged transaction (then-callback)

  • 非自动提交事务
  • sequelize.transaction()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return sequelize.transaction().then(function (t) {
return User.create({
firstName: 'Homer',
lastName: 'Simpson'
}, {transaction: t}).then(function (user) {
return user.addSibling({
firstName: 'Lisa',
lastName: 'Simpson'
}, {transaction: t});
}).then(function () {
t.commit();
}).catch(function (err) {
t.rollback();
});
});

Automatically pass transactions to all queries

  • 自动添加事务
  • 无需{ transaction: t }
  • require(‘continuation-local-storage’)
1
2
3
4
5
6
7
8
9
10
var cls = require('continuation-local-storage'),
namespace = cls.createNamespace('my-very-own-namespace');
sequelize.transaction(function (t1) {
namespace.get('transaction') === t1; // true
});
sequelize.transaction(function (t2) {
namespace.get('transaction') === t2; // true
});

Array transaction

  • 事务并行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
models.sequelize.transaction(function(t) {
return models.Products.create(args, {
transaction: t
}).then(function(result) {
return Promise.all([models.user.create({
name: 'test'
}, {
transaction: t
}), models.address.create({
id: '1'
}, {
transaction: t
})]).then(function() {})
})
})

push + promise.all 实现循环读取、写入

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
models.sequelize.transaction(function(t) {
var tranArray = [];
if (!_.isUndefined(args.categories_id)) {
var categories_idArray = args.categories_id.split(',');
tranArray.push(models.Categories_product.destroy({
where: {
product_id: product_id
}
}, {
transaction: t
}));
categories_idArray.forEach(function(categories_id) {
tranArray.push(models.Categories_product.create({
product_id: product_id,
categories_id: categories_id
}, {
transaction: t
}));
});
}
if (_.isUndefined(areacode)) {
tranArray.push(models.Products.update(args, {
where: {
id: product_id
}
}));
} else {
tranArray.push(models.Product_lang_details.update(args,
where: {
product_id: product_id,
areacode: areacode
}));
tranArray.push(models.Product_shipping_to.update({
freezeTableName: args.freezeTableName
},
where: {
product_id: product_id,
areacode: areacode
}));
}
return Promise.all(tranArray);
}).then(function() {
return utils.response(res, 200, {
'message': 'update product successful!'
});
}).catch(function(error) {
logger.error('product update fall', error);
return utils.responseError(res, 500, {
'message': 'format error'
});
});
###

SELECT、DELETE、UPDATE

sql orm
select findAll,findOne,findById,findOrCreate,findAndCountAll
delete destroy
update update
insert create
sql orm
SELECT foo, bar … Model.findAll({attributes: [‘foo’, ‘bar’]});
SELECT foo, bar AS baz … Model.findAll({attributes: [‘foo’, [‘bar’, ‘baz’]]});
SELECT COUNT(hats) AS no_hats … Model.findAll({attributes: [[sequelize.fn(‘COUNT’, sequelize.col(‘hats’)), ‘no_hats’]]});
SELECT id, foo, bar, quz … Model.findAll({attributes: {exclude: [‘baz’] }});
1
2
3
4
5
6
7
8
9
Model.findAll({
attributes: ['id', 'foo', 'bar', 'baz', 'quz', [sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']]
});
//等于
Model.findAll({
attributes: { include: [[sequelize.fn('COUNT', sequelize.col('hats')), 'no_hats']] }
});
SELECT id, foo, bar, baz, quz, COUNT(hats) AS no_hats ...

WHERE

sql orm
SELECT * FROM post WHERE authorId = 12 AND status = ‘active’ Post.findAll({where: { authorId: 2,status: ‘active’}});

Operators

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
Post.update({
updatedAt: null,
}, {
where: {
deletedAt: {
$ne: null
}
}
});
// UPDATE post SET updatedAt = null WHERE deletedAt NOT NULL;
Post.findAll({
where: sequelize.where(sequelize.fn('char_length', sequelize.col('status')), 6)
});
// SELECT * FROM post WHERE char_length(status) = 6;
{
rank: {
$or: {
$lt: 1000,
$eq: null
}
}
}
// rank < 1000 OR rank IS NULL
{
createdAt: {
$lt: new Date(),
$gt: new Date(new Date() - 24 * 60 * 60 * 1000)
}
}
// createdAt < [timestamp] AND createdAt > [timestamp]
{
$or: [
{
title: {
$like: 'Boat%'
}
},
{
description: {
$like: '%boat%'
}
}
]
}
// title LIKE 'Boat%' OR description LIKE '%boat%'
op define
$and: {a: 5} AND (a = 5)
$or: [{a: 5}, {a: 6}] (a = 5 OR a = 6)
$gt: 6, > 6
$gte: 6, >= 6
$lt: 10, < 10
$lte: 10, <= 10
$ne: 20, != 20
$between: [6, 10], BETWEEN 6 AND 10
$notBetween: [11, 15], NOT BETWEEN 11 AND 15
$in: [1, 2], IN [1, 2]
$notIn: [1, 2], NOT IN [1, 2]
$like: ‘%hat’, LIKE ‘%hat’
$notLike: ‘%hat’ NOT LIKE ‘%hat’
$iLike: ‘%hat’ ILIKE ‘%hat’ (case insensitive) (PG only)
$notILike: ‘%hat’ NOT ILIKE ‘%hat’ (PG only)
$like: { $any: [‘cat’, ‘hat’]} LIKE ANY ARRAY[‘cat’, ‘hat’] - also works for iLike and notLike
$overlap: [1, 2] && [1, 2] (PG array overlap operator)
$contains: [1, 2] @> [1, 2] (PG array contains operator)
$contained: [1, 2] <@ [1, 2] (PG array contained by operator)
$any: [2,3] ANY ARRAY[2, 3]::INTEGER (PG only)
$col: ‘user.organization_id’ “user”.”organization_id”, with dialect specific column identifiers, PG in this example –$col取表的字段

WARNING

tabel need primarykey:如果没有,Sequlize会自己加个自增主键,可能引起错误

Node

findOrCreate: 查到一个,查不到就新建

1
2
3
4
5
6
7
8
9
10
models.BrandReview.findOrCreate({
where: {
mem_id: mem_id,
brand_id: brand_id
},
defaults: {
score: score //新建的数据
}
})
//返回值为数组,[json,created] 第一位是查询或创建的数据,第二位标识是否新建

update:返回值为数据,[2],数字代码改动记录数

destroy:返回数字,代表删除记录数