内容导航简单开发react将react与mobx结合开发使用react-router进行多页面开发将项目打包到后端项目中进行部署将完成的项目做成脚手架,避免重复的环境搭建需要环境确保node已经安装确保npm已经安装创建项目npx create-react-app test # test 为你需要创建项目的名字,会在命令当前目录下创建test的目录,包含项目所有的文件你已经完成了创建,开始跑起来npm start你可以看到react已经能够在local host:3000访问了,只有一个欢迎页面目录结构node_modules是当前目录安装的模块存放的地方publicindex.html 是单页面的入口src可存放自己编写的代码,App是默认生成的欢迎页面逻辑,index 是js的主入口开始更改你的代码A. react简单开发1.将App.js的代码更改如下import React, {Component} from ‘react’;import ‘./App.css’;class App extends Component { constructor(props) { super(props) this.state = {todos: [{checked: false, text: “hello”}, {checked: true, text: “world”}]} this.handleClick=this.handleClick.bind(this) } handleClick(index) { let todos = this.state.todos todos[index].checked = !todos[index].checked this.setState({todos:todos}) } render() { let todos = this.state.todos let todosDiv = todos.map((item, index) => { return (<Todo index={index} checked={item.checked} text={item.text} handleClick={this.handleClick}/>) }) return ( <div className=“App”> {todosDiv} </div> ); }}class Todo extends Component { constructor(props){ super(props) this.handleClick=this.handleClick.bind(this) } handleClick() { let index = this.props.index this.props.handleClick(index) }; render() { return ( <p><input type={‘checkbox’} checked={this.props.checked} onClick={this.handleClick}/> {this.props.text}:{this.props.index} </p> ) }}export default App;再次npm start一下看看效果吧~可以看到我们组件已经能够响应点击了B. 引入mobx作为状态管理提出问题在上面我们可以看到想要更改状态是比较困难的,首先要将handClick方法由子组件传给父组件,再进行处理。如果我们的组件是四五层组件的时候得一步一步的往上级传递,这就会导致组件传递写的很臃肿。这个时候就需要一个将状态(即state这个值)独立开来。react有很多状态管理的组件,比如redux,mobx。但redux写起来还是不如mobx简单明了。下面我们就来接入mobx。接入步骤安装依赖npm install mobx –savenpm install mobx-react –save启用装饰器语法# 如果有git的话,要将没有保存的文件上传之后或者删除之后才能跑eject命令yarn run ejectnpm install –save-dev babel-preset-mobx在package.json中找到babel项目,在presets里面增加"mobx"“babel”: { “presets”: [ “react-app”, “mobx” ]},加入core-decoratorsnpm install core-decorators –save在src下增加store.AppStore.js文件import {action, observable} from “mobx”;class AppStore { @observable todos; constructor() { this.todos = [{checked: false, text: “hello”}, {checked: true, text: “world”}] } @action.bound handleClick(index) { let todos = this.todos todos[index].checked = !todos[index].checked }}export default AppStore;改写index.jsimport React from ‘react’;import ReactDOM from ‘react-dom’;import ‘./index.css’;import App from ‘./App’;import * as serviceWorker from ‘./serviceWorker’;import {Provider} from “mobx-react”;import AppStore from ‘./store/AppStore’let rootStore = {}rootStore[‘app’] = new AppStore()ReactDOM.render( <Provider {…rootStore}> <App/> </Provider>, document.getElementById(‘root’));// If you want your app to work offline and load faster, you can change// unregister() to register() below. Note this comes with some pitfalls.// Learn more about service workers: https://bit.ly/CRA-PWAserviceWorker.unregister();改写App.jsimport React, {Component} from ‘react’;import ‘./App.css’;import {inject, observer} from “mobx-react”;import {autobind} from “core-decorators”;@inject(“app”)@autobind@observerclass App extends Component { constructor(props) { super(props) } render() { let todos = this.props.app.todos let todosDiv = todos.map((item, index) => { return (<Todo index={index}/>) }) return ( <div className=“App”> {todosDiv} </div> ); }}@inject(“app”)@autobind@observerclass Todo extends Component { constructor(props) { super(props) } handleClick() { let index = this.props.index this.props.app.handleClick(index) }; render() { let index = this.props.index let todo = this.props.app.todos[index] return ( <p><input type={‘checkbox’} checked={todo.checked} onClick={this.handleClick}/> {todo.text}:{index} </p> ) }}export default App;```npm start一下,来看看效果吧简要说明@inject(“app”)表示注入在index.js中的rootStore的属性app。是由<Provider {…rootStore}>这个标签来实现动态的注入的@autobind 将组件之间的绑定自动完成@observer mobx用来将react组件转换为响应式组件的注解,详情查看mobx的文档上面可以看出,将原本的state的属性抽离到AppStore中了,对值得更改方法也是直接调用AppStore的方法,从而避免了react组件的一级一级往上传递C. 引入react-router作为多页面管理提出问题上面我们完成了单页面的开发。当需要多个页面时我们就需要使用react-router来对不同路径进行渲染了接入react-router步骤安装依赖npm install react-router mobx-react-router –save增加新的页面,在src中增加component/Test.js import * as React from “react”; class Test extends React.Component{ render() { return(<p>welcome!</p>) } } export default Test;更改index.jsimport React from ‘react’;import ReactDOM from ‘react-dom’;import ‘./index.css’;import App from ‘./App’;import * as serviceWorker from ‘./serviceWorker’;import {Provider} from “mobx-react”;import AppStore from ‘./store/AppStore’import {Route, Router, Switch} from “react-router”;import {RouterStore, syncHistoryWithStore} from “mobx-react-router”;import createHashHistory from “history/createHashHistory"import Test from “./component/Test"let rootStore = {}const hashHistory = createHashHistory()const routerStore = new RouterStore()const history = syncHistoryWithStore(hashHistory, routerStore)rootStore[‘app’] = new AppStore()routerStore[‘routing’] = routerStoreReactDOM.render( <Provider {…rootStore}> <Router history={history}> <p>here is the menu</p> <Switch> <Route path={"/test”} component={Test}/> <Route path={”/"} component={App}/> </Switch> </Router> </Provider>, document.getElementById(‘root’));// If you want your app to work offline and load faster, you can change// unregister() to register() below. Note this comes with some pitfalls.// Learn more about service workers: https://bit.ly/CRA-PWAserviceWorker.unregister();npm start一下,访问下/#/test,和/#/路径,看看效果吧简要说明createHashHistory是单页面的访问,会在url加个#号作为定位,这个对于要打包到后台作为页面时是很方便的。如果你直接使用node部署的话可以直接使用createBrowserHistory,url就会是没有#号的url。D. 结合ui框架接入步骤找到一个合适的react ui框架,install之后按照ui框架的教程就可以开发一个相对比较好看的页面了常见的框架有semantic,bootstrap,ant等。E. 结合maven打包进spring boot项目提出问题当我们需要跟spring boot等后端项目结合,而又不想单独部署前端页面时,就需要打包进后端项目了接入步骤新建一个多模块的maven项目按照之前创建的步骤,创建前端的模块,假设模块名字为view,并在前端模块的目录下增加pom.xml<build> <plugins> <plugin> <groupId>com.github.eirslett</groupId> <artifactId>frontend-maven-plugin</artifactId> <version>1.2</version> <executions> <– Install our node and npm version to run npm/node scripts–> <execution> <id>install node and npm</id> <goals> <goal>install-node-and-npm</goal> </goals> <configuration> <– 指定node的版本例如 v6.9.1 –> <nodeVersion>${nodeVersion}</nodeVersion> <npmVersion>${npmVersion}</npmVersion> <nodeDownloadRoot>https://npm.taobao.org/mirrors/node/</nodeDownloadRoot> <npmDownloadRoot>http://registry.npmjs.org/npm/-/</npmDownloadRoot> </configuration> </execution> <– Set NPM Registry –> <execution> <id>npm set registry</id> <goals> <goal>npm</goal> </goals> <configuration> <–<arguments>config set registry https://registry.npmjs.org</arguments>–> <arguments>config set registry https://registry.npm.taobao.org</arguments> </configuration> </execution> <– Set SSL privilege –> <execution> <id>npm set non-strict ssl</id> <goals> <goal>npm</goal> </goals> <– Optional configuration which provides for running any npm command –> <configuration> <arguments>config set strict-ssl false</arguments> </configuration> </execution> <– Install all project dependencies –> <execution> <id>npm install</id> <goals> <goal>npm</goal> </goals> <– optional: default phase is “generate-resources” –> <phase>generate-resources</phase> <– Optional configuration which provides for running any npm command –> <configuration> <arguments>install</arguments> </configuration> </execution> <– Build and minify static files –> <execution> <id>npm run build</id> <goals> <goal>npm</goal> </goals> <configuration> <arguments>run build</arguments> </configuration> </execution> </executions> </plugin> </plugins> </build>当进行mvn package时就会在目录下生成build目录,包含所有的页面和脚本了。在spring boot后端项目中,将前端打包好的页面拷贝到后端目录中 <build> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>Copy App Content</id> <phase>generate-resources</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>src/main/resources/public</outputDirectory> <overwrite>true</overwrite> <resources> <resource> <directory>${project.parent.basedir}/view/build</directory> <includes> <include>static/</include> <include>index.html</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>其中outputDirectory指明要放入的文件夹directory指明要拷贝哪里的资源文件,需要根据你的前端模块名进行相应的修改mvn package 一下,后端模块的打包jar里面就会有相应的资源文件啦F. 前后端联调步骤在前端项目package.json中指明接口的代理"proxy":“http://localhost:8080/“如果servletPath不为/,则需要在后面补上相应的servletPath当你的后端项目有设置servletPath的时候,需要相应配置前端的打包的servletPath,否则默认为/的servletpath方法1: package.json 增加"homepage”: “.“方法2: config.paths.js文件下修改配置function getServedPath(appPackageJson) { const publicUrl = getPublicUrl(appPackageJson); //将/修改为./ const servedUrl = envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : ‘./’); return ensureSlash(servedUrl, true);}G. 将你创建好的项目做成脚手架提出问题如果每个项目都要经历上面的步骤,才能完成,那前期工作量是在太繁琐又重复借助maven的archetype来帮你自动生成一个初始项目吧接入步骤按照上面的流程我们已经建好了项目在项目目录下执行 mvn archetype:create-from-project,生成的target就是你的脚手架项目cd target/generated-sources/archetype 目录下,执行mvn install 就把archetype放入了本地仓库了,可以进行使用了为了deploy到远程仓库中,需要在target/generated-sources/archetype 目录下的pom.xml中加入自己的远程仓库的地址,然后在target/generated-sources/archetype 目录下mvn deploy就可以了屏蔽掉部分不想打包进archetype的文件要屏蔽部分文件夹时在pom中加入plugin<plugin> <artifactId>maven-archetype-plugin</artifactId> <version>3.0.1</version> <configuration> <propertyFile>archetype.properties</propertyFile> </configuration> </plugin>新建archetype.properties文件,配置要忽略的通配符excludePatterns=/.idea/,**.iml怎么使用archetype创建项目在idea中,在点击file-> new-> project后弹出的对话框中选择maven在create from archetype打勾,点击Add archetype加入创建好的archetype填写对应的groupId,artifaceId,version后在列表中选择已有的archetype按引导进行后续步骤的创建,然后就会自动生成跟你项目一样的啦跨store的访问什么是跨store访问在上面我们有这样的代码const routerStore = new RouterStore() rootStore[‘app’] = new AppStore() routerStore[‘routing’] = routerStore有时候我们往往需要在一个store的方法中去访问下别的store的内容,这个时候就是跨store的访问,就需要在初始化时将rootStore传给这个store,通过rootStore去访问,改写index.jsrootStore[‘app’] = new AppStore(rootStore) 改写AppStore.js,增加构造函数constructor(rootStore) { this.rootStore = rootStore }这样就可以在AppStore.js的函数中通过this.rootStore 去获取所有store的json,从而访问所有的store了