# 创建项目
- 选择一个你常用的命令进行安装
npm create vite
yarn create vite
# scss安装
- 在项目终端中输入面的命令
npm install sass -d
- 在src/assets文件夹下新建,scss文件夹(文件名称随意),在文件夹下新建main.scss文件
- 在vite.config.js文件中添加配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData:'@import "./src/assets/sass/main.scss";'
}
}
}
})
# 安装路由
- 在终端输入选择的命令
npm install vue-router@next
- 在src下新建ruter文件夹。在ruter文件夹下新建俩个ts文件(index.ts、routes.ts),进行路由配置
//index.ts
import { createRouter, createWebHistory } from 'vue-router'
// 导入路由页面的配置
import routes from './routes';
// 路由参数配置
const router = createRouter({
// 使用hash(createWebHashHistory)模式,(createWebHistory是HTML5历史模式,支持SEO)
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
// 始终滚动到顶部
return { top: 0 };
}
})
// 全局前置守卫,这里可以加入用户登录判断
router.beforeEach((to, from, next) => {
// 继续前进 next()
// 返回 false 以取消导航
next()
})
// 全局后置钩子,这里可以加入改变页面标题等操作
router.afterEach((to, from) => {
const _title = to.meta.title
if (_title) {
window.document.title = _title
}
})
export default router
//routes.ts
const routes = [
{
path: "/",
redirect:"/home"
},
{
path:'/home',
name:"home",
component:()=> import("../views/home.vue")
}
]
export default routes
- main.ts中引入使用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(app).use(router).mount('#app')
# 安装Vuex
- 在终端输入选择的命令
npm install vuex@next
# 安装axios
- 在终端输入选择的命令
npm install axios
# 项目相关命令
"scripts": {
"dev": "vite --host", //显示IP地址
"build": "vue-tsc && vite build",
"preview": "vite preview"
}
# 更换Vue模板支持工具
Vue模板支持的VSCode扩展是Vetur,Vue3建议换成更加友好的Vue-Official
# 生命周期
// 引入需要的
import { onMounted, onUpdated, onUnmounted } from 'vue';
export default {
setup () {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
Vue2 Vue3
beforeCreate() -> 使用 setup()
created() -> 使用 setup()
beforeMount() -> onBeforeMount() 组件挂载到节点上之前执行的函数
mounted() -> onMounted() 组件挂载完成后执行的函数
beforeUpdate() -> onBeforeUpdate() 组件更新之前执行的函数
updated() -> onUpdated() 组件更新完成之后执行的函数
beforeDestroy() -> onBeforeUnmount() 组件卸载之前执行的函数
destroyed() -> onUnmounted() 组件卸载完成后执行的函数
ativated() -> onActivated() <keep-alive>中的组件,会多出两个周期函数,被激活时执行
deactivated() -> onDeactivated() 比如从 A 组件,切换到 B 组件,A 组件消失时执行
errorCaptured() -> onErrorCaptured() 当捕获一个来自子孙组件的异常时激活钩子函数
- 这些生命周期钩子注册函数只能在 setup() 期间同步使用, 因为它们依赖于内部的全局状态来定位当前组件实例(正在调用 setup() 的组件实例)
- 组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除
# 常用方法
# ref的使用
<template>
<div>
<p>{{num1}}</p>
<button @click="num1++">按钮1</button>
<p>{{num2}}</p>
<button @click="hdClick1">按钮2</button>
<p>{{objRef.num}}</p>
<button @click="hdClick2">按钮2</button>
<p ref="op">这是p标签</p>
</div>
</template>
<script lang="ts">
import { ref,Ref,nextTick } from 'vue';
export default {
setup () {
//直接这样定义的不是响应式数据
let num1 = 20;
//上面template中依旧使用{{num}},因为vue在编译模板的时候会自动用.value获取
let num2 = ref(20);
//实质上是Ref<T>类型
//let num2:Ref<number> = ref(20);
const hdClick1 = ()=>{
num2.value++;
console.log(num2.value)
}
//复杂类型数据
let obj = {
num:30
}
let objRef = ref(obj)
const hdClick2 = ()=>{
objRef.value.num++;
console.log(objRef.value.num)
}
console.log(objRef.value.num)
//读取dom的内容
let op = ref(); //读取绑定了ref属性且值为op的标签
console.log("setup",op.value); //undefined
nextTick(()=>{
console.log("setup",op.value); //这是p标签
})
return{
num1,
num2,
hdClick1,
objRef,
hdClick2
}
}
}
</script>
# reactive的使用
<template>
<div>
<p>{{objRet.num}}</p>
</div>
</template>
<script lang="ts">
import { ref,reactive } from 'vue';
export default {
setup () {
let obj = {
num:30
}
let objRet = reactive(obj);
//不需要写value
console.log(objRet.num);
return{
objRet
}
}
}
</script>
# setup的使用
<template>
<div>
<p>{{objRet.num}}</p>
</div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
//不需要export和setup
let obj = {
num:30
}
let objRet = reactive(obj);
</script>
# toRefs的使用
<template>
<div>
<p>{{num}}</p>
<button @click="hdClick">按钮</button>
</div>
</template>
<script lang="ts" setup>
import { reactive,toRefs } from 'vue';
let obj = {
num:30,
list:[10,20,30,40]
}
//在解构reactive()得到的对象的时候,将他们解构成响应式数据
let { num,list } = toRefs(reactive(obj));
const hdClick = () =>{
num.value++;
console.log(num.value);
}
</script>
# watch的使用
<template>
<div>
<p>{{num}}</p>
<button @click="num++">按钮</button>
</div>
</template>
<script lang="ts" setup>
import { reactive,toRefs,watch } from 'vue';
//简单数据使用
let num = ref(20);
watch(num,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
//复杂数据使用
let obj = {
num:30
}
let objRet = reactive(obj);
let { num } = toRefs(objRet);
watch(num,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
watch(()=>objRet.num,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
watch([()=>objRet.num],(newVal,oldVal)=>{
console.log(newVal,oldVal); //返回2个数组
})
//watchEffect的使用
watchEffect(()=>{
//凡是写在这里的数据,只要发生变化,都会触发这里的代码执行
console.log(objRet.num);
})
</script>
# computed的使用
<template>
<div>
<p>{{num1}}</p>
<button @click="num1++">按钮1</button>
<p>{{num2}}</p>
<button @click="num2++">按钮2</button>
</div>
</template>
<script lang="ts" setup>
import { reactive,toRefs,computed } from 'vue';
//简单数据使用
let num = ref(20);
let num1 = computed(()=>{
return num.value *2;
})
//复杂数据使用
let obj = {
num:30
}
let objRet = reactive(obj);
let num2 = computed(()=>{
return objRet.num *2;
})
//其他使用
let data = reactve({
checkList:[false,false,false,false]
})
let {list,checkList} = toRefs(data);
let checkAll = computed({
get(){
//checkList 包含有一个false,就应该返回false
return !data.checkList.includes(false);
},
set(newVal){
//把checkList的所有值都改成newVal
data.checkList=data.checkList.map(()=>newVal);
}
})
</script>
# Teleport的使用
<template>
<!--可以把里面的内容传送到指定标签最后的位置-->
<Teleport to=".app">
<Teleport to="body">
<Teleport to="#aaa">
<p>这是一个P标签</p>
</Teleport>
</template>
<script lang="ts" setup>
import { reactive,toRefs,computed } from 'vue';
//简单数据使用
let num = ref(20);
let num1 = computed(()=>{
return num.value *2;
})
//复杂数据使用
let obj = {
num:30
}
let objRet = reactive(obj);
let num2 = computed(()=>{
return objRet.num *2;
})
//其他使用
let data = reactve({
checkList:[false,false,false,false]
})
let {list,checkList} = toRefs(data);
let checkAll = computed({
get(){
//checkList 包含有一个false,就应该返回false
return !data.checkList.includes(false);
},
set(newVal){
//把checkList的所有值都改成newVal
data.checkList=data.checkList.map(()=>newVal);
}
})
</script>
# 组件通信
# 父传子
<!--父组件-->
<template>
<!--调用子组件-->
<Chid :arr="state.arr"></Chid>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { reactive,toRefs,ref } from 'vue';
let state =reactive({
arr:[{
name:'小明',
age:18
},{
name:'小红',
age:20
}]
});
</script>
<!--子组件-->
<template>
<table>
<tr v-for="item,index in arr" :key="index">
<td>{{(item as {name:string}).name}}</td>
<td>{{(item as {age:number}).age}}</td>
</tr>
</table>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
defineProps({
arr:{
type:Number,
default:[]
}
})
</script>
# 子传父(子组件调用父组件的方法)
<!--父组件-->
<template>
<!--子组件-->
<Chid @fn="chanNum"></Chid>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { reactive,toRefs,ref } from 'vue';
let num =ref(20);
const chanNum = () =>{
num.value++
}
</script>
<!--子组件-->
<template>
<p>{{num}}</p>
<button @click="hdClick">按钮</button>
</template>
<script lang="ts" setup>
import { defineEmits } from 'vue';
// 子传父的时候需要先定义好emit这个方法
const emit =defineEmits<{
(event: 'fn'):void
}>()
const hdClick = () =>{
//通过$emit('自定义参数名','参数')
emit("fn")
}
</script>
# 子传父(子组件修改父组件的属性)
<!--父组件-->
<template>
<!--子组件-->
<Chid v-model:num="num"></Chid>
</template>
<script lang="ts" setup>
import Child from './Child.vue';
import { reactive,toRefs,ref } from 'vue';
let num =ref(20);
</script>
<!--子组件-->
<template>
<p>{{num}}</p>
<button @click="hdClick">按钮</button>
</template>
<script lang="ts" setup>
import { defineProps,defineEmits } from 'vue';
defineProps({
num:{
type:Number,
default:30
}
})
// 子传父的时候需要先定义好emit这个方法
const emit =defineEmits<{
//update是固定写法,后面的变量是父组件v-model后面这个变量
//n是参数
(event: 'update:num',n:number):void
}>()
let m = props.num
const hdClick = () =>{
m++;
//$emit('上面event的值',要修改成的值)
emit("update:num",m)
}
</script>
# 插槽用法
# 匿名插槽
<template>
<Child>
<a href="#">a标签</a>
<button></button>
</Child>
</template>
<script lang="ts" setup>
import Child from "./Child.vue"
</script>
<template>
<p>子组件</p>
<slot></slot>
</template>
<script lang="ts" setup>
</script>
# 具名插槽
<template>
<Child>
<!--简写:<template #link>-->
<template v-slot:link>
<a href="#">a标签</a>
</template>
<!--简写:<template #btn>-->
<template v-slot:btn>
<button></button>
</template>
</Child>
</template>
<script lang="ts" setup>
import Child from "./Child.vue"
</script>
<template>
<slot name="link"></slot>
<p>子组件</p>
<slot name="btn"></slot>
</template>
<script lang="ts" setup>
</script>
# 插槽作用域
<template>
<Child>
<template #link>
<a href="#">a标签</a>
</template>
<template #btn="scope">
<button>按钮 {{scope.title}}{{scope.num}}</button>
</template>
</Child>
</template>
<script lang="ts" setup>
import Child from "./Child.vue"
</script>
<template>
<slot name="link"></slot>
<p>子组件</p>
<slot name="btn" title="哈哈" :num="num"></slot>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
let num =ref(30)
</script>
# 其他使用
# Vue2和Vue3响应式原理的区别
- Vue2是Object.defineProperty
- Vue3是Proxy代理
# 全局接口
- 定义全局接口
//根目录 -> types -> table.d.ts
interface UerType{
name:string;
age:number;
}
//类型增强
declare var globalVar:string;
declare var globalObj:ObjType;
declare function fn(s:string):void;
- 修改tsconfig.json文件
{
"include":[
……
"types/**/*.d.ts"
]
}
- 在其他文件中使用
let arr = props.arr as UerType[]
//使用类型增强
console.log(globalVar,globalObj)
# 配置项目路径别名
目前ts对@指向src目录的提示是不支持的,vite默认也是不支持的,需要手动配置@符号的指向
- tsconfig.json中添加两项配置
"compilerOptions":{
…
"baseUrl":"./",
"paths":{
// 对应src的路径
//import HelloWorld from '@/components/HelloWorld.vue'
"@/*":[
"src/*"
],
// 对应types的路径
//import { UserType } from "#/table"
"#/*":[
"types/*"
]
}
}
- 在vite.config.ts中添加配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve:{
alias:{
"@":path.join(__dirname,'src'),
"#":path.join(__dirname,'type')
}
}
})
- 需要安装关于node这个库的ts声明配置
npm i -D @type/node
# 定义接口返回值类型
interface AdminLoginData{
username:string;
password:string;
}
interface Result<T>{
code:number;
data:T;
message:string;
}
interface AdminLoginRes{
token:string
}
export const adminLoginApi = (data:AdminLoginData) : Promise<Result<AdminLoginRes>> =>
request.post('/admin/login',data);
常见问题 →