yuffie-browser v0.4.6
Yuffie Browser
vue3 版阿拉丁 h5 基础库 主要有容器、客户端、配置平台的通信及模拟,前端路由导航(活动入口)功能
0.4.0版本以后,layout组件支持新配置平台,不再对group_id进行判断过滤
[TOC]
项目初始化方式
项目建议使用 vite 提供的 vue 模板初始化,可参考文档。
h5 库安装方法
yarn add yuffie-browser
全局注册
createYuffie 创建一个 vue 插件,并且在 main.ts 中注册,使用方法如下:
--- yuffie.ts
import { createYuffie, YuffieConfig } from 'yuffie-browser'
// 活动树,用于声明活动顺序
const routeTree = [
// 层级1
{
type: '10001',
},
{
type: '10002',
children: [
{
type: '10002-1',
},
{
type: '10002-2',
},
],
},
{
type: '10003',
children: [
{
type: '10003-1',
},
{
type: '10003-2',
},
],
},
{
type: '10004',
children: [
{
type: '10004-1',
},
{
type: '10004-2',
},
],
},
]
// 活动目录扫描
const activities = import.meta.glob('/src/activities/**/activity.vue')
const apiMockFiles = import.meta.globEager('/src/activities/**/api_mock.*')
const clientMocksFiles = import.meta.globEager('/src/activities/**/client_mock.*')
const configMocksFiles = import.meta.globEager('/src/activities/**/config_mock.*')
// 根据活动树结构生成路由
const createRoute = (rt: typeof routeTree, p = '/src/activities') => {
const routes: YuffieConfig['routes'] = []
rt.forEach((r, i) => {
const path = `${p}/${r.type}`
routes[i] = {
type: r.type,
component: activities[`${path}/activity.vue`],
apiMock: apiMockFiles[Object.keys(apiMockFiles).find(file => RegExp(`${path}/api_mock.*`).test(file))!]?.default,
clientMock:
clientMocksFiles[Object.keys(clientMocksFiles).find(file => RegExp(`${path}/client_mock.*`).test(file))!]
?.default,
configMock:
configMocksFiles[Object.keys(configMocksFiles).find(file => RegExp(`${path}/config_mock.*`).test(file))!]
?.default,
}
if (r.children) {
routes[i].children = createRoute(r.children, path)
}
})
return routes
}
const config: YuffieConfig = {
url: 'ws://121.4.214.235:1945',
useMock: true,
// 也支持手写yuffie-routes
routes: createRoute(routeTree),
}
export default createYuffie(config)
--- main.ts
createApp(root).use(Yuffie).mount('#app')
YuffieConfig 是注册配置的类型文件,包括:
| 字段 | 说明 |是否必填|示例|
| ------------ | ------------------------------------- |--|--|
| url | 后台地址(websocket) |是|ws://10.12.28.63:1945
|
| routes | 路由 |是|类型为YuffieRouteList
,包含所有需展示的活动配置,支持自定义,也可以扫描路由自动生成|
| useMock | 是否使用 mock|是| true||
| token | 用户登录的 token,用于联调阶段模拟。|否||
| gameId | gameId,用于 session 模拟。 |否||
| groupId | 游戏组号,用于 session 模拟。 |否||
| roleId | 角色 Id,用于 session 模拟。 |否||
| userId | 用户 Id, 用于 session 模拟。 |否||
| channalId | 渠道 ID, 用于 session 模拟。 |否||
| useMock | 是否使用 mock|是| true||
mockLoadingTime | 请求 loading 时间,单位:毫秒 | number
| 否 |
| onReceive | 发送 websocket 消息后的回调,可以用于消息的展示。 |否|onReceive(res) {console.log(res)}
|
use(Yuffie)后根节点会注册一些全局状态,提供给活动页面。同时该插件也能自动判断运行时的活动 id,如果是 mock 启动,则会自动替换 mock 响应。
活动中使用
在活动代码中,使用useYuffie
方法获取 h5 库的 context,示例如下:
import { useYuffie } from 'yuffie-browser'
const { onCreate, send, getActivityConfig, client } = useYuffie()
此方法等同于
import { yuffieInjectToken } from 'yuffie-browser'
inject(yuffieInjectToken)
上下文中注入的方法
包含路由,通信,事件监听等。详见下表
- yuffieNotice
- yuffieRuntimeConfig
- yuffieSession
- activityConfigs
- send
- getActivityConfig
- client
- onCreate
字段 | 说明 | 类型 | 是否为响应式(Ref) |
---|---|---|---|
yuffieNotice | websocket 中主动推送事件的总线,包含 on,off 方法。用于订阅和取消订阅。其中 NotifyEnum.ConnectNotify 方法为 ws 链接建立后触发,需要在根节点监听此方法,并实现登录鉴权等方法,可以参照示例中的登录方法 | {on: (type: NotifyEnum, callback: NoticeHandler) = > void, off: (type: NotifyEnum, callback: NoticeHandler) => {} } | 否 |
yuffieRuntimeConfig | 运行时参数,包含 id 和 token,token 为登录时('/'路由时),读取 route.query.token 参数,并自动保存到此参数中;id 为活动路由时 route.query.id 参数 | Ref<Partial<YuffieRuntimeConfig>> | 是 |
yuffieSession | 用于保存登录接口返回的身份信息 | Ref<Partial<YuffieSessionConfig>> | 是 |
yuffieTime | 登录前为本地时间,登录后为服务器时间。毫秒级时间戳 | Ref<number> | 是 |
yuffieLoading | 是否正在发送请求,可以用作全局的 loading 判断 | Ref<boolean> | 是 |
useMock | 是否开启 mock(和全局注册的 useMock 值一致,无响应式) | boolean | 否 |
activityConfigs | 活动静态配置,会从通知中自动读取, yuffieNotice 中订阅,并 unzip 获取。如果传递此列表则说明连接后台,且后台无配置推送。如果此配置有值,则下方 getActivityConfig 方法不会读取 config_mock 文件,而是读取此配置。 | Ref<Array<YuffieActivityConfig>> | 是 |
getActivityConfig | 根据活动类型获取活动静态配置,如果是 mock 环境,则会读取同文件夹下的 config_mock.js 中数据。 | function<T = any>(id?) { return T } | 否 |
send | promise 包装的 websocket 请求,method 和 payload 为请求后台参数,id 如果不传,则默认为当前活动 id,指定后则根据 id 参数发送。 | function<T = any>(method: string, payload: object, id?: string) { return Promise<YuffieResponse<T>> } | 否 |
onCreate | 组件初始化方法,模拟生命周期,在浏览器隐藏到打开时自动触发传入的函数。 | onCreate(func: function) | 否 |
client | 游戏端注入浏览器的方法调用,下文会详细说明 | 否 |
方法如果需要推导类型,则可传入方法泛型参数,如:
const res = send<string>(...payload)
示例中会自动推断请求返回的 res 类型为 string
client 对象为浏览器注入的 js 方法,包含以下内容:
方法 | 类型 |
---|---|
isOnline | () => Promise<boolean> |
showEntry | () => void |
hideEntry | () => void |
showMark | () => void |
hideMark | () => void |
showBrowser | () => void |
hideBrowser | () => void |
getItemsCount | (payload: Array<{type: number;id: number;}>) =>Promise<Array<number>> |
canPutInBag | (payload: Array<{type: number;id: number;}>) =>Promise<boolean> |
event | {on: (type: ClientNotifyEnum, callback: function) = > void,off: (type: ClientNotifyEnum, callback: function) => {}} |
方法对应的作用见阿拉丁接入文档。
event 对象和 yuffieNotice 使用方法大致相同,包含 on,和 off 两个方法。用于订阅游戏主动通知的事件,事件包含on-bag-change
, on-browser-show
两种。
如果是 mock 模式启动,调用 client 的方法,会自动读取当前活动目录下的 client_mock.js 读取 mock 的返回值。
Layout 组件
根据代码(自动路由中为文件路径),和配置平台的条件,自动生成带红点的导航栏,和 router-view 的组件。
如果全局注册时传递 useMock 为 true,导航栏会根据全局注册 routes,不判断条件直接展示路径下的全部活动。 否则则会判断配置中的组号(group_id)时间(activity_time)显示标题(show_title)等条件,过滤出对应的活动。
可以多层级嵌套使用,用于展示树形系列活动布局。
<script lang="ts">
import { YuffieLayout } from 'yuffie-browser'
export default {
name: 'App',
components: {
YuffieLayout,
},
}
</script>
<template>
<YuffieLayout>
<!-- 导航 -->
<template #menu="{ activity, showMark }">
<div class="menu">
<div>{{ activity.name }}</div>
</div>
</template>
<!-- 自定义菜单 -->
<template #custom>
<div>自定义内容</div>
</template>
<!-- 关闭 -->
<template #close>
<div class="close" />
</template>
</YuffieLayout>
</template>
其中有 slot menu 需要自行编写,用于活动的导航定义。 建议在根组件编写一下两个 slot:
- custom
- close
custom 用于右上角的自定义按钮,目前在天龙怀旧中为刷新和问号按钮。close 用于右上角的关闭按钮。 menu 和 close 都已经绑定好了点击的事件(切换路由,点击关闭游戏窗口)
slot menu 提供的上下文参数包括
- activity: 单个活动的配置
- showMark: 是否显示红点
- filterResult: 是否被配置过滤, 即根据条件此 activity 是否应该显示(needFilter 为 true 时默认会过滤显示的配置,此值会为 true;当组件中使用 needFilter 为 false,活动配置不被过滤时,则 filterResult 的值为根据配置中活动是否显示计算的结果)
- clientEvent: 跳转事件,当点击阻止冒泡时,可以使用此字段
layout 组件的 props:
props 字段 | 类型 | 说明 |
---|---|---|
groupId | string | 组号:传递则根据此字段筛选活动展示,如果不传,则会根据登录返回的 session 中读取 |
useTheme | boolean | 是否使用主题 |
theme | string | 主题,即 assets/themes 下的 css 文件,此字段会读取此 prop 的同名 css,或 websocket 推送配置的 theme 字段,无配置则读取 default.css |
autoFirstView | boolean | 进入活动布局后,是否自动进入需展示的第一个活动,默认为 false |
showView | boolean | 是否显示 router-view, 默认为 true |
showTitle | boolean | 是否显示导航栏, 默认为 true |
titleStyle | StyleValue | 导航栏容器样式 |
viewStyle | StyleValue | router-view 容器样式 |
rootStyle | StyleValue | 组件根节点样式 |
path | string | 组件的当前路径,如果不传,则会自动读取 route.path 的值。用于查找当前路径的子活动。如果有多层级导航同时显示的情况,需要手动指定导此字段。 根节点必须设置为'/',用于入口和入口红点的展示 |
needFilter | boolean | 活动导航是否根据配置过滤,默认为 true。为 false 用于某些活动没有活动配置,或者层级导航需要展示不开启的活动情况。 |
layout 组件的暴露属性(使用 definExpose 声明,通过组件的 ref 调用):
| props 字段 | 类型 | 说明 |
| ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| filterActivities | ComputedRes<Array<YuffieActivityConfig>>
| 根据过滤条件筛选出来,导航要显示的活动列表 |
| onClickRoute | (activity: YuffieActivityConfig) => void
| 点击活动触发的路由跳转方法 |
以下是默认样式,可以覆盖,实现布局的自定义
// layout
.layout-main-container {
display: flex;
position: relative;
box-sizing: border-box;
overflow: auto;
.layout-menu {
overflow-y: auto;
}
.layout-view {
flex: 1;
}
.layout-tools {
position: absolute;
top: 0;
right: 0;
display: flex;
}
}
注意:使用主题时,会等待后台配置推送后后,再加载主题 css。 使用树形布局时,每层 v-deep 的覆盖都会影响子节点,因此建议使用行内样式,来控制布局的结构。如果必须使用外部样式,在子层级可能需要手动清除样式的影响。
树形布局如果父级节点没有活动 id,即进入层级后只有导航情况,则需要在子活动中配置 parent_type, parent_title 两个字段,用于找到父级组件,并展示标题。自动生成的父级配置会自动计算子活动的红点和展示时间。
建议的活动代码组织方式
活动需要以下四种文件:
- activity.vue
- api_mock.js
- client_mock.js
- config_mock.js
activity.vue 为单文件组件,包含一种活动类型的前端代码逻辑。
api_mock.js 为对后端接口的数据模拟,需要 export 一个 js object, key 为调用后台的 method, value 为需要模拟的响应,示例如下:
export default {
main: [
{
levelId: 1,
limit: 1,
},
{
levelId: 2,
limit: 1,
},
{
levelId: 3,
limit: 0,
},
],
get_items: {
code: 0,
msg: '领取成功',
},
}
client_mock.js 为对注入到浏览器环境的 js 事件模拟,需要 export 一个 js object, key 为注入 window 中的方法名称, value 为需要模拟的结果,示例如下:
export default {
showEntry: true,
hideEntry: true,
isOnline: true,
showMark: true,
hideMark: true,
showBrowser: true,
hideBrowser: true,
getItemsCount: 1000,
canPutInBag: true,
}
config_mock.js 为对活动静态配置的模拟,具体结构需要参照活动的具体需求来定义,示例如下:
export default {
id: '2021070199',
title: '标题',
}
Mock
库中提供了虚拟数据生成的工具,使用方法如下:
import { faker } from 'yuffie-browser'
const guid4 = faker.guid4()
const num = faker.digitOnes()
const url = faker.imgUrl()
faker 基于 mockjs 库的 random 模块封装,补充提供了以下方法:
方法 | 方法说明 |
---|---|
imgUrl | 获取一个随机图片 |
digitOnes | 获取一个 0-9 范围内的数字 |
digitTens | 获取一个 10-9999 范围内的数字 |
digitOnesStr | 获取一个 0-9 范围内的数字转字符串 |
digitTensStr | 获取一个 10-9999 范围内的数字转字符串 |
guid4 | 四位 guid |
guid4Hex | 16 进制的四位 guid |
guid8Hex | 16 进制的八位 guid |
name | 名字 |
title | 标题 |
字符串转换
库中提供了驼峰和下划线字符串互转的方法
方法 | 方法说明 |
---|---|
toHump | 字符串 下划线转驼峰 |
toLine | 字符串 驼峰转下划线 |
record2Line | 对象 key 从下划线转化为驼峰(非递归) |
record2Hump | 对象 key 从驼峰转化为下划线(非递归) |
使用方法如下:
import { toHump, toLine, record2Line } from 'yuffie-browser'
console.log(toHump('group_id')) // 'groupId'
console.log(toLine('groupId')) // 'group_id'
console.log(record2Line({ roleId: '1', groupId: '2' })) // {role_id: '1', group_id: '2'}
console.log(record2Line({ role_id: '1', group_id: '2' })) // { roleId: '1', groupId: '2' }
框架如何发布
编译后上传 npm 发布即可。
yarn build && yarn publish
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago