Faced with the ever-changing front-end, I said I couldn't learn anymore: joy:. Webpack has been updated a long time ago, and React launched the hooks API some time ago. It just so happened that I was on vacation at home during the Spring Festival and I was free of time, so I should quickly make up for the React technology stack.
There are many articles on the Internet that introduce hooks knowledge points, but they are all fragmentary and can only write some small demos. There is no more systematic yet, and the brand new hooks are used to explain the actual project construction. So here we build the basic scaffolding of a single-page Web App project from the perspective of developing actual projects, and implement a react project template based on the hooks API.
The most attractive thing about Hooks is to use functional components instead of object-oriented class components. If react involves state, the solution usually only uses class components. Once the business logic is complex, it is easy to cause components to be bloated, and the module solution is also a problem. After using hooks-based function components, the code is not only more concise and more comfortable to write, but also much more convenient to reuse modules. I am very optimistic about its future.
Webpack 4 configuration
Instead of using create-react-app, we configure the development environment from scratch, because it will be more convenient to customize certain functions. The following is a general configuration file.
const { resolve } = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const { HotModuleReplacementPlugin } = require('webpack'); = { entry: './src/',//Single entrance output: { path: resolve(__dirname, 'dist'), filename: '[name].[hash].js'//Add hash from the output file }, optimization: { // Instead of commonchunk, code segmentation runtimeChunk: 'single', splitChunks: { cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all' } } } }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: ['babel-loader'] }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.scss$/, use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 1, modules: true,//css modules localIdentName: '[name]___[local]___[hash:base64:5]' }, }, 'postcss-loader', 'sass-loader'] }, { /* When the file size is less than limit, the url-loader converts the file into the format of the Data URI and inlines it to the referenced place When the file is greater than limit, the url-loader will call file-loader, store the file in the output directory, and rewrite the referenced file path into the output path */ test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/, use: [{ loader: 'url-loader', options: { limit: 1000 } }] } ] }, plugins: [ new CleanWebpackPlugin(['dist']),//When generating a new file, clear the generated directory new HtmlWebpackPlugin({ template: './public/',//Template path favicon: './public/', minify: { //compression removeAttributeQuotes:true, removeComments: true, collapseWhitespace: true, removeScriptTypeAttributes:true, removeStyleLinkTypeAttributes:true }, }), new HotModuleReplacementPlugin()//HMR ] };
Then, based on the file, the files of the development environment are configured, mainly to start the development server.
const merge = require('webpack-merge'); const common = require('./'); = merge(common, { mode: 'development', devtool: 'inline-source-map', devServer: { contentBase: './dist', port: 4001, hot: true } });
For a file that generates a mode, as long as mode:'production' is defined, webpack 4 will automatically compress the optimization code when packaged.
const merge = require('webpack-merge'); const common = require('./'); = merge(common, { mode: 'production', devtool: 'source-map' });
scripts in configuration
{ "scripts": { "start": "webpack-dev-server --open --config ", "build": "webpack --config " } }
Babel configuration
babelrc file, css module package is recommended herebabel-plugin-react-css-modules 。
react-css-modules supports both global css (default className property), and local css module (styleName property), as well as css precompiler, which is used here.
{ "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-runtime", [ "react-css-modules", { "exclude": "node_modules", "filetypes": { ".scss": { "syntax": "postcss-scss" } }, "generateScopedName": "[name]___[local]___[hash:base64:5]" } ] ] }
React Projects
Below is the basic directory tree structure of the project, and then start from the entrance to refine the entire project step by step.
├ ├ src │ ├ component // Component Directory│ ├ reducer // reducer directory│ ├ │ ├ │ ├ │ └ ├ public // Static file directory│ ├ css │ └ ├ .babelrc ├ ├ └
The state management component uses redux , react-router for building single-page projects, because the hooks API is used, react-redux connection state state is no longer needed.
< value={{ state, dispatch }}> basically replaces react-redux's ** `.
// import React, { useReducer } from 'react' import { render } from 'react-dom' import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom' import Context from './' import Home from './component/' import List from './component/' import rootReducer from './reducer' import '../public/css/' const Root = () => { const initState = { list: [ { id: 0, txt: 'webpack 4' }, { id: 1, txt: 'react' }, { id: 2, txt: 'redux' }, ] }; // useReducer maps state, dispatch const [state, dispatch] = useReducer(rootReducer, initState); return < value={{ state, dispatch }}> <Router> <Switch> <Route exact path="/" component={Home} /> <Route exact path="/list" component={List} /> <Route render={() => (<Redirect to="/" />)} /> </Switch> </Router> </> } render( <Root />, ('root') )
, and , is consistent with the previous writing style.
// export const ADD_COMMENT = 'ADD_COMMENT' export const REMOVE_COMMENT = 'REMOVE_COMMENT' // import { ADD_COMMENT, REMOVE_COMMENT } from './constants' export function addComment(comment) { return { type: ADD_COMMENT, comment } } export function removeComment(id) { return { type: REMOVE_COMMENT, id } }
import { ADD_COMMENT, REMOVE_COMMENT } from '../' const list = (state = [], payload) => { switch () { case ADD_COMMENT: if (()) { return [...state, ...]; } else { return [...state, ]; } case REMOVE_COMMENT: return (i => != ); default: return state; } }; export default list
import { combineReducers } from 'redux' import list from './' const rootReducer = combineReducers({ list, //user }); export default rootReducer
The biggest difference is the component component. Based on functional style, the internal expression is like a plug-and-play slot, which can easily extract common components and then reference them from the outside. Compared with the previous object-oriented approach, I think function expressions are more popular among front-end developers.
- useContext Get global state
- useRef replaces the previous ref
- useState replaces the previous state
- useEffect can replace the previous life cycle hook function
// Monitor the parameters in the array and execute once they changeuseEffect(() => { updateData(); },[id]); //If you do not pass the second parameter, it is equivalent to execution every time componentDidMount and componentDidUpdateuseEffect(() => { updateData(); }); //The second parameter passes an empty array, which is equivalent to only executing when componentDidMount and componentWillUnMount,//The return function in the first parameter is used to perform the cleaning functionuseEffect(() => { initData(); reutrn () => ('componentWillUnMount cleanup...'); }, []);
Finally, it is the components that implement the specific interface and business logic. The following is the List component.
// import React, { useRef, useState, useContext } from 'react' import { bindActionCreators } from 'redux' import { Link } from 'react-router-dom' import Context from '../' import * as actions from '../' import Dialog from './' import './' const List = () => { const ctx = useContext(Context);//Get global state state const { user, list } = ; const [visible, setVisible] = useState(false); const [rid, setRid] = useState(''); const inputRef = useRef(null); const { removeComment, addComment } = bindActionCreators(actions, ); const confirmHandle = () => { setVisible(false); removeComment(rid); } const cancelHandle = () => { setVisible(false); } const add = () => { const input = , val = (); if (!val) return; addComment({ id: (() * 1000000), txt: val }); = ''; } return <> <div styleName="form"> <h3 styleName="sub-title">This is list page</h3> <div> <p>hello, {} !</p> <p>your email is {} !</p> <p styleName="tip">please add and remove the list item !!</p> </div> <ul> { (l => <li key={}>{}<i className="icon-minus" title="remove item" onClick={() => { setVisible(true); setRid(); }}></i></li>) } </ul> <input ref={inputRef} type="text" /> <button onClick={add} title="add item">Add Item</button> <Link styleName="link" to="/">redirect to home</Link> </div> <Dialog visible={visible} confirm={confirmHandle} cancel={cancelHandle}>remove this item ?</Dialog> </> } export default List;
Project Code
/edwardzhong/webpack_react
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.