关于vue.js:SpringVue实现token登录

原文链接 前端代码:https://github.com/Snowstorm0... 后端代码:https://github.com/Snowstorm0... 应用 Spring+Vue 实现 token 登录、退出、拜访拦挡等性能。 1 前端1.1 创立我的项目关上cmd,输出ui命令: vue ui若没有反馈,可能是版本太低,须要卸载后重装: npm uninstall vue-cli -g #卸载npm install @vue/cli -g #装置执行ui命令胜利后,会呈现提醒: Starting GUI... Ready on http://localhost:8000并会主动关上页面: 创立名为SpringAndVue-vue的我的项目,预设抉择“手动”;性能开启 Babel、Router、Vuex、Linter/Formatter;配置抉择“ESLint with error prevention only”;版本倡议应用 “vue2.0”。创立新我的项目。 通过cd进入目录,启动我的项目: npm run serve1.2 装置插件1.2.1 element-ui关上cmd,输出ui命令: vue ui在插件项搜寻,并点击装置。 vue2.0 抉择装置 “vue-cli-plugin-element”;vue3.0 抉择装置 “vue-cli-plugin-element-plus”。 1.2.2 axiosTerminal装置axios,每个新我的项目都须要装置: # vue-cli2.0命令npm install axios# vue-cli3.0命令npm add axios1.3 主体代码1.3.1 Appsrc/app.vue: <template> <div id="app"> <el-container style="height: 500px; border: 1px solid #eee"> <!-- 头部文字 --> <el-container> <el-header style="text-align: right; font-size: 18px"> <span>公众号:代码的路</span> </el-header> <br><br> <router-view></router-view> </el-container> </el-container> </div></template><style> /* 顶栏 */ .el-header { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } /* 底栏 */ .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } /* 侧栏 */ .el-aside { background-color: #D3DCE6; color: #333; text-align: center; line-height: 200px; } /* 次要区域 */ .el-main { background-color: #E9EEF3; color: #333; text-align: center; } body > .el-container { margin-bottom: 40px; }</style><script> export default { data() { const item = { }; return { tableData: Array(20).fill(item) } } };</script>1.3.2 登录页src/views/login.vue ...

August 26, 2022 · 6 min · jiezi

关于vue.js:axios-post请求写法

后盾用body形式承受参数: 1、axios.post(api,data)这种形式间接传递json格局的data数据,平平无奇 后盾用单个参数形式承受参数: 1、axios.post(api,qs.stringify(data))这种形式须要用qs对json格局的data数据进行格局转换,略微有点简单 2、 axios({ url: api, method: 'post', data: qs.stringify(data)})和下面逻辑一样,写法不一样 3、 axios({ url: api, method: 'post', params: data})这种形式不须要qs进行格局转换,更简略 留神:因为后盾的接管形式是单个参数承受,data:data这样会导致后盾拿到的是一个json对象,接口会报400,"Required String parameter 'xxx' is not present"。

August 25, 2022 · 1 min · jiezi

关于vue.js:vue实现图片裁剪vuecropper的使用方法

vue-cropper的装置 npm install vue-cropper或yarn add vue-cropper只须要一个图片地址和一个导出截取后图片的办法,vue-cropper在截取图片后会返回一个图片的base64数据。 <template> <div class="cropper-content"> <div class="cropper-box"> <div class="cropper"> <vue-cropper ref="cropper" :img="option.img" :outputSize="option.outputSize" :outputType="option.outputType" :info="option.info" :canScale="option.canScale" :autoCrop="option.autoCrop" :autoCropWidth="option.autoCropWidth" :autoCropHeight="option.autoCropHeight" :fixed="option.fixed" :fixedNumber="option.fixedNumber" :full="option.full" :fixedBox="option.fixedBox" :canMove="option.canMove" :canMoveBox="option.canMoveBox" :original="option.original" :centerBox="option.centerBox" :height="option.height" :infoTrue="option.infoTrue" :maxImgSize="option.maxImgSize" :enlarge="option.enlarge" :mode="option.mode" @realTime="realTime" @imgLoad="imgLoad" > </vue-cropper> </div> <!--底部操作工具按钮--> <div class="footer-btn"> <div class="scope-btn"> <label class="btn" for="uploads">抉择封面</label> <input type="file" id="uploads" style="position: absolute; clip: rect(0 0 0 0)" accept="image/png, image/jpeg, image/gif, image/jpg" @change="selectImg($event)" /> <el-button size="mini" type="danger" plain icon="el-icon-zoom-in" @click="changeScale(1)" >放大</el-button > <el-button size="mini" type="danger" plain icon="el-icon-zoom-out" @click="changeScale(-1)" >放大</el-button > <el-button size="mini" type="danger" plain @click="rotateLeft" >左旋转</el-button > <el-button size="mini" type="danger" plain @click="rotateRight" >右旋转</el-button > </div> <div class="upload-btn"> <el-button size="mini" type="success" @click="uploadImg('blob')" >上传封面 <i class="el-icon-upload"></i ></el-button> </div> </div> </div> <!--预览效果图--> <div class="show-preview"> <div :style="previews.div" class="preview"> <img :src="previews.url" :style="previews.img" /> </div> </div> </div></template><script>import { VueCropper } from 'vue-cropper';export default { name: 'CropperImage', components: { VueCropper }, props: ['Name'], data() { return { name: this.Name, previews: {}, option: { img: '', //裁剪图片的地址 outputSize: 1, //裁剪生成图片的品质(可选0.1 - 1) outputType: 'jpeg', //裁剪生成图片的格局(jpeg || png || webp) info: true, //图片大小信息 canScale: true, //图片是否容许滚轮缩放 autoCrop: true, //是否默认生成截图框 autoCropWidth: 230, //默认生成截图框宽度 autoCropHeight: 150, //默认生成截图框高度 fixed: true, //是否开启截图框宽高固定比例 fixedNumber: [1.53, 1], //截图框的宽高比例 full: false, //false按原比例裁切图片,不失真 fixedBox: true, //固定截图框大小,不容许扭转 canMove: false, //上传图片是否能够挪动 canMoveBox: true, //截图框是否拖动 original: false, //上传图片依照原始比例渲染 centerBox: false, //截图框是否被限度在图片外面 height: true, //是否依照设施的dpr 输入等比例图片 infoTrue: false, //true为展现实在输入图片宽高,false展现看到的截图框宽高 maxImgSize: 3000, //限度图片最大宽度和高度 enlarge: 1, //图片依据截图框输入比例倍数 mode: '230px 150px' //图片默认渲染形式 } }; }, methods: { //初始化函数 imgLoad(msg) { console.log('工具初始化函数=====' + msg); }, //图片缩放 changeScale(num) { num = num || 1; this.$refs.cropper.changeScale(num); }, //向左旋转 rotateLeft() { this.$refs.cropper.rotateLeft(); }, //向右旋转 rotateRight() { this.$refs.cropper.rotateRight(); }, //实时预览函数 realTime(data) { this.previews = data; }, //抉择图片 selectImg(e) { let file = e.target.files[0]; if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) { this.$message({ message: '图片类型要求:jpeg、jpg、png', type: 'error' }); return false; } //转化为blob let reader = new FileReader(); reader.onload = (e) => { let data; if (typeof e.target.result === 'object') { data = window.URL.createObjectURL(new Blob([e.target.result])); } else { data = e.target.result; } // 赋值抉择图片的地址 this.option.img = data; }; //转化为base64 reader.readAsDataURL(file); }, //上传图片 uploadImg(type) { let _this = this; if (type === 'blob') { //获取截图的blob数据 this.$refs.cropper.getCropBlob(async (data) => { let formData = new FormData(); }); } } }};</script><style scoped lang="scss">.cropper-content { display: flex; display: -webkit-flex; justify-content: flex-end; .cropper-box { flex: 1; width: 100%; .cropper { width: auto; height: 300px; } } .show-preview { flex: 1; -webkit-flex: 1; display: flex; display: -webkit-flex; justify-content: center; .preview { overflow: hidden; border: 1px solid #67c23a; background: #cccccc; } }}.footer-btn { margin-top: 30px; display: flex; display: -webkit-flex; justify-content: flex-end; .scope-btn { display: flex; display: -webkit-flex; justify-content: space-between; padding-right: 10px; } .upload-btn { flex: 1; -webkit-flex: 1; display: flex; display: -webkit-flex; justify-content: center; } .btn { outline: none; display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; -webkit-appearance: none; text-align: center; -webkit-box-sizing: border-box; box-sizing: border-box; outline: 0; -webkit-transition: 0.1s; transition: 0.1s; font-weight: 500; padding: 8px 15px; font-size: 12px; border-radius: 3px; color: #fff; background-color: #409eff; border-color: #409eff; margin-right: 10px; }}</style>预览效果图: ...

August 25, 2022 · 3 min · jiezi

关于vue.js:一个简单的vue公式编辑器组件

示例 一个基于vue实现数据统计公式的基本功能。新建一个 formula.vue 组件,应用 <formula ref="formulaPage" :data-list="dataList" ></formula> <template> <div id="formulaPage"> <!-- 公式编辑区域 --> <div class="formulaView" id="formulaView" ref="formulaView" @click.stop="recordPosition()" > <div class="content-item" v-for="(item,index) in formulaList" :key="index" @click.stop="recordPosition(index)" > <div class="num" v-if="item.type == 'num'" > &zwj;{{item.value}} </div> <div class="plain" v-else-if="item.type == 'plain'" > &zwj;{{item.value}} </div> <div class="obj" v-else-if="item.type == 'obj'" > &zwj;{{item.value}} </div> <!--光标--> <div class="cursor" v-if="item.cursor" ></div> </div> </div> <div class="tab mt_10 flex-lr"> <div class=""> <el-select @change="e=>{addItem(e,'obj',)}" style="width: 120px" v-model="dataId" placeholder="抉择指标" > <el-option v-for="item in dataList" :label="item.name" :value="item.id" :key="item.id" ></el-option> </el-select> <el-select @change="e=>{addItem(e,'plain',)}" v-model="operatorId" placeholder="抉择数学运算符" style="width: 120px" class="ml_20" > <el-option v-for="item in operatorList" :label="item.name" :value="item.id" :key="item.id" > </el-option> </el-select> </div> <div class=""> <span class="mr_10 pointer theme-col" @click="clearAll()" > 革除全副</span> </div> </div> </div></template><script> /** * dataList 须要抉择数据的汇合 * defaultList 初始值的汇合 * @change 比变更后回传的数据 * **/ export default { name: '', props:{ dataList:{ type:Array, default() { return []; }, }, defaultList:{ type:Array, default() { return []; }, }, }, data: function () { return { // 公式字符串 formulaStr:'', dataId:'', operatorId:'', formulaList:[], //运算符 operatorList:[ { name:'+', id:'+' }, { name:'-', id:'-' }, { name:'*', id:'*' }, { name:'/', id:'/' }, { name:'()', id:'()' }, ] } }, watch:{ formulaList(val){ this.$emit('change',val) } }, created() { //监听鼠标事件 this.$nextTick(function () { document.addEventListener("keydown", this.keydown, false); document.addEventListener('click',this.formulaBlur); }); }, destroyed () { //移除监听事件 document.removeEventListener("keydown", this.keydown, false); document.removeEventListener('click',this.formulaBlur); }, methods: { // 获取 getFormula: function(){ }, // 点选时记录光标地位 recordPosition(index) { if(this.formulaList && this.formulaList.length >0){ this.formulaList = this.formulaList.map((item,itemIndex)=>{ item.cursor = false; if(index > -1 && index == itemIndex){ item.cursor = true; }else if((index!==0 && !index) && itemIndex == (this.formulaList.length -1)){ item.cursor = true; } return item }); }else { this.formulaList = [ { cursor:true, type:'placeholder', value:'', } ] } // this.$forceUpdate(); }, //失去焦点 formulaBlur(e){ this.formulaList = this.formulaList?.map(item=>{ item.cursor = false; return item }) }, /** * @returns {addItem<*, void, *>} * 增加字段 * type obj 字段 num 数字 plain符号 * place 是否批改光标地位 */ addItem: function (val, type,place = true) { if(!val) return false; let that = this; //插入括号 if(type == 'plain' && val == '()'){ val = '('; setTimeout(function () { that.addItem(')',type,false) },50) } let obj={},data = { value:'', key:val, type:type, }; if(type == 'obj'){ //获取数据 为 value 赋值 obj = this.dataList?.find(item=>item.id == val); data.value = obj.name; }else { data.value = val; } if(this.formulaList && this.formulaList.length>0){ const length = this.formulaList.length; for (let i = 0; i < length; i++) { //查找光标地位 如果光标地位为空 则在最初增加 if(this.formulaList[i].cursor){ this.formulaList.splice(i+1,0,data); place && this.recordPosition(i+1); break; }else if(i === (this.formulaList.length - 1)){ this.formulaList.push(data) this.recordPosition(this.formulaList.length - 1); } } }else { if(!this.formulaList){ this.formulaList = []; } this.formulaList.push(data); this.recordPosition(this.formulaList.length - 1); } }, //革除全副 clearAll(){ this.formulaList = []; let that = this; setTimeout(function () { that.recordPosition(); },100); }, //删除 deleteItem(type){ let arr = JSON.parse(JSON.stringify(this.formulaList)),index = null; const length = arr?.length; for (let i = 0; i < length; i++) { if(arr[i].cursor && arr[i].key){ index = i; if(type == 'del'){ index = i + 1; } if(index > -1){ this.formulaList.splice(index,1); if(type == 'del') { }else { this.recordPosition(index - 1); } } break; } } }, // 键盘输入 keydown(e){ //禁止输出 // 检测光标是否存在 let index,cursorData = this.formulaList?.find((item,itemIndex)=>{ if(item.cursor){ index = itemIndex } return item.cursor }); if(!cursorData){ return false; } //左右挪动键管制光标地位 if (e && [37,39].includes(e.keyCode)){ if(e.keyCode == 37){index = index - 1;}else {index = index + 1;} if(index > -1 && index < this.formulaList.length){ this.recordPosition(index); } }else if (e && e.keyCode == 8){ //Backspace 键 删除后面的值 this.deleteItem(); }else if (e && [107,109,106,111].includes(e.keyCode)){ //运算符列表 this.addItem(e.key,'plain') }else if (e && e.shiftKey && [48,57].includes(e.keyCode) ){ //括号 if( e.keyCode == 48) e.key = ')'; if( e.keyCode == 57) e.key = '('; this.addItem(e.key,'plain') }else if (e && e.keyCode == 46){ //delete键删除光标前面的值 this.deleteItem('del'); }else { document.returnValue = false; var tt=/^([1-9]{1}[0-9]{0,7})$/;//能输出负数 if(tt.test(e.key)){ //输出为数字 插入数字 this.addItem(e.key,'num') } } }, /** * 公式转为字符串 * 格局 [id]符号数字 * **/ parsingFormula: function(formulaStr){ let str = '',arr = []; arr = this.formulaList?.map(item=>{ let val = item.key; if(val){ if(item.type == 'obj'){ val = '['+val+']' } str = str+val; } return val }); return str }, /** * 格局效验 * */ formatValidation(){ let objData = null; let arr = this.formulaList?.filter(item=>{ if(item.type == 'obj'){ objData = item; } return item.key; }),data = {type:true,mag:''}; if(!objData){ data.mag = '至多增加一个指标'; }else { for (let i = 0; i < arr.length; i++) { if(i < arr.length-1){ //判断以后类型 if(arr[i].type == 'obj' && arr[i+1].type =='plain' ){ //类型为obj时 后一个 需以 符号结尾 data.mag = '指标后缀'; } } } } if(data.mag){ data.type = false; } return data; }, } }</script><style lang="scss"> #formulaPage { .formulaView{ padding: 3px 4px; width: 100%; height: 120px; border: 1px solid #eee; line-height: 1.3; font-size: 12px; overflow-y: scroll; .content-item{ position: relative; height: 16px; cursor: text; user-select: none; display: flex; align-items: center; float: left; .cursor{ height: 13px; width: 1px; background: #333; animation:defaultCursor 1s steps(2) infinite; position: absolute; right: 0; } .obj { padding: 0 5px; margin: 0 1px; background: #f1f1f1; border-radius: 3px; } .num{ color: #000; background: #fff; padding: 0 1px 0 0; } } } } @keyframes defaultCursor { 0% { opacity: 1; } 100% { opacity: 0; } }</style>

August 23, 2022 · 4 min · jiezi

关于vue.js:拼多多排行榜

vue配合animatedata(){ return { otherList: [ { order: 4, id:1, word: "小明1", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" }, { order: 5, id:2, word: "小明2", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" }, { order: 6, id:3, word: "小明", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" }, { order: 7, id:4, word: "小明4", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" }, { order: 8, id:5, word: "小明5", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" }, { order: 9, id:6, word: "小明666", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" }, { order: 10, id:7, word: "小明777", time: "21`15`234", src: "@/assets/bg_cjfx.jpg" } ], length: "", nowIndex: "", preIndex: ""} }办法 ...

August 22, 2022 · 2 min · jiezi

关于vue.js:修改项目中的elementplus源码的方法即pnpm-patch的使用

首先通过 pnpm view elemnt-plus查看你我的项目中的element-plus的版本,确定好版本当前,就执行上面的命令:(我电脑上的版本是2.2.14) pnpm patch element-plus@2.2.14而后就零碎就会给我一个目录,我的电脑是win10,目录是这样的:C:\Users\myname\AppData\Local\Temp\1914c1cb00c764bb85a448696126ce13\user,我就能够在这个目录里批改我的源码了,我的项目里引入的elemnt-plus包并不是源码的模式,所以你还要从git上clone个element-plus包下来 cd element-pluspnpm install你能够开始批改你的 element-plus了,批改完,build pnpm build而后把dist目录里的文件,copy到方才的C:\Users\myname\AppData\Local\Temp\1914c1cb00c764bb85a448696126ce13\user目录里,并笼罩掉。最初一步,执行一下命令即可: pnpm patch-commit C:\Users\myname\AppData\Local\Temp\1914c1cb00c764bb85a448696126ce13\user我过后到这一步,总是报错:ERR_PNPM_PATCH_NOT_APPLIED,起因是你的我的项目可能并非间接应用了element-plus包,而是其余的库依赖于element-plus,所以你须要用 pnpm i element-plus装置一下,而后再从新执行以上步骤即可。

August 22, 2022 · 1 min · jiezi

关于vue.js:Vue-warn-Method-xx-has-already-been-defined-as-a-prop

报错如下Vue wran]: Method "onLogin" has already been defined as a prop. 起因: 一个组件内,props、data、computed、methods都会被挂载到vue实例(vm)上,因而不能重名 解决: 1、命名避开重名2、在watch监听里写

August 21, 2022 · 1 min · jiezi

关于vue.js:token数据的持久化管理借助插件vuexpersistedstate

token 须要做长久化存储计划一 : vuex 配合 localStorage: token 数据一式两份存储的起因: vuex 基于内存进行存储,速度快,然而刷新就会失落,localStorage 基于磁盘进行存储,存取较慢,然而刷新不会失落vuex 和 localStorage 联合应用就能实现 token 的长久化存储token 须要做长久化存储计划二 : 应用插件实现 --> vuex-persistedstate 在 vuex 中筹备 user、cart 模块将插件配置到 vuex 的 plugins 选项中,配置 user、cart 模块进行状态长久化批改 state 数据就会触发主动同步机制,能够批改一下数据监测测试是否同步胜利步骤: [文档: https://developer.aliyun.com/...] 装置:npm i vuex-persistedstate在 src/store 文件夹下新建 modules 文件,在 modules 下新建 user.js 和 cart.js --> [src/store/modules/user.js]应用插件须要去到 store/index.js 的 plugins 下进行插件治理触发的机制: paths 中配置的模块中数据产生扭转插件会主动帮忙同步留神: 数据长久化的实现默认是将数据存在 localStorage 中的,然而这个能够被定义paths 用于指定长久化的数据对象,能够是整个模块,也能够指定单个数据 --> user.tokenpaths 中配置的模块中数据产生扭转插件会主动帮忙同步,执行的流程是: 每次数据扭转会将 vuex 的数据同步到 localStorage,而后刷新的时候将 localStorage 的数据同步到 vuex

August 21, 2022 · 1 min · jiezi

关于vue.js:Vue实现登录功能

原文链接 代码地址:https://github.com/Snowstorm0... 1 创立我的项目关上cmd,输出ui命令: vue ui若没有反馈,可能是版本太低,须要卸载后重装: npm uninstall vue-cli -g #卸载npm install @vue/cli -g #装置执行ui命令胜利后,会呈现提醒: Starting GUI... Ready on http://localhost:8000并会主动关上页面: 创立名为SpringAndVue-vue的我的项目,预设抉择“手动”;性能开启 Babel、Router、Vuex、Linter/Formatter;配置抉择“ESLint with error prevention only”;版本倡议应用 “vue2.0”。创立新我的项目。 通过cd进入目录,启动我的项目: npm run serve2 装置插件2.1 element-ui关上cmd,输出ui命令: vue ui在插件项搜寻,并点击装置。 vue2.0 抉择装置 “vue-cli-plugin-element”;vue3.0 抉择装置 “vue-cli-plugin-element-plus”。 2.2 axiosTerminal装置axios,每个新我的项目都须要装置: # vue-cli2.0命令npm install axios# vue-cli3.0命令npm add axios2.3 mockjsTerminal装置mockjs npm install mockjs 3 增加性能3.1 login创立 login.vue页面: <template> <div class="loginbBody"> <div class="loginDiv"> <div class="login-content"> <h1 class="login-title">用户登录</h1> <el-form :model="loginForm" label-width="100px" :rules="rules" ref="loginForm"> <el-form-item label="名字" prop="name"> <el-input style="width: 200px" type="text" v-model="loginForm.name" autocomplete="off" size="small"></el-input> </el-form-item> <el-form-item label="明码" prop="password"> <el-input style="width: 200px" type="password" v-model="loginForm.password" show-password autocomplete="off" size="small"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="confirm">确 定</el-button> </el-form-item> </el-form> </div> </div> </div></template><script> export default { name: "login", data(){ return{ loginForm:{ name:'', password:'' }, // 输出信息长度验证 rules:{ name: [ { required: true, message: '请输出用户名', trigger: 'blur' }, { min: 2, max: 5, message: '用户名长度在 2 到 5 个字符', trigger: 'blur' } ], password: [ { required: true, message: '请输明码', trigger: 'blur' }, { min: 2, max: 5, message: '明码长度在 2 到 5 个字符', trigger: 'blur' } ] } } }, methods:{ // 登录后跳转到主页 confirm(){ this.$refs.loginForm.validate((valid) => { if (valid) { //valid胜利为true,失败为false //去后盾验证用户名明码,并返回token this.$axios.post('/login',this.loginForm).then(res=>{ console.log(res.data) if(res.data.state==1){ //存储token到本地 this.$store.commit("SET_TOKEN",res.data.vData.token); //跳转到主页 this.$router.replace('/home'); }else{ alert('用户名或明码谬误!'); return false; } }); } else { console.log('校验失败'); return false; } }); } } }</script><style scoped > .loginbBody { width: 100%; height: 100%; background-color: #B3C0D1; } .loginDiv { position: absolute; top: 50%; left: 50%; margin-top: -200px; margin-left: -250px; width: 450px; height: 330px; background: #fff; border-radius: 5%; } .login-title { margin: 20px 0; text-align: center; } .login-content { width: 400px; height: 250px; position: absolute; top: 25px; left: 25px; }</style>3.2 配置路由在router.js中配置一级路由: ...

August 19, 2022 · 2 min · jiezi

关于vue.js:vue多个异步请求时的token失效

后端: 用户附带Bearer auth认证token去申请接口,而后后端判断接口是否过期?如果过期了,那么在返回信息的响应头authorization中增加一个新token,并容许了过期token的最初一个申请,之后再用这个过期token拜访接口就不会再返回新token了,因为新token曾经返回过一次了。 为了更好的测试,咱们将后端jwt的过期工夫设置为1分钟,则一分钟过期一次,而后刷新token,直到24小时后刷新工夫过期,须要从新登录。前端: 步骤:登录时,咱们申请login接口,而后接口返回给咱们登录胜利,以及token而后将token保留在vuex与LocalStorage,进入后盾首页进入后盾首页之后,咱们首先须要获取的是用户信息(在后盾首页index组件中申请),还有对应的用户权限菜单(在router拦截器中申请)、以及后盾首页须要显示的数据(在后盾首页index组件中申请)测试:因为token 1分钟 过期,咱们将在登录胜利进入后盾首页之后进行操作期待1分钟这个时候咱们刷新页面。 过程:他会同时执行3个申请,因为是异步申请,再者因为是不同组件,没有应用async/await,所以这3个申请中,任意一个第一工夫发出请求的,会在响应头中失去新的token,而后还没有来得及在vuex及LocalStorage中保留,第二个申请就带着Vuex/LocalStorage中的就token拜访了,而后失去后端返回的token生效及谬误状态码 后果:申请回来之后响应拦截器发现此token生效了,而后就间接跳转到登录页面,删除了vuex/LocalStorage中的所有信息,实际上在第一个申请收回的时候,新的token曾经返回了,只不过是还没有保留呢,第二个申请就带着旧token拜访了。 要害代码截图:axios封装: 路由拦截器: index.vue 刷新页面时申请: 发问:这种状况我尝试过很多办法,其中很多人都提出来应用监听,监听什么呢?token始终都是存在的,不会为空,如果监听token时候无效,那么又多了一次申请。 如果说去别的页面也会遇到这种问题,比方在用户核心页面,刷新页面会同时收回用户列表申请和我的信息申请及菜单申请,也会呈现这种问题。 如果解决这种问题,是前端解决还是后端解决?后端这样设计api是因为要实现token的单点登录,一个token只能一个人应用,过期的token也不会再刷新。 我切实想不到一个好的解决办法,心愿帅气的前端大佬们帮我指点迷津,我将感激不尽。

August 19, 2022 · 1 min · jiezi

关于vue.js:种草-Vue3-中几个好玩的插件和配置

小伙伴们晓得 TienChin 我的项目前端用的是 Vue3,当咱们把 Vue3 官网刷了一遍之后回来看 TienChin 我的项目的前端,发现还是有很多不太一样的中央,明天松哥就来和大家捋一捋 Vue3 中几个好玩的插件和配置,学完之后,置信大家对 TienChin 我的项目前端的很多写法就明确了。 1. Vite首先来给大家介绍一下 Vite,尽管这在 Vue3 中并不是必须的,然而思考到 TienChin 我的项目前端用了这个,还是给大家略微说两句。 Vite(法语意为 "疾速的",发音 /vit/,发音同 "veet")是一种新型前端构建工具,可能显著晋升前端开发体验。它次要由两局部组成: 一个开发服务器,它基于 原生 ES 模块 提供了 丰盛的内建性能,如速度快到惊人的 模块热更新(HMR)。一套构建指令,它应用 Rollup 打包你的代码,并且它是预配置的,可输入用于生产环境的高度优化过的动态资源。Vite 意在提供开箱即用的配置,同时它的插件 API 和 JavaScript API 带来了高度的可扩展性,并有残缺的类型反对。 如果小伙伴们绝得生疏,那么无妨回顾下咱们之前在 vhr 中给大家介绍的 Webpack,其实这个 Vite 相当于就是 Webpack。相比于 Webpack 的传统工具,Vite 最大的特点就是快。 Vite 通过在一开始将利用中的模块辨别为依赖和源码两类,改良了开发服务器启动工夫,因为依赖变动小而源码才是常常会变的货色。 不晓得小伙伴们看到这里有没有想到咱们 Java 中也有一个相似的玩意,那就是 Spring Boot 热加载。 Spring Boot 的热加载中用到了两个类加载器:一个是 base classloader,专门用来加载一些第三方的类;还有一个是 restart classloader,专门用来加载咱们本人写的类。热加载的时候,只须要 restart classloader 工作即可。好了,对于咱们 Java 工程师来说,大家晓得 Vite 是一个我的项目构建工具就能够了,接下来的例子我要通过 Vite 来和大家演示。 ...

August 17, 2022 · 3 min · jiezi

关于vue.js:HappyPack报错记录HappyPack-unable-to-locate-the-plugin-list

happyPack报错:HappyPack: unable to locate the plugin list! This most likely indicates an internal error. 原代码: configureWebpack: config => { //happypack - js 局部 config.module.rules.push({ test: /\.js$/, loader: 'happypack/loader?id=happyBabel', }) config.plugins.push(new HappyPack({ id: 'happyBabel', loaders: [{ loader: 'babel-loader?cacheDirectory=true', }], threadPool: happyThreadPool, //容许 HappyPack 输入日志 verbose: true, })}尝试后发现应该是js的rule局部有问题,去掉下面写在configureWebpack中的rule局部,批改为在chainWebpack中革除js的rule规定后,再进行happypack配置。 批改后代码: //happyPack - js 局部chainWebpack: config => { const jsRule = config.module.rule('js'); jsRule.uses.clear(); jsRule.use('happypack/loader?id=happyBabel') .loader('happypack/loader?id=happyBabel') .end();}configureWebpack: config => { //happypack - js 局部 config.plugins.push(new HappyPack({ id: 'happyBabel', loaders: [{ loader: 'babel-loader?cacheDirectory=true', }], threadPool: happyThreadPool, //容许 HappyPack 输入日志 verbose: true, })}

August 17, 2022 · 1 min · jiezi

关于vue.js:实现类似购物车的单选多选全选操作

指标:实现相似购物车的单选、多选、全选 实现如下: <template> <div class="div-table"> <a-row type="flex" class="div-table-thead"> <a-col :span="aRowCol.img"> <a-checkbox v-model:checked="checkedAll" @change="onChangeCheckedAll"></a-checkbox> <span style="display: inline; margin-left: 20px">图片</span> </a-col> <a-col :span="aRowCol.name"> <span style="text-align: left">名称</span> </a-col> <a-col :span="aRowCol.price"> <span>价格</span> </a-col> <a-col :span="aRowCol.quantity"> <span>数量</span> </a-col> <a-col :span="aRowCol.discount"> <span>优惠</span> </a-col> <a-col :span="aRowCol.amount"> <span>小计</span> </a-col> <a-col :span="aRowCol.leftNum"> <span>残余数量</span> </a-col> </a-row> <div class="div-table-tbody" v-if="acData.length"> <div class="div-table-tbody-item" v-for="(item, index) in acData" :key="index"> <div class="tbody-item-title"> <a-checkbox v-model:checked="item.checked" @change="onChangeCheckedItemAll(item)"></a-checkbox> <span style="margin-left: 20px">{{ item.acName }}</span> </div> <a-row type="flex" align="middle" class="tbody-item-row" v-for="(i, ind) in item.acList" :key="ind"> <a-col :span="aRowCol.img"> <a-checkbox v-model:checked="i.checked" @change="onChangeChecked(item, i)" style="margin-right: 10px"></a-checkbox> <a-image style="width: 96px; height: 96px; border-radius: 5px; margin-right: 10px" :preview="false" :src="i.url ? i.url : ''" :fallback="fallImage" /> </a-col> <a-col :span="aRowCol.name" class="card-info"> <span>{{ i.skuName }}</span> <span class="font12 light-gray">{{ i.skuModel }}</span> </a-col> <a-col :span="aRowCol.price"> <span>¥{{ i.price }}</span> </a-col> <a-col :span="aRowCol.quantity"> <span>{{ i.totalNum }}</span> </a-col> <a-col :span="aRowCol.discount"> <span>¥{{ i.discountAmount }}</span> </a-col> <a-col :span="aRowCol.amount"> <span>¥{{ i.amount }}</span> </a-col> <a-col :span="aRowCol.leftNum"> <span>{{ i.leftNum }}</span> </a-col> </a-row> </div> </div> </div></template><script setup>import { ref } from "@vue/reactivity"// 图片占位符const fallImage = ''const aRowCol = ref({ img: 4, name: 5, quantity: 3, price: 3, discount: 3, amount: 3, leftNum: 3})const acData = ref([ { acName: '第一个分组', acId: 1, acList: [{ url: null, rowId: '1', skuName: '1-1 子分组的名字', skuCode: '1-1的id', skuModel: '类型类型类型类型类型类型11111型', price: 100.00, totalNum: 2, discountAmount: 100.00, leftNum: 1, amount: 100.00, acType: null, acName: "普通商品", acId: 1 }, { url: null, rowId: '1', skuName: '1-2 子分组的名字', skuCode: '1-2的id', skuModel: '类型类型类型类型类型类型类型类型111111', price: 200.00, totalNum: 1, discountAmount: 100.00, leftNum: 1, amount: 300.00, acType: null, acName: "普通商品", acId: 1 }] }, { acName: '第2个分组', acId: 2, acList: [{ url: null, rowId: '2', skuName: '2-1 子分组的名字', skuCode: '2-1的id', skuModel: '类型类型类型222222222222', price: 100.00, totalNum: 2, discountAmount: 50.00, leftNum: 1, amount: 250.00, acName: '第2个分组', acId: 2 }, { url: null, rowId: '2', skuName: '2-2 子分组的名字', skuCode: '2-2的id', skuModel: '类型类型类型类型类型类222', price: 50.00, totalNum: 1, discountAmount: 0.00, leftNum: 0, amount: 100.00, acName: '第2个分组', acId: 2 }] }])const checkedAll = ref(false)const checkedList = ref([])// 数组去重const uniqueArr = arr => { const obj = {} // 辅助数组 return arr.reduce((sum, idx) => { if (!obj[idx.rowId]) { // 判断以后orderRowId是否曾经在checkedList外面 // 如果不在 obj[idx.rowId] = true // 则将以后orderRowId的值设置为true(也能够是其余 sum.push(idx) // 而后push进以后的数组 } return sum }, [])}// 是否全选(只依据流动类型全选可用)const isCheckedAll = itemData => { // 全选 checkedList与addCommodityData雷同 const tempList = [] checkedList.value.forEach(item => { const sameItemIdx = acData.value.findIndex(a => a.acName == item.acName) // 比照原数组与选中数组各项的长度是否一样 tempList.push( acData.value[sameItemIdx].acList.length == item.acList.length ) }) const uncheckLen = tempList.findIndex(item => item == false) // 找出长度不统一的值 if (itemData.checked && checkedList.value.length == acData.value.length && uncheckLen < 0) { // 只有与原数组各项长度保持一致能力选中全选 return true } else if (itemData.checked && itemData.acList.length == 6) { // 以后流动的长度与后盾以后页面长度(假如页面长度为6)一样 return true } else if ( itemData.checked && acData.value.length == 1 && itemData.acList.length == acData.value[0].acList.length ) { // 以后流动所有数据与接口返回数据长度一样且接口返回只有一个数组 return true } else { return false }}// 全选const onChangeCheckedAll = all => { acData.value && acData.value.map(item => { item.checked = all.target.checked item.acList.map(i => { i.checked = all.target.checked }) }) if (all.target.checked) { // 选中 // 比拟checkedList与acData是否有雷同分组 if (checkedList.value.length > 0) { const tempList = JSON.parse(JSON.stringify(acData.value)) // 深拷贝数组 checkedList.value.forEach((item, index) => { tempList.forEach(i => { // 比拟checkedList与acData是否有雷同流动,属于雷同流动,增加进对应的流动中 if (i.acId == item.acId) { checkedList.value[index].acList = [...checkedList.value[index].acList, ...i.acList] // 数组去重操作 checkedList.value[index].acList = uniqueArr(checkedList.value[index].acList) } else { checkedList.value.push(i) } }) }) } else { // 第一次勾选,checkedList为空 checkedList.value = JSON.parse(JSON.stringify(acData.value)) // 深拷贝数组,第一次勾全选 } } else { // 勾销选中 // 比拟acList与checkedList的长度值,如果checkedList长度大于acList,勾销勾选,就从checkedList里删掉acList checkedList.value.forEach(item => { const sameItemIdx = acData.value.findIndex(a => a.acId == item.acId) // 比照原数组与选中数组各项的长度是否一样 if (acData.value[sameItemIdx].acList.length == item.acList.length) { checkedList.value = [] } else { // 找到匹配的数据,删除 acData.value[sameItemIdx].acList.forEach(a => { item.acList.map((i, ind) => { if (i.rowId == a.rowId) { item.acList.splice(ind, 1) } }) }) } }) } console.log('all', checkedList.value)}// 依据分组全选const onChangeCheckedItemAll = itemData => { const hasItemIdx = checkedList.value.findIndex(item => item.acId == itemData.acId) console.log('hasItemIdx', hasItemIdx) if (itemData.checked) { // 如果之前有选中项 itemData.acList.map(item => { item.checked = true }) const copyArr = JSON.parse(JSON.stringify(itemData)) // 深拷贝 if (hasItemIdx < 0) { // 从未有选中的数据 checkedList.value.push(copyArr) } else { // 之前曾经有选中的数据 checkedList.value.forEach((item, index) => { copyArr.acList.forEach(i => { if (copyArr.acId == item.acId) { checkedList.value[index].acList = [...checkedList.value[index].acList, i] // 数组去重操作 checkedList.value[index].acList = uniqueArr(checkedList.value[index].acList) } }) }) } checkedAll.value = isCheckedAll(itemData) } else { itemData.acList.map(item => { item.checked = false }) if (checkedList.value[hasItemIdx].acList.length == itemData.acList.length) { checkedList.value.splice(hasItemIdx, 1) } else { itemData.acList.forEach(a => { checkedList.value[hasItemIdx].acList.forEach((b, idx) => { if (b.rowId == a.rowId) { checkedList.value[hasItemIdx].acList.splice(idx, 1) } }) }) } checkedAll.value = false } console.log('huodong', checkedList.value)}// 单个选中const onChangeChecked = (itemData, iData) => { const itemIdx = checkedList.value.findIndex(item => item.acName == itemData.acName) let iIdx if (itemIdx > -1) { iIdx = checkedList.value[itemIdx].acList.findIndex(i => i.rowId == iData.rowId) } if (iData.checked) { // 选中 if (itemIdx > -1) { checkedList.value[itemIdx].acList.push(iData) } else { checkedList.value.push({ acName: itemData.acName, acId: itemData.acId, acList: [iData] }) } // 勾选操作 if (itemData.acList.length == 1) { itemData.checked = true } else if (itemData.acList.length > 1) { if (itemIdx > -1 && checkedList.value[itemIdx].acList.length == itemData.acList.length) itemData.checked = true } checkedAll.value = isCheckedAll(itemData) } else { // 勾销选中 if (itemIdx > -1) { if (checkedList.value.length == 1) { checkedList.value[0].acList.length == 1 ? (checkedList.value = []) : checkedList.value[itemIdx].acList.splice(iIdx, 1) } else if (checkedList.value.length > 1) { checkedList.value[itemIdx].acList.length == 1 ? checkedList.value.splice(itemIdx, 1) : checkedList.value[itemIdx].acList.splice(iIdx, 1) } } iData.checked = false checkedAll.value = false itemData.checked = false } console.log('www', checkedList.value)}</script>

August 17, 2022 · 4 min · jiezi

关于vue.js:Vue组件异步请求一定要在mounted回调执行

有很多所谓的最佳实际通知你这个观点,但其实这个观点是十分全面的,甚至在大部分场景下他是错的。

August 13, 2022 · 1 min · jiezi

关于vue.js:Vue组件的通信方式

概述vue的两大个性是响应式编程和组件化。组件(Component)是 Vue 最外围的性能,然而各个组件实例的作用域是互相独立的,这表明不同组件之间的数据是无奈间接互相援用的。如果想要跨组件援用数据,就须要用到组件通信了,在通信之前先要了解组件之间的关系: 如上图所示:父子关系:A与B,A与C,B与D,C与E兄弟关系:B与C隔代关系(可能隔更多代):A与D,A与E跨级关系:B与E,D与E等 通信形式一、props/$emit父组件通过v-bind绑定一个自定义的属性,子组件通过props接管父组件传来的数据;子组件通过$emit触发事件,父组件用on()或者在子组件的自定义标签上应用v-on来监听子组件触发的自定义事件,从而接管子组件传来的数据。 1、父组件向子组件传值上面通过一个例子来阐明父组件向子组件传值,父组件parent.vue把数据books:['JavaScript高级程序设计', 'CSS新世界', '图解 HTTP 彩色版']传给子组件child.vue,并在child.vue中展现进去 // 父组件parent.vue<template> <div> <Child :books="books"/> </div></template><script>import Child from './components/Child.vue'export default { name: 'parent', components: { Child }, data() { return { books: ['JavaScript高级程序设计', 'CSS新世界', '图解 HTTP 彩色版'] } }}</script>// 子组件child.vue<template> <div> <ul> <li v-for="(item, index) in books" :key="index">{{item}}</li> </ul> </div></template><script>export default { props: { books: { type: Array, default: () => { return [] } } }}</script>留神:通过props传递数据是单向的,父组件数据变动时会传递给子组件,但子组件不能通过批改props传过来的数据来批改父组件的相应状态,即所谓的单向数据流。 2、子组件向父组件传值上面通过子组件点击书籍列表,用$emit()触发,而后再父组件中获取 ...

August 12, 2022 · 5 min · jiezi

关于vue.js:Vue仿钉钉审批流程

workflow钉钉审批流程设置workflow钉钉审批流程设置,基于vue开发。QQ交换群:639251756 线上开源地址 https://github.com/StavinLi/Workflow github点个星吧!预览地址 https://stavinli.github.io/Workflow/dist/index.html#/我的项目介绍UI钉钉格调技术点组件自调用+递归解决,按树状终局解决审批流程问题次要性能点界面缩放 <div class="zoom"> <div :class="'zoom-out'+ (nowVal==50?' disabled':'')" @click="zoomSize(1)"></div> <span>{{nowVal}}%</span> <div :class="'zoom-in'+ (nowVal==300?' disabled':'')" @click="zoomSize(2)"></div></div>节点设置(包含审批人、发起人、抄送人、条件设置) <el-drawer title="审批人设置" :visible.sync="approverDrawer" direction="rtl" class="set_promoter" size="550px" :before-close="saveApprover"> <div class="demo-drawer__content"> <div class="drawer_content"> <div class="approver_content"> <el-radio-group v-model="approverConfig.settype" class="clear" @change="changeType"> <el-radio :label="1">指定成员</el-radio> <el-radio :label="2">主管</el-radio> <el-radio :label="4">发起人自选</el-radio> <el-radio :label="5">发起人本人</el-radio> <el-radio :label="7">间断多级主管</el-radio> </el-radio-group> ...节点新增 <div class="add-node-btn"> <el-popover placement="right-start" v-model="visible"> <div class="add-node-popover-body"> <a class="add-node-popover-item approver" @click="addType(1)"> <div class="item-wrapper"> <span class="iconfont"></span> </div> <p>审批人</p> </a> <a class="add-node-popover-item notifier" @click="addType(2)"> <div class="item-wrapper"> <span class="iconfont"></span> </div> <p>抄送人</p> </a> <a class="add-node-popover-item condition" @click="addType(4)"> <div class="item-wrapper"> <span class="iconfont"></span> </div> <p>条件分支</p> </a> </div> ...5.谬误校验 let {type,error,nodeName,conditionNodes} = childNodeif (type == 1 || type == 2) { if (error) { this.tipList.push({ name: nodeName, type: ["","审核人","抄送人"][type] }) } this.reErr(childNode)} else if (type == 3) { this.reErr(childNode)} else if (type == 4) { this.reErr(childNode) for (var i = 0; i < conditionNodes.length; i++) { if (conditionNodes[i].error) { this.tipList.push({ name: conditionNodes[i].nodeName, type: "条件" }) } this.reErr(conditionNodes[i]) }}6.含糊搜寻匹配人员、职位、角色 ...

August 11, 2022 · 1 min · jiezi

关于vue.js:用Setup-API来写Piniajs

0.写在后面本教程应用基于Vue.js 3我的项目来解说1.装置参考官网装置 yarn add pinia# or with npmnpm install pinia2.导入在main.js或main.ts导入Pinia.js import { createPinia } from 'pinia'app.use(createPinia())3.新建Store先在src文件夹中创立store文件夹新建index.ts或index.js文件,本教程应用ts import { ref } from 'vue'import { defineStore } from 'pinia'export const useMainStore = defineStore('main', () => { // 就像写script setup一样来写store const count = ref<number>(1) const increment = ():void => { count.value++; } return { count, increment }})4.应用Store在App.vue <template> <div> count: {{ count }} <button @click="increment()">increment</button> </div></template><script lang="ts" setup>import { storeToRefs } from 'pinia'import { useMainStore } from '@/store/index'// hooks一样来应用const useMainStore = useMainStore()const { count, increment } = storeToRefs(useMainStore)// vue外面也能够间接改count.value = 0</script>

August 9, 2022 · 1 min · jiezi

关于vue.js:vfor为什么要加上key字段

v-for 在渲染结束之后,如果有了新的改变,比方是在数组两头增加了一个元素,这个时候会执行diff算法去判断,拿到两个数组之后获取两个数组的长度,用较短的 length 去进行遍历比拟,以以后条件下效率最高的形式执行更改。此时,如果元素没有增加 key 字段,遍历渲染如下:看图咱们能够发现,从匹配到不同元素之后就开始替换和新增造成了资源节约,而后咱们看一下,加了key 之后的遍历流程,通过对key的匹配,实现最大水平上的复用:如果了解有误,欢送斧正!

August 8, 2022 · 1 min · jiezi

关于vue.js:vuevitewebpack项目打包后部署到gitee报404问题

应用vite创立应用vite创立的vue3我的项目,如果打包完后想部署到gitee上,会显示空白,并且控制台报404谬误 遇到这种问题,须要在vite.config.js里的配置项增加一个base属性 import { defineConfig } from "vite";import vue from "@vitejs/plugin-vue";// https://vitejs.dev/config/export default defineConfig({ //部署git配置门路,否则报404 base: "./", plugins: [vue()],});应用webpack创立须要在vue.config.js里增加publicPath const BASE_URL = process.env.NODE_ENV === 'production' ? '/我的项目目录/' : '/'module.exports = { lintOnSave:false, publicPath:BASE_URL,}

August 8, 2022 · 1 min · jiezi

关于vue.js:VUE3学习多方搬运学习一

vue2 和 vue3 的区别vue2 数据定义在 data,办法定义在 methods,操作一个数据往往会影响到 data, methods, computed, watch由此带来了大量的逻辑耦合,因而 vue2 给出了解决方案那就是 Mixin,然而 Mixin 常常导致命名抵触等各种问题,所以 Vue3 推出了 Composition API 用以解决以上痛点。 setupvue3新增了一个属性选项 -> setup,该选项是 Composition API 在组件内的入口,在组件创立的时候会先于 beforeCreate 执行,接管两个参数 setup(props, context)。 props props 是响应式的,也正因为是响应式的,因而不能应用构造的形式去获取其中的数据,否则响应式会生效,谬误示范如下: export default defineComponent({ setup(props, contxt){ let { name } = props console.log(name) //此时获取到的name值曾经无奈响应组件中的 name 值的变动了 }})context 在 setup 中咱们是拜访不到 vue2 里那种能拿到组件数据的 this 对象的,因而 context 提供了 this 对象最罕用的三个属性 attr, slot, emit 别离对应 vue2 中的 $attr(属性), $slot(插槽), $emit(发射事件),这几个属性都是主动同步最新的值,因而咱们拿到的数据也是最新的。对于reactive、ref 和 toRefs在 vue2 中咱们定义数据是在组件的 data 中,在 vue3 中咱们能够应用 reactive, ref 来定义数据应用。 ...

August 6, 2022 · 1 min · jiezi

关于vue.js:vxetable-切换页面后表格行错位的解决方法

呈现问题在我的项目开发过程中,发现 vxetable 如果点击表格进入详情页面,再点击返回,此时表格行会呈现错位的问题。如果在错位的表格进行一下滚动表格操作,显示又恢复正常。如果在页面上应用 keep-alive,对页面进行缓存,又应用了 vxetable 就有可能呈现下面形容的问题。 解决办法及思路step1. 首先是思考如何解决错位的问题,既然是可能通过滚动条复原错位的表格,那就从这里动手。对 vxe 文档进行查阅后,发现其是有相应的表格滚动条刷新办法的,然而因为我的项目应用的版本过低,只好本人手写一下。如果有和我碰到一样问题的小伙伴能够间接应用 vxe 自带的 api 。既然是缓存的页面才会呈现问题,那就在 activated 的钩子里执行一下刷新滚动条的操作。 activated(){ this.refreshScroll(this.$ref.vxeTable);},methods: { refreshScroll (vxetable) { const { lastScrollLeft, lastScrollTop } = vxetable; return vxetable.clearScroll().then(() => { if (lastScrollLeft || lastScrollTop) { // 重置最初滚动状态 vxetable.lastScrollLeft = 0 vxetable.lastScrollTop = 0 // 还原滚动状态 return vxetable.scrollTo(lastScrollLeft, lastScrollTop) } }) },}step2.找到错位的解决办法之后,就要思考如何可能在我的项目中全局解决,总不可能一个页面一个页面的去加 ref 和 activated 。最初想到了应用 vue 的混入 mixin 的办法,进行全局混入。新建一个 mixin.js,并在 main.js 引入 //minxin.jsimport XEUtils from 'xe-utils'//全局混入,解决vxe表格点击详情后返回的表格错位问题export default{ //解决vxe表格点击详情后返回的表格错位问题 activated(){ let $xetable = this.findTable(); if($xetable){ this.refreshScroll($xetable); } }, methods: { //寻找vxetable findTable () { return XEUtils.find( this, comp => comp && comp.$vnode && comp.$vnode.componentOptions && comp.$vnode.componentOptions.tag === 'vxe-table' ) }, //刷新滚动条 refreshScroll (vxetable) { const { lastScrollLeft, lastScrollTop } = vxetable; return vxetable.clearScroll().then(() => { if (lastScrollLeft || lastScrollTop) { // 重置最初滚动状态 vxetable.lastScrollLeft = 0 vxetable.lastScrollTop = 0 // 还原滚动状态 return vxetable.scrollTo(lastScrollLeft, lastScrollTop) } }) }, },};okk,解决 ...

August 5, 2022 · 1 min · jiezi

关于vue.js:8月总结高频vue面试题

v-model 能够被用在自定义组件上吗?如果能够,如何应用?能够。v-model 实际上是一个语法糖,如: <input v-model="searchText">复制代码实际上相当于: <input v-bind:value="searchText" v-on:input="searchText = $event.target.value">复制代码用在自定义组件上也是同理: <custom-input v-model="searchText">复制代码相当于: <custom-input v-bind:value="searchText" v-on:input="searchText = $event"></custom-input>复制代码显然,custom-input 与父组件的交互如下: 父组件将searchText变量传入custom-input 组件,应用的 prop 名为value;custom-input 组件向父组件传出名为input的事件,父组件将接管到的值赋值给searchText;所以,custom-input 组件的实现应该相似于这样: Vue.component('custom-input', { props: ['value'], template: ` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > `})复制代码Vue 模板编译原理Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步 第一步是将 模板字符串 转换成 element ASTs(解析器)第二步是对 AST 进行动态节点标记,次要用来做虚构DOM的渲染优化(优化器)第三步是 应用 element ASTs 生成 render 函数代码字符串(代码生成器)相干代码如下 export function compileToFunctions(template) { // 咱们须要把html字符串变成render函数 // 1.把html代码转成ast语法树 ast用来形容代码自身造成树结构 不仅能够形容html 也能形容css以及js语法 // 很多库都使用到了ast 比方 webpack babel eslint等等 let ast = parse(template); // 2.优化动态节点 // 这个有趣味的能够去看源码 不影响外围性能就不实现了 // if (options.optimize !== false) { // optimize(ast, options); // } // 3.通过ast 从新生成代码 // 咱们最初生成的代码须要和render函数一样 // 相似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world")))) // _c代表创立元素 _v代表创立文本 _s代表文Json.stringify--把对象解析成文本 let code = generate(ast); // 应用with语法扭转作用域为this 之后调用render函数能够应用call扭转this 不便code外面的变量取值 let renderFn = new Function(`with(this){return ${code}}`); return renderFn;}模板编译原理详解 传送门 ...

August 4, 2022 · 3 min · jiezi

关于vue.js:vue使用Mock模拟接口数据

1.开发环境 vue22.电脑系统 windows11专业版3.在开发的过程中,咱们在没有接口的时候须要模仿接口数据,怎么操作呢?应用Mockjs。4.装置Mock: yarn add mockjs -Dornpm i mockjs -D5.在我的项目中新建Mock相干的文件(GolbMethods/MockApi.js): import Mock from "mockjs";const drawData = { result: 1, reward: { productName: "家长伴学月卡", },};Mock.mock("/api/draw", "get", drawData);//生抽抽奖模仿接口5-1.新建Api文件,寄存接口(GolbMethods/Api.js): import axios from "axios";// export const drawApi = () => axios.get("/api/draw");export const drawApi = () => new Promise((resolve) => { setTimeout(() => { return resolve(axios.get("/api/draw")); }, 3000);});6.在main.js中引入: require("./GolbMethods/MockApi.js");7.在对应的vue文件中应用: import { drawApi } from "../GolbMethods/Api.js";methods代码:async drawA() { const res = await drawApi(); console.log(res);},mounted代码:this.drawA();// 成果如下:8.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰。 ...

August 3, 2022 · 1 min · jiezi

关于vue.js:发版迭代导致懒加载路由跳转报错

后盾管理系统,vue.config.js 去掉了预加载。 当版本迭代,某个菜单发版了,当用户未刷新时,点击改菜单没有反馈。 起因是因为懒加载须要申请改菜单的js,发版导致哈希值变动,找不到该文件。 解决方案拦挡路由报错,进行重载页面。 router.onError((error) => { const pattern = /Loading chunk (\d)+ failed/g const isChunkLoadFailed = error.message.match(pattern) if (isChunkLoadFailed) { window.location.href = `${location.origin}${router.history.pending.fullPath}` } else { console.log(error) }})如何进行本地模仿调试应用 router.beforeEach 拦挡指标路由,throw Error 模仿应用浏览器对加载的js文件,进行block解决,阻止网络申请。(network面板 点击指标js,右键抉择 Block request domain)

August 1, 2022 · 1 min · jiezi

关于vue.js:vue的PARTⅠ

PART Ⅰ——vue的根底构建1、先看货色<section id="assignment"> <input type="text" v-model="tempName" @keydown:enter=""/> <p :class="userClass">Style me!</p> <button @click="changeVisble">Toggle Paragraph</button> <input type="text" /> <p>Style me inline!</p> </section>const app = Vue.createApp({ data() { return { tempName: "", className: "", isVisble: true, }; }, watch: {}, computed: { userClass() { return "this.className;"; }, }, methods: { changeVisble() { this.isVisble = !this.isVisble; }, },});app.mount("#assignment");下面两段代码就是一个个别的vue构件或什么货色,上面首先分几点议论vue怎么起作用的 1、vue怎么和html相关联 2、vue从哪里去解决html 2、具体解决1、怎么关联<section id="assignment"></section>const app = Vue.createApp();//建设一个appapp.mount("#assignment");//将app和html的局部相关联2、VUE的根本局部(目前所学,在这一部分是够用的)const app = Vue.createApp({ data() { return {}; },//这个是古代js的简写,其实是//data:fuction(){}, watch: {}, computed: {}, methods: {},});一共四个局部一个函数三个构造体。data函数返回一个构造体外面是数据(这个是真的数据不蕴含函数),会主动包装成app类中的属性(这个很重要),在这个局部拜访间接this.就行。剩下三个是三个构造体外面都是办法。 ...

July 30, 2022 · 1 min · jiezi

关于vue.js:vue项目提示yargs报错

莫名其妙更新了共事的代码后果报错了,百度了半天也没找到问题所在,而后又认真看了下报错信息看到yargs报错,联合报错提醒的具体位置,在那个文件下看到了共事调试误引入的代码,,问题所在,把他屏蔽掉即可。如果须要就npm install yargs --save 参考资料:https://blog.csdn.net/weixin_...

July 29, 2022 · 1 min · jiezi

关于vue.js:初探Vue

Vue的两个版本Vue次要有两个版本,别离是完整版和非完整版 完整版是vue.js / vue.min.js 非完整版是vue.runtime.js / vue.runtime.min.js(vue.js是多了一些正文,min.js是去掉了正文,压缩了代码) 完整版完整版同时包含编译器(compiler) 和 运行时(runtime) 编译器的性能是将模板字符串编译为 JavaScript 渲染函数(render函数)的代码 运行时的性能包含创立 Vue 实例、渲染并解决虚构 DOM 等,它包含除了编译器的其余所有性能 只蕴含运行时版只蕴含运行时版就只有运行时,没有编译器 两个版本的区别 Vue完整版Vue运行时版特点有compiler没有compiler视图写在HTML里,或者写在template选项里写在render函数里,用h创立标签cdn引入vue.jsvue.runtime.jswebpack引入须要配置alias默认应用vue@cli引入须要额定配置默认应用那到底应该应用哪一个版本呢?最佳实际: 永远用非完整版,而后配合vue-loader和vue文件 起因 对于用户来说,非完整版下载的js文件体积小,用户体验好,但只反对h函数保障开发体验,只能写h函数的话,开发体验不好,如果有compiler, 开发者就能在vue文件里写更直观的HTML标签和template, 所以咱们须要一个compilervue-loader就能够引入compiler, 把vue文件里的HTML标签和template 会在构建时预编译成 h函数,这样用户和开发者都快乐template 和 render 的用法// 须要编译器new Vue({ template: '<div>{{ hi }}</div>'})// 不须要编译器new Vue({ render (h) { return h('div', this.hi) }})template标签和JS里的template //vue文件中的template标签 <template> <div id="app"> {{n}} <button @click="add">+1</button> </div> </template>//js中的template template : ` <div id="app"> {{n}} <button @click="add">+1</button> </div> `render函数: //不完整版在js中构建视图 render(h){ return h('div', [this.n,h('{on:{click:this.add}’,'+1']) }//不完整版应用vue-loader//先创立一个demo.vue文件,在外面构建视图 import demo from "./demo.vue" new Vue({ el: "#app", render(h) { return h(demo) } })codesandbox在线创立Vue我的项目进入官网https://codesandbox.io/点击 "Create a Sandbox, it's free"抉择 "Vue"能够把我的项目下载到本地,抉择左上角的file——而后Export to ZIP登录codesandbox.io后只能创立50个我的项目,不登录能够创立有限个咱们能够在codesandbox里在线写Vue的代码,不必任何本地的装置依赖 ...

July 28, 2022 · 1 min · jiezi

关于vue.js:vue大屏-免登录显示数据

router.config.js{ path: '/queueScreen', name: 'queueScreen', component: () => import(/* webpackChunkName: "user" */ '@/views/customstg/QueueScreen') }, { path: '/billScreen', name: 'billScreen', component: () => import(/* webpackChunkName: "user" */ '@/views/customstg/BillScreen') },白名单permission.jsconst whiteList = ['/billScreen', ps, 留神斜杠 最初在index.js中应用路由策略信息import { constantRouterMap } from '@/config/router.config'

July 27, 2022 · 1 min · jiezi

关于vue.js:如何以最快速度将Vue接入在线客服系统

尽管 Vue 框架和作者尤雨溪自己早已是研发行业,尤其是前端开发畛域人尽皆知的存在,然而本文正式开始之前,还是要做一个简略的介绍,来关照一下不太理解的读者。 Vue 框架,是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为能够自底向上逐层利用。Vue 的外围库只关注视图层,不仅易于上手,还便于与第三方库或既有我的项目整合。另一方面,当与现代化的工具链以及各种反对类库联合应用时,Vue 也齐全可能为简单的单页利用提供驱动。——摘自 Vue 官方网站如果没有技术背景也没关系,咱们来艰深地解释一下: 一般来说,我的项目研发分为前端和后端,如果以某个网页为例来解释,你能够简略了解为:后端研发负责“怎么让网页失常运行”,而前端研发负责“怎么让网页运行起来更好看”。 但这个时候,问题呈现了:想让网页难看有无数种计划,前端研发人员不管用哪种计划,都得从零开始一点一点地搭建,费时费力。 于是相似 Vue 这样的前端框架就诞生了,它间接通知所有前端研发人员:不必花工夫想这个问题了,我给你搭好了一个框架,你只须要把你感觉难看的元素放进去就行了(即:模块化)。 这样一来,不仅晋升了研发的效率,还升高了老本,两全其美,十分完满~ 好了,解释完 Vue 的用处,咱们就该回到明天的正题了:如何将 Vue 接入在线客服? 首先,在线客服的 JS 插件代码个别放在 index 入口页,能够参考下图: 接入胜利后,出现的成果,如下图右下角红框所示(这里用美洽官网来举例): 是不是灰常简略? 然而先别着急,因为实在环境下的需要是简单的。 比方:如果想在某些页面暗藏征询入口(或按钮),或者在特定的页面用特定的形式展现征询按钮,该如何能力实现? 能够通过调用_MEIQIA('withoutBtn');办法(下图)实现暗藏入口(或按钮)吗? 事实上,这种形式实时调用是没有成果的,一旦暗藏就会将所有页面的征询入口(或按钮)全副暗藏。 咱们倡议:比拟好的做法是自定义按钮,在须要征询按钮的页面调用美洽_MEIQIA('showPanel');办法关上聊天窗口。 有些技术人员依照这样的形式做了,比方:在 Vue 模板里间接调用 然而却发现: 这样的办法是无奈实现的。起因是 Vue 并不反对这样间接调用,会报错。 正确的办法应该是调用 window._MEIQIA('showPanel'); 如下图所示: 这样就会在指定页面中,点击自定义按钮的状况下,弹出对话窗口,而其余页面不显示征询入口或按钮,如下图所示: ————————————————版权申明:本文为CSDN博主「美洽技术」的原创文章,遵循CC 4.0 BY-SA版权协定,转载请附上原文出处链接及本申明。原文链接:https://blog.csdn.net/meiqia8...

July 27, 2022 · 1 min · jiezi

关于vue.js:WebRTC实现简单音视频通话功能

1 WebRTC音视频通话性能简介本文介绍如何基于WebRTC疾速实现一个简略的实时音视频通话。 在开始之前,您能够先理解一些实时音视频推拉流相干的根底概念: 流:一组按指定编码格局封装的音视频数据内容。一个流能够蕴含几个轨道,比方视频和音频轨道。推流:把采集阶段封包好的音视频数据流推送到 ZEGO 实时音视频云的过程。拉流:从 ZEGO 实时音视频云将已有音视频数据流拉取播放的过程。房间:是 ZEGO 提供的音视频空间服务,用于组织用户群,同一房间内的用户能够相互收发实时音视频及音讯。 用户须要先登录某个房间,能力进行音视频推流、拉流操作。用户只能收到本人所在房间内的相干音讯(用户进出、音视频流变动等)。更多相干概念可参考即构官网对于音视频SDK的介绍 术语阐明。 2 实现WebRTC视频通话的前提条件在实现根本的WebRTC实时音视频性能之前,请确保: 已在我的项目中集成 ZEGO Express SDK,详情请参考 疾速开始 - 集成。已在 ZEGO 控制台 创立我的项目,申请无效的 AppID 和 ServerSecret,详情请参考 控制台 - 项目管理 中的“我的项目信息”。3 WebRTC音视频通话示例代码咱们提供了一个实现了WebRTC音视频通话根本流程的残缺示例 HTML 文件,可作为WebRTC开发过程中的参考。 <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>Zego Express Video Call</title> <!-- 此处须要改成正确的 SDK 版本号 --> <script src="ZegoExpressWebRTC-x.x.x.js"></script> <style type="text/css"> h1, h4 { text-align: center; } .video-wrapper { width: 610px; margin: 0 auto; } .video-wrapper h4 { width: 300px; display: inline-block; position: relative; } #remote-video, #local-video { width: 300px; height: 270px; display: inline-block; position: relative; } .video-wrapper video { height: auto; } </style></head><body> <h1> Zego RTC Video Call </h1> <div class="video-wrapper"> <h4>Local video</h4> <h4>Remote video</h4> <div id="local-video"></div> <div id="remote-video"></div> </div> <script> // 文档中的 js 示例代码可粘贴至此处 // 我的项目惟一标识 AppID,Number 类型,请从 ZEGO 控制台获取 let appID = 0 // 接入服务器地址 Server,String 类型,请从 ZEGO 控制台获取(获取形式请参考上文“前提条件”) let server = "" // 初始化实例 const zg = new ZegoExpressEngine(appID, server); zg.setDebugVerbose(false) // 房间状态更新回调 // 此处在登录房间胜利后,立刻进行推流。在实现具体业务时,您可抉择其余机会进行推流,只有保障以后房间连贯状态是连贯胜利的即可。 // 房间状态更新回调 zg.on('roomStateChanged', async (roomID, reason, errorCode, extendedData) => { if (reason == 'LOGINED') { console.log("与房间连贯胜利,只有当房间状态是连贯胜利时,能力进行推流、拉流等操作。") } }) zg.on('roomUserUpdate', (roomID, updateType, userList) => { // 其余用户进出房间的告诉 }); zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => { // 房间内其余用户音视频流变动的告诉 if (updateType == 'ADD') { // 流新增,开始拉流 // 此处演示拉取流新增的列表中第一条流的音视频 const streamID = streamList[0].streamID; // streamList 中有对应流的 streamID const remoteStream = await zg.startPlayingStream(streamID); // 创立媒体流播放组件 const remoteView = zg.createRemoteStreamView(remoteStream); remoteView.play("remote-video", {enableAutoplayDialog:true}); } else if (updateType == 'DELETE') { // 流删除,通过流删除列表 streamList 中每个流的 streamID 进行进行拉流。 const streamID = streamList[0].streamID; zg.stopPlayingStream(streamID) } }); // 登录房间,胜利则返回 true // userUpdate 设置为 true 能力收到 roomUserUpdate 回调。 let userID = "user1"; // userID 用户本人设置,必须保障全局惟一 let userName = "user1";// userName 用户本人设置,没有唯一性要求 let roomID = "123"; // roomID 用户本人设置,必须保障全局惟一 // token 由用户本人的服务端生成,为了更快跑通流程,能够通过即构控制台 https://console.zego.im/ 获取长期的音视频 token,token 为字符串 let token = ``; zg.loginRoom(roomID, token, { userID, userName: userID }, { userUpdate: true }).then(async result => { if (result == true) { console.log("login success"); // 与房间连贯胜利,只有当房间状态是连贯胜利时,能力进行推流、拉流等操作。 // 创立流、预览 // 调用 createStream 接口后,须要期待 ZEGO 服务器返回流媒体对象能力执行后续操作 const localStream = await zg.createStream(); // 创立媒体流播放组件 const localView = zg.createLocalStreamView(localStream); localView.play("local-video", {enableAutoplayDialog:true}); // 开始推流,将本人的音视频流推送到 ZEGO 音视频云,此处 streamID 由用户定义,需全局惟一 let streamID = new Date().getTime().toString(); zg.startPublishingStream(streamID, localStream) } }); // // 登录房间的第二种写法 // (async function main(){ // await zg.loginRoom(roomID, token, { userID, userName: userID }, { userUpdate: true }) // })() </script></body></html>4 WebRTC音视频通话实现流程以用户 A 拉取用户 B 的流为例,一次简略的WebRTC实时音视频通话次要流程如下: ...

July 27, 2022 · 7 min · jiezi

关于vue.js:VUE-中文点击-成语点击-英文点击-校验-插件

背景一些确认框,能够换成这种点击成果 成果 插件阐明成语点击,随机打乱成语,反对任意汉字,英文 Installationnpm i -S idiom-verification UsageIn main.js of a Vue Project: import idiomVerification from 'idiom-verification';Vue.use(idiomVerification);In any Vue component, 文字校验 <idiom-verification @suc="() => {}" :list="['恭喜发财', '心想事成']"/>英文校验 <idiom-verification @suc="() => {}" :list="['abcd', 'qwer']"/>Options and Defaultssucfunctionclick success and emit listarrayinit show words

July 27, 2022 · 1 min · jiezi

关于vue.js:VUE实现Web端多人语音视频聊天

1 多人语音聊天性能介绍本文展现了如何应用 ZEGO Express SDK 结构多人音视频通话场景,即实现多对多实时音视频聊天互动。用户可在房间内与其余用户进行实时音视频通话,相互推拉流。该场景可用于多人实时音视频聊天、多人视频会议等。 2 Web端实现多人语音聊天筹备工作在利用多人音视频通话场景之前,请确保: 已在我的项目中集成 ZEGO Express SDK,实现根本的实时音视频性能,详情请参考 疾速开始 - 集成 和 疾速开始 - 实现流程。已在 ZEGO 控制台 创立我的项目,并申请无效的 AppID,详情请参考 控制台 - 项目管理 中的“我的项目信息”。3 vue集成语音聊天示例源码下载请参考 下载示例源码 获取源码。 相干源码请查看ZEGO Express SDK “src/Examples/Scenes/VideoForMultipleUsers” 目录下的文件。 4 ZEGO音视频SDK应用步骤本节将介绍如何应用 ZEGO Express SDK 实现多人视频通话。 流程图如下:API 调用时序图如下: ZEGO Express SDK 可反对多人视频通话,如上时序图以 2 名房间成员间的实时视频通话为例,倡议开发者参考上述流程设计本人的多人实时视频通话场景。 4.1 创立多人音视频聊天引擎实例创立 ZegoExpressEngine 引擎实例,将申请到的 AppID 传入参数 “appID”,将接入服务器地址传入参数 “server”。 “server” 为接入服务器地址,获取形式如下: 登录 ZEGO 控制台。在对应我的项目下单击“查看”。进入“我的项目配置”界面,在“我的项目信息”页签的“配置信息”中,单击 “ServerSecret” 前面的小眼睛按钮即可获取对应的接入服务器地址。const zg = new ZegoExpressEngine(appID, server);4.2 多人语音聊天开启房间内用户变动告诉开发者需在每位用户调用 loginRoom 接口登录房间时,将 ZegoRoomConfig 中的 “userUpdate” 设置为 “true” ,用于接管其余用户进出房间的回调告诉(即 roomUserUpdate。 ...

July 26, 2022 · 2 min · jiezi

关于vue.js:基于Thinkphp-6-Vue2的自动生成代码的后台管理系统

RDS是基于Thinkphp 6 + Vue2 + ElementUI + Vxe-table的,前后拆散的,主动生成代码的,通用后盾管理系统;零碎能够实现一般性零碎设计开发中90%的代码量,对于表的CRUD操作能够100%生成可用代码,根本不必批改就能够间接应用;更重要的是对于解决一对多的多表关联关系,通过适当设计也能够做到90%的代码主动生成;零碎内置了泛滥理论开发中罕用的表单组件,在进行零碎设计的时候就能够定义这些表单,进行一键生成,无需编写代码;零碎中内置了很多罕用的操作方法和操作方法模板,对数据的操作能够90%一键生成代码;零碎内置基于角色的操作权限管制和基于部门的数据权限操作控制,十分不便就能实现数据的隔离管制;零碎采纳Vxe-table充分利用其弱小性能,能够通过虚构滚动技术,可实现对大量数据的晦涩操作;零碎文档地址:http://doc.rdscode.cn性能预览地址:http://demo.raiseinfo.cn 常见操作界面数据字典页面表格列的可编辑个性可编辑表格一对多的关系演示多对多的关系演示 字段分表存储常见文本表单类型常见单选操作常见复选操作日期时间表单上传表单组件编辑器 其余表单组件菜单(模块)治理菜单(模块)的字段菜单(模块)的办法

July 26, 2022 · 1 min · jiezi

关于vue.js:vue禁用

常见禁用文件禁用:isDisabled="true"文本只读:readOnly = "true"下拉抉择/工夫抉择:open="false":allowclear="false"

July 26, 2022 · 1 min · jiezi

关于vue.js:es6的export与import的探索

在半个小时前,对export import 搞也搞不懂,学也学不会,半个小时痛定思痛,钻研后发现原来弄成本人能够了解的形式也不难 试验要害货色 import * as obj from '../js', 通过console.info(obj) 控制台察看打印内容,会发现export导出的是一个对象,export 和export default的区别在于这个对象的key不一样,export 导出key是在定义时候指定的命名,export default 导出的时候default这个命名。 语法对export default做了语法糖解决,因而,能够通过import 任意名字 from './js'进行接管,这个是语法糖带来的便当,咱们操作空间太少,就不对export default做奇思妙想的施展 export导出对象是对象,咱们就能够想到能够用es6提供的解构进行解决 试验过程,写在语雀上,就截图把

July 25, 2022 · 1 min · jiezi

关于vue.js:手把手教你在-Vue3-中自定义指令

@[toc]TienChin 我的项目前端是 Vue3,前端有这样的一个需要:有一些前端页面上的按钮要依据用户的权限来决定是否展现进去,如果用户具备相应的权限,那么就展现对应的按钮;如果用户不具备对应的权限,那么按钮就暗藏起来。大抵上就这样一个需要。 看到这个需要,可能有小伙伴首先想到用 v-if 指令,这个指令的确也能做,然而,因为用户具备的权限一般来说可能是多个,甚至可能还有通配符,所以这个比对并不是一个容易的事件,必定得写办法。。。所以,如果能用一个指令来实现这个性能,那么就会显得业余很多了。 说干就干,咱们来看看 Vue3 中如何自定义指令。 1. 成绩展现咱们先来看看实现自定义指令最终的应用形式: <button @click="btnClick" v-hasPermission="['user:delete']">删除用户</button>小伙伴们看到,这个 v-hasPermission 就是咱们的自定义指令,如果以后用户具备 user:delete 权限,这个按钮就会展现进去,如果以后用户不具备这个权限,这个按钮就不会展现进去。 2. 指令根底先要和小伙伴们说一下,Vue2 和 Vue3 在自定义指令上有一些差别,并不完全一致,上面的介绍次要是针对 Vue3 的介绍。 我先来和小伙伴们分享一下咱们具体是怎么做的,而后在解说代码的时候再来和大家说说各个参数的含意。 2.1 两种作用域自定义指令能够定义全局的,也能够定义部分的。 在正式开搞之前,小伙伴们须要先明确,自定义指令有两种作用域,一种是部分的自定义指令,还有一种是全局的自定义指令。部分的自定义指令就只能在以后 .vue 文件中应用,全局的则能够在所有的 .vue 文件中应用。 2.1.1 部分指令间接在以后 .vue 文件中定义即可,如下: directives: { focus: { // 指令的定义 mounted(el) { el.focus() } }}不过,在 Vue3 中,也能够这样写: <template> <div> <button v-onceClick="10000" @click="btnClick">ClickMe</button> </div></template><script> import {ref} from 'vue'; export default { name: "MyVue01", setup() { const a = ref(1); const btnClick = () => { a.value++; } return {a, btnClick} }, directives: { onceClick: { mounted(el, binding, vnode) { el.addEventListener('click', () => { if (!el.disabled) { el.disabled = true; setTimeout(() => { el.disabled = false; }, binding.value || 1000); } }); } } } }</script>这里我自定义了一个名叫 onceClick 的指令,给一个 button 按钮加上这个指令之后,能够设置这个 button 按钮在点击多久之后,处于禁用状态,避免用户反复点击。 ...

July 25, 2022 · 2 min · jiezi

关于vue.js:vue插槽slot详解

最近被问起是否理解vue的插槽(slot),咋一想发现,仿佛很少用到这玩意。所以整顿了下slot的一些用法。 slot (父组件 在子组件<slot> </slot>处插入内容)Vue 实现了一套内容散发的 API,将<slot>元素作为承载散发内容的进口,这是vue文档上的阐明。具体来说,slot就是能够让你在组件内增加内容的‘空间’。举个例子: //子组件 : (假如名为:ebutton) <template> <div class= 'button'> <button> </button> </div></template>//父组件:(援用子组件 ebutton)<template> <div class= 'app'> <ebutton> </ebutton> </div></template>咱们晓得,如果间接想要在父组件中的<ebutton></ebutton> 中增加内容,是不会在页面上渲染的。那么咱们如何使增加的内容可能显示呢?在子组件内增加slot 即可。 //子组件 : (假如名为:ebutton)<template> <div class= 'button'> <button></button> <slot></slot> //slot 能够放在任意地位。(这个地位就是父组件增加内容的显示地位) </div> </template>子组件能够在任意地位增加slot , 这个地位就是父组件增加内容的显示地位。 编译作用域 (父组件 在子组件<slot> </slot>处插入 data)下面咱们理解了,slot 其实就是可能让咱们在父组件中增加内容到子组件的‘空间’。咱们能够增加父组件内任意的data值,比方这样: //父组件:(援用子组件 ebutton)<template> <div class= 'app'> <ebutton> {{ parent }}</ebutton> </div></template>new Vue({ el:'.app', data:{ parent:'父组件' }})应用数据的语法齐全没有变,然而,咱们是否间接应用子组件内的数据呢?显然不行!! // 子组件 : (假如名为:ebutton)<template> <div class= 'button'> <button> </button> <slot></slot> </div></template>new Vue({ el:'.button', data:{ child:'子组件' }})// 父组件:(援用子组件 ebutton)<template> <div class= 'app'> <ebutton> {{ child }}</ebutton> </div></template>间接传入子组件内的数据是不能够的。因为:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。 ...

July 25, 2022 · 2 min · jiezi

关于vue.js:vue-使用hammerjs-完成图片的双指放大缩小及单指移动

<template> <div class="page"> <div class="demo"> <img id="demoImg" :src="require('../../assets/newimages/code.png')" class="demo-img" /> </div> </div></template><script>import Hammer from 'hammerjs'import { setTimeout } from 'timers'export default { data () { return { config: {}, id: null, mc: null, timer: false, translateX: 0, translateY: 0, scale: 1, firstTouch: true, relateX: 0, relateY: 0, oldX: 0, oldY: 0, oldScale: 1 } }, mounted () { this.$nextTick(() => { this.picInit() }) }, methods: { picInit () { this.id = document.getElementById('demoImg') this.mc = new Hammer(this.id) this.relateX = (document.body.clientWidth - this.id.offsetWidth) / 2 this.relateY = (document.body.clientHeight - this.id.offsetHeight) / 2 this.mc.add(new Hammer.Pan({ direction: Hammer.DIRECTION_ALL, threshold: 0, pointers: 0 })) this.mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith(this.mc.get('pan')) this.mc.on('hammer.input', this.isFinal) this.mc.on('panstart panmove', this.onPan) this.mc.on('pinchstart pinchmove', this.onPinch) this.setPosition() }, isFinal (ev) { if (ev.isFinal) { this.oldX = this.translateX this.oldY = this.translateY this.oldScale = this.scale } }, // 初始化图片地位及缩放 setPosition () { this.selfPosition({ translateX: this.relateX, translateY: this.relateY, scale: this.scale }) }, // 单点触发 - 利落 onPan (ev) { // console.log(this.firstTouch) if (this.firstTouch) { this.oldX = this.relateX this.oldY = this.relateY } // console.log(this.oldX) // console.log(this.oldY) this.translateX = this.oldX + ev.deltaX this.translateY = this.oldY + ev.deltaY const position = { translateX: this.translateX, translateY: this.translateY, scale: this.scale } this.selfPosition(position) this.firstTouch = false }, // 多点触发 - 缩放 onPinch (ev) { this.scale = this.oldScale * ev.scale this.selfPosition({ translateX: this.translateX, translateY: this.translateY, scale: this.scale }) // this.selfPosition(this.position) }, selfPosition (pos) { return this.picAnimate()(() => this.tempPos(pos)) }, tempPos (pos) { let style = [ `translate3d(${pos.translateX}px, ${pos.translateY}px, 0)`, `scale(${pos.scale}, ${pos.scale})` // `scale(${pos.scale > 1.2 ? 1.2 : pos.scale}, ${pos.scale > 1.2 ? 1.2 : pos.scale})` ] style = style.join(' ') this.id.style.transform = style }, picAnimate () { return window[Hammer.prefixed(window, 'requestAnimationFrame')] || function (callback) { setTimeout(callback, 1000 / 60) } } }}</script><style lang="less">.page { width: 375px; height: 100vh; background: url('../../assets/newimages/bg.png') center center no-repeat; background-size: 100% 100%;}.demo { position: absolute;}.demo-img { width: 260px; height: 260px; display: block; overflow: hidden;}</style>

July 23, 2022 · 2 min · jiezi

关于vue.js:FESRCVue数据变动引发update组件的性能问题与优化方案

背景一个音讯列表中,有个计时的数据,每秒都会更新。当应用Vue Devtool Flash Updates插件察看组件update机会时,发现不管列表中的某个子项数据是否有变动,都会导致整个列表从新update(下图)。因为update时外面子组件都会从新update,再加上每秒计时导致整个列表的性能收到影响。 冀望是如果有某个子项计时数据变动,那应该只更新某个子项,冀望如下图 排查过程方向:查看父子组件的传递中是否有其余props导致了变动,一一排除属性 后果:发现并没有属性可能影响组件自身,只剩列表数组自身,还是会触发方向:对列表到子项中的各个组件手动做useMemo解决 后果:从内到外都做了缓存,发现几十父组件缓存,其中一个数组元素变动,子组件收到的列表还是会受影响方向:查看vuex中state getters数据变动,组件内state,getters的异同,看是否会引起不必要的更新 Vuex排查后果:vuex的getters中的日期工夫数据会引起不必要的刷新,导致即便没有计时显示也会每秒update,革新成变动时再批改vuex state。能够解决问题。Vue排查后果:如果只是组件state里的数组元素的某个属性批改,是部分更新的。但如果是数组中的一个元素被批改,列表还是会整个update论断对于 Array<Object> 列表渲染: Vue.$set( state.arr , i , val ) 会触发整个列表组件的update改为Vue.$set( state.arr[i] , k , val ) 则不会对于 Vuex 的 getters : getters依赖的getters更新,不像react有useMemo机制,即便根底类型值不变也会触发updategetters依赖的state更新,根底类型值不变不会触发update

July 20, 2022 · 1 min · jiezi

关于vue.js:FESRC-Vue3-pinia-开发感受与踩坑记录

集体踩坑 vue3+pinia 1个月后整体评分:vue3上手容易 ★★★★☆ 写过react的上手更容易开发敌对 ★★★★☆ 局部插件版本独自管制 pinia上手容易 ★★★★★ 写过vuex的上手更容易开发敌对 ★★★★★ 类型申明敌对 以下记录体验新玩具的一些应用感触与一些开发中遇到的坑。 感触之前始终习惯vue class style component,当初换成 defineComponent更相似于react function component.整体开发上没有受影响pinia在 组件setup生命周期里useStore()创立间接应用,包含vue-router也用useRouter的形式,整体上手复杂度和Vuex的import大同小异。在类型申明上pinia更具劣势,可能在vue组件tsx中取得很好的类型申明提醒,前提是在pinia store中都要手动标注。特地是action的返回类型。pinia store的申明比vuex缩小了mutation的模型局部。在store建数据模型时能够更正当的形象业务模型,也防止了action-mutation能力批改state的麻烦。当然这样也有害处,原我的项目中简单的mutation相当于要独自抽离成filter之类的概念,但这种filter也不会很多,间接批改state还是比拟不便的,除非前后端的数据转换特地多。当把 attrs,slots,emit 写成setup生命周期入参时,好像又回到了angular scope依赖注入的感觉,对开发影响不大,只是每个文件多了一行而已,写法从this.$xxx改成间接写emit之类的反而更简短。如果是复制模板文件很容易遗记改defineComponent里的name参数,也就是组件名称。对业务不影响但在vue-devtool里会显示成雷同的组件名称,不容易排查。遇到的坑对 vue-svg-loader 锁版本 应用"vue-svg-loader": "0.17.0-beta.2"配置 vue.config.js如下: chainWebpack: (config) => {// vue-svg-loaderconst svgRule = config.module.rule("svg");svgRule.uses.clear();svgRule.delete("type");svgRule.delete("generator");svgRule.use("vue-loader").loader("vue-loader").end().use("vue-svg-loader").loader("vue-svg-loader");},对pinia 锁版本 应用"pinia": "^2.0.0-rc.0"vue3 和目前的浏览器插件devtool 有抵触, 须要下载beta版的vue-devtool (BUGFIX Vue.js devtools 5.0.0 beta 3 fix)如果嫌2个vue devtool切换不不便,能够自行装置vue-devtool,并配置到html模板;例如: <% if (process.env.NODE_ENV==='development') { %> <script src="http://localhost:8098"></script> <% } %>vue router 新写法匹配其余路由 { path: "/:pathMatch(.*)*", name: "NotFound", component: NotFoundPage, },css module反对: ...

July 20, 2022 · 1 min · jiezi

关于vue.js:基于-Vue3-打造前台中台通用提效解决方案完结

Download: https://www.itwangzi.cn/2624....以前始终认为,VNode 也能像 DOM 那样,依据 children 属性,将 VNode 连贯组成一棵树。但最近我发现这是不对的,VNode 不能独自组成一棵残缺的页面树。Demo咱们用一个例子来阐明一下,Vue Playground 在线预览链接[1]App.vue<script setup>import Comp from './Comp.vue'</script><template>  <h1>Hello World!</h1>  <Comp> test-slot </Comp></template>Comp.vue<template>  <h2>    slot:    <slot></slot>  </h2></template>对应的比拟残缺的树结构如下(图差不多看看就行,文章前面会有解析):红框中的内容,是理论渲染到页面的内容。那为什么不是独自将 VNode 连贯组合成树,就像下图一样:要搞清楚这个问题,咱们先来看看 VNode 是怎么创立的。VNode 的创立下图是一个 vue 单文件组件的编译后果:咱们晓得,Vue 的 template 模板最终都会编译成渲染函数,如右图,略微一看,如同一个函数都不意识,但其实将 createELementVNode 换成咱们熟知的 h 函数 ,就很好了解了。1. _createElementBlock(_Fragment, ...),创立一个 Fragment,(如果 Vue 组件内有多个根元素,会用 Fragment 包裹,以保障一个组件的根元素只有一个)2. _hoisted_1,动态的 html 内容,会被晋升到渲染函数外,这样每次调用渲染函数,就不须要从新创立 VNode(Vue 的编译优化)3. _createVNode(Comp, ...),为 Comp 组件创立 VNode。每次渲染/更新视图,都会调用一遍渲染函数,会生成新的 VNode。该组件的渲染函数,会生成如下的 VNode:从这个例子能够看出:1. 渲染函数会创立一棵 VNode 树,这阐明了 VNode 能够连贯组合成一棵树,但只是组件外部的一棵树2. VNode 树形容的是以后组件的状态:Comp 组件的 VNode 的 children 是 test slots 文本。这与 DOM 树是有区别的3. children 属性可能会用于形容非凡的嵌套关系,如:slot渲染函数是在编译时,从 Vue template 编译进去的,其 VNode 树的嵌套构造,曾经在编译时就确定下来,跟运行时无关。运行时,无非是确定一些 VNode 的值,例如某个变量的绑定、某个 slots 的值Vue 组件外部实例当两个组件的渲染函数都被执行实现之后,会有两个 VNode Tree:那么这两个 VNode Tree,是怎么关联起来的,怎么能力组合成一棵残缺的树?ComponentInternalInstance 组件实例起到了关联 VNode Tree 的作用,咱们能够通过应用 getCurrentInstance[2] 获取到组件的外部实例(getCurrentInstance 只裸露给高阶应用场景,典型的比方在库中)它们的关系如下:• 组件 VNode,可能通过 component 属性,拜访到 ComponentInternalInstance 实例• ComponentInternalInstance 实例,可能通过 subtree 属性,拜访到组件的最顶层内容的 VNode因而,咱们会失去一开始的残缺的树:总结本文用一个简略的例子,阐明了 VNode 的创立过程,是每次组件渲染/更新时,调用渲染函数创立的 VNode,VNode 树只形容以后组件的状态,其嵌套关系在编译时就曾经确认。VNode 无奈组成一个残缺的树,是因为 VNode 树之前不能间接进行连贯,children 属性不能间接用于连贯 VNode 树,因为存在一些非凡的嵌套关系(如:slot、suspense)一个组件会失去一棵 VNode 树,而 VNode 树,须要用组件外部实例进行连贯。通过组件 VNode 获取组件外部实例,而后再通过 subtree 连贯该组件的 VNode 树。如果没有引入组件的概念,那 VNode 树就是一棵残缺的树。 ...

July 18, 2022 · 1 min · jiezi

关于vue.js:关于vue2的Ref的特点

先看一下官网的解释从下面咱们能够晓得$refs会返回dom或者组件实例或者蕴含前两者的数组,然而有个问题就是一个组件页面里有多个同名的ref,Vue会怎么解决呢?这里我要分出两种状况非v-for渲染的一般元素或组件不是v-for渲染进去的同名ref,论断是 以dom构造为主先是从上到下,而后是从里层到外层找到最初一个对应名称的ref,如果是原生dom返回dom元素自身,如果是组件返回组件实例。看上面的例子。template result 最初发现是拿到了<HelloWorld>组件的实例,如果删除 <HelloWorld> 上的ref 就会拿到 它的里层的 ref="one" 的 div 因为它是最上面的同名ref,这里有个留神的中央就是插槽必须失效能力拿到插槽中的元素或组件,不然会拿到外层的<HelloWorld>组件。v-for渲染的一般元素或组件第二种状况就是应用v-for渲染了多个同名ref的元素,首先咱们要把v-for看做一个整体,他同样会遵循第一条的规定,比方在v-for的元素同级上面存在一个同名ref标签或者组件,$refs就会拿到上面的dom或者组件实例,如果返过去就是拿到v-for的元素数组。而后咱们看只有v-for的状况,下面说到数组,当咱们用 ref 去取 v-for 中的元素的时候返回的是数组不论外面是不是有没有多个同名ref元素,看下图。template result 当v-for中没有重名ref时会返回只有一条数据的数组,所以只有获取v-for中的ref就必然会拿到一个数组,所以在解决数据的时候要留神。看下图,咱们应用index动静渲染ref保障了ref的惟一。template result 接下来讨论一下v-for ref获取的元素的程序问题,咱们来看呈现多层级同名ref的状况,简略来说 从里到外,从上到下。看下图。template result 再来看看v-for 蕴含组件的时候是否有什么区别,看下图。template result 能够看到也是遵循上述规定的。

July 18, 2022 · 1 min · jiezi

关于vue.js:基于多数据源零代码同时生成多个数据库CRUD增删改查RESTful-API接口

多数据源回顾通过后面文章的介绍,目前曾经反对支流数据库,包含MySql,PostgreSql,Oracle,Microsoft SQL Server等,通过配置零代码实现了CRUD增删改查RESTful API。采纳形象工厂设计模式,能够无缝切换不同类型的数据库。然而如果须要同时反对不同类型的数据库,如何通过配置进行治理呢?这时候引入多数据源性能就很有必要了。 简介利用spring boot多数据源性能,能够同时反对不同类型数据库mysql,oracle,postsql,sql server等,以及雷同类型数据库不同的schema。零代码同时生成不同类型数据库增删改查RESTful api,且反对同一接口中跨库数据拜访二次开发。 UI界面配置一个数据源,多个从数据源,每一个数据源互相独立配置和拜访。 外围原理配置数据库连贯串配置application.properties,spring.datasource为默认主数据源,spring.datasource.hikari.data-sources[]数组为从数据源 #primaryspring.datasource.driverClassName=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/crudapi?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=truespring.datasource.username=rootspring.datasource.password=root#postgresqlspring.datasource.hikari.data-sources[0].postgresql.driverClassName=org.postgresql.Driverspring.datasource.hikari.data-sources[0].postgresql.url=jdbc:postgresql://localhost:5432/crudapispring.datasource.hikari.data-sources[0].postgresql.username=postgresspring.datasource.hikari.data-sources[0].postgresql.password=postgres#sqlserverspring.datasource.hikari.data-sources[1].sqlserver.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriverspring.datasource.hikari.data-sources[1].sqlserver.url=jdbc:sqlserver://localhost:1433;SelectMethod=cursor;DatabaseName=crudapispring.datasource.hikari.data-sources[1].sqlserver.username=saspring.datasource.hikari.data-sources[1].sqlserver.password=Mssql1433#oraclespring.datasource.hikari.data-sources[2].oracle.url=jdbc:oracle:thin:@//localhost:1521/XEPDB1spring.datasource.hikari.data-sources[2].oracle.driverClassName=oracle.jdbc.OracleDriverspring.datasource.hikari.data-sources[2].oracle.username=crudapispring.datasource.hikari.data-sources[2].oracle.password=crudapi#mysqlspring.datasource.hikari.data-sources[3].mysql.driverClassName=com.mysql.cj.jdbc.Driverspring.datasource.hikari.data-sources[3].mysql.url=jdbc:mysql://localhost:3306/crudapi2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=truespring.datasource.hikari.data-sources[3].mysql.username=rootspring.datasource.hikari.data-sources[3].mysql.password=root动静数据源——DynamicDataSourceSpring boot提供了抽象类AbstractRoutingDataSource,复写接口determineCurrentLookupKey, 能够在执行查问之前,设置应用的数据源,从而实现动静切换数据源。 public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); }}数据源Context——DataSourceContextHolder默认主数据源名称为datasource,从数据源名称保留在ThreadLocal变量CONTEXT_HOLDER外面,ThreadLocal叫做线程变量, 意思是ThreadLocal中填充的变量属于以后线程, 该变量对其余线程而言是隔离的, 也就是说该变量是以后线程独有的变量。 在RestController外面依据须要提前设置好以后须要拜访的数据源key,即调用setDataSource办法,拜访数据的时候调用getDataSource办法获取到数据源key,最终传递给DynamicDataSource。 public class DataSourceContextHolder { //默认数据源primary=dataSource private static final String DEFAULT_DATASOURCE = "dataSource"; //保留线程连贯的数据源 private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); private static final ThreadLocal<String> HEADER_HOLDER = new ThreadLocal<>(); public static String getDataSource() { String dataSoure = CONTEXT_HOLDER.get(); if (dataSoure != null) { return dataSoure; } else { return DEFAULT_DATASOURCE; } } public static void setDataSource(String key) { if ("primary".equals(key)) { key = DEFAULT_DATASOURCE; } CONTEXT_HOLDER.set(key); } public static void cleanDataSource() { CONTEXT_HOLDER.remove(); } public static void setHeaderDataSource(String key) { HEADER_HOLDER.set(key); } public static String getHeaderDataSource() { String dataSoure = HEADER_HOLDER.get(); if (dataSoure != null) { return dataSoure; } else { return DEFAULT_DATASOURCE; } }}动静数据库提供者——DynamicDataSourceProvider程序启动时候,读取配置文件application.properties中数据源信息,构建DataSource并通过接口setTargetDataSources设置从数据源。数据源的key和DataSourceContextHolder中key一一对应 ...

July 16, 2022 · 4 min · jiezi

关于vue.js:基于crudapi后端Java-SDK二次开发之API认证和鉴权二

基于crudapi后端Java SDK二次开发之API认证和鉴权(二)回顾通过上一篇文章 基于crudapi后端Java SDK二次开发之环境搭建(一)的介绍,后盾API曾经搭建实现。RBAC权限模型中介绍了用户和权限相干内容,本文次要介绍API集成中认证和鉴权相干内容。 背景理论我的项目中,为了保障数据安全,API须要认证才能够拜访,本文次要介绍三种API认证形式,基于Spring Security框架实现, 包含Cookie,Basic Auth,JWT令牌Token。 Swagger api文档https://demo.crudapi.cn/swagger-ui.html 默认用户名明码: superadmin/1234567890Cookie登录api登录胜利后,浏览器主动解决cookie并辨认登录状态,适宜web拜访场景,方便快捷! POST https://demo.crudapi.cn/api/auth/loginaccept: application/jsoncontent-type: application/x-www-form-urlencodedusername: superadminpassword: 1234567890JWT令牌Token登录胜利后,记录TOKEN,每次发送申请之前,设置一下即可,后盾会解析TOKEN并辨认用户,并判断是否具备权限,适宜手机挪动端拜访场景,有效期比cookie长! 登录apiPOST https://demo.crudapi.cn/api/auth/jwt/loginaccept: application/jsoncontent-type: application/x-www-form-urlencodedusername: superadminpassword: 1234567890 获取JWT Token从申请返回的头外面获取token字段内容,格局为Bearer XXXXX 设置JWT token设置Type为Bearer TokenBearer XXXXX和XXXX两种格局都能够,后盾自动识别 查看Authorization申请头Authorization字段自动识别为:Bearer XXXXX 根本认证Basic Auth间接采纳户名和明码的形式,适宜任何简略解决的场景,要注意安全问题。 设置设置Type为Basic Auth输出用户名明码即可 查看Authorization申请头Authorization字段自动识别为: Basic c3VwZXJhZG1pbjoxMjM0NTY3ODkw,其中c3VwZXJhZG1pbjoxMjM0NTY3ODkw为superadmin:1234567890的Base64编码。 其它接口登记登录GET https://demo.crudapi.cn/api/auth/logout 创立用户接口POST https://demo.crudapi.cn/api/business/user { "name": "testuser", "username": "testuser", "password": "testuser", "enabled": true, "accountNonExpired": true, "accountNonLocked": true, "credentialsNonExpired": true, "roleLines": [{ "name": "业务数据角色", "role": { "id": 20, "name": "业务数据角色", }, "roleId": 20 }]}curl示例查问序列号curl -u 'superadmin:1234567890' -X GET -H 'Content-Type: application/json' 'https://demo.crudapi.cn/api/metadata/sequences/1'curl -H 'Authorization:Basic c3VwZXJhZG1pbjoxMjM0NTY3ODkw' -X GET -H 'Content-Type: application/json' 'https://demo.crudapi.cn/api/metadata/sequences/1'查问序列号curl -u 'superadmin:1234567890' -X POST -H 'Content-Type: application/json' -d '{"currentTime":false,"sequenceType":"STRING","minValue":1,"maxValue":999999999,"nextValue":1,"incrementBy":1,"name":"orderCode","caption":"订单流水号","format":"SO_%9d"}' 'https://demo.crudapi.cn/api/metadata/sequences'导入EXCEL数据curl -u 'superadmin:1234567890' -F "file=@product.xlsx" "https://demo.crudapi.cn/api/business/product/import"小结本文次要介绍了API集成三种形式,在理论利用中,依据具体业务场景抉择最佳形式即可! ...

July 16, 2022 · 1 min · jiezi

关于vue.js:vue3-解决-ESLint-各类型错误

后期我的项目中疏忽了 eslint 查看,导致一运行 npm run lint 呈现两千多条谬误(; ) 造孽啊花了两三天搞完,做个谬误汇总。<!-- more --> 环境和配置我的项目用 vue@3.2 + vite + ant-design@6.0对于eslint 配置的用法可参考:ESLint中文eslint 有专门利用于 vue 的插件:eslint-plugin-vue大抵贴一下版本依赖 devDependencies: { "@babel/eslint-parser": "^7.18.2", "eslint": "^8.7.0", "eslint-config-prettier": "^8.3.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^25.7.0", "eslint-plugin-vue": "^8.3.0",}eslint 的配置采纳 JS 文件格式,通过几次批改曾经忘了一开始的内容,只贴根底配置如下: // .eslintrc.jsmodule.exports = { // 只作用于当前目录 root: true, // 运行环境 env: { node: true, es6: true, }, // 解析器 parser: '@babel/eslint-parser', // 解析器选项 parserOptions: { sourceType: 'module', }, // 插件 plugins: ['import'], // 扩大配置 extends: [ 'plugin:vue/vue3-recommended', 'plugin:import/recommended', 'prettier', ], // 启用规定 rules: {}, // 全局变量 globals: { h: true, }, // 为指定文件指定处理器 overrides: [ { files: ['*.vue', '*.jsx'], parser: 'vue-eslint-parser', parserOptions: { ecmaVersion: 2018, }, rules: {} } ],}ERROR: Parsing error: Unexpected token .错误代码: ...

July 15, 2022 · 2 min · jiezi

关于vue.js:wallysQCA9882Access-Point-Wireless-ACAN-MiniPCIE-Card

Access Point Wireless Module Wireless AC/AN MiniPCIE Standard Card802.11AC 802.11AN wifi QCA9882 2x 2 5G High power Radio card DR882 https://www.wallystech.com/Ne... Wallys Communications (SuZhou) Co., Ltd., http://www.wallystech.com,which is a professional supplier specializing in product design, manufacturing and offering superior OEM/ODM/JDM services in wireless communications. As a specialized manufacturer and exporter for these products in China,We sincerely hope to establish business relations with your esteemed corporation.  MT7915/MT7975/IPQ6000/IPQ6018/IPQ6010/IPQ4019/IPQ4029/ipq4018/IPQ4028/IPQ8072/IPQ8072A/IPQ8074/IPQ8074A/IPQ9074/QCN9074/QCN9072/QCN9024/IPQ5018/AR9223/QCA9880/QCA9882 /AR9582/AR9531/AR9344 BY:Wallys Communications (Suzhou ) Co., LTDEMAIL:sales3@wallystech.com Features Featuring with industrial-grade Atheros’s QCA9882 chipsetIntegrated with 2x 2 5G high power Radio Card4.940GHz to 5.825GHz Frequency Range2 x 5G MMCX Connectors20MHz/40MHz/80MHz BandwidthSupport 11AC/ANRoHS compliance ensure a high level protection of human health and the environment from risks that can be posed by chemicalsApplications ...

July 14, 2022 · 2 min · jiezi

关于vue.js:工作问题ms记录

一、级联抉择最初一级是空的起因:最初一级hcildren为空数组,应该是空的或者undefined解决:formatData(data) { for (var i = 0; i < data.length; i++) { if (data[i].children.length < 1) { data[i].children = undefined } else { this.formatData(data[i].children) } } return data },

July 14, 2022 · 1 min · jiezi

关于vue.js:vue2-vite-配置-tailwindcss-不生效

依照网上的办法配置,包含官网的 vue3 + vite 配置 tailwind 文档。 配置良久都没有成果,编译进去的文件是这样的 能够看到款式并没有编译进去。 通过各种探讨,参考vue2配置的tailwind,发现有人须要独自在 vue.config.js加一个配置 // vue.config.jsmodule.exports = { css: { loaderOptions: { postcss: { plugins: [require('tailwindcss'), require('autoprefixer')] } } } }抱着试试看的态度,在 vite.config.js 加上对应配置 css: { postcss: { plugins: [ require('tailwindcss'), require('autoprefixer') ] }}完满解决!!~ 十分nice~

July 13, 2022 · 1 min · jiezi

关于vue.js:vue3的diff算法

一、可能性(常见):1. 旧的:a b c新的:a b c d2. 旧的: a b c新的:d a b c3. 旧的:a b c d新的:a b c4. 旧的:d a b c新的: a b c5. 旧的:a b c d e i f g新的:a b e c d h f g对应的实在虚构节点(为不便了解,文中用字母代替): // vnode对象const a = { type: 'div', // 标签 props: {style: {color: 'red'}}, // 属性 children: [], // 子元素 key: 'key1', // key el: '<div style="color: 'red'"></div>', // 实在dom节点 ...}二、找法则去掉后面和前面雷同的局部 ...

July 13, 2022 · 7 min · jiezi

关于vue.js:vue3x新特性

1. 组合式api代替选项式api, 作为组合式api的入口,拆散逻辑关注点,将同一个逻辑关注点的代码收集在一起另外,还提供了逻辑复用计划,代替Vue2.x Mixin(命名抵触隐患,不同组件间配置化应用不够灵便)应用 import dataGet from './dataGet.js';import dataDelete from './dataDelete.js';import dataUpdate from './dataUpdate.js';export default { props: { id: String, }, // 组件创立之前执行,this实例无法访问 setup(props, context) { // 解构props, 获取对id的响应式援用 const { id } = toRefs(props); const { xxx } = dataGet(); const { xxx } = dataDelete(); const { xxx } = dataUpdate(); /* something others */ // 返回的内容会裸露给组件的其余部分包含模板 return { xxx }; // 或者返回一个渲染函数, 此时u须要应用expose裸露组件外部的办法/属性以供父组件拜访 context.expose({ property }); return () => h('div', 'test composite-api'); }}// SFC中应用组合式api的语法糖// 代码预编译为setup函数的内容<script setup>......// defineProps, defineEmits// 不反对渲染函数......</script>setup中应用依赖注入 ...

July 12, 2022 · 2 min · jiezi

关于vue.js:如何做前端工程化

如何做"前端工程化"?前端工程化就是为了让前端开发可能“自成体系”,集体认为次要应该从模块化、组件化、规范化、自动化四个方面思考。 一、模块化 1、JS的模块化 拆分、封装大块的业务逻辑,抽离公共函数等2、CSS的模块化 尽管SASS、LESS、Stylus等预处理器实现了CSS的文件拆分,但没有解决CSS模块化的一个重要问题:选择器的全局净化问题。按情理,一个模块化的文件应该要暗藏外部作用域,只裸露大量接口给使用者。而依照目前预处理器的形式,导入一个CSS模块后,已存在的款式有被笼罩的危险。尽管重写款式是CSS的一个劣势,但这并不利于多人合作。3、资源的模块化 资源模块化后,依赖关系单一化。所有CSS和图片等资源的依赖关系对立走JS路线,无需额定解决CSS预处理器的依赖关系,也不需解决代码迁徙时的图片合并、字体图片等门路问题;资源解决集成化。当初能够用loader对各种资源做各种事件,比方简单的vue-loader等等;我的项目构造清晰化。应用Webpack后,你的我的项目构造总能够示意成这样的函数: dest = webpack(src, config)二、组件化 1、从UI拆分下来的每个蕴含模板(HTML)+款式(CSS)+逻辑(JS)性能齐备的结构单元,咱们称之为组件。2、组件化≠模块化。模块化只是在文件层面上,对代码或资源的拆分;而组件化是在设计层面上,对UI(用户界面)的拆分三、规范化 规范化其实是工程化中很重要的一个局部,我的项目初期标准制订的好坏会间接影响到前期的开发品质。比方:1、目录构造的制订2、目录构造的正当设定,能为我的项目带来很多长处: 有助于进步我的项目的逻辑构造合理性; 对应扩大和单干; 不便资源的对立定位治理。3、编码标准 制订一套良好的编码标准能够加强团队开发合作、进步代码品质。 举荐参考凹凸实验室打造的前端代码标准。 编码标准包含: 1、HTML标准。 基于 W3C、苹果开发者 等官网文档,并联合团队业务和开发过程中总结的标准约定,让页面HTML代码更具语义性。 2、CSS标准。 对立标准团队 CSS 代码书写格调和应用 CSS 预编译语言语法格调,提供罕用媒体查问语句和浏览器公有属性援用,并从业务层面对立标准罕用模块的援用。 3、JS标准。 对立标准团队 CSS 代码书写格调和应用 CSS 预编译语言语法格调,提供罕用媒体查问语句和浏览器公有属性援用,并从业务层面对立标准罕用模块的援用。 4、图片标准。 理解各种图片格式个性,依据个性制订图片标准,包含但不限于图片的品质约定、图片引入形式、图片合并解决等,旨在从图片层面优化页面性能。 5、命名标准。 从 目录、图片、HTML/CSS文件、ClassName 的命名等层面约定标准团队的命名习惯,加强团队代码的可读性。 6、前后端接口标准 “基于 Ajax 带来的 SPA 时代”,这种模式下,前后端的分工十分清晰,前后端的要害合作点是 Ajax 接口,引发一个重要问题:前后端的对接界面单方却关注甚少,没有任何接口约定标准状况下各自撸起袖子就是干,导致咱们在产品我的项目开发过程中,前后端的接口联调对接工作量占比在30%-50%左右,甚至会更高。往往前后端接口联调对接及零碎间的联调对接都是整个产品我的项目研发的软肋。 接口标准次要初衷就是标准约定后行,尽量避免沟通联调产生的不必要的问题,让大家身心愉快地专一于各自善于的畛域。 7、文档标准 8、组件治理 9、git分支治理 10、commit形容标准 11、视觉图标标准四、自动化 前言:前端工程化的很多脏活累活都应该交给自动化工具来实现。须要秉持的一个理念是: 任何简单机械的重复劳动都应该让机器去实现。1、继续继承2、自动化构建3、自动化部署4、自动化测试以上就是对于怎么去做前端工程化的一些具体参考

July 12, 2022 · 1 min · jiezi

关于vue.js:Vue面试题归总

Vue的长处说说你对SPA单页面的了解,它的优缺点别离是什么?SPA首屏加载速度慢的怎么解决?Vue初始化过程中(new Vue(options))都做了什么?对MVVM的了解?Vue数据双向绑定原理Vue的响应式原理Vue3.x响应式数据原理Vue3.0 里为什么要用 Proxy API代替 defineProperty API?Proxy 与 Object.defineProperty 优劣比照vue中组件的data为什么是一个函数?而new Vue 实例里,data 能够间接是一个对象vue中data的属性能够和methods中办法同名吗,为什么?vue中created与mounted区别Vue中computed与method的区别虚构DOM中key的作用用index作为key可能会引发的问题Vue中watch用法详解vue中对mixins的了解和应用vue中的插槽为什么vue采纳异步渲染Vue 的异步更新机制是如何实现的?$nextTick的了解Vue中罕用的一些指令vue的自定义指令你有写过自定义指令吗?自定义指令的利用场景有哪些?v-show和v-if指令的共同点和不同点为什么防止v-if和v-for一起应用vue为什么在 HTML 中监听事件?Vue.set 扭转数组和对象中的属性vm.$set(obj, key, val) 做了什么?说说vue的生命周期的了解第一次页面加载会触发哪几个钩子?Vue组件通信有哪些形式router和route的区别vue-router有几种钩子函数?vue-router路由跳转形式vue-router路由传参keep-alive理解吗Vuex是什么?怎么应用?什么状况下应用 Vuex?Vuex和单纯的全局对象有什么区别?为什么 Vuex 的 mutation 中不能做异步操作?axios 是什么,其特点和罕用语法对SSR有理解吗,它次要解决什么问题?Vue要做权限治理该怎么做?管制到按钮级别的权限怎么做?Vue我的项目前端开发环境申请服务器接口跨域问题做过哪些Vue的性能优化?Vue3有理解过吗?能说说跟Vue2的区别吗?Vue 3.0 所采纳的 Composition Api 与 Vue 2.x应用的Options Api 有什么区别?element-ui中遇到的问题 Vue的长处轻量级框架:只关注视图层,是一个构建数据的视图汇合,大小只有几十 kb ;简略易学:国人开发,中文文档,不存在语言障碍 ,易于了解和学习;双向数据绑定:保留了 angular 的特点,在数据操作方面更为简略;组件化:保留了 react 的长处,实现了 html 的封装和重用,在构建单页面利用方面有着独特的劣势;视图,数据,构造拆散:使数据的更改更为简略,不须要进行逻辑代码的批改,只须要操作数据就能实现相干操作;虚构DOM:dom 操作是十分消耗性能的, 不再应用原生的 dom 操作节点,极大解放 dom 操作,但具体操作的还是 dom 不过是换了另一种形式;运行速度更快:相比拟于 react 而言,同样是操作虚构 dom ,就性能而言, vue 存在很大的劣势。 说说你对SPA单页面的了解,它的优缺点别离是什么?是什么 SPA( single page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载实现,SPA 不会因为用户的操作而进行页面的从新加载或跳转而页面的变动是利用路由机制实现 HTML 内容的变换,防止页面的从新加载。长处 ...

July 12, 2022 · 4 min · jiezi

关于vue.js:vue设计与实现中的cleanup

在vue设计与实现这本书中有一个这样的例子,如下 const data = { ok: true, text: "hello world}const obj = new Proxy(data, {...})effect(() => { document.body.innerHtml = obj.ok ? obj.text : "not";})在这块代码当中,当obj.ok为true,就会将document.body的值赋值为obj.text的值,也就是说这会触发两个get,别离是obj.ok和obj.text。 当咱们将obj.ok赋值为false的时候,会触发这个函数,然而不会进行obj.text的get操作。 咱们想要的是当obj.text发生变化的时候,是不会触发这个函数的,然而当初text的deps也对这个函数进行依赖收集,这个时候就须要用到cleanup函数了 cleanup函数的实现思维咱们能够在副作用函数执行的时候,将它从与之相干的依赖汇合中进行删除,当副作用函数执行实现后再进行依赖收集 首先,须要将effect函数和track函数进行一些革新 function effect(fn) { const effectFn = () =>{ cleanup(effectFn) activeEffect = effectFn fn() } effectFn.deps = [] effectFn()}function track(target, key) { if(!activeEffect) return let depsMap = bucket.get(target) if(!depsMap) { bucket.set(target, (depsMap = new Map)) } let deps = depsMap.get(key) if(!deps) { depsMap.set(key, (deps = new Set())) } deps.add(activeEffect) // 将与effecFn相关联的依赖汇合退出到effectFn的deps当中 activeEffect.deps.push(deps)}在这块代码当中,effectFn函数执行时会进行cleanup操作,再进行fn的执行 ...

July 11, 2022 · 1 min · jiezi

关于vue.js:打造拉动式企业培训管理方案释放人才潜能

文/况育军、顿唯、谢昕辰 编辑/麦壁瑜 前言在当今社会,人才的造就对企业来说至关重要,而人才的造就又很大水平上依赖于企业的培训治理。但企业培训是一个双向交互的过程,不单是管理者两厢情愿地灌输知识就行,更重要的是让员工可能踊跃地承受和吸纳。 很多管理者苦于没有好的培训治理计划,或是有很多创意但囿于不足能够落地的工具。那么,明天就给大家介绍如何用明道云这套零代码工具,去落地一套生动有趣的企业培训治理计划。 传统培训的特点传统的企业培训大多是自上而下的这么一种形式,由企业的下级管理者发动并组织,上级的员工被动地承受。从员工的角度看,培训对于他们来说更像是“ I Have To”的一项工作,而不是“I Love To”的一场派对,员工达成培训工作后也没有太多成就感。 在培训内容上,传统培训很少关注员工的个性化需要,内容大多干燥而古老。有些企业甚至间断几年都是沿用同一套培训课程,更新迭代速度太慢。如果培训既不能满足员工的主观诉求,对他们日常工作的裨益又非常无限,那么员工自然而然地就对参加培训没有太多积极性,培训流于形式,员工草草了事。在培训气氛上,也不足趣味性和互动性,培训讲师和学员的互动大多局限于课上,在课前和课后的交换互动比拟匮乏。而课上因为要实现既定的课程进度,留给大家互动探讨的工夫也不会太多。 新培训计划的特点鉴于传统培训形式的种种不足之处,咱们想了一种新的培训计划。新的培训计划要可能关注到员工的个性化诉求,同时要有肯定的趣味性才不显得那么干燥,并且要兼具互动性,可能让培训人和学员之间有足够的互动交换和意见反馈。咱们总结下来,新的培训计划的特点能够概括为以下几个属性——自驱属性、社交属性、楷模属性。 自驱属性 和传统自上而下的培训形式不同,新的培训计划更重视施展员工的自驱性,培训课程的设计思路来源于底层员工的需要反馈和意见,而不只是下级管理者独立决策。用供应链的实践来看,传统培训更像是“推动式供应链”,以制造商(管理者)为外围,产品生产进去后逐渐推向客户(员工);而新的培训形式则更偏向于“拉动式供应链”,以客户需要为导向来决定产品的生产。 图;员工培训需要建设咱们设计了一个需要池,所有的员工都能够在这里提交对于培训治理的需要意见,这个设计借鉴了产品治理利用的设计思路。不论是To C端还是To B端的产品,必然是要以终端用户的需要为导向,而不是产品设计者闭门造车就行。所以产品的新功能设计研发之前,都会有用户需要的收集、需要的评审、设计工作的调配、研发资源的排期等过程。 这样的设计思路其实同样能够用在企业培训课程的设计研发上。员工依据本身理论状况剖析本人有哪些有余的中央,有哪些常识和能力须要通过系统化培训来补齐,并提出个性化的需要。管理者收集员工的需要,综合思考需要的价值、需要的普遍性、可行性、实现老本等因素,来做出培训课程设计的决策。 图:员工培训需要池如上图所示,员工能够在需要池这里提交和治理本人的需要,并可能查看到其余共事提交的需要,也能十分清晰地晓得本人反馈的需要是否被驳回,已驳回的需要到哪一步了,是还未开始设计课程、还是正在设计中,以及课程预计公布的日期。 在这里,信息真正做到了公开通明。员工不再只是被动承受,而是积极主动地推动甚至主导培训课程的设计研发。当本人提交的需要被驳回并造成了培训课程,员工的成就感会更大,承受培训课程的志愿度更高,同时也激励员工一直地检视本身,并积极参与到企业培训的建设中去,充分发挥“正向飞轮”的威力。 社交属性 大家平时用得最多的社交软件是什么?兴许是微信的朋友圈,兴许是微博的动静,这些社交产品都具备两个性能——点赞和评论,这两个性能曾经可能满足人们绝大部分的社交需要了。借助明道云,在面向外部员工的培训治理利用里,咱们一样可能实现点赞加评论的性能,为企业培训治理赋予社交的属性。 图:可点赞和评论的企业“朋友圈”通过明道云的自定义动作,咱们可能很轻松地做出点赞的按钮,这个按钮背地甚至能够触发后盾的工作流去实现更加丰盛多样的自动化操作,比方被点赞后的告诉揭示,点赞数量和点赞人的自动更新。 每一条记录的右侧,是明道云自带的IM(Instant Messaging)即时聊天性能,这里就像社交软件的评论区一样,能够聊天评论,还能通过表情、图片、文件等多种形式来纵情表达意见。 除了上图展现的需要之外,员工同样能够对每一个课程、每一次考核进行探讨,在培训课程设计阶段、开始之前、完结之后都能够进行探讨和反馈,不再局限于课堂。 图:被点赞后的音讯揭示楷模属性 每一个初入职场的小白,或者都已经膜拜公司内的优良前辈,神往过将来本人也能成为像他们一样的大佬,但在理论工作咱们可能没有太多机会去理解他们。可能咱们会比拟好奇,大佬们是如何成为大佬的?他们都把握了什么样的技能?我和大佬的差距有多大? 借助明道云,咱们能够更全面地理解优良的公司前辈们都有什么技能,对楷模的画像更加直观而具体。 在员工名录下,咱们能够直观地看到这个员工以后把握的技能有哪些,每个技能的精通水平达到了什么段位;金字塔顶尖的大佬们,兴许早已把每一个技能点都升到了满级。对于这样的楷模,咱们由衷地拜服,却不会因而而泄气,因为咱们与楷模的差距不再是不可逾越的天堑,而是有了能够被复制的成长门路。如果临时做不到满级,就先从点亮一星做起,后方已有指路明灯,纵然道阻且长,咱们却走得不迷茫。 图:进击的“职场小白”俯视星空的同时,还需好高鹜远,小白们要把握更多技能,培训课程少不了。在技能库大全里,咱们列举了丰盛的技能库,培训讲师们能够在每项技能下建设培训课程,员工们能够对照着技能库查漏补缺,抉择加入本人感兴趣的课程。 图:丰盛的技能库参加完课程并通过考核之后,咱们就能够点亮这项技能了,并且能够通过参加更多的课程和考核,来一直晋升技能等级,每一次点亮星星,都离大佬更近了一步! 图:满级“六边形兵士”的技能图谱 后记本文给大家介绍的培训治理计划,更多的是抛砖引玉,置信在宽广的企业管理者人群之中,并不不足翻新的想法和先进的治理理念。如果你也有许多离奇的创意,但还没有找到一款适合的套件产品来施展,又不想付出太多的试错老本去定制开发,那无妨尝试一下明道云。疾速实现简略易学,不会写代码的业务人员也能上手搭建,do it youself,所有皆有可能!

July 11, 2022 · 1 min · jiezi

关于vue.js:序列帧展示车性能优化

实现思路用一个板作为车模型展现,在板上绑定材质,36张图片对应一个纹理;鼠标滑动车时,进行更换纹理,比间接更换纹理的图片成果更好,毛病是会创立大量的纹理,对性能要求较高;在苹果手机上呈现解体的状况,局部机型会呈现手机卡崩卡黑屏的状况。解决思路是将图片进行压缩,从20M左右压缩到5M左右,图片从品质、大小两个角度进行优化。手机卡崩的状况缩小! 查看代码有大量反复纹理,通过建设缓存进行缩小缓存,通过上述形式实现挪动端兼容

July 8, 2022 · 1 min · jiezi

关于vue.js:2D组件垂直水平对齐

垂直、程度对齐业务逻辑组件不可拖拽,只能设置宽高开启填充后,每次批改参数时须要进行遍历以后组的子元素进行计算

July 8, 2022 · 1 min · jiezi

关于vue.js:vuex-的那些事

vuex 是专门为vue.js 服务的状态治理库(本人的了解就是:为vue框架存储全局的数据,能够在各个部分组件内应用) 特点: 响应性: store 中状态扭转, 在组件中也能高效更新扭转store状态惟一门路, 通过 mutation 中提交commit,dispatch 办法是调异步函数的外围: state 繁多状态树——对象: 每个模块能够领有本人的状态树对象//在store 中定义const state = { count: 0, name: ""}mapState 辅助函数:把store中的多个状态映射到部分组件内的计算属性 import { mapState } from 'vuex'computed: { //应用对象开展运算符将此状态混入到部分组件中 ...mapState(['name']), //名称不变 ...mapState({firstName: 'name'}]), //起个别名 }getter:状态计算属性对象。 若须要应用store中的多个状态进行计算,失去的值利用到多个组件中, 就能够应用getter, getters能够说成是store 的计算属性在store中定义 const getters = { // 以属性的办法定义 doCount1: (state, getters)=> { return "hello " + state.name } // 以返回带参数的函数办法定义, doCount2 :(state, getters)=>(id)=> { return "hello " + state.name + "your id is number " + id }}拜访时应用也略有不同属性拜访: this.$store.getters.doCount1办法拜访: this.$store.getters.doCount2(id)mapGetters 辅助函数, 把store中的getters 映射到部分组件内的计算属性 ...

July 8, 2022 · 2 min · jiezi

关于vue.js:vue3varlet安装报错找不到unpluginvuecomponentsvite

老手在应用vue3创立我的项目后,依照文档Vite引入varlet后。运行报错: Error: Cannot find module 'unplugin-vue-components/vite' 去百度搜寻后,发现有两种解决方案:1.删除node_modules文件夹后,重新安装modules模块2.降级本地node版本至14以上 然而,依照以上两种形式操作后还是报错。 这时候你能够重新安装下这个模块unplugin-vue-componentsnpm i unplugin-vue-components -D

July 7, 2022 · 1 min · jiezi

关于vue.js:写了一个密码输入框pc

次要思路: 应用 input 用于输出,宽高刚好等于六个明码框就行应用六个 div 用于展现输出的明码应用 * 号代替也可每个明码块里写一个用于展现提示符的 div 依据 place 匹配提示符应该呈现的格子input 的层级须要高于六个明码展现块,设置 opacity: 0; 把 input 暗藏起来,这样用户就看不到 input 框,点击明码块的时候其实是点击的 input 框进行输出明码块遍历明码数组做对应的展现就行代码如下: <template> <div class="salary-container"> <div class="dialog"> <div class="box-border"> <div class="box"> <input class="int" v-model="name" maxlength="6" type="text"> <div class="enter" v-for="(item, index) in password"> <!-- 跳跃的批示符 --> <div :class="{'tips': place == index}"></div> {{item}} </div> </div> </div> </div> </div></template><script>export default { data() { return { name: '', place: 0, password: ['','','','','',''] } }, created() { this.init() }, watch: { name(newV){ let arr = newV.split('') //转为数组 if(arr.length > 6){ arr = arr.splice(0, 6) //只取六位 } this.place = arr.length //更新批示符的地位 this.password = ['','','','','',''] arr.map((item, index) => { this.password[index] = item }) } },}</script><style lang="scss" scoped>@keyframes fade { //闪动的动画 from { opacity: 1.0; } 50% { opacity: 0.2; } to { opacity: 1.0; }}.dialog{ width: 100vw; height: 100vh; z-index: 999; background-color: rgba(0,0,0, .6); display: flex; justify-content: center; align-items: center; .box-border{ width: 500px; height: 200px; background-color: #fff; border: 1px solid #fff; box-shadow: 0px 0px 10px rgba(0,0,0, .6) inset; display: flex; justify-content: center; align-items: center; .box{ position: relative; display: flex; .enter{ width: 50px; height: 50px; border: 1px solid #000; margin: 0 5px 0 0; z-index: 100; display: flex; font-size: 30px; justify-content: center; align-items: center; //跳跃的批示符 .tips{ height: 30px; width: 1px; background-color: #000; animation: fade 1000ms infinite; } } .int{ border: none; display: inline-block; width: 100%; height: 50px; position: absolute; z-index: 999; opacity: 0; } } }}.salary-container{ padding:30px;}</style>效果图: ...

July 6, 2022 · 2 min · jiezi

关于vue.js:js对已选数据进行范围过滤

记录一次 @边城大佬 帮我解决的难题。感激大佬,以下是大佬的主页,大家能够关注下。https://segmentfault.com/u/ja...我的项目场景:类型蕴含全年和节令,节令是能够配置的,不论抉择年还是节令,都能够新增需要响应数据,弹窗中有两个下拉框,起始时刻和终止时刻,范畴是0-23,抉择一条信息填写完能够点击保留,点击页面中新增能够关上弹窗从新减少信息遇到问题: 第一次增加数据:如果起始工夫抉择3,终止工夫抉择6进行保留,下次新增时3-6是不能抉择,而且新抉择的工夫范畴不能蕴含3-6,比方新抉择的起始工夫是1,终止工夫是9,因为1-9蕴含了3-6的局部,所以不能抉择,然而0-3能够抉择,7-10能够抉择。 第二次增加数据:如果起始工夫抉择9,终止工夫抉择14进行保留,这时新增的全副数据是: hasAddData = [ {startTime: 3, endTime: 6, upValue: 1,downValue: 0.3}, {startTime: 9, endTime: 14, upValue: 0.5,downValue: 0.8},]下次新增时,只能抉择的数据列表为: [0,1,2],[7,8],[15,16,17,18,19,20,21,22,23],如果起始时刻抉择0,终止时刻只能是0、1或者2,如果起始时刻抉择是7,终止时刻只能是8。 以此类推,写出起始时刻的下拉框数据和终止时刻的下拉框数据。弹窗图示: 以下是实现逻辑: <template> <div class="addTime"> <el-dialog title="新增需要响应时段" :visible.sync="showAddDialog.visible" :close-on-click-modal="false" :close-on-press-escape="false" :modal-append-to-body="false" :append-to-body="true" width="450px" top="20vh" v-loading="formLoading" > <el-form size="mini" label-position="left" label-width="140px" :model="timeInfoForm" :rules="timeRules" ref="timeInfoForm" > <el-form-item label="节令" prop="season" v-if="showAddDialog.type === 2" > <el-select v-model="timeInfoForm.season" placeholder="请抉择节令" style="width: 245px" @change="handleSeasonChange" > <el-option v-for="item in seasonList" :key="item" :label="item" :value="item" ></el-option> </el-select> </el-form-item> <el-form-item label="起始时刻" prop="st"> <el-select v-model="timeInfoForm.st" placeholder="请抉择起始时刻" style="width: 245px; margin-right: 5px" @change="handleStartChange" > <el-option v-for="item in stList" :key="item" :label="item" :value="item" ></el-option> </el-select >时 </el-form-item> <el-form-item label="终止时刻" prop="et"> <el-select v-model="timeInfoForm.et" placeholder="请抉择终止时刻" style="width: 245px; margin-right: 5px" > <el-option v-for="item in etList" :key="item" :label="item" :value="item" ></el-option> </el-select >时 </el-form-item> <el-form-item label="可上调电负荷比例" prop="up"> <el-input v-model.trim="timeInfoForm.up" placeholder="0-1之间" clearable style="width: 245px" > </el-input> </el-form-item> <el-form-item label="可下调电负荷比例" prop="down"> <el-input v-model.trim="timeInfoForm.down" placeholder="0-1之间" clearable style="width: 245px" > </el-input> </el-form-item> </el-form> <div slot="footer"> <el-button type="reset" @click="handleFormCancel" size="small" >取 消</el-button > <el-button type="primary" @click="handleFormSubmit" size="small" >保 存</el-button > </div> </el-dialog> </div></template>export default { name: "addTime", props: { showAddDialog: { type: Object, default: () => {}, }, }, data() { return { formLoading: false, timeInfoForm: { season: "", st: "", et: "", up: "", down: "", }, seasonList: [], hasAddData: [], // 已增加数据 }; }, created() { // data是父组件传递过去曾经增加的数据 let data = this.$deepClone(this.showAddDialog.data); // type---> 1年,2节令 if (this.showAddDialog.type == 2) { // columns是父组件传递过去的节令数据 this.seasonList = this.showAddDialog.columns; this.timeInfoForm.season = this.seasonList[0]; this.handleDataFormat(); } else { this.hasAddData = data; } }, computed: { // 获取默认的0-23小时数据 getAllTimeData() { let arr = []; for (let i = 0; i <= 23; i++) { arr.push(i); } return arr; }, // 获取起始时刻的数据 stList() { return this.getAllTimeData.filter((t) => this.hasAddData.every(({ st, et }) => t < st || t > et) ); }, // 获取终止时刻的数据 etList() { let start = this.timeInfoForm.st; let startIndex = this.stList.indexOf(start); return this.stList.filter( (t, i) => t >= start && t - start === i - startIndex ); }, }, methods: { // 切换起始工夫 handleStartChange() { this.timeInfoForm.et = ""; }, // 节令格式化数据 handleDataFormat() { let data = this.$deepClone(this.showAddDialog.data); // 应用reduce函数,将数据分组 /** let arr = { 秋季: [ {season: "秋季", st: 12, et: 15, up: 0.2, down: 0.4} {season: "秋季", st: 18, et: 21, up: 1, down: 1} ], 冬季: [ {season: "冬季", st: 12, et: 15, up: 0.2, down: 0.4} ], 秋季: [], 夏季: [] } */ let arr = data.reduce((prev, curr) => { let key = curr["season"]; if (!prev[key]) { prev[key] = []; } prev[key].push(curr); return prev; }, {}); // 如果该节令下没有数据,则应用默认的0-23小时数据 if (!arr[this.timeInfoForm.season]) { this.hasAddData = []; } else { this.hasAddData = arr[this.timeInfoForm.season]; } }, // 切换节令 handleSeasonChange(val) { this.timeInfoForm.season = val; this.handleDataFormat(); }, },};次要逻辑是计算出起始时刻和终止时刻,脑子中有一些想法,然而代码总是编写有问题,因而记录下大佬的编码,再次感激边城大佬的帮忙。 ...

July 6, 2022 · 3 min · jiezi

关于vue.js:Vue项目ERRORCannot-find-module-xxxx类报错的解决办法

当初发现只有是报错Error:Cannot find module的问题,都能够用上面形式解决 报错内容:运行npm install没有问题,当运行我的项目,npm run dev 或者是yarn dev的时候,就会在终端里呈现大片的绿色。遇到很屡次,最终好使的办法是:到以后的我的项目目录下,(文件管理器),把node_nodule文件和package-lock.json文件删除,而后再终端从新执行npm install最初再运行就好了,亲测,我的是好使了。

July 6, 2022 · 1 min · jiezi

关于vue.js:vue-router

一、 编程式导航

July 6, 2022 · 1 min · jiezi

关于vue.js:Vue的事件修饰符

修饰符.stop - 阻止冒泡.prevent - 阻止默认事件.capture - 阻止捕捉.self - 只监听触发该元素的事件.once - 只触发一次.left - 左键事件.right - 右键事件.middle - 两头滚轮事件 别离解释什么叫冒泡。。。冒泡:就是指父元素和子元素有雷同的事件,当触发子元素事件时,会向上冒泡,同时也会触发父元素事件默认事件:默认事件就是标签或者是元素自带的事件,浏览器的也算捕捉:指的是触发父元素的事件时,同时子元素的事件也会响应,是向下响应的其余都是比拟好了解,不做解释在应用Vue中,应用下面的修饰符进行对事件进行防止或者欠缺举个很简略的例子 <input v-on:keyup.13="submit">这个.13是keyCode的序号,也就是键值,个别不会记住所以就有来代替他们的别名:.enter.tab.delete (捕捉 "删除" 和 "退格" 键).esc.space.up.down.left.right.ctrl.alt.shift.meta具体应用的时候能够百度这些别名,而后.xx就能够了。

July 5, 2022 · 1 min · jiezi

关于vue.js:eslint在vscode里的配置问题

一、下载安装首先在我的项目中装置eslint插件,在我的项目外面把他进行install npm install eslint --save-dev #如果我的项目没有装置eslint依赖而后把他从新设置为全局利用装置胜利后会在package.json里看到对于eslint的版本号 二、创立eslint rule config文件倡议应用npx进行装置,因为npx能够比npm更能专一于模块,npm次要用于installnpx eslint --init #生成eslint文档 三、学习eslint配置规定rules: no-console: 2 # 禁止应用console,如果应用了console则编译谬误 no-debugger: 1 # 禁止应用debugger,如果应用了debugger则正告 从下面能够晓得,规定就是 属性:规定选项no-console: 2 规定选项有0,1,2;或off、warn、error。 "off" or 0 - turn the rule off "warn" or 1 - turn the rule on as a warning (doesn't affect exit code) "error" or 2 - turn the rule on as an error (exit code will be 1)这样能够进行eslint的配置须要什么样的规定就 属性:规定 就完事了。

July 5, 2022 · 1 min · jiezi

关于vue.js:vue的vif引起的js报错问题和解决方法

文章不易,请关注公众号 毛毛虫的小小蜡笔,多多反对,谢谢 背景在dag图中的节点,双击后会关上对应的配置页面。 这个配置页面是做成了组件,挂载在父节点中。其中父节点是v-if来挂载的。 父节点挂载在index.vue中,代码如下图所示: 配置页面的组件挂载到父节点中,代码如下图所示: 组件中的mouted和dblclick事件,代码如下图所示: 其中,dblclick后,会先执行closeDialogs,而后再把组件show进去: 同时,在组件beforeDestroy的时候,也会销毁事件: 留神,这里也用了jquery,因为dag图是通过jsPlumb来绘制的。 问题双击dag图的树节点,关上了配置页面,在不敞开配置页面的前提下,再双击其余树节点,而后再双击方才的树节点,就会触发js报错。 如下图所示: 详情 请查看:毛毛虫的小小蜡笔

July 5, 2022 · 1 min · jiezi

关于vue.js:git删除已上传的nodemodules文件

git删除已上传的node_modules文件 新建我的项目没配置gitignore文件,导致node_modules文件上传到了git仓库,上面是解决办法,能够删除仓库已上传的node_modules文件: 按程序执行以下命令: git rm -r --cached node_modulesgit commit -m 'delete node_modules file'git push origin master

July 4, 2022 · 1 min · jiezi

关于vue.js:百行代码实现-Vue-2-响应式

百行代码实现 Vue 2 响应式 如果感觉能够就关注一下公众号吧~

July 4, 2022 · 1 min · jiezi

关于vue.js:vue首页优化

问题,首页sql group by,导致速度慢, 一天大略10万数据上线半年左右,sql变10s+计划首页sql, group数据独自查问 this.$http.get(this.url.list+"New", para).then((res) => { this.total = res.data.total; var datas = res.data.rows; console.log(res.data); for (var i = 0; i < datas.length; i++) { var data = datas[i]; //异步ajax启动, this.obtainCustomeEntryCount(datas[i].billNo, datas[i]); this.obtain310Receipt(datas[i].billNo, datas[i]); this.obtainCvsAPICount(datas[i].billNo, datas[i]); } this.vCustomeEnrtys = datas; this.listLoading = false; $("#loading").hide(); }); }

July 3, 2022 · 1 min · jiezi

关于vue.js:解决-使用-vuei18n-入口文件配置-控制台报警问题

形容 You are running the esm-bundler build of vue-i18n. It is recommended to configure your bundler to explicitly replace feature flag globals with boolean literals to get proper tree-shaking in the final bundle.vite.config.ts 文件中 import { defineConfig } from 'vite';import vue from '@vitejs/plugin-vue';export default defineConfig({ server: { proxy: { '/api': { target: 'http://127.0.0.1:8000/api', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), }, }, }, alias: { 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js', }, plugins: [vue()],});要害配置 ...

July 3, 2022 · 1 min · jiezi

关于vue.js:同一台电脑局部安装vue2和vue3

1.新建两个文件夹别离为vue2和vue3 如下图: 2.装置 装置vue2:npm install vue-cli@2.9.6装置vue3:npm install @vue/cli3.查看 装置胜利,在vue2和vue3文件夹执行 vue -V4.重命名 批改本人习惯的名字,返回vue2/node_modules/.bin 目录下,将vue改为vue2,vue.cmd改为vue2.cmd;vue3的同理vue2和vue3文件夹下别离执行 `vue2 -V` `vue3 -V`呈现版本号则胜利5.设置零碎环境变量,如下图 6.轻易关上命令行,应用 vue2 -V vue3 -V,如呈现版本号则胜利, vue2创立我的项目:vue2 init webpack my-appvue3创立我的项目:vue3 create my-app7.若vue3须要拉去vue2的模板,需在vue3的装置目录下执行一下命令: npm install @vue/cli-init切记!!!不是**全局装置**至此,能够应用 vue3 init webpack my-app 创立我的项目参考文章vue-cli

July 1, 2022 · 1 min · jiezi

关于vue.js:这样的商城系统全开源免费商用还要什么自行车

想做本人的微商城,但资金投入压力太大是吧?想要进行零碎二次开发,但代码不开源怎么办?想要数据独立治理,但 Saas 零碎不反对怎么办?还想要多终端适配?还想要难看的 UI 设计?这些都想要?那就看过去!收费商用的!业余保障的! 全开源收费商用的商城零碎 ——CRMEB 开源买通版!以上需要统统帮你满足! 全开源收费商用CRMEB 买通版商城零碎代码全开源可收费商用,用户可间接疾速部署进行应用,通过泛滥使用者和开发者的应用测试,零碎更加稳固牢靠。 反对二次开发能够满足企业的个性化需要,在现有构造根底上二次开发,缩小开发工作量,能无效节约老本,保障系统品质。 数据私有化CRMEB 开源买通版是独立部署的零碎,保障数据私有化,产品数据、订单数据、客户数据等等都能够实现私有化治理,不必放心数据泄露。 多终端适配该零碎反对公众号商城、小程序商城、H5 商城、APP 多端适配,满足不同用户的应用需要。 UI 设计时尚好看零碎界面 UI 设计好看时尚,紧跟时下潮流,视觉传播成果更好,各功能区散布正当,各功能区交互简略易操作,让零碎界面整体更清晰,重点更凸显。 源码附件曾经打包好上传到百度云了,大家自行下载即可~ 链接: https://pan.baidu.com/s/14G-b...提取码: yu27百度云链接不稳固,随时可能会生效,大家放松保留哈。 如果百度云链接生效了的话,请留言通知我,我看到后会及时更新~ 开源地址码云地址:http://github.crmeb.net/u/defu Github 地址:http://github.crmeb.net/u/defu

July 1, 2022 · 1 min · jiezi

关于vue.js:vue响应式实现

咱们晓得Vue是一个MVVM框架,它一个核心思想就是数据驱动。数据驱动就是指视图是由数据驱动生成的,咱们若想让视图扭转,要通过批改数据来进行批改,而不是像原始 JS那样间接操作DOM。 响应式?首先理解一下Vue是如何实现数据驱动的,这是一幅Vue官网的配图。 黄色那一块是Vue负责渲染的render function,当视图初始化以及视图更新时都会调用这个render function。而每次渲染时都会无奈防止地触碰到咱们的数据,也就是图中紫色的data局部。渲染时通过触发波及数据的getter,把波及数据作为依赖收集到watcher中。(而且这个watcher在每个组件实例中都会对应有一个)所以在前面咱们批改这些收集到的依赖数据时,就会触发数据的setter。setter办法会批改数据的值并告诉(notify)给watcher,最初watcher再触发render funnction进行视图的从新渲染。下面说的就是Vue实现数据驱动的过程,能够看进去实现数据驱动的要害是让咱们的数据变成响应式,而getter和setter这两个办法又是让数据变成响应式的要害。然而咱们写在data中的原始数据并没有对应性能的getter和setter啊?其实这就是Vue的工作:重写数据的getter和setter。 Vue2那么Vue是怎么实现数据的响应式的?首先咱们来看一下Vue2的实现原理。 Vue3两者的比照

June 30, 2022 · 1 min · jiezi

关于vue.js:Hunter狩猎者夹子机器人swap自动交易机器人哈希竞猜农场烤吐司游戏LP流动性节点质押智能合约合约量化案列讲解

1、反复元素断定以下办法能够查看给定列表是不是存在反复元素,它会应用 set() 函数来移除所有反复元素。 def all_unique(lst): return len(lst)== len(set(lst)) x = [1,1,2,2,3,2,3,4,5,6] y = [1,2,3,4,5] all_unique(x) # False all_unique(y) # True2、分块给定具体的大小,定义一个函数以依照这个大小切割列表。 from math import ceil def chunk(lst, size): return list( map(lambda x: lst[x size:x size + size], list(range(0, ceil(len(lst) / size))))) chunk([1,2,3,4,5],2) [[1,2],[3,4],5]3、压缩这个办法能够将布尔型的值去掉,例如(False,None,0,“”),它应用 filter() 函数。 def compact(lst): return list(filter(bool, lst)) compact([0, 1, False, 2, '', 3, 'a', 's', 34]) [ 1, 2, 3, 'a', 's', 34 ]4、 应用枚举咱们罕用 For 循环来遍历某个列表,同样咱们也能枚举列表的索引与值。 ...

June 29, 2022 · 2 min · jiezi

关于vue.js:vuetreeselect遇到的坑

1、绑定的v-model="value" value默认值为null,不然会呈现(unknown)报错;2、数据回显的时候也会呈现闪动一下(unknown)状况 normalizer(node) { if(node){ node.id = node.name?node.name:null; node.label = node.name?node.name:null; if (node.children == null || node.children == 'null'|| node.children.length == 0) { delete node.children } } },在这里的时候回显的值是失常的,然而树的数据申请是异步的还没解构齐全,有个异步的操作,所以能够把赋值操作放在tree的数据处理好之后,能够防止闪动问题3、其他人的问题(没有遇到这样的问题,保留一下做个记录)http://t.zoukankan.com/smile-...https://blog.csdn.net/qq_4188...(这个办法我可太喜爱了哈哈哈简略粗犷但高效)

June 29, 2022 · 1 min · jiezi

关于vue.js:词云滚动效果

实现成果: 第一种: 应用tagcanvas<template> <div class="world-cloud-3d"> <div class="world-cloud-canvas-wrapper"> <canvas id="world-cloud-canvas" width="400" height="200" style="width: 100%; max-width: 400px" > </canvas> </div> <div style="display: none" id="weightTags"></div> </div></template><script>import '@/assets/js/tagcanvas.js'export default { data: function () { return {} }, props: { wordArr: { type: Array, default: () => [ { name: '西安市公共交通团体有限公司', light: true }, { name: '广平县永信财税服务有限公司', light: true }, { name: '河南薪火传承数字科技有限公司', light: true }, { name: '深圳市智谷天厨科技有限公司', light: true }, { name: '天津麟才教育征询有限公司', light: true }, { name: '深圳市九策有限公司', light: true }, { name: '深圳大疆有限公司', light: true }, { name: '云基华海信息技术', light: true } ] } }, methods: { // 启动词云 startWorldCloud () { this.createTagListDom() const o = { maxSpeed: 0.01, // 增加最大的静止速度 minSpeed: 0.01, // 增加最小的静止速度这样就能够保障始终静止,不会进行 textHeight: 25, outlineMethod: 'colour', // tag hover 之后的 轮廓成果 fadeIn: 800, outlineColour: '#fff', outlineOffset: 0, depth: 0.97, minBrightness: 0.2, wheelZoom: false, reverse: true, // 静止方向与鼠标挪动方向相同 shadowBlur: 2, shuffleTags: true, shadowOffset: [1, 1], stretchX: 1.7, // Stretch or compress the cloud horizontally. 程度拉伸词云 initial: [0.1, 0.1], // 给词云增加一个初始的静止方向 textFont: null, // 字体设置为 null 就会继承 每个 tag的a 标签的字体 textColour: null, // 字体色彩设置为 null 就会继承 每个 tag的a 标签的字体色彩 weight: true, // weight 关上,就能够依据默认的字体的大小作为权重,对tag 的款式进行调整 weightMode: 'size', // 款式调整的形式 weightSize: 0.5 // 调整 tag 字体的大小,加个 权重 } // eslint-disable-next-line no-undef TagCanvas.Start('world-cloud-canvas', 'weightTags', o) try { // 如果不是更新,阐明是第一次渲染,就启动 tagcanvas, 否则就代表更新 // eslint-disable-next-line no-undef if (!updateFlag) { // eslint-disable-next-line no-undef TagCanvas.Start('world-cloud-canvas', 'weightTags', o) } else { // eslint-disable-next-line no-undef TagCanvas.Update('world-cloud-canvas') } } catch (e) {} }, // 依据父组件传过来的 wordArr 创立 TagList Dom列表,放到页面中供,canvas 渲染 // 这里后端的数组中的数据结构是一个对象 { name: 要展现的tag名字, light: true/false 据定是否要减轻展现} createTagListDom: function () { const res = [...this.wordArr] const fragment = new DocumentFragment() for (let i = 0; i < res.length; i++) { const a = document.createElement('a') // 字符串长度大于10就要换行 if (res[i].name.length > 10) { const charArr = res[i].name.split('') charArr.splice(10, 0, '<br>') res[i].name = charArr.join('') } a.innerHTML = res[i].name a.href = 'javascript:void(0)' // 如果是要减轻展现就 设置属性为 huge 或large, 否则就设置属性为 medium 或small if (res[i].light) { const readomValue = Math.random() a.className = readomValue > 0.5 ? 'huge' : 'large' } else { const readomValue = Math.random() a.className = readomValue > 0.5 ? 'medium' : 'small' } fragment.append(a) } // 更新 tagContainer中的 tag元素 const tagsContainer = document.querySelector('#weightTags') tagsContainer.innerHTML = '' tagsContainer.append(fragment) } }, watch: { // 如果词云发生变化就要 重绘 tagcanvas wordArr: function () { this.startWorldCloud(true) } }, mounted () { // 组件装载成绩 绘制 tagcanvas this.startWorldCloud() }}</script><style lang="less">.world-cloud-3d { width: 100%; height: 100%; // border: 1px solid red; .world-cloud-canvas-wrapper { width: 400px; height: 200px; max-width: 400px; max-height: 200px; } #weightTags { font-size: 12px; .huge { font-size: 30px; color: rgb(98,203,235); } .large { font-size: 25px; color: rgb(98,203,235, 0.6); } .medium { font-size: 20px; color: rgb(98,203,235, 0.4); } .small { font-size: 15px; color: rgb(98,203,235, 0.2); } }}</style>tagcanvas.js源码: ...

June 29, 2022 · 22 min · jiezi

关于vue.js:Vue2nextTick

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <ul ref="ulRef"> <li v-for="(item, index) in items" :key="index"> {{ item }} </li> </ul> <button @click="add"> add </button> </div></body><script> var app = new Vue({ el: '#app', data() { return{ items: ['1', '2', '3'] } }, methods: { add() { this.items.push(Math.random()) // dom 异步渲染完结后执行 this.$nextTick(() => { const ul = this.$refs.ulRef const len = ul.childNodes.length console.log('len ', len) }) } } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2兄弟组件通信

创立 eventHub 兄组件向 eventHub 发送数据弟组件监听 eventHub 获取数据 <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <div> <myinput @add="add" /> </div> <div> <mylist/> </div> </div></body><script> const eventHub = new Vue() Vue.prototype.$eventHub = eventHub // Vue.component('myinput', { template: ` <div> <input v-model="title"> <button @click="add"> add </button> </div> `, data() { return { title: '', } }, methods: { add() { this.$eventHub.$emit('add', this.title) } } }) Vue.component('mylist', { template: ` <ul> <li v-for="item in list" :key="item.id"> {{ item.title }} </li> </ul> `, data: function() { return { list: [] } }, created() { this.$eventHub.$on('add', (title) => { this.list.push({ id: Math.random(), title, }) }) } }) // var app = new Vue({ el: '#app', })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2父子组件通信

父组件注入数据子组件通过 props 接管子组件 emit 数据父组件通过 @ 监听 <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <div> <myinput @add="add" /> </div> <div> <mylist :list="list"/> </div> </div></body><script> Vue.component('myinput', { template: ` <div> <input v-model="title"> <button @click="add"> add </button> </div> `, data() { return { title: '', } }, methods: { add() { this.$emit('add', this.title) } } }) Vue.component('mylist', { template: ` <ul> <li v-for="item in list" :key="item.id"> {{ item.title }} </li> </ul> `, props: { list: Array } }) // var app = new Vue({ el: '#app', data: { list: [ { id: 1, title: 'title1', }, { id: 2, title: 'title2', }, ], }, methods: { add(title) { this.list.push({ id: Math.random(), title, }) } } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2事件

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <p>{{ num }}</p> <button @click="incrementBy1"> + 1 </button> <button @click="incrementBy10(10, $event)"> + 10 </button> </div></body><script> var app = new Vue({ el: '#app', data: { num: 0, }, methods: { incrementBy1(e) { this.num++ }, incrementBy10(step, e) { this.num += 10 console.log(e) }, } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:标准化编程规范解决方案之Git-Hooks

Git Hooks上一节中咱们应用了 git cz 来代替了 git commit 实现了规范化的提交诉求,然而仍然存在着有人会遗记应用的问题。 那么这一节咱们就来看一下这样的问题,咱们应该如何去进行解决。 先来明确一下咱们最终要实现的成果: 咱们心愿: 当《提交形容信息》不合乎 约定式提交标准 的时候,阻止以后的提交,并抛出对应的谬误提醒 而要实现这个目标,咱们就须要先来理解一个概念,叫做 Git hooks(git 钩子 || git 回调办法) 也就是:git 在执行某个事件之前或之后进行一些其余额定的操作 而咱们所冀望的 阻止不合规的提交音讯,那么就须要应用到 hooks 的钩子函数。 上面是我整理出来的所有的 hooks ,大家能够进行一下参考,其中加粗的是罕用到的 hooks: Git Hook调用机会阐明pre-applypatchgit am执行前 applypatch-msggit am执行前 post-applypatchgit am执行后不影响git am的后果pre-commitgit commit执行前能够用git commit --no-verify绕过commit-msggit commit执行前能够用git commit --no-verify绕过post-commitgit commit执行后不影响git commit的后果pre-merge-commitgit merge执行前能够用git merge --no-verify绕过。prepare-commit-msggit commit执行后,编辑器关上之前 pre-rebasegit rebase执行前 post-checkoutgit checkout或git switch执行后如果不应用--no-checkout参数,则在git clone之后也会执行。post-mergegit commit执行后在执行git pull时也会被调用pre-pushgit push执行前 pre-receivegit-receive-pack执行前 update post-receivegit-receive-pack执行后不影响git-receive-pack的后果post-update当 git-receive-pack对 git push 作出反应并更新仓库中的援用时 push-to-checkout当`git-receive-pack对git push做出反馈并更新仓库中的援用时,以及当推送试图更新以后被签出的分支且receive.denyCurrentBranch配置被设置为updateInstead时 pre-auto-gcgit gc --auto执行前 post-rewrite执行git commit --amend或git rebase时 sendemail-validategit send-email执行前 fsmonitor-watchman配置core.fsmonitor被设置为.git/hooks/fsmonitor-watchman或.git/hooks/fsmonitor-watchmanv2时 p4-pre-submitgit-p4 submit执行前能够用git-p4 submit --no-verify绕过p4-prepare-changelistgit-p4 submit执行后,编辑器启动前能够用git-p4 submit --no-verify绕过p4-changelistgit-p4 submit执行并编辑完changelist message后能够用git-p4 submit --no-verify绕过p4-post-changelistgit-p4 submit执行后 post-index-change索引被写入到read-cache.c do_write_locked_index后 PS:具体的 HOOKS介绍 可点击这里查看 ...

June 28, 2022 · 2 min · jiezi

关于vue.js:Vue2vfor

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <h1> 遍历数组 </h1> <ul> <!-- for 优先级 高于 if --> <!-- 放一起报错 --> <li v-for="(item, index) in array" :key="item.id"> {{ index }} - {{ item.value }} </li> </ul> <!-- --> <h1> 遍历对象 </h1> <ul> <li v-for="(value, key, index) in obj" :key="key"> {{ index }} - {{ key }} - {{ value }} </li> </ul> </div></body><script> var app = new Vue({ el: '#app', data: { array: [ {id:1, value:'a'}, {id:2, value:'b'}, {id:3, value:'c'}, ], obj: { name: 'tao1', hobby: 'reading', } } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2show-和-if-的区别

show 会将不显示的元素 display noneif 则不会在页面渲染不显示的元素 <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <h1>v-if</h1> <p v-if="data==='a'"> a </p> <p v-else-if="data==='b'"> b </p> <p v-else> c </p> <!-- --> <h1>v-show</h1> <p v-show="data==='a'"> a </p> <p v-show> b </p> </div></body><script> var app = new Vue({ el: '#app', data: { data: 'a', } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2动态绑定类名与样式

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <!-- <ul class="list"> <li class="item active">1</li> <li class="item ">2</li> <li class="item ">3</li> </ul> --> <!-- <ul class="list"> <li class="item " :class="{'active':isActive}">1</li> <li class="item ">2</li> <li class="item ">3</li> </ul> --> <ul class="list"> <li class="item " :class="['active']">1</li> <li class="item " :style="css">2</li> <li class="item ">3</li> </ul> </div></body><script> var app = new Vue({ el: '#app', data: { isActive: true, css: { color: 'red', } } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:开源1年后Fesjs-升级为企业级通用前端应用解决方案

/ 诞生 / 转瞬曾经退出公司 5 年,始终在做银行客服业务相干的中后盾利用的开发工作;日复一日做相似性很高的开发工作,难免会有些干燥与乏味,于是乎,思考如何可能更 Smart 的形式来综合的优化和晋升中后盾利用开发的效率。 从过往的工作中提炼了重要的三点, 首先是 Fast ,要有极致麻利的开发体验;而后是 Easy,学习成本低容易上手;最初,还要 Strong,代码稳健性更强,性能优;由此构建成了 Fes.js 这款基于 Vue 3 的前端利用解决方案。 以约定、配置化、组件化的设计思维,让用户仅仅关怀用组件搭建页面内容。技术曲线平缓,上手也简略,通过了多个我的项目打磨后趋于稳定,丰盛的 Vue 3 生态 和 Fes.js 插件,让业务开发更麻利,顺滑。 / 成长 / Fes.js在 2020 年开源之初,只定位在中后盾利用解决方案,当初新增了开发 H5 的性能,曾经演进为通用前端利用解决方案。接下来,就和大家分享一下 Fes.js 经验了哪些革新,实现了这次迭代降级,心愿能给大家带来借鉴,也期待大家试用降级后的 Fes.js。v1 中后盾前端利用大多要解决这些事: 构建, dev 和 build 入口 index.html 入口 main.js,初始化 vue 实例 路由相干 权限相干 页面布局 接口申请 ... 在过后 CLI 工具很热门,比方 Vue-CLI 和团队内的 mn,他们解决构建相干事件,而其余事件须要我的项目自行处理。那么开发新我的项目步骤就是先拷贝老我的项目,再改改代码,再增加新性能,长此以往发现一些问题: 继承 “待优化” 代码,不相熟改起来很吃力,不改又是隐患 技术栈老旧,降级很吃力,不如从新弄一套新的 一个 request 工具函数,十种写法,保护起来心累 那么提供一个封装这些通用性能的框架,对立代码标准和代码组织形式,代码优化和降级交给框架,用户齐全不必操心,v1 版本就做这了这些事。 设计思路 ...

June 28, 2022 · 2 min · jiezi

关于vue.js:Vue2watch

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <input v-model="info.hobby"> <p> {{ info }} </p> <input v-model="name"> <p> {{ name }} </p> </div></body><script> var app = new Vue({ el: '#app', data: { name: 'tao', info: { hobby: 'reading', } }, watch: { name(newV, oldV) { console.log('name', newV, oldV) }, // info(newV, oldV) { // console.log('info', newV, oldV) // }, // 深度监听对象 info: { handler: function(newV, oldV) { // 新老值援用的是同一个对象 console.log('info', newV, oldV) }, deep: true, } } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2computed

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <p> {{ number }} </p> <p> double1: {{ double1 }} </p> <input v-model="double2"> {{ double2 }} </div></body><script> var app = new Vue({ el: '#app', data: { number: 5 }, computed: { double1() { // 当 double1 变动时才会从新执行 return this.number * 2 }, // double2() { // // 批改 input 报错,须要 setter // return this.number * 2 // }, double2: { get: function() { return this.number * 2 }, // 批改须要 setter 拦挡 set: function(v) { this.number = v / 2 } }, } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue2插值-动态属性-表达式-vhtml

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app"> <!-- 插值表达式 --> {{ message }} <p id="test"> {{ message }} </p> <!-- 动静属性 --> <p :id="dynamicId"> {{ message }} </p> <p>{{ number }}</p> <p>{{ number + 1 }}</p> <!-- 表达式 --> <p>{{ flag ? 'yes' : 'no'}}</p> <p>{{ message.split('').reverse().join('') }}</p> <!-- 不是表达式 --> <!-- <p>{{ var a = 1 }}</p> --> <div v-html="html1"></div> <div v-html="html2"> <p> 原有元素被笼罩 </p> </div> <!-- xss 攻打 --> <div v-html="html3"></div> </div></body><script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!', dynamicId: Math.random(), number: 1, flag: true, html1: "<p> tao1 </p>", html2: "<h1> tao2 </h1>", html3: "<img src=123 onerror=alert(1)>", } })</script></html>

June 28, 2022 · 1 min · jiezi

关于vue.js:vue-设置动态-ref

最近应用el-tabs组件遇到了一个问题:就是el-tabs中el-tab-pane是动静生成的,而且后续须要获取el-tab-pane中元素的ref,代码大略是上面的样子:<el-tabs v-model="activeName" type="card" @tab-click="handleClick"> <el-tab-pane v-for="(item, index) in case_tabs" :label="item.name" :name="index.toString()" :key="index"> <TabList :ref="`tab${index}`"/> </el-tab-pane></el-tabs>这个应用场景是当切换tab时,要调用组件<TabList />外部的的load办法。 handleClick(tab) { this.activeName = tab.index this.$refs[`tab${tab.index}`].load()}看似没有故障,然而点击切换时就报错了,提醒this.$refs[tab${tab.index}]是undefined。 这时就须要去搞明确这个ref到底是什么样的工作机制,vue官网查到以下内容: ref 被用来给元素或子组件注册援用信息。援用信息将会注册在父组件的 $refs 对象上。如果在一般的 DOM 元素上应用,援用指向的就是 DOM 元素;如果用在子组件上,援用就指向组件实例 当 v-for 用于元素或组件的时候,援用信息将是蕴含 DOM 节点或组件实例的数组。 对于 ref 注册工夫的重要阐明:因为 ref 自身是作为渲染后果被创立的,在初始渲染的时候你不能拜访它们 - 它们还不存在!$refs 也不是响应式的,因而你不应该试图用它在模板中做数据绑定。 可见,咱们在应用v-for时应该把子组件的ref寄存在一个数组中。 解决方案<template> <el-tabs v-model="activeName" type="card" @tab-click="handleClick" > <el-tab-pane v-for="(item, index) in case_tabs" :label="item.name" :name="index.toString()" :key="index"> <TabList :ref="setItemRef"/> </el-tab-pane> </el-tabs><template><script>export default { name: "CaseList", ..., data(){ return { refList: [] } } methods: { setItemRef(el) { this.refList.push(el) }, handleClick(tab) { this.activeName = tab.index this.refList[tab.index].load() }, }}</script>这样程序就能够失常工作了。 ...

June 28, 2022 · 1 min · jiezi

关于vue.js:WallysWiFi-6-80211ax-4×4-MUMIMO-5GHz-QCN9074-Single-Band

WiFi 6 (802.11ax) 4×4 MU-MIMO 5GHz QCN9074 Single Band Wireless Module DR9074-5G(PN02.1) QCN9074 QCN9024 802.11ax 4x4 MU-MIMO 5GHz wifi6 QCN9074 11ax 4x4 5G M.2 DR9074-5G(PN02.1) https://www.wallystech.com/Ne... MT7915/MT7975/IPQ6000/IPQ6018/IPQ6010/IPQ4019/IPQ4029/ipq4018/IPQ8072/IPQ8074/QCN9074/QCN9072/QCN9024/IPQ5018/BY:Wallys Communications (Suzhou ) Co., LTDEMAIL:sales3@wallystech.com Product Description 9074-5G (PN02.1) based on IPQ9074 Chipset is an enterprise wireless module integratedwith 4×4 MU-MIMO 5GHz Single Band Wireless Module designed specifically to provide userswith mobile access to high-bandwidth video streaming, voice, and data transmission for officeand challenging RF environment in factories, warehouses establishment. ...

June 28, 2022 · 1 min · jiezi

关于vue.js:Vue项目之使用EditorConfig-Eslint和Prettier实现代码规范

背景每个人的代码格调不同,比方有的人代码缩进喜爱用Tab,有的人喜爱用Space;有的人代码语句后会加上分号,有的人则不加。并且不同的人应用的开发工具也不同,有的人喜爱应用WebStorm,有的人喜爱用VSCode。。。 如果是一个人独立开发,这些当然都没啥问题,本人想怎么写就怎么写。代码规范化之后可能也没法立刻看到带来的益处。并且相同,可能在某些人看来反而是一种解放。 然而如果是团队协同开发,代码规范化所带来的益处就很显著。比方,对立团队成员的代码格调,不便前期保护,防止扰乱Git Diff等等。 针对以上这些问题,本文会介绍如何在Vue我的项目中实现根本的代码标准配置。 思路不同人可能会应用不同开发工具,不同开发工具有着不同的默认配置,针对这个问题,这里咱们采纳开发工具配置工具EditorConfig去对立这些开发工具的默认配置。 有时候开发中难免会写出一些语法错误等问题,比方应用了未声名的变量,switch语句少写了break等等,而这些问题要等到编译或者运行时才会被发现。针对这个问题,这里咱们应用代码查看工具Eslint来查看这些代码问题,将这些问题提前裸露进去并修复,而不是等到编译或运行时提醒报错才发现。 此外,每个人的代码格调不同,比方有的人代码缩进喜爱用Tab,有的人喜爱用Space,有的人一行宽度设置了80个字符,有的人设置了120甚至更多等等,针对这个问题,这里咱们应用代码格式化工具Prettier来对立这些代码格调。 实现思考到有的Vue我的项目是用Vue CLI搭建的,有的是用Webpack或其余工具搭建的,因而接下来我将用VSCode开发工具,依据不同的搭建工具别离实现代码标准。应用Vue CLI创立的Vue我的项目给新我的项目增加代码标准当咱们应用vue create命令创立新我的项目时,命令行界面会询问咱们是否抉择 Linter / Formatter,这就是Vue CLI给咱们提供的代码标准,这里须要选上。 抉择Eslint + Prettier抉择之后,命令行会接着询问咱们抉择哪一种Eslint配置。列表中都是比拟风行的配置,能够依据本人的须要抉择。这里咱们抉择Eslint + Prettier。 主动执行lint规定配置接下来,命令行还会询问何时主动执行lint,这里咱们须要把两个都选上,选上之后不论是保留文件,还是应用Git提交代码,都会主动执行lint,并主动修复谬误(提交代码时)。 抉择独立配置文件最初,命令行还会询问是否把这些配置放在各自独立的文件中,还是全副放在package.json中,思考到模块的明确性和前期保护,这里咱们抉择In dedicated config files。 最初,等我的项目初始化实现后,咱们应用开发工具关上我的项目,会发现我的项目的根目录多了一个.eslintrc.js文件。 这就是Eslint配置文件,能够参考Eslint官网配置文档理解更多。对了,开发工具还要装置Eslint插件并配置好,这样Eslint才会失效。 配置Prettier首先,咱们须要在我的项目的根目录新建文件.prettierrc.js,而后参考Prettier官网配置文档,配置一些罕用项,这里列出我常常应用的配置,大家能够依据须要自行调整。 // .prettierrc.jsmodule.exports = { useTabs: false, // 敞开tab缩进,应用Space缩进 tabWidth: 2, // 每次缩进2个字符 semi: true, // 结尾加分号 singleQuote: true, // 应用单引号 jsxSingleQuote: true, // jsx中应用单引号 trailingComma: 'es5', // 结尾逗号应用es5规定 bracketSpacing: true, // 括号和参数之间有空格 jsxBracketSameLine: false, // 标签属性较多时,标签箭头>另起一行 arrowParens: 'always', // 箭头函数参数永远加括号 quoteProps: 'as-needed', // 属性加引号须要加时再加 printWidth: 80, // 每行字符个数};其次,咱们还须要给开发工具做一些配置,让开发工具辨认咱们刚刚增加的Prettier配置。 ...

June 28, 2022 · 1 min · jiezi

关于vue.js:封装小技巧数字处理函数的封装

伸手请间接跳到【办法合集】~这次咱们来聊聊对于数字解决的一些问题。我的项目中对数字的解决肯定是避不开的,毕竟数据就是由数字组成的嘛(大雾),所以对于一下常见的数字解决场景咱们进行适当的封装也能无效简洁代码,上面就由简略到简单的程序来介绍几个。 将任意值转为有效数字在一些场合,咱们可能会失去一些类型不平安的值,并须要将其作为数字进行解决,这种场景在写库的时候尤为常见。当咱们间接应用 parseFloat 转化时,在不非法数字时会失去 NaN,这将导致后续所有的运算全副变成 NaN,因而咱们须要将其回退到 0 确保后续运算失常。 function toNumber(value: unknown) { const number = parseFloat(value as string) return Number.isNaN(number) ? 0 : number}将数值限定在一个范畴这个场景和下面很类似,同样是为了保障数字的正确性,须要将数字限定在一个特定的范畴中,针对的是数字的值。 function boundRange(number: number | string, min: number, max: number) { return Math.max(min, Math.min(max, toNumber(number)))}这两个场景都是很简略的解决,旨在简化屡次呈现的繁冗代码。 将个位数变成两位这个太简略,不多说了,像是在解决日期、工夫上经常会呈现这样的需要。 function doubleDigits(number: number) { return number < 10 ? `0${number}` : number.toString()}将数字格式化成三位阶换而言之,就是常见的将数字依照三位一组分隔开的记数法,在进步一些大数字的可读性上,或者金额的显示上用的比拟多: function segmentNumber(number: number | string, separator = ',', segment = 3) { if (typeof number !== 'number') { number = parseFloat(number) } if (Number.isNaN(number)) return '0' let [integer, decimal] = String(number).split('.') const formatRegExp = new RegExp(`(\\d+)(\\d{${segment}})`) while (formatRegExp.test(integer)) { integer = integer.replace(formatRegExp, `$1${separator}$2`) } decimal = decimal ? `.${decimal}` : '' return `${integer}${decimal}`}将数字保留特定位数的小数这是咱们这一次的重磅选手,在 js 中将数字保留特定位数是个技术活,因为 js 的小数存在精度失落问题,咱们先来看一个例子: ...

June 28, 2022 · 3 min · jiezi

关于vue.js:element-form-校验规则amp自定义校验规则

总结: form 校验规定&自定义校验规定 如template<el-form ref="condFormRef" id="condForm" :model="cond" label-width="100px" size="mini" :rules="condFormRules"> <el-form-item label="估算:" prop="budget"> <el-input style="width:60%" v-model="cond.budget" maxlength="15" clearable> </el-input> <small>&nbsp;元</small> </el-form-item></el-form>在form 上定义上绑定 rules 属性, 给须要校验的item 减少prop 属性, prop 值须要个rules中的keys 保持一致condFormRules:{ budget:[ {required: true, message: '', trigger: ['blur','change']}, {validator: validateBudget, trigger: 'blur'}, ],}阐明: (1) required: 是否必填, true则显示红星星, 当设置了必填时, 必须有message, 哪怕为空,否则不填时给默认提醒"budget is required" 留神: 如{message: 'aaaaaaaa', trigger: ["blur","change"]}, 没有required,则会校验,但上次校验失败的信息不会主动隐没, 即要有message时, 必须有required (2) trigger: 可选["blur","change"]触发执行校验的事件 blur:失去焦点时, change:数据扭转时 this.$refs.condFormRef.validate() 时,trigger都会执行(3) validator:自定义校验属性,能够定义本人的校验规定,如validateBudget自定义的规定函数, 校验逻辑简单时能够自定义函数实现。如下校验逻辑简单的:要求数字且正整数或最多带两位的小数 var validateBudget=(rule, value, callback) => { if(!value || !Number(value)){ //不输出或输0.0或0.00时,都进入 callback(new Error('请输出估算')); } else if(!(/(^[1-9]\d*(\.\d{1,2})?)$/.test(value) || /(^[0](\.\d{1,2}){1})$/.test(value))){ callback(new Error('请输出正整数或最多带两位的小数')); } else{ callback(); } };简略时,能够间接写在模板中, 如限度输出数字 ...

June 28, 2022 · 1 min · jiezi

关于vue.js:composition-api在项目中的使用总结

背景第一次晓得composition api是从vue3的RFC提案中据说的,印象最深的是options api和composition api的比照图: 这种图片很清晰的形容出composition api的劣势:可能把雷同逻辑关注点都归为一组,不必在不同的选项中来回滚动切换,这样能达到更好的逻辑复用的成果。这点对于我有很大的吸引力,很多简单业务中,template的内容很少,大部分代码都是js逻辑,性能比较复杂,为了更好的代码复用,采纳了mixins封装了很多专用逻辑,但mixins的缺点很显著,mixins外面应用的变量或办法在vue组件中复用须要重复横跳,但过后受限于vue2,只能达到这种水平,如果当初从新做,应用composition api能达到更好的逻辑复用作用。 composition api首先先相熟一下composition api,有哪些api,目前只须要把握加粗的局部,就能够体验组合式api的魅力了,其余的api等须要用到了再通过vue3的官网文档深刻学习。 reactive 用来将对象转变为响应式的,与vue2的observable相似,ref用来取得独自或者为根底数据类型取得响应性。为什会会有两个取得响应性的api呢,稍后咱们将具体阐明。computed、watch,provide、inject不必狐疑和vue2中做的是一样的事件。你肯定留神到上面这些加了on结尾的生命周期钩子函数,没错在组合式api中,这就是他们注册的形式。然而为什么不见了beforeCreate和created呢?因为setup就是在这个阶段执行的,而setup就是关上组合式api世界的大门。你能够把setup了解为class的constructor,在vue组件的创立阶段,把咱们的相干逻辑执行,并且注册相干的副作用函数。 当初咱们说回ref和reactive。 reactive在官网中的阐明,承受一个对象,返回对象的响应式正本。ref在官网中的形容"承受一个外部值并返回一个响应式且可变的 ref 对象。ref 对象具备指向外部值的单个 property.value"。听着很绕口,简略来讲就是reactive能够为对象创立响应式;而ref除了对象,还能够接管根底数据类型,比方string、boolean等。 那为什么会有这种差别呢?在vue3当中响应式是基于proxy实现的,而proxy的target必须是简单数据类型,也就是寄存在堆内存中,通过指针援用的对象。其实也很好了解,因为根底数据类型,每一次赋值都是全新的对象,所以根本无法代理。 那么如果咱们想获得简略类型的响应式怎么办呢?这时候就须要用到ref。 class RefImpl<T> { private _value: T public readonly __v_isRef = true constructor(private _rawValue: T, public readonly _shallow = false) { this._value = _shallow ? _rawValue : convert(_rawValue) } get value() { track(toRaw(this), TrackOpTypes.GET, 'value') return this._value } set value(newVal) { if (hasChanged(toRaw(newVal), this._rawValue)) { this._rawValue = newVal this._value = this._shallow ? newVal : convert(newVal) trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal) } }}...const convert = <T extends unknown>(val: T): T => isObject(val) ? reactive(val) : valref通过创立外部状态,将值挂在value上,所以ref生成的对象,要通过value应用。重写get/set取得的监听,同时对对象的解决,也依赖了reactive的实现。 由此,ref并不只是具备对根本数据类型的响应式解决能力,他也是能够解决对象的。 所以我认为ref和reactive的辨别并不应该只是简略/简单对象的辨别,而是应该用编程思维辨别的。咱们应该防止,把reactive 当作data在顶部将所有变量申明的想法,而是应该联合具体的逻辑性能,比方一个管制灰度的Flag那他就因该是一个ref,而分页当中的页码,pageSize,total等就应该是一个reactive申明的对象。也就是说一个setup当中能够有多出响应变量的申明,而且他们该当是与逻辑紧密结合的。 ...

June 27, 2022 · 6 min · jiezi

关于vue.js:标准化编程规范解决方案之ESLint

为什么须要编程标准?工欲善其事,必先利其器 对于大型的企业级我的项目而言,通常状况下咱们都是一个团队很多人来独特进行开发的。而又因为团队人员对技术了解上的参差不齐,所以就会导致呈现一种状况,那就是《一个我的项目无奈具备对立的编程标准,导致我的项目的代码像多个不同材质的补丁拼接起来一样》 构想一下,上面的这段代码有一个团队进行开发,因为没有具备对立的代码规范,所以生成了上面的代码: 这段代码能够失常运行没有问题,然而整体的代码构造却十分的难看。 有的中央有空格进行宰割,有的中央却没有 有的中央是单引号,有的中央却是双引号 有的中央有分号,有的中央没有分号 ....这样的我的项目尽管能够失常运行,然而如果把它放到大厂的我的项目中,的确 不及格 的,它会被认为是 不可保护、不可扩大的代码内容 那么所谓的大厂规范的代码构造应该是什么样子的呢? 咱们把下面的代码进行一下修改,做一个比照: 批改之后的代码具备了对立的标准之后,是不是看起来就难受多了! 并且以上所列举进去的只是《编程标准》中的一小部分内容! 那么有些同学可能就会说了,你列举进去这些编程标准有什么用啊! 哪怕你写上一部书,咱们一个团队这么多人,总不能指望所有人都看一遍,并且严格的恪守你所说的标准吧! 说的没错!指望人被动的恪守这些标准不太事实 那怎么办呢? 那么咱们可不可以另辟蹊径,让程序主动解决规范化的内容呢? 答案是:能够的! 本系列中我会为大家分享,如何自动化的对代码进行标准,其中次要包含: 编码标准git 标准两大类 编程标准一:代码检测工具 ESLint在咱们去创立我的项目的时候,脚手架工具曾经帮忙咱们装置了 ESLint 代码检测工具。 对于 ESLint 的小名,同学们或多或少的应该都据说过,只不过有些同学可能理解的多一些,有些同学理解的少一些。 那么本大节咱们就先来聊一下,这个赫赫有名的代码检测工具 ESLint 首先 ESLint 是 2013年6月 创立的一个开源我的项目,它的指标非常简单,只有一个,那就是 提供一个插件化的 javascript 代码检测工具 ,说白了就是做 代码格局检测应用的 在咱们以后的我的项目中,蕴含一个 .eslintrc.js 文件,这个文件就是 eslint 的配置文件。 随着大家对代码格局的规范性越来越器重,eslint 也逐步被更多的人所接管,同时也有很多大厂在原有的 eslint 规定根底之上进行了一些延长。 咱们在创立我的项目时,就进行过这样的抉择: ? Pick a linter / formatter config: ESLint with error prevention only // 仅蕴含谬误的 ESLint ESLint + Airbnb config // Airbnb 的 ESLint 延长规定 ESLint + Standard config // 规范的 ESLint 规定咱们以后抉择了 规范的 ESLint 规定 ,那么接下来咱们就在该规定之下,看一看 ESLint 它的一些配置都有什么? ...

June 27, 2022 · 2 min · jiezi

关于vue.js:自定义-FullCalendar-v5-Vue2

阐明 FullCalendar 版本为 5.x,Vue 版本为 2.x 次要内容 去除默认 headerToolbar,自定义工具栏;任意月份跳转,可返回以后月份;增加事件;查看事件详情(点击)成果 代码为了了解和应用,合并简化了代码。主性能流程简洁明了,可依据理论需要场景 DIY。 代码中工夫格式化选用 day.js,可依据我的项目更改工夫格式化办法 引入依赖 $ npm i @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction @fullcalendar/vueCalendar.vue <template> <div> <div class="toolbar-boxer"> <div> <el-date-picker v-model="selectMonth" type="month" :clearable="false" placeholder="抉择月" value-format="yyyy-MM" @change="handleDatePick"> </el-date-picker> <el-button class="margin-left" v-if="btnReturnCurrenMonthVisiable" @click="handleReturnCurrentMonth">返回今日</el-button> </div> <div class="toolbar-right"> <el-button type="primary" @click="handleAddEvent">增加课程</el-button> </div> </div> <full-calendar ref="fullCalendar" class="calendar" :options="calendarOptions" /> </div></template><script>import FullCalendar from '@fullcalendar/vue'import dayGridPlugin from '@fullcalendar/daygrid'import interactionPlugin from '@fullcalendar/interaction'import dayjs from 'dayjs'export default { props: { events: { type: Array, default: () => [] } }, watch: { // 监听事件list的变动,更新界面 events (newVal, oldVal) { this.calendarOptions.events = newVal } }, data () { return { btnReturnCurrenMonthVisiable: false, currentDate: null, selectMonth: null, // 以后抉择的月份 calendarApi: null, calendarOptions: { plugins: [ dayGridPlugin, interactionPlugin ], initialView: 'dayGridMonth', locale: 'zh-cn', headerToolbar: false, // 不显示头部 toolbar // headerToolbar: { // start: 'addEventBtn', // center: 'title' // }, events: this.events, eventTimeFormat: { // 事件工夫的格式化 hour: 'numeric', minute: '2-digit', hour12: false }, displayEventEnd: true, // 事件显示完结工夫 eventClick: this.handleEventClick }, } }, mounted () { this.calendarApi = this.$refs.fullCalendar.getApi() this.currentDate = dayjs(this.calendarApi.getDate()).format('YYYY-MM') this.selectMonth = this.currentDate }, methods: { /** * 点击事件 */ handleEventClick (eventClickInfo) { this.$emit('onEventClick', eventClickInfo) }, /** * 增加事件 */ handleAddEvent () { this.$emit('onAddEvent') }, /** * 下拉抉择日期并跳转至相应月份 */ handleDatePick (month) { // 抉择了别的月份,则显示返回今日按钮 this.btnReturnCurrenMonthVisiable = month !== this.currentDate this.calendarApi.gotoDate(month) }, /** * 返回以后月份 */ handleReturnCurrentMonth () { this.calendarApi.gotoDate(this.currentDate) this.btnReturnCurrenMonthVisiable = false this.selectMonth = this.currentDate }, }, components: { FullCalendar },}</script><style scoped>.toolbar-boxer { display: flex; flex-direction: row; justify-content: space-between;}.toolbar-right { text-align: right;}.calendar { margin-top: 1rem;}.margin-left { margin-left: 1rem;}</style>在页面中应用 Calendar.vue 组件 ...

June 26, 2022 · 2 min · jiezi

关于vue.js:前端工程化vue2webpack3项目迁移vite2踩坑总结

目录前言迁徙前后比照迁徙流程迁徙业务代码到vite我的项目我的项目开发阶段报错解决我的项目打包阶段解决总结一. 前言公司有个特地大保护工夫长的后盾管理系统,应用的是vue2和webpack3,尽管配置了很多编译优化,但启动和开发热更新速度仍然很慢,极大的影响了开发效率,于是筹备迁徙vite2来优化,当初已迁徙降级,来复盘记录下过程,至于vite速度快于webpack的起因,曾经有很多文章讲的很明确,这里只记录下迁徙过程和遇到的问题。 二. 迁徙前后比照比照迁徙前webpack3迁徙后vite2启动开发模式40秒3秒之内热更新6秒+1秒之内打包构建(vite不做低版本浏览器兼容)2分30秒40秒打包构建(vite做低版本浏览器兼容)2分30秒1分05秒三. 迁徙流程先创立新的vite我的项目新版vite我的项目默认是反对vue3的,须要把vue改成vue2版本后配置vite-plugin-vue2插件来反对vue2把我的项目代码改成vue2写法,确保新vite我的项目能够失常运行vue2把原webpack我的项目生产环境依赖复制到vite我的项目,剔除掉webpack相干的插件依赖复制原我的项目src文件代码和其余业务相干代码到新vite我的项目。新vite我的项目配置开发环境启动命令,依据报错信息来进行调整。在测试开发和打包环境都没问题后,替换原先的我的项目。四. 迁徙业务代码到vite我的项目4.1 创立新的vite我的项目关上vite官网,依照官网提醒创立新的vite我的项目(因为原先我的项目没有用ts,所以创立我的项目不选ts版本,包管理工具也仍然抉择是npm)。 npm init vite@latest my-vue-app -- --template vue创立实现后,应用vs code关上,关上命令行,执行npm i装置依赖 npm i装置依赖实现后,应用npm run dev启动我的项目 npm run dev启动实现后,关上我的项目地址http://localhost:3000/ 此时根本的vite2+vue3我的项目曾经启动胜利了,但此时vite反对的还是vue3版本的,咱们须要让vite反对vue2版本。 4.2 配置vite反对vue2此时关上vite.config.js,外面的代码为 import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/export default defineConfig({  plugins: [vue()]})@vitejs/plugin-vue插件是对vue3语法做反对,如果要反对vue2,须要用vite-plugin-vue2 第一步,从vite中删除 @vitejs/plugin-vue配置,从package.json文件中也删除。 npm uninstall @vitejs/plugin-vue -D第二步,装置vite-plugin-vue2依赖 npm install vite-plugin-vue2 -D第三步,在vite.config.js文件配置vite-plugin-vue2 import { defineConfig } from 'vite'import { createVuePlugin } from "vite-plugin-vue2";// https://vitejs.dev/config/export default defineConfig({  plugins: [createVuePlugin()]})第四步,批改vue版本由3改为2版本 npm install vue@2 -S第五步, 批改main.js,创立根vue实例写法改为vue2写法 ...

June 26, 2022 · 5 min · jiezi

关于vue.js:前端调试的正确姿势

前言平时工作量大,没有很多的自测工夫,难免会在写代码的时候一不小心写出BUG,同时常常测试的工夫又紧,留给开发批改缺点的工夫少,如果把握正确的调试姿态,就能疾速定位问题,从容应对。本文就次要讲一下前端调试的正确姿态,心愿可能给到大家帮忙。查找代码找到出错的中央能力debug,那怎么高效地找到出问题的代码。 console.log通常咱们遇到bug,第一直觉必定看console的报错信息, 这种报错,谬误起因和报错地位高深莫测,很不便就定位到出问题的代码,所以console.log尽管显得有点低级,但它简略实用,简略的缺点还是能够通过这种办法定位的,生产环境利用webpack去掉,就不会影响性能方面(有文章剖析过console.log会导致内存透露,能够查看)。 全局查找能够依据出问题的中央关键字,个别是dom元素的id,class,name或者页面中呈现的唯一性的中文汉字,搜寻就能够间接定位到代码。 调用堆栈堆栈是一个数据结构,每一个函数调用时都会将函数的指针和参数值保留到堆栈中,后进先出,最初调用的函数最先出栈。 能够通过打断点,查看Chrome开发面板的sources的call stack,能够查看追溯到源代码的地位,这个特地适宜console报错地位不明确的状况(比方报错指向vue.esm.js)。 剖析代码剖析代码最罕用的就是断点,一步步调试查看后果,定位问题。 断点间接在代码里debugger 在代码运行到该处就能触发断点,这对于sources面板不好找源码特地不便,麻烦之处就是须要记住去掉,不然后续开发常常会被断点。 sources源码中加断点 这种打断点十分不便,但有时候没那么容易找到源码。 dom批改时加断点 ajax申请断点 异样时断点 调试代码调试代码,最好放弃本地环境和线上环境始终,这样根本保障批改缺点不会被测试验证不过。那怎么放弃本地环境和线上保持一致呢。 vue-cli的本地代理性能,在配置文件devServer.proxy中配置反向代理,这种形式最好弄一个网站作为中介代理,开发的时候只须要批改网站上的配置,本地服务不必反复启动。本人搭建nginx反向代理,成果同上。后端接口设置Access-Control-Allow-Origin,利用cors跨域本地间接拜访线上数据,这种办法最不便,但须要后端共事配合。应用fiddler AutoResponder性能,间接把服务器重定向到本地目录(须要编写一个正则表达式),批改本地文件刷新浏览器就能看的批改成果。应用Chrome DevTools的Overrides性能,把服务器的文件映射到本地,能够反对在sources面板中批改办法间接失效,这种最适宜调试jsp这种不必webpack压缩的老我的项目。调试技巧(不断更新中)try catch最近工作中遇到一个问题,在应用的try catch的逻辑中,报错了,但浏览器报错指出了报错的地位,但没有给出具体的起因,这行的逻辑是指向了另外一个文件的函数,就是整条逻辑链比拟长,前面定位问题花了很长时间,前面我在思考这是try catch的起因还是我本人写法的起因。 js是单线程的,报错会导致后续的语句阻塞,try catch次要的用处就是躲避一些未知的谬误,避免语句阻塞。应用try catch是没有问题的,本人的写法应该也没有问题,就是定位问题的形式太繁多,其实是能够应用下面提到的异样捕捉来定位问题。 异样捕捉之后,能够通过调用堆栈看到调用程序,再顺藤摸瓜,而不是始终盯着报错的行进行验证。 顺便介绍一个Chrome新版本的一个调试性能,本来咱们打印个别须要写在源码里写console.log,后面也讲到console.log会影响性能,除了能够用webpack在生产环境的时候去掉console.log,新版本的Chrome还提供了一个更加不便的打印办法。 这样就不必在源码外面增加console.log语句了。 本文由博客一文多发平台 OpenWrite 公布!

June 25, 2022 · 1 min · jiezi

关于vue.js:Vue3中props和defineEmit的区别

因为之前写的我的项目都是基于react来开发和思考的,转到vue中从来未接触过emit这个概念,官网的文档写的也不是特地分明,(也或者是我的思维还尚未从react的编程理念直达进去)查阅了很多作者对于emit的解释,但从他们的答复中也并不能了解的特地透彻。 好在这几天通过公司我的项目的练手,本人又查阅了一些视频,终于明确了这个办法的根本逻辑。如果你是react转vue,那么本文将对你有很大的帮忙;如果你是vue的初学者,也不要胆怯,本文尽可能应用“说人话”的角度去解释。 浏览本文的前提条件,你须要对父子组件通信,靠props来实现这个概念有较好的了解。本文章将对props和emit的区别进行解说,来让你彻底明确emit到底是个什么货色,篇幅较长,急躁一点,学习常识切忌急功近利。 1.在这里咱们筹备一个App组件作为父组件和一个useEmit.vue文件作为子组件。(其余文件不须要看,都是我之前练习应用的。) App组件的构造也非常简单, useEmit组件的构造就一个按钮, 这是整体的页面构造: 2.这时候提出咱们的需要,当初子组件内有一个变量name,须要渲染到父组件内,咱们如何解决?(在这里咱们不思考变量晋升) 一.通过Props大抵的思路其实很简略,就是父组件提供一个函数,通过props传递给子组件,子组件通过调用这个函数,来将本人的变量给父组件传递过来。 1.咱们在App组件内定义一个函数。 2.思考:这个函数必定须要一个参数去接管儿子传递过去的信息。ok,当咱们拿到当前,咱们就去打印一下看看。 3.接下来就是传递给子组件,在自组件去调用就行。vue里须要留神,传递变量的时候须要在属性后面加:,来通知vue我前面双引号里传递的是一个变量。 留神:这里和react不一样的中央在于,也是我认为设计不好的一个中央,因为vue应用双引号的中央很多,非常容易让人产生纳闷,刚开始看公司代码的时候,因为那时候不晓得vue的设计理念,常常不晓得到底这个货色是字符串还是变量 这里额定交叉一下常识,在react里传递props,如果是传递变量,就会用{}来示意,字符串就用"" 4.子组件就得去接管这个办法,回到子组件。这里通过vue3的definProps去拿,并且定义好要传给父组件的变量name。不分明这个办法的能够看我上一篇文章。 在子组件身上<button>按钮绑定点击事件为调用这个办法。 5.回到页面看看成果。 控制台正确打印出了子组件的变量,下一步就是回到父组件去用变量接管即可。 6.,在App组件定义一个sonNames来接就完事了 而后点击按钮能够失常显示。 二.emit1.让咱们回到父组件首先咱们要搞明确---js原生事件这个名词和vue中提出的理念---自定义事件的区别。 咱们在父组件里定义了一个函数 而后间接在子组件身上绑定这个点击事件 这个@click其实就是代表着onclick这个原生js里原汁原味的那个鼠标点击事件。 2.那我忽然感觉click这几个字母好难看,我不喜爱怎么办?那我可不可以不写click这几个字母,写成myDIY能够吗?咱们试一试。 页面也好好的,控制台也没报错,vscode里也没报错,好神奇。那么问题来了,这个myDIY事件原生js里没有啊,你也没方法触发啊?什么?你说我点几下不就完事了?很遗憾,浏览器不意识这个myDIY事件,你说他就是点击事件,那浏览器还感觉它是键盘事件呢。 3.这时候子组件的props里也并没有myDIY这个属性。 肯定要转变本人的观点,这个写法看起来如同是父组件给子组件传递货色,然而并不是。你临时能够了解为子组件抢了父组件的办法getChildrenName筹备拿来本人用(对,没错,我就是这样了解的!不苟言笑)为什么说筹备呢?因为目前为止,你没方法触发这个myDIY事件。 ok,emit来了,这时候我再强调一点,肯定肯定留神,这不是父组件向子组件传递办法!!!而是子组件筹备自力更生 4.回到子组件内,咱们应用defineEmits来接管。 你都学Vue3了,我默认你应该会TS的哦,应该能够看进去什么意思吧?咱们正在束缚emit函数的类型,emit它是一个函数,它接管的第一个参数就是你自定义的那个事件也就是myDIY,它没有返回值所以就是void。 稍等,咱们看一下,如同有谬误 谬误是App组件传过来的,咱们回过头看看子组件抢过来的函数如同须要一个参数而咱们definEmit中如同并没给它传递参数 接下来返回子组件,给它加上。 5.ok,而后再给子组件的button绑定上咱们刚刚抢过来的那个函数 6.功败垂成,点击按钮也能够正确展现子组件的name 7.其实感觉有点像Object.prototype.call这办法的味儿了,emit如同就是那个call,扭转了父组件定义函数的指向,并且能够传递本人的参数。 总结 1.通过props来给父组件传货色,子组件始终用的都是他人的货色。 2.通过emit,如同这货色就是我的一样!

June 25, 2022 · 1 min · jiezi

关于vue.js:vmodel修饰符

有一些状况,咱们不心愿用户输出空格这种无意义的字符,或者其余不非法的字符能够通过上面的办法解决。禁止输出空格v-model.trim<el-input v-model="test"></el-input><!--增加修饰符--><el-input v-model.trim="test"></el-input>这样空格就无奈输出了。 或者(偶然会有问题): <el-input oninput="value=value.replace(/\s/g,'')" v-model="studentModel.name" />依照这个应用正则替换的思路,还能够实现其余成果,如下: 只能输出数字v-model.number<el-input v-model.number="studentModel.age" /><!-- 或者 --><el-input oninput="value=value.replace(/\D/g,'')" v-model="studentModel.name" />如果你用的是element-ui组件,更简单的校验能够放到el-form的validator中,如下: formRules: { name: [ {required: true, message: '请输出学员姓名'}, { validator:(rule,val,cb) => { val = val.replace(new RegExp(EmojiRanges.join('|'), 'g'), ''); val = val.trim(); if (val.includes('+') || val.includes('/') || val.includes('\\')) { return cb(new Error('名称中不可蕴含+/\\特殊字符')) } this.studentModel.name = val; return cb(); }, trigger: 'change' } ]}v-model 其余修饰符除了像 .trim 这样的 2.x 硬编码的 v-model 修饰符外,当初 3.x 还反对自定义修饰符: <ChildComponent v-model.capitalize="pageTitle" />把输出内容转换为大写。

June 24, 2022 · 1 min · jiezi

关于vue.js:Vuejs设计与实现学习总结第四章3副作用函数与函数栈

theme: channing-cyan highlight: monokai嵌套副作用函数是会嵌套的: effect(function effectFn1 (){ effect function effectFn2 () { }})当effectFn1执行时, effectFn2肯定会执行, 在 Vue.js 中的渲染函数就是在一个副作用函数中执行, 当组件发成嵌套时是就会呈现副作用函数的嵌套: effect(() => { Foo.render() effect(() => { Bar.render() })})在以后的版本中若effectFn1所绑定的属性产生获取操作时, 只会执行effectFn2, 因为设置了一个全局变量贮存以后激活的副作用函数,就意味着在同一时刻, 该变量存储的副作用函数只有一个. 在初始化之后, 该全局变量贮存的是effectFn2. 函数栈为解决嵌套的产生的问题, 能够用函数栈, 在副作用函数执行时, 将以后的副作用函数压入栈, 当副作用函数执行完结后弹出栈, 并让activeEffect始终指向栈顶的副作用函数 // 全局变量贮存以后激活的 effect 函数let activeEffectconst effectStack = []function effect(fn) { const effectFn = () => { cleanup(effectFn) // 调用以后的副作用函数时, 赋值给 全局变量 activeEffect = effectFn // 在调用副作用函数之前将该函数压入栈 effectStack.push(effectFn) fn() // 以后的副作用函数执行完结后, 出栈 effectStrack.pop() // activeEffect 还原为之前的值 activeEffect = effectStack[effectStack.length - 1] } effectFn.desp = [] effectFn()}

June 24, 2022 · 1 min · jiezi