<button id="go">点击我</button>
<script>
document.getElementById("go").addEventListener("click", () => {
openUrl("https://so.toutiao.com/search?dvpf=pc&keyword=Plzbefat");
});
function openUrl(url) {
window.open(url);
}
</script>
<a href="https://so.toutiao.com/search?dvpf=pc&keyword=Plzbefat" target="_blank"
>点击我</a
>
a 标签自带部分样式 , 点击过后会有颜色差
target参数详解:
一般使用a标签是不需要带上js逻辑的 , 这样使用起来简单明了 , 其余情况就使用 window.open() 就可以啦.
但是目前大多数使用 a 标签的时候 , 都觉得它原有的样式太丑 , 所以覆盖样式也是基本操作 .
css 对a标签的样式覆盖:
<style>
/* 未点击过链接*/
a:link {
color: red;
}
/* 已经点击过链接 */
a:visited {
color: green;
}
/* 鼠标移动到a标签上方 */
a:hover {
color: hotpink;
}
/* 按住不放链接的时候 */
a:active {
color: blue;
}
/* a标签的下划线 */
a {
text-decoration: none;
}
</style>
<a href="https://so.toutiao.com/search?dvpf=pc&keyword=Plzbefat" target="_blank"
>头条@Plzbefat</a
>
看看颜色和下划线
安卓Android系统是可以实现从Facebook中的网页,唤起默认的浏览器。
但是iOS最多能实现打开Safari,但是不能指定具体的网址。
window.location.href = 'https://www.baidu.com'
没有Facebook的“跳转外部浏览器”的弹窗出现,依然还是在Facebook的browser中刷新
facebook中打开的网页
window.location.href = `ftp://43.xxx.xxx.xxx/index.html`
中转网页中
window.open(”https://www.baidu.com”, “_self”);
Safari已经不支持ftp协议。
能弹出Facebook的“跳转外部浏览器”的弹窗,点“确定”后可以唤起Safari,但是Safari中的中转index.html不能解析,Safari的白色提示页面提示“ftp url is blocked”
const currentLink = location.href
const link = currentLink.replace('https://', '').replace('http://', '').replace('www.', '')
window.location.href = `x-web-search://${link}`
能弹出Facebook的“跳转外部浏览器”的弹窗,点“确定”后可以唤起Safari,但是进入的是Safari的默认搜素引擎的搜索界面,搜索输入框中是link的参数部分
如果使用以下的方式,那么只会出现一个网址是空的Safari界面
window.location.href = `x-web-search://`
window.location = `googlechrome://${link}`// ios to chrome
const currentLink = location.href
const link = currentLink.replace('https://', '').replace('http://', '').replace('www.', '')
if (ua.isAndroid()) {
window.location.href = `intent://${link}#Intent;scheme=https;end`// android
}
或者使用:
<script>
function isFacebookApp() {
var ua = navigator.userAgent || navigator.vendor || window.opera;
return (ua.indexOf("FBAV") > -1) || (ua.indexOf("FBAN") > -1);
}
if (isFacebookApp()) {
var currentLink = location.href;
if (currentLink.indexOf('https') > -1) {
var currentLink = currentLink.replace('https://', '');
currentLink = currentLink.replace('www.', '');
var chromeLink = "intent://" + currentLink + "#Intent;scheme=https;package=com.android.chrome;end";
window.location.href = chromeLink;
}
if (currentLink.indexOf('http') > -1) {
var currentLink = currentLink.replace('http://', '');
currentLink = currentLink.replace('www.', '');
var chromeLink = "intent://" + currentLink + "#Intent;scheme=http;package=com.android.chrome;end";
window.location.href = chromeLink;
}
}
</script>
// tryOpenDefault(() => {
// window.open(url, '_blank');
// }, 1000)
// tryOpenDefault(() => {
// window.location.href = url;
// }, 2000)
// tryOpenDefault(() => {
// window.open(url, '_system');
// }, 3000)
// tryOpenDefault(() => {
// window.location.href = 'intent://' + url + '#Intent;' + 'scheme=https;end';
// }, 4000)
// 会弹出跳转box,但是又快速退出回到帖子页
// tryOpenDefault(() => {
// var a = document.createElement('a');
// a.setAttribute('href', url);
// a.setAttribute('target', '_blank'); // Ensures it opens in a new tab/window
// a.click();
// }, 5000)
// window.location.href = `prefs://${link}`
// window.location.href = `x-safari-https://${link}` // box but not jump
// window.location.href = `site://${link}` // not work
// not work
// var a = document.createElement('a');
// a.setAttribute('href', currentLink);
// a.setAttribute('target', '_blank'); // Ensures it opens in a new tab/window
// a.click();
// not work again
// var a = document.createElement('a');
// a.setAttribute('href', currentLink);
// a.setAttribute('target', '_blank'); // Ensures it opens in a new tab/window
// var dispatch = document.createEvent("HTMLEvents");
// dispatch.initEvent("click", true, true);
// a.dispatchEvent(dispatch);
// window.open(location.href, '_blank') // not work
// window.location.href = location.href // not work
// window.location.href = `safari://${currentLink}` // can prompt box, but can not jump still
// window.location.href = `safari://${link}`// can prompt box, but can not jump
// window.location.href = `googlechrome://${link}`// can open chrome
目前经过各种尝试发现,在安卓上确实是可以通过intent的方式唤起系统的浏览器,但是iOS的Safari浏览器,并没有合适的方法唤起浏览器并打开对应的网址。
所以如果在iOS上的Facebook或者是其他app的内置浏览器(即in-app browser)上,想仅仅只通过web中来实现是做不到的。除非这个in-app浏览器所在的app是可以内置我们自己的代码的。
因为在iOS系统中,app打开Safari的方式都是通过iOS的系统API:
[[UIApplication sharedInstance] openUrl:@"https://xxx.xxx.xxx"]
这样的方式来实现跳转Safari的。所以除非web和app有通信机制,调用iOS原生代码的这个API。
而且即使通过在Mac上的应用程序右键Safari浏览器,点击“查看内容”,打开Safari应用的info.plist,查看Safari的URL Scheme,也就只有有限的http、https、ftp等深链接。
我在Mac上测试时,发现是可以通过以下代码:(有点忘了是不是safari开头,应该还有一个x-safari-http的scheme头,还是webkit:这个)
window.location.href = `safari://43.xxx.xxx.xxx/index.html`
在Mac上是可以从谷歌Chrome浏览器跳转打开Safari的,但是在移动端是不行的。
所以在iOS的第三方app的内置浏览器中,想打开系统Safari浏览器,最好还是要做一个引导的浮层,指向右上角的三个点,引导用户主动点击Facebook等第三方app的“打开外部浏览器”选项。
们经常写 HTML 、 CSS 和 JavaScript ,写好这些之后,我们就会在浏览器中看到页面,那浏览器究竟在这背后做了一些什么事情呢?本篇文章将揭晓答案!
了解浏览器的渲染原理是我们在通往更深层次的前端开发中不可缺少的,它可以让我们从更深层次、角度去考虑性能优化等~
下面进入正文~
浏览器会分配一个线程“自上而下,从左到右”依次解析和渲染代码,那么进程和线程是什么,它们之间有着怎样的关系呢?
一个进程就是一个程序运行的实例。当启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码,运行中的数据和一个执行任务的主线程,这样的一个运行环境就叫进程
线程不能单独存在,它是由进程来启动和管理的。线程依附于进程,进程中使用多线程并行处理能提升运算效率
1、进程中的任意一线程执行出错,都会导致整个进程的崩溃
2、线程之间可以共享数据
3、当一个进程关闭后,操作系统会回收进程所占用的内存
4、进程之间的内容相互隔离
了解浏览器的渲染原理,我们就要从理解 HTML 、 CSS 和 JavaScrip 开始,我们先来看一张图
HTML (超文本标记语言),顾名思义,由标记(标签)和文本组成,每个标签都有自己的语意,浏览器会根据标签和文本展示对应的内容。
CSS (层叠样式表),由选择器和属性组成,它可以改变 HTML 的样式,比如上图中,我们改变了 span 的颜色由蓝色为绿色。
JavaScript ,我们可以通过 JS 完成很多事情,例如上图中修改样式。
下面开始分析渲染的原理
渲染模块由于渲染的机制的复杂,被划分为了很多子阶段,输入的 HTML 经过这些子阶段,最后会输出为像素。这样的处理流程就叫做 渲染流水线
按照渲染的时间顺序,流水线可分为几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成
由于浏览器无法直接理解和使用 HTML ,所以需要将 HTML 转换为浏览器能够理解的结构( DOM 树)
我们来分析一下下面这段代码会构建出一棵什么样的 DOM 树
我们先将上面的代码运行,然后在浏览器控制台输入 document ,看看会有什么效果
我们一层级一层级的打开就会看到如上图的效果,我们可以根据这每一层级展开的效果,绘制出一棵 DOM 树结构,如下:
接下来,我们试一下利用 JS 修改一下内容,看有什么改变:
我们可以看到“浏览器”的文字变成了“chrome”
再来看一下 DOM 树是否有改变
我们看到在“浏览器”的位置换成了“chrome”,那么如何让 DOM 节点拥有样式?
样式计算,顾名思义,就是 计算出 DOM 节点中每个元素的具体样式 ,这个阶段会分为三部分:
浏览器会新开辟一个线程,去服务器获取对应的资源文件(不阻碍主线程的渲染)
从上到下解析,解析完继续解析 DOM 结构。在真实项目中,如果 css 代码不是很多,或是移动端项目,我们应该使用内嵌式,以此来减少 http 资源的请求,提高页面渲染速度
它是同步的,不会开辟新线程去加载资源文件,而是让主线程去获取,这阻碍 DOM 结构的继续渲染;只有把外部样式导入进来,并且解析后,才会继续渲染 DOM 结构
浏览器就像不能理解 HTML 一样,不理解 CSS ,所以当渲染引擎接收到 CSS 文件时,会执行转换操作,将 CSS 文本转换为浏览器可以理解的 styleSheets 结构。
在 HTML 中,在浏览器中输入 document 可以查看 html 的结构。在 css 中,可以输入 document.styleSheets 看到 css 的结构
现在的结构是空的,我们来加一些样式,看看效果
属性值标准化就是将所有值转换为渲染引擎容易理解的、标准化的计算值。我们大致看一下效果:
body {
font-size: 2em;
color: black;
font-weight: bold;
...
}
复制代码
body {
font-size: 16px;
color: rgb(0, 0, 0);
font-weight: 700;
...
}
复制代码
样式计算有两个CSS的规则:继承规则和层叠规则
CSS 继承就是每个 DOM 节点都包含有父节点的样式。我们来看一下下面这段代码中如何应用到 DOM 节点上
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
h1 {
color: red;
}
div {
color: blue;
}
span {
font-size: 16px;
}
</style>
</head>
<body>
<h1>掘金</h1>
<div>
<span>浏览器</span>
<span>渲染原理</span>
构建DOM树
</div>
</body>
</html>
复制代码
子节点会拥有父节点的样式,由此我们可以画出这样一张图
我们还可以打开控制台,看一下选中 span 标签,都会看到哪些内容
通过上图,我们可看到一个元素的样式、继承过程等, userAgent 样式是浏览器默认的内置样式,如果我们不提供任何样式,就会使用此样式。
层叠在 CSS 处于核心地位,它是 CSS 的一个基本特征,它定义了如何合并来自多个源的属性值的算法。
样式计算阶段最终输出的内容是每个 DOM 节点的样式,并且保存在了 ComputedStyle 中。我们可以通过控制台看到某个 DOM 元素最终的计算样式
现在我们不知道 DOM 元素的几何位置信息,所以现在我们需要计算出 DOM 树中可见元素的几何位置,这个计算过程就叫做布局。布局阶段有两个过程:
创建布局树的意思就是创建一棵只包含可见元素的树。我们来看下面一段代码创建布局树的过程
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
h1 {
color: red;
}
div {
color: blue;
}
div span {
font-size: 16px;
}
div span:last-child {
display: none;
}
</style>
</head>
<body>
<h1>掘金</h1>
<div>
<span>浏览器</span>
<span>渲染原理</span>
构建DOM树
</div>
</body>
</html>
复制代码
构建布局树的过程中, DOM 树中所有不可见的节点都不会包含在这棵树中。浏览器会遍历 DOM 树中所有能看见的节点,然后把这些节点加入到布局中;不可见的节点就会被忽略, head 标签下面的内容、 div 下最后一个 span 节点都不会在布局树中,我们看一下这个过程图感受一下~
布局计算就是计算布局树节点的坐标位置。这个计算过程极为复杂。
渲染引擎会为特定的节点生成专用的图层,并生成一棵对应的图层树。这样做是因为页面中可能含有很多复杂的效果,我们可以打开控制台看一下页面的分层情况
我们可以看到,渲染引擎给页面分了很多图层,这些图层会按照一定顺序叠加在一起,形成最终的页面
那么图层的来源有哪些?
1、拥有层叠上下文属性的元素会被提升为单独的一层
层叠上下文可以使能够使 HTML 元素具有三维的概念,这些 HTML 元素按照自身属性的优先级分布在垂直于这个二维平面的 z 轴上。哪些元素具有层叠上下文属性?
2、需要剪裁的地方会被创建为图层
当我们创建一个有宽度和高度的 div 时,里面的文字内容可能会超出这个区域,这时候渲染引擎会把裁剪文字内容的一部分用于显示在 div 区域,例如
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
background: yellow;
overflow: auto;
}
</style>
</head>
<body>
<div>
我们经常写`HTML`、`CSS`和`JavaScript`,写好这些之后,我们就会在浏览器中看到页面,那浏览器究竟在这背后做了一些什么事情呢?本篇文章将揭晓答案!
了解浏览器的渲染原理是我们在通往更深层次的前端开发中不可缺少的,它可以让我们从更深层次、角度去考虑性能优化等~
</div>
</body>
</html>
复制代码
运行结果
我们再打开控制台的 layers 看一下效果
可以看到渲染引擎为文字部分单独创建了一个图层。
在布局树中的节点如果拥有对应的图层,这个节点就是一个图层,如果没有,这个节点就属于父节点的图层,如下图:
创建好图层树后,渲染引擎会绘制图层树中的每个图层。渲染引擎会将图层绘制分解为很多小的绘制指令,然后将这些指令按照顺序组成待绘制列表,我们可以打开控制台的 layers ,选择 document 层,看一下效果
栅格化就是将图块转换位位图,图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有图块的栅格化都是在线程池内执行的。
图层绘制列表准备好之后,主线程会把这个绘制列表提交给合成线程,绘制操作由渲染引擎中的合成线程来完成。
合成线程将图层划分为图块,然后合成线程会按照视口(可见区域)附近的图块优先生成位图。
所有的图块都被光栅化后,合成线程会生成一个绘制图块的命令( DrawQuad ),然后将该命令提交给浏览器进程。浏览器进程里面 viz 组件用来接收 DrawQuad 命令,将其页面内容绘制到内存中,最后将内存显示到屏幕。这个时候,我们就看到了页面
根据上文中描述,我们可以画出这样一张图
我还在网上找到了另外一张图
这两张图都是描述浏览器的渲染流程的。
*请认真填写需求信息,我们会在24小时内与您取得联系。