Bootstrap

学习笔记--Vue

01-Vue基本概念

一、为何学Vue

目标

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

例子

把数组数据-循环铺设到li中, 看看分别如何做的?

1、原生js做法

<ul id="myUl"></ul>
<script>
    let arr = ["春天", "夏天", "秋天", "冬天"];
    let myUl = document.getElementById("myUl");
    for (let i = 0; i < arr.length; i++) {
        let theLi = document.createElement("li");
        theLi.innerHTML = arr[i];
        myUl.appendChild(theLi);
    }
</script>

2、Vue.js做法

<li v-for="item in arr">{{item}}</li>
<script>
    new Vue({
        // ...
        data: {
            arr: ["春天", "夏天", "秋天", "冬天"] 
        }
    })
</script>

(vue的底层还是原生js)

开发更加的效率和简洁, 易于维护, 快!快!快!就是块 (甚至测试, Java, Python工程师都要学点vue, 方便与前端沟通)

现在很多项目都是用vue开发的

市场上90%工作都要求会vue, 会vue拿高薪, 甚至java或测试都要学点vue

小结

为什么学 Vue
1、开发更快,更高效
2、企业开发都在使用
3、前端工程师必备技能,高薪

二、Vue是什么

logo镇楼

渐进式javacript框架, 一套拥有自己规则的语法

官网地址: Vue.js - 渐进式 JavaScript 框架 | Vue.js (作者: 尤雨溪)

尤雨溪:大学专业并非是计算机专业,在大学期间他学习专业是室内艺术和艺术史,后来读了美术设计和技术的硕士,正是在读硕士期间,他偶然接触到了JavaScript ,从此被这门编程语言深深吸引,开启了自己的前端生涯。

2014 年 2 月,Vue.js 正式发布

2015 年 10 月 27 日,正式版发布 1.0.0

2016 年 4 月 27 日,发布 2.0 的预览版本

1、什么是渐进式

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

逐渐使用,逐渐进步

简单来说:一辆车(轮子+发动机)-> 方向盘 -> 车灯 -> 翅膀 = 能飞的汽车

Vue渐进式: Vue从基础开始, 会循序渐进向前学习, 如下知识点可能你现在不明白, 但是学完整个vue回过头来看, 会很有帮助

声明式渲染 -> 组件系统 -> 客户端路由 -> 大规模状态管理 -> 构建工具

2、什么是库和框架

补充概念:

库:  封装的属性或方法 (例jquery.js)

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

  • 茅草屋 -> 随意加盖
  • 大厦的脚手架 -> 遵守规则

三、Vue学习的方式

  • 传统开发模式:基于html/css/js文件开发vue

  • 工程化开发方式:在webpack环境中开发vue,这是最推荐, 企业常用的方式

1、Vue如何学

  1. 每天的知识点自测最好做到了如指掌 - 做不到只能花30分钟去记住结论和公式
  2. 记住vue指令作用, 基础语法 - 弄一个字典(一一映射关系)

第一列:某个知识点

第二列:语法示列

第三列:当前知识点的详细介绍

第四列:当前知识点的应用场景

  1. 在课上例子, 练习, 案例, 作业, 项目中, 反复磨炼使用

例子、项目,没有标准答案,以完成功能为导向。

实现功能,不是最终目的。同样一个功能,自己思考,还有没有其他的写法,其他的实现思路。

  1. 学会查找问题的方式和解决方式(弄个报错总结.md, 避免反复进坑)

遇到报错,先翻译。翻译完了再根据报错内容,查找bug

2、总结

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

在哪里书写 Vue 代码呢?

在工程化环境下用 Vue 开发项目。

02-Vue基础-@vue/cli脚手架

一、@vue/cli 脚手架介绍

1、思考

1、新的开发模式是不是离不开 webpack?

2、自己配置 webpack 是不是非常痛苦?

3、有没有一套搭建好的,拿来即用的环境呢?

2、目标

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

3、介绍

  • @vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目
    脚手架是为了保证各施工过程顺利进行而搭设的工作平台

4、@vue/cli的好处

开箱即用
0配置webpack
babel支持
css, less支持
开发服务器支持

5、小结

需要自己配置 webpack 吗?

Vue 官方提供了@vue/cli 包 - 可以创建脚手架项目

脚手架的好处

0 配置开箱即用,基于它开发我们的项目

二、@vue/cli安装

1、思考

有了 vue 命令,如何创建一套脚手架项目?

2、目标

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

3、步骤

1、全局安装命令

yarn global add @vue/cli
# OR
npm install -g @vue/cli

注意: 如果半天没动静(95%都是网速问题), 可以ctrl+c:停止重新来、换一个网继续重来

2、查看vue脚手架版本

vue -V

4、总结

1、如何安装全局包?

yarn global add 包名 / npm install 包名 -g

2、我们得到什么?

全局包会在计算机中配置全局命令(例:vue 命令)

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

1、思考

有了 vue 命令,如何创建一套脚手架项目?

2、目标

使用vue命令, 创建脚手架项目

注意: 项目名不能带大写字母, 中文和特殊符号

3、步骤

  1. 创建项目
# vue和create是命令, vuecli-demo是文件夹名
vue create vuecli-demo
  1. 选择模板
    可以上下箭头选择, 弄错了ctrl+c重来

选择用什么方式下载脚手架项目需要的依赖包

  1. 回车等待生成项目文件夹+文件+下载必须的第三方包们

  1. 进入脚手架项目下, 启动内置的热更新本地服务器
cd vuecil-demo

npm run serve
# 或
yarn serve

只要看到绿色的 - 啊. 你成功了(底层node+webpack热更新服务)

打开浏览器输入上述地址

3、总结

vue命令创建工程目录, 项目内置webpack本地热更新服务器, 帮我们打包项目预览项目

1、如何创建一个开箱即用的脚手架项目

vue create 项目名

2、如何在网页浏览这个项目

yarn serve 启动本地热更新开发服务器

四、@vue/cli 目录和代码分析

1、目标

讲解重点文件夹, 文件的作用, 以及文件里代码的意思

 vuecil-demo        # 项目目录
    ├── node_modules # 项目依赖的第三方包
    ├── public       # 静态文件目录
      ├── favicon.ico# 浏览器小图标
      └── index.html # 单页面的html文件(网页浏览的是它)
    ├── src          # 业务文件夹
      ├── assets     # 静态资源
        └── logo.png # vue的logo图片
      ├── components # 组件目录
        └── HelloWorld.vue # 欢迎页面vue代码文件 
      ├── App.vue    # 整个应用的根组件
      └── main.js    # 入口js文件
    ├── .gitignore   # git提交忽略配置
    ├── babel.config.js  # babel配置
    ├── package.json  # 依赖包列表
    ├── README.md    # 项目说明
	└── yarn.lock    # 项目包版本锁定和缓存地址

2、小结

主要文件及含义

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

五、@vue/cli 项目架构了解

1、思考

项目入口和个文件关系是什么呢?

2、目标

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

3、小结

main.js、App.vue 和 index.html 作用和关系?

1、main.js - 项目打包入口 - Vue 初始化

2、App.vue - Vue 页面主入口

3、index.html - 浏览器运行的文件

4、App.vue => main.js => index.html

六、@vue/cli 自定义配置

1、目标

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

src并列处新建vue.config.js

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

2、小结

脚手架项目配置文件

src 文件夹并列处准备 vue.config.js 配置文件

七、eslint了解

1、目标

知道eslint的作用, 和如何暂时关闭, 它是一个代码检查工具

2、例子

先在main.js 随便声明个变量, 但是不要使用

观察发现, 终端和页面都报错了

记住以后见到这样子的错误, 证明你的代码不严谨

方式1: 手动解决掉错误, 以后项目中会讲如何自动解决

方式2: 暂时关闭eslint检查(因为现在主要精力在学习Vue语法上), 在vue.config.js中配置后重启服务

3、小结

1、eslint 是什么?

eslint 是代码检查工具,违反规定就报错

2、如何关闭?

在 vue.config.js 中设置 lintOnSave 为 false 重启服务器即可

八、@vue/cli 单vue文件讲解

1、目标

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

2、文件详解

Vue推荐采用.vue文件来开发项目

template里只能有一个根标签

vue文件-独立模块-作用域互不影响

style配合scoped属性, 保证样式只针对当前template内标签生效

vue文件配合webpack, 把他们打包起来插入到index.html

<!-- template必须, 只能有一个根标签, 影响渲染到页面的标签结构 -->
<template>
  <div>欢迎使用vue</div>
</template>

<!-- js相关 -->
<script>
export default {
  name: 'App'
}
</script>

<!-- 当前组件的样式, 设置scoped, 可以保证样式只对当前页面有效 -->
<style scoped>
</style>

最终: Vue文件配合webpack, 把他们打包起来插入到index.html, 然后在浏览器运行

3、小结

单 vue 文件好处

独立作用域,不再担心变量问题。

单文件使用注意事项

要注意 template 里只能有一个根标签

九、@vue/cli 欢迎界面清理

1、目标

我们开始写我们自己的代码, 无需欢迎页面

src/App.vue默认有很多内容, 可以全部删除留下框

assets 和 components 文件夹下的一切都删除掉 (不要默认的欢迎页面)

 04-Vue基础-vue指令  

一、vue基础-插值表达式

1、思考

1、以前改变标签内容,如何做?
2、用 Vue 有没有更简单的方式?

2、目的

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

3、语法

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

语法: {{ 表达式 }}

<template>
  <div>
    <h1>{{ msg }}</h1>
    <h2>{{ obj.name }}</h2>
    <h3>{{ obj.age > 18 ? '成年' : '未成年' }}</h3>
  </div>
</template>

<script>
export default {
  data() { // 格式固定, 定义vue数据之处
    return {  // key相当于变量名
      msg: "hello, vue",
      obj: {
        name: "小vue",
        age: 5
      }
    }
  }
}
</script>

<style>
</style>

4、总结

什么是插值表达式

双大括号,可以把 vue 数据变量显示在标签内

Vue 中变量声明在哪里

data 函数返回的对象上,用 key 属性声明

二、vue基础-MVVM设计模式

1、思考

在上个代码基础上,在 devtool 工具改变 M 层的变量,观察 V 层(视图的自动同步)

为什么数据改变页面自动更新了?

2、目的

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

3、设计模式

是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。

  • MVVM,一种软件架构模式,决定了写代码的思想和层次
    • M:   model数据模型          (data里定义)
    • V:    view视图                   (html页面)
    • VM: ViewModel视图模型  (vue.js源码)

  • MVVM通过数据双向绑定让数据自动地双向同步  不再需要操作DOM
    • V(修改视图) -> M(数据自动同步)
    • M(修改数据) -> V(视图自动同步)

1. 在vue中,不推荐直接手动操作DOM!!!

2. 在vue中,通过数据驱动视图,不要在想着怎么操作DOM,而是想着如何操作数据!!(思想转变)

4、总结

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

1、什么是设计模式

设计模式适度代码分层,引入一种架构的概念

2、MVVM 是什么?

MVVM(模型、视图、视图模型双向关联的一种设计模式)

3、MVVM 的好处?

减少 DOM 操作,提高开发效率

三、vue指令-v-bind

1、思考

以前给 a 标签设置 href 如何做呢?

2、目标

给标签属性设置vue变量的值

3、语法

插值表达式不能用在html的属性上,如果想要动态的设置html元素的属性,需要使用v-bind指令

vue指令, 实质上就是特殊的 html 标签属性, 特点:  v- 开头

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

  • 语法:v-bind:属性名="vue变量"
  • 简写::属性名="vue变量"
<!-- vue指令-v-bind属性动态赋值 -->
<a v-bind:href="url">我是a标签</a>
<img :src="imgSrc">

4、总结

把vue变量的值, 赋予给dom属性上, 影响标签显示效果

如何给 dom 标签的属性,设置 Vue 数据变量?

:属性名="vue 数据变量"

四、vue指令-v-on

1、思考

以前如何给按钮绑定点击事件呢?

2、目标

给标签绑定事件

3、语法

    • v-on:事件名="要执行的少量代码"
    • v-on:事件名="methods中的函数"
    • v-on:事件名="methods中的函数(实参)"
  • 简写: @事件名="methods中的函数"

<!-- vue指令:   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="addCountFn(5)">一次加5件</button>

<button @click="subFn">减少</button>

<script>
    export default {
        // ...其他省略
        methods: {
            addFn(){ // this代表export default后面的组件对象(下属有data里return出来的属性)
                this.count++
            },
            addCountFn(num){
                this.count += num
            },
            subFn(){
                this.count--
            }
        }
    }
</script>

4、小结

1、如何给 dom 标签绑定事件?

@事件名=“methods 里的函数名”

2、如何给事件传值呢?

@事件名=“methods 里的函数名(实参)”

五、vue指令-v-on事件对象

1、思考

1、js 原生如何阻止标签的默认行为?
2、vue 事件处理函数如何获取事件对象呢?

2、目标

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

3、语法

无传参, 通过形参直接接收

传参, 通过$event指代事件对象传给事件处理函数

<template>
  <div>
    <a @click="one" href="http://www.baidu.com">阻止百度</a>
    <hr>
    <a @click="two(10, $event)" href="http://www.baidu.com">阻止去百度</a>
  </div>
</template>

<script>
export default {
  methods: {
    one(e){
      e.preventDefault()
    },
    two(num, e){
      e.preventDefault()
    }
  }
}
</script>

4、小结

Vue 事件处理函数,如何拿到事件对象呢?

1、无传参,直接在形参接收
2、有传参,手动传入$event 对象

六、vue指令-v-on修饰符

1、思考

1、e.preventDefault()单词很长不好写吧?
2、有没有一种更简单的方式实现呢?

2、目标

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

3、语法

@事件名.修饰符="methods里函数"

    • .stop - 阻止事件冒泡
    • .prevent - 阻止默认行为
    • .once - 程序运行期间, 只触发一次事件处理函数
<template>
  <div @click="fatherFn">
    <!-- vue对事件进行了修饰符设置, 在事件后面.修饰符名即可使用更多的功能 -->
    <button @click.stop="btn">.stop阻止事件冒泡</button>
    <a href="http://www.baidu.com" @click.prevent="btn">.prevent阻止默认行为</a>
    <button @click.once="btn">.once程序运行期间, 只触发一次事件处理函数</button>
  </div>
</template>

<script>
export default {
  methods: {
    fatherFn(){
      console.log("father被触发");
    },
    btn(){
      console.log(1);
    }
  }
}
</script>

4、总结

修饰符给事件扩展额外功能

vue 有哪些主要修饰符,都有什么功能?

1、.stop - 阻止事件冒泡

2、.prevent - 阻止默认行为

3、.once - 只执行一次事件处理函数

七、vue指令-v-on按键修饰符

1、思考

1、以前我想判断用户是否按下回车怎么做?

2、鼠标事件有修饰符,那按键有修饰符吗?

2、目标

给键盘事件, 添加修饰符, 增强能力

3、语法

更多修饰符

<template>
  <div>
    <input type="text" @keydown.enter="enterFn">
    <hr>
    <input type="text" @keydown.esc="escFn">
  </div>
</template>

<script>
export default {
 methods: {
   enterFn(){
     console.log("enter回车按键了");
   },
   escFn(){
     console.log("esc按键了");
   }
 }
}
</script>

4、总结

多使用事件修饰符, 可以提高开发效率, 少去自己判断过程

1、按键修饰符如何用?

@键盘事件.按键修饰符=“methods 里函数名”

2、有哪些主要按键修饰符?

1、.enter - 只要按下回车才能触发这个键盘事件函数
2、.esc - 只要按下取消键才能触发这个键盘事件函数

八、课上练习-翻转世界

1、目标

点击按钮 - 把文字取反显示 - 再点击取反显示(回来了)

提示把字符串取反赋予回去

效果演示:

2、思考

1、如何反转字符串?

2、视图如何实时更新?

3、分析

1、定义变量 message:‘HELLO, WORLD’

2、插值表达式赋予到dom上, 准备按钮和文字

3、按钮绑定点击事件和函数

4、对message值用split拆分, 返回数组

5、数组元素翻转用reverse方法

6、再把数组用join拼接成字符串赋予给message

7、因为Vue是MVVM设计模式, 数据驱动视图, 所以视图自动改变

正确代码:

<template>
  <div>
    <h1>{{ message }}</h1>
    <button @click="btn">逆转世界</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "HELLO, WORLD",
    };
  },
  methods: {
    btn(){
      this.message = this.message.split("").reverse().join("")
    }
  }
};
</script>

4、总结

记住方法特点, 多做需求, vue是数据变化视图自动更新, 减少操作DOM时间, 提高开发效率

点击翻转字符串你有什么收获?

1、写需求要先静态标签, 再考虑动态效果, 找好第一步干什么

2、记住方法的特点 – 可以自己总结字典和口诀

3、Vue是靠数据驱动视图, 只需要关心数据变化即可

九、vue指令 v-model

1、思考

1、js中获取表单的值, 赋予给变量?

2、js变量改变, 同步显示到表单里?

3、Vue能否把他们互相关联起来呢?

2、目标

把value属性和vue数据变量, 双向绑定到一起

3、语法

  • 语法: v-model="vue数据变量"
  • 双向数据绑定
    • 数据变化 -> 视图自动同步
    • 视图变化 -> 数据自动同步
  • 演示: 用户名绑定 - vue内部是MVVM设计模式
<template>
  <div>
    <!-- 
    	v-model:是实现vuejs变量和表单标签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="from">
        <option value="北京市">北京</option>
        <option value="南京市">南京</option>
        <option value="天津市">天津</option>
      </select>
    </div>
    <div>
      <!-- (重要)
      遇到复选框, v-model的变量值
      非数组 - 关联的是复选框的checked属性
      数组   - 关联的是复选框的value属性
       -->
      <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="radio" value="男" name="sex" v-model="gender">男
      <input type="radio" value="女" name="sex" v-model="gender">女
    </div>
    <div>
      <span>自我介绍</span>
      <textarea v-model="intro"></textarea>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: "",
      pass: "",
      from: "",
      hobby: [], 
      gender: "",
      intro: "",
    };
    // 总结:
    // 特别注意: v-model, 在input[checkbox]的多选框状态
    // 变量为非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用
    // 变量为数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值
  }
};
</script>

4、总结

本阶段v-model只能用在表单元素上, 以后学组件后讲v-model高级用法

1、v-model用在哪里?

暂时只能用在表单标签上

2、v-model有什么作用?

把vue的数据变量和表单的value属性双向绑定在一起

5、扩展

实现原理:

代码:

<input type="text" v-on:input="msg = $event.target.value" v-bind:value="msg" />


export default {
  data() {
    return {
      msg: '',
    };
  },
};

十、vue指令 v-model绑定不同表单

1、思考

1、下拉菜单, 复选框, 单选框如何绑定Vue变量呢?

2、小结

1、下拉菜单v-model写在哪里?

在select, value在option上

2、v-model用在复选框时, 需要注意什么?

v-model的vue变量是

非数组 – 关联的是checked属性

数组 – 关联的是value属性

3、vue变量初始值会不会影响表单的默认状态?

会影响, 因为双向数据绑定-互相影响

十一、vue指令 v-model修饰符

1、思考

1、表单同步到变量里的 数 是什么类型的呢?

2、能否让v-model转成数值再赋予给变量呢?

2、目标

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

3、语法

v-model.修饰符="vue数据变量"

    • .number   以parseFloat转成数字类型
    • .trim          去除首尾空白字符
    • .lazy           在change时触发而非inupt时

<template>
  <div>
    <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="intro"></textarea>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      age: "",
      motto: "",
      intro: ""
    }
  }
}
</script>

3、总结

v-model修饰符, 可以对值进行预处理, 非常高效好用

v-model有哪些修饰符, 提高我们编程效率?

1、.number – 转成数值类型赋予给Vue数据变量

2、.trim – 去除左右两边空格后把值赋予给Vue数据变量

3、.lazy – 等表单失去焦点, 才把值赋予给Vue数据变量

十二、vue指令 v-text和v-html

1、目的

更新DOM对象的innerText/innerHTML

2、语法

    • v-text="vue数据变量"
    • v-html="vue数据变量"
    • 注意: 会覆盖插值表达式

<template>
  <div>
    <p v-text="str"></p>
    <p v-html="str"></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      str: "<span>我是一个span标签</span>"
    }
  }
}
</script>

3、总结

v-text把值当成普通字符串显示, v-html把值当做html解析

1、v-text和v-html有什么作用?

都可以设置标签显示的内容

2、区别是什么?

v-text把值当成普通字符串显示
v-html把值当成标签进行解析显示

十三、vue指令 v-show和v-if

1、目标

控制标签的隐藏或出现

2、语法

    • v-show="vue变量"
    • v-if="vue变量"

3、原理

    • v-show 用的display:none隐藏   (频繁切换使用)
    • v-if  直接从DOM树上移除

4、高级

    • v-else使用

<template>
  <div>
    <h1 v-show="isOk">v-show的盒子</h1>
    <h1 v-if="isOk">v-if的盒子</h1>

    <div>
      <p v-if="age > 18">我成年了</p>
      <p v-else>还得多吃饭</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isOk: true,
      age: 15
    }
  }
}
</script>

5、总结

使用v-show和v-if以及v-else指令, 方便通过变量控制一套标签出现/隐藏

案例-折叠面板

1、目标

点击展开或收起时,把内容区域显示或者隐藏

此案例使用了less语法, 项目中下载模块

yarn add [email protected] [email protected] -D

只有标签和样式

<template>
  <div id="app">
    <h3>案例:折叠面板</h3>
    <div>
      <div class="title">
        <h4>芙蓉楼送辛渐</h4>
        <span class="btn" >
          收起
        </span>
      </div>
      <div class="container">
        <p>寒雨连江夜入吴,</p>
        <p>平明送客楚山孤。</p>
        <p>洛阳亲友如相问,</p>
        <p>一片冰心在玉壶。</p>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      
    }
  }
}
</script>

<style lang="less">
body {
  background-color: #ccc;
  #app {
    width: 400px;
    margin: 20px auto;
    background-color: #fff;
    border: 4px solid blueviolet;
    border-radius: 1em;
    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
    padding: 1em 2em 2em;
    h3 {
      text-align: center;
    }
    .title {
      display: flex;
      justify-content: space-between;
      align-items: center;
      border: 1px solid #ccc;
      padding: 0 1em;
    }
    .title h4 {
      line-height: 2;
      margin: 0;
    }
    .container {
      border: 1px solid #ccc;
      padding: 0 1em;
    }
    .btn {
      /* 鼠标改成手的形状 */
      cursor: pointer;
    }
  }
}
</style>

正确答案:

<template>
  <div id="app">
    <h3>案例:折叠面板</h3>
    <div>
      <div class="title">
        <h4>芙蓉楼送辛渐</h4>
        <span class="btn" @click="isShow = !isShow">
          {{ isShow ? '收起' : '展开' }}
        </span>
      </div>
      <div class="container" v-show="isShow">
        <p>寒雨连江夜入吴, </p>
        <p>平明送客楚山孤。</p>
        <p>洛阳亲友如相问,</p>
        <p>一片冰心在玉壶。</p>
      </div>
    </div>
  </div>
</template>

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

十四、vue指令-v-for

1、目标

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

2、语法

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

3、目标结构

    • 可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)

注意
v-for的临时变量名不能用到v-for范围外

<template>
  <div id="app">
    <div id="app">
      <!-- v-for 把一组数据, 渲染成一组DOM -->
      <!-- 口诀: 让谁循环生成, v-for就写谁身上 -->
      <p>学生姓名</p>
      <ul>
        <li v-for="(item, index) in arr" :key="item">
          {{ index }} - {{ item }}
        </li>
      </ul>

      <p>学生详细信息</p>
      <ul>
        <li v-for="obj in stuArr" :key="obj.id">
          <span>{{ obj.name }}</span>
          <span>{{ obj.sex }}</span>
          <span>{{ obj.hobby }}</span>
        </li>
      </ul>

      <!-- v-for遍历对象(了解) -->
      <p>老师信息</p>
      <div v-for="(value, key) in tObj" :key="value">
        {{ key }} -- {{ value }}
      </div>

      <!-- v-for遍历整数(了解) - 从1开始 -->
      <p>序号</p>
      <div v-for="i in count" :key="i">{{ i }}</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: ["小明", "小欢欢", "大黄"],
      stuArr: [
        {
          id: 1001,
          name: "孙悟空",
          sex: "男",
          hobby: "吃桃子",
        },
        {
          id: 1002,
          name: "猪八戒",
          sex: "男",
          hobby: "背媳妇",
        },
      ],
      tObj: {
        name: "小黑",
        age: 18,
        class: "1期",
      },
      count: 10,
    };
  },
};
</script>

4、总结

vue最常用指令, 铺设页面利器, 快速把数据赋予到相同的dom结构上循环生成

十五、v-for-更新监测

1、目标

当v-for遍历的目标结构改变, Vue触发v-for的更新

情况1: 数组翻转

情况2: 数组截取

情况3: 更新值

2、口诀

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

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

<template>
  <div>
    <ul>
      <li v-for="(val, index) in arr" :key="index">
        {{ val }}
      </li>
    </ul>
    <button @click="revBtn">数组翻转</button>
    <button @click="sliceBtn">截取前3个</button>
    <button @click="updateBtn">更新第一个元素值</button>
  </div>
</template>

<script>
export default {
  data(){
    return {
      arr: [5, 3, 9, 2, 1]
    }
  },
  methods: {
    revBtn(){
      // 1. 数组翻转可以让v-for更新
      this.arr.reverse()
    },
    sliceBtn(){
      // 2. 数组slice方法不会造成v-for更新
      // slice不会改变原始数组
      // this.arr.slice(0, 3)

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

      // 解决-this.$set()
      // 参数1: 更新目标结构
      // 参数2: 更新位置
      // 参数3: 更新值
      this.$set(this.arr, 0, 1000)
    }
  }
}
</script>

<style>

</style>

这些方法会触发数组改变, v-for会监测到并更新页面

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

这些方法不会触发v-for更新

  • slice()
  • filter()
  • concat()

注意: vue不能监测到数组里赋值的动作而更新, 如果需要请使用Vue.set() 或者this.$set(), 或者覆盖整个数组

3、总结

改变原数组的方法才能让v-for更新

十六、v-for更新性能为何高

v-for 的默认行为会尝试原地修改元素而不是移动它们。

详解v-for就地更新流程(可以看ppt动画)

这种 虚拟DOM对比方式, 可以提高性能 - 但是还不够高

十七、虚拟dom

1、目标

了解虚拟DOM的概念

.vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 才会渲染显示到真实DOM页面上

2、介绍

  1. 内存中生成一样的虚拟DOM结构(本质是个JS对象)
    因为真实的DOM属性好几百个, 没办法快速的知道哪个属性改变了
    比如template里标签结构
<template>
    <div id="box">
        <p class="my_p">123</p>
    </div>
</template>


对应的虚拟DOM结构

  1. 以后vue数据更新
    • 生成新的虚拟DOM结构
    • 和旧的虚拟DOM结构对比
    • 利用diff算法, 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁

好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)

好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)

3、总结

虚拟DOM保存在内存中, 只记录dom关键信息, 配合diff算法提高DOM更新的性能

在内存中比较差异, 然后给真实DOM打补丁更新上

十八、diff算法

vue用diff算法, 新虚拟dom, 和旧的虚拟dom比较

情况1: 根元素变了, 删除重建

旧虚拟DOM

<div id="box">
    <p class="my_p">123</p>
</div>

新虚拟DOM

<ul id="box">
    <li class="my_p">123</li>
</ul>

情况2: 根元素没变, 属性改变, 元素复用, 更新属性

旧虚拟DOM

<div id="box">
    <p class="my_p">123</p>
</div>

新虚拟DOM

<div id="myBox" title="标题">
    <p class="my_p">123</p>
</div>

十九、key的作用

情况3: 根元素没变, 子元素没变, 元素内容改变

无key - 就地更新

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

<ul id="myUL">
    <li v-for="str in arr">
        {{ str }} 
        <input type="text">
    </li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>


export default {
    data(){
        return {
            arr: ["老大", "新来的", "老二", "老三"]
        }
    },
    methods: {
        addFn(){
            this.arr.splice(1, 0, '新来的')
        }
    }
};

旧 - 虚拟DOM结构 和 新 - 虚拟DOM结构 对比过程

性能不高, 从第二个li往后都更新了

有key - 值为索引

  • 还是就地更新

因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的

<ul id="myUL">
    <li v-for="(str, index) in arr" :key="index">
        {{ str }} 
        <input type="text">
    </li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
    data(){
        return {
            arr: ["老大", "新来的", "老二", "老三"]
        }
    },
    methods: {
        addFn(){
            this.arr.splice(1, 0, '新来的')
        }
    }
};

key为索引-图解过程 (又就地往后更新了)

  1. v-for先循环产生新的DOM结构, key是连续的, 和数据对应
  2. 然后比较新旧DOM结构, 找到区别, 打补丁到页面上
    最后补一个li, 然后从第二个往后, 都要更新内容
  3. 口诀: key的值有id用id, 没id用索引

有key - 值为id

key的值只能是唯一不重复的, 字符串或数值

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签

新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建

旧DOM结构的key, 在新的DOM结构里没有了, 则移除key所在的标签

<template>
  <div>
    <ul>
      <li v-for="obj in arr" :key="obj.id">
        {{ obj.name }}
        <input type="text">
      </li>
    </ul>
    <button @click="btn">下标1位置插入新来的</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        {
          name: '老大',
          id: 50
        },
        {
          name: '老二',
          id: 31
        },
        {
          name: '老三',
          id: 10
        }
      ],
    };
  },
  methods: {
    btn(){
      this.arr.splice(1, 0, {
        id: 19, 
        name: '新来的'
      })
    }
  }
};
</script>

<style>
</style>

总结: 不用key也不影响功能(就地更新), 添加key可以提高更新的性能

06-Vue基础-动态class

一、思考

1、JS 判断标签是否应该使用某个类名?

2、vue 能不能直接在标签里判断?

二、目标

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

三、语法

:class="{类名: 布尔值}"

<template>
  <div>
    <ul>
      <li
        v-for="(item, index) in arr"
        :class="{ active: str === index }"
        :key="item.id"
        @click="Fn(index)"
      >
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        { id: 1, name: "张三" },
        { id: 2, name: "李四" },
        { id: 3, name: "王五" },
      ],
      str: null,
    };
  },
  methods: {
    Fn(num) {
      this.str = num;
    },
  },
};
</script>

<style scoped>
.active {
  color: red;
  font-size: 40px;
}
</style>

四、小结

如何给标签 class 属性动态赋值?

:class=“{类名: 布尔值}”, true 使用, false 不用

五、扩展

1、多个动态 class,如何处理?

class=“{类名 1:布尔值 1,class2:布尔值 2}”

多个 class,用“,”逗号隔开

<p :class="{active:true, activeS: true }">动态class</p>

2、固定 class 如何书写?

<p class="active" :class="{ activeS: true }">动态class</p>

class 与 :class 可以并存

3、class 的数组语法

<div :class="[activeClass, errorClass]"></div>
data() {
return{
  activeClass: 'active',
  errorClass: 'text-danger'
}
}

呈现出来的效果:

<div class="active text-danger"></div>

4、三元运算

<p :class="bool ? 'hei' : 'ha'">好好学习</p>
data() {
    return {
      bool: false,
    };
  },
};

07-Vue基础-动态style

一、语法

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

注意:

1.如果css样式是需要短横线的,需要将短横线删除使用小驼峰命名法

<div :style="{backgroundColor: 'pink' }">
      好好学习天天向上
    </div>

2.css属性值需要加引号可以直接引用数值,可以不用设置变量

<div :style="{ fontSize: '50px' }">
      好好学习天天向上
    </div>

动态style也有动态style对象语法,动态style数组语法,动态style三元运算语法

<template>
  <div>
    <!-- 动态style -->
    <div :style="{ color: red, fontSize: '50px', backgroundColor: 'pink' }">
      好好学习天天向上
    </div>

    <!-- 动态style对象语法 -->
    <div :style="styleObj">吃饭了吗</div>

    <!-- 动态style的数组语法 -->
    <div :style="[baseStyle, overStyle]">一会去吃饭</div>

    <!-- 动态style三元运算写法 -->
    <div :style="{ backgroundColor: heihei ? 'red' : 'green' }">
      你好吗,三元运算
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      red: "red",

      num: 50,
      styleObj: {
        color: "red",
        fontSize: "70px",
      },
      baseStyle: {
        color: "red",
        fontSize: "20px",
      },
      overStyle: {
        width: "100px",
        height: "200px",
      },
      heihei: false,
    };
  },
};
</script>

<style></style>

08-案例-品牌管理

一、品牌管理(铺设)

目标

数据铺设

需求

  • 需求1: 把默认数据显示到表格上
  • 需求2: 注意资产超过100的, 都用红色字体标记出来

细节

① 先铺设静态页面 --- 去.md文档里, 复制数据和标签模板

② 此案例使用bootstrap, 需要下载, 并导入到工程main.js中

③ 用v-for配合默认数据, 把数据默认铺设到表格上显示

④ 直接在标签上, 大于100价格, 动态设置red类名

图示

步骤

  1. 因为案例使用了bootstrap, 工程化开发, 模块化用npm/yarn下载引入使用
yarn add bootstrap

2.在main.js - 引入bootstrap

import "bootstrap/dist/css/bootstrap.css" // 默认找文件夹下的index文件(但是这个不是所以需要写路径)

3正确代码

<template>
  <div id="app">
    <div class="container">
      <!-- 顶部框模块 -->
      <div class="form-group">
        <div class="input-group">
          <h4>品牌管理</h4>
        </div>
      </div>

      <!-- 数据表格 -->
      <table class="table table-bordered table-hover mt-2">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>价格</th>
            <th>创建时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(item, index) in list" :key="index">
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>

            <!-- 如果价格超过100,就有red这个类 -->
            <td :class="{ red: item.price > 100 }">{{ item.price }}</td>
            <td>{{ item.time }}</td>
            <td><a href="#">删除</a></td>
          </tr>
        </tbody>
        <!-- 
        <tfoot >
          <tr>
            <td colspan="5" style="text-align: center">暂无数据</td>
          </tr>
        </tfoot>
            -->
      </table>

      <!-- 添加资产 -->
      <form class="form-inline">
        <div class="form-group">
          <div class="input-group">
            <input type="text" class="form-control" placeholder="资产名称" />
          </div>
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <div class="form-group">
          <div class="input-group">
            <input type="text" class="form-control" placeholder="价格" />
          </div>
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <!-- 阻止表单提交 -->
        <button class="btn btn-primary">添加资产</button>
      </form>
    </div>
  </div>
</template>

<script>
import "../node_modules/bootstrap/dist/css/bootstrap.css";
export default {
  data() {
    return {
      name: "", // 名称
      price: 0, // 价格
      list: [
        { id: 100, name: "外套", price: 199, time: new Date("2010-08-12") },
        { id: 101, name: "裤子", price: 34, time: new Date("2013-09-01") },
        { id: 102, name: "鞋", price: 25.4, time: new Date("2018-11-22") },
        { id: 103, name: "头发", price: 19900, time: new Date("2020-12-12") },
      ],
    };
  },
};
</script>

<style>
.red {
  color: red;
}
</style>

小结

1、布局用 bootstrap, yarn 下载引入使用

2、v-for 把数据渲染到表格里

3、:class 动态判断价格给红色类名

二、品牌管理(添加资产)

目标

数据新增

需求

  • 需求1: 实现表单数据新增进表格功能
  • 需求2: 判断用户输入是否为空给提示

分析

① 添加资产按钮 – 绑定点击事件
② 给表单v-model绑定vue变量收集用户输入内容
③ 添加数据到数组中
④ 判断用户内容是否符合规定

图示

在上个案例代码基础上接着写

<template>
  <div id="app">
    <div class="container">
      <!-- 顶部框模块 -->
      <div class="form-group">
        <div class="input-group">
          <h4>品牌管理</h4>
        </div>
      </div>

      <!-- 数据表格 -->
      <table class="table table-bordered table-hover mt-2">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>价格</th>
            <th>创建时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="item in list" :key="item.id">
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>

            <!-- 如果价格超过100,就有red这个类 -->
            <td :class="{ red: item.price > 100 }">{{ item.price }}</td>
            <td>{{ item.time }}</td>
            <td><a href="#">删除</a></td>
          </tr>
        </tbody>
        <!-- 
        <tfoot >
          <tr>
            <td colspan="5" style="text-align: center">暂无数据</td>
          </tr>
        </tfoot>
            -->
      </table>

      <!-- 添加资产 -->
      <form class="form-inline">
        <div class="form-group">
          <div class="input-group">
            <input
              type="text"
              v-model="name"
              class="form-control"
              placeholder="资产名称"
            />
          </div>
          <!-- 绑定获取数据 -->
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <div class="form-group">
          <div class="input-group">
            <input
              type="text"
              v-model="price"
              class="form-control"
              placeholder="价格"
            />
          </div>
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <!-- 阻止表单提交 -->
        <button class="btn btn-primary" @click.prevent="addList">
          添加资产
        </button>
      </form>
    </div>
  </div>
</template>

<script>
import "../node_modules/bootstrap/dist/css/bootstrap.css";
export default {
  data() {
    return {
      name: "", // 名称
      price: 0, // 价格
      list: [
        { id: 100, name: "外套", price: 199, time: new Date("2010-08-12") },
        { id: 101, name: "裤子", price: 34, time: new Date("2013-09-01") },
        { id: 102, name: "鞋", price: 25.4, time: new Date("2018-11-22") },
        { id: 103, name: "头发", price: 19900, time: new Date("2020-12-12") },
      ],
    };
  },
  methods: {
    // 设定如果为空值提示内容不能为空
    addList() {
      if (this.name.trim() == "" || this.price == 0) {
        alert("内容不能为空!");
        return;
      }
      // 获取数组最后一个的id加一生成新表格id
      let id = this.list[this.list.length - 1].id + 1;
      this.list.push({
        id: id,
        name: this.name,
        price: this.price,
        time: new Date(),
      });
    },
  },
};
</script>

<style>
.red {
  color: red;
}
</style>

小结

1、@绑定事件

2、v-model 收集表单数据

3、.prevent 阻止按钮提交表单刷新页面

4、v-model 修饰符

5、数组取最后一个元素

三、品牌管理(删除)

目标

数据删除

需求

  • 需求1: 点击删除的a标签, 删除数据
  • 需求2: 删除没数据了要提示暂无数据的tfoot

分析

① a标签绑定点击事件
② 给事件方法传id
③ 通过id, 找到对应数据删除
④ 删除光了要让tfoot显示
⑤ 删除光了再新增, 有bug(id值问题)需要修复

图示

在上个案例代码基础上接着写


<td><a href="#" @click="delFn(obj.id)">删除</a></td>
          
<script>
// 目标: 删除功能
// 1. 删除a标签-点击事件
// 2. 对应方法名
// 3. 数据id到事件方法中
// 4. 通过id, 找到这条数据在数组中的下标
// 5. splice方法删除原数组里的对应元素
// 6. 设置tfoot, 无数据给出提示
// 7. 无数据再新增, id要判断一下
export default {
  // ...其他代码
  methods: {
    // ...其他代码
    delFn(id){
      // 通过id找到这条数据在数组中下标
      let index = this.list.findIndex(obj => obj.id = id)
      this.list.splice(index, 1)
    }
  }
};
</script>

小结

涉及到了哪些技术点?

1、事件传 id

2、删除数组元素方法

3、id 值判断问题

四、品牌管理(时间格式化)

目标

复制上个案例, 在此基础上, 把表格里的时间用过滤器+moment模块, 格式化成YYYY-MM-DD 格式

图示

步骤

  1. 下载moment处理日期的第三方工具模块



  1. moment官网文档: 文档 | Moment.js 中文网
yarn add moment

  1. 定义过滤器, 把时间用moment模块格式化, 返回我们想要的格式

// 目标: 处理时间
// 1. 下载moment模块
import moment from 'moment'


// 2. 定义过滤器, 编写内部代码
filters: { 
    formatDate (val){
        return moment(val).format('YYYY-MM-DD')
    }
}

<!-- 3. 使用过滤器 -->
<td>{{ obj.time | formatDate }}</td>

五、品牌管理(总价和均价)

目标

基于之前的案例, 完成总价和均价的计算效果

<template>
  <div id="app">
    <div class="container">
      <!-- 顶部框模块 -->
      <div class="form-group">
        <div class="input-group">
          <h4>品牌管理</h4>
        </div>
      </div>

      <!-- 数据表格 -->
      <table class="table table-bordered table-hover mt-2">
        <thead>
          <tr>
            <th>编号</th>
            <th>资产名称</th>
            <th>价格</th>
            <th>创建时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="item in list" :key="item.id">
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>

            <!-- 如果价格超过100,就有red这个类 -->
            <td :class="{ red: item.price > 100 }">{{ item.price }}</td>
            <td>{{ item.time | time }}</td>
            <td><a href="#" @click="dellFn(item.id)">删除</a></td>
          </tr>
          <tr>
            <td>总资产{{ allPrice }}</td>
            <td>平均资产{{ junPrices }}</td>
          </tr>
        </tbody>

        <tfoot v-if="list.length === 0">
          <tr>
            <td colspan="5" style="text-align: center">暂无数据</td>
          </tr>
        </tfoot>
      </table>

      <!-- 添加资产 -->
      <form class="form-inline">
        <div class="form-group">
          <div class="input-group">
            <input
              type="text"
              v-model="name"
              class="form-control"
              placeholder="资产名称"
            />
          </div>
          <!-- 绑定数据,获取数据 -->
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <div class="form-group">
          <div class="input-group">
            <input
              type="text"
              v-model.number="price"
              class="form-control"
              placeholder="价格"
            />
          </div>
        </div>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <!--添加点击事件, 阻止表单提交 -->
        <button class="btn btn-primary" @click.prevent="addList">
          添加资产
        </button>
      </form>
    </div>
  </div>
</template>

<script>
import "../node_modules/bootstrap/dist/css/bootstrap.css";
import moment from "moment";
export default {
  data() {
    return {
      name: "", // 名称
      price: 0, // 价格
      list: [
        { id: 100, name: "外套", price: 199, time: new Date("2010-08-12") },
        { id: 101, name: "裤子", price: 34, time: new Date("2013-09-01") },
        { id: 102, name: "鞋", price: 25.4, time: new Date("2018-11-22") },
        { id: 103, name: "头发", price: 19900, time: new Date("2020-12-12") },
      ],
    };
  },
  methods: {
    // 设定如果为空值提示内容不能为空
    addList() {
      if (this.name.trim() == "" || this.price == 0) {
        alert("内容不能为空!");
        return;
      }

      // 获取数组最后一个的id加一生成新表格id
      let id = 100;
      if (this.list.length != 0) {
        id = this.list[this.list.length - 1].id + 1;
      }
      this.list.push({
        id: id,
        name: this.name,
        price: this.price,
        time: new Date(),
      });
    },
    dellFn(id) {
      // console.log(id);
      let index = this.list.findIndex((obj) => obj.id == id);
      // console.log(id);
      this.list.splice(index, 1);
    },
  },
  filters: {
    time(val) {
      return moment(val).format("YYYY-MM-DD");
    },
  },
  computed: {
    allPrice() {
      return this.list.reduce((sum, obj) => {
        return (sum += obj.price);
      }, 0);
    },
    junPrices() {
      return (this.allPrice / this.list.length).toFixed(2);
    },
  },
};
</script>

<style>
.red {
  color: red;
}
</style>

小结

总价来源于所有数据计算而来的结果, 故采用计算属性

涉及到的知识点

1、computed 定义和使用计算属性
2、数组 reduce 方法累积运算

09-Vue基础-过滤器

一、vue过滤器定义使用

思考

1、JS 原生如何封装一个字母翻转的函数?

2、生活当中的过滤器是什么? Vue 中如何实现过滤器?

目的

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

重点

1、过滤器只能用在, 插值表达式和v-bind表达式

2、Vue中的过滤器场景

  • 字母转大写, 输入"hello", 输出"HELLO"
  • 字符串翻转, "输入hello, world", 输出"dlrow ,olleh"

语法

  • Vue.filter("过滤器名", (值) => {return "返回处理后的值"})
Vue.filter('reverse', (val, s) => {
  return val.split('').reverse().join(s);
});
filters: {过滤器名字: (值) => {return "返回处理后的值"}
filters: {
  reverse(val) {
    return val.split('').reverse().join(s);
  },
},

例子

  • 全局定义“字母都大写”的过滤器
  • 局部定义“字符串翻转”的过滤器
<template>
  <div>
    <p>原来的样子: {{ msg }}</p>
    <!-- 2. 过滤器使用
      语法: {{ 值 | 过滤器名字 }}
     -->
    <p>使用翻转过滤器: {{ msg | reverse }}</p>
    <p :title="msg | toUp">鼠标长停</p>
  </div>
</template>

<script>
export default {
  data(){
    return {
      msg: 'Hello, Vue'
    }
  },
  // 方式2: 局部 - 过滤器
  // 只能在当前vue文件内使用
  /*
     语法: 
     filters: {
       过滤器名字 (val) {
         return 处理后的值
       }
     }
  */
  filters: {
    toUp (val) {
      return val.toUpperCase()
    }
  }
}
</script>

总结

把值转成另一种形式, 使用过滤器, Vue3用函数替代了过滤器.

全局注册最好在main.js中注册, 一处注册到处使用

二、传参和多过滤器

目标

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

语法

    • 过滤器传参:   vue变量 | 过滤器(实参)
    • 多个过滤器:   vue变量 | 过滤器1 | 过滤器2

<template>
  <div>
    <p>原来的样子: {{ msg }}</p>
    <!-- 1.
      给过滤器传值
      语法: vue变量 | 过滤器名(值)
     -->
    <p>使用翻转过滤器: {{ msg | reverse('|') }}</p>
    <!-- 2.
      多个过滤利使用
      语法: vue变量 | 过滤器1 | 过滤器2
     -->
    <p :title="msg | toUp | reverse('|')">鼠标长停</p>
  </div>
</template>

<script>
export default {
  data(){
    return {
      msg: 'Hello, Vue'
    }
  },
  filters: {
    toUp (val) {
      return val.toUpperCase()
    }
  }
}
</script>

总结

过滤器可以传参, 还可以对某个过滤器结果, 后面在使用一个过滤器

1、多个过滤器使用?

vue 变量 | 过滤器 1 | 过滤器 2

2、如何给过滤器传额外值

vue 变量 | 过滤器(值)

10-Vue基础-计算属性-computed

一、vue计算属性-computed

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

语法

<div>
  <p>{{ '计算属性名' }}</p>
</div>
computed: {
    "计算属性名" () {
        return "值"
    }
}

需求

<template>
  <div>
    <p>{{ num }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      a: 10,
      b: 20,
    };
  },
  computed: {
    num() {
      return this.a + this.b;
    },
  },
};
// 计算属性;一个变量的值,需要另外变量计算而得来,函数内变量会自动重新计算返回结果
</script>

注意

计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同

总结

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

1、计算属性使用场景?

当变量的值, 需要通过别人计算而得来

2、计算属性特点?

函数内使用的变量改变, 重新计算结果返回

3、计算属性注意事项?

计算属性名和 data 里名字不能重复

二、vue计算属性-缓存特性

目标

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

代码

<template>
  <div>
    <p>{{ Message }}</p>
    <p>{{ Message }}</p>
    <p>{{ Message }}</p>
    <p>{{ getMessage() }}</p>
    <p>{{ getMessage() }}</p>
    <p>{{ getMessage() }}</p>
  </div>
</template>

<script>
// 计算属性执行函数后,会把return值缓存起来,如果值不变,多次调用都是从缓存取值,函数调用一此计算一次,如果值发生变化,计算属性会重新执行再次缓存
export default {
  data() {
    return {
      msg: "Hello,Vue",
    };
  },
  computed: {
    Message() {
      console.log("计算属性执行");
      return this.msg.split("").reverse().join("");
    },
  },
  methods: {
    getMessage() {
      console.log("函数执行");
      return this.msg.split("").reverse().join("");
    },
  },
};
</script>

总结

计算属性根据依赖变量结果缓存, 依赖变化重新计算结果存入缓存, 比普通方法性能更高

1、计算属性好处是?

带缓存
依赖项不变, 直接从缓存取
依赖项改变, 函数自动执行并重新缓存

2、计算属性使用场景?

当变量值, 依赖其他变量计算而得来才用

三、vue计算属性-完整写法

目标:计算属性也是变量

语法:

computed: {
    "属性名": {
        set(值){
            
        },
        get() {
            return "值"
        }
    }
}

给关联v-model需要给计算属性变量赋值的时候需要用到计算属性完整写法

<template>
  <div>
    <div>
      <span> 姓名: </span>
      <input type="text" v-model="full" />
    </div>
  </div>
  <!-- // 计算属性也是变量
// 问题:给计算属性赋值,需要set
// 解决:完整语法:
// computed:{
  "计算属性名"(){}
  "计算属性名":{
    set(值){

    },
    get(){
      return值
    }
  }
   } -->
</template>

<script>
export default {
  computed: {
    full: {
      // 给full赋值触发set方法
      set(val) {
        console.log(val);
      },
      // 使用full出发get方法
      get() {
        return "无名氏";
      },
    },
  },
};
</script>

<style></style>

set 接收要赋予的值
get 里要返回给这个计算属性具体值

11-案例-全选和反选

一、小选影响全选

一、小选影响全选

需求

小选框都选中(手选), 全选自动选中

分析

① 先静态后动态, 从.md拿到静态标签和数据

② 循环生成复选框和文字, 对象的c属性和小选框的选中状态, 用v-model双向绑定

③ 定义isAll计算属性, 值通过小选框们统计c属性状态得来

图示

代码

<template>
  <div>
    <span>全选:</span>
    <input type="checkbox"/>
    <button>反选</button>
    <ul>
      <li>
        <input type="checkbox"/>
        <span>任务名</span>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        {
          name: "猪八戒",
          c: false,
        },
        {
          name: "孙悟空",
          c: false,
        },
        {
          name: "唐僧",
          c: false,
        },
        {
          name: "白龙马",
          c: false,
        },
      ],
    };
  }
};
</script>

正确代码,不可复制

<template>
  <div>
    <span>全选:</span>
    <!-- 4. v-model 关联全选 - 选中状态 -->
    <input type="checkbox" v-model="isAll"/>
    <button>反选</button>
    <ul>
      <li v-for="(obj, index) in arr" :key="index">
        <!-- 3. 对象.c - 关联 选中状态 -->
        <input type="checkbox" v-model="obj.c"/>
        <span>{{ obj.name }}</span>
      </li>
    </ul>
  </div>
</template>

<script>
// 目标: 小选框 -> 全选
// 1. 标签+样式+js准备好
// 2. 把数据循环展示到页面上
export default {
  data() {
    return {
      arr: [
        {
          name: "猪八戒",
          c: false,
        },
        {
          name: "孙悟空",
          c: false,
        },
        {
          name: "唐僧",
          c: false,
        },
        {
          name: "白龙马",
          c: false,
        },
      ],
    };
  },
  // 5. 计算属性-isAll
  computed: {
    isAll () {
         // 6. 统计小选框状态 ->  全选状态
        // every口诀: 查找数组里"不符合"条件, 直接原地返回false
        return this.arr.every(obj => obj.c === true)
    }
  }
};
</script>

小结

小选框如何影响全选框选中状态的?

v-model 给全选框绑定 isAll 计算属性

isAll 计算属性里返回统计小选框选中状态结果

二、全选影响小选

需求

  • 需求1: 获取到全选状态 – 改装isAll计算属性
  • 需求2: 全选状态同步给所有小选框

分析

①: isAll改成完整写法, set里获取到全选框, 勾选的状态值

②: 遍历数据数组, 赋给所有小选框v-model关联的属性

图示

代码

<script>
export default {
  // ...其他代码
  // 5. 计算属性-isAll
  computed: {
    isAll: {
      set(val){
        // 7. 全选框 - 选中状态(true/false)
        this.arr.forEach(obj => obj.c = val)
      },
      get(){
        // 6. 统计小选框状态 ->  全选状态
        // every口诀: 查找数组里"不符合"条件, 直接原地返回false
        return this.arr.every(obj => obj.c === true)
      }
    }
  }
};
</script>

小结

全选如何影响小选框选中状态的?

1、小选框 v-model, 关联数组里对象的 c 属性

2、isAll 计算属性的 set 方法, 拿到全选框状态

3、状态值赋给, 数组里每个对象的 c 属性

三、反选

需求

点击反选, 让所有小选框, 各自取相反勾选状态

分析

1、小选框的勾选状态, 在对象的 c 属性

2、遍历所有对象, 把对象的 c 属性取相反值赋予回去即可

图示

代码

<button @click="btn">反选</button>

<script>
export default {
  // ...其他代码省略
  methods: {
    btn(){
      // 8. 让数组里对象的c属性取反再赋予回去
      this.arr.forEach(obj => obj.c = !obj.c)
    }
  }
};
</script>

小结

反选如何实现的?

遍历每个对象, 把 c 属性的值取反再赋予回去

12-Vue基础-侦听器

目标

侦听到name值得改变

语法

watch:{
变量名(newVal,oldVal){

}
}

代码

<template>
  <div>
    <input type="text" v-model="name" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: "",
    };
  },
  // 目标,侦听到name值得改变
  // newVal是新值,oldVal是旧值
  // watch:{
  //     变量名(newVal,oldVal){
  //
  // }
  //

  // }
  watch: {
    name(newVal, oldVal) {
      console.log(newVal, oldVal);
    },
  },
};
</script>

小结

如何侦听到某个变量值改变呢?

使用 watch 配置项, key 是要侦听的 data/计算属性名

二、深度侦听和立即执行

可以执行深度侦听

语法

watch: {
  user: {
    handler(newVal, oldVal) {
      // user里的对象
      console.log(newVal, oldVal);
    },
    deep: true,
    immediate: true,
  },
},

代码

<template>
  <div>
    <input type="text" v-model="user.name" />
    <input type="text" v-model="user.age" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: {
        name: "",
        age: 0,
      },
    };
  },
  // 深度侦听:deep:true
  // 一打开网页立即执行:immediate:true
  watch: {
    user: {
      handler(newVal, oldVal) {
        console.log(newVal, oldVal);
      },
      deep: true,
      immediate: true,
    },
  },
};
</script>

小结

1、如何侦听一个对象/数组呢?

把侦听器写成对象形式, 给 handler 方法和 deep:true

2.一打开网页立马执行

immediate: true

13-计算属性和侦听器的区别

计算属性(computed)

属性检测(watch)

首次运行

首次不运行

调用时需要在模板中渲染,修改计算所依赖元数据

调用时只需修改元数据

默认深度依赖

默认浅度观测

1、功能:computed是计算属性;watch是监听一个值的变化执行对应的回调。
2、是否调用缓存:computed函数所依赖的属性不变的时候会调用缓存;watch每次监听的值发生变化时候都会触发。
3、是否调用return:computed必须有;watch可以没有。
4、使用场景:computed的值受多个属性影响的时候;例如购物车商品结算;watch当一条数据影响多条数据的时候,例如搜索框。

14-Vue组件

一、为什么用组件

思考

1、以前遇到重复的标签, 如何做的?

2、效率或独立性高吗, 担心变量重名?

思考

1、以前遇到重复的标签, 如何做的?

2、效率或独立性高吗, 担心变量重名?

3、代码实现

import Pannel from './components/Pannel';
export default {
  components: {
    //注册组件
    //'标签名': '组件名',
   // Pannel: Pannel,
//当标签名和组件名一致的时候可以缩写
        Pannel
  },
};

引入后可以直接引用

<template>
  <div id="app">
    <Pannel></Pannel>
  </div>
</template>

小结

1、遇到重复标签想复用?

封装成组件

2、组件好处?

各自独立, 便于复用

二、vue组件的概念

思考

组件的概念,什么是组件?

重点

1、组件是可复用的 Vue 实例, 封装标签, 样式和 JS 代码。

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

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

小结

1、组件是什么?

可复用的 vue 实例, 封装标签, 样式, JS

2、什么时候封装组件?

遇到重复标签, 可复用的时候

3、组件好处?

各自独立, 互不影响

三、vue组件的基本使用

思考

如何创建和使用组件?

重点

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

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

(重要): 组件内template只能有一个根标签

(重要): 组件内data必须是一个函数, 独立作用域

1、创建组件, 封装要复用的标签, 样式, JS 代码

2、注册组件

全局注册 – main.js 中

import Vue from 'vue';
import App from './App.vue';
import 组件对象 from 'vue文件路径';
Vue.component('组件名', 组件对象);

局部注册 – 某.vue 文件内 – 语法如图

import 组件对象 from 'vue文件路径';
export default {
  components: {
    PannelL: Pannel,
  },
};

3、使用组件

小结

1、创建和使用组件步骤?

创建.vue 文件,封装标签 – 样式 – JS 进去

注册组件 (全局 / 局部)

使用组件 (组件名用作标签)

2、组件运行结果?

把组件标签最终替换成, 封装的组件内标签

四、vue组件scoped的作用

思考

组件内的 scoped 是如何工作的?

目的

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

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

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

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

语法

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

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

<style scoped>

重点
当前组件内标签都被添加 data-v-hash 值 的属性
 

<div data-v-0a305208></div>
div[data-v-0a305208] {
  color: red;
}

css 选择器都被添加 [data-v-hash 值] 的属性选择器

小结

1、Vue 组件内样式, 只针对当前组件内标签生效如何做?

给 style 上添加 scoped

2、原理和过程是什么?

会自动给标签添加 data-v-hash 值属性, 所有选择都 带属性选择

15-Vue组件-通信

一、组件通信父向子
思考
1、从一个组件内, 给另外一个组件传值?
2、不使用 localStorage, 直接传递, 如何做?
目的
从外面给组件内传值, 先学会语法, 练习中在看使用场景
目标
父组件 - > 子组件 传值
首先明确父和子是谁, 在父引入子 (被引入的是子) 父: App.vue 子: MyProduct.vue 创建 MyProduct.vue
需求
封装一个商品组件MyProduct.vue - 外部传入具体要显示的数据, 如下图所示

步骤

  1. 创建组件components/MyProduct.vue - 复制下面标签
  2. 组件内在props定义变量, 用于接收外部传入的值
  3. App.vue中引入注册组件, 使用时, 传入具体数据给组件显示

重点

首先渲染静态页面,勾出框架

MyProdect.vue

<template>
  <div class="my-product">
    <h3>标题: 超级好吃的口水鸡</h3>
    <p>价格:50元</p>
    <p>全场8折</p>
  </div>
</template>

<script>
export default {
  props: ["title", "price", "intro"],
};
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

App.vue

<template>
  <div>
    <product></product>
    <product></product>
    <product></product>
  </div>
</template>

<script>
// 1创建vue组件
import product from "./components/Myprodect copy.vue";
// 引入组件
// 注册组件 components:{key和变量名可以同写}
export default {
  components: {
    product,
  },
};
</script>

<style></style>

子组件内, 定义变量, 准备接收, 然后使用变量

<template>
  <div class="my-product">
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ intro }}</p>
  </div>
</template>

<script>
export default {
  props: ['title', 'price', 'intro']
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

2、父组件

父组件(App.vue)内, 要展示封装的子组件(MyProduct.vue)

步骤:引入组件, 注册组件, 使用组件, 传值进去

<template>
  <div>
    <!-- 目标:父向子传值 
    让组件每次显示不同的数据信息-->
    <!-- 子组件定义变量接收变值 -->
    <!-- 向父组件传值进去 -->
    <product title="好吃的口水鸡" price="50" intro="开业大酬宾"></product>
    <product title="可爱多" price="10" intro="老板不在家,一折"></product>
    <!-- 当前面不加冒号的时候,后面是值字符串,是固定的值-->
    <product title="北京烤鸭" price="200" :intro="str"></product>
  </div>
</template>

<script>
// 1创建vue组件
import product from "./components/Myprodect copy.vue";
// 引入组件
// 注册组件 components:{key和变量名可以同写}
export default {
  components: {
    product,
  },
  data() {
    return {
      str: "快来吃,真好吃",
    };
  },
};
</script>

<style></style>

小结

组件封装复用的标签和样式, 而具体数据要靠外面传入

1、什么时候需要父传子技术?

从一个 vue 组件里把值传给另一个 vue 组件(父- >子)

2、父传子口诀(步骤)是什么?

子组件内, props 定义变量, 在子组件使用变量

父组件内, 使用子组件, 属性方式给 props 变量传值

二、组件通信父向子-配合循环

思考

1、能循环使用组件吗?

2、每次循环使用组件, 独立向组件内传值?

目的

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

目标

父组件 - > 子组件 循环使用-传值

数据

list: [
        {
          id: 1,
          proname: "超级好吃的棒棒糖",
          proprice: 18.8,
          info: "开业大酬宾, 全场8折",
        },
        {
          id: 2,
          proname: "超级好吃的大鸡腿",
          proprice: 34.2,
          info: "好吃不腻, 快来买啊",
        },
        {
          id: 3,
          proname: "超级无敌的冰激凌",
          proprice: 14.2,
          info: "炎热的夏天, 来个冰激凌了",
        },
      ],

代码

<template>
  <div>
    <!-- 每次父相子循环代码都会独立执行 -->
    <myprodect
      v-for="item in list"
      :key="item.id"
      :title="item.proname"
      :price="item.proprice"
      :info="item.info"
    ></myprodect>
  </div>
</template>

<script>
//目标:循环使用组件
// 创建组件
// 2引入组件
import Myprodect from "./components/Myprodect copy.vue";
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          proname: "超级好吃的棒棒糖",
          proprice: 18.8,
          info: "开业大酬宾, 全场8折",
        },
        {
          id: 2,
          proname: "超级好吃的大鸡腿",
          proprice: 34.2,
          info: "好吃不腻, 快来买啊",
        },
        {
          id: 3,
          proname: "超级无敌的冰激凌",
          proprice: 14.2,
          info: "炎热的夏天, 来个冰激凌了",
        },
      ],
    };
  },
  components: {
    Myprodect,
  },
};
</script>

<style></style>

重点

1、key 属性绑定 id

2、组件属性加冒号,因为传的是变量

小结

循环使用组件注意事项?

每次循环, 变量和组件, 都是独立的

三、组件通信-单项数据流

思考

1、子组件内想实现砍价功能, 点一次按钮砍掉价格?

2、子组件内能改变, 父传入的数据吗?

重点

1、子组件修改, 不通知父级, 会造成数据不一致性,子数据来源于数据

2、Vue 规定 props 里的变量, 本身是只读的,不能用来赋值

原则

1、父组件的数据发生了改变,子组件会自动跟着变
2、子组件不能直接修改父组件传递过来的props  props是只读的

小结

1、为何不建议, 子组件修改父组件传过来的值?

父子数据不一致, 而且子组件是依赖父传入的值

2、什么是单向数据流?

从父到子的数据流向, 叫单向数据流

3、props 里定义的变量能修改吗?

不能, props 里的变量本身是只读的

四、组件通信-子向父

思考

那子组件如何才能修改父组件里的数据呢?

需求

课上例子, 砍价功能, 子组件点击实现随机砍价功能

<template>
  <div class="my-product">
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ info }}</p>
    <button @click="subFn">宝刀砍一元</button>
  </div>
</template>

<script>
export default {
  props: ["index", "title", "price", "info"],
  methods: {
    subFn() {
      this.price = this.price - 1;
      // 子组件传入父亲的数据不通知数据会造成数据不一致行,
      
    },
  },
};
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

 因为单向数据流问题,会导致报错

重点

子组件触发父自定义事件方法

语法

父: @自定义事件名="父methods函数"

子: this.$emit("自定义事件名", 传值) - 执行父methods里函数代码

1、父组件

绑定自定义事件和事件处理函数

<template>
  <div>
    <MyProduct @subprice="fn"></MyProduct>
  </div>
  <!-- @自定义事件名="父组件 methods 里函数名" -->
</template>
export default {
  methods: {
    fn('参数'){
      // 逻辑代码
    }
  }
};

2、子组件

恰当的时机, 触发父组件自定义事件, 导致父 methods 里事件处理函数执行

<button @click="subFn">宝刀-砍1元</button>
export default {
  methods: {
    subFn() {
      //this.$emit('自定义事件名', 值);
      this.$emit('subprice', '值'); // 子向父
    },
  },
};

小结

1、什么时候使用子传父技术?

当子想要去改变父里的数据

2、子传父如何实现?

父组件内, 给组件@自定义事件="父 methods 函数" 子组件内, 恰当时机

this.$emit('自定义事件名', 值);

 传值后可以相互影响相互改变

五、跨组件通信

思考

两个没有任何引入关系的组件, 要如何互相通信呢?

重点

EventBus

常用于跨组件通信时使用

两个组件的关系非常的复杂,通过父子组件通讯是非常麻烦的。这时候可以使用通用的组件通讯方案:事件总线(event-bus)

步骤

1、src/EventBus/index.js – 创建空白 Vue 对象并导出

2、在要接收值的组件(List.vue) eventBus.$on('事件名', 函数体)

3、在要传递值的组件(MyProduct.vue) eventBus.$emit('事件名', 值)

语法

EventBus/index.js- 定义事件总线bus对象

import Vue from 'vue'
// 导出空白vue对象
export default new Vue()

List.vue注册事件 - 等待接收要砍价的值 (直接复制) - 准备兄弟页面

<template>
  <ul class="my-product">
      <li v-for="(item, index) in arr" :key="index">
          <span>{{ item.proname }}</span>
          <span>{{ item.proprice }}</span>
      </li>
  </ul>
</template>

<script>
export default {
  props: ['arr'],
}
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

List.vue正确代码(EventBus接收方)

<template>
  <ul class="my-product">
    <li v-for="(item, index) in arr" :key="index">
      <span>{{ item.proname }}</span>
      <span>{{ item.proprice }}</span>
    </li>
  </ul>
</template>

<script>
// 目标: 跨组件传值
// 1. 引入空白vue对象(EventBus)
// 2. 接收方 - $on监听事件
import eventBus from "../EventBus";
export default {
  props: ["arr"],
  // 3. 组件创建完毕, 监听send事件
  created() {
    eventBus.$on("send", (index, price) => {
      this.arr[index].proprice > 1 &&
        (this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2));
    });
  },
};
</script>

<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

小结

1、什么时候使用 eventBus 技术?

当 2 个没有引用关系的组件之间要通信传值

2、eventBus 技术本质是什么?

空白 Vue 对象, 只负责emit

六、拓展(组件通信的方式)

1、子父组件通信

2、事件总线(EventBus)(适合小型项目:页面少、功能少)

3、依赖注入(不推荐使用)(数据流向不好追溯)

4、vuex(企业级项目常用)

5、浏览器缓存

6、etc。。。。

Vue生命周期

一、人的生命周期

思考

1、什么是生命周期?

2、Vue所谓的生命周期又是什么?

重点

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

Vue_生命周期

二、钩子函数

目标

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

作用

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

场景

组件创建完毕后,可以在created 生命周期函数中发起Ajax 请求,从而初始化 data 数据

分类

4大阶段8个方法

  • 初始化
  • 挂载
  • 更新
  • 销毁

阶段

方法名

方法名

初始化

beforeCreate

created

挂载

beforeMount

mounted

更新

beforeUpdate

updated

销毁

beforeDestroy

destroyed

官网文档

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

小结

1、如何知道Vue生命周期到达了什么阶段?

使用钩子函数

2、钩子函数有哪些?

初始化 / 挂载 / 更新 / 销毁

三、初始化阶段

目标

掌握初始化阶段2个钩子函数作用和执行时机

重点

1.new Vue() – Vue实例化(组件也是一个小的Vue实例)

2.Init Events & Lifecycle – 初始化事件和生命周期函数

3.beforeCreate – 生命周期钩子函数被执行

4.Init injections&reactivity – Vue内部添加data和methods等

5.created – 生命周期钩子函数被执行, 实例创建

6.接下来是编译模板阶段 –开始分析

7.Has el option? – 是否有el选项 – 检查要挂到哪里

没有. 调用$mount()方法

有, 继续检查template选项

components/Life.vue - 创建一个文件

<script>
export default {
    data(){
        return {
            msg: "hello, Vue"
        }
    },
    // 一. 初始化
    // new Vue()以后, vue内部给实例对象添加了一些属性和方法, data和methods初始化"之前"
    beforeCreate(){
        console.log("beforeCreate -- 执行");
        console.log(this.msg); // undefined
    },
    // data和methods初始化以后
    // 场景: 网络请求, 注册全局事件
    created(){
        console.log("created -- 执行");
        console.log(this.msg); // hello, Vue

        this.timer = setInterval(() => {
            console.log("哈哈哈");
        }, 1000)
    }
}
</script>

App.vue - 引入使用

<template>
  <div>
    <h1>1. 生命周期</h1>
 	<Life></Life>
  </div>
</template>

<script>
import Life from './components/Life'
export default {
  components: {
    Life
  }
}
</script>

小结

1、Vue实例从创建到编译模板执行了哪些钩子函数?

beforeCreate / created

2、created函数触发能获取data?

能获取data, 不能获取真实DOM

四、挂载阶段

思考

1、我们在template里写的是真实的DOM吗?

2、哪个钩子函数里能获取真实DOM呢?

目标

掌握挂载阶段2个钩子函数作用和执行时机

重点

1.template选项检查

有 - 编译template返回render渲染函数

无 – 编译el选项对应标签作为template(要渲染的模板)

2.虚拟DOM挂载成真实DOM之前

3.beforeMount – 生命周期钩子函数被执行

4.Create … – 把虚拟DOM和渲染的数据一并挂到真实DOM上

5.真实DOM挂载完毕

6.mounted – 生命周期钩子函数被执行

components/Life.vue - 创建一个文件

<template>
  <div>
      <p>学习生命周期 - 看控制台打印</p>
      <p id="myP">{{ msg }}</p>
  </div>
</template>

<script>
export default {
    // ...省略其他代码
    
    // 二. 挂载
    // 真实DOM挂载之前
    // 场景: 预处理data, 不会触发updated钩子函数
    beforeMount(){
        console.log("beforeMount -- 执行");
        console.log(document.getElementById("myP")); // null

        this.msg = "重新值"
    },
    // 真实DOM挂载以后
    // 场景: 挂载后真实DOM
    mounted(){
        console.log("mounted -- 执行");
        console.log(document.getElementById("myP")); // p
    }
}
</script>

小结

1、Vue实例从创建到显示都经历了哪些钩子函数?

beforeCreate / created / beforeMount / mounted

2、created函数里, 能获取真实DOM吗?

不能获取真实DOM

3、在什么钩子函数里可以获取真实DOM?

mounted

五、更新阶段

思考

data数据改变会触发哪些钩子函数呢?

目标

掌握更新阶段2个钩子函数作用和执行时机

重点

1.当data里数据改变, 更新DOM之前

2.beforeUpdate – 生命周期钩子函数被执行

3.Virtual DOM…… – 虚拟DOM重新渲染, 打补丁到真实DOM

4.updated – 生命周期钩子函数被执行

5.当有data数据改变 – 重复这个循环

components/Life.vue - 创建一个文件

准备ul+li循环, 按钮添加元素, 触发data改变->导致更新周期开始

 

小结

1、什么时候执行updated钩子函数

当数据发生变化并更新页面后

2、在哪可以获取更新后的DOM

在updated钩子函数里

六、销毁阶段

目标

掌握销毁阶段2个钩子函数作用和执行时机

重点

1.当$destroy()被调用 – 比如组件DOM被移除(例v-if)

2.beforeDestroy – 生命周期钩子函数被执行

3.拆卸数据监视器、子组件和事件侦听器

4.实例销毁后, 最后触发一个钩子函数

5.destroyed – 生命周期钩子函数被执行

components/Life.vue - 准备生命周期方法(Life组件即将要被删除)

<script>
export default {
    // ...省略其他代码
    
    // 四. 销毁
    // 前提: v-if="false" 销毁Vue实例
    // 场景: 移除全局事件, 移除当前组件, 计时器, 定时器, eventBus移除事件$off方法
    beforeDestroy(){
        // console.log('beforeDestroy -- 执行');
        clearInterval(this.timer)
    },
    destroyed(){
        // console.log("destroyed -- 执行");
    }
}
</script>

主要: App.vue - 点击按钮让Life组件从DOM上移除 -> 导致Life组件进入销毁阶段

<Life v-if="show"></Life>
<button @click="show = false">销毁组件</button>

<script>
    data(){
        return {
            show: true
        }
    },
</script>

小结

一般在beforeDestroy/destroyed里做什么?

手动消除计时器/定时器/全局事件

&ref和&nextTick

一、ref获取原生dom元素

思考

1、Vue中如何获取真实DOM元素?

2、还有没有更多方式?

目标

通过id或ref属性获取原生DOM

在mounted生命周期 – 2种方式获取原生DOM标签

1、目标标签 – 添加id / ref

2、恰当时机, 通过id / 通过ref属性 获取目标标签

重点

<h1 id="h" ref="myH">获取原生dom</h1>
mounted(){ //mounted 生命周期内部
	console.log(document.getElementById("h")); // h1
	console.log(this.$refs.myH); // h1
},

小结

Vue中如何获取原生DOM呢?

用id属性或者ref属性都可以

二、ref属性组件对象

思考

Vue中能否获取组件对象呢?

目标

通过ref属性获取组件对象

1、创建Demo组件, 写一个方法

2、App.vue使用Demo组件, 给ref属性-名字随意

3、恰当时机, 通过ref属性 获取组件对象, 可调用组件对象里方法等

代码

components/Child/Demo.vue

<template>
  <div>
      <p>我是Demo组件</p>
  </div>
</template>
<script>
export default {
    methods: {
        fn(){
            console.log("demo组件内的方法被调用了");
        }
    }
}
</script>

More.vue - 获取组件对象 - 调用组件方法

<template>
  <div>
      <p>1. 获取原生DOM元素</p>
      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
      <p>2. 获取组件对象 - 可调用组件内一切</p>
      <Demo ref="de"></Demo>
  </div>
</template>
<script>
// 目标: 获取组件对象
// 1. 创建组件/引入组件/注册组件/使用组件
// 2. 组件起别名ref
// 3. 恰当时机, 获取组件对象
import Demo from './Child/Demo'
export default {
    mounted(){
        console.log(document.getElementById("h")); // h1
        console.log(this.$refs.myH); // h1

        let demoObj = this.$refs.de;
        demoObj.fn()
    },
    components: {
        Demo
    }
}
</script>

小结

1、如何获取组件对象呢?

目标组件添加ref属性

this.$refs.名字 获取组件对象

2、拿到组件对象能做什么?

调用组件里的属性/方法

三、nextTick基础使用

思考

data数据改变, 马上获取DOM里的内容如何写?

目标

目标:点击改data,获取原生DOM内容

1、创建标签显示数据

2、点击+1,马上获取原生DOM内容

原因: Vue更新DOM是异步的

目标:等DOM更新后, 触发此方法里函数体执行

1、语法: this.$nextTick(函数体)

代码

<template>
  <div>
      <p>1. 获取原生DOM元素</p>
      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
      <p>2. 获取组件对象 - 可调用组件内一切</p>
      <Demo ref="de"></Demo>
      <p>3. vue更新DOM是异步的</p>
      <p ref="myP">{{ count }}</p>
      <button @click="btn">点击count+1, 马上提取p标签内容</button>
  </div>
</template>

<script>
// 目标: 获取组件对象
// 1. 创建组件/引入组件/注册组件/使用组件
// 2. 组件起别名ref
// 3. 恰当时机, 获取组件对象
import Demo from './Child/Demo'
export default {
    mounted(){
        console.log(document.getElementById("h")); // h1
        console.log(this.$refs.myH); // h1

        let demoObj = this.$refs.de;
        demoObj.fn()
    },
    components: {
        Demo
    },
    data(){
        return {
            count: 0
        }
    },
    methods: {
        btn(){
            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); // 1
            })
        }
    }
}
</script>

小结

1、data改变更新DOM是同步还是异步的?

异步

2、我们可以在哪里访问到更新后的DOM呢?

this.$nextTick里的函数体

updated生命周期钩子函数

四、nextTick使用场景

思考

$nextTick还有什么使用场景呢?

目标

点击按钮自身隐藏, 出现输入框并马上处于激活状态

效果演示:

代码

<template>
  <div>
      <input ref="myInp" type="text" placeholder="这是一个输入框" v-if="isShow">
      <button v-else @click="btn">点击我进行搜索</button>
  </div>
</template>

<script>
// 目标: 点按钮(消失) - 输入框出现并聚焦
// 1. 获取到输入框
// 2. 输入框调用事件方法focus()达到聚焦行为
export default {
    data(){
        return {
            isShow: false
        }
    },
    methods: {
        async btn(){
            this.isShow = true;
            // this.$refs.myInp.focus()
            // 原因: data变化更新DOM是异步的
            // 输入框还没有挂载到真实DOM上
            // 解决:
            // this.$nextTick(() => {
            //     this.$refs.myInp.focus()
            // })
            // 扩展: await取代回调函数
            // $nextTick()原地返回Promise对象
            await this.$nextTick()
            this.$refs.myInp.focus()
        }
    }
}
</script>

小结

1、$nextTick函数原地返回什么?

Promise对象

2、如何在JS中主动触发标签的事件呢?

获取到DOM对象, 调用事件方法

五、组件的name

name的作用

1、递归组件的(组件调用自身组件)

这个时候被递归的组件name必写

2、组件的缓存keep-alive

一个组件需不需要被缓存,也是需要通过name属性来控制。

3、vue调试插件DevTools

显示组件的name,语义化

组件进阶

一、组件进阶 - 动态组件

思考

问题1: 如何切换2个组件, 互斥的显示或隐藏呢?

问题2: 不用v-if可以实现吗?有没有简单的方式?

目标

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

需求

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

效果如下:

步骤&代码

  1. 准备被切换的 - UserName.vue / UserInfo.vue 2个组件
  2. 引入到UseDynamic.vue注册
  3. 准备变量来承载要显示的"组件名"
  4. 设置挂载点, 使用is属性来设置要显示哪个组件
  5. 点击按钮 – 修改comName变量里的"组件名"
<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/01/UserName'
import UserInfo from '../components/01/UserInfo'
export default {
    data(){
        return {
            comName: "UserName"
        }
    },
    components: {
        UserName,
        UserInfo
    }
}
</script>

在App.vue - 引入01_UseDynamic.vue并使用显示

小结

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

  1. 什么是动态组件?
    在同一个挂载点, 可以切换显示不同组件
  2. 如何使用动态组件?
    vue内置的component组件, 配合is属性
  3. 如何切换?
    改变is属性的值, 为要显示的组件名即可

二、组件进阶 - 组件缓存

思考

问题1: 频繁的切换会不会导致组件频繁创建和销毁呢?

问题2: 性能高不高呢?

问题3: 如何避免这个问题?

目标

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

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

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

演示2: 使用keep-alive内置的vue组件, 让动态组件缓存而不是销毁

语法

Vue内置的keep-alive组件 包起来要频繁切换的组件

代码&步骤

02_UseDynamic.vue

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

补充生命周期

  • activated - 激活
  • deactivated - 失去激活状态

总结

keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法。

  1. 如何进行组件缓存?
    vue内置的keep-alive组件把要缓存的组件包起来
  2. 组件缓存好处?
    不会频繁的创建和销毁组件, 页面更快呈现

三、组件进阶 - 激活和非激活

思考

如何知道组件被切走了, 还是切换回来了呢?

目标

被缓存的组件不再创建和销毁, 而是激活和非激活

补充2个钩子方法名:

activated – 激活时触发

deactivated – 失去激活状态触发

小结

如何知道缓存的组件是出现还是消失了?

activated – 获得激活状态

deactivated – 失去激活状态

拓展

include="组件Aname"

组件A会被缓存,其他组件不缓存

exclude="组件A的name"

组件A不缓存,其他组件缓存

四、组件进阶 - 组件插槽

思考

问题1: 组件里的数据不确定可以怎么做?

问题2:组件里的标签不确定怎么办呢?

目标

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

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

插槽例子:

需求

以前折叠面板案例, 想要实现不同内容显示, 我们把折叠面板里的Pannel组件, 添加组件插槽方式

语法

  1. 组件内用<slot></slot>占位
  2. 使用组件时<Pannel></Pannel>夹着的地方, 传入标签替换slot

代码&步骤

03/Pannel.vue - 组件(直接复制)

<template>
  <div>
    <!-- 按钮标题 -->
    <div class="title">
      <h4>芙蓉楼送辛渐</h4>
      <span class="btn" @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>
h3 {
  text-align: center;
}

.title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #ccc;
  padding: 0 1em;
}

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

.container {
  border: 1px solid #ccc;
  padding: 0 1em;
}

.btn {
  /* 鼠标改成手的形状 */
  cursor: pointer;
}

img {
  width: 50%;
}
</style>

views/03_UserSlot.vue - 使用组件(直接复制)

框: 在这个基础重复使用组件

<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
    </div>
  </div>
</template>

<script>
export default {
};
</script>

<style>
#app {
  width: 400px;
  margin: 20px auto;
  background-color: #fff;
  border: 4px solid blueviolet;
  border-radius: 1em;
  box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
  padding: 1em 2em 2em;
}
</style>

views/03_UseSlot.vue - 组件插槽使用

<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
      <Pannel>
          <img src="../assets/mm.gif" alt="">
          <span>我是内容</span>
      </Pannel>
      <Pannel>
          <p>寒雨连江夜入吴,</p>
          <p>平明送客楚山孤。</p>
          <p>洛阳亲友如相问,</p>
          <p>一片冰心在玉壶。</p>
      </Pannel>
      <Pannel></Pannel>
    </div>
  </div>
</template>

<script>
import Pannel from "../components/03/Pannel";
export default {
  components: {
    Pannel,
  },
};
</script>

小结

  1. 当组件内某一部分标签不确定怎么办?
    用插槽技术
  2. 插槽具体如何使用?
    先在组件内用slot占位
    使用组件时, 传入具体标签插入
  3. 插槽运行效果?
    传入的标签会替换掉slot显示

五、组件进阶 - 插槽默认内容

思考

问题1: 使用我封装组件的人, 不给slot传标签, 怎么办?

问题2: 可否设置默认内容?

目标

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

语法&口诀

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

<slot>默认内容</slot>

小结

  1. 如何给插槽设置默认显示内容?
    slot标签内写好默认要显示内容
  2. 什么时候插槽默认内容会显示?
    当使用组件并未给我们传入具体标签或内容时

六、组件进阶 - 具名插槽

思考

问题1: 组件内有2处以上不确定标签怎么办?

问题2: 能否给slot起个名字呢?

目标

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

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

语法&代码

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

  1. slot使用name属性区分名字
  2. template配合v-slot:名字来分发对应标签

components/04/Pannel.vue - 留下具名slot

<template>
  <div>
    <!-- 按钮标题 -->
    <div class="title">
      <slot name="title"></slot>
      <span class="btn" @click="isShow = !isShow">
        {{ isShow ? "收起" : "展开" }}
      </span>
    </div>
    <!-- 下拉内容 -->
    <div class="container" v-show="isShow">
     <slot name="content"></slot>
    </div>
  </div>
</template>

views/04_UseSlot.vue使用

<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
      <Pannel>
        <template v-slot:title>
          <h4>芙蓉楼送辛渐</h4>
        </template>
        <template v-slot:content>
          <img src="../assets/mm.gif" alt="">
          <span>我是内容</span>
        </template>
      </Pannel>
      <Pannel>
        <template #title>
          <span style="color: red;">我是标题</span>
        </template>
        <template #content>
          <p>寒雨连江夜入吴,</p>
          <p>平明送客楚山孤。</p>
          <p>洛阳亲友如相问,</p>
          <p>一片冰心在玉壶。</p>
        </template>
      </Pannel>
    </div>
  </div>
</template>

<script>
import Pannel from "../components/04/Pannel";
export default {
  components: {
    Pannel,
  },
};
</script>

小结

  1. 组件内多处不确定的标签如何做?
    slot占位, 给name属性起名字来区分
    template配合v-slot:name分发要替换的标签
  2. v-slot: 可以简化成什么?
    #

七、组件进阶 - 作用域插槽

思考

问题: 使用插槽时, 想使用组件内的变量?

目标

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

复习: 插槽内slot中显示默认内容

例子: 默认内容在子组件中, 但是父亲在给插槽传值, 想要改变插槽显示的默认内容

语法&代码

  1. 子组件, 在slot上绑定属性和子组件内的值
  2. 使用组件, 传入自定义标签, 用template和v-slot="自定义变量名"
  3. scope变量名自动绑定slot上所有属性和值

components/05/Pannel.vue - 定义组件, 和具名插槽, 给slot绑定属性和值

<template>
  <div>
    <!-- 按钮标题 -->
    <div class="title">
      <h4>芙蓉楼送辛渐</h4>
      <span class="btn" @click="isShow = !isShow">
        {{ isShow ? "收起" : "展开" }}
      </span>
    </div>
    <!-- 下拉内容 -->
    <div class="container" v-show="isShow">
     <slot :row="defaultObj">{{ defaultObj.defaultOne }}</slot>
    </div>
  </div>
</template>

<script>
// 目标: 作用域插槽
// 场景: 使用插槽, 使用组件内的变量
// 1. slot标签, 自定义属性和内变量关联
// 2. 使用组件, template配合v-slot="变量名"
// 变量名会收集slot身上属性和值形成对象
export default {
  data() {
    return {
      isShow: false,
      defaultObj: {
        defaultOne: "无名氏",
        defaultTwo: "小传同学"
      }
    };
  },
};
</script>

views/05_UseSlot.vue

<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
      <Pannel>
        <!-- 需求: 插槽时, 使用组件内变量 -->
        <!-- scope变量: {row: defaultObj} -->
        <template v-slot="scope">
          <p>{{ scope.row.defaultTwo }}</p>
        </template>
      </Pannel>
    </div>
  </div>
</template>

<script>
import Pannel from "../components/05/Pannel";
export default {
  components: {
    Pannel,
  },
};
</script>

小结

  1. 作用域插槽什么时候使用?
    使用组件插槽技术时, 需要用到子组件内变量
  2. 作用域插槽使用口诀?
    子组件在slot身上添加属性和子组件的值
    使用组件处template配合v-slot=“变量名”(收集slot身上的所有属性和值)

八、组件进阶 - 作用域插槽使用场景

思考

问题:作用域插槽具体使用场景有哪些呢?

目标

了解作用域插槽使用场景, 自定义组件内标签+内容

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

准备MyTable.vue组件 – 内置表格, 传入数组循环铺设页面, 把对象每个内容显示在单元格里

准备UseTable.vue – 准备数据传入给MyTable.vue使用

代码&步骤

components/06/MyTable.vue - 模板(直接复制)

<template>
  <div>
      <table border="1">
          <thead>
              <tr>
                  <th>序号</th>
                  <th>姓名</th>
                  <th>年龄</th>
                  <th>头像</th>
              </tr>
          </thead>
          <thead>
              <tr>
                  <td></td>
                  <td></td>
                  <td></td>
                  <td></td>
              </tr>
          </thead>
      </table>
  </div>
</template>

<script>
export default {

}
</script>

views/06_UseTable.vue - 准备数据, 传入给MyTable.vue组件里循环使用

list: [
    {
        name: "小传同学",
        age: 18,
        headImgUrl:
        "http://yun.itheima.com/Upload/./Images/20210303/603f2d2153241.jpg",
    },
    {
        name: "小黑同学",
        age: 25,
        headImgUrl:
        "http://yun.itheima.com/Upload/./Images/20210304/6040b101a18ef.jpg",
    },
    {
        name: "智慧同学",
        age: 21,
        headImgUrl:
        "http://yun.itheima.com/Upload/./Images/20210302/603e0142e535f.jpg",
    },
],

例子: 我想要给td内显示图片, 需要传入自定义的img标签

正确做法:

在MyTable.vue的td中准备占位, 但是外面需要把图片地址赋予给src属性,所以在slot上把obj数据绑定

components/06/MyTable.vue   - 正确代码

<template>
  <div>
      <table border="1">
          <thead>
              <tr>
                  <th>序号</th>
                  <th>姓名</th>
                  <th>年龄</th>
                  <th>头像</th>
              </tr>
          </thead>
          <tbody>
              <tr v-for="(obj, index) in arr" :key="index">
                  <td>{{ index + 1 }}</td>
                  <td>{{ obj.name }}</td>
                  <td>{{ obj.age }}</td>
                  <td>
                      <slot :row="obj">
                          <!-- 默认值给上,如果使用组件不自定义标签显示默认文字 -->
                          {{ obj.headImgUrl}}
                      </slot>
                  </td>
              </tr>
          </tbody>
      </table>
  </div>
</template>

<script>
export default {
    props: {
        arr: Array
    }
}
</script>
在UseTable使用MyTable的时候, template上v-slot绑定变量, 传入img组件设置图片地址

<template>
  <div>
    <MyTable :arr="list"></MyTable>
    <MyTable :arr="list">
        <!-- scope: {row: obj} -->
       <template v-slot="scope">
            <a :href="scope.row.headImgUrl">{{ scope.row.headImgUrl }}</a>
       </template>
    </MyTable>
    <MyTable :arr="list">
       <template v-slot="scope">
            <img style="width: 100px;" :src="scope.row.headImgUrl" alt="">
       </template>
    </MyTable>
  </div>
</template>

<script>
import MyTable from "../components/06/MyTable";
export default {
  components: {
    MyTable,
  },
  data() {
    return {
      list: [
        {
          name: "小传同学",
          age: 18,
          headImgUrl:
            "http://yun.itheima.com/Upload/./Images/20210303/603f2d2153241.jpg",
        },
        {
          name: "小黑同学",
          age: 25,
          headImgUrl:
            "http://yun.itheima.com/Upload/./Images/20210304/6040b101a18ef.jpg",
        },
        {
          name: "智慧同学",
          age: 21,
          headImgUrl:
            "http://yun.itheima.com/Upload/./Images/20210302/603e0142e535f.jpg",
        },
      ],
    };
  },
};
</script>

<style>
</style>

小结

我们为什么要使用作用域插槽?

可以让组件更加灵活的适用于不同的场景和项目

插槽可以自定义标签, 作用域插槽可以把组件内的值取出来自定义内容

自定义指令

一、自定义指令

思考

问题1: Vue内置指令不满足要求?

问题2: 能否自己定义一些指令来使用?

目标

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

局部注册

语法

<input type="text" v-focus>

export default {
    directives: {
        focus: {
            inserted(el){
                el.focus()
            }
        }
    }
}

全局注册

语法

// 全局指令 - 到处"直接"使用
Vue.directive("gfocus", {
  inserted(el) {
    el.focus() // 触发标签的事件方法
  }
})

全局注册自定义指令, 哪里都能用, 局部注册, 只能在当前vue文件里用

小结

我们为什么要自定义指令?

在Vue内置指令满足不了需求时, 可以自己定义使用

二、自定义指令传值

思考

问题: 指令能不能传值呢?

目标

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

语法

// 目标: 自定义指令传值
Vue.directive('color', {
  inserted(el, binding) {
    el.style.color = binding.value
  },
  update(el, binding) {
    el.style.color = binding.value
  }
})

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;