VUE学习笔记(黑马2023版)

第一天

第一天准备文件:
链接:https://pan.baidu.com/s/14Cd_aMQMsgMTZi72AefZ1g?pwd=6666
提取码:6666

1.Vue是什么?

Vue是一个用于构建用户界面渐进式框架
1.构建用户界面:基于数据动态渲染页面
2.渐进式:循序渐进的学习
3.框架:一套完整的项目解决方案,提升开发效率

2.创建一个Vue实例

创建Vue实例,初始化渲染

2.1.准备容器(Vue所管理的范围)

比如:

<div id="app">
<!-- 这里将会编写一些用于渲染的代码逻辑 -->
<div>

2.2.引包(开发版本包/生产版本包)

前往官网下载包
进入官网后依次点击起步
官网图片
点击安装

在这里插入图片描述
在安装时选择开发版本,因为开发版本包含完整的警告和调试模式
在这里插入图片描述
如果不想安装,可以用用一下语句直接导入
在这里插入图片描述

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

2.3.创建实例

<script>
//一旦引入VueJS核心包,在全局环境,就有了Vue构造函数
    const app = new Vue()
</script>

2.4.指定配置项 el data

el指定挂载点,选择器指定控制的是哪个盒子
data提供数据
比如在第三步的基础上:

<script>
    const app = new Vue({
        el:'#app',
        data:{
            msg:'Hello 黑马'
        }
    })
</script>

上述代码指控制id为app的盒子,数据为‘Hello 黑马’
再将第一步的div盒子更改为:

<div id="app">
    {{msg}}
</div>

最后在网页中运行的效果为:
在这里插入图片描述
完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
<div id="app">
    {{msg}}
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app = new Vue({
        el:'#app',
        data:{
            msg:'Hello 黑马'
        }
    })
</script>

</body>
</html>

3.插值表达式

插值表达式{{ }}
插值表达式是一种Vue的模板语法

3.1.作用:

利用表达式进行插值,渲染到页面中
表达式:是可以被求值的代码,JS引擎会将其计算出一个结果

3.2.语法:

{{ 表达式 }}
比如:

<div id="app">
    {{msg}}
</div>

3.3.注意点

(1)使用的数据必须存在,要事先声明
(2)支持的是表达式,而不是语句,比如:if for…
例如以下是错误的:

<p>
    {{ if }}
</p>

(3)不能再标签属性中使用{{ }}插值
例如以下是错误的:

<p title="{{ username }}">
</p>

3.4.实践案例

实践案例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    

<div id="app">
    <p>{{ nickname }}</p>
    <p>{{ nickname.toUpperCase() }}</p>
    <p>{{ nickname + '你好' }}</p>
    <p>{{ age>=18 ? '成年' : '未成年' }}</p>
    <p>{{ friend.name }}</p>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app = new Vue({
        el:'#app',
        data:{
            nickname: 'tony',
            age: 18,
            friend: {
                name: 'jepson',
                desc: '热爱学习'
            }
        }
    })
</script>

</body>
</html>

效果图如下:
在这里插入图片描述

4.Vue响应式特性

4.1.什么是响应式?

数据改变,视图自动更新

4.2.访问和修改数据

访问数据:“实例.属性名”
例如:在上一个案例实践的网页中,按f12进入控制台输入一下代码,成功访问数据
在这里插入图片描述
修改数据:“实例.属性名”=“值”
例如:在上一个案例实践的网页中,按f12进入控制台输入一下代码,成功修改数据
在这里插入图片描述

5.Vue开发者工具安装:装插件调试Vue应用

5.1浏览器为谷歌的安装方法

通过极简插件安装

1.点击链接打开极简网址
2.搜索Vue
3.安装Vue Devtools
在这里插入图片描述
4.点击推荐下载
5.下载完成后解压
6.打开管理扩展程序
在这里插入图片描述
7.将刚下载解压好的后缀为.crx的程序拖拽到下图所示页面的空白部分中
在这里插入图片描述
8.弹出提示框,点击添加扩展程序
9.安装完成后点击插件详情
在这里插入图片描述
10.将允许访问文件网址打开
在这里插入图片描述
11.重启浏览器(一定要重启,不是刷新),插件生效
在这里插入图片描述

5.2浏览器为Edge的安装方法

1.如图点击扩展
在这里插入图片描述
2.在弹出的窗口中选择“打开Microsoft Edge加载项”
3.在新窗口中搜索vue
4.安装Vue.js devtools
在这里插入图片描述
5.安装完成后,点击管理扩展
在这里插入图片描述
6.点击Vue.js devtools的“详细信息”,将“允许访问文件URL”打上勾
在这里插入图片描述
7.重启浏览器(一定要重启,不是刷新),插件生效
在这里插入图片描述

6.Vue指令

Vue会根据不同的指令,针对标签实现不同的功能
指令:带有v-前缀的特殊标签属性

6.1 v-html

  • 作用:设置元素的innerHTML
  • 语法:v-html=“表达式”
	<div v-html="表达式"></div>
  • 案例实践:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
<div id="app">
    <div v-html="msg"></div>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app = new Vue({
        el:'#app',
        data:{
            msg:`<a href="http://www.itheima.com">
                黑马程序员
                </a>`//注意``是模版字符串,不要省略
        }
    })
</script>

</body>
</html>

效果展示:
在这里插入图片描述

6.2 v-show 和 v-if

6.2.1 v-show
  • 作用:控制元素显示隐藏
  • 语法:v-show=“表达式” 表达式值true显示,false隐藏
  • 原理:切换display:none控制显示隐藏
  • 适用场景:频繁切换显示隐藏的场景
6.2.2v-if
  • 作用:控制元素显示隐藏(条件渲染
  • 语法:v-if=“表达式” 表达式值true显示,false隐藏
  • 原理:基于条件判断,是否创建移除元素节点
  • 使用场景:要么显示,要么隐藏,不频繁切换的场景
6.2.3案例实践
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Document</title>
   <style>
       .box{
           height: 50px;
           width: 100px;
           border-radius: 5px;
           text-align: center;
           border: solid 3px;
           padding: 10px;
           margin: 10px;
       }
   </style>
</head>
<body>
   
   <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.7.14/dist/vue.js"></script>

<script>
   const app = new Vue({
       el:'#app',
       data:{
           flag:true
       }
   })
</script>
   

</body>
</html>
flag:true//当flag元素为true时,id为app的控件显示

效果如图:

在这里插入图片描述

flag:false//当flag元素为false时,id为app的控件不显示

效果如图:
在这里插入图片描述

6.3 v-else和v-else-if

  • 作用:辅助v-if进行判断渲染
  • 语法:
    v-else
    v-else-if=“表达式”
<p v-if="表达式">我是一个p标签</p>
<p v-else-if="表达式">我是一个p标签</p>
<p v-else>我是一个p标签</p>
  • 注意:需要紧挨v-if使用
  • 案例实践
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <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 >= 70">成绩评定B:奖励周末郊游</p>
        <p v-else-if="score >= 60">成绩评定C:奖励零食礼包</p>
        <p v-else>成绩评定D:惩罚一周不能玩手机</p>
    </div>


    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

    <script>
        const app = new Vue({
            el:'#app',
            data:{
                gender: 2,
                score: 80
            }
        })
    </script>

</body>
</html>

案例结果如下:
在这里插入图片描述

6.4v-on

  • 作用:注册事件=添加监听+提供处理逻辑
  • 语法:
    ①v-on:事件名=“内联语句”
    例如:
<button v-on:click="count--">我是一个按钮</button>

②v-on:事件名=“methods中的函数名”
例如:

<button v-on:click="fn">我是一个按钮</button>
  • 简写:@事件名
<!--这种方法更为常用-->
<button @click="fn">我是一个按钮</button>
  • 案例实践
    ①v-on:事件名="内联语句"的案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
<div id="app">
    <!-- 点击移除实现count减一 -->
    <button v-on:click="count--">-</button>
    <span>{{ count }}</span>
    <!-- 点击一次实现count加1,此处'@'为v-on的简写方式,平常生活中更为常用 -->
    <button @click="count++">+</button>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app = new Vue({
        el:'#app',
        data:{
            count: 100
        }
    })
</script>

</body>
</html>

案例结果如下:
在这里插入图片描述

②v-on:事件名="methods中的函数名"的案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
<div id="app">
    <!-- 此处的@click=""内部的值要与methods中的方法名一致 -->
    <button @click="fn">切换显示隐藏</button>
    <h1 v-show="isshow">黑马程序员</h1>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app2 = new Vue({
        el:'#app',
        data:{
            isshow: true
        },
        methods:{
            fn(){
                //此处的语句可以替换为app2.isshow = !app2.isshow,但鼓励下面的做法,更利于代码的维护
                this.isshow = !this.isshow
            }
        }
    })
</script>

</body>
</html>

案例结果如下:
在这里插入图片描述
在这里插入图片描述

  • v-on参数传递
    • 语句
<button @click="fn(参数一,参数二)">
	按钮
</button>
const app= new Vue({
	el:#app,
	methods:{
		fn (a, b) {
			console.log('这是一个fn函数')
		}
	}
})
  • 参数传递案例实践
    案例说明:通过点击对应的饮品,实现银行卡余额减少对应价钱
    代码如下:
<!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>Document</title>
  <style>
    .box {
      border: 3px solid #000000;
      border-radius: 10px;
      padding: 20px;
      margin: 20px;
      width: 200px;
    }
    h3 {
      margin: 10px 0 20px 0;
    }
    p {
      margin: 20px;
    }
  </style>
</head>
<body>

  <div id="app">
    <div class="box">
      <h3>小黑自动售货机</h3>
      <button @click="buy(5)">可乐5元</button>
      <button @click="buy(10)">咖啡10元</button>
      <button @click="buy(8)">牛奶8元</button>
    </div>
    <p>银行卡余额:{{ money }}元</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        money: 100
      },
      methods: {
        buy (price) {
          this.money -= price
        }
      }
    })
  </script>
</body>
</html>

部分案例效果图如下:
在这里插入图片描述

6.5v-bind

  • 作用:动态的设置html的标签属性(src url title…)
  • 语法:v-bind:属性名=“表达式”
<img v-bind:属性名="表达式">
  • 简写::属性名=“表达式”
<img :属性名="表达式">
  • 案例实践
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
<div id="app">
    <img v-bind:src="imgUrl" v-bind:title="msg" alt="">
    <!-- 此处:src和:title均为上面v-bind的简写 -->
    <img :src="imgUrl" :title="msg" alt="">
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app = new Vue({
        el:'#app',
        data:{
            imgUrl:'./imgs/10-01.png',
            msg:'hello 波仔'
        }
    })
</script>

</body>
</html>

案例结果如下:
在这里插入图片描述

图片切换案例-波仔学习之旅

  • 核心思路分析:
    1.数组存储图片路径
    2.准备下标index,数组[下标]
  • 案例代码如下:
<!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>Document</title>
</head>
<body>
  <div id="app">
    <button v-show="index > 0" @click="index--">上一页</button>
    <div>
      <img :src="list[index]" alt="">
    </div>
    <button v-show="index < list.length - 1" @click="index++">下一页</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        index:0,
        list: [
          './imgs/11-00.gif',
          './imgs/11-01.gif',
          './imgs/11-02.gif',
          './imgs/11-03.gif',
          './imgs/11-04.png',
          './imgs/11-05.png',
        ]
      }
    })
  </script>
</body>
</html>

案例部分效果如下:
在这里插入图片描述
在这里插入图片描述

6.6v-for

  • 作用:基于数据循环,多次渲染整个元素
  • 遍历数组语法:
    v-for=“(item,index) in 数组
    item:每一项
    index:下标
<li v-for="(item,index) in list">我是一个li标签</li>
  • 省略index
    v-for = “item in 数组”
<li v-for="item in list">我是一个li标签</li>
  • 案例实践
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
<div id="app">
    <h3>小黑水果店</h3>
    <ul>
        <li v-for="(item,index) in list">{{ item }} - {{ index }}</li>
    </ul>
    
    <ul>
        <!-- 省略index形式 -->
        <li v-for="item in list">{{ item }}</li>
    </ul>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    const app = new Vue({
        el:'#app',
        data:{
            list:['西瓜','苹果','鸭梨']
        }
    })
</script>

</body>
</html>

案例结果如下:
在这里插入图片描述

图书管理案例-小黑的书架

  • 案例说明:
    实现下图页面,并按删除按钮后,实现对应项的删除
    在这里插入图片描述
  • 核心思路分析:
    ①通过v-for遍历数组
    ②给删除按钮绑上@click,实现点击后删除的功能
  • 实现代码
<!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>Document</title>
</head>
<body>

  <div id="app">
    <h3>小黑的书架</h3>
    <ul>
      <li v-for="(item,index) in booksList" :key="item.id">
        <span>{{ item.name }}</span>
        <span>{{ item.author }}</span>
        <!-- 注册点击事件,通过id进行删除数组中的对应项 -->
        <button @click="del(item.id)">删除</button>
      </li>
    </ul>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        booksList: [
          { id: 1, name: '《红楼梦》', author: '曹雪芹' },
          { id: 2, name: '《西游记》', author: '吴承恩' },
          { id: 3, name: '《水浒传》', author: '施耐庵' },
          { id: 4, name: '《三国演义》', author: '罗贯中' }
        ]
      },
      methods:{
        del ( id ) {
          //通过id进行删除数组中的对应项
          //filter(不会改变原数组):根据条件,保留满足条件的对应项,得到一个新数组
          this.booksList=this.booksList.filter(item => item.id != id)

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

6.7 v-for中的key

  • 作用:给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用
  • 语法:
    key属性=“唯一标识”
<li v-for="(item,index) in 数组" :key="唯一值">
  • 注意点:
    ①key的值只能是字符串数字类型
    ②key的值必须具有唯一性
    ③推荐使用id作为key(唯一),不推荐使用index作为key(会变化,不对应)

6.8 v-model

  • 作用:
    表单元素使用,双向数据绑定 -> 可以快速获取或设置表单元素内容
    ①数据变化 -> 视图自动更新
    视图变化 -> 数据自动更新
  • 语法:v-model=‘变量’
<input type="text" 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>Document</title>
</head>
<body>

  <div id="app">
    <!-- v-model让数据和视图,形成双向数据绑定 -->
    账户:<input type="text" v-model="username"> <br><br>
    密码:<input type="password" v-model="password"> <br><br>
    <button @click="login">登录</button>
    <button @click="reset">重置</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username:'',
        password:''
      },
      methods:{
        login(){
          console.log(this.username,this.password)
        },
        reset(){
          this.username='',
          this.password=''
        }
      }
    })
  </script>
</body>
</html>

综合案例-小黑记事本

功能需求:

1.列表渲染
v-for key的设置,{{}}插值表达式
2.删除功能
①v-on调用传参
②filter过滤,覆盖原数组
3.添加功能
①通过v-model绑定输入框 -> 实时获取表单元素的内容
②点击按钮,进行新增,往数组最前面加unshift
4.底部统计和清空
①数组.length累计长度
②覆盖数组清空列表
③v-show控制隐藏
在这里插入图片描述

代码实现:
<!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" />
<link rel="stylesheet" href="./css/index.css" />
<title>记事本</title>
</head>
<body>

<!-- 主体区域 -->
<section id="app">
  <!-- 输入框 -->
  <header class="header">
    <h1>小黑记事本</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/dist/vue.js"></script>
<script>

  const app = new Vue({
    el: '#app',
    data: {
      todoName:'',
      list:[
        {id: 1, name: '跑步一公里'},
        {id: 2, name: '跳绳200次'},
        {id: 3, name: '游泳100米'}
      ]      
    },
    methods:{
      del(id){
        this.list = this.list.filter( item =>item.id !== id)
      },
      add(){
        //trim()去除空格
        if(this.todoName.trim() === ''){
          alert('请输入任务名称')
          return
        }
        //unshift()增加到数组的最前面
        this.list.unshift({
          id: +new Date(),//用时间戳临时作为id
          name:this.todoName,
        })
        this.todoName=''
      },
      clear(){
        this.list=[]
      }
    }
  })

</script>
</body>
</html>

第二天

第二天准备文件:
链接:https://pan.baidu.com/s/1_3NUO6dKECpi7OmV5VMSzg?pwd=6666
提取码:6666

7.指令修饰符

通过"."指明一些指令后缀,不同后缀封装了不同的处理操作

7.1按键修饰符

@keyup.enter 用于键盘回车监听(较为常用)
例如:

<!-- 回车后执行add操作 -->
<input @keyup.enter="add" v-model="todoName"/>

7.2 v-model修饰符

  • v-model.trim去除首尾空格(较为常用)
    例如:
    <input v-model.trim="username" type="text"><br>
  • v-model.number转数字(较为常用)
    例如:
    <input v-model.number="age" type="text"><br>

7.3 事件修饰符

  • @事件名.stop阻止冒泡
<!-- 当点击son div时,父级元素father div的@click不会被执行 -->
    <div @click="alert('老父亲被点击了')" class="father">
      <div @click.stop="alert('儿子被点击了')" class="son">儿子</div>
    </div>
  • @事件名.prevent阻止默认行为
<a @click.prevent="notLink" href="http://www.baidu.com">阻止默认行为</a>

8. v-bind对于样式控制的增强 - 操作class

语法:
:class=“对象/数组”

对象->键就是类名,值是布尔值。如果值为true,有这个类,否则没有这个类
适用场景:一个类名,来回切换

<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>

数组->数组中所有的类,都会添加到盒子上,本质就是一个class列表
适用场景:批量添加或删除类

<div class="box" :class="[类名1, 类名2 ,类名3 ]"></div>

案例:京东秒杀tab导航高亮

  • 案例说明:点击不同的链接,实现点击的链接高亮功能
    在这里插入图片描述
  • 核心思路:
    1.基于数据动态渲染tab(v-for实现)
    2.准备下标记录高亮的是哪一个tab
    3.基于下标,动态控制class类名(v-bind:class)
  • 案例实现:
<!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>Document</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>

  <div id="app">
    <ul>
   	  <!-- 用v-for遍历数组,将选中的index传给activeIndex -->
      <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/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        activeIndex: 0, //存储选中的下标
        list: [
          { id: 1, name: '京东秒杀' },
          { id: 2, name: '每日特价' },
          { id: 3, name: '品类秒杀' }
        ]

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

9.v-bind对于样式控制的增强 - 操作style

  • 语法:
    :style=“样式对象”
<div class="box" :style="{ CSS属性名1: CSS属性, CSS属性名2: CSS属性}"></div>
  • 适用场景:某个具体属性的动态设置
  • 实践案例
    实现进度条的控制,如图所示
    在这里插入图片描述

代码如下:

<!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>Document</title>
  <style>
    .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>
  <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="./vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        percent: 30
      }
    })
  </script>
</body>
</html>

10.v-model应用于其他表单元素

常见的表单元素都可以用v-model绑定关联 -> 快速获取设置表单元素的值
他会根据控件类型自动选取正确的方法来更新元素

  • 输入框 input:text -> value
  • 文本域 textarea -> value
  • 复选框 input:checkbox -> checked
  • 单选框 input:radio -> checked
  • 下拉菜单 select -> value

案例实践如下:

<!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>Document</title>
  <style>
    textarea {
      display: block;
      width: 240px;
      height: 100px;
      margin: 10px 0;
    }
  </style>
</head>
<body>

  <div id="app">
    <h3>小黑学习网</h3>

    姓名:
      <input type="text" v-model="username"> 
      <br><br>

    是否单身:
      <input type="checkbox" v-model="isSingle"> 
      <br><br>

    <!-- 
      前置理解:
        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="./vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username:'',
        isSingle:false,
        gender:"",
        cityId:'101',
        desc:""
      }
    })
  </script>
</body>
</html>

11.计算属性

  • 概念:基于现有的数据,计算出来的新属性。因爱的数据变化,自动计算
  • 语法:
  • ①声明在computed配置项中,一个计算属性对应一个函数
  • ②使用起来和普通属性一样使用{{ 计算属性名 }}
computed: {
	计算属性名(){
		基于现有数据,编写求值逻辑
		return 结果
	}
}

案例实践

<!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>Document</title>
  <style>
    table {
      border: 1px solid #000;
      text-align: center;
      width: 240px;
    }
    th,td {
      border: 1px solid #000;
    }
    h3 {
      position: relative;
    }
  </style>
</head>
<body>

  <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/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(){
          //0表示求和起始值,reduce遍历list,将每个item的值加上后返回给sum
          let total= this.list.reduce((sum, item) => sum + item.num, 0)
          return total
        }
      }
    })
  </script>
</body>
</html>

案例效果展示:
在这里插入图片描述

11.1computed计算属性 vs methods方法

  • computed计算属性
    作用:封装了一段对于数据的处理,求得一个结果
    缓存特性:计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算 -> 并再次缓存
  • methods方法:
    作用:给实例提供一个方法,调用以处理业务逻辑

11.2 计算属性完整写法

计算属性默认的简写,只能读取访问,不能“修改”
如果要**“修改”,需要写计算属性的完整写法**
简写:

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

完整写法:

computed: {
	计算属性名(){
		get(){
			一段代码逻辑(计算逻辑)
			return 结果
		},
		set(修改的值){
			一段代码逻辑(修改逻辑)
		}
	}
}

综合案例 - 成绩案例

  • 案例说明:
    制作一个成绩表,有编号,科目,成绩属性,并且有删除,添加,和求总分,平均分的功能
    在这里插入图片描述- 需求说明
  1. 渲染功能
    v-if v-else:用来判断呈现表格的形式
    v-for:遍历数组
    v-bind:设置不及格的高亮
  2. 删除功能
    点击传参,filter过滤覆盖原数组
    .prevent阻止默认行为
  3. 添加功能
    v-model(.trim ,number)绑定数据
    unshift修改数组更新视图
  4. 统计总分,求平均分
  • 代码实现:
<!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" />
    <link rel="stylesheet" href="./styles/index.css" />
    <title>Document</title>
  </head>
  <body>
    <div id="app" class="score-case">
      <div class="table">
        <table>
          <thead>
            <tr>
              <th>编号</th>
              <th>科目</th>
              <th>成绩</th>
              <th>操作</th>
            </tr>
          </thead>

          <tbody v-if="list.length > 0">
            <tr v-for="(item,index) in list" :key="item.id">
              <td>{{ index+1 }}</td>
              <td>{{ item.subject }}</td>
              <!-- 不及格标红 -->
              <td :class="{ red: item.score < 60 }">{{item.score}}</td>
              <td><a @click.prevent="del(item.id)" href="www.baidu.com">删除</a></td>
            </tr>
            
          </tbody>

          <tbody v-else>
            <tr>
              <td colspan="5">
                <span class="none">暂无数据</span>
              </td>
            </tr>
          </tbody>

          <tfoot>
            <tr>
              <td colspan="5">
                <span>总分:{{ totalScore }}</span>
                <span style="margin-left: 50px">平均分:{{ averageScore }}</span>
              </td>
            </tr>
          </tfoot>
        </table>
      </div>
      <div class="form">
        <div class="form-item">
          <div class="label">科目:</div>
          <div class="input">
            <input
              type="text"
              placeholder="请输入科目"
              v-model.trim="subject"
            />
          </div>
        </div>
        <div class="form-item">
          <div class="label">分数:</div>
          <div class="input">
            <input
              type="text"
              placeholder="请输入分数"
              v-model.number="score"
            />
          </div>
        </div>
        <div class="form-item">
          <div class="label"></div>
          <div class="input">
            <button @click="add" class="submit" >添加</button>
          </div>
        </div>
      </div>
    </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, subject: '语文', score: 20 },
            { id: 7, subject: '数学', score: 99 },
            { id: 12, subject: '英语', score: 70 },
          ],
          subject: '',
          score: ''
        },
        methods: {
          del(id){
            //filter():筛选,根据id判断筛选内容是否符合条件,若不等则返回给list
            this.list = this.list.filter(item => item.id !== id)
          },
          add(){
            if(!this.subject){
              alert("请输入科目")
              return
            }
            if(typeof this.score !== 'number'){
              alert("请输出正确的成绩")
              return
            }
            //往前面加unshift,往后面加push
            this.list.unshift({
              id: +new Date(),
              subject: this.subject,
              score: this.score
            })
            this.subject=''
            this.score=''
          }
        },
        computed:{
          totalScore(){
            return this.list.reduce((sum, item) => sum+item.score , 0)
          },
          averageScore(){
            if(this.list.length === 0){
              return 0
            }
            //计算属性可以直接使用,toFixed(2)保留两位小数
            return (this.totalScore / this.list.length).toFixed(2)
          }
        }
      })
    </script>
  </body>
</html>

12.watch侦听器(监视器)

  • 作用:监听数据变化,执行一些业务逻辑或异步操作
  • 语法:
  1. 简单写法:
    简单类型数据,直接监视
data:{
	words:'苹果',
	obj:{
		words:'苹果'
	}
},

watch: {
	数据属性名(newValue, oldValue) {
		一些业务逻辑 或 异步操作
	},
	'对象.属性名'(newValue, oldValue) {
		一些业务逻辑 或 异步操作
	}
	
}

案例:
需求:输入内容,实时翻译
在这里插入图片描述
代码实现:

<!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>Document</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
        },
        // 具体讲解:(1) watch语法 (2) 具体业务实现
        watch: {
          // 该方法会在数据变化时调用执行
          // newValue新值, oldValue老值(一般不用)
          // words (newValue) {
          //   console.log('变化了', newValue)
          // }

           'obj.words' (newValue) {
            //console.log('变化了', newValue)
            //防抖:延迟执行 -> 干啥事先等一等,延迟一会,一段时间内没有再次触发,才执行
            clearTimeout(this.timer)
            this.timer = setTimeout(async () => {
              //这里是ajax的内容
              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>

  1. 完整写法
    添加额外配置项
    (1)deep:true 对复杂类型深度监视
    (2)immediate:true 初始化立刻执行一次handler方法
data:{
	obj:{
		words:'苹果',
		lang:'italy'
	}
},

watch: {
	数据属性名(newValue, oldValue) {
		deep: true,//深度监视
		handler(newValue){
			console.log(newValue)
		}
	}
}

案例演示:
需求:在上个案例的基础上,输入内容,修改语言,实时翻译
代码:

<!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>Document</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 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: {
          // words: ''
          obj: {
            words: '',
            lang:'italy'
          },

          result:'',//翻译结果
          timer: null
        },
        // 具体讲解:(1) watch语法 (2) 具体业务实现
        watch: {
          obj: {
            deep: true, //深度监视
            immediate: true, //立刻执行,一进入页面handler立刻执行
            handler( newValue ){
            //   //防抖:延迟执行 -> 干啥事先等一等,延迟一会,一段时间内没有再次触发,才执行
            clearTimeout(this.timer)
            this.timer = setTimeout(async () => {  
              //这里是ajax的内容
              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)
            }
          }
        }
      })
    </script>
  </body>
</html>

综合案例 - 水果购物车

  • 案例说明:
    完成一个水果购物车网页,如图所示
    在这里插入图片描述

  • 需求说明:

  1. 渲染功能
    v-if v-else:渲染body
    v-for:遍历水果数组
    :class:设置选中的css效果
  2. 删除功能
    点击传参@click,filter过滤覆盖原数组
  3. 修改个数
    点击传参@click,find找对象
  4. 全选反选
    计算属性computed(完整写法)
  5. 统计选中的总价和总数量
    计算属性computed,reduce条件求和
  6. 持久化到本地
    watch监视,localStorage,JSON.stringify,JSON.parse
  • 完整代码:
<!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" />
    <link rel="stylesheet" href="./css/inputnumber.css" />
    <link rel="stylesheet" href="./css/index.css" />
    <title>购物车</title>
  </head>
  <body>
    <div class="app-container" id="app">
      <!-- 顶部banner -->
      <div class="banner-box"><img src="./img/fruit.jpg" alt="" /></div>
      <!-- 面包屑 -->
      <div class="breadcrumb">
        <span>🏠</span>
        /
        <span>购物车</span>
      </div>
      <!-- 购物车主体 -->
      <div class="main" v-if="fruitList.length > 0">
        <div class="table">
          <!-- 头部 -->
          <div class="thead">
            <div class="tr">
              <div class="th">选中</div>
              <div class="th th-pic">图片</div>
              <div class="th">单价</div>
              <div class="th num-th">个数</div>
              <div class="th">小计</div>
              <div class="th">操作</div>
            </div>
          </div>
          <!-- 身体 -->
          <div class="tbody">
            <div v-for="(item, index) in fruitList" :key="item.id" class="tr" :class="{ active: item.isChecked }">
              <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
              <div class="td"><img :src="item.icon" alt="" /></div>
              <div class="td">{{ item.price }}</div>
              <div class="td">
                <div class="my-input-number">
                  <button :disabled="item.num <= 1" class="decrease" @click="sub(item.id)"> - </button>
                  <span class="my-input__inner">{{ item.num }}</span>
                  <button class="increase" @click="add(item.id)"> + </button>
                </div>
              </div>
              <div class="td">{{ item.num * item.price }}</div>
              <div class="td"><button @click="del(item.id)">删除</button></div>
            </div>
          </div>
        </div>
        <!-- 底部 -->
        <div class="bottom">
          <!-- 全选 -->
          <label class="check-all">
            <input type="checkbox" v-model="isAll" />
            全选
          </label>
          <div class="right-box">
            <!-- 所有商品总价 -->
            <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;<span class="price">{{totalPrice}}</span></span>
            <!-- 结算按钮 -->
            <button class="pay">结算( {{totalCount}} )</button>
          </div>
        </div>
      </div>
      <!-- 空车 -->
      <div class="empty" v-else>🛒空空如也</div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
      const defaultArr= [
      {
        id: 1,
        icon: './img/火龙果.png',
        isChecked: true,
        num: 2,
        price: 6,
      },
      {
        id: 2,
        icon: './img/荔枝.png',
        isChecked: false,
        num: 7,
        price: 20,
      },
      {
        id: 3,
        icon: './img/榴莲.png',
        isChecked: false,
        num: 3,
        price: 40,
      },
      {
        id: 4,
        icon: './img/鸭梨.png',
        isChecked: true,
        num: 10,
        price: 3,
      },
      {
        id: 5,
        icon: './img/樱桃.png',
        isChecked: false,
        num: 20,
        price: 34,
      }
      ]
      const app = new Vue({
        el: '#app',
        data: {
          // 水果列表
          fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr,
        },
        methods:{
          del(id){
            this.fruitList = this.fruitList.filter(item => item.id !== id)
          },
          add (id) {
            const fruit = this.fruitList.find(item => item.id === id)
            fruit.num++;
          },
          sub (id) {
            const fruit = this.fruitList.find(item => item.id === id)
            fruit.num--;
          }
        },
        computed:{

          isAll:{
            get () {
              return this.fruitList.every(item => item.isChecked)
            },
            set (value) {
              //基于拿到的布尔值,要让所有的小选框同步状态
              this.fruitList.forEach(item => {
                item.isChecked = value
              });
            }
          },
          //统计选中的总数
          totalCount(){
            return this.fruitList.reduce((sum,item) => {
              if (item.isChecked){
                return sum+item.num
              }else{
                return sum
              }
            } , 0)
          },
          //统计选中的总价
          totalPrice(){
            return this.fruitList.reduce((sum,item) => {
              if(item.isChecked){
                return sum+item.num*item.price
              }else{
                return sum
              }
            }, 0)
          }

        },
        watch: {
          fruitList: {
            deep:true,
            handler(newValue){
              //将变化后的newValue存入本地(转JSON)
              localStorage.setItem('list',JSON.stringify(newValue))
            }
          }
        }
      })
    </script>
  </body>
</html>

第三天

13.Vue生命周期和生命周期的四个阶段

  • Vue生命周期:一个Vue实例从创建到校徽的整个过程
  • 生命周期四个阶段
  1. 创建阶段:创建响应式数据
    发送初始化渲染请求
  2. 挂载阶段:渲染模版
    操作dom
  3. 更新阶段:修改数据、更新视图
  4. 销毁阶段:销毁实例
  • 钩子函数
    Vue生命周期过程中,会自动运行一些函数,被称为生命周期钩子,让开发者可以在特定阶段运行自己的代码
    在这里插入图片描述

综合案例-小黑记账清单

  • 案例说明:完成下图页面,实现增删,消费总计,与饼图渲染功能

在这里插入图片描述

  • 核心思路分析:
  1. 基本渲染
    (1)发送请求,获取数据,封装起来,再在created中调用
    (2)将获取到的数据存到data中
    (3)结合数据,v-for进行渲染
    (4)用计算属性完成消费统计功能
  2. 添加功能
    (1)收集表单数据,用v-model进行关联
    (2)给添加按钮注册点击事件,发送添加请求
    (3)重新渲染
  3. 删除功能
    (1)给’删除’注册点击事件,传参数id
    (2)根据id发送删除请求
    (3)重新渲染
  4. 饼图渲染
    (1)在mounted中初始化echarts实例
    (2)渲染函数中setOption,动态更新饼图
  • 代码实现
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</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 }}</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}}</td>
            </tr>
          </tfoot>
        </table>
      </div>

      <!-- 右侧图表 -->
      <div class="echarts-box" id="main"></div>
    </div>
  </div>
  <script src="https://cdn.bootcss.com/echarts/3.7.1/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.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: {
        list: [],
        name: '',
        price: ''
      },
      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: [

              ],
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        })
      },

      computed: {
        totalPrice() {
          return this.list.reduce((sum, item) => sum + item.price, 0)
        }

      },
      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: 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) {
          const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)
          this.getlist()
        }
      }
    })
  </script>
</body>

</html>

14. 工程化开发和脚手架Vue CLI

开发Vue的两种方式:

  1. 核心包传统开发模式:基于 html / css / js 文件,直接引入核心包,开发Vue
  2. 工程化开发模式:基于构建工具(例如: webpack)的环境中开发Vue

脚手架的基本介绍

Vue CLI 是 Vue 官方提供的一个全局命令工具
可以帮助我们快速创建一个开发Vue项目的标准化基础架子【集成 webpack 配置】

安装教程:(前置条件需要node环境)

  1. 右键单击win开始菜单
    在这里插入图片描述
    在弹出的窗口中,选择Windows PowerShell(管理员)
  2. 在窗口中输入
npm i @vue/cli -g

在这里插入图片描述

(用yarn一直没装上,在这里选了npm)

  1. 安装完成后,用以下命令检验安装
vue --verison

出现以下信息,表示安装成功
在这里插入图片描述

创建项目和启动项目

1.打开vscode,在你想要创建项目的位置打开中断,并输入以下命令

vue create 你的项目名称

在这里插入图片描述

  1. 出现以下信息,选择第二个vue 2项目
    在这里插入图片描述
    出现以下信息,表示创建成功
    在这里插入图片描述
  2. 按照控制台的提示信息启动项目
    分别输入以下命令
cd 你的项目名称
npm run serve
  1. 进入控制台提示信息中的两个网址
    在这里插入图片描述
    出现以下画面表示运行成功
    在这里插入图片描述

15.组件化开发和根组件

  • 组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为
    好处:便于维护,利于复用,有利于提升开发效率
    组件分类:普通组件、根组件
  • 根组件:整个应用最上层的组件,包括所有普通小组件

App.vue文件(单文件组成的三个组成部分)

  1. 语法高亮插件:Vetur
  2. 三部分组成
  • template:结构(Vue2中有且只能一个元素)
  • script:js逻辑
  • style:样式(可支持less,需要装包)
    具体结构如以下代码所示
<template>
  <!-- Vue2中只能有一个根元素 -->
  <div class="app" @click="fn()">
    我是结构
  </div>
</template>

<script>
export default{
  methods:{
    fn(){
      alert("hello")
    }
  }
}
</script>


<style lang="less">

.app {
width: 400px;
height: 400px;
background-color: pink;
}

</style>
  • 安装less的方法:
    在控制台输入以下命令,即可安装成功(此处为用npm安装的方式)
npm install less-loader less --save-dev

在这里插入图片描述

16.普通组件的注册使用

组件注册的两种方式:

  1. 局部注册
  • 创建.vue文件(三个组成部分:Header,Mian,Footer)
  • 在使用的组件内导入并注册
  1. 全局注册
  • 创建.vue文件(三个组成部分)
  • main.js中进行全局注册
    eg.
//导入需要全局注册的组件
import HmButton from './components/HmButton'

//调用Vue.component进行全局注册
//Vue.component('组件名',组件对象)
Vue.component('HmButton', HmButton)

使用:

  • 当成html标签使用
<组件名></组件名>
  • 组件名规范:驼峰命名法
    eg:HmHeader

局部注册使用范例:

在这里插入图片描述

<!-- App.vue文件 -->
<script>
import HmHeader from './components/HmHeader.vue'
import HmMain from './components/HmMain.vue'
import HmFooter from './components/HmFooter.vue'

export default {
  components:{
    HmHeader:HmHeader,
    HmMain,
    HmFooter
  }
}
</script>
Logo

前往低代码交流专区

更多推荐