第一部分 Vue讲解(代码版)

news2024/11/24 14:26:52

1.第一个vue实例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>01.创建第一个Vue实例</title>
</head>
<body>
<!--创建Vue实例,初始化渲染-->
<!--1.容器(Vue所管理的范围)-->
<!--2.导包(开发版本包/生产版本包) 官网查找-->
<!--(1) 开发环境版本,包含了有帮助的命令行警告 (使用)-->
<!--<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>-->
<!--(2) 生产环境版本,优化了尺寸和速度 -->
<!--<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>-->
<!--3.实例(new对象)-->
<!--4.配置项(el:挂载点,data:数据)-->
<!--5.完成渲染-->

<!--1.容器-->
<div class="box"></div>
<div class="box2"></div>
<div id="app">
    <!--    编写用于渲染的代码逻辑-->
    <h1>{{msg}}</h1>
    {{count}}
</div>
<!--2.引入开发版本核心包,该包包含完整的注释和警告-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    <!--    一旦引入VueJS核心包,在全局环境中,就有了Vue构造函数,有了构造函数就可以创建实例-->
    //基于引入的核心包,创建实例对象,在对象中写入配置项(el,data)
    const app = new Vue({
        //通过el配置选择器(指定渲染的容器),只当Vue管理的是哪个盒子
        el: "#app",
        //通过data提供数据
        data: {
            msg: "hello!!!",
            count: 666
        }
    })
</script>
</body>
</html>

2.插值表达式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>02.插值表达式</title>
</head>
<body>
<!--插值表达式:Vue的一种模板语法
作用:利用表达式,进行插值渲染
语法:{{表达式}}
使用的注意:
(1)不能在标签属性中使用{{}}
(2)使用的数据要存在
(3)支持的式表达式,不是语句 if for
-->
<div id="app">
   <p>{{nickname}}</p>
    <p>{{nickname.toUpperCase()}}</p>
    <p>{{nickname + ',hello!!!'}}</p>
    <p>{{age>=18?'成年':'未成年'}}</p>
    <p>{{nickname}}的朋友:{{friend.name}}</p>
    <p>{{friend.desc}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            nickname: "cc",
            age:22,
            friend:{
                name:"lucy",
                desc:"热爱学习"
            }
        }
    })
</script>
</body>
</html>

3.Vue响应式特性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>03.Vue的响应式特性</title>
</head>
<body>
<!--数据的响应式处理--》响应式:数据变化,视图自动更新-->
<!--使用Vue开发--》专注于业务核心逻辑即可-->
<div id="app">
    {{msg}}
    {{count}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            // 响应式数据
            msg: "你好,cc",
            count: 100
        }
    })
    //data中的数据,是会被添加到实例上
    //1.访问数据  实例.属性名

    //2.修改数据  实例.属性名=新值
</script>
</body>
</html>

4.Vue指令

4.1 vue-html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.Vue指令</title>
</head>
<body>
<!--指令:带有v-前缀的特殊标签属性,不同属性对应不同的功能-->
<!--vue-html:
作用:设置元素的innerHTML,可以解析标签中的数据;
语法:v-html="表达式"-->
<div id="app">
    <div v-html="msg"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app=new Vue({
        el:"#app",
        data:{
            //注:数据值要用''包裹
            msg:'<a href="https://www.baidu.com/">百度</a>'
            // msg:'<h3>百度</h3>'
        }
    })
</script>
</body>
</html>

4.2 v-show和v-if

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.vue指令(2)</title>
</head>
<body>
<!--vue指令:v-show和v-if-->
<!--(1)v-show
作用:控制元素显示隐藏
语法:v-show="表达式",表达式值true显示,false隐藏
底层原理:切换css的display:none来控制显示隐藏
适用场景:频繁切换需要隐藏的场景-->

<!--(2)v-if
作用:控制元素显示隐藏(条件渲染)
语法:v-if="表达式",表达式值true显示,false隐藏
底层原理:根据判断条件 控制元素的创建和移除(条件渲染)
适用场景:要么显示,要么隐藏,不频繁切换的场景-->
<div id="app">
    <div v-show="flag" class="box">v-show控制</div>
    <div v-if="flag" class="box">v-if控制</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app=new Vue({
    el:"#app",
    data:{
        flag:true
    }
})
</script>
<style>
    .box{
        text-align: center;
        border-radius: 5px;
        box-shadow: 2px 2px 2px #ccc;
    }
</style>
</body>
</html>

4.3 v-else 和 v-else-if

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.Vue指令(3)</title>
</head>
<body>
<!--vue指令:v-else 和 v-else-if-->
<!--作用:辅助v-if进行判断渲染
语法:v-else  v-else-if="表达式"
注意:需要紧挨着v-if一起使用-->

<div id="app">
<p v-if="gender===1">性别:男</p>
<p v-else>性别:女</p>
    <hr>
    <p v-if="score>=90">成绩评定A:奖励电脑一台</p>
    <p v-else-if="score>=80">成绩评定B:奖励周末郊游</p>
    <p v-else-if="score>=70">成绩评定C:奖励零食礼包</p>
    <p v-else>成绩评定D:惩罚一周不能玩手机</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app=new Vue({
        el:"#app",
        data:{
           gender:0,
            score:80
        }
    })
</script>
</body>
</html>

4.4 v-on

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.Vue指令(4)</title>
</head>
<body>
<!--vue指令:v-on
作用:注册事件,注册事件=添加监听+提供处理逻辑
语法:(1)v-on:事件名=“内联语句(可执行语句)"
     (2)v-on:事件名=”methods中的函数名“
 简写: @事件名
-->
<div id="app">
    <!--    (1)v-on:事件名=”内联语句“-->
    <button v-on:click="count--">-1</button>
    <span>{{count}}</span>
    <button v-on:click="count++">+1</button>
    <button v-on:click="count=count+2">+2</button>
    <!--    v-on简写方式:@事件名(将v-on:替换成@)-->
    <button v-on:click="count=count+2">+2</button>
    <hr>
    <!--    (2)v-on:事件名=”methods中的函数名"-->
    <button @click="fn">切换显示隐藏</button>
    <h1 v-show="isShow">hello</h1>
    <hr>
    <!--    v-on调用传参-->
    <button @click="price(-5)">-5</button>
    <span>{{count}}</span>
    <button @click="price(5)">+5</button>
    <hr>
    <div class="box">
        <h1>yueyue自动售货机</h1>
        <button @click="yueyue(5)">可乐5元</button>
        <button @click="yueyue(8)">牛奶10元</button>
        <button @click="yueyue(10)">咖啡10元</button>
    </div>
    <p>余额:{{money}}元</p>
    <hr>
    <div class="box2">
        <button @click="datasum(1,1)">{{param1}}+{{param2}}={{sum}}</button>
    </div>

</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        //提供数据
        data: {
            count: 100,
            isShow: true,
            money:100,
            param1:0,
            param2:0,
            sum:0
        },
        //提供方法
        methods: {
            //让提供的所有methods中的函数,this都指向当前实例
            fn() {
                // console.log("执行了fn",app.isShow)
                console.log(app === this)   //true
                app.isShow = !app.isShow
                this.isShow = !this.isShow
            },
            price(x){
                this.count+=x
                console.log("count=",this.count)
            },
            yueyue(x){
                this.money-=x
                if(this.money<0){
                    alert("余额不足,努力挣钱!!!")
                    return
                }
                console.log("money=",this.money)
            },
            datasum(x,y){
                this.param1=x
                this.param2=y
                this.sum=x+y
                console.log(this.param1,"+",this.param2,"=",this.sum(x,y))
            }
        }

    })
</script>
</body>
</html>

4.5 v-bind

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.Vue指令(5)</title>
</head>
<body>
<!--vue指令:v-bind-->
<!--作用:动态的设置html的标签属性->src  url
语法:v-bind:属性名="表达式"
简写:  :事件

-->
<div id="app">
    <img v-bind:src="imgUrl" v-bind:title="msg" alt=""/>
    <!--    简写-->
    <img :src="imgUrl" :title="msg" alt=""/>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            msg: "波仔",
            imgUrl: "../images/01.imgs/10-02.png"
        },
    })
</script>
</body>
</html>

4.6 v-for

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.vue指令(6)</title>
</head>
<body>
<!--vue指令:v-for-->
<!--作用:基于数据循环,多次渲染整个元素->数组(常用)、对象、数字
语法:v-for="(item,index) in 数组",item每一项,index下标
注意:v-for中使用的是()
-->
<!--v-for="(item,index) in 数组" :key="item.id"
语法:key属性="唯一标识"
作用:给列表项添加的唯一标识,便于vue进行列表项的正确排序复用
注意点:
(1)key的值只能是字符串或数字类型
(2)key的值必须具有唯一性
(3)推荐使用id作为key(唯一),不推荐使用index作为key(会变化,不对应)-->
<div id="app">
    <h3>yueyue水果店</h3>
    <ul>
<!--        v-for中的index是可以省略的,且当只有一个参数时,括号()也是可以省略的-->
        <li v-for="item in list">
            {{item}}
        </li>
        <hr>
        <li v-for="(item,index) in list ">
            {{item}}---{{index}}
        </li>
    </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            list: ['西瓜', '苹果', '香蕉']
        },
        methods: {}

    })
</script>
</body>
</html>

4.7 v-model

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.Vue指令(7)</title>
</head>
<body>
<!--vue指令:v-model-->
<!--作用:给表单元素使用,双向数据绑定->可以快速获取或设置表单元素内容
(1)数据变化->视图自动更新
(2)视图变化->数据自动更新-->
<!--语法:v-model='变量'-->
<div id="app">
    账户:<input type="text" v-model="username"><br><br>
    密码:<input type="password" v-model="password"><br><br>
    <button @click="login">登录</button>
    <button @click="reset">重置</button>
    <hr>
    <hr>

</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            username: '',
            password: ''
        },
        methods: {
            login(username, password) {
                this.username = username,
                    this.password = password
                console.log(this.username, this.password)
            },
            reset() {
                this.username = '',
                this.password = ''
            }
        }
    })
</script>
</body>
</html>

4.8 案例

4.8.1 指令案例(yueyue书屋)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>01.yueyue书屋</title>
</head>
<body>
<!--需求:
(1)列表的渲染
(2)删除功能
-->
<div id="app">
    <h3>yueyue书屋</h3>
    <ul>
        <li v-for="(item,index) in bookList" :key="item.id">
            <span>{{item.name}}</span>
            <span>{{item.author}}</span>
            <button @click="del(item.id)">删除</button>
        </li>
    </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            bookList: [
                {id: 1, name: "《红楼梦》", author: "曹雪芹"},
                {id: 2, name: "《西游记》", author: "吴承恩"},
                {id: 3, name: "《水浒传》", author: "施耐庵"},
                {id: 4, name: "《三国演义》", author: "罗贯中"}
            ]
        },
        methods: {
            del(id) {
                // console.log("删除",id)
                //    通过id进行删除数组中的对应项->filter(不会改变原数组)
                //    filter:根据条件,保留满足条件的对应项,得到一个新数组
                // console.log(this.bookList.filter(item => item.id !== id));
                this.bookList=this.bookList.filter(item => item.id !== id)
            }
        }
    })
</script>
</body>
</html>

4.8.2 指令案例(yueyue记事本)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>02.yueyue记事本</title>
</head>
<body>
<!--需求:
(1)列表渲染
(2)删除功能
(3)添加功能
(4)底部统计和清空
-->
<!--主体区域-->
<section id="app">
<!--    输入框-->
    <header class="header">
        <h1>yueyue记事本</h1>
        <input v-model="todoName" placeholder="请输入任务" class="new-todo"/>
        <button @click="add()" class="add">添加任务</button>
    </header>

    <!--列表区域-->
    <section class="main">
        <ul class="todo-list">
            <li class="todo" v-for="(item,index) in list" :key="item.id">
                <div class="view">
                    <span class="index">{{index + 1}}.</span><label>{{item.name}}</label>
                    <button @click="del(item.id)" class="destroy"></button>
                </div>
            </li>
        </ul>
    </section>

    <!--统计和清空->如果没有任务了,底部隐藏掉(v-show)-->
    <footer class="footer" v-show="list.length>0">
        <!--    统计-->
        <span class="todo-count">合计:<strong>{{list.length}}</strong></span>
        <!--    清空-->
        <button @click="clear" class="clear-completed">
            清空任务
        </button>
    </footer>
</section>
<!--底部-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
//添加功能
//(1)通过v-model绑定输入框-》实时获取表单元素的内容
//(2)点击按钮,进行新增,往数组最前面加(unshift)
    const app = new Vue({
        // el: ".main",   //删除
        el: "#app", //添加
        data: {
            todoName:"",
            list: [
                {id: 1, name: "跑步一公里"},
                {id: 2, name: "跳绳200次"},
                {id: 3, name: "游泳100米"}
            ]
        },
        methods: {
            //删除
            del(id) {
                console.log(id)
                // filter():保留所有不等于该id的项
                this.list = this.list.filter(item => item.id !== id)
            },
            //添加
            add(){
                if(this.todoName.trim()==''){
                    alert("请输入任务名称")
                    return
                }
                this.list.unshift({
                    id: +new Date(),
                    name: this.todoName
                })
                this.todoName=''
            },
            //清空
            clear(){
                this.list=[]
            }
        }
    })
</script>
</body>
</html>

5.指令修改符

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>01.指令修饰符</title>
</head>
<body>
<!--指令修饰符:通过"."指明一些指令后缀,不同后缀封装了不同的操作->简化代码-->
<!--
(1)按键修饰符
    @keyup.enter:键盘回车监听
(2)v-model修饰符
    v-model.trim:去除首尾空格
    v-model.number:转数字
(3)事件修饰符
    @事件名.stop:阻止冒泡
    @事件名.prevent:阻止默认行为
-->

<!--主体区域-->
<section id="app">
<!--    输入框-->
    <header class="header">
        <h1>yueyue记事本</h1>
<!--        使用指令修饰符-->
        <input @keyup.enter="add" v-model="todoName" placeholder="请输入任务" class="text">
        <button @click="add" class="add">添加任务</button>
    </header>
<!--    列表区域-->
    <section class="main">
        <ul class="todo-list">
            <li class="todo" v-for="(item,index) in list" :key="item.id">
                <div class="view">
                    <span class="index">{{index + 1}}.</span><label>{{item.name}}</label>
                    <button @click="del(item.id)" class="destroy"></button>
                </div>
            </li>
        </ul>
    </section>
    <!--统计和清空->如果没有任务了,底部隐藏掉(v-show)-->
    <footer class="footer" v-show="list.length>0">
        <!--    统计-->
        <span class="todo-count">合计:<strong>{{list.length}}</strong></span>
        <!--    清空-->
        <button @click="clear" class="clear-completed">
            清空任务
        </button>
    </footer>
</section>
<!--底部-->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    //添加功能
    //(1)通过v-model绑定输入框-》实时获取表单元素的内容
    //(2)点击按钮,进行新增,往数组最前面加(unshift)
    const app = new Vue({
        // el: ".main",   //删除
        el: "#app", //添加
        data: {
            todoName:"",
            list: [
                {id: 1, name: "跑步一公里"},
                {id: 2, name: "跳绳200次"},
                {id: 3, name: "游泳100米"}
            ]
        },
        methods: {
            //删除
            del(id) {
                console.log(id)
                // filter():保留所有不等于该id的项
                this.list = this.list.filter(item => item.id !== id)
            },
            //添加
            add(){
                if(this.todoName.trim()==''){
                    alert("请输入任务名称")
                    return
                }
                this.list.unshift({
                    id: +new Date(),
                    name: this.todoName
                })
                this.todoName=''
            },
            //清空
            clear(){
                this.list=[]
            }
        }
    })
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>02.指令修饰符(1)</title>
</head>
<body>
<div id="app">
    <h3>@keyup.enter->监听键盘回车事件</h3>
    <input @keyup.enter="fn" v-model="username" type="text">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            username: ''
        },
        methods: {
            fn(e) {
                //     if(e.key==='Enter'){
                console.log('键盘回车时触发', this.username)
            }
            // }
        }
    })
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>02.指令修饰符(2)</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: pink;
            margin-top: 20px;
        }
        .son {
            width: 100px;
            height: 100px;
            background-color: skyblue;
        }
    </style>
</head>
<body>
<div id="app">
    <h3>v-model修饰符 .trim // .number</h3>
    姓名:<input v-model.trim="username" type="text"><br>
    年纪:<input v-model.number="age" type="text"><br>


    <h3>@事件名.stop     →  阻止冒泡</h3>
    <div @click="fatherFn" class="father">
        <div @click.stop="sonFn" class="son">son</div>
    </div>

    <h3>@事件名.prevent  →  阻止默认行为</h3>
    <a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            username: '',
            age: '',
        },
        methods: {
            fatherFn () {
                alert('father被点击了')
            },
            sonFn (e) {
                // e.stopPropagation()
                alert('son被点击了')
            }
        }
    })
</script>
</body>
</html>

6.V-bind增强

6.1 v-bind对于样式控制

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>03.v-bind对于样式控制的增强</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            border: 3px solid #000;
            font-size: 30px;
            margin-top: 10px;
        }
        .pink {
            background-color: pink;
        }
        .big {
            width: 300px;
            height: 300px;
        }
    </style>
</head>
<body>
<!--v-bind对于样式控制的增强-->
<!--为了方便开发者进行样式控制,Vue拓展了v-bind的语法,可以针对class类名和style行内样式进行控制-->
<!--v-bind对于样式控制的增强--操作class -->
<!--语法:class="对象/数组"
(1)对象->键就是类名,值是布尔值,如果值为true,有这个类,否则没有这个类
<div class="box" :class="{类名1:布尔值,类名2:布尔值}"></div>
(2)数组->数组中所有的类,都会添加到盒子上,本质就是一个class列表
<div class="box" :class="[类名1,类名2,类名3]"></div>
-->

<div id="app">
    <div class="box" :class="{ pink: true, big: true }">黑马程序员</div>
    <div class="box" :class="['pink', 'big']">黑马程序员</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {

        }
    })
</script>
</body>
</html>

6.2 v-bind对于样式控制增强

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>03.v-bind对于样式控制的增强-操作style</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: rgb(187, 150, 156);
        }

         .progress {
             height: 25px;
             width: 400px;
             border-radius: 15px;
             background-color: #272425;
             border: 3px solid #272425;
             box-sizing: border-box;
             margin-bottom: 30px;
         }
        .inner {
            width: 50%;
            height: 20px;
            border-radius: 10px;
            text-align: right;
            position: relative;
            background-color: #409eff;
            background-size: 20px 20px;
            box-sizing: border-box;
            transition: all 1s;
        }
        .inner span {
            position: absolute;
            right: -20px;
            bottom: -25px;
        }
    </style>
</head>
<body>
<!--语法::style="样式对象"
<div class="box" :style="{CSS属性名1:CSS属性值,CSS属性名2:CSS属性值}"></div>
适用场景:某个具体属性的动态设置
-->
<div id="app2">
    <div class="box" :style="{ width: '400px', height: '400px', backgroundColor: 'green' }"></div>
</div>

<div id="app">
    <!-- 外层盒子底色 (黑色) -->
    <div class="progress">
        <!-- 内层盒子 - 进度(蓝色) -->
        <div class="inner" :style="{ width: percent + '%' }">
            <span>{{ percent }}%</span>
        </div>
    </div>
    <button @click="percent = 25">设置25%</button>
    <button @click="percent = 50">设置50%</button>
    <button @click="percent = 75">设置75%</button>
    <button @click="percent = 100">设置100%</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            percent: 30
        }
    })
</script>
</body>
</html>

6.3 案例

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>03.案例—京东秒杀tab导航高亮</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        ul {
            display: flex;
            border-bottom: 2px solid #e01222;
            padding: 0 10px;
        }
        li {
            width: 100px;
            height: 50px;
            line-height: 50px;
            list-style: none;
            text-align: center;
        }
        li a {
            display: block;
            text-decoration: none;
            font-weight: bold;
            color: #333333;
        }
        li a.active {
            background-color: #e01222;
            color: #fff;
        }

    </style>
</head>
<body>
<!--核心思路:
(1)基于数据动态渲染tab,->v-for
(2)准备下标高亮的是哪一个tab->activeIndex
(3)基于下标,动态控制class类名 v-bind:class
-->
<div id="app">
    <ul>
<!--        所谓高亮,其实就是修改下标-->
        <li v-for="(item, index) in list" :key="item.id" @click="activeIndex = index">
            <a :class="{ active: index === activeIndex }" href="#">{{ item.name }}</a>
        </li>
    </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            activeIndex: 2, // 记录高亮
            list: [
                { id: 1, name: '京东秒杀' },
                { id: 2, name: '每日特价' },
                { id: 3, name: '品类秒杀' }
            ]
        }
    })
</script>
</body>
</html>

7. v-model应用于其他表单

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.v-model应用于其他表单元素</title>
</head>
<body>
<!--常见的表单元素都可以用v-model绑定关联->快速获取或设置表单元素的值
它会根据控件类型自动选取正确的方法来更新元素-->
<div id="app">
  <h1>yueyue学习网</h1>
  姓名:<input type="text" v-model="username">
  <br><br>

  是否单身:<input type="radio"><input type="radio"><br><br>

<!--  所在城市:-->
  所在城市:
  <select>
    <option>北京</option>
    <option>上海</option>
    <option>河南省</option>
  </select>
    <!--
     前置理解:
       1. name:  给单选框加上 name 属性 可以分组 → 同一组互相会互斥
       2. value: 给单选框加上 value 属性,用于提交给后台的数据
     结合 Vue 使用 → v-model
   -->
    性别:
    <input v-model="gender" type="radio" name="gender" value="1"><input v-model="gender" type="radio" name="gender" value="2"><br><br>

    <!--
      前置理解:
        1. option 需要设置 value 值,提交给后台
        2. select 的 value 值,关联了选中的 option 的 value 值
      结合 Vue 使用 → v-model
    -->
    所在城市:
    <select v-model="cityId">
        <option value="101">北京</option>
        <option value="102">上海</option>
        <option value="103">成都</option>
        <option value="104">南京</option>
    </select>
    <br><br>

    自我描述:
    <textarea v-model="desc"></textarea>

    <button>立即注册</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            username: '',
            isSingle: false,
            gender: "2",
            cityId: '102',
            desc: ""
        }
    })
</script>
</div>
</body>
</html>

8.计算属性

8.1 计算属性的默认用法

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>05.计算属性</title>
    <style>
        table {
            border: 1px solid #000;
            text-align: center;
            width: 240px;
        }
        th,td {
            border: 1px solid #000;
        }
        h3 {
            position: relative;
        }
    </style>
</head>
<body>
<!--计算属性:基于现有的数据,计算出来的新属性,依赖的数据变化,自动重新计算
计算属性->可以将一段求值的代码进行封装-->
<!--语法:
(1)声明再computed配置项中,一个计算属性对应一个函数
(2)使用起来和普通属性一样使用{{计算属性名}}-->
<div id="app">
    <h3>小黑的礼物清单</h3>
    <table>
        <tr>
            <th>名字</th>
            <th>数量</th>
        </tr>
        <tr v-for="(item, index) in list" :key="item.id">
            <td>{{ item.name }}</td>
            <td>{{ item.num }}个</td>
        </tr>
    </table>

    <!-- 目标:统计求和,求得礼物总数 -->
    <p>礼物总数:{{ totalCount }} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            // 现有的数据
            list: [
                { id: 1, name: '篮球', num: 1 },
                { id: 2, name: '玩具', num: 2 },
                { id: 3, name: '铅笔', num: 5 },
            ]
        },
        computed: {
            totalCount () {
                // 基于现有的数据,编写求值逻辑
                // 计算属性函数内部,可以直接通过 this 访问到 app 实例
                // console.log(this.list)

                // 需求:对 this.list 数组里面的 num 进行求和 → reduce
                let total = this.list.reduce((sum, item) => sum + item.num, 0)
                return total
            }
        }
    })
</script>
</body>
</html>

8.2.computed和methods

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>06.computed计算属性和methods方法</title>
    <style>
        table {
            border: 1px solid #000;
            text-align: center;
            width: 300px;
        }
        th,td {
            border: 1px solid #000;
        }
        h3 {
            position: relative;
        }
        span {
            position: absolute;
            left: 145px;
            top: -4px;
            width: 16px;
            height: 16px;
            color: white;
            font-size: 12px;
            text-align: center;
            border-radius: 50%;
            background-color: #e63f32;
        }
    </style>
</head>
<body>
<!--
作用方面:
(1)计算属性:封装了一段对于数据的处理,求得一个结果
(2)methods:给实例提供一个方法,调用以处理业务逻辑
语法方面:
(1)计算属性
    1)写在computed配置项中
    2)作为属性,直接使用->this.计算属性{{计算属性}}
(2)methods
    1)写在methods配置项中;
    2)作为方法,需要调用->this.方法名()  {{方法名()}}   @事件名=“方法名”

使用computed计算属性值的优势:缓存特性(提升性能)
计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算->并再次缓存
-->
<div id="app">
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <table>
        <tr>
            <th>名字</th>
            <th>数量</th>
        </tr>
        <tr v-for="(item, index) in list" :key="item.id">
            <td>{{ item.name }}</td>
            <td>{{ item.num }}个</td>
        </tr>
    </table>

    <p>礼物总数:{{ totalCountFn() }} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            // 现有的数据
            list: [
                { id: 1, name: '篮球', num: 3 },
                { id: 2, name: '玩具', num: 2 },
                { id: 3, name: '铅笔', num: 5 },
            ]
        },

        methods: {
            totalCountFn () {
                console.log('methods方法执行了')
                let total = this.list.reduce((sum, item) => sum + item.num, 0)
                return total
            }
        },

        computed: {
            // 计算属性:有缓存的,一旦计算出来结果,就会立刻缓存
            // 下一次读取 → 直接读缓存就行 → 性能特别高
            // totalCount () {
            //   console.log('计算属性执行了')
            //   let total = this.list.reduce((sum, item) => sum + item.num, 0)
            //   return total
            // }
        }
    })
</script>
</body>
</html>

8.3 计算属性的完整写法

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>07.计算属性完整写法</title>
    <style>
        input {
            width: 30px;
        }
    </style>
</head>
<body>
<!--计算属性完整写法
计算属性默认的简写:只能读取访问,不能“修改”
如果需要“修改”->需要写计算属性的完整写法

语法(默认写法):
computed:{
  计算属性名(){
     一段代码逻辑(计算逻辑)
     return 结果
  }
}

语法(完整写法):
computed:{
计算属性名:{
   get(){
      一段代码逻辑(计算逻辑)
      return 结果
     },
   set(修改的值){
      一段代码逻辑(修改逻辑)
     }
   }
}
-->
<div id="app">
    姓:<input type="text" v-model="firstName"> +
    名:<input type="text" v-model="lastName"> =
    <span>{{ fullName }}</span><br><br>

    <button @click="changeName">改名卡</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            firstName: '刘',
            lastName: '备',
        },
        methods: {
            changeName () {
                this.fullName = '黄忠'
            }
        },
        computed: {
            // 简写 → 获取,没有配置设置的逻辑
            // fullName () {
            //   return this.firstName + this.lastName
            // }

            // 完整写法 → 获取 + 设置
            fullName: {
                // (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)
                //     会将返回值作为,求值的结果
                get () {
                    return this.firstName + this.lastName
                },
                // (2) 当fullName计算属性,被修改赋值时,执行set
                //     修改的值,传递给set方法的形参
                set (value) {
                    // console.log(value.slice(0, 1))
                    // console.log(value.slice(1))
                    this.firstName = value.slice(0, 1)
                    this.lastName = value.slice(1)
                }
            }
        }
    })
</script>
</body>
</html>

9.watch监听器

watch是一种用于监视数据变化并执行相应操作的机制,它通常用于Vue.js框架中,可以监视vue实例中的数据变化,并在数据变化时,执行相应的回调函数或触发其他操作;
在Vue.js中,可以通过在Vue实例的选项中定义一个watch对象来使用watch监视,watch对象的属性是要监视的数据属性,值是一个回调函数,用于在数据变化时执行相应的操作。

9.1 watch监听器(监视器)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>08.watch侦听器(监视器)</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-size: 18px;
        }
        #app {
            padding: 10px 20px;
        }
        .query {
            margin: 10px 0;
        }
        .box {
            display: flex;
        }
        textarea {
            width: 300px;
            height: 160px;
            font-size: 18px;
            border: 1px solid #dedede;
            outline: none;
            resize: none;
            padding: 10px;
        }
        textarea:hover {
            border: 1px solid #1589f5;
        }
        .transbox {
            width: 300px;
            height: 160px;
            background-color: #f0f0f0;
            padding: 10px;
            border: none;
        }
        .tip-box {
            width: 300px;
            height: 25px;
            line-height: 25px;
            display: flex;
        }
        .tip-box span {
            flex: 1;
            text-align: center;
        }
        .query span {
            font-size: 18px;
        }

        .input-wrap {
            position: relative;
        }
        .input-wrap span {
            position: absolute;
            right: 15px;
            bottom: 15px;
            font-size: 12px;
        }
        .input-wrap i {
            font-size: 20px;
            font-style: normal;
        }
    </style>
</head>
<body>
<!--watch侦听器(监视器)
作用:监视数据变化,执行一些业务逻辑或异步操作
语法:
(1)简单写法->简单类型数据,直接监视
data:{
  word:'苹果',
  obj:{
     words:'苹果'
  }
},
watch:{
  //该方法会在数据变化时,触发执行
  数据属性名(newValue,oldValue){
    一些业务逻辑 或 异步操作
  },
  对象.属性名(newValue,oldValue){
    一些业务逻辑  或 异步操作
  }
}
(2)完整写法->添加额外配置项

-->

<div id="app">
    <!-- 条件选择框 -->
    <div class="query">
        <span>翻译成的语言:</span>
        <select>
            <option value="italy">意大利</option>
            <option value="english">英语</option>
            <option value="german">德语</option>
        </select>
    </div>

    <!-- 翻译框 -->
    <div class="box">
        <div class="input-wrap">
            <textarea v-model="obj.words"></textarea>
            <span><i>⌨️</i>文档翻译</span>
        </div>
        <div class="output-wrap">
            <div class="transbox">mela</div>
        </div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    // 接口地址:https://applet-base-api-t.itheima.net/api/translate
    // 请求方式:get
    // 请求参数:
    // (1)words:需要被翻译的文本(必传)
    // (2)lang: 需要被翻译成的语言(可选)默认值-意大利
    // -----------------------------------------------

    const app = new Vue({
        el: '#app',
        data: {
            // words: ''
            obj: {
                words: ''
            }
        },
        // 具体讲解:(1) watch语法 (2) 具体业务实现
        watch: {
            // 该方法会在数据变化时调用执行
            // newValue新值, oldValue老值(一般不用)
            // words (newValue) {
            //   console.log('变化了', newValue)
            // }
//用于监视obj中的words数据值的变化,如果变化了就执行console.log(...)方法,注意此处属性名监视属性要使用单引号''包裹,具体是否使用''或反引号``包裹看下面图片内容;
            'obj.words' (newValue) {
                console.log('变化了', newValue)
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

9.2 watch监听器(简写)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>08.watch简写-业务实现</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-size: 18px;
        }
        #app {
            padding: 10px 20px;
        }
        .query {
            margin: 10px 0;
        }
        .box {
            display: flex;
        }
        textarea {
            width: 300px;
            height: 160px;
            font-size: 18px;
            border: 1px solid #dedede;
            outline: none;
            resize: none;
            padding: 10px;
        }
        textarea:hover {
            border: 1px solid #1589f5;
        }
        .transbox {
            width: 300px;
            height: 160px;
            background-color: #f0f0f0;
            padding: 10px;
            border: none;
        }
        .tip-box {
            width: 300px;
            height: 25px;
            line-height: 25px;
            display: flex;
        }
        .tip-box span {
            flex: 1;
            text-align: center;
        }
        .query span {
            font-size: 18px;
        }

        .input-wrap {
            position: relative;
        }
        .input-wrap span {
            position: absolute;
            right: 15px;
            bottom: 15px;
            font-size: 12px;
        }
        .input-wrap i {
            font-size: 20px;
            font-style: normal;
        }
    </style>
</head>
<body>
<div id="app">
    <!-- 条件选择框 -->
    <div class="query">
        <span>翻译成的语言:</span>
        <select>
            <option value="italy">意大利</option>
            <option value="english">英语</option>
            <option value="german">德语</option>
        </select>
    </div>

    <!-- 翻译框 -->
    <div class="box">
        <div class="input-wrap">
            <textarea v-model="obj.words"></textarea>
            <span><i>⌨️</i>文档翻译</span>
        </div>
        <div class="output-wrap">
            <div class="transbox">{{ result }}</div>
        </div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    // 接口地址:https://applet-base-api-t.itheima.net/api/translate
    // 请求方式:get
    // 请求参数:
    // (1)words:需要被翻译的文本(必传)
    // (2)lang: 需要被翻译成的语言(可选)默认值-意大利
    // -----------------------------------------------

    const app = new Vue({
        el: '#app',
        data: {
            // words: ''
            obj: {
                words: ''
            },
            result: '', // 翻译结果
            // timer: null // 延时器id
        },
        // 具体讲解:(1) watch语法 (2) 具体业务实现
        watch: {
            // 该方法会在数据变化时调用执行
            // newValue新值, oldValue老值(一般不用)
            // words (newValue) {
            //   console.log('变化了', newValue)
            // }

            'obj.words' (newValue) {
                // console.log('变化了', newValue)
                // 防抖: 延迟执行 → 干啥事先等一等,延迟一会,一段时间内没有再次触发,才执行
                clearTimeout(this.timer)
                this.timer = setTimeout(async () => {
                    const res = await axios({
                        url: 'https://applet-base-api-t.itheima.net/api/translate',
                        params: {
                            words: newValue
                        }
                    })
                    this.result = res.data.data
                    console.log(res.data.data)
                }, 300)
            }
        }
    })
</script>
</body>
</html>

9.3 watch监听器(完整写法)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>08.watch侦听器完整写法</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-size: 18px;
        }
        #app {
            padding: 10px 20px;
        }
        .query {
            margin: 10px 0;
        }
        .box {
            display: flex;
        }
        textarea {
            width: 300px;
            height: 160px;
            font-size: 18px;
            border: 1px solid #dedede;
            outline: none;
            resize: none;
            padding: 10px;
        }
        textarea:hover {
            border: 1px solid #1589f5;
        }
        .transbox {
            width: 300px;
            height: 160px;
            background-color: #f0f0f0;
            padding: 10px;
            border: none;
        }
        .tip-box {
            width: 300px;
            height: 25px;
            line-height: 25px;
            display: flex;
        }
        .tip-box span {
            flex: 1;
            text-align: center;
        }
        .query span {
            font-size: 18px;
        }

        .input-wrap {
            position: relative;
        }
        .input-wrap span {
            position: absolute;
            right: 15px;
            bottom: 15px;
            font-size: 12px;
        }
        .input-wrap i {
            font-size: 20px;
            font-style: normal;
        }
    </style>
</head>
<body>
<!--watch侦听器完整写法->添加额外配置项
(1)deep:true对复杂类型深度监视
(2)immediate:true初始化立刻执行一次handler方法
-->
<!--
data:{
 obj:{
   words:'苹果',
   lang:'italy'
 },
},
watch:{  //watch完整写法
 数据属性名:{
   deep:true,//深度监视
   handler(newValue){
     console.log(newValue)
   }
 }
}
-->
<div id="app">
    <!-- 条件选择框 -->
    <div class="query">
        <span>翻译成的语言:</span>
        <select v-model="obj.lang">
            <option value="italy">意大利</option>
            <option value="english">英语</option>
            <option value="german">德语</option>
        </select>
    </div>

    <!-- 翻译框 -->
    <div class="box">
        <div class="input-wrap">
            <textarea v-model="obj.words"></textarea>
            <span><i>⌨️</i>文档翻译</span>
        </div>
        <div class="output-wrap">
            <div class="transbox">{{ result }}</div>
        </div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    // 需求:输入内容,修改语言,都实时翻译

    // 接口地址:https://applet-base-api-t.itheima.net/api/translate
    // 请求方式:get
    // 请求参数:
    // (1)words:需要被翻译的文本(必传)
    // (2)lang: 需要被翻译成的语言(可选)默认值-意大利
    // -----------------------------------------------

    const app = new Vue({
        el: '#app',
        data: {
            obj: {
                words: '小黑',
                lang: 'italy'
            },
            result: '', // 翻译结果
        },
        watch: {
            obj: {
                deep: true, // 深度监视(对象中的数据可以全部进行监视)
                immediate: true, // 立刻执行,一进入页面handler就立刻执行一次
                handler (newValue) {
                    clearTimeout(this.timer)
                    this.timer = setTimeout(async () => {
                        const res = await axios({
                            url: 'https://applet-base-api-t.itheima.net/api/translate',
                            params: newValue
                        })
                        this.result = res.data.data
                        console.log(res.data.data)
                    }, 300)
                }
            }


            // 'obj.words' (newValue) {
            //   clearTimeout(this.timer)
            //   this.timer = setTimeout(async () => {
            //     const res = await axios({
            //       url: 'https://applet-base-api-t.itheima.net/api/translate',
            //       params: {
            //         words: newValue
            //       }
            //     })
            //     this.result = res.data.data
            //     console.log(res.data.data)
            //   }, 300)
            // }
        }
    })
</script>
</body>
</html>

10. Vue的生命周期

10.1 vue的四个生命周期

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>01.Vue生命周期(4个阶段)</title>
</head>
<body>
<!--vue生命周期:一个Vue实例从创建到销毁的整个过程;
生命周期的四个阶段:(1)创建;(2)挂载;(3)更新;(4)销毁
(1)创建阶段:new VUe(),初始化操作,将普通数据转化为响应式数据;
(2)挂载阶段:渲染模板,将数据放到标签模板中;
(3)更新阶段:数据修改,更新视图(用户使用);
(4)销毁阶段:浏览器关闭;-->
<!--1)发初始化渲染请求要在创建阶段的最后,响应式数据完成之后才可以;
    2)操作dom:挂载阶段结束之后,模板数据渲染后;-->


<!--Vue生命周期函数(钩子函数)-->
<!--Vue生命周期过程中,会自动运行一些函数,被称为【生命周期钩子】,让开发者可以在【特定阶段】运行自己的代码。-->
<!--在生命周期4个阶段中会vue提供8个函数(钩子函数),分别位于每个阶段的前和后,每个钩子函数中可以写自己要执行的逻辑代码,这样可以实现开发者在特定阶段执行想要执行的代码-->
<!--钩子函数:就是承载vue生命周期中每个阶段要执行的代码,例如:在创建阶段后的钩子函数中可以写发送初始化请求的代码、在挂载阶段后可以写操作dom的代码-->
<!--钩子函数(8个):
(1)before Create:
(2)created(常用):发送初始化,渲染请求;
(3)before Mount
(4)mounted(常用):操作dom
(5)before Update
(6)updated
(7)before Destroy:释放Vue以外的资源(清除定时器,延时器...(会配合组件使用))
(8)destroyed:销毁Vue实例
-->

<div id="app">
    <h3>{{title}}</h3>
    <div>
        <button @click="count--">-</button>
        <span>{{count}}</span>
        <button @click="count++">+</button>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    const app = new Vue({
        el: "#app",
        data: {
            count: 100,
            title: '计数器'
        },
        //    1.创建阶段(准备阶段)
        beforeCreate() {
            console.log("beforeCreate 响应式数据准备好之前")
        },
        created() {
            console.log("created 响应式数据准备好之后",this.count)

            //this.数据名=请求回来的数据
        //    可以开始发送初始化渲染的请求
        },
        //    2.挂载阶段(渲染阶段)
        beforeMount() {
            console.log("beforeMount 模板渲染之前",document.querySelector('h3').innerHTML)
        },
        mounted() {
            console.log("mounted 模板渲染之后",document.querySelector('h3').innerHTML)
        },
        //    3.更新阶段
        beforeUpdate(){
            console.log("beforeUpdate 更新阶段之前(数据修改了,但视图还没更新)",document.querySelector('h3').innerHTML)
        },
        updated(){
            console.log("updated 更新之后(数据修改了,视图也更新了)",document.querySelector('h3').innerHTML)
        },

        //    4.销毁阶段
        beforeDestory() {
            console.log("beforeDestory (卸载vue实例)")
        },
        destoryed() {
            console.log("destoryed 销毁之后")
        }
    })
</script>
</body>
</html>

10.2 vue的生命周期—created

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>02.created应用</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            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>
    <!--    created:响应式数据准备好了,可以开始发送初始化渲染请求-->
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(item,index) in list" :key="item.id" class="news">
            <div class="left">
                <div class="title">{{item.title}}</div>
                <div class="info">
                    <span>{{item.source}}</span>
                    <span>{{item.time}}</span>
                </div>
            </div>
            <div class="right">
                <img src="item.img" alt="">
            </div>
        </li>
    </ul>
</div>
<script src="./tools/vue.js"></script>
<script src="./tools/axios.js"></script>
<script>
    // 接口地址:http://hmajax.itheima.net/api/news
    // 请求方式:get
    const app = new Vue({
        el: '#app',
        data: {
            list: []
        },
        async created(){
            //1.发送请求,获取数据
            const res=await  axios.get('http://hmajax.itheima.net/api/news')
            //2.将数据更新给data中的list
            this.list=res.data.data
            // console.log(res)
        }
    })
</script>
</body>
</html>

10.3 vue的生命周期—mounted

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>03.mounted应用</title>
    <!--    mounted:模板渲染完成,可以开始操作DOM-->
    <!--    在mounted完成之后,就代表着dom模板已经渲染完成了,才可以操作dom-->
    <!-- 初始化样式 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
    <!-- 核心样式 -->
    <style>
        html,
        body {
            height: 100%;
        }

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

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

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

        .search-container .search-box input {
            width: 512px;
            height: 16px;
            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;
            box-sizing: content-box;
            -webkit-tap-highlight-color: transparent;
        }

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

        body {
            background: no-repeat center /cover;
            background-color: #edf0f5;
        }
    </style>
</head>
<body>
<div class="container" id="app">
    <div class="search-container">
        <img src="https://www.itheima.com/images/logo.png" alt="">
        <div class="search-box">
            <input type="text" v-model="words" id="inp">
            <button>搜索一下</button>
        </div>
    </div>
</div>

<script src="./vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            words: ''
        },
        //    核心思路:
        //    1.等输入框渲染出来

        //    2.让输入框获取焦点
        mounted() {
            console.log(document.querySelector('#inp'));
            document.querySelector('#inp').focus()
        }
    })
    </script>
</body>
</html>

10.4 案例-yueyue记账清单

10.4.1 需求介绍

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.yueyue记账清单</title>
<!--    yueyue记账清单
   功能需求:
    (1)基本渲染;
    (2)添加功能;
    (3)删除功能;
    (4)饼图渲染;
    -->
    <!-- CSS only -->
    <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
    />
    <style>
        .red {
            color: red!important;
        }
        .search {
            width: 300px;
            margin: 20px 0;
        }
        .my-form {
            display: flex;
            margin: 20px 0;
        }
        .my-form input {
            flex: 1;
            margin-right: 20px;
        }
        .table > :not(:first-child) {
            border-top: none;
        }
        .contain {
            display: flex;
            padding: 10px;
        }
        .list-box {
            flex: 1;
            padding: 0 30px;
        }
        .list-box  a {
            text-decoration: none;
        }
        .echarts-box {
            width: 600px;
            height: 400px;
            padding: 30px;
            margin: 0 auto;
            border: 1px solid #ccc;
        }
        tfoot {
            font-weight: bold;
        }
        @media screen and (max-width: 1000px) {
            .contain {
                flex-wrap: wrap;
            }
            .list-box {
                width: 100%;
            }
            .echarts-box {
                margin-top: 30px;
            }
        }
    </style>
</head>
<body>
<div id="app">
    <div class="contain">
        <!-- 左侧列表 -->
        <div class="list-box">

            <!-- 添加资产 -->
            <form class="my-form">
                <input type="text" class="form-control" placeholder="消费名称" />
                <input type="text" class="form-control" placeholder="消费价格" />
                <button type="button" class="btn btn-primary">添加账单</button>
            </form>

            <table class="table table-hover">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>消费名称</th>
                    <th>消费价格</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td>1</td>
                    <td>帽子</td>
                    <td>99.00</td>
                    <td><a href="javascript:;">删除</a></td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>大衣</td>
                    <td class="red">199.00</td>
                    <td><a href="javascript:;">删除</a></td>
                </tr>
                </tbody>
                <tfoot>
                <tr>
                    <td colspan="4">消费总计: 298.00</td>
                </tr>
                </tfoot>
            </table>
        </div>

        <!-- 右侧图表 -->
        <div class="echarts-box" id="main"></div>
    </div>
</div>
<script src="../echarts.min.js"></script>
<script src="../vue.js"></script>
<script src="../axios.js"></script>
<script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     *
     * 功能需求:
     * 1. 基本渲染
     * 2. 添加功能
     * 3. 删除功能
     * 4. 饼图渲染
     */
    const app = new Vue({
        el: '#app',
        data: {

        },
    })
</script>
</body>
</html>

10.4.2 删除功能

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.yueyue记账清单-删除功能</title>
    <!-- CSS only -->
    <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
    />
    <style>
        .red {
            color: red!important;
        }
        .search {
            width: 300px;
            margin: 20px 0;
        }
        .my-form {
            display: flex;
            margin: 20px 0;
        }
        .my-form input {
            flex: 1;
            margin-right: 20px;
        }
        .table > :not(:first-child) {
            border-top: none;
        }
        .contain {
            display: flex;
            padding: 10px;
        }
        .list-box {
            flex: 1;
            padding: 0 30px;
        }
        .list-box  a {
            text-decoration: none;
        }
        .echarts-box {
            width: 600px;
            height: 400px;
            padding: 30px;
            margin: 0 auto;
            border: 1px solid #ccc;
        }
        tfoot {
            font-weight: bold;
        }
        @media screen and (max-width: 1000px) {
            .contain {
                flex-wrap: wrap;
            }
            .list-box {
                width: 100%;
            }
            .echarts-box {
                margin-top: 30px;
            }
        }
    </style>
</head>
<body>
<div id="app">
    <div class="contain">
        <!-- 左侧列表 -->
        <div class="list-box">

            <!-- 添加资产 -->
            <form class="my-form">
                <input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
                <input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
                <button @click="add" type="button" class="btn btn-primary">添加账单</button>
            </form>

            <table class="table table-hover">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>消费名称</th>
                    <th>消费价格</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                <tr v-for="(item, index) in list" :key="item.id">
                    <td>{{ index + 1 }}</td>
                    <td>{{ item.name }}</td>
                    <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
                    <td><a @click="del(item.id)" href="javascript:;">删除</a></td>
                </tr>
                </tbody>
                <tfoot>
                <tr>
                    <td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
                </tr>
                </tfoot>
            </table>
        </div>

        <!-- 右侧图表 -->
        <div class="echarts-box" id="main"></div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     *
     * 功能需求:
     * 1. 基本渲染
     *    (1) 立刻发送请求获取数据 created
     *    (2) 拿到数据,存到data的响应式数据中
     *    (3) 结合数据,进行渲染 v-for
     *    (4) 消费统计 => 计算属性
     * 2. 添加功能
     *    (1) 收集表单数据 v-model
     *    (2) 给添加按钮注册点击事件,发送添加请求
     *    (3) 需要重新渲染
     * 3. 删除功能
     *    (1) 注册点击事件,传参传 id
     *    (2) 根据 id 发送删除请求
     *    (3) 需要重新渲染
     * 4. 饼图渲染
     */
    const app = new Vue({
        el: '#app',
        data: {
            list: [],
            name: '',
            price: ''
        },
        computed: {
            totalPrice () {
                return this.list.reduce((sum, item) => sum + item.price, 0)
            }
        },
        created () {
            // const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
            //   params: {
            //     creator: '小黑'
            //   }
            // })
            // this.list = res.data.data

            this.getList()
        },
        methods: {
            async getList () {
                const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
                    params: {
                        creator: '小黑'
                    }
                })
                this.list = res.data.data
            },
            async add () {
                if (!this.name) {
                    alert('请输入消费名称')
                    return
                }
                if (typeof this.price !== 'number') {
                    alert('请输入正确的消费价格')
                    return
                }

                // 发送添加请求
                const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
                    creator: '小黑',
                    name: this.name,
                    price: this.price
                })
                // 重新渲染一次
                this.getList()

                this.name = ''
                this.price = ''
            },
            async del (id) {
                // 根据 id 发送删除请求
                const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
                // 重新渲染
                this.getList()
            }
        }
    })
</script>
</body>
</html>

10.4.3 基本渲染

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>yueyue记账清单-基本渲染</title>
    <!-- CSS only -->
    <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
    />
    <style>
        .red {
            color: red!important;
        }
        .search {
            width: 300px;
            margin: 20px 0;
        }
        .my-form {
            display: flex;
            margin: 20px 0;
        }
        .my-form input {
            flex: 1;
            margin-right: 20px;
        }
        .table > :not(:first-child) {
            border-top: none;
        }
        .contain {
            display: flex;
            padding: 10px;
        }
        .list-box {
            flex: 1;
            padding: 0 30px;
        }
        .list-box  a {
            text-decoration: none;
        }
        .echarts-box {
            width: 600px;
            height: 400px;
            padding: 30px;
            margin: 0 auto;
            border: 1px solid #ccc;
        }
        tfoot {
            font-weight: bold;
        }
        @media screen and (max-width: 1000px) {
            .contain {
                flex-wrap: wrap;
            }
            .list-box {
                width: 100%;
            }
            .echarts-box {
                margin-top: 30px;
            }
        }
    </style>
</head>
<body>
<div id="app">
    <div class="contain">
        <!-- 左侧列表 -->
        <div class="list-box">

            <!-- 添加资产 -->
            <form class="my-form">
                <input type="text" class="form-control" placeholder="消费名称" />
                <input type="text" class="form-control" placeholder="消费价格" />
                <button type="button" class="btn btn-primary">添加账单</button>
            </form>

            <table class="table table-hover">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>消费名称</th>
                    <th>消费价格</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                <tr v-for="(item, index) in list" :key="item.id">
                    <td>{{ index + 1 }}</td>
                    <td>{{ item.name }}</td>
                    <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
                    <td><a href="javascript:;">删除</a></td>
                </tr>
                </tbody>
                <tfoot>
                <tr>
                    <td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
                </tr>
                </tfoot>
            </table>
        </div>

        <!-- 右侧图表 -->
        <div class="echarts-box" id="main"></div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     *
     * 功能需求:
     * 1. 基本渲染
     *    (1) 立刻发送请求获取数据 created
     *    (2) 拿到数据,存到data的响应式数据中
     *    (3) 结合数据,进行渲染 v-for
     *    (4) 消费统计 => 计算属性
     * 2. 添加功能
     * 3. 删除功能
     * 4. 饼图渲染
     */
    const app = new Vue({
        el: '#app',
        data: {
            list: []
        },
        computed: {
            totalPrice () {
                return this.list.reduce((sum, item) => sum + item.price, 0)
            }
        },
        async created () {
            const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
                params: {
                    creator: 'yueyue'
                }
            })
            this.list = res.data.data
        }
    })
</script>
</body>
</html>

10.4.4 添加功能

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.yueyue记账清单-添加功能</title>
    <!-- CSS only -->
    <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
    />
    <style>
        .red {
            color: red!important;
        }
        .search {
            width: 300px;
            margin: 20px 0;
        }
        .my-form {
            display: flex;
            margin: 20px 0;
        }
        .my-form input {
            flex: 1;
            margin-right: 20px;
        }
        .table > :not(:first-child) {
            border-top: none;
        }
        .contain {
            display: flex;
            padding: 10px;
        }
        .list-box {
            flex: 1;
            padding: 0 30px;
        }
        .list-box  a {
            text-decoration: none;
        }
        .echarts-box {
            width: 600px;
            height: 400px;
            padding: 30px;
            margin: 0 auto;
            border: 1px solid #ccc;
        }
        tfoot {
            font-weight: bold;
        }
        @media screen and (max-width: 1000px) {
            .contain {
                flex-wrap: wrap;
            }
            .list-box {
                width: 100%;
            }
            .echarts-box {
                margin-top: 30px;
            }
        }
    </style>
</head>
<body>
<div id="app">
    <div class="contain">
        <!-- 左侧列表 -->
        <div class="list-box">

            <!-- 添加资产 -->
            <form class="my-form">
                <input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
                <input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
                <button @click="add" type="button" class="btn btn-primary">添加账单</button>
            </form>

            <table class="table table-hover">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>消费名称</th>
                    <th>消费价格</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                <tr v-for="(item, index) in list" :key="item.id">
                    <td>{{ index + 1 }}</td>
                    <td>{{ item.name }}</td>
                    <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
                    <td><a href="javascript:;">删除</a></td>
                </tr>
                </tbody>
                <tfoot>
                <tr>
                    <td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
                </tr>
                </tfoot>
            </table>
        </div>

        <!-- 右侧图表 -->
        <div class="echarts-box" id="main"></div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     *
     * 功能需求:
     * 1. 基本渲染
     *    (1) 立刻发送请求获取数据 created
     *    (2) 拿到数据,存到data的响应式数据中
     *    (3) 结合数据,进行渲染 v-for
     *    (4) 消费统计 => 计算属性
     * 2. 添加功能
     *    (1) 收集表单数据 v-model
     *    (2) 给添加按钮注册点击事件,发送添加请求
     *    (3) 需要重新渲染
     * 3. 删除功能
     * 4. 饼图渲染
     */
    const app = new Vue({
        el: '#app',
        data: {
            list: [],
            name: '',
            price: ''
        },
        computed: {
            totalPrice () {
                return this.list.reduce((sum, item) => sum + item.price, 0)
            }
        },
        created () {
            // const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
            //   params: {
            //     creator: '小黑'
            //   }
            // })
            // this.list = res.data.data

            this.getList()
        },
        methods: {
            async getList () {
                const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
                    params: {
                        creator: '小黑'
                    }
                })
                this.list = res.data.data
            },
            async add () {
                if (!this.name) {
                    alert('请输入消费名称')
                    return
                }
                if (typeof this.price !== 'number') {
                    alert('请输入正确的消费价格')
                    return
                }

                // 发送添加请求
                const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
                    creator: 'yueyue',
                    name: this.name,
                    price: this.price
                })
                // 重新渲染一次
                this.getList()

                this.name = ''
                this.price = ''
            }
        }
    })
</script>
</body>
</html>

10.4.5 静态结构

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.yueyue记账清单-静态结构</title>
    <!-- CSS only -->
    <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
    />
    <style>
        .red {
            color: red!important;
        }
        .search {
            width: 300px;
            margin: 20px 0;
        }
        .my-form {
            display: flex;
            margin: 20px 0;
        }
        .my-form input {
            flex: 1;
            margin-right: 20px;
        }
        .table > :not(:first-child) {
            border-top: none;
        }
        .contain {
            display: flex;
            padding: 10px;
        }
        .list-box {
            flex: 1;
            padding: 0 30px;
        }
        .list-box  a {
            text-decoration: none;
        }
        .echarts-box {
            width: 600px;
            height: 400px;
            padding: 30px;
            margin: 0 auto;
            border: 1px solid #ccc;
        }
        tfoot {
            font-weight: bold;
        }
        @media screen and (max-width: 1000px) {
            .contain {
                flex-wrap: wrap;
            }
            .list-box {
                width: 100%;
            }
            .echarts-box {
                margin-top: 30px;
            }
        }
    </style>
</head>
<body>
<div id="app">
    <div class="contain">
        <!-- 左侧列表 -->
        <div class="list-box">

            <!-- 添加资产 -->
            <form class="my-form">
                <input type="text" class="form-control" placeholder="消费名称" />
                <input type="text" class="form-control" placeholder="消费价格" />
                <button type="button" class="btn btn-primary">添加账单</button>
            </form>

            <table class="table table-hover">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>消费名称</th>
                    <th>消费价格</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td>1</td>
                    <td>帽子</td>
                    <td>99.00</td>
                    <td><a href="javascript:;">删除</a></td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>大衣</td>
                    <td class="red">199.00</td>
                    <td><a href="javascript:;">删除</a></td>
                </tr>
                </tbody>
                <tfoot>
                <tr>
                    <td colspan="4">消费总计: 298.00</td>
                </tr>
                </tfoot>
            </table>
        </div>

        <!-- 右侧图表 -->
        <div class="echarts-box" id="main"></div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
    /**
     * 接口文档地址:
     * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
     *
     * 功能需求:
     * 1. 基本渲染
     * 2. 添加功能
     * 3. 删除功能
     * 4. 饼图渲染
     */
    const app = new Vue({
        el: '#app',
        data: {

        },
    })
</script>
</body>
</html>

10.4.6 饼图渲染

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>04.yueyue记账清单-饼图渲染</title>
  <!-- CSS only -->
  <link
          rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
  />
  <style>
    .red {
      color: red!important;
    }
    .search {
      width: 300px;
      margin: 20px 0;
    }
    .my-form {
      display: flex;
      margin: 20px 0;
    }
    .my-form input {
      flex: 1;
      margin-right: 20px;
    }
    .table > :not(:first-child) {
      border-top: none;
    }
    .contain {
      display: flex;
      padding: 10px;
    }
    .list-box {
      flex: 1;
      padding: 0 30px;
    }
    .list-box  a {
      text-decoration: none;
    }
    .echarts-box {
      width: 600px;
      height: 400px;
      padding: 30px;
      margin: 0 auto;
      border: 1px solid #ccc;
    }
    tfoot {
      font-weight: bold;
    }
    @media screen and (max-width: 1000px) {
      .contain {
        flex-wrap: wrap;
      }
      .list-box {
        width: 100%;
      }
      .echarts-box {
        margin-top: 30px;
      }
    }
  </style>
</head>
<body>
<div id="app">
  <div class="contain">
    <!-- 左侧列表 -->
    <div class="list-box">

      <!-- 添加资产 -->
      <form class="my-form">
        <input v-model.trim="name" type="text" class="form-control" placeholder="消费名称" />
        <input v-model.number="price" type="text" class="form-control" placeholder="消费价格" />
        <button @click="add" type="button" class="btn btn-primary">添加账单</button>
      </form>

      <table class="table table-hover">
        <thead>
        <tr>
          <th>编号</th>
          <th>消费名称</th>
          <th>消费价格</th>
          <th>操作</th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="(item, index) in list" :key="item.id">
          <td>{{ index + 1 }}</td>
          <td>{{ item.name }}</td>
          <td :class="{ red: item.price > 500 }">{{ item.price.toFixed(2) }}</td>
          <td><a @click="del(item.id)" href="javascript:;">删除</a></td>
        </tr>
        </tbody>
        <tfoot>
        <tr>
          <td colspan="4">消费总计: {{ totalPrice.toFixed(2) }}</td>
        </tr>
        </tfoot>
      </table>
    </div>

    <!-- 右侧图表 -->
    <div class="echarts-box" id="main"></div>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  /**
   * 接口文档地址:
   * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058
   *
   * 功能需求:
   * 1. 基本渲染
   *    (1) 立刻发送请求获取数据 created
   *    (2) 拿到数据,存到data的响应式数据中
   *    (3) 结合数据,进行渲染 v-for
   *    (4) 消费统计 => 计算属性
   * 2. 添加功能
   *    (1) 收集表单数据 v-model
   *    (2) 给添加按钮注册点击事件,发送添加请求
   *    (3) 需要重新渲染
   * 3. 删除功能
   *    (1) 注册点击事件,传参传 id
   *    (2) 根据 id 发送删除请求
   *    (3) 需要重新渲染
   * 4. 饼图渲染
   *    (1) 初始化一个饼图 echarts.init(dom)  mounted钩子实现
   *    (2) 根据数据实时更新饼图 echarts.setOption({ ... })
   */
  const app = new Vue({
    el: '#app',
    data: {
      list: [],
      name: '',
      price: ''
    },
    computed: {
      totalPrice () {
        return this.list.reduce((sum, item) => sum + item.price, 0)
      }
    },
    created () {
      // const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
      //   params: {
      //     creator: '小黑'
      //   }
      // })
      // this.list = res.data.data

      this.getList()
    },
    mounted () {
      this.myChart = echarts.init(document.querySelector('#main'))
      this.myChart.setOption({
        // 大标题
        title: {
          text: '消费账单列表',
          left: 'center'
        },
        // 提示框
        tooltip: {
          trigger: 'item'
        },
        // 图例
        legend: {
          orient: 'vertical',
          left: 'left'
        },
        // 数据项
        series: [
          {
            name: '消费账单',
            type: 'pie',
            radius: '50%', // 饼的半径
            data: [
              // { value: 1048, name: '球鞋' },
              // { value: 735, name: '防晒霜' }
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
              }
            }
          }
        ]
      })
    },

    methods: {
      async getList () {
        const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {
          params: {
            creator: '小黑'
          }
        })
        this.list = res.data.data

        // 更新图表
        this.myChart.setOption({
          // 数据项
          series: [
            {
              // data: [
              //   { value: 1048, name: '球鞋' },
              //   { value: 735, name: '防晒霜' }
              // ]
              data: this.list.map(item => ({ value: item.price, name: item.name}))
            }
          ]
        })
      },
      async add () {
        if (!this.name) {
          alert('请输入消费名称')
          return
        }
        if (typeof this.price !== 'number') {
          alert('请输入正确的消费价格')
          return
        }

        // 发送添加请求
        const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {
          creator: '小黑',
          name: this.name,
          price: this.price
        })
        // 重新渲染一次
        this.getList()

        this.name = ''
        this.price = ''
      },
      async del (id) {
        // 根据 id 发送删除请求
        const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
        // 重新渲染
        this.getList()
      }
    }
  })
</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1579234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java面试题】MySQL上篇(索引)

文章目录 索引1.索引的分类&#xff1f;2.B树和B树的区别&#xff1f;2.1B树2.2B树 3.为什么使用索引会加快查询&#xff1f;4.创建索引的注意点&#xff1f;5.索引在哪些情况下会失效&#xff1f;6.聚簇索引和非聚簇索引的区别&#xff1f;7.回表查询是什么&#xff1f;8.什么…

思迈特:“人工智能+”浪潮里,国产BI到了关键时刻

作为首个“AI程序员”&#xff0c;Devin最近参与了一系列工作&#xff0c;包括在人力资源外包平台Upwork完成编程工作&#xff1b;潜入一家明星创业公司内部群交流&#xff0c;为公司CTO调整代码方案等。这让整个软件工程行业大受震撼&#xff0c;程序员留言“刷屏”。 “AI…

TinyMPC 使用教程(二)

系列文章目录 前言 一、如何使用 TinyMPC 1.1 加载程序库 import tinympc import numpy as nptinympc_python_dir "/path/to/tinympc-python" tinympc_dir tinympc_python_dir "/tinympc/TinyMPC"prob tinympc.TinyMPC() prob.compile_lib(tinympc_d…

计算机网络:数据链路层 - CSMA/CD协议

计算机网络&#xff1a;数据链路层 - CSMA/CD协议 媒体接入控制CSMA/CD协议截断二进制指数退避算法帧长与帧间间隔信道利用率 媒体接入控制 如图所示&#xff0c;这是一根同轴电缆&#xff0c;有多台主机连接到这根同轴电缆上&#xff0c;他们共享这根传输媒体&#xff0c;形成…

又整新活,新版 IntelliJ IDEA 2024.1 有点东西!

就在上周&#xff0c;Jetbrains 又迎来了一波大版本更新&#xff0c;这也是 JetBrains 2024首个大动作&#xff01; JetBrains 为其多款 IDE 发布了 2024 年度首个大版本更新 (2024.1)。 作为旗下重要的产品之一&#xff0c;IntelliJ IDEA当然也不例外。这不&#xff0c;现如今…

使用 Meltano 将数据从 Snowflake 导入到 Elasticsearch:开发者之旅

作者&#xff1a;来自 Elastic Dmitrii Burlutskii 在 Elastic 的搜索团队中&#xff0c;我们一直在探索不同的 ETL 工具以及如何利用它们将数据传输到 Elasticsearch&#xff0c;并在传输的数据上实现 AI 助力搜索。今天&#xff0c;我想与大家分享我们与 Meltano 生态系统以及…

矩阵链乘法问题

描述 输入 输入共n1行 第一行输入矩阵的总个数n[2,1000] 后n行分别输入矩阵的维数[1,100] 输出 最后一行输出少乘法次数 输入样例 1 6 30 35 35 15 15 5 5 10 10 20 20 25 输出样例1 15125 代码实现 #include<iostream> #include<vector> #include<…

设计模式之观察者模式讲解

概念&#xff1a;定义对象间一种一对多的依赖关系&#xff0c;使得当每一个对象改变状态&#xff0c;则所有依赖于它的对象都会得到通知并被自动更新。 抽象主题&#xff1a;或者叫被观察者&#xff0c;可以持有、增加、删除观察者对象。具体主题&#xff1a;实现抽象主题定义的…

yolov5旋转目标检测遥感图像检测-无人机旋转目标检测(代码和原理)

YOLOv5&#xff08;You Only Look Once version 5&#xff09;是一个流行且高效的实时目标检测深度学习模型&#xff0c;最初设计用于处理图像中的水平矩形边界框目标。然而&#xff0c;对于旋转目标检测&#xff0c;通常需要对原始YOLOv5架构进行扩展或修改&#xff0c;以便能…

【经典算法】LCR187:破冰游戏(约瑟夫问题,Java/C/Python3/JavaScript实现含注释说明,Easy)

目录 题目思路及实现方式一&#xff1a;迭代模拟&#xff08;用链表模拟这个游戏&#xff09;思路代码实现Java版本C语言版本Python3版本 复杂度分析 方式二&#xff1a;数学迭代思路代码实现Java版本C语言版本Python3版本 复杂度分析 方式三&#xff1a;递归思路代码实现Java版…

数字化智慧养老:引领老年人融入科技时代新生活

hello宝子们...我们是艾斯视觉擅长ui设计和前端开发10年经验&#xff01;希望我的分享能帮助到您&#xff01;如需帮助可以评论关注私信我们一起探讨&#xff01;致敬感谢感恩&#xff01; 人类社会已经步入了一个全新的数字时代。在这个时代&#xff0c;互联网、大数据、人工智…

学习操作系统之单道批处理系统

较之前操作的改进&#xff1a; 在原先的工作基础上&#xff0c;扩大存储&#xff0c;一次放入多个作业再进行处理。 单道&#xff1a;内存中始终只有一道作业 批处理&#xff1a;磁带上有多道作业&#xff0c;安装一次磁带&#xff0c;可以处理一批作业 1953年诞生了第一代…

【C语言】指针篇(指针数组,数组指针,函数指针,一级、二级指针)

文章目录 一、指针基础1.什么是指针2.指针的定义和初始化3.指针的解引用4.野指针和空指针5.指针的类型6.指针的大小7.指针的运算8.指针和数组9.指针和字符串10.二级指针 二、指针数组和数组指针1.指针数组2.数组指针3.练习 三、数组传参和指针传参1.一维数组传参2.二维数组传参…

开源区块链系统/技术 总结(欢迎补充,最新)

1. FISCO BCOS FISCO BCOS 2.0 技术文档 — FISCO BCOS 2.0 v2.9.0 文档https://fisco-bcos-documentation.readthedocs.io/ 2. ChainMaker&#xff08;长安链&#xff09; 文档导航 — chainmaker-docs v2.3.2 documentationhttps://docs.chainmaker.org.cn/v2.3.2/html/in…

你们是如何保证消息不丢失的?

1、什么是死信 在 RabbitMQ 中充当主角的就是消息&#xff0c;在不同场景下&#xff0c;消息会有不同地表现。 死信就是消息在特定场景下的一种表现形式&#xff0c;这些场景包括&#xff1a; 1. 消息被拒绝访问&#xff0c;即 RabbitMQ返回 basicNack 的信号时 或者拒绝basi…

CKA 基础操作教程(五)

Kubernetes Ingress 理论学习 Ingress 提供从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源所定义的规则来控制。 Ingress 资源示例&#xff1a; apiVersion: networking.k8s.io/v1 # 指定 Kubernetes 中使用的 API 版本 kind: Ingress # 指定对象…

【日常记录】【JS】填充数组的三种方案

文章目录 1、for 循环填充2、new Array、fill、map 三者配合填充3、Array.from 填充数组参考链接 一般在开发中需要生成一个数组&#xff0c;用于测试等其他情况&#xff0c;以下介绍三种常见方案 1、for 循环填充 如果需要对这个数组的内容做一些特殊处理&#xff0c;写起来就…

Mysql底层原理七:InnoDB 行记录

1.行格式 1.1 Compact行格式 1.1.1 示意图 1.1.2 准备一下 1&#xff09;建表 mysql> CREATE TABLE record_format_demo (-> c1 VARCHAR(10),-> c2 VARCHAR(10) NOT NULL,-> c3 CHAR(10),-> c4 VARCHAR(10)-> ) CHARSETascii ROW_FORMATCOM…

企业网络安全运营能力的多维度评价及优化策略

网络安全是企业面临的一个日益重要的问题&#xff0c;安全运营能力的强弱直接关系到企业的健康可持续发展和综合竞争力的提升。为推动企业网络安全工作的标准化建设&#xff0c;提升企业的网络安全运营能力&#xff0c;本文从安全建设、安全应对和安全效果三个角度出发&#xf…

【迅为iTOP-4412-linux 系统制作(4)】ADB 或者 TF 卡烧写测试

准备工作 编译生成的内核镜像uImage 和设备树 dtb 文件“exynos4412-itop-elite.dtb”已经可以使用了。 把编译生成的uimage和dtb文件。拷贝fastboot工具。官方的u-boot-iTOP-4412.bin 也拷贝到 platform-tools 文件夹目录内。system.img 也拷贝到 platform-tools 文件夹目录…