• 作者:老汪软件技巧
  • 发表时间:2024-09-23 07:01
  • 浏览量:

在上一节曾简单的介绍了一下组件,却是以一个.vue文件的视角来介绍的,这一节我们以JS的视角的介绍一下组件。

既然vue是一个组件化开发的框架,那么布局、样式、事件交互、数据以及数据处理等功能肯定是一个组件必须具备的。对于组件的的编码风格有两种,一种是老版本vue就开始存在的选项式风格,另一种是从vue3才开始有的组合式风格。

刚开始学习Vue可直接在app.vue文件清空,测试单组件的用法,多组件的引用放在后续再讲。

预览项目工程安装依赖和预览项目

前面已经讲过了创建vue项目工程,接下来讲讲怎么预览这个项目。

第一步:使用终端命令行定位到当前项目的位置;

第二步:安装项目的依赖,刚开始创建的工程是没有安装项目所需的依赖,使用npm install命令即可安装项目所需的所有依赖,如果已经安装过了则可跳过。

第三步:使用命令:npm run dev命令来预览这个项目,当命令运行成功后,会提示一个预览地址,直接在浏览器打开即可。

package.json简要说明

项目的依赖和项目的预览、打包等命令都是定义在项目中的package.json文件中。scripts中定义了项目程中的预览、打包等命令。dependencies中定义了一些项目运行中所需的依赖,devDependencies中定义了一些项目开发过程中所需的依赖。

使用npm install命令可安装package.json中定义的所需依赖,使用npm run scripts中的命令名(如:npm run dev),可运行在scripts中定义的命令。

具体npm的学习可以 中学习或百度即可,刚开始只需了解什么是npm,安装、卸载依赖等即可。

选项式风格

选项式风格编码就是将组件的数据、逻辑处理等都以对象的属性、方法形式呈现。

1、响应式数据

选项式风格定义模板所使用的响应式数据都在data方法中,该方法返回一个数据对象,这个数据对象就是这个组件的响应式数据。



2、事件

根据HTML和JS的知识想要一个事件产生交互效果,有两个要素:定义事件、绑定事件。

定义事件是在methods属性中实现的,methods属性的值是一个对象,当前组件的事件方法都定义在这个对象中,同时在事件方法中,可以通过使用this关键字获取组件的中data部分的响应式数据。


绑定事件的语法格式为:@事件名="事件方法名", 如将上面的定义的两个方法绑定中在如下的两个button元素中,当点击这两个button按钮将会触发对应的事件修改n的值,当n的值发生变化时,页面也会随着n的更新而更新。

<template>
    <div>当前计数:{{n}}div>
    <button @click="up">加1button>
    <button @click="down">减1button>
template>

@后面的事件名,这个是和原生JS的事件名和事件作用一一应对的(change、keyup、keydown等事件),同时也可自定义事件名,这个会在后续组件传值的章节中讲到。

3、计算属性

模板中的表达式虽然方便,但如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。就可以将一些模板中的计算逻辑放在计算属性中。

_Vue快速上手:三、初识组件_vue的组件化是如何实现的

比如这种场景:有一批订单要给出订单的总金额、批次交易内最大订单金额、最小订单金额。如果按以上知识使用单纯的模板语法写出来的维护性和可读性就不好了,如:(如以下map、reduce等方法不知道可以去学习一下JS的ES6+语法和API)

<template>
    <div>最小:{{ goods.map(item => item.sum).reduce((prev, cur) => prev > cur? cur: prev) }}div>
    <div>最大:{{ goods.map(item => item.sum).reduce((prev, cur) => prev < cur? cur: prev) }}div>
template>
<script>
export default {
    data() {
        return {
            goods: [
                { id: 1, sum: 102 },
                { id: 2, sum: 30 },
                { id: 3, sum: 200 },
                { id: 4, sum: 46 }
            ]
        }
    }
}
script>

模板上本来就应该干净一些做一些插值即可,如以上这么费劲的在模板上做这么复杂的计算工作实在没必要(为能在模板中使用,多做了不必要的运算,先是用map重组数组然后又用reduce来计算,虽然看上去代码少但是运算量却多了,数据少还好数据一多就有性能问题了)。当然这种情况可以封装一个方法放在methods中,然后在模板中调用这个方法,但是不推荐,如以下:

<template>
    <div>最小:{{ min() }}div>
    <div>最大:{{ max() }}div>
template>
<script>
export default {
    data() {
        return {
            goods: [
                { id: 1, sum: 102 },
                { id: 2, sum: 30 },
                { id: 3, sum: 200 },
                { id: 4, sum: 46 }
            ]
        }
    },
    methods: {       
        min() {
            let min = this.goods[0].sum;
            for(let i = 1; i < this.goods.length; i++) {
                if(min > this.goods[i].sum) {
                    min = this.goods[i].sum;
                }
            }
            return min;
        },
        max() {
            let max = this.goods[0].sum;
            for(let i = 1; i < this.goods.length; i++) {
                if(max < this.goods[i].sum) {
                    max = this.goods[i].sum;
                }
            }
            return max;
        }
    }
}
script>

但是前面我们就说过methods中主要还是以定义事件方法为主,如果其它方法也定义在这一块,就会使得methods也会变得臃肿混乱同样不利于维护。所以最好的办法是像这种情况放在计算属性中,计算属性放在computer中,如以下:

<template>
    <div>最小:{{ min }}div>
    <div>最大:{{ max }}div>
template>
<script>
export default {
    data() {
        return {
            goods: [
                { id: 1, sum: 102 },
                { id: 2, sum: 30 },
                { id: 3, sum: 200 },
                { id: 4, sum: 46 }
            ]
        }
    },
    computed: {        
        min() {
            let min = this.goods[0].sum;
            for(let i = 1; i < this.goods.length; i++) {
                if(min > this.goods[i].sum) {
                    min = this.goods[i].sum;
                }
            }
            return min;
        },
        max() {
            let max = this.goods[0].sum;
            for(let i = 1; i < this.goods.length; i++) {
                if(max < this.goods[i].sum) {
                    max = this.goods[i].sum;
                }
            }
            return max;
        }
    }
}
script>

请注意计算属性它是一个属性,虽然在computed中定义的是一个个的方法,但是每一个方法名都会转换成属性名,所以在模板中使用的时候{{ sum() }}这种就是错的,正确的使用方式为{{ sum }}。

当计算属性中计算过程有依赖到响应式数据时,计算属性的值会随着响应式数据的变化自动更新计算结果到页面中。

4、侦听器

计算属性允许我们声明性地计算衍生值,并且随着响应式数据变化而变化,但是它毕竟是个值,能力有限。同样还有一些场景需要我们去侦听响应式数据的状态变化去执行一些操作,这时候就需要侦听器登场了,侦听器放在watch中,假如我们要侦听到n的值在小于0时就会弹出一个提示框出来,如下:

<template>
    <div>{{ n }}div>
    <button @click="handleN">点击button>
template>
<script>
export default {
    data() {
        return {
            n: 10
        }
    },
    watch: {
        n(newValue, oldValue) {
            if(newValue < 0) {
                newValue = oldValue;
                alert("n的值不能小于0");
            }
        }
    },
    methods: {
        handleN() {
            this.n--;
        }
    },
}
script>

使用watch侦听状态时,watch的方法名一定要和被侦听的响应式数据名要一致。如上例中被侦听的数据的名为n,所以在侦听器中要侦听到n就要使用n方法来侦听。

侦听器方法有两个参数newValue,oldValue,一个是侦听到属性变化后的新值,另一个是变化前的老值。

watch默认是深层的,当被侦听的属性是一个对象时,对象所有属性以及嵌套属性的改变都能侦听到。如果只想侦听第一层属性的变化时,则要用到浅侦听,需要加一个deep: false,这个属性默认是为true如:

watch() {
    myObj(newValue, oldValue) {
        // 执行的操作
    },
    deep: false // 开启浅层侦听
}

关于选项式风格不会讲得太详细,大多数情况还是用的组合式开发,当然有些特殊情况下和vue2版本的时候还是要用选项式,只是那时vue基本已经入门了,可以自行去看文档了。当然了基础打得好的建议直接看文档上手更快,而且理解更深。

组合式风格

组合式风格的写法和上面的选项式不一样,是使用一系列的组合式API(就是一系列公共函数方法),来开发。需要哪个就导入哪个使用,所有的组合式API方法在一个setup方法使用。如以下:

<script>
    // 导入vue提供的API
    import { ref, reactive } from "vue"; 
    
    export default {
        setup() {
            // 在此处进行组合式开发
            
            return {} // 返回要在模板中使用的响应式数据
        }
    }
script>

总结:将要使用的内置API或自定义的API导入,然后在合适的位置使用,最后将需要在模板中使用的数据返回出去。

同时为了方便大家书写更方便,vue后续又推出了一个组合式开发的语法糖写法script setup形式,如以下:

<script setup> // 这里的setup属性标记不能丢
    // 导入vue提供的API
    import { ref, reactive } from "vue";      
    // 其它组合式开发代码    
script>

这个语法糖写是不是方便了很多,在开发阶段了省去了export 导出、setup、return这几步,将其放在编译阶段自动补上,然后会将里面所声明的数据和方法都会自动返回出去。

1、响应式数据

声明响应式数据时,需要用到两个vue提供的API方法:ref和reactive,ref是用来声明普通类型的数据(如字符串、数值、布尔类型等),而reactive则用来声明引用类型的数据(对象、数组等)。如:

<template>
    <div>{{ msg }}div>
    <p>{{ studys.name }} --- {{ studys.age }} --- {{ studys.lang }}p>
template>
<script setup>
    import { ref, reactive } from "vue";
    const msg = ref("欢迎学习前端!");
    const studys = reactive({name: '林允儿', age: 18, lang: "kr"});    
   
script>

<style lang="scss" scoped>
style>

要修改响应式数据的值可分为两种情况:ref和reactive。