• vue 项目中一些问题记录


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

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

    Watch immediate

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

    1. // bad
    2. created() {
    3. this.getsearchText();
    4. },
    5. watch: {
    6. searchText: 'getSearchText',
    7. }
    8. 复制代码

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

    1. // good
    2. watch: {
    3. searchText: {
    4. handler: 'getSearchText',
    5. immediate: true,
    6. }
    7. }
    8. 复制代码

    vue路由跳转打开新窗口

    使用 this.$router.resolve

    1. const openNewUrl=(url) => {
    2. let routeData = this.$router.resolve({path: url})
    3. window.open(routeData.href, '_blank')
    4. }
    5. 复制代码

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

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

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

    v-model拆分为:value和@input

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

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

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

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

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

    1. let el= document.querySelector('#target')
    2. window.addEventListener('mousedown', e => {
    3.   // 获取被点击的元素
    4.   const clickedEl = e.target;
    5.   
    6.   if (el.contains(clickedEl)) {
    7.    //在 "el "里面点击了
    8.   } else {
    9.    //在 "el "外点击了
    10.   }
    11. });
    12. 复制代码

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

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

    hookEvent

    组件内使用

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

    1. // bad
    2. mounted() {
    3. // 创建一个定时器
    4. this.timer = setInterval(() => {
    5. // ......
    6. }, 500);
    7. },
    8. // 销毁这个定时器。
    9. beforeDestroy() {
    10. if (this.timer) {
    11. clearInterval(this.timer);
    12. this.timer = null;
    13. }
    14. }
    15. 复制代码

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

    1. // good
    2. mounted() {
    3. let timer = setInterval(() => {
    4. // ......
    5. }, 500);
    6. this.$once("hook:beforeDestroy", function() {
    7. if (timer) {
    8. clearInterval(timer);
    9. timer = null;
    10. }
    11. });
    12. }
    13. 复制代码

    监听子组件生命周期函数

    原本

    1. //父组件
    2. <child
    3. :value="value"
    4. @childMounted="onChildMounted"
    5. />
    6. method () {
    7. onChildMounted() {
    8. // do something...
    9. }
    10. },
    11. // 子组件
    12. mounted () {
    13. this.$emit('childMounted')
    14. },
    15. 复制代码

    hooks

    1. //父组件
    2. <child
    3. :value="value"
    4. @hook:mounted="onChildMounted"
    5. />
    6. method () {
    7. onChildMounted() {
    8. // do something...
    9. }
    10. },
    11. 复制代码

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

    外部监听生命周期函数

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

    1. <template>
    2. <custom-select @hook:updated="onSelectUpdated" />
    3. template>
    4. <script>
    5. import CustomSelect from './components/custom-select'
    6. export default {
    7. components: {
    8. CustomSelect
    9. },
    10. methods: {
    11. onSelectUpdated() {
    12. console.log('custom-select组件的updated钩子函数被触发')
    13. }
    14. }
    15. }
    16. script>
    17. 复制代码

    vue跳转相同路径报错

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

    1. const originalPush = VueRouter.prototype.push
    2. const originalReplace = VueRouter.prototype.replace
    3. // push
    4. VueRouter.prototype.push = function push(location, onResolve, onReject) {
    5. if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
    6. return originalPush.call(this, location).catch(err => err)
    7. }
    8. // replace
    9. VueRouter.prototype.replace = function push(location, onResolve, onReject) {
    10. if (onResolve || onReject) return originalReplace.call(this, location, onResolve, onReject)
    11. return originalReplace.call(this, location).catch(err => err)
    12. }
    13. 复制代码

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

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

    1. // vue.config.js
    2. module.exports = {
    3. publicPath: './'
    4. }
    5. 复制代码

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

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

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

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

    1. div {
    2. display:fixed;
    3. height:100%;
    4. overflow:scroll;
    5. }
    6. 复制代码

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

    1. div {
    2. top: 0;
    3. bottom:0;
    4. position:fixed;
    5. overflow-y:scroll;
    6. overflow-x:hidden;
    7. }
    8. 复制代码

    require.context() 自动注册

    require.context():

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

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

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

    1. // 利用require.context()自动引入 除 index.js 外其他 js 文件
    2. const routerContext = require.context('./', true, /\.js$/)
    3. routerContext.keys().forEach(route => {
    4. // 如果是根目录的 index.js 、不处理
    5. if (route.startsWith('./index')) {
    6. return
    7. }
    8. const routerModule = routerContext(route)
    9. /**
    10. * 兼容 import export 和 require module.export 两种规范
    11. */
    12. routes = routes.concat(routerModule.default || routerModule)
    13. })
    14. 复制代码

    生产环境去除 console.log

    vue.config.js 中配置

    1. configureWebpack: (config) => {
    2. if (process.env.NODE_ENV === "production") {
    3. config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true;
    4. config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = [
    5. "console.log",
    6. ];
    7. }
    8. }
    9. 复制代码

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

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

    1. <templat>
    2. <el-form @submit.native.prevent >< /el-form >
    3. </templat>
    4. 复制代码

    el-select 下拉框样式修改

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

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

    1. 方法一
    1. @change="onChange($event,customParam)"
    2. 复制代码
    1. 方法二
    1. @change="((val)=>{changeEvent(val,args)})"
    2. 复制代码

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

    1. <el-dropdown trigger="click" @command="((val)=>{handleCommand(val,scope.row)})">
    2. <span class="el-dropdown-link">
    3. <i class="el-icon-more el-icon--right"></i>
    4. </span>
    5. <el-dropdown-menu slot="dropdown">
    6. <el-dropdown-item command="volumes">新增</el-dropdown-item>
    7. <el-dropdown-item command="log">查看</el-dropdown-item>
    8. <el-dropdown-item command="shell">更新</el-dropdown-item>
    9. <el-dropdown-item command="container">删除</el-dropdown-item>
    10. </el-dropdown-menu>
    11. </el-dropdown>
    12. 复制代码

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

    解决

    1. <el-input class="clear-number-input" type="number"></el-input>
    2. <style scoped>
    3. .clear-number-input ::v-deep input[type="number"]::-webkit-outer-spin-button,
    4. .clear-number-input ::v-deep input[type="number"]::-webkit-inner-spin-button {
    5. -webkit-appearance: none !important;
    6. }
    7. </style>
    8. 复制代码

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

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

    1. // 自动填充样式覆盖
    2. input:-internal-autofill-previewed,
    3. input:-internal-autofill-selected {
    4. -webkit-text-fill-color: #fff;
    5. transition: background-color 5000s ease-out 0.5s;
    6. }
    7. 复制代码

    巧用$options

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

    例如:

    1. this.value = this.$options.data().value;
    2. 复制代码

    dialog 里重置表单

    利用上面介绍的 $options 特性

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

    1. // 弹框打开时
    2. initForm(){
    3. this.$refs['form'] && this.$refs['form'].resetFields()
    4. this.form = this.$options.data.call(this).form;
    5. }
    6. 复制代码

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

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

    1. // 只能选择一个
    2. props: {
    3. type: String,
    4. validator(value) {
    5. return ['A', 'B', 'C'].indexOf(value) > -1
    6. }
    7. }
    8. 复制代码

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

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

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

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

    1. resetFields() {
    2. //...
    3. if (this.$listeners.resetFields) {
    4. // 自定义事件
    5. this.$emit('resetFields')
    6. } else {
    7. // 默认刷新列表事件
    8. this.loadList()
    9. }
    10. }
    11. 复制代码

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

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

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

    vue element 多个 Form 表单同时验证

    1. <template>
    2. <el-form ref="form1"></el-form>
    3. <el-form ref="form2"></el-form>
    4. <el-form ref="form3"></el-form>
    5. </template>
    6. <script>
    7. export default{
    8. methods: {
    9. onValidate() { // 保存操作
    10. const formArr =['form1', 'form2','form3']//三个form表单的ref
    11. const resultArr = [] //用来接受返回结果的数组
    12. let _self = this
    13. function checkForm(formName) { //封装验证表单的函数
    14. let result = new Promise(function (resolve, reject) {
    15. _self.$refs[formName].validate((valid) => {
    16. if (valid) {
    17. resolve();
    18. } else { reject() }
    19. })
    20. })
    21. resultArr.push(result) // 得到promise的结果
    22. }
    23. formArr.forEach(item => { // 根据表单的ref校验
    24. checkForm(item)
    25. })
    26. Promise.all(resultArr).then(values => {
    27. // 此时必填完成,做保存后的业务操作
    28. // ...
    29. console.log('success');
    30. }).catch(_ => {
    31. console.log('err')
    32. })
    33. },
    34. }
    35. }
    36. </script>
    37. 复制代码

    Vue中的method赋值为高阶函数

    1. <script>
    2. import { debounce } from "lodash";
    3. export default {
    4. methods: {
    5. search: debounce(async function (keyword) {
    6. // ... 请求逻辑
    7. }, 500),
    8. },
    9. };
    10. </script>
    11. 复制代码

    给 slot 插槽绑定事件

    • 1、作用域插槽 slot-scope 传方法
    1. <template>
    2. <slot change-display="changeDisplay">slot>
    3. <div v-show="visiable">*下拉框代码省略*<div>
    4. <template>
    5. <script>
    6. export default {
    7. data(){
    8. return {
    9. visiable: false
    10. }
    11. }
    12. methods:{
    13. changeDisplay(){
    14. this.visiable = !this.visiable
    15. }
    16. }
    17. }
    18. script>
    19. 复制代码

    使用:

    1. <dropdown v-model="value" :list="list">
    2. <button slot-scope="{changeDisplay}"
    3. @click="changeDisplay">{{value}}button>
    4. dropdown>
    5. 复制代码
    • 2、vnode 中对应的页面元素
    1. <!-- 伪代码:下拉框组件 -->
    2. <template>
    3. <slot></slot>
    4. <div v-show="visiable">*下拉框代码省略*<div>
    5. <template>
    6. <script>
    7. export default {
    8. data(){
    9. return {
    10. visiable: false
    11. reference: undefined
    12. }
    13. }
    14. methods:{
    15. changeDisplay(){
    16. this.visiable = !this.visiable
    17. }
    18. }
    19. mounted() {
    20. if (this.$slots.default) {
    21. this.reference = this.$slots.default[0].elm
    22. }
    23. if (this.reference) {
    24. this.reference.addEventListener('click', this.changeVisiable, false)
    25. // hook
    26. this.$once('hook:beforeDestroy', () => {
    27. this.reference.removeEventListener('click', this.changeVisiable)
    28. })
    29. }
    30. }
    31. }
    32. </script>
    33. 复制代码

    二次封装作用域插槽

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

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

    子组件

    1. <template>
    2. <Tree class="tree" v-if="items.length" :data="items" :options="options" :filter='search' ref="tree" v-model="treeModel">
    3. <!-- 作用域插槽 -->
    4. <template slot-scope="{node}">
    5. <span v-if="!$scopedSlots.default">{{node.text}}</span>
    6. <slot v-else :node="node"></slot>
    7. </template>
    8. </Tree>
    9. </template>
    10. 使用
    11. ```vue
    12. <template>
    13. <custom-tree ref="tree" checkbox :data='data' :props="{children:'children',text: 'text'}">
    14. <template slot-scope="{node}">
    15. <span class="tree-text">
    16. <!-- 自定义插槽内容 -->
    17. <template v-if="!node.children">
    18. <van-icon name="user-o" size="18" class="icon" />
    19. {{ node.text }}
    20. </template>
    21. <template v-else>
    22. {{ node.text }}
    23. </template>
    24. </span>
    25. </template>
    26. </custom-tree>
    27. </template>
  • 相关阅读:
    kafka伪集群部署,使用KRAFT模式
    NCCL源码解析③:机器内拓扑分析
    孩子近视家里的灯如何选择?盘点眼科医生分享的护眼台灯
    设计模式之命令模式
    前端技术点滴整理-1
    大气环境一站式技能提升:SMOKE-CMAQ实践技术
    Postgresql中VACUUM操作原理和应用
    【无代码爬虫】web scraper 之 采集多个内容
    Git笔记
    一文搞定,JMeter的三种参数化方式
  • 原文地址:https://blog.csdn.net/sinat_17775997/article/details/126401717