предисловие
Несколько дней назад я рефакторил свой проект с открытым исходным кодом Vue2 с помощью Vue3, и, наконец, осталась проблема: подключаемый модуль веб-сокета, используемый в проекте, не мог нормально использоваться. Итак, я решил переписать этот плагин для поддержки Vue3.
В этой статье будет записан процесс переписывания этого плагина и его публикация в репозиторий npm.Кстати, автору плагина будет выдан PR.Заинтересованные разработчики могут прочитать эту статью.
Плагин интерпретации
Как показано на рисунке выше, рефакторингу подвергнется именно плагин.На данный момент звездочек 735. Сначала закодируем плагинcloneк местному.
git clone https://github.com/nathantsoi/vue-native-websocket
После загрузки на локалку используйте свой любимыйideОткройте его, его каталог выглядит следующим образом:
Справочная интерпретация
После некоторой разборки функции каждого каталога выглядят следующим образом:
- папка проекта vue-native-websocket
- dist скомпилированная папка проекта
- Файл скомпилированного кода build.js
- зависимости проекта node_modules
- папка с исходным кодом проекта src
- Реализация очереди событий и раздача вебсокета Emitter.js
- Код входа плагина Main.js vue
- Режим наблюдателя Observer.js, инкапсуляция основных функций службы веб-сокетов
- тестовый файл тестового модуля
- Конфигурация eslint проекта .eslintrc.json
- файлы .gitignore, которые необходимо игнорировать при загрузке в репозиторий git
- .nvmrc указывает версию узла, которую предполагается использовать в проекте.
- .travis.yml файл конфигурации автоматической сборки
- Файл журнала выпуска версии CHANGELOG.md
- npm-shrinkwrap.json Файл блокировки версии пакета npm
- Файл конфигурации зависимостей проекта package.json
- PUBLISH.md Спецификация релиза после модификации плагина
- Документация по использованию плагина README.md
- webpack.config.js файл конфигурации веб-пакета
- Файл блокировки версии пакета yarn.lock
- dist скомпилированная папка проекта
Прочитав код, мы обнаружили, что его логика реализации очень лаконична, одно слово: замечательно.
Основной код плагинаsrc3 файла в каталоге, далее начнем с входного файла плагинаMain.jsНачните читать.
Как показано ниже, он представляет два файла и плагины, официально требуемые Vue, в качестве объекта, который должен быть предоставлен.installметод.
import Observer from './Observer'
import Emitter from './Emitter'
export default {
install (Vue, connection, opts = {}) {
// ... 其它代码省略 ... //
}
}
Итак, давайте взглянем на первый импортированный файлObserver.jsкод.
Как показано ниже, он вводитEmitter.jsфайл и собственный код реализации.
import Emitter from './Emitter'
export default class {
constructor (connectionUrl, opts = {}) {
// ... 其它代码省略... //
})
}
Emitter.js
Аналогично начнем читать с введенного им файла, а именноEmitter.js, код выглядит следующим образом, после того, как я прочитал код и добавил соответствующие комментарии, он реализует очередь прослушивания событий и функцию запуска событийemit
class Emitter {
constructor () {
this.listeners = new Map()
}
/**
* 添加事件监听
* @param label 事件名称
* @param callback 回调函数
* @param vm this对象
* @return {boolean}
*/
addListener (label, callback, vm) {
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
}
/**
* 移除监听
* @param label 事件名称
* @param callback 回调函数
* @param vm this对象
* @return {boolean}
*/
removeListener (label, callback, vm) {
// 从监听列表中获取当前事件
let listeners = this.listeners.get(label)
let index
if (listeners && listeners.length) {
// 寻找当前事件在事件监听列表的位置
index = listeners.reduce((i, listener, index) => {
if (typeof listener.callback === 'function' && listener.callback === callback && listener.vm === vm) {
i = index
}
return i
}, -1)
if (index > -1) {
// 移除事件
listeners.splice(index, 1)
this.listeners.set(label, listeners)
return true
}
}
return false
}
/**
* 触发监听
* @param label 事件名称
* @param args 参数
* @return {boolean}
*/
emit (label, ...args) {
// 获取事件列表中存储的事件
let listeners = this.listeners.get(label)
if (listeners && listeners.length) {
listeners.forEach((listener) => {
// 扩展callback函数,让其拥有listener.vm中的方法
listener.callback.call(listener.vm, ...args)
})
return true
}
return false
}
}
export default new Emitter()
Observer.js
Далее оглядываемся назадObserver.jsКод, который реализует инкапсуляцию основных функций службы веб-сокетов, является ядром этого плагина. этоconstructorНиже показана часть кода, которая определяет параметры и начальные значения, которые может передать вызывающая сторона плагина.
constructor (connectionUrl, opts = {}) {
// 获取参数中的format并将其转成小写
this.format = opts.format && opts.format.toLowerCase()
// 如果url以//开始对其进行处理添加正确的websocket协议前缀
if (connectionUrl.startsWith('//')) {
// 当前网站如果为https请求则添加wss前缀否则添加ws前缀
const scheme = window.location.protocol === 'https:' ? 'wss' : 'ws'
connectionUrl = `${scheme}:${connectionUrl}`
}
// 将处理好的url和opts赋值给当前类内部变量
this.connectionUrl = connectionUrl
this.opts = opts
// 是否开启重连,默认值为false
this.reconnection = this.opts.reconnection || false
// 最大重连次数,默认值为无穷大
this.reconnectionAttempts = this.opts.reconnectionAttempts || Infinity
// 重连间隔时间,默认为1s
this.reconnectionDelay = this.opts.reconnectionDelay || 1000
// 重连超时id,默认为0
this.reconnectTimeoutId = 0
// 已重连次数,默认为0
this.reconnectionCount = 0
// 传输数据时的处理函数
this.passToStoreHandler = this.opts.passToStoreHandler || false
// 建立连接
this.connect(connectionUrl, opts)
// 如果配置参数中有传store就将store赋值
if (opts.store) { this.store = opts.store }
// 如果配置参数中有传vuex的同步处理函数就将mutations赋值
if (opts.mutations) { this.mutations = opts.mutations }
// 事件触发
this.onEvent()
}
функция соединения
Посмотрим еще разconnetРеализация метода, его код выглядит следующим образом, он установит соединение через веб-сокет в соответствии с адресом сервера веб-сокета и параметрами плагина, переданными пользователем.
// 连接websocket
connect (connectionUrl, opts = {}) {
// 获取配置参数传入的协议
let protocol = opts.protocol || ''
// 如果没传协议就建立一个正常的websocket连接否则就创建带协议的websocket连接
this.WebSocket = opts.WebSocket || (protocol === '' ? new WebSocket(connectionUrl) : new WebSocket(connectionUrl, protocol))
// 启用json发送
if (this.format === 'json') {
// 如果websocket中没有senObj就添加这个方法对象
if (!('sendObj' in this.WebSocket)) {
// 将发送的消息转为json字符串
this.WebSocket.sendObj = (obj) => this.WebSocket.send(JSON.stringify(obj))
}
}
return this.WebSocket
}
функция повторного подключения
Посмотрим еще разreconnectРеализация метода, его код выглядит следующим образом, он считывает максимальное количество повторных подключений, переданных пользователем, а затем повторно устанавливает соединение с сервером веб-сокета.
// 重新连接
reconnect () {
// 已重连次数小于等于设置的连接次数时执行重连
if (this.reconnectionCount <= this.reconnectionAttempts) {
this.reconnectionCount++
// 清理上一次重连时的定时器
clearTimeout(this.reconnectTimeoutId)
// 开始重连
this.reconnectTimeoutId = setTimeout(() => {
// 如果启用vuex就触发vuex中的重连方法
if (this.store) { this.passToStore('SOCKET_RECONNECT', this.reconnectionCount) }
// 重新连接
this.connect(this.connectionUrl, this.opts)
// 触发WebSocket事件
this.onEvent()
}, this.reconnectionDelay)
} else {
if (this.store) {
// 如果启用vuex则触发重连失败方法
this.passToStore('SOCKET_RECONNECT_ERROR', true) }
}
}
функция триггера события
Посмотрим еще разonEventфункция, ее код реализации выглядит следующим образом, она вызоветEmitterМетод emit в веб-сокете распределяет и расширяет четыре события прослушивания в веб-сокете и отправляет их вEmitterкласс для управления.
// 事件分发
onEvent () {
['onmessage', 'onclose', 'onerror', 'onopen'].forEach((eventType) => {
this.WebSocket[eventType] = (event) => {
Emitter.emit(eventType, event)
// 调用vuex中对应的方法
if (this.store) { this.passToStore('SOCKET_' + eventType, event) }
// 处于重新连接状态切事件为onopen时执行
if (this.reconnection && eventType === 'onopen') {
// 设置实例
this.opts.$setInstance(event.currentTarget)
// 清空重连次数
this.reconnectionCount = 0
}
// 如果处于重连状态且事件为onclose时调用重连方法
if (this.reconnection && eventType === 'onclose') { this.reconnect() }
}
})
}
обработчик события vuex
Давайте взглянем на функцию реализации для обработки событий vuex.Его код реализации выглядит следующим образом, который используется для запуска методов в vuex, что позволяет вызывающей стороне передатьpassToStoreHandlerФункция обработчика событий используется для обработки событий перед запуском.
/**
* 触发vuex中的方法
* @param eventName 事件名称
* @param event 事件
*/
passToStore (eventName, event) {
// 如果参数中有传事件处理函数则执行自定义的事件处理函数,否则执行默认的处理函数
if (this.passToStoreHandler) {
this.passToStoreHandler(eventName, event, this.defaultPassToStore.bind(this))
} else {
this.defaultPassToStore(eventName, event)
}
}
/**
* 默认的事件处理函数
* @param eventName 事件名称
* @param event 事件
*/
defaultPassToStore (eventName, event) {
// 事件名称开头不是SOCKET_则终止函数
if (!eventName.startsWith('SOCKET_')) { return }
let method = 'commit'
// 事件名称字母转大写
let target = eventName.toUpperCase()
// 消息内容
let msg = event
// data存在且数据为json格式
if (this.format === 'json' && event.data) {
// 将data从json字符串转为json对象
msg = JSON.parse(event.data)
// 判断msg是同步还是异步
if (msg.mutation) {
target = [msg.namespace || '', msg.mutation].filter((e) => !!e).join('/')
} else if (msg.action) {
method = 'dispatch'
target = [msg.namespace || '', msg.action].filter((e) => !!e).join('/')
}
}
if (this.mutations) {
target = this.mutations[target] || target
}
// 触发store中的方法
this.store[method](target, msg)
}
Main.js
Мы прочитали основной код реализации подключаемого модуля выше. Наконец, давайте взглянем на входной файл подключаемого модуля. Его код выглядит следующим образом. Он применяет пакет, связанный с веб-сокетами, который мы реализовали ранее, к Vue. Глобальный. Он сделал следующее:
- Глобально смонтируйте свойство $socket, чтобы упростить доступ к соединениям сокетов, установленным сокетами.
- Когда ручное подключение включено, смонтируйте метод ручного подключения и метод закрытого подключения к глобальному
- Глобальное микширование, добавление прослушивателей событий сокета, удаление глобально добавленного метода перед уничтожением компонента
import Observer from './Observer'
import Emitter from './Emitter'
export default {
install (Vue, connection, opts = {}) {
// 没有传入连接,抛出异常
if (!connection) { throw new Error('[vue-native-socket] cannot locate connection') }
let observer = null
opts.$setInstance = (wsInstance) => {
// 全局属性添加$socket
Vue.prototype.$socket = wsInstance
}
// 配置选项中启用手动连接
if (opts.connectManually) {
Vue.prototype.$connect = (connectionUrl = connection, connectionOpts = opts) => {
// 调用者传入的参数中添加set实例
connectionOpts.$setInstance = opts.$setInstance
// 创建Observer建立websocket连接
observer = new Observer(connectionUrl, connectionOpts)
// 全局添加$socket
Vue.prototype.$socket = observer.WebSocket
}
// 全局添加连接断开处理函数
Vue.prototype.$disconnect = () => {
if (observer && observer.reconnection) {
// 重新连接状态改为false
observer.reconnection = false
}
// 如果全局属性socket存在则从全局属性移除
if (Vue.prototype.$socket) {
// 关闭连接
Vue.prototype.$socket.close()
delete Vue.prototype.$socket
}
}
} else {
// 未启用手动连接
observer = new Observer(connection, opts)
// 全局添加$socket属性,连接至websocket服务器
Vue.prototype.$socket = observer.WebSocket
}
const hasProxy = typeof Proxy !== 'undefined' && typeof Proxy === 'function' && /native code/.test(Proxy.toString())
Vue.mixin({
created () {
let vm = this
let sockets = this.$options['sockets']
if (hasProxy) {
this.$options.sockets = new Proxy({}, {
set (target, key, value) {
// 添加监听
Emitter.addListener(key, value, vm)
target[key] = value
return true
},
deleteProperty (target, key) {
// 移除监听
Emitter.removeListener(key, vm.$options.sockets[key], vm)
delete target.key
return true
}
})
if (sockets) {
Object.keys(sockets).forEach((key) => {
// 给$options中添加sockets中的key
this.$options.sockets[key] = sockets[key]
})
}
} else {
// 将对象密封,不能再进行改变
Object.seal(this.$options.sockets)
// if !hasProxy need addListener
if (sockets) {
Object.keys(sockets).forEach(key => {
// 添加监听
Emitter.addListener(key, sockets[key], vm)
})
}
}
},
beforeDestroy () {
if (hasProxy) {
let sockets = this.$options['sockets']
if (sockets) {
Object.keys(sockets).forEach((key) => {
// 销毁前如果代理存在sockets存在则移除$options中给sockets添加过的key
delete this.$options.sockets[key]
})
}
}
}
})
}
}
Рефакторинг плагина
Раньше мы читали плагин целиком, а потом уже можно было его использоватьVue3 + TypeScriptпровести его рефакторинг.
Код автора очень хорошо написан, и нет необходимости менять логику, я просто изменил его реализацию кода с js на ts, и изменил метод написания, от которого отказался Vue3, хотя модификация относительно проста, я научился авторский дизайн плагина.Мысли и некоторые тс ямы на которые я наступил,урожай немаленький.
Далее я поделюсь с вами своим процессом рефакторинга и некоторыми ямами, на которые я наступил.
Установить зависимости
Перед рефакторингом с помощью ts нам нужно сначала установить соответствующие пакеты зависимостей и выполнить следующие команды для их установки.
yarn add typescript prettier eslint eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser standard --dev
Затем создайте в корневом каталоге проектаtsconfig.jsonфайл, для файла конфигурации машинописного текста добавьте следующую конфигурацию, установите"declaration": trueВы можете автоматически сгенерировать файл объявления в каталоге типов при запуске команды tsc.
{
"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 выдаст ошибку, метода sendObj не существует в Websocket, сначала хотелlib.dom.d.tsЭтот метод определен в , но, подумав, делать это нецелесообразно, нельзя изменять файл объявления глобальной библиотеки, ведь это плагин.
После некоторого метания я нашел ответ в документации ts Официальная документация ts описана следующим образом.
Как описано в официальной документации, файл декларации ts lookup будет начинаться с текущего файла, нам нужно только использовать его в текущем классеdeclare globalЧтобы расширить, код выглядит следующим образом:
// 扩展全局对象
declare global {
// 扩展websocket对象,添加sendObj方法
interface WebSocket {
sendObj(obj: JSON): void;
}
}
После добавления приведенного выше кода ошибка устранена, перейдите к полному коду:src/Observer.ts
Определение типа функции обратного вызова
существуетEmitter.tsВ файле вызывающий метод добавления мониторинга может передать в него callback-функцию.Параметры этой callback-функции неизвестны, поэтому необходимо указать для него правильный тип.В начале я использовалFunctiontype, но eslint сообщил об ошибке, он не рекомендует использовать его таким образом, ошибка в следующем:
После некоторого метания я нашел следующее решение, просто деконструировать параметры при объявлении типа.
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
Убедитесь, что плагин работает
После рефакторинга плагина мы копируем файлы всего проекта в node_modules/vue-native-websocket проекта vue3, чтобы заменить исходные файлы.
Импортируйте и используйте плагин в 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, окончательный результат выглядит следующим образом:
Отправить PR автору
Кстати, дайте автору пр, и киньте код, который я модифицировал, автору😄vue-native-websocket/pulls
Опубликовать в репозиторий npm
На этом рефакторинг плагина закончен, модифицируем команду сборки в package.json и заменяем ее наtsc, изменить входной файлmainи запись файла объявления типаtypes. Часть кода выглядит следующим образом, перейдите к полному коду:package.json
{
"main": "dist/Main.js",
"types": "dist/types/Main.d.ts",
"scripts": {
"build": "tsc"
}
}
Затем выполнитеyarn run buildКоманда создаст папку dist в корневом каталоге проекта и поместит в нее упакованный файл js.
Файлы в каталоге dist — это пакеты, которые мы хотим опубликовать в репозиторий npm.Перед публикацией в репозиторий npm нам нужно сделать что-то, чтобы сделать плагин более стандартизированным.
Определить спецификации push новой версии
Мы создаем файл PUBLISH.md в корневом каталоге проекта, чтобы сообщить разработчикам, как нажимать после изменения плагина.
## 新版本推送规范
- 对插件进行修改
- 执行 `yarn build` 来生成打包后的文件
- 修改`package.json`中的版本号
- 提交你的修改
- 运行`package.json`中的`changelog`命令来生成更新记录
- 最后将项目推送到你的仓库,然后为主仓库创建一个Pull request
Написать документацию к плагину
В качестве подключаемого модуля необходим файл README.md. Этот файл расскажет разработчикам, как использовать подключаемый модуль. Полный код можно найти по адресу:README.md
Определить спецификацию коммита
Здесь нет ни правил, ни квадратов, то же самое верно и для плагинов. Нам нужны некоторые инструменты для определения спецификации кода коммита, что упростит поддержку плагина.
Установить зависимости
Выполните следующую команду, чтобы установить нужный нам пакет плагина
yarn global add commitizen
Вышеупомянутая команда установит инструмент фиксации глобально.Его функция состоит в том, чтобы предоставить разработчикам инструмент-скрипт для создания информации фиксации, которая соответствует спецификации в соответствии с рекомендациями.
Выполните следующую команду, чтобы сохранить его вpackage.jsonзависимости, будетconfig.commitizenдобавить конфигурацию вpackage.jsonкорневой каталог, конфигурация говоритcommitizen, какой адаптер мы на самом деле хотим использовать, когда пытаемся зафиксировать этот репозиторий.
commitizen init cz-conventional-changelog --save-dev --save-exact
Тогда мы можем пройтиgit czкоманда для фиксации git commit
Принудительно применять спецификации коммитов
использоватьcommitizenинструмент, мы можем выполнитьgit czкоманда для отправки информации о коммите, которая соответствует спецификации, но во время разработки разработчики плагинов не отправляют коммиты через командные строки.Если мы хотим заставить других отправлять коммиты с помощью других инструментов, таких как vscode/webstorm, вы можете использоватьcommitlint+huskyспособ совместного использования.
УстановитьcommitlintПакет проверяет, соответствует ли наше сообщение фиксации обычному формату фиксации, и устанавливает его с помощью следующей команды.
yarn add @commitlint/config-angular @commitlint/cli --dev
Создан в корневом каталоге проектаcommitlint.config.jsфайл, определяющийgit commit -mПри отправке спецификации, после ее создания, пропишите в файле следующее содержимое.
module.exports = { extends: ["@commitlint/config-angular"] };
После выполнения вышеуказанных операций мы можем проверить проверку информации о коммите, отправленной командой.Как показано ниже, если отправлена неправильная фиксация, она сообщит об ошибке.
Далее давайте вместе с Husky реализуем проверку фиксации IDE и выполним следующую команду для установки пакета зависимостей.
yarn add husky --dev
Добавьте хук commit-msg в package.json для проверкиcommitlintТехнические характеристики.
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
После завершения приведенной выше конфигурации, независимо от того, как мы отправляем фиксацию, если информация о фиксации не соответствует нашим спецификациям, будет сообщено об ошибке.Как показано ниже, я использую webstorm для отправки нерегулярной фиксации, и он сообщит об ошибке.
Автоматически генерировать CHANGELOG
Если коммиты соответствуют только что определенному формату Angular, то CHANGELOG может быть автоматически сгенерирован с помощью скрипта при выпуске новой версии.
Здесь мы используем обычный инструмент changelog-cli для его создания и выполняем следующую команду для установки зависимостей.
yarn global add conventional-changelog-cli
Выполните следующую команду в корневом каталоге проекта, чтобы сгенерировать файл CHANGELOG.md:
conventional-changelog -p angular -i CHANGELOG.md -s
Мы можем настроить приведенную выше команду в сценарии в package.json, чтобы мы могли передатьyarn run changelogгенерировать
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
},
Содержимое сгенерированного файла следующее:
Выпуск плагина
Наконец, мы можем опубликовать плагин в репозитории npm.
Здесь основное внимание уделяется рефакторингу подключаемых модулей. Разработчики, которые хотят изучить этапы публикации подключаемых модулей с нуля, могут перейти к другой моей статье:Vue реализует плагин полноэкранной загрузки и публикует его в репозиторий npm.
Войдите в корневой каталог проекта в терминале, выполните следующую команду, войдите в репозиторий npm и введите свое имя пользователя и пароль.
npm login
Выполнение подчиненных команд для публикации в репозиторий npm.
npm publish --access public
Плагин выпущен успешно, переходим в репозиторий npm для поискаvue-native-websocket-vue3, как показано ниже, уже можно найти
Адрес склада npm:vue-native-websocket-vue3
Наконец, мы можем использовать пряжу для установки и использования в проекте.
напиши в конце
- Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊
- Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌