如何结合Webpack 4和Babel 7创建一个出色的React应用

I previously wrote an article called “How to conquer Webpack 4 and build a sweet React app.” Soon after I wrote the article, babel swooped in with a major breaking change and many of the packages got deprecated. So I decided to write a new tutorial.

我之前写过一篇名为“ 如何征服Webpack 4并构建一个出色的React应用程序 ”的文章 在我写了这篇文章后不久,babel进行了重大的重大更改,许多软件包被弃用了。 所以我决定写一个新的教程。

I will focus on setting up webpack with react which will have .scss support along with code splitting

我将重点介绍如何使用ract设置webpack ,它具有.scss支持以及代码拆分功能

The purpose for writing this again is simple: I want everyone to feel comfortable. Because setting up webpack can be really daunting. Especially for new developers out there. Follow along, and what seemed difficult and maybe scary will seem like a piece of cake.

再次编写此代码的目的很简单:我希望每个人都感到舒服。 因为设置webpack确实很艰巨。 特别是对于那里的新开发人员。 跟着走,似乎困难甚至可怕的事情似乎只是小菜一碟。

Before we start, here is the source code. I know this has loads of things in it. I plan to use the same code base to talk about webpack, react, SCSS, hot module replacement, testing with jest and enzyme, linting code, and adding a code formatter like prettier in other articles to come, so I will continuously keep on updating this code base. I will not bloat this code base — I promise.

在开始之前,这里是源代码 。 我知道里面有很多东西。 我计划使用相同的代码库来谈论Webpack,React,SCSS,热模块替换,用玩笑和酶进行测试,整理代码,并在以后的其他文章中添加像prettier这样的代码格式化程序,因此我将不断更新此代码库。 我保证不会夸大此代码库。

Note: If you feel like making a PR for the repository, you are more than welcome :) So let’s get started.

注意:如果您想为存储库创建PR,欢迎您::因此,让我们开始吧。

For simplicity sake, this article is only going to focus on;

为了简单起见,本文仅着重讨论;

  • Setting up Webpack 4 with Babel 7 for React

    使用Babel 7为React设置Webpack 4
  • Support for .SCSS

    支持.SCSS
  • Code Splitting

    代码分割
  • Development environment with HMR (Hot Module Replacement)

    具有HMR(热模块替换)的开发环境
  • Production configuration

    生产配置
  • Dividing your Webpack configuration into chunks

    将您的Webpack配置分为多个块
  • Handling staging, demo, production, test and other environments in code

    在代码中处理登台,演示,生产,测试和其他环境
  • Generating a visualizer in production build to check which chunk of code took how much size and what are the dependencies of the chunks. Super useful.

    在生产版本中生成可视化工具,以检查哪个代码块占用了多少大小以及这些块的依赖关系是什么。 超级有用。

先决条件 (Prerequisite)

You need to have node installed in order to use npm (node package manager).

您需要安装节点才能使用npm(节点程序包管理器)。

First things first, create a folder called app then open up your terminal and go into that app folder and type:

首先,创建一个名为app的文件夹,然后打开您的终端并进入该app文件夹并键入:

npm init -y

This will create a package.json file for you.

这将为您创建一个package.json文件。

Second create a folder called src in your app folder. Inside app/src create a file called index.js and write the following code.

其次,在您的app文件夹中创建一个名为src的文件夹。 在app/src内部,创建一个名为index.js的文件,并编写以下代码。

console.warn('I am a Star Trek nerd');
console.log('So through out this tutorial, you will see a lot of Star Trek quotes');
console.log('Starting now');
console.log("Compassion: that’s the one thing no machine ever had. Maybe it’s the one thing that keeps men ahead of them. -Dr McCoy");

You can write anything above of course. I chose Star Trek.

您当然可以写任何东西。 我选择了《星际迷航》。

Next we need to install a couple of dependencies. You can just copy the dependencies & devDependencies from the package.json below into your own and do an npm install:

接下来,我们需要安装几个依赖项。 您可以将下面的package.json中的dependenciesdevDependencies复制到自己的文件中,然后执行npm install

{
  "name": "react-boiler-plate",
  "version": "1.0.0",
  "description": "A react boiler plate",
  "main": "src/index.js",
  "author": "Adeel Imran",
  "license": "MIT",
  "scripts": {
    "start": "a script will come here"
  },
  "dependencies": {
    "react": "^16.5.2",
    "react-dom": "^16.5.2"
  },
  "devDependencies": {
    "@babel/core": "^7.0.0",
    "@babel/plugin-proposal-class-properties": "^7.0.0",
    "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
    "@babel/plugin-proposal-throw-expressions": "^7.0.0",
    "@babel/plugin-syntax-dynamic-import": "^7.0.0",
    "@babel/polyfill": "^7.0.0-beta.51",
    "@babel/preset-env": "^7.0.0-beta.51",
    "@babel/preset-react": "^7.0.0-beta.51",
    "babel-loader": "^8.0.0-beta.0",
    "copy-webpack-plugin": "^4.5.1",
    "css-loader": "^0.28.11",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.4.3",
    "node-sass": "^4.8.3",
    "optimize-css-assets-webpack-plugin": "^4.0.0",
    "sass-loader": "^7.0.3",
    "style-loader": "^0.21.0",
    "uglifyjs-webpack-plugin": "^1.2.5",
    "webpack": "^4.12.0",
    "webpack-cli": "^3.0.8",
    "webpack-dev-server": "^3.1.4",
    "webpack-merge": "^4.1.3",
    "webpack-visualizer-plugin": "^0.1.11"
  }
}

Yes I know, I know! That’s a lot to create a hello world react app. But wait, this is all you will need. Even if you want to create a enterprise level app. (Maybe one or two more things depending on your requirements, but this is the backbone for it.)

是的,我知道,我知道! 创建一个hello world react应用程序需要大量资源。 但是,等等,这就是您所需要的。 即使您要创建企业级应用程序。 (根据您的要求,也许还有一两件事,但这是它的基础。)

So let’s talk about each and everyone of them before we dive deep into the code.

因此,在深入研究代码之前,让我们谈谈每个人。

webpack: We need Webpack to bundle our code.

webpack :我们需要Webpack来捆绑我们的代码。

webpack-cli: We will be using some CLI features of Webpack to make our lives easier while writing some scripts.

webpack-cli :我们将使用Webpack的某些CLI功能,以简化编写脚本的过程。

webpack-dev-server: I will create a server using the webpack-dev-server package. This is only meant to be used in the development environment, and not for production. This means while developing and working on my code, I don’t need a separate server like NodeJS to setup manually.

webpack-dev-server :我将使用webpack-dev-server包创建服务器。 这仅用于开发环境,而不用于生产。 这意味着在开发和处理代码时,不需要像NodeJS这样的单独服务器即可进行手动设置。

webpack-merge: To divide our configuration into chunks, more on this later

webpack-merge :将我们的配置分为多个块,稍后会详细介绍

webpack-visualizer-plugin: To see a visual representation of each of our bundle size — how much space they are taking and what are their dependencies.

webpack-visualizer-plugin :要查看每个捆绑包大小的可视化表示-它们占用了多少空间以及它们之间的依赖关系。

style-loader: This adds CSS to the DOM by injecting a /> tag in the header

style-loader :这通过在标头中插入 />标签将CSS添加到DOM中

sass-loader: For SCSS support

sass-loader :用于SCSS支持

node-sass: A dependency for sass-loader

node-sass :sass-loader的依赖项

css-loader: To convert our .scss files into .css

css-loader :将我们的.scss文件转换为.css

mini-css-extract-plugin: This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS.

mini-css-extract-plugin :此插件将CSS提取到单独的文件中。 它为每个包含CSS的JS文件创建一个CSS文件。

uglifyjs-webpack-plugin: To minify JavaScript code for production

uglifyjs-webpack-plugin :最小化用于生产JavaScript代码

optimize-css-assets-webpack-plugin To minify CSS code for production

优化css-assets-webpack-plugin最小化CSS代码以进行生产

html-webpack-plugin: This does more then generate an HTML file, it supports on demand .css and .js files automatically added to your HTML files on demand

html-webpack-plugin :这会做更多的工作,然后生成一个HTML文件,它支持按需将.css和.js文件自动按需添加到您HTML文件中

copy-webpack-plugin: Copies files/folders to your build folder.

copy-webpack-plugin :将文件/文件夹复制到构建文件夹。

babel-loader: This is the loader that helps webpack compile .js files

babel-loader :这是帮助webpack编译.js文件的加载器

@babel/core: Babel core compiler, this is a dependency that lets you use babel-loader

@ babel / core :Babel核心编译器,这是一个依赖关系,可让您使用babel-loader

@babel/preset-react Babel preset for React code

@ babel / preset-react Babel预设为React代码

@babel/preset-env: Babel preset that allows you to use the latest JavaScript

@ babel / preset-env :Babel预设,允许您使用最新JavaScript

@babel/pollyfill: Babel includes a polyfill that includes a custom regenerator runtime and core-js. This will emulate a full ES2015+ environment. This means support for async/await type of cool syntax sugar.

@巴贝尔/ pollyfill :巴别包括填充工具 ,其包括一个自定义的再生器的运行时和核心-JS 。 这将模拟完整的ES2015 +环境。 这意味着支持async/await类型的凉爽语法糖。

Up till now this is pretty much what I wrote in How to conquer Webpack 4 and build a sweet React app.

到目前为止,这几乎是我在“ 如何征服Webpack 4和构建一个出色的React应用程序”中写的内容

So what changed?

那么,什么改变了?

Well! Babel introduced a breaking change (for the greater good, believe me) which you can read more here: Removing Babel’s Stage Preset. What this meant was that before if you included babel-preset-stage-2 let’s say, it would include all proposals related to stage-2, which would bloat your code. But you just might need one specific feature of stage-2.

好! Babel引入了一项重大突破(为了更大的利益,请相信我),您可以在此处阅读更多内容: 删除Babel的舞台预设 这意味着如果在您包含babel-preset-stage-2之前,它会包含与stage-2相关的所有建议,这会使您的代码膨胀。 但是您可能只需要Stage-2的一项特定功能。

So in order to combat this, babel deprecated all those preset plugins and shipped individual features. You now have to set those up manually. Cool right? So let’s talk a bit about those individual packages and what they do.

因此,为了解决这个问题,babel弃用了所有这些预设插件,并提供了各个功能。 现在,您必须手动设置它们。 酷吧? 因此,让我们谈谈那些单独的软件包及其作用。

@babel/plugin-proposal-class-properties: Coverts your class syntax into a function for browsers that don’t support class syntax

@ babel / plugin-proposal-class-properties :将class语法隐藏到不支持class语法的浏览器的function

@babel/plugin-proposal-export-namespace-from Supports syntax like import * as ns from '../path/to/module';

@ babel / plugin-proposal-export-namespace-from支持类似import * as ns from '../path/to/module';语法,如import * as ns from '../path/to/module';

@babel/plugin-proposal-throw-expressions New syntax to throw exceptions from within an expression context. I love this feature :D

@ babel / plugin-proposal-throw-expressions用于从表达式上下文内引发异常的新语法。 我喜欢这个功能:D

@babel/plugin-syntax-dynamic-import This is what helps with code splitting. Webpack ships with code splitting by default (Since webpack 1). But when you want to code split in webpack while you are using babel, then you need to use this plugin.

@ babel / plugin-syntax-dynamic-import这有助于代码拆分。 Webpack默认附带代码拆分功能(自webpack 1起)。 但是,当您想在使用babel时在webpack中进行代码拆分时则需要使用此插件。

Note: for this tutorial you won’t need@babel/plugin-proposal-export-namsespace-from & @babel/plugin-proposal-throw-expressions

注意:对于本教程,您不需要@babel/plugin-proposal-export-namsespace-from@babel/plugin-proposal-throw-expressions

Also here is a list of all babel plugins. I mean all of them. Check out the list here.

这也是所有babel插件的列表。 我的意思是所有人。 这里查看清单

And now that you know why we need what we need — nothing extra — you’ll feel more confident implementing the webpack configuration.

现在,您知道了我们为什么需要我们所需要的东西(仅此而已),您将更加自信地实施webpack配置。

Let’s start by adding a .babelrc file in the root of out app folder:

首先,在out app文件夹的根目录中添加一个.babelrc文件:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-throw-expressions"
  ]
}

We have 2 main presets preset-env & preset-react . The rest are our plugins to add “wings” to our code.

我们有2个主要的预设preset-envpreset-react 。 其余的是我们的插件,用于在代码中添加“ wings ”。

And to quote Captain Kirk from Star Trek (because why not):

并引用《星际迷航》的柯克上尉的话(因为为什么不这样):

Perhaps man wasn’t meant for paradise. Maybe he was meant to claw, to scratch all the way. Captain Kirk

也许人不是天堂。 也许他本来是要抓牢,从头开始的。 柯克船长

In his defense, Captain Kirk was up against the likes of General Change, Khan, The Borg and so many dangerous foes. All we are up against is the beautiful Webpack and Babel. So perhaps we developers are meant for paradise.

在防守中,柯克船长与通用汽车,汗,博格和许多危险的敌人抗衡。 我们所面对的只是美丽的WebpackBabel 。 因此,也许我们的开发者是天堂。

So let’s set our webpack up.

因此,让我们设置Webpack。

Create a config folder in your app . If you feel lost you can at any time refer to the GitHub repository for this. Now inside our config folder let’s create a file called webpack.base.config.js The reason I call this base is because it is going to be used for our development and for production. Because why write the same thing twice? Again if this isn’t making much sense, just bear with me a few more minutes.

在您的app创建一个config文件夹。 如果你觉得失去了你可以在任何时候指的GitHub [R epository这一点。 现在,在我们的config文件夹中,我们创建一个名为webpack.base.config.js的文件。之所以称其为base是因为它将用于我们的开发和生产。 因为为什么要写两次相同的东西? 再说一次,如果这没有多大意义,请再耐心等待几分钟。

In your config/webpack.base.config.js write this:

在您的config/webpack.base.config.js编写以下代码:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
    ]
  }
}

Once you have it in place, run this command in your root app directory. (I’ll tell you what this command does a bit later with the code we wrote above, I promise.)

安装到位后,在根app目录中运行此命令。 (我保证,稍后我将告诉您该命令对上面编写的代码做了什么)。

$ node_modules/.bin/webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback

Once you run this command, you will see this screen:

一旦运行此命令,您将看到以下屏幕:

So what happened here? Well when we ran the webpack command, it did find our index.js file that we wrote earlier in app/src/index.js — but it didn’t have a .html to run it in. So let’s create an index.html file in our app/src folder:

那么这里发生了什么? 好了,当我们运行webpack命令时,它确实找到了我们先前在app/src/index.js编写的index.js文件-但是它没有.html可以运行。因此,让我们创建一个index.html在我们的app/src文件夹中的文件:






  
  
  Tutorial



  

Let’s update our webpack.base.config.js as well:

让我们也更新我们的webpack.base.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ 
      template: './src/index.html', 
      filename: './index.html' 
    })
  ]
}

Let’s run that command again now:

现在让我们再次运行该命令:

$ node_modules/.bin/webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback

Your browser will open up. If you are using Google Chrome, press ctrl+shift+j and your browser console will open up. You will see something like this:

您的浏览器将打开。 如果您使用的是Google Chrome浏览器,请按ctrl+shift+j ,将打开浏览器控制台。 您将看到如下内容:

So let’s talk what happened here. Our webpack.base.config.js has two main things: modules and plugins. A module can have multiple rules, and each rule is applied to certain file type. The certain file type that we want to apply that rule to is in test of that rule:

因此,让我们谈谈这里发生了什么。 我们的webpack.base.config.js有两个主要方面:模块和插件。 一个模块可以有多个规则,并且每个规则都适用于某些文件类型。 我们要对该规则应用的特定文件类型正在test该规则:

rules: [      
  {        
    test: /\.js$/,        
    exclude: /node_modules/,        
    use: {          
      loader: 'babel-loader'        
    }      
  },    
]

Here by saying test: /\.js$./, we are telling webpack to apply this rule only for .js files. The other thing is exclude which also takes in a regex expression of what not not to include. This is where we tell it not to compile node_modules because this will compile all of it, and there are loads of dependencies installed. Check the node_modules yourself. The last part is use.

这里说test: /\.js$./,我们告诉webpack仅将此规则应用于.js文件。 另一件事是exclude ,它也接受不包括在内的正则表达式。 这是我们告诉它不要编译node_modules因为它将编译所有代码,并且安装了许多依赖项。 自己检查node_modules 。 最后一部分是use

Now webpack knows where to apply the rule using test and where not to apply the rule using exclude — but what is the rule exactly? That is where use comes into play: here we specify loader: 'babel-loader'. Now what babel-loader does is that it looks for .babelrc file that we wrote earlier. And all the presets & plugins we wrote there. It takes all of them and applies those to our .js files.

现在,webpack知道在何处使用test应用规则,在何处不使用exclude应用规则,但是确切的规则是什么? 这就是use发挥作用的地方:这里我们指定loader: 'babel-loader' 。 现在, babel-loader作用是查找我们先前编写的.babelrc文件。 以及我们在此处编写的所有预设和插件。 它需要所有这些并将它们应用于我们的.js文件。

Which brings us to the next question: how does Webpack 4 find those files? Well Webpack 4 ships with loads of default stuff already set up for you. Two of those are entry and output .

这就引出了下一个问题: Webpack 4如何找到这些文件? Webpack 4附带了许多已经为您设置的默认内容。 其中两个是entryoutput

entry point by default is the src directory that we wrote in our app folder.

默认情况下, entry点是我们在app文件夹中编写的src目录。

output point is where all the compiled bundled code is generated, which is going to be dist folder in out app folder. (You won’t see that now, because we haven’t compiled our code yet for production.)

output点是生成所有已编译捆绑代码的位置,该代码将是out app文件夹中的dist文件夹。 (您现在不会看到它,因为我们还没有为生产而编译我们的代码。)

Next we’ll talk about html-webpack-plugin The purpose of this plugin is simple as the name suggests. It creates HTML files to serve all of your bundled files. (All of it — .js, .css, .scss, .img etc)

接下来,我们将讨论html-webpack-plugin顾名思义,此插件的用途很简单。 它创建HTML文件以提供所有捆绑文件。 (所有这些-.js,.css,.scss,.img等)

Let’s talk about when we run the following:

让我们谈谈何时运行以下命令:

$ node_modules/.bin/webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback

This command will open up port http://localhost:8080 or another port if 8080 is taken. (I’ll talk more about what this command does later — for now let’s move on).

如果使用8080此命令将打开端口http://localhost:8080或其他端口。 (稍后我将详细讨论该命令的作用-现在让我们继续)。

The index.html that is generated looks like this:

生成的index.html如下所示:

Blue part: The blue part is simply where I put in my meta tags and defined a title for the app.

蓝色部分:蓝色部分只是我放置我的元标记并为应用定义标题的地方。

Yellow part: The yellow part highlighted is the hard coded part that we wrote in our index.html file. This is where our future React app will reside.

黄色部分:突出显示的黄色部分是我们在index.html文件中编写的硬编码部分。 这是我们未来的React应用程序将驻留的地方。

Red Part: The part I underlined in red is the most interesting part. We never wrote this in our index.html file, so where did it come from?

红色部分:我用红色强调的部分是最有趣的部分。 我们从未在index.html文件中编写此代码,那么它是从哪里来的呢?

Webpack is very smart. It took that file in your index.js , bundled it all up nicely, and added it up all neatly in the file called main.js . Then it injected it in our index.html file. Super Cool!

Webpack非常聪明。 将该文件放入index.js ,将其很好地捆绑在一起,并将其整齐地添加到名为main.js的文件中。 然后将其注入到我们的index.html文件中。 超酷!

We are almost 60% done! Believe me the hard part is over…

我们差不多完成了60% 相信我,困难的部分已经过去了……

让我们添加React (Let’s Add React)

The cool thing is, all our dependencies are already installed. And everything is already configured. So in your app/src/index.js remove all the code and replace it with this:

很棒的事情是,我们所有的依赖项都已安装。 一切都已经配置好了。 因此,在您的app/src/index.js删除所有代码并将其替换为:

import React from 'react';
import ReactDOM from 'react-dom';

const App = () => {
  return (
    

We are a most promising species, Mr. Spock, as predators go. Did you know that? I frequently have my doubts. I dont. Not any more. And maybe in a thousand years or so, we will be able to prove it.

- Captain Kirk

); }; ReactDOM.render(, document.getElementById('app'));

Now if your terminal is still running the webpack-dev-server script, just check the browser out. If not, here is the script. I don’t want you to scroll all the way up again.

现在,如果您的终端仍在运行webpack-dev-server脚本,只需将浏览器检出即可。 如果没有,这是脚本。 我不希望您再次向上滚动。

$ node_modules/.bin/webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback

This is what you will see:

这是您将看到的:

Now make sure you don’t close the terminal, and go in your app/src/index.js and make some changes to your /> component. Try changing the sentence in the paragraph. Once changed, go back to your browser and the content is already there updated. How cool is that? :D

现在,请确保您不关闭终端,并进入app/src/index.js并对 />组件进行一些更改。 尝试更改段落中的句子。 更改后,返回浏览器,内容已经在那里更新。 多么酷啊? :D

This sums up 70% of our tutorial — only 30% more to go. You are doing great.

这总计占我们教程的70%-仅剩30%。 你做的很棒。

让我们添加SCSS支持 (Let’s Add SCSS Support)

Let’s start by updating our config/webpack.base.config.js by adding another rule for .scss files

让我们开始通过为.scss文件添加另一个规则来更新config/webpack.base.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: './index.html'
    }),
  ]
}

So the use I use here takes an array instead of an object like what I did for the .js files. This is because we need to apply a set of rules here:

因此,我在这里use用法是一个数组,而不是像我对.js文件所做的那样的对象。 这是因为我们需要在此处应用一组规则:

use: [ 'style-loader','css-loader','sass-loader' ]

So let’s read the use array from right to left — this is important. What we are telling Webpack is to take any .scss files it finds and parse it for its own understanding using the sass-loader. Once it has converted it into sass, we then ask Webpack to convert the sass into CSS. For that we apply css-loader.

因此,让我们right to left阅读use数组-这很重要。 我们告诉Webpack的是,获取它找到的任何.scss文件,并使用sass-loader对其进行解析以对其自身的理解 一旦将其转换为sass,我们便要求Webpack将sass转换为CSS。 为此,我们使用css-loader

As of this point we have converted the .scss into .css. But there is no way for us to add the converted files into our .html. For this we use the last loader called style-loader which takes all the converted .css and injects it into our index.html file.

至此,我们已将.scss转换为.css。 但是我们无法将转换后的文件添加到.html 。 为此,我们使用最后一个称为样式加载器的加载器 ,该加载器将所有转换后的.css导入到我们的index.html文件中。

So let’s add some .scss to test this out. In your src/ folder add a file called myStyles.scss Mine looks like the one below:

因此,让我们添加一些.scss进行测试。 在您的src/文件夹中,添加一个名为myStyles.scss的文件。

body {
  background-color: skyblue;
  color: black;
}

.app {
  width: 450px;
  margin: 0 auto;
  padding-top: 50px;
}

And my src/index.js file looks like this:

我的src/index.js文件如下所示:

import React from 'react';
import ReactDOM from 'react-dom';

import './myStyles.scss';;

const App = () => {
  return (
    

We are a most promising species, Mr. Spock, as predators go. Did you know that? I frequently have my doubts. I dont. Not any more. And maybe in a thousand years or so, we will be able to prove it.

- Captain Kirk

); }; ReactDOM.render(, document.getElementById('app'));

Restart your webpack-dev-server by running this command again:

通过再次运行以下命令来重新启动webpack-dev-server

$ node_modules/.bin/webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback

This was the last time I’ll make you manually write that script up. After this we will move this command into our scripts section in our package.json.

这是我最后一次让您手动编写该脚本。 之后,我们将该命令移到package.json scripts部分。

Your browser will open up, this is what it looks like now:

您的浏览器将打开,这是现在的样子:

Now in your myStyles.scss file, try making some changes. Like make the font-size: white; come back to your browser. It reflects those changes. You don’t have to restart your server again — just for the .scss to compile.

现在,在myStyles.scss文件中,尝试进行一些更改。 像使font-size: white; 回到您的浏览器。 它反映了这些变化。 您无需再次重新启动服务器-只需编译.scss

With this, most of our development configuration is done. Our React application is live, and has hot module replacement for .js files as well as .scss files

这样,我们大部分的开发配置就完成了。 我们的React应用程序处于活动状态,并且具有.js文件和.scss文件的热模块替换。

So before we move further, let’s add the webpack-dev-server script in our package.json. In your scripts section, add the following code:

因此,在继续操作之前,让我们在package.json添加webpack-dev-server脚本。 在您的scripts 部分,添加以下代码:

"scripts": {
    "start": "webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback --env.PLATFORM=local --env.VERSION=stag",
    "prebuild": "webpack --mode production --config config/webpack.prod.config.js --env.PLATFORM=production --env.VERSION=stag --progress",
    "build": "node server",
},

For now I’ll talk about the start command. I’ll talk about the prebuild and build scripts later in the production configuration section.

现在,我将讨论start命令。 我将在生产配置部分稍后讨论prebuild buildbuild脚本。

So what does this command do: npm run start

那么,该命令的作用是: npm run start

"start": "webpack-dev-server --mode development --config config/webpack.base.config.js --open --hot --history-api-fallback"

Let’s break this down. When we run npm run start we’re telling it to run a package called webpack-dev-server. Then we pass it some configurations.

让我们分解一下。 当我们运行npm run start我们告诉它运行一个名为webpack-dev-server的软件包。 然后我们通过一些配置。

  • webpack-dev-server serves a webpack app and updates the browser on changes.

    webpack-dev-server提供一个webpack应用程序,并根据更改更新浏览器。

  • --mode development tells webpack to compile the code in development mode. This is basically to make the compilation time faster.

    webpack --mode development告诉webpack在开发模式下编译代码。 这基本上是为了加快编译时间。

  • --config config/webpack.base.config.js So by default if you have webpack.config.js file in your root app folder, you don’t have to supply the --config flag to it. But since I want to explicitly add all my webpack related configurations in the config folder, I pass in --config option that tells webpack where to look for the configuration

    --config config/webpack.base.config.js因此,默认情况下,如果您的根app文件夹中有webpack.config.js文件,则不必为其提供--config标志。 但是由于我想在config文件夹中明确添加所有与Webpack相关的配置,因此我传入--config选项,该选项告诉webpack在哪里寻找配置

  • --open command opens the browser, when webpack is done with its compilation.

    --open命令打开浏览器时的WebPack与它的编译完成。

  • --hot flag tells webpack to actively watch for code changes in the src folder. If any changes happen, it reloads the browser.

    --hot标志告诉webpack主动监视src文件夹中的代码更改。 如果发生任何更改,它将重新加载浏览器。

  • --history-api-fallback This option enables History API Fallback support in webpack-dev-server, effectively asking the server to fallback to index.html in the event that a requested resource cannot be found.

    --history-api-fallback此选项启用webpack-dev-server History API Fallback支持,在webpack-dev-server所需资源的情况下有效地要求服务器回退到 index.html

  • --env.PLATFORM & --env.VERSION are custom flags that I pass in my configuration (more on this later).

    --env.PLATFORM--env.VERSION是我在配置中传递的自定义标志(稍后会详细介绍)。

Now that we are done, let move onto our production configurations.

现在我们完成了,让我们继续进行生产配置。

But before we do that, let’s talk about webpack-merge. Now this is a real winner. It takes in one configuration and another one and merges them both together to give us one. The way it works is you need to wrap your configuration with merge like the one below. Let’s start off by making our webpack.base.config.js file into a webpack-merge usable chunk:

但是在我们这样做之前,让我们先谈谈webpack-merge 。 现在,这是一个真正的赢家。 它采用一种配置,然后采用另一种配置,并将它们合并在一起,形成一个配置。 它的工作方式是,您需要像下面的merge那样用merge包装您的配置。 首先,将我们的webpack.base.config.js文件制作成一个webpack-merge可用块:

const webpack = require('webpack');
const merge = require("webpack-merge");

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = env => {
  const { PLATFORM, VERSION } = env;
  return merge([
      {
        module: {
          rules: [
            {
              test: /\.js$/,
              exclude: /node_modules/,
              use: {
                loader: 'babel-loader'
              }
            },
            {
              test: /\.scss$/,
              use: [
                'style-loader',
                'css-loader',
                'sass-loader'
              ]
            }
          ]
        },
        plugins: [
          new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: './index.html'
          }),
          new webpack.DefinePlugin({ 
            'process.env.VERSION': JSON.stringify(env.VERSION),
            'process.env.PLATFORM': JSON.stringify(env.PLATFORM)
          }),
        ],
    }
  ])
};

Previously where we where exporting an object, now we are exporting a function which returns merge and takes in the configuration.

以前我们在导出object ,现在我们正在导出一个返回merge并接受配置的function

Let’s break this down as to what this is doing.The first thing we talk about is this:

让我们来解释一下这是做什么的。我们谈论的第一件事是:

module.exports = function(env) {}

The new flags added in our start command — env.PLATFORM=local — env.VERSION=stag are passed to our webpack configurations, which we can access with the env param in module.exports = function (env) {}. So what can we do with this?

start命令中添加的新标志— env.PLATFORM=local — env.VERSION=stag传递到我们的webpack配置,我们可以使用module.exports = function (env) {}env参数来访问它们。 那么我们该怎么办呢?

  • We can set up a conditional statement in our webpack configuration, that if a certain condition is met, then do this or that (more on this later). Basically we will change our configuration on compile time to cater to whichever environment is being run — production or development.

    我们可以在我们的webpack配置中设置条件语句,如果满足某个条件,则执行此操作或执行该操作(稍后再进行介绍)。 基本上,我们将在编译时更改配置,以适应正在运行的任何环境(生产或开发)。
  • The other thing that we can do here is pass them in our code as well. So what do I mean by pass in our code? One new plugin I added for this is called new webpack.DefinePlugin. (Also that is why I had to include webpack at the top of webpack.base.config.js.) What this does is: “The DefinePlugin allows you to create global constants which can be configured at compile time.” You can read more about this here.

    我们在这里可以做的另一件事就是在我们的代码中传递它们。 那么,我的代码传递是什么意思? 我为此添加的一个新插件称为new webpack.DefinePlugin (这也是为什么我必须在webpack.base.config.js的顶部包含webpack的webpack.base.config.js 。)它的作用是:“ DefinePlugin允许您创建可以在编译时配置的全局常量。 ”您可以在此处了解更多信息

Next we return a configuration inside the function like this:

接下来,我们在函数内部返回如下配置:

return merge({ 
   // our webpack configuration here
});

Well not much has changed here. All we did was wrap our configuration in merge. This gives us the ability to merge this entire configuration into the other one that we will create.

嗯,这里没有太大变化。 我们所做的只是将配置包装在merge 。 这使我们能够merge整个配置merge到我们将要创建的另一个配置中。

One thing added is a new plugin called DefinePlugin which I already talked about.

添加的一件事是一个我已经讨论过的名为DefinePlugin的新插件。

If you are a nerd like me and want to dig deeper into webpack-merge I suggest you dive in herethis was developed by the cool folks at SurviveJS.

如果您是像我这样的书呆子,并且想更深入地研究webpack-merge我建议您潜入这里 -这是由SurviveJS

Before moving to the production settings, let’s check if our base configurations are working.

在转到production设置之前,让我们检查一下我们的基本配置是否有效。

In your src/index.js file add this somewhere:

在您的src/index.js文件中,将此添加到某处:

console.log('process.env.VERSION', process.env.VERSION);
console.log('process.env.PLATFORM', process.env.PLATFORM);
console.log('process.env.NODE_ENV', process.env.NODE_ENV);

In your terminal run npm run start. Wait for your browser to load up. Open up your terminal.

在您的终端中运行npm run start 。 等待浏览器加载。 打开您的终端。

The first two you see in the console is the result of us passing the --env flags from our script to our webpack configuration and setting it with DefinePlugin. The third one is with the --mode flag that we pass in our script. If mode is development or production, then that is set up in our process.env.NODE_ENV flag.

您在控制台中看到的前两个是我们将--env标志从脚本传递到Webpack配置并使用DefinePlugin进行设置的结果。 第三个是在脚本中传递的--mode标志。 如果模式是开发或生产,则在我们的process.env.NODE_ENV标志中进行设置。

Now that that’s cleared up, let’s move on.

现在已经清除了,让我们继续。

In your config folder, create a new file called webpack.prod.config.js and add the following code into it as shown below:

在您的config文件夹中,创建一个名为webpack.prod.config.js的新文件,并将以下代码添加到其中,如下所示:

var merge = require('webpack-merge');

// Plugins
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var Visualizer = require('webpack-visualizer-plugin');

var baseConfig = require('./webpack.base.config');

const prodConfiguration = env => {
  return merge([
    {
      optimization: {
        runtimeChunk: 'single',
        splitChunks: {
          cacheGroups: {
            vendor: {
              test: /[\\/]node_modules[\\/]/,
              name: 'vendors',
              chunks: 'all'
            }
          }
        },
        minimizer: [new UglifyJsPlugin()],
      },
      plugins: [
        new OptimizeCssAssetsPlugin(),
        new Visualizer({ filename: './statistics.html' })
      ],
    },
  ]);
}

module.exports = env => {
  return merge(baseConfig(env), prodConfiguration(env));
}

Let’s start from the bottom with module.exports = env => {}

让我们从模块的底部开始module.exports = env => {}

We merge two configurations: one is our baseConfig and the other is prodConfiguration . The --env flags we pass in our scripts are passed on as an object in the env => {} params in our function. We then pass them onto both the baseConfig & prodConfig.

我们合并两个配置:一个是我们的baseConfig ,另一个是prodConfiguration 。 我们在脚本中传递的--env标志作为对象在函数中的env => {}参数中传递。 然后,我们将它们传递给he baseCon无花果& prodCon无花果。

So what is prodConfig ?

那么prodConfigprodConfig

It is basically a list of the optimizations we want to perform when our code goes up for production.

从根本上讲,这是我们在代码投入生产时要执行的优化的列表。

The optimization.minimizer takes in a new UglifyJsPlugin. What this does is uglify and minify our .js files.

optimization.minimizer接受了一个new UglifyJsPlugin 。 这是在丑化和缩小我们的.js文件。

The optimization.splitChunks actually takes all of your common code and creates a vendor.bundle.js file. It is not going to make one now. But as our code base grows, we have multiple routes, and there are different modules being used like date-fns moment lodash material-ui etc. It will take all the common code from entire app and make a common file called vendor.bundle.js . This way, the repeated code isn’t used again & again. (I am against this approach, but for educational purposes I described it here.)

optimization.splitChunks实际上采用了所有通用代码,并创建了vendor.bundle.js文件。 它现在不会成为一个。 但是随着代码库的增长,我们有多种途径,并且使用了不同的模块,例如date-fns lodash moment lodash material-ui等。它将从整个应用程序中获取所有通用代码,并创建一个名为vendor.bundle.js的通用文件vendor.bundle.js 。 这样,重复的代码就不会再使用了。 (我反对这种方法,但是出于教育目的,我在此进行了描述。)

Going forward I’ll comment the optimization.splitChunks but, it will exist there in the code repository if you want to use it. You just have to uncomment this section. I prefer to split my code based on routes. Having common code chunked out into a separate module means that your entire common code is going to be loaded first. This can be huge, and as a result the user’s first time interaction will take longer (because now all of these dependencies are being loaded, which might not need to be in the respective page that the user is seeing/viewing).

展望未来,我将对optimization.splitChunks评论,但是,如果您要使用它,它将存在于代码存储库中。 您只需要取消注释此部分。 我更喜欢根据路线拆分代码。 将通用代码分块到一个单独的模块中意味着您将首先加载整个通用代码。 这可能非常庞大,结果用户的首次交互将花费更长的时间(因为现在所有这些依赖项都已加载,可能不需要在用户正在查看/查看的相应页面中)。

Next up, we have a couple of plugins. One of them happens to be new OptimizeCssAssetsPlugin(). All it does is take all of our generated .css and minify/optimize it. This doesn’t work right now, because we are using style-loader and style loader directly injects the generated .css into the DOM.

接下来,我们有几个插件。 其中之一恰好是new OptimizeCssAssetsPlugin() 。 它所做的就是.css所有我们生成的.css并将其最小化/优化。 现在这行不通,因为我们正在使用style-loader并且样式加载器将生成的.css直接注入DOM中。

First, we need tell webpack to extract all the generated .css into a separate file, and then the optimizations added by this plugin are applied. (We’ll do this a bit later.)

首先,我们需要告诉webpack将所有生成的.css提取到一个单独的文件中,然后应用此插件添加的优化。 (我们稍后再做。)

The other plugin added here is called new Visualizer({ filename: ‘./statistics.html’ }) .This plugin is awesome: it generates a statistics.html file in the dist/ folder for you. Open the file, and you’ll see a graphic like the one below.

此处添加的另一个插件称为new Visualizer({ filename: './statistics.html' }) 。此插件很棒:它在dist/文件夹中为您生成statistics.html文件。 打开文件,您将看到类似下图的图形。

Right now we only have a single module called main.js. But with time, as we add more modules, and have code splitting added to it. More modules will start showing up here and we can actually see what modules take what size. This can be really useful when you are trying to reduce the size of your application.

现在,我们只有一个名为main.js模块。 但是随着时间的推移,随着我们添加更多模块,并添加了代码拆分功能。 更多模块将在此处开始显示,我们可以实际看到哪些模块具有什么大小。 当您尝试减小应用程序的大小时,这可能非常有用。

Coming back to OptimizeCssAssetsPlugin() . In order to optimize the .css generated, we need to move this into a separate module. For that I am going to use mini-css-extract-plugin This will require us to make changes in both of our webpack files, the .base and .prod files.

返回到OptimizeCssAssetsPlugin() 。 为了优化生成的.css,我们需要将其移至单独的模块中。 为此,我将使用mini-css-extract-plugin这将要求我们对我们的webpack文件, .base.prod文件进行更改。

Let’s talk about the changes I made in webpack.base.config.js .Only one module was added called const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);. Then in our .scss rules we checked if the PLATFORM flag passed has the value production. If so, we add MiniCssExtractPlugin.loader, and otherwise we add the style-loader.

我们来谈谈我在webpack.base.config.js所做的更改。仅添加了一个名为const MiniCssExtractPlugin = require(“mini-css-extract-plugin”); 。 然后在我们的.scss规则中,我们检查传递的PLATFORM标志是否具有值production 。 如果是这样,我们添加MiniCssExtractPlugin.loader ,否则我们添加style-loader

style-loader is used to actively watch and change our compiled .css in development mode, while MiniCssExtractPlugin.loader is used when we need to extract that generated CSS into a separate module. This is only for production.

style-loader用于在开发模式下主动监视和更改已编译的.css ,而MiniCssExtractPlugin.loader用于需要将生成CSS提取到单独的模块中时使用。 这仅用于生产。

In the other file webpack.prod.config.js we have these two plugins added:

在另一个文件webpack.prod.config.js我们添加了以下两个插件:

new MiniCssExtractPlugin(),
new OptimizeCssAssetsPlugin(),

The first will extract this into a separate module called main.css and the other will minify/uglify the generated CSS.

第一个将其提取到名为main.css的单独模块中,另一个将最小化/丑化生成CSS。

Having done this, we are almost 90% done. If you have stayed this far, kudos to you.

完成此操作后,我们几乎完成了90%。 如果您住得这么远,就对您表示敬意。

Before we proceed further, here is what Captain Kirk has to say

在继续之前,这是柯克船长必须说的

You know the greatest danger facing us is ourselves, and irrational fear of the unknown. There is no such thing as the unknown. Only things temporarily hidden, temporarily not understood.

您知道我们面临的最大危险是我们自己,以及对未知事物的非理性恐惧。 没有未知的事物。 只有暂时隐藏的东西,暂时无法理解。

- James T. Kirk, The Corbomite Maneuver
-詹姆斯·柯克(James T. Kirk),Corbomite演习

Let’s add more functionality, to our code. Now there are two ways to add files in your code. One is by using another loader called file-loader which will help you add files of any type into your .js files like we did with .scss files.

让我们在代码中添加更多功能。 现在,有两种方法可以在代码中添加文件。 一种方法是使用另一种称为file-loader ,它将像您对.scss文件所做的那样,帮助您将任何类型的文件添加到.js文件中。

I want to talk about another approach here, because I think assets like fonts, images and others should be loaded in parallel rather than in your .js files. This helps provide a better experience for the user. So for that propose we will load our images statically.

我想在这里讨论另一种方法,因为我认为应该并行加载字体,图像和其他资源,而不是.js文件。 这有助于为用户提供更好的体验。 因此,对于该建议,我们将静态加载图像。

For this we will use a plugin called copy-webpack-plugin. The best thing about all this is you already have this installed. In your webpack.base.config.js add another plugin, like the below:

为此,我们将使用一个名为copy-webpack-plugin 。 关于这一切的最好的事情是您已经安装了这个。 在您的webpack.base.config.js添加另一个插件,如下所示:

The copy-webpack-plugin takes in an argument called from. This tells the plugin where to locate the static files and then copy them in the dist folder. Here I am telling it to look for a folder called src/static and copy all of its content in the dist/ folder.

copy-webpack-plugin接受一个名为from的参数。 这告诉插件在哪里可以找到静态文件,然后将其复制到dist文件夹中。 在这里,我告诉它寻找一个名为src/static的文件夹,并将其所有内容复制到dist/文件夹中。

Once you have added this and set it up, all you have to do is, in your app/src folder, create a new folder called static . In this folder, create another folder called images so your folder will have a directory like this: app/src/static/images

添加并设置完毕后,您要做的就是在app/src文件夹中创建一个名为static的新文件夹。 在此文件夹中,创建另一个名为images文件夹,以便您的文件夹具有以下目录: app/src/static/images

I am going to put an image here called header.jpg, but you can call it whatever you like. This is the image I am using: https://unsplash.com/photos/Idi6I490p7I (Photo by Felix Mittermeier on Unsplash).

我将在此处放置一个名为header.jpg的图像,但是您可以随意命名。 这是我正在使用的图像: https : //unsplash.com/photos/Idi6I490p7I ( Felix Mittermeier在Unsplash上拍摄 )。

Now in order for this to work, you need to run the npm run prebuild command (I’ll talk more about npm run prebuild & npm run build later when we set up our NodeJS server with ExpressJS) because we need our static files to be copied. The npm run start command won’t copy this to the dist/ folder because it doesn’t compile code to the dist/ folder.

现在,为了使其正常工作,您需要运行npm run prebuild命令(稍后我们将ExpressJS设置为npm run prebuild服务器时,我将详细介绍npm run prebuildnpm run build ),因为我们需要将static文件复制。 npm run start命令不会将其复制到dist/文件夹,因为它不会将代码编译到dist/文件夹。

Once you have run the npm run prebuild command this is what you will see:

运行npm run prebuild命令后,将看到以下内容:

So how can we access this file in our code?

那么如何在代码中访问该文件呢?

I am going to make some changes in my index.js file along with myStyles.scss .You can follow along as well — we’re just adding an /> along with some .scss

我将与myStyles.scss一起在index.js文件中进行一些更改。您也可以遵循—我们只是在 />和some .scss中添加

The only thing to note here is in the index.js file where I add an image:

唯一需要注意的是在index.js文件中添加图像的位置:

header

The main thing is the path we give in the src.

最主要的是我们在src给出的路径。

Once you have added this, let’s check how this looks in the browser. Go and run npm run start command.

添加完后,让我们检查一下它在浏览器中的外观。 去运行npm run start命令。

让我们回顾一下到目前为止我们已经完成的成就 (Let’s recap what we have accomplished so far)
  • Setting up Webpack 4 with Babel 7 for React

    使用Babel 7为React设置Webpack 4
  • Support for .SCSS

    支持.SCSS
  • Development environment with HMR [For both .js & .scss]

    使用HMR的开发环境[.js和.scss都适用]
  • Production configuration

    生产配置
  • Dividing your Webpack configuration into chunks

    将您的Webpack配置分为多个块
  • Generating a visualizer in production build to check which chunk of code is how big and what are the dependencies of the chunks. Super useful.

    在生产版本中生成可视化工具,以检查哪个代码块有多大以及这些块的依存关系是什么。 超级有用。
  • Support for static files

    支持静态文件
我们仍然需要完成的事情 (Things We Still Need To Accomplish)
  • Add support for async/await in our code

    在我们的代码中添加对async/await支持

  • Create a NodeJS server using ExpressJS for our production build

    使用ExpressJS为我们的生产版本创建NodeJS服务器
  • Code Splitting

    代码分割

Let’s start with async/await first. For this purpose I am going to make a smart /> component. Inside this component I am going to call an API that gets me information about Captain Kirk, because he is awesome. So in our index.js add the following code:

让我们先从async/await开始。 为此,我将创建一个智能的 />组件。 在该组件内部,我将调用一个API,该API向我获取有关Kirk船长的信息,因为他很棒。 因此,在我们的index.js添加以下代码:

All I am doing here is calling an API using try/catch async/await and getting information about Captain Kirk. Simple right? This should work. Let’s fire this up in the browser.

我在这里所做的只是使用try/catch async/await调用API并获取有关Kirk上尉的信息。 简单吧? 这应该工作。 让我们在浏览器中启动它。

Run the command:

运行命令:

npm run start

If you hit ctrl+shift+j your console will open up, and you will see an error there called regeneratorRuntime . So what is this error and how do we get rid of it?

如果您按ctrl+shift+j您的控制台将打开,您将在此看到一个名为regeneratorRuntime的错误 那么这个错误是什么,我们如何消除它呢?

This error gets thrown when the browser doesn’t support async/await or generators for that matter.

当浏览器不支持async/awaitgenerators器时,将抛出此错误。

But Adeel! That’s the only reason we are using babel right?

但是阿黛尔 ! 那是我们使用通天塔的唯一原因吗?

Yes! Here’s what Henry Zhu, the awesome dude behind babel, has to say about this:

是! 这是通天塔背后的真棒哥们亨利·朱 ( Henry Zhu)对此的评价:

If you are using generators/async and the environment doesn’t support it natively we compile using regenerator which uses a runtime. So you’ll have to include regeneratorRuntime either yourself or use babel-polyfill.

如果您使用的是generators / async而环境本身不支持它,我们将使用运行时的regenerator进行编译。 因此,您必须自己包括regeneratorRuntime或使用babel-polyfill。

Reference taken from an issue.

参考来自一个问题

Now you know why this exists, so let’s solve it. We need to make some changes to our webpack.base.config.js:

现在您知道为什么存在这种情况了,让我们解决它。 我们需要对我们的webpack.base.config.js进行一些更改:

const path = require('path');
const webpack = require('webpack');
const merge = require("webpack-merge");

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const APP_DIR = path.resolve(__dirname, '../src'); // <===== new stuff added here

module.exports = env => {
  const { PLATFORM, VERSION } = env;
  return merge([
      {
        entry: ['@babel/polyfill', APP_DIR], // <===== new stuff added here
        module: {
          rules: [
            {
              test: /\.js$/,
              exclude: /node_modules/,
              use: {
                loader: 'babel-loader'
              }
            },
            {
              test: /\.scss$/,
              use: [
                PLATFORM === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
                'css-loader',
                'sass-loader'
              ]
            }
          ]
        },
        plugins: [
          new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: './index.html'
          }),
          new webpack.DefinePlugin({ 
            'process.env.VERSION': JSON.stringify(env.VERSION),
            'process.env.PLATFORM': JSON.stringify(env.PLATFORM)
          }),
          new CopyWebpackPlugin([ { from: 'src/static' } ]),
        ],
    }
  ])
};

Check line no.8 and line no.14 in the snippet added above.

检查上面添加的代码段中的第line no.8line no.14

By default Webpack 4 takes in entry point of src/. But if we want to have multiple entry points, we can customize the entry point as well. In my entry point I am just telling it two things:

默认情况下,Webpack 4接受src/入口点。 但是,如果我们要有多个入口点,则也可以自定义entry点。 在我的切入点中,我只是告诉两件事:

entry: ['@babel/polyfill', APP_DIR],
  • @babel/polyfill Babel plugin that includes a polyfill that includes a custom regenerator runtime and core-js.

    @babel/polyfill Babel插件,其中包括一个polyfill ,其中包括一个自定义的再生器运行时和core-js 。

  • APP_DIR the path to our src/ folder which I wrote on line no.8 const APP_DIR = path.resolve(__dirname, ‘../src’); All this line is doing is pointing to the path of src/ folder in our app/ folder.

    APP_DIR我在line no.8写的src/文件夹的路径const APP_DIR = path.resolve(__dirname, '../src'); 这行所要做的只是指向我们的app/文件夹中src/文件夹的路径。

So the entry just takes in “points” as to what to compile.

因此,该entry仅包含有关编译内容的“要点”。

Now that this is cleared up, let’s run the npm run start command again.

现在已清除此问题,让我们再次运行npm run start命令。

So far so good!

到目前为止,一切都很好!

现在已经完成了所有设置,让我们使用ExpressJS创建一个NodeJS服务器。 (Now that everything is set up, let’s create a NodeJS server using ExpressJS.)

The first thing we have to install is Express, so in your terminal write this:

我们必须安装的第一件事是Express,因此在您的终端中输入以下内容:

npm install express --save

Or if you use yarn (like I do):

或者,如果您使用纱线 (像我一样):

yarn add express

Next in the root app folder create a new folder called server. Inside the folder create a index.js file like the one shown below:

接下来,在根app文件夹中创建一个名为server的新文件夹。 在该文件夹内,创建一个index.js文件,如下所示:

Let’s discuss this code before we proceed further.

在继续进行之前,让我们讨论一下此代码。

We instantiate our app with express() and then set up a static public folder called dist. This is the same folder created by Webpack when we run our production command.

我们使用express()实例化应用程序,然后设置一个名为dist的静态公共文件夹 这是我们运行生产命令时Webpack创建的文件夹。

We include our routes file — we will create that in a second — and set the routes file to the / directory.

我们包含了routes文件-我们将在稍后创建它-并将routes文件设置为/目录。

Next we set up a port. If none is provided via the node CLI, we use port 3000. After that, we create an HTTP server and listen on that server via the port. At the very last, we console to our terminal that we are running the server on that certain port.

接下来,我们设置一个端口。 如果未通过节点CLI提供任何服务,则使用端口3000 。 之后,我们创建一个HTTP服务器,并通过端口在该服务器上进行侦听。 最后,我们在终端上控制台我们正在该特定端口上运行服务器。

Let’s create our last file called routes/index.js:

让我们创建最后一个名为routes/index.js :文件routes/index.js :

const path = require('path');
const router = require('express').Router();

router.get('*', (req, res) => {
  const route = path.join(__dirname, '..', '..', 'dist', 'index.html');
  res.sendFile(route);
});

module.exports = router;

Here we check that whatever the user comes on, the path redirects the user to the dist/index.html where our React application lives.

在这里,我们检查是否有用户参与,该路径会将用户重定向到React应用程序所在的dist/index.html

And that’s it. We are done.

就是这样。 我们完了。

Now go in your terminal and type:

现在进入终端并输入:

npm run build

This will take a moment. It will show you the progress while it compiles. After that, it consoles a message that it is listening to port 3000 if no port is provided.

这需要一点时间。 它将在编译时向您显示进度。 此后,如果没有提供listening to port 3000 ,它将发出一条消息,通知它正在listening to port 3000

Now go to your browser http:localhost:3000/ and your application is alive.

现在转到浏览器http:localhost:3000/ ,您的应用程序仍处于运行状态。

Since we are at it, let’s talk in detail about what npm run prebuild and npm run build do.

npm run prebuild ,让我们详细讨论npm run prebuildnpm run build作用。

Basically if we write the word pre for a script, in this case prebuild, every time we run our command npm run build it will first execute npm run prebuild and then run the script npm run build .

基本上,如果我们为脚本写一个词pre ,在这种情况下是prebuild ,则每次运行命令npm run build ,它将首先执行npm run prebuild ,然后运行脚本npm run build

All npm run build does is run node server/index.js (You don’t have to write /index.js) in the command. NodeJS is smart enough to know it needs to run the index.js inside the server folder.

npm run build所做的所有工作都是在命令中运行node server/index.js (您不必编写/index.js)。 NodeJS非常聪明,知道需要在server文件夹中运行index.js

This sums up our NodeJS application setup as well.

这也总结了我们的NodeJS应用程序设置。

One last topic to go. I’ll give a very brief overview on code splitting, and how you can achieve it.

最后一个话题。 我将简要概述代码拆分以及如何实现。

代码分割 (Code Splitting)

At the start of this tutorial, we added @babel/plugin-syntax-dynamic-import This gives us the ability to lazily load our code inside our application.

在本教程开始时,我们添加了@babel/plugin-syntax-dynamic-import这使我们能够将代码延迟加载到应用程序中。

Inside my src/ folder, I am going to create a component called Foo.js which looks something like this.

在我的src/文件夹中,我将创建一个名为Foo.js的组件,它看起来像这样。

Nothing special about Foo here.

这里的Foo没什么特别的。

The special thing starts when we include this component in our src/index.js file.

当我们在src/index.js文件中包含此组件时,特殊的事情就开始了。

You might be thinking something like this:

您可能在想这样的事情:

import Foo from './Foo';
class App extends React.Component {
   state = {};
   render() {
      return (
        

I am App

) } }

Well no, for a dynamic import we have to do this:

好吧,对于动态导入,我们必须这样做:

Things to note here are in line 9 line 14, 15, 16 line 40 line 57:

事情需要注意这里是line 9 line 14, 15, 16 line 40 line 57

  • Line 9: We Set Foo as null

    Line 9 :我们将Foo设置为null

  • Line 14, 15, 16 : As soon as our component mounts, we import our /> component

    Line 14, 15, 16 :装入组件后,我们将导入 />组件

Let’s talk more about this:

让我们更多地谈论这个:

import(/* webpackChunkName: 'Foo' */ './Foo').then(Foo => {     
   this.setState({Foo: Foo.default });    
})

Let’s break this down even more.

让我们进一步分解一下。

import(/* webpackChunkName: ‘Foo’ */ ‘./Foo’) : This has 2 parts to it, we set a chunk name called Foo in /* webpackChunkName: ‘Foo’ */. You can call this whatever you want. What this does is when your application loads the ./Foo file, it will get loaded by the name of Foo as defined in /* webpackChunkName: ‘Foo’ */

import(/* webpackChunkName: 'Foo' */ './Foo') :这有2个部分,我们在/* webpackChunkName: 'Foo' */设置了一个名为Foo的块名称。 您可以随便叫这个。 这是当您的应用程序加载./Foo文件时,它将按照/* webpackChunkName: 'Foo' */定义的Foo名称进行加载/* webpackChunkName: 'Foo' */

This feature is called magic comments in webpack, because it’s lets you name the file when you load it in your code.

此功能在webpack中称为魔术注释,因为它使您可以在将代码加载到文件中时为文件命名。

The other part of import(/* webpackChunkName: ‘Foo’ */ ‘./Foo’) is the ‘./Foo’ at the very end of the statement. This is the path from where we include our file.

import(/* webpackChunkName: 'Foo' */ './Foo')的另一部分是语句末尾的'./Foo' 。 这是我们包含文件的路径。

This returns us a promise .then(Foo => {}). Since our export of <Foo /> was export default when we set our state of Foo we set it to this.setState({Foo: Foo.default }); in order to assign the Foo component to the state variable Foo.

这返回给我们一个承诺.then(Foo => {})。 由于我们的出口of <富/ > was expor吨默认当我们设置我们的sta的Foo的TE我们小号et it to this.setState({Foo: Foo.de故障}); 为了将Foo组件分配给状态变量Foo。

line 57 : This is where we display our /> component. Unless it is not loaded i.e, it is null, we show a loading message. And once we have the &lt;Foo /> component we show it.

line 57 :这是我们显示 />组件的地方。 除非未加载,即为null,否则我们将显示加载消息。 并且一旦有了e the & lt; Foo />组件,我们就会展示它。

And that, my friends, is code splitting.

我的朋友们,这就是代码拆分。

I really do hope this was helpful for you. If it was please do let me know so that I can write more stuff like this. You can always reach me out on Twitter and again if you followed along till the end, I am really proud of you guys. YOU GUYS ARE ROCKING IT!

我确实希望这对您有所帮助。 如果是的话,请告诉我,以便我可以写更多这样的东西。 您随时可以在Twitter上与我联系 如果你们一直坚持到底,我真的为你们感到骄傲。 你们正在摇滚!



This article was originally published in Freecodecamp publication previously on Medium. Read here

本文最初发布在Freecodecamp出版物上,之前在Medium上。 在这里阅读

翻译自: https://www.freecodecamp.org/news/how-to-combine-webpack-4-and-babel-7-to-create-a-fantastic-react-app-845797e036ff/

你可能感兴趣的:(python,java,vue,编程语言,javascript,ViewUI)