React如今已經有不少腳手架工具,如create-react-app,支持一鍵建立一個React應用項目結構,很方便,可是享受方便的同時,也失去了對項目架構及技術棧完整學習的機會,並且一般腳手架建立的應用技術架構並不能徹底知足咱們的業務需求,須要咱們本身修改,完善,因此若是但願對項目架構有更深掌控,最好仍是從0到1理解一個項目。css
此次應用架構設計不使用任何腳手架,須要本身建立每個文件,引入每個技術和三方庫,最終造成完整的應用,包括選擇的完整技術棧。 項目結構圖以下:1. src爲應用源代碼目錄; 2. webpack.config.js爲webpack配置入口文件; 3. package.json爲項目依賴管理文件; 4. .babelrc文件,babel的配置文件,使用babel編譯React和JavaScript代碼; 5.README.md爲項目說明文檔; 6.bird-config是gulp-bird轉發工具的配置文件;7.postcss.config.js是postcss的配置文件html
Featurenode
對整個技術棧進行分析,要考慮哪幾個方面呢?react
根據以上劃分決定選用如下第三方庫和工具構成項目的完整技術棧:jquery
React組件化開發原則是組件負責渲染UI,組件不一樣狀態對應不一樣UI,一般遵循如下組件設計思路:webpack
以本項目section的組件劃分爲例,index.js是入口文件,同時也承擔了路由組件的角色,layout是佈局組件,僅僅負責UI界面結構,homeindex是容器組件,是首頁的業務邏輯,islider則是輪播圖的獨立組件,可複用。 ios
git init
touch README
git add README
git commit -m'first commit'
git remote add origin git@XXXXX.git
git push origin master
複製代碼
採用npm install XXX -save
或npm install XXX -save-dev
配置安裝相關npm包,若是直接複製,能夠直接npm install
安裝git
{
"name": "zhiqiu",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "./node_modules/.bin/webpack --config webpack.config.js --env.NODE_ENV=production",
"dev": "./node_modules/.bin/webpack --config webpack.config.js --env.NODE_ENV=development",
"qa": "./node_modules/.bin/webpack --config webpack.config.js --env.NODE_ENV=qa",
"start": "./node_modules/.bin/webpack-dev-server --config webpack.config.js --env.NODE_ENV=local"
},
"proxy": {
"/mansion": {
"target": "http://localhost:8009"
}
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"axios": "^0.18.0",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"classnames": "^2.2.5",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.2.0",
"islider": "^0.1.0",
"mobx": "^4.1.0",
"mobx-react": "^5.0.0",
"node-sass": "^4.8.3",
"path": "^0.12.7",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"sass-loader": "^6.0.7",
"style-loader": "^0.20.3",
"url-loader": "^1.0.1",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-dev-server": "^2.11.2"
},
"dependencies": {
"autoprefixer": "^8.6.4",
"babel-plugin-lodash": "^3.3.4",
"gulp-bird": "^0.2.4",
"hi-ui": "0.0.9",
"islider.js": "^2.2.2",
"jquery": "^3.3.1",
"lodash": "^4.17.10",
"postcss-loader": "^2.1.5",
"vconsole": "^3.2.0"
}
}
複製代碼
路由安裝mobx-react,react-router模塊; React Router是完整的React路由解決方案,也是開發React應用最常使用的路由管理庫,它提供簡單的API,以聲明式方式實現強大的路由功能,諸如按需加載,動態路由等。 1.聲明式:語法簡潔,清晰; 2.按需加載:延遲加載,根據使用須要判斷是否須要加載; 3.動態路由:動態組合應用路由結構,更靈活,更符合組件化開發模式;github
/**
* @file index 入口頁面
* @author guoyueting
*/
import React from 'react';
import {render} from 'react-dom';
import {HashRouter, Route, Redirect} from 'react-router-dom';
import {Provider} from 'mobx-react';
import storeTree from './storeTree';
import 'section/common/scss/common.scss';
import Layout from 'section/layout/layout.js';
class App extends React.Component {
render() {
return (
<Provider {...storeTree} >
<HashRouter>
<Route path="/" component={Layout}/>
</HashRouter>
</Provider>
);
}
}
render(<App/>, document.getElementById('app'));
複製代碼
數據狀態管理,安裝mobx,肯定storetreeweb
/**
* @file storeTree 整個app的狀態樹
* @author guoyueting
*/
'use strict';
import {observable, action, extendObservable, runInAction} from 'mobx';
import NavState from 'section/nav/navStore';
import iSliderState from 'section/homeindex/component/islider/isliderStore';
import hotPointState from 'section/homeindex/component/hotpoint/hotpointStore';
import recommendState from 'section/homeindex/component/recommend/recommendStore';
import serviceState from 'section/service/serviceStore';
import repairState from 'section/myrepair/myrepairStore';
class AppState {
// 全局store
// ...
}
export default {
AppState: new AppState(),
NavState: new NavState(),
iSliderState: new iSliderState(),
hotPointState: new hotPointState(),
recommendState: new recommendState(),
serviceState: new serviceState(),
repairState: new repairState()
}
複製代碼
@observer 函數/修飾器用於react組件。經過mobx-react依賴包來提供。它經過mobx.autorun來包裝了組件的render函數,以確保組件的render函數在任何數據的更改是強制從新渲染。
Autorun是用在一些你想要產生一個不用觀察者參與的被動調用函數裏面。當autorun被使用的時候,一旦依賴項發生變化,autorun提供的函數就會被執行。
action是任何改變狀態的事物。
/**
* @file serviceStore
* @author guoyueting
*/
'use strict';
import {observable, action, runInAction} from 'mobx';
import _ from 'lodash';
import * as model from 'src/itsm/model/model';
import {getSearchParam} from 'section/common/js/utils';
export default class serviceState {
@observable serviceList = [];
@action getServiceList() {
let params = {
mappedAppKey: getSearchParam('appKey')
};
model.getServiceListList(params).then(data => {
runInAction(()=>{
this.serviceList = _.get(data, 'data.data');
});
});
}
@action serviceClick(id) {
let params = {
mappedAppKey: getSearchParam('appKey'),
id: id
};
model.serviceClick(params);
}
}
複製代碼
安裝axios,並經過get/post/put/delete等方式請求接口,對於json數據,通常在post請求進行數據格式轉換,並在請求頭部設置:
/**
* @file 接口文件
* @author guoyueting
*/
import axios from 'axios';
let axiosConfig = {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Origin': '*'
}
};
// 獲取列表
export let getHotList = function (data) {
return axios.post('/rdwtv2/api/hot', JSON.stringify(data), axiosConfig);
};
複製代碼
gulp-bird是由百度BEFE團隊開發的代理轉發工具,並非一個gulp插件,在bird基礎上進行了一些優化併發布到了npm,配置方法和bird同樣。 配置目標服務器host和port等,參考以下。
/**
* @file bird-config.js
* @author guoyueting
*/
var bird = require('./node_modules/gulp-bird/index');
// 靜態服務器配置,可同時配置多個,域名需host到127.0.0.1
var server = {
'8009': {
// 靜態文件根目錄
'basePath': './src/',
// 是否開啓調試模式,true(表示server端不緩存),false(反之)
'debug': true
// 忽略的靜態文件請求,與此正則匹配的請求將直接走轉發規則(可選配置)
// 'ignoreRegExp': /\/js\/urls\.js/g
}
};
// 轉發規則——靜態服務器沒有響應的或者忽略的請求將根據一下規則轉發
var transpondRules = {
'8009': {
// 目標服務器的ip和端口,域名也可,但注意不要被host了
targetServer: {
'port': '8680',
// 'port': '8080',
'host': 'http://m1-ite-hidev04.m1.baidu.com',
// 'host': 'cp01-ps-dev373-liuchao31.epc.baidu.com',
// 當爲true時,若是cookie or header中有相同key,則替換
'replaceHeaders': true,
'headers': {
'cookie': ''}
}
// 特殊請求轉發,可選配置,內部的host、port和attachHeaders爲可選參數
// regExpPath: {
// '/oa-frontend-apply-1.0.0-SNAPSHOT': {
// 'host': 'cp01-dev-heliping.epc.baidu.com/',
// 'port': '8080',
// //'attachHeaders': {'app-id': 5},
// 'path': '\/'
// }
// }
},
'ajaxOnly': false
};
var toolsConf = {
weinre: {
// 和移動調試工具條中的vconsole衝突, 當爲true時vconsole自動關閉
open: false,
port: 8009
},
// 移動端調試工具條,PC端開發可關閉
showTools: false
};
bird.start(server, transpondRules, toolsConf);
複製代碼
下面是一個較爲基礎的webpack文件配置。entry是配置模塊的入口,webpack執行構建的第一步將從入口開始搜尋及遞歸解析出全部入口依賴的模塊;output定義了打包後的輸出文件名、路徑;module配置模塊的讀取和解析規則,一般用來配置loader;resolve配置Webpack如何尋找模塊對應的文件;Plugins用於擴展webpack的功能,幾乎全部Webpack沒法直接實現的功能都能在社區找到開源的Plugin去解決。
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const path = require('path');
const webpack = require('webpack');
module.exports = function(env) {
console.log(env);
return {
entry: {
'index': './src/itsm/index.js'
},
output: {
filename: 'js/[name]-[chunkhash].js',
path: path.resolve(__dirname, './build'),
chunkFilename: '[name].[chunkhash:4].child.js'
},
module: {
rules: [{
test: /(\.jsx|\.js)$/i,
use: [{
loader: 'babel-loader'
}]
// exclude: /node_modules/
},
{
test: /(\.scss|\.sass)$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
module: false,
minimize: true
}
}, {
loader: 'postcss-loader',
}, {
loader: 'sass-loader',
options: {
sourceMap: false
}
}]
})
},
{
test: /(\.png|\.jpg|\.jpeg|\.gif)$/i,
use: [{
loader: 'url-loader',
options: {
limit: 100
}
}]
}]
},
resolve: {
alias: {
'src': path.resolve(__dirname, 'src'),
'section': path.resolve(__dirname, 'src/itsm/section'),
'img': path.resolve(__dirname, 'src/itsm/img'),
'modules': path.resolve(__dirname, 'node_modules/islider.js/build')
}
},
plugins: [
new CleanWebpackPlugin(['./build']),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || 'development'),
'isDev': JSON.stringify(env.isDev || 'true')
}),
new ExtractTextPlugin('style.css'),
new HtmlWebpackPlugin({
template: './src/itsm/index.html',
filename: './index.html',
chunks: ['index']
})
],
devServer: {
host: '0.0.0.0',
port: 9822,
proxy: {
'/rdwtv2': 'http://localhost:8009'
}
}
}
}
複製代碼
配置不一樣環境的變量值,生產環境,QA環境,開發環境某些變量可能須要根據環境進行配置,而後用webpack的definePlugin插件,定義全局變量,能夠保證在和環境有關的變量取值的正確性。
"scripts": {
"build": "./node_modules/.bin/webpack --config webpack.config.js --env.NODE_ENV=production",
"dev": "./node_modules/.bin/webpack --config webpack.config.js --env.NODE_ENV=development",
"qa": "./node_modules/.bin/webpack --config webpack.config.js --env.NODE_ENV=qa",
"start": "./node_modules/.bin/webpack-dev-server --config webpack.config.js --env.NODE_ENV=local"
}
複製代碼
將來架構設計還須要完善的點:
最後,附上demo地址:https://github.com/guoyueting/react-mobx-seed
原文地址:https://guoyueting.github.io/2018/07/01/React%E8%84%9A%E6%89%8B%E6%9E%B6/