最近在学习react+es6+webpack ,从网上看了资料,整理了这个项目,顺便记录一下学习历程,这里要求对react ,es6 , webpack ,nodejs有一定的基础,但是又不知道怎么取搭建项目的。
下面我就把各个部分的代码贴出来,供大家参考:
1.首先肯定是我们的依赖,package.json
{
"name": "webpack-bry",
"version": "1.0.0",
"description": "bry first webpack program",
"main": "index.js",
"devDependencies": { //各种依赖
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-react-transform": "^3.0.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"css-loader": "^0.28.7",
"html-webpack-plugin": "^2.30.1",
"react-transform-hmr": "^1.0.4",
"style-loader": "^0.18.2",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.8.2"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack", //从webpack启动
"server": "webpack-dev-server --open" //用server启动
},
"author": "bry",
"license": "ISC",
"dependencies": {
"react": "^15.6.1",
"react-dom": "^15.6.1",
"webpack": "^3.6.0"
}
}
2.接下来是webpack的配置文件:webpack.config.js
const webpack = require('webpack');
module.exports = {
devtool: 'eval-source-map', //生成Source Maps(使调试更容易)
entry: __dirname + "/app/ManageSystem.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/build",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
//使用webpack构建本地服务器
devServer: {
contentBase: "./build",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转 在开发单页应用时非常有用,如果设置为true,所有的跳转将指向index.html
inline: true,//实时刷新
port:8081, //设置默认监听端口,如果省略,默认为'8080'
hot: true //开启热加载
},
//Babel配置 es2015就是es6
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"es2015", "react"
]
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究'), //版权插件
new webpack.HotModuleReplacementPlugin()//热加载插件
],
}
3.index.html 我们的主页
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Projecttitle>
<link href="style.css" rel="stylesheet" />
head>
<body>
<div id='root'>
div>
<script type="text/javascript" src="bundle.js">script>body>
html>
4.入口文件:ManageSystem.js
import React from 'react'; //import 是es6的导入模块的写法
import {render} from 'react-dom';
import StaffHeader from './StaffHeader.js';
import StaffItemPanel from './StaffItemPanel.js';
import StaffFooter from './StaffFooter.js';
import StaffDetail from './StaffDetail.js';
import Staff from './STAFF.js';
class App extends React.Component {
constructor(){
super();
this.state = {
staff : new Staff,
staffDetail: null
};
}
//增
addStaffItem(item){
this.setState({
staff: this.state.staff.addStaffItem(item)
});
}
//删
removeStaffItem(key){
this.setState({
staff: this.state.staff.removeStaffItem(key)
});
}
/*
*详情
*/
//打开
detailStaffItem(key){
this.setState({
staffDetail: this.state.staff.staff.filter(item => {
return item.key==key;
})[0]
});
}
//关闭
closeDetail(){
this.setState({
staffDetail: null
});
}
//编辑
editDetail(item){
this.setState({
staff : this.state.staff.editStaffItem(item)
});
}
/*
* 排序
*/
sortStaff(sortType) {
this.setState({
staff: this.state.staff.sortStaff(sortType)
});
}
/*
* 筛选
*/
filtStaff(filtType) {
this.setState({
staff: this.state.staff.filtStaff(filtType)
});
}
/*
* 搜索
*/
searchStaff(word) {
this.setState({
staff: this.state.staff.searchStaff(word)
});
}
render(){
return (
this.sortStaff.bind(this)} filtStaff={this.filtStaff.bind(this)} searchStaff={this.searchStaff.bind(this)}/>
this.state.staff.staff} removeStaffItem={this.removeStaffItem.bind(this)} detailStaffItem={this.detailStaffItem.bind(this)}/>
this.addStaffItem.bind(this)}/>
this.state.staffDetail} closeDetail={this.closeDetail.bind(this)} editDetail={this.editDetail.bind(this)}/>
);
}
}
render( , document.getElementById('root'));
5.数据管理 : STAFF.js
class staffItem {
constructor(item){
this.info = {};
this.info.name = item.name;
this.info.age = item.age || 0;
this.info.sex = item.sex;
this.info.id = item.id;
this.info.descrip = item.descrip || '';
this.key = ++staffItem.key;
}
}
staffItem.key = 0;
export default class STAFF {
constructor(){
this.allStaff = [
new staffItem(STAFF.rawData[0]),
new staffItem(STAFF.rawData[1]),
new staffItem(STAFF.rawData[2]),
new staffItem(STAFF.rawData[3]),
new staffItem(STAFF.rawData[4]),
new staffItem(STAFF.rawData[5]),
new staffItem(STAFF.rawData[6]),
new staffItem(STAFF.rawData[7]),
new staffItem(STAFF.rawData[8]),
new staffItem(STAFF.rawData[9]),
new staffItem(STAFF.rawData[10])
];
this.staff = [];
this.sortType = 0;//0-身份 1-年龄升 2-年龄降
this.filtType = 0;//0-all 1-主任 2-老师 3-学生 4-实习
this.word = '';//搜索关键字
this._sortStaff(this.sortType); //默认按身份排序
this._filtStaff(this.filtType);
}
//增
addStaffItem(item) {
let newItem = new staffItem(item);
this.allStaff.push(newItem);
//排序 筛选 搜索过滤
this._sortStaff(this.sortType);
this._filtStaff(this.filtType);
this._searchStaff(this.word);
return this;
}
//删
removeStaffItem(key) {
let newStaff = this.allStaff.filter(item => {
return item.key != key;
});
this.allStaff = newStaff;
//筛选 搜多过滤
this._filtStaff(this.filtType);
this._searchStaff(this.word);
return this;
}
//改
editStaffItem(item) {
this.allStaff.forEach(staffItem => {
if(staffItem.key == item.key) {
staffItem.info.name = item.name;
staffItem.info.sex = item.sex;
staffItem.info.age = item.age;
staffItem.info.id = item.id;
staffItem.info.descrip = item.descrip;
}
});
this._sortStaff(this.sortType);
this._filtStaff(this.filtType);
this._searchStaff(this.word);
return this;
}
//筛选
_filtStaff(filtType){
this.filtType = filtType;
switch(parseInt(filtType)){
case 0:
this.staff = this.allStaff;
break;
case 1:
this.staff = this.allStaff.filter(item => {
return item.info.id == '主任';
});
break;
case 2:
this.staff = this.allStaff.filter(item => {
return item.info.id == '老师';
});
break;
case 3:
this.staff = this.allStaff.filter(item => {
return item.info.id == '学生';
});
break;
case 4:
this.staff = this.allStaff.filter(item => {
return item.info.id == '实习';
});
break;
default: break;
}
}
//排序
_sortStaff(sortType) {
this.sortType = sortType;
switch(parseInt(sortType)){
case 0: //身份
this.allStaff.forEach(item => {
switch(item.info.id) {
case '主任':
item.info.id = 1; break;
case '老师':
item.info.id = 2; break;
case '学生':
item.info.id = 3; break;
case '实习':
item.info.id = 4; break;
default: break;
}
});
this.allStaff.sort(function(item1, item2){
if(item1.info.id < item2.info.id)
return -1;
else if (item1.info.id > item2.info.id)
return 1;
else
return 0;
});
this.allStaff.forEach(item => {
switch(item.info.id) {
case 1:
item.info.id = '主任'; break;
case 2:
item.info.id = '老师'; break;
case 3:
item.info.id = '学生'; break;
case 4:
item.info.id = '实习'; break;
default: break;
}
});
break;
case 1: //年龄升
this.allStaff.sort(function(item1, item2){
if(item1.info.age < item2.info.age)
return -1;
else if (item1.info.age > item2.info.age)
return 1;
else
return 0;
});
break;
case 2: //年龄降
this.allStaff.sort(function(item1, item2){
if(item1.info.age < item2.info.age)
return 1;
else if (item1.info.age > item2.info.age)
return -1;
else
return 0;
});
break;
default: break;
}
}
//搜索
_searchStaff(word){
this.word = word;
//在staff中搜索
this.staff = this.staff.filter(item => {
return item.info.name.indexOf(word)!=-1 ||
(item.info.age+'').indexOf(word)!=-1 ||
item.info.id.indexOf(word)!=-1 ||
item.info.sex.indexOf(word)!=-1;
});
}
filtStaff(filtType){
this._filtStaff(filtType);
this._searchStaff(this.word);
return this;
}
sortStaff(sortType){
this._sortStaff(sortType);
this._filtStaff(this.filtType);
this._searchStaff(this.word);
return this;
}
searchStaff(word){
this._filtStaff(this.filtType);
this._searchStaff(word);
return this;
}
}
//模拟数据库
STAFF.rawData = [{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 20, name: '张三', id: '主任'},
{ descrip:'我是一匹来自远方的狼。', sex: '女', age: 21, name: '赵静', id: '学生'},
{ descrip:'我是一匹来自远方的狼。', sex: '女', age: 22, name: '王二麻', id: '学生'},
{ descrip:'我是一匹来自远方的狼。', sex: '女', age: 24, name: '李晓婷', id: '实习'},
{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 23, name: '张春田', id: '实习'},
{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 22, name: '刘建国', id: '学生'},
{ descrip:'我是一匹来自远方的狼。', sex: '男', age: 24, name: '张八', id: '主任'},
{ descrip:'我是一匹来自远方的狗。', sex: '男', age: 35, name: '李四', id: '老师'},
{ descrip:'我是一匹来自远方的猪。', sex: '男', age: 42, name: '王五', id: '学生'},
{ descrip:'我是一匹来自远方的牛。', sex: '男', age: 50, name: '赵六', id: '实习'},
{ descrip:'我是一匹来自远方的马。', sex: '男', age: 60, name: '孙七', id: '实习'}];
6.新增用户组件:StaffFooter.js
import React from 'react';
import ReactDOM from 'react-dom'
export default class StaffFooter extends React.Component{
handlerAddClick(evt){
evt.preventDefault();
let item = {};
let addForm = ReactDOM.findDOMNode(this.refs.addForm);
let sex = addForm.querySelector('#staffAddSex');
let id = addForm.querySelector('#staffAddId');
item.name = addForm.querySelector('#staffAddName').value.trim();
item.age = addForm.querySelector('#staffAddAge').value.trim();
item.descrip = addForm.querySelector('#staffAddDescrip').value.trim();
item.sex = sex.options[sex.selectedIndex].value;
item.id = id.options[id.selectedIndex].value;
/*
*表单验证
*/
if(item.name=='' || item.age=='' || item.descrip=='') {
let tips = ReactDOM.findDOMNode(this.refs.tipsUnDone);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
//非负整数
let numReg = /^\d+$/;
if(!numReg.test(item.age) || parseInt(item.age)>150) {
let tips = ReactDOM.findDOMNode(this.refs.tipsUnAge);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
this.props.addStaffItem(item);
addForm.reset();
//此处应在返回添加成功信息后确认
let tips = ReactDOM.findDOMNode(this.refs.tips);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
}
render(){
return (
"titleCenter">人员新增
)
}
}
7.头部搜索信息组件:StaffHeader.js
import React from 'react';
import ReactDOM from 'react-dom'
export default class StaffHeader extends React.Component{
//排序
handlerOrderChange(){
let sel = ReactDOM.findDOMNode(this.refs.selOrder);
let selValue = sel.options[sel.selectedIndex].value;
this.props.sortStaff(selValue);
}
//筛选
handlerIdChange(){
let sel = ReactDOM.findDOMNode(this.refs.selId);
let selValue = sel.options[sel.selectedIndex].value;
this.props.filtStaff(selValue);
}
//search
handlerSearch(){
let bar = ReactDOM.findDOMNode(this.refs.searchBar);
let value = bar.value;
this.props.searchStaff(value);
}
render(){
return (
"titleCenter">人员管理系统
"optHeader">
"headerTd">ref='searchBar' onChange={this.handlerSearch.bind(this)} type='text' placeholder='Search...' />
"headerTd">
<select id='idSelect' ref="selId" onChange={this.handlerIdChange.bind(this)}>
select>
<select id='orderSelect' ref="selOrder" onChange={this.handlerOrderChange.bind(this)}>
select>
);
}
}
8.一条职员信息的组件:StaffItem.js
import React from 'react';
export default class StaffItem extends React.Component{
//delete
handlerDelete(evt){
this.props.removeStaffItem(this.props.item.key);
}
//detail
handlerDetail(evt){
this.props.detailStaffItem(this.props.item.key);
}
render(){
return (
'cursor': 'pointer'}}
>
'itemTd'>{this.props.item.info.name}
'itemTd'>{this.props.item.info.age}
'itemTd'>{this.props.item.info.id}
'itemTd'>{this.props.item.info.sex}
'itemTd'>
"itemBtn" onClick={this.handlerDelete.bind(this)}>删除
"itemBtn" onClick={this.handlerDetail.bind(this)}>详情
);
}
}
9.整个职员信息列表的组件:StaffItemPanel.js
import React from 'react';
import StaffItem from './StaffItem.js';
export default class StaffItemPanel extends React.Component{
render(){
let items = [];
if(this.props.items.length == 0) {
items.push(<tr><th colSpan="5" className="tempEmpty">暂无用户th>tr>);
}else {
this.props.items.forEach(item => {
items.push(<StaffItem key={item.key} item={item} removeStaffItem={this.props.removeStaffItem} detailStaffItem={this.props.detailStaffItem}/>);
});
}
return (
<table className='itemPanel'>
<thead>
<th className='itemTd'>姓名th>
<th className='itemTd'>年龄th>
<th className='itemTd'>身份th>
<th className='itemTd'>性别th>
<th className='itemTd'>操作th>
thead>
<tbody>{items}tbody>
table>
);
}
}
10.职员详细信息组件:StaffDetail.js
import React from 'react';
import ReactDOM from 'react-dom'
export default class StaffDetail extends React.Component{
handlerEdit(){
let item = {};
let editTabel = ReactDOM.findDOMNode(this.refs.editTabel);
let sex = editTabel.querySelector('#staffEditSex');
let id = editTabel.querySelector('#staffEditId');
item.name = editTabel.querySelector('#staffEditName').value.trim();
item.age = editTabel.querySelector('#staffEditAge').value.trim();
item.descrip = editTabel.querySelector('#staffEditDescrip').value.trim();
item.sex = sex.options[sex.selectedIndex].value;
item.id = id.options[id.selectedIndex].value;
item.key = this.props.staffDetail.key;
/*
*表单验证
*/
if(item.name=='' || item.age=='' || item.descrip=='') {
let tips = ReactDOM.findDOMNode(this.refs.DtipsUnDone);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
//非负整数
let numReg = /^\d+$/;
if(!numReg.test(item.age) || parseInt(item.age)>150) {
let tips = ReactDOM.findDOMNode(this.refs.DtipsUnAge);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
this.props.editDetail(item);
//此处应在返回修改成功信息后确认
let tips = ReactDOM.findDOMNode(this.refs.Dtips);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
}
handlerClose(){
this.props.closeDetail();
}
componentDidUpdate(){
if(this.props.staffDetail == null){}
else {
let selSex = ReactDOM.findDOMNode(this.refs.selSex);
for(let i=0; iif(selSex.options[i].value == this.props.staffDetail.info.sex){
selSex.options[i].selected = 'selected';
break;
}
}
let selId = ReactDOM.findDOMNode(this.refs.selId);
for(let i=0; iif(selId.options[i].value == this.props.staffDetail.info.id){
selId.options[i].selected = 'selected';
break;
}
}
}
}
render(){
let staffDetail = this.props.staffDetail;
if(!staffDetail)
return null;
return (
"overLay">
"titleCenter">点击'完成'保存修改,点击'关闭'放弃未保存修改并退出.
ref="editTabel">
姓名
'staffEditName' type="text" defaultValue={staffDetail.info.name}>
年龄
'staffEditAge' type="text" defaultValue={staffDetail.info.age}>
性别
<select ref='selSex' id='staffEditSex'>
select>
身份
<select ref="selId" id='staffEditId'>
select>
个人描述
ref='Dtips' className='tips'>修改成功
ref='DtipsUnDone' className='tips'>请录入完整的人员信息
ref='DtipsUnAge' className='tips'>请录入正确的年龄
);
}
}
11.接下来是最后的样式了:style.css
body,html {
font-family: '微软雅黑';
}
#app{
width: 600px;
margin: 100px auto;
position: relative;
}
.titleCenter{
text-align: center;
}
.optHeader {
margin: 30px auto;
}
.headerTd {
width: 33%;
text-align: center;
}
.optHeader select {
width: 5em;
}
.itemPanel {
width: 100%;
border: 2px solid #b0c4de;
border-radius: 4px;
margin-bottom: 100px;
}
.itemTd {
width: 20%;
line-height: 1.5em;
text-align: center;
}
.itemBtn {
width: 3em;
font-size: 80%;
color: #1e90ff;
display: inline-block;
}
.tempEmpty {
line-height: 1.5em;
background: #dcdcdc;
}
.addForm label{
text-align: center;
}
.addForm input, .addForm select, .addForm textarea{
width: 200px;
margin: 0 auto 10px auto;
display: block;
}
.addForm button {
padding: 3px 20px;
background-color: #1e90ff;
color: white;
font-weight: bold;
border-radius: 4px;
margin: 5px auto;
display: block;
}
.tips {
display: none;
color: #708090;
margin: 0 auto;
text-align: center;
}
.overLay {
text-align: center;
z-index: 100;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
padding-top: 50px;
background-color: rgba(255, 255, 255, 0);
animation-name: detailMerge;
animation-duration: 0.4s;
animation-fill-mode: forwards;
}
.overLay table {
width: 100%;
}
.overLay tr {
line-height: 1.5em;
}
.overLay th {
width: 40%;
}
.overLay td {
width: 60%;
text-align: center;
}
.overLay input, .overLay select, .overLay textarea {
width: 200px;
}
.overLay button {
padding: 3px 20px;
background-color: #1e90ff;
color: white;
font-weight: bold;
border-radius: 4px;
margin: 5px auto;
display: inline-block;
}
@keyframes detailMerge {
from {
background-color: rgba(255, 255, 255, 0);
}
to {
background-color: rgba(255, 255, 255, 0.95);
}
}
@-webkit-keyframes detailMerge {
from {
background-color: rgba(255, 255, 255, 0);
}
to {
background-color: rgba(255, 255, 255, 0.8);
}
}
现在所有文件的代码都贴出来了,最后进入项目的文件夹,敲几个命令就可以了:
1.安装依赖项
npm install
2.打包:
npm start
或者
直接启动 :
npm run server
按照这些信息你先把项目搭建起来,之后你再细细的研究react是怎么进行组件化的,以及es6的完美结合.