解决前端开发中自动化工具、性能优化、模块化框架、开发规范、代码部署、开发流程等问题
grunt
fis3
gulp
官网:https://webpack.js.org/
模块打包器,可以将有依赖关系的资源经过 webpack 处理后打包生成独立的静态资源
从 v4.0.0 开始,webpack 可以不用再引入一个配置文件来打包项目
指示 webpack 应该使用哪个模块,来作为构建其内部依赖图(dependency graph)的开始。
默认值是 ./src/index.js
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。
主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中
webpack 只能理解 JavaScript 和 JSON 文件,loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
$ npm init -y
$ npm install webpack webpack-cli --save-dev
$ npx webpack
打包时,默认的入口文件是 :./src/index.js
文件,默认的出口文件:./dist/main.js
文件
webpack.config.js
$ npx webpack --config <配置文件路径>
可以将命令行下运行的 npx webpack --config <...>
在 package.json 的 npm scripts
中添加脚本任务:
{
"scripts": {
"start": "webpack --config <配置文件的路径>"
}
}
$ npm i css-loader style-loader -D
如果使用 sass 来开发 css(后缀名为 .scss)
$ npm i sass-loader node-sass -D
如果使用 less 来开发 css(后缀名为 .less)
$ npm i less-loader less -D
$ npm i file-loader -D
安装 html-webpack-plugin 插件来处理 html
安装 clean-webpack-plugin 插件来清理 /dist 目录
安装 webpack-dev-server 本地开发服务器
用于构建用户界面的 JavaScript 库
$ npx create-react-app my-app
创建项目时,会自动安装三个包:
目录结构:
my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
{
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(<h1>Hello React</h1>, document.getElementById('root'))
是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。
const App = (props) => {
return <h1>Hello {
props.message}</h1>
}
在函数内部,返回 JSX 表示的 React 元素,函数参数是使用组件时所传递的属性对象,注意,组件名称必须首字母大写。
// class 组件
class App extends React.Component {
render() {
return <h1>Hello {
this.props.message}</h1>
}
}
在 class 组件中,继承 React.Component 父类,必须在子类中实现一个 render()
方法,用于渲染组件,在渲染过程中,可以使用 this.props
获取组件接收到的属性。
<App message="React" />
在组件中返回的 React 元素需要使用单个根元素包裹,可以使用
组件包裹元素,在生成渲染的 DOM 树中不会出现节点本身信息,也可以使用 <> >
标记来包裹元素节点,与
组件的使用是等效的
主标题
副标题
jsx 实际上是语法糖(React.createElement):
React.createElement(
'div',
{
className: 'container',
title: '提示标题'
},
React.createElement(
'h1',
{
className: 'title'
},
'主标题'
),
React.createElement(
'h2',
null,
'副标题'
)
)
创建出虚拟节点:
{
tagName: 'div',
props: {
className: 'container',
title: '提示标题'
},
children: [
{
tagName: 'h1',
props: {
className: 'title'
},
children: ['主标题']
},
{
tagName: 'h2',
props: null,
children: ['副标题']
}
]
}
虚拟DOM在进行比较时,使用 diff/fiber 算法进行比较(在内存中比较两个节点的异同),比较完毕后,将不同的节点在实体 DOM 树中渲染
在运行时检测 React 组件的 props 类型(对组件属性实行类型约束)
class App extends Component {
static propTypes = {
prop1: PropTypes.string,
prop2: PropTypes.number
}
}
属性默认值
class App extends Component {
static defaultProps = {
prop1: 'hello',
prop2: 0
}
}
在组件的 props 属性中,有一个 children
属性,表示的是使用组件时,内嵌在组件内部的后代节点:
自定义的标题
第二个定义
props.a === '1'
props.b === 'abc'
props.children ===
props 是从父组件中传递到子组件中的数据,通常 props 应该是只读的(在子组件中不应该修改 props 属性值)-- 单向数据流
state 是组件内部需要使用到的数据,可以在组件内实现对 state 数据的修改。
在 React 中,修改 state 中的数据,必须通过调用 setState()
方法来实现修改,才能够在修改数据后页面响应式渲染。
state 在 class 组件中可以直接使用,函数组件中没有 state 的。所以通常将函数组件也叫做无状态组件。
注意:setState() 方法在更新状态数据时是异步的,如果需要获取到更新后的状态数据值,可以在 setState()方法的回调函数中处理。
在 jsx 元素中,直接在元素标签内联书写事件属性
事件属性名采用驼峰命名规范,如:onClick,onChange
单行文本框:绑定 value 属性与处理 onChange 事件
state = {
inputValue: '初始值' // 输入框中绑定的数据
}
// 表单元素(单行文本框)中监测到输入变化时触发调用
changeHandler = (event) => {
// console.log('change', event.currentTarget.value)
// setState() 修改 inputValue 值
this.setState({
inputValue: event.currentTarget.value
})
}
如果需要引用到 DOM 元素或是 React 组件,可以使用 ref 来实现引用。
旧版本 API 通常使用字符串的 ref(不建议再使用):
新版本 API 使用 React.createRef() 实现:
inputRef = React.createRef()
父子通信
跨组件通信
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
创建 Context 对象:
// 创建 Context 对象
cosnt context = React.createContext()
// 解构生产组件与消费组件
const {
Provider, Consumer } = context
// Provider 是向 Context 中存数据及操作数据的方法
// Consumer 是从 Context 中取数据或操作数据的方法
export {
Provider, Consumer }
使用 Context 对象
利用 Provider 组件来保存数据,通常利用 Provider 组件将需要共享数据的组件包裹起来
{}}}>
Provider 接收一个 value
属性,传递给消费组件(Consumer)。当 Provider 的 value
值发生变化时,它内部的所有消费组件都会重新渲染。
利用 Consumer 组件来取数据:
{
(value) => {
/* 根据 value 来渲染节点 */
}
}
在 Consumer 组件内函数表达式中的参数 value 是 Provider 组件保存的value数据
图谱
UNSAFE_componentWillMount()
,不建议使用函数的返回值只依赖其参数值:
函数执行过程中不能造成副作用:
// 是纯函数
function add(num1, num2) {
return num1 + num2
}
const result = add(1, 2)
console.log('result = ', result)
// 不是纯函数
function sort(array) {
for(let i = 0, len = array.length; i < len - 1; i++) {
for(let j = 0; j < len - 1 - i; j++) {
if (array[j] > array[j + 1]) {
const tmp = array[j]
array[j] = array[j + 1]
array[j + 1] = tmp
}
}
}
return array
}
const arr = [3, 7, 1, 2, 0, 5, 4]
console.log('前-arr: ', JSON.stringify(arr))
const result = sort(arr)
console.log('arr: ', JSON.stringify(arr))
console.log('result: ', JSON.stringify(result))
高阶组件
本质上就是一个函数,该函数传递一个组件作为参数,返回一个新的组件,通常返回的新组件是在原组件基础之上作过包装的组件。这其实是装饰者设计模式。
store:仓库,将 state 与更新 state 的动作包装在一起
state:状态,数据
action:是一个普通对象,表明状态更新的动作。通常有两个属性:type 和 payload
reducer:是一个纯函数,主要用于更新状态,传递 state 与 action 作为参数,返回更新后的新的状态数据,reducer 函数是唯一能够更新状态的地方,并且是通过 dispatch() 来分发 action 调用 reducer() 更新状态
dispatch:分发 action,实现更新状态
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
为了描述 action 如何改变 state tree ,你需要编写 reducers。
redux 与 react 并没有直接关系,但两者配合使用最好,在 redux 包中并不包含绑定库,需要单独安装 react-redux
包。
:正常情况下,你的根组件应该嵌套在
中才能使用 connect()
方法。
connect([mapStateToProps], [mapDispatchToProps])
:
mapStateToProps
函数就会被调用。函数必须返回一个纯对象,这个对象会与组件的 props 合并dispatch
函数,然后由你来决定如何返回一个对象。如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch
方法会将 action creator 的返回值作为参数执行。这些属性会被合并到组件的 props 中。标准做法是引入 redux-thunk 中间件
组件缓存 store。通常使用
将整个
组件包裹起来,这样,
后代的组件都可以共享到缓存的 store 了。Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
利用 Hook 可以在函数组件中使用到很多只有在 class 组件中才有的特性。
Hook 使你在无需修改组件结构的情况下复用状态逻辑
Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
Hook 使你在非 class 的情况下可以使用更多的 React 特性
state Hook,可以在函数组件中,调用 React.useState() 来使用 state Hook
const [stateName, setStateMethod] = React.useState(initValue)
// initValue 是一个 state 的初始值
// stateName 是代表 state 状态值的变量名
// setStateMethod 是一个修改 state 状态值的方法
// stateName、setStateMethod 这些名称都可以根据语义取其它的名字
如果一个函数组件中需要使用到多个 state,则调用多次 useState() 方法即可
Effect Hook 可以让你在函数组件中执行副作用操作
如果要在函数组件中使用执行副作用操作的方法(原来在 class 组件中的 componentDidMount() / componentDidUpdate() ),可以使用 React.useEffect() 方法来达到效果。
// componentDidMount() / componentDidUpdate() 都会执行进入
React.useEffect(() => {
})
// componentDidMount()
React.useEffect(() => {
}, [])
useContext()
useCallback()
useReducer()
useRef()
useMemo()
…
路由相关包,在 web 开发中使用 react-router-dom 包,如果是原生app(react-native)开发,则使用 react-router-native 包。
使用
$ yarn create react-app project
或
$ npx create-react-app project
$ cd project
$ yarn add antd
或
$ npm i antd -S
import {
Button } from 'antd'
import 'antd/dist/antd.css'
class MyComponent extends React.Component {
render() {
return <Button type="primary">按钮</Button>
}
}
$ yarn add @craco/craco # 利用社区解决方案来自定义配置时,先安装 craco
安装 craco 后,将 package.json 中 npm scripts
的脚本修改一下:
{
"scripts": {
"start": "craco start",
"build": "craco build",
...
}
}
如果项目中要引入 less 预处理器,则:
$ yarn add craco-less
在项目根目录下添加 craco.config.js
文件,并配置:
const CracoLessPlugin = require('craco-less')
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: {
},
javascriptEnabled: true
}
}
}
}
]
}
注意,如果是在 antd 的 4.x 版本中自定义配置,官方推荐的是 craco
社区解决方案。如果是 antd 的 3.x 版本中,官方推荐的是使用 react-app-rewired 与 customize-cra 来配置(配置文件名称:config-overrides.js )
antd 默认文案是 英文,如果需要修改文案:
import React from 'react'
import { render } from 'react-dom'
import { ConfigProvider } from 'antd'
import zhCN from 'antd/es/locale/zh_CN'
import App from './App'
render(
,
document.getElementById('root')
)