首页 > 编程学习 > vue 项目开发,我遇到了这些问题

vue 项目开发,我遇到了这些问题

发布时间:2022/10/1 6:11:03

日常工作中,使用vue进行项目开发居多,因此把一些vue项目常见问题进行总结,在此记录,避免以后开发中做过多的重复工作。

而掌握一些有用的技巧,使用一些更高级的技术点,也能让我们成为更好的 Vue 开发者

Watch immediate

当 watch 一个变量的时候,初始化时并不会执行,如下面的例子,你需要在created的时候手动调用一次。

// bad
created() {this.getsearchText();
},
watch: {searchText: 'getSearchText',
} 

你可以添加immediate属性,这样初始化的时候也会触发,代码也就可以简化成这样

// good
watch: {searchText: {handler: 'getSearchText',immediate: true,}
} 

vue路由跳转打开新窗口

使用 this.$router.resolve

const openNewUrl=(url) => {let routeData = this.$router.resolve({path: url})window.open(routeData.href, '_blank')} 

el-input 限制输入框只能输入数字

<el-input v-model.number="num"onkeyup="this.value = this.value.replace(/[^\d.]/g,'');"></el-input> 

el-input 过滤特殊字符或身份证脱敏

v-model拆分为:value和@input

<el-input :value="input" @input='e => input = idCardValid (e)' placeholder="请输入内容"></el-input> 
 methods:{idCardValid(val){const idCard= val.replace(/^(\d{6})\d+(\d{4})$/, "$1******$2")console.log(idCard)return idCard} 
}, 

使用a标签下载本地静态资源文件

  • 1、public目录下存放要下载的静态资源
  • 2、a 标签下载
 <a href="/demo.rar" download="demo.rar">点击下载</a> 

检测元素外部(或内部)的单击

例如我们检测一个id为 target 的 div 目标元素

let el= document.querySelector('#target')
window.addEventListener('mousedown', e => {
  // 获取被点击的元素
  const clickedEl = e.target;
  
  if (el.contains(clickedEl)) {
   //在 "el "里面点击了
  } else {
   //在 "el "外点击了
  }
}); 

iframe框架内页面控制父框架页面跳转到某地址

const { href } = this.$router.resolve({ path: "/index", query: { key: key } });
// iframe 控制父页面跳转
window.parent.window.location.href = href 

hookEvent

组件内使用

开发中用到定时器时我们一般这样

// bad
mounted() {// 创建一个定时器this.timer = setInterval(() => {// ......}, 500);},// 销毁这个定时器。
beforeDestroy() {if (this.timer) {clearInterval(this.timer);this.timer = null;}
} 

而借助 hook,可以更方便维护

// good
mounted() {let timer = setInterval(() => {// ......}, 500);this.$once("hook:beforeDestroy", function() {if (timer) {clearInterval(timer);timer = null;}});} 

监听子组件生命周期函数

原本

//父组件
<child
 :value="value"
 @childMounted="onChildMounted"
/>
method () {
 onChildMounted() {
 // do something...
 }
},

// 子组件
mounted () {
 this.$emit('childMounted')
}, 

hooks:

//父组件
<child:value="value"@hook:mounted="onChildMounted"
/>
method () {onChildMounted() {// do something...}
}, 

在Vue组件中,可以用过$on,$once去监听所有的生命周期钩子函数,如监听组件的 updated 钩子函数可以写成 this.$on('hook:updated', () => {})

外部监听生命周期函数

我们有时会遇到这样的情况,用了一个第三方组件,当需要监听第三方组件数据的变化,但是组件又没有提供change事件时。我们可以利用Vue 提供的@hook:updated 来监听组件的 updated 生命钩子函数

<template><!--通过@hook:updated监听组件的updated生命钩子函数--><!--组件的所有生命周期钩子都可以通过@hook:钩子函数名 来监听触发--><custom-select @hook:updated="onSelectUpdated" />
</template>
<script>
import CustomSelect from './components/custom-select'
export default {components: {CustomSelect},methods: {onSelectUpdated() {console.log('custom-select组件的updated钩子函数被触发')}}
}
</script> 

vue跳转相同路径报错

在vue的router的js中添加下面代码,new VueRouter 前

const originalPush = VueRouter.prototype.push
const originalReplace = VueRouter.prototype.replace
// push
VueRouter.prototype.push = function push(location, onResolve, onReject) {if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)return originalPush.call(this, location).catch(err => err)
}
// replace
VueRouter.prototype.replace = function push(location, onResolve, onReject) {if (onResolve || onReject) return originalReplace.call(this, location, onResolve, onReject)return originalReplace.call(this, location).catch(err => err)
} 

Vue-cli3 打包后报错 Failed to load resource: net::ERR_FILE_NOT_FOUND

根目录下新建文件 vue.config.js

// vue.config.js
module.exports = {publicPath: './'
} 

默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如 www.my-app.com/。如果应用被部署在一个… www.my-app.com/my-app/,则设置 publicPath 为 /my-app/。

这个值也可以被设置为空字符串 (‘’) 或是相对路径 (‘./’),这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径。

css解决fixed布局不会出现滚动条的问题

如果我们布局的是fixed并且想要高度为100%的时候,我们一般会这样设置:

div {display:fixed;height:100%;overflow:scroll;
} 

但是这样的话不会出现滚动条,设置

div {top: 0;bottom:0;position:fixed;overflow-y:scroll;overflow-x:hidden;
} 

require.context() 自动注册

require.context():

你可以通过 require.context() 函数来创建自己的 context。

可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。

webpack 会在构建中解析代码中的 require.context() 。

// 利用require.context()自动引入 除 index.js 外其他 js 文件
const routerContext = require.context('./', true, /\.js$/)
routerContext.keys().forEach(route => {// 如果是根目录的 index.js 、不处理if (route.startsWith('./index')) {return}const routerModule = routerContext(route)/** * 兼容 import export 和 require module.export 两种规范 */routes = routes.concat(routerModule.default || routerModule)
}) 

生产环境去除 console.log

vue.config.js 中配置

configureWebpack: (config) => {if (process.env.NODE_ENV === "production") {config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true;config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ["console.log",];}
} 

vue+elementUI在输入框中按回车键会刷新页面

当一个 form 元素中只有一个输入框时,在该输入框中按下回车应提交该表单。如果希望阻止这一默认行为,可以在 <el-form> 标签上添加 @submit.native.prevent。

<templat>
<el-form @submit.native.prevent >< /el-form >
</templat> 

el-select 下拉框样式修改

使用样式穿透修改下拉框样式,你会发现打死都不生效,那是因为下拉框是默认挂载在 body 下面。解决办法:设置 :popper-append-to-body="false",然后再用样式穿透

element-ui select组件change事件传递多个参数的方法

1.方法一

@change="onChange($event,customParam)" 

2.方法二

@change="((val)=>{changeEvent(val,args)})" 

其他组件的的默认事件同样的方法传递

<el-dropdown trigger="click" @command="((val)=>{handleCommand(val,scope.row)})"><span class="el-dropdown-link"><i class="el-icon-more el-icon--right"></i></span><el-dropdown-menu slot="dropdown"><el-dropdown-item command="volumes">新增</el-dropdown-item><el-dropdown-item command="log">查看</el-dropdown-item><el-dropdown-item command="shell">更新</el-dropdown-item><el-dropdown-item command="container">删除</el-dropdown-item></el-dropdown-menu>
</el-dropdown> 

el-input type=number 去除聚焦时的上下箭头

解决

<el-input class="clear-number-input" type="number"></el-input>

<style scoped> .clear-number-input ::v-deep input[type="number"]::-webkit-outer-spin-button,
.clear-number-input ::v-deep input[type="number"]::-webkit-inner-spin-button {-webkit-appearance: none !important;
} </style> 

chrome表单自动填充导致input文本框背景失效

我们在开发登录页的时候经常遇到,登陆页的表单自动填充导致input文本框背景失效的问题。

// 自动填充样式覆盖
input:-internal-autofill-previewed,
input:-internal-autofill-selected {-webkit-text-fill-color: #fff; transition: background-color 5000s ease-out 0.5s;
} 

巧用$options

$options是一个记录当前Vue组件的初始化属性选项,当我们想把 data 里的某个值重置为初始值时,非常有用

例如:

this.value = this.$options.data().value; 

dialog 里重置表单

利用上面介绍的 $options 特性

我们经常的业务场景是这样:一个el-dialog中有一个el-form,而且我们通常是新增和编辑复用同一个组件,现在我们要求每次打开el-dialog时都要重置el-form里的数据,并且清除校验状态。

 // 弹框打开时
initForm(){this.$refs['form'] && this.$refs['form'].resetFields()this.form = this.$options.data.call(this).form;
} 

将一个 prop 限制在一个类型的列表中

我们在使用 prop 时,可能会有时候需要判断该 prop 是否在我们规定的范围内(或者说规定的值内),这个时候我们可以使用 prop 定义中的 validator 选项,将一个 prop 类型限制在一组特定的值里。

 // 只能选择一个props: {type: String,validator(value) {return ['A', 'B', 'C'].indexOf(value) > -1}} 

validator 函数接收一个prop值,如果 prop 有效或无效,则返回 true 或 false。

Vue在子组件中判断父组件是否传来事件

在做二次封装时,我们经常用到,v-bind="$attrs"v-on="$listeners"进行多层组件监听,那么我们还可以利用 $listeners在子组件中判断父组件是否传来事件

例如我们封装一个搜索组件,里面有重置按钮,当我们点击重置按钮时,默认操作是清空搜索栏的值并且刷新列表,而如果父组件传来事件,则以自定义事件为准,即我们想点击重置按钮做一些其他的自定义操作。

resetFields() {//...if (this.$listeners.resetFields) {// 自定义事件this.$emit('resetFields')} else {// 默认刷新列表事件this.loadList()}
} 

同一组件上存在多个table进行tabs和v-if/v-show切换时,多表格的数据会相互混淆,串在一起,引发bug

为每个table指定对应且唯一的key属性。

其他一些类似的问题也可以尝试为其添加key属性来解决

vue element 多个 Form 表单同时验证

<template>
<el-form ref="form1"></el-form>
<el-form ref="form2"></el-form>
<el-form ref="form3"></el-form>
</template>
<script>
export default{methods: {onValidate() { // 保存操作const formArr =['form1', 'form2','form3']//三个form表单的refconst resultArr = [] //用来接受返回结果的数组let _self = thisfunction checkForm(formName) { //封装验证表单的函数let result = new Promise(function (resolve, reject) {_self.$refs[formName].validate((valid) => {if (valid) {resolve();} else { reject() }})})resultArr.push(result) // 得到promise的结果}formArr.forEach(item => { // 根据表单的ref校验checkForm(item)})Promise.all(resultArr).then(values => { // 此时必填完成,做保存后的业务操作//...console.log('success');}).catch(_ => {console.log('err')})},}
}
</script> 

Vue中的method赋值为高阶函数

<script>import { debounce } from "lodash";export default {methods: {search: debounce(async function (keyword) {// ... 请求逻辑}, 500),},};
</script> 

给 slot 插槽绑定事件

  • 1、作用域插槽 slot-scope 传方法
<!-- 伪代码:下拉框组件 -->
 
<template><slot change-display="changeDisplay"></slot><div v-show="visiable">*下拉框代码省略*<div>
<template>
 
<script>
export default {data(){return {visiable: false}}
 methods:{changeDisplay(){this.visiable = !this.visiable}}
}
</script> 

使用:

<!--使用下拉弹窗组件-->
<dropdown v-model="value" :list="list"><button slot-scope="{changeDisplay}" @click="changeDisplay">{{value}}</button>
</dropdown> 
  • 2、vnode 中对应的页面元素
<!-- 伪代码:下拉框组件 -->
<template><slot></slot><div v-show="visiable">*下拉框代码省略*<div>
<template>
 
<script>
export default {data(){return {visiable: falsereference: undefined}}
 methods:{changeDisplay(){this.visiable = !this.visiable}}
 mounted() {if (this.$slots.default) {this.reference = this.$slots.default[0].elm}if (this.reference) {this.reference.addEventListener('click', this.changeVisiable, false)// hookthis.$once('hook:beforeDestroy', () => {this.reference.removeEventListener('click', this.changeVisiable)})}}
}
</script> 

二次封装作用域插槽

在二次封装组件时,我们知道可以通过判断 $slots.xxx 是否存在来判断我们在使用这个组件时是否传递了插槽内容。从而更好的定制默认的插槽内容。

那么在二次封装一个原本具有作用域插槽的组件时,我们可以通过 $scopedSlots.xxx 来进行判断

子组件

<template>
<Tree class="tree" v-if="items.length" :data="items" :options="options" :filter='search' ref="tree" v-model="treeModel">
<!-- 作用域插槽 --><template slot-scope="{node}"><span v-if="!$scopedSlots.default">{{node.text}}</span><slot v-else :node="node"></slot></template>
</Tree>
</template>

使用

```vue
<template><custom-tree ref="tree" checkbox :data='data' :props="{children:'children',text: 'text'}"><template slot-scope="{node}"><span class="tree-text"><!-- 自定义插槽内容 --><template v-if="!node.children"><van-icon name="user-o" size="18" class="icon" />{{ node.text }}</template><template v-else>{{ node.text }}</template></span></template></custom-tree>
</template> 

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号