1.目录结构
2.webpack.config.js
说明:
1.涉及到的插件需要npm install安装;
2.html-webpack-plugin创建服务于 webpack bundle 的 HTML 文件;
3.clean-webpack-plugin清除dist目录重复的文件;
4.extract-text-webpack-plugin分离css文件。
var path = require('path'); var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var CleanWebpackPlugin = require('clean-webpack-plugin'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; var config = { context: path.resolve(__dirname,'./src'),entry: { app: './main.js' },output: { path: path.resolve(__dirname,'./dist'),filename: '[name].bundle.js' },devtool: 'cheap-module-eval-source-map',module: { rules: [ { test: /\.jsx?$/,exclude: /node_modules/,loader: 'babel-loader' },{ test: /\.css$/,use: ExtractTextPlugin.extract({ fallback: "style-loader",use: ["css-loader","postcss-loader"] }) },{ test: /\.less$/,use: ["style-loader","css-loader","less-loader"] },{ test: /\.(png|jpg)$/,loader: 'url-loader',options: { limit: 8129 } } ] },devServer:{ historyApiFallback: true,host:'0.0.0.0',hot: true,//HMR模式 inline: true,//实时刷新 port: 8181 // 修改端口,一般默认是8080 },resolve: { extensions: ['.js','.jsx','.css'],modules: [path.resolve(__dirname,'node_modules'] },plugins: [ new webpack.HotModuleReplacementPlugin(),new UglifyJsPlugin({ sourceMap: true }),new webpack.LoaderOptionsPlugin({ minimize: true,debug: true }),new HtmlWebpackPlugin({ template:'./templateIndex.html' }),new ExtractTextPlugin({ filename: '[name].[hash].css',disable: false,allChunks: true,}),new CleanWebpackPlugin(['dist']) ],} module.exports = config; // webpack里面配置的bundle.js需要手动打包才会变化,目录可以由自己指定; // webpack-dev-server自动检测变化自动打包的是开发环境下的bundle.js,打包路径由contentBase决定,两个文件是不一样的.
3.postcss.config.js
(Autoprefixer)
module.exports = { plugins: { 'autoprefixer': {browsers: 'last 5 version'} } } // 兼容最新的5个浏览器版本
4.新建.babelrc
{ "presets": ['es2015','react','stage-3'] }
5.index.html
<!doctype html> <html lang="en"> <head> <Meta charset="utf-8"> <title>React Project</title> </head> <body> <div id="content"></div> <script src="app.bundle.js"></script> </body> </html>
6.package.json
npm install 或 yarn -> 安装模块,npm run build -> 打包,npm start -> 启动localhost:8181
{ "name": "reactproject","version": "1.0.0","description": "","main": "index.js","dependencies": { "jquery": "^3.1.1","react": "^15.3.2" },"devDependencies": { "autoprefixer": "^7.1.2","babel-core": "^6.14.0","babel-loader": "^6.2.5","babel-plugin-Syntax-async-functions": "^6.13.0","babel-plugin-transform-async-to-generator": "^6.16.0","babel-preset-es2015": "^6.14.0","babel-preset-react": "^6.11.1","babel-preset-stage-3": "^6.17.0","bootstrap": "^4.0.0-alpha.2","clean-webpack-plugin": "^0.1.16","css-loader": "^0.25.0","extract-text-webpack-plugin": "^3.0.0-rc.2","file-loader": "^0.9.0","html-webpack-plugin": "^2.29.0","jshint": "^2.9.3","jshint-loader": "^0.8.3","json-loader": "^0.5.4","less": "^2.7.1","less-loader": "^2.2.3","moment": "^2.15.1","node-sass": "^3.10.0","postcss-loader": "^2.0.6","react-bootstrap": "^0.30.5","react-dom": "^15.3.2","sass-loader": "^4.0.2","style-loader": "^0.13.1","url-loader": "^0.5.7","webpack": "^3.3.0","webpack-dev-server": "^2.5.1" },"scripts": { "start": "webpack-dev-server --hot --inline --progress --colors --content-base .","build": "webpack --progress --colors" },"keywords": [ "reactcode" ],"author": "xhh","license": "ISC" }
7.main.js
:入口文件
import React from 'react' import { render } from 'react-dom'; import $ from 'jquery'; import Demo1 from './js/demo1.js'; // import Demo2 from './js/demo2.js'; render(<Demo1 title="这是提示" />,$('#content')[0]); // render(<Demo2 myName="园中桥" sex="female"/>,$('#content')[0]);
8.templateIndex.html
打包后的模板index文件,插件html-webpack-plugin的template指定的目录。
<!doctype html> <html lang="en"> <head> <Meta charset="utf-8"> <title>Template Index html</title> </head> <body> <div id="content"></div> </body> </html>
9.demo
demo1.js
import React from 'react'; import '../css/demo1.css'; const arr = [ { name:'name1',tel:'12343456783' },{ name:'name2',tel:'12343456784' },{ name:'name3',tel:'12343456785' } ]; export default class Demo1 extends React.Component { constructor(props) { super(props); this.state = { content: true,value: 'inputText' }; } handleClick(){ this.setState({ content: !this.state.content }) // this.refs.myInput.focus(); } handleChange(event) { this.setState({value: event.target.value}); } renderArr() { return arr.map((item,index)=>{ return <li key={index}>name:{item.name},tel:{item.tel}</li> }) } render(){ let btnStyle = { border: '1px solid #ccc',background:'#fff',color: '#a106ce' } return ( /* 注释 */ <div> <button style={btnStyle} className="btn" type="button" onClick={()=>this.handleClick()}>change state</button><br/> <p title={this.props.title} style={{ color:'#A349A4' }}>Hello { this.props.textCont}!</p> <p>{this.state.content ? 'initial value' : 'later value'}</p> { /* 标签里面的注释外面要用花括号 */ } <input type="text" value={this.state.value} ref="myInput" onChange={this.handleChange.bind(this)} /> <h4>{this.state.value}</h4> <DemoChild><p>lalala</p></DemoChild> <ul> { this.renderArr() } </ul> </div> ) } } Demo1.propTypes = { title: React.PropTypes.string.isrequired } Demo1.defaultProps = { textCont: 'React' } class DemoChild extends React.Component { constructor(props) { super(props); } render(){ return ( <div>我是子组件{this.props.children}</div> ) } }
demo1.css
ul { list-style: none; padding: 0; margin:0; display: flex; } .btn:focus { outline: none; }
demo2.js
:父子组件生命周期
import React,{ Component,PropTypes } from 'react'; import '../css/demo2.css'; export default class Demo2 extends Component { constructor(props){ super(props); this.state = { stateName: this.props.myName + ',你好',count: 0,} console.log('init-constructor'); } static get defaultProps() { return { myName: "xhh",age: 25 } } doUpdateCount(){ this.setState({ count: this.state.count+1 }) } componentWillMount() { console.log('componentWillMount'); } componentDidMount() { console.log('componentDidMount') } componentWillReceiveProps(nextProps){ console.log('componentWillReceiveProps') } shouldComponentUpdate(nextProps,nextState){ console.log('shouldComponentUpdate'); // return nextProps.id !== this.props.id; if(nextState.count > 10) return false; return true; } componentWillUpdate(nextProps,nextState){ console.log('componentWillUpdate'); } componentDidUpdate(prevProps,prevState){ console.log('componentDidUpdate'); } componentWillUnmount(){ console.log('componentWillUnmount'); } render(){ console.log('render'); return ( <div> <p className="colorStyle">姓名:{this.props.myName}</p> <p>问候:{this.state.stateName}</p> <p>年龄:{this.props.age}</p> <p>性别:{this.props.sex}</p> <p>父元素计数是:{this.state.count}</p> <button onClick={ this.doUpdateCount.bind(this) } style={{ padding: 5,backgroundColor: '#ccc' }}>点我开始计数</button> <SubMyPropType count1={this.state.count} /> </div> ) } } Demo2.propTypes = { myName: PropTypes.string,age: PropTypes.number,sex: PropTypes.string.isrequired } class SubMyPropType extends Component { componentWillMount() { console.log('subMyPropType-componentWillMount'); } componentDidMount() { console.log('subMyPropType-componentDidMount') } componentWillReceiveProps(nextProps){ console.log('subMyPropType-componentWillReceiveProps') } shouldComponentUpdate(nextProps,nextState){ console.log('subMyPropType-shouldComponentUpdate'); if(nextProps.count1 > 5) return false; return true; } componentWillUpdate(nextProps,nextState){ console.log('subMyPropType-componentWillUpdate'); } componentDidUpdate(prevProps,prevState){ console.log('subMyPropType-componentDidUpdate'); } componentWillUnmount(){ console.log('subMyPropType-componentWillUnmount'); } render(){ console.log('subMyPropType-render'); return( <p>子元素计数是:{this.props.count1}</p> ) } }
demo2.css
.colorStyle { color: #0f0; }