精华内容
下载资源
问答
  • plsql 无监听程序

    2012-02-09 15:30:01
    系统是win7 64位 oracle11g 64位,安装32位PLSQL Developer已经按照网上提供的方式安装客户端,设置好环境变量,但是都出现都出现以上错误。 PS:之前将数据库与实例名称更换名称,SID值之后都更换过来。监听可以...
  • 前言本文介绍如何对微信小程序进行性能优化,下面内容将从两大方面进行分析总结:工具的使用;常见的优化方向;工具的使用1. 使用微信小程序自带的工具插件“体验评分”,运行环境的要求和使用流程参考...

    前言

    本文介绍如何对微信小程序进行性能优化,下面内容将从两大方面进行分析总结:
    • 工具的使用;

    • 常见的优化方向;

    工具的使用

    1. 使用微信小程序自带的工具插件“体验评分”,运行环境的要求和使用流程参考“https://developers.weixin.qq.com/miniprogram/dev/devtools/audits.html”;最终运行效果如下图:

    e729c202bfb24d2f64caf18f28c51359.png

    2. 然后根据效果图的分析报告,查看需要优化的细项(里面都有详细问题描述和优化路径说明),并根据说明一一进行优化:

    常见的问题包括:

    1. 应避免出现任何 JavaScript 异常:

    因为出现JavaScript异常可能导致小程序的交互无法进行下去;

    小程序所有请求应响应正常:

    因为请求失败可能导致小程序的交互无法进行下去;

    所有请求的耗时不应太久:

    因为请求耗时太长会让用户一直等待甚至离开,应当优化好服务器处理时间、减小回包大小,让请求快速响应;

    避免将未绑定在 WXML 的变量传入setData:

    因为setData操作会引起框架处理一些渲染界面相关的工作,而一个未绑定的变量意味着与界面渲染无关,传入setData会造成不必要的性能消耗;

    合理设置可点击元素的响应区域大小:

    因为过小会导致用户很难点中,体验很差;

    避免渲染界面的耗时过长:

    因为渲染界面的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要校验下是否同时渲染的区域太大;

    避免执行脚本的耗时过长:

    因为执行脚本的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要确认并优化脚本的逻辑;

    对网络请求做必要的缓存以避免多余的请求:

    因为发起网络请求总会让用户等待,可能造成不好的体验,应尽量避免多余的请求,比如对同样的请求进行缓存;

    wxss 覆盖率较高,较少或没有引入未被使用的样式:

    按需引入 wxss 资源,如果小程序中存在大量未使用的样式,会增加小程序包体积大小,从而在一定程度上影响加载速度;

    文字颜色与背景色搭配较好,适宜的颜色对比度更方便用户阅读;

    所有资源请求都建议使用 HTTPS:

    因为使用 HTTPS,可以让你的小程序更加安全,而 HTTP 是明文传输的,存在可能被篡改内容的风险;

    不使用废弃接口:

    因为使用即将废弃或已废弃接口,可能导致小程序运行不正常。一般而言,接口不会立即去掉,但保险起见,建议不要使用,避免后续小程序突然运行异常;

    避免过大的 WXML 节点数目:

    建议一个页面使用少于 1000 个 WXML 节点,节点树深度少于 30 层,子节点数不大于 60 个。一个太大的 WXML 节点树会增加内存的使用,样式重排时间也会更长;

    避免将不可能被访问到的页面打包在小程序包里:

    因为小程序的包大小会影响加载时间,所以应该尽量控制包体积大小,避免将不会被使用的文件打包进去;

    及时回收定时器:

    因为定时器是全局的,并不是跟页面绑定的,所以当页面因后退被销毁时,定时器应注意手动回收;

    避免使用 css ':active' 伪类来实现点击态:

    使用 css ':active' 伪类来实现点击态,很容易触发,并且滚动或滑动时点击态不会消失,体验较差;建议使用小程序内置组件的 'hover-*' 属性来实现;

    滚动区域可开启惯性滚动以增强体验:

    因为惯性滚动会使滚动比较顺畅,在安卓下默认有惯性滚动,而在 iOS 下需要额外设置 -webkit-overflow-scrolling: touch 的样式;

    常见的优化方向

    总结一些经常会出现问题的地方,并介绍可使用的解决方法;
    1. 启动加载性能优化:

    • 控制代码包大小:

      • 开启开发者工具中"上传代码时自动压缩";

      • 及时清理无用代码和资源文件;

      • 减少代码包中的图片等资源文件的大小和数量;

    • 分包加载:

      • 将小程序中不经常使用的页面放到多个分包内,主包是保留最常用的核心页面;启动时只加载主包,使用时按需下载分包;

      • 使用分包加载会出现用户首次进入分包页面时需要进行分包的下载和注入,造成页面切换的延迟;开发者可使用分包预下载,预先配置页面可能会跳转到的分包,框架在进入页面后根据配置进行预下载;

    • 独立分包:

      • 从分包页面启动时,必需要先依赖于主包的下载和注入,启动速度受主包限制;使用独立分包,从独立分包页面启动,只下载和注入分包就可以打开页面;

    首屏加载的体验优化建议:

    • 提前请求:异步数据请求不需要等待页面渲染完成(onLoad 阶段就可以发起请求,不用等ready);

    • 利用缓存:利用storage API对异步请求数据进行缓存,二次启动时先利用缓存数据渲染页面,而下拉刷新或者缓存过期才更新数据;

    • 避免白屏:先展示页面骨架和基础内容;

    • 及时反馈:即时地对需要用户等待的交互操作给出反馈,避免用户以为小程序无响应;

    避免不当使用setData:

    • 存在setData的数据过大(通讯耗时与数据量相关,页面更新延期;使用setData的特殊key实现局部更新);

    // 1: 初始一个list,存储列表数据this.data = startList// 2: 监听滚动事件,滚动到底部获取新数据,并追加到list尾部,最后重新setDataonReachBottom:()=>{  const {list} = this.data  fetchNewData().then((res)=>{    list.push(res.list);    this.setData({list})  }}

            大部分人面对长列表滚动的时候,一开始的处理方式都是这样的,如果数据不多,只有几页可能不会太暴露问题;但当页数过多,几十页甚至上百页的情况,list的数据会越来越大,每次setData的数据就会越来越多,因而每次页面重新渲染的节点就会越来越多,从而导致滚动到后面,加载越来越慢。另外,由于小程序的视图渲染层和数据逻辑处理层是分开的,不是在同一个线程上面的,从用户触发页面交互,到处理数据逻辑,最后呈现页面,数据到视图是需要传输的,因而小程序本身对数据大小也有限制,不能超过1M。

            我们可以通过数据路径的写法,来将数据分批的传输到视图层中,减少一次性setData的数据大小。具体写法如下

    // 1.通过一个二维数组来存储数据let feedList = [[array]];// 2.维护一个页面变量值,加载完一次数据page++let page = 1// 3.页面每次滚动到底部,通过数据路径更新数据onReachBottom:()=>{  fetchNewData().then((newVal)=>{    this.setData({      ['feedList[' + (page - 1) + ']']: newVal,    })  }}// 4.最终我们的数据是[[array1],[array2]]这样的格式,然后通过wx:for遍历渲染数据
    • 列表局部更新:

    比如,将点赞的id传过去,查找相对应id的那条数据的下标(index是不会改变的),用setData进行局部刷新

    this.setData({    list[index] = newList[index]})

    短时间内频繁调用setData:

            因为会出现操作卡顿、交互延迟、页面渲染延迟等问题;

            优化:避免不必要的setData,对连续setData调用进行合并;

    • 避免后台态页面进行setData,因为后台态页面去setData也会抢占前台页面的执行

    存在短时间内发起太多图片请求(图片懒加载):

            就是渲染页面时,一次性发送了过多的图片请求,导致了同一时间发起了过多的http请求,http连接是非常耗时的,尤其是一次性发起这么多,并且一次性发起的http链接也是有限制的,比如chrome浏览器就限制一次性最多6个。

            所以在渲染页面时,不在视图范围内的图片我们不加载,只有元素出现在视图范围内了,再渲染。

            常规的做法是,通过 getBoundingClientRect() 获取元素的位置,然后与页面滚动位置比较,如果出现在视图内,就将 img 显示。这种方式有2个问题:

    1)getBoundingClientRect()方法调用本身容易引起页面重排;

             2)监听滚动事件本身就频繁触发,虽然可以通过节流的方式来减少,但还是容易增加无谓代码处理;

            微信提供了 IntersectionObserver 对象,IntersectionObserver 对象,用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。

            通过这个api我们不用再主动去监听元素位置了,在页面渲染一开始,通过这个api指明需要监听的元素,系统会自动去监听了元素位置。

    let data = list;<img class="img-{{index}}" wx:for="{{data}}">img>data.forEach((item,index)=>{  this.createIntersectionObserver().relativeToViewport.observe(`.img-${index}`,res=>{    if (res.intersectionRatio > 0){      this.setData({        item.imgShow:true      })    }  })})

            intersectionRatio值大于0,说明元素出现在视图中了,重新setData数据,显示图片组件。

    存在图片太大而显示区域过小:

            这个问题就是指图片尺寸太大了,而页面上我们显示的尺寸又太小了,图片尺寸大,请求图片就越慢,导致页面渲染速度下降。

            对于页面里面的图片,最好都把图片存储在cdn服务器上,一个是能充分利用cdn缓存来加快请求速度,另外一个就是cdn上能够将图片进行一定的处理,比如裁剪。我司就是通过cdn来响应图片处理,然后请求图片时告诉cdn服务器需要什么要的尺寸图片,由cdn服务器响应对应尺寸图片。

    key值在列表渲染中的作用:

            key值在列表渲染的时候,能够提升列表渲染性能,为什么呢?首先得想想小程序的页面是如何渲染的,主要分为以下几步:

            1. 将wxml结构的文档构建成一个vdom虚拟数

            2. 页面有新的交互,产生新的vdom数,然后与旧数进行比较,看哪里有变化了,做对应的修改(删除、移动、更新值)等操作

            3. 最后再将vdom渲染成真实的页面结构

            key值的作用就在第二步,当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率

            key值如果不指明,默认会按数组的索引来处理,因而会导致一些类似input等输入框组件的值出现混乱的问题。

            因而,在做list渲染时,如果list的顺序发生变化时,最好增加key,且不要简单的使用数组索引当做key。

    避免不当使用onPageScroll:

            只在必要的时候监听pageScroll事件;

            避免在onPageScroll中执行复杂逻辑;

            避免在onPageScroll中频繁调用setData;

            避免频繁查询节点信息(SelectQuery);

    canvas渲染:

            分层绘制到不同canvas,不变的部分单独绘制到一个canvas,动态生成的绘制到一个canvs,最后再合入到一个canvas。

    总结:

    上文总结的只是平时一些常见的问题,并不是全部的问题;且随着微信小程序基础库的升级,有些问题可能会被优化解决,但也可能会产生其他的新问题。
    展开全文
  • OMIX 2.0 是 WeStore 的进化版,WeStore使用的是数据变更前后的 diff,diff 出的 json 就是 setData 的 patch,OMIX 2.0 使用的是 observer 监听数据的变更得到 setData 的 patch。和 OMIX 对比,WeStore 运行时需要...

    bd4a71d9d3a3aaafc028fa5865b55b28.png

    好的设计只有一种,我们认为 OMIX 2.0 的设计刚刚好。

    OMIX 2.0 是 WeStore 的进化版,WeStore 使用的是数据变更前后的 diff,diff 出的 json 就是 setData 的 patch,OMIX 2.0 使用的是 observer 监听数据的变更得到 setData 的 patch。

    和 OMIX 对比,WeStore 运行时需要更多的计算,OMIX 初始化时需要更多的内存和计算,但是数据变更时 OMIX 速度比 WeStore 快,编程体验方面,OMIX 不需要手动 update,WeStore 需要手动 update。

    ef8729fe99357ced28a2bd7d33d0de28.png

    特性

    • 无状态视图设计

    • 对小程序零入侵

    • 只有一个 API

    • 支持计算属性

    • 轻松驾驭小项目、中项目和大型项目

    • 也适用小游戏,是的没错,使用 小程序开发小游戏,本文第二个案例使用 OMIX 实现一个小游戏

    快速入门

    API

    • create(store, option) 创建页面, store 从页面注入,可跨页面跨组件共享

    • create(option) 创建组件

    • this.store 和 this.data 全局 store 和 data,页面和页面所有组件可以拿到, 操作 data 会自动更新视图

    不需要注入 store 的页面或组件用使用PageComponent 构造器, Component 通过 triggerEvent 与上层通讯或与上层的 store 交互

    简单实战

    实现一个简单的 log 列表的展示

    4d6ca77f59b64efa2e6473e4782c3f14.png

    定义全局 store:

    export default {
    data: {
    logs: []
    }
    }

    定义页面:

    import create from '../../utils/create'import util from '../../utils/util'import store from '../../store'create(store, {// 声明依赖
    use: ['logs'], //也支持复杂路径依赖,比如 ['list[0].name']// 计算属性,可以直接绑定在 wxml 里
    computed: {logsLength() {return this.logs.length
    }
    },onLoad: function () {this.store.data.logs = (wx.getStorageSync('logs') || []).map(log => {return util.formatTime(new Date(log))
    })setTimeout(() => {//响应式,自动更新视图this.data.logs[0] = 'Changed!'
    }, 1000)setTimeout(() => {//响应式,自动更新视图this.data.logs.push(Math.random(), Math.random())
    }, 2000)setTimeout(() => {//响应式,自动更新视图this.data.logs.splice(this.store.data.logs.length - 1, 1)
    }, 3000)
    }
    })
    <view class="container log-list">
    <block wx:for="{{logs}}" wx:for-item="log">
    <text class="log-item">{{index + 1}}. {{log}}text>block>view>

    定义 test-store 组件, 组件内也可以组件使用全局的 logs,组件源码:

    import create from '../../utils/create'create({
    use: ['logs'],//计算属性
    computed: {logsLength() {return this.logs.length
    }
    }
    })
    <view class="ctn">
    <view>Log Length: {{logs.length}}view>
    <view>Log Length by computed: {{logsLength}}view>view>

    其他可选配置说明

    修改 store.js 的 debug 字段用来打开和关闭 log 调试:

    export default {
    data: {
    motto: 'Hello World',
    userInfo: {},
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    logs: []
    },
    debug: true, //调试开关,打开可以在 console 面板查看到 store 变化的 log
    updateAll: true //当为 true 时,无脑全部更新,组件或页面不需要声明 use
    }

    全局更新开发默认是关闭的,调试开关默认打开,可以在store.data 的所以变动都会出现在开发者工具 log 面板,如下图所示:

    f6f2f5e7c3ca8bcea9a75aefe0280fdf.png

    其他

    这里需要注意,改变数组的 length 不会触发视图更新,需要使用 size 方法:

    this.data.arr.size(2) //会触发视图更新this.data.arr.length = 2 //不会触发视图更新this.data.arr.push(111) //会触发视图更新//每个数组的方法都有对应的 pure 前缀方法,比如 purePush、pureShift、purePop 等this.data.arr.purePush(111) //不会触发视图更新

     计算属性

      use: ['motto','userInfo','hasUserInfo','canIUse'
    ],
    computed: {reverseMotto() {return this.motto.split('').reverse().join('')
    }
    }

    计算属性定义在页面或者组件的 computed 里,如上面的 reverseMotto, 它可以直接绑定在 wxml 里,motto 更新会自动更新 reverseMotto 的值。

    store 变化监听

    const handler = function (evt) {console.log(evt)
    }//监听,允许绑定多个store.onChange(handler)//移除监听store.offChange(handler)

    复杂 store 拆分到多文件

    当小程序变得非常复杂的时候,单文件单一的 store 会变得非常臃肿,所以需要拆分为多个 store 到新的文件,这里举个例子:

    store-a.js:

    export const data = {
    name: 'omix'
    }export function changeName(){data.name = 'Omix'
    }

    store-b.js:

    export const data = {
    name: 'omix',
    age: 2
    }export function changeAge(){data.age++
    }

    store.js 合并所以子 store 到对应模块(a, b):

    import { data as dataA, changeName } from 'store-a.js'import { data as dataB, changeAge } from 'store-b.js'const store = {
    data:{
    a: dataA,
    b: dataB
    },
    a: { changeName },
    b: { changeAge }
    }export default store

    数据绑定:

    <view>
    <text>{{a.name}}text>
    <text>{{b.name}}-{{b.age}}text>view>

    数据使用:

    import create from '../../utils/create'import store from '../../store/store'create(store, {//声明依赖
    use: ['a.name', 'b'],onLoad: function () {setTimeout(_ => {store.a.changeName()
    }, 1000)setTimeout(_ => {store.b.changeAge()
    }, 2000)
    }
    })

    多 store 注入的完整的案例可以 点击这里

    Path 命中规则

    当 store.data 发生变化,相关依赖的组件会进行更新,举例说明 Path 命中规则:

    Observer Path(由数据更改产生)use 中的 path是否更新
    abcabc更新
    abc[1]abc更新
    abc.aabc更新
    abcabc.a不更新
    abcabc[1]不更新
    abcabc[1].c不更新
    abc.babc.b更新

    只要注入组件的 path 等于 use 里声明 或者在 use 里声明的其中 path 子节点下就会进行更新,以上只要命中一个条件便进行更新!

    如果你的小程序真的很小,那么请无视上面的规则,直接把 store 的 updateAll 声明为 true 便可。如果小程序页面很多很复杂,为了更优的性能,请给每一个页面或非存组件声明 use

    贪吃蛇游戏实战

    b7559e6f8109759338f213073745d841.png

    领域模型设计

    • 提取主要实体,比如(蛇、游戏)

    • 从实体名词中总结出具体业务属性方法,

      • 包含结束暂停状态、地图、分数、帧率、游戏主角、食物

      • 包含开始游戏、暂停游戏、结束游戏、生产食物、重置游戏等方法

      • 包含运动方向、body属性

      • 包含移动和转向方法

      • 游戏

    • 建立实体属性方法之间的联系

      • 游戏主角唯一,即蛇

      • 蛇吃食物,游戏分数增加

      • 食物消失,游戏负责再次生产食物

      • 蛇撞墙或撞自身,游戏状态结束

    • 核心循环设计

      • 判断是否有食物,没有就生产一个(低帧率)

      • 蛇与自身碰撞检测

      • 蛇与障碍物碰撞检测

      • 蛇与食物碰撞检测

      • 蛇移动

    使用代码描述蛇实体

    class Snake {constructor() {this.body = [3, 1, 2, 1, 1, 1]this.dir = 'right'
    }move(eating) {const b = this.bodyif (!eating) {b.pop()b.pop()
    }switch (this.dir) {case 'up':b.unshift(b[0], b[1] - 1)breakcase 'right':b.unshift(b[0] + 1, b[1])breakcase 'down':b.unshift(b[0], b[1] + 1)breakcase 'left':b.unshift(b[0] - 1, b[1])break
    }
    }turnUp() {if (this.dir !== 'down')this.dir = 'up'
    }turnRight() {if (this.dir !== 'left')this.dir = 'right'
    }turnDown() {if (this.dir !== 'up')this.dir = 'down'
    }turnLeft() {if (this.dir !== 'right')this.dir = 'left'
    }
    }

    蛇的转向有个逻辑,就是不能反方向后退,比如正在向上移动,不能直接直接向下转向,所以在 turnUp,turnRight,turnDown,turnLeft 中都有对应的条件判断。

    使用代码描述游戏实体

    import Snake from './snake'class Game {constructor() {this.map = []this.size = 16this.loop = nullthis.interval = 500this.paused = falsethis._preDate = Date.now()this.init()
    }init() {this.snake = new Snakefor (let i = 0; i < this.size; i++) {const row = []for (let j = 0; j < this.size; j++) {row.push(0)
    }this.map.push(row)
    }
    }tick() {this.makeFood()const eating = this.eat()this.snake.move(eating)this.mark()
    }mark() {const map = this.mapfor (let i = 0; i < this.size; i++) {for (let j = 0; j < this.size; j++) {
    map[i][j] = 0
    }
    }for (let k = 0, len = this.snake.body.length; k < len; k += 2) {this.snake.body[k + 1] %= this.sizethis.snake.body[k] %= this.sizeif (this.snake.body[k + 1] < 0) this.snake.body[k + 1] += this.sizeif (this.snake.body[k] < 0) this.snake.body[k] += this.size
    map[this.snake.body[k + 1]][this.snake.body[k]] = 1
    }if (this.food) {
    map[this.food[1]][this.food[0]] = 1
    }
    }start() {this.loop = setInterval(() => {if (Date.now() - this._preDate > this.interval) {this._preDate = Date.now()if (!this.paused) {this.tick()
    }
    }
    }, 16)
    }stop() {clearInterval(this.loop)
    }pause() {this.paused = true
    }play() {this.paused = false
    }reset() {this.paused = falsethis.interval = 500this.snake.body = [3, 1, 2, 1, 1, 1]this.food = nullthis.snake.dir = 'right'
    }toggleSpeed() {this.interval === 500 ? (this.interval = 150) : (this.interval = 500)
    }makeFood() {if (!this.food) {this.food = [this._rd(0, this.size - 1), this._rd(0, this.size - 1)]for (let k = 0, len = this.snake.body.length; k < len; k += 2) {if (this.snake.body[k + 1] === this.food[1]&& this.snake.body[k] === this.food[0]) {this.food = nullthis.makeFood()break
    }
    }
    }
    }eat() {for (let k = 0, len = this.snake.body.length; k < len; k += 2) {if (this.snake.body[k + 1] === this.food[1]&& this.snake.body[k] === this.food[0]) {this.food = nullreturn true
    }
    }
    }_rd(from, to) {return from + Math.floor(Math.random() * (to + 1))
    }
    }

    可以看到上图使用了 16*16 的二维数组来存储蛇、食物、地图信息。蛇和食物占据的格子为 1,其余为 0。

    [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    ]

    所以上面代表了一条长度为 5 的蛇和 1 个食物,你能在上图中找到吗?

    定义 store

    import Game from '../models/game'const game = new Gameconst { snake, map } = gamegame.start()class Store {
    data = {
    map,
    paused: false,
    highSpeed: false
    }turnUp() {snake.turnUp()
    }turnRight() {snake.turnRight()
    }turnDown() {snake.turnDown()
    }turnLeft() {snake.turnLeft()
    }pauseOrPlay = () => {if (game.paused) {game.play()this.data.paused = false
    } else {game.pause()this.data.paused = true
    }
    }reset() {game.reset()
    }toggleSpeed() {game.toggleSpeed()this.data.highSpeed = !this.data.highSpeed
    }
    }export default new Store

    会发现, store 很薄,只负责中转 View 的 action,到 Model,以及隐藏式自动映射 Model 上的数据到 View。

    游戏面板渲染

    WXML:

    <view class="game">
    <view class="p" wx:for="{{map}}" wx:for-item="row" wx:for-index="index">
    <block wx:for="{{row}}" wx:for-item="col">
    <block wx:if="{{col}}">
    <view class="b s">view>block>
    <block wx:else>
    <view class="b">view>block>block>view>view>

    带有 class 为 s 的格式是黑色的,比如食物、蛇的身体,其余的会灰色底色。

    对应 js:

    import create from '../../utils/create'create({
    use: ['map']
    })

    map 代表依赖 store.data.map,map 更新会自动更新视图。

    控制主界面面板

    <view>
    <game />
    <view class="ctrl">
    <view class="btn cm-btn cm-btn-dir up" bindtap="turnUp"><i>i><em>em><span>上span>view>
    <view class="btn cm-btn cm-btn-dir down" bindtap="turnDown"><i>i><em>em><span>下span>view>
    <view class="btn cm-btn cm-btn-dir left" bindtap="turnLeft"><i>i><em>em><span >左span>view>
    <view class="btn cm-btn cm-btn-dir right" bindtap="turnRight"><i>i><em>em><span >右span>view>
    <view class="btn cm-btn space" bindtap="toggleSpeed"><i>i><span >{{highSpeed? '减速': '加速'}}span>view>
    <view class="btn reset small" bindtap="reset"><i >i><span >重置span>view>
    <view class="btn pp small" bindtap="pauseOrPlay"><i>i><span >{{paused ? '继续' : '暂停'}}span>view>view>view>

    主界面使用 page,引用 component:

    {"usingComponents": {"game": "/components/game/index"
    }
    }

    对应 JS:

    import create from '../../utils/create'import store from '../../store/index'create(store, {
    use: ['paused', 'highSpeed'],turnUp() {store.turnUp()
    },turnDown() {store.turnDown()
    },turnLeft() {store.turnLeft()
    },turnRight() {store.turnRight()
    },toggleSpeed() {store.toggleSpeed()
    },reset() {store.reset()
    },pauseOrPlay() {store.pauseOrPlay()
    }
    })

    帧率控制

    怎么控制主帧率和局部帧率。一般情况下,我们认为 60 FPS 是流畅的,所以我们定时器间隔是有 16ms,核心循环里的计算量越小,就越接近 60 FPS:

    this.loop = setInterval(() => {//
    }, 16)

    但是有些计算没有必要 16 秒计算一次,这样会降低帧率,所以可以记录上一次执行的时间用来控制帧率:

    this.loop = setInterval(() => {//执行在这里是大约 60 FPSif (Date.now() - this._preDate > this.interval) {//执行在这里是大约  1000/this.interval FPSthis._preDate = Date.now()//暂停判断if (!this.paused) {//核心循环逻辑this.tick()
    }
    }
    }, 16)

    由于小程序 JSCore 里不支持 requestAnimationFrame,所以这里使用 setInterval。当然也可以使用 raf-interval 循环执行 tick:

    this.loop = setRafInterval(() => {//执行在这里是大约 60 FPSif (Date.now() - this._preDate > this.interval) {//执行在这里是大约  1000/this.interval FPSthis._preDate = Date.now()//暂停判断if (!this.paused) {//核心循环逻辑this.tick()
    }
    }
    }, 16)

    用法和 setInterval 一致,只是内部使用 setTimeout 且如果支持 requestAnimationFrame 会优先使用 requestAnimationFrame

    贪吃蛇架构

    那么是整个项目是 MVC、MVP 还是 MVVM?

    从贪吃蛇源码可以看出:视图(components,pages)和模型(models)是分离的,没有相互依赖关系,但是在 MVC 中,视图依赖模型,耦合度太高,导致视图的可移植性大大降低,所以一定不是 MVC 架构。

    abea38dadeecebc73044435e671986c3.png

    在 MVP 模式中,视图不直接依赖模型,由 Presenter 负责完成 Model 和 View 的交互。MVVM 和 MVP 的模式比较接近。ViewModel 担任这 Presenter 的角色,并且提供 UI 视图所需要的数据源,而不是直接让 View 使用 Model 的数据源,这样大大提高了 View 和 Model 的可移植性,比如同样的 Model 切换使用 Flash、HTML、WPF 渲染,比如同样 View 使用不同的 Model,只要 Model 和 ViewModel 映射好,View 可以改动很小甚至不用改变。

    从贪吃蛇源码可以看出,View(components) 里直接使用了 Presenter(stores) 的 data 属性进行渲染,data 属性来自于 Model(models) 的属性,并没有出现 Model 到 ViewModel 的映射。所以一定不是 MVVM 架构。

    所以上面的贪吃蛇属于 MVP !只不过是进化版的 MVP,因为 M 里的 map 的变更会自定更是 View,从 M->P->V的回路是自动化的,代码里看不到任何逻辑。仅仅需要声明依赖:

    use: ['map']

    这样也规避了 MVVM 最大的问题:M 到 VM 映射的开销。

    进化版 MVP 优势

    1. 复用性

    Model 和 View 之间解耦,Model 或 View 中的一方发生变化,Presenter 接口不变,另一方就没必要对上述变化做出改变,那么 Model 层的业务逻辑具有很好的灵活性和可重用性。

    2. 灵活性

    Presenter 的 data 变更自动映射到视图,使得 Presenter 很薄很薄,View 属于被动视图。而且基于 Presenter 的 data 可以使用任何平台、任何框架、任何技术进行渲染。

    3. 测试性

    假如 View 和 Model 之间的紧耦合,在 Model 和 View 同时开发完成之前对其中一方进行测试是不可能的。出于同样的原因,对 View 或 Model 进行单元测试很困难。现在,MVP模式解决了所有的问题。MVP 模式中,View 和 Model 之间没有直接依赖,开发者能够借助模拟对象注入测试两者中的任一方。

    举个逻辑复用的例子,比如 OMI 团队发起的 snake-mvp 项目,下面的几个项目的 model 和 presenter 几乎一模一样,完全复用,只是渲染视图层根据不同的框架做了不同的适配。

    9c072e48e2d5d5b405804ae07b1b9d71.png比如 react 的视图层 :

    import React from 'react'import Game from '../game'import store from '../../stores/index'import { $ } from 'omis'require('../../utils/css').add(require('./_index.css'))export default $({render() {const { store } = $const { paused } = store.datareturn <div className="container">
    <h1>[P]REACT + OMIS SNAKEh1>
    <Game>Game>
    <div className="ctrl">
    <div className="btn cm-btn cm-btn-dir up" onClick={store.turnUp}><i>i><em>em><span>Upspan>div>
    <div className="btn cm-btn cm-btn-dir down" onClick={store.turnDown}><i>i><em>em><span>Downspan>div>
    <div className="btn cm-btn cm-btn-dir left" onClick={store.turnLeft}><i>i><em>em><span >Leftspan>div>
    <div className="btn cm-btn cm-btn-dir right" onClick={store.turnRight}><i>i><em>em><span >Rightspan>div>
    <div className="btn cm-btn space" onClick={store.toggleSpeed}><i>i><span >Gearspan>div>
    <div className="btn reset small" onClick={store.reset}><i >i><span >Resetspan>div>
    <div className="btn pp small" onClick={store.pauseOrPlay}><i>i><span >{paused ? 'Play' : 'Pause'}span>div>div>div>
    },useSelf: ['paused'],store
    })

    Q & A

    Q: 比如我一个弹窗组件,可能在很多页面使用,也可能在同一个页面使用多次;如果使用store来作为组件间通信的话,怎么应用可以实现组件是纯组件而不跟业务相关呢?

    A: 纯组件不用不用 create 创建,且该组件内使用 triggerEvent 通知父组件改变 store.data 或者调用 store 的方法与外界通讯。

    d61928ec7bb1063aea0681ec492b3282.png

    展开全文
  • 1、原理概述iOS 应用程序常见的退出场景包括:• 双击 Home 键切换到其它应用程序• 按 Home 键让当前应用程序进入后台• 双击 Home 键上滑强杀当前应用程序• 当前应用程序发生崩溃导致应用程序退出iOS 应用程序...

    3d5d8b15b83f26d7139ca8d7d2da1c57.png

    1、原理概述

    iOS 应用程序常见的退出场景包括:

    • 双击 Home 键切换到其它应用程序

    • 按 Home 键让当前应用程序进入后台

    • 双击 Home 键上滑强杀当前应用程序

    • 当前应用程序发生崩溃导致应用程序退出

    iOS 应用程序常见的启动场景包括:

    • 冷启动:指应用程序被系统杀死后,在这种状态下启动的应用程序。

    • 热启动:指应用程序没有被系统杀死,仍在后台运行,在这种状态下启动的应用程序。

    在介绍应用程序退出($AppEnd)和启动($AppStart)事件的全埋点实现方案之前,我们先简单介绍一下 iOS 应用程序状态相关的内容。

    1)应用程序状态

    大家都知道,对于一个标准的 iOS 应用程序来说,在不同的时期会有不同的状态,如下图 2-1 所示。

    14e890195888ac8f3c8029f8ff076a50.png

    图 2-1 iOS 应用程序状态转换图

    正常情况下,iOS 应用程序主要有五种常见的状态。

    • Not running

    非运行状态。指应用程序还没有被启动,或者已被系统终止。

    • Inactive

    前台非活动状态。指应用程序即将进入前台状态,但当前未接收到任何事件(它可能正在执行其它代码)。应用程序通常只在转换到其它状态时才会短暂地进入这个状态。

    • Active

    前台活跃状态。指应用程序正在前台运行,可接收事件并进行处理。这也是一个 iOS 应用程序处于前台的正常模式。

    • Background

    进入后台状态。指应用程序进入了后台并可执行代码。大多数应用程序在被挂起前都会短暂地进入这个状态。

    • Suspended

    挂起状态。指应用程序进入后台但没有执行任何代码,系统会自动的将应用程序转移到此状态,并且在执行此操作之前不会通知应用程序。挂起时,应用程序会保留在内存中,但不执行任何代码。当系统出现内存不足情况时,系统可能会在未通知应用程序的情况下清除被挂起的应用程序,为前台应用程序尽可能腾出更多的运行资源。

    在应用程序不同状态转换的过程中,系统会回调实现了 UIApplicationDelegate 协议的类的一些方法,并发送相应的本地通知(系统会先回调相应的方法,待回调方法执行完后,再发送相应的通知),回调方法和本地通知的对应关系可参考如下表 2-1 所示。

    a7b2fa82d0caabfb32b7fbf0c33666e9.png

    表 2-1 回调方法和本地通知对应关系

    关于 iOS 应用程序状态更详细的内容,也可参考苹果官网文档介绍。

    (1)应用程序退出

    通过上面介绍的内容可知,当一个 iOS 应用程序退出时,就意味着该应用程序进入了“后台”,即处于 Background 状态。因此,对于实现 $AppEnd 事件的全埋点,我们只需要注册监听 UIApplicationDidEnterBackgroundNotification 通知,然后在收到通知时触发 $AppEnd 事件,即可达到 $AppEnd 事件全埋点的效果。

    (3)应用程序启动

    应用程序的启动,一般情况下,大致可以分为两类场景:

    • 冷启动

    • 热启动(从后台恢复)

    不管是冷启动还是热启动,触发 $AppStart 事件的时机,都可以理解成是当“应用程序开始进入前台并处于活动状态”,也即前文介绍的 Active 状态。

    因此,为了实现 $AppStart 事件的全埋点,我们可以注册监听 UIApplicationDidBecomeActiveNotification 本地通知,然后在其相应的回调方法里触发 $AppStart 事件。

    通过测试可以发现,仍有以下几个特殊场景存在问题:

    • 下拉通知栏并上滑,会触发 $AppStart 事件

    • 上滑控制中心并下拉,会触发 $AppStart 事件

    • 双击 Home 键进入切换应用程序页面,最后又选择当前应用程序,会触发 $AppStart 事件

    以上几个场景均会触发 $AppStart 事件,明显与实际情况有所不符。

    那这些现象是什么原因导致的呢?

    我们继续分析可以发现以下几个现象:

    • 下拉通知栏时,系统会发送 UIApplicationWillResignActiveNotification 通知;上滑通知栏时,系统会发送 UIApplicationDidBecomeActiveNotification 通知

    • 上滑控制中心时,系统会发送 UIApplicationWillResignActiveNotification 通知;下拉控制中心时,系统会发送 UIApplicationDidBecomeActiveNotification 通知

    • 双击 Home 键进入切换应用程序页面时,系统会发送 UIApplicationWillResignActiveNotification 通知,然后选择当前应用程序,系统会再发送 UIApplicationDidBecomeActiveNotification 通知

    很容易总结出规律:在以上几个场景下,系统均是先发送UIApplicationWillResignActiveNotification 通知,然后再发送 UIApplicationDidBecomeActiveNotification 通知。而我们又是通过注册监听 UIApplicationDidBecomeActiveNotification 通知来实现 $AppStart 事件全埋点,因此均会触发 $AppStart 事件。

    那如何解决这个问题呢?

    在解决这个问题之前,我们先看另一个现象:不管是冷启动还是热启动,系统均没有发送 UIApplicationWillResignActiveNotification 通知。

    因此,只要在收到 UIApplicationDidBecomeActiveNotification 通知时,判断之前是否收到过 UIApplicationWillResignActiveNotification 通知,若没有收到,则触发 $AppStart 事件;若已收到,则不触发 $AppStart 事件。这样即可解决上面的问题。

    (4)被动启动

    被动启动?什么意思?完全没有听说过!

    你若有这些疑问,那就对了!因为这完全是我们神策数据自创的一个名词。

    在 iOS 7 之后,苹果新增了后台应用程序刷新功能,这个功能允许操作系统在一定的时间间隔内(这个时间间隔根据用户不同的操作习惯而有所不同,可能是几个小时,也可能是几天),拉起应用程序并同时让其进入后台运行,以便应用程序可以获取最新的数据并更新相关内容,从而可以确保用户在打开应用程序的时候可以第一时间查看到最新的内容。例如新闻或者社交媒体类型的应用程序,可以使用这个功能在后台获取到最新的数据内容,在用户打开应用程序时可以缩短应用程序启动和获取内容展示的等待时间,最终提升产品的用户体验。

    后台应用程序刷新,对于用户来说可以缩短等待时间;对于产品来说,可以提升用户体验;但对于数据采集 SDK 来说,可能会带来一系列的问题,比如:当系统拉起应用程序(会触发 $AppStart 事件)并同时让其进入后台运行时,应用程序的第一个页面(UIViewController)也会被加载,也即会触发一次页面浏览事件($AppViewScreen 事件),这明显是不合理的,因为用户并没有打开应用程序,更没有浏览第一个页面。其实,整个后台应用程序刷新的过程,对于用户而言,完全是透明的、无感知的。

    因此,在实际的数据采集过程中,我们需要避免这种情况的发生,以免影响到正常的数据分析。

    我们把由 iOS 系统触发的应用程序自动进入后台运行的启动,称之为(应用程序的)被动启动,使用 $AppStartPassively 事件来表示。

    后台应用程序刷新是最常见的造成被动启动的原因之一。而后台应用程序刷新只是其中一种后台运行模式,还有一些其它后台运行模式同样也会触发被动启动,我们下面会详细介绍。

    使用 Xcode 创建新的应用程序,默认情况下后台刷新功能是关闭的,我们可以在 Capabilities 标签中开启 Background Modes,然后就可以勾选所需要的功能了,如下图 2-2 所示:

    636966199d779d25f47f476e8f86d65f.png

    图 2-2 开启 Background Modes

    通过上图 2-2 我们可以看到,还有如下几种后台运行模式,它们同样也会导致触发被动启动($AppStartPassively 事件):

    • Location updates:此模式下,会由于地理位置变化而触发应用程序启动

    • Newsstand downloads:这种模式只针对报刊杂志类应用程序,当有新的报刊可下载时,会触发应用程序启动

    • External Accessory communication:此模式下,一些 MFi 外设通过蓝牙或者 Lightning 接头等方式与 iOS 设备连接,从而可在外设给应用程序发送消息时,触发对应的应用程序启动

    • Uses Bluetooth LE accessories:此模式与 External Accessory communication 类似,只是无需限制 MFi 外设,而需要的是 Bluetooth LE 设备

    • Acts as a Bluetooth LE accessory:此模式下,iPhone 作为一个蓝牙外设连接,可以触发应用程序启动

    • Background fetch:此模式下,iOS 系统会在一定的时间间隔内触发应用程序启动,去获取应用程序数据

    • Remote notifications:此模式是支持静默推送,当应用程序收到这种推送后,不会有任何界面提示,但会触发应用程序启动

    2、实现步骤

    完整的项目源码后续会 release 给大家。

    3、知识点

    • UIApplicationDelegate

    • Background App Refresh

    本文作者:王灼洲,神策数据合肥研发中心负责人,在深度服务超过 1000 家企业客户后,依托丰富的技术沉淀与经验积累,推《iOS 全埋点解决方案》白皮书,白皮书中讲解了基于 iOS 平台的数据埋点技术和解决方案,介绍了 iOS 全埋点实现原理,包括应用程序启动和退出、页面浏览、控件点击、手势等,具有极强的理论性和实操性,填补行业空白。

    相关阅读

    从零到一搭建推荐系统指南

    如何从零到一落地精细化运营活动

    智能运营案例介绍: 每日一淘

    金融业数字化转型 MVP 实践

    如果你对本文感兴趣,可关注神策数据·用户行为洞察研究院专栏;
    如果你想免费下载《iOS 全埋点解决方案》白皮书,可在神策数据官网下载。

    展开全文
  • 程序分享票据shareTickets通常开发者希望转发出去的小程序被二次打开的时候能够获取到一些信息,例如群的标识。现在通过调用 wx.showShareMenu 并且设置 withShareTicket 为 true ,当用户将小程序转发到任一群聊...

    小程序分享票据shareTickets

    通常开发者希望转发出去的小程序被二次打开的时候能够获取到一些信息,例如群的标识。现在通过调用 wx.showShareMenu 并且设置 withShareTicket 为 true ,当用户将小程序转发到任一群聊之后,此转发卡片在群聊中被其他用户打开时,可以在 App.onLaunch 或 App.onShow 获取到一个 shareTicket。通过调用 wx.getShareInfo() 接口传入此 shareTicket 可以获取到转发信息。

    • 和场景值scene一样,shareTicket也是在App.onShow中获取比较合理
    • 必须在分享前调用wx.showShareMenu方法,否则不会带分享票据
    //分享前share.js
    
    • 只有分享到任一群聊,shareTicket才会有值,否则是undefined
    • shareTicket也可以用来区分转发消息的场景
    • shareTicket主要用来获取转发详情,传入wx.getShareInfo()中获取加密数据,需要后端配合,返回解密数据
    注意:注意:注意
    鉴于官方“分享监听”能力调整,网上90%的滞后代码,误人子弟,在此必须给自己一个小要求,定期复读自己的文章,根据当时能力水平,提高文章质量,修正错误和滞后信息(吐槽一下度娘已死,没人打我吧?打我就删除)

    类似如下代码,现在已不支持回调

    23caa355aa310dd397098793d0582007.png

    此次调整可能影响到三种分享功能的用法

    第一种:判断用户是否分享成功,进而给予用户奖励。

    例如:小程序提示用户“分享到5个群,可以获得一张20元的优惠券”。

    这类诱导用户分享的行为是我们平台所不倡导的,后续将没有办法实现。

    第二种:分享完成后变更当前的页面状态

    例如:赠送礼品场景下,用户点击“赠送”按钮,将礼品分享出去,分享成功后,界面展示“等待领取”。

    这类场景,我们建议可以适当调整交互方案。例如在分享后继续保留“赠送”按钮,但在页面上提示用户一个礼品只能被一人领取,重复赠送无效。

    第三种:通过用户分享之后的 shareTicket 获取群唯一标识 openGId ,以显示对应群的相关信息。

    例如:通过分享小程序到某个群里,可以查看该群内成员的排行榜。

    此次调整后,用户分享完成后无法立刻显示该群的排行榜信息,但仍可在用户从群消息点击进入小程序时显示该群的排行榜信息。

    分享监听能力调整后,对于分享的骚操作基本在App.onShow中进行,相当于授权那样,必须打开转发消息才能获取转发详情,

    下面用代码来给大家分析分析。

    方案:微信在更新分享接口后,原有的在onShareAppMessage中直接拿shareTicket已不复存在。根据最新文档显示,需要在App.onLaunch()跟App.onShow()中获取。

    index.js

    Page

    index.wxml

    <!--

    app.js

    //app.js
    

    这里也是需要用到解密的,和运动步数的那篇文章一样解密就可以了,然后返回openGid 就好了。

    注意事项

    1:必须调用这个接口wx.showShareMenu({withShareTicket: true}),否则在App.onLaunch()跟App.onShow()时,你拿不到shareTicket.

    2:微信开发者工具可以模拟1044的场景,但是不会显示群名称,因为你不在群里。所以测试的时候,自己拉个微信群,然后分享到测试群,就能拿到群名称。

    以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要请戳这里链接 或者关注咱们下面的专栏
    PHP大神进阶zhuanlan.zhihu.com
    b0ad64a417686a2ccdc674df6579d720.png
    展开全文
  • 应用程序的启动,一般情况下,大致可以分为两类场景: • 冷启动 • 热启动(从后台恢复)不管是冷启动还是热启动,触发 $AppStart 事件的时机,都可以理解成是当“应用程序开始进入前台并处于活动状态”, 也即前文...
  • 服务器计算(Severless computing,简称 Serverless)现在是软件架构圈中的热门话题,国外三大云计算供应商(Amazon、Google 和 Microsoft)都在大力投入这个领域,涌现了不计其数的相关书籍、开源框架、商业产品、...
  • plsql无监听程序问题 错误:ORA-12560: TNS: 协议适配器错误 注:有TNS字样,就是监听的问题啦。 查看监听状态:lsnrctl status  正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(POR TNS-12541: TNS: 无监听程序 ...
  • 前一段时间,由于需要修改了数据库的主机名,当时修改数据库主机名使用即时生效的方法修改的,昨天公司停电,重启数据库,登录plsql就报错了“ora-12541:TNS:无监听程序”错误主要原因就是由于我修改了主机名,导致...
  • 什么是优雅重启在不停机的情况下,就地部署一个应用程序的新版本或者修改其配置的能力已经成为现代软件系统的标配。这篇文章讨论优雅重启一个应用的不同方法,并且提供一个功能独立的案例来深挖实现细节。如果你不...
  • 最近在使用plsql连接本地oracle数据库的时候,在同一网络环境中,出现了可以连接本地oracle,但是远程主机却无法连接,显示出无监听程序的问题,一直没法解决,后来才发现是本地oracle配置出了问题。解决办法如下:...
  • 恰好碰到此成绩,或应承...************************************************************************************本文链接:http://www.51zdd.com/oracle_plsql_remote_connection.html>本机为win7 32位系统,...
  • PLSQL Developer出现无监听程序错误

    千次阅读 2018-11-11 18:54:59
    第一次用pl/sql developer显示无监听程序错误 解决办法: 两个都要启动 页面在:计算机–&gt;右击管理–&gt;服务和应用程序–&gt;服务
  • PLsql连接提示无监听程序

    千次阅读 2016-07-18 11:08:51
    连接Oracle时报错ORA-12541: TNS: 无监听程序   1、在安装目录下找到tnsnames.ora文件,修改localhost值为本机的IP地址,如下所示: 这个文件存在于下面的路径中:“盘名:\oracle\product\10.2.0\db_1\NETWORK\...
  • 我在PLSQL DEVELOPE的时候怎么也连不上,提示是TNS无监听程序不过我是有开啊,你看看我在SQLPLUS下的操作吧C:\Documents and Settings\fujitsu>net start oracleservicefayxlOracleServiceFAYXL 服务正在启动 .......
  • 今天在使用PLSQL Developer连接Oracle 数据库的时候,连接不上了。以前都能连接上的,很久没使用了,突然就连接不上了。于是网上搜索了下,出现这种情况的原因很多,在这里我说一下我的解决方案吧!解决方案:将...
  • Oracle总结【PLSQL学习】 Mysql免安装配置教程(图文版) Oracle11安装和卸载教程 SqlServer安装和连接JDBC资料 JDBC JDBC总结 JDBC【介绍JDBC、使用JDBC连接数据库、简单的工具类】 JDBC【PreparedStatment、批处理...
  • 什么是优雅重启在不停机的情况下,就地部署一个应用程序的新版本或者修改其配置的能力已经成为现代软件系统的标配。这篇文章讨论优雅重启一个应用的不同方法,并且提供一个功能独立的案例来深挖实现细节。如果你不...
  • 内容提要: vue-element-admin的登录功能分析。 最近学习不在状态,争取明天开始调整好。... @keyup.enter.native="handleLogin" 监听键盘 enter 按下后的事件。 handleLogin:点击登录后调用的方法。
  • 整理|赵钰莹 Next.js 是一个用于在服务端渲染 React 应用程序的简单框架,Next.js 8 为移动应用程序新增了服务器功能。Next.js 是创建 JavaScript 应用程序的 React 框架,开发人员可基于此快速创建可自定义的...
  • 2 掌握select子句 distinct关键字 3 掌握字符串连接,order by排序 4 掌握模糊查询,where子句,等值判断,非等值判断,为空判断等条件 对应视频: http://www.itbaizhan.cn/course/id/85.html 对应文档: 对应作业 8....
  • 求教,PLSQL连接报错无监听程序 最近在学程序,已经到了学习oracle数据库阶段了额,我在2003的虚拟机安装了服务端的oracle,然后在本机电脑分别安装了navicat和PLSQL远程连接端,我已经配置了两个,包括环境变量,...
  • oracle无监听程序的解决方法(PLSQL)Oracle ORA12514 监听程序当前无法识别连接描述符中请求的服务
  • 修改了LISTENER.ORA , tnsnames.ORA添加之后LISTENER.ORA就是这个样子的:# listener.ora Network Configuration File: D:\ENVIRONMENT\oracle\product\11.2.0\dbhome_1\network\admin\listener.ora# Generated by ...
  • 简介: 在我们开发 mPaaS 小程序的过程中,如果已有小程序 API 或事件无法满足开发需求,您也可以自行扩展。在我们开发 mPaaS 小程序的过程中,如果已有小程序 API 或事件无法满足开发需求,您也可以自行扩展。小程序...
  • 文章目录出现的问题问题定位 出现的问题 问题1: 提示TNS: 监听程序当前无法识别...PLSQL Developer提示TNS:无监听程序 或者 程序连接数据库失败( The Network Adapter could not establish the connection。 ) 问题...
  • PLSQL连接Oracle出现连接超时,无监听程序。 前言 之前安装好plsql的时候,登陆时出现了一次无监听程序,后来配置好了之后,换了个网络,又出现了连接超时和无监听程序,有点头大,好在之后解决了,下面是我个人遇到...
  • 微信小程序页面分享到朋友圈的功能,终于在安卓系统灰度测试了!目前只在安卓系统!只在安卓系统!只在安卓系统!iOS系统还没有办法体验。首先,我们看一下官方文档的描述,解读一下小程序分享到朋友圈的实现步骤和...
  • plsql ORA-12541:TNS:无监听程序

    千次阅读 2016-02-25 14:24:59
    先确定数据库已经启动了,监听启动也成功执行,控制台可以登录,但是plsqldev登不上报 ORA-12541:TNS:无监听程序 这样应该是listener.ora 文件配置错误检查修改 附linux正确的listener.ora文件配置 # ...
  • PLSQL连接远程Oracle出现ORA-12541: 无监听程序

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 212
精华内容 84
关键字:

plsql无监听程序