使用webpack构建项目
How far can we get with Webpack’s default configuration?
Webpack的默认配置能达到多远?
I had the privilege of speaking at GDG Devfest last month with a talk that was centered around using webpack in our modern day applications. You can check out the slides here.
上个月,我荣幸地在GDG Devfest上发表了演讲,主题是在现代应用程序中使用webpack。 您可以在此处查看幻灯片。
Daily, I get to work as an Engineer and/or consultant with amazing and fast-paced teams, and webpack seems like the recurring factor throughout these teams (we use ReactJs for most of our applications). Initially, my talk was supposed to focus on using webpack with frontend frameworks/libraries like ReactJS, Vue, Angular etc.
每天,我都会在惊人而快节奏的团队中担任工程师和/或顾问,而Webpack似乎是这些团队中反复出现的因素(我们在大多数应用程序中使用ReactJs)。 最初,我的演讲应该集中在将Webpack与诸如ReactJS,Vue,Angular等前端框架/库一起使用。
Before submitting my proposal, I decided to run a mini survey to know what people thought about webpack. To my surprise, a lot of people labeled webpack as “Only used with frameworks” which was far from the truth. Still others said that “setting up webpack was daunting”. This led me to focus more on using webpack with Vanilla JS and seeing how far we could go with webpack’s default configuration.
在提交提案之前,我决定进行一次小型调查,以了解人们对Webpack的看法。 令我惊讶的是,很多人将webpack标记为“仅用于框架”,这与事实相去甚远。 还有一些人说“设置webpack令人生畏”。 这使我更加专注于将Webpack与Vanilla JS结合使用,并了解到webpack的默认配置可以走多远。
But first:
但首先:
什么是WEBPACK? (WHAT IS WEBPACK?)
I personally define webpack as a tool that takes many Javascript modules and merges them into one Javascript module that can be sent off to the browser.
我个人将webpack定义为一种工具,它使用许多Javascript模块并将它们合并为一个Javascript模块,然后可以将其发送到浏览器。
I know, it’s an oversimplification of what webpack does, but people seem to understand it. To explain more, webpack is a bundler that looks for Javascript modules with dependencies (basically, Javascript files that need code from other Javascript files), squashes them together, and then produces a Javascript file or files that have no dependencies. That way they can easily be shipped to the browser.
我知道,这是对webpack的简化,但是人们似乎理解它。 为了进一步说明,webpack是一个捆绑程序,用于查找具有依赖性的Javascript模块(基本上是需要其他Javascript文件中的代码的Javascript文件),将它们压缩在一起,然后生成一个或多个不具有依赖性的Javascript文件。 这样,就可以轻松将它们运送到浏览器。
Webpack的历史 (History of Webpack)
To understand the problems that webpack tries to solve, we need to know a little bit about the history of webpack itself. To keep this section very short, I’ve just outlined two important tools and one concept:
要了解webpack试图解决的问题,我们需要了解一下webpack本身的历史。 为了使本节简短,我仅概述了两个重要的工具和一个概念:
Google Web Toolkit: This is a framework from Google that converts Java to Javascript (I know, right?). It has one feature that seems to be my personal favorite feature in webpack which is “code splitting”. (I will explain code splitting in a subsequent article.)
Google Web Toolkit :这是Google的框架,可将Java转换为Javascript(我知道,对吗?)。 它有一个功能似乎是我在webpack中最喜欢的功能,即“代码拆分”。 (我将在后续文章中解释代码拆分。)
Modules_Webmake: This is the library that webpack originates from. It’s essentially a tool that allows us to organize our Javascript files for the browser the same way we do for NodeJS (awesome).
Modules_Webmake :这是webpack源自的库。 从本质上讲,它是一种工具,它使我们可以为浏览器组织Javascript文件,就像对NodeJS一样。
- IIFE: means immediately invoked function expression. This is basically a Javascript function that is called or invoked the same time as it was created. IIFE:表示立即调用的函数表达式。 这基本上是一个Javascript函数,它在创建时就被调用或调用。
立即调用函数表达式 (Immediately Invoked Function Expression)
I broke this into its own section because I had to explain further. This is an example of an IIFE:
我将其分为自己的部分,因为我不得不进一步解释。 这是IIFE的一个示例:
If we were to place this function in our script tag, this would run immediately. The script tag is loaded by the browser. It’s kind of equivalent to attaching a function to window.onload
but with an added advantage.
如果我们将此功能放在脚本标签中,它将立即运行。 脚本标记由浏览器加载。 这等效于将一个函数附加到window.onload
但具有更多优点。
Because of the way closures work in Javascript, all the variables that were declared in the IIFE are scoped within that function. This means I wouldn’t have issues like namespace clashes in my codebase but at the same time, I still have access to the functions exposed by the IIFE.
由于闭包在Javascript中的工作方式,因此IIFE中声明的所有变量都在该函数内作用域。 这意味着我不会在代码库中遇到名称空间冲突之类的问题,但是与此同时,我仍然可以访问IIFE公开的功能。
为什么选择Webpack? (Why Webpack?)
So, what are the problems we face today that webpack helps us solve?
那么,webpack可以帮助我们解决什么今天的问题呢?
First, we have the issue of script tags. I have worked on a codebase where each HTML page has at the very least 30 script tags arranged in a very precise order. I know some might say that isn’t really an issue, but the browser will have to make one request per file which can hurt your “time to load”. Also the script tags can get hard to manage, where rearranging just one could break the application (I tried that ?).
首先,我们有脚本标签的问题。 我曾在一个代码库上工作,其中每个HTML页面至少有30个脚本标签以非常精确的顺序排列。 我知道有人可能说这不是真正的问题,但是浏览器将不得不对每个文件发出一个请求,这可能会损害您的“加载时间”。 此外,脚本标签可能会变得难以管理,在其中重新排列可能会破坏应用程序(我尝试过吗?)。
Second, we still have the issue of namespacing where the global namespace can get cluttered. I know we are very creative people especially when it comes to naming variables, but then when you work on a larger team there are times where the variable names just clash with each other. Or even your future self might think of the same name again (yeah it happens).
第二,我们仍然遇到命名空间问题,这会使全局名称空间变得混乱。 我知道我们非常有创造力,尤其是在命名变量时,但是当您在更大的团队中工作时,有时变量名之间会发生冲突。 甚至未来的自己可能会再次想到相同的名字(是的,它发生了)。
I know some organizations that make it habit for their developers to always keep their variables within the scope of their function, but we can’t always rely on that (or on this
). In the end, it just makes separation of concern difficult.
我知道有些组织习惯于让开发人员习惯于将变量始终保持在其功能范围之内,但是我们不能始终依赖this
(或this
)。 最后,这只会使关注点分离变得困难。
Third, remember I mentioned that webpack originated from modules_webmake. Because webpack allows us to organize our files the same way we do in NodeJS (using CommonJS), we have the added benefit of writing modular code which scales really well (just ask people that use frontend frameworks).
第三,记得我提到过webpack起源于modules_webmake。 因为webpack允许我们像在NodeJS中一样(使用CommonJS)来组织文件,所以我们还有额外的好处,那就是编写模块化代码,可以很好地扩展(请问使用前端框架的人)。
普通JS (CommonJS)
I won’t explain too much about CJS as this isn’t the point of the article. But you can say it’s a JS module system used in NodeJS.
我不会对CJS进行过多解释,因为这不是本文的重点。 但是您可以说这是NodeJS中使用的JS模块系统。
Webpack allows us to use this module and even the “better” ES module system in the browser without any issue (Webpack handles it in a smart way). This helps us write really modular and maintainable code where a JS file can handle a single functionality (Single Responsibility Principle).
Webpack允许我们在浏览器中使用此模块,甚至使用“更好的” ES模块系统(Webpack以智能的方式进行处理)。 这可以帮助我们编写真正的模块化且可维护的代码,其中JS文件可以处理单个功能(单一职责原则)。
ES模块(ESM) (ES Modules (ESM))
This is another module system that, believe it or not, is already implemented by current browsers. But unfortunately, it has it’s limitations there. Webpack also allows us to use this module with no issue (as webpack still converts it in the end), but I found that using ESM makes most codebases I have worked on more readable. I would have loved to dive deeper into this but that isn’t the aim of this article. For a better explanation I would recommend this amazing article.
不管您相信与否,这是当前浏览器已经实现的另一个模块系统。 但是不幸的是,它在那里有局限性。 Webpack还允许我们毫无问题地使用此模块(因为Webpack最终仍会对其进行转换),但是我发现使用ESM使我研究过的大多数代码库更具可读性。 我本来想更深入地研究这一点,但这不是本文的目的。 为了得到更好的解释,我推荐这篇了不起的文章 。
Webpack如何工作? (How does Webpack work?)
I know I said earlier that Webpack is magic but I lied. To put it as simply as possible:
我知道我之前说过Webpack很神奇,但是我撒了谎。 尽可能简单地说:
- Webpack takes a path to a single entry point, which is a JS file, and looks for import statements (it could either be ESM or CJS). Webpack采取指向单个入口点(即JS文件)的路径,并寻找导入语句(可以是ESM或CJS)。
- It then traverses the imported file, also looking for more import statements, while it creates a dependency graph in the process. 然后,它遍历导入的文件,同时在过程中创建依赖关系图时还要查找更多的导入语句。
To explain better, take a look at the image:
为了更好地解释,请看一下图片:
I have two files there, index.js
and helpers.js
These two files perform different functions, but I am importing and using the function in helpers.js in my index.js file. By default, Webpack’s entry point is ./src/index.js
and from there it tries to build the dependency graph as shown below:
我在那里有两个文件, index.js
和helpers.js
这两个文件执行不同的功能,但是我正在index.js文件中的helpers.js中导入和使用该功能。 默认情况下,Webpack的入口点是./src/index.js
并从那里尝试构建依赖关系图,如下所示:
如何开始 (How to get started)
To get a better understanding of how webpack works, we are going to build a simple TODO app. It will have just the basic add and delete functionality and we are going to bundle it using Webpack’s default configuration (so no webpack config file). This is what the app will look like:
为了更好地了解webpack的工作原理,我们将构建一个简单的TODO应用。 它仅具有基本的添加和删除功能,我们将使用Webpack的默认配置(因此没有webpack配置文件)将其捆绑。 该应用将如下所示:
The first step is to create a new project directory and create two folders, a folder named dist
and another named src
. By default, Webpack’s entry point is the path ./src/index.js
and it outputs the bundled JS into ./dist/main.js
— that’s why we are creating the two folders.
第一步是创建一个新的项目目录,并创建两个文件夹,一个名为dist
的文件夹,另一个名为src
的文件夹。 默认情况下,Webpack的入口点是路径./src/index.js
,它将捆绑的JS输出到./dist/main.js
-这就是我们创建两个文件夹的原因。
In the dist
folder you can create the index.html
file. This is not necessary for webpack as the file can be placed anywhere within the project directory and you can just make reference to the main.js
file. In the end, your project structure should look like this:
在dist
文件夹中,您可以创建index.html
文件。 对于webpack而言,这不是必需的,因为该文件可以放置在项目目录中的任何位置,并且您可以仅引用main.js
文件。 最后,您的项目结构应如下所示:
In the src
folder we will create the index.html
file where we will start the implementation of our TO-DO app’s functionalities. But first, let’s populate the index.html
file. Since creating a TO-DO app isn’t part of this tutorial I will just show the code below:
在src
文件夹中,我们将创建index.html
文件,在该文件中我们将开始实施TO-DO应用程序的功能。 但是首先,让我们填充index.html
文件。 由于创建TO-DO应用程序不是本教程的一部分,因此我将仅显示以下代码:
<html>
<head>
<title>Todo App</title>
</head>
<body>
<div class="container">
<p>
<label for="new-task">Add Item</label>
<input id="new-task" type="text">
<button id="addTask">Add</button>
</p>
<h3>Todo</h3>
<ul id="tasks">
</ul>
</div>
<script src="main.js"></script>
</body>
</html>
Let us now make it functional. We are going to break the two functions (Add and Delete) into their own files and then import them into index.js
. We will create two files in our src
folder named addTask.js
and deleteTask.js
. Your project structure should now look like this:
现在让我们使其起作用。 我们将把两个函数(添加和删除)分解为它们自己的文件,然后将它们导入到index.js
。 我们将在src
文件夹中创建两个文件addTask.js
和deleteTask.js
。 您的项目结构现在应如下所示:
We can now start adding the necessary logic, so let’s implement the deleteTask.js
first because it has no dependencies. Paste this in your deleteTask.js
file:
现在我们可以开始添加必要的逻辑,因此让我们首先实现deleteTask.js
因为它没有依赖性。 将此粘贴到您的deleteTask.js
文件中:
const deleteTask = function(e) {
console.log("Delete Task...", e);
//Remove the parent list item from the ul
var listItem = e.target.parentNode;
var ul = listItem.parentNode;
ul.removeChild(listItem);
};
export default deleteTask;
All that is going on in that file is we are creating the deleteTask
function and then exporting it as a default export.
该文件中所有要做的就是创建deleteTask
函数,然后将其导出为默认导出。
We can now implement the addTask
function. In the addTask.js
file add the following code:
现在,我们可以实现addTask
函数。 在addTask.js
文件中,添加以下代码:
import deleteTask from "./deleteTask";
const createNewTaskElement = function(taskString) {
const listItem = document.createElement("li");
const label = document.createElement("label");
const deleteButton = document.createElement("button");
deleteButton.innerText = "Delete";
deleteButton.className = "delete";
deleteButton.addEventListener("click", deleteTask);
label.innerText = taskString;
listItem.appendChild(label);
listItem.appendChild(deleteButton);
return listItem;
};
const addTask = function(e) {
const taskList = document.getElementById("tasks");
const task = document.getElementById("new-task");
if (task.value !== "") {
const newTaskItem = createNewTaskElement(task.value);
taskList.appendChild(newTaskItem);
task.value = "";
}
};
export default addTask;
In this one, we first of all import the deleteTask.js
file. By default, if no extension is specified in the import, webpack automatically assumes that it’s a .js
file. Then we have the function that creates the list item containing the task that was entered in the form. The only thing to note is that we are attaching the delete function to the click handler of the delete button. Then we create the actual addTask function and export it.
在这一部分中,我们首先导入deleteTask.js
文件。 默认情况下,如果在导入中未指定扩展名,则webpack会自动假定其为.js
文件。 然后,我们具有创建列表项的功能,该列表项包含在表单中输入的任务。 唯一需要注意的是,我们将删除功能附加到删除按钮的单击处理程序上。 然后,我们创建实际的addTask函数并将其导出。
We will then need to import our addTask
function into index.js
. Paste the code below into your index.js
file:
然后,我们需要将addTask
函数导入index.js
。 将以下代码粘贴到index.js
文件中:
import addTask from './addTask';
const addTaskButton = document.getElementById("addTask");
addTaskButton.addEventListener("click", addTask);
This is pretty straightforward: we are importing the addTask
function and attaching it to the click handler for the addTaskButton
. If you followed the steps above you should be good to go.
这非常简单:我们正在导入addTask
函数,并将其附加到addTaskButton
的单击处理程序。 如果您按照上述步骤操作,那应该很好。
Finally, to get our main.js
file we need to run Webpack through our codebase. For this step, make sure you have NodeJS installed on your system, then we will install webpack globally using this command:
最后,要获取我们的main.js
文件,我们需要通过代码库运行Webpack。 对于这一步,请确保您已的NodeJS您的系统上安装,然后我们将安装的WebPack全球使用这个命令:
npm install -g webpack OR sudo npm install -g webpack
Once it’s done installing run the following command:
安装完成后,运行以下命令:
webpack
It will bundle up our file successfully but we should see a warning in the terminal like this:
它将成功捆绑我们的文件,但是我们应该在终端中看到如下警告:
Webpack is just warning us that we didn’t specify a mode. We could leave it as is and run the code, everything should work fine. But if you don’t like the warning then you can run Webpack like this:
Webpack只是警告我们,我们没有指定模式 。 我们可以按原样保留它并运行代码,一切都应该正常工作。 但是,如果您不喜欢该警告,则可以这样运行Webpack:
webpack --mode=development
And you’re good to go.
而且你很好。
结语 (Wrapping up)
If you got lost along the way you can always use the GitHub repo for reference (It has some CSS styling in it, though).
如果您一路迷路,可以随时使用GitHub存储库作为参考(不过,它具有一些CSS样式)。
I hope this article was able to show you what Webpack has to offer (just the basics, with no configuration whatsoever). In subsequent articles, I will try to show how to set up various custom configurations for features like code splitting, lazy loading and configuring Webpack to work with multi-page applications.
我希望本文能够向您展示Webpack所提供的功能(仅仅是基础知识,没有任何配置)。 在随后的文章中,我将尝试展示如何为各种功能(如代码拆分 , 延迟加载和配置Webpack以与多页面应用程序一起使用)设置各种自定义配置。
In order to keep this article as basic as possible, I avoided the use of a package.json
file in the article. The use of a package.json
file and installing webpack locally is the most scalable way of using webpack and I will go into it in my next article on using Webpack.
为了使本文尽可能基本,我避免在本文中使用package.json
文件。 使用package.json
文件和在本地安装webpack是使用webpack的最可扩展的方法,我将在下一篇有关使用Webpack的文章中进行介绍。
To help navigate the coming articles, it will really help if you can drop a comment of what you would like to see explained or implemented regarding Webpack. ??
为了帮助浏览即将到来的文章,如果您对希望对Webpack进行解释或实现的内容发表评论,将会很有帮助。 ??
I will like to especially thank Sean T. Larkin, Israel Obiagba and Hassan Sani for their feedback in making the article better than initially planned. You all rock!
我要特别感谢Sean T. Larkin , 以色列Obiagba和Hassan Sani提出的反馈,以使本文比最初计划的要好。 你们都摇滚!
翻译自: https://www.freecodecamp.org/news/how-to-build-modern-applications-with-webpack-c81ccf6dd54f/
使用webpack构建项目