React技術棧+Express+Mongodb實現個人部落格 -- Part 4
內容回顧
前面的篇幅主要介紹了:
- React技術棧+Express+Mongodb實現個人部落格 -- Part 1 部落格頁面展示
- React技術棧+Express+Mongodb實現個人部落格 -- Part 2 後臺管理頁面
- React技術棧+Express+Mongodb實現個人部落格 -- Part 3 Express + Mongodb建立Server端
到目前階段,你對Express, Webpack, React
已經有了基本的瞭解,但前端頁面服務和API服務目前還是分離的,本篇文章主要介紹:
- 使用Webpack打包工程
- 使用常用的外掛
package.json
修改package.json
檔案,安裝需要的package
{
"name": "react_express_blog",
"version": "1.0.0",
"description": "react-express-mongo demo",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch-client": "cross-env NODE_ENV=development node ./server/index.js",
"start-prod": "cross-env NODE_ENV=production node bin/server",
"start-dev-api": "nodemon --watch server/api server/api/index.js",
"start": "npm run watch-client & npm run start-dev-api"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sam408130/react-blog/tree/part3"
},
"author": "Sam",
"license": "ISC",
"bugs": {
"url": "https://github.com/sam408130/react-blog/tree/part3/issues"
},
"homepage": "https://github.com/sam408130/react-blog/tree/part3",
"dependencies": {
"antd": "^2.13.1",
"axios": "^0.16.2",
"bluebird": "^3.5.0",
"body-parser": "^1.18.0",
"compression": "^1.7.0",
"connect-history-api-fallback": "^1.3.0",
"cookie-parser": "^1.4.3",
"cookies": "^0.7.1",
"dateformat": "^3.0.2",
"echarts-for-react": "^2.0.0",
"express": "^4.15.4",
"express-session": "^1.15.5",
"http-proxy": "^1.16.2",
"markdown": "^0.5.0",
"mongoose": "^4.11.11",
"qs": "^6.5.1",
"react": "^15.6.1",
"react-addons-pure-render-mixin": "^15.6.0",
"react-dom": "^15.6.1",
"react-helmet": "^5.2.0",
"react-markdown": "^2.5.0",
"react-redux": "^5.0.6",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-slick": "^0.15.4",
"redux": "^3.7.2",
"redux-saga": "^0.15.6",
"remark": "^8.0.0",
"remark-react": "^4.0.1",
"serialize-javascript": "^1.4.0",
"serve-favicon": "^2.4.4"
},
"devDependencies": {
"autoprefixer": "^7.1.4",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-import": "^1.4.0",
"babel-plugin-react-transform": "^2.0.2",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-remove-console": "^6.8.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-react-optimize": "^1.0.1",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"babel-runtime": "^6.26.0",
"clean-webpack-plugin": "^0.1.16",
"concurrently": "^3.5.0",
"cross-env": "^5.0.5",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.30.1",
"install": "^0.10.1",
"less": "^2.7.2",
"less-loader": "^4.0.5",
"node-loader": "^0.6.0",
"node-sass": "^4.5.3",
"nodemon": "^1.12.0",
"npm": "^5.4.1",
"open-browser-webpack-plugin": "0.0.5",
"postcss-loader": "^2.0.6",
"progress-bar-webpack-plugin": "^1.10.0",
"react-hot-loader": "^3.0.0-beta.6",
"redbox-react": "^1.5.0",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"url-loader": "^0.5.9",
"webpack": "^3.5.6",
"webpack-dev-middleware": "^1.12.0",
"webpack-hot-middleware": "^2.19.1",
"webpack-isomorphic-tools": "^3.0.3"
}
}
上面的配置檔案是工程所需要的所有模組,先安裝,下面會逐一介紹用途:
npm install
webpack配置檔案
在根目錄建立webpack
開發環境的配置檔案webpack.dev.js
首先看一下完整的檔案內容
const pathLib = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CleanPlugin = require('clean-webpack-plugin');
const config = require('./config/config');
const ROOT_PATH = pathLib.resolve(__dirname);
const ENTRY_PATH = pathLib.resolve(ROOT_PATH, 'src');
const OUTPUT_PATH = pathLib.resolve(ROOT_PATH, 'build');
const AppHtml = pathLib.resolve(ENTRY_PATH,'index.html')
console.log(pathLib.resolve(ENTRY_PATH, 'index.js'));
module.exports = {
entry: {
index: [
'react-hot-loader/patch',
`webpack-hot-middleware/client?path=http://${config.host}:${config.port}/__webpack_hmr`,
'babel-polyfill',
pathLib.resolve(ENTRY_PATH, 'index.js')
],
vendor: ['react', 'react-dom', 'react-router-dom']
},
output: {
path: OUTPUT_PATH,
publicPath: '/',
filename: '[name]-[hash:8].js'
},
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
exclude: /node_modules/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]-[local]-[hash:base64:5]',
importLoaders: 1
}
},
'postcss-loader'
]
},
{
test: /\.css$/,
include: /node_modules/,
use: ['style-loader',
{
loader: 'css-loader'
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: ["style-loader", 'css-loader', "postcss-loader", "less-loader"]
},
{
test: /\.(png|jpg|gif|JPG|GIF|PNG|BMP|bmp|JPEG|jpeg)$/,
exclude: /node_modules/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
},
{
test: /\.(eot|woff|ttf|woff2|svg)$/,
use: 'url-loader'
}
]
},
plugins: [
new CleanPlugin(['build']),
new ProgressBarPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),//改善chunk傳輸
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
"progress.env.NODE_ENV": JSON.stringify('development')
}),
new HtmlWebpackPlugin({
inject: true,
template: AppHtml,
}),
new webpack.NoEmitOnErrorsPlugin(),//保證出錯時頁面不阻塞,且會在編譯結束後報錯
new webpack.HashedModuleIdsPlugin(),//用 HashedModuleIdsPlugin 可以輕鬆地實現 chunkhash 的穩定化
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
return module.context && module.context.indexOf('node_modules') !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest"
}),
new OpenBrowserPlugin({
url: `http://${config.host}:${config.port}`
})
],
resolve: {
extensions: ['.js', '.json', '.sass', '.scss', '.less', 'jsx']
}
}
檔案中的幾個引數,entry, output, devtool, module, plugins
起的作用,在上一篇內容都有介紹,沒有完全理解的同學,可以再回看一下React技術棧+Express+Mongodb實現個人部落格 -- Part 3 Express + Mongodb建立Server端中webpack
的介紹。
定義引數
檔案頭部是下面配置內容中使用的引數,統一寫在頭部可以方便呼叫
entry
index部分定義工程的入口,檔案中分為4個部分:
-
react-hot-loader/patch
結合babel-polyfill
, 允許我們在使用jsx
語法編寫react
時,能讓修改的部分自動重新整理。但這和自動重新整理網頁是不同的,因為 hot-loader 並不會重新整理網頁,而僅僅是替換你修改的部分。 -
webpack-hot-middleware
,允許Express
結合Webpack
實現HMR
。HMR 即 Hot Module Replacement 是 Webpack 一個重要的功能。它可以使我們不用通過手動地重新整理瀏覽器頁面實現將我們的更新程式碼實時應用到當前頁面中。 -
index.js
是部落格的入口頁面 -
vendor
,該部分的作用是,將指定的模組打包成一個vendor.js
檔案,從最後編譯的bundle.js
工程檔案中分離出來。
output
該部分定義打包檔案輸出的路徑,由於我們使用了html-webpack-plugin
外掛,這裡就可以使用檔名加hash
的方法,解決快取問題。
devtool
開發總是離不開除錯,方便的除錯能極大的提高開發效率,不過有時候通過打包後的檔案,你是不容易找到出錯了的地方,對應的你寫的程式碼的位置的,Source Maps就是來幫我們解決這個問題的。
通過簡單的配置,webpack就可以在打包時為我們生成的source maps,這為我們提供了一種對應編譯檔案和原始檔的方法,使得編譯後的程式碼可讀性更高,也更容易除錯。
在webpack的配置檔案中配置source maps,需要配置devtool,它有以下四種不同的配置選項,各具優缺點,描述如下:
devtool | 配置結果 |
---|---|
source-map |
在一個單獨的檔案中產生一個完整且功能完全的檔案。這個檔案具有最好的source map ,但是它會減慢打包速度; |
cheap-module-source-map |
在一個單獨的檔案中生成一個不帶列對映的map ,不帶列對映提高了打包速度,但是也使得瀏覽器開發者工具只能對應到具體的行,不能對應到具體的列(符號),會對除錯造成不便; |
eval-source-map |
使用eval 打包原始檔模組,在同一個檔案中生成乾淨的完整的source map 。這個選項可以在不影響構建速度的前提下生成完整的sourcemap ,但是對打包後輸出的JS 檔案的執行具有效能和安全的隱患。在開發階段這是一個非常好的選項,在生產階段則一定不要啟用這個選項 |
cheap-module-eval-source-map |
這是在打包檔案時最快的生成source map 的方法,生成的Source Map 會和打包後的JavaScript檔案同行顯示,沒有列對映,和eval-source-map 選項具有相似的缺點; |
這裡我們使用cheap-module-eval-source-map
module
上一節我們介紹了loaders
的作用,以及如何讓css檔案模組化。module
部分是我們使用的所有loaders,在完成該部分時,記得在根路徑上配置.babelrc
和postcss.config.js
檔案
// .babelrc
{
"presets": ["es2015","react","stage-0","env"],
"plugins": ["react-hot-loader/babel",["import", { "libraryName": "antd", "style": true }],"transform-runtime","transform-class-properties"],
"env": {
"production":{
"preset":["react-optimize"]
}
}
}
// postcss.config.js
module.export = {
plugins:[
require('autoprefixer')({browsers:'last 2 versions'})
]
};
plugins
1. html-webpack-plugin
這個外掛的主要作用有兩個:
- 為html檔案中引入的外部資源如script、link動態新增每次compile後的hash,防止引用快取的外部檔案問題
- 可以生成建立html入口檔案,比如單頁面可以生成一個html檔案入口,配置N個html-webpack-plugin可以生成N個頁面入口
程式碼中是這麼定義的
const AppHtml = pathLib.resolve(ENTRY_PATH,'index.html')
...
new HtmlWebpackPlugin({
inject: true,
template: AppHtml,
}),
AppHtml
是我們定義的模板檔案:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<title>Sam's Blog</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
這樣配置後,打包時這個html檔案會自動引用打包好的bundle.js
和其他檔案
2. open-browser-webpack-plugin
這個外掛允許傳入一個url
地址,作用是當webpack
打包成功後,會在瀏覽器中自動開啟傳入的地址,使用方法是:
new OpenBrowserPlugin({
url: `http://${config.host}:${config.port}`
})
3. progress-bar-webpack-plugin
打包過程顯示進度
4. clean-webpack-plugin
使用者清除生產環境下,無效的輸出檔案。
5. webpack.optimize.AggressiveMergingPlugin
在工程檔案中,經常會在不同檔案中引用同一個module
,使用該配置可以防止重複打包,減小最終檔案的大小。
啟動專案
我們的專案入口是服務端的server.js
import path from 'path';
import Express from 'express';
import favicon from 'serve-favicon'
import httpProxy from 'http-proxy';
import compression from 'compression';
import connectHistoryApiFallback from 'connect-history-api-fallback';
import config from '../config/config';
const app = new Express();
const port = config.port;
const targetUrl = `http://${config.apiHost}:${config.apiPort}`;
const proxy = httpProxy.createProxyServer({
target:targetUrl
});
app.use('/api', (req,res) => {
proxy.web(req,res,{target:targetUrl})
});
app.use('/', connectHistoryApiFallback());
app.use('/',Express.static(path.join(__dirname,"..",'build')));
app.use(compression());
app.use(favicon(path.join(__dirname,'..','public','favicon.ico')));
//熱更新
if(process.env.NODE_EVN!=='production'){
const Webpack = require('webpack');
const WebpackDevMiddleware = require('webpack-dev-middleware');
const WebpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('../webpack.dev');
const compiler = Webpack(webpackConfig);
app.use(WebpackDevMiddleware(compiler, {
publicPath: '/',
stats: {colors: true},
lazy: false,
watchOptions: {
aggregateTimeout: 300,
poll: true
},
}));
app.use(WebpackHotMiddleware(compiler));
}
app.listen(port,(err)=>{
if(err){
console.error(err)
}else{
console.log(`===>open http://${config.host}:${config.port} in a browser to view the app`);
}
});
在生產環境下,前段部分頁面的路由通過打包的檔案載入
app.use('/',Express.static(path.join(__dirname,"..",'build')));
開發環境下,通過webpack-dev-middleware
和webpack-hot-middleware
實現頁面載入和熱更新
if(process.env.NODE_EVN!=='production'){
const Webpack = require('webpack');
const WebpackDevMiddleware = require('webpack-dev-middleware');
const WebpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('../webpack.dev');
const compiler = Webpack(webpackConfig);
app.use(WebpackDevMiddleware(compiler, {
publicPath: '/',
stats: {colors: true},
lazy: false,
watchOptions: {
aggregateTimeout: 300,
poll: true
},
}));
app.use(WebpackHotMiddleware(compiler));
}
總結
到此,我們完成了webpack
打包和啟動檔案配置,結合上一篇文章的內容,目前的專案檔案地址在這裡
使用方法:
git clone https://github.com/sam408130/react-blog.git
git checkout part3
npm install
npm start
啟動後,瀏覽器自動開啟部落格的首頁:
下一節內容介紹如何使用redux
,使用redux-saga
處理非同步請求的action
,以及所有頁面的資料請求和頁面更新。
系列文章
React技術棧+Express+Mongodb實現個人部落格
React技術棧+Express+Mongodb實現個人部落格 -- Part 1 部落格頁面展示
React技術棧+Express+Mongodb實現個人部落格 -- Part 2 後臺管理頁面
React技術棧+Express+Mongodb實現個人部落格 -- Part 3 Express + Mongodb建立Server端
React技術棧+Express+Mongodb實現個人部落格 -- Part 4 使用Webpack打包部落格工程
React技術棧+Express+Mongodb實現個人部落格 -- Part 5 使用Redux
React技術棧+Express+Mongodb實現個人部落格 -- Part 6 部署
相關文章
- 個人技術部落格
- 個人技術部落格(α)
- 劉相兵個人技術部落格
- 實戰react技術棧+express前後端部落格專案(5)-- 前後端實現登入功能ReactExpress後端
- onethink開發個人技術部落格
- React構建個人部落格React
- React個人部落格開發React
- 使用React搭建個人部落格React
- 實戰react技術棧+express前後端部落格專案(4)-- 部落格首頁程式碼的編寫以及redux-saga的組織ReactExpress後端Redux
- 從零開始的個人技術部落格
- 實戰react技術棧+express前後端部落格專案(2)-- 前端react-xxx、路由配置ReactExpress後端前端路由
- 實戰react技術棧+express前後端部落格專案(0)-- 預熱一波ReactExpress後端
- 熱點部落格,技術歷程和技術積累 (個人)
- 詩意的邊緣個人部落格 - 一個走在php之路的技術員個人部落格技術網站PHP網站
- 磨刀霍霍,開啟個人技術部落格之路
- 個人部落格入門4
- react全家桶+express實戰個人部落格系列教程ReactExpress
- 實戰react技術棧+express前後端部落格專案(1)– 整體專案結構搭建ReactExpress後端
- 實戰react技術棧+express前後端部落格專案(1)-- 整體專案結構搭建ReactExpress後端
- node全棧-簡潔的個人輕部落格全棧
- React技術棧實現XX點評App-DemoReactAPP
- React製作個人部落格小結React
- [應用案例]onethink開發個人技術部落格
- 個人技術棧總結
- 技術部落格收藏
- 寶塔皮膚搭建wordpress個人技術部落格(圖文教程)
- 手把手教你用Hexo搭建個人技術部落格Hexo
- 個人技術部落格(第一次軟工總結)軟工
- 實戰react技術棧+express前後端部落格專案(6)-- 使用session實現免登陸+管理後臺許可權驗證ReactExpress後端Session
- React專案技術棧React
- React 技術棧系列教程React
- 手把手教你用React實現一個簡單的個人部落格React
- 玩遍部落格網站,我整理了全套的建站技術棧網站
- 如何寫技術部落格
- oracle、sql技術部落格OracleSQL
- Oracle技術部落格集合Oracle
- 關於個人部落格的選擇及實現
- 想寫技術部落格了