二、vue3模板语法之列表渲染、OptionsAPI与侦听器

96 阅读1分钟

一. 模板语法

1.1. 事件绑定 v-on用法

1.1.1. v-on各种写法

  • v-on:click="counter++"
  • v-on:click
  • @click
  • 别的事件
  • v-on="{click: xxxx}"
  • 修饰符stop
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        width: 100px;
        height: 100px;
        background-color: orange;
        margin-top: 10px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1.完整的写法 -->
      <div class="box" v-on:click="divClick"></div>
​
      <!-- 2.语法糖写法 -->
      <div class="box" @click="divClick"></div>
​
      <!-- 3.绑定的方法位置,也可以写成一个表达式(不常用,不推荐) -->
      <h2>{{counter}}</h2>
      <button @click="increment">+1</button>
      <button @click="counter++">+1</button>
​
      <!-- 4.绑定其他方法 -->
      <div class="box" @mousemove="divMouseMove"></div>
​
      <!-- 5.元素绑定多个事件 -->
       <div class="box" @click="divClick" @mousemove="divMouseMove"></div>
       <!-- <div class="box" v-on="{click:divClick,mousemove:divMouseMove}"></div> -->
       <!-- <div class="box" @="{click:divClick,mousemove:divMouseMove}"></div> -->
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            message: "Hello Vue",
            counter: 0,
          };
        },
        methods: {
          divClick() {
            console.log("divClick");
          },
          increment() {
            this.counter++;
          },
          divMouseMove() {
            console.log("divMouseMove")
          }
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

1.1.2. 各种参数方式

  • 默认传递 event

  • 自定义参数:

    • name, age, $event
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <!-- 1.默认传递event对象 -->
      <button @click="btn1Click">按钮1</button>
​
      <!-- 2.只有自己的参数 -->
      <button @click="btn2Click('why',age)">按钮2</button>
​
      <!-- 3. 自己的参数和event对象-->
      <!-- 在模板中想要明确的获取event对象:$event -->
      <button @click="btn3Click('why',age,$event)">按钮3</button>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            message: "Hello Vue",
            age: 18,
          };
        },
        methods: {
          // 1.默认参数:event对象
          // 总结:如果在绑定事件的时候,没有传递任何的参数,那么event对象会被默认传递进来
          btn1Click(event) {
            console.log("btn1Click", event);
          },
          // 2.明确参数:
          btn2Click(name, age) {
            console.log("btn2Click", name, age);
          },
​
          // 3.明确参数+event对象
          btn3Click(name, age, event) {
            console.log("btn3Click", name, age, event);
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

1.1.3. 修饰符

  • v-on支持修饰符,修饰符相当于对事件进行了一些特殊的处理:

    • .stop - 调用 event.stopPropagation()
    • .prevent - 调用 event.preventDefault()
    • .capture - 添加事件侦听器时使用 capture 模式
    • .capture - 添加事件侦听器时使用 capture 模式
    • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
    • .{keyAlias} - 仅当事件是从特定键触发时才触发回调
    • .once - 只触发一次回调
    • .left - 只当点击鼠标左键时触发
    • .right - 只当点击鼠标右键时触发
    • .middle - 只当点击鼠标中键时触发
    • .passive - { passive: true } 模式添加侦听器
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
          .box {
            width: 100px;
            height: 100px;
            background-color: orange;
          }
        </style>
      </head>
      <body>
        <div id="app">
          <div class="box" @click="divClick">
            <button @click.stop="btnClick">按钮</button>
          </div>
          
        </div>
        <script src="../lib/vue.js"></script>
        <script>
          // 1.创建app
          const app = Vue.createApp({
            // data:option api
            data: function () {
              return {
                message:"Hello Vue"
              };
            },
            methods: {
              btnClick(event) {
                // event.stopPropagation();
                console.log("btnClick");
              },
              divClick() {
                console.log("divClick");
              }
            }
          });
    ​
          // 2.挂载app
          app.mount("#app");
        </script>
      </body>
    </html>
    

1.2. 条件渲染

1.2.1. v-if/else/else-if

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <h1 v-if="score > 90">优秀</h1>
      <h1 v-else-if="score > 80">良好</h1>
      <h1 v-else-if="score >= 60">及格</h1>
      <h1 v-else>不及格</h1>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            score: 40,
          };
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

1.2.2. template元素

  • v-if
  • v-for
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <!-- v-if="条件" -->
      <template v-if="Object.keys(info).length">
        <h2>个人信息</h2>
        <ul>
          <li>姓名: {{info.name}}</li>
          <li>年龄: {{info.age}}</li>
        </ul>
      </template>
​
      <!-- v-else -->
      <template v-else>
        <h2>没有输入个人信息</h2>
        <p>请输入个人信息后,再进行展示</p>
      </template>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data() {
          return {
            info: { name: "why", age: 18 },
          };
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

1.2.3. v-show

  • if用法区别:

    • v-show不能和template结合
    • v-else不能结合
  • if的本质区别:

    • v-if为false元素会销毁/不存在
    • v-show为false元素的display none
  • 选择:

    • 切换非常频繁使用v-show
    • 不频繁 v-if
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      img {
        width: 200px;
        height: 200px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div>
        <button @click="toggle">切换</button>
      </div>
​
      <div v-show="isShowCode">
        <img
          src="https://game.gtimg.cn/images/yxzj/web201706/images/comm/floatwindow/wzry_qrcode.jpg"
          alt=""
        />
      </div>
​
       <div v-if="isShowCode">
        <img
          src="https://game.gtimg.cn/images/yxzj/web201706/images/comm/floatwindow/wzry_qrcode.jpg"
          alt=""
        />
      </div>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            isShowCode: true,
          };
        },
        methods: {
          toggle() {
            this.isShowCode = !this.isShowCode;
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

二. 列表渲染

1.1. v-for的基本使用

  • item in 数组
  • (item, index) in 数组
  • (item, index) of 数组
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .item {
        margin-top: 5px;
        background-color: orange;
      }
      .item .title {
        color:red
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1.电影列表进行渲染 -->
      <h2>电影列表</h2>
      <ul>
        <li v-for="movie in movies">{{movie}}</li>
      </ul>
​
      <!-- 2.电影列表同时有索引 -->
      <ul>
        <li v-for="(movie,index) in movies">{{index + 1}} --- {{movie}}</li>
      </ul>
​
      <!-- 3.遍历数组复杂数据 -->
      <h2>商品列表</h2>
      <div class="item" v-for="item in products">
        <h3 class="title">商品:{{item.name}}</h3>
        <span>价格:{{item.price}}</span>
        <span>秒杀:{{item.desc}}</span>
      </div>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            // 1.movies
            movies: ["星际穿越", "少年派", "大话西游", "哆啦A梦"],
            // 2.数组:存放的时对象
            products:[
              {id:110,name:"Mackbook",price:9.9,desc:"9.9秒杀,快来抢购!"},
              {id:111,name:"iPhone",price:8.8,desc:"9.9秒杀,快来抢购!"},
              {id:112,name:"小米电脑",price:9.9,desc:"9.9秒杀,快来抢购!"}
              
            ]
          };
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

1.2. v-for其他的类型

  • 对象

    • (value, key, index) in obj
  • 数字

    • item in 10
  • 可迭代对象(字符串)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <!-- 1.遍历数组 -->
​
      <!-- 2.遍历对象 -->
      <ul>
        <li v-for="(value,key,index) in info">
          {{value}} --- {{key}} --- {{index}}
        </li>
      </ul>
​
      <!-- 3.遍历字符串(iterable) -->
      <ul>
        <li v-for="item in message">{{item}}</li>
      </ul>
​
      <!-- 4.遍历数字 -->
      <ul>
        <li v-for="item in 10">{{item}}</li>
      </ul>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            message: "Hello Vue",
            movies: [],
            info: { name: "why", age: 18, height: 1.88 },
          };
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

1.3. v-for绑定key属性

1.3.1. VNode/虚拟DOM

  • template元素 -> VNode

  • 虚拟DOM作用之一:

    • 跨平台

1.3.2. key的作用

  • 有key的操作:

    • 根据key找到之前的VNode进行复用;
    • 没有VNode可以复用, 再创建新的VNode
  • 没有key的操作:

    • diff算法, 后续VNode复用性就不强

1.3.3. key绑定id

三. Options API

3.1. 计算属性 computed

3.1.1. 复杂数据的处理方式

  • mustache插值语法自己写逻辑

    • 缺点一:模板中存在大量的复杂逻辑,不便于维护(模板中表达式的初衷是用于简单的计算)
    • 缺点二:当有多次一样的逻辑时,存在重复的代码
    • 缺点三:多次使用的时候,很多运算也需要多次执行,没有缓存
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <div id="app">
          <!-- 插值语法表达式直接进行拼接 -->
          <!-- 1.拼接名字 -->
          <h2>{{firstName + " " + lastName}}</h2>
          <h2>{{firstName}} {{lastName}}</h2>
    ​
          <!-- 2.显示分数等级 -->
          <h2>{{ score >= 60 ? "及格":"不及格" }}</h2>
    ​
          <!-- 3.反转单词显示文本 -->
          <h2>{{ message.split(" ").reverse().join(" ") }}</h2>
        </div>
        <script src="../lib/vue.js"></script>
        <script>
          // 1.创建app
          const app = Vue.createApp({
            // data:option api
            data: function () {
              return {
                // 1.姓名
                firstName: "kobe",
                lastName: "bryant",
    ​
                // 2.分数:及格/不及格
                score: 80,
    ​
                // 3.一串文本:对文本中的单词进行反转显示
                message:"my name is why"
              };
            },
          });
    ​
          // 2.挂载app
          app.mount("#app");
        </script>
      </body>
    </html>
  • methods完成逻辑

    • 我们事实上先显示的是一个结果,但是都变成了一种方法的调用
    • 多次使用方法的时候,没有缓存,也需要多次计算
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <div id="app">
          <!-- 插值语法表达式直接进行拼接 -->
          <!-- 1.拼接名字 -->
          <h2>{{getFullname()}}</h2>
          <h2>{{getFullname()}}</h2>
          <h2>{{getFullname()}}</h2>
    ​
          <!-- 2.显示分数等级 -->
          <h2>{{ getScoreLevel() }}</h2>
    ​
          <!-- 3.反转单词显示文本 -->
          <h2>{{ reverseMessage() }}</h2>
        </div>
        <script src="../lib/vue.js"></script>
        <script>
          // 1.创建app
          const app = Vue.createApp({
            // data:option api
            data: function () {
              return {
                // 1.姓名
                firstName: "kobe",
                lastName: "bryant",
    ​
                // 2.分数:及格/不及格
                score: 80,
    ​
                // 3.一串文本:对文本中的单词进行反转显示
                message: "my name is why",
              };
            },
            methods: {
              getFullname() {
                return this.firstName + " " + this.lastName;
              },
              getScoreLevel() {
                return this.score >= 60 ? "及格" : "不及格";
              },
              reverseMessage() {
                return this.message.split(" ").reverse().join(" ");
              },
            },
          });
    ​
          // 2.挂载app
          app.mount("#app");
        </script>
      </body>
    </html>
    

3.1.2. 计算属性用法

  • computed

    • 注意:计算属性看起来像是一个函数,但是我们在使用的时候不需要加()
    • 我们会发现无论是直观上,还是效果上计算属性都是更好的选择
    • 并且计算属性是有缓存的
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <!-- 插值语法表达式直接进行拼接 -->
      <!-- 1.拼接名字 -->
      <h2>{{ fullname }}</h2>
      <h2>{{ fullname }}</h2>
      <h2>{{ fullname }}</h2>
​
      <!-- 2.显示分数等级 -->
      <h2>{{ scoreLevel }}</h2>
​
      <!-- 3.反转单词显示文本 -->
      <h2>{{ reverseMessage }}</h2>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            // 1.姓名
            firstName: "kobe",
            lastName: "bryant",
​
            // 2.分数:及格/不及格
            score: 80,
​
            // 3.一串文本:对文本中的单词进行反转显示
            message: "my name is why",
          };
        },
        computed: {
          fullname() {
            // 1.计算属性默认对应的是一个函数
            return this.firstName + " " + this.lastName;
          },
          scoreLevel() {
            return this.score >= 60 ? "及格" : "不及格";
          },
          reverseMessage() {
            return this.message.split(" ").reverse().join(" ");
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

3.1.3. computed和methods区别

  • computed底层会缓存, 性能更高
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <!-- 1.methods -->
      <h2>{{getFullname()}}</h2>
      <h2>{{getFullname()}}</h2>
      <h2>{{getFullname()}}</h2>
​
      <!-- 2.computed -->
      <h2>{{fullname}}</h2>
      <h2>{{fullname}}</h2>
      <h2>{{fullname}}</h2>
​
      <!-- 3.修改name值 -->
      <button @click="changeLastName()">修改lastname</button>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            firstName: "kobe",
            lastName: "bryant",
          };
        },
        methods: {
          getFullname() {
            console.log("getFullname------");
            return this.firstName + " " + this.lastName;
          },
          changeLastName() {
            this.lastName = "why";
          },
        },
        computed: {
          fullname() {
            console.log("computed fullname-----");
            return this.firstName + " " + this.lastName;
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

3.1.4. computed的完整写法

  • set
  • get
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <h2>{{fullname}}</h2>
​
      <button @click="setFullname">设置fullname</button>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            firstname: "coder",
            lastname: "why",
          };
        },
        computed: {
          // 语法糖的写法
          // fullname() {
          //   return this.firstname + " " + this.lastname
          // },
​
          // 完整的写法
          fullname: {
            get: function () {
              return this.firstname + " " + this.lastname;
            },
            set: function (value) {
              const names = value.split(" ");
              this.firstname = names[0];
              this.lastname = names[1];
            },
          },
        },
        methods: {
          setFullname() {
            this.fullname = "kobe bryant";
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

3.2. 侦听器 watch

3.2.1. 基本侦听watch

  • watch: { message(newValue, oldValue) {} }

  • 注意: 对象类型

    • Proxy对象 -> Vue.toRaw(newValue)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <h2>{{message}}</h2>
      <button @click="changeMessage">修改message</button>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            message: "Hello Vue",
            info: { name: "why", age: 18 },
          };
        },
        methods: {
          changeMessage() {
            this.message = "你好啊,李银河";
            this.info = { name: "kobe" };
          },
        },
        watch: {
          // 1.默认有两个参数:newValue/oldValue
          message(newValue, oldValue) {
            console.log("message数据发生了变化", newValue, oldValue);
          },
          info(newValue, oldValue) {
            // 2.如果是对象类型,那么拿到的是代理对象
            // console.log("info数据发生了变化", newValue, oldValue);
            // console.log(newValue.name, oldValue.name);
​
            // 3.获取原生对象
            // console.log({ ...newValue });
            console.log(Vue.toRaw(newValue));
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

3.2.2. 侦听的选项

  • deep
  • immediate
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <h2>{{info.name}}</h2>
      <button @click="changeInfo">修改info</button>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            info: { name: "why", age: 18 },
          };
        },
        methods: {
          changeInfo() {
            // 1.创建一个新对象,赋值给info
            // this.info = {name:"kobe"}
​
            // 2.直接修改原对象某一个属性
            this.info.name = "kobe";
          },
        },
        watch: {
          // 默认watch监听不会进行深度监听
          // info(newValue, oldValue) {
          //   console.log("侦听到info的改变:", newValue, oldValue);
          // },
          info: {
            handler(newValue, oldValue) {
              console.log("侦听到info的改变:", newValue, oldValue);
              console.log(newValue === oldValue);
            },
            // 监听器选项
            // info进行深度监听
            deep: true,
            // 第一次渲染直接执行一次监听器
            immediate: true,
          },
          "info.name": function (newValue, oldValue) {
            console.log("name发生了改变:", newValue, oldValue);
          },
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>

3.2.3. 其他的写法

  • "info.name"
  • 别的写法
  • created -> this.$watch()
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <h2>{{message}}</h2>
      <button @click="changeMessage">修改message</button>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
      // 1.创建app
      const app = Vue.createApp({
        // data:option api
        data: function () {
          return {
            message: "Hello Vue",
          };
        },
        methods: {
          changeMessage() {
            this.message = "你好啊,李银河";
          },
        },
        // 生命周期回调函数:当前的组件被创建时自动执行
        // 一般在该函数中,会进行网络请求
        created() {
          // ajax/fetch/axios
          console.log("created");
​
          this.$watch(
            "message",
            (newValue, oldValue) => {
              console.log("message数据变化:", newValue, oldValue);
            },
            { deep: true }
          );
        },
      });
​
      // 2.挂载app
      app.mount("#app");
    </script>
  </body>
</html>