簡評:相信不少開發者在入門 react 的時候都是使用 create-react-app 或 react-slingshot 這些腳手架來快速建立應用,當有特殊需求,須要修改 eject 出來的 webpack 配置文件時,面對各類配置項不知如何下手,本文會介紹如何使用 webpack 手動搭建一個 react 項目。javascript
新建工程css
1.先新建一個 demo 項目,項目目錄結構爲:
html
2.在工程根目錄新建 index.html 文件java
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>My React Boilerplate</title> </head> <body> <div id="app"></div> </body> </html>
3.安裝 react react-dom 依賴:node
npm i react react-dom
4.建立應用 /src/components/App.jsreact
import React, { Component } from 'react' class App extends Component { render() { return ( <div> <h1>Welcome to My Starter App</h1> </div> ) } } export default App
5.建立 /src/index.jswebpack
import React from 'react' import ReactDOM from 'react-dom' import App from './components/App' import './styles/style.sass' ReactDOM.render( <App />, document.getElementById('app') )
以上就是咱們的 demo 工程。若是咱們經過 react-create-app 上面的代碼已經能夠正常運行了,可是如今的代碼沒有進行任何的處理沒法直接在瀏覽器中運行。web
咱們須要將 jsx 和 ES6 代碼轉換成瀏覽器中可運行的代碼。npm
Babeljson
Babel 就是爲了處理上面的問題,咱們能夠使用 JavaScript 最新的語法特性,而後使用 babel 插件對代碼進行轉換以達到最大的兼容性。首先安裝相關依賴:
npm i babel-cli babel-core babel-preset-env babel-preset-react babel-preset-stage-2--save-dev
而後在工程跟目錄建立一個 .babelrc 配置文件,內容爲:
{ "presets": ["env", "react", "stage-2"] }
參數說明:
測試
剛纔已經建立了 App.js 的 React 組件,而且安裝配置了 babel,爲了代碼的健壯性咱們再添加測試環境這裏使用 Jest 和 Enzyme。
1.安裝依賴:
npm i jest enzyme enzyme-adapter-react-16 react-test-renderer --save-dev
2.而後建立 /test/enzyme.setup.js 文件,添加以下代碼:
import Enzyme from 'enzyme' import Adapter from 'enzyme-adapter-react-16' Enzyme.configure({ adapter: new Adapter() })
3.在 package.json 中添加 jest 功能字段:
{ ..., "jest": { "setupTestFrameworkScriptFile": "./test/enzyme.setup.js" }, ... }
4.編寫測試代碼:
建立 /test/App.test.js 文件,爲 App 組件編寫測試代碼:
import App from '../src/components/App' import React from 'react' import { shallow } from 'enzyme' describe('App', () => { test('should match snapshot', () => { const wrapper = shallow(<App />) expect(wrapper.find('h1').text()).toBe('Welcome to My Starter App') expect(wrapper).toMatchSnapshot }) })
當執行 jest ./test 來啓動測試代碼。
爲了方即可以將他添加到 package.json 中:
{ ..., "scripts": { "test": "jest ./test" } }
Webpack
webpack 能夠將咱們工程代碼打包到一個文件中,好比咱們有不少個 js 代碼相互依賴,打包的時候會將這些 js 文件合併成一個文件,還能夠使用插件來預處理和處理最終生成的代碼。
1.安裝 webpack 依賴:
npm i webpack --save-dev
webpack 運行的時候回自動找到項目根目錄的 webpack.config.js 文件,因此咱們能夠先建立這個文件,並加入以下代碼。
const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), } }
entry 指明入口文件,webpack 會從這個文件開始鏈接全部的依賴。 output 指明打包後的文件存放的位置。
Webpack loaders
loaders 可讓 webpack 處理不少不一樣格式的文件(例如:圖片、CSS 、 JSX ...),
這裏咱們沒有用到圖片和 CSS 資源,只須要處理 ES6 和 JSX,只須要 babel-loader。
1.安裝 babel-loader:
npm i babel-loader --save-dev
而後在 webpack.config.js 文件中添加打包規則添加後代碼以下:
const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), }, module: { rules: [ { test: /\.js$/, exclude: ['node_modules'], use: [{ loader: 'babel-loader' }], } ] }
2.若是須要使用 Sass 和 SCSS,咱們須要其餘的 loader。
npm i node-sass sass-loader style-loader css-loader --save-dev
而後在 webpack.config.js 中添加 sass 和 scss 文件的轉換規則,最終代碼以下:
const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), }, module: { rules: [ { test: /\.js$/, exclude: ['node_modules'], use: [{ loader: 'babel-loader' }], }, { test: /\.s(a|c)ss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }], } ] }
如今能夠在工程中使用 Sass 了,建立 /src/styles/style.sass 文件,並添加以下代碼:
body font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif color: white background: black
而後在 index.js 帶人 style.sass:
import React from 'react' import ReactDOM from 'react-dom' import App from './components/App' import './styles/style.sass' ReactDOM.render( <App />, document.getElementById('app')
3.如今有一個需求,咱們須要將打包後的 js 文件自動導入到 html 文件中,咱們能夠使用 html-webpack-plugin 自動完成這部份內容。
安裝 html-webpack-plugin:
npm i html-webpack-plugin --save-dev
而後在 webpack.config.js 中導入這個插件:
const CleanWebpackPlugin = require('clean-webpack-plugin') const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), }, module: { rules: [ { test: /\.js$/, exclude: ['node_modules'], use: [{ loader: 'babel-loader' }], }, { test: /\.s(a|c)ss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }], } ] }, plugins: [ new HtmlWebPackPlugin({ template: 'index.html' }) ] }
當每次 build 的時候的時候會在 dist 目錄中生成打包後的文件,咱們須要打包時清除這些內容能夠使用 clean-webpack-plugin 插件:
npm i clean-webpack-plugin --save-dev
在 webpack.config.js 中添加 clean-webpack-plugin:
const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebPackPlugin = require('html-webpack-plugin') const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), }, module: { rules: [ { test: /\.js$/, exclude: ['node_modules'], use: [{ loader: 'babel-loader' }], }, { test: /\.s(a|c)ss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }], } ] }, plugins: [ new HtmlWebPackPlugin({ template: 'index.html' }), new CleanWebpackPlugin(['dist']), ] }
全部的 plugin 和 loader 已經加載完了,爲了方便咱們開發,還須要 webpack 開發服務器,這樣咱們就能夠實時查看代碼修改的效果了。
npm i webpack-cli webpack-dev-server --save-dev
在 webpack.config.js 中添加 devServer 字段:
const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebPackPlugin = require('html-webpack-plugin') const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), }, module: { rules: [ { test: /\.js$/, exclude: ['node_modules'], use: [{ loader: 'babel-loader' }], }, { test: /\.s(a|c)ss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }], } ] }, plugins: [ new HtmlWebPackPlugin({ template: 'index.html' }), new CleanWebpackPlugin(['dist']), ], devServer: { host: 'localhost', port: 3000, open: true } }
爲了方便運行,能夠將 webpack-dev-server 命令添加到 package.json 中:
{ ... "scripts": { "start": "webpack-dev-server", "test": "jest ./test" }, }
如今只須要 npm start 就能夠查看 demo 的運行效果,到這裏 react webpack 項目開發環境已經算是搭建完成。
可是這個配置沒有對生產環境作區分,也就是說生產環境的代碼和開發環境代碼同樣,但實際開發中每每須要對生產環境代碼作優化好比(壓縮 js代碼,修改變量名和方法名),在開發環境中爲了編譯調試又不但願優化這部份內容。咱們能夠將兩種環境區分開來。
這個時候有能夠用到這個插件 webpack-merge:
npm i webpack-merge --save-dev
如今咱們能夠將原來的 webpack 配置文件分離成三個文件,
webpack.common.js 存放公共配置項:
const CleanWebpackPlugin = require('clean-webpack-plugin') const HtmlWebPackPlugin = require('html-webpack-plugin') const path = require('path') module.exports = { entry: { main: './src/index.js' }, output: { filename: '[name].[hash].js', path: path.resolve('./dist'), }, module: { rules: [ { test: /\.js$/, exclude: ['node_modules'], use: [{ loader: 'babel-loader' }], }, { test: /\.s(a|c)ss$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }], } ] }, plugins: [ new HtmlWebPackPlugin({ template: 'index.html' }), new CleanWebpackPlugin(['dist']), ] }
webpack.dev.js 在通用配置項基礎上添加開發環境配置項:
const merge = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'development', devServer: { host: 'localhost', port: 3000, open: true } })
webpack.prod.js 在通用配置項基礎上中添加生成環境配置項:
const merge = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'production', })
最後在 package.json 中添加生產環境打包腳本:
{ ... "scripts": { "build": "webpack --config webpack.prod.js", "start": "webpack-dev-server --config webpack.dev.js", "test": "jest ./test" }, }