vite-ts-vue3笔记

发布时间:2023年12月30日

vite

npm create vite@latest
npm run dev 开始预览时 使用模块 是 es module
打包生产环境 使用rollup

webpack基于node
vite 基于 原生 module和 rollup

vite的基础配置

根目录下的vite.config.js

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    host: '0.0.0.0',
    port: 3000,
    open: true,
    proxy: {
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
      '@c': fileURLToPath(new URL('./src/components', import.meta.url)),
      '@a': fileURLToPath(new URL('./src/api', import.meta.url)),
      '@v': fileURLToPath(new URL('./src/views', import.meta.url))
    }
  }
})

注意:
路径别名 如果vite 使用ts 还需要在 ts.config.json配置该路径

{
  "extends": "@vue/tsconfig/tsconfig.web.json",
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@a/*": ["./src/api/*"],
      "@c/*": ["./src/components/*"],
      "@v/*": ["./src/views/*"]
    },
    "ignoreDeprecations": "5.0"
  },

  "references": [
    {
      "path": "./tsconfig.node.json"
    }
  ]
}

接口的导出导入 前面加 type关键字

interface Type1 {
  name: string
}
interface Type2 {
  aaa: number
}
interface Type3 {
  bbb: string
}
// 导出时  加上type关键字
export type {
  Type1,
  Type2,
  Type3
}

导入type

import type { xxx, xx } from '路径'
import type Xxx from '路径'

vue3组合式api

支持 vue2 选项式api options api

{
  data(){
    return {}
  },
  methods: {},
  watch: {},
  computed: {}
}

组合式api composition Api是vue3新增语法, 函数式编程

组合式api setup

1 组合式api 入口函数式 setup 组件一切业务要定义在setup函数中
(数据、方法、生命周期钩子、watch/计算属性)
2 触发在 实例创建完成之前 这里不能通过 this 访问实例
3 返回对象,对象里面属性和方法 暴露给模板, setup中定义变量或者函数,模板想要中 return出去

{
  setup(){
    const msg = '你好世界';
    const fn = () => {

    }
    return {
      msg,
      fn
    }
  }
}
  • setup 访问 props
{
  props: {
    title: {
      type: String,
      required: true
    }
  },
  // setup第一个参数就是props
  setup(props){}
}
  • setup第二个参数 上下文

  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit) 记住emit即可 这是触发自定义事件的方法
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

定义响应式数据

ref定义响应式数据

  • 定义响应式数据
export default {
  setup () {
    /* 
      原理:
        定义一个包装对象,将 值 定义到包装对象的 value属性中
        利用Object.defineProperty 劫持 value属性
      注意:
        在js中使用 一定要加value 模板中可以省略value
    */
    const msg = ref('你好vue3');
    const changeMsg = () => {
      msg.value = '值改变了'
    }
    return {
      msg,
      changeMsg
    }
  }
}
  • 获取dom
import { ref, onMounted } from 'vue'
export default {
  setup () {
    // 定义容器 存储需要获取dom
    const btnRef = ref(null);
    
    onMounted(() => {
      console.log(btnRef.value);
    })
    return {
      btnRef
    }
  }
}
<button ref="btnRef">按钮</button>

缺点:
使用包装对象,将值定义在value,某个值就是对象, 包装一层访问层级太深
使用场景:
定义 基本数据类型 和 数组 (非对象的数据)

reactive

使用场景:
1 适用于 定义 对象这个 响应式数据

import { reactive, toRefs } from 'vue'
export default {
  setup () {
    /* 
      利用Proxy直接代理 对象 返回代理对象
    */
    const data = reactive({
      a: 10,
      b: 20
    })
    const changeData = () => {
      data.a = 100
    }
    return {
      data
    }
  }
}
{{data.a}}
  • toRef
    问题?
    经常会将 reactive 里面某个属性 解构出来 定义一个变量来保存
    问题?
    这个变量不是响应式,修改 响应式对象 视图不刷新

利用 ref方法将 reactive 的响应式对象某个属性 转换成 响应式数据(利用ref,在js中使用 a.value)

import { reactive, toRef } from 'vue'
export default {
  setup () {
    /* 
      利用Proxy直接代理 对象 返回代理对象
    */
    const data = reactive({
      a: 10,
      b: 20
    })
    // a是包装对象 值在value中, js中访问需要加 .value 利用Object.defineProperty劫持
    const a = toRef(data, 'a')
    const changeData = () => {
      a.value = 100
    }
    return {
      data,
      a
    }
  }
}
{{a}}
  • toRefs
    批量 利用 toRef将 响应式对象所有的属性都转换成响应式数据(返回一个对象)
import { reactive, toRefs } from 'vue'
export default {
  setup () {
    /* 
      利用Proxy直接代理 对象 返回代理对象
    */
    const data = reactive({
      a: 10,
      b: 20
    })
   
    const changeData = () => {
      data.a = 100
    }
    return {
      ...toRefs(data)
    }
  }
}
{{a}}
{{b}}

注意:
reactive 不能重新赋值 重新赋值会变成普通对象

侦听器

  • 基础侦听
import { ref, watch } from 'vue'
export default {
  setup () {
    const msg = ref('');
    // 01基础侦听 ref 不需要加 value
    watch(msg, (newVal) => {
      console.log('msg改变了新值是', newVal);
    })

    return {
      msg
    }
  }
}
  • 侦听getter函数
import { ref, watch } from 'vue'
export default {
  setup () {
   
    const num1 = ref(2);
    const num2 = ref(3);
    
    // 参数1定义getter函数 侦听是getter函数返回值
    // 侦听是getter函数返回值, 侦听器回调 val也是 getter返回表达式的值
    watch(() => num1.value+num2.value, (val) => {
      console.log('num1+num2改变了值是', val);
    }) 
    return {
      num1,
      num2,
    }
  }
}
  • 侦听多个值
import { ref, watch, reactive } from 'vue'
export default {
  setup () {
    const num1 = ref(2);
    const num2 = ref(3);
    // 同时侦听多个值
    watch([num1, num2], ([val1, val2]) => {
      console.log('num1或者num2改变了值是', val1, val2);
    }) 
    return {
      num1,
      num2,
    }
  }
}
  • 侦听响应式对象
    默认就是深度侦听
import { ref, watch, reactive } from 'vue'
export default {
  setup () {
    // 侦听响应式对象 reactive 默认就是深度侦听
    const data = reactive({
      a: 10,
      b: 20
    })
    // 默认就是深度侦听 data任意属性改变都会触发 
    watch(data, (val) => {
      console.log('data改变了值是', val);
    })
    
    return {
      data
    }
  }
}
  • ref定义数组 侦听需要加deep
    引入 ref 侦听 比较 的 ref 的value属性值的变化, ref保存value是数组,数组是引用类型
    const arr = ref([1, 2, 3, 4]);
    watch(arr, (val) => {
      console.log('arr改变了', val);
    }, {
      deep: true
    })

侦听器的属性

watch(侦听源, callback[, options])

watch(obj, () => {}, {
  deep: true, // 深度侦听
  immediate: true,
  flush: 'post'
})

watchEffect

export default {
  setup (prop) {
    const num1 = ref(10);
    const num2 = ref(20);
    /* 
      1 初始化立即执行一次回调
      2 将 在 回调中使用的数据当成依赖,依赖改变 回调再次触发
    */
    watchEffect(() => {
      consolelog(num1.value+ num2.value)
    })
    return {
      num1,
      num2
    }
  }
}

watch vs. watchEffect?
watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。

watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

计算属性

import { ref, computed } from 'vue'
export default {
  setup () {
    const msg = ref('hello vue3');
    const reverseMsg = computed(() => {
      // 经过计算返回结果即可
      return msg.value.split('').reverse().join('')
    })
    console.log(reverseMsg.value);

    // 计算属性 setter
    const num = ref(10);
    const doubleNum = computed({
      get(){
        return num.value * 2
      },
      set(val) {
        console.log('setter触发', val);
        num.value = val / 2;
      }
    })
    const changeDoubleNum = ()=> {
      doubleNum.value = 100;
    }
   
    return {
      msg,
      reverseMsg,
      num,
      doubleNum,
      changeDoubleNum
    }
  }
}

注意:
在js中访问 需要加 value属性
本质上计算属性是 ref 包装响应式数据

组合式api生命周期钩子

去除了 beforeCreate个created (被setup替代了)

其他钩子前面加 on

onBeforeMount()
onMounted()
onBeforeUpdate()
onUpated()
onBeforeUnmount()
onUnmounted()

onActivated()
onDeactivated()

注意:
初始化发送请求 也是在 onMounted中触发

零碎 nextTick 在 组合式api 使用路由和 vuex

hook函数,钩子函数 (必须在特定地方调用,获取特定的东西或者在特定时间点 触发函数)

hook函数: 大部分 都是以 use开头

vue hook函数 一定要 在setup函数中调用 在其他函数中调用无效

import { nextTick } from 'vue'

import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'

setup语法糖

基础语法

<script setup>
console.log('hello script setup')
</script>
  • 顶层绑定自动暴露给模板
<script setup>
// 变量
const msg = 'Hello!'

// 函数
function log() {
  console.log(msg)
}
</script>

<template>
  <button @click="log">{{ msg }}</button>
</template>
  • 引入组件自动注册
    建议 组件使用还是大驼峰
<script setup>
import MyComponent from './MyComponent.vue'
</script>

<template>
  <MyComponent />
</template>
  • 特定命名指令也会自动注册
    vNameOfDirective
<script setup>
const vMyDirective = {
  beforeMount: (el) => {
    // 在元素上做些操作
  }
}
</script>
<template>
  <h1 v-my-directive>This is a Heading</h1>
</template>
  • 新增defineProps 做props 校验
    问题? 原因 setup语法糖 没了 显示 组件config对象 没法定义 props属性
<template>
  <div>
    <h2>我是子组件</h2>
    {{ title }}
    {{ num }}
  </div>
</template>

<script setup>
 const props = defineProps({
  title: String,
  num: {
    type: Number,
    required: true
  }
 })

</script>
  • defineEmits
    问题?
    省略了 setup函数后,如何拿到 emit方法触发子组件自定义事件呢
<template>
  <div>
    <h2>我是子组件</h2>
    {{ title }}
    {{ num }}
    <button @click="fn">子向父通信</button>
  </div>
</template>

<script setup>
 const props = defineProps({
  title: String,
  num: {
    type: Number,
    required: true
  }
 })

 const emit = defineEmits(['biubiu', 'piupiu']);
 const fn = () => {
  emit('biubiu', '携带的数据')
 }
</script>
  • 顶层可以直接await
    原因:
    编译成setup函数时,会自动给 setup函数 添加 async

组合式api结合ts

  • 新增defineProps 使用ts 泛型做 类型校验
    优点:ts 校验 可以在代码的时候立即校验 (普通props校验只能在代码运行时,在控制台警告)
    • 直接校验
      <script setup lang="ts">
        interface PropType {
          title: string,
          num?: number
        }
    
        const props = defineProps<PropType>();
      </script>
    
    • props 默认值
        <script setup lang="ts">
        interface PropType {
          title: string,
          num?: number
        }
        // 一定是可选prop定义默认值
        const props = withDefaults(defineProps<PropType>(), {
          num: 100
        })
      </script>
    
  • defineEmits 泛型做校验
const emit = defineEmits<{
  (e: 'biubiu', msg: string): void;
  (e: 'piupiu', title: string): void
}>();
// 只能触发两个事件 分别是 biubiu piupiu 携带参数格式都是string

面试题?
vue2 和vue3区别

  • ref 使用泛型做类型标注
const msg = ref<string>('你好世界')
  • reactive 直接指定类型即可 (接口)
interface PersonType {
  name: string;
  age: number;
  gender: string
}

const data: PersonType = reactive({
  name: 'xxx',
  age: 18,
  gender: '男'
})
  • 计算属性标注类型
const doubleNum = computed<number>(() => {
  return xxxx
})

pinia

pinia官网

对比 Vuex 3.x/4.x
Vuex 3.x 只适配 Vue 2,而 Vuex 4.x 是适配 Vue 3 的。

Pinia API 与 Vuex(<=4) 也有很多不同,即:

mutation 已被弃用。它们经常被认为是极其冗余的。它们初衷是带来 devtools 的集成方案,但这已不再是一个问题了。
无需要创建自定义的复杂包装器来支持 TypeScript,一切都可标注类型,API 的设计方式是尽可能地利用 TS 类型推理。
无过多的魔法字符串注入,只需要导入函数并调用它们,然后享受自动补全的乐趣就好。
无需要动态添加 Store,它们默认都是动态的,甚至你可能都不会注意到这点。注意,你仍然可以在任何时候手动使用一个 Store 来注册它,但因为它是自动的,所以你不需要担心它。
不再有嵌套结构的模块。你仍然可以通过导入和使用另一个 Store 来隐含地嵌套 stores 空间。虽然 Pinia 从设计上提供的是一个扁平的结构,但仍然能够在 Store 之间进行交叉组合。你甚至可以让 Stores 有循环依赖关系。
不再有可命名的模块。考虑到 Store 的扁平架构,Store 的命名取决于它们的定义方式,你甚至可以说所有 Store 都应该命名。

pinia基础使用

  • main.js中使用pinia
import { createPinia } from 'pinia'
const pinia = createPinia();

app.use(pinia);
  • 创建仓库
    类比 vue 选项式api state(相当于data) getters相当于computed actions相当于methods
import { defineStore } from "pinia";

const useUserStore = defineStore('user', {
  // 存储这个仓库公共状态
  state: () => ({
    num: 10,
    num2: 1000
  }),
  getters: {
    doubleNum(state){
      return state.num * 2
    }
  },
  // 类比为methods中的方法
  actions: {
    changeNum(n: number){
      this.num += n
    }
  }
});


export default useUserStore

在组件中 操作pinia

调用 hook 获取仓库实例

import useUserStore from 'xxxx'
const userStore = useUserStore(); // 被Proxy代理后的对象
  • 操作state
    • 获取
      userStore.状态名 // 即可获取state
    
    • 修改state
      1 直接修改 (不建议)

        userStore.state名 =

      2 使用 $patch方法 优点:同时批量修改多个

        userStore.$patch({
          num:,
          num2:2
        })
      

      3 在action中修改 状态 推荐方案
      actions中方法 也是直接 挂载到 仓库实例,action内部直接 通过this即可 修改state

        userStore.changeNum()
      
        {
          state: () => {
            return {
              num: 10
            }
          },
          actions: {
            changeNum(n){
              this.num += n
            }
          }
        }
      

pinia中处理异步

直接在action 发送异步请求成功给 state 赋值

import { defineStore } from "pinia";
import axios from 'axios'
const useUserStore = defineStore('user', {
  state: () => ({
    cates: []
  }),
  actions: {
    fetchCates(params={}){
      axios.get('https://api.it120.cc/conner/cms/category/list', {params}).then(res => {
        if (res.data.code === 0) {
          this.cates = res.data.data
        }
      })
    }
  }
});


export default useUserStore

storeToRefs

类似于 toRefs功能,将 仓库中 多个state 解构成 多个 利用 Object.definePerperty 代理 响应式数据

解决:
直接解构赋值 store 状态 视图不刷新的问题

import useUserStore from '@/stores/user'
import { storeToRefs } from 'pinia';

const userStore = useUserStore();

const {num, num2, cates} = storeToRefs(userStore);
const changeNum = () => {
  userStore.num = 567890;
}
// num num2 cates 都变成响应式数据了

pinia持久化

  • 使用缓存,将 pinia数据备份
  • 使用pinia插件
    使用 pinia-plugin-persistedstate
    详情见:
文章来源:https://blog.csdn.net/GAGGAAAAA/article/details/135291358
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。