От автора: Webpack 4 официально вышел! А вы знали, что его называют Legato? В новой версии множество новых функций и улучшений. Игра Sean T. Larkin поднялась на 3 уровня в Webpack 4. Если коротко, Webpack4 на 98% быстрее. В нем есть Mode, #0CJS и ощутимые настройки по умолчанию. Теперь деление на части – это настраиваемое значение по умолчанию для разных режимов. Вместе с поддержкой .mjs и WebAssembly были представлены типы модулей. JS будущего уже здесь!
Я очень легко мигрировал один базовый VueJs стартер на Webpack4. Пришлось немного подумать, так как некоторые плагины до сих пор не обновлены до Webpack4. Однако когда поддержка появится, перенести существующие проекты на последнюю версию можно будет за несколько минут. Я начал с #0CJS (Zero-Config JS), в котором используются стандартные режимы. Однако после интеграции Vue я добавил пару базовых настроек для стартер-проекта, среди которых роутинг, тестирование и настраиваемый HTML файл.
Перейдем к коду: я использовал 0-Configuration режимы для production/development билдов и webpack-dev-server для разработки. В конец статьи я добавил все статьи по Webpack4, которые смог найти, чтобы облегчить вам процесс миграции. Подзаголовки указывают на код в репозитории Git. Можете посмотреть его прямо там, кликнув по заголовку.
1. Package.json:
Первичный package.json – простой. Скрипт для разработки, сборки и еще один для webpack-dev-server. Также есть скрипты для тестирования.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
{ "name": "webpack-4-quickstart", "version": "1.0.0", "description": "> Webpack 4 tutorial: VueJs starter-kit, from 0 Conf to Production Mode", "main": "index.js", "scripts": { "start": "cross-env NODE_ENV=development webpack-dev-server --inline --hot", "dev": "webpack --mode development", "build": "webpack --mode production", "test": "node_modules/.bin/karma start", "test-grep": "node_modules/.bin/karma start --grep" }, "repository": { "type": "git", "url": "git+////github.com/nnupoor/webpack4vue.git" }, "keywords": [], "author": "Neha Nupoor", "license": "MIT", "bugs": { "url": "//github.com/nnupoor/webpack4vue/issues" }, "homepage": "//github.com/nnupoor/webpack4vue#readme", "devDependencies": { "autoprefixer-loader": "^3.2.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.1", "chai": "^4.1.1", "cross-env": "^5.1.3", "css-loader": "^0.23.1", "extract-text-webpack-plugin": "^2.1.0", //not working currently. "file-loader": "^0.8.4", "html-webpack-plugin": "webpack-contrib/html-webpack-plugin", //is a patch right now. "inject-loader": "^3.0.1", "istanbul": "^0.4.5", "karma": "^1.7.0", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^2.2.0", "karma-coverage": "^1.1.1", "karma-mocha": "^1.3.0", "karma-requirejs": "^1.1.0", "karma-sinon": "^1.0.5", "karma-webpack": "^2.0.4", "karma-webpack-grep": "^1.0.1", "mocha": "^3.5.0", "node-sass": "^4.5.3", "requirejs": "^2.3.5", "sass-loader": "^6.0.5", "sinon": "^3.2.1", "style-loader": "^0.14.1", "surge": "^0.18.0", "svg-sprite-generator": "0.0.7", "svg-sprite-loader": "^0.3.0", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.13", "vue-resource": "^1.2.1", "vue-router": "^2.3.0", "webpack": "^4.0.0", "webpack-cli": "^2.0.8", "webpack-dev-server": "^3.0.0", "webpack-serve": "^0.1.4" }, "dependencies": { "vue": "^2.5.13" } } |
После настройки package.json необходимо перейти к настройке файла webpack.config.js. Я настроил базовый файл, но его можно разделить или улучшить в будущем. Кода ниже должно быть достаточно для начала.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
const path = require('path'); const fs = require('fs'); const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const NODE_ENV = process.env.NODE_ENV; const setPath = function(folderName) { return path.join(__dirname, folderName); } const isProd = function() { return (process.env.NODE_ENV === 'production') ? true : false; } const buildingForLocal = () => { return (NODE_ENV === 'development'); }; const setPublicPath = () => { let env = NODE_ENV; if (env === 'production') { return '//your-host.com/production/'; } else if (env === 'staging') { return '//your-host.com/staging/'; } else { return '/'; } }; const extractCSS = new ExtractTextPlugin({ filename: "css/styles.[hash].css",//"[name].[contenthash].css", disable: process.env.NODE_ENV === "development" }); const extractHTML = new HtmlWebpackPlugin({ title: 'History Search', filename: 'index.html', inject: true, template: setPath('/src/tpl/tpl.ejs'), environment: process.env.NODE_ENV, isLocalBuild: buildingForLocal(), imgPath: (!buildingForLocal()) ? 'assets' : 'src/assets' }); const config = { /** * You can use these too for bigger projects. For now it is 0 conf mode for me! */ // entry: { // build: path.join(setPath('src'), 'main.js'), // vendor: path.join('setPath('src'), 'vendor.js') // }, // output: { // path: buildingForLocal() ? path.resolve(__dirname) : setPath('dist'), //this one sets the path to serve // publicPath: setPublicPath(), // filename: buildingForLocal() ? 'js/[name].js' : 'js/[name].[hash].js' // }, optimization:{ runtimeChunk: false, splitChunks: { chunks: "all", //Taken from //gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693 } }, resolveLoader: { modules: [setPath('node_modules')] }, mode: buildingForLocal() ? 'development' : 'production', devServer: { historyApiFallback: true, noInfo: false }, plugins: [ extractHTML, // extractCSS, new webpack.DefinePlugin({ 'process.env': { isStaging: (NODE_ENV === 'development' || NODE_ENV === 'staging'), NODE_ENV: '"'+NODE_ENV+'"' } }) ], module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { js: 'babel-loader' } } }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: [{ loader: "babel-loader", options: { presets: ['es2015'] } }] }, { test: /\.css$/, use: extractCSS.extract({ fallback: "style-loader", use: ["css-loader", "autoprefixer-loader"] }) }, { test: /\.scss$/, use: !buildingForLocal() ? extractCSS.extract({ fallback: "style-loader", use: ['css-loader', 'autoprefixer-loader', 'sass-loader'] }) : [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "sass-loader" // compiles Sass to CSS }] }, { test: /\.svg$/, loader: 'svg-sprite-loader' }, { test: /\.(png|jpg|gif)$/, loader: 'file-loader', query: { name: '[name].[ext]?[hash]', useRelativePath: buildingForLocal() } } ] }, }; module.exports = config; |
2.1. Настройка HtmlWebpackPlugin:
Если просто, то я использовал HtmlWebpackPlugin для получения шаблона .ejs и наполнил его сгенерированным js/css. HtmlWebpackPlugin официально не работает, но я подключил патч от Webpack.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const extractHTML = new HtmlWebpackPlugin({ title: 'History Search', filename: 'index.html', inject: true, template: setPath('/src/tpl/tpl.ejs'), environment: process.env.NODE_ENV, isLocalBuild: buildingForLocal(), imgPath: (!buildingForLocal()) ? 'assets' : 'src/assets' }) // Inside config object const config = { . . plugins: [extractHTML], } |
2.2. Извлечение CSS с помощью ExtractTextPlugin:
Далее мы с помощью ExtractTextPlugin извлечем CSS , чтобы упростить разбиение CSS. Прямо сейчас этот плагин не работает. (*Правка: они выпустили бета версию плагина, о которой я не знал. Sean T. Larkin сказал мне об этом, и я обновил код, чтобы он заработал.*)
1 2 3 4 5 6 7 8 9 10 |
const extractCSS = new ExtractTextPlugin({ filename: "css/styles.[hash].css",//"[name].[contenthash].css", disable: process.env.NODE_ENV === "development" }); // Inside config object const config = { . . plugins: [extractHTML], } |
2.3. Настройка режимов:
Режим конфига Webpack можно указать явно в объекте конфига.
1 2 3 4 5 |
const config = { . . mode: buildingForLocal() ? 'development' : 'production' } |
2.4. Настройка конфигураций оптимизации:
Также можно настроить объект «optimization». Плагины типа ‘NoEmitOnErrorsPlugin’, ‘ModuleConcatenationPlugin’, ‘NamedModulesPlugin’ переехали на конфиг оптимизации и имеют значения по умолчанию на основе выбранного режима. ‘CommonsChunkPlugin’ был удален, теперь его конфиг находится внутри объекта optimization. Его можно настроить следующим образом:
1 2 3 4 5 6 |
optimization:{ runtimeChunk: false, splitChunks: { chunks: "all", //Taken from //gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693 } } |
Чем хороша эта миграция – я понял свои ошибки, просто прочитав их в консоли. Во время настройки конфига выше я допустил много ошибок, а благодаря информативным ошибкам, я смог все правильно настроить.
2.5. Настройка devServer для webpack-dev-server:
Перейдем к настройке devServer. Я заметил, что если установить noInfo: true, то я не получу никакой информации о том, на каком порту запущен сервер, в отличие от предыдущих версий. Для каких-либо заключений нужно еще покопаться. Сейчас значение false.
1 2 3 |
devServer: { noInfo: false } |
2.6. Настройка модулей
Ничего необычного, старый Vue, JS, CSS, SCSS, SVG и загрузчики изображений.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { // postcss: [require('postcss-cssnext')()] // options: { // extractCSS: true // } loaders: { js: 'babel-loader' } } }, { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: [{ loader: "babel-loader", options: { presets: ['es2015'] } }] }, // { // test: /\.css$/, // use: extractCSS.extract({ // fallback: "style-loader", // use: ["css-loader", "autoprefixer-loader"] // }) // }, { test: /\.scss$/, use: //!buildingForLocal() ? // extractCSS.extract({ // fallback: "style-loader", // use: ['css-loader', 'autoprefixer-loader', 'sass-loader'] // }) : [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader" // translates CSS into CommonJS }, { loader: "sass-loader" // compiles Sass to CSS }] }, { test: /\.svg$/, loader: 'svg-sprite-loader' }, { test: /\.(png|jpg|gif)$/, loader: 'file-loader', query: { name: '[name].[ext]?[hash]', useRelativePath: buildingForLocal() } } ] } |
Как видите, я закомментировал функции extractCSS. Они не работают. Scoped CSS в Vue – это победа! (*Правка: сейчас работает. См. выше.*)
Вот и все. Это базовая конфигурация/миграция на Webpack4, которую мне понадобилось провести для запуска простого проекта VueJs с нуля. Я добавил базовые тесты, роутинг, следуя структуре страницы, макета и компонентов.
Заметка: в коде выше я удалил пару строк для простоты объяснения. В репозитории Git лежит весь необходимый код для тех, кто хочет начать работу в VueJs и Webpack4. Я готов учиться и отвечать на любые возникшие вопросы.
Автор: neha nupoor
Источник: //medium.com/
Редакция: Команда webformyself.