【开发】前端工程——ReactJS
前置知识:JavaScript&ES6
ReactJS
前端开发的四个阶段
1. 静态页面阶段
在第一个阶段中前端页面都是静态的,所有前端代码和前端数据都是后端生成的,前端纯粹只是增加一些特殊效果。
后端MVC模式
- Model(模型层):提供/保持数据
- Controller(控制层):数据处理,实现业务逻辑
- View(视图层):展示数据,提供用户界面
此时的前端只是后端MVC中的V
2. ajax阶段
2004年AJAX诞生,改变了前端开发。Gmail和Google地图这样革命性产品出现,使前端的作用不再是展示页面,还可以管理数据并与用户互动
3. 前端MV阶段
把MVC模式照搬到了前端,只有 M(读写数据)和V(展示数据),没有C(处理数据)
有些框架提出 MVVM模式,用View Model代替Controller。Model拿到数据后,View Model将数据处理成视图层(View)需要的格式
4. SPA阶段
前端可以做到读写数据,切换视图,用户交互。网页其实是一个应用程序,而不是信息的纯展示。这种单张网页的应用程序称为SPA(Single Page Application)
2010年后,前端工程师从开发页面(切模板),逐渐变成了开发“前端应用”,跑在浏览器里面的应用
目前,流行的前端框架 Vue
,React
都属于SPA开发框架
ReactJS
简介
官网
用于构建用户界面的JavaScript框架,由Facebook开发
ReactJS把复杂的页面,拆分成一个个的组件,将这些组件拼装起来,就会呈现一个页面
ReactJS可用于MVC、MVVM等架构
HelloWorld
1. 新建static web项目
2. 初始化项目,添加umi依赖
tyarn init -y
tyarn add umi --dev
3. 编写HelloWorld程序
在工程的根目录下新建config/config.js
在UmiJS的约定中,config/config.js将作为UmiJS的全局配置文件
在Umi中,约定的目录结构如下:
.├── dist/ // 默认的 build 输出目录├── mock/ // mock 文件所在目录,基于 express├── config/├── config.js // umi 配置,同 .umirc.js,二选一└── src/ // 源码目录,可选├── layouts/index.js // 全局布局├── pages/ // 页面目录,里面的文件即路由├── .umi/ // dev 临时目录,需添加到 .gitignore├── .umi-production/ // build 临时目录,会自动删除├── document.ejs // HTML 模板├── 404.js // 404 页面├── page1.js // 页面 1,任意命名,导出 react 组件├── page1.test.js // 用例文件,umi test 会匹配所有 .test.js 和 .e2e.js 结尾的文件└── page2.js // 页面 2,任意命名├── global.css // 约定的全局样式文件,自动引入,也可以用 global.less├── global.js // 可以在这里加入 polyfill├── app.js // 运行时配置文件├── .umirc.js // umi 配置,同 config/config.js,二选一├── .env // 环境变量└── package.json
在config.js文件中输入,以便后面使用:
//导出一个对象,暂时设置为空对象,后面再填充内容
export default{};
创建HelloWorld.js页面文件
在Umi中,约定存放页面代码的文件夹在 src/pages
,可以通过 singular:false
来设置单数的命名方式
在HelloWorld.js中输入如下内容:
export default () => {return <div>hello world</div>;
}
构建和部署
我们写的js,必须通过umi先转码后才能正常执行。
umi build
启动服务,查看页面效果
# 启动服务
umi dev
可以看到,通过 /HelloWorld
即可访问到刚写的HelloWorld.js文件
在umi中,可以使用约定式的路由,将在pages下的JS文件都会按照文件名映射到一个路由
添加 umi-plugin-react
插件
umi-plugin-react插件是umi官方基于react封装的插件
链接
#添加插件
tyarn add @umijs/preset-react --dev
在config.js中引入该插件
export default{dva: {},antd: {}
};
JSX语法
JSX语法就是,可以在js文件中插入html片段,是React自创的一种语法
JSX语法会被Babel等转码工具进行转码,得到正常的js代码再执行
注意
所有的html标签必须是闭合的
在JSX语法中,只能有一个根标签,不能有多个
const div1 = <div><div>hello</div> <div>world</div></div>//正确 const div2 = <div>hello</div> <div>world</div> //错误
在JSX语法中,如果想要在html标签中插入js脚本,需要通过
{}
插入js脚本export default()=>{const fun = () =>"黑马程序"return (<div><div>{fun()}</div> <div>hello world</div></div>); }
组件
1. 自定义组件
import React from "react";
//1. 导入Reactclass HelloWorld extends React.Component{
//2. 继承React.Componentrender(){//3. 重写render()方法,用于渲染页面return <div>Hello World</div>}
}//4. 导出该类
export default HelloWorld;
2. 导入自定义组件
import React from "react";
import HelloWorld from "./HelloWorld";class Show extends React.Component{render() {return (<div><HelloWorld></HelloWorld></div>);}
}export default Show;
组件参数
import React from "react";
//1. 导入Reactclass HelloWorld extends React.Component{
//2. 继承React.Componentrender(){//3. 重写render()方法,用于渲染页面return (<div><div>Hello World</div><div>lastName={this.props.lastName}</div><div>{this.props.children}</div></div>);}
}//4. 导出该类
export default HelloWorld;
import React from "react";
import HelloWorld from "./HelloWorld";class Show extends React.Component{render() {return (<div><HelloWorld lastName={"Auspice"}>Tian</HelloWorld></div>);}
}export default Show;
组件的状态
每一个 页面组件 都有一个状态,其保存在 this.state
中,当状态值发生变化时,React框架会自动调用 render()
方法,重新渲染画面
注意
- this.state值的设置要在构造参数中完成,不能直接对
this.state
修改 - 要修改this.state的值,需要调用
this.setState()
完成
案例:用过点击按钮,不断更新this.state,从而反映到页面
import React from "react";class Test extends React.Component{constructor(props) {//构造函数中必须有props参数super(props);//调用父类构造方法this.state = {//初始化statedataList:[1,2,3],maxItem:3}}render() {return (<div><ul>{//遍历值this.state.dataList.map((value, index) => {return <li key={index}>{value}</li>})}</ul><button onClick={()=>{//为按钮添加点击事件let maxItem = this.state.maxItem+1;let newArr = [...this.state.dataList,maxItem]this.setState({dataList:newArr,maxItem:maxItem})}}>加一</button></div>);}
}export default Test;
生命周期
组件运行过程中,存在不同的阶段。React为这些阶段提供了钩子方法(生命周期方法lifestyle methods),允许开发者自定义每个阶段自动执行的函数。
import React from 'react'; //第一步,导入React
class LifeCycle extends React.Component {constructor(props) {super(props);//构造方法console.log("constructor()");} componentDidMount() {//组件挂载后调用console.log("componentDidMount()");} componentWillUnmount() {//在组件从 DOM 中移除之前立刻被调用。console.log("componentWillUnmount()");} componentDidUpdate() {//在组件完成更新后立即调用。在初始化时不会被调用。console.log("componentDidUpdate()");} shouldComponentUpdate(nextProps, nextState){// 每当this.props或this.state有变化,在render方法执行之前,就会调用这个方法。// 该方法返回一个布尔值,表示是否应该继续执行render方法,即如果返回false,UI 就不会更新,默认返回true。// 组件挂载时,render方法的第一次执行,不会调用这个方法。console.log("shouldComponentUpdate()");}render() {return (<div><h1>React Life Cycle!</h1></div>);}
} export default LifeCycle;
Model
分层
服务端系统:
- Controller负责与用户直接打交道,渲染页面、提供接口等,侧重于展示型逻辑
- Service负责处理业务逻辑,供Controller层调用
- DataAccess 层负责与数据源对接,进行纯粹的数据读写,供Service层调用
前端代码结构:
- Page负责与用户直接打交道:侧重于展示型交互逻辑
- 渲染页面
- 接受用户的操作输入
- Model负责处理业务逻辑,为Page做数据、状态的读写、变换、暂存等
- Service负责与HTTP接口对接,进行纯粹的数据读写
使用dva进行数据分层管理
dva官网
umi-dva插件
@Connect(mapModelToProps,mapDispatcherToProps)
:将model层中数据及函数绑定到page层
- mapModelToProps:
- 将page层和model层进行连接
- 返回model中的数据
- 将返回的数据绑定到this.props中
- mapDispatcherToProps
- 将定义的函数绑定到this.props中
- 调用model层(reducers)中定义的函数
1. 引入dva框架
umi对dva进行了整合,在 config.js 中进行配置:
export default {dva: {immer: true,hmr: false,},
};
2. 创建model文件
umi中,约定 src/models 文件夹中定义model
export default {namespace:'TestData',state:{dataList:[1,2,3],maxItem:3}
}
3. 将model层数据导入page层
import React from "react";
import {connect} from "umi";const namespace = "TestData";// connect第一个回调函数,作用:将page层和model层进行链接,返回model层中的数据,并将数据绑定到 this.props
@connect((dvaState)=>{return {dataList:dvaState[namespace].dataList,maxItem: dvaState[namespace].maxItem}
})
class Test extends React.Component{render() {return (<div><ul>{//遍历值this.props.dataList.map((value, index) => {return <li key={index}>{value}</li>})}</ul></div>...);}
}export default Test;
流程
- umi框架启动,会自动读取models目录下文件
- @Connect修饰符的第一个参数,接受一个方法,该方法必须返回
{}(对象)
,将接收到model数据 - 全局model中,通过
namespace
进行区分,所以通过state[namespace]
进行数据获取 - 返回的数据会被封装到
this.props
中,所以通过this.props.data
获取到model中的数据
4. 更新model中定义的数据
export default {namespace:'TestData',state:{dataList:[1,2,3],maxItem:3},reducers:{//定义一些函数addNewData:function (state){//state为修改前statelet maxItem = state.maxItem+1;let newArr = [...state.dataList,maxItem]return {//通过return返回更新后的数据dataList:newArr,maxItem:maxItem}}}
}
import React from "react";
import {connect} from "umi";const namespace = "TestData";@connect((dvaState)=>{return {dataList:dvaState[namespace].dataList,maxItem: dvaState[namespace].maxItem}},(dvaDispatch)=>{//dvaDispatch : 可以调用model层定义的函数return{add:function(){dvaDispatch({//通过dvaDispatcher调用model层定义的函数//@param : type——指定函数名 //namespace/函数名type:namespace+"/addNewData"})}}}
)
class Test extends React.Component{render() {return (<div><ul>{//遍历值this.props.dataList.map((value, index) => {return <li key={index}>{value}</li>})}</ul><button onClick={()=>{this.props.add();}}>加一</button></div>);}
}export default Test;
Model中异步请求数据
1. 请求工具类
src/utils
目录下创建 request.js
,用于异步请求数据
function checkStatus(response) {if (response.status >= 200 && response.status < 300) {return response;}const error = new Error(response.statusText);error.response = response;throw error;
}/**
* Requests a URL, returning a promise.
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default async function request(url, options) {const response = await fetch(url, options);checkStatus(response);return await response.json();
}
2. model层添加异步请求
import request from '../utils/request';export default {namespace:'TestData',state:{dataList:[],maxItem:0},reducers:{//定义一些函数addNewData:function (state,result){//state为修改前state,result就是拿到的结果数据if(result.data){//如果data存在,说明是初始化数据,直接返回return result.data;}let maxItem = state.maxItem+1;let newArr = [...state.dataList,maxItem]return {//通过return返回更新后的数据dataList:newArr,maxItem:maxItem}}},effects: { //新增effects配置,用于异步加载数据*initData(params, sagaEffects) { //*表示 定义异步方法const {call, put} = sagaEffects; //获取到call、put方法const url = "/ds/list"; // 定义请求的urllet data = yield call(request, url); //执行请求yield put({ // 调用reducers中的方法type : "addNewData", //指定方法名data : data //传递ajax回来的数据});}}
}
3. 绑定model调用异步请求
import React from "react";
import {connect} from "umi";const namespace = "TestData";@connect((dvaState)=>{return {dataList:dvaState[namespace].dataList,maxItem: dvaState[namespace].maxItem}},(dvaDispatch)=>{return{add:function(){dvaDispatch({type:namespace+"/addNewData"})},initData:()=>{dvaDispatch({type:namespace+"/initData"})}}}
)
class Test extends React.Component{componentDidMount() {//组件加载完后进行初始化操作this.props.initData();}render() {return (<div><ul>{//遍历值this.props.dataList.map((value, index) => {return <li key={index}>{value}</li>})}</ul><button onClick={()=>{this.props.add();}}>加一</button></div>);}
}export default Test;
原因:
返回的数据不是json格式,解析出错
4. moke数据
Mock 数据是前端开发过程中必不可少的一环,是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发独立自主,不会被服务端的开发所阻塞。
umi中支持对请求的模拟
在项目根目录下创建 mock
目录,创建 MockTestData.js
文件
export default {'GET /ds/test': function (req, res) { //模拟请求返回数据res.json({//返回dataList: [1, 2, 3, 4],maxItem: 4});}
}
import request from '../utils/request';export default {namespace:'TestData',state:{dataList:[],maxItem:0},reducers:{addNewData:function (state,result){if(result.data){//判断result中的data是否存在,如果存在,说明是初始化数据,直接返回/*mock: 若响应中的字段名与page层的属性不一致,需要做映射return {dataList:result.data.data,maxItem:result.data.maxNum}*/return result.data;}let maxItem = state.maxItem+1;let newArr = [...state.dataList,maxItem]return {dataList:newArr,maxItem:maxItem}}},effects: { *initData(params, sagaEffects) { const {call, put} = sagaEffects;const url = "/ds/test"; let data = yield call(request, url); yield put({ type : "addNewData", data : data });}}
}
umi - model 注册
model示例
export default {namespace: '', // 表示在全局 state 上的 keystate: {}, // 状态数据reducers: {}, // 管理同步方法,必须是纯函数effects: {}, // 管理异步操作,采用了 generator 的相关概念subscriptions: {}, // 订阅数据源
};
umi model注册
umi中,按照约定的目录 src/models 文件夹中被注册为model
model 分两类,一是全局 model,二是页面 model。全局 model 存于 /src/models/
目录,所有页面都可引用;页面 model 不能被其他页面所引用。
src/models/**/*.js
为 global modelsrc/pages/**/models/**/*.js
为 page model- global model 全量载入,page model 在 production 时按需载入,在 development 时全量载入
- page model 为 page js 所在路径下
models/**/*.js
的文件 - page model 会向上查找,比如 page js 为
pages/a/b.js
,他的 page model 为pages/a/b/models/**/*.js
+pages/a/models/**/*.js
,依次类推 - 约定 model.js 为单文件 model,解决只有一个 model 时不需要建 models 目录的问题,有 model.js 则不去找
models/**/*.js
React框架分类
- Flux
- 利用一个单向的数据流补充了React的组合视图组件,更像一种模式而非框架
- Redux
- JS状态容器,提供可预测的状态管理,Redux使组件状态共享变得简单
- Ant Design of React
- 阿里开源的基于React的企业级后台产品,继承了多种React框架
- Ant Design提供了丰富的组件,包括:按钮、表单、表格、布局、分页、树组件、日历等
AntDesign
简介
Ant Design是阿里蚂蚁金服 基于React开发的UI组件 ,主要用于中后台系统的使用
官网
特性
-
【开发】前端工程——ReactJS相关推荐
- 前端工程基础知识点--Browserslist (基于官方文档翻译)
总结不出更好的了,感觉官方文档已经写得够清楚了,翻译的不好,请大家斧正 简介 browserslist 是在不同的前端工具之间共用目标浏览器和 node 版本的配置工具.它主要被以下工具使用: Aut ...
- 基于webpack搭建前端工程解决方案探索
关于前端工程 \\ 下面是百科关于"软件工程"的名词解释: \\ \ 软件工程是一门研究用工程化方法构建和维护有效的.实用的和高质量的软件的学科. \ \\ 其中,工程化是方法,是 ...
- 前端工程与性能优化:静态资源版本更新与缓存
2019独角兽企业重金招聘Python工程师标准>>> 每个参与过开发企业级web应用的前端工程师或许都曾思考过前端性能优化方面的问题.我们有雅虎14条性能优化原则,还有两本很经典的 ...
- H5新人福音~零配置搭建现代化的前端工程
X-BUILD一套基于Webpack(v4.21.0)快速搭建H5场景开发环境的脚手架,只需要几分钟的时间就可以运行起来.X-BUILD是针对H5开发的一套自动化构建工具,致力于提升开发效率,减小开发 ...
- 为前端工程之崛起而编程!
作者 | 蚂蚁保险体验技术团队 责编 | 胡巍巍 程序员转行学什么语言? https://edu.csdn.net/topic/ai30?utm_source=csdn_bw 本文经授权转载知乎&qu ...
- Gulp vs Grunt 前端工程构建工具
Gulp vs Grunt 前端工程的构建工具对比 1. Grunt -> Gulp 早些年提到构建工具,难免会让人联想到历史比较悠久的Make,Ant,以及后来为了更方便的构建结构类似的Jav ...
- 软件工程-软件开发的工程思维
软件工程-软件开发的工程思维 目录 软件工程-软件开发的工程思维 前言 什么是软件工程? 定义 出现的背景 软件工程核心知识 与项目管理的区别 软件工程的目标 为什么需要软件工程 如何做好软件工程:原 ...
- 如何搭建高质量、高效率的前端工程体系--页面结构继承
推荐理由: 推荐理由: 程序员在我们的印象中,就是不停的敲代码:而写的程序如何确保不出现bug,而且还能及时发现问题,下面我推荐的这篇文章,围绕整个前端的开发流程出发解决这两个问题,帮助大家提高产品质 ...
- 腾讯IVWEB团队:如何搭建高质量、高效率的前端工程体系 页面结构继承
腾讯云技术社区-简书主页持续为大家呈现云计算技术文章,欢迎大家关注! 作者:莫卓颖 序言 相信很多程序员都会经历两件事:第一件事情是没日没夜加班撸代码:第二件事情是写的程序出现bug没有及时发现而被老 ...
最新文章
- 如何判断locals()变量或globals()变量是否存在或是否为空?
- BOOST_LOCAL_FUNCTION宏用法的测试程序
- [BUUCTF-pwn]——mrctf2020_shellcode_revenge(可见字符shellcode)(内涵peak小知识)
- python实现Redis订阅发布
- python有序队列_【Python】:拓展Queue实现有序不重复队列
- 1.9 编程基础之顺序查找 11 连续出现的字符 python
- c# tooltip 取消关联控件
- WAP网站制作(WAP网站建设)全攻略教程二
- 汽车行业中SOP和PT是什么意思?
- GNOME Evince开源项目作者Marco因癌症离世
- 用Node.js写一个爬虫来爬小说
- 怎样调整计算机视角,迷你世界电脑版怎么调整视角 | 手游网游页游攻略大全
- 智慧城市背景下的“多规合一”标准探究
- AC、HC、AHC、ACT、LS的区别
- 公博评级编号1开头代表什么_公博评级的XF,MS,AU都是啥意思
- 【工业互联网】周剑:工业互联网平台作用机理和发展路径
- JAVA-@Primary的常用方式
- VtigerCRM配置网易企业邮箱SMTP 和 POP3服务
- 网络编程--TCP-qq聊天室
- ES 查询示例 搜索 分组 去重 分页 排序
热门文章
- 前端工程基础知识点--Browserslist (基于官方文档翻译)