关于Vue3&TypeScript的踩坑汇总

目录
  • 安装环境
  • 创建项目
    • 1、配置路由
    • 2、安装VueX
    • 3、安装国际化
    • 4、vite.config.ts常用配置
    • 5、找不到模块声明declare
    • 6、path模块找不到
  • 打包问题
    • Vite打包后显示跨域
  • Vue3.0的新语法糖-script setup
  • Css中使用JS代码
  • Ref、toRef、toRefs、Reactive总结
  • v-model
  • PropType
  • toRefs解构、Reactive
  • Ref、toRef、toRefs、Reactive
  • readonly
  • isProxy
  • toRaw
  • markRaw
  • Computed
  • VueCli4.5+TypeScript
    • 传值方式
    • VueX状态管理
  • 父元素获取子元素的方法
    • 方法一
    • 方法二
    • h(标签, {属性},[可以继续嵌套h()])
  • Vue2-Vue3生命周期对比

安装环境

1.node版本12以上

创建项目

npm init @vitejs/app + 项目名
npm init vite@latest +项目名

1、配置路由

router-index.ts

npm install vue-router@4 --save
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
const routes: Array<RouteRecordRaw> = [
 {
 path: "/",
 name: "Home",
 meta: {
  title: "首页",
  keepAlive: true
 },
 component: () => import("../views/Home/index.vue"),
 },
 {
 path: "/login",
 name: "Login",
 meta: {
  title: "登录",
  keepAlive: true
 },
 component: () => import("../views/Login/index.vue"),
 },
];
const router = createRouter({
 history: createWebHashHistory(),
 routes
});
export default router;

2、安装VueX

npm i vuex@next --save
import { createStore } from "vuex";
export default createStore({
 state: {
 listData:{1:10},
 num:10
 },
 mutations: {
 setData(state,value){
  state.listData=value
 },
 addNum(state){
  state.num=state.num+10
 }
 },
 actions: {
 setData(context,value){
  context.commit('setData',value)
 },
 },
 modules: {}
});

记得挂载

3、安装国际化

npm i vue-i18n@next -S
npm i js-cookie -S
npm i @types/js-cookie -D

4、vite.config.ts常用配置

npm i vite-plugin-compression

5、找不到模块声明declare

shime.d.ts

declare moudle ''

6、path模块找不到

npm install @types/node --save-dev

打包问题

tsconfig.json

"skipLibCheck": true,

Vite打包后显示跨域

解决办法:,npm 安装 http-server 包。

打开dist文件夹,终端输入

http-server -p  端口号

Vue3.0的新语法糖-script setup

置esS端S端置td小。。优点果。外面V了出。来。xx可。割。方法属性属性变量参数定:

Options API 约定:

我们需要在 props 里面设置接收参数

我们需要在 data 里面设置变量

我们需要在 computed 里面设置计算属性

我们需要在 watch 里面设置监听属性

我们需要在 methods 里面设置事件方法

你会发现 Options APi 都约定了我们该在哪个位置做什么事,这反倒在一定程度上也强制我们进行了代码分割。

现在用 Composition API,不再这么约定了,于是乎,代码组织非常灵活,我们的控制代码写在 setup 里面即可。

setup函数提供了两个参数 props和context,重要的是在setup函数里没有了this,在 vue3.0 中,访问他们变成以下形式: this.xxx=》context.xxx

我们没有了 this 上下文,没有了 Options API 的强制代码分离。Composition API 给了我们更加广阔的天地,那么我们更加需要慎重自约起来。

对于复杂的逻辑代码,我们要更加重视起 Composition API 的初心,不要吝啬使用 Composition API 来分离代码,用来切割成各种模块导出。

1、**template标签下面可以有多个节点了**,终于不用先写一个DIV了

2、**setup函数可以代替之前的data,methods,computed,watch,Mounted等对象**,但是props声明还是在外面

ref与reactive方法的区别是什么?一个是把值类型添加一层包装,使其变成响应式的引用类型的值。另一个则是引用类型的值变成响应式的值。所以**两者的区别只是在于是否需要添加一层引用包装**?其目的都是对数据添加响应式效果。

##### 思想优点

**vue2采用面向对象编程的思想,vue3则采用函数式编程的思想。**

原因:充分利用函数式编程组合大于继承的优势,采用函数式编程更利于逻辑功能的复用,webpack打包时更有利于tree-shaking,更利于代码的压缩,更利于返回值类型校验,压缩后的文件体积更小。

<script setup lang="ts">
// imported components are also directly usable in template
import Foo from "../Foo/index.vue";
import { ref } from "vue";
// write Composition API code just like in a normal setup()
// but no need to manually return everything
const count = ref(0);
const inc = () => {
  count.value++;
};
</script>
<template>
  <div>
    <Foo />
    HomePage组件
  </div>
</template>

Css中使用JS代码

Css中使用变量

.Title {
  font-size: 30px;
  @media screen and (max-width: 1000px) {
    font-size: 12px;
  }
  color: v-bind(colorRed);
}

Css去掉scope

:root {
  --varColor: red;
}
.aColor {
  color: var(--varColor);
}

Ref、toRef、toRefs、Reactive总结

Ref拷贝出来的值不能直接进行算数运算,必须使用.value

let qq = ref<number>(1);
  qq.value++;

Ref和Reactive,在不影响数据源的情况下可以将一般数据类型转化成响应式数据类型。

当响应式对象里头数据变化的时候原始对象的数据也会变化

toRef和toRefs是引用型,是在不影响数据源的情况下,将原始数据转化为响应式数据。且不更新UI界面,但是如果页面中有Reactive等其他响应式,则会同时更新UI界面。

defineProps父子传值

定义子组件props(子组件的data)

defineProps({
 msg: String,
});

defineEmits

const emit = defineEmits(['click']);

v-model

v-model 指令扩展为 modelValue 和 onUpdate:modelValue 在模板编译过程中,我们必须自己提供这些 props:

export default {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  render() {
    return h(SomeComponent, {
      modelValue: this.modelValue,
      'onUpdate:modelValue': (value) => this.$emit('update:modelValue', value)
    })
  }
}

PropType

假如我有一个todoItem 组件,需要item 信息属性,需要准守 TodoItem interface。

1、引入

import { PropType } from 'vue'

2、定义接口

export interface TodoItem {
  text: string
  done: boolean
}

3、属性验证

props: {
    todo: {
      type: Object as PropType<TodoItem>,
      default: {
        text: '',
        done: false
      }
    }
  }

toRefs解构、Reactive

对于以上代码,toRefs()将响应式的对象 state 变为普通对象 stateAsRefs 后,return 时使用 ES6 的扩展运算符,在模版中可以直接使用其内部属性,且仍具有响应性( 对响应式对象 state 使用扩展运算符后,其内部属性就失去了响应性 )。

对响应式对象进行 toRefs 后,可以对其进行解构方便 vue 模版使用,但是不会使其失去响应性。

Ref、toRef、toRefs、Reactive

ref()接收一个 js 基本数据类型的参数;toRef()接收两个参数,第一个为对象,第二个为对象中的某个属性;

ref()创建的数据会触发 vue 模版更新;toRef()创建的响应式数据并不会触发 vue 模版更新,所以toRef()的本质是引用,与原始数据有关联

toRef[data]

toRef针对的是响应式,针对的不是普通对象,如果用于非响应式,产出的结果不具有响应式

toRef 用于为源响应式对象上的属性新建一个ref,从而保持对其源对象属性的响应式连接。

接收两个参数:源响应式对象和属性名,返回一个ref数据。例如使用父组件传递的props数据时,要引用props的某个属性且要保持响应式连接时就很有用。

<template>
  <div>
    <h1 style="color: royalblue" @click="clickHandler">
      {{ names }}
    </h1>
  </div>
</template>
<script lang="ts" setup>
import { ref, defineProps, toRef } from "vue";
const props = defineProps({
  kindleValue: {
    type: Object,
    default: () => {
      return {};
    },
  },
});
const clickHandler = () => {
  names.value++;
  console.log(names.value, props.kindleValue);
};
const names = toRef(props.kindleValue, "name");
</script>
<style scoped lang="less"></style>

readonly

接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。

isProxy

检查对象是否是由 reactive 或 readonly 创建的 proxy

toRaw

作用:将一个由reactive生成的响应式对象转为普通对象

使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。

const props = defineProps({
  navbarList: {
    type: Object,
    default: () => {
      return {};
    },
  },
  activeKey: {
    type: Number,
    default: () => {
      return 0;
    },
  },
  isBlur: Boolean,
});
const emit = defineEmits(['change']);
// 选择Tab
const changeActiveKey = (key: number) => {
  const { navbarList } = props;
  const linkUrl = toRaw(navbarList) as Array<linkType>;
  console.log(key);
  emit('change', key);
  router.push(`${linkUrl[key].children[0].linkUrl}`);
};

markRaw

作用:标记一个对象,使其永远不会再成为响应式对象。

应用场景:

1.有些值不应被设置为响应式的,例如复杂的第三方类库等。

2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

Computed

返回值是readOnly,属性是只读的。

用法一

const show = computed({
  get() {
    return props.value;
  },
  set(val: boolean) {
    emit("update:modelValue", val);
  }
});

用法二

const itemStyle = computed(() => {
  return item => {
    return {
      background:
        item?.path === active.value ? useEpThemeStoreHook().epThemeColor : "",
      color: item.path === active.value ? "#fff" : "",
      fontSize: item.path === active.value ? "16px" : "14px"
    };
  };
});
const navbarList = computed(() => {
  return store.state.user.menu;
});

defineComponent

VueCli4.5+TypeScript

传值方式

多层组件传值:Provide-inject

父子组件传值:defineProps、defineEmits

VueX状态管理

watch和watchEffect的区别

1、watch 是需要传入侦听的数据源,而 watchEffect 是自动收集数据源作为依赖。

2、watch 可以访问侦听状态变化前后的值,而 watchEffect 没有。

3、watch 是属性改变的时候执行,而 watchEffect 是默认会执行一次,然后属性改变也会执行。

// imported components are also directly usable in template
import { ref, reactive, watch, watchEffect } from "vue";
// write Composition API code just like in a normal setup()
// but no need to manually return everything
const count = ref(0);
let a = ref<number>(0);
type obj = {
  name: string;
  age: number;
};
const Person = reactive<Array<obj>>([{ name: "LKK", age: 10 }]);
watch(
  () => Person[0].age,
  (newVal, oldVal) => {
    console.log(newVal, oldVal, "ssss");
  }
);
watchEffect(() => {
  console.log(Person[0].age); // 即使 testObject.a 没有发生变化,也会在组件更新之前输出一遍
});
const inc = () => {
  count.value++;
};
const clickHandler = function () {
  Person[0].age += 1;
  // a.value++;
};

监听多个数据

  watch(() => [state.province, state.country], ([newprovince,newcountry],[oldprovince,oldcountry]) => {
        console.log(oldprovince,'省份---', newprovince);
        console.log(newcountry,'cs---', oldcountry);
        // 判断是不是省份发生的变化
        if(oldprovince !== newprovince) {
        const arr = state.allCity.filter((item) => item.province == state.province);
        state.countries = arr[0].cities;
        console.log(arr);
        state.country = "";
        }
        state.detailedAdress = "";
        console.log(state.countries);
      }
    );

类型“unknown”上不存在属性“name”

import type { PropType } from 'vue';

父元素获取子元素的方法

方法一

子元素需要用defineComponent导出写法

<template>
  <div>
    <a-modal title="Title" v-model:visible="visible" :confirm-loading="confirmLoading" @ok="handleOk">
      <p>{{ modalText }}</p>
    </a-modal>
  </div>
</template>
<script lang="ts">
import { ref, defineComponent } from 'vue';
export default defineComponent({
  setup() {
    const modalText = ref<string>('Content of the modal');
    const visible = ref<boolean>(false);
    const confirmLoading = ref<boolean>(false);
    const showModal = () => {
      visible.value = true;
    };
    const handleOk = () => {
      modalText.value = 'The modal will be closed after two seconds';
      confirmLoading.value = true;
      setTimeout(() => {
        visible.value = false;
        confirmLoading.value = false;
      }, 2000);
    };
    return {
      modalText,
      visible,
      confirmLoading,
      showModal,
      handleOk,
    };
  },
});
</script>

defineComponent无法获取父组件传过来的后台数据。

方法二

defineExpose

// 子组件
<script setup>
import { defineExpose } from 'vue'
const childFun = () => {
	console.log('我是子组件方法')
}
// 重点!!这里需要使用defineExpose暴露出去
defineExpose({
	childFun
})
</script>
// 父组件
<template>
	<child ref="childRef"></child>
</template>
<script setup>
import { ref } from "vue";
// 引入子组件
import child from "./child.vue";
// 获取子组件
const childRef = ref(null);
const fun = () => {
	childRef.value.childFun();// 调用子组件的方法
}
</script >

h(标签, {属性},[可以继续嵌套h()])

其实h()函数和createVNode()函数都是创建dom节点,他们的作用是一样的,但是在VUE3中createVNode()函数的功能比h()函数要多且做了性能优化,渲染节点的速度也更快。

import { createApp } from "vue";
//import App from "./App.vue";
import { defineComponent, h, createVNode } from "vue";
import HelloWorld from "./components/HelloWorld.vue";
const img = require('./assets/logo.png'); // eslint-disable-line
const App = defineComponent({
  render() {
        return h("div", { id: "app" }, [
            h("img", { src: img }),
            h(HelloWorld, { msg: "HelloWorld" }),
            createVNode("h1", { class: "hello" }, "HelloWorld")
        ]
        );
  },
});
createApp(App).mount("#app");

Vue2-Vue3生命周期对比

beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 详解vue-class迁移vite的一次踩坑记录

    目录 what happen 探究 解决 总结 what happen 最进项目从 vue-cli 迁移到了 vite,因为是 vue2 的项目,使用了 vue-class-component类组件做 ts 支持.当然迁移过程并没有那么一帆风顺,浏览器控制台报了一堆错,大致意思是某某方法为 undefined,无法调用.打印了下当前 this,为 undefined 的方法都来自于 vuex-class 装饰器下的方法.这就是一件很神奇的事,为什么只有 vuex-class 装饰器下的方法才会为

  • vite2.0 踩坑实录

    目录 vite项目构建优化 其它 最后 算是对上一篇的补充,记录了一些在配置项目中遇到的问题,希望对大家能有所帮助- vite项目构建优化 路由动态导入 经过rollup的构建,动态导入的文件将会生成异步的chunk文件,在我们访问项目的时候按需加载,极大的提升应用的加载速度 import Home from '@/views/home/index.vue' import Layout from '@/components/Layout.vue' const routes: Array<Rout

  • 关于TypeScript的踩坑记录

    目录 用字符串做下标报错 函数内使用this报错 找不到模块XXX 引入模块提示找不到声明文件(接上一个问题) JSON直接解析localStorage值报错 初始加载的组件未命名,浏览器打开页面后控制台报错 初始值未定义类型,后面赋值报错 在Vue原型上添加属性使用时报错 element-ui使用$message报错 vue-cli里使用process对象报错类型找不到 vue-cli里tsconfig.json文件报错 tsconfig.json配置解释 用字符串做下标报错 代码示例: co

  • 使用VueCli3+TypeScript+Vuex一步步构建todoList的方法

    前言 Vue3.x 即将来袭,使用 TypeScirpt 重构,TypeScript 将成为 vue 社区的标配,出于一名程序员的焦虑,决定现在 Vue2.6.x 踩一波坑. vue 官方文档已经简略地对 typescript 的支持进行了介绍,我们使用 Vue Cli3 直接生成项目 创建项目 ❓为什么使用 Vue Cli3 构建项目 官方维护,后续升级减少兼容性问题 使用以下配置进行项目的生成: Babel 对 Ts 进行转译 TSLint 对 TS 代码进行规范,后续会使用 prettie

  • 关于Vue3&TypeScript的踩坑汇总

    目录 安装环境 创建项目 1.配置路由 2.安装VueX 3.安装国际化 4.vite.config.ts常用配置 5.找不到模块声明declare 6.path模块找不到 打包问题 Vite打包后显示跨域 Vue3.0的新语法糖-script setup Css中使用JS代码 Ref.toRef.toRefs.Reactive总结 v-model PropType toRefs解构.Reactive Ref.toRef.toRefs.Reactive readonly isProxy toRa

  • 关于Vue3过渡动画的踩坑记录

    目录 背景 问题定位 进一步分析 总结 背景 在我的 <Vue 3 开发企业级音乐 App>课程问答区,有个同学提了个问题,在歌手列表到歌手详情页面到转场动画中,只有进入动画,却没有离场动画: 该学生确实在这个问题上研究了有一段时间,而且从他的描述,我一时半会儿也想不出哪有问题,于是让他把代码传到 GitHub 上,毕竟直接从代码层面定位问题是最靠谱的. 问题定位 一般遇到此类问题的时候,我的第一反应是他用的 Vue 3 版本可能有问题,毕竟 Vue 3 还在不断迭代过程,某个版本有一些小 b

  • vue3搭配pinia的踩坑实战记录

    目录 前言 An Object could not be cloned? VUE 3的toRaw PINIA与VUE 3可以混合搭配? 同样的操作在VUE 3下的结果 最后的解决方式 总结 前言 最近接手了一个新项目,用的是VUE3+pinia的组合.由于之前没有用过这2个库,只能现学现做.幸运的是这两者的上手难度都不大,项目可以正常开发.但这其中也遇到了一些坑,今天就来讲其中我印象最深的一个. An Object could not be cloned? 不知道有多少开发者遇到过这个报错——A

  • vue3中vuex与pinia的踩坑笔记记录

    目录 介绍 安装使用 简单对比写法差异与共同点 Vuex 和 Pinia 的优缺点 何时使用Pinia,何时使用Vuex 总结 介绍 Pinia 是 Vue.js 的轻量级状态管理库,最近很受欢迎.它使用 Vue 3 中的新反应系统来构建一个直观且完全类型化的状态管理库. Pinia的成功可以归功于其管理存储数据的独特功能(可扩展性.存储模块组织.状态变化分组.多存储创建等). 另一方面,Vuex也是为Vue框架建立的一个流行的状态管理库,它也是Vue核心团队推荐的状态管理库.Vuex高度关注应

  • 记VUE3+TS获取组件类型的方法踩坑及解决

    目录 VUE3+TS获取组件类型的方法踩坑 遇到的坑 问题原因 解决办法 VUE3+TS获取组件ref实例 如何获取组件的类型呢? 总结 VUE3+TS获取组件类型的方法踩坑 获取组件类型的方法 const AccountRef = ref<InstanceType<typeof LoginAccount>>() 遇到的坑 typeof LoginAccount一直报红线提示错误 LoginAction: () => vo...' provides no match for

  • Python 3.x踩坑实战汇总

    目录 纪要 处处有坑 1. 文件读取 open 2. 正则表达式 \S 与 \\S 3. 正则表达式匹配方法 match 4. 帮助文档 pydoc 5. 字符串 encode base64 编码 6. Python 调用 C# 动态链接库 总结 纪要 本文用于记录学习 Python 过程中遇到的一些小问题,如果遇到的是比较大的问题会单独开页面分析学习 处处有坑 1. 文件读取 open # 我们打开文件使用 open 方法 xml = open("demo.xml") # 使用 op

  • 关于实现Vue3版抖音滑动插件踩坑指南

    目录 起步 调研 实现思路 工程构建 代码实现 video实现 slide.vue 组合使用 视频自动播放问题 git地址 总结 起步 年前单位需要搞一个类似抖音的需求,这本应是客户端的任务,然而,不知天高地厚的我却接了下来,然而下细致调研之下,发现网上并没有成熟的方案,但是却又很多需求,各大论坛全是提问的帖子,却少有人回答和解决. 这一瞬间,俺慌了,毕竟单位的活,排期都是定死的,这时候临阵退缩,实乃下下策.于是只能撸起袖子加油干.毕竟自己揽的事,含着泪也要干完,这就是男人,一个吐沫一个钉! 调

  • Vite vue3多页面入口打包以及部署踩坑实战

    目录 为什么需要多入口? 一.改造项目 二.vite.config.ts配置 三.部署 总结 为什么需要多入口? 公司原生的移动端上需要用webview引入一些性能要求不高的H5页面,初步考虑后选择用vue试个水,前期页面跳转选择使用vue-router,测试过程中在安卓高版本下右滑返回效果尚可,ios端初步尝试使用的最左侧touch事件移动距离检测以及router判断index添加过场动画,但是整体的效果依然达不到下图的效果. 原先项目中是使用多个html页面以及原生自带的协议去打开html,

  • vue3不能使用history.pushState修改url参数踩坑

    目录 前言 问题 追根溯源 解决 前言 在重构我的 vue-use-sync-url(辅助将数据和 url 参数进行同步的工具库)时,遇到了一个使用 window.history.pushState 来修改地址栏的 url 参数的 bug,准确来说是 vue-router 的 bug,下面就来讲讲具体是怎么回事. 问题 场景如下,有一个输入框里面输入了内容,点击搜索按钮使用 window.history.pushState 将数据同步到 url 参数上.然后再点击 go about 按钮跳转到别

随机推荐