####安装最新的node.js
npm install -g n //首先安装n模块
n stable //升级Node.js到最新稳定版
n 5.0.0 //或者指定版本升级
node -v //检查更新是否成功
npm config set registry “https://registry.npm.taobao.org”
首先确保自己安装了nodejs,然后全局安装yeoman
npm install -g yo
然后直接安装脚手架
npm install -g generator-reactpackage
mkdir admins 创建一个 admins 文件
cd admins 进入 admins
yo reactpackage 生成文件
然后就会在此目录下生成以下目录结构:
├── data
│ └── test.json
├── src
│ ├── components
│ │ └── App.js
│ ├── images
│ │ └── yeoman.png
│ ├── styles
│ │ └── app.scss
│ ├── vendor
│ │ └── jquery.js
│ ├── views
│ │ └── home.html
├── node_modules
├── index.html
├── package.json
└── webpack.config.js
然后使用以下命令:
npm start
npm run build
本地运行
npm install
npm start
- Webpack + Bable + React + ReactDom + Antd + ReactRouter + less + Redux + Fetch
- 富文本编辑:react-draft-wysiwyg
- 全屏插件:screenfull
- 目录树可以直接用tree系统命令 windows,linux都支持,mac下需要手动安装
├── build.js 项目打包后的文件
├── config webpack配置文件
│ ├──...
│ ├──webpack.config.dev.js 开发环境配置
│ ├──webpack.config.prod.js 生产环境配置
├── node_modules node模块目录
├── public
│ └──index.html
├── scripts
│ ├── build.js 打包项目文件
│ ├── start.js 启动项目文件
│ └── test.js 测试项目文件
├── src
│
│
│ │ ├── actions redux中的action
│ │ ├── components 通用功能组件
│ │ ├── container 通用样式组件
│ │ ├── api
│ │ ├── pages 页面模块
│ │ ├── reducers redux中的reducer
│ │ ├── utils 工具类
│ │ │ ├── config.js 通用配置
│ │ │ ├── menu.js 菜单配置
│ │ │ └── fetch.js ajax模块
│ │ └── routes.js 前端路由
│ └── server 服务端目录
│ └── controller
├── .gitignore
├── package.json
├── README.md
└── yarn.lock
- React 的ES5、ES6写法对照表
- React 组件之间如何交流
- react-router 中的history
- react-router 按需加载
- ECMAScript 6入门
- Webpack 实例和文章
- React
- Fetch
- Fetch API
- 使用Mock.js进行独立于后端的前端开发
* Mounting:已插入真实 DOM
* Updating:正在被重新渲染
* Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
* componentWillMount :会在组件render之前执行,并且永远都只执行一次。
* componentDidMount:组件加载完毕之后立即执行
* componentWillReceiveProps: 组件接收到一个新的prop时被执行。这个方法在初始化render时不会被调用
* componentWillUpdate:组件接收到新的props或者state但还没有render时被执行
* componentDidUpdate :在组件完成更新后立即执行
- export与export default均可用于导出常量、函数、文件、模块等
- 你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用
- 在一个文件或模块中,export、import可以有多个,export default仅有一个
- 通过export方式导出,在导入时要加{ },export default则不需要
1 export
//demo1.js
export const str = 'hello'
export function f(a){return a +1}
对应的导入方式:
//demo2.js
import { str, f } from 'demo1' //也可以分开写两次,导入的时候带花括号
2 export default
//demo1.js
export default const str = 'hello world'
对应的导入方式:
//demo2.js
import str from 'demo1' //导入的时候没有花括号
使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名
let BaseUrl = "www.baidu.com";
export default BaseUrl
//一个文件内不能有多个export default
-
类似:父子:Parent 与 Child_1、Child_2、Child_1_1、Child_1_2、Child_2_1
-
兄弟:Child_1 与 Child_2、Child_1_1 与 Child_2
-
在 React 中,React 组件之间的关系为从属关系,与 DOM 元素之间的父子关系有所不同
* 通讯是单向的,数据必须是由一方传到另一方。在 React 中,父组件可以向子组件通过传 props 的方式,向子组件进行通讯
class Parent extends Component{
state = {
msg: 'start'
};
//组件加载完毕之后立即执行
componentDidMount() {
setTimeout(() => {
this.setState({
msg: 'end'
});
}, 1000);
}
render() {
return <Child_1 msg={this.state.msg} />;
}
}
class Child_1 extends Component{
render() {
return <p>{this.props.msg}</p>
}
}
- 父组件与子组件多个层级传递参数可以用...运算符(Object 剩余和展开属性),将父组件的信息,以更简洁的方式传递给更深层级的子组件。
// 通过 ... 运算符 向 Child_1_1 传递 Parent 组件的信息
class Child_1 extends Component{
render() {
return <div>
<p>{this.props.msg}</p>
<Child_1_1 {...this.props}/>
</div>
}
}
class Child_1_1 extends Component{
render() {
return <p>{this.props.msg}</p>
}
}
- 子组件向父组件通讯
class Parent extends Component{
state = {
msg: 'start'
};
transferMsg(msg) {
this.setState({
msg
});
}
render() {
return <div>
<p>child msg: {this.state.msg}</p>
<Child_1 transferMsg = {msg => this.transferMsg(msg)} />
</div>;
}
}
class Child_1 extends Component{
componentDidMount() {
setTimeout(() => {
this.props.transferMsg('end')
}, 1000);
}
render() {
return <div>
<p>child_1 component</p>
</div>
}
}
//使用了 箭头函数,将父组件的 transferMsg 函数通过 props 传递给子组件,得益于箭头函数,保证子组件在调用 transferMsg 函数时,其内部 this 仍指向父组件。
//对于层级比较深的子组件与父组件之间的通讯,仍可使用 ... 运算符,将父组件的调用函数传递给子组件,具体方法和上面的例子类似。
- 兄弟组件间通讯
- 可以使用Redux来进行同级之间的通讯.
- 在项目的config目录下有四个文件:
- dev-server.js(开发环境的node文件,为了webpack的热替换)
- webpack.base.js(提取的webpack通用配置,开发环境和生产环境都引入其配置项)
- webpack.dev.js(开发环境使用的webpack,其中包含了热替换)
- webpack.pro.js(生产环境使用的webpack,把公共js、css提取出来并且压缩源代码)
- 文件之间的引用步骤 :开发环境:dev-server.js → webpack.dev.js → webpack.base.js
- 文件之间的引用步骤 : 生产环境:webpack.pro.js → webpack.base.js
package.json结构:
如果想运行起来项目就需要在此文件的scripts中配置一些配置项:
"scripts": {
"init": "webpack --config build/webpack.dev.js",
"dev" : "node build/dev-server.js",
"pro" : "webpack --config build/webpack.pro.js"
}
注:
1. 如果是要在开发环境下,首次运行应该先npm run init,再npm run dev
(原因:npm run dev只是起热加载服务,真正访问需要dist目录中有index.html文件,所以需要先npm run init把init.html文件生成一次。)
2. 如果在生产环境下,直接执行npm run pro
- dev-server.js
* 可以看到第三行会引用当前目录下的webpack.dev.js文件
* 这里没有.js后缀是因为在webpack.base.js中有个配置可以省略写后缀名
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.dev');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
historyApiFallback: true,
headers: { "Access-Control-Allow-Origin": "*" },
contentBase: __dirname,
hot: true,
quiet: false,
noInfo: false,
stats: { colors: true }
}).listen(3001, '127.0.0.1', function (err, result) {
if (err) console.log(err);
console.log('正在监听host.com:3001');
});
- webpack.dev.js
var path = require('path');
var webpack = require('webpack');
var webpackBase = require('./webpack.base');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// 基本配置
var devConfig = Object.assign(webpackBase, {
devtool: 'cheap-module-eval-source-map',
devServer: true,
hotComponents: true,
entry: {
index: [
'webpack-dev-server/client?http://127.0.0.1:3001',//入口路径
'webpack/hot/only-dev-server',
path.resolve(__dirname, '../src/index.jsx')
]
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: 'http://127.0.0.1:3001/dist/',//热加载地址
hash: true,
filename: 'index.js'
}
});
// 插件配置
devConfig.plugins = (webpackBase.plugins || []).concat(
// 热替换
new webpack.HotModuleReplacementPlugin(),
// html模板
new HtmlWebpackPlugin({
favicon: path.resolve(__dirname, '../src/i/favicon.ico'), //favicon路径
filename: './index.html', //生成的html存放路径,相对于 path
template: path.resolve(__dirname, '../src/template/dev_index.html'), //html模板路径
inject: 'body', //允许插件修改哪些内容,包括head与body
hash: true, //为静态资源生成hash值
}),
// 配置全局变量(不同环境加载不同配置文件)
new webpack.ProvidePlugin({
ENV: path.resolve(__dirname, "../config/dev")
})
);
module.exports = devConfig;
注:在webpack配置路径时,最好要这样写,可确保路径正确path.resolve(__dirname, "../config/dev")
- webpack.base.js
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// 语言规范解释器,babel6开始插件化了
babel: {
presets: ['es2015', 'stage-0', 'react']
},
resolve: {
// 自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: ['react-hot','jsx?harmony','babel?presets[]=es2015,presets[]=react,presets[]=stage-0'],
exclude: /node_modules/
},{
test: /\.js$/,
loaders: ['react-hot','babel-loader'],
//exclude: [nodeModulesPath, ueditorPath]
exclude: /node_modules/,
//,query: {presets: ['es2015','react']}
},{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},{
test: /\.less$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader!less-loader')
},{
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
// 超过limit的图片会让 url-loader处理
loader: 'url-loader?limit=1&name=i/[name].[ext]'
}
]
},
plugins: [
// 整合css文件
new ExtractTextPlugin("css/[name].css", {allChunks: true}),
// 使用压缩的react包
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production")
}
})
]
};
- webpack.prod.js
var path = require('path');
var webpack = require('webpack');
var webpackBase = require('./webpack.base');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
// 基本配置
var proConfig = Object.assign(webpackBase, {
devtool: 'cheap-module-source-map',
entry: {
main: path.resolve(__dirname, '../src/index.jsx'), // 逻辑代码
common: ['react','antd','jquery'] // 公用类代码
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: "",
hash: true,
filename: 'js/[name].entry.js'
}
});
// 插件配置
proConfig.plugins = (webpackBase.plugins || []).concat(
// html模板
new HtmlWebpackPlugin({
favicon: path.resolve(__dirname, '../src/i/favicon.ico'), //favicon路径
filename: './index.html', //生成的html存放路径,相对于 path
template: path.resolve(__dirname, '../src/template/dev_index.html'), //html模板路径
inject: 'body', //js插入的位置,true/'head'/'body'/false
hash: true, //为静态资源生成hash值
chunks: ['main', 'common'],//需要引入的chunk,不配置就会引入所有页面的资源
minify:{ //压缩HTML文件
removeComments:true, //移除HTML中的注释
collapseWhitespace:false //删除空白符与换行符
}
}),
// 压缩
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
// 把入口文件里面的数组common打包成common.entry.js
new webpack.optimize.CommonsChunkPlugin('common', 'js/common.entry.js'),
// 配置全局变量(不同环境加载不同配置文件)
new webpack.ProvidePlugin({
ENV: path.resolve(__dirname, "../config/pro")
})
);
module.exports = proConfig;
- 可以看到webpack.dev.js和webpack.pro.js都有这么一个配置项(dev加载config/dev.jsx;pro加载config/pro.jsx)
// 配置全局变量(不同环境加载不同配置文件)
new webpack.ProvidePlugin({
ENV: path.resolve(__dirname, "../config/pro")
})
这个配置项为了使整个项目都可以使用ENV的全局变量
-
react-router的依赖基础 - history
-
history是一个独立的第三方js库,可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。具体来说里面的history分为三类:
-
旧版浏览器: 用于旧版浏览器,主要通过hash来实现,对应createHashHistory
-
高版本浏览器: 用于支持 HTML5 history API 的浏览器,对应createBrowserHistory
-
node环境下: 主要存储在memeory里面,对应createMemoryHistory
上面针对不同的环境提供了三个API,但是三个API有一些共性的操作,将其抽象了一个公共的文件createHistory:
// 内部的抽象实现
function createHistory(options={}) {
...
return {
listenBefore, // 内部的hook机制,可以在location发生变化前执行某些行为,AOP的实现
listen, // location发生改变时触发回调
transitionTo, // 执行location的改变
push, // 改变location
replace,
go,
goBack,
goForward,
createKey, // 创建location的key,用于唯一标示该location,是随机生成的
createPath,
createHref,
createLocation, // 创建location
}
}
上述这些方式是history内部最基础的方法,createHashHistory、createBrowserHistory、createMemoryHistory只是覆盖其中的某些方法而已。其中需要注意的是,此时的location跟浏览器原生的location是不相同的,最大的区别就在于里面多了key字段,history内部通过key来进行location的操作。
function createLocation() {
return {
pathname, // url的基本路径
search, // 查询字段
hash, // url中的hash值
state, // url对应的state字段
action, // 分为 push、replace、pop三种
key // 生成方法为: Math.random().toString(36).substr(2, length)
}
}
-
Router 与 Route 一样都是 react 组件,它的 history 对象是整个路由系统的核心,它暴漏了很多属性和方法在路由系统中使用
-
Route 的 path 属性表示路由组件所对应的路径,可以是绝对或相对路径,相对路径可继承;
-
Redirect 是一个重定向组件,有 from 和 to 两个属性;
-
Route 的 onEnter 钩子将用于在渲染对象的组件前做拦截操作,比如验证权限;
<Route onEnter={requireAuth} path="archives" component={Archives}>
- 在 Route 中,可以使用 component 指定单个组件,或者通过 components 指定多个组件集合;
const routes = (
<HashRouter history={customHistory} >
<div>
<Route path="/" component={Container} />
<Route path="/login" component={Login} />
<Redirect from='*' to='/login' />
</div>
</HashRouter>
);
- param 通过 /:param 的方式传递;