全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-02-04_扩展你的前端知识库,毫无废话!

您的位置:首页 >> 新闻 >> 行业资讯

扩展你的前端知识库,毫无废话! 又是持续学习的一天!这篇文章给大家分享前端中一些原理性问题,以及开发中一些功能的实现,分别为: Form表单字段校验如何实现Vue 项目中 data-v-xxx 的生成Vue项目打包后产生的 JS 请求远程加载 .vue 组件并使用render函数实现一个菜单下拉框一、Form表单字段校验如何实现 每个前端开发者肯定都接触过 Form 表单组件,组件库都会有这个组件,平时用得也非常多,但是我们很容易忽略它的一些功能是怎么实现的,比如表单校验是如何实现的?为啥每次输入的时候,就能马上触发到表单的校验呢?这些都是值得我们去深究的问题,只有这样才能不断提升开发能力! image.png下面以一个例子来研究表单校验功能的实现: template div a-form:rules="rules":data="formData" a-form-itemfield="name" a-inputv-model="formData.name"/ /a-form-item /test-form /div /template scriptlang="ts"setup import{reactive}from'vue' importAFormfrom'./Form.vue'; importAFormItemfrom'./Form-Item.vue' importAInputfrom'./Input.vue'; //规则 construles={ name:{ required:true } } //数据 constformData=reactive({ name:'哈哈哈' }) /script 其实校验的核心功能就是三个东西 表单规则:在 a-form 中表单字段:在 a-form-item 中表单的值:在 a-input 中image.png我们可以通过表单字段去获取到实时的表单值,接着去表单规则中去匹配,就可以获取到校验结果了;只有将这三个东西结合起来,才能做到校验,他们的关系如下图: image.png大致分为几步: 1、Form 将 rules、validate 传给 From-Item2、Form-Item 将 field、onChange 函数传给 Input3、Input 的 value 改变时触发 validate、onChange 函数去执行校验,并且决定错误提示的展示为了传值方便,这里主要采用 provide、inject 的方式(Vue3 中组件之间传值的方式) 1. 具体实现——FormForm 组件的作用:执行校验函数的主要逻辑,根据传入的 field 判断该字段的校验是否通过,然后将校验结果存到 errorMap 中。 template form slot/slot /form /template scriptlang="ts"setup import{provide,reactive}from'vue' constprops=defineProps{rules:data:any} //字段有多个,所以需要维护一个错误表 consterrorMap=reactiveany({}) //校验函数 constvalidateFn=(field:string):Promisevoid={ returnnewPromise((resolve,reject)={ const{rules,data}=props; construleItem=rules[field] constdataItem=data[field] if(ruleItem.requireddataItem===''){ returnreject() } resolve() }; //执行校验 constvalidate=(field:string)={ validateFn(field).then(()={ errorMap[field]=false }).catch(()={ errorMap[field]=true }) } //注入 provide('a-form',{ validate, getErrorMap:()=errorMap }) /script 2. 具体实现——From-ItemFrom-Item 组件的作用:提供 onChange 方法以及需要校验的字段 field,当 validate 执行完后获取校验的结果,然后决定是否展示错误提示 template div slot/slot divstyle="color:red"v-if="data.showError"字段必填/div /div /template scriptlang="ts"setup import{provide,inject,reactive}from'vue'; constprops=defineProps{field:string} constAForm=inject{validate:(field:string)=PromiseanygetErrorMap:any}('a-form'); constdata=reactive({ showError:false, }); constonChange=()={ setTimeout(()={ if(AForm){ constshowError=AForm.getErrorMap()[props.field] //决定展示不展示错误提示 data.showError=showError } }) } //注入 provide('a-form-item',{ getField:()=props.field, onChange }); /script 3. 具体实现——InputInput 组件的作用:当输入框的值 value 发生改变时,依次触发 validate、onChange 函数去执行校验 template input@input="onChange":value="data.inputValue"/ /template scriptlang="ts"setup import{reactive,watch,inject}from'vue'; constprops=defineProps{modelValue:string} constemits=defineEmits(['update:modelValue']); //接收注入 constAForm=inject{validate:(field:string)=PromiseanygetErrorMap:any}( 'a-form', ); constAFormItem=inject{getField:()=string;onChange:()=void}('a-form-item') //内部维护value constdata=reactive({ inputValue:props.modelValue, }); watch( ()=props.modelValue, v={ data.inputValue= }, ); //valuechange时,执行validate、onChange constonChange=(e:Event)={ emits('update:modelValue',(e.targetasHTMLInputElement).value); if(AFormAFormItem){ AForm.validate(AFormItem.getField()) AFormItem.onChange() } }; /script 二、Vue 项目中 data-v-xxx 的生成 先来看一个问题: 大意是说 Vue scoped 的 data-v-xxx 是根据文件相对路径计算的,如果微前端的两个 Vue 子项目采用相同的路径结构,那么算出来的 data-v-xxx 是一样的,可能会导致样式冲突。 image.pngwebpack + vue-loader 对 vue2 的处理constshortFilePath=path .relative(rootContext||process.cwd(),filename) .replace(/^(..[/])+/,'').replace(//g,'/') constid=hash( isProduction ?shortFilePath+'\n'+source.replace(/\r\n/g,'\n') :shortFilePath ) vite + @vitejs/plugin-vue 对 vue3 的处理importpathfrom"node:path"; import{createHash}from"node:crypto"; importslashfrom"slash"; functiongetHash(text){ returncreateHash("sha256").update(text).digest("hex").substring(0,8); } //获取文件相对路径 constnormalizedPath=slash(path.normalize(path.relative(root,filename))); //计算ID descriptor.id=getHash(normalizedPath+(isProduction?source:"")); 可以发现,不管是 vue-loader 还是 @vitejs/plugin-vue,data 属性 ID 的生成机制都是一样的,即: 开发环境下会根据文件相对路径生成唯一 ID,比如 vite 中 src/App.vue 固定生成 7a7a37b1生产环境下会根据文件相对路径+文件内容共同生成唯一 ID为什么开发环境和生产环境的 ID 计算方式不一样? 首先,开发环境下最好不要加入文件内容进行 hash 计算,因为 hash 计算是耗时的,内容越多耗时越长,而且还会频繁变动节点样式,徒增成本。 那生产环境为什么还要加入文件内容计算 hash 呢?如果 ID 与文件内容无关,就可以实现稳定的 data 属性,对于 E2E 测试用例,就可以直接使用 data 属性进行元素寻址。 三、Vue项目打包后产生的 JS 请求 如果我们关注 Vue 项目的打包,会发现在打包后产生若干 js 请求文件,如 app.js 或 chunk-vendors.js ,在这里深入探讨 Vue 项目打包后产生的文件都是什么。 注:Vue 打包后和 Vue 项目直接本地运行后产生的 js 请求文件一致,为了方便展示,这里采用本地启动项目后产生的文件进行探讨。 1. 基础请求文件我们创建一个最基础的 Vue 项目,不引入路由、Vuex 及其他任何第三方 JS,运行项目,我们可以发现 Vue 产生了两个请求文件: namestatustypesizetimechunk-vendors.js200script392 kB67 msapp.js200script15.5 kB70 ms2. app.js 文件我们在 app.vue 中添加大量文字,重新运行项目,可以发现,app.js 的大小发生了改变,但 chunk-vendors.js 的大小并没有发生改变: 结论:打包后,app.js 就是我们的业务层代码,如 main.js 里面的内容、各个 Vue 组件里面的内容 3. chunk-vendors.js 文件我们在 app.vue 中安装并引入 moment.js: script importmomentfrom"moment" moment() exportdefault{ name:"App", }; /script 重新运行项目,我们可以发现 chunk-vendors.js 的尺寸几乎翻倍,但 app.js 的大小几乎没变: 结论:打包后,chunk-vendors.js 集成的是 node_modules 里面用到的 js 内容 4. 路由请求文件以一个企业级项目为例,我们在项目中定义一个路由页面。 constheadquarters=()=import("@/views/headquarters/index.vue"); exportdefault[ { path:"/", name:"headquarters", component:headquarters, meta:{ keepAlive:false, title:"总部展示大屏", name:"总部展示大屏", }, }, ]; 启动项目http://localhost:8080/demo/[1],我们可以看到页面请求了3个JS文件 注:链接中的 demo 是项目的 baseUrl 它们的作用如下: 请求文件作用资源尺寸请求时间0.jsheadquarters路由内容(prefetch cache)60 msapp.jsmain.js等非路由页面内容893 kB12 mschunk-vendors.jsnode_modules内容37.9 MB427ms我们再增加一个路由 { path:"/data-analysis", name:"dataAnalysis", component:()=import("@/views/data-analysis/index.vue"), meta:{ keepAlive:false, title:"数据分析", name:"数据分析", }, }, 重新输入链接 http://localhost:8080/demo ,你可能会疑惑,明明添加了路由,但是为什么请求文件还是 0.js? 实际上,因为我们没有访问对应路由链接,输入包含新路由的链接,就可以看到新路由文件的请求,这里的 1.js 就是 dataAnalysis 组件内容: 四、远程加载 .vue 组件并使用 平时咱们都是在项目写好组件使用,那么如何从服务端加载 .vue 文件用并在项目中跑起来呢? 大致分以下三步: http 请求远程文件将文本组件转换为 vue 响应式组件component :is 渲染1. vue3-sfc-loaderVue3/Vue2 单文件组件加载器,在运行时从 html/js 动态加载 .vue 文件,不需要 node.js 环境,不需要 webpack 构建步骤,这里以 Vue2 为例: Vue2 的使用路径在 /dist 目录下: import{loadModule}from'vue3-sfc-loader/dist/vue2-sfc-loader.js' 2. 封装动态远程加载组件使用 is 动态渲染加载的组件,同时使用透传属性 v-bind,事件响应处理 v-on="$listeners" template divclass="async-component" component:is="remote"v-if="remote"v-bind="$attrs"v-on="$listeners"/ /div /template 动态加载: moduleCache 将 Vue 对象传下去(强制性)getFile 获取远程文件逻辑,此处使用原生 fetch API 请求addStyle 创建并添加样式constcom=awaitloadModule(url,{ moduleCache:{ vue:Vue, }, //获取文件 asyncgetFile(url){ constres=awaitfetch(url) if(!res.ok){ throwObject.assign(newError(`${res.statusText}${url}`),{res}) } return{ getContentData:asBinary=(asBinary?res.arrayBuffer():res.text()), } }, //添加样式 addStyle(textContent){ conststyle=Object.assign(document.createElement('style'),{textContent}) constref=document.head.getElementsByTagName('style')[0]||null document.head.insertBefore(style,ref) }, }) 五、render函数实现一个菜单下拉框 1. 下拉框组件//Select.vue template divclass="select-wrap" span福利商城/span spanSaas平台/span span活动定制/span /div /template image.png2. 渲染组件我们要将这个组件渲染在网页上,操作应该是这样的:当鼠标移动到产品服务时,将下拉框组件作为一个组件实例渲染在页面的合适位置,要知道组件渲染的位置,我们必须知道父组件的 dom 位置,我们通过 ref 来获取父组件的 dom 信息: //App.vue divref="select" spanclass="name"产品服务/span /div scriptsetup import{ref}from"vue" importSelectfrom"./Select.vue" import{createVNode,h,render,VNode}from'vue' constselect=ref() //在Vue3中,渲染一个Vonde,核心逻辑如下: functioncreateDom(){ constleft=select.value.offsetLeft+"px" constwidth=select.value.getBoundingClientRect().left+"px" constprops={ width, left, } //1、创造包裹虚拟节点的div元素 constcontainer=document.createElement('div'); //2、创造虚拟节点 letvm=createVNode(Select,props) //3、将虚拟节点创造成真实DOM render(vm,container) //4、将渲染的结果放到body下 document.body.appendChild(container.firstElementChild) } /script 其中,prop 是传递给 Select 组件的距离参数,在组件内设置即可 3. 销毁组件销毁组件,我们可以使用 render 渲染一个空对象即可: render(vm,container) 如果需要子组件来销毁自身,我们可以使用父子传值 template divclass="select-wrap"@mouseleave="beforeUnload" span福利商城/span spanSaas平台/span span活动定制/span /div /template scriptsetup constemit=defineEmits(['destroy']) functionbeforeUnload(){ emit('destroy') } /script 父组件里,我们需要在 props 中添加一个 onDestroy 函数 functioncreateDom(){ constleft=select.value.offsetLeft+"px" constwidth=select.value.getBoundingClientRect().left+"px" constprops={ width, left, onDestroy:()={ render(null,container) }, } //1、创造包裹虚拟节点的div元素 constcontainer=document.createElement('div'); //2、创造虚拟节点 letvm=createVNode(Select,props) //3、将虚拟节点创造成真实DOM render(vm,container) //4、将渲染的结果放到body下 document.body.appendChild(container.firstElementChild) } 参考资料[1] http://localhost:8080/demo/: https://link.juejin.cn?target=http%3A%2F%2Flocalhost%3A8080%2Fdemo%2F 阅读原文

上一篇:2025-10-02_数据库加密方案实践:我们选的不是最完美,但是真的够用了。 下一篇:2022-07-29_2022海浪电影周官宣 | 五个维度,一场生活与电影的交响

TAG标签:

18
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价