【前端进阶】玩转 VUE 组件式开发

发布于:2023-01-14 ⋅ 阅读:(204) ⋅ 点赞:(0)

1.整体目标

  • 了解组件开发的整体流程
  • 掌握组件事件和标签事件的区别
  • 掌握在组建上使用 v-model 的方式

2.Button 组件开发

2.1 确定组件API

属性

属性名 说明 类型 默认值
type 设置按钮类型,可选值为 primary danger 或者不设 String default
size 设置按钮大小,可选值为small large 或者不设置 String default

事件

事件名称 说明 回调参数
click 按钮点击事件 (event)=>void

2.2 编写测试基础Button

组件有很多的功能,但是这些功能都是一个最原始的组件逐渐拓展而来的,所以我们先完成一个最基础的button组件,然后逐渐增加功能

// components/button/index.vue
<template>
  <button class="btn-default">
    <slot></slot>
  </button>
</template>

<script>
export default {
  name: "HButton",
};
</script>
<!-- 开发组件的时候 scoped 是必须要加的 不能影响其他的样式 !-->
<style scoped lang="less">
.btn-default {
  line-height: 1.499;
  position: relative;
  display: inline-block;
  font-weight: 400;
  white-space: nowrap;
  text-align: center;
  background-image: none;
  box-shadow: 0.5px 2px 0 rgba(0, 0, 0, 0.05);
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  transition: all 0.3s cubic-bezier(0.6, 0.04, 0.3, 1);
  touch-action: manipulation;
  padding: 5px 10px;
  font-size: 14px;
  border-radius: 4px;
  color: rgba(0 0 0 / 0.65);
  //outline: none;
  border: 1px solid #ddd;
  background-color: #fff;
  &:focus {
    color: rgab(0, 0, 0, 1);
    box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
  }
  &:hover {
    color: rgba(133, 13, 3, 0.31);
  }
}
</style>

2.3 完成tpye配置

1.准备对应的class类


// primary
.btn-primary {
  color: #fff;
  background-color: #1890ff;
  border-color: #1890ff;
  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
}

// danger
.btn-danger {
  color: #fff;
  background-color: #ff4d4f;
  border-color: #ff4d4f;
  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
}

2.编写props

setup(props) {
  console.log(props);
  let activeClass = computed(() => {
    return "btn-" + props.type;
  });
  return {
    activeClass,
  };
},

2.4 完成size的配置

1.准备对应的class 类

 //size :small
.btn-size-small {
  padding: 4px 8px;
  font-size: 12px;
}
 //size :large
.btn-size-large {
  padding: 6px 12px;
  font-size: 16px;
}

2.编写props

 props: {
    type: {
      type: String,
      default: "default",
    },
    size: {
      type: String,
      default: "default",
      // 参数校验
      validator: function (value) {
        // value 代表当前传来的值
        //console.log(value);
        // 对传入的参数做校验 满足条件返回true 不满足就返回false
        // 如果传入的参数不在可选参数的列表中 给出用户提示 告诉哪个参数传错了
        // 如果传入的参数 在可选的参数列表中 直接通过 不会报错
        const sizeList = ["small", "large", "default"];
        return sizeList.includes(value);
      },
    },
setup(props) {
  console.log(props);
  let typeClass = computed(() => {
    return "btn-type-" + props.type;
  });
  let sizeClass = computed(() => {
    return "btn-size-" + props.size;
  });
  return {
    typeClass,
    sizeClass,
  };
},

2.5 完成事件绑定

// 父组件
</template>
	<Button @click="clickHandler">click me</Button>
</template>
<script>
setup() {
    const clickHandler = (e) => {
      console.log("click me", e.target);
    };
    return {
      clickHandler,
    };
  },
</script>

注意:

  • vue2 中 事件如果写到原生支持的标签身上会被直接识别为原生事件, <button @click='handler'></button>
  • 事件如果写到自定义组件身上 默认状态会被识别为自定义事件 <Button @click='handler'></Button>
    1. 增加 .native 修饰符 可以让vue 系统帮助我们把事件绑定成为浏览器支持的原生事件
    2. 就当成自定义事件识别 然后按照父子组件通信的手段 通过子组件内事件触发 然后调用$emit 方法触发即可
  • 在vue3 中不会有这种问题 内置的事件就是内置的事件 除了内置的事件都会被当作自定义事件进行解析

2.6 总结

  • 编写组件时 应该API先行 先确定组件该如何给用户使用 再根据API编写逻辑
  • props 的名称应该具备语义化,类型应该规范,并且可以添加自定义校验
  • 组件上绑定的类似于原生的事件 默认是不会被识别的,需要额外处理
  • 组件有一些设计需要整体把控,比如props 与对应类名的匹配

3. Editor编辑器组件开发

3.1 确定基础API

指令名 说明 类型 默认值
v-model 提供编辑器数据的双向绑定 String

3.2 编写测试基础Editor

Editor/index.js

<template>
  <div style="border: 1px solid #ccc">
    <Toolbar
      style="border-bottom: 1px solid #ccc"
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      :mode="mode"
    />
    <Editor
      style="height: 500px; overflow-y: hidden"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      :mode="mode"
      @onCreated="handleCreated"
    />
  </div>
</template>

<script>
import "@wangeditor/editor/dist/css/style.css"; // 引入 css

import { onBeforeUnmount, ref, shallowRef, onMounted } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";

export default {
  components: { Editor, Toolbar },
  name: "costume-editor",
  setup() {
    // 编辑器实例,必须用 shallowRef
    const editorRef = shallowRef();

    // 内容 HTML
    const valueHtml = ref("<p>hello</p>");

    // 模拟 ajax 异步获取内容
    onMounted(() => {
      console.log(valueHtml);
      console.log(editorRef);
      setTimeout(() => {
        valueHtml.value = "<p>模拟 Ajax 异步设置内容</p>";
      }, 1500);
    });

    const toolbarConfig = {};
    const editorConfig = { placeholder: "请输入内容..." };

    // 组件销毁时,也及时销毁编辑器
    onBeforeUnmount(() => {
      const editor = editorRef.value;
      if (editor == null) return;
      editor.destroy();
    });

    const handleCreated = (editor) => {
      editorRef.value = editor; // 记录 editor 实例,重要!
    };

    return {
      editorRef,
      valueHtml,
      mode: "simple", // 或 'simple'
      toolbarConfig,
      editorConfig,
      handleCreated,
    };
  },
};
</script>

<style scoped></style>

3.3 完成v-model 双向绑定

//vue2 中 v-model 语法唐
<!--
    1. 编辑器组件可以接收我传入的数据 并且可以显示到编辑器内部 实现数据的回显
    2. 一旦编辑器中进行内容编辑之后 绑定的content 属性 也可以得到修改 变成编辑器中最新的值

    体现出来的就是双向数据绑定的特性

    实现步骤:
    1.实现回显
    v-model 一旦写到一个组件身上 默认情况下 相当于传入了名为value 的自定属性 以及名为input的自定义事件
    v-model 其实是一个语法糖 可以写一次完成两件事情

    2.编辑器一旦编辑内容就把绑定的content属性修改掉
    难点1 如果知道内容已经遍及了 三方插件提供了方法
    难点2 如果拿到最新的编辑内容然后传出来给content(子传父)

 -->
// vue 2
<Editor v-model="content"></Editor>
// 相当于
<Editor :value="content" @input="content = $event" />
// Editor 组件
<template>
  <div>
    <input type="content" :value="value" @input="$emit('input',$event.target.value)">
  </div>
</template>

<script>
export default {
  props: {
    content: String,  // 默认接收一个名为 content 的 prop
  }
}
</script>
// vue3 中 v-model

<template>
  <div>
    <input
      type="text"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"  // 事件名改为 update:modelValue
    />
  </div>
</template>

<script>
export default {
  props: {
    modelValue: String, // 默认 prop 从 value 改为 modelValue
  },
};
</script>


网站公告

今日签到

点亮在社区的每一天
去签到