理解React模型
以往操作DOM一般通过原生JS或jQuery库,此方式随意、灵活,但当web界面越来越大,js文件越来越多,很叫人精疲力竭。React提出两大创新,一是组件化单向数据流思想,另一个是用虚拟DOM局部更新。
image.png
如上图就是一个React组件的单向数据流。当用户执行某个操作,即触发此组件的某个方法,此方法可直接更新组件内State属性变量,也可以从远程服务器fetch数据再更新State。用户手动绑定State与DOM节点,当属性改变,React运行diff算法,根据shouldComponentUpdate返回值,确认是否应刷新此组件在DOM的展示。
理解React-Redux
image.pngReact-redux将State从组件中抽出,集中管理,便于组件之间共享变量。如上图就是Redux循环流。用户动作在抛出后被中间件捕获,同时也被Reducer捕获。中间件查找此事件是否有对应的函数动作,如有则执行函数,否则忽略。Reducer则根据事件,查找如何更改Store,如果有相应代码,则执行代码,更新Store,否则忽略。注意中间件也可以抛出事件,如redux-sagas,通常,用户动作触发事件被sagas捕获,sagas执行各种复杂代码,包括向远程服务器取数据,然后再抛出事件,被reducer捕获,然后更新store中对应的内容。
生命周期
function组件没有声明周期,React class组件有生命周期。以下图为新版
image.png
老版本更新周期函数为以下四个。
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
image.png
React 16.3起将componentWillReceiveProps变更为getDerivedStateFromProps,并添加getSnapshotBeforeUpdate,主要用于异步渲染。
详细可参考官网https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
构建React项目
理解之后,就可以开始创建react app。分为:
构建npm项目
npm init
安装必要模块
npm install .....
npm install -D ....
//package.json
{
"name": "portfolio",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production",
"start": "webpack-dev-server"
},
"author": "",
"license": "ISC",
"dependencies": {
"antd": "^3.13.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"redux": "^4.0.1",
"redux-saga": "^0.16.2"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.5",
"css-loader": "^2.1.0",
"html-webpack-plugin": "^3.2.0",
"redux-devtools-extension": "^2.13.7",
"style-loader": "^0.23.1",
"webpack": "^4.28.4",
"webpack-chunk-hash": "^0.6.0",
"webpack-cli": "^3.2.1",
"webpack-dev-server": "^3.1.14"
}
}
由于react使用es6,有很多新特性,而大多数浏览器还不支持es6,所以还需要webpack进行转义。webpack除了转译功能,还支持本地webserver,文件分割,代码检查等,详细配置较多,以下是我自己的常用模板。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var webpack = require('webpack');
module.exports = {
entry: './src/index.jsx',
output: {
path: path.join(__dirname, 'dist'),
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: '/',
},
mode:"development",
module: {
rules: [{
test: /\.js|.jsx$/,
exclude: path.resolve(__dirname, 'node_modules/'),
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env','@babel/preset-react'],
plugins:["@babel/plugin-proposal-class-properties","@babel/plugin-transform-runtime","@babel/plugin-syntax-dynamic-import"]
},
}
},{
test: /\.css$/,
loaders: [
'style-loader',
'css-loader',
],
}]
},
resolve: {
extensions: ['.js', '.jsx'],
},
plugins:[
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: 'portfolio',
template: path.join(__dirname, 'src', 'index.ejs'),
// favicon: path.join(__dirname, 'src', 'favicon.ico'),
meta: [
{
name: 'description',
content: 'simple website',
},
],
minify: {
collapseWhitespace: true,
},
}),
],
optimization: {
nodeEnv: 'development',
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
minChunks: 2,
},
default: {
minChunks: 2,
reuseExistingChunk: true,
},
},
},
},
devServer: {
contentBase: path.join(__dirname, "dist"),
open:true, // 自动打开浏览器
overlay: true,
compress:true,
inline:true,//inline模式热加载
hot:true//开启热加载
}
};
编写react代码
以下为案例入口文件,包含一个简单HomePage组件,包含另一个整体组件App。App组件中,包含Router组件,可以根据URL匹配组件。RestrictedPage组件是高阶组件,将内部组件进一步封装,限定仅登陆用户可见。ReactDOM.render将react组件挂载到DOM页面,同时注入由Redux创建的store对象。
class HomePage extends Component{
render(){
return <div>this is an empty page</div>
}
}
function App(props){
return (
<Router history={history}>
<React.Fragment>
<NavBar/>
<Switch>
<Route exact path="/" component={HomePage}/>
<Route path="/user/login" component={LoginPage}/>
<Route path="/portfolio" component={()=><RestrictedPage><PortfolioPage/></RestrictedPage>}/>
<Route path="/factor" component={ ()=><RestrictedPage><FilterEditor/></RestrictedPage>} />
</Switch>
</React.Fragment>
</Router>
)
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'))
参考:
https://grokonez.com/frontend/react/react-component-lifecycle-methods-from-v16-3-react-lifecycle-example
https://reactjs.org/docs/code-splitting.html
网友评论