Foreword: I believe you have seen a lot of Web music APP, what imitate netease cloud, QQ music and so on. I take you to make a web music APP

The react family barrel is used in this project, which is relatively comprehensive in technology. A lot of things are not explained from the basics

The technical requirements

  1. React (Version 16.2.0, Basic skills)
  2. React-router (4.2.2, Basics)
  3. React-redux (5.0.6, Basics)
  4. Es6 (Master the Basics)
  5. Webpack (3.8.1, Master the basics)
  6. html5
  7. css3

Create a project

Use create-react-app to create the skeleton of the project, and then install the routing (routing changes due to the 4.x version). Install react-router-dom 4.2.2.

The structure of the created project is as follows

How do I use my own WebPack configuration?

In the React scaffolding, the basic configuration of webpack is already configured for us, so how to add our own custom configuration file? Here we use the rewire module. First, install the Rewire module

npm install rewire proxyquire --save-dev
Copy the code

Rewire only needs to be packaged for use at project development time and installed into development dependencies

Writing configuration files

Create the scripts folder in the root directory of your project, and create a customized-build.js file as follows:

/* This module runs the react-scripts script (Create React App) to customize the webpack configuration. Create the "overrides-config.dev.js" and "overrides-config.prod.js" files in the root directory of the project. A config-overrides file should export a single function that takes a config and modifies it as necessary. module.exports  = function(webpackConfig) { webpackConfig.module.rules[0].use[0].options.useEslintrc = true; }; */ var rewire = require('rewire'); var proxyquire = require('proxyquire'); switch(process.argv[2]) { // The "start" script is run during development mode case 'start': rewireModule('react-scripts/scripts/start.js', loadCustomizer('./overrides-config.dev')); break; // The "build" script is run to produce a production bundle case 'build': rewireModule('react-scripts/scripts/build.js', loadCustomizer('./overrides-config.prod')); break; // The "test" script runs all the tests with Jest case 'test': // Load customizations from the config-overrides.testing file. // That file should export a single function that takes a  config and returns a config let customizer = loadCustomizer('./overrides-config.testing'); proxyquire('react-scripts/scripts/test.js', { // When test.js asks for '.. /utils/createJestConfig' it will get this instead: '.. /utils/createJestConfig': (... args) => { // Use the existing createJestConfig function to create a config, then pass // it through the customizer var createJestConfig = require('react-scripts/utils/createJestConfig'); return customizer(createJestConfig(... args)); }}); break; default: console.log('customized-build only supports "start", "build", and "test" options.'); process.exit(-1); } // Attempt to load the given module and return null if it fails. function loadCustomizer(module) { try { return require(module); } catch(e) { if(e.code ! == "MODULE_NOT_FOUND") { throw e; } } // If the module doesn't exist, return a // noop that simply returns the config it's given. return config => config;  } function rewireModule(modulePath, customizer) { // Load the module with `rewire`, which allows modifying the // script's internal variables. let defaults = rewire(modulePath); // Reach into the module, grab its global 'config' variable, // and pass it through the customizer function. // The customizer should *mutate* the config object, because // react-scripts imports the config as a `const` and we can't // modify that reference. let config = defaults.__get__('config'); customizer(config); }Copy the code

The above code is used to get the corresponding module configuration during dev and build at runtime. NPM run start NPM run build webpack node_modules/react-scripts/config

webpack.config.dev.jsConfiguration files at development time

Webpack.config.prod. js produces the configuration file for packaging

function rewireModule(modulePath, customizer) {
  // Load the module with `rewire`, which allows modifying the
  // script's internal variables.
  let defaults = rewire(modulePath);

  // Reach into the module, grab its global 'config' variable,
  // and pass it through the customizer function.
  // The customizer should *mutate* the config object, because
  // react-scripts imports the config as a `const` and we can't
  // modify that reference.
  let config = defaults.__get__('config');
  customizer(config);
}
Copy the code

The config in the above code is the module.exports object in webpack.config.dev.js(development time) or webpack.config.prod.js(production packaging time). With this object we can add configuration to it

Configure the Stylus preprocessor language

Because Stylus is built for the Node environment, the syntax is almost indistinguishable from CSS. All the projects use stylus as the CSS preprocessor language. Github address for Stylus: github.com/stylus/styl…

First install the Stylus module

npm install stylus stylus-loader --save-dev
Copy the code

After the module is installed, create overrides-config.base.js file under the scripts folder, and create overrides-config.dev.js and overrides-config.prod.js files for development and production packaging

overrides-config.dev.js

module.exports = function(config) { // Use your ESLint /*let eslintLoader = config.module.rules[0]; eslintLoader.use[0].options.useEslintrc = true; */ // Add the stylus loader second-to-last // (last one must remain as the "file-loader") let loaderList = config.module.rules[1].oneOf; loaderList.splice(loaderList.length - 1, 0, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"] }); };Copy the code

overrides-config.prod.js

const paths = require('react-scripts/config/paths'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); // Webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. const publicPath = paths.servedPath; // Some apps do not use client-side routing with pushState. // For these, "homepage" can be set to "." to enable relative asset paths. const shouldUseRelativeAssetPaths = publicPath === './'; const cssFilename = 'static/css/[name].[contenthash:8].css'; const extractTextPluginOptions = shouldUseRelativeAssetPaths ? // Making sure that the publicPath goes back to to build folder. { publicPath: Array(cssFilename.split('/').length).join('.. / ')}, {}; module.exports = function(config) { // Use your ESLint /*let eslintLoader = config.module.rules[0]; eslintLoader.use[0].options.useEslintrc = true; */ // Add the stylus loader second-to-last // (last one must remain as the "file-loader") let loaderList = config.module.rules[1].oneOf; loaderList.splice(loaderList.length - 1, 0, { test: /\.styl$/, loader: ExtractTextPlugin.extract( Object.assign( { fallback: { loader: require.resolve('style-loader'), options: { hmr: false } }, use: [ { loader: require.resolve('css-loader'), options: { importLoaders: 1, minimize: true, sourceMap: true } }, { loader: require.resolve('stylus-loader') } ] } ), extractTextPluginOptions) }); };Copy the code

Modifying the Startup Script

Open the package.json file

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
Copy the code

Modify the start and build scripts as follows

"scripts": {
    "start": "node scripts/customized-build start",
    "build": "node scripts/customized-build build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
Copy the code

Test the WebPack configuration

Change the app.css in your project to app.styl, removing curly braces and semicolons

.App text-align: center .App-logo animation: App-logo-spin infinite 20s linear height: 80px .App-header background-color: #222 height: 150px padding: 20px color: white .App-title font-size: App-intro font size: large @keyframes app-logo-spin {from {transform: rotate(0deg); } to { transform: rotate(360deg); }}Copy the code

Run the NPM start command to run the custom configuration file

View the address **Check whether the content on http://localhost:3000** is the same as that on the first creation. If no changes are made, no problem is found

Join autoprefixer

Autoprefixer is a plug-in for CSS vendor prefixes. The React scaffolding already relies on the Autoprefixer plug-in, so NPM has installed Autoprefixer for us

For Stylus and Autoprefixer to use together here use a PostStylus plugin at github address: github.com/seaneking/p… . Install poststylus

npm install poststylus --save-dev
Copy the code

In the overrides-config.base.js configuration file, add the Poststylus plug-in configuration

const webpack = require('webpack');
const poststylus = require('poststylus');
const autoprefixer = require('autoprefixer');
Copy the code
module.exports.stylusLoaderOptionsPlugin = new webpack.LoaderOptionsPlugin({
    options: {
      stylus: {
        use: [
          poststylus([ 
            require('postcss-flexbugs-fixes'),
            autoprefixer({
              browsers: [
                '>1%',
                'last 4 versions',
                'Firefox ESR',
                'not ie < 9', // React doesn't support IE8 anyway
              ],
              flexbox: 'no-2009',
            })
          ])
        ]
      }
    }
  });
Copy the code

Then import overrides-config.base. Js in overrides-config.dev. Js and overrides-config.prod.js and add the following code to the model.exports function

// Use Poststylus Plugin to handle stylus
config.plugins.push(baseConfig.stylusLoaderOptionsPlugin);
Copy the code

When not using the Poststylus plug-in, we view the style of the Logo image

After using

Prefixes have been automatically added for us

Configure the root path alias for the project

Add the following code to the overrides-config.base.js file: / / add the following code to the overrides-config.base.js file: / / add the following code to the overrides-config.base

const path = require('path');
Copy the code
function resolve (dir) { return path.join(__dirname, '.. ', dir) } module.exports.rootPath = resolve('src');Copy the code

Finally, it was added to the model.exports function of overrides-config.dev.js and overrides-config.prod.js

// Define the root path alias
let alias = config.resolve.alias;
alias["@"] = baseConfig.rootPath;
Copy the code

Overrides -config.base.js Complete configuration is as follows

const path = require('path'); const webpack = require('webpack'); const poststylus = require('poststylus'); const autoprefixer = require('autoprefixer'); function resolve (dir) { return path.join(__dirname, '.. ', dir) } module.exports.rootPath = resolve('src'); module.exports.stylusLoaderOptionsPlugin = new webpack.LoaderOptionsPlugin({ options: { stylus: { use: [ poststylus([ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], flexbox: 'no-2009', }) ]) ] } } });Copy the code

The last

Full project address: github.com/dxx/mango-m…

The code for this chapter is in the chapter1 branch