miloer

miloer 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

miloer 提出了问题 · 3月19日

遇到一个挺奇怪的问题,iframe加载资源

iframe加载的资源src是https资源,但是浏览器确报错‘认为加载的是http资源’

下图是加载的iframe
image

下图是浏览器报错
image

我感觉资源被浏览器又处理了一遍?

关注 4 回答 3

miloer 赞了文章 · 2020-04-27

【笔记】重学前端-winter

本文为:winter 发布在极客时间 【重学前端】系列课程的的笔记和总结
支持正版哦: https://time.geekbang.org/col...
文章太大了,划分成多个文章,链接贴在对应章节里啦

导语

如果深入进去了解,你会发现,表面上看他们可能是一时忘记了,或者之前没注意但实际上是他们对于前端的知识体系和底层原理没有真正系统地理解

前端学习方法:
一是立足标准,系统性总结和整理前端知识,建立自己的认知和方法论
二是放眼团队,从业务和工程角度思考前端团队的价值和发展需要

第一个方法:建立知识架构
知识的“目录”或者索引:把零散的知识组织起来,也能够帮助我们发现一些知识上的盲区,面试时,定位到知识架构中的位置,相关点讲出

第二个方法:追本溯源
看这个出现的背景和原因,提出他是为了解决什么或者其底层是什么,其中还可以发掘一些趣闻,便于记忆

课程目标:
把无法通过查阅解决的原理和背景讲清楚
不方便查阅和记忆的内容整理好

前端知识体系:
javascript:用一定的词法和语法,表达一定语义,从而操作运行时
数据结构
1.类型:JavaScript 的类型系统就是它的 7 种基本类型和 7 种语言类型
2.实例:内置对象部分
算法:JavaScript 的执行过程

clipboard.png
html
1.元素
文档元信息:通常是出现在 head 标签中的元素,包含了描述文档自身的一些信息
语义相关:扩展了纯文本,表达文章结构、不同语言要素的标签;
链接:提供到文档内和文档外的链接
替换型标签:引入声音、图片、视频等外部元素替换自身的一类标签...
表单:用于填写和提交信息的一类标签;
表格:表头、表尾、单元格等表格的结构。
2.把 HTML 当作一门语言来了解下:语法和语言机制
3.补充标准:ARIA,它是 HTML 的扩展,在可访问性领域,它有至关重要的作用

clipboard.png

clipboard.png

clipboard.png

1.HTML语义

定义:
语义类标签则是纯文字的补充,eg:标题、自然段、章节、列表,这些内容都是纯文字无法表达的
建议:
只靠 div 和 span 就能走天下了:用于描述“软件界面”多过于“富文本”
好处:
1.无css时,清晰的目录结构
2.SEO
3.读屏软件,根据文章可以自动生成目录 eg:阅读视图功能

使用场景:
1.作为自然语言和纯文本的补充,用来表达一定的结构或者消除歧义 eg: ruby(注释)/em(重音)
2.文章标题摘要
hgroup 标签:在 hgroup 中的 h1-h6 被视为同一标题的不同组成部分

<hgroup>
<h1>JavaScript 对象 </h1>
<h2> 我们需要模拟类吗?</h2>
</hgroup>
<p>balah balah</p>
......

clipboard.png

不加hgroup

clipboard.png
section 的嵌套会使得其中的 h1-h6 下降一级,HTML5之后,只需要 section 和 h1 就足以形成文档的树形结构

<section>
    <h1>HTML 语义 </h1>
    <p>balah balah balah balah</p>
    <section>
        <h1> 弱语义 </h1>
        <p>balah balah</p>
    </section>
    <section>
        <h1> 结构性元素 </h1>
        <p>balah balah</p> 
    </section>
......
</section>

clipboard.png

3.适合机器阅读的整体结构
“阅读模式”,以及各种非浏览器终端的出现

<body>
    <header>
        <nav>
            ……
        </nav>
    </header>
    <aside>
        <nav>
            ……
        </nav>
    </aside>
    <section>……</section>
    <section>……</section>
    <section>……</section>
    <footer>
        <address>……</address>
    </footer>
</body>

article,报纸的多文章结构适合用 article 来组织,article为独立性质文章,article与body有相似结构,也可包含header/footer
header,如其名,通常出现在前部,表示导航或者介绍性的内容
footer,通常出现在尾部,包含一些作者信息、相关链接、版权信息
aside 表示跟文章主体不那么相关的部分,它可能包含导航、广告等工具性质的内容

2.HTML语义:如何运用语义类标签来呈现Wiki网页?

实现如下wiki网页需要的标签:
https://en.wikipedia.org/wiki...
aside: 左侧侧边栏/导航性质的工具内容
article: 独立为文章主体
abbr 标签表示缩写
hr 表示故事走向的转变或者话题的转变
strong 重要,黑体
blockquote, q, cite: blockquote 表示段落级引述内容,q 表示行内的引述内容,cite 表示引述的作品名。
time 机器阅读方便
figure, figcaption 表示与主文章相关的图像、照片等流内容
dfn 被包裹的名词
pre 不需要浏览器帮我们做自动换行,不需要浏览器进行排版。
samp 计算机程序的示例输出
code 代码

除上面用到的标签之外,还需要用到:
clipboard.png

3.JavaScript类型:关于类型,有哪些你不知道的细节?

1.开篇winter提出了几个问题,测试是否了解js类型
为什么有的编程规范要求用 void 0 代替 undefined
字符串有最大长度吗?
0.1 + 0.2 不是等于 0.3 么?为什么JavaScript 里不是这样的?
ES6 新加入的 Symbol 是个什么东西?
为什么给对象添加的方法能用在基本类型上?

2.js七种基本类型
1)Undefined
定义:是全局对象的一个属性
什么时候值为Undefined:
1.任何变量在赋值前是 Undefined 类型、值为 Undefined
2.或使用void 运算来把任一一个表达式变成 undefined 值
3.函数不定义return返回值时,函数返回undefined
判断一个值是否为Undefined

全等,不能用==: == undefined 会检查x是不是null
if (x === undefined)
typeof x === 'undefined'
x === void 0

为什么有的编程规范要求用 void 0 代替 undefined:
js中undefined 是一个变量,而并非是一个关键字,避免无意中被篡改,我建议使用 void 0 来获取 Undefined 跟 null 的区别
2)Null
null 是 JavaScript 关键字
Null 类型也只有一个值,就是 null,它的语义表示空值
可以放心用 null 关键字来获取 null 值
3)Boolean
4)String
长度:最大长度是 2^53 - 1
字符串的编码长度影响,我们字符串的操作 charAt、charCodeAt、length 等方法针对的都是 UTF16 编码,所以处理非 BMP(超出 U+0000 - U+FFFF 范围)要谨慎
(补充: 字符是以 Unicode 的方式表示的,每一个 Unicode 的码点表示一个字符,UTF 是 Unicode 的编码方式,规定了码点在计算机中的表示方法,常见的有 UTF16 和 UTF8)
JavaScript 中的字符串是永远无法变更的,字符串具有值类型的特征。

5)Number
范围:
Number 类型有 18437736874454810627(即 2^64-2^53+3) 个值
基本符合 IEEE 754-2008 规定的双精度浮点数规则
为了表达几个额外的语言场景,规定了几个例外情况
1.NaN,占用了 9007199254740990,这原本是符合 IEEE 规则的数字
2.Infinity,无穷大;
3.-Infinity,负无穷大。
JavaScript 中有 +0 和 -0
注意除法:“忘记检测除以 -0,而得到负无穷大”
区分 +0 和 -0 的方式,正是检测 1/x 是 Infinity 还是 -Infinity
Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff
浮点数非整数的 Number 类型无法用 ==(=== 也不行)
原因:有些小数以二进制表示位数是无穷的

十进制           二进制
    0.1              0.0001 1001 1001 1001 ...
    0.2              0.0011 0011 0011 0011 ...
    0.3              0.0100 1100 1100 1100 ...
    0.4              0.0110 0110 0110 0110 ...
    0.5              0.1
    0.6              0.1001 1001 1001 1001 ...

现象:

console.log(1.0-0.9 == 0.1)    //false
console.log(1.0-0.8 == 0.2)    //false
console.log(1.0-0.7 == 0.3)    //false
console.log(1.0-0.6 == 0.4)    //true
console.log(1.0-0.5 == 0.5)    //true
console.log(1.0-0.4 == 0.6)    //true

解决:

1.console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);
2.((0.3*1000)-(0.2*1000))/1000 = 0.1
3. 在判断浮点运算结果前对计算结果进行精度缩小,因为在精度缩小的过程总会自动四舍五入
console.log(parseFloat((1.0-0.9).toFixed(10)) === 0.1)   //true

6)Symbol
7)Object

10.浏览器:一个浏览器是如何工作的?(阶段一)

https://segmentfault.com/a/11...

11.浏览器:一个浏览器是如何工作的?(阶段二)

https://segmentfault.com/a/11...

12.浏览器:一个浏览器是如何工作的?(阶段三)

https://segmentfault.com/a/11...

13.浏览器:一个浏览器是如何工作的?(阶段四)

https://segmentfault.com/a/11...

14.浏览器:一个浏览器是如何工作的?(阶段五)

https://segmentfault.com/a/11...

16.JavaScript执行(一):Promise里的代码为什么比setTimeout先执行?

https://segmentfault.com/a/11...

查看原文

赞 10 收藏 8 评论 1

miloer 收藏了文章 · 2020-02-11

一篇文章搞定百度OCR图片文字识别API

研究百度OCR的API,主要是向做对扫描版的各种PDF进行文字识别并转Word文档的需求。

这里用Postman客户端进行测试和演示。因为Postman是对各种API操作的最佳入门方式。一旦在Postman里实现了正确的调用,剩下的就只是一键生成代码,和一些细节的修改了。

参考百度云官方文档:文字识别API参考
下载官方文档PDF:OCR.zh.pdf

授权字符串 Access Token

Token字符串永远是你使用别人API的第一步,简单说,就是只有你自己知道的密码,在你每次向服务器发送的请求里面加上这个字符串,就相当于完成了一次登录。

如果没有Token授权认证,API的访问可能会像浏览网页一样简单。

Access Token一般是调用API最重要也最麻烦的地方了:每个公司都不一样,各种设置安全问题让你的Token复杂化。而百度云的Token,真的是麻烦到一定地步了。

参考:百度API的鉴权认证机制 (建议你不要参考,因为它的流程图会先把你镇住的)

简单说,获取百度云token字符串的主要流程就是:

  • 创建一个应用,获得只有自己知道的id和密码
  • 用POST方式把id和密码发给百度的一个链接:

https://aip.baidubce.com/oauth/2.0/token

  • 其中,需要你向这个地址传送三个参数:

    • grant_type = client_credentials 这个是固定的
    • client_id = xxx 这个是你在百度云管理后台创建OCR应用的时候,那个应用的API Key
    • client_secret = xxx 这个是你的应用的Secret Key
  • 等待服务器返还给你一个包含token字符串的数据
  • 记住这个token字符串,并用来访问每一次的API

来看看怎么利用Postman操作,如下图所示:
image

填好以后点击Send发送,就会获得一个JSON数据,如下图:
image

然后你用你的程序(Python, PHP, Node.js等,随便),获取这个JSON中的access_token
即可用到正式的API请求中,做为授权认证。

正式调用API: 以"通用文字识别"为例

API链接:https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic

提交方式:POST

调用方式有两种:

  • 方式一:直接在URL填写信息

直接把API所需的认证信息放在URL里是最简单最方便的。

  • ~方式二:Headers填写信息方式~

建议忽略这种方式,需要填写很多request的标准headers,太麻烦。

Headers设置:

  • Content-Type = application/x-www-form-urlencoded

只要填这一项就够了。

Body数据传送的各项参数:

  • access_token = xxx 把之前获取到的token字符串填到这里来
  • image = xxx 把图片转成base64字符串填到这里,不需要开头的data:image/png;base64,
  • url = xxx 也可以不用传图片而是传一个图片的链接。但是百年无效,不要用!
  • language_type = CHN_ENG 识别语言类型。默认中英。

Body的数据如图所示:
image

然后就可以点Send发送请求了。
成功后,可以得到百度云返回的一个JSON数据,类似下图:
image

返回的是一行一行的识别字符。百度云的识别率是相当高的,几乎100%吧。毕竟是国内本土的机器训练出来的。

API常用地址

以下是百度云的OCR常用API地址,每个API所需的参数都差不多,略有不同。所有的API和地址以及详细所需的参数,参考官方文档,很简单。一个弄明白了就其他的都明白了。

API请求地址调用量限制
通用文字识别https://aip.baidubce.com/rest...50000次/天免费
通用文字识别(含位置信息版)https://aip.baidubce.com/rest...500次/天免费
通用文字识别(高精度版)https://aip.baidubce.com/rest...500次/天免费
通用文字识别(高精度含位置版)https://aip.baidubce.com/rest...50次/天免费
网络图片文字识别https://aip.baidubce.com/rest...500次/天免费
查看原文

miloer 提出了问题 · 2019-05-06

解决vue 中 使用async 函数的出现问题

项目中使用 async 函数

mounted 调用时没问题
图片描述

但是在组件中在调用就不行了,这是怎么回事?
图片描述

图片描述

关注 2 回答 1

miloer 回答了问题 · 2019-04-22

vue报错更新失败

看报错,我猜你JSON.parse()解析的数据有问题。

关注 3 回答 3

miloer 收藏了文章 · 2019-03-01

基于vue-router的动态权限实现方案

使用vue开发带权限管理系统,尤其是采用了vue-router做路由,很多人都遇到的一个问题就是如何动态加载路由path对应的component。

典型的应用场景就是:前端菜单不静态的写在vue程序里,而是要从后台程序和数据库返回的菜单来动态加载到vue应用中。

网上很多问权限的问题,但几乎找不到很好的解决答案,在很长一段时间里,非常打击使用vue技术栈开发的信心。最有质量的一篇文章是:
http://blog.csdn.net/s8460049...
但作者并没有完全解决这个问题,还留有几个问题是:
1)登录之后跳转到首页,此时路由已经是加载完成的了不能更改,菜单可以显示但是没有路由。
2)前端应用人为刷新网页路由产生某些问题。

本文即在这篇文章的基础上对这两个问题解决,以使其完整。

前提是认真拜读上面提到的那篇文章,下面直接用代码说话:

问题1的解决思路:
登录之后跳转到首页,router是vue应用的router 引入进登录方法,在登录之后跳转之前对router进行改变,改变要点1是精确赋值到router的routes具体地方,比如我这里是routes[0]的子路由,2是用addRoutes函数使其生效。

登录功能的js

export const login = ({commit}, data) => {  Service.post('/login', Qs.stringify(data))
    .then(res => {
      const success = Object.is(res.statusText, 'OK') && Object.is(res.data.code, '0')
      if (success) {
        var menus = generateMenus(res.data.menus)
        window.sessionStorage.routes = JSON.stringify(menus)
        if (menuModule.state.items.length <= 0) { // 避免注销后在不刷新页面的情况下再登录重复加载路由
          commit(types.ADD_MENU, menus)
          // 动态加载路由关键2行
          router.options.routes[0].children.push(...generateRoutesFromMenu(menuModule.state.items))
          router.addRoutes(router.options.routes)
        }
        window.sessionStorage.loginName = data.loginName
        router.push({path: '/'})
      } else {
        commit('loginErr', res.data.msg)
      }
    })
}


function generateRoutesFromMenu (menu = [], routes = []) {
  for (let i = 0, l = menu.length; i < l; i++) {
    let item = menu[i]
    if (item.path) {
      routes.push(item)
    }
    if (!item.component) {
      item.component = resolve => require([`views/` + item.component + `.vue`], resolve)
      generateRoutesFromMenu(item.children, routes)
    }
  }
  return routes
}

问题2的解决思路:
是不在主app里引入实例化vue-router的js,而是直接在app里实例化router,目的就是网页刷新的时候每次都确保生成动态的router。

app.js部分代码:


Vue.use(Router)
let menus = window.sessionStorage.routes //登录成功返回的菜单
if (menus) {
  let items = JSON.parse(menus)
  store.commit(ADD_MENU, items)
}

const router = new Router({
  mode: 'hash',
  linkActiveClass: 'is-active',
  scrollBehavior: () => ({ y: 0 }),
  routes: [
    {
      name: 'Main',
      path: '/',
      component: require('views/Main.vue'),
      children: [ //动态路由之所以作为Main的子路由是基于:登录之后跳转到Main主页,该主页是类似于frame的页面加载框架,只有将动态路由作为Main的子路由才能确保其他页面显示到Main框架内。
        ...generateRoutesFromMenu(menuModule.state.items)
      ]
    },
    {
      name: 'Login',
      path: '/login',
      component: require('views/Login.vue')
    }
  ]
})

function generateRoutesFromMenu (menu = [], routes = []) {
  for (let i = 0, l = menu.length; i < l; i++) {
    let item = menu[i]
    if (item.path) {
      routes.push(item)
    }
    if (!item.component) {
      item.component = resolve => require([`views/` + item.component + `.vue`], resolve)
      generateRoutesFromMenu(item.children, routes)
    }
  }
  return routes
}

另附menu items代码

const state = {
  items: [  // 什么菜单都不定义,完全由后端返回
  ]
}
const mutations = {
  [types.ADD_MENU] (state, menuItems) {
    if (menuItems.length > 0) {
      menuItems.map(function (item) {
        item.children.map(function (child) {
          child.component = lazyLoading(child.component)
        })
      })
      state.items.push(...menuItems)
    }
  },

lazyloding

export default (name, index = false) => () => import(`views/${name}${index ? '/index' : ''}.vue`)

git代码暂不能全部公开,有问题可留言。

**

更新:

现在公开git代码:
https://github.com/m3shine/vu...
注意:
本文讲的内容是基于cookie的,此次公开的代码是纯净的代码,不含业务,登录是基于vue-jwt-auth,动态路由部分原理跟本文讲到的地方一样。

**

打个区块链小程序的广告:
https://www.chainheader.com/

查看原文

miloer 关注了标签 · 2019-01-07

flutter

clipboard.png

Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平台开发高质量原生 UI 的移动 SDK。

Flutter is Google’s mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.

Flutter 官网:https://flutter.dev/
Flutter 中文资源:https://flutter-io.cn/
Flutter Github:https://github.com/flutter/fl...

关注 1016

miloer 收藏了文章 · 2018-12-21

基于moment写一个滑动日历

效果如图(日期可左右滑动)
clipboard.png

思路:
1、先得到相邻三个周的数据,初始化的时候容器向左移动一个视口的距离,确保中间周在可视范围(在可视范围的索引为1)
2、触摸移动阶段,比如向左移动,相当于改变可是范围的索引,也就是2,即向左移动过两个视口的范围
3、移动结束,这时右边已经没有待显示的数据,需要重组数据,再向后加一周,使当前显示的周在中间,同时需要改变显示的索引为1

1、用moment处理日期数据

在当前视口内显示本周的7天,由于需要滑动,所以事先还需要把今天以前的一周和以后的一周准备好

let today = moment().format('YYYY-MM-DD') // 当前日期:"2018-09-14"
moment(today).subtract(7, 'd').format('YYYY-MM-DD') // 上一周的今天:"2018-09-07"
moment(today).add(7, 'd').format('YYYY-MM-DD') // 下一周的今天:"2018-09-21"

得到数组: dates
clipboard.png

由此数据可以生成三个模板,分别表示上周,本周和下周,再根据此数据,计算上周,本周和下周的详情。

      getDays: function (day) {
        let arr = []
        /* 计算传进来的日期为星期几 */
        let weekOfDate = Number(moment(day).format('E'))
        // 提前定义好的: this.week = ['一', '二', '三', '四', '五', '六', '日']
        for (let i = 0; i < this.week.length; i++) {
          arr.push(
            {
              date: moment(day).subtract(weekOfDate - i - 1, 'd').format('YYYY-MM-DD'),
              week: this.week[i]
            }
          )
        }
        return arr
      }

遍历数组dates。分别传进getDays可的到三周的详情

clipboard.png

然后遍历数组进行页面渲染

            <template v-for="(item, index) in dates">
                <div class="slider">
                    <div class="day" v-for="(day, dayIndex) in getDays(item.date)">
                        <div :class="{today: day.date === defaultDate}">{{day.date.split('-')[2]}}</div>
                    </div>
                </div>
            </template>

这里,静态显示已经完成

为组件添加滑动功能

改写上方的页面渲染代码

   <div class="week-slider">
        <div
            class="sliders"
            ref="sliders"
            @touchstart="touchStart"
            @touchmove="touchmove"
            // 初始样式,应该向饰扣左方移动一个视口的距离,确保当前周在中间
            :style="getTransform" 
            @touchend="touchend"
            @webkit-transition-end="transitionEnd"
            @transitionend="transitionEnd">
            <template v-for="(item, index) in dates">
                <div class="slider">
                    <div class="day" v-for="(day, dayIndex) in getDays(item.date)">
                        <div :class="{today: day.date === defaultDate}">{{day.date.split('-')[2]}}</div>
                    </div>
                </div>
            </template>
        </div>
    </div>
     // actIndex: 当前活动视图的缩影,初始为1,sliderWidth:视口的宽度, distan: {x:0, y: 0}: 触摸移动的距离
     // 
     getTransform: function () {
        this.endx = (-this.actIndex * this.sliderWidth)  + this.distan.x
        let style = {}
        style['transform'] = 'translateX(' + this.endx + 'px)'
        // 这一条必须写,因为触摸移动的时候需要过渡动画,但是在动画结束重组数据的时候需要瞬间回到该去的位置,不能要过渡动画
        style['transition'] = this.isAnimation ? 'transform .5s ease-out' : 'none'
        return style
      }

最后触摸时间处理:

      touchStart: function (e) {
        this.start.x = e.touches[0].pageX
      },
      touchmove: function (e) {
        // 这里需要过渡动画
        this.isAnimation = true
        this.distan.x = e.touches[0].pageX - this.start.x
        // 需要移动的容器
        let dom = this.$refs.sliders
        // 向左
        this.endx = this.endx + this.distan.x
        dom.style['transform'] = 'translateX('+ this.endx + 'px)'
      },
      touchend: function (e) {
        this.isAnimation = true
        this.distan.x = e.changedTouches[0].pageX - this.start.x
        // 向右
        if (this.distan.x > 0) {
          this.direction = 'right'
          this.actIndex = 0
        } else if (this.distan.x < 0) {
          this.direction = 'left'
          this.actIndex = 2
        }
        this.distan.x = 0
      },

过渡结束后重置容器位置

// 过渡结束
      transitionEnd: function () {
        this.isAnimation = false
        if (this.actIndex === 2) {
            this.dates.push({
                date: moment(this.dates[this.actIndex].date).add(7, 'd').format('YYYY-MM-DD')
            })
            this.dates.shift()
            this.actIndex = 1
        }else if (this.actIndex === 0) {
            this.dates.unshift({
                date: moment(this.dates[this.actIndex].date).subtract(7, 'd').format('YYYY-MM-DD')
            })
            this.dates.pop()
            this.actIndex = 1
        }
      }
查看原文

miloer 赞了回答 · 2018-12-18

js eval函数解析问题?

你这样输出的结果实际上是

document.write(eval("'[1, 2, 3]'"))
document.write(eval("[1, 2, 3]").toString())

document.write 输出的是字符串,第一行 '[1, 2, 3]' 本身就是字符串可以直接输出,而第二行是数组,需要通过 toString() 来转换到 String 类型

[1, 2, 3].toString() // 1,2,3

关注 4 回答 2

miloer 回答了问题 · 2018-12-18

为什么document.querySelectorAll()返回的是NodeList对象,而不是HTMLCollection对象

关注 4 回答 1

认证与成就

  • 获得 120 次点赞
  • 获得 17 枚徽章 获得 1 枚金徽章, 获得 3 枚银徽章, 获得 13 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-01-20
个人主页被 2.1k 人浏览