首页 > 其他分享 >Vue学习笔记

Vue学习笔记

时间:2024-10-13 14:17:32浏览次数:3  
标签:Vue default 笔记 学习 state vue 组件 import 路由

Vue概念:

为什么学习Vue:

更少的时间,干更多的活,开发网站速度

原生js ---------------jQuery------------Vue

案例:把数组数据–循环渲染到页面上

原生js:

  <body>
    <ul id="myul"></ul>
  </body>
  <script>
    let arr = ['aa', 'bb', 'cc', 'dd', 'ee']
    let myUl = document.getElementById('myul')
    for (let i = 0; i < arr.length; i++) {
      let thenLi = document.createElement('li')
      thenLi.innerHTML = arr[i]
      console.log(thenLi)
      myUl.appendChild(thenLi)
    }
  </script>

vue:

<body>
    <div id="app">
      <ul>
        <li v-for="item in arr">{{item}}</li>
      </ul>
    </div>
  </body>
  <script>
    new Vue({
      el: '#app',
      data() {
        return {
          arr: ['aa', 'bb', 'cc', 'dd', 'ee'],
        }
      },
    })
  </script>

注意:vue写起来很爽,vue的底层还是原生js

vue开发更加的效率和简洁,易于维护,快快快,现在很多的项目都是用vue开发的

vue是什么:

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架

什么是渐进式:

渐进式:逐渐进步,想用什么就用什么,不用全部都用

vue从基础开始学习,循序渐进向前学习

声明式渲染—组件系统—路由—vuex

什么是库和框架:

库: 封装的属性和方法(jquery)

框架:拥有自己的规则和元素,比库强大的多(vue.js)

vue如何学:

  • 每天的知识点做到了如指掌,花时间去记住结论和公式(语法)
  • 记住vue指令的作用,基础语法,整理笔记
  • 课上的例子,练习,案例,作业,项目反复的练习
  • 学会查找问题和解决问题(弄个报错总结文档.md)

vue是渐进式的框架,有自己的规则,我们要记住语法,特点和作用,反复练习,多总结

vue的学习:

传统的开发模式:

基于html/css/js文件开发vue:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 第一步:引入vue.js -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <!-- 第二步:创建vue容器  
    vue的语法代码只能写在下面的div中 -->
    <div id="app"></div>
  </body>
  <!-- 第三步:创建vue实例 -->
  <script>
    // console.log(Vue)
    var vm = new Vue({
      el: '#app', //提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标
      data: {
        //Vue 实例的数据对象
        msg: 'hello word',
        flag: false,
        arr: [11, 2, 3, 4, 56, 6],
        num: 2,
      },
    })
  </script>
</html>

工程化的开发模式:

webpack环境中开发vue,最推荐,企业常用的方式

插值表达式:

目的:在dom标签中,直接插入内容

又叫:声明式渲染/文本插值

语法: {{表达式}}

    <div id="app">
      <!-- 插值表达式中不能写循环语法,不能写条件语句 -->
      <p>{{num}}</p>
      <p>{{num+1}}</p>
      <p>{{34}}</p>
      <p>{{flag}}</p>
      <p>{{flag?'hello':"nihao"}}</p>
      <p>{{str}}</p>
      <p>{{str +"vue是必须要学会的"}}</p>
      <p>{{'hello world'}}</p>
      <p>{{arr}}</p>
      <p>{{arr.reverse()}}</p>
      <p>{{obj}}</p>
      <p>{{obj.name}}</p>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        num: 3,
        flag: false,
        str: '大家一定要好好学习vue',
        arr: [11, 22, 333, 44],
        obj: {
          name: 'zs',
          age: 12,
        },
      },
    })
  </script>

总结:dom中插值表达式赋值,vue的变量必须在data中声明

vue基础——MVVM设计模式:

转变思想,用数据驱动视图改变; 操作dom的事,vue源码干了

设计模式:一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结

MVVM:

一种软件架构模式,决定了写代码的思想和层次

M : model 数据模型 (data中定义)

V :view视图 (html页面)

VM:ViewModel 视图模型(vue.js源码)

MVVM通过数据双向绑定让数据自动双向同步,不再需要操作DOM

V (修改视图)-----> M(数据自动同步)

M(修改数据)------>V(视图自动同步)

在这里插入图片描述

总结:vue源码采用MVVM设计模式思想,大大减少了DOM操作,提高开发效率

Vue指令:

指令 (Directives) 是带有 v- 前缀的特殊 attribute

每个指令,都有独立的作用

v-bind:

给标签属性设置变量的值

语法: v-bind:属性名=“vue变量”

简写: :属性名=“vue变量”

  <body>
    <div id="app">
      <!-- v-bind  属性绑定:   把vue变量的值,赋予给dom属性上,影响标签的显示效果 -->
      <a v-bind:href="url">我是a标记</a>
      <a :href="url">我是a标记</a>

      <hr />
      <img v-bind:src="imgurl" alt="" />
      <img :src="imgurl" alt="" />
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        url: 'http://www.baidu.com',
        imgurl: 'https://img01.yzcdn.cn/vant/ipad.jpeg',
      },
    })
  </script>

v-on:

给标签定义事件

语法:

  • v-on:事件名=“要执行的少量代码”
  • v-on:事件名=“methods中的函数”
  • v-on:事件名=“methods中的函数(实参)”
  • 简写:@事件名=“methods中的函数”
<body>
    <div id="app">
      <!--  v-on 事件绑定 -->
      <p>要购买的商品的数量{{count}}</p>
      <button v-on:click="count=count+1">增加1</button>
      <button v-on:click="addFn">增加1</button>
      <button v-on:click="addCount(5)">一次增5</button>

      <button @click="subFn">减1</button>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count: 2,
      },
      methods: {
        //专门用来定义函数(方法)的
        addFn() {
          //this代表的是当前的vue实例对象
          console.log(this)
          this.count++
        },
        addCount(num) {
          this.count += num //
        },
        subFn() {
          this.count--
        },
      },
    })
  </script>
v-on事件对象:

vue处理函数中,拿到事件对象

语法:

  • 无传参: 通过形参直接接收
  • 传参: 通过$event指定事件对象传给事件处理函数
<body>
    <div id="app">
      <a @click="fn" href="http://www.baidu.com">阻止百度</a>
      <hr />
      <a @click="eveFn(10,$event)" href="http://www.baidu.com"
        >阻止跳转到百度</a
      >
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        fn(e) {
          // 阻止默认行为
          e.preventDefault()
        },
        eveFn(num, e) {
          e.preventDefault()
        },
      },
    })
  </script>
v-on修饰符:

在事件后面.修饰符名,给事件带来更强大的功能

修饰符是由点开头的指令后缀来表示的

语法:

  • @事件名.修饰符 =“methods函数”
    • .stop 阻止事件冒泡
    • .prevent 阻止默认行为
    • .once 程序运行期间,只触发一次事件处理函数
    • .self 只当在 event.target 是当前元素自身时触发处理函数
 <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <style>
      .main {
        width: 400px;
        height: 200px;
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div @click="fn1">
        <button @click.stop="btnFn">stop阻止事件冒泡</button>
        <a @click.prevent.stop="aFn" href="http://www.baidu.com"
          >prevent阻止默认行为</a
        >
        <button @click.once="onceFn">once触发一次</button>
      </div>

      <!--.self  只当在 event.target 是当前元素自身时触发处理函数  -->
      <div @click.self="fn1" class="main">
        <button @click="btnFn">stop阻止事件冒泡</button>
        <a @click.prevent="aFn" href="http://www.baidu.com"
          >prevent阻止默认行为</a
        >
        <button @click.once="onceFn">once触发一次</button>
      </div>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        fn1() {
          alert('fn1')
        },
        btnFn() {
          alert('btnFn')
        },
        aFn() {
          alert('aFn')
        },
        onceFn() {
          alert('onceFn')
        },
      },
    })
  </script>
v-on按键修饰符:

给按键事件添加修饰符

语法:

  • @keyup.enter 监听回车按键
  • @keyup.esc 监听返回按键
<body>
    <div id="app">
      <input type="text" @keyup.enter="enterFn" />
      <br />
      <input type="text" @keyup.esc="escFn" />
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        enterFn() {
          alert('enterFn')
        },
        escFn() {
          alert('escFn')
        },
      },
    })
  </script>

v-text/v-html:

更新DOM对象的einnerText和innerHTML

语法:

  • v-text=“vue数据变量”
  • v-html=“vue数据变量"
<body>
    <div id="app">
      <p>{{str1}}</p>
      <p>{{str2}}</p>
      <hr />
      <p v-text="str1"></p>
      <p v-text="str2"></p>
      <hr />
      <p v-html="str1"></p>
      <p v-html="str2"></p>
      <!--  v-text把值当做普通字符串显示 
      
            v-html 把值当做html解析
      -->
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        str1: '<span>我是一个span标签</span>',
        str2: 'hello',
      },
      methods: {},
    })
  </script>

v-model:

主要用于表单元素,把value属性和vue数据变量,双向绑定到一起

语法:

v-model=‘vue数据变量’

  • 双向数据绑定(vue内部采用MVVM设计模式)
    • 数据变化–>视图自动同步
    • 视图变化–>数据自动同步
<body>
    <div id="app">
      <!-- v-model:实现vue变量和表单标签value属性,双向绑定的指令 -->
      <div>
        <span>用户名</span>
        <input type="text" v-model="username" />
      </div>
      <div>
        <span>密码</span>
        <input type="password" v-model="pass" />
      </div>
      <div>
        <span> 来自于</span>
        <!-- 下拉菜单要绑定在select上 -->
        <select v-model="city">
          <option value="北京市">北京</option>
          <option value="南京市">南京</option>
          <option value="天津市">天津</option>
        </select>
      </div>

      <div>
        <span>性别</span>
        <input type="radio" value="男" v-model="gender" />男
        <input type="radio" value="女" v-model="gender" />女
      </div>

      <!-- 
遇到复选框  v-model的变量值
数组     关联的是复选框的value值
非数组     关联的是复选框的checked属性
       -->
      <div>
        <span>爱好</span>
        <input type="checkbox" v-model="hobby" value="抽烟" />抽烟
        <input type="checkbox" v-model="hobby" value="喝酒" />喝酒
        <input type="checkbox" v-model="hobby" value="打游戏" />打游戏
      </div>

      <div>
        <span>全选</span>
        <input type="checkbox" v-model="checkall" />
      </div>

      <div>
        <span>自我评价</span>
        <textarea v-model="content"></textarea>
      </div>

      <hr />
      <div>{{username}}</div>
      <div>{{pass}}</div>
      <div>{{city}}</div>
      <div>{{gender}}</div>
      <div>{{hobby}}</div>
      <div>{{checkall}}</div>
      <div>{{content}}</div>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        username: '用户名',
        pass: '',

        city: '南京市',
        gender: '男',
        hobby: ['打游戏'],
        checkall: true,
        content: '',
      },
      methods: {},
    })
  </script>

v-model修饰符:

让v-model拥有更强大的功能

语法:

  • v-model.修饰符=“vue数据变量”

    • .number 以parseFloat转成数字类型
    • .trim 去除首尾空白字符
    • .lazy change时触发而非input 不会每次输入都执行事件
 <body>
    <div id="app">
      <div>
        <span>年龄</span>
        <input type="text" v-model.number="age" />
      </div>

      <div>
        <span>人生格言</span>
        <input type="text" v-model.trim="motto" />
      </div>

      <div>
        <span>自我介绍</span>
        <textarea v-model.lazy="content"></textarea>
      </div>
      {{content}}
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        age: 0,
        motto: '',
        content: '',
      },
      methods: {},
    })
  </script>

v-show和v-if:

控制标签的显示隐藏

语法:

  • v-show=“vue变量”
  • v-if=“vue变量”

原理:

  • v-show用的是display:none控制标签的显示隐藏 (频繁切换)
  • v-if 用的是插入和删除节点来控制标签的显示隐藏
  • v-else
<body>
    <div id="app">
      <h1 v-show="isOk">v-show的盒子</h1>
      <h1 v-if="isOk">v-if的盒子</h1>

      <div>
        <!-- v-if和v-else的两个标签必须紧挨着   -->
        <p v-if="age>15">我成年了</p>
        <p v-else>未成年</p>
      </div>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        isOk: true,
        age: 15,
      },
      methods: {},
    })
  </script>
</html>

v-for:

列表渲染,所在标签结构,按照数据数量 循环生成

语法:

  • v-for=“(值,索引) in 目标结构”
  • v-for=“值 in 目标结构”

目标结构

  • 可以遍历数组/对象/数字/字符串
<body>
    <div id="app">
      <ul>
        <li v-for="(item,index) in arr">{{index}} ---{{item}}</li>
      </ul>
      <hr />
      <ul>
        <li v-for="obj in stuArr">
          {{obj.name}}---{{obj.sex}}---{{obj.hobby}}
        </li>
      </ul>
      <hr />
      <div v-for="(value,key) in tObj">{{key}}---{{value}}</div>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        arr: ['aaa', 'bbb', 'ccc', 'ddd'],
        stuArr: [
          {
            id: 1001,
            name: '孙悟空',
            sex: '男',
            hobby: '唱歌',
          },
          {
            id: 1002,
            name: '猪八戒',
            sex: '男',
            hobby: '背媳妇',
          },
        ],
        tObj: {
          name: '小妞',
          age: 12,
          class: '1班',
        },
      },
      methods: {},
    })
  </script>
</html>

vue数据监听-key的作用:

1、数组翻转 2、数组截取 3、更新值

  • 数组变更方法,就会导致v-for更新,页面更新

    (push,pop,shift,unshift,splice,sort,reverse)

  • 数组非变更方法,返回新数组,不会导致v-for更新,可采用覆盖数组 或this.$set

    (filter,concat,slice)

  <body>
    <div id="app">
      <ul>
        <li v-for="(val,index) in arr" :key="index">{{val}}</li>
      </ul>
      <button @click="revBtn">数组翻转</button>
      <button @click="sliceFn">截取前3个</button>
      <button @click="updateFn">更新第一个元素值</button>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        arr: [4, 3, 2, 1, 4, 5, 7],
      },
      methods: {
        revBtn() {
          // 数组翻转 可以让v-for更新
          this.arr.reverse()
          // console.log(this.arr)
        },
        sliceFn() {
          // 数组slice 不会造成v-for更新
          // slice不会改变原数组
          // this.arr.slice(0, 3)
          // console.log(this.arr)

          // 解决v-for更新,覆盖原始数组
          let newArr = this.arr.slice(0, 3)
          this.arr = newArr
        },
        updateFn() {
          // 更新某个值的时候  v-for是监测不到的
          this.arr[0] = 1999

          // 解决  this.$set
          // 参数1  更新目标结构(对象/数组)
          // 参数2  更新位置
          // 参数3  更新值

          this.$set(this.arr, 0, 1999)
        },
      },
    })
  </script>

v-for如何更新DOM呢:

在这里插入图片描述

真实DOM:

document对象上,渲染到浏览器上显示的标签

在这里插入图片描述

虚拟DOM:

本质是保存节点信息 ,属性和内容的一个js对象

在这里插入图片描述

内存中虚拟DOM比较:

内存中比较变化的部分,然后给真实DOM打补丁(更新)

在这里插入图片描述

虚拟DOM好处:

提高DOM更新的性能,不频繁操作真实的DOM,在内存中找到变化的部分,更新真实的DOM(打补丁)

问题:
  • 新的虚拟DOM根元素,或者属性变化,如何更新
  • 具体如何比较新旧虚拟DOM

diff算法:

diff算法是通过同级比较  来比较新旧虚拟dom
根元素变化   删除重新建立整个dom树
根元素未变  属性改变   DOM复用 只更新属性

同级比较,根元素变化-整个dom树删除重建:

在这里插入图片描述

同级比较,根元素不变-属性改变 更新属性:

在这里插入图片描述

标签内子元素/内容改变:

无key:

从第二个往后更新内容–性能不高

在这里插入图片描述

有key,值为索引:

有key属性,基于key的来比较新旧虚拟dom。移除key不存在元素

在这里插入图片描述

有key,key值唯一:

在这里插入图片描述

vue基础-动态class:

使用v-bind给标签class设置动态的值

语法:

  • :class=“{类名:布尔值}”
<style>
      .red_str {
        color: red;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 
         :class="{类名:布尔值}"
         场景:  vue 变量控制标签是否应该有类名
       -->
      <p :class="{'red_str':bool}">动态class</p>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        bool: true,
      },
    })
  </script>

vue基础-动态style:

给标签动态设置style的值

语法:

  • :style=“{css属性:值}”
  <body>
    <div id="app">
      <!-- 

        语法:

:style="{css属性:值}"
       -->

      <p :style="{backgroundColor:colorStr,color:str,border:borStr}">
        动态style
      </p>

      <p :style="{'background-color':colorStr,color:str,border:borStr}">
        动态style
      </p>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        colorStr: 'red',
        str: '#fff',
        borStr: '5px solid blue',
      },
    })
  </script>

vue过滤器:

定义使用:

转换格式,过滤器就是一个函数传入值返回处理后的值

过滤器只能用在插值表达式v-bind表达式

场景:

  • 字母转大写:输入 hello 输出 ‘HELLO’
  • 字符串翻转 输入’hello world’ 输出 ’dlrow olleh‘
  • 时间戳转换为时间日期格式

语法:

  • 全局: Vue.filter(‘过滤器名’,(值)=>{return 返回处理后的值})
  • 局部: filters:(过滤器名字,(值)=>{return 返回处理后的值})
<body>
    <div id="app">
      <p>原来的样子:{{msg}}</p>
      <!-- 
        2:过滤器使用
        语法:  {{值|过滤器名字}}
       -->
      <p>{{msg | toUp}}</p>
      <p>{{msg | reverse}}</p>
    </div>
    <hr />

    <div id="main">
      {{str }}
      <p>{{str | reverse}}</p>
    </div>
  </body>
  <script>
    // 全局过滤器 任何一个vue文件中都可以使用
    /* 
    Vue.filter("过滤器的名字",(val)=>{return 处理后的值})
    */
    Vue.filter('reverse', (val) => {
      return val.split('').reverse().join('')
    })

    var vm = new Vue({
      el: '#app',
      data: {
        msg: 'hello vue',
      },
      // 局部--局部--过滤器
      // 只能在当前vue文件(vue实例)中使用
      /* 
        filters:{
          过滤器1(val){
            return 处理后的值
          },
          过滤器2(val){
            return 处理后的值
          }
        }
      */
      filters: {
        toUp(val) {
          return val.toUpperCase()
        },
      },
    })

    // ---------------------------
    var aa = new Vue({
      el: '#main',
      data: {
        str: 'good bye',
      },
    })
  </script>
处理日期的工具模块moment:
调用:
<script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script>
<body>
    <div id="app">
      <!-- 
        定义初始数据,渲染到购物车页面
        点击对应的删除按钮,删除对应的数据
        当数据没有了,显示一条提示消息
       -->
      <table class="tb">
        <tr>
          <th>编号</th>
          <th>名称</th>
          <th>创立时间</th>
          <th>操作</th>
        </tr>
        <tr v-for="(item,index) in arr" :key="item.id">
          <td>{{index+1}}</td>
          <td>{{item.name}}</td>
          <td>{{item.time |formatData}}</td>
          <td>
            <button @click="del(index)">删除</button>
          </td>
        </tr>
        <tr v-if="arr.length===0">
          <td colspan="4">没有数据</td>
        </tr>
      </table>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        arr: [
          {
            id: 1,
            name: '奔驰',
            time: new Date(),
          },
          {
            id: 2,
            name: '宝马',
            time: new Date(),
          },
          {
            id: 3,
            name: '奥迪',
            time: new Date(),
          },
        ],
      },
      methods: {
        del(index) {
          this.arr.splice(index, 1)
        },
      },
      filters: {
        formatData(val) {
          return moment(val).format('YYYY-MM-DD, h:mm:ss')
        },
      },
    })
  </script>

传参和多过滤器:

可同时使用多个过滤器,或给过滤器传参

语法:

  • 过滤器传参 vue变量 | 过滤器(实参)
  • 多个过滤器 vue变量|过滤器1|过滤器2
<body>
    <div id="app">
      <p>原来的样子{{msg}}</p>
      <!-- 
        给过滤器传参
        语法  vue变量 | 过滤器名(值)
       -->
      <p>翻转过滤器{{msg | reverse("-")}}</p>
      <!-- 
       多个过滤器使用
       语法  vue变量 | 过滤器1 | 过滤器2

       -->
      <p>{{msg|toUp|reverse("|")}}</p>

      <p :title="msg|toUp|reverse('|')">鼠标停留</p>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        msg: 'hello world',
      },
      filters: {
        toUp(val) {
          return val.toUpperCase()
        },
        reverse(val, s) {
          return val.split('').reverse().join(s)
        },
      },
    })
  </script>

vue计算属性:

computed:

一个数据依赖另外一些数据计算而来的结果

(一个变量,值要通过计算得到,变量要在computed中定义)

语法:
computed:{
    '计算属性名'(){
        return 值
    }
}
代码:
 <body>
    <div id="app">
      <p>{{num}}</p>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        a: 10,
        b: 20,
      },
      /* 
       一个变量的值 ,需要用另外的变量计算得来
       computed:{
        计算属性名(){
           return 值
        }
       }
       注意:计算属性和data都是变量---不能重名
            2函数内依赖的变量变化,会自动重新计算结果返回
      */
      computed: {
        // 1页面加载会默认执行一次  给变量num赋初始值
        num() {
          console.log(1)
          return this.a + this.b
        },
      },
    })
  </script>
vue计算属性–缓存:

计算属性 基于他们的依赖项的值结果进行缓存的,只要依赖的变量不变,都直接从缓存取结果

在这里插入图片描述

 <body>
    <div id="app">
      <p>{{reverseMessage}}</p>
      <p>{{reverseMessage}}</p>
      <p>{{reverseMessage}}</p>
      <p>{{getMessage()}}</p>
      <p>{{getMessage()}}</p>
      <p>{{getMessage()}}</p>
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        msg: 'hello world',
      },
      computed: {
        /* 
        带缓存
        计算属性对应的函数执行后,会把return的值缓存起来
        依赖项不变,多次调用都是从缓存取值
        依赖项变化  函数会自动 重新执行--并缓存新的值
        
        */
        reverseMessage() {
          console.log('计算属性执行了')
          return this.msg.split('').reverse().join('')
        },
      },
      methods: {
        getMessage() {
          console.log('函数执行了')
          return this.msg.split('').reverse().join('')
        },
      },
    })
  </script>
计算属性-完整写法:

计算属性也是变量 ,如果想要直接赋值,需要使用完整写法

语法:
computed:{
    ‘属性名’:{
        set(值){
            
        },
        get(){
            
        }
    }
}
需求:

计算属性给v-model使用

 <body>
    <div id="app">
      <div>
        <span>姓名</span>
        <input type="text" v-model="full" />
      </div>
    </div>
  </body>
  <script>
    // 给计算属性赋值  需要setter

    /* 
    computed:{
    ‘属性名’:{
        set(值){
            
        },
        get(){
            
        }
    }
}
    
    */
    var vm = new Vue({
      el: '#app',
      data: {},
      computed: {
        full: {
          set(val) {
            // 给full赋值触发set方法
            console.log(val, 'set')
          },
          get() {
            console.log('get')
            //使用full的值 触发get方法
            return '无名氏'
          },
        },
      },
    })
  </script>

vue侦听器:

可以侦听data/computed属性值改变

语法:

watch:{
    '被侦听的属性名'(newValue,oldValue){
      
    }
}

代码:

  <body>
    <div id="app">
      <input type="text" v-model="name" />
    </div>
  </body>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        name: '',
      },
      /* 
      目标:侦听name值的改变
      watch:{    '被侦听的属性名'(newValue,oldValue){            }}
      */
      watch: {
        // newValue  当前最新值
        // oldValue   旧值    上一刻值
        name(newValue, oldValue) {
          console.log(newValue, oldValue)
        },
      },
    })
  </script>

想要侦听一个属性变化,使用侦听属性watch

深度侦听和立即执行:

侦听复杂类型,或者立即执行侦听函数

语法:
watch:{
    '被侦听的属性名':{
        immediate:true,//立即执行
        deep:true, //深度侦听复杂类型变化
        handler(newValue,oldValue){
          
        }
    }
}

@vue/cli脚手架:

webpack自己配置环境很麻烦,下载@vue/cli包,用vue命令创建脚手架项目

@vue/cli是vue官方提供的一个全局模块包(vue命令),此包用于创建脚手架项目

好处:
  • 0配置webpack

  • babel支持、

  • css,less支持

  • 开发服务器支持

安装:

把@vue/cli模块包安装到全局,电脑拥有vue命令,才能创建脚手架工程

全局安装:
npm install -g @vue/cli
# OR
yarn global add @vue/cli
查看版本:
vue -V

如果出现版本号,安装成功,否则失败

@vue/cli创建项目启动服务:

使用vue命令创建脚手架项目(项目名不能带大写字母,中文和特殊符号)

创建项目:
//vue create 是命令  vuecli-demo是文件夹名
vue create vuecli-demo
选择模板:

可以上下箭头选择,弄错了ctrl+c重来

在这里插入图片描述

回车,等待生成项目文件夹+文件+下载必须的第三方包
在这里插入图片描述

进入脚手架项目,启动内置的热更新本地服务器

cd vuecli-demo

npm run serve
or
yarn serve

启动成功了(底层node+webpack热更新服务)

在这里插入图片描述

打开浏览器输入上述地址

@vue/cli目录和代码分析:

vuecli-demo       #项目目录
    node_modules  项目依赖的第三方包
    public   静态文件目录
        index.html  但页面的html文件(网页浏览的就是它)
    src  业务文件夹  &****
        assets  静态资源
        components  组件目录
           HelloWorld.vue  欢迎页面的vue代码文件
        APP.vue  整个应用的根组件
        main.js  主入口文件
    .gitignore  git提交忽略的配置
    babel.config.js babel配置
    package.json  依赖包列表
    README.md  项目说明
    vue.config.js vue的配置文件

主要文件及含义

node_modules 下都是下载的第三方包
public/index.html  ---浏览器运行的网页
src/main.js    webpack打包的入口文件
src/App.vue    vue项目入口页面
package.json   依赖包列表文件
vue.config.js vue的配置文件

@vue/cli项目架构了解:

知道项目入口,以及代码执行顺序和引入关系

在这里插入图片描述

@vue/cli自定义配置:

项目中没有webpack.config.js文件,应为@vue/cli用的vue.config.js

module.exports = defineConfig({
  // 覆盖webpack的配置
  devServer: {
    //自定义服务配置
    open: true, //自动打开浏览器
    port: 3000,
  },
 })

eslint了解:

  • main.js随便声明一个变量,但是使用,终端和页面都报错了(在main里面写入let)
  • 代码步严谨

在这里插入图片描述

在这里插入图片描述

解决:

1:手动解决掉错误,后面项目中会讲如何解决

2:暂时关闭eslint检查,在vue.config.js中配置后重启服务

module.exports = defineConfig({
  ...
  lintOnSave: false, // 关闭eslint检查
  transpileDependencies: true,
})

@vue/cli单文件:

单vue文件好处,独立作用域互不影响

  • vue推荐采用.vue文件来开发项目
  • template里只能有一个根标签
  • vue文件-独立模块 --作用域互不影响
  • style配合scoped属性,保证样式只针对当前template内标签生效
  • vue配合webpack,把他们打包起来插入到index.html
// template 只能有一个根标签  
<template>
  <div>

  </div>
</template>

<script>
  export default {
  
  }
</script>
//  当前组件的样式  设置scoped 可以保证样式只对当前页面有效
<style scoped>

</style>

@vue/cli欢迎界面清理:

  • src/App.vue 默认有很多内容,可以全部删除留下框
  • assets和components文件夹下一切都删除掉(不要默认的欢迎页面)

vscolde插件补充:

vue文件代码高亮插件:

在这里插入图片描述

代码提示插件:

在这里插入图片描述

快速生成:

在这里插入图片描述

脚手架项目中定义全局过滤器:

过滤时间

  • 项目中下载moment模块
yarn add moment
  • main.js引入moment并创建全局过滤器
import moment from 'moment'
// 全局过滤器

Vue.filter('formData', (val) => {
  return moment(val).format('YYYY-MM-DD  h:mm:ss')
})
  • 项目中的任何一个vue文件都可以使用
{{ obj.time | formData }}

vue组件:

为什么用组件:

在这里插入图片描述

需求:想要多个收起 展开的部分

方法一:复制代码
  • 代码重复 冗余
  • 不利于维护
<template>
  <div id="app">
    <!-- 目标:点击展开或收起时,把内容区域显示或者隐藏 -->
    <h3>折叠面板</h3>
    <div>
      <div class="title">
        <h4>芙蓉楼送辛渐</h4>
        <span @click="isShow = !isShow">{{ isShow ? '收起' : '展开' }}</span>
      </div>
      <div class="container" v-show="isShow">
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
      </div>
    </div>
    <!-- 复制 -->
    <div>
      <div class="title">
        <h4>芙蓉楼送辛渐</h4>
        <span @click="isShow1 = !isShow1">{{ isShow1 ? '收起' : '展开' }}</span>
      </div>
      <div class="container" v-show="isShow1">
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
      </div>
    </div>
    <!-- 复制 -->
    <div>
      <div class="title">
        <h4>芙蓉楼送辛渐</h4>
        <span @click="isShow2 = !isShow2">{{ isShow2 ? '收起' : '展开' }}</span>
      </div>
      <div class="container" v-show="isShow2">
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
        <p>一片冰心在玉壶</p>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: false,
      isShow1: false,
      isShow2: false,
    }
  },
}
</script>

<style scoped>
#app {
  width: 400px;
  margin: 20px auto;
  background-color: yellow;
  border: 4px solid burlywood;
  border-radius: 2px;
  padding: 10px;
}
h3 {
  text-align: center;
}
.title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #ccc;
  padding: 0 5px;
}
</style>

vue组件-概念:

可复用的vue实例,封装标签,样式和代码

组件化: 封装的思想,把页面上 可复用的部分,封装为组件,从而方便项目的开发和维护

一个页面,可以拆分成一个个组件(一个vue文件),一个组件就是一个整体,每个组件可以有自己独立的结构,样式和行为(html,css,js)

在这里插入图片描述

vue组件–基础使用:

每个组件都是一个独立的个体,代码里体现为一个独立的vue文件

哪部分标签复用,就把哪部分封装到组件内

  • 组件内template只能有一个标签
  • 组件内的data必须是一个函数,独立作用域
步骤:
1.创建组件
  • 创建组件 components/Pannel.vue

封装结构+样式+js 组件都是独立的,为了复用

<template>
  <div>
    <div class="title">
      <h4>芙蓉楼送辛渐</h4>
      <span @click="isShow = !isShow">{{ isShow ? '收起' : '展开' }}</span>
    </div>
    <div class="container" v-show="isShow">
      <p>一片冰心在玉壶</p>
      <p>一片冰心在玉壶</p>
      <p>一片冰心在玉壶</p>
      <p>一片冰心在玉壶</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: false,
    }
  },
}
</script>

<style scoped>
.title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #ccc;
  padding: 0 5px;
}
</style>
2.注册组件

注册组件,创建后需要注册后再使用

全局注册使用

全局入口main.js 在new Vue之上注册

语法:

// 全局注册组件
// 1 引入组件
import Pannel from './components/Pannel'
// 2 全局注册组件
/* 
 Vue.component('组件名',组件对象)
*/
Vue.component('PannelG', Pannel)

全局注册组件PannelG,就可以当做标签在任意vue文件中的template中使用

单双标签都可以或者小写加 - 形式,运行后,会把这个自定义标签当做组件解析,使用 组件里封装的标签替换到这个位置

<PannelG></PannelG>
<PannelG />
<pannel-g></pannel-g>
局部注册使用:

语法

import 组件对象 from 'vue文件路径'

export default {
    components:{
        '组件名':组件对象
    }
}

在vue文件中引入、注册、使用

<template>
  <div id="app">

    <hr />
    <MyPannel></MyPannel>
    <my-pannel></my-pannel>
  </div>
</template>

<script>
// 1引入组件
import MyPannel from './components/MyPannel.vue'
export default {
  //2 局部注册组件
  components: {
    MyPannel: MyPannel,
  },
}
</script>

组件使用总结:

  • (创建)封装html+css+js到独立的.vue文件中
  • (引入注册)组件文件==>得到组件配置对象
  • (使用)当前页面当做标签使用

vue组件-scoped作用:

解决多个组件样式名相同,冲突问题

需求:div标签名选择器,设置背景色

问题: 组件里的div和外面的div都生效了

解决:给Pannel.vue组件里style标签上加scoped属性

<style scoped>

在style上假如scoped属性,就会在此组件的标签上加上一个随机生成的data-v开头的属性

而且必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到

style上加scoped,组件内的样式只在当前vue组件生效

组件通信:

每个组件的变量和值都是独立的

父:使用其他组件的vue文件(App.vue)

子:被引入的组件(MyPannel.vue)

从外面给组件内传值,学会语法,练习,项目看使用场景

父向子:
  • 创建组件componets/MyProduct.vue
  • 组件内在props定义变量,用于接收外部传入的值
  • App.vue中引入注册组件,使用时,传入具体数据给组件显示

口诀:

1:子组件--props--变量(准备接收)

2:父组件--自定义属性-传值进去
父组件–App.vue
<template>
  <div>
    <!-- 
      每次组件显示不同的数据信息
      2-自定义属性:给子组件传值

      口诀:
      1:子组件--props--变量(准备接收)
      2:父组件--自定义属性-传值进去
     -->
    <my-product title="超好吃的口水鸡" price="199"></my-product>
    <my-product title="超级难吃的浏览" price="299"></my-product>
    <my-product :title="tit" :price="p"></my-product>
  </div>
</template>

<script>
import MyProduct from './components/MyProduct.vue'
export default {
  data() {
    return {
      tit: '超级大的西瓜',
      p: 50,
    }
  },
  components: {
    // MyProduct: MyProduct,
    MyProduct,
  },
}
</script>
子组件 MyProduct.vue
<template>
  <div class="list">
    <h3>标题:{{ title }}</h3>
    <p>价格:{{ price }}</p>
    <p>开业大酬宾,全场8折</p>
  </div>
</template>

<script>
export default {
  // 1-组件内props定义变量  用来接收外部传入的值
  props: ['title', 'price'],
}
</script>

<style scoped>
.list {
  width: 300px;
  height: 140px;
  border: 1px solid;
  border-radius: 10px;
}
</style>

在这里插入图片描述

父传子–配合循环:

把数据循环分别传入给组件内显示

<template>
  <div>
    <!-- 
      每次组件显示不同的数据信息
      2-自定义属性:给子组件传值

      口诀:
      1:子组件--props--变量(准备接收)
      2:父组件--自定义属性-传值进去
     -->
    <my-product
      v-for="item in arr"
      :key="item.id"
      :title="item.tit"
      :price="item.price"
    ></my-product>
  </div>
</template>

<script>
import MyProduct from './components/MyProduct.vue'
export default {
  data() {
    return {
      arr: [
        {
          id: 1,
          tit: '超好吃的口水鸡',
          price: 199,
        },
        {
          id: 2,
          tit: '超好吃的老鸭',
          price: 49,
        },
        {
          id: 3,
          tit: '超级无敌的冰激凌',
          price: 1999,
        },
      ],
    }
  },
  components: {
    // MyProduct: MyProduct,
    MyProduct,
  },
}
</script>
单向数据流:

在vue中需要遵循单向数据流原则

1:父组件的数据发生了改变,子组件会自动跟着变

2:子组件不能直接修改父组件传递过来的props,props是只读的

父组件传给子组件是一个对象,子组件修改对象的属性, 是不会报错的,对象是引用类型,互相更新

总结:props的值不能重新赋值,对象引用关系属性值改变,互相影响

子传父:

从子组件把值传出来给外面使用

语法:

  • 父 : @自定义事件名=“父methods函数”
  • 子 : this.$emit(‘自定义事件名’,传值)—执行父methods里函数
父组件
<template>
  <div>
    <!-- 
      11-父组件:
      @自定义事件名="父methods函数"
     -->
    <my-product @priceevent="priceFn" :title="tit" :price="price"></my-product>
  </div>
</template>

<script>
import MyProduct from './components/MyProduct.vue'
export default {
  data() {
    return {
      tit: '超级好吃的口水鸡',
      price: 199,
    }
  },
  methods: {
    priceFn(val) {
      // val就是从子组件传递过来的值
      console.log(val)
      this.price = val
    },
  },
  components: {
    // MyProduct: MyProduct,
    MyProduct,
  },
}
</script>
子组件
<template>
  <div class="list">
    <h3>标题:{{ title }}</h3>
    <p>价格:{{ price }}</p>
    <p>开业大酬宾,全场8折</p>
    <button @click="changPrice">更改</button>
  </div>
</template>

<script>
export default {
  // 1-组件内props定义变量  用来接收外部传入的值
  props: ['title', 'price'],
  methods: {
    changPrice() {
      // 从子组件中把值传递出去

      // this.$emit触发自定义事件
      // this.$emit('自定义事件名',传递参数)

      this.$emit('priceevent', 599)
    },
  },
}
</script>

在这里插入图片描述

非父子:

两个组件的关系非常复杂,通过父子组件通信是非常麻烦的,使用通用的组件通讯方式:事件总线(event-bus

第一步:定义事件总线对象EventBus:

src/EventBus/index.js

import Vue from 'vue'

// 导出空白的vue对象
export default new Vue()
MyProduct.vue触发事件:
<script>
import EventBus from '../EventBus'
export default {
  methods: {
    transmitFn() {
      //  传递数据方
      // 触发自定义事件
      EventBus.$emit('transmitEvent', 'hello')
    },
  },
}
</script>
MyChild.vue注册事件:
import EventBus from '../EventBus'
export default {
  data() {
    return {
      msg: '',
    }
  },
  mounted() {
    // 组件创建完毕  监听transmitEvent事件
    // 接收方
    // 自定义事件  transmitEvent
    EventBus.$on('transmitEvent', (v) => {
      console.log(v, '接收方')
      this.msg = v
    })
  },
}

总结:空的vue对象,只负责on注册事件emit触发事件 一定要确保$on先执行

vue生命周期:

组件从创建到销毁的整个过程就是生命周期

钩子函数:

vue框架内置函数,随着组件的生命周期阶段,自动执行

作用:

特定的时间点,执行特定的操作

场景:

组件创建完毕后,可以在created生命周期函数中发起ajax请求,初始化data数据;mounted函数中,操作Dom元素; beforeDestory生命周期函数中 ,解绑事件,清除定时器…

4大阶段 8个方法:

创建:
  • beforeCreate
  • created

在这里插入图片描述

挂载:
  • beforeMount
  • mounted

在这里插入图片描述

更新:
  • beforeUpdate
  • updated

在这里插入图片描述

代码:
<template>
  <div>
    <p>学习vue生命周期</p>
    <p id="myP">{{ msg }}</p>
    <ul id="myul">
      <li v-for="(val, index) in arr">
        {{ val }}
      </li>
    </ul>
    <button @click="arr.push(122)">点击添加</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: 'hello world',
      arr: [5, 8, 2, 1],
      timer: null, //保存定时器
    }
  },
  /* 
    一:创建阶段
    new Vue()以后,vue内部给实例添加了一些属性和方法  data和methods
  */
  beforeCreate() {
    //创建前  没有数据  没有方法
    console.log('beforeCreate')
    console.log(this.msg) //undefined
  },
  created() {
    // 创建后
    // data和methods初始化之后
    // 场景:网络请求   注册全局事件  开启定时器
    console.log('created')
    console.log(this.msg) //  "hello world"

    this.timer = setInterval(() => {
      console.log('娃哈哈')
    }, 1000)
  },
  /* 
  
  挂载
  真实dom挂载之前
  场景:预处理data,不会触发updated钩子函数
  */
  beforeMount() {
    console.log('beforeMount')
    console.log(document.getElementById('myP')) //null
  },
  /*
  真实dom挂载以后
  场景: ajax请求  dom操作 echarts 
   */
  mounted() {
    console.log('mounted')
    console.log(document.getElementById('myP')) //<p data-v-7ba5bd90="" id="myP">hello world</p>
  },

  /* 
    以上四个生命周期钩子函数在整个生命周期过程中只执行一次
  */

  /* 
 更新阶段:data数据改变才会执行
 */
  beforeUpdate() {
    // 更新之前
    console.log('beforeUpdate')
    console.log(document.querySelectorAll('#myul>li')[4]) //undefined
  },

  updated() {
    // 更新之后
    // 获取更新后的真实dom
    console.log('updated')
    console.log(document.querySelectorAll('#myul>li')[4]) //<li data-v-7ba5bd90=""> 122 </li>
  },
}
</script>

<style scoped></style>
销毁:
  • beforeDestory
  • destoryed

在这里插入图片描述

$refs:

$refs获取DOM:

利用ref和$refs获取DOM元素

<template>
  <div>
    <p>获取原生Dom元素</p>
    <h1 id="myH" ref="myH">我是一名学员</h1>
  </div>
</template>

<script>
export default {
  mounted() {
    // console.log(document.getElementById('myH'))
    /* 
    1:给标签定义ref属性
    2:通过this.$refs.属性名  获取元素
    */
    console.log(this.$refs.myH)
  },
}
</script>

$refs获取组件对象:

  • 创建组件MyProduct.vue
<template>
  <div>MyProduct组件</div>
</template>

<script>
export default {
  data() {
    return {
      msg: 'hello',
    }
  },
  methods: {
    fn() {
      console.log('组件被调用了')
    },
  },
}
</script>
  • 获取组件对象,调用组件方法
<template>
  <div>
    <h2>获取组件对象--可以调用组件内的一切</h2>
    <my-product ref="myP"></my-product>
  </div>
</template>

<script>
/* 
 1:创建组件/引入组件/注册组件/使用组件
 2: 给组件起别名 ref
 3: 恰当时机,获取组件对象
*/
import MyProduct from './components/MyProduct'
export default {
  components: {
    MyProduct,
  },
  mounted() {
    let myPobj = this.$refs.myP
    console.log(myPobj.msg)
    myPobj.fn()
  },
}
</script>

$nextTick:

vue更新DOM–异步的:

需求:点击count++,通过原生DOM拿标签内容,无法拿到新值

<template>
  <div>
    <h2>$nextTick</h2>
    <p ref="myP">
      {{ count }}
    </p>
    <button @click="addCount">count++</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    addCount() {
      this.count++ //vue监听数据更新,开启一个DOM更新队列(异步任务)
      // console.log(this.$refs.myP.innerHTML) //0

      /* 
         vue更新DOM 异步任务
         解决:this.$nextTick
         过程: DOM更新完会挨个触发$nextTick里的函数体
      */
      this.$nextTick(() => {
        console.log(this.$refs.myP.innerHTML)
      })
    },
  },
}
</script>

场景:

点击搜索按钮,弹出文本框并获取焦点,按钮消失

$nextTick和async、await

<template>
  <div>
    <button @click="btnFn" v-if="isShow">搜索</button>
    <input v-else type="text" placeholder="这是一个文本框" ref="myInp" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: true,
    }
  },
  methods: {
    async btnFn() {
      this.isShow = false
      // this.$refs.myInp.focus()
      // 原因:DOM更新是异步的  文本框还没有挂载到真实DOM上

      // this.$nextTick(() => {
      //   this.$refs.myInp.focus()
      // })
      //  等同于
      // await取代回调函数
      await this.$nextTick()
      this.$refs.myInp.focus()
    },
  },
}
</script>

组件name使用:

可以用组件的name属性值,来注册组件名字

我们封装的组件,可以自己定义name属性组件名 让使用者有个统一的前缀风格

components/MyCom.vue

<template>
  <div>我是一个Com组件</div>
</template>

<script>
export default {
  name: 'ComNameHaHa',   //注册时可以定义自己的名字
}
</script>

<style scoped></style>

App.vue中注册和使用:

<template>
  <div>
    <ComNameHaHa></ComNameHaHa>
    <MyProducts></MyProducts>
  </div>
</template>

<script>
import Com from './components/MyCom.vue'
import Pro from './components/MyProduct.vue'
export default {
  components: {
    [Pro.name]: Pro,
    [Com.name]: Com, //对象里的key是变量的话  []  属性名表达式
    // 相当于
    // ComNameHaHa: Com,
  },
}
</script>

动态组件:

多个组件使用一个挂载点,并动态切换,就是动态组件

vue内置component组件,配合is属性,设置要显示的组件

需求:完成一个注册功能页面,2个按钮切换,一个填写注册信息,一个填写用户简介信息

  • 定义两个组件 UserName.vue , UserInfo.vue 2个组件
  • 引入到App.vue组件中
  • data中定义变量来存放要显示的组件名
  • 要设置挂载点 <component> 使用is属性来设置要显示那个组件
  • 点击按钮 修改变量里的组件名
<template>
  <div>
    <button @click="comName = 'UserName'">账号密码</button>
    <button @click="comName = 'UserInfo'">个人信息</button>
    <p>下面显示动态切换组件</p>
    <div style="border: 1px solid red">
      <component :is="comName"></component>
    </div>
  </div>
</template>

<script>
/* 
同一个挂载点要切换不同组件  显示
 1:创建要切换的组件    标签+样式
 2:引入到要展示的vue文件内,注册
 3:变量  承载要显示的组件名
 4:设置挂载点 <component :is="变量"></component>
 5:点击按钮 切换comName值为要显示的组件名

*/
import UserName from './components/UserName.vue'
import UserInfo from './components/UserInfo.vue'
export default {
  data() {
    return {
      comName: 'UserName',
    }
  },
  components: {
    UserName,
    UserInfo,
  },
}
</script>

组件缓存:

组件切换会导致组件被频繁的销毁和重新创建,性能不高

使用vue内置的keep-alive组件,可以让包裹的组件保存在内存中不被销毁

演示:给UserName.vue 和 UserInfo.vue 注册created 和destroyed生命周期事件,观察创建和销毁的过程

使用keep-alive内置的vue组件,让动态组件缓存

语法:

Vue内置的keep-alive组件,包裹要频繁切换的组件

App.vue:

<div style="border: 1px solid red">
    <!-- vue内置的keep-alive组件,把包起来的组件缓存起来 -->
    <keep-alive>
        <component :is="comName"></component>
    </keep-alive>
</div>

补充生命周期:(组件缓存下新增的两个钩子)

  • activated 激活时触发

  • deactivated 失去激活状态触发

<template>
  <div>
    <p>用户名:<input type="text" /></p>
    <p>密码: <input type="text" /></p>
  </div>
</template>

<script>
export default {
  created() {
    console.log('UserName创建')
  },
  destroyed() {
    console.log('UserName销毁')
  },
  // 组件缓存下  多了两个钩子函数
  activated() {
    console.log('activated')  //激活状态
  },
  deactivated() {
    console.log('deactivated')  // 失去激活状态
  },
}
</script>

组件插槽slot:

用来实现组件内容的分发,通过slot标签,可以接收到写在组件标签内的内容

vue提供组件插槽的能力,允许开发者在封装组件时,把不确定的部分定义为插槽

基础使用–匿名插槽:

  • 组件内使用slot 占位
  • 使用组件时 <pannel></pannel>中间传入标签替换slot
Pannel.vue:
<template>
  <div>
    <div class="title">
      <h4>鹅鹅鹅</h4>
      <span @click="isShow = !isShow">{{ isShow ? '收起' : '展开' }}</span>
    </div>
    <div class="container" v-show="isShow">
      <!--1: 组件内使用slot 占位 -->
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: false,
    }
  },
}
</script>

<style scoped>
h4 {
  text-align: center;
}
.title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #ccc;
  padding: 0 10px;
}

.title h4 {
  line-height: 2;
  margin: 0;
}

.container {
  border: 1px solid #ccc;
  padding: 0 10px;
}
</style>
App.vue:
<template>
  <div>
    <!-- 2 使用组件时 pannel 
    在组件中间 传入标签替换slot -->
    <pannel>
      <p>曲项向天歌</p>
      <p>曲项向天歌</p>
      <p>曲项向天歌</p>
      <p>曲项向天歌</p>
      <p>曲项向天歌</p>
    </pannel>
    <pannel>
      <img src="./assets/logo.png" alt="" />
    </pannel>
  </div>
</template>

<script>
import Pannel from './components/Pannel.vue'
export default {
  components: { Pannel },
}
</script>
默认内容:

如果外面不给传,想给个默认显示内容

<slot>夹着内容默认显示内容,如果不给插槽slot传东西,就使用slot夹着的内容在原地显示

<slot>默认内容</slot>

具名插槽v-slot:

当一个组件内有2处以上需要外部传入标签的地方

传入的标签可以分别派发给不同的slot位置

v-slot一般跟template标签使用(template是HTML5新出标签内容模板元素,不会渲染在页面上,一般被vue解析内部标签)

Pannel.vue:
<template>
  <div>
    <div class="title">
      <slot name="title"></slot>
      <span @click="isShow = !isShow">{{ isShow ? '收起' : '展开' }}</span>
    </div>
    <div class="container" v-show="isShow">
      <!--1: 组件内使用slot 占位 -->
      <slot name="content"></slot>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: false,
    }
  },
}
</script>
App.vue:
<template>
  <div>
    <!-- 2 使用组件时 pannel 
    在组件中间 传入标签替换slot -->
    <pannel>
      <template v-slot:title>
        <h4>鹅鹅鹅</h4>
      </template>
      <template v-slot:content>
        <span>我是内容</span>
      </template>
    </pannel>
       <!-- v-slot: 简写为 # -->
    <pannel>
      <template #title>
        <h2>hello</h2>
      </template>
      <template #content>
        <p>123</p>
      </template>
  </div>
</template>
总结:

v-slot: 可以简写为 #

slot的name属性起插槽名,使用组件时,template配合#插槽名传入具体的标签

作用域插槽(可以将主组件的属性获取到):

子组件里的值,在给插槽赋值时在父组件环境下使用

步骤:
  • 子组件:在slot上绑定属性和子组件内的值
  • 使用组件时,传入自定义标签,用template和v-slot=“自定义变量名scope”
  • scope变量名自动绑定slot上所有的属性和值
Pannel.vue:
<template>
  <div>
    <div class="title">
      <!-- 1:slot上绑定属性和子组件内的值 -->
      <slot name="title" :msg="msg" tit="nihao"></slot>
      <span @click="isShow = !isShow">{{ isShow ? '收起' : '展开' }}</span>
    </div>
    <div class="container" v-show="isShow">
      <!--1: 组件内使用slot 占位 -->
      <slot name="content"> </slot>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShow: false,
      msg: 'hello world',
    }
  },
}
</script>
App.vue:
<template>
  <div>
    <pannel>
      <!--2 传入自定义标签 template和v-slot:插槽名="自定义变量名" -->
      <template v-slot:title="scope">
        <!--3 scope上自动绑定slot上所有属性和值 -->
        <h4>{{ scope.tit }}</h4>
      </template>
      <template v-slot:content>
        <span>我是内容</span>
      </template>
    </pannel>
    <!-- v-slot: 简写为 # -->
    <pannel>
      <template #title>
        <h2>hello</h2>
      </template>
      <template #content>
        <p>123</p>
      </template>
    </pannel>
  </div>
</template>
总结:
组件内变量绑定到slot上,使用组件v-slot:插槽名=“自定义变量”,变量上会自动绑定属性和值

使用场景:

需求:封装一个表格组件,在表格组件内循环产生单元格

  • 准备MyTable.vue组件—内置表格,传入数组循环铺设页面,把对象每个内容显示在单元格里
  • App.vue组件里,准备数据传入给MyTable.vue使用
  • 分析
    • 想要给td内显示图片,传入自定义的img标签(td中准备slot占位符,但是外面需要把图片地址赋予给src属性,在slot上把item数据进行绑定)
MyTable.vue:

需求:封装一个表格组件,在表格组件内循环产生单元格

  • 准备MyTable.vue组件—内置表格,传入数组循环铺设页面,把对象每个内容显示在单元格里
  • App.vue组件里,准备数据传入给MyTable.vue使用
  • 分析
    • 想要给td内显示图片,传入自定义的img标签(td中准备slot占位符,但是外面需要把图片地址赋予给src属性,在slot上把item数据进行绑定)
MyTable.vue:
<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>序号</th>
          <th>姓名</th>
          <th>年龄</th>
          <th>头像</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item, index) in list" :key="index">
          <td>{{ index + 1 }}</td>
          <td>{{ item.name }}</td>
          <td>{{ item.age }}</td>
          <td>
            <slot :row="item">
              <!-- 默认值,使用组件不自定义标签显示默认文字 -->
              {{ item.headImgUrl }}
            </slot>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  props: ['list'],
}
</script>
App.vue:
<template>
  <div>
    <my-table :list="list">
      <template v-slot="scope">
        <img :src="scope.row.headImgUrl" width="50" alt="" />
      </template>
    </my-table>
    <my-table :list="list">
      <template v-slot="{ row }">  // 解构
        <a :href="row.headImgUrl">
          {{ row.headImgUrl }}
        </a>
      </template>
    </my-table>
  </div>
</template>

<script>
import MyTable from './components/MyTable.vue'
export default {
  components: { MyTable },
  data() {
    return {
      list: [
        {
          name: '张三',
          age: 18,
          headImgUrl: 'https://img01.yzcdn.cn/vant/ipad.jpeg',
        },
        {
          name: '李四',
          age: 23,
          headImgUrl: 'https://img01.yzcdn.cn/vant/ipad.jpeg',
        },
        {
          name: '王五',
          age: 45,
          headImgUrl: 'https://img01.yzcdn.cn/vant/ipad.jpeg',
        },
      ],
    }
  },
}
</script>

自定义指令:

除了核心功能默认内置的指令(v-model和v-show),vue允许我们注册自定义指令。 v-xxxx

html+css的复用的主要形式是组件

你需要对普通DOM元素进行底层操作,这时候就会用到自定义指令

目标:获取标签 扩展额外的功能

局部注册和使用

只能在当前组件中使用

<template>
  <div>
    <div class="main">
      <input type="text" v-focus />
    </div>
  </div>
</template>

<script>
/*
局部指令
directives:{
  '指令名':{
     bind(el,binding,vnode){},
     inserted(el,binding,vnode){}
  }
}

在标签上使用自定义指令  v-指令名

inserted 指令所在标签 被插入到页面上触发(一次)
update  指令对应的数据/标签更新时  触发
*/
export default {
  directives: {
    // 页面加载时获取焦点
    focus: {
      inserted(el, binding, vnode) {
        // console.log(el)
        el.focus()
      },
    },
  },
}
</script>

全局注册和使用

在任何的.vue文件中使用

main.js中用Vue.directive 全局注册指令

// 全局注册
Vue.directive('gfocus', {
  inserted(el) {
    el.focus()
  },
})

自定义指令-传值:

使用自定义指令,传入一个值

需求:定义color指令,传入一个颜色,给标签设置文字颜色

<template>
  <div>
    <div class="main">
      <input type="text" v-focus />
      <input type="text" v-gfocus />
    </div>
    <p v-color="theColor" @click="changeColor">修改文字颜色</p>
  </div>
</template>

<script>
/*
局部指令
directives:{
  '指令名':{
     bind(el,binding,vnode){},
     inserted(el,binding,vnode){}
  }
}

在标签上使用自定义指令  v-指令名

inserted 指令所在标签 被插入到页面上触发(一次)
update  指令对应的数据/标签更新时  触发
*/
export default {
  data() {
    return {
      theColor: 'blue',
    }
  },
  methods: {
    changeColor() {
      this.theColor = 'yellow'
    },
  },
  directives: {
    // 页面加载时获取焦点
    focus: {
      inserted(el, binding, vnode) {
        // console.log(el)
        el.focus()
      },
    },
    // 给标签设置文字颜色
    color: {
      inserted(el, binding) {
        el.style.color = binding.value
      },
      update(el, binding) {
        el.style.color = binding.value
      },
    },
  },
}
</script>

vue路由:

生活中的路由:

设备和ip的映射关系

在这里插入图片描述

后端路由:

接口和服务的映射关系

在这里插入图片描述

前端路由:

路径和组件的映射关系

在这里插入图片描述

为什么使用路由:

在一个页面里,切换业务场景

vue单页面应用(SPA)所有的功能在一个html页面上实现

优点:
  • 整体不刷新页面,用户体验好
  • 数据传递容易,开发效率高
缺点:
  • 开发成本高(需要学习专门知识)
  • 首次加载比较慢,不利于SEO

vue-router介绍:

vue集成路由

vue-router模块包,它和vue.js深度集 成

定义映射规则-----模块化—提供2个内置全局组件

路由-组件分类:

.vue文件分2类,一个页面组件,一个是复用组件

.vue文件本质无区别

src/views(pages)文件夹 —页面组件—配合路由使用

src/components文件夹 —复用组件(展示数据–复用)

在这里插入图片描述

总结:views下的页面组件,配合路由切换;components下的一般引入到views下的vue中复用展示数据

vue-router使用:

安装:
npm i [email protected]
导入路由:

src下创建router/index.js

import Vue from 'vue'
// 1导入路由
import VueRouter from 'vue-router'
// 引入组件
// import Home from '../views/Home'
// import Login from '../views/Login'
// import Register from '../views/Register'

// 2 使用路由插件
// 在vue中  使用vue的插件  需要调用Vue.use()
Vue.use(VueRouter)

// 3 创建vue路由规则
const routes = [
  // 路径和组件的映射关系
  {
    path: '/',
    // component: Home,
    // 路由懒加载
    component: () => import('../views/Home'),
  },
  {
    path: '/login',
    component: () => import('../views/Login'),
  },
  {
    path: '/register',
    component: () => import('../views/Register'),
  },
]

// 4创建路由对象  传入规则
const router = new VueRouter({
  // routes: routes,
  routes,
})

// 导出路由对象
export default router
main.js:
import router from './router'

new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app')
路由出口:

App.vue

<router-view></router-view>
声明式导航:

使用全局组件 router-link来替代a标签

  • vue-router提供了一个全局组件 router-link
  • router-link 实质上最终会渲染成a链接,to属性等价于提供href属性(to无需#)
  • router-link提供了声明式导航高亮的功能(自带类名)
<template>
  <div>
    <div class="nav">
      <router-link to="/">首页</router-link>
      <router-link to="/login">登陆</router-link>
      <router-link to="/register">注册</router-link>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {}
</script>

<style scoped>
.nav {
  display: flex;
  width: 400px;
  justify-content: space-around;
}

.router-link-exact-active {  // 高亮显示,点击谁谁变红
  color: red;
}
</style>

重定向:

强制切换到目标path上

  • 网页打开url默认hash值是 / 路径
  • redirect 是设置要重定向到那个路由路径

需求:网页默认打开,匹配路由 ‘/’ 强制切换到 ‘/home’上

const routes = [
  {
    path: '/',
    redirect: '/home', //重定向到  /home
  },
  }

总结: 强制重定向后,还会重新来数组中匹配一次规则

路由-404页面:

如果路由hash值没有和数组里规则匹配

默认给一个404页面

路由最后,path匹配 *(任意路径) --前面都不匹配,就匹配最后这个,显示对应的组件

  • 创建NotFound页面
<template>
  <div>404</div>
</template>

<script>
export default {}
</script>

<style scoped></style>
  • 修改路由配置
const routes = [
     // 写在最后
  // {
  //   path: '*',
  //   redirect: '/home',
  // },
  {
    path: '*',
    component: () => import('../views/NotFound'),
  },
]

**总结:**如果路由未命中任何规则,给出一个兜底的404页面

路由模式设置:

修改路由在地址栏的模式

hash路由 : 地址栏URL中的#符号 http://localhost:3000/#/abc ,不会被包括在HTTP请求中,对后端完全没有影响,改变hash不会重新加载页面

history路由:http://localhost:3000/abc(需要服务器支持,否则找的是文件夹)

利用了HTML5 新增的pushState() 和replaceState()方法
// 4创建路由对象  传入规则
const router = new VueRouter({
  // routes: routes,
  routes,
  mode: 'history', //默认是hash
})

vue路由-编程式导航:

编程式导航:用js代码跳转

声明式导航:router-link实现跳转

语法:
this.$router.push({
    path:"路由路径",
    name:"路由名"
})
router/index.js 路由规则里,给路由起名字
const routes = [
  // 路径和组件的映射关系
  {
    path: '/home',
    name: 'home',
    component: () => import('../views/Home'),
  },
  {
    path: '/login',
    name:"login",
    component: () => import('../views/Login'),
  },
  {
    path: '/register', 
    name:"register",
    component: () => import('../views/Register'),
  },

]
App.vue中router-link换成span,配合js的编程式导航跳转
<template>
  <div>
    <div class="nav">
      <!-- <router-link to="/">首页</router-link>
      <router-link to="/login">登陆</router-link>
      <router-link to="/register">注册</router-link> -->
      <span @click="goTo('/home', 'home')">首页</span>
      <span @click="goTo('/login', 'login')">登录</span>
      <span @click="goTo('/register', 'register')">注册</span@click=>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>

/* 
编程式导航:  js方式跳转路由
语法:
this.$router.push({path:"路由路径"})
this.$router.push({name:"路由名"})
注意:
虽然用name跳转,但是url的hash值还是切换path路径值
场景
方便修改,name路由名(在页面上看不见,随便修改)
path可以在url的hash值看到(尽量符合组件内规范)
*/
export default {
  methods: {
    goTo(targetPath,targetName) {
      this.$router.push({
        // path:targetPath
        name:targetName
      })
    }
  },
}
</script>

嵌套路由:

在现有的一级路由下,再嵌套二级路由

在这里插入图片描述

  • 创建需要用的所有组件
    • src/views/Find.vue ----发现音乐
    • src/views/My.vue—我的音乐
    • src/views/Second/Recommend.vue —发现音乐/推荐页面
    • src/views/Second/Ranking.vue —发现音乐/排行榜
    • src/views/Second/SongList.vue —发现音乐/歌单页面
  • main.js 配置2级路由
    • 一级路由由path从/开始定义
    • 二级路由往后path 直接写名字 ,无需 / 开头
    • 二级路由在上级路由的children数组里编写路由信息对象
  • 说明
    • App.vue 的router-view 负责发现音乐 和我的音乐页面 切换
    • Find.vue的router-view负责发现音乐下的三个页面切换

配置路由规则:

const routes = [
  {
    path: '/',
    redirect: '/find', //重定向到  /home
  },
  {
    path: '/find',
    redirect: '/find/recommend',
    component: () => import('../views/Find'),
    children: [
      {
        path: 'recommend',
        component: () => import('../views/Second/Recommend'),
      },
      {
        path: 'ranking',
        component: () => import('../views/Second/Ranking'),
      },
      {
        path: 'songlist',
        component: () => import('../views/Second/SongList'),
      },
    ],
  },
  {
    path: '/my',
    component: () => import('../views/My'),
  },
  {
    path: '*',
    component: () => import('../views/NotFound'),
  },
]

App.vue写路由出口:

App.vue 的router-view 负责发现音乐 和我的音乐页面 切换

<template>
  <div>
    <div class="nav">
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {}
</script>

<style scoped>
.nav {
  display: flex;
  width: 400px;
  justify-content: space-around;
}
</style>

Find.vue写二级路由出口:

Find.vue的router-view负责发现音乐下的三个页面切换

<template>
  <div>
    <div class="nav_main">
      <router-link to="/find/recommend">推荐</router-link>
      <router-link to="/find/ranking">排行榜</router-link>
      <router-link to="/find/songlist">歌单</router-link>
    </div>
    <div style="1px solid red">
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {}
</script>

<style scoped>
.nav_main {
  background-color: red;
  color: white;
  padding: 10px 0;
}
.nav_main a {
  text-align: center;
  text-decoration: none;
  color: white;
  margin: 7px 20px;
  padding: 0px 15px;
  height: 20px;
  display: inline-block;
  line-height: 20px;
}
.nav_main a:hover {
  background-color: brown;
}

.nav_main .router-link-exact-active {
  background-color: brown;
}
</style>

**总结:**嵌套路由,找准在那个页面里写router-view和对应的规则里写children

声明式导航-类名区别:

router-link 自带的2个类名的区别是什么

router-link-active(模糊匹配) url中hash值,包含href属性值这个路径

router-link-exact-active(精确匹配) url中hash值路径,与href属性值完全相同,设置此类名

在这里插入图片描述

路由传参:

跳转路由时 可以给路由对应的组件内传参

声明式导航:

router-link 上的to属性,语法格式

/path?参数名=值
/path/值  ------需要路由对象提前配置   path:'/path/:参数名'
对应的页面组件接收传递过来的值
$route.query.参数名
$route.params.参数名
router/index.js
// 3 创建vue路由规则
const routes = [
  {
    path: '/',
    redirect: '/list', //重定向到  /home
  },
  {
    path: '/list',
    component: () => import('../views/List'),
  },
  {
    path: '/part',
    component: () => import('../views/Part'),
  },
  {
    path: '/detail/:name',
    component: () => import('../views/Detail'),
  },
  {
    path: '*',
    component: () => import('../views/NotFound'),
  },
]
List.vue:
<template>
  <div>
    <router-link to="/part?name=小川">朋友--小川</router-link>
    <router-link to="/detail/小妞">朋友--小妞</router-link>
    <router-link :to="'/part?name=' + n1">朋友--{{ n1 }}</router-link>
    <router-link :to="'/detail/' + n1">朋友--{{ n1 }}</router-link>
  </div>
</template>
<script>
export default {
  data() {
    return {
      n1: '花姐',
      n2: '露露',
    }
  },
}
</script>
Part.vue:
<template>
  <div>
    <p>关注明星</p>
    {{ $route.query.name }}
    <hr />
    {{ name }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: '',
    }
  },
  created() {
    // 创建完成  第一次操作data中数据  执行一次
    this.name = this.$route.query.name
    console.log(this.name)
  },
}
</script>
Detail.vue:
<template>
  <div>
    detail
    {{ $route.params.name }}
    <hr />
    {{ name }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: '',
    }
  },
  created() {
    this.name = this.$route.params.name
  },
}
</script>

编程式导航:

语法:

query/params 任选一个

this.$router.push({
    path:"路由路径",
    name:"路由名",
    query:{
        "参数名":"值"
    },
    params:{
        "参数名":"值"
    }
})
List.vue:
<template>
  <div>
    <router-link to="/part?name=小川">朋友--小川</router-link>
    <router-link to="/detail/小妞">朋友--小妞</router-link>
    <router-link :to="'/part?name=' + n1">朋友--{{ n1 }}</router-link>
    <router-link :to="'/detail/' + n1">朋友--{{ n1 }}</router-link>

    <hr />
    <span @click="oneFn">朋友--小川</span>
    <span @click="twoFn">朋友--小妞</span>
    <span>朋友--{{ n1 }}</span>
    <span>朋友--{{ n1 }}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      n1: '花姐',
      n2: '露露',
    }
  },
  methods: {
    oneFn() {
      this.$router.push({
        path: '/part',
        query: {
          // name: '小川',
          name: this.n2,
        },
      })
    },
    twoFn() {
      // path会自动的忽略params
      // this.$router.push({
      //   name: 'detail',
      //   params: {
      //     name: '小妞',
      //   },
      // })
      this.$router.push('/detail/' + this.n1)
    },
  },
}
</script>

路由守卫:

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。

全局前置守卫:

需求:在跳转路由前,判断用户是否登录,登录了才能跳转到“我的音乐“页面,未登录弹窗提示

在路由对象上使用固定的方法 beforeEach

// 1 在路由对象上使用固定的方法 beforeEach
/* 
路由跳转"之前" 先执行这里,决定是否跳转
router.beforeEach((to,from ,next)=>{
to  要跳转到的路由 (路由对象信息)  目标
from 从哪里跳转的路由(路由对象信息)  来源
next  函数体, next() 才会让路由正常的跳转切换,  next(false)在原地停留  next("路由路径") 强制修改到另一个路由路径上
不调用next 页面留在原地
})


*/
// 在跳转路由前,判断用户是否登录,登录了才能跳转到“part“页面,未登录弹窗提示
const isLogin = false //登陆状态 (未登陆)
router.beforeEach((to, from, next) => {
  // console.log(to)
  // console.log(from)
  if (to.path === '/part' && isLogin === true) {
    alert('请登陆')
    next(false)
  } else {
    next() //正常放行
  }
})

Vant组件库:

vant 轻量、可靠的移动端 Vue 组件库

安装:

yarn add vant@latest-v2 -S

导入所有的组件:

main.js中导入:
// 导入所有的组件
import Vant from 'vant'
import 'vant/lib/index.css'

Vue.use(Vant)

使用组件:

<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>

手动按需引入:

只引入使用的组件

在不使用插件的情况下,可以手动引入需要的组件。—每一个组件中引入

import Button from 'vant/lib/button';
import 'vant/lib/button/style';

注册–使用:

<template>
  <div>
    <van-button type="primary">主要按钮</van-button>
    <van-button type="info">信息按钮</van-button>
    <van-button type="default">默认按钮</van-button>
    <van-button type="warning">警告按钮</van-button>
    <van-button type="danger">危险按钮</van-button>
  </div>
</template>

<script>
import Button from 'vant/lib/button'
import 'vant/lib/button/style'
export default {
  components: {
    // VanButton: Button,
    [Button.name]: Button,
  },
}
</script>

自动按需引入组件:

babel-plugin-import 是一款 babel 插件,它会在编译过程中将 import 的写法自动转换为按需引入的方式。

安装插件:
yarn add babel-plugin-import -D
babel.config.js配置,重新启动项目:
module.exports = {
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
};
引入:

main.js

import { Button, Icon, Tabbar, TabbarItem, Tag } from 'vant'

Vue.use(Tabbar)
Vue.use(TabbarItem)
Vue.use(Button)
Vue.use(Icon)

Vue.use(Tag)
组件中使用:
<template>
  <div>
    <van-button type="primary">主要按钮</van-button>
    <van-button type="info">信息按钮</van-button>
    <van-button type="default">默认按钮</van-button>
    <van-button type="warning">警告按钮</van-button>
    <van-button type="danger">危险按钮</van-button>
    <van-icon name="chat-o" />
    <van-icon name="https://b.yzcdn.cn/vant/icon-demo-1126.png" />
  </div>
</template>

<script>
export default {}
</script>

<style scoped></style>

案例:

路由配置:

二级路由

组件:
  • Layout.vue —总的框架
  • List.vue ----商品列表
  • Search.vue -----商品搜索
  • My.vue-----我的信息
配置路由:
const routes = [
  {
    path: '/',
    redirect: '/list',
    component: () => import('../views/Layout'),
    children: [
      {
        path: 'list',
        component: () => import('../views/List'),
      },
      {
        path: 'search',
        component: () => import('../views/Search'),
      },
      {
        path: 'my',
        component: () => import('../views/My'),
      },
    ],
  },
  {
    path: '*',
    component: () => import('../views/NotFound'),
  },
]

底部封装:

  • 创建MyTabBar.vue组件
<template>
  <van-tabbar v-model="active" route>
    <van-tabbar-item icon="home-o" to="/list">商品列表</van-tabbar-item>
    <van-tabbar-item icon="search" to="/search">商品搜索</van-tabbar-item>
    <van-tabbar-item icon="friends-o" to="/my">我的信息</van-tabbar-item>
  </van-tabbar>
</template>

<script>
export default {
  data() {
    return {
      active: 0,
    }
  },
}
</script>

顶部封装:

  • 创建MyHead.vue组件
<template>
  <div class="head">TabBar案例</div>
</template>

<script>
export default {}
</script>

<style scoped>
.head {
  height: 50px;
  line-height: 50px;
  background-color: blue;
  text-align: center;
  color: white;
}
</style>

商品列表:

  • 封装MyTable.vue ===标签和样式
  • axios 在MyGoodList.vue请求数据回来
  • 请求地址https://www.escook.cn/api/goods
  • 传入MyTable.vue 中循环数据显示

axios 在MyGoodList.vue请求数据回来

  • 下载axios
    yarn add axios
    
  • MyGoodList.vue
<template>
  <div>
    <my-table :list="list"></my-table>
  </div>
</template>

<script>
import axios from 'axios'
import MyTable from './MyTable.vue'
export default {
  components: {
    MyTable,
  },
  data() {
    return {
      list: [],
    }
  },
  created() {
    // axios({
    //   url: 'https://www.escook.cn/api/goods',
    // }).then((res) => {
    //   let {
    //     data: { data: result },
    //   } = res
    //   console.log(result)
    // })
    this.getDate()
  },
  methods: {
    async getDate() {
      let {
        data: { data: result },
      } = await axios({ url: 'https://www.escook.cn/api/goods' })
      console.log(result)
      this.list = result
    },
  },
}
</script>
  • MyTable.vue
<template>
  <table class="table">
    <thead>
      <tr>
        <th>#</th>
        <th>名称</th>
        <th>价格</th>
        <th>标签</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in list">
        <td>{{ item.id }}</td>
        <td>{{ item.goods_name }}</td>
        <td>{{ item.goods_price }}</td>
        <td>{{ item.tags }}</td>
        <td>
          <van-button type="primary">删除</van-button>
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    list: Array,
  },
}
</script>

<style scoped>
.table {
  width: 100%;
  margin: 20px auto;
  border: 1px solid #333;
  border-collapse: collapse;
}
td,
th {
  border: 1px solid #333;
  height: 30px;
}
</style>

商品表格–插槽:

使用插槽技术,和作用域插槽技术,给MyTable.vue组件,自定义列标题,自定义表格内容

需求:允许用户自定义表个头和表格单元格内容

  • 把MyTable.vue里准备slot
  • 使用MyTable组件时传入具体标签

步骤:

  • 提高组件==复用性和灵活性 ==,把表格列标题thead部分预留slot ,设置name属性
  • 使用MyTable.vue时,传入列标题标签
  • 表格内容td部分也可以让组件使用者自定义,也给tbody预留slot 和name属性
  • 使用插槽需要用到插槽内的item对象上的数据,作用域插槽
MyTable.vue:
<template>
  <table class="table">
    <thead>
      <tr>
        <!-- <th>#</th>
        <th>名称</th>
        <th>价格</th>
        <th>标签</th>
        <th>操作</th> -->
        <slot name="header"></slot>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, index) in list">
        <!-- <td>{{ item.id }}</td>
        <td>{{ item.goods_name }}</td>
        <td>{{ item.goods_price }}</td>
        <td>{{ item.tags }}</td>
        <td>
          <van-button type="primary">删除</van-button>
        </td> -->
        <slot name="body" :row="item" :index="index"></slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    list: Array,
  },
}
</script>
MyGoodList.vue:
<template>
  <div>
    <my-table :list="list">
      <template #header>
        <th>#</th>
        <th>名称</th>
        <th>价格</th>
        <th>标签</th>
        <th>操作</th>
      </template>
      <template #body="{ row, index }">
        <td>{{ row.id }}</td>
        <td>{{ row.goods_name }}</td>
        <td>{{ row.goods_price }}</td>
        <td>{{ row.tags }}</td>
        <td>
          <van-button type="primary">删除</van-button>
        </td>
      </template>
    </my-table>
  </div>
</template>

商品表格tags:

<td>
    <van-tag v-for="(str, ind) in row.tags" :key="ind" type="primary">{{
        str
        }}</van-tag>
</td>

商品表格–删除功能:

点击删除按钮删除数据

分析:

  • 删除按钮绑定点击事件
  • 作用域插槽绑定id出来
  • 传给删除方法,删除MyGoodList.vue里数组的数据
MyGoodList.vue—注册点击事件:
 <van-button type="primary" @click="removeBtn(row.id)"
            >删除</van-button>
MyGoodList.vue 根据id删除:
methods:{
    removeBtn(id) {
      // 1 根据id查找下标
      let index = this.list.findIndex((obj) => obj.id === id)
      // 2 实现删除
      this.list.splice(index, 1)
    },
}

商品表格–添加tab:

需求:点击tab按钮,出现输入框自动获取焦点,失去焦点关闭输入框,会出新增tag,esc清空内容

  • 点击tab,按钮消失,输入框出现
  • 输入框自动获焦
  • 失去焦点,输入框消失,按钮出现
  • 检测输入框回车,无数据拦截
  • 输入框取消,清空数据
  • 检测输入框回车,有数据添加
点击tab,按钮消失,输入框出现:
  <div class="top">
            <van-field
              v-model="value"
              v-if="row.inputVisible"
              placeholder="请输入tab内容"
            />
            <van-button v-else @click="row.inputVisible = true" type="info"
              >tag+</van-button
            >
 </div>
输入框自动获焦:
<van-field
              v-model="value"
              v-if="row.inputVisible"
              placeholder="请输入tab内容"
              :autofocus="true"
            />
失去焦点,输入框消失,按钮出现:
 <van-field
              v-model="value"
              v-if="row.inputVisible"
              placeholder="请输入tab内容"
              :autofocus="true"
              @blur="row.inputVisible = false"
            />
输入框回车新增tag:
1:监听input的回车事件
<van-field
              v-model="row.inputValue"
              v-if="row.inputVisible"
              placeholder="请输入tab内容"
              :autofocus="true"
              @blur="row.inputVisible = false"
              @keydown.enter="enterFn(row)"
            />
2.事件处理函数
 // 新增tag
    enterFn(row) {
      // console.log(row, 0)
      // 非空判断
      if (row.inputValue.trim().length === 0) {
        return alert('请输入数据')
      }

      // 添加
      row.tags.push(row.inputValue)
      row.inputValue = ''
    },

input框 esc清空内容:

 @keydown.esc="row.inputValue = ''"

Vuex基础–介绍:

为什么会有vuex?

Vuex 是一个专为 Vue.js 应用程序开发的状态 管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化

Vuex是采用集中式管理组件依赖的共享数据的一个工具,可以解决不同组件数据共享问题

在这里插入图片描述

结论:

  • 修改state状态必须通过mutations
  • mutations只能执行同步代码,类似ajax,定时器之类的代码不能在mutations中执行
  • 执行异步代码,通过actions,然后将数据提交给mutations才可以完成
  • state的状态即共享数据可以在组件中引用
  • 组件中可以调用aciton

Vuex的使用:

下载:
yarn add [email protected]
src/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({})

export default store
main.js引入:
import store from './store/index'
new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount('#app')
Vuex–state:

state 放置所有公共状态的属性,如果你有一个公共状态数据,你只需要定义在state对象中

定义state:
// 初始化vuex对象
const store = new Vuex.Store({
  state: {
    // 管理数据
    count: 0,
  },
})

如何在组件中获取count?

原始形式–插值表达式:
Good.vue:

组件中可以使用this.$store 获取到vuex中的store对象实例,可通过state属性获取count

<div>state的数据{{ $store.state.count }}</div>

<script>
export default {
  created() {
    console.log(this.$store.state.count)
  },
}
</script>
辅助函数–mapState:

mapState是辅助函数,帮助我们把store中的数据映射到组件的计算属性中,属于一种方便用法

List.vue:

第一步:导入mapState

第二步:采用数组形式引入state属性

第三部:利用展开运算法将导出的状态映射给计算属性

<template>
  <div>
    list
    {{ count }}
  </div>
</template>

<script>
//导入mapState
import { mapState } from 'vuex'
export default {
//采用数组形式引入state属性
//利用展开运算法将导出的状态映射给计算属性
  computed: {
    ...mapState(['count']),
    // 类似于
    // count() {
    //   return this.$store.state.count
    // },
  },
}
</script>
vuex–mutations:

state数据的修改只能通过mutatons,并且mutations必须是同步更新,目的是形成数据快照

数据快照:一次mutation的执行,立刻得到一种视图状态,因为是立刻,所以必须是同步

定义mutations:
const store = new Vuex.Store({
  state: {
    // 管理数据
    count: 70,
  },
  // d定义mutations
  mutations: {
    
  }
})

mutations是一个对象,对象中存放修改state的方法

mutations: {
    // 方法里的参数 第一个参数是当前store的state属性
    // 第二个参数payload 运输参数  调用mutations的时候 可以传递参数
     addCount(state) {
      state.count += 1
    },
    addCountN(state, n) {
      state.count += n
    },
  },

如何在组件中调用mutations?

原始形式 $store:
Good.vue:
<template>
  <div>
    state的数据{{ $store.state.count }}
    <hr />
    <button @click="addCount">+1</button>
  </div>
</template>

<script>
export default {
  created() {
    console.log(this.$store.state.count)
  },
  methods: {
    addCount() {
      // 调用store中的mutations 提交给mutations
      // commit('mutations方法名',参数)
      this.$store.commit('addCount')
    },
  },
}
</script>
带参数的传递:
<button @click="addCountN(9)">+n</button>

 methods: {
    addCountN(n) {
      this.$store.commit('addCountN', n)
    },
  },
辅助参数–mapMutations:

mapMutations和mapState很像,把位于mutations中的方法提取出来,可以将它导入到methods中

<template>
  <div>
    list
    {{ count }}
    <button @click="addCount">+1</button>
    <button @click="addCountN(8)">+n</button>
  </div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
export default {
  computed: {
    ...mapState(['count']),
    // 类似于
    // count() {
    //   return this.$store.state.count
    // },
  },
  methods: {
    // 把位于mutations中的方法提取出来,可以将它导入到methods中
    ...mapMutations(['addCount', 'addCountN']),
  },
}
</script>
vuex-actions:

state是存放数据的,mutations是同步更新数据,actions是负责进行异步操作

定义actions:
actions: {
    // 获取异步的数据 context 表示当前的store实例
    // 可以通过context.state 获取状态
    // 也可以通过context.commit 来提交mutations
    // 也可以context.dispatch调用其它的action
    getAsyncCount(context) {
      setTimeout(() => {
        // 1秒后,要去修改state
        context.commit('addCount')
      }, 1000)
    },
  },
原始调用–$store.dispatch:
addAsyncCount() {
      this.$store.dispatch('getAsyncCount')
    },
传参函数:
  • actions
actions: {
    getAsyncCountN(context, n) {
      setTimeout(() => {
        // 1秒后,要去修改state
        context.commit('addCountN', n)
      }, 1000)
    },
  },
  • 调用
 <button @click="addAsyncCountN(6)">+nAsync</button>

 addAsyncCountN(m) {
      this.$store.dispatch('getAsyncCountN', m)
 },
辅助函数–mapActions

actions也有辅助函数,可以将action导入到组件中

<template>
  <div>
    list
    {{ count }}

    <button @click="getAsyncCount">+1Async</button>
    <button @click="getAsyncCountN(6)">+nAsync</button>
  </div>
</template>

<script>
import { mapState,  mapActions } from 'vuex'
export default {
  computed: {
    ...mapState(['count']),
    // 类似于
    // count() {
    //   return this.$store.state.count
    // },
  },
  methods: {
    ...mapActions(['getAsyncCount', 'getAsyncCountN']),
  },
}
</script>
vuex-getters:

除了state之外,有时还需要从state中派生出一些状态这些状态是依赖state的,会用到getters

state中定义了list,是1~10的数组

 state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  },

组件中需要显示大于5的数据,正常的方式,是需要list在组件中进行再一步的处理,但是getters可以帮助我们实现它

定义getters:
getters: {
    // getters函数第一个参数是state
    // 必须要有返回值
    filterList: (state) => state.list.fiter((item) => item > 5),
  },
原始方式–getters:
<ul>
      <li v-for="(item, index) in $store.getters.filterList">
        {{ item }}
      </li>
    </ul>
辅助函数–mapGetters:
<template>
  <div>
    list
    {{ count }}
    <ul>
      <li v-for="(item, index) in filterList" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['filterList']),
  
  }
}
</script>
vuex-Module:
为什么会有模块化:

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂的时候,store对象就会变得相当臃肿。

我们把所有的状态都放在state中,项目变得越来越大的时候,vuex会变得越来越难以维护------Vuex模块化

在这里插入图片描述

模块化简单应用:

  • 定义两个模块 countModule 和 arrModule
  • countModule 管理状态count
  • arrModule 管理状态 arr
const store = new Vuex.Store({
  modules: {
    countModule: {
      state: {
        count: 0,
      },
    },
    arrModule: {
      state: {
        arr: [2, 4, 67, 23, 12, 34, 68, 76],
      },
    },
  },
})
Good.vue组件中,分别显示count 和arr:
<div>count:{{ $store.state.countModule.count }}</div>
    <div>
      <ul>
        <li v-for="item in $store.state.arrModule.arr">
          {{ item }}
        </li>
      </ul>
    </div>

注意:获取子模块的状态,通过$store.state.模块名.属性名来获取

上面的获取有点麻烦,可以getters 来改变一下

//getters是根级别的getters
getters: {
    count: (state) => state.countModule.count,
    arr: (state) => state.arrModule.arr,
  },
组件中通过mapGetters获取数据
<template>
  <div>
    <div>count:{{ count }}</div>
    <ul>
      <li v-for="(item, index) in arr" :key="index">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['count', 'arr']),
  },
}
</script>
模块化中的命名空间:

namespaced 命名空间

默认情况下,模块内部的action,mutation,getter 是注册在全局命名空间的,—这样使得多个模块对同一mutation或者action作出响应

在这里插入图片描述

我们想要保证内部模块的高封闭性,可以采用namespaced来进行设置

const store = new Vuex.Store({
  modules: {
    countModule: {
      namespaced: true,  //命名空间
      state: {
        count: 0,
      },
      action: {},
      mutations: {
        addCount (state) {
          // 这里的state表示的是countModule的state
          state.count++
        },
      },
      getters: {},
    }
})
调用:

方法一: 直接调用-带上模块的属性名路径

this.$store.commit('countModule/addCount')

方法二:辅助函数–带上模块的属性名路径


    <button @click="add">+1</button>
  methods: {
    ...mapMutations(['countModule/addCount', 'arrModule/delArr']),
    add() {
      this['countModule/addCount']()
    }
  },

v-model语法糖:

父子组件通信,单项的,很多时候需要双向通信

父组件使用 msg.sync=“aa”,子组件使用 $emit(“update:msg”,参数)
父组件
 <Good :msg.sync="test"></Good>
子组件
<template>
  <div>
    {{ msg }}
    <button @click="fn">更改msg</button>
  </div>
</template>

<script>
export default {
  props: {
    msg: {
      default: '',
    },
  },
  methods: {
    fn() {
      this.$emit('update:msg', 'world')
    },
  },
}
</script>
父组件传值传对象:
父组件使用:v-model
  • 第一种

vue2.x中,v-model语法糖的简写

<List  :msg="msg" @ccEvent="fn"/>

父组件App.vue

<template>
  <div>
    <List v-model="test"></List>
  </div>
</template>

<script>
import List from './views/List.vue'
export default {
  components: {
    List,
  },
  data() {
    return {
      test: 'hello',
    }
  },
}
</script>

子组件 List.vue

<template>
  <div>
    {{ '值为' + msg }}
    <button @click="fn">更改父组件的值</button>
  </div>
</template>

<script>
export default {
  // model  有2个属性,
  // prop属性将msg作为该组件被使用时,v-model能取到的值
  // event 就是自定义事件 是emit('ccEvent')的时候  参数的值就是父组件v-model收到的值
  model: {
    prop: 'msg',
    event: 'ccEvent',
  },
  props: {
    msg: '',
  },
  methods: {
    fn() {
      this.$emit('ccEvent', this.msg + 2)
    },
  },
}
</script>
  • 第二种
<List  :value="msg" @input="fn"/>

父组件 App.vue

 <List v-model="test"></List>

子组件 List.vue

<template>
  <div>
    {{ '值为' + value }}
    <button @click="fn">更改父组件的值</button>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      //必须使用value
      default: '',
    },
  },
  methods: {
    fn() {
      // 这里必须是input发送数据,
      // 发送数据会被父级v-model="test" 接收到,
      // 再被value=test传回来
      this.$emit('input', this.value + 2)
    },
  },
}
</script>

st: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},


组件中需要显示大于5的数据,正常的方式,是需要list在组件中进行再一步的处理,但是getters可以帮助我们实现它

##### 定义getters:

getters: {
// getters函数第一个参数是state
// 必须要有返回值
filterList: (state) => state.list.fiter((item) => item > 5),
},


##### 原始方式--getters:

  • {{ item }}
```
辅助函数–mapGetters:
<template>
  <div>
    list
    {{ count }}
    <ul>
      <li v-for="(item, index) in filterList" :key="index">{{ item }}</li>
    </ul>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['filterList']),
  
  }
}
</script>
vuex-Module:
为什么会有模块化:

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂的时候,store对象就会变得相当臃肿。

我们把所有的状态都放在state中,项目变得越来越大的时候,vuex会变得越来越难以维护------Vuex模块化

[外链图片转存中…(img-UdJZ2hDM-1728799233543)]

模块化简单应用:

  • 定义两个模块 countModule 和 arrModule
  • countModule 管理状态count
  • arrModule 管理状态 arr
const store = new Vuex.Store({
  modules: {
    countModule: {
      state: {
        count: 0,
      },
    },
    arrModule: {
      state: {
        arr: [2, 4, 67, 23, 12, 34, 68, 76],
      },
    },
  },
})
Good.vue组件中,分别显示count 和arr:
<div>count:{{ $store.state.countModule.count }}</div>
    <div>
      <ul>
        <li v-for="item in $store.state.arrModule.arr">
          {{ item }}
        </li>
      </ul>
    </div>

注意:获取子模块的状态,通过$store.state.模块名.属性名来获取

上面的获取有点麻烦,可以getters 来改变一下

//getters是根级别的getters
getters: {
    count: (state) => state.countModule.count,
    arr: (state) => state.arrModule.arr,
  },
组件中通过mapGetters获取数据
<template>
  <div>
    <div>count:{{ count }}</div>
    <ul>
      <li v-for="(item, index) in arr" :key="index">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters(['count', 'arr']),
  },
}
</script>
模块化中的命名空间:

namespaced 命名空间

默认情况下,模块内部的action,mutation,getter 是注册在全局命名空间的,—这样使得多个模块对同一mutation或者action作出响应

[外链图片转存中…(img-xaBI50xX-1728799233543)]

我们想要保证内部模块的高封闭性,可以采用namespaced来进行设置

const store = new Vuex.Store({
  modules: {
    countModule: {
      namespaced: true,  //命名空间
      state: {
        count: 0,
      },
      action: {},
      mutations: {
        addCount (state) {
          // 这里的state表示的是countModule的state
          state.count++
        },
      },
      getters: {},
    }
})
调用:

方法一: 直接调用-带上模块的属性名路径

this.$store.commit('countModule/addCount')

方法二:辅助函数–带上模块的属性名路径


    <button @click="add">+1</button>
  methods: {
    ...mapMutations(['countModule/addCount', 'arrModule/delArr']),
    add() {
      this['countModule/addCount']()
    }
  },

v-model语法糖:

父子组件通信,单项的,很多时候需要双向通信

父组件使用 msg.sync=“aa”,子组件使用 $emit(“update:msg”,参数)
父组件
 <Good :msg.sync="test"></Good>
子组件
<template>
  <div>
    {{ msg }}
    <button @click="fn">更改msg</button>
  </div>
</template>

<script>
export default {
  props: {
    msg: {
      default: '',
    },
  },
  methods: {
    fn() {
      this.$emit('update:msg', 'world')
    },
  },
}
</script>
父组件传值传对象:
父组件使用:v-model
  • 第一种

vue2.x中,v-model语法糖的简写

<List  :msg="msg" @ccEvent="fn"/>

父组件App.vue

<template>
  <div>
    <List v-model="test"></List>
  </div>
</template>

<script>
import List from './views/List.vue'
export default {
  components: {
    List,
  },
  data() {
    return {
      test: 'hello',
    }
  },
}
</script>

子组件 List.vue

<template>
  <div>
    {{ '值为' + msg }}
    <button @click="fn">更改父组件的值</button>
  </div>
</template>

<script>
export default {
  // model  有2个属性,
  // prop属性将msg作为该组件被使用时,v-model能取到的值
  // event 就是自定义事件 是emit('ccEvent')的时候  参数的值就是父组件v-model收到的值
  model: {
    prop: 'msg',
    event: 'ccEvent',
  },
  props: {
    msg: '',
  },
  methods: {
    fn() {
      this.$emit('ccEvent', this.msg + 2)
    },
  },
}
</script>
  • 第二种
<List  :value="msg" @input="fn"/>

父组件 App.vue

 <List v-model="test"></List>

子组件 List.vue

<template>
  <div>
    {{ '值为' + value }}
    <button @click="fn">更改父组件的值</button>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      //必须使用value
      default: '',
    },
  },
  methods: {
    fn() {
      // 这里必须是input发送数据,
      // 发送数据会被父级v-model="test" 接收到,
      // 再被value=test传回来
      this.$emit('input', this.value + 2)
    },
  },
}
</script>

标签:Vue,default,笔记,学习,state,vue,组件,import,路由
From: https://blog.csdn.net/Python_Ghost/article/details/142897831

相关文章

  • 基于SpringBoot+Vue的扶贫助农有机农产品商城【源码+安装+讲解+售后+开题+答辩PPT】
    【1】系统介绍①背景在快速发展的中国,农村经济虽受益于国家整体繁荣,但仍面临基础设施落后、信息闭塞及销售渠道狭窄等挑战,阻碍了优质农产品的市场拓展,影响农民收入。互联网与电商的兴起,为解决这一难题提供了新思路。它们能跨越地域界限,直接链接农户与消费者,简化销售流程,......
  • vue3 路由文件配置
    //通过模板路由配置import{createRouter,createWebHistory}from"vue-router";//创建路由器constroutes=[//登录路由{path:"/login",component:()=>import("../views/login/index.vue"),name:"......
  • [Python学习日记-46] Python 中第三方开源模块的安装、使用与上传自己写的模块
    [Python学习日记-46]Python中第三方开源模块的安装、使用与上传自己写的模块简介下载与安装如何使用安装好的第三方开源模块如何上传自己写的模块到PyPi简介    在前面的模块介绍和导入当中主要介绍的都是Python内置的一些模块,我们把它称为标准库,而这个库......
  • 硬件开发笔记(三十一):TPS54331电源设计(四):PCB布板12V转5V电路、12V转3.0V和12V转4V电路
    若该文为原创文章,转载请注明原文出处本文章博客地址:https://hpzwl.blog.csdn.net/article/details/142757509长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…硬件相关开发......
  • 2024-2025-1 20241322 《计算机基础与程序设计》第3周学习总结
    2024-2025-120241322《计算机基础与程序设计》第3周学习总结作业信息这个作业属于哪个课程<班级的链接>(如2024-2025-1-计算机基础与程序设计)这个作业要求在哪里<作业要求的链接>(如2024-2025-1计算机基础与程序设计第一周作业)这个作业的目标<数字分类与计数法......
  • Living-Dream 系列笔记 第81期
    树上差分概述:擅长在树上一条路径上统计边或者点的信息。下文令差分数组为\(d_i\),\(lca\)为路径两端点的LCA,\(fa_i\)为\(i\)的父亲。按边的差分将\(a\)到\(b\)的路径上的边权加\(val\):\[d_a+val\tod_a\\d_b+val\tod_b\\d_{lca}-2\timesval\tod_{lc......
  • 基于Springboot+Vue的学生毕业离校系统的设计与实现(源码+LW+包运行)
    限时福利:联系即免费送开题报告和任务书,欢迎咨询(可指定任意题目)源码获取:https://download.csdn.net/download/u011832806/89432081基于SpringBoot+Vue的学生毕业离校系统开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven系统演示......
  • vue跨标签页通信(或跨窗口)详细教程
    在Vue应用中,跨标签页(或跨窗口)的通信通常涉及到两个或多个浏览器标签页之间的信息共享。由于每个标签页或窗口都是独立的JavaScript执行环境,它们不能直接通过Vue或其他JavaScript库来直接相互通信。但是,有一些方法可以实现这种跨标签页的通信,主要依靠浏览器提供的WebA......
  • 008. vue组件的嵌套
    页面层级#1.main.ts引入App.文件import{createApp}from'vue'import'./style.css'importAppfrom'./简答组件的使用/App.vue'createApp(App).mount('#app')#2.定义Footer.vue<scriptsetuplang="ts"></......
  • 基于Node.js+vue个人理财系统(开题+程序+论文) 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景在当今复杂多变的经济环境中,个人理财已成为人们日常生活中不可或缺的一部分。随着金融市场的快速发展,各类投资产品层出不穷,如国债、股票、基金等,为投资者提......