後臺項目應用分享javascript
webpack + react + redux + antdcss
兼容性IE9+html
(討論)前端
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
咱們先來看一下Redux
官方文檔中的定義:react
展現組件 | 容器組件 | |
---|---|---|
做用 | 描述如何展示(骨架、樣式) | 描述如何運行(數據獲取、狀態更新) |
直接使用 Redux | 否 | 是 |
數據來源 | props | 監聽 Redux state |
數據修改 | 從props調用回調函數 | 向 Redux 派發 actions |
調用方式 | 手動 | 一般由 React Redux 生成 |
結合官方定義,咱們把組件分爲三個層次:webpack
(討論)ios
(演示)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 說明文檔
// .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
經過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組件組合成一個通用(業務)組件。
注意,這裏的組件是Component
(見前面的定義)類型。
(演示)
調用方式很是簡單,在命令行執行:
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)
}
}
ejs
模板,能夠注入變量,語法親和教程不少,隨便找一篇:怎樣令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的優點在哪裏?
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 生產環境配置
拆分原則:
module
和 resolve
在開發和生產環境中的配置差別性相對較小,很是適合抽取到公共配置中entry
、output
和plugins
相對來講開發和生產環境有不一樣的配置,所以放到dev
和prod
各自配置中devtool
和 devServer
等僅出如今開發環境的配置直接放入dev
配置中對服務端渲染的改進:
index.html
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);
}
})
]
}
兩種方案:
——接口定義,告訴咱們有哪些接口,接口支持哪些http方法,每一個接口字段的含義是什麼等
(演示)
——接口沒有開發完成,前端根據接口文檔模擬的數據
(演示)
——接口已經開發完成,使用代理的方式實現本地接口聯調
webpack中的相關配置:
devServer:{
...
proxy: {
"/api": "http://localhost:3000"
}
}
前端:
react-router
中定義全部頁面的路由後端:
前端:
後端: