1.1 前端开发的演变
- 静态页面阶段:后端 MVC模式,前端即V
- AJAX 阶段:实现了(获取数据 => 处理数据 => 展示数据)完整业务逻辑
- 前端 MVC 阶段:MVVM,View-Model数据处理,简化版的Controller。
- SPA 阶段:开发页面 => 开发前端应用,代表框架: Vue、Angular、React
总结:
React:网页组件的解决方案,任何应用架构都可以采用
antd 基于 React 框架,采用 Redux 架构,进行封装后大大了简化开发工作量
1.2 初始化环境
本课程的工程目录结构
为什么需要脚手架:存在编译过程,需要编译工具搭建
编译工具:umi(同时也是前端框架,对社区的 webpack、react-router 等进行封装)
开发环境:安装 nodejs(js 执行环境);使用以下代码测试是否安装成功
node -v
npm -v
安装 cnpm 获得更快速、更安全的包管理体验;
npm install -g cnpm --registry=https://registry.npm.taobao.org
···cnpm···
使用npm install -g yarn模块化 安装了yarn
安装 umi 依赖,即
cnpm init
初始化 package.json(nodejs 约定的用来存放项目信息和配置信息的文件);
cnpm install umi --save-dev
或者 yarn global add umi
(让依赖信息保存到 package.json 中的 dependencise 里,这里dev代表开发时使用。这些模块在我们的项目部署后是不需要的,所以我们可以使用 --save-dev 的形式安装。像 express 这些模块是项目运行必备的,应该安装在 dependencies 节点下,所以我们应该使用 -save 的形式安装。)
我们也可以直接使用 create umi 命令构建完整项目:
mkdir test-dva-umi-app & cd test-dva-umi-app
yarn create umi
然后,选择你需要的功能进行安装,如图所示。
回车确定后,会根据选择自动创建好目录和文件。此时的目录结构如下图所示。
安装依赖,启动项目
yarn 或 npm install
yarn start 或 npm start
第一个页面:配置文件被约定为 config/config.js,初始化的内容如下:export default {};
;
新建 src 目录用来存放配置和单测以外的代码,可以使用export default {singular: true,}
修改pages文件夹为page;
在package.json 中的 script 里添加命令,命令可以在根目录中通过 cnpm run "scriptname"来运行;
{
"scripts": {
"dev": "umi dev",
"build": "umi build"
}
}
新建 src/page/HelloWorld.js 文件,代码如下:
export default () => {
return hello world;
}
这时通过 cnpm run dev 即可编译项目,编译完成会提示访问地址,会自动打开浏览器访问。
但是访问是会提示404找不到路径,这是因为没有配置路由
在配置文件 config/config.js 中添加配置路由,其中 component 是一个字符串,是相对于 page 目录的相对路径,这样我们默认访问页面就是HelloWorld了
export default {
routes: [{
path: '/',
component: './HelloWorld',
}],
}
添加umi-plugin-react插件集
执行cnpm install umi-plugin-react --save-dev
修改config.js
export default {
plugins: [
['umi-plugin-react', {
// 这里暂时还没有添加配置,该插件还不会有作用,我们会在后面的课程按照需求打开相应的配置
}],
],
routes: [{
path: '/',
component: './HelloWorld',
}],
}
cnpm 安装的依赖会被默认安装到项目的 node_modules 目录下。这个目录通常是不需要提交到代码仓库中的。如果你使用的是 git 来作为代码的管理工具,那么你可以添加 .gitignore 文件到项目根目录中,避免将不必要的代码提交到 git 仓库中。
.gitignore 如下:
node_modules
dist
.umi
其中 .umi 是 umi 在开发过程中产生的临时入口文件,便于开发调试,同样也不需要提交到代码仓库中。dist 是构建出来的产物,通常也不需要提交。
我们建议你可以在本地通过 git 管理起你的代码,方便在后面的课程中更好的操作你的代码。
git init
git add -A
git commit -m 'init'
通过 cnpm run build 来构建出最终的产物,执行该命令后会生成最终的 HTML、CSS 和 JS 到 dist 目录下。它们是浏览器可以直接识别并运行的代码,这样你就可以将它们部署到你想要的服务器上了。
1.3 尝试组件
组件的概念
上一节的HelloWorld就是一个组件,一张网页可以由多个互相独立的功能单位组成,这种功能单位就叫做“组件”(component)。组件内部还可以包含下一级的组件。
组件的好处 :
- 有利于细化 UI 逻辑,不同的组件负责不同的功能点。
- 有利于代码复用,多个页面可以使用同样的组件。
- 有利于人员分工,不同的工程师负责不同的组件。
jsx语法
JSX 语法的特点就是,凡是使用 JavaScript 的值的地方,都可以插入这种类似 HTML 的语法。
注意:1.所有 HTML 标签必须是闭合的2. 只能有一个根元素
//没有闭合语法的标签,必须在标签尾部加上斜杠,比如
// 报错,有两个根元素
const element = hello
world
;
// 不报错,将两个根元素用一个组件包起来
const element = hello
world
;
HTML 原生标签使用小写,自定义的组件标签首字母大写
允许js与jsx混写,{}进入js上下文
const element = (
Hello, {formatName(user)}!
);
组件定义
继承React.Component基类,重写render方法
import React from 'react';
class ShoppingList extends React.Component {
render() {
return (
Shopping List for {this.props.name}
- Instagram
- WhatsApp
- Oculus
);
}
}
export default ShoppingList;
上面代码定义了一个 ShoppingList 组件。自定义的组件必须继承React.Component这个基类,然后必须有一个render方法,给出组件的输出。export 输出的是class,名称要对上
组件的使用
import React from 'React';
import ShoppingList from './shoppinglist.js';
class Content extends React.Component {
render() {
return (
//无内容闭合标签
//有内容标签
{/* 插入的其他内容 */}
);
}
}
export default Content;
标签属性传值:this.props
该组件包含一个name参数
Shopping List for {this.props.name}
组件内部,所有参数都放在this.props属性上面。通过this.props.name就可以拿到传入的值(张三)。
标签内容传值:this.props.children
this.props对象有一个特殊的参数this.props.children,表示当前组件“包裹”的所有内容(类似innerHTML)。比如,上面代码里面的Shopping List for {this.props.name},就是
元素的this.props.children。在 React 里面意味着组件内部可以拿到用户在组件里面放置的内容。
下面是一个组件,内部使用props.children,获取用户传入的内容。
import React from 'React';
const Picture = (props) => {
return (
this is pic page
{props.children}
)
}
class Content extends React.Component {
render () {
//类似static函数,定义常量
const picture = {
src: 'http://img2.imgtn.bdimg.com/it/u=2914548178,2707730282&fm=200&gp=0.jpg',
};
return (
)
}
}
export default Content;
import React from 'React';
const Picture = (props) => {
return (
this is pic page
{props.children}
)
}
class Content extends React.Component {
render () {
//类似static函数,定义常量
const picture = {
src: 'http://img2.imgtn.bdimg.com/it/u=2914548178,2707730282&fm=200&gp=0.jpg',
};
return (
)
}
}
export default Content;
组件内部状态:this.state
除了接受外部参数,组件内部也有不同的状态,组件的内部状态记录在this.state这个对象上面。
import React from 'React';
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Y',
};
}
render() {
return (
);
}
}
export default Square;
上面代码中,组件Square的构造方法constructor里面定义了当前状态this.state对象。Square 组件的这个对象只有一个value属性,一开始的值是Y。
用户点击按钮以后,onClick监听函数执行this.setState()方法。React 使用这个方法,更新this.state对象。这个方法有一个特点,就是每次执行以后,它会自动调用render方法,导致 UI 更新。UI 里面使用this.state.value,输出状态值。随着用户点击按钮,页面就会显示X。
组件生命周期
以下组件只有在初始化的时候才调用。
componentWillMount,在渲染前调用,在客户端也在服务端。
- componentDidMount ,在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
以下组件只在更新的时候才被调用,初始化时不调用。
标记的为常用方法
- componentDidUpdate ,在组件完成更新后立即调用。挂载成功每次调用 render 方法都会触发这个方法
- componentWillUnmount,在组件从 DOM 中移除之前立刻被调用
componentWillReceivePorps,在组件接收到一个新的 prop (更新后)时被调用
shouldComponentUpdate,返回一个布尔值。在组件接收到新的props或者state时被调用,使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。
componentWillUpdate,在组件接收到新的props或者state但还没有render时被调用
static getDerivedStateFromProps(props, state):该方法在render方法执行之前调用,包括组件的第一次记载。它应该返回一个新的 state 对象,通常用在组件状态依赖外部输入的参数的情况。
getSnapshotBeforeUpdate():该方法在每次 DOM 更新之前调用,用来收集 DOM 信息。它返回的值,将作为参数传入componentDidUpdate()方法。