2、vue 组件化开发
2.7 自定义组件的 总结
- 编写组件时,应该 api 先行,先确定组件 该 如何给用户用,再根据 api 编写逻辑
- props 的名称 应该具备语义化,类型应该符合规范,并且可以添加自定义校验
- 组件上绑定的类似于原生的事件,默认是不会被识别的,需要额外处理
- 组件有一些设计需要整体把控,比如 props 与 对应类名的匹配,这是我们故意设计的
2.8 Editor 编辑器组件开发
通常,我们封装组件,不会自己从头开始封装, 一般来说都是借鉴别人的 第三方基础插件 进行封装
npm 安装 npm i wangeditor --save
CDN 链接 https://cdn.jsdelivr.net/npm/wangeditor@latest/dist/wangEditor.min.js
2.9 初始化 Editor
<template>
<div ref="editorRef">editor</div>
</template>
<script>
// 引入第三方的编辑器插件
import E from "wangeditor";
export default {
props: {},
data() {
return {};
},
created() {},
mounted() {
// 因我们需要 DOM 渲染完成 才能初始化
// 所以我们 再 mounted 钩子函数里面初始化
this.initEditor();
},
watch: {},
computed: {},
methods: {
initEditor() {
// 编辑器初始化方法
const editor = new E(this.$refs.editorRef);
editor.create();
},
},
components: {},
};
</script>
<style lang="less" scoped>
</style>
2.10 实现数据的回显
app.vue
<template>
<div>
<br />
<!--
1、编辑器组件可以接收我传入的数据,并且可以显示到编辑器内部,实现数据的回显
2、一旦编辑器中进行内容编辑之后,绑定的 content 属性,也可以得到修改,变成编辑器中最新的值
-->
<!--
实现步骤
1、实现 回显:
v-model 一旦写到 一个组件身上,默认情况下 相当于
传入了名为 value 的自定义 属性,以及名为 input 的自定义事件
v-model 其实是一个语法糖,可以写一次完成两件事情
-->
<Editor v-model="content" />
</div>
</template>
<script>
import Editor from "./components/Editor/index";
export default {
props: {},
data() {
return {
content: "我是编辑器,我出来了",
};
},
components: {
DButton,
Editor,
},
};
</script>
<style lang="less" scoped>
</style>
index.vue
methods: {
initEditor() {
// 编辑器初始化方法
const editor = new E(this.$refs.editorRef);
editor.create();
editor.txt.html(this.value); // 重新设置编辑器内容
},
},
2.11 实现 编辑器内容更改,传出到 content
就是子传父
2、编辑器一旦编辑内容就把 绑定的 content 属性修掉
1)如何 知道 内容已经编辑了,三方插件提供了方法
2)如果拿到最新的编辑内容然后传出来 交给 content (子传父) $emit(“input”,传递的参数)
methods: {
initEditor() {
// 编辑器初始化方法
const editor = new E(this.$refs.editorRef);
// 配置 onchange 回调函数
editor.config.onchange = (newHtml) => {
// newHtml 就是编辑器 拿到的,最新的内容
this.$emit("input", newHtml);
console.log("change 之后最新的 html", newHtml);
};
// 配置触发 onchange 的时间频率,默认为 200ms
editor.config.onchangeTimeout = 500; // 修改为 500ms
editor.create();
editor.txt.html(this.value); // 重新设置编辑器内容
},
2.11 解决bug,首次数据回显是可以的,第二次不行
如果我们 content 修改了, 会传递到自定义组件,只是自定义组件没有调用相应的方法,进行重新赋值
实现思路,就是每一次 content 发生变化, props中都能 拿到最新的值
我们可以 监控以下, props 中的 value 一旦 value 发生变化,就重新执行一下设置编辑器内容的方法
用 watch 也可以
methods: {
initEditor() {
// 编辑器初始化方法
const editor = new E(this.$refs.editorRef);
// 配置 onchange 回调函数
editor.config.onchange = (newHtml) => {
// newHtml 就是编辑器 拿到的,最新的内容
this.$emit("input", newHtml);
console.log("change 之后最新的 html", newHtml);
};
// 配置触发 onchange 的时间频率,默认为 200ms
editor.config.onchangeTimeout = 500; // 修改为 500ms
editor.create();
editor.txt.html(this.value); // 重新设置编辑器内容
// 当 content 发生变化, props 其实 已经拿到了,最新的值,但是 视图中, 并没有发生变化
// 我们可以 监控一下, props 中的 value 一旦 value 发生变化,就重新执行一下设置编辑器内容的方法
// 用 内置 的 watch 的 optionsAPI
// $watch 方法 和 watch 配置项 内部实现的 核心原理是一致的,但是 $watch 更加灵活,它并不是组件进行初始化就立刻坚挺的,而是 再某个时刻 你想要监听 的时候,才进行的监听
this.$watch("value", (newVal, oldVal) => {
editor.txt.html(this.value); // 重新设置编辑器内容
});
},
},
2.12 总结
- 在实际开发中,可能会根据业务借助 三方成熟的插件 修改成我们自己的 vue组件
- 在自己的组件 中 把三方组件的功能按照 api 实现出来,然后 暴露一些 api 供用户使用
- 组件身上添加 v-model ,一般用于 组件内部 有可能输入的控件,比如 input textarea 内容编辑,目的是为了实现双向绑定,
- 一旦组件身上写了 v-model 之后,相当于 1. 名称为 value 的自定义属性的绑定 2. 名称为input 的自定义事件
- ===> <Editor :value="" @input="">
- 父子传参中 通过定义 props:{value} 来接收传下来的数据
- 用过 触发 $emit(‘input’) 实现将子组件的数据添加到父组件中
- 特别注意。v-model 语法糖下,我们并不需要 在父组件定义一个方法来修改 父组件给子组件传递 value 的值,vue系统自己知道要修改谁
源代码
app.vue
<template>
<div>
<br />
<!--
1、编辑器组件可以接收我传入的数据,并且可以显示到编辑器内部,实现数据的回显
2、一旦编辑器中进行内容编辑之后,绑定的 content 属性,也可以得到修改,变成编辑器中最新的值
-->
<!--
实现步骤
1、实现 回显:
v-model 一旦写到 一个组件身上,默认情况下 相当于
传入了名为 value 的自定义 属性,以及名为 input 的自定义事件
v-model 其实是一个语法糖,可以写一次完成两件事情
2、编辑器一旦编辑内容就把 绑定的 content 属性修掉
1)如何 知道 内容已经编辑了,三方插件提供了方法
2)如果拿到最新的编辑内容然后传出来 交给 content (子传父) $emit("input",传递的参数)
-->
<Editor v-model="content" />
</div>
</template>
<script>
import DButton from "./components/Button/index.vue";
import Editor from "./components/Editor/index";
export default {
props: {},
data() {
return {
content: "我是编辑器,我出来了",
};
},
created() {},
mounted() {},
watch: {},
computed: {},
methods: {
handle(e) {
console.log("点击了", e);
},
},
components: {
DButton,
Editor,
},
};
</script>
<style lang="less" scoped>
</style>
二次封装的组件源代码
<template>
<div ref="editorRef">editor</div>
</template>
<script>
// 引入第三方的编辑器插件
import E from "wangeditor";
export default {
props: {
value: {
type: String,
},
},
data() {
return {};
},
created() {},
mounted() {
// 因我们需要 DOM 渲染完成 才能初始化
// 所以我们 再 mounted 钩子函数里面初始化
this.initEditor();
},
watch: {},
computed: {},
methods: {
initEditor() {
// 编辑器初始化方法
const editor = new E(this.$refs.editorRef);
// 配置 onchange 回调函数
editor.config.onchange = (newHtml) => {
// newHtml 就是编辑器 拿到的,最新的内容
this.$emit("input", newHtml);
console.log("change 之后最新的 html", newHtml);
};
// 配置触发 onchange 的时间频率,默认为 200ms
editor.config.onchangeTimeout = 500; // 修改为 500ms
editor.create();
editor.txt.html(this.value); // 重新设置编辑器内容
// 当 content 发生变化, props 其实 已经拿到了,最新的值,但是 视图中, 并没有发生变化
// 我们可以 监控一下, props 中的 value 一旦 value 发生变化,就重新执行一下设置编辑器内容的方法
// 用 内置 的 watch 的 optionsAPI
// $watch 方法 和 watch 配置项 内部实现的 核心原理是一致的,但是 $watch 更加灵活,它并不是组件进行初始化就立刻坚挺的,而是 再某个时刻 你想要监听 的时候,才进行的监听
this.$watch("value", (newVal, oldVal) => {
editor.txt.html(this.value); // 重新设置编辑器内容
});
},
},
components: {},
};
</script>
<style lang="less" scoped>
</style>