Vue 相关

全局事件总线的大致使用流程:

注意:一定要记得解绑全局事件

//创建一个全局事件总线 、、在main.js入口文件中挂载
new Vue({
    beforCreate(){
        Vue.prototype.$bus=this;    
    }
})

//需要	接收	数据的组件
{
    //绑定全局事件总线上的 自定义事件
    mounted(){
        this.$bus.$on("需要绑定的事件"""绑定事件后需要触发的函数"")    
    }
    //解绑全局事件总线上的 自定义事件
    beforeDestroy(){
        this.$bus.$off("需要解绑的事件")    
    }
}
    
//需要   发送   数据的组件          
{
    this.$bus.$emit("需要触发的自定义事件",触发事件时需要传入的参数)
}                    
  

bus 事件demo

// app 组件中
import Vue from 'vue'
export default {
  name: 'app',
  beforeCreate() {
    Vue.prototype.$bus = this
  },
}

// 监听事件的组件
  methods: {
    bustestHandle(){
        console.log('全局事件总线,bus孙组件成功调用爷组件中的事件')
    }
  },
    mounted() {
    this.$bus.$on('random', (data) => { // random 这个名字可以随意起,但是必须和 $emit 触发事件的名字相一致.
      this.bustestHandle()
      console.log(data,'参入的参数')
    })
  },
      beforeDestroy(){
        this.$bus.$off("random")    
    }

// 触发事件的组件
    <el-button @click="busHandle">触发爷组件事件</el-button>
    busHandle(){
        this.$bus.$emit('random','天生我材必有用')  // random 这个名字可以随意起,但是必须和 $on 监听事件的名字相一致.
    }

provide inject 使用注意事项

provide 用于为后代组件提供数据
inject 用于接收 顶层组件提供的数据

注意: 如果在后代组件中使用 provide ,在顶层组件中使用 inject ,顶层组件会拿不到后代组件提供的数据.

必须是 父组件提供(provide),子组件注入(inject)!!!

provide 使用注意事项

provide 对象式使用

官方文档@provide方法文档
对象式使用时,无法访问的指向 vue 实例的 this

export default {
  provide: {
    message: "hello!",
  },
};

provide 函数式使用

函数式使用时,可以访问到指向 vue 势力的 this

使用 函数式provide 提供一个函数时,需要注意.

  • 如果提供的函数表达式是 message:function(){}(函数字面量),则访问不到指向 vue 实例的 this.
  • 如果提供的函数表达式是 messsage:()=>{}(箭头函数),则可以访问到指向 vue 实例的 this.
export default {
  data() {
    return {
      message: "hello!",
    };
  },
  provide() {
    // 使用函数的形式,可以访问到 `this`
    return {
      message: this.message,
      selectUserChange: () => {
        console.log(this); // 箭头函数方式,可以访问到 this
      },
      selectUserChangeFunction: function () {
        console.log(this); // function 字面量方式 ,访问不到 this
      },
      //   上面的声明方式等同于
      selectUserChangeFunction() {
        console.log(this); // function 字面量方式 ,访问不到 this
      },
    };
  },
};

$attrs$listeners 使用

参考文章@今天写注释了吗

$attrs 的使用:

  1. 在爷组件(index.vue)中,类似 props 传值,将需要传递的值绑定在父组件上。
  2. 在父组件中,也是类似 props 传值,但是这里传递的就不是值了,而是$attrs。
  3. 在孙组件中,接收 props,这样就可以在孙组件中使用这个数据了。(需要注意的是父组件中不需要接收 props,只要在孙组件中接收就可以。)

$listeners 的使用:

  1. 在爷组件(index.vue)中,绑定事件。
  2. 在父组件中,也是类似绑定事件,但是这里绑定的不是具体的事件,而是 v-on=“$listeners”。
  3. 在孙组件中,需要的时候触发($emit)这个事件即可。 然后上代码:
:正常 props 爷孙组件传值

//爷组件
<grand-father>
    <FatherComponent :message="message"></FatherComponent>
</grand-father>

// 父组件
<FatherComponent >
    <SonComponent :message="message"></SonComponent>
</FatherComponent>

props:['message']

//子组件
<SonComponent >
    {{message}}
</SonComponent>

props:['message']

: 使用 $attrs 和 $listener
//爷组件
<grand-father>
    <FatherComponent :message="message"></FatherComponent>
</grand-father>

// 父组件
<FatherComponent >
    <SonComponent v-bind="$attrs"></SonComponent>
</FatherComponent>

//子组件
<SonComponent >
    {{message}}
</SonComponent>

props:['message']

keep-alive 组件使用

  1. 默认情况下,动态切换组件时,消失的组件会被销毁,出现的组件会被重新创建.可能会出现组件重复创建销毁的情况,如果在创建组件时,不希望组件被重复的销毁可以使用keepalive 内置组件,将某些自定义组件缓存起来,组件消失时就不在被销毁了.
  2. 被keep-alive 缓存的组件,会在原有声明周期函数的基础上,在增加两个钩子函数,activated激活时触发:deactivated组件隐藏时触发.
html 结构中
<keep-alive  exclude="组件名">
    
    //include:要缓存的组件name属性值:
    //exclude: 要排除缓存的组件name;
    
    	<component  :is="组件结构名">

</keep-alive>
    
script 结构中:
被keep-alive  缓存的组件,会增减两个钩子函数,  activated(激活时触发)   deactivated(组件隐藏时触发)
activated(){
    console.log('组件被激活,重新显示')
}deactivated(){
    console.log('组件失去激活状态,重新隐藏了。')
}
    

vue中的事件修饰符

1、prevent:阻止默认事件(常用)
2、stop:阻止事件冒泡(常用)
3、once:事件只触发一次(常用);
4、capture:使用事件的捕获模式;
5、self:只有event.target 是当前操作的元素时,才触发事件。
6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕。

//阻止事件默认行为
<button @click.prevent="info()"></button>

//阻止事件冒泡
<button @click.stop="info()"></button>

//事件只触发一次
<button @click.once="info()"></button>

//使用事件的捕获模式
<button @click.capture="info()"></button>

//e.target  当前操作元素。
<button @click.self="info()"></button>

//passvie 事件的默认行为立即执行,无需等待事件回调执行完毕。
<button @click.passive="info()"></button>

v-model 的修饰符

  1. .lazy修饰符
  2. .number修饰符
  3. .trim修饰符

<!--  lazy修饰符:使得用户在输入数据之后,当数据失去焦点或点击回车时,才会进行数据的更新-->
  <input type="text" v-model.lazy="message">
  <h2>用户输入的内容: {{message}}</h2>


<!--  number修饰符:将用户输入的类型由string型转为number型-->
  <input type="number" v-model.number="num">
  <h2>{{num}}: {{typeof num}}</h2>

<!--  trim修饰符:将用户输入的文本信息中开头的空格,结尾的空格都删除-->
  <input type="text" v-model.trim="name">
  <h2>用户输入的内容: {{name}}</h2>

vue 路由守卫

全局路由守卫

  • 前置全局路由守卫:router.beforeEach
  • 后置全局路由守卫:router.afterEach

独享路由守卫(只有前置独享路由守卫)

  • 前置独享路由守卫:beforeEnter

组件内路由守卫

  • beforeRouteEnter,进入该组件时调用;
  • beforeRouteLeave,离开该组件时调用;
//配置路由
//全局路由守卫,和独享路由守卫配置再 router.js文件中;
const router=[
    {
        path:"/",
        component:App,   
        //前置独享路由守卫
        beforeEnter:(to,from,next)=>{
                    
        } 
    },
]

//全局前置路由守卫
router.beforeEach((to,from,next)=>{
    //这里可以写 鉴定权限的逻辑
    
    //鉴定权限的逻辑内可以写,token验证的逻辑
})
//全局后置路由守卫
router.beforeAfter((to,from,next)=>{
    //全局后置路由守卫,这里面可以写 路由跳转后 页签的改变;
})

vue中router-link 自带的active-class类名属性

主要实现的功能

  • 点击实现高亮状态
<style>
   ._active{
      background-color : red;
   }
</style>
<p>
   <router-link v-bind:to = "{ path: '/route1'}" active-class = "_active">Router Link 1</router-link>
   <router-link v-bind:to = "{ path: '/route2'}" tag = "span">Router Link 2</router-link>
</p>

active-class 属性的使用

  1. 在 router-link 上绑定 active-class 属性
    • 可能会出现 两个 路由同时高亮的情况;
    • 解决: 在 “/” 路径 的 router-link 添加一个 exact 属性
html代码如下:
 	  <router-link to="/" active-class="_active" exact >Test</router-link> |
      <router-link to="/Test2" active-class="_active">Test2</router-link> |
      <router-link to="/Test3" active-class="_active">Test3</router-link> |
      <router-link to="/Test4" active-class="_active">Test4</router-link> |
      
css代码如下:
    > a {
      font-weight: bold;
      color: #2c3e50;
    }
    > ._active {
      color: #42b983;
    }

vue中存入一个对象。

参考文章@skyblue_afan
参考文章@洛抒

  1. router路由编程式传参,一般两种方式。一种是query传参,另外一种则是params传参。由于params传参刷新页面,会导致数据丢失。所以采用query传参方式比较多,但当使用query传递对象,数组时,刷新页面会报[object Object],数据无法使用

思路 :

  • 先使用JSON.stringify()转为字符串,字符串为单条数据,刷新页面数据就不会丢失,再将字符串通过JSON.parse()转回来就可以。
 handelDetails(row) {
      this.$router.push({
        path: "details",
        query: {
          row: JSON.stringify(row),//先转为字符串
          sataus: "Editor",
        },
      });
    },

 created() {
    // 接受传递的参数
    console.log(this.$route.query.sataus, '单条数据')
    console.log(JSON.parse(this.$route.query.row), '对象数据');//转为对象
  },

vue 中关于 $emit的使用

参考文章@榴莲不好吃
大概思路:

  • 在子组件上自定义一个事件,然后子组件通过触发这个事件来 修改父组件的值.(或者说是 父组件接收子组件中的值.)
子组件:
<template>
  <div class="train-city">
    <h3>父组件传给子组件的toCity:{{sendData}}</h3> 
    <br/><button @click='select(`大连`)'>点击此处将‘大连’发射给父组件</button>
  </div>
</template>
<script>
  export default {
    name:'trainCity',
    props:['sendData'], // 用来接收父组件传给子组件的数据
    methods:{
      select(val) {
        let data = {
          cityname: val
        };
        this.$emit('showCityName',data);//select事件触发后,自动触发showCityName事件
      }
    }
  }
</script>
父组件:
<template>
    <div>
        <div>父组件的toCity{{toCity}}</div>
        <train-city @showCityName="updateCity" :sendData="toCity"></train-city>
    </div>
<template>
<script>
  import TrainCity from "./train-city";
  export default {
    name:'index',
    components: {TrainCity},
    data () {
      return {
        toCity:"北京"
      }
    },
    methods:{
      updateCity(data){//触发子组件城市选择-选择城市的事件
        this.toCity = data.cityname;//改变了父组件的值
        console.log('toCity:'+this.toCity)
      }
    }
  }
</script>

vue 中点击路由跳转一个新页面

参考文章@就像风1样
参考文章@浊清。。。
注意:

  • resolve页面跳转可用新页面打开
  • 2.1.0版本后,使用路由对象的resolve方法解析路由,可以得到location、router、href等目标路由的信息。得到href就可以使用window.open开新窗口了
方法一:给 router-link 标签设置 tag="a" 和 target="_blank"  两个属性.

<router-link tag="a" target="_blank" :to="{name:'detail',query:{goodsId:'1111'}}">热门好货</router-link>

方法二:使用resolve方法将路径转换出来,window来打开

const new = this.$router.resolve({name: '/detail', params: {id: e}})
window.open(new.href,'_blank')
   
const {href} = this.$router.resolve({
        name: 'foo',
        query: {
          bar
        }
      })
window.open(href, '_blank')

vue中 动态路由挂载

  1. 在这里插入图片描述
    2.在这里插入图片描述

vue中 v-on监听多个方法

参考文章@Billow_lamb
大致思路:

  • 写成一个对象形式: v-on:{ click:fn1() , foucs:fn2() }
例一:
 <el-input
   v-model="value"
   v-on="{
     input:onInput,
     focus:onFocus,
     blur:onBlur
   }"
  >
  </el-input>
例二:
<el-button v-on="{mouseenter: onEnter,mouseleave: onLeave}">鼠标进来1</el-button>

vue中 npm run build 打包后静态资源的配置 vue.config.js的配置

参考文章@ruo水水水

  • cli3中bathUrl 已经替换为 publicPath在这里插入图片描述

vue中 vue.config.js 文件的相关配置项

vue官方在线文档
参考文章@muzidigbig

vue中 如何引入自定义字体

参考文章-1@如光不息丶
参考文章-2@汤圆真的好可爱
参考文章-3@昨夜太平长安888

注意 :

  • 在vue项目引入 .ttf 字体文件的时候 ,一点要点双击该文件 进行 本地的安装…
  • 在需要添加该字体的类名下 添加 该属性.
  • 全局引入该字体时需要将原有的字体样式清除.如下图
    • 在这里插入图片描述

1.首先你要先下载你需要的字体

2. 在你的项目 src 文件下创建一个文件夹

在这里插入图片描述

3. 其中 css 文件里面设置一下样式

@font-face {
  font-family: "fzqkbysjt";
  src: url('FZQKBYSJW.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
 }

4. 在main.js中引入字体样式

// 引入字体样式
import './common/fonts/font.css';

5. 完成以上步骤就可以在需要用到的样式中直接用啦

方法一:在需要用到的样式中直接用
.fongt{
	//fzqkbysjt为上面CSS文件中自定义的字体名
	font-family: fzqkbysjt;
} 

方法二:在 app.vue 文件中全局引入
	<style>
		@import "./font-style/font.css" ;
		body {
		font-family: PingFang;
		}
	</style>

vue中 如何使用 slot 插槽

参考文章@107098
参考文章@前端不释卷leo
(1)匿名插槽:没有名字的插槽
(2)具名插槽:有名字(name属性)的插槽
(3)作用域插槽:子组件中的数据只能子组件访问到,但是我们的插槽往往定义在父组件中,这个时候就需要作用域插槽来打通他们之间的关系,然后就能在父组件中愉快的使用子组件的数据了

  • 注意: v-slot 只能添加在 template上 (只有一种例外情况),这一点和已
    经废弃的 slot attribute 不同。
//将插槽放在 子组件中。(占个坑位)
<template>
  <div>
      <slot name="content"></slot>
  </div>
</template>

//在父组件中使用插槽
<template>
  <div>
    <slot-components>
      <template #content>
        <h3>再组件标签中放入插槽内容</h3>
      </template>
      
		第二种写法:
		
	<template v-slot:content>
        <h3>再组件标签中放入插槽内容</h3>
      </template>
    </slot-components>
  </div>
</template>

注意:v-slot 只能添加在 <template>  (只有一种例外情况),这一点和已
经废弃的 slot attribute 不同。

插槽传参:

插槽组件:
<template>
  <div>
      <slot name="content" :info="info"></slot>
  </div>
</template>
父组件取参数:
<template>
  <div>
    <slot-components>
      <template #content="slotProps">
        <h3>再组件标签中放入插槽内容</h3>
		<h3>{{slotProps.info}}</h3>
      </template>
      
		第二种写法:
		
	<template v-slot:content="slotProps">
        <h3>再组件标签中放入插槽内容</h3>
        <h3>{{slotProps.info}}</h3>
      </template>
    </slot-components>
  </div>
</template>

vue中 打包文件后如何打开

  1. 首先需要再 vue.config.js 文件中配置 下面的配置
    • publicPath:’ . / ’
  2. 再 dist 目录下找到 index.html 文件 双击打开即可.

打包完成后,路由跳转可能会出现空白页,这有可能是路由模式造成的问题.

vue 中 父组件给子组件绑定 v-model

  • 父组件在子组件上添加 v-model 属性
  • 子组件可以通过 prop 接收 value 来获取.
  • 父组件的model 值改变的时候, 子组件通过 接收的value 值也会发生改变
    参考文章@荣光无限
父组件:
    <input type="text" v-model="testValue" />
    <children v-model="testValue"></children>
子组件:
  props: ["value"],

vue .sync 修饰符

  • 主要用于父子组件传递 参数.
  • 子组件可以 使用 $emit(‘update:属性名’,参数) 来实现子组件改变父组件状态.
  • 参考视频@前端小野森森
  • 参考文章@王+V
    • 技巧:
      • 使用 .sync 修饰符时,变量计量使用 ‘ 驼峰 ’ 写法。
      • 不使用 .sync 修饰符时, 变量尽量使用 ‘ 短横杠 ’ 写法。
父组件:
    <children :testValue.sync="testValue"></children>
子组件触发:
	 testHandle() {
      this.$emit("update:testValue", "新标题");
    },

不使用.sync 时,需要这样写.
    <children @update:testValue="testValue = $event"></children>
    //$event 就是子组件修改过后的参数.
子组件触发:
	testHandle() {
      this.$emit("update:testValue", "新标题");
    },

使用.sync 传递一个对象的时:
    <children v-bind.sync="formInline"></children>
    //传递对象的时候,不是把formInline整个对象传给子组件,而是把
    //formInlin对象里的每一个属性给传给子组件,
    //子组件接收的时候,需要分别接收.

Vue 把 this 赋值给 that 操作.

在javascript中,this用以指代当前对象,但是this却不是固定不变的. 在axios 的回调函数中,this 指向axios 本身 , 会出现 this.data 未定义的情况. 我们只需要在 请求外的函数中 把 this 赋值给 that 即可解决这种问题.
参考文章@起个名字好难__

 methods:{
        searchMusic() {
            var that=this;
            axios.get("url" + this.query).then(
                function (res) {
                	this.musicList=res.data.result.songs; //undefind
                    that.musicList=res.data.result.songs; // [ ... ]
                }
            ).catch(function (err) {
                console.log(err);
            })
        }

vue 路由传参的方式

参考文章@一羊迁徙

  • params 传参
  • 路由属性配置传参
  • query 传参

注意:

  • params 传参,刷新页面时,参数 ’ ’ 丢失,
  • query 传参,刷新页面时,参数 ’ 不会 ’ 丢失.
  • params 不能与 path 一起使用
  • !!! path 路由跳转, 只 支持 query 传参.
  • !!! name 路由跳转, 同时支持 query 传参 和 params 传参
// 方式一:params 传参
        toDetail(){
            this.$router.push({
                name:"AntMemu",//值是在配置路由规则时给路由的命名,相当于别名
                params:{menu:this.leftMenu}//传递过去的参数
            })
        }
//配置路由规则   
{
    path: '/AntMemu',
    name: 'AntMemu',//命名路由
    component: AntMemu,
    props:true//子组件开启props传参
  }
 //获取路由的参数   (刷新页面,数据会丢失)
menu:this.$route.params.menu//写在data函数中即可


//方式二:路由属性配置传参
toDetail(){
            this.$router.push({
                path:`/AntMemu/${this.id}`,//值是在配置路由规则时给路由的命名,相当于别名
            })
        }
 //获取路由的参数  (刷新页面 数据不会丢失)
id:this.$route.params.id

//方式三:query 传参
this.$router.push({
                name:"AntMemu",//值是在配置路由规则时给路由的命名,相当于别名
             query:{id:this.id}
            })
//获取路由的参数		(刷新页面,参数不会丢失)
id:this.$route.query.id

vue 项目中如何引入图片

参考文章@aithena

方式一:在data 函数中通过 require 来引入图片。
icon: require('@/assets/images/modules-a5.png'),
方式二:在style 中 引入图片。
background: url(../../assets/images/entry-icon.png) 0 0 no-repeat; 
方式三:在html 通过 src 属性引入图片。
<img src="@/assets/images/a-1.png" alt="">

vue 使用 $parent 方法失效。

问题描述:

  • 在子组件中使用 $parent 方法 获取父组件中的属性时 , 是一个 undefined.

解决办法:

  • 检查 子组件标签外是否 包裹封装过的 组件标签.
  • 如果包裹了一层 组件标签就 多一个 .$parent ,以此类推.
  • 参考文章@韩大璐
//父组件在调用子组件的时候,在子组件的外层包裹了一层UI组件的某个组件
//子组件外层还包裹了一个iview的Modal组件
<template>
    <Modal
        v-model="applyStatus"
        title="扩容申请"
        width="1200"
        @on-ok="expansionSubmit">
        <children v-if="applyStatus" ref="childApply"></children>
    </Modal>
</template>
//此时获取父组件中的属性.
this.$parent.$parent.数据

!!!以此类推,外层包裹了几个组件就需要几个$parent

vue $nextTick 的使用场景

参考文章@天命爱心职责~

案例一:点击修改dom元素的样式


  <div>
      <div :id="myid" class="" ref="str">123456789</div>
      <button @click="changeColor()">修改dom元素</button>
    </div>

 methods: {
      changeColor(){
          this.myid = 'color'
          
          this.$nextTick( () =>{
              if (this.$refs.str.id == 'color') {
                    this.$refs.str.className = 'background'
              }
          })
      }
    }

案例二:点击编辑自动获取光标


    <div>
      <!-- :disabled='isDisabled'最初设置禁止输入属性 -->
      <input ref="inputTitle" type="text" value='' :disabled='isDisabled' placeholder="请输入内容">
      <button @click="getedit()">编辑</button>
    </div>

   methods: {
      getedit(){
          this.isDisabled = false
          this.$nextTick(function(){
					  this.$refs.inputTitle.focus() //获取焦点
				})
      }
    }

案例三:在created阶段vue实例已经创建,但是还不能获取DOM元素。所以在这个阶段要修改dom元素,一般都是要放在this.$nextTick()中


<template>
    <div><input ref="str" type="text" :value="string"></div>
</template>
<script>
  export default {
    data() {
      return {
        string:'苹果',
      };
    },
    created() {
      this.$nextTick(()=>{
        this.$refs.str.value = '西瓜'
        }
      )
    },
    mounted(){
       console.log(this.$refs.str.value);
    }
  }
</script>

或者使用定时器:

 created() {
      setTimeout(()=>{
        this.$refs.str.value = '西瓜'
      },10)
    },

Vue 自定义指令v-loadmore实现列表下拉加载 触底加载事件

需求:

  • 需要给 el-table 添加触底加载事件
    解决办法:
  • 自定义一个指令 , 然后给需要 触底加载的元素添加 style=“height:230px;overflow:scroll;”
  • 参考文章@二熊不是熊二
  1. main.js 同级创建一个 directive.js文件用于编写 自定义指令
import Vue from 'vue'

Vue.directive('loadmore', {
    bind(el, binding) {
        var p = 0;
        var t = 0;
        var down = true;
        el.addEventListener('scroll', function() {
            //判断是否向下滚动
            p = this.scrollTop;
            if(t < p){
                down = true;
            }else{
                down = false;
            }
            t = p;
            const scrollDistance = this.scrollHeight - this.scrollTop - this.clientHeight;
            if (scrollDistance <= 0 && down) {
                // 滚动到底部后触发事件
                binding.value()
            }
        });
    }
})
  1. 在main.js中引入 使用 directives
import * as directives from './directives'

Vue.use(directives)
  1. 在需要触底加载的元素上使用 自定义指令
  • v-loadmore 后加下拉后需要触发的事件loadMore(根据业务需求自行定义)
  • 样式中明确该元素的固定高度,设置scroll
  • 注意:必须要在行内样式中 注明元素的高度,同时添加 overflow: scroll 属性
 <el-table
      v-loadmore="loadMore"
      :data="tableData"
      style="width: 100%; height: 360px; overflow: scroll"
    >
      <el-table-column prop="date" label="日期" width="180"> </el-table-column>
      <el-table-column prop="name" label="姓名" width="180"> </el-table-column>
      <el-table-column prop="address" label="地址"> </el-table-column>
  </el-table>

vue 在 data 中调用 methods 中的方法

思路: 在 data(){} 中保存 this 指向。

data() {
let self = this  // 加上这一句就OK了
return {
	swiperOption: {
	on: {
          reachEnd(swiper, event){
            self.getRmlp()  // getRmlp是vue项目里methods中的函数
            }
        }
        }
    }

vue model 属性( 和 data methods 同级的 model 属性)

允许一个自定义组件在使用 v-model 时定制 prop 和 event。
参考文章@小菜菜~~~

实现效果 类似于 v-bind.sync

// 父组件
<followUpModal ref="followUpModal" v-model="followUpModalVisible"></followUpModal>

// 子组件
<el-dialog
    :title="modalTitle"
    :visible.sync="followUpModalVisible"
    width="50%"
    center
    :modal="false"
    class="tool-dialog"
    @close="onclose"
    destroy-on-closecloseBriefModal
    :close-on-click-modal="false">
    <div>test</div>
  </el-dialog>

    model: {
    prop: 'followUpModalVisible',
    event: 'changeVisible',
  },
  props: {
    followUpModalVisible: {
      type: Boolean,
      default() {
        return false;
      },
    },
  },

    methods: {
    // 点击关闭抽屉层
    onclose() {
      // 修改model中的值
      this.$emit('changeVisible', false);image.png
    },
  },
父组件:
    <children :testValue.sync="testValue"></children>
子组件触发:
	 testHandle() {
      this.$emit("update:testValue", "新标题");
    },

Vue 里给 v-html 元素添加样式

问题: 使用 v-html 时,给 v-html 内容标签设置 css 样式不生效。

解决办法:

  • scoped 属性导致 css 仅对当前组件生效(用 css3 的属性选择器+生成的随机属性实现的),而 html 绑定渲染出的内容可以理解为是子组件的内容,子组件不会被加上对应的属性,所以不会应用 css.解决的话把 scoped 属性去掉就行了
  • 而如果在组件中使用了 v-html,要为 v-html 中的标签添加 CSS 样式,我们需要在写样式的时候添加>>>:

参考文章@sun6sian

<style scoped>
>>> p {
  font-size: 14px;
  line-height: 28px;
  text-align: left;
  color: rgb(238, 238, 238);
  color: #585858;
  text-indent: 2em;
}
</style>

Mixin 混入使用

参考文章@小猪课堂

Mixin 的作用

将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部即可。这样既可以减少代码冗余度,也可以让后期维护起来更加容易。

注意:Mixin中的数据和方法都是独立的,组件之间使用后是互相不影响的。

  • 生命周期执行顺序:先执行 mixin 中的生命周期函数,然后执行组件内的生命周期函数

使用步骤

  1. 新建一个 mixin.js 文件,用于存放公共逻辑和相关公用配置
  2. 全局混入或者局部混入 ,
  3. 最后即可使用 mixin 文件中的相关方法和配置。
//  mixin.js 文件

let mixinUtils = {
  data() {
    return {
      message: "我是 mixin 工具",
    };
  },
  methods: {
    mixinHandler() {
      alert("我是 mixin 方法执行");
    },
  },
};

export default mixinUtils;

局部混入

//  需要混入 mixin 的组件
import mixinUtils from "@/utils/mixin.js";
export default {
  mixins: [mixinUtils],
  mounted() {},

  data() {
    return {};
  },

  methods: {
    doSomething() {
      //!! 使用:   调用 mixin 中封装的方法
      this.mixinHandler();
    },
  },
};

全局混入

// main.js 文件
import mixinUtils from "@/utils/mixin.js";
Vue.mixin(mixins);

异步请求的情况

参考文章@LYHappy&

当混合里面包含异步请求函数,而我们又需要在组件中使用异步请求函数的返回值时,我们会取不到此返回值

// mixin.js 文件
export const myMixins = {
  components: {},
  data() {
    return {
      num: 1,
    };
  },
  methods: {
    getDate1() {
      new Promise((resolve, reject) => {
        let a = 1;
        setTimeout(() => {
          resolve(a);
        }, 500);
      }).then((res) => {
        console.log(res, "res");
        return res;
      });
    },
  },
};

使用方法文件

// todo.vue
import { myMixins } from "./myMixins.js";

export default {
  mixins: [myMixins],
  data() {
    return {};
  },
  mounted() {
    console.log(this.getDate1, "template1-func_one"); //  undefined  "template1-func_one"
  },
};

解决办法

不要返回结果而是直接返回异步函数

// myMixins.js
export const myMixins = {
  components: {},
  data() {
    return {
      num: 1,
    };
  },
  methods: {
    async getDate1() {
      let result = await new Promise((resolve, reject) => {
        let a = 1;
        setTimeout(() => {
          resolve(1);
        }, 500);
      });
      return result;
    },
  },
};

Vue 中使用节流和防抖函数

参考文章@twinkle

参考文章@潇潇 mini

正确使用

<el-button type="primary" @click="saveHandler">保存</el-button>

<script>
  import { deepCopy, debounce } from '@/utils/wheelUtils.js'
  methods:{
    saveHandler: debounce(function () {
      this.tableData[this.tableIndex] = this.inputFormList.form
      let bodyData = {
        ...this.formList.form,
        busnissKey: this.businessKey,
        taskId: this.taskId,
        handleUserCode: this.currentUserInfo.userCode,
        deliverablesList: this.tableData
      }
      API.deliverablesSubmitEdit(bodyData).then((res) => {
        this.inputDialogVisible = false
        this.deliverablesSubmitSearchFn()
        this.$message({ type: 'success', message: '操作成功' })
      })
    }, 1000),
  }
</script>

错误使用

错误1:在点击事件中 又声明一个函数 debounce
<el-button type="primary" @click="saveHandler">保存</el-button>

<script>
  import { deepCopy, debounce } from '@/utils/wheelUtils.js'
  methods:{
    saveHandler()=>{
      debounce(fn=>{},dealy)
    },

  }
</script>
错误2:直接在点击事件上 使用debounce
<el-button type="primary" @click="debounce(saveHandler,1000)">保存</el-button>

<script>
  import { deepCopy, debounce } from '@/utils/wheelUtils.js'
  methods:{
    saveHandler()=>{
      this.tableData[this.tableIndex] = this.inputFormList.form
      let bodyData = {
        ...this.formList.form,
        busnissKey: this.businessKey,
        taskId: this.taskId,
        handleUserCode: this.currentUserInfo.userCode,
        deliverablesList: this.tableData
      }
      API.deliverablesSubmitEdit(bodyData).then((res) => {
        this.inputDialogVisible = false
        this.deliverablesSubmitSearchFn()
        this.$message({ type: 'success', message: '操作成功' })
      })
    },

  }
</script>

Vue watch 对象/数组 无法比较新旧值

问题: vue 中使用 watch 监听对象/数组,无法比较新旧值
原因: 对象和数组是引用数据类型, new old 指向同一个内存地址.
解决办法: 借助 计算属性,返回一个新对象,监听该计算属性,即可获取前后值的变化

参考文章@夏狗花花

--------------------------watch配置项--------------------------
 projectTableDataCp: {
      deep: true,
      handler(val, old) {
        console.log(val, old)

      }
    }
--------------------------data配置项--------------------------
data(){
    return {
        projectTableData: [],
    }
}
--------------------------computed 计算属性配置项--------------------------
  computed: {
    projectTableDataCp() {

      return JSON.parse(JSON.stringify(this.projectTableData))
    }
  },

Vue 自定义指令相关使用(directive)

vue2 官方文档@自定义指令

自定义指令相关文章@骑着蜗牛去放羊~

自定义指令对象 钩子函数

  • bind : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update :所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新

  • componentUpdated :指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。

  • binding:一个对象,包含以下属性:

    • name:指令名,不包括 v- 前缀。

    • value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。

    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。

    • expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。

    • arg:传给指令的参数,例如 v-my-directive:foo 中,参数为 “foo”。

    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true,bar:true}

    • vnode:Vue 编译生成的虚拟节点。

    • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

自定义指令封装

  1. 可以创建一个 directives文件夹,将需要封装的自定义指令文件都放在该文文件夹中.
  2. directives文件夹中,创建一个 index.js文件,将所有的自定义指令都放在该文件中。
  3. main.js中引入该文件,并注册自定义指令。

具体操作

-----------------permission.js 文件-----------------

// 指令封装

import store from '@/store'

function checkPermission(el, binding) {
  const { value } = binding
  const roles = store.getters && store.getters.roles

  if (value && value instanceof Array) {
    if (value.length > 0) {
      const permissionRoles = value

      const hasPermission = roles.some(role => {
        return permissionRoles.includes(role)
      })

      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    }
  } else {
    throw new Error(`need roles! Like v-permission="['admin','editor']"`)
  }
}

export default {
  inserted(el, binding) {
    checkPermission(el, binding)
  },
  update(el, binding) {
    checkPermission(el, binding)
  }
}



-----------------index.js 文件-----------------

import permission from './permission'
import longpress from './longpress'
// 自定义指令
const directives = {
  permission,
  longpress,
}

export default {
  install(Vue) {
    Object.keys(directives).forEach((key) => {
      // 全局注册的自定义指令
      Vue.directive(key, directives[key])
    })
  },
}

-----------------main.js 文件-----------------

import Vue from 'vue'
import Directives from './JS/directives'
Vue.use(Directives)

常用自定义指令封装

v-copy 复制粘贴指令

文件封装

const copy = {
  bind(el, { value }) {
    el.$value = value;
    el.handler = () => {
      if (!el.$value) {
        // 值为空的时候,给出提示。可根据项目UI仔细设计
        console.log("无复制内容");
        return;
      }
      // 动态创建 textarea 标签
      const textarea = document.createElement("textarea");
      // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
      textarea.readOnly = "readonly";
      textarea.style.position = "absolute";
      textarea.style.left = "-9999px";
      // 将要 copy 的值赋给 textarea 标签的 value 属性
      textarea.value = el.$value;
      // 将 textarea 插入到 body 中
      document.body.appendChild(textarea);
      // 选中值并复制
      textarea.select();
      const result = document.execCommand("Copy");
      if (result) {
        console.log("复制成功"); // 可根据项目UI仔细设计
      }
      document.body.removeChild(textarea);
    };
    // 绑定点击事件,就是所谓的一键 copy 啦
    el.addEventListener("click", el.handler);
  },
  // 当传进来的值更新的时候触发
  componentUpdated(el, { value }) {
    el.$value = value;
  },
  // 指令与元素解绑的时候,移除事件绑定
  unbind(el) {
    el.removeEventListener("click", el.handler);
  },
};

export default copy;

使用

<template>
  <button v-copy="copyText">复制</button>
</template>

<script>
  export default {
    data() {
      return {
        copyText: "a copy directives",
      };
    },
  };
</script>

v-longpress 长按触发

文件封装

const longpress = {
  bind: function (el, binding, vNode) {
    if (typeof binding.value !== "function") {
      throw "callback must be a function";
    }
    // 定义变量
    let pressTimer = null;
    // 创建计时器( 2秒后执行函数 )
    let start = (e) => {
      if (e.type === "click" && e.button !== 0) {
        return;
      }
      if (pressTimer === null) {
        pressTimer = setTimeout(() => {
          handler();
        }, 2000);
      }
    };
    // 取消计时器
    let cancel = (e) => {
      if (pressTimer !== null) {
        clearTimeout(pressTimer);
        pressTimer = null;
      }
    };
    // 运行函数
    const handler = (e) => {
      binding.value(e);
    };
    // 添加事件监听器
    el.addEventListener("mousedown", start);
    el.addEventListener("touchstart", start);
    // 取消计时器
    el.addEventListener("click", cancel);
    el.addEventListener("mouseout", cancel);
    el.addEventListener("touchend", cancel);
    el.addEventListener("touchcancel", cancel);
  },
  // 当传进来的值更新的时候触发
  componentUpdated(el, { value }) {
    el.$value = value;
  },
  // 指令与元素解绑的时候,移除事件绑定
  unbind(el) {
    el.removeEventListener("click", el.handler);
  },
};

export default longpress;

使用


<template>
  <button v-longpress="longpress">长按</button>
</template>

<script>
export default {
  methods: {
    longpress () {
      alert('长按指令生效')
    }
  }
}
</script>


v-debounce 防抖

文件封装

const debounce = {
  inserted: function (el, binding) {
    let timer;
    el.addEventListener("click", () => {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(() => {
        binding.value();
      }, 1000);
    });
  },
};

export default debounce;

使用

<template>
  <button v-debounce="debounceClick">防抖</button>
</template>

<script>
  export default {
    methods: {
      debounceClick() {
        console.log("只触发一次");
      },
    },
  };
</script>

v-permission 权限指令

文件封装

function checkArray(key) {
  let arr = ["1", "2", "3", "4"];
  let index = arr.indexOf(key);
  if (index > -1) {
    return true; // 有权限
  } else {
    return false; // 无权限
  }
}

const permission = {
  inserted: function (el, binding) {
    let permission = binding.value; // 获取到 v-permission的值
    if (permission) {
      let hasPermission = checkArray(permission);
      if (!hasPermission) {
        // 没有权限 移除Dom元素
        el.parentNode && el.parentNode.removeChild(el);
      }
    }
  },
};

export default permission;

使用

<div class="btns">
  <!-- 显示 -->
  <button v-permission="'1'">权限按钮1</button>
  <!-- 不显示 -->
  <button v-permission="'10'">权限按钮2</button>
</div>

v-waterMarker 水印指令

文件封装

function addWaterMarker(str, parentNode, font, textColor) {
  // 水印文字,父元素,字体,文字颜色
  var can = document.createElement("canvas");
  parentNode.appendChild(can);
  can.width = 200;
  can.height = 150;
  can.style.display = "none";
  var cans = can.getContext("2d");
  cans.rotate((-20 * Math.PI) / 180);
  cans.font = font || "16px Microsoft JhengHei";
  cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)";
  cans.textAlign = "left";
  cans.textBaseline = "Middle";
  cans.fillText(str, can.width / 10, can.height / 2);
  parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
}

const waterMarker = {
  bind: function (el, binding) {
    addWaterMarker(
      binding.value.text,
      el,
      binding.value.font,
      binding.value.textColor
    );
  },
};

export default waterMarker;

使用

<template>
  <div v-waterMarker="{text:'lzg版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
</template>


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值