唐洋洋的个人博客

\(^o^)/~


  • 首页

  • 分类

  • 归档

  • 标签

使用Javascript做算法题(四)Longest Increasing Path in a Matrix

发表于 2017-12-18 | 分类于 leetcode算法题

前言

  • 最近在LeetCode上做出了第一道难度为hard的题:) 觉得这题中规中矩挺不错的,mark一下:题目地址

题目描述

  • 给出一个正整数矩阵,找出其中最长的递增路径的长度。只能上下左右移动,不能走对角线,不能环绕重复路线,不能超越边距。
    1
    2
    3
    4
    5
    6
    7
    8
    举例:
    输入:
    nums = [
    [9,9,4],
    [6,6,8],
    [2,1,1]
    ]
    返回 最长路径为4 路径为:1->2->6->9

解题思路

  • 基本思路分以下几个点:
  • 0.依次遍历输入的矩阵(二维数组),找出每个点作为起点时的最长递归路径长度,然后返回其中的最大值
  • 1.使用dfs递归方法去遍历查找各个方向,建立一个表示移动方向的数组,每次dfs查找一个新的节点是遍历这个数组,得出最大值作为这个点为起点的最长路径,保存到dp中
  • 2.新建一个二维数组作为dp数组,每次得到一个坐标点为起点的最大长度时存入dp数组相应位置,以后再查找到这个坐标点时直接返回之前保存的值避免重复运算
  • 上面可能说的有点乱,大概就是dfs遍历和dp数组结合,难度其实并不算大,但是可以优化的细节还是挺多的,下面代码附有具体注释

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @param {number[][]} matrix
* @return {number}
*/
var longestIncreasingPath = function(matrix) {
if (!matrix || matrix.length === 0) {
return 0
}
var direct = [[0, -1], [0, 1], [-1, 0], [1, 0]] // 方向数组
var max = 1 // 全局最大值
var dp = new Array() // dp二维数组,保存每个点作为起点的最大路径长度
var width = matrix[0].length
var deep = matrix.length
while (deep) {
dp.push(new Array(width).fill(0)) // 每个点初始长度为0,dfs遍历到不为0的情况直接返回她的值(因为是之前已经得到了)
deep--
}
deep = matrix.length
var dfs = function (i, j) {
if (dp[i][j] > 0) return dp[i][j];
var mx = 1
for (var k = 0; k < direct.length; k++) {
var x = i + direct[k][0]
var y = j + direct[k][1]
if (x < 0 || x >= deep || y < 0 || y >=width) { // 超过范围跳过
continue
}
if (matrix[x][y] <= matrix[i][j]) {// 不是递增跳过
continue
}
var len = dfs(x, y) + 1
mx = Math.max(mx, len)
}
dp[i][j] = mx
return mx
}
for (var i = 0; i < deep; i++) {
for (var j = 0; j < width; j++) {
max = Math.max(max, dfs(i, j))
}
}
return max
};

python入门(二)

发表于 2017-12-18 | 分类于 后端 , python

函数

  • python中自带一些全局函数,比如在js中封装在math对象的数学方法:max、min、abs,也有可以用于数据转换的函数:int、str;用于遍历的方法:all、any。官网上可以看到https://docs.python.org/3/library/functions.html

定义函数

  • def 用来定义函数。
  • isinstance()方法可以用来判断参数类型,第一个参数是需要判断的对象,第二参数是候选的变量类型,多个类型之一的话要用tuple类型表示
    1
    isinstance(x, (int, float))

定义空函数

  • pass相当于一个占位符,表示什么也不做
    1
    2
    def nop():
    pass

设置函数的默认参数

  • 使用等号设置参数的默认值,和es6语法一致。需要注意的是:必选参数在前,默认参数在后
  • 定义默认参数要牢记一点:默认参数必须指向不变对象!
    1
    2
    3
    4
    5
    6
    def power(x, n=2):
    s = 1
    while n > 0:
    n = n - 1
    s = s * x
    return s

定义可变参数

  • 类似于es6中的…(function (…args) {}),这里的numbers是一个tuple类型的数据

    1
    2
    3
    4
    5
    def calc(*numbers):
    sum = 0
    for n in numbers:
    sum = sum + n * n
    return sum
  • 传入参数时也可以使用*把数组[]和元组()类型的参数 => 变成可变参数

    1
    calc(*[1,2,3], *(4,5,6))

关键字参数

  • python中传入参数时,可以用关键字来修饰参数,这样甚至就可以不用考虑传参的顺序。
  • 未出现在参数中的关键字参数会进入other(一个dict类型的数据)里面,同样的也可以使用**来讲dict类型的数据转换成可变参数传入。
  • 注意:多余的未知参数会进入到args中,多余的关键字参数则会进入到other中

    1
    2
    3
    4
    def person(name, age, *args, **other):
    print('name:', name, 'age:', age, 'other:', other, 'args:', args)
    # person(age=45, name='Adam')
    person('Adam', 45, 123, 456, yaho='lalala', **{'city':'Beijing', 'job':'Engineer'})
  • 但是使用关键字参数时有很多限定条件:关键字参数必须放在一般参数的后面

命名关键字参数

  • 定义参数时,跟在或者可变参数(args)后面的参数,被视为命名关键字参数,这些参数在调用函数传参时,必须传入参数名。
    1
    2
    3
    def person(name, age, *或者*args, city, job):
    print(name, age, city, job)
    person('Adam', 45, city='Beijing', job='Engineer')

参数组合

  • 在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

说一句递归

  • python并不能实现尾调用优化,递归调用函数时,每一次调用都会增加一次栈帧,超过上线后就会报错。

python入门(一)

发表于 2017-12-14 | 分类于 后端 , python

Python简介

  • Python是用来编写应用程序的高级编程语言
  • Python为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作“内置电池(batteries included)”

Python特(缺)点

  • 第一个缺点就是运行速度慢,和C程序相比非常慢,因为Python是解释型语言,你的代码在执行时会一行一行地翻译成CPU能理解的机器码,这个翻译过程非常耗时,所以很慢。而C程序是运行前直接编译成CPU能执行的机器码,所以非常快。
  • 第二个缺点就是代码不能加密。如果要发布你的Python程序,实际上就是发布源代码,这一点跟C语言不同,C语言不用发布源代码,只需要把编译后的机器码(也就是你在Windows上常见的xxx.exe文件)发布出去。要从机器码反推出C代码是不可能的,所以,凡是编译型的语言,都没有这个问题,而解释型的语言,则必须把源码发布出去。

Python 安装

  • mac上面:安装Homebrew后,直接通过命令brew install python3安装即可。

编辑器和解释器

  • python是解释型语言,使用上面的命令安装后就有自带的解释器。在命令行输入python可以进入python交互模式,类似于node。输入python3 文件名.py即可执行python文件
  • 文本编辑器使用sublime Text即可

python基础语法

  • Python的语法比较简单,采用缩进方式,循环条件语句都不需要括号
  • Python区分大小写
  • python中能直接处理的数据类型:整数、浮点数、字符串、布尔值、空值(None)。也有变量,变量不需要声明类型。
  • 这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错(比如java)。
  • 一些操作符:and == && . . . or == || . . . 10 // 3 = 1 (Math.floor(10/3))
  • str通过encode()方法可以编码为指定的bytes,相反的有decode方法
  • 使用 % 来格式化字符串
    1
    'Hi, %s, you have $%d.' % ('Michael', 1000000)

list和tuple

python内置list类型(相当于数组);有序列表:tuple(元组),tuple一旦初始化就不能修改;只有1个元素的tuple定义时必须加一个逗号,来消除歧义

dict和set

  • Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。

    1
    2
    d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
    d['Michael']
  • dict获取值的方法:get()获取,第二个参数是取不到的时候返回值避免报错,key in d也可以判断dict中是否有对应的键值。不能用点.操作符!

    1
    d.get('Thomas', -1)
  • dict与list区别:dict查找和插入速度快,但是占用空间和资源更多。

  • dict可以用在需要高速查找的很多地方,在Python代码中几乎无处不在,正确使用dict非常重要,需要牢记的第一条就是dict的key必须是不可变对象。
  • set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。(只是一组无重复的不可变对象的key?)

小结

  • 这里我就对python和JavaScript在语法上进行一个初步的比较:
  1. 两者都是解释形的弱类型语言,变量的类型可随时改变。
  2. 在python中的数据类型更多:比如类似于js数组的就有list/tuple。他们之间的比较后面有时间我再深入了解下。
  3. 条件语句以及循环语句都是类似的,但是在python中没有用大括号{}来表示层级结构,而是通过冒号:以及缩进来控制的。

用react-native实现一个数独游戏(总结)

发表于 2017-12-13 | 分类于 react , react-native

前言

  • 最近用react-native做了一个数独游戏的app,到今天基本功能已经全都完成并打包在真机上测试了,这里总结一下开发过程中的一些问题和实现的思路。
    image.png

环境和平台选择

  • 项目上读取的接口是我自己服务器上部署的,用node实现的接口,链接mysql数据库,数独题目就是从这里读取得到。因为服务器上只有http接口,所以app开发时也就选择的android版本。

功能及实现

  • 因为只是一个简单的demo,做的功能也比较简单:新游戏/继续游戏/选择难度。页面有三个:首页/棋盘页面/关于。
  • 页面导航使用的react-navigation,从首页进入棋盘页面时,会传参数过去表示是否是新游戏和游戏难度。

    1
    this.props.navigation.navigate('Main', {difficulty: text, newgame: true})
  • 数独棋盘的生成,是从数据库得到数据,然后再渲染出来。一开始我是打算随机生成棋盘的,但是发现生成的棋盘可能会出现多解的情况,最后想想还是算了,在网上找了几个数独题目存在数据库里。

  • 触发提交按钮后,会对棋盘进行验证,有空白没有填完则返回,然后再依次判断各个点是否满足数独条件,判断方法可参考LeetCode上的一道题:Valid Sudoku,下面还是贴下判断结果的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    let arr = this.state.arr
    let dp = {}
    let index = 0
    for (let a = 0; a < 3; a++) {
    for (let b = 0; b < 3; b++) {
    dp[`area${a}${b}`] = new Map()
    }
    }
    for (let i = 0; i < 9; i++) {
    dp[`x${i}`] = new Map()
    dp[`y${i}`] = new Map()
    }
    for (let i = 0; i < 9; i++) {
    for (let j = 0; j < 9; j++) {
    let temp = arr[index]
    if (temp !== '') {
    let xIndex = 'x' + j
    let yIndex = 'y' + i
    let areaIndex = 'area' + Math.floor(j/3) + Math.floor(i/3)
    if (dp[xIndex].has(temp) || dp[yIndex].has(temp) || dp[areaIndex].has(temp)) {
    return [false, '有空格填错了哦~', index]
    } else {
    dp[xIndex].set(temp, true)
    dp[yIndex].set(temp, true)
    dp[areaIndex].set(temp, true)
    }
    } else {
    return [false, '还有空格没填完哦~']
    }
    index++
    }
    }
    return [true]
  • 数据存储还是使用的react-native提供的AsyncStorage,并对其进行一层包装。未完成的棋局缓存起来,继续游戏的时候再调用。

  • 基本上就是这些了,以后有机会再补充。

react-native/react 开发中遇到的问题

发表于 2017-12-06 | 分类于 react , react-native

前言

  • 最近对react-native产生些兴趣,准备学一学,写写demo练练手,然而之前对react页不是很熟悉,所以一边学一边写code,一边看react-native的文档一边撘环境。中途还是遇到很多问题,写篇文章mark下。

react-native搭建开发环境时遇到的问题

  • 搭建Android开发环境:
  1. java版本不能装9.0+的,路径格式不同,Android-studio无法识别路径。
  2. Android SDK 要装25.0+的,不然会提示版本过低无法继续,然而教程(甚至是fb官网上)还是写的装23.0
  3. 模拟器,先在Android-studio打开Android模拟器之后,再在命令行执行react-native run-android命令
  4. 执行react-native run-*命令时,启动的packager server需要使用8081端口,确保这个端口没有占用!

RN中写样式

  • 本来平时我css就写得少,RN中这种变种的css写起来更蛋疼。。。。React-Native 样式指南
  • RN中的样式相当于css的缩减版,命名方式要用驼峰法,然后大部分属性都能和css中对应。布局:也有相对定位和绝对定位,也支持flex。官方推荐flex,可以解决大多数的布局情况。
  • 建议使用StyleSheet.create()生成样式的类名,也可以直接在组件上写”内联”样式。
  • RN中元素的宽高是没有单位的,直接用数字表示即可,也可以用flex来实现弹性宽高。
  • 另外就是要注意,不同的组件可以设置的样式是不同的,比如遇到无法设置宽高的组件就只能在它外层包个view,然后设置这个view的宽高。

RN中调试

  • 打开调试菜单:iOS模拟器中:Command⌘ + D 快捷键;Android模拟器对应的则是Command⌘ + M
  • Enable Live Reload:开启自动刷新
  • Enable Hot Reload:开启热加载
  • 访问控制台日志:在终端中运行:react-native log-ios或者log-android
  • 在开发者菜单中选择”Debug JS Remotely”选项,即可以开始在Chrome中调试JavaScript代码。点击这个选项的同时会自动打开调试页面 http://localhost:8081/debugger-ui.
  • 注:Chrome中并不能直接看到App的用户界面,而只能提供console的输出,以及在sources项中断点调试js脚本。

RN中的网络通信

  • 文档上介绍的是使用fetch,我也专门去了解了一下下,算是ajax的进化版,结合了一些html5的新特性。RN中也支持websocket。
  • 默认情况下,iOS会阻止所有非https的请求。如果你请求的接口是http协议,那么首先需要添加一个App Transport Security的例外,详细可参考这篇帖子。

RN中的数据存储

  • 在浏览器环境,我们可以使用cookie、localstorage或者sessionstorage来实现数据缓存,在RN中则不能使用以上的三种方式。
  • AsyncStorage是一个简单的、异步的、持久化的Key-Value存储系统,它对于App来说是全局性的。它用来代替LocalStorage。

react相关

  • react事件需要手动绑定this,或者使用箭头函数来声明

    1
    2
    3
    4
    this.handleClick = this.handleClick.bind(this);
    // 或者
    onClick={(e) => this.handleClick(e)}
    handleClick = () => {...}
  • react的组件必须以大写字母命名: 小写字母的标签会被认为是html的标签而不会被编译。一般表达式声明react的组件也要用大写字母开头:

    1
    2
    const SpecificStory = components[props.storyType];
    return <SpecificStory story={props.story} />;

Fetch初探

发表于 2017-11-22 | 分类于 Html

前言

  • fetch这个东西,之前也看到过有人介绍,但毕竟还是实验性阶段的东西,最近在看react-native的文档,讲到网络的地方,文档上是推荐地使用fetch,想必框架本身也对其有所封装。这里就先自己学习一下fetch相关的基础知识。

基本概念

  • Fetch 是一个现代的概念, 等同于XMLHttpRequest。它提供了许多与XMLHttpRequest相同的功能,但被设计成更具可扩展性和高效性。下图显示了fetch在浏览器中的支持情况,可以看到在主流浏览器的支持性还是不错的,实在不支持fetch的浏览器(比如IE)上面也可以使用相应的polyfill库。
    fetch在浏览器中的支持情况.png

具体用法

  • 使用fetch首先需要具备promise的相关知识,还涉及到一个Response对象,直接来看下代码,这是一段调用json格式数据接口的示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch('http://101.200.35.148:8081/todo/getTodo')
    .then(response => {
    response.json().then(
    // 这里的result就是最终的接口数据了
    result => {
    console.log(result)
    }
    )
    })
  • 在支持fetch的浏览器中,fetch方法是global的可以直接调用。上述代码是一个get请求(默认方法是get),fetch执行后会返回一个promise对象,这个promise对象resolve之后得到一个response对象(Response的实例)。

  • response对象相当于整个流程中的一个中间状态,它自带一个json方法用于解析从接口得到的数据,还有一个属性bodyUsed标识是否已经被解析,response.json()执行后也是返回一个promise对象,正确执行后then()这里得到的result对象就是最终的接口数据了。执行完json方法后,response对象的bodyUsed会变成true,表示这个response对象是已经被读取过了的,这时候如果再次调用response的json方法就会报错。

POST请求

  • 接着来看下fetch如何处理post请求,fetch方法可以接收第二个参数来设置发送请求的方法、header、请求参数等,下面是一个post请求的示例。

    1
    2
    3
    4
    5
    fetch("/post/api/", {
    method: "POST",
    body: "A=参数A&B=参数B",
    headers: new Headers()
    })
  • 其中body就表示请求参数,可根据具体情况自行调整数据结构;method设定请求方法,默认为get大小写皆可,headers用于设置发送请求的header。fetch在请求post的时候如果没有成功,则会尝试重新以get方法发送请求。

使用async/await

  • 因为多了一层response,这里的promise回调代码结构就略显复杂,所以用fetch时使用async/await代码就会精简许多,可读性也会大大提高,比如下面这样:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    async componentDidMount () {
    try {
    let res = await fetch('http://101.200.35.148:8081/todo/getTodo')
    let {result} = await res.json()
    this.setState({
    result
    })
    } catch(e) {
    console.log(e)
    }
    }

额外内容

  • fetch除了用于post/get调取接口以外还可以请求图片,下面是一段请求图片,然后将图片显示在dom上的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var myImage = document.querySelector('img');
    fetch('flowers.jpg')
    .then(function(response) {
    return response.blob();
    })
    .then(function(myBlob) {
    var objectURL = URL.createObjectURL(myBlob);
    myImage.src = objectURL;
    });
  • 可以看到代码第一步还是获取response对象,之后调用了response.blob()来得到一个blob对象,这里又涉及到了另一个新特性Blob,继续调用URL.createObjectURL可将blob对象转换为一个临时链接供img标签使用。Blob相关暂不多做介绍,这里只是为了说明一下fetch还有这一种用法。

  • 参考: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

JavaScript中的 抽象语法树 AST

发表于 2017-11-17 | 分类于 JavaScript

AST

  • 抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,一种编程语言的源代码,通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。 示意图.png
  • 程序代码本身可以被映射成为一棵语法树,而通过操纵语法树,我们能够精准的获得程序代码中的每一个精确的节点。例如声明语句,赋值语句,而这是用正则表达式所不能准确体现的地方。esprima提供了一个在线解析JavaScript代码的地址,可以清楚地观察到js代码被转化为JSON格式,由一个个具体的符号组成

作用

  • 抽象语法树的作用非常的多,比如编译器、IDE、压缩优化代码等。在JavaScript中,虽然我们并不会常常与AST直接打交道,但却也会经常的涉及到它。例如使用UglifyJS来压缩代码,实际这背后就是在对JavaScript的抽象语法树进行操作。
    示意图2.png

socket.io 实现在线聊天室

发表于 2017-11-15 | 分类于 后端

效果图.gif

websocket

  • WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。websocket链接建立后,服务端和客户端可通过连接通道自由通信。wx协议也有类似于https的wss协议,也是多了一层TLS协议。
  • websocket具体详细的内容就不多做介绍了,因为我这次使用的socket.io框架,写起来的语法和原生websocket语法还是有很大差别的。

Socket.io简介

  • socket.io可以看做是一个对websocket进行了封装的一个框架,目的也是为了实现客户端和服务端的全双工通信。socket.io的底层是engine.io,engine.io在不支持websocket的浏览器中,会使用ajax的长轮询实现效果。
  • 具体过程,浏览器和服务端首先建立xhr链接,第一次握手后,服务端返回浏览器一个upgrades字段,告诉前端可以升级为websocket协议,socket.io客户端的框架会进行环境监测支持websocket的话,就会将xhr链接升级为websocket。建立起一个websocket连接后,服务器和浏览器之间还会定期的ping-pong来确定网络链接是否正常。

具体实现

  • 再来看下具体实现的代码如何写,socket.io官网上面的文档和教程还是挺详细的,而我们要实现这个多人聊天室也只需要其中一些比较基本的api即可。

服务端部署

  • 服务端我用的node,直接用Npm安装socket.io,可以用socket.io直接搭建服务器,也可以在原有的express/koa上部署,我之前用的express,这里就是将socket.io添加到原有的express服务器上:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    let app = new (require('express'))()
    const server = app.listen(8081, () => {
    let peopleCounts = 0
    let io = require('socket.io')(server)
    io.on('connection', function (client) {
    // 这里的作用域是局部的,针对一个窗口发起的ws链接
    ++peopleCounts
    let uname = ''
    let uColor = require('./methods/util').getColor()
    client.on('disconnect', function (e) {
    --peopleCounts
    io.emit('sys message', {
    data: `系统消息: "${uname}"离开聊天室`,
    counts: peopleCounts
    })
    })
    require('./methods/getNickName')()
    .then(name => {
    uname = name
    io.emit('initUser', {
    name,
    color: uColor
    })
    io.emit('sys message', {
    data: `系统消息: "${name}"进入聊天室`,
    counts: peopleCounts
    })
    })
    .catch(e => { console.log(e) })
    client.on('chat message', function (data) {
    io.emit('message from server', data)
    })
    })
    console.log('Example app listening on port 8081!')
    })
  • 在express服务器搭建成功的回调函数里绑定socket.io,这里的io变量就是表示socket.io的服务端。

  • .on方法监听事件,这些事件来自建立websocket连接时或者浏览器端主动触发。
  • .emit触发事件,前端可以监听这些事件。比如一个客户端连接到服务端时,服务端会生成一个随机昵称(getNickName方法非必要),然后返回给客户端。

客户端部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import io from 'socket.io-client'
export default {
name: 'cheatRoom',
data () {
return {
description: '',
nickName: '',
counts: 1,
msg: [],
color: ''
}
},
created () {
window.vm = this
this.initSocket()
},
beforeDestroy () {
// socket链接与vue生命周期绑定,vue销毁时手动断开socket链接
this.socket.close()
}
methods: {
initSocket () {
const socket = io(this._config().preurl)
socket.on('connect', () => {
this.socket = socket
})
// 这里api语法类似于jQuery,支持once监听一次,off取消监听
// [https://socket.io/docs/client-api/#socket-on-eventname-callback](https://socket.io/docs/client-api/#socket-on-eventname-callback)
socket.once('initUser', ({name, color}) => {
this.nickName = name
this.color = color
})
socket.on('sys message', ({ data, counts }) => {
this.counts = counts
this.msg.push({
description: data,
sys: true
})
})
socket.on('message from server', (item) => {
item.sys = false
this.msg.push(item)
})
},
loadAction () {
let { description, nickName, color } = this
if (this.description.length === 0) {
return
}
this.socket.emit('chat message', {
description,
nickName,
color
})
this.description = ''
}
  • 前端需要用npm安装socket.io-client,不能直接使用原生的websocket语法建立连接。
  • 初始化时this._config().preurl是写在配置里的一个url地址,同其他接口地址前缀一样,但是这里的url地址是http协议,也就是说一开始是把一个http协议地址传给的socket.io,然后socket.io在建立http链接之后才升级成的websocket链接。
  • on方法,用来监听来自服务端的事件,once表示监听一次,还可以取消监听
  • emit触发事件,让服务端响应。这里的loadAction就表示发送信息。

2017/11/24 更新

  • 客户端我用的vue-cli搭建的项目,整个聊天室就是在一个vue实例中,socket连接与vue实例的生命周期绑定,created的时候建立连接,beforeDestroy的时候断开连接

结语

  • 整体写法有点像观察者模式,服务端和客户端都是以监听/触发事件的形式完成通信,完全不同于传统的ajax交互方式,十分有趣。

微信小程序 scroll-view实现锚点滑动

发表于 2017-11-10 | 分类于 JavaScript

前言

  • 最近开始做小程序,通读一遍文档再上手并不算难,但不得不说小程序里还是有一些坑。这里说一下如何实现页面锚点跳转,一个城市列表的效果示意图如下:
    city.gif
  • 因为在微信小程序的环境中不能想在浏览器里设置标签,或者操作dom滚动,传统做法就行不通了,一切都得按小程序的文档来。
  • 一开始我们的做法是使用boundingClientRect()方法获取每个锚点的坐标,然后再用wx.pageScrollTo()方法滑动过去。结果发现效果不是很好,因为boundingClientRect方法返回的每个点的坐标会随着屏幕滑动而变化,可能还会引起页面抖动,最后还是选择scroll-view(可滚动视图区域)
    组件来实现锚点效果。

具体实现

  • 具体API就不赘述了,可以去看官方文档,这里讲几个需要注意的地方,下面是一个示意的scroll-view组件代码,上面的几个属性是必须的:

    1
    <scroll-view scroll-y style="height: 200px;" bindscroll="scroll" scroll-into-view="{{toView}}" >
  • scroll-into-view:这个绑定了一个属性,它的值应该是页面元素的id,设置它的值就可以跳转到ID对应的元素那里了。

  • scroll-y:添加这个属性标明是竖向滑动的,对应的scroll-x则表示横向滑动,竖向滑动时scroll-view必须设置一个固定的height
  • bindscroll:监听滑动,传给他一个事件,滑动时执行该事件
  • 文档上给的属性特别多,暂时只需要上述几个就可实现我们想要的效果。实现原理也很简单,内容部分,每个英文简写的view设置一个id,然后在导航list那里点击时,就把scroll-into-view的值设置成点击的那个id即可实现跳转。
  • 再说一下scroll-view的高度问题,这个一定要做适配的固定高度,不然在不同屏幕大小的手机上的显示效果有差异。

几点优化

  • 到这里功能基本都实现了,但后面还发现一些问题:如果要隐藏scroll-view的滚动条,需要设置css样式:::-webkit-scrollbar

    1
    2
    3
    4
    5
    ::-webkit-scrollbar {
    width: 0;
    height: 0;
    color: transparent;
    }
  • 还有就是点了一个锚点实现了跳转,这个时候你滚动页面再点之前点的锚点,页面就不会再跳转了,这个时候就需要监听滚动事件,滚动时将scroll-into-view属性的值清空。或者在每次锚点跳转后,再由一个异步操作将scroll-into-view属性的值清空。

2017/12/05补充:

  • scroll-view默认是无滑动动画的,需要滚动的动画效果需要在组件上设置:scroll-with-animation=’true’
  • 关于固定高度height的设置问题,一开始我以为这个高度和滚动元素的数目/高度有关,这个时候处理动态变化的列表就很麻烦。后面在网上看到的一个方法就是使用wx.getSystemInfo方法得到windowHeight,把这个设置为scroll-view的高度(单位为px)即可。

javascript事件流、事件代理、target和currentTarget

发表于 2017-10-31 | 分类于 JavaScript

前言

  • 这篇讲的内容是非常基础的,关于javascript中的事件相关可能不太全面,只是围绕几个知识点。可能现在mvvm框架用多了也不用多去在意dom和event,但是原生的基础知识还是需要掌握的(万一以后面试问到了呢~~)
  • Javascript与html之间的交互是通过事件来实现的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间,可以用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。

事件流

  • 事件流描述的是从页面接收到事件的顺序,一些历史发展的内容这里就不提了,直接讲”DOM2级事件”,事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。最先发生的是事件捕获,然后是具体的实际目标收到事件,最后才是冒泡阶段,可以在这个阶段对事件做出响应。

网上找的示意图.png

添加事件监听

  • 给元素添加事件监听的方法,可以直接在html上添加或者在javascript中指定相关的事件处理程序,但是需要注意,这种以’on’开头的事件属于’DOM0’级事件,重复定义时会覆盖掉原事件,而使用addEventListener则不会覆盖之前添加的。

    1
    <input id='btn' value='click me' onclick='alert(this.value)'>
  • 还可以使用addEventListener/removeEventListener方法,所有dom节点都包含这方法,传入三个参数:事件名、处理事件的函数、标示在捕获还是在冒泡阶段调用回调函数的布尔值。

  • addEventListener添加的事件处理函数只能用removeEventListener方法移除,传入参数与addEventListener相同,意味着addEventListener添加的匿名函数无法移除,所以在用addEventListener添加处理函数时不能使用匿名函数。环境不支持addEventListener的时候,还可以使用attachEvent/detachEvent,这里就不多做介绍了。
    1
    2
    3
    4
    let btn = document.getElementById('btn')
    let handler = function () {alert(this.id)}
    btn.addEventListener('click', handler,false)
    btn.removeEventListener('click', handler,false)

事件委托

  • 事件委托是什么呢?最简单来说:在父元素上添加可以处理子元素事件的事件处理函数。利用了事件冒泡,指定一个事件处理函数,来处理同一种类型的多个事件。这里顺便一起说下target和currenttarget的区别,target是触发事件的最具体的元素, currenttarget是绑定事件的元素(在函数中一般等于this)。下面具体代码示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <div id="outter">
    <div id="inner"></div>
    </div>
    var outter = document.querySelector('#outter')
    outter.addEventListener('click',function(e){
    console.log(e.target.id) //inner
    console.log(e.currentTarget.id) //outter
    console.log(this === e.currentTarget) //true
    },false);
123
TokenYangForever

TokenYangForever

放点随笔文章

25 日志
14 分类
18 标签
© 2017 TokenYangForever
由 Hexo 强力驱动
主题 - NexT.Muse