├── demo03 ├── readme.md ├── src │ ├── components │ │ ├── story │ │ │ ├── story.scss │ │ │ └── story.jsx │ │ ├── travel │ │ │ ├── travel.scss │ │ │ └── travel.jsx │ │ ├── home │ │ │ ├── home.scss │ │ │ └── home.jsx │ │ └── global │ │ │ ├── _varible.scss │ │ │ ├── global.scss │ │ │ ├── menuLi.jsx │ │ │ ├── menu.scss │ │ │ ├── menu.jsx │ │ │ ├── reset.scss │ │ │ └── mediaQuery.scss │ └── app.js ├── .babelrc ├── index.html ├── package.json └── webpack.config.js ├── demo01 ├── src │ ├── home │ │ ├── home.js │ │ ├── home.css │ │ └── home.scss │ ├── es6test │ │ ├── es6test1.js │ │ └── es6test2.js │ └── app.js ├── .babelrc ├── index.html ├── server.js ├── package.json └── webpack.config.js ├── demo02 ├── src │ ├── home │ │ ├── home.css │ │ ├── home.scss │ │ ├── home2.js │ │ └── home1.js │ ├── test │ │ ├── test.jsx │ │ ├── test.js │ │ ├── test2.js │ │ ├── test5.js │ │ ├── test3.js │ │ ├── test4.js │ │ ├── test7.js │ │ ├── test6.js │ │ └── test8.js │ └── app.js ├── .babelrc ├── index.html ├── server.js ├── package.json └── webpack.config.js ├── .gitignore ├── demo04 ├── .gitignore ├── src │ ├── reducers │ │ ├── index.js │ │ ├── visibilityFilter.js │ │ ├── todos.js │ │ └── todos.spec.js │ ├── actions │ │ ├── index.js │ │ └── index.spec.js │ ├── components │ │ ├── App.js │ │ ├── Footer.js │ │ ├── Todo.js │ │ ├── Link.js │ │ └── TodoList.js │ ├── index.js │ └── containers │ │ ├── AddTodo.js │ │ ├── VisibleTodoList.js │ │ └── FilterLink.js ├── package.json ├── README.md └── public │ └── index.html └── readme.md /demo03/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo03/src/components/story/story.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo03/src/components/travel/travel.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo01/src/home/home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | -------------------------------------------------------------------------------- /demo01/src/home/home.css: -------------------------------------------------------------------------------- 1 | #example{ 2 | color: yellow; 3 | } -------------------------------------------------------------------------------- /demo02/src/home/home.css: -------------------------------------------------------------------------------- 1 | #example{ 2 | color: yellow; 3 | } -------------------------------------------------------------------------------- /demo03/src/components/home/home.scss: -------------------------------------------------------------------------------- 1 | h5{ 2 | font-size: 3rem; 3 | } 4 | -------------------------------------------------------------------------------- /demo01/src/home/home.scss: -------------------------------------------------------------------------------- 1 | #example { 2 | h1{ 3 | font-size: 50px; 4 | } 5 | } -------------------------------------------------------------------------------- /demo02/src/home/home.scss: -------------------------------------------------------------------------------- 1 | #example { 2 | h1{ 3 | font-size: 50px; 4 | } 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /demo01/node_modules 2 | /demo02/node_modules 3 | /demo03/node_modules 4 | /demo04/node_modules 5 | -------------------------------------------------------------------------------- /demo01/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins":["react-hot-loader/babel"] 6 | } -------------------------------------------------------------------------------- /demo02/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins":["react-hot-loader/babel"] 6 | } -------------------------------------------------------------------------------- /demo03/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins":["react-hot-loader/babel"] 6 | } -------------------------------------------------------------------------------- /demo01/src/es6test/es6test1.js: -------------------------------------------------------------------------------- 1 | import Point from './es6test2'; 2 | let p = new Point(1,2); 3 | console.log(p.toString()); -------------------------------------------------------------------------------- /demo03/src/components/global/_varible.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * scss全局变量 3 | */ 4 | $gFontFamily : "Microsoft YaHei"; 5 | $gBackGroundColor:#f5f5f5; 6 | $gThemeColor:#46F1AE; -------------------------------------------------------------------------------- /demo04/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /demo03/src/components/global/global.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | @import 'varible'; 3 | @import 'mediaQuery'; 4 | html { 5 | // font-size: 20px; 6 | font-family: $gFontFamily; 7 | } 8 | 9 | #main{ 10 | margin-top:10px; 11 | } 12 | -------------------------------------------------------------------------------- /demo01/src/es6test/es6test2.js: -------------------------------------------------------------------------------- 1 | //定义类 2 | class Point { 3 | constructor(x, y) { 4 | this.x = x; 5 | this.y = y; 6 | } 7 | 8 | toString() { 9 | return '(' + this.x + ', ' + this.y + ')'; 10 | } 11 | } 12 | export default Point; 13 | // module.exports = Point; -------------------------------------------------------------------------------- /demo04/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import todos from './todos' 3 | import visibilityFilter from './visibilityFilter' 4 | 5 | const todoApp = combineReducers({ 6 | todos, 7 | visibilityFilter 8 | }) 9 | 10 | export default todoApp 11 | -------------------------------------------------------------------------------- /demo01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | webpack demo 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo03/src/components/home/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import "./home.scss"; 5 | 6 | class Home extends React.Component{ 7 | render(){ 8 | return ( 9 |
这是home
10 | ); 11 | } 12 | } 13 | 14 | export default Home; 15 | -------------------------------------------------------------------------------- /demo03/src/components/story/story.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import "./story.scss"; 5 | 6 | class Story extends React.Component{ 7 | render(){ 8 | return( 9 |
这是story
10 | ); 11 | } 12 | } 13 | 14 | export default Story; 15 | -------------------------------------------------------------------------------- /demo04/src/reducers/visibilityFilter.js: -------------------------------------------------------------------------------- 1 | const visibilityFilter = (state = 'SHOW_ALL', action) => { 2 | switch (action.type) { 3 | case 'SET_VISIBILITY_FILTER': 4 | return action.filter 5 | default: 6 | return state 7 | } 8 | } 9 | 10 | export default visibilityFilter 11 | -------------------------------------------------------------------------------- /demo03/src/components/travel/travel.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import "./travel.scss"; 5 | 6 | class Travel extends React.Component{ 7 | render(){ 8 | return ( 9 |
这是travel
10 | ); 11 | } 12 | } 13 | 14 | export default Travel; 15 | -------------------------------------------------------------------------------- /demo02/src/home/home2.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | 4 | var HelloMessage = React.createClass({ 5 | render: function() { 6 | return

Hello {this.props.name}

; 7 | } 8 | }); 9 | 10 | ReactDOM.render( , 11 | document.getElementById('example2') 12 | ); -------------------------------------------------------------------------------- /demo04/src/actions/index.js: -------------------------------------------------------------------------------- 1 | let nextTodoId = 0 2 | export const addTodo = (text) => ({ 3 | type: 'ADD_TODO', 4 | id: nextTodoId++, 5 | text 6 | }) 7 | 8 | export const setVisibilityFilter = (filter) => ({ 9 | type: 'SET_VISIBILITY_FILTER', 10 | filter 11 | }) 12 | 13 | export const toggleTodo = (id) => ({ 14 | type: 'TOGGLE_TODO', 15 | id 16 | }) 17 | -------------------------------------------------------------------------------- /demo04/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Footer from './Footer' 3 | import AddTodo from '../containers/AddTodo' 4 | import VisibleTodoList from '../containers/VisibleTodoList' 5 | 6 | const App = () => ( 7 |
8 | 9 | 10 |
11 |
12 | ) 13 | 14 | export default App 15 | -------------------------------------------------------------------------------- /demo02/src/home/home1.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import "./home.css"; 5 | import "./home.scss"; 6 | 7 | class HelloMessage extends React.Component { 8 | render() { 9 | return

Hello { this.props.name }

; 10 | } 11 | } 12 | 13 | ReactDOM.render( , 14 | document.getElementById('example') 15 | ); -------------------------------------------------------------------------------- /demo03/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Our Home,Our Heart 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo02/src/test/test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | function tick() { 5 | const element = ( 6 |
7 |

Hello, world!

8 |

It is {new Date().toLocaleTimeString()}.

9 |
10 | ); 11 | ReactDOM.render( 12 | element, 13 | document.getElementById('root') 14 | ); 15 | } 16 | 17 | setInterval(tick, 1000); -------------------------------------------------------------------------------- /demo02/src/test/test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | class RButton extends React.Component{ 5 | handleClick(){ 6 | alert("点击"); 7 | } 8 | render(){ 9 | return ( 10 | 11 | ); 12 | } 13 | } 14 | 15 | ReactDOM.render( 16 | , 17 | document.getElementById('example') 18 | ); -------------------------------------------------------------------------------- /demo02/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | webpack demo 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo04/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { createStore } from 'redux' 4 | import { Provider } from 'react-redux' 5 | import App from './components/App' 6 | import reducer from './reducers' 7 | 8 | const store = createStore(reducer) 9 | 10 | render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ) 16 | -------------------------------------------------------------------------------- /demo01/server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config'); 4 | 5 | new WebpackDevServer(webpack(config), { 6 | publicPath: config.output.publicPath, 7 | hot: true, 8 | historyApiFallback: true 9 | }).listen(3000, 'localhost', function(err, result) { 10 | if (err) { 11 | return console.log(err); 12 | } 13 | 14 | console.log('Listening at http://localhost:3000/') 15 | }); 16 | -------------------------------------------------------------------------------- /demo02/server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config'); 4 | 5 | new WebpackDevServer(webpack(config), { 6 | publicPath: config.output.publicPath, 7 | hot: true, 8 | historyApiFallback: true 9 | }).listen(3000, 'localhost', function(err, result) { 10 | if (err) { 11 | return console.log(err); 12 | } 13 | 14 | console.log('Listening at http://localhost:3000/') 15 | }); 16 | -------------------------------------------------------------------------------- /demo04/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import FilterLink from '../containers/FilterLink' 3 | 4 | const Footer = () => ( 5 |

6 | Show: 7 | {" "} 8 | 9 | All 10 | 11 | {", "} 12 | 13 | Active 14 | 15 | {", "} 16 | 17 | Completed 18 | 19 |

20 | ) 21 | 22 | export default Footer 23 | -------------------------------------------------------------------------------- /demo04/src/components/Todo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Todo = ({ onClick, completed, text }) => ( 5 |
  • 11 | {text} 12 |
  • 13 | ) 14 | 15 | Todo.propTypes = { 16 | onClick: PropTypes.func.isRequired, 17 | completed: PropTypes.bool.isRequired, 18 | text: PropTypes.string.isRequired 19 | } 20 | 21 | export default Todo 22 | -------------------------------------------------------------------------------- /demo02/src/app.js: -------------------------------------------------------------------------------- 1 | // var home1 = require('./home/home1.js'); 2 | var home2 = require('./home/home2.js'); 3 | import ReactDOM from 'react-dom'; 4 | 5 | // var RButton = require('./test/test.js'); 6 | // var RButton = require('./test/test2.js'); 7 | // var RButton = require('./test/test.jsx'); 8 | 9 | // var test4 = require('./test/test4.js'); 10 | // var test5 = require('./test/test5.js'); 11 | // var test6 = require('./test/test6.js'); 12 | // var test7 = require('./test/test7.js'); 13 | var test8 = require('./test/test8.js'); 14 | -------------------------------------------------------------------------------- /demo03/src/components/global/menuLi.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import ReactDOM from 'react-dom'; 3 | import {Link} from 'react-router'; 4 | class MenuLi extends React.Component{ 5 | render(){ 6 | let linkTo = this.props.name =="Home"?"/":"/"+this.props.name; 7 | let activeClass = this.props.name =="Home"?"":"active"; 8 | return ( 9 |
  • 10 | 11 | {this.props.name} 12 | 13 |
  • 14 | ); 15 | } 16 | } 17 | 18 | export default MenuLi; -------------------------------------------------------------------------------- /demo04/src/reducers/todos.js: -------------------------------------------------------------------------------- 1 | const todos = (state = [], action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return [ 5 | ...state, 6 | { 7 | id: action.id, 8 | text: action.text, 9 | completed: false 10 | } 11 | ] 12 | case 'TOGGLE_TODO': 13 | return state.map(todo => 14 | (todo.id === action.id) 15 | ? {...todo, completed: !todo.completed} 16 | : todo 17 | ) 18 | default: 19 | return state 20 | } 21 | } 22 | 23 | export default todos 24 | -------------------------------------------------------------------------------- /demo03/src/components/global/menu.scss: -------------------------------------------------------------------------------- 1 | @import 'varible'; 2 | #menu { 3 | margin: 0 auto; 4 | margin-top: 10px; 5 | ul { 6 | font-size: 0.9rem; 7 | li { 8 | display: inline-block; 9 | list-style-type: none; 10 | font-size: inherit; 11 | margin-left: 1em; 12 | line-height: 1.5; 13 | &:hover{ 14 | cursor: pointer; 15 | color: $gThemeColor; 16 | } 17 | .active{ 18 | color: $gThemeColor; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo04/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todos", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "enzyme": "^2.8.2", 7 | "react-addons-test-utils": "^15.5.1", 8 | "react-scripts": "^1.0.2" 9 | }, 10 | "dependencies": { 11 | "prop-types": "^15.5.10", 12 | "react": "^15.5.0", 13 | "react-dom": "^15.5.0", 14 | "react-redux": "^5.0.5", 15 | "redux": "^3.5.2" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "test": "react-scripts test" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo04/src/components/Link.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Link = ({ active, children, onClick }) => { 5 | if (active) { 6 | return {children} 7 | } 8 | 9 | return ( 10 | // eslint-disable-next-line 11 | { 13 | e.preventDefault() 14 | onClick() 15 | }} 16 | > 17 | {children} 18 | 19 | ) 20 | } 21 | 22 | Link.propTypes = { 23 | active: PropTypes.bool.isRequired, 24 | children: PropTypes.node.isRequired, 25 | onClick: PropTypes.func.isRequired 26 | } 27 | 28 | export default Link 29 | -------------------------------------------------------------------------------- /demo02/src/test/test2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 默认的属性 3 | * defaultProps 4 | */ 5 | import React from 'react'; 6 | import ReactDOM from 'react-dom'; 7 | 8 | class MyTitle extends React.Component{ 9 | propTypes:{ 10 | title:React.PropTypes.string.isRequired, 11 | } 12 | 13 | 14 | /*getDefaultProps(){ 15 | return { 16 | title:"默认的标题" 17 | }; 18 | }*///React.createClass使用 19 | 20 | static get defaultProps() { 21 | return { 22 | title:"默认的标题" 23 | }; 24 | } 25 | render(){ 26 | return

    {this.props.title}

    ; 27 | } 28 | } 29 | 30 | // var data = 123; 31 | 32 | ReactDOM.render( 33 | , 34 | document.getElementById("title") 35 | ) -------------------------------------------------------------------------------- /demo04/README.md: -------------------------------------------------------------------------------- 1 | # Redux Todos Example 2 | 3 | > 例子源码地址 https://github.com/reactjs/redux/tree/master/examples/todos 4 | 5 | 例子教程: [http://cn.redux.js.org/docs/basics/index.html](http://cn.redux.js.org/docs/basics/index.html) 6 | 概念教程:[阮一峰的日志](http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html) 7 | 8 | 整体的概念逻辑可以看阮老师的三篇文章,这里贴出来文章中的[一张图](http://www.ruanyifeng.com/blogimg/asset/2016/bg2016091802.jpg) 9 | 10 | ## 文件夹说明 11 | - src 12 | + actions Actions Creators有关 13 | + components 一般的UI组件,不负责逻辑 14 | + containers 处理逻辑的组件,使用connect方法从一般UI组件转化而来 15 | + reducers Reducers 16 | - index.js 入口 Store在此创建 17 | - public 18 | + index.html 存放入口网页html 19 | 20 | 21 | -------------------------------------------------------------------------------- /demo02/src/test/test5.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 表单 3 | */ 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | 7 | class Input extends React.Component{ 8 | constructor(props){ 9 | super(props); 10 | this.state = { 11 | value:"zry" 12 | }; 13 | this.handleChange = this.handleChange.bind(this); 14 | } 15 | handleChange(event){ 16 | this.setState({value:event.target.value}); 17 | } 18 | render(){ 19 | var value = this.state.value; 20 | return( 21 |
    22 | 23 |

    {value}

    24 |
    25 | ); 26 | } 27 | } 28 | 29 | ReactDOM.render( 30 | , 31 | document.getElementById('example') 32 | ); -------------------------------------------------------------------------------- /demo03/src/components/global/menu.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import MenuLi from './menuLi'; 5 | 6 | import './menu.scss'; 7 | 8 | let menuLis = ["Home","Story","Travel","TimeLine","Future"]; 9 | 10 | class MenuUl extends React.Component{ 11 | render(){ 12 | return( 13 |
      14 | { 15 | menuLis.map(function(menuLi) { 16 | return 17 | }) 18 | } 19 |
    20 | ); 21 | } 22 | } 23 | 24 | class Nav extends React.Component{ 25 | render(){ 26 | return( 27 | 32 | ) 33 | } 34 | } 35 | 36 | 37 | export default Nav; -------------------------------------------------------------------------------- /demo04/src/components/TodoList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Todo from './Todo' 4 | 5 | const TodoList = ({ todos, onTodoClick }) => ( 6 |
      7 | {todos.map(todo => 8 | onTodoClick(todo.id)} 12 | /> 13 | )} 14 |
    15 | ) 16 | 17 | TodoList.propTypes = { 18 | todos: PropTypes.arrayOf(PropTypes.shape({ 19 | id: PropTypes.number.isRequired, 20 | completed: PropTypes.bool.isRequired, 21 | text: PropTypes.string.isRequired 22 | }).isRequired).isRequired, 23 | onTodoClick: PropTypes.func.isRequired 24 | } 25 | 26 | export default TodoList 27 | -------------------------------------------------------------------------------- /demo04/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Redux Todos Example 7 | 8 | 9 |
    10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /demo04/src/actions/index.spec.js: -------------------------------------------------------------------------------- 1 | import * as actions from './index' 2 | 3 | describe('todo actions', () => { 4 | it('addTodo should create ADD_TODO action', () => { 5 | expect(actions.addTodo('Use Redux')).toEqual({ 6 | type: 'ADD_TODO', 7 | id: 0, 8 | text: 'Use Redux' 9 | }) 10 | }) 11 | 12 | it('setVisibilityFilter should create SET_VISIBILITY_FILTER action', () => { 13 | expect(actions.setVisibilityFilter('active')).toEqual({ 14 | type: 'SET_VISIBILITY_FILTER', 15 | filter: 'active' 16 | }) 17 | }) 18 | 19 | it('toggleTodo should create TOGGLE_TODO action', () => { 20 | expect(actions.toggleTodo(1)).toEqual({ 21 | type: 'TOGGLE_TODO', 22 | id: 1 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /demo04/src/containers/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import { addTodo } from '../actions' 4 | 5 | let AddTodo = ({ dispatch }) => { 6 | let input 7 | 8 | return ( 9 |
    10 |
    { 11 | e.preventDefault() 12 | if (!input.value.trim()) { 13 | return 14 | } 15 | dispatch(addTodo(input.value)) 16 | input.value = '' 17 | }}> 18 | { 19 | input = node 20 | }} /> 21 | 24 |
    25 |
    26 | ) 27 | } 28 | AddTodo = connect()(AddTodo) //connect 使其为容器组件 29 | 30 | export default AddTodo 31 | -------------------------------------------------------------------------------- /demo02/src/test/test3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 与真实的DOM交互 3 | * ref的使用 4 | */ 5 | import React from 'react'; 6 | import ReactDOM from 'react-dom'; 7 | 8 | class MyInput extends React.Component{ 9 | constructor(props){ 10 | super(props); 11 | console.log(this); 12 | 13 | this.handleClick = this.handleClick.bind(this); 14 | //or 15 | } 16 | handleClick(){ 17 | console.log(this); 18 | this.refs.myTextInput.focus(); 19 | } 20 | 21 | render(){ 22 | return ( 23 |
    24 | 25 | 26 |
    27 | ); 28 | } 29 | } 30 | 31 | ReactDOM.render( 32 | , 33 | document.getElementById('example') 34 | ); -------------------------------------------------------------------------------- /demo02/src/test/test4.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this.state 3 | */ 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | 7 | class LikeButton extends React.Component{ 8 | /*getInitialState(){ 9 | return {liked:false}; 10 | }*/ 11 | constructor(props){ 12 | super(props); 13 | 14 | this.state = { 15 | liked:false 16 | }; 17 | this.handleClick = this.handleClick.bind(this); 18 | } 19 | handleClick(){ 20 | this.setState({liked:!this.state.liked}); 21 | } 22 | render(){ 23 | var text = this.state.liked ? 'like' : 'haven\'t liked'; 24 | return ( 25 |

    26 | You {text} this. Click to toggle. 27 |

    28 | ); 29 | } 30 | } 31 | 32 | ReactDOM.render( 33 | , 34 | document.getElementById('example') 35 | ); -------------------------------------------------------------------------------- /demo01/src/app.js: -------------------------------------------------------------------------------- 1 | // var p = require("./es6test/es6test1.js"); //(1,2) --es6 2 | 3 | /* var React = require('react'); 4 | var ReactDOM = require('react-dom'); 5 | 6 | var HelloMessage = React.createClass({ 7 | render: function() { 8 | return

    Hello {this.props.name}

    ; 9 | } 10 | }); 11 | 12 | ReactDOM.render( , 13 | document.getElementById('example') 14 | );*/ 15 | 16 | import React from 'react'; 17 | import ReactDOM from 'react-dom'; 18 | 19 | import "./home/home.css"; 20 | import "./home/home.scss"; 21 | 22 | class HelloMessage extends React.Component { 23 | render() { 24 | return

    Hello { this.props.name }

    ; 25 | } 26 | } 27 | 28 | ReactDOM.render( , 29 | document.getElementById('example') 30 | ); 31 | -------------------------------------------------------------------------------- /demo04/src/containers/VisibleTodoList.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { toggleTodo } from '../actions' 3 | import TodoList from '../components/TodoList' 4 | 5 | const getVisibleTodos = (todos, filter) => { 6 | switch (filter) { 7 | case 'SHOW_ALL': 8 | return todos 9 | case 'SHOW_COMPLETED': 10 | return todos.filter(t => t.completed) 11 | case 'SHOW_ACTIVE': 12 | return todos.filter(t => !t.completed) 13 | default: 14 | throw new Error('Unknown filter: ' + filter) 15 | } 16 | } 17 | 18 | const mapStateToProps = (state) => ({ 19 | todos: getVisibleTodos(state.todos, state.visibilityFilter) 20 | }) 21 | 22 | const mapDispatchToProps = { 23 | onTodoClick: toggleTodo 24 | } 25 | 26 | const VisibleTodoList = connect( 27 | mapStateToProps, 28 | mapDispatchToProps 29 | )(TodoList) 30 | 31 | export default VisibleTodoList 32 | -------------------------------------------------------------------------------- /demo03/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Router,Route,IndexRoute,hashHistory} from 'react-router'; 4 | 5 | import './components/global/global.scss'; 6 | 7 | import Nav from './components/global/menu'; 8 | import Home from './components/home/home'; 9 | import Story from './components/story/story'; 10 | import Travel from './components/travel/travel'; 11 | 12 | 13 | class App extends React.Component{ 14 | render(){ 15 | return( 16 |
    17 |
    20 | ) 21 | } 22 | } 23 | 24 | ReactDOM.render(( 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ),document.body 33 | ); 34 | 35 | -------------------------------------------------------------------------------- /demo02/src/test/test7.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AJAX 3 | */ 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import $ from 'jquery'; 7 | 8 | class UserGist extends React.Component { 9 | constructor(props){ 10 | super(props); 11 | this.state ={ 12 | username: '', 13 | lastGistUrl: '' 14 | } 15 | } 16 | componentDidMount(){ 17 | $.get(this.props.source, (result)=> { 18 | let lastGist = result[0]; 19 | this.setState({ 20 | username: lastGist.owner.login, 21 | lastGistUrl: lastGist.html_url 22 | }); 23 | }); 24 | } 25 | render() { 26 | return ( 27 |
    28 | {this.state.username}'s last gist is 29 | here. 30 |
    31 | ); 32 | } 33 | } 34 | 35 | ReactDOM.render( 36 | , 37 | document.getElementById('example') 38 | ); -------------------------------------------------------------------------------- /demo04/src/containers/FilterLink.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { setVisibilityFilter } from '../actions' 3 | import Link from '../components/Link' 4 | 5 | //建立state对象到props对象的映射关系 6 | //mapStateToProps会订阅 Store,每当state更新的时候, 7 | //就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染 8 | /* 9 | All 10 | 11 | ownProps.filter 为"SHOW_ALL" 12 | */ 13 | const mapStateToProps = (state, ownProps) => ({ 14 | active: ownProps.filter === state.visibilityFilter 15 | }) 16 | //用来建立 UI 组件的参数到store.dispatch方法的映射 17 | //dispatch和ownProps(容器组件的props对象)两个参数 18 | 19 | const mapDispatchToProps = (dispatch, ownProps) => ({ 20 | onClick: () => { 21 | dispatch(setVisibilityFilter(ownProps.filter)) 22 | } 23 | }) 24 | 25 | const FilterLink = connect( 26 | mapStateToProps, 27 | mapDispatchToProps 28 | )(Link) //用Link组件生成容器组件,容器组件负责逻辑 29 | 30 | export default FilterLink 31 | -------------------------------------------------------------------------------- /demo01/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server --hot --progress --colors", 9 | "build": "webpack --progress --colors" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "zry" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "babel-core": "^6.18.0", 19 | "babel-loader": "^6.2.7", 20 | "babel-preset-es2015": "^6.18.0", 21 | "babel-preset-react": "^6.16.0", 22 | "css-loader": "^0.25.0", 23 | "jsx-loader": "^0.13.2", 24 | "node-sass": "^3.10.1", 25 | "react": "^15.3.2", 26 | "react-dom": "^15.3.2", 27 | "react-hot-loader": "^3.0.0-beta.6", 28 | "sass-loader": "^4.0.2", 29 | "style-loader": "^0.13.1", 30 | "webpack": "^1.13.3", 31 | "webpack-dev-server": "^1.16.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /demo02/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server --hot --progress --colors", 9 | "build": "webpack --progress --colors" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/zrysmt/react-demo.git" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "babel-core": "^6.18.0", 19 | "babel-loader": "^6.2.7", 20 | "babel-preset-es2015": "^6.18.0", 21 | "babel-preset-react": "^6.16.0", 22 | "css-loader": "^0.25.0", 23 | "jsx-loader": "^0.13.2", 24 | "node-sass": "^3.10.1", 25 | "react": "^15.3.2", 26 | "react-dom": "^15.3.2", 27 | "react-hot-loader": "^3.0.0-beta.6", 28 | "sass-loader": "^4.0.2", 29 | "style-loader": "^0.13.1", 30 | "webpack": "^1.13.3", 31 | "webpack-dev-server": "^1.16.2" 32 | }, 33 | "dependencies": { 34 | "jquery": "^3.1.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /demo02/src/test/test6.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 组件的生命周期: 3 | * Mounting:已插入真实 DOM 4 | * Updating:正在被重新渲染 5 | * Unmounting:已移出真实 DOM 6 | */ 7 | import React from 'react'; 8 | import ReactDOM from 'react-dom'; 9 | 10 | class Hello extends React.Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | opacity: 1.0 15 | }; 16 | 17 | } 18 | componentDidMount() { 19 | this.timer = setInterval( 20 | () => { 21 | var opacity = this.state.opacity; 22 | opacity -= .05; 23 | if (opacity < 0.1) { 24 | opacity = 1.0; 25 | } 26 | this.setState({ 27 | opacity: opacity 28 | }); 29 | },100); 30 | } 31 | render() { 32 | return (
    34 | Hello { this.props.name }
    35 | ); 36 | } 37 | } 38 | 39 | 40 | ReactDOM.render( , 41 | document.getElementById('example') 42 | ); 43 | -------------------------------------------------------------------------------- /demo03/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "SET DEBUG=true && webpack-dev-server --history-api-fallback --progress --profile --inline --colors --hot --port 8080 --open", 8 | "build": "SET DEBUG=false && webpack --progress --profile --colors", 9 | "Mac-build": "DEBUG=false && webpack --progress --profile --colors" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/zrysmt/react-demo.git" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "babel-core": "^6.18.0", 19 | "babel-loader": "^6.2.7", 20 | "babel-preset-es2015": "^6.18.0", 21 | "babel-preset-react": "^6.16.0", 22 | "css-loader": "^0.25.0", 23 | "html-webpack-plugin": "^2.29.0", 24 | "jsx-loader": "^0.13.2", 25 | "node-sass": "^3.10.1", 26 | "path": "^0.12.7", 27 | "react": "^15.3.2", 28 | "react-dom": "^15.3.2", 29 | "react-hot-loader": "^3.0.0-beta.6", 30 | "sass-loader": "^4.0.2", 31 | "style-loader": "^0.13.1", 32 | "webpack": "^1.13.3", 33 | "webpack-dev-server": "^1.16.2" 34 | }, 35 | "dependencies": { 36 | "jquery": "^3.1.1", 37 | "react-router": "^3.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo03/src/components/global/reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /demo01/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | /*entry:{ 5 | page: "./src/app.js" 6 | },*/ 7 | //修改entry 8 | entry: [ 9 | "webpack-dev-server/client?http://127.0.0.1:3000", 10 | "webpack/hot/only-dev-server", 11 | "./src/app.js" 12 | ], 13 | output: { 14 | path: __dirname, 15 | filename: "build/bundle.js", 16 | publicPath: "/build" 17 | }, 18 | module: { 19 | loaders: [ 20 | // { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },//支持es6 21 | // {test:/\.js?$/,exclude: /node_modules/,loader:'jsx-loader?harmony'},//支持react 22 | // {test:/\.js?$/,exclude: /node_modules/,loader:'babel?presets[]=react,presets[]=es2015'},//同时支持es6 react或者 23 | { test: /\.js?$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'react'] } }, //同时支持es6 react 24 | { test: /\.css$/, loader: "style!css" }, 25 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 26 | ] 27 | }, 28 | resolve: { 29 | extensions: ['', '.js', '.json'] 30 | }, 31 | plugins: [ 32 | new webpack.NoErrorsPlugin(), //允许错误不打断程序 33 | new webpack.HotModuleReplacementPlugin() //webpack热替换插件 34 | ], 35 | devtool: 'source-map' 36 | }; 37 | -------------------------------------------------------------------------------- /demo02/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | /*entry:{ 5 | page: "./src/app.js" 6 | },*/ 7 | //修改entry 8 | entry: [ 9 | "webpack-dev-server/client?http://127.0.0.1:3000", 10 | "webpack/hot/only-dev-server", 11 | "./src/app.js" 12 | ], 13 | output: { 14 | path: __dirname, 15 | filename: "build/bundle.js", 16 | publicPath: "/build" 17 | }, 18 | module: { 19 | loaders: [ 20 | // { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },//支持es6 21 | // {test:/\.js?$/,exclude: /node_modules/,loader:'jsx-loader?harmony'},//支持react 22 | // {test:/\.js?$/,exclude: /node_modules/,loader:'babel?presets[]=react,presets[]=es2015'},//同时支持es6 react或者 23 | { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'react'] } }, //同时支持es6 react 24 | { test: /\.css$/, loader: "style!css" }, 25 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 26 | ] 27 | }, 28 | resolve: { 29 | extensions: ['', '.js', '.json'] 30 | }, 31 | plugins: [ 32 | new webpack.NoErrorsPlugin(), //允许错误不打断程序 33 | new webpack.HotModuleReplacementPlugin() //webpack热替换插件 34 | ], 35 | devtool: 'source-map' 36 | }; 37 | -------------------------------------------------------------------------------- /demo03/src/components/global/mediaQuery.scss: -------------------------------------------------------------------------------- 1 | $viewport-increment: 0.1px; // customize to suite 2 | // Desktop 3 | $desktop: 1024px; // customize to suite 4 | $pre-desktop: $desktop - $viewport-increment; 5 | // Tablet 6 | $tablet: 768px; // customize to suite 7 | $pre-tablet: $tablet - $viewport-increment; 8 | // Palm 9 | $palm: 640px; // customize to suite 10 | $post-palm: $palm + $viewport-increment; 11 | // Constraints 12 | $min: min-width; 13 | $max: max-width; 14 | 15 | @mixin mediaQuery($constraint, $viewport1, $viewport2: null) { 16 | @if $constraint==$min { 17 | @media screen and ($min: $viewport1) { 18 | @content; 19 | } 20 | } 21 | @else if $constraint==$max { 22 | @media screen and ($max: $viewport1) { 23 | @content; 24 | } 25 | } 26 | @else { 27 | @media screen and ($min: $viewport1) and ($max: $viewport2) { 28 | @content; 29 | } 30 | } 31 | } 32 | 33 | html { 34 | @include mediaQuery($max, $palm) { 35 | font-size: 75% !important; 36 | } 37 | @include mediaQuery(null, $post-palm, $pre-tablet) { 38 | font-size: 87.5% !important; 39 | } 40 | @include mediaQuery($min, $tablet) { 41 | font-size: 90% !important; 42 | } 43 | @include mediaQuery($min, $desktop) { 44 | font-size: 125% !important; 45 | } 46 | } -------------------------------------------------------------------------------- /demo02/src/test/test8.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AJAX--Promise 3 | */ 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import $ from 'jquery'; 7 | 8 | class RepoList extends React.Component{ 9 | constructor(props){ 10 | super(props); 11 | this.state ={ 12 | loading: true, error: null, data: null 13 | } 14 | } 15 | componentDidMount(){ 16 | this.props.promise.then( 17 | value =>this.setState({loading:false,data:value}), 18 | error =>this.setState({loading:false,error:error}) 19 | ) 20 | } 21 | render() { 22 | if (this.state.loading) { 23 | return Loading...; 24 | } 25 | else if (this.state.error !== null) { 26 | return Error: {this.state.error.message}; 27 | } 28 | else { 29 | var repos = this.state.data.items; 30 | var repoList = repos.map(function (repo) { 31 | return ( 32 |
  • 33 | {repo.name} ({repo.stargazers_count} stars)
    {repo.description} 34 |
  • 35 | ); 36 | }); 37 | return ( 38 |
    39 |

    Most Popular JavaScript Projects in Github

    40 |
      {repoList}
    41 |
    42 | ); 43 | } 44 | } 45 | } 46 | 47 | ReactDOM.render( 48 | , 51 | document.getElementById('example') 52 | ); -------------------------------------------------------------------------------- /demo04/src/reducers/todos.spec.js: -------------------------------------------------------------------------------- 1 | import todos from './todos' 2 | 3 | describe('todos reducer', () => { 4 | it('should handle initial state', () => { 5 | expect( 6 | todos(undefined, {}) 7 | ).toEqual([]) 8 | }) 9 | 10 | it('should handle ADD_TODO', () => { 11 | expect( 12 | todos([], { 13 | type: 'ADD_TODO', 14 | text: 'Run the tests', 15 | id: 0 16 | }) 17 | ).toEqual([ 18 | { 19 | text: 'Run the tests', 20 | completed: false, 21 | id: 0 22 | } 23 | ]) 24 | 25 | expect( 26 | todos([ 27 | { 28 | text: 'Run the tests', 29 | completed: false, 30 | id: 0 31 | } 32 | ], { 33 | type: 'ADD_TODO', 34 | text: 'Use Redux', 35 | id: 1 36 | }) 37 | ).toEqual([ 38 | { 39 | text: 'Run the tests', 40 | completed: false, 41 | id: 0 42 | }, { 43 | text: 'Use Redux', 44 | completed: false, 45 | id: 1 46 | } 47 | ]) 48 | 49 | expect( 50 | todos([ 51 | { 52 | text: 'Run the tests', 53 | completed: false, 54 | id: 0 55 | }, { 56 | text: 'Use Redux', 57 | completed: false, 58 | id: 1 59 | } 60 | ], { 61 | type: 'ADD_TODO', 62 | text: 'Fix the tests', 63 | id: 2 64 | }) 65 | ).toEqual([ 66 | { 67 | text: 'Run the tests', 68 | completed: false, 69 | id: 0 70 | }, { 71 | text: 'Use Redux', 72 | completed: false, 73 | id: 1 74 | }, { 75 | text: 'Fix the tests', 76 | completed: false, 77 | id: 2 78 | } 79 | ]) 80 | }) 81 | 82 | it('should handle TOGGLE_TODO', () => { 83 | expect( 84 | todos([ 85 | { 86 | text: 'Run the tests', 87 | completed: false, 88 | id: 1 89 | }, { 90 | text: 'Use Redux', 91 | completed: false, 92 | id: 0 93 | } 94 | ], { 95 | type: 'TOGGLE_TODO', 96 | id: 1 97 | }) 98 | ).toEqual([ 99 | { 100 | text: 'Run the tests', 101 | completed: true, 102 | id: 1 103 | }, { 104 | text: 'Use Redux', 105 | completed: false, 106 | id: 0 107 | } 108 | ]) 109 | }) 110 | 111 | }) 112 | -------------------------------------------------------------------------------- /demo03/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var HtmlwebpackPlugin = require('html-webpack-plugin'); 4 | 5 | // 定义当前是否处于开发debug阶段 6 | var isDebug = JSON.stringify(JSON.parse(process.env.DEBUG || 'false')); 7 | 8 | // 根据isDebug变量定义相关config变量 9 | var configVarObj = {}; 10 | if(isDebug === 'true') { 11 | console.log('I am in debuging............'); 12 | configVarObj = { 13 | htmlPath: 'index.html', // 定义输出html文件路径 14 | // devtool: 'cheap-source-map' // 生成sourcemap,便于开发调试 15 | devtool: 'eval' // 生成sourcemap,便于开发调试 16 | }; 17 | } else { 18 | console.log('I am in releasing............'); 19 | configVarObj = { 20 | htmlPath: /*cjebTemplateFolder + */'/index.html', // 定义输出html文件路径 21 | devtool: '' 22 | }; 23 | } 24 | 25 | module.exports = { 26 | context: path.join(__dirname, 'src'), 27 | entry: { 28 | app:"./app.js", 29 | vendors: [ 30 | 'jquery' 31 | ] 32 | }, 33 | output: { 34 | path: path.resolve(__dirname, 'output'), 35 | // 输出文件名 36 | filename: 'js'+'/[name].min.js?[hash]', 37 | // cmd、amd异步加载脚本配置名称 38 | chunkFilename: 'js'+'/[name].chunk.js?[hash]', 39 | publicPath: '' 40 | }, 41 | module: { 42 | loaders: [ 43 | // { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },//支持es6 44 | // {test:/\.js?$/,exclude: /node_modules/,loader:'jsx-loader?harmony'},//支持react 45 | // {test:/\.js?$/,exclude: /node_modules/,loader:'babel?presets[]=react,presets[]=es2015'},//同时支持es6 react或者 46 | { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'react'] } }, //同时支持es6 react 47 | { test: /\.css$/, loader: "style!css" }, 48 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 49 | ] 50 | }, 51 | resolve: { 52 | extensions: ['', '.js', '.jsx','.json'] 53 | }, 54 | devtool: configVarObj.devtool, 55 | plugins: [ 56 | new HtmlwebpackPlugin({ 57 | title: 'test', 58 | template: path.join(__dirname, './index.html'), 59 | filename: 'index.html', 60 | minify: { 61 | minifyJS: true, 62 | removeComments: true, 63 | minifyCSS: true 64 | }, 65 | }), 66 | new webpack.optimize.UglifyJsPlugin({ 67 | compress: { 68 | warnings: false 69 | } 70 | }), 71 | // new ExtractTextPlugin("output/[name].css"),//独立css文件 72 | // new webpack.optimize.CommonsChunkPlugin('vendors', 'js/[name].chunk.js?[hash]'), 73 | /* new webpack.ProvidePlugin({ 74 | "$": "jquery" 75 | }),*/ 76 | //定义全局变量 77 | new webpack.DefinePlugin({ 78 | __DEV__: isDebug 79 | }) 80 | ] 81 | }; 82 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | ## 文件夹说明 3 | + demo01 是下面博文的主要demo,介绍下怎么配置 4 | + demo02 [阮一峰React入门案例](http://www.ruanyifeng.com/blog/2015/03/react.html)改造成使用ES6语法形式 5 | + demo03 React-router的一个完整例子,我的[另外一篇博客](http://blog.csdn.net/future_todo/article/details/53036638)的demo 6 | >http://blog.csdn.net/future_todo/article/details/53036638 7 | + demo04 redux的一个简单的例子 8 | 9 | ## 一步一步讲解react+webpack的配置(demo01) 10 | 看了很多博客,大都是把配置文件一笔带过,或者干脆不给出配置文件,然而环境搭建对于新手来说是既困难又重要,显然网络上的博客不利于新手开始学习。 11 | BZ打算从从头开始,一步一步配置webpack,能够使用ES6+React组合开发,废话少说让我们一起来开始Webpack+ES6+React之旅。 12 | 13 | 14 | > 可以在我的[github](https://github.com/zrysmt/react-demo) 中clone或者fork 15 | https://github.com/zrysmt/react-demo 16 | 17 | 18 | 当然你也可以使用本博文最后总结的部分或者从我的[github](https://github.com/zrysmt/react-demo)中获得,全部安装插件`npm install`,然后执行`webpack`就可以编译了。 19 | - 使用命令`npm init`新建包管理文件 20 | - 安装webpack ,`npm i --save-dev webpack@1.13.3`,注意:这里用的版本是1.x(@1.13.3)版本的。忽略(@1.13.3)直接下载的会是新版本的,写法会有些改变; 21 | - 在项目根目录下新增`webpack.config.js`文件,它的基本结构是: 22 | 23 | 24 | ```javascript 25 | var webpack = require('webpack'); 26 | module.exports = { 27 | entry:{ 28 | page: "./src/app.js" 29 | }, 30 | output: { 31 | path: './build', 32 | filename: "bundle.js" 33 | }, 34 | module: { 35 | loaders: [ 36 | {test:/\.js$/,exclude: /node_modules/,loader:''},//加载器在这里 37 | //下面我们会在这里增加 38 | ] 39 | } 40 | }; 41 | ``` 42 | # 1.支持es6 43 | ```bash 44 | npm i --save-dev babel-core babel-loader babel-preset-es2015 45 | ``` 46 | webpack.config.js 47 | ```javascript 48 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' } 49 | ``` 50 | .babelrc 51 | ```javascript 52 | { 53 | "presets": [ 54 | "es2015" 55 | ] 56 | } 57 | ``` 58 | 入口app.js,es6语法 59 | ```javascript 60 | var p = require("./es6test/es6test1.js") 61 | ``` 62 | ```javascript 63 | //es6test1.js 64 | import Point from './es6test2'; 65 | let p = new Point(1,2); 66 | console.log(p.toString()); 67 | //es6test2.js 68 | class Point { 69 | constructor(x, y) { 70 | this.x = x; 71 | this.y = y; 72 | } 73 | 74 | 75 | toString() { 76 | return '(' + this.x + ', ' + this.y + ')'; 77 | } 78 | } 79 | export default Point; 80 | ``` 81 | 82 | 83 | # 2. 支持React语法 84 | ```javascript 85 | {test:/\.jsx$/,exclude: /node_modules/,loader:'jsx-loader?harmony'} 86 | ``` 87 | 88 | 89 | 入口文件app.js 90 | ```javascript 91 | var React = require('react'); 92 | var ReactDOM = require('react-dom'); 93 | 94 | 95 | var HelloMessage = React.createClass({ 96 | render: function() { 97 | return

    Hello {this.props.name}

    ; 98 | } 99 | }); 100 | 101 | 102 | ReactDOM.render( , 103 | document.getElementById('example') 104 | ); 105 | ``` 106 | # 3. React和es6同时支持 107 | - 安装 108 | 109 | 110 | ```bash 111 | npm i --save-dev babel-preset-react 112 | ``` 113 | 注意:.babelrc文件和上面的是一样也是必须的。 114 | webpack.config.js 115 | ```javascript 116 | {test:/\.jsx?$/,exclude: /node_modules/,loader: 'babel', query: {presets: ['es2015', 'react']}}, 117 | //同时支持es6 react 或者下面的写法都可以 118 | {test:/\.jsx?$/,exclude: /node_modules/,loader:'babel?presets[]=react,presets[]=es2015'}, 119 | //同时支持es6 react 120 | ``` 121 | 使用React,es6语法 122 | ```javascript 123 | import React from 'react'; 124 | import ReactDOM from 'react-dom'; 125 | 126 | 127 | class HelloMessage extends React.Component { 128 | render() { 129 | return

    Hello { this.props.name }

    ; 130 | } 131 | } 132 | ReactDOM.render( , 133 | document.getElementById('example') 134 | ); 135 | ``` 136 | # 4. 其余配置 137 | ## css/sass 138 | - 安装 139 | ```bash 140 | npm i --save-dev style-loader css-loader sass-loader node-sass 141 | ``` 142 | - webpack.config.js 143 | ```javascript 144 | { test: /\.css$/, loader: "style!css" }, 145 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 146 | ``` 147 | 使用很简单 148 | ```javascript 149 | import "./home/home.css"; 150 | import "./home/home.scss"; 151 | ``` 152 | ## css文件可以独立出来 153 | ``` 154 | npm i --save-dev extract-text-webpack-plugin 155 | ``` 156 | 配置 157 | ``` 158 | /*下面两行的作用是分离css*/ 159 | { test: /\.css$/, loader:ExtractTextPlugin.extract("style-loader", "css-loader") }, 160 | { test: /\.scss$/, loader:ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader") }, //sass 161 | ``` 162 | ## 图片加载 163 | ```bash 164 | npm i --save-dev url-loader 165 | ``` 166 | 配置webpack.config.js 167 | ``` 168 | {test: /\.(png|jpg)$/, 169 | exclude: /node_modules/, 170 | loader: 'url?limit=8192'} 171 | ``` 172 | 意思是大于8192kb的图片会以base64的形式加载 173 | 174 | 使用: 175 | ``` 176 | 177 | //或者在css中直接使用 178 | background: url(imgs/toolbar.png) no-repeat; 179 | ``` 180 | ## path路径管理 181 | 有时候在配置文件中经常会用到路径管理 182 | ``` 183 | npm i --save-dev path 184 | ``` 185 | 在配置文件中 186 | ``` 187 | var path = require('path'); 188 | 189 | output: { 190 | path: path.resolve(__dirname, 'output'), 191 | }, 192 | ``` 193 | 194 | # 5. 基本功能配置总结: 195 | package.json 196 | ```javascript 197 | { 198 | "name": "react-demo", 199 | "version": "1.0.0", 200 | "description": "", 201 | "main": "index.js", 202 | "scripts": { 203 | "test": "echo \"Error: no test specified\" && exit 1", 204 | "start": "webpack-dev-server --hot --progress --colors", 205 | "build": "webpack --progress --colors" 206 | }, 207 | "repository": { 208 | "type": "git", 209 | "url": "zry" 210 | }, 211 | "author": "", 212 | "license": "ISC", 213 | "devDependencies": { 214 | "babel-core": "^6.18.0", 215 | "babel-loader": "^6.2.7", 216 | "babel-preset-es2015": "^6.18.0", 217 | "babel-preset-react": "^6.16.0", 218 | "css-loader": "^0.25.0", 219 | "jsx-loader": "^0.13.2", 220 | "node-sass": "^3.10.1", 221 | "react": "^15.3.2", 222 | "react-dom": "^15.3.2", 223 | "sass-loader": "^4.0.2", 224 | "style-loader": "^0.13.1", 225 | "webpack": "^1.13.3" 226 | } 227 | } 228 | ``` 229 | webpack.config.js 230 | ```javascript 231 | var webpack = require('webpack'); 232 | 233 | 234 | module.exports = { 235 | entry:{ 236 | page: "./src/app.js" 237 | }, 238 | output: { 239 | path: './build', 240 | filename: "bundle.js" 241 | }, 242 | module: { 243 | loaders: [ 244 | // { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },//支持es6 245 | // {test:/\.jsx?$/,exclude: /node_modules/,loader:'jsx-loader?harmony'},//支持react 246 | // {test:/\.jsx?$/,exclude: /node_modules/,loader:'babel?presets[]=react,presets[]=es2015'},//同时支持es6 react或者 247 | {test:/\.jsx?$/,exclude: /node_modules/,loader: 'babel', query: {presets: ['es2015', 'react']}},//同时支持es6 react 248 | {test: /\.(png|jpg)$/, exclude: /node_modules/, loader: 'url?limit=8192'}, 249 | /*下面两行的作用是分离css*/ 250 | /*{ test: /\.css$/, loader:ExtractTextPlugin.extract("style-loader", "css-loader") }, 251 | { test: /\.scss$/, loader:ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader") }, //sass加载器*/ 252 | { test: /\.css$/, loader: "style!css" }, 253 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 254 | ] 255 | }, 256 | resolve: { 257 | extensions: ['', '.js', '.json'] 258 | }, 259 | plugins: [ 260 | // new ExtractTextPlugin("output/[name].css"),//独立css文件 261 | new webpack.NoErrorsPlugin() 262 | ], 263 | devtool: 'source-map' 264 | }; 265 | ``` 266 | 别忘了.babelrc文件 267 | ```javascript 268 | { 269 | "presets": [ 270 | "es2015" 271 | ] 272 | } 273 | ``` 274 | # 6. React支持热插拔 275 | webpack-dev-server支持热插拔(热替换 HRM),使用HRM功能也有两种方式:命令行方式(推荐)和Node.js API 方式。 276 | ## 6.1 Node.js API方式 277 | Node.js API方式需要做三个配置: 278 | 1) 把`webpack/hot/dev-server`加入到webpack配置文件的entry项; 279 | 2) 把`new webpack.HotModuleReplacementPlugin()`加入到webpack配置文件的plugins项; 280 | 3) 把`hot:true`加入到webpack-dev-server的配置项里面。 281 | 282 | 使用 react 编写代码时,能让修改的部分自动刷新。但这和自动刷新网页是不同的,因为 hot-loader 并不会刷新网页,而仅仅是替换你修改的部分 283 | - 安装 284 | ```bash 285 | npm i --save-dev react-hot-loader webpack-dev-server 286 | ``` 287 | - 修改.babelrc 288 | ```javascript 289 | { 290 | "presets": [ 291 | "es2015" 292 | ], 293 | "plugins":["react-hot-loader/babel"] 294 | } 295 | ``` 296 | - 修改webpack.config.js 297 | 298 | ```javascript 299 | var webpack = require('webpack'); 300 | module.exports = { 301 | //修改:entry 302 | entry: [ 303 | "webpack-dev-server/client?http://127.0.0.1:3000", 304 | "webpack/hot/only-dev-server", 305 | "./src/app.js" 306 | ], 307 | //修改:output 308 | output: { 309 | path: __dirname, 310 | filename: "build/bundle.js", 311 | publicPath: "/build" 312 | }, 313 | module: { 314 | loaders: [ 315 | { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'react'] } }, //同时支持es6 react 316 | {test: /\.(png|jpg)$/, exclude: /node_modules/, loader: 'url?limit=8192'}, 317 | /*下面两行的作用是分离css*/ 318 | /*{ test: /\.css$/, loader:ExtractTextPlugin.extract("style-loader", "css-loader") }, 319 | { test: /\.scss$/, loader:ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader") }, //sass加载器*/ 320 | { test: /\.css$/, loader: "style!css" }, 321 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 322 | ] 323 | }, 324 | resolve: { 325 | extensions: ['', '.js', '.json'] 326 | }, 327 | plugins: [ 328 | // new ExtractTextPlugin("output/[name].css"),//独立css文件 329 | new webpack.NoErrorsPlugin(), //允许错误不打断程序 330 | new webpack.HotModuleReplacementPlugin() //增加:webpack热替换插件 331 | ], 332 | devtool: 'source-map' 333 | }; 334 | ``` 335 | - 增加server.js文件 336 | ```javascript 337 | var webpack = require('webpack'); 338 | var WebpackDevServer = require('webpack-dev-server'); 339 | var config = require('./webpack.config'); 340 | 341 | 342 | new WebpackDevServer(webpack(config), { 343 | publicPath: config.output.publicPath, 344 | hot: true, 345 | historyApiFallback: true 346 | }).listen(3000, 'localhost', function(err, result) { 347 | if (err) { 348 | return console.log(err); 349 | } 350 | console.log('Listening at http://localhost:3000/') 351 | }); 352 | 353 | 354 | ``` 355 | 如何使用?--在命令行中 356 | ```bash 357 | webpack & 358 | node server.js //启动node服务 359 | ``` 360 | 在浏览器输入`localhost:3000`即可查看结果,我们修改css文件,保存之后网页会自动刷新显示在浏览器上。js文件修改需要手动刷新一次。 361 | 362 | ## 6.2 命令行方式 363 | 命令行方式比较简单,需要加入`--inline --hot`。 364 | 例子位置在我的[github](https://github.com/zrysmt/react-demo/tree/master/demo03)。 365 | > https://github.com/zrysmt/react-demo/tree/master/demo03 366 | 367 | 这个例子中执行的命令是: 368 | ```bash 369 | SET DEBUG=true && webpack-dev-server --history-api-fallback --progress --profile --inline --colors --hot --port 8080 --open 370 | ``` 371 | 指定端口8080,可以是其它的没有被占用过的任意端口。 372 | 373 | 具体配置项也有所改变 374 | ```js 375 | var webpack = require('webpack'); 376 | var path = require('path'); 377 | var HtmlwebpackPlugin = require('html-webpack-plugin'); 378 | 379 | // 定义当前是否处于开发debug阶段 380 | var isDebug = JSON.stringify(JSON.parse(process.env.DEBUG || 'false')); 381 | 382 | // 根据isDebug变量定义相关config变量 383 | var configVarObj = {}; 384 | if(isDebug === 'true') { 385 | console.log('I am in debuging............'); 386 | configVarObj = { 387 | htmlPath: 'index.html', // 定义输出html文件路径 388 | // devtool: 'cheap-source-map' // 生成sourcemap,便于开发调试 389 | devtool: 'eval' // 生成sourcemap,便于开发调试 390 | }; 391 | } else { 392 | console.log('I am in releasing............'); 393 | configVarObj = { 394 | htmlPath: /*cjebTemplateFolder + */'/index.html', // 定义输出html文件路径 395 | devtool: '' 396 | }; 397 | } 398 | 399 | module.exports = { 400 | context: path.join(__dirname, 'src'), 401 | entry: { 402 | app:"./app.js", 403 | vendors: [ 404 | 'jquery' 405 | ] 406 | }, 407 | output: { 408 | path: path.resolve(__dirname, 'output'), 409 | // 输出文件名 410 | filename: 'js'+'/[name].min.js?[hash]', 411 | // cmd、amd异步加载脚本配置名称 412 | chunkFilename: 'js'+'/[name].chunk.js?[hash]', 413 | publicPath: '' 414 | }, 415 | module: { 416 | loaders: [ 417 | { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015', 'react'] } }, //同时支持es6 react 418 | { test: /\.css$/, loader: "style!css" }, 419 | { test: /\.scss$/, loader: "style!css!sass" }, //sass加载器 420 | ] 421 | }, 422 | resolve: { 423 | extensions: ['', '.js', '.jsx','.json'] 424 | }, 425 | devtool: configVarObj.devtool, 426 | plugins: [ 427 | new HtmlwebpackPlugin({ 428 | title: 'test', 429 | template: path.join(__dirname, './index.html'), 430 | filename: 'index.html', 431 | minify: { 432 | minifyJS: true, 433 | removeComments: true, 434 | minifyCSS: true 435 | }, 436 | }), 437 | new webpack.optimize.UglifyJsPlugin({ 438 | compress: { 439 | warnings: false 440 | } 441 | }), 442 | //定义全局变量 443 | new webpack.DefinePlugin({ 444 | __DEV__: isDebug 445 | }) 446 | ] 447 | }; 448 | ``` 449 | 450 | > 可以在我的[github](https://github.com/zrysmt/react-demo) https://github.com/zrysmt/react-demo中clone或者fork 451 | 452 | 453 | 参考阅读: 454 | - [react-facebook官网](https://facebook.github.io/react/docs/hello-world.html) 455 | - [react中文版-极客学院](http://wiki.jikexueyuan.com/project/react/) 456 | - [React 入门实例教程--阮一峰](http://www.ruanyifeng.com/blog/2015/03/react.html) 457 | - [【译】 在 React.js 中使用 ES6+](http://www.open-open.com/lib/view/open1442217632805.html) 458 | - [React.js中常用的ES6写法总结](http://blog.csdn.net/haoshidai/article/details/52244620) 459 | - [使用 react-hot-loader](https://segmentfault.com/a/1190000004660311) 460 | - [WEBPACK DEV SERVER说明](http://www.jianshu.com/p/941bfaf13be1) --------------------------------------------------------------------------------