1 | function getMonthStartEnd (paramDate) { |
1 | console.log(getMonthStartEnd('2023-12')) // {s: 2023-12-01, e: 2023-12-31} |
1 |
|
1 | console.log(getStartEnd('day')) |
作者:技术胖
原文地址:https://jspang.com/article/81#top
公司的测试项目和我自己的项目全部使用React开发,所以先来推荐我经常使用的React组件库。
Ant Design :文档齐全,社区生态良好,有手机版,还有PC版。可以用来快速创建手机/后台/内部应用的UI组件库。我的博客,就是那这个组件库制作的。
React Bootstrap : BootStrap我在JQuery时代就开始使用,现在已经推出了React Bootstrap, 虽然UI设计上没什么出彩,但是快速完成个人小项目已经足够了。
MATERIAL-UI:实现了Google的Material Design 全新设计语言的React组件库。在Github上有超过5万star,最受欢迎的React组件库之一。现在也叫做Mui。
网址:material-ui.com/ or https://mui.com/zh/
目前公司大部分的前端开发使用的是Vue框架,所以好的Vue UI组件库也必须推荐一下。而且Vue在国内也是最流行的前端框架。
Element : 国内最流行的Vue UI组件库,文档完整,支持在Vue3下使用。而且Element还提供了Element-React版本和Element-Angular版本。也就是说你掌握一套UI,可以在主流的前端开发框架中使用,如果你只学一套UI组件库,学这个就没错了。
vant :有赞公司旗下的开源组件库,适用于移动端开发。组件齐全,使用简单,UI设计也非常漂亮。
网址:youzan.github.io/vant/#/zh-C…
View UI: 我刚接触Vue开发使用,第一个项目中使用的UI组件库,UI设计的非常精美,有免费版和专业版(也就是收费版)。从收费版出了以后,我使用的就少了。
如果想页面制作的好看,动画效果一定是少不了的。所以必须推荐动画插件。
Animate.css 纯css的动画库,所以无论你前端使用什么框架,都可以非常方便的使用它,内置了超多的动画效果,并且预览起来也非常方便。网址:https://animate.style/
我是真的很喜欢这个动画库,我参与的90%的项目中都使用到了这个动画库。
所有介绍了一些UI组件库,组件库的组件大而全,但精细程度有时候是不够的,所以有时候我们还需要一些小而精的专属组件。
轮播图组件:swiper,这个提供的轮播图效果太多了,中文文档齐全,还有交流QQ群。支持原生、JQuery、Vue、React、Angular等,任何前端开发场景中使用。而且还有PC端和移动端。比较讨厌的是官网有很多弹出广告,但也能理解,为了生存吗。
滚动插件:mescroll.js , 移动端的滚动经常会出现不流畅,延迟等问题。我也尝试着去使用了很多大神些的滚动插件,多方比较,我选定了这款插件。他是基于H5的,不依赖来与其它前端框架,侵入性很小,实用性很大。如果你在滚动中遇到了问题,可以使用这个插件来解决。
开发中除了界面的制作,也需要一些工具类的轮子,下面就介绍一下我开发中使用的一些工具类轮子。
函数库:lodash.js ,是一个一致性、模块化、高性能的JavaScript实用工具库。这个函数库可以在原生JS中使用,也可以在React和Vue中使用。几乎你开发中所有的函数,这个库都给你写好。你需要作的就是熟练和恰到好处的使用。
函数库:Day.js , 它是一个极简的JavaScript库,可以为你很好的验证、操作和显示日期和时间。
timeago.js,它是显示几小时之前,很多随时更新的网站和应用,为了显示出及时性,不再显示具体发布的时间,而是改为几分钟或者几小时前发布的。这样显着时效性更好。这个插件的作用就是这个。如果需要,请收藏好。
echarts 数据可视化: 我们公司的所有数据可视化插件都使用的这个组件库,可以满足企业级开发的需求。这个也算是国内最好的可视化开发组件库了。
Markdown编辑器: Markdown ,作为程序员,编写Markdown格式的文档已经是必会技能了,所以如果是为程序员开发的应用和网站,一定要支持Markdown功能。我在选择Markdown编辑器时踩了很多坑,最终才找到了这款好用的组件。
表单验证:validator.js ,这个组件我想大部分人都用过,因为无论时用户端,还是管理端前台开发验证都时必须的,无论是公司,还是个人。我都一直在使用这个验证插件。
Vue拖拽组件 :vue-draggable, 用于现在开发的应用都需要有移动端,所以拖拽操作越来越多了,它是我目前看到的基于Vue的最好拖拽组件。
Vue生成二维码:vue-qr 如果你需要生成二维码,用这个组件绝对没错,公司的项目一直在使用,可以方便快捷的生成任何形式的二维码。包括彩色和自定义样式。
Vue图片剪裁 : vue-cropper 无论开发任何应用,都需要用户上传图片。但又为了保持页面的一致性,所以要对上传的图片,安装设计规范,进行裁切。这时候你就可以使用这个组件了。
图片懒加载:vue-lazyload 其实很多UI组件库已经有这个图片懒加载的给功能了,但是还是单独提出来一下,因为它不会和其它Vue组件库冲突,而且功能更多。
Vue上传组件:vue-simple-upload 上传也是我们绕不开的开发需求,所以你必须拥有一个完全好用的上传组件。它非常好用,但缺点是没有官方网站,只有一个Github地址。
]]>其实还有很多常用的轮子,但这20个是我工作中使用频率比较高的前20个,如果你有什么好用的前端轮子,也可以在评论区留言。如果以后再遇到好的组件库和轮子,篇文章下面不断的更新。
1 | class URL { |
1 | import URL from 'url' |
1 | { |
/hybrid/html/muiplayer.html
,这里的html文件要写你自己的文件名/hybrid/html/muiplayer.html?p=123
1 | // 页面 生命周期 onLoad 中 |
?=
、反向预查匹配?<=
、正向预查不匹配?!
、反向预查不匹配?<!
uni-app中,替换url中的//
为/
,但是不替换://
1 | 'https://www.aaa.com//a.html'.replace(/(?<=\w)\/+/g, '/') |
https://www.aaa.com/a.html
打包后APP无法启动,
调试基座可以启动,但是当进入写有正则的页面后,会卡死
1 | 'https://www.aaa.com//a.html'.replace(/(\w)\/+/g, '$1/') |
1 | // 此接口返回host地址和一些广告配置,广告用不上忽略即可 |
1 |
|
1 | // {sex} 可取值 man、lady |
1 | https://scxs.pysmei.com/Categories/BookCategory.html |
1 | // 此处{id}为所有分类接口获取的分类id |
1 | // {sex} 可取值 man、lady |
1 | // {id}为书单列表接口返回的书单id |
1 | // {id} 小说id,比较特殊,举例说明 |
1 | // {id} 图片id,小说详情接口中返回 |
1 | // 此处的{id} 同详情的{id} |
1 | // {sw} 搜索词 |
1 | https://scxs.pysmei.com//StaticFiles/NewHotBook.html |
掘金大牛:「历时8个月」10万字前端知识体系总结(基础知识篇)
原始html内容,有xss风险
监听引用类型,使用下面方法深度监听,但是拿不到oldVal
1 | watch:{ |
1 | data () { |
或者
1 | :class=[black, red] |
object 类型,驼峰写法
v-if 是否渲染,更新不频繁使用
v-show 是通过 css 的 display 控制显示与隐藏,频繁切换使用
v-for
优先级比v-if
高$event
event
是原生对象1 | @click.stop.prevent |
event.target
的值表明事件是挂在当前元素上的
v-model.trim
v-model.lazy
v-model.number
简写
1 | props:['list'] |
复杂写法,常用,可以定义类型和默认值
1 | props: { |
父组件
1 | <Input @add="clickhandler"/> |
子组件
1 | methods:{ |
vue 具有自定义事件能力 $on、$off、$emit
组件卸载时,解除事件绑定,销毁子组件,定时器等
created:vue实例化完成
mounted:渲染完成
组件实例创建: 先父后子
组件渲染: 先子后父【子组件渲染完,才能挂载】
父组件中
1 | <CunstomVModal v-model={name}> |
子组件中 CunstomVModal
1 | <template> |
1 | <template> |
1 | export default { |
1 | // 每个组件只渲染一次,且不会销毁。 |
1 | <sript> |
用js模拟dom结构,计算出最小的变更,操作dom
h函数,返回vnode(js)
patch(container, vnode) 初次渲染
patch(oldVnode, newVnode) 更新,新旧vnode都有children,那么updateChildren
patch(oldVnode, null) 销毁清空
- with语法
- vue组件可以用render代替template
createApp
1 | // vue2 |
emits属性
1 | emits: ['onClickhandler'] // 建议 onXxx |
生命周期
多事件处理, @click=”one($event),two($event)”
Fragment, 模板不需要包在一个根组件中了,template下面可以写多个标签
移除.sync改为v-model参数
异步组件的引用方式, defindAsyncComponent
移除filter,如:v-if=”boolA | boolB”
Teleport,
Suspense
1 | <Suspense> |
1 | import { onBeforeMount } from 'vue' |
1 | export default { |
1 | <template> |
1 | // watch 默认不会立即执行 |
1 | this.setState((state, props) => {}) |
Transaction类的主要作用使用提供的包装(Wrapper)来包装一个函数。
Transaction会接受一个方法 func,和一组Wrapper。Transaction会在func执行之前,执行一组Wrapper中的initialize方法。而后执行func方法,在func方法执行完了之后,执行Wrapper提供的所有close方法。
1、共同
2、不同
拆分配置 和 merge
启动本地服务 webpack-dev-server
处理 ES6
1 | modules.exports = { |
1 | { |
处理样式
处理图片
模块化
1 | entry: { |
1 | coonst MiniCssExtracctplugin = require('mini-css-extract-plugin') |
1 | const TerserJSPlugin = require('terser-webpack-plugin') |
1 | module.exports = { |
1 | // 定义 chunk |
1 | { |
1 | // 不引入所有语言包 |
1 | modules.exports={ |
1 | const HappyPack = require('happypack') |
new ParallelUglifyPlugin({
uglifyJS: {
// 还是使用 uglifyjs 压缩,只是开启了多进程
output: {
beautify: false, // 最紧凑的输出
comments: false // 删除所有注释
},
compress: {
// 删除所有 console 语句,可兼容ie
drop_console: true,
}
}
})
1 | module.exports = { |
1、打包dll配置 webpacl.dll.js
1 | module.exports = { |
2、修改index.html模板
添加
3、配置webpack.dev.js
1 |
|
体积更小
合理分包,不重复引用
速度更快,内损使用更少
小图片base64编码
bundle + hash
懒加载 import 语法
提取公共代码 splitChunks
IgnorePlugin
cdn加速
使用 production
Scope Hosting 改变打包作用域
1 | const ModuleConcatenationPlugin = require('webpack/lib/optmize/ModuleConcatenationPlugin') |
1 | { |
。babelrc中
1 | plugins: [ |
本文转载自https://github.com/ascoders/weekly/blob/master/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF/255.%E7%B2%BE%E8%AF%BB%E3%80%8ASolidJS%E3%80%8B.md
为什么要介绍 SolidJS 而不是其他前端框架?因为 SolidJS 在教 React 团队正确的实现 Hooks,这在唯 React 概念与虚拟 DOM 概念马首是瞻的年代非常难得,这也是开源技术的魅力:任何观点都可以被自由挑战,只要你是对,你就可能脱颖而出。
整篇文章以一个新人视角交代了 SolidJS 的用法,但本文假设读者已有 React 基础,那么只要交代核心差异就行了。
SolidJS 仅支持 FunctionComponent 写法,无论内容是否拥有状态管理,也无论该组件是否接受来自父组件的 Props 透传,都仅触发一次渲染函数。
所以其状态更新机制与 React 存在根本的不同:
与 React 整个渲染函数重新执行相对比,Solid 状态响应粒度非常细,甚至一段 JSX 内调用多个变量,都不会重新执行整段 JSX 逻辑,而是仅更新变量部分:
1 | const App = ({ var1, var2 }) => ( |
上面这段代码在 var1
单独变化时,仅打印 var1
,而不会打印 var2
,在 React 里是不可能做到的。
这一切都源于了 SolidJS 叫板 React 的核心理念:面相状态驱动而不是面向视图驱动。正因为这个差异,导致了渲染函数仅执行一次,也顺便衍生出变量更新粒度如此之细的结果,同时也是其高性能的基础,同时也解决了 React Hooks 不够直观的顽疾,一箭 N 雕。
SolidJS 用 createSignal
实现类似 React useState
的能力,虽然看上去长得差不多,但实现原理与使用时的心智却完全不一样:
1 | const App = () => { |
我们要完全以 SolidJS 心智理解这段代码,而不是 React 心智理解它,虽然它长得太像 Hooks 了。一个显著的不同是,将状态代码提到外层也完全能 Work:
1 | const [count, setCount] = createSignal(0); |
这是最快理解 SolidJS 理念的方式,即 SolidJS 根本没有理 React 那套概念,SolidJS 理解的数据驱动是纯粹的数据驱动视图,无论数据在哪定义,视图在哪,都可以建立绑定。
这个设计自然也不依赖渲染函数执行多次,同时因为使用了依赖收集,也不需要手动申明 deps 数组,也完全可以将 createSignal
写在条件分支之后,因为不存在执行顺序的概念。
用回调函数方式申明派生状态即可:
1 | const App = () => { |
这是一个不如 React 方便的点,因为 React 付出了巨大的代价(在数据变更后重新执行整个函数体),所以可以用更简单的方式定义派生状态:
1 | // React |
当然笔者并不推崇 React 的衍生写法,因为其代价太大了。我们继续分析为什么 SolidJS 这样看似简单的衍生状态写法可以生效。原因在于,SolidJS 收集所有用到了 count()
的依赖,而 doubleCount()
用到了它,而渲染函数用到了 doubleCount()
,仅此而已,所以自然挂上了依赖关系,这个实现过程简单而稳定,没有 Magic。
SolidJS 还支持衍生字段计算缓存,使用 createMemo
:
1 | const App = () => { |
同样无需写 deps 依赖数组,SolidJS 通过依赖收集来驱动 count
变化影响到 doubleCount
这一步,这样访问 doubleCount()
时就不用总执行其回调的函数体,产生额外性能开销了。
对标 React 的 useEffect
,SolidJS 提供的是 createEffect
,但相比之下,不用写 deps,是真的监听数据,而非组件生命周期的一环:
1 | const App = () => { |
这再一次体现了为什么 SolidJS 有资格 “教” React 团队实现 Hooks:
在 SolidJS,生命周期函数有 onMount
、onCleanUp
,状态监听函数有 createEffect
;而 React 的所有生命周期和状态监听函数都是 useEffect
,虽然看上去更简洁,但即便是精通 React Hooks 的老手也不容易判断哪些是监听,哪些是生命周期。
为什么 SolidJS 可以这么神奇的把 React 那么多历史顽疾解决掉,而 React 却不可以呢?核心原因还是在 SolidJS 增加的模板编译过程上。
以官方 Playground 提供的 Demo 为例:
1 | function Counter() { |
被编译为:
1 | const _tmpl$ = /*#__PURE__*/ template(`<button type="button"></button>`, 2); |
首先把组件 JSX 部分提取到了全局模板。初始化逻辑:将变量插入模板;更新状态逻辑:由于 insert(_el$, count)
时已经将 count
与 _el$
绑定了,下次调用 setCount()
时,只需要把绑定的 _el$
更新一下就行了,而不用关心它在哪个位置。
为了更完整的实现该功能,必须将用到模板的 Node 彻底分离出来。我们可以测试一下稍微复杂些的场景,如:
1 | <button> |
这段代码编译后的模板结果是:
1 | const _el$ = _tmpl$.cloneNode(true), |
将模板分成了一个整体和三个子块,分别是字面量、变量、字面量。为什么最后一个变量没有加进去呢?因为最后一个变量插入直接放在 _el$
末尾就行了,而中间插入位置需要 insert(_el$, count, _el$4)
给出父节点与子节点实例。
SolidJS 的神秘面纱已经解开了,下面笔者自问自答一些问题。
React Hooks 使用 deps 收集依赖,在下次执行渲染函数体时,因为没有任何办法标识 “deps 是为哪个 Hook 申明的”,只能依靠顺序作为标识依据,所以需要稳定的顺序,因此不能出现条件分支在前面。
而 SolidJS 本身渲染函数仅执行一次,所以不存在 React 重新执行函数体的场景,而 createSignal
本身又只是创建一个变量,createEffect
也只是创建一个监听,逻辑都在回调函数内部处理,而与视图的绑定通过依赖收集完成,所以也不受条件分支的影响。
因为 SolidJS 函数体仅执行一次,不会存在组件实例存在 N 个闭包的情况,所以不存在闭包问题。
React 响应的是组件树的变化,通过组件树自上而下的渲染来响应式更新。而 SolidJS 响应的只有数据,甚至数据定义申明在渲染函数外部也可以。
所以 React 虽然说自己是响应式,但开发者真正响应的是 UI 树的一层层更新,在这个过程中会产生闭包问题,手动维护 deps,hooks 不能写在条件分支之后,以及有时候分不清当前更新是父组件 rerender 还是因为状态变化导致的。
这一切都在说明,React 并没有让开发者真正只关心数据的变化,如果只要关心数据变化,那为什么组件重渲染的原因可能因为 “父组件 rerender” 呢?
虚拟 dom 虽然规避了 dom 整体刷新的性能损耗,但也带来了 diff 开销。对 SolidJS 来说,它问了一个问题:为什么要规避 dom 整体刷新,局部更新不行吗?
对啊,局部更新并不是做不到,通过模板渲染后,将 jsx 动态部分单独提取出来,配合依赖收集,就可以做到变量变化时点对点的更新,所以无需进行 dom diff。
count()
不能写成 count
?笔者也没找到答案,理论上来说,Proxy 应该可以完成这种显式函数调用动作,除非是不想引入 Mutable 的开发习惯,让开发习惯变得更加 Immutable 一些。
由于响应式特性,解构会丢失代理的特性:
1 | // ✅ |
虽然也提供了 splitProps
解决该问题,但此函数还是不自然。该问题比较好的解法是通过 babel 插件来规避。
没有 deps 虽然非常便捷,但在异步场景下还是无解:
1 | const App = () => { |
SolidJS 的核心设计只有一个,即让数据驱动真的回归到数据上,而非与 UI 树绑定,在这一点上,React 误入歧途了。
虽然 SolidJS 很棒,但相关组件生态还没有起来,巨大的迁移成本是它难以快速替换到生产环境的最大问题。前端生态想要无缝升级,看来第一步是想好 “代码范式”,以及代码范式间如何转换,确定了范式后再由社区竞争完成实现,就不会遇到生态难以迁移的问题了。
但以上假设是不成立的,技术迭代永远都以 BreakChange 为代价,而很多时候只能抛弃旧项目,在新项目实践新技术,就像 Jquery 时代一样。
如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
关注 前端精读微信公众号
]]>版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)
1 | function formatSecondToHHmmss(time) { |
1 | console.log(formatSecondToHHmmss(75000)) // 00:01:15 |
git commit -m ‘type: (scope:) subject’
/空行/
(body)
type 必需,用于说明 commit 的类别,只允许使用下面8个标识,并且为小写
scope 可选, 用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
subject 必须,是 commit 目的的简短描述,不超过50个字符。
body 可选,部分是对本次 commit 的详细描述,可以分成多行。
1 | const arr = [1, [2, [3, [4, 5]]]] |
谁调用方法,this就指向谁
1 | const obj = { |
其实不必烦恼,git是支持配置多账号的。废话不多说,直接上干货。
通过 ssh-keygen 命令生成多个账号的key,
注意:回车之后,有一步是填写密钥文件的路径和名称,此时可修改路径和名称,建议路径不要改,只改名字,以便区分。默认是 C:/Users/用户名/.ssh/id_rsa
1 | ssh-keygen -t rsa -c "你的邮箱" |
在C:\Users\用户名.ssh 文件夹下面创建 config 文件,注意,这个文件没有后缀,然后用记事本打开,然后写配置,怕麻烦的就复制下面的,改吧改吧就行了
1 | # 配置 github.com |
将 对应的 key 粘贴到对应网站 添加 ssh-key 的地方。
]]>线程和进程区别:多线程可以并行处理任务,线程不能单独存在,它是由进程来启动和管理的。一个进程是一个程序的运行实例。
线程和进程的关系:1、进程中任意一线程执行出错,都会导致整个进程的崩溃。2、线程之间共享进程中的数据。3、当一个进程关闭后,操作系统会回收进程所占用的内存。4、进程之间的内容相互隔离。
单进程 浏览器:1、不稳定。单进程中的插件、渲染线程崩溃导致整个浏览器崩溃。2、不流畅。脚本(死循环)或插件会使浏览器卡顿。3、不安全。插件和脚本可以获取到操作系统任意资源。
多进程浏览器:1、解决不稳定。进程相互隔离,一个页面或者插件崩溃时,影响仅仅时当前插件或者页面,不会影响到其他页面。2、解决不流畅。脚本阻塞当前页面渲染进程,不会影响到其他页面。3、解决不安全。采用多进程架构使用沙箱。沙箱看成时操作系统给进程上来一把锁,沙箱的程序可以运行,但是不能在硬盘上写入任何数据,也不能在敏感位置读取任何数据。
多进程架构:分为 浏览器进程、渲染进程、GPU 进程、网络进程、插件进程。
缺点:1、资源占用高。2、体系架构复杂。
面向服务架构:把原来的各种模块重构成独立的服务,每个服务都可以在独立的进程中运行,访问服务必须使用定义好的接口,通过 IPC 通讯,使得系统更内聚、松耦合、易维护和拓展。
用户输入 URL 并回车
浏览器进程检查 URL,组装协议,构成完整 URL
浏览器进程通过进程通信(IPC)把 URL 请求发送给网络进程
网络进程接收到 URL 请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程
如果没有,网络进程向 web 服务器发起 http 请求(网络请求),请求流程如下:
进行 DNS 解析,获取服务器 IP 地址,端口
利用 IP 地址和服务器建立 tcp 连接
构建请求头信息
发送请求头信息
服务器响应后,网络进程接收响应头和响应信息,并解析响应内容
网络进程解析响应流程:
检查状态码,如果是 301/302,则需要重定向,从 Location 自动读取地址,重新进行第 4 步,如果是 200,则继续处理请求
200 响应处理:检查响应类型 Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续渲染。如果是 html 则通知浏览器进程准备渲染进程进行渲染
准备渲染进程
浏览器进程检查当前 URL 是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程
传输数据、更新状态
渲染进程准备好后,浏览器向渲染进程发起“提交文档”的消息,渲染进程接收到消息和网络进程建立传输数据的“管道”
渲染进程接收完数据后,向浏览器发送“确认提交”
浏览器进程接收到确认消息后 engine 浏览器界面状态:安全、地址 URL、前进后退的历史状态、更新 web 页面
当执行 new CreateObj 的时候,JavaScript 引擎做了四件事:
this 的使用分为:
动态语言:在使用时需要检查数据类型的语言。
弱类型语言:支持隐式转换的语言。
JavaScript 中的 8 种数据类型,它们可以分为两大类——原始类型和引用类型。
原始类型数据存放在栈中,引用类型数据存放在堆中。堆中的数据是通过引用与变量关系联系起来的。
从内存视角了解闭包:词法扫描内部函数,引用了外部函数变量,堆空间创建一个“closure”对象,保存变量。
使用 Promise 解决了回调地狱问题,消灭嵌套和多次处理;
模拟实现 Promise
functionBromise(executor) {
var _onResolve = nullthis.then = function (onResolve) {
_onResolve = onResolve
}
functionresolve(value) {
setTimeout(() => {
_onResolve(value)
}, 0)
}
executor(resolve, null)
}
await xxx
会创建一个 Promise 对象,将 xxx
任务提交给微任务队列;resolve(xxx)
等待执行,触发 then 的回调函数;]]>
Popup
为例,以最方便快捷的形式发布一个完整的 npm 包。本文包含以下内容:
Popup
组件的开发;
一些工具的使用
README.md
文件。本文不会和组件库那篇文章一般死扣打包细节,因为单个组件和组件库的打包有本质上的区别:
tsdx 内置三种项目模板:
模板还内置了start
、build
、test
以及lint
等 npm scripts,的确是零配置开箱即用(大误)。
为了方便讲解,此处选择react
模板。
执行npx tsdx create react-easy-popup
,选择react
完成项目创建后进入项目目录。
由于tsdx
没有提供样式文件打包支持,使用css in js
方案会带来额外的依赖以及运行时消耗,所以需要简单配置一下tsdx
以支持 less 样式。
参照customization-tsdx这一小节进行配置。
安装相关依赖:
1 | yarn add rollup-plugin-postcss autoprefixer cssnano less --dev |
新建 tsdx.config.js
,写入以下内容:
tsdx.config.js
1 | const postcss = require('rollup-plugin-postcss'); |
在 package.json
中配置browserslist
字段。
package.json
1 | // ... |
清空src
目录,新建index.tsx
、index.less
。
src/index.tsx
1 | import * as React from 'react'; |
src/index.less
1 | .react-easy-popup { |
example/index.tsx
1 | import 'react-app-polyfill/ie11'; |
进入项目根目录,执行以下命令:
1 | yarn start |
现在 src
目录下的内容的变更会被实时监听,在根目录下生成的dist
文件夹包含打包后的内容。
开发时调试的文件夹为example
,另起一个终端。执行以下命令:
1 | cd example |
在localhost:1234
可以发现项目启动啦,样式生效且有浏览器前缀。
若 example 启动后网页报错,删除 example 下的.cache 以及 dist 目录重新 start
需要注意的是 example
的入口文件index.tsx
引入的是我们打包后的文件,即dist/index.js
。
但是引入路径却为'../.'
,这是因为 tsdx
使用了 parcel
的 aliasing。
同时,观察根目录下的dist
文件夹:
dist
1 | ├── index.d.ts # 组件声明文件 |
也可以很轻易地在package.json
中找到main
、module
以及typings
相关配置。
基于 rollup 手动搭一个组件模板并不困难,但是社区已经提供了方便的轮子,就不要重复造轮子啦。既要有造轮子的能力,也要有不造轮子的觉悟。似乎我们正在造轮子?
Popup
在移动端场景下极其常见,其内部基于Portal
实现,自身又可以作为Toast
和Modal
等组件的下层组件。
要实现Popup
,就要先基于ReactDOM.createPortal实现一个Portal
。
此处结合官方文档做一个简单总结。
Portal
是一种将子节点渲染到存在于父组件以外的 DOM
节点的优秀的方案。overflow: hidden
或 z-index
样式,我们又需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。同时还有很重要的一点:portal
与普通的 React
子节点行为一致,仍存在于React
树,所以Context
依旧可以触及。有一些弹层组件会提供xxx.show()
的 API 形式进行弹出,这种调用形式较为方便,虽然底层也是基于Portal
,但是内部重新执行了ReactDOM.render
,脱离了当前主应用的React
树,自然也无法获取到Context
。
清空 src 目录,新建以下文件:
1 | ├── index.less # 样式文件 |
在编写代码之前,需要确定好Portal
组件的 API。
与ReactDOM.createPortal
方法接受的参数基本一致:指定的挂载节点以及内容。唯一的区别是:Portal
在未传入指定的挂载节点时,会创建一个节点以供使用。
属性说明类型默认值
node可选,自定义容器节点HTMLElement-
children需要传送的内容ReactNode-
在type.ts
中写入Portal
的Props
类型定义。
src/type.ts
1 | export type PortalProps = React.PropsWithChildren<{ |
现在开始编写代码:
1 | import * as React from 'react'; |
注意:此处没有使用 React.FC 去进行声明 react-typescript-cheatsheet:Section 2: Getting Started => Function Components => What about
React.FC
/React.FunctionComponent
?
代码实现比较简单,就是调用了一下ReactDOM.createPortal
,没有考虑到使用者未传入node
的情况:需要内部创建,组件销毁时销毁该node
。
1 | import * as React from "react"; |
同时为了让非 ts 用户能够享受到良好的运行时错误提示,需要安装prop-types
。
1 | yarn add prop-types |
src/portal.tsx
1 | // ... |
这样就完成了 Portal
组件的编写,在入口文件进行导出。
src/index.ts
1 | export { default as Portal } from './portal'; |
example/index.ts
中引入Portal
,进行测试。
example/index.tsx
1 | import "react-app-polyfill/ie11"; |
在网页中看到预期的DOM
结构。
老规矩,先规划 API,写好类型定义,再动手写代码。
我写这个组件的时候参考了Popup-cube-ui。
最终确定 API 如下:
属性说明类型默认值
visible可选,控制 popup 显隐booleanfalse
position可选,内容定位‘center’ / ‘top’ / ‘bottom’ / ‘left’ / ‘right’‘center’
mask可选,控制蒙层显隐booleantrue
maskClosable可选,点击蒙层是否可以关闭booleanfalse
onClose可选,关闭函数,若 maskClosable 为 true,点击蒙层调用该函数function()=>{}
node可选,元素挂载节点HTMLElement-
destroyOnClose可选,关闭是否卸载内部元素booleanfalse
wrapClassName可选,自定义 Popup 外层容器类名string‘’
src/type.ts
1 | export type Position = 'top' | 'right' | 'bottom' | 'left' | 'center'; |
编写 Popup
的基本结构。
src/popup.tsx
1 | import * as React from 'react'; |
在入口文件进行导出。
src/index.ts
1 | + export { default as Popup } from './popup'; |
在正式开发逻辑之前,先明确一点:
蒙层 Mask 以及内容 Content 入场以及出场均有动画效果。具体表现为:蒙层为 Fade 动画,内容则取决于当前 position,比如内容在中间(position === ‘center’),则其动画效果为 Fade,如果在左边(position === ‘left’),则其动画效果为 SlideRight,其他 position 以此类推。
再回顾张鑫旭大大的一篇文章:小 tip: transition 与 visibility
划重点:
opacity
的值在 0
与 1
之间相互过渡(transition
)可以实现 Fade 动画。然而元素即使透明度变成 0,肉眼看不见,在页面上却依旧点击,还是可以覆盖其他元素的,我们希望元素淡出动画结束后,元素可以自动隐藏;display:none
。而display:none
无法应用 transition
效果,甚至是破坏作用;visibility:hidden
可以看成 visibility:0
;visibility:visible
可以看成 visibility:1
。实际上,只要 visibility
的值大于 0
就是显示的。总结一下:我们想用opacity
实现淡入淡出的 Fade 动画,但是希望元素淡出后,能够隐藏,而不仅仅是透明度为 0
,覆盖在其他元素上。所以需要配置 visibility
属性,淡出动画结束时,visibility
值也由visible
变为了hidden
,元素成功隐藏。
如果蒙层淡出动画结束后仅仅是透明度变为 0,却未隐藏,那么蒙层在视觉上虽然消失了,实际还是覆盖在页面上,就无法触发页面上的事件。
借助react-transition-group完成动画效果,需要内置一些动画样式。
新建animation.less
,写入以下动画样式。
展开查看代码
安装相关依赖。
1 | yarn add react-transition-group classnames |
Portal
即可;CSSTransition
组件的in
属性,控制蒙层以及内容的过渡显隐;CSSTransition
组件的unmountOnExit
属性,决定隐藏时是否卸载内容节点;className
;className
,从而控制蒙层有无;用过 antd
的同学都知道,antd
的modal
在首次visible === true
之前,内容节点是不会被挂载的,只有首次 visible === true
,内容节点才挂载,而后都是样式上隐藏,而不会去卸载内容节点,除非手动设置 destroyOnClose
属性,我们也顺带实现这个特点。
代码逻辑比较简单,在拼接类名时注意配合样式文件一起阅读,重要的点都有注释标出。
展开查看逻辑代码
展开查看样式代码
组件编写完毕,接下来在example/index.ts
中编写相关示例测试功能即可。
相信大多数人使用一个 npm 包会先看示例再看文档。
接下来将 example
中的示例项目打包,并部署到 github pages 上。
安装gh-pages
。
1 | yarn add gh-pages --dev |
package.json 新增脚本。
package.json
1 | { |
由于 gh-pages 默认部署在https://username.github.io/repo
下,而非根路径。为了能够正确引用到静态资源,还需要修改打包的 public-url
。
修改 example 的 package.json 中的打包命令:
1 | { |
https://username.github.io/repo
记得换成你自己的哦。
在根目录下执行 yarn deploy
,等脚本执行完再去看看吧。
一份规范的 README 会显得作者很专业,此处使用readme-md-generator
生成基本框架,向里面填充内容即可。
readme-md-generator:📄 CLI that generates beautiful README.md files
1 | npx readme-md-generator -y |
在上一篇文章中,专门编写了一个脚本来处理以下六点内容:
这次就不生成 CHANGELOG 文件了,其他五点配合np
,操作十分简单。
np:A better npm publish
1 | yarn add np --dev |
package.json
1 | { |
1 | npm login |
--no-yarn
: 不使用 yarn
。发包时出现 npm 与 yarn 之间的一些问题;--no-tests
:测试用例暂时还未编写,先跳过;--no-cleanup
:发包时不要重新安装 node_modules;更多配置请查看官方文档。
]]>本文转载自
https://github.com/worldzhao/blog/issues/2
要对页面的内容进行反转,但是某些内容又不需要反转,是不是很头疼?
其实,这些头疼的问题是有成熟的解决方案的。
HTML dir 属性
为body元素加上dir=”rtl”,浏览器就跨一自动反转了。
rtlcss
使用rtlcss,他的实现思路就是配置rtl属性使用,将页面上的left相关属性都转为right属性,核心思想就是某些属性的全局转换。
文本方向为从右向左的段落:
文本方向从右到左!
所有主流浏览器都支持 dir 属性
dir 属性规定元素内容的文本方向。
在 HTML5 中, dir 属性可用于任何的 HTML 元素 (它会验证任何HTML元素。但不一定是有用)。
在 HTML 4.01 中, dir 元素不能用于: <base>, <br>, <frame>, <frameset>, <hr>, <iframe>, <param>, 和 <script>
。
1 | <element dir="ltr|rtl|auto"> |
值 | 描述 |
---|---|
ltr | 默认。从左向右的文本方向。 |
tl | 从右向左的文本方向。 |
auto | 让浏览器根据内容来判断文本方向。仅在文本方向未知时推荐使用。 |
html dir 属性
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Global_attributes/dir
]]>rtlcss官网
https://rtlcss.com/
本文已发布在公众号“入门前端”
据不完全统计,99%看到的人都关注了这个公众号,点击下面关注并设置星标,不迷路!
dev-sidecar
开发者边车,命名取自service-mesh的service-sidecar,意为为开发者打辅助的边车工具
通过本地代理的方式将https请求代理到一些国内的加速通道上
一、 特性
1、 dns优选(解决***污染问题)
根据网络状况智能解析最佳域名ip地址,获取最佳网络速度
解决一些网站和库无法访问或访问速度慢的问题
建议遇到打开比较慢的国外网站,可以优先尝试将该域名添加到dns设置中(注意:被***封杀的无效)
2、 请求拦截
拦截打不开的网站,代理到加速镜像站点上去。
可配置多个镜像站作为备份
具备测速机制,当访问失败或超时之后,自动切换到备用站点,使得目标服务高可用
3、 github加速
github 直连加速 (通过修改sni实现,感谢 fastGithub 提供的思路)
release、source、zip下载加速
clone 加速
头像加速
解决readme中图片引用无法加载的问题
gist.github.com 加速
解决git push 偶尔失败需要输入账号密码的问题(fatal: TaskCanceledException encountered / fatal: HttpRequestException encountered)
raw/blame加速
以上部分功能通过X.I.U的油猴脚本实现, 以下是仓库和脚本下载链接,大家可以去支持一下。
https://github.com/XIU2/UserScript
https://greasyfork.org/scripts/412245
由于此脚本在ds中是打包在本地的,更新会不及时,你可以直接通过浏览器安装油猴插件使用此脚本,从而获得最新更新(ds本地的可以通过加速服务->基本设置->启用脚本进行关闭)。
4、 Stack Overflow 加速
将ajax.google.com代理到加速CDN上
recaptcha 图片验证码加速
5、 npm加速
支持开启npm代理
官方与淘宝npm registry一键切换,
某些npm install的时候,并且使用cnpm也无法安装时,可以尝试开启npm代理再试
安全警告:
请勿使用来源不明的服务地址,有隐私和账号泄露风险
本应用及服务端承诺不收集任何信息。介意者请使用安全模式。
]]>注意:由于electron无法监听windows的关机事件,开着ds情况下直接重启电脑,会导致无法上网,你可以手动启动ds即可恢复网络,你也可以将ds设置为开机自启。
关于此问题的更多讨论请前往:https://gitee.com/docmirror/dev-sidecar/issues/I49OUL https://github.com/docmirror/dev-sidecar/issues/109
对于一个提案,从提出到最后被纳入ES新特性,TC39的规范中分为五步:
目前,一些提案还处于第三阶段,一些提案已经进入第四阶段。提案的功能将在达到第 4 阶段后被添加到新的ECMAScript标准中,这意味着它们已获得 TC-39 的批准,通过了测试,并且至少有两个实现。下面就来看看 ECMAScript 2022 预计会推出的新功能吧~
在ES2017中引入了 async
函数和 await
关键字,以简化 Promise
的使用,但是 await
关键字只能在 async
函数内部使用。尝试在异步函数之外使用 await
就会报错:SyntaxError - SyntaxError: await is only valid in async function
。
顶层 await
允许我们在 async
函数外面使用 await
关键字,目前提案正处于第 4 阶段,并且已经在三个主要的浏览器的 JavaScript 引擎中实现,模块系统会协调所有的异步 promise
。
顶层 await
允许模块充当大型异步函数,通过顶层 await
,这些ECMAScript模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。下面来看一个简单的例子。
由于 await
仅在 async
函数中可用,因此模块可以通过将代码包装在 async
函数中来在代码中包含 await
:
1 | // a.js |
我们还可以立即调用顶层async
函数(IIAFE):
1 | import fetch from "node-fetch"; |
这样会有一个缺点,直接导入的 users 是 undefined,需要在异步执行完成之后才能访问它:
1 | // usingAwait.js |
当然,这种方法并不安全,因为如果异步函数执行花费的时间超过100毫秒, 它就不会起作用了,users 仍然是 undefined。
另一个方法是导出一个 promise,让导入模块知道数据已经准备好了:
1 | //a.js |
虽然这种方法似乎是给出了预期的结果,但是有一定的局限性:导入模块必须了解这种模式才能正确使用它。
而顶层await
就可以消除这些缺点:
1 | // a.js |
顶级 await 在以下场景中将非常有用:
(1)动态加载模块
1 | const strings = await import(`/i18n/${navigator.language}`); |
(2)资源初始化
1 | const connection = await dbConnector(); |
(3)依赖回退
1 | let translations; |
目前,在这些地方已经支持 Top-level await:
公共类字段允许我们使用赋值运算符 (=) 将实例属性添加到类定义中。下面来一个计数器的例子:
1 | import React, { Component } from "react"; |
在这个例子中,在构造函数中定义了实例字段和绑定方法,通过新的类语法,我们可以使代码更加直观。新的公共类字段语法允许我们直接将实例属性作为属性添加到类上,而无需使用构造函数方法。这样就简化了类的定义,使代码更加简洁、可读:
1 | import React from "react"; |
有些小伙伴可能就疑问了,这个功能很早就可以使用了呀。但是它现在还不是标准的 ECMAScript,默认是不开启的,如果使用 create-react-app 创建 React 项目,那么它默认是启用的,否则我们必须使用正确的babel插件才能正常使用(@babel/preset-env)。
下面来看看关于公共实例字段的注意事项:
(1)公共实例字段存在于每个创建的类实例上。它们要么是在Object.defineProperty()
中添加,要么是在基类中的构造时添加(构造函数主体执行之前执行),要么在子类的super()
返回之后添加:
1 | class Incrementor { |
(2)未初始化的字段会自动设置为 undefined:
1 | class Incrementor { |
(3)可以进行字段的计算:
1 | const PREFIX = 'main'; |
默认情况下,ES6 中所有属性都是公共的,可以在类外检查或修改。下面来看一个例子:
1 | class TimeTracker { |
可以看到,在类中没有任何措施可以防止在不调用 setter 的情况下更改属性。
而私有类字段将使用哈希#前缀定义,从上面的示例中,我们可以修改它以包含私有类字段,以防止在类方法之外更改属性:
1 | class TimeTracker { |
当我们尝试在 setter 方法之外修改私有类字段时,就会报错:
1 | person.hours = 4 // Error Private field '#hours' must be declared in an enclosing class |
我们还可以将方法或 getter/setter
设为私有,只需要给这些方法名称前面加#
即可:
1 | class TimeTracker { |
由于尝试访问对象上不存在的私有字段会发生异常,因此需要能够检查对象是否具有给定的私有字段。可以使用 in
运算符来检查对象上是否有私有字段:
1 | class Example { |
查看更多公有和私有字段提案信息:点击这里
在ES6中,不能在类的每个实例中访问静态字段或方法,只能在原型中访问。ES 2022 将提供一种在 JavaScript 中使用 static
关键字声明静态类字段的方法。下面来看一个例子:
1 | class Shape { |
我们可以从类本身访问静态字段和方法:
1 | console.log(Shape.color); // blue |
实例不能访问静态字段和方法:
1 | const shapeInstance = new Shape(); |
静态字段只能通过静态方法访问:
1 | console.log(Shape.getColor()); // blue |
这里的 Shape.getMessage() 就报错了,这是因为 getMessage 不是一个静态函数,所以它不能通过类名 Shape 访问。可以通过以下方式来解决这个问题:
1 | getMessage() { |
静态字段和方法是从父类继承的:
1 | class Rectangle extends Shape { } |
与私有实例字段和方法一样,静态私有字段和方法也使用哈希 (#
) 前缀来定义:
1 | class Shape { |
私有静态字段有一个限制:只有定义私有静态字段的类才能访问该字段。这可能在我们使用 this 时导致出乎意料的情况:
1 | class Shape { |
在这个例子中,this 指向的是 Rectangle 类,它无权访问私有字段 #color。当我们尝试调用 Rectangle.getMessage() 时,它无法读取 #color 并抛出了 TypeError。可以这样来进行修改:
1 | class Shape { |
静态字段目前是比较稳定的,并且提供了各种实现:
静态私有和公共字段只能让我们在类定义期间执行静态成员的每个字段初始化。如果我们需要在初始化期间像 try … catch 一样进行异常处理,就不得不在类之外编写此逻辑。该提案就提供了一种在类声明/定义期间评估静态初始化代码块的优雅方法,可以访问类的私有字段。
先来看一个例子:
1 | class Person { |
上面的代码就会引发错误,可以使用类静态块来重构它,只需将try…catch包裹在 static 中即可:
1 | class Person { |
此外,类静态块提供对词法范围的私有字段和方法的特权访问。这里需要在具有实例私有字段的类和同一范围内的函数之间共享信息的情况下很有用。
1 | let getData; |
这里,Person 类与 readPrivateData 函数共享了私有实例属性。
JavaScript 中的日期处理 [Date()](https://www.mybj123.com/tag/date/)
对象一直是饱受诟病,该对象是1995 年受到 Java 的启发而实现的,自此就一直没有改变过。虽然Java已经放弃了这个对象,但是 Date()
仍保留在 JavaScript 中来实现浏览器的兼容。
Date() API 存在的问题:
1 | const today = new Date(); |
此时,两个时间输出是一样的,不符合我们的预期。正因为 Date() 对象存在的种种问题。平时我们经常需要借助moment.js
、Day.js
等日期库,但是它们的体积较大,有时一个简单的日期处理就需要引入一个库,得不偿失。
目前,由于Date API 在很多库和浏览器引擎中的广泛使用,没有办法修复API的不好的部分。而改变Date API 的工作方式也很可能会破坏许多网站和库。
正因如此,TC39提出了一个全新的用于处理日期和时间的标准对象和函数——Temporal。新的Temporal API 提案旨在解决Date API的问题。它为 JavaScript 日期/时间操作带来了以下修复:
Temporal 将取代 Moment.js 之类的库,这些库很好地填补了 JavaScript 中的空白,这种空白非常普遍,因此将功能作为语言的一部分更有意义。
由于该提案还未正式发布,所以,可以借助官方提供的prlyfill来测试。首选进行安装:
1 | npm install @js-temporal/polyfill |
导入并使用:
1 | import { Temporal } from '@js-temporal/polyfill'; |
Temporal 对象如下:
下面就来看看 Temporal 对象有哪些实用的功能。
Temporal.Now
会返回一个表示当前日期和时间的对象:
1 | // 自1970年1月1日以来的时间(秒和毫秒) |
Temporal.Instant
根据 ISO 8601 格式的字符串返回一个表示日期和时间的对象,结果会精确到纳秒:
1 | Temporal.Instant.from('2022-02-01T05:56:78.999999999+02:00[Europe/Berlin]'); |
除此之外,我们还可以获取纪元时间的对应的日期(UTC 1970年1月1日0点是纪元时间):
1 | Temporal.Instant.fromEpochSeconds(1.0e8); |
Temporal.ZonedDateTime
返回一个对象,该对象表示在特定时区的日期/时间:
1 | new Temporal.ZonedDateTime( |
我们并不会总是需要使用精确的时间,因此 Temporal API 提供了独立于时区的对象。这些可以用于更简单的活动。
它们都有类似的构造函数,以下有两种形式来创建简单的时间和日期:
1 | new Temporal.PlainDateTime(2021, 5, 4, 13, 14, 15); |
所有 Temporal 对象都可以返回特定的日期/时间值。例如,使用ZonedDateTime
:
1 | const t1 = Temporal.ZonedDateTime.from('2025-12-07T03:24:30+02:00[Africa/Cairo]'); |
其他有用的属性包括:
dayOfWeek
(周一为 1 至周日为 7)dayOfYear
(1 至 365 或 366)weekOfYear
(1 到 52,有时是 53)daysInMonth
(28、29、30、31)daysInYear
(365 或 366)inLeapYear
(true或false)所有 Temporal 对象都可以使用 compare()
返回整数的函数进行比较。例如,比较两个ZonedDateTime
对象:
1 | Temporal.ZonedDateTime.compare(t1, t2); |
这个比较结果会有三种情况:
1 | const date1 = Temporal.Now, |
compare()
的结果可以用于数组的 sort()
方法来对时间按照升序进行排列(从早到晚):
1 | const t = [ |
提案还提供了几种方法来对任何 Temporal 对象执行日期计算。当传递一个Temporal.Duration对象时,它们都会返回一个相同类型的新的 Temporal,该对象使用years
, months
, weeks
, days
, hours
, minutes
, seconds
, milliseconds
, microseconds
和 nanoseconds
字段来设置时间。
1 | const t1 = Temporal.ZonedDateTime.from('2022-01-01T00:00:00+00:00[Europe/London]'); |
until()
和 since()
方法会返回一个对象,该 Temporal.Duration
对象描述基于当前日期/时间的特定日期和时间之前或之后的时间,例如:
1 | t1.until().months; // 到t1还有几个月 |
[equals()](https://www.mybj123.com/tag/equals/)
方法用来确定两个日期/时间值是否相同:
1 | const d1 = Temporal.PlainDate.from('2022-01-31'); |
虽然这不是 Temporal API 的一部分,但 JavaScript Intl(国际化)API提供了一个 [DateTimeFormat()](https://www.mybj123.com/tag/datetimeformat/)
构造函数,可以用于格式化 Temporal 或 Date 对象:
1 | const d = new Temporal.PlainDate(2022, 3, 14); |
附:
在ES2022之前,可以使用 Object.prototype.hasOwnProperty()
来检查一个属性是否属于对象。
提案中的 Object.hasOwn
特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法。
1 | const example = { |
at()
是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索:
1 | const array = [0,1,2,3,4,5]; |
除了数组,字符串也可以使用at()
方法进行索引:
1 | const str = "hello world"; |
在 ECMAScript 2022 提案中,new Error() 中可以指定导致它的原因:
1 | function readFiles(filePaths) { |
这个新提案已经进入第 4 阶段,它将允许我们利用 d 字符来表示我们想要匹配字符串的开始和结束索引。以前,我们只能在字符串匹配操作期间获得一个包含提取的字符串和索引信息的数组。在某些情况下,这是不够的。因此,在这个新提案中,如果设置标志 /d
,将额外获得一个带有开始和结束索引的数组。
1 | const matchObj = /(a+)(b+)/d.exec('aaaabb'); |
由于 /d
标识的存在,matchObj还有一个属性.indices
,它用来记录捕获的每个编号组:
1 | console.log(matchObj.indices[1]) // [0, 4] |
我们还可以使用命名组:
1 | const matchObj = /(?<as>a+)(?<bs>b+)/d.exec('aaaabb'); |
这里给两个字符匹配分别命名为as
和bs
,然后就可以通过groups来获取到这两个命名分别匹配到的字符串。
它们的索引存储在 matchObj.indices.groups
中:
1 | console.log(matchObj.indices.groups.as); // [0, 4] |
匹配索引的一个重要用途就是指向语法错误所在位置的解析器。下面的代码解决了一个相关问题:它指向引用内容的开始和结束位置。
1 | const reQuoted = /“([^”]+)”/dgu; |
首次使用只需要点击右下角的 扩展 按钮,
即可弹出 扩展商店 选择弹框,可选择 chrome扩展商店 和 edge扩展商店
进入选择的扩展商店,搜索自己需要的扩展,然后点击“下载”,下载完成后会自己提示是否安装扩展。点击”确定”
安装成功后,点击浏览器下方的“扩展”按钮,会显示已安装的扩展列表,点击扩展,进入扩展详情页
详情页对应PC上点击扩展显示的页面,手机上使用一个底部弹窗的方式实现的,显示的内容与PC一致,扩展的设置页面和PC是也是相同的。
这款浏览器同样采用的是谷歌的Chromium内核,其他使用方面和chrome、edge 都大同小异。
官方的介绍是:
全新Chromium高速内核引擎,精心为您适配更好用的网络帮手,体验飞一般的感觉
Infinity New Tab、Tampermonkey、Adblock Plus等成千上万个扩展在狐猴浏览器上也同样可以使用
多层次保护,利用沙箱技术阻挡恶意软件。使您免遭网上诱骗网站和危险网站的侵害,保障您的隐私安全
下载地址
关注公众号“入门前端”回复“20220211”获取
]]>