9.组件事件

150 阅读3分钟

知识点

1.全局样式 在vue中想使用一些全局样式,只需要在main.js导入的文件就可以了。

main.js导入global.less 就可以了

// 入口文件
import Vue from "vue";
import App from "./App.vue";
import "./styles/global.less";

new Vue({
  render: (h) => h(App),
}).$mount("#app");

var.less

@import "./var.less";

html {
  color: @words;
  font-family: "-apple-system", "BlinkMacSystemFont", "Roboto", "Helvetica Neue",
    "微软雅黑", sans-serif;
  overflow: hidden;
}
body {
  overflow: hidden;
  margin: 0;
}

a {
  color: inherit;
  text-decoration: none;
  &:hover {
    color: @primary;
  }
}

var.less

@danger: #cc3600; // 危险、错误
@primary: #6b9eee; // 主色调、链接
@words: #373737; // 大部分文字、深色文字
@lightWords: #999; // 少部分文字、浅色文字
@warn: #dc6a12; // 警告
@success: #7ebf50; // 成功
@gray: #b4b8bc; // 灰色
@dark: #202020; // 深色

2.v-if 和 v-show

image-20201113133827438

image-20201113134051281

面试题:v-if 和 v-show 有什么区别?

1.v-if能够控制是否生成vnode,也就间接控制了是否生成对应的dom。当v-if为true时,会生成对应的vnode,并生成对应的dom元素;当其为false时,不会生成对应的vnode,自然不会生成任何的dom元素。

2.v-show始终会生成vnode,也就间接导致了始终生成dom。它只是控制dom的display属性,当v-show为true时,不做任何处理;当其为false时,生成的dom的display属性为none。

3.使用v-if可以有效的减少树的节点和渲染量,但也会导致树的不稳定;而使用v-show可以保持树的稳定,但不能减少树的节点和渲染量。

因此,在实际开发中,显示状态变化频繁的情况下应该使用v-show,以保持树的稳定;显示状态变化较少时应该使用v-if,以减少树的节点和渲染量。

变化频繁用v-show,变化少用v-if.

APP.vue Pager传入total,current两个属性,注册了 @事件 = "js表达式"。vue 中子组件不能改变从外部传入的数据,要想改变,需要先通过事件传入到父组件,在数据的源头进行更改,或其他操作。iOS可以从Block,或者代理把事件传递出去,vue是通过子组件的事件传递出来。

<template>
  <div>
    <h1>App组件</h1>
    <Pager
      :total="total"
      :current="current"
      @pageChange="handlePageChange($event)"
    />
  </div>
</template>

<script>
import Pager from "./components/Pager";
export default {
  components: {
    Pager,
  },
  data() {
    return {
      current: 1,
      total: 302,
    };
  },
  methods: {
    handlePageChange(newPage) {
      this.current = newPage;
      console.log("加载当前页数据");
    },
  },
};
</script>

<style scoped></style>

Pager.vue

<template>
  <!-- 只有总页数大于1时才显示 -->
  <div class="pager-container" v-if="pageNumber > 1">
    <a @click="handleClick(1)" :class="{ disabled: current === 1 }">
      |&lt;&lt;
    </a>
    <a @click="handleClick(current - 1)" :class="{ disabled: current === 1 }">
      &lt;&lt;
    </a>
    <a
      @click="handleClick(n)"
      v-for="(n, i) in numbers"
      :key="i"
      :class="{ active: n === current }"
    >
      {{ n }}
    </a>

    <a
      @click="handleClick(current + 1)"
      :class="{ disabled: current === pageNumber }"
    >
      &gt;&gt;
    </a>
    <a
      @click="handleClick(pageNumber)"
      :class="{ disabled: current === pageNumber }"
    >
      &gt;&gt;|
    </a>
  </div>
</template>

<style lang="less" scoped>
@import "~@/styles/var.less";
.pager-container {
  display: flex;
  justify-content: center;
  margin: 20px 0;
  a {
    color: @primary;
    margin: 0 6px;
    cursor: pointer;
    &.disabled {
      color: @lightWords;
      cursor: not-allowed;
    }
    &.active {
      color: @words;
      font-weight: bold;
      cursor: text;
    }
  }
}
</style>

<script>
export default {
  props: {
    current: {
      type: Number,
      default: 1,
    },
    total: {
      type: Number,
      default: 0,
    },
    limit: {
      type: Number,
      default: 10,
    },
    visibleNumber: {
      type: Number,
      default: 10,
    },
  },
  computed: {
    // 总页数
    pageNumber() {
      return Math.ceil(this.total / this.limit);
    },
    // 得到显示的最小数字
    visibleMin() {
      let min = this.current - Math.floor(this.visibleNumber / 2);
      if (min < 1) {
        min = 1;
      }
      return min;
    },
    visibleMax() {
      let max = this.visibleMin + this.visibleNumber - 1;
      if (max > this.pageNumber) {
        max = this.pageNumber;
      }
      return max;
    },
    numbers() {
      let nums = [];
      for (let i = this.visibleMin; i <= this.visibleMax; i++) {
        nums.push(i);
      }
      return nums;
    },
  },
  methods: {
    handleClick(newPage) {
      if (newPage < 1) {
        newPage = 1;
      }
      if (newPage > this.pageNumber) {
        newPage = this.pageNumber;
      }
      if (newPage === this.current) {
        return;
      }
      // 抛出一个事件
      this.$emit("pageChange", newPage);
    },
  },
};
</script>

1.在组件开发的时候,一般是在组件内部放一个容器div,div的类名一般是 "组件名-container",这是习惯做法

2.v-if,这里只在开始的时候判断pageNumber是否有,更改不频繁,用v-if,< 代表是左箭头

3.class绑定的是一个对象类型,对象类型里面可以按 {a样式:true,b样式:true, c样式:flase}就代表 a样式,b样式生效,c样式不生效。 :class="{ disabled: current === 1 } 代表current 为1的时候disabled生效。

  1. 分析一下这一句,v-for 中n,和序号i不轮写前写后,当前这一句都能用,key这里不能绑定i序号来提高效率,:class="{ active: n === current } 仍然是如果当前的n===current的时候active样式生效。 {{ n }} 是大胡子语法。
 <a
      @click="handleClick(n)"
      v-for="(n, i) in numbers"
      :key="i"
      :class="{ active: n === current }"
    >
      {{ n }}
    </a>
  1. <style lang="less" scoped> lang="less代表里面写的是less代码,less里面可以定义变量。

  2. script里面的代码 props代表父组件传进来的属性,type定义类型,default 代表默认值。

7.computed里面是计算属性, pageNumber()在computed是带括号写的,但是在用的时候是直接用pageNumber就可以了。、

8.methods 里面定义的是方法, this.$emit("pageChange", newPage);就是为了因为数据是单项数据流,不能直接修改,需要抛出事件,让父组件去修改。

v-if 多个组件

<template>
  <div class="user-name">
    <span v-if="status === 'loading'">loading...</span>

    <template v-else-if="status === 'login'">
      <router-link to="/user">{{ user.name }}</router-link>
      <a href="" @click.prevent="handleLoginOut">退出</a>
    </template>

    <router-link v-else to="/login" exact-path>Login</router-link>
  </div>
</template>

由于v-if 只能包一个组件,如果两个组件都在一个逻辑判断里面,可以使用template 来包一下。

3.组件事件

image-20201113134557175

抛出事件:子组件在某个时候发生了一件事,但自身无法处理,于是通过事件的方式通知父组件处理

事件参数:子组件抛出事件时,传递给父组件的数据

注册事件:父组件申明,当子组件发生某件事的时候,自身将做出一些处理

4.组件文档说明

属性
属性名含义类型必填默认值
current当前页码Number1
total总数据量Number0
limit页容量Number10
visibleNumber可见页码数Number10
事件
事件名含义事件参数参数类型
pageChange页码变化新的页码Number

如何使用组件?

编写组件说明文档

./src/components/README.md

如何测试组件效果?

1.新建一个和组件同名的文件夹,然后在文件夹里面放一个index.vue,然后在vue中放组件的代码.再新建一个test.vue,然后像app.vue中使用这个组件。

test.vue中的代码

<template>
  <div>
    <Pager
      :total="total"
      :current="current"
      @pageChange="handlePageChange($event)"
    />
  </div>
</template>

<script>
import Pager from "./";
export default {
  components: {
    Pager,
  },
  data() {
    return {
      current: 1,
      total: 302,
    };
  },
  methods: {
    handlePageChange(newPage) {
      this.current = newPage;
      console.log("加载当前页数据");
    },
  },
};

2.在使用导入的时候只需要按下面的方式就可以,webpack会自动导入的。

image.png

3.在 cli.vuejs.org/zh/guide/pr… 文档下安装

npm install -g @vue/cli-service-global

然后执行 vue serve ./src/test.vue 就可以测试某个组件。

4.如果觉得上面的命令长度太长,可以在package.json中加上下面的代码,测试的时候就可以直接使用 npm run test:Pager来代替上面长的命令。

"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test:Pager": "vue serve ./src/components/Pager/test.vue",
    "test:Avatar": "vue serve ./src/components/Avatar/test.vue",
    "test:Icon": "vue serve ./src/components/Icon/test.vue"
  },