使用Vue3+TS重构百星websocket插件(下)

简介: 使用Vue3+TS重构百星websocket插件(下)

插件重构


前面我们把插件整体的读了一遍,接下来就可以用Vue3 + TypeScript来重构它了。

作者的代码写的很精巧,逻辑方面不用做改动,我只是将它的代码实现从js改成了ts,修改了被Vue3废弃的写法,虽然做的修改比较简单,但是学到了作者的插件设计思想以及踩到的一些ts的坑,收获还算挺大。


接下来,就跟大家分享下我的重构过程以及踩到的一些坑。


安装依赖


在用ts重构前,我们需要先安装相关依赖包,执行下述命令即可安装。


yarn add typescript prettier eslint eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser standard --dev


随后,在项目根目录创建tsconfig.json文件,为typescript的配置文件,添加下述配置,设置"declaration": true即可在运行tsc命令时自动在types目录下生成声明文件。


{
  "exclude": [
    "./node_modules"
  ],
  "compilerOptions": {
    "lib": [
      "esnext",
      "dom"
    ],
    "baseUrl": "./",
    "outDir": "./dist/", // 打包到的目录
    "target": "ES2015", // 转换成的目标语言
    "module": "esnext",
    "declaration": true,// 是否生成声明文件
    "declarationDir": "./dist/types/",// 声明文件打包的位置
    "strict": true, // 开启严格模式
    "sourceMap": true, // 便于浏览器调试
    "moduleResolution": "node", // 使用node模块
    "experimentalDecorators": true, // 使用装饰器
    "skipLibCheck": true, // 跳过库检查
    "esModuleInterop": true, // es模块互操作
    "allowSyntheticDefaultImports": true, // 允许默认导入
    "noImplicitAny": true, // 不能使用any
    "noImplicitThis": true, // 不能使用this
    "alwaysStrict": true, // 严格模式
    "noUnusedLocals": true, // 不能有未使用的变量
    "noUnusedParameters": true, // 不能有未使用的参数
    "noImplicitReturns": true // 必须声明返回值
  },
  "include": [
    "src/**/*.ts"
  ]// 要打包的文件
}


修改已经废弃的语法


在插件的入口文件Main.js中,插件需要向Vue全局挂载属性,即Vue.prototype.xx = xx,在vue3中这一写法已经废除,需要用app.config.globalProperties.xx = xx来替换,重构好的main.ts文件部分代码如下:


import { App } from "vue";
export default {
    install(app: App, connection: string, opts: websocketOpts = { format: "" }): void {
      // ... 其它代码省略 ....//        
      opts.$setInstance = (wsInstance: EventTarget) => {
            // 全局属性添加$socket
            app.config.globalProperties.$socket = wsInstance;
        };
    }
}


完整代码请移步:src/Main.ts


beforeDestroy生命周期被移除


在插件的入口文件app.mixin中,组件销毁前它需要从全局移除已经添加在全局的属性,即beforeDestroy,在Vue3中这一写法已经被移除,需要用beforeUnmount来替换,其部分代码如下:


import { App } from "vue";
export default {
    install(app: App, connection: string, opts: websocketOpts = { format: "" }): void {
      // .... 其它代码省略 ....//
      app.mixin({
                    beforeUnmount() {
                if (hasProxy) {
                    const sockets = this.$options["sockets"];
                    if (sockets) {
                        Object.keys(sockets).forEach((key) => {
                            // 销毁前如果代理存在sockets存在则移除$options中给sockets添加过的key
                            delete this.$options.sockets[key];
                        });
                    }
                }
            }
      })
    }
}


扩展全局对象


Observer.ts中,需要向Websocket中添加sendObj方法,这在js中很简单,直接websocket.sendObj = ()=>{}即可。但是在ts中它就会报错,Websocket中不存在sendObj方法,一开始我想在lib.dom.d.ts中定义这个方法,但是想了想这样做不妥,不能修改全局的库声明文件,毕竟这是插件。


640.png

                          image-20201102210949765  


经过我的一番折腾后,在ts的文档中找到了答案,ts的官方文档描述如下。


640.png

                               image-20201102210650833


正如官方文档所描述,ts查找声明文件会从当前文件开始找,我们只需要在当前类中用declare global来扩展即可,代码如下:


// 扩展全局对象
declare global {
    // 扩展websocket对象,添加sendObj方法
    interface WebSocket {
        sendObj(obj: JSON): void;
    }
}


添加上述代码后,报错就解决了,完整代码请移步:src/Observer.ts


640.png

                            image-20201102211101120


回调函数类型定义


Emitter.ts文件里,添加监听的方法调用者可以传一个回调函数进去,这个回调函数的参数是未知的,因此就需要给他指定正确的类型,一开始我用的Function类型,但是eslint报错了,他不建议这么使用,报错如下:


640.png

                                   image-20201102212611648


经过我的一番折腾后,找到了如下解决方案,声明类型时只需要将参数解构即可。


addListener(label: T, callback: (...params: T[]) => void, vm: T): boolean {
        if (typeof callback === "function") {
            // label不存在就添加
            this.listeners.has(label) || this.listeners.set(label, []);
            // 向label添加回调函数
            this.listeners.get(label).push({ callback: callback, vm: vm });
            return true;
        }
        return false;
    }


完整代码请移步:src/Emitter.ts


验证插件能否正常工作


插件重构完成后,我们将整个项目的文件复制到一个vue3项目的node_modules/vue-native-websocket下,替换原先的文件。


640.png

                     image-20201103001444839


在main.ts中导入并使用插件。


import { createApp } from "vue";
const app = createApp(App);
// 使用VueNativeSock插件,并进行相关配置
app
  .use(store)
  .use(router)
  .mount("#app");
// 使用VueNativeSock插件,并进行相关配置
app.use(
  VueNativeSock,
  `${base.lkWebSocket}/${localStorage.getItem("userID")}`,
  {
    // 启用Vuex集成
    store: store,
    // 数据发送/接收使用使用json
    format: "json",
    // 开启手动调用 connect() 连接服务器
    connectManually: true,
    // 开启自动重连
    reconnection: true,
    // 尝试重连的次数
    reconnectionAttempts: 5,
    // 重连间隔时间
    reconnectionDelay: 3000
  }
);


在组件中与websocket服务端建立连接


mounted() {
    // 判断websocket是否连接: 当前为未连接状态并且本地存储中有userID
    if (
      !this.$store.state.socket.isConnected &&
      localStorage.getItem("userID") !== null
    ) {
      // 连接websocket服务器
      this.$connect(`${base.lkWebSocket}/${localStorage.getItem("userID")}`);
    }
  }


调用sendObj方法来发送消息。


this.$socket.sendObj({
       msg: msgText,
       code: 0,
       username: this.$store.state.username,
       avatarSrc: this.$store.state.profilePicture,
       userID: this.$store.state.userID
  });


调用onmessage方法来接收服务端消息。


// 监听消息接收
    this.$options.sockets.onmessage = (res: { data: string }) =>    {
    }


完整代码请移步:chat-system,最终结果如下:


640.png

                       image-20201103002555455


给作者提个PR


顺便给作者提个pr,将我修改的代码丢给作者😄vue-native-websocket/pulls


640.png

                              image-20201103005547871


发布至npm仓库


至此,插件的重构就结束了,我们修改package.json中的build命令,替换为tsc,修改入口文件main以及类型声明文件入口types。部分呢代码如下,完整代码请移步:package.json


{
    "main": "dist/Main.js",
    "types": "dist/types/Main.d.ts",
    "scripts": {
    "build": "tsc"
  }
}


随后,执行yarn run build命令,就会在项目的根目录下创建dist文件夹并将打包后的js文件放入其中。


640.png

                      image-20201102214629366


dist目录中的文件就是我们要发布至npm仓库的包,在发布至npm仓库之前,我们要先做一些事情,让插件更加规范化。


定义新版本推送规范


我们在项目根目录创建PUBLISH.md文件,用于告知开发者修改本插件后如何进行推送。


## 新版本推送规范
- 对插件进行修改
- 执行 `yarn build` 来生成打包后的文件
- 修改`package.json`中的版本号
- 提交你的修改
- 运行`package.json`中的`changelog`命令来生成更新记录
- 最后将项目推送到你的仓库,然后为主仓库创建一个Pull request


编写插件使用文档


作为一个插件,README.md文件是必不可少的,这个文件会告诉开发者如何使用这个插件,完整代码请移步:README.md


定义提交规范


无规矩不成方圆,插件亦是如此。我们需要通过一些工具来定义提交代码时规范,这样会使插件更易维护。


安装依赖


执行下述命令安装我们需要的插件包


yarn global add commitizen


上述命令会全局安装commitizen工具,它的作用是提供一个脚本工具给到开发者来按照指引生成符合规范的 commit 信息。


执行下述命令,既可将其保存到package.json的依赖项,将config.commitizen配置添加到package.json的根目录,该配置告诉commitizen,当我们尝试提交此仓库时,我们实际上希望使用哪个适配器。


commitizen init cz-conventional-changelog --save-dev --save-exact


然后我们就可以通过git cz命令,来提交 git commit


640.png

                             image-20201102221728435


强制执行commit规范


使用commitizen工具,我们可以通过执行git cz命令来提交符合规范的 commit 信息,但是在开发中,插件开发者不是通过命令行的方式来提交 commit 的,如果我们要强制校验其他人通过 vscode/webstorm 等其他工具的方式提交 commit,可以使用commitlint+husky的方式来配合使用。


安装commitlint检查我们的 commit message 是否符合常规的提交格式,通过下述命令安装。


yarn add  @commitlint/config-conventional @commitlint/cli --dev


在package.json中添加配置,指定提交规范,这里我们选用Angular 格式的配置


"commitlint": {
    "extends": [
      "@commitlint/config-conventional"
    ]
  },


做完上述操作后,我们就可以验证命令提交的commit信息校验了,接下来我们来配合husky实现ide的commit校验,执行下述命令安装依赖包。


yarn add husky --dev


在package.json中添加commit-msg 的钩子,用于检查commitlint规范。


"husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }


完成上述配置后,不管我们通过什么方式来提交 commit,如果 commit 信息不符合我们的规范,都会进行报错。


自动生成CHANGELOG


如果commit都符合刚才定义的Angular格式,那么发布新版本时, CHANGELOG 就可以用脚本自动生成。


此处我们使用conventional-changelog-cli 工具来生成它,执行下述命令来安装依赖。


yarn global add conventional-changelog-cli


在项目根目录执行下述命令,即可生成CHANGELOG.md 文件:


conventional-changelog -p angular -i CHANGELOG.md -s


我们可以将上述命令配置进package.json中的scripts中,这样我们就可以通过yarn run changelog来生成了


"scripts": {
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
  },


生成的文件内容如下所示:


640.png

                               image-20201102235321074


插件发布


最后,我们就可以将插件发布至npm仓库了。


此处,重点内容在插件的重构,想从零开始学插件发布步骤的开发者可移步我的另一篇文章:Vue实现一个全屏加载插件并发布至npm仓库


在终端进入项目根目录,执行下述命令,登录npm仓库,输入自己的用户名和密码


npm login


640.png

                                     image-20201103003251083


执行下属命令发布至npm仓库。


npm publish --access public


640.png

                  image-20201103003532065  


插件发布成功,我们去npm仓库搜一下vue-native-websocket-vue3,如下所示,已经可以搜到了


640.png

                                     image-20201103003826881


npm仓库地址:vue-native-websocket-vue3


最后,我们就可以在项目中使用yarn来安装使用了。


640.png

                                  image-20201103004600660


写在最后


  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 本文首发于掘金,未经许可禁止转载💌
相关文章
|
7月前
|
JavaScript
vue-cli创建vue3+ts项目
vue-cli创建vue3+ts项目
70 0
|
2月前
vue3+Ts 二次封装ElementUI form表单
【10月更文挑战第8天】
230 59
|
4月前
Vue3中getCurrentInstance如何与ts结合使用
【8月更文挑战第16天】Vue3中getCurrentInstance如何与ts结合使用
187 2
Vue3中getCurrentInstance如何与ts结合使用
|
3月前
|
前端开发
vue3+ts项目中使用mockjs
vue3+ts项目中使用mockjs
312 58
|
6月前
|
JavaScript 前端开发 开发者
vue3+ts配置跨域报错问题解决:> newpro2@0.1.0 serve > vue-cli-service serve ERROR Invalid options in vue.
【6月更文挑战第3天】在 Vue CLI 项目中遇到 "ERROR Invalid options in vue.config.js: ‘server’ is not allowed" 错误是因为尝试在 `vue.config.js` 中使用不被支持的 `server` 选项。正确配置开发服务器(如代理)应使用 `devServer` 对象,例如设置代理到 `http://xxx.com/`: ```javascript module.exports = { devServer: {
297 1
|
6月前
|
JavaScript 前端开发 数据安全/隐私保护
vue3+ts+elementplus写一个登录页面教程
【6月更文挑战第3天】本文介绍了如何使用 Vue 3 和 TypeScript 创建一个登录页面。首先,需安装 Vue CLI,然后创建新项目并启用 TypeScript 支持。接着,创建 `Login.vue` 组件,设计登录表单,包括用户账号、密码和验证码字段,并实现相关验证规则。页面样式包括背景、登录框和按钮等元素的布局与样式。最后,展示了`<script>`部分的代码,包括表单验证逻辑、生成验证码的函数以及登录提交处理。文章还提供了一个登录页面的截图和完整代码示例。
2704 1
|
7月前
|
JavaScript 数据可视化 算法
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
129 6
|
3月前
|
JavaScript 前端开发 UED
组件库实战 | 用vue3+ts实现全局Header和列表数据渲染ColumnList
该文章详细介绍了如何使用Vue3结合TypeScript来开发全局Header组件和列表数据渲染组件ColumnList,并提供了从设计到实现的完整步骤指导。
|
4月前
|
JavaScript
基于Vue3+TS简单设计一个查看文章时点击展开和点击收起的小功能
该文章展示了如何使用Vue 3和TypeScript创建一个简单的展开和收起功能,用于文章查看时的交互体验。
151 0
基于Vue3+TS简单设计一个查看文章时点击展开和点击收起的小功能
|
6月前
|
资源调度 JavaScript 前端开发
vite+vue3+ts从0到1搭建企业级项目(2)
vite+vue3+ts从0到1搭建企业级项目
95 1