第七期:前端九条启发分享
一、 ts数组类型推导莫名被烦扰
事件是这样的, 周五的时候须要做一个小优化, 某个文件外面本来导出3个 "json对象
", 我须要改成导出三个function
不便依据传参扭转导出内容, 这时就产生了奇怪的类型报错, 假如以后有如下的代码:
上面代码不会报错, 能够失常运行
export interface Dog { name: string; age?: 1 | 2 | 3;}export const a = (): Dog => ({ name: "金毛",});export const b = () => ({ name: "比熊", age: 2,});export const c = () => ({ name: "拉布拉多",});const cc: Dog[] = [a(), b(), c()];
然而奇怪的是, 上面这段代码就会报错
export interface Dog { name: string; age?: 1 | 2 | 3;}export const a = (): Dog => ({ name: "金毛",});export const b = () => ({ name: "比熊", age: 2,});export const c = (): Dog => ({ name: "拉布拉多",});const cc: Dog[] = [a(), b(), c()];
我只是为第三个办法指定了返回值, 第二个办法竟然报错了, 我不为第三个办法指定返回值反而第三个办法不会报错, 接下来还有奇怪的景象, 但我为第三个办法指定为返回any类型的时候, 竟然第二个办法也不报错了:
通过钻研发现, 当数组内某个值被指定为any
的时候, 数组类型会变成any[]
, 所以才有了c()
被指定为返回any不报错。
当a与c
两个办法都被指定为Dog
类型时, ts
发现b办法
的返回值并不一定合乎Dog
的推演, 因为Dog
类型外面的age
只能含有1|2|3
所以报错, 然而如果你齐全不给a b c
指定返回值类型也不会报错, ts会主动推导出2在范畴内, 真神奇!
二、 如何设计 undefined | bl 这种构造的组件
举个例子假如tip提示框
组件, 他有一个是否显示的参数showTip
为布尔值, 当这个值为true
的时候就算用户没有用鼠标悬停这个弹框也会呈现, 为false
就是怎么也无奈让它显示。
以后有一个需要要求进入页面后这个框被动弹出停留3s
后隐没, 那其实咱们很容易就想到将showTip
赋予为true
3秒而后变为undefined
即可, 因为只存在两种状况, 要么true要么undefined, 就好比如下的代码编写形式:
const obj = {};if(isFirstOpenPage){ obj.showTip = true;}return <tip {...obj}>
然而当我应用某个组件库的时候呈现了只有定义过showTip
属性就无奈置为undefined
的状况, 导致我弹出tip
之后就无奈复原到让用户去管制显隐了。
这其实就是代码设计时候要思考的问题了, 当咱们设计一个值为布尔
的属性时, 应该同时也思考到undefined
也可能是一种传值状况。
三、 react如何刷新组件本身
我遇到的状况是, 有a, b, c
三个组件, 这三个组件别离对应三种不同的非凡状况, 每个状况都有可能呈现, 但这三个组件同一时间只能呈现一个, 比方以后呈现了a
组件, 用户点击了敞开才会呈现b
组件, 这就导致每次当用户点击敞开时我要判断是否另外两个组件须要被渲染进去。
问题点就在于比方移除a组件
, 组件库的做法是间接把a的dom构造
移除了, 导致react监控不到。
const [_, forceUpdate] = useReducer(x => x + 1, 0); if (showA) { return <A forceUpdate={forceUpdate} />; } else if (showB) { return <B forceUpdate={forceUpdate} />; } else if (showC) { return <C forceUpdate={forceUpdate} />; } else { return null; }
每个组件外面
onClose={() => { if (forceUpdate) { forceUpdate(); } }}
上述办法的要害就是useReducer
的变动能够引起组件的从新渲染。
四、 为字符串插入jsx元素
事件是这样的, 比方以后咱们有个i18n文案
是highest price {max} - lowest price {min}
, 现有办法能够通过传入两个字符串替换掉{max}
与{min}
, 然而只能用字符串替换, 无奈插入dom构造, 比方我想用蓝色的字来替换。
假如intl
办法能够把字符串外面的{max}替换掉, 咱们对intl
进行裁减变成intlPlus
, 上面是平时应用的形式。
intl.format( "highest price {max} - lowest price {min}" [ "99", "33" ])
intlPlus 的编写
import intl from './intl';const pix = '-------------';export default function intlPlus(i18n: string, arg: any[]) { const len = arg.length; const box = Array(len).fill(pix); const text = intl.format(i18n, box); const textArr = text.split(pix); const res: any[] = []; for (let i = 0; i < textArr.length; i++) { res.push(textArr[i]); if (arg[i] !== undefined) { res.push(arg[i]); } } return res;}
下面的原理就是应用'-------------';
作为站位符号, 而后再按程序对占位符号进行切割, 而后按一个文案一个插值的模式循环插入, 并以数组的模式输入进来, 应用办法如下:
intl.format( "highest price {max} - lowest price {min}" [ "99", <span style={{color: red}}> 33 </span> ])
五、 如何避免测试环境地址泄露
比方咱们平时 测试环境
的地址不要让外界看到, 比方如下的代码就是会泄露测试域名的代码:
const pathObj = { prod: 'www.xxxxxx.com', dev: 'www.xxxxxxx-dev.com', test: 'www.xxxxxx-test.com'}// ....if(location.host === 'localhost:3000'){ window.open(pathObj.dev)}
插件 webpack.DefinePlugin
闪亮退场✨, 这个插件能够让咱们设置一些"全局"
(带引号的)的变量, 这些变量能够在正式 打包之前
应用:
plugins: [ new webpack.DefinePlugin({ isDev: process.env.NODE_ENV === "production" ? JSON.stringify("false") : JSON.stringify("true") }), ]
下面很奇怪的应用了JSON.stringify
, 因为只有这样webpack
能力把它当做字符串来解决, 否则会被当做语句来解决, 接下来咱们举个应用时的例子:
咱们察看打包文件:
察看可知'oooooooo'曾经不存在于打包文件外面了。
它的原理就是在全局进行替换, 比方下面的代码:
// 打包前:if (isDev) { console.log("tttttttttt");} else { console.log("ooooooooooo");}// 打包时:if (false) { console.log("tttttttttt");} else { console.log("ooooooooooo");}
很显著了, tree-shaking
不会放过这种代码, 所以上面的'ooooooo'逻辑就会被删掉。
这个插件有点难了解的点就是, 它并不是运行时执行的, 而是tree-shaking
前进行了一次全局的替换。
之所以它能够间接写在全局isDev
, 然而不能够写window.isDev
就是因为它并没有挂载在widnow对象上, 这类变量并不会在用户端运行。
六、写成{}导出后i18n翻译不实时失效
有很多须要实时变动的配置最好不要设计为json的模式, 比方我的项目里的翻译, 当用户切换语言英语为中文的时候, 发现某些中央翻译没有变动仍然还是英语, 这些中央的特色就是全是导出的json相似上面这种:
export UserName = { title: <span> {i18n(USER_NAME_TITLE)} </span>}
应用形式
import {UserName} from './userName'// ...<Table columns={[UserName]}>
上述写法的表格表头
并不会随着语言的切换而扭转文案。
七、 item2的宰割很好用
命令行工具能够很大的缓解咱们多我的项目启动的问题, 如果你是在vscode
提供的shell
上启动多个我的项目, 就会感觉到来回切换有点吃力了, 更糟的状况是你同时开发两个以上微前端
我的项目, 所以这个item2
必须好好安利一下, 因为这个切实是太好用了, 还没这样玩的敌人能够快快玩起来:
每次点击都会减少一个小窗口, 让咱们看看把控制台分成'6份':
为了避免凌乱, 咱们能够为每个窗口命名:
设置过程如下:
还能够设置本人喜爱的背景图案:
设置过程如下:
八、 tip提醒离奇被暗藏
我在做的一个弹出框外面有个table表格
, 这个table
其中一列的表头默认是弹出状态, 然而遇到的bug是这个弹出状态某些时候会一闪而过, 从新变回未弹出的状态, 看过组件库的源码之后确定不是组件自身的问题。
通过屡次测试发现, 这个bug与点击弹出弹框的按钮的地位无关, 这才让我想明确bug的起因, 因为这个弹出框有一个弹出动画, 从右边由小变大的呈现, 这个变大的过程中理论曾经预渲染了这个弹框外面的组件, 导致弹框呈现的过程中会通过鼠标, 这就导致组件认为用户悬停过, 导致tip暗藏...
解决形式是将这个弹框呈现时的300毫秒的鼠标事件禁止掉。
九、 借助 Whistle 设置线上域名开发本地代码, 避免被csrf进攻
因为server
端常常会加一些安全策略, 比方只能某个referer
的网站能力调用api
, 其余网址过去的都会报403
, 这个时候咱们前端可怜的http://localhost:3000
就遭殃了, 每次都要与相干人员沟通本地调试的问题。
索性咱们间接应用测试环境的域名进行开发就得了, 不应用http://localhost:
(相似于windows批改本地的host文件)总算行了吧...
第一步: 装置Whistle
npm install -g whistlew2 start -p 8899 // 启动服务
间接关上域名即可看到操作界面: http://localhost:8899
第二步: 装置proxy
因为咱们须要把浏览器的申请全副代理到Whistle, 所以须要用到proxy这个谷歌插件,
搜寻 : Proxy SwitchyOmega
这一步是代理浏览器的申请到这个地址
第三步: 配置Whistle
后面的是被代理的地址, 空格前面是指标地址
以后有这样一个页面
代理之后
第六步: Whistle配置秘钥证书
因为Whistle要帮忙咱们解决所有的申请, 这其中也必然会有https申请, 所以肯定要导入Whistle的证书:
功败垂成, 开开心心的应用测试环境域名
进行本地开发
吧。
end
这次就是这样, 心愿与你一起提高。