HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

uniapp对微信小程序异步加载分包组件的做法--vite插件方案

分包

微信原生文档见 文档
社区有方案,是在style中写入
这里的方案是使用vite插件,在编译完成后,对目标插件进行代码注入 (仅适用vue3 + vite) , 目前运行在uniapp 微信小程序上

目录结构:

/project-config  
      componentPlaceholder  
             componentPlaceholder.js  
      vite.uni-component-placeholder.js

vite插件代码

/**   
    实现了 uni编译完成后 , 处理 componentPlaceholder   

 */   
var fs = require('fs');  
var path = require('path');  

class ProcessComponentPlaceholder{  

    constructor(){  

        this.destFolder = process.env.UNI_OUTPUT_DIR;  

    }  

    process(){  
        let dev_fold = process.env.UNI_INPUT_DIR;  
        let dir = dev_fold+"/project-config/componentPlaceHolder/"  
        let files = fs.readdirSync(dir, 'utf-8');  
        let fileMap = {}  
        files.some((f) => {  
            let p = dir + "/" + f;  
            let stat = fs.lstatSync(p);  
            if (!stat.isDirectory()) {  
                fileMap[f] = p;  
            }  
        })  

        this.processNodes(fileMap)  

    }  

    processNodes(fileMap){  
        console.log("处理异步组件引用componentPlaceHolder",fileMap)  
        for(let jsonFileName in fileMap){  
            let path = fileMap[jsonFileName];  
            if(!path.lastIndexOf(".js")<0)continue;  
            let obj = require(path);  
            // console.log("读componentPlaceHolder", obj)  
            this.processOneConfig(obj);  
        }   
        console.table("componentPlaceHolder处理完毕")  
    }  

    processOneConfig(config){  
        for(let f in config){ //某个配置文件  
            let weixinJSONFile = this.destFolder+f+".json";  
            fs.readFile(weixinJSONFile,'utf8',(err, data)=>{  
                  if (err) {  
                    return console.log('componentPlaceHolder文件读取失败,失败原因是:' + err)  
                  }  
               let weixinJSON = JSON.parse(data);  
               // console.log("读componentPlaceHolder",weixinJSON);  

                //准备合并配置      
                let usingComponents = weixinJSON["usingComponents"]||{};  
                let componentPlaceholder = weixinJSON["componentPlaceholder"]||{};  
                let customConfig = config[f];  
                for(let tag in customConfig)   {  
                    let tagVal = customConfig[tag];  
                    let path = tagVal.path;  
                    let replace = tagVal.replace;  
                    // console.log(weixinJSONFile+ "  " +tag+" "+path )  
                    if(!usingComponents[tag]){  
                        usingComponents[tag]="../.."+path;  //这里的双层目录有必要可能动态算相对层级,根据项目自身情况而定  
                    }  
                    if(!componentPlaceholder[tag]){  
                        componentPlaceholder[tag] = replace;  
                    }  
                }  
                weixinJSON.usingComponents = usingComponents;  
                weixinJSON.componentPlaceholder = componentPlaceholder;  
               fs.writeFileSync(weixinJSONFile, JSON.stringify(weixinJSON,null,4))  

            }) ;  
        }  

    }  

}  

export default (options)=> {  
    var name = 'vite-plugin-copy-uniapp_config';  

    return {  
        name: name,  
        enforce: 'post',   
        closeBundle:()=>{  //buildEnd之后运行  
            options.forEach(function(option) {  

                    let processor =  new ProcessComponentPlaceholder();  
                     processor.process();  
            });  
        }  

    };  
}   

配置文件 componentPlaceholder.js 代码:

/**   
 配置:  

    {  
        "某个包的组件路径,不带.vue后缀":{  
            "组件名,一般为文件名不带.vue和路径":{  
                path:"引用某个包的组件路径,不带.vue后缀",  
                replace:"未加载完成时的替换组件,比如view或某个全局组件"  
            }   
        }  
    }   
 */  
module.exports =  {   
    "/pages/index/index":{  
        "tabbar-me":{  
            path:"/package-my/pages/my/my",  
            replace:"view"  
        },   
    },  

}  

vite.config.js

import viteComponentPlaceHolder from "./project-config/vite.uni-component-placeholder.js"  

plugins.push(viteComponentPlaceHolder([{}]))  

export default defineConfig({  
    plugins   
});  

启动时会有日志
08:48:43.602 处理异步组件引用componentPlaceHolder {
08:48:43.609 'componentPlaceholder.js': '/project-config/componentPlaceHolder//componentPlaceholder.js'
08:48:43.610 }

继续阅读 »

微信原生文档见 文档
社区有方案,是在style中写入
这里的方案是使用vite插件,在编译完成后,对目标插件进行代码注入 (仅适用vue3 + vite) , 目前运行在uniapp 微信小程序上

目录结构:

/project-config  
      componentPlaceholder  
             componentPlaceholder.js  
      vite.uni-component-placeholder.js

vite插件代码

/**   
    实现了 uni编译完成后 , 处理 componentPlaceholder   

 */   
var fs = require('fs');  
var path = require('path');  

class ProcessComponentPlaceholder{  

    constructor(){  

        this.destFolder = process.env.UNI_OUTPUT_DIR;  

    }  

    process(){  
        let dev_fold = process.env.UNI_INPUT_DIR;  
        let dir = dev_fold+"/project-config/componentPlaceHolder/"  
        let files = fs.readdirSync(dir, 'utf-8');  
        let fileMap = {}  
        files.some((f) => {  
            let p = dir + "/" + f;  
            let stat = fs.lstatSync(p);  
            if (!stat.isDirectory()) {  
                fileMap[f] = p;  
            }  
        })  

        this.processNodes(fileMap)  

    }  

    processNodes(fileMap){  
        console.log("处理异步组件引用componentPlaceHolder",fileMap)  
        for(let jsonFileName in fileMap){  
            let path = fileMap[jsonFileName];  
            if(!path.lastIndexOf(".js")<0)continue;  
            let obj = require(path);  
            // console.log("读componentPlaceHolder", obj)  
            this.processOneConfig(obj);  
        }   
        console.table("componentPlaceHolder处理完毕")  
    }  

    processOneConfig(config){  
        for(let f in config){ //某个配置文件  
            let weixinJSONFile = this.destFolder+f+".json";  
            fs.readFile(weixinJSONFile,'utf8',(err, data)=>{  
                  if (err) {  
                    return console.log('componentPlaceHolder文件读取失败,失败原因是:' + err)  
                  }  
               let weixinJSON = JSON.parse(data);  
               // console.log("读componentPlaceHolder",weixinJSON);  

                //准备合并配置      
                let usingComponents = weixinJSON["usingComponents"]||{};  
                let componentPlaceholder = weixinJSON["componentPlaceholder"]||{};  
                let customConfig = config[f];  
                for(let tag in customConfig)   {  
                    let tagVal = customConfig[tag];  
                    let path = tagVal.path;  
                    let replace = tagVal.replace;  
                    // console.log(weixinJSONFile+ "  " +tag+" "+path )  
                    if(!usingComponents[tag]){  
                        usingComponents[tag]="../.."+path;  //这里的双层目录有必要可能动态算相对层级,根据项目自身情况而定  
                    }  
                    if(!componentPlaceholder[tag]){  
                        componentPlaceholder[tag] = replace;  
                    }  
                }  
                weixinJSON.usingComponents = usingComponents;  
                weixinJSON.componentPlaceholder = componentPlaceholder;  
               fs.writeFileSync(weixinJSONFile, JSON.stringify(weixinJSON,null,4))  

            }) ;  
        }  

    }  

}  

export default (options)=> {  
    var name = 'vite-plugin-copy-uniapp_config';  

    return {  
        name: name,  
        enforce: 'post',   
        closeBundle:()=>{  //buildEnd之后运行  
            options.forEach(function(option) {  

                    let processor =  new ProcessComponentPlaceholder();  
                     processor.process();  
            });  
        }  

    };  
}   

配置文件 componentPlaceholder.js 代码:

/**   
 配置:  

    {  
        "某个包的组件路径,不带.vue后缀":{  
            "组件名,一般为文件名不带.vue和路径":{  
                path:"引用某个包的组件路径,不带.vue后缀",  
                replace:"未加载完成时的替换组件,比如view或某个全局组件"  
            }   
        }  
    }   
 */  
module.exports =  {   
    "/pages/index/index":{  
        "tabbar-me":{  
            path:"/package-my/pages/my/my",  
            replace:"view"  
        },   
    },  

}  

vite.config.js

import viteComponentPlaceHolder from "./project-config/vite.uni-component-placeholder.js"  

plugins.push(viteComponentPlaceHolder([{}]))  

export default defineConfig({  
    plugins   
});  

启动时会有日志
08:48:43.602 处理异步组件引用componentPlaceHolder {
08:48:43.609 'componentPlaceholder.js': '/project-config/componentPlaceHolder//componentPlaceholder.js'
08:48:43.610 }

收起阅读 »

七分钟掌握 uni-app自定义基座,离线打包的生成方法

App离线打包 自定义基座
// what's uapp  
const uapp = 'universal app'

uapp 是一款跨平台APP开发工具箱,所有积累都来自多年产品开发中的不断实践。开发者仅需写一套代码,就能横扫所有平台。

制作了一个七分钟视频,包含了android,ios 手机平台上,离线打包,自定义基座的制作方法,视频首秀,喜欢的小伙伴帮忙点赞支持

https://www.ixigua.com/7294197408232276495

继续阅读 »
// what's uapp  
const uapp = 'universal app'

uapp 是一款跨平台APP开发工具箱,所有积累都来自多年产品开发中的不断实践。开发者仅需写一套代码,就能横扫所有平台。

制作了一个七分钟视频,包含了android,ios 手机平台上,离线打包,自定义基座的制作方法,视频首秀,喜欢的小伙伴帮忙点赞支持

https://www.ixigua.com/7294197408232276495

收起阅读 »

uni-app 开发中,监听 input 键盘事件获取不到按下按键值怎么办?

uniapp 教程

uniapp 开发 H5 时,无法监听按钮键盘事件的原因以及解决方法。

问题描述:
不少 uni-app 开发者在使用 input 组件时,监听 keyup 事件时,获取不到键盘的 keyCode。编写的代码如下:

<template>  
  <input @keyup="handleKeyUp">  
</template>

但是在 handleKeyUp() 方法里获取不到键盘的编码,出现这个问题的原因是 uni-app 的内置组件 <input> 其实是封装过的,编译为 h5 时不是 html 原生的 input 元素,所以才无法监听原生的键盘事件。

解决方法参考这个:uni-app input 键盘事件获取按键值

继续阅读 »

uniapp 开发 H5 时,无法监听按钮键盘事件的原因以及解决方法。

问题描述:
不少 uni-app 开发者在使用 input 组件时,监听 keyup 事件时,获取不到键盘的 keyCode。编写的代码如下:

<template>  
  <input @keyup="handleKeyUp">  
</template>

但是在 handleKeyUp() 方法里获取不到键盘的编码,出现这个问题的原因是 uni-app 的内置组件 <input> 其实是封装过的,编译为 h5 时不是 html 原生的 input 元素,所以才无法监听原生的键盘事件。

解决方法参考这个:uni-app input 键盘事件获取按键值

收起阅读 »

uni-app通过SSE支持流式效果

uni-app支持SSE

因为uni.request没有办法支持SSE,为此尝试了各种方案,着了大急。
现将尝试的各种方案进行一个整理,若有疏漏请大家帮忙补充。

H5

因为Web端运行在浏览器内核上,SSE的支持是比较完备的,可以使用axios、@microsoft/fetch-event-source 等实现,各种案例也比较完善因此不再赘述。

微信小程序

微信小程序的SSE方案参考的是《微信小程序除了WebSocket其他思路实现流传输文字(打字机)效果》
因为我们是在uniapp中实现,所以在原文方案的基础上使用的uni相关的API来实现,考虑到要实现停止和兼容H5的的接口,最后引入了abort-controller@uni-helper/uni-network来进行封装。

import type { UnCancelTokenListener, UnGenericAbortSignal, UnHeaders } from '@uni-helper/uni-network'  

/**  
 * 二进制解析成文本  
 * @param data 二进制数据  
 * @returns 文本  
 */  
export function decodeArrayBuffer(data: ArrayBuffer | undefined) {  
  if (!data) {  
    return ''  
  }  
  return decodeUsingURIComponent(data)  
}  

/**  
 * URIComponent解码二进制流(不用引入额外包)  
 * @param data 二进制流  
 * @returns 文本  
 */  
function decodeUsingURIComponent(data: ArrayBuffer) {  
  const uint8Array = new Uint8Array(data)  
  let text = String.fromCharCode(...uint8Array)  
  try {  
    text = decodeURIComponent(escape(text))  
  } catch (e) {  
    console.error('decodeUsingURIComponent: Can not decodeURI ', text)  
  }  
  return text  
}  

type onStreamReceivedListener = (text: string) => void  

export function fetchStreamChat(  
  params: { prompt: string; uuid: string },  
  signal?: UnGenericAbortSignal,  
  listener?: onStreamReceivedListener  
) {  
  const onHeadersReceived = (response?: { headers?: UnHeaders }) => {  
    console.log('fetchStreamChat.onHeadersReceived: ', response?.headers)  
  }  
  const onChunkReceived = (response?: { data?: ArrayBuffer }) => {  
    const text = decodeArrayBuffer(response?.data)  
    listener?.(text)  
  }  
  return post<string>({  
    url: '/openai/completions/stream',  
    headers: {  
      Accept: 'text/event-stream',  
      'Content-Type': 'application/json',  
      token: 'your-token'  
    },  
    data: {  
      content: params.prompt,  
      scene: params.uuid,  
      source: 'gpt4',  
    },  
    responseType: 'arraybuffer',  
    enableChunked: true,  
    onHeadersReceived,  
    onChunkReceived,  
    signal: signal  
  })  
}

特殊注意以上代码使用时对abort-controller的引入方式

import AbortController from 'abort-controller/dist/abort-controller'  

let controller = new AbortController()  
const onResponseListener = async (responseText: string) => {  
console.log('==response==\n', responseText)  
}  
await fetchStreamChat({ prompt, uuid }, controller.signal, onResponseListener)

后端NGINX配置

# 注意这里只配置代理发送接口,不然其他接口也会受影响  

location /openai/completions/stream {  
    # ...more config  

    proxy_set_header Transfer-Encoding "";  
    chunked_transfer_encoding on;  
    proxy_buffering off;  
}

APP

App目前看到的方案最多,但是目前为止没有找到很合适的方案。有更好的方案请各位大佬补充Thanks♪(・ω・)ノ

1. plus.net.XMLHttpRequest

参考方案 《XMLHttpRequest模块管理网络请求》,具体代码如下

import type { UnCancelTokenListener, UnGenericAbortSignal, UnHeaders } from '@uni-helper/uni-network'  

type onStreamReceivedListener = (text: string) => void  

export class CanceledError extends Error {  
  constructor(message?: string) {  
    super(message ?? 'canceled')  
  }  
}  

export function fetchStreamChatForApp(  
  params: { prompt: string; uuid: string },  
  signal?: UnGenericAbortSignal,  
  listener?: onStreamReceivedListener  
) {  
  return new Promise((resolve, reject) => {  
    // 梳理好请求数据  
    const token =  'your-token'  
    const data = JSON.stringify({  
      content: params.prompt,  
      scene: params.uuid,  
      source: 'gpt3.5',  
    })  
    // 处理资源释放  
    let onCanceled: UnCancelTokenListener  
    const done = () => {  
      signal?.removeEventListener?.('abort', onCanceled)  
    }  

    // 封装请求  
    // @ts-ignore  
    let xhr: plus.net.XMLHttpRequest | undefined  
    // @ts-ignore  
    xhr = new plus.net.XMLHttpRequest()  
    xhr.withCredentials = true  
    // 配置终止逻辑  
    if (signal) {  
      signal.addEventListener?.('abort', () => {  
        console.log('fetchStreamChatForApp signal abort')  
        xhr.abort()  
      })  
    }  
    let nLastIndex = 0  
    xhr.onreadystatechange = function () {  
      console.log(`onreadystatechange(${xhr.readyState}) → `)  
      if (xhr.readyState === 4) {  
        if (nLastIndex < xhr.responseText.length) {  
          const responseText = xhr.responseText as string  
          // 处理 HTTP 数据块  
          if (responseText) {  
            const textLen = responseText.length  
            const chunk = responseText.substring(nLastIndex)  
            nLastIndex = textLen  
            listener?.(chunk)  
          }  
        }  
        if (xhr.status === 200) {  
          resolve({ code: ResultCode.SUCCESS, msg: 'end' })  
          done()  
        } else {  
          reject(new Error(xhr.statusText))  
          done()  
        }  
      }  
    }  
    xhr.onprogress = function (event: any) {  
      const responseText = xhr.responseText  
      if (responseText) {  
        const textLen = responseText.length  
        const chunk = responseText.substring(nLastIndex)  
        nLastIndex = textLen  
        listener?.(chunk)  
        console.log('onprogress ', chunk)  
      }  
    }  

    xhr.onerror = function (error: any) {  
      console.error('Network Error:', error)  
      reject(error)  
      done()  
    }  
    // 配置请求  
    xhr.open('POST', 'https://your-site/api/openai/completions/stream')  
    xhr.setRequestHeader('Accept', 'text/event-stream')  
    xhr.setRequestHeader('token', token)  
    xhr.setRequestHeader('User-Agent', 'Mobile')  
    xhr.setRequestHeader('Content-Type', 'application/json')  
    xhr.setRequestHeader('Host', 'mapi.lawvector.cn')  
    xhr.setRequestHeader('Connection', 'keep-alive')  

    // 处理终止逻辑  
    if (signal) {  
      onCanceled = cancel => {  
        console.log('fetchStreamChatForApp onCanceled ', cancel)  
        if (!xhr) {  
          return  
        }  
        reject(new CanceledError('canceled'))  
        xhr.abort()  
        xhr = undefined  
      }  
      // @ts-expect-error no types  
      signal?.aborted ? onCanceled() : signal?.addEventListener('abort', onCanceled)  
    }  
    xhr.send(data)  
  })  
}

当前方案经验证,可以从流式接口获取到数据,但是流式效果不太好,而且从网上汇总来的信息来看,plus.net存在较多问题,比如《plus.net.XMLHttpRequest()在苹果端移动网络环境下不能使用》等。因此 不推荐 plus.net方案

2. event-source-polyfill

参考方案《OpenAI流式请求实现方案》《react + ts + event-source-polyfill 实现方案》《Vue中使用eventSource处理ChatGPT聊天SSE长连接获取数据》

实际App上运行发现报错 TypeError: XMLHttpRequest is not a constructor

哪位大佬可以解决上述问题请补充,不胜感激!

3. fetch-event-source

参考方案《js调用SSE客户端》《fetch-event-source源码解析》ChatGPT-SSE流式响应
经过验证发现单独引入@microsoft/fetch-event-source 会抛出异常
ChatGPT-SSE流式响应分析应该是需要结合renderjs进行使用。
目前推荐使用该方案~

4. App原生语言插件

参考《EventSource (sse)等自定义网络请求 》
因为该插件目前仅支持Android,不推荐。
理论上,原生插件是一定能够解决这个问题,期待大佬们开发更完善的原生插件。

继续阅读 »

uni-app支持SSE

因为uni.request没有办法支持SSE,为此尝试了各种方案,着了大急。
现将尝试的各种方案进行一个整理,若有疏漏请大家帮忙补充。

H5

因为Web端运行在浏览器内核上,SSE的支持是比较完备的,可以使用axios、@microsoft/fetch-event-source 等实现,各种案例也比较完善因此不再赘述。

微信小程序

微信小程序的SSE方案参考的是《微信小程序除了WebSocket其他思路实现流传输文字(打字机)效果》
因为我们是在uniapp中实现,所以在原文方案的基础上使用的uni相关的API来实现,考虑到要实现停止和兼容H5的的接口,最后引入了abort-controller@uni-helper/uni-network来进行封装。

import type { UnCancelTokenListener, UnGenericAbortSignal, UnHeaders } from '@uni-helper/uni-network'  

/**  
 * 二进制解析成文本  
 * @param data 二进制数据  
 * @returns 文本  
 */  
export function decodeArrayBuffer(data: ArrayBuffer | undefined) {  
  if (!data) {  
    return ''  
  }  
  return decodeUsingURIComponent(data)  
}  

/**  
 * URIComponent解码二进制流(不用引入额外包)  
 * @param data 二进制流  
 * @returns 文本  
 */  
function decodeUsingURIComponent(data: ArrayBuffer) {  
  const uint8Array = new Uint8Array(data)  
  let text = String.fromCharCode(...uint8Array)  
  try {  
    text = decodeURIComponent(escape(text))  
  } catch (e) {  
    console.error('decodeUsingURIComponent: Can not decodeURI ', text)  
  }  
  return text  
}  

type onStreamReceivedListener = (text: string) => void  

export function fetchStreamChat(  
  params: { prompt: string; uuid: string },  
  signal?: UnGenericAbortSignal,  
  listener?: onStreamReceivedListener  
) {  
  const onHeadersReceived = (response?: { headers?: UnHeaders }) => {  
    console.log('fetchStreamChat.onHeadersReceived: ', response?.headers)  
  }  
  const onChunkReceived = (response?: { data?: ArrayBuffer }) => {  
    const text = decodeArrayBuffer(response?.data)  
    listener?.(text)  
  }  
  return post<string>({  
    url: '/openai/completions/stream',  
    headers: {  
      Accept: 'text/event-stream',  
      'Content-Type': 'application/json',  
      token: 'your-token'  
    },  
    data: {  
      content: params.prompt,  
      scene: params.uuid,  
      source: 'gpt4',  
    },  
    responseType: 'arraybuffer',  
    enableChunked: true,  
    onHeadersReceived,  
    onChunkReceived,  
    signal: signal  
  })  
}

特殊注意以上代码使用时对abort-controller的引入方式

import AbortController from 'abort-controller/dist/abort-controller'  

let controller = new AbortController()  
const onResponseListener = async (responseText: string) => {  
console.log('==response==\n', responseText)  
}  
await fetchStreamChat({ prompt, uuid }, controller.signal, onResponseListener)

后端NGINX配置

# 注意这里只配置代理发送接口,不然其他接口也会受影响  

location /openai/completions/stream {  
    # ...more config  

    proxy_set_header Transfer-Encoding "";  
    chunked_transfer_encoding on;  
    proxy_buffering off;  
}

APP

App目前看到的方案最多,但是目前为止没有找到很合适的方案。有更好的方案请各位大佬补充Thanks♪(・ω・)ノ

1. plus.net.XMLHttpRequest

参考方案 《XMLHttpRequest模块管理网络请求》,具体代码如下

import type { UnCancelTokenListener, UnGenericAbortSignal, UnHeaders } from '@uni-helper/uni-network'  

type onStreamReceivedListener = (text: string) => void  

export class CanceledError extends Error {  
  constructor(message?: string) {  
    super(message ?? 'canceled')  
  }  
}  

export function fetchStreamChatForApp(  
  params: { prompt: string; uuid: string },  
  signal?: UnGenericAbortSignal,  
  listener?: onStreamReceivedListener  
) {  
  return new Promise((resolve, reject) => {  
    // 梳理好请求数据  
    const token =  'your-token'  
    const data = JSON.stringify({  
      content: params.prompt,  
      scene: params.uuid,  
      source: 'gpt3.5',  
    })  
    // 处理资源释放  
    let onCanceled: UnCancelTokenListener  
    const done = () => {  
      signal?.removeEventListener?.('abort', onCanceled)  
    }  

    // 封装请求  
    // @ts-ignore  
    let xhr: plus.net.XMLHttpRequest | undefined  
    // @ts-ignore  
    xhr = new plus.net.XMLHttpRequest()  
    xhr.withCredentials = true  
    // 配置终止逻辑  
    if (signal) {  
      signal.addEventListener?.('abort', () => {  
        console.log('fetchStreamChatForApp signal abort')  
        xhr.abort()  
      })  
    }  
    let nLastIndex = 0  
    xhr.onreadystatechange = function () {  
      console.log(`onreadystatechange(${xhr.readyState}) → `)  
      if (xhr.readyState === 4) {  
        if (nLastIndex < xhr.responseText.length) {  
          const responseText = xhr.responseText as string  
          // 处理 HTTP 数据块  
          if (responseText) {  
            const textLen = responseText.length  
            const chunk = responseText.substring(nLastIndex)  
            nLastIndex = textLen  
            listener?.(chunk)  
          }  
        }  
        if (xhr.status === 200) {  
          resolve({ code: ResultCode.SUCCESS, msg: 'end' })  
          done()  
        } else {  
          reject(new Error(xhr.statusText))  
          done()  
        }  
      }  
    }  
    xhr.onprogress = function (event: any) {  
      const responseText = xhr.responseText  
      if (responseText) {  
        const textLen = responseText.length  
        const chunk = responseText.substring(nLastIndex)  
        nLastIndex = textLen  
        listener?.(chunk)  
        console.log('onprogress ', chunk)  
      }  
    }  

    xhr.onerror = function (error: any) {  
      console.error('Network Error:', error)  
      reject(error)  
      done()  
    }  
    // 配置请求  
    xhr.open('POST', 'https://your-site/api/openai/completions/stream')  
    xhr.setRequestHeader('Accept', 'text/event-stream')  
    xhr.setRequestHeader('token', token)  
    xhr.setRequestHeader('User-Agent', 'Mobile')  
    xhr.setRequestHeader('Content-Type', 'application/json')  
    xhr.setRequestHeader('Host', 'mapi.lawvector.cn')  
    xhr.setRequestHeader('Connection', 'keep-alive')  

    // 处理终止逻辑  
    if (signal) {  
      onCanceled = cancel => {  
        console.log('fetchStreamChatForApp onCanceled ', cancel)  
        if (!xhr) {  
          return  
        }  
        reject(new CanceledError('canceled'))  
        xhr.abort()  
        xhr = undefined  
      }  
      // @ts-expect-error no types  
      signal?.aborted ? onCanceled() : signal?.addEventListener('abort', onCanceled)  
    }  
    xhr.send(data)  
  })  
}

当前方案经验证,可以从流式接口获取到数据,但是流式效果不太好,而且从网上汇总来的信息来看,plus.net存在较多问题,比如《plus.net.XMLHttpRequest()在苹果端移动网络环境下不能使用》等。因此 不推荐 plus.net方案

2. event-source-polyfill

参考方案《OpenAI流式请求实现方案》《react + ts + event-source-polyfill 实现方案》《Vue中使用eventSource处理ChatGPT聊天SSE长连接获取数据》

实际App上运行发现报错 TypeError: XMLHttpRequest is not a constructor

哪位大佬可以解决上述问题请补充,不胜感激!

3. fetch-event-source

参考方案《js调用SSE客户端》《fetch-event-source源码解析》ChatGPT-SSE流式响应
经过验证发现单独引入@microsoft/fetch-event-source 会抛出异常
ChatGPT-SSE流式响应分析应该是需要结合renderjs进行使用。
目前推荐使用该方案~

4. App原生语言插件

参考《EventSource (sse)等自定义网络请求 》
因为该插件目前仅支持Android,不推荐。
理论上,原生插件是一定能够解决这个问题,期待大佬们开发更完善的原生插件。

收起阅读 »

还在手动上传小程序? uni-mini-ci帮你一键发布多平台uni-app小程序

小程序 集成

背景

在没有CI工具帮助的时候,我们使用uni-app开发小程序通常会在小程序发版时先进行 build 构建,然后在小程序开发者工具中打开构建产物,然后进行上传代码的操作。这种人力运维的稳定性是不可控的,而且当我们一次要发布多个平台或者多个小程序时,这种人力运维的工作方式将浪费我们大量的时间。

为了解决这个问题,微信推出了miniprogram-ci,开发者可不打开小程序开发者工具,独立使用 miniprogram-ci 进行小程序代码的上传、预览等操作,随后支付宝、钉钉等其他小程序厂商也跟着推出了各自的CI工具,这一举措将开发者从繁琐的上传流程中解放了出来。

uni-mini-ci是一款支持微信、钉钉、支付宝、企业微信等小程序平台的持续集成工具。它集成了多个平台的ci工具一次配置发布到多端,让开发者可以轻松地将应用程序发布到多个小程序平台上。

为什么不直接使用各自平台的CI工具?

  1. 多个小程序平台CI工具的配置文件、配置项等有所差异,其能力也有所不同,
  2. uni-app存在构建的步骤,使用各自小程序平台的CI工具则需要在构建产物中各自创建配置文件。
  3. 钉钉小程序的DingTalk Design CLI,不支持指定版本号,而uni-mini-ci支持(至于为什么支持,可以去看一下uni-mini-ci的文档)。

所以使用uni-mini-ci可以让多平台的小程序持续集成统一化。

在哪些场景使用?

  1. Github Actions 小程序持续集成
  2. Jenkins 小程序持续集成
  3. GitLab CI/CD 小程序持续集成
  4. 本地上传多个小程序平台

借助Jenkins等工具可以将开发者从构建、上传小程序的工作中,彻底解放出来,只需动动手指,即可实现小程序的上传。

示例与实践

Jenkins示例

这里是一个简单的Jenkins示例,其中关于.minicirc的配置可见uni-mini-ci,示例中的变量都可以在jenkins中定义。

这里jenkins环境要全局安装uni-mini-ci


## 安装依赖  
npm install  

检查配置文件是否存在

if [ -f .minicirc ]; then

如果文件存在,删除它

rm .minicirc  

fi

将配置写入配置文件

echo '{
"dd": {
"appid": "'$miniAppId'",
"token": "'$token'",
"projectPath": "dist/build/mp-alipay",
"autoincrement": '$autoincrement'
},
"weixin": {
"appid": "'$appid'",
"privateKeyPath": "build/ci_keys/private.'$appid'.key",
"projectPath": "dist/build/mp-weixin",
"setting": {
"minifyJS": true,
"minifyWXML": true,
"minifyWXSS": true,
"minify": true
},
"version": "'$version'",
"desc": "'$desc'"
}' > .minicirc

构建钉钉并上传

npm run build:mp-dingtalk
minici --platform dd

构建微信并上传

npm run build:mp-weixin
minici --platform weixin


### Github Actions 示例  
Github Actions与jenkins的实现基本一致,不过Github Actions是使用`.yml`作为配置文件的。  

```yaml  
name: Upload To Weixin Alipay  

on:  
  push:  
    tags:  
      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10  

jobs:  
  upload:  
    runs-on: ubuntu-latest  
    steps:  
    - uses: actions/checkout@v3  

    - name: Install Dependencies  
      run: |  
        npm i uni-mini-ci -g  
        yarn install  

    - shell: bash  
      env:  
        PRIVATE_KEY: ${{ secrets.MP_PRIVATE_KEY }}  
        APPID: ${{ secrets.MP_APPID }}  
        TOOLID: ${{ secrets.ALI_TOOL_ID }}  
        ALI_APPID: ${{ secrets.ALI_APPID }}  
        ALI_PRIVATE_KEY: ${{ secrets.ALI_PRIVATE_KEY }}  
      run: |  
          echo "$PRIVATE_KEY" > private.key  
          echo '{  
            "alipay": {  
              "appid": "'$ALI_APPID'",  
              "toolId":"'$TOOLID'",  
              "privateKey": "'$ALI_PRIVATE_KEY'",  
              "projectPath": "dist/build/mp-alipay",  
              "autoincrement":true  
            },  
            "weixin": {  
              "appid": "'$APPID'",  
              "privateKeyPath": "private.key",  
              "projectPath": "dist/build/mp-weixin",  
              "setting": {  
                "minifyJS": true,  
                "minifyWXML": true,  
                "minifyWXSS": true,  
                "minify": true  
              }  
            },  
            "version": "",  
            "desc": ""  
          }' > .minicirc  
        fi  

    - name: Use Node.js ${{ matrix.node-version }}  
      uses: actions/setup-node@v3  
      with:  
        node-version: "14.x"  

    - name: upload  
      run: |  
        npm run build:mp-alipay  
        minici --platform alipay  

        # 构建微信并上传  
        npm run build:mp-weixin  
        minici --platform weixin  

总结

通过使用uni-mini-ci,我们简化了使用uni-app开发的小程序的发布流程。借助Github Actions、Jenkins、GitLab CI/CD等工具的能力,我们可以实现小程序的自动化构建和部署,提高开发效率,使开发者能够更专注于业务逻辑的开发。

相关文章

支持多平台小程序的uni-app持续集成工具 - 掘金 (juejin.cn)

继续阅读 »

背景

在没有CI工具帮助的时候,我们使用uni-app开发小程序通常会在小程序发版时先进行 build 构建,然后在小程序开发者工具中打开构建产物,然后进行上传代码的操作。这种人力运维的稳定性是不可控的,而且当我们一次要发布多个平台或者多个小程序时,这种人力运维的工作方式将浪费我们大量的时间。

为了解决这个问题,微信推出了miniprogram-ci,开发者可不打开小程序开发者工具,独立使用 miniprogram-ci 进行小程序代码的上传、预览等操作,随后支付宝、钉钉等其他小程序厂商也跟着推出了各自的CI工具,这一举措将开发者从繁琐的上传流程中解放了出来。

uni-mini-ci是一款支持微信、钉钉、支付宝、企业微信等小程序平台的持续集成工具。它集成了多个平台的ci工具一次配置发布到多端,让开发者可以轻松地将应用程序发布到多个小程序平台上。

为什么不直接使用各自平台的CI工具?

  1. 多个小程序平台CI工具的配置文件、配置项等有所差异,其能力也有所不同,
  2. uni-app存在构建的步骤,使用各自小程序平台的CI工具则需要在构建产物中各自创建配置文件。
  3. 钉钉小程序的DingTalk Design CLI,不支持指定版本号,而uni-mini-ci支持(至于为什么支持,可以去看一下uni-mini-ci的文档)。

所以使用uni-mini-ci可以让多平台的小程序持续集成统一化。

在哪些场景使用?

  1. Github Actions 小程序持续集成
  2. Jenkins 小程序持续集成
  3. GitLab CI/CD 小程序持续集成
  4. 本地上传多个小程序平台

借助Jenkins等工具可以将开发者从构建、上传小程序的工作中,彻底解放出来,只需动动手指,即可实现小程序的上传。

示例与实践

Jenkins示例

这里是一个简单的Jenkins示例,其中关于.minicirc的配置可见uni-mini-ci,示例中的变量都可以在jenkins中定义。

这里jenkins环境要全局安装uni-mini-ci


## 安装依赖  
npm install  

检查配置文件是否存在

if [ -f .minicirc ]; then

如果文件存在,删除它

rm .minicirc  

fi

将配置写入配置文件

echo '{
"dd": {
"appid": "'$miniAppId'",
"token": "'$token'",
"projectPath": "dist/build/mp-alipay",
"autoincrement": '$autoincrement'
},
"weixin": {
"appid": "'$appid'",
"privateKeyPath": "build/ci_keys/private.'$appid'.key",
"projectPath": "dist/build/mp-weixin",
"setting": {
"minifyJS": true,
"minifyWXML": true,
"minifyWXSS": true,
"minify": true
},
"version": "'$version'",
"desc": "'$desc'"
}' > .minicirc

构建钉钉并上传

npm run build:mp-dingtalk
minici --platform dd

构建微信并上传

npm run build:mp-weixin
minici --platform weixin


### Github Actions 示例  
Github Actions与jenkins的实现基本一致,不过Github Actions是使用`.yml`作为配置文件的。  

```yaml  
name: Upload To Weixin Alipay  

on:  
  push:  
    tags:  
      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10  

jobs:  
  upload:  
    runs-on: ubuntu-latest  
    steps:  
    - uses: actions/checkout@v3  

    - name: Install Dependencies  
      run: |  
        npm i uni-mini-ci -g  
        yarn install  

    - shell: bash  
      env:  
        PRIVATE_KEY: ${{ secrets.MP_PRIVATE_KEY }}  
        APPID: ${{ secrets.MP_APPID }}  
        TOOLID: ${{ secrets.ALI_TOOL_ID }}  
        ALI_APPID: ${{ secrets.ALI_APPID }}  
        ALI_PRIVATE_KEY: ${{ secrets.ALI_PRIVATE_KEY }}  
      run: |  
          echo "$PRIVATE_KEY" > private.key  
          echo '{  
            "alipay": {  
              "appid": "'$ALI_APPID'",  
              "toolId":"'$TOOLID'",  
              "privateKey": "'$ALI_PRIVATE_KEY'",  
              "projectPath": "dist/build/mp-alipay",  
              "autoincrement":true  
            },  
            "weixin": {  
              "appid": "'$APPID'",  
              "privateKeyPath": "private.key",  
              "projectPath": "dist/build/mp-weixin",  
              "setting": {  
                "minifyJS": true,  
                "minifyWXML": true,  
                "minifyWXSS": true,  
                "minify": true  
              }  
            },  
            "version": "",  
            "desc": ""  
          }' > .minicirc  
        fi  

    - name: Use Node.js ${{ matrix.node-version }}  
      uses: actions/setup-node@v3  
      with:  
        node-version: "14.x"  

    - name: upload  
      run: |  
        npm run build:mp-alipay  
        minici --platform alipay  

        # 构建微信并上传  
        npm run build:mp-weixin  
        minici --platform weixin  

总结

通过使用uni-mini-ci,我们简化了使用uni-app开发的小程序的发布流程。借助Github Actions、Jenkins、GitLab CI/CD等工具的能力,我们可以实现小程序的自动化构建和部署,提高开发效率,使开发者能够更专注于业务逻辑的开发。

相关文章

支持多平台小程序的uni-app持续集成工具 - 掘金 (juejin.cn)

收起阅读 »

uni.openLocation和VUE3有兼容问题

vue3

VUE3 使用 uni.openLocation 浏览器不会打开新页面。会在原页面调用地图组件遮盖,这样使用浏览器自带返回按钮导致路由变化,页面始终被地图组件遮盖。(地图组件无法被浏览器返回事件关闭)

VUE3 使用 uni.openLocation 浏览器不会打开新页面。会在原页面调用地图组件遮盖,这样使用浏览器自带返回按钮导致路由变化,页面始终被地图组件遮盖。(地图组件无法被浏览器返回事件关闭)

【解决】您的应用在运行时,未见向用户告知权限申请的目的,向用户索取(相机)等权限,不符合华为应用市场审核标准。

应用上架

简约解决方式:

安卓app:不唤起权限,检测当前权限是否允许了

uni.showModal({  
    title: "温馨提示",  
    content: "将申请相机权限,用于扫码功能",  
    success: (res) => {  
        if (res.confirm) {  
            // 唤起相册、相机、扫码、地理位置之类的api,如:uni.scanCode  
        }  
    }  
})

更好的解决办法(实现如下图功能):可能有bug,仅供参考安卓权限说明蒙层

↓↓↓ 各位大佬点点赞

继续阅读 »

简约解决方式:

安卓app:不唤起权限,检测当前权限是否允许了

uni.showModal({  
    title: "温馨提示",  
    content: "将申请相机权限,用于扫码功能",  
    success: (res) => {  
        if (res.confirm) {  
            // 唤起相册、相机、扫码、地理位置之类的api,如:uni.scanCode  
        }  
    }  
})

更好的解决办法(实现如下图功能):可能有bug,仅供参考安卓权限说明蒙层

↓↓↓ 各位大佬点点赞

收起阅读 »

2023年DCloud插件大赛获奖名单

公告

获奖名单

本次插件大赛收到大量优质插件,尤其是uni-app x的相关生态大幅完善,常用插件基本都有了

获奖名单公布如下:

奖项 分类 获奖作者 获奖插件
特等奖 uni-app x+uniCloud lieft 快亿商城,uni-app x(uvue+uts)和uniCloud云端一体完整原生商城项目源码
奖项 分类 获奖作者 获奖插件
一等奖 uni-app x 同名自定义 TMUI4.0 tmx-ui UVUE组件及UTS插件库
一等奖 uni-app x UxFrame UxFrame 低代码高性能UI框架
一等奖 uniCloud 禾店科技 禾店短剧
奖项 分类 获奖作者 获奖插件
二等奖 uni-app x FirstUI FirstUI-unix
二等奖 UTS插件 kux kux-mlkit-scancode谷歌扫码
二等奖 uni-app x 康爱公社 kux-request 简洁高效的uts请求库
二等奖 UTS插件 网易云信 【官方】网易云信RTC音视频通话点对点呼叫组件(uni-app x)
二等奖 UTS插件 865***@qq.com 高德地图点标记导航离线地图-原生
二等奖 UTS插件 码农朱哲 svg组件
二等奖 uni-app x 陌上华年 qrcode 二维码生成
二等奖 uniCloud 小猿敲代码 汇玩App(完整项目可直接上线,含试玩游戏,试玩应用,uniad结合变现的APP网赚平台)
奖项 分类 获奖作者 获奖插件
三等奖 HBuilderX 智谱AI CodeGeeX: AI Code AutoComplete, Chat, Auto Comment
三等奖 HBuilderX 猫猫猫猫 GitHub Copilot
三等奖 uni-app x abc***@163.com t-uvue-ui 首个基于UNI-APP X开发的前端UI框架
三等奖 uni-app x GraceUI uXui 一款基于 uni-app x 的、免费、开源的 UI 框架
三等奖 uni-app x 229***@qq.com 考试答题模板(appx)
三等奖 uni-app x 陌上年华 手写板-签名签字-lime-signature
三等奖 uni-app x 陌上年华 watermark 防盗水印
三等奖 UTS插件 JKX 高德地图组件
三等奖 UTS插件 UxFrame UxFrame 微信SDK 微信支付 微信登录 微信分享 微信企业客服
三等奖 UTS插件 UxFrame UxFrame 实时音视频通话直播SDK 支持语音通话 视频通话 语音直播 视频直播 录屏直播
三等奖 UTS插件 小飞007 腾讯直播推流
三等奖 UTS插件 小飞007 UTS实现操作SQLite加密数据库
三等奖 UTS插件 珊瑚 Android端的AES、MD5、RSA、SHA、SM2、SM3、SM4加密解密
三等奖 UTS插件 木杉丶 原生安卓Android ble低功耗蓝牙插件
三等奖 UTS插件 康爱公社 kux-dayjs
三等奖 UTS插件 码农朱哲 android无须权限,选择文件
三等奖 UTS插件 865***@qq.com 腾讯x5webview支持离线内核
三等奖 UTS插件 狼人残风001 悬浮窗小窗口画中画自定义画中画界面
三等奖 uniCloud monkeyFree 【用户端】【多行业适用】多端配置自定义官网
三等奖 uniCloud 一幅画 星之链社区-unicloud版

因很多插件值得推荐,所以增加了贡献奖的名额。

奖项 分类 获奖作者 获奖插件
贡献奖 HBuilderX 113***@qq.com aTool-便捷翻译、大小写、驼峰、下划线转换,复制页面路径、文件名、翻译并替换
贡献奖 HBuilderX 113***@qq.com a-snippet【代码块】
贡献奖 HBuilderX zqy233 formatAndSave
贡献奖 HBuilderX zqy233 HBuilderX Auto Rename Tag
贡献奖 HBuilderX Doyoung 统计代码行
贡献奖 HBuilderX 无语西风 蛋壳Uploader - 没有mac电脑也能上传ipa到AppStore
贡献奖 uni-app x + uniCloud lieft ai聊天(chatGPT),uni-app x和uniCloud云端一体完整项目源码
贡献奖 uni-app x Mr丶小斌 喵购商城-使用uni-app-x开发的电商前端模版
贡献奖 uni-app x liu***@mzsat.cn easyXUI 不仅仅是UI 更是为UniApp X设计的电商模板库
贡献奖 uni-app x 照相 【z-paging-x下拉刷新、上拉加载】z-paging uniappx版已上线!
贡献奖 uni-app x WX_UI wx-ui 基于uni-app x开发的高性能混合UI库
贡献奖 uni-app x 邓琪昌 flower-icons(iconPark) 图标库,全平台全版本兼容,最新支持 uni-app-x
贡献奖 uni-app x 回梦無痕 HM-dragSortsUvue拖动排序
贡献奖 uni-app x 陌上年华 echarts
贡献奖 UTS插件 陌上年华 lime-camera 相机
贡献奖 UTS插件 陌上年华 lime-recorder 录音管理器
贡献奖 UTS插件 木杉丶 安卓原生自定义前后摄像头拍照,可同时设置前后摄像头预览,隐藏摄像头,照片返回为base64
贡献奖 UTS插件 码农朱哲 uts ffmpeg插件
贡献奖 UTS插件 码农朱哲 google授权登录插件
贡献奖 UTS插件 码农朱哲 高斯模糊view
贡献奖 UTS插件 码农朱哲 uts加载jetpack compose
贡献奖 UTS插件 码农朱哲 动态设置应用icon图标
贡献奖 UTS插件 码农朱哲 ios截屏防护-安全组件
贡献奖 uni-app x 康爱公社 kux-router 参考 vue-router 的api设计实现的 uts 路由库
贡献奖 uni-app x 康爱公社 kux-color uts专用的颜色转换和主题色生成工具
贡献奖 UTS插件 kux kux-broadcast-receiver广播接收
贡献奖 UTS插件 kux kux-bluetooth 完全按照微信小程序蓝牙API设计标准实现的一个蓝牙操作插件
贡献奖 UTS插件 kux kux-pip画中画
贡献奖 UTS插件 小飞007 UTS实现FFmpeg命令处理音视频
贡献奖 UTS插件 小飞007 腾讯异常监控App崩溃日志工具
贡献奖 UTS插件 小飞007 GooglePay内购V6支付
贡献奖 UTS插件 狼人残风001 双屏异显多屏异显示自定义异屏内容
贡献奖 UTS插件 狼人残风001 NFC读写支持文本和url包名NDED和非NDED
贡献奖 UTS插件 865***@qq.com 读取本地相册图片视频音频,可自定义UI界面
贡献奖 UTS插件 865***@qq.com 日历事件管理(增删查改)提醒事件管理(增删查改)
贡献奖 UTS插件 雨中漫步007 陀螺仪加速度传感器摇一摇
贡献奖 UTS插件 雨中漫步007 文字转语音实时播放可设置语调语速语言
贡献奖 UTS插件 珊瑚 Android简单的哀悼日置灰插件
贡献奖 UTS插件 珊瑚 Android 扩展通知栏
贡献奖 UTS插件 小白2023 深信服零信任VPN uts插件
贡献奖 UTS插件 小白2023 nfc-uts
贡献奖 UTS插件 107***@qq.com jk-uts-udp UDP通信插件
贡献奖 UTS插件 GraceUI uxEditor 一款基于 uni-app x 的内容编辑及内容渲染插件
贡献奖 UTS插件 wan***@hzsmtech.com 一键获取设备唯一标识,OAID、AAID、IMEI、AndroidID
贡献奖 UTS插件 wan***@hzsmtech.com 原生tts,语音合成,文本转语音
贡献奖 UTS插件 鱼住未来 UTS NFC USB读卡器 身份证读取
贡献奖 UTS插件 JKX 通用加密:sha1AES
贡献奖 UTS插件 241***@qq.com sm-crypto 国密加密
贡献奖 UTS插件 862***@qq.com flutterCanvas
贡献奖 UTS插件 同名自定义 Ocr文本识别ai模型离线本机识别无需服务端
贡献奖 UTS插件 叶柳垂杨 SVGA 动画组件 支持自定义文本、图片,图层隐藏功能
贡献奖 UTS插件 you***@qq.com Sqlite数据库执行插件,提供数据库语句执行操作
贡献奖 UTS插件 100***@qq.com mqtt以及rabbitMQ的UTS插件
贡献奖 UTS插件 silianpan888 跨平台Office文档预览UTS插件
贡献奖 UTS插件 379***@qq.com hr-getLocation-amap 高德定位
贡献奖 uniCloud VK1688 unicloud数据库一键搬家一键迁移工具,支持阿里云、腾讯云、支付宝云互转。支持跨账号
贡献奖 uniCloud VK1688 vkmall(vk商城client端)云端一体项目,拥有完善的框架开发文档,易二开。
贡献奖 uniCloud zerojs zero-ai (打造一个属于自己的chatgpt)
贡献奖 uniCloud 王三三 【免费】uBlog支持网页端小程序APP桌面端的博客CMS系统(前端)
贡献奖 uniCloud 110***@qq.com 老腊肉记账
贡献奖 uniCloud 微光月牙 邮箱登录验证码发送、验证、验证码有效期、倒计时发送,接入qq邮箱

奖项设置

特等奖:
奖品:2万元插件包销 + 8888元现金红包 + 插件市场置顶推荐半个月 + HBuilderX预置 + HBuilderX超大鼠标垫 + DCloud奖牌
名额:1名

一等奖:
奖品:2万元插件包销 + 1部Apple设备(mac mini 或 ipad 2选1) + 1000元现金红包+插件市场置顶推荐半个月 + HBuilderX预置 + HBuilderX超大鼠标垫 + DCloud奖牌
名额:3名
mac mini配置为M2芯片、16G内存、256G硬盘。
ipad配置为10.9寸,256G。
其中1部ipad由支付宝提供,定向给予支持unicloud支付宝小程序云版的unicloud插件作者。

二等奖
奖品:2000元插件包销 + 极客外设1台 (HHKB 机械键盘 或 小米 34寸曲面显示器 2选1) + 500元现金红包 + 插件市场置顶推荐1个星期 + HBuilderX超大鼠标垫 + DCloud奖牌
名额:8名

三等奖
奖品:(200元uniCloud代金券 或 护眼套餐 2选1) + HBuilderX超大鼠标垫 + DCloud奖牌
名额:20名
护眼套餐包括2件奖品整包发送:

  1. 京造养眼叶黄素
  2. 小米防蓝光眼镜

贡献奖
奖品:HBuilderX超大鼠标垫
名额:50名

注:红包需缴纳个人所得税或通过云账户发放。

“插件包销”,是指获奖插件通过插件市场销售,DCloud兜底包销。以1等奖的2万元包销为例,如果获奖插件在插件市场1年内销售额没有达到2万元,则由DCloud付差额给获奖者进行兜底。包销只针对付费插件,如免费插件获得二等奖及以上奖励,其中的包销奖励无效。

“插件包销”即不虚、也不遥远,去年插件大赛获包销奖励的插件,均提前完成包销。其价值就是实实在在的人民币。

“HBuilderX预置”,是在HBuilderX新建项目界面,可直接选择该项目模板。这为插件带来大量的流量。不适合预置的插件类型,无法领取此奖项。

除上述奖品外,

  • 三等奖及以上获奖插件作者,都将进入DCloud VIP技术支持群,享受优先的技术支付、问题反馈。
  • 所有获奖插件的集锦页面,还将通过HBuilderX工具、论坛、QQ微信群进行全量推广,给予优秀插件充分的曝光。

HBuilderX预置窗体界面如下:

奖牌照片如下:

历史信息

  • 参赛起止时间:从23年10月24日起,到24年3月31日止。

  • 鼓励的插件范围

  • uni-app x 前端插件,如ui库。现有插件可参考uvue组件

  • uts插件,扩展原生能力。现有插件可参考uts插件。除了插件市场的插件,uni-app x的源码仓库,也有大量插件源码可参考,因为uni-app x的很多组件和api就是用uts开发的,api仓库详见,组件仓库详见

  • uniCloud插件,优秀的云端一体插件。现有插件可参考云端一体项目模板

  • 有助于开发者广告变现的运营类插件

  • HBuilder插件

奖品领取

请各位获奖作者尽快提交自己的邮寄地址,我们会陆续联系获奖人员发放奖品;
邮寄地址提交方式:登录ask社区,点击右上角个人头像,进入设置界面,设置界面下方补充快递邮寄地址。

所有2选1的奖品,插件作者可以发邮件到 service@dcloud.io,说明社区账号、获奖插件及希望选择的奖品。

已获奖的插件作者请继续升级迭代插件;
未获奖的今年还有机会,官方会继续为建设更好的uni-app x生态推出其他计划。不管是为了下次大赛获奖,还是为了把握uni-app x的新浪潮,或者在插件市场通过售卖插件变现,都是值得期待的好事。

继续阅读 »

获奖名单

本次插件大赛收到大量优质插件,尤其是uni-app x的相关生态大幅完善,常用插件基本都有了

获奖名单公布如下:

奖项 分类 获奖作者 获奖插件
特等奖 uni-app x+uniCloud lieft 快亿商城,uni-app x(uvue+uts)和uniCloud云端一体完整原生商城项目源码
奖项 分类 获奖作者 获奖插件
一等奖 uni-app x 同名自定义 TMUI4.0 tmx-ui UVUE组件及UTS插件库
一等奖 uni-app x UxFrame UxFrame 低代码高性能UI框架
一等奖 uniCloud 禾店科技 禾店短剧
奖项 分类 获奖作者 获奖插件
二等奖 uni-app x FirstUI FirstUI-unix
二等奖 UTS插件 kux kux-mlkit-scancode谷歌扫码
二等奖 uni-app x 康爱公社 kux-request 简洁高效的uts请求库
二等奖 UTS插件 网易云信 【官方】网易云信RTC音视频通话点对点呼叫组件(uni-app x)
二等奖 UTS插件 865***@qq.com 高德地图点标记导航离线地图-原生
二等奖 UTS插件 码农朱哲 svg组件
二等奖 uni-app x 陌上华年 qrcode 二维码生成
二等奖 uniCloud 小猿敲代码 汇玩App(完整项目可直接上线,含试玩游戏,试玩应用,uniad结合变现的APP网赚平台)
奖项 分类 获奖作者 获奖插件
三等奖 HBuilderX 智谱AI CodeGeeX: AI Code AutoComplete, Chat, Auto Comment
三等奖 HBuilderX 猫猫猫猫 GitHub Copilot
三等奖 uni-app x abc***@163.com t-uvue-ui 首个基于UNI-APP X开发的前端UI框架
三等奖 uni-app x GraceUI uXui 一款基于 uni-app x 的、免费、开源的 UI 框架
三等奖 uni-app x 229***@qq.com 考试答题模板(appx)
三等奖 uni-app x 陌上年华 手写板-签名签字-lime-signature
三等奖 uni-app x 陌上年华 watermark 防盗水印
三等奖 UTS插件 JKX 高德地图组件
三等奖 UTS插件 UxFrame UxFrame 微信SDK 微信支付 微信登录 微信分享 微信企业客服
三等奖 UTS插件 UxFrame UxFrame 实时音视频通话直播SDK 支持语音通话 视频通话 语音直播 视频直播 录屏直播
三等奖 UTS插件 小飞007 腾讯直播推流
三等奖 UTS插件 小飞007 UTS实现操作SQLite加密数据库
三等奖 UTS插件 珊瑚 Android端的AES、MD5、RSA、SHA、SM2、SM3、SM4加密解密
三等奖 UTS插件 木杉丶 原生安卓Android ble低功耗蓝牙插件
三等奖 UTS插件 康爱公社 kux-dayjs
三等奖 UTS插件 码农朱哲 android无须权限,选择文件
三等奖 UTS插件 865***@qq.com 腾讯x5webview支持离线内核
三等奖 UTS插件 狼人残风001 悬浮窗小窗口画中画自定义画中画界面
三等奖 uniCloud monkeyFree 【用户端】【多行业适用】多端配置自定义官网
三等奖 uniCloud 一幅画 星之链社区-unicloud版

因很多插件值得推荐,所以增加了贡献奖的名额。

奖项 分类 获奖作者 获奖插件
贡献奖 HBuilderX 113***@qq.com aTool-便捷翻译、大小写、驼峰、下划线转换,复制页面路径、文件名、翻译并替换
贡献奖 HBuilderX 113***@qq.com a-snippet【代码块】
贡献奖 HBuilderX zqy233 formatAndSave
贡献奖 HBuilderX zqy233 HBuilderX Auto Rename Tag
贡献奖 HBuilderX Doyoung 统计代码行
贡献奖 HBuilderX 无语西风 蛋壳Uploader - 没有mac电脑也能上传ipa到AppStore
贡献奖 uni-app x + uniCloud lieft ai聊天(chatGPT),uni-app x和uniCloud云端一体完整项目源码
贡献奖 uni-app x Mr丶小斌 喵购商城-使用uni-app-x开发的电商前端模版
贡献奖 uni-app x liu***@mzsat.cn easyXUI 不仅仅是UI 更是为UniApp X设计的电商模板库
贡献奖 uni-app x 照相 【z-paging-x下拉刷新、上拉加载】z-paging uniappx版已上线!
贡献奖 uni-app x WX_UI wx-ui 基于uni-app x开发的高性能混合UI库
贡献奖 uni-app x 邓琪昌 flower-icons(iconPark) 图标库,全平台全版本兼容,最新支持 uni-app-x
贡献奖 uni-app x 回梦無痕 HM-dragSortsUvue拖动排序
贡献奖 uni-app x 陌上年华 echarts
贡献奖 UTS插件 陌上年华 lime-camera 相机
贡献奖 UTS插件 陌上年华 lime-recorder 录音管理器
贡献奖 UTS插件 木杉丶 安卓原生自定义前后摄像头拍照,可同时设置前后摄像头预览,隐藏摄像头,照片返回为base64
贡献奖 UTS插件 码农朱哲 uts ffmpeg插件
贡献奖 UTS插件 码农朱哲 google授权登录插件
贡献奖 UTS插件 码农朱哲 高斯模糊view
贡献奖 UTS插件 码农朱哲 uts加载jetpack compose
贡献奖 UTS插件 码农朱哲 动态设置应用icon图标
贡献奖 UTS插件 码农朱哲 ios截屏防护-安全组件
贡献奖 uni-app x 康爱公社 kux-router 参考 vue-router 的api设计实现的 uts 路由库
贡献奖 uni-app x 康爱公社 kux-color uts专用的颜色转换和主题色生成工具
贡献奖 UTS插件 kux kux-broadcast-receiver广播接收
贡献奖 UTS插件 kux kux-bluetooth 完全按照微信小程序蓝牙API设计标准实现的一个蓝牙操作插件
贡献奖 UTS插件 kux kux-pip画中画
贡献奖 UTS插件 小飞007 UTS实现FFmpeg命令处理音视频
贡献奖 UTS插件 小飞007 腾讯异常监控App崩溃日志工具
贡献奖 UTS插件 小飞007 GooglePay内购V6支付
贡献奖 UTS插件 狼人残风001 双屏异显多屏异显示自定义异屏内容
贡献奖 UTS插件 狼人残风001 NFC读写支持文本和url包名NDED和非NDED
贡献奖 UTS插件 865***@qq.com 读取本地相册图片视频音频,可自定义UI界面
贡献奖 UTS插件 865***@qq.com 日历事件管理(增删查改)提醒事件管理(增删查改)
贡献奖 UTS插件 雨中漫步007 陀螺仪加速度传感器摇一摇
贡献奖 UTS插件 雨中漫步007 文字转语音实时播放可设置语调语速语言
贡献奖 UTS插件 珊瑚 Android简单的哀悼日置灰插件
贡献奖 UTS插件 珊瑚 Android 扩展通知栏
贡献奖 UTS插件 小白2023 深信服零信任VPN uts插件
贡献奖 UTS插件 小白2023 nfc-uts
贡献奖 UTS插件 107***@qq.com jk-uts-udp UDP通信插件
贡献奖 UTS插件 GraceUI uxEditor 一款基于 uni-app x 的内容编辑及内容渲染插件
贡献奖 UTS插件 wan***@hzsmtech.com 一键获取设备唯一标识,OAID、AAID、IMEI、AndroidID
贡献奖 UTS插件 wan***@hzsmtech.com 原生tts,语音合成,文本转语音
贡献奖 UTS插件 鱼住未来 UTS NFC USB读卡器 身份证读取
贡献奖 UTS插件 JKX 通用加密:sha1AES
贡献奖 UTS插件 241***@qq.com sm-crypto 国密加密
贡献奖 UTS插件 862***@qq.com flutterCanvas
贡献奖 UTS插件 同名自定义 Ocr文本识别ai模型离线本机识别无需服务端
贡献奖 UTS插件 叶柳垂杨 SVGA 动画组件 支持自定义文本、图片,图层隐藏功能
贡献奖 UTS插件 you***@qq.com Sqlite数据库执行插件,提供数据库语句执行操作
贡献奖 UTS插件 100***@qq.com mqtt以及rabbitMQ的UTS插件
贡献奖 UTS插件 silianpan888 跨平台Office文档预览UTS插件
贡献奖 UTS插件 379***@qq.com hr-getLocation-amap 高德定位
贡献奖 uniCloud VK1688 unicloud数据库一键搬家一键迁移工具,支持阿里云、腾讯云、支付宝云互转。支持跨账号
贡献奖 uniCloud VK1688 vkmall(vk商城client端)云端一体项目,拥有完善的框架开发文档,易二开。
贡献奖 uniCloud zerojs zero-ai (打造一个属于自己的chatgpt)
贡献奖 uniCloud 王三三 【免费】uBlog支持网页端小程序APP桌面端的博客CMS系统(前端)
贡献奖 uniCloud 110***@qq.com 老腊肉记账
贡献奖 uniCloud 微光月牙 邮箱登录验证码发送、验证、验证码有效期、倒计时发送,接入qq邮箱

奖项设置

特等奖:
奖品:2万元插件包销 + 8888元现金红包 + 插件市场置顶推荐半个月 + HBuilderX预置 + HBuilderX超大鼠标垫 + DCloud奖牌
名额:1名

一等奖:
奖品:2万元插件包销 + 1部Apple设备(mac mini 或 ipad 2选1) + 1000元现金红包+插件市场置顶推荐半个月 + HBuilderX预置 + HBuilderX超大鼠标垫 + DCloud奖牌
名额:3名
mac mini配置为M2芯片、16G内存、256G硬盘。
ipad配置为10.9寸,256G。
其中1部ipad由支付宝提供,定向给予支持unicloud支付宝小程序云版的unicloud插件作者。

二等奖
奖品:2000元插件包销 + 极客外设1台 (HHKB 机械键盘 或 小米 34寸曲面显示器 2选1) + 500元现金红包 + 插件市场置顶推荐1个星期 + HBuilderX超大鼠标垫 + DCloud奖牌
名额:8名

三等奖
奖品:(200元uniCloud代金券 或 护眼套餐 2选1) + HBuilderX超大鼠标垫 + DCloud奖牌
名额:20名
护眼套餐包括2件奖品整包发送:

  1. 京造养眼叶黄素
  2. 小米防蓝光眼镜

贡献奖
奖品:HBuilderX超大鼠标垫
名额:50名

注:红包需缴纳个人所得税或通过云账户发放。

“插件包销”,是指获奖插件通过插件市场销售,DCloud兜底包销。以1等奖的2万元包销为例,如果获奖插件在插件市场1年内销售额没有达到2万元,则由DCloud付差额给获奖者进行兜底。包销只针对付费插件,如免费插件获得二等奖及以上奖励,其中的包销奖励无效。

“插件包销”即不虚、也不遥远,去年插件大赛获包销奖励的插件,均提前完成包销。其价值就是实实在在的人民币。

“HBuilderX预置”,是在HBuilderX新建项目界面,可直接选择该项目模板。这为插件带来大量的流量。不适合预置的插件类型,无法领取此奖项。

除上述奖品外,

  • 三等奖及以上获奖插件作者,都将进入DCloud VIP技术支持群,享受优先的技术支付、问题反馈。
  • 所有获奖插件的集锦页面,还将通过HBuilderX工具、论坛、QQ微信群进行全量推广,给予优秀插件充分的曝光。

HBuilderX预置窗体界面如下:

奖牌照片如下:

历史信息

  • 参赛起止时间:从23年10月24日起,到24年3月31日止。

  • 鼓励的插件范围

  • uni-app x 前端插件,如ui库。现有插件可参考uvue组件

  • uts插件,扩展原生能力。现有插件可参考uts插件。除了插件市场的插件,uni-app x的源码仓库,也有大量插件源码可参考,因为uni-app x的很多组件和api就是用uts开发的,api仓库详见,组件仓库详见

  • uniCloud插件,优秀的云端一体插件。现有插件可参考云端一体项目模板

  • 有助于开发者广告变现的运营类插件

  • HBuilder插件

奖品领取

请各位获奖作者尽快提交自己的邮寄地址,我们会陆续联系获奖人员发放奖品;
邮寄地址提交方式:登录ask社区,点击右上角个人头像,进入设置界面,设置界面下方补充快递邮寄地址。

所有2选1的奖品,插件作者可以发邮件到 service@dcloud.io,说明社区账号、获奖插件及希望选择的奖品。

已获奖的插件作者请继续升级迭代插件;
未获奖的今年还有机会,官方会继续为建设更好的uni-app x生态推出其他计划。不管是为了下次大赛获奖,还是为了把握uni-app x的新浪潮,或者在插件市场通过售卖插件变现,都是值得期待的好事。

收起阅读 »

十分钟使用vitepress+github action+gitee pages 搭建你的专属文档

帮助文档

介绍

VitePressVuePress 的精神继承者。最初的 VuePress 基于Vue 2webpack。在VitePress内部使用了Vue 3Vite,这使得VitePress在开发体验、生产性能、默认主题的精细化和更灵活的自定义API方面提供了显著的改进。本文将介绍使用VitePress搭建uni-app路由库uni-mini-router的文档,并通过github action实现自动化部署到github pagesgitee pages

创建项目

安装

关于安装配置的问题,vitepress目前还没有稳定的版本,所以可能会有所变动,推荐还是看一下文档然后进行创建。

mkdir your-project  
npm init  
yarn add -D vitepress

安装向导

VitePress 附带一个命令行设置向导,可帮助您搭建基本项目的基架。安装后,通过运行以下命令启动向导:

npx vitepress init

您会看到几个简单的问题:

┌  Welcome to VitePress!  
│  
◇  Where should VitePress initialize the config?  
│  ./docs  
│  
◇  Site title:  
│  uni-mini-router(你的项目名称)  
│  
◇  Site description:  
│  一个基于vue3+typescript的uni-app路由库(你的项目介绍)  
│  
◇  Theme:  
│  Default Theme  
│  
◇  Use TypeScript for config and theme files?  
│  Yes  
│  
◇  Add VitePress npm scripts to package.json?  
│  Yes  
│  
└  Done! Now run npm run docs:dev and start writing.

执行完本步骤后,将会向你的package.json注入以下脚本:

{  
  ...  
  "scripts": {  
    "docs:dev": "vitepress dev docs",  
    "docs:build": "vitepress build docs",  
    "docs:preview": "vitepress preview docs"  
  },  
  ...  
}

运行

npm run docs:dev

配置

docs/.vitepress文件夹中有一个 config.mts 文件,我们可以在这里配置文档项目,配置项参考配置

import { defineConfig } from 'vitepress'  

// https://vitepress.dev/reference/site-config  
export default defineConfig({  
  title: "uni-mini-router",  
  description: "一个基于vue3+typescript的uni-app路由库",  
  themeConfig: {  
    // https://vitepress.dev/reference/default-theme-config  
    nav: [  
      { text: 'Home', link: '/' },  
      { text: 'Examples', link: '/markdown-examples' }  
    ],  

    sidebar: [  
      {  
        text: 'Examples',  
        items: [  
          { text: 'Markdown Examples', link: '/markdown-examples' },  
          { text: 'Runtime API Examples', link: '/api-examples' }  
        ]  
      }  
    ],  

    socialLinks: [  
      { icon: 'github', link: 'https://github.com/vuejs/vitepress' }  
    ]  
  }  
})  

部署到 Github Pages + Gitee Pages

  1. 修改configbase
    • 如果要部署到 https://<USERNAME>.github.io/,则可以省略 base,因为它默认为 “/”。
    • 如果您正在部署到 https://<USERNAME>.github.io/<REPO>/,例如,您的存储库位于 github.com/<REPO>/,然后将 base 设置为 /<REPO>/
// 示例  
import { defineConfig } from 'vitepress'  

export default defineConfig({  
  base: "/uni-mini-router/", // 这里为仓库名  
  title: "uni-mini-router",  
})
  1. 创建 Github Action 部署Github Pages并同步至Gitee Pages

Github Pages在国内的访问速度并不理想,而Gitee则没有类似Github Action的功能且标准版Gitee Pages不支持自动部署,所以我们通过Github Action 将文档部署Github Pages并同步至Gitee Pages。

在项目根目录下创建.github文件夹,.github中创建workflows文件夹并创建文件deploy.yml


name: Deploy VitePress site to Pages  

on:  
  push:  
    tags:  
      - '*'  

  workflow_dispatch:  

jobs:  
  deploy-and-sync:  
    runs-on: ubuntu-latest  
    steps:  
      - name: Checkout   
        uses: actions/checkout@v4  
        with:  
          ref: 'master'  

      - name: Install yarn  
        run: corepack enable  

      - uses: actions/setup-node@v3  
        with:  
          node-version: '18'  
          cache: 'yarn'  

      - name: Install dependencies  
        run: yarn install  

      - name: Build Site  
        run: npm run docs:build  

      # 将文档产物提交到gh-pages分支  
      - name: Deploy for Gitee   
        uses: JamesIves/github-pages-deploy-action@v4.4.1  
        with:  
          branch: gh-pages  
          folder: docs/.vitepress/dist  
          # enable single-commit to reduce the repo size  
          single-commit: true  
          clean: true  

      - name: Sync to Gitee  
        uses: wearerequired/git-mirror-action@v1.2.0  
        env:  
          SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}  
        with:  
          # GitHub 仓库地址  
          source-repo: git@github.com:Moonofweisheng/uni-mini-router.git  
          # Gitee 仓库地址  
          destination-repo: git@gitee.com:Moonofweisheng/uni-mini-router.git  

      - name: Build Gitee Pages  
        uses: yanglbme/gitee-pages-action@main  
        with:  
          # 替换为你的 Gitee 用户名  
          gitee-username: Moonofweisheng  
          # 注意在 Settings->Secrets 配置 GITEE_PASSWORD  
          gitee-password: ${{ secrets.GITEE_PASSWORD }}  
          # 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错  
          gitee-repo: Moonofweisheng/uni-mini-router  
          # 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在)  
          branch: gh-pages  

其中GITEE_RSA_PRIVATE_KEY是私钥,生成步骤如下:

  1. 在 GitHub 项目的​Settings -> Pages路径下配置Build and deploymentsourceDeploy from a branchbranch选择gh-pages,路径选择/root
  2. 在 Gitee 项目的服务 -> Gitee Pages中设置部署分支gh-pages,部署目录则不用填。
  3. 在命令行终端或 Git Bash 使用命令 ssh-keygen 生成 SSH Key,连续三次回车。生成的 id_rsa 是私钥,id_rsa.pub 是公钥。
  4. 在 GitHub 项目的​Settings -> Secrets路径下配置好命名为 GITEE_RSA_PRIVATE_KEY 密钥。GITEE_RSA_PRIVATE_KEY 存放 id_rsa 私钥。
  5. 在 GitHub 的个人设置页面SSH and GPG keys​ 配置 SSH 公钥(即:id_rsa.pub),命名任意。
  6. 在 Gitee 的个人设置页面SSH 公钥​ 配置 SSH 公钥(即:id_rsa.pub),命名可任意。
  7. GitHub 项目的 ​Settings -> Secrets 路径下配置好命名为 GITEE_RSA_PRIVATE_KEYGITEE_PASSWORD 的两个密钥。其中:GITEE_RSA_PRIVATE_KEY 存放 id_rsa 私钥;GITEE_PASSWORD 存放 Gitee 帐号的密码。
  8. 在 GitHub 项目的​Settings -> Actions -> General路径下配置Fork pull request workflows from outside collaboratorsRequire approval for first-time contributors who are new to GitHub,将Workflow permissions配置为Read and write permissions

至此我们就实现了通过Github Action 部署Github Pages并同步至Gitee Pages

Algolia 搜索

vitepress自带站内搜索功能有限,所以我推荐使用Algolia搜索,Algolia 提供了一套强大的搜索功能,包括全文搜索、过滤、排序、分页和高亮等。它还支持多语言搜索、拼写纠正和近义词处理,以提供更准确和相关的搜索结果。

申请 Docsearch

首先,我们打开Docsearch的申请地址,填写网站地址、邮箱和仓库地址等信息,这里注意,文档内容和代码要是开源且可以公开访问的。申请过后我们会收到三封邮件,按照指示即可完成申请。

配置文档

在文档的config中增加themeConfig,配置search

export default defineConfig({  
  base: "/uni-mini-router/",  
  title: `Uni Mini Router`,  
  ...  
  themeConfig: {  
    search: {  
      provider: 'algolia',  
      options: {  
        appId: '你的appid',  
        apiKey: '你的APIkey,注意是搜索的key',  
        indexName: '你的indexName',  
      },  
    },  
  },  
  ...  
})

执行爬虫

我们这里使用Algolia提供的爬虫进行爬取,访问地址,选中我们创建的index,然后点击右上角的【Restart crawling】执行。
当然即使我们不手动执行,Algolia也会定期对我们的文档进行爬取的。

总结

通过使用VitePressGitHub PagesGitee Pages,以及GitHub Actions,我们成功搭建了一个专属文档的自动化部署系统。VitePress作为VuePress的继任者,提供了更好的开发体验和性能表现。我们使用VitePress创建了一个uni-app路由库的文档,并通过GitHub Actions实现了自动部署到GitHub PagesGitee Pages

为了解决GitHub Pages在国内访问速度不理想的问题,我们使用GitHub Actions将文档部署到GitHub Pages,并通过Gitee Pages实现了同步部署。这样,无论用户在国内还是国外,都可以快速访问到我们的文档。

为了提供更好的搜索功能,我们申请了AlgoliaDocsearch服务,并在配置文件中添加了搜索功能的相关配置。通过执行Algolia提供的爬虫,我们可以定期更新文档的搜索索引,以提供准确和相关的搜索结果。

总的来说,通过使用VitePressGitHub PagesGitee Pages,以及GitHub ActionsAlgolia,我们成功搭建了一个功能强大、美观专业的文档系统,为用户提供了更好的阅读和搜索体验。

链接

继续阅读 »

介绍

VitePressVuePress 的精神继承者。最初的 VuePress 基于Vue 2webpack。在VitePress内部使用了Vue 3Vite,这使得VitePress在开发体验、生产性能、默认主题的精细化和更灵活的自定义API方面提供了显著的改进。本文将介绍使用VitePress搭建uni-app路由库uni-mini-router的文档,并通过github action实现自动化部署到github pagesgitee pages

创建项目

安装

关于安装配置的问题,vitepress目前还没有稳定的版本,所以可能会有所变动,推荐还是看一下文档然后进行创建。

mkdir your-project  
npm init  
yarn add -D vitepress

安装向导

VitePress 附带一个命令行设置向导,可帮助您搭建基本项目的基架。安装后,通过运行以下命令启动向导:

npx vitepress init

您会看到几个简单的问题:

┌  Welcome to VitePress!  
│  
◇  Where should VitePress initialize the config?  
│  ./docs  
│  
◇  Site title:  
│  uni-mini-router(你的项目名称)  
│  
◇  Site description:  
│  一个基于vue3+typescript的uni-app路由库(你的项目介绍)  
│  
◇  Theme:  
│  Default Theme  
│  
◇  Use TypeScript for config and theme files?  
│  Yes  
│  
◇  Add VitePress npm scripts to package.json?  
│  Yes  
│  
└  Done! Now run npm run docs:dev and start writing.

执行完本步骤后,将会向你的package.json注入以下脚本:

{  
  ...  
  "scripts": {  
    "docs:dev": "vitepress dev docs",  
    "docs:build": "vitepress build docs",  
    "docs:preview": "vitepress preview docs"  
  },  
  ...  
}

运行

npm run docs:dev

配置

docs/.vitepress文件夹中有一个 config.mts 文件,我们可以在这里配置文档项目,配置项参考配置

import { defineConfig } from 'vitepress'  

// https://vitepress.dev/reference/site-config  
export default defineConfig({  
  title: "uni-mini-router",  
  description: "一个基于vue3+typescript的uni-app路由库",  
  themeConfig: {  
    // https://vitepress.dev/reference/default-theme-config  
    nav: [  
      { text: 'Home', link: '/' },  
      { text: 'Examples', link: '/markdown-examples' }  
    ],  

    sidebar: [  
      {  
        text: 'Examples',  
        items: [  
          { text: 'Markdown Examples', link: '/markdown-examples' },  
          { text: 'Runtime API Examples', link: '/api-examples' }  
        ]  
      }  
    ],  

    socialLinks: [  
      { icon: 'github', link: 'https://github.com/vuejs/vitepress' }  
    ]  
  }  
})  

部署到 Github Pages + Gitee Pages

  1. 修改configbase
    • 如果要部署到 https://<USERNAME>.github.io/,则可以省略 base,因为它默认为 “/”。
    • 如果您正在部署到 https://<USERNAME>.github.io/<REPO>/,例如,您的存储库位于 github.com/<REPO>/,然后将 base 设置为 /<REPO>/
// 示例  
import { defineConfig } from 'vitepress'  

export default defineConfig({  
  base: "/uni-mini-router/", // 这里为仓库名  
  title: "uni-mini-router",  
})
  1. 创建 Github Action 部署Github Pages并同步至Gitee Pages

Github Pages在国内的访问速度并不理想,而Gitee则没有类似Github Action的功能且标准版Gitee Pages不支持自动部署,所以我们通过Github Action 将文档部署Github Pages并同步至Gitee Pages。

在项目根目录下创建.github文件夹,.github中创建workflows文件夹并创建文件deploy.yml


name: Deploy VitePress site to Pages  

on:  
  push:  
    tags:  
      - '*'  

  workflow_dispatch:  

jobs:  
  deploy-and-sync:  
    runs-on: ubuntu-latest  
    steps:  
      - name: Checkout   
        uses: actions/checkout@v4  
        with:  
          ref: 'master'  

      - name: Install yarn  
        run: corepack enable  

      - uses: actions/setup-node@v3  
        with:  
          node-version: '18'  
          cache: 'yarn'  

      - name: Install dependencies  
        run: yarn install  

      - name: Build Site  
        run: npm run docs:build  

      # 将文档产物提交到gh-pages分支  
      - name: Deploy for Gitee   
        uses: JamesIves/github-pages-deploy-action@v4.4.1  
        with:  
          branch: gh-pages  
          folder: docs/.vitepress/dist  
          # enable single-commit to reduce the repo size  
          single-commit: true  
          clean: true  

      - name: Sync to Gitee  
        uses: wearerequired/git-mirror-action@v1.2.0  
        env:  
          SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}  
        with:  
          # GitHub 仓库地址  
          source-repo: git@github.com:Moonofweisheng/uni-mini-router.git  
          # Gitee 仓库地址  
          destination-repo: git@gitee.com:Moonofweisheng/uni-mini-router.git  

      - name: Build Gitee Pages  
        uses: yanglbme/gitee-pages-action@main  
        with:  
          # 替换为你的 Gitee 用户名  
          gitee-username: Moonofweisheng  
          # 注意在 Settings->Secrets 配置 GITEE_PASSWORD  
          gitee-password: ${{ secrets.GITEE_PASSWORD }}  
          # 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错  
          gitee-repo: Moonofweisheng/uni-mini-router  
          # 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在)  
          branch: gh-pages  

其中GITEE_RSA_PRIVATE_KEY是私钥,生成步骤如下:

  1. 在 GitHub 项目的​Settings -> Pages路径下配置Build and deploymentsourceDeploy from a branchbranch选择gh-pages,路径选择/root
  2. 在 Gitee 项目的服务 -> Gitee Pages中设置部署分支gh-pages,部署目录则不用填。
  3. 在命令行终端或 Git Bash 使用命令 ssh-keygen 生成 SSH Key,连续三次回车。生成的 id_rsa 是私钥,id_rsa.pub 是公钥。
  4. 在 GitHub 项目的​Settings -> Secrets路径下配置好命名为 GITEE_RSA_PRIVATE_KEY 密钥。GITEE_RSA_PRIVATE_KEY 存放 id_rsa 私钥。
  5. 在 GitHub 的个人设置页面SSH and GPG keys​ 配置 SSH 公钥(即:id_rsa.pub),命名任意。
  6. 在 Gitee 的个人设置页面SSH 公钥​ 配置 SSH 公钥(即:id_rsa.pub),命名可任意。
  7. GitHub 项目的 ​Settings -> Secrets 路径下配置好命名为 GITEE_RSA_PRIVATE_KEYGITEE_PASSWORD 的两个密钥。其中:GITEE_RSA_PRIVATE_KEY 存放 id_rsa 私钥;GITEE_PASSWORD 存放 Gitee 帐号的密码。
  8. 在 GitHub 项目的​Settings -> Actions -> General路径下配置Fork pull request workflows from outside collaboratorsRequire approval for first-time contributors who are new to GitHub,将Workflow permissions配置为Read and write permissions

至此我们就实现了通过Github Action 部署Github Pages并同步至Gitee Pages

Algolia 搜索

vitepress自带站内搜索功能有限,所以我推荐使用Algolia搜索,Algolia 提供了一套强大的搜索功能,包括全文搜索、过滤、排序、分页和高亮等。它还支持多语言搜索、拼写纠正和近义词处理,以提供更准确和相关的搜索结果。

申请 Docsearch

首先,我们打开Docsearch的申请地址,填写网站地址、邮箱和仓库地址等信息,这里注意,文档内容和代码要是开源且可以公开访问的。申请过后我们会收到三封邮件,按照指示即可完成申请。

配置文档

在文档的config中增加themeConfig,配置search

export default defineConfig({  
  base: "/uni-mini-router/",  
  title: `Uni Mini Router`,  
  ...  
  themeConfig: {  
    search: {  
      provider: 'algolia',  
      options: {  
        appId: '你的appid',  
        apiKey: '你的APIkey,注意是搜索的key',  
        indexName: '你的indexName',  
      },  
    },  
  },  
  ...  
})

执行爬虫

我们这里使用Algolia提供的爬虫进行爬取,访问地址,选中我们创建的index,然后点击右上角的【Restart crawling】执行。
当然即使我们不手动执行,Algolia也会定期对我们的文档进行爬取的。

总结

通过使用VitePressGitHub PagesGitee Pages,以及GitHub Actions,我们成功搭建了一个专属文档的自动化部署系统。VitePress作为VuePress的继任者,提供了更好的开发体验和性能表现。我们使用VitePress创建了一个uni-app路由库的文档,并通过GitHub Actions实现了自动部署到GitHub PagesGitee Pages

为了解决GitHub Pages在国内访问速度不理想的问题,我们使用GitHub Actions将文档部署到GitHub Pages,并通过Gitee Pages实现了同步部署。这样,无论用户在国内还是国外,都可以快速访问到我们的文档。

为了提供更好的搜索功能,我们申请了AlgoliaDocsearch服务,并在配置文件中添加了搜索功能的相关配置。通过执行Algolia提供的爬虫,我们可以定期更新文档的搜索索引,以提供准确和相关的搜索结果。

总的来说,通过使用VitePressGitHub PagesGitee Pages,以及GitHub ActionsAlgolia,我们成功搭建了一个功能强大、美观专业的文档系统,为用户提供了更好的阅读和搜索体验。

链接

收起阅读 »

小程序UV互有没有呢

微信小程序

人数迟迟未达到,有没有互的,召集知己

人数迟迟未达到,有没有互的,召集知己

APP备案公钥、证书MD5指纹/签名MD5值获取最简单方法

本文只详细讲解android app获取方法,三个平台获取方法(android、windows、macOS):

一. Android手机平台:前提,您的应用已安装到手机;然后,android应用市场搜索下载安装 APP备案助手,此app可直接获取所有已安装app的公钥、证书MD5指纹/签名MD5值,示例:获取 抖音app公钥、MD5等信息,

APP备案助手 获取结果,与下面两种方式获取结果一致;

二. Windows平台,下载jadx-gui反编译工具,github下载地址: https://github.com/skylot/jadx/releases

下载上图这个zip,然后解压后,打开 jadx-gui.exe,

点击 Open file/打开文件,打开apk安装包文件,

点击APK signature, Modulus/模数 为公钥,十进制显示的;MD5 Fingerprint/MD5签名,APP备案填写时需要去掉空格,填写32位长度的十六进制数据。

三. macOS平台,下载jadx-gui反编译工具,下载方法

终端执行 brew install jadx

执行完毕后,终端再输入命令 jadx-gui,即可打开jadx,

点击 Open file/打开文件,打开apk安装包文件,

点击APK signature, Modulus/模数 为公钥,十进制显示的;MD5 Fingerprint/MD5签名,APP备案填写时需要去掉空格,填写32位长度的十六进制数据。

继续阅读 »

本文只详细讲解android app获取方法,三个平台获取方法(android、windows、macOS):

一. Android手机平台:前提,您的应用已安装到手机;然后,android应用市场搜索下载安装 APP备案助手,此app可直接获取所有已安装app的公钥、证书MD5指纹/签名MD5值,示例:获取 抖音app公钥、MD5等信息,

APP备案助手 获取结果,与下面两种方式获取结果一致;

二. Windows平台,下载jadx-gui反编译工具,github下载地址: https://github.com/skylot/jadx/releases

下载上图这个zip,然后解压后,打开 jadx-gui.exe,

点击 Open file/打开文件,打开apk安装包文件,

点击APK signature, Modulus/模数 为公钥,十进制显示的;MD5 Fingerprint/MD5签名,APP备案填写时需要去掉空格,填写32位长度的十六进制数据。

三. macOS平台,下载jadx-gui反编译工具,下载方法

终端执行 brew install jadx

执行完毕后,终端再输入命令 jadx-gui,即可打开jadx,

点击 Open file/打开文件,打开apk安装包文件,

点击APK signature, Modulus/模数 为公钥,十进制显示的;MD5 Fingerprint/MD5签名,APP备案填写时需要去掉空格,填写32位长度的十六进制数据。

收起阅读 »

解决JSON.parse大数字转换精度丢失问题

request JavaScript json

因为js语言问题,数字大于16位后,会丢失16位以后的数字:比如声明 let n = 1234567890123456789;打印n之后会显示1234567890123456800;
同理JSON.parse将json字符串转为Object时,如果其中有大数字的值,也会发生精度丢失问题。
使用uni.request或uniCloud.httpclient.request 接收远程api返回数据时,如果返回数据里有大数字,并且直接设置了返回数据类型为json的话,则不能正常接收大数字。
此时就需要先接收为纯文本,也就是dataType = "text",再通过脚本匹配大数字后,给其加上字符串的引号,再进行JSON.parse的转换。这样就能正确接收大数字值了。

下面是转换函数实现:

// 解决大数字转换丢失问题  
function jsonParse(text) {  
    text = text.replace(/([^"'\d])(\d{16,})/g, "$1\"$2\"")  
    return JSON.parse(text);  
}
继续阅读 »

因为js语言问题,数字大于16位后,会丢失16位以后的数字:比如声明 let n = 1234567890123456789;打印n之后会显示1234567890123456800;
同理JSON.parse将json字符串转为Object时,如果其中有大数字的值,也会发生精度丢失问题。
使用uni.request或uniCloud.httpclient.request 接收远程api返回数据时,如果返回数据里有大数字,并且直接设置了返回数据类型为json的话,则不能正常接收大数字。
此时就需要先接收为纯文本,也就是dataType = "text",再通过脚本匹配大数字后,给其加上字符串的引号,再进行JSON.parse的转换。这样就能正确接收大数字值了。

下面是转换函数实现:

// 解决大数字转换丢失问题  
function jsonParse(text) {  
    text = text.replace(/([^"'\d])(\d{16,})/g, "$1\"$2\"")  
    return JSON.parse(text);  
}
收起阅读 »