1.dispatch、dev派发
src/models/formStatus.js:
const FromStatusModel = {
namespace: "fromStatus",
state: {
isDisable: false,
},
reducers: {
saveIsDisable(state, { payload }) {
return {
...state,
...payload,
};
},
},
};
export default FromStatusModel;
改变和提交的js文件写法:
import { useDispatch, useSelector } from 'umi';
const dispatch = useDispatch();
const { isDisable } = useSelector((state) => state.formStatus);
const onclick = () => {
dispatch({
type: 'formStatus/saveIsDisabled',
payload: { isDisable: isDisable === true ? false : true },
});
};
<Button type="primary" onclick={onclick()}>
dev派发
</Button>
<div>是否可以传递{isDisable}</div>
通过useSelector进行传值,通过dispatch进行改变内容。
在 React 和 Redux 的上下文中,"dispatch" 是一个核心概念,它指的是派发(发送)一个 action,这是一个告诉 Redux store 如何更新状态的普通 JavaScript 对象。在 Redux 中,dispatch 是由 Redux 提供的一个 action 派发器(action dispatcher)。
当你在 Redux 应用中派发一个 action 时,Redux store 会根据这个 action 中的信息来更新其状态。这个过程通常是通过 dispatch 一个由 actionCreator
返回的 action 来完成的。
// 假设有一个 actionCreator 函数
function addTodo(text) {
return {
type: 'ADD_TODO',
payload: text
};
}
// 在一个组件中,你可能会这样使用 dispatch
const dispatch = useDispatch();
function handleClick() {
dispatch(addTodo('Learn UMI'));
}
在这个例子中,addTodo
是一个 actionCreator,它返回一个 action 对象。当 handleClick
函数被调用时,它 dispatch 这个 action,导致 Redux store 更新。
在开发过程中,你可能会使用如下的做法来派发 action:
// 使用 Redux 的 actionCreator 创建一个 action
const action = addTodo('Learn UMI');
// 派发 action
dispatch(action);
如果你是在寻找如何在 React 应用中使用 Redux 的 dispatch 方法,那么关键是要确保你有一个 Redux store(通过 Provider
组件提供给你的应用),并且你的组件是通过 useDispatch
钩子来获取 dispatch 函数的。
这个dev中可以加函数操作:
effects: {
*fetchNotices(_, { call, put, select }) {
// 调用queryNotices函数来获取通知数据
const data = yield call(queryNotices);
// 把获取到的数据放入redux的global reducer的notices字段
yield put({
type: "saveNotices",
payload: data,
});
// 计算未读通知的数量
const unreadCount = yield select((state) => state.global.notices.filter((item) => !item.read).length);
// 更新用户通知的总数和未读数
yield put({
type: "user/changeNotifyCount",
payload: {
totalCount: data.length,
unreadCount,
}
})
},
*clearNotices({ payload }, { put, select }) {
// 清除特定的通知
yield put({
type: "saveClearedNotices",
payload,
});
// 获取通知的总数
const count = yield select((state) => state.global.notices.length);
// 获取未读通知的数量
const unreadCount = yield select((state) => state.global.notices.filter((item) => !item.read).length);
// 更新用户通知的总数和未读数
yield put({
type: "user/changeNotifyCount",
payload: {
totalCount: count,
unreadCount,
}
});
},
*changeNoticeReadState({ payload }, { put, select }) {
// 改变特定通知的阅读状态
const notices = yield select((state) => state.global.notices.map((item) => {
const notice = { ...item };
if (notice.id === payload) {
notice.read = true;
}
return notice;
}));
// 更新redux的global reducer的notices字段
yield put({
type: "saveNotices",
payload: notices,
});
// 计算新的未读通知数量
const unreadCount = yield select((state) => state.global.notices.filter((item) => !item.read).length);
// 更新用户通知的总数和未读数
yield put({
type: "user/changeNotifyCount",
payload: {
totalCount: notices.length,
unreadCount,
}
})
}
}
使用Redux Saga中间件处理异步逻辑的例子。它定义了三个sagaEffects,用于处理通知(notices)的不同操作:
fetchNotices
: 获取通知数据,并更新状态和未读通知计数。clearNotices
: 清除通知,并更新状态和通知计数。changeNoticeReadState
: 改变特定通知的阅读状态,并更新状态和未读通知计数。
请注意,这段代码假设您已经设置了一个saga中间件,并且您的action类型(如saveNotices
、user/changeNotifyCount
)和reducer已经正确地在您的应用程序中定义。此外,queryNotices
应该是一个返回Promise的外部函数,它负责从服务器获取通知数据。
-
*fetchNotices(_, { call, put, select }) { ... }
:这是一个使用了 Generator 函数的 Saga,它定义了一个名为 fetchNotices 的异步操作。在这个 Saga 中,它首先调用了queryNotices
函数来获取通知数据,然后将数据保存到应用的状态中,并更新用户的通知数量信息。 -
*clearNotices({ payload }, { put, select }) { ... }
:这是另一个使用了 Generator 函数的 Saga,它定义了一个名为 clearNotices 的异步操作。在这个 Saga 中,它首先保存清除后的通知数据到应用状态中,然后更新用户的通知数量信息。 -
*changeNoticeReadState({ payload }, { put, select }) { ... }
:这也是一个使用了 Generator 函数的 Saga,它定义了一个名为 changeNoticeReadState 的异步操作。在这个 Saga 中,它首先更新通知的阅读状态,然后保存更新后的通知数据到应用状态中,并更新用户的通知数量信息。
总之,这段代码展示了如何使用 Redux-Saga 来处理异步操作和副作用。通过使用 Generator 函数和 effects,可以很好地管理应用中的异步逻辑,并且触发 action 来更新应用的状态。这种方式可以有效地处理复杂的异步操作流程,并且使代码更加清晰和易于维护。
2.页面组件逻辑过多时的写法!
const newDatas = {
dataOne: (obj) => <div style={{ color: 'black' }}>{obj}</div>,
dataTwo: (obj) => <div style={{ color: 'red' }}>{obj}</div>,
dataThree: (obj) => <div style={{ color: 'blue' }}>{obj}</div>,
};
<>
{newDatas.dataOne('第一个')}
{newDatas.dataTwo('第二个')}
{newDatas.dataThree('第三个')}
</>
可以将不同逻辑的组件分开写,虽然写法不好看,但是非常的规则,非常的整齐!
3.ProFormDependency 多级联动
import { ProFormRadio, ProForm, ProFormDependency } from '@ant-design/pro-form';
<ProForm onFinish={() => {}}>
<ProFormRadio.Group
name="client_type"
label="类型选择"
initialValue="一般"
options={[
{ label: '一般', value: '一般' },
{ label: '二般', value: '二般' },
{ label: '三般', value: '三般' },
]}
fieldProps={{
optionType: 'button',
buttonStyle: 'solid',
onChange: (e) => {
if (e?.target?.value === '一般') {
console.log('一般');
} else {
console.log('其他');
}
},
}}
rules={[{ required: true, message: '必填' }]}
/>
<ProFormDependency name={['client_type']}>
{({ client_type }) => {
return ['一般', '四般', '五般']?.includes(client_type) && <div>显示下一级</div>;
}}
</ProFormDependency>
</ProForm>
npm i @ant-design/pro-components --save
调用组建的时候需要安装依赖,单独每个模块进行安装依赖!
4.JavaScript 有哪些数据类型,它们的区别?
JavaScript 共有八种数据类型,分别是 Undefined、Null、Boolean、 Number、String、Object、Symbol、BigInt。
其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:
●Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了 解决可能出现的全局变量冲突的问题。
●BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数, 使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
这些数据可以分为原始数据类型和引用数据类型:
●栈:原始数据类型(Undefined、Null、Boolean、Number、String)
●堆:引用数据类型(对象、数组和函数) 两种类型的区别在于存储位置的不同:
●原始数据类型直接存储在栈(stack)中的简单数据段,占据空间 小、大小固定,属于被频繁使用数据,所以放入栈中存储;
●引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固 定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈 中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引 用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。 堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
●在数据结构中,栈中数据的存取方式为先进后出。
●堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大 小来规定。 在操作系统中,内存被分为栈区和堆区:
●栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的 值等。其操作方式类似于数据结构中的栈。
●堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可 能由垃圾回收机制回收。
5.数据类型检测的方式有哪些
(1) typeof
typeof "sty" //string
数据类型:number、boolean、string、object、function、undefined
其中:数组,对象,null都会被判断为object。
(2) instanceof
"str" instanceof String //
instanceof 可以正确判断对象的类型,其内部运行机制是判断在其 原型链中能否找到该类型的原型。
instanceof 只能正确判断引用数据类型,而不能判断基 本数据类型。instanceof 运算符可以用来测试一个对象在其原型链 中是否存在一个构造函数的 prototype 属性。
// 例子 1
const obj = new Object();
console.log(obj instanceof Object); // true,因为 obj 是 Object 的实例
// 例子 2
class MyClass {}
const myInstance = new MyClass();
console.log(myInstance instanceof MyClass); // true,因为 myInstance 是 MyClass 的实例
// 例子 3
const arr = [1, 2, 3];
console.log(arr instanceof Array); // true,因为 arr 是 Array 的实例
// 例子 4
console.log(null instanceof Object); // false,因为 null 是 null,不是 Object 的实例
(3) constructor
在 JavaScript 中,constructor
属性是一个对象的内部属性,它指向对象的构造函数。每个对象都有一个 constructor
属性,该属性指向创建该对象的函数。
以下是 constructor
属性的几个关键点:
-
访问:你可以通过对象实例直接访问
constructor
属性。例如:const obj = {}; console.log(obj.constructor === Object); // true,因为 obj 是 Object 的实例
-
默认行为:在大多数情况下,当你创建一个对象时,它的
constructor
属性会自动设置为创建该对象的函数。例如:const obj = new Object(); console.log(obj.constructor === Object); // true
-
重写:你可以通过手动设置
constructor
属性来改变它的值。例如:const obj = {}; obj.constructor = Array; console.log(obj.constructor === Array); // true
-
原型链:如果你查看一个对象的原型,你会发现
constructor
属性指向了原型链上的构造函数。例如:const arr = []; console.log(arr.constructor === Array); // true
-
自定义构造函数:当你定义一个自定义的构造函数时,
constructor
属性会指向该构造函数。例如:class MyClass {} const myInstance = new MyClass(); console.log(myInstance.constructor === MyClass); // true
-
使用
constructor
属性:虽然constructor
属性是一个有用的属性,但并不是所有对象都使用它来初始化实例。例如,在箭头函数中,constructor
属性被设置为Function
而不是ArrowFunction
。因此,在使用constructor
属性来判断对象的类型时,需要小心。
总之,constructor
属性是一个方便的方式来访问对象的构造函数,但它并不总是准确地反映对象的实际类型。在判断对象类型时,通常建议使用 instanceof
运算符或 Object.prototype.toString.call()
方法来获取更准确的信息。
(4) Object.prototype.toString.call()
Object.prototype.toString.call()
是一种在 JavaScript 中检查对象类型的方法,它可以返回一个表示对象类型的字符串。这个方法使用 Object.prototype.toString
的 call
方法(或 apply
方法),通过传递一个对象作为参数来调用它。
Object.prototype.toString
方法的默认行为会返回一个表示对象类型的字符串,格式通常为 [object ClassName]
。例如,对于一个数组,它会返回 [object Array]
;对于一个函数,它会返回 [object Function]
。
以下是如何使用 Object.prototype.toString.call()
来检查对象类型的示例:
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(function() {})); // [object Function]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(new Error())); // [object Error]
console.log(Object.prototype.toString.call('hello')); // [object String]
console.log(Object.prototype.toString.call(123)); // [object Number]
使用 Object.prototype.toString.call()
方法的好处是它非常准确,因为它利用了对象的内部属性来判断类型。然而,这个方法也有一些缺点:
-
性能:这个方法比简单的
instanceof
运算符或constructor
属性要慢,因为它涉及到调用一个方法。 -
不可预测的结果:如果你手动改变了对象的
constructor
属性,那么使用Object.prototype.toString.call()
可能会得到不可预测的结果。 -
兼容性:虽然这个方法在所有现代浏览器中都是可用的,但在旧版本的浏览器中可能不支持。
总的来说,Object.prototype.toString.call()
是一个强大的工具,可以用于检查 JavaScript 对象的类型。但与任何工具一样,应根据具体需求和上下文来决定是否使用它。
6.null 和 undefined 区别
在 JavaScript 中,null
和 undefined
是两个不同的概念,它们表示不同的“无值”状态。
-
null
:null
是一个表示“无值”的原始值,即它表示一个明确地没有对象值的情况。- 当一个变量被显式地赋值为
null
时,它的值就是null
。 - 在数值上下文中,
null
被视为 0 或 false,具体取决于上下文。 typeof null
的结果是"object"
,这是一个历史遗留问题,因为最初null
被设计为对象类型。
-
undefined
:undefined
是一个表示“未定义”的原始值,即它表示一个变量已声明但尚未被赋值的情况。- 当一个变量被声明了但没有被赋值时,它的值就是
undefined
。 - 在数值上下文中,
undefined
被视为 NaN(Not a Number)。 typeof undefined
的结果是"undefined"
。
区别:
- 用途不同:
null
通常用于显式地设置一个变量为“无值”,而undefined
通常用于表示一个变量尚未被赋值。 - 数值处理不同:在数值上下文中,
null
被视为 0 或 false,而undefined
被视为 NaN。 - 类型标记不同:
typeof null
返回"object"
,而typeof undefined
返回"undefined"
。
示例:
let a; // a 此时是 undefined
a = null; // a 现在是 null
console.log(a); // null
console.log(typeof a); // object,这是因为在 JavaScript 中 null 被视为对象类型
console.log(a + 1); // 0,因为 null 在数值上下文中被视为 0
console.log(a === null); // true
console.log(a == null); // true,这表示 a 是否为 null 或 undefined
console.log(a === undefined); // false
console.log(typeof b); // undefined,因为 b 从未被声明
总结:null
和 undefined
都是用来表示“无值”的状态,但它们在用法和含义上有所不同。在处理 JavaScript 中的“无值”状态时,了解这两个概念的区别是很重要的。
当对这两种类型使用 typeof 进行判断时,Null 类型化会返回 “object”,这是一个历史遗留的问题。当使用双等号对两种类型的 值进行比较时会返回 true,使用三个等号时会返回 false。
7.intanceof 操作符的实现原理及实现
instanceof
操作符是 JavaScript 中用于检查一个对象是否为某个构造函数的实例。其基本用法如下:
object instanceof constructor
其中,object
是需要检查的对象,constructor
是一个构造函数。如果 object
是 constructor
的实例,或者 object
的原型链中包含 constructor.prototype
,那么表达式的结果为 true
;否则为 false
。
实现原理
instanceof
操作符的实现原理涉及原型链的搜索。具体步骤如下:
- 获取
object
的[[Prototype]]
(即object
的原型)。 - 检查
[[Prototype]]
是否等于constructor.prototype
。 - 如果等于,则返回
true
。 - 否则,继续查找
[[Prototype]]
的[[Prototype]]
,即[[Prototype]]
的原型。 - 重复步骤 2 和 3,直到找到
constructor.prototype
或者object
的原型链结束(即object
的原型链中不再包含constructor.prototype
的原型)。 - 如果整个原型链搜索结束,仍然没有找到
constructor.prototype
,则返回false
。
实现
下面是一个简单的 instanceof
操作符的实现示例:
function myInstanceof(object, constructor) {
let proto = Object.getPrototypeOf(object);
let constructorProto = constructor.prototype;
while (proto !== null) {
if (proto === constructorProto) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
// 使用示例
console.log(myInstanceof({}, Object)); // true
console.log(myInstanceof([], Array)); // true
console.log(myInstanceof(null, Object)); // false
console.log(myInstanceof(123, Number)); // false
这个实现是基于原型链的搜索原理,通过不断查找 object
的原型,直到找到 constructor.prototype
或者原型链结束。
需要注意的是,这个实现没有考虑 instanceof
操作符的细节,比如对 Symbol
类型和 BigInt
类型的支持,以及 constructor
是否应该是一个实际的构造函数。在实际环境中,instanceof
操作符的实现会更加复杂和全面。
-
__proto__
属性:虽然大多数现代浏览器都支持__proto__
属性,但这个属性并不是所有 JavaScript 环境都支持的(例如,Node.js)。因此,使用__proto__
可能会导致跨环境的兼容性问题。在标准 JavaScript 中,应该使用Object.getPrototypeOf
方法来获取对象的原型。 -
null
检查:在原型链的搜索中,如果遇到null
,应该立即返回false
。这是因为null
没有原型,不应该在原型链中继续搜索。 -
循环退出条件:如果原型链中没有
constructorProto
,函数应该返回false
,而不是在无限循环中。 -
性能:在每次调用
instanceof
时,这个函数都会执行一个循环,直到找到匹配或者原型链结束。对于大型对象或深层原型链,这可能会导致性能问题。
8. 如何获取安全的 undefined 值?
在 JavaScript 中,undefined
是一个表示“未定义”的原始值,它代表了变量的值尚未被赋值时的状态。由于 undefined
是一个全局变量,直接赋值给一个变量会导致 undefined
被污染,从而影响 undefined
的正常判断。
为了获取一个安全的 undefined
值,可以使用 void 0
表达式。这是因为 void
操作符用于执行一个表达式,但不返回任何值。当 void 0
被执行时,它不返回任何值,但实际上执行的是 undefined
赋值操作,因此它等同于 undefined
。
这里有一个例子:
let safeUndefined = void 0;
console.log(safeUndefined); // undefined
在这个例子中,safeUndefined
被赋值为 void 0
,即 undefined
。这样,我们就得到了一个安全的 undefined
值,它不会污染全局的 undefined
变量。
使用 void 0
来获取 undefined
是一种在函数中传递参数时保持参数原始值的方法,特别是在使用模块化编程时,这种方法可以帮助保持代码的清晰性和可维护性。
9.Object.is() 与比较操作符 “===”、“==” 的区别?
Object.is()
是 ECMAScript 6 (ES6) 引入的一个新的比较操作符,它用于比较两个值是否完全相等。Object.is()
与传统的比较操作符 ===
和 ==
有以下区别:
-
Object.is(value1, value2)
- 比较两个值是否严格相等,即值和数据类型都必须相同。
- 对于
NaN
,Object.is(NaN, NaN)
返回true
,而 === 和 == 都会返回false
。 - 对于
+0
和-0
,Object.is(+0, -0)
返回false
,而 === 和 == 都会返回true
。
-
===(严格等于)
- 比较两个值是否严格相等,即值必须相同,但数据类型可以不同。
- 对于
NaN
,=== 返回false
,因为它与任何值都不相等,包括它自己。 - 对于
+0
和-0
,=== 返回true
,因为在数学上它们是相等的。
-
==(等于)
- 比较两个值是否相等,如果数据类型不同,会进行类型转换,然后再进行比较。
- 对于
NaN
,== 返回false
,因为它与任何值都不相等,包括它自己。 - 对于
+0
和-0
,== 返回true
,因为它们在数值上下文中被视为相等。
总结:
Object.is()
提供了与 === 类似的行为,但处理了NaN
和+0/-0
的特殊情况。- === 用于需要严格相等比较的情况,不进行类型转换。
- == 用于需要进行类型转换的比较,可能返回意外的结果。
使用 Object.is()
可以帮助你更准确地比较两个值,特别是在需要处理 NaN
和 +0/-0
的情况下。