vue3.0 出来了一段时间了,但是直到这一段时间,才想着要认真学一学,真是惭愧  Vue 组合式 API

1、上手

下载 官方的 vue3.0 的一个包

git clone https://github.com/vuejs/vue-next-webpack-preview.git vue3

cd vue3

yarn

yarn dev

2、API  学习

①、setup函数

这个函数完全就是 vue3.0 的核心了,也是所有函数的入口

export default {
  setup(props, context) {
    ...
  },
}
  1. 这个函数传入两个参数,分别为 props 和 context
  2. props 为 父组件传递的参数,而 context 为 attrs, emit, slots
  3. props 是响应式的,但是不可以 使用 解构或者展开,这样会 导致响应式 失败(原因会在第二点讲)
  4. context 可以使用解构,slots 相当于以前的 $slots ,emit 相当于以前的 $emit,attrs 则是在 组件标签上的内容

props 和 attrs 的区别:

如下面代码 所示,name属性 在 props 参数对象中定义了的,就会进入 props 里面,否则 其他在组件标签上的内容会进入 attrs 里面

export default {
  emits: ['close'],
  props: {
    name: String
  },
  setup(props, {attrs, emit, slots}) {
    emit('close')
    console.log(props, attrs, emit, slots)
  }
}

②、ref 和 reactive

这两个都是构建响应式对象的 函数,但是 也有着显著的区别

<template> 
 <p>{{state.username}}</p>
  <p>{{state.password}}</p>
  <p>{{count}}</p>
</template>
<script>
import {
  ref,
  reactive
} from 'vue'

export default {
 setup () {
  const count = ref(0)
  const inc = () => {
    count.value++
  }
  const state = reactive({
    username: 'jack',
    password: 'rose'
  })

  return {
    count,
    inc,
    state
  }
}
</script>
  1. 如上面所示,在使用 ref 定义的值之后,必须使用 xxx.value 才能获得对应的值,而 reactive 则不需要
  2. reactive 响应化的数据不能使用解构或者展开,要不然会失去响应

那么为什么 props 和 reactive不能解构或者展开,而 ref 之后的数据又必须使用 .value 访问 呢?

  1. 无论是 Object.defineProperty 还是 proxy,只能对 对象数据保持 响应式
  2. 如果是一个 基本属性的话,那改变了就是改变了,vue 内部是不能监听到他的变化的
  3. 所以 在 ref 中,一个 基本类型 变成了对象,而且使用 value 来获取
  4. 当 ref 作为 reactive 对象的 property 被访问或修改时,也将自动解套 value 值,其行为类似普通属性

  5. 当reactive props 结构的结果为 基本类型 ,那么同样 也是失去了 监听的效果
  6. 但是当 reactive 内部的值 是一个对象的话,那么 解构或者展开 依旧保持 响应,这是内部处理了 深度响应的结果

export default function () {
  console.log('counter this', this)
  const count = ref(0)
  const inc = () => {
    count.value++
  }
  const state = reactive({
    username: {
      firstname: 'smith'
    },
    password: 'rose'
  })

  return {
    count,
    inc,
    ...state  // 这里展开出来的 username.firstname 依旧是响应式的
  }
}

reactive 函数可以使用  toRef 或者 toRefs 进行解构

  const state = reactive({
    username: {
      firstname: 'smith'
    },
    password: 'rose'
  })

  const stateAsRefs = toRefs(state)
  const passwordref = toRef(state, 'password')

③、computed、readonly、watch、watchEffect

  1. computed 返回的值 就和 ref 一样,都是 需要使用 .value 获取,理由同上
  2. watch 可以监听一个值,也可以同时监听多个值
  3. readonly 返回一个只读代理,即使是对象里面的对象,也是 readonly 的
export default function () {
  const count = ref(0)
  const count2 = ref(0)
  const double = computed(() => count.value * 2)
  const state = reactive({
    username: {
      firstname: 'smith'
    },
    password: 'rose'
  })
  const copy = readonly(state)  // 即使是 username.firstname 也是只读的
  watch(count, (value) => {   // 监听 ref
      console.log(value, obj.double)
  })
  watch(() => state.password, (value) => { // 监听 state
      ...
  })
  watch([count, count2], ([countNow, count2Now], [countPrev, count2Prev]) => { // 监听 多个数据源
      ...
  })
  return {
    double,
  }
}

 如果想要watch 函数立即执行的话,就可以使用 watchEffect

    watchEffect(() => {
      console.log('watch', count.value)
    })
  1. watchEffect 会在第一时间执行,在执行的同时会收集内部的依赖,和 computed 类似,所以不需要指定依赖
  2. 正如名称所示,可以执行一些有副作用的函数,比如 ajax 请求
  3. 例如 使用一个 响应式的参数,参数为 page.page, page.pagesize ,就可以在这里调用
  4. 这个一般还可以和 onMounted 生命周期组合

④、生命周期钩子函数

import { onMounted } from 'vue'
export default {
  setup() {
    onMounted(() => {
      const dom = document.querySelector('img')
      console.log(dom)
    })
   ...
  },
}

上一张 官方图

⑤、依赖注入

  1. 可以使用 ref 来保证注入的值是响应的
  2. inject 函数可以有第二个值,也就是默认值
// App.vue

import { provide,  } from 'vue'
import Modal from './components/Modal.vue';
export default {
  components: {
    Modal
  },
  setup(props, context) {
    const refobj = ref({
      name: 'jack'
    })
    provide('judgeRules', {name: 'required'})  // 注入
    provide('judgeRules2', refobj)
    ...
  },
}
// Modal.vue
import { inject } from 'vue'
export default {
  emits: ['close'],
  props: {
    name: String
  },
  setup(props, {attrs, emit, slots}) {
    emit('close')
    const judgeRules = inject('judgeRules', {})   // 接受,第二个参数为默认值
    console.log('inject', judgeRules)
  },
}

⑥、模板refs

套用官方的一句话:当使用组合式 API 时,reactive refs 和 template refs 的概念已经是统一的。

说到这里,就不能不提 这里和 react 的 ref 惊人的相似性了

<template>
    <Modal ref="module" @close="handleClose" name="jacl" age="1">
      <p>hhh</p>
    </Modal>
  
</template>

<script>
import { ref } from 'vue'
import Modal from './components/Modal.vue';
export default {
  components: {
    Modal
  },
  setup(props, context) {
    const module = ref(null)
    onMounted(() => {
      console.log(module)
    })
    return { module}
  },
}
</script>

⑦、自定义 ref

反正都提到了 react ,也提到了 ref,那就不能不提一下 自定义的 ref 了,在 react叫 自定义 hook

// counter.js
import {
  ref,
  computed,
  reactive
} from 'vue'

export default function () {
  const count = ref(0)
  const inc = () => {
    count.value++
  }
  const state = reactive({
    username: 'jack',
    password: 'rose'
  })
  const double = computed(() => count.value * 2)
  return {
    count,
    inc,
    double,
    state
  }
}
// App.vue

import counter from './js/counter'
export default {
  components: {
    Modal
  },
  setup(props, context) {
    const {
        count,
        inc,
        double,
        state
    } = counter()
    return { count, inc, double, state }
  }
}

3、响应式API深入理解

很多人很自然的认为 vue3.0 的响应式 就是 Proxy 了,其实并不是

function ref(value) {
    return createRef(value);
}
function createRef(rawValue, shallow = false) {
    if (isRef(rawValue)) {
        return rawValue;
    }
    let value = shallow ? rawValue : convert(rawValue);
    const r = {
        __v_isRef: true,  // 标注当前就是 一个 ref
        get value() {     // 使用了 js object 中的 get set 来对 数据进行响应式的处理
            track(r, "get" /* GET */, 'value');
            return value;
        },
        set value(newVal) {
            if (hasChanged(toRaw(newVal), rawValue)) {
                rawValue = newVal;
                value = shallow ? newVal : convert(newVal);
                trigger(r, "set" /* SET */, 'value',  { newValue: newVal } );
            }
        }
    };
    return r;
}

在 ref 函数中,处理基本类型 使用了 es6 中 对 对象的扩展方法, get 和 set 来进行响应式处理,同理,computed 也进行了相应的处理

function computed(getterOrOptions) {
    。。。
    computed = {
        __v_isRef: true,
        ["__v_isReadonly" /* IS_READONLY */]: isFunction(getterOrOptions) || !getterOrOptions.set,
        // expose effect so computed can be stopped
        effect: runner,
        get value() {
            if (dirty) {
                value = runner();
                dirty = false;
            }
            track(computed, "get" /* GET */, 'value');
            return value;
        },
        set value(newValue) {
            setter(newValue);
        }
    };
    return computed;
}

而只有在对象响应式函数 reactive 中,使用了 proxy

function reactive(target) {
    // if trying to observe a readonly proxy, return the readonly version.
    if (target && target["__v_isReadonly" /* IS_READONLY */]) {
        return target;
    }
    return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers);
}
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {
    if (!isObject(target)) { // 如果不是对象的话,就报错
        {
            console.warn(`value cannot be made reactive: ${String(target)}`);
        }
        return target;
    }
    // target is already a Proxy, return it.
    // exception: calling readonly() on a reactive object
    if (target["__v_raw" /* RAW */] &&
        !(isReadonly && target["__v_isReactive" /* IS_REACTIVE */])) {
        return target;
    }
    // target already has corresponding Proxy
    const reactiveFlag = isReadonly
        ? "__v_readonly" /* READONLY */
        : "__v_reactive" /* REACTIVE */;
    if (hasOwn(target, reactiveFlag)) {
        return target[reactiveFlag];
    }
    // only a whitelist of value types can be observed.
    if (!canObserve(target)) {
        return target;
    }
    const observed = new Proxy(target, collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers);
    def(target, reactiveFlag, observed);
    return observed;
}

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐