Vue 学习笔记-2022/12/09

Vue 组件深入

一、响应性组件 props 和 组件刷新

ZMLFNKAZU9H_2L6C0UVDFIR

<template>
  <main>
    <div class="container">
      <PaginationComponent
        :totalPage="totalPage"
        :defaultCurrentPage="currentPage"
      />
        <!-- totalPage 发生变化 同时 data也发生变化 也会传给PaginationComponent 子组件 子组件也随着刷新 -->
      <button @click="totalPage += 1">增加页数</button>
    </div>
  </main>
</template>

<script>
import PaginationComponent from "./components/PaginationComponent.vue";

export default {
  components: {
    PaginationComponent,
  },
  data() {
    return {
      totalPage: 6,
      currentPage: 4,
    };
  },
};
</script>

<template>
  <div>
    <p>当前页: {{ currentPage }}</p>
      <!--接收父组件的 n 然后传给子组件-->
    <button v-for="n in totalPage" @click="changePage(n)">
      {{ n }}
    </button>
  </div>
</template>
<script>
export default {
  props: ["totalPage", "defaultCurrentPage"],
  data() {
    return {
      currentPage: this.defaultCurrentPage,
    };
  },
  methods: {
    changePage(n) {
      this.currentPage = n;
    },
  },
};
</script>
二、 组件的数据流向


在子组件里最好使用 emit 去触发事件

其他属性则可以用 props 进行传递

三、组件的生命周期

四、Provide / Inject 给深度组件传值

provide 在我的个人理解就是出值

Inject 则是接收值

provide 有两种用法

// 可以使用 this 但是必须为函数 内容是响应式 
provide() {
    return {
      title: this.movie.title,
    };
  },
// 直接返回 内容是静态的
provide: {
  title: "测试电影",
},

Inject 用法

export default {
  // 在数组里定义 provide 的属性名
  inject: ["title"],
};
五、在传递slot模板中访问子组件的属性

<template>
  <ul>
    <li v-for="contact in contacts" :key="contact.id">
      <slot />
    </li>
  </ul>
</template>

<template>
  <main>
    <div>
        <!-- 这里使用了解构语法 因为子组件只是声明了一个默认的 solt 模板 -->
        <ContactList v-slot="{ contact }">
        <!--  // 在多个模板的情况下 还是需要去定义对象名的 {props为自定义变量名} -->
        <!-- <ContactList v-slot="props"> -->
        <!-- <ContactList v-slot:default="props"> -->
        <!-- <template v-slot:default="props"> -->
        <!-- <p>{{ props.contact.name }}</p>
        <!-- <p>{{ props.contact.email }}</p> -->
        <p>{{ contact.name }}</p>
        <p>{{ contact.email }}</p>
        <!-- </template> -->
      </ContactList>
    </div>
  </main>
</template>

<script>
import ContactList from "./components/ContactList.vue";

export default {
  components: {
    ContactList,
  },
};
</script>
六、组件支持 v-model
1.单个 v-model 绑定

<template>
  <main>
    <div>
      <SearchInput v-model="searchTerm" />
      <p>{{ searchTerm }}</p>
    </div>
  </main>
</template>

<script>
import SearchInput from "./components/SearchInput.vue";

export default {
  components: {
    SearchInput,
  },
  data() {
    return {
      searchTerm: "",
    };
  },
};
</script>

<template>
  <label
    ><span>搜索:</span
    ><input
      type="text"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    />
  </label>
</template>
<script>
export default {
  // props 和 emits 都为固定接收内容
  props: ["modelValue"],
  emits: ["update:modelValue"],
};
</script>
2.多个 v-model 绑定

<template>
  <label
    ><span>搜索:</span
    ><input
      type="text"
      :value="searchTerm"
      @input="$emit('update:searchTerm', $event.target.value)"
    />
  </label>
  <label>
    <span>类别:</span>
    <select
      :value="category"
      @change="$emit('update:category', $event.target.value)"
    >
      <option value="default">默认</option>
      <option value="fontend">前端</option>
      <option value="backend">后端</option>
      <option value="fullstack">全栈</option>
    </select>
  </label>
</template>
<script>

<template>
  <main>
    <div>
      <!-- v-model 后面的参数必须和子组件接收的属性名相同,例如 searchTerm -->
      <SearchInput
        v-model:searchTerm="searchTerm"
        v-model:category="category"
      />
      <div class="splitLine"></div>
      <p>搜索词:{{ searchTerm }}</p>
      <p>类别:{{ category }}</p>
    </div>
  </main>
</template>

<script>
import SearchInput from "./components/SearchInput.vue";

export default {
  components: {
    SearchInput,
  },
  data() {
    return {
      // 名字无需和 SearchInput 中的属性名相同
      // 例如这里可以叫 searchQuery,
      searchTerm: "",
      category: "default",
    };
  },
};
</script>
七、Ref实例

万不得已使用,这个会破坏组件数据流向

1.获取Dom实例
<template>
  <input type="text"  ref="inputControl" />
</template>

<script>
export default {
  //获取所有的refs拿到设置的ref 也就是inputControl 然后使用获取焦点的事件
  mounted() {
    this.$refs.inputControl.focus();
  },
};
</script>
2.获取组件实例

<template>
  <!-- 绑定inputText  -->
  <input type="text" v-model="inputText" ref="inputControl" />
</template>

<script>
export default {
  data() {
    return {
      inputText: "",
    };
  },
  mounted() {
    this.$refs.inputControl.focus();
  },
  methods: {
    blur() {
      this.$refs.inputControl.blur();
    },
  },
};
</script>

<template>
  <main>
    <div>
      <AutoFocus ref="autofocus" />
    </div>
  </main>
</template>

<script>
import AutoFocus from "./components/AutoFocus.vue";
export default {
  components: {
    AutoFocus,
  },
  mounted() {
    // 拿到 ref 实例就能操作 子组件里面的方法
    setTimeout(() => {
      console.log(this.$refs.autofocus.inputText);
      this.$refs.autofocus.blur();
    }, 5000);
  },
};
</script>
八、自定义指令

自定义组件实际上是就是一系列的生命周期的钩子

在不同生命周期进行操作数据

app.directive("fsize", {
  mounted(el, binding) {
    el.style.fontSize = binding.value + "px";
  },
  // 这样在 data 更新时,才会触发指令更新
  updated(el, binding) {
    el.style.fontSize = binding.value + "px";
  },
});

// 如果 mounted 和 updated 的代码相同,可以合并为一个:
app.directive("fsize", (el, binding) => {
  el.style.fontSize = binding.value + "px";
});

// 带有 args:
app.directive("fsize", (el, binding) => {
  el.style.fontSize = binding.value + (binding.arg || "px");
});


// 进行使用
<p v-fsize:[unit]="fontSize">这是一个段落</p>
// [unit] 为响应式数据

<script>
export default {
  data() {
    return {
      fontSize: 18,
      unit: "em",
    };
  },
};
</script>
九、动态组件
1.设置动态的 h 标签
<!-- TextHeading.vue -->
<template>
  <Component :is="heading"><slot></slot></Component>
</template>

<script>
export default {
  props: ["level"],
  computed: {
    heading() {
      return `h${this.level}`;
    },
  },
};
</script>

<template>
  <main>
    <div>
      <!-- 动态 HTML 元素  -->
      <TextHeading level="1">一级标题</TextHeading>
      <TextHeading level="2">二级标题</TextHeading>
      <TextHeading level="3">三级标题</TextHeading>
      <TextHeading level="4">四级标题</TextHeading>
      <TextHeading level="5">五级标题</TextHeading>
      <TextHeading level="6">六级标题</TextHeading>
    </div>
  </main>
</template>

<script>
import TextHeading from "./components/TextHeading.vue";
export default {
  components: {
    TextHeading,
  },
};
</script>
2.动态切换组件
  1. ProfileForm.vue
<template>
  <form @submit.prevent>
    <label>昵称:<input type="text" /></label>
    <label>生日:<input type="date" /></label>
    <label>地址:<input type="text" /></label>
  </form>
</template>
<script>
export default {};
</script>
<style scoped>
::-webkit-calendar-picker-indicator {
  filter: invert(1);
}
</style>
  1. RegisterForm.vue
<template>
  <form @submit.prevent>
    <label>手机号:<input type="number" /></label>
    <label
      >验证码:<input type="number" /><button class="sendSMSCodeBtn">
        发送验证码
      </button></label
    >
  </form>
</template>
<script>
export default {};
</script>
<style scoped>
.sendSMSCodeBtn {
  margin-left: 24px;
}
</style>
  1. App.vue
<!-- 弊端 无法保存数据 每次切换都会生成新的组件实例 -->
<template>
  <main>
    <div>
       <!--  keepAlive 可以缓存数据 -->
     <KeepAlive>
        <Component :is="currentForm" />
      </KeepAlive>
      <div class="buttons">
        <button
          v-if="currentForm === 'RegisterForm'"
          @click="currentForm = 'ProfileForm'"
        >
          下一步
        </button>
        <template v-else-if="currentForm === 'ProfileForm'">
          <button @click="currentForm = 'RegisterForm'">
            上一步
          </button>
          <button>完成</button>
        </template>
      </div>
    </div>
  </main>
</template>

<script>
import RegisterForm from "./components/RegisterForm.vue";
import ProfileForm from "./components/ProfileForm.vue";
export default {
  components: {
    RegisterForm,
    ProfileForm,
  },
  data() {
    return {
      currentForm: "RegisterForm",
    };
  },
};
</script>
十、组件传送
1. teleport 传送
<!-- 给 to 属性对应的选择器就可以进行传送 -->
<Teleport to="body">
    <div v-if="show" class="alertBox">
      <div class="closeIcon" @click="show = false">X</div>
      <div class="content">
        <slot>消息提示框组件</slot>
      </div>
    </div>
</Teleport>
2.teleport 多次传送

根据自定义的Css进行追加

十一、编程式的模板
1.使用渲染函数

使用它可以直接在模板里使用 JavaScript 语法

难搞 感觉这个东西麻烦的很

<script>
import { h } from "vue";

export default {
  props: ["title"],
  render() {
    return h("div", { class: "card" }, [
      h("div", { class: "title" }, this.title),
      h("div", { class: "content" }, this.$slots.default()),
    ]);
  },
};
</script>
2.渲染函数中使用指令
<script>
import { h } from "vue";

export default {
  props: ["title"],
  render() {
    // map 代替 v-for,if/else 代替 v-if,children 可以是数组
    return h("div", { class: "card" }, [
      h("div", { class: "title" }, this.title),
      [
        h("div", { class: "content" }, this.$slots.default()),
        [1, 2, 3, 4].map((item) => h("h" + item, {}, item)),
      ],
    ]);
  },
};
</script>
十二、Mxins 组件配置

现在不推荐使用了,有更好的进行替代了

其实在我的个人理解 mixin 配置就和个普通的对象一样 只是别的组件可以进行复用而已 只需要在 mixins: [xxx] 导入即可,当然是我的个人理解,可能有错

1. 普通使用
// 导出
const PaginationMixin = {
  props: ["totalPage", "defaultCurrentPage"],
  data() {
    return {
      currentPage: this.defaultCurrentPage,
    };
  },
  methods: {
    changePage(page) {
      this.currentPage = page;
    },
  },
};

export default PaginationMixin;

// 导入
import PaginationMixin from "../mixins/PaginationMixin";

export default {
  mixins: [PaginationMixin],
};


如果 mixin 的属性和组件的属性发生的冲突 则组件的属性会覆盖 mixin 的相同属性,不同的属性则会进行合并

如果 mixin 的生命周期钩子和 组件的生命周期相同,这个时候二者都会执行,不过 mixin的钩子优先级更高会先执行

2. 全局 mixin 配置

全局 mixin 配置 适合给组件做通用的配置,或者是自定义的配置

// 需要用 this 访问
app.mixin({
  siteTitle: "我的 Vue 应用",
  computed: {
    siteTitle() {
      return this.$options.siteTitle;
    },
  },
});
十三、异步组件
1.组件异步加载
import { defineAsyncComponent } from "vue";

// es6 异步导入 + defineAsyncComponent 实现第一次挂载时导入
const ProductPage = defineAsyncComponent(() =>
  import("./components/ProductPage.vue")
);
十四、组件错误处理
1.全局处理
app.config.errorHandler = (err, vm, info) => {
  console.log(err);
  console.log(vm);
  console.log(info);
};
2.局部组件处理(错误边界)

F)JRYX4YIPS9JCXAQ(VSPK7

 // 错误捕获生命周期钩子
  errorCaptured(err, instance, info) {
  },
End

本文标题: Vue 学习笔记-2022/12/09

本文链接: https://dnslin.com/archives/80.html

除非另有说明,本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

声明:转载请注明文章来源。

最后修改:2022 年 12 月 09 日
如果觉得我的文章对你有用,请随意赞赏