使用vite插件进行kintone自定义开发(手机版自定义范例)

前言

Youtube上的前端网红「Theo」在React文档仓库发起了一个Pull request,号召React文档不要再默认推荐CRA(create react app),而是应该将Vite作为构建应用的首选。 vite的影响力已经从vue蔓延到了react,可见在前端工程化开发中,它已经越来越流行,是时候该从webpack切换到vite了。

为什么使用vite

Vite 采用了基于ES Module的开发服务器。进行本地开发时,使用HMR,速度快了很多。相较于webpack,有很多优势:

  1. 更快的热更新机制
  2. 更快的打包效率
  3. 更简单的配置

在做kintone开发时,我也很想尝试使用vite进行开发,构建。所以也查询了一些相关文章。 但是只看到了一些介绍如何使用vite(rollup)打包kintone自定义js的文章。但是却没有看到如何利用vite进行kintone开发的文章。所以我想到开发一个vite插件来解决这个问题。

vite-plugin-kintone-dev

www.npmjs.com/package/vit...

这个插件实现的功能:

  1. 支持使用vite创建kintone自定义js,hmr让你的开发快如闪电
  2. 支持react,vue等不同的前端框架
  3. 构建时支持打包并自动上传kintone

实践:kintone手机版自定义

这次我们结合vite插件,以kintone手机版的自定义开发为范例,给大家做演示。 文章有点长,大家可以提前先看下成果图:最后成果图 技术栈:vite4 + vue3 + vant4

1. 使用vite脚手架初始化vue项目

首先通过vite脚手架工具。创建一个vue项目

sh 复制代码
npm create vue@latest

设置项目名: kintone-mobile-custom(这是我的预设) 选择vue,TypeScript。然后根据需求进行选择。并进行初始化安装.

sh 复制代码
cd kintone-mobile-custom
npm install

2. 安装kintone开发的vite插件

sh 复制代码
npm install -D vite-plugin-kintone-dev

第一次启动时,会自动检查你的env文件的设置模版。如果没有配置,会启动命令行交互,让你输入配置信息。同时自动更新你的env文件。

如果你的env文件设置有误,可以自行去修改。 (serve模式下为".env.development"文件, build模式下为".env.production"文件)

插件的参数说明

json 复制代码
{
   outputName: "mobile",   //最后打包的名字
   upload: true
}

配置vite

vite.config.ts

ts 复制代码
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import kintoneDev from "vite-plugin-kintone-dev";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    kintoneDev({
      outputName: "mobile",
      upload: true
    }),
  ],
});

3. 安装其他的一些库

图片库

接下来我们再配置一个图片库。 非常常用的一个插件图库插件Unplugin Icons github.com/unplugin/un...

sh 复制代码
npm install -D unplugin-icons unplugin-vue-components

它的具体设置,请参考它的官网。

添加所有icon资源,不用担心,真正打包时,它是按需加载的。

sh 复制代码
npm i -D @iconify/json

手机ui库

然后我们使用vant这个库来做为手机开发的ui库。 vant-ui.github.io/vant/#/en-U...

sh 复制代码
npm install vant

tsconfig.app.json配置

tsconfig.app.json

json 复制代码
...
"compilerOptions": {
    "types": ["unplugin-icons/types/vue"],
    ...
}
...

vite的最后配置

vite.config.ts

ts 复制代码
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
import Components from "unplugin-vue-components/vite";
import { FileSystemIconLoader } from "unplugin-icons/loaders";
import kintoneDev from "vite-plugin-kintone-dev";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    kintoneDev({
      platform: "PORTAL",
      type: "MOBILE",
    }),
    vue(),
    Components({
      resolvers: [IconsResolver()],
    }),
    Icons({
      compiler: "vue3",
      customCollections: {
        "my-icons": FileSystemIconLoader("./src/assets/icons"),
      },
    }),
  ],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
});

代码的开发

1. 修改main.ts

初始化vue,并 将它的根节点挂载在手机的门户上方的空白部分的元素 src/main.ts

ts 复制代码
...
kintone.events.on("mobile.portal.show", (event) => {
  const app = createApp(App);
  app.use(router);
  app.use(Tabbar);
  app.use(TabbarItem);
  app.mount(kintone.mobile.portal.getContentSpaceElement()!);
  return event;
});
...

2. 隐藏掉原先的手机画面

src/assets/main.css

css 复制代码
.gaia-mobile-v2-portal-announcement-container,
.gaia-mobile-v2-portal-appwidget-container,
.gaia-mobile-v2-portal-spacewidget-container {
  display: none;
}
.van-hairline--top-bottom::after,
.van-hairline-unset--top-bottom::after {
  border-width: 0;
}
.gaia-mobile-v2-viewpanel-header {
  background-color: #4b4b4b;
}
.gaia-mobile-v2-viewpanel-contents {
  border-radius: 0;
}
.van-tabbar {
  width: 100vw;
}
.van-tabbar--fixed {
  left: unset;
}
.gaia-mobile-v2-portal-header-container .gaia-mobile-v2-portal-header::after {
  background: none;
}
.group-module-background {
  background-color: rgba(255, 255, 255);
  border-radius: 10px;
  box-shadow: 0 0 5px 0 #ced3d4;
}

3. 添加tabbar

src/App.vue

js 复制代码
<script setup lang="ts">
import { ref } from "vue"
import type { Ref } from 'vue'

const active: Ref<number> = ref(0)
</script>

<template>
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </router-view>

  <van-tabbar route v-model="active" active-color="#febf00" inactive-color="#b8b8b5">
    <van-tabbar-item replace to="/">
      <span>Home</span>
      <template #icon="props">
        <i-mdi-home class="tabbar-icon" />
      </template>
    </van-tabbar-item>
    <van-tabbar-item replace to="/contacts">
      <span>Contacts</span>
      <template #icon="props">
        <i-mdi-contacts class="tabbar-icon" />
      </template>
    </van-tabbar-item>
    <van-tabbar-item replace to="/space">
      <span>Space</span>
      <template #icon="props">
        <i-mdi-shape-circle-plus class="tabbar-icon" />
      </template>
    </van-tabbar-item>
    <van-tabbar-item replace to="/star">
      <span>Star</span>
      <template #icon="props">
        <i-mdi-star class="tabbar-icon" />
      </template></van-tabbar-item>
    <van-tabbar-item replace to="/todo">
      <span>Todo</span>
      <template #icon="props">
        <i-mdi-tooltip-edit class="tabbar-icon" />
      </template></van-tabbar-item>
  </van-tabbar>
</template>

<style scoped>
.tabbar-icon {
  font-size: 1em;
}
</style>

4. 模拟一个订单列表应用

我们在kintone上创建一个应用。并准备以下字段。

フィールド名 フィールドコード Type
title title 文字列 (1行)
num num 数値
desc desc 文字列 (1行)
price price 数値
thumb thumb 文字列 (1行)

接着请自行添加一些数据。并且设置【API token】

5. 安装kintone js sdk

sh 复制代码
npm install @kintone/rest-api-client 

6. 添加kintone的ts声明

  1. 安装kintone的ts生成工具
sh 复制代码
npm install -D @kintone/dts-gen
  1. 根据应用生成ts声明 请根据自己的环境输入
sh 复制代码
npx @kintone/dts-gen --base-url https://xxxx.cybozu.com -u  xxxx -p xxxx --app-id xx
ts:tsconfig.app.json 复制代码
{
  ...
  "files": ["./node_modules/@kintone/dts-gen/kintone.d.ts"],
  ...
}

7. 封装kintone api请求

src/service/kintoneApi.ts

ts 复制代码
//@ts-ignore
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
const APP_ID = import.meta.env.VITE_APP_ID;

export class KintoneApi {
  client: KintoneRestAPIClient;
  constructor() {
    this.client = new KintoneRestAPIClient({
      baseUrl: `https://${import.meta.env.VITE_KINTONE_URL}`,
      auth: {
        apiToken: import.meta.env.VITE_API_TOKEN,
      },
    });
  }

  public async getAllRecords() {
    try {
      return await this.client.record.getAllRecords({ app: APP_ID });
    } catch (error) {
      return;
    }
  }
}

8. env文件添加一些kintone的配置

添加kintone应用的app id和api token .env.development

sh 复制代码
VITE_API_TOKEN=xxx
VITE_APP_ID=xxx

9. 创建页面

先删除view下原有的文件添加以下文件。

view/Home.vue view/Contacts.vue view/Space.vue view/Star.vue view/Todo.vue

在这些页面写一些模拟内容

Home页通过获取kintone的数据来模拟订单数据 src/view/Home.vue

js 复制代码
<template>
  <List v-model:loading="loading" :finished="finished" @load="onLoad">
    <Card v-for="(item, index) in list" :key="index" :num="item.num.value" :price="item.price.value"
      :desc="item.desc.value" :title="item.title.value" :thumb="item.thumb.value" />
  </List>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import type { Ref } from 'vue'
import { Card, List } from 'vant'
import { KintoneApi } from '@/service/kintoneApi'

const list: Ref<kintone.types.Fields[]> = ref([])
const loading = ref(false);
const finished = ref(false);

const onLoad = () => {
  const kintoneClient = new KintoneApi()
  kintoneClient.getAllRecords().then((res) => {
    list.value = res
    loading.value = false;
    finished.value = true;
  })
};
</script>

其他页面准备一些模拟数据 比如Contacts.vue view/Contacts.vue

js 复制代码
<template>
  <div>
    <h1>Contacts</h1>
  </div>
</template>

10. 设置路由

这边需要使用createWebHashHistory这种方式创建路由。因为createWebHistory方式刷新时会被后端路由解析。 src/router/index.ts

ts 复制代码
import { createRouter, createWebHashHistory } from "vue-router";

const router = createRouter({
  history: createWebHashHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      name: "home",
      component: () => import("../views/Home.vue"),
    },
    {
      path: "/contacts",
      name: "contacts",
      component: () => import("../views/Contacts.vue"),
    },
    {
      path: "/space",
      name: "space",
      component: () => import("../views/Space.vue"),
    },
    {
      path: "/star",
      name: "star",
      component: () => import("../views/Star.vue"),
    },
    {
      path: "/todo",
      name: "todo",
      component: () => import("../views/Todo.vue"),
    },
  ],
});

export default router;

启动项目

sh 复制代码
npm run dev

启动后,kintone首页自定义会自动上传kintone_module_hack.js文件

最后的成果图

我们看到一个简单的手机自定义骨架就出来了。 这只是一个最简单的范例,实际上,还需要考虑的地方还有很多,比如集中式状态管理,页面生命周期缓存,数据下拉刷新等等。如果你对手机版感兴趣,我这边有个三年前的项目。不过当时使用的还是vue2。可以看看。 github.com/kintone-sam...

构建阶段

sh 复制代码
npm run build

运行完,会自动上传打包后的代码到kintone。

示例项目github地址

github.com/GuSanle/kin...

在react中使用的范例

本插件同样适用于使用vite在kintone中构建react应用。 使用react范例可参考: github.com/GuSanle/vit...

插件原理

插件的原理是通过js自定义,hack出一个类型为module的script标签。用来加载main.ts文件。 不过基于vue和react的不同,还需要结合vite的文档做一些代码的inject。

可能存在的一些问题

如果开发时遇到イベントハンドラー登録の適切なタイミングについて问题, 可以尝试在使用kintone事件后挂载后,使用如下代码解决问题。 (构建时,可以删除,因为构建时,不再使用esm模式,不存在异步加载问题。) src/main.ts的示例: src/main.ts

ts 复制代码
import { createApp } from "vue";
import App from "./App.vue";

kintone.events.on("app.record.detail.show", (event) => {
  const app = createApp(App);
  app.mount(kintone.app.record.getHeaderMenuSpaceElement()!);
  return event;
});

//通过手动执行kintone事件,来解决异步事件执行时机问题
const event = new Event("load");
// @ts-ignore
cybozu.eventTarget.dispatchEvent(event);

总结

使用了vite之后,hmr技术使得我们对代码进行修改后,会马上在页面上体现,非常的迅速,开发体验非常好。 kintone结合vite的开发就讲到这里。如果你对我这个插件或者这个手机范例感兴趣,欢迎一起交流,给个star。谢谢。

相关推荐
王小金Ryan4 天前
开发一个Vite插件,给所有DOM节点插入自定义属性
vue.js·vite
前端霸王防脱发洗发水6 天前
Vite常用插件配置
javascript·vue.js·vite
friend_ship9 天前
Vue3.0都有哪些新特性及优化点
vue.js·vite·vue3.0·es6新特性·proxy响应式对象
jason_yang9 天前
vue3复习-源码-迷你版vite
vue.js·vite
jason_yang9 天前
vue3复习-源码-编译原理-自定义vite插件
vue.js·vite
小霖家的混江龙9 天前
Vite 打包 H5 如何注入版本号
前端·vite
web_code10 天前
vite依赖预构建(源码分析)
前端·面试·vite
yinshimoshen18 天前
基于vite实现基本的浏览器兼容解决方案
前端·vite
applebomb22 天前
vite server正则表达式
正则·vite·proxy·regexp·转发·server
o翔哥o25 天前
我把大型团队项目从 vite 前端迁移到了 rsbuild,收益如何?
前端·vite·前端工程化