Typescript、Vue、小程序等等所有踩过的坑都在这了

阅读: 评论:0

Typescript、Vue、小程序等等所有踩过的坑都在这了

Typescript、Vue、小程序等等所有踩过的坑都在这了

踩不完的坑

本文主要收录在项目开发和学习新技术时踩过的坑,总结于此加深记忆以及日后方便查阅。话痨博主,需要直接看答案的小伙伴直接Ctrl+F搜“言归正传”就是每一个问题的解决方案啦❤️

本博文首发在我的gitpage,由于是用Vuepress写的,在CSDN可能会出现一些阅读上不太友好的问题(其实是因为我懒不想改了)
可以访问我的博客查看原文获取更好的阅读体验,可以顺手看看我的github有没有你需要的代码哟

Typescript相关

Typescript项目单元测试

在UStorage的开发过程中碰到该问题。

起初主要在写逻辑代码并未进行浏览器测试,所以进行开发的时候未使用webpack,直接将Typescript文件使用tsc编译成JS文件在tsc watch的命令行中查看错误提示。当时将tsconfig.json文件中的&#dule"改成EX2015以便使用ES6的module功能。后来引入了webpack和ts-loader对TS进行处理进行页面调试,一切正常。

后来在写跑单元测试的时候,引入了Mocha + chai + istanbul。在运行Mocha命令的时候会出现import模块的报错:

当时一度以为是mocha --require ts-node/register 的时候出了问题,翻阅了相关mocha的文档和ts-node的文档但是并未解决该问题。

后来无意中在某一篇文章中看到关于tsconfig.json的Module配置,然后将

{"compilerOptions": {"module": "es2015" /* Specify module code generation: 'none', 'commonjs',}
}

改为

{"compilerOptions": {"module": "commonjs" /* Specify module code generation: 'none', 'commonjs',}
}

修改之后,运行 mocha --require ts-node/register test/**/*.spec.ts成功

瞎J8猜的分析应该是ts-node对typescript进行编译的时候,会根据tsconfig的配置进行编译。由于我刚开始设置的module是es2015所以在运行的时候出现了import,并且node模块基于commomJs,从而无法识别ESM的Import关键字,从而抛出异常。

Typescript在Vue中无法识别全局变量

受公司的五六个后台管理系统维护的摧残下,经过讨论决定用Vue+Typescript+vue-class-component+tslint+element-ui搞一个后台的项目前端基盘,以后新项目开始clone一手,直接进行开发不再配置岂不美哉~我不会说是因为我还没看完yeoman。

起初是为了方便之后的项目初始化,就只规划了比较通用类似axios封装、全局less mixins/variable等功能,而且一开始由于考虑公司技术栈,打算不上Typescript的。结果后来不知道为什么BOSS把一个新系统的开发任务丢给了我们亲爱的游戏前端小胖,所以就改用Typescript。

::: tip

项目初始化的时候是使用vue-cli3.0直接进行的初始化,Vue CLI 3初始化typescipt项目

:::

遇到的第一个问题是:全局变量无法识别

全局变量无法识别

在main文件中声明$http属性:

// import http from '@/core/http/index'Vue.prototype.$http = http;...

然后在页面中访问的时候编辑器就开始报错了。。。其实我在调用前已经按照官方文档的说明新建了一个d.ts文件并声明了$http这个属性

大概的情况就是这样:

官方文档中是这样说的:

但是事实告诉我,声明的http.d.ts文件并没有任何卵用,该报错还是一样报错啊。

// http.d.tsimport Vue from 'vue';declare module 'vue/types/vue'{interface Vue{$http:any;}
}

就是上边这个文件,在我声明之后依然报错。

言归正传

后来在Google和各种博客间畅游以后,我终于发现,在项目生成的shims-vue.d.ts文件中,添加一段声明就可以正常使用了。。。


import Vue from 'vue';declare module '*.vue' {export default Vue;
}<!-- 就是这里 -->
declare module 'vue/types/vue' {interface Vue {$http: any;}
}

Typescript,调用refs对象的方法,编译器提示错误。

今天在写sea-admin这个项目的时候,在方法中调用element-ui的table组件的toggleAllSelection方法的时候,TS的类型判断报错,虽然不会影响页面和功能,但是编辑器的高亮提示看着是真的难受。

提示的内容大意就是**this.$refs[’[‘sea-table’]’]**是一个Vue|Element|Vue[]|Element[],但是Vue的d.ts文件里关于Vue的interface中并没有定义toggleAllSelection这样一个方法,所以编辑器提示我们这个对象中不存在toggleSelection的方法。

下图是vue.d.ts声明的Vue接口,关于Vue的$refs属性声明了这个属性的值是一个Vue|Element|Vue[]|Element[]

这里的Element指的是DOM相关Element类,关于Element可以戳这Element-Web API接口参考-MDN

言归正传

为了解决烦人的错误提示,我想到了两种方法:

1. 将"this.$refs[‘sea-table’]"复制给一个类型声明为any的对象

const table = this.$refs['sea-table']leAllSelection();

这样改写了以后我们发现一切正常啦???

2. 使用Typescript的类型断言

类型断言:有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

Typescript的类型断言语法有两种:as和<>。具体的使用请参考基础类型

两种写法分别如下:


// as(this.$refs['sea-table'] as any).toggleAllSelection();// <>(<any> this.$refs['sea-table']).toggleAllSelection();

原理如下:

有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 ---- 基础类型—Typescript

Typescript+Vue+vue-class-component下获取props

这个问题比较简单,但是由于刚开始学习Typescript+Vue的开发模式,所以刚开始搞得我也比较懵逼。其实是以前没仔细看Vue的文档所以还是记录下来。

出现场景

在组件中定义了一个height的prop,可以传递number和string类型的值用来设定组件的高度。

在computed中对number进行处理,string类型直接返回,number类型就给后边加上’px’后返回。

javascript的Vue项目一般直接用this.[prop名]取prop的值进行处理:


// javascriptcomputed(){if(typeof this.height === 'number'){// ...something}
}

但是在Typescript项目中直接调用this.height发现会报错,报错的内容也是在Vue的对象上没有height这个属性。

由于当然我们也可以使用refs里说的方法来解决当然我们如果将各种类型报错全部使用any的类型断言解决,那Typescript的意义就没有了,还不如直接上js来得舒服。

其实Vue的文档里已经说得很清楚实例属性.

我们平时使用的this.height这种语法只不过是vue的一颗语法糖。具体实现和思想,会在之后的Vue源码阅读笔记里归纳总结,这里不深入。

通过阅读Vue官方文档我们知道,this.height其实对应的是this. p r o p s . h e i g h t , 并 且 V u e 的 声 明 文 件 中 也 清 晰 的 描 述 了 V u e 对 象 的 props.height,并且Vue的声明文件中也清晰的描述了Vue对象的 props.height,并且Vue的声明文件中也清晰的描述了Vue对象的prop属性,所以,怎么做不用我多说了吧。

言归正传
  get h() {const height = this.$props.height;  //加上$propsreturn typeof height === 'string' ? height : `${height}px`;}

这样就perfect解决了烦人的报错!???

Typesciript在vue-class-component风格下组件触发的事件无法修改父组件的属性

还是sea-admin开发的过程中,Table组件的对操作传递过来的方法进行触发的时候,出现修改data控制modal失效。

如下:

在Diary组件中引用了组件TableMain,在传入的tableControl里定义了表格的操作栏里的操作,当我点击编辑的时候,通过修改Diary组件中的isShowDialog(截图中并没有这个组件)的时候,在handle方法中获取调用this.isShowModal = truethis.$data.isShowModal = true均无法实现我需要的效果,查看Vue-devtools发现在修改this.isShowModal = true的时候,组件的data并没有被同时修改。

后来在方法中打印了this,发现内容如下:

打印出的是继承自Vue的Diary对象,并不是Vue组件的实例。

而我们平时使用Vue时调用的this都是VueComponent这个实例。(即调用Vueponent后生成的实例).

所以在Diary对象中修改this.data肯定无法影响到页面的实例。

所以我们对代码进行如下处理:

声明一个editItem的方法:

在handle函数中调用this.editItem()这个方法,就会发现一切正常了。

::: warning

无论Javascript还是Typescript中,传递给子组件的prop对象中包含的需要在子组件中调用的方法,想在父组件中获取到正确的this值,必须采用箭头函数的形式,否则获取到的this是该对象而不是定义时的Vue实例。

:::

Vue相关

mpvue

mpvue是一个巨坑吧。。。建议没有入坑的小伙伴还是不要尝试入坑mpvue了。伤不起啊伤不起,coding一时爽,debug两通宵。待老夫明天加完班回来补充完这一节 ----2019.08.31(立flag)

created/beforeCreate/beforeDestory等生命周期的问题

mpvue-issues834
mpvue-issues1416

有很多人遇到了这个问题,小程序启动的时候所有页面的都触发了这两个钩子。

大概情况如下:


// Page1
created(){console.log('this is page1')
}// Page2
created(){console.log('this is page2')
}// page3
created(){console.log('this is page3')
}

在启动小程序的时候,如果我们的启动页是page1,我们期望的效果应该是在控制台输出:

this is page1

但实际上控制台输出的效果是

this is page1this is page2this is page3

说明在项目初始化的时候所有页面的created/beforeCreated钩子都被执行了(同时new这么多Vue实例真的不会卡吗)

页面navigateBack和点击返回按钮不会触发beforeDestory/destroyed

这个问题是我在issues找关于下一个坑的答案的时候看到的,并没有亲身遇到过(机智的我在发现Mpvue生命周期有问题以后就改用了小程序的生命周期onUnload…)

言归正传

使用mounted()钩子可以按照我们的预期在页面进入的时候触发,所以在这个时候可以对data进行操作。

还有就是mounted钩子是不会在navigateBack和返回的时候触发,所以如果在navigateTo的页面进行操作返回上一页需要进行数据刷新,建议写在小程序的onShow钩子里。(小程序的onLoad也不会触发)

进入相同的路由,之前操作过的数据不会重新初始化

简而言之就是,页面销毁再进入的时候,页面数据不会重新初始化

举个例子

<template><span>{{title}}</span>  // 用来测试标题<Modal :visible="isShowModal"></Modal> // 假设这里有个模态框组件<button @click="showModal">显示模态框</button>
</template>export default {import {getTitleRequest} from '../../network/api'; // 请求标题的方法data(){return{title:'',   // q1isShowModal:true   // q2}},methods:{showModal(){this.isShowModal = true},async getTitle(id){this.title = await getTitleRequest(id);}},mounted(){let id = this.$route.query.Title(id);},
}

假设我们第一次进入页面,通过query传递参数id = 1;

调用mounted()钩子,触发getTitle的方法,请求到title为"标题1",并赋值给this.title,页面上会显示"标题1"

然后点击button,这个时候modal会显示出来。

然后我们navigateBack这个页面后,重新进入这个页面,通过query传递过来的参数为id = 2;

进入页面后会发现如下情况:

  1. 页面加载后title会先显示为"标题1",然后一闪而过(根据请求getTitle返回的速度),变成"标题2"

  2. 我们之前点开的modal依旧是显示状态

并且根据之前的destroy/beforeDestroy方法不会触发来推断,页面的vue实例之前并未被销毁。导致数据还是在我操作了id=1并点击了button以后的状态(这个问题在我把H5用mpvue重构以后才发现。。。然后心态当场炸裂)

言归正传

解决方案:

可以在onUnload钩子里手动重置data的数据,


onUnload(){Object.assign(this.data,this.$options.data())
}

关于this.$options可以参考Vue官方文档

:::warning
千万不要在onShow()/onHide()里重置数据,因为在用户最小化小程序,切换到其他APP后再切回来,息屏后唤醒都会执行这两个生命周期,如果你在onShow里重置了数据,并且获取数据的方法写在mounted(),你会发现重新打开后页面该有的数据都么得了哟
:::

或者可以写一个Vue的plugin,通过全局mixin混入onUnload钩子,在钩子里重置数据

let ResetDataPlugin = {install:function(){Vue.mixin({onUnload(){if(this.$options.data){Object.assign(this.data,this.$options.data())}}})}
}

这种调用方法会给所有的页面添加处理,如果不是项目写完了发现这个问题,或者对这块的影响没有要求。建议还是每个页面进行单独处理。

在同样的详情页面,比如detail?id=1进入detail?id=2,然后从detail?id=2返回到detail?id=1,恭喜你,你发现页面的数据其实还是detail?id=2。这个问题可以参考issues1021、
issues300

笔者亲身遇到mpvue的坑影响最严重的大概就是这部分,其余还有列表渲染的性能,和小程序组件库vant-ui和iview weapp兼容的问题,目前暂时还没有特别好的解决方案,所以在此不表。

关于组件销毁后数据不重置的问题,截止本文撰写时20190919依然存在于mpvue2.0.0版本里,并且在框架层面上并未得到解决。issue#140里有很多解决方案,涵盖了很多场景,想要深入了解的童鞋可以关注。

本博文所有内容为博主的个人总结和经验,由于博主才疏学浅,若有任何遗漏、理解错误之处,望请不吝赐教。github

本文发布于:2024-01-28 11:54:47,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17064140927238.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:都在   这了   程序   Typescript   Vue
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23