1.axios,码实如何中断请求?
2.vue为何弃用经典的jquery.ajax,选择新技术axios?
3.分析axios源码来找出无法使用all和spread等方法的原因
4.pnpm+workspace+changesets构建你的monorepo工程
5.Axios源码深度剖析 - AJAX新王者
6.TypeScript封装axios——Vue3+Ts实践
axios,如何中断请求?
深入解析 Axios 源码,探讨如何中断请求。码实在分析过程中,码实将重点放在了 default.js 文件以及与主动取消 Axios 请求相关的码实 /cancel 目录,揭示了核心工具方法的码实运作机制。
在 default.js 中,码实桑拿足浴源码导出了十个变量和方法,码实皆服务于默认功能,码实为理解和使用 Axios 奠定基础。码实
进一步探索 /cancel 目录,码实了解与取消请求相关的码实机制,涉及 cancel token API,码实需在配置中添加 cancelToken 属性,码实通过回调函数实现取消功能。码实
深入分析取消逻辑,码实从简单的 Cancel 类到 isCancel 方法,再到复杂的 CancelToken 类,逐步揭秘取消请求的内部运作。
关键点在于 CancelToken 类的实现,通过两层闭包巧妙地将“resolve”暴露给外部,同时提供了用于抛出错误和获取工厂方法的原型方法。
总结,本篇解析了默认功能的实现和取消逻辑的核心,为理解 Axios 源码提供了重要视角,下篇将继续深入探讨耦合度较高的工具方法,为开发和优化 Axios 提供更多洞察。
vue为何弃用经典的jquery.ajax,选择新技术axios?
深入探究 Vue 与 axios、ajax 的关系,首先需要理解,Vue 选择使用 axios 而非 jQuery.ajax,这一决策并非基于放弃 ajax 技术,而是基于性能优化和易用性考量。
从源码层面来看,axios 确实基于 XMLHttpRequest,即 ajax 技术实现,其封装与优化了 ajax 的使用体验。然而,Vue 选择 axios,并非因为放弃了 ajax,而在于寻求与现代前端开发更好的集成与协同。
Vue 的目标是提供简洁、高效、可维护的前端框架。在面对复杂的数据交互需求时,axios 以其简洁的 API、Promise 风格的回调机制、以及对异步操作的友好支持,成为 Vue 推荐的 HTTP 客户端首选。
对比 jQuery.ajax,axios 更注重与 Promise 的集成,这使得 Vue 在处理异步请求时,能够更加流畅、灵活。此外,axios 提供了更丰富的配置选项,如请求超时、请求头自定义等,这些特性对于 Vue 应用的开发具有显著优势。
在 Vue 生态系统中,定时播音 源码axios 作为请求库,与 Vue Router、Vuex 等核心组件相互协作,为开发者提供了一站式解决方案,简化了应用中复杂的网络请求管理。这种协同不仅提升了开发效率,还增强了代码的可读性和可维护性。
综上所述,Vue 选择 axios 并非放弃 ajax,而是在现代前端开发环境下,对工具的精心挑选与优化。通过与 axios 的集成,Vue 实现了性能与易用性的双重提升,为开发者提供了一套高效、灵活的解决方案。因此,对于这类问题,正确的答案不应是质疑,而是对技术选型背后逻辑的深入理解与尊重。
分析axios源码来找出无法使用all和spread等方法的原因
在使用axios进行创建时,若采用axios.create({ })方法,将无法使用all、spread、Cancel、CancelToken、isCancel等方法。
网上关于此问题的解答,通常是axios维护者建议重新引入axios package以解决问题。然而,这种方法并不理想,因为重新引入会导致axios配置丢失,需要重新配置,相当繁琐。
在我们的项目中,经常需要使用自定义设置的axios实例,例如设置基础URL和超时时间。设置完成后,我们可以使用newAxios.post来完成需求。但若尝试使用all、spread、Cancel、CancelToken、isCancel等方法,系统会提示方法不存在。
接下来,我们将分析axios源码,探究为何使用axios.create方法后无法使用all、spread等方法。
首先,打开axios源码目录下的lib/axios.js文件,这是Axios的入口处,也是create函数所在的地方。让我们看一下create的源代码:
接下来,我们将逐步解读代码。mergeConfig方法从字面上可以理解为一个合并配置的方法,即合并我们的配置与默认配置,覆盖默认配置。关于合并配置的email源码jsp代码,这里就不详细介绍了。有兴趣的可以查看mergeConfig。因此,现在的代码如下:
现在,我们来看一下剩下的createInstance函数:
context变量包含axios实例代码。我们只需知道,实例Axios后,context变量原型链上有request、delete、get、head、options、post、put、patch方法,自身有interceptors对象。
现在,让我们看看下面的bind和extend方法:
第一个bind函数是让Axios.prototype.request函数中的this指向context变量。
后面两个extend方法,是把第二个参数的可枚举对象复制到第一个参数中,即instance变量中。
从第一个bind方法开始,现在instance变量中有一个request方法。
然后第二个extend方法,把Axios.prototype里的方法复制到instance变量中。现在instance变量中有request、delete、get、head、options、post、put、patch方法。
最后第三个extend方法,把context里的方法复制到instance变量中。现在变量中有request、delete、get、head、options、post、put、patch、interceptors、defaults。
这样就结束了,create方法直接返回instance变量。我们没有在create方法中看到all、spread等方法。这也是为什么使用create方法后无法使用这些方法。那么这些方法在哪呢?还是在lib/axios.js文件中:
可以看到,这里是把这些方法直接赋值在axios方法上,然后直接暴露出去。所以当我们使用axios时,可以使用all、spread等方法。但使用axios.create就无法使用all、spread、2017企业源码Cancel、CancelToken、isCancel方法。
如果能改axios源码,可以将lib/axios.js修改如下:
但是,这当然不可能。所以,我们需要在不改源代码的情况下实现。
有一个暴力的解决方案,不过我个人比较喜欢:
很简单,一行代码解决问题。这里之所以要加上注释,是因为在eslint中不允许对__proto__进行重新赋值。
pnpm+workspace+changesets构建你的monorepo工程
pnpm+workspace+changesets构建你的monorepo工程什么是monorepo?
什么是monorepo?以及和multirepo的区别是什么?
关于这些问题,在之前的一篇介绍lerna的文章中已经详细介绍过,感兴趣的同学可以再回顾下。
简而言之,monorepo就是把多个工程放到一个git仓库中进行管理,因此他们可以共享同一套构建流程、代码规范也可以做到统一,特别是如果存在模块间的相互引用的情况,查看代码、修改bug、调试等会更加方便。
什么是pnpm?pnpm是新一代的包管理工具,号称是最先进的包管理器。按照官网说法,可以实现节约磁盘空间并提升安装速度和创建非扁平化的node_modules文件夹两大目标,具体原理可以参考pnpm官网。
pnpm提出了workspace的概念,内置了对monorepo的支持,那么为什么要用pnpm取代之前的lerna呢?
这里我总结了以下几点原因:
lerna已经不再维护,后续有任何问题社区无法及时响应
pnpm装包效率更高,并且可以节约更多磁盘空间
pnpm本身就预置了对monorepo的支持,不需要再额外第三方包的支持
onemorething,就是好奇心了?
如何使用pnpm来搭建menorepo工程安装pnpm$?npm?install?-g?pnpm v7版本的pnpm安装使用需要node版本至少大于v..0,所以在安装之前首先需要检查下node版本。工程初始化为了便于后续的演示,先在工程根目录下新建packages目录,并且在packages目录下创建pkg1和pkg2两个工程,分别进到pkg1和pkg2两个目录下,执行npminit命令,初始化两个工程,package.json中的name字段分别叫做@qftjs/menorepo1和@qftjs/monorepo2(PS:@qftjs是提前在npm上创建好的组织,没有的话需要提前创建)。
为了防止根目录被发布出去,需要设置工程工程个目录下package.json配置文件的private字段为true。
为了实现一个完整的例子,这里我使用了father-build对模块进行打包,father-build是基于rollup进行的一层封装,使用起来更加便捷。
在pkg1和pkg2的src目录下个创建一个index.ts文件:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;分别在pkg1和pkg2下新增.fatherrc.ts和tsconfig.ts配置文件。
//?.fatherrc.tsexport?default?{ ?target:?'node',?cjs:?{ ?type:?'babel',?lazy:?true?},?disableTypeCheck:?false,};//?tsconfig.ts{ ?"include":?["src",?"types",?"test"],?"compilerOptions":?{ "target":?"es5","module":?"esnext","lib":?["dom",?"esnext"],"importHelpers":?true,"declaration":?true,"sourceMap":?true,"rootDir":?"./","strict":?true,"noImplicitAny":?true,"strictNullChecks":?true,"strictFunctionTypes":?true,"strictPropertyInitialization":?true,"noImplicitThis":?true,"alwaysStrict":?true,"noUnusedLocals":?true,"noUnusedParameters":?true,"noImplicitReturns":?true,"noFallthroughCasesInSwitch":?true,"moduleResolution":?"node","baseUrl":?"./","paths":?{ ?"*":?["src/*",?"node_modules/*"]},"jsx":?"react","esModuleInterop":?true?}}全局安装father-build:
$?pnpm?i?-Dw?father-build最后在pkg1和pkg2下的package.json文件中增加一条script:
{ ?"scripts":?{ "build":?"father-build"?}}这样在pkg1或者pkg2下执行build命令就会将各子包的ts代码打包成js代码输出至lib目录下。
要想启动pnpm的workspace功能,需要工程根目录下存在pnpm-workspace.yaml配置文件,并且在pnpm-workspace.yaml中指定工作空间的目录。比如这里我们所有的子包都是放在packages目录下,因此修改pnpm-workspace.yaml内容如下:
packages:?thinkphp简单源码-?'packages/*'初始化完毕后的工程目录结构如下:
.├──?README.md├──?package.json├──?packages│├──?pkg1││├──?package.json││├──?src│││└──?index.ts││└──?tsconfig.json│└──?pkg2│├──?package.json│├──?src││└──?index.ts│└──?tsconfig.json├──?pnpm-workspace.yaml└──?tsconfig.root.json安装依赖包使用pnpm安装依赖包一般分以下几种情况:
全局的公共依赖包,比如打包涉及到的rollup、typescript等
pnpm提供了-w,--workspace-root参数,可以将依赖包安装到工程的根目录下,作为所有?package的公共依赖。
比如:
$?pnpm?install?react?-w如果是一个开发依赖的话,可以加上-D参数,表示这是一个开发依赖,会装到pacakage.json中的devDependencies中,比如:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;0给某个package单独安装指定依赖
pnpm提供了--filter参数,可以用来对特定的package进行某些操作。
因此,如果想给pkg1安装一个依赖包,比如axios,可以进行如下操作:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;1需要注意的是,--filter参数跟着的是package下的package.json的name字段,并不是目录名。
关于--filter操作其实还是很丰富的,比如执行pkg1下的scripts脚本:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;2filter后面除了可以指定具体的包名,还可以跟着匹配规则来指定对匹配上规则的包进行操作,比如:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;3此命令会执行所有package下的build命令。具体的用法可以参考filter文档。
模块之间的相互依赖
最后一种就是我们在开发时经常遇到的场景,比如pkg1中将pkg2作为依赖进行安装。
基于pnpm提供的workspace:协议,可以方便的在packages内部进行互相引用。比如在pkg1中引用pkg2:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;4此时我们查看pkg1的package.json,可以看到dependencies字段中多了对@qftjs/monorepo2的引用,以workspace:开头,后面跟着具体的版本号。
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;5在设置依赖版本的时候推荐用workspace:*,这样就可以保持依赖的版本是工作空间里最新版本,不需要每次手动更新依赖版本。
当pnpmpublish的时候,会自动将package.json中的workspace修正为对应的版本号。
只允许pnpm当在项目中使用pnpm时,如果不希望用户使用yarn或者npm安装依赖,可以将下面的这个preinstall脚本添加到工程根目录下的package.json中:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;6preinstall脚本会在install之前执行,现在,只要有人运行npminstall或yarninstall,就会调用only-allow去限制只允许使用pnpm安装依赖。
Release工作流在workspace中对包版本管理是一个非常复杂的工作,遗憾的是pnpm没有提供内置的解决方案,一部分开源项目在自己的项目中自己实现了一套包版本的管理机制,比如Vue3、Vite等。
pnpm推荐了两个开源的版本控制工具:
changesets
rush
这里我采用了changesets来做依赖包的管理。选用changesets的主要原因还是文档更加清晰一些,个人感觉上手比较容易。
按照changesets文档介绍的,changesets主要是做了两件事:
Changesetsholdtwokeybitsofinformation:aversiontype(followingsemver),andchangeinformationtobeaddedtoachangelog.
简而言之就是管理包的version和生成changelog。
配置changesets安装
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;7初始化
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;8执行完初始化命令后,会在工程的根目录下生成.changeset目录,其中的config.json作为默认的changeset的配置文件。
修改配置文件如下:
//?pkg1/src/index.tsimport?pkg2?from?'@qftjs/monorepo2';function?fun2()?{ ?pkg2();?console.log('I?am?package?1');}export?default?fun2;9说明如下:
changelog:changelog生成方式
commit:不要让changeset在publish的时候帮我们做gitadd
linked:配置哪些包要共享版本
access:公私有安全设定,内网建议restricted,开源使用public
baseBranch:项目主分支
updateInternalDependencies:确保某包依赖的包发生upgrade,该包也要发生versionupgrade的衡量单位(量级)
ignore:不需要变动version的包
___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH:在每次version变动时一定无理由patch抬升依赖他的那些包的版本,防止陷入major优先的未更新问题
如何使用changesets一个包一般分如下几个步骤:
为了便于统一管理所有包的发布过程,在工程根目录下的pacakge.json的scripts中增加如下几条脚本:
编译阶段,生成构建产物
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;0清理构建产物和node_modules
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;1执行changeset,开始交互式填写变更集,这个命令会将你的包全部列出来,然后选择你要更改发布的包
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;2执行changesetversion,修改发布包的版本
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;3这里需要注意的是,版本的选择一共有三种类型,分别是patch、minor和major,严格遵循semver规范。
这里还有个细节,如果我不想直接发release版本,而是想先发一个带tag的prerelease版本呢(比如beta或者rc版本)?
这里提供了两种方式:
手工调整
这种方法最简单粗暴,但是比较容易犯错。
首先需要修改包的版本号:
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;4然后运行:
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;5注意发包的时候不要忘记加上--tag参数。
通过changeset提供的Prereleases模式
利用官方提供的Prereleases模式,通过preenter<tag>命令进入先进入pre模式。
常见的tag如下所示:
名称功能alpha是内部测试版,一般不向外部发布,会有很多Bug,一般只有测试人员使用beta也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出rcReleaseCandidate)系统平台上就是发行候选版本。RC版不会再加入新的功能了,主要着重于除错//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;6之后在此模式下的changesetpublish均将默认走beta环境,下面在此模式下任意的进行你的开发,举一个例子如下:
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;7完成版本发布之后,退出Prereleases模式:
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;8构建产物后发版本
//?pkg2/src/index.tsfunction?fun2()?{ ?console.log('I?am?package?2');}export?default?fun2;9规范代码提交代码提交规范对于团队或者公司来说是非常重要的,养成良好的代码提交规范可以方便回溯,有助于对本次提交进行review,如果单纯的只是要求团队成员遵循某些代码提交规范,是很难形成强制约束的,现在我们就尝试通过工具来约束代码提交规范。
使用commitizen规范commit提交格式commitizen的作用主要是为了生成标准化的commitmessage,符合Angular规范。
一个标准化的commitmessage应该包含三个部分:Header、Body和Footer,其中的Header是必须的,Body和Footer可以选填。
//?.fatherrc.tsexport?default?{ ?target:?'node',?cjs:?{ ?type:?'babel',?lazy:?true?},?disableTypeCheck:?false,};0Header部分由三个字段组成:type(必需)、scope(可选)、subject(必需)
Typetype必须是下面的其中之一:
feat:增加新功能
fix:修复bug
docs:只改动了文档相关的内容
style:不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
refactor:代码重构时使用,既不是新增功能也不是代码的bud修复
perf:提高性能的修改
test:添加或修改测试代码
build:构建工具或者外部依赖包的修改,比如更新依赖包的版本
ci:持续集成的配置文件或者脚本的修改
chore:杂项,其他不需要修改源代码或不需要修改测试代码的修改
revert:撤销某次提交
scope
用于说明本次提交的影响范围。scope依据项目而定,例如在业务项目中可以依据菜单或者功能模块划分,如果是组件库开发,则可以依据组件划分。
subject
主题包含对更改的简洁描述:
注意三点:
使用祈使语气,现在时,比如使用"change"而不是"changed"或者”changes“
第一个字母不要大写
末尾不要以.结尾
Body
主要包含对主题的进一步描述,同样的,应该使用祈使语气,包含本次修改的动机并将其与之前的行为进行对比。
Footer
包含此次提交有关重大更改的信息,引用此次提交关闭的issue地址,如果代码的提交是不兼容变更或关闭缺陷,则Footer必需,否则可以省略。
使用方法:
commitizen和cz-conventional-changelog如果需要在项目中使用commitizen生成符合AngularJS规范的提交说明,还需要安装cz-conventional-changelog适配器。
//?.fatherrc.tsexport?default?{ ?target:?'node',?cjs:?{ ?type:?'babel',?lazy:?true?},?disableTypeCheck:?false,};1工程根目录下的package.json中增加一条脚本:
//?.fatherrc.tsexport?default?{ ?target:?'node',?cjs:?{ ?type:?'babel',?lazy:?true?},?disableTypeCheck:?false,};2接下来就可以使用$pnpmcommit来代替$gitcommit进行代码提交了,看到下面的效果就表示已经安装成功了。
commitlint&&husky前面我们提到,通过commitizen&&c
Axios源码深度剖析 - AJAX新王者
Axios 是一个基于 Promise 的 HTTP 请求库,支持浏览器和 Node.js 环境。其源码在 GitHub 上开源,欢迎 fork 使用并提出指正。以下为 Axios 的核心目录结构说明,主要关注在 /lib/ 目录下的文件。
在使用 Axios 时,你可能会遇到多种调用方式,本文将带你深入了解这些方式及其原理。
首先,我们来了解一下 Axios 的基本用法。你可以使用以下几种方式发起请求:
1. `axios(option)`:提供一个配置对象进行调用。
2. `axios(url[, option])`:传入 URL 和配置对象。
3. 对于 GET、DELETE 等方法:`axios[method](url[, option])`。
4. 对于 POST、PUT 等方法:`axios[method](url[, data[, option]])`。
5. 使用默认实例:`axios.request(option)`。
通过以上方式,你可以轻松发起 HTTP 请求。
深入源码分析,你将发现 Axios 的强大之处。通过 `axios.js` 文件的入口,核心在于 `createInstance` 方法,该方法能生成一个指向 `Axios.prototype.request` 的 Function,从而实现多种调用方式。
在 Axios 的核心 `Axios` 类中,`request` 方法是所有功能的中枢,无论是 GET、POST 还是其他方法,最终都通过 `request` 方法实现。
配置项是 Axios 与用户交互的关键,它涵盖了几乎所有功能的配置。配置项从低到高优先级顺序为:默认配置对象、`defaults` 属性、`request` 方法参数。
在使用 Axios 时,配置项是如何生效的?答案在于合并多个配置源,最终得到一个综合配置对象。
此外,Axios 提供了拦截器系统,让你可以控制请求前后的数据处理。每个 Axios 实例都有 `interceptors` 属性,用于管理拦截器,让你实现精细的控制。
核心的 `dispatchRequest` 方法则负责处理请求流程,包括请求适配器、发送请求、数据转换等步骤。最后,通过 Promise,你可以优雅地处理异步请求。
数据转换器让你能轻松地在请求和响应数据之间进行转换,如将对象转换为 JSON 格式。默认情况下,Axios 自动处理 JSON 数据转换。
在使用 Axios 时,你还能灵活地控制超时、取消请求、设置 header、携带 cookie 等功能。通过源码分析,你可以深入理解 Axios 的内部机制。
总结,Axios 以其强大、灵活的功能和简洁的 API 设计,成为现代应用中不可或缺的 HTTP 请求工具。通过本文的深入探讨,你将对 Axios 的运作机制有更深刻的理解,从而更好地利用其功能。
TypeScript封装axios——Vue3+Ts实践
在Vue3 + TypeScript的项目中,封装axios库以提升代码质量、降低耦合度并提高维护性。选择封装的原因主要有两个:一是减少每个模块对axios的依赖性,避免因第三方库维护问题而引发的复杂修改;二是解决在发送网络请求时的重复代码问题,如添加token、显示加载等。
通过面向对象的思想封装axios,首先定义了axios实例类型以及响应和请求配置的类型,确保了代码的类型安全。为了解决axios源代码中缺少拦截器属性的问题,引入了接口扩展和类的方法来支持自定义拦截器。在封装的过程中,通过接口定义了拦截器的结构,并且利用类的方法实现了请求和响应的拦截器设置,以及控制加载状态的功能。这样,封装后的axios不仅支持了基本的请求和响应操作,还提供了高度定制化的功能,使得在不同模块中重用相同的网络请求逻辑变得可能。
封装后的axios实例可以作为全局资源在项目中使用,例如在创建request.ts文件中,通过定义HRequest类来管理请求配置和拦截器的设置。这样,每个请求都可以通过HRequest实例调用,同时可以统一管理加载状态,比如在每个请求时显示加载提示。通过将HRequest实例导出,其他部分可以轻松引用,实现代码的复用和维护。
封装后的axios不仅简化了代码结构,提高了代码的可读性和可维护性,还使得项目在扩展和维护过程中变得更加灵活。通过封装,可以方便地在不同项目中重用这一套网络请求处理逻辑,从而节省了开发时间并提高了代码质量。
React组件设计-仿网易有道翻译主页
前言
React组件化开发非常有利于搭建项目,也提高了组件的复用性。由于频繁使用网易有道翻译这个软件,让我萌生出想要征服ta的冲动。开发过程中遇到了些许问题,页面还有很多功能还未完善,现在只有一个首页,后续功能持续完善中。
前期准备在组件页面成型之初需要几个开源组件库:
axios:它是一个基于promise的网络请求库,用于获取后端数据(fastmock网站可以让你在没有后端程序的情况下能真实地在线模拟ajax请求),是前端常用的数据请求工具;
antd-mobile:由蚂蚁金融团队推出的一个开源的react组件库,这个组件库拥有很多使用的组件;
swiper:能实现触屏焦点图、触屏Tab切换、触屏轮播图切换等常用效果。
styled-compenonts:真正的cssinjs,增强CSS以对React组件系统进行样式设置的结果,具有简单的动态样式、轻松维护等优点。
正文组件展示?
组件设计思路顶部:用flex布局,方便快捷(一切皆可flex)
搜索栏:使用antd-mobile组件库的SearchBar,点击转跳到搜索页面
图标轮播和轮播图:主要使用swiper进行设计,实现自动轮播效果
底部栏:用fixed固定住
组件封装先对项目进行脚手架的建构(使用vite脚手架,使用起来快速方便)
npminit@viteja/appsrc下的目录内容
api:存放与数据相关的链接,组件所有的数据将会在这一个文件夹下的request.js中使用ajax进行数据请求
assets:存放静态资源,font、image等
components:放置重复使用的组件
config:存放页面标题配置
modules:配置页面自适应横竖屏
pages:各个页面
routes:页面的路由
搜索栏直接使用antd-mobile的SearchBar
importReactfrom'react'import{ SearchBar}from'antd-mobile'import{ Link}from'react-router-dom'import{ Wrapper}from'./style'exportdefaultfunctionSearch(){ return(
{ /*点击搜索框跳转搜索页面*/})}```
数据请求前端页面数据的展示不能写死在代码里面,需要数据请求,fastmock则走入了我的视野,在线接口Mock工具fastmock?在线模拟ajax请求(fastmock在没有后端程序的情况下可以实现ajax请求,有需要的小伙伴可以去尝试)
api文件夹下的request.js进行axios数据请求
importaxiosfrom'axios'exportconstgetBanners=()=>axios.get('/post/Ajax、Fetch、Axios 篇
Ajax是async javaScript and xml的缩写,是一种可以扩展的文本标记语言,常用于从服务端返回数据结构,现在基本都是使用 json 格式返回数据。在不刷新全局的条件下,局部刷新页面是Ajax的主要作用。
创建Ajax实例的方法为let xhr = new XMLHttpRequest(),注意:IE6不兼容这种写法。打开请求时,配置请求前的配置项共有5个参数,包括:xhr.open([/api/',这将设置一个Authorization头,覆写掉现有的任意使用headers设置的自定义Authorization头;auth表示HTTP基础验证应当用于连接代理,并提供凭据;这将会设置一个Proxy-Authorization头,覆写掉已有的通过使用header设置的自定义Proxy-Authorization头。
Axios可以说把请求这件事做到了极致,封装的很好用,浏览器支持的方法,除了IE低版本以外,最新版的浏览器都是支持的。
Fetch是http的数据请求方式,是XMLHttpRequest的一种代替方案,没有使用到XMLHttpRequest这个类。fetch()采用模块化设计,API分散在Response对象、Request对象、Headers对象上。fetch()通过数据流(Stream对象)处理数据,对于请求大文件或者网速慢的场景相当有用。XMLHttpRequest没有使用数据流,所有的请求都必须完成后才拿到。在默认情况下fetch不会接受或者发送cookies。
fetch(url,optionObj)基本使用中,fetch参数没有同步的设定,因为fetch是基于promise封装的本身就是异步的。fetch虽然使用的是promise封装,但是catch函数不能直接捕获到错误,需要在第一个then函数内做些操作。fetch发送post请求时,当发生的是跨域请求,fetch会先发送一个OPTIONS请求,来确认服务器是否允许接受请求,这个请求主要是用来询问服务器是否允许修改header等一些操作。服务器同意后返回,才会发送真正的请求。没有发生跨域的情况下不会产生两次请求。
fetch的三个模块包括:Response对象、Request对象、Headers对象。
fetch发送post两次请求的原因是在使用fetch发送post请求时如果是跨域,那么导致fetch第一次发送了一个Options请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求。
fetch的缺点包括:get/head请求不能设置body属性;fetch请求后,服务器返回的状态码无论是多少包括(4xx,5xx),fetch都认为是失败的,也就是使用catch也不能直接捕捉到错误,需要再第一个then中做一些处理。
Ajax、Fetch、Axios综合中,实现一个Ajax可以将原生的Ajax封装成promise。Ajax、Axios、Fetch的区别包括:传统的Ajax利用的是XMLHttpRequest这个对象,和后端进行交互。JQueryAjax是对原生XHR的封装,多请求间有嵌套的话就会出现回调地狱的问题。axios使用promise封装xhr,解决了回调地狱问题。fetch不是XMLHttpRequest,fetch是原生的js,使用的是promise。
fetch使用的是promise方便使用异步,没有回调地狱的问题。要实现一个Ajax请求,可以将原生的Ajax封装成promise。实现两个有顺序的Ajax请求可以使用promise.then()。Ajax解决浏览器缓存问题可以通过设置请求头,例如:anyAjaxObj.setRequestHeader("If-Modified-Since","0") 或 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。