Это развитие заметки Установка Webpack, Babel, LESS на виртуальный хостинг. Там все хорошо, но недостаток способа в том, что мы гененрируем один единственный файл, рассчитанный на старые браузеры. При этом мы получаем хорошую совместимость, но мы не используем в полное мере возможности современных браузеров. Хуже того, мы вынуждены отдавать им код большего размера, чем это им необходимо, и пролетаем с лучшей производительностью, которую может нам дать ES6.
Идея в том, чтобы отдавать браузерам, поддерживающим ES6 современную версию кода, а старым - обработанную babel.
К счастью, этого нетрудно добиться. Подключать скрипты будем так:
<!-- modern -->
<script type="module" src="modern.mjs"></script>
<!-- legacy -->
<script nomodule src="legacy.js"></script>
Для современных браузеров указан атрибут type="module". Старые его не поймут и не будут загружать этот файл. Современные же, увидев у второго атрибут nomodule, пропустят этот файл. Старые, напротив, загрузят. Кроме того, для современных браузеров используется расширение файла mjs.
Здесь есть своя ложка дегдя, связанная с Safari 10.1, который не понимает nomodule, но эту проблему можно решить добавив инлайновый JS сниппет до вызова <script nomodule>.
Поэтому наша задача - как сгенерировать два разных бандла. Делать это будем запуская webpack с двумя разными конфигурациями: в одной делаем только бандл, не используя babel, во второй - полную обработку с добавлением полифилов и совместимости с ES5.
Будем считать, что node и babel уже установлены, как я описывал в Установка Webpack, Babel, LESS на виртуальный хостинг в пунктах 1 - 4. Дальше начинаются отличия.
Дополнительно потребуется модуль core-js:
npm install --save core-js@3
Подумав, я отказался от .babelrc и .browserslistrc. Слишком много разных файлов, становится неудобно.
Список поддерживаемых браузеров задаем в package.json:
"browserslist": [
"IE 11",
"last 2 versions"
],
Предположим, что исходные файлы у нас лежат в директории ./src
в корне проекта. В директории ./www
- корень веб-сервера. Бандлы помещаем в директорию ./www/assets.
Создаем webpack.config.
Подключаем нужные модули:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const autoprefixer = require('autoprefixer');
Задаем путь в корень веб-сервера:
const webPath = path.join(__dirname, "/www/assets");
Задаем вспомогательные переменные. Пути поиска модулей:
const scriptResolve = {
modules: [
path.resolve('./src/js'),
path.resolve('./node_modules')
]
};
Правила обработки стилей. У меня предполагается less:
const cssRules = {
test: /\.less$/,
use:
[
{
loader: 'style-loader'
},
MiniCssExtractPlugin.loader,
{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer
],
sourceMap: true
}
},
{
loader: 'less-loader'
}
]
};
Собственно, то, ради чего все затевалось. Современная конфигурация:
const modernConfig = {
entry: ["./src/js/main.js", "./src/styles/main.less"],
output: {
publicPath: '/',
path: webPath,
filename: "js/[name].modern.[chunkhash].mjs"
},
resolve: scriptResolve,
devtool: "source-map",
//watch: true,
module: {
rules:
[
cssRules
]
},
plugins: [
new HtmlWebpackPlugin({
inject: false,
hash: true,
template: "./src/modern.webpack-gen.tpl",
filename: "templates/modern.webpack-gen.tpl"
}),
new MiniCssExtractPlugin({
filename: "css/[name].[chunkhash].css"
})
]
};
Здесь, как видно, используется только генерация бандла javascript и css. В качестве html-шаблона используется файл modern.webpack-gen.tpl. Его содержание:
<link rel="stylesheet" href="/assets<%=htmlWebpackPlugin.files.chunks.main.css %>" />
<script type="module" src="/assets<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script>
Совместимая конфигурация с использованием babel:
const legacyConfig = {
entry: ["./src/js/main.js"],
output: {
publicPath: '/',
path: webPath,
filename: "js/[name].legacy.[chunkhash].js"
},
resolve: scriptResolve,
devtool: "source-map",
//watch: true,
module: {
rules:
[
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options:
{
presets:
[
[
"@babel/preset-env",
{
useBuiltIns: "entry",
//debug: true,
corejs: "3.0.0",
targets:
{
esmodules: false
}
}
]
]
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
inject: false,
hash: true,
template: "./src/legacy.webpack-gen.tpl",
filename: "templates/legacy.webpack-gen.tpl"
})
]
};
Файл шаблона legacy-бандла:
<script nomodule src="/assets<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script>
Теперь надо экспортировать обе конфигурации:
module.exports = [
modernConfig, legacyConfig
];