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

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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

获得乙级风力发电设计资质需要哪些注册工程师资格?

要获得乙级风力发电设计资质&#xff0c;通常需要以下注册工程师资格&#xff1a; 注册电气&#xff08;发输电&#xff09;工程师&#xff1a;他们负责风力发电项目的电气系统设计、优化、设备选型和配置&#xff0c;确保电力供应的安全性和可靠性。他们的专业知识对于保证风…

SpringBoot的旅游管理系统+论文+ppt+免费远程调试

项目介绍: 基于SpringBoot旅游网站 旅游管理系统 本旅游管理系统采用的数据库是Mysql&#xff0c;使用SpringBoot框架开发。在设计过程中&#xff0c;充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。 &#xff08;1&…

吴恩达深度学习笔记:深层神经网络(Deep Neural Networks)4.5-4.8

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第四周&#xff1a;深层神经网络(Deep Neural Networks)4.5 为什么使用深层表示&#xff1f;&#xff08;Why deep representations?&#xff09; 第一门课&#xff1a;神经网络和深度学习 (…

5.网络编程-socker(golang版)

目录 一、什么是socket&#xff1f; 二、Golang中使用TCP TCP服务端 TCP客户端​​​​​​​ 三、TCP黏包&#xff0c;拆包 1.什么是粘包&#xff0c;拆包&#xff1f; 2.为什么UDP没有粘包&#xff0c;拆包&#xff1f; 3.粘包拆包发生场景 4.TCP黏包 黏包服务端 …

官宣定档“2024上海国际半导体产业展会”定于11月份在沪召开

2024上海国际半导体产业展览会 2024 Shanghai Semiconductor Expo 时间:2024年11月18-20日 地点:上海新国际博览中心 前言 近年来&#xff0c;上海半导体产业呈现出快速发展的态势。一方面&#xff0c;得益于国家政策的大力支持&#xff0c;上海半导体产业得到了迅猛的发展。…

计算机组成结构2

概念 存储系统 解决成本-速度-容量之前的矛盾问题 寄存器–cache–内存–硬盘–外存储 局部性原理 时间局部&#xff1a;相邻的时间访问同一个数据空间局部&#xff1a;相邻的空间地址会被连续访问 cache cpu与主存之间&#xff0c;命中cache后就不需要访问主存&#xff0c;…

景联文科技:为AI大模型提供高质海量训练数据

在全球AI浪潮的推动下&#xff0c;大量训练数据已成为AI算法模型发展和演进中的关键一环。 艾瑞咨询数据显示&#xff0c;包括数据采集、数据处理&#xff08;标注&#xff09;、数据存储、数据挖掘等模块在内的AI基础数据服务市场&#xff0c;将在未来数年内持续增长。 预计到…

跟着GPT学设计模式之适配器模式

题图来自APOD 你好&#xff0c;这里是codetrend专栏“跟着GPT学设计模式”。 说明 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;用于将一个类的接口转换为客户端所期望的另一个接口。适配器模式允许不兼容的接口协同工作&#xff0c…

面向跳转编程JOP问题及挑战

BTI分支目标识别精讲与实践系列 思考 1、什么是代码重用攻击?什么是ROP攻击?区别与联系? 2、什么是JOP攻击?间接分支跳转指令? 3、JOP攻击的缓解技术?控制流完整性保护? 4、BTI下的JOP如何缓解?什么是目标着陆台? 5、BTI的架构细节?硬件原理?间接分支类型?指…

数据库(mysql)-基本查询语句(DQL)

查询语句 这边查询是给予一定表格,这边先做个解释 教师表包括(name(姓名),gender(性别),salary(工资),title(职位),subject_id(课程的编号),comm(奖金)) 学生表包括(姓名(name),gender(性别),job(职位),生日(birth)) 模版 SELECT 字段名 FROM 查询表 WHERE 查询语句 或与非…

Laravel 项目如何运行

如有一个 Laravel 项目&#xff0c;在配置好 PHP 版本和运行环境后&#xff0c;可以直接在项目下直接运行&#xff1a; php artisan serve 来启动你的项目。 通过浏览器查看 当项目运行后&#xff0c;默认的启动端口为 8000&#xff0c;可以通过浏览器来进行查看运行的 Larav…

c++的学习之路:17、stack、queue与priority_queue

摘要 本文主要是介绍一下stack、queue、priority_queue的使用以及模拟实现&#xff0c;文章末附上代码以及思维导图。 目录 摘要 一、stack的介绍和使用 1、stack的介绍 2、stack的使用 3、stack的模拟实现 二、queue的介绍和使用 1、queue的介绍 2、queue的使用 3、…

leetcode刷题日记之接雨水问题

题目描述 解题思路 这个题目相当于一个桶的容量是多少&#xff0c;这取决于最短的模板的高度&#xff0c;&#xff0c;对于位置来讲&#xff0c;第i个位置所能承载的最大的容量为左右两侧最低的高度减去该位置的高度&#xff0c;如果两侧的最低位置小于height【i】&#xff0c…

TSINGSEE青犀边缘计算AI智能分析网关V4客流统计算法的配置步骤及使用

TSINGSEE青犀AI智能分析网关V4内置了近40种AI算法模型&#xff0c;支持对接入的视频图像进行人、车、物、行为、烟火等实时检测分析&#xff0c;上报识别结果&#xff0c;并能进行语音告警播放。硬件支持RTSP、GB28181协议、以及厂家私有协议接入&#xff0c;可兼容市面上常见的…

Python学习从0到1 day21 第二阶段 面向对象 ④ 类型注解

仗剑红尘已是癫&#xff0c;有酒平步上青天 —— 24.4.7 一、变量的类型注解 学习目标 1.理解为什么使用类型注解 2.掌握变量的类型注解语法 为什么使用类型注解 tip&#xff1a;CTRLP&#xff0c;可以提示函数中传入的参数 当我们需要使用pycharm的自动补全功能&#xff0c;又…

Java | Leetcode Java题解之第18题四数之和

题目&#xff1a; 题解&#xff1a; class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> quadruplets new ArrayList<List<Integer>>();if (nums null || nums.length < 4) {return…

(表征学习论文阅读)A Simple Framework for Contrastive Learning of Visual Representations

Chen T, Kornblith S, Norouzi M, et al. A simple framework for contrastive learning of visual representations[C]//International conference on machine learning. PMLR, 2020: 1597-1607. 1. 前言 本文作者为了了解对比学习是如何学习到有效的表征&#xff0c;对本文所…

Disk Drill Enterprise for Mac v5.5.1515数据恢复软件中文版

Disk Drill 是 Mac 操作系统固有的Mac数据恢复软件&#xff1a;使用 Recovery Vault 轻松保护文件免遭意外删除&#xff0c;并从 Mac 磁盘恢复丢失的数据。支持大多数存储设备&#xff0c;文件类型和文件系统。 软件下载&#xff1a;Disk Drill Enterprise for Mac v5.5.1515激…

【YOLOV8】项目目录重点部分介绍和性能评估指标

目录 一 项目目录重点部分介绍 二 性能评估指标 一 项目目录重点部分介绍 1 ultralytics

3D医疗图像配准 | 基于Vision-Transformer+Pytorch实现的3D医疗图像配准算法

项目应用场景 面向医疗图像配准场景&#xff0c;项目采用 Pytorch ViT 来实现&#xff0c;形态为 3D 医疗图像的配准。 项目效果 项目细节 > 具体参见项目 README.md (1) 模型架构 (2) Vision Transformer 架构 (3) 量化结果分析 项目获取 https://download.csdn.net/down…