HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

如何解决在uni-app使用mpvue-echarts报this.echarts.setCanvasCreator is not a function的错误

mpvue 小程序 uniapp

注意::此方法本人仅用在微信小程序,其他端没有测试,h5测试用不了

  1. 安装依赖
    npm install echarts mpvue-echarts

  2. 找到mpvue-echarts模块里的echarts文件,找到相应代码覆盖即可,替换如下

    <template>  
    <canvas  
    v-if="canvasId"  
    class="ec-canvas"  
    :id="canvasId"  
    :canvasId="canvasId"  
    @touchstart="touchStart"  
    @touchmove="touchMove"  
    @touchend="touchEnd"  
    @error="error"  
    ></canvas>  
    </template>  
    <script>  
    import WxCanvas from "./wx-canvas";  
    export default {  
    props: {  
    canvasId: {  
      type: String,  
      default: "ec-canvas"  
    },  
    lazyLoad: {  
      type: Boolean,  
      default: false  
    },  
    disableTouch: {  
      type: Boolean,  
      default: false  
    },  
    throttleTouch: {  
      type: Boolean,  
      default: false  
    }  
    },  
    // #ifdef H5  
    mounted() {  
    if (!this.lazyLoad) this.init();  
    },  
    // #endif  
    // #ifndef H5  
    onReady() {  
    if (!this.lazyLoad) this.init();  
    },  
    // #endif  
    methods: {  
    setChart(chart) {  
      this.chart = chart;  
    },  
    init() {  
      const { canvasId } = this;  
      this.ctx = wx.createCanvasContext(canvasId, this);  
      this.canvas = new WxCanvas(this.ctx, canvasId);  
      const query = wx.createSelectorQuery().in(this);  
      query  
        .select(`#${canvasId}`)  
        .boundingClientRect(res => {  
          if (!res) {  
            setTimeout(() => this.init(), 50);  
            return;  
          }  
          this.$emit("onInit", {  
            width: res.width,  
            height: res.height  
          });  
        })  
        .exec();  
    },  
    canvasToTempFilePath(opt) {  
      const { canvasId } = this;  
      this.ctx.draw(true, () => {  
        wx.canvasToTempFilePath({  
          canvasId,  
          ...opt  
        });  
      });  
    },  
    touchStart(e) {  
      const { disableTouch, chart } = this;  
      if (disableTouch || !chart || !e.mp.touches.length) return;  
      const touch = e.mp.touches[0];  
      chart._zr.handler.dispatch("mousedown", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
      chart._zr.handler.dispatch("mousemove", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
    },  
    touchMove(e) {  
      const { disableTouch, throttleTouch, chart, lastMoveTime } = this;  
      if (disableTouch || !chart || !e.mp.touches.length) return;  
      if (throttleTouch) {  
        const currMoveTime = Date.now();  
        if (currMoveTime - lastMoveTime < 240) return;  
        this.lastMoveTime = currMoveTime;  
      }  
      const touch = e.mp.touches[0];  
      chart._zr.handler.dispatch("mousemove", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
    },  
    touchEnd(e) {  
      const { disableTouch, chart } = this;  
      if (disableTouch || !chart) return;  
      const touch = e.mp.changedTouches ? e.mp.changedTouches[0] : {};  
      chart._zr.handler.dispatch("mouseup", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
      chart._zr.handler.dispatch("click", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
    }  
    }  
    };  
    </script>  
    <style scoped>  
    .ec-canvas {  
    width: 100%;  
    height: 100%;  
    flex: 1;  
    }  
    </style>

    3.图表示例文件

    <template>  
    <view class="materials">  
    <view class="echarts-wrap">  
      <mpvue-echarts class="ec-canvas" @onInit="onInit" canvasId="demo-canvas" ref="chart1" />  
    </view>  
    <button @click="changeChart">更改</button>  
    </view>  
    </template>  
    
    <script>  
    import * as echarts from "echarts/dist/echarts.min";  
    import mpvueEcharts from "mpvue-echarts";  
    function getDate(date) {  
    return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;  
    }  
    let chart1 = null;  
    export default {  
    components: {  
    mpvueEcharts  
    },  
    data() {  
    return {};  
    },  
    onReady() {},  
    methods: {  
    changeChart() {  
      chart1.setOption(this.getOptions(10, 30));  
    },  
    getOptions(nan, nv) {  
      return {  
        title: {  
          text: "性别比例",  
          x: "center",  
          textStyle: {  
            fontSize: 16  
          }  
        },  
        backgroundColor: "#FFF",  
        tooltip: {  
          trigger: "axis",  
          axisPointer: {  
            // 坐标轴指示器,坐标轴触发有效  
            type: "shadow" // 默认为直线,可选为:'line' | 'shadow'  
          }  
        },  
        xAxis: { type: "value", splitNumber: 7 },  
        yAxis: { type: "category", show: false, data: [getDate(new Date())] },  
        series: [  
          {  
            name: "男",  
            type: "bar",  
            stack: "总量",  
            data: [nan],  
            barWidth: 50,  
            itemStyle: { normal: { color: "#00aaff" } }  
          },  
          {  
            name: "女",  
            type: "bar",  
            stack: "总量",  
            data: [nv],  
            itemStyle: { normal: { color: "#f4516c" } }  
          }  
        ]  
      };  
    },  
    onInit(e) {  
      let { width, height } = e;  
      let canvas = this.$refs.chart1.canvas;  
      echarts.setCanvasCreator(() => canvas);  
      chart1 = echarts.init(canvas, null, {  
        width: width,  
        height: height  
      });  
      canvas.setChart(chart1);  
      chart1.setOption(this.getOptions(50, 10));  
      this.$refs.chart1.setChart(chart1);  
    }  
    }  
    };  
    </script>  
    <style>  
    .echarts-wrap {  
    width: 100%;  
    height: 300px;  
    }  
    </style>
继续阅读 »

注意::此方法本人仅用在微信小程序,其他端没有测试,h5测试用不了

  1. 安装依赖
    npm install echarts mpvue-echarts

  2. 找到mpvue-echarts模块里的echarts文件,找到相应代码覆盖即可,替换如下

    <template>  
    <canvas  
    v-if="canvasId"  
    class="ec-canvas"  
    :id="canvasId"  
    :canvasId="canvasId"  
    @touchstart="touchStart"  
    @touchmove="touchMove"  
    @touchend="touchEnd"  
    @error="error"  
    ></canvas>  
    </template>  
    <script>  
    import WxCanvas from "./wx-canvas";  
    export default {  
    props: {  
    canvasId: {  
      type: String,  
      default: "ec-canvas"  
    },  
    lazyLoad: {  
      type: Boolean,  
      default: false  
    },  
    disableTouch: {  
      type: Boolean,  
      default: false  
    },  
    throttleTouch: {  
      type: Boolean,  
      default: false  
    }  
    },  
    // #ifdef H5  
    mounted() {  
    if (!this.lazyLoad) this.init();  
    },  
    // #endif  
    // #ifndef H5  
    onReady() {  
    if (!this.lazyLoad) this.init();  
    },  
    // #endif  
    methods: {  
    setChart(chart) {  
      this.chart = chart;  
    },  
    init() {  
      const { canvasId } = this;  
      this.ctx = wx.createCanvasContext(canvasId, this);  
      this.canvas = new WxCanvas(this.ctx, canvasId);  
      const query = wx.createSelectorQuery().in(this);  
      query  
        .select(`#${canvasId}`)  
        .boundingClientRect(res => {  
          if (!res) {  
            setTimeout(() => this.init(), 50);  
            return;  
          }  
          this.$emit("onInit", {  
            width: res.width,  
            height: res.height  
          });  
        })  
        .exec();  
    },  
    canvasToTempFilePath(opt) {  
      const { canvasId } = this;  
      this.ctx.draw(true, () => {  
        wx.canvasToTempFilePath({  
          canvasId,  
          ...opt  
        });  
      });  
    },  
    touchStart(e) {  
      const { disableTouch, chart } = this;  
      if (disableTouch || !chart || !e.mp.touches.length) return;  
      const touch = e.mp.touches[0];  
      chart._zr.handler.dispatch("mousedown", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
      chart._zr.handler.dispatch("mousemove", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
    },  
    touchMove(e) {  
      const { disableTouch, throttleTouch, chart, lastMoveTime } = this;  
      if (disableTouch || !chart || !e.mp.touches.length) return;  
      if (throttleTouch) {  
        const currMoveTime = Date.now();  
        if (currMoveTime - lastMoveTime < 240) return;  
        this.lastMoveTime = currMoveTime;  
      }  
      const touch = e.mp.touches[0];  
      chart._zr.handler.dispatch("mousemove", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
    },  
    touchEnd(e) {  
      const { disableTouch, chart } = this;  
      if (disableTouch || !chart) return;  
      const touch = e.mp.changedTouches ? e.mp.changedTouches[0] : {};  
      chart._zr.handler.dispatch("mouseup", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
      chart._zr.handler.dispatch("click", {  
        zrX: touch.x,  
        zrY: touch.y  
      });  
    }  
    }  
    };  
    </script>  
    <style scoped>  
    .ec-canvas {  
    width: 100%;  
    height: 100%;  
    flex: 1;  
    }  
    </style>

    3.图表示例文件

    <template>  
    <view class="materials">  
    <view class="echarts-wrap">  
      <mpvue-echarts class="ec-canvas" @onInit="onInit" canvasId="demo-canvas" ref="chart1" />  
    </view>  
    <button @click="changeChart">更改</button>  
    </view>  
    </template>  
    
    <script>  
    import * as echarts from "echarts/dist/echarts.min";  
    import mpvueEcharts from "mpvue-echarts";  
    function getDate(date) {  
    return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;  
    }  
    let chart1 = null;  
    export default {  
    components: {  
    mpvueEcharts  
    },  
    data() {  
    return {};  
    },  
    onReady() {},  
    methods: {  
    changeChart() {  
      chart1.setOption(this.getOptions(10, 30));  
    },  
    getOptions(nan, nv) {  
      return {  
        title: {  
          text: "性别比例",  
          x: "center",  
          textStyle: {  
            fontSize: 16  
          }  
        },  
        backgroundColor: "#FFF",  
        tooltip: {  
          trigger: "axis",  
          axisPointer: {  
            // 坐标轴指示器,坐标轴触发有效  
            type: "shadow" // 默认为直线,可选为:'line' | 'shadow'  
          }  
        },  
        xAxis: { type: "value", splitNumber: 7 },  
        yAxis: { type: "category", show: false, data: [getDate(new Date())] },  
        series: [  
          {  
            name: "男",  
            type: "bar",  
            stack: "总量",  
            data: [nan],  
            barWidth: 50,  
            itemStyle: { normal: { color: "#00aaff" } }  
          },  
          {  
            name: "女",  
            type: "bar",  
            stack: "总量",  
            data: [nv],  
            itemStyle: { normal: { color: "#f4516c" } }  
          }  
        ]  
      };  
    },  
    onInit(e) {  
      let { width, height } = e;  
      let canvas = this.$refs.chart1.canvas;  
      echarts.setCanvasCreator(() => canvas);  
      chart1 = echarts.init(canvas, null, {  
        width: width,  
        height: height  
      });  
      canvas.setChart(chart1);  
      chart1.setOption(this.getOptions(50, 10));  
      this.$refs.chart1.setChart(chart1);  
    }  
    }  
    };  
    </script>  
    <style>  
    .echarts-wrap {  
    width: 100%;  
    height: 300px;  
    }  
    </style>
收起阅读 »

在 uni-app 内解析 xml

xml

使用npm安装xmldom

初始化npm工程(如果已经初始化,请跳过此步骤)

npm init

安装依赖

npm i xmldom

解析xml

var DOMParser = require('xmldom').DOMParser;  
var doc = new DOMParser().parseFromString(  
    '<xml xmlns="a" xmlns:c="./lite">\n'+  
        '\t<child>test</child>\n'+  
        '\t<child></child>\n'+  
        '\t<child/>\n'+  
    '</xml>'  
    ,'text/xml');  
doc.documentElement.setAttribute('x','y');  
doc.documentElement.setAttributeNS('./lite','c:x','y2');  
var nsAttr = doc.documentElement.getAttributeNS('./lite','x')  
console.info(nsAttr)  
console.info(doc)

详细文档:xmldom
示例工程:解析xml天气信息

继续阅读 »

使用npm安装xmldom

初始化npm工程(如果已经初始化,请跳过此步骤)

npm init

安装依赖

npm i xmldom

解析xml

var DOMParser = require('xmldom').DOMParser;  
var doc = new DOMParser().parseFromString(  
    '<xml xmlns="a" xmlns:c="./lite">\n'+  
        '\t<child>test</child>\n'+  
        '\t<child></child>\n'+  
        '\t<child/>\n'+  
    '</xml>'  
    ,'text/xml');  
doc.documentElement.setAttribute('x','y');  
doc.documentElement.setAttributeNS('./lite','c:x','y2');  
var nsAttr = doc.documentElement.getAttributeNS('./lite','x')  
console.info(nsAttr)  
console.info(doc)

详细文档:xmldom
示例工程:解析xml天气信息

收起阅读 »

当 uni-app 遇见 vscode

vscode

uni-app 是一个用 vue 语法来开发小程序、App、H5 的框架,官方推荐的开发工具为 HBuilderX,使用起来有很好的开发体验。

不过,由于 HBuilderX 没有 Linux 版以及很多前端之前已经习惯了 vscode,不想更换编辑器。直接使用 vscode 开发 uni-app,其体验并不是很好。

其实 uni-app 和 vscode 也可以很搭,接下来为大伙带来 vscode 中 uni-app 的正确打开姿势。

CLI 工程

全局安装 vue-cli 3.x(如已安装请跳过此步骤)

npm install -g @vue/cli

通过 CLI 创建 uni-app 项目

vue create -p dcloudio/uni-preset-vue my-project

此时,会提示选择项目模板,初次体验建议选择 hello uni-app 项目模板,如下所示:

在vscode中打开项目

安装vue语法提示插件vetur

CLI 工程默认带了uni-app语法提示和5+App语法提示

安装组件语法提示

组件语法提示是uni-app的亮点,其他框架很少能提供。

npm i @dcloudio/uni-helper-json

导入 HBuilderX 自带的代码块

从 github 下载 uni-app 代码块,放到项目目录下的 .vscode 目录即可拥有和 HBuilderX 一样的代码块。

运行项目

npm run dev:%PLATFORM%

发布项目

npm run build:%PLATFORM%

%PLATFORM% 可取值如下:

平台
h5 H5
mp-alipay 支付宝小程序
mp-baidu 百度小程序
mp-weixin 微信小程序
mp-toutiao 头条小程序
mp-qq qq 小程序

CLI 方式参考文档

HBuilderX 工程

HBuilderX 创建的工程默认不带 types 语法提示,在 vscode 中编辑的时候,可以自行安装

初始化npm(如已初始化跳过此步骤)

npm init -y

安装 uni-app 语法提示

npm i @types/uni-app @types/html5plus -D

另外,uni-app 项目下的 manifest.json、pages.json 等文件可以包含注释。vscode 里需要改用 jsonc 编辑器打开。

关于uni-app插件市场的插件,vscode一样可以使用。

  • 如果这些插件有npm使用方式(如uni-ui),可以用npm
  • 如果作者没提供npm方式,那么下载zip包解压也是可以用的
继续阅读 »

uni-app 是一个用 vue 语法来开发小程序、App、H5 的框架,官方推荐的开发工具为 HBuilderX,使用起来有很好的开发体验。

不过,由于 HBuilderX 没有 Linux 版以及很多前端之前已经习惯了 vscode,不想更换编辑器。直接使用 vscode 开发 uni-app,其体验并不是很好。

其实 uni-app 和 vscode 也可以很搭,接下来为大伙带来 vscode 中 uni-app 的正确打开姿势。

CLI 工程

全局安装 vue-cli 3.x(如已安装请跳过此步骤)

npm install -g @vue/cli

通过 CLI 创建 uni-app 项目

vue create -p dcloudio/uni-preset-vue my-project

此时,会提示选择项目模板,初次体验建议选择 hello uni-app 项目模板,如下所示:

在vscode中打开项目

安装vue语法提示插件vetur

CLI 工程默认带了uni-app语法提示和5+App语法提示

安装组件语法提示

组件语法提示是uni-app的亮点,其他框架很少能提供。

npm i @dcloudio/uni-helper-json

导入 HBuilderX 自带的代码块

从 github 下载 uni-app 代码块,放到项目目录下的 .vscode 目录即可拥有和 HBuilderX 一样的代码块。

运行项目

npm run dev:%PLATFORM%

发布项目

npm run build:%PLATFORM%

%PLATFORM% 可取值如下:

平台
h5 H5
mp-alipay 支付宝小程序
mp-baidu 百度小程序
mp-weixin 微信小程序
mp-toutiao 头条小程序
mp-qq qq 小程序

CLI 方式参考文档

HBuilderX 工程

HBuilderX 创建的工程默认不带 types 语法提示,在 vscode 中编辑的时候,可以自行安装

初始化npm(如已初始化跳过此步骤)

npm init -y

安装 uni-app 语法提示

npm i @types/uni-app @types/html5plus -D

另外,uni-app 项目下的 manifest.json、pages.json 等文件可以包含注释。vscode 里需要改用 jsonc 编辑器打开。

关于uni-app插件市场的插件,vscode一样可以使用。

  • 如果这些插件有npm使用方式(如uni-ui),可以用npm
  • 如果作者没提供npm方式,那么下载zip包解压也是可以用的
收起阅读 »

iOS证书类型及功能介绍

iOS打包

很多刚开始接触iOS证书的开发者可能不是很了解iOS证书的类型功能和概念。

下面对iOS证书的几个方面进行介绍。

iOS证书的类型和作用

1、iOS开发证书

iOS开发证书是用于测试APP,在开发过程中安装到苹果手机真机测试APP的运行情况。

2、iOS发布证书

当APP开发测试好后上线就需要用到iOS发布证书,用iOS发布证书打包的ipa才能上传到App Store审核。

3、iOS推送证书

iOS推送证书是用于推送通知的,平时我们在手机的系统栏下拉看到的那些消息就是推送通知,如果要做这个功能就需要配置推送证书。

4、iOS企业证书

iOS企业证书需要企业开发者账号才能申请,用于无法上架App Store的苹果APP打包签名使用。

常用的就是以上这4种iOS证书,当你需要什么功能的时候就知道用哪个类型的iOS证书。

iOS证书的构成和有效性

iOS证书由两个文件构成。p12文件和.mobileprovision文件

p12文件相当于公钥,.mobileprovision文件相当于私钥。

开发证书p12文件苹果规定只能申请2个,发布证书p12文件只能申请3个!iOS证书配置.mobileprovision文件是没有任何数量限制的!

p12可以共用,一个p12可以对应无数个.mobileprovision文件!所以就算p12申请一个也够用了,可以对应创建无数套iOS证书!

所以对于证书数量的担忧是没有意义的!不会限制你上架多少个APP!

ios证书的有效期是一年,在我们软件的和开发者后台都能看到到期时间。

iOS证书可以删除吗,到期怎么更新

iOS证书是可以随意删除的(不管有没有到期),不会影响已经上架的APP。

如果你还在测试APP。删除了测试用的开发证书,APP将打不开,当然这个也没什么关系,测试APP是要不停打包的,重新申请证书打包重新安装就可以了。

iOS证书到期了需不需要重新申请呢,具体看需要,如果你要更新已经上架的APP,更新版本,删除到期的证书重新申请打包上传更新。

如果不更新APP,到期了不管他也没什么关系,不会影响上架了的APP。

iOS证书的相关申请请看详细的测试与上架教程!

ios app真机测试到上架App Store详细教程

继续阅读 »

很多刚开始接触iOS证书的开发者可能不是很了解iOS证书的类型功能和概念。

下面对iOS证书的几个方面进行介绍。

iOS证书的类型和作用

1、iOS开发证书

iOS开发证书是用于测试APP,在开发过程中安装到苹果手机真机测试APP的运行情况。

2、iOS发布证书

当APP开发测试好后上线就需要用到iOS发布证书,用iOS发布证书打包的ipa才能上传到App Store审核。

3、iOS推送证书

iOS推送证书是用于推送通知的,平时我们在手机的系统栏下拉看到的那些消息就是推送通知,如果要做这个功能就需要配置推送证书。

4、iOS企业证书

iOS企业证书需要企业开发者账号才能申请,用于无法上架App Store的苹果APP打包签名使用。

常用的就是以上这4种iOS证书,当你需要什么功能的时候就知道用哪个类型的iOS证书。

iOS证书的构成和有效性

iOS证书由两个文件构成。p12文件和.mobileprovision文件

p12文件相当于公钥,.mobileprovision文件相当于私钥。

开发证书p12文件苹果规定只能申请2个,发布证书p12文件只能申请3个!iOS证书配置.mobileprovision文件是没有任何数量限制的!

p12可以共用,一个p12可以对应无数个.mobileprovision文件!所以就算p12申请一个也够用了,可以对应创建无数套iOS证书!

所以对于证书数量的担忧是没有意义的!不会限制你上架多少个APP!

ios证书的有效期是一年,在我们软件的和开发者后台都能看到到期时间。

iOS证书可以删除吗,到期怎么更新

iOS证书是可以随意删除的(不管有没有到期),不会影响已经上架的APP。

如果你还在测试APP。删除了测试用的开发证书,APP将打不开,当然这个也没什么关系,测试APP是要不停打包的,重新申请证书打包重新安装就可以了。

iOS证书到期了需不需要重新申请呢,具体看需要,如果你要更新已经上架的APP,更新版本,删除到期的证书重新申请打包上传更新。

如果不更新APP,到期了不管他也没什么关系,不会影响上架了的APP。

iOS证书的相关申请请看详细的测试与上架教程!

ios app真机测试到上架App Store详细教程

收起阅读 »

UNI-APP 开发微信公众号(H5)JSSDK 分享到微信中详解

微信JSSDK

首先声明本人纯小白,各demo基本网站上找来测试了两三天拼凑出来的,做不了技术解答哈望见谅,分享出来只是希望能够帮到像我一样的小白
1、安装js-sdk
可参考官方提供的 https://ask.dcloud.net.cn/article/35380
有两种方法:
①NPM安装

npm install jweixin-module --save

②直接下载引用(下载链接:https://unpkg.com/jweixin-module@1.4.1/out/index.js)
两种方式的差别,我感觉主要是npm安装的方式引用就是直接引用包名,下载引用需要引用详细地址,也不知道这种理解正确不。

var jweixin = require('jweixin-module')
var jweixin = require('jweixin-module/index.js')

2、在需要转发的页面onshow()里添加分享代码

// #ifdef H5  
            var jweixin = require('jweixin-module') //npm安装的引用  
            var surl = encodeURIComponent(window.location.href.split('#')[0]); //据说可以解决URL中带参数的问题,前台用的js编码,后台php解码  
            uni.request({  
                url: '/jssdk/gettokens.php',  
                method: 'POST',  
                header: {  
                    'content-type': 'application/x-www-form-urlencoded'  //post一定要带这个header,被这里坑了半天  
                },  
                data: {  
                    url: surl  
                },  
                success: res => {  
                    jweixin.config({  
                        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。  
                        appId: res.data.appId, // 必填,公众号的唯一标识  
                        timestamp: res.data.timestamp, // 必填,生成签名的时间戳  
                        nonceStr: res.data.nonceStr, // 必填,生成签名的随机串  
                        signature: res.data.signature, // 必填,签名,见附录1  
                        surl: res.data.surl, //自己添加的,debug为true的时候可以网页打印出对应的URL是否正确  
                        jsApiList: ["updateAppMessageShareData", "updateTimelineShareData"]  
                    });  

                    jweixin.ready(function() {  
                        //自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)  
                        jweixin.updateAppMessageShareData({  
                            title: '标题', // 分享标题  
                            desc: "描述内容", // 分享描述  
                            link: "链接", // 分享链接  
                            imgUrl: '显示图片', // 分享图标                                
                            success: function() {  
                                // 用户确认分享后执行的回调函数  
                            },  
                            cancel: function() {  
                                // 用户取消分享后执行的回调函数  
                            }  
                        });  
                        //自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容(1.4.0)  
                        jweixin.updateTimelineShareData({  
                            title: '标题', // 分享标题                                  
                            link: '链接', // 分享链接  
                            imgUrl: '显示图片', // 分享图标                                
                            success: function() {  
                                // 用户确认分享后执行的回调函数  
                            },  
                            cancel: function() {  
                                // 用户取消分享后执行的回调函数  
                            }  
                        });  

                    });  
                },  
                fail: () => {  
                    console.log('request fail', err);  
                },  
                complete: () => {}  
            });  

// #endif  

3、gettokens.php文件,PHP后台api接口,用于前台request请求,返回wx.config部分。在此文件中修改成自己的appid和appsecre。可以在PC端直接访问此接口,到下面的签名校验工具中先校验签名文件是否正常
4、access_token.php 缓存token
5、jsapi_ticket.php 缓存ticket,可以从此文件中获取jsapi_ticket到微信 JS 接口签名校验工具中校验签名是否正确(https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign)
6、jssdk_wx.php 后台签名验证文件,此4个文件可从下面附件下载

继续阅读 »

首先声明本人纯小白,各demo基本网站上找来测试了两三天拼凑出来的,做不了技术解答哈望见谅,分享出来只是希望能够帮到像我一样的小白
1、安装js-sdk
可参考官方提供的 https://ask.dcloud.net.cn/article/35380
有两种方法:
①NPM安装

npm install jweixin-module --save

②直接下载引用(下载链接:https://unpkg.com/jweixin-module@1.4.1/out/index.js)
两种方式的差别,我感觉主要是npm安装的方式引用就是直接引用包名,下载引用需要引用详细地址,也不知道这种理解正确不。

var jweixin = require('jweixin-module')
var jweixin = require('jweixin-module/index.js')

2、在需要转发的页面onshow()里添加分享代码

// #ifdef H5  
            var jweixin = require('jweixin-module') //npm安装的引用  
            var surl = encodeURIComponent(window.location.href.split('#')[0]); //据说可以解决URL中带参数的问题,前台用的js编码,后台php解码  
            uni.request({  
                url: '/jssdk/gettokens.php',  
                method: 'POST',  
                header: {  
                    'content-type': 'application/x-www-form-urlencoded'  //post一定要带这个header,被这里坑了半天  
                },  
                data: {  
                    url: surl  
                },  
                success: res => {  
                    jweixin.config({  
                        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。  
                        appId: res.data.appId, // 必填,公众号的唯一标识  
                        timestamp: res.data.timestamp, // 必填,生成签名的时间戳  
                        nonceStr: res.data.nonceStr, // 必填,生成签名的随机串  
                        signature: res.data.signature, // 必填,签名,见附录1  
                        surl: res.data.surl, //自己添加的,debug为true的时候可以网页打印出对应的URL是否正确  
                        jsApiList: ["updateAppMessageShareData", "updateTimelineShareData"]  
                    });  

                    jweixin.ready(function() {  
                        //自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)  
                        jweixin.updateAppMessageShareData({  
                            title: '标题', // 分享标题  
                            desc: "描述内容", // 分享描述  
                            link: "链接", // 分享链接  
                            imgUrl: '显示图片', // 分享图标                                
                            success: function() {  
                                // 用户确认分享后执行的回调函数  
                            },  
                            cancel: function() {  
                                // 用户取消分享后执行的回调函数  
                            }  
                        });  
                        //自定义“分享到朋友圈”及“分享到QQ空间”按钮的分享内容(1.4.0)  
                        jweixin.updateTimelineShareData({  
                            title: '标题', // 分享标题                                  
                            link: '链接', // 分享链接  
                            imgUrl: '显示图片', // 分享图标                                
                            success: function() {  
                                // 用户确认分享后执行的回调函数  
                            },  
                            cancel: function() {  
                                // 用户取消分享后执行的回调函数  
                            }  
                        });  

                    });  
                },  
                fail: () => {  
                    console.log('request fail', err);  
                },  
                complete: () => {}  
            });  

// #endif  

3、gettokens.php文件,PHP后台api接口,用于前台request请求,返回wx.config部分。在此文件中修改成自己的appid和appsecre。可以在PC端直接访问此接口,到下面的签名校验工具中先校验签名文件是否正常
4、access_token.php 缓存token
5、jsapi_ticket.php 缓存ticket,可以从此文件中获取jsapi_ticket到微信 JS 接口签名校验工具中校验签名是否正确(https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign)
6、jssdk_wx.php 后台签名验证文件,此4个文件可从下面附件下载

收起阅读 »

创建新的Webview之后无法直接使用close关闭窗口的问题

为了理解方便我写了个例子,就是在初始页面打开新的页面然后再将新的页面关闭:

index.html  

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title></title>  
    <script src="js/mui.min.js"></script>  
    <link href="css/mui.min.css" rel="stylesheet"/>  
    <script type="text/javascript" charset="utf-8">  
        mui.init();  
    </script>  
</head>  
<body>  
    测试一  
    <script src="html5plus://ready"></script>  
    <!-- 要使用窗口执行,需要加载plus文件 -->  
    <script type="text/javascript">  
        mui.openWindow({  
            url: "test.html",  
            id: "test.html",  
            show:{  
                autoShow: true,  
                aniShow: "none",  
                duration: 0  
            }  
        });  
    </script>  
</body>  
</html>
<!doctype html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <title></title>  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <link href="css/mui.css" rel="stylesheet" />  
    </head>  

    <body>  
        测试二  
        <script src="js/mui.js"></script>  
        <script type="text/javascript">  
            mui.init()  
        </script>  
        <script src="html5plus://ready"></script>  
        <script type="text/javascript">  
            var currentWebview = plus.webview.currentWebview();  
            currentWebview.close();   //问题出现  
        </script>  
    </body>  

</html>

真机测试:安卓6.0, 索尼SOV32
如果直接用close关闭,页面虽然关闭了,但是程序就一直处于忙碌状态(屏幕中间有图标一直在转圈圈)。
后来问题的解决是用window的延迟方法把关闭延迟了几秒,就正常的close了,没有圈圈在屏幕上打转。

疑问:为什么要延迟才会解决这个问题?

继续阅读 »

为了理解方便我写了个例子,就是在初始页面打开新的页面然后再将新的页面关闭:

index.html  

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title></title>  
    <script src="js/mui.min.js"></script>  
    <link href="css/mui.min.css" rel="stylesheet"/>  
    <script type="text/javascript" charset="utf-8">  
        mui.init();  
    </script>  
</head>  
<body>  
    测试一  
    <script src="html5plus://ready"></script>  
    <!-- 要使用窗口执行,需要加载plus文件 -->  
    <script type="text/javascript">  
        mui.openWindow({  
            url: "test.html",  
            id: "test.html",  
            show:{  
                autoShow: true,  
                aniShow: "none",  
                duration: 0  
            }  
        });  
    </script>  
</body>  
</html>
<!doctype html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <title></title>  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <link href="css/mui.css" rel="stylesheet" />  
    </head>  

    <body>  
        测试二  
        <script src="js/mui.js"></script>  
        <script type="text/javascript">  
            mui.init()  
        </script>  
        <script src="html5plus://ready"></script>  
        <script type="text/javascript">  
            var currentWebview = plus.webview.currentWebview();  
            currentWebview.close();   //问题出现  
        </script>  
    </body>  

</html>

真机测试:安卓6.0, 索尼SOV32
如果直接用close关闭,页面虽然关闭了,但是程序就一直处于忙碌状态(屏幕中间有图标一直在转圈圈)。
后来问题的解决是用window的延迟方法把关闭延迟了几秒,就正常的close了,没有圈圈在屏幕上打转。

疑问:为什么要延迟才会解决这个问题?

收起阅读 »

最新ios提审通过经验分享

苹果 审核 iOS
  1. ios打包上架
    https://zhuanlan.zhihu.com/p/66575901
  2. 审核流程
    https://blog.csdn.net/pcf1995/article/details/79650345
    应用版本名称和应用版本号对应起来,比如1.0.1对应101
  3. 审核被拒重新提审流程
    https://www.cnblogs.com/appuploader/p/7993011.html

踩坑记录:

  1. 半个月前申请的苹果开发者发布证书,在uniapp云打包的时候报错
    解决:重新生成一个发布证书
  2. ipa包通过application loader上传报错,找不到bound Id
    解决:需要先到苹果开发者-app store管理-我的app里面创建一个该bound id的应用
  3. Application loader上传时报错图片有问题
    解决:提供的app store图标需要是png图片,且必须直角,不透明(即没有alpha,可以使用mac预览里面的导出功能,在导出的时候去掉alpha的勾即可)
  4. 拒审原因:5.1.1,使用手机设备应用时未表明用途
    解决:在manifest里面配置说明文案
继续阅读 »
  1. ios打包上架
    https://zhuanlan.zhihu.com/p/66575901
  2. 审核流程
    https://blog.csdn.net/pcf1995/article/details/79650345
    应用版本名称和应用版本号对应起来,比如1.0.1对应101
  3. 审核被拒重新提审流程
    https://www.cnblogs.com/appuploader/p/7993011.html

踩坑记录:

  1. 半个月前申请的苹果开发者发布证书,在uniapp云打包的时候报错
    解决:重新生成一个发布证书
  2. ipa包通过application loader上传报错,找不到bound Id
    解决:需要先到苹果开发者-app store管理-我的app里面创建一个该bound id的应用
  3. Application loader上传时报错图片有问题
    解决:提供的app store图标需要是png图片,且必须直角,不透明(即没有alpha,可以使用mac预览里面的导出功能,在导出的时候去掉alpha的勾即可)
  4. 拒审原因:5.1.1,使用手机设备应用时未表明用途
    解决:在manifest里面配置说明文案
收起阅读 »

XHbuilder 还不错哈

HBuilderX

本人轻度应用,使用three.js,有语法提示,挺快的。向我一样的可以用用这个

本人轻度应用,使用three.js,有语法提示,挺快的。向我一样的可以用用这个

uniapp 自定义 iOS 端推送提示音

Push 推送提示音 推送

iOS端自定义推送铃声,详情请看 自定义推送铃声

iOS端自定义推送铃声,详情请看 自定义推送铃声

uni-app 2.2 大幅优化H5端性能体验,只开发H5,也要用uni-app

性能 摇树优化 tree_shaking 优化

背景

uni-app发布以来,已经服务了几十万开发者。让我们意外,或者说惊喜的是,有大量开发者用uni-app只编写H5版,并没有多端发布(可参考案例)。

这其实也符合uni-app的初衷,uni-app的定位并不是需要多端发布时才用uni-appuni-app是一个使用vue.js开发所有前端应用的统一框架。对于一个前端工程师来说,uni-app在手,啥需求都不愁。

过去的版本迭代中,uni-app已经成为了更好的小程序开发框架,比使用原生微信开发更有优势。(见评测

uni-app2.2的新版中,我们大幅优化了H5版的性能,让使用uni-app开发的H5,性能体验和直接使用vue.js开发H5拉齐。

可能不少开发者有某种误解:多端框架要适配多端,所以性能肯定不如原生。我们想纠正一下:

  1. 切忌想当然,多看数据评测。还不信就自己动手实验
  2. 请问使用vue.js开发的web性能好,还是使用原生js开发web性能好?答案是:使用vue.js框架。为什么?因为它在底层会自动优化数据同步、虚拟dom,比大多数开发手动写的代码要更高效。同样的,使用uni-app也如此,框架底层的优化处理比大多数开发者手动写setdata或dom操作更高效。
  3. 多端适配很多是在编译时做的,并不影响运行时的性能

优化难点

想优化H5端的性能,并不是一件容易的事。

“功能全面”和“小巧极速”,这是一对最难调和的冤家。

为了保障多端的一致性,uni-app实现了一套小程序的H5版Runtime,支持各种小程序的组件、API、配置。所以uni-app的H5版拥有比其他框架更好的跨端一致性。

但这也造成了老版的uni-app,输出H5端时,包体积过大(框架未压缩前有500k,部署gzip后162k)。

这确实是一个非常大的runtime,包含了几十个内置组件,数百个API。而且这些API仍然在快速增加中。

不能像其他框架一样因为功能少,所以体积小。我们不会用功能换性能,我们需要更好的方案。

优化方法

uni-app包含几十个内置组件、数百个API,是个“大而全”的框架;但开发者在开发具体应用时,未必能使用到所有的组件及API。若能根据项目具体情况,删掉没用到的组件及API,保留对项目有用的组件及API,便可精简框架、减少体积,这即是“摇树优化”的思路。

摇树优化(Tree-Shaking),顾名思义,摇晃树干,将枯死无用的枝条摇掉,仅保留有用的树枝。对应到框架层面理解,就是一个框架的众多组件和API,可以按需使用,把未引用的框架部分裁剪掉。Tree-Shaking 最早由 Rollup 提出,属于死码删除的一种形式。

常见的前端框架摇树,一般是基于明确的import引用关系。比如引用某UI库时,在A页面使用该UI库的search组件,此时需要写js代码import这个search组件,那么摇树分析就很容易。

uni-app和小程序一样,内置组件和API是不需要import的,这提升了开发的易用性,但此时却加大了摇树的难度,依靠简单的import分析无法实现摇树了。

幸好对DCloud团队而言,AST语法分析是看家本事,多年来HBuilder以js和vue语法提示著称。通过AST分析,uni-app新版可以精准判定这个项目使用了哪些组件和API。

不过这还不够,分析工程源码使用了什么组件和API之后,还得考虑框架各组件和API之间可能存在依赖和耦合关系,这需要进一步的计算和关系梳理,具体而言:

  • 组件:通过vue-template-compiler分析出来的AST,映射生成项目中使用到的组件清单,然后再基于Webpack插件将使用到的组件打包构建
  • API:编译器维护一个 api 依赖关系的 json 文件,该 json 文件描述每个 api 可能依赖的文件,然后 babel 查找到对应的 api 后,根据api 的依赖关系自动导入,重新编译

在工程师持续的加班奋战后,uni-app终于推出了全新的2.2版本,解决了这些难题,大幅降低了发行包体积,gzip后的框架体积,从162k降低到92k,仅相当于你在工程中引用了vue.jsvue-router、以及部分es6 polyfill库。(后续有详细比较)

除了大幅降低发行包体积,新版还调整了预载策略,可以进一步加快页面的渲染速度。

优化结果

搭建环境

我们使用vue-cli创建uni-app默认模板

vue create -p dcloudio/uni-preset-vue my-project

项目创建后,编译生成H5端的发行目录

npm run build:h5

或者在HBuilderX的可视化界面新建uni-app项目,选默认模板,然后点菜单发行到h5。

然后配置nginx服务器,指定root目录并启用gzip压缩,示例如下:

server {  
    ...  
    gzip on;  
    gzip_min_length 1k;  
    gzip_buffers 4 16k;  
    gzip_comp_level 4;  
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;  
    ...  
}  

runtime 动态裁剪

然后通过 Chrome DevTools 的 Network 面板,查看优化前的首页网络请求包大小,结果如下:

然后启用H5平台的优化开关,重新查看首页的网络请求包大小,结果如下:

可以看到框架主库(chunk-vendors.js)从162k变为92.8k,体积压缩43%!

实际上,框架主库主要分为三个部分:

  • vue/vue-router基础库
  • es6 polyfill库
  • uni-app runtime(组件&API实现)

如果对这三个部分再拆开对比,我们会看到uni-app组件库优化比例更高:

vue/vue-router es6 polyfill库 uni-app runtime 累计
优化前 38k 43k 81k 162k
优化后 38k 26k 28.8k 92.8k

Tips:

  • uni-app runtime从81k瘦身为28.8k,裁剪比例达到64%
  • 新编译器对es6的使用也做了动态扫描,项目中用到的es6语法(包括uni-app runtime用到的es6语法),才会打包对应的polyfill实现,故es6 polyfill库从43k瘦身为26k

脚本执行时间

然后,我们再通过Chrome DevTools 的 Performance 面板,查看优化前后的性能数据,对比结果如下:

可以看出,最耗时的脚本执行时间,从227ms提升为154ms,时间提升达到32%。

如何使用

虽然内部实现比较复杂,但uni-app对外暴漏了简单的配置,开发者只需在配置文件中打开一个开关即可。具体在 manifest.json 中h5节点配置如下选项:

"h5" : {  
    "optimization":{  
        "treeShaking":{  
            "enable":true //启用摇树优化  
        }  
    }  
}

2.2版的其他优化

uni-app2.2版中,还开放了package.jsonvue-config.js的自定义,开发者可以自由的配置编译策略。

现在可以自定义支持所有小程序平台,包括钉钉小程序、高德小程序、抖音小程序...等。这样除了标准的8大平台(iOS、Android、H5、微信小程序、支付宝小程序、百度小程序、头条小程序、QQ小程序),这些生态的子生态也可以分版本条件编译。

同样,也支持对H5端进行多子端编译,比如微信里的内嵌的H5、App里内嵌的H5...都可以分开条件编译。

如此灵活的条件编译,对于一套工程的多端发布、共享复用、同步升级,有莫大的好处。即便是仅开发H5版,uni-app的多端条件编译也提供了更灵活和强大的工程化能力。

2.2版本还可以设置各种静态资源、js、小程序自定义组件的编译和拷贝策略。如果你之前的h5项目或小程序项目想转换至uni-app下,又不想挪动某些目录结构,那么在vue-config.js里配置策略即可。

使用uni-app开发H5和直接开发H5相比的优势

在与直接开发h5拉齐性能的基础之上,uni-app给开发者提供了更多优势:

  1. 开发效率
    uni-app提供了大量适合手机页面的基础组件(其实就是小程序组件)。还提供了扩展组件uni ui。插件市场更有600多款插件。
    无论开发者想找一个电商的模板,还是找一个图表组件,都可以手到擒来。开发效率更胜以往。
  2. 多端编译
    我们开发H5时,经常需要给浏览器输出一份、给微信等超级App输出一份、给自家的App输出一份。甚至不同地域、不同用户画像,都会输出不同版本。以前,开发者只能对js部分进行条件编译,甚至不得不建多个仓库进行多版维护。

uni-app解决了这些烦恼,它的条件编译非常灵活强大:

  • 不管是组件、js还是css,都可以按平台编译输出
  • 还可以多个平台进行“与和或”的运算编译
  • 除了文件中的代码,整个文件、甚至整个目录,都可以条件编译

例如微信、QQ等在支持x5内核的内置浏览器中,使用x5的视频同层渲染;或者在微信服务号中调用微信卡劵,这段代码只有build到 dist/h5-weixin 这个目录下的版本才会被编译进去,其他平台不会有这段代码

// #ifdef H5-WEIXIN  
wx.openCard({  
    cardList: [{  
        cardId: '',  
        code: ''  
    }]// 需要打开的卡券列表  
});  
// #endif  

相关代码全部托管在 github,欢迎大家 star 或提交 pr!

继续阅读 »

背景

uni-app发布以来,已经服务了几十万开发者。让我们意外,或者说惊喜的是,有大量开发者用uni-app只编写H5版,并没有多端发布(可参考案例)。

这其实也符合uni-app的初衷,uni-app的定位并不是需要多端发布时才用uni-appuni-app是一个使用vue.js开发所有前端应用的统一框架。对于一个前端工程师来说,uni-app在手,啥需求都不愁。

过去的版本迭代中,uni-app已经成为了更好的小程序开发框架,比使用原生微信开发更有优势。(见评测

uni-app2.2的新版中,我们大幅优化了H5版的性能,让使用uni-app开发的H5,性能体验和直接使用vue.js开发H5拉齐。

可能不少开发者有某种误解:多端框架要适配多端,所以性能肯定不如原生。我们想纠正一下:

  1. 切忌想当然,多看数据评测。还不信就自己动手实验
  2. 请问使用vue.js开发的web性能好,还是使用原生js开发web性能好?答案是:使用vue.js框架。为什么?因为它在底层会自动优化数据同步、虚拟dom,比大多数开发手动写的代码要更高效。同样的,使用uni-app也如此,框架底层的优化处理比大多数开发者手动写setdata或dom操作更高效。
  3. 多端适配很多是在编译时做的,并不影响运行时的性能

优化难点

想优化H5端的性能,并不是一件容易的事。

“功能全面”和“小巧极速”,这是一对最难调和的冤家。

为了保障多端的一致性,uni-app实现了一套小程序的H5版Runtime,支持各种小程序的组件、API、配置。所以uni-app的H5版拥有比其他框架更好的跨端一致性。

但这也造成了老版的uni-app,输出H5端时,包体积过大(框架未压缩前有500k,部署gzip后162k)。

这确实是一个非常大的runtime,包含了几十个内置组件,数百个API。而且这些API仍然在快速增加中。

不能像其他框架一样因为功能少,所以体积小。我们不会用功能换性能,我们需要更好的方案。

优化方法

uni-app包含几十个内置组件、数百个API,是个“大而全”的框架;但开发者在开发具体应用时,未必能使用到所有的组件及API。若能根据项目具体情况,删掉没用到的组件及API,保留对项目有用的组件及API,便可精简框架、减少体积,这即是“摇树优化”的思路。

摇树优化(Tree-Shaking),顾名思义,摇晃树干,将枯死无用的枝条摇掉,仅保留有用的树枝。对应到框架层面理解,就是一个框架的众多组件和API,可以按需使用,把未引用的框架部分裁剪掉。Tree-Shaking 最早由 Rollup 提出,属于死码删除的一种形式。

常见的前端框架摇树,一般是基于明确的import引用关系。比如引用某UI库时,在A页面使用该UI库的search组件,此时需要写js代码import这个search组件,那么摇树分析就很容易。

uni-app和小程序一样,内置组件和API是不需要import的,这提升了开发的易用性,但此时却加大了摇树的难度,依靠简单的import分析无法实现摇树了。

幸好对DCloud团队而言,AST语法分析是看家本事,多年来HBuilder以js和vue语法提示著称。通过AST分析,uni-app新版可以精准判定这个项目使用了哪些组件和API。

不过这还不够,分析工程源码使用了什么组件和API之后,还得考虑框架各组件和API之间可能存在依赖和耦合关系,这需要进一步的计算和关系梳理,具体而言:

  • 组件:通过vue-template-compiler分析出来的AST,映射生成项目中使用到的组件清单,然后再基于Webpack插件将使用到的组件打包构建
  • API:编译器维护一个 api 依赖关系的 json 文件,该 json 文件描述每个 api 可能依赖的文件,然后 babel 查找到对应的 api 后,根据api 的依赖关系自动导入,重新编译

在工程师持续的加班奋战后,uni-app终于推出了全新的2.2版本,解决了这些难题,大幅降低了发行包体积,gzip后的框架体积,从162k降低到92k,仅相当于你在工程中引用了vue.jsvue-router、以及部分es6 polyfill库。(后续有详细比较)

除了大幅降低发行包体积,新版还调整了预载策略,可以进一步加快页面的渲染速度。

优化结果

搭建环境

我们使用vue-cli创建uni-app默认模板

vue create -p dcloudio/uni-preset-vue my-project

项目创建后,编译生成H5端的发行目录

npm run build:h5

或者在HBuilderX的可视化界面新建uni-app项目,选默认模板,然后点菜单发行到h5。

然后配置nginx服务器,指定root目录并启用gzip压缩,示例如下:

server {  
    ...  
    gzip on;  
    gzip_min_length 1k;  
    gzip_buffers 4 16k;  
    gzip_comp_level 4;  
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;  
    ...  
}  

runtime 动态裁剪

然后通过 Chrome DevTools 的 Network 面板,查看优化前的首页网络请求包大小,结果如下:

然后启用H5平台的优化开关,重新查看首页的网络请求包大小,结果如下:

可以看到框架主库(chunk-vendors.js)从162k变为92.8k,体积压缩43%!

实际上,框架主库主要分为三个部分:

  • vue/vue-router基础库
  • es6 polyfill库
  • uni-app runtime(组件&API实现)

如果对这三个部分再拆开对比,我们会看到uni-app组件库优化比例更高:

vue/vue-router es6 polyfill库 uni-app runtime 累计
优化前 38k 43k 81k 162k
优化后 38k 26k 28.8k 92.8k

Tips:

  • uni-app runtime从81k瘦身为28.8k,裁剪比例达到64%
  • 新编译器对es6的使用也做了动态扫描,项目中用到的es6语法(包括uni-app runtime用到的es6语法),才会打包对应的polyfill实现,故es6 polyfill库从43k瘦身为26k

脚本执行时间

然后,我们再通过Chrome DevTools 的 Performance 面板,查看优化前后的性能数据,对比结果如下:

可以看出,最耗时的脚本执行时间,从227ms提升为154ms,时间提升达到32%。

如何使用

虽然内部实现比较复杂,但uni-app对外暴漏了简单的配置,开发者只需在配置文件中打开一个开关即可。具体在 manifest.json 中h5节点配置如下选项:

"h5" : {  
    "optimization":{  
        "treeShaking":{  
            "enable":true //启用摇树优化  
        }  
    }  
}

2.2版的其他优化

uni-app2.2版中,还开放了package.jsonvue-config.js的自定义,开发者可以自由的配置编译策略。

现在可以自定义支持所有小程序平台,包括钉钉小程序、高德小程序、抖音小程序...等。这样除了标准的8大平台(iOS、Android、H5、微信小程序、支付宝小程序、百度小程序、头条小程序、QQ小程序),这些生态的子生态也可以分版本条件编译。

同样,也支持对H5端进行多子端编译,比如微信里的内嵌的H5、App里内嵌的H5...都可以分开条件编译。

如此灵活的条件编译,对于一套工程的多端发布、共享复用、同步升级,有莫大的好处。即便是仅开发H5版,uni-app的多端条件编译也提供了更灵活和强大的工程化能力。

2.2版本还可以设置各种静态资源、js、小程序自定义组件的编译和拷贝策略。如果你之前的h5项目或小程序项目想转换至uni-app下,又不想挪动某些目录结构,那么在vue-config.js里配置策略即可。

使用uni-app开发H5和直接开发H5相比的优势

在与直接开发h5拉齐性能的基础之上,uni-app给开发者提供了更多优势:

  1. 开发效率
    uni-app提供了大量适合手机页面的基础组件(其实就是小程序组件)。还提供了扩展组件uni ui。插件市场更有600多款插件。
    无论开发者想找一个电商的模板,还是找一个图表组件,都可以手到擒来。开发效率更胜以往。
  2. 多端编译
    我们开发H5时,经常需要给浏览器输出一份、给微信等超级App输出一份、给自家的App输出一份。甚至不同地域、不同用户画像,都会输出不同版本。以前,开发者只能对js部分进行条件编译,甚至不得不建多个仓库进行多版维护。

uni-app解决了这些烦恼,它的条件编译非常灵活强大:

  • 不管是组件、js还是css,都可以按平台编译输出
  • 还可以多个平台进行“与和或”的运算编译
  • 除了文件中的代码,整个文件、甚至整个目录,都可以条件编译

例如微信、QQ等在支持x5内核的内置浏览器中,使用x5的视频同层渲染;或者在微信服务号中调用微信卡劵,这段代码只有build到 dist/h5-weixin 这个目录下的版本才会被编译进去,其他平台不会有这段代码

// #ifdef H5-WEIXIN  
wx.openCard({  
    cardList: [{  
        cardId: '',  
        code: ''  
    }]// 需要打开的卡券列表  
});  
// #endif  

相关代码全部托管在 github,欢迎大家 star 或提交 pr!

收起阅读 »

uniapp关于使用plus.device.getInfo获取imei的值的经验分享

IMEI

自从新版本的hbuilder x逐渐废弃plus.device.imei的时候,我这边也开始使用plus.device.getInfo,但是使用的过程中发现获取不到imei的值,后面才发现我使用的方法不对,不能像使用 plus.device.imei 的时候直接给变量赋值 然后下文在使用。

不明白的或者看不懂的,直接把我下面的案例代码,拿去执行一下 就就会明白了,imei是怎样获取的

下面直接上一个案例

//开始复制

plus.device.getInfo({
success:function(e){
//这里获取到imei 剩下的逻辑也写在这里面
var imei=e.imei;
//里面写接下来的逻辑
console.log('imei='+imei);//这里是能获取imei的
}
})

//不能在外面再来使用imei 一般是获取不到值的
console.log(imei);//这里的imei是空的 是获取不到的 因为和上面的方法是同时执行的 所以这个时候的imei并没有值

//结束

继续阅读 »

自从新版本的hbuilder x逐渐废弃plus.device.imei的时候,我这边也开始使用plus.device.getInfo,但是使用的过程中发现获取不到imei的值,后面才发现我使用的方法不对,不能像使用 plus.device.imei 的时候直接给变量赋值 然后下文在使用。

不明白的或者看不懂的,直接把我下面的案例代码,拿去执行一下 就就会明白了,imei是怎样获取的

下面直接上一个案例

//开始复制

plus.device.getInfo({
success:function(e){
//这里获取到imei 剩下的逻辑也写在这里面
var imei=e.imei;
//里面写接下来的逻辑
console.log('imei='+imei);//这里是能获取imei的
}
})

//不能在外面再来使用imei 一般是获取不到值的
console.log(imei);//这里的imei是空的 是获取不到的 因为和上面的方法是同时执行的 所以这个时候的imei并没有值

//结束

收起阅读 »

人人商城打包APP说明

插件支持功能
1、支付宝原生支付,微信原生支付
2、分享微信,微博,QQ
3、微信App验证功能,上架苹果市场时必须用到。
4、退出App
5、清理App缓存
6、状态栏颜色随网页内导航条变化

需要准备
1、人人商城网站系统
2、初雪云高级版本App制作网址:https://www.chuxueyun.com/deal/5/cateid-30.html

3、人人电商的插件下载:https://www.lanzous.com/i5ciy5e

4、需先申请微信开放平台权限,支付权限,登录权限,分享权限申请,申请地址:https://open.weixin.qq.com/
5、申请QQ互联权限,申请地址:https://connect.qq.com/index.html
6、申请支付宝支付权限,申请地址:https://b.alipay.com/index2.htm

注意
1:支付配置
安装后端插件:http://t.cn/AiYF6JQX

下载人人商城的插件,解压替换到对应的ewei_shopv2目录内。

注意

请先在人人商城后台配置好支付设置,如下图:

2:微信登录修改

ewei_shopv2\core\model\member.php 文件checkMemberSNS方法里,大概位置在886行

$token = trim($_GPC['token']);

改成

$token = trim($_GPC['access_token']);

$appid和$secret的数值改成自己的微信开放平台的appid和秘钥即可,大概位置在889行

ewei_shopv2\core\mobile\account\index.php文件中sns()方法里将, 大概位置在189行

if ($_W['ispost'] && !empty($sns) && !empty($_GPC['openid']))

改成

if (!empty($sns) && !empty($_GPC['openid'])) {

3.配置平台的UA
设置User Agant

打开App制作平台点击基础配置–>User Agant配置,UA类型选择:自定义 自定义UA填写CK 2.0一定要大写。(中间有个空格)

第三方配置:微信配置填写微信开放平台对应的值,一定要是开放平台,网址https://open.weixin.qq.com

第三方配置:QQ配置填写QQ开放平台对应的值,一定要是开放平台,网址http://open.qq.com QQ安卓登录与分享权限,要求上架应用宝后才可以使用

APP内对应的支付,第三方登录,分享功能全部打开。

高级配置:打开新窗口,请开启

如果被拒,请查看:链接(http://webapp.chuxueyun.com/1174652)

继续阅读 »

插件支持功能
1、支付宝原生支付,微信原生支付
2、分享微信,微博,QQ
3、微信App验证功能,上架苹果市场时必须用到。
4、退出App
5、清理App缓存
6、状态栏颜色随网页内导航条变化

需要准备
1、人人商城网站系统
2、初雪云高级版本App制作网址:https://www.chuxueyun.com/deal/5/cateid-30.html

3、人人电商的插件下载:https://www.lanzous.com/i5ciy5e

4、需先申请微信开放平台权限,支付权限,登录权限,分享权限申请,申请地址:https://open.weixin.qq.com/
5、申请QQ互联权限,申请地址:https://connect.qq.com/index.html
6、申请支付宝支付权限,申请地址:https://b.alipay.com/index2.htm

注意
1:支付配置
安装后端插件:http://t.cn/AiYF6JQX

下载人人商城的插件,解压替换到对应的ewei_shopv2目录内。

注意

请先在人人商城后台配置好支付设置,如下图:

2:微信登录修改

ewei_shopv2\core\model\member.php 文件checkMemberSNS方法里,大概位置在886行

$token = trim($_GPC['token']);

改成

$token = trim($_GPC['access_token']);

$appid和$secret的数值改成自己的微信开放平台的appid和秘钥即可,大概位置在889行

ewei_shopv2\core\mobile\account\index.php文件中sns()方法里将, 大概位置在189行

if ($_W['ispost'] && !empty($sns) && !empty($_GPC['openid']))

改成

if (!empty($sns) && !empty($_GPC['openid'])) {

3.配置平台的UA
设置User Agant

打开App制作平台点击基础配置–>User Agant配置,UA类型选择:自定义 自定义UA填写CK 2.0一定要大写。(中间有个空格)

第三方配置:微信配置填写微信开放平台对应的值,一定要是开放平台,网址https://open.weixin.qq.com

第三方配置:QQ配置填写QQ开放平台对应的值,一定要是开放平台,网址http://open.qq.com QQ安卓登录与分享权限,要求上架应用宝后才可以使用

APP内对应的支付,第三方登录,分享功能全部打开。

高级配置:打开新窗口,请开启

如果被拒,请查看:链接(http://webapp.chuxueyun.com/1174652)

收起阅读 »