美文网首页
Vue 未解决问题的结果

Vue 未解决问题的结果

作者: Guangchao | 来源:发表于2021-09-16 14:28 被阅读0次

    1. 在利用cli3创建项目时,具名插槽的使用无法显示,也没有报错

    在vue.2.6.3 版本以后插槽的使用方法略改,匹配name属性时:

    1. 必须在 template 中标注
    2. 形式 v-slot:xxx
    <slot name="item-icon"></slot>
    <template v-slot:item-icon></template>
    

    2.当我想要将父组件中的图片填充进插槽该怎么做?

    既然仅仅用 img 不能使用插槽,那么我将 template 标签包裹在 img 外,这样就能使图片显示,插槽的功能就能正常使用

    <template v-slot:item-icon>
        <img src = "xxxxx">
    </template>
    

    3.接下来的问题是,我怎么让 template 标签具有 div 标签具有的块级元素的特点?怎么让其分两行显示

     <template v-slot:item-text>分类</template>
    

    解决:

    <template v-slot:item-icon>
        <div>
            首页
       </div>
    </template>
    

    总结:插槽的使用或者说组件的使用可以理解成直接面向对象的编程思想,将布局、排版、样式等统一定好之后,只需要调用某个组件或者某个插槽,将其中的内容直接调换即可,不必考虑其他的因素。
    4.路由跳转中,push 和 replace 都无法使用的情况,报出

    image.png
    这种情况,该怎么解决?
        发现自己定义路由时,配置路由映射的routes 写成了 router,这时修改完之后仍会报错,经过查询CSD 后了解到应该是脚手架版本不同引发的,
    

    在main.js 文件中

    脚手架2 中创建 Vue 实例,引入router的方式如下
    <script>
        new Vue ({
            el:'#app',
          router,
           render:h => h(app)
        })
    </script>
    
    脚手架3 中创建的 Vue 实例的方式如下
    <script>
        //这里用到了生命周期函数,也可以用脚手架2创建vue实例的方法
        creatApp(App).mount('#app')
    </script>
    
    脚手架4 中创建的 Vue 实例,引入router的方式如下
    <script>
        const app = creatApp(App)
        app.use(router)
        app.mount('#app')
    </script>
    

    在index.js 文件中

    在脚手架2 中导入vue-router
    <script>
        //创建路由配置文件
        import Router from 'vue-router'
        import Vue from 'vue'
       Vue.use(Router)
        //脚手架3 的导入方式与2相同
    </script>
    
    脚手架4 中导入vue-router
    <script>
        //导入
        import {creatRouter,createWebHistory} from 'vue-router'
        //加载插件
        const routerHistory = createWebHistory()
        //创建 router
        const router = createrRouter({
            history:routerHistory,
            routes:[{},{}.....]
        })
    </script>
    
    安装cli4
    npm install vue-router@4
    

    5. router 和route 的区别在哪?

    router 为 VurRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,
    
    route 相当于当前正在跳转的路由对象,可以从里面获得name、path、query等
    

    6.路由传参的方式:

    1. 可以手写完整的path

      this.$router.push({path:'/user/${userId}'})
      
    1. 可以用parmas传递

      this.$router.push({name:'user',parmas:{ userId: 123}})
      
    2. 也可以用query传递

      this.$router.push({path:'register',query: { plan: 'private'}})
      

    注意: 如果提供了path ,params 将被忽略,但是query不属于这种情况。如果使用完整路径和query传参,刷新页面时不会造成路由传参的参数丢失

    那么params 和 query的区别有哪些?

    6.什么是依赖?

    从目前自我已知的角度来说就是利用第三方框架,通过百度 了解到一个叫做 :注入依赖

    什么是**依赖注入**?允许一个祖先组件像其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
    
    不用关心dom层级 **这里肯定会有使用依赖时,层级影响使用的情况**,只要在祖先内部就可以一直使用祖先组件提供的 provide   
    
    provide 选项允许我们指定我们想要提供给后代组件的数据或方法
    

    然后在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的属性:

        祖先组件不需要知道哪些后代组件使用它提供的属性
    
        后代组件不需要知道被注入的属性来自哪里
    

    provide / inject 是解决组件之间通信问题的利器,不受层级结构的限制但不是响应式的,但不能随便滥用,通信代表耦合(什么是耦合? 低耦合通俗来讲:写一个组件,在任何复杂的项目或任何复杂的场景下都可以即拿即用,高耦合的意思就是这个项目的组件,开发后只有一个场景好用,其他场景很难用,耦合度越高越说明组件的抽象程度和你对项目业务的理解越烂)

    7.生命周期函数该怎么用?

        目前了解的生命周期钩子函数,分别有beforeCreate、created、beforeMount(组件挂载)、mounted、beforeUpdate(当这个钩子被调用时,组件 DOM 已经更新)、updated、activated(被 keep-alive 缓存的组件激活时调用,**该钩子在服务器端渲染期间不被调用。**)、deactivated
    
        这些生命周期函数在页面加载,组件创建,渲染等各个时刻自己会被调用
    

    8.history 和 hash 模式?

    默认是hash 模式

    9.处理[Vue warn]: Failed to mount component: template or render function not defined. 错误解决方法

    组件中没有添加 <template> 标签!!!!

    10.Es6了解多少?

    const声明对象里的值为什么可以改变?const 声明的值为什么不能改变?、

    1.ES6允许使用“箭头”(=>)定义函数
    var f = v => v;
    

    //等价于

    var f = function(v){
         return v;    
    };
    

    如果箭头函数不需要参数,则定义如下:

    var f = () => 5;
    

    // 等同于

    var f = function () { return 5 };
    

    如果箭头函数需要单个参数或者多个参数,则定义如下:

    var sum = (num1, num2) => num1 + num2;
    

    // 等同于

    var sum = function(num1, num2) {
      return num1 + num2;
    };
    

    如果箭头函数的代码块多于一条语句,则要使用大括号将他们括起来,并使用return语句返回:

    var sum = (num1, num2) => { return num1 + num2; }
      注:箭头函数内部的this不是windows对象,而是函数自身。
    
    2、import,export

    export与export default均可用于导出常量、函数、文件、模块等,你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用,但在一个文件或模块中,export、import可以有多个,export default仅有一个。

    //demo1.js
    export const str = 'hello world'
    
    export function f(a){
        return a+1
    }
    
    
    
    //demo2.js
    import { str, f } from 'demo1' //也可以分开写两次,导入的时候带花括号
      也可以使用下面的方式进行导入:
    
    //demo1.js
    export default const str = 'hello world'
    
    
    //demo2.js
    import str from 'demo1' //导入的时候没有花括号
    
    3、let 和 const 命令

    这里有必要再提,var 修饰的变量
    var 修饰的变量的作用域是存在当前函数的作用域内,如果声明在函数外的顶层声明,那么就属于 全局变量 、如果当前变量并没有用任何修饰词,那么该变量则属于全局变量。
    let 作用于当前代码块的区域
    let 声明的变量具有一下几个特点

    1. let 声明的变量具有块级作用域的特征
    2. 在同一个块级作用域,不能重复声明变量
    3. let 声明的变量不存在变量提升
      这里加一个例子
            for(var i = 0; i <10 ; i++){
               setTimeout(function(){
                   console.log(i);
               },100)
           }
           for(let i = 0; i < 10; i++) {
               setTimeout(function(){
                   console.log(i);
               },100)
           }
           for(const i = 0; i < 10; i++) {
               setTimeout(function(){
                   console.log(i);
               },100)
           }
    
    

    const只读

    在vue中使用const会定义 变量、方法、对象等,通常是使用const定义一个变量指向方法、对象等
    定义变量为一个具体的值时,该值是无法改变的,当定义的变量指向的是对象或方法时,其内容值可以改变

    11.变量和方法的增强写法

    原来的写法

    coconst obj={
        name:'haha',
        age:12,
        run:function () {
            console.log('hahah');
        },
        jump:function () {
            console.log('hehehe');
        }
    }
    const name="hahah";
    const age=12;
    //es5的写法
    const obj={
        name:name,
        age:age
        }
    }
    

    属性的增强写法

    //es6的写法
    const name = "haha";
    const age = 12;
    const obj = {
        name,
        age
    }
    

    函数的增强写法

    const obj = {
        run(){
            console.log("hehehe");
        },
        jump(){
            console.log("he")
        }
    }
    

    12.Mvvm框架(Model-View-ViewModel)?

    前端的视图层分为三部分: Model、View、ViewModel

    1. 『View』:视图层(UI 用户界面)
    2. 『ViewModel』:业务逻辑层(一切 js 可视为业务逻辑)\
    3. 『Model』:数据层(存储数据及对数据的处理如增删改查)

    ViewModel是桥梁,是连接 view 视图层和数据层的纽带,这层纽带的体现体现在双向数据绑定

    mvvm避免了于DOM 的直接接触

    Mvc是什么?

    MVC 即 model ,view , control ,jQuery 就是采用这种设计模式

    13.生命周期

    从创建到销毁的过程

    在此过程中有一些声明周期钩子供我们使用,以便我们在使用组件化时的特定时期来做特定的事

    简单理解有四大过程: 分别是创建、挂载、更新、销毁

    14.mustatch语法 里能写什么?

    双阔号里的属性值或方法名

    15.v-once

    只执行一次

    16.v-html

    将dom元素包括标签等都可以显示出来

    17.v-text

    只显示dom的文本

    18.v-clock

    19.动态绑定 v-bind

    语法糖 写法 :“ :xxx”

    20.计算属性 computer与 方法methods的区别

    两者的本质区别是; computed 是基于响应式依赖进行缓存的,计算属性的触发条件是它的依赖变化了才会重新执行。

    而methods 就像普通函数,需要主动调用才能执行。而不是依赖数据的变换,并且也不需要返回一个结果,可以仅仅执行一个过程。当我们在获取一个数据时需要对一个大的数组进行大量循环擦能获取时,那我们选择计算属性,基于依赖进行缓存将会节省大量的性能消耗。而不是像methods一样每次调用都执行。

    21.v-on ,v-bind 语法糖?修饰符,用法

    @、:、

    22.阻止默认事件prevent

    23.v-if 和 v-show

    相同点是通过真假值动态显示元素,不同点是v-if的值为false 时直接将元素从DOM 树中移除,而v-show是隐藏元素

    <body>
            <div id="app">
                <input type="button" value="切换显示" @click="changeIsShow" />
                <p v-show="isShow">不装了,我摊牌了,没错你要找的就是我</p>
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <script>
                var  app = new Vue({
                    el:"#app",
                    data:{
                        isShow:false
                    },
                    methods:{
                        changeIsShow(){
                            this.isShow = !this.isShow
                        }
                        
                    }
                })
            </script>
        </body>
    
    <body>
            <div id="app">
                <input type="button" value="点我切换显示" @click="changeIsShow" />
                <p v-if="isShow">不装了,我摊牌了,没错你要找的就是我</p>
            </div>
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
            <script>
                var  app = new Vue({
                    el:"#app",
                    data:{
                        isShow:false
                    },
                    methods:{
                        changeIsShow(){
                            this.isShow = !this.isShow
                        }
                    }
                })
            </script>
        </body>
    

    24.v-for key值 遍历

    在列表渲染时使用 key 属性

    官方文档:

    当Vue.js 用v-for 正在更新已渲染过的元素列表时,它默认用“就地复用''策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素

    假设Vue 实例中的 data 数据中有个变量num存了一个数组类型数据,其中包含【1,2,3,4,5】这时向数组中添加数字 将num 改为 【0,1,2,3,4,5】,这时没有key值,渲染更新的步骤是:

    原先的div内容为1的变为0,2变为1 依次类推,相当于数组中增加了一个 值5

    <div v-for = "item in num">
        {{item }}
    </div>
    
    <div v-for = "(item,index) in num" :key = "index">
        {{item}}
    </div>
    

    这里用 index 变量,根据列表渲染的规则,它实际上对应了数组中每个元素的索引,这样的好处是它可以使每个元素的 key 值都不同,要利用 key 属性的有点,保证统一父元素的子元素有不同的 key 属性

    此时数组 num 从 【1,2,3,4,5】 变成 【0,1,2,3,4,5】渲染输出的更新步骤就变化了,新增一个div元素它的内容为0 ,并将它插入原先内容为1 的元素之前。

    25.v-module

    动态数据绑定

    26.filter()函数 过滤器

    初步了解在对某一对象使用回调函数时对该对象内的数据进行过滤

    const cartList = this.$store.getters.cartList;
            return cartList
            .filter((item) => {
              return item.checked;
            })
    

    27.了解vue的高阶函数

    filter、map、 reduce

    1、filter 函数,返回布尔值

    返回值是true ,则返回当前的值;如果返回值是false ,则不返回当前值。循环判断数据里面的数据直至完成;

    2、map 函数,对数组中的值进行处理后,返回相应的值。循环判断数据里的值,直至完成;

    map(function(n){
        return n*3
    })
    

    3、reduce 函数,合计,计算总值

    reduce(function(preValue,n){
        return preValue + n
    })
    

    28.创建组件的步骤

    创建——注册——使用

    const component = {
        template: "#xxx",
    }
    const app = new Vue({
        components: {
            compone
        }
    })
    

    29.父传子子传父

    父组件向子组件传值,利用props属性

    父组件传给子组件数值,子组件不要对其进行修改,而是需要找个变量将父组件传递的数值给赋值过来 然后对变量进行操作

    <body>
        <p>
            {{cart}}
        </p>
    </body>
    <script>
    const app = new Vue({
        components: {
          cart
        }
        data: {
            list:[1,3,4,5,6,4]       
         }
    })
    const cart = {
        template:"#cart",
        props:{
            cart:{
              type:Array,
                default(){
                    return []
                }  
             }
        }
    }
    </script>
    

    子组件向父组件传值 利用 $.emit发送事件

    <template>
        <div @click = checkBtn>
        </div>
    </template>
    <script>
        export default{
         template: "#children",
         methods: {
           checkBtn(){
               this.$emit('checkBtn')
           }
         }
        }
        
    </script>
    
    <template>
        <button @btnClick = checkBtn>
            
        </button>
    </template>
    <script>
    export default{
      components: {
         children
        },       
     },
      methods: {
        btnClick(){
          console.log(1111)
        }
      }
    </script>
    

    31.兄弟传

    方法一:
    1. 兄弟组件之间传值要借助事件车,通过事件车的方式传递数据
    2. 中央事件总线(通俗讲是:eventBus 事件车)的实质就是创建一个vue 实例,通过一个空的vue实例作为桥梁实现vue组件之间的通信,它是实现非父子组件通信的一种解决方案
    3. 作用: 实现非父子组件之间的数据出啊目的
    4. 传递数据方,通过一个事件触发 bus.$emit (方法名,传递的数据)
    5. 接收数据方
    接收数据方,通过
    mounted(){
        bus.$on(方法名,function(接收数据的参数))
        //用该组件的数据接收传递过来的数据
        //此时函数中的this已经发生了改变,可以使用箭头函数
    }
    
    方法二:

    vuex

    方法三:

    子——》父——》子

    32.插槽

    插槽放在子组件中,由父组件来填充内容(方便修改、

    33.页面间传递参数 vuex

    34.路由传参

    router 导航,分为两大类:

    编程式导航router.push、声明式导航<router-link>

    1.**字符串**
    
    1. 直接通过字符串的方式将路由地址以字符串的方式来跳转,这种方式简单,但是不能传递参数
    
    this.$router.push('home')
    
    2.**对象**    
    
    想要传递参数主要就是以对象的方式来写,分为两种方式: 命名路由和查询参数
    

    命名路由

    命名路由的前提就是在注册路由的地方需要给路由命名:

    如:

    export default new Router({
       routes: [
            {
                path:'/',
                name:'hello',//命名路由传递参数需要使用params来传递
                component: Hello
            }
        ] 
    })
    //使用方法如下 ,这个可以直接添加跳转事件,跳转新的页面
    this.$router.push({name:'hello',params:{ userId:123 }})
    
    //接收传递的参数如下
    <div>
        {{this.$router.params.userId}}
    </div>
    

    查询参数

    查询参数其实就是在路由地址后面带上参数和传统的url 是一致的,传递参数使用 query 而且必须配和 path 来传递参数而不能使用 name,目标页面接收传递的参数使用query

    注意:和name配对的是params 和path 配对的是query

    //使用方法如下 ,可以直接添加事件进行页面跳转
    this.$router.push({ path: '/news', query: { userId: 123 }});
    //接收参数如下:
    <div>
        {{this.$router.query.uesrId}}
    </div>
    

    36.vuex

    相当于一个公共仓库,保存着所有组件都能共用的数据

    //在store文件中新建 index.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex);
    
    export default new Vuex.Stroe({
        state:{
            pathName:'',
            currDbSource: {},
            currJobDate:{},
            DbSource:[]
        },
        mutation: {
             // 保存当前菜单栏的路径
            savePath(state,pathName){
                state.pathName = pathName;
            },
            // 保存当前点击的数据源
            saveCurrDbSource(state,currDbSource){
                state.currDbSource = currDbSource;
            },
            // 保存当前点击的元数据
            saveCurrJobData(state,currJobData){
                state.currJobData = currJobData;
            },
            // 保存所有数据源
            saveDbSource(state,DbSource){
                state.DbSource = DbSource;
            }
        }
    })
    

    这里解释,state 是自定义的一些变量,用来保存数据,mutations 是用来触发事件,相当于方法,用户需要通过触发这个方法来借此保存数据。第二个参数是用户需要传入的值。

    37.侦听属性

    38.export default 中的 name 属性到底有啥用呢?

    类型 : string
    
    详情:

    允许组件模板递归的调用自身。组件在全局用 app.component 注册时,全局ID自动为组件的name

    指定 name 选项的另一个好处是便于调试。有名字的组件有更友好的警告信息。另外,在有 vue-devtools ,未命名的组件将显示成<AnonymousComponent>

    这很没有语义。通过提供name选项可以获得更有语义信息的组件树

    39.data: function () 和 data()

    es6语法

    40.什么是组件缓存?

    41.为什么需要封装组件?

    1. 为了组件的复用
    2. 为了代码的可维护性和可读性
    3. 怎么实现组件的封装呢?主要是抽离思想和并联动态变化的思想

    42.怎么封装一个轮播组件?

    1. 图片的话请求网络数据,最后将图片数据加载进框架里面
    2. 还是先搭建框架,外框和放置图片框架
    3. 其次是方法:第一个是定时器自动播放的方法、第二个是拖动图片滚动的方法
    4. 最后是加载,怎么加载这些方法,需要用到生命周期函数

    43.export default function 和 export function 的区别

    export default function xxx (){
    //..输出
    }
    import xxx from 'xxx';//输入
    
    export function bbb (){
    //..输出
    }
    import {bbb} from 'bbb';//输入
    

    export default 命令用于指定模块的默认输出,显然,一个模块只能有一个默认输出,因此export default 命令只能使用一次。所以 ,import 命令后才不用大括号

    44.ref 和 children

    ref 如果是绑定在组件中的,那么 通过 this.$refs.refname获得的是一个组件对象

    ref 如果是绑定在普通的元素中,那么通过 this.$refs.refname获得的是一个元素对象

    45.style 中的 scoped 属性有什么作用?

    规定css 样式的作用域

    46.watch??

    47.数组常用的方法有哪些?

    48.Vue中的ref 和$refs 的介绍和使用

    在JavaScript 中需要通过 document.querySelector('#demo')来获取dom 节点,然后再获取这个节点的值。在Vue中,我们不用获取dom节点,元素绑定ref后,直接通过 this.$refs 即可调用,这样可以减少获取dom节点的消耗。

    ref介绍:

    ref 被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上,如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子组件上,引用就指向子组件实例

    简单说,ref 特性就是为元素或子组件赋予一个 Id 引用 ,通过 this.$refs.refName 来访问元素或子组件实例

    <p ref = "p">    Hello</p><children ref = "children"></children>
    
    this.$refs.p
    this.$refs.children
    
    this.$refs 介绍

    this.$refs 是一个对象,持有当前组件中注册过 ref 特性的所有 DOM 元素和子组件实例

    注意:$refs 只有在组件渲染完成后才填充,在初始渲染的时候不能访问,并且他们是非响应式的,因此不能在模板中做数据绑定

    49.拦截器

    什么是axios 拦截器?

    拦截器就是拦截每一次的请求和响应,然后进行相应的处理。

    拦截器按照状态不同可以分为 请求拦截器和响应拦截器

    50.computed 和 methods 的区别

    51.网络四种请求方法

    axios请求方法:get,post,put,patch,delete

    get:获取数据
    post:提交数据(表单提交+文件上传)
    put:更新数据(所有数据推送到后端)
    patch:更新数据(只将修改的数据推送到后端)
    delete:删除数据

    52. routerview,routerlink

    routerview 命令视图 routerlink 命令路线 本身就是两个组件。

    routerview 显示组件, routerlink 路由跳转

    <router-link :to ="/url/">
        <li><a>跳转路由</a></li>
    </router-link>
    

    53.axios 使用小结

    axios 在vue框架中用于向服务器请求数据,之前了解过一点jQuery 的AJAX 。vue框架的axios 基于Promise ,可以使用 Promise API

    作品中用到的axios的请求方式,

    axios.request( config )

    另外有其他几种方式,用到再说

    1. axios.request(config)
    2. axios.get(url [,config])
    3. axios.post(url[data [,config]])
    4. axios.put(url [data [,config]])
    5. axios.delete(url [,config])
    6. axios.patch(url [data [,config]])
    7. axios.head(url [,config])
    8. axios(config)

    默认的网络请求方法是get,如果需要发送多个请求,并发请求的话需要用到 all 方法。这里没有用到

    import axios from 'axios'
    export function request(config){
        const instance = axios.create({ //创建axios实例的目的是为了方便使用全局配置,另一方面减少各个组件对axios框架的依赖性
            baseURL: "http://xxxxxxx",
            timeout:xxxx
        })
        //添加axios拦截器
        instance.interceptors.request.use(config => { //添加请求拦截器,检测在请求网络数据时的状态
            return config
        },err=> {
            console.log(err)
        })
        instance.interceptors.response.use(res => { //添加响应拦截器,页面在获取数据后进行检测状态
            return res.data //成功则返回结果
        },err => {
            console.log(err);
        })
        return instance(config) //发送网络请求
    }
    

    这里 config 配置选项有必要了解

    {
        //服务器的地址,是必须的选项
        url:'/user'
        
        //请求的方式,默认是get
        method:'get'
        
        //如果url不是绝对地址,则会加上 baseURL
        baseURL:'http://localhost:3000'
        
        //headers是自定义要被发送的信息头
        headers:{'X-Requested-with':'XMLHttpRequest'},
            
        //params 是请求连接中的请求参数,必须是个纯对象
        params: { ID:123 }
        
        //timeout 定义请求的事件,单位是毫秒,如果请求事件超过设定时间,请求停止
        timeout:1000
        
        其他的用到再了解
    }
    

    创建完网络请求的axios实例 instance 后,各个组件需要请求各自的数据,这里不将所有组件请求的方法放在同一个页面中,将其分类

    例如 home 首页

    import { request } from './request'
    
    export function getHomeMultidata(){
        return request({
            url:'/home/multidata'//将配置信息返回至request网络请求方法中请求数据
        })
    }//接下来在组件中引用该方法,获取存储和渲染数据
    

    54.使用vue的优点

    vue 的两大特点: 响应式编程,组件化

    vue 的优势: 轻量级框架,双向数据绑定,组件化,视图化,数据和结构的分离,虚拟DOM,运行速度块,可以依赖第三方UI库,节省开发时间

    组件化开发易于复用、不易污染

    55.模块化开发的含义

    vue中的 模块化开发 主要是以 ES6 的模块化开发进行设计

    export 导出, import 导入

    相关文章

      网友评论

          本文标题:Vue 未解决问题的结果

          本文链接:https://www.haomeiwen.com/subject/ylvrgltx.html