huey-ls的博客


  • 首页

  • 归档

  • 关于

Chrome 66版本视频/音频自动播放策略介绍

发表于 2020-01-22

前言

最近在做直播相关业务开发,chrome大佬将在 2020年底不支持flash啦,那现在html5的播放器开始成为了主流,各大平台也有了自己的html5播放器~ 觉得我们肯定也得赶紧抱上这个变化,就开始了html5视频播放开发之旅。

愉快的开发之旅中,总是能遇到一个神奇的问题: chrome 里尝试播放偶现了个报错

1
Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD

一开始以为是video.play 调用过早,于是放到到了 canplay 事件后执行。

1
2
3
video.addEventListener('canplay', () => {
video.play();
})

但是测试到时候发现依旧不能解决,难道又有什么新坑么?
仔细一看,chrome很贴心呀,已经放了一个链接https://goo.gl/xX8pDD于是赶紧打开看下。
发现这是 最新chrome增加了对视频自动播放控制的策略。

Chrome 66版本视频/音频自动播放策略介绍

先附上chrome大佬的说明: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes

新策略介绍

Chrome’s autoplay policies are simple:

  • Muted autoplay is always allowed.
  • Autoplay with sound is allowed if:
    • User has interacted with the domain (click, tap, etc.).
    • On desktop, the user’s Media Engagement Index threshold has been crossed, meaning the user has previously played video with sound.
    • The user has added the site to their home screen on mobile or installed the PWA on desktop.
  • Top frames can delegate autoplay permission to their iframes to allow autoplay with sound.

简单翻译一下是这个意思:

  • 静音的视频/音频可以自动播放
  • 以下情况的自动播放会被允许
    • 用户有点击等操作
    • PC:MEI(后面会介绍)够高
    • 移动端:你的站点被用户添加到首页,或者安装了pwa
  • 使用iframe时,如果外层有自动播放权限,并给iframe添加了 allow="autoplay"(后面提及的)

处理方案

我们能直接控制一般是这2条

  1. 用户有点击等操作
  2. 静音的视频/音频可以自动播放

1. 增加用户有点击等操作

可以修改你的交互场景,比如:

  • 可以在视频播放上放一个大的播放按钮,点击再开始播放
  • 点击按钮后弹窗里打开视频再播放
  • spa应用,跳转也是当前页面的,可以让用户无感知的体验自动播放

但是呢,以上这些都有一些局限性,离真正的自动播放还有一点差别的。

2. 针对静音的视频/音频可以自动播放这一特点兼容

这个一般针对页面一打开就需要自动播放一个视频的.

我们可以考虑如下代码进行兼容:

1
2
3
4
5
6
7
8
9
10
var video = document.getElementById('video');
video.play()
.then(() => {
// 播放成功啦
}, () => {
//播放失败
video.muted = true; // 设置为静音然后再次播发
video.play();
})

虽然被静音了,但是能保证视频还是正常被播放的。

2种方案虽然都不是特别完美,但这也chrome希望我们做的处理。
大家可以按照自己的需求,选择上面2种处理方案中的一种~

Media Engagement Index (MEI)

简单来说,这是chrome根据当前用户行为,对你网站视频的一个评分,评分越高,他就会允许你自动播放视频

可以通过一下子地址,查看你chrome里的信息~

1
chrome://media-engagement/

我们继续关注下提高评分的方法,提高评分有4个标准:

  1. Consumption of the media (audio/video) must be greater than 7 seconds.
    用户看视频/音频超过7秒
  2. Audio must be present and unmuted. 这段时间有音频且不是静音的。
  3. Tab with video is active. 页面所属的标签页是激活状态,也就是用户正在浏览你的页面
  4. Size of the video (in px) must be greater than 200x140. 视频尺寸得够大,需要超过200x140

这4个标准看上去就是chrome在评估,用户是不是真的在使用你的网页来看视频,只要用户持续使用,相信你的自动播放也不太会被限制。

PS: 用户如果手动清理缓存会清空这个评分的,不过用户这个操作一般不会太频繁吧..

小结

chrome的策略我们只能竟然去兼容.. 毕竟浏览器对于前端来说是爸爸级别的压制。

先用兼容方案让用户使用起来~ 慢慢的,如果用户真的一直使用你的网站,MEI的评分更高,也就太会被限制啦

最后推一下自己的 video播放器 popcorn-video正在不断的迭代开发,欢迎提建议和bug

现在已经支持react组件,在react框架下,相信会比video.js有更好的使用体验~

开通语雀啦啦啦

发表于 2020-01-22

我的语雀地址 https://www.yuque.com/huey-ls

2019年总结一下下

发表于 2020-01-01

2019 总结

2019年感慨一下

今年也是一个震荡的一年..

虽然一直在网易,但是一年待了3个部门,感觉都有点迷失了自己。顺便说,不知道是不是拖延症… 自己的计划永远赶不上

关于博客

2019年维护的太少(一直没多过吧,自动打脸),2020年还是继续加油,希望以后定位为日常开发经验总结的小博客吧

收获

个人自研小轮子

  1. http-fetch-client 和 cherry-tomato 定位也是明确了,也有了一些应用,觉得也算能用,有用武之地的东东了。以后我的代码设计应该也会在这个基础上做了。
  2. sugar 本来是计划于 node.js 做的web服务,正在初步尝试应用,根据使用不断修改,希望能早一日使用起来吧。

新接触的技术平台

2019年不断更换,有一点好的是接触的特别多的内容。比之前2年的内容都多,做过直播、搞过threejs,也开发了小程序,现在又来研究node.js。

其他框架使用

  1. lerna 真香,现在已经离不开他了,真是方便
  2. typescript 同样真香,配合 vscode 的支持,真是美滋滋

2020 展望一下

到了总结发现自己做的好想还是太少…

还是要希望多写一点,也能更多的做出点价值,能找到自己的未来的路。

迟到的微信小程序开发小结

发表于 2019-12-30

迟到的微信小程序开发小结

回顾一下…

从7月到9月,一直忙于微信小程序开发,发现一直没有总结过,就来总结下微信小程序的开发经验。

虽然项目本身壮志未酬身先死… 但是总结经验还是来一波的。

技术框架

  1. taro + typescript
  2. lerna
  3. 自研小框架 http-fetch-client + cherry-tomato

taro + typescript

为什么使用 taro

使用 taro 是调研了下当前主流的小程序框架

  1. wepy、mpvue等,感觉维护有点少
  2. taro 活跃度很高,更新也很快,维护的感觉很好
  3. 本人倾向于 react 语法.. 怎么感觉这才是关键
  4. 值得关注的新框架 kbone & remaxjs
    a. 这2个框架不同于其他,主要是运行时根据虚拟DOM生成需要的小程序节点,所以拥有比如其他依赖编译的框架更高的自由度,更接近 react/vue 原本的使用体验(当然原本的小程序能力限制还是有的,只是模版限制可能更少)
    b. 根据虚拟DOM生成需要的小程序节点 这一点带来灵活的好处同时也带来了更大的难度和风险,大家选择时要慎重。
    c. kbone 是微信官方推荐的
    d. remaxjs 是阿里某大佬开源的

为什么使用 typescript

既然已经使用了react, 再配上 typescript 不完全没问题么😊

typescript 在vscode里简直神器,各种提示各个规范,写起来都不用看原来都代码想是参数什么的

lerna

本来是想基于 lerna 做 monorepo ,方便以后扩展出 web/h5 等运行环境等,抽取公共代码。但是项目中落了… 还未考验运行效果。

中间遗留的问题及不完美的地方

  1. lerna 管理后的多个包… 每个都需要使用 babel or tsc 做转换,编译过程不是很实时,启动方式也比较麻烦,运行流程:
    a. lerna run dev --scope=主工程 启动主工程
    b. lerna run build:watch --scope=依赖包工程 watch依赖包工程
    c. 而且 小程序的npm包是需要从 node_module 复制到根目录(比如root/npm/xxx)才能被使用的,导致依赖包工程编译后还需要再复制一次,过程特别复杂,taro及微信小城本身的npm支持,都不支持watch并复制,所以得自己写 小程序npm 方案,结果一直没有特别好的运行下去。
    d. 猜想可以用解决方式:手动创建软连接,来使他像是一个工程的文件,暂时还没尝试,可以期待一下。
    f. 特别备注:web/h5 用webpack就很棒,直接可以打包 依赖包工程,完全没有以上问题。

推广一下自研小框架~

http-fetch-client http请求框架

为了支持微信小程序😄,现在已经支持注入请求模块了,意味着可以支持任意平台了~

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
import HttpFetchClient, { Response } from 'http-fetch-client';
const fetchClient = new HttpFetchClient({
adapter: (request) => {
const url = request.getURL();
return new Promise((
resolve, reject
) => {
resolve(new Response({
state: 200 // or 3xx,4xx, 500, xxx 等非0
readyState: 4,
headers: {...},
body: {...},
url: url
}))
reject(new Response({
state: 0 // 一般是超时或者 abort
readyState: 4,
headers: {...},
body: {...},
url: url
}))
})
}
})
fetchClient.get(...)
fetchClient.post(...)

cherry-tomato 小番茄真好吃

这是做 js Model 的,配合 typescript 让你轻松了解完整功能

基于 git submodule + lerna 的多项目工程

发表于 2019-07-01

目录

  • 第一篇:基于 lerna 的多项目工程
  • 第二篇:使用 git submodule 拆封仓库

第二篇:使用 git submodule 拆分仓库(书写中)

本文概要

额外知识点

git submodule

git submodule
官方大文档

demo

https://github.com/ignous/project-demo

前言:为什么还要git submodule

上一章节中已经讲了使用lerna管理依赖,但是为什么还要git submodule呢。

我们可以这么设想,随着项目的不断扩张,功能越来越多,需要的客户端也越来越多,需要支持web,h5,小程序等各个平台。也出现了各个不同业务,除了主业务外可能还会有数据统计、运营活动管理、订单支付等多个。单个仓库就越来越臃肿了,迫切的需要拆分子项目来分开管理。

使用 git submodule + lerna的建议目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root/
├── packages/ // 这里是使用到的 github/gitlab 上的公共模块
│ ├── account/ // 帐号模块
│ │ └── ... // 仓库代码
│ └── 其他模块/ // 其他模块
│ └── package.json
├── client/ // 这里放当前客户端,因为拆分了,一般一个仓库对应一个项目
│ ├── src/
│ │ └── ... // 这里就和平时的一样了
│ ├── babel.config.js // babel
│ ├── package.json
│ └── webpack.config.js // webpack 配置
├── .gitmodules // git submodule配置(本文重点)
├── .eslintrc.js // 所有项目统一的eslint配置,不用担心每个项目规则不一样了~
├── lerna.json // lerna 配置
└── package.json //

以后将会有多个仓库,每个仓库中的client对应当前项目的业务代码,packages是使用到的功能模块(依旧不需要npm)

基于 git submodule + lerna 的多项目工程

发表于 2019-07-01

目录

  • 第一篇:基于 lerna 的多项目工程
  • 第二篇:使用 git submodule 拆分仓库

第一篇:基于 lerna 的多项目工程

本文概要

本文主要是为了说明如何使用lerna在工程里管理多个依赖包及多个终端项目。并梳理一个基本的项目结构

额外知识点

lerna

lerna github
本文就不扩展了,有兴趣的同学可以看下

demo

https://github.com/ignous/project-demo

前言:为什么要建立单工程多项目

我们同时有 web + h5 + 后台的前端系统,3个项目分别部署,但是又有通用的部分。比如

  1. 登陆,3个系统是同样的登陆系统
  2. 直播。视频播放
  3. 文件上传系统,因为使用了第三方直穿的方式,需要前端和第三方api有紧密的操作。
    …

有各种各样相同的逻辑,但是又需要在三端呈现不同的ui。

重复书写多套肯定不可取,这样会带来大量的更新和维护难度。

当然方案还有npm,把公用模块发布成npm包的形式也是我们尝试过的方案,但是业务永远是多变的,npm包发布和更新版本带来的复杂度,会导致我们在联调和测试时发布多个beta版本,比如测试发现了个bug,你要修是不是又要升一个版本,交互视觉做一些细节调整又是一个版本。最后发现可能有30%甚至50%的时间用于版本的发布和更新。

基于这些原因我们就考虑是不是有更好的办法解决问题~

基于lerna的单工程多项目文件结构

这是我们思考的一个方案,单个工程,单个仓库,天生就解决的发布的问题,但是如何同时让多个客户端项目并存,我们采用了lerna

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root/
├── packages/ // 这里放公共模块
│ ├── account/ // 帐号模块
│ │ ├── src
│ │ │ └── ... // 功能代码
│ │ ├── index.js
│ │ └── package.json
│ └── 其他模块/ // 其他模块
│ └── package.json
├── clients/ // 这里放各个客户端,如web,h5,admin
│ ├── web/
│ │ ├── src/
│ │ │ └── ... // 这里就和平时的一样了
│ │ ├── babel.config.js // babel
│ │ ├── package.json
│ │ └── webpack.config.js // webpack 配置
│ ├── h5/
│ │ └── package.json
│ └── 其他项目/
│ └── package.json
├── .eslintrc.js // 所有项目统一的eslint配置,不用担心每个项目规则不一样了~
├── lerna.json // lerna 配置
└── package.json //

lerna配置

1
2
3
4
5
6
{
"packages": [
"packages/*",
"clients/*"
]
}

设置lerna中的包含 packages及clients文件夹下的项目

根目录下package.json的配置

1
2
3
4
5
6
{
scripts: {
"setup": "lerna link && lerna bootstrap", // 执行link关联项目及bootstrap安装项目中所有的依赖
"dev": "lerna run dev --stream"
}
}

这样我们就可以在根目录运行 npm run setup 完成所有包的安装和关联啦

npm run dev的命令后面会讲到,我们先来看下具体子项目的配置

packages中公共模块的配置

公共项目的package.json

以 account 模块为例

1
2
3
4
5
6
7
8
9
10
11
{
"name": "@packages/account",
"main": "index.js",
"files": [
"src",
"index.js",
"package.json"
],
"scripts": {
}
}

其实只是配置了name和main,都是npm的基本配置

如何在clients项目中使用公共模块(以web为例)

  1. clients/web/package.json 中添加基本配置

    1
    2
    3
    4
    5
    6
    {
    "name": "@clients/web",
    "script": {
    "dev": "webpack-dev-server --host 0.0.0.0"
    }
    }
  2. 添加依赖 继续编辑clients/web/package.json

    1
    2
    3
    4
    5
    {
    "dependencies": {
    "@packages/account": "^1.0.0",
    }
    }
  3. 根目录运行 npm run setup 或者 lerna link 进行关联的安装

  4. clients/web/src/main.js 添加代码

    1
    2
    3
    import account from '@packages/account';
    account.getName();
  5. 运行 npm run dev -- --scope @clients/web 启动服务查看效果

和使用普通的npm包一样简单方便

ok,这样包共用就很简单了,web和h5就可以同时用一个帐号逻辑啦。

webpack.config.js配置

但是这里还有一个小问题~

在web项目里我们用 webpack 编译,但是 @packages/* 在node_modules里,会默认被babel-loader 忽略,所以需要加个配置

为babel-loader添加配置

1
2
3
4
5
6
7
8
9
10
{
loader: 'babel-loader',
"include": [
"src",
path.resolve(__dirname, '../../packages/')
],
"exclude": [
"dist"
],
}

include 里的地址必须是包的实际地址, node_modules里的是link,直接使用会导致匹配不正确的

配置完成

可以直接在根目录运行npm run dev -- --scope @clients/web启动web项目的开发服务啦

也可以运行npm run dev 同时启动web+h5项目的开发服务啦

http-fetch-client介绍~

发表于 2017-10-20

前言

之前在公司业务开发的时候经常会遇到这么几个问题:

  1. 如何统一处理各种500、400或者网络中断等问题
  2. 当前端页面越来复杂,基于安全性,当用户离开过久就会导致页面失效,就需要用户手动刷新页面。面对这种情况,是否可以通过设置ajax发送刷新请求,然后重发当前请求处理,避免用户手动刷新

在jQuery年代,我们可以通过ajaxSuccess等全局监听处理问题1(但是对重发,由于没有封装一层Request, 不能容易优雅的进行重发)。

在现在Fetch的时代,global handle已经不见,但是在你的业务开发中确需要你再次封装。

基于以上的情况,我开始考虑自己封装一个,给自己也给大家分享。

特点

http-fetch-client采用了中间件的形式创建回调函数。

example:

1
2
3
4
5
import FetchClient from 'http-fetch-client';
let fetch = new FetchClient();
fetch.get(...).use((response, request) => {
// 这里就是你的回调哦~~
})

应用场景

如果你需要一个全局的FetchClient对你所有的请求进行控制,解决问题1&问题2~

如何创建retry的特殊用法

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
import FetchClient from 'http-fetch-client';
let fetch = new FetchClient();
let globalCount = 0;
let normalCount = 0;
fetch.use(function (response, request) {
// this 是 Handles 的实例
globalCount++;
if (response.text() !== 'success') {
// 创建一个新的FetchClient
let newFetchClient = new FetchClient();
// 重新绑定之前所有的回调函数
this.handleQueue.forEach((handle) => {
newFetchClient.use(handle);
});
newFetchClient.request(request);
return false;
}
});
fetch
.get('http://fake.com')
.use((response) => {
normalCount++;
console.log('response text', response.text());
console.log('normalCount', normalCount);
console.log('globalCount', globalCount);
})
// 如何请求第一次失败,返回不是success, 则重试一次,第二次才返回success
// console
// response text, success 成功拿到success
// normalCount, 1 业务回调只执行了1次
// globalCount, 2 全局重试执行了2次

Response

Method

  • text/json/blob
    对返回的内容进行格式化

    1
    2
    3
    4
    fetch.get(...).use((response) => {
    console.log(response.text()) // String: test
    console.log(response.json()) // Object: {a:'..'}
    })
  • getHeaders
    返回response headers

  • isTimeout 返回是否超时
  • isAborted 返回是否取消

Attribute

  • status {Number} http状态
  • ok {Boolean} http状态在200-300中间

Request

1
2
import { Request } from 'http-fetch-client';
new Request(url, options);

Method

  • getHeaders 获取请求头
  • setHeaders 设置请求头
  • getBody/getBodyForm/getBodyJson/getBodyFormData 获取请求body, 并转化为某种格式
  • setBody 设置请求body
  • getUrl/getUrlWithQuery(for GET) 获取url
  • getOptions 获取 options
  • setOptions 设置 options
  • getMethod 获取 method

Options Attribute

  • sendType {String} 发送的数据格式,支持 json/form(default),设置会会自动添加header中的 Content-Type

    1
    2
    3
    4
    {
    'json': 'application/json; charset=UTF-8', // default
    'form': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
  • acceptType {String} 接受的数据格式,支持 json(default),设置会会自动添加header中的 Accept

    1
    2
    3
    {
    'json': 'application/json,text/javascript' // default
    }
  • async {Boolean}

  • body|data {Object}
  • headers {Object}

更多用法

参考:https://github.com/ignous/http-fetch-client

备注

项目地址:https://github.com/ignous/http-fetch-client

npm安装

1
npm install --save http-fetch-client

git 本地和 remotes 及其他小知识

发表于 2016-08-09

初学git的时候总是会有这么2个问题:

  1. origin是什么?fetch 和 pull, push 和 commit 到底什么区别?
  2. push reject?
  3. 合并分支怎么回事?冲突了怎么办?

本文就是基于我的git使用经验来聊聊这2个问题

问题1:origin是什么?fetch 和 pull, push 和 commit 到底什么区别?

先附上图片一张
img
由图中可以看到本地其实有2个类似仓库的概念的东西
你每一次fetch,其实只是把远程服务器上的内容拉到你本地remotes下

再说origin:

origin是本地对远程仓库的默认别名
我们执行git remote -v查看设置

1
2
3
4
git remote -v
# 输出:
# origin git@github.com:your_name/your_repertory.git (fetch)
# origin git@github.com:your_name/your_repertory.git (push)

这个配置就能说明,我们执行git fetch origin的时候就会去git@github.com:your_name/your_repertory.git获取最新的远程代码。
同理push也就是把代码推到git@github.com:your_name/your_repertory.git

我们可以使用git remote rename origin rpo添加多个远程仓库
这个时候输出就会如下

1
2
3
4
git remote -v
# 输出:
# rpo git@github.com:your_name/your_repertory.git (fetch)
# rpo git@github.com:your_name/your_repertory.git (push)

由此可以看到origin只是你clone时,git添加的默认远程分支,你可以改它名字为任何名字,并不会是很多刚接触时,认为必须origin不可的概念。

push 和 commit

很多刚开始使用git的同学,都不太明白commit后为什么还要push
其实看上图可知commit只是修改了你本地的一个仓库内容,最后push才是完成推送到远程的一个动作(同时也会更新你本地的remotes)
所以,大家其实可以情景的在本地commit或其他操作,只要不使用push,完全不用担心远程仓库被搞坏。

fetch 和 pull

记住一个概念 pull = fetch + merge,相信你就不会有问题

问题2: push rejected??push 失败

1
2
3
4
5
6
7
8
$ git push origin master
To git@github.com:your_name/your_repertory.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:your_name/your_repertory.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

其实只是一个很常见的问题,当你们多人在同一个分支上合作开发的时候,你想要push到远程,然而在你之前另一位同学A,push了一个新的上去,此时你的remotes里的就不是最新的了,git不知道你的是对的,还是A同学的是对的,所以他就把后面的提交拒绝了,你也就收到了这个报错。
此时,我们只需要按提示的进行git pull(或者,推荐 git fetch 最新的在手动使用 rebase/merge 等操作),把A的更新到你的本地分支上,就可以成功的push了

问题3:冲突了怎么办?

其实这个教程非常的多,只是要切记冲突处理完前,请不要做切换分支等操作,正常的处理好冲突,如果无法处理时,使用abort取消才是正确的方式

未完待续,如果有需要补充或者疑问可以通过 git 的 issues 或者直接联系我,我会再补充

制作一个基于nodejs的命令行工具

发表于 2016-08-01

这是我个人的nodejs命令行工具开发和学习过程,总结起来留作记录和查询

例子

我们编辑bin/sum-cli.js写一点小脚本,计算2个值之和,代码如下:

1
2
3
4
5
6
#!/usr/bin/env node
const argv = process.argv.slice(2);
sum(argv[1], argv[2]);
function sum(a, b){
console.log(a + b);
}

#!/usr/bin/env node 的介绍请看最后小知识

npm bin

本文的关键,设置bin,让npm链接你的脚本到全局,我们在package.json中添加

1
2
3
bin: {
sum: 'bin/sum-cli'
}

npm link

执行

1
npm link

npm link可以直接链接到你的全局node_modules中,这样我们就不用通过npm install了

开始使用我们的命令吧

1
2
sum 1 2
#result 3

到此,我们已经成功完成一个脚本了

其它一些可能遇到的小知识点(也许这是本文中最有用的几点)

  • 如何在 bin 中开启 harmony 模式
    文件第一行处加入:

    1
    #!/usr/bin/env node --harmony

    这是 linux 脚本中指定脚本的解释程序,我们这里表示用node –harmony解释我们的脚本,自然就能开启 harmony 模式了

  • 命令行框架
    yargs
    Commander
    这2个功能强大,而且齐全,可作为开发主力使用

minimist 作者本次使用的命令行参数解析工具
拥有以下优点:
0依赖,超轻量,是你制作一个项目时,命令只作为辅助时的良好选择

first day

发表于 2016-07-21

第一篇博客,让我们继续努力吧

huey-LS

10 日志
13 标签
© 2020 huey-LS
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.3