1. js代码嵌入网页的方式
1. script标签嵌入脚本
script标签有个属性type,用于指定脚本的类型;
1. "text/javascript": 默认值,指定运行的是js代码
<script> </script>
2. "application/javascript": 在较新的浏览器中,指定是JS代码
<script src="application/javascript"> //TODO </script>
3. "text/babel": 指定脚本是JSX语法的代码;
使用前提是需要引入
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
babel文件将JSX语法中的标签,解析为React.createElement();如果不引入,则浏览器不识别text/babel类型;
另外,由于babel引入了React,则需要引入React文件;然后想要渲染需要ReactDOM文件;
<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>JSX</title> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> </script> </head> <body> <div id="root"></div> <script type="text/babel"> const a = [1,2].map(i => <div>{i}</div>); ReactDOM.render(a,root); </script> </body> </html>
4. 其他值: 不是上面的三种类型,浏览器不认识,则不执行,将其当作普通的标签。
可以获取标签内部的内容。element.text
<script type="x-custom"> console.log('sth'); </script>
2. script引入外部脚本
通过src属性,引入外部脚本。如果脚本文件内容使用了非英文字符,还要注明使用的字符编码。
脚本文件内容必须是纯js内容。外联script标签中间的内容会被忽略。
<script charset="utf-8" src="./test.js" ></script>
还有一个integrity属性: 内容是外部脚本的hash签名,验证脚本的一致性,用于防止外部修改外联脚本。
<script
src="/assets/application.js" integrity="sha256-TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs="> </script>
如果修改了外部脚本,签名将不再匹配,浏览器会拒绝加载。
3. 事件属性
<div id="root" onclick="const a = test();console.log(a)">ClickMe</div> <script type="text/babel"> function test() { return 5; } </script>
4. javascript协议
a标签的href属性,一般放置URL。但是也支持javacript协议,此时触发a标签,会执行协议后的js代码。
<a href="javascript: console.log(1)">点击</a> // 点击后,在控制台打印1
如果javascript:协议后,返回一个字符串,那么,原有文档消失,新建一个文档,文档内容只有该字符串。
<a href="javascript:(1).toString()">点击</a>
另: 浏览器的地址栏也支持该协议。
如果不想页面跳转,不想生成的新的文档取代原来的文档,可以使用void或者void 0;
<a href="javascript: void (1).toString()">点击</a>
或者
<a href="javascript: (1).toString(); void 0;">点击</a>
应用:
该协议可以用于标签栏。
<a href="http://www.baidu.com">百度</a>//百度的标签 // 操作: 鼠标可以直接拖动该链接到书签栏
也可以自定义书签栏,实现某个自定义功能
// 实现点击查看当前实现;可以将其拖入书签栏 <a href="javascript: alert(new Date().toLocaleString())">当前时间</a>
2. script标签加载协议
1. src属性
默认http协议
<script async src="localhost:3000/js/1.js"></script>
默认页面本身的协议(当前页面是https,下面js就是https)
<script async src="//localhost:3000/js/1.js"></script>
2. crossorigin属性
有两个枚举值。anonymous和use-credentials
crossorigin="anonymous" //跨域不发送凭证 相当于直接使用crossorigin crossorigin="use-credentials" // 跨域发送凭证
3. 浏览器组成
浏览器核心是:渲染引擎和js引擎。
1. 渲染引擎
1. 解析代码;HTML->DOM, CSS->CSSOM 2. 合并成渲染树 3. 布局 4. 绘制
以上步骤不是严格按照顺序执行;有时第一部分未完成,也会执行后面的步骤渲染。
2. 重流和重绘
1. 定义
渲染树转为网页布局,称为“布局流”(flow);
布局显示到页面,称为“绘制”(paint);
脚本操作或者样式表操作,都可能触发重流(reflow)或者重绘(repaint);
重流肯定导致重绘;重绘不一定需要重流。
2. 特征
浏览器会累计DOM变动
var foo = document.getElementById('foobar'); foo.style.color = 'blue'; //触发重绘 foo.style.marginTop = '30px'; //触发重流和重绘
上面代码虽然触发两次重绘,但是实际只会执行一次。因为浏览器累计变动。
基于DOM变动累计,优化如下:
示例:
<ul id="list"></ul> <script> const list = document.querySelector('#list'); const fruits = ['Apple', 'Orange', 'Banana', 'Melon']; fruits.forEach(fruit => { const li = document.createElement('li'); // 创建,读取DOM li.innerHTML = fruit; list.appendChild(li); //写入DOM }); </script> //混写会导致不停的重流重绘,每插入一个,重新渲染一次
2. 使用DocumentFragment或者VituralDOM
DocumentFragment不是真实的DOM树的一部分,它不会触发重新渲染。
示例:
// 解决第一部分的问题 const list = document.querySelector('#list'); const fruits = ['Apple', 'Orange', 'Banana', 'Melon']; const fragment = document.createDocumentFragment(); fruits.forEach(fruit => { const li = document.createElement('li'); li.innerHTML = fruit; fragment.appendChild(li); // 不会触发重新渲染 }); list.appendChild(fragment); //相当于一次性写入;即所有写入操作写在一起;只执行一次重流重绘
3. 使用window.requestAnimationFrame()
会将变动累计到下一次重流时执行;不会立即要求页面重流
// 读写DOM混写在一起,导致不停的重流重绘 function doubleHeight(element) { var currentHeight = element.clientHeight; //读取DOM element.style.height = (currentHeight * 2) + 'px'; //样式写入DOM } all_my_elements.forEach(doubleHeight); // 重绘代价低;只执行一次重流重绘 function doubleHeight(element) { var currentHeight = element.clientHeight; // 把所有的写操作累计在一起;下次重流时一次性执行完成 window.requestAnimationFrame(function () { element.style.height = (currentHeight * 2) + 'px'; }); } all_my_elements.forEach(doubleHeight);
4. 将样式变动写在一起
样式修改不要一项一项的修改;使用css的class一次性改变样式;。
5. 定位使用absolute或者fixed定位
可以减少对其他元素的影响;flex布局开销就会比较大
6. 隐藏元素使用visibility: hidden
隐藏元素不要使用display属性;它会导致布局变动;
而visibility属性不会更改文档的布局。只会重绘。
切换显示使用visibility: visible;
如果父元素设为hidden, 子元素设为visible; 子元素可见。
3. js引擎
js处理过程:
1. 词法分析 2. 语法分析->语法树 3. 生成字节码 4. 字节码在js引擎(虚拟机)上,在运行时通过js引擎的即时编译器(JIT)成机器码,并缓存编译结果。 // 或者 直接将源码通过js引擎的即时编译器编译成机器码
js引擎也可以叫做虚拟机。现有常见虚拟机
Chakra(Microsoft Internet Explorer) Nitro/JavaScript Core (Safari) Carakan (Opera) SpiderMonkey (Firefox) V8 (Chrome, Chromium)