Webpack
Пример настройки webpack для реакта:
npm init
npm install -save react react-dom
npm install --global webpack
npm install -save-dev webpack-dev-server webpack
npm install -save-dev babel-loader babel-core babel-preset-es2015 babel-preset-react
#webpack.config.js
const path = require('path');
module.exports = {
entry: './index',
output: {
path: path.resolve('dist'),
filename: 'bundle.js',
sourceMapFilename: 'bundle.map',
publicPath: '/dist/'
},
devtool: '#source-map',
module: {
loaders: [
{ test: /\.js$/, loader:'babel-loader', exclude: /node_modules/}
]
}
}
#.babelrc
{
"presets":[
"es2015", "react"
]
}
#index.html
<!doctype html>
<html>
<head>
<title>Sample App</title>
</head>
<body>
<div id='root'>
</div>
<script src="./dist/bundle.js"></script>
</body>
</html>
#index.js
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(
<h1>Hello!</h1>,
document.getElementById("root")
)
Построение
webpack
Запуск на дев-сервере
webpack-dev-server
Запуск с hot module replacement
webpack-dev-server --inline
Туториал:
Установка: npm install webpack -g
Создаем файл src\index.js:
console.log('hello!');
Запускаем для него webpack:
webpack src\index.js assets\bundle.js
После этого webpack генерит bundle.js с кучей сгенерированного кода. Здесь описан наш index.js и все его зависимости:
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello!');
/***/ })
/******/ ]);
В html после этого достаточно подключить один файл:
<!doctype html> <html> <body> <script src="assets/bundle.js"></script> </body> </html>
Теперь создадим более сложную конфигурацию билда:
#build.js
module.exports = {
entry: './index.js',
output: {
filename: 'assets/bundle.js'
}
}
Запуск сборки:
webpack --config build.js
Плагины
Плагины описываются в конфиге в секции plugins:
const webpack = require('webpack');
module.exports = {
entry: './index.js',
output: {
filename: 'assets/bundle.js'
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}
ПРИМЕЧАНИЕ: если при билде будет ругаться на отсутствующий модуль webpack, то надо сделать npm install webpack --save-dev
UglifyJsPlugin - Это плагин минификации, после него bundle.js выглядит так:
!function(e){function n(t){if(r[t])return r[t].exports;var o=r[t]={i:t,l:!1,exports:{}};return e[t].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r={};n.m=e,n.c=r,n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=0)}([function(e,n){console.log("hello!")}]);
Загрузчики
Описываются в секции loaders
Позволяют автоматически загружать нужные файлы (когда мы в коде загрузим их через require(...)
или import
.
Если где-то в коде есть require/import, а соответствующий лоадер не прописан, то будет ошибка компиляции.
Например, файлы json, js могут подключаться как модуль.
А CSS могут автоматически встраиваться в DOM
module: {
loaders: [
{test: /\.css$/, loader: 'style-loader!css-loader'},
{test: /\.json$/, loader: 'json-loader'},
{test: /\.hbs$/, loader: 'handlebars-loader'},
{
test: /\.(eot|woff|ttf|svg|png|jpg)$/,
loader: 'url-loader?limit=30000&name=[name]-[hash].[ext]'
},
Здесь test
означает паттерн, по которому искать, а loader
- какой загрузчик использовать.
#src/customer.json:
{"name": "Habrahabr"}
<h1>Hello, dear {{name}}</h1>
var $ = require('jquery');
// загружаем данные из JSON-файла в объект:
var customer = require('./customer.json');
// загружаем и компилируем шаблон:
var Header = require('./header.hbs');
require('./header.css');
// отдаём данные в шаблон и выводим полученный HTML
$('body').prepend(Header(customer));
test: /\.(css|scss)$/,
loaders: ExtractTextPlugin.extract({
fallbackLoader: 'style',
loader: 'css?minimize!sass!postcss' // "?" означает, что все что после, будет передано в css-loader как параметры.
// а "!" означает, что здесь применяется пайплайн справа налево и результат postcss будет передан в sass, потом в minimize
})
Для загрузки css обычно используется 2 загрузчика:
- css-loader - просто загружает css, чтобы они могли использоваться как модули через import/require - style-loader - встраивает стиль css в страницу
Типичное применение:
{
test: /\\.(css|scss)$/,
loaders: [ 'style-loader', 'css-loader'] // альтернативная запись: loader:'style!css'``
},
ExtractTextWebpack - плагин, позволяющий объединить все css файлы в один большой css-файл и подключать в html только его.
#loaders:
{
test: /\\.(css|scss)$/,
loaders: ExtractTextPlugin.extract({
fallbackLoader: 'style',
loader: 'css?minimize!sass!postcss'
})
},
#plugins:
new ExtractTextPlugin('index-[contenthash].css')
Такая запись означает, что при загрузке css/scss через require, нужно попытаться прогнать его через пайплайн postcss->sass->minimize и результат записать в один общий файл index-[contenthash].css
А если при обработке пайплайном произошла какая-либо ошибка, то используем обычный style-loader, который заинлайнит наш стиль прямо в html
Скрипты NPM
{
...
"scripts": {
"build": "webpack"
},
...
}
После этого можно вместо webpack писать npm run build
Через Webpack можно загружать: