HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

什么是子组件 什么是父组件?

uniapp中的父组件、子组件

components中的组件为子组件,pages中的组件为父组件。也可以理解为components中的组件为模板、对象;而pages中的组件为实例。

uniapp中的父组件、子组件

components中的组件为子组件,pages中的组件为父组件。也可以理解为components中的组件为模板、对象;而pages中的组件为实例。

CSS如何让页面背景颜色铺满

<style>
page{
background-color: #F5F6FA;
}
</style>
单独另外设置一个style
前端兼职QQ 728045048

<style>
page{
background-color: #F5F6FA;
}
</style>
单独另外设置一个style
前端兼职QQ 728045048

uni-im 微信小程序发送消息后返回Cannot read property 'slice'

uni_im

{"success":false,"error":{"code":"FunctionBizError","message":"Cannot read property 'slice' of undefined","httpStatus":200}}

原因是微信小程序,用微信登录后未设置昵称,导致后端判断不了,增加一个判断即可

云对象 uni-im-co/index.obj.js

443行

let title = (nickname || username || email || mobile).slice(0, 20)

修改为

        let title = (nickname || username || email || mobile)  
        if(title!=undefined){  
                    title = title.slice(0, 20)  
                }

456行

title: title.slice(0, 20), // "收到im消息,离线时显示的标题",

修改为

title: title, // "收到im消息,离线时显示的标题"
继续阅读 »

{"success":false,"error":{"code":"FunctionBizError","message":"Cannot read property 'slice' of undefined","httpStatus":200}}

原因是微信小程序,用微信登录后未设置昵称,导致后端判断不了,增加一个判断即可

云对象 uni-im-co/index.obj.js

443行

let title = (nickname || username || email || mobile).slice(0, 20)

修改为

        let title = (nickname || username || email || mobile)  
        if(title!=undefined){  
                    title = title.slice(0, 20)  
                }

456行

title: title.slice(0, 20), // "收到im消息,离线时显示的标题",

修改为

title: title, // "收到im消息,离线时显示的标题"
收起阅读 »

vue2 uni.request返回数组

vue2 uni.request

今天发现一个奇怪的问题,uni.request,返回的结果竟然是数组,原来我记得是对象的

        uni.request({  
            url      : ...,  
            data     : ...,  
            timeout  : ...,  
            dataType : ...,  
            header   : ...,  
            method   : ...  
        }).then(res => {  

            // 这里打印出来是一个数组  
            console.log('request', res)  

        }).catch((err)=>{  

        });  

继续阅读 »

今天发现一个奇怪的问题,uni.request,返回的结果竟然是数组,原来我记得是对象的

        uni.request({  
            url      : ...,  
            data     : ...,  
            timeout  : ...,  
            dataType : ...,  
            header   : ...,  
            method   : ...  
        }).then(res => {  

            // 这里打印出来是一个数组  
            console.log('request', res)  

        }).catch((err)=>{  

        });  

收起阅读 »

Vue打包项目报错 Uncaught SyntaxError: Unexpected token ‘<‘ 的解决方法

打包 vue.js

打包时路径配置错误
在vue.config.js中
publicPath的默认值为 '/'
把publicPath的值设为“./”就可以了

publicPath#
Type: string

Default: '/'

部署应用包时的基本 URL。用法和 webpack 本身的 output.publicPath 一致,
但是 Vue CLI 在一些其他地方也需要用到这个值,所以请始终使用 publicPath 而不要直接修改 webpack 的 output.publicPath。

默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,
例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。
例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath 为 /my-app/。

这个值也可以被设置为空字符串 ('') 或是相对路径 ('./'),
这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径,
也可以用在类似 Cordova hybrid 应用的文件系统中。

继续阅读 »

打包时路径配置错误
在vue.config.js中
publicPath的默认值为 '/'
把publicPath的值设为“./”就可以了

publicPath#
Type: string

Default: '/'

部署应用包时的基本 URL。用法和 webpack 本身的 output.publicPath 一致,
但是 Vue CLI 在一些其他地方也需要用到这个值,所以请始终使用 publicPath 而不要直接修改 webpack 的 output.publicPath。

默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,
例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。
例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath 为 /my-app/。

这个值也可以被设置为空字符串 ('') 或是相对路径 ('./'),
这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径,
也可以用在类似 Cordova hybrid 应用的文件系统中。

收起阅读 »

【ChatGPT】将Chat GPT接入你的uni-app中

OpenAI ChatGPT uni_im uniCloud

更好的排版请访问:我的语雀工作空间我的知乎专栏我的稀土掘金

注意:实际项目使用时,务必添加内容安全检测,文字检测!!! 推荐使用:uni-sec-check

在uni-im中集成OpenAI

本文将介绍如何在uni-im中集成OpenAI API,以便为您的聊天应用程序提供更加智能和高效的聊天服务。通过集成OpenAI API,您可以利用各种自然语言处理和机器学习技术,如文本生成、语言翻译、图像分类等,来增强您的应用程序的功能和性能。

OK,Talk is cheap. Show me the code.

步骤一:注册OpenAI账号并获取API密钥

首先,您需要有OpenAI网站账号,并获取API密钥。网上有很多相关教程

访问 Account API Keys - OpenAI API ,点击这里的 Create new secret key,创建一个新的 key,并保存备用。

步骤二:创建uni-im应用程序

接下来,我们在HBuilder X中创建一个uni-im项目,插件地址:https://ext.dcloud.net.cn/plugin?name=uni-im,并在开发者中心中启用UniPush2.0服务。

在此示例中我们直接使用uni-im的示例项目来进行。

1、在插件市场中点击【使用 HBuilderX 导入示例项目】

2、在HBuilder X中创建项目

3、绑定uniCloud服务空间

4、部署云端资源

5、开始部署

等待自动部署,如果是新服务空间一路同意。缺表就创建。如果是和其他项目共用一个服务空间。请谨慎操作

6、在开发者后台开启UniPush


7、跑起来


没有报错就成功了。[GtPush] ["already connected"]

步骤三:在UniCloud中集成OpenAI API

1、新建云函数

2、修改云函数Nodejs版本

因为let's encrypt根证书过期,nodejs8版本请求使用了let's encrypt证书的网站时会出现 certificate has expired所以需要将云函数升级到nodejs12。相关文档: 云函数通过https访问其他服务器时出现“certificate has expired”

3、调用OpenAI API

OpenAI接口文档地址:https://platform.openai.com/docs/api-reference/completions uniCloud访问其他HTTP服务文档:https://uniapp.dcloud.net.cn/uniCloud/cf-functions.html#httpclient

'use strict';  
//OpenAI SDK、uniCloud.httpclient.request方式 二选一。  
// const {  
//  Configuration,  
//  OpenAIApi  
// } = require("openai");  

exports.main = async (event, context) => {  
    //event为客户端上传的参数  
    console.log('event : ', event)  
    // const configuration = new Configuration({  
    //  apiKey: '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',  
    // });  
    // const openai = new OpenAIApi(configuration);  
    // const {  
    //  data: {  
    //      choices  
    //  },  
    //  status,  
    //  statusText  
    // } = await openai.createCompletion({  
    //  model: "text-davinci-003",  
    //  prompt: `input:${event.body}?  
    //   output:`,  
    //  max_tokens: 300,  
    //  temperature: 1,  
    //  stop: ['output:']  
    // });  
    const {  
        data: {  
            choices  
        },  
        status,  
        statusText  
    } = await uniCloud.httpclient.request('https://api.openai.com/v1/completions', {  
        method: 'POST',  
        data: {  
            // GPT-3 模型。详细介绍请参考:https://platform.openai.com/docs/models/overview  
            model: "text-davinci-003",  
            prompt: `input:${event.body}?  
      output:`,  
            max_tokens: 300,  
            temperature: .6,  
            stop: ['output:']  
        },  
        headers: {  
            Authorization: `Bearer ${YOUR_API_KEY}`  
        },  
        timeout: 10000,  
        contentType: 'json', // 指定以application/json发送data内的数据  
        dataType: 'json' // 指定返回值为json格式,自动进行parse  
    })  
    //返回数据给客户端  
    return {  
        data: choices,  
        errCode: status,  
        errMsg: statusText  
    }  
};

4、测试一下

a、首先在云函数右击,配置运行测试参数

b、本地运行云函数

c、Bingo~

OpenAI的对接就这么简单。接下来我们来改造一下Uni-im

步骤四:在Uni-IM中集成Chat-GPT机器人。

1、创建Chat-GPT机器人账号


Chrome devtools-Applocation中找到“机器人”的uid63eb5550819ce84ffc2df8c5

当然,更推荐你去uniCloud 控制台-云数据库 中查看uni-id-users表中的_id字段.

2、在公共模块中添加uid

3、调用ChatGPT接口回复用户信息

这一步我们将改造uni-im-co云对象,在用户发送消息的方法中请求OpenAI接口,并将接口返回的消息回复给用户。uni-im-co云对象的sendMsg是用户发送消息,并存储到数据库的方法。
我们在uni-im-co云对象中ctrl+G/control+G定位代码到435

1、第一步

// 修改文件:uni-im-co/index.obj.js:435  

// 请求公共模块中的ChatGPT _uid常量  
const isChatGPT = uniImConfig.config('ChatGPT_uid')  
if(isChatGPT === to_uid){  
    try{  
        // 请求chatGPT云函数  
        const {result: { data: openAIResp }} = await uniCloud.callFunction({  
            name:'ChatGPT',  
            data:{  
                body  
            }  
        })  
        // 拼接消息  
        const allChatData = openAIResp.reduce((prev, {text})=> prev + text, '')  
        // 因为这一步要模拟ChatGPT给用户发消息,所以调换一下发送人和接受人的uid。  
        const tempParams = { ...params,  
        ...{  
            from_uid: to_uid,  
            to_uid: from_uid,  
            body: allChatData,  
            client_create_time: +new Date,  
            original_from_uid: to_uid // 这个字段用来递归调用时区分当前发送者uid  
        }};  
        // 递归调用云对象中的sendMsg方法  
        uniCloud.importObject('uni-im-co').sendMsg(tempParams, context);  
    } catch (error){  
        console.log('error boredape:>>>>>>>>>>>> ', error);  
    }  
}

1、兼容发送者uid

刚才我们在递归调用sendMsg方法时,传递了一个original_from_uid字段。这个字段是用来区分当前发送者uid的。我们在sendMsg方法参数中兼容处理一下它(我们在uni-im-co云对象中ctrl+G/control+G定位代码到248行找到sendMsg方法)

// 文件位置:uni-im-co/index.obj.js:248  
const {  
            to_uid,  
            group_id,  
            body,  
            type,  
            isRetries,  
            appId,  
            original_from_uid // 新增  
        } = params  
        //发送者身份id  
        const from_uid = this.uid || original_from_uid; // 修改

4、大功告成!让我们来跟ChatGPT进行第一次正式约会。

1、创建一个自己的账号

2、手动跳转到chat页面

目前我们还不能直接和机器人对话。那就让我们主动一点在浏览器中输入房间号:(此处的user_id就是ChatGPTuid)
http://localhost:8081/#/uni_modules/uni-im/pages/chat/chat?user_id=63eb5550819ce84ffc2df8c5

emmmmm~报错了。


看来是uniPush不支持本地调试的问题,我们部署一下


如果有模块冲突请确认是否替换.

3、OK。我们部署完之后切换使用云端云函数

d、完成后重新运行一下项目。

EMMMMMMMMM~这又是什么错呢?

4、我们查一下云函数的运行日志


发现这个是因为OpenAI超时导致的。云函数/云对象默认的超时时间时5S,这对于OpenAI来说很难在这么短时间内回复。

5、那我们修改一下ChatGPTuni-im-co这两个云函数和云对象的超时时间

最大只支持10S

6、uni-im-co云对象在递归时会走云对象的拦截器_before进行鉴权。递归调用没有`this.clientInfo参数。所以鉴权失败,无法发送消息返回给用户。

我们来修改一下uni-im-co云对象的拦截器。判断当前云对象的调用环境。如果是云函数调用的则不进行鉴权。定位代码到uni-im-co/index.obj.js:35

        if (needLoginMethodName.includes(this.getMethodName())) {  
            let res = await this.uniIdCommon.checkToken(this.clientInfo.uniIdToken)  
            // console.log('checkToken', JSON.stringify(res));  
                        // 判断当前调用的云对象方法是 ‘sendMsg’ 并且 是云函数调用的(递归)  
            if(this.getMethodName() === 'sendMsg' && this.getClientInfo().source === 'function'){} // 新增  
            else if (res.errCode ) {  
                throw new Error(res.errCode)  
            }  
            this.uid = res.uid  
        }

再来试一把

The END!

以上就是所有我们使用UniIM集成OpenAI的全部步骤。使用UniCloud快速对接OpenAI,封装属于自己的ChatGPT

稍后我会上传Demo工程源码,不过强烈推荐您自己动手哦。能让您更加了解UniCloudUniIM。真的是超棒的框架!

如果您觉得我的文章不错。请在左下方点赞,并关注我哦,如果有新文章会第一时间给您推送。您的支持是对我生发爆肝最大的动力。
如果您还想让我帮您体验使用Uni-app/UniCloud集成第三方好玩的SDK,请在下方评论区留言哦。

继续阅读 »

更好的排版请访问:我的语雀工作空间我的知乎专栏我的稀土掘金

注意:实际项目使用时,务必添加内容安全检测,文字检测!!! 推荐使用:uni-sec-check

在uni-im中集成OpenAI

本文将介绍如何在uni-im中集成OpenAI API,以便为您的聊天应用程序提供更加智能和高效的聊天服务。通过集成OpenAI API,您可以利用各种自然语言处理和机器学习技术,如文本生成、语言翻译、图像分类等,来增强您的应用程序的功能和性能。

OK,Talk is cheap. Show me the code.

步骤一:注册OpenAI账号并获取API密钥

首先,您需要有OpenAI网站账号,并获取API密钥。网上有很多相关教程

访问 Account API Keys - OpenAI API ,点击这里的 Create new secret key,创建一个新的 key,并保存备用。

步骤二:创建uni-im应用程序

接下来,我们在HBuilder X中创建一个uni-im项目,插件地址:https://ext.dcloud.net.cn/plugin?name=uni-im,并在开发者中心中启用UniPush2.0服务。

在此示例中我们直接使用uni-im的示例项目来进行。

1、在插件市场中点击【使用 HBuilderX 导入示例项目】

2、在HBuilder X中创建项目

3、绑定uniCloud服务空间

4、部署云端资源

5、开始部署

等待自动部署,如果是新服务空间一路同意。缺表就创建。如果是和其他项目共用一个服务空间。请谨慎操作

6、在开发者后台开启UniPush


7、跑起来


没有报错就成功了。[GtPush] ["already connected"]

步骤三:在UniCloud中集成OpenAI API

1、新建云函数

2、修改云函数Nodejs版本

因为let's encrypt根证书过期,nodejs8版本请求使用了let's encrypt证书的网站时会出现 certificate has expired所以需要将云函数升级到nodejs12。相关文档: 云函数通过https访问其他服务器时出现“certificate has expired”

3、调用OpenAI API

OpenAI接口文档地址:https://platform.openai.com/docs/api-reference/completions uniCloud访问其他HTTP服务文档:https://uniapp.dcloud.net.cn/uniCloud/cf-functions.html#httpclient

'use strict';  
//OpenAI SDK、uniCloud.httpclient.request方式 二选一。  
// const {  
//  Configuration,  
//  OpenAIApi  
// } = require("openai");  

exports.main = async (event, context) => {  
    //event为客户端上传的参数  
    console.log('event : ', event)  
    // const configuration = new Configuration({  
    //  apiKey: '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',  
    // });  
    // const openai = new OpenAIApi(configuration);  
    // const {  
    //  data: {  
    //      choices  
    //  },  
    //  status,  
    //  statusText  
    // } = await openai.createCompletion({  
    //  model: "text-davinci-003",  
    //  prompt: `input:${event.body}?  
    //   output:`,  
    //  max_tokens: 300,  
    //  temperature: 1,  
    //  stop: ['output:']  
    // });  
    const {  
        data: {  
            choices  
        },  
        status,  
        statusText  
    } = await uniCloud.httpclient.request('https://api.openai.com/v1/completions', {  
        method: 'POST',  
        data: {  
            // GPT-3 模型。详细介绍请参考:https://platform.openai.com/docs/models/overview  
            model: "text-davinci-003",  
            prompt: `input:${event.body}?  
      output:`,  
            max_tokens: 300,  
            temperature: .6,  
            stop: ['output:']  
        },  
        headers: {  
            Authorization: `Bearer ${YOUR_API_KEY}`  
        },  
        timeout: 10000,  
        contentType: 'json', // 指定以application/json发送data内的数据  
        dataType: 'json' // 指定返回值为json格式,自动进行parse  
    })  
    //返回数据给客户端  
    return {  
        data: choices,  
        errCode: status,  
        errMsg: statusText  
    }  
};

4、测试一下

a、首先在云函数右击,配置运行测试参数

b、本地运行云函数

c、Bingo~

OpenAI的对接就这么简单。接下来我们来改造一下Uni-im

步骤四:在Uni-IM中集成Chat-GPT机器人。

1、创建Chat-GPT机器人账号


Chrome devtools-Applocation中找到“机器人”的uid63eb5550819ce84ffc2df8c5

当然,更推荐你去uniCloud 控制台-云数据库 中查看uni-id-users表中的_id字段.

2、在公共模块中添加uid

3、调用ChatGPT接口回复用户信息

这一步我们将改造uni-im-co云对象,在用户发送消息的方法中请求OpenAI接口,并将接口返回的消息回复给用户。uni-im-co云对象的sendMsg是用户发送消息,并存储到数据库的方法。
我们在uni-im-co云对象中ctrl+G/control+G定位代码到435

1、第一步

// 修改文件:uni-im-co/index.obj.js:435  

// 请求公共模块中的ChatGPT _uid常量  
const isChatGPT = uniImConfig.config('ChatGPT_uid')  
if(isChatGPT === to_uid){  
    try{  
        // 请求chatGPT云函数  
        const {result: { data: openAIResp }} = await uniCloud.callFunction({  
            name:'ChatGPT',  
            data:{  
                body  
            }  
        })  
        // 拼接消息  
        const allChatData = openAIResp.reduce((prev, {text})=> prev + text, '')  
        // 因为这一步要模拟ChatGPT给用户发消息,所以调换一下发送人和接受人的uid。  
        const tempParams = { ...params,  
        ...{  
            from_uid: to_uid,  
            to_uid: from_uid,  
            body: allChatData,  
            client_create_time: +new Date,  
            original_from_uid: to_uid // 这个字段用来递归调用时区分当前发送者uid  
        }};  
        // 递归调用云对象中的sendMsg方法  
        uniCloud.importObject('uni-im-co').sendMsg(tempParams, context);  
    } catch (error){  
        console.log('error boredape:>>>>>>>>>>>> ', error);  
    }  
}

1、兼容发送者uid

刚才我们在递归调用sendMsg方法时,传递了一个original_from_uid字段。这个字段是用来区分当前发送者uid的。我们在sendMsg方法参数中兼容处理一下它(我们在uni-im-co云对象中ctrl+G/control+G定位代码到248行找到sendMsg方法)

// 文件位置:uni-im-co/index.obj.js:248  
const {  
            to_uid,  
            group_id,  
            body,  
            type,  
            isRetries,  
            appId,  
            original_from_uid // 新增  
        } = params  
        //发送者身份id  
        const from_uid = this.uid || original_from_uid; // 修改

4、大功告成!让我们来跟ChatGPT进行第一次正式约会。

1、创建一个自己的账号

2、手动跳转到chat页面

目前我们还不能直接和机器人对话。那就让我们主动一点在浏览器中输入房间号:(此处的user_id就是ChatGPTuid)
http://localhost:8081/#/uni_modules/uni-im/pages/chat/chat?user_id=63eb5550819ce84ffc2df8c5

emmmmm~报错了。


看来是uniPush不支持本地调试的问题,我们部署一下


如果有模块冲突请确认是否替换.

3、OK。我们部署完之后切换使用云端云函数

d、完成后重新运行一下项目。

EMMMMMMMMM~这又是什么错呢?

4、我们查一下云函数的运行日志


发现这个是因为OpenAI超时导致的。云函数/云对象默认的超时时间时5S,这对于OpenAI来说很难在这么短时间内回复。

5、那我们修改一下ChatGPTuni-im-co这两个云函数和云对象的超时时间

最大只支持10S

6、uni-im-co云对象在递归时会走云对象的拦截器_before进行鉴权。递归调用没有`this.clientInfo参数。所以鉴权失败,无法发送消息返回给用户。

我们来修改一下uni-im-co云对象的拦截器。判断当前云对象的调用环境。如果是云函数调用的则不进行鉴权。定位代码到uni-im-co/index.obj.js:35

        if (needLoginMethodName.includes(this.getMethodName())) {  
            let res = await this.uniIdCommon.checkToken(this.clientInfo.uniIdToken)  
            // console.log('checkToken', JSON.stringify(res));  
                        // 判断当前调用的云对象方法是 ‘sendMsg’ 并且 是云函数调用的(递归)  
            if(this.getMethodName() === 'sendMsg' && this.getClientInfo().source === 'function'){} // 新增  
            else if (res.errCode ) {  
                throw new Error(res.errCode)  
            }  
            this.uid = res.uid  
        }

再来试一把

The END!

以上就是所有我们使用UniIM集成OpenAI的全部步骤。使用UniCloud快速对接OpenAI,封装属于自己的ChatGPT

稍后我会上传Demo工程源码,不过强烈推荐您自己动手哦。能让您更加了解UniCloudUniIM。真的是超棒的框架!

如果您觉得我的文章不错。请在左下方点赞,并关注我哦,如果有新文章会第一时间给您推送。您的支持是对我生发爆肝最大的动力。
如果您还想让我帮您体验使用Uni-app/UniCloud集成第三方好玩的SDK,请在下方评论区留言哦。

收起阅读 »

强缓存与协商缓存(原文:https://www.jianshu.com/p/9c95db596df5/)

缓存的优点:

减少了不必要的数据传输,节省带宽
减少服务器的负担,提升网站性能
加快了客户端加载网页的速度
用户体验友好
缺点:
资源如果有更改但是客户端不及时更新会造成用户获取信息滞后,
如果老版本有bug的话,情况会更加糟糕。

一、强缓存

respone header 的cache-control,常见的设置是max-age public private no-cache no-store等
cache-control:max-age=31536000,public,immutable

max-age表示缓存的时间 单位为秒
public表示可以被浏览器和代理服务器缓存
immutable表示该资源永远不变,在没过缓存期时,
即使用户做了刷新操作,也不向服务器发起http请求,直接读取缓存。

强缓存总结

cache-control: max-age=xxxx,public
客户端和代理服务器都可以缓存该资源;
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,
如果用户做了刷新操作,就向服务器发起http请求

cache-control: max-age=xxxx,private
只让客户端可以缓存该资源;代理服务器不缓存
客户端在xxx秒内直接读取缓存,statu code:200

cache-control: max-age=xxxx,immutable
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,
即使用户做了刷新操作,也不向服务器发起http请求

cache-control: no-cache
跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,
只有在强缓存失效了才走协商缓存的,
设置了no-cache就不会走强缓存了,每次请求都回询问服务端。

cache-control: no-store
不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了。

二、协商缓存

协商缓存就是需要客户端和服务器两端进行交互的。
怎么设置协商缓存?
response header里面的设置
etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT

etag:每个文件有一个,改动文件了就变了,就是个文件hash,每个文件唯一,
就像用webpack打包的时候,每个资源都会有这个东西,如: app.js打包后变为 app.c20abbde.js,加个唯一hash,也是为了解决缓存问题。
last-modified:文件的修改时间,精确到秒
也就是说,每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,
服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,
和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,
这时候对客户端来说,每次请求都是要进行协商缓存了,即:
发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->没过期-->返回304状态码-->客户端用缓存的老资源。
这就是一条完整的协商缓存的过程。

当然,当服务端发现资源真的过期的时候,会走如下流程:

发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->过期-->返回200状态码-->客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。

所以协商缓存步骤总结:
请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
如果资源没更改,返回304,浏览器读取本地缓存。
如果资源有更改,返回200,返回最新的资源。
补充一点,response header中的etag、last-modified在客户端重新向服务端发起请求时,会在request header中换个key名:
// response header
etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT

// request header 变为
if-none-matched: '5c20abbd-e2e8'
if-modified-since: Mon, 24 Dec 2018 09:49:49 GMT

为什么要有etag?
你可能会觉得使用last-modified已经足以让浏览器知道本地的缓存副本是否足够新,
为什么还需要etag呢?HTTP1.1中etag的出现(也就是说,etag是新增的,为了解决之前只有If-Modified的缺点)
主要是为了解决几个last-modified比较难解决的问题:

一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),
这个时候我们并不希望客户端认为这个文件被修改了,而重新get;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),
if-modified-since能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);

某些服务器不能精确的得到文件的最后修改时间。

继续阅读 »

缓存的优点:

减少了不必要的数据传输,节省带宽
减少服务器的负担,提升网站性能
加快了客户端加载网页的速度
用户体验友好
缺点:
资源如果有更改但是客户端不及时更新会造成用户获取信息滞后,
如果老版本有bug的话,情况会更加糟糕。

一、强缓存

respone header 的cache-control,常见的设置是max-age public private no-cache no-store等
cache-control:max-age=31536000,public,immutable

max-age表示缓存的时间 单位为秒
public表示可以被浏览器和代理服务器缓存
immutable表示该资源永远不变,在没过缓存期时,
即使用户做了刷新操作,也不向服务器发起http请求,直接读取缓存。

强缓存总结

cache-control: max-age=xxxx,public
客户端和代理服务器都可以缓存该资源;
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,
如果用户做了刷新操作,就向服务器发起http请求

cache-control: max-age=xxxx,private
只让客户端可以缓存该资源;代理服务器不缓存
客户端在xxx秒内直接读取缓存,statu code:200

cache-control: max-age=xxxx,immutable
客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,
即使用户做了刷新操作,也不向服务器发起http请求

cache-control: no-cache
跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,
只有在强缓存失效了才走协商缓存的,
设置了no-cache就不会走强缓存了,每次请求都回询问服务端。

cache-control: no-store
不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了。

二、协商缓存

协商缓存就是需要客户端和服务器两端进行交互的。
怎么设置协商缓存?
response header里面的设置
etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT

etag:每个文件有一个,改动文件了就变了,就是个文件hash,每个文件唯一,
就像用webpack打包的时候,每个资源都会有这个东西,如: app.js打包后变为 app.c20abbde.js,加个唯一hash,也是为了解决缓存问题。
last-modified:文件的修改时间,精确到秒
也就是说,每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,
服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,
和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,
这时候对客户端来说,每次请求都是要进行协商缓存了,即:
发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->没过期-->返回304状态码-->客户端用缓存的老资源。
这就是一条完整的协商缓存的过程。

当然,当服务端发现资源真的过期的时候,会走如下流程:

发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->过期-->返回200状态码-->客户端如第一次接收该资源一样,记下它的cache-control中的max-age、etag、last-modified等。

所以协商缓存步骤总结:
请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
如果资源没更改,返回304,浏览器读取本地缓存。
如果资源有更改,返回200,返回最新的资源。
补充一点,response header中的etag、last-modified在客户端重新向服务端发起请求时,会在request header中换个key名:
// response header
etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT

// request header 变为
if-none-matched: '5c20abbd-e2e8'
if-modified-since: Mon, 24 Dec 2018 09:49:49 GMT

为什么要有etag?
你可能会觉得使用last-modified已经足以让浏览器知道本地的缓存副本是否足够新,
为什么还需要etag呢?HTTP1.1中etag的出现(也就是说,etag是新增的,为了解决之前只有If-Modified的缺点)
主要是为了解决几个last-modified比较难解决的问题:

一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),
这个时候我们并不希望客户端认为这个文件被修改了,而重新get;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),
if-modified-since能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);

某些服务器不能精确的得到文件的最后修改时间。

收起阅读 »

ios

iOS

ios

ios

同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示

uniapp

系统版本号:10.0.16299
编译器版本:3.6.18(vue3)
fast启动模式
手机型号
:iqoo neo5
控制台截图


描述:多次开关手机开发者模式、usb调试;使用HBuilder自带的ADB进行真机运行;保持手机和电脑在同一个局域网下;
使用标准基座和自定义基座都无法解决该问题。

继续阅读 »

系统版本号:10.0.16299
编译器版本:3.6.18(vue3)
fast启动模式
手机型号
:iqoo neo5
控制台截图


描述:多次开关手机开发者模式、usb调试;使用HBuilder自带的ADB进行真机运行;保持手机和电脑在同一个局域网下;
使用标准基座和自定义基座都无法解决该问题。

收起阅读 »

百川旗舰版集成流程图

1.首先加入百川http://baichuan.taobao.com/,在阿里百川控制台创建应用获取appkey:

2.在阿里百川控制台"我的产品后台"中查看是否开通 “百川电商sdk” 和 “百川电商旗舰版” :

3.在https://suite.baichuan.taobao.com/suite2.htm?spm=a3c0d.8115090.0.0#/sdk上生成安全图片:
(使用V6版安全图片,android 需要上传包含包名和签名证书的apk, iOS只需要填写 bundleId;图片需另命名为yw_1222_baichuan.jpg)

继续阅读 »

1.首先加入百川http://baichuan.taobao.com/,在阿里百川控制台创建应用获取appkey:

2.在阿里百川控制台"我的产品后台"中查看是否开通 “百川电商sdk” 和 “百川电商旗舰版” :

3.在https://suite.baichuan.taobao.com/suite2.htm?spm=a3c0d.8115090.0.0#/sdk上生成安全图片:
(使用V6版安全图片,android 需要上传包含包名和签名证书的apk, iOS只需要填写 bundleId;图片需另命名为yw_1222_baichuan.jpg)

收起阅读 »

提一个路由改进建议

路由

目前的路由是没有分组的,如果一个项目几百个页面,在pages.json文件全部展示出来,找起来是非常麻烦的,而且路由匹配的速度应该也不快,可以设计路由分组,同模块下多个页面,可以在一个组下,例如下面这种php的写法,也方便在编辑器折叠,找起来也比较方便
//会员数据请求
Route::group('user/', function(){
//会员信息查询与更新
Route::group('handle/', function () {
Route::get('memberInfo/:name', 'user.UserHandle/getUserInfo');//会员个人资料页面获取信息
Route::get('TeamList', 'user.UserHandle/getTeamList');//获取团队信息
Route::get('TeamInfo/:name', 'user.UserHandle/getTeamInfo');//获取团队会员详细信息
Route::get('setContact/:name', 'user.UserHandle/setContact');//修改会员联系状态
Route::get('SystemList', 'user.UserHandle/getSystemList');//获取系统会员
Route::get('SystemInfo/:name', 'user.UserHandle/getSystemInfo');//获取系统会员详细信息
Route::get('getInstructorInfo', 'user.UserHandle/getInstructorInfo');//获取指导老师信息
Route::get('newestInfo', 'user.UserHandle/NewestInfo');//获取会员最新信息
Route::get('wechatUnbound', 'user.UserHandle/WechatUnbound');//解绑公众号
Route::post('setGroup/:name', 'user.UserHandle/setGroup');//修改团队会员等级
Route::post('setModifyPw/:name', 'user.UserHandle/setModifyPw');//修改团队会员密码
Route::post('upUserData', 'user.UserHandle/upUserInfo');//会员个人资料页面更新信息
Route::post('settglink', 'user.UserHandle/setTgLink');//修改推广识别码
Route::post('upvidwds', 'user.UserHandle/upWordVideoData');//小程序,会员提交视频和文字介绍
Route::post('lineRegUserInfo', 'user.UserHandle/lineRegUserInfo');//针对line注册的会员,进行一个资料完善
});

//订单相关  
Route::group('order/', function () {  
    Route::get('getlist', 'user.Order/getOrderList');//获取订单列表  
    Route::get('getData/:id', 'user.Order/getOrderId');//根据id获取订单信息  
    Route::get('confirm/:id', 'user.Order/confirmReceiptOrderId');//会员确认收货  
    Route::get('cancel/:id', 'user.Order/cancelOrderId');//会员取消订单  
    Route::get('softdel/:id', 'user.Order/deleteOrderId');//会员删除订单  
    // 评论相关  
    Route::get('getCommentPackage/:id', 'user.OrderComment/getOrderPackage');//会员获取评论订单  
    Route::post('upCommentContent/:id', 'user.OrderComment/commentOrderPackage');//会员提交评论订单内容  
    // 退款相关  
    Route::get('getReturnList', 'user.OrderReturn/getOrderReturnList');//获取退款列表  
    Route::get('getReturnId/:id', 'user.OrderReturn/getOrderReturnId');//根据id获取退换信息  
    Route::get('getReturnPackage/:id', 'user.OrderReturn/getOrderPackage');//退款页面获取订单的产品信息  
    Route::post('addReturnPackage', 'user.OrderReturn/addOrderReturn');//提交退款信息  
    Route::get('cancelService/:id', 'user.OrderReturn/cancelServiceList');//取消退换货  
});  

});

继续阅读 »

目前的路由是没有分组的,如果一个项目几百个页面,在pages.json文件全部展示出来,找起来是非常麻烦的,而且路由匹配的速度应该也不快,可以设计路由分组,同模块下多个页面,可以在一个组下,例如下面这种php的写法,也方便在编辑器折叠,找起来也比较方便
//会员数据请求
Route::group('user/', function(){
//会员信息查询与更新
Route::group('handle/', function () {
Route::get('memberInfo/:name', 'user.UserHandle/getUserInfo');//会员个人资料页面获取信息
Route::get('TeamList', 'user.UserHandle/getTeamList');//获取团队信息
Route::get('TeamInfo/:name', 'user.UserHandle/getTeamInfo');//获取团队会员详细信息
Route::get('setContact/:name', 'user.UserHandle/setContact');//修改会员联系状态
Route::get('SystemList', 'user.UserHandle/getSystemList');//获取系统会员
Route::get('SystemInfo/:name', 'user.UserHandle/getSystemInfo');//获取系统会员详细信息
Route::get('getInstructorInfo', 'user.UserHandle/getInstructorInfo');//获取指导老师信息
Route::get('newestInfo', 'user.UserHandle/NewestInfo');//获取会员最新信息
Route::get('wechatUnbound', 'user.UserHandle/WechatUnbound');//解绑公众号
Route::post('setGroup/:name', 'user.UserHandle/setGroup');//修改团队会员等级
Route::post('setModifyPw/:name', 'user.UserHandle/setModifyPw');//修改团队会员密码
Route::post('upUserData', 'user.UserHandle/upUserInfo');//会员个人资料页面更新信息
Route::post('settglink', 'user.UserHandle/setTgLink');//修改推广识别码
Route::post('upvidwds', 'user.UserHandle/upWordVideoData');//小程序,会员提交视频和文字介绍
Route::post('lineRegUserInfo', 'user.UserHandle/lineRegUserInfo');//针对line注册的会员,进行一个资料完善
});

//订单相关  
Route::group('order/', function () {  
    Route::get('getlist', 'user.Order/getOrderList');//获取订单列表  
    Route::get('getData/:id', 'user.Order/getOrderId');//根据id获取订单信息  
    Route::get('confirm/:id', 'user.Order/confirmReceiptOrderId');//会员确认收货  
    Route::get('cancel/:id', 'user.Order/cancelOrderId');//会员取消订单  
    Route::get('softdel/:id', 'user.Order/deleteOrderId');//会员删除订单  
    // 评论相关  
    Route::get('getCommentPackage/:id', 'user.OrderComment/getOrderPackage');//会员获取评论订单  
    Route::post('upCommentContent/:id', 'user.OrderComment/commentOrderPackage');//会员提交评论订单内容  
    // 退款相关  
    Route::get('getReturnList', 'user.OrderReturn/getOrderReturnList');//获取退款列表  
    Route::get('getReturnId/:id', 'user.OrderReturn/getOrderReturnId');//根据id获取退换信息  
    Route::get('getReturnPackage/:id', 'user.OrderReturn/getOrderPackage');//退款页面获取订单的产品信息  
    Route::post('addReturnPackage', 'user.OrderReturn/addOrderReturn');//提交退款信息  
    Route::get('cancelService/:id', 'user.OrderReturn/cancelServiceList');//取消退换货  
});  

});

收起阅读 »

html实体空格无效

js

空格分为 两种: 普通文本空格和html实体空格

普通文本空格 unicode 正则 描述
全角-中文空格键 \u3000 中文网页上常会出现

半角-英文空格键 \u0020 \s 最常见,写代码

html实体空格 unicode 十六进制 描述
&nbsp; \u00a0 \xa0
1个字符宽度
不间断空格,主要用在office中,让一个单词在结尾处不会换行显示

&ensp; \u2002
1个字符宽度
半角空格

&emsp; \u2003
2个字符宽度
全角空格

\u2009 小于1个字符宽度
&nbsp;

3.其他制表符
\n 回车(\u000a)
\t 水平制表符(\u0009)
\s 空格(\u0008)
\r 换行(\u000d)

解决方案1
将html空格替换成想要的unicode码

解决方案2 vue 的 v-html
<div v-html="'&nbsp;&nbsp;'"></div>

关于在线文档编辑时,文字对齐的有效操作:在全角的情况下点击空格键,对应的实体字符是&emsp;

继续阅读 »

空格分为 两种: 普通文本空格和html实体空格

普通文本空格 unicode 正则 描述
全角-中文空格键 \u3000 中文网页上常会出现

半角-英文空格键 \u0020 \s 最常见,写代码

html实体空格 unicode 十六进制 描述
&nbsp; \u00a0 \xa0
1个字符宽度
不间断空格,主要用在office中,让一个单词在结尾处不会换行显示

&ensp; \u2002
1个字符宽度
半角空格

&emsp; \u2003
2个字符宽度
全角空格

\u2009 小于1个字符宽度
&nbsp;

3.其他制表符
\n 回车(\u000a)
\t 水平制表符(\u0009)
\s 空格(\u0008)
\r 换行(\u000d)

解决方案1
将html空格替换成想要的unicode码

解决方案2 vue 的 v-html
<div v-html="'&nbsp;&nbsp;'"></div>

关于在线文档编辑时,文字对齐的有效操作:在全角的情况下点击空格键,对应的实体字符是&emsp;

收起阅读 »