# vue3+TS搭建知乎
<template>
<img alt="Vue logo" src="./assets/logo.png">
<button @click="fun">{{status}}</button>
<div @click="changecount">count-{{count}}</div>
<div>{{double}}</div>
</template>
<script lang="ts">
import { defineComponent,ref,computed} from 'vue';
export default defineComponent({
name: 'App',
setup(){
// console.log(this,"this")
const count =ref(0)
const double =computed(()=>{
return count.value*2
})
function changecount(){
// console.log(this)
console.log(count)
count.value++
}
return {
count,
changecount,
double
}
},
data(){
return {
status:1
}
},
methods:{
fun(){
++this.status
}
}
});
</script>
# reactive 和 toRefs
reactive相对于ref可以处理多个元素,同时使用定义在reactive里的元素时,不需要像ref那也修改value,可以直接修改该值。但是它的问题就是在模板语法中需要加上定义的集合名,如data.xxx才可以. 不过可以借助toRefs解决。
<template>
<button @click="data.changecount">{{data.count}}</button>
<div>{{data.double}}</div>
</template>
<script lang="ts">
import { defineComponent,computed,reactive} from 'vue';
interface DataProps{
count:number;
double:number;
changecount:()=>void
}
export default defineComponent({
name: 'App',
setup(){
const data:DataProps=reactive({
count:1,
double:computed(()=>{
return data.count*2
}),
changecount:()=>{
data.count++
}
})
return {
data
}
},
});
</script>
<template>
<button @click="changecount">{{count}}</button>
<div>{{double}}</div>
</template>
<script lang="ts">
import { defineComponent,computed,reactive,toRefs} from 'vue';
interface DataProps{
count:number;
double:number;
changecount:()=>void
}
export default defineComponent({
name: 'App',
setup(){
const data:DataProps=reactive({
count:1,
double:computed(()=>{
return data.count*2
}),
changecount:()=>{
data.count++
}
})
const refData=toRefs(data)
return {
...refData
}
},
});
</script>
# vue2为什么不直接支持添加新属性在data中
vue3可以不借助$set直接修改对象和数组
<template>
<button @click="changecount">{{count}}</button>
<div>{{double}}</div>
<div v-for="(item,index) in arr" :key="index">{{item}}</div>
<span>{{person.name}}</span>
</template>
<script lang="ts">
import { defineComponent,computed,reactive,toRefs} from 'vue';
interface DataProps{
count:number;
double:number;
changecount:()=>void;
arr:number[];
person:{name?:string}
}
export default defineComponent({
name: 'App',
setup(){
const data:DataProps=reactive({
count:1,
double:computed(()=>{
return data.count*2
}),
changecount:()=>{
data.count++
},
arr:[1,2,3],
person:{}
})
data.arr[2]=1000
data.person.name=`123`
const refData=toRefs(data)
return {
...refData
}
},
});
</script>
# vue3生命周期
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured //抓取报错信息
//新增其他的两个钩子函数
onRenderTracked
onRenderTriggered
生命周期函数可以写在setup中,需引入,同时也可以在setup外按照原先的vue2写法写生命周期函数
<template>
<button @click="changecount">{{count}}</button>
<div>{{double}}</div>
<div v-for="(item,index) in arr" :key="index">{{item}}</div>
<span>{{person.name}}</span>
</template>
<script lang="ts">
import {
defineComponent,
onMounted,
onUpdated
} from 'vue';
export default defineComponent({
name: 'App',
setup() {
onMounted(() => {
console.log(111)
})
onUpdated(()=>{
console.log(222)
})
},
mounted() {
console.log(333)
},
updated(){
console.log(444)
}
});
</script>
# watch在vue3的用法
警告
1 A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.
如监听data.count 这种形式写进去会报错,所以可 getter/effect function ,使用reactive则看不到具体的信息,返回的是 proxy
<template>
<button @click="changecount">{{count}}</button>
<button @click='changenamefun'>执行watch</button>
<div>{{double}}</div>
<div v-for="(item,index) in arr" :key="index">{{item}}</div>
<span>{{person.name}}</span>
</template>
<script lang="ts">
import {
defineComponent,
ref,
computed,
reactive,
toRefs,
watch
} from 'vue';
interface DataProps {
count: number;
double: number;
changecount: () => void;
arr: number[];
person: {
name ? : string
}
}
export default defineComponent({
name: 'App',
setup() {
const data: DataProps = reactive({
count: 1,
double: computed(() => {
return data.count * 2
}),
changecount: () => {
data.count++;
data.arr.push(1)
},
arr: [1, 2, 3],
person: {}
})
const changename=ref('')
const changenamefun=()=>{
changename.value +='hello'
}
watch([changename,()=>data.count],(newV,oldV)=>{
console.log(newV,oldV)
console.log(changename.value)
})
data.arr[2] = 1000
data.person.name = `123`
const refData = toRefs(data)
return {
...refData,
changename,
changenamefun
}
},
});
</script>
# vue3 compositon api写法
可复用,可添加别名,且逻辑清晰
import useEventMouse from '@/hooks/useEventMouse'
......
const {x,y}=useEventMouse()
import {ref,onMounted,onUnmounted} from 'vue'
export default function useEventMouse(){
const x=ref(0)
const y=ref(0)
const clicke =(e:MouseEvent)=>{
x.value =e.pageX
y.value =e.pageY
}
onMounted(()=>{
document.addEventListener('click',clicke)
})
onUnmounted(()=>{
document.removeEventListener('click',clicke)
})
return {
x,y
}
}
# emits
vue3多了个emits对调用之前进行参数判断,可以控制返回值类型等等
emits:{
'x1-n':null,
//'x1-n':(payload:any)=>{
// return palyload.type === 'close'
//}
}
.......
setup(props,context){
context.emit('x1-n')
}
<template>
账号:<input type="text" v-model="username" /><br>
密码:<input type="password" v-model="password" /><br>
<button @click="doLogin">登录</button>
</template>
<script>
export default {
// emits参数验证
emits:{
submit: ({username,password}) => {
if(username.length != '' && password.length != ''){
return true
}else{
return false
}
}
},
data(){
return {
username: '',
password: '',
}
},
methods:{
doLogin(){
this.$emit('submit', {
username: this.username,
password: this.password
})
}
}
}
</script>
export default {
emits: ["hide"],//如果不需要约束条件,就可以写成数组形势
setup(_,{emit}) {
function close(){
emit("hide","数据")
}
return { close }; // 记得return出去.
},
};
# Vue3中废弃组件事件的$on,$off 和 $once 实例方法
在 2.x 中,Vue 实例可用于触发通过事件触发 API 强制附加的处理程序 ($on,$off 和 $once),这用于创建 event hub,以创建在整个应用程序中使用的全局事件侦听器
// eventHub.js
const eventHub = new Vue()
export default eventHub
// ChildComponent.vue
import eventHub from './eventHub'
export default {
mounted() {
// 添加 eventHub listener
eventHub.$on('custom-event', () => {
console.log('Custom event triggered!')
})
},
beforeDestroy() {
// 移除 eventHub listener
eventHub.$off('custom-event')
}
}
// ParentComponent.vue
import eventHub from './eventHub'
export default {
methods: {
callGlobalCustomEvent() {
eventHub.$emit('custom-event') // 如果ChildComponent mounted,控制台中将显示一条消息
}
}
}
Vue3.x推荐使用外部库mitt来代替 $on $emit $off
# mitt
cnpm i mitt --save
// emit 不是单例
const m1 = mitt();
const m2 = mitt();
m1 === m2; // false
// 多个mitt之间可以实现合并,合并后的mitt的all属性指向的是同一个内存地址
const m3 = mitt(m2.all);
m3.all === m2.all; // true
m2.on('hi', () => { console.log('我是m2'); });
m3.emit('hi'); // 我是m2
m2.on('*', () => { console.log('我是公共的'); });
m2.emit('hi');
// 我是m2
// 我是公共的
# Suspense内置组件处理异步
- xxx组件返回的是个promise对象
- 使用
<Suspense></Suspense>包裹所有异步组件相关代码 <Suspense></Suspense>下<template #default></template>插槽包裹异步组件<Suspense></Suspense>下<template #fallback></template>插槽包裹渲染异步组件之前的内容
<template>
<div id="app">
<Suspense>
<template #default>
<Async></Async>
</template>
<template #fallback>
<h1>Loading...</h1>
</template>
</Suspense>
</div>
</template>
<script lang="ts">
// import { ref } from "vue";
import Async from "./components/Async.vue";
export default {
name: "App",
components: {
Async
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
# vue2和vue3全局修改
- 全局配置:Vue.config ->app.config
- 注册组件: Vue.component->app.component
- 注册指令: Vue.directive->app.directive
- 扩展类: Vue.mixin->app.mixin
- 全局插件: Vue.use ->app.use
其他不会修改vue全局行为的则是从vue上拿出来,以es6模块化形式导入使用,更好的方便tree-shaking
# vue3中的事件event
const handle = (e:MouseEvent) => {
}
# vue3中使用this.$refs相同功能
因为setup中不能直接用this,所以可以定义ref为null但是定义的名字要和标签上的ref一模一样,然后导出即可
<template>
<div class="dropdown" ref="dropdownRef">
</div>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
name: 'Dropdown',
setup() {
const dropdownRef = ref<null | HTMLElement>(null)
return {
dropdownRef
}
}
})
</script>
# vue3之vue-router用法
import { createRouter, createWebHistory } from 'vue-router'
import axios from 'axios'
import Home from './views/Home.vue'
import Login from './views/Login.vue'
import Signup from './views/Signup.vue'
import ColumnDetail from './views/ColumnDetail.vue'
import CreatePost from './views/CreatePost.vue'
import PostDetail from './views/PostDetail.vue'
import store from './store'
const routerHistory = createWebHistory()
const router = createRouter({
history: routerHistory,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login,
meta: { redirectAlreadyLogin: true }
},
{
path: '/signup',
name: 'signup',
component: Signup,
meta: { redirectAlreadyLogin: true }
},
{
path: '/create',
name: 'create',
component: CreatePost,
meta: { requiredLogin: true }
},
{
path: '/column/:id',
name: 'column',
component: ColumnDetail
},
{
path: '/posts/:id',
name: 'post',
component: PostDetail
}
]
})
router.beforeEach((to, from, next) => {
const { user, token } = store.state
const { requiredLogin, redirectAlreadyLogin } = to.meta
if (!user.isLogin) {
if (token) {
axios.defaults.headers.common.Authorization = `Bearer ${token}`
store.dispatch('fetchCurrentUser').then(() => {
if (redirectAlreadyLogin) {
next('/')
} else {
next()
}
}).catch(e => {
console.error(e)
store.commit('logout')
next('login')
})
} else {
if (requiredLogin) {
next('login')
} else {
next()
}
}
} else {
if (redirectAlreadyLogin) {
next('/')
} else {
next()
}
}
})
export default router
- useRouter
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { useRouter } from 'vue-router'
export default defineComponent({
name: 'Login',
setup() {
const emailVal = ref('')
const router = useRouter()
router.push('/')
}
return {
emailVal
}
}
})
</script>
- useRoute
<script lang="ts">
import { defineComponent} from 'vue'
import { useRoute } from 'vue-router'
export default defineComponent({
setup() {
const route = useRoute()
const currentId = route.params.id
}
})
</script>
# vue3之vuex
<script lang="ts">
import { defineComponent, computed, onMounted } from 'vue'
import { useStore } from 'vuex'
import { GlobalDataProps, ColumnProps } from '../store'
export default defineComponent({
setup() {
const store = useStore<GlobalDataProps>()
onMounted(() => {
store.dispatch('fetchColumn', currentId)
})
const list = computed(() => store.getters.getPostsByCid(currentId))
return {
list
}
}
})
</script>
getters不仅可以返回一个固定的值,也可以返回函数
getters: {
getColumnById: (state) => (id: string) => {
return state.columns.data[id]
},
getPostsByCid: (state) => (cid: string) => {
return objToArr(state.posts.data).filter(post => post.column === cid)
}
}