搭建React全家桶框架

搭建后的全家桶框架目錄名字

1. 創建文件夾react-family并進入

2. npm init -y 生成package.json

3.安裝webpack

3.1 npm install --save-dev?webpack

3.2 創建webpack.dev.config.js

const?path?=?require('path')

module.exports?=?{??

????/*?入口?*/??

????entry:?path.join(__dirname,?'src/index.js'),??

????/*?輸出到dist文件夾,輸出文件名字為bundle.js?*/??

????output:?{????

????????path:?path.join(__dirname,?'./dist'),

????????filename:?'bundle.js'??

????}

}


3.3 新建入口文件

創建src文件夾,并在src下添加index.js文件

document.getElementById(‘app’).innerHTML = “Webpack!”

3.4 執行命令 webpack --config webpack.dev.config.js

生成了dist文件夾和bundle.js,在dist文件添加index.html文件,并引入bundle.js,在瀏覽器打開index.html,可以看到Webpack!


index.html

4?Babel

Babel 把用最新標準編寫的 JavaScript 代碼向下編譯成可以在今天隨處可用的版本。 這一過程叫做“源碼到源碼”編譯, 也被稱為轉換編譯。

babel-core 調用Babel的API進行轉碼

babel-loader

babel-preset-es2015 用于解析 ES6

babel-preset-react 用于解析 JSX

babel-preset-stage-0 用于解析 ES7 提案

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0

其中babel-loader 8.1.0版本和?babel-core 6.26.3版本不兼容,可以babel-loader改為7版本

新建babel配置文件.babelrc

{

??"presets":?[

????"es2015",

????"react",

????"stage-0"

??],

??"plugins":?[]

}

修改webpack.dev.config.js,增加babel-loader!

module:?{

????rules:?[{

????????test:?/\.js$/,

????????use:?['babel-loader?cacheDirectory=true'],

????????include:?path.join(__dirname,?'src')

????}]

??}

測試下,修改入口文件 src/index.js

var?func?=?str?=>?{

??document.getElementById('app').innerHTML?=?str

}

func('我現在在使用Babel!')

執行打包命令webpack --config webpack.dev.config.js

瀏覽器打開index.html,我們看到正確輸出了我現在在使用Babel!

打開打包后的bundle.js,可以看到箭頭函數被轉換成普通函數了!

5 react

npm install --save react react-dom

添加Hello組件? src/components/Hello/Hello.js

import?React,?{?Component?}?from?'react'

export?default?class?Hello?extends?Component?{

??render?()?{

????return?(

??????<div>

????????hello?react

??????</div>

????)

??}

}

修改入口文件?src/index.js使用react

import React from 'react';

import ReactDom from 'react-dom';

import Hello from './components/Hello/Hello';

ReactDom.render(

? ? <Hello/>, document.getElementById('app'));

打包webpack --config webpack.dev.config.js,打開index.html查看效果

6 命令優化

每次打包都得執行長命令webpack –config webpack.dev.config.js,所以我們優化下

修改package.json里面的script,增加dev-build

"scripts":?{

????"test":?"echo?\"Error:?no?test?specified\"?&&?exit?1",

????"dev-build":?"webpack?--config?webpack.dev.config.js"

??},

現在我們打包只需要執行npm run dev-build

7 react-router

cnpm install --save react-router-dom

新建兩個頁面

// src/pages/Home/Home.js

import?React,?{?Component?}?from?'react'

export?default?class?Home?extends?Component?{

??render?()?{

????return?(

??????<div>

????????this?is?home

??????</div>

????)

??}

}

// src/pages/ Page1/Page1.js?

import?React,?{?Component?}?from?'react'

export?default?class? Page1 extends?Component?{

??render?()?{

????return?(

??????<div>

????????this?is??page1

??????</div>

????)

??}

}

//? src/router/router.js

import?React?from?'react'

import?{?BrowserRouter?as?Router,?Route,?Switch,?Link?}?from?'react-router-dom'

import?Home?from?'../pages/Home/Home'

import?Page1?from?'../pages/Page1/Page1'

const?getRouter?=?()?=>?(

??<Router>

????<div>

??????<ul>

????????<li><Link?to='/'>首頁</Link></li>

????????<li><Link?to='/page1'>Page1</Link></li>

??????</ul>

??????<Switch>

????????<Route?exact?path='/'?component={Home}?/>

????????<Route?path='/page1'?component={Page1}?/>

??????</Switch>

????</div>

??</Router>

)

export?default?getRouter

修改入口文件src/index.js,引用Router

import?ReactDom?from?'react-dom'

import?getRouter?from?'./router/router'

ReactDom.render(

??getRouter(),?document.getElementById('app'))

8 webpack-dev-server

cnpm install webpack-dev-server@2 --save-dev

(版本2不會報錯)

修改webpack.dev.config.js,增加webpack-dev-server的配置。

devServer:?{

????contentBase:?path.join(__dirname,?'./dist'),

????historyApiFallback:?true,

????host:?'0.0.0.0',

????port:?8765

??}

現在執行 webpack-dev-server --config webpack.dev.config.js

瀏覽器打開http://localhost:8765,可以頁面路由切換了

修改package.json里面的script,增加start

"start":?"webpack-dev-server?--config?webpack.dev.config.js",

color(CLI only) console中打印彩色日志

historyApiFallback 任意的404響應都被替代為index.html。有什么用呢?你現在運行npm start,然后打開瀏覽器,訪問http://localhost:8080,然后點擊Page1到鏈接http://localhost:8080/page1

然后刷新頁面試試。是不是發現刷新后404了。為什么?dist文件夾里面并沒有page1.html,當然會404了,所以我們需要配置

historyApiFallback,讓所有的404定位到index.html。

host 指定一個host,默認是localhost。如果你希望服務器外部可以訪問,指定如下:host: “0.0.0.0”。比如你用手機通過IP訪問。

hot 啟用Webpack的模塊熱替換特性。

port 配置要監聽的端口。默認就是我們現在使用的8080端口。

proxy 代理。比如在 localhost:3000 上有后端服務的話,你可以這樣啟用代理:

proxy:?{

? ? '/api':?'地址'

}

progress(CLI only) 將編譯進度輸出到控制臺。

9 模塊熱替換(Hot Module Replacement)

當我們修改代碼的時候,瀏覽器會自動刷新,為了修改代碼的時候,瀏覽器不會刷新。

修改package.json 增加 –hot

"start":?"webpack-dev-server?--config?webpack.dev.config.js?--color?--progress?--hot",

src/index.js 增加module.hot.accept(), 當模塊更新的時候,通知index.js。

import?ReactDom?from?'react-dom'

import?getRouter?from?'./router/router'

if?(module.hot)?{

??module.hot.accept()

}

ReactDom.render(

??getRouter(),?document.getElementById('app'))

執行npm run start,修改Home.js, 在不刷新頁面的情況下,內容更新了

但是當模塊熱替換的時候,state會重置

// src/pages/Home/Home.js
import?React,?{?Component?}?from?'react'

export?default?class?Home?extends?Component?{

??constructor?(props)?{

????super(props)

????this.state?=?{

??????count:?0

????}

??}

??_handleClick?()?{

????this.setState({

??????count:?++this.state.count

????})

??}

??render?()?{

????return?(

??????<div>

????????this?is?home~<br?/>

????????當前計數:{this.state.count}<br?/>

????????<button?onClick={()?=>?this._handleClick()}>自增</button>

??????</div>

????)

??}

}

當我們修改代碼的時候,webpack在更新時候,也把count初始為0了

為了在react模塊更新的同時,能保留state等頁面中其他狀態,我們需要引入react-hot-loader

cnpm install react-hot-loader --save-dev

修改.babelrc 增加 react-hot-loader/babel

{

??"presets":?[

????"es2015",

????"react",

????"stage-0"

??],

??"plugins":?[

????"react-hot-loader/babel"

??]

}

webpack.dev.config.js入口增加react-hot-loader/patch

entry:?[

????'react-hot-loader/patch',

????path.join(__dirname,?'src/index.js')

??]

修改src/index.js

import?React?from?'react'

import?ReactDom?from?'react-dom'

import?{?AppContainer?}?from?'react-hot-loader'

import?getRouter?from?'./router/router'

/*?初始化?*/

renderWithHotReload(getRouter())

/*?熱更新?*/

if?(module.hot)?{

??module.hot.accept('./router/router',?()?=>?{

????const?getRouter?=?require('./router/router').default

????renderWithHotReload(getRouter())

??})

}

function?renderWithHotReload?(RootElement)?{

??ReactDom.render(

????<AppContainer>

??????{RootElement}

????</AppContainer>,

????document.getElementById('app')

??)

}

執行npm start,修改頁面的時候,state不更新

10?文件路徑優化

修改webpack.dev.config.js

resolve:?{

????alias:?{

??????pages:?path.join(__dirname,?'src/pages'),

??????components:?path.join(__dirname,?'src/components'),

??????router:?path.join(__dirname,?'src/router'),

??????actions:?path.join(__dirname,?'src/redux/actions'),

??????reducers:?path.join(__dirname,?'src/redux/reducers')

????}

??}

修改后,引用路徑就可以這樣子了

import?Home?from?'pages/Home/Home'

import?Page1?from?'pages/Page1/Page1'

11?redux

因為太多了,這邊簡單列一個例子叭

// src/redux/actions/counter.js

export?const?INCREMENT?=?'counter/INCREMENT'

export?const?DECREMENT?=?'counter/DECREMENT'

export?const?RESET?=?'counter/RESET'

export?function?increment?()?{

??return?{?type:?INCREMENT?}

}

export?function?decrement?()?{

??return?{?type:?DECREMENT?}

}

export?function?reset?()?{

??return?{?type:?RESET?}

}

// src/redux/reducers/counter.js

import?{?INCREMENT,?DECREMENT,?RESET?}?from?'../actions/counter'

/*

*?初始化state

?*/

const?initState?=?{

??count:?0

}

/*

*?reducer

?*/

export?default?function?reducer?(state?=?initState,?action)?{

??switch?(action.type)?{

????case?INCREMENT:

??????return?{

????????count:?state.count?+?1

??????}

????case?DECREMENT:

??????return?{

????????count:?state.count?-?1

??????}

????case?RESET:

??????return?{?count:?0?}

????default:

??????return?state

??}

}

// src/redux/reducers.js?

import?{?combineReducers?}?from?'redux'

import?counter?from?'reducers/counter'

export?default?combineReducers({

??counter

})

// src/redux/store.js?
import?{?createStore?}?from?'redux'

import?combineReducers?from?'./reducers'

let?store?=?createStore(combineReducers)

if?(module.hot)?{

??module.hot.accept('./reducers',?()?=>?{

????const?nextCombineReducers?=?require('./reducers').default

????store.replaceReducer(nextCombineReducers)

??})

}

export?default?store

在組件中引用redux,react-redux提供了一個方法connect,connect接收兩個參數,mapStateToProps,就是把redux的state,轉為組件的Props,mapDispatchToprops, 就是把發射actions的方法,轉為Props屬性函數。

cnpm install --save?react-redux

創建src/pages/Counter/Counter.js,并在路由文件添加路由

import?React,?{?Component?}?from?'react'

import?{?increment,?decrement,?reset?}?from?'actions/counter'

import?{?connect?}?from?'react-redux'

class?Counter?extends?Component?{

??render?()?{

????return?(

??????<div>

????????<div>當前計數為{this.props.counter.count}</div>

????????<button?onClick={()?=>?this.props.increment()}>自增

????????</button>

????????<button?onClick={()?=>?this.props.decrement()}>自減

????????</button>

????????<button?onClick={()?=>?this.props.reset()}>重置

????????</button>

??????</div>

????)

??}

}

const?mapStateToProps?=?(state)?=>?{

??return?{

????counter:?state.counter

??}

}

const?mapDispatchToProps?=?(dispatch)?=>?{

??return?{

????increment:?()?=>?{

??????dispatch(increment())

????},

????decrement:?()?=>?{

??????dispatch(decrement())

????},

????reset:?()?=>?{

??????dispatch(reset())

????}

??}

}

export?default?connect(mapStateToProps,?mapDispatchToProps)(Counter)

在入口文件傳入store

import?React?from?'react'

import?ReactDom?from?'react-dom'

import?{?AppContainer?}?from?'react-hot-loader'

import?{?Provider?}?from?'react-redux'

import?store?from?'./redux/store'

import?getRouter?from?'router/router'

/*?初始化?*/

renderWithHotReload(getRouter())

/*?熱更新?*/

if?(module.hot)?{

??module.hot.accept('./router/router',?()?=>?{

????const?getRouter?=?require('router/router').default

????renderWithHotReload(getRouter())

??})

}

function?renderWithHotReload?(RootElement)?{

??ReactDom.render(

????<AppContainer>

??????<Provider?store={store}>

????????{RootElement}

??????</Provider>

????</AppContainer>,

????document.getElementById('app')

??)

}

執行npm start,打開localhost:8080/counter

12?devtool優化

增加webpack配置devtool!可以查看瀏覽器報錯的詳細代碼位置

src/webpack.dev.config.js增加devtool: ‘inline-source-map’

13? 編譯css

cnpm install css-loader style-loader --save-dev

css-loader使你能夠使用類似@import 和 url(…)的方法實現 require()的功能;

style-loader將所有的計算后的樣式加入頁面中; 二者組合在一起使你能夠把樣式表嵌入webpack打包后的JS文件中。

修改page1.js


Page.css

測試查看page1頁面效果

14?編譯圖片

cnpm install --save-dev url-loader file-loader

webpack.dev.config.js rules增加

{

????????test:?/\.(png|jpg|gif)$/,

????????use:?[{

??????????loader:?'url-loader',

??????????options:?{

????????????limit:?8192

??????????}

????????}]

??????},

options limit 8192意思是,小于等于8K的圖片會被轉成base64編碼,直接插入HTML中,減少HTTP請求。

引用圖片,查看效果

修改page1.js

15?按需加載

打包完后,所有頁面只生成了一個build.js,當我們首屏加載的時候,就會很慢。因為也下載了別的頁面的js。

如果每個頁面都打包了自己單獨的JS,在進入自己頁面的時候才加載對應的js,那首屏加載就會快很多。

cnpm install bundle-loader --save-dev

創建src/router/Bundle.js

import?React,?{

??Component

}?from?'react'

class?Bundle?extends?Component?{

??state?=?{

????//?short?for?"module"?but?that's?a?keyword?in?js,?so?"mod"

????mod:?null

??};

??UNSAFE_componentWillMount()?{

????this.load(this.props)

??}

??UNSAFE_componentWillReceiveProps(nextProps)?{

????if?(nextProps.load?!==?this.props.load)?{

??????this.load(nextProps)

????}

??}

??load(props)?{

????this.setState({

??????mod:?null

????});

????props.load((mod)?=>?{

??????this.setState({

????????//?handle?both?es?imports?and?cjs

????????mod:?mod.default???mod.default?:?mod

??????})

????})

??}

??render()?{

????return?this.props.children(this.state.mod)

??}

}

export?default?Bundle;

修改路由文件src/router/router.js

import?React?from?'react'

import?{?Route,?Switch?}?from?'react-router-dom'

import?Bundle?from?'./Bundle'

import?Home?from?'bundle-loader?lazy&name=home!pages/Home/Home'

import?Page1?from?'bundle-loader?lazy&name=page1!pages/Page1/Page1'

import?Counter?from?'bundle-loader?lazy&name=counter!pages/Counter/Counter'

import?Loading?from?'components/Loading/Loading'

const?createComponent?=?(component)?=>?(props)?=>?(

??<Bundle?load={component}>

????{

??????(Component)?=>?Component???<Component?{...props}?/>?:?<Loading?/>

????}

??</Bundle>

)

const?getRouter?=?()?=>?(

??<div?className='margin'>

????<Switch>

??????<Route?exact?path='/'?component={createComponent(Home)}?/>

??????<Route?path='/page1'?component={createComponent(Page1)}?/>

??????<Route?path='/counter'?component={createComponent(Counter)}?/>

????</Switch>

??</div>

)

export?default?getRouter

npm run?start,打開瀏覽器,進入新的頁面,都會加載自己的JS,但是名字都是0.bundle.js,分不清哪個頁面的js,所以修改配置文件

output:?{

????path:?path.join(__dirname,?'./dist'),

????filename:?'[name].[chunkhash].js',

????chunkFilename:?'[name].[chunkhash].js',

????publicPath:?'/'

??},

運行發現名字變成home.js(router.js里面import Home from ‘bundle-loader?lazy&name=home!pages/Home/Home’;這里name=home)

16?緩存

為了讓用戶第二次訪不重復下載js,所以要做個緩存,但緩存后,我們修改代碼,更新線上后,用戶還是之前的js,那就會出bug

所以代碼更新后,最好使打包生成的名字不一樣。比如第一次叫home.a.js,第二次叫home.b.js。

修改配置文件

output:?{

??path:?path.join(__dirname,?'./dist'),

??filename:?'[name].[hash].js',

??chunkFilename:?'[name].[chunkhash].js'

}

打包都用增加hash,修改了文件,打包后相應的文件名字也改變了

17?HtmlWebpackPlugin

這個插件,每次會自動把js插入到你的模板index.html里面

cnpm install html-webpack-plugin --save-dev

新建模板src/index.html

<!DOCTYPE?html>

<html?lang="en">

<head>

??<meta?charset="UTF-8">

??<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">

??<title>test2</title>

</head>

<body>

??<div?id="app"></div>

</body>

</html>

修改webpack.dev.config.js,增加plugin

new?HtmlWebpackPlugin({

??????filename:?'index.html',

??????template:?path.join(__dirname,?'src/index.html')

????}),

18?提取公共代碼

打包后bundle.js里面包含了react,redux,react-router等等公共庫,這些代碼基本上不會改變的。但是,他們合并在bundle.js里面,每次項目發布,重新請求bundle.js的時候,相當于重新請求了react等這些公共庫所以把react這些不會改變的公共庫提取出來,用戶緩存下來。從此以后,用戶再也不用下載這些庫了

const?webpack?=?require('webpack')

entry:?{

????app:?[

??????path.join(__dirname,?'src/index.js')

????],

????vendor:?['react',?'react-router-dom',?'redux',?'react-dom',?'react-redux']

??},

// plugins

new?webpack.optimize.CommonsChunkPlugin({

??????name:?'vendor'

????}),

把react等庫生成打包到vendor.hash.js里面去。

但是發現編譯生成的文件app.[hash].js和vendor.[hash].js生成的hash一樣的,因為每次修改代碼,都會導致vendor.[hash].js名字改變,那我們提取出來的意義也就沒了。其實文檔上寫的很清楚

output:?{

??path:?path.join(__dirname,?'./dist'),

??filename:?'[name].[hash].js',?//這里應該用chunkhash替換hash

??chunkFilename:?'[name].[chunkhash].js'

}

但是如果用chunkhash,會報錯。和webpack-dev-server –hot不兼容,

所以配置正式版webpack.config.js

新建webpack.config.js

先刪除webpack-dev-server相關的東西

devtool的值改成cheap-module-source-map

hash改成chunkhash

const?path?=?require('path')

var?HtmlWebpackPlugin?=?require('html-webpack-plugin')

var?webpack?=?require('webpack')

module.exports?=?{

??devtool:?'cheap-module-source-map',

??entry:?{

????app:?[

??????path.join(__dirname,?'src/index.js')

????],

????vendor:?['react',?'react-router-dom',?'redux',?'react-dom',?'react-redux']

??},

??output:?{

????path:?path.join(__dirname,?'./dist'),

????filename:?'[name].[chunkhash].js',

????chunkFilename:?'[name].[chunkhash].js'

??},

??module:?{

????rules:?[{

??????test:?/\.js$/,

??????use:?['babel-loader'],

??????include:?path.join(__dirname,?'src')

????},?{

??????test:?/\.css$/,

??????use:?['style-loader',?'css-loader']

????},?{

??????test:?/\.(png|jpg|gif)$/,

??????use:?[{

????????loader:?'url-loader',

????????options:?{

??????????limit:?8192

????????}

??????}]

????}]

??},

??plugins:?[

????new?HtmlWebpackPlugin({

??????filename:?'index.html',

??????template:?path.join(__dirname,?'src/index.html')

????}),

????new?webpack.optimize.CommonsChunkPlugin({

??????name:?'vendor'

????})

??],

??resolve:?{

????alias:?{

??????pages:?path.join(__dirname,?'src/pages'),

??????component:?path.join(__dirname,?'src/component'),

??????router:?path.join(__dirname,?'src/router'),

??????actions:?path.join(__dirname,?'src/redux/actions'),

??????reducers:?path.join(__dirname,?'src/redux/reducers')

????}

??}

}

在package.json增加打包腳本

"build":?"webpack?--config?webpack.config.js"

執行npm run build~看看dist文件夾

19?文件壓縮

cnpm i --save-dev uglifyjs-webpack-plugin

修改配置文件

var?UglifyJSPlugin?=?require('uglifyjs-webpack-plugin')

//?plugins

new?UglifyJSPlugin(),

npm run build發現打包文件大小減小了好多

20?指定環境

許多 library 將通過與 process.env.NODE_ENV 環境變量關聯,以決定 library 中應該引用哪些內容。例如,當不處于生產環境中時,某些 library 為了使調試變得容易,可能會添加額外的日志記錄(log)和測試(test)。其實,當使用 process.env.NODE_ENV === ‘production’ 時,一些 library 可能針對具體用戶的環境進行代碼優化,從而刪除或添加一些重要代碼。我們可以使用 webpack 內置的 DefinePlugin 為所有的依賴定義這個變量:

//?plugins
new?webpack.DefinePlugin({

??????'process.env':?{

????????'NODE_ENV':?JSON.stringify('production')

??????}

????}),

npm run build后發現vendor.[hash].js又變小了。

21?優化緩存

剛才我們把[name].[hash].js變成[name].[chunkhash].js后,npm run build后,發現app.xxx.js和vendor.xxx.js不一樣。

隨便修改代碼一處,例如Home.js,隨便改變個字,你發現home.xxx.js名字變化的同時,vendor.xxx.js名字也變了。這不行啊。這和沒拆分不是一樣一樣了嗎?我們希望是vendor.xxx.js名字永久不變,一直緩存在用戶本地的。

官方文檔推薦了一個插件HashedModuleIdsPlugin

plugins:?[

??new?webpack.HashedModuleIdsPlugin(),

? new webpack.optimize.CommonsChunkPlugin({ name: 'runtime' }) //? 引入順序在這里很重要。CommonsChunkPlugin 的 ‘vendor’ 實例,必須在 ‘runtime’ 實例之前引入。

]

22?打包優化

cnpm install clean-webpack-plugin --save-dev

修改webpack.config.js

const?CleanWebpackPlugin?=?require('clean-webpack-plugin');

plugins:?[

????new?CleanWebpackPlugin(['dist'])

]

23?抽取css

cnpm install --save-dev extract-text-webpack-plugin

const?ExtractTextPlugin?=?require("extract-text-webpack-plugin");

module.exports?=?{

??module:?{

????rules:?[

??????{

????????test:?/\.css$/,

????????use:?ExtractTextPlugin.extract({

??????????fallback:?"style-loader",

??????????use:?"css-loader"

????????})

??????}

????]

??},

??plugins:?[

?????new?ExtractTextPlugin({

?????????filename:?'[name].[contenthash:5].css',

?????????allChunks:?true

?????})

??]

}

npm run build后發現單獨生成了css文件

24?合并提取webpack公共配置

把公共的配置文件提取出來。提取到webpack.common.config.js里面~

webpack.dev.config.js和webpack.config.js寫自己的特殊的配置。

這里我們需要用到webpack-merge來合并公共配置和單獨的配置。

cnpm install --save-dev webpack-merge

新建webpack.common.config.js

const?path?=?require('path');

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

const?webpack?=?require('webpack');

commonConfig?=?{

????entry:?{

????????app:?[

????????????path.join(__dirname,?'src/index.js')

????????],

????????vendor:?['react',?'react-router-dom',?'redux',?'react-dom',?'react-redux']

????},

????output:?{

????????path:?path.join(__dirname,?'./dist'),

????????filename:?'[name].[chunkhash].js',

????????chunkFilename:?'[name].[chunkhash].js',

????????publicPath:?"/"

????},

????module:?{

????????rules:?[{

????????????test:?/\.js$/,

????????????use:?['babel-loader?cacheDirectory=true'],

????????????include:?path.join(__dirname,?'src')

????????},?{

????????????test:?/\.(png|jpg|gif)$/,

????????????use:?[{

????????????????loader:?'url-loader',

????????????????options:?{

????????????????????limit:?8192

????????????????}

????????????}]

????????}]

????},

????plugins:?[

????????new?HtmlWebpackPlugin({

????????????filename:?'index.html',

????????????template:?path.join(__dirname,?'src/index.html')

????????}),

????????new?webpack.HashedModuleIdsPlugin(),

????????new?webpack.optimize.CommonsChunkPlugin({

????????????name:?'vendor'

????????}),

????????new?webpack.optimize.CommonsChunkPlugin({

????????????name:?'runtime'

????????})

????],

????resolve:?{

????????alias:?{

????????????pages:?path.join(__dirname,?'src/pages'),

????????????components:?path.join(__dirname,?'src/components'),

????????????router:?path.join(__dirname,?'src/router'),

????????????actions:?path.join(__dirname,?'src/redux/actions'),

????????????reducers:?path.join(__dirname,?'src/redux/reducers')

????????}

????}

};

module.exports?=?commonConfig;

修改?webpack.dev.config.js

const?merge?=?require('webpack-merge');

const?path?=?require('path');

const?commonConfig?=?require('./webpack.common.config.js');

const?devConfig?=?{

????devtool:?'inline-source-map',

????entry:?{

????????app:?[

????????????'react-hot-loader/patch',

????????????path.join(__dirname,?'src/index.js')

????????]

????},

????output:?{

????????/*這里本來應該是[chunkhash]的,但是由于[chunkhash]和react-hot-loader不兼容。只能妥協*/

????????filename:?'[name].[hash].js'

????},

????module:?{

????????rules:?[{

????????????test:?/\.css$/,

????????????use:?["style-loader",?"css-loader"]

????????}]

????},

????devServer:?{

????????contentBase:?path.join(__dirname,?'./dist'),

????????historyApiFallback:?true,

????????host:?'0.0.0.0',

????}

};

module.exports?=?merge({

????customizeArray(a,?b,?key)?{

????????/*entry.app不合并,全替換*/

????????if?(key?===?'entry.app')?{

????????????return?b;

????????}

????????return?undefined;

????}

})(commonConfig,?devConfig);

修改webpack.config.js

const?merge?=?require('webpack-merge');

const?webpack?=?require('webpack');

const?UglifyJSPlugin?=?require('uglifyjs-webpack-plugin');

const?CleanWebpackPlugin?=?require('clean-webpack-plugin');

const?ExtractTextPlugin?=?require("extract-text-webpack-plugin");

const?commonConfig?=?require('./webpack.common.config.js');

const?publicConfig?=?{

????devtool:?'cheap-module-source-map',

????module:?{

????????rules:?[{

????????????test:?/\.css$/,

????????????use:?ExtractTextPlugin.extract({

????????????????fallback:?"style-loader",

????????????????use:?"css-loader"

????????????})

????????}]

????},

????plugins:?[

????????new?CleanWebpackPlugin(['dist/*.*']),

????????new?UglifyJSPlugin(),

????????new?webpack.DefinePlugin({

????????????'process.env':?{

????????????????'NODE_ENV':?JSON.stringify('production')

????????????}

????????}),

????????new?ExtractTextPlugin({

????????????filename:?'[name].[contenthash:5].css',

????????????allChunks:?true

????????})

????]

};

module.exports?=?merge(commonConfig,?publicConfig);

25?優化目錄結構并增加404頁面

優化下目錄結構,把router和nav分開,新建根組件App

新建根組件components/App/APP.js

import?React,?{Component}?from?'react';

import?Nav?from?'components/Nav/Nav';

import?getRouter?from?'router/router';

export?default?class?App?extends?Component?{

????render()?{

????????return?(

????????????<div>

????????????????<Nav/>

????????????????{getRouter()}

????????????</div>

????????)

????}

}

新建components/Nav/Nav組件,把router/router.js里面的nav提出來。

新建components/Loading/Loading組件,把router/router.js里面的Loading提出來。

入口文件src/index.js修改

import?React?from?'react';

import?ReactDom?from?'react-dom';

import?{AppContainer}?from?'react-hot-loader';

import?{Provider}?from?'react-redux';

import?store?from?'./redux/store';

import?{BrowserRouter?as?Router}?from?'react-router-dom';

import?App?from?'components/App/App';

renderWithHotReload(App);

if?(module.hot)?{

????module.hot.accept('components/App/App',?()?=>?{

????????const?NextApp?=?require('components/App/App').default;

????????renderWithHotReload(NextApp);

????});

}

function?renderWithHotReload(RootElement)?{

????ReactDom.render(

????????<AppContainer>

????????????<Provider?store={store}>

????????????????<Router>

????????????????????<RootElement/>

????????????????</Router>

????????????</Provider>

????????</AppContainer>,

????????document.getElementById('app')

????)

}

新建pages/NotFound/NotFound組件。

修改router/router.js,增加404

import?NotFound?from?'bundle-loader?lazy&name=notFound!pages/NotFound/NotFound';

<Route?component={createComponent(NotFound)}/>

26?集成PostCSS

npm install --save-dev postcss-loader

npm install --save-dev postcss-cssnext //??postcss-cssnext允許你使用未來的 CSS 特性(包括 autoprefixer)

修改webpack配置文件,增加postcss-loader

修改webpack.config.js

rules:?[{

??test:?/\.css$/,

??use:?ExtractTextPlugin.extract({

??????fallback:?"style-loader",

??????use:?["css-loader",?"postcss-loader"]

??})

}]

修改webpack.dev.config.js

rules:?[{

??test:?/\.(css|scss)$/,

??use:?["style-loader",?"css-loader",?"postcss-loader"]

}]

根目錄增加postcss配置文件。

module.exports?=?{

??plugins:?{

????'postcss-cssnext':?{}

??}

}

先這些叭沒有了


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。