给后端同学的Vue课程(二):极简核心知识

186 阅读5分钟

插值与指令

<!-- {{ age }} 插值表达式 可写任意JS表达式 -->
<p>今年芳龄 {{ age }}</p>

<!-- 
v-on:click="onBtnClick" Vue指令 
v-xxx指令名 
冒号后是指令参数(可选) 
"引号内可写任意JS表达式" 
--------------------
!!!特别注意引号内是JS表达式而非普通字符串!!!
!!!特别注意引号内是JS表达式而非普通字符串!!!
!!!特别注意引号内是JS表达式而非普通字符串!!!
--------------------
-->
<button v-on:click="onBtnClick">SendMsg</button>

基础指令

v-if

  • renderHim数据成立时才渲染DOM结构
  • 否则不渲染,即DOM压根不存在
<span v-if="renderHim"> 我渲染与否由renderHim控制 </span>

v-show

  • showHim数据成立时才显示DOM结构
  • 否则display:none,DOM还在但不可见
<span v-show="showHim"> 我显示与否由showHim控制 </span>

v-for

  • 这里arr数组有几个元素,就在ul中渲染出几个li元素
  • index不需要时可以写作<li v-for="item in arr">{{ item }}</li>
<ul>
    <li v-for="(item, index) in arr">{{ index }}: {{ item }}</li>
</ul>

v-bind

  • 此处input标签的value属性动态地由数据count所控制
  • count变化,则input的value属性就随之变化,此之谓 数据驱动视图
  • 这一规则可以推广到任意标签的任意属性,包括自定义属性
  • v-bind:属性名="数据项"可以简写为:属性名="数据项"
<input type="text" v-bind:value="count">
<input type="text" :value="count">

v-on

  • 此处button标签的click事件动态地由方法subCount所处理
  • 这一规则可以推广到任意标签的任意事件,包括自定义事件
  • v-on:事件名="事件处理函数"可以简写为@事件名="事件处理函数"
<button v-on:click="subCount">-</button>
<button @click="addCount">+</button>
export default {
    data() {
        return {
            ...
        };
    },
    methods: {
        subCount() {
            this.count--;
        },
        addCount() {
            this.count++;
        }
    },
    components: { RouterLink }
}

v-model

  • 此处一方面checkbox的checked属性值由数据 allSelected控制,当数据主动变化时,元素属性也随之变化;
  • 另一方面,当用户在表单元素身上交互时,会反向去修改数据,所有与该数据关联的状态都会随之发生改变;
  • 这就是大名鼎鼎的 表单元素的双向数据绑定
  • 此处checkbox的checked属性+change事件与数据allSelected的双向同步关系可以推广到所有的表单元素;
  • 更多案例请查阅 官方文档
<input type="checkbox" :checked="allSelected" @change="allSelected = !allSelected">
<input type="checkbox" v-model="allSelected">

组件化开发

组件拆分

  • 页面中你认为有必要复用的部分都可以拆出去形成一个独立子组件;
  • 组件拆分的目的是好复用、好维护;
  • Vue项目开发本质就是在维护一棵组件树,做好它们的数据和互动关系;

简单的父组件实现

<template>
    <div>
        <Son></Son>
    </div>
</template>

<script>
import Son from "../../components/Son.vue"
export default {
    components: {
        Son
    },
}
</script>

简单的子组件实现

<template>
    <div>
        <h1>爷是儿子</h1>
    </div>
</template>

父子通信

  • 父组件向子组件传递自己的数据主要是通过props来实现的;
  • 子组件需要声明接收这些props;
  • 当父组件的数据发生变化时,子组件能够自动响应;

父组件向子组件传递数据

<template>
    <div>
        <Son :msg="msgFromDad"></Son>
    </div>
</template>

<script>
import Son from "../../components/Son.vue"
export default {
    components: {
        Son
    },
    data() {
        return {
            msgFromDad:"好好学习,天天向上"
        }
    },
}
</script>

子组件对接收的数据进行声明

props: ["msg"],

使用来自父组件的数据

<template>
    <div>
        <h1>爷是儿子 {{ name }}</h1>
        <p>爹的信息 {{ msg }}</p>
    </div>
</template>

<script>
export default {
    props: ["msg"],
}
</script>

子父通信

  • 当子组件需要去修改来自父组件的数据时,需要发送自定义事件
  • 子组件需要声明这些自定义事件;
  • 父组件在部署子组件时需要声明对这些自定义事件的处理方式;
  • 这样当子组件发送事件时父组件就能响应并修改自身的数据状态了;

子组件声明需要发送的自定义事件

emits: ["sendMsg"],

在子组件中点击按钮向父组件发送自定义事件

<template>
    <div>
        <p>爹的信息 {{ msg }}</p>
        <button @click="onBtnClick">SendMsg</button>
    </div>
</template>

<script>
export default {
    props: ["msg"],
    emits: ["sendMsg"],
    methods: {
        onBtnClick() {
            // 发送自定义事件并携带数据
            this.$emit("sendMsg", "爹美国的同学们都坐地铁上学只有我是开跑车")
        }
    },
}
</script>

父组件处理来自子组件的自定义事件

<template>
    <div>
        <Son :msg="msgFromDad" @sendMsg="onMsg"></Son>
        <hr>
        <button @click="showSon = !showSon">狗带</button>
    </div>
</template>

<script>
import Son from "../../components/Son.vue"
export default {
    components: {
        Son
    },
    data() {
        return {
            msgFromDad:"好好学习,天天向上"
        }
    },
    methods:{
        onMsg(msg){
            console.log("onMsg",msg);
            this.msgFromDad = "信息已收到,已经打过去20亿,快去给劳资买辆地铁,别在外面给我丢人!"
        }
    }
}
</script>

Video_2023-02-04_165815 00_00_00-00_00_30.gif

生命周期

概述

  • 一个组件在内存中的形态本质上是一个JS对象;
  • 所以一个组件从创建到消亡对应着一个JS对象在内存中从创建到消亡的过程;
  • 一个完整的组件生命周期要经历创建、挂载(即渲染DOM)、更新(数据状态发生变化时会驱动视图更新)、销毁四个阶段;
  • 在每个阶段执行前和执行完毕时分别会有一次插入自定义逻辑(钩子)的机会,因此形成的8个对应生命周期分别为:
  • 实例创建阶段:beforeCreate + created;
  • 实例挂载阶段:beforeMount + mounted;
  • 实例更新阶段:beforeUpdate + updated;
  • 实例销毁阶段:beforeDestroy + destroyed;

图解

lifecycle.png

常用钩子

我们最常用的三个生命周期为:

  • 挂载完毕(mounted):此时DOM已经渲染完毕,我们常常在此时去联网加载数据,数据初始化完毕视图即可驱动视图更新;
  • 更新完毕(updated):此时数据更新完毕,我们可以按需做一些必要的校正或同步工作;
  • 即将销毁(beforeDestroy):组件即将销毁,我们常常会在此阶段做一些数据保存和资源清理的工作;

更多内容请查阅 官方文档

计算属性

概述

  • 计算属性computed本质为依赖一手数据计算而来的二手数据;
  • 当一手数据发生变化时,我们需要一个计算过程换算出一些二手数据;
  • 例如从人口普查的一手数据中我们可以换算得到男女比例、城乡比例、高等教育比例等等;
  • 因此计算属性在形态上类似于一个方法:
/* 方法定义 */
methods: {
    /* 由周岁换算虚岁 */
    xusuiMethod() {
        console.log("xusuiMethod");
        return this.age + 1
    }
},

/* 计算属性定义 */
computed: {
    /* 由周岁换算虚岁 */
    xusuiComputed() {
        console.log("xusuiComputed");
        return this.age + 1
    }
},

在模板上使用二者能得到相同效果:

<p>虚岁from方法: {{ xusuiMethod() }}</p>
<p>虚岁计算属性: {{ xusuiComputed }}</p>

计算属性 VS 方法

计算属性与常规方法的不同之处在于:

  • 方法在每次组件更新(DOM需要重新渲染)时都会重新执行;
  • 而计算属性只要它所依赖的一手数据项没有发生变化,就不会重新执行,而是直接调用更新前的数据缓存;
  • 从这个角度而言,在功能相同的前提下,相比于方法,计算属性能够有效地提升组件性能!

案例

  • 本例我们在组件中每秒更新一次时间数据date
  • 该数据不影响计算虚岁所依赖的一手数据项age,因此当组件更新时,计算属性不会重新执行;
  • 其对应的方法则会在每次组件更新时都执行,性能上不如计算属性高;
<template>
    <div>
        <p>当前时间: {{ date }}</p>
        <p>实岁:{{ age }}</p>
        <p>虚岁from方法: {{ xusuiMethod() }}</p>
        <p>虚岁计算属性: {{ xusuiComputed }}</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            age: 10,
            date: "",
            timer: null
        }
    },

    /* 方法定义 */
    methods: {
        /* 由周岁换算虚岁 */
        xusuiMethod() {
            console.log("xusuiMethod");
            return this.age + 1
        }
    },

    /* 计算属性定义 */
    computed: {
        /* 由周岁换算虚岁 */
        xusuiComputed() {
            console.log("xusuiComputed");
            return this.age + 1
        }
    },

    /* 组件挂载时每秒更新一下时间 */
    mounted() {
        console.log("mounted");
        if (!this.timer) {
            this.timer = setInterval(() => {
                console.log("timer is running...");
                this.date = new Date().toLocaleString()
            }, 1000);
        }
    },

    updated() {
        console.log("updated");
    },

    /* 组件销毁前清除定时器 否则它会在后台继续执行 */
    beforeDestroy() {
        console.log("beforeDestroy");
        if (this.timer) {
            clearInterval(this.timer)
            this.timer = null
            console.log("timer cleared");
        }
    }
}
</script>

执行效果

Video_2023-02-04_175903 00_00_00-00_00_30.gif


本系列教程源码 点赞收藏加关注哦 😈