半年来一直在做一个 PaaS 的项目,比较关注市面上的相关产品。最近发现一个叫做 wercker 的项目,感觉做的还不错,介绍一下。
目前有很多的平台(如 mesos, rancher, kubernetes)都支持以 docker image
的形式进行应用的部署,但是却没有很多的工具帮助将 ci/cd 与这些平台进行更好的对接。而 wercker 的口号是 From code to container
,强调自己可以做 ci/cd 的事情将代码转化为容器。那么之后就可以将这个容器作为交付的内容在需要的环境进行部署了。
pipeline as cde
wercker 提供一个类似于 ansible
的 wercker.yml
并提供与 ansible
类似的自定义命令来做部署的工作。
自定义命令的功能非常的强大,种类也非常的丰富。例如 npm-install
安装 node
的依赖,internal/docker-push
将生成的 image
上传到 docker registry
,marathon-deploy
将应用部署到 marathon
平台。
整个 pipeline
可以通过这些命令拼装起来,所有的 pipeline 都可以通过一个 wercker.yml
文件进行管理。
本地环境
wercker 有一个命令行工具 wercker-cli
支持在本地通过 docker
和 wercker-cli
构建一个本地的开发环境,并且支持在本地环境提供 backing service
。
多 vendor 支持
wercker 可以和多个 PaaS
对接的,包括 heroku
kubernetes
marathon
ecs
等。这一点非常的难能可贵,想象一下,作为一个开发者,当有了类似于 ecs
或者 heroku
这样的公有云之后再配合 wercker
这样的工具可以快速的搭建 pipeline
以及完成以前需要花费更多时间才能得到的 ci/cd
开发效率真是大大的提升。
ui 界面
提供一个 ui 界面管理整个 pipeline
与 github & bitbucket 对接
支持 github bitbucket hook,在有新的 commit 之后自动构建、部署。
管理关键数据,有些数据不适合存放在 wercker.yml
中,例如 heroku
的 accesskey
,docker-hub
的账号密码。
webpack + redux + react 开发前端最终会将所有的 js 依赖打包成为一个(或者几个,因配置不同而不同)js 文件。虽然 webpack
很好的帮助我们解决了依赖的问题,避免了一大堆分散的 js 文件出现在页面里,但是最终打包出来的 js 文件依然会变成所有依赖的 js 的 size 的总和,成为前端页面响应速度的巨大负担。不过通过一些调优可以最大化的减少最终的打包文件的大小并提升运行性能。
dev
& production
env首先,我们可以通过设定不同的 NODE_ENV
环境变量去控制在不同的环境下引入的配置。通过在 webpack.config.js
中读取 process.env.NODE_ENV
可以为 webpack
提供不同的 plugin
用于控制 webpack
的打包机制。下面是一个例子:
var plugins = process.env.NODE_ENV === 'production' ? [
new webpack.DefinePlugin({
API_PREFIX: JSON.stringify(process.env.API_PREFIX) || '"{{API_PREFIX}}"',
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
] : [
new webpack.DefinePlugin({
API_PREFIX: JSON.stringify(process.env.API_PREFIX) || '"{{API_PREFIX}}"',
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
})
];
其中 DefinePlugin
可以为 webpack
提供全局变量,这里我们利用它将 node
中的 process.env.NODE_ENV
转换为 webpack
构建最终的 js
时用到的全局变量。如果直接在 webpack
构建时处理的 js
文件中直接引用 nodejs
才能读取的 process.env
是不会有任何效果的。
然后,我们通过这个 process.env.NODE_ENV
的不同加载不同的 configureStore.js
,将在 production
环境下用不到的 redux
中间件清理掉。
if (process.env.NODE_ENV === 'production') {
module.exports = require('./configureStore.prod')
} else {
module.exports = require('./configureStore.dev')
}
configureStore.prod.js
:
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from '../reducers/index'
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
return store;
}
configureStore.dev.js
:
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger';
import rootReducer from '../reducers/index'
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(thunk, createLogger()));
return store;
}
webpack
插件压缩输出文件在各种前端构建工具中都受不了 uglify
的过程,webpack
也不例外。在上文 webpack.config.js
的例子中对 production
环境下的 webpack
就提供了各种插件用于压缩文件。
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
webpack-bundle-size-analyzer
分析依赖大小如果以上两种优化都做了(尤其是 uglify)那么恭喜你,你的 js 输出文件已经是处理前的三分之一了。我在自己的一个项目中最终的 bundle.js
文件从 2.2MB
降到了 800KB
效果还是非常好的。
但是 800KB
还是好大的一个文件,如果还想继续优化呢?那就需要有针对性的进行优化了。我们在开发的过场中依赖了乱七八糟的 package
那么是不是能通过减少依赖或者是更换依赖的方式来进一步的减少最终输出文件的大小呢?那么,首先需要知道每个依赖占据的比例了。这里我们采用一个工具 webpack-bundle-size-analyzer
分析所有依赖的大小。
npm install -g webpack-bundle-size-analyzer
然后
webpack --json | webpack-bundle-size-analyzer
就可以看到所有依赖的大小占比了:
react: 667.34 KB (28.9%)
fbjs: 33.59 KB (5.03%)
<self>: 633.74 KB (95.0%)
moment: 454.54 KB (19.7%)
bootstrap: 273.93 KB (11.9%)
jquery: 251.51 KB (10.9%)
react-router: 159.31 KB (6.91%)
history: 52.69 KB (33.1%)
deep-equal: 3.8 KB (7.22%)
query-string: 1.62 KB (3.08%)
strict-uri-encode: 182 B (10.9%)
<self>: 1.45 KB (89.1%)
<self>: 47.26 KB (89.7%)
warning: 1.76 KB (1.11%)
invariant: 1.48 KB (0.929%)
hoist-non-react-statics: 1.35 KB (0.850%)
<self>: 102.03 KB (64.0%)
formsy-react-components: 36.24 KB (1.57%)
classnames: 2.58 KB (7.11%)
<self>: 33.66 KB (92.9%)
superagent: 30.57 KB (1.32%)
component-emitter: 3.11 KB (10.2%)
reduce-component: 405 B (1.29%)
<self>: 27.06 KB (88.5%)
formsy-react: 30.55 KB (1.32%)
form-data-to-object: 1.19 KB (3.91%)
<self>: 29.36 KB (96.1%)
axios: 29.18 KB (1.26%)
redux: 25.8 KB (1.12%)
lodash: 3.34 KB (12.9%)
symbol-observable: 451 B (1.71%)
<self>: 22.02 KB (85.4%)
react-redux: 25.54 KB (1.11%)
lodash: 3.34 KB (13.1%)
invariant: 1.48 KB (5.80%)
hoist-non-react-statics: 1.35 KB (5.30%)
<self>: 19.37 KB (75.8%)
redux-logger: 8.29 KB (0.359%)
style-loader: 6.99 KB (0.303%)
webpack: 3 KB (0.130%)
node-libs-browser: 2.76 KB (91.8%)
process: 2.76 KB (100%)
<self>: 0 B (0.00%)
<self>: 251 B (8.17%)
object-assign: 1.95 KB (0.0844%)
css-loader: 1.47 KB (0.0638%)
redux-thunk: 306 B (0.0130%)
superagent-prefix: 198 B (0.00838%)
react-dom: 63 B (0.00267%)
<self>: 300.1 KB (13.0%)
这里在优化前的输出情况,其中 react
最大,占整个输出的 28.9%
,未压缩前 bundle.js
为 2.24MB
。react
是我们的核心依赖,是没什么办法做优化了,但是占比第二高的 moment
仅仅是一个用于日期格式化的工具,占比如此之高实在是可疑。通过搜索其他的可选方案,我将 moment
替换为 date-fns
,bundle 文件减小为 1.7MB
压缩后文件减小为 558KB
。
如果你真的还觉得太大了...那,就只有靠 nginx 那边做 gzip
优化了...
gzip on;
gzip_types text/plain application/xml text/css text/html application/javascript;
最近开始利用业余时间采用 react + redux
的前端架构山寨一个金数据(或者说是 WuFoo,毕竟两个东西看起来真的很像)以增加自己对这些框架的熟练度。在这个过程中记录下一些自己遇到的坑。今天就是一个 webpack
如何和 bootstrap
结合的坑。
为了在项目之初就一个不是那么丑的界面,都会选择一些比较成熟的前端 css 框架。bootstrap
是比较流行的一个。bootstrap
一方面是基本的 css
另一方面还有一些 jQuery
的插件形式的类库支持其中的一些组件,当然还有一些它所需要的字体文件。那么这里问题就来了:
webpack
中引入 jQuery
以及它的插件webpack
引入一些其他类型的文件,例如字体首先,我们还是要安装 bootstrap
以及它所依赖的 jquery
。
npm install --save bootstrap-sass jquery
这里顺便说一句,虽然 jquery
看似过时了,但是它所构建的生态是非常庞大的,尤其是像 jquery-ui
这样的东西可以说是一些富交互应用所必须的。那么如何将 react
的 component
和 jquery
的一些组件很好的结合是在选择 react
这样的框架之初就考虑进去的。后面在涉及到一些复杂的交互的时候会出现 jquery-ui
与 react
一起使用的例子。
然后,我们可以在 webpack.config.js
中以 entry 的形式引入 bootstrap-loader
var path = require("path");
module.exports = {
devtool: 'cheap-module-source-map',
entry: [
'bootstrap-loader',
'./index.js'
],
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.scss$/,
exclude: /node_modules/,
loader: "style!css!sass"
}
]
}
};
注意看 entry
本来就是一个数组,我们可以在这里引入多个入口。
做了一番调研之后发现其实没必要自己把所有的事情都做了,有这么一个 bootstrap-loader
可以帮助在 webpack
的项目中引入 bootstrap
。
npm install --save-dev bootstrap-loader
不过单单是安装它是不过的,其实 bootstrap-loader
所做的事情就是帮助我们把各种样子的文件引入到我们的项目中,那么为了处理不同类型的文件需要一些其他的 loader
的支持(前面的博客有提及 webpack
只能处理 js
如果需要处理其他类型的东西就需要 loader
的帮助)。这里,我们还要引入一大堆的 loader
。
npm install --save-dev resolve-url-loader url-loader file-loader imports-loader
其中 file-loader
用于加载其他类型的文件,url-loader
和 file-loader
类似,只是在文件比较小的时候返回 Data Url 的形式。resolve-url-loader
和之前提到的 sass-loader
一起使用,用于处理 sass
中 url()
的路径。这些 loader
都要和 webpack.config.js
中的 loaders
配置项配合使用:
var path = require("path");
module.exports = {
...
module: {
loaders: [
...
{
test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "url?limit=10000"
},
{
test: /\.(ttf|eot|svg)(\?[\s\S]+)?$/,
loader: 'file'
}
]
}
};
imports-loader
是个很有意思的 loader
它定义了一个简单的格式用于引入使用它的类库所需要的依赖。
module.exports = {
...
module: {
loaders: [
...
{
test: /bootstrap-sass\/assets\/javascripts\//,
loader: 'imports?jQuery=jquery'
}
]
}
};
如上所示,在 webpack.config.js
中加入这样一个 loader
。其中说明在引入 bootstrap
下的 javascripts
时,为他们提供 jQuery
这样的变量。那么 imports-loader
会在引入 bootstrap
的 js
之前为他们提供如下的代码:
var jQuery = require('jquery');
估计在后续使用 jquery
的其他东西的时候还会用到它的。