Css模块化
- 方式一
- 直接将style.css改名为style.module.css
- 使用
import style from './style.module.css
的方式引起 - 使用
<div className={style.xxx}>
来使用样式
- 方式二
npm run eject
暴露webpack.config.js- 搜索
cssRegex
,在底部添加配置 - 使用方式与方式一相同(不需要将style.css改为style.module.css)
// 项目/config/webpack.config.js
// 1.在cssRegex配置modules和exclude
// 2.新增一个cssRegex,配置include(重要,不添加会出现全局样式不生效的bug)
// 3.在cssModuleRegex配置localIdentName
{
test: cssRegex,
use: getStyleLoaders({
importLoaders: 1,
//1. 新增modules配置名字
modules:{
localIdentName: "[path]-[name]-[local]-[hash:base64:6]"
},
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
}),
//2.配置全局样式(该文件夹内不用模块化)
exclude:[
path.join(__dirname,"..","node_modules"),
path.join(__dirname,"..","src/assets/css/common"),
path.join(__dirname,"..","src/components"),
],
sideEffects: true,
},
//3.添加一个loader配置(不加这段,全局样式会不生效)-------
{
test:cssRegex,
use:["style-loader","css-loader"],
include:[
path.join(__dirname,"..","node_modules"),
path.join(__dirname,"..","src/assets/css/common"),
path.join(__dirname,"..","src/components"),
]
},
//---------------------------------------
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
// getLocalIdent: getCSSModuleLocalIdent,
//4.在cssModuleRegex里配置模块化命名
localIdentName: "[path]-[name]-[local]-[hash:base64:6]"
},
}),
},
redux的使用
使用步骤
- 创建
// 根组件
//1.引入redux和react-redux
import { createStore } from 'redux'
import { Provider } from 'react-redux'
//2. 准备数据state和方法action
function counterReducer (state = { count: 4 }, { type, payload }) {
switch (type) {
case 'INC':
return Object.assign({}, state, payload)
case 'DEC':
return Object.assign({}, state, payload)
default:
return state
}
}
//3. 把数据state和方法action存进redux仓库里
let store = createStore(counterReducer)
//4. 注入仓库:使用Provider包裹在根组件外部
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
- 使用
import React, { Component } from 'react'
import { connect } from 'react-redux'
class Index extends Component {
constructor(props) {
super()
this.state = {
num: props.state.count
}
}
increment () {
/*
this.props.dispatch({type,payload})
type:必填,必须叫type,代表action里的某一个方法名
payload:选填,可随便取名,代表要传递的数据
*/
this.props.dispatch({ type: 'INC', payload: { count: ++this.state.num } })
}
decrement () {
this.props.dispatch({ type: 'DEC', payload: { count: --this.state.num } })
}
render () {
return (
<div>
首页
<h3>数量:{this.state.num}</h3>
<button onClick={this.decrement.bind(this)}>-</button>
<button onClick={this.increment.bind(this)}>+</button>
</div>
)
}
}
export default connect(state => {
return {
state: state
}
})(Index)
Saga的使用
- 创建
- 从
redux-saga/effects
引入方法 - 创建一个异步方法
function* saga
- 定义一个方法
function* xxx
,把异步方法使用takeLatest放入其中 - 导出定义好的saga
- 从
// src/saga/index.js
import { call, put, takeLatest } from 'redux-saga/effects'
import Api from './../apis'
/**
* call(fn,argument)
*/
function* fetchProjectInfo({ payload }) {
const res = yield call(Api.fetchProjectInfo, payload.workspaceId) // call(fn,arg),接口的参数需要写在后面,和接口方法平级,都是call的参数
yield put({ type: 'FETCH_PROJECT_INFO', payload: res })
}
function* rootSaga() {
yield takeLatest('SAGA_PROJECT_INFO', fetchProjectInfo)
}
export default rootSaga
- 使用
- 引入createSagaMiddleware
- 执行const sagaMiddleware = createSagaMiddleware()
- 引入applyMiddleware
- 注入store:applyMiddleware(sagaMiddleware)
// src/store/index.js
import { createStore, applyMiddleware } from 'redux'
import reducers from './reducers'
import createSagaMiddleware from 'redux-saga'
import rootSaga from '../saga'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducers,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
export default store
react + typescript
//创建项目
// create-react-app typescript-react-app --typescript
//打开项目
// cd typescript-react-app
//安装依赖,建议使用yarn
//yarn
//运行
//npm run start
数据共享
context
分支:feature/context
context创建
- 创建一个context:React.createContext()
- 添加默认值:defaultValue={key:str|number|boolen|fn|...}
- 解构出Provider与Consumer
- 生成一个类组件
- render函数return时包在最外层
- Provider的value属性是一个以defaultValue同key的对象:
import React,{Component} from 'react'
/**
* 1.创建一个context:React.createContext()
* 2.添加默认值:defaultValue={key:str|number|boolen|fn|...}
* 3.解构出Provider与Consumer
* 4.生成一个类组件
* 5.render函数return时包在最外层
* 6.Provider的value属性是一个以defaultValue同key的对象:<Provider value={obj}></Provider>
*/
interface IProps{
children: ReactNode
}
type IState={
auth: boolean,
username: string,
}
const defaultValue = {
auth:false,
username:'',
signin:()=>{}
}
let {Provider,Consumer:AppConsumer} = React.createContext(defaultValue)
class AppProvider extends Component<IProps,IState> {
constructor(props:IProps){
super(props)
this.state={
auth:false,
username:''
}
}
signIn = ()=>{
this.setState({
auth:true,
username:'John'
})
}
render(){
return (
<Provider value={{
auth:this.state.auth,
username:this.state.username,
signin:()=>this.signIn()
}}>
{
this.props.children
}
</Provider>
)
}
}
export {
AppProvider,//导出包在要用context的组件上
AppConsumer// 导出包在要用context里的数据的组件上
}
context使用
- 在要用到context的组件外部包上AppProvider
- 需要消费context共享的数据的组件包上AppConsumer
//App.tsx
import {AppProvider} from '../src/context/AppContext'
function App() {
return (
<AppProvider>
<div className="App">
<User></User>
</div>
</AppProvider>
);
}
//users/Index.tsx
import Welcome from './Welcome'
import Login from './Login'
import { AppConsumer} from "./../../context/AppContext"
export default class Index extends Component {
render() {
return (
<AppConsumer>
{
({auth})=>{
return auth ? <Welcome></Welcome> : <Login></Login>
}
}
</AppConsumer>
)
}
}
//Login.tsx
import {AppConsumer} from './../../context/AppContext'
export default class Login extends Component {
render() {
return (
<AppConsumer>
{
({signin})=>{
return <button onClick={signin}>login</button>
}
}
</AppConsumer>
)
}
}
Redux
分支:feature/redux
//安装依赖
//yarn add redux react-redux
//需要安装react-redux的类型声明文件
// yarn add @types/react-redux
store的创建
- 在根文件夹创建store/index.ts
- 创建store,塞入reducers
//store/index.ts
import { createStore} from 'redux'
import reducers from './reducers'
const store = createStore(reducers);
export default store;
reducers的创建
通常情况下不同道模块会有自己的reducers,所以使用模块化处理,在store下创建reducers文件夹,用index.ts合并每一个reducers
- 创建单个reducers,为一个函数,接收一个state和action
- 创建合并道reducers/index.ts,引入combineReducers,接收每一个单独道reducers,导出合并的reducers
//store/reducers/index.ts
import { combineReducers} from 'redux'
import todos from './todo'
export default combineReducers({
todos
})
//store/reducers/todo.ts
const TodoReducers = (state,action)=>{
//...
}
export default TodoReducers;
types和aciton的定义
- 单独在store文件夹下建立两个文件夹,将类型放入
- 单独到reducers使用types和action的类型
// store/types/index.ts
export interface ITodo {
id: number,
text: string,
completed: boolean
}
//------------------------------------------------
//store/action/index.ts
const ADD_TODO = 'ADD_TODO'
const TOGGLE_TODO = 'TOGGLE_TODO'
export interface ITodoAciton {
type: typeof ADD_TODO | typeof TOGGLE_TODO ,
id: number,
text: string
}
//------------------------------------------------
//store/reducers/todo.ts
import {ITodo} from '../types'
import {ITodoAciton} from '../action'
const defaultValue = {
id:0,
text:'hello',
completed:false
}
const todoReducers = (state:Array<ITodo>=[defaultValue],action:ITodoAciton)=>{
switch (action.type){
case 'ADD_TODO':
return [
...state,
{
id:action.id,
text:action.text,
completed:false
}
]
default:
return state
}
}
export default todoReducers
创建connect
//pages/todo/connect.ts
import { connect } from 'react-redux'
import { ITodo } from '../../store/types'
import { Dispatch} from 'redux'
type Todos = {
todos: Array<ITodo>
}
const mapStateToProps = (state:Todos)=>{
console.log('state:',state);
return {
todos: state.todos
}
}
const mapDispatchToProps = (dispatch:Dispatch)=>{
return {
add:()=>{
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)
使用connect将redux和组件连接起来
- 相当于一个高阶组件
//pages/todo/visibleList.tsx
import React, { Component } from 'react'
import TodoItem from '../../components/TodoItem'
import connect from './connect'
import { ITodo } from '../../store/types'
type IProps = {
todos: Array<ITodo>
}
class VisibleList extends Component<IProps> {
render() {
return (
<ul>
{
//这里的props就是connect的结果,从reducers那里获取的
//此处的this.props.todos对应着combineReducers({todos}),名字以combineReducers对象的为准
this.props.todos.map(item=>{
return <TodoItem {...item}></TodoItem>
})
}
</ul>
)
}
}
export default connect(VisibleList)
事件派发改变store
- 组件使用connect连接redux
- 使用this.props.fn()来获取connect.tsx里的方法
- connect里定义方法来触发store
//pages/todo/AddTodo.tsx
import React, { Component } from 'react'
import connect from './connect'
let textRef:React.RefObject<HTMLInputElement> = React.createRef();
type P = {
add:(text:string)=>void
}
class AddTodo extends Component<P> {
handleClick=()=>{
//获取input里面的数据,有受控和非受控两种方式,ref为非受控非方式
let txt = (textRef.current as HTMLInputElement).value
//调用添加方法add,即是connect.ts中的add方法
this.props.add(txt)
}
render() {
return (
<div>
<input type="text" ref={textRef} />
<button onClick={this.handleClick}>添加</button>
</div>
)
}
}
export default connect(AddTodo)
//---------------------------------------------------
//pages/todo/connect.ts
import { connect } from 'react-redux'
import { ITodo } from '../../store/types'
import { Dispatch} from 'redux'
type Todos = {
todos: Array<ITodo>
}
let id = 1;
const mapStateToProps = (state:Todos)=>{
console.log('state:',state);
return {
todos: state.todos
}
}
const mapDispatchToProps = (dispatch:Dispatch)=>{
return {
//组件通过this.props.add('123')的方式调用这个方法,同时传参进来
add:(txt:string)=>{
dispatch({ //事件派发给store中的ADD_TODO,同时传递参数
type:"ADD_TODO",
id: id++,
text: txt
})
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)