Vue积累

142 阅读7分钟

Vue积累

对象的hasOwnProperty

判断对象内是否有此属性

Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。

var o = new Object();
o.prop = 'exists';

function changeO() {
  o.newprop = o.prop;
  delete o.prop;
}

o.hasOwnProperty('prop');  // true
changeO();
o.hasOwnProperty('prop');  // false

箭头函数

var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

elements.map(function(element) {
  return element.length;
}); // 返回数组:[8, 6, 7, 9]

// 1. 上面的普通函数可以改写成如下的箭头函数
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// 2. 当箭头函数只有一个参数时,可以省略参数的圆括号
elements.map(element => {
 return element.length;
}); // [8, 6, 7, 9]

// 3. 当箭头函数的函数体只有一个 `return` 语句时,可以省略 `return` 关键字和方法体的花括号
elements.map(element => element.length); // [8, 6, 7, 9]

// 在这个例子中,因为我们只需要 `length` 属性,所以可以使用参数解构
// 需要注意的是字符串 "length" 是我们想要获得的属性的名称,而 `lengthFooBArX` 则只是个变量名,
// 可以替换成任意合法的变量名
elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]

export const

在这里插入图片描述在这里插入图片描述

Vuex

import * as api from '@/api/mapCommon.js';

const state = {
    // 矢量图配置信息
    vmapConfig: {
        mapEnable:0
    },
    // 测量方式(测距/测面)
    currMeasure: '',
    // 是否全屏
    isFullScreen: false
}

const mutations = {
    // 获取矢量图配置信息
    GET_VMAP_CONFIG (state, val) {	// 参数都是从第二个开始的,第一个就是上方的 state
        state.vmapConfig = val;
    },
    SET_CURR_MEASURE (state, val) {
        state.currMeasure = val;
    },
    SET_FULLSCREEN_STATUS (state, val) {
        state.isFullScreen = val;
    },
    CHANGE_FULLSCREEN_STATUS (state) {
        this.commit('mapCommon/SET_FULLSCREEN_STATUS', !state.isFullScreen);

    }
}

const actions = {
    getVmapConfig ({ commit }) {
        api.getVmapConfig().then(res => {
            res = JSON.parse(res)
            commit('GET_VMAP_CONFIG', res);
        });
    }
    getPermission({ commit }, userId) {
        return new Promise(async (resolve, reject) => {
            getPermission(userId).then(res => {
                const menuList = res.data.users[0].menuNodes
                commit('SET_MENU_LIST', menuList)
                resolve(menuList)
            }).catch(error => {
                reject()
            })
        })
    }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

mapState -> state

import { mapState } from 'vuex';

computed: {
    ...mapState({
        currMeasure: state => state.mapCommon.currMeasure,
    }),

},
this.$store.state.mapCommon.currMeasure	// 可直接调用

mutations

// 标记打点
setTag () {
    this.$store.commit('mapCommon/SET_CURR_MEASURE', '')	
    this.$emit('setTag')
},

mapActions -> actions

import { mapActions } from 'vuex'

methods: {
    // 方法 1 
    ...mapActions({
        getVmapConfig: 'mapCommon/getVmapConfig',	//...mapActions的方法	调用不同Module的数据必须这样写 mapCommon 这个是模块名
    }),
   // 方法 2
    getVehicleParams () {
        this.$store.dispatch('realTimeMonitor/getVehicleParams')	// 直接调用得方法
    }
}

this.$store.dispatch

两个方法都是传值给vuex的mutation改变state

dispatch:异步操作,数据提交至 actions ,可用于向后台提交数据
this.$store.dispatch('isLogin', true);

this.$store.commit

commit:同步操作,数据提交至 mutations ,可用于登录成功后读取用户信息写到缓存里
this.$store.commit('loginStatus', 1);

注:必须要用commit(‘SET_TOKEN’, tokenV)调用mutations里的方法,才能在store存储成功。

Mixin

import { regex } from '@/utils/validate'
export default {
    data() {
        return {
            validErrorMsg: {
                userName: '请输入1-50个字符且不包含空格 ‘ “ ;:& @',
                password: '请输入8-32字符且不包含空格 ‘ “ ;:& @',
            }
        }
    },
    methods: {
        /**
         * @description 校验方法
         * @param {Object} rule 校验规则
         * @param {String} value 值
         * @param {Function} callback 回调
         */
        regexEpValidRules(rule, value, callback) {
            if (regex[rule['validRule']].test(value)) {
                callback()
            } else {
                callback(new Error(this.validErrorMsg[rule['validRule']]))
            }
        },
    }
}

import mixin from '@/mixins'
export default {
    mixins: [mixin],
    
    methods: {
        toggleSideBar() {
            this.regexEpValidRules('app/toggleSideBar')	// 可以直接用
        },
    }
}

组件 props

<ddd aa-bb = 'cc' />

props: {
    cc:{
        type: String,
        default: ''
    },

父组件

<alarmImages
	v-if="!noImages"
	:picture-str="picture"
	:alarm-date="alarmDateStr"
	:location-name="locationName"
/>

import alarmImages from './alarmImages.vue'

export default {
    components: {
        alarmImages,
    },
}

子组件

<template>
  <div class="alarm-images">
    <div><span>{{ $t('alarm.modal.time') }}</span><span>{{ alarmDate }}</span></div>
    <div class="alarm-modal-address">
      <span>{{ $t('alarm.modal.address') }}</span><span>{{ locationName }}</span>
    </div>
    <img
      v-for="item in imageList"
      :key="item"
      :src="item"
      alt=""
      class="alarm-images-item"
    >
  </div>
</template>

<script>
import { queryAlarms } from '@/api/alarm-process/index.js'
export default {
    props: {
        pictureStr:{
            type: String,
            default: ''
        },
        alarmDate: {
            type: String,
            default: ''
        },
        locationName: {
            type: String,
            default: ''
        }
    },
    methods:{
    	changeFilter() {
            console.log(this.pictureStr)	// 直接在这里使用
        }
	}
}
</script>

Vue-cli watch监听$route

需要在有子路由的情况下 才能监听到 app 是所有路由的父路由,所以可以监听,其他的就需要该路由下有子路由才行

watch: {
        $route(to) {
            if (to.path.indexOf('index') != -1) {
                //路由变化后重新获取帖子列表
                this.$http.get('/api/communityList').then((res) => {
                    if (res.data.errCode === 0) {
                        let getRes = res.data.data.hotList
                        this.postList = getRes
                    }
                })
            }
        }
    }

this.$parent

this.$parent 可以访问到父组件 上所有的 data(){ 里的数据信息和生命周期方法,methods里的方法 }!

async/await

1.async/await场景

这是一个用同步的思维来解决异步问题的方案,当前端接口调用需要等到接口返回值以后渲染页面时。

methods: {
    initChart() {
      this.chart = echarts.init(document.getElementById(this.id));
          fetchOverview().then(response => { 
            this.option.series[0].data = response.items
          }).catch(error => {
            console.log(error);
          })
      this.chart.setOption(this.option);
    },
  }
methods: {
    async initChart() {
      this.chart = echarts.init(document.getElementById(this.id));
      await fetchOverview().then(response => { 
        this.option.series[0].data = response.items
      }).catch(error => {
        console.log(error);
      })
      this.chart.setOption(this.option);
    },
  }

this.$emit

子组件

<el-dialog :before-close="closeVideo" >
      
closeVideo() { 
    this.$emit('closeVideoFilterModal',value) 	// 第一步
},

父组件

import videoFilter from '@/components/videoFilter'

components: { voiceIntercom },
    
<video-filter @closeVideoFilterModal="closeVideoFilterModal" />	// 第二部 @这个

closeVideoFilterModal(value) { 	// 第三步 这个就是子组件绑定的时间 value为传参
    this.pictureAdjustmentVisible = false 
},

Vue.component 全局注册组件

// 导入组件,组件必须声明 name
import iTree from './index.vue'

// 为组件添加 install 方法,用于按需引入
iTree.install = function (Vue) {
    Vue.component(iTree.name, iTree)
}

export default iTree

<iTree></iTree>

表格内动态添加操作按钮

TableBase 子组件

<template>
    <el-table>
        <el-table-column v-else :key="index" :prop="item.prop" :label="$t(item.label)":show-overflow-tooltip="true">
          <template slot-scope="scope">
            <template v-if="item.render">
              <custom-cell :column="item" :row="scope.row" :render="item.render" :index="scope.$index"/>
            </template>
            <template v-else-if="item.slot">
              <slot :name="item.slot" :row="scope.row" :index="scope.$index"/>	// 这里是往表格内动态加 是显示按钮还是显示文本
            </template>
            <template v-else>
              <span>{{ scope.row[item.prop] }}</span>
            </template>
          </template>
        </el-table-column>
    </el-table>
</template>

父组件

可以根据传入配置动态显示 复选框 或者 按钮

<table-base :table-height="'650'" :columns="columns" :list-data="listData" :total="total" :loading="loading" >
  <template slot="operation" slot-scope="scope">	// 这个就是 slot 动态组件
    <el-button v-if="scope.row.handleStat == 0" type="primary" size="small" @click="openAlarmModal(scope.row, 'handle')">
      {{ $t('alarm.process.process') }}
    </el-button>
    <el-button v-else type="primary" size="small" @click="openAlarmModal(scope.row, 'view')">
      {{ $t('real.time.monitor.view') }}
    </el-button>
  </template>
</table-base>
columns: [
    {type: 'selection'},
    { prop: 'plateNo', label: this.$t('hard.disk.failure.plateNo'), width: '100px' },
    { prop: 'deviceName', label: this.$t('hard.disk.failure.deviceName'), width: '120px' },
    { prop: 'handleDate', label: this.$t('alarm.process.process.time') },
    { slot: 'operation', label: this.$t('alarm.process.operation'), width: '120px'},
],

template及template上使用v-for

<template>
    <div class="root">
        <template v-for="item,index in 5">		// 这个 template 标签只会渲染出来一个 div, 但是采用 div 来渲染的话就会出来很多个 div
            <div>测试{{index}}</div>
        </template>

    </div>
</template>
<div class='root'>
    <div>测试0</div>
    <div>测试1</div>
    <div>测试2</div>
    <div>测试3</div>
    <div>测试4</div>
</div>

Sass的@mixin与@include

@mixin 指令允许我们定义一个可以在整个样式表中重复使用的样式。

@include 指令可以将混入(mixin)引入到文档中。

@mixin important-text {
  color: red;
  font-size: 25px;
  font-weight: bold;
  border: 1px solid blue;
}

.danger {
  @include important-text;
  background-color: green;
}

向混入传递变量

/* 混入接收两个参数 */
@mixin bordered($color, $width) {
  border: $width solid $color;
}

.myArticle {
  @include bordered(blue, 1px);  // 调用混入,并传递两个参数
}

.myNotes {
  @include bordered(red, 2px); // 调用混入,并传递两个参数
}

created()与activated()

created():在创建vue对象时,当html渲染之前就触发;但是注意,全局vue.js不强制刷新或者重启时只创建一次,也就是说,created()只会触发一次;

activated():在vue对象存活的情况下,进入当前存在activated()函数的页面时,一进入页面就触发;可用于初始化页面数据等


使用<keep-alive>会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务。

被包含在 <keep-alive> 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

activated:在组件被激活时调用,在组件第一次渲染时也会被调用,之后每次keep-alive激活时被调用。

deactivated:在组件被停用时调用。

注意:只有组件被 keep-alive 包裹时,这两个生命周期才会被调用,如果作为正常组件使用,是不会被调用,以及在 2.1.0 版本之后,使用 exclude 排除之后,就算被包裹在 keep-alive 中,这两个钩子依然不会被调用!另外在服务端渲染时此钩子也不会被调用的。

什么时候获取数据?

当引入keep-alive 的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

我们知道 keep-alive 之后页面模板第一次初始化解析变成HTML片段后,再次进入就不在重新解析而是读取内存中的数据,即,只有当数据变化时,才使用VirtualDOM进行diff更新。有需要的话,页面进入的数据获取应该在activated中也放一份。数据下载完毕手动操作DOM的部分也应该在activated中执行才会生效。

所以,有需要的话,应该activated中留一份数据获取的代码,或者不要created部分,直接将created中的代码转移到activated中。

vue动态绑定class的方式

第一种:(最简单的绑定)

1.绑定单个class

 html部分:
 <div :class="{'active':isActive}"></div>	// js部分:判断是否绑定一个active

data() {
    return {
        isActive: true
    };
}

结果渲染为:
<div class="active"></div>
 

2.若要绑定多个class,需要逗号隔开就行:(这里的activeTwo加不加引号都可以,也一样都能渲染,如下)
 <div class="activeOne" v-bind:class="{ activeTwo: isActive, 'activeThree': hasError }"></div>

js部分:判断是否绑定对应class
data() {
    return {
        isActive: true,
        hasError: true
    };
}

结果渲染为:
<div class="activeOne activeTwo activeThree"></div>
 

第二种:(绑定的数据对象)

<div :class="classObject"></div>
data: { 
    classObject: { 
        active: true,
    } 
}
 
第三种:(绑定一个返回对象的计算属性)

<div :class="classObject"></div>
export default {
  data() { return { isActive: true, }; },
  computed: {
  classObject: function () { 
      return { 
          active: this.isActive, 
      } 
  }
}
    
结果渲染为:
<div class="active"></div>
 

第四种:(单纯数组方法)
<div :class="[activeClass, errorClass]"></div>
    
 data() { 
     return { 
         activeClass: "active", 
         errorClass: "disActive" 
 	}; 
 },
     
结果渲染为:
<div class="active disActive"></div>
 

第五种:(数组与三元运算符结合判断选择需要的class)

<div :class="[isActive?'active':'disActive']"></div>
 data() { 
     return { 
         isActive: false, 
     }
 },
结果渲染为:

<div class="disActive"></div>

file转base64 与 base64转file

// file转base64
getBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = () => resolve(reader.result)
        reader.onerror = (error) => reject(error)
    })
},
// base64转file
base64ToFile(data) {
    // 将base64 的图片转换成file对象上传 atob将ascii码解析成binary数据
    const binary = atob(data.split(',')[1])
    const mime = data.split(',')[0].match(/:(.*?);/)[1]
    const array = []
    for (let i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i))
    }
    const fileData = new Blob([new Uint8Array(array)], {
        type: mime
    })

    const file = new File([fileData], `${new Date().getTime()}.png`, { type: mime })

    return file
},

this.$set

this.$set(obj, key, value)

<script>
export default {
 data() {
   return {
     student: {
       name: '张三',
     }
   }
 },
 methods: {
   setMessage() {
     this.$set(this.student, 'age', 15)
     console.log(this.student)
   }
 }
}
</script>

slot-scope

slot-scope的出现却实现了父组件调用子组件内部的数据,子组件的数据通过slot-scope属性传递到了父组件

//    作用域插槽
    Vue.component("todo-list",{
        inheritAttrs:false,
        props:{
            todos:[Array,Object]
        },
        template:
        `<ul>
            <li v-for="todo in todos" :key="todo.id" style="display: block;" >
                  //这里的:data=“todo”就是讲子组件todo这个数据传给了父组件
                <slot :data="todo">{{todo.text}}</slot>//todo.text是默认值,父组件会把它覆盖掉
            </li>
         </ul>
        `
    });


<!--使用作用域插槽,通过slot-scope实现-->
<todo-list :todos="todos">
    <template slot-scope="slotProps">
        <span v-if="slotProps.data.isTrue"></span>
        {{slotProps.data.text}}
    </template>
</todo-list>

JavaScript eval() 函数

eval() 函数计算或执行参数。

如果参数是表达式,则 eval() 计算表达式。如果参数是一个或多个 JavaScript 语句,则 eval() 执行这些语句。

props怎么自定义验证

props: {
    num: {
        default: 1,
        validator: function (value) {
            // 返回值为false则验证不通过,报错
            return [1, 2, 3, 4, 5].indexOf(value) !== -1
        }
    }
}

watch的immediate属性

// 比如平时created时要请求一次数据,并且当搜索值改变,也要请求数据,我们会这么写:

created(){
	this.getList()
},
watch: {
    searchInputValue(){
    	this.getList()
    }
}

// 使用immediate完全可以这么写,当它为true时,会初始执行一次

watch: {
    searchInputValue: {
        handler: 'getList',
        immediate: true
    }
}

watch监听一个对象时,如何排除某些属性的监听

下面代码是,params发生改变就重新请求数据,无论是a,b,c,d属性改变

data() {
    return {
        params: {
            a: 1,
            b: 2,
            c: 3,
            d: 4
        },
    };
},

watch: {
    params: {
        deep: true,
        handler() {
            this.getList;
        },
    },
}

只要a,b改变时重新请求,c,d改变时不重新请求

mounted() {
    Object.keys(this.params)
        .filter((_) => !["c", "d"].includes(_)) // 排除对c,d属性的监听
        .forEach((_) => {
            this.$watch((vm) => vm.params[_], handler, {
            	deep: true,
        });
    });
},
    
data() {
    return {
        params: {
            a: 1,
            b: 2,
            c: 3,
            d: 4
        },
    };
},
    
watch: {
    params: {
        deep: true,
        handler() {
            this.getList;
        },
    },
}

computed如何实现传参

// html
<div>{{ total(3) }}

// js
computed: {
    total() {
        return function(n) {
        	return n * this.num
        }
    },
}

Vue引入JS(JS内含this)

/config.js


export const handelOrderSource = function (value) {
    const orderSourceList = {
        1: this.$t('电召订单'),
        2: this.$t('线下订单'),
        3: this.$t('APP订单'),
    }
    return orderSourceList[value]
}

export const handelOrderInfo = function (value) {
    const self = this

    value.orderSource = handelOrderSource.call(self, value.orderSource)	// 如果传参了 需要这样写 .call(this, argument)
    return value
};


/aaa.vue
import { handelOrderInfo } from '../assets/js/config.js'

this.orderInfo = handelOrderInfo.call(this, this.orderInfo)	// 如果传参了 需要这样写 .call(this, argument)