Vue笔记(更新中)

Vue的简介

官网文档

地址:https:/cn.vuejs.org/

Vue开发前的准备

node -v要大于15.0的版本

在指定目录下的命令行创建vue项目

npm init vue@latest

填写项目名称,然后全部回车即可,最后运行下面三行命令进行初始化

a1dffa4021994ee7bf920383f0862ee3.png

开发环境

推荐的IDE配置是VScode + Volar扩展插件

Vue项目目录结构

d183ec775ef5447cb3fe63588d9b7529.png

1 .vscode                                                           --- VSCode工具配置文件夹

2 node_modules                                                --- Vue项目运行依赖文件夹

3 public                                                              --- 资源文件夹(浏览器图标)

4 src                                                                  --- 源码文件夹

5 .gitignore                                                        --- git忽略文件

6 index.html                                                      --- 入口的HTML文件

7 package.json                                                 --- 信息描述文件

8 README.md                                                 --- 注释文件

9 vite.config.js                                                  --- Vue的配置文件

模板语法

先删除components和assets中所有文件,再将App.vue文件几乎全删,只剩一对</template>和</script>。

文本插值

最基本的数据绑定形式是文本插值,它使用的是“双大括号”语法:

<template>
  <h3>模板语法</h3>
  <p>{{ msg }}</p>
  <p>{{ hello }}</p>
</template>

<script>
export default{
    data(){
        return{
            msg:"神奇的语法",
            hello:"Hello World!"
        }
    }
}
</script>

使用JS表达式

每绑定仅支持单一表达式,也就是一段能够被求值的js代码,一个简单的合法判断语句是可以在写在return后面的:

<template>
  <p>{{ number+1 }}</p>
  <p>{{ ok? "Yes" : "No" }}</p>
  <p>{{ msg.split("").reverse().join("") }}</p>
</template>

<script>
export default{
    data(){
        return{
            number:10,
            ok:true,
            msg:"大家好"
        }
    }
}
</script>

无效

<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}

<!-- 条件控制不支持,请使用三元表达式 -->
{{ if(ok){return msg} }}

原始HTML

双大括号将会将数据插值为纯文本,而不是HTML,若想插入HTML,你需要使用v-html指令:’

<template>
  <p>{{ rawHtml }}</p>
  <p v-html="rawHtml"></p>
</template>

<script>
export default{
    data(){
        return{
            rawHtml:"<a href='http://itbaizhan.com'>百战程序员</a>"
        }
    }
}
</script>

属性绑定

双大括号不能在HTML属性中使用,想要变量绑定一个属性的属性值,应该使用v-bind指令

<template>
    <div>{{ dynamicId }}</div>
    <div v-bind:class="dynamicClass"></div>
 </template>

<script>
export default{
    data(){
        return{
            dynamicId:"appid",
            dynamicClass:"appclass"
        }
    }
}
</script>

此时第二个div标签中class属性值为appclass

如果绑定的属性值为null,就等于没有添加该属性

简写

省去v-bind,直接:属性='变量'

<div :class="dynamicClass"></div>



布尔类型

<template>
    <button :disabled="isButtonDisabled">Button</button>
</template>

<script>
export default{
    data(){
        return{
            isButtonDisabled:false
        }
    }
}
</script>

disabled属性默认是true不能点击的,为false为可点击的。

动态绑定多个值

如果你有像这样的一个包含多个属性的js对象

<template>
    <div v-bind="objectOfAttrs"></div>
</template>

<script>
export default{
    data(){
        return{
            objectOfAttrs:{
                id:"appId",
                class:"appClass"
            }
        }
    }
}
</script>

1、可以用js对象存储标签中要添加的多个属性及属性值,然后直接动态绑定到标签中

2、js对象中属性名要与标签中的属性名保持一致

3、格式为v-bind='变量名'

条件渲染

在Vue中,提供了条件渲染,这类似于js中的条件语句

v-if

v-else

v-else-if

v-show

v-if

v-if指令用于条件性地渲染一块内容,这块内容只会在指令的表达式返回真值时才被渲染

v-开头的指令可以直接变量绑定

<template>
    <h3>条件渲染</h3>
    <div v-if="flag">你能看见我吗?</div>
</template>
<script>
export default{
    data(){
        return{
            flag:false
        }
    }
}
</script>

v-else

你也可以使用v-else为v-if添加一个‘else区块’

<template>
    <div v-if="flag">你能看见我吗</div>
    <div v-else>你还是看看我把</div>
</template>
<script>
export default{
    data(){
        return{
            flag:true
        }
    }
}
</script>

v-else-if

顾名思义,v-else-if提供的时相应于v-if的''else if区块',它可以连续多次重复使用

<template>
    <div v-if="type == 'A'">A</div>
    <div v-else-if="type == 'B'">B</div>
    <div v-else-if="type == 'C'">C</div>
    <div v-else>NOT A/B/C</div>
</template>
<script>
export default{
    data(){
        return{
            type:'E'
        }
    }
}
</script>

v-show

另一个可以用来按条件显示一个元素的指令是v-show,其用法基本一致

<template>
    <div v-show="flag">你能看见我吗?</div>
</template>
<script>
export default{
    data(){
        return{
            flag:false
        }
    }
}
</script>

v-if VS v-show

v-if 是真实的按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建

v-if 是惰性的,如果在初次渲染时条件值为 false,则不会做任何事,条件区块只有当条件首次变为 true 时才被渲染

相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。

总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好,如果在运行时绑定条件很少改变,则 v-if 会更合适

列表渲染

我们可以使用 v-for 指令基于一个数组来渲染一个列表,v-for 指令基于一个数组来渲染一个列表,v-for 指令的值需要使用 item in items 形式的特殊语法。其中 items 是源数据的数组,而 item 是迭代项的别名。

<template>
    <p v-for="item in names">{{ item }}</p>
</template>

<script>
export default {
    data(){
        return{
            names:["百战程序员","尚学堂","IT"]
        }
    }
}
</script>

复杂数据

大多数情况,我们渲染的数据来源于网络请求,也就是JSON格式

<template>
    <div v-for="item in result">
        <p>{{ item.id }}:{{ item.title }}</p>
    </div>
</template>

<script>
export default {
    data(){
        return{
            result:[{
                "id":1,
                "title":"aaa"
            },{
                "id":2,
                "title":"bbb"
            },{
                "id":3,
                "title":"ccc"
            }]
        }
    }
}
</script>

v-for 也支持使用可选的第二个参数表示当前项的位置索引 (item,index) in items

<template>
    <div v-for="(item,index) in names">{{ index+1 }}:{{ item }}</div>
</template>

<script>
export default {
    data(){
        return{
            names:["百战程序员","黑马程序员","尚学堂"]
        }
    }
}
</script>

v-for 与对象

你也可以使用 v-for 来遍历一个对象的所有属性 (value,key,index) in userInfo

<template>
    <div v-for="(value,key,index) in userInfo">{{ index }},{{ key }},{{ value }}</div>
</template>

<script>
export default {
    data(){
        return{
            userInfo:{
                name:"itheima",
                age:20,
                sex:"男",
                phone:111111
            }
        }
    }
}
</script>

通过key管理状态

在 v-for 中,key 是每一个元素唯一的索引,是不会改变的。

<template>
    <h3>key属性添加到v-for中</h3>
    <p v-for="(item,index) in names" :key="index">{{ item }}</p>
</template>

<script>

export default{
    data(){
        return{
            names:["百战程序员","尚学堂","IT"]
        }
    }
}

</script>

key的来源

请不要使用 index 作为 key 的值,我们要确保每一条数据的唯一索引不会发生变化

<template>
    <h3>key属性添加到v-for中</h3>
    <div v-for="(item) in userInfo" :key="item.id">
    <p>{{ item.id }},{{ item.name }},{{ item.age }}</p>
    </div>
</template>

<script>

export default{
    data(){
        return{
            userInfo:[{
                "id":1,
                "name":"aaa",
                "age":20
            },{
                "id":2,
                "name":"bbb",
                "age":22
            },{
                "id":3,
                "name":"ccc",
                "age":18
            }]
        }
    }
}

</script>

事件处理

我们可以使用 v-on 指令(简写@)来监听DOM事件,并在事件触发时执行对应 js ,用法:v-on:click="methodName" 或 @click="handler"

事件处理器的值可以是

内联事件处理器:事件被触发时执行的内联 js 语句(与onclick类似)

方法事件处理器:一个指向组件上定义的方法的属性名或是路径

内联事件处理器

内联事件处理器通常用于简单场景

<template>
    <button @click="count++">点击加1</button>
    <p>count:{{ count }}</p>
</template>

<script>
export default {
    data(){
        return{
            count:0
        }
    }
}
</script>

方法事件处理器

<template>
    <button @click="addCount">add</button>
    <p>{{ count }}</p>
</template>

<script>
export default{
    data(){
        return{
            count : 0
        }
    },
    // 日后所有的方法都放在这里
    methods:{
        addCount(){
            this.count++
        }
    }
}
</script>

事件传参

事件参数可以获取 event 对象和通过事件传递数据

获取 event 对象

<template>
    <button @click="addCount">add</button>
    <p>{{ count }}</p>
</template>

<script>
export default{
    data(){
        return{
            count : 0
        }
    },
    // 日后所有的方法都放在这里
    methods:{
        addCount(e){
            // Vue中的event对象,就是原生js的Event对象,e.target就是标签button对象
            console.log(e.target.innerHTML = "Add" + this.count)
            this.count++
        }
    }
}
</script>

传递参数

<template>
    <h3>事件传参</h3>
    <div v-for="(item,index) in names" :key="index">
        <button @click="getNameHandler(item)">{{ item }}</button>
    </div>
</template>

<script>
export default{
    data(){
        return{
            names:["iwen","ime","frank"]
        }
    },
    // 日后所有的方法都放在这里
    methods:{
        getNameHandler(name){
            console.log(name)
        }
    }
}
</script>

传递参数过程中获取 event 对象,在传入参数时加一个 $event 就可以了

<template>
    <h3>事件传参</h3>
    <div v-for="(item,index) in names" :key="index">
        <button @click="getNameHandler(item,$event)">{{ item }}</button>
    </div>
</template>

<script>
export default{
    data(){
        return{
            names:["iwen","ime","frank"]
        }
    },
    // 日后所有的方法都放在这里
    methods:{
        getNameHandler(name,e){
            console.log(name)
            console.log(e.target.innerHTML)
        }
    }
}
</script>

事件修饰符

在处理事件和调用 event.preventDefault() 或 event.stopPropagation() 是很常见的。尽管我们可以在直接在方法内调用,但如果方法能专注于数据逻辑而不用区处理DOM事件的细节会更好

为解决这一问题,Vue为 v-on 提供了事件修饰符,常用有以下几个:

.stop

.prevent

.once

.enter

...

具体参考

地址:https://cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers

阻止默认事件和冒泡事件

<template>
    <h3>事件修饰符</h3>
    <!-- 阻止跳转默认事件 -->
    <a @click.prevent="clickHandle" href="https://itbaizhan.com">百战程序员</a>

    <!-- 默认点击触发子元素事件,然后冒泡到父元素事件 -->
    <!-- 可以在子元素@click加.stop,能够阻止事件冒泡到父元素 -->
    <div @click="clickDiv">
        <p @click.stop="clickP">
            测试冒泡
        </p>
    </div>
</template>

<script>
export default {
    data(){
        return{

        }
    },
    methods:{
        clickHandle(e){
            // 阻止默认事件
            // e.preventDefault();
            console.log("点击了")
        },
        clickDiv(){
            console.log("Div")
        },
        clickP(e){
            // 阻止事件冒泡到父元素
            // e.stopPropagation();
            console.log("P")
        }
    }
}
</script>

数组变化侦测

原数组变更

Vue 能够侦听响应式数组的变更方法,并在题目被调用时触发相关的更新。这些变更方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
<template>
    <h3>数组变化侦听</h3>
    <button @click="addListHandle">添加数据</button>
    <ul>
        <li v-for="(item,index) in names" :key="index">{{ item }}</li>
    </ul>
</template>

<script>
export default {
    data() {
        return{
            names:["iven","ime","frank"]
        }
    },
    methods:{
        addListHandle(e){
            // 往原数组加入元素,原数组改变,所以渲染出来也要变
            this.names.push("Alice")
        }
    }
}
</script>

替换一个数组

变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一一些不可变方法,例如 filter(),concat() 和 slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的、

<template>
    <h3>数组变化侦听</h3>
    <button @click="addListHandle">添加数据</button>
    <ul>
        <li v-for="(item,index) in names" :key="index">{{ item }}</li>
    </ul>
</template>

<script>
export default {
    data() {
        return{
            names:["iven","ime","frank"]
        }
    },
    methods:{
        addListHandle(e){
            // 在原数组后面拼接一个数组并返回一个新的数组,赋值给原数组,从而更新了原数组
            this.names = this.names.concat(["Alice"])
        }
    }
}
</script>

计算属性

<template>
    <h3>{{ itbaizhan.name }}</h3>
    <!-- <p>{{ itbaizhan.content.length > 0 ? "YES":"NO" }}</p> -->
    <p>{{ itbaizhanContent }}</p>
</template>

<script>
export default {
    data(){
        return{ 
            itbaizhan:{
                name:"百战程序员",
                content:["前端","Java","python"]
            }
        }
    },
    // 计算属性
    computed:{
        itbaizhanContent(){
            return this.itbaizhan.content.length > 0 ? "YES":"NO"
        }
    }
}
</script>

计算属性缓存 vs 方法(未写)

Class绑定

绑定对象

<template>
    <!-- 一个标签可以绑定多个class属性值 -->
    <!-- 当isActive为true才绑定active属性值,当hasError为true才绑定text-danger属性值 -->
    <p :class="{'active':isActive,'text-danger':hasError}">Class文本绑定</p>
</template>

<script>
export default{
    data(){
        return{
            isActive:true,
            hasError:true
        }
    }
}
</script>

<style>
.active{
    color:red
}
.text-danger{
    font-size: 25px;
}
</style>

多个对象绑定

<template>
    <p :class="classObject">Class文本绑定2</p>
</template>

<script>
export default{
    data(){
        return{
            classObject:{
                "active":true,
                "text-danger":true
            }
        }
    }
}
</script>

<style>
.active{
    color:red
}
.text-danger{
    font-size: 25px;
}
</style>

绑定数组

<template>
    <p :class="[arrayActive,arrayError]">Class文本绑定3</p>
</template>

<script>
export default{
    data(){
        return{
            arrayActive:'active',
            arrayError:'text-danger'
        }
    }
}
</script>

<style>
.active{
    color:red
}
.text-danger{
    font-size: 25px;
}
</style>

如果你也想在数组中有条件地渲染某个class,你可以使用三元表达式

<template>
    <p :class="[isActive?arrayActive:'',hasError?arrayError:'']">Class文本绑定4</p>
</template>

<script>
export default{
    data(){
        return{
            isActive:true,
            hasError:true,
            arrayActive:'active',
            arrayError:'text-danger'
        }
    }
}
</script>

<style>
.active{
    color:red
}
.text-danger{
    font-size: 25px;
}
</style>

数组里面嵌入对象

<template>
    <p :class="[isActive?arrayActive:'',{'text-danger':hasError}]">Class文本绑定5</p>
</template>

<script>
export default{
    data(){
        return{
            isActive:true,
            hasError:true,
            arrayActive:'active',
            arrayError:'text-danger'
        }
    }
}
</script>

<style>
.active{
    color:red
}
.text-danger{
    font-size: 25px;
}
</style>

Style绑定

绑定对象

<template>
    <p :style="{color:activeColor,fontSize:activeFontSize}">Style样式1</p>
    <p :style="styleObject">Style样式2</p>
</template>

<script>
export default{
    data(){
        return{
            activeColor:'green',
            activeFontSize:'30px',
            styleObject:{
                color:'red',
                fontSize:'50px'
            }
        }
    }
}
</script>

绑定数组

<template>
    <p :style="[styleObject]">Style样式3</p>
</template>

<script>
export default{
    data(){
        return{
            styleObject:{
                color:'red',
                fontSize:'50px'
            }
        }
    }
}
</script>

侦听器

我们可以使用 watch 选项在每次响应式属性发生变化时触发一个函数

<template>
    <h3>侦听器</h3>
    <p>{{ msg }}</p>
    <button @click="updateHandle">修改数据</button>
</template>

<script>
export default {
    data() {
        return{
            msg:'hello'
        }
    },
    methods:{
        updateHandle(){
            this.msg = 'world'
        }
    },
    watch:{
        // 函数名要与变量名一致
        // newValue 为修改后的值
        // oldValue 为修改前的值
        msg(newValue,oldValue){
            // 数据发生变化时,自动执行的逻辑
            console.log(newValue,oldValue)
        }
    }
}
</script>

注意:函数名要与侦听变量对象名保持一致。

表单输入绑定

在前端处理表单时,我们常常需要将表单输入框的内容同步给 js 中响应的值,手动连接值绑定和更改监听器可能会很麻烦,就是说,表单输入的数据能够实时获取到,而不仅仅是提交时才获取,v-model 指令帮我们简化了这一步骤, v-model 能将数据实时赋值给变量

输入框

<template>
    <h3>表单输入绑定</h3>
    <form>
        <input type="text" v-model="msg">
        <p>{{ msg }}</p>
    </form>
</template>

<script>
export default{
    data(){
        return{
            msg:""
        }
    }
}
</script>

复选框

单一的复选框,绑定布尔类型值

<template>
    <h3>表单输入绑定</h3>
    <form>
        <input type="text" v-model="msg">
        <p>{{ msg }}</p>
        <input type="checkbox" id="checkbox" v-model="checked">
        <label for="checkbox">{{ checked }}</label>
    </form>
</template>

<script>
export default{
    data(){
        return{
            msg:"",
            checked:null
        }
    }
}
</script>

修饰符

v-model 也提供了修饰符:.lazy、.number、.trim

.lazy

默认情况下,v-model 会在每次 input 事件后更新数据。你可以添加 .lazy 修饰符来改为在每次 change 事件后更新数据

<template>
    <h3>表单输入绑定</h3>
    <form>
        <input type="text" v-model.lazy="msg">
        <p>{{ msg }}</p>
    </form>
</template>

<script>
export default{
    data(){
        return{
            msg:""
        }
    }
}
</script>

回车后才获取数据

模板引用

虽然 Vue 的声明性渲染模型为你抽象了大部分对DOM的直接操作,但在某些情况下,我们需仍然需要直接访问底层DOM元素。要实现这一点,我们可以使用特殊的 ref 属性

挂载结束后引用都会被披露在 this.$refs 之上

<template>
    <div ref="container" class="container">容器</div>
    <input type="text" ref="username"><br>
    <button @click="getElementHandle">获取元素</button>
</template>


<script>
export default {
    data() {
        return{
            content:'内容'
        }
    },
    methods:{
        getElementHandle(){
            console.log(this.$refs.container)
            console.log(this.$refs.username)
            this.$refs.container.innerHTML = "容器66"
            this.$refs.username.value = '666'
        }
    }
}
</script>

ref 与 event 区别

event 是操作本身事件触发的DOM(标签)元素

ref 是操作其他的DOM(标签)元素

如果没有特别需求,最好不要操作DOM元素

组件组成

组件最大优势是可复用性

当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue 文件中,这被叫做单文件组件(简称SFC)

组件引入

方式一:

  • 引入组件
  • 注入组件
  • 使用组件
<template>
    <!-- 第三步:使用组件 -->
    <!-- 两种标签显示方法都可以 -->
    <MyComponent /> 
    <my-component />
</template>


<script>

// 第一步:引入组件
import MyComponent from './components/MyComponent.vue';

export default{
    // 第二步:注入组件
    components:{
        MyComponent:MyComponent
    }
}

</script>

<style scoped>

</style>



方式二:(更简化,推荐)

  • <script>标签添加setup属性
  • 引入组件
  • 使用组件
<template>
    <!-- 使用组件:两种使用方法都可以 -->
    <MyComponent /> 
    <my-component />
</template>

<script setup>

import MyComponent from './components/MyComponent.vue';

</script>

<style scoped>

</style>


注意添加setup属性后不能再写export default{},但可以再写一个<script>,再另外一个<script>标签中写export default{}

<style>标签中scoped表示只在当前组件中生效,不加scoped表示在全局中生效

组件嵌套

组件允许我们将 UI 划分为独立的,可重用的部分,并且可以将每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构

创建组件及引用关系

Header.vue

<template>
    <h3>header</h3>
</template>

<style scoped>
h3{
    width: 100%;
    height: 100px;
    border: 5px solid #999;
    text-align: center;
    line-height: 100px;
    box-sizing: border-box;
}
</style>

Main.vue

<template>
    <div class="main">
        <h3>Main</h3>
        <Article />
        <Article />
    </div>
</template>

<script setup>
import Article from './Article.vue';
</script>

<style scoped>
.main{
    float: left;
    width: 70%;
    height: 600px;
    border: 5px solid #999;
    box-sizing: border-box;
}
</style>

Aside.vue

<template>
    <div class="aside">
        <h3>Aside</h3>
        <Item />
        <Item />
        <Item />
    </div>
</template>

<script setup>
import Item from './Item.vue';
</script>

<style scoped>
.aside{
    float: right;
    width: 30%;
    height: 600px;
    border: 5px solid #999;
    box-sizing: border-box;
}
</style>

Article.vue

<template>
    <h3>Article</h3>
</template>
<style scoped>
h3{
    width: 80%;
    margin: 0 auto;
    text-align: center;
    line-height: 100px;
    box-sizing: border-box;
    margin-top: 50px;
    background: #999;
}
</style>

Item.vue

<template>
    <h3>Item</h3>
</template>

<style scoped>
h3{
    width: 80%;
    margin: 0 auto;
    text-align: center;
    line-height: 100px;
    box-sizing: border-box;
    margin-top: 10px;
    background: #999;
}
</style>

App.vue

<template>
    <Header />
    <Main />
    <Aside />
</template>

<script setup>
import Aside from "./components/Aside.vue";
import Header from "./components/Header.vue";
import Main from "./components/Main.vue";
</script>

组件注册方式

一个Vue组件在使用前需要先被“注册”,这样Vue才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册

全局注册

全局注册后,所有地方都可以引入该组件

  • main.js文件中注册
  • 引入组件
  • 注册组件

import { createApp } from 'vue'
import App from './App.vue'
// 第一步:先引入组件
import Header from './components/Header.vue'

const app = createApp(App)

// 第二步:在这中间写组件的注册
// 左边是使用该组件的标签名,右边是import后面引入名
app.component("MyHeader",Header)

app.mount('#app')

局部注册

全局注册虽然很方便,但有以下几个问题:

  • 全局注册,但并没有被使用的组件无法在生产打包时被自动移除,如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的JS文件中
  • 全局注册在大型项目中使用的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性

局部注册需要使用components选项

<template>
    <!-- 第三步:使用组件 -->
    <!-- 两种标签显示方法都可以 -->
    <MyComponent /> 
    <my-component />
</template>

<script>
// 第一步:引入组件
import MyComponent from './components/MyComponent.vue';
export default{
    // 第二步:注入组件
    components:{
        MyComponent:MyComponent
    }
}
</script>

之前写的三步就是局部注册,建议用setup属性来简化注册过程。

组件传递数据_props

组件与组件之间不是完全独立的,而是有交集的,那就是组件与组件之间是可以传递数据的

传递数据的解决方案就是 props

使用组件时,可以添加属性和属性值来传递数据,属性名为变量名,属性值为变量值

<template>
    <h3>Parent</h3>
    <ChildVue data="Parent数据" />
</template>

<script setup>
import ChildVue from "./Child.vue";
</script>

被引用的组件可以通过props来获取传递数据,数组里面的字符串为传过来的变量名,需要保持一致

<template>
    <h3>Child</h3>
    <p>{{ data }}</p>
</template>
<script>
export default{
    data(){
        return{
            
        }
    },
    props:["data"]
}
</script>

传递的数据是没有数量限制的!

动态数据的传递 v-bind

Parent.vue

<template>
    <h3>Parent</h3>
    <ChildVue :data="msg"/>
    <p>Parent:{{ msg }}</p>
</template>

<script>
import ChildVue from "./Child.vue";
export default{
    components:{
        ChildVue
    },
    data(){
        return{
            msg:"hello world !"
        }
    }
}
</script>


Child.vue

<template>
    <h3>Child</h3>
    <p>Child:{{ data }}</p>
</template>
<script>
export default{
    props:["data"]
}
</script>

注意事项:

props 传递数据,只能从父级传递到子级,不能反其道而行!

组件传递多种数据类型

不同类型数据的传递

Parent.vue

<template>
    <h3>Parent</h3>
    <ChildVue :title="msg" :age="age" :names="names" :userInfo="userInfo"/>
</template>

<script>
import ChildVue from "./Child.vue";
export default{
    components:{
        ChildVue
    },
    data(){
        return{
            // 字符串
            msg:"hello world !",
            // 数字
            age:20,
            // 数组
            names:["iwen","ime","frank"],
            // 对象
            userInfo:{
                name:'wl',
                age:21
            }
        }
    }
}
</script>

Child.vue

<template>
    <h3>Child</h3>
    <p>{{ title }}</p>
    <p>{{ age }}</p>
    <p v-for="(item,index) in names" :key="index">{{ item }}</p>
    <p>{{ userInfo.name }} : {{ userInfo.age }}</p>
</template>

<script>
export default{
    props:["title","age","names","userInfo"]
}
</script>

组件传递Props校验

Vue 组件可以更细致地声明对传入的 props 的校验要求

props除了以数组表示,还可以以类表示

<template>
    <h3>ComponentB</h3>
    <p>{{ title }}</p>
    <p>{{ age }}</p>
    <P v-for="(item,index) in names" :key="index">{{ item }}</P>
</template>

<script>

export default{
    props:{
        title:{
            type:String,
            required:true
        },
        age:{
            type:Number,
            default:0
        },
        // 数组和字符串可以直接default,但是如果是数组和对象,必须通过工厂函数返回默认值
        names:{
            type:Array,
            default(){
                return ["空"]
            }
        }
    }
}

</script>
  • title、age、names为传递过来的参数,通过参数名匹配来获取
  • type属性为传递参数的类型,若传递的参数类型与指定类型不一致,控制台会提醒
  • default为默认值,若没有传入该参数,这该参数为默认值。但数组和对象的默认值必须是通过工厂函数返回默认值
  • required为该参数的必要性,若值为true说明该参数必须传递
  • 传递的参数只能读取,不能进行修改,修改时会报错的

组件事件

在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件

触发自定义事件的目的是组件之间传递数据

用子传父即被引用的组件向引用的组件传递数据

Child.vue

<template>
    <h3>Child</h3>
    <button @click="clickEventHandle">传递数据</button>
</template>

<script>
export default{
    data(){
        return{
            msg:"Child传递的数据"
        }
    },
    methods:{
        clickEventHandle(){
            // 自定义事件
            this.$emit("someEvent",this.msg)
        }
    }
}
</script>
  • 点击按钮触发元素事件
  • 元素事件中通过this.$emit()自定义事件,第一个参数为自定义事件的事件名,第二个参数为传入的数据

Parent.vue

<template>
    <h3>组件事件</h3>
    <Child @someEvent="getHandle" />
    <p>{{ msg }}</p>
</template>

<script>
import Child from './Child.vue'
export default{
    data(){
        return{
            msg:""
        }
    },
    components:{
        Child
    },
    methods:{
        getHandle(data){
            console.log("触发了",data)
            this.msg = data
        }
    }
}
</script>
  • 通过子元素自定义事件someEvent触发了getHandle方法
  • getHandle方法中data为传入的参数,通过this.msg = data来获取到传入的数据
  • {{ msg }} 显示传入的数据

温馨提示

组件之间传递数据的方案:

  • 父传子:props
  • 子传父:自定义事件(this.$emit

组件事件配合v-model使用

作用:被引用页面实时输入的内容,引用页面能够实时获取

SearchComponent.vue(被引用页面)

<template>
搜索:<input type="text" v-model="search">
<p>子:{{ search }}</p>    
</template>

<script>
export default{
    data(){
        return{
            search:''
        }
    },
    // 监听器
    watch:{
        search(newValue,oldValue){
            this.$emit('searchEvent',newValue)
        }
    }
}
</script>

Main.vue(引用页面)

<template>
    <h3>Main</h3>
    <search-component @searchEvent="getSearch" />
    <p>父:{{ search }}</p>
</template>

<script>
import SearchComponent from "./SearchComponent.vue";
export default{
    data(){
        return{
            search:''
        }
    },
    components:{
        SearchComponent
    },
    methods:{
        getSearch(data){
            this.search = data
        }
    }
}
</script>

<style scoped>
</style>

组件数据传递

我们之前讲解过了组件之间的数据传递,props 和 自定义事件 两种方式

  • props:父传子
  • 自定义事件:子传父

除了上述的方案,props 也可以实现子传父

  • 父组件向子组件传递函数
  • 子组件中调用函数时回传数据
  • 父组件中执行函数获取数据

注意:由子组件调用函数,但函数的执行是在父组件中,所以父组件能获取传递的数据

ComponentA.vue(父组件)

<template>
    <h3>ComponentA</h3>
    <ComponentB :dataA="dataA" :onEvent="dataFn"/>
    <p>{{ dataB }}</p>
</template>

<script>
import ComponentB from './ComponentB.vue';
export default{
    data(){
        return{
            dataA:'父传子的数据',
            // 获取子传父的数据
            dataB:''
        }
    },
    components:{
        ComponentB
    },
    methods:{
        dataFn(data){
            this.dataB = data
        }
    }
}

</script>

ComponentB.vue(子组件)

<template>
    <h3>ComponentB</h3>
    <p>{{ dataA }}</p>
    <p>{{ onEvent(dataB) }}</p>
</template>

<script>

export default{
    data(){
        return{
            dataB:'子传父的数据'
        }
    },
    props:{
        dataA:{
            type:String
        },
        onEvent:{
            type:Function
        }
    }
}

</script>

透传 Attributes

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为props或emits的attribute或者v-on事件监听器。最常见的例子就是classstyleid

当一个组件以单个元素为根作渲染时,透传的attribute会自动被添加到根元素上

引用页面

<template>
    <AttrComponent class="attr-container" />
</template>

<script>

import AttrComponent from "./components/AttrComponent.vue";
export default{
    components:{
        AttrComponent
    }
}

</script>

<style scoped>
.attr-container{
    color: red;
}
</style>

传递了class属性,属性值为attr-container

被引用页面

<template>
    <h3>透传属性</h3>
</template>

<script>
export default{
    inheritAttrs:false
}
</script>

class属性及属性值添加到根节点<h3>,若没有根节点,则都不添加传递过来的class属性及属性值

inheritAttrs:false可以拒绝透明传递

插槽Slot

我们可以在引用组件时往组件里面添加一些HTML片段,通过<slot>实现

引用组件

<template>
    <SlotsBase>
        <div>
            <h3>插槽标题</h3>
            <p>插槽内容</p>
        </div>
    </SlotsBase>
</template>

<script setup>

import SlotsBase from "./components/SlotsBase.vue";

</script>

往里组件里面添加了<div>、<h3>、<p>标签的HTML片段

被引用组件

<template>
    <h3>插槽基础知识</h3>
    <slot></slot>
</template>

通过<slot>标签来显示添加的HTML片段,还可以通过改变<slot>位置来改变显示的位置。

渲染作用域

添加的HTML动态绑定是在父元素中绑定的

<template>
    <SlotsBase>
        <div>
            <h3>{{ title }}</h3>
            <p>{{ content }}</p>
        </div>
    </SlotsBase>
</template>

<script>

import SlotsBase from "./components/SlotsBase.vue";
export default{
    data(){
        return{
            title:'插槽标题',
            content:'插槽内容'
        }
    },
    components:{
        SlotsBase
    }
}

</script>

默认内容

在外部没有提供任何内容情况下,可以为插槽指定默认内容

<template>
    <h3>插槽基础知识</h3>
    <slot>默认内容</slot>
</template>

具名插槽

我们可以通过<template>标签添加多个HTML片段,每个片段通过v-slot设置一个名字,被引用组件可以通过<slot>标签,name属性指定片段名进行渲染。

引用组件

<template>
    <SlotsBase>
        <template v-slot:header>
        <div>
            <h3>{{ title }}</h3>
        </div>
        </template>
        <template v-slot:main>
        <div>
            <p>{{ content }}</p>
        </div>
        </template>
    </SlotsBase>
</template>

<script>

import SlotsBase from "./components/SlotsBase.vue";
export default{
    data(){
        return{
            title:'插槽标题',
            content:'插槽内容'
        }
    },
    components:{
        SlotsBase
    }
}

</script>

添加了两个HTML片段,片段名分别为headermain

v-slot有对应的简写#,因此<template v-slot:header>可以简写为<template #header>

被引用组件

<template>
    <h3>插槽基础知识</h3>
    <slot name="header"></slot>
    <slot name="main"></slot>
</template>

通过name属性指定片段进行渲染

插槽数据传递

子元素显示插槽时可以向父元素插槽HTML片段传递数据

<template>
    <h3>Slots再续集</h3>
    <slot :childMsg="childMsg"></slot>
</template>

<script>
export default{
    data(){
        return{
            childMsg:'子元素数据'
        }
    }
}
</script>

父元素可以从v-slot="slotProps"获取slotProps对象,从slotProps对象中获取传递的数据

<template>
    <SlotsA v-slot="slotProps">
        <h3>{{ content }}-{{ slotProps.childMsg }}</h3>
    </SlotsA>
</template>

<script>
import SlotsA from './components/SlotsA.vue';
export default{
    data(){
        return{
            content:'测试内容'
        }
    },
    components:{
        SlotsA
    }
}

</script>

具名插槽传递数据

子元素添加name属性

<template>
    <h3>Slots再续集</h3>
    <slot name="header" :childMsg="childMsg"></slot>
    <slot name="main" :job="jobMsg"></slot>
</template>

<script>
export default{
    data(){
        return{
            childMsg:'子元素数据',
            jobMsg:'itbaizhan'
        }
    }
}
</script>

父元素要改变slotProps对象的获取方式

<template>
    <SlotsA>
        <template #header="slotProps">
            <h3>{{ content }}-{{ slotProps.childMsg }}</h3>
        </template>
        <template #main="slotProps">
            <p>{{ slotProps.job }}</p>
        </template>
    </SlotsA>
</template>

<script>
import SlotsA from './components/SlotsA.vue';
export default{
    data(){
        return{
            content:'测试内容'
        }
    },
    components:{
        SlotsA
    }
}

</script>

组件生命周期

生命周期函数

  • 创建期:beforeCreate  created
  • 挂载期:beforeMounte  mounted
  • 更新期:beforeUpdate  updated
  • 销毁期:beforeUnmount  unmounted
<template>
    <h3>组件生命周期</h3>
    <p>{{ msg }}</p>
    <!-- 点击后,发生组件更新前,更新后 -->
    <button @click="updateHandle">更新数据</button>
</template>

<script>

export default{
    data(){
        return{
            msg:'更新之前'
        }
    },
    methods:{
        updateHandle(){
            this.msg = '更新之后'
        }
    },
    beforeCreate(){
        console.log('组件创建之前')
    },
    created(){
        console.log('组件创建之后')
    },
    beforeMount(){
        console.log('组件渲染之前')
    },
    mounted(){
        console.log('组件渲染之后')
    },
    beforeUpdate(){
        console.log('组件更新之前')
    },
    updated(){
        console.log('组件更新之后')
    },
    beforeUnmount(){
        console.log('组件销毁之前')
    },
    unmounted(){
        console.log('组件销毁之后')
    }
}

</script>

生命周期应用

通过ref获取元素DOM结构

<template>
    <h3>组件生命周期函数应用</h3>
    <p ref="name">百战程序员</p>
</template>

<script>

export default{
    beforeMount(){
        console.log(this.$refs.name) //undefined
    },
    mounted(){
        console.log(this.$refs.name) // 读到了
    }
}

</script>

在生命周期mounted渲染后才能获取

模拟网络请求渲染数据

<template>
    <h3>组件生命周期函数应用</h3>
    <p ref="name">百战程序员</p>
    <ul>
        <li v-for="(item,index) in banner" :key="index">
            <h3>{{ item.title }}</h3>
            <p>{{ item.content }}</p>
        </li>
    </ul>
</template>

<script>

export default{
    data(){
        return{
            // 存储网络请求的数据
            banner:[]
        }
    },
    created(){
        // 获取网络请求的数据
        this.banner = [{
            'title':'itheima',
            'content':'aaaa'
        },{
            'title':'itbaizhan',
            'content':'bbb'
        },{
            'title':'itniuba',
            'content':'ccc'
        }]
    }
}

</script>

也可以使用mounted生命周期函数

动态组件

有些场景会需要在两个组件来回切换,比如Tab界面

A、B两个组件

<template>
    <h3>ComponentA</h3>
</template>
<template>
    <h3>ComponentB</h3>
</template>
<template>
    <component :is="tabComponent"></component>
    <button @click="changeHandle">切换组件</button>
</template>

<script>

import ComponentB from "./components/ComponentB.vue";
import ComponentA from "./components/ComponentA.vue";
export default{
    data(){
        return{
            tabComponent:'ComponentA'
        }
    },
    components:{
        ComponentA,
        ComponentB
    },
    methods:{
        changeHandle(){
            this.tabComponent = this.tabComponent == 'ComponentA' ? 'ComponentB' : 'ComponentA'
        }
    }
}

</script>

通过<template>标签的is属性绑定显示的组件名,通过点击修改组件名来切换组件,默认ComponentA组件,点击后改变组件名来切换组件

组件保持存活

当使用<component is='...'>来在多个组件间切换时,被切换掉的组件会被卸载。我们可以通过<keep-alive>组件强制被切换掉的组件仍然保持“存活”状态

<template>
    <h3>ComponentA</h3>
    <p>{{ msg }}</p>
    <button @click="updateHandle">更新数据</button>
</template>

<script>

export default{
    data(){
        return{
            msg:'老数据'
        }
    },
    beforeUnmount(){
        console.log('组件A被卸载之前')
    },
    unmounted(){
        console.log('组件A被卸载之后')
    },
    methods:{
        updateHandle(){
            this.msg = '新数据'
        }
    }
}

</script>
<template>
    <keep-alive>
        <component :is="tabComponent"></component>
    </keep-alive>
    <button @click="changeHandle">切换组件</button>
</template>

<script>

import ComponentB from "./components/ComponentB.vue";
import ComponentA from "./components/ComponentA.vue";
export default{
    data(){
        return{
            tabComponent:'ComponentA'
        }
    },
    components:{
        ComponentA,
        ComponentB
    },
    methods:{
        changeHandle(){
            this.tabComponent = this.tabComponent == 'ComponentA' ? 'ComponentB' : 'ComponentA'
        }
    }
}

</script>

不加<keep-alive>,当点击更新数据后,页面显示新数据,再次切换到该页面时,仍然显示老数据,这是因为切换时,之前的组件被删除了。加了<keep-alive>后,则显示新数据,因为之前组件未删除

异步组件

在大型项目中,我们可能需要拆分应用未更小的块,并仅在需要时再从服务器加载相关组件。Vue提供了defineAsynoComponent方法来实现此功能

<template>
    <keep-alive>
        <component :is="tabComponent"></component>
    </keep-alive>
    <button @click="changeHandle">切换组件</button>
</template>

<script>
// 引入defineAsyncComponent
import { defineAsyncComponent } from "vue";
import ComponentA from "./components/ComponentA.vue";
// 异步加载组件B,这点格式要注意
const ComponentB = defineAsyncComponent(()=>
    import("./components/ComponentB.vue")
)
export default{
    data(){
        return{
            tabComponent:'ComponentA'
        }
    },
    components:{
        ComponentA,
        ComponentB
    },
    methods:{
        changeHandle(){
            this.tabComponent = this.tabComponent == 'ComponentA' ? 'ComponentB' : 'ComponentA'
        }
    }
}

</script>

在引入的地方需要进行相应改动

Vue引入第三方

Swiper 开源、免费、强大的触摸滑动插件

官网文档:https://swiperjs.com/vue

安装指定版本:npm install --save swiper@8.1.6

简单轮播图(手动滑动)

<template>
    <swiper>
        <swiper-slide>
            <img src="./assets/1.png">
        </swiper-slide>
        <swiper-slide>
            <img src="./assets/1.png">
        </swiper-slide>
        <swiper-slide>
            <img src="./assets/1.png">
        </swiper-slide>
    </swiper>
</template>
<script>
    // Import Swiper Vue.js components
    import { Swiper, SwiperSlide } from 'swiper/vue';
  
    // Import Swiper styles
    import 'swiper/css';
  
    export default {
      components: {
        Swiper,
        SwiperSlide,
      }
    }
</script>
<style>
img{
    width: 100%;
  }
</style>

添加指示器

  • 引入指示器
  • 引入指示器样式
  • data()的return那固定写法
  • :modules="modules" :pagination="{clickable:true}"
<template>
    <swiper :modules="modules" :pagination="{clickable:true}">
        <swiper-slide>
            <img src="./assets/1.png">
        </swiper-slide>
        <swiper-slide>
            <img src="./assets/1.png">
        </swiper-slide>
        <swiper-slide>
            <img src="./assets/1.png">
        </swiper-slide>
    </swiper>
</template>
<script>
    // Import Swiper Vue.js components
    import { Swiper, SwiperSlide } from 'swiper/vue';
    // 引入指示器
    import { Pagination } from 'swiper';
  
    // Import Swiper styles
    import 'swiper/css';
    // 引入指示器CSS
    import 'swiper/css/pagination'
  
    export default {
      components: {
        Swiper,
        SwiperSlide,
      },
      data(){
        return{ // 指示器的固定写法
            modules:[ Pagination ]
        }
      }
    }
</script>
<style>
img{
    width: 100%;
  }
</style>

更多参考官方文档

Axios网络请求

Axios是一个基于promise的网络请求库

安装

Axios的应用需要单独安装的npm install --save axios

引入

组件中引入:import axios from 'axios'

全局引用:

import axios from 'axios'

const app = createApp(App)
app.config.globalProperties.$axios = axios
app.mount('#app')

// 在组件中调用
this.$axios

在传递参数时,需要使用querystring插件进行格式转换

  • 安装依赖:        
  • 引入querystirng组件
  • 转换参数格式:querystring.stringify({})

get请求方式

<script>

import axios from 'axios';
import qs from 'querystring';

export default{
    mounted(){
        axios({
            method:'get',
            // 指定请求路径并传递参数
            url:'/api/index?' + qs.stringify({
                // 左边是传递的参数名,右边是参数值
                userId:Cookies.get("userId")
            })
        }).then(res=>{
            // 成功后初始化数据
            this.user = res.data.data
        })
    }
}

</script>

post请求方式

<script>

import axios from 'axios';
import querystring from 'querystring'

export default{
    mounted(){
        axios({
            method:'post',
            url:'http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php',
            data:querystring.stringify({
                // 左边是传递的参数名,右边是参数值
                user_id:'iwen@qq.com',
                password:'iwen123',
                verification:'crfvw'
            })
        }).then(res=>{
            console.log(res.data);
        })
    }
}

</script>

快捷方案

get请求方式

import axios from 'axios';
import querystring from 'querystring'

export default{
    mounted(){
        axios.get('http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php')
        .then(res =>{
            console.log(res.data)
        })
    }
}

</script>

post请求方式

<script>

import axios from 'axios';
import querystring from 'querystring';

mounted(){
    axios.post('http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php',querystring.stringify({
        user_id:'iwen@qq.com',
        password:'iwen123',
        verification:'crfvw'
    })).then(res=>{
        console.log(res.data)
    })
}

</script>

Axios网络请求的封装

在src目录下创建文件夹utils,并创建文件request.js,用来存储网络请求对象axios

request.js

import axios from "axios";
import { error } from "console";
import { config } from "process";
import querystirng from 'querystring'

const errorHandle = (status,info) =>{
    switch(status){
        case 400:
            console.log('语法错误');
            break;
        case 401:
            console.log('服务器认证失败')
            break;
        default:
            console.log(info)
            break;
    }
}

const instance = axios.create({
    // 网络请求的公共配置
    timeout:5000
})

// 拦截器

// 发送数据前
instance.interceptors.request.use(
    // 成功函()
    config =>{
        if(config.methods === 'post'){
            config.data = querystirng.stringify(config.data)
        }
        // config:包含着网络请求的所有信息
        return config
    },
    error =>{
        return Promise.reject(error)
    }
)

// 获取数据之前
instance.interceptors.response.use(
    // 成功函()
    response =>{
        return response.status == 200 ? Promise.resolve(response) : Promise.reject(response)
    },
    error =>{
        const { response } = error
        // 错误的处理才是我们需要最关注的
        errorHandle(response.status,response.info)
    }
)

export default instance;

创建api文件夹,并创建index.js文件和path.js文件

path.js文件中存放公共的请求路径

const base = {
    baseUrl: 'aaaa',
    chengpin: 'bbbb'
}

export default base

index.js文件中存放完整请求路径的函数

import axios from "axios";
import path from "./path";

const api = {
    // 诚品详情地址
    getChengpin(){
        return axios.get(path.baseUrl + path.chengpin)
    }
}

export default api

页面中引入并调用函数

import axios from "axios";
import path from "./path";

const api = {
    // 诚品详情地址
    getChengpin(){
        return axios.get(path.baseUrl + path.chengpin)
    }
}

export default api

Vue引入路由配置

在Vue中,我们可以通过vue-router路由管理页面之间的关系

在Vue中引入路由

第一步:安装路由

npm install --save vue-router

第二步:配置独立的路由文件

先创建router文件夹,再创建index.js文件

import {createRouter,createWebHashHistory} from "vue-router"
import HomeView from "@/views/HomeView.vue"
import AboutView from "@/views/AboutView.vue"

// 配置信息中需要页面的相关配置

const routes = [
    {
        // 指定HomeView页面的路径为/,即首页
        path:"/",
        component:HomeView
    },
    {
        // 指定HomeView页面的路径为/about
        path:"/about",
        // 异步引入,更快
        component:()=> import('../views/AboutView.vue')
    }
]

const router = createRouter({
    history:createWebHashHistory(),
    routes
})

export default router;

createWebHashHistory()属性值l对应地址:localhost:5173/#/

createWebHistory()属性值对应地址:localhost:5173/

第三步:main.js中引入router

// 引入router
import router from './router'

// 使用router
app.use(router)

其中 const app = createApp(App)

第四步:App.vue进行演示

<template>
    <router-view></router-view>
</template>

<script>

</script>

 <router-view>相当于引入首页 / 组件,作为程序主入口

<router-link>路由跳转

    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>

该标签是页面公共的,常用作页面顶部导航栏

路由传递参数

第一步:在路由配置中指定参数的key

    {
        path:"/news/:name",
        component:()=> import('../views/NewView.vue')
    }

第二步:在跳转过程中携带参数

    <router-link class="box" to="/news/百度">News</router-link>

第三步:在详情页面读取路由携带的参数

<p> {{ $route.params.name }}</p>

嵌套路由配置

  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值