浅显易懂vue3

795 阅读4分钟

vue3+vite+ts 浅显易懂语法整理

由于学习vue3到现在一直没有用到项目上,时间久了不写就容易忘记,整理一篇vue3+vite最新项目构建以及语法糖笔记

上手教程

npm install vite@lastext
推荐使用vscode volar(快捷分割真香)

基础语法

生命周期钩子

setup是围绕beforeCreate和created这两个钩子函数运行的,所以不需要定义它们
vue2.0setup
beforeCreatesetup
createdsetup
beforeMountonBefooreMount 挂载之前
mountdonMountd 页面加载完成
beforeUpadteonBeforeUpadte 更新前
upadteonUpadte 更新后
beforeUnmountonBeforeUnmount 组件卸载前
unmountedonUnmounted 组件卸载后
activatedonActivated 活跃前
deactivatedonDeactivated 活跃后
errorCapturedonErrorCaptured 子孙组件异常钩子

setup语法糖

—优势
|——组件引入无需注册
|——写法上不用写setup函数以及export(优化了一下繁杂的写法)
|——没有this
<script setup lang="ts">
    ......
</script>

声明响应式变量

ref 建议基本数据类型
reactive 建议复杂数据类型(推荐)
二者区别:ref获取值需要通过.value
import {ref,reactive} from "vue"
const count = ref(0)
const data = reactive({
    obj:{
        name:'mool'
    },
    arr:[1,2,3]
})
//如果觉得分开声明很杂乱,这边建议使用reactive统一声明(推荐)
const data = reactive({
    count:1,
    obj:{
        name:'mool'
    },
    arr:[1,2,3]
})

typescript

const count = ref<number>(0)

props

defineProps无需引入,defineProps相较与props写法上没有太大出入
//子组件
defineProps({
    msg:String,//无默认值声明
    age:{//有默认值声明
       type:Number,
       default:() => 10
    }
})
//父组件
<child msg="props"/>

typescript

//加上ts限制类型(好处支持多种类型)
defineProps<{
    msg:string|number,//必传属性
    age?:number //可传可不传属性
}>()

emit

defineEmits无需引入,需先声明后通过emit调用
//子组件
const emit = defineEmits(['childEmit'])
emit('childEmit',参数)
//父组件
<child msg="props" @childEmit="emit"/>
const emit = () => {}

typescript

//加上ts限制类型(好处支持多种类型)
//子组件
const emit = defineEmits<{
    (event:'childEmit',data:string):void
    ...
}>()
emit('childEmit',参数)//string类型
//父组件
<child msg="props" @childEmit="emit"/>
const emit = () => {}

useAttrs

获取除了响应式props之外的属性
import {useAttrs} from "vue"
const attr = useAttrs()

useSlots

获取除了响应式props之外的属性
import {useSlots} from "vue"
const slot = = useSlots().default()

expose/ref

2.0通过组件上ref去获取对应组件的实例
3.0有别于需预先通过defineExpose暴露方法以及属性,后声明ref去获取
//子组件
defineExpose({
  child: '子组件',
  fn:(value:string)=>{
    return value
  }
})
//父组件
<child ref="getRef" msg="props" @childEmit="emit"/>
<script setup lang="ts">
import {ref, onMounted} from "vue"
const refs= ref()
onMounted(()=> {
  console.log('ref:', refs)
  console.log('fn:', refs.value.fn('111'))
})
</script>

watch和watchEffect

watch (建议使用,代码更具有可观性)
-惰性监听(可通过配置immediate)
-可以监听多个数据的变化
-参数可以拿到变化前后的值
watchEffect
-每次加载都会执行
-不需要传递监听的参数
-只能获取变化后的值
-可以手动停止监听
-自动收集依赖
import {watchEffect,watch} from "vue"
const name = ref('mool') 
const data = reactive({name:'yaa'})//监听reactive声明的对象需要箭头函数形式
watch([name,()=>data.name],(newVal,oldVal)=>{
  console.log('newVal:', newVal)
  console.log('oldVal:', oldVal)
},immediate:true)//是否加载时执行

watchEffect(()=>{//自动感知收集依赖
  console.log('count1111:',data.name)
})

methods

setup中声明函数类似于js声明函数
function fn (params) {
    console.log(params)
}
const fn = params => { //推荐
     console.log(params)
}

typescript

function fn (params:(string|number)) {
    console.log(params)
}
const fn = (params: (string | number)): (string | number) => { //推荐
  console.log(params)
  return params
}

withDefaults

给props设置默认值
interface Props {
  mag: string,
  age?: number
}

withDefaults(defineProps<Props>(), {
  msg: 'mool',
  age: 10
})

组件通信汇总

- props/emit
- expose/ref
- useAttrs
- v-model
- provide/inject
- vuex

props

跳转props

emit

跳转emit

expose/ref

跳转expose/ref

useAttrs

跳转useAttrs

v-model

  <child v-model:msg="msg" v-model:value="value"></child>  
  const emit = defineEmits(["msg","value"])
  // 用法 
  const handlerClick = () => { emit("update:msg", "新的msg") emit("update:value", "新的value") }

provide/inject

//父组件
import { provide } from "vue"
provide("name", "mool")
//子组件
import { inject } from "vue" 
const name = inject("name")

vuex

跳转vuex

vuex

安装4.+:npm install vuex@next --save
├── store 
  └── index.ts
  └── typeing.ts
  └── getters.ts
  └── mutations.ts
  └── actions.ts

index.ts

import {InjectionKey} from "vue"
import {useStore as baseUseStore, createStore, Store} from "vuex"
import * as type from "./typeing"
import {getters} from "./getters"
import {mutations} from "./mutations"
import {actions} from "./actions"
export const key: InjectionKey<Store<type.State>> = Symbol()
export const store: Store<type.State> = createStore({
  state: {
    username: 'mool',
    role: [],
    menu: [{}]
  },
  getters,
  mutations,
  actions
})

export const useStore = () => {
  return baseUseStore(key)
}

typeing.ts

统一分模块声明state接口,采用接口继承整合接口
interface User {
  username: string
}

interface Role {
  role: object
}

interface Item {
  [index: number]: object
}

export interface State extends User, Role {
  menu: Array<Item>
}

getters.ts

import * as type from "./typeing";

export const getters = {
  GET_NAME: (state: type.State): string => {
    return state.username
  }
}

mutations.ts

import * as type from "./typeing";

export const mutations = {
  SET_NAME(state: type.State, params: string) {
    state.username = params
  }
}

actions.ts

import {State} from "./typeing";

export const actions = {
  DIS_NAME( context: { commit: any, state: State }, params: string) {
    context.commit('SET_NAME', params)
  }
}

用法

// mian.ts
import {store, key} from "./store"
app.use(store, key)
//组件用法
import {useStore} from "../store"
const store = useStore()
store.commit('SET_NAME', 'yaa')
store.dispatch('DIS_NAME', 'MOOL')
store.getters.GET_NAME

router

安装4.+:npm install vue-router@next --save
相比于2.0没太大的区别

index.ts

import {createRouter, createMemoryHistory} from "vue-router";
import Home from "../pages/index.vue"
const routes = [
  {
    path: '/',
    component: Home,
  }
]
const router = createRouter({
  history: createMemoryHistory(),
  routes
})
export default router
//在main.ts中注册
import router from "./router"
app.use(router)

用法

import { useRouter } from "vue-router"
const router = useRouter()
router.push 
...

vite

vite.config.ts

相关配置查看(https://vitejs.dev/config/)
全面些的查看(https://gitee.com/dlhf/admin-ui/blob/main/vite.config.ts)
import {ConfigEnv, defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import {loadEnv} from "vite"
import {resolve} from "path"

export default ({command, mode}: ConfigEnv) => {
  return defineConfig({
    base: './',//打包后的静态资源路径修改
    plugins: [vue()],
    server: {
      host: '0.0.0.0',
      port: Number(loadEnv(mode, process.cwd()).VITE_APP_PORT),
      strictPort: true,//设为 true 时若端口已被占用则会直接退出,而不是尝试下一个可用端口
      https: false,
      open: false,//是否启动时打开浏览器
      proxy: {
        "/api": {
          target: loadEnv(mode, process.cwd()).VITE_APP_BASE_URL,
          changeOrigin: true,
          rewrite: (path) => path.replace(/^/api/, '')
        }
      },
      hmr: {
        overlay: false //屏蔽报错窗口
      }
    },
    resolve: { //设置文件路径
      alias: {
        '@': resolve(__dirname, './src')
      }
    },
    build: {
      outDir: 'dist',
      assetsDir: "assets", //指定静态资源存放路径
      sourcemap: false, //是否构建source map 文件
      chunkSizeWarningLimit: 1500,
      terserOptions: {
        // 生产环境移除console
        compress: {
          drop_console: true,
          drop_debugger: true,
        },
      },
      rollupOptions: {
        output: {
          manualChunks(id) {
            if (id.includes('node_modules')) {
              return id.toString().split('node_modules/')[1].split('/')[0].toString()
            }
          }
        }
      }
    }
  })
}