组件实例中的使用
setup
- 所以Vue3中API的入口和出口,Composition API都在写在其里面,Options API可以共存写在其中,但推荐使用全Composition API的写法
- setup在beforeCreate,create之前创建,因此没有this
- 在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
- setup有两个参数,第一个是props,用于接收传递的属性对象,可以通过watchEffect监听,第二个参数是context,其包含attrs,slots,emit属性
ref
const r = ref("hello")
console.log(r) // RefImpl {_rawValue: "hello", _shallow: false, __v_isRef: true, _value: "hello"}
console.log(r.value) // hello
- ref 接受一个参数值并返回一个响应式且可改变的 ref 对象
- ref 对象拥有一个指向内部值的单一属性 .value
- 在setup内取值需要使用.value的形式
- setup返回的ref在模板中会自动解开,可以在模板直接使用{{r}}取值
- ref的本质是拷贝,与原始数据无关联性
- ref接受一个参数,一般是基本类型值(String 、Nmuber 、Boolean 等)或单值对象。如果传入的参数是一个对象,将会调用 reactive 方法进行深层响应转换(此时访问ref中的对象会返回Proxy对象,说明是通过reactive创建的);引用类型值(Object 、Array)使用reactive
reactive
const r2 = reactive("hello")
console.log(r2) // value cannot be made reactive: hello
const r3 = reactive({name:"hello"})
console.log(r3) // Proxy {name: "hello"}
console.log(r3.name) // hello
- reactive 接收一个普通对象然后返回该普通对象的响应式代理(proxy)
- 需要传递一个对象,否则会抛出异常
- setup返回出去后在模板中只能拿到其对象 // {name:“hello”}
reactive和ref都是vue3中用来创建响应式数据的api,作用等同于在vue2中的data,不同的是他们使用了ES6的PorxyAPI解决了vue2 defineProperty 无法监听数组和对象新增属性的痛点
toRef
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
const rrr = {foo: 1}
const rrr2 = ref(rrr.foo)
console.log(rrr2) // RefImpl {_rawValue: 1, _shallow: false, __v_isRef: true, _value: 1}
console.log(rrr.foo) // 1
console.log(rrr2.value) // 1
rrr2.value++
console.log(rrr.foo) // 1
console.log(rrr2.value) // 2
const rrr3 = toRef(rrr,'foo')
console.log(rrr3) // ObjectRefImpl {_object: {…}, _key: "foo", __v_isRef: true}
console.log(rrr.foo) // 1
console.log(rrr3.value) // 1
rrr3.value++
console.log(rrr.foo) // 2
console.log(rrr3.value) // 2
- toRef 可以用来为一个 reactive 对象的属性创建一个 ref
- 这个 ref 可以被传递并且能够保持响应性
- toRef的本质是引用关系,与原始数据存在关联性
- toRef当数据发生改变时,界面不会自动更新
toRefs-解构响应式对象数据
toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,相当于变成一个个的ref()
data
<p>{{name}}</p> // trist
data() {
return {
name: "trist",
age: 22,
sex: "boy"
}
}
console.log(this.age) // 22
<p>{{name}}</p> // trist
import { reactive,toRefs } from 'vue';
setup {
const data = reactive({
name: "trist",
age: 22,
sex: "boy"
})
console.log(data.age) // 22
return {
...toRefs(data)
}
}
如上是data中属性在vue3内的写法
- 首先因为Vue3组合式API的写法,我们需要从vue中按需导入我们所需要的API方法
- 我们使用reactive定义复杂数据类型的响应式数据,用一个总对象的写法,把所需的data数据写在其中
- 在setup中,若我们需要操作data内的数据,我们通过data进行访问,如console.log(data.age)
- 我们使用展开运算符使得定义在大对象内的数据进行解套,{{data.name}} => {{name}} 使用展开运算符便于我们取值,借助toRefs()函数可以将reactive()创建出来的响应式对象,转换为普通对象,只不过这个对象上的每个属性节点,都是ref()类型的响应式数据,使得响应式对象能进行解构并不丢失响应性
methods
methods {
add() {
data.age++
}
}
setup() {
...
const add = () => {
data.age++
}
return {
...,
add
}
}
- 定义方法函数,如在外部template中需要触发该方法,记得return出去
生命周期
import { onMounted } from 'vue';
setup(props, ctx) {
...
onMounted(() => {
ctx.emit('loading', false);
data.age++
add()
});
}
- 将所需的生命周期钩子导入使用就行了
watch
watch
import { ref, watch } from 'vue';
let a = ref(1);
watch(a,(news, olds) => {
console.log('监听a的变化', a.value, '新值', news, '旧值', olds);
},{ immediate: true }); //immediate:true 可以默认执行一次
watchEffect
watchEffect 监听器的升级版本,立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
import { reactive, toRefs, ref, watch, watchEffect } from 'vue';
let aa = reactive({num:10})
setTimeout(()=> {
aa.num++
},1000)
watchEffect(() => {
console.log(aa.num) // 10 // 11
});
computed
computde返回值为一个ref的实例,能在模板中自动解开
1. 使用计算属性:
import { computed } from 'vue';
const total = computed(() => {
let sum = 0
data.cartList.forEach(item => {
sum += item.goodsCount * item.sellingPrice
})
return sum
})
2. 获取vuex的值:
import { mapState } from 'vuex';
computed: {
...mapState({
isLogin: state => state.user.isLogin
})
}
import { reactive, toRefs, computed } from 'vue';
import { useStore } from 'vuex';
setup(props, ctx) {
const store = useStore()
// 直接定义在大对象中
const data = reactive({
isLogin: computed(() => store.state.user.isLogin)
})
const count = computed(() => {
return store.state.cartCount
})
return {
...toRefs(data),
count
}
}
useStore是vuex的入口
我们可以从useStore内拿到vuex的属性和方法
父子组件间传值
父组件:
<template>
<div class="home">
<!-- 父子组件传值 -->
<son num="66" name="trist" :age="age" @baba="getSon"><a>我是插槽</a></son>
</div>
</template>
<script>
import son from './son.vue';
import { reactive, toRefs } from 'vue';
import { useRoute, useRouter } from 'vue-router';
export default {
name: 'home',
components: {
son
},
setup(props, ctx) {
// 获取当前路由信息
const route = useRoute();
// 全局路由的实例
const router = useRouter();
const state = reactive({
name: 'trist',
age: 22,
sex: 'boy'
});
// 监听子组件事件
const getSon = val => {
console.log(val);
}
return {
...toRefs(state),
getSon
};
}
};
</script>
子组件:
<template>
<div>
子组件{{ son }}
<p style="background-color: #CCCCCC;"><slot></slot></p>
<grandson></grandson>
</div>
</template>
<script>
import { ref } from 'vue';
import grandson from './grandson.vue';
export default {
components: {
grandson
},
// 定义要接收的值,可以设置默认值,验证规则等
props: {
num: {
type: String
},
},
// 如果要接受父组件里面传过来的,就要在setup里面书写参数,第一个属性,就是这个参数
setup(props, ctx) {
console.log(props)
console.log(ctx.slots)
console.log(ctx.attrs)
const son = ref(props.num);
// attrs用于接收除props定义以外的属性
const attr = ctx.attrs.name
console.log(attr)
// 传值给父组件
ctx.emit('baba', '我是子组件');
return {
son
};
}
};
</script>
v-model
回顾:vue中的数据绑定
- 插值(双大括号),将文本动态渲染在模板中
- v-bind(😃,对实例中的data进行单向的数据绑定
- v-model:双向数据绑定
原理:
v-model实际上是一个语法糖,它先使用v-bind对数据进行单向绑定,再监听目标对象,通过$event.target触发从而改变数据的值,实现数据的双向绑定
Vue2.x中:
v-model允许自定义prop和event,但单个组件只允许使用一个model,若如果开发者出于不同的目的需要使用其他的 prop,他们就不得不使用 v-bind.sync
// father
<son v-model="msg"></son>
// son
model: {
prop: "get",
event: "set"
}
props {
get: String
}
this.$emit('set','set msg to father')
Vue3.x中:
Vue3将v-model与 .sync 进行了整合,并淘汰了 .sync 的用法,允许用户定义多个model
// father
<son v-model:name="name" v-model:age="age"></son>
// son
props: {
name: String,
age: Number
}
setup(props,ctx) {
// 接受父组件的值
console.log(props.name)
console.log(props.age)
// 改变父组件的值
ctx.emit('update:name','trist')
ctx.emit('update:age', '22')
}
框架配置中的使用
创建Vue实例
在2.X版本中创建一个vue 实例是通过 new Vue()来实现的,到了3.X中则是通过使用createApp这个 API返回一个应用实例,并且可以通过链条的方式继续调用其他的方法
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/router.js'
import store from './store'
// 创建实例
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app') // 挂载
// 也可以这样写
createApp(App).use(store).use(router).mount('#app')
参数 | 用法 |
---|---|
config(配置) | 包含应用配置的对象。同Vue2中config。提供统一配置。 |
directive(指令) | 注册或检索全局指令。指令是一组具有生命周期的钩子。 |
mixin(混入) | 在整个应用范围内应用混入。一旦注册,它们就可以在当前的应用中任何组件模板内使用它。插件作者可以使用此方法将自定义行为注入组件。不建议在应用代码中使用。 |
mount(挂载) | 应用实例的根组件挂载在提供的 DOM 元素上。同Vue2中的el。 |
provide(搭配Inject) | 设置一个可以被注入到应用范围内所有组件中的值。组件应该使用 inject 来接收 provide 的值。provide 和 inject 绑定不是响应式的。 |
unmount(卸载) | 在提供的 DOM 元素上卸载应用实例的根组件。 |
use(使用) | 安装 Vue.js 插件。在同一个插件上多次调用此方法时,改插件将仅安装一次。 |
component(组件) | 注册或检索全局组件。注册还会使用给定的 name 参数自动设置组件的 name。 |
router
import {
createRouter,
createWebHistory
} from 'vue-router'
// 配置路由信息
const routes = [
{
path: '/test',
name: 'Test',
component: () => import('@/views/test.vue'),
meta: {
title: ""
}
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
scrollBehavior(to, from, savedPosition) {
return {
x: 0,
y: 0
}
}
})
export default router
- 新的 history 配置取代 mode,“history”: createWebHistory()
“hash”: createWebHashHistory() - 移动了 base 配置,base 配置被作为 createWebHistory (其他 history 也一样)的第一个参数传递
- scrollBehavior 中返回的对象与 ScrollToOptions 类似:x 改名为 left,y 改名为 top
import { useRoute, useRouter } from 'vue-router';
// 获取当前路由信息
const route = useRoute();
// 全局路由的实例
const router = useRouter();
console.log(route);
console.log(router);
- List item