Vue3.x学习笔记(摘录)

Vue3.x学习笔记(摘录)

官网链接:https://v3.cn.vuejs.org/

相关链接:Vue3.x入门

相关链接:初探 Vue3.0 中的一大亮点——Proxy !

PS:内容仅从相关链接地址摘录,便于学习及使用时查看回顾

常用语法

来源链接:Vue3.x入门

入口函数

setup

export default {
  setup () {
    console.log('setup执行了')
    console.log(this)
  },
  beforeCreate() {
    console.log('beforeCreate执行了')
    console.log(this)
  }
}

响应式API

reactive

返回对象的响应式副本

const obj = reactive({ count: 0 })

注意

reactive 将解包所有深层的 refs,同时维持 ref 的响应性。

const count = ref(1)
const obj = reactive({ count })

// ref 会被解包
console.log(obj.count === count.value) // true

// 它会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2

// 它也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3

readonly

接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 用于响应性追踪
  console.log(copy.count)
})

// 变更 original 会触发依赖于副本的侦听器
original.count++

// 变更副本将失败并导致警告
copy.count++ // 警告!

isProxy

检查对象是否是由 reactivereadonly 创建的 proxy。

isReactive

检查对象是否是由 reactive 创建的响应式代理。

import { reactive, isReactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: 'John'
    })
    console.log(isReactive(state)) // -> true
  }
}

注意

如果该代理是 readonly 创建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true

isReadonly

检查对象是否是由 reactive 创建的只读代理。

Refs

ref

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。

使用步骤

  1. 从vue框架中导出ref函数
  2. 在setup函数中调用ref函数并传入数据(简单类型或者复杂类型)
  3. 在setup函数中把ref函数调用完毕的返回值以对象的形式返回出去
  4. 注意:在setup函数中使用ref结果,需要通过.value 访问,模板中使用不需要加.value
<template>
  <div>{{ money }}</div>
  <button @click="changeMondy">改值</button>
</template>
 
<script>
import { ref } from 'vue'
export default {
  setup() {
    let money = ref(100)
    console.log(money.value)
    return {
      money
    }
  }
}
</script>
toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 和原始 property 已经“链接”起来了
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3
toRef

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

当从组合式函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应性的情况下对返回的对象进行解构/展开

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // 操作 state 的逻辑

  // 返回时转换为ref
  return toRefs(state)
}

export default {
  setup() {
    // 可以在不失去响应性的情况下解构
    const { foo, bar } = useFeatureX()
    return {
      foo,
      bar
    }
  }
}

ref 和 reactive的区别

  1. ref 函数可以接收一个简单类型的值,返回一个可改变的 ref 响应式对象,从而弥补reactive函数不支持简单类型的问题
  2. reactive和ref函数都可以提供响应式数据的转换
  3. reactive这个函数产生的响应式对象 在return出去的时候千万不能直接解构或者展开否则会丢失响应式

推荐一种写法:只有我们明确知道要转换的对象内部的字段名称我们才使用reactive,否则就一律使用ref,从而降低在语法选择上的心智负担

如何return一个reactive这个函数产生的响应式对象?

// 问题: reactive这个函数产生的响应式对象 在return出去的时候千万不能直接解构或者展开 否则会丢失响应式
   const state = reative({name:"cp"})
   {{state.name}}
 
    return {
      ...state
    }
// 解决方案:toRefs()
   const state = reative({name:"cp"})
   {{state.name}}
 
    return {
      ...toRefs(state)
      }

computed

根据现有响应式数据经过一定的计算得到全新的数据

使用步骤

  1. 从vue中导入computed函数
  2. setup中执行computed函数 并且在函数内部定义好计算公式 return即可
  3. 使用一个变量接收一下computed执行之后的结果 这个结果就是要用到的计算属性
  4. 如果在模板中要用到计算属性依旧需要return

示例

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误

或者,接受一个具有 getset 函数的对象,用来创建可写的 ref 对象。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

watch

基于响应式数据的变化执行回调逻辑,和vue2中的watch的功能完全一致

  1. 普通监听
  2. 立即执行
  3. 深度监听

侦听器 watch 监听数据变化 然后执行回调 立刻执行 immediate:true 开启监听 deep:true

使用步骤

  1. 从vue框架中导入watch函数
  2. 在setup函数中执行watch函数开启对响应式数据的监听
  3. watch函数接收三个常规参数
    1. 第一个参数为函数,返回你要监听变化的响应式数据
    2. 第二个参数为响应式数据变化之后要执行的回调函数
    3. 第三个参数为一个对象,在里面配置是否开启立刻执行或者深度监听

示例

<template>
  {{ age }}
  <button @click="age++">change age</button>
</template>
 
<script>
import { ref, watch } from 'vue'
export default {
  setup() {
    const age = ref(18)
    watch(
    () => {  // 第一个参数为函数,返回你要监听变化的响应式数据
 
      // 返回你想要监听的响应式属性(ref产生的对象必须加.value)
      return age.value
    }, 
    () => {  // 第二个参数为响应式数据变化之后要执行的回调函数
 
      // 数据变化之后的回调函数
      console.log('age发生了变化')
    },
    {  // 第三个参数为一个对象,在里面配置是否开启立刻执行或者深度监听
        immediate: true
    
    })
    return {
      age
    }
  }
}

onMounted

使用步骤

  1. 先从vue中导入以on打头的生命周期钩子函数
  2. 在setup函数中调用生命周期函数并传入回调函数
  3. 生命周期钩子函数可以调用多次

生命周期钩子函数:在组件从无到有整个过程中会经历很多个阶段 每个阶段时机成熟会自动调用对应的函数。这些被自动调用的函数就成为生命周期钩子函数

mounted: 发送ajax请求 获取真实的dom(地图场景 可视化图表依赖真实的dom 初始化操作)
created: 发送ajax请求 (响应式处理之后 组件其它配置项中共享某些数据但是又不想把它变成响应式的)
destroyed: 清理内存 (setTimeout setInteval) 组件销毁的时候要对定时器进行销毁操作 防止常驻内存

初始化 beforeCreate -> created -> beforeMount -> mounted 只会执行一次
运行时 beforeUpate -> updated 只要一修改响应式数据就会执行一次
组件销毁 beforeDestroy -> destoryed 组件被销毁 $destory() 路由切换

示例

<template>
  <div>生命周期函数</div>
</template>
 
<script>
import { onMounted } from 'vue'
export default {
  setup() {
    // 时机成熟 回调函数自动执行
    onMounted(() => {
      console.log('mouted生命周期执行了')
    })
     onMounted(() => {
      console.log('mouted生命周期函数又执行了')
    })
  }
}
</script>

父子通信

props

实现步骤

  1. setup函数提供俩个参数,第一个参数为props,第二个参数为一个对象context(ctx)
  2. props为一个对象,内部包含了父组件传递过来的所有prop数据,context对象包含了attrs,slots, emit属性,其中的emit可以触发自定义事件的执行从而完成子传父

父组件

<template>
  <son :name="name" @get-msg="getMsg"></son>
</template>
 
<script>
import { ref } from 'vue'
import Son from './components/son'
export default {
  components: {
    Son
  },
  setup() {
    const name = ref('cp')
    function getMsg(msg) {
      console.log(msg)
    }
    return {
      name,
      getMsg
    }
  }
}
</script>

子组件

<template>
  <div>
    {{name}}
    <button @click="setMsgToSon">set</button>
  </div>
</template>
 
<script>
export default {
  props: {
    name: {
      type: String
    }
  },
  emits: ['get-msg'], // 声明当前组件触发的自定义事件
  setup(props,{emit}) {
    console.log(props.name)
    function setMsgToSon(){
      emit('get-msg','这是一条来自子组件的msg信息')
    }
    return {
      setMsgToSon
    }
  }
}
</script>

provide

顶层组件在setup方法中使用provide函数提供数据

provide('key',value)

inject

任何底层组件在setup方法中使用inject函数获取数据

const data = inject('key')

Proxy

来源链接:初探 Vue3.0 中的一大亮点——Proxy !

目标对象的操作之前提供了拦截,可以对外界的操作进行过滤和改写,修改某些操作的默认行为,这样我们可以不直接操作对象本身,而是通过操作对象的代理对象来间接来操作对象,达到预期的目的

ES6 原生提供的 Proxy 语法很简单,用法如下:

let proxy = new Proxy(target, handler);

拦截对象

get

用于拦截对象的读取属性操作,target 是指目标对象,property 是被获取的属性名 , receiverProxy 或者继承 Proxy 的对象,一般情况下就是 Proxy 实例。

--handler.get(target,property,receiver)
示例
let proxy = new Proxy({},{
    get : function (target,prop) {
        console.log(`get ${prop}`);
        return 10;
    }
})
    
console.log(proxy.a)    // get a
                        // 10
注意

如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同,也就是不能对其进行修改,否则会抛出异常

et obj = {};
Object.defineProperty(obj, "a", {
    configurable: false,
    enumerable: false,
    value: 10,
    writable: false
});

let proxy = new Proxy(obj,{
    get : function (target,prop) {
        return 20;
    }
})

console.log(proxy.a)    // Uncaught TypeError

set

用于拦截设置属性值的操作,参数于 get 方法相比,多了一个 value ,即要设置的属性值。

严格模式下,set方法需要返回一个布尔值,返回 true 代表此次设置属性成功了,如果返回false且设置属性操作失败,并且会抛出一个TypeError

--handler.set(target, property, value, receiver)
示例
let proxy = new Proxy({},{
    set : function (target,prop,value) {
        if( prop === 'count' ){
            if( typeof value === 'number'){
                console.log('success')
                target[prop] = value;
            }else{
                throw new Error('The variable is not an integer')
            }
        }
    }
})
    
 proxy.count = '10';    // The variable is not an integer
 
 proxy.count = 10;      // success
注意

如果目标属性是不可写及不可配置的,则不能改变它的值,即赋值无效

let obj = {};
Object.defineProperty(obj, "count", {
    configurable: false,
    enumerable: false,
    value: 10,
    writable: false
});

let proxy = new Proxy(obj,{
    set : function (target,prop,value) {
        target[prop] = 20;
    }
})

proxy.count = 20 ;
console.log(proxy.count)   // 10

拦截函数

apply

用于拦截函数的调用,共有三个参数,分别是目标对象(函数)target,被调用时的上下文对象 thisArg 以及被调用时的参数数组 argumentsList,该方法可以返回任何值。

target 必须是是一个函数对象,否则将抛出一个TypeError

--handler.apply(target, thisArg, argumentsList)
示例
function sum(a, b) {
    return a + b;
}

const handler = {
    apply: function(target, thisArg, argumentsList) {
        console.log(`Calculate sum: ${argumentsList}`); 
        return target(argumentsList[0], argumentsList[1]) * 2;
    }
};

let proxy = new Proxy(sum, handler);

console.log(sum(1, 2));     // 3
console.log(proxy(1, 2));   // Calculate sum:1,2
                            // 6
注意

实际上,apply 还会拦截目标对象的 Function.prototype.apply()Function.prototype.call(),以及 Reflect.apply() 操作

console.log(proxy.call(null, 3, 4));    // Calculate sum:3,4
                                        // 14

console.log(Reflect.apply(proxy, null, [5, 6]));    // Calculate sum: 5,6
                                                    // 22

拦截new

construct

用于拦截 new 操作符,为了使 new 操作符在生成的 Proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法;它接收三个参数,目标对象 target ,构造函数参数列表 argumentsList 以及最初实例对象时,new 命令作用的构造函数

--handler.construct(target, argumentsList, newTarget)
示例
let p = new Proxy(function() {}, {
    construct: function(target, argumentsList, newTarget) {
        console.log(newTarget === p );                          // true
        console.log('called: ' + argumentsList.join(', '));     // called:1,2
        return { value: ( argumentsList[0] + argumentsList[1] )* 10 };
    }
});

console.log(new p(1,2).value);      // 30
注意

该方法必须返回一个对象,否则会抛出异常

var p = new Proxy(function() {}, {
    construct: function(target, argumentsList, newTarget) {
        return 2
    }
});

console.log(new p(1,2));    // Uncaught TypeError

拦截in

has

has方法可以看作是针对 in 操作的钩子,当我们判断对象是否具有某个属性时,这个方法会生效,典型的操作就是 in ,改方法接收两个参数 目标对象 target 和 要检查的属性 prop,并返回一个 boolean

--handler.has(target,prop)
示例
let p = new Proxy({}, {
    has: function(target, prop) {
        if( prop[0] === '_' ) {
            console.log('it is a private property')
            return false;
        }
        return true;
    }
});

console.log('a' in p);      // true
console.log('_a' in p )     // it is a private property
                            // false
注意

如果目标对象的某一属性本身不可被配置,则该属性不能够被代理隐藏,如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏,否则将会抛出 TypeError

let obj = { a : 1 };

Object.preventExtensions(obj); // 让一个对象变的不可扩展,也就是永远不能再添加新的属性

let p = new Proxy(obj, {
    has: function(target, prop) {
        return false;
    }
});

console.log('a' in p); // TypeError is thrown
posted @ 2022-06-02 15:44  迷茫T亿星  阅读(44)  评论(0)    收藏  举报