习目标:了解JavaScript是如何与HTML结合来创建动态网页,网页中嵌入JavaScript的不同方式,JavaScript的内容类型及其与<script>的关系
<script>是由Netscape创造出来,后来加到HTML规范中的。
<script>有8个属性:
1、async:表示立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或者等待其他脚本加载。只对外部脚本文件有效。
2、charset:使用src属性指定代码字符集。这个属性很少用,因为大多数浏览器不在乎它的值。
3、crossorigin;配置资源请求的CORS(跨源资源共享)设置。默认情况下不使用CORS。crossorigin = “anonymous”配置文件请求不用设置凭据标志。crossorigin = ”use-credentials“设置凭据标志,意味着出站请求会包含凭据。
4、defer:表示脚本可以延迟到文档全部解析和显示后再执行。新版本中只能用于外部脚本。
5、integrity:允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource integrity),如果验证签名不匹配则脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。
6、language:此属性已被废止。
7、src:表示包含外部要执行的代码的外部文件。
8、type:代替language,表示代码块中脚本语言的内容类型(也称为MIME类型),按照惯例这个值始终都是”text/JavaScript“,尽管”text/JavaScript“和”text/ecmascript“都已经废弃。JavaScript文件的MIME类型通常是”application/x-javascript“,不过给type属性这个值的话可能会导致脚本被忽略。在非IE的浏览器中有效的值还有”application/JavaScript“和”application/ecmascript"。如果这个值是module,则代码会被当成是ES6模块,而且只有这时候代码中才能出现import和export关键字。
使用<script>的方式有内联和外嵌两种,只要把code写入<script>code</script>中就好,code中要是包含字符串“<script>”,只要加上转义字符“\”即可。
如果要外嵌JavaScript代码只要使用src属性来链接外部文件即可如:
<script src=“example.js”></script>
XHTML 文档中,可以忽略结束标签写成<script src=“example.js”/>即可,但是这在HTML中不能使用。
过去把JavaScript和CSS一起写在head中,但是这意味着必须下载所有code并解析和解释完成后才开始渲染页面,对于JavaScript很多的页面会导致页面渲染速度过慢,为解决这个问题,JavaScript一般写在body元素的页面内容的最后边,如下
<html>
<head></head>
<body>
message
<script>code<\script>
<\body>
</html>
在外联JavaScript时可以使用defer属性来推迟脚本的运行。可以写成:
<html>
<head>
<script defer src = "example.js">code<\script>
</head>
<body>
message
<\body>
</html>
async属性从脚本处理方式上与defer类似,但是不同的是标记async的脚本并不能保证脚本按照他们的出现顺序执行,比如:
<html>
<head>
<script sync src = "example1.js">code<\script>
<script sync src = "example2.js">code<\script>
</head>
<body>
message
<\body>
</html>
不能保证example1比example2先执行。
除了<script>以外还可以用其他方式加载脚本。因为JavaScript可以使用DOM API,所以通过向DOM中动态地加入script元素同样可以加载指定脚本。只要创建一个script元素并将其添加到DOM即可。
let script = document.createElement('script');
script.src = 'gibberish.js';
document.head.appendChild(script);
当然,在把 HTMLElement 元素添加到 DOM 且执行到这段代码之前不会发送请求。默认情况下,以这种方式创建的<script>元素是以异步方式加载的,相当于添加了 async 属性。不过这样做可能会有问题,因为所有浏览器都支持 createElement()方法,但不是所有浏览器都支持 async 属性。因此,如果要统一动态脚本的加载行为,可以明确将其设置为同步加载:
let script = document.createElement('script');
script.src = 'gibberish.js';
script.async = false;
document.head.appendChild(script);
以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响它们在资源获取队列中的优先级。根据应用程序的工作方式以及怎么使用,这种方式可能会严重影响性能。要想让预加载器知道这些动态请求文件的存在,可以在文档头部显式声明它们:
<link rel="preload" href="gibberish.js">
可扩展超文本标记语言(XHTML,Extensible HyperText Markup Language)是将 HTML 作为 XML的应用重新包装的结果。与 HTML 不同,在 XHTML 中使用 JavaScript 必须指定 type 属性且值为text/javascript,HTML 中则可以没有这个属性。XHTML 虽然已经退出历史舞台,但实践中偶尔可能也会遇到遗留代码,为此本节稍作介绍。在 XHTML 中编写代码的规则比 HTML 中严格,这会影响使用<script>元素嵌入 JavaScript 代码。下面的代码块虽然在 HTML 中有效,但在 XHML 中是无效的。
<script type="text/javascript">
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
</script>
在 HTML 中,解析<script>元素会应用特殊规则。XHTML 中则没有这些规则。这意味着 a < b语句中的小于号(<)会被解释成一个标签的开始,并且由于作为标签开始的小于号后面不能有空格,这会导致语法错误。避免 XHTML 中这种语法错误的方法有两种。第一种是把所有小于号(<)都替换成对应的 HTML实体形式(<)。结果代码就是这样的:
<script type="text/javascript">
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
</script>
这样代码就可以在 XHTML 页面中运行了。不过,缺点是会影响阅读。好在还有另一种方法。第二种方法是把所有代码都包含到一个 CDATA 块中。在 XHTML(及 XML)中,CDATA 块表示文档中可以包含任意文本的区块,其内容不作为标签来解析,因此可以在其中包含任意字符,包括小于号,并且不会引发语法错误。使用 CDATA 的格式如下:
<script type="text/javascript"><![CDATA[
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
]]></script>
在兼容 XHTML 的浏览器中,这样能解决问题。但在不支持 CDATA 块的非 XHTML 兼容浏览器中则不行。为此,CDATA 标记必须使用 JavaScript 注释来抵消:
<script type="text/javascript">
//<![CDATA[
function compare(a, b) {
if (a < b) {
console.log("A is less than B");
} else if (a > b) {
console.log("A is greater than B");
} else {
console.log("A is equal to B");
}
}
//]]>
</script>
这种格式适用于所有现代浏览器。虽然有点黑科技的味道,但它可以通过 XHTML 验证,而且对XHTML 之前的浏览器也能优雅地降级。
自 1995 年 Netscape 2 发布以来,所有浏览器都将 JavaScript 作为默认的编程语言。type 属性使用一个 MIME 类型字符串来标识<script>的内容,但 MIME 类型并没有跨浏览器标准化。即使浏览器默认使用 JavaScript,在某些情况下某个无效或无法识别的 MIME 类型也可能导致浏览器跳过(不执行)相关代码。因此,除非你使用 XHTML 或<script>标签要求或包含非 JavaScript 代码,最佳做法是不指定 type 属性。在最初采用 script 元素时,它标志着开始走向与传统 HTML 解析不同的流程。对这个元素需要应用特殊的解析规则,而这在不支持 JavaScript 的浏览器(特别是 Mosaic)中会导致问题。不支持的浏览器会把<script>元素的内容输出到页面上,从而破坏页面的外观。Netscape 联合 Mosaic 拿出了一个解决方案,对不支持 JavaScript 的浏览器隐藏嵌入的 JavaScript 代码。最终方案是把脚本代码包含在一个 HTML 注释中,像这样:
<script><!--
function sayHi(){
console.log("Hi!");
}
//--></script>
使用这种格式,Mosaic 等浏览器就可以忽略<script>标签中的内容,而支持 JavaScript 的浏览器则必须识别这种模式,将其中的内容作为 JavaScript 来解析。虽然这种格式仍然可以被所有浏览器识别和解析,但已经不再必要,而且不应该再使用了。在XHTML 模式下,这种格式也会导致脚本被忽略,因为代码处于有效的 XML 注释当中。
虽然可以直接在 HTML 文件中嵌入 JavaScript 代码,但通常认为最佳实践是尽可能将 JavaScript 代码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下。
可维护性。JavaScript 代码如果分散到很多 HTML 页面,会导致维护困难。而用一个目录保存所有 JavaScript 文件,则更容易维护,这样开发者就可以独立于使用它们的 HTML 页面来编辑代码。
缓存。浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。
适应未来。通过把 JavaScript 放到外部文件中,就不必考虑用 XHTML 或前面提到的注释黑科技。包含外部 JavaScript 文件的语法在 HTML 和 XHTML 中是一样的。在配置浏览器请求外部文件时,要重点考虑的一点是它们会占用多少带宽。在 SPDY/HTTP2 中,预请求的消耗已显著降低,以轻量、独立 JavaScript 组件形式向客户端送达脚本更具优势。比如,第一个页面包含如下脚本:
<script src="mainA.js"></script>
<script src="component1.js"></script>
<script src="component2.js"></script>
<script src="component3.js"></script>
...
后续页面可能包含如下脚本:
<script src="mainB.js"></script>
<script src="component3.js"></script>
<script src="component4.js"></script>
<script src="component5.js"></script>
...
在初次请求时,如果浏览器支持 SPDY/HTTP2,就可以从同一个地方取得一批文件,并将它们逐个放到浏览器缓存中。从浏览器角度看,通过 SPDY/HTTP2 获取所有这些独立的资源与获取一个大JavaScript 文件的延迟差不多。在第二个页面请求时,由于你已经把应用程序切割成了轻量可缓存的文件,第二个页面也依赖的某些组件此时已经存在于浏览器缓存中了。当然,这里假设浏览器支持 SPDY/HTTP2,只有比较新的浏览器才满足。如果你还想支持那些比较老的浏览器,可能还是用一个大文件更合适。
IE5.5 发明了文档模式的概念,即可以使用 doctype 切换文档模式。最初的文档模式有两种:混杂模式(quirks mode)和标准模式(standards mode)。前者让 IE 像 IE5 一样(支持一些非标准的特性),后者让 IE 具有兼容标准的行为。虽然这两种模式的主要区别只体现在通过 CSS 渲染的内容方面,但对JavaScript 也有一些关联影响,或称为副作用。本书会经常提到这些副作用。
IE 初次支持文档模式切换以后,其他浏览器也跟着实现了。随着浏览器的普遍实现,又出现了第三种文档模式:准标准模式(almost standards mode)。这种模式下的浏览器支持很多标准的特性,但是没有标准规定得那么严格。主要区别在于如何对待图片元素周围的空白(在表格中使用图片时最明显)。
混杂模式在所有浏览器中都以省略文档开头的 doctype 声明作为开关。这种约定并不合理,因为混杂模式在不同浏览器中的差异非常大,不使用黑科技基本上就没有浏览器一致性可言。标准模式通过下列几种文档类型声明开启:
<!-- HTML 4.01 Strict -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<!-- XHTML 1.0 Strict -->
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- HTML5 -->
<!DOCTYPE html>
准标准模式通过过渡性文档类型(Transitional)和框架集文档类型(Frameset)来触发:
<!-- HTML 4.01 Transitional -->
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<!-- HTML 4.01 Frameset -->
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<!-- XHTML 1.0 Transitional -->
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- XHTML 1.0 Frameset -->
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
准标准模式与标准模式非常接近,很少需要区分。人们在说到“标准模式”时,可能指其中任何一个。而对文档模式的检测(本书后面会讨论)也不会区分它们。本书后面所说的标准模式,指的就是除混杂模式以外的模式。
针对早期浏览器不支持 JavaScript 的问题,需要一个页面优雅降级的处理方案。最终,<noscript>元素出现,被用于给不支持 JavaScript 的浏览器提供替代内容。虽然如今的浏览器已经 100%支持JavaScript,但对于禁用 JavaScript 的浏览器来说,这个元素仍然有它的用处。<noscript>元素可以包含任何可以出现在<body>中的 HTML 元素,<script>除外。在下列两种情况下,浏览器将显示包含在<noscript>中的内容:
浏览器不支持脚本;
浏览器对脚本的支持被关闭。任何一个条件被满足,包含在<noscript>中的内容就会被渲染。否则,浏览器不会渲染<noscript>中的内容。
下面是一个例子:
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
<script defer="defer" src="example1.js"></script>
<script defer="defer" src="example2.js"></script>
</head>
<body>
<noscript>
<p>This page requires a JavaScript-enabled browser.</p>
</noscript>
</body>
</html>
这个例子是在脚本不可用时让浏览器显示一段话。如果浏览器支持脚本,则用户永远不会看到它。
JavaScript 是通过<script>元素插入到 HTML 页面中的。这个元素可用于把 JavaScript 代码嵌入到HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。本章的重点可以总结如下。
要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。
所有<script>元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的情况下,包含在<script>元素中的代码必须严格按次序解释。
对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。
可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。
可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。
通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。
本文主要理理js模块化相关知识。
涉及到内联脚本、外联脚本、动态脚本、阻塞、defer、async、CommonJS、AMD、CMD、UMD、ES Module。顺带探究下Vite。
假设你是一个前端新手,现在入门,那么我们创建一个html页面,需要新建一个index.html文件:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="content">hello world</p>
</body>
</html>如果需要在页面中执行javascript代码,我们就需要在 HTML 页面中插入 <script> 标签。
有2种插入方式:
1、放在<head>中
2、放在<body>中
比如,点击hello world之后,在hello world后面加3个感叹号的功能,我们在head中加入script标签,并给hello world绑定点击事件:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<script>
function myFunction() {
document.getElementById('content').innerHTML = 'hello world!!!'
}
</script>
</head>
<body>
<p id="content" onclick="myFunction()">hello world</p>
</body>
</html>如果加在body中,一般放在body的最后面:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="content" onclick="myFunction()">hello world</p>
<script>
function myFunction() {
document.getElementById('content').innerHTML = 'hello world!!!'
}
</script>
</body>
</html>简单的逻辑我们可以用这2种方式写,这种方式叫做内联脚本。
当逻辑复杂时,我们可以把上面的script标签中的代码抽取出来,比如在html的同级目录创建一个js文件夹,里面新建一个a.js的文件。
a.js中写上面script标签中的代码:
function myFunction() {
document.getElementById('content').innerHTML = 'hello world!!!'
}上面的script标签则可以改成:
<script src="./js/a.js"></script>上面的2种写法,浏览器在加载html时,遇到script标签,会停止解析html。
内联脚本会立刻执行;外联脚本会先下载再立刻执行。
等脚本执行完毕才会继续解析html。
(html解析到哪里,页面就能显示到哪里,用户也能看到哪里)
比如下面的代码:
<p>...content before script...</p>
<script src="./js/a.js"></script>
<p>...content after script...</p>解析到第一个p标签,我们能看到...content before script...显示在了页面中,然后浏览器遇到script标签,会停止解析html,而去下载a.js并执行,执行完a.js才会继续解析html,然后页面中才会出现...content after script...。
我们可以通过Chrome的Developer Tools分析一下index.html加载的时间线:
这会导致2个问题:
1、脚本无法访问它下面的dom;
2、如果页面顶部有个笨重的脚本,在它执行完之前,用户都看不到完整的页面。
对于问题2,我们可以把脚本放在页面底部,这样它可以访问到上面的dom,且不会阻塞页面的显示:
<body>
...all content is above the script...
<script src="./js/a.js"></script>
</body>但这不是最好的办法,我们接着往下看。
我们给script标签加defer属性,就像下面这样:
<p>...content before script...</p>
<script defer src="./js/a.js"></script>
<p>...content after script...</p>defer 特性告诉浏览器不要等待脚本。于是,浏览器将继续解析html,脚本会并行下载,然后等 DOM 构建完成后,脚本才会执行。
这样script标签不再阻塞html的解析。
这时再看时间线:
需要注意的是,具有 defer 特性的脚本保持其相对顺序。
比如:
<script defer src="./js/a.js"></script>
<script defer src="./js/b.js"></script>上面的2个脚本会并行下载,但是不论哪个先下载完成,都是先执行a.js,a.js执行完才会执行b.js。
这时,如果b.js依赖a.js,这种写法将很有用。
另外需要注意的是,defer 特性仅适用于外联脚本,即如果 script标签没有 src属性,则会忽略 defer 特性。
我们可以给script标签加async属性,就像下面这样:
<script async src="./js/a.js"></script>这会告诉浏览器,该脚本完全独立。
独立的意思是,DOM 和其他脚本不会等待它,它也不会等待其它东西。async 脚本就是一个会在加载完成时立即执行的完全独立的脚本。
这时再看时间线:
可以看到,虽然下载a.js不阻塞html的解析,但是执行a.js会阻塞。
还需要注意多个async时的执行顺序,比如下面这段代码:
<p>...content before script...</p>
<script async src="./js/a.js"></script>
<script async src="./js/b.js"></script>
<p>...content after script...</p>两个p标签的内容会立刻显示出来,a.js和b.js则并行下载,且下载成功后立刻执行,所以多个async时的执行顺序是谁先下载成功谁先执行。
一些比较独立的脚本,比如性能监控,就很适合用这种方式加载。
另外,和defer一样,async 特性也仅适用于外联脚本。
我们可以动态地创建一个script标签并append到文档中。
let script = document.createElement('script')
script.src = '/js/a.js'
document.body.append(script)append后脚本就会立刻开始加载,表现默认和加了async属性一致。
我们可以显示的设置script.async = false来改变这个默认行为,那么这时表现就和加了defer属性一致。
上面的这些写法,当script标签变多时,容易导致全局作用域污染,还要维护书写顺序,要解决这个问题,需要一种将 JavaScript 程序拆分为可按需导入的单独模块的机制,即js模块化,我们接着往下看。
很长一段时间 JavaScript 没有模块化的概念,直到 Node.js 的诞生,把 JavaScript 带到服务端,这时,CommonJS诞生了。
CommonJS定义了三个全局变量:
require,exports,modulerequire 读入并执行一个 js 文件,然后返回其 exports 对象;
exports 对外暴露模块的接口,可以是任何类型,指向 module.exports;
module 是当前模块,exports 是 module 上的一个属性。
Node.js 使用了CommonJS规范。
比如:
// a.js
let name = 'Lily'
export.name = name
// b.js
let a = require('a.js')
console.log(a.name) // Lily由于CommonJS不适合浏览器端,于是出现了AMD和CMD规范。
AMD(Asynchronous Module Definition) 是 RequireJS 在推广过程中对模块定义的规范化产出。
基本思想是,通过 define 方法,将代码定义为模块。当这个模块被 require 时,开始加载依赖的模块,当所有依赖的模块加载完成后,开始执行回调函数,返回该模块导出的值。
使用时,需要先引入require.js:
<script src="require.js"></script>
<script src="a.js"></script>然后可以这样写:
// a.js
define(function() {
let name = 'Lily'
return {
name
}
})
// b.js
define(['a.js'], function(a) {
let name = 'Bob'
console.log(a.name) // Lily
return {
name
}
})CMD(Common Module Definition) 是 Sea.js 在推广过程中对模块定义的规范化产出。
使用时,需要先引入sea.js:
<script src="sea.js"></script>
<script src="a.js"></script>然后可以这样写:
// a.js
define(function(require, exports, module) {
var name = 'Lily'
exports.name = name
})
// b.js
define(function(require, exports, module) {
var name = 'Bob'
var a = require('a.js')
console.log(a.name) // 'Lily'
exports.name = name
})UMD (Universal Module Definition) 目的是提供一个前后端跨平台的解决方案(兼容全局变量、AMD、CMD和CommonJS)。
实现很简单,判断不同的环境,然后以不同的方式导出模块:
(function (root, factory) {
if (typeof define === 'function' && (define.amd || define.cmd)) {
// AMD、CMD
define([], factory);
} else if (typeof module !== 'undefined' && typeof exports === 'object') {
// Node、CommonJS
module.exports = factory();
} else {
// 浏览器全局变量
root.moduleName = factory();
}
}(this, function () {
// 只需要返回一个值作为模块的export
// 这里我们返回了一个空对象
// 你也可以返回一个函数
return {};
}));AMD 和 CMD 是社区的开发者们制定的模块加载方案,并不是语言层面的标准。从 ES6 开始,在语言标准的层面上,实现了模块化功能,而且实现得相当简单,完全可以取代上文的规范,成为浏览器和服务器通用的模块解决方案。
ES6 的模块自动采用严格模式。模块功能主要由两个命令构成:export和import。
export命令用于规定模块的对外接口;
import命令用于输入其他模块提供的功能。
比如上面的代码,我们可以这样写:
// a.js
const name = 'Lily'
export {
name
}
// 等价于
export const name = 'Lily'
// b.js
import { name } from 'a.js'
console.log(name) // Lily
// b.js
import * as a from 'a.js'
console.log(a.name) // Lily此外,还可以用export default默认导出的写法:
// a.js
const name = 'Lily'
export default {
name
}
// b.js
import a from 'a.js'
console.log(a.name) // Lily如果只想运行a.js,可以只import:
// b.js
import 'a.js'我们可以给script标签加type=module让浏览器以 ES Module 的方式加载脚本:
<script type="module" src="./js/b.js"></script>这时,script标签会默认有defer属性(也可以设置成async),支持内联和外联脚本。
这时我们运行打开index.html,会发现浏览器报错了:
这是因为 type=module 的 script 标签加强了安全策略,浏览器加载不同域的脚本资源时,如果服务器未返回有效的 Allow-Origin 相关 CORS 头,会禁止加载改脚本。而这里启动的index.html是一个本地文件(地址是file://路径),将会遇到 CORS 错误,需要通过一个服务器来启动 HTML 文件。
在浏览器支持 ES Module 之前,我们用工具实现JavaScript模块化的开发,比如webpack、Rollup 和 Parcel 。但是当项目越来越大后,本地热更新越来越慢,而 Vite 旨在利用ESM解决上述问题。
Vite使用简单,可以去官网(https://cn.vitejs.dev/)看看。
老的规范了解即可,未来是ES Module的,用Vite可以极大的提升开发时的体验,生产环境用Rollup打包。
https://cn.vuejs.org/index.html
需要你做一下预习:https://cn.vuejs.org/v2/guide/index.html
数据驱动。如果我们要改变页面效果,不再需要直接操作dom元素,只需要改变数据就好。数据改变之后框架会自动的帮我们进行页面更新。
js最初的出现就是为了解决一个页面中弹出一个提示,或者做一个简单的计算。当时的环境下,浏览器可用的内存很小,为了解决这些问题,js语言必须简单、没有太复杂的数据结构、占用内存小。
但是随着时间的发展,网页的功能越来越复杂,需要的交互越来越多,js需要做的事情就更多。随着时代的发展,浏览器的厂家也越来越多,每家浏览器对js语法的支持也不一样。ECMA这个组织,建立一个统一的标准,在不停的制定一些语言语法的规范。
语言在发展的过程中,会吸取或者借鉴一下同行的一些优势,来完善自身。
为了解决业务场景的复杂化,出现了很多框架或者开发模式:
jQuery是前期出现的一个神级的插件,它提供了一个标准的元素选择方案,让我们可以快速的做元素选择。选中之后做后序的各种操作。它统一了各个浏览器中js语法的差异,使用jQuery写代码就不需要考虑各个版本浏览器中语法的差别
MVC框架Backbone,是早期经典的前端开发框架(jQuery+underscore.js+backbone.js+require.js);做SPA单页面应用程序开发
angular.js,是google的。分为两类:angular.js和angular
react.js,是facebook出的框架,目前是全球使用最广泛的。国内十家公司的react可能有十种写法
vue.js,是一个个人项目,目前是国内使用比较广泛的。国内十家公司的vue只能有一种写法
https://cli.vuejs.org/zh/
npm install -g @vue/cli # 全局安装vue脚手架,希望你成功刚才我初始化项目时选择的内容
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 2.x
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No翻译之后的中文版本
? Please pick a preset: Manually select features(选择使用手动方式创建项目)
? Check the features needed for your project: Choose Vue version, Babel, CSS Pre-processors, Linter(我现择了四项:1. 手动选择vue的版本【必选】,2.安装babel【必选】,3.css预处理,后面可以选择使用sass或者less等预处理语言,4.选择了代码规范性检测,写代码不符合规范时报错)
? Choose a version of Vue.js that you want to start the project with 2.x(选择vue2)
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)(选择使用sass)
? Pick a linter / formatter config: Basic(选择lint规范性检测的基础配置)
? Pick additional lint features: Lint on save(在保存的时候检测代码)
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files(把每一个插件的配置文件单独放置)
? Save this as a preset for future projects? No(以后都不使用这种配置)npm的配置文件,路径在windows的c:/users/你的用户名/.npmrc
registry=https://registry.npm.taobao.org/
init.author.email=你的邮箱
init.author.name=你的名字
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/vue中的路由插件,路由的作用是实现页面跳转。简单点理解就是再浏览器中访问指定的地址的时候展示的组件或者页面内容
https://router.vuejs.org/zh/
路由分两种模式:hash和history
区别:hash浏览器支持性好,不需要做额外的配置,可以直接使用;history模式再发布的时候需要做特殊的设置,在web服务器上做了配置之后才能使用;hash模式的路由,地址路径中有#进行分割,#后面的表示路径,history模式中没有#
npm i vue-router # 安装路由插件import Vue from 'vue'
import VueRouter from 'vue-router'
// 可以直接再组件中使用router-view和router-link等路由内置组件和对象($route和$router)
Vue.use(VueRouter)
const router = new VueRouter({
routes: [] // 路由表,或者叫路由数据,就是我们网文指定地址时候展示的组件
})参数传递之后,在对应的页面使用$route属性可以直接获取参数
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB,抽空了看看
beforeEach
afterEach
children
在一个组件中放多个router-view,通过name属性进行命名指定
在定义路由的时候使用components属性指定展示的组件,可以通过设置属性名为router-view的name属性,属性值为对应的组件的方式实现
new Router({
routes: [{
path: '/demo',
name: 'Demo',
components: {
default: ()=>import('.....'),
first: ()=>import('...')
}
}]
})https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},https://cn.vuejs.org/v2/api/#%E5%85%A8%E5%B1%80-API
https://vuex.vuejs.org/zh/
vuex是vue中的一个状态管理插件,通俗的讲就是一个全局的数据管理工具。作用是实现项目中数据的集中式管理。
vuex是遵循单向数据流机制的:就是数据是单向流动的,分为三部分(view,state,action)
在view视图中,通过dispatch派发一个action改变数据,数据改变之后view视图重新渲染
vuex中的数据流向:在组件中通过dispatch派发一个action,在action中获取数据,然后通过commit提交一个mutation改变数据,数据改变之后组件重新渲染
他们可以接收的参数为:
mapXxx('命名空间', [数组])
mapXxx('命名空间', {对象})
如果没有命名空间空间参数,表示获取根节点上的内容你有没有用过vuex?
两种回答方式:
vuex不是项目开发的时候必选的一个插件,但是在需要用的时候你要知道它的存在,它的作用就是显示数据在不同的组件之间进行共享的。
手机app
管理后台
https://lurongtao.gitee.io/felixbooks-interview2/
vue.config.js,所有的相关配置信息都在vue-cli的官网上:https://cli.vuejs.org/zh/config/#vue-config-js
https://webpack.docschina.org/ webpack官网,作为了解
module.exports = {
publicPath: "./", // 表示打包之后资源文件的加载路径
// 再做性能优化的时候,需要做到
/**
* 1. 路由文件的懒加载,使用 ()=> import('xxx')的方式引入,可以把路由组件单独打包成js文件,在需要使用的时候再引入
* 2. 使用cdn的方式引入第三方资源库
*
* **/
// configureWebpack,对webpack工具做额外的设置
configureWebpack: {
externals: {
// 属性名是js源代码中引入的时候使用的包名,属性值是引入js文件后再浏览器中可以直接使用的名字
vue: "Vue",
vuex: "Vuex",
"vue-router": "VueRouter",
axios: "axios",
"element-ui": "ELEMENT",
},
},
// 脚手架内置了一个node的开发服务器,可以直接让我们通过网络路径访问代码
devServer: {
// port: 998, // 改变开发服务器的端口号
proxy: {
// 访问以/api开头的地址时做一个代理转发
// 代理只有再开发的时候有用,打包之后就没用了
"/api": {
target: "https://papi.jiemian.com/page/api", // 目标服务器
ws: true, // 开启ws
changeOrigin: true, // 改变origin
pathRewrite: { "^/api": "" }, // 路径重写,把/api替换成空白
},
},
},
};
是一个基于vue语法的服务器端渲染(SSR)框架。使用vue语法编写多页面应用程序,就是每一次路由跳转打开的都是一个新的html文件。它解决了SPA单页面应用程序的一个通病(最怕刷新)。
https://www.nuxtjs.cn/
yarn是facebook出的一款包管理工具,和npm一样的功能
https://yarnpkg.com/
安装使用
npm i yarn -g # 全局安装yarn
yarn add xx # 安装模块,相当于 npm i xx
yarn remove xx # 删除模块,相当于 npm uninstall xx.nuxt打包文件、nuxt.config.js配置文件、package.json依赖配置文件、static静态文件放在服务器
*请认真填写需求信息,我们会在24小时内与您取得联系。