Webpack系列——手把手教你使用Webpack搭建简易的React开发环境

文章首发于我的github及个人博客,github请看https://github.com/huruji/blo...,转载请注明出处。

在这篇文章中我们开始利用我们之前所学搭建一个简易的React开发环境,用以巩固我们之前学习的Webpack知识。首先我们需要明确这次开发环境需要达到的效果:1、能够编译JSX语言 2、css样式使用Sass开发 3.能够将基础的ES6转化为ES5 4.能够使用ESLint在开发的时候为我们做代码风格审查

首先,安装基本使用的webpack、webpack-dev-server

npm i webpack webpack-dev-server -D

基本页面的生成

为了可以生成一个基本的页面我们使用html-webpack-plugin,为了方便我们定制,我们自己在src定义一个html文件,使用template指定这个文件。

安装html-webpack-plugin

npm i html-webpack-plugin -D

在src文件夹下生成一个html文件,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

在webpack.config.js中写入以下内容作为基本的设置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const config = {
  entry: './src/main.js',output: {
    filename: 'bundle-[hash].js',path: path.join(__dirname,'dist')
  },devtool:'inline-source-map',devServer: {
    contentBase: './dist',hot: true
  },plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),new webpack.HotModuleReplacementPlugin()
  ]
}

module.exports = config;

此时在命令行中运行以下命令可以看到一切正常运行,尽管目前在浏览器上还没有任何效果:

webpack-dev-server --open

编译es6和jsx语言

在React开发的时候我们使用jsx语言和es6,因此需要使用babel对我们的开发进行一个编译,使用babel即可:
安装babel-loader:

npm i babel-loader -D

为了使用这个babel-loader,我们需要安装babel-core(当我们需要以编程方式使用babel时就需要安装这个):

npm i babel-core -D

为了编译es6和jsx需要安装相应的preset,即需要安装babel-preset-react和babel-preset-es2015:

npm i babel-preset-es2015 babel-preset-react -D

在webpack的配置文件中引入babel-loader:

const config = {
  //....
  module:{
    rules: [
      {
        test: /\.(js|jsx)$/,use:[
          'babel-loader'
        ]
      }
    ]
  }
  // ......
}

module.exports = config;

配置babel的配置文件,在.babelrc文件中写入以下内容:

{
  "presets": [
    "es2015","react"
  ]
}

此时我们测试一下是否可以正常编译jsx和es2015,安装react和react-dom,同时在src中的main.js和App.js写入部分内容

npm i react react-dom -S

main.js

import ReactDOM from 'react-dom';
import React from 'react';
import App from './App';

ReactDOM.render(<App />,document.getElementById('app'));

App.js

import React from 'react';

export default function () {
  return (
    <div className="header">
      React
    </div>
  );
}

在命令行运行命令,可以发现浏览器已经正常显示了,也就是说正常编译了jsx和es6

webpack-dev-server --open

此时,整个webpack.config.js文件内容如下:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const config = {
  entry: './src/main.js',module: {
    rules: [
      {
        test: /\.(js|jsx)/,use:[
          'babel-loader'
        ]
      }
    ]
  },plugins: [
    new HtmlWebpackPlugin({
      title:'React简易开发环境',template: './src/index.html'
    }),new webpack.HotModuleReplacementPlugin()
  ]
}

module.exports = config;

编译Sass样式

编译Sass和之前文章提到的一样,需要使用style-loader、css-loader、sass-loader,首先进行安装:

npm i style-loader css-loader sass-loader -D

因为sass-loader是依赖node-sass的,同时因为sass-loader的uri是相对于output的,因此需要使用resolve-url-loader

npm i node-sass resolve-url-loader -D

在webpack.config.js中进行配置:

const config = {
  // ......
  module: {
    rules: [
      //......
      {
        test: /\.(sass|scss|css)/,use: [
        "style-loader","css-loader","resolve-url-loader","sass-loader?sourceMap"
        ]
      }
    ]
  },// ......
}

module.exports = config;

在src文件夹中新建一个名为sass的文件夹,同时新建_header.scss、_variablers.scss、main.scss,三个文件内容分别为:
_variablers.scss

$bgColor: red;
$fontColor: #fff;

_header.scss

.header{
    background: $bgColor;
    color: $fontColor;
    height:300px;
}

main.scss

@import "variables","header"

在main.js中引入main.scss文件:

import './sass/main.scss';

此时再次运行命令,可以在浏览器中看到header部分的样式已经生效。

此时整个webpack.config.js文件内容如下:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const config = {
  entry: './src/main.js',use:[
          'babel-loader'
        ]
      },{
        test: /\.(sass|scss|css)/,new webpack.HotModuleReplacementPlugin()
  ]
}

module.exports = config;

配置ESLint为我们做代码风格检查

使用eslint首先安装eslint和eslint-loader:

npm i eslint eslint-loader -D

为了让eslint支持es6我们需要将eslint的解析器修改为babel-eslint,使用npm安装

npm i babel-eslint -D

在webpack.config.js中配置eslint-loader

const config = {
  // ......
  module: {
    rules: [
      {
        test: /\.(js|jsx)/,use:[
          'babel-loader','eslint-loader'
        ]
      }
    ]
  },// ......
}

module.exports = config;

新建一个eslint的配置文件.eslintrc.js:

module.exports = {
  "parserOptions": {
    "ecmaVersion": 6,"sourceType": "module","ecmaFeatures": {
      "jsx": true
    }
  },"env": {
    "browser": true,"node": true
  },"parser": "babel-eslint"
};

此时运行命令行会发现正常运行,原因是eslint默认所有规则都是禁用的,我们在.eslintrc.js中添加一条简单的禁用console的规则,当出现console时,将会报warning

module.exports = {
  "parserOptions": {
    "ecmaVersion": 6,"parser": "babel-eslint","rules": {
      "no-console": 1
  }
};

此时再次运行命令,可以发现以下界面,控制台已经很明确的告诉我们,我们的App.js中出现了console,说明此时eslint已经生效。

但是在一个项目中我们如果配置每一个规则会显得非常麻烦,因此我们选择使用airbnb的规则,使用npm安装:

npm i eslint-config-airbnb -D

安装完成之后可以发现控制台告诉我们需要安装eslint-plugin-jsx-a11y、eslint-plugin-import、eslint-plugin-react,同时安装时应该大于或者等于某个版本号:

npm i eslint-plugin-jsx-a11y@5.1.1 eslint-plugin-import@2.7.0 eslint-plugin-react@7.1.0 -D

在.eslintrc.js文件中使用extends指定继承自airbnb的配置,如下:

module.exports = {
  "parserOptions": {
    "ecmaVersion": 6,"extends": "airbnb","rules": {
      "no-console": 1
  }
};

此时,再次运行命令之后可以发现,在命令行和控制台中都报出了我们的代码风格问题,如下:

airbnb中的所有规则我们可以根据我们的需要进行重写,我们注意到其中一条error如下:

JSX not allowed in files with extension '.js'      react/jsx-filename-extension

前面的为相应说明,后面的为规则,这条不允许我们在.js文件中书写JSX语言,后面为对应的规则,显然是eslint-plugin-react插件的规则,我们可以重写以允许我们在.js文件中书写JSX,如下:

module.exports = {
  "parserOptions": {
    "ecmaVersion": 6,"rules": {
      "no-console": 1,"react/jsx-filename-extension": [1,{ "extensions": [".js",".jsx"] }]
  }
};

再次运行可以发现这条error已经不存在了。

在项目中解析图片模块

在之前的文章中我们已经提到过了,我们可以使用file-loader来实现:

npm i file-loader -D

在webpack.config.js中配置:

const config = {
  // ......
  module: {
    rules: [
      {
        test: /\.(png|jpg|svg|gif)/,use:[
          "file-loader"
        ]
      }
    ]
  },// ......
}

module.exports = config;

此时我们可以引入图片资源了。

支持更多的ES6方法

我们在使用babel的时候我们需要明确知道的一点是,babel默认只是为我们转化语法层面上的东西(如箭头函数),并不会为我们去将一些方法进行转化为es2015的实现,也就是说如果我使用Array.of方法,如果浏览器不支持这个方法,及时按照上面的babel转化也是依旧没有办法运行的,我们可以在App.js中使用Array.of方法来测试一下,如下:

Array.of(1,2,3,4).forEach(function(item){
        console.log(item);
    });

我们这次使用webpack命令直接在dist文件夹中生成相应的文件,我们可以在js文件中找到以下内容:

这就验证了上文的说法,因此我们需要使用babel-polyfill
首先进行安装:

npm i install babel-polyfill -D

安装完成之后我们需要在webpack的入口中进行配置,将webpack的entry修改为以下内容:

entry: ['babel-polyfill','./src/main.js']

开发与生产环境分离

我们现在使用webpack命令为我们打包一下内容,我们会发现打包后的文件非常大,只有部分内容却打包之后有3000+kb,这是不能用在生产环境上的,如下:

文件体积太大一个重要原因是devtool开启了inline-source-map方便我们定位bug,同时代码没有压缩也是重要原因之一,因此我们需要将开发和生产环境分离,使用不同的webpack配置。

还记得我们系列之前介绍的webpack-merge吗?我们通过这个插件可以将公共的配置分离到一起。首先进行安装

npm i webpack-merge -D

新建一个名为webpack.common.js文件作为公共配置,写入以下内容:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const config = {
  entry: ['babel-polyfill','./src/main.js'],template: './src/index.html'
    })
  ]
}

module.exports = config;

新建一个名为webpack.dev.js文件作为开发环境配置,写入以下内容:

const merge = require('webpack-merge');
const common = require('./webpack.common');
const webpack = require('webpack');

const config = merge(common,{
  devtool:'inline-source-map','eslint-loader'
        ]
      },"sass-loader?sourceMap"
        ]
      },{
        test: /\.(png|jpg|svg|gif)/,plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
});

module.exports = config;

刚刚我们提到我们在开发环境中应该压缩混淆代码同时精简输出,因此需要使用uglifyjs-webpack-plugin插件,首先进行安装:

npm i uglifyjs-webpack-plugin -D

新建一个名为webpack.prod.js的文件作为生产环境配置,写入以下内容:

const merge = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

const config = merge(common,{
  devtool:false,plugins:[
    new UglifyJSPlugin()
  ]
});

module.exports = config;

因为在开发时我们需要使用的命令是

webpack-dev-server --open --config webpack.dev.js

而在生产中我们需要使用的命令是

webpack --config webpack.prod.js

为了精简我们在命令行中的输入我们将这些命令写在package.json中

"scripts": {
    "dev": "webpack-dev-server --open --colors --progress --inline --config webpack.dev.js","build": "webpack --colors --progress --config webpack.prod.js"
  }

此时我们只要在命令行中输入npm run dev即可开启开发环境,使用npm run build即可自动生成用于生产环境的文件。

使用clean-webpack-plugin

现在还有一个问题是我们修改文件之后再次使用npm run build命令则会出现多个js文件,这是因为我们使用了hash占位符,
这个占位符可以保证用户访问网站时始终保持最新的js文件,因此我们使用clean-webpack-plugin帮助我们每次删除dist文件夹的内容

npm i clean-webpack-plugin -D

在webpack.prod.js中引用:

const merge = require('webpack-merge');
const common = require('./webpack.common');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const config = merge(common,{
  // ......
  plugins:[
    new CleanWebpackPlugin(['./dist']),new UglifyJSPlugin()
  ]
});

module.exports = config;

开发src目录划分

虽然目前一个简易的React开发环境已经搭建好了,但是还是需要对src目录进行划分以保证良好的开发体验,以下是划分的目录:

└───Components
      └───......
      └───......
└───Containers
      └───......
      └───......
└───static
      └───sass
      └───img
└───index.html
└───main.js

目录功能相信一眼就能看出来了。这时一个简易的环境就已经搭建好了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom