当前位置: 首页 > news >正文

vue:组件的使用

Vue:组件的使用

1、什么是组件

1.1、传统方式开发的应用

一个网页通常包括三部分:结构(HTML)、样式(CSS)、交互(JavaScript)。在传统开发模式下,随着项目规模的增大,代码结构会变得愈发复杂。例如,多个页面可能共享部分样式和交互逻辑,但代码却分散在不同文件中,维护和修改时需要在多个地方查找相关代码,不仅效率低下,还容易引发错误。
在这里插入图片描述

1.2、组件化方式开发的应用

使用组件化方式开发解决了以上的两个问题:
① 每一个组件都有独立的js,独立的css,这些独立的js和css只供当前组件使用,不存在纵横交错。更加便于维护。以一个电商项目为例,商品展示组件有自己独立的JavaScript代码来处理商品数据展示逻辑,CSS样式只作用于该组件内的元素,与其他组件如购物车组件、用户登录组件等相互独立,当需要修改商品展示样式或逻辑时,只需要专注于该组件内部代码,不会影响到其他组件。
② 代码复用性增强。组件不仅让js、css复用了,HTML代码片段也复用了(因为要使用组件直接引入组件即可)。在项目中,像导航栏、按钮等组件,在多个页面或不同位置可能会重复使用。通过组件化,只需要编写一次这些组件代码,然后在需要的地方引入,大大提高了开发效率。
在这里插入图片描述

1.3、什么是组件?

① 组件:实现应用中局部功能的代码和资源的集合。凡是采用组件方式开发的应用都可以称为组件化应用。在Vue中,一个简单的按钮组件可能包含按钮的HTML结构、样式以及点击事件处理的JavaScript代码,这些代码和资源组合在一起,实现了按钮的功能,并且可以在不同地方复用。
② 模块:一个大的js文件按照模块化拆分规则进行拆分,生成多个js文件,每一个js文件叫做模块。凡是采用模块方式开发的应用都可以称为模块化应用。例如,在一个大型JavaScript项目中,将数据请求功能、数据处理功能等分别拆分成不同的模块,每个模块专注于特定功能,提高代码的可维护性和可复用性。
③ 任何一个组件中都可以包含这些资源:HTML、CSS、JS、图片、声音、视频等。从这个角度也可以说明组件是可以包括模块的。比如一个视频播放组件,除了HTML结构和CSS样式来定义播放界面,JavaScript代码来控制播放逻辑外,还可能包含视频资源文件以及用于播放控制的图片图标等。

1.4、组件的划分粒度很重要

粒度太粗会影响复用性。为了让复用性更强,Vue的组件也支持父子组件嵌套使用。例如,在一个复杂的页面布局中,将页面划分为头部、主体、底部等大粒度组件后,主体部分又可以进一步细分为文章列表组件、推荐组件等子组件。子组件由父组件来管理,父组件由父组件的父组件管理。在Vue中根组件就是vm。因此每一个组件也是一个Vue实例。这意味着每个组件都有自己独立的生命周期、数据和方法,它们之间通过特定的通信机制进行交互,构建出复杂而有序的应用结构。
在这里插入图片描述

2、组件的创建,注册,使用

2.1、组件的创建、注册、局部使用

在这里插入图片描述

第一步:创建组件
Vue.extend({该配置项和new Vue的配置项几乎相同,略有差别})
区别有哪些?

  1. 创建Vue组件的时候,配置项中不能使用el配置项。因为组件具有通用性,不特定为某个容器服务,它为所有容器服务。如果组件中指定了el,就限制了其只能在特定的DOM元素下使用,无法在其他地方复用。
  2. 配置项中的data不能使用直接对象的形式,必须使用function。以保证数据之间不会相互影响。例如,在多个相同组件实例存在的情况下,若data为对象,一个实例修改了数据,其他实例也会受到影响;而使用函数形式,每个实例都会有自己独立的数据副本。
    3、使用template配置项来配置模板语句:HTML结构。通过template可以清晰地定义组件的HTML结构,使其与JavaScript和CSS分离,增强代码的可读性和维护性。

第二步:注册组件
局部注册:
在配置项当中使用components,语法格式:
components : {
组件的名字 : 组件对象
}
全局注册:
Vue.component(‘组件的名字’, 组件对象)。全局注册的组件可以在任何Vue实例的模板中使用,无需在每个组件内部再次注册。但在大型项目中,过多的全局注册组件可能会导致命名冲突和代码难以维护,因此局部注册在很多场景下更具优势。

第三步:使用组件
1、直接在页面中写<组件的名字></组件的名字>
2、也可以使用单标签<组件的名字 />。这种方式一般在脚手架中使用,否则会有元素不渲染的问题。在非脚手架环境中,某些浏览器可能不支持单标签自闭合语法,导致组件无法正确渲染,所以建议在非脚手架环境中使用双标签形式。
补充:组件的使用分为三步:
第一步:创建组件
Vue.extend({该配置项和new Vue的配置项几乎相同,略有差别})。
第二步:注册组件
局部注册:
在配置项当中使用components,语法格式:
components : {
组件的名字 : 组件对象
}
全局注册:
Vue.component(‘组件的名字’, 组件对象)
第三步:使用组件

<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <!-- 3. 使用组件 -->
        <userlist></userlist>
    </div>

    <script>
        // 1.创建组件(结构HTML 交互JS 样式CSS)
        const myComponent = Vue.extend({
            template: `
                <ul>
                    <li v-for="(user,index) of users" :key="user.id">
                        {{index}},{{user.name}}
                    </li>
                </ul>
                `,
            data() {
                return {
                    users: [
                        { id: "001", name: "jack" },
                        { id: "002", name: "lucy" },
                        { id: "003", name: "james" },
                    ],
                };
            },
        });

        // Vue实例
        const vm = new Vue({
            el: "#app",
            data: {
                msg: "第一个组件",
            },
            // 2. 注册组件(局部注册)
            components: {
                // userlist是组件的名字。myComponent只是一个变量名。
                userlist: myComponent,
            },
        });
    </script>
</body>

2.2、为什么组件中data数据要使用函数形式

面试题:为什么组件中data数据要使用函数形式
在 Vue 组件中 data 使用函数形式,原因有三:
一是保证组件复用性,若 data 为对象,复用实例会共享数据,修改一处影响其他实例;函数形式能让各实例有独立 data 副本,数据互不干扰。比如在一个商品列表页面,每个商品项都是一个组件实例,如果data为对象,当修改一个商品的价格时,其他商品的价格也会跟着改变,这显然不符合预期;而使用函数形式,每个商品组件实例都有自己独立的价格数据。
二是实现数据隔离与安全,降低数据冲突和意外修改风险,提升代码可维护性。不同组件实例的数据相互独立,避免了因一个实例的数据修改影响其他实例,使得代码在维护和扩展时更加稳定可靠。
三是契合 Vue 设计理念,使组件更独立、遵循单一职责原则。每个组件专注于自己的功能和数据,提高了代码的模块化程度。

<script>
    // 数据只有一份,数据会互相影响
    let dataobj = {
        counter: 1,
    };
    let a = dataobj;
    let b = dataobj;

    function datafun() {
        return {
            counter: 1,
        };
    }
    // 只要运行一次函数,就会创建一个全新的数据,互不影响
    let x = datafun();
    let y = datafun();
</script>

2.3、创建组件对象的简写方式

在这里插入图片描述

创建组件对象也有简写形式:Vue.extend() 可以省略。直接写:{}。Vue在内部会自动处理,当使用这种简写方式注册组件时,它会将对象作为参数传递给Vue.extend() 进行组件创建。这种简写方式使代码更加简洁,提高了开发效率。

<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <!-- 3. 使用组件 -->
        <userlogin></userlogin>
    </div>

    <script>
        // 1. 创建组件
        /* const userLoginComponent = Vue.extend({
            template : `
            <div>
                <h3>用户登录</h3>
                <form @submit.prevent="login">
                    账号:<input type="text" v-model="username"> <br><br>
                    密码:<input type="password" v-model="password"> <br><br>
                    <button>登录</button>
                </form>
            </div>
            `,
            data(){
                return {
                    username : '',
                    password : ''
                }
            },
            methods: {
                login(){
                    alert(this.username + "," + this.password)
                }
            },
        }) */
        // 底层会在局部或全局注册组件时,自动调用Vue.extend()
        const userLoginComponent = {
            template: `
                <div>
                    <h3>用户登录</h3>
                    <form @submit.prevent="login">
                        账号:<input type="text" v-model="username"> <br><br>
                        密码:<input type="password" v-model="password"> <br><br>
                        <button>登录</button>
                    </form>
                </div>
                `,
            data() {
                return {
                    username: "",
                    password: "",
                };
            },
            methods: {
                login() {
                    alert(this.username + "," + this.password);
                }
            },
        };

        // Vue实例
        const vm = new Vue({
            el: "#app",
            data: {
                msg: "第二个用户登录组件",
            },
            // 2. 注册组件(局部注册)
            components: {
                userlogin: userLoginComponent,
            },
        });
    </script>
</body>

2.4、组件的全局注册

在这里插入图片描述

<body>
    <!-- 
        组件的使用分为三步:
            第一步:创建组件
                Vue.extend({该配置项和new Vue的配置项几乎相同,略有差别})。
            第二步:注册组件
                局部注册:
                    在配置项当中使用components,语法格式:
                        components : {
                            组件的名字 : 组件对象
                        }
                全局注册:
                    Vue.component('组件的名字', 组件对象)
            第三步:使用组件 
    -->
    <div id="app">
        <h1>{{msg}}</h1>
        <!-- 3. 使用组件 -->
        <userlogin></userlogin>
    </div>
    <hr />

    <div id="app2">
        <userlogin></userlogin>
    </div>

    <script>
        const userLoginComponent = {
            template: `
                <div>
                    <h3>用户登录</h3>
                    <form @submit.prevent="login">
                        账号:<input type="text" v-model="username"> <br><br>
                        密码:<input type="password" v-model="password"> <br><br>
                        <button>登录</button>
                    </form>
                </div>
                `,
            data() {
                return {
                    username: "",
                    password: "",
                };
            },
            methods: {
                login() {
                    alert(this.username + "," + this.password);
                }
            },
        };

        // 全局注册
        Vue.component("userlogin", userLoginComponent);

        // 第2个vue实例
        const vm2 = new Vue({
            el: "#app2",
        });

        // Vue实例
        const vm = new Vue({
            el: "#app",
            data: {
                msg: "全局注册组件",
            },
            // 注册组件(局部注册)
            // components: {
            //   userlogin : userLoginComponent
            // },
        });
    </script>
</body>

全局注册的组件在整个应用中都可使用,方便在不同的Vue实例中复用。但在大型项目中,要注意组件命名的唯一性,避免不同模块中同名组件冲突。例如,在一个包含多个功能模块的项目中,若两个模块都定义了名为“button”的组件并全局注册,就会导致命名冲突,使应用出现不可预期的错误。

2.5、组件的命名细节

注册组件细节:

  1. 在Vue当中是可以使用自闭合标签的,如果组件需要多次使用,前提必须在脚手架环境中使用。在非脚手架环境下,一些浏览器可能不识别自闭合标签,导致组件渲染异常。例如在IE浏览器的某些版本中,使用自闭合标签可能会使组件无法正常显示内容。
  2. 在创建组件的时候Vue.extend()可以省略,但是底层实际上还是会调用的,在注册组件的时候会调用。这一特性使得代码书写更加简洁,开发者无需每次都显式调用Vue.extend(),提高了开发效率。
  3. 组件的名字
    (1):全部小写。这种命名方式简单直观,在HTML模板中使用时符合HTML标签的命名习惯,例如<my - component>
    (2):首字母大写,后面都是小写。如<MyComponent>,在一些代码风格规范中常用于区分组件和普通HTML标签。
    (3):kebab - case命名法(串式命名法。例如:user - login)。这是一种常用的命名方式,在HTML模板中可读性强,易于理解组件的功能。
    (4):CamelCase命名法(驼峰式命名法。例如:UserLogin)。但是这种方式只允许在脚手架环境中使用。在脚手架项目中,使用驼峰式命名法可以使组件名在JavaScript代码中更符合编程习惯,同时在模板中通过特定配置也能正确识别。
    (5)不要使用HTML内置的标签名作为组件的名字。例如:header,main,footer。使用HTML内置标签名作为组件名可能会导致混淆和冲突,使浏览器无法正确解析和渲染组件。
    (6)在创建组件的时候,通过配置项配置一个name,这个name不是组件的名字,是设置Vue开发者工具中显示的组件的名字。例如,在调试复杂应用时,通过设置name可以更方便地在开发者工具中识别和查找组件,提高调试效率。
<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <!-- 3. 使用组件 -->
        <hello - world></hello - world>
        <hello - world />
        <!-- 使用多个的时候,会报错 -->
        <!-- <hello - world />
        <hello - world /> -->
    </div>

    <script>
        // 1、创建组件
        // const hello = {
        //   template: `<h1> helloworld </h1>`,
        // };
        // 2、全局注册组件
        // Vue.component("hello - world", hello);

        // 注册的时候,同时创建组件
        Vue.component("hello - world", {
            name: "hw",
            template: `<h1> HelloWorld </h1>`,
        });

        // Vue实例
        const vm = new Vue({
            el: "#app",
            data: {
                msg: "组件注册注意点",
            },
        });
    </script>
</body>

3、组件的嵌套

在这里插入图片描述

哪里要使用,就到哪里去注册,去使用。组件嵌套是构建复杂页面结构的重要方式。通过合理的组件嵌套,可以将一个大型页面拆分成多个层次分明、功能独立的组件。例如,在一个电商商品详情页面中,可能会有商品基本信息组件、商品图片展示组件、商品评论组件等,这些组件又可能各自包含子组件,如商品图片展示组件中可能包含图片切换子组件、图片预览子组件等。

在父组件中注册并使用子组件时,要注意组件的作用域和通信问题。父组件可以通过props向子组件传递数据,子组件可以通过 $ emit触发事件向父组件传递信息。比如在一个包含商品列表组件(父组件)和单个商品展示组件(子组件)的场景中,父组件可以将商品数据列表通过props传递给子组件,子组件在用户点击商品详情按钮时,通过$emit触发一个自定义事件,通知父组件进行相应的操作,如跳转到商品详情页。

在实际项目开发中,合理规划组件嵌套的层级非常关键。过深的嵌套层级可能会导致组件间通信变得复杂,增加维护成本。一般建议将嵌套层级控制在3 - 4层以内,若超过这个范围,可以考虑通过状态管理工具(如Vuex)来简化组件间的数据传递和共享。

当父组件更新时,会触发子组件的更新生命周期钩子函数。子组件可以在beforeUpdateupdated钩子函数中,根据父组件传递过来的新数据进行相应的操作,比如重新计算某些依赖数据、更新DOM元素等。

在组件嵌套的场景下,调试也需要一些技巧。当发现页面显示异常或功能错误时,可以利用Vue开发者工具,通过组件树来快速定位到可能出现问题的组件层级。可以查看每个组件的props数据、data状态以及事件触发情况,从而更高效地排查问题。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF - 8" />
    <title>组件嵌套</title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <app></app>
    </div>
    <script>
        //4创建child组件
        const child = {
            template: `
         <h3>child组件</h3>
        `,
        };

        //3创建girl组件
        const girl = {
            template: `
         <h2>girl组件</h2>
        `,
        };

        //2 创建son组件
        const son = {
            template: `
       <div>
          <h2>son组件</h2>
           <child />
        </div>
        `,
        components: {
            child,
        },
        mounted() {
            // 可以在这里访问子组件实例
            const childInstance = this.$children[0];
            console.log('子组件child实例:', childInstance);
        }
      };

      //1、创建app组件,并注册son组件和girl组件
      const app = {
          template: `
         <div>
           <h1>app组件</h1>
           <girl />
           <son />
          </div>
        `,
        components: {
            girl,
            son,
        },
        updated() {
            console.log('app组件更新了');
        }
      };
      // 创建vm,并注册app组件
      const vm = new Vue({
          el: "#root",
          // 1.3 使用app组件
          //1.2、 注册app组件
          components: {
              app,
          },
          data() {
              return {
                  // 可以在这里定义一些共享数据,通过props传递给子组件
                  sharedData: '这是来自根组件的数据'
              };
          }
      });
    </script>
</body>

在上述代码中,app组件作为最外层组件,嵌套了girlson组件,而son组件又嵌套了child组件。通过在组件的生命周期钩子函数中添加日志输出,可以清晰地看到组件的加载和更新顺序。在son组件的mounted钩子函数中,可以通过this.$children来访问子组件实例,实现父子组件间更直接的交互。同时,app组件中的updated钩子函数可以在组件数据更新时触发,用于执行一些与更新相关的逻辑。

4、VueComponent & Vue

4.1、this

new Vue({})配置项中的this就是:Vue实例(vm)。在这个实例中,可以访问data中的数据、调用methods中的方法,并且其生命周期钩子函数也围绕着整个Vue应用的创建、挂载、更新和销毁过程执行。例如,在mounted钩子函数中,可以操作DOM元素,因为此时Vue实例已经挂载到了页面上。
Vue.extend({})配置项中的this就是:VueComponent实例(vc)。每个通过Vue.extend创建的组件都是一个VueComponent实例。它也有自己的datamethods和生命周期钩子函数,但与Vue实例不同的是,它主要用于构建可复用的组件模块。比如在一个组件的created钩子函数中,可以进行一些组件特定的数据初始化操作,而不会影响到其他组件。
打开vm和vc你会发现,它们拥有大量相同的属性。例如:生命周期钩子、methods、watch等。这是因为VueComponent的设计目的就是为了复用Vue的基本功能,同时又能保持组件的独立性和可定制性。例如,无论是Vue实例还是VueComponent实例,都可以通过watch来监听数据的变化,并在数据变化时执行相应的逻辑。

<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <user></user>
    </div>
    <script>
        // 创建组件
        const user = Vue.extend({
            template: `
            <div>
                <h1>user组件</h1>
            </div>
            `,
            mounted() {
                // user是什么呢????是一个全新的构造函数 VueComponent构造函数。
                // this是VueComponent实例
                console.log('vc', this)
            },
        });

        // vm
        const vm = new Vue({
            el: "#app",
            data: {
                msg: "vm与vc",
            },
            components: {
                user,
            },
            mounted() {
                // this是Vue实例
                console.log("vm", this);
            },
        });
    </script>
</body>

在上述代码中,在user组件(VueComponent实例)的mounted钩子函数中输出this,可以看到其指向的是组件自身的实例,包含了组件特有的属性和方法。而在Vue实例的mounted钩子函数中输出this,则指向整个Vue应用的实例,包含了应用级别的数据和配置。

4.2 vm === vc ???

只能说差不多一样,不是完全相等。
例如:
vm上有el,vc上没有。因为el用于指定Vue实例挂载的DOM元素,而组件本身是可复用的,不应该固定在某个特定的DOM元素上。
另外data也是不一样的。vc的data必须是一个函数。这是为了保证每个组件实例都有独立的数据副本,避免数据共享导致的问题。
只能这么说:vm上有的vc上不一定有,vc上有的vm上一定有。因为VueComponent继承了Vue的部分特性,同时又有自己独特的属性和行为。例如,Vue实例有$mount方法用于手动挂载实例到DOM,而VueComponent实例通常不需要直接调用这个方法,它会在父组件注册和使用时,由Vue框架自动处理挂载过程。

4.3 通过vc可以访问Vue原型对象上的属性

通过vc可以访问Vue原型对象上的属性:
为什么要这么设计?代码复用。Vue原型对象上有很多方法,例如:$mount(),对于组件VueComponent来说就不需要再额外提供了,直接使用vc调用$mount(),代码得到了复用。
Vue框架是如何实现以上机制的呢?
VueComponent.prototype.__proto__ = Vue.prototype

1、回顾原型对象

<script>
    // prototype  __proto__

    // 构造函数(函数本身又是一种类型,代表Vip类型)
    function Vip() {}

    // Vip类型/Vip构造函数,有一个 prototype 属性。
    // 这个prototype属性可以称为:显式的原型属性。
    // 通过这个显式的原型属性可以获取:原型对象

    // 获取Vip的原型对象
    let x = Vip.prototype;

    // 通过Vip可以创建实例
    let a = new Vip();
    // 对于实例来说,都有一个隐式的原型属性: __proto__
    // 注意:显式的(建议程序员使用的)。隐式的(不建议程序员使用的。)
    
    // 这种方式也可以获取到Vip的原型对象
    let y = a.__proto__;

    // 原型对象只有一个,其实原型对象都是共享的。
    console.log(x === y); // true

    // 作用:
    // 在给“Vip的原型对象”扩展属性
    Vip.prototype.counter = 1000;

    // 通过a实例可以访问这个扩展的counter属性吗?可以访问。为什么?原理是啥?
    // 访问原理:首先去a实例上找counter属性,如果a实例上没有counter属性的话,
    //会沿着__proto__这个原型对象去找。
    
    // 下面代码看起来表面上是a上有一个counter属性,实际上不是a实例上的属性,是a实例对应的原型对象上的属性counter。
    console.log(a.counter);
    //console.log(a.__proto__.counter)
</script>

在JavaScript中,原型链是实现对象属性和方法继承的重要机制。通过将VueComponent.prototype.__proto__设置为Vue.prototypeVueComponent实例就可以访问Vue原型对象上的属性和方法,实现了代码的复用。例如,如果在Vue原型对象上定义了一个全局的工具方法,那么所有的VueComponent实例都可以直接调用这个方法,而无需在每个组件中重复定义。
在这里插入图片描述

2、底层实现

VueComponent.prototype.__proto__ = Vue.prototype
在这里插入图片描述

<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <user></user>
    </div>
    <script>
        // 创建组件
        const user = Vue.extend({
            template: `
            <div>
                <h1>user组件</h1>
            </div>
            `,
            mounted() {
                // this是VueComponent实例
                // user是什么呢????是一个全新的构造函数 VueComponent构造函数。
                // 为什么要这样设计?为了代码复用。
                // 底层实现原理:
                // VueComponent.prototype.__proto__ = Vue.prototype
                console.log("vc.counter", this.counter);
            },
        });
        // vm
        const vm = new Vue({
            el: "#app",
            data: {
                msg: "vm与vc",
            },
            components: {
                user,
            },
            mounted() {
                // this是Vue实例
                console.log("vm", this);
            },
        });

        // 这个不是给Vue扩展counter属性。
        // 这个是给“Vue的原型对象”扩展一个counter属性。
        Vue.prototype.counter = 1000;
        console.log("vm.counter", vm.counter);
        // 本质上是这样的:
        console.log("vm.counter", vm.__proto__.counter);
        console.log("user.prototype.__proto__ === Vue.prototype", user.prototype.__proto__ === Vue.prototype);
    </script>
</body>

在上述代码中,通过将counter属性添加到Vue原型对象上,VueComponent实例(user组件)和Vue实例(vm)都可以访问到这个属性。通过console.log("user.prototype.__proto__ === Vue.prototype", user.prototype.__proto__ === Vue.prototype);可以验证VueComponent原型对象与Vue原型对象的关联关系,这就是实现VueComponent实例能够访问Vue原型对象属性和方法的底层机制。

相关文章:

  • Qt 实操记录:打造自己的“ QQ 音乐播放器”
  • Pycharm接入DeepSeek,提升自动化脚本的写作效率
  • 【css酷炫效果】纯CSS实现故障文字特效
  • 设计模式(创建型)-单例模式
  • 【前端面试题】闭包和其应用
  • 安卓apk加固后,Android11+无法安装
  • 在NET6项目中报错,未能在命名空间System.Data.SqlClient中找到类型名SqlCommand,解决办法
  • 一次由IDEA配置引发的Redis连接问题
  • 区块链赋能:用Python开发去中心化投票系统
  • 清晰易懂的Node.js安装教程
  • 五种方案实现双链路可靠数据传输
  • IntelliJ IDEA 调试技巧指南
  • LLM系列笔记之微调数据集格式
  • 网络编程---创建客户端和服务端以及协议包
  • 开源免费一句话生成儿童故事视频核心思想解析
  • Java基础语法练习42(基本绘图-基本的事件处理机制-小坦克的绘制-键盘控制坦克移动)
  • leetcode-47.全排列II
  • 深度学习之防止过拟合
  • 【华三】路由器交换机忘记登入密码或super密码的重启操作
  • 打乱一维数组中的元素,并按照4个一组的方式添加到二维数组中
  • 网站三级域名对网站有影响吗/怎么引流推广自己的产品
  • 最大源码网站/疫情最新情况
  • 网站建设服务项目/seo怎么优化
  • 公积金网站建设模板/武汉最新疫情
  • 鲜花网站数据库建设分析/足球积分排行榜最新
  • 企业做营销型网站/新东方教育培训机构