react分享

 

後臺項目應用分享javascript

後臺項目應用分享

webpack + react + redux + antdcss

策略篇

框架選擇

兼容性IE9+html

  • 組件化:React
  • 狀態管理:React Redux
  • 前端路由: React Router
  • Ajax請求: Axios
  • UI庫:Ant Design
  • 構建工具:Webpack

組件化開發

組件?組件!

(討論)前端

CSS in JS下的樣式開發思路

  • 樣式跟着組件走
button-group.js
button-group.less
  • 使用工具方法
@import (reference) "~BaseLess";
.username{
display: inline-block;
max-width: 200px;
.text-overflow() //使用單行溢出隱藏方法
}

注:在less文件中引用alias定義或node _modules下的less文件,須要在路徑前加~java

  • 使用compose實現樣式複用
/* components/Button.css */
.base { /* 全部通用的樣式 */ }

.normal {
composes: base;
/* normal 其它樣式 */
}

.disabled {
composes: base;
/* disabled 其它樣式 */
}
imp
  • 重置全局樣式
.collapsed {
//anticon類原樣輸出
:global(.anticon) {
font-size: 16px;
margin-left: 8px;
}
:global(.anticon+span),
:global(.ant-menu-submenu-vertical > .ant-menu-submenu-title:after) {
display: none;
}
}

擴展閱讀:CSS Modules 詳解及 React 中實踐node

展現組件 VS 容器組件

咱們先來看一下Redux官方文檔中的定義:react

  展現組件 容器組件
做用 描述如何展示(骨架、樣式) 描述如何運行(數據獲取、狀態更新)
直接使用 Redux
數據來源 props 監聽 Redux state
數據修改 從props調用回調函數 向 Redux 派發 actions
調用方式 手動 一般由 React Redux 生成

結合官方定義,咱們把組件分爲三個層次:webpack

  • 應用(App): 整個管理系統是一個應用(單頁模式通常只有一個應用,多頁模式可能有多個應用)
  • 容器(Container):能夠從路由訪問獲得的組件叫作容器,相似傳統開發模式中的後端頁面
  • 組件(Component):容器之外的組件都叫組件

Action/Reducer 應該和組件綁定嗎

(討論)ios

調試Redux

(演示)nginx

構建一個調試工具配置文件devtool.js

/**
* redux調試工具
*/


import React from 'react';
import { createDevTools } from 'redux-devtools';
import LogMonitor from 'redux-devtools-log-monitor';
import DockMonitor from 'redux-devtools-dock-monitor';

export default createDevTools(
<DockMonitor defaultIsVisible={false}
toggleVisibilityKey="alt-h"
changePositionKey="alt-q">

<LogMonitor />
</DockMonitor>

);

開發環境,在容器root.dev.js中引入調試工具

import React from 'react';
import DevTools from './devtools';
import Layout from 'components/layout';
import style from './style.less'

export default class Root extends React.Component {

render () {
return (
<div className={style.root}>
<Layout>{this.props.children}</Layout>
<DevTools />
</div>

)
}
}

開發環境,在store.dev.js中引入調試工具

import DevTools from 'containers/root/devtools'

export default function configureStore(initialState, reducers) {
const store = createStore(
...
compose(
...
//redux調試工具
DevTools.instrument()
)
);
...
return store
}

規範建議

命名規範

  • 變量名以駝峯方式,如:
const userInfo = {}
  • 類名以大寫字母開頭,如:
Class UserManager extend from React.Component{
...
}
  • 文件(夾)名一概小寫,如下劃線_或中槓線-做爲分隔符
  • 文件(夾)名如下劃線_區分類型,如:common_action.js,表示Action
  • 文件(夾)名以圓點.區分環境,如:root.dev.js,表示開發環境

模塊規範

  • 同一目錄下的模塊之間以相對路徑的方式引用
  • 不一樣目錄下的模塊之間以絕對路徑的方式引用
  • 經常使用模塊以alias的方式引用(推薦alias以大寫字母開頭,以區分模塊路徑引用)

思考:這樣設計的好處是什麼?

目錄劃分

build                構建工具及配置
dist 目標目錄
src 源碼目錄
mock mock數據
public 開發環境臨時目錄
node_modules npm包目錄
node_shrinkwrap npm離線包目錄

延伸閱讀:爲何要有npm離線包?

build:構建工具及配置

build/
lib/ 工具庫
*.js
shell/ 部署腳本
*.sh
webpack.config.common.js webpack公共配置
webpack.config.dev.js webpack開發環境配置
webpack.config.prod.js webpack生產環境配置
webpack.dll.config.js webpack.dllPlugin配置
config.js 構建配置

config.js示例:

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

//提取多文件共用配置、項目可定製的配置

const pkg = require('../package.json');
const src = path.resolve(__dirname, '../src');
const dist = path.resolve(__dirname, '../dist/resource')

module.exports = {

/*
* 如下配置在項目中一般不須要變更
*/


//package.json
pkg,
//源文件路徑,使用絕對路徑
src,
//導出路徑,使用絕對路徑
dist,
//靜態資源目錄,使用絕對路徑
contentBase: path.resolve(__dirname, '../public'),
//導出資源映射表路徑,使用絕對路徑
manifest: path.resolve(__dirname, '../dist/manifest'),
//資源映射表名稱,以下配置將根據當前日期生成對應的資源映射表
manifestFileName: function() {
return `${new Date().getFullYear()}-${new Date().getMonth()+1}-${new Date().getDate()}.json`
},
//dll生成路徑
dllPath: {
development: path.resolve(src, 'vendor'),
production: dist
},
//dll資源映射
dllManifest: {
development: path.resolve(src, 'vendor/dll-manifest.json'),
production: path.resolve(dist, 'dll-manifest.json')
},
//js壓縮配置
UglifyJsOptions: {
compress: {
//不輸出警告
warnings: false
},
//不輸出註釋
comments: false
},

/*
* 如下配置在項目中一般須要定製
*/


//生產環境中前端資源路徑(須要與nginx配置保持一致),能夠爲域名url
publicPath: '/Public/',
//模塊別名,相對於conf.src路徑配置
//- 推薦以大寫字母開頭,以區分非別名
alias: {
// 起別名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
// "module": "new-module",
// 起別名 "only-module" -> "new-module",但不匹配 "module/path/file" -> "new-module/path/file"
// "only-module$": "new-module",
// 起別名 "module" -> "./app/third/module.js" 和 "module/file" 會致使錯誤
// 模塊別名相對於當前上下文導入
// "module": "./app/third/module.js"
"Coms": "components/common",
"ActionTypes$": "utils/action_types.js",
"Api$": "utils/api.js",
"Helper$": "utils/helper.js",
"Ajax$": "utils/ajax.js",
"BaseLess$":"utils/baseless/baseless.less"
},
//代理配置
proxy: {
"/": {
target: "http://test-matrix-v2.yileyoo.com",
changeOrigin: true
}
},
//mock配置
mock: {
//mock目錄,使用絕對路徑
mockPath: path.resolve(__dirname, '../mock'),
//支持設置統一接口後綴,如:.do
apiExt: ''
},
//html模板配置
template: {
all:{
title:'Matrix管理平臺'
},
development:{
serverOutput:'<script src="/server/output"></script>'
},
production:{
serverOutput:'<script>window.REDUX_STATE = {!! $jsData !!};</script>'
}
},
theme: path.resolve(src, 'theme/red.less')
}

dist:存放構建結果

dist/
resource
index_xxx.html
app_xxx.js
vendor_xxx.js
app
manifest
2017-x-x.json

src:源碼目錄

src/
----------------------------------------------------
actions/ Redux Action目錄
*_action.js
reducers/ Redux Reducer目錄
*_reducer.js
store/ Redux Store目錄
index.js
store.dev.js
store.prod.js
----------------------------------------------------
routes/ React路由目錄
index.js
----------------------------------------------------
containers/ React容器目錄
root/
index.js
root.dev.js
root.prod.js
devtools.js
components/ React組件目錄
common/ React公共組件目錄(alias:Coms)
page_1/
index.js
*.js
*.less
... React頁面組件
page_n/
----------------------------------------------------
vendor/ 第三方庫目錄
vendor.js
verdor.js.map
dll-manifest.json
static/ 靜態資源目錄
favicon.ico
utils/ 工具目錄
ajax.js ajax工具(alias:Ajax)
api.js api工具(alias:Api)
action_types.js action類型工具(alias:ActionTypes)
helper.js 經常使用工具方法(alias:Helper)
baseless/*.less 經常使用less方法(alias:BaseLess)
theme/ 主題目錄
*.less
----------------------------------------------------
config/ 配置目錄
*_config.js
----------------------------------------------------

mock:存放mock數據的目錄

mock/
*.js
*.json

public:開發環境臨時目錄

public/
index.html

npm package:npm包

node_modules                npm包目錄
node_shrinkwrap npm離線包目錄

other files:根目錄下其餘文件

.gitignore                  git忽略配置
.gitlab-ci.yml gitlab-ci配置
npm-shrinkwrap.json npm包版本鎖定配置
package.json npm包配置
webpack.config.js webpack配置入口
README.md 說明文檔

UI篇

如何引入ant

// .babelrc or babel-loader option
{
"plugins": [
["import", { libraryName: "antd", style: "css" }] // `style: true` 會加載 less 文件
]
}

而後只需從 antd 引入模塊便可,無需單獨引入樣式。等同於下面手動引入的方式。

// babel-plugin-import 會幫助你加載 JS 和 CSS
import { DatePicker } from 'antd';

如何定製antd

antd經過less變量提供了較靈活的主題定製功能。(參考:修改 Ant Design 的樣式變量

須要修改babel-loader配置:

{
"plugins": [
["import", {
libraryName: "antd",
style: true // 這裏須要修改成`style: true`以實現主題配置
}]
]
}

這裏咱們作了簡單的封裝:

1)在src/theme目錄建一個主題文件,如:red.less(參考:antd默認主題文件

@primary-color: #f00;

2)在build/config.js文件配置主題文件的路徑

{
...
theme: path.resolve(src, 'theme/red.less')
}

後續咱們還將推出主題配置監聽多主題切換等功能,敬請期待~

動手寫一個antd組件

任意在項目中可複用的組件,均可以經過antd組件組合成一個通用(業務)組件。
注意,這裏的組件是Component(見前面的定義)類型。

(演示)

工具篇

devServer什麼鬼

  • 一個node express應用
  • 提供靜態資源服務
  • 支持文件監聽
  • 支持熱加載
  • 支持路由代理
  • 支持接口轉發

調用方式很是簡單,在命令行執行:

webpack-dev-server --env development --port 3000 --hot --inline --progress --open

webpack中的相關配置:

//開發服務器配置
devServer: {
//告訴服務器從哪裏提供內容。只有在你想要提供靜態文件時才須要。
//devServer.publicPath 將用於肯定應該從哪裏提供 bundle,而且此選項優先。
contentBase: [
conf.contentBase,
path.join(conf.src, 'static'),
path.join(conf.src, 'vendor')
],
//信息顯示配置
stats: "normal",
//是否顯示全屏遮罩
overlay: true,
//watch配置
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 100
},
//聯調模式下,使用數據代理
proxy: isDebug ? conf.proxy : {},
//開啓瀏覽器歷史
historyApiFallback: true,
//擴展devServer
setup(app) {
//非聯調模式下,使用mock數據
!isDebug && makeMock(app, conf.mock)
}
}

不容忽視的HtmlWebpackPlugin

  • 自動引用webpack生成的資源,支持過濾
  • 默認支持ejs模板,能夠注入變量,語法親和
  • 社區生態好,一衆擴展插件能夠知足各類需求(參考

dllPlugin教程沒有教你

教程不少,隨便找一篇:怎樣令webpack的構建加快十倍、DllPlugin的用法

不實用!!!

get√到打開方式後,咱們來針對實際狀況作個總結:

  • 區分開發和生產環境
  開發環境 生產環境
警告信息
內容壓縮
文件hash
SourceMap 可選
  • 怎麼在頁面引用

使用add-asset-html-webpack-plugin插件

{
plugins:[
...
// 在入口頁面中引入靜態資源
new AddAssetHtmlPlugin({
//經過dllManifest讀取dll文件名
filepath: path.resolve(conf.dllPath[NODE_ENV], `${dllManifest.name}.js`)
})
]
}
  • 加一個庫就必須手寫一下entry不須要!!!
entry: {
//讀取package.json中的依賴
vendor: Object.keys(conf.pkg.dependencies)
}
  • 完整的webpack.dll.config.js
const path = require('path');
const webpack = require('webpack');
const conf = require('./config');

module.exports = function( /*經過命令行參數--env傳入*/ NODE_ENV) {
//是否生產環境
const isProd = NODE_ENV === 'production';
//文件名(不帶後綴)
const name = `[name]${isProd?"_[chunkhash:8]":""}`;
//輸出文件路徑
const filePath = conf.dllPath[NODE_ENV];
//輸出manifest路徑
const manifest = conf.dllManifest[NODE_ENV];
//sourcemap配置
const devtool = isProd ? '' : 'source-map';
//插件
let plugins = [
new webpack.DllPlugin({
//解析包路徑的上下文,這個要跟接下來配置的 webpack.config.js 一致。
context: __dirname,
//manifest.json文件的輸出路徑,這個文件會用於後續的業務代碼打包
path: manifest,
//dll暴露的對象名,要跟output.library保持一致
name: name
})
];
//生產環境使用壓縮版
if (isProd) {
plugins = plugins.concat([
//變量定義
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(NODE_ENV)
}),
// js壓縮配置
new webpack.optimize.UglifyJsPlugin(conf.UglifyJsOptions)
])
}
return {
entry: {
//讀取package.json中的依賴
vendor: Object.keys(conf.pkg.dependencies)
},
output: {
path: filePath,
filename: name + '.js',
//須要與filename保持一致,用於頁面引用
library: name
},
devtool,
plugins
}
};

擴展閱讀:webpack 構建性能優化策略小結
思考:相比其餘方案,dllPlugin的優點在哪裏?

SourceMap全方案

  • loader中啓用SourceMap
{
loader: 'xxx-loader',
options: {
...
sourceMap: true
}
}
  • devtools中配置SourceMap類型

開發環境:惟快不破,最大化知足調試需求

{   
...
devtool: 'cheap-module-eval-source-map'
}

生產環境:須要考慮安全性和性能

{    
...
devtool: 'source-map'
}

擴展閱讀:Webpack devtool source map

拆分配置文件

webpack.config.common.js       公共配置
webpack.config.dev.js 開發環境配置
webpack.config.prod.js 生產環境配置

拆分原則:

  • moduleresolve在開發和生產環境中的配置差別性相對較小,很是適合抽取到公共配置中
  • entryoutputplugins 相對來講開發和生產環境有不一樣的配置,所以放到devprod各自配置中
  • devtooldevServer 等僅出如今開發環境的配置直接放入dev配置中

協同篇

承載頁

純靜態 vs 服務端渲染

  • 純靜態:服務端僅提供數據接口,完全不須要後端維護,缺點是沒法作資源回溯和切換
  • 服務端渲染:服務端提供數據接口,並將部分數據或狀態渲染到頁面,缺點是耦合了先後端部署邏輯

對服務端渲染的改進:

  • 將後端模板指向前端部署目錄的html文件,如: index.html
  • 使用固定的後端模板,將數據或狀態以JSON對象的方式輸出
  • 使用HtmlWebpackPlugin,將後端模板注入到自動生成的頁面中

build/config中的配置:

//html模板配置
template: {
all:{
title:'Matrix管理平臺'
},
development:{
serverOutput:'<script src="/server/output"></script>'
},
production:{
serverOutput:'<script>window.REDUX_STATE = {!! $jsData !!};</script>'
}
}

webpack中的配置:

{
plugins:[
...
// 根據模板建立入口頁面
new HtmlWebpackPlugin(Object.assign({
template: path.resolve(conf.src, 'index.ejs'),
filename: path.resolve(conf.contentBase, 'index.html')
}, /*全環境模板配置*/conf.template.all, /*當前環境模板配置*/conf.template[NODE_ENV]))
]
}

index.ejs模板中引用

<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
</head>

<body>
<div id="root" style="height: 100%"></div>
<%= htmlWebpackPlugin.options.serverOutput %>
</body>

</html>

資源灰度 & 回溯

使用 assets-webpack-plugin插件,在webpack中生成資源映射json文件

{
plugins:[
...
//生成資源映射表
new AssetsPlugin({
path: conf.manifest,
filename: conf.manifestFileName(),
processOutput: function (assets) {
//注入dll依賴信息
assets.vendor = {
'js': conf.publicPath + dllManifest.name + '.js'
};
return JSON.stringify(assets);
}
})
]
}

兩種方案:

  • 同一個html模板,僅切換資源。需提供資源映射表
  • 不一樣html模板,切換模板。須要提供模板映射表

數據接口

文檔

——接口定義,告訴咱們有哪些接口,接口支持哪些http方法,每一個接口字段的含義是什麼等

(演示)

mock

——接口沒有開發完成,前端根據接口文檔模擬的數據

(演示)

proxy

——接口已經開發完成,使用代理的方式實現本地接口聯調

webpack中的相關配置:

devServer:{
...
proxy: {
"/api": "http://localhost:3000"
}
}

權限控制

頁面權限

前端:

  • react-router中定義全部頁面的路由
  • 頁面初始化時,請求後端接口獲取菜單權限
  • 僅顯示具有權限的菜單

後端:

  • 提供獲取菜單權限的接口
  • 將接受到的路由請求做過濾,具有權限的則轉向前端頁面
  • 將404/500等錯誤路由轉到前端頁面

操做權限

前端:

  • 請求後端接口獲取操做權限
  • 根據操做權限控制操做按鈕是否顯示

後端:

  • 提供獲取操做權限的接口
相關文章
相關標籤/搜索