This article is intended to help those who have been using scaffolding without the concept of engineering friends.
Many of the steps in this article report errors at run time and analyze the configuration that needs to be added from the errors to gain an impression and understanding of the configuration of each line that is added.
React This article will take you on a walk.
Create a directory
mkdir demo && cd demo
npm init
touch index.js
Copy the code
webpack
Install webPack dependencies
yarn add webpack webpack-cli --dev Install webPack dependencies
touch webpack.config.js Create a webapck configuration file
Copy the code
Modify the configuration
Here is a basic WebPack configuration:
const path = require('path');
module.exports = {
entry: "./index.js".output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'}};Copy the code
Adjust the package. The json
In package.json, add scipts as follows:
"scripts": {
"build": "webpack"
},
Copy the code
Try a build
Execute on the command line
npm run build
Copy the code
You’ll see the following warning:
WARNING in configuration The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment. You can also set it to ‘none’ to disable any default behavior. Learn more: Webpack.js.org/concepts/mo…
Since the Mode configuration item was added to WebPack after 4.0, it sets production for us by default. However, this warning does not affect build results for now. You can see that the dist directory has generated a file named my-first-webpack.bundle.js.
Now that the basic configuration is complete, React is introduced.
React
Install react dependencies
yarn add react react-dom Install react-related dependencies
Copy the code
use
Before using it, add index.html to dist directory as follows:
<html>
<head>
<meta charset="UTF-8">
<title>React</title>
</head>
<body>
<div id="root"></div>
<script src="./my-first-webpack.bundle.js"></script>
</body>
</html>
Copy the code
React HelloWorld HelloWorld HelloWorld HelloWorld HelloWorld Never mind, code you always know it, stick over!
Change the contents of index.js to the following:
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello, world!</h1>.document.getElementById('root'));Copy the code
OK, it looks perfect, execute “build” and see the result.
ERROR in ./index.js 4:16
Module parse failed: Unexpected token (4:16)
You may need an appropriate loader to handle this file type.
| import ReactDOM from 'react-dom';
|
> ReactDOM.render(<div>Hello World</div>,
| document.getElementById('root'));
Copy the code
An error? You may need an appropriate loader to handle this file type. The programmer is great, and the error message is very detailed, telling us that we need the appropriate loader to process this file. Loader? Don’t ask me, I didn’t do it. Scroll down to the next section of the react-Jsx introduction and scroll down to the bottom. Is there an official suggestion? Keep looking for keywords. Do you see something called Babel? Nani, could JSX have something to do with it? Although this paragraph is recommended for editor Settings, programmers should be curious.
Babel
Google Babel and take a look inside. So I came across a new term, Babel. What does it do? What does it bring us? Check out the front page.
-
ES2015 and later
Babel supports the latest versions of JavaScript through syntax converters.
-
Polyfill
Since Babel only converts syntax (such as arrow functions), you can use Babel-Polyfill to support new global variables, such as promises, new native methods.
-
JSX and Flow
Babel can convert JSX syntax and remove type comments.
-
pluggable
Babel is built outside of plug-ins. You can use existing plug-ins or write your own to compose your own transformation pipeline.
-
adjustable
Source Map support makes it easy to debug compiled code.
After reading the introduction of the home page, whether and I have the same exclamation: good thing ah! If it’s a good thing, use it. From the webpack configuration option, you can see that the two keywords that appeared just now come from Babel, loader, and Emma.
yarn add babel-loader babel-core babel-preset-env babel-polyfill babel-preset-react --dev
Is this wave installation longer? Because we just saw es2015, higher syntax, Polyfill, JSX loaded
Copy the code
throughconfig
configuration
Modify the webpack.config.js configuration as follows:
const path = require('path');
module.exports = {
entry: "./index.js".output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader"}}};Copy the code
create.babelrc
The configuration file
touch .babelrc # to create babelrc
Copy the code
Paste the following into. Babelrc:
{
"presets": ["env"."react"]}Copy the code
Babel-env and babel-preset-react are used, and polyfill is used for preset. Go ahead and read the wave file. Let’s use polyfill and modify the configuration in webpack.config.js as follows:
const path = require('path');
module.exports = {
entry: ["babel-polyfill"."./index.js"].output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader"}}};Copy the code
Run the NPM run build command to see what happens. Sure enough, it’s compiled.
You think the project is just finished? NO, this is just the beginning!
react-router
What is SPA, single page application? What is a single page app? With only one HTML, common JS CSS is referenced only once, rendering the application through a partial refresh.
Follow the steps to get started quickly.
The installation
yarn add react-router-dom
Copy the code
use
Now you can copy/paste any of the examples into src/App.js. Here’s the basic one:
Here’s how to use create-react-app. We didn’t use create-react-app, but we built it ourselves. Create your own SRC and app.js.
mkdir src && touch src/App.js
Copy the code
Upload the official 🌰 to app.js:
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const Home = (a)= > (
<div>
<h2>Home</h2>
</div>
)
const About = (a)= > (
<div>
<h2>About</h2>
</div>
)
const Topic = ({ match }) = > (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
const Topics = ({ match }) = >( <div> <h2>Topics</h2> <ul> <li> <Link to={`${match.url}/rendering`}> Rendering with React </Link> </li> <li> <Link to={`${match.url}/components`}> Components </Link> </li> <li> <Link to={`${match.url}/props-v-state`}> Props v. State </Link> </li> </ul> <Route path={`${match.path}/:topicId`} component={Topic}/> <Route exact path={match.path} render={() => ( <h3>Please select a topic.</h3> )}/> </div> ) const BasicExample = () => ( <Router> <div> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/topics">Topics</Link></li> </ul> <hr/> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> <Route path="/topics" component={Topics}/> </div> </Router> ) export default BasicExampleCopy the code
Next reference in index.js:
import React from 'react'
import ReactDOM from 'react-dom';
import App from './src/App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
Copy the code
Run NPM run build and see. No error. Open dist/index.html in your browser. Click on the link and nothing happens? What’s going on? Don’t panic, open the console and see:
Uncaught DOMException: Failed to execute ‘pushState’ on ‘History’: A history state object with URL ‘file:///’ cannot be created in a document with origin ‘null’ and URL
What about emmmm? Don’t ask me, I didn’t do it… Since Chrome doesn’t work, take a look at Safari. If it doesn’t work, take a look at the error message.
[Error] SecurityError: Blocked attempt to use history.pushState() to change session history URL from file:///demo/dist/index.html to file:///about. Paths and fragments must match for a sandboxed document.
It seems that Safari’s error is a little more friendly, and we can clearly see that it’s trying to change the link. Do you think it’s reasonable to do that for security reasons? Won’t you be able to access any file on your computer with an HTML? Emmm, sure enough, doesn’t make sense. Paths and fragments must match for a sandboxed document.
So let’s build a sandbox environment.
webpack-dev-server
Configuration Reference Link
The installation
yarn add webpack-dev-server --dev
Copy the code
Modify the configuration
-
Add scripts to package.json:
"dev": "webpack-dev-server" Copy the code
-
In the webpack.config.js root node, add:
devServer: { contentBase: path.join(__dirname, 'dist'), compress: true.port: 9000 } Copy the code
-
Execute srcripts just added above:
npm run dev Copy the code
-
Open the link localhost:9000 in your browser
The function is fine, the style is unbearable, right? To change!
-
Create the app. CSS file in SRC and add the following content:
.container { list-style: none; } Copy the code
-
In app.js, add the following code:
import './app.css'; Copy the code
-
Apply styles to ul from BasicExample:
<ul className="container"> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/topics">Topics</Link></li> </ul> Copy the code
At this point, you’ll find a familiar mistake.
You may need an appropriate loader to handle this file type.
This time we can quickly locate the lack of csS-related loaders.
Again withloader
meet
Although the webpack documentation is always so late, but some basic things, still can learn from it. Now that we have encountered loader once again, let’s get to the bottom of what loader is and what it provides for us.
Loader is used to convert the module source code. Loader allows you to preprocess files when importing or “loading” modules. As such, Loader is similar to “tasks” in other build tools and provides a powerful way to handle the front-end build steps. Loader can convert files from different languages (such as TypeScript) to JavaScript, or convert inline images to data urls. Loader even allows you to import CSS files directly into JavaScript modules!
If you need to import files, you need to use the loader. In that case, one at a time.
css-loader
The installation
yarn add style-loader css-loader --dev
Copy the code
configuration
Modify the rules under module in webpack.config.js, just like adding babel-loader, add the following configuration:
{
test: /\.css$/,
use: [
'style-loader'.'css-loader']}Copy the code
One more word about CSS-loader: when you don’t want to pollute global CSS, you want to use it in the following ways:
import styles from 'app.css';
<div className={styles.container} />
Copy the code
Please use CSS Modules, webpack configuration CSS module is very simple:
'css-loader? modules&localIdentName=[name]-[hash:base64:5]'.Copy the code
Adjust csS-Loader to the above content.
file-loader
Handle all kinds of ICONS and pictures
The installation
yarn add file-loader --dev
Copy the code
configuration
Modify rules under module in webpack.config.js and add the following configuration:
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader']}Copy the code
Font files are still handled by file-loader.
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader']}Copy the code
Loader is temporarily added here, remember at this time? In order for WebPack to process the corresponding files, there must be a corresponding loader.
Continue to refresh to see our demo project, it works.
I met the Plugin
Loader configuration finished, continue to press the document to see what we can learn.
Plug-ins are the backbone of WebPack. Webpack itself is also built on top of the same plugin system you use in your WebPack configuration!
Plug-ins are designed to solve other things that loader cannot implement.
html-webpack-plugin
The admin output has this paragraph:
What happens if we change the name of one of our entry points, or even add a new name? The generated package will be renamed in a build, but our index.html file will still reference the old name. We use HtmlWebpackPlugin to solve this problem.
OK, understand its purpose, useful, pack!
The installation
yarn add html-webpack-plugin --dev
Copy the code
configuration
Add plugins to the root node of webpack.config.js:
/ / into HTML - webpack - the plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
Copy the code
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management'})].Copy the code
Restart NPM run dev and you will notice that the page is blank. Oh, my God, isn’t this a pit? Open the console and take a look at Target Container is not a DOM element. Take a look at the Elements TAB and say, gee. It seems that our
has magically disappeared. Looking at the document again, I found this sentence:
If you want to learn more about all the features and options that the HtmlWebpackPlugin provides, you should familiarize yourself with the HtmlWebpackPlugin repository. Webpack require path to the template. Please see the docs for details
For templates, let’s remove the dist/index.html file, put it in the root directory of the project, and then modify the configuration in webpack.config.js:
new HtmlWebpackPlugin({
title: 'Demo'.template: './index.html'
})
Copy the code
Let’s restart the service, NPM run dev
Only one instance of babel-polyfill is allowed. What’s wrong with that? We clearly only have one entry, why cited many times? Delete it and restart it. And you’re done.
clean-webpack-plugin
You may have noticed that our /dist folder is quite cluttered due to the legacy of past guides and code examples. Webpack generates files and places them in the /dist folder, but WebPack has no way of keeping track of which files are actually used in the project.
Still useful, still loaded!
The installation
yarn add clean-webpack-plugin --dev
Copy the code
configuration
Modify webpack.config.js to introduce CleanWebpackPlugin:
const CleanWebpackPlugin = require('clean-webpack-plugin');
Copy the code
Add the plugins:
new CleanWebpackPlugin(['dist']),
Copy the code
Distinguish between production and development environments
Why distinguish between production and development environments?
The build goals for development and production environments are very different. In a development environment, we need a powerful Source Map and Localhost Server with live reloading or hot Module replacement capabilities. In the production environment, we shifted our focus to smaller bundles, lighter source maps, and more optimized resources to improve load times. To follow logical separation, we generally recommend writing separate WebPack configurations for each environment.
Split the WebPack configuration
Follow the official tutorial to split it. In this case, I recommend creating a config directory and placing the configuration in config. Therefore, our configuration file should be as follows:
config/webpack.common.js
:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: ["babel-polyfill"."./index.js"].output: {
path: path.resolve(__dirname, '.. /dist'),
filename: '[name].[contenthash:12].js'
},
module: {
rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader? cacheDirectory"
},
{
test: /\.css$/.use: [
'style-loader'."css-loader"] {},test: /\.(png|svg|jpg|gif)$/.use: [
'file-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/.use: [
'file-loader']]}},plugins: [
new CleanWebpackPlugin(["dist"] and {root: path.resolve(__dirname, ".. /"),}),new HtmlWebpackPlugin({
title: 'Demo'.template: './index.html'}})];Copy the code
config/webpack.dev.js
:
const merge = require('webpack-merge');
const common = require('./webpack.common');
const path = require('path');
module.exports = merge(common, {
mode: 'development'.devtool: 'inline-source-map'.devServer: {
contentBase: path.resolve(__dirname, '.. /dist'),
compress: true.port: 9000}});Copy the code
The config/webpack. Prod. Js:
const merge = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: 'production'});Copy the code
Also, scripts in package.json should be adjusted accordingly:
"build": "webpack --config config/webpack.prod.js",
"dev": "webpack-dev-server --config config/webpack.dev.js"
Copy the code
About separating CSS
Extract -text-webpack-plugin【4.0 deprecated 】
Tips: This is a Plugin from start to quit. If you are interested, you can continue with the Plugin. If not, please skip to the next section.
It moves all the *.css references in entry chunks to separate CSS files.
The installation
yarn add extract-text-webpack-plugin --dev
Copy the code
configuration
Paste in the configuration as shown in 🌰 and change it to webpack.config.js:
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
mode: "production".entry: ["babel-polyfill"."./index.js"].output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader? cacheDirectory"
},
{
test: /\.css$/.use: ExtractTextPlugin.extract({
fallback: "style-loader".use: "css-loader"})}, {test: /\.(png|svg|jpg|gif)$/.use: [
'file-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/.use: [
'file-loader']]}},plugins: [
new ExtractTextPlugin("styles.css")],devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true.port: 9000}};Copy the code
When you restart the service, you will notice an error? WTF? After all this work, it still doesn’t work? At this point we need to note that it must have worked at one time or we can’t put it on the document, which shows that the WebPack document is behind. In this case, since we are currently using webpack 4.x version, at this time, first go to the Github of ExtractTextWebpackPlugin to search if you have thought of issue, the keyword webpack 4. See an issue.
@vasivas don’t use extract-text-webpack-plugin for extract CSS, please use github.com/webpack-con…
If there is such an operation, take a look at the Mini-CSs-extract-plugin.
About Webpack, here is the guide, this article is not to explain webpack, more about the part of Webpack, you can see: @flower pants written article:
Using webpack4 with proper posture (part 1)
Using webpack4 with proper posture
We ended up with the following separation of CSS: webpack.common.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: ["babel-polyfill"."./index.js"].output: {
path: path.resolve(__dirname, '.. /dist'),
filename: '[name].[contenthash:12].js'
},
module: {
rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader? cacheDirectory"
},
{
test: /\.css$/.use: [
MiniCssExtractPlugin.loader,
'css-loader? modules&localIdentName=[name]-[hash:base64:5]',]}, {test: /\.(png|svg|jpg|gif)$/.use: [
'file-loader'] {},test: /\.(woff|woff2|eot|ttf|otf)$/.use: [
'file-loader']]}},plugins: [
new CleanWebpackPlugin(["dist"] and {root: path.resolve(__dirname, ".. /"),}),new HtmlWebpackPlugin({
title: 'Demo'.template: './index.html'})]};Copy the code
Webpack. Prod. Js:
const merge = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = merge(common, {
mode: 'production'.optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true.parallel: true.sourceMap: true
}),
new OptimizeCSSAssetsPlugin({}) // use OptimizeCSSAssetsPlugin]},plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash:12].css'.chunkFilename: '[name].[contenthash:12].css' // use contenthash *]}}));Copy the code
Webpack. Dev. Js:
const merge = require('webpack-merge');
const common = require('./webpack.common');
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = merge(common, {
mode: 'development'.devtool: 'inline-source-map'.devServer: {
contentBase: path.resolve(__dirname, '.. /dist'),
compress: true.port: 9000
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css'.chunkFilename: '[id].css',})});Copy the code
The separation
Still see above @ flower underpants article, cent of very delicate. Here we simply separate:
Change it in webpack.common.js:
const merge = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = merge(common, {
mode: 'production'.optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true.parallel: true.sourceMap: true
}),
new OptimizeCSSAssetsPlugin({}) // use OptimizeCSSAssetsPlugin].runtimeChunk: {
name: "manifest"
},
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/.name: "vendors".priority: - 20.chunks: "all"}}}},plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash:12].css'.chunkFilename: '[name].[contenthash:12].css' // use contenthash *]}}));Copy the code
OK, it seems perfect.
React, Babel, and WebPack are all in place, so it’s time for the next tool to standardize your code for collaboration with your team.
Eslint
website
The installation
npm install eslint -g Install ESLint globally
Copy the code
Initialize the
eslint --init
Copy the code
Choose the Default Settings
? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Airbnb (https://github.com/airbnb/javascript)
? Do you use React? Yes
? What format do you want your config file to be in? JavaScript
Checking peerDependencies of eslint-config-airbnb@latest
? The style guide "airbnb"Requires eslint@^4.19.1. You are currently using [email protected]. Do You want to downgrade? YesCopy the code
Choose Airbnb directly here.
Adjust the configuration
After the initial installation, we found that our previous app.js had an error, and we needed to adjust the esLint-related configuration rules to make it more suitable for our intended use: open the.eslintrc.js file and adjust it as follows:
module.exports = {
"extends": "airbnb"."plugins": ["react"."jsx-a11y"."import"]."rules": {
"import/no-extraneous-dependencies": "off"."react/jsx-filename-extension": [1, { "extensions": [".js".".jsx"]}],"react/prop-types": 0}};Copy the code
There are more ways to use ESLint that you need to explore personally.
The introduction ofant-design
website
The installation
yarn add antd
Copy the code
According to the need to load
If you don’t want to refer to CSS every time, you can use babel-plugin-import here:
yarn add babel-plugin-import --dev
Copy the code
Babelrc file modified to:
{
"presets": ["env"."react"]."plugins": [["import", { "libraryName": "antd"."libraryDirectory": "es"."style": "css"}}]]Copy the code
The trial
In app.js, make a reference:
import { DatePicker } from 'antd';
const Home = (a)= > (
<div>
<h2>
<DatePicker />
</h2>
</div>
);
Copy the code
After refreshing, we find that the components are displayed, but the styles are not in effect. Continue to find the solution: www.jianshu.com/p/603a61471… The elder brother wrote clearly, it turns out that our rules configuration still has some defects, according to the content adjustment is as follows:
{
test: /\.css$/.exclude: /node_modules/.use: [
MiniCssExtractPlugin.loader,
'css-loader? modules&localIdentName=[name]-[hash:base64:5]',]}, {// antD style processing
test: /\.css$/.exclude: /src/.use: [{loader: 'style-loader' },
{
loader: 'css-loader'.options: {
importLoaders: 1,},},],},Copy the code
After the adjustment, restart webPack.
The last
This article is only the introduction, so that you have a certain understanding of front-end engineering, more need to explore according to the keywords themselves.
I made last week based on Baidu map packaging is now open source, welcome to join together to do things:
github
website