Shaozj's blog


  • 首页

  • 归档

  • 分类

  • 标签

yeoman 快速上手

发表于 2017-01-08   |   分类于 前端 , 构建   |  

项目文件名和目录结构

需要按照约定的方式 generator-name 给项目文件夹命名,name 为你的 generator 的名字。项目目录结构一般如下:

1
2
3
4
5
6
7
generator-name
├───package.json
└───generators/
├───app/
│ └───index.js
└───router/
└───index.js

package.json 例子:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "generator-name",
"version": "0.1.0",
"description": "",
"files": [
"generators"
],

"keywords": ["yeoman-generator"],
"dependencies": {
"yeoman-generator": "^1.0.0"
}

}

name 属性必须为 generator- 开头,keywords 属性中必须包含 “yeoman-generator”。

yo name 和 yo name:router,产生所需文件。

编写 index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'use strict';
const Generators = require('yeoman-generator');

module.exports = class extends Generator {
constructor(args, opts) {
// 为了正确初始化,需要调用 super constructor
super(args, opts);

// 然后,添加自己的代码
this.option('babel'); // 这个方法添加了对 `--babel` flag 的支持
}

// 添加自己的方法
method1() {
console.log('method 1 just ran');
}

method2() {
console.log('method 2 just ran');
}
};

每个在 class 中添加的方法都会在 generator 被调用时被执行一次,而且一般是按顺序执行。不过,有些特殊的方法名会触法特殊的执行顺序。

运行 generator

在 generator-name/ 目录下的命令行中,输入:

1
(sudo) npm link

这将安装你的项目依赖,并将本地文件链接到全局模块中。 成功后,即可通过 yo name 指令来生产项目,可以验证你写的 generator。

运行循环(run loop)

run loop 是一个队列系统并支持优先级。优先级由特殊的 prototype 方法名决定。如果方法名未匹配优先级,则将放入 default 组。按运行顺序,可用的优先级名如下:

1
2
3
4
5
6
7
8
1. initializing : 初始化阶段
2. prompting : 接受用户输入阶段
3. configuring : 保存配置信息和文件,如.editorconfig
4. default : 非特定的功能函数名称,如上面说到的method1
5. writing : 生成项目目录结构阶段
6. conflicts : 统一处理冲突,如要生成的文件已经存在是否覆盖等处理
7. install : 安装依赖阶段,如通过npm、bower
8. end : 生成器即将结束

下面是一个例子:

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
'use strict';
const Generators = require('yeoman-generator');
const utils = require('../../utils/all');
const prompts = require('./prompts');
const path = require('path');
const fs = require('fs');
const packageInfo = require('../../package.json');


const baseRootPath = path.join(__dirname, 'react-webpack-multipage-template');

/**
* Base generator. Will copy all required files from react-webpack-multipage-template
*/

class AppGenerator extends Generators.Base {

constructor(args, options) {

super(args, options);

// Make options available
this.option('skip-welcome-message', {
desc: 'Skip the welcome message',
type: Boolean,
defaults: false
});
this.option('skip-install');

// Use our plain template as source
this.sourceRoot(baseRootPath);

this.config.save();
}

initializing() {

if(!this.options['skip-welcome-message']) {
this.log(require('yeoman-welcome'));
this.log('Out of the box I include Webpack and some default React components.\n');
}
}

prompting() {

return this.prompt(prompts).then((answers) => {

// Make sure to get the correct app name if it is not the default
if(answers.appName !== utils.yeoman.getAppName()) {
answers.appName = utils.yeoman.getAppName(answers.appName);
}

// Set needed global vars for yo
this.appName = answers.appName;
this.style = answers.style;
this.cssmodules = answers.cssmodules;
this.postcss = answers.postcss;
this.generatedWithVersion = parseInt(packageInfo.version.split('.').shift(), 10);

// Set needed keys into config
this.config.set('appName', this.appName);
this.config.set('appPath', this.appPath);
this.config.set('style', this.style);
this.config.set('cssmodules', this.cssmodules);
this.config.set('postcss', this.postcss);
this.config.set('generatedWithVersion', this.generatedWithVersion);
});
}

configuring() {

// Generate our package.json. Make sure to also include the required dependencies for styles
let defaultSettings = this.fs.readJSON(`${baseRootPath}/package.json`);
this.log('defaultSettings: ', defaultSettings);
this.log('baseRootPath: ', defaultSettings);
let packageSettings = {
name: this.appName,
private: true,
version: '0.0.1',
description: `${this.appName} - Generated by generator-react-multipage`,
main: 'src/index.js',
scripts: defaultSettings.scripts,
repository: '',
keywords: [],
author: 'Your name here',
devDependencies: defaultSettings.devDependencies,
dependencies: defaultSettings.dependencies
};

// Add needed loaders if we have special styles
let styleConfig = utils.config.getChoiceByKey('style', this.style);
if(styleConfig && styleConfig.packages) {

for(let dependency of styleConfig.packages) {
packageSettings.devDependencies[dependency.name] = dependency.version;
}
}

// Add postcss module if enabled
let postcssConfig = utils.config.getChoiceByKey('postcss', 'postcss');
if(this.postcss && postcssConfig && postcssConfig.packages) {

for(let dependency of postcssConfig.packages) {
packageSettings.devDependencies[dependency.name] = dependency.version;
}
}

// Add cssmodules if enabled
const cssmoduleConfig = utils.config.getChoiceByKey('cssmodules', 'cssmodules');
if(this.cssmodules && cssmoduleConfig && cssmoduleConfig.packages) {
for(let dependency of cssmoduleConfig.packages) {
packageSettings.dependencies[dependency.name] = dependency.version;
}
}

this.fs.writeJSON(this.destinationPath('package.json'), packageSettings);
}

writing() {

const excludeList = [
'LICENSE',
'README.md',
'CHANGELOG.md',
'node_modules',
'package.json',
'.istanbul.yml',
'.travis.yml'
];

// Get all files in our repo and copy the ones we should
fs.readdir(this.sourceRoot(), (err, items) => {

for(let item of items) {

// Skip the item if it is in our exclude list
if(excludeList.indexOf(item) !== -1) {
continue;
}

// Copy all items to our root
let fullPath = path.join(baseRootPath, item);
if(fs.lstatSync(fullPath).isDirectory()) {
this.bulkDirectory(item, item);
} else {
if (item === '.npmignore') {
this.copy(item, '.gitignore');
} else {
this.copy(item, item);
}
}
}
});
}

install() {

// Currently buggy!
if(this.postcss) {
const postcss = require('./postcss');
postcss.write(path.join(this.destinationRoot(), 'conf/webpack/Base.js'));
}

if(!this.options['skip-install']) {
this.installDependencies({ bower: false });
}
}
}

module.exports = AppGenerator;

webpack 构建优化

发表于 2016-12-23   |   分类于 前端,构建   |  

两个问题

  1. 包体积过大
  2. 打包速度太慢

优化1 webpack.DllPlugin

能加快打包速度,减小自身业务代码打包后的体积,但代码总体积(包含了第三方库)反而增大

项目中我们要将第三方库的代码单独打包,一般采用的方式是 webpack.optimize.CommonsChunkPlugin 这个插件。

1
2
3
4
5
6
7
8
9
entry: {
vendor: ['react', 'react-dom'] // 类库
};

new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.js',
minChunks: Infinity
}),

然而,用这种方式,每次我们重新构建代码时,vendor 的代码都要重新构建,从而使得构建的速度变慢。

webpack.DllPlugin 就可以解决这个问题,将第三方库事先编译为dll库,之后构建项目自身源码时,就不许要重新构建dll库中的代码。

首先,我们需要编写 webpack.dll.config.js 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const path = require('path');
const webpack = require('webpack');

module.exports = {
entry: {
vendor: ['react', 'react-dom', 'antd']
},
output: {
path: path.join(__dirname, 'build'),
filename: '[name].dll.js',
library: '[name]_library'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'build', '[name]-manifest.json'),
name: '[name]_library'
})
]
};

其实,webpack 中的插件也都可以用在 webpack.dll 配置文件中。

执行 webpack --config webpack.dll.config.js 后,即可在 build 目录下找到生成的 dll bundle 和对应的 manifest 文件。

1
2
3
4
5
6
7
8
9
> webpack --config webpack.dll.config.js

Hash: 674d064bed0bcc476713
Version: webpack 1.14.0
Time: 4349ms
Asset Size Chunks Chunk Names
vendor.dll.js 3.59 MB 0 [emitted] vendor
[0] dll vendor 12 bytes {0} [built]
+ 913 hidden modules

然后,我们在 webpack.config.js 中配置 DLLReferencePlugin 插件。

1
2
3
4
5
6
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./build/vendor-manifest.json')
})
]

在 html 中,需要手动引入 dll 库

1
<script src="/build/vendor.dll.js"></script>

性能对比

首次构建:(build)
采用 CommonsChunkPlugin :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hash: 6ade5ef99f806b597685
Version: webpack 1.14.0
Time: 13349ms
Asset Size Chunks Chunk Names
program-list/index.js.map 18.7 kB 2 [emitted] program-list/index
index/index.js 26.2 kB 0 [emitted] index/index
program-list/index.js 19.3 kB 2 [emitted] program-list/index
theme-manage/index.js 622 kB 3 [emitted] theme-manage/index
vendor.js 1.43 MB 4 [emitted] vendor
index/index.js.map 19.3 kB 0 [emitted] index/index
new-theme/index.js.map 499 kB 1 [emitted] new-theme/index
new-theme/index.js 446 kB 1 [emitted] new-theme/index
theme-manage/index.js.map 727 kB 3 [emitted] theme-manage/index
vendor.js.map 1.71 MB 4 [emitted] vendor
index.html 2.45 kB [emitted]
new-theme.html 2.46 kB [emitted]
program-list.html 2.47 kB [emitted]
theme-manage.html 2.47 kB [emitted]

采用 DllPlugin :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hash: 6f2755df167fdbf28a55
Version: webpack 1.14.0
Time: 8209ms
Asset Size Chunks Chunk Names
program-list/index.js.map 10.4 kB 2 [emitted] program-list/index
index/index.js 26.1 kB 0 [emitted] index/index
program-list/index.js 12 kB 2 [emitted] program-list/index
theme-manage/index.js 48.4 kB 3 [emitted] theme-manage/index
vendor.js 303 kB 4 [emitted] vendor
index/index.js.map 19.3 kB 0 [emitted] index/index
new-theme/index.js.map 99.5 kB 1 [emitted] new-theme/index
new-theme/index.js 111 kB 1 [emitted] new-theme/index
theme-manage/index.js.map 39.5 kB 3 [emitted] theme-manage/index
vendor.js.map 358 kB 4 [emitted] vendor
index.html 2.5 kB [emitted]
new-theme.html 2.51 kB [emitted]
program-list.html 2.52 kB [emitted]
theme-manage.html 2.52 kB [emitted]

可以看到,用 DllPlugin 能比 CommonsChunkPlugin 约5s。这里的例子是一个多页面构建的例子。
注意,此处我们虽然看到 DllPlugin 打包出来的文件要比 CommonsChunkPlugin 的小,但其实是我们把第三方库提取到了 vendor.dll.js 中的原因,加上 vendor.dll.js 中的文件,DllPlugin 打包总文件体积更大。

模块热加载(rebuild)
采用 CommonsChunkPlugin :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Hash: d8187730060863aecaad
Version: webpack 1.14.0
Time: 2478ms
Asset Size Chunks Chunk Names
6ade5ef99f806b597685.hot-update.json 36 bytes [emitted]
index/index.js 26.2 kB 0 index/index
program-list/index.js 19.3 kB 2 program-list/index
theme-manage/index.js 622 kB 3 theme-manage/index
vendor.js 1.43 MB 4 [emitted] vendor
1.6ade5ef99f806b597685.hot-update.js 6.41 kB 1 [emitted] new-theme/index
new-theme/index.js 446 kB 1 [emitted] new-theme/index
index/index.js.map 19.3 kB 0 index/index
program-list/index.js.map 18.7 kB 2 program-list/index
theme-manage/index.js.map 727 kB 3 theme-manage/index
new-theme/index.js.map 499 kB 1 [emitted] new-theme/index
1.6ade5ef99f806b597685.hot-update.js.map 4.41 kB 1 [emitted] new-theme/index
vendor.js.map 1.71 MB 4 [emitted] vendor

采用 DllPlugin :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Hash: ca95bad33ad492f0a11b
Version: webpack 1.14.0
Time: 1005ms
Asset Size Chunks Chunk Names
456baae3287b91efa0d4.hot-update.json 36 bytes [emitted]
index/index.js 26.1 kB 0 index/index
program-list/index.js 12 kB 2 program-list/index
theme-manage/index.js 48.4 kB 3 theme-manage/index
vendor.js 303 kB 4 [emitted] vendor
1.456baae3287b91efa0d4.hot-update.js 6.4 kB 1 [emitted] new-theme/index
new-theme/index.js 111 kB 1 [emitted] new-theme/index
index/index.js.map 19.3 kB 0 index/index
program-list/index.js.map 10.4 kB 2 program-list/index
theme-manage/index.js.map 39.5 kB 3 theme-manage/index
new-theme/index.js.map 99.5 kB 1 [emitted] new-theme/index
1.456baae3287b91efa0d4.hot-update.js.map 4.41 kB 1 [emitted] new-theme/index
vendor.js.map 358 kB 4 [emitted] vendor

追求极致体验,dll 值得拥有。

另外还需注意到的一点是,antd 这个包非常大,大概有 2.6m,用 dll 的方式,就一次把整个antd 的包都打入 vendor.dll.js 中,使得 vendor.dll.js 非常大。这在开发环境时,为了追求打包速度,可以不考虑包大小。在生产环境下,建议还是采用 antd 推荐的 babel 插件,做按需加载处理,从而减小包的总大小

优化2 HappyPack

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const HappyPack = require('happypack');

plugins: [
new HappyPack({
id: 'jsx',
threads: 4,
loaders: ['react-hot', 'babel?' + JSON.stringify({
presets: ['es2015', 'stage-0', 'react']
})]
})
]

loaders: [
{
test: /\.(js|jsx)$/,
//loader: 'react-hot!babel-loader',
loaders: ['happypack/loader?id=jsx'],
include: [].concat(
config.additionalPaths,
[ path.join(__dirname, '/../src') ]
)
}
]

采用 happypack 能进一步将构建时间从10s提升至5s。

以上两个优化都是优化了构建速度,然而并没能减小代码编译后包的体积大小,这是以后需要继续探索改进的地方。

参考文档:

  1. 开发工具心得:如何 10 倍提高你的 Webpack 构建效率
  2. webpack使用优化(基本篇)
  3. webpack使用优化(react篇)
  4. webpack 的 dll 功能
  5. 彻底解决Webpack打包性能问题
  6. 使用 webpack + react + redux + es6 开发组件化前端项目
  7. happypack github
  8. webpack 2.1 beta20 + happypack 多线程

fetch

发表于 2016-12-15   |   分类于 前端   |  

用 XMLHttpRequest 来执行异步请求,虽然有用,却并不是最佳 api。它在设计上不符合职责分离原则,将输入、输出和用事件来跟踪的状态混杂在一个对象里(XMLHttpRequest 对象)。而且基于事件的异步模型写起来也没有现代的 Promise,generator/yield,async/await 友好。
Fetch 的出现就是为了解决 XHR 的问题,其主要优点有:

  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 同构方便,使用 isomorphic-fetch

Fetch API 旨在用来简化 HTTP 请求,它包含以下类和方法:

  • fetch 方法:用于发起 HTTP 请求
  • Request 类:用来描述请求
  • Response 类:用来表示响应
  • Headers 类:用来表示 HTTP 头部信息

这些类和方法在网上有着丰富的教程,在此不做赘述,可以查阅参考文献。

常见的坑

  • fetch 请求默认是不带 cookie 的,需要设置 fetch(url, {credentials: 'include'})
  • 服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。可以用response.status 或 response.ok 判断服务端是否返回错误。

fetch 发送 post 请求

$.ajax方法

首先我们看下 $.ajax post 数据的方法。

$.ajax({
  type: 'POST',
  url: '/video/v5video/item/add.htm',
  data: data,
  dataType: 'json'
})

chrome 中的请求信息:

这种情况下,服务器可以正常地解析 post 数据。

方法1:

fetch('/video/v5video/item/add.htm', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data),
  credentials: 'include'
})

chrome 中的请求信息:

可以看到,请求数据在 payload 中,在一些后台中(如webx),并不支持解析 payload 中的数据,此时将不能正常 post 数据。

方法2:

fetch('/video/v5video/item/add.htm', {
  method: 'POST',
  headers: {},
  body: setFormData(data),
  credentials: 'include'
});

function setFormData(data) {
  let formData = new FormData();
  for (let name in data) {
    formData.append(name, data[name]);
  }
  return formData;
}

chrome 中的请求信息:

存在着和方法一同样的问题。

方法3:

有没有什么方法可以让 fetch 发出和 $.ajax 一样的请求呢?答案是有的:

fetch('/video/v5video/item/add.htm', {
  method: 'POST',
  body: setUrlSearchParams(data),
  credentials: 'include'
});

function setUrlSearchParams(data) {
  let urlSearchParams = new URLSearchParams();
  for (let name in data) {
    urlSearchParams.append(name, data[name]);
  }
  return urlSearchParams;
}  

chrome 中的请求信息:

这样就达到了和 $.ajax 一样的效果了。

参考文献

  • [1] Fetch Living Standard
  • [2] A window.fetch JavaScript polyfill
  • [3] 这个API很“迷人”——(新的Fetch API)
  • [4] Fetch API
  • [5] 传统 Ajax 已死,Fetch 永生
  • [6] AJAX POST请求中参数以form data和request payload形式在servlet中的获取方式
  • [7] Fetch发送POST请求的坑与解决方案

mac下eclipse起服务器使用80端口

发表于 2016-12-04   |   分类于 nginx   |  

方法

  • 使用第三方代理软件nginx

步骤

  • 1、安装pcre

    • 下载pcre http://www.pcre.org/
    • 安装pcre

      $ cd ~/Downloads
      $ tar xvzf pcre-8.5
      $ cd pcre-8.5
      $ sudo ./configure --prefix=/usr/local
      $ sudo make
      $ sudo make install 
      
  • 2、安装Nginx

    • 下载Nginx http://nginx.org/

    • 安装Nginx

      $ cd ~/Downloads
      $ tar xvzf nginx-1.6.0.tar.gz
      $ cd nginx-1.6.0
      $ sudo ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-cc-opt="-Wno-deprecated-declarations"
      $ sudo make
      $ sudo make install
      
  • 3、开启Nginx

    • 将/usr/local/nginx/sbin加入到环境变量里
    • 运行

      $ sudo nginx 
      
    • 停止

      $ sudo nginx -s stop
      
  • 3、配置Nginx

    • 配置nginx,监听80端口,然后跳转到8080端口。在目录/usr/local/nginx/conf下找到配置文件nginx.conf,如下编辑

      server {
          listen       80;
          server_name  tv-lq.alibaba.net;
          add_header Cache-Control private;
          charset utf-8;
          fastcgi_intercept_errors on;
      
          location / {
              proxy_next_upstream http_502 http_504 error timeout invalid_header;
              proxy_pass http://127.0.0.1:8080;
              proxy_set_header Host $host;
              proxy_set_header X-Forwarded-For $remote_addr;
              proxy_intercept_errors on;
          }
      }
      

webpack 多页面构建

发表于 2016-12-04   |   分类于 前端   |  

webpack 多页面构建

目标:

  • 基于webpack支持react多页面构建(不用gulp,gulp-webpack 构建速度太慢[3]), generator-react-webpack 对单页面支持很好,但对多页面,需要改造
  • 提高开发人员的效率
  • 并能对项目进行足够的性能优化
  • 提高构建的效率

配置文件编写(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
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
var path = require('path');
var glob = require('glob');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var node_dir = path.join(__dirname, './node_modules/');
var HtmlWebpackPlugin = require('html-webpack-plugin');

// 获取所有入口文件
var getEntry = function(globPath) {
var entries = {
vendor: ['jquery','react','react-dom','./src/app'] // 类库
};
glob.sync(globPath).forEach(function(entry) {
var pathname = entry.split('/').splice(-2).join('/').split('.')[0];
entries[pathname] = [entry];
});
console.log(entries);
return entries;
};
// 判断是否是在当前生产环境
var isProduction = process.env.NODE_ENV === 'production';
var entries = getEntry('./src/view/*/*.jsx');
var chunks = Object.keys(entries);
module.exports = {
entry: entries,
output: {
path: path.join(__dirname, './dist'),
filename: isProduction ?'js/[name].[hash:8].js':'js/[name].js',
publicPath: '/dist/',
chunkFilename: 'chunk/[name].chunk.js'
},
module: {
noParse:[
/*path.join(node_dir,'./react/dist/react.min.js'),
path.join(node_dir,'./jquery/dist/jquery.min.js'),
path.join(node_dir,'./react-dom/dist/react-dom.min.js')*/

],
loaders: [{
test: /\.jsx?$/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
},
exclude: node_dir
}, {
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css')
}, {
test: /\.less$/,
loader: ExtractTextPlugin.extract('style', 'css!less')
}, {
test: /\.(png|jpe?g|gif)$/,
loader: 'url?limit=8192&name=img/[hash:8].[ext]'
}, {
//文件加载器,处理文件静态资源
test: /\.(woff|woff2|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file?limit=10000&name=fonts/[hash:8].[ext]'
}]
},
resolve: {
extensions: ['', '.js', '.jsx', '.json'],
alias: {
mod: node_dir
}
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery', // 使jquery变成全局变量,不用在自己文件require('jquery')了
jQuery: 'jquery',
React: 'react',
ReactDOM: 'react-dom'
}),
// 类库统一打包生成一个文件
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: isProduction ? 'js/vendor.[hash:8].js':'js/vendor.js',
minChunks: 3 // 提取使用3次以上的模块,打包到vendor里
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new ExtractTextPlugin(isProduction ? 'css/[name].[hash:8].css':'css/[name].css')
],
devtool: isProduction ? null : 'source-map'
};
// 生成HTML文件
chunks.forEach(function(pathname) {
if (pathname == 'vendor') {
return;
}
var conf = {
title: 'My App',
filename: isProduction? '../view/' + pathname + '.html' : pathname + '.html',
template: './src/template.html',
inject: 'body',
minify: {
removeComments: true,
collapseWhitespace: false
}
};
if (pathname in module.exports.entry) {
conf.chunks = ['vendor', pathname];
conf.hash = false;
}
module.exports.plugins.push(new HtmlWebpackPlugin(conf));
});

Webpack的配置主要包括以下几大项目:

  • entry:js入口源文件

    • 为了使用多入口文件,你可以给entry传入一个对象。对象的key代表入口点名字,value代表入口点。当使用多入口点的时候,需要重载output.filename,否责每个入口点都写入到同一个输出文件里面了。使用[name]来得到入口点名字。
    • 例子:

      {
          entry: {
              a: "./a",
              b: "./b",
              //支持数组形式,将加载数组中的所有模块,但以最后一个模块作为输出
              //该方法可以添加多个彼此不互相依赖的文件
              c: ["./c", "./d"]
          },
          output: {
              path: path.join(__dirname, "dist"),
              filename: "[name].entry.js" // a.enrty.js, b.enrty.js, c.entry.js
          }
      }
      
  • output:生成文件

    • output参数是个对象,定义了输出文件的位置及名字.

    • 例子:

      output: {
          path: "dist/js/page",
          publicPath: "/output/",
          filename: "[name].bundle.js"
      }
      
      path: 打包文件存放的绝对路径 
      publicPath: 网站运行时的访问路径 
      filename:打包后的文件名
      
  • module:模块加载器

    • 在webpack中JavaScript,CSS,LESS,TypeScript,JSX,CoffeeScript,图片等静态文件都是模块,不同模块的加载是通过模块加载器(webpack-loader)来统一管理的。loaders之间是可以串联的,一个加载器的输出可以作为下一个加载器的输入,最终返回到JavaScript上。

    • 例子:

      module: {
          //加载器配置
          loaders: [
              //.css 文件使用 style-loader 和 css-loader 来处理
              { 
                  test: /\.css$/, 
                  loader: 'style-loader!css-loader' 
              },
              //.js 文件使用 jsx-loader 来编译处理
              { 
                  test: /\.js$/, 
                  loader: 'jsx-loader?harmony' 
              },
              //.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'
              }
          ]
      }    
      
    • 多个loader可以用在同一个文件上并且被链式调用。链式调用时从右到左执行且loader之间用“!”来分割。

    • 模块加载器(loader)自身可以根据传入不同的参数进行配置。
  • resolve:文件路径的指向

    • webpack在构建包的时候会按目录的进行文件的查找,resolve属性中的extensions数组中用于配置程序可以自行补全哪些文件后缀:

    • 例子:

      resolve: {
          //查找module的话从这里开始查找
          root: '/pomy/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'
          }
      }
      
  • plugins:插件,比loader更强大,能使用更多webpack的api

    • 插件一般都是用于输出bundle的node模块。例如,uglifyJSPlugin获取bundle.js然后压缩和混淆内容以减小文件体积。类似的extract-text-webpack-plugin内部使用css-loader和style-loader来收集所有的css到一个地方最终将结果提取结果到一个独立的”styles.css“文件,并且在html里边引用style.css文件。

      var ExtractTextPlugin = require("extract-text-webpack-plugin");
      
      module: {
          loaders: [
              {
                  test: /\.css$/,
                  loader: ExtractTextPlugin.extract("style-loader", "css-loader")
              },
              {
                  test: /\.less$/,
                  loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
              }
          ]
      },
      plugins: [
          new ExtractTextPlugin("[name].css")
      ]
      
    • code-splitting 插件CommonsChunkPlugin

      • 将多次引用的模块单独打包

        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            filename: isProduction ? 'js/vendor.[hash:8].js':'js/vendor.js',
            minChunks: 3 // 提取使用3次以上的模块,打包到vendor里
        })
        
    • 多页面 html 生成插件 html-webpack-plugin

      • 例子:

        var HtmlWebpackPlugin = require('html-webpack-plugin');
        
        chunks.forEach(function(pathname) {
            if (pathname == 'vendor') {
                return;
            }
            var conf = {
                title: 'My App',
                filename: isProduction? '../view/' + pathname + '.html' : pathname + '.html',
                template: './src/template.html',
                inject: 'body',
                minify: {
                    removeComments: true,
                    collapseWhitespace: false
                }
            };
            if (pathname in module.exports.entry) {
                conf.chunks = ['vendor', pathname];
                conf.hash = false;
            }
            module.exports.plugins.push(new HtmlWebpackPlugin(conf));
        });
        
* src目录下有个template.html文件,无需引入任何css和js,webpack会自动帮我们打包引入,`<%= htmlWebpackPlugin.options.title %>`读取配置好的页面标题

    
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title> <%= htmlWebpackPlugin.options.title %> </title>
</head>
<body>
<div id="app"></div>
</body>
</html>
* 最终通过打包,会生成对应入口的html文件,

比如src/view/index/index.js会生成view/index/index.html

    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> My App </title>
<link href="/dist/css/vendor.abf9657f.css" rel="stylesheet">
<link href="/dist/css/index/index.abf9657f.css" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="/dist/js/vendor.abf9657f.js"></script>
<script type="text/javascript" src="/dist/js/index/index.abf9657f.js"></script>
</body>
</html>
* 你会发现相关资源文件都自动引入了,十分便捷。

webpack 常用命令

  • webpack 最基本的启动webpack命令
  • webpack -w 提供watch方法,实时进行打包更新
  • webpack -p 对打包后的文件进行压缩
  • webpack -d 提供SourceMaps,方便调试
  • webpack –colors 输出结果带彩色,比如:会用红色显示耗时较长的步骤
  • webpack –profile 输出性能数据,可以看到每一步的耗时
  • webpack –display-modules 默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块

webpack dev server

  • 配置示例:

    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
    var webpack = require('webpack');
    var WebpackDevServer = require('webpack-dev-server');
    var config = require('./webpack.config.js');

    for (var i in config.entry) {
    // 每个入口文件加入 client websocket 热加载脚本
    config.entry[i].unshift(
    "webpack-dev-server/client?http://127.0.0.1:3000/",
    "webpack/hot/only-dev-server"
    );
    }
    config.module.loaders.unshift({
    test: /\.jsx?$/,
    loader: 'react-hot',
    exclude: /node_modules/
    });
    config.plugins.push(new webpack.HotModuleReplacementPlugin());
    new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    historyApiFallback: true,
    stats: { colors: true }
    }).listen(3000, '127.0.0.1', function (err, result) {
    if (err) {
    console.log(err);
    }
    console.log('server start');
    });
  • 用处

    • 开启服务器调试环境
    • 解决以下两个问题:
      • webpack –watch 打包速度慢
      • 不能做到hot replace
  • 配置

    • Content Base

      • 如果不进行设定的话,服务器伺服默认是在当前目录下。
      • 命令行设置 webpack-dev-server --content-base build/
      • webpack 配置

        devServer: {
            contentBase: './src/',
            historyApiFallback: true,
            hot: true,
            port: defaultSettings.port,
            publicPath: '/assets/',
            noInfo: false
        }
        
      • publicPath

        • webpack server 伺服的 bundle 是在内存中的,其相对路径由 publicPath 字段指定。
        • 如果用以上的配置,bundle 可以通过地址 localhost:8080/assets/bundle.js 访问到。(注意:访问的不是output目录下的文件而是内存中的数据!)
  • 自动更新和热替换

    • 配置:

      var config = require("./webpack.config.js");
      config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server");
      var compiler = webpack(config);
      var server = new webpackDevServer(compiler, {
          hot: true
          ...
      });
      server.listen(8080);
      

      关键配置: config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server");

      在每个入口文件注入 client websocket 热加载脚本。

参考文档:

webpack 多页面构建

  1. 使用 React 写个简单的活动页面运营系统
  2. Webpack+React多页面应用探索
  3. webpack不适合多页面应用?你写的插件还不够多
  4. 【AlloyTeam优化系列】构建篇
    webpack react generator
  5. generator-react-webpack
    webpack 构建优化
  6. 开发工具心得:如何 10 倍提高你的 Webpack 构建效率
  7. webpack使用优化(基本篇)
  8. webpack使用优化(react篇)
    webpack-dev-server
  9. webpack-dev-server使用方法
  10. WEBPACK DEV SERVER
    多入口以及code-splitting
  11. webpack multiple-entry-points example
  12. MULTIPLE ENTRY POINTS
  13. CODE SPLITTING
    webpack 配置
  14. 详解前端模块化工具-Webpack 不错的文章
  15. 【翻译】Webpack——令人困惑的地方好文章
    webpack CommonsChunkPlugin详细教程
  16. webpack CommonsChunkPlugin详细教程

老生常谈之 css 居中

发表于 2016-08-20   |   分类于 css , 前端   |  

绝对定位居中(Absolute Centering)技术

.Absolute-Center {  
  margin: auto;  
  position: absolute;  
  top: 0; left: 0; bottom: 0; right: 0;  
}  
  • 解释:

通过以上描述,绝对居中(AbsoluteCentering)的工作机理可以阐述如下:

1、在普通内容流(normal content flow)中,margin:auto的效果等同于margin-top:0;margin-bottom:0。
W3C中写道If ‘margin-top’, or’margin-bottom’ are ‘auto’, their used value is 0.

2、position:absolute使绝对定位块跳出了内容流,内容流中的其余部分渲染时绝对定位部分不进行渲染。

Developer.mozilla.org:…an element that is positioned absolutely is taken out of the flow and thustakes up no space

3、为块区域设置top: 0; left: 0; bottom: 0; right: 0;将给浏览器重新分配一个边界框,此时该块block将填充其父元素的所有可用空间,父元素一般为body或者声明为position:relative;的容器。

Developer.mozilla.org:For absolutely positioned elements, the top, right, bottom, and left propertiesspecify offsets from the edge of the element’s containing block (what theelement is positioned relative to).

4、 给内容块设置一个高度height或宽度width,能够防止内容块占据所有的可用空间,促使浏览器根据新的边界框重新计算margin:auto

Developer.mozilla.org: The margin of the[absolutely positioned] element is then positioned inside these offsets.

5、由于内容块被绝对定位,脱离了正常的内容流,浏览器会给margin-top,margin-bottom相同的值,使元素块在先前定义的边界内居中。
W3.org: If none of the three [top, bottom,height] are ‘auto’: If both ‘margin-top’ and ‘margin-bottom’ are ‘auto’, solvethe equation under the extra constraint that the two margins get equal values.AKA: center the block vertically

这么看来, margin:auto似乎生来就是为绝对居中(Absolute Centering)设计的,所以绝对居中(Absolute Centering)应该都兼容符合标准的现代浏览器。

简而言之(TL;DR):绝对定位元素不在普通内容流中渲染,因此margin:auto可以使内容在通过top: 0; left: 0; bottom: 0;right: 0;设置的边界内垂直居中。

负外边距(Negative Margins)居中

.is-Negative {  
    width: 300px;  
    height: 200px;  
    padding: 20px;  
    position: absolute;  
    top: 50%; left: 50%;  
    margin-left: -170px; /* (width + padding)/2 */  
    margin-top: -120px; /* (height + padding)/2 */  
}

Transform 居中

.is-Transformed {   
  width: 50%;  
  margin: auto;  
  position: absolute;  
  top: 50%; left: 50%;  
  -webkit-transform: translate(-50%,-50%);  
      -ms-transform: translate(-50%,-50%);  
          transform: translate(-50%,-50%);  
}  

表格单元格(Table-Cell)居中

// HTML:
<div class="Center-Container is-Table">  
  <div class="Table-Cell">  
    <div class="Center-Block">  
    <!-- CONTENT -->  
    </div>  
  </div>  
</div>  

css:

// CSS:
.Center-Container.is-Table { display: table; }  
.is-Table .Table-Cell {  
  display: table-cell;  
  vertical-align: middle;  
}  
.is-Table .Center-Block {  
  width: 50%;  
  margin: 0 auto;  
}  

行内块元素(Inline-Block)居中

// HTML:
<div id="box">
    <div id="content">我是内容<br />我也是内容</div>
    <div id="actor">我是演员</div>
</div>

css:

// CSS:
#box { height: 400px; background: #c00;}
#content, #actor { display: inline-block; vertical-align: middle;}
#content { font-size: 12px; color: #fff;}
#actor {height: 400px; font-size: 0;}

加粗体的部分是关键所在。原理是都显示为行框的高度由最高的行内框决定。div#actor作为演员,高度指定和父元素一致。
兼容性:支持inline-block的浏览器均可。对于IE6/7,可以先使用hack方式使其支持Inline-block后,使用此方法实现垂直居中。

Flexbox居中

.box {
  display: flex;
  justify-content: center;
  align-items: center;
}

vim前端配置

发表于 2016-08-20   |   分类于 前端 , vim   |  

VIM 插件管理器

Vundle

  • 下载
    $ git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim

  • 配置插件
    在~/.vimrc文件中添加

    set nocompatible              " be iMproved, required
    filetype off                  " required
    
    " set the runtime path to include Vundle and initialize
    set rtp+=~/.vim/bundle/Vundle.vim
    call vundle#begin()
    " alternatively, pass a path where Vundle should install plugins
    "call vundle#begin('~/some/path/here')
    
    " let Vundle manage Vundle, required
    Plugin 'VundleVim/Vundle.vim'
    
    " The following are examples of different formats supported.
    " Keep Plugin commands between vundle#begin/end.
    " plugin on GitHub repo
    Plugin 'tpope/vim-fugitive'
    " plugin from http://vim-scripts.org/vim/scripts.html
    Plugin 'L9'
    " Git plugin not hosted on GitHub
    Plugin 'git://git.wincent.com/command-t.git'
    " git repos on your local machine (i.e. when working on your own plugin)
    Plugin 'file:///home/gmarik/path/to/plugin'
    " The sparkup vim script is in a subdirectory of this repo called vim.
    " Pass the path to set the runtimepath properly.
    Plugin 'rstacruz/sparkup', {'rtp': 'vim/'}
    " Install L9 and avoid a Naming conflict if you've already installed a
    " different version somewhere else.
    Plugin 'ascenator/L9', {'name': 'newL9'}
    
    " All of your Plugins must be added before the following line
    call vundle#end()            " required
    filetype plugin indent on    " required
    " To ignore plugin indent changes, instead use:
    "filetype plugin on
    "
    " Brief help
    " :PluginList       - lists configured plugins
    " :PluginInstall    - installs plugins; append `!` to update or just :PluginUpdate
    " :PluginSearch foo - searches for foo; append `!` to refresh local cache
    " :PluginClean      - confirms removal of unused plugins; append `!` to auto-approve removal
    "
    " see :h vundle for more details or wiki for FAQ
    " Put your non-Plugin stuff after this line  
    
  • 安装插件
    打开vim,运行:PluginInstall

vim-jsbeautify

vim-jsbeautify

  • 在~/.vimrc中添加
    Plugin 'maksimr/vim-jsbeautify'
  • 打开vim,运行:PluginInstall

vim-javascript

vim-javascript

    • 在~/.vimrc中添加
      Plugin 'pangloss/vim-javascript'
  • 打开vim,运行:PluginInstall

语法高亮配置

如果语法高亮没有生效,在.vimrc 加入以下3句:

syntax enable " enable syntax hightlight and completion
syntax on " syntax highlighing
filetype on " try to detect file types

emmet-vim

emmet-vim
还是一样的套路
Plugin 'mattn/emmet-vim'
教程网站http://mattn.github.io/emmet-vim/
中文教程https://www.zfanw.com/blog/zencoding-vim-tutorial-chinese.html

我的 .vimrc 配置文件

https://github.com/shaozj/vimrc

XSS 安全防范

发表于 2016-08-14   |   分类于 前端 , 安全   |  

XSS是跨站脚本攻击,主要特点是开发者之外的人向页面内容注入了脚本执行。
XSS攻击有两个要素,一是来自用户或者三方的内容,二是被当做html或者javascript代码执行。
在面对XSS攻击时,需要牢记的一句话是:一切输入和输出都是敌对的!

XSS 攻击的输入点

开发者之外的人可以通过以下输入点向页面中注入攻击脚本:

  • window.location/document.location/document.referrer
    • 来自url的内容,因为链接可能来自外部页面,所以页面url应当视为外部用户内容。
  • 后端接口返回数据
    • 来自服务端的数据,这些数据可能由其它系统提供,其中可能包含用户的直接输入,所以应该把通过后端接口请求得到的数据视为外部用户产生内容。
  • document.cookie
    • cookie有较多渠道被写入,为安全起见,应当视为用户产生内容。
  • input.value/textarea.value
    • 用户产生内容。

XSS 攻击的输出点

XSS 攻击输出点是开发者在代码中写的语句,这些语句可能会将用户输入的内容作为代码执行,进而发生XSS 攻击。XSS 攻击主要有以下输出点:

  • document.write/document.writeln:直接向文档流写入html内容,不提倡使用.

    • 如果希望用这种方式输出文本,在写入之前,应当使用document.createTextNode+appendChild将之转换成html文本语法。
    • 如果确有特殊设计,希望用这种方式输出html,在写入之前,应当过滤html语法字符。
  • innerHTML/$el.html():直接向文档节点内插入html内容,除使用模板外,不提倡使用。

    • 如果希望用这种方式输出文本,应当使用innerText或者document.createTextNode+appendChild代替。
    • 如果确有特殊设计,希望用这种方式输出html,在写入之前,应当过滤html语法字符。
  • new Function:通过参数字符串创建新函数,除特殊场景外,不提倡使用。如果来自不安全的输入的字符需要拼接成字符串,建议使用JSON.stringify处理。

  • eval:把字符串当做函数执行,除特殊场景外,不提倡使用。如果来自不安全的输入的字符需要拼接成字符串,建议使用JSON.stringify处理。

  • setTimeout/setInterval:时间相关函数,如果第一个参数是字符串,那么效果类似eval,任何场景下,不提倡这样使用,第一个参数应该永远是函数类型。

  • image.src/link.href/a.href:设置url,因为可能是javascript协议,建议使用lib.httpurl解析url的结构和参数,确定域名属于安全域名。

  • window.location:跳转url,因为uri可能是javascript协议,建议使用lib.httpurl解析url的结构和参数,确定域名属于安全域名。

XSS 攻击实例

HTML 攻击

  • 输出点

    • <img src="[代码]">
    • 现在弹个窗:

      <img src="x" onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">

    • 原code:

      <img src="x" onerror="alert(1)">

ES6 模板字符串

发表于 2016-08-07   |   分类于 前端   |  

无论是前端还是后端,都存在着模板引擎。模板引擎有着丰富的表现力,使得我们可以根据不同的数据渲染出不同的页面结构以及内容。在es6中引入了模板字符串,使得我们可以在字符串中嵌入变量、表达式以及可以直接写多行字符串。另外,模板字符串还支持嵌套使用。这些特性,一些模板引擎基本的功能,我们可以用es6模板字符串来实现。

for 循环

  • 一种比较直接的方式是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
${
(()=>{
let html = '';
for (let i = 0, len = blocksData.length; i < len; i++) {
let curData = blocksData[i];
let jsonData = JSON.stringify(curData);
html += `<div class="item-box block4in1 ${setClass(i)}" data-json="${eH(jsonData)}" fe-role="Widget">
<div class="item-content">
<img class="poster" src="${eH(baseUrl + curData.recommendImage)}">
<div class="item-mask"></div>
</div>
</div>`;
}
return html;
})()
}

可以看到,我们利用一个立即执行函数,在函数内写一个for循环来实现。

  • 对以上的方法稍加优化,我们可以得到如下的代码:
1
2
3
4
5
6
7
8
9
10
11
12
${
blocksData.reduce((prev, next, i, arr) => {
let jsonData = JSON.stringify(next);
return prev +=
`<div class="item-box block4in1 ${setClass(i)}" data-json="${eH(jsonData)}" fe-role="Widget">
<div class="item-content">
<img class="poster" src="${eH(baseUrl + next.recommendImage)}">
<div class="item-mask"></div>
</div>
</div>`;
}, '')
}

我们利用了Array的reduce方法,从而不需要将代码封装在一个立即执行函数之中。

if else 条件判断

很容易想到用三目运算符:

${(data && data != '') ? `<div>${data}</div>` : `<div>no contnet</div>`}

如果只在存在数据时有相应的html,不存在数据时没有html,则可以这么写:

${(data && data != '') && `<div>${data}</div>`}

特殊字符转义

在模板中非常重要的一点是字符串的转义,因为涉及到安全的问题,一个简单的方式是利用标签模板:

var message = SaferHTML`<p>${bonk.sender} has sent you a bonk.</p>`;

上面的代码等价于:

var message = SaferHTML(templateData, bonk.sender);

其中, templateData 是一个不可变的字符串数组,在上例中为:

Object.freeze(["<p>", " has sent you a bonk.</p>"]

SaferHTML需要我们自己去实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function SaferHTML(templateData) {
var s = templateData[0];
for (var i = 1; i < arguments.length; i++) {
var arg = String(arguments[i]);

// Escape special characters in the substitution.
s += arg.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");

// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}

从安全角度来看,这个 SaferHTML 非常脆弱。在 HTML 中,不同的地方需要用不同的方式去转义,SaferHTML 并没有做到。
另外一点是,这个方法只适用于没有嵌套使用模板字符串的情况,如果嵌套使用,那么一些不该被转义的字符串页会被转义。在这种情况下,我们还是老老实实地对每一处改转义的地方进行转义.

首先,需要编写一个转义函数:

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
const reUnescapedHtml = /[&<>"'`]/g; // 需要被转码的字符
const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); // 测试是否有unescaped字符时不需要 'g'
const htmlEscapes = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'`': '&#96;'
};
const escapeHtmlChar = basePropertyOf(htmlEscapes);

function basePropertyOf(object) {
return function(key) {
return object == null ? undefined : object[key];
};
}

/**
* 转义html字符串
* @param string {String} 输入的字符串
* @return {String} 转义后的字符串
*/

function escapeHTML(string) {
return (string && reHasUnescapedHtml.test(string))
? string.replace(reUnescapedHtml, escapeHtmlChar)
: string;
}

module.exports = escapeHTML;

然后我们在每一个需要转义字符串的地方调用 escapeHTML 方法进行转义。

参考文献:

DOM based XSS Prevention Cheat Sheet
[Web 安全] 了解XSS与防范
模版字符串
i18n with tagged template strings in ECMAScript 6

webpack学习

发表于 2016-03-23   |   分类于 前端   |  

功能需求

  • 编译
  • 压缩
  • web服务器

webpack学习

$ webpack --config XXX.js   //使用另一份配置文件(比如webpack.config2.js)来打包

$ webpack --watch   //监听变动并自动打包

$ webpack -p    //压缩混淆脚本,这个非常非常重要!

$ webpack -d    //生成map映射文件,告知哪些模块被最终打包到哪里了
12
Zhenjiang Shao

Zhenjiang Shao

16 日志
10 分类
21 标签
RSS
GitHub

Links

剑空
© 2017 Zhenjiang Shao
由 Hexo 强力驱动
主题 - NexT.Mist