Skip to content

Commit 5c92154

Browse files
author
Ken Berkeley
committed
improvement
1 parent 4ed3d10 commit 5c92154

File tree

13 files changed

+1118
-64
lines changed

13 files changed

+1118
-64
lines changed

README.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
> ### 更新
1010
> 2016/8/28   引入 `cross-env` 解决跨平台问题,新增优化项 `DedupePlugin`
1111
> 2016/8/29   重命名 `makeContainer / makeReducer.js => createContainer / createReducer.js`
12-
> 2016/9/10   重构 `src/redux/`
12+
> 2016/9/10   重构 `src/redux/`
13+
> 2016/10/15   同步 Vue Demo 的改动
1314
1415
## 目录
1516
#### § [技术栈](#features)
@@ -57,28 +58,28 @@
5758
转译成 ES5(答案请自行到 [Babel REPL][babel-repl] 在线编译验证)
5859

5960
### <a name="installation">⊙ 安装</a>
60-
> 建议升级到 node 5.x/6.x + npm 3.x 环境
61-
> 推荐使用 `cnpm` 或手动切换到淘宝 npm 源
62-
> `npm set registry https://registry.npm.taobao.org/`
61+
> 推荐升级到 node 5.x/6.x + npm 3.x 环境**强烈推荐**使用 [`cnpm`](https://github.com/cnpm/cnpm) 安装依赖或手动
62+
> 切换到淘宝 npm 源:`npm set registry https://registry.npm.taobao.org/`
63+
> (经测试,`cnpm` 对于 `node-sass` 等问题多多的 Package 拥有秒杀能力)
6364
6465
本示例项目需要结合 [简易留言板 RESTful API](https://github.com/kenberkeley/msg-board-api)
6566
模拟前后端分离开发(还为了与 [Vue Demo][vue-demo] 共用)
66-
请分别 `git clone`,打开**两个**命令窗口( Windows 下推荐使用 `Cygwin`**分别**切换到两者的目录下
67-
分别敲下 `npm install` 安装依赖(为避免 Windows 下的 npm 软链接问题,可加上 `--no-bin-link` 完全解构所有依赖)
67+
请分别 `git clone`,打开**两个**命令窗口( Windows 下推荐使用 `Cygwin / Git Bash`**分别**切换到两者的目录下
68+
分别敲下 `npm install` 安装依赖(为避免 Windows npm 2.x 的软链接问题,可加上 `--no-bin-link` 完全解构所有依赖)
6869

6970
> 虽然我们已经切换到了淘宝 npm 源,但安装 `node-sass@3.8.0` 的时候还是很有可能卡住
7071
> 因为它的安装需要从 Github 的 AWS 服务器拉取二进制文件,因此您可以为它指定源:
7172
> `npm i node-sass@3.8.0 --registry=https://registry.npm.taobao.org`
7273
>
7374
> 如果您想简单粗暴一点,[这里](http://pan.baidu.com/s/1o8eu4t0)还提供了 `node_modules.zip`,直接解压即可
7475
75-
最后需要全局安装跨平台环境配置器`npm i cross-env -g`
76+
最后需要全局安装跨平台环境变量配置器`npm i cross-env -g`
7677

7778
### <a name="start">⊙ 启动</a>
7879
先后在 `msg-board-api``react-demo` 的命令窗口下,敲下 `npm start`
7980
如无意外,默认浏览器就会自动打开 `localhost:9090`,您立即可以看到效果
8081
若浏览器没有自动弹出,则请自行手动访问
81-
> 开发过程中,通过 Webpack 处理的静态资源都由基于内存的 `webpack-dev-server` 提供
82+
8283
> P.S. 如果您还不清楚如何安装与启动,请看这个 [issue][how-to-start]
8384
8485
***
@@ -90,6 +91,7 @@
9091
├─ build/ # Webpack 配置目录
9192
├─ dist/ # build 生成的生产环境下的项目
9293
├─ src/ # 源码目录(开发都在这里进行)
94+
│ ├─ assets/ # 放置需要经由 Webpack 处理的静态文件
9395
│ ├─ components/ # 组件(COMPONENT)
9496
│ ├─ redux/ # Redux 一箩筐
9597
│ │ ├─ actions/ # (ACTION)
@@ -147,9 +149,9 @@
147149
* 前端开发服务器为 `localhost:9090`,可在 `build/webpack.config.dev.js` 中找到
148150
> 后端 RESTful API 基地址写在了 `src/services/xhr/config.js` 中,请根据实际自行修改
149151
150-
* 框架 / 类库 须分离打包以加快开发时的编译速度并有利于缓存,详见 `build/webpack.base.conf.js` 中的 `vendor`
151-
> 实际上该步骤可通过读取 `package.json``dependencies` 字段实现自动化,但其灵活度不够高,必要性也不大
152-
> P.S. 安装包时勿忘添加 `--save`
152+
* 框架 / 类库 须分离打包以加快开发时的编译速度并有利于缓存,详见 `build/webpack.base.conf.js` 中的 `vendor`
153+
> 实际上该步骤可通过读取 `package.json``dependencies` 字段实现自动化,但其灵活度不够高,必要性也不大
154+
> P.S. 安装包时勿忘 `--save / --save-dev` 以添加依赖记录
153155
154156
* <a name="alias">**路径别名**</a> 的定义位于 `build/webpack.base.conf.js`,好处就是**引入与重构都很方便**
155157
> 例如,在某组件中,引入 `userService` 需要 `import userService from '../../../services/userService'`
@@ -199,7 +201,7 @@
199201

200202
## <a name="deployment">&sect; 部署</a>
201203
`react-demo` 的命令窗口下,敲下 `npm run build`,将会在项目根目录下生成 `dist/`
202-
> 您可以使用命令行静态资源服务器 [serve](https://github.com/tj/serve) ( `npm i serve -g` ),敲下 `serve -p [端口] dist` 来快速查看 build 后的项目
204+
> 您可以使用命令行静态资源服务器 [serve](https://github.com/tj/serve) ( `npm i serve -g` ),敲下 `serve dist/ -p [端口]` 来快速查看 build 后的项目
203205
> 还可以 `cd dist` 后,`python -m SimpleHTTPServer [端口]``php -S localhost:[端口]` 快速便捷地实现静态资源服务器
204206
>
205207
> 关于生产环境下的部署与优化,已超出本文档的论述范围,请自行查阅相关资料

build/dev-server.js renamed to build/dev.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,5 @@ app.use(require('webpack-dev-middleware')(compiler, {
2525
app.use(require('webpack-hot-middleware')(compiler));
2626

2727
app.listen(9000, '127.0.0.1', function(err) {
28-
if (err) {
29-
console.log(err);
30-
return;
31-
}
32-
console.log('Listening at http://127.0.0.1:9000');
28+
err && console.log(err);
3329
});

build/prod.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var fs = require('fs'),
2+
path = require('path'),
3+
webpack = require('webpack'),
4+
config = require('./webpack.prod.conf');
5+
6+
webpack(config, function(err, stats) {
7+
// show build info to console
8+
console.log( stats.toString({ chunks: false, color: true }) );
9+
10+
// save build info to file
11+
fs.writeFile(
12+
path.join(config.commonPath.dist, '__build_info__'),
13+
stats.toString({ color: false })
14+
);
15+
});

build/webpack.base.conf.js

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
var path = require('path'),
2-
webpack = require('webpack');
2+
webpack = require('webpack'),
3+
NyanProgressPlugin = require('nyan-progress-webpack-plugin');
34

4-
var src = path.resolve(__dirname, '../src'); // 源码目录
5+
var rootPath = path.resolve(__dirname, '..'), // 项目根目录
6+
src = path.join(rootPath, 'src'); // 开发源码目录
57
var commonPath = {
6-
dist: path.resolve(__dirname, '../dist'), // build 后输出目录
8+
rootPath: rootPath,
9+
dist: path.join(rootPath, 'dist'), // build 后输出目录
710
indexHTML: path.join(src, 'index.html'), // 入口基页
8-
staticDir: path.resolve(__dirname, '../static') // 无需处理的静态资源目录
11+
staticDir: path.join(rootPath, 'static') // 无需处理的静态资源目录
912
};
1013

1114
module.exports = {
@@ -34,11 +37,12 @@ module.exports = {
3437
publicPath: '/static/'
3538
},
3639
resolve: {
37-
extensions: ['', '.js', '.jsx', '.json'],
40+
extensions: ['', '.js', '.jsx'],
3841
alias: {
3942
// ================================
4043
// 自定义路径别名
4144
// ================================
45+
ASSET: path.join(src, 'assets'),
4246
COMPONENT: path.join(src, 'components'),
4347
ACTION: path.join(src, 'redux/actions'),
4448
REDUCER: path.join(src, 'redux/reducers'),
@@ -52,12 +56,11 @@ module.exports = {
5256
}
5357
},
5458
resolveLoader: {
55-
root: path.join(__dirname, 'node_modules')
59+
root: path.join(rootPath, 'node_modules')
5660
},
5761
module: {
5862
loaders: [{
5963
test: /\.(js|jsx)$/,
60-
include: src,
6164
loaders: ['react-hot', 'babel?' + JSON.stringify({
6265
cacheDirectory: true,
6366
plugins: [
@@ -70,30 +73,34 @@ module.exports = {
7073
presets: ['react-optimize']
7174
}
7275
}
73-
}), 'eslint']
76+
}), 'eslint'],
77+
include: src,
78+
exclude: /node_modules/
7479
}, {
7580
test: /\.json$/,
7681
loader: 'json'
7782
}, {
78-
test: /\.less$/,
79-
loader: 'css!less'
83+
test: /\.html$/,
84+
loader: 'html'
8085
}, {
81-
test: /\.(png|jpg|gif|svg)$/,
86+
test: /\.(png|jpe?g|gif|svg)$/,
8287
loader: 'url',
8388
query: {
84-
limit: 10000,
85-
name: '[name].[ext]?[hash]'
89+
limit: 10240, // 10KB 以下使用 base64
90+
name: 'img/[name]-[hash:6].[ext]'
8691
}
8792
}, {
88-
test: /\.(eot|woff|ttf|svg)$/,
89-
loader: 'url-loader?limit=30000&name=[name]-[hash].[ext]'
93+
test: /\.(woff2?|eot|ttf|otf)$/,
94+
loader: 'fonts/url-loader?limit=10240&name=[name]-[hash:6].[ext]'
9095
}]
9196
},
9297
eslint: {
9398
formatter: require('eslint-friendly-formatter')
9499
},
95100
plugins: [
101+
new NyanProgressPlugin(), // 进度条
96102
new webpack.optimize.CommonsChunkPlugin({
103+
// 公共代码分离打包
97104
names: ['vendor', 'mainifest']
98105
}),
99106
new webpack.DefinePlugin({

build/webpack.dev.conf.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,21 @@ config.entry.app = [
1919
config.entry.app
2020
];
2121

22-
// generate loader string to be used with extract text plugin
23-
function generateExtractLoaders(loaders) {
24-
return loaders.map(function(loader) {
25-
return loader + '-loader' + (SOURCE_MAP ? '?sourceMap' : '');
26-
}).join('!');
27-
}
28-
2922
config.output.publicPath = '/';
3023

31-
config.plugins = (config.plugins || []).concat([
24+
// 开发环境下直接内嵌 CSS 以支持热替换
25+
config.module.loaders.push({
26+
test: /\.css$/,
27+
loader: 'style!css'
28+
}, {
29+
test: /\.less$/,
30+
loader: 'style!css!less'
31+
}, {
32+
test: /\.scss$/,
33+
loader: 'style!css!sass'
34+
});
35+
36+
config.plugins.push(
3237
new webpack.optimize.OccurenceOrderPlugin(),
3338
new webpack.HotModuleReplacementPlugin(),
3439
new webpack.NoErrorsPlugin(),
@@ -47,6 +52,6 @@ config.plugins = (config.plugins || []).concat([
4752
}, {
4853
reload: false
4954
})
50-
]);
55+
);
5156

5257
module.exports = config;

build/webpack.prod.conf.js

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
var webpack = require('webpack'),
2-
rimraf = require('rimraf'),
32
config = require('./webpack.base.conf'),
4-
ExtractTextPlugin = require('extract-text-webpack-plugin'),
53
HtmlWebpackPlugin = require('html-webpack-plugin'),
64
CopyWebpackPlugin = require('copy-webpack-plugin'),
5+
CleanWebpackPlugin = require('clean-webpack-plugin'),
6+
ExtractTextPlugin = require('extract-text-webpack-plugin'),
77
SOURCE_MAP = false;
88

9-
rimraf.sync(config.commonPath.dist);
10-
11-
// naming output files with hashes for better caching.
12-
// dist/index.html will be auto-generated with correct URLs.
13-
config.output.filename = '[name].[chunkhash].js';
14-
config.output.chunkFilename = '[id].[chunkhash].js';
9+
config.output.filename = '[name].[chunkhash:6].js';
10+
config.output.chunkFilename = '[id].[chunkhash:6].js';
1511

1612
config.devtool = SOURCE_MAP ? 'source-map' : false;
1713

18-
config.plugins = (config.plugins || []).concat([
19-
// 复制高度静态资源
20-
new CopyWebpackPlugin([
14+
// 生产环境下分离出 CSS 文件
15+
config.module.loaders.push({
16+
test: /\.css$/,
17+
loader: ExtractTextPlugin.extract('style', 'css')
18+
}, {
19+
test: /\.less$/,
20+
loader: ExtractTextPlugin.extract('style', 'css!less')
21+
}, {
22+
test: /\.scss$/,
23+
loader: ExtractTextPlugin.extract('style', 'css!sass')
24+
});
25+
26+
config.plugins.push(
27+
new CleanWebpackPlugin('dist', {
28+
root: config.commonPath.rootPath,
29+
verbose: false
30+
}),
31+
new CopyWebpackPlugin([ // 复制高度静态资源
2132
{
2233
from: config.commonPath.staticDir,
2334
ignore: ['*.md']
@@ -30,16 +41,12 @@ config.plugins = (config.plugins || []).concat([
3041
}
3142
}),
3243
new webpack.optimize.OccurenceOrderPlugin(),
33-
// extract css into its own file
34-
new ExtractTextPlugin('[name].[contenthash].css'),
35-
// generate dist index.html with correct asset hash for caching.
36-
// you can customize output by editing /build/index.template.html
37-
// see https://github.com/ampedandwired/html-webpack-plugin
44+
new ExtractTextPlugin('[name].[contenthash:6].css'),
3845
new HtmlWebpackPlugin({
3946
filename: '../index.html',
4047
template: config.commonPath.indexHTML,
4148
chunksSortMode: 'none'
4249
})
43-
]);
50+
);
4451

4552
module.exports = config;

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"author": "Ken Berkeley <kenberkeley@foxmail.com>",
55
"version": "0.1.0",
66
"scripts": {
7-
"start": "cross-env NODE_ENV=development node build/dev-server.js",
8-
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config build/webpack.prod.conf.js"
7+
"start": "cross-env NODE_ENV=development node build/dev.js",
8+
"build": "cross-env NODE_ENV=production node build/prod.js"
99
},
1010
"repository": {
1111
"type": "git",
@@ -20,8 +20,7 @@
2020
"react-router": "^2.7.0",
2121
"react-router-redux": "^4.0.5",
2222
"redux": "^3.5.2",
23-
"redux-thunk": "^2.1.0",
24-
"superagent": "^2.2.0"
23+
"redux-thunk": "^2.1.0"
2524
},
2625
"devDependencies": {
2726
"babel-core": "^6.0.0",
@@ -36,6 +35,7 @@
3635
"babel-runtime": "^6.9.0",
3736
"browser-sync": "^2.11.1",
3837
"browser-sync-webpack-plugin": "^1.0.1",
38+
"clean-webpack-plugin": "^0.1.13",
3939
"connect-history-api-fallback": "^1.1.0",
4040
"copy-webpack-plugin": "^3.0.1",
4141
"css-loader": "^0.23.0",
@@ -49,23 +49,24 @@
4949
"express-favicon": "^1.0.1",
5050
"extract-text-webpack-plugin": "^0.9.1",
5151
"file-loader": "^0.8.4",
52+
"html-loader": "^0.4.3",
5253
"html-webpack-plugin": "^1.7.0",
5354
"inject-loader": "^2.0.1",
5455
"json-loader": "^0.5.4",
5556
"less": "^2.6.0",
5657
"less-loader": "^2.2.2",
5758
"node-sass": "^3.4.2",
59+
"nyan-progress-webpack-plugin": "^1.1.4",
5860
"react-hot-loader": "^1.3.0",
5961
"redux-devtools": "^3.3.1",
6062
"redux-devtools-dock-monitor": "^1.1.1",
6163
"redux-devtools-log-monitor": "^1.0.11",
6264
"redux-logger": "^2.6.1",
63-
"rimraf": "^2.5.0",
6465
"sass-loader": "^3.2.0",
66+
"style-loader": "^0.13.1",
6567
"url-loader": "^0.5.7",
6668
"webpack": "^1.12.2",
6769
"webpack-dev-middleware": "^1.4.0",
68-
"webpack-dev-server": "^1.14.1",
6970
"webpack-hot-middleware": "^2.6.0",
7071
"why-did-you-update": "0.0.8"
7172
},

src/app.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,25 @@ ReactDOM.render(
3737
MOUNT_NODE
3838
)
3939

40+
// === Webpack 处理 assets,取消注释即可进行测试 === //
41+
/* 处理 less / sass */
42+
// import 'ASSET/less/normalize.less'
43+
// import 'ASSET/scss/normalize.scss'
44+
45+
/* 处理 img,小于 10KB 的转为 base64,否则使用 URL */
46+
// import base64 from 'ASSET/img/smaller.png'
47+
// import url from 'ASSET/img/larger.png'
48+
49+
// function appendImgToBody(content) {
50+
// const img = document.createElement('img')
51+
// img.src = content
52+
// document.body.appendChild(img)
53+
// }
54+
55+
// appendImgToBody(base64)
56+
// appendImgToBody(url)
57+
58+
4059
/**
4160
* 【拓展】
4261
* react-redux 的 Provider 中传入的属性

src/assets/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
放置需要经由 Webpack 处理的文件
2+
e.g. img css font 等

src/assets/img/larger.png

12.1 KB
Loading

src/assets/img/smaller.png

3.91 KB
Loading

0 commit comments

Comments
 (0)