# 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)
    }
  }
最后更新: 8/25/2022, 8:27:06 AM