Vue.js

Vue2.js核心

1. Vue的特点

  • 采用 组件化 模式,提高代码复用率、且让代码更好维护
  • 声明式编码,让编码人员无需直接DOM,提高开发效率。(js原生是命令式编码)
  • 使用虚拟DOM+Diff算法,尽量复用DOM节点。
    • 流程:数据—>虚拟DOM—>真实DOM
    • Diff(差异算法),复用DOM节点

2. Vue模板语法有两大类

  • 差值语法:
    • 功能:用于解析标签体内容
    • 写法:{{xxx}},“xxx”是 js表达式,且直接读取data中的所有属性。
  • 指令语法:
    • 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
    • 例如:v-bind:href="xxx"或简写为::href="xxx",“xxx”是 js表达式,且直接读取data中的所有属性。
    • 备注:Vue中有很多的指令,且形式都是:v-???,此时我们只是那v-bind举个例子。

3. Vue中两种数据绑定的方法

  • 单项绑定(v-bind):数据只能从data流向页面。
  • 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。注:
    1. 双向绑定一般是应用在 表单 类元素上(input、select等)
    2. v-model:value可以简写成v-model,因为v-model默认收集的就是value值。

4. datael的两种写法

  • el 有两种写法:

    1. new Vue时配置el属性
    2. 先创建Vue实例,随后在通过vm.$mount('#root')指定el的值
  • data有两种写法

    1. 对象式
    2. 函数式

    如何选择:当用组件时,data必须使用函数式,否则会报错。

  • 一个重要的原则:

    由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再指向Vue实例了。

5. MVVM模型

  • MVVM:

    1. M:模型(model):对应data中的数据
    2. V:视图(view):模板代码
    3. VM:视图模型(ViewModel):Vue实例对象

    image-20220520145601696

  • 备注:

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

6. 数据代理

  • Vue中的数据代理

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

  • Vue中数据代理的好处

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

  • 基本原理

    • 通过Object.defineProperty()data对象中所有的属性添加到vm
    • 为每个添加到vm上的属性,都指定一个getter/setter
    • getter/setter内部去操作(读、写)data中对应的属性。

image-20220520153655652

7. 事件处理

  • 使用v-on:xxx@xxx绑定事件,其中xxx是事件名
  • 事件的回调函数需要配置在methods对象中,最终会在vm
  • methods中配置的函数,不要使用箭头函数!否则this指向就不是vm了;而是window
  • methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象。
  • @click="demo"@click="demo($event)"效果一样,但后者可以传参。

8. 事件修饰符

  • prevent:阻止默认事件(常用);
  • stop:阻止事件冒泡(常用);
  • once:事件只触发依次(常用);
  • capture:使用事件的捕获模式;
  • self:只有event.target是当前操作的元素时才触发事件;
  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
  • 使用方法:例如@click:pervent="xxx"

9. 键盘事件

  • 用法:keyupkeydown,例如:@keyup.enter="xxx"

  • Vue中常用的按键别名:

    • 回车 =>enter

    • 删除 =>delete(捕获“删除”和“退格”键)

    • 退出 =>esc

    • 空格 =>space

    • 换行 =>tab

    • 上 =>up

    • 下 =>down

    • 左 =>left

    • 右 =>right

10. 计算属性 (computed

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

11. 监视属性(侦听属性)(watch

  • 当被监视的属性发生变化时,回调函数自动调用,进行相关操作
  • 监视的属性必须存在,才能进行监视
  • 监视的两种写法:
    1. new Vue()时传入watch配置
    2. 通过vm.$watch()监视
  • 深度监视:
    1. Vue中的watch默认不监视对象内部值得改变(一层)
    2. 配置deep:true可以监视对象内部值得改变(多层)
  • 备注:
    1. Vue自身可以监视对象内部值的改变,但Vue提供的watch默认不可以
    2. 使用watch时根据数据的具体结构,决定是否采用深度监视

12. computedwatch之间的区别

  • computed能完成的功能,watch都可以完成
  • watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
  • 两个重要的原则:
    1. 所被Vue管理的函数,最好写成普通函数(function(){}),这样this的指向才是vm或组件实例对象
    2. 所有不被Vue所管理的函数(如:定时器、ajax的回调函数、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm或组件实例对象。

13. 绑定样式

  • class样式
    • 写法:class="xxx","xxx"可以是字符串、对象、数组。
    • 字符串写法适用于:类名不确定,要动态获取
    • 对象写法适用于:要绑定多个样式,个数不确定,名字不确定。
    • 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
  • style样式
    • 写法::style="{fontSize:xxx}",其中xxx是动态值
    • style={a,b}其中a,b是样式对象

14. 条件渲染

  • v-if

    • 写法:

      v-if = "表达式"    // 表达式 === ture/false
      v-else-if="表达式"
      v-else
      
    • 适用于:切换频率较低的场景

    • 特点:不展示的DOM元素直接被删除

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

  • v-show

    • 写法:v-show="表达式"
    • 适用于:切换频率较高的场景。
    • 特点:不展示的DOM元素未被移除,仅仅是使用样式被隐藏
  • 备注:使用v-if时,元素可能无法获取到,而使用v-show一定能获取到

15. 列表渲染

  • 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. 对比规则:
      • 旧虚拟DOM中找到了与新虚拟DOM相同的key
        1. 若虚拟DOM中的内容没变,直接使用之前的真实DOM
        2. 若虚拟DOM中的内容变了,则生成新的真实DOM,随后替换掉页面之前的真实DOM
      • 旧虚拟DOM中未找到与新虚拟DOM相同的key
        • 创建新的真实DOM,随后渲染到页面
    3. index作为key可能引发的问题:
      • 若对数据进行:逆序添加、逆序删除操作,会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
      • 如果结构中还包含输入类的DOM:会产生错误的DOM更新 ===> 界面有问题
    4. 开发中如何选择key:
      • 最好使用每条数据的唯一标识作为key,如id.
      • 如果仅用于列表展示而没有唯一标识,可使用index作为key

16. Vue监视数据的原理

  • Vue会监视data中所有层次的数据

  • 如何监视对象中的数据?

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

      1. 对象中后加的属性,Vue默认不做响应式处理
      2. 如需给后加的属性做响应式,使用如下API:
      Vue.set(target,propertyName/index,value)  //或
      vm.$set(target,propertyName/index,value)
      
  • 如何监视数组中的数据:

    • 通过包裹数组更新元素的方法实现,本质就是做两件事:
      1. 调用原生对应的方法对数组进行更新。
      2. 重新解析模板,进而更新页面
  • 在Vue修改数组中的某个元素一定要用如下方法:

    1. 使用API:push()/pop()/shift()/unshift()/splice()/sort()/reverse()
    2. 应用Vue.set()或vm.$set()
  • 注意:Vue.set()或vm.$set()不能给vm及根数据对象添加属性

17. 收集表单数据

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

18. 过滤器

  • 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
  • 语法:
    1. 注册过滤器:Vue.filter(name,callback)new Vue(filter:{})
    2. 使用过滤器:{{xxx | 过滤器名}}v-bind:属性='xxx' | 过滤器名
  • 备注:
    1. 过滤器也可以接受额外的参数,多个过滤器也可以串联
    2. 过滤并没有改变原本的数据,是产生新的对应的数据

19. Vue的内置指令集

  • v-bind:单向绑定解析表达式,简写:xxx
  • v-model:双向数据绑定
  • v-for:遍历数组、对象、字符串
  • v-on:绑定事件监听,简写@
  • v-if/v-else-if/v-else:条件渲染(动态控制节点是否存在)
  • v-show:条件渲染(动态控制节点是否展示)
  • v-text:向其所在的节点中渲染文本内容,并会替换节点中的内容。
  • v-html:向指定节点中包含html结构的内容,并会替换节点中的内容。注:存在安全性问题,不要用在用户提交的内容上,容易导致XSS攻击
  • v-cloak:本质上是特殊属性,Vue实例创建完成并接管容器后,会删除v-cloak属性。使用css配合v-cloak可以解决网速变慢时页面展示出{{xxx}}的问题
  • v-once:是所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变将不会引起所在结构的更新,可用于优化性能。
  • v-pre:跳过其所在节点的编译过程,可用于:没有使用指令语法及插值语法的节点而加快编译。

20. 自定义指令

  • 定义语法:

    1. 局部指令:

      new Vue({
      	directives:{指令名:配置对象}
      })
      // 或
      new Vue({
      	directives:{指令名:回调函数}
      })
      
    2. 全局指令:

      Vue.directives(指令名,配置对象)
      // 或
      Vue.directives(指令名,回调函数)
      
  • 配置对象中常见的3个回调函数

    1. bind:指令与元素成功绑定时调用
    2. inserted:指令所在元素插入页面时调用
    3. update:指令所在模板结构被重新解析时调用。
  • 备注:

    • 指令定义时不加v-,但使用时要加v-
    • 指令名如果是多个单词,要使用kabab-case命名方式,不要使用camelCase命名

21. 生命周期函数

image-20220524171528372

  • 常用的生命周期钩子:
    1. mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等 初始化操作
    2. beforDestroy:清除定时器、解绑自定义事件、取消订阅消息等 收尾工作
  • 关于销毁Vue实例:$destroy()
    • 销毁后自定义事件会失效,但原生DOM事件依然有效
    • 一般不会在beforDestroy操作数据,因为即使操作数据,也不会再触发更新数据了。

22. ref属性

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

  • 应用于:html标签上获取真实DOM元素 或 组件标签上获取组件实例对象(vc)

  • 使用方式:

    // 打标识
    <h1 ref="xxx"></h1>  //或
    <School ref="xxx" />
    
    // 获取
    this.$ref.xxx
    

23. 组件的自定义事件

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

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

  • 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>
    2. 第二种方式,在父组件中:
    <Demo ref="demo"/>
    ......	
    mounted(){
       this.$refs.xxx.$on('atguigu',this.test)
    }
  • 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

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

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

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

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

24. 全局事件总线(GlobalEventBus)

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

  2. 安装全局事件总线:

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

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

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxxx',数据)

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

25. 消息订阅与发布(pubsub)

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

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

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

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      
    4. 提供数据:pubsub.publish('xxx',数据)

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

26. nextTick

  • 语法:this.$nextTick(回调函数)

  • 作用:在下一次 DOM 更新结束后执行其指定的回调。

  • 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

27. webStorage

  • 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

  • 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

  • 相关API:

    1. xxxxxStorage.setItem('key', 'value');该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    2. xxxxxStorage.getItem('person');该方法接受一个键名作为参数,返回键名对应的值。
    3. xxxxxStorage.removeItem('key');该方法接受一个键名作为参数,并把该键名从存储中删除。
    4. xxxxxStorage.clear()该方法会清空存储中的所有数据。
  • 备注:

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

28. 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. 缺点:配置略微繁琐,请求资源时必须加前缀。

29. 插槽

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

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

  • 使用方式:

    • 默认插槽:
    父组件中:
            <Category>
               <div>html结构1</div>
            </Category>
    子组件中:
            <template>
                <div>
                   <!-- 定义插槽 -->
                   <slot>插槽默认内容...</slot>
                </div>
            </template>
    
    • 具名插槽:
    父组件中:
            <Category>
                <template slot="center">
                  <div>html结构1</div>
                </template>
    
                <template v-slot:footer>
                   <div>html结构2</div>
                </template>
            </Category>
    子组件中:
            <template>
                <div>
                   <!-- 定义插槽 -->
                   <slot name="center">插槽默认内容...</slot>
                   <slot name="footer">插槽默认内容...</slot>
                </div>
            </template>
    
    • 作用域插槽:
      • 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
    父组件中:
    		<Category>
    			<template scope="scopeData">
    				<!-- 生成的是ul列表 -->
    				<ul>
    					<li v-for="g in scopeData.games" :key="g">{{g}}</li>
    				</ul>
    			</template>
    		</Category>
    
    		<Category>
    			<template slot-scope="scopeData">
    				<!-- 生成的是h4标题 -->
    				<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
    			</template>
    		</Category>
    子组件中:
            <template>
                <div>
                    <slot :games="games"></slot>
                </div>
            </template>
    		
            <script>
                export default {
                    name:'Category',
                    props:['title'],
                    //数据在子组件自身
                    data() {
                        return {
                            games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                        }
                    },
                }
            </script>
    

30. props配置项

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

  • 传递数据:<Demo name="xxx"/>

  • 接收数据:

    1. 第一种方式(只接收):props:['name']

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

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

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

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

31. mixin(混入)

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

  2. 使用方式:

    第一步定义混合:

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

    第二步使用混入:

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

32. 插件

  1. 功能:用于增强Vue

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

  3. 定义插件:

    对象.install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
    
        // 2. 添加全局指令
        Vue.directive(....)
    
        // 3. 配置全局混入(合)
        Vue.mixin(....)
    
        // 4. 添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
    }
    
  4. 使用插件:Vue.use()

Vue3.js核心

1. 拉开序幕的setup

  • 理解:Vue3的一个新的配置项,值是一个函数。
  • setup是所有Composition API(组合API)“表演的舞台”
  • 组件中所用到的:数据、方法等等,均要配置在setup中。
  • setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点)
    2. 又返回一个渲染函数:则可以自定义渲染内容。(了解)
  • 注意:
    1. 尽量不要与Vue2.x配置混用
      • Vue2.x配置(data、methods、computed...中可以访问setup中的属性、方法。
      • 但在setup中不能访问到Vue2.x配置(data、methods、computed).
      • 如果有重名,setup优先。
    2. setup不能是一个async函数,因为返回值不再是return 的对象,而是promise,模板看不到return 对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)。

2. ref函数

  • 作用:定义一个响应式函数
  • 语法:const xxx = ref(initValue)
    • 创建一个包含响应式的引用对象(reference对象,简称ref对象)
    • JS中操作数据:xxx.value
    • 模板中读取数据:不需要value,直接<div>{{xxx}}</div>
  • 备注:
    • 接收的数据可以使:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
    • 对象类型的数据:内部 *“求助”*了Vue3.0中的一个新的函数——reactive函数。

3. reactive函数

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

4. Vue3.0中的响应式原理

  • Vue2.x的响应式

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

      Object.defineProperty(data,'count',{
      	get(){},
      	set(){}
      })
      
    • 存在问题:

      1. 新增属性、删除属性,界面不会更新。
      2. 直接通过下标修改数组,界面不会自动更新。
  • Vue3.0的响应式

    • 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。

    • 通过Reflect(反射):对源对象的属性进行操作。

      • MDN文档中描述的ProxyReflect

        • Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

        • Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/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)
        	}
        })
        

5. reactive对比ref

  • 从定义数据角度比较:
    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
    • ref定义的数据:操作数据需要.value读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

6. setup的两个注意点

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

7. 计算属性与监视属性

  • computed函数

    • 与Vue2.x中computed配置功能一致

    • 写法

      import {computed} from 'vue'
      
      setup(){
          ...
      	//计算属性——简写
          let fullName = computed(()=>{
              return person.firstName + '-' + person.lastName
          })
          //计算属性——完整
          let fullName = computed({
              get(){
                  return person.firstName + '-' + person.lastName
              },
              set(value){
                  const nameArr = value.split('-')
                  person.firstName = nameArr[0]
                  person.lastName = nameArr[1]
              }
          })
      }
      
  • watch函数

    • 与Vue2中watch配置功能一致

    • 两个小坑:

      1. 监视reactive定义的响应式数据时:oldValue无法正确获取、并强制开启了深度监视(deep配置失效)
      2. 监视reactive定义的响应式数据中某个属性时:响应式数据中某个属性时**:deep配置有效**
      //情况一:监视ref定义的响应式数据
      watch(sum,(newValue,oldValue)=>{
      	console.log('sum变化了',newValue,oldValue)
      },{immediate:true})
      
      //情况二:监视多个ref定义的响应式数据
      watch([sum,msg],(newValue,oldValue)=>{
      	console.log('sum或msg变化了',newValue,oldValue)
      }) 
      
      /* 情况三:监视reactive定义的响应式数据
      			若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
      			若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
      */
      watch(person,(newValue,oldValue)=>{
      	console.log('person变化了',newValue,oldValue)
      },{immediate:true,deep:false}) //此处的deep配置不再奏效
      
      //情况四:监视reactive定义的响应式数据中的某个属性
      watch(()=>person.job,(newValue,oldValue)=>{
      	console.log('person的job变化了',newValue,oldValue)
      },{immediate:true,deep:true}) 
      
      //情况五:监视reactive定义的响应式数据中的某些属性
      watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
      	console.log('person的job变化了',newValue,oldValue)
      },{immediate:true,deep:true})
      
      //特殊情况
      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配置的回调执行了')
      })
      

8. 生命周期

  • Vue2

  • Vue3:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55ezalDt-1654075906024)(https://md-1257824135.cos.ap-nanjing.myqcloud.com/uPic/lifecycle-20220529180910316.svg)]

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

9. 自定义hook函数

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

10. toRef

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

11. shallowReactiveshallowRef

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

12. readonlyshallowReadonly

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

13. toRawmarkRaw

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

14. customRef

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

  • 实现防抖效果:

    <template>
    	<input type="text" v-model="keyword">
    	<h3>{{keyword}}</h3>
    </template>
    
    <script>
    	import {ref,customRef} from 'vue'
    	export default {
    		name:'Demo',
    		setup(){
    			// let keyword = ref('hello') //使用Vue准备好的内置ref
    			//自定义一个myRef
    			function myRef(value,delay){
    				let timer
    				//通过customRef去实现自定义
    				return customRef((track,trigger)=>{
    					return{
    						get(){
    							track() //告诉Vue这个value值是需要被“追踪”的
    							return value
    						},
    						set(newValue){
    							clearTimeout(timer)
    							timer = setTimeout(()=>{
    								value = newValue
    								trigger() //告诉Vue去更新界面
    							},delay)
    						}
    					}
    				})
    			}
    			let keyword = myRef('hello',500) //使用程序员自定义的ref
    			return {
    				keyword
    			}
    		}
    	}
    </script>
    

15. provideinject

  • 作用:实现祖与后代组件间的通信

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

  • 具体写法:

    1. 祖组价中:

      setup(){
      	.....
      	let car = reactive({name:'奔驰',price:'40万'})
      	provide('car',car)
      	.....
      }
      
    2. 后代组件中:

      setup(){
      	......
      	const car = inject('car')
      	return {car}
      	.......
      }
      

16. 响应式数据的判断

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

17. Composition API的优势

  • Options API存在的问题(Vue2)
  • Composition API的优势

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

18. 新的组件

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包裹组件,并配置好defaultfallback

      <template>
      	<div class="app">
      		<h3>我是App组件</h3>
      		<Suspense>
      			<template v-slot:default>
      				<Child/>
      			</template>
      			<template v-slot:fallback>
      				<h3>加载中.....</h3>
      			</template>
      		</Suspense>
      	</div>
      </template>
      

19. 其他

1. 全局API的转移

  • Vue 2.x 有许多全局 API 和配置。

    • 例如:注册全局组件、注册全局指令等。

      //注册全局组件
      Vue.component('MyButton', {
        data: () => ({
          count: 0
        }),
        template: '<button @click="count++">Clicked {{ count }} times.</button>'
      })
      
      //注册全局指令
      Vue.directive('focus', {
        inserted: el => el.focus()
      }
      
  • Vue3.0中对这些API做出了调整:

    • 将全局的API,即:Vue.xxx调整到应用实例(app)上

      2.x 全局 API(Vue3.x 实例 API (app)
      Vue.config.xxxxapp.config.xxxx
      Vue.config.productionTip移除
      Vue.componentapp.component
      Vue.directiveapp.directive
      Vue.mixinapp.mixin
      Vue.useapp.use
      Vue.prototypeapp.config.globalProperties

2. 其他改变

  • 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” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

yige001

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

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

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

打赏作者

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

抵扣说明:

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

余额充值