第3天表格宽度问题、引用组件时使用v-model、循环注册全局组件、文件导出的三种方法、this.$nextTick()

62 阅读3分钟

1.表格宽度问题

通过ELement-UI设置宽度时,使用width 是固定的,min-width 会把剩余宽度按比例分配给设置了 min-width 的列.但是这里遇到一个问题,就是在小屏幕刷新页面可以适配,把小屏幕拖拽到大屏幕也可以适配,但是把大屏幕拖拽到小屏幕就不可以适配了。因此解决方案是:在每个表格外面加上el-row,el-col标签,这样就可以适配上面的这几个问题了。出现这种情况的原因是因为设置min-width在大屏幕会自适应,突然调到小屏幕就不会立马生效,因此可以把两个表格宽度设置成百分比或者使用el-row和el-col,这样就可以解决问题了。

2.在引用组件时使用v-model

v-model在组件中使用时,相当于input事件和value值传给子组件,在子组件中默认使用value接收父组件传过来的值,在子组件创建时,把this.show = this.value;关闭时this.show = false;并且要在子组件使用watch监听器监视value值,这样做的目的是防止子组件改变父组件的值,进而报错,因此才这样做,监听器中就是把变化后的值赋值给show,例如:this.show = val;

<!-- 信息弹窗 -->
  <TagInfo v-if="showTagInfo" v-model="showTagInfo" :row="row" @getTableList="handleUpdate"></TagInfo>

上面引用这个组件,下面是它这个组件:

      <el-drawer
        title="弹窗信息"
        :visible.sync="show"
        :direction="direction"
        @closed="close"
        custom-class="dialog-wrap"
        size="600px"
      >
        <el-form
          label-width="100px"
          :rules="editDialogRules"
          :model="editDialogForm"
          ref="editDialogForm"
        >
          <el-col :span="14">
            <el-form-item label="商品名称:">{{editDialogForm.goodsName}}</el-form-item>
          </el-col> 
        </el-drawer>    
          
         <script>
           props:{
            value: {
              type: Boolean,
              default: false,
            },
            row:{
              type:Object,
              default:{},
            }
          },
              watch:{
                value(val) {
                  this.show = val;
                  this.resetForm("editDialogForm");
                }

              },	
                  created() {
                    this.show = this.value;
                  },
                    close() {
                      this.show = false;
                      this.$emit("input", false);
                      this.$emit("close", false);
                    },   
          </script>

3.注册全局组件

在main.js中使用这个,当然前提是在components文件夹下,创建不同文件夹,并在对应的文件夹下创建index.vue组件。这样可以循环注册多个组件。

// 注册全局组件
const components = require.context('./components', true, /index\.vue$/);
components.keys().forEach(item => {
  const fileItem = components(item).default
  // 如果组件内定义name 则需要全局引用的
  if (fileItem.name) {
    Vue.component(fileItem.name, fileItem)
  }
})

4.文件导出的三种方法:前端可以不直接点击通过浏览器进行下载

1>完整的封装一个下载接收流的代码:

//封裝接收流的函數
    exportData(oParam) {
      oParam.url = oParam.url || "";
      oParam.name = oParam.name || "下载文件" + Date.now();
      oParam.type = oParam.type || "1";
      if (!oParam.url) return false;
      if (oParam.type === "1") {
        let x = new XMLHttpRequest();
        x.open("GET", oParam.url, true);
        x.responseType = "blob";
        x.onload = function (e) {
          let urlData = window.URL.createObjectURL(x.response);
          let a = document.createElement("a");
          a.href = urlData;
          a.download =
            oParam.name + oParam.url.substring(oParam.url.lastIndexOf(".") + 1);
          a.click();
        };
        x.send();
      } else {
        let x = document.createElement("a");
        x.href = oParam.url;
        x.download = "";
        x.style.display = "none";
        document.body.appendChild(x);
        x.click();
        document.body.removeChild(x);
      }
    },

2>导出文件的js代码

downloadWithBlob(url) {
    const elt = document.createElement('a');
    elt.setAttribute('href', url);
    elt.setAttribute('download', 'file.csv');
    elt.style.display = 'none';
    document.body.appendChild(elt);
    elt.click();
},

3>导出文件

//导出商品信息文件
exportFile(result) {
  if (!result) {
    console.log("下载失败,解析数据为空!");
    return;
  }
  // 创建a标签,并隐藏改a标签
  let link = document.createElement("a");
  link.style.display = "none";
  // a标签的href属性指定下载链接
  link.href = result;
  //setAttribute() 方法添加指定的属性,并为其赋指定的值。
  link.setAttribute('download', 'file.png');
  document.body.appendChild(link);
  link.click();
},

5.vue中this.$nextTick()的用法

this.$nextTick 将回调延迟到下次DOM更新循环之后执行。在修改数据之后立即使用它,然后等待DOM更新。 this.$nextTick 跟全局方法 vue.nextTick 一样,不同的是,回调的 this 自动绑定到调用它的实例上。 总的来说,假设我们更改了某个 dom 元素内部的文本,而这时候我们想直接打印这个更改之后的文本是需要 dom 更新之后才会实现的,就像我们把将要打印输出的代码放在 setTimeout(fn, 0) 中

<template>
<button ref="tar" 
        type="button" 
        name="button" 
        @click="testClick">{{content}}</button>
</template>

<script>
export default {
    data () {
        return {
            content: '初始值'
        }
    }
 methods: {
   testClick(){
     this.content = '改变了的值'
     // 这时候直接打印的话,由于dom元素还没更新
     // 因此打印出来的还是未改变之前的值
     console.log(this.$refs.tar.innerText) // 初始值
   }
 }
}
</script>

this.$nextTick这个方法作用是,当数据被修改后使用这个方法,会回调获取更新后的dom再渲染出来

methods:{
testClick() {
    this.content = '改变了的值'
    this.$nextTick(() => {
        // dom元素更新后执行,因此这里能正确打印更改之后的值
        console.log(this.$refs.tar.innerText) // 改变了的值
    })
}
}

this.$nextTick()方法主要是用在随数据改变而改变的dom应用场景中,vue中数据和dom渲染由于是异步的, 所以,要让dom结构随数据改变这样的操作都应该放进this.$nextTick()的回调函数中。

created()中使用的方法时,dom还没有渲染,如果此时在该钩子函数中进行dom赋值数据(或者其它dom操作)时无异于徒劳,所以,此时this.$nextTick()就会被大量使用,而与created()对应的是mounted()的钩子函数则是在dom完全渲染后才开始渲染数据,所以在mounted()中操作dom基本不会存在渲染问题。

使用场景:

1>Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。

2>当项目中你想在改变DOM元素的数据后基于新的dom做点什么,对新DOM一系列的js操作都需要放进Vue.nextTick()的回调函数中; 通俗的理解是:更改数据后当你想立即使用js操作新的视图的时候需要使用它.

3>在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法。

Vue.nextTick(callback) 使用原理: 原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。

当你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题x。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。