文章首發(fā)于我的github及個(gè)人博客,github請(qǐng)看https://github.com/huruji/blog/issues/8舞虱,轉(zhuǎn)載請(qǐng)注明出處独撇。
在這篇文章中我們開始利用我們之前所學(xué)搭建一個(gè)簡(jiǎn)易的React開發(fā)環(huán)境厘灼,用以鞏固我們之前學(xué)習(xí)的Webpack知識(shí)义图。首先我們需要明確這次開發(fā)環(huán)境需要達(dá)到的效果:1、能夠編譯JSX語(yǔ)言 2蝌戒、css樣式使用Sass開發(fā) 3.能夠?qū)⒒A(chǔ)的ES6轉(zhuǎn)化為ES5 4.能夠使用ESLint在開發(fā)的時(shí)候?yàn)槲覀冏龃a風(fēng)格審查
首先串塑,安裝基本使用的webpack、webpack-dev-server
npm i webpack webpack-dev-server -D
基本頁(yè)面的生成
為了可以生成一個(gè)基本的頁(yè)面我們使用html-webpack-plugin北苟,為了方便我們定制桩匪,我們自己在src定義一個(gè)html文件,使用template指定這個(gè)文件友鼻。
安裝html-webpack-plugin
npm i html-webpack-plugin -D
在src文件夾下生成一個(gè)html文件傻昙,內(nèi)容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
在webpack.config.js中寫入以下內(nèi)容作為基本的設(shè)置:
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;
此時(shí)在命令行中運(yùn)行以下命令可以看到一切正常運(yùn)行闺骚,盡管目前在瀏覽器上還沒(méi)有任何效果:
webpack-dev-server --open
編譯es6和jsx語(yǔ)言
在React開發(fā)的時(shí)候我們使用jsx語(yǔ)言和es6,因此需要使用babel對(duì)我們的開發(fā)進(jìn)行一個(gè)編譯妆档,使用babel即可:
安裝babel-loader:
npm i babel-loader -D
為了使用這個(gè)babel-loader僻爽,我們需要安裝babel-core(當(dāng)我們需要以編程方式使用babel時(shí)就需要安裝這個(gè)):
npm i babel-core -D
為了編譯es6和jsx需要安裝相應(yīng)的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文件中寫入以下內(nèi)容:
{
"presets": [
"es2015",
"react"
]
}
此時(shí)我們測(cè)試一下是否可以正常編譯jsx和es2015,安裝react和react-dom蔗衡,同時(shí)在src中的main.js和App.js寫入部分內(nèi)容
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>
);
}
在命令行運(yùn)行命令纤虽,可以發(fā)現(xiàn)瀏覽器已經(jīng)正常顯示了,也就是說(shuō)正常編譯了jsx和es6
webpack-dev-server --open
此時(shí)绞惦,整個(gè)webpack.config.js文件內(nèi)容如下:
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簡(jiǎn)易開發(fā)環(huán)境',
template: './src/index.html'
}),
new webpack.HotModuleReplacementPlugin()
]
}
module.exports = config;
編譯Sass樣式
編譯Sass和之前文章提到的一樣逼纸,需要使用style-loader、css-loader济蝉、sass-loader杰刽,首先進(jìn)行安裝:
npm i style-loader css-loader sass-loader -D
因?yàn)閟ass-loader是依賴node-sass的,同時(shí)因?yàn)閟ass-loader的uri是相對(duì)于output的王滤,因此需要使用resolve-url-loader
npm i node-sass resolve-url-loader -D
在webpack.config.js中進(jìn)行配置:
const config = {
// ......
module: {
rules: [
//......
{
test: /\.(sass|scss|css)/,
use: [
"style-loader",
"css-loader",
"resolve-url-loader",
"sass-loader?sourceMap"
]
}
]
},
// ......
}
module.exports = config;
在src文件夾中新建一個(gè)名為sass的文件夾贺嫂,同時(shí)新建_header.scss、_variablers.scss雁乡、main.scss,三個(gè)文件內(nèi)容分別為:
_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';
此時(shí)再次運(yùn)行命令第喳,可以在瀏覽器中看到header部分的樣式已經(jīng)生效。
此時(shí)整個(gè)webpack.config.js文件內(nèi)容如下:
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簡(jiǎn)易開發(fā)環(huán)境',
template: './src/index.html'
}),
new webpack.HotModuleReplacementPlugin()
]
}
module.exports = config;
配置ESLint為我們做代碼風(fēng)格檢查
使用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;
新建一個(gè)eslint的配置文件.eslintrc.js:
module.exports = {
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true,
"node": true
},
"parser": "babel-eslint"
};
此時(shí)運(yùn)行命令行會(huì)發(fā)現(xiàn)正常運(yùn)行曲饱,原因是eslint默認(rèn)所有規(guī)則都是禁用的,我們?cè)?eslintrc.js中添加一條簡(jiǎn)單的禁用console的規(guī)則珠月,當(dāng)出現(xiàn)console時(shí)扩淀,將會(huì)報(bào)warning
module.exports = {
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true,
"node": true
},
"parser": "babel-eslint",
"rules": {
"no-console": 1
}
};
此時(shí)再次運(yùn)行命令,可以發(fā)現(xiàn)以下界面啤挎,控制臺(tái)已經(jīng)很明確的告訴我們驻谆,我們的App.js中出現(xiàn)了console,說(shuō)明此時(shí)eslint已經(jīng)生效庆聘。
但是在一個(gè)項(xiàng)目中我們?nèi)绻渲妹恳粋€(gè)規(guī)則會(huì)顯得非常麻煩旺韭,因此我們選擇使用airbnb的規(guī)則,使用npm安裝:
npm i eslint-config-airbnb -D
安裝完成之后可以發(fā)現(xiàn)控制臺(tái)告訴我們需要安裝eslint-plugin-jsx-a11y掏觉、eslint-plugin-import区端、eslint-plugin-react,同時(shí)安裝時(shí)應(yīng)該大于或者等于某個(gè)版本號(hào):
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
}
};
此時(shí)织盼,再次運(yùn)行命令之后可以發(fā)現(xiàn)杨何,在命令行和控制臺(tái)中都報(bào)出了我們的代碼風(fēng)格問(wèn)題,如下:
airbnb中的所有規(guī)則我們可以根據(jù)我們的需要進(jìn)行重寫沥邻,我們注意到其中一條error如下:
JSX not allowed in files with extension '.js' react/jsx-filename-extension
前面的為相應(yīng)說(shuō)明危虱,后面的為規(guī)則,這條不允許我們?cè)?js文件中書寫JSX語(yǔ)言唐全,后面為對(duì)應(yīng)的規(guī)則埃跷,顯然是eslint-plugin-react插件的規(guī)則,我們可以重寫以允許我們?cè)?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"] }]
}
};
再次運(yùn)行可以發(fā)現(xiàn)這條error已經(jīng)不存在了弥雹。
在項(xiàng)目中解析圖片模塊
在之前的文章中我們已經(jīng)提到過(guò)了,我們可以使用file-loader來(lái)實(shí)現(xiàn):
npm i file-loader -D
在webpack.config.js中配置:
const config = {
// ......
module: {
rules: [
{
test: /\.(png|jpg|svg|gif)/,
use:[
"file-loader"
]
}
]
},
// ......
}
module.exports = config;
此時(shí)我們可以引入圖片資源了延届。
支持更多的ES6方法
我們?cè)谑褂胋abel的時(shí)候我們需要明確知道的一點(diǎn)是剪勿,babel默認(rèn)只是為我們轉(zhuǎn)化語(yǔ)法層面上的東西(如箭頭函數(shù)),并不會(huì)為我們?nèi)⒁恍┓椒ㄟM(jìn)行轉(zhuǎn)化為es2015的實(shí)現(xiàn)方庭,也就是說(shuō)如果我使用Array.of方法厕吉,如果瀏覽器不支持這個(gè)方法,及時(shí)按照上面的babel轉(zhuǎn)化也是依舊沒(méi)有辦法運(yùn)行的械念,我們可以在App.js中使用Array.of方法來(lái)測(cè)試一下头朱,如下:
Array.of(1,2,3,4).forEach(function(item){
console.log(item);
});
我們這次使用webpack命令直接在dist文件夾中生成相應(yīng)的文件,我們可以在js文件中找到以下內(nèi)容:
這就驗(yàn)證了上文的說(shuō)法龄减,因此我們需要使用babel-polyfill
首先進(jìn)行安裝:
npm i install babel-polyfill -D
安裝完成之后我們需要在webpack的入口中進(jìn)行配置髓窜,將webpack的entry修改為以下內(nèi)容:
entry: ['babel-polyfill','./src/main.js']
開發(fā)與生產(chǎn)環(huán)境分離
我們現(xiàn)在使用webpack命令為我們打包一下內(nèi)容,我們會(huì)發(fā)現(xiàn)打包后的文件非常大欺殿,只有部分內(nèi)容卻打包之后有3000+kb寄纵,這是不能用在生產(chǎn)環(huán)境上的,如下:
文件體積太大一個(gè)重要原因是devtool開啟了inline-source-map方便我們定位bug脖苏,同時(shí)代碼沒(méi)有壓縮也是重要原因之一程拭,因此我們需要將開發(fā)和生產(chǎn)環(huán)境分離,使用不同的webpack配置棍潘。
還記得我們系列之前介紹的webpack-merge嗎恃鞋?我們通過(guò)這個(gè)插件可以將公共的配置分離到一起。首先進(jìn)行安裝
npm i webpack-merge -D
新建一個(gè)名為webpack.common.js文件作為公共配置亦歉,寫入以下內(nèi)容:
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簡(jiǎn)易開發(fā)環(huán)境',
template: './src/index.html'
})
]
}
module.exports = config;
新建一個(gè)名為webpack.dev.js文件作為開發(fā)環(huán)境配置恤浪,寫入以下內(nèi)容:
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;
剛剛我們提到我們?cè)陂_發(fā)環(huán)境中應(yīng)該壓縮混淆代碼同時(shí)精簡(jiǎn)輸出,因此需要使用uglifyjs-webpack-plugin插件肴楷,首先進(jìn)行安裝:
npm i uglifyjs-webpack-plugin -D
新建一個(gè)名為webpack.prod.js的文件作為生產(chǎn)環(huán)境配置水由,寫入以下內(nèi)容:
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;
因?yàn)樵陂_發(fā)時(shí)我們需要使用的命令是
webpack-dev-server --open --config webpack.dev.js
而在生產(chǎn)中我們需要使用的命令是
webpack --config webpack.prod.js
為了精簡(jiǎn)我們?cè)诿钚兄械妮斎胛覀儗⑦@些命令寫在package.json中
"scripts": {
"dev": "webpack-dev-server --open --colors --progress --inline --config webpack.dev.js",
"build": "webpack --colors --progress --config webpack.prod.js"
}
此時(shí)我們只要在命令行中輸入npm run dev即可開啟開發(fā)環(huán)境,使用npm run build即可自動(dòng)生成用于生產(chǎn)環(huán)境的文件赛蔫。
使用clean-webpack-plugin
現(xiàn)在還有一個(gè)問(wèn)題是我們修改文件之后再次使用npm run build命令則會(huì)出現(xiàn)多個(gè)js文件砂客,這是因?yàn)槲覀兪褂昧薶ash占位符泥张,這個(gè)占位符可以保證用戶訪問(wèn)網(wǎng)站時(shí)始終保持最新的js文件,因此我們使用clean-webpack-plugin幫助我們每次刪除dist文件夾的內(nèi)容
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;
開發(fā)src目錄劃分
雖然目前一個(gè)簡(jiǎn)易的React開發(fā)環(huán)境已經(jīng)搭建好了鞠值,但是還是需要對(duì)src目錄進(jìn)行劃分以保證良好的開發(fā)體驗(yàn)媚创,以下是劃分的目錄:
└───Components
└───......
└───......
└───Containers
└───......
└───......
└───static
└───sass
└───img
└───index.html
└───main.js
目錄功能相信一眼就能看出來(lái)了。這時(shí)一個(gè)簡(jiǎn)易的環(huán)境就已經(jīng)搭建好了彤恶。
最后是一個(gè)廣告貼钞钙,最近新開了一個(gè)分享技術(shù)的公眾號(hào),歡迎大家關(guān)注??