整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Vue之组件化

Vue之组件化
  • 荐Vscode编译器、vue官网:https://cn.vuejs.org/v2/guide/installation.html
  • 1.组件化

    在这里插入图片描述



    在这里插入图片描述

    2.注册组件

    在这里插入图片描述

    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    
    <div id='app'>
        <!-- //3.使用组件 -->
        <my-cpn></my-cpn>
    </div>
    <script src='../js/vue.js'></script>
    <script>
        //1.创建组件构造器对象(es6 ` 可以换行不用+)
        const cpnC=Vue.extend({
            template: ` 
            <div>
                <h2>我是标题</h2>
                <p>我是内容,哈哈哈</p>
                <p>我是内容,呵呵呵</p>
            </div>`
        })
        //2.注册组件
        Vue.component('my-cpn',cpnC)
    
    
    
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy'
            }
        })
    </script>
    </body>
    </html>
    

    3.注册组件步骤解析

    在这里插入图片描述



    在这里插入图片描述


    注:必须在new Vue挂载的实例之下,才被渲染出来

    4.全局组件和局部组件

    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    <div id='app'>
        <cpn></cpn>
        <cpn></cpn>
    </div>
    
    <div id='app2'>
        <cpn></cpn>
    </div>
    <script src='../js/vue.js'></script>
    <script>
        //1.创建组件构造器对象(es6 ` 可以换行不用+)
        const cpnC=Vue.extend({
            template: ` 
            <div>
                <h2>我是标题</h2>
                <p>我是内容,哈哈哈</p>
                <p>我是内容,呵呵呵</p>
            </div>`
        })
        //2.注册组件(全局组件),意味着可以在多个Vue的实例下面使用
        //Vue.component('my-cpn',cpnC)
    
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy'
            },components: {
                //局部组件:
                // cpn 使用组件时候的标签名
                cpn:cpnC
            }
        });
        const app2=new Vue({
            el:'#app2', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy'
            }
        })
    </script>
    </body>
    </html>
    

    在这里插入图片描述



    在这里插入图片描述

    5.父组件和子组件

    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    <div id='app'>
        <cpn2></cpn2>
    </div>
    
    <div id='app2'>
        <cpn></cpn>
    </div>
    <script src='../js/vue.js'></script>
    <script>
        //1.创建第一个组件
        const cpnC1=Vue.extend({
            template: ` 
                <div>
                    <h2>我是标题1</h2>
                    <p>我是内容,哈哈哈</p>
                </div>`
        });
        //2.创建第二个组件(父组件)
        const cpnC2=Vue.extend({
            template: ` 
            <div>
                <h2>我是标题2</h2>
                <p>我是内容,呵呵呵</p>
                <cpn1></cpn1>
            </div>`,
            components:{
                cpn1:cpnC1
            }
        });
    
        //root组件
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy'
            },components: {
                //2.注册:局部组件:
                // cpn 使用组件时候的标签名
                cpn2:cpnC2
            }
        });
    
    </script>
    </body>
    </html>
    

    在这里插入图片描述

    6.注册组件的语法糖

    在这里插入图片描述

    在这里插入图片描述



    在这里插入图片描述

    7.模板的分类写法

    • 全局组件分离
    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    <div id='app'>
        <cpn></cpn>
    </div>
    <!-- 1.第一种 -->
    <!-- <script type="text/x-template" id="cpn">
    <div>
        <h2>我是标题</h2>
        <p>我是内容</p>
    </div>
    </script> -->
    
    <!-- 2.第二种 -->
    <template id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>我是内容</p>
        </div>
    </template>
    <script src='../js/vue.js'></script>
    <script>
        //1.注册一个全局组件
        Vue.component('cpn',{
            template:`#cpn`
        })
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy'
            }
        })
    </script>
    </body>
    </html>
    
    • 局部组件分离
    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    <div id='app'>
        <cpn></cpn>
    </div>
    <!-- 1.第一种 -->
    <!-- <script type="text/x-template" id="cpn">
    <div>
        <h2>我是标题</h2>
        <p>我是内容</p>
    </div>
    </script> -->
    
    <!-- 2.第二种 -->
    <template id="cpn">
        <div>
            <h2>我是标题</h2>
            <p>我是内容</p>
        </div>
    </template>
    <script src='../js/vue.js'></script>
    <script>
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy'
            },
            components: {
                'cpn':{
                    template:'#cpn'
                }
            }
        })
    </script>
    </body>
    </html>
    

    8.组件数据

    在这里插入图片描述


    注:组件内不能访问data数据,所以Vue组件因该有自己保存数据的地方

    在这里插入图片描述

    • 为什么必须是一个函数?
    • 在这里插入图片描述

    在这里插入图片描述

    9.父子间通信

    在这里插入图片描述

    10.父级向子级传递

    在这里插入图片描述

    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    <div id='app'>
        <cpn :cmovies='movies' :cmessage='message'></cpn>
    </div>
    <template id="cpn">
        <div>
            <ul>
                <li v-for="item in cmovies">{{item}}</li>
            </ul>
            <p> {{cmessage}}</p>
            <h2>{{cmovies}}</h2>
        </div>
    </template>
    <script src='../js/vue.js'></script>
    <script>
        // 父传子:props
        //局部组件
        const cpn={
            template:'#cpn',
            //props: ['cmovies','cmessage']
            props:{
                //1.类型限制
                //cmovies:Array,
                //cmessage:String
    
                //2.提供一些默认值
                cmessage:{
                    type:String,
                    default:"aaaaa",
                    required:true ///必传
                },
                //类型是对象或数组的时候,默认值必须是一个函数
                cmovies:{
                    type:Array,
                    default(){
                        return []
                    }
                }
            }
        }
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy',
                movies: ['海','王者','夏洛特']
            },
            components: {
                'cpn':cpn
            }
        })
    </script>
    </body>
    </html>
    
    • 数据验证的写法(props对象)
    • 在这里插入图片描述

    11.props驼峰标识

    注:vue不支持直接的驼峰标识,需要进一步的转换,如:childMyMessage,需要在绑定的时候使用:child-my-message

    <!DOCTYPE html>
    <html lang='en'>
    <head>
        <meta charset='UTF-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <title>Document</title>
    </head>
    <body>
    <div id='app'>
        <cpn :cinfo='info' :child-my-message="message"></cpn>
    
    </div>
    <template id="cpn">
        <!-- 如果有多个标签,需要外部有个div包起来 -->
        <div>
            <h2>{{cinfo}}</h2>
            <h2>{{childMyMessage}}</h2>
        </div>
    </template>
    <script src='../js/vue.js'></script>
    <script>
    
        const cpn={
            template: '#cpn',
            props:{
                cinfo:{
                    type : Object,
                    default(){
                        return {}
                    }
                },
                childMyMessage:{
                    type : String,
                    default : ''
                }
            }
        }
        const app=new Vue({
            el:'#app', //用于挂载要管理的元素
            data:{ //定义数据
                message:    '你好啊,李银河!',
                name:   'codewhy',
                info:{
                    name:   'why',
                    age:    18,
                    height: 1.88
                }
            },
            components: {
                'cpn':cpn
            }
        })
    </script>
    </body>
    </html>
    

    12.子级向父级传递

    注:发射事件,自定义事件,子传父;然后父组件监听事件,注意名字不要用驼峰标识。

    通过前面的学习,对自定义组件的相关概念和知识点也有了一定了解,今天我们就来学习一下给自定义元素及其子元素设置样式的几种方法。

    直接给自定义标签添加样式

    index.html:

    <style>
        my-card{
            display: block;
            margin: 20px;
            width: 200px;
            height: 200px;
            border: 3px solid #000;
        }
    </style>
    <my-card></my-card>
    <script src="./index.js"></script>
    

    index.js:

    class MyCard extends HTMLElement {
        constructor() {
            super();
            this.shadow=this.attachShadow({ mode: "open" });
        }
    }
    
    window.customElements.define("my-card", MyCard);
    

    结果样式生效:

    需要注意的是:继承自 HTMLElement 的自定义元素,其 style.display 默认为 inline。

    由以上结果可以推论出:

    1. 给自定义元素添加 class,然后通过 class 名称设置样式可以生效;
    2. 给自定义元素添加行内样式,可以生效;
    3. 在自定义元素构造函数中给 this 添加样式,可以生效。

    给自定义元素内部子元素设置样式

    在主 DOM 通过类名设置

    在 style 标签中增加如下样式:

    <style>
        my-card {
            display: block;
            margin: 20px;
            width: 200px;
            height: 200px;
            border: 3px solid #000;
        }
        .card-header {
            padding: 10px;
            font-weight: bold;
            background-color: yellow;
        }
    </style>
    <my-card></my-card>
    
    <script>
        class MyCard extends HTMLElement {
            constructor () {
                super();
                this.shadow=this.attachShadow({mode: "open"});
    
                let headerEle=document.createElement("div");
                headerEle.className="card-header";
                headerEle.innerText="My Card";
                this.shadow.appendChild(headerEle);
            }
        }
        window.customElements.define("my-card", MyCard);
    </script>
    

    结果:不生效。

    通过前面的学习,我们知道:自定义元素内部实际上是一个 Shadow DOM,它和主 DOM 是相互隔离的,所以,主 DOM 中的样式是影响不到 Shadow DOM 的。

    使用 JS 给 Shadow DOM 增加 style 标签

    这里分为两种场景:在主 DOM 使用 JS 、在 Custom Elements 构造函数中使用 JS。

    第一种:在主 DOM 使用 JS 给 Shadow DOM 增加 style 标签:

    <style>
        my-card {
            display: block;
            margin: 20px;
            width: 200px;
            height: 200px;
            border: 3px solid #000;
        }
    </style>
    <my-card>
    </my-card>
    
    <script>
        class MyCard extends HTMLElement {
            constructor () {
                super();
                this.shadow=this.attachShadow({mode: "open"});
    
                let headerEle=document.createElement("div");
                headerEle.className="card-header";
                headerEle.innerText="My Card";
                this.shadow.appendChild(headerEle);
            }
        }
        window.customElements.define("my-card", MyCard);
        // 给 Shadow DOM 增加 style 标签
        let styleEle=document.createElement("style");
        styleEle.textContent=`
            .card-header{
                padding:10px;
                background-color: yellow;
                font-size: 16px;
                font-weight: bold;
            }
        `;
        document.querySelector("my-card").shadowRoot.appendChild(styleEle);
    </script>
    

    效果如下:

    第二种:在 Custom Elements 构造函数中使用 JS 增加 style 标签:

    <style>
        my-card {
            display: block;
            margin: 20px;
            width: 200px;
            height: 200px;
            border: 3px solid #000;
        }
    </style>
    <my-card>
    </my-card>
    
    <script>
        class MyCard extends HTMLElement {
            constructor () {
                super();
                this.shadow=this.attachShadow({mode: "open"});
                let styleEle=document.createElement("style");
                styleEle.textContent=`
                    .card-header{
                        padding:10px;
                        background-color: yellow;
                        font-size: 16px;
                        font-weight: bold;
                    }
                `;
                this.shadow.appendChild(styleEle);
    
    
                let headerEle=document.createElement("div");
                headerEle.className="card-header";
                headerEle.innerText="My Card";
                this.shadow.appendChild(headerEle);
            }
        }
        window.customElements.define("my-card", MyCard);
    </script>
    

    效果如下:

    就以上两种方式来说,第二种更符合组件化的特征,并且使用第一种方式时要注意,如果将添加 style 标签的代码放在定义 Custom Elements 之前会报错(找不到自定义元素)。

    引入 CSS 文件

    这里使用 JS 创建 link 标签,然后引入 CSS 文件给自定义元素内部的子元素设置样式,代码如下:

    <style>
        my-card {
            display: block;
            margin: 20px;
            width: 200px;
            height: 200px;
            border: 3px solid #000;
        }
    </style>
    <my-card>
    </my-card>
    
    <script>
        class MyCard extends HTMLElement {
            constructor () {
                super();
                this.shadow=this.attachShadow({mode: "open"});
                let linkEle=document.createElement("link");
                linkEle.rel="stylesheet";
                linkEle.href="./my_card.css";
                this.shadow.appendChild(linkEle);
    
    
                let headerEle=document.createElement("div");
                headerEle.className="card-header";
                headerEle.innerText="My Card";
                this.shadow.appendChild(headerEle);
            }
        }
        window.customElements.define("my-card", MyCard);
    </script>
    

    my_card.css 代码如下:

    .card-header{
        padding:10px;
        background-color: yellow;
        font-size: 16px;
        font-weight: bold;
    }
    

    效果如下:

    当然,这里也可以在主 DOM 中使用 JS 给 Shadow DOM 引入 CSS 文件,但是,这样做不符合组件化的特征,所以略过。

    结束语

    以上就是给自定义元素及其子元素进行样式设置的基本方法总结。

    ~

    ~ 本文完,感谢阅读!

    ~

    学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!

    大家好,我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!

    今前端编程中,利用语义化的 HTML 结合 CSS 来完一个组件并不是一件难事,这也意味着无论在 ReactVue 中都可以插入,不过它俩不是今天的主角,接下来我将用一个例子来介绍如何封装一个完整的原生 HTMLWeb Components 组件,让我们开始吧!

    HTML结构

    首先我们来了解下 HTML 中的 <details> 元素,它可以用于创建一个小部件,其中包含仅在小部件处于“打开”状态时才可见的附加信息,<details>元素内可以包含的内容没有任何限制。

    默认情况下,元素创建的小部件<details>处于“关闭”状态(open标签可使其打开)。通过单击小部件在“打开”和“关闭”状态之间切换,以显示或隐藏标签中包含的附加信息,内部标签 <summary> 元素则可为该部件提供概要。

    一个简单的例子如下:

    <details>
        <summary> 不能说的秘密 </summary>
        藏的这么深,可还是被你发现了
    </details>
    details {
        border: 1px solid #aaa;
        border-radius: 4px;
        padding: .5em .5em 0;
    }
    
    summary {
        font-weight: bold;
        margin: -.5em -.5em 0;
        padding: .5em;
    }
    
    details[open] {
        padding: .5em;
    }
    
    details[open] summary {
        border-bottom: 1px solid #aaa;
        margin-bottom: .5em;
    }

    使用语义化 HTML 的优点:页面内容结构更清晰,方便开发者阅读,更利于浏览器的理解与加载,搜索引擎解析与SEO优化。

    添加亿点样式

    原生元素默认的样式很简陋,因此我们需要为其定制一下样式,这块内容我们简单带过,只讲解关键部分,样式内容有省略,具体可以在文末的码上掘金中看到呈现效果。

    .ContentWarning > summary {
      position: relative;
      list-style: none; /** 去除默认样式 **/
      user-select: none; 
      cursor: pointer;
      /** 为其添加一个斜线背景 **/
      --stripe-color: rgb(0 0 0 / 0.1);
      background-image: repeating-linear-gradient(45deg,
          transparent,
          transparent 0.5em,
          var(--stripe-color) 0.5em,
          var(--stripe-color) 1em);
    }
    
    /** 通过var变量调整悬停时的颜色样式 **/
    .ContentWarning>summary: hover,
    .ContentWarning>summary: focus {
      --stripe-color: rgb(150 0 0 / 0.1);
    }

    封装模板

    现在我们来把它封装成一个完整的组件,这需要先将 HTML 编写在模板 template 当中,并设置一个 id,如下所示:

    <template id="warning-card">  
      <details class="ContentWarning">
        <summary>
          <strong>?? 注意:</strong> 以下为隐藏内容
        </summary>
        <slot name="desc"> 藏的这么深,可还是被你发现了 </slot>
      </details>
    </template>

    熟悉 Vue 的小伙伴应该很容易理解上面的代码,结构很相似,不过网页不会直接渲染它包裹的内容。此外我们还对此模板设置了一个插槽 slot,后面会讲到它的作用。

    定义组件

    有了上面封装好的模板,我们就需要在 JS 中定义成可用组件来让其能够被使用,调用 window 下的 customElements.define 方法,第一个参数是传入组件名称,我们定义组件名为: warning-card ,第二个参数传入一个继承了 HTMLElement 的类,在其构造方法当中获取并克隆一个新的 HTML 节点,它会通过 appendChild 渲染到页面当中。

    window.customElements.define('warning-card',
      class extends HTMLElement {
        constructor() {
          super();
          var templateElem=document.getElementById('warning-card');
          var content=templateElem.content.cloneNode(true);
          this.appendChild(content);
        }
      })

    接着我们就可以在页面中把它当作组件那样使用了:

    <warning-card> </warning-card>

    插槽与传参

    回头看看上面我们模板中设置的插槽 slot,此时还是没有生效的,我们需要稍微改写一下构造函数中的渲染方式,将 web 组件定义为一个 Shadow DOM,这样构造的是一个可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起的独立元素,并在最后使用 Node.cloneNode() 方法添加了模板的拷贝到 Shadow 的根结点上。

    window.customElements.define('warning-card',
      class extends HTMLElement {
        constructor() {
          super();
          var template=document.getElementById('warning-card').content;
          this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));
        }
      })

    现在我们尝试使用下组件,往其内容添加一个图片,指向名为 descslot 插槽中:

    <warning-card>
      <img slot="desc" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba825ffee78c4a1b9c0232e5d2f1d048~tplv-k3u1fbpfcp-watermark.image?" />
    </warning-card>

    这时你会发现,图片插入到 details 元素的隐藏区域当中了,slot 已经成功生效,但是样式却消失了,这时因为组件已经被完全隔离,我们需要将样式作用在其内部才会生效。

    <template id="warning-card">
      <style>
        <!-- TODO: 组件的样式 -->
      </style>
      
      <details class="ContentWarning">
        <summary>
          <strong>?? 注意:</strong>
        </summary>
        <slot name="desc">THE DESCRIPTION</slot>
      </details>
    </template>

    这样组件就正常了:

    除了定制模板中的插槽,我们也可以通过 HTML 标签属性来实现一些简单的传参,例如在 summary 标签中显示一个标题:

    <warning-card title="前方高能">
    </warning-card>

    我们只需要在模板中定义好这个标题的位置:

    <template id="warning-card">
      <details class="ContentWarning">
        <summary>
            <!-- TODO: 模板中加入一个span标签 -->
          <strong>?? 注意:</strong> <span id="title"></span>
        </summary>
      </details>
    </template>

    最后在构造函数中我们通过 document 的原生方法写入模板中就可以了:

    window.customElements.define('warning-card',
      class extends HTMLElement {
        constructor() {
          super();
          var template=document.getElementById('warning-card').content;
          // TODO: 找到title标签,写入传入组件的title属性值
          template.querySelector('#title').innerText=this.getAttribute('title');
          this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));
        }
      })

    结束

    至此,我们通过一个简单的原生组件学习了如何编写 Web Components,可以在此代码片段中查看具体源码:原生Web Components组件 - 码上掘金原生Web Components组件 - 码上掘金。

    以上就是文章的全部内容,希望对你有所帮助!如果觉得文章写的不错,可以点赞收藏,也欢迎关注,我会持续更新更多前端有用的知识与实用技巧,我是茶无味de一天,希望与你共同成长~