Vue 3.0 学习笔记

Vue 3 学习笔记

一、初识vue3

1.vue3简介

  • 2020年9月18日,vue3发布3.0版本,代号大海贼时代来临,One Piece

  • 特点:

    • 无需构建步骤,渐进式增强静态的 HTML

    • 在任何页面中作为 Web Components 嵌入

    • 单页应用 (SPA)

    • 全栈 / 服务端渲染 (SSR)

    • Jamstack / 静态站点生成 (SSG)

    • 开发桌面端、移动端、WebGL,甚至是命令行终端中的界面

2.Vue3带来了什么

  • 打包大小减少40%

  • 初次渲染快55%,更新渲染快133%

  • 内存减少54%

3.分析目录结构

  • main.js中的引入
  • 在模板中vue3中是可以没有根标签了,这也是比较重要的改变
  • 应用实例并不只限于一个。createApp API 允许你在同一个页面中创建多个共存的 Vue 应用,而且每个应用都拥有自己的用于配置和全局资源的作用域。
//main.js

//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import {createApp} from 'vue
import App from './App.vue

//创建应用实例对象-app(类似于之前vue2中的vm实例,但是app比vm更轻)
createApp(APP).mount('#app')
//卸载就是unmount,卸载就没了
//createApp(APP).unmount('#app')

//之前我们是这么写的,在vue3里面这一块就不支持了,会报错的,引入不到 import vue from 'vue'; 
new Vue({
	render:(h) => h(App)
}).$mount('#app')

//多个应用实例
const app1 = createApp({
  /* ... */
})
app1.mount('#container-1')

const app2 = createApp({
  /* ... */
})
app2.mount('#container-2')

二、 常用Composition API(组合式API)

1. setup函数

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

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

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

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

    • 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注)

    • 若返回一个渲染函数:则可以自定义渲染内容。(不常用)

  • 5.注意点:

    • 尽量不要与Vue2.x配置混用

      • Vue2.x配置(data ,methos, computed…)中访问到setup中的属性,方法
      • 但在setup中不能访问到Vue2.x配置(data.methos,compued…)
      • 如果有重名,setup优先
    • setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性

      但如果是异步引入的方式,是可以返回一个promise的,采用defineAsyncComponent引入时

<template>
    <div>
        <h1>SetupMain</h1>
        <h2>姓名:{{ name }}</h2>
        <h2>年龄:{{ age }}</h2>
        <h2>地址:{{ address() }}</h2>
    </div>
</template>

<script>
export default {
    name: "SetupMain",
    setup() {
        function address() {
            return "无锡。。。。"
        }
        return {
            name: "wsy",
            age: 24,
            address
        }
    }
}
</script>

2. ref函数

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

3. reactive函数

  • 作用:定义一个对象类型的响应式数据(基本类型不使用这个函数,用ref函数)
  • 语法:const 代理对象 = reactive(被代理对象)接收一个对象(或数组),返回一个代理器对象(proxy对象)
  • reactive定义的响应式数据是深层次的(多层数据改变都可以监测)
  • 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据都是响应式的

在这里插入图片描述

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

Vue2.x的响应式
  • 实现原理:

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

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

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

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

    也可以通过调用强制更新钩子 this.$forceUpdate()

    <template>
        <div>
            <h1>Test</h1>
            <h2>姓名:{{ person.name }}</h2>
            <h2 v-show="person.age">年龄:{{ person.age }}</h2>
            <h2 v-show="person.sex">性别:{{ person.sex }}</h2>
            <h2>数组:{{ list }}</h2>
            <button @click="fun1">删除年龄</button>
            <br>
            <button @click="fun2">新增性别</button>
            <br>
            <button @click="fun3">修改数组</button>
            <br>
        </div>
    </template>
    
    <script>
    import Vue from "vue"
    
    export default {
        name: "Test",
        data() {
            return {
                person: {
                    name: "xiaohua",
                    age: 18,
                },
                list: [1, 2, 3]
            }
        },
        methods: {
            fun1() {
                console.log("删除年龄成功")
                //这种方式是不会产生页面修改的
                delete this.person.age;
    
                //可以通过这种方式强制更新
                // this.$forceUpdate()
                // this.$delete(this.person,"age")
                // Vue.delete(this.person,"age")
            },
            fun2() {
                console.log("新增性别成功")
                //这种方式是不会产生页面修改的
                // this.person.sex = "女"
    
                // this.$set(this.person,"sex","女")
                Vue.set(this.person, "sex", "女")
            },
            fun3() {
                console.log("修改数组成功")
                //这种方式是不会产生页面修改的
                // this.list[0]=2
    
                this.list.splice(0, 1, 2)
            },
    
        }
    }
    </script>
    
    
Vue3.x的响应式
  • 实现原理:

    • 通过 Proxy(代理):拦截对象中任意属性的变化,包括属性值的读写,属性的添加,属性的删除等
    • 通过 Reflect(反射):对源对象的属性进行操作。
  • 大致实现代码

    var person = {name:"xiaoli",age:12}
    
    //#region
    //利用Proxy
    var p = new Proxy(person,{
      /**
        * @param target 源对象 (被代理的对象)
        * @param property 具体的某个字段 例如‘name’
        * @param receiver 就是当前这个proxy实例
        * */
      //拦截读取属性值
      get(target, property, receiver) {
        console.log(`有人读取了p身上的${property}属性`)
        /*console.log(target, property, receiver)*/
        //在底层用的不是这种写法 ,而是借助于Reflect
        // return target[property]
        return Reflect.get(target,property)
      },
      //拦截设置属性值或添加新属性
      set(target, property, newValue, receiver) {
        console.log(`有人修改了p身上的${property}属性,新值为${newValue}`)
        // console.log(target, property, receiver)
        //return target[property] = newValue
        return Reflect.set(target,property,newValue)
      },
      //拦截删除属性
      deleteProperty(target, property, receiver) {
        console.log(`有人删除了p身上的${property}属性`)
        // console.log(target, property, receiver)
        // return delete target[property]
        return Reflect.deleteProperty(target,property)
      }
    });
    //#endregion
    

    效果:

    在这里插入图片描述

5. reactivce对比ref

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

6. setup的两个注意点

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

7. 计算属性与监视属性

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

  • 写法

    import {computed, reactive, ref} from "vue";
    
    export default {
        name: "ComputedComp",
        setup(){
            let person = reactive({
                firstName :"",
                lastName:""
            })
    
            //计算属性 简写 只读取的情况
            // let fullName = computed(()=>{
            //     return person.firstName+"_"+person.lastName
            // })
    
            //计算属性完整写法
            let fullName = computed({
                get(){
                    return person.firstName+"_"+person.lastName
                },
                set(value){
                    let nameArr = value.split("_");
                    person.firstName = nameArr[0];
                    person.lastName = nameArr[1]
    
                }
            })
    
            return{
                fullName,
                person
            }
        }
    
2.watch函数
  • 与Vue2.x中watch配置功能一直

  • 两个问题

    • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)
    • 监视reactive定义的响应式数据中的某个属性(多层级)时:deep配置有效
    setup() {
      let sum = ref(0);
      let msg = ref("你好")
      let person = reactive({name: "小张", age: 12, job: {a: {b: 1}}})
      //情况一,监视ref定义的响应式数据
      watch(sum,(newValue , oldValue)=>{
        console.log("sum变化了",newValue,oldValue)
      })
    
      //情况二,监视多个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.name,(newValue , oldValue)=>{
        console.log("person内的name属性发生变化了",newValue,oldValue)
      })
    
      //情况五:监视reactive所定义的一个响应式数据中的某些数据
      watch([()=>person.name,()=>person.age],(newValue , oldValue)=>{
        console.log("person内的name或age属性发生变化了",newValue,oldValue)
      })
    
      //特殊情况:监视reactive所定义的一个响应式数据中的多层级数据,可以利用deep
      watch(() => person.job, (newValue, oldValue) => {
        console.log("person内的job属性发生变化了", newValue, oldValue)
      }, {deep: true})
    
      return {
        sum, msg, person
      }
    
  • watch中的.value问题
    1. 监视ref定义的基本类型数据,不可以加value

      在这里插入图片描述

    2. 监视ref定义的对象类型数据,需要加value

      let personRef = ref({name: "小张", age: 12, job: {a: {b: 1}}})
      // 监视ref定义的对象类型数据,需要加value
      watch(personRef.value,(newValue , oldValue)=>{
        console.log("personRef变化了",newValue,oldValue)
      })
      
3.watchEffect函数
  • watch的语法是:既要指明监视的属性,也要指明监视的回调

  • watchEffect的语法是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就用哪个属性

  • watchEffect有点像computed

    • 但computed注重重新计算出来的值(回调函数的返回值),所以必须要写返回值
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
    //watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调
    watchEffect(()=>{
    const value = sum.value;
    const job = person.job.a.b;
    console.log("watchEffect被调用了")
    })
    
    

8. 生命周期

1.vue 2.0 与 vue 3.0的生命周期对比

在这里插入图片描述

2.生命周期对应
vue 2.0vue 3.0对应setup中组合api备注
setup-无setup是3.0提出的
beforeCreatebeforeCreate被setup取代创建前
createdcreated被setup取代创建
beforeMountbeforeMountonBeforeMount()挂载前
mountedmountedonMounted()挂载
beforeUpdatebeforeUpdateonBeforeUpdate()更新前
updatedupdatedonUpdated()更新
beforeDestroybeforeUnmount(改名)onBeforeUnmoun()卸载前(2.0的钩子被改名)
destroyunmounted(改名)onUnmounted()卸载

代码实例

 setup(){
        let sum  = ref(1)
        console.log("===setup===")

        onBeforeMount(()=>{
            console.log("===setup 中 onBeforeMount===")
        })
        return{
            sum
        }
    },
    beforeCreate() {console.log("===beforeCreate===")},
    created() {console.log("===created===")},
    beforeMount() {console.log("===beforeMount===")},
    mounted() {console.log("===mounted===")},
    beforeUpdate() {console.log("===beforeUpdate===")},
    updated() {console.log("===updated===")},
   //销毁钩子已经被弃用,写了也没有用
    beforeDestroy() {console.log("===beforeDestroy===")},
    //销毁钩子已经被弃用,写了也没有用
    destroyed() {console.log("===destroyed===")},
    beforeUnmount() {console.log("===beforeUnmount===")},
    unmounted() {console.log("===unmounted===");}

注意:配置项中的钩子函数与setup中的钩子函数同时存在,两个都会被触发,但是setup中的函数先被触发

9. 自定义hook函数

  • hook函数——本质是一个函数,把setup函数中使用的Composition API进行了封装

  • 类似于vue2.x中的mixin

  • 优点:复用代码,让setup中的逻辑更清楚易懂

  • 使用

    1.将需要使用的函数代码封装到一个js文件中

    import {onMounted, onUnmounted, reactive} from "vue";
    
    export default function (){
        let point = reactive({x:0,y:0})
        function clickPoint(event){
            console.log(event.pageX,event.pageY)
            point.x = event.pageX;
            point.y = event.pageY;
        }
    
        onMounted(()=>{
            //绑定一个打点事件
            window.addEventListener("click",clickPoint)
        })
    
        onUnmounted(()=>{
            //移除一个打点事件
            window.removeEventListener("click",clickPoint)
        })
        return point
    }
    

    2.在需要使用的vue文件中引入

    <template>
        <h1>Hook函数</h1>
        <h2>point: x:{{ point.x }} y:{{ point.y }}</h2>
    </template>
    
    <script>
    import clickPoint from "@/hook/usePoint"
    
    export default {
        name: "HookMain",
        setup() {
            let point = clickPoint()
            return {
                point
            }
        }
    }
    </script>
    

10. toRef

  • 作用:创建一个ref对象,其value指向另一个对象中的某个属性

  • 语法:const name = toRef(person,'name'),const x = toRefs(person)

  • 应用:要将响应式对象中的某个属性单独提供给外部使用时

  • 扩展:toRefstoRef功能一致,但可以批量创建多个ref对象

  • 使用:

    import {reactive, toRef, toRefs} from "vue";
    
    export default {
        name: "RefComp",
        setup() {
            let person = reactive({name: "小张", age: 12, job: {a: {b: 1}}})
            let nameRef = toRef(person, "name");
            let personRef = toRefs(person);
    
            return {
                ...personRef,//后续使用就可以直接使用name等属性,不用写personRef
                person, nameRef
            }
        }
    }
    

三、 其他Composition API

1. shallowReactive 与 shallowRef函数

  • shallowReactive:值处理对象最外层属性的响应式(浅响应式)

  • shallowRef:只处理基本数据类型的响应式,不进行对象的响应处理

  • 使用

    • 如果有一个对象数据,结构比较深,但变化时只是外层属性变化,使用shallowReactive
    • 如果有一个对象属性,后续功能不会修改对象中的属性,而是生成新的对象进行替换,使用shallowRef
  • setup() {
      //只会处理对象对外层的属性响应(浅响应式)
      // let person = shallowReactive(
      //值处理基本类型的响应式 不进行对象类型的响应式处理
      let person = shallowRef(
        // let person = reactive(
        {name: "小张", age: 12, job: {a: {b: 1}}})
    
      let sum = shallowRef(0)
    
      return {
        sum,
        person
      }
    }	
    

2.readonly 与 shallowReadonly

  • readonly:让一响应式数据变为只读的(深只读)

  • shallowReadonly:让一个响应式数据变为只读的(浅只读)

  • 应用场景:不希望数据被修改时

    let person = reactive(
      {name: "小张", age: 12, job: {a: {b: 1}}})
    
    let sum = ref(0)
    
    // person = readonly(person)
    person = shallowReadonly(person)
    sum = readonly(sum)
    

3.toRaw 与 markRaw

  • toRaw:

    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw:

    • 作用:标记一个对象,使其永远不会再成为响应式对象
    • 应用场景:
      1. 有些值不应该被设置为响应式的,例如复杂的第三方类库等。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
  • function fun() {
      //可以将一个reactive生成的对象转为普通对象
      console.log(toRaw(person))
    }
    
    function addCar() {
      const car = {name: '奔驰s450', price: 100};
      //可以标记一个对象,使其永远不会再成为响应式对象
      markRaw(car);
      person.car = car;
    }
    

4.customRef

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

  • 案例,实现防抖效果

    <template>
      <h1>CustomRefComp</h1>
      <input v-model="keyword"/>
      <h3>{{keyword}}</h3>
    
    </template>
    
    <script>
    import {customRef} from "vue";
    
    export default {
        name: "CustomRefComp",
        setup(){
            // let keyword = ref("hello")
            //自定义一个myRef
            function myRef(value,delay){
                let timer
                //通过customRef去实现自定义
                return customRef((track,trigger)=>{
                    return{
                        get(){
                            track() //告诉vue这个value是需要被追踪的
                            console.log("get被触发")
                            return value;
                        },
                        set(newValue){
                            clearTimeout(timer)
                            console.log("set被触发",newValue)
                            timer = setTimeout(()=>{
                                value = newValue
                                trigger() //告诉vue去更新界面
                            },delay)
                        }
                    }
                })
            }
            let keyword = myRef("hello",500)
    
            return{
                keyword
            }
        }
    }
    

5.provide 与 inject

在这里插入图片描述

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

  • 使用:父组件利用provide选项来提供数据,子组件利用inject选项来使用这些数据

  • 具体写法

    1. 祖组件中

      setup(){
      	......
        let car = reactive({name:"奔驰s450",price:"100w"})
        //为后代组件提供数据
        provide("car",car);
      	......
      }
      
    2. 后代组件中

      setup(){
        ......
        //接收数据
        const car = inject("car")
        return{ car }
        ......
      }
      
  • 注意: 传递的数据是一个响应式数据,所以在后代组件中修改数据,是会影响到所有使用这个数据的地方的。

6.响应式数据的判断

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

四、Composition API 的优势

1.Options API 存在的问题

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

2.Composition API 的优势

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

五、新的组件

1.Fragment

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

2.Teleport

  • Teleport是一种能够将我们的组件html结构移动到指定位置的技术

  • 优点:

    • 不影响其他组件html的结构

      举例说明:在多层嵌套的组件结构中,我的孙组件有个弹窗组件,需要显示或隐藏,如果不使用Teleport,那么每次展示弹窗时候会影响别的组件html结构,而使用Teleport就不会影响

    • 方便定位,直接把html结构直接传送走,比如案例的传送至body处或者其他处。

  • 使用例子

    <teleport to="body">
      <div v-if="isShow" class="mask">
        <div class="dialog">
          <h1>弹窗</h1>
          <h2>内容:</h2>
          <h2>内容:</h2>
          <h2>内容:</h2>
          <button @click="isShow=false">关闭弹窗</button>
        </div>
      </div>
    </teleport>
    
    <style scoped>
    .app {
        background-color: #aa2222;
    }
    
    .mask{
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0,0,0,.5);
    }
    
    .dialog{
        position: absolute;
        top: 50%;
        left: 50%;
       transform: translate(-50%,-50%);
        width: 500px;
        background-color: #FF8A3B;
        padding: 10px;
        text-align: center;
    }
    
    </style>
    

    这种方式可以将标签内写的元素移动到to后面的元素后面,这样方便我们去做定位,例如这个例子相对于body进行垂直水平居中

    在这里插入图片描述

3.Suspense(后续可能更改)

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验

  • 使用

    • 异步引入组件

      import {defineAsyncComponent} from "vue";
      const SusComp = defineAsyncComponent(()=>import("./components/03.新的标签/02.Suspense/SusComp.vue"))
      
    • 使用Suspense包裹组件,并配置好defaultfallback

      <Suspense>
       	异步组件返回的时候展示自己的标签
        <template v-slot:default>
      		<SusComp></SusComp>
        </template>
        
        异步组件没返回的时候展示fallback
        <template v-slot:fallback>
      		<h1>请稍等</h1>
        </template>
      
      </Suspense>
      
  • 注意:当使用异步引入组件的时候,被引入组件的setup可以返回一个promise

    setup(){
      let name = ref("ffff");
      return new Promise((resolve, reject)=>{
        setTimeout(()=>{
          resolve({name})
        },1000)
      })
    }
    

六、其他

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.其他改变

  • 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;
      }
      

      v-enterv-leave 修改为 v-enter-fromv-leave-from

  • 移除 keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes

  • 移除 v-on.native修饰符

    • 父组件中绑定事件

      <my-component
        v-on:close="handleComponentEvent"
        v-on:click="handleNativeClickEvent"
      />
      
    • 子组件中声明自定义事件

      <script>
        export default {
      		//如果这里加上clikc,则click也会被当成自定义事件
          emits: ['close']
        }
      </script>
      

      ==注意:==使用emits声明的就是自定义事件,未声明的就当做原生事件

  • 移除 过滤器(filter)

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LvhaoIT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值