【vue】组件及组件化+组件⽣命周期

代码获取

04-组件及组件化+组件⽣命周期

⼀、组件及组件化

1. 为什么需要组件?

1.1 思考

以可折叠⾯板为例, 现要展⽰3个, 如何操作?

可折叠⾯板案例的代码 :

vue 复制代码
<script setup>
import { ref } from 'vue'
const visible = ref(false)
</script>
<template>
    <h3>可折叠⾯板</h3>
    <div class="panel">
        <div class="title">
            <h4>⾃由与爱情</h4>
            <span class="btn" @click="visible = !visible"> {{ visible ? '收起' : '展开' }} </span>
        </div>
        <div class="container" v-show="visible">
            <p>⽣命诚可贵,</p>
            <p>爱情价更⾼。</p>
            <p>若为⾃由故,</p>
            <p>两者皆可抛。</p>
        </div>
    </div>
</template>
<style lang="scss">
body {
    background-color: #ccc;
}

#app {
    width: 400px;
    margin: 20px auto;
    background-color: #fff;

    border: 4px solid green;
    border-radius: 1em;
    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
    padding: 1em 2em 2em;
}

#app h3 {
    text-align: center;
}

.panel {
    .title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        border: 1px solid #ccc;
        padding: 0 1em;
    }

    .title h4 {
        line-height: 2;
        margin: 0;
    }

    .container {
        border: 1px solid #ccc;
        padding: 0 1em;
        border-top-color: transparent;
    }

    .btn {
        cursor: pointer;
    }
}
</style>
1.2 解决⽅案

有请重量级主⻆ 组件 闪亮登场

  1. 把需要复⽤的⼀段标签, 抽离并封装到⼀个单独的vue⽂件⾥, 连同相关JS和CSS放到⼀起

  2. 哪⾥要⽤这个组件,哪⾥导⼊, 当做标签使⽤即可

1.2.1、新建⽂件并填充代码

新建 src/components/MyPanel.vue

vue 复制代码
<template>
    <div class="panel">
        <div class="title">
            <h4>⾃由与爱情</h4>
            <span class="btn" @click="visible = !visible"> {{ visible ? '收起' : '展开' }} </span>
        </div>
        <div class="container" v-show="visible">
            <p>⽣命诚可贵,</p>
            <p>爱情价更⾼。</p>
            <p>若为⾃由故,</p>
            <p>两者皆可抛。</p>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue'
const visible = ref(false)
</script>

<style scoped>
.panel {
    .title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        border: 1px solid #ccc;
        padding: 0 1em;
    }

    .title h4 {
        line-height: 2;
        margin: 0;
    }

    .container {
        border: 1px solid #ccc;
        padding: 0 1em;
        border-top-color: transparent;
    }

    .btn {
        cursor: pointer;
    }
}
</style>
1.2.2、App.vue导⼊并使⽤
vue 复制代码
<script setup>
// 导入
import MyPanel from './components/MyPanel.vue'
</script>
<template>
    <h3>可折叠⾯板</h3>
    <!-- 使用 -->
    <MyPanel />
    <MyPanel />
    <MyPanel />
</template>
<style lang="scss">
body {
    background-color: #ccc;
}

#app {
    width: 400px;
    margin: 20px auto;
    background-color: #fff;

    border: 4px solid green;
    border-radius: 1em;
    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
    padding: 1em 2em 2em;
}

#app h3 {
    text-align: center;
}

</style>
1.3 总结
  1. 为什么需要组件?

​ 答:当遇到⼀段标签(UI)需要复⽤的时候

  1. 如何让这⼀段标签(UI)复⽤?

​ 答:抽离 -> 封装( JS+HTML+CSS ) -> 导⼊ -> 使⽤

2. 组件及组件化

2.1 组件

组件是⼀个 独⽴的、可复⽤ 的 Vue 实例,也是⼀段 独⽴的 UI 视图 ,代码上体现在是⼀个独⽴的.vue ⽂件,包含 JS+HTML+CSS 3 部分组成。

类似乐⾼和积⽊⼀样,我们可以通过任意的乐⾼或积分进⾏组合,拼装成我们需要的成品。

2.2 组件化
  • 定义:⼀种代码的开发思想,体现在⼀个⻚⾯可以拆分成⼀个个组件,每个组件有着⾃⼰独⽴的结构、样式、⾏为; 通过 组件的组合与拼装 形成⼀个完整的⻚⾯,本质是代码的⼀种拆分思想,化⼤为⼩、化繁为简、分⽽治之

  • 好处:各⾃独⽴、便于复⽤

2.3 总结
  1. 什么是组件

​ 答:⼀个 可复⽤的、独⽴的 Vue 实例(UI) ,包含 3 部分代码

  1. 组件化的好处是什么?

​ 答: 化⼤为⼩、化繁为简 , 利于代码复⽤和维护

3. 根组件 App.vue

3.1 根组件

整个应⽤最上层的组件,包裹所有普通⼩组件

3.2 组件是由三部分构成
  • 三部分构成

    • template:HTML 结构

    • script: JS 逻辑

    • style: CSS 样式 (可⽀持less/scss,需要装包)

  • 让组件⽀持less/scss

    • style标签, lang="less/scss" 开启 less/scss 功能

    • 装包: npm i less less-loader -D 或者 npm i sass -D

3.3 总结
  1. App组件我们称之为什么?

​ 答:根组件(顶层组件)

4. 组件的使⽤

4.1 创建

新建 .vue ⽂件,编写组件的 3 部分代码

4.2 导⼊
js 复制代码
import 组件对象 from '相对路径'
// eg
import MyPanel from './components/MyPanel.vue'
4.3 注册(仅限于全局组件)

注意: 局部组件⽆需注册,全局组件要在 main.js 中注册

4.4 使⽤

把组件当做⾃定义标签使⽤(单双标签均可)

js 复制代码
<组件名></组件名>
<组件名 />
    
// eg
<!-- ⼤驼峰标 双标签 -->
<MyPanel></MyPanel>
<!-- ⼤驼峰 ⾃闭合的单标签 -->
<MyPanel />
<!-- 烤串法 双标签 -->
<my-panel></my-panel>
<!-- 烤串法 ⾃闭合的单标签 -->
<my-panel />
4.5 练习

在App.vue中使⽤组件的⽅式完成下⾯布局

components/zxj2022Header.vue

vue 复制代码
<script setup></script>

<template>
    <div class="zxj-header">我是zxj-header</div>
</template>

<style>
.zxj-header {
    height: 100px;
    line-height: 100px;
    background-color: #8064a2;
}
</style>

components/zxj2022main.vue

vue 复制代码
<script setup></script>

<template>
    <div class="zxj-main">我是zxj-main</div>
</template>

<style>
.zxj-main {
    height: 400px;
    margin: 20px 0;
    line-height: 400px;
    background-color: #f79646;
}
</style>

components/zxj2022footer.vue

vue 复制代码
<script setup></script>

<template>
    <div class="zxj-footer">我是zxj-footer</div>
</template>

<style>
.zxj-footer {
    height: 100px;
    line-height: 100px;
    background-color: #4f81bd;
}
</style>

App.vue

vue 复制代码
<template>
    <div>
        <zxj2022Head />
        <zxj2022Main />
        <zxj2022footer />
    </div>
</template>

<script setup>
import zxj2022Head from './components/zxj2022Head.vue';
import zxj2022Main from './components/zxj2022Main.vue';
import zxj2022footer from './components/zxj2022footer.vue';
</script>

<style>
* {
    margin: 0;
}

#app {
    height: 100vh;
    padding: 10px;
    background: skyblue;
    font-size: 30px;
    color: #fff;
    text-align: center;
}
</style>
4.6 总结
  1. A组件内部导⼊组件能在B组件使⽤吗?

​ 答: no, 不能

  1. 使⽤组件⾃定义标签 时应该按照什么命名法?

​ 答: 1、⼤驼峰法

​ 2、烤串法

5. 组件的全局注册

5.1 特点

全局注册的组件,在项⽬的任何组件中都能使⽤

5.2 步骤
  1. 创建.vue组件(三个组成部分)

  2. main.js 中进⾏全局注册

5.3 使⽤⽅式

当成HTML标签直接使⽤:

  • 双标签: <组件名></组件名>

  • ⾃闭合的单标签: <组件名 />

5.4 注意

组件名规范: ⼤驼峰命名法或烤串法

5.5 语法
js 复制代码
// main.js
import MyPanel from './components/MyPanel.vue'
// 注册全局组件
// app.component('组件名', 组件对象)
// ⼤驼峰组件名
app.component('MyPanel', MyPanel)
// 烤串法组件名
app.component('my-panel', MyPanel)
5.6 总结
  1. 全局注册组件在任何⼀个组件中可不可以⽤?

​ 答:Yes,⼀旦注册,任意 .vue 中都可⽤

⼆、组件⽣命周期

1. ⽣命周期介绍

1.1 思考
  • 什么时候可以发送初始化渲染请求?(越早越好)
  • 什么时候可以开始操作DOM?(⾄少DOM得渲染出来)
1.2 概念

就是⼀个Vue实例(组件)从 创建 到 卸载 的整个过程

1.3 四个阶段

⽣命周期四个阶段:① 创建 ② 挂载 ③ 更新 ④ 卸载

  1. 创建阶段:创建响应式数据

  2. 挂载阶段:渲染模板

  3. 更新阶段:修改数据,更新视图

  4. 卸载阶段:卸载组件

1.4 总结
  1. 什么是vue的⽣命周期?

​ 答:组件从 创建到卸载 的过程

  1. 组件的⽣命周期共经历哪⼏个阶段?

​ 答:4个, 创建、挂载、更新、卸载/销毁

2. 组件⽣命周期钩⼦

2.1 介绍

每个 Vue 组件实例在创建时都需要经历⼀系列的初始化步骤,⽐如设置好数据监听,编译模板,挂载实例到真实 DOM 树上,以及在数据改变时更新 DOM。在此过程中,会 ⾃动运⾏⼀些函数 ,这些函数被称为【Vue⽣命周期钩⼦

2.2 作⽤

给了开发者在特定阶段添加⾃⼰代码的机会

2.3 代码⽰例(选项式API)

新建 components/LifeCycle.vue

vue 复制代码
<script>
export default {
    // 提供响应式数据
    data() {
        return {
            count: 0
        }
    },
    // 提供⽅法/函数
    methods: {
        fn() {
            console.log('fn 函数执⾏了')
        }
    },
    setup() {
        console.log('0-setup')
    },
    // 创建阶段(第⼀阶段):Vue组件创建/出⽣阶段:

    // 创建前:此时⽆法访问 data 数据,也⽆法调⽤ methods ⽅法
    beforeCreate() {
        console.log('1-beforeCreate')
        // console.log(this.count) // undefined
        // console.log(this.fn) // undefined
    },
    // 创建后:此时可以访问 data 数据,也可以调⽤ methods ⽅法
    created() {
        console.log('2-created')
        // console.log(this.count) // 0
        // // console.log(this.fn)// 访问到函数
        // this.fn()

        // 开启定时器
        // 给当前组件实例新增了⼀个 timerId 属性,保存了当前定时器的 id 值
        this.timerId = setInterval(() => {
            console.log(this.count)
        }, 1000)
    },

    // 挂载阶段(第⼆阶段):模版渲染阶段

    // 挂载前:此时写在 template 下的标签还没有变成真实DOM,故⽽⽆法获取DOM
    beforeMount() {
        console.log('3-beforeMount')
        console.log(document.querySelector('p')) // null
    },

    // 挂载后:此时写在 template 下的标签已经变成了真实DOM,故⽽可以获取DOM(是最早可以DOM的时机)
    mounted() {
        console.log('4-mounted')
        console.log(document.querySelector('p')) // <p>0</p>
        document.querySelector('p').style.color = 'red'
    },

    // 更新阶段(第三阶段):数据变了,组件重新渲染的过程

    // 更新前
    beforeUpdate() {
        console.log('5-beforeUpdate')
        // console.log(this.count)

        console.log(document.querySelector('p').innerText) // 旧内容(以前的内容)
    },

    // 更新后
    updated() {
        console.log('6-updated')
        // console.log(this.count)
        console.log(document.querySelector('p').innerText) // 新内容
    },

    // 卸载阶段(第四阶段):组件移除阶段
    beforeUnmount() {
        console.log('7-beforeUnmount')
    },

    unmounted() {
        console.log('8-mounted')
        // 关闭定时器
        clearInterval(this.timerId)
    }
}
</script>
<template>
    <div>
        <p>{{ count }}</p>
        <button @click="count++">+1</button>
    </div>
</template>
<style scoped></style>
2.4 总结
  1. ⽣命周期钩⼦函数的作⽤?

​ 答: 钩⼦函数在特定时机会 ⾃动执⾏ , 给了开发者在不同时机有 添加⾃⼰代码 的机会

  1. 选项式API下, 组件⾸次渲染,会执⾏哪⼏个钩⼦?

​ 答:5个, setup/beforeCreate/created/beforeMount/mounted

  1. 选项式API下, 如果⼀进⼊组件就向后端发请求,在哪个钩⼦进⾏?

​ 答: created

  1. 选项式API下, 最早可以操作原⽣DOM, 在哪个钩⼦进⾏?

​ 答:mounted

  1. 选项式API下, 组件销毁, 要做优化⼯作, 在哪进⾏?

​ 答: unmounted

3. 组合式API⽣命周期钩⼦

3.1 对⽐Vue2钩⼦函数

Vue2钩⼦函数(选项式) VS Vue3 钩⼦函数(组合式)

创建阶段 挂载阶段 更新阶段 销毁阶段
Vue2 beforeCreate created beforeMount mounted beforeUpdate updated beforeUnmount unmounted
Vue3 setup(⽹络请求) onBeforeMount onMounted(操作DOM) onBeforeUpdate onUpdated onBeforeUnmount onUnmounted(清理⼯作)
3.2 代码实例

components/LifeCycle.vue

vue 复制代码
<script setup>
import { onMounted, onUnmounted } from 'vue'
// 开启定时器
const timer = setInterval(() => {
    console.log('Hello World')
}, 1000)
// 组件挂载后
onMounted(() => {
    // console.log(document.querySelector('p'))
    // 将 p 标签的字体颜⾊设置为 green
    document.querySelector('p').style.color = 'green'
})
// 组件卸载后
onUnmounted(() => {
    // 关闭定时器
    clearInterval(timer)
})
</script>

<template>
<div>

</div>
</template>



<style scoped>

</style>
3.3 总结
  1. 组合式API下, 如果⼀进⼊组件就向后端发请求,在哪个钩⼦进⾏?

​ 答: setup

  1. 组合式API下, 最早可以操作原⽣DOM, 在哪个钩⼦进⾏?

​ 答: onMounted

  1. 组合式API下, 组件销毁, 要做优化⼯作, 在哪进⾏?

​ 答: onUnmounted

4. 案例-⽣命周期钩⼦应⽤

4.1 在setup中请求数据并渲染
4.1.1 静态代码
vue 复制代码
<script setup>
// 运⾏ vue3-node-server 项⽬, 进⼊根⽬录:
// 1. 安装依赖: npm i
// 2. 启动服务: npm run start
// 接⼝地址:http://localhost:4000/api/news
// 请求⽅式:get
// 请求参数:⽆
</script>
<template>
    <ul>
        <li class="news">
            <div class="left">
                <div class="title">5G商⽤在即,三⼤运营商营收持续下降</div>
                <div class="info">
                    <span>新京报经济新闻</span>
                    <span>2222-10-28 11:50:28</span>
                </div>
            </div>
            <div class="right">
                <img src="http://ajax-api.itheima.net/images/0.webp" alt="img" />
            </div>
        </li>
        <li class="news">
            <div class="left">
                <div class="title">5G商⽤在即,三⼤运营商营收持续下降</div>
                <div class="info">
                    <span>新京报经济新闻</span>
                    <span>2222-10-28 11:50:28</span>

                </div>
            </div>
            <div class="right">
                <img src="http://ajax-api.itheima.net/images/0.webp" alt="img" />
            </div>
        </li>
    </ul>
</template>

<style>
* {
    margin: 0;
    padding: 0;
}

ul {
    list-style: none;
}

.news {
    display: flex;
    height: 120px;
    width: 600px;
    margin: 0 auto;
    padding: 20px 0;
    cursor: pointer;
}

.news .left {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding-right: 10px;
}

.news .left .title {
    font-size: 20px;
}

.news .left .info {
    color: #999999;
}

.news .left .info span {
    margin-right: 20px;
}

.news .right {
    width: 160px;
    height: 120px;
}

.news .right img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
</style>
4.1.2 安装 axios
she 复制代码
npm i axios

# or

yarn add axios
4.1.3 请求数据
js 复制代码
import { ref, onMounted } from 'vue'
import axios from 'axios' // 引⼊ axios, 使⽤ axios 发送请求

const newsList = ref([])

axios.get('http://localhost:4000/api/news').then(res => {
    newsList.value = res.data.data
    console.log(newsList.value);
})
4.1.4 完整代码
vue 复制代码
<script setup>
// 运⾏ vue3-node-server 项⽬, 进⼊根⽬录:
// 1. 安装依赖: npm i
// 2. 启动服务: npm run start
// 接⼝地址:http://localhost:4000/api/news
// 请求⽅式:get
// 请求参数:⽆

import { ref, onMounted } from 'vue'
import axios from 'axios' // 引⼊ axios, 使⽤ axios 发送请求

const newsList = ref([])

axios.get('http://localhost:4000/api/news').then(res => {
    newsList.value = res.data.data
    console.log(newsList.value);
})


</script>
<template>
    <ul v-for="(news, index) in newsList" :key="news.id">
        <li class="news">
            <div class="left">
                <div class="title">{{ news.title }}</div>
                <div class="info">
                    <span>{{news.source}}</span>
                    <span>{{news.time}}</span>
                </div>
            </div>
            <div class="right">
                <img :src="news.img" alt="img" />
            </div>
        </li>
    </ul>
</template>

<style>
* {
    margin: 0;
    padding: 0;
}

ul {
    list-style: none;
}

.news {
    display: flex;
    height: 120px;
    width: 600px;
    margin: 0 auto;
    padding: 20px 0;
    cursor: pointer;
}

.news .left {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding-right: 10px;
}

.news .left .title {
    font-size: 20px;
}

.news .left .info {
    color: #999999;
}

.news .left .info span {
    margin-right: 20px;
}

.news .right {
    width: 160px;
    height: 120px;
}

.news .right img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
</style>

4.2 在onMounted中操作DOM

输⼊框聚焦

4.2.1 静态代码
vue 复制代码
<script setup>
</script>
<template>
    <div class="container">
        <img width="150"
            src="https://foruda.gitee.com/avatar/1698232656737798373/12025902_jie-c-language_1698232656.png"
            alt="logo" />
        <div class="search-box">
            <input type="text" id="search" />
            <button>搜 索</button>
        </div>
    </div>
</template>

<style>
html,
body {
    height: 100vh;
}

.container {
    position: absolute;
    top: 30%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
}

.container .search-box {
    display: flex;
}

.container img {
    margin-bottom: 30px;
}

.container .search-box input {
    width: 512px;
    height: 17px;
    padding: 12px 16px;
    font-size: 16px;
    margin: 0;
    vertical-align: top;
    outline: 0;
    box-shadow: none;
    border-radius: 10px 0 0 10px;
    border: 2px solid #c4c7ce;
    background: #fff;
    color: #222;
    overflow: hidden;
    -webkit-tap-highlight-color: transparent;
}

.container .search-box button {
    width: 112px;
    height: 44px;
    line-height: 42px;
    background-color: #ad2a27;
    border-radius: 0 5px 5px 0;
    font-size: 17px;
    box-shadow: none;
    font-weight: 400;
    margin-left: -1px;
    border: 0;
    outline: 0;
    letter-spacing: normal;
    color: white;
    cursor: pointer;
}

body {
    background: #f1f2f3 no-repeat center / cover;
}
</style>
4.2.2 完整代码
vue 复制代码
<script setup>

// 操作 DOM, 在组件挂载的时候就是最佳时机
import { onMounted } from 'vue'
onMounted(() => {
    document.querySelector('#search').focus();
})

</script>
<template>
    <div class="container">
        <img width="150"
            src="https://foruda.gitee.com/avatar/1698232656737798373/12025902_jie-c-language_1698232656.png"
            alt="logo" />
        <div class="search-box">
            <input type="text" id="search" />
            <button>搜 索</button>
        </div>
    </div>
</template>

<style>
html,
body {
    height: 100vh;
}

.container {
    position: absolute;
    top: 30%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
}

.container .search-box {
    display: flex;
}

.container img {
    margin-bottom: 30px;
}

.container .search-box input {
    width: 512px;
    height: 17px;
    padding: 12px 16px;
    font-size: 16px;
    margin: 0;
    vertical-align: top;
    outline: 0;
    box-shadow: none;
    border-radius: 10px 0 0 10px;
    border: 2px solid #c4c7ce;
    background: #fff;
    color: #222;
    overflow: hidden;
    -webkit-tap-highlight-color: transparent;
}

.container .search-box button {
    width: 112px;
    height: 44px;
    line-height: 42px;
    background-color: #ad2a27;
    border-radius: 0 5px 5px 0;
    font-size: 17px;
    box-shadow: none;
    font-weight: 400;
    margin-left: -1px;
    border: 0;
    outline: 0;
    letter-spacing: normal;
    color: white;
    cursor: pointer;
}

body {
    background: #f1f2f3 no-repeat center / cover;
}
</style>
相关推荐
Monly217 分钟前
JS:JSON操作
前端·javascript·json
小何学计算机1 小时前
Nginx 配置基于主机名的 Web 服务器
服务器·前端·nginx
web_code1 小时前
vite依赖预构建(源码分析)
前端·面试·vite
觉醒法师1 小时前
HarmonyOS开发 - 本地持久化之实现LocalStorage支持多实例
前端·javascript·华为·typescript·harmonyos
小何学计算机2 小时前
Nginx 配置基于IP 地址的 Web 服务器
前端·tcp/ip·nginx
w风雨无阻w2 小时前
Vue3 学习笔记(十一)Vue生命周期
javascript·vue.js·前端框架·vue3
清清ww2 小时前
【vue】13.深入理解递归组件
前端·javascript·vue.js
清清ww2 小时前
【vue】09.computer和watch的使用
前端·javascript·vue.js
Gnevergiveup2 小时前
2024网鼎杯青龙组Web+Misc部分WP
开发语言·前端·python
你不讲 wood2 小时前
使用 Axios 上传大文件分片上传
开发语言·前端·javascript·node.js·html·html5