vue记录点

1.vue2路由监听

watch: {
 $route: function (to, from) {
     //事件处理
  }
}

 

2.vue3路由监听

watch(
   () => Router.currentRoute.value.path,
   () => {
      // 处理事件
   }
);

 

3.setup

setup 不是生命周期钩子函数!

它只是基于 beforeCreate 运行,但函数内部无法通过 this 获取组件实例

而且 setup 有着生命周期钩子不具备的参数:props 和 context

setup(props, context) {
    // 组件 props
    console.log(props);
    const { attrs, slots, emit } = context;
    // Attribute (非响应式对象)
    console.log(attrs);
    // 组件插槽 (非响应式对象)
    console.log(slots);
    // 触发事件 (方法)
    console.log(emit);
}

这里的 props 就是组件内部定义的 props,是由父组件传入的参数

假如父组件传入了一个属性名为 title 的 props,在 setup 中可以通过 props.title 直接取到

而且这个 props 是响应式的,当传入新的 prop 时,props 会被更新

但正是因为 props 是响应式的,所以不能直接对 props 使用 ES6 解构,因为它会消除 prop 的响应性

Vue 3 提供了一个 toRefs 全局方法来解决这个问题:

import { toRefs } from 'vue';

setup (props) {
    // 通过 toRefs 包装后的 props 可以在 ES6 解构之后依然具有响应性
    const { title } = toRefs(props);
    console.log(title);
}

 

4. 在 setup 中注册生命周期钩子

如果是在 setup 内部注册生命周期钩子,则需要从 Vue 引入带有 on 前缀的生命周期工具函数

import { toRefs, onMounted } from 'vue';

setup (props) {
    // 使用 `toRefs` 创建对prop的 `title` property 的响应式引用
    const { title } = toRefs(props);
    // 注册生命周期钩子
    onMounted(() => {
        console.log('onMounted', title);
    });
}

上面的 onMounted 会接收一个函数作为参数,在组件中对应的生命周期钩子 (mounted) 触发时,会执行这个被传入的函数

除了 onMounted 以外,其他 created 之后的生命周期也有对应的注册函数,如 onUpdated、onBeforeUnmount 等

但 setup 中并没有 beforeCreate 和 created 这两个生命周期的注册函数

因为 setup 本身是基于 beforeCreate 运行,会用到 beforeCreate 和 created 的场景都可以通过 setup 替代

也就是说,在 beforeCreate 和 created 中编写的任何代码都应该直接在 setup 函数中编写

5. setup 的返回值

如果 setup 显式的返回了一个对象,这个对象的所有内容都会暴露给组件的其余部分

setup() {
  return {
    name: 'wise wrong',
    foo: (text: string) => {
      console.log(`Hello ${text}`);
    },
  };
},
mounted() {
  // 直接使用 setup 的返回值
  this.foo(this.name);
}

上面的 setup 返回了一个 foo 函数,还有一个 name 字段,目前这个 name 还不是一个响应式变量

为此我们需要使用 ref 全局方法

import { ref } from 'vue';

setup (props) {
    return {
        name: ref('wise wrong');
    }
}

通过 ref 包装的变量会具有响应性,在组件中可以像正常的组件属性一样使用

但在 setup 内部,ref 包装后的变量需要通过 value 来修改变量的值

import { ref } from 'vue';

setup() {
  const name = ref('wise wrong');
  onMounted(() => {
    // 在 setup 内部通过 value 修改变量值
    name.value = "Let's study Vue 3";
  });
  return {
    name,
  };
},
methods: {
  test() {
    // 在组件中可以直接修改 ref 变量
    this.name = 'good job';
  },
},

除了返回一个对象以外,setup 还可以返回一个渲染函数

import { h, ref, reactive } from 'vue'

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })
    // 返回一个渲染函数以覆盖 template
    return () => h('div', [readersNumber.value, book.title])
  }
}

如果直接返回了渲染函数,组件中定义的 template 将会失效

在 Setup 中使用 Computed、Watch

setup 的本意是用来替代 mixin,因此除了 data、methods、生命周期钩子之外,还有许多组件选项需要解决

这里先介绍 computed 和 watch

 

1. Computed

在组件中 computed 用来管理包含响应式数据的复杂逻辑

computed: {
  booksMessage() {
    return this.books.length > 0 ? 'Yes' : 'No'
  }
}

计算属性可以在组件中直接使用,并会随着响应式数据的更新而更新

<template>
  <p>{{ booksMessage }}</p>
</template>

而在 setup 中,需要通过全局函数 computed 来包装

复制代码
import { ref, computed } from 'vue'

setup() {
  const books = ref([]);
  const booksMessage = computed(() => books.value.length > 0 ? 'Yes' : 'No');
  return {
    books,
    booksMessage,
  }
}
复制代码

 

 2. Watch

 watch 用来监听数据的变化

可以通过 watch 定义对应的处理函数,当数据发生变化时候会调用该函数

复制代码
data: () => ({
  question: '',
  answer: 'good luck :)'
}),
watch: {
  question(newQuestion, oldQuestion) {
    // 当 question 变化的时候会执行该函数
    if (newQuestion.indexOf('?') > -1) {
      console.log('answer: ', this.answer);
    }
  }
},
复制代码

在 setup 中,也需要使用全局函数 watch 来包装

复制代码
import { ref, watch } from 'vue'

setup() {
  const question = ref('');
  const answer = ref('good luck :)');

  watch(question, (newValue, oldValue) => {
    if (newValue.indexOf('?') > -1) {
      console.log('answer: ', this.answer.value);
    }
  });

  return {
    question,
    answer,
  }
}
复制代码

 

 

抽取公共逻辑

在了解了基本的 Composition API 之后,可以尝试将公共逻辑抽取出来单独维护

假设有以下两个逻辑点需要被抽取:

1. 在组件加载完成后,通过父组件传入的参数 query 请求数据列表 list,当 query 变化时需要重新请求并更新 list;

2. 根据关键字 keyword 从数据列表 list 中筛选数据,并使用 computed 记录结果。

 

这两段逻辑可以直接在 setup 中实现,但这样会使得 setup 看起来非常累赘

我们可以把这段逻辑拆成两个函数并单独维护

首先在项目目录 src 下创建一个新的文件夹 composables

然后创建 useList.js 文件,完成第一个逻辑:

复制代码
// src/composables/useList.js

import { ref, onMounted, watch } from 'vue';

// 发起接口请求
function fetchList() {
  // ...
}

export default function useList(query) {
  // 创建数据列表 list (data)
  const list = ref([]);
  // 创建查询并更新 list 的方法 (methods)
  const getList = async () => {
    list.value = await fetchList(query);
  };

  // 生命周期 mounted
  onMounted(getList);
  // 监听 query 的变化并更新 list
  watch(query, getList);

  return {
    list,
    getList,
  };
}
复制代码

然后创建 useFilter.js 文件,完成第二个逻辑:

复制代码
// src/composables/useFilter.js

import { ref, computed } from 'vue';

export default function useFilter(list) {
  // 创建搜索关键字 keyword (data)
  const keyword = ref('');
  // 创建筛选结果 filterRes (computed)
  const filterRes = computed(() => {
    return list.filter((value) => {
      return value.includes(keyword.value);
    });
  });

  return {
    keyword,
    filterRes,
  };
}
复制代码

然后在组件中引入,并在 setup 返回组件需要的字段

复制代码
import { defineComponent, toRefs } from 'vue';
import useList from '@/composables/useList';
import useFilter from '@/composables/useFilter';

export default defineComponent({
  props: ['query'],
  setup(props) {
    const { query } = toRefs(props);
    const { list, getList } = useList(query);
    const { keyword, filterRes } = useFilter(list);
    return {
      keyword, // 筛选关键字
      getList, // 查询并更新数据列表
      list: filterRes, // 不需要返回未经筛选的列表
    };
  },
});
复制代码

 

表单部分

校验相关

<el-form ref="ruleForm" :rules="myRules"></el-form>

// 整个表单校验
this.$refs.ruleForm.validate();
// 单个表单字段credentialsFiles校验
this.$refs.ruleForm.validateField("credentialsFiles");
// 移除校验结果
this.$refs.ruleForm.clearValidate();
// 移除校验结果并重置字段值
this.$refs.ruleForm.resetFields();
// 校验规则
myRules: {
  enterpriseId: [{ required: true, message: messageContant, trigger: "change" }],
  contractTimeData: {
    validator: (rule, value, cb) => {
      if (value == "" || value == null || (value.length && value.length == 0))
        cb(new Error($i18n.$ct("请输入内容", "Please input")));
      cb();
    },
    trigger: "blur",
  },
  description: this.isChecked
    ? []
    : [
        { required: true, message: messageContant, trigger: "change" },
        { pattern: /^.{5,200}$/, message: $i18n.$ct("内容长度5-200", "Content length 5-200"), trigger: "change" },
      ],
},

 

表格部分

根据某一属性合并行或列

// 在table加span-method属性
<el-table :data="tableData" :span-method="objectSpanMethod" >
    <el-table-column :type="要合并列标志" :label="测试" >
    <el-table-column :label="测试2" >
</el-table>

// data
spanArr: [],
pos: 0,

// methods
/**
 * 计算合并数组 在获取表格数据后调用
 * data 表格数据
 * prop 根据这个属性合并
 */
getSpanArr(data, prop) {
  // data就是我们从后台拿到的数据
  for (let i = 0; i < data.length; i += 1) {
    if (i === 0) {
      this.spanArr.push(1);
      this.pos = 0;
    } else {
      // 判断当前元素与上一个元素是否相同
      if (data[i][prop] === data[i - 1][prop]) {
        this.spanArr[this.pos] += 1;
        this.spanArr.push(0);
      } else {
        this.spanArr.push(1);
        this.pos = i;
      }
    }
  }
},
// 行合并
objectSpanMethod({ column, rowIndex }) {
  if (column.type === "merge") {
    const myRow = this.spanArr[rowIndex];
    const myCol = myRow > 0 ? 1 : 0;
    return {
      // [0,0] 表示这一行不显示, [2,1]表示行的合并数
      rowspan: myRow,
      colspan: myCol,
    };
  }
},

 

posted @ 2022-12-16 14:31  JSKevin  阅读(83)  评论(0编辑  收藏  举报