解如何在 JavaScript 中轻松检查字符串是否仅包含数字。 匹配包含由字符分隔的数字的字符串。
要检查字符串是否仅包含 JavaScript 中的数字,请在此正则表达式上调用 test() 方法:^\d+$。 如果字符串仅包含数字,则 test() 方法将返回 true。 否则,它将返回 false。
例如:
function containsOnlyNumbers(str) {
return /^\d+$/.test(str);
}console.log(containsOnlyNumbers('HTML5')); // false
console.log(containsOnlyNumbers('1234')); // true
console.log(containsOnlyNumbers('3 bananas')); // false
RegExp test() 方法搜索正则表达式和字符串之间的匹配项。
/ 和 / 字符用于开始和结束正则表达式。
^ 字符标记字符串输入的开始,$ 字符标记字符串的结束。
\d 模式匹配字符串中的任何数字 (0 - 9)。
在 \d 之后添加 + 字符会使正则表达式匹配一次或多次出现的 \d 模式。
因此,正则表达式匹配一个以连续数字序列开头和结尾的字符串。
我们可以使用 [0-9] 模式来匹配数字。 此模式匹配 0 到 9 之间的任何数字字符。
function containsOnlyNumbers(str) {
return /^[0-9]+$/.test(str);
}console.log(containsOnlyNumbers('HTML5')); // false
console.log(containsOnlyNumbers('1234')); // true
console.log(containsOnlyNumbers('3 bananas')); // false
您可能会发现 [0-9] 比 \d 更具可读性,尤其是在您对正则表达式中的特殊字符不是很熟悉的情况下。
匹配包含由字符分隔的数字的字符串
有时我们希望匹配数字可能由特定字符(例如空格或逗号)分隔的字符串。
function containsOnlyNumbers(str) {
return /^(\d+,)*(\d+)$/.test(str);
}console.log(containsOnlyNumbers('123456789')); // true (separator not required)
console.log(containsOnlyNumbers('123,456,789')); // true
console.log(containsOnlyNumbers('123-456-789')); // false
我们使用这种格式的正则表达式来做到这一点:^(\d+{ch})*(\d+)$,其中 {ch} 是分隔数字的字符。
所以我们可以使用一个非常相似的正则表达式来匹配一个只包含用连字符分隔的数字的字符串:
function containsOnlyNumbers(str) {
return /^(\d+-)*(\d+)$/.test(str);
}console.log(containsOnlyNumbers('123456789')); // true
console.log(containsOnlyNumbers('123,456,789')); // false
console.log(containsOnlyNumbers('123-456-789')); // true
或空格:
function containsOnlyNumbers(str) {
return /^(\d+ )*(\d+)$/.test(str);
}console.log(containsOnlyNumbers('123456789')); // true
console.log(containsOnlyNumbers('123 456 789')); // true
console.log(containsOnlyNumbers('123-456-789')); // false
提示:如果您遇到过带有难以理解的模式的正则表达式,来自 MDN 文档的正则表达式备忘单可能会有所帮助。
像以前一样,我们可以使用 [0-9] 代替 \d 来表示正则表达式:
function containsOnlyNumbers(str) {
return /^([0-9]+-)*([0-9]+)$/.test(str);
}console.log(containsOnlyNumbers('123456789')); // true
console.log(containsOnlyNumbers('123,456,789')); // false
console.log(containsOnlyNumbers('123-456-789')); // true
关注七爪网,获取更多APP/小程序/网站源码资源!
TML 段落
段落是通过 <p> 标签定义的。
实例
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>这是段落。</p>
<p>这是段落。</p>
<p>这是段落。</p>
<p>段落元素由 p 标签定义。</p>
</body>
</html>
[/demo]
注释:浏览器会自动地在段落的前后添加空行。(<p> 是块级元素)
提示:使用空的段落标记 <p></p> 去插入一个空行是个坏习惯。用 <br /> 标签代替它!(但是不要用 <br /> 标签去创建列表。不要着急,您将在稍后的篇幅学习到 HTML 列表。)
不要忘记结束标签
即使忘了使用结束标签,大多数浏览器也会正确地将 HTML 显示出来:
实例
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>This is a paragraph.
<p>This is a paragraph.
<p>This is a paragraph.
<p>不要忘记关闭你的 HTML 标签!</p>
</body>
</html>
[/demo]
上面的例子在大多数浏览器中都没问题,但不要依赖这种做法。忘记使用结束标签会产生意想不到的结果和错误。
注释:在未来的 HTML 版本中,不允许省略结束标签。
提示:通过结束标签来关闭 HTML 是一种经得起未来考验的 HTML 编写方法。清楚地标记某个元素在何处开始,并在何处结束,不论对您还是对浏览器来说,都会使代码更容易理解。
HTML 折行
如果您希望在不产生一个新段落的情况下进行换行(新行),请使用 <br /> 标签:
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>
To break<br />lines<br />in a<br />paragraph,<br />use the br tag.
</p>
</body>
</html>
[/demo]
<br /> 元素是一个空的 HTML 元素。由于关闭标签没有任何意义,因此它没有结束标签。
<br> 还是 <br />
您也许发现 <br> 与 <br /> 很相似。
在 XHTML、XML 以及未来的 HTML 版本中,不允许使用没有结束标签(闭合标签)的 HTML 元素。
即使 <br> 在所有浏览器中的显示都没有问题,使用 <br /> 也是更长远的保障。
HTML 输出 - 有用的提示
我们无法确定 HTML 被显示的确切效果。屏幕的大小,以及对窗口的调整都可能导致不同的结果。
对于 HTML,您无法通过在 HTML 代码中添加额外的空格或换行来改变输出的效果。
当显示页面时,浏览器会移除源代码中多余的空格和空行。所有连续的空格或空行都会被算作一个空格。需要注意的是,HTML 代码中的所有连续的空行(换行)也被显示为一个空格。
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>春晓</h1>
<p>
春眠不觉晓,
处处闻啼鸟。
夜来风雨声,
花落知多少。
</p>
<p>注意,浏览器忽略了源代码中的排版(省略了多余的空格和换行)。</p>
</body>
</html>
[/demo]
(这个例子演示了一些 HTML 格式化方面的问题)
HTML 标签参考手册
标签 描述
<p> 定义段落。
<br /> 插入单个折行(换行)。
HTML 可定义很多供格式化输出的元素,比如粗体和斜体字。
下面有很多例子,您可以亲自试试:
HTML 文本格式化实例
文本格式化
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<b>This text is bold</b>
<br />
<strong>This text is strong</strong>
<br />
<big>This text is big</big>
<br />
<em>This text is emphasized</em>
<br />
<i>This text is italic</i>
<br />
<small>This text is small</small>
<br />
This text contains
<sub>subscript</sub>
<br />
This text contains
<sup>superscript</sup>
</body>
</html>
[/demo]
预格式文本
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<pre>
这是
预格式文本。
它保留了 空格
和换行。
</pre>
<p>pre 标签很适合显示计算机代码:</p>
<pre>
for i=1 to 10
print i
next i
</pre>
</body>
</html>
[/demo]
“计算机输出”标签
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<code>Computer code</code>
<br />
<kbd>Keyboard input</kbd>
<br />
<tt>Teletype text</tt>
<br />
<samp>Sample text</samp>
<br />
<var>Computer variable</var>
<br />
<p>
<b>注释:</b>这些标签常用于显示计算机/编程代码。
</p>
</body>
</html>
[/demo]
地址
[demo]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<address>
Written by <a href="mailto:webmaster@example.com">Donald Duck</a>.<br>
Visit us at:<br>
Example.com<br>
Box 564, Disneyland<br>
USA
</address>
</body>
</html>
[/demo]
缩写和首字母缩写
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<abbr title="etcetera">etc.</abbr>
<br />
<acronym title="World Wide Web">WWW</acronym>
<p>在某些浏览器中,当您把鼠标移至缩略词语上时,title 可用于展示表达的完整版本。</p>
<p>仅对于 IE 5 中的 acronym 元素有效。</p>
<p>对于 Netscape 6.2 中的 abbr 和 acronym 元素都有效。</p>
</body>
</html>
[/demo]
文字方向
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>
如果您的浏览器支持 bi-directional override (bdo),下一行会从右向左输出 (rtl);
</p>
<bdo dir="rtl">
Here is some Hebrew text
</bdo>
</body>
</html>
[/demo]
块引用
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
这是长的引用:
<blockquote>
这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。这是长的引用。
</blockquote>
这是短的引用:
<q>
这是短的引用。
</q>
<p>
使用 blockquote 元素的话,浏览器会插入换行和外边距,而 q 元素不会有任何特殊的呈现。
</p>
</body>
</html>
[/demo]
删除字效果和插入字效果
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>一打有 <del>二十</del> <ins>十二</ins> 件。</p>
<p>大多数浏览器会改写为删除文本和下划线文本。</p>
<p>一些老式的浏览器会把删除文本和下划线文本显示为普通文本。</p>
</body>
</html>
[/demo]
如何查看 HTML 源码
您是否有过这样的经历,当你看到一个很棒的站点,你会很想知道开发人员是如何将它实现的?
你有没有看过一些网页,并且想知道它是如何做出来的呢?
要揭示一个网站的技术秘密,其实很简单。单击浏览器的“查看”菜单,选择“查看源文件”即可。随后你会看到一个弹出的窗口,窗口内就是实际的 HTML 代码。
文本格式化标签
标签 描述
<b> 定义粗体文本。
<big> 定义大号字。
<em> 定义着重文字。
<i> 定义斜体字。
<small> 定义小号字。
<strong> 定义加重语气。
<sub> 定义下标字。
<sup> 定义上标字。
<ins> 定义插入字。
<del> 定义删除字。
<s> 不赞成使用。使用 <del> 代替。
<strike> 不赞成使用。使用 <del> 代替。
<u> 不赞成使用。使用样式(style)代替。
“计算机输出”标签
标签 描述
<code> 定义计算机代码。
<kbd> 定义键盘码。
<samp> 定义计算机代码样本。
<tt> 定义打字机代码。
<var> 定义变量。
<pre> 定义预格式文本。
<listing> 不赞成使用。使用 <pre> 代替。
<plaintext> 不赞成使用。使用 <pre> 代替。
<xmp> 不赞成使用。使用 <pre> 代替。
引用、引用和术语定义
标签 描述
<abbr> 定义缩写。
<acronym> 定义首字母缩写。
<address> 定义地址。
<bdo> 定义文字方向。
<blockquote> 定义长的引用。
<q> 定义短的引用语。
<cite> 定义引用、引证。
<dfn> 定义一个定义项目。
ue 模板的编译到渲染,结合源码的分析介绍从 template 到 AST,到 VNode(虚拟 DOM),再将 VNode 挂载渲染成真是的 DOM。本文从思路流程方面分析模板编译的整个过程,不着重一字一句的具体代码解读。这个过程的代码比较机械枯燥,可以参看文末的参考链接。
从 vue 模板到渲染成 dom,整个流程如图:
整体而言,Vue 的处理方式大致分为三步:
注:抽象语法树(AST)是指对源码进行解析后形成的树状的语法结构。通俗地理解,有了 AST 以后,后续处理可以直接在树结构上进行,不用再处理源码中的各种书写格式、括号优先级等问题。
实现编译的核心源码入口:
// src/compiler/index.js
export const createCompiler=createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
const ast=parse(template.trim(), options)
if (options.optimize !==false) {
optimize(ast, options)
}
const code=generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
parse() 方法是将源码转换成 AST 的方法,它的核心是调用 parseHTML() 方法解析。相关源码位于 src/compiler/parser/index.js。
parseHTML() 做的事就是从头扫描 HTML 字符串,按一定的规则判断当前字符是什么(标签、属性、文本、注释等等),并调用相应的回调方法,从而完成 HTML 字符串的解析,并返回 AST 树。
示例:
<!-- 模板 template -->
<div class="counter">
<div>{{ count }}</div>
<button @click="increment">+</button>
</div>
经过 parse / parseHTML 解析后生成的 AST 是这样的:
{
attrsList: [],
attrsMap: {
class: "counter"
},
children: [{
attrsList: [],
attrsMap: {},
children: [{
end: 37,
expression: "_s(count)",
start: 26,
text: "{{ count }}",
tokens: [{
@binding: "count"
}]
}],
end: 43,
parent: {...},
plain: true,
rawAttrsMap: {},
start: 21,
tag: "div",
type: 1,
},{
attrsList: [{
end: 69,
name: "@click",
start: 51,
value: "increment",
}],
attrsMap:{
@click: "increment",
},
children: [{
end: 71,
start: 70,
text: "+",
type: 3 // text
}],
end: 80,
events:{
click:{
dynamic: false,
end: 69,
start: 51,
value: "increment"
}
},
hasBindings: true,
parent: {...},
plain: false,
rawAttrsMap:{
@click:{
end: 69,
name: "@click",
start: 51,
value: "increment",
}
},
start: 43,
tag: "button",
type: 1
}],
end: 86,
parent: undefined,
plain: false,
rawAttrsMap:{
class:{
end: 20,
name: "class",
start: 5,
value: "counter"
}
}
start: 0,
staticClass: "\"counter\"",
tag: "div",
type: 1
}
这一步之后,进行 optimize() 处理。
optimize() 是对 AST 进行优化的过程,以提升后续渲染性能。这个方法位于 src/compiler/optimizer.js,作用是分析出纯静态的 DOM(不含表达式,可以直接渲染的 DOM),将它们放入常量中,在后续 patch 的过程中可以忽略它们。
optimize() 处理逻辑,当一个元素有表达式时肯定就不是静态的,当一个元素是文本节点时,肯定是静态的,如果子元素是非静态的,则父元素也是非静态的。
optimize() 处理后的 AST:
{
attrsList: [],
attrsMap: {
class: "counter"
},
children: [{
attrsList: [],
attrsMap: {},
children: [{
end: 37,
expression: "_s(count)",
start: 26,
// 看这里
static: false,
text: "{{ count }}",
tokens: [{
@binding: "count"
}]
}],
end: 43,
parent: {...},
plain: true,
rawAttrsMap: {},
// 看这里
static: false,
staticRoot: false,
start: 21,
tag: "div",
type: 1,
},{
attrsList: [{
end: 69,
name: "@click",
start: 51,
value: "increment",
}],
attrsMap:{
@click: "increment",
},
children: [{
end: 71,
start: 70,
// 看这里
static: true,
text: "+",
type: 3
}],
end: 80,
events:{
click:{
dynamic: false,
end: 69,
start: 51,
value: "increment"
}
},
hasBindings: true,
parent: {...},
plain: false,
rawAttrsMap:{
@click:{
end: 69,
name: "@click",
start: 51,
value: "increment",
}
},
start: 43,
// 看这里
static: false,
staticRoot: false,
tag: "button",
type: 1
}],
end: 86,
parent: undefined,
plain: false,
rawAttrsMap:{
class:{
end: 20,
name: "class",
start: 5,
value: "counter"
}
}
start: 0,
// 看这里
static: false,
staticClass: "\"counter\"",
staticRoot: false,
tag: "div",
type: 1
}
实现编译的核心源码入口:
// src/compiler/index.js
export const createCompiler=createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
const ast=parse(template.trim(), options)
if (options.optimize !==false) {
optimize(ast, options)
}
const code=generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
在 parse() 和 optimize() 运行完之后,执行 generate():
// src\compiler\codegen\index.js
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state=new CodegenState(options)
const code=ast ? genElement(ast, state) : '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
export function genElement (el: ASTElement, state: CodegenState): string {
//对一些标签属性的处理
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state) // 处理v-once
} else if (el.for && !el.forProcessed) {
return genFor(el, state) // 处理v-for
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag==='template' && !el.slotTarget) {
return genChildren(el, state) || 'void 0'
} else if (el.tag==='slot') {
return genSlot(el, state)
} else {
// component or element
let code
//组件的处理
if (el.component) {
code=genComponent(el.component, el, state)
} else {
//核心的body部分
//1、生成节点的数据对象data的字符串
const data=el.plain ? undefined : genData(el, state)
//2、查找其子节点,生成子节点的字符串
const children=el.inlineTemplate ? null : genChildren(el, state, true)
//3、将tag,data,children拼装成字符串
code=`_c('${el.tag}'${
data ? `,${data}` : '' // data
}${
children ? `,${children}` : '' // children
})`
}
// module transforms
for (let i=0; i < state.transforms.length; i++) {
code=state.transforms[i](el, code)
}
return code
}
generate() 返回的 render 表达式结构像这样:
_c(
// 1、标签
'div',
//2、模板相关属性的数据对象
{
...
},
//3、子节点,循环其模型
[
_c(...)
]
<template>
<div id="app">
<h1>Hello</h1>
<span>{{message}}</span>
</div>
</template>
// 上述dom 对应的虚拟 render 表达式
with(this){
return _c('div',{
attrs:{"id":"app"}
},
[
_c('h1',[
_v("Hello")
]),
_c('span',[
_v(_s(message))
])
])
}
示例中出现较多的方法是_c(),即 vm._c(),这个方法本质是 createElement() 的封装(源码在src\core\instance\render.js)。除此之外,在 render() 方法中,还有可能出现_v()、_s()、_h()、_m() 等诸多辅助方法。
// src/core/instance/render-helpers/index.js
export function installRenderHelpers (target: any) {
target._o=markOnce // 处理 v-once
target._n=toNumber // 处理修饰符.number <input v-model.number="age" type="number">
target._s=toString // ......
target._l=renderList
target._t=renderSlot // 处理 slot
target._q=looseEqual
target._i=looseIndexOf
target._m=renderStatic
target._f=resolveFilter
target._k=checkKeyCodes
target._b=bindObjectProps
target._v=createTextVNode
target._e=createEmptyVNode
target._u=resolveScopedSlots
target._g=bindObjectListeners
在后续组件进行挂载时,render 方法会被调用,这些辅助方法会将 render 转化为虚拟 DOM(VNode)。
虚拟 DOM(VNode)是什么样的?
可看一下 VNode 类:
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
// strictly internal
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: Function | void; // async component factory function
asyncMeta: Object | void;
isAsyncPlaceholder: boolean;
ssrContext: Object | void;
fnContext: Component | void; // real context vm for functional nodes
fnOptions: ?ComponentOptions; // for SSR caching
devtoolsMeta: ?Object; // used to store functional render context for devtools
fnScopeId: ?string; // functional scope id support
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag=tag
this.data=data
this.children=children
this.text=text
this.elm=elm
this.ns=undefined
this.context=context
this.fnContext=undefined
this.fnOptions=undefined
this.fnScopeId=undefined
this.key=data && data.key
this.componentOptions=componentOptions
this.componentInstance=undefined
this.parent=undefined
this.raw=false
this.isStatic=false
this.isRootInsert=true
this.isComment=false
this.isCloned=false
this.isOnce=false
this.asyncFactory=asyncFactory
this.asyncMeta=undefined
this.isAsyncPlaceholder=false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
说白了,虚拟 DOM 就是一种使用 JS 数据结构模拟 DOM 元素及其关系的方法。
DOM 操作之所以慢,主要有两方面原因:
使用 VNode 不再需要关注 DOM 元素所有的属性和方法,仅仅只需要关注元素类型、属性、子内容等即可,因此 VNode 可以解决上述两个导致 DOM 操作慢的问题。
除此之外,VNode 元素之间也会形成和 DOM 树类似的树状结构,开发者可以将 DOM 元素的对比、变更提前到 VNode 层面去完成,直到 VNode 完成变更以后,计算出发生变动的 VNode,最后再根据这些 VNode 去进行真实 DOM 元素的变更。这样就可以大大减少需要进行的 DOM 操作,从而提升性能。
虚拟 DOM(VNode) 因为是纯 JavaScript 数据结构,因此具有很好的跨平台性。
Vue.prototype.
mount定义在platforms/web/runtime/index.js,mount 本质上是调用了 mountComponent()。
// core/instance/lifecycle.js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
// 省略一大段对render的判断
callHook(vm, 'beforeMount')
let updateComponent
updateComponent=()=> {
vm._update(vm._render(), hydrating)
}
// 定义Watcher
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true)
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode==null) {
vm._isMounted=true
callHook(vm, 'mounted')
}
return vm
}
vm._update() 的第一个参数 render 方法返回的虚拟 DOM,其负责将虚拟 DOM 渲染到真实的 DOM 中。
// core/instance/lifecycle.js
// _update() 主要源码
const prevVnode=vm._vnode
vm._vnode=vnode
if (!prevVnode) {
// initial render
vm.$el=vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// updates
vm.$el=vm.__patch__(prevVnode, vnode)
}
这段代码首先判断了 vm._vnode 是否存在,如果不存在,则说明这个组件是初次渲染,否则说明之前渲染过,这一次渲染是需要进行更新。针对这两种情况,分别用不同的参数调用了 __patch__() 方法:
最底层的处理逻辑,还是通过原始的 dom 操作方法 insertBefore、appendChild 等处理渲染的。
作者:越谷
链接:https://juejin.cn/post/7380241307116748810
来源:稀土掘金
*请认真填写需求信息,我们会在24小时内与您取得联系。