- How does the development pattern work?
- By Dan Abramov
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: Jerry – FD
- Proofreader: TokenJan, Hanxiaosss
How does the development pattern work?
If your JavaScript codebase is already a bit complex, you may need a solution to package and run different code for online and development environments.
It is useful to distinguish between packaging and running different code for development and online environments. In development mode React includes a number of alerts to help you detect problems without causing bugs online. However, the code necessary to help find problems often leads to larger code packages and slower application performance.
This deceleration is acceptable in a development environment. In fact, running code slower in a development environment can be even more helpful, as it can partially bridge the gap between a high-performance development machine and an average-speed user device.
We don’t want any performance loss in the online environment. Therefore, our online environment removes these validations. So how does it work? Let’s come to Kangkang.
The key to running different code in a development environment is your JavaScript build tool (no matter which one you’re using). In Facebook it looks like this:
if (__DEV__) {
doSomethingDev();
} else {
doSomethingProd();
}
Copy the code
Here, __DEV__ is not really a variable. When the browser has finished loading dependencies between modules, they are replaced with constants. The result is this:
// In the development environment:
if (true) {
doSomethingDev(); / / 👈
} else {
doSomethingProd();
}
// Online environment:
if (false) {
doSomethingDev();
} else {
doSomethingProd(); / / 👈
}
Copy the code
In an online environment, you may have compression tools (e.g., Terser) enabled in your code. Most JavaScript compression tools place restrictions on invalid code, such as removing logical branches of if (false). So in an online environment, you might just see:
// Online environment (compressed) :
doSomethingProd();
Copy the code
(Note that there are some important specifications for the current mainstream JavaScript tools that can guide how to effectively remove invalid code, but that’s a topic for another day.)
Maybe you’re not using the magic variable __DEV__, but if you’re using a popular JavaScript packaging tool like Webpack, there are some conventions you need to follow. For example, a very common expression like this:
if(process.env.NODE_ENV ! = ='production') {
doSomethingDev();
} else {
doSomethingProd();
}
Copy the code
Some frameworks like React and Vue use this form. When you use NPM to package and load them. (A single
This particular convention originally comes from Node.js. In Node.js, there is a global process variable that represents your current system environment variable, which is a property of the Process. env object. However, if you see this syntax in the front-end code base, there is no real process variable. 🤯
Instead, the entire process.env.node_env expression is replaced with a literal string when packaged, just like the magic __DEV__ variable:
// In the development environment:
if ('development'! = ='production') { // true
doSomethingDev(); / / 👈
} else {
doSomethingProd();
}
// In the online environment:
if ('production'! = ='production') { // false
doSomethingDev();
} else {
doSomethingProd(); / / 👈
}
Copy the code
Because the entire expression is a constant (‘production’! == ‘production’ is always false) packaging-compression tools can also use this to remove other logical branch code.
// Online environment (packaged and compressed) :
doSomethingProd();
Copy the code
That’s the end of the prank
Note that this feature will not work with more complex expressions:
let mode = 'production';
if(mode ! = ='production') {
// 🔴 is not guaranteed to be removed
}
Copy the code
JavaScript static analysis tools are not particularly smart because of the dynamic nature of the language. When they find a variable like mode, instead of something like false or ‘production’! Static expressions such as == ‘production’ will most likely fail.
Similarly, if you use the top-level import declaration in JavaScript, the logic to automatically remove useless code will not work because it does not cross module boundaries.
// 🔴 is not guaranteed to be removed
import {someFunc} from 'some-module';
if (false) {
someFunc();
}
Copy the code
So your code needs to be written very strictly to ensure that the condition is absolutely static and that any code you want to remove is contained within the condition.
To make sure everything works as planned, your packaging tool needs to replace process.env.node_env, and it needs to know in which mode you want to build the project.
A few years ago, forgetting to configure environment variables was common. You will often find projects in development mode deployed online.
That’s bad, because it makes the site load slowly.
The situation has improved significantly in the past two years. For example, Webpack adds a simple mode option instead of manually changing process.env.node_env. React DevTools now also displays a red icon for sites in development mode to make it easy to spot.
Some installation tools that do pre-setup for you like Create React App, Next/Nuxt, Vue CLI, Gatsby, etc., split development and online build into two separate commands to make mistakes less likely. (For example, NPM start and NPM run build.) This means that only online build code can be deployed, so developers will never make this mistake again.
One point that has been discussed is making online mode the default and development mode optional. Personally, I don’t think it’s very good. Most of the people who benefit from warnings about development patterns are developers who are new to the framework. They don’t realize they have to turn on the development mode switch, and they miss a lot of bugs that should have been warned.
Yes, the performance issues are bad, but so is the buggy user experience. For example, React Key alerts help prevent bugs like sending the wrong message or buying the wrong product. Disabling this warning in development is risky for both you and your users. Because if it’s off by default, and then you find the switch and turn it on, you’ll find too many warnings to clean up. So most people turn it off again. So that’s why it needs to be turned on at the beginning, not later.
Finally, even if these alerts are optional in development and developers know they need to be turned on early in development, we still have to go back to the original problem. There will still be developers who accidentally deploy them to an online environment!
So let’s go back to this point.
Personally, I firmly believe that the right mode of tool presentation and use depends on whether you are debugging or deploying. Almost every other environment (be it mobile, desktop, or server) other than page browsers has had ways of differentiating and loading different development and online environments for decades.
Rather than relying on frameworks alone or relying on AD hoc conventions, perhaps it is time for JavaScript environments to treat this distinction as an important requirement.
Enough with the big idea!
Let’s take a look at the code again:
if(process.env.NODE_ENV ! = ='production') {
doSomethingDev();
} else {
doSomethingProd();
}
Copy the code
You may be wondering: If the Process object doesn’t exist in the front-end code, why do frameworks like React and Vue rely on it in NPM packages?
(Again: Yes<script>
Tags can be loaded into the browser using the methods provided by React and Vue, which do not rely on Process. Instead, you have to manually select it in development mode.js
It’s in an online environment.min.js
File. The following sections are only about using the packaging tool to remove React or Vue from NPMimport
Come in and use them.
Like many problems in programming, this particular convention is mostly historical. The reason we still use it is because it has now been adopted and adapted by many other tools. Switching to something else would be costly and not particularly worthwhile.
So what are the historical reasons behind this?
Many years before the syntax for import and export was standardized, there were many ways to express relationships between modules. Such as the popular Node.js require() and Module.exports, also known as CommonJS.
The code registered for distribution on NPM was (or is it still?) mostly written for Node.js in the early days of Express. The most popular server-side Node.js framework, which uses the NODE_ENV environment variable to validate online schemas. Some other NPM packages use the same convention.
Early JavaScript packaging tools like Browserify wanted to use NPM code for front-end engineering. (Yes, almost no one was using NPM in the front end at that time! Can you imagine? So they stretched the conventions of the Node.js ecosystem at the time and applied them to the front-end code.
The first envify changes were made in 2013. React was open source around that time, and NPM and Browserify seemed to be the best solution for packaging front-end CommonJS code at the time.
React has been available in NPM versions (as well as
React requires the online environment to remove code that should only be in development mode. As it happens, Browserify already provides a solution to this problem, so the React version for NPM also accepts the convention to use process.env.node_env. Over time, some other tools and frameworks, including Webpack and Vue, The same measures were taken.
By 2019, Browserify had lost much of its market share. However, the convention to replace process.env.node_env with ‘development’ or ‘production’ during the build phase remains as popular as ever.
(It’s also interesting to see how the ES module evolved as a model for distributing references online, rather than just being used in development, and how it slowly changed the scales. Let me know on Twitter)
Another thing you might find confusing is that in the React source code on GitHub, you’ll see __DEV__ used as a magic variable. The React code on NPM uses process.env.node_env instead. How is this done?
Historically, we used __DEV__ in our source code to match the Facebook source code. For a long time React was copied directly into Facebook’s code repository, so it had to follow the same rules. For NPM code, we have a build phase that checks and uses process.env.node_env! == ‘production’ to replace __DEV__ literally.
This can sometimes be a problem. Sometimes code that follows node.js conventions will work fine on NPM but break Facebook, and vice versa.
Since React 16, we’ve changed that approach. Instead, we now compile a package for each environment (including
This means that when the React source appears with if (__DEV__), we actually produce two code blocks per package. One is precompiled to __DEV__ = true and the other to __DEV__ = false. The entry to each NPM package “determines” which one to export.
Such as:
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
Copy the code
This is the only place where your packaging tool replaces ‘development’ or ‘production’ with strings. This is the only place where your compression tool removes code that should only be required in the development environment.
React.production.min. js and react.development.js no longer have any process.env.node_env checks. This makes sense because access to process.env can be slow when the code is actually running in Node.js. Compiling the packages in both modes ahead of time also helps us optimize file sizes to be more consistent, regardless of which packaging-compression tool you use.
That’s how it works!
I wish there was a better way than relying on conventions, but here we are. If patterns are an important concept in all JavaScript environments, it would be great if there were some way to expose code running in a development environment at the browser level where it shouldn’t be.
On the other hand, conventions in a single project can spread throughout the ecosystem, which is amazing. EXPRESS_ENV became NODE_ENV in 2010 and spread to the front end in 2013. The solution may not be perfect, but the cost of accepting it for each project is far less than the cost of persuading everyone else to make some changes. This taught us a valuable lesson about top-down versus bottom-up program acceptance. To understand how it can step by step transform the criteria of success compared to the criteria of failure.
Separating development from online patterns is a very useful technique. I recommend that you use this technique in your libraries and applications to do some of the checksums that are heavy online, but useful (and often rigorous) in a development environment.
As with any powerful feature, there are situations where you might abuse it. That’s the topic of my next article!
If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.