如何从头开始创建可用于生产环境的Webpack 4配置
Webpack is a powerful bundler and dependency manager used by many enterprise-level companies as tooling for their front-end code.
Webpack是一個功能強大的打包程序和依賴項管理器,許多企業級公司都將其用作前端代碼的工具。
Typically, webpack is configured when a project is first set up, and small tweaks are then made to the config files as needed from time to time. Because of this, many developers don’t have a lot of experience working with webpack.
通常,Webpack是在首次設置項目時配置的,然后會根據需要不時對配置文件進行細微調整。 因此,許多開發人員沒有很多使用webpack的經驗。
In this hands-on tutorial, we’ll go through the basics of setting up your very own production-ready webpack config using webpack 4. We’ll discuss output management, asset management, dev and prod configs, Babel, minification, cache busting, and more.
在本動手教程中,我們將介紹使用webpack 4設置自己的可用于生產環境的webpack配置的基礎知識。我們將討論輸出管理,資產管理,開發和產品配置,Babel,最小化,緩存清除, 和更多。
Let's get started!
讓我們開始吧!
演示應用 (Demo App)
For the purposes of this demo, we'll be setting up a webpack config from scratch using webpack 4. Our app will just use vanilla JavaScript so that we don't get bogged down with any framework-specific details. The actual app code will be pretty small so that we can focus more on webpack.
出于本演示的目的,我們將使用webpack 4從頭開始設置webpack配置。我們的應用程序將僅使用原始JavaScript,這樣我們就不會陷入任何特定于框架的細節。 實際的應用程序代碼將很小,因此我們可以將更多精力放在webpack上。
If you'd like to follow along, all of the code in this article can be found in GitHub. The starting point is found here, and the finished result is found here.
如果您想繼續,可以在GitHub中找到本文中的所有代碼。 在這里找到起點,在這里找到 完成的結果 。
初始點 (Starting Point)
To begin, we'll start out with just a few files in our project directory. The directory structure looks like this:
首先,我們將從項目目錄中的幾個文件開始。 目錄結構如下所示:
webpack-demo|_ src|_ index.js|_ .gitignore|_ index.html|_ package.json|_ README.md|_ yarn.lockThe index.html file is nice and simple, just a page header and a script tag:
index.html文件非常簡單,只有頁面標題和script標簽:
<!doctype html> <html><head><title>Webpack Training 1</title></head><body><h1>Webpack Training 1</h1><script src="./src/index.js"></script></body> </html>The script tag references our ./src/index.js file, which has just a few lines of JavaScript in it that outputs the text, "Hello from webpack!":
script標記引用了我們的./src/index.js文件,該文件中只有幾行JavaScript,它會輸出文本“ Hello from webpack!”:
const p = document.createElement('p') p.textContent = 'Hello from webpack!' document.body.append(p)If you drag the index.html file into your browser, you should be able to view our simple web page:
如果將index.html文件拖到瀏覽器中,則應該能夠查看我們的簡單網頁:
安裝依賴項 (Install Dependencies)
I've included webpack and webpack-cli as devDependencies in the package.json file.
我已經將webpack和webpack-cli作為devDependencies在package.json文件中。
To install those, run:
要安裝它們,請運行:
yarn installWebpack測試運行 (Webpack Test Run)
Webpack 4 is set up as a "zero config" tool, meaning that you can run it out of the box without doing any initial configuration. Now, for any real project you will need to do some configuration, but it's nice that you can at least do a quick sanity check to ensure that webpack is able to run without having to go through a bunch of initial configuration steps.
Webpack 4被設置為“零配置”工具,這意味著您可以直接使用它,而無需進行任何初始配置。 現在,對于任何實際項目,您都需要進行一些配置,但是很好的是,您至少可以進行快速的健全性檢查,以確保webpack能夠運行而無需執行許多初始配置步驟。
So, let's check it out. Run:
因此,讓我們檢查一下。 跑:
yarn webpackYou should now see a dist directory created in your project directory. And inside it you should see a main.js file, which is our minified code.
現在,您應該在項目目錄中看到一個dist目錄。 在其中,您應該看到一個main.js文件,這是我們的main.js代碼。
Great! Webpack appears to be working.
大! Webpack似乎正在運行。
參考輸出代碼 (Reference the Output Code)
OK, now that we have JavaScript code in our dist directory, let's have our index.html file reference that. Instead of the script tag looking like this:
好的,既然我們在dist目錄中有JavaScript代碼,那么讓我們的index.html文件引用它。 代替如下所示的script標簽:
<script src="./src/index.js"></script>Let's change it to this:
讓我們將其更改為:
<script src="./dist/main.js"></script>Now, refresh the page in your browser, and you should still see the exact same output, only this time the "Hello from webpack!" text is being generated by the ./dist/main.js file now.
現在,在瀏覽器中刷新頁面,您仍然應該看到完全相同的輸出,只是這次是“ Webpack的Hello!”。 ./dist/main.js文件現在正在生成文本。
創建一個Webpack配置文件 (Create a Webpack Config File)
Now that we have webpack installed and have gone through a quick sanity check exercise, let's create an actual webpack config file. Create a file called webpack.config.js and place the following code inside it:
現在我們已經安裝了webpack并進行了快速的健全性檢查練習,讓我們創建一個實際的webpack配置文件。 創建一個名為webpack.config.js的文件,并將以下代碼放入其中:
const path = require('path')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')} }The entry property tells webpack where our source code is located. It is the "entry point" for our app.
entry屬性告訴webpack我們的源代碼在哪里。 這是我們應用程序的“入口點”。
The output property tells webpack what to call the output file and which directory to place it in.
output屬性告訴webpack如何調用輸出文件以及將其放置在哪個目錄中。
Simple enough, right?
很簡單,對不對?
Now let's create an npm script in our package.json file:
現在,讓我們在package.json文件中創建一個npm腳本:
"scripts": {"build": "webpack --config=webpack.config.js" }Now we can run our build process with the command yarn build. Go ahead and run that command to verify you have things set up properly. You could even delete your dist directory prior to running the yarn build command to verify that the directory is being generated.
現在,我們可以使用命令yarn build運行構建過程。 繼續并運行該命令以驗證是否已正確設置。 您甚至可以在運行yarn build命令之前刪除dist目錄,以驗證是否正在生成目錄。
更改輸出文件名 (Change the Output File Name)
Now, just for fun, let's change the output file name. To do this, we'll open up our webpack.config.js file and change the output property from this:
現在,只是為了好玩,讓我們更改輸出文件名。 為此,我們將打開webpack.config.js文件,并webpack.config.js更改output屬性:
output: {filename: 'main.js',path: path.resolve(__dirname, 'dist') }To this:
對此:
output: {filename: 'tacos.js',path: path.resolve(__dirname, 'dist') }Now run yarn build again to generate the output. You should see a tacos.js file in your dist directory now.
現在再次運行yarn build來生成輸出。 您現在應該在dist目錄中看到tacos.js文件。
But wait! We also see the old main.js file in our dist directory too! Wouldn't it be nice if webpack could delete the old unneeded output each time we do a new build?
可是等等! 我們還在dist目錄中也看到了舊的main.js文件! 如果webpack每次執行新的構建時都可以刪除舊的不需要的輸出,那不是很好嗎?
There's got to be a plugin for that.
必須有一個插件。
Webpack插件 (Webpack Plugins)
Webpack has a rich ecosystem of modules called "plugins", which are libraries that can modify and enhance the webpack build process. We'll explore a handful of helpful plugins as we continue to improve our webpack config throughout the rest of this article.
Webpack擁有豐富的模塊生態系統,稱為“ 插件 ”,這是可以修改和增強Webpack構建過程的庫。 我們將在本文的其余部分中繼續改進Webpack的配置,探索一些有用的插件。
CleanWebpack插件 (CleanWebpackPlugin)
OK, back to our problem. It'd be nice if we could clean up the dist directory before each new build. There's a plugin for that!
好,回到我們的問題。 如果我們能在每次新構建之前清理dist目錄,那就太好了。 有一個插件!
We can use the CleanWebpackPlugin to help us here. First, we need to install it in our project:
我們可以使用CleanWebpackPlugin在這里幫助我們。 首先,我們需要在項目中安裝它:
yarn add --dev clean-webpack-pluginTo use it, we'll simply require the plugin in our webpack.config.js file and then include it in the plugins array in our config setup:
要使用它,我們只require在webpack.config.js文件中插入插件,然后在配置設置中將其包含在plugins數組中:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin()] }Now run yarn build again, and you should see only a single output file in your dist directory. Problem solved!
現在再次運行yarn build ,您應該在dist目錄中僅看到一個輸出文件。 問題解決了!
HTMLWebpack插件 (HTMLWebpackPlugin)
One other thing that's a little annoying with our setup is that any time we change the output file name in our webpack.config.js file, we also have to change that file name we reference in our script tag in our index.html file. Wouldn't it be nice if webpack could manage that for us?
設置中的另一件令人討厭的事情是,每當我們更改webpack.config.js文件中的output文件名時,我們還必須更改index.html文件中的script標記中引用的文件名。 如果webpack可以為我們管理這不是很好嗎?
There's a plugin for that! We can use the HTMLWebpackPlugin to help us manage our HTML file. Let's install it in our project now:
有一個插件! 我們可以使用HTMLWebpackPlugin幫助我們管理HTML文件。 讓我們現在將其安裝在我們的項目中:
yarn add --dev html-webpack-pluginNow let's move our index.html file inside our src directory so that it's a sibling to the index.js file.
現在,讓我們將index.html文件移動到src目錄中,以便它成為index.js文件的同級文件。
webpack-demo|_ src|_ index.html|_ index.js|_ .gitignore|_ package.json|_ README.md|_ yarn.lockWe can also delete the script tag in our index.html file since we'll have webpack handle inserting the appropriate script tag for us. Delete that line so that your index.html file looks like this:
我們也可以刪除index.html文件中的script標簽,因為我們將使用webpack句柄為我們插入適當的script標簽。 刪除該行,以便您的index.html文件如下所示:
<!doctype html> <html><head><title>Webpack Training 1</title></head><body><h1>Webpack Training 1</h1></body> </html>Now let's require this plugin in our webpack.config.js file and then include it in the plugins array in our config setup, just like we did for the first plugin:
現在,讓我們在webpack.config.js文件中require此插件,然后將其包含在配置設置中的plugins數組中,就像我們對第一個插件所做的那樣:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),] }In those options for the HtmlWebpackPlugin, we specify the filename for what we'd like the output file to be called.
在對這些選項HtmlWebpackPlugin ,我們指定filename ,因為我們想什么輸出文件被調用。
We specify for inject that we would like our JavaScript file to be injected into the body tag by setting the value to true.
通過將值設置為true ,我們為inject指定我們希望將JavaScript文件注入到body標簽中。
And finally, for the template we supply the location of our index.html file in the src directory.
最后,對于template ,我們在src目錄中提供了index.html文件的位置。
完整性檢查 (Sanity Check)
OK, let's make sure everything is still working properly. Run yarn build, and verify that you see two files in your dist directory: index.html and main.js.
好的,讓我們確保一切仍然正常運行。 運行yarn build ,并驗證是否在dist目錄中看到兩個文件: index.html和main.js
If you look closely in your index.html file, you'll see the main.js file referenced.
如果您仔細查看index.html文件,將會看到所引用的main.js文件。
Now, open the ./dist/index.html file in your browser to verify that your page loads correctly. If you followed these steps correctly, your page should still be working:
現在,在瀏覽器中打開./dist/index.html文件,以驗證頁面是否正確加載。 如果您正確執行了這些步驟,則您的頁面仍將正常工作:
創建開發服務器 (Create a Development Server)
We've made some good improvements so far using the CleanWebpackPlugin and the HtmlWebpackPlugin. As we've made these changes, we've had to manually run the yarn build command each time to see new changes in our app. We've also just been viewing the file in our browser rather than viewing the content served from a server running locally. Let's improve our process by creating a development server.
到目前為止,我們使用CleanWebpackPlugin和HtmlWebpackPlugin進行了一些改進。 完成這些更改后,每次必須手動運行yarn build命令才能在應用程序中查看新更改。 我們也只是在瀏覽器中查看文件,而不是查看本地運行的服務器提供的內容。 讓我們通過創建開發服務器來改進流程。
To do this, we'll use webpack-dev-server. First, we'll need to install it:
為此,我們將使用webpack-dev-server 。 首先,我們需要安裝它:
yarn add --dev webpack-dev-serverNow, let's split up our single webpack.config.js file into two separate config files, one for production and one for development. We'll call the file for production webpack.config.prod.js and the file for development webpack.config.dev.js.
現在,讓我們將單個webpack.config.js文件拆分為兩個單獨的配置文件,一個用于生產,一個用于開發。 我們將其稱為生產webpack.config.prod.js的文件,并將其webpack.config.dev.js開發webpack.config.dev.js的文件。
開發Webpack配置 (Development Webpack Config)
Here's our development config file:
這是我們的開發配置文件:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'development',devtool: 'inline-source-map',devServer: {contentBase: './dist',},entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),] }Note that we've specified the mode as development now, and we've specified that we would like an inline-source-map for our JavaScript files, meaning that a source map is included at the end of each JavaScript file. For our dev server, we've specified that our content will be found in the dist directory.
請注意,我們現在已將mode指定為development mode ,并且已指定我們想要JavaScript文件的inline-source-map ,這意味著在每個JavaScript文件的末尾都包含一個源映射。 對于開發服務器,我們指定了將在dist目錄中找到我們的內容。
All the rest of the development config has stayed the same.
所有其他開發配置均保持不變。
生產Webpack配置 (Production Webpack Config)
Now, here's our production config file:
現在,這是我們的生產配置文件:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'production',devtool: 'source-map',entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),] }This file also looks very similar to our original config file. Here we've specified that the mode is production and that we would like the source-map option for source maps, which provides separate source map files for minified code.
該文件看起來也與我們的原始配置文件非常相似。 在這里,我們指定了mode為production mode ,并且希望使用source-map的source-map選項,該選項為縮小的代碼提供了單獨的源地圖文件。
生產和開發NPM腳本 (Production and Development NPM Scripts)
Finally, let's add a few more npm scripts in our package.json file so that we can work with our development and production webpack configs:
最后,讓我們在package.json文件中添加更多npm腳本,以便我們可以使用開發和生產Webpack配置:
"scripts": {"build": "webpack --config=webpack.config.prod.js","build-dev": "webpack --config=webpack.config.dev.js","start": "webpack-dev-server --config=webpack.config.dev.js --open" }Now, let's try out each of these scripts.
現在,讓我們嘗試這些腳本。
Run yarn build to see the production build output. You should see that the main.js file in your dist directory is minified and that it has an accompanying main.js.map source map file.
運行yarn build以查看生產構建輸出。 您應該看到的是, main.js在文件dist目錄的微細化以及它有一個伴隨main.js.map源地圖文件。
Now run yarn build-dev to see the development build output. You should see the main.js file in your dist directory, but now note that it is not minified.
現在運行yarn build-dev以查看開發構建輸出。 您應該在dist目錄中看到main.js文件,但是現在注意它沒有縮小。
Lastly, run yarn start to start up the development server. This will open up the app on http://localhost:8080/. No more having to view the files directly by just pulling them into your browser! We now have a real live development server!
最后,運行yarn start啟動開發服務器。 這將在http://localhost:8080/上打開該應用程序。 無需將它們直接拖到瀏覽器中直接查看文件! 現在,我們有一個真正的實時開發服務器!
The output you see should still look the same as it always has:
您看到的輸出應該仍然與以前一樣:
在開發過程中進行更改 (Making Changes During Development)
Now that we have a working dev server, let's experiment with making some simple changes to our ./src/index.js file. Instead of outputting "Hello from webpack!", let's change it to say "Hello from dev server!".
現在我們有了一個可以正常工作的開發服務器,讓我們嘗試對./src/index.js文件進行一些簡單的更改。 與其輸出“ Webpack的Hello!”,而是將其改為“開發服務器的Hello!”。
Save the file, and then see the page on your dev server automatically reload and update for you! That'll be a nice boost to your developer productivity.
保存文件,然后在開發服務器上看到頁面自動為您重新加載和更新! 這將大大提高開發人員的工作效率。
不要重復自己(干) (Don't Repeat Yourself (DRY))
Now that we have two separate webpack config files, one for development and one for production, you may have noticed that we have a lot of duplicated code between the two files.
現在我們有了兩個單獨的webpack配置文件,一個用于開發,一個用于生產,您可能已經注意到,兩個文件之間有很多重復的代碼。
Every developer out there has had the DRY principle drilled into their heads since day one: Don't repeat yourself. If you find yourself writing the same code in multiple places, it may be a good idea to turn that into shared code that can be written in one place and then used in multiple places. That way when you need to make changes, you only need to implement those changes in one place.
從第一天開始,那里的每個開發人員就已經將DRY原則付諸實踐:不要重復自己。 如果發現自己在多個地方編寫相同的代碼,則最好將其轉換為可以在一個地方編寫然后在多個地方使用的共享代碼。 這樣,當您需要進行更改時,只需要在一個地方實施這些更改即可。
So, how can we clean up the duplication in our webpack config files? There's a plugin for that!
那么,我們如何清理webpack配置文件中的重復項呢? 有一個插件!
Webpack合并 (WebpackMerge)
We can use the webpack-merge plugin to manage shared code that multiple config files rely on. To do this, we'll first install the package:
我們可以使用webpack-merge插件來管理多個配置文件所依賴的共享代碼。 為此,我們將首先安裝該軟件包:
yarn add --dev webpack-mergeNow we'll create a third webpack config file called webpack.config.common.js. This is where we'll keep our shared code. Right now, our development and production config files share the same entry point, output, and plugins. All that differs between the two files are the mode, source map, and dev server.
現在,我們將創建第三個webpack配置文件,名為webpack.config.common.js 。 這是我們保留共享代碼的地方。 現在,我們的開發和生產配置文件共享相同的入口點,輸出和插件。 這兩個文件之間的所有不同之處在于模式,源映射和開發服務器。
So, the contents of our webpack.config.common.js file will be:
因此,我們的webpack.config.common.js文件的內容為:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),] }And now, we can merge this shared config object into our development config like this:
現在,我們可以像下面這樣將共享的配置對象合并到我們的開發配置中:
const merge = require('webpack-merge') const commonConfig = require('./webpack.config.common')module.exports = merge(commonConfig, {mode: 'development',devtool: 'inline-source-map',devServer: {contentBase: './dist',}, })And we can merge the shared config object into our production config like this:
我們可以將共享配置對象合并到生產配置中,如下所示:
const merge = require('webpack-merge') const commonConfig = require('./webpack.config.common')module.exports = merge(commonConfig, {mode: 'production',devtool: 'source-map', })Look how much shorter and cleaner those two files look! Beautiful!
看看這兩個文件看起來更短更干凈! 美麗!
造型我們的應用程序 (Styling Our App)
Things are looking pretty good with our webpack configs so far. We have a working dev server and we've split out our code into development, production, and shared configuration files.
到目前為止,我們的webpack配置看起來一切都很好。 我們有一個工作的開發服務器,并且已經將代碼分為開發,生產和共享配置文件。
Let's start working on our actual app code now. The plain black and white page is a little boring to look at. Let's style it up!
讓我們立即開始實際的應用程序代碼。 普通的黑白頁面看起來有些無聊。 讓我們來造型??吧!
In our src directory, let's create an index.css file and place the following lines of CSS inside it:
在我們的src目錄中,創建一個index.css文件,并將以下CSS行放入其中:
body {background: deeppink;color: white; }Then, in our ./src/index.js file, let's import that CSS file:
然后,在我們的./src/index.js文件中,導入該CSS文件:
import './index.css'Now, run yarn start to get our development server running again.
現在,運行yarn start使我們的開發服務器再次運行。
Oh no! We get an error!
不好了! 我們得到一個錯誤!
ERROR in ./src/index.css 1:5 Module parse failed: Unexpected token (1:5) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > body { | background: deeppink; | color: white;@ ./src/index.js 1:0-20What are these "loaders" it speaks of?
它所說的這些“裝載者”是什么?
Webpack加載器 (Webpack Loaders)
Earlier, we discussed webpack plugins, which let you extend the webpack build process. There is also an ecosystem of webpack "loaders", which help webpack know how to understand and load different file types. Out of the box, webpack understands how to handle our JavaScript files, but it doesn't know what to do with CSS files yet. Let's fix that.
之前,我們討論了webpack插件,這些插件使您可以擴展webpack的構建過程。 還有一個webpack“ 加載程序 ”生態系統,它可以幫助webpack知道如何理解和加載不同的文件類型。 webpack開箱即用,可以理解如何處理我們JavaScript文件,但是尚不知道如何處理CSS文件。 讓我們修復它。
StyleLoader和CSSLoader (StyleLoader and CSSLoader)
There are two loaders in particular that will be helpful for us here: style-loader and css-loader. Let's get those included in our project and then discuss how they work.
這里有兩個特別對我們有用的加載器 : style-loader和css-loader 。 讓我們將它們包含在我們的項目中,然后討論它們如何工作。
To start, as always, we'll need to install those two dependencies:
與往常一樣,我們首先需要安裝這兩個依賴項:
yarn add --dev style-loader css-loaderThen we can add them to our webpack.config.common.js file in the module rules section down at the bottom:
然后,我們可以將它們添加webpack.config.common.js部底部模塊規則部分的webpack.config.common.js文件中:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),],module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']}]} }This section sets up rules for webpack so it knows what to do with each file it encounters. The test property is a regular expression that webpack checks against the file name. In this case, we want to handle files with a .css extension.
本節為webpack設置規則,以便知道如何處理遇到的每個文件。 test屬性是一個正則表達式,Webpack會根據文件名進行檢查。 在這種情況下,我們要處理擴展名為.css文件。
Then, the use property tells webpack what loader or loaders to use to handle files matching the criteria. Note that the order here matters!
然后, use屬性告訴webpack use哪些加載程序來處理符合條件的文件。 請注意,這里的順序很重要!
Webpack loaders are read from right to left. So first the css-loader will be applied, and then the style-loader will be applied.
Webpack加載程序從右到左讀取。 因此,首先將應用css-loader ,然后將應用style-loader 。
Now, what do these loaders actually do for us?
現在,這些裝載機實際上對我們有什么作用?
css-loader interprets and resolves imported CSS files that you reference in your JavaScript. So in this case, css-loader helps make this line work:
css-loader解釋并解析您在JavaScript中引用的導入CSS文件。 因此,在這種情況下, css-loader可幫助使此行正常工作:
import './index.css'Next, style-loader injects the CSS into the DOM. By default, style-loader takes the CSS it encounters and adds it to the DOM inside a style tag.
接下來, style-loader將CSS注入DOM。 默認情況下, style-loader會獲取遇到CSS,并將其添加到style標簽內的DOM中。
Let's restart our dev server by killing the current process (if you still have it running) and then starting it again with yarn start. Now, in the web browser, you should see this on https://localhost:8080/:
讓我們通過終止當前進程(如果仍在運行)來重新啟動開發服務器,然后使用yarn start再次啟動它。 現在,在Web瀏覽器中,您應該在https://localhost:8080/上看到它:
Oooh, so colorful!
噢,好多彩!
有關其他Webpack加載程序的說明 (A Note on Other Webpack Loaders)
We won't cover loaders for other file types in this article, but be aware that there's a loader for everything imaginable! You can use file-loader or url-loader for loading images and other assets. You can use sass-loader to handle converting Sass/SCSS files to CSS before piping that output to css-loader and style-loader. Webpack can handle Less files too with less-loader if that's your preference.
我們不會在本文中介紹其他文件類型的加載程序,但是請注意,可以想象的一切都有一個加載程序! 您可以使用文件加載器或url加載器來加載圖像和其他資產。 您可以使用sass-loader將Sass / SCSS文件轉換為CSS,然后再將其輸出到css-loader和style-loader 。 如果您愿意,Webpack也可以使用更少的加載器來處理更少的文件。
The moral of the story is: For any given file type, there's a loader that can handle it.
這個故事的寓意是:對于任何給定的文件類型,都有一個可以處理它的加載器。
BabelLoader (BabelLoader)
Ok, back to our demo app. We've written just a few lines of JavaScript so far. It'd be nice if we could write our JavaScript using new features that aren't well-supported in every browser yet. Babel is a JavaScript compiler that can turn ES6+ code into ES5 code.
好的,回到我們的演示應用程序。 到目前為止,我們僅編寫了幾行JavaScript。 如果我們可以使用尚未在每個瀏覽器中都得到很好支持的新功能來編寫JavaScript,那就太好了。 Babel是一個JavaScript編譯器,可以將ES6 +代碼轉換為ES5代碼。
And (you guessed it), there's a loader for that: babel-loader.
而且(您猜對了),有一個加載器用于: babel-loader 。
To set up babel-loader, we'll follow the instructions on their installation guide linked above.
要設置babel-loader ,我們將按照上面鏈接的安裝指南中的說明進行操作。
First, we'll install our dependencies:
首先,我們將安裝依賴項:
yarn add --dev babel-loader @babel/coreNext, we'll add a new rule to our module rules array in our webpack.config.common.js file:
接下來,我們將在webpack.config.common.js文件中的模塊規則數組中添加新規則:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),],module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']},{test: /\.(js|jsx)$/,exclude: /[\\/]node_modules[\\/]/,use: {loader: 'babel-loader',},},]} }This will tell webpack that when it encounters .js or .jsx files to use Babel to transform the code. We use the exclude property to make sure Babel doesn't try to transform JavaScript files in our node_modules directory. Those are third-party dependencies that should already have been taken care of by their creators.
這將告訴webpack在遇到.js或.jsx文件時使用Babel轉換代碼。 我們使用exclude屬性來確保Babel不會嘗試轉換node_modules目錄中JavaScript文件。 這些是第三方依賴關系,其創建者應該已經解決了。
Next, we'll add one more dependency for a Babel preset:
接下來,我們將為Babel預設添加一個依賴項:
yarn add --dev @babel/preset-envAnd then we'll create a .babelrc file where we can do other Babel configuration as needed. We'll keep our file pretty simple and just specify the Babel preset that we want to use:
然后,我們將創建一個.babelrc文件,可以根據需要進行其他Babel配置。 我們將使文件保持非常簡單,僅指定我們要使用的Babel預設:
{"presets": ["@babel/preset-env"] }And finally, let's write some ES6 code in our ./src/index.js file:
最后,讓我們在./src/index.js文件中編寫一些ES6代碼:
import './index.css'const p = document.createElement('p') p.textContent = 'Hello from webpack!' document.body.appendChild(p)const p2 = document.createElement('p') const numbers1 = [1, 2, 3, 4, 5, 6] const numbers2 = [7, 8, 9, 10] const numbers3 = [...numbers1, ...numbers2] p2.textContent = numbers3.join(' ') document.body.appendChild(p2)This is a really trivial example, but we're using the spread operator here to concatenate two arrays.
這是一個非常簡單的示例,但是我們在這里使用了散布運算符來連接兩個數組。
Now, if we kill our running process and run yarn start again, we should see this in the browser:
現在,如果我們終止正在運行的進程并再次運行yarn start ,我們應該在瀏覽器中看到以下內容:
Great! Everything is working nicely.
大! 一切都很好。
暫時缺少樣式 (Temporarily Missing Styles)
If you disable the cache in your browser and reload the page for our demo app, you may notice a slight blip in which the page appears with just the un-styled HTML, and then the page background turns pink and the text turns white as the styles are applied.
如果您在瀏覽器中禁用了緩存并重新加載了演示應用程序的頁面,您可能會注意到一個輕微的斑點,其中頁面僅顯示未設置樣式HTML,然后頁面背景變為粉紅色,文本變為白色,應用樣式。
This behavior results from how style-loader works. As mentioned above, style-loader takes CSS and places it in a style tag in your HTML. Because of that, there's a brief period of time in which the style tag hasn't been appended yet!
此行為是由style-loader工作原理引起的。 如上所述, style-loader CSS并將其放在HTML的style標簽中。 因此,有一小段時間尚未添加style標簽!
Now, this is OK for a development environment, but we definitely wouldn't want this kind of behavior occurring in production. Let's fix that.
現在,這對于開發環境是可以的,但我們絕對不希望在生產中發生這種行為。 讓我們修復它。
MiniCssExtractPlugin (MiniCssExtractPlugin)
Rather than injecting CSS into our HTML as style tags, we can use the MiniCssExtractPlugin to generate separate CSS files for us. We'll use this in our production config while still just using style-loader in our development config.
可以使用MiniCssExtractPlugin為我們生成單獨CSS文件,而不是將CSS作為style標簽注入HTML。 我們將在生產配置中使用它,而仍然在開發配置中使用style-loader 。
First, let's install the dependency in our project:
首先,讓我們在項目中安裝依賴項:
yarn add --dev mini-css-extract-pluginNow in our webpack.config.common.js file let's remove the CSS rule since we'll be handling this differently in development and production. We're left with this in our shared config:
現在,在我們的webpack.config.common.js文件中,讓我們刪除CSS規則,因為在開發和生產中我們將對此進行不同的處理。 我們在共享配置中保留了以下內容:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),],module: {rules: [{test: /\.(js|jsx)$/,exclude: /[\\/]node_modules[\\/]/,use: {loader: 'babel-loader',},},]} }Now, in our webpack.config.dev.js file, let's add back in style-loader and css-loader that we just removed from our shared config:
現在,在webpack.config.dev.js文件中,讓我們webpack.config.dev.js添加剛剛從共享配置中刪除的style-loader和css-loader :
const merge = require('webpack-merge') const commonConfig = require('./webpack.config.common')module.exports = merge(commonConfig, {mode: 'development',devtool: 'inline-source-map',devServer: {contentBase: './dist',},module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']},]} })And finally, in our webpack.config.prod.js file, let's add in our new mini-css-extract-plugin:
最后,在我們的webpack.config.prod.js文件中,讓我們添加新的mini-css-extract-plugin :
const merge = require('webpack-merge') const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const commonConfig = require('./webpack.config.common')module.exports = merge(commonConfig, {mode: 'production',devtool: 'source-map',module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader,'css-loader',],},],},plugins: [new MiniCssExtractPlugin({filename: '[name].[contenthash].css',}),] })This one is a little different because it actually is both a plugin and a loader, so it goes in the module rules and in the plugins sections.
這個有點不同,因為它實際上既是一個插件又是一個加載器,因此它出現在模塊規則和插件部分中。
Also note that we use the square brackets in our file name to dynamically set the name to the original source file's name and also include the contenthash, which is a hash (an alphanumeric string) that represents the file's contents.
另外請注意,我們使用了方括號在我們的文件名的動態設置name為原始源文件的名稱,還包括contenthash ,這是一個散列(字母數字字符串),表示該文件的內容。
Now if you run yarn build this time to generate the production build, you should get some output in your terminal that looks like this:
現在,如果您這次運行yarn build來生成生產版本,則應該在終端中獲得一些輸出,如下所示:
Note that it actually generates a CSS file now, and the content hash is included in the file name.
請注意,它實際上現在會生成一個CSS文件,并且內容哈希包含在文件名中。
Alright, problem solved! No more blip when the page loads in production since we have the styles included as a link tag to an actual CSS file.
好了,問題解決了! 當頁面在生產環境中加載時,不再出現任何問題,因為我們將樣式包含為link實際CSS文件的link標記。
快取清除 (Cache Busting)
Since we've included the content hash in the generated CSS file, now is a good time to talk about cache busting. Why, you ask, would we want the content hash included in our file names? To help the browser understand when a file has changed!
由于我們已經在生成CSS文件中包括了內容哈希,因此現在是討論緩存清除的好時機。 您問為什么要在文件名中包含內容哈希? 幫助瀏覽器了解文件更改的時間!
Your browser tries to be helpful by caching files it has seen before. For example, if you've visited a website, and your browser had to download assets like JavaScript, CSS, or image files, your browser may cache those files so that it doesn't have to request them from the server again.
您的瀏覽器試圖通過緩存以前見過的文件來提供幫助。 例如,如果您訪問了一個網站,并且瀏覽器必須下載JavaScript,CSS或圖像文件之類的資產,則您的瀏覽器可能會緩存這些文件,從而不必再次從服務器請求它們。
This means that if you visit the site again, your browser can use the cached files instead of requesting them again, so you get a faster page load time and a better experience.
這意味著,如果您再次訪問該站點,您的瀏覽器可以使用緩存的文件,而不必再次請求它們,因此您可以獲得更快的頁面加載時間和更好的體驗。
So, what's the problem here? Imagine if we had a file called main.js used in our app. Then, a user visits your app and their browser caches the main.js file.
那么,這是什么問題呢? 假設我們的應用程序中有一個名為main.js的文件。 然后,用戶訪問您的應用程序,并且他們的瀏覽器緩存main.js文件。
Now, at some later point in time, you've released new code for your app. The contents of the main.js file have changed. But, when this same user visits your app again, the browser sees that it needs a main.js file, notes that it has a cached main.js file, and just uses the cached version. The user doesn't get your new code!
現在,在稍后的某個時間點,您已經為您的應用發布了新代碼。 main.js文件的內容已更改。 但是,當同一用戶再次訪問您的應用程序時,瀏覽器會發現它需要一個main.js文件,并注意到它具有一個已緩存的main.js文件,并且僅使用了該版本。 用戶沒有得到您的新代碼!
To solve this problem, a common practice is to include the content hash in each file's name. As discussed earlier, the content hash is a string representation of the file's contents. If the file's contents don't change, the content hash doesn't change. But, if the file's contents do change, then the content hash also changes.
為了解決此問題,通常的做法是在每個文件的名稱中包含內容哈希。 如前所述,內容哈希是文件內容的字符串表示形式。 如果文件的內容不變,則內容哈希也不變。 但是,如果文件的內容確實發生了變化,則內容哈希也將發生變化。
Because the file name will now change when the code changes, the browser will download the new file since it won't have that specific file name in its cache.
因為代碼更改時文件名現在會更改,所以瀏覽器將下載新文件,因為它的緩存中沒有該特定文件名。
包括內容哈希 (Including the Content Hash)
To include the content hash in our JavaScript file names, we'll modify just one line of code in our webpack.config.common.js file. This line:
為了將內容哈希包含在我們JavaScript文件名中,我們將只修改webpack.config.common.js文件中的一行代碼。 這行:
filename: 'main.js'Will change to this line:
將更改為這一行:
filename: '[name].[contenthash].js'So that the entire file looks like this:
這樣整個文件看起來像這樣:
const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {entry: './src/index.js',output: {filename: '[name].[contenthash].js', // this line is the only differencepath: path.resolve(__dirname, 'dist')},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({filename: 'index.html',inject: true,template: path.resolve(__dirname, 'src', 'index.html'),}),],module: {rules: [{test: /\.(js|jsx)$/,exclude: /[\\/]node_modules[\\/]/,use: {loader: 'babel-loader',},},]} }Now if you run yarn build, you'll see that both your JavaScript and your CSS have content hashes included:
現在,如果運行yarn build ,您將看到JavaScript和CSS都包含內容哈希:
If you run yarn build again and compare your new output to your old output, you'll notice that the content hashes are exactly the same both times.
如果再次運行yarn build并將新輸出與舊輸出進行比較,您會發現內容哈希值兩次完全相同。
But, if you edit your ./src/index.js file in any way and then run yarn build again, you'll get a new content hash because the content has changed! Try it!
但是,如果您以任何方式編輯./src/index.js文件,然后再次運行yarn build ,您將得到一個新的內容哈希,因為內容已更改! 試試吧!
縮小CSS (Minifying CSS)
Last but not least, we may want to minify our CSS. We're already minifying our JavaScript for the production build, but we're not minifying our CSS yet. Let's do that.
最后但并非最不重要的一點是,我們可能要縮小CSS。 我們已經在生產版本中最小化了JavaScript,但是還沒有最小化CSS。 來做吧。
We can minimize our CSS by using the optimize-css-assets-webpack-plugin. Let's install that dependency now:
我們可以通過使用optimize-css-assets-webpack-plugin最小化CSS。 現在安裝該依賴項:
yarn add --dev optimize-css-assets-webpack-pluginNow we can add that to an optimization section of our webpack.config.prod.js file:
現在,我們可以將其添加到webpack.config.prod.js文件的優化部分:
const merge = require('webpack-merge') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') const commonConfig = require('./webpack.config.common')module.exports = merge(commonConfig, {mode: 'production',devtool: 'source-map',module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader,'css-loader',],},],},plugins: [new MiniCssExtractPlugin({filename: '[name].[contenthash].css',}),],optimization: {minimizer: [new OptimizeCssAssetsPlugin({cssProcessorOptions: {map: {inline: false,annotation: true,},},}),],}, })Now if we run yarn build and then check out the contents of our dist directory, we can see that the resulting CSS is minified. Nice!
現在,如果我們運行yarn build ,然后檢查dist目錄的內容,我們可以看到生成CSS已縮小。 真好!
body{background:#ff1493;color:#fff} /*# sourceMappingURL=main.66e0d6aeae6f3c6fb895.css.map */But wait! If we look at our resulting JavaScript file, it's not minified! Hmmm. It was minified before, so what happened here?
可是等等! 如果我們查看生成JavaScript文件,那么它沒有縮小! 嗯 之前已經縮小了,所以這里發生了什么?
The issue is that we're now manually configuring the optimization minimizer section of our webpack config. When that section isn't in the webpack config file, webpack defaults to using its own minimizer preferences, which includes minifying JavaScript when the mode is set to production.
問題是我們現在正在手動配置webpack配置的優化最小化器部分。 如果該部分不在webpack配置文件中,則webpack默認使用其自己的最小化器首選項,其中包括將mode設置為production時最小化JavaScript。
Since we're now overriding those defaults by adding in our preferences for minifying CSS assets, we'll need to also explicitly include instructions for how we want webpack to minify JavaScript assets.
由于我們現在通過添加用于最小化CSS資產的首選項來覆蓋這些默認值,因此,我們還需要明確包含有關如何使webpack最小化JavaScript資產的說明。
TerserWebpack插件 (TerserWebpackPlugin)
We can minify our JavaScript files using the TerserWebpackPlugin. Let's start by installing that dependency:
我們可以使用TerserWebpackPlugin縮小JavaScript文件。 讓我們從安裝該依賴關系開始:
yarn add --dev terser-webpack-pluginThen, in our webpack.config.prod.js file, let's add the terser-webpack-plugin to our optimization minimizer settings at the bottom of the file:
然后,在我們的webpack.config.prod.js文件中,將terser-webpack-plugin到文件底部的優化最小化器設置中:
const merge = require('webpack-merge') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const commonConfig = require('./webpack.config.common')module.exports = merge(commonConfig, {mode: 'production',devtool: 'source-map',module: {rules: [{test: /\.css$/,use: [MiniCssExtractPlugin.loader,'css-loader',],},],},plugins: [new MiniCssExtractPlugin({filename: '[name].[contenthash].css',}),],optimization: {minimizer: [new OptimizeCssAssetsPlugin({cssProcessorOptions: {map: {inline: false,annotation: true,},},}),new TerserPlugin({// Use multi-process parallel running to improve the build speed// Default number of concurrent runs: os.cpus().length - 1parallel: true,// Enable file cachingcache: true,sourceMap: true,}),],}, })Now if we run yarn build and look at the output in the dist directory, we should see that both our CSS files and our JavaScript files are minified. There we go!
現在,如果我們運行yarn build并查看dist目錄中的輸出,我們應該看到CSS文件和JavaScript文件都已縮小。 好了!
結語 (Wrapping Up)
If you've followed along this far, I commend you!
如果您一直遵循這一原則,我向您表示贊賞!
Let's review what we've learned so far:
讓我們回顧一下到目前為止所學到的東西:
- Webpack is a build tool for asset bundling and dependency management. Webpack是用于資產捆綁和依賴管理的構建工具。
- Webpack can be configured by a config file. Webpack可以通過配置文件進行配置。
- Plugins modify and extend the webpack build process. 插件會修改并擴展Webpack的構建過程。
- Loaders instruct webpack how to handle different file types. 加載程序指示webpack如何處理不同的文件類型。
The clean-webpack-plugin can be used to remove old build artifacts from the dist directory.
clean-webpack-plugin可用于從dist目錄中刪除舊的構建工件。
The html-webpack-plugin helps manage the HTML file, including injecting JavaScript into the file via script tags.
html-webpack-plugin有助于管理HTML文件,包括通過script標簽將JavaScript注入文件中。
webpack-dev-server creates a dev server to make local development easier.
webpack-dev-server創建一個dev服務器來webpack-dev-server本地開發。
It's helpful to have separate webpack configs for development and production. You can share and merge config files using the webpack-merge plugin.
為開發和生產使用單獨的webpack配置很有幫助。 您可以使用webpack-merge插件共享和合并配置文件。
We can handle styling our app by including loaders like css-loader, style-loader, sass-loader, less-loader, and the mini-css-extract-plugin (which functions as both a plugin and a loader).
我們可以通過包含css-loader , style-loader , sass-loader , less-loader和mini-css-extract-plugin (既充當插件又充當加載器)之類css-loader來處理應用css-loader style-loader 。
We can include new JavaScript syntax and features by using Babel and babel-loader.
我們可以使用Babel和babel-loader來包含新JavaScript語法和功能。
- We can include content hashes in our file names to help with cache busting and managing new versions of our released code. 我們可以在文件名中包含內容哈希,以幫助清除緩存和管理已發布代碼的新版本。
We can minify our CSS with the optimize-css-assets-webpack-plugin.
我們可以使用optimize-css-assets-webpack-plugin最小optimize-css-assets-webpack-plugin 。
We can minify our JavaScript with the terser-webpack-plugin.
我們可以使用terser-webpack-plugin縮小JavaScript。
下一步是什么? (What's Next?)
Throughout this article, we've created a pretty respectable webpack config. All of these techniques we've discussed are industry standards and are common to use in enterprise-level projects.
在本文中,我們已經創建了一個非常受人尊敬的webpack配置。 我們討論的所有這些技術都是行業標準,在企業級項目中很常見。
But there's still more! Other advanced webpack topics include code splitting, lazy loading, tree shaking, and more!
但是還有更多! 其他高級Webpack主題包括代碼拆分 , 延遲加載 , 搖樹等等!
If you're interested in exploring webpack more on your own, I'd highly recommend reading through the official webpack guides.
如果您有興趣自行探索Webpack,強烈建議您閱讀官方Webpack指南 。
Again, all of the code we've gone through in this tutorial can be found in GitHub. The starting point is found here, and the finished result is found here.
同樣,可以在GitHub上找到我們在本教程中學習過的所有代碼。 在這里找到起點,在這里找到 完成的結果 。
Thanks for reading, and happy coding!
感謝您的閱讀,并祝您編程愉快!
翻譯自: https://www.freecodecamp.org/news/creating-a-production-ready-webpack-4-config-from-scratch/
總結
以上是生活随笔為你收集整理的如何从头开始创建可用于生产环境的Webpack 4配置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 免费 cms建站系统_最受欢迎
- 下一篇: 树莓派RaspBerryPi上使用3g模