搭建angular4 + webpack

官方推荐基础包

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
### mkdir ap && cd ap
wget -O webpack.zip https://angular.cn/resources/zips/webpack/webpack.zip
unzip webpack.zip
npm install
npm i --save-dev html-webpack-plugin style-loader css-loader babel-loader eslint-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-1 babel-plugin-transform-class-properties eslint babel-eslint postcss-loader
npm install --save babel-runtime
### vim src/tsconfig.json
add "allowJs": true
under "compilerOptions"
### vim config/webpack.common.js
### at module.rules add
{
enforce: 'pre',
test: /\.js$/,
exclude: /(node_modules)/,
loader: 'eslint-loader',
options: {}
},
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'stage-1'],
plugins: ['transform-runtime', 'transform-class-properties']
}
}
}

hstart

###介绍

因为每次建测试环境都需要重复安装插件配置webpack等于是开发了一个简单的工具

1
npm i -g hstart

use

1
2
3
4
hstart -d your_project
//work with react
hstart -d your_project --react

create frontend environment with webpack

  • allow import css
  • allow import less
  • allow import html with art-template-loader
  • this tool is just create configs and start demo
  • allow es6 with babel

art-template

1
2
3
4
5
6
7
8
//index.js:
import list from './list.html';
list({title: 'hello world'});
//list.html
<h3>{{title}}</h3>

build

1
npm run build

develope

1
npm run dev

各语言开发必备插件faker

背景

当前开发经常需要大量的随机数据,如果这些随机数据是真实的,那么就太好了,于是faker就出现了, 并且当前几乎所有开发语言都有faker插件

应用

  • 多国语
  • 随机性强
  • 可生成随机图片链接
  • 生成信用卡信息
  • uuid
  • 等等…

前端 chart 插件选择

背景

大数据时代图表分析应用需求越来越高,最近做的项目分析了一下当前流行的charts插件

有哪些选择

  • google chart 谷歌出品必属精品

  • Chart.js 老牌不解释

  • echarts 百度出品,外表华丽,内涵也有

  • d3

  • vis

  • gantt 类似谷歌,个人以为没谷歌好看

  • mermaid 基于d3,两点在于markdown编写,直接生成图表等, 好屌好暴力

  • js-sequence-diagrams 楼上同时也是基于本插件做成的,非常骚,精品

react开发框架分析

可选的框架

当前react还是火的不行,各种框架层出不穷,但是名气高的就几个,这里列出两个

  • material-ui
  • antd

material-ui是安卓风格,antd是ios风格

material-ui

  • 文档详细
  • 效果炫
  • 插件强大
  • 可配置性非常高
  • 面向安卓开发首选
  • 很适合做后台开发框架

antd

  • 国人开发的框架
  • 文档除了详细还有教程,非常感人
  • 引导插入react redux等编程
  • 提供layout布局,并且有很多示例
  • 插件多的可怕,给人一种安全感,不用到处找插件了
  • 提供了metion,是不是第一个集成metion的框架?
  • 引导前端界面设计, 对于程序员简直是不能放手了
  • 也非常适合开发后台
  • 个人觉得是当前web、ios开发首选
  • 支持typescript

webpack 必备模块和插件

配置文件见extract-text-webpack-plugin部分

loader

  • css-loader
  • style-loader
  • eslint-loader
  • babel-loader

import css文件用, eslint则是检查编程规范, babel让你可以写es6语法

extract-text-webpack-plugin

  • 将所有import的css打包成文件[]
  • 适合在production环境下使用

先贴代码

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
const prod = false;
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const css_loader_dev = [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'postcss-loader' }
];
const css_loader_prod = ExtractTextPlugin.extract([
'css-loader',
'postcss-loader'
]);
const import_options = [
{
libraryName: 'antd'
}
];
const plugins = [];
const css_loader_use = prod ? css_loader_prod : css_loader_dev;
if (prod) {
plugins.push(new ExtractTextPlugin('styles.css'));
}
module.exports = {
context: path.resolve(__dirname, 'app'),
entry: './app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'pubilc'),
publicPath: '/'
},
devServer: {
contentBase: path.join(__dirname, 'public'),
compress: true,
//外网访问
disableHostCheck: true
},
module: {
rules: [
{
test: /\.css$/,
use: css_loader_use
},
{
enforce: 'pre',
test: /\.js$/,
exclude: /(node_modules)/,
loader: 'eslint-loader',
options: {}
},
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015', 'stage-1', 'react'],
plugins: [
'transform-runtime',
'transform-class-properties',
'transform-react-jsx',
'transform-class-properties',
['import', import_options]
]
}
}
}
]
},
resolve: {
alias: {
styles: path.resolve(__dirname, 'app/css')
}
},
plugins: plugins
};
  • 按自己需求定义prod来引入相关配置

babel-plugin-import

当前import会将模块整个引入, 不符合按需加载的思想整,在babel配置中引入后则实现按需加载,打包后减少了很多无用代码

nodejs数据模型设计初探

模型基类

以下以es6语法书写

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
class Model {
  //定义规则
  rules = {};
  //存储模型数据
  data = {};
  //存储异步验证列表
  await_list = [];
  //存储错误信息
  errors = [];
error = null;
  //定义基本错误信息
  ERROR_INT = 'must be int.';
ERROR_NO_PROPERTY = 'have no property';
ERROR_MAX = 'max length over';
ERROR_MIN = 'min length over';
ERROR_REG = 'reg error';
ERROR_FUNC = 'value not valid';
ERROR_EMAIL = 'email address is not valid';
  //错误信息显示
  error_labels = {
'must be int.': '必须为数字',
'have no property': '没有该属性定义',
'value not valid': '属性没有通过验证'
};
  //属性信息显示
  attribute_labels = {};
  //获取属性信息
  getAttributeLabel(name) {
return this.attribute_labels[name] || name;
}
  //获取错误信息显示
  getErrorLabel(name) {
return this.error_labels[name] || name;
}
  //添加错误信息
  addError(error, name) {
let column = this.getAttributeLabel(name);
let err = this.getErrorLabel(error);
let msg = `"${column}": ${err}`;
if (this.error === null) this.error = msg;
this.errors.push(msg);
}
  //验证前动作
  beforeValid() {}
  //验证
  valid() {
this.beforeValid();
//get all keys that need to valid.
let columns = Object.keys(this.rules);
//if no one need to valid, return success
if (columns.length === 0) return true;
for (let k = 0; k < columns.length; k++) {
let name = columns[k];
let rule = this.rules[name];
let value = this.data[name];
for (let i = 0; i < rule.length; i++) {
switch (typeof rule[i]) {
case 'string':
case 'number':
case 'email':
switch (rule[i]) {
case 'int':
if (!isFinite(value)) this.addError(this.ERROR_INT, name);
break;
case 'email':
if (
!/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA- Z]{2,}))$/.test(
value
)
)
this.addError(this.ERROR_EMAIL, name);
}
break;
case 'object':
if (
rule[i].hasOwnProperty('max') && `${value}`.length > rule[i].max
) {
this.addError(this.ERROR_MAX, name);
}
if (
rule[i].hasOwnProperty('min') && `${value}`.length < rule[i].min
) {
this.addError(this.ERROR_MIN, name);
}
if (rule[i] instanceof RegExp && !rule[i].test(value)) {
this.addError(this.ERROR_REG, name);
}
break;
case 'function':
let func_check = rule[i](value);
if (func_check instanceof Promise) this.await_list.push(func_check);
else if (true !== rule[i](value))
this.addError(this.ERROR_FUNC, name);
break;
default:
break;
}
}
}
//validation had sync check
if (this.await_list.length) {
return this.await();
}
return this.errors.length === 0;
}
  //异步处理
 async await() {
let res = new Promise(resolve => {
Promise.all(this.await_list).then(cs => {
let c = 0;
for (let i = 0; i < cs.length; i++) {
if (typeof cs[i] !== 'boolean') {
c++;
this.addError(cs[i].msg, cs[i].name);
}
}
if (c) resolve(false);
resolve(true);
});
});
return res;
}
  //获取全部错误
  getErrors() {
return this.errors;
}
  //获取第一条错误
  getError() {
return this.error;
}
}

demo

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
class User extends Model {
rules = {
name: [{ max: 10 }, { min: 2 }, 'unique', /abc/],
email: [
'email',
this.checkEmail.bind(this),
this.unique.bind(this),
this.test
]
};
data = {
name: null,
sex: null,
avatar: null,
email: null,
created: null
};
attribute_labels = {
name: '姓名',
email: '邮箱'
};
unique() {
//always resolve: true or false
return new Promise(resolve => {
setTimeout(() => {
console.log('unique checking');
resolve({ msg: '有很多这种数据哦', name: 'name' });
}, 3000);
});
}
test() {
return new Promise(resolve => {
setTimeout(() => {
console.log('test checking');
resolve({ msg: '这怎么可能验证通过, 虽然只是测试', name: 'email' });
}, 1000);
});
}
checkEmail() {
// if (this.data.email.startsWith('fox')) return false;
return true;
}
}
let user = new User();
user.data = {
name: 'abc',
email: 'abc@gmail.com'
};
user.valid().then(() => {
console.log(user.errors);
});
//if there is no sync func valid
user.valid() === false //return true
//console logs 输出
       
test checking
unique checking
[ '"姓名": 有很多这种数据哦', '"邮箱": 这怎么可能验证通过, 虽然只是测试' ]

react-router

MemoryRouter

  • url保存在内存中不会更改地址栏
  • 适合测试
  • 适合app开发,如react-native
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
//base
import { MemoryRouter } from 'react-router'
<MemoryRouter>
<App/>
</MemoryRouter>
//initialEntries: array
<MemoryRouter
initialEntries={[ '/one', '/two', { pathname: '/three' } ]}
initialIndex={1}
>
<App/>
</MemoryRouter>
#initialIndex: number
The initial location's index in the array of initialEntries.
#getUserConfirmation: func
A function to use to confirm navigation. You must use this option when using <MemoryRouter> directly with a <Prompt>.
#keyLength: number
The length of location.key. Defaults to 6.
<MemoryRouter keyLength={12}/>
#children: node

Prompt

  • 当要离开页面时询问用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Prompt } from 'react-router'
<Prompt
when={formIsHalfFilledOut}
message="Are you sure you want to leave?"
/>
#message: string
<Prompt message="Are you sure you want to leave?"/>
#message: func
<Prompt message={location => (
`Are you sure you want to go to ${location.pathname}?`
)}/>
#when: bool
<Prompt when={formIsHalfFilledOut} message="Are you sure?"/>

Redirect

  • 像浏览器一样页面跳转
  • 会重写history中当前location的记录
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
import { Route, Redirect } from 'react-router'
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
#to: string
<Redirect to="/somewhere/else"/>
#to: object
<Redirect to={{
pathname: '/login',
search: '?utm=your+face',
state: { referrer: currentLocation }
}}/>
#push: bool
<Redirect push to="/somewhere/else"/>
#from: string
<Switch>
<Redirect from='/old-path' to='/new-path'/>
<Route path='/new-path' component={Place}/>
</Switch>

Route

  • 路由可能是最重要的组件
  • 最基本的用法是:当路由更改时渲染不同的组件
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
import { BrowserRouter as Router, Route } from 'react-router-dom'
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route path="/news" component={NewsFeed}/>
</div>
</Router>
//If the location of the app is / then the UI hierarchy will be something like:
<div>
<Home/>
<!-- react-empty: 2 -->
</div>
//And if the location of the app is /news then the UI hierarchy will be:
<div>
<!-- react-empty: 1 -->
<NewsFeed/>
</div>
#有三种不同的方式去渲染路由
Route render methods
* <Route component>
* <Route render>
* <Route children>

web开发神器 browser-sync

安装

环境需要

  • node
  • npm
1
npm i -g browser-sync

proxy

执行后会自动打开浏览器

1
browser-sync start --proxy localhost:9000 --files ~/project/php

完成

现在修改~/project/php下的文件,浏览器就会自动刷新