好久之前搭建的一个react执行环境,受历史影响是webpack3.10.0和webpack-dev-server2.7.1的环境,新项目准备用webpack4重新弄弄了,旧的记录就合并发布了(在没有快速目录的地方写的,之前是分为了10个文件)

所使用的一些包可能进过不断的迭代升级已不支持

01.初始化项目(安装需要的包)

//生成package.json
npm init

安装基础包

npm install react react-dom --save
npm install webpack@3.10.0 webpack-dev-server@2.7.1 --save-dev <!--webpack和webpack-dev-server同时全局安装一下-->
npm install webpack@3.10.0 wepack-dev-server@2.7.1 -g

webpack-dev-server@2.7.3是最后兼容ie9的版本,升级之后会导致打包的文件在ie9下运行报错

安装和配置babel

npm install
babel-core 核心功能
babel-loader
babel-polyfill 转换低版本代码
babel-preset-env 解析Es6
babel-preset-react 解析JSX
--save-dev

安装loader包

npm install
css-loader
style-loader
file-loader
url-loader
image-webpack-loader
html-loader
--save-dev
  • --save 会把依赖包名称添加到 package.json 文件 dependencies

dependencies是运行时依赖,是我们发布后还需要依赖的模块

  • --save-dev 则添加到 package.json 文件 devDependencies

devDependencies是开发时的依赖,在发布后用不到它,而只是在我们开发才用到它

当npm install时,会下载dependencies和devDependencies中的模块,当使用npm install –production或者注明NODE_ENV变量值为production时,只会下载dependencies中的模块

02.执行程序,创建文件

创建/.babelrc

//.babelrc
{
"presets": [
"react",
"env"
]
}

创建webpack.config.js

const path = require('path');

module.exports = {
context: path.resolve(__dirname, 'app'),
resolve: {
extensions: ['.js', '.jsx'], //后缀名自动补全,可以使用jsx的文件后缀
modules: [
path.resolve(__dirname, 'node_modules')
]
},
entry: './app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react'],
}
}
},
{
test: /index\.html/,
use: [{
loader: 'file-loader',
options: {
name (file) {
return 'index.[ext]'
}
}
}]
}
]
},
};

创建/app/component/App.js

import React from 'react';
import Header from './Header';
import Content from './Content';
import Sidebar from './Sidebar'; class App extends React.Component {
render() {
return (
<div className="app">
<Header/>
<Sidebar/>
<Content/>
</div>
)
}
}
export default App;

创建/app/component/Header.js

import React from 'react';

class Header extends React.Component {
render() {
return (
<div>this is header</div>
)
}
}
export default Header;

创建/app/component/Sidebar.js

import React from 'react';

class Sidebar extends React.Component {
render() {
return (
<div className="sidebar">
this is sidebar
</div>
)
}
}
export default Sidebar;

创建/app/component/Content.js

import React from 'react';

class Content extends React.Component {
render() {
return (
<div className="content">
this is content
</div>
)
}
}
export default Content;

创建/app/app.js入口文件

import './index.html';
import React from 'react';
import ReactDom from 'react-dom';
import App from './component/App'; ReactDom.render(
<App/>, document.getElementById('root')
);

创建/app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<meta charset="UTF-8">
<title>hello world</title>
</head>
<body>
<div id="root"></div>
<script src="./app.js"></script>
</body>
</html>

执行webpack打包

<!--控制台执行webpack会在/dist中生成一个app.js和index.html-->
webpack

开启本地服务器

修改webpack.config.js

//webpack-dev-server配置
devServer: {
contentBase: './dist',
historyApiFallback: true,
inline: true,//源文件改变,会自动刷新页面
port: 1234,//设置默认监听端口,如果省略,默认为"8080"
},

修改package.json

"scripts": {
"start": "webpack-dev-server"
},

执行start

npm start

03.添加css样式

新建/style/app.css

html, body, #root, .app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
} div {
box-sizing: border-box;
} .header {
position: fixed;
top: 0;
left: 150px;
right: 0;
padding: 0 30px;
height: 61px;
background: #ebebeb; } .title {
width: 90%;
margin: 0 5%;
height: 80px;
line-height: 80px;
color: #fff;
font-size: 20px;
text-align: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
} .sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
width: 150px;
background: #3d3c4a; } .header-con {
position: relative;
width: 100%;
height: 60px;
} .user {
position: absolute;
right: 0;
top: 15px;
height: 30px;
line-height: 30px;
color: gray;
} .text {
float: left;
height: 30px;
} .avatar {
float: right;
width: 30px;
height: 30px; } .avatar img {
width: 100%;
height: 100%;
border-radius: 90%;
} .content {
position: fixed;
top: 60px;
left: 150px;
right: 0;
bottom: 0;
padding: 30px;
color: gray;
}

修改/app/component/Header.js

import React from 'react';

class Header extends React.Component {
render() {
return (
<div className="header">
<div className="header-con">
<div className="user">
欢迎您!
</div>
</div>
</div>
)
}
}
export default Header;

修改/app/component/Sidebar.js

import React from 'react';

class Sidebar extends React.Component {
render() {
return (
<div className="sidebar">
<div className="title">
easterCat
</div>
</div>
)
}
}
export default Sidebar;

修改/app/component/App.js

...
import '../style/app.css';

添加css依赖/webpack.config.js

    module: {
rules: [
{
test: /\.css$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}],
},
]
},

打包运行

webpack

此时的css打包是自动添加到index的style标签里面

04.添加图片的依赖

创建/app/images/avatar.jpg

修改/app/component/Header.js

import React from 'react';
import Avatar from '../images/avatar.jpg'; class Header extends React.Component {
render() {
return (
<div className="header">
<div className="header-con">
<div className="user">
<span className="text">欢迎您!</span>
<span className="avatar">
<img src={Avatar} alt="" />
</span>
</div>
</div>
</div>
)
}
}
export default Header;

添加图片依赖/webpack.config.js

    module: {
rules: [
{
test: /.*\.(gif|png|jpe?g|svg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
name: 'images/[name].[ext]'
}
}, {
loader: 'image-webpack-loader'
}]
},
]
},

05.添加预编译语言-sass

<!--less和sass都用,此处用sass-->
npm install sass-loader node-sass --save-dev

修改webpack.config.js

module: {
rules:[{
test: /\.scss$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader',
options: {
sourceMap: true
}
}, {
loader: 'sass-loader',
options: {
sourceMap: true,
outputStyle: 'expanded',
sourceMapContents: true
}
}]
},]
},

添加app.scss

@import 'body';
@import 'header';
@import 'sidebar';
@import 'content';

将app.css拆分成body.scss/header.scss/sidebar.scss/content.scss

<!--例:header.scss-->
.header {
position: fixed;
top: 0;
left: 150px;
right: 0;
padding: 0 30px;
height: 61px;
background: #ebebeb;
.user {
position: absolute;
right: 0;
top: 15px;
height: 30px;
line-height: 30px;
color: gray;
}
.header-con {
position: relative;
width: 100%;
height: 60px;
}
.text {
float: left;
height: 30px;
} .avatar {
float: right;
width: 30px;
height: 30px;
img {
width: 100%;
height: 100%;
border-radius: 90%;
}
}
}

修改/app/component/app.js

import '../style/app.css';

<!--修改为-->
import '../style/app.scss';

重新打包

webpack

安装第二个预编译语言less

npm install less less-loader --save-dev

修改webpack.config.js

module: {
rules: [
{
test: /\.less$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader',
options: {
sourceMap: true
}
}, {
loader: 'less-loader',
options: {
sourceMap: true,
outputStyle: 'expanded',
sourceMapContents: true,
}
}]
},
]
},

06.添加一个ui框架antd-design

antd-design

npm install antd --save

<!--babel-plugin-import必须要装-->
npm install babel-plugin-import --save-dev

引入antd样式

<!--在App.js中引入-->
import 'antd/dist/antd.css' 或 import 'antd/dist/antd.less' <!--在app.scss中引入-->
@import "~antd/dist/antd.css";

使用组件,修改content.js

import React from 'react';
import Table from 'antd/lib/Table'; class Content extends React.Component {
render() {
const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '电话号码',
dataIndex: 'number',
key: 'number',
}, {
title: '邮箱',
dataIndex: 'email',
key: 'email',
}]; const data = [];
for (let i = 1; i < 15; i++) {
let obj = {
name: 'doudou',
age: 32,
number: 123456789,
email: '123456789@163.com',
};
obj.key = i;
data.push(obj);
} return (
<div className="content">
<Table columns={columns} dataSource={data}/>
</div>
)
}
}
export default Content;

babel-plugin-import插件

从 antd 引入模块即可,无需引入样式,babel-plugin-import 会加载 JS 和 CSS

修改.babelrc

//.babelrc
{
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": true //引入less,如果使用css文件就改为'css'
}
]
]
}

修改/app/component/Content.js

import {Table} from 'antd';

此时app.scss和App.js中引入的antd.css可以去掉了

问题

如果在编译antd的样式文件报错,更换一个less,当前是@2.7.2

使用antd-design

更改/app/component/App.js

import React from 'react';
import Header from './layout/Header';
import Content from './layout/Content';
import Sidebar from './layout/Sidebar';
import '../style/app.scss';
import {Layout} from 'antd'; class App extends React.Component {
constructor() {
super();
this.state = {
collapsed: false
}; this.toggle = () => {
this.setState({
collapsed: !this.state.collapsed
})
};
} render() {
return (
<Layout className="layout-app">
<Layout.Sider
trigger={null}
collapsible
collapsed={this.state.collapsed}
>
<Sidebar/>
</Layout.Sider>
<Layout>
<Layout.Header style={{background: '#fff', padding: 0}}>
<Header collapsed={this.state.collapsed}
toggle={this.toggle}
/>
</Layout.Header>
<Layout.Content style={{margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280}}>
<Content/>
</Layout.Content>
</Layout>
</Layout>
)
}
}
export default App;

修改/app/component/layout/Header.js

import React from 'react';
import avatar_img from '../../images/avatar.jpg';
import {Icon, Avatar} from 'antd'; class Header extends React.Component {
constructor(props) {
super(props);
} render() {
const {
collapsed,
toggle
} = this.props; return (
<div className="layout-header">
<Icon
className="trigger"
type={collapsed ? 'menu-unfold' : 'menu-fold'}
onClick={toggle}
/>
<Avatar className="avatar"
src={avatar_img}/>
</div>
)
}
}
export default Header;

修改/app/component/layout/Sidebar.js

import React from 'react';
import {Menu, Icon} from 'antd'; class Sidebar extends React.Component {
constructor(props) {
super(props);
} render() {
return (
<div className="layout-sidebar">
<div className="logo"/>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1">
<Icon type="user"/>
<span>nav 1</span>
</Menu.Item>
<Menu.Item key="2">
<Icon type="video-camera"/>
<span>nav 2</span>
</Menu.Item>
<Menu.Item key="3">
<Icon type="upload"/>
<span>nav 3</span>
</Menu.Item>
</Menu>
</div>
)
}
}
export default Sidebar;

修改/app/component/layout/Content.js

import React from 'react';
import {Table} from 'antd'; class Content extends React.Component {
render() {
const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '电话号码',
dataIndex: 'number',
key: 'number',
}, {
title: '邮箱',
dataIndex: 'email',
key: 'email',
}]; const data = [];
for (let i = 1; i < 15; i++) {
let obj = {
name: 'doudou',
age: 32,
number: 123456789,
email: '123456789@163.com',
};
obj.key = i;
data.push(obj);
} return (
<Table columns={columns} dataSource={data}/>
)
}
}
export default Content;

新建layout.scss

.layout-app {
height: 100%;
} .layout-sidebar {
.logo {
height: 32px;
background: #333;
border-radius: 6px;
margin: 16px;
}
} .layout-header {
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 16px;
cursor: pointer;
transition: color .3s;
&:hover {
color: #108ee9;
}
}
.avatar {
float: right;
margin-top: 16px;
margin-right: 16px;
cursor: pointer;
}
}

07.引入路由react-router

安装react-router

npm install react-router-dom --save

修改/app/app.js

import './index.html';
import './styles/app.less';
import React from 'react';
import ReactDom from 'react-dom';
import App from './components/App.jsx';
import {BrowserRouter, Route} from 'react-router-dom'; ReactDom.render(
<BrowserRouter>
<Route path="/" component={App}/>
</BrowserRouter>
,
document.getElementById('root')
);

修改/app/component/App.js

import React from 'react';
import Home from './Home';
import Login from './Login';
import {Route} from 'react-router-dom'; class App extends React.Component {
componentDidMount() {
const {
location,
history
} = this.props;
if (location.pathname === '/home' || location.pathname === '/') {
history.replace('/home')
} else if (location.pathname === '/login') {
history.replace('/login')
}
} render() {
return (
<div className="app">
<Route path="/home" component={Home}/>
<Route path="/login" component={Login}/>
</div>
)
}
} export default App;

添加/app/component/home/Home.js

import React from 'react';
import {matchPath} from 'react-router-dom';
import Header from './Header';
import Content from './Content';
import Sidebar from './Sidebar';
import {Layout} from 'antd'; export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
collapsed: false
};
this.ress = ['content01', 'content02', 'content03'];
this.res = null;
const match = matchPath(this.props.history.location.pathname, {
path: '/home/:res'
});
if (match) {
this.res = match.params.res;
} this.toggle = () => {
this.setState({
collapsed: !this.state.collapsed
})
};
} componentWillMount() {
if (!this.res || !this.res.length || this.ress.indexOf(this.res) === -1) {
this.props.history.replace(`/home/content01`)
}
} render() {
return (
<Layout className="layout-app">
<Layout.Sider
trigger={null}
collapsible
collapsed={this.state.collapsed}
>
<Sidebar res= {this.res}/>
</Layout.Sider>
<Layout>
<Layout.Header style={{background: '#fff', padding: 0}}>
<Header collapsed={this.state.collapsed}
toggle={this.toggle}
/>
</Layout.Header>
<Layout.Content style={{margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280}}>
<Content/>
</Layout.Content>
</Layout>
</Layout>
)
}
}

添加/app/component/login/Login.js

import React from 'react';
import {Form, Icon, Input, Button, Checkbox} from 'antd';
const FormItem = Form.Item; class Logining extends React.Component {
constructor(props) {
super(props); this.handleSubmit = (e) => {
const {
history
} = this.props; e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
if (values.userName === 'admin' && values.password === '123456') {
history.replace('/home')
}
console.log('Received values of form: ', values);
}
});
}
} render() {
const {
getFieldDecorator
} = this.props.form; return (
<div className="login">
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{required: true, message: 'Please input your username!'}],
})(
<Input prefix={<Icon type="user" style={{fontSize: 13}}/>} placeholder="Username"/>
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{required: true, message: 'Please input your Password!'}],
})(
<Input prefix={<Icon type="lock" style={{fontSize: 13}}/>} type="password"
placeholder="Password"/>
)}
</FormItem>
<FormItem>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(
<Checkbox>Remember me</Checkbox>
)}
<a className="login-form-forgot" href="">Forgot password</a>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
Or <a href="">register now!</a>
</FormItem>
</Form>
</div>
);
}
} const Login = Form.create()(Logining); export default Login;

修改/app/component/layout/Sidebar.js

import React from 'react';
import {Menu, Icon} from 'antd';
import {NavLink} from 'react-router-dom'; export default class Sidebar extends React.Component {
render() {
const {
res
} = this.props;
return (
<div className="layout-sidebar">
<div className="logo"/>
<Menu theme="dark"
mode="inline"
defaultSelectedKeys={[res]}
>
<Menu.Item key="content01">
<NavLink to="/home/content01">
<Icon type="user"/>
<span>nav 1</span>
</NavLink>
</Menu.Item>
<Menu.Item key="content02">
<NavLink to="/home/content02">
<Icon type="video-camera"/>
<span>nav 2</span>
</NavLink>
</Menu.Item>
<Menu.Item key="content03">
<NavLink to="/home/content03">
<Icon type="upload"/>
<span>nav 3</span>
</NavLink>
</Menu.Item>
</Menu>
</div>
)
}
}

修改/app/component/layout/Content.js

import React from 'react';
import {Route} from 'react-router-dom';
import Content01 from './content01';
import Content02 from './content02';
import Content03 from './content03'; export default class Content extends React.Component {
render() {
return (
<div>
<Route path="/home/Content01" component={Content01}/>
<Route path="/home/Content02" component={Content02}/>
<Route path="/home/Content03" component={Content03}/>
</div>
)
}
}

修改/app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<meta charset="UTF-8">
<title>hello world</title>
</head>
<body>
<div id="root"></div>
<script src="./app.js"></script>
</body>
</html>

08.引入状态管理react-redux

阮一峰redux

阮一峰react-redux

安装redux

npm i redux react-redux --save

新建/app/store/store.js

import {createStore} from 'redux';
import user from '../components/content01.reducer'; const store = createStore(user); export default store;

修改/app/app.js

import {Provider} from 'react-redux';
import store from './store/store'; ReactDom.render(
<Provider store={store}>
<BrowserRouter>
<Route path="/" component={App}/>
</BrowserRouter>
</Provider>
,
document.getElementById('root')
);

新建/app/component/content01.reducer.js

let initState = {
data: [{
name: 'doudou',
age: 32,
phone: 123456789,
email: '123456789@163.com',
key: 1,
}]
}; function user(state = initState, action) {
const data = state.data;
switch (action.type) {
case 'ADD_ONE_USER':
data[data.length] = action.payload;
return {data: data};
default:
return state;
}
}
export default user;

新建/app/component/content01.action.js

export function addOneUser(value) {
return {
type: 'ADD_ONE_USER',
payload: value
}
}

修改/app/component/content01.jsx

import React from 'react';
import {Table, Icon, Button} from 'antd';
import {connect} from 'react-redux';
import {addOneUser} from './content01.action';
import FromContent from './FromContent'; class Content01 extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false,
date: null
};
this.showModal = () => {
this.setState({
visible: true,
data: new Date()
});
};
this.closeModal = () => {
this.setState({
visible: false,
});
}; this.submit = (values) => {
values.key = Date.parse(new Date());
this.props.addOneUser(values);
this.closeModal();
}
} render() {
const columns = [{
title: '姓名',
dataIndex: 'name',
key: 'name',
}, {
title: '年龄',
dataIndex: 'age',
key: 'age',
}, {
title: '电话号码',
dataIndex: 'phone',
key: 'phone',
}, {
title: '邮箱',
dataIndex: 'email',
key: 'email',
}]; const data = this.props.data; return (
<div>
<Button type="primary" onClick={this.showModal}>
<Icon type="user-add"/>添加
</Button>
<Table columns={columns} dataSource={data}/>
{
this.state.visible ? <FromContent date={this.state.date}
submit={this.submit}
showModal={this.showModal}
closeModal={this.closeModal}
/> : null
}
</div>
)
}
} const mapStateToProps = (state) => {
return {
data: state.data
}
}; const mapDispatchToProps = (dispatch) => {
return {
addOneUser: (value) => dispatch(addOneUser(value))
}
}; export default connect(mapStateToProps, mapDispatchToProps)(Content01);

新建/app/component/FromContent.js

import React from 'react';
import {Modal, Button, Form, Input} from 'antd';
const FormItem = Form.Item;
import {increaseAction} from './content01.action'; class FromContent01 extends React.Component {
constructor(props) {
super(props); this.handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
this.props.submit(values);
}
});
}
} render() {
const {getFieldDecorator} = this.props.form;
const {closeModal, date} = this.props; return (
<Modal
title="添加一个成员"
key={date}
visible={true}
onCancel={closeModal}
footer={null}
>
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem label="姓名">
{getFieldDecorator('name', {
rules: [{required: true, message: 'Please input your name!'}],
})(
<Input placeholder="姓名"/>
)}
</FormItem>
<FormItem label="年龄">
{getFieldDecorator('age', {
rules: [{required: true, message: 'Please input your age!'}],
})(
<Input placeholder="年龄"/>
)}
</FormItem>
<FormItem label="电话号码">
{getFieldDecorator('phone', {
rules: [{
required: true, message: 'Please input your phone!',
}],
})(
<Input type="电话号码"/>
)}
</FormItem>
<FormItem label="邮箱">
{getFieldDecorator('email', {
rules: [{
required: true, message: 'Please input your email!',
}],
})(
<Input type="邮箱"/>
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
</FormItem>
</Form>
</Modal>
)
}
}
const FromContent = Form.create()(FromContent01);
export default FromContent;

09.更好的使用redux

redux-freeze运行时报错机制

redux-thunkredux的异步中间件

redux-logger日志中间件

redux-immutable在不可变数据类型中合并reducer

redux-create-reducer使用action和reducer的关联变的简单

关于redux中间件的讲解

安装依赖插件

npm i --save redux-immutable redux-freeze redux-thunk redux-logger immutable redux-create-reducer

修改/app/store/store.js

import {createStore, applyMiddleware, compose} from 'redux';
import {combineReducers} from 'redux-immutable';
import freeze from "redux-freeze"
import thunk from 'redux-thunk'
import logger from 'redux-logger'
import {Map} from 'immutable';
import reducers from './reducers'; let middlewares = [];
middlewares.push(thunk);
middlewares.push(logger);
middlewares.push(freeze); //添加中间件
let middleware = applyMiddleware(...middlewares);
//添加redux dev tools,可以在谷歌商城里直接安装工具,搜索名字
middleware = compose(middleware, window.devToolsExtension()); const reducer = combineReducers(reducers); const store = createStore(
reducer,
Map({}),
middleware
); export default store;

新建/app/store/reducers.js

import user from '../components/content01.reducer';
export default {
user
}

修改/app/component/content01.reducer.js

import {createReducer} from 'redux-create-reducer';
import {fromJS} from 'immutable'; import {
ADD_ONE_USER
} from './content01.actions'; const initState = fromJS({
data: [{
name: 'doudou',
age: 32,
phone: 123456789,
email: '123456789@163.com',
key: 1,
}]
}); const handlers = {
[ADD_ONE_USER]: (user, action) => {
return user.set('data', user.get('data').push(fromJS(action.payload)));
}
}; export default createReducer(initState, handlers);

修改/app/component/content01.action.js

export const ADD_ONE_USER = 'ADD_ONE_USER';

export function addOneUser(value) {
return dispatch => {
return dispatch({
type: 'ADD_ONE_USER',
payload: value
})
}
}

修改/app/component/content01.js

const mapStateToProps = (state) => {
return {
data: state.get('user').get('data'),
}
}; const mapActionCreators = {
addOneUser
}; export default connect(mapStateToProps, mapActionCreators)(Content01);

10.优化环境

下载需要的依赖

npm i
extract-text-webpack-plugin
copy-webpack-plugin
clean-webpack-plugin
webpack-merge
html-webpack-plugin
postcss-smart-import
postcss-loader
precss
--save-dev

将webpack.config.js拆分

webpack.common.js
const path = require('path');
const ROOTPATH = path.resolve(__dirname, '.'); module.exports = {
context: path.resolve(ROOTPATH, 'app'),
resolve: {
extensions: ['.js', '.jsx'], //后缀名自动补全,可以使用jsx的文件后缀
modules: [path.resolve(ROOTPATH, "node_modules")],
alias: {
COMPONENTS_PATH: './components',
}
},
entry: {
app: ['babel-polyfill', './app.js']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react'],
}
}
},
{
test: /.*\.(gif|png|jpe?g|svg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
name: 'images/[name].[ext]'
}
}, {
loader: 'image-webpack-loader'
}]
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: false
}
}],
},
{
test: /\.(woff|svg|eot|ttf)\??.*$/,
use: [{
loader: 'url-loader',
options: {
limit: 50000,
}
}]
}
]
},
devServer: {
contentBase: path.resolve(ROOTPATH, './app'),//为一个目录下的文件提供本地服务器,在这里设置其所在目录
historyApiFallback: true,//跳转将指向index.html
inline: true,//开启自动刷新页面
port: 1234,//设置默认监听端口,如果省略,默认为"8080"
hot: true,//开启热替换
},
plugins: [],
};

webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const path = require('path'); const ROOTPATH = path.resolve(__dirname, '.'); module.exports = merge(common, {
output: {
path: path.resolve(ROOTPATH, 'dist'), //打包的文件夹
filename: '[name].js',
publicPath: ''
},
module: {
rules: [
{
test: /\.css$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}],
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader',
options: {
sourceMap: true
}
}, {
loader: 'sass-loader',
options: {
sourceMap: true,
outputStyle: 'expanded',
sourceMapContents: true
}
}]
},
{
test: /\.less$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader',
options: {
sourceMap: true
}
}, {
loader: 'less-loader',
options: {
sourceMap: true,
outputStyle: 'expanded',
sourceMapContents: true,
}
}]
},
{
test: /index\.html/,
use: [{
loader: 'file-loader',
options: {
name: 'index.[ext]'
}
}]
}
]
},
plugins: [],
});

webpack.pro.js

const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; const ROOTPATH = path.resolve(__dirname, '.'); module.exports = merge(common, {
output: {
path: path.resolve(ROOTPATH, 'dist'), //打包的文件夹
filename: '[name].[hash].bundle.js',
publicPath: ''
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({fallback: 'style-loader', use: ['css-loader']}),
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: [{
loader: 'css-loader'
}, {
loader: 'sass-loader'
}]
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [{
loader: "css-loader", options: {importLoaders: 1}
}, {
loader: "postcss-loader"
}, {
loader: 'less-loader'
}]
})
},
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new ExtractTextPlugin({
filename: '[name].[contenthash].css',
allChunks: true, // don't contain embedded styles
}),
//加入js压缩的实例
new UglifyJsPlugin({
mangle: {
mangle: false
},
compress: {
sequences: true,
dead_code: true,
conditionals: true,
booleans: true,
unused: false,
if_return: true,
join_vars: true,
drop_console: false,
warnings: false
},
}),
new HtmlWebpackPlugin({
title: 'xxx局',
filename: path.resolve(ROOTPATH, 'dist/template.html'), // the path to create html
template: path.resolve(ROOTPATH, 'app/public/template.html'), //the path of template html,
minify: false,
// favicon: path.resolve(__dirname, 'app/images/cscec_favicon-2.ico')
}),
new CleanWebpackPlugin(['dist', 'dist.zip', 'dist.rar'], {exclude: ['lib']}),
new CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.bundle.js',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, './node_modules')
) === 0
)
}
}),
],
});

创建app/public/template.html

修改package.json

  "scripts": {
"build": "set NODE_ENV=production&&webpack -p --config webpack.pro.js",
"start": "webpack-dev-server --devtool eval --progress --colors --config webpack.dev.js "
},

创建postcss.config.js

module.exports = {
plugins: [
require('postcss-smart-import')({ /* ...options */ }),
require('precss')({ /* ...options */ }),
require('autoprefixer')({ /* ...options */ })
]
};

执行npm run build

生成的代码已经进过了分离,压缩,添加了版本号,能够利用缓存提升加载速度

11.在react中使用jsplumb、jquery和jquery-ui。

方法1.ProvidePlugin

自动加载模块,不用再每个组件使用require和import

<!--在webpack.common.js的plugins中添加-->
new webpack.ProvidePlugin({
"$": "jquery",
"jQuery": "jquery",
"window.jQuery": "jquery"
})

此时可以使用全局变量$和jQuery了,可以在任意一个组件中console.log($)就可以看到一个函数

<!--引入需要jquery的插件-->
import jp from '../../plugins/jsplumb/jsplumb';
import "jquery-ui/ui/widgets/draggable";

接下来就可以使用jquery和jquery的插件了

<!--可以使用$以及使用jquery-ui的draggable方法了-->
$(el).draggable({
helper: "clone",
containment: ".js-layout",//设置拖动的范围
scroll: false,
cursor: "pointer",//设置拖动光标总是在中心
cursorAt: {top: 30, left: 60},
start: function (ev) {
wh = {
w: $(this).width() / 2,
h: $(this).height() / 2
}
},
drag: function (ev) {
offset = $(this).offset(); },
stop: function (ev) {
console.log('鼠标的位置', {top: ev.clientY, left: ev.clientX});
console.log('拖放元素的位置', {top: offset.top, left: offset.left});
let pos = {x: ev.clientX - offset.left - wh.w - 9, y: ev.clientY - offset.top - wh.h - 100 - 8};
movecb(pos)
}
});

webpack文档

方法2.expose-loader

使用expose-loader可以暴露全局变量

    module: {
rules: [
{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: 'jQuery'
}, {
loader: 'expose-loader',
options: '$'
}]
},
]
},

此时的效果和方法一的一样

webpack文档

使用jquery-ui的方法

使用npm
<!--可以通过npm i下载-->
npm i jquery-ui --save <!--然后在组件中引入-->
import "jquery-ui/ui/widgets/draggable";

需要一个个引入你需要的方法,也可以做一个总文件进行引入

直接下载

从官网下载jquery-ui,然后放到项目中的一个文件夹下

import "../../plugins/jquery-ui"

这样可以直接引入生效,但是打包的文件会大不少

查看文章

webpack多页应用架构系列(四):老式jQuery插件还不能丢,怎么兼容?

Webpack引入jquery及其插件的几种方法

12.开发常用工具

  • prop-types
  • classnames

附1.依赖

搭建框架

babel 官网 中文网站 api文档

webpack

webpack-dev-server

babel基本

babel其他

react

react

react-router

react-router

[react-router-dom]

redux

redux

react-redux

redux-freeze运行时报错机制

redux-thunkredux的异步中间件

redux-logger日志中间件

redux-immutable在不可变数据类型中合并reducer

redux-create-reducer使用action和reducer的关联变的简单

关于redux中间件的讲解

ant-design

在react.js上使用antd-design

antd-design

babel-plugin-import 用于antd按需加载

loader

less-loader

css-loader

style-loader

babel-polyfill

[file-loader]

[url-loader]

[image-webpack-loader]

html-webpack-plugin

[extract-text-webpack-plugin] 将css单独打包

[React] react+redux+router+webpack+antd环境搭建一版的更多相关文章

  1. react+webpack+babel环境搭建

    [react+webpack+babel环境搭建] 1.react官方文档推荐使用 babel-preset-react.babel-preset-es2015 两个perset. Babel官方文档 ...

  2. vue开发环境搭建Mac版

    一.前言 要做一个移动端app,面对webapp最流行的三个技术React,angular,vue,三选一,如何选,可参考blog移动app技术选型,react,angular, vue, 下面是对  ...

  3. React+Webpack+ES6环境搭建(自定义框架)

    引言 目前React前端框架是今年最火的.而基于React的React Native也迅速发展.React有其独特的组件化功能与JSX的新语法,帮助前端设计有了更好的设计与便捷,而React Nati ...

  4. client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

    [本文源址:http://blog.csdn.net/q1056843325/article/details/54729657 转载请加入该地址] 明天就是除夕了 预祝大家新春快乐 [ ]~( ̄▽ ̄) ...

  5. react v16.12 源码阅读环境搭建

    搭建后的代码(Keep updated): https://github.com/lirongfei123/read-react 欢迎将源码阅读遇到的问题提到issue 环境搭建思路: 搭建一个web ...

  6. React Native入门教程 1 -- 开发环境搭建

    有人问我为啥很久不更新博客..我只能说在学校宿舍真的没有学习的环境..基本上在宿舍里面很颓废..不过要毕业找工作了,我要渐渐把这个心态调整过来,就从react-native第一篇博客开始.话说RN也出 ...

  7. React Native在window下的环境搭建(二):创建新项目

    React Native创建一个新项目: react-native init TestAndroidApp 提示:你可以使用--version参数(注意是两个杠)创建指定版本的项目.例如react-n ...

  8. Redux教程1:环境搭建,初写Redux

    如果将React比喻成士兵的话,你的程序还需要一位将军,去管理士兵(的状态),而Redux恰好是一位好将军,简单高效: 相比起React的学习曲线,Redux的稍微平坦一些:本系列教程,将以" ...

  9. vue+webpack项目环境搭建

    首先安装node.js 下载地址:https://nodejs.org/en/download/ 看下是否成功安装 node -v 安装webpack,命令行npm install webpack - ...

随机推荐

  1. Rabin-Karp ACM训练

    求解问题 寻找S中T出现的位置或次数.假设S的长度为n, T的长度为m, 通过枚举S长度为m的字串的hash值与T的hash值比较.此时使用滚动hash的优化使复杂度不为O(mn). 算法说明 滚动h ...

  2. vue开发(开发环境+项目搭建)

    Vue.js是一套构建用户界面的渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合.另一方面,Vu ...

  3. 自行搭建私有云kodexplorer

    kodexplorer是一款开源的私有云框架,可以通过它实现个人网盘的功能,如果拥有一个性能不错的VPS,那么就可以摆脱奇慢无比的百度云等网盘啦!最近百度网盘还发出申明,说要限制使用空间.用别人的东西 ...

  4. 服务器黑屏,只出现cmd窗口的解决办法

    先上图,如图所示,正常启动或者进入安全模式都出现此现象,尝试了各种办法,比如: 1.打开此页面后,重新开一台可以远程的电脑连接,此方法不通: 2.进任务管理器无explorer.exe进程,且创建此进 ...

  5. [LeetCode] Lemonade Change 买柠檬找零

    At a lemonade stand, each lemonade costs $5.  Customers are standing in a queue to buy from you, and ...

  6. python从入门到实践-7章用户输入和while循环

    #!/user/bin/env python# -*- coding:utf-8 -*- # input() 可以让程序暂停工作# int(input('please input something: ...

  7. nova boot from volume代码分析

    首先要创建一个bootable volume curl -i http://16.158.166.197:8776/v1/c24c59846a7f44538d958e7548cc74a3/volume ...

  8. 吴恩达机器学习笔记54-开发与评价一个异常检测系统及其与监督学习的对比(Developing and Evaluating an Anomaly Detection System and the Comparison to Supervised Learning)

    一.开发与评价一个异常检测系统 异常检测算法是一个非监督学习算法,意味着我们无法根据结果变量

  9. Go语言单元测试与基准测试

    目录 单元测试 概述 go test参数解读 单元测试日志 基准测试 基础测试基本使用 基准测试原理 自定义测试时间 测试内存 控制计时器 Go语言拥有一套单元测试和性能测试系统,仅需要添加很少的代码 ...

  10. Kubernetes---Pod的扩容和缩容

    用RC的Scale机制来实现Pod的扩容和缩容 把redis-slave的pod扩展到3个  ,  kubectl scale rc redis-slave --replicas=3 现在来缩容,把 ...