Webpack 系列之手把手教你使用 Webpack 搭建简易的 React 开发环境
在这篇文章中我们开始利用我们之前所学搭建一个简易的React开发环境,用以巩固我们之前学习的Webpack知识。首先我们需要明确这次开发环境需要达到的效果:
- 能够编译 JSX 语言
- css 样式使用 Sass 开发
- 能够将基础的 ES6 转化为 ES5
- 能够使用 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></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', output: { filename: 'bundle-[hash].js', path: path.join(__dirname, 'dist') }, devtool:'inline-source-map', devServer: { contentBase: './dist', hot: true }, 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-loade r的 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', output: { filename: 'bundle-[hash].js', path: path.join(__dirname, 'dist') }, devtool:'inline-source-map', devServer: { contentBase: './dist', hot: true }, module: { rules: [ { test: /.(js|jsx)/, use:[ 'babel-loader' ] },{ test: /.(sass|scss|css)/, use: [ "style-loader", "css-loader", "resolve-url-loader", "sass-loader?sourceMap" ] } ] }, plugins: [ new HtmlWebpackPlugin({ title:'React简易开发环境', template: './src/index.html' }), 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, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "env": { "browser": true, "node": true }, "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, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "extends": "airbnb", "env": { "browser": true, "node": true }, "parser": "babel-eslint", "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, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "extends": "airbnb", "env": { "browser": true, "node": true }, "parser": "babel-eslint", "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'], output: { filename: 'bundle-[hash].js', path: path.join(__dirname, 'dist') }, plugins: [ new HtmlWebpackPlugin({ title:'React简易开发环境', 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', devServer: { contentBase: './dist', hot: true }, module: { rules: [ { test: /.(js|jsx)/, use:[ 'babel-loader', 'eslint-loader' ] },{ test: /.(sass|scss|css)/, use: [ "style-loader", "css-loader", "resolve-url-loader", "sass-loader?sourceMap" ] },{ test: /.(png|jpg|svg|gif)/, use:[ "file-loader" ] } ] }, 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, module: { rules: [ { test: /.(js|jsx)/, use:[ 'babel-loader' ] },{ test: /.(sass|scss|css)/, use: [ "style-loader", "css-loader", "resolve-url-loader", "sass-loader?sourceMap" ] },{ test: /.(png|jpg|svg|gif)/, use:[ "file-loader" ] } ] }, 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
目录功能相信一眼就能看出来了。这时一个简易的环境就已经搭建好了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 动手实现一个 JSONP
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论