vue的学习(上)

前言:

本篇主要讲一下vue的基本语法,!

这里可以直接看他的语雀笔记Vue核心 Vue简介 初识 · 语雀 (yuque.com)

啊啊啊他写的太好了, 建议直接看他的, 如果愿意多看一下我的话, 就都看吧。

vue简介及环境搭建:

如果建议别用管理员cmd

选择器类别小贴士

vue相关的前端配置

引入vue的包或者下载:

//开发版本:
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

//生产版本:
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>


//通过相对路径引入:
<script src="./js/vue.js"></script>


vue基本介绍:

  1. 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象

  2. root 容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法

  3. root 容器里的代码被称为Vue模板

  4. Vue 实例与容器是一一对应的

  5. 真实开发中只有一个Vue实例,并且会配合着组件一起使用

  6. {{xxx}}中的 xxx 要写 js 表达式,且 xxx 可以自动读取到data中的所有属性

  7. 一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新

注意区分:js 表达式 和 js代码(语句)

js表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
a	a+b		demo(1)		x === y ? 'a' : 'b'
js代码(语句)
if(){}		for(){}

第一个简单的vue程序:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>初识Vue</title>

    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>

  </head>
  <body>

    <!-- 准备好一个容器 -->
    <div id="demo">
      <h1>Hello,{{ name.toUpperCase() }},{{ address }}</h1>
    </div>

    <script type="text/javascript" >
      Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

      // 创建Vue实例
      new Vue({
        el: '#demo',	// el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
        data: { 			// data中用于存储数据,数据供el所指定的容器去使用,值暂时先写成一个对象
          name: 'cess',
          address: '成都'
        }
      })
    </script>
  </body>
</html>

常用指令:

v-test和v-html:

当我们网速很慢或者javascript出错时,会显示{undefined{xxx}};Vue提供的v-text、v-html可以解决这个问题;

v-test

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-text指令</title>
</head>

<body>
    <div id="app">
        <!--字符串的拼接依旧是加号-->
        <h2 v-text="message+'!'">成都</h2>
        <h2 v-text="info+'!'">成都</h2><!--v-text一定会替换掉内部的默认值-->
        <h2>{{ message +'!'}}--成都</h2><!--这里深圳没被替换, 因为这里用了两个大括号, 只是具备替换-->
    </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
                message:"兴趣使然的小小!!!",
                info:"计算机学院"
            }
        })
    </script>
</body>

</html>

v-html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-html指令</title>
</head>

<body>
    <div id="app">
        <!--html结构-->
        <p v-text="content"></p>
        <p v-html="content"></p>
    </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
                content:"兴趣使然的小小",
                content:"<a href='https://ladfeng.top'>兴趣使然的小小</a>"

            }
        })
    </script>
</body>

</html>

(事件监听)v-on:

v-on 就是监听事件,可以用v-on指令监听DOM事件来触发一些javascript代码;

@v-on的缩写!

@clickv-on:click

@dblclickv-on:dblclick

一些按键事件@keydown和@keyup

image-20220422171258424

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>键盘事件</title>
    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>
  </head>
  <body>

    <div id="root">
      <h2>欢迎打开{{name}}笔记</h2>
      <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"><br/>
      <input type="text" placeholder="按下tab提示输入" @keydown.tab="showInfo"><br/>
      <input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo"><br/>
    </div>

    <script type="text/javascript">
      Vue.config.productionTip = false	// 阻止 vue 在启动时生成生产提示。
      Vue.config.keyCodes.huiche = 13		// 定义了一个别名按键

      new Vue({
        el: '#root',
        data: {
          name: 'cess'
        },
        methods: {
          showInfo(e) {
            // console.log(e.key,e.keyCode)
            console.log(e.target.value)
          }
        },
      })
    </script>
  </body>
</html>
  1. 使用v-on:xxx或@xxx绑定事件,其中 xxx 是事件名

  2. 事件的回调需要配置在methods对象中,最终会在vm上

  3. methods中配置的函数,不要用箭头函数,否则 this 就不是vm了

  4. methods中配置的函数,都是被 Vue所管理的函数,this 的指向是vm或组件实例对象

  5. @click="demo"和@click="demo($event)"效果一致,但后者可以传参

demo2

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>事件的基本使用</title>
    <!-- 引入Vue -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="root">
    <h2>欢迎来看{{name}}的笔记</h2>
    <!-- <button v-on:click="showInfo">点我提示信息</button> -->
    <button @click="showInfo1">点我提示信息1(不传参)</button>
    <button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    const vm = new Vue({
        el: '#root',
        data: {
            name: '小小',
        },
        methods: {
            showInfo1(event) {
                console.log(event.target.innerText)
                // console.log(this) // 此处的this是vm
                alert('同学你好!')
            },
            showInfo2(event, number) {
                console.log(event, number)
                console.log(event.target.innerText)
                // console.log(this) // 此处的this是vm
                alert('同学你好!!'+number)
            }
        }
    })
</script>
</body>
</html>

更多的事件处理Vue核心 事件处理 · 语雀 (yuque.com)

demo3

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>事件修饰符</title>
    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
        * {margin-top: 20px;}
        .demo1 {height: 50px;background-color: skyblue;}
        .box1 {padding: 5px;background-color: skyblue;}
        .box2 {padding: 5px;background-color: white;}
        .list {width: 200px;height: 200px;background-color: skyblue;overflow: auto;}
        li {height: 100px;}
    </style>
</head>
<body>

<div id="root">
    <h2>欢迎来到{{ name }}学习</h2>
    <!-- 阻止默认事件(常用) -->
    <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>

    <!-- 阻止事件冒泡(常用) -->
    <div class="demo1" @click="showInfo">
        <button @click.stop="showInfo">点我提示信息</button>
        <!-- 修饰符可以连续写 -->
        <!-- <a href="http://www.qq.com" @click.prevent.stop="showInfo">点我提示</a> -->
    </div>

    <!-- 事件只触发一次(常用) -->
    <button @click.once="showInfo">点我提示信息</button>

    <!-- 使用事件的捕获模式 -->
    <div class="box1" @click.capture="showMsg(1)">
        div1
        <div class="box2" @click="showMsg(2)">
            div2
        </div>
    </div>

    <!-- 只有event.target是当前操作的元素时才触发事件; -->
    <div class="demo1" @click.self="showInfo">
        <button @click="showInfo">点我提示信息</button>
    </div>

    <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; -->
    <!-- scroll是滚动条滚动,passsive没有影响 -->
    <!-- wheel是鼠标滚轮滚动,passive有影响 -->
    <ul @wheel.passive="demo" class="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false

    new Vue({
        el: '#root',
        data: {
            name: '尚硅谷'
        },
        methods: {
            showInfo(e) {
                alert('同学你好!')
                // console.log(e.target)
            },
            showMsg(msg) {
                console.log(msg)
            },
            demo() {
                for (let i = 0; i < 100000; i++) {
                    console.log('#')
                }
                console.log('累坏了')
            }
        }
    })
</script>
</body>
</html>

(数据绑定)v-model:

Vue中有两种数据绑定的方式:

  1. 单向绑定v-bind数据只能从data流向页面
  2. 双向绑定v-model数据不仅能从data流向页面, 还可以从页面流向data

获取和设置表单元素的值(双向数据绑定)

demo1:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>数据绑定</title>
    <!-- 引入Vue -->

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="root">
    <!-- 普通写法 -->
    <!-- 单向数据绑定:<input type="text" v-bind:value="name"><br/> -->
    <!-- 双向数据绑定:<input type="text" v-model:value="name"><br/> -->

    <!-- 简写 -->
    单向数据绑定:<input type="text" :value="name"><br/>
    双向数据绑定:<input type="text" v-model="name"><br/>

    <!-- 如下代码是错误的,因为 v-model 只能应用在表单类元素(输入类元素)上 -->
    <!-- <h2 v-model:x="name">你好啊</h2> -->
</div>

<script type="text/javascript">
    Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示。

    new Vue({
        el: '#root',
        data: {
            name: 'cess'
        }
    })
</script>
</body>
</html>

demo2

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-model指令</title>
</head>

<body>
    <div id="app">
        <input type="button" value="重置message" @click="setM">
        <!--当敲击的键盘起来的,就触发事件getM-->
        <input type="text" v-model="message" @keyup.enter="getM">
        <h2>{{ message }}</h2>
    </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
                message:"兴趣使然的小小"
            },
            methods: {
                getM:function(){
                    alert("浏览器已经收到了message: "+this.message);
                },
                setM:function(){
                    this.message ="兴趣使然的小小";
                }
            },
        })
    </script>
</body>

</html>

(数据绑定和样式绑定)v-bind:

v-bind: 用来绑定标签的属性从而通过vue动态修改标签的属性

v-bind:属性名 简化之后 :属性名

demo1:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>模板语法</title>
    <!-- 引入Vue -->

    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

</head>
<body>

<div id="root">
    <h2>插值语法</h2>
    <h4>你好,{{ name }}</h4>
    <hr />
    <h2>指令语法</h2>
    <a v-bind:href="tencent.url.toUpperCase()" x="hello">点我去看{{ tencent.name }}1</a>
    <a :href="tencent.url" x="hello">点我去看{{ tencent.name }}2</a>
</div>
</body>

<script type="text/javascript">
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    new Vue({
        el: '#root',
        data: {
            name: '小小',
            tencent: {
                name: '开端',
                url: 'https://v.qq.com/x/cover/mzc00200mp8vo9b/n0041aa087e.html',
            }
        }
    })
</script>
</html>

demo02

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-bind指令</title>
    <style>
        .active{
            border: 5px solid red;
        }
    </style>
</head>

<body>
    <div id="app">
        <!--原始的写法-->
        <img v-bind:src="imgSrc" alt="">
        <br>
        点击下面的一张图片!<br>
        <!--缩写冒号就行!-->
<!--        <img v-bind:src="imgSrc" alt="这里是没有图片的时候显示的文本!" v-bind:title="imgTitle+'的图片!!!'" v-bind:class="isActive?'active':''" v-on:click="toggleActive">&lt;!&ndash;这里有用到三元运算符!&ndash;&gt;-->
        <img
                :src="imgSrc"
                alt="这里是没有图片的时候显示的文本!"
                :title="imgTitle+'的图片!!!'"
                :class="isActive?'active':''"
                @click="toggleActive"
        ><!--这里有用到三元运算符!-->

        <br>
        <img
                :src="imgSrc"
                alt=""
                :title="imgTitle+'!!!'"
                :class="{active:isActive}"
                @click="toggleActive"
        ><!--用对象的形式!推荐使用! -->
    </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
                imgSrc:"https://qiniu.ladidol.top/img/image-20220421111816375.png",
                imgTitle:"小小",
                isActive:false
            },
            methods: {
                toggleActive:function(){
                    this.isActive = !this.isActive;
                }
            },
        })
    </script>
</body>
</html>

Vue核心 计算属性 侦听属性 · 语雀 (yuque.com)

特别提一下computed计算属性

  1. 定义:要用的属性不存在,需要通过已有属性计算得来

  2. 原理:底层借助了Objcet.defineproperty()方法提供的getter和setter

  3. get函数什么时候执行?
    a初次读取时会执行一次
    b当依赖的数据发生改变时会被再次调用

  4. 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便

  5. 备注
    a计算属性最终会出现在vm上,直接读取使用即可
    b如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
    c如果计算属性确定不考虑修改,可以使用计算属性的简写形式

使用computed计算属性之前

<title>姓名案例_methods实现</title>
<script type="text/javascript" src="../js/vue.js"></script>

<div id="root">
  姓:<input type="text" v-model="firstName"><br/>
  名:<input type="text" v-model="lastName"><br/>
  全名:<span>{{ fullName() }}</span>
</div>

<script type="text/javascript">
  Vue.config.productionTip = false
  new Vue({
    el: '#root',
    data: {
      firstName: '张',
      lastName: '三'
    },
    methods: {
      fullName() {
        return this.firstName + '-' + this.lastName
      }
    },
  })
</script>

之后

<title>姓名案例_计算属性实现</title>
<script type="text/javascript" src="../js/vue.js"></script>

<div id="root">
  姓:<input type="text" v-model="firstName"> <br/>
  名:<input type="text" v-model="lastName"> <br/>
  测试:<input type="text" v-model="x"> <br/>	// 这里修改 不会调 fullName的get方法
  全名:<span>{{fullName}}</span> <br/>
  <!-- 全名:<span>{{fullName}}</span> <br/> -->
  <!-- 全名:<span>{{fullName}}</span> <br/> -->
</div>

<script type="text/javascript">
  Vue.config.productionTip = false
  const vm = new Vue({
    el: '#root',
    data: {
      firstName:'张',
      lastName:'三',
      x:'你好'
    },
    computed: {
      //完整写法
      // fullName: {
      // 	get() {
      // 		console.log('get被调用了')
      // 		return this.firstName + '-' + this.lastName
      // 	},
      // 	set(value) {
      // 		console.log('set', value)
      // 		const arr = value.split('-')
      // 		this.firstName = arr[0]
      // 		this.lastName = arr[1]
      // 	}
      // }

      // 简写
      fullName() {
        console.log('get被调用了')
        return this.firstName + '-' + this.lastName
      }
    }
  })
</script>

这里还可以提到watch属性

绑定样式

Vue核心 绑定样式 条件渲染 · 语雀 (yuque.com)

<style>
  .basic {width: 300px;height: 50px;border: 1px solid black;}
  .happy {border: 3px solid red;background-color: rgba(255, 255, 0, 0.644);
    background: linear-gradient(30deg, yellow, pink, orange, yellow);}
  .sad {border: 4px dashed rgb(2, 197, 2);background-color: skyblue;}
  .normal {background-color: #bfa;}
  .atguigu1 {background-color: yellowgreen;}
  .atguigu2 {font-size: 20px;text-shadow: 2px 2px 10px red;}
  .atguigu3 {border-radius: 20px;}
</style>

<div id="root">
  <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
  <div class="basic" :class="mood" @click="changeMood">{{name}}</div><br/><br/>

  <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
  <div class="basic" :class="classArr">{{name}}</div><br/><br/>

  <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
  <div class="basic" :class="classObj">{{name}}</div><br/><br/>

  <!-- 绑定style样式--对象写法 -->
  <div class="basic" :style="styleObj">{{name}}</div><br/><br/>

  <!-- 绑定style样式--数组写法 -->
  <div class="basic" :style="styleArr">{{name}}</div>
</div>

<script type="text/javascript">
  Vue.config.productionTip = false

  const vm = new Vue({
    el: '#root',
    data: {
      name: '尚硅谷',
      mood: 'normal',
      classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
      classObj: {
        atguigu1: false,
        atguigu2: false,
      },
      styleObj: {
        fontSize: '40px',
        color: 'red',
      },
      styleObj2: {
        backgroundColor: 'orange'
      },
      styleArr: [
        {
          fontSize: '40px',
          color: 'blue',
        },
        {
          backgroundColor: 'gray'
        }
      ]
    },
    methods: {
      changeMood() {
        const arr = ['happy', 'sad', 'normal']
        const index = Math.floor(Math.random() * 3)
        this.mood = arr[index]
      }
    },
  })
</script>

v-if和v-show:

简单的判断语法, 其实功能有点类似

v-show

v-show:用来控制页面中某个标签元素是否展示 底层使用控制是 display 属性

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>v-show指令</title>
  </head>
  <body>
    <div id="app">
      <input type="button" value="切换显示状态" @click="changeIsShow">
      <input type="button" value="累加年龄" @click="addAge">
      <span>{{age}}</span>
      <img v-show="isShow" src="https://qiniu.ladidol.top/img/image-20220421111715822.png" alt="">
      <img v-show="age>=18" src="https://qiniu.ladidol.top/img/image-20220421111816375.png" alt="">
    </div>
    <!-- 1.开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      var app = new Vue({
        el:"#app",
        data:{
          isShow:false,
          age:10
        },
        methods: {
          changeIsShow:function(){
            this.isShow = !this.isShow;
          },
          addAge:function(){
            this.age++;
          }
        },
      })
    </script>
  </body>
</html>

v-if

v-if: 用来控制页面元素是否展示 底层控制是DOM元素 操作DOM

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-if指令</title>
</head>
<body>
    <div id="app">
        <input type="button" value="切换显示" @click="toggleIsShow">
        <p v-if="isShow">兴趣使然的小小</p>
        <p v-show="isShow">ladidol的  v-show修饰</p><br>
        <span>现在的温度: {{temperature}}℃</span>
        <h2 v-if="temperature>=35">这个温度我要热死啦</h2>
    </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
                isShow:false,
                temperature:40
            },
            methods: {
                toggleIsShow:function(){
                    this.isShow = !this.isShow;
                }
            },
        })
    </script>
</body>

</html>
条件渲染

配合template标签

使用 保留页面加载时隐藏的内容。通常使用 JavaScript 来显示

template

<title>条件渲染</title>
<script type="text/javascript" src="../js/vue.js"></script>

<div id="root">
  <h2>当前的n值是:{{ n }}</h2>
  <button @click="n++">点我n+1</button>

  <!-- 使用v-show做条件渲染 -->
  <!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
  <!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->

  <!-- 使用v-if做条件渲染 -->
  <!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
  <!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->

  <!-- v-else和v-else-if -->
  <!-- <div v-show="n === 1">Angular</div> -->
  <!-- <div v-show="n === 2">React</div> -->
  <!-- <div v-show="n === 3">Vue</div> -->

  <!-- <div v-if="n === 1">Angular</div> -->
  <!-- <div v-else-if="n === 2">React</div> -->
  <!-- <div v-else-if="n === 3">Vue</div> -->
  <!-- <div v-else>哈哈</div> -->


  <!-- v-if与template的配合使用 -->
  <template v-if="n === 1">
    <h3>你好</h3>
    <h3>尚硅谷</h3>
    <h3>北京</h3>
  </template>

</div>

<script type="text/javascript">
  Vue.config.productionTip = false
  const vm = new Vue({
    el:'#root',
    data:{
      name:'尚硅谷',
      n:0
    }
  })
</script>

v-for:

v-for指令是循环渲染一组data中的数组,需要以 item in items 或者(item,index) in items形式,items是源数据数组并且item是数组元素迭代的别名。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>v-for指令</title>
</head>

<body>
    <div id="app">
        <input type="button" value="添加数据" @click="add">
        <input type="button" value="移除数据" @click="remove">

        <ul>
            <!--index和value-->
            <li v-for="(it,index) in school">
                {{ index+1 }}学校:{{ it }}
            </li>
        </ul>
        <!--这里只遍历了, value-->
        <h2 v-for="item in vegetables" v-bind:title="item.name">
            {{ item.name }}
        </h2>
    </div>
    <!-- 1.开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el:"#app",
            data:{
                school:["丰禾镇中心小学","四川省邻水实验学校","成都信息工程大学"],
                vegetables:[
                    {name:"西兰花炒蛋"},
                    {name:"蛋炒西蓝花"},
                    {name:"鱼香肉丝"},
                    {name:"麻婆豆腐"}
                ]
            },
            methods: {
                add:function(){
                    this.vegetables.push({ name:"锅巴土豆!" });
                },
                remove:function(){
                    //删除第一个元素
                    // this.vegetables.shift();
                    //删除最后一个!
                    this.vegetables.pop();
                }
            },
        })
    </script>
</body>

</html>

v-bind:key是为了在v-for循环中给 Vue 一个提示

内置命令

Vue核心 内置指令 自定义指令 · 语雀 (yuque.com)

v-cloak

<title>v-cloak指令</title>

<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

<style>
    [v-cloak] {
        display:none;
    }
</style>

<div id="root">
    <h2 v-cloak>{{ name }}</h2>
    <h2>{{ name }}</h2>
</div>

<!--// 够延迟5秒收到vue.js-->
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>

<script type="text/javascript">
    console.log(1)
    Vue.config.productionTip = false
    new Vue({
        el:'#root',
        data:{name:'小小'}
    })
</script>

image-20220506231241749

到此可以简单了解一下axios和vue的结合:

后端理解ajax和axios_兴趣使然的小小的博客-CSDN博客

数组的常用方法:

image-20220422170629381

Vue生命周期

Vue核心 Vue生命周期 · 语雀 (yuque.com)

生命周期.png

基础语法案例汇总:

计数器:

image-20220421115308023

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      #app {
        width: 480px;
        height: 100px;
        margin: 200px auto;
      }
      .input-num {
        margin-top: 20px;
        height: 100%;
        display: flex;
        border-radius: 10px;
        overflow: hidden;
        box-shadow: 0 0 4px black;
      }
      .input-num button {
        width: 150px;
        height: 100%;
        font-size: 40px;
        color: gray;
        cursor: pointer;
        border: none;
        outline: none;
      }
      .input-num span {
        height: 100%;
        font-size: 40px;
        flex: 1;
        text-align: center;
        line-height: 100px;
      }
    </style>
  </head>
  <body>
    <div id="app">
<!--      <img src="" alt="" />-->
      <!-- 计数器 -->
      <div class="input-num">
        <button @click="sub">-</button>
        <span>{{num}}</span>
        <button @click="add">+</button>
      </div>
    </div>
  </body>
</html>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 编码 -->
<script>
  // 创建Vue实例
  var app = new Vue({
    el: "#app",
    data: {
      num: 1,
      max: 10,
      min: 0
    },
    methods: {
      add: function() {
        if(this.num<this.max){//注意这里要带上this这个关键字
          this.num++;
        }else{
          alert("别点了,最大了");
        }
      },
      sub: function() {
        if(this.num>this.min){
          this.num--;
        }else{
          alert("别点了,最小了");
        }
      }
    }
  });
</script>

图片切换案例:

image-20220421115339444

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      html,
      body,
      #mask {
        width: 100%;
        height: 100%;
      }

      #mask {
        background-color: #c9c9c9;
        position: relative;
      }

      #mask .center {
        position: absolute;
        background-color: #fff;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        padding: 10px;
      }
      #mask .center .title {
        position: absolute;
        display: flex;
        align-items: center;
        height: 56px;
        top: -61px;
        left: 0;
        padding: 5px;
        padding-left: 10px;
        padding-bottom: 0;
        color: rgba(175, 47, 47, 0.8);
        font-size: 26px;
        font-weight: normal;
        background-color: white;
        padding-right: 50px;
        z-index: 2;
      }
      #mask .center .title img {
        height: 40px;
        margin-right: 10px;
      }

      #mask .center .title::before {
        content: "";
        position: absolute;
        width: 0;
        height: 0;
        border: 65px solid;
        border-color: transparent transparent white;
        top: -65px;
        right: -65px;
        z-index: 1;
      }

      #mask .center > img {
        display: block;
        width: 700px;
        height: 458px;
      }

      #mask .center a {
        text-decoration: none;
        width: 45px;
        height: 100px;
        position: absolute;
        top: 179px;
        vertical-align: middle;
        opacity: 0.5;
      }
      #mask .center a :hover {
        opacity: 0.8;
      }

      #mask .center .left {
        left: 15px;
        text-align: left;
        padding-right: 10px;
        border-top-right-radius: 10px;
        border-bottom-right-radius: 10px;
      }

      #mask .center .right {
        right: 15px;
        text-align: right;
        padding-left: 10px;
        border-top-left-radius: 10px;
        border-bottom-left-radius: 10px;
      }

    </style>
  </head>

  <body>
    <div id="mask">
      <div class="center">

        <h2 class="title">
          <img src="https://qiniu.ladidol.top/img/image-20220421114425049.png"
               alt=""> 小小的博客乐园!
        </h2>

        <!--用数组来存图片,通过改变index来改变展示的图片!-->
        <img :src="imgList[index]" alt="" />
        <a
          href="javascript:void(0)"
          @click="prev"
          class="left"
          v-show="index>0"
        >
          <img src="https://qiniu.ladidol.top/img/image-20220421114038121.png" alt="" />
        </a>
        <!--如果超过范围, 就不显示按钮!-->
        <a
          href="javascript:void(0)"
          @click="next"
          class="right"
          v-show="index<imgList.length-1"
        >
          <img src="https://qiniu.ladidol.top/img/image-20220421114013041.png" alt="" />
        </a>
      </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
   
    <script>
      const app = new Vue({
        el: "#mask",
        data: {
          imgList: [
            "https://qiniu.ladidol.top/img/image-20220421114859124.png",
            "https://qiniu.ladidol.top/img/image-20220421114839120.png",
            "https://qiniu.ladidol.top/img/image-20220421114817142.png",
          ],
          index: 0
        },
        methods: {
          // 上一张
          prev() {
            this.index--;
          },
          // 下一张
          next() {
            this.index++;
          }
        }
      });
    </script>
  </body>
</html>

天知道:

Vue网络应用-axios的基本使用

axios.get(地址?key=value&key2=values).then(function(response){},function(err){})
axios.post(地址,{key:value,key2:value2}).then(function(response){},function(err){})

emm依旧一些不理解, 为啥还原不了那个动态刷新

后端理解ajax和axios_兴趣使然的小小的博客-CSDN博客

reset.css

body,ul,h1,h2,h3,h4,h5,h6{
    margin: 0;
    padding: 0;
}
h1,h2,h3,h4,h5,h6{
    font-size:100%;
    font-weight:normal;
}
a{
    text-decoration:none;
}
ul{
    list-style:none;
}
img{
    border:0px;
}

/* 清除浮动,解决margin-top塌陷 */
.clearfix:before,.clearfix:after{
    content:'';
    display:table;    
}
.clearfix:after{
    clear:both;
}
.clearfix{
    zoom:1;
}

.fl{
    float:left;
}
.fr{
    float:right;
}

index.css

body{
    font-family:'Microsoft YaHei';   
}
.wrap{
    position: fixed;
    left:0;
    top:0;
    width:100%;
    height:100%;
    /* background: radial-gradient(#f3fbfe, #e4f5fd, #8fd5f4); */
    /* background:#8fd5f4; */
    /* background: linear-gradient(#6bc6ee, #fff); */
    background:#fff;

}
.search_form{
    width:640px;
    margin:100px auto 0;
}
.logo img{
    display:block;
    margin:0 auto;
}
.form_group{
    width:640px;
    height:40px;
    margin-top:45px;
}
.input_txt{
   width:538px;
   height:38px;
   padding:0px;
   float:left;
   border:1px solid #41a1cb;
   outline:none;
   text-indent:10px;
}

.input_sub{
    width:100px;
    height:40px;
    border:0px;
    float: left;
    background-color: #41a1cb;
    color:#fff;
    font-size:16px;
    outline:none;
    cursor: pointer;
    position: relative;
}
.input_sub.loading::before{
    content:'';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: url('../img/loading.gif');
}

.hotkey{
    margin:3px 0 0 2px;
}

.hotkey a{
    font-size:14px;
    color:#666;
    padding-right:15px;
}
.weather_list{
    height:200px;
    text-align:center;
    margin-top:50px;
    font-size:0px;
}
.weather_list li{
    display:inline-block;
    width:140px;
    height:200px;
    padding:0 10px;
    overflow: hidden;
    position: relative;
    background:url('../img/line.png') right center no-repeat;
    background-size: 1px 130px;
}

.weather_list li:last-child{
    background:none;
}

/* .weather_list .col02{
    background-color: rgba(65, 165, 158, 0.8);
}
.weather_list .col03{
    background-color: rgba(94, 194, 237, 0.8);
}
.weather_list .col04{
    background-color: rgba(69, 137, 176, 0.8);
}
.weather_list .col05{
    background-color: rgba(118, 113, 223, 0.8);
} */


.info_date{
    width:100%;
    height:40px;
    line-height:40px;
    color:#999;
    font-size:14px;
    left:0px;    
    bottom:0px;    
    margin-top: 15px;
}
.info_date b{
    float: left;
    margin-left:15px;
}

.info_type span{
    color:#fda252;
    font-size:30px;
    line-height:80px;
}
.info_temp{
    font-size:14px;  
    color:#fda252;
}
.info_temp b{
    font-size:13px;
}
.tem .iconfont {
    font-size: 50px;
  }

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>天知道</title>
  <link rel="stylesheet" href="css/reset.css" />
  <link rel="stylesheet" href="css/index.css" />
</head>

<body>
  <div class="wrap" id="app">
    <div class="search_form">
      <div class="logo"><img src="img/logo.png" alt="logo" /></div>
      <div class="form_group">
        <!--搜索框-->
        <input type="text" v-model="city"  @keyup.enter="searchWeather"   class="input_txt" placeholder="请输入查询的天气"/>
        <button class="input_sub" @click="searchWeather">
          搜 索
        </button>
      </div>
      <div class="hotkey">
<!--        <a href="javascript:;">北京</a>-->
<!--        <a href="javascript:;">上海</a>-->
<!--        <a href="javascript:;">广州</a>-->
<!--        <a href="javascript:;">深圳</a>-->
        <!--对于上面的像列表一样的东西, 可以用v-for来代替-->
        <!--这里href-->
        <a href="javascript:;" v-for="city in hotCitys" @click="clickCity(city)">{{city}}</a>
<!--        <a href="#" v-for="city in hotCitys" @click="clickCity(city)">{{city}}</a>-->
      </div>
    </div>
    <ul class="weather_list">
      <!--这里是展示, 查询到的天气结果-->
      <!--为了是每一次搜索同一个城市的时候都有刷新一下的效果, 这里用v-bind绑定一下数据-->
      <li v-for="(item,index) in weatherList" :key="item.date" :style="{transitionDelay:index*100+'ms'}">
        <div class="info_type">
          <span class="iconfont">{{ item.type }}</span>
        </div>
        <div class="info_temp">
          <b>{{ item.low }}</b>
          ~
          <b>{{ item.high }}</b>
        </div>
        <div class="info_date">
          <span>{{ item.date }}</span>
        </div>
      </li>
    </ul>
  </div>
  <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- 官网提供的 axios 在线地址 -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <!-- 自己的js -->
<!--  <script src="./js/main.js"></script>-->
<script>
  var app = new Vue({
    el: "#app",
    data: {
      city: '',
      weatherList: [],
      hotCitys: ["北京", "重庆", "广安", "成都"]
    },
    methods: {
      searchWeather: function () {
        //  console.log('天气查询');
        //  console.log(this.city);
        // 调用接口
        // 保存this
        var that = this;
        this.weatherList = [];//这个重置一下,数组中的数据,会有个一闪一闪的动画
        axios.get('http://wthrcdn.etouch.cn/weather_mini?city=' + this.city)
                // .then(res => {//这里是匿名函数!
                //   console.log(res);
                //   this.weatherList = res.data.data.forecast;
                // })
                .then(function (response) {
                  // console.log(response);
                  console.log(response.data.data.forecast);
                  that.weatherList = response.data.data.forecast;
                })
                .catch(function (err) {
                })
                .finally(() => { });
      },
      clickCity(city){
        this.city = city;
        this.searchWeather();
      }
    },
  })
</script>
</body>

</html>

记事本:

主要功能:

image-20220422165453435

主要数据类型:

data: {
    list: ["写代码", "吃饭饭", "睡觉觉"],
    inputValue: "好好学习,天天向上"
},

全部源码:

image-20220422172048493

<html>

<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <title>小黑记事本</title>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <meta name="robots" content="noindex, nofollow" />
  <meta name="googlebot" content="noindex, nofollow" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    html,
    body {
      margin: 0;
      padding: 0;
    }
    body {
      background: #fff;
    }
    button {
      margin: 0;
      padding: 0;
      border: 0;
      background: none;
      font-size: 100%;
      vertical-align: baseline;
      font-family: inherit;
      font-weight: inherit;
      color: inherit;
      -webkit-appearance: none;
      appearance: none;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    body {
      font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
      line-height: 1.4em;
      background: #f5f5f5;
      color: #4d4d4d;
      min-width: 230px;
      max-width: 550px;
      margin: 0 auto;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      font-weight: 300;
    }

    :focus {
      outline: 0;
    }

    .hidden {
      display: none;
    }

    #todoapp {
      background: #fff;
      margin: 180px 0 40px 0;
      position: relative;
      box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
    }

    #todoapp input::-webkit-input-placeholder {
      font-style: italic;
      font-weight: 300;
      color: #e6e6e6;
    }

    #todoapp input::-moz-placeholder {
      font-style: italic;
      font-weight: 300;
      color: #e6e6e6;
    }

    #todoapp input::input-placeholder {
      font-style: italic;
      font-weight: 300;
      color: gray;
    }

    #todoapp h1 {
      position: absolute;
      top: -160px;
      width: 100%;
      font-size: 60px;
      font-weight: 100;
      text-align: center;
      color: rgba(175, 47, 47, .8);
      -webkit-text-rendering: optimizeLegibility;
      -moz-text-rendering: optimizeLegibility;
      text-rendering: optimizeLegibility;
    }

    .new-todo,
    .edit {
      position: relative;
      margin: 0;
      width: 100%;
      font-size: 24px;
      font-family: inherit;
      font-weight: inherit;
      line-height: 1.4em;
      border: 0;
      color: inherit;
      padding: 6px;
      border: 1px solid #999;
      box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
      box-sizing: border-box;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    .new-todo {
      padding: 16px;
      border: none;
      background: rgba(0, 0, 0, 0.003);
      box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
    }

    .main {
      position: relative;
      z-index: 2;
      border-top: 1px solid #e6e6e6;
    }

    .toggle-all {
      width: 1px;
      height: 1px;
      border: none; /* Mobile Safari */
      opacity: 0;
      position: absolute;
      right: 100%;
      bottom: 100%;
    }

    .toggle-all + label {
      width: 60px;
      height: 34px;
      font-size: 0;
      position: absolute;
      top: -52px;
      left: -13px;
      -webkit-transform: rotate(90deg);
      transform: rotate(90deg);
    }

    .toggle-all + label:before {
      content: "❯";
      font-size: 22px;
      color: #e6e6e6;
      padding: 10px 27px 10px 27px;
    }

    .toggle-all:checked + label:before {
      color: #737373;
    }

    .todo-list {
      margin: 0;
      padding: 0;
      list-style: none;
      max-height: 420px;
      overflow: auto;
    }

    .todo-list li {
      position: relative;
      font-size: 24px;
      border-bottom: 1px solid #ededed;
      height: 60px;
      box-sizing: border-box;
    }

    .todo-list li:last-child {
      border-bottom: none;
    }

    .todo-list .view .index {
      position: absolute;
      color: gray;
      left: 10px;
      top: 20px;
      font-size: 16px;
    }

    .todo-list li .toggle {
      text-align: center;
      width: 40px;
      /* auto, since non-WebKit browsers doesn't support input styling */
      height: auto;
      position: absolute;
      top: 0;
      bottom: 0;
      margin: auto 0;
      border: none; /* Mobile Safari */
      -webkit-appearance: none;
      appearance: none;
    }

    .todo-list li .toggle {
      opacity: 0;
    }

    .todo-list li .toggle + label {
      /*
            Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
            IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
        */
      background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E");
      background-repeat: no-repeat;
      background-position: center left;
    }

    .todo-list li .toggle:checked + label {
      background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E");
    }

    .todo-list li label {
      word-break: break-all;
      padding: 15px 15px 15px 60px;
      display: block;
      line-height: 1.2;
      transition: color 0.4s;
    }

    .todo-list li.completed label {
      color: #d9d9d9;
      text-decoration: line-through;
    }

    .todo-list li .destroy {
      display: none;
      position: absolute;
      top: 0;
      right: 10px;
      bottom: 0;
      width: 40px;
      height: 40px;
      margin: auto 0;
      font-size: 30px;
      color: #cc9a9a;
      margin-bottom: 11px;
      transition: color 0.2s ease-out;
    }

    .todo-list li .destroy:hover {
      color: #af5b5e;
    }

    .todo-list li .destroy:after {
      content: "×";
    }

    .todo-list li:hover .destroy {
      display: block;
    }

    .todo-list li .edit {
      display: none;
    }

    .todo-list li.editing:last-child {
      margin-bottom: -1px;
    }

    .footer {
      color: #777;
      padding: 10px 15px;
      height: 20px;
      text-align: center;
      border-top: 1px solid #e6e6e6;
    }

    .footer:before {
      content: "";
      position: absolute;
      right: 0;
      bottom: 0;
      left: 0;
      height: 50px;
      overflow: hidden;
      box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6,
      0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6,
      0 17px 2px -6px rgba(0, 0, 0, 0.2);
    }

    .todo-count {
      float: left;
      text-align: left;
    }

    .todo-count strong {
      font-weight: 300;
    }

    .filters {
      margin: 0;
      padding: 0;
      list-style: none;
      position: absolute;
      right: 0;
      left: 0;
    }

    .filters li {
      display: inline;
    }

    .filters li a {
      color: inherit;
      margin: 3px;
      padding: 3px 7px;
      text-decoration: none;
      border: 1px solid transparent;
      border-radius: 3px;
    }

    .filters li a:hover {
      border-color: rgba(175, 47, 47, 0.1);
    }

    .filters li a.selected {
      border-color: rgba(175, 47, 47, 0.2);
    }

    .clear-completed,
    html .clear-completed:active {
      float: right;
      position: relative;
      line-height: 20px;
      text-decoration: none;
      cursor: pointer;
    }

    .clear-completed:hover {
      text-decoration: underline;
    }

    .info {
      margin: 50px auto 0;
      color: #bfbfbf;
      font-size: 15px;
      text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
      text-align: center;
    }

    .info p {
      line-height: 1;
    }

    .info a {
      color: inherit;
      text-decoration: none;
      font-weight: 400;
    }

    .info a:hover {
      text-decoration: underline;
    }

    /*
        Hack to remove background from Mobile Safari.
        Can't use it globally since it destroys checkboxes in Firefox
    */
    @media screen and (-webkit-min-device-pixel-ratio: 0) {
      .toggle-all,
      .todo-list li .toggle {
        background: none;
      }

      .todo-list li .toggle {
        height: 40px;
      }
    }

    @media (max-width: 430px) {
      .footer {
        height: 50px;
      }

      .filters {
        bottom: 10px;
      }
    }

  </style>
<!--  <link rel="stylesheet" type="text/css" href="./css/index.css" />-->
</head>

<body>
  <!-- 主体区域 -->
  <section id="todoapp">
    <!-- 输入框 -->
    <header class="header">
      <h1>小小记事本</h1>
      <input v-model="inputValue" @keyup.enter="add" autofocus="autofocus" autocomplete="off" placeholder="请输入任务"
        class="new-todo" />
    </header>
    <!-- 列表区域 -->
    <section class="main">
      <ul class="todo-list">
        <!--list就是-->
        <li class="todo" v-for="(item,index) in list">
          <div class="view">
            <span class="index">{{ index+1 }}.</span>
            <label>{{ item }}</label>
            <button class="destroy" @click="remove(index)"></button>
          </div>
        </li>
      </ul>
    </section>
    <!-- 统计和清空 -->
    <footer class="footer" v-show="list.length>0">
      <span class="todo-count" >
        <strong>{{list.length}}</strong> items left
      </span>
      <button class="clear-completed" @click="clear">
        Clear
      </button>
    </footer>
  </section>
  <!-- 底部 -->
  <footer class="info">
    <p>
      <a href="https://ladfeng.top/"><img src="https://qiniu.ladidol.top/img/image-20220421114425049.png" alt="" /></a>
    </p>
  </footer>
  <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    var app = new Vue({
      el: "#todoapp",
      data: {
        list: ["写代码", "吃饭饭", "睡觉觉"],
        inputValue: "好好学习,天天向上"
      },
      methods: {
        add: function () {
          /*add就push一个元素到最后*/
          this.list.push(this.inputValue);
        },
        remove:function(index){
          console.log("删除");
          console.log(index);
          /*remove就把数组的第index个开始,裁剪一个*/
          this.list.splice(index,1);
        },
        clear:function () {
          this.list = [];
        }
      },
    })
  </script>
</body>

</html>

音乐播放器:

image-20220423205713600

服务器返回的数据比较复杂时,获取的时候需要注意层级结构

通过审查元素快速定位到需要操纵的元素, 比如这里的#player

注意你要对页面上的东西进行什么操作!

index.css

body,
ul,
dl,
dd {
  margin: 0px;
  padding: 0px;
}

.wrap {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: url("../images/bg.jpg") no-repeat;
  background-size: 100% 100%;
}

.play_wrap {
  width: 800px;
  height: 544px;
  position: fixed;
  left: 50%;
  top: 50%;
  margin-left: -400px;
  margin-top: -272px;
  /* background-color: #f9f9f9; */
}

.search_bar {
  height: 60px;
  background-color: #1eacda;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  z-index: 11;
}

.search_bar img {
  margin-left: 23px;
}

.search_bar input {
  margin-right: 23px;
  width: 296px;
  height: 34px;
  border-radius: 17px;
  border: 0px;
  background: url("../images/zoom.png") 265px center no-repeat
    rgba(255, 255, 255, 0.45);
  text-indent: 15px;
  outline: none;
}

.center_con {
  height: 435px;
  background-color: rgba(255, 255, 255, 0.5);
  display: flex;
  position: relative;
}

.song_wrapper {
  width: 200px;
  height: 435px;
  box-sizing: border-box;
  padding: 10px;
  list-style: none;
  position: absolute;
  left: 0px;
  top: 0px;
  z-index: 1;
}

.song_stretch {
  width: 600px;
}

.song_list {
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  height: 100%;
}
.song_list::-webkit-scrollbar {
  display: none;
}

.song_list li {
  font-size: 12px;
  color: #333;
  height: 40px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  width: 580px;
  padding-left: 10px;
}

.song_list li:nth-child(odd) {
  background-color: rgba(240, 240, 240, 0.3);
}

.song_list li a {
  display: block;
  width: 17px;
  height: 17px;
  background-image: url("../images/play.png");
  background-size: 100%;
  margin-right: 5px;
  box-sizing: border-box;
}

.song_list li b {
  font-weight: normal;
  width: 122px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.song_stretch .song_list li b {
  width: 200px;
}

.song_stretch .song_list li em {
  width: 150px;
}

.song_list li span {
  width: 23px;
  height: 17px;
  margin-right: 50px;
}
.song_list li span i {
  display: block;
  width: 100%;
  height: 100%;
  cursor: pointer;
  background: url("../images/table.png") left -48px no-repeat;
}

.song_list li em,
.song_list li i {
  font-style: normal;
  width: 100px;
}

.player_con {
  width: 400px;
  height: 435px;
  position: absolute;
  left: 200px;
  top: 0px;
}

.player_con2 {
  width: 400px;
  height: 435px;
  position: absolute;
  left: 200px;
  top: 0px;
}

.player_con2 video {
  position: absolute;
  left: 20px;
  top: 30px;
  width: 355px;
  height: 265px;
}

.disc {
  position: absolute;
  left: 73px;
  top: 60px;
  z-index: 9;
}
.cover {
  position: absolute;
  left: 125px;
  top: 112px;
  width: 150px;
  height: 150px;
  border-radius: 75px;
  z-index: 8;
}
.comment_wrapper {
  width: 180px;
  height: 435px;
  list-style: none;
  position: absolute;
  left: 600px;
  top: 0px;
  padding: 25px 10px;
}
.comment_wrapper .title {
  position: absolute;
  top: 0;
  margin-top: 10px;
}
.comment_wrapper .comment_list {
  overflow: auto;
  height: 410px;
}
.comment_wrapper .comment_list::-webkit-scrollbar {
  display: none;
}
.comment_wrapper dl {
  padding-top: 10px;
  padding-left: 55px;
  position: relative;
  margin-bottom: 20px;
}

.comment_wrapper dt {
  position: absolute;
  left: 4px;
  top: 10px;
}

.comment_wrapper dt img {
  width: 40px;
  height: 40px;
  border-radius: 20px;
}

.comment_wrapper dd {
  font-size: 12px;
}

.comment_wrapper .name {
  font-weight: bold;
  color: #333;
  padding-top: 5px;
}

.comment_wrapper .detail {
  color: #666;
  margin-top: 5px;
  line-height: 18px;
}
.audio_con {
  height: 50px;
  background-color: #f1f3f4;
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}
.myaudio {
  width: 800px;
  height: 40px;
  margin-top: 5px;
  outline: none;
  background-color: #f1f3f4;
}
/* 旋转的动画 */
@keyframes Rotate {
  from {
    transform: rotateZ(0);
  }
  to {
    transform: rotateZ(360deg);
  }
}
/* 旋转的类名 */
.autoRotate {
  animation-name: Rotate;
  animation-iteration-count: infinite;
  animation-play-state: paused;
  animation-timing-function: linear;
  animation-duration: 5s;
}
/* 是否正在播放 */
.player_con.playing .disc,
.player_con.playing .cover {
  animation-play-state: running;
}

.play_bar {
  position: absolute;
  left: 200px;
  top: -10px;
  z-index: 10;
  transform: rotate(-25deg);
  transform-origin: 12px 12px;
  transition: 1s;
}
/* 播放杆 转回去 */
.player_con.playing .play_bar {
  transform: rotate(0);
}
/* 搜索历史列表 */
.search_history {
  position: absolute;
  width: 296px;
  overflow: hidden;
  background-color: rgba(255, 255, 255, 0.3);
  list-style: none;
  right: 23px;
  top: 50px;
  box-sizing: border-box;
  padding: 10px 20px;
  border-radius: 17px;
}
.search_history li {
  line-height: 24px;
  font-size: 12px;
  cursor: pointer;
}
.switch_btn {
  position: absolute;
  right: 0;
  top: 0;
  cursor: pointer;
}
.right_line {
  position: absolute;
  left: 0;
  top: 0;
}
.video_con video {
  position: fixed;
  width: 800px;
  height: 546px;
  left: 50%;
  top: 50%;
  margin-top: -273px;
  transform: translateX(-50%);
  z-index: 990;
}
.video_con .mask {
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  z-index: 980;
  background-color: rgba(0, 0, 0, 0.8);
}
.video_con .shutoff {
  position: fixed;
  width: 40px;
  height: 40px;
  background: url("../images/shutoff.png") no-repeat;
  left: 50%;
  margin-left: 400px;
  margin-top: -273px;
  top: 50%;
  z-index: 995;
}

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>悦听player</title>
  <!-- 样式 -->
  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <div class="wrap">
    <!-- 播放器主体区域 -->
    <div class="play_wrap" id="player">
      <div class="search_bar">
        <img src="images/player_title.png" alt="" />
        <!-- 搜索歌曲 -->
        <input type="text" autocomplete="off" v-model="query" @keyup.enter="searchMusic" />
      </div>
      <div class="center_con">
        <!-- 搜索歌曲列表 -->
        <div class='song_wrapper'>
          <ul class="song_list">
            <li v-for="item in musicList"><!--对歌曲进行循环展示-->
              <a href="javascript:;" @click="playMusic(item.id)"></a> <!--播放按钮-->
              <b>{{ item.name }} -{{ item.artists[0].name }}</b><!--歌曲名和歌手名-->
              <span v-if="item.mvid!=0" @click="playMV(item.mvid)"><i></i></span><!--如果有mv的话,就显示mv标识-->
            </li>
          </ul>
          <img src="images/line.png" class="switch_btn" alt="">
        </div>
        <!-- 歌曲信息容器 -->
        <div class="player_con" :class="{playing:isPlaying}">
          <img src="images/player_bar.png" class="play_bar" />
          <!-- 黑胶碟片 -->
          <img src="images/disc.png" class="disc autoRotate" />
          <img :src="musicCover" class="cover autoRotate" />
        </div>
        <!-- 评论容器 -->
        <div class="comment_wrapper">
          <h5 class='title'>热门留言</h5>
          <div class='comment_list'>
            <dl v-for="item in hotComments">
              <dt><img :src="item.user.avatarUrl" alt=""></dt>
              <dd class="name">{{ item.nickname}}</dd>
              <dd class="detail">
                {{ item.content }}
              </dd>
            </dl>
          </div>
          <img src="images/line.png" class="right_line">
        </div>
      </div>
      <div class="audio_con">
        <audio ref='audio' @play="play" @pause="pause" :src="musicUrl" controls autoplay loop class="myaudio"></audio>
      </div>
      <div class="video_con" v-show="isShow" style="display: none;">
        <video :src="mvUrl" controls="controls"></video>
        <div class="mask" @click="hide"></div>
      </div>
    </div>
  </div>
  <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- 官网提供的 axios 在线地址 -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!--  <script src="./js/main.js"></script>-->
<script>
  /*
  1:歌曲搜索接口
    请求地址:https://autumnfish.cn/search
    请求方法:get
    请求参数:keywords(查询关键字)
    响应内容:歌曲搜索结果

  2:歌曲url获取接口
    请求地址:https://autumnfish.cn/song/url
    请求方法:get
    请求参数:id(歌曲id)
    响应内容:歌曲url地址
  3.歌曲详情获取
    请求地址:https://autumnfish.cn/song/detail
    请求方法:get
    请求参数:ids(歌曲id)
    响应内容:歌曲详情(包括封面信息)
  4.热门评论获取
    请求地址:https://autumnfish.cn/comment/hot?type=0
    请求方法:get
    请求参数:id(歌曲id,地址中的type固定为0)
    响应内容:歌曲的热门评论
  5.mv地址获取
    请求地址:https://autumnfish.cn/mv/url
    请求方法:get
    请求参数:id(mvid,为0表示没有mv)
    响应内容:mv的地址
*/
  var app = new Vue({
    el: "#player",
    data: {
      // 查询关键字
      query: "",
      // 歌曲数组
      musicList: [],
      // 歌曲地址
      musicUrl: "",
      // 歌曲封面
      musicCover: "",
      // 歌曲评论
      hotComments: [],
      // 动画播放状态
      isPlaying: false,
      // 遮罩层的显示状态
      isShow: false,
      // mv地址
      mvUrl: ""
    },
    methods: {
      // 歌曲搜索
      searchMusic: function() {
        var that = this;
        axios.get("https://autumnfish.cn/search?keywords=" + this.query)
                .then(
                        function(response) {
                          //先通过console看一下response的数据结构!
                          // console.log(response);
                          that.musicList = response.data.result.songs;
                          console.log(response.data.result.songs);
                        },
                        function(err) {}
                );
      },
      // 歌曲播放
      //播放歌曲的本质就是设置了歌曲的audio的src, 本请求主要是为了得到歌曲的url
      playMusic: function(musicId) {
        //   console.log(musicId);
        var that = this;
        // 获取歌曲地址
        axios.get("https://autumnfish.cn/song/url?id=" + musicId).then(
                function(response) {
                  // console.log(response);
                  // console.log(response.data.data[0].url);
                  that.musicUrl = response.data.data[0].url;
                },
                function(err) {}
        );

        // 歌曲详情获取
        axios.get("https://autumnfish.cn/song/detail?ids=" + musicId).then(
                function(response) {
                  // console.log(response);
                  // console.log(response.data.songs[0].al.picUrl);
                  that.musicCover = response.data.songs[0].al.picUrl;
                },
                function(err) {}
        );

        // 歌曲评论获取
        axios.get("https://autumnfish.cn/comment/hot?type=0&id=" + musicId).then(
                function(response) {
                  // console.log(response);
                  // console.log(response.data.hotComments);
                  that.hotComments = response.data.hotComments;
                },
                function(err) {}
        );
      },
      // 歌曲播放
      play: function() {
        // console.log("play");
        this.isPlaying = true;
      },
      // 歌曲暂停
      pause: function() {
        // console.log("pause");
        this.isPlaying = false;
      },
      // 播放mv
      playMV: function(mvid) {
        var that = this;
        axios.get("https://autumnfish.cn/mv/url?id=" + mvid).then(
                function(response) {
                  // console.log(response);
                  console.log(response.data.data.url);
                  that.isShow = true;
                  that.mvUrl = response.data.data.url;
                },
                function(err) {}
        );
      },
      // 隐藏
      hide: function() {
        this.isShow = false;
      }
    }
  });

</script>
</body>

</html>

href="#"和href=”javascript:"的区别 - 妮小朱 - 博客园 (cnblogs.com)

javascript:表示这是一个空连接。点击之后没任何反应。
类似的是#,但是一个#点击之后页面很长的情况下会会滚到顶部;而javascript:是没有这种问题的。经过测试他还是在当前位置。
当然###这样的效果就跟javascript:一样了

END

本文于2022年5月6日23:20:01更新了一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兴趣使然的小小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值