JavaWeb——Vue

一、Vue简介:

Vue是一款用于构建用户界面的JavaScript框架,基于标准的HTML、CSS 和JavaScript构建,并提供了一套声明式的、组件化的编程模型;

Vue的两个核心功能:

(1).声明式渲染:Vue基于标准 HTML拓展了一套模板语法,使得可以声明式地描述最终输出的HTML和JavaScript状态之间的关系;

(2).响应性:Vue 会自动跟踪JavaScript状态并在其发生变化时响应式地更新DOM

二、Vue3通过Vite实现工程化

在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。当构建越来越大型的应用时,需要处理的JavaScript代码量也呈指数级增长;包含数千个模块的大型项目相当普遍。基于JavaScript开发的工具就会开始遇到性能瓶颈:通常需要很长时间才能启动开发服务器,即使使用模块热替换文件修改后的效果也需要几秒钟才能在浏览器中反映出来

Vite旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多JavaScript 工具使用编译型语言编写。前端工程化的作用包括但不限于以下几个方面:

(1).快速创建项目:使用脚手架可以快速搭建项目基本框架,避免从零开始搭建项目的重复劳动和繁
琐操作,从而节省时间和精力;

(2).统一的工程化规范:前端脚手架可以预设项目目录结构、代码规范、git提交规范等统一的工程化规范,让不同开发者在同一个项目上编写出风格一致的代码,提高协作效率和质量;

(3).代码模板和组件库:前端脚手架可以包含一些常用的代码模板和组件库,使开发者在实现常见功能时不再重复造轮子,避免因为轮子质量不高带来的麻烦,能够更加专注于项目的业务逻辑;

(4).自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等;

Vite+Vue3项目的目录结构:

(1).public/目录:用于存放一些公共资源,如 HTML 文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。

(2).src/ 目录:存放项目的源代码,包括JavaScript、css、vue组件、图像和字体等资源。在开发过程中这些文件会被vite实时编译和处理,并在浏览器中进行实时预览和调试

a.assets/目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等

b.components/目录:用于存放组件相关的文件,组件是代码复用的一种方式,用于抽象出一个可复用的部件,方便在不同的场景中进行重复使用

c.layouts/目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等

d.pages/目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下可以创建对应的文件夹,用于存储不同的页面组件

e.plugins/目录:用于存放Vite插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等

f.router/目录:用于存放Vue.js的路由配置文件,负责管理视图和URL之间的映射关系,方便实现页面之间的跳转和数据传递;

g.store/目录:用于存放Vuex状态管理相关的文件,负责管理应用程序中的数据和状态方便统一管理和共享数据,提高开发效率;

h.utils/目录:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等;

(3).vite.config.js文件:vite的配置文件,可以通过该文件配置项目的参数、插件、打包优化等,该文件可以使用CommonJS或ES6模块的语法进行配置;

(4).package.json文件:标准的Node.js项目配置文件,包含了项目的基本信息和依赖关系,其中可以通过scripts字段定义几个命令,如dev、build、serve 等,用于启动开发、构建和启动本地服务器等操作;

(5).Vite项目的入口为src/main.js 文件,这是vue.js应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中通常会引入vue.js及其相关插件和组件,同时会创建vue实例,挂载到HTML 页面上指定的DOM元素中

.vue文件:

传统的页面由html文件css文件和js文件三个文件组成(多文件组件);vue将这文件合并成一个vue文件(Single-File component,简称 SFC,单文件组件);vue文件对js/css/html统一封装,这是VUE中的概念,该文件由三个部分组成:

(1).<script>标签:代表组件的js代码,代替传统的js文件;

(2).<template>标签:代表组件的html部分代码,代替传统的html文件;

(3).<style>标签:代表组件的css样式代码,代替传统的css文件;

工程化vue项目的组织方式:

(1).index.html是项目的入口,其中<div id ='app'></div>是用于挂载所有组建的元素;

(2).index.html中的script标签引入了一个main.js文件,具体的挂载过程在main.js中执行;

(3).main.is是vue工程中非常重要的文件,决定这项目使用的依赖,是导入的第一个组件;

(4).App.vue是vue中的核心组件,所有的其他组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换;

<script type-"module">
    //存储vue页面逻辑js代码
    import {ref} from 'vue'
    export default{
        setup(){
            // 非响应式数据:修改后VUE不会更新DOM
            // 响应式数据:修改后VUE会更新DOM
            // VUE2中数据默认是响应式的
            // VUE3中数据要经过ref或者reactive处理后才是响应式的
            // ref是VUE3框架提供的一个函数,需要导入
            // let counter = 1
            // ref处理的响应式数据在js编码修改的时候需要通过.value操作
            // ref响应式数据在绑定到html上时不需要.value
            let counter = ref(1)
            function increase(){
                // 通过.value修改响应式数据
                counter.value++
            }
            function decrease(){
                counter.value--
            }
            return{
                counter,
                increase,
                decrease,
            }
        }
    }
</script>
<template>
    <div>
        <button @click="decrease()">-</button>
            {{ counter }}
        <button @click="increase()">+</button>
    </div>
</template>
<style scoped>
    button{
        border: 1px solid red;
    }
</style>

Vue3 setup函数和语法糖:

<script type="module" setup>
/* 通过setup关键字,可以省略 export default {setup(){return{}}}这些冗余的语法结构 */
    import {ref} from 'vue'
    // 定义响应式数据
    let counter =ref(1)
    // 定义函数
    function increase(){
        counter.value++
    }
    function decrease(){
        counter.value--
    }
</script>
<template>
    <div>
        <button @click="decrease()">-</button>
            {{ counter }}
        <button @click="increase()">+</button>
    </div>
</template>
<style scoped>
    button{
        border:1px solid red;
    }
</style>

Vite+Vue3关于样式的导入方式:

(1).全局引入main.js:

import './style/reset.css'

(2).vue文件script代码引入:

import './style/reset.css'

(3).vue文件style代码引入:

@import './style/reset.css'

三、Vue视图渲染技术:

模版语法:

vue使用一种基于HTML的模板语法,能够声明式地将其组件实例的数据绑定到呈现的DOM上。所有的Vue模板都是语法层面合法的HTML,可以被符合规范的浏览器和HTML解析器解析。在底层机制中,vue会将模板编译成高度优化的Javascript代码。结合响应式系统,当应用状态变更时vue能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的DOM操作。

插值表达式和文本渲染

插值表达式:最基本的数据绑定形式是文本插值,它使用的是“Mustache"语法,即双大括号{{}}:

(1).插值表达式是将数据渲染到元素的指定位置的手段之一;

(2).插值表达式不绝对依赖标签,其位置相对自由;

(3).插值表达式中支持javascript的运算表达式;

(4).插值表达式中也支持函数的调用;

<script setup type="module">
    let msg = "hello vue3"
    let getMsg = ()=>{
        return "hello wue3 message"
    }
    let age = 19
    let bee = '蜜 蜂'
    // 购物车
    const carts =[{name: '可乐', price:3 , number:10}, {name:'薯片',price:6,number:8}];
    // 计算购物车总金额
    function compute(){
        let count = 0;
        for(let index in carts){
            count += carts[index].price*carts[index].number;
        }
        return count;
    }
</script>
<template>
    <div>
        <h1>{{ msg }}</h1>
            msg的值为:{{ msg }}<br>
            getMsg返回的值为:{{getMsg()}}<br>
            是否成年:{{age>=18?'true':'false'})<br>
            反转:{{bee.split('').reverse().join('-')}}<br>
            购物车总金额:{{compute()}}<br>
            购物车总金额:{{carts[0].price*carts[0].number + carts[1].price*carts[1].number}}
<br>
    </div>
</template>
<style scoped>
</style>

为了渲染双标中的文本,也可以选择使用v-text和v-html命令:

(1).v-***这种写法的方式使用的是vue的命令;

(2).v-***的命令必须依赖元素,并且要写在元素的开始标签中;

(3).v-***指令支持ES6中的字符串模板;

(4).插值表达式中支持javascript的运算表达式;

(5).插值表达式中也支持函数的调用;

(6).v-text可以将数据渲染成双标签中间的文本,但是不识别html元素结构的文本;

(7).v-html可以将数据渲染成双标签中间的文本,识别html元素结构的文本;

<script setup type="module">
    let msg = 'hello vue3'
    let getMsg=()=>{
        return msg
    }
    let age = 19
    let bee = '蜜 蜂'
    let redMsg = '<font color=\'red\'>msg</font>'
    let greenMsg = '<font color=\'green\'>${imsg}</font>'
</script>
<template>
    <div>
        <span v-text='msg'></span><br>
        <span v-text='redMsg'></span><br>
        <span v-text='getMsg()'></span> <br>
        <span v-text='age>18?"成年":"未成年"></span> <br>
        <span v-text='bee.split('") reverse().join("_")'></span> <br>
        <span v-html='msg'></span> <br>
        <span v-html='redMsg'></span><br>
        <span v-html='greenMsg'></span> <br>
        <span v-html="`<font color='green'>${imsg}</font>`"></span><br>
    </div>
</template>
<style scoped>
</style>

Attribute属性渲染:

想要渲染一个元素的attribute,应该使用v-bind指令

由于插值表达式不能直接放在标签的属性中,要渲染元素的属性就应该使用v-bind;

v-bind可以用于渲染任何元素的属性,语法为v-bind:属性名='数据名',可以简写为:属性名='数据名'

<script setup type="module">
    const data ={
        name:'尚硅谷'
        url:"http://www.atguigu.com"
        logo:"http://www.atguigu.com/images/index_new/logo.png"
    }
</script>
<template>
    <div>
        <a
            v-bind:href='data.url'
            target="_self">
            <img
                :src="data.logo" 
            :title="data.name">
        <br>
            <input type="button"
                :value="点击访问${data.name}">
        </a>
    </div>
</template>
<style scoped>
</style>

事件的绑定

可以使用-on来监听DOM事件,并在事件触发时执行对应的vue的JavaScript代码

用法:v-on:click="handler"或简写为@click="handler"

vue中的事件名=原生事件名去掉on前缀,如: onclick对应click

handler的值可以是方法事件处理器,也可以是内联事件处理器;

绑定事件时可以通过一些绑定的修饰符,常见的事件修饰符如下:

(1).once:只触发一次事件;

(2).prevent:阻止默认事件;

(3).stop:阻止事件冒泡;

(4).capture:使用事件捕获模式而不是冒泡模式;

(5).self:只在事件发送者自身触发时才触发事件;

<script setup type="module">
    import {ref} from 'vue'
    //响应式数据 当发生变化时,会自动更新dom树
    let count = ref(8)
    let addCount = ()=>{
        count.value++
    }
    let incrCount = (event)=>{
        count.value++
        // 通过事件对象阻止组件的默认行为
        event.preventDefault();
    }
</script>
<template>
    <div>
        <h1>count的值是:{{ count }}</h1>
        <!--方法事件处理器 -->
        <button v-on:click="addCount()">addCount</button> <br>
        <!-- 内联事件处理器 -->
        <button @click="count++">incrCount</button> <br>
        <!-- 事件修饰符 once 只绑定事件一次 -->
        <button @click.once="count++">addOnce</button> <br>
        <!-- 事件修饰符 prevent 阻止组件的默认行为 -->
        <a href="http://www.atguigu.com" target="_blank" @click.prevent="count++">prevents/a><br>
        <!-- 原生js方式阻止组件默认行为(推荐)-->
        <a href="http://www.atguigu.com" target="_blank" @click.prevent="incrCount(Sevent)">prevents</a><br>
    </div>
</template>
<style scoped>
</style>

响应式基础:

此处的响应式是指数据模型(自定义的变量、对象)发生变化时,自动更新DOM树内容,页面上显示的内容会进行同步变化。vue3的数据模型不是自动响应式的,需要一些特殊的处理。

响应式实现关键字ref

ref可以将一个基本类型的数据(如字符串,数字等)转换为一个响应式对象,ref只能包裹单一元素:

<script type="module" setup>
    /* 从vue中引入ref方法 */
    import {ref} from 'vue'
    let counter =ref(0);
    function show(){
        alert(counter.value);
    }
    /* 函数中要操作ref处理过的数据,需要通过.value形式 */
    let decr =()=>{
        counter.value--;
    }
    let incr =()=>{
        counter.value++;
    }
</script>
<template>
   <div>
        <button @click="counter--">-</button>
        <button @click="decr()">-</button>
        {{ counter }}
        <button @click="counter++">+</button>
        <button @click="incr()">+</button>
        <hr>
        <button @click="show()">显示counter值</button>
    </div>
</template>
<style scoped>
</style>

响应式实现关键字reactive

可以使用reactive()函数创建一个响应式对象或数组:

<script type="module" setup>
    /* 从vue中引入reactive方法 */
    import {ref,reactive} from 'vue'
    let data = reactive({
        counter:6
    })
    function show(){
        alert(data.counter);
    }
    /* 函数中要操作reactive处理过的数据,需要通过 对象名.属性名的方式 */
    let decr=()=>{
        data.counter--;
    }
    let incr=()=>{
        data.counter++;
    }
</script>
<template>
    <div>
        <button @click="data.counter--">-</button>
        <button @click="decr()">-</button>
        {{ data.counter }}
        <button @click="data.counter++">+</button>
        <button @click="incr()">+</button>
        <hr>
        <button @click="show()">显示counter值</button>
    </div>
</template>
<style scoped>
</style>

使用ref适用于以下开发场景:

(1).包装基本类型数据:ref主要用于包装基本类型数据(如字符串、数字等),即只有一个值的数据,如果想监听这个值的变化用ref最为方便,在组件中使用时也很常见

(2).访问方式简单:ref对象在访问时与普通的基本类型值没有太大区别,只需要通过.value访问其实际值即可;

使用reactive适用于以下开发场景:

(1).包装复杂对象:reactive可以将一个普通对象转化为响应式对象,这样在数据变化时会自动更新界面,特别适用于处理复杂对象或者数据结构

(2).需要递归监听的属性:使用reactive可以递归追踪所有响应式对象内部的变化,从而保证界面的自动更新。

ref适用于简单情形下的数据双向绑定,对于只有一个字符等基本类型数据或自定义组件等情况,建议可以使用ref;而对于对象、函数等较为复杂的数据结构以及需要递归监听的属性变化,建议使用reactive

扩展响应式关键字toRefs和toRef:

toRef基于reactive响应式对象上的一个属性,创建一个对应的ref响应式数据。这样创建的ref与其源属性保持同步:改变源属性的值将更新ref的值,反之亦然。toRefs将一个响应式对象多个属性转换为一个多个ref数据,这个普通对象的每个属性都是指向源对象相应属性的ref。每个单独的ref都是使用toRef()创建的。

<script type="module" setup>
    /* 从vue中引入reactive方法 */
    import {ref,reactive,toRef,toRefs} from 'vue'
    let data = reactive({
        counter:0,
        name:"test"
    })
    // 将一个reactive响应式对象中的某个属性转换成一个ref响应式对象
    let ct = toRef(data.'counter');
    // 将一个reactive响应式对象中的多个属性转换成多个ref响应式对象
    let (counter, name) = toRefs(data)
    function show(){
        alert(data.counter);
        // 获取ref的响应对象,需要通过.value属性
        alert(counter.value);
        alert(name.value);
    }
    /* 函数中要操作ref处理过的数据,需要通过.value形式 */
    let decr =()=>{
        data.counter--;
    }
    let incr =()=>{
        /* ref响应式数据,要通过.value属性访间 */
        counter.value++;
    }
</script>
<template>
    <div>
        <button @click="data.counter--">-</button>
        <button @click="decr()">-</button>
        {{ ct }}
        <button @click="data.counter++">+</button>
        <button @click="incr()">+</button>
        <hr>
        <button @click="show()">显示counter值</button>
    </div>
</template>
<style scoped>
</style>
    

条件和列表渲染:

条件渲染:

1.v-if条件渲染:

(1).v-if='表达式'只会在指令的表达式返回真值时才被渲染

(2).也可以使用v-else为v-if添加一个"else 区块"

(3).一个v-else元素必须跟在一个v-if元素后面,否则它将不会被识别

<script type = "module" setup>
    import {ref} from 'vue'
    let awesome = ref(true)
</script>
<template>
    <div>
        <h1 vif = "awesome">Vue is awesome!</h1>
        <h1 v-else>Oh no</h1>
        <button @click="awesome = !awesome">Toggle</button>
    </div>
</template>
<style scoped>
</style>

2.v-show条件渲染扩展:

(1).另一个可以用来按条件显示一个元素的指令是v-show,其用法基本一样;

(2).不同之处在于v-show会在DOM渲染中保留该元素; v-show仅切换了该元素上名为display的CSS属性;

(3).v-show不支持在<template>元素上使用,也不能和v-else搭配使用;

<script type="module" setup>
    import {ref} from "vue"
    let awesome = ref(true)
</script>
<template>
    <div>
        <h1 id="ha" v-show="awesome">Vue is awesome!</h1>
        <h1 id="hb" v-if="awesome">Vue is awesome!</h1>
        <h1 id="hc" v-else> Oh no</h1>
        <button @click="awesome = !awesome">Toggle</button>
    </div>
</template>
<style scoped>
</style>

3.v-if和v-show的对比:

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

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

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

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

列表渲染:

可以使用v-for指令基于一个数组来渲染个列表:

v-for指令的值需要使用item in items形式的特殊语法,其中items是源数据的数组,而item是迭代项的别名;

在v-for块中可以完整地访问父作用域内的属性和变量v-for也支持使用可选的第二个参数表示当前项的位置索引;

<script type "module" setup>
    import { ref,reactive} from 'vue'
    let parentMessage = ref('产品')
    let items = reactive([
        {
            id:'item1',
            message:'薯片'
        },
        {
            id:'item2',
            message:'可乐'
        },
    })
</script>
<template>
    <div>
        <ul>
            <li v-for='item in items':key="item.id">
                {{ item.message }}
            </li>
        </ul>
        <ul>
            <li v-for = "(item, index) in items" :key = "index">
                {{ parentMessage }} - {index}} - {{ item.message }}
            </li>
        </ul>
    </div>
</template>
<style scoped>
</style>

双向绑定:

单项绑定和双向绑定:

单向绑定:响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变不会同步更新到响应式数据

双向绑定:响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变会同步更新到响应式数据;

用户通过表单标签才能够输入数据,所以双向绑定都是应用到表单标签上的,其他标签不行;

v-model专门用于双向绑定表单标签的value属性,语法为v-model:value='',可以简写为v-model='';

v-model还可以用于各种不同类型的输入,例如<textarea>、<select>等元素;

<script type "module" setup>
    //引入模块
    import {reactive, ref} from 'vue'
    let hbs = ref([]);//装爱好的值
    let user = reactive((username:null,password:null,introduce:null,pro:null})
    function login(){
        alert(hbs.value);
        alert(JSON.stringify(user));
    }
    function clearx(){
        user.username = ''
        user.password = ''
        user.introduce = ''
        user.pros = ''
        hbs.value.splice(0,hbs.value.length)
    }
</script>
<template>
    <div>
        账号:<input type = "text" placeholder = "请输入账号!" v-model="user.username"> <br>
        密码:<input type = "text" placeholder= "请输入账号!" v-model= "user.password"><br>        
        爱好:
            吃 <input type = "checkbox" name = "hbs" v-model = "hbs" value = "吃">
            喝 <input type = "checkbox" name = "hbs" v-model = "hbs" values = "喝">
        <br>
        简介:<textarea v-model="user.introduce"></textarea>
        <br>
        籍贯:
            <select v-model="user.pro">
                <option value="1">黑</option>
                <option value="2">吉</option>
                <option value="3">辽</option>
                <option value="4">京</option>
                <option value="5">津</option>
                <option value="6">冀</option>
            </select>
        <br>
            <button @click="login()">登录</button>
            <button @click="clearx()">重置</button>
        <hr>
            显示爱好:{{ hbs }}
        <hr>
            显示用户信息:{{ user }}
    </div>
</template>
<style scoped>
</style>

属性计算:

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。推荐使用计算属性来描述依赖响应式状态的复杂逻辑。

<script type="module" setup>
    // 引入模块
    import {reactive, computed} from 'vue'
    const author = reactive({
        name:'John Doe'
        books:[
            'Vue 2-Advanced Guide',
            'Vue 3- Basic Guide',
            'Vue 4 - The Mystery'
        ]
    })
    const publishedBooksMessage = computed(()=>{
        console.log("publishedBooksMessage")
        return author.books.length>0 ?'Yes':'No'
    })
    let hasBooks =()=> {
        console.log("hasBooks")
        return author.books.length > 8?'Yes':'no'
    }
</script>
<template>
    <div>
        <p>{{ author.name }} Has published books?:</p>
        <span>{{ author.books.length>0?'Yes':'No'}}</span>
        <span>{{ hasBooks()}}</span>
        <span>{{ hasBooks()}}</span>
        <p>{{author.name}} Has published books?:</p>
        <span>{{ publishedBooksMessage }}</span>
    </div>
</template>
<style scoped>
</style>  

在这里定义了一个计算属性publishedBooksMessage;computed()方法期望接收一个getter函数,返回值为一个计算属性ref。和其他一般的ref类似,可以通过publishedBooksMessage.value访问计算结果。计算属性ref也会在模板中自动解包,因此在模板表达式中引用时无需添加.value

Vue的计算属性会自动追踪响应式依赖,它会检测到publishedBooksMessage依赖于author.books,所以当author.books改变时任何依赖于publishedBooksMessage的绑定都会同时更新

若将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要author.books不改变,无论多少次访问publishedBooksMessage都会立即返回先前的计算结果

数据监听器:

计算属性允许声明性地计算衍生值。然而在有些情况下需要在状态变化时执行一些“副作用”:例如更改DOM或是根据异步操作的结果去修改另一处的状态。可以使用watch函数在每次响应式状态发生变化时触发回调函数:

watch主要用于以下场景:

当数据发生变化时需要执行相应的操作;

监听数据变化,当满足一定条件时触发相应操作;

在异步操作前或操作后需要执行相应的操作;

监控响应式数据(watch):

<script type= "module" setup>
    import {ref,reactive,watch} from 'vue'
    let firstname = ref("")
    let lastname = reactive((name:''})
    let fullname = ref('')
    //监听一个ref响应式数据
    watch(firstname,(newValue,oldValue)=>{
        console.log(`${oldValue}变为${newValue}`)
        fullname.value = firstname.value+lastname.name}
    )
    //监听reactive响应式数据的指定属性
    watch(()=>lastname.name,(newValue,oldValue)=>{
        console.log('${oldValue}变为${newValue}')
        fullname.value=firstname.value+lastname.name}
    )
    //监听reactive响应式数据的所有属性(深度监视,一般不推荐)
    //deep:true 深度监视
    //immediate:true深度监视在进入页面时立即执行一次
    watch(()=>lastname,(newValue,oldValue)=>{
        // 此时的newValue和oldValue一样,都是lastname
        console.log(newValue)
        console.log(oldValue)
        fullname.value=firstname.value+lastname.name
    },{deep:true,immediate:false})
</script>
<template>
    <div>
        全名:{{ fullname }}<br>
        姓氏:<input type="text" v-model="firstname"><br>
        名字:<input type="text" v-model="lastname.name" ><br>
    </div>
</template>
<style scoped>
</style>
    

监控响应式数据(watchEffect):watchEffect默认监听所有的响应式数据

<script type="module" setup>
    import {ref,reactive,watch,watchEffect} from 'vue'
    let firstname = ref('')
    let lastname = reactive({name:''})
    let fullname = ref('')
    // 监听所有响应式数据
    watchEffect(()=>{
        // 直接在内部使用监听属性即可!不用外部声明
        // 也不需要,即时回调设置!默认初始化就加载!
        console.log(firstname.value)
        console.log(lastname.name)
        fullname.value = `${firstname.value}${lastname.name}`
        })
</script>
<template>
    <div>
        全名:{{fullname}} <br>
        姓氏:<input type="text" v-model="firstname"><br>
        名字:<input type-"text" v-model="lastname.name"><br>
    </div>
</template>
<style scoped>
</style>

watch和watcheffect都能响应式地执行有副作用的回调,它们之间的主要区别是追踪响应式依赖的方式:

(1).watch只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调,watch会避免在发生副作用时追踪依赖,因此能更加精确地控制回调函数的触发时机;

(2).watchEffect则会在副作用发生期间追踪依赖。它会在同步执行过程中自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确;

Vue生命周期

生命周期简介:每个vue组件实例在创建时都需要经历一系列的初始化步骤,上比如设置好数据侦听,编译模板,挂载实例到DOM,以及在数据改变时更新DOM。在此过程中它也会运行被称为生命周期钩子的函数 ,让开发者有机会在特定阶段运行自己的代码

常见钩子函数:

(1).onMounted():注册一个回调函数,在组件挂载完成后执行;

(2).onUpdated():注册一个回调函数,在组件因为响应式状态变更而更新其DOM树之后调用;

(3).onUnmounted():注册一个回调函数,在组件实例被卸载之后调用;

(4).onBeforeMount():注册一个钩子,在组件被挂载之前被调用

(5).onBeforeUpdate():注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用

(6).onBeforeUnmount():注册一个钩子,在组件实例被卸载之前调用

<script setup>
    import {ref,onUpdated,onMounted,onBeforeUpdate) from 'vue'
    let message = ref('hello')
    //挂载完毕生命周期
    onMounted(()=>{
        console.log('-----------onMounted----')
        let spanl = document.getElementById("span1")
        console.log(span1.innerText)
    })
    //更新前生命周期
    onBeforeUpdate(()=>{
        console.log('-------onBeforeUpdate----')
        console.log(message.value)
        let span1 = document.getElementById("span1")
        console.log(span1.innerText)
    })
    //更新完成生命周期
    onUpdated(()=>{
        console.log('----onUpdated---')
        let span1 = document.getElementById("span1")
        console.log(span1.innerText)
    })
</script>
<template>
    <div>
        <span id="span1" v-text="message"></span><br>
        <input type="text" v-model="message">
    </div>
</template>
<style scoped>
</style>

Vue组件:

组件基础:组件允许将UI划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合;在实际应用中组件常常被组织成层层嵌套的树状结构

这和嵌套HTML元素的方式类似,vue实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。 

组件之间传递数据:

父传子:Vue3中父组件向子组件传值可以通过 props进行,具体操作如下

首先,在父组件中定义需要传递给子组件的值,接着在父组件的模板中引入子组件,同时在引入子组件的标签中添加props属性并为其设置需要传递的值。

在vue3 中父组件通过props传递给子组件的值是响应式的。也就是说如果在父组件中的传递的值发生了改变,子组件中的值也会相应地更新。

父组件代码:App.vue

<script setup>
    import Son from './components/Son.vue'
    import {ref,reactive,toRefs} from 'vue'
    let message = ref('parent data!')
    let title = ref(42)
    function changeMessage(){
        message.value= '修改数据!'
        title.value++
    }
</script>
<template>
    <div>
        <h2>{{ message }}</h2>
        <hr>
        <!--使用子组件,并且传递数据!-->
        <Son:message="message":title="title"></Son>
        <hr>
        <button @click="changeMessage">点击更新</button>
    </div>
</template>
<style scoped>
</style>

子组件代码:Son.vue

<script setup type="module">
    import {ref,isRef,defineProps} from 'vue'
    //声明父组件传递属性值
    defineProps({
        message:String,
        title:Number
    })
</script>
<template>
    <div>
    <div>{{ message }}</div>
    <div>{{ title }}</div>
    </div>
</template>
<style>
</style>

子传父:

父组件:App.vue

<script setup>
    import Son from './components/Son.vue'
    import {ref} from 'vue'
    let pdata = ref('')
    const padd = (data) => {
        console.log('2222');
        pdata.value =data;
    }
    //自定义接收,子组件传递数据方法,参数为数据
    const psub =(data)=>{
        console.log('11111');
        pdata.value = data;
    }
</script>
<template>
    <div>
        <!-- 声明@事件名应该等于子模块对应事件名,调用方法可以是当前自定义-->
        <Son @add="padd" @sub="psub"></Son>
        <hr>
        {{ pdata }}
    </div>
</template>
<style>
</style>

子组件:Son.vue

<script setup>
    import {ref,defineEmits} from 'vue'
    //1.定义要发送给父组件的方法,可以1或者多个
    let emites = defineEmits(['add','sub']);
    let data=ref(1);
    function sendMsgToParent(){
        //2.出发父组件对应的方法,调用defineEmites对应的属性
        emites('add','add data!'+data.value)
        emites('sub','sub data!'+data.value)
        data.value ++;
    }
</script>
<template>
    <div>
        <button @click="sendMsgToParent">发送消息给父组件</button>
    </div>
</template>

兄弟传参:

Navigator.vue发送数据到App.vue

<script setup type= "module">
    import {defineEmits} from 'vue'
    const emits = defineEmits(['sendMenu']);
    //触发事件,向父容器发送数据
    function send(data){
        emits("sendMenu",data);
    }
</script>
<template>
    <!-- 推荐写一个根标签-->
        <div>
            <ul>
                <li @click="send('学员管理')">学员管理</li>
                <li @click="send('图书管理')">图书管理</1i>
                <li @click="send('诸假管理')">请假管理</li>
                <li @click="send('考试管理')">考试管理</1i>
                <li @click="send('讲师管理')">讲师管理</1i>
            </ul>
        </div>
</template>
<style>
</style>

App.vue:发送数据到Content.vue

<script setup>
    import Header from './components/Header.vue'
    import Navigator.from './components/Navigator.vue'
    import Content from './components/Content.vue'
    import {ref} from "vue"
    //定义接受navigator传递参数
    var navigator menu = ref('ceshi');
    const receiver =(data)=>{
        navigator_menu.value = data;
    }
</script>
<template>
    <div>
        <hr>
            {{ navigator_menu }}
        <hr>
        <Header class="header"></Header>
        <Navigator @sendMenu="receiver" class "navigator"></Navigator>
        <!-- 向子组件传递数据-->
        <Content class="content" :message "navigator menu"></Content>
    </div>
</template>
<style scoped>
    .header{
        height:80px;
        border:1px solid red;
    }
    .navigator{
        width:15%;
        height:880px;
        display:inline-block;
        border:1px blue solid;
        float:left;
    }
    .content{
        width:83%;
        height: 800px;
        display:inline-block;
        border:1px goldenrod solid;
        float: right;
    }
</style>

content.vue

<script setup type="module">
    defineProps({
        message:String
    })
</script>
<template>
    <div>
        展示的主要内容!
        <hr>
        {{ imessage }}
    </div>
</template>
<style>
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值