vue3.0 api 的简单学习
vue3.0 出来了一段时间了,但是直到这一段时间,才想着要认真学一学,真是惭愧Vue 组合式 API1、上手下载 官方的 vue3.0 的一个包git clone https://github.com/vuejs/vue-next-webpack-preview.git vue3cd vue3yarnyarn dev2、API 学习1、setup函数这个函数完全就是 vue3.0 的核心了,也是
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) {
...
},
}
- 这个函数传入两个参数,分别为 props 和 context
- props 为 父组件传递的参数,而 context 为 attrs, emit, slots
- props 是响应式的,但是不可以 使用 解构或者展开,这样会 导致响应式 失败(原因会在第二点讲)
- 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>
- 如上面所示,在使用 ref 定义的值之后,必须使用 xxx.value 才能获得对应的值,而 reactive 则不需要
- reactive 响应化的数据不能使用解构或者展开,要不然会失去响应
那么为什么 props 和 reactive不能解构或者展开,而 ref 之后的数据又必须使用 .value 访问 呢?
- 无论是 Object.defineProperty 还是 proxy,只能对 对象数据保持 响应式
- 如果是一个 基本属性的话,那改变了就是改变了,vue 内部是不能监听到他的变化的
- 所以 在 ref 中,一个 基本类型 变成了对象,而且使用 value 来获取
当 ref 作为 reactive 对象的 property 被访问或修改时,也将自动解套 value 值,其行为类似普通属性
- 当reactive props 结构的结果为 基本类型 ,那么同样 也是失去了 监听的效果
- 但是当 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
- computed 返回的值 就和 ref 一样,都是 需要使用 .value 获取,理由同上
- watch 可以监听一个值,也可以同时监听多个值
- 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)
})
- watchEffect 会在第一时间执行,在执行的同时会收集内部的依赖,和 computed 类似,所以不需要指定依赖
- 正如名称所示,可以执行一些有副作用的函数,比如 ajax 请求
- 例如 使用一个 响应式的参数,参数为 page.page, page.pagesize ,就可以在这里调用
- 这个一般还可以和 onMounted 生命周期组合
④、生命周期钩子函数
import { onMounted } from 'vue'
export default {
setup() {
onMounted(() => {
const dom = document.querySelector('img')
console.log(dom)
})
...
},
}
上一张 官方图
⑤、依赖注入
- 可以使用 ref 来保证注入的值是响应的
- 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;
}
更多推荐
所有评论(0)