Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从Vue3.0来看组件新写法 #62

Closed
bosens-China opened this issue Oct 28, 2020 · 0 comments
Closed

从Vue3.0来看组件新写法 #62

bosens-China opened this issue Oct 28, 2020 · 0 comments
Labels
框架相关 目前Vue和React为主

Comments

@bosens-China
Copy link
Owner

bosens-China commented Oct 28, 2020

之前写过一篇文章漫谈 vue 组件设计 ,这里写这篇文章原因是不满一个相同的组件需要在各个页面重复写,而且这样搞维护成本实在太高,但是受限于 vue2.x 的响应机制没有想到更好的方法,所以在某些组件中使用的时候(例如在表单中使用需要添加校验规则、按需使用组件)需求很难实现。

而从 Vue3.0 的响应式的重写以及组合 api 的出现,让我觉得眼前一亮,下面以省、市、区级联选择和表单校验为例子,因为主要
讲解思路所以 UI 方面选择了ant-design-vue帮助快速构建视图

在线演示

准备实现功能

1

先上一张最终实现的效果图,下面内容会对照着讲解,代码放置到了https://github.com/bosens-China/combination-demo

  • 省、市、区组件可以自由组合使用(但是这个例子选取的不太好关系相互之间依赖,所以实际上只能省+市+区省+市
  • 不需要手动管理状态,只需要提供一个v-model作为绑定值即可

实现

根据上面需求,我们一个个实现

  • 组件组合使用

要实现组件组合使用,这就要求组件本身耦合度不能太高,只展示数据并不进行任何的省、市、区具体功能的处理

<a-select value={props.modelValue} onChange={onChange}>
  {...modelValueList.value.map((item) => {
    return <a-select-option value={item.name}>{item.value}</a-select-option>;
  })}
</a-select>

组件的结构如上所示,其实省、市、区的组件结构是一样的不同的只是结构不同,所以实现的时候只需要使用工厂模式即可,这样可以做到少维护可扩展。

  • 管理更新状态

这一点使用了组合 api 来完成,在新发布的版本中,vue 把响应式功能抽离出来,响应式数据可以通过ref来定义,通过watch来做到监听,例如这一个例子

import { watch, ref } from "vue";
const test = ref(null);
watch(test, () => {
  console.log("变更");
});
test.value = 5;

而且这个 ref 可以共享给其他程序使用,例如通过import导入给其他组件使用,这样刷新的时候自然而然组件也会发生变化。

下面通过一个rely.js文件来进行组件状态管理,它可以帮助我们实现以下流程

123

在实际过程中我们切换省、市、区的时候需要我们还可以判断下绑定值存不存在 list 数据中,不存在就要清空。

下面通过注释的方式来讲解

import { ref, watch } from "vue";
import { getProvince, getCity, getArea } from "../../api/region";

const created = () => {
  // 下面字段分比为省、市、区的绑定字段以及list数据
  const province = ref(undefined);
  const provinceList = ref([]);
  const city = ref(undefined);
  const cityList = ref([]);
  const area = ref(undefined);
  const areaList = ref([]);

  // 初始化省级列表
  getProvince()
    .then((res) => {
      provinceList.value = res;
    })
    .catch(() => {
      provinceList.value = [];
    });

  // 监听省级变化,更新市级的变化
  watch(province, (value) => {
    if (!value) {
      city.value = undefined;
      cityList.value = [];
      return;
    }
    getCity(value)
      .then((res) => {
        cityList.value = res;
        // 如果绑定值不存在对应的list数据中清除掉
        city.value = res.find((f) => f.name === city.value)?.name;
      })
      .catch(() => {
        cityList.value = [];
        city.value = undefined;
      });
  });
  // 监听市级变化,更新区的数据
  watch(city, (value) => {
    if (!value) {
      areaList.value = [];
      area.value = undefined;
      return;
    }
    getArea(value)
      .then((res) => {
        areaList.value = res;
        // 如果绑定值不存在对应的list数据中清除掉
        area.value = res.find((f) => f.name === area.value)?.name;
      })
      .catch(() => {
        areaList.value = [];
        area.value = undefined;
      });
  });
  // 返回数据,提供给其他组件使用
  return {
    province,
    provinceList,
    city,
    cityList,
    area,
    areaList,
  };
};
export default created;

这里需要返回一个函数,因为组件状态不能被污染

  • 组合

assembly.js

import { watch } from "vue";
import created from "./rely";

const basicsComponents = () => {
  // 数据
  const { province, provinceList, area, areaList, city, cityList } = created();
  // 工厂模式返回视图
  const assemble = (modelValue, modelValueList) => {
    return {
      // 3.0中默认绑定的字段
      props: {
        modelValue: null,
      },
      setup(props, { emit }) {
        // 监听绑定值变化
        watch(modelValue, (value) => {
          emit("update:modelValue", value);
        });
        // 监听modelValue值,更新到v-model中
        watch(
          () => props.modelValue,
          (value) => {
            modelValue.value = value;
          },
          { immediate: true }
        );
        const onChange = (value) => {
          emit("update:modelValue", value);
        };
        return () => (
          <a-select value={props.modelValue} onChange={onChange}>
            {...modelValueList.value.map((item) => {
              return (
                <a-select-option value={item.name}>
                  {item.value}
                </a-select-option>
              );
            })}
          </a-select>
        );
      },
    };
  };

  const componentsProvince = assemble(province, provinceList);
  const componentsCity = assemble(city, cityList);
  const componentsArea = assemble(area, areaList);
  return {
    componentsProvince,
    componentsCity,
    componentsArea,
  };
};
export default basicsComponents;

最后

在使用过程中发现 Vue3.0 的热更新速度提升幅度不小使用起来很丝滑,但是还是有不少问题,比如我对 css 添加了scoped在开发环境下没有任何问题,在生产环境下data-***属性没有出现导致样式没作用起来,所以生产环境使用需要谨慎。

文章如果对你有帮助,可以点一下star支持一下作者。

@bosens-China bosens-China added the 框架相关 目前Vue和React为主 label Oct 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
框架相关 目前Vue和React为主
Projects
None yet
Development

No branches or pull requests

1 participant