小讴

小讴 查看完整档案

北京编辑北京印刷学院  |  广告学 编辑中电智绘系统技术  |  前端高级工程师 编辑 github.com/simon9124 编辑
编辑

从前做策划、半路出家的前端程序猿,爱吉他爱户外爱篮球更爱写代码,努力为了更好的生活。大家可添加我微信(powerful_simon),互相交流互相学习,一起弹琴唱歌打球爬山。同时还望前辈们多多指点,多多帮忙内推~

个人动态

小讴 发布了文章 · 9月21日

《javascript高级程序设计》学习笔记 | 5.7.单体内置对象

欢迎关注前端小讴的github,阅读更多原创技术文章

单体内置对象

相关代码 →

  • 由 ECMAScript 实现提供的,不依赖于宿主环境的对象,在 ECMAScript 程序执行之前就已经存在
  • Global 和 Math

Global 对象

  • 不属于任何其他对象的属性和方法,最终都是 Global 的属性和方法
  • isNan(),isFinite(),parseInt(),parseFloat()
URI 编码方法返回值
encodeURI()URI 编码,冒号、正斜杠、问号、井号除外
encodeURIComponent()URI 编码,所有非标准字符
decodeURI()URI 解码,只针对使用 encode()编码的字符
decodeURIComponent()URI 解码,所有非标准字符
var uri = 'https://element cn/#tab'
console.log(encodeURI(uri)) // https://element%20cn/#tab,本身属于URI的字符不编码(冒号、正斜杠、问号、井号)
console.log(encodeURIComponent(uri)) // https%3A%2F%2Felement%20cn%2F%23tab,编码所有非标准字符
console.log(decodeURI('https%3A%2F%2Felement%20cn%2F%23tab')) // https%3A%2F%2Felement cn%2F%23tab,只针对使用 encode()编码的字符解码
console.log(decodeURIComponent('https%3A%2F%2Felement%20cn%2F%23tab')) // https://element cn/#tab,解码所有非标准字符
eval 方法返回值
eval()将传入的参数当作实际的 EXMAScript 语句解析
  • 将传入的参数当作实际的 EXMAScript 语句解析
  • 被执行的代码具有与该执行环境相同的作用域链
  • eval() 创建的变量或函数不会被提升
  • 严格模式下,外部访问不到 eval() 中创建的变量或函数,为 eval 赋值也会报错
eval("console.log('hi')") // "hi",将传入的参数当作实际的 EXMAScript 语句解析
eval("function sayHi() {console.log('hi')}")
sayHi() // "hi",被执行的代码具有与该执行环境相同的作用域链
// console.log(msg) // 报错,eval() 创建的变量或函数不会被提升
eval("var msg = 'hi'")
console.log(msg) // "hi",被执行的代码具有与该执行环境相同的作用域链
Global 对象属性说明
undefined特殊值 undefined
NaN特殊值 NaN
Infinity特殊值 Infinity
Object构造函数 Object
Array构造函数 Array
Function构造函数 Function
Boolean构造函数 Boolean
String构造函数 String
Number构造函数 Number
Date构造函数 Date
RegExp构造函数 RegExp
Error构造函数 Error
EvalError构造函数 EvalError
RangeError构造函数 RangeError
ReferenceError构造函数 ReferenceError
SyntaxError构造函数 SyntaxError
TypeError构造函数 TypeError
URIError构造函数 URIError
// web浏览器将global全局对象作为window对象
var color = 'red'
function sayColor() {
  console.log(window.color)
}
window.sayColor() // red

Math 对象

max 和 min
min()确定一组数值的最大值
max()确定一组数值的最小值
console.log(Math.max(3, 54, 32, 16)) // 54
console.log(Math.min(3, 54, 32, 16)) // 3
  • 确定数组中的最大值/最小值
var values = [1, 2, 3, 4, 5]
console.log(Math.max.apply(Math, values)) // 把 Math 对象作为 apply()的第一个参数,将数组作为第二个参数
舍入方法
ceil()向上取整
floor()向下取整
round()四舍五入
console.log(Math.ceil(1.9)) // 2
console.log(Math.floor(1.9)) // 1
console.log(Math.round(1.9)) // 2
random 方法
random()返回大于 0 小于 1 的随机数
console.log(Math.random()) // 大于 0 小于 1 的随机数
  • 选择 1-10 之间的整数
console.log(Math.floor(Math.random() * 10 + 1))
  • 选择 m-n 之间的整数
function selectFrom(lowerValue, upperValue) {
  var choices = upperValue - lowerValue + 1 // 获取范围内的数量
  return Math.floor(Math.random() * choices + lowerValue)
}
var num = selectFrom(7, 32)
console.log(num)

总结 & 问点

  • 单体内置对象有哪些?分别有什么特点?
  • Global 对象有哪些方法?分别有什么用法?
  • eval 方法的用法和特点?
    严格模式下,eval 方法受到哪些限制?
  • Global 对象有哪些属性?
  • 在 web 浏览器中,将 global 全局对象作为什么对象实现的?
  • Math 对象有哪些方法?分别有什么用法?
  • 请用 Math 对象确定数组中的最大值/最小值
  • 请用 Math 对象随机生成 m-n 之间的整数
查看原文

赞 0 收藏 0 评论 0

小讴 发布了文章 · 8月26日

小程序重构 [cnode社区]:mpvue + 开源api,现已上线!

欢迎关注前端小讴的github,阅读更多原创技术文章

闲暇时光表浪费,由于技术栈一直用的vue,因此用 mpVue 开发了小程序版本的 cnode 社区,api 也都是官网开源的。有人会问 “mpvue不是已经停止维护了嘛?”——的确是的,而且使用起来有很有坑。但这是我第一次写小程序,找个最相近的语言最适合——无论选择哪种技术栈,产品要完整做出来才行。小程序现已上线,后续还将逐步尝试在taro、wepy、uni-app等框架开发,毕竟代码需要修改的比较少,争取实现“一套代码,多端通用”

image

欢迎star

https://github.com/simon9124/simon-cnode

效果预览

image
image

查看原文

赞 0 收藏 0 评论 0

小讴 发布了文章 · 8月25日

《javascript高级程序设计》学习笔记 | 5.6.基本包装类型

欢迎关注前端小讴的github,阅读更多原创技术文章

基本包装类型

相关代码 →

  • 3 个特殊的引用类型 BooleanNumberString
  • 每读取一个基本类型值,后台就创建一个对应的基本包装类型的对象
var s1 = 'some text'
var s2 = s1.substring(2) // 基本类型不是对象,本不应该有方法

// 因为后台自动完成了下列处理:
var s1 = new String('some text') // 1.创建String类型的实例
var s2 = s1.substring(2) // 2.在实例上调用指定方法
s1 = null // 3.销毁该实例
  • 上述步骤同样适用于 BooleanNumber类型
  • 引用类型和基本包装类型的主要区别在于对象的生存期

    • 引用类型:离开作用域前,对象一直在内存中
    • 基本包装类型:执行完,立即被销毁
var s1 = 'some text'
s1.color = 'red' // 创建String对象
console.log(s1.color) // undefined,s1对象已被销毁,同时再次创建String对象,新对象没有color属性
  • 对基本包装类型的实例调用 typeof 会返回"Object",所有基本包装类型的对象都会被转换为布尔值 true
  • Object 构造函数,根据传入值的类型返回基本包装类型的实例
var objText = new Object('some text') // 创建String的实例
console.log(objText instanceof String) // true
var objBoolean = new Object(false) // 创建Boolean的实例
console.log(objBoolean instanceof Boolean) // true
var objNumber = new Object(123) // 创建Number的实例
console.log(objNumber instanceof Number) // true
console.log(objNumber instanceof Boolean) // false
  • 使用 new 调用基本包装类型的构造函数 vs 直接调用同名的转型函数
var value = '25'
var number = Number(value) // 转型函数,转成Number类型
console.log(typeof number) // number
var obj = new Number(value) // 构造函数,构造Number对象实例
console.log(typeof obj) // object

Boolean 类型

var booleanObject = new Boolean(true) // 调用Boolean构造函数,并传入true/false
重写(继承)的方法返回值
valueOf()Boolean true / false
toString()String "true" / "false"
  • 基本类型布尔值 vs 引用类型布尔值:

    • typeof 操作符对基本类型返回 boolean,对引用类型返回 object
    • instance 操作符对基本类型返回 false,对引用类型返回 true
var falseObj = new Boolean(false) // falseObj是基本包装类型,被转换成true(所有基本包装类型对象都会被转换成true)
var falseValue = false // falseValue是基本类型,就是false

console.log(falseObj && true) // true
console.log(falseValue && true) // false
console.log(typeof falseObj) // object,基本包装类型
console.log(typeof falseValue) // boolean,基本类型
console.log(falseObj instanceof Boolean) // true,Boolean对象是Boolean类型的实例
console.log(falseValue instanceof Boolean) // false

Number 类型

var numberObject = new Number(10) // 调用Number构造函数,并传入数值
重写(继承)的方法返回值
valueOf()Number 数值
toLocaleString()String 字符串形式的数值
toString()String 字符串形式的数值
  • 为 toString()方法传递表示基数的参数,告诉返回几进制的字符串形式
var num1 = 10
console.log(num1.toString()) // 十进制,"10"
console.log(num1.toString(2)) // 二进制,"1010"
console.log(num1.toString(8)) // 八进制,"12"
console.log(num1.toString(10)) // 十进制,"10"
console.log(num1.toString(16)) // 十六进制,"a"
格式化字符串方法返回值
toFixed(num)String 指定小数位
toExponential(num)String 指数表示法
toPrecision(num)String 返回 fixed 或 exponential 格式
var num2 = 10000
console.log(num2.toFixed(2)) // 指定小数位,"10000.00"
console.log(num2.toExponential(2)) // 指数表示法,"1.00e+4"

var num3 = 99
console.log(num3.toPrecision(1)) // 用一位数表示,"1e+2"
console.log(num3.toPrecision(2)) // 用二位数表示,"99"
console.log(num3.toPrecision(3)) // 用三位数表示,"99.0"
  • 基本类型数值 vs 引用类型数值:

    • typeof 操作符对基本类型返回 number,对引用类型返回 object
    • instance 操作符对基本类型返回 false,对引用类型返回 true
var numberObject = new Number(10)
var numberValue = 10
console.log(typeof numberObject) // object,基本包装类型
console.log(typeof numberValue) // value,基本类型
console.log(numberObject instanceof Number) // true,Number对象是Number类型的实例
console.log(numberValue instanceof Number) // false

String 类型

var StringObject = new String('hello world') // 调用String构造函数,并传入字符串
重写(继承)的方法返回值
valueOf()String 字符串值
toLocaleString()String 字符串值
toString()String 字符串值
  • String 类型每个实例都有 length 属性,表示字符串包含的字符数量,双节字符也算作 1 个字符
console.log(stringObject.length) // "11"
字符方法返回值
charAt(num)给定位置的字符
charCodeAt()给定位置的字符编码
console.log(stringObject.charAt(1)) // "e"
console.log(stringObject.charCodeAt(1)) // "101"
console.log(stringObject[1]) // "e"
字符串操作方法返回值
concat()拼接得到的字符串
slice(num1,num2)被操作字符串的子字符串
substring(num1,num2)被操作字符串的子字符串
substr(num1,num2)被操作字符串的子字符串
console.log(stringObject.concat(' wonderful')) // "hello world wonderful"
console.log(stringObject.slice(3)) // "lo world"
console.log(stringObject.substring(3)) // "lo world"
console.log(stringObject.substr(3)) // "lo world"
console.log(stringObject.slice(3, 7)) // "lo w",第二个参数指定到哪里结束(不包含)
console.log(stringObject.substring(3, 7)) // "lo w",第二个参数指定到哪里结束(不包含)
console.log(stringObject.substr(3, 7)) // "lo worl",第二个参数指定要返回的字符个数
console.log(stringObject.slice(-3)) // "rld",-3转换为11-3=8
console.log(stringObject.substring(-3)) // "hello world",-3转换为0
console.log(stringObject.substr(-3)) // "rld",-3转换为11-3=8
console.log(stringObject.slice(3, -4)) // "lo w",-4转换为11-3=7
console.log(stringObject.substring(3, -4)) // "lo w",-4转换为0,substring(3,0)再转换为substring(0,3)
console.log(stringObject.substr(3, -4)) // "",-4转换为0,返回包含零个字符的字符串
字符串位置方法返回值
indexOf()子字符串的位置,前 → 后搜索
lastIndexOf()子字符串的位置,后 → 前搜索
console.log(stringObject.indexOf('o')) // 4
console.log(stringObject.lastIndexOf('o')) // 7
console.log(stringObject.indexOf('o', 6)) // 7,第二个参数表示从哪个位置开始搜索
console.log(stringObject.lastIndexOf('o', 6)) // 4,第二个参数表示从哪个位置开始搜索

var loremString =
  'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'
var position = new Array()
var pos = loremString.indexOf('o')
while (pos > -1) {
  position.push(pos)
  pos = loremString.indexOf('o', pos + 1)
}
console.log(position) // 所有包含o的字符位置集合
trim()方法返回值
trim()创建副本,删除前置后缀空格,返回副本
trimLeft()创建副本,删除前置空格,返回副本
trimRight()创建副本,删除后缀空格,返回副本
var stringValue = '  hello world  '
var trimStringValue = stringValue.trim()
console.log(stringValue) // "  hello world  "
console.log(trimStringValue) // "hello world"
字符串大小写转换方法返回值
toLowerCase()转小写
toLocaleLowerCase()根据地区转小写
toUpperCase()转大写
toLocaleUpperCase()根据地区转大写
console.log(stringObject.toUpperCase()) // "HELLO WORLD"
console.log(stringObject.toLocaleUpperCase()) // "HELLO WORLD"
console.log(stringObject.toLowerCase()) // "hello world"
console.log(stringObject.toLocaleLowerCase()) // "hello world"
字符串模式匹配方法返回值
match()数组
search()首个匹配项索引,没有返回-1
replace()替换后的字符串
split()分割成多个字符串,放在数组中
var text = 'cat, bat, sat, fat'
var pattern = /.at/

var matches = text.match(pattern)
console.log(matches) // [ 'cat',index: 0,input: 'cat, bat, sat, fat',groups: undefined ]
// match方法返回一个数组,第一项与整个模式匹配的字符串,后面的项保存着与正则表达式捕获匹配的字符串
console.log(matches.index) // 0
console.log(matches[0]) // "cat"
console.log(pattern.lastIndex) // 0

var pos = text.search(/at/)
console.log(pos) // 1,记录"at"首次在字符串出现的位置

var result = text.replace('at', 'ond')
console.log(result) // "cond, bat, sat, fat",第一个参数是字符串所以只替换第一个匹配的
result = text.replace(/at/g, 'ond')
console.log(result) // "cond, bond, sond, fond",第一个参数是全局正则所以替换全部的
result = text.replace(/(.at)/g, 'word($1)')
console.log(result) // "word(cat), word(bat), word(sat), word(fat)",$1表示第一个匹配的参数

function htmlEscape(text) {
  return text.replace(/[<>"&]/g, function (match, pos, originalText) {
    switch (match) {
      case '<':
        return '&lt;'
        break
      case '>':
        return '&gt;'
        break
      case '&':
        return '&amp;'
        break
      case '"':
        return '&quot;'
        break
    }
  })
}
console.log(htmlEscape('<p class="greeting">Hello world!</p>')) // "&lt;p class=&quot;greeting&quot;&gt;Hello world!&lt;/p&gt;"

console.log(text.split(',')) // [ 'cat', ' bat', ' sat', ' fat' ]
console.log(text.split(',', 2)) // [ 'cat', ' bat' ],第二个参数指定数组大小
console.log(text.split(/[^\,]+/)) // [ '', ',', ',', ',', '' ],通过正则获取包含都好字符的数组,正则指定分隔符出现在了字符串开头和末尾,因此首位是空字符串
localeCompare()方法返回值
localeCompare()字符串参数在字母表中的前后关系
var stringCompare = 'yellow'
console.log(stringCompare.localeCompare('brick')) // 1,brick字母表在yellow之前
console.log(stringCompare.localeCompare('yellow')) // 0,yellow字母表与yellow相同
console.log(stringCompare.localeCompare('zoo')) // -1,zoo字母表在yellow之后

function determineOrder(value) {
  var result = stringCompare.localeCompare(value)
  result < 0 &&
    console.log(`The string 'yellow' comes before the string ${value}.`)
  result > 0 &&
    console.log(`The string 'yellow' comes after the string ${value}.`)
  result === 0 &&
    console.log(`The string 'yellow' is equal to the string ${value}.`)
}
determineOrder('brick') // "The string 'yellow' comes after the string brick."
determineOrder('yellow') // "The string 'yellow' is equal to the string zoo."
determineOrder('zoo') // "The string 'yellow' comes before the string zoo."
fromCharCode()方法返回值
fromCharCode()转换后的字符串
// 将若干字符编码转换成字符串,与charCodeAt()相反
console.log(String.fromCharCode(104, 101, 108, 108, 111)) // "hello"
html 方法返回值
anchor(name)<a name="name">string</a>
big()<big>string</big>
bold()<b>string</b>
fixed()<tt>string</tt>
fontcolor(color)<font color="color">string</font>
fontsize(size)<font size="size">string</font>
italics()<i>string</i>
link(url)<a herf="url">string</a>
small()<small>string</small>
strike()<strike>string</strike>
sub()<sub>string</sub>
sup()<sup>string</sup>

总结 & 问点

  • 基本包装类型包含哪些特殊的引用类型?读取时后台经历了什么步骤?
  • 引用类型和基本包装类型的主要区别是什么?
  • 对基本包装类型的实例调用 typeof 会返回什么?基本包装类型的对象都会被转换为什么?
  • 请使用 Object 构造函数判断基本包装类型的实例
  • 使用 new 调用基本包装类型的构造函数与直接调用同名转型函数的区别是什么?
  • Boolean 的引用类型重写了哪些方法?其基本类型和引用类型的区别是什么?
  • Number 的引用类型重写了哪些方法?提供了哪些格式化字符串方法?其基本类型和引用类型的区别是什么?
  • String 的引用类型重写了哪些方法?提供了哪些字符串操作或解析方法?其基本类型和引用类型的区别是什么?
  • 请说明 charAt()、charCodeAt()、concat()、slice()、substr()、substring()、indexOf()、lastIndexOf()、trim()、toLowerCase()、toUpperCase()、match()、search()、replace()、split()、localeCompare()、fromCharCode()等方法的含义,并举出相关例子
查看原文

赞 0 收藏 0 评论 0

小讴 发布了文章 · 8月7日

解决 mpvue 经典 bug:同路由切换时,上次的数据会保留

关注前端小讴,阅读更多原创技术文章

由于一直在用vue技术栈,因此初次开发小程序尝试使用【mpvue】。尽管听闻框架已停止维护,为了上手迅速,不顾可能存在的众多bug,毅然用起来。

果不其然,一个大bug出现了同一路由切换时,上一次的页面数据会保留

项目实战bug:mpvue重构cnode社区
image

github用户已在mpvue的issues给出相关原因:
image

参考众多答案后,用“建页面栈”的办法得以解决

const dataStack = []; // 建一个页面栈

export default {
  data () {
    return {...};
  },
  onLoad () {
    dataStack.push({ ...this.$data }); // 备份数据
    // 其他初始化....一定要先备份,再做其他初始化
  },
  onUnload () {
    Object.assign(this.$data, dataStack.pop()); // 恢复数据
  }
}

由于 vue 的 mixin 中的代码先于页面执行,因此上述方法可优化到 mixin.js 文件中

let mixin = {
  data() {
    return {
      dataStack: [], // 解决mpvue相同组件数据不更新问题,建立栈堆
    };
  },
  onUnload() {
    Object.assign(this.$data, this.dataStack.pop()); // 恢复
  },
  onLoad() {
    this.dataStack.push({ ...JSON.parse(JSON.stringify(this.$data)) }); // 备份
  },
};
export default mixin;

bug得以解决:
image

功能和项目完整代码参见simon-cnode(mpvue重构cnode社区),欢迎star!

查看原文

赞 0 收藏 0 评论 0

小讴 发布了文章 · 7月30日

手摸手,封装一个vue分页组件

关注前端小讴,阅读更多原创技术文章

【组件化】是每一个前端工程师的必备技能,诚然我们将element、iview、vant等UI组件库运用得娴熟自如,实际开发中还是经常需要封装更适合的业务组件,既帮助快速开发、又让代码简洁明了、还能锻炼我们的组件化能力。

梳理思路

node.js官网的分页组件为例,假设我们要实现下面这样的分页:
最终截图.png
最终截图2.png
最终截图3.png
最终截图4.png

从产品角度梳理思路后,要实现的分页组件有以下特点:
1.当前页页码颜色变化
2.最多显示5个页码,不足5个显示实际数量
3.总页数超过5个时:总页码-当前页>2,则末尾显示省略号;当前页>2,则开头显示省略号
4.总页数超过5个时:当前页为最后1页,则一共显示3个页码;当前页为倒数第二页,则一共显示4个页码;其余均显示5个页码
5.总页数超过5个、且同时满足总页码-当前页>2当前页>2时,当前页总是显示在5个页码的最中间
6.点击最左、最右侧箭头,分别跳转到第1、最后1页

基本结构

先写一个静态的结构:

<template>
  <div class="page">
    <span class="page-block center">{{'«'}}</span>
    <span v-if="pageList[2]-2>1"
          class="page-block center">...</span>
    <span v-for="pageNum in pageList"
          :key="pageNum"
          class="page-block center"
          :style="{color:pageNum===page?'#80bd01':'#778087'}">{{pageNum}}</span>
    <span v-if="pageMax-pageList[2]>2"
          class="page-block center">...</span>
    <span class="page-block center">{{'»'}}</span>
  </div>
</template>

<script>
export default {
  data () {
    return {
      pageList: [1,2,3,4,5], // 页码列表
      page: 1, // 当前页码
      pageMax: 7 // 最大页数
    };
  }
};
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
.page {
  padding: 10px;
  background-color: #fff;

  &-block {
    display: inline-block;
    width: 30px;
    height: 28px;
    padding: 4px 8px;
    font-size: 0.8em;
    line-height: 18px;
    border: 1px solid #ddd;
    border-right: none;
    box-sizing: border-box;

    &:first-child {
      width: 34px;
      border-top-left-radius: 4px;
      border-bottom-left-radius: 4px;
    }

    &:last-child {
      width: 34px;
      border-right: 1px solid #ddd;
      border-top-right-radius: 4px;
      border-bottom-right-radius: 4px;
    }
  }
}
</style>

在父组件引用该组件:

<template>
  <pagination></pagination>
</template>

<script>
import Pagination from "@/components/pagination";
export default {
  components: { Pagination }
}
</script>

此时效果:将看到一个标准的、大于5页的静态分页:
最终截图.png

props接收值

组件的某些值应该是父组件传递过来的,通过计算后挂载

props: {
  // 内容总数
  total: {
    type: Number,
    default: 0
  },
  // 每页数量
  limit: {
    type: Number,
    default: 10
  },
  // 当前页码
  page: {
    type: Number,
    default: 1
  },
},
data () {
  return {
    pageList: [] // 页码列表
  };
},
computed: {
  // 最大页数
  pageMax () {
    return Math.ceil(this.total / this.limit);
  },
},
onLoad () { // 我的框架是mpvue,vue用created
  this.initData();
},
methods: {
  // 生成pageList页码列表
  initData () {
    this.pageList = []; // 清空页码
    var i = 1;
    do {
      this.pageList.push(i);
      i++;
    } while (i <= this.pageMax);
    this.pageList.length > 5 && (this.pageList = this.pageList.slice(0, 5)); // 最多显示5页
  }
}

父组件传递这些值:

<pagination :total="dataList.length"
            :limit="limit"
            :page="page"></pagination>
data () {
  return {
    dataList: [], // 列表
    page: 1, // 当前页码
    limit: 20, // 每页数量
  };
},
onLoad () {
  this.getData();
},
methods: {
  getData () {
     // 动态获取列表 - 以后端分页为例
     this.dataList = (await getDataList(this.limit, this.page)).data;
  }
}

此时效果:将根据父组件——也就是使用该组件的页面——的真实数据,渲染分页组件。页码数量大于5不大于5组件内容不同,且当前页码为首页

event事件回调

组件中追加事件,回调给父组件(调用的组件):

<template>
  <div class="page">
    <span class="page-block center"
          @click="pageChange(1)">{{'«'}}</span>
    <span v-if="pageList[2]-2>1"
          class="page-block center">...</span>
    <span v-for="pageNum in pageList"
          :key="pageNum"
          class="page-block center"
          :style="{color:pageNum===page?'#80bd01':'#778087'}"
          @click="pageChange(pageNum)">{{pageNum}}</span>
    <span v-if="pageMax-pageList[2]>2"
          class="page-block center">...</span>
    <span class="page-block center"
          @click="pageChange(pageMax)">{{'»'}}</span>
  </div>
</template>
methods: {
  // 子组件事件回调:分页
  pageChange (page) {
    this.$emit("page-change", page);
  },
}

父组件执行子组件回调过来的事件:

<pagination :total="dataList.length"
            :limit="limit"
            :page="page"
            @page-change="pageChange"></pagination>
methods: {
  // 分页
  pageChange (page) {
    this.page = page;
    this.getData();
  },
}

此时效果:点击页码颜色会发生变化,父组件接收到子组件的回调事件,返回一个值page——即当前页码,可根据页码做分页的内容渲染;点击最左、最右侧箭头,分别跳转到第1、最后1页

watch页码变化

由于不同页码要渲染的组件内容不同,因此需要监听页码变化,刷新组件内容;同时total总数更新时,重新加载组件

watch: {
  // 监听页码变化 -> 页码列表更新
  page (val) {
    if (val <= 3) {
      this.pageList = [];
      var i = 1;
      do {
        this.pageList.push(i);
        i++;
      } while (i <= this.pageMax);
      this.pageList.length > 5 && (this.pageList = this.pageList.slice(0, 5)); // 最多显示5页
    } else if (val === this.pageMax) {
      this.pageList = [val - 2, val - 1, val];
    } else if (val === this.pageMax - 1) {
      this.pageList = [val - 2, val - 1, val, val + 1];
    } else {
      this.pageList = [val - 2, val - 1, val, val + 1, val + 2];
    }
  },
  // 监听页码变化 -> 总数更新
  total (val) {
    this.initData();
  }
}

此时效果:组件根据页码变化数量变化,渲染出不同的内容,至此该分页组件全部内容完成

扩展

可以像element和iview那样,为组件追加当前页显示数量下拉框,也可以由父组件决定子组件的样式布局,页码按钮的数量(本例为最大5个)。。。总体思路不变,即:
1.子组件接收值并根据值而改变渲染
2.子组件追加事件,回调给父组件
3.根据需要追加监听

完整代码

<template>
  <div class="page">
    <span class="page-block center"
          @click="pageChange(1)">{{'«'}}</span>
    <span v-if="pageList[2]-2>1"
          class="page-block center">...</span>
    <span v-for="pageNum in pageList"
          :key="pageNum"
          class="page-block center"
          :style="{color:pageNum===page?'#80bd01':'#778087'}"
          @click="pageChange(pageNum)">{{pageNum}}</span>
    <span v-if="pageMax-pageList[2]>2"
          class="page-block center">...</span>
    <span class="page-block center"
          @click="pageChange(pageMax)">{{'»'}}</span>
  </div>
</template>

<script>
export default {
  props: {
    // 内容总数
    total: {
      type: Number,
      default: 0
    },
    // 每页数量
    limit: {
      type: Number,
      default: 10
    },
    // 当前页码
    page: {
      type: Number,
      default: 1
    },
  },
  data () {
    return {
      pageList: [] // 页码列表
    };
  },
  computed: {
    // 最大页数
    pageMax () {
      return Math.ceil(this.total / this.limit);
    },
  },
  onLoad () {
    this.initData();
  },
  methods: {
    initData () {
      this.pageList = []; // 清空页码
      var i = 1;
      do {
        this.pageList.push(i);
        i++;
      } while (i <= this.pageMax);
      this.pageList.length > 5 && // 最多显示5页
        (this.pageList = this.pageList.slice(0, 5));
    },
    // 子组件事件回调:分页
    pageChange (pageCurrent) {
      this.$emit("page-change", pageCurrent);
    },
  },
  watch: {
    // 监听页码变化 -> 页码列表更新
    page (val) {
      if (val <= 3) {
        this.pageList = [];
        var i = 1;
        do {
          this.pageList.push(i);
          i++;
        } while (i <= this.pageMax);
        this.pageList.length > 5 && // 最多显示5页
          (this.pageList = this.pageList.slice(0, 5));
      } else if (val === this.pageMax) {
        this.pageList = [val - 2, val - 1, val];
      } else if (val === this.pageMax - 1) {
        this.pageList = [val - 2, val - 1, val, val + 1];
      } else {
        this.pageList = [val - 2, val - 1, val, val + 1, val + 2];
      }
    },
    // 监听页码变化 -> 总数更新
    total (val) {
      this.initData();
    }
  }
};
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
.page {
  padding: 10px;
  background-color: #fff;

  &-block {
    display: inline-block;
    width: 30px;
    height: 28px;
    padding: 4px 8px;
    font-size: 0.8em;
    line-height: 18px;
    border: 1px solid #ddd;
    border-right: none;
    box-sizing: border-box;

    &:first-child {
      width: 34px;
      border-top-left-radius: 4px;
      border-bottom-left-radius: 4px;
    }

    &:last-child {
      width: 34px;
      border-right: 1px solid #ddd;
      border-top-right-radius: 4px;
      border-bottom-right-radius: 4px;
    }
  }
}
</style>

api

props:
属性说明类型默认值
total内容总数Number0
limit每页数量Number10
page当前页码Number1
events:
事件名说明返回值
page-change页码改变的回调,返回改变后的页码当前页码
查看原文

赞 0 收藏 0 评论 0

小讴 发布了文章 · 7月20日

《javascript高级程序设计》学习笔记 | 5.5.Function类型

欢迎关注前端小讴的github,阅读更多原创技术文章

Function 类型

  • 函数是对象,每个函数都是 Function 类型的实例,都与其他引用类型一样具有属性方法
  • 函数名是指向函数对象的指针,不会与某个函数绑定(一个函数可能会有多个名字)

相关代码 →

3 种声明方式

// 1.函数声明定义
function sum(num1, num2) {
  return num1 + num2
}

// 2.函数表达式定义
var sum = function (num1, num2) {
  return num1 + num2
}

// 3.Function构造函数定义
/* 不推荐使用构造函数定义,因为会造成解析2次代码(1次解析常规js代码,1次解析传入构造函数中的字符串) */
var sum = new Function('num1', 'num2', 'return num1+num2')

访问函数指针

function sum(num1, num2) {
  return num1 + num2
}
console.log(sum(10, 10))

var anotherSum = sum // 使用不带圆括号的函数名是访问函数指针,而非调用函数
console.log(anotherSum(10, 10))

/* sum 和 anotherSum 同时指向同一个函数 */

sum = null // sum与函数断绝关系
console.log(anotherSum(10, 10)) // 但anotherSum()仍可正常调用
console.log(sum(10, 10)) // 会报错,sum is not a function

没有重载

function addSomeNumber(num) {
  return num + 100
}
// 创建第二个函数
function addSomeNumber(num) {
  return num + 200
}
// 第二个函数等价于下列代码 -> 覆盖了引用第一个函数的变量addSomeNumber
addSomeNumber = function (num) {
  return num + 200
}
var result = addSomeNumber(100)
console.log(result) // 300

函数声明与函数表达式

js 引擎在代码开始执行之前,解析器通过函数声明提升(function declaration hoisting)的过程,将声明函数放到源代码树的顶部,使其在执行任何代码之前可用(可以访问);而函数表达式则必须等到解析器执行到所在代码行才被解释执行。

函数声明函数表达式的唯一区别就是什么时候可以通过变量访问函数

console.log(sumDeclare(10, 10)) // 函数声明会提前
function sumDeclare(num1, num2) {
  return num1 + num2
}
console.log(sumExpression(10, 10)) // 函数表达式不会提前,会报错,sumExpression is not a function
var sumExpression = function (num1, num2) {
  return num1 + num2
}

作为值的函数

像传递参数一样,把一个函数传递给另一个函数,也可以将一个函数作为另一个函数的结果返回

function callSomeFunction(someFunction, someArgument) {
  return someFunction(someArgument)
}

function add10(num) {
  return num + 10
}
var result1 = callSomeFunction(add10, 10) // 访问函数的指针而不是执行函数
console.log(result1) // 20

function getGreeting(name) {
  return 'Hello,' + name
}
var result2 = callSomeFunction(getGreeting, 'Nicholas') // 访问函数的指针而不是执行函数
console.log(result2) // Hello,Nicholas

【例】想要根据数组对象的某个对象属性进行排序:

/**
 * 按照对象数组的某个object key,进行数组排序
 * @param {String} key 要排序的key
 * @param {String} sort 正序/倒序:asc/desc,默认为asc
 */
function arraySort(key, sort) {
  return function (a, b) {
    if (sort === 'asc' || sort === undefined || sort === '') {
      // 正序:a[key] > b[key]
      if (a[key] > b[key]) return 1
      else if (a[key] < b[key]) return -1
      else return 0
    } else if (sort === 'desc') {
      // 倒序:a[key] < b[key]
      if (a[key] < b[key]) return 1
      else if (a[key] > b[key]) return -1
      else return 0
    }
  }
}
var userList = [
  { name: 'Tony', id: 3 },
  { name: 'Tom', id: 2 },
  { name: 'Jack', id: 5 },
]
console.log(userList.sort(arraySort('id'))) // 按 id 正序排列
console.log(userList.sort(arraySort('id', 'desc'))) // 按 id 倒序排列

函数内部属性

函数内部有 2 个特殊对象 argumentsthis,1 个内部属性 caller

arguments 是一个类数组对象,保存着函数的所有参数。对象有 callee 属性,该属性是一个指针,指向拥有这个 arguments 对象的函数

// 递归函数:计算阶乘
function factorial(num) {
  if (num <= 1) return 1
  else return num * factorial(num - 1)
}
// 内部不再引用函数名,无论函数名是什么,都可以保证完成递归调用
function factorial(num) {
  if (num <= 1) return 1
  else return num * arguments.callee(num - 1) // callee指向拥有这个arguments对象的函数
}

var trueFactorial = factorial // 保存函数的指针
factorial = function () {
  return 0
}
console.log(trueFactorial(5)) // 120,已用arguments.callee解除函数体内代码与函数名的耦合,仍能正常计算
console.log(factorial(5)) // 0

this 引用的是函数执行的环境对象,即 this 值(全局作用域调用函数时引用的是 window

// vscode是node运行环境,无法识别全局对象window,测试时需做微调
window.color = 'red'
var o = { color: 'blue' }
function sayColor() {
  console.log(this.color)
}
sayColor() // red,此时this指向对象window
o.sayColor = sayColor
o.sayColor() // blue,此时this指向对象o

ES5 定义了 caller 属性,保存着调用当前函数的函数引用—— 谁调用了当前函数,caller 就是谁(全局作用域中调用当前函数 caller 值为 null)

function callerTest() {
  console.log(callerTest.caller)
}
callerTest() // 在全局作用域种调用当前函数,caller 的值为 null

function outer() {
  inner()
}
function inner() {
  console.log(inner.caller)
}
outer() // outer()调用了inner,因此打印outer()函数的源代码

function inner() {
  console.log(arguments.callee.caller) // 可以解除紧密耦合
}
outer() // 结果不变,打印outer()函数的源代码
  • 函数在严格模式下运行时,访问 arguments.callee 会报错
  • ES5 定义了 arguments.caller 属性,严格模式会报错,非严格模式始终是 undefined,该属性是为了区分 arguments.caller 和 函数的 caller
  • 严格模式不能为函数的 caller 属性赋值,会报错

函数属性和方法

每个函数都包含 2 个属性:lengthprototype

length 表示函数希望接收的命名参数的个数

function nameLength(name) {
  return name
}
function sumLength(sum1, sum2) {
  return sum1 + sum2
}
function helloLength() {
  return 'Hello'
}
console.log(nameLength.length, sumLength.length, helloLength.length) // 1,2,0

prototype 保存着函数所有实例方法不可枚举(使用for-in无法发现),每个函数都包含 3 个非继承而来的方法apply()call()bind()

apply()call()的用途和结果相同 —— 都是在特定的作用域中调用函数,apply() 接收 arguments 对象或数组实例,call() 接收每一个参数

function sumPrototype(num1, num2) {
  return num1 + num2
}

apply()接收 2 个参数:① 运行函数的作用域;② 参数数组(实例或 arguments 对象均可)

function applySum1(num1, num2) {
  return sumPrototype.apply(this, arguments) // 传入arguments对象
}
function applySum2(num1, num2) {
  return sumPrototype.apply(this, [num1, num2]) // 传入数组实例
}
console.log(applySum1(10, 10))
console.log(applySum2(10, 10))

call()接收若干参数:① 运行函数的作用域;剩余参数分别传入

function callSum(num1, num2) {
  return sumPrototype.call(this, num1, num2) // 分别传入每个参数
}
console.log(callSum(10, 10))

apply()call() 真正强大的地方在于能够扩充函数运行的作用域

// vscode是node运行环境,无法识别全局对象window,测试时需做微调
window.color = 'red'
var o = { color: 'blue' }
function sayColor() {
  console.log(this.color)
}
sayColor() // red,此时this指向对象window
sayColor().call(this) // red,此时this指向对象window
sayColor().call(window) // red,此时this指向对象window
sayColor().call(o) // blue,此时this指向对象o

ES5 追加 bind() 方法,其创建一个函数实例,其 this 被绑定到传给 bind() 函数的值

// vscode是node运行环境,无法识别全局对象window,测试时需做微调
window.color = 'red'
var o = { color: 'blue' }
function sayColor() {
  console.log(this.color)
}
var bindColor = sayColor.bind(o)
bindColor() // blue,此时this被绑定给对象o

总结 & 问点

  • 函数是什么?函数名是什么?
  • 函数分别有哪几种声明方式?
  • js 的函数有重载嘛?为什么?
  • 函数声明与函数表达式有什么区别?
  • 什么是函数的声明提前?
  • 请写出 function:根据对象数组的某个对象进行排序
  • 函数内部有哪些对象和属性?这些对象和属性分别有哪些特点或用法?
  • 严格模式下,函数内部的对象或属性分别会有哪些限制?
  • 函数有哪些属性?其实例方法都保存在哪里?
  • 函数有哪些非继承而来的方法?分别有什么用法和作用?
查看原文

赞 1 收藏 1 评论 0

小讴 发布了文章 · 6月28日

iview-dynamicRouter:基于 iview-admin 的“后端动态路由模板”,自由配置路由、自由更改菜单

欢迎关注[前端小讴的github],阅读更多原创技术文章

iView DynamicRouter

iView-DynamicRouter 基于 iview-admin(branch:template),由“后端动态提供路由数据,经前端处理后生成动态路由和菜单”的【后端动态路由模板】,内置“权限管理”业务模型,可任意调整左侧菜单栏、修改其相关权限、监听当前路由和菜单,是一套更安全、更智能的后台管理系统模板。

项目地址

功能特点

登录后,不同权限的用户监听到不同的动态路由动态菜单

菜单自由选择上级、自由增删改

自由选择菜单的层级,大屏路由亦可在子菜单挂载:

修改“角色-菜单”关联,控制菜单权限

核心思路

路由数据处理:

路由改造:

菜单渲染:

核心代码

  • 如果你想快速使用,请看 配置文档 →
  • 如果你想和我一起,从 iview-admin 的原始框架一步步实现 iView-DynamicRouter,请看 开发指南 →
如果这个模板或模板中的代码段能够在开发中帮助到你,欢迎前往 项目地址 给个 ⭐ ,非常感谢!
查看原文

赞 1 收藏 0 评论 0

小讴 关注了用户 · 5月28日

Linmi @linmi

Segmentfault Community Support | Notion Pro


一对一免费提供内容选题、写作指导 → 微信 linmib

关注 19

小讴 关注了用户 · 5月26日

SegmentFault @segmentfault

SegmentFault 社区管理媛 - 思否小姐姐

纯粹的技术社区离不开所有开发者的支持和努力 biubiu

更多技术内容与动态欢迎关注 @SegmentFault 官方微博与微信公众号!

点击添加思否小姐姐个人微信号

关注 83879

小讴 发布了文章 · 4月17日

解决 vue 开发环境中一直发http://localhost:8080/sockjs-node/info?t=xxx 请求

欢迎关注前端小讴的github,阅读更多原创技术文章

sockjs-node 是一个JavaScript库,提供跨浏览器JavaScript的API,创建了一个低延迟、全双工的浏览器和web服务器之间通信通道。

服务端:sockjs-node(https://github.com/sockjs/soc...
客户端:sockjs-client(https://github.com/sockjs/soc...

开发时,如果变更网络环境(如切换wifi导致开发服务器的IP地址更换),服务器不知道如何确定访问源,就有可能造成上述情况,解决办法如下:

办法一

  • 找到/node_modules/sockjs-client/dist/sockjs.js
  • 找到代码的 1605行
try {
  //  self.xhr.send(payload); 把这里注掉
} catch (e) {
  self.emit('finish',0,'');
  self.\_cleanup(false);
}

办法二

  • /package-lock.json
  • 关闭开发环境时的 sockjs 接口
  • 微信图片_20200417151024.png
查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 48 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • iview-dynamicRouter

    iView Admin的后端动态生成路由模板,内置“权限管理”业务模型,可任意调整左侧菜单栏、修改其相关权限、监听当前路由和菜单,是一套更安全、更智能的后台管理系统模板。

  • github博客

    深入前端核心理论,记录个人博客

注册于 2017-12-11
个人主页被 871 人浏览