Getting started with React and Webpack

Getting started with React and Webpack

I've been learning React.js recently. Before, I used to write React code in the most native way, but I found it very troublesome to organize it. I heard that it is easy to organize React components with Webpack, so I spent some time learning it and gained a lot.

Let’s talk about React

A component has its own structure, logic, and style. It depends on some resources and other components. For example, when writing a component, the common way is:

- Define the structure through the front-end template engine

- Write your own logic in the JS file

- Writing component styles in CSS

- Solve the interdependencies between modules through libraries such as RequireJS and SeaJS,

So what does this look like in React?

Structure and logic

In the React world, the structure and logic are organized by JSX files. React embeds the template into the logic, implementing a JSX that is a mixture of JS code and HTML.

structure

In a JSX file, you can define components directly through React.createClass:

  1. var CustomComponent = React.creatClass({
  2. render: function(){
  3. return ();
  4. }
  5. });

In this way, we can easily define a component. The structure of the component is defined in the render function, but this is not a simple template engine. We can use js to conveniently and intuitively manipulate the component structure. For example, I want to add a few nodes to the component:

In this way, React allows components to have a flexible structure. So how does React handle logic?

logic

Anyone who has written a front-end component knows that a component usually needs to respond to its own DOM events and do some processing. If necessary, it also needs to expose some external interfaces. So how do React components achieve these two points?

Incident Response

For example, if I have a button component, and I need to do some processing logic after clicking it, then the React component will look like this:

  1. var ButtonComponent = React.createClass({
  2. render: function(){
  3. return (Dragon Slaying Sword, click to get it);
  4. }
  5. });

Clicking a button should trigger the corresponding logic. A more intuitive way is to bind an onclick event to the button, which contains the logic to be executed:

  1. function getDragonKillingSword() {
  2. //Send the sword  
  3. }
  4. var ButtonComponent = React.createClass({
  5. render: function(){
  6. return (Dragon Slaying Sword, click to get it);
  7. }
  8. });

This implements the response to internal events, but what if you need to expose the interface?

Exposing interfaces

In fact, now getDragonKilling

  1. < br >  

Sword is already an interface. What if there is a parent component that wants to call this interface?

The parent component looks like this:

  1. var ImDaddyComponent = React.createClass({
  2. render: function (){
  3. return (
  4. //Other components  
  5. //Other components  
  6. );
  7. }
  8. });

If you want to call the component method manually, first set a ref="" attribute on ButtonComponent to mark it. For example, here we set the subcomponent to , then in the logic of the parent component, you can call the interface method in this way in the parent component's own method:

  1. this .refs.getSwordButton.getDragonKillingSword();

It looks cool~ But here comes the question: the parent component wants to be able to call the method when the button is clicked, so what should it do?

Configuration parameters

The parent component can directly pass the function to be executed to the child component:

Then call the parent component method in the child component:

  1. var ButtonComponent = React.createClass({
  2. render: function (){
  3. return (Dragon Slaying Sword, click to get it);
  4. }
  5. });

Child components can obtain any parameters passed in when the parent component creates the child component through this.props, so this.props is often used as a configuration parameter.

Each person can only receive one Dragon Slaying Sword. The button should be grayed out once it is clicked. A state of whether it has been clicked should be added to the subcomponent. How should this be handled?

Component Status

In React, each component has its own state, which can be obtained through this.state in its own method, and the initial state is defined by the getInitialState() method. For example, the initial state of the Dragon Sword Button component should be unclicked, so the getInitialState method should define the initial state clicked: false. In the click execution method, the state value should be modified to click: true:

  1. var ButtonComponent = React.createClass({
  2. getInitialState: function (){
  3. //Determine the initial state  
  4. return {
  5. clicked: false  
  6. };
  7. },
  8. getDragonKillingSword: function (){
  9. //Send the sword  
  10. //Modify click status  
  11. this .setState({
  12. clicked: true  
  13. });
  14. },
  15. render: function (){
  16. return (Dragon Slaying Sword, click to get it);
  17. }
  18. });

In this way, the maintenance of the click state is completed, so the style of the node should also be maintained according to the state in the render function. For example, if the button is set to disabled here, the render function should add the corresponding judgment logic:

  1. render: function (){
  2. var clicked = this .state.clicked;
  3. if (clicked)
  4. return (Dragon Slaying Sword, click to get it);
  5. else   
  6. return (Dragon Slaying Sword, click to get it);
  7. }

Sections

Here is a brief introduction to managing the structure and logic of components through JSX. In fact, React also defines many methods for components, as well as the life cycle of the components themselves, which makes the logical processing of components more powerful.

#p#

Resource loading

CSS files define the styles of components. Current module loaders are usually able to load CSS files, and if they cannot, corresponding plug-ins are generally provided. In fact, CSS and images can be regarded as a kind of resource, because they generally do not need to be processed after loading.

React does not do anything special in this regard. Although it provides an Inline Style method to write CSS in JSX, it is estimated that not many people will try it. After all, CSS styles are no longer just simple CSS files. They are usually pre-processed with Less, Sass, etc., and then post-processed with postcss, myth, autoprefixer, cssmin, etc. Resource loading is generally done simply and crudely using module loaders.

Component Dependencies

The processing of component dependencies is generally divided into two parts: component loading and component use.

Component loading

React does not provide a related component loading method. It still needs to be introduced through the <script> tag, or the component's JSX and resource files need to be loaded using a module loader.

Component Usage

If you are careful, you will find that there are actually examples of its use before. If you want to use another component in one component, such as using ChildComponent in ParentComponent, you only need to write <ChildComponent /> in the render() method of ParentComponent, and you can pass some parameters when necessary.

doubt

At this point, you will find a problem. React only handles structure and logic, and does not care about resources or dependencies. Yes, React has nearly 20,000 lines of code, and does not even provide a module loader. What's more, unlike Angularjs, jQuery, etc., it does not come with any scaffolding... There is no Ajax library, no Promise library, and nothing else...

Virtual DOM

Why is it so big? Because it implements a virtual DOM. What does the virtual DOM do? Let's start with the browser itself.

As we know, when a browser renders a web page, after loading an HTML document, it will parse the document and build a DOM tree, which will then be combined with the CSSOM tree generated by parsing CSS to produce the fruit of love - the RenderObject tree, and then render the RenderObject tree into a page (of course, there may be some optimizations in the middle, such as the RenderLayer tree). These processes all exist in the rendering engine, which is separated from the JavaScript engine (JavaScriptCore or V8) in the browser. However, in order to facilitate JS to operate the DOM structure, the rendering engine will expose some interfaces for JavaScript to call. Since these two parts are separated from each other, communication comes at a cost, so the performance of JavaScript calling the interface provided by DOM is not very good. Various performance optimization best practices are also trying to reduce the number of DOM operations as much as possible.

What does the virtual DOM do? It implements the DOM tree directly with JavaScript (roughly). The HTML structure of the component does not directly generate the DOM, but is mapped to a virtual JavaScript DOM structure. React then implements a diff algorithm on this virtual DOM to find the smallest change, and then writes these changes to the actual DOM. This virtual DOM exists in the form of a JS structure, so the computing performance will be better, and because the number of actual DOM operations is reduced, the performance will be greatly improved.

I understand the logic, but why don’t we have a module loader?

So we need Webpack

Let’s talk about Webpack

What is Webpack?

In fact, it is a packaging tool, not a module loader like RequireJS or SeaJS. By using Webpack, it can handle dependencies like Node.js, and then resolve the dependencies between modules and package the code.

Installing Webpack

First you need Node.js

Then install webpack via npm install -g webpack. You can also use gulp to handle webpack tasks. If you use gulp, use npm install --save-dev gulp-webpack

Configure Webpack

The Webpack build process requires a configuration file. A typical configuration file is like this:

  1. var webpack = require( 'webpack' );
  2. var commonsPlugin = new webpack.optimize.CommonsChunkPlugin( 'common.js' );
  3.  
  4. module.exports = {
  5. entry: {
  6. entry1: './entry/entry1.js' ,
  7. entry2: './entry/entry2.js'  
  8. },
  9. output: {
  10. path: __dirname,
  11. filename: '[name].entry.js'  
  12. },
  13. resolve: {
  14. extensions: [ '' , '.js' , '.jsx' ]
  15. },
  16. module: {
  17. loaders: [{
  18. test: /\.js$/,
  19. loader: 'babel-loader'  
  20. }, {
  21. test: /\.jsx$/,
  22. loader: 'babel-loader!jsx-loader?harmony'  
  23. }]
  24. },
  25. plugins: [commonsPlugin]
  26. };

The packaging behavior of Webpack is configured here, which is mainly divided into several parts:

  • entry: specifies the entry file for packaging. Each key-value pair is an entry file.
  • output: configures the packaging result, path defines the output folder, filename defines the name of the packaging result file, and [name] in filename will be replaced by the key in entry (here entry1 and entry2)
  • resolve: defines the configuration for resolving module paths. The most commonly used one is extensions, which can be used to specify the module suffix. This way, you don’t need to write the suffix when importing the module, and it will be automatically completed.
  • module: defines the processing logic for the module. Here, you can use loaders to define a series of loaders and some regular expressions. When the file to be loaded matches the regular expression of test, the following loader will be called to process the file. This is why webpack is so powerful. For example, it is defined here that all files ending with .js are processed by babel-loader, and files ending with .jsx are processed by jsx-loader first, and then by babel-loader. Of course, these loaders also need to be installed through npm install
  • plugins: Here are the plugins that need to be used. For example, commonsPlugin will extract the common parts when packaging multiple entry files and generate common.js

Of course, Webpack has many other configurations, please refer to its configuration documentation for details

Execute packaging

If you install webpack via npm install -g webpack, you can directly execute the packaging command through the command line, such as this:

  1. $webpack --config webpack.config.js
 This will read the webpack.config.js in the current directory as the configuration file to perform packaging operations

If you use the gulp plugin gulp-webpack, you can write the gulp task in the gulpfile:

  1. var gulp = require( 'gulp' );
  2. var webpack = require( 'gulp-webpack' );
  3. var webpackConfig = require( './webpack.config' );
  4. gulp.task( "webpack" , function () {
  5. return gulp
  6. .src( './' )
  7. .pipe(webpack(webpackConfig))
  8. .pipe(gulp.dest( './build' ));
  9. });

#p#

Component Writing

Using Babel to improve your image

Webpack allows us to use the CommonJS specification of Node.js to write modules. For example, a simple Hello world module can be processed like this:

  1. var React = require( 'react' );
  2.  
  3. var HelloWorldComponent = React.createClass({
  4. displayName: 'HelloWorldComponent' ,
  5. render: function () {
  6. return (<div>Hello world</div>);
  7. }
  8. });
  9.  
  10. module.exports = HelloWorldComponent;

Wait, this is no different from the previous way of writing, still not cool... Programmers should write code like geeks, more cool than cool, this is too low. Now it's ES6, React code should also be written in ES6, babel-loader does this. Babel can convert ES6 code into ES5. First you need to install it through the command npm install --save-dev babel-loader, and you can use it after the installation is complete. One way to use it is to configure it in the loaders of webpack.config.js as introduced before, and the other is to use it directly in the code, such as:

  1. var HelloWorldComponent = require( '!babel!jsx!./HelloWorldComponent' );
 So how can we use Babel to improve the quality of our code? Let’s modify the previous HelloWorld code:
  1. import React from 'react' ;
  2.  
  3. export default   class HelloWorldComponent extends React.Component {
  4. constructor() {
  5. super ();
  6. this .state = {};
  7. }
  8. render() {
  9. return (<div>Hello World</div>);
  10. }
  11. }

In this way, if you need to import the HelloWorldComponent component in other components, you only need to:

  1. import HelloWorldComponent from './HelloWorldComponent'  
 How about it? Isn't it more cool? By importing the module, you can also directly define the inheritance relationship between classes. There is no need for getInitialState here . Just use this.state = xxx in the constructor .

Of course, Babel brings more than that. With its help, you can try many excellent ES6 features, such as arrow functions. The characteristic of arrow functions is that the internal this is consistent with the external one, so you can say goodbye to that and _this.

  1. ['H', 'e', ​​'l', 'l', 'o'].map((c) = > {
  2. return ( < span > {c} </ span > );
  3. });

There are many others, please refer to Babel's learning documentation for details

Style Writing

I am a patient who is strongly dependent on Less. If I write CSS directly without Less, I will feel weak, unwilling to work, and irritable. I also don’t like to add prefixes when writing Less. I usually handle it directly with gulp+less+autoprefixer. So how should I write it in the React component organized by Webpack?

Yes, loader is still used

You can add Less configuration to loaders in webpack.config.js:

  1. {
  2. test: /\.less$/,
  3. loader: 'style-loader!css-loader!autoprefixer-loader!less-loader'  
  4. }

With this configuration, you can directly introduce the Less style into the module code:

  1. import React from 'react' ;
  2.  
  3. require( './HelloWorldComponent.less' );
  4.  
  5. export default   class HelloWorldComponent extends React.Component {
  6. constructor() {
  7. super ();
  8. this .state = {};
  9. }
  10. render() {
  11. return (<div>Hello World</div>);
  12. }
  13. }

other

Webpack's loader provides a lot of help for React componentization. For example, images also provide relevant loaders:

  1. { test: /\.png$/, loader: "url-loader?mimetype=image/png" }
 For more loaders, please visit webpack's wiki

##Live debugging of React components under Webpack

Another powerful combination of Webpack and React is that after modifying the component source code, the changes can be synchronized to the page without refreshing the page. Here we need to use two libraries: webpack-dev-server and react-hot-loader.

First you need to install these two libraries, npm install --save-dev webpack-dev-server react-hot-loader

After the installation is complete, it is time to start configuration. First, you need to modify the entry configuration:

  1. entry: {
  2. helloworld: [
  3. 'webpack-dev-server/client?http://localhost:3000' ,
  4. 'webpack/hot/only-dev-server' ,
  5. './helloworld'  
  6. ]
  7. },

In this way, you can specify the server corresponding to the resource hot start, and then configure react-hot-loader to the loaders configuration. For example, all my component codes are placed in the scripts folder:

  1. {
  2. test: /\.js?$/,
  3. loaders: [ 'react-hot' , 'babel' ],
  4. include: [path.join(__dirname, 'scripts' )]
  5. }

Finally, configure plugins, add hot replacement plugins and error prevention plugins:

  1. plugins: [
  2. new webpack.HotModuleReplacementPlugin(),
  3. new webpack.NoErrorsPlugin()
  4. ]

The configuration is now complete, but now you need to start a server for debugging, and the previous configuration maps it to http://localhost:3000, so start a server on local port 3000 and create a server.js in the project root directory:

  1. var webpack = require( 'webpack' );
  2. var WebpackDevServer = require( 'webpack-dev-server' );
  3. var config = require( './webpack.config' );
  4.  
  5. new WebpackDevServer(webpack(config), {
  6. publicPath: config.output.publicPath,
  7. hot: true ,
  8. historyApiFallback: true  
  9. }).listen(3000, 'localhost' , function (err, result) {
  10. if (err) console.log(err);
  11. console.log( 'Listening at localhost:3000' );
  12. });

In this way, you can start the debugging server on the local port 3000. For example, if my page is index.html in the root directory, you can directly access the page through http://localhost:3000/index.html. After modifying the React component, the page will also be modified synchronously. It seems that websocket is used here to synchronize data. The picture shows a simple effect:

[[138854]]

Finish

React's component-based development is very interesting, and Webpack makes it easier to write and manage React components. This only covers a small part of React and Webpack. There are more best practices to be discovered on the road of learning.

<<:  In the App Era, Do Mobile Browsers Still Have a Chance?

>>:  W3C releases Mobile Checker

Recommend

7 predictions about live streaming

Live streaming is a stopover on the journey from ...

Xiaohongshu marketing promotion, 13 tips for hot-selling notes!

In January 2019, the number of Xiaohongshu users ...

The mystery of our nose: Why do we only have one nostril?

We use our noses every day, for breathing, smelli...

iPhone 6 case comparison

Foreign website Subscribe released a video compari...

Apple ignores doubts and buys Beats: Don’t spoil my fun!

Buy, buy, buy!!! This phrase has been very popular...