先附上React官网中文版 & React官网英文版有很多问题都要通过查询官方文档来解决,要学会查文档
一、React 简介
1. 关于 React
整几个面试题来认识一下~~
什么是 React ?
React 是一个用于构建用户界面的 JavaScript 库。
- 是一个将数据渲染为 HTML 视图的开源 JS 库
- 它遵循基于组件的方法,有助于构建可重用的 UI 组件
- 它用于开发复杂的交互式的 web 和移动 UI
React 有什么特点?
- 使用虚拟 DOM ,不总是直接操作页面真实DOM
- 它可以用服务器渲染
- 它遵循单向数据流或数据绑定
- 高效
- 声明式编码,组件化编码
React 的一些主要优点?
- 它提高了应用的性能
- 可以方便在客户端和服务器端使用
- 由于使用 JSX,代码的可读性更好
- 使用React,编写 UI 测试用例变得非常容易
什么是声明式编程?
虚拟DOM帮助我们从命令式编程转到了声明式编程的模式, React官方的说法:Virtual DOM 是一种编程理念。 在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象 我们可以通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调(Reconciliation);
这种编程的方式赋予了React声明式的API:
- 你只需要告诉React希望让UI是什么状态;
- React来确保DOM和这些状态是匹配的;
- 你不需要直接进行DOM操作,只可以从手动更改DOM、属性操作、事件处理中解放出来
2. Hello React
我们在编写React代码时,有三个依赖都是必不可少的:
- React 核心库
- 操作 DOM 的 react 扩展库
- 将 jsx 转为 js 的 babel 库 引入方式:
- 方式一:直接CDN引入
- 方式二:下载后,添加本地依赖
- 方式三:通过npm管理(后续脚手架再使用)
//这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库,放在第一个-->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel"> /* 此处一定要写babel ---jsx*/
//1.创建虚拟DOM
const VDOM = <h1>Hello,React</h1> /* 此处一定不要写引号,因为不是字符串 */
// const VDOM = '<h1>Hello,React</h1> 加入单引号h1标签失效
//2.渲染虚拟DOM到页面
// ReactDOM.render(虚拟DOM,容器)
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
ReactDOM.render函数:
- 参数一:传递要渲染的内容,这个内容可以是HTML元素,也可以是React的组件 , 这里我们传入了一个h1元素,后面我们就会使用React组件
- 参数二:将渲染的内容,挂载到哪一个HTML元素上 ,这里我们已经提定义一个id为app的div
3. 虚拟 DOM 和真实 DOM 的两种创建方法
关于虚拟DOM: 1.本质是Object类型的对象(一般对象) 2.虚拟DOM比较“轻”(即内部属性少),真实DOM比较“重”(即内部属性多),因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。 3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
1. JS 创建虚拟 DOM
如果标签内部还有标签,则会再次使用React.createElement,十分繁琐,这里只是一个简单的实例,具体解释在下方
<!-- 准备好一个“容器” -->
<div id="test">这里原本的内容会被覆盖掉</div>
<script type="text/javascript">
//1.创建虚拟DOM
// const VDOM = React.createElement(标签名,标签属性,标签内容)
const VDOM = React.createElement('h1', { id: 'title' }, React.createElement('span', {}, 'Hello,React'))
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
2. Jsx 创建虚拟DOM
<!-- 准备好一个“容器” -->
<div id="test"></div>
<script type="text/babel" > /* 此处一定要写babel */
//1.创建虚拟DOM
const VDOM = ( /* 此处一定不要写引号,因为不是字符串 */
<h1 id="title">
<span>Hello,React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
js 的写法并不是常用的,常用jsx来写,毕竟JSX更符合书写的习惯
二、jsx 语法
1. 全称: JavaScript XML
2. react定义的一种类似于XML的JS扩展语法: JS + XML本质是React . createElement ( component , props , ... children ) 方法的语法糖
3. 作用: 用来简化创建虚拟DOM
1) 写法:
var ele = <h1>Hello JSX!</h1>
2) 注意1:它不是字符串, 也不是HTML/XML标签
3) 注意2:它最终产生的就是一个JS对象
1. 语法规则:
- 定义虚拟DOM,不能使用
"" - 标签中混入JS表达式的时候使用
{},js语句不能放入{}中 - 样式的类名指定不能使用class,使用
className - 内联样式要使用
{{}}包裹 - 不能有多个根标签,只能有一个根标签
- 标签必须闭合,自闭合也行,即如果是单标签,必须以
/结尾 - 如果小写字母开头,就将标签转化为 html 同名元素,如果 html 中无该标签对应的元素,就报错;如果是大写字母开头,react 就去渲染对应的组件,如果没有就报错
- 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写
<style>
.title {
background-color: orange;
width: 200px;
}
</style>
<script type="text/babel">
const myId = 'aTgUiGu'
const myData = 'HeLlo,rEaCt'
//1.创建虚拟DOM
const VDOM = (
<div>
<h2 className="title" id={myId.toLowerCase()}>
<span style={{ color: 'white', fontSize: '29px' }}>{myData.toLowerCase()}</span>
</h2>
<h2 className="title" id={myId.toUpperCase()}>
<span style={{ color: 'white', fontSize: '29px' }}>{myData.toLowerCase()}</span>
</h2>
<input type="text" />
</div>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
1. 注释
写在花括号里
ReactDOM.render(
<div>
<h1>小丞</h1>
{/*注释...*/}
</div>,
document.getElementById('example')
);
2. jsx嵌入数据
- 情况一:当变量是Number、String、Array类型时,可以直接显示 ,数组自动展开全部成员
- 情况二:当变量是null、undefined、Boolean类型时,内容为空; 如果希望可以显示null、undefined、Boolean,那么需要转成字符串; 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
- 情况三:对象类型不能作为子元素(not valid as a React child
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
// 1.在{}中可以正常显示显示的内容
name: "why", // String
age: 18, // Number
names: ["zgc", "wf"], // Array
// 2.在{}中不能显示(忽略)
test1: null, // null
test2: undefined, // undefined
test3: true, // Boolean
flag: true,
// 3.对象不能作为jsx的子类,报错
// friend: {
// name: "kobe",
// age: 40,
// },
};
}
render() {
return (
<div>
<h2>{this.state.name}</h2>
<h2>{this.state.age}</h2>
<h2>{this.state.names}</h2>
{/*不显示*/}
<h2>{this.state.test1}</h2>
<h2>{this.state.test2}</h2>
<h2>{this.state.test3}</h2>
{/*显示*/}
<h2>{this.state.test1 + ""}</h2>
<h2>{this.state.test2 + ""}</h2>
<h2>{this.state.test3.toString()}</h2>
<h2>{this.state.flag ? "你好啊" : null}</h2>
<h2>{this.state.friend}</h2>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
2. tip: JSX 小练习
一定注意区分:【js语句(代码)】与【js表达式】
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
(1). a
(2). a+b
(3). demo(1)
(4). arr.map()
(5). function test () {}
(6)三元运算符
(7)执行一个函数
2.语句(代码):
下面这些都是语句(代码):
(1).if(){}
(2).for(){}
(3).switch(){case:xxxx}
根据动态数据生成 li
const data = ['A','B','C']
const VDOM = (
<div>
<ul>
{
//不能放js语句,只能写js表达式
data.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
ReactDOM.render(VDOM,document.querySelector('.test'))
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstname: "zhang",
lastname: "bryant",
isLogin: false,
};
}
render() {
const { firstname, lastname, isLogin } = this.state;
return (
<div>
{/*1.运算符表达式*/}
<h2>{firstname + " " + lastname}</h2>
<h2>{20 * 50}</h2>
{/*2.三元表达式*/}
<h2>{isLogin ? "欢迎回来~" : "请先登录~"}</h2>
{/*3.进行函数调用*/}
<h2>{this.getFullName()}</h2>
</div>
);
}
getFullName() {
return this.state.firstname + " " + this.state.lastname;
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
案例1:
<!-- 2.编写React代码 -->
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
message: "Hello World",
movies: ["大话西游", "盗梦空间", "星际穿越", "流浪地球"],
};
}
render() {
const liArray = [];
for (let movie of this.state.movies) {
liArray.push(<li>{movie}</li>);
}
return (
<div>
<h2>电影列表1</h2>
<ul>{liArray}</ul>
<h2>电影列表2</h2>
<ul>
{this.state.movies.map((item) => {
return <li>{item}</li>;
})}
</ul>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
案例2:
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
counter: 0,
};
}
render() {
return (
<div>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={this.increment}>+1</button>
<button onClick={this.decrement.bind(this)}>-1</button>
<img src="" alt="" />
</div>
);
}
increment = () => {
this.setState({
counter: this.state.counter + 1,
});
};
decrement() {
this.setState({
counter: this.state.counter - 1,
});
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
3. jsx绑定属性
- 比如元素都会有title属性
- 比如img元素会有src属性
- 比如a元素会有href属性
- 比如元素可能需要绑定class
- 比如原生使用内联样式style 注意:
绑定style内联样式--有两个大括号
{{}}, 第一个表示里面的内容为js表达式,第二个表示里面的内容为一个对象,key:value格式,key采用小驼峰命名,value写字符串,如果去掉引号为变量
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
title: "标题",
imgUrl:
"http://p2.music.126.net/L8IDEWMk_6vyT0asSkPgXw==/109951163990535633.jpg",
link: "http://www.baidu.com",
active: true,
};
}
// 封装方法:图片地址后加参数控制图片大小
getSizeImage(imgUrl, size) {
// 参数控制图片大小
return imgUrl + `?param=${size}x${size}`;
}
render() {
const { title, imgUrl, link, active } = this.state;
return (
<div>
{/* 1.绑定普通属性 */}
<h2 title={title}>我是标题</h2>
<img src={this.getSizeImage(imgUrl, 140)} alt="" />
<hr />
<a href={link} target="_blank">
百度一下
</a>
{/* 2.绑定class */}
<div className="box title">我是div元素</div>
<div className={"box title " + (active ? "active" : "")}>
我也是div元素
</div>
<label htmlFor=""></label>
{/* 3.绑定style {{ ... }}*/}
<div style={{ color: "red", fontSize: "10px" }}>
我是div,绑定style属性
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
4. React事件绑定
如果原生DOM原生有一个监听事件,我们可以如何操作呢?
- 方式一:获取DOM原生,添加监听事件;
- 方式二:在HTML原生中,直接绑定onclick; 在React中是如何操作呢?
我们来实现一下React中的事件监听,这里主要有两点不同 :
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写;
- 我们需要
通过{}传入一个事件处理函数,这个函数会在事件发生时被执行;
1. this的绑定问题
在事件执行后,我们可能需要获取当前类的对象中相关的属性,这个时候需要用到this ,如果我们这里直接打印this,也会发现它是一个undefined .
为什么是undefined呢?
原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数; 而它内部调用时,并不知道要如何绑定正确的this;
如何解决this的问题呢?
- 方案一:bind给btnClick显示绑定this
- 方案二:使用 ES6 class fields 语法
- 方案三:事件监听时传入箭头函数(推荐)
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
message: "你好啊",
counter: 100,
};
this.btnClick = this.btnClick.bind(this);
}
render() {
return (
<div>
{/* 1.方案一: bind绑定this(显示绑定) */}
<button onClick={this.btnClick}>按钮1</button>
{/* 2.方案二: 定义函数时, 使用箭头函数 */}
<button onClick={this.increment}>+1</button>
{/* 2.方案三(推荐): 直接传入一个箭头函数, 在箭头函数中调用需要执行的函数*/}
<button
onClick={() => {
{
/*这里箭头函数的this为render里的this*/
}
this.decrement("why");
}}
>
-1
</button>
</div>
);
}
btnClick() {
console.log(this.state.message);
}
// 箭头函数中永远不绑定this
// ES6中给对象增加属性: class fields
increment = () => {
console.log(this.state.counter);
};
decrement(name) {
console.log(this.state.counter, name);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
2. 事件参数传递
在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数
- 情况一:获取event对象 : 很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为) 假如我们用不到this,那么直接传入函数就可以获取到event对象;
- 情况二:获取更多参数 :有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
movies: ["大话西游", "海王", "流浪地球", "盗梦空间"],
};
this.btnClick = this.btnClick.bind(this);
}
render() {
return (
<div>
<button onClick={this.btnClick}>按钮</button>
<ul>
{/*this.state.movies.map((每一项数据, 索引值, 整个数组)*/}
{this.state.movies.map((item, index, arr) => {
return (
<li
onClick={(e) => {
this.liClick(item, index, e);
}}
>
{item}
</li>
);
})}
</ul>
</div>
);
}
// 默认帮你传入event
btnClick(event) {
console.log("按钮发生了点击", event);
}
liClick(item, index, event) {
console.log("li发生了点击", item, index, event);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
5. React条件渲染
某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容:
- 在vue中,我们会通过指令来控制:比如v-if、v-show;
- 在React中,所有的条件判断都和普通的JavaScript代码一致; 常见的条件渲染的方式有哪些呢?
- 方式一:条件判断语句 适合逻辑较多的情况
- 方式二:三元运算符 适合逻辑比较简单
- 方式三: && 适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLogin: true,
};
}
render() {
const { isLogin } = this.state;
// 1.方案一:通过if判断: 逻辑代码非常多的情况
let welcome = null;
if (isLogin) {
welcome = <h2>欢迎回来~</h2>;
} else {
welcome = <h2>请先登录~</h2>;
}
return (
<div>
{welcome}
{/* 2.方案二: 三元运算符 */}
<button onClick={(e) => this.loginClick()}>
{isLogin ? "退出" : "登录"}
</button>
<hr />
<h2>{isLogin ? "你好啊, zgc" : null}</h2>
{/* 3.方案三: 逻辑与&& */}
{/* 逻辑与: 一个条件不成立, 后面的条件都不会进行判断了,
如果前面的条件都为真,则将最后面条件的结果作为整个表达式的结果返回出去 */}
<h2>{isLogin && "你好啊, zgc"}</h2>
{/*上下这两个的区别为当isLogin为false时h2标签是否渲染*/}
{isLogin && <h2>你好啊, zgc</h2>}
</div>
);
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin,
});
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
1. React实现 v-show的效果
主要是控制display属性是否为none
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLogin: true,
};
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin,
});
}
render() {
const { isLogin } = this.state;
return (
<div>
<button onClick={(e) => this.loginClick()}>
{isLogin ? "退出" : "登录"}
</button>
{/*React实现v-show*/}
<h2 style={{ display: isLogin ? "block" : "none" }}>
你好啊, coderwhy
</h2>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
6. React列表渲染--高阶函数
很多时候我们在展示一个数组中的数据之前,需要先对它进行一些处理:
- 比如过滤掉一些内容:filter函数
- 比如截取数组中的一部分内容:slice函数
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
names: ["abc", "cba", "nba", "mba", "dna"],
numbers: [110, 123, 50, 32, 55, 10, 8, 333]
}
}
render() {
return (
<div>
<h2>名字列表</h2>
<ul>
{
this.state.names.map(item => {
return <li>{item}</li>
})
}
</ul>
<h2>数字列表(过滤)</h2>
<ul>
{
this.state.numbers.filter(item => {
return item >= 50;
}).map(item => {
return <li>{item}</li>
})
}
</ul>
<h2>数字列表(过滤简写)</h2>
<ul>
{
this.state.numbers.filter(item => item >= 50).map(item => <li>{item}</li>)
}
</ul>
<h2>数字列表(截取)</h2>
<ul>
{
//截取前4条数据 0,1,2,3
this.state.numbers.slice(0, 4).map(item => {
return <li>{item}</li>
})
}
</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
三、 jsx的本质-源码
实际上,jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖, 所有的jsx最终都会被转换成React.createElement的函数调用。
createElement需要传递三个参数:
- 参数一:type 当前ReactElement的类型;如果是标签元素,那么就使用字符串表示 “div”; 如果是组件元素,那么就直接使用组件的名称;
- 参数二:config 所有jsx中的属性都在config中以对象的属性和值的形式存储
- 参数三:children 存放在标签中的内容,以children数组的方式进行存储;当然,如果是多个元素呢?React内部有对它们进行处理,处理的源码在下方
const message1 = <h2>Hello React</h2>; // jsx -> babel -> React.createElement()
const message2 = React.createElement("h2", null, "Hello React");
<!-- 准备好一个“容器” -->
<div id="test">这里原本的内容会被覆盖掉</div>
<script type="text/javascript">
//1.创建虚拟DOM
// const VDOM = React.createElement(标签名,标签属性,标签内容)
const VDOM = React.createElement('h1', { id: 'title' }, React.createElement('span', {}, 'Hello,React'))
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'))
</script>
//JSX代码
const VDOM = (
<h1 id="title"> <span>Hello,React</span> </h1>
)
1. 源码:
React.createElement在源码的什么位置呢?
Github React源码下载:github.com/facebook/re…
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
if (__DEV__) {
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
2. babel的官网中查看转换
我们知道默认jsx是通过babel帮我们进行语法转换的,所以我们之前写的jsx代码都需要依赖babel。 可以在babel的官网中快速查看转换的过程:babeljs.io/repl/#?pres…
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div>
<div className="header">
<h1 title="标题">我是标题</h1>
</div>
<div className="content">
<h2>我是页面的内容</h2>
<button>按钮</button>
<button>+1</button>
<a href="http://www.baidu.com">百度一下</a>
</div>
<div className="footer">
<p>我是尾部的内容</p>
</div>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById("app"));
</script>
转换成:
React.createElement("h2", null, header,content,footer...)格式
<script type="text/babel">
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
className: "header"
}, /*#__PURE__*/React.createElement("h1", {
title: "\u6807\u9898"
}, "\u6211\u662F\u6807\u9898")), /*#__PURE__*/React.createElement("div", {
className: "content"
}, /*#__PURE__*/React.createElement("h2", null, "\u6211\u662F\u9875\u9762\u7684\u5185\u5BB9"), /*#__PURE__*/React.createElement("button", null, "\u6309\u94AE"), /*#__PURE__*/React.createElement("button", null, "+1"), /*#__PURE__*/React.createElement("a", {
href: "http://www.baidu.com"
}, "\u767E\u5EA6\u4E00\u4E0B")), /*#__PURE__*/React.createElement("div", {
className: "footer"
}, /*#__PURE__*/React.createElement("p", null, "\u6211\u662F\u5C3E\u90E8\u7684\u5185\u5BB9")));
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>