Андреев
Сергей
- не таск ранер
- модульный сборщик на js
<script src="jquery.js"></script>
<script src="plugin1.js"></script>
<script src="plugin2.js"></script>
<script src="plugin3.js"></script>
<script src="init.js"></script>
script tag
$ cat jquery.js \
plugin1.js plugin2.js plugin3.js \
init.js \
> bundle.js
cat
<script src="bundle.js"></script>
var concat = require('gulp-concat');
gulp.task('scripts', function() {
return gulp.src(['./lib/file3.js', './lib/file1.js', './lib/file2.js'])
.pipe(concat('all.js'))
.pipe(gulp.dest('./dist/'));
});
Таск ранер
<script src="bundle.js"></script>
- плохо расширяемые
- плохо подходили для больших приложений
Надо: быть максимально модульным
Все есть модули
любой файл модуль
любой модуль есть js
процес сборки можно полностью переопределить
/***/ function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(2)();
exports.push([module.id, ".webpack {\n color: red;\n}", ""]);
/***/ },
// app.js
var css = require("css!./file.css");
// file.css
.webpack {
color: red;
}
$ webpack
Hash: 35409495c2b26d7d5468
Version: webpack 1.12.13
Time: 874ms
Asset Size Chunks Chunk Names
bundle.js 3.28 kB 0 [emitted] app
[0] ./app.js 36 bytes {0} [built]
+ 2 hidden modules
- разделение на чанки
- асинхроная загрузка
- оптимизация
- строит дерево зависимостей
- модули
- конфиг
- cli
- загрузчики
- Плагины
$: npm i -g webpack
$: webpack <entry> <output>
$: webpack [--color] [--watch] [--config]
module.exports = {
entry: {
app: "./app.js"
},
output: {
path: "dist",
filename: "bundle.js"
}
}
webpack.config.js
'use strict';
const ISDEV = process.env.NODE_ENV === 'development';
const webpack = require('webpack');
var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var autoprefixer = require('autoprefixer');
module.exports = {
cache: true,
context: path.join(__dirname, '/app'),
entry: {
app:'./main',
vendors: './vendors',
styles: 'less/main.less'
},
watch: ISDEV,
watchOptions: {
aggregateTimeout: 100
},
devtool: ISDEV ? "cheap-inline-module-source-map" : null,
output: {
path: path.join(__dirname,'/../www/assets/'), //path(__dirname,'../www/js')
filename: '[name].min.js',
publicPath: '/assets/'
},
resolveLoader: {
modulesDirectories: ['node_modules'],
moduleTemplates: ['*-loader'],
extensions: ['', '.js']
},
resolve: {
alias: {
'jquery': 'jquery/dist/jquery.js',
'jquery.ui': 'jquery-ui-1.10.4.custom.js',
'jquery.chosen': 'chosen.jquery.min.js',
'jquery.autosize': 'jquery.autosize.js',
'jquery.ba-throttle-debounce': 'jquery.ba-throttle-debounce.min.js',
'jquery.maskedinput': 'jquery.maskedinput.min.js',
'jquery.tmpl': 'jquery.tmpl.js',
'jquery.tokeninput': 'jquery.tokeninput.js',
'jquery.select2': 'select2.min.js',
'jquery.zebra-datepicker': 'zebra_datepicker.js',
hyperd: 'hyperd.min.js',
$: 'jquery'
},
root: path.join(__dirname, '/app'),
extensions: ['', '.js'],
modulesDirectories: ['libs','bower_components','node_modules'],
fallback: path.join(__dirname, 'app/helpers')
},
module: {
loaders: [
{
test: /\.js$/,
include: /\/app/,
exclude: /\/libs\/less/,
loader: 'babel?optional[]=runtime'
},
{
test: /^jquery\./,
include: /\/libs/,
loader: 'imports?$=jquery&jQuery=jquery'
},
{
test: /\.hbs$/,
include: /\/app/,
loader: 'handlebars'
},
{
test: /\.less$/,
include: /\/app\/less/,
loader: ExtractTextPlugin.extract('css?root=./../../&!postcss!less')
},
{
test: /\.(jpe?g|png|gif)$/i,
include: /\/images/,
loaders: function () {
if (ISDEV) {
return [
'url?limit=500000&name=images/[path][name].[ext]'
]
} else {
return [
'url?limit=5000&name=images/[path][name].[ext]',
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
]
}
}()
},
{
test : /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
include: /\/fonts/,
loader : 'url?limit=5000&name=fonts/[name].[ext]',
}
]
},
plugins: [
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
isDev: JSON.stringify(ISDEV)
}),
new ExtractTextPlugin('app.min.css',{allChunks: true}),
new webpack.ProvidePlugin({
'windows.jQuery': 'jquery',
'windows.$': 'jquery'
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.min.js'),
],
postcss: function () {
return !ISDEV? [autoprefixer] : [];
}
};
if (!ISDEV) {
module.exports.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
// don't show unreachable variables etc
warnings: false,
drop_console: true,
unsafe: true
}
})
);
}
webpack.config.js
- преобразуют данные из одного формата в другой;
- могут добавлять в упаковку другие модули;
- как правило, делают одну трансформацию;
- работают только с одним модулем.
loaders: [
{
test: /\.js$/,
include: /\/app/,
loader: 'babel?optional[]=runtime'
},
{
test: /^jquery\./,
include: /\/libs/,
loader: 'imports?$=jquery&jQuery=jquery'
},
{
test: /\.hbs$/,
include: /\/app/,
loader: 'handlebars'
},
{
test: /\.less$/,
include: /\/app\/less/,
loader: ExtractTextPlugin.extract('css?root=./../../&!postcss!less')
},
{
test: /\.(jpe?g|png|gif)$/i,
include: /\/images/,
loaders: 'url?limit=500000&name=images/[path][name].[ext]'
},
{
test : /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
include: /\/fonts/,
loader : 'url?limit=5000&name=fonts/[name].[ext]',
}
]
- имеют доступ ко всем модулям (до и после трансформации);
- могут добавлять в упаковку свои модули (например, «runtime»);
- имеют доступ ко всем ресурсам, создаваемым после упаковки;
- применяются для изменения конфигурации сборки, оптимизации, добавления в модули каких-то объектов, горячего обновления ресурсов.
"scripts": {
"watch": "webpack -d --progress --colors --watch",
"dev": "env NODE_ENV=development webpack --progress --colors",
"build": "webpack --progress --colors",
"analyze": "webpack --progress --profile --json > stats.json",
"size": "cat stats.json | analyze-bundle-size"
},
// webpack.config.js
const ISDEV = process.env.NODE_ENV === 'development';
module.exports = {
watch: ISDEV,
devtool: ISDEV ? "cheap-inline-module-source-map" : null,
}
if (!ISDEV) {
module.exports.plugins.push(
new webpack.optimize.UglifyJsPlugin()
);
}
/static imports
import _ from 'lodash'
// dynamic imports
require.ensure([], function(require) {
let contacts = require('./contacts')
})
require.ensure([], function(require) {
require.include("./file");
require("./file2");
});
Код, который узнает о факте обновления, должен написать разработчик.
для react уже есть
var ExtractTextPlugin = require('extract-text-webpack-plugin');
...
{
test: /\.less$/,
include: /\/app\/less/,
loader: ExtractTextPlugin.extract('css?root=./../../&!postcss!less')
},
DedupePlugin
CommonsChunkPlugin
UglifyJsPlugin
product build - 42 564 ms
dev build - 21 262 ms
dev watch js - 400 - 2 500 ms
dev watch less - 2 000 - 5 000 ms
jQuery плагины