后台项目应用分享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
中定义全部页面的路由后端:
前端:
后端: