Vue

Vue

官方文档:Vue.js - 渐进式 JavaScript 框架 | Vue.js

Vue API 风格

选项式 API 和 组合式 API

Vue 开发前的准备

nodejs的安装

创建vue项目

npm init vue@latest

cnpm install

npm run dev

开发环境:Vscode+Vue-Offical(插件)

Vue 项目目录结构

.vscode					#VSCode工具的配置文件夹
node_modules			#Vue项目的运行依赖文件夹
public					#资源文件夹
src						#源代码文件夹
.gitignore				#Git文件
index.html				#入口HTML文件
package.json			#信息描述文件
README.md				#注释文件
vite.config.js			#Vue配置文件

模板语法

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

<script>
export default {
  data() {
    return {
      msg: "神奇的语法",
      hello: "Hello,World!"
    }
  }
}
</script>
<template>
  <h3>模板语法</h3>
  <p>{{ msg }}</p>
  <p>{{ hello }}</p>
  <p>{{ number + 1 }}</p>
  <p>{{ ok ? 'YES' : 'NO' }}</p>
  <p>{{ message.split("").reverse().join("") }}</p>
</template>

<script>
export default {
  data() {
    return {
      msg: "神奇的语法",
      hello: "Hello,World!",
      number: 10,
      ok:true,
      message: "大家好"
    }
  }
}
</script>

原始HTML:插入 HTML

<template>
  <h3>模板语法</h3>
  <p>{{ msg }}</p>
  <p>{{ hello }}</p>
  <p>{{ number + 1 }}</p>
  <p>{{ ok ? 'YES' : 'NO' }}</p>
  <p>{{ message.split("").reverse().join("") }}</p>
  <p v-html = "rawHtml"></p>
</template>

<script>
export default {
  data() {
    return {
      msg: "神奇的语法",
      hello: "Hello,World!",
      number: 10,
      ok:true,
      message: "大家好",
      rawHtml:"<a href='http://baidu.com'>百度</a>"
    }
  }
}
</script>

属性绑定

双大括号不能再 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令,可以使用: 替代。

如果绑定的值为 NULL 或 Undefined,那么绑定对象会被移除。

<template>
  <div v-bind:id = "dynamicId" v-bind:class = "dynamicClass">测试</div>
</template>

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

<style>

.appclass {
  color: red;
  font-size: 30px;
}
</style>

如果想设置按钮是否可以点击

<template>
  <button :disabled="isButtonDisable">按钮</button>
</template>

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

一个对象绑定多个值:

<template>
  <div v-bind:id = "dynamicId" v-bind:class = "dynamicClass">测试1</div>
  <button :disabled = "isButtonDisable">按钮</button>
  <div v-bind = "objectOFAtters">测试2</div>
</template>

<script>
export default {
  data() {
    return {
      dynamicClass: "appclass",
      dynamicId: "appid",
      isButtonDisable: false,
      objectOFAtters: {
        class: "appclass",
        id: "appid",
      }
    }
  }
}
</script>

<style>

.appclass {
  color: red;
  font-size: 30px;
}
</style>

条件渲染

v-if

<template>
    <h3>条件渲染</h3>
    <div v-if = "flag">你能看见我么</div>
    <div v-else>那你还是看看吧</div>

    <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 {
            flag: true,
            type: "B",
        }
    }
}
</script>

v-show

<template>
    <h3>条件渲染</h3>
    <div v-if = "flag">你能看见我么</div>
    <div v-else>那你还是看看吧</div>

    <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>

    <div v-show = "flag">你能看见我吗</div>
</template>

<script>
export default {
    data() {
        return {
            flag: true,
            type: "B",
        }
    }
}
</script>

v-show 有较高的渲染开销,v-if 有较高的切换开销。

列表渲染

<template> 
    <h3>列表渲染</h3>
    <p v-for = "item in names">{{ item }}</p>
</template>

<script>
export default {
    data() {
        return {
            names:["XiaoMo", "Hello", "IT"]
        }
    }
}
</script>

也可以改为 "item of names"

渲染对象:

<template> 
    <h3>列表渲染</h3>
    <p v-for = "item in names">{{ item }}</p>
    <div>
        <p v-for="(value, key, index) in userInfo">{{ value }} - {{ key }} - {{ index }}</p>
    </div>
</template>

<script>
export default {
    data() {
        return {
            names:["XiaoMo", "Hello", "IT"],
            userInfo: {
                name: "iwen",
                age: 20,
                sex: "男",
            }
        }
    }
}
</script>

通过 Key 管理状态

为了给 Vue 一个提示,一边它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为伟哥元素对应的块提供一个唯一的 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:["XiaoMo", "Hello", "World"],
        }
    }
}

</script>

事件处理

v-on 可以简写为 @

内联事件处理器:用于简单的事件

<template>
    <h3>内联事件处理器</h3>
    <button v-on:click="count++">Add</button>
    <p>{{ count }}</p>
</template>

<script>

export default {
    data() {
        return {
            count: 0,
        }
    }
}

</script>

方法事件处理器

<template>
    <h3>方法事件处理器</h3>
    <button v-on:click="addCount">Add</button>
    <p>{{ count }}</p>
</template>

<script>

export default {
    data() {
        return {
            count: 0,
        }
    },
    methods: {
        addCount() {
            // 读取到data里面的数据的方案
            this.count ++
        }
    }
}

</script>

事件传参

<template>
    <h3>事件传参</h3>
    <button v-on:click="addCount">Add</button>
    <p>{{ count }}</p>
</template>

<script>

export default {
    data() {
        return {
            count: 0,
        }
    },
    methods: {
        // event对象
        addCount(e) {
            e.target.innerHTML = "Add" + this.count
            this.count ++
        }
    }
}

</script>
<template>
    <h3>事件传参</h3>
    <button v-on:click="addCount('hello')">Add</button>
    <p>{{ count }}</p>
</template>

<script>

export default {
    data() {
        return {
            count: 0,
        }
    },
    methods: {
        // event对象
        addCount(msg) {
            // e.target.innerHTML = "Add" + this.count
            console.log(msg)
            this.count ++
        }
    }
}

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

<script>

export default {
    data() {
        return {
            names:["XiaoMo", "ime", "frank"]
        }
    },
    methods: {
        getNameHandler(name) {
            console.log(name);
        }
    }
}

</script>

事件修饰符

<template>
    <h3>事件修饰符</h3>
    <a @click="clickHandle" href="http://baidu.com">百度</a>
    <div @click="clickDiv">
        <p @click="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>

数组变化侦听

变更方法

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

<script>

export default {
    data() {
        return {
            names:["XiaoMo", "ime", "frank"]
        }
    },
    methods: {
        addListHandle() {
            // 引起UI的变化
            this.names.push("HaHa")
            
            // 不会引起UI自动更新
            this.names.concat(["HaHa"])
        }
    }
}

</script>

替换方法

想发生变化的话:

this.name = this.name.concat(["HaHa"])
<template>
    <h3>数组变化侦听</h3>
    <button @click="addListHandle">添加数据</button>
    <ul>
        <li v-for="(item,index) of names" :key="index">{{ item }}</li>
    </ul>
    <button @click="filterHandele">合并数据</button>
    <h3>数组1</h3>
    <p v-for="(item, index) of nums1" :key="index">{{ item }}</p>

    <h3>数组2</h3>
    <p v-for="(item, index) of nums2" :key="index">{{ item }}</p>

</template>

<script>

export default {
    data() {
        return {
            names:["XiaoMo", "ime", "frank"],
            nums1:[1, 2, 3, 4, 5],
            nums2:[6, 7, 8, 9, 10],
        }
    },
    methods: {
        addListHandle() {
            // 引起UI的变化
            this.names.push("HaHa")
            
            // 不会引起UI自动更新
            this.names.concat(["HaHa"])
        },
        filterHandele() {
            this.nums1 = this.nums1.concat(this.nums2)
        },
    }
}

</script>

计算属性

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

    <h3>数组2</h3>
    <p v-for="(item, index) of nums2" :key="index">{{ item }}</p>

</template>

<script>

export default {
    data() {
        return {
            names:["XiaoMo", "ime", "frank"],
            nums1:[1, 2, 3, 4, 5],
            nums2:[6, 7, 8, 9, 10],
        }
    },
    methods: {
        addListHandle() {
            // 引起UI的变化
            this.names.push("HaHa")
            
            // 不会引起UI自动更新
            this.names.concat(["HaHa"])
        },
        filterHandele() {
            this.nums1 = this.nums1.concat(this.nums2)
        },
    }
}

</script>

计算属性和函数的区别

计算属性的值会基于其响应式依赖被缓存,一个计算属性的值仅在其响应式依赖更新时再重新计算。

函数则每次都会重新调用并计算。

Class 绑定

<template>
    <p :class="{'active': isActive, 'text-danger': hasError}"> Class样式绑定</p>
</template>

<script>

export default {
    data() {
        return {
            isActive: true,
            hasError: true,
        }
    },
}

</script>

<style>

.active {
    font-size: 30px;
}

.text-danger {
    color: red;
}

</style>

多个对象的绑定形式

<template>
    <p :class="{'active': isActive, 'text-danger': hasError}"> Class样式绑定1</p>
    <p :class="classObject"> Class样式绑定2</p>
</template>

<script>

export default {
    data() {
        return {
            isActive: true,
            hasError: true,
            classObject: {
                'active': false,
                'text-danger': true,
            }
        }
    },
}

</script>

<style>

.active {
    font-size: 30px;
}

.text-danger {
    color: red;
}

</style>

绑定数组

<template>
    <p :class="{'active': isActive, 'text-danger': hasError}"> Class样式绑定1</p>
    <p :class="classObject"> Class样式绑定2</p>
    <p :class="[arrActive, arrHasError]"> Class样式绑定3</p>
</template>

<script>

export default {
    data() {
        return {
            isActive: true,
            hasError: true,
            classObject: {
                'active': false,
                'text-danger': true,
            },
            arrActive: "active",
            arrHasError: "text-danger",
        }
    },
}

</script>

<style>

.active {
    font-size: 30px;
}

.text-danger {
    color: red;
}

</style>

数组和对象

必须是数组嵌套对象,不能是对象嵌套数组。

Style 绑定

<template>
    <p :style="{color: activeColor, fontSize: fontSize + 'px'}">Style 绑定1</p>
    <p :style=stytleObject>Style 绑定2</p>
</template>


<script>

export default {
    data() {
        return {
            activeColor: 'red',
            fontSize: 30,
            stytleObject: {
                color: "red",
                fontSize: "30px",
            },
        }
    },
}

</script>

侦听器

只能监听响应式的数据。

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

<script>

export default {
    data() {
        return {
            message: "Hello",
        }
    },
    methods: {
        updatHandle() {
            this.message = "World!"
        }
    },
    // 侦听器
    watch: {
        // newValue改变之后的数据
        // oldValue改变之后的数据
        // 函数名必须与侦听的数据对象保持一致
        message(newValue, oldValue) {
            // 数据发生变化,自动执行的函数
            console.log(oldValue);
            console.log(newValue);
        }
    }
}

</script>

表单输入绑定

v-model

实时获取:

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

<script>

export default {
    data() {
        return {
            message: "",
            checked: false,
        }
    }
}

</script>

非实时获取(失去焦点后获取)

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

<script>

export default {
    data() {
        return {
            message: "",
            checked: false,
        }
    }
}

</script>

模板引用

获取dom

内容改变: {{ 模板语法 }}
属性改变: v-bind: 指令
事件:v-on:click

如果没有特别的需求,不要操作DOM
<template>
    <div ref="container" class="container">{{ content }}</div>
    <input type="text" ref="username">
    <button @click="getElementHandle">获取元素</button>
</template>

<script>

export default {
    data() {
        return {
            content: "内容",
        }
    },
    methods: {
        getElementHandle() {
            this.$refs.container.innerHTML="HaHaHa";
            console.log(this.$refs.username.value);
        }
    }
}

</script>

组件组成

我们一般会将Vue组件定义再一个单独的 .vue 文件中。

<template>
    <div class="container">{{ message }}</div>
  </template>
  
  <script>
  
  export default {
    data() {
      return {
        message: "组建的基本组成"
      }
    }
  }
  
  </script>
  
  <style>
  .container {
    font-size: 30px;
    color: red;
  }
  </style>

引用方法

<template>
  <!-- 2.显示组件 -->
  <MyComponent />
</template>

<script setup>

// 1.引入组件
import MyComponent from './components/MyComponent.vue';

</script>

<style></style>

组件嵌套关系

image-20250413134903366

App.vue

<template>
  <Headers/>
  <Mains/>
  <Asides/>
  
</template>

<script setup>

import Headers from "./pages/Headers.vue"
import Mains from "./pages/Mains.vue";
import Asides from "./pages/Asides.vue";

</script>

<style>

</style>

Articles.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>

Asides.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>

Mains.vue

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

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

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

Headers.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>

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>

组件的注册方式

全局注册

import { createApp } from 'vue'
import App from './App.vue'
import Headers from "./pages/Headers.vue"

const app = createApp(App)

// 在这中间写组件的注册
app.component("Headers", Headers)

app.mount('#app')

局部注册

...见上节

组件传递数据

props

parent.vue

<template>
    <h3>Parent</h3>
    <Child title="Parent数据" demo = "测试"/>
</template>

<script>

import Child from "./Child.vue"

export default {
    data() {
        return {

        }
    },
    components: {
        Child
    },
}

</script>

Child.vue

<template>
    <h3>Child</h3>
    <p>{{ title }}</p>
    <p>{{ demo }}</p>
</template>

<script>

export default {
    data() {
        return {
            
        }
    },
    props:["title", "demo"]
}

</script>

动态数据传递

parent.vue

<template>
    <h3>Parent</h3>
    <Child :title="message"/>
</template>

<script>

import Child from "./Child.vue"

export default {
    data() {
        return {
            message: "动态数据"
        }
    },
    components: {
        Child
    },
}

</script>

注意事项:

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

组件传递多种数据类型

<template>
    <h3>Parent</h3>
    <Child :title="message" :age="age"/>
</template>

<script>

import Child from "./Child.vue"

export default {
    data() {
        return {
            message: "动态数据",
            age: 20,
        }
    },
    components: {
        Child
    },
}

</script>

组件传递 Props 效验

Props 是只读的,不可修改。

<template>
    <h3>Component B</h3>
    <p>{{ title }}</p>
    <p>{{ age }}</p>
    <p v-for="(item, index) of names" :key="index">{{ item }}</p>
</template>

<script>

export default {
    data() {
        return {

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

</script>

组件事件

$emit 方法触发自定义事件,实现子传父数据。

Child.vue

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

<script>

export default {
    data() {
        return {
            msg: "Child数据!",
        }
    },
    methods: {
        clickEventHandle() {
            // 自定义事件
            this.$emit("someEvent", "Child数据")
        }
    }
}

</script>

Event.vue

<template>
    <h3>组件事件</h3>
    <Child @someEvent="getHandle"/>
    <p>父元素:{{ message }}</p>
</template>

<script>

import Child from "./Child.vue"

export default {

    data() {
        return {
            message:""
        }
    },
    components: {
        Child,
    },
    methods: {
        getHandle(data) {
            console.log("触发了", data);
            this.message = data;
        }
    }
}


</script>

组件事件配合 v-model 使用

Mains.vue

<template>
    <h3>Main</h3>
    <p>搜索的内容为:{{ search }}</p>
    <Search @searchEvent="getSearch"/>
</template>

<script>

import Search from './Search.vue';

export default{
    data() {
        return {
            search:""
        }
    },
    components: {
        Search,
    },
    methods: {
        getSearch(data) {
            this.search = data;
        }
    }
}

</script>

Search.vue

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

<script>

export default {
    data() {
        return {
            search: ""
        }
    },
    watch: {
        search(newValue, oldeValue) {
            this.$emit("searchEvent", newValue);
        }
    }
}

</script>

组合数据传递

通过 props 实现子传父

AAA.vue

<template>
    <h3>AAA</h3>
    <p>{{ message }}</p>
    <BBB title="标题" :onEvent="dataFn"/>
</template>

<script>

import BBB from './BBB.vue';

export default {
    data() {
        return {
            message: ""
        }
    },
    components: {
        BBB,
    },
    methods: {
        dataFn(data) {
            this.message = data
        }
    }
}

</script>

BBB.vue

<template>
    <h3>BBB</h3>
    <p>{{ title }}</p>
    <p>{{ onEvent("传递数据") }}</p>
</template>

<script>
export default {
    data() {
        return {
            
        }
    },
    props: {
        title: String,
        onEvent: Function,
    }
}

</script>

插槽Slots

传递HTML结构

slot 元素是插槽的一个出口,标志了渲染的位置。

App.vue

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

<script>

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

export default {
  components: {
    SlotsBase,
  }
}

</script>

<style>

</style>

SlotsBase.vue

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

<script>



</script>

渲染作用域

插槽内容可以访问到父组件的数据作用域。

App.vue

<template>
  <!-- <SlotsBase>
    <div>
      <h3>插槽标题</h3>
      <p>插槽内容</p>
    </div>
  </SlotsBase> -->
  <SlotsTwo>
    <h3>{{ message }}</h3>
  </SlotsTwo>
</template>

<script>

import SlotsBase from './components/SlotsBase.vue';
import SlotsTwo from './components/SlotsTwo.vue';

export default {
  data() {
    return {
      "message": "插槽内容续集",
    }
  },
  components: {
    SlotsBase,
    SlotsTwo,
  }
}

</script>

<style>

</style>

SlotsTwo.vue

<template>
    <h3>Slots续集</h3>
    <slot></slot>
</template>

<script>

export default {
    data() {
        return {
            
        }
    }
}

</script>

默认内容

如果插槽内无传递,那么可以在插槽中自己写默认内容。

具名插槽

可以通过名字来实现多个插槽不同渲染。

App.vue

<template>
  <!-- <SlotsBase>
    <div>
      <h3>插槽标题</h3>
      <p>插槽内容</p>
    </div>
  </SlotsBase> -->
  <SlotsTwo>
    <template v-slot:header>
      <h3>{{ message }}</h3>
    </template>
    <template v-slot:main>
      <h3>{{ message }}</h3>
    </template>
  </SlotsTwo>
</template>

<script>

import SlotsBase from './components/SlotsBase.vue';
import SlotsTwo from './components/SlotsTwo.vue';

export default {
  data() {
    return {
      "message": "插槽内容续集",
    }
  },
  components: {
    SlotsBase,
    SlotsTwo,
  }
}

</script>

<style>

</style>

SlotsTwo.vue

<template>
    <h3>Slots续集</h3>
    <slot name="header"></slot>
    <hr>
    <slot name="main"></slot>
</template>

<script>

export default {
    data() {
        return {

        }
    }
}

</script>

v-slot: 可以用 # 简写。

slots同时使用父元素和子元素

App.vue

<template>
  <!-- <SlotsBase>
    <div>
      <h3>插槽标题</h3>
      <p>插槽内容</p>
    </div>
  </SlotsBase> -->
  <!-- <SlotsTwo>
    <template #header>
      <h3>{{ message }}</h3>
    </template>
    <template #main>
      <h3>{{ message }}</h3>
    </template>
  </SlotsTwo> -->

  <SlotsAttr v-slot="slotProps">
    <h3>{{ Test }} - {{ slotProps.msg }}</h3>
  </SlotsAttr>
</template>

<script>

import SlotsBase from './components/SlotsBase.vue';
import SlotsTwo from './components/SlotsTwo.vue';
import SlotsAttr from './components/SlotsAttr.vue';

export default {
  data() {
    return {
      "message": "插槽内容续集",
      "Test": "测试内容",
    }
  },
  components: {
    SlotsBase,
    SlotsTwo,
    SlotsAttr,
  }
}

</script>

<style>

</style>

SlotsAttr.vue

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

<script>

export default {
    data() {
        return {
            childMessage: "子组件信息",
        }
    },
}

</script>

具名插槽实现子父组件相互交互

App.vue

<template>
  <!-- <SlotsBase>
    <div>
      <h3>插槽标题</h3>
      <p>插槽内容</p>
    </div>
  </SlotsBase> -->
  <!-- <SlotsTwo>
    <template #header>
      <h3>{{ message }}</h3>
    </template>
    <template #main>
      <h3>{{ message }}</h3>
    </template>
  </SlotsTwo> -->

  <SlotsAttr>
    <template #header="slotProps">
      <h3>{{ Test }} - {{ slotProps.msg }}</h3>
    </template>
    <template #main="slotProps">
      <p>{{ slotProps.job }}</p>
    </template>
  </SlotsAttr>
</template>

<script>

import SlotsBase from './components/SlotsBase.vue';
import SlotsTwo from './components/SlotsTwo.vue';
import SlotsAttr from './components/SlotsAttr.vue';

export default {
  data() {
    return {
      "message": "插槽内容续集",
      "Test": "测试内容",
    }
  },
  components: {
    SlotsBase,
    SlotsTwo,
    SlotsAttr,
  }
}

</script>

<style>

</style>

SlotsAtte.vue

<template>
    <h3>Slots再续集</h3>
    <slot name="header" :msg="childMessage"></slot>
    <slot name="main" :job="jobmessage"></slot>
</template>

<script>

export default {
    data() {
        return {
            childMessage: "子组件信息",
            jobmessage: "XiaoMo",
        }
    },
}

</script>

组件生命周期

App.vue

<template>
  <h3>组件的生命周期</h3>
  <p>{{ message }}</p>
  <button @click="updateHandle"> 更新数据 </button>
</template>
<script>

/**
 * 生命周期函数
 *  创建期: beforeCreate created
 *  挂载期: beforeMount mounted
 *  更新期: beforeUpdate updated
 *  销毁期: beforeUnmount unmounted
 */

export default {
  data() {
    return {
      message: "更新之前",
    }
  },
  methods: {
    updateHandle() {
      this.message = "更新之后";
    }
  },
  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 读取元素

<template>
    <h3>组件生命周期函数应用</h3>
    <p ref="name">XiaoMo</p>
</template>

<script>

export default {
    beforeMount() {
        console.log(this.$refs.name); // undefined
    },
    mounted() {
        console.log(this.$refs.name); // ok
    }
}

</script>

模拟网络请求:

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

<script>
 
export default {
    data() {
        return {
            banner: [],
        }
    },
    beforeMount() {
        console.log(this.$refs.name); // undefined
    },
    mounted() {
        // 模拟网络请求
        this.banner = [
            {
                "title": "HaHa",
                "content": "QwQ",
            },
            {
                "title": "XiaoMo",
                "content": "QwQ",
            },
        ]
        console.log(this.$refs.name); // ok
    },
}

</script>

动态组件

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

<script>

import UseDemo from './components/UseDemo.vue';
import AAA from './components/AAA.vue';
import BBB from './components/BBB.vue';

export default {
  data() {
    return {
      tabComponent: "AAA",
    }
  },
  components: {
    UseDemo,
    AAA,
    BBB,
  },
  methods: {
    changeHandle() {
      this.tabComponent = this.tabComponent == "AAA" ? "BBB" : "AAA";
    }
  }
  
}

</script>

组件保持存活

让组件不被卸载。

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

<script>

import UseDemo from './components/UseDemo.vue';
import AAA from './components/AAA.vue';
import BBB from './components/BBB.vue';

export default {
  data() {
    return {
      tabComponent: "AAA",
    }
  },
  components: {
    UseDemo,
    AAA,
    BBB,
  },
  methods: {
    changeHandle() {
      this.tabComponent = this.tabComponent == "AAA" ? "BBB" : "AAA";
    }
  }
  
}

</script>

异步组件

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

<script>

import { defineAsyncComponent } from 'vue' 
import UseDemo from './components/UseDemo.vue';
import AAA from './components/AAA.vue';
// import BBB from './components/BBB.vue';
const BBB = defineAsyncComponent(() =>
  import("./components/BBB.vue")
)

export default {
  data() {
    return {
      tabComponent: "AAA",
    }
  },
  components: {
    UseDemo,
    AAA,
    BBB,
  },
  methods: {
    changeHandle() {
      this.tabComponent = this.tabComponent == "AAA" ? "BBB" : "AAA";
    }
  }
  
}

</script>

依赖注入

props逐级透传

App.vue

<template>
    <h3> 祖先 </h3>
    <Parent />
</template>

<script>

import Parent from "./components/Parent.vue"

export default {
    provide: {
        message: "祖先的财产",
    },
    components: {
        Parent
    },
}

</script>

Child.vue

<template>
    <h3> Child </h3>
    <p>{{ message }}</p>
</template>

<script>

export default {
    inject:["message"]
}

</script>

必须要存在依赖关系才可以使用 provideinject 传递数据。

全局注入

// import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.provide("golabData", "全局数据")

app.mount('#app')

Vue 应用

import { createApp } from 'vue'
import App from './App.vue'

// app: Vue 的实例对象
// 在一个 Vue 项目中,有且只有一个 Vue 实例对象
const app = createApp(App)

// App:根组件

app.provide("golabData", "全局数据")

// 挂载到容器中
app.mount('#app')
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
  </head>
  <body>
    <!-- 
        浏览器可执行文件:
          1.HTML
          2.CSS
          3.JS
          4.Image

        构建工具:Webpack vite
    -->
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
posted @ 2025-04-13 23:04  XiaoMo247  阅读(36)  评论(0)    收藏  举报