VUE 尚硅谷 学习内容详解

前言:

必备前提:

●HTML + CSS + JavaScript

●WebAPI ( DOM + BOM)

●Ajax

可选前提:

●Node.js

特点:

1.采用组件化模式,提高代码复用率、且让代码更好维护。

 2.声明式编码,让编码人员无需直接操作DOM,提高开发效率。

 3.使用 虚拟DOM +优秀的 Diff算法 ,尽量复用DOM节点。


本篇文章学习内容包括:

1️⃣前端工程化与Webpack

●前端工程化的相关概念

●Webpack 的常见用法

●打包发布

●Source Map

目标:

①了解工程化的前端开发方式;

②能够知道Webpack在实际开发中所起到的作用。

2️⃣vue基础入门- part1

●Vue 的基本使用步骤

●Vue中常用的指令

●vue-devtools 调试工具

目标:

①能够使用 Vue的指令完成页面结构的渲染

②能够使用 Vue的调试工具辅助Vue的开发

3️⃣vue基础入门- part2

●过滤器和侦听器

●计算属性的用法

●axios的基本用法

●vue-cli 的安装和使用

目标:

①能够在 实际开发中合理运用过滤器、侦听器、计算属性解决自己遇到的问题

②能够使用 axios发起Ajax请求

③能够使用vue-cli工具生成工程化的Vue项目

4️⃣组件与生命周期

●组件的注册与使用

●组件的 props自定义属性

●解决组件样式冲突

●组件的生命周期

●组件之间的通讯 (数据共享)

目标:

①能够掌握.vue单文件组件的基本用法

②能够掌握组件通讯的三种方式

③掌握组件生命周期的执行顺序和应用场景

5️⃣ref &购物车案例

●使用ref引用DOM元素和组件实例

●$nextTick的基本使用

●购物车案例

目标:

①能够使用ref获取页面上DOM或组件的引用

②能够知道$nextTick的应用场景并合理地使用

③通过“购物车案例”巩固前4天所有学知识

6️⃣Vue组件的高级用法

●动态组件的使用

●插槽的使用(默认插槽、具名插槽、作用域插槽)

●自定义指令

●ESLint的使用

目标:

①能够使用keep-alive实现组件的缓存

②能够使用插槽提高组件的复用性

③能够了解常见的ESLint语法规则

7️⃣路由( vue-router )

●路由的基本配置与使用

●路由重定向

●嵌套路由、 动态路由

●编程式导航、路由导航守卫

目标:

①能够在项目中安装和配置路由

②能够使用路 由实现单页面应用程序的开发

③能够使用 导航守卫控制路由的访问权限

8️⃣黑马头条(收尾案例)

●Vant组件库

●封装axios

●上拉加载&下拉刷新

●Vant主题定制

目标:

①掌握Vant组件库的基本使用

②能够知道如何封装axios请求模块

③能够知道如何实现上拉加载和下拉刷新功能


前端工程化与webpack

①理解什么是前端工程化

●转变对前端开发的认知

②了解webpack的基本用法

●为后面Vue和React课程的学习做技术储备

③不强制要求大家能手动配置webpack

●一定要知道webpack在项目中有什么作用

●清楚webpack中的核心概念

小白眼中的前端开发VS实际的前端开发

小白眼中的前端开发:

●会写HTML + CSS + JavaScript就会前端开发

●需要美化页面样式,就拽一个bootstrap过来

●需要操作DOM或发起Ajax请求,再拽-一个jQuery过来

●需要快速实现网页布局效果,就拽一个Layui过来

实际的前端开发:

模块化(js的模块化、CSS 的模块化、资源的模块化)

组件化(复用现有的UI结构、样式、行为)

规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、Git分支管理)

自动化(自动化构建、自动部署、自动化测试)

前端工程化

指的是:在企业级的前端项目开发中, 把前端开发所需的工具、技术、流程、经验等进行规范化、标准化。

企业中的Vue项目和React项目,都是基于工程化的方式进行开发的。

好处:前端开发自成体系,有一套标准的开发方案和流程

前端工程化的解决方案

早期的前端工程化解决方案:

●grunt ( Grunt: JavaScript 世界的构建工具 | Grunt中文网Grunt 是基于 Node.js 的项目构建工具。它可以自动运行你所设定的任务。Grunt 拥有数量庞大的插件,几乎任何你所要做的事情都可以用 Grunt 实现。https://www.gruntjs.net/ )

●gulp ( https:/ /www.gulpjs.com.cn/ )

目前主流的前端工程化解决方案:

●webpack ( https:/ /www.webpackjs.com/ )

●parcel ( Parcel 中文网https://zh.parceljs.org/


VUE基础入门

①el: 挂载点

el是用来设置Vue实例挂载(管理)的元素

Vue.js - 渐进式 JavaScript 框架 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs.org

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

var app = new Vue({
    el:"#app",
    data:{
        message:"黑马程序员"
    }
})

Vue实例的作用范围是什么呢?

Vue会管理el选项命中的元素及其内部的后代元素

是否可以使用其他的选择器?

可以使用其他的选择器但是建议使用ID选择器(唯一)

是否可以设置其他的dom元素呢?

可以使用其他的双标签, 不能使用HTML和BODY

②data:数据对象

◆Vue中用到的数据定义在data中

◆data中可以写复杂类型的数据

◆渲染复杂类型数据时,遵守js的语法即可

本地应用

①v-text

设置标签的文本值(textContent)

  • v-text 指令的作用是:设置标签的内容(textContent)
  • 默认写法会替换全部内容使用差值表达式{ }可以替换指定内容

②v-html

设置标签的 innerHTML。

  • v-html指令的作用是: 设置元素的innerHTML
  • 内容中有html结构会被解析为标签
  • v-text指令无论内容是什么,只会解析为文本

③v-on基础

为元素绑定事件。

  • v-on指令的作用是:为元素绑定事件
  • 事件名不需要写on
  • 指令可以简写为@
  • 绑定的方法定义在methods属性中
  • 方法内部通过this关键字可以访问定义在data中数据

✍🏻计数器案例

 实现思路:

1. data中定义数据: 比如num

2. methods中添加两个方法: 比如add(递增),sub(递减)

3.使用v-text将num设置给span标签

4.使用v-on将add, sub分别绑定给+,按钮

5.累加的逻辑: 小于10累加,否则提示

6.递减的逻辑: 大于0递减,否则提示

实现步骤:

  • 创建vue示例时: el(挂载点),data(数据),methods(方法)
  • v-on 指令的作用是绑定事件,简写为@
  • 方法中通过 this,关键字获取data中的数据
  • v-text 指令的作用是:设置元素的文本值,简写为({ }}
  • v-html指令的作用是:设置元素的innerHTML

④v-show

根据表达值的真假,切换元素的显示和隐藏。

  • v-show 指令的作用是:根据真假切换元素的显示状态
  • 原理是修改元素的display,实现显示隐藏
  • 指令后面的内容最终都会解析为布尔值
  • 值为true元素显示,值为false元素隐藏
  • 数据改变之后,对应元素的显示状态会同步更新

⑤v-if

根据表达值的真假, 切换元素的显示和隐藏(操纵dom元素)

  • v-if 指令的作用是:根据表达式的真假切换元素的显示状态
  • 本质是通过操纵dom元素来切换显示状态
  • 表达式的值为true,元素存在于dom树中为false,从dom树中移除
  • 频繁的切换v-show,反之使用v-if,前者的切换消耗小

⑥v-bind

设置元素的属性(比如:src,title,class)

<div id="app">
    <img v-bind :src= "imgSrc" >
    <img v-bind :title="imgtitle + '!!!' ">
    <img v-bind :class="isActive? 'active':"">  //三元表达式
    <img v-bind :class="{active:isActive}">
</div>

var app = new Vue({
    el:" #app",
    data:{
        imgSrc:"图片地址",
        imgTitle:"黑马程序员",
        isActive:false
    }    
})
  • v-bind 指令的作用是:为元素绑定属性
  • 完整写法是v-bind : 属性名
  • 简写的话可以直接省略v-bind,只保留:属性名
  • 需要动态的增删class建议使用对象的方式

✍🏻图片切换案例

  • 列表数据使用数组保存
  • v-bind指令可以设置元素属性,比如src
  • v-show和v-if都可以切换元素的显示状态,频繁切换用v-show


⑦v-for

根据数据生成列表结构。

  • v-for 指令的作用是:根据数据生成列表结构
  • 数组经常和v-for结合使用
  • 语法是( item,index ) in数据
  • item和index可以结合其他指令一起使用
  • 数组长度的更新会同步到页面上,是响应式的

⑧v-on补充

传递自定义参数事件修饰符。

  • 事件绑定的方法写成函数调用的形式,可以传入自定义参数
  • 定义方法时需要定义形参来接收传入的实参
  • 事件的后面跟上 .修饰符 可以对事件进行限制
  • .enter 可以限制触发的按键为回车
  • 事件修饰符有多种

⑨v-model

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

  • v-model 指令的作用是便捷的设置和获取表单元素的值
  • 绑定的数据会和表单元素 相关联
  • 绑定的数据 ←→ 表单元素的值

✍🏻案例:小黑记事本

 实现思路:

①新增

1.生成列表结构(v-for数组)

2.获取用户输入(v-model)

3.回车新增数据(v-on .enter 添加数据)

1. v-for指令的作用

2. v-model指令的作用

3. v-on指令,事件修饰符

4.通过审查元素快速定位

②删除

点击删除指定内容(v-on splice索引)

  • 1.数据改变,和数据绑定的元素同步改变
  • 2.事件的自定义参数
  • 3. splice方法的作用
  • 4.splice( ) 方法用于添加或删除数组中的元素

③统计

统计信息个数(v-text length)

基于数据的开发方式

④清空

点击清除所有信息(v-on清空数组)

⑤隐藏

没有数据时,隐藏元素(V-show  v-if 数组非空)

  • 列表结构可以通过v-for指令结合数据生成
  • v-on结合事件修饰符可以对事件进行限制比如.enter
  • v-on在绑定事件时可以传递自定义参数
  • 通过v-model可以快速的设置和获取表单元素的值
  • 基于数据的开发方式


axios

功能强大的网络请求库。

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

axios.get(地址?查询字符串key=value&key2=value2).then(function(response){  },funtion(err){ })
axios.post(地址,参数对象{key:value,key2:value2}).then(function(response){ },function(err){  })
  • axios必须先导入才可以使用
  • 使用get或post方法即可发送对应的请求
  • then方法中的回调函数会在请求成功或失败时触发
  • 通过回调函数的形参可以获取响应内容或错误信息
  • axios回调函数中的this已经改变,无法访问到data中数据
  • 把this保存起来,回调函数中直接使用保存的this即可

✍🏻天知道案例

 实现思路:

①回车查询

1.按下回车(v-On .enter)

2.查询数据(axios接口v-model )

3.渲染数据(v-for数组that)

 ⭕注意:

  • 应用的逻辑代码建议和页面分离,使用单独的js文件编写
  • axios回调函数中this指向改变了,需要额外的保存一份
  • 服务器返回的数据比较复杂时,获取的时候需要注意层级结构

②点击查询

1.点击城市(v-on自定义参数)

2.查询数据(this( )方法)

3.渲染数据

  • 自定义参数可以让代码的复用性更高
  • methods中定义的方法内部,可以通过this关键字点出其他的方法


✍🏻音乐播放器案例

①歌曲搜索

1.按下回车(v-on .enter)

2.查询数据(axios接口V- model )

3.渲染数据(v-for数组that)

②歌曲播放

③歌曲封面

④歌曲评论

⑤播放动画

⑥mv播放


尚硅谷vue

webpack的基本使用

概念: webpack是前端项目工程化的具体解决方案

主要功能:它提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端JavaScript的兼容性、性能优化等强大的功能。

好处: 让程序员把工作的重心放到具体功能的实现上,提高了前端开发效率和项目的可维护性

注意: 目前Vue, React 等前端项目,基本上都是基于webpack进行工程化开发的。

创建列表隔行变色项目

实现思路:

①新建项目空白目录,并运行ηpm init -y命令,初始化包管理配置文件package.json

②新建src源代码目录

③新建src -> index.html首页和src -> index.js脚本文件

④初始化首页基本的结构

⑤运行ηpm install jquery-S命令,安装jQuery 

⑥通过ES6模块化的方式导入jQuery, 实现列表隔行变色效果

在项目中安装webpack

在终端运行如下的命令,安装webpack相关的两个包:

npm install webpack@5 42.1 webpack-cli@4.7.2 -D

-S是 --save的简写

-D是 --save-dev的简写

①在项目根目录中,创建名为 webpack.config.js 的webpack配置文件,并初始化如下的基本配置:

module.exports = {
    mode: 'development'
     // mode用来指定构建模式。可选值有development和production
}

②在package.json的scripts节点下,新增dev脚本如下:

"scripts": {
    "dev": "webpack" // script 节点下的脚本,可以通过npm run 执行。例如npm run dev
}

③在终端中运行npm run dev命令,启动webpack进行项目的打包构建。


初识Vue:

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

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

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

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

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

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

7.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;

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

1.表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方:

(1). a

(2). a+b

(3). demo(1)

(4).x===y? 'a' : 'b'

2.js代码(语句)

(1). if( ){ }

(2). for( ){ }

Vue模板语法有2大类:

1.插值语法:

功能:  用于解析标签体内容。

写法: {{xxx}}, xxx是js表达式,且可以直接读取到data中的所有属性。

2.指令语法:

功能: 用于解析标签(包括:标签属性、标签体内容、绑定事件.....)。

举例:  v-bind:href="xxx"  或简写为  :href="xxx",  xxx同样要写js表达式,且可以直接读取到data中的所有属性。

备注: Vue中有很多的指令,且形式都是: v-????, 此处只是v- bind举个例子

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

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

1.单向绑定(v-bind):数据只能从data流向页面。

2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。

⭕备注:

1.双向绑定一般都应用在表单类元素上(如: input、 select等 )

2.v-model:value可以简写为v-model, 因为v- model默认收集的就是value值。

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

el两种写法

//el的两种写法
/* const v = new Vue({
    //el:'#root',//第一种写法
    data:{
        name:'尚佳谷'
    }
  })
    console.log(v)
    v.$mount('#root') //第二种写法 */

data两种写法

//data的两种写法
new Vue({
    el:'#root',//data的第一种写法:对象式data:(*
    /*name:'尚硅谷'
    }*/
    //data的第二种写法:函数式
    data:function(){
        return{
            name:'尚佳谷'
        }
    }
})

⭕data与el的2种写法

1.el有2种写法

(1) new Vue时候配置el属性。

(2)先创建Vue实例,随后再通过vm. $mount( ' #root' )指定e1的值。

2. data有2种写法

(1).对象式

(2).函数式

如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。

3.一个重要的原则:

由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数, this就不再是Vue实例了(而是window)。

MVVM模型

1. M:模型(Model) :对应data中的数据

2. V:视图(View) :模板

3. VM:视图模型(ViewModel) : Vue实例对象

观察发现:

1. data中所有的属性,最后都出现在了vm身上。

2.vm身上所有的属性及Vue原型上所有属性,在Vue模板中都可以直接使用。

<script type="text/javascript">
let number = 18
let person = {
    name:'张三',
    sex:'男',
}
Object.defineProperty(person, 'age',{
    // value:18,
    // enumerable:true,//控制属性是否可以枚举,默认值是false
    // writable:true,//控制属性是否可以被修改,默认值是false
    // configurable:true //控制属性是否可以被删除,默认值是false
    //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
    get(){
        console.log("有人读取age属性了轻");
        return number
    },
    //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
    set(value){
        console.log('有人修改了age属性,且值是',value)
    }
})
</script>

数据代理:

通过一个对象代理对另一个对象中属性的操作。

1. Vue中的数据代理:

通过vm对象来代理data对象中属性的操作(读/写)。

2.Vue中数据代理的好处:

更加方便的操作data中的数据。

3.基本原理:

通过Object.defineProperty( )把data对象中所有属性添加到vm上。

为每一个添加到vm上的属性,都指定一个 getter/setter

getter/setter 内部去操作(读/写) data中对应的属性。


事件的基本使用:

1.使用 v-on:xxx 或 @xxx绑定事件,其中xxx是事件名;

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

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

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

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

.preventDefault( ) 阻止默认行为

①Vue中的事件修饰符:

1.prevent:阻止默认事件(常用)

2.stop: 阻止事件冒泡(常用) ;

3.once: 事件只触发一次(常用);

4. capture:使用事件的捕获模式;

5.self: 只有event . target是当前操作的元素时才触发事件;

6. passive: 事件的默认行为立即执行,无需等待事件回调执行完毕;(异步执行)

事件冒泡:即多个组件嵌套,触发内层组件时,会一一触发外层事件

.stopPropagation( )


②键盘事件

1.Vue中常用的按键别名:

  • 回车=> enter
  • 删除=>delete(捕获“删除”和“退格”键)
  • 退出=> esc
  • 空格=>space
  • 换行=> tab(特殊,必须配合keydown使用)
  • 上=> up
  • 下=> down
  • 左=> left
  • 右=> right

2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)

例:cap-slk

3.系统修饰键(用法特殊) : ctrl、 alt、 shift、 meta

(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。

(2).配合keydown使用:正常触发事件。

4.也可以使用keyCode去指定具体的按键(不推荐)

5. Vue.config. keyCodes.自定义键名 = 键码,可以去定制按键别名。


计算属性与监听:

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

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

3. get函数什么时候执行?

(1).初次读取时会执行一次。

(2).当依赖的数据发生改变时会被再次调用。

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

5.备注:

1.计算属性最终会出现在vm上,直接读取使用即可。

2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生。

监视属性-watch

 实现思路:

1.通过通过vm对象的$watch( )或watch配置来监视指定的属性;

2.当属性变化时,回调函数自动调用,在函数内部进行计算。

immediate :true, // 初始化时让handler调用
handler( newValue , oldValue){ }

⭕注意点:

1.当被监视的属性变化时,回调函数自动调用,进行相关操作

2.监视的属性必须存在,才能进行监视! !

3.监视的两种写法:

(1).new Vue时 传入watch配置

(2).通过 vm.$watch 监视

✍🏻天气案例-深度监视

深度监视:

(1).Vue中的 watch 默认不监测对象内部值的改变(一层)。

(2).配置 deep:true 可以监测对象内部值改变(多层)。

⭕备注:

(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!

(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

watch:{
    //正常写法
    /* isHot:(
        // immediate:true,//初始化时让handler调用一下
        // deep:true,//深度监视
        handler(newValue,oldValue){
            console.log('isHot被修改了',newValue,oldValue)
        },*/
    //简写
    isHot:(newValue,oldValue)=>{
        console.log('isHot被修改了',newValue,oldValue,this)
    }
}

computed和watch之间的区别:

1. computed能完成的功能,watch 都可以完成。

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

⭕两个重要的小原则:

1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。

2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数,Promise的回调函数等),最好写成箭头函数,

这样this的指向才是vm或组件实例对象。


class与style绑定

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

绑定样式:

1. class样式

写法:class="xxx" xxx可以是字符串、对象、数组。

字符串写法适用于:类名不确定,要动态获取。

对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

2. style样式

:style=" {fontSize: xxx}"其中xxx是动态值。

:style="[a,b]"其中a、b是样式对象。


条件渲染

条件渲染指令

v-if与v-else

v-show

template只能配合v-if不能配合v-show

条件渲染:

1.v-if

写法:

(1).v-if="表达式”

(2).v-else-if="表达式"

(3).v-else="表达式"

适用于: 切换频率较低的场景。

特点: 不展示的DOM元素直接被移除。

注意: v-if可以和:v-else-if、v-else起使用, 但要求结构不能被“打断”。

2.v-show

写法: v-show=" 表达式”

适用于:切换频率较高的场景。

特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

⭕备注:

使用v-if的时,元素可能无法获取到,而使用v-show 定可以获取到。

3.v-for指令

1.用于展示列表数据

2.语法: v-for="(item, index) in xxx" :key="yyy"

3.可遍历: 数组、对象、字符串(用的很少)、指定次数(用的很少)

🌟面试题: react、 vue中的key有什么作用? (key的内部原理)

1.虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM],

随后Vue进行[新虚拟DOM]与[旧虚拟DOM]的差异比较,比较规则如下:

2.对比规则:

(1) .旧虚拟DOM中找到了与新虚拟DOM相同的key: 

①.若虛拟DOM中内容没变,直接使用之前的真实DOM!

②.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2) .旧虚拟DOM中未找到与新虚拟DOM相同的key

创建新的真实DOM,随后渲染到到页面。

3.用index作为key可能会引发的问题:

1.若对数据进行:逆序添加、逆序删除等破坏顺序操作:

会产生没有必要的真实DOM更新==>界面效果没问题,但效率低。

2.如果结构中还包含输入类的DOM:

会产生错误DOM更新==>界面有问题。

4.开发中如何选择key?:

1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。

2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有间题的。


🌟✍🏻列表渲染(模糊搜索-列表过滤)

placeholder 可以用来描述输入字段预期值的简短的提示信息

实现思路:

①收集用户的输入

(双向绑定 - v-model

watch收集改变的数值

②匹配数据

//#region 

//#enregion 

讲注释折叠

1️⃣用watch实现;

2️⃣用computed实现

  • 给按钮点击添加数据
  • computed中添加是否排序的判断条件
  • 原顺序会把sortType改为0,0为false,直接返回persons里原来的顺序
  • 节点复用率

splice(替换元素的序号,替换长度(1个元素是1,2个是2..),'替换的内容')

  • push 最后新增一个元素
  • pop 最后删除一个元素
  • shift 删除第一个
  • unshift 在第一个前添加
  • splice 数组指定位置 插入/删除/替换 一个元素
  • sort 排序数组
  • reverse 反转数组


Vue监测数据的原理

 Object.defineProperty(obj,property,descriptor)

参数一:obj

绑定属性的目标对象

参数二:property

绑定的属性名

参数三:descriptor

属性描述(配置),且此参数本身为一个对象

  • 属性值1:value

设置属性默认值

  • 属性值2:writable

设置属性是否能够修改

  • 属性值3:enumerable

设置属性是否可以枚举,即是否允许遍历

  • 属性值4:configurable

设置属性是否可以删除或编辑

  • 属性值5:get

获取属性的值

  • 属性值6:set

设置属性的值

因为不是响应式数据,set可以添加为响应式数据

  • arr.push === Array . prototype .push true
  • vm._data. student. hobby push === Array . prototype. push ❌false

修改列表案例:

  1. 年龄+1岁 Vue.set; this.$set
  2. 添加性别属性,默认值:男
  3. 在列表首位添加一个朋友 unshift
  4. 修改第一个朋友的名字为:张三 对象.属性
  5. 添加一个爱好 push
  6. 修改第一个爱好为: 开车 splice,Vue. set

⭕注意好数组,字符串基本类型,以及对象 要修改对象.属性

(否则VUE中就没有get()以及set()


🌟Vue监视数据的原理:

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

2.如何监测对象中的数据? 

通过setter实现监视,且要在new Vue时就传入要监测的数据。

(1).对象中后追加的属性,Vue默认不做响应式处理

(2) .如需给后添加的属性做响应式,请使用如下API:

Vue.set(target, propertyName/ index, value) 或

vm. $set(target, propertyName/ index, value)

3.如何监测数组中的数据?

通过包裹数组更新元素的方法实现,本质就是做了两件事:

(1).调用原生对应的方法对数组进行更新。

(2).重新解析模板,进而更新页面。

4.在Vue修改数组中的某个元素一定要用如下方法:

1.使用这些API: push( )、pop( )、 shift( )、 unshift( )、 splice( )、 sort()、reverse( )

2.Vue.set( )或vm.$set( )

特别注意: Vue.set( )vm. $set( )不能给vm或vm的根数据对象(即vm和vm._data)添加属性! ! ! 


✍🏻案例: 收集表单数据

实现思路:

  • 若: <input type="text"/>, 则v- model收集的是value值,用户输入的就是value值。
  • 若: <input type="radio"/>, 则v- model收集的是value值,且要给标签配置value值。
  • 若: <input type=" checkbox"/>
    • 1.没有配置input的value属性,那么收集的就是checked (勾选or未勾选,是布尔值)
    • 2.配置input的value属性:
      • (1)v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,是布尔值)
      • (2 )v-model的初始值是数组,那么收集的的就是value组成的数组
  • 备注: v-model的三个修饰符:
    • lazy:失去焦点再收集数据
    • number:输入字符串转为有效的数字
    • trim:输入首尾空格过滤
<form @submit.prevent="demo"> //阻止刷新


过滤器

定义:

对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。

语法: 

1.注册过滤器: Vue. filter(name ,callback)或new Vue{filters:{}}

2.使用过滤器: {{ xxx | 过滤器名}} 或v-bind:属性 = "xxx | 过滤 器名"

备注:

1.过滤器也可以接收额外参数、多个过滤器也可以串联;

2.并没有改变原本的数据,是产生新的对应的数据。


指令

1️⃣内置指令

  • v-bind:单向绑定解析表达式,可简写为:XX
  • v-model:双向数据绑定
  • v- for:遍历数组/对象/字符串
  • v-on :绑定事件监听,可简写为@
  • v-if:条件渲染(动态控制节点是否存存在)
  • v-else:条件渲染(动态控制节点是否存存在)
  • v-show:条件渲染(动态控制节点是否展示)

①v-text

1.作用:向其所在的节点中渲染文本内容。

2.与插值语法的区别: v-text 会替换掉节点中的内容, {{xx}} 则不会。

cookie的工作原理

②v-html指令:

1.作用: 向指定节点中渲染包含html结构的内容。

2.与插值语法的区别:

(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。

(2) .v-html可以识别html结构。

3.严重注意: v-html有安全性问题! ! ! !

(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。

(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!

③v-cloak指令 (没有值) :

1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。

2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。

④v-once指令:

1.v-once所在节点在初次动态渲染后,就视为静态内容了。

2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

⑤v-pre指令: 

1.跳过其所在节点的编译过程。

2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。


2️⃣自定义指令

需求1: 定义一个 v-big 指令,和 v-text 功能类似, 但会把绑定的数值放大10倍。

directives :{
    //big函数何时会被调用? 
    //1.指令与元素成功绑定时(一上来)。
    //2. 指令所在的模板被重新解析时。
    big( element,binding){  //函数式
        //console.log('big' )
        element. innerText = binding.value * 10 
    }
}

需求2: 定义一个v-fbind指令, 和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。

//对象式
fbind:{
    //指令与元素成功绑定时(一上来)
    bind(element, binding){
        element.value = binding.value
        //注意此处的this是window
    },
    //指令所在元素被插入页面时
    inserted( element,binding){
        element.focus()
        //获取焦点
    },
    //指令所在的模板被重新解析时
    update (element , binding){ 
      element.value = binding.value 
    }
}

⭕定义全局指令

自定义指令总结:

一、定义语法:

(1).局部指令:

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

(2).全局指令:

Vue.directive(指令名,配置对象)

或 Vue.directive(指令名,回调函数)

二、配置对象中常用的3个回调:

(1).bind: 指令与元素成功绑定时调用。

(2).inserted: 指令所在元素被插入页面时调用。

(3).update: 指令所在模板结构被重新解析时调用。

三、备注:

1.指令定义时不加v-,但使用时要加v-;

2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。


生命周期

⭕同名可以对象的简写形式

//Vue完成模板的解析并把真实的DOM元素放入页面后(挂载完毕)调用mounted

mounted( ){ }

生命周期:

1.又名: 生命周期回调函数、生命周期函数、生命周期钩子。

2.是什么: Vue在关键时刻帮我们调用的一些特殊名称的函数。

3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。

4.生命周期函数中的this指向是vm 或 组件实例对象。

 缺少nextTick、activated、deactivated

挂载流程;

更新流程;

clearInterval( )函数是在JavaScript中用于取消setInterval()函数设定的定时执行操作

this . $destroy( )

常用的生命周期钩子:

1. mounted:发送ajax请求、 启动定时器、绑定自定义事件、订阅消息等[初始化操作]。

2. beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。

关于销毁Vue实例

1.销毁后借助Vue开发者工具看不到任何信息。

2.销毁后自定义事件会失效,但原生DOM事件依然有效。

3.一般不会再beforeDestroy操 作数据, 因为即便操作数据, 也不会再触发更新流程了。


组件化编程

组件的定义:实现应用中局部功能代码(css、html、js)和资源(zip、mp4)的集合

模块:

1. 理解:向外提供特定功能的js程序,一般就是一个js文件

2.为什么: js 文件很多很复杂

3. 作用:复用js,简化js的编写,提高js运行效率

组件

1.理解:用来实现局部(特定)功能效果的代码集合(html/css/js/image.....)

2.为什么:一个界面的功能很复杂

3.作用: 复用编码,简化项目编码,提高运行效率

模块化

当应用中的is都以模块来编写的,那这个应用就是一个模块化的应用。

组件化

组件化的应用当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用。

非单文件组件:

一个文件中包含有n个组件。

单文件组件:

一个文件中只包含有1个组件。

Vue中使用组件的三大步骤:

一、定义组件(创建组件》

二、注册组件

三、使用组件(写组件标签)

一、如何定义一个组件?

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别

区别如下:

1.el不要写,为什么?- 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。

2.data必须写成函数,为什么? - 避免组件被复用时,数据存在引用关系。

备注:使用template可以配置组件结构。

二、如何注册组件?

1.局部注册: 靠new Vue的时候传入components选项

2.全局注册: 靠Vue.component('组件名' ,组件)

三、编写组件标签:

<school></school>

⭕注意点:

1.关于组件名:

一个单词组成:

第一种写法(首字母小写): school

第二种写法(首字母大写): School

多个单词组成:

第一种写法(kebab-case命名): my-school

第二种写法(Camelcase命名): MySchoo (需要Vue脚手架支持)

备注:

(1).组件名尽可能回避HTML中已有的元素名称,例如: h2、H2都不行。

(2).可以使用name配置项指定组件在开发者工具中呈现的名字。

2.关于组件标签:

第一种写法:<school></school>

第二种写法:<school/>

备注: 不用使用脚手架时,<school/>会导致后续组件不能渲染

3.一个简写方式:

const school = Vue.extend(options) 可简写为: const school = options


VueComponent:

1.school组件本质是一个名头VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。

2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的: new VueComponent(options)

3.特别注意: 每次调用Vue.extend,返回的都是一个全新的VueComponent! ! ! !

4.关于this指向:

(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是[VueComponent实例对象]

(2).new Vue( options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是[Vue实例对象]

5.VueComponent的实例对象,以后简称vc (也可称之为: 组件实例对象)。

Vue的实例对象,以后简称vm。

1.一个重要的内置关系: VueComponent.prototype._proto

=== Vue .prototype

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

实例的隐式原型对象永运指向缔造者的原型对象

 单文件组件

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

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

<style>
*组件的样式 */
</style>


Vue脚手架

关于不同版本的Vue:

1.vue.js与vue.runtime.xxx.js的区别:

(1).vue.js是完整版的Vue,包含: 核心功能+模板解析器。

(2).vue.runtime.xxx.js是运行版的Vue,只包含: 核心功能;没有模板解析器.

2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

配置参考

lintOnSave:false //关闭语法检查

## 脚手架文件结构

   ├── node_modules

   ├── public

   │   ├── favicon.ico: 页签图标

   │   └── index.html: 主页面

   ├── src

   │   ├── assets: 存放静态资源

   │   │   └── logo.png

   │   │── component: 存放组件

   │   │   └── HelloWorld.vue

   │   │── App.vue: 汇总所有组件

   │   │── main.js: 入口文件

   ├── .gitignore: git版本管制忽略的配置

   ├── babel.config.js: babel的配置文件

   ├── package.json: 应用包配置文件

   ├── README.md: 应用描述文件

   ├── package-lock.json:包版本控制文件

ref属性

1.被用来给元素或子组件注册引用信息 (id的替代者)

2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

3.使用方式:

打标识: <h1 ref="xxx">.....</h1> 或 <School ref="xxx"x/school>

获取: this.$refs.xxx

v-bind 得到是表达式 ——> 数据处理(年龄+1)

props配置

①//简单声明接收// props :[ 'name' , ' age' , ' sex']

②//接收的同时对数据进行类型限制
props:{
    name:String,
    age : Number,
    sex:String
}

③//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:(
    name:{
        type:string,//name的类型是字符串
        required:true,//name是必要的
    },
    age:{
        type: Number
        default:99 //默认值
    },
    sex:{
        type:String,
        required:true
    }
}

props配置项

  1. 功能:让组件接收外部传过来的数据
  2. 传递数据:<Demo name="xxx"/>

接收数据:

  1. 第一种方式(只接收):props:['name']
  2. 第二种方式(限制类型):props:{name:String}

第三种方式(限制类型、限制必要性、指定默认值):

props:{
    name:{
    type:String, //类型
    required:true, //必要性
    default:'老王' //默认值
    }
}

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

mixin(混入)

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

使用方式:

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

第二步使用混入:

  1. 全局混入:Vue.mixin(xxx)
  2. 局部混入:mixins:['xxx']

插件

  1. 功能:用于增强Vue
  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

定义插件:

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

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

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

scoped样式

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

✍🏻Todo-list 案例

组件化编码流程(通用)

1.实现静态组件: 抽取组件,使用组件实现静态页面效果

2.展示动态数据:

2.1数据的类型、名称是什么?

2.2数据保存在哪个组件?“

3.交互——从绑定事件监听开始

缩进快捷键:选中代码->tap:往后缩  shift+tap:往前进

Vue的混合(也叫做混入 定义:多个组件的共同相等的配置属性项放入一个混合对象的Js文件中。它分为全局混合和局部混合。混合也相当于融入,对原数据不会发生改变,对生命周期“会混合中的提前调用,组件在后)

⭕注意:当我们使用porps配置属性项接收到的外部数据会主动直接存放到当前的this(vc或者vm身上),并且我们要防止data配置属性项里面的数据发生重名冲突,还要注意我们接收的数据是没有办法直接修改

event.target.value 收集文本框输入的文字

v-model 双向数据绑定

拿到之后清空 e.target.value = "";

add会和vc里面的同名

<label> 标签为 input 元素定义标签(label)。

label 元素不会向用户呈现任何特殊的样式。不过,它为鼠标用户改善了可用性,因为如果用户点击 label 元素内的文本,则会切换到控件本身。

🌟APP定义函数从而能让Header能够调用,传入数值

即APP--->Header,Header——>APP

然后APP拿到传入的数值传给List:APP——>List

暂时做不到Header——>List

App.vue中

<MyList :todos="todos" />

List.vue中

<MyItem

v-for="todoObj in todos"

      :key="todoObj.id"

      :todo="todoObj"

/>

props:['todos']

Item.vue中

props['todo']

<input
      type="text"
      placeholder="请输入你的任务名称,按回车键确认"
      v-model="title"
      @keyup.enter="add"
/>
//如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props
<input type="checkbox" v-model="todo.done"/>

reducer 逐个遍历数组元素,每一步都将当前元素的值与上一步的计算结果相加(上一步的计算结果是当前元素之前所有元素的总和)——直到没有更多的元素被相加。

 doneTotal(){
      return this.todos.reduce((pre, current) => {
        return pre+(current.done ? 1 :0)
      },0)
    =
    return this.todos.reduce((pre,current)=> pre+(current.done ? 1 : 0),0)

 }

统计初始值为0,数值长度多少就会调用几次

收到的参数

  • pre为上一次调用函数的返回值
  • curren为当前的值

一个计算属性可以通过多个计算属性再进行计算

filter并不影响原数组

总结TodoList案例

1.组件化编码流程:

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

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

​ 1).一个组件在用:放在组件自身即可。

​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

(3).实现交互:从绑定事件开始。

2.props适用于:

​ (1).父组件 ==> 子组件 通信 (标签属性、ref属性)

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

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

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

JSON.stringify

JSON.stringify 方法将某个对象转换成 JSON 字符串形式

JSON.parse( ) 方法将数据转换为 JavaScript 对象,用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换 (操作)

setItem、getItem、removeItem、clear


webStorage

  1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

3.相关API:

1.xxxxxStorage.setItem('key', 'value');

该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

2.xxxxxStorage.getItem('person');

该方法接受一个键名作为参数,返回键名对应的值。

3.xxxxxStorage.removeItem('key');

该方法接受一个键名作为参数,并把该键名从存储中删除。

4.xxxxxStorage.clear()

该方法会清空存储中的所有数据。

4.备注:

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

空数组没东西却有length属性

第一层监视:数组有无变化——>深度监测(不能简写)


组件自定义事件

v-on 缩写形式:@

v-on在student的组件标签上,所以说是给student的组件对象vue component(VC)身上绑定了一个事件。如果触发了事件,函数就会被调用。

 this.$emit( ) 主要用于子组件像父组件传值。

...params 扩展运算符 ——> 展开为数组

<!--通过父组件给子组件传递函数类型的props实现: 子给父传递数据 -->
<School :getSchoolName="getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现: 子给父传递数据(第一种写法,使用@或v-on) -->
<Student @atguigu.once="getStudentName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现: 子给父传递数据(第二种写法,使用ref)-->
<!-- <Student ref="student"/> -->

$once 一次性

this.$off(' ') //解绑一个自定义事件

// this.$off(['atguigu',demo']) //解绑多个自定义事件this.$off() //解绑所有的自定义事件

this.$destroy( )

//销毁了当前student组件的实例vc,销毁后所有student实例的自定义事件全都不奏效

箭头函数this指向函数定义时的对象

@click.native 在组件中声明这是原生的事件

  • 一种组件间通信的方式,适用于:子组件 ===> 父组件
  • 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

3.绑定自定义事件:

第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

第二种方式,在父组件中:

<Demo ref="demo"/>
......
mounted(){
   this.$refs.xxx.$on('atguigu',this.test)
}
  • 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

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

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

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

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


全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信
  2. 安装全局事件总线:
   new Vue({
       ......
       beforeCreate() {
           Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
       },
       ......
   }) 

3.使用事件总线:

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

  methods(){
    demo(data){......}
  }
  ......
  mounted() {
    this.$bus.$on('xxxx',this.demo)
  }

 提供数据:this.$bus.$emit('xxxx',数据)

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

source: "<Root>" ——>全局事件总线


消息订阅与发布(pubsub) 

  1. 一种组件间通信的方式,适用于任意组件间通信。
  2. 使用步骤:
    1. 安装pubsub:npm i pubsub-js
    2. 引入: import pubsub from 'pubsub-js'
    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
    4.   methods(){
          demo(data){......}
        }
        ......
        mounted() {
          this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
        }
      

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

      5.最好在 beforeDestroy 钩子中,用PubSub.unsubscribe(pid)去取消订阅。

即在组件中追加的数据需要this.$set(todo,'isEdit',true)

因为vue没有给geter seter,所以没有数据监视

@blur 是当元素失去焦点时所触发的事件

把 todo.hasOwnProperty('isEdit') 修改成 Object.hasOwnProperty.call(todo,'isEdit')


$nextTick 

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

动画效果

Vue封装的过度与动画

  1. 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
  2. 图示:
  1. 写法:
  2. 准备好样式:
    1. 元素进入的样式:
      1. v-enter:进入的起点
      2. v-enter-active(能简单实现动画):进入过程中
      3. v-enter-to:进入的终点

              2.元素离开的样式:

  1. v-leave:离开的起点
  2. v-leave-to:离开的终点
  3. v-leave-active:离开过程中
  4. 使用<transition>包裹要过渡的元素,并配置name属性:
  <transition name="hello">
      <h1 v-show="isShow">你好啊!</h1>
  </transition>

3.备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。


Vue脚手架配置代理

方法一

​在vue.config.js中添加如下配置:

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

 说明:

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

方法二

​编写vue.config.js配置具体代理规则:

module.exports = {
    devServer: {
      proxy: {
      '/api1': {// 匹配所有以 '/api1'开头的请求路径
        target: 'http://localhost:5000',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api1': ''}
      },
      '/api2': {// 匹配所有以 '/api2'开头的请求路径
        target: 'http://localhost:5001',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api2': ''}
      }
    }
  }
}
/*
   changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
   changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
   changeOrigin默认值为true
*/

说明:

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

✍🏻案例:用户搜索

模板字符串 ES6

不能直接操作_data,因为会没有数据代理(getter,setter)

vue-resource


slot 插槽

解构赋值

插槽

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件
  2. 分类:默认插槽、具名插槽、作用域插槽
  3. 使用方式:
    1. 默认插槽:
        父组件中:
                <Category>
                   <div>html结构1</div>
                </Category>
        子组件中:
                <template>
                    <div>
                       <!-- 定义插槽 -->
                       <slot>插槽默认内容...</slot>
                    </div>
                </template>

    2. 具名插槽:
        父组件中:
                <Category>
                    <template slot="center">
                      <div>html结构1</div>
                    </template>
               <template v-slot:footer>
                  <div>html结构2&lt;/div>
               </template>
           </Category>
        子组件中:
                <template>
                    <div>
                       <!-- 定义插槽 -->
                       <slot name="center">插槽默认内容...</slot>
                       <slot name="footer">插槽默认内容...</slot>
                    </div>
                </template>
      

    3. 作用域插槽:

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

      2. 具体编码:
         父组件中:
                 <Category>
                     <template scope="scopeData">
                         <!-- 生成的是ul列表 -->
                         <ul>
                             <li v-for="g in scopeData.games" :key="g">{{g}}</li>
                         </ul>
                     </template>
                 </Category>
        
        
             <Category>;
                 <template slot-scope="scopeData">
                    <!-- 生成的是h4标题 -->
                    <h4 v-for="g in scopeData.games" :key="g">{{g}}&lt;/h4>
                </template>
            </Category>
        
        子组件中:
                 <template>
                     <div>
                         <slot :games="games"></slot>
                     </div>
                 </template>
        
             <script>;
                 export default {
                     name:'Category',
                     props:['title'],
                     //数据在子组件自身
                     data() {
                         return {
                             games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                         }
                     },
                 }
            </script>
        


Vuex

1.概念:

专门在Vue中实现集中式状态(数据)管理的一个Vue插件。对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

$emit 配置全局事件总线,是任意组件之间传值

 

 什么时候使用 Vuex

1.多个组件依赖于同一状态

2.来自不同组件的行为需要变更同一状态


✍🏻案例 :Vue版求和

 v-bind - 引号里的值当成 JS 表达式解析

v-model.number 强制类型转换

 何时使用?

​ 多个组件需要共享数据时


2.搭建vuex环境

  1. 创建文件:src/store/index.js
       //引入Vue核心库
       import Vue from 'vue'
       //引入Vuex
       import Vuex from 'vuex'
       //应用Vuex插件
       Vue.use(Vuex)
    
       //准备actions对象——响应组件中用户的动作
       const actions = {}
       //准备mutations对象——修改state中的数据
       const mutations = {}
       //准备state对象——保存具体的数据
       const state = {}
    
       //创建并暴露store
       export default new Vuex.Store({
           actions,
           mutations,
           state
       })
    

  2. 在  main.js 中创建vm时传入 store 配置项
       ......
       //引入store
       import store from './store'
       ......
    
       //创建vm
       new Vue({
           el:'#app',
           render: h => h(App),
           store
       })
    


3.基本使用

  1. 初始化数据、配置 actions、配置 mutations,操作文件 store.js
   //引入Vue核心库
   import Vue from 'vue'
   //引入Vuex
   import Vuex from 'vuex'
   //引用Vuex
   Vue.use(Vuex)

   const actions = {
       //响应组件中加的动作
       jia(context,value){
           // console.log('actions中的jia被调用了',miniStore,value)
           context.commit('JIA',value)
       },
   }

   const mutations = {
       //执行加
       JIA(state,value){
           // console.log('mutations中的JIA被调用了',state,value)
           state.sum += value
       }
   }
  
   //初始化数据
   const state = {
      sum:0
   }

   //创建并暴露store
   export default new Vuex.Store({
       actions,
       mutations,
       state,
   })
  1. 组件中读取vuex中的数据:$store.state.sum
  2. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit 

state 存储数据源(类似vue中的date),getters可以将数据源进行加工(类似computed) 


4.getters的使用 

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。(计算属性:getters   区别:能否跨组件)
  2. store.js中追加getters配置
    ......
    
       const getters = {
           bigSum(state){
               return state.sum * 10
           }
       }
    
       //创建并暴露store
       export default new Vuex.Store({
           ......
           getters
       })
  3. 组件中读取数据:$store.getters.bigSum

扩展运算符,用于取出参数对象的所有可以遍历属性,并将其复制到当前对象之中

const obj2 = {
  a:1,
  b:2,
  c:3,
  d:4
}

const obj3 = {...obj2}

 5.四个map方法的使用

  • mapState方法:用于帮助我们映射state中的数据为计算属性(数组写法:生成计算属性名需要与读取的变量名一致)
      computed: {
           //借助mapState生成计算属性:sum、school、subject(对象写法)
            ...mapState({sum:'sum',school:'school',subject:'subject'}),
    
           //借助mapState生成计算属性:sum、school、subject(数组写法)
           ...mapState(['sum','school','subject']),
       },
  • mapGetters方法:用于帮助我们映射getters中的数据为计算属性
       computed: {
           //借助mapGetters生成计算属性:bigSum(对象写法)
           ...mapGetters({bigSum:'bigSum'}),
    
           //借助mapGetters生成计算属性:bigSum(数组写法)
           ...mapGetters(['bigSum'])
       },
    
  • mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数
       methods:{
           //靠mapActions生成:incrementOdd、incrementWait(对象形式)
           ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
           //靠mapActions生成:incrementOdd、incrementWait(数组形式)
           ...mapActions(['jiaOdd','jiaWait'])
       }
    
  • mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数
     methods:{
           //靠mapActions生成:increment、decrement(对象形式)
           ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    
           //靠mapMutations生成:JIA、JIAN(对象形式)
           ...mapMutations(['JIA','JIAN']),
       }

 备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

addpersonWang(context,value){
    if(value.name .index0f('王') === 0)(
        context.commit('ADD_PERSON',value)
    }
}

或者是value.name.startsWith('王')



const countOptions = {
    namespaced;true //命名空间
}

6.模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。
  2. 修改store.js
       const countAbout = {
         namespaced:true,//开启命名空间
         state:{x:1},
         mutations: { ... },
         actions: { ... },
         getters: {
           bigSum(state){
              return state.sum * 10
           }
         }
       }
    
       const personAbout = {
         namespaced:true,//开启命名空间
         state:{ ... },
         mutations: { ... },
         actions: { ... }
       }
    
       const store = new Vuex.Store({
         modules: {
           countAbout,
           personAbout
         }
       })
    

  • 开启命名空间后,组件中读取state数据:
     //方式一:自己直接读取
     this.$store.state.personAbout.list
     //方式二:借助mapState读取:
     ...mapState('countAbout',['sum','school','subject']),
  • 开启命名空间后,组件中读取getters数据:
       //方式一:自己直接读取
       this.$store.getters['personAbout/firstPersonName']
       //方式二:借助mapGetters读取:
       ...mapGetters('countAbout',['bigSum'])
    
  • 开启命名空间后,组件中调用dispatch
       //方式一:自己直接dispatch
       this.$store.dispatch('personAbout/addPersonWang',person)
       //方式二:借助mapActions:
       ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  • 开启命名空间后,组件中调用commit
       //方式一:自己直接commit
       this.$store.commit('personAbout/ADD_PERSON',person)
       //方式二:借助mapMutations:
       ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
    


 路由 vue-router

 

Vue-router 的理解

Vue的一个插件库,专门用来实现SPA应用。

对SPA应用的理解

1.单页Web 应用(single page web application,SPA)。

2.整个应用只有一个完整的页面。

3.点击页面中的导航链接不会刷新页面,只会做页面的局部更新

4.数据需要通过ajax请求获取。

什么是路由?

1.一个路由就是一组映射关系(key - value)

2. key为路径 value可能是 function 或 componente


路由分类

1.后端路由(服务器):

(1)理解: value是function,用于处理客户端提交的请求。

(2)工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。

2.前端路由:

(1) 理解: value是component,用于展示页面内容。

(2)工作过程:当浏览器的路径改变时,对应的组件就会显示。


✍🏻案例: 单页面跳转

  1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router
  2. 应用插件:Vue.use(VueRouter)
  3. 编写router配置项:
      //引入VueRouter
      import VueRouter from 'vue-router'
      //引入Luyou 组件
      import About from '../components/About'
      import Home from '../components/Home'
    
      //创建router实例对象,去管理一组一组的路由规则
      const router = new VueRouter({
          routes:[
              {
                  path:'/about',
                  component:About
              },
              {
                  path:'/home',
                  component:Home
              }
          ]
      })
    
      //暴露router
      export default router
    
  4. 实现切换(active-class可配置高亮样式)
     <router-link active-class="active" to="/about">About</router-link>
  5. 指定展示位置
      <router-view></router-view>

⭕注意点:

  1. 路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。


✍🏻案例:嵌套(多级)路由

 

 Shift+tab 代码往前缩进


2.多级路由(多级路由)

  • 配置路由规则,使用children配置项:
   routes:[
       {
           path:'/about',
           component:About,
       },
       {
           path:'/home',
           component:Home,
           children:[ //通过children配置子级路由
               {
                   path:'news', //此处一定不要写:/news
                   component:News
               },
               {
                   path:'message',//此处一定不要写:/message
                   component:Message
               }
           ]
       }
   ]
  • 跳转(要写完整路径):
   <router-link to="/home/news">News</router-link>


路由传参

v-bind 主要用于属性绑定,简写为:

先将其转换为JS语句,然后用模板字符串(混着字符串变量即需要${ })


3.路由的query参数

  • 传递参数
       <!-- 跳转并携带query参数,to的字符串写法 -->
       <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    
       <!-- 跳转并携带query参数,to的对象写法 -->
       <router-link 
           :to="{
               path:'/home/message/detail',
               query:{
                  id:666,
                   title:'你好'
               }
           }"
       >跳转</router-link>
    
  • 接收参数:
       $route.query.id
       $route.query.title

4.命名路由

  1. 作用:可以简化路由的跳转。
  2. 如何使用
    1. 给路由命名:
        {
            path:'/demo',
            component:Demo,
            children:[
                {
                    path:'test',
                    component:Test,
                    children:[
                        {
                            name:'hello' //给路由命名
                            path:'welcome',
                            component:Hello,
                        }
                    ]
                }
            ]
        }
      
    2. 简化跳转:

        <!--简化前,需要写完整的路径 -->
        <router-link to="/demo/test/welcome">跳转</router-link>
      
        <!--简化后,直接通过名字跳转 -->
        <router-link :to="{name:'hello'}">跳转</router-link>
      
        <!--简化写法配合传递参数 -->
        <router-link 
            :to="{
                name:'hello',
                query:{
                   id:666,
                    title:'你好'
                }
            }"
        >跳转</router-link>
      

5.路由的 params 参数

  • 配置路由,声明接收params参数
       {
           path:'/home',
           component:Home,
           children:[
               {
                   path:'news',
                   component:News
               },
               {
                   component:Message,
                   children:[
                       {
                           name:'xiangqing',
                           path:'detail/:id/:title', //使用占位符声明接收params参数
                           component:Detail
                       }
                   ]
               }
           ]
       }
    
  • 传递参数
       <!-- 跳转并携带params参数,to的字符串写法 -->
       <router-link :to="/home/message/detail/666/你好">跳转</router-link>
    
       <!-- 跳转并携带params参数,to的对象写法 -->
       <router-link 
           :to="{
               name:'xiangqing',
               params:{
                  id:666,
                   title:'你好'
               }
           }"
       >跳转</router-link>
    

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  • 接收参数:
   $route.params.id
   $route.params.title


6.路由的props配置

作用:让路由组件更方便的收到参数 

{
    name:'xiangqing',
    path:'detail/:id',
    component:Detail,

    //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
    // props:{a:900}

    //🌟第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
    // props:true

    //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
    props(route){
        return {
            id:route.query.id,
            title:route.query.title
        }
    }
}


7.<router-link>的 replace 属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

8.编程式路由导航

  1. 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活
  2. 具体编码:
       //$router的两个API
       this.$router.push({
           name:'xiangqing',
               params:{
                   id:xxx,
                   title:xxx
               }
       })
    
       this.$router.replace({
           name:'xiangqing',
               params:{
                   id:xxx,
                   title:xxx
               }
       })
       this.$router.forward() //前进
       this.$router.back() //后退
       this.$router.go() //可前进也可后退
    

 9.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。
  2. 具体编码:
     <keep-alive include="News"> 
          <router-view></router-view>
      </keep-alive>


10.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。


11.🌟路由守卫

  1. 作用:对路由进行权限控制
  2. 分类:全局守卫、独享守卫、组件内守卫

1️⃣ 全局守卫:

   //全局前置守卫:初始化时执行、每次路由切换前执行
   router.beforeEach((to,from,next)=>{
       console.log('beforeEach',to,from)
       if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
           if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
               next() //放行
           }else{
               alert('暂无权限查看')
               // next({name:'guanyu'})
           }
       }else{
           next() //放行
       }
   })

   //全局后置守卫:初始化时执行、每次路由切换后执行
   router.afterEach((to,from)=>{
       console.log('afterEach',to,from)
       if(to.meta.title){ 
           document.title = to.meta.title //修改网页的title
       }else{
           document.title = 'vue_test'
       }
   })

全局守卫会在路由切换的整个流程中被调用,包括导航离开当前页面、导航到达目标页面、以及导航被终止等情况。Vue Router 提供了三个全局守卫:beforeEach、beforeResolve 和 afterEach。

  • beforeEach(to, from, next):在导航离开当前页面时触发,可以通过调用 next 函数来允许或中止导航。
  • beforeResolve(to, from, next):在导航被确认之前,同时在所有组件内部守卫和异步路由组件被解析之后触发。
  • afterEach(to, from):在导航完成后被触发,此时可以执行一些数据的清理操作等。

2️⃣独享守卫:

   beforeEnter(to,from,next){
       console.log('beforeEnter',to,from)
       if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
           if(localStorage.getItem('school') === 'atguigu'){
               next()
           }else{
               alert('暂无权限查看')
               // next({name:'guanyu'})
           }
       }else{
           next()
       }
   }

独享守卫是针对单个路由配置的守卫函数,它们会在路由被激活时按照顺序执行。Vue Router 提供了两个独享守卫:beforeEnter 和 beforeRouteLeave。

  • beforeEnter(to, from, next):在路由进入前被调用,可以通过调用 next 函数来允许或中止导航。
  • beforeRouteLeave(to, from, next):在路由离开前被调用,可以通过调用 next 函数来允许或中止导航。

3️⃣组件内守卫:

   //进入守卫:通过路由规则,进入该组件时被调用
   beforeRouteEnter (to, from, next) {
   },
   //离开守卫:通过路由规则,离开该组件时被调用
   beforeRouteLeave (to, from, next) {
   }

组件内守卫可以对当前组件进行深度定制的导航守卫。Vue Router 提供了四个组件内守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 和 scrollBehavior。

  • beforeRouteEnter(to, from, next):在路由进入前被调用,但是当前组件实例还没被创建,因此不能访问 this 对象。可以通过传递一个回调函数作为参数,该回调函数接收一个组件实例作为参数,来访问组件中的数据。
  • beforeRouteLeave(to, from, next):在路由离开前被调用,可以通过调用 next 方法来允许或中止导航。
  • beforeRouteUpdate(to, from, next):在当前路由改变而该组件被复用(例如,带有动态参数的路由)时被调用。可以通过调用 next 方法来允许或中止导航。
  • scrollBehavior(to, from, savedPosition):用于修改页面滚动行为,默认情况下 Vue Router 会尝试对页面滚动进行恢复,如果要修改这个行为可以通过 scrollBehavior 方法来实现。

📜全局守卫、独享守卫和组件内守卫应用场景

1️⃣全局守卫
全局守卫适用于需要控制整个页面访问过程的场景,例如判断用户是否登录或者是否具有相应权限,如果没有登录或没有权限,则可以使用 next(false) 或者 next('/login') 等方法来中止导航,重定向到指定页面。

2️⃣独享守卫
独享守卫适用于特定路由的访问控制,例如需要根据用户角色或权限来确认是否允许进入某个路由。在 beforeEnter 中可以进行相关判断,如果不满足条件,则可以中止导航,重定向到指定页面。

3️⃣组件内守卫
组件内守卫用于控制当前组件的访问过程,例如需要获取异步数据时,在 beforeRouteEnter 中可以执行相应的异步请求操作,然后通过回调函数返回数据,从而确保该组件内的数据已经加载完成才允许访问;或者在 beforeRouteLeave 中进行一些数据的清理操作等。

另外,还有一些常见的应用场景,如:

  • 在 beforeEach 中可以对全局路由进行处理,例如路由拦截器。
  • 在 afterEach 中可以对页面进行埋点统计等操作。
  • 在 beforeRouteUpdate 中可以根据查询参数的变化更新组件内的数据。

12.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. 1️⃣hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. 2️⃣history模式:
    1. 地址干净,美观 。
    2. 兼容性和 hash 模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

⭕UI组件库

移动端常用 UI组件库

 PC端常用UI组件库

Element Ul   一个 Vue 3 UI 框架 | Element Plus

 View Ul   iView / View Design 一套企业级 UI 组件库和前端解决方案


VUE3

 Vue3 简介

 

1.性能的提升

  • 打包大小减少41%
  • 初次渲染快55%, 更新渲染快133%
  • 内存减少54%

2.源码的升级

  • 使用 Proxy 代替 defineProperty 实现响应式
  • 重写 虚拟 DOM 的实现和 Tree-Shaking

3.拥抱TypeScript

  • Vue3可以更好的支持TypeScript

4.新的特性

1️⃣Composition API (组合API)

  • setup配置
  • ref与reactive
  • watch与watchEffect
  • provide与inject

2️⃣新的内置组件

  • Fragment
  • Teleport
  • Suspense

3️⃣其他改变

  • 新的生命周期钩子
  • data 选项应始终被声明为一个函数
  • 移除 keyCod e支持作为 v-on 的修饰符

 


Vite

优势:

  • 开发环境中,无需打包操作,可快速的冷启动
  • 轻量快速的热重载(HMR)
  • 真正的按需编译,不再等待整个应用编译完成

 传统构建与vite构建对比图

new Vue({
  el:'#app'
  //指定为哪个容器服务
}

new Vue({
  render:(h)=>(return h(App)}
  //render 解析模板,值是一个函数,收到h参数,h组件需要将外壳组件App传进去
}).$mount('#app')
//精简写
new Vue({
  render:h =>h(App)
}).$mount('#app')
//拆解写为
const vm = new Vue({
  render:h => h(App)
})
vm.$mount('#app')
//main.js 中引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp ] from 'vue'
import App from './App.vue'
//创建应用实例对象-app(类似于之前Vue2中的vm,但app比vm更“轻”-少些属性与方法
const app = createApp(App)

//挂载
app.mount('#app')
<template>
  <!-- App.vue 中 Vue3组件中的模板结构可以没有根标签 div -->
  <img alt="Vue logo" src=" ./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

setup

1.理解:Vue3.0中一个新的配置项,值为一个函数

2.setup是所有Composition API (组合API)“表演的舞台”

3.组件中所用到的:数据data、方法methods等等,均要配置在setup

4.setup函数的两种返回值:

  1. 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注!)
  2. 若返回一个渲染函数: 则可以自定义渲染内容。 (了解)

5.注意点:

1️⃣尽量不要与Vue2x配置混用

  • Vue2.x配置 (data、methos、computed...) 中可以访问到setup中的属性、方法
  • 但在setup中不能访问到Vue2.x配置 (data、methos、computed...)。
  • 如果有重名,setup优先

2️⃣setup 不能是一个 async 函数,因为返回值不再是 return 的对象,而是 promise, 模板看不到return对象中的属性(后期也可以返回一个 Promise 实例,但需要 Suspense 和异步组件的配合)

<template>
  <h1>一个人的信息</h1>
  <h2>姓名: [[name]}</h2>
  <h2>年龄: {{age]}</h2>
  <button @click="sayHello">说话</button>
</template>

<script>
  import (h} from 'vue'
  export default (
    name:' App',
    //此处只是测试一下setup,暂时不考虑响应式的问题.
    setup(){
    //数据
    let name ='张三'
    let age = 18
    
    //方法
    function sayHello(){
      alert((我叫${name},我${age}岁了,你好啊!)
    }
    
    //返回一个对象(常用)
    return {
      name,
      age,
      sayHello
    }
  }
  //返回一个函数(渲染函数)
  // return ()=> h("h1',尚硅谷')
</script>

 

⭕setup的两个注意点:

(1)setup执行的时机

  • 在beforeCreate 之前执行一次,this 是 undefined.

(2)setup的参数

  • props: 值为对象,包含: 组件外部传递过来,且组件内部声明接收了的属性
  • context:上下文对象
    • atrs:值为对象,包含: 组件外部传递过来,但没有在 props 配置中声明的属性,相当于 this.$attrs
    • slots: 收到的插槽内容,相当于 this.$slots
    • emit: 分发自定义事件的函数,相当于 this.$emit

ref 函数

作用: 定义一个响应式的数据

语法: const xxx = ref(initValue)

  • 创建一个包含响应式数据的引用对象 (reference对象,ref对象)
  • JS中操作数据:xxx.value
  • 模板中读取数据:不需要.value,直接: <div>({xxx}}</div>

备注:

  • 接收的数据可以是: 基本类型、也可以是对象类型
  • 基本类型的数据:响应式依然是靠?ObjectdefineProperty( )的get与set完成的。
  • 对象类型的数据:内部“求助”了Vue3.0中的一个新函数--reactive 函数(Proxy的操作封装在函数中)

 reactive函数

  • 作用: 定义一个对象类型的响应式数据 (基本类型不要用它,要用 ref 函数)
  • 语法: const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象 (proxy的实例对象)
  • reactive定义的响应式数据是“深层次的”
  • 内部基于 ES6的 Proxy 实现,通过代理对象操作源对象内部数据进行操作(数据劫持)

Vue3.0中的响应式原理

1️⃣vue2.x的响应式

①实现原理:

  • 对象类型: 通过 Object.defineProperty( ) 对属性的读取、修改进行拦截(数据劫持)
  • 数组类型: 通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
Object .defineProperty(data,'count',[
  get () { }
  set () { }
})

②存在问题:

  • 新增属性、删除属性,界面不会更新
  • 直接通过下标修改数组,界面不会自动更新

 

2️⃣Vue3.0的响应式

实现原理:

  • 通过Proxy(代理): 拦截对象中任意属性的变化,包括: 属性值的读写、属性的添加、属性的删除等。
  • 通过Reflect (反射):对被代理对象的属性进行操作
new Proxy(data,
// 拦截读取属性值
  get (target, prop) {
    return Reflect.get(target, prop)
  },
  // 拦截设置属性值或添加新属性
  set (target, prop, value) {
    return Reflect.set(target, prop, value)
  },
  // 拦截删除属性
  deleteProperty (target, prop) {
    return Reflect.deleteProperty(target, prop)
  }
})

proxy.name = 'tom'

 


🌟reactive 对比 ref

(1)从定义数据角度对比:

  • ref 用来定义: 基本类型数据
  • reactive 用来定义: 对象 (或数组)类型数据
  • 备注: ref 也可以用来定义对象 (或数组)类型数据,它内部会自动通过 reactive 转为代理对象

(2)从原理角度对比:

  • ref 通过 Object.defineProperty( ) getset 来实现响应式 (数据劫持)。
  • reactive 通过使用 Proxy 来实现响应式 (数据劫持),并通过 Reflect 操作源对象内部的数据。

(3)从使用角度对比:

  • ref 定义的数据:操作数据需要 .value,读取数据时模板中直接读取不需要 .value
  • reactive 定义的数据: 操作数据与读取数据:均不需要 .value

 


🌟计算属性 computed 与 监视 watch

(1)computed函数

  • 与Vue2.x 中 computed 配置功能一致
  • 写法:
    import {computed} from 'vue'
    
    setup(){
      ...
      //计算属性-简写
      let fulIName = computed(()=>{
        return person.firstName + _+ person.lastName
      })//计算属性一完整
      let fulIName = computed({
        get(){
          return person.firstName + '-'+ person.lastName
        },  
        set(value){
          const nameArr = value.split('-')
          person.firstName = nameArr[0l
          person.lastName = nameArr[1]
         }
       })
    }    
    

 (2)watch 函数

  • 与Vue2.x中watch配置功能一致
  • 两个小“坑”:
    • 监视reactive定义的响应式数据时: oldValue无法正确获取、强制开启了深度监视 (deep配置失效)。
    • 监视reactive定义的响应式数据中某个属性时(值为对象): deep配置有效
name: 'Demo',
setup(){
//数据
let sum = ref(0)
let msg = ref('你好啊')
let person = ref({
  name:'张三'
  age:18,
  job:{
    j1:{
      salary:20
    }
  }
})

//情况一: 监视ref所定义的一个响应式数据
watch(sum,newValue,oldValue)=>{
  //immediate:true 立即监视,即开始就会调用一次
  console.log('sum变了' ,newValue,oldValue)
},{immediate:true}) 
//不需要.value,因为监视的是sum的结构

//情况二: 监视ref所定义的多个响应式数据
watch([sum,msg],(newValue,oldValue)=>{
  console.log('sum或msg变',newValue,oldValue)
},(immediate:true}) 

//情况三: 监视reactive所定义的一个响应式数据的全部属性
//1.注意: 此处无法正确的获取oldValue
//2.注意: 强制开启了深度监视 (deep配置无效)
watch(person,(newValue,oldValue)=>{
  console.log('person变化了',newValue,oldValue)
},{deep:false}) //此处的deep配置无效 

//情况四: 监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.age,(newValue,oldValue)=>{
  console.log('person的age变化了',newValue,oldValue)
})

//情况五: 监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name, ()=>person .age],(newValue,oldValue)=>{
  console.log('person的name或age变化了',newValue,oldValue)
}) 

//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
  console.log('person的job变化了' ,newValue,oldValue)
},(deep:true})
//此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效

 


watchEffect函数

  • watch的套路是: 既要指明监视的属性,也要指明监视的回调
  • watchEffect的套路是: 不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
  • watchEffect有点像computed:
    • 但computed注重的计算出来的值 (回调函数的返回值),所以必须要写返回值
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
  const x1 = sum.value
  const x2 = person.age
  console.log('watchEffect配置的回调执行了')
})

生命周期

  • Vue3.0 中可以继续使用 Vue2.x 中的生命周期钩子,但有有两个被更名
    • beforeDestroy 改名为 beforeUnmount
    • destroyed 改名为 unmounted
  • Vue3.0 也提供了 Composition API 形式的生命周期钩子,与 Vue2.x 中钩子对应关系如下:
    • beforeCreate ===> setup()
    • created =======> setup()
    • beforeMount ===>onBeforeMount0
    • mounted =======>onMounted
    • beforeUpdate ===> onBeforeUpdate
    • updated =======> onUpdated
    • beforeUnmount ==>onBeforeUnmount
    • unmounted=====>onUnmounted

自定义hook函数

  • 什么是hook?  本质是一个函数,把 setup 函数 中使用的 Composition API 进行了封装
  • 类似于 Vue2.x 中的 mixin
  • 自定义 hook 的优势: 复用代码,让 setup 中的逻辑更清楚易懂

toRef

  • 作用: 创建一个 ref 对象,其value值指向另一个对象中的某个属性
  • 语法: const name = toRef(person ,'name')
  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefs 与 toRef 功能一致,但可以批量创建多个 ref 对象,语法: toRefs(person)

 其它 Composition API

 1.shallowReactive 与 shallowRef

  • shallowReactive: 只处理对象最外层属性的响应式 (浅响应式)
  • shallowRef: 只处理基本数据类型的响应式,不进行对象的响应式处理.
  • 什么时候使用?
    • 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactive.
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef.

 


2.readonly与shallowReadonly

  • readonly: 让一个响应式数据变为只读的 (只读)
  • shallowReadonly: 让一个响应式数据变为只读的 (只读)
  • 应用场景: 不希望数据被修改时

3.toRaw与markRaw 

  • toRaw:
    • 作用: 将一个由 reactive 生成的响应式对象转为普通对象
    • 使用场景: 用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw:
    • 作用: 标记一个对象,使其永远不会再成为响应式对象
    • 应用场景:
      • 1.有些值不应被设置为响应式的,例如复杂的第三方类库等.
      • 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

4.customRef

作用: 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制

 


5.provide 与 inject

  • 作用: 实现祖孙组件间通信

 

  • 套路: 父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据

响应式数据的判断: 

  • isRef: 检查一个值是否为个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

 Composition API的优势

1.Options API 存在的问题

使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改(拆散了)

2.Composition API的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

 


 

新的组件

1.Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级,减小内存占用

2.Teleport

什么是Teleport 传送?  Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

<teleport to="移动位置">
  <div v-if="isShow" class="mask">
    <div class="dialog">
      <h3>我是一个弹窗</h3>
      <button @click="isShow = false">关闭弹窗</button>
    </div>
  </div>
</teleport>

3.Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
  • 使用步骤:
    • 异步引入组件
      import {defineAsyncComponent] from 'vue'
      const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
      
    • 使用 Suspense 包裹组件,并配置好 default 与 fallback
      <template>
        <div class="app">
          <h3>我是App组件</h3>
          <Suspense>
            <template v-slot:default>
              <Child/>
            </template>
            <template v-slot:fallback>
              <h3>加载中.....</h3></template>
          </Suspense>
        </div>
      </template>
      

 

全局API的转移

  • Vue 2.x有许多全局API和配置
    • 例如:注册全局组件、注册全局指令等
      //注册全局组件
      Vue.component('MyButton',{
        data: () => ({
          count: 0
        }),
        template: '<button @click="count++">Clicked {f count ]) times.</button>'
      })
      
      //注册全局指令
      Vue.directive('focus',{
        inserted: el => el.focus()
      }
      
  • Vue3.0中对这些API做出了调整
    • 将全局的API,即:Vue.xxx调整到应用实例(app)上

2.x全局API(Vue)

3X实例API(app)

Vue.config.xxxx

app.config.xxxx

Vue.config.productionTip

移除

Vue.component

app.component

Vue.directive

app.directive

Vue.mixin

app.mixin

Vue.use

app.use

Vue.prototype

app.config.globalProperties

 

其他改变

  • data选项应始终被声明为一个函数。
  • 过度类名的更改:
    • Vue2.x写法
      .V-enter,
      .v-leave-to {
        opacity:0;
      }
      
      .v-leave,
      .v-enter-to {
        opacity:1;
      }
      
    • Vue3.x写法
      .V-enter-from,
      .v-leave-to {
        opacity:0;
      }
      
      .v-leave-from,
      .v-enter-to {
        opacity:1;
      }
      
  • 移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
  • 移除v-on.native 修饰符
    • 父组件中绑定事件
      <my-component
        v-on:close="handleComponentEvent"
        v-on:click="handleNativeClickEvent"
      />
    • 子组件中声明自定义事件
      <script>
        export default {
          emits:['close']
        }
      </script>

  • 移除过滤器 (filter)

过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器

 

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值